Skip to content

Keep every parallel tool call of a streamed Claude message - #1791

Merged
iamtoruk merged 1 commit into
getagentseal:mainfrom
kimlj:fix-streamed-tool-blocks
Oct 27, 2026
Merged

iamtoruk merged 1 commit into
getagentseal:mainfrom
kimlj:fix-streamed-tool-blocks

Conversation

@kimlj

@kimlj kimlj commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Claude Code writes one JSONL line per content block, so a message that calls several tools in parallel spans several lines with the same message.id. dedupeStreamingMessageIds keeps only the last line (the MCP tool calls and tool_use blocks missing from dashboard due to streaming write deduplication #110 fix), which has the right usage but only the last tool_use. Every earlier parallel tool call was dropped from the tool, MCP and shell breakdowns and from turn classification.
  • The last line is still kept. The tool_use blocks of the earlier lines are put back in front of its content, matched by block id, so the older shape from MCP tool calls and tool_use blocks missing from dashboard due to streaming write deduplication #110 (every block restated in the final write) is not counted twice. A message whose last line already holds every block is returned unchanged.
  • Calls, tokens and cost do not change. The claude parse tag gets streamed-tool-blocks-v1 so cached sessions re-parse. DAILY_CACHE_VERSION is not bumped: the v77 note says the next bump has to raise the Node floor first, so days the daily cache has already settled keep their current figures until then.

Found while building the session comparison proposed in #1790: the tool counts for two of my sessions did not match the transcripts.

Testing / Verification

  • I have tested this locally against real data (not just unit tests)
  • npm test passes: no new failures, but not clean on Windows (see Tests below)
  • npm run build succeeds
  • Not a UI change
  • UI change: before and after screenshots attached below

Real data, one day of my Claude Code history (2026-09-11), codeburn report --format json, main vs this branch:

main this branch Counted from the raw JSONL
API calls 482 482 482 assistant message ids
Cost $170.77 $170.77
Tool calls (tools[]) 417 1,589 1,589 unique non-MCP tool_use ids
Shell commands (shellCommands[]) 367 945
Exploration turns 24 20
Coding turns 34 38
Delegation turns 0 2

On main, 1,172 of 1,589 tool calls that day (74%) were missing. The ground-truth column is a script that reads every tool_use block in that day's transcripts and counts unique block ids. One session alone, 49 tool calls in its transcript, showed 18.

The classification changes are the knock-on effect: a turn whose Agent or Edit call sat on an earlier line was classified without it, so delegation turns showed as zero.

Tests:

  • tests/parser-streamed-tool-blocks.test.ts (new, 5 cases): parallel tool calls on separate lines are all kept; an MCP call in parallel with a built-in tool is kept; usage still counts once; the MCP tool calls and tool_use blocks missing from dashboard due to streaming write deduplication #110 restated shape does not double count; an already-complete last line is returned as-is. The two tool cases fail on main with expected [ [ 'Bash' ] ] to deeply equal [ [ 'Read', 'Read', 'Bash' ] ].
  • Full suite (npm test), this branch vs stock main, Windows 11, Node 24: no new failures. main has 43 failures on this machine before any change (symlink, keychain and timing tests); the branch fails the same set. Six files that differed between full runs (sync-attribution, cli-plan, dashboard-process-exit and three more) give the same result on both branches when rerun on their own, so those are load flakes on Windows.

Environment: Windows 11, Node 24.18, Claude Code transcripts.

@kimlj
kimlj requested a review from iamtoruk as a code owner October 23, 2026 05:19
@ozymandiashh ozymandiashh added area: cli The codeburn CLI and its core parsing/reporting engine bug Something isn't working provider: claude Claude Code provider labels Oct 24, 2026
Claude Code writes one JSONL line per content block, so a message that calls
several tools in parallel spans several lines with the same message id.
dedupeStreamingMessageIds kept only the last line, which holds the right usage
but only the last tool_use. The earlier tool calls were dropped from the tool,
MCP and shell breakdowns and from turn classification.

Keep the last line, and put back the tool_use blocks of the earlier lines,
matched by block id so a stream that restates every block in its final write
(getagentseal#110) is not counted twice. Calls, tokens and cost are unchanged.

Bump the claude parse tag so cached sessions re-parse. The daily cache version
is left alone: v77 is the last bump the zstd split can carry.
@kimlj
kimlj force-pushed the fix-streamed-tool-blocks branch from 8735134 to 50dca74 Compare October 27, 2026 06:48
@iamtoruk
iamtoruk merged commit e341438 into getagentseal:main Oct 27, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: cli The codeburn CLI and its core parsing/reporting engine bug Something isn't working provider: claude Claude Code provider

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants