Repository navigation
[bug] Devin drops namespaced client tool identity and rejects CUA js #4456
Description
Client or integration
Codex App
Provider or upstream service
Devin / Cognition cloud-direct (devin, model swe-2)
OpenCodex version
@bitkyc08/opencodex 2.52.0 and dev at dc33113a9
Endpoint or capability
/v1/responses streaming client tool calls with namespaced MCP tools
Current behaviour
The Devin adapter advertises every tool to Cognition as the local tool.name, dropping its Codex namespace. For a request that declares CUA as mcp__cua_repl__js, Cognition sees and returns js. runTurn forwards that bare name unchanged, while the Responses bridge authorizes the canonical request name. The fail-closed guard then terminates the turn with HTTP 502.
Redacted local observations on 2026-09-13 recorded 14 identical 502s across four conversations from devin/swe-2 between 12:28 and 12:33 KST.
Expected behaviour
When Devin returns a bare tool name that uniquely identifies one request tool, the adapter should restore the request's canonical wire identity before the shared Responses bridge validates and dispatches it. A bare name shared by multiple request tools must fail without choosing by declaration order. Unknown names must remain subject to the existing undeclared-tool guard.
Minimal redacted request or reproduction
# OpenCodex 2.52.0, Codex CLI/App 0.154.0
# Route a Codex task through devin/swe-2 with the CUA MCP tool declared as:
# namespace: mcp__cua_repl
# name: js
# Ask the model to continue a browser/UI task that requires that tool.
# The adapter sends Cognition the bare name "js" and returns the same bare name.Actual response or error
stream disconnected before completion: routed provider emitted undeclared client tool "js"; only request-declared tools may be calledUpstream documentation
Cognition's cloud-direct Connect-RPC endpoint has no public tool-naming specification. The concrete client contract is the Responses request catalog: the client declared mcp__cua_repl__js, and OpenCodex must preserve that tool identity across an adapter that intentionally advertises its local name upstream.
Relevant source on dev: src/adapters/devin.ts maps tools with name: tool.name and emits event.name unchanged. The shared bridge uses namespacedToolName(namespace, name) as the authorized wire identity.
Suggested mapping or implementation notes
Build a request-scoped map from each advertised local name to namespacedToolName(tool.namespace, tool.name). Restore only uniquely owned names on tool_call_start. Represent collisions explicitly and emit a non-retryable adapter error before dispatch; preserve unknown names for the shared fail-closed guard.
Keep Cognition's current bare-name request contract and catalog nudge unchanged. This makes the response half symmetric without broadly authorizing bare aliases in the provider-independent bridge.
Additional context and attachments
- The bare-name request mapping was introduced with feat(devin): Cognition cloud provider, carried from #4078 and hardened #4285.
- refactor(devin): retire the ACP adapter and give the shared adapter the tool-catalog nudge #4415 now documents and nudges the same bare names, which makes the missing return mapping visible.
- Searches for the exact error and for Devin namespace/tool-name PRs found no existing report or contribution.
- Request/response details above omit account IDs, conversation IDs, and credentials.
Checks
- I searched existing provider and compatibility issues.
- The request and response were redacted.
- The expected behaviour is based on an upstream specification or a concrete client requirement.
Activity
lidge-jun commentedon Sep 13, 2026
리뷰 · 우선순위 76 / 80
설명
이 이슈는 Devin cloud-direct가 요청에 선언된 이름공간 도구 이름을 버리고, Cognition이 돌려준 짧은 이름만 Responses 다리로 넘기면서 턴이 502로 죽는 버그입니다. 보고는 Codex App +
devin/swe-2에서 CUA를mcp__cua_repl__js로 선언했을 때, 어댑터가 Cognition에는js만 알리고, 응답도js로 내려온 뒤 공유 undeclared-tool 가드가routed provider emitted undeclared client tool "js"로 끊는 패턴입니다. 2026-09-13 12:28–12:33 KST에 같은 502가 네 대화에서 14번 찍혔다고 하니, 추측이 아니라 재현된 운영 장애입니다.지금
dev(HEAD261bab915)의src/adapters/devin.ts를 보면 요청 절반은 의도적으로 짧은 이름입니다.mapOcxToolsToDevin이name: tool.name만 넣고 namespace를 붙이지 않습니다.#4285에서 그렇게 바꾼 뒤#4415가 같은 bare-name 계약을 문서/너지로 고정했습니다. 그런데 응답 절반인tool_call_start는emit({ type: "tool_call_start", id: event.id, name: event.name })로 받은 이름을 그대로 보냅니다. 공유 Responses 다리는namespacedToolName(namespace, name)으로 권한을 검사하므로, 요청 카탈로그의mcp__cua_repl__js인데 어댑터가js를 내면 fail-closed가 맞습니다. 즉 “요청은 bare로 광고하고, 응답은 canonical로 복원하지 않음”이 구멍입니다.이 타이밍이 중요합니다. 바로 전에
#4453으로 Devin prompt cache 요청 + 세션 identity가dev에 들어왔고,#4450으로 헤더 마감도 고쳤습니다. Devin 줄은 “긴 턴을 살리고 캐시를 쓰게” 쪽으로 가고 있는데, 도구 한 번만 호출해도 턴이 502면 그 이득이 사라집니다. 고침 모양도 이슈가 잘 짚었습니다. 요청 범위 맵advertisedLocalName → namespacedToolName(...)을 만들고,tool_call_start에서 유일하게 소유된 이름만 복원하고, 충돌은 선언 순서로 고르지 말고 어댑터 비재시도 오류로 끊고, 모르는 이름은 기존 undeclared 가드에 맡기세요. Cognition 쪽 bare 요청 계약과 카탈로그 너지는 유지하는 편이#4415와 맞습니다.경로 src/adapters/devin.ts mapOcxToolsToDevin - 요청 광고는 계속
tool.name만 써도 됩니다. 여기에 namespace를 다시 붙이면 Cognition 계약이 깨집니다. 고칠 곳은 응답 복원입니다.경로 src/adapters/devin.ts tool_call_start emit -
event.name을 그대로 내보내면 안 됩니다. 같은 턴의 요청 도구 목록으로 만든 identity 맵에서 canonical wire name을 찾아 넣으세요. 충돌(여러 도구가 같은 local name)은 첫 선언을 고르지 말고 명시적 오류로 막으세요.경로 공유 Responses undeclared-tool 가드 - 여기서 bare alias를 넓게 허용하는 우회는 하지 마세요. Devin만의 대칭 복원이 맞고, 다른 어댑터까지 bare를 열어 주면
#4176/execnested-helper 가드와 충돌합니다.이슈 본문 재현 절차 - 셸 예시는 개념 재현이라 CI fixture로 바로 쓰기 어렵습니다.
mapOcxToolsToDevin+ streamtool_call_start를 묶은 단위 테스트(유일 매핑 성공 / 충돌 실패 / 미지 이름 통과→가드)를 PR에 넣는 게 검증 기준이 됩니다.메인테이너의 판단이 필요한 지점
createDevinAdapter클로저에 둘지, cloud-direct 쪽과 공유 헬퍼로 뺄지#4415문서/너지에 “응답은 canonical로 되돌린다” 문장을 같이 고칠지dev(2.53.0 라인)만으로 충분한지너의 추천
우선순위 높게
help wanted로 두고, 첫 PR은 Devin 응답 identity 복원 + 충돌/미지 이름 테스트만으로 좁히세요. 공유 bridge에 bare alias를 넓히는 PR은 거절하세요. types/config 분할과 무관하고,#4453캐시 작업과도 파일 충돌이 거의 없으니 병렬로 가져갈 수 있습니다.이 댓글은 grok-bot이 작성했습니다
lidge-jun commentedon Sep 13, 2026
Fixed on dev via #4457, carried and merged inside lane C's tip #4487 (merge commit 55bb9f3, verified as an ancestor of origin/dev). The landed commits credit @jeongjin0 with a Co-authored-by trailer, and the carry also folded a fail-closed guard for a canonical/local tool-name collision.
Cross-platform CI run 34744712476 concluded success on the exact merged head. Closing manually because this merges into dev rather than the default branch.