fix(claude-code): stop handing the CLI's own tool calls to the harness - #5994
fix(claude-code): stop handing the CLI's own tool calls to the harness#5994Guykaganovsky1 wants to merge 1 commit into
Conversation
A native `tool_use` block from the Claude Code CLI is the CLI's OWN call — a
builtin (Bash / Read / Write / Edit …) or a server from the `--mcp-config` we
hand it — and the CLI executes it inside its own agentic loop. The matching
`tool_result` blocks were already dropped for exactly that reason.
The call half was surfaced anyway, as `ProviderDelta::ToolCallStart` +
`ToolCallArgsDelta` and in the aggregated `ChatResponse.tool_calls`. That hands
OpenHuman's harness a tool it does not own and cannot run, and it never sees a
result for it. With the full-access toggle on (no `--disallowedTools`, so the
CLI keeps Bash and friends) a turn that reached for `Bash` produced:
[tinyagents::mw] no-progress nudge … tool=Bash step=4
[tinyagents::mw] repeated tool failure — halting run … tool=Write step=6
run halted by circuit breaker; surfacing as breaker_halt
…and the turn then burned its 900s wall-clock backstop. Neither half is
surfaced now, so this provider behaves as what it is: a chat model whose tool
use is internal. OpenHuman's own tools reach it through the prompt catalogue,
not through native tool calls.
Second fix in the same failure: the driver's per-turn timeout was 300s, which is
shorter than a turn the CLI is expected to take once full access lets it work.
The child was killed mid-turn and it surfaced as a provider timeout rather than
a slow answer. The default is now 900s — matching the harness's own backstop —
and is overridable with `OPENHUMAN_CLAUDE_CODE_TURN_TIMEOUT_SECS`.
Verified end to end against the live CLI: "create a file … then read it back"
returns `File written, read back: TOOLS_WORK`, the file exists on disk, and the
run logs zero `repeated tool failure` / `breaker_halt` lines. Before the change
the same class of turn halted at step 6.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
How this change flows1 changed behaviour across 8 relationships. 6 surrounding behaviours are shown (60 graph nodes walked). 36 further behaviours left out to keep the diagram readable. flowchart LR
n0["run_turn<br/>changed"]:::changed
n1["handle_assistant_block"]:::impacted
n2["Value"]:::impacted
n3["on_block_start"]:::impacted
n4["ProviderDelta"]:::impacted
n5["seatbelt_profile"]:::impacted
n6["new"]:::impacted
n0 -->|calls| n5
n1 -->|uses| n2
n1 -->|calls| n3
n1 -->|uses| n4
n1 -->|calls| n6
n3 -->|uses| n2
n3 -->|uses| n4
n3 -->|calls| n6
classDef changed fill:#0d4429,stroke:#238636,color:#e6edf3
classDef impacted fill:#161b22,stroke:#6e7681,color:#c9d1d9
classDef flagged fill:#5a1e02,stroke:#d93f0b,color:#ffffff
classDef blocking fill:#67060c,stroke:#f85149,color:#ffffff
Green: changed behaviour. Grey: surrounding behaviour. Arrows name the call, use, implementation, or test relationship. Orange: has findings. Red: has a finding that blocks the merge. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Team Run ID: 📒 Files selected for processing (3)
Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review. 📝 WalkthroughWalkthroughThe Claude Code driver now uses a configurable turn timeout with a 900-second default. Native CLI tool events remain internal, while text continues to stream to the harness. ChangesClaude Code handling
Estimated code review effort: 3 (Moderate) | ~20 minutes Merge Risk: ⚪ Minimal · up to Claude Code native tool activity remains internal while text streaming is preserved, and turns use a configurable 900-second default timeout. No current merge-blocking risk is identified. Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Title checkExplanation The title clearly and concisely describes the primary change: preventing Claude Code CLI-internal tool calls from reaching the harness. The timeout change is secondary and does not need to appear in the title.
Warning Your free Security trial is over. An organization admin can upgrade to Advanced for continuous pull request security review or dismiss this notice. Comment |
Summary
tool_useblock from the Claude Code CLI is the CLI's own call — a builtin (Bash/Read/Write/Edit…) or a server from the--mcp-configwe hand it. The CLI runs it inside its own agentic loop.tool_resultblocks were already dropped for that reason, but the call half was still surfaced to OpenHuman's harness, which cannot run those tools and never sees a result for them.--disallowedTools, so the CLI keepsBash), a turn that reached for a builtin failed repeatedly until the circuit breaker halted the run, then burned the 900s wall-clock backstop.Problem
Observed in a live desktop session with
chat_provider = "claude-code:claude-opus-5"andclaude_code_settings.json={"full_access": true}:Two independent defects behind one symptom:
event_mapper.rsmapped everytool_useblock toProviderDelta::ToolCallStart/ToolCallArgsDeltaand pushed it intoChatResponse.tool_calls— including the CLI's own builtins. The harness then tried to executeBash, which is not an OpenHuman tool. Asymmetric withmap_event'sUserarm, which drops the correspondingtool_resultprecisely because "the harness owns tools … not CC internals".TURN_TIMEOUTwas a hard 300s. Full access means the CLI does real multi-step work; the child was killed mid-turn and the failure read as a provider timeout rather than a slow answer.Solution
tool_useblock (so itsinput_json_deltas and stop event are swallowed rather than leaking as stray deltas) but surface nothing — noToolCallStart, noToolCallArgsDelta, no entry intool_calls. The provider is then what it actually is: a chat model whose tool use is internal. OpenHuman's own tools reach it through the prompt catalogue, not native tool calls.turn_timeout()readsOPENHUMAN_CLAUDE_CODE_TURN_TIMEOUT_SECS, defaulting to 900s to match the harness's own wall-clock backstop.Submission Checklist
cli_internal_tool_calls_are_not_surfaced_to_the_harness(the regression) andtext_in_a_turn_with_a_cli_tool_call_still_streams(the answer must survive the dropped block).tool_call_assembles_inputpinned the old behaviour and was replaced by the first of these.cargo test --lib inference::provider::claude_code: 45 passed, 0 failed.N/A: bug fix to existing behaviour, no feature row added, removed or renamed## Related—N/A: no matrix row covers the claude-code event mapperN/A: no release-cut surface changesCloses #NNN—N/A: no filed issue; found while running the Claude Code CLI provider with full accessImpact
Desktop/CLI, and only for workloads routed to
claude-code:<model>. Behaviour change: the aggregated response no longer reports the CLI's internal tool calls, so a caller that inspectedChatResponse.tool_callsfor this provider now sees an empty list — which is the honest answer, since those calls were never executable here and never had results attached.The timeout default triples. A genuinely hung CLI now takes 900s to surface instead of 300s; the env var is there for anyone who wants the old value back.
Verified end to end against the live CLI: "create a file … then read it back" returns
File written, read back: TOOLS_WORK, the file exists on disk, and the run logs zerorepeated tool failure/breaker_haltlines. The same class of turn halted at step 6 before the change.Related
N/A — no linked issue.
Summary by CodeRabbit