Repository navigation
Description
What happened
Streaming a Claude Code run writes a tool_start record for every tool call and never writes the matching tool_end. execution.ndjson — and the cockpit and --progress views built from it — therefore show each Bash/Edit call as still running, and nothing in the log distinguishes a call that finished from one that was killed.
Two independent causes:
src/core/agent-stream.js:227(mapClaudePayload) treats everytype: "user"line as output echo and returns[]. In Claude Code'sstream-json, that is exactly wheretool_resultblocks arrive, so the closing event is thrown away.- Neither adapter records the CLI's own tool-call id — Codex sends
item.id, Claude sendstool_use.idandtool_use_id— so even with the first cause fixed, nothing can pair a result to its call, or tell two concurrent runs apart when the CLI reuses an id.
A run killed mid-call (the default 300s agent timeout, Ctrl-C, a crashed CLI) never emits a result at all, so its calls stay open forever with no terminal status.
What you expected
Every tool_start eventually gets exactly one tool_end for the same call, carrying the CLI's own id, and a call the CLI never finished is closed with a status that says so when the sink shuts down.
Reproduction
Smallest reproduction, no real CLI needed (evalid 0.5.33, Node 24):
import { normalizeAgentEvents } from './src/core/agent-stream.js';
const lines = [
'{"type":"assistant","message":{"content":[{"type":"tool_use","id":"toolu_1","name":"Bash","input":{"command":"ls"}}]}}',
'{"type":"user","message":{"content":[{"type":"tool_result","tool_use_id":"toolu_1","content":"ok"}]}}',
];
for (const line of lines) {
console.log(JSON.stringify(normalizeAgentEvents(line, { adapter: 'claude', seq: 0 })));
}Output today:
[{"seq":0,"event":"tool_start","event_type":"tool_use","tool":"Bash","args_preview":"{\"command\":\"ls\"}","args_digest":"4cf29611a6693486"}]
[]
The tool_start carries no id and the result maps to nothing.
xflow doctor --project-root .
reports a healthy project — this is a streaming-shape gap, not a configuration problem.
Environment
- evalid version: 0.5.33 (405ffa5)
- Node: 24.21.0
- Python: 3.15.2
- OS: Windows 11
- Agent CLI involved: Claude Code (stream-json); Codex has the same missing-id gap but does emit its closing events
I have a fix with tests ready and will open the PR next — additive record fields only, no consumer changes.
Activity
surlymochan commentedon Nov 12, 2026
Fixed by #183 (merged 2026-11-12, squash 37a1b1a — auto-closed on merge): claude tool_result now maps to tool_end with the CLI's own tool_call_id, and sink.close() marks still-open calls abandoned. Thanks @MAOLVLHL!