fix(core): persist usage for tool-only subagent rounds - #7557
Conversation
Review & Local Verification Report代码审查设计评价:关键的遥测完整性修复。 本 PR 修复了 subagent 纯工具调用轮次的 usage 数据丢失问题。 问题: 当模型轮次只产生 tool call(无文本/思考)时, 修复(3 个文件):
影响: 对 token 计费和可观测性至关重要——纯工具轮次(如 subagent 只调用 read_file/grep 不产生文本)同样消耗 token,必须被追踪。 结论LGTM。 最小化修复,确保 usage 数据不因"无文本"而被丢弃。测试覆盖了核心场景。 |
E2E test reportTested commit Build
Real sub-agent run
Focused regression coverage
|
|
Thanks for the PR! Template looks good ✓ Problem: observed bug with clear evidence. The old code explicitly dropped usage-only rounds — the existing test was literally named "drops usage-only ROUND_TEXT to keep the canonical view valid". The E2E report in the comments confirms tool-call-only rounds now persist their usage metadata correctly, with token counts matching the CLI summary. Direction: aligned. Token usage completeness is core telemetry — under-counted subagent usage skews billing, cache-hit stats, and trajectory aggregation. Claude Code's CHANGELOG has similar fixes for token telemetry accuracy (e.g. "Fixed session cost and token telemetry double-counting"). Size: 20 production lines (agent-transcript.ts: 18, agent-core.ts: 2), 79 test lines. Well under all thresholds. Approach: minimal and focused. Two guard conditions relaxed — Moving on to code review. 🔍 中文说明感谢贡献! 模板完整 ✓ 问题:已观测到的 bug,有明确证据。旧代码显式丢弃了纯 usage 回合——原有测试名称就是 "drops usage-only ROUND_TEXT to keep the canonical view valid"。评论中的 E2E 报告确认纯工具调用回合现在能正确持久化 usage metadata,token 计数与 CLI 汇总一致。 方向:对齐。Token usage 完整性是核心遥测——subagent usage 低估会影响计费、缓存命中统计和 trajectory 聚合。Claude Code 的 CHANGELOG 有类似的 token 遥测准确性修复。 规模:20 行生产代码(agent-transcript.ts: 18, agent-core.ts: 2),79 行测试代码。远低于所有阈值。 方案:最小化且聚焦。放宽两个守卫条件—— 进入代码审查 🔍 — Qwen Code · qwen3.8-max-preview Reviewed at |
Code Coverage Summary
CLI Package - Full Text ReportCore Package - Full Text ReportFor detailed HTML reports, please see the 'coverage-reports-22.x-ubuntu-latest' artifact from the main CI run. |
Code ReviewIndependent proposal: Given the problem (tool-call-only subagent rounds lose usage metadata), I'd relax two guards: (1) emit ROUND_TEXT in Comparison with the diff: The PR does exactly this — no more, no less. Two production files, 20 lines total. The approach matches my independent proposal precisely. No critical blockers. No AGENTS.md violations. The change is minimal, correctly scoped, and follows existing patterns. The One observation: the model consistently produces thought text in every round with the current provider, so the usage-only path (no text, no thought, only tool calls) is model-dependent. The unit tests correctly mock this scenario. The fix is still valid — different models or provider configurations may not emit thought text. Real-Scenario TestingRan the built Subagent JSONL transcript (parsed for readability): Both model rounds (records [1] and [4]) carry Second run (shell command subagent): Same pattern — usage persisted on every model round. Unit TestsAll four affected test files pass: 150 tests total, 0 failures. The new test in 中文说明代码审查独立方案: 针对纯工具调用 subagent 回合丢失 usage metadata 的问题,我会放宽两个守卫:(1) 与 diff 对比: PR 的实现与我的独立方案完全一致——不多不少。两个生产文件,共 20 行。 无关键阻塞项。无 AGENTS.md 违规。改动最小化、范围正确、遵循现有模式。 一个观察:当前 provider 的模型在每个回合都会产生 thought 文本,因此纯 usage 路径(无文本、无 thought、仅工具调用)取决于模型。单元测试正确地 mock 了这个场景。修复仍然有效——不同模型或 provider 配置可能不产生 thought 文本。 真实场景测试使用构建后的 Subagent JSONL transcript 显示两个模型回合都携带了完整的 单元测试四个受影响的测试文件全部通过:150 个测试,0 失败。 — Qwen Code · qwen3.8-max-preview Reviewed at |
|
Confidence: 5/5 — Clean across every stage; would merge without hesitation. This is a textbook minimal fix. The problem is real and well-evidenced — the old code explicitly dropped usage-only rounds (the old test name said so: "drops usage-only ROUND_TEXT to keep the canonical view valid"). The fix relaxes exactly two guard conditions, adds nothing unnecessary, and the resume path already handles the new record shape correctly via The implementation matches my independent proposal precisely — I couldn't find a simpler path. 20 production lines, 79 test lines, no scope creep. The real-scenario runs confirm usage metadata persists on every model round, and the unit tests explicitly cover the tool-call-only path that the current model doesn't naturally trigger. If I had to maintain this in six months, I'd thank the author — the code is clearer than what it replaced. 中文说明置信度:5/5 — 每个阶段都干净,毫不犹豫可以合并。 这是一个教科书式的最小化修复。问题真实且有充分证据——旧代码显式丢弃了纯 usage 回合(旧测试名称就是 "drops usage-only ROUND_TEXT to keep the canonical view valid")。修复恰好放宽了两个守卫条件,没有添加任何不必要的东西,恢复路径已通过 实现与我的独立方案完全一致——找不到更简路径。20 行生产代码,79 行测试代码,无范围蔓延。真实场景运行确认每个模型回合都持久化了 usage metadata,单元测试明确覆盖了当前模型不会自然触发的纯工具调用路径。 如果六个月后我要维护这段代码,我会感谢作者——代码比替换前的更清晰。 — Qwen Code · qwen3.8-max-preview Reviewed at |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed — no blockers. Suggestions are inline.
— qwen3.7-max via Qwen Code /review
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
No issues found. LGTM! ✅
— qwen3.7-max via Qwen Code /review
Review —
|
🔬 Local Build & Test Verification ReportEnvironment: macOS (darwin-arm64), Node.js v22.22.2, vitest 3.2.4 1. Build Verification
2. Unit Tests (110 passed)Ran all three test files touched by this PR plus the closely related
Key PR-specific tests verified:
3. E2E Real-World TestRan a real headless session spawning a foreground sub-agent ( Session: JSONL transcript analysis (
Observations:
4. Code Review NotesThe change is minimal and well-scoped (3 production files, +9/−11 lines):
5. Verdict✅ Ready to merge. The change is correct, minimal, well-tested, and backward compatible. CI is green. Local build + unit tests + E2E all pass. 🇨🇳 中文版本🔬 本地构建与测试验证报告环境: macOS (darwin-arm64), Node.js v22.22.2, vitest 3.2.4 1. 构建验证
2. 单元测试(110 项通过)运行了本 PR 修改的三个测试文件以及密切相关的
PR 关键测试验证:
3. E2E 真实测试运行了一个真实的 headless 会话,生成前台 sub-agent( 会话 ID: JSONL transcript 分析:
4. 代码审查要点改动精简且范围明确(3 个生产文件,+9/−11 行):
5. 结论✅ 可以合入。 改动正确、精简、测试充分、向后兼容。CI 全绿。本地构建 + 单元测试 + E2E 均通过。 |


What this PR does
This change persists provider usage metadata for every sub-agent model round, including responses that contain only tool calls and no text or reasoning. Usage-only rounds are written as assistant records with an empty model message so trajectory exporters can consume their token fields, while existing text and reasoning records keep their current shape.
Why it's needed
Sub-agents already emit per-round usage at runtime, but the sidechain transcript was only materialized when a response contained text or reasoning. Tool-heavy agents therefore lost prompt, completion, and cached-token usage for tool-call-only rounds. Harbor can recursively aggregate sub-agent trajectories, but incomplete Qwen Code sidechains still caused root
final_metricsand cache-hit statistics to be under-counted.Reviewer Test Plan
How to verify
Run a foreground or background sub-agent that must inspect a file before answering. In the generated per-agent JSONL, confirm that both the tool-call-only model round and the final text round contain exactly one
usageMetadataobject. The tool-call-only usage record should havemessage: {"role":"model","parts":[]}. Resume a persisted background sub-agent and confirm the usage-only record is not added to model history. The focused transcript and headless-agent tests cover these behaviors.Evidence (Before & After)
N/A — this changes persisted transcript metadata and has no TUI presentation change.
Tested on
Environment (optional)
Node.js 22; local package build, TypeScript typecheck, focused ESLint/Prettier checks, and core unit tests.
Risk & Scope
ChatRecordshape and are already filtered from resumed model history.Linked Issues
N/A
中文说明
这个 PR 做了什么
这个改动会为每一个 sub-agent 模型回合持久化 provider usage metadata,包括只有工具调用、没有文本或思考内容的响应。仅包含 usage 的回合会写成带空 model message 的 assistant 记录,以便 trajectory exporter 消费其中的 token 字段;已有文本和思考记录的格式保持不变。
为什么需要
Sub-agent 运行时已经会产生逐回合 usage,但之前只有响应包含文本或思考内容时才会生成 sidechain transcript 记录。因此,工具调用密集型 agent 会丢失纯工具调用回合的 prompt、completion 和 cached-token usage。Harbor 可以递归聚合 sub-agent trajectory,但 Qwen Code sidechain 本身不完整时,根
final_metrics和缓存命中统计仍会被低估。Reviewer 测试计划
如何验证
运行一个必须先读取文件再回答的前台或后台 sub-agent。检查生成的 per-agent JSONL,确认纯工具调用模型回合和最终文本回合都各自只包含一个
usageMetadata。纯工具调用的 usage 记录应包含message: {"role":"model","parts":[]}。恢复一个已持久化的后台 sub-agent,并确认 usage-only 记录不会加入模型历史。定向 transcript 和 headless-agent 测试已覆盖这些行为。证据(修改前后)
N/A——该改动只影响持久化 transcript metadata,不改变 TUI 展示。
测试平台
环境(可选)
Node.js 22;已执行本地 package build、TypeScript typecheck、定向 ESLint/Prettier 检查和 core 单元测试。
风险与范围
ChatRecord格式,并且在恢复模型历史时已经会被过滤。关联 Issue
N/A