Skip to content

fix(tokens): count a subagent's tokens as the session's - #24

Merged
ericleepi314 merged 1 commit into
mainfrom
fix/token-counting-subagents
Aug 19, 2026
Merged

fix(tokens): count a subagent's tokens as the session's#24
ericleepi314 merged 1 commit into
mainfrom
fix/token-counting-subagents

Conversation

@ericleepi314

Copy link
Copy Markdown
Collaborator

The bug

Every token read-out undercounted a run that fanned out.

A child's assistant/message usage was added to 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 the stats line's tokens: 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:

tokens: 1.1k in / 84 out        ← parent only, what it showed
tokens: 42.6k in / 3.1k out     ← what the session actually spent

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

onChildSessionEvent folds a registered child's step into usageTotals alongside the child's own counters.

The two halves of the bus are already disjoint — bindAgent routes by session identity, and onChildSessionEvent drops 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 on main with calls: 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 a CostSnapshot, the price-free usage path through message.complete, and the rider a resume replays out of the log.

Verification

npm test — 150 files, 1950 passed / 11 skipped. npm run typecheck and npm run verify:boundary clean.

🤖 Generated with Claude Code

…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>
@ericleepi314
ericleepi314 merged commit 7f48673 into main Aug 19, 2026
1 check passed
@ericleepi314
ericleepi314 deleted the fix/token-counting-subagents branch August 19, 2026 14:55
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