Summary
PR #749 made Claude Code subscription mode (claude_code.enabled: true) a true pass-through for the headless infer agent path: infer no longer re-executes claude's tool calls locally - the adapter captures claude's own streamed tool results (TakeToolCallResults(), internal/infra/adapters/claude_code_client.go) and processSyncResponse (cmd/agent.go) synthesizes the tool messages from them instead of calling executeToolCalls.
The chat TUI path still has the original double-execution bug. Chat runs through the event-driven state machine, and EventDrivenAgent.executeTools (internal/agent/agent_tools.go) unconditionally calls executeToolCallsParallel on the accumulated tool calls - there is no IsClaudeCodeMode gate anywhere in internal/agent/states/, agent_event_driven.go, or agent_streaming.go. In claude_code mode, claude executes a tool internally and streams the call, then infer executes it again locally (or pops an approval prompt for an action claude has already performed).
The system-prompt and reminder gates from #749 already cover chat (shared addSystemPrompt / injectDueReminders in internal/agent/agent_utils.go); only tool execution is missing.
Proposed refactor:
- In claude_code mode,
executeTools should skip executeToolCallsParallel and synthesize results from provider.TakeToolCallResults(), matched by tool-call ID with a neutral "executed by Claude Code" fallback, then feed them through handleToolResults unchanged.
- Plumb provider/client access into
EventDrivenAgent so it can call TakeToolCallResults().
- Bypass the
ApprovingTools state in claude_code mode - the actions already happened inside claude, so a local approval prompt is meaningless.
- Mirror the headless behavior so both paths share the same pre-executed-results handling where practical.
Source PR: #749 (headless fix; this gap was explicitly flagged there as a follow-up).
Acceptance Criteria
Summary
PR #749 made Claude Code subscription mode (
claude_code.enabled: true) a true pass-through for the headlessinfer agentpath: infer no longer re-executes claude's tool calls locally - the adapter captures claude's own streamed tool results (TakeToolCallResults(),internal/infra/adapters/claude_code_client.go) andprocessSyncResponse(cmd/agent.go) synthesizes the tool messages from them instead of callingexecuteToolCalls.The chat TUI path still has the original double-execution bug. Chat runs through the event-driven state machine, and
EventDrivenAgent.executeTools(internal/agent/agent_tools.go) unconditionally callsexecuteToolCallsParallelon the accumulated tool calls - there is noIsClaudeCodeModegate anywhere ininternal/agent/states/,agent_event_driven.go, oragent_streaming.go. In claude_code mode, claude executes a tool internally and streams the call, then infer executes it again locally (or pops an approval prompt for an action claude has already performed).The system-prompt and reminder gates from #749 already cover chat (shared
addSystemPrompt/injectDueRemindersininternal/agent/agent_utils.go); only tool execution is missing.Proposed refactor:
executeToolsshould skipexecuteToolCallsParalleland synthesize results fromprovider.TakeToolCallResults(), matched by tool-call ID with a neutral "executed by Claude Code" fallback, then feed them throughhandleToolResultsunchanged.EventDrivenAgentso it can callTakeToolCallResults().ApprovingToolsstate in claude_code mode - the actions already happened inside claude, so a local approval prompt is meaningless.Source PR: #749 (headless fix; this gap was explicitly flagged there as a follow-up).
Acceptance Criteria