Skip to content

fix(devin): restore namespaced tool identities - #4487

Merged
lidge-jun merged 16 commits into
devfrom
codex/260913-carry-devin-restore-tool-names
Sep 13, 2026
Merged

fix(devin): restore namespaced tool identities#4487
lidge-jun merged 16 commits into
devfrom
codex/260913-carry-devin-restore-tool-names

Conversation

@lidge-jun

Copy link
Copy Markdown
Owner

Summary

Carry of #4457 by @jeongjin0 onto current dev. This is the lane C tip, stacked on #4485, so its own diff is just this change.

Cognition's request mapper advertises the local tool name, so a namespaced Codex tool such as mcp__cua_repl__js is sent upstream as js and comes back as js. The Responses bridge then validates that bare name against the request-declared tools, does not find it, and fails the turn as an undeclared client tool — the call is lost even though the tool was advertised.

  • Restore each unique bare tool-call name to the request-declared Codex namespace identity before the bridge validates it.
  • Reject duplicate local-name ownership before client dispatch instead of selecting a namespaced tool by declaration order, and leave unknown names for the shared undeclared-tool guard.
  • Preserve the existing bare-name Cognition catalog and replay contract, and document the request-scoped return map.

Closes #4456.

Review finding folded in

The unresolved finding on src/adapters/devin.ts is correct and is fixed here. The return map tracked only advertised local names, but the adapter accepts canonical names on return too — an existing test pins that. With { namespace: "a", name: "x" } and { namespace: "b", name: "a__x" }, a returned a__x is both the first tool's canonical identity and the second tool's advertised name. The map resolved it to b__a__x, so src/bridge.ts dispatched the call through the second tool's identity, which the caller may not have named.

Canonical identities are now registered as aliases of themselves, and a conflicting alias is marked ambiguous, so that name fails before dispatch like any other ambiguous bare name. Every existing case is unchanged, which the test matrix pins: a single namespaced tool, a duplicate identical declaration, a bare/namespaced collision, an undeclared name left to the shared guard, and the unambiguous local and unrelated canonical names that still resolve.

Conflict resolved

dev landed #4484 while this lane was preparing, which merges devin-cli into devin and rewrites the same structure/adapters/registry.md paragraph this change extends. src/adapters/devin.ts merged without conflict — #4484 moves provider identity and login, this change touches the tool-name return map, and they do not overlap. The document takes dev's rewritten paragraph, with the tool-name contract re-applied on top of the new wording rather than the old. Confirmed by running the full Devin test set including #4484's own devin-provider-merge-migration.test.ts and its renamed devin-login.test.ts.

Attribution, per AGENTS.md and CREDITS.md — the landing commit must carry this trailer, and the squash message must not drop it:

Co-authored-by: Jeongjin Shin <80797980+jeongjin0@users.noreply.github.com>

Lane C of devlog/_plan/260913_contributor_carry_train/ is #4473#4485 → this. Retarget to dev once #4485 lands.

Verification

Source head edc6db3ffa6b0857e38589e44a02ebc9fda82dfe carried onto #4485's head, then origin/dev merged up through the stack so this tip sits on dev at f7d9dbad03.

  • bun test on all seven Devin files — 136 pass, 0 fail: devin-adapter, devin-cli-authmode-migration, devin-hardening, devin-login, devin-prompt-cache, devin-provider-merge-migration, devin-stream-deadline. The last two come from dev's feat(providers): merge devin-cli into devin (import-first login, deprecated alias) #4484 and are included deliberately, because that is the change this tip had to merge.
  • bun run typecheck — clean.
  • bun run structure:check — passes.
  • bun run privacy:scan — passes.
  • The local full suite was not run. This branch is the lane tip, so its hosted CI run is the suite proof and the merge gate for all three lane C pull requests.

The live /v1/responses calls to devin/swe-2 in #4457 are the author's evidence and were not reproduced here; no live Cognition request was made from this carry.

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed.
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults. No authentication, credential, workflow, dependency or release surface is touched, and privacy:scan passes. The folded-in finding is itself a dispatch-correctness fix: it stops a tool call from reaching a tool the caller did not name.

yongzhao chen and others added 16 commits September 13, 2026 15:50
… [skip ci]

Carry of #4438 by Yongzhaooo, with the open CodeRabbit finding on
src/adapters/openai-chat.ts folded in.

A timeline developer message whose only part is non-text (a video part, for
example) serializes to an empty string here. The generic Chat path drops such a
message through the existing break, but the new chronological exception turned it
into { role: "system", content: "" }, which some upstreams reject. Skip it on the
non-native path so the OCG route matches the generic path instead of inventing a
content-free system message. Native OpenAI developer behavior is unchanged.

