Skip to content

fix: register claude-code transforms so tool calls pass through - #735

Open
wayniacal wants to merge 1 commit into
rynfar:mainfrom
wayniacal:fix/claudecode-transforms
Open

fix: register claude-code transforms so tool calls pass through#735
wayniacal wants to merge 1 commit into
rynfar:mainfrom
wayniacal:fix/claudecode-transforms

Conversation

@wayniacal

Copy link
Copy Markdown

ADAPTER_TRANSFORMS has no entry for claude-code, so getAdapterTransforms("claude-code") returns [] and a Claude Code client keeps the createRequestContext defaults: blockedTools: [], passthrough: undefined. The SDK subprocess then runs the client's task itself, on the proxy host, with its own built-in Read/Write/Bash, while the CLI executes the same tool_use blocks locally. Every side effect happens twice.

claudeCodeAdapter.usesPassthrough() already returns resolvePassthrough(true), but nothing calls it. server.ts reads pipelineCtx.*. MERIDIAN_PASSTHROUGH=1 does not reach this path either.

Same class as #546, where openai had to be added to the transform's adapters array for the same reason.

How it surfaced

A scheduled agent on 1.57.0 that edits a git repo and posts a summary through an HTTP API. One run produced three posts:

12:02:10.431  post A   CLI executed the post command
12:02:10.444  post B   SDK subprocess ran the identical command, 13 ms later
12:02:17.383  post C   second SDK session, its own wording

Proxy log for that run:

[PROXY] 4ab4f7fb adapter=claude-code model=opus[1m] tools=27 lineage=new session=new
[PROXY] 652b45ff adapter=claude-code model=opus[1m] tools=0  lineage=new session=new

652b45ff is the CLI's tools=0 structured-output request (conversation title). With no transform, that one also became a full agent with built-in tools, so it did the entire task independently: its own file edits, its own commits, a racing push to the same branch. Its transcript records it reading the other session's concurrent edits and concluding a linter had reformatted the file, then deleting 21 lines to "dedupe". It emitted the title at the end, after the side effects.

Anything a Claude Code client does through Meridian is currently doubled this way. Writes, commits, HTTP calls.

Fix

transforms/claudecode.ts mirrors claudeCodeAdapter, registered under adapter.name ("claude-code", not "claudecode"). Core tool names stay PascalCase, since Claude Code's toolkit is Read/Write/Edit/Bash/Glob/Grep; reusing openCodeTransforms verbatim would hand the deferral logic OpenCode's lowercase names and defer the tools it means to load eagerly.

Tests

Parity suite extended for claude-code, including a check that the registry entry exists at all, since that is the failure here rather than a wrong value. Full suite passes (2106 + the isolated files, 0 failures).

Verified against the live proxy with an agent asked to append one line to a file:

before   2 lines written, meridian-side session tool calls: {'Bash': 1, …}
after    1 line written,  meridian-side session tool calls: {'mcp__oc__Bash': 1}
         [PROXY] deferred=23/27 tools (core: Read,Write,Edit,Bash,Glob,Grep)

server.ts reads pipelineCtx.*, never the adapter methods, and
ADAPTER_TRANSFORMS had no entry for adapter "claude-code". Claude Code
clients therefore got the createRequestContext defaults, blockedTools
empty and passthrough undefined, so the SDK subprocess ran the client's
task internally on the proxy host with its own built-in Read/Write/Bash
while the CLI executed the same tool_use blocks locally. Every side
effect happened twice, milliseconds apart.

Observed on a scheduled agent: one run produced three near identical
API posts and two racing git pushes to the same branch, from three
agents (the CLI, the SDK mirror of its turn, and a second SDK session
spawned by the CLI's tools=0 structured-output request).

Mirrors claudeCodeAdapter, held in sync by the parity tests. Same class
of bug as rynfar#546.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant