Repository navigation
fix(jsonl): dedup cumulative token totals by message.id - #725
Conversation
Claude Code writes one JSONL entry per content block of a single API call, all sharing one message.id. The cumulative token path summed every entry, inflating Tokens Input/Output/Total by up to ~1.84x (2.02x in the independent report on sirmalloc#549). Group consecutive counted entries sharing an id: the prompt side counts once, output_tokens as the running max. - transcripts without duplicate-id entries are unchanged (existing tests) - streaming partials resolve to the finalized value, counted once - sidechain rows keep their treatment in sums and recency; grouped by their own id so one subagent call counts once per content block Closes sirmalloc#549 Co-Authored-By: Claude Code <[email protected]>
|
Isn't there already an open PR for this at #595 ? |
|
Good catch, and thanks for the pointer — I hadn't cross-linked the issue and the PRs when I opened mine, so the overlap wasn't visible: both #725 and #595 address the same bug (#549). Fix direction Dedup consecutive counted entries by |
|
For what it's worth, a few more open PRs are working on #549 too, and not all of them link the issue: #591 and #622 cover both the token totals and the speed widgets like #595, #735 covers just the speed widgets (so it fits together with this one), and #587 includes its own version alongside its cost breakdown widgets. Might help to compare notes across all of them so there's one version to review. |
|
My comment just above was done by Claude... didn't mean to double post about it here. |
Preserve maximum-output accounting and context snapshots from sirmalloc#725 and sirmalloc#735 while deduplicating nonconsecutive message IDs. Keep independent timing intervals and subagent collectors, retain both sets of regression tests, and compute speed window bounds once.
Upstream: weekly limit timers (sirmalloc#768), prompt cache miss widgets (sirmalloc#770), context window size env override (sirmalloc#352, sirmalloc#407), windowed speed fixes (sirmalloc#769, sirmalloc#771), separator/renderer fixes (sirmalloc#773), custom-command styling (sirmalloc#775), clickable cwd links (sirmalloc#744), account email from live credentials (sirmalloc#724), cache timer reported expiry + short toggle (sirmalloc#576, sirmalloc#592), claude-status lock (sirmalloc#635, sirmalloc#772), dedup token metrics (sirmalloc#622, sirmalloc#725, sirmalloc#735). Not adopted: sirmalloc#765 terminal width from COLUMNS with fixed reserve - the fork keeps flexMode/compactThreshold and tmux pane priority. Hand edits outside conflicts: - README.md: kept fork Configure flex separator behavior bullet and older-updates details wrapper; dropped upstream Related Projects additions (claude-eta) - AGENTS.md: kept fork terminal width wording (sirmalloc#765 not adopted) - src/types/FlexMode.ts, src/tui/components/TerminalWidthMenu.tsx, src/tui/components/__tests__/TerminalWidthMenu.test.ts, src/tui/components/TerminalOptionsMenu.tsx, src/tui/components/__tests__/TerminalOptionsMenu.test.ts: restored, fork keeps terminal width settings - src/tui/App.tsx, src/tui/components/index.ts, src/types/index.ts: re-wired terminal width menu and FlexMode export - src/types/StatusJSON.ts: dropped fork duplicate prompt_cache key, upstream schema is a superset - src/utils/terminal.ts: COLUMNS source gated on non-tmux so tmux pane width keeps priority - src/widgets/WeeklyLimitTimer.ts, src/utils/usage-prefetch.ts, src/widgets/__tests__/WeeklyLimitTimer.test.ts: compat fix - fableUsage/fableResetAt -> weeklyFableUsage/weeklyFableResetAt - src/widgets/ApiUsage.tsx: compat fix - Context Bar honors effective context window (override, clamp, 1.0M format); Weekly renders a valid value despite a session error - src/widgets/__tests__/ApiUsage.localModelFallback.test.ts: output tokens no longer counted in context (upstream sirmalloc#730) - src/utils/__tests__/renderer-flex-width.test.ts, src/utils/__tests__/renderer-ansi.test.ts, src/utils/__tests__/gradient.test.ts, src/utils/__tests__/renderer-separator-collapse.test.ts, src/utils/__tests__/config.test.ts, src/tui/__tests__/App.test.ts: fork deviation - fork width model (flexMode, 6-column full reserve, kept settings, Terminal Width menu entry) - src/utils/__tests__/default-colors.test.ts: fork deviation - accept ansi256:/hex: defaults - src/utils/__tests__/usage-prefetch.test.ts: fork deviation - weekly usage comes from the API, fork widget imports from ApiUsage - src/widgets/__tests__/CurrentWorkingDir.test.ts, src/widgets/__tests__/CustomCommand.test.ts: Settings literal gains flexMode/compactThreshold - src/utils/__tests__/context-window.test.ts, src/utils/__tests__/jsonl-metrics.test.ts: restored fork tests upstream dropped - docs/test-retirements.md: ledger for the dev-reload snapshot-path and RemoteControlStatus glyph (sirmalloc#605) tests upstream replaced
Why
Closes #549. Claude Code writes one JSONL entry per content block of a single API call, all sharing one
message.id; the cumulative token path summed every entry, inflating Tokens Input/Output/Total (measured ~1.84×, independently confirmed at ~2.02× in the issue thread).What this does
Group consecutive counted entries that share a
message.id: the prompt side counts once,output_tokensas the running max (the approach endorsed in the issue thread). Entries without an id each start a new group — transcripts without duplicate-id entries are unchanged. Sidechain rows keep their treatment in sums and recency and group by their own id, so one subagent call counts once instead of once per content block.Recency snapshots (
contextLength, latest-usage) stay pinned to the group's first counted entry: duplicates only growoutput_tokensand repeat identical prompt-side usage, so the early return deliberately keeps the first snapshot.Verification
main.git-hardening-execLFS failure under Bun on macOS, unrelated — filing separately); target file green under bothbun testandvitest; lint/type-check clean.🤖 Generated with Claude Code