The finding also asked for video parts to be mapped to a Chat video_url part.
That is declined here: the Chat serializer has never emitted video for any
destination or role, including ordinary user messages on current dev, so it is a
pre-existing gap across every Chat provider rather than something this change
introduces, and no upstream in this repository is known to accept that part type.
Landing it inside a destination-scoped ordering fix would change every Chat
destination on unvalidated wire format.

structure/transports/responses.md is at its 600-line budget on dev with no
headroom, so its four-line cross-reference is dropped rather than adding the
repository's first grace.oversizeDocs entry for a cross-link. The owning
description stays in structure/providers/chat-compat.md and the cross-references
in runtime.md, transports/inventory.md, providers/cursor.md and
data-planes/inbound-compat.md are unchanged.

Co-authored-by: Yongzhao <133014490+Yongzhaooo@users.noreply.github.com>
A caller abort that lands while the native Chat stall clock is waiting on
upstream silence must be the only reported outcome. The stream closes as a
cancellation, onCancel fires once, upstream is cancelled once, and no
upstream_stall_timeout terminal surfaces even after the deadline it was
racing has elapsed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 176cbbd)
… doc [skip ci]

Carry of #4389 by olddonkey onto current dev.

structure/transports/responses.md is exactly at its 600-line budget on dev, so
the two new owning sections this change needs could not be added there at all:
bun run structure:check failed at 630 lines. structure/AGENTS.md says an
over-budget doc is split along a topic boundary with its own manifest entry, and
that a grace.oversizeDocs entry is only for a split already planned — so this
takes the split rather than parking a promise nobody would keep.

Request-copy and stream-buffer accounting are one topic and now live in
structure/transports/byte-accounting.md with its own manifest entry.
responses.md is byte-identical to dev again. Both documents sit in
structure/transports/, so the seventeen cross-references this change adds keep
their relative prefix and only change file name and are otherwise untouched.

structure/gui-and-management-api.md was 602 lines for the same reason. Its
cross-reference is dropped instead: the dashboard and management API own neither
the request-decompression nor the SSE-rewrite path, which makes it the least
load-bearing of the seventeen. The other sixteen are unchanged.

Co-authored-by: Olddonkey <22208754+olddonkey@users.noreply.github.com>
Map each unique Cognition bare tool name back to the request-declared Codex identity before bridge validation. Refuse ambiguous local-name collisions instead of selecting by declaration order.

(cherry picked from commit edc6db3)
Carry of #4457 by jeongjin0, with the unresolved CodeRabbit finding on
src/adapters/devin.ts folded in.

The return map tracked only advertised local names, but the adapter accepts
canonical names on return too, which the existing catalog test pins. With
{ namespace: "a", name: "x" } and { namespace: "b", name: "a__x" }, a returned
a__x is both the first tool's canonical identity and the second tool's advertised
name. The map resolved it to b__a__x, so src/bridge.ts dispatched the call
through the second tool's identity — a tool the caller may not have named.

Register canonical identities as aliases of themselves and mark a conflicting
alias ambiguous, so that name now fails before dispatch like any other ambiguous
bare name. The unambiguous local name and the unrelated canonical name still
resolve, and every existing case is unchanged: a single namespaced tool, a
duplicate identical declaration, a bare/namespaced collision, and an undeclared
name left for the shared guard.

Co-authored-by: Jeongjin Shin <80797980+jeongjin0@users.noreply.github.com>
# Conflicts:
#	structure/adapters/registry.md
@lidge-jun
lidge-jun requested a review from Ingwannu as a code owner September 13, 2026 07:13
@coderabbitai

coderabbitai Bot commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (2)
  • ^dev$
  • ^preview$

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 4fd66c3f-7e75-4d1a-94c5-ac3e596e469a

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 13, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-13T07:16:54.610961Z 9b30902 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

Copy link
Copy Markdown
Contributor

Deterministic PR hygiene checks passed.

@github-actions github-actions Bot added the bug Something isn't working label Sep 13, 2026
@lidge-jun

Copy link
Copy Markdown
Owner Author

리뷰 · 우선순위 74 / 80

설명
이 PR은 기여자 @jeongjin0의 #4457을 현재 dev 쪽으로 옮긴 캐리이고, 본문에 Closes #4456이 있습니다. Devin(Cognition) 쪽 요청 매퍼가 도구를 로컬 이름만 올립니다. 그래서 Codex가 mcp__cua_repl__js처럼 네임스페이스를 붙여 선언해도 업스트림에는 js로 가고, 돌아올 때도 js입니다. Responses 브리지는 그 맨이름을 요청에 선언된 도구 목록과 맞춰 보는데, 거기엔 정식 이름만 있어서 "선언되지 않은 클라이언트 도구"로 턴이 죽습니다. CUA js 같은 실제 호출이 광고는 됐는데 실행 직전에 버려지는 증상입니다.

