Skip to content

Restructure codex provider to emit tool call + EventToolResult on the live stream #12

Description

@moshloop

Summary

Restructure the codex (codex app-server) provider so command/tool execution is surfaced on the live Event stream as a tool call + a complete tool result, matching the new EventToolResult / Event.ToolCallID contract being added for live tool-result streaming.

Background

Captain is gaining a live tool-result contract on pkg/ai/types.go:

  • Event.ToolCallID string — correlation key set on tool-call and tool-result events.
  • EventToolResult EventKind = "tool_result" — the complete result of a prior call (ToolCallID, output in Text, Success, Raw).

claude-agent is being wired up in the same change (forwarding the SDK's tool_result blocks). Codex is deferred here because its mapping needs a real restructure rather than a field addition.

Problem (current behaviour)

In pkg/ai/provider/codex_appserver_protocol.go:

  • item/commandExecution/outputDelta is mapped to EventToolUse{Tool:"command", Input:{"delta": ...}} — i.e. command output is emitted as if it were a tool call.
  • item/started / item/completed for command/tool/file items also emit EventToolUse (via mapAppServerItem), with the item id stashed only on Event.Raw (not on the Event itself).

So there is no clean call→result correlation, output masquerades as a call, and there is no ToolCallID on the Event.

Proposed change

Per the uniform contract (one EventToolResult per call = complete output; backends that stream output accumulate internally):

  1. item/started (command/tool/file) → EventToolUse{ToolCallID: it.ID, Tool, Input} (the call).
  2. item/commandExecution/outputDeltaaccumulate into per-turn state keyed by ItemID (no longer emit an EventToolUse). Accumulation lives alongside the existing usage fold in the codex turn state (codex_appserver.go).
  3. item/completed for that item → emit one EventToolResult{ToolCallID: it.ID, Text: accumulated, Success} (and stash a claude.ToolUse with Response/IsError on Raw for the shared renderer).
  4. Set ToolCallID on every codex tool event.

Acceptance criteria

  • A codex turn that runs a command surfaces: one EventToolUse (the command, with ToolCallID) followed by one EventToolResult (the accumulated output, same ToolCallID).
  • captain live render and captain history render the codex command output identically.
  • clicky/aichat correlates the tool-output-available SSE part to the right tool card via ToolCallID (no synthetic placeholder).
  • Unit tests in pkg/ai/provider/codex_appserver_test.go cover the call/result split, ToolCallID, and multi-delta accumulation.

References

  • Contract + claude-agent + aichat changes: (the related branch/PR adding EventToolResult/ToolCallID).
  • Files: pkg/ai/provider/codex_appserver_protocol.go (mapAppServerNotification, mapAppServerItem), pkg/ai/provider/codex_appserver.go (turn state), pkg/ai/provider/codex_events.go (Raw stash helpers).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions