Skip to content

agents proxy: opencode M4 — tool parts and real token/cost totals - #1950

Open
julia-ye wants to merge 1 commit into
juliaye/opencode-proxy-m3from
juliaye/opencode-proxy-m4
Open

agents proxy: opencode M4 — tool parts and real token/cost totals#1950
julia-ye wants to merge 1 commit into
juliaye/opencode-proxy-m3from
juliaye/opencode-proxy-m4

Conversation

@julia-ye

@julia-ye julia-ye commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #1949 (M3, which stacks on #1948 — review in order; this diff is M4 only). Fourth slice: tool-using prompts render their command activity, and real token/cost numbers reach the TUI.

What's here

  • Tool parts, live: run.tool_call_started/run.tool_call_completed become tool message.part.updated frames (runningcompleted/error), re-carrying the input on completion (the TUI keeps a part's final state). The canonical tool_call_id is the guest opencode's own prt_… id — time-encoded by the guest — so it's reused verbatim and the part sorts where the tool actually ran. Dangling tools are closed out as errors on turn end, on both the completed and failed paths.
  • Real totals: run.completed's payload carries per-turn total_tokens_in/out and run_cost_micros (confirmed live against the dev stack), rendered as a closing step-finish part and on the finalized assistant message — the two places the TUI's token counter and cost display read from. usage_recorded/cost_accrued/run.log become explicit no-ops instead of unhandled-log spam.
  • History parity: tool parts, step-finish, and totals reconstruct in the replay path too; text/reasoning part ids are minted at turn end so the answer sorts after the tool activity within the message.
  • Footer model fix: assistant message info uses flat modelID/providerID — the nested model{} form is user-message shape (confirmed against real-server history payloads), and using it on assistant messages left the TUI's footer model segment blank.

Ground truth

Captured a real opencode serve tool turn (bash) through the logging relay for the part lifecycle (pending → running with streaming metadata.output → completed with output/exit/title, bracketed by step-start/step-finish), and a hosted-session tool turn for the canonical payload shapes. Both recorded in the capture doc on the internal side.

Testing

  • Unit: tool-turn frame sequence (guest id reuse, input re-carry, title, step-finish tokens/cost), dangling-tool close-out on failure, history tool reconstruction (part order tool → text, totals on info + step-finish), flat model fields.
  • Live: a tool-using prompt in a real opencode TUI rendered the command block (# Running in /workspace, $ echo …, output), the streamed answer, the model name + real duration in the message footer, and a token count in the status bar — with zero unhandled routes.

Not in this PR

Permission round-trips (M5), stream reconnect (M6). Known cosmetic gap: per-step step-finish granularity (one closing part stands in; canonical only exposes per-turn totals).

🤖 Generated with Claude Code

Live: run.tool_call_started/completed become tool part.updated frames
(running -> completed/error), re-carrying the input on completion and
reusing the guest opencode's own prt_ ids so parts sort where the tool
actually ran; dangling tools are closed out as errors on turn end (both
completed and failed). run.completed's own payload carries the per-turn
totals (total_tokens_in/out, run_cost_micros) — rendered as a closing
step-finish part and on the finalized assistant message, which is what
the TUI's token counter and cost display key off. usage_recorded /
cost_accrued / run.log become explicit no-ops instead of unhandled-log
spam.

History: tool parts, step-finish, and real totals reconstruct the same
way; text/reasoning part ids are minted at turn END so the answer sorts
after the tool activity.

Also fixes the blank footer model segment: assistant message info uses
FLAT modelID/providerID (nested model{} is user-message shape — confirmed
against real-server history payloads).

Live-verified: a tool-using prompt renders the command block (workdir,
command, output), the answer, a real duration, the model name in the
footer, and a token count in the status bar.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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