고치는 위치는 src/adapters/devin.tsrunTurn입니다. 요청에 선언된 도구로 맨이름→정식 이름 맵을 만들고, tool_call_start가 올 때 그 맵으로 복원한 뒤 emit합니다. 같은 맨이름을 여러 도구가 나눠 가지면 선언 순서로 고르지 않고, 디스패치 전에 비재시도 502 오류로 막습니다. 정식 이름 자체도 맵에 넣어서 Cognition이 정식 이름을 그대로 돌려줘도 통과합니다. 한 도구의 정식 이름이 다른 도구의 광고 맨이름과 겹치면(예: a__x vs b__a__x) 예전에는 조용히 잘못된 쪽으로 갔는데, 이제는 모호로 실패합니다. 모르는 이름은 예전처럼 shared undeclared-tool 가드에 맡깁니다.

지금 tip(f7d9dbad0)에는 방금 #4484로 devin-clidevin으로 합쳐졌습니다. 본문도 말하듯 이 레인이 준비되는 동안 tip이 같은 structure/adapters/registry.md 문단을 다시 썼고, 이 PR은 tip 문장 위에 도구 이름 계약을 다시 얹었습니다. devin.ts 쪽은 #4484(프로바이더 id·로그인)와 이 변경(반환 이름 맵)이 겹치지 않는다고 확인했다고 합니다. tip에 buildDevinReturnedToolNameMap / mapDevinToolCallStart는 아직 없습니다. Devin이 막 tip의 중심 축이 된 직후라, #4456 계열 회귀를 막는 우선순위가 높습니다.

베이스는 열린 #4485(codex/260913-carry-memory-stream-optimizations)이고, lane C는 #4473#4485 → 이 tip입니다. 헤드 커밋 메시지가 스트림 할당 링크를 tip에 머지하며 registry.md 충돌을 풀었다고 나옵니다. 검증은 Devin 테스트 세트(맵·모호성·별칭 충돌·리플레이)와 #4484 마이그레이션·로그인 테스트까지 포함했다고 적혀 있고, 이 브랜치가 레인 tip이라 호스팅 CI가 세 PR 전체의 머지 게이트입니다. 라이브 Cognition 호출은 #4457 작성자 증거이고 이 캐리에서는 재현하지 않았다고 스스로 밝혔습니다.

경로/심볼 - 베이스가 #4485라 tip에 단독 머지하면 lane C가 꼬입니다. #4485(그리고 그 아래 #4473) 이후 dev 리타겟이 필수입니다.
src/adapters/devin.ts / mapDevinToolCallStart - 모호한 맨이름은 502·비재시도입니다. 클라이언트가 같은 로컬 이름을 두 MCP에서 동시에 쓰는 요청은 이제 항상 실패하니, 그게 원하는 fail-closed인지 메인테이너가 한 번 확인하면 좋습니다.
structure/adapters/registry.md - #4484 문단 위에 다시 쓴 계약입니다. tip이 또 같은 문단을 건드리면 충돌이 재발하니, 리타겟 때 문단이 한 번만 남았는지 보세요.
경로/심볼 - 라이브 devin/swe-2 + CUA js 재현은 이 캐리에 없습니다. CI 단위 테스트만으로 머지할지, 머지 직후 스모크 한 번 할지 정하세요.
경로/심볼 - #4457 원본과 #4456 이슈가 같은 줄기입니다. squash·닫기 때 이슈·크레딧(기여자) 처리가 빠지지 않게 하세요.

메인테이너의 판단이 필요한 지점

너의 추천
#4485가 dev에 들어간 뒤 이 tip을 리타겟하고, 레인 tip CI가 초록이면 머지하세요. #4456은 Closes로 닫고, @jeongjin0 크레딧을 유지하세요. tip의 #4484 Devin 통합 직후라 단독 cherry-pick 유혹이 있어도, registry 문단·스택 순서를 깨지 말고 레인 통째로 올리는 편이 안전합니다.

이 댓글은 grok-bot이 작성했습니다

@lidge-jun
lidge-jun changed the base branch from codex/260913-carry-memory-stream-optimizations to dev September 13, 2026 07:59
@lidge-jun
lidge-jun merged commit 55bb9f3 into dev Sep 13, 2026
30 checks passed
@lidge-jun
lidge-jun deleted the codex/260913-carry-devin-restore-tool-names branch September 13, 2026 08:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants