Skip to content

[TASK] Refactor chat state machine to skip local tool execution in Claude Code mode #750

Description

@edenreich

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

  • In claude_code mode, chat does not execute tool calls locally; claude's streamed tool results are used as the tool messages
  • No approval prompt is shown in chat for tools claude already executed
  • Gateway-mode chat behavior is unchanged (local execution + approval flow intact)
  • Tests cover the claude_code chat path (skip execution, synthesized results by tool-call ID, fallback for missing IDs)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Fields

    No fields configured for Task.

    Projects

    Status
    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions