fix(tokens): count a subagent's tokens as the session's - #24
Merged
Conversation
…e fold
The token read-outs undercounted every run that fanned out. A child's
`assistant/message` usage went onto the child's own row — the agents
overlay, and the `Done (3 tool uses · 1.2k tokens)` a delegation settles
with — and stopped there. The session odometer behind `tokens: N in /
M out`, `/status` and the context read-out never heard about it, so a
turn that delegated most of its work reported the sliver the parent did
itself:
tokens: 1.1k in / 84 out ← parent only
tokens: 42.6k in / 3.1k out ← what the session actually spent
It is one bill. The two halves of the bus are already disjoint —
`bindAgent` routes by session identity and `onChildSessionEvent` drops
anything the lifecycle never announced as a child — so folding the
child's step in counts it exactly once, and a sibling top-level session
still cannot leak in.
Also lands the test coverage for the fold that #23 carried in without
it: `statsFromUsage` and its precedence behind a CostSnapshot, the
price-free `usage` path through `message.complete`, and the rider a
resume replays out of the log.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
Every token read-out undercounted a run that fanned out.
A child's
assistant/messageusage was added to the child's own row — the agents overlay, and theDone (3 tool uses · 1.2k tokens)a delegation settles with — and stopped there. The session odometer behind the stats line'stokens: N in / M out,/status, and the status rule's context read-out never heard about it. A turn that delegated most of its work reported only the sliver the parent did itself:The stats line has claimed subagent-inclusive totals since it was written (
src/lib/sessionStats.ts: "the same accumulators /cost prints, subagents included"). It just wasn't true on this backend.The fix
onChildSessionEventfolds a registered child's step intousageTotalsalongside the child's own counters.The two halves of the bus are already disjoint —
bindAgentroutes by session identity, andonChildSessionEventdrops anything the subagent lifecycle never announced as a child — so the step is counted exactly once, and a sibling top-level session in the same process still cannot leak in. There's a test for that second half.Tests
bills the child's tokens to the session odometer, not just its own row— parent step + child step (with a cache read) land in one total; fails onmainwithcalls: 1, input: 100.ignores a sibling session that was never announced as a child— guards the disjointness the fix relies on.Plus the coverage for the fold that #23 carried in without it:
statsFromUsage, its precedence behind aCostSnapshot, the price-freeusagepath throughmessage.complete, and the rider a resume replays out of the log.Verification
npm test— 150 files, 1950 passed / 11 skipped.npm run typecheckandnpm run verify:boundaryclean.🤖 Generated with Claude Code