Skip to content

feat(runtime): show a turn's cost and context while it runs - #7074

Merged
Hmbown merged 2 commits into
codewhale-hq:mainfrom
gaord:feat/live-turn-usage
Oct 25, 2026
Merged

Hmbown merged 2 commits into
codewhale-hq:mainfrom
gaord:feat/live-turn-usage

Conversation

@gaord

@gaord gaord commented Oct 25, 2026

Copy link
Copy Markdown
Contributor

What and why

A client only learned a turn's cost and context when the turn ended, so a long
agentic run looked frozen and then jumped. The runtime already emits a usage
receipt per model call; those receipts now also carry:

  • billed_prompt_tokens — the prompt size the provider billed for that call
    (the last itemised request when retries were reported, else the call's input),
    the same basis the context meter reads.
  • cost — that call priced on the route it was dispatched on, as
    {usd, cny, usd_priced, cny_priced}. It is null when the route cannot price
    every token class the call used: an unpriced step is absent, not free.

The route is captured from TurnStarted/RouteDispatched in the turn monitor,
so no store read happens on the hot path and the price always names the route
that served that step. The turn's authoritative totals still arrive with
turn.completed; GET /v1/threads/{id}/usage remains the record.

runtime_mobile.html uses the receipts for a usage reading in the chat header:
it moves while the turn runs, is keyed per thread so switching away and back
does not restart it, dedupes replayed journal frames by seq, and never adopts
a compaction turn's usage as the conversation's spend.

Docs: docs/RUNTIME_API.md and docs/zh_hans/RUNTIME_API.md add turn.usage
to the event list with these fields.

Issue

No-Issue: additive event fields and a mobile-page reading; no tracking issue.

How I tested it

  • scripts/dev-cargo.sh check -p codewhale-tui --lib --locked
  • scripts/dev-test.sh crates/tui/src/runtime_threads.rs runtime_threads::tests — 291 passed
  • New: step_receipts_carry_a_price_for_the_dispatched_route (real engine through
    monitor_turn: receipt is priced on its route and names the billed prompt),
    priced_step_usage_prices_a_metered_route_on_its_established_endpoint,
    billed_prompt_tokens_reads_the_last_itemised_request
  • New: the_mobile_page_shows_a_turns_usage_while_it_runs (subscribes, keeps
    receipts out of the event log, per-thread figure, replay dedupe, compaction)
  • scripts/dev-cargo.sh test -p codewhale-tui --test feature_registry --locked
  • rustfmt --check clean on the touched Rust files

Not verified: no browser run of the mobile page; its script is syntax-checked
(node --check) and covered by the structural test only.

A tool that listens to a running turn could only report what it cost after the
turn ended, so long agentic work looked frozen and then jumped at the end. Each
model call now reports the price of that call on the route that served it and
the prompt the provider billed, so a client can follow the work as it happens.
The turn's own totals remain the record once it ends.

The mobile control page shows that reading in its header, keeps each
conversation's figure when you switch away and come back, and never presents a
compaction pass as the conversation's spend.
@gaord
gaord requested a review from Hmbown as a code owner October 25, 2026 04:52

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

Review fixes on top of the contributor's live-usage change, kept on the
contributor's branch so the PR lands as itself.

- Automatic compaction items no longer blank the conversation's figure, and a
  compaction pass is never presented as the conversation's spend. A compaction
  that owns a separate Runtime turn resets the figure like any new turn.
- turn.completed replaces the token counts with the authoritative
  payload.turn.usage but keeps the priced-call estimates already observed, so
  the price no longer disappears at settlement.
- The figure carries its turn id and replay high-water sequence per thread. A
  new turn resets it, so the next turn no longer accumulates onto the last
  one, and a replayed start, receipt or completion cannot reset or re-add it.
- Money reads "~$0.10 partial" with a tooltip that names what it excludes
  (unpriced, child and compaction work); an unpriced call accrues tokens but
  never invents free money, and amounts require the matching *_priced flag.
- The four tests that matched the page's source strings and the new code
  comments are removed to follow the repository's no-new-tests, no-new-comments
  rule for this change; the existing recovery-test receipt assertions stay.
  docs/RUNTIME_API.md and its zh_hans mirror describe the final behaviour.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@Hmbown
Hmbown merged commit 0c9af1a into codewhale-hq:main Oct 25, 2026
21 of 24 checks passed
Hmbown pushed a commit that referenced this pull request Oct 25, 2026
…queue fix

Two landed changes had no [Unreleased] entry.

- #7074 (@gaord): the Runtime API's turn.usage receipts now carry each call's
  priced cost and billed prompt size and the mobile control page shows live
  usage with a partial cost estimate. The contributor's PR did not touch the
  changelog and the merge range has no other mention, so this entry carries the
  credit.
- #6561: a generic fatal engine error no longer turns on offline queueing by
  itself (7d84d0b). The environment-only authentication branch is unchanged.

crates/tui/CHANGELOG.md is regenerated by scripts/sync-changelog.sh.

Verified: scripts/release/check-versions.sh exits 0 after this change
(workspace, npm and lockfile 0.10.2; 26 linked issue references checked in
v0.10.1..HEAD) and sync-changelog reports the mirror up to date. Docs only; no
source file changed.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Hmbown pushed a commit that referenced this pull request Oct 25, 2026
node web/scripts/check-facts.mjs failed at d4d50f7 and at e6c400e with
"generated agent docs are stale". Two committed sources changed without their
generated copies:

- docs/RUNTIME_API.md (the #7074 merge) was newer than its sha256 and embedded
  content in web/data/agent-docs.generated.json.
- the cloud-computer-dispatch summary in docs/features.toml, shortened in
  46aa866 for the 120 character limit, was newer than
  web/data/features.generated.json.

Regenerated with node web/scripts/derive-facts.mjs only (version=0.10.2 crates=30
providers=50 models=91 tools=79); no hand edit. The diff is those two files, 4
lines each. This clears the one baseline gate failure that the crate-split windows
would otherwise inherit, so a later docs gate can tell its own failure from this one.

Verified: node web/scripts/check-facts.mjs exits OK ("committed facts.generated.ts
matches workspace") after this change; it printed FAIL before it. No cargo, no source
file touched.

Co-Authored-By: Claude Sonnet 5.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.

2 participants