Skip to content

fix(cli): Bound ACP textual tool-result payloads - #8450

Merged
doudouOUC merged 4 commits into
mainfrom
fix/acp-tool-result-wire-budget
Aug 5, 2026
Merged

fix(cli): Bound ACP textual tool-result payloads#8450
doudouOUC merged 4 commits into
mainfrom
fix/acp-tool-result-wire-budget

Conversation

@doudouOUC

@doudouOUC doudouOUC commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

What this PR does

This PR adds an internal ACP transport projection for canonical textual tool-call fields in every status. Eligible pure-text content arrays and primitive-string rawOutput values are bounded independently to 65,536 UTF-8 bytes as serialized JSON fields, using exact JSON escape accounting, deterministic 20/80 head-tail previews, and bounded multi-block allocation. Live updates and every replay collector are covered, while A2UI and structured or rich content remain unchanged.

The projection is immutable and idempotent. Canonical transcripts, model-facing responses, artifact metadata, ACP schemas, public APIs, and offline exports are not changed. The design documentation records the contract and updates the older statement that immediate ACP display events are always unchanged.

Why it's needed

Phase 1 bounds model-facing tool responses, but ACP display construction can still duplicate a complete tool result in both content and rawOutput after model finalization. A deterministic fake-MCP reproduction produced a 1,000,385-byte ACP frame for one 499,999-byte result on origin/main. This PR limits the eligible display fields at the actual live and replay delivery boundaries without making transport loss canonical.

Reviewer Test Plan

How to verify

Run the focused projector, live Session, replay collector, and offline export tests from packages/cli. Confirm the JSON-byte threshold cases pass at 65,535, 65,536, and 65,537 bytes; Unicode, escape, surrogate, multi-block, A2UI, rich-content, idempotence, and structural-sharing cases remain correct; live and replay fields are each at most 65,536 bytes; and offline export preserves the original length and SHA-256.

Run the root build and typecheck, then bundle and run the serve fast-path closure check. The candidate should build without changing the fast-path bundle closure. Repeating the deterministic fake-MCP exchange should retain both fields, produce parseable JSON, and keep the ordinary textual fixture frame below 256 KiB.

Evidence (Before & After)

The same fake OpenAI response and stdio fake MCP tool returned 499,999 ASCII bytes to both bundles. Only lengths and SHA-256 values were captured; no result body was printed.

Measurement origin/main (be4f5e0963) Candidate (cc1ceddfde)
content JSON UTF-8 bytes 500,055 65,536
rawOutput JSON UTF-8 bytes 500,001 65,482
Complete ACP NDJSON frame bytes 1,000,385 131,347

Source fixture: 499,999 bytes, SHA-256 146e22690b10e8ea73a95f10a7399f26318528775e4479cc9f078f42e79c78a4. The offline export regression preserves the original source length and SHA-256. The candidate frame result applies only to this ordinary textual fixture and is not a universal frame-size contract.

Tested on

OS Status
🍏 macOS ✅ tested
🪟 Windows ⚠️ not tested
🐧 Linux ⚠️ not tested

Environment (optional)

macOS 26.4.1, Node.js 24.12.0, npm 10.9.8, local production bundles, sandbox disabled.

Risk & Scope

  • Main risk or tradeoff: Eligible ACP textual display fields become intentionally lossy above 65,536 JSON UTF-8 bytes. The marker does not claim recovery when no reusable producer artifact exists.
  • Not validated / out of scope: Structured and rich payloads, A2UI, generic NDJSON frame caps, backpressure, replay aggregate limits, Headless output, diagnostics, and artifact lifecycle remain unchanged and may still carry large values.
  • Breaking changes / migration notes: None. Field presence, field types, schemas, canonical transcripts, model input, and offline export remain compatible.

Linked Issues

Refs #8447
Refs #7306
Refs #8091

中文说明

本 PR 做了什么

本 PR 为所有状态的 ACP tool-call update 中的规范文本字段增加内部 transport projection。符合条件的纯文本 content 数组与 primitive string rawOutput 会分别限制在字段 JSON 序列化后 65,536 UTF-8 字节以内,使用精确的 JSON 转义字节计算、确定性的 20/80 头尾预览以及有界的多 block 预算分配。live update 和所有 replay collector 都受到覆盖,A2UI、结构化或富内容保持不变。

投影过程不可变且幂等。canonical transcript、模型侧响应、artifact metadata、ACP schema、公共 API 和离线 export 均不改变。设计文档记录了该契约,并修正了旧文档中“即时 ACP display event 始终不变”的表述。

为什么需要

Phase 1 已限制模型侧工具响应,但 ACP display 会在模型 finalization 之后仍将完整工具结果同时放入 contentrawOutput,形成重复放大。确定性的 fake-MCP 复现在 origin/main 上仅一个 499,999 字节结果就产生了 1,000,385 字节 ACP frame。本 PR 在实际 live 和 replay 交付边界限制符合条件的 display 字段,同时避免让 transport 有损结果进入 canonical 数据。

Reviewer 测试计划

如何验证

packages/cli 运行 projector、live Session、replay collector 和 offline export 的聚焦测试。确认 65,535、65,536、65,537 字节阈值用例通过;Unicode、转义、代理对、多 block、A2UI、富内容、幂等和结构共享用例保持正确;live 与 replay 字段分别不超过 65,536 字节;offline export 保留原始长度和 SHA-256。

运行根目录 build 和 typecheck,然后执行 bundle 与 serve fast-path 闭包检查。候选版本应正常构建且不改变 fast-path bundle 闭包。重复确定性的 fake-MCP 交换后,两个字段都应继续存在,JSON 可解析,普通文本 fixture frame 小于 256 KiB。

证据(修改前后)

两个 bundle 使用完全相同的 fake OpenAI 响应和 stdio fake MCP 工具,返回 499,999 ASCII 字节。只采集长度和 SHA-256,没有打印结果正文。

测量项 origin/main (be4f5e0963) 候选版本 (cc1ceddfde)
content JSON UTF-8 字节 500,055 65,536
rawOutput JSON UTF-8 字节 500,001 65,482
完整 ACP NDJSON frame 字节 1,000,385 131,347

源 fixture 为 499,999 字节,SHA-256 是 146e22690b10e8ea73a95f10a7399f26318528775e4479cc9f078f42e79c78a4。offline export 回归测试保留原始源长度和 SHA-256。候选 frame 结果只适用于这一普通文本 fixture,不是通用 frame 大小契约。

测试平台

OS 状态
🍏 macOS ✅ 已测试
🪟 Windows ⚠️ 未测试
🐧 Linux ⚠️ 未测试

环境(可选)

macOS 26.4.1、Node.js 24.12.0、npm 10.9.8、本地 production bundle、未启用 sandbox。

风险与范围

  • 主要风险或权衡:符合条件的 ACP 文本 display 字段超过 65,536 JSON UTF-8 字节后会有意变为有损预览。没有可复用 producer artifact 时,marker 不承诺可以恢复完整结果。
  • 未验证 / 范围外:结构化和富 payload、A2UI、通用 NDJSON frame 上限、backpressure、replay aggregate limit、Headless output、诊断与 artifact lifecycle 均保持不变,仍可能携带大值。
  • 破坏性变更 / 迁移说明:无。字段存在性、字段类型、schema、canonical transcript、模型输入和 offline export 均保持兼容。

关联 Issue

Refs #8447
Refs #7306
Refs #8091

@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Qwen Triage finishedview run. See the stage comments in this thread for the result.

Qwen Triage 已完成 —— 查看运行。结果见本线程中的各阶段评论。

@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Re-run at an unchanged head (2f7daf6f) — triggered by a maintainer re-review request. No commits have landed since the previous pass; what changed is the surrounding state: CI on this commit has now settled (one red check, classified in Stage 2), @wenshao approved, and the sandboxed verify lane is in flight as part of this triage run. Gate findings are unchanged.

Template looks good ✓

Problem: observed and measured, not theoretical. The deterministic fake-MCP reproduction showed a single 499,999-byte tool result producing a 1,000,385-byte ACP frame on origin/main, because display construction duplicates the full result into both content and rawOutput after model finalization. This is the documented Phase 2 follow-up (#8447) to the merged Phase 1 model-facing budget, which explicitly left immediate ACP display events unchanged — the gap is real and known.

Direction: aligned. Phase 1 bounded the model-facing response and deliberately excluded ACP display events; this closes that exclusion at the live and replay delivery boundaries while keeping canonical transcripts, model input, and offline export lossless. The one awareness flag stands: the capped fields sit on the ACP wire surface external clients render — schema-compatible but intentionally lossy above 65,536 JSON UTF-8 bytes. That product decision is documented in the design doc, tracked in #8447/#8091, and has had two senior reviewers over exactly this surface.

Size: not applicable (cli-only change, no core paths). At the current head: 410 production lines (401 projector + 9 wiring), 697 test lines, 66 docs lines.

Approach: scope remains right — one projector applied at the live Session.sendUpdate chokepoint and the shared replay collector, plus boundary tests and the design-doc contract update. The newest commit on the branch is tests only (inner-content metadata exemption, the paired 1,191-block fit boundary, legacy toolName-only A2UI detection); the rebase kept main's newer Session screen/voice coverage alongside this PR's projection tests. No drive-by changes.

Risk: acp-integration matches the high-risk revert-correlated paths, so CI evidence is required before approval and review depth is full. CI on this head is now settled: everything green except Test (ubuntu-latest, Node 22.x), which failed on a single packages/core glob-test timeout this PR does not touch (classification and evidence in Stage 2); the macOS/Windows Node legs and the CLI integration leg are merge_group-only by ci.yml design. The sandboxed verify lane is running in this triage run and will post its report in this thread.

Moving on to code review. 🔍

中文说明

本次 re-run 针对未变化的 head(2f7daf6f)——由维护者的重新审查请求触发。上次审查之后没有新 commit 落地;变化的是周边状态:该 commit 的 CI 已经结束(一个红色检查项,定性见 Stage 2),@wenshao 已批准,沙箱 verify 通道正在本次 triage 运行中进行。门槛结论不变。

模板完整 ✓

问题:已观测且有实测数据,不是理论问题。确定性 fake-MCP 复现显示:一个 499,999 字节的工具结果在 origin/main 上产生了 1,000,385 字节的 ACP frame,因为 display 构建在模型 finalization 之后把完整结果同时放进了 contentrawOutput。这是已合并的 Phase 1 模型侧预算的文档化 Phase 2 后续(#8447)——Phase 1 明确保留了即时 ACP display event 不变,缺口真实且已知。

方向:对齐。Phase 1 限制了模型侧响应并刻意排除 ACP display event;本 PR 在 live 和 replay 交付边界补上这个排除项,同时保持 canonical transcript、模型输入和离线 export 无损。提醒仍然存在:被限制的字段位于外部客户端渲染的 ACP wire 表面——schema 兼容,但超过 65,536 JSON UTF-8 字节后有意有损。这个产品决策有设计文档记录、在 #8447/#8091 中跟踪,且已有两位资深审查者审视过这一表面。

规模:不适用(仅 cli 改动,未触及核心路径)。当前 head:410 行生产代码(401 行 projector + 9 行接线)、697 行测试、66 行文档。

方案:范围仍然合理——一个 projector 应用于 live Session.sendUpdate chokepoint 和共享 replay collector,加上边界测试与设计文档契约更新。分支上最新的 commit 只有测试(内部 content 元数据豁免、成对的 1,191 块容纳边界、仅 toolName 的旧式 A2UI 检测);rebase 保留了 main 上较新的 Session screen/voice 覆盖与本 PR 的投影测试。无顺手改动。

风险:acp-integration 命中与 revert 相关的高风险路径,因此批准前必须有 CI 证据,review 深度为完整深度。本 head 的 CI 已结束:除 Test (ubuntu-latest, Node 22.x) 外全部为绿;该检查项因一个本 PR 完全未触及的 packages/core glob 测试超时而失败(定性与证据见 Stage 2);macOS/Windows Node 任务与 CLI 集成任务按 ci.yml 设计仅在 merge_group 运行。沙箱 verify 通道正在本次 triage 运行中进行,报告会发布在本线程。

进入代码审查 🔍

Qwen Code · qwen3.8-max

Reviewed at 2f7daf6f5c2957693cc4b4cba2291119cd6eba1f · re-run with @qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Code review

Re-reviewed at 2f7daf6f (unchanged head — this pass was triggered by a maintainer re-review request, not by new commits). Production code is identical to the state reviewed last pass: one projector module, one line of wiring at the live chokepoint, one projection inside the shared replay collector. My independent baseline for this problem is unchanged — a pure projector at the single live delivery chokepoint plus the shared replay collector, strict shape gating, exact JSON-byte accounting, idempotent by construction — and the PR still matches it.

What I verified against the tree and diff on this pass, beyond the prior pass's checks:

  • Session.sendUpdate remains the only production call site of client.sessionUpdate (grep across all packages, tests/docs excluded). The sessionUpdate() helper in the serve live-coordinator that looks similar is a bus-event parser for routing logic, not a second delivery path.
  • Bus consumers downstream of the projection are safe against truncation: the live-coordinator's workerIdFromEvent matches create_sub_session output with a fully anchored regex over a short builtin string, and the compaction engine consumes the display stream only to build the bounded replay window for late-joining clients — neither feeds model context, which stays on the lossless canonical transcript.
  • The byte accounting matches native JSON.stringify semantics case-by-case (short escapes, \u00XX controls, surrogate pairs at 4 UTF-8 bytes, lone surrogates as 6-byte escapes), head/tail selection never splits a surrogate pair in either direction, every scan is budget-capped, the water-fill allocation is deterministic (index-ordered remainder), and the final native-serialization check with fallback collapse guarantees the cap even if the allocation math ever drifted.
  • A2UI exemption reuses the daemon's own isA2uiToolMeta (confirmed exported from @qwen-code/acp-bridge/bridgeClient, covering both serverId-based and legacy toolName-only shapes) instead of a private heuristic; the content-block eligibility gate is exact-key at both levels, so blocks carrying extra fields (e.g. annotations) stay exempt.
  • Offline export (ui/utils/export/collect.ts) keeps its own collector and sendUpdate, correctly outside the projection, with a SHA-256 regression test pinning losslessness.

The tests pin the change: the 65,535/65,536/65,537 boundary cases, the 1,191/1,192 block-count pair, the exemption and idempotence regressions all fail if the projection is removed or loosened — this is not a suite that passes with the diff reverted. The packages/cli suite on this commit confirms it at scale (details below).

Two senior reviewers have been over this surface at the equivalent pre-rebase commit: @wenshao's measurement-based review (300-case seeded fuzz of the extracted projector) found no correctness bug — his findings are design/robustness/observability; @yiliang114 approved twice with independent fuzz verification, and both approvals were dismissed only by the author's pushes, never by a finding. Their remaining items are tracked below as follow-ups, not blockers.

sequenceDiagram
    participant P1 as Tool producer
    participant P2 as Session.sendUpdate (live)
    participant P3 as Replay collector
    participant P4 as Text projector
    participant P5 as ACP client
    participant P6 as Canonical transcript
    P1->>P6: record lossless tool result
    P1->>P2: tool_call_update with text result
    P2->>P4: project eligible text fields
    P4-->>P5: bounded update frame
    P6->>P3: session load or paged replay
    P3->>P4: project eligible text fields
    P4-->>P5: bounded replay frame
Loading
Files changed (9 of 9 shown)
File What changed
packages/cli/src/acp-integration/session/acp-tool-result-text-projection.ts The pure, immutable, idempotent projector: JSON-byte accounting, 20/80 previews, water-fill multi-block allocation with scan caps
packages/cli/src/acp-integration/session/acp-tool-result-text-projection.test.ts Boundary cases at 65,535/65,536/65,537 bytes, escape and surrogate accounting, seeded fuzz vs native JSON, allocation, exemptions, idempotence
packages/cli/src/acp-integration/session/Session.ts One-line wiring at the single live sendUpdate chokepoint
packages/cli/src/acp-integration/session/Session.test.ts Live delivery bound check plus reference-equality idempotence for live and replay re-delivery
packages/cli/src/acp-integration/session/history-replay-page.ts Projects inside the shared replay collector context covering all replay entry points
packages/cli/src/acp-integration/session/history-replay-page.test.ts Bounds checks for bulk and paged replay collection
packages/cli/src/ui/utils/export/collect.test.ts Regression guard proving offline export stays lossless (no production code touched)
docs/design/2026-08-03-acp-tool-result-text-projection.md New design doc recording the contract, exemptions, and non-goals
docs/design/final-tool-response-budget.md Replaces the Phase 1 statement that immediate ACP display events remain unchanged

Test evidence

Evidence below is the PR's own CI on the reviewed commit, read through the API — I do not run PR code in this environment. CI has now settled at this head. Everything is green except one check: Test (ubuntu-latest, Node 22.x) failed, and the failure is not caused by this PR:

  • The single failing test is src/tools/glob.test.ts > GlobTool > execute > should allow path outside workspace (external path support) in packages/coreError: Test timed out in 15000ms. The test globs the runner's /tmp and exceeded the 15 s per-test timeout under runner load; the other 59 tests in the same file passed at ~55 ms each, and the remaining 19,234 core tests passed.
  • This PR touches zero files in packages/core (all changes are under packages/cli/src/acp-integration/session/ plus docs), and nothing in the diff touches glob, path resolution, or anything the failing test exercises — the timeout is an environment-dependent flake.
  • The suite that does cover this PR passed completely: packages/cli ran 772 test files, 17,675 passed / 22 skipped, 0 failed — including all of this PR's projector, live-Session, replay, and export tests. Daemon E2E, the SDK Java matrix on all three platforms, Desktop Shell, and the web-shell smoke also passed. (macOS/Windows Node legs and the CLI integration leg are merge_group-only by ci.yml design — skipped on every pull_request event, not a fail-fast consequence.)
  • For context, main's own most recent CI run (2026-08-04) also failed — on an unrelated git refspec error during checkout — so red noise on this workflow is not new.

Not verified: the frame-level before/after numbers (1,000,385 → 131,347 bytes) remain the author's macOS measurements until the sandboxed verify report below posts; the unit suite pins the algorithm, and two independent reviewers fuzz-verified the byte accounting, but the end-to-end frame claim itself is author-reported.

Check Conclusion
Test (ubuntu-latest, Node 22.x) ❌ failure — 1 unrelated flaky test (see analysis above)
Real daemon E2E / Java 11 ✅ success
Desktop Shell (ubuntu-22.04) ✅ success
web-shell E2E Smoke (ubuntu-latest, Node 22.x) ✅ success
Post Coverage Comment (ubuntu-latest, 22.x) ✅ success
ubuntu-latest / Java 11 ✅ success
ubuntu-latest / Java 17 ✅ success
ubuntu-latest / Java 21 ✅ success
macos-latest / Java 21 ✅ success
windows-latest / Java 21 ✅ success
Classify PR ✅ success
Test (macos-latest, Node 22.x) ⏭️ skipped (merge_group only)
Test (windows-latest, Node 22.x) ⏭️ skipped (merge_group only)
Integration Tests (CLI, No Sandbox) ⏭️ skipped (merge_group only)

One row per check name (latest run); failures sort first. / 每个检查名一行(取最新一次运行),失败项排在最前。

Sandboxed verification is in flight on this very run — the verify job of the current Qwen Triage run is executing now, and its report will post in this thread. It settles the one claim static review and the unit suite cannot: the frame-level before/after in a real MCP exchange (both fields present and parseable, the ~1 MB frame down to ~131 KB), which today still traces to the author's macOS measurements. The algorithm-level gap that lane would otherwise close is already substantially covered by the two independent fuzz verifications at the equivalent commit.

Non-blocking follow-ups, unchanged from the last pass and still open:

  1. Observability (wenshao Where is the config saved? #2): projection is silent — no debug log or _meta flag distinguishing display truncation from producer truncation. A createDebugLogger line with { toolCallId, field, originalBytes } would pay for itself on the first "my output is cut off" report.
  2. Design-doc consistency (wenshao TypeError in Authentication Selection Interface #5): final-tool-response-budget.md's Problem paragraph and Invariant 7 still read as if display events were untouched; worth an amendment note.
  3. copyString idiom (wenshao OpenAI API Error: 401 Incorecct API Key provided #6): split('').join('') copies correctly but ~37× slower than a utf16le round trip; one-line change on the send path.
  4. Marker recovery hint (wenshao 如何自定义密钥文件 .env可能与其他文件冲突 #3): when _meta.artifacts exists, reusing phase 1's artifact-path header would give users the recovery path that already sits two fields away.
  5. Residual >256 KB vectors (yiliang114): rawInput on tool_call start frames, structured non-string rawOutput, non-canonical blocks, and bulk-replay aggregates remain unbounded — deferred to tracking(serve): Split daemon resource protection into reviewable PRs #8091/Bound textual tool-result display payloads across ACP and headless output #8447/Add privacy-safe tool-result boundary diagnostics #8448/Harden tool-output budgeting, observability, and artifact lifecycle #7306 by design; the description scopes this PR to canonical textual result fields.
中文说明

代码审查

2f7daf6f 重新审查(head 未变化——本次由维护者的重新审查请求触发,而非新 commit)。生产代码与上次审查时完全一致:一个 projector 模块、live chokepoint 处一行接线、共享 replay collector 内一处投影。我为这个问题独立拟的方案不变——在唯一 live 交付 chokepoint 和共享 replay collector 上做纯投影、严格形状门控、精确 JSON 字节计数、构造上幂等——PR 仍然与该思路一致。

本次在树与 diff 上核实的内容(在上次核实之外):

  • Session.sendUpdate 仍是 client.sessionUpdate 唯一的生产调用点(全 packages grep、排除测试与文档)。serve live-coordinator 中名字相似的 sessionUpdate() 辅助函数是用于路由逻辑的 bus 事件解析器,不是第二条交付路径。
  • 投影下游的 bus 消费者对截断是安全的:live-coordinator 的 workerIdFromEvent 用全锚定正则匹配 create_sub_session 的短内建输出;compaction engine 消费 display 流只是为了给晚加入的客户端构建有界 replay 窗口——两者都不进入模型上下文,模型上下文仍然走无损的 canonical transcript。
  • 字节计数逐情形与原生 JSON.stringify 语义一致(短转义、\u00XX 控制字符、代理对 4 UTF-8 字节、孤立代理 6 字节转义),头尾选取在两个方向都不拆分代理对,所有扫描都有预算上限,水位线分配是确定性的(按 index 分配余数),最终原生序列化校验加折叠兜底保证上限必然成立。
  • A2UI 豁免复用 daemon 自己的 isA2uiToolMeta(确认自 @qwen-code/acp-bridge/bridgeClient 导出,覆盖 serverId 形态与仅 toolName 的旧式形态),而非私有启发式;content 块资格门控在两层都是精确键匹配,携带额外字段(如 annotations)的块保持豁免。
  • 离线 export(ui/utils/export/collect.ts)保留自己的 collector 与 sendUpdate,正确位于投影之外,SHA-256 回归测试钉住无损性。

测试钉住了改动本身:65,535/65,536/65,537 字节边界用例、1,191/1,192 块数成对用例、豁免与幂等回归,在移除或放宽投影时都会失败——这不是"去掉 diff 也一样绿"的套件。该 commit 上的 packages/cli 套件在规模上确认了这一点(详见下方)。

两位资深审查者在等价 pre-rebase commit 上看过这一表面:@wenshao 的实测型审查(300 例固定种子 fuzz)未发现正确性 bug;@yiliang114 以独立 fuzz 验证两次批准,其批准均只因作者推送而被 dismiss。其余事项记录为后续跟进,不构成阻塞。

(时序图与文件清单见上方英文部分。)

测试证据

以下证据来自被审 commit 的 PR 自有 CI(通过 API 读取)——本环境不运行 PR 代码。该 head 的 CI 现已结束:除一项外全部为绿。唯一失败的 Test (ubuntu-latest, Node 22.x) 不是本 PR 导致

  • 唯一失败用例是 packages/coresrc/tools/glob.test.ts > should allow path outside workspace (external path support)——Error: Test timed out in 15000ms.。该测试对 runner 的 /tmp 做 glob,在 runner 负载下超过了 15 秒单测超时;同文件其余 59 个用例各约 55 ms 通过,其余 19,234 个 core 测试全部通过。
  • 本 PR 未触及 packages/core 的任何文件(全部改动位于 packages/cli/src/acp-integration/session/ 与文档),diff 中没有任何内容涉及 glob、路径解析或该用例所测逻辑——这是一个依赖运行环境的超时 flake。
  • 覆盖本 PR 的套件全部通过packages/cli 运行 772 个测试文件,17,675 通过 / 22 跳过 / 0 失败——包括本 PR 全部 projector、live Session、replay 与 export 测试。daemon E2E、三平台 SDK Java 矩阵、Desktop Shell、web-shell 冒烟也均通过。(macOS/Windows Node 任务与 CLI 集成任务按 ci.yml 设计仅在 merge_group 运行——在每个 pull_request 事件上都跳过,并非 fail-fast 后果。)
  • 背景信息:main 最近一次 CI(2026-08-04)同样失败——原因是 checkout 阶段的无关 git refspec 错误——该工作流的红色噪声并非新现象。

未验证:frame 级 before/after 数字(1,000,385 → 131,347 字节)在下方沙箱 verify 报告发布之前仍是作者在 macOS 上的测量;单元测试钉住了算法本身,两位独立审查者 fuzz 验证过字节计数,但端到端 frame 结论仍为作者自述。

沙箱验证正在本次运行中进行——当前 Qwen Triage 运行的 verify job 正在执行,报告会发布在本线程。它将补齐静态审查与单元测试无法覆盖的唯一结论:真实 MCP 交换下的 frame 级 before/after(两个字段仍存在且可解析、约 1 MB frame 降至约 131 KB)。该通道原本要补的算法级缺口已由等价 commit 上的两次独立 fuzz 验证相当程度覆盖。

非阻塞跟进事项与上次一致且仍未处理:可观测性(wenshao #2)、设计文档一致性(wenshao #5)、copyString 写法(wenshao #6)、marker 恢复提示(wenshao #3)、残余 >256 KB 向量(yiliang114,按设计延后到 #8091/#8447/#8448/#7306)。详见英文部分。

Qwen Code · qwen3.8-max

Reviewed at 2f7daf6f5c2957693cc4b4cba2291119cd6eba1f · re-run with @qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Confidence: 4/5 — clean re-review at an unchanged head; the only thing withholding approval is one red CI check that this PR demonstrably did not cause, plus the verify report still in flight.

CI has settled since the last pass, so the honest state of play: the code review found nothing new to doubt — I re-verified the wiring and the blast radius independently this pass (Session.sendUpdate is the only live delivery site; bus consumers downstream of the projection don't depend on lossless payloads; the projector's byte accounting matches native serialization case-by-case; the tests fail if the projection is removed). The single red check, Test (ubuntu-latest, Node 22.x), failed on one packages/core glob test timing out while globbing the runner's /tmp — a file and a package this PR doesn't touch at all, while the packages/cli suite that covers every line of this diff went 772 files / 17,675 tests with zero failures. Details and the classification evidence are in Stage 2.

Stepping back: my independent baseline for this problem is still the shape this PR takes — a pure projector at the single live delivery chokepoint and the shared replay collector, strict eligibility gating, idempotent by construction. The production code under review is identical to what two senior reviewers already passed at the equivalent pre-rebase commit, and @wenshao has now approved this exact commit (2f7daf6f) as well — that is one of the two required approvals, a separate vote from the bot's.

Why I am not approving in this pass, despite the verdict: the bot's approval attests to the state of the reviewed commit, and that commit's required unit-suite check is red. Even with the failure classified as an unrelated environmental flake, approving now would attest to a green that does not exist — and the repo's own finalize workflow holds approvals to the same standard. The unblock is mechanical: re-run the failed job (the author has write access, as does any maintainer). The standing approve-on-green marker below means no further /triage is needed — once the re-run lands green on 2f7daf6f, the finalize workflow issues the commit-pinned approval automatically. If the re-run lands red on anything plausibly related to this PR, it withholds it and flags the status comment instead.

What keeps this from a 5: the same open non-blocking follow-ups from @wenshao's review — observability (a projected frame is currently silent), the design-doc consistency note, the copyString idiom, and the optional artifact-path recovery hint. All recorded in Stage 2 so nothing is silently dropped; none block this diff.

Verdict: approve once CI is green on the reviewed commit. ✅

中文说明

回顾全局:我为这个问题独立拟的方案仍然是本 PR 的形态——在唯一 live 交付 chokepoint 和共享 replay collector 上做纯投影、严格资格门控、构造上幂等。本次我独立重新核实了接线与影响面(Session.sendUpdate 是唯一的 live 交付点;投影下游的 bus 消费者不依赖无损载荷;projector 的字节计数逐情形与原生序列化一致;移除投影则测试失败)。唯一的红色检查项 Test (ubuntu-latest, Node 22.x) 失败于一个 packages/core glob 测试在对 runner 的 /tmp 做 glob 时超时——文件与包都是本 PR 完全未触及的;而覆盖本 diff 每一行的 packages/cli 套件 772 个文件 / 17,675 个测试零失败。细节与定性证据见 Stage 2。

本次不发出批准的原因:机器人的批准是对被审 commit 状态的背书,而该 commit 的必选单测检查目前是红色。即使该失败已被定性为无关的环境性 flake,现在批准也是在为一个不存在的绿色背书——仓库自己的 finalize 工作流对批准也是同一标准。解锁动作是机械的:重跑失败的 job(作者与维护者都有 write 权限)。下方常驻的 approve-on-green 标记意味着无需再次 /triage——一旦重跑在 2f7daf6f 上变绿,finalize 工作流会自动发出绑定该 commit 的批准;如果重跑在任何与本 PR 可能相关的内容上变红,它会拒绝批准并在状态评论中标记。

不到 5 分的原因:@wenshao 审查中仍未处理的非阻塞跟进事项——可观测性、设计文档一致性说明、copyString 写法、可选的 artifact 路径恢复提示。均已记录在 Stage 2,不会被悄悄丢掉;都不阻塞本 diff。

结论:在被审 commit 的 CI 全绿后批准。✅(CI 全绿后的批准动作由 finalize 工作流执行。)

Qwen Code · qwen3.8-max

Reviewed at 2f7daf6f5c2957693cc4b4cba2291119cd6eba1f · re-run with @qwen-code /triage

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Summary

Package Lines Statements Functions Branches
CLI 83.3% 83.3% 89.36% 82.4%
Core N/A% N/A% N/A% N/A%
CLI Package - Full Text Report
-------------------|---------|----------|---------|---------|-------------------
File               | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------------|---------|----------|---------|---------|-------------------
All files          |    83.3 |     82.4 |   89.36 |    83.3 |                   
 src               |   84.77 |    80.78 |   88.39 |   84.77 |                   
  cli.ts           |   95.68 |    84.11 |     100 |   95.68 | ...60-561,565-566 
  gemini.tsx       |   72.66 |    76.99 |   80.76 |   72.66 | ...1280-1284,1405 
  ...ractiveCli.ts |   86.49 |    80.46 |   87.93 |   86.49 | ...2926,2932,2994 
  ...liCommands.ts |   89.11 |    85.15 |      90 |   89.11 | ...90,507,541,663 
  ...ActiveAuth.ts |     100 |     87.5 |     100 |     100 | 66-80             
 ...cp-integration |   70.24 |     72.9 |   89.96 |   70.24 |                   
  acpAgent.ts      |   69.93 |    72.78 |      90 |   69.93 | ...77,11882-11884 
  authMethods.ts   |      92 |       60 |     100 |      92 | 33-34             
  errorCodes.ts    |       0 |        0 |       0 |       0 | 1-22              
  ...ion-skills.ts |     100 |    88.23 |     100 |     100 | 17,32             
  generation.ts    |    97.1 |    81.25 |     100 |    97.1 | 109,112           
  ...DirContext.ts |     100 |      100 |     100 |     100 |                   
 ...ration/service |   97.04 |    95.71 |   93.33 |   97.04 |                   
  filesystem.ts    |   97.04 |    95.71 |   93.33 |   97.04 | ...21-122,238-239 
 ...ration/session |   90.88 |    86.21 |   96.29 |   90.88 |                   
  Session.ts       |   89.94 |     84.6 |   95.41 |   89.94 | ...81,10308-10312 
  ...entTracker.ts |    96.8 |    89.36 |      90 |    96.8 | 137-143,221       
  ...projection.ts |   98.57 |    93.29 |     100 |   98.57 | ...76,333,344,356 
  ...stop-guard.ts |     100 |    98.07 |     100 |     100 | 37,127            
  ...eplay-page.ts |   93.22 |    90.81 |     100 |   93.22 | 72,83-86,112-122  
  ...y-replayer.ts |   98.53 |    95.52 |     100 |   98.53 | 238-240           
  index.ts         |       0 |        0 |       0 |       0 | 1-40              
  ...ssionUtils.ts |   89.76 |    87.32 |     100 |   89.76 | ...54-270,326-328 
  tasksSnapshot.ts |    94.3 |     87.5 |     100 |    94.3 | 65-71             
  ...on-tracker.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...ssion/emitters |   95.79 |    93.52 |   96.66 |   95.79 |                   
  ...ageEmitter.ts |   95.34 |    94.28 |     100 |   95.34 | 52-59             
  PlanEmitter.ts   |     100 |       90 |     100 |     100 | 66                
  base-emitter.ts  |   78.26 |       75 |     100 |   78.26 | 23-24,26-28       
  index.ts         |       0 |        0 |       0 |       0 | 1-10              
  ...ll-emitter.ts |   99.18 |    96.47 |     100 |   99.18 | 355-356           
 ...ession/rewrite |    91.8 |    89.13 |   94.44 |    91.8 |                   
  LlmRewriter.ts   |    82.4 |     86.2 |     100 |    82.4 | ...,88-89,166-170 
  ...Middleware.ts |   96.96 |    88.09 |     100 |   96.96 | 144,152-154       
  TurnBuffer.ts    |     100 |      100 |     100 |     100 |                   
  config.ts        |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 src/agent-view    |   89.03 |    81.37 |   89.09 |   89.03 |                   
  ...t-cli-argv.ts |     100 |      100 |     100 |     100 |                   
  protocol.ts      |     100 |      100 |     100 |     100 |                   
  ...sor-client.ts |   80.38 |    72.54 |   76.66 |   80.38 | ...22-626,652-656 
  ...or-process.ts |   96.61 |    89.47 |   84.61 |   96.61 | 129-130,150-151   
  ...sor-runner.ts |    84.9 |     75.6 |      85 |    84.9 | ...44,468,471-481 
  ...sor-server.ts |   85.71 |    83.06 |   95.45 |   85.71 | ...67-468,471-488 
  ...isor-store.ts |   97.73 |    81.16 |     100 |   97.73 | ...92,594,607,643 
  ...nal-bridge.ts |   93.98 |     91.3 |   83.33 |   93.98 | 228-238           
 src/commands      |   89.43 |    72.18 |   64.51 |   89.43 |                   
  auth.ts          |     100 |    83.33 |     100 |     100 | 11,14             
  channel.ts       |   55.55 |      100 |       0 |   55.55 | 18-22,30-40       
  extensions.tsx   |   96.77 |      100 |      50 |   96.77 | 39                
  hooks.tsx        |   66.66 |      100 |       0 |   66.66 | 20-24             
  mcp.ts           |   95.45 |      100 |      50 |   95.45 | 31                
  review.ts        |   98.59 |      100 |      50 |   98.59 | 82                
  serve.ts         |   87.35 |    66.66 |     100 |   87.35 | ...97,709,725-729 
  sessions.ts      |     100 |      100 |      50 |     100 |                   
  update.ts        |   98.13 |    94.44 |   66.66 |   98.13 | 82-83             
 ...mmands/channel |    88.3 |    87.64 |    90.3 |    88.3 |                   
  channel-cwd.ts   |     100 |      100 |     100 |     100 |                   
  ...l-registry.ts |      80 |    84.61 |      80 |      80 | 37-40,49-52,63-66 
  ...entry-path.ts |      75 |       50 |     100 |      75 | 8-9               
  config-utils.ts  |   95.87 |    96.35 |     100 |   95.87 | ...08-213,271-274 
  configure.ts     |    14.7 |      100 |       0 |    14.7 | 18-21,23-84       
  daemon-worker.ts |    93.9 |    85.39 |   94.11 |    93.9 | ...1211,1218-1219 
  loop-runtime.ts  |   91.66 |      100 |      50 |   91.66 | 15,22             
  ...classifier.ts |   98.49 |    96.51 |     100 |   98.49 | 115-116,161       
  ...tact-store.ts |   93.51 |    87.65 |     100 |   93.51 | ...71,288-289,337 
  pairing.ts       |   72.85 |      100 |      50 |   72.85 | 22-28,57-68       
  pidfile.ts       |   95.55 |       90 |     100 |   95.55 | ...50-251,315-316 
  proxy.ts         |     100 |      100 |     100 |     100 |                   
  reload.ts        |    77.5 |    86.95 |      75 |    77.5 | 72-84,93-97       
  runtime.ts       |   82.43 |    86.44 |     100 |   82.43 | ...87-191,251-253 
  set.ts           |   75.72 |    85.71 |      50 |   75.72 | 65-83,111-116     
  start.ts         |    85.8 |    82.17 |      88 |    85.8 | ...85,591-594,606 
  ...ure-format.ts |   93.65 |    82.45 |     100 |   93.65 | ...42,48-49,74-75 
  status.ts        |   78.57 |    59.25 |   66.66 |   78.57 | ...36-137,150-161 
  stop.ts          |   57.83 |    82.35 |      50 |   57.83 | ...3,74-76,85-111 
 ...nds/extensions |   88.82 |    87.64 |   87.09 |   88.82 |                   
  consent.ts       |   72.53 |       90 |   42.85 |   72.53 | ...86-142,157-163 
  disable.ts       |     100 |       90 |     100 |     100 | 30                
  enable.ts        |     100 |    91.66 |     100 |     100 | 38                
  install.ts       |   82.95 |    81.57 |      75 |   82.95 | ...96-199,202-211 
  link.ts          |     100 |      100 |     100 |     100 |                   
  list.ts          |     100 |     87.5 |     100 |     100 | 18                
  new.ts           |     100 |      100 |     100 |     100 |                   
  settings.ts      |   99.15 |      100 |   83.33 |   99.15 | 151               
  sources.ts       |   93.42 |    87.09 |   92.85 |   93.42 | ...4-66,96-98,167 
  uninstall.ts     |   74.57 |       40 |   66.66 |   74.57 | 45-47,60-67,70-73 
  update.ts        |   96.71 |    97.05 |     100 |   96.71 | 114-118           
  utils.ts         |      75 |    53.84 |     100 |      75 | ...27-131,133-137 
 ...les/mcp-server |       0 |        0 |       0 |       0 |                   
  example.ts       |       0 |        0 |       0 |       0 | 1-60              
 ...amples/starter |       0 |        0 |       0 |       0 |                   
  example.ts       |       0 |        0 |       0 |       0 | 1-64              
 src/commands/mcp  |   90.17 |    84.39 |   83.33 |   90.17 |                   
  add.ts           |    99.3 |    96.07 |     100 |    99.3 | 154-155           
  approve.ts       |   76.19 |     87.5 |   66.66 |   76.19 | ...,89-99,114-124 
  list.ts          |   92.59 |    83.87 |      80 |   92.59 | ...62-164,180-181 
  reconnect.ts     |   78.85 |    66.66 |   85.71 |   78.85 | 42-55,169-191     
  remove.ts        |     100 |       80 |     100 |     100 | 21-25             
 ...ommands/review |   86.49 |    87.76 |    87.2 |   86.49 |                   
  agent-prompt.ts  |    92.1 |    92.95 |   96.42 |    92.1 | ...1954,2057-2136 
  base-tree.ts     |   74.87 |    79.16 |   77.77 |   74.87 | ...29-350,352-365 
  capture-local.ts |   68.57 |     90.9 |      75 |   68.57 | 107-111,158-189   
  ...k-coverage.ts |   46.92 |    13.33 |   66.66 |   46.92 | ...35-240,253-263 
  cleanup.ts       |   89.12 |    82.22 |   83.33 |   89.12 | ...99-504,506-507 
  ...ent-status.ts |   93.03 |    83.87 |   83.33 |   93.03 | 291,531-551       
  ...ose-review.ts |   96.11 |    91.88 |   95.83 |   96.11 | ...1792,1820-1842 
  cost-ledger.ts   |   94.67 |    95.86 |   78.57 |   94.67 | ...00-501,541-551 
  drive.ts         |   72.22 |    88.88 |   72.72 |   72.22 | ...34-469,473-487 
  extract-step.ts  |   91.36 |    90.62 |   88.88 |   91.36 | ...90-707,714-729 
  fetch-pr.ts      |    76.7 |    68.75 |   63.63 |    76.7 | ...95,417,450-455 
  findings.ts      |   89.35 |    89.13 |   95.45 |   89.35 | ...15-918,927-928 
  load-rules.ts    |   26.41 |      100 |   16.66 |   26.41 | ...41-153,155-156 
  mock-provider.ts |   95.44 |    90.25 |   89.47 |   95.44 | 145,690-709       
  parse-args.ts    |   99.32 |    96.42 |     100 |   99.32 | 400,473           
  plan-diff.ts     |   64.04 |      100 |   66.66 |   64.04 | 127-163           
  pr-context.ts    |   81.77 |    80.86 |   92.85 |   81.77 | ...1043,1072-1074 
  presubmit.ts     |   83.75 |    92.72 |   88.88 |   83.75 | ...77-578,655-685 
  ...ish-assets.ts |   77.24 |    82.75 |   71.42 |   77.24 | ...89-535,537-548 
  ...ve-anchors.ts |   77.77 |    88.88 |      75 |   77.77 | ...77-182,194-211 
  run.ts           |   82.16 |    87.12 |   91.66 |   82.16 | ...52,468-516,529 
  save-artifact.ts |   79.13 |    79.68 |   88.23 |   79.13 | ...50-381,383-386 
  script-lint.ts   |   83.67 |    78.41 |   88.88 |   83.67 | ...59-773,775-797 
  submit.ts        |   83.88 |    83.11 |    90.9 |   83.88 | ...66-470,570-606 
  test-delta.ts    |   87.13 |    91.46 |      75 |   87.13 | 206-237,477-485   
  test-efficacy.ts |   88.04 |    84.12 |   95.45 |   88.04 | ...2602,2610-2630 
  test-plan.ts     |    90.9 |       92 |   89.47 |    90.9 | ...88-789,853-870 
 ...w/__fixtures__ |     100 |      100 |     100 |     100 |                   
  ...r-default.mjs |     100 |      100 |     100 |     100 |                   
  ...der-empty.mjs |     100 |      100 |     100 |     100 |                   
  ...der-named.mjs |     100 |      100 |     100 |     100 |                   
 ...nds/review/lib |   96.59 |    93.93 |    96.9 |   96.59 |                   
  agent-briefs.ts  |   98.83 |      100 |       0 |   98.83 | 662-663           
  anchors.ts       |     100 |    94.79 |     100 |     100 | ...33,169,178,225 
  assets.ts        |     100 |      100 |     100 |     100 |                   
  authorization.ts |    92.4 |    92.59 |     100 |    92.4 | 127-133           
  budget.ts        |     100 |      100 |     100 |     100 |                   
  coverage.ts      |   95.73 |    95.21 |   95.45 |   95.73 | ...13,350,459-476 
  deadline.ts      |   97.68 |    91.22 |     100 |   97.68 | 140-141,190,352   
  diff-flags.ts    |     100 |        0 |     100 |     100 | 63                
  diff-plan.ts     |   98.73 |    93.01 |     100 |   98.73 | ...41,264,290-291 
  effort.ts        |     100 |      100 |     100 |     100 |                   
  gh.ts            |   85.92 |    91.11 |   73.33 |   85.92 | ...32,269-270,297 
  git.ts           |   97.64 |    95.65 |     100 |   97.64 | 180-181           
  heavy.ts         |     100 |      100 |     100 |     100 |                   
  inline-counts.ts |     100 |      100 |     100 |     100 |                   
  ledger.ts        |     100 |      100 |     100 |     100 |                   
  local-diff.ts    |    84.4 |    88.46 |     100 |    84.4 | ...63-473,475-483 
  merge-base.ts    |     100 |      100 |     100 |     100 |                   
  path-rules.ts    |     100 |      100 |     100 |     100 |                   
  paths.ts         |     100 |     87.5 |     100 |     100 | 92                
  prompt-record.ts |   95.57 |     87.5 |     100 |   95.57 | ...34,173-174,180 
  receipt.ts       |     100 |      100 |     100 |     100 |                   
  report.ts        |   94.68 |    93.75 |     100 |   94.68 | 187-191           
  retirement.ts    |     100 |    88.75 |     100 |     100 | ...64-265,284,383 
  review-footer.ts |     100 |      100 |     100 |     100 |                   
  roster.ts        |     100 |    94.23 |     100 |     100 | 143,161,206       
  shell-quote.ts   |     100 |      100 |     100 |     100 |                   
  test-utils.ts    |     100 |      100 |     100 |     100 |                   
  transcripts.ts   |   96.49 |    94.44 |     100 |   96.49 | ...98,284-285,309 
  workspaces.ts    |   98.88 |    92.39 |     100 |   98.88 | 212-213           
  worktree.ts      |     100 |      100 |     100 |     100 |                   
 ...mands/sessions |   91.56 |    86.95 |   83.33 |   91.56 |                   
  common.ts        |     100 |      100 |     100 |     100 |                   
  list.ts          |   90.96 |    86.66 |   81.81 |   90.96 | 208-219,221-222   
 src/config        |   94.42 |    88.91 |   95.65 |   94.42 |                   
  auth.ts          |   89.35 |    83.56 |     100 |   89.35 | ...97-298,314-315 
  ...eMcpImport.ts |   87.91 |    81.52 |     100 |   87.91 | ...63-371,453-454 
  compile-cache.ts |     100 |      100 |     100 |     100 |                   
  config.ts        |   88.93 |    88.61 |   83.78 |   88.93 | ...2456,2458-2466 
  ...cy-monitor.ts |   88.75 |    76.19 |     100 |   88.75 | ...3,90-92,98,101 
  ...ust-policy.ts |   83.04 |    88.49 |     100 |   83.04 | ...39,253,352-353 
  ...heme-names.ts |     100 |      100 |     100 |     100 |                   
  environment.ts   |   94.33 |    89.61 |   94.73 |   94.33 | ...35-639,655-656 
  ...le-watcher.ts |   90.86 |    83.65 |   95.83 |   90.86 | ...23-325,370,418 
  ...resh-state.ts |   90.57 |    97.29 |   93.75 |   90.57 | 137-142,146-152   
  ...ime-reload.ts |     100 |    69.69 |     100 |     100 | ...12-113,122-123 
  hot-reload.ts    |     100 |    89.13 |     100 |     100 | 47,172-178,238    
  keyBindings.ts   |   97.43 |       50 |     100 |   97.43 | 236-239           
  ...ngsAdapter.ts |     100 |    94.11 |     100 |     100 | 64                
  ...ig-watcher.ts |   95.17 |    83.05 |     100 |   95.17 | ...78,200,292-293 
  ...er-secrets.ts |   98.97 |    96.87 |     100 |   98.97 | 85                
  mcpApprovals.ts  |   96.55 |    95.55 |     100 |   96.55 | 223-224,229-231   
  mcpJson.ts       |     100 |      100 |     100 |     100 |                   
  mcpServers.ts    |   92.85 |     87.5 |     100 |   92.85 | 46-47             
  ...idersScope.ts |      95 |    94.73 |     100 |      95 | 11-12             
  ...abledTools.ts |     100 |      100 |     100 |     100 |                   
  ...comparison.ts |     100 |      100 |     100 |     100 |                   
  ...n-settings.ts |   99.15 |    93.75 |     100 |   99.15 | 63                
  sandboxConfig.ts |   61.64 |    71.87 |   66.66 |   61.64 | ...54-68,73,77-89 
  ...ings-cache.ts |   98.26 |    97.14 |     100 |   98.26 | 201-202           
  settings.ts      |   90.99 |     92.3 |      90 |   90.99 | ...1006,1008-1009 
  ...ingsSchema.ts |     100 |      100 |     100 |     100 |                   
  ...ngsWatcher.ts |   95.54 |    88.34 |     100 |   95.54 | ...28,277-278,293 
  ...d-env-keys.ts |     100 |      100 |     100 |     100 |                   
  ...l-settings.ts |     100 |      100 |     100 |     100 |                   
  ...paths-lite.ts |   89.47 |       88 |     100 |   89.47 | 43-44,53-54,56-57 
  ...tedFolders.ts |   93.42 |    94.21 |     100 |   93.42 | ...96-397,433-444 
 ...nfig/migration |   95.23 |    77.77 |   83.33 |   95.23 |                   
  index.ts         |   95.65 |     87.5 |     100 |   95.65 | 117-118           
  scheduler.ts     |   96.55 |    77.77 |     100 |   96.55 | 19-20             
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 ...ation/versions |   94.91 |      100 |     100 |   94.91 |                   
  ...-v2-shared.ts |     100 |      100 |     100 |     100 |                   
  v1-to-v2.ts      |   81.75 |      100 |     100 |   81.75 | ...28-229,231-247 
  v2-to-v3.ts      |     100 |      100 |     100 |     100 |                   
  v3-to-v4.ts      |     100 |      100 |     100 |     100 |                   
  v5-to-v4.ts      |      96 |      100 |     100 |      96 | 94-95,99          
 src/core          |     100 |      100 |     100 |     100 |                   
  auth.ts          |     100 |      100 |     100 |     100 |                   
  initializer.ts   |     100 |      100 |     100 |     100 |                   
  theme.ts         |     100 |      100 |     100 |     100 |                   
 src/dualOutput    |    71.8 |    70.31 |   66.66 |    71.8 |                   
  ...tputBridge.ts |   71.95 |    70.96 |   68.42 |   71.95 | ...08-409,417-420 
  ...utContext.tsx |     100 |      100 |     100 |     100 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-8               
 src/export        |       0 |        0 |       0 |       0 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-7               
 src/generated     |     100 |      100 |     100 |     100 |                   
  git-commit.ts    |     100 |      100 |     100 |     100 |                   
 src/hooks         |     100 |      100 |     100 |     100 |                   
  ...elete-hook.ts |     100 |      100 |     100 |     100 |                   
 src/i18n          |   85.98 |    81.92 |   89.65 |   85.98 |                   
  index.ts         |   73.45 |    77.77 |      90 |   73.45 | ...70-271,294-299 
  languages.ts     |   93.07 |     92.3 |   85.71 |   93.07 | ...35,164-169,184 
  ...nslateKeys.ts |     100 |      100 |     100 |     100 |                   
  ...lationDict.ts |   93.33 |    66.66 |     100 |   93.33 | 15                
 src/i18n/locales  |     100 |      100 |     100 |     100 |                   
  ca.js            |     100 |      100 |     100 |     100 |                   
  de.js            |     100 |      100 |     100 |     100 |                   
  en.js            |     100 |      100 |     100 |     100 |                   
  fr.js            |     100 |      100 |     100 |     100 |                   
  ja.js            |     100 |      100 |     100 |     100 |                   
  pt.js            |     100 |      100 |     100 |     100 |                   
  ru.js            |     100 |      100 |     100 |     100 |                   
  zh-TW.js         |     100 |      100 |     100 |     100 |                   
  zh.js            |     100 |      100 |     100 |     100 |                   
 ...nonInteractive |      80 |    76.31 |   81.35 |      80 |                   
  session.ts       |   84.08 |    75.27 |   93.61 |   84.08 | ...1007,1016-1026 
  types.ts         |    42.5 |      100 |   33.33 |    42.5 | ...31-632,635-636 
 ...active/control |   75.63 |    89.09 |      80 |   75.63 |                   
  ...rolContext.ts |    6.45 |        0 |       0 |    6.45 | 56-95             
  ...Dispatcher.ts |   91.79 |    92.45 |   88.88 |   91.79 | ...49-367,387,390 
  ...rolService.ts |    6.89 |        0 |       0 |    6.89 | 46-188            
 ...ol/controllers |   42.27 |    64.66 |   48.64 |   42.27 |                   
  ...Controller.ts |   39.49 |      100 |      80 |   39.49 | 88-92,127-210     
  ...Controller.ts |       0 |        0 |       0 |       0 | 1-56              
  ...Controller.ts |   53.96 |    65.71 |   58.33 |   53.96 | ...37-642,644-649 
  ...Controller.ts |   14.06 |      100 |       0 |   14.06 | ...82-117,130-133 
  ...Controller.ts |   37.92 |    60.71 |   46.66 |   37.92 | ...41-653,662-691 
 .../control/types |       0 |        0 |       0 |       0 |                   
  serviceAPIs.ts   |       0 |        0 |       0 |       0 | 1                 
 ...Interactive/io |    98.1 |    94.13 |   95.23 |    98.1 |                   
  ...putAdapter.ts |   97.98 |     93.2 |   98.07 |   97.98 | ...1415,1431-1432 
  ...putAdapter.ts |      96 |    91.66 |   85.71 |      96 | 51-52             
  ...nputReader.ts |     100 |    94.73 |     100 |     100 | 67                
  ...putAdapter.ts |   98.49 |      100 |   90.47 |   98.49 | 85-86,126-127     
  index.ts         |     100 |      100 |     100 |     100 |                   
 src/patches       |       0 |        0 |       0 |       0 |                   
  is-in-ci.ts      |       0 |        0 |       0 |       0 | 1-17              
 src/remoteInput   |   87.31 |    75.32 |   88.23 |   87.31 |                   
  ...utContext.tsx |     100 |      100 |     100 |     100 |                   
  ...putWatcher.ts |   88.01 |       76 |   93.33 |   88.01 | ...49-350,361-364 
  index.ts         |       0 |        0 |       0 |       0 | 1-8               
 src/runtime       |   99.61 |    95.04 |     100 |   99.61 |                   
  ...livery-ipc.ts |     100 |     90.9 |     100 |     100 | 94,106,134        
  ...l-delivery.ts |     100 |      100 |     100 |     100 |                   
  cpu-percent.ts   |     100 |      100 |     100 |     100 |                   
  ...erver-name.ts |     100 |      100 |     100 |     100 |                   
  ...-constants.ts |     100 |      100 |     100 |     100 |                   
  ...-summaries.ts |   86.66 |       50 |     100 |   86.66 | 11,19             
  ...ber-errors.ts |     100 |    95.32 |     100 |     100 | 53,93-94,172,192  
  ...ls-mapping.ts |     100 |      100 |     100 |     100 |                   
 src/serve         |   87.17 |    83.29 |   90.31 |   87.17 |                   
  ...tp-enabled.ts |     100 |      100 |     100 |     100 |                   
  ...ion-bridge.ts |     100 |      100 |     100 |     100 |                   
  auth.ts          |    93.4 |    92.95 |     100 |    93.4 | ...19-320,323-325 
  ...em-adapter.ts |     100 |      100 |     100 |     100 |                   
  capabilities.ts  |     100 |    98.07 |     100 |     100 | 670               
  ...cp-command.ts |     100 |      100 |     100 |     100 |                   
  ...horization.ts |   92.79 |    93.33 |    87.5 |   92.79 | 75-80,135-136     
  ...op-mcp-ipc.ts |   81.06 |    73.68 |   94.11 |   81.06 | ...37-242,267,289 
  ...nt-service.ts |   93.89 |    86.61 |     100 |   93.89 | ...66-468,475,477 
  ...-selection.ts |     100 |      100 |     100 |     100 |                   
  ...ings-store.ts |   86.06 |     92.2 |   95.83 |   86.06 | ...94-206,372-375 
  ...ebhook-ipc.ts |    98.5 |    86.66 |     100 |    98.5 | 47                
  ...iagnostics.ts |     100 |      100 |     100 |     100 |                   
  ...worker-env.ts |     100 |      100 |     100 |     100 |                   
  ...rker-group.ts |   87.27 |     85.2 |     100 |   87.27 | ...10,816-820,838 
  ...er-manager.ts |   89.39 |    83.88 |   93.33 |   89.39 | ...98,711,722-724 
  ...tartup-ipc.ts |   97.72 |    96.66 |     100 |   97.72 | 88-89             
  ...supervisor.ts |   92.42 |    84.44 |    97.1 |   92.42 | ...1462,1516-1520 
  ...e-grouping.ts |     100 |    94.28 |     100 |     100 | 71,137            
  core-runtime.ts  |     100 |      100 |     100 |     100 |                   
  ...ub-session.ts |    90.1 |    77.83 |   94.73 |    90.1 | ...1014,1021-1026 
  daemon-logger.ts |    82.2 |    77.42 |   91.76 |    82.2 | ...1720,1747-1753 
  ...trics-ring.ts |     100 |      100 |     100 |     100 |                   
  ...s-provider.ts |   68.04 |    52.77 |     100 |   68.04 | ...44-249,282-290 
  daemon-status.ts |   98.47 |    90.59 |     100 |   98.47 | ...1197,1199-1200 
  debug-mode.ts    |     100 |      100 |     100 |     100 |                   
  demo.ts          |     100 |      100 |     100 |     100 |                   
  env-snapshot.ts  |   93.37 |    85.18 |     100 |   93.37 | 114-117,195-202   
  ...-scheduler.ts |   87.34 |    83.87 |     100 |   87.34 | 33-36,48-50,79-81 
  ...d-provider.ts |   92.06 |    86.95 |     100 |   92.06 | ...72,287-293,316 
  ...-path-argv.ts |     100 |      100 |     100 |     100 |                   
  ...h-settings.ts |   94.41 |    88.75 |     100 |   94.41 | ...24,702,718,728 
  fast-path.ts     |   90.61 |    81.25 |   95.45 |   90.61 | ...02-511,577-578 
  ...ration-sse.ts |   42.55 |    33.33 |     100 |   42.55 | 23-24,30,33-56    
  health-query.ts  |     100 |      100 |     100 |     100 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-143             
  ...e-observer.ts |   89.89 |    83.24 |      96 |   89.89 | ...11-512,541-543 
  ...back-binds.ts |     100 |    88.88 |     100 |     100 | 32                
  ...-workspace.ts |    90.9 |    85.71 |     100 |    90.9 | ...30-131,142-143 
  ...iders-edit.ts |     100 |    82.14 |     100 |     100 | 58-60,65,81       
  ...ory-picker.ts |     100 |    86.95 |     100 |     100 | 36,66,92          
  ...sion-audit.ts |     100 |      100 |   93.33 |     100 |                   
  rate-limit.ts    |   92.77 |    88.42 |     100 |   92.77 | ...93-295,307-309 
  ...qwen-serve.ts |   83.48 |    79.38 |      75 |   83.48 | ...7164,7170-7171 
  ...tup-errors.ts |     100 |      100 |     100 |     100 |                   
  ...-keepalive.ts |   94.19 |    88.57 |     100 |   94.19 | ...26,530-531,571 
  ...-lifecycle.ts |     100 |      100 |     100 |     100 |                   
  server.ts        |   90.25 |    90.87 |   70.75 |   90.25 | ...2657,2671-2675 
  ...on-helpers.ts |     100 |      100 |     100 |     100 |                   
  ...t-event-id.ts |     100 |    95.23 |     100 |     100 | 12                
  ...-admission.ts |   98.71 |    89.65 |     100 |   98.71 | 68                
  types.ts         |     100 |      100 |     100 |     100 |                   
  ...ion-limits.ts |     100 |      100 |     100 |     100 |                   
  ...t-sessions.ts |    93.3 |    76.83 |     100 |    93.3 | ...13,816,829-831 
  ...l-resolver.ts |   90.32 |    66.66 |     100 |   90.32 | 16,45-46          
  ...ell-static.ts |   91.07 |    86.66 |     100 |   91.07 | ...79-182,216-219 
  ...ace-agents.ts |   66.13 |    70.57 |   92.68 |   66.13 | ...2246,2256-2266 
  ...generation.ts |    95.4 |    82.35 |   66.66 |    95.4 | 55-56,78,92       
  ...-git-state.ts |     100 |    91.93 |    90.9 |     100 | 161,172,202,265   
  ...ace-inputs.ts |     100 |      100 |     100 |     100 |                   
  ...ace-memory.ts |      83 |    74.54 |     100 |      83 | ...30-537,597-604 
  ...ers-status.ts |   98.58 |       79 |     100 |   98.58 | 106,134,174,177   
  ...tion-store.ts |   89.67 |    88.27 |   92.59 |   89.67 | ...91-400,411-414 
  ...e-registry.ts |   93.89 |     87.5 |     100 |   93.89 | ...18-519,525-526 
  ...e-remember.ts |   98.23 |    92.51 |     100 |   98.23 | ...36,340-345,386 
  ...te-runtime.ts |   83.98 |    90.19 |     100 |   83.98 | ...48-156,216-237 
  ...me-storage.ts |     100 |      100 |     100 |     100 |                   
  ...management.ts |   72.63 |    72.72 |      96 |   72.63 | ...88-889,896-900 
  ...lls-status.ts |     100 |    95.45 |     100 |     100 | 152               
  ...reconciler.ts |   91.63 |    84.26 |     100 |   91.63 | ...71-273,306-307 
 ...serve/acp-http |   77.23 |    78.75 |   93.33 |   77.23 |                   
  ...r-registry.ts |   96.92 |    94.87 |     100 |   96.92 | 184-187           
  client-mcp-ws.ts |   54.85 |    58.62 |   72.72 |   54.85 | ...99-300,304-305 
  ...n-registry.ts |    98.2 |    88.55 |     100 |    98.2 | 1015,1041-1052    
  dispatch.ts      |   71.95 |    75.11 |   95.55 |   71.95 | ...4897,4945-4951 
  index.ts         |   81.97 |     79.8 |    90.9 |   81.97 | ...2296,2380-2381 
  json-rpc.ts      |     100 |    96.96 |     100 |     100 | 92                
  safe-ws-send.ts  |   52.94 |    71.42 |     100 |   52.94 | 33-42,47-55       
  sse-stream.ts    |   93.96 |    88.57 |   84.61 |   93.96 | ...57-159,161-163 
  ...ort-stream.ts |       0 |        0 |       0 |       0 | 1                 
  ws-stream.ts     |   91.86 |       80 |     100 |   91.86 | 45,50,96,100-103  
 src/serve/auth    |   86.86 |     79.7 |   93.87 |   86.86 |                   
  device-flow.ts   |   96.35 |    80.57 |   97.61 |   96.35 | ...1358,1453,1519 
  ...w-provider.ts |   44.24 |    74.07 |   71.42 |   44.24 | ...23-284,297,301 
 ...rve/cdp-tunnel |   87.73 |    76.21 |    97.5 |   87.73 |                   
  ...r-emulator.ts |   93.27 |    77.77 |     100 |   93.27 | ...53-256,282-283 
  ...verse-link.ts |      88 |    76.19 |     100 |      88 | ...28-329,420-423 
  ...l-registry.ts |     100 |      100 |     100 |     100 |                   
  cdp-ws.ts        |   76.28 |    61.29 |    87.5 |   76.28 | ...13-217,223-228 
 ...nel/acceptance |    6.12 |    57.89 |   46.15 |    6.12 |                   
  ...helpers.d.mts |       0 |        0 |       0 |       0 | 1                 
  ...e-helpers.mjs |   97.64 |    70.96 |     100 |   97.64 | 22-23             
  ...mcp-smoke.mjs |       0 |        0 |       0 |       0 | 1-124             
  ...cceptance.mjs |       0 |        0 |       0 |       0 | 1-473             
  ...re-server.mjs |       0 |        0 |       0 |       0 | 1-59              
  ...ols-smoke.mjs |       0 |        0 |       0 |       0 | 1-268             
  real-tab.mjs     |       0 |        0 |       0 |       0 | 1-218             
  ...al-chrome.mjs |       0 |        0 |       0 |       0 | 1-223             
 src/serve/fs      |   86.39 |    80.74 |     100 |   86.39 |                   
  audit.ts         |     100 |    96.15 |     100 |     100 | 204               
  errors.ts        |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...x-registry.ts |     100 |      100 |     100 |     100 |                   
  paths.ts         |   77.64 |     73.8 |     100 |   77.64 | ...65,594-598,611 
  policy.ts        |   90.42 |    89.18 |     100 |   90.42 | 161-169           
  text-cursor.ts   |   88.23 |       90 |     100 |   88.23 | 74-77,92-95       
  ...ile-system.ts |   86.16 |    79.55 |     100 |   86.16 | ...2510,2520-2521 
 src/serve/live    |   77.28 |    69.21 |   89.91 |   77.28 |                   
  ...en-context.ts |   95.74 |    81.25 |     100 |   95.74 | ...0,66-67,99-100 
  ...-workspace.ts |   88.63 |    82.53 |     100 |   88.63 | ...40-241,253-254 
  discovery.ts     |   85.77 |    76.92 |      90 |   85.77 | ...49-250,255-256 
  ...structions.ts |     100 |      100 |     100 |     100 |                   
  ...oordinator.ts |   82.67 |    76.75 |   97.01 |   82.67 | ...1319,1351-1353 
  ...-installer.ts |   45.17 |    81.96 |   68.18 |   45.17 | ...80-381,395-407 
  ...oordinator.ts |   76.17 |     64.4 |   85.36 |   76.17 | ...1858,1949-1950 
  ...controller.ts |   67.82 |    79.31 |   72.72 |   67.82 | ...66-278,287-295 
  ...ak-to-user.ts |   96.66 |      100 |   83.33 |   96.66 | 37-38             
  ...sk-service.ts |   86.22 |    59.64 |   93.33 |   86.22 | ...1152,1175-1182 
  ...task-tools.ts |      99 |      100 |   85.71 |      99 | 205-206           
  ...redentials.ts |   96.26 |    93.47 |     100 |   96.26 | 91-94             
  ...me-session.ts |   65.63 |    57.24 |   88.88 |   65.63 | ...2270,2275-2282 
  ...up-context.ts |   94.83 |    77.58 |     100 |   94.83 | ...18,327-330,350 
  ...ion-source.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/serve/routes  |   85.22 |     79.5 |   95.15 |   85.22 |                   
  a2ui-action.ts   |   96.84 |     88.5 |    87.5 |   96.84 | ...70-272,309-311 
  capabilities.ts  |     100 |      100 |     100 |     100 |                   
  ...nel-notify.ts |   86.45 |       88 |     100 |   86.45 | ...,83-87,103-104 
  ...l-webhooks.ts |   93.56 |    84.09 |     100 |   93.56 | ...42,292,332,334 
  daemon-status.ts |   85.45 |    83.33 |     100 |   85.45 | 98-105            
  goals.ts         |   98.92 |     90.9 |     100 |   98.92 | 146               
  health-demo.ts   |   94.73 |     86.2 |     100 |   94.73 | 62-66,154         
  live-setup.ts    |   33.33 |     37.5 |      50 |   33.33 | ...18-123,130-135 
  live.ts          |    82.4 |    71.42 |     100 |    82.4 | ...-94,96-101,121 
  permission.ts    |     100 |     92.3 |     100 |     100 | 50,98             
  ...uled-tasks.ts |   87.29 |    82.94 |   92.59 |   87.29 | ...1275,1318-1319 
  ...on-runtime.ts |     100 |    90.47 |     100 |     100 | 58,94             
  session.ts       |   85.42 |    81.81 |   95.31 |   85.42 | ...4768,4770-4771 
  sse-events.ts    |   84.45 |     87.5 |   77.77 |   84.45 | ...36,453-456,485 
  usage-stats.ts   |     100 |    95.45 |     100 |     100 | 118               
  ...space-auth.ts |   85.55 |    75.64 |     100 |   85.55 | ...21-326,331,345 
  ...el-control.ts |   86.26 |    78.94 |     100 |   86.26 | ...17-318,339-347 
  ...management.ts |   90.38 |     78.4 |     100 |   90.38 | ...55-456,475-476 
  ...d-contacts.ts |     100 |      100 |     100 |     100 |                   
  ...controller.ts |   83.09 |       79 |      90 |   83.09 | ...1032,1038,1041 
  ...extensions.ts |   87.23 |    72.76 |   94.11 |   87.23 | ...1826,1871-1872 
  ...-file-read.ts |      91 |    80.91 |     100 |      91 | ...20-621,624-625 
  ...file-write.ts |   84.44 |    64.51 |     100 |   84.44 | ...73-275,355-357 
  ...t-branches.ts |   75.43 |    66.66 |     100 |   75.43 | ...13-618,627-634 
  ...e-git-diff.ts |   97.32 |    90.56 |     100 |   97.32 | 161-162,189-191   
  ...ce-git-log.ts |     100 |    93.18 |     100 |     100 | 52,77,188         
  workspace-git.ts |   77.08 |    89.65 |     100 |   77.08 | 97-118            
  ...github-prs.ts |   88.26 |    63.46 |     100 |   88.26 | ...38-239,264-265 
  ...-lifecycle.ts |   95.23 |    75.75 |     100 |   95.23 | ...50-151,186-187 
  ...management.ts |   87.41 |    84.13 |     100 |   87.41 | ...1660,1680-1685 
  ...cp-control.ts |    73.2 |    67.54 |   85.71 |    73.2 | ...27-633,644-645 
  ...ace-models.ts |   95.53 |    89.74 |     100 |   95.53 | ...52-157,296-297 
  ...ermissions.ts |    77.9 |    72.41 |     100 |    77.9 | ...69-277,298-316 
  ...e-settings.ts |   75.04 |    72.99 |     100 |   75.04 | ...79-690,696-697 
  ...tup-github.ts |   77.97 |    70.58 |   84.21 |   77.97 | ...46-352,397-398 
  ...ace-skills.ts |   69.87 |    78.12 |     100 |   69.87 | ...59-284,290-324 
  ...ace-status.ts |   82.94 |     74.5 |     100 |   82.94 | ...84-486,490-491 
  ...pace-tools.ts |   75.94 |    69.69 |   66.66 |   75.94 | ...59-164,193-194 
  ...pace-trust.ts |   78.92 |    66.21 |      80 |   78.92 | ...38-343,351-352 
  ...pace-voice.ts |   91.33 |    80.92 |     100 |   91.33 | ...70-673,676-678 
 src/serve/server  |   90.72 |    89.11 |   96.55 |   90.72 |                   
  access-log.ts    |   98.68 |     97.1 |     100 |   98.68 | 115,186           
  ...er-helpers.ts |   63.82 |    77.96 |   81.81 |   63.82 | ...16,330,332-347 
  ...w-registry.ts |    98.8 |    81.81 |     100 |    98.8 | 107               
  ...r-handlers.ts |   97.29 |       75 |     100 |   97.29 | 17                
  ...r-response.ts |   85.66 |    76.83 |     100 |   85.66 | ...02,719,782-791 
  fs-factory.ts    |     100 |    92.72 |     100 |     100 | 34,42,103,159     
  ...branch-ops.ts |     100 |      100 |     100 |     100 |                   
  ...t-deadline.ts |     100 |      100 |     100 |     100 |                   
  ...iter-setup.ts |      65 |    73.33 |   33.33 |      65 | 30-35,38-43,47-48 
  ...st-helpers.ts |   95.11 |    95.19 |     100 |   95.11 | ...65-167,422-427 
  self-origin.ts   |   76.19 |       80 |     100 |   76.19 | 45-54             
  ...e-features.ts |      95 |     87.5 |     100 |      95 | 182-188           
  ...on-archive.ts |   89.55 |    87.78 |   97.14 |   89.55 | ...32-836,888-889 
  ...ion-export.ts |     100 |    94.44 |     100 |     100 | 64                
  session-list.ts  |   93.55 |    91.01 |     100 |   93.55 | ...79,681-687,827 
  telemetry.ts     |   99.02 |    97.44 |     100 |   99.02 | ...25,639,781-783 
 src/serve/voice   |   83.35 |    92.22 |   90.47 |   83.35 |                   
  ...ice-config.ts |   84.61 |       30 |     100 |   84.61 | 90-99,103-104     
  voice-ws.ts      |   77.16 |    94.73 |   83.33 |   77.16 | ...68,483,521-523 
  ...oordinator.ts |     100 |    98.21 |     100 |     100 | 176               
 ...kspace-service |   89.11 |    86.15 |   90.69 |   89.11 |                   
  index.ts         |   88.66 |    85.77 |   89.47 |   88.66 | ...1286-1290,1293 
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/services      |   92.31 |    88.44 |   97.84 |   92.31 |                   
  ...mandLoader.ts |     100 |    88.88 |     100 |     100 | 105-118           
  ...killLoader.ts |   97.19 |    85.29 |     100 |   97.19 | 142,153-154       
  ...andService.ts |   98.73 |      100 |     100 |   98.73 | 107               
  ...mandLoader.ts |   86.83 |    83.87 |     100 |   86.83 | ...30-335,340-345 
  ...omptLoader.ts |   79.55 |    88.29 |   83.33 |   79.55 | ...48,178,245-246 
  ...mandLoader.ts |   97.77 |    92.15 |     100 |   97.77 | 176,183-184       
  ...nd-factory.ts |   91.42 |    91.66 |     100 |   91.42 | 128,137-144       
  ...ation-tool.ts |     100 |    95.45 |     100 |     100 | 125               
  ...ndMetadata.ts |   98.23 |    96.72 |     100 |   98.23 | 83,87             
  commandUtils.ts  |      96 |     90.9 |     100 |      96 | 48                
  ...and-parser.ts |   90.69 |    85.71 |     100 |   90.69 | 63-66             
  ...ionService.ts |     100 |      100 |     100 |     100 |                   
  prompt-stash.ts  |   96.66 |    92.85 |     100 |   96.66 | 34-35             
  ...tree-lease.ts |   88.23 |    86.48 |     100 |   88.23 | ...94-199,232-233 
  ...low-loader.ts |     100 |    96.15 |     100 |     100 | 88                
  setup-github.ts  |    90.8 |    80.95 |     100 |    90.8 | ...49-450,457-458 
  ...-args-file.ts |   93.93 |    91.66 |    87.5 |   93.93 | 208-210,224-230   
  types.ts         |     100 |      100 |     100 |     100 |                   
  ...e-keyterms.ts |   98.64 |    95.71 |     100 |   98.64 | 116,142-143       
  voice-model.ts   |     100 |      100 |     100 |     100 |                   
  voice-service.ts |   90.37 |    87.87 |     100 |   90.37 | ...80,287,352-357 
  ...e-settings.ts |     100 |    95.23 |     100 |     100 | 19                
  ...ranscriber.ts |   90.46 |    82.19 |      96 |   90.46 | ...66-668,671-673 
 ...rvices/insight |     100 |      100 |     100 |     100 |                   
  dates.ts         |     100 |      100 |     100 |     100 |                   
 ...ght/generators |   88.91 |    86.29 |   96.15 |   88.91 |                   
  DataProcessor.ts |   88.28 |    86.24 |   94.73 |   88.28 | ...1352,1356-1363 
  ...tGenerator.ts |   98.24 |    85.71 |     100 |   98.24 | 47                
  ...teRenderer.ts |     100 |      100 |     100 |     100 |                   
 .../insight/types |       0 |       50 |      50 |       0 |                   
  ...sightTypes.ts |       0 |        0 |       0 |       0 |                   
  ...sightTypes.ts |       0 |        0 |       0 |       0 | 1                 
 ...mpt-processors |   97.27 |    94.04 |     100 |   97.27 |                   
  ...tProcessor.ts |     100 |      100 |     100 |     100 |                   
  ...eProcessor.ts |   94.52 |    84.21 |     100 |   94.52 | 46-47,93-94       
  ...tionParser.ts |     100 |      100 |     100 |     100 |                   
  ...lProcessor.ts |   97.41 |    95.65 |     100 |   97.41 | 95-98             
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/services/tips |   97.27 |    84.61 |     100 |   97.27 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  tipHistory.ts    |   92.59 |       70 |     100 |   92.59 | ...24,146,153,162 
  tipRegistry.ts   |     100 |      100 |     100 |     100 |                   
  tipScheduler.ts  |     100 |    91.66 |     100 |     100 | 55                
 src/startup       |   88.99 |    83.47 |    90.9 |   88.99 |                   
  ...p-prefetch.ts |   98.09 |    94.23 |    87.5 |   98.09 | 50,209,225-226    
  ...reeStartup.ts |   80.53 |     74.6 |     100 |   80.53 | ...94,403,409-412 
 src/test-utils    |   94.09 |    79.16 |   77.77 |   94.09 |                   
  ci-env.ts        |      88 |     62.5 |     100 |      88 | 22-23,28          
  ...omMatchers.ts |   69.69 |       50 |      50 |   69.69 | 32-35,37-39,45-47 
  ...mised-lock.ts |     100 |      100 |   66.66 |     100 |                   
  ...andContext.ts |     100 |      100 |     100 |     100 |                   
  render.tsx       |     100 |      100 |     100 |     100 |                   
 src/ui            |   72.71 |    75.23 |    65.9 |   72.71 |                   
  App.tsx          |   33.33 |       75 |   33.33 |   33.33 | 32-86             
  AppContainer.tsx |   73.93 |    72.14 |   70.58 |   73.93 | ...4099,4215-4221 
  ...tionNudge.tsx |    9.58 |      100 |       0 |    9.58 | 24-94             
  ...ackDialog.tsx |    30.3 |      100 |       0 |    30.3 | 26-76             
  ...tionNudge.tsx |    7.69 |      100 |       0 |    7.69 | 25-103            
  colors.ts        |      60 |      100 |   35.29 |      60 | ...52,54-55,60-61 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  keyMatchers.ts   |   95.91 |    97.14 |     100 |   95.91 | 25-26             
  ...tic-colors.ts |     100 |      100 |     100 |     100 |                   
  ...ractiveUI.tsx |   68.12 |    64.86 |   33.33 |   68.12 | ...98,321,341-346 
  ...inePresets.ts |   96.27 |    83.87 |     100 |   96.27 | ...97,402,410-412 
  textConstants.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/ui/auth       |   58.53 |    66.18 |   51.06 |   58.53 |                   
  AuthDialog.tsx   |   59.01 |     42.1 |   16.66 |   59.01 | ...25,332-354,358 
  ...nProgress.tsx |       0 |        0 |       0 |       0 | 1-64              
  ...etupSteps.tsx |   60.21 |    70.73 |   57.69 |   60.21 | ...90,794,803,806 
  useAuth.ts       |    94.6 |    73.52 |     100 |    94.6 | ...21-222,241-247 
  ...rSetupFlow.ts |   43.18 |    33.33 |      50 |   43.18 | ...78-399,416-459 
 src/ui/commands   |    82.5 |    82.83 |   89.12 |    82.5 |                   
  aboutCommand.ts  |     100 |      100 |     100 |     100 |                   
  agentsCommand.ts |   83.78 |      100 |      60 |   83.78 | 30-32,42-44       
  ...odeCommand.ts |    93.1 |    95.23 |     100 |    93.1 | 77-82             
  arenaCommand.ts  |   63.89 |    65.71 |   65.21 |   63.89 | ...01-606,691-699 
  authCommand.ts   |     100 |      100 |     100 |     100 |                   
  branchCommand.ts |     100 |      100 |     100 |     100 |                   
  btwCommand.ts    |   94.32 |    77.41 |     100 |   94.32 | 35-36,114-119     
  bugCommand.ts    |     100 |    77.77 |     100 |     100 | 27,61             
  cdCommand.ts     |    92.3 |    82.75 |     100 |    92.3 | ...,94-99,178,187 
  clearCommand.ts  |    80.9 |    70.83 |     100 |    80.9 | ...24-125,133-142 
  ...essCommand.ts |   68.06 |    54.05 |      75 |   68.06 | ...96-197,211-214 
  ...astCommand.ts |   84.17 |       75 |     100 |   84.17 | ...,91-97,125-130 
  ...ig-command.ts |   93.12 |    88.42 |     100 |   93.12 | ...07-315,321-323 
  ...extCommand.ts |   68.28 |    70.14 |   84.61 |   68.28 | ...66-599,610-611 
  copyCommand.ts   |    98.7 |    96.29 |     100 |    98.7 | 66-67,172,272,323 
  ...or-command.ts |   85.95 |    80.55 |   88.88 |   85.95 | ...68-274,298-309 
  deleteCommand.ts |     100 |      100 |     100 |     100 |                   
  diffCommand.ts   |     100 |    87.87 |     100 |     100 | ...63,231-232,245 
  ...ryCommand.tsx |   81.64 |    87.67 |    90.9 |   81.64 | ...73-278,325-332 
  docsCommand.ts   |     100 |     90.9 |     100 |     100 | 25                
  doctorCommand.ts |   65.37 |    81.88 |   94.11 |   65.37 | ...85-535,538-672 
  dreamCommand.ts  |   85.45 |    88.88 |     100 |   85.45 | 58-65             
  editorCommand.ts |     100 |      100 |     100 |     100 |                   
  ...rt-command.ts |   82.97 |    78.57 |     100 |   82.97 | 47-52,67-70,91-96 
  exportCommand.ts |   98.25 |    91.02 |     100 |   98.25 | ...81,198-199,364 
  ...onsCommand.ts |   52.31 |    56.25 |   69.23 |   52.31 | ...09,277-329,390 
  forgetCommand.ts |     100 |       90 |     100 |     100 | 59                
  forkCommand.ts   |     100 |    94.11 |     100 |     100 | 96,147            
  goalCommand.ts   |   72.81 |    86.84 |   66.66 |   72.81 | ...63-168,277-280 
  helpCommand.ts   |     100 |      100 |     100 |     100 |                   
  ...oryCommand.ts |     100 |      100 |     100 |     100 |                   
  hooksCommand.ts  |   81.13 |    65.71 |   85.71 |   81.13 | ...,86-93,131-132 
  ideCommand.ts    |   60.75 |    64.28 |   41.17 |   60.75 | ...05-306,310-324 
  ...figCommand.ts |   52.83 |    81.25 |      70 |   52.83 | ...74-319,321-330 
  initCommand.ts   |   91.86 |       80 |     100 |   91.86 | 48,83-88          
  ...ghtCommand.ts |   77.87 |    71.42 |     100 |   77.87 | ...44-245,250-272 
  ...ageCommand.ts |   93.45 |    89.06 |     100 |   93.45 | ...68-169,196-206 
  learn-command.ts |     100 |      100 |     100 |     100 |                   
  lspCommand.ts    |     100 |    86.95 |     100 |     100 | 31,101-102        
  mcpCommand.ts    |     100 |      100 |     100 |     100 |                   
  memoryCommand.ts |     100 |      100 |     100 |     100 |                   
  modelCommand.ts  |   84.78 |    82.47 |     100 |   84.78 | ...1071,1105-1110 
  ...onsCommand.ts |     100 |      100 |     100 |     100 |                   
  planCommand.ts   |   78.82 |    76.92 |     100 |   78.82 | 30-35,51-56,68-73 
  quitCommand.ts   |     100 |      100 |     100 |     100 |                   
  recapCommand.ts  |   21.81 |      100 |      50 |   21.81 | 24-73             
  ...ns-command.ts |   98.83 |    81.81 |     100 |   98.83 | 100               
  ...berCommand.ts |     100 |     87.5 |     100 |     100 | 46                
  renameCommand.ts |   89.06 |    88.37 |     100 |   89.06 | ...72-176,202-209 
  ...oreCommand.ts |    90.9 |    86.04 |     100 |    90.9 | ...41-146,176-177 
  resumeCommand.ts |     100 |      100 |     100 |     100 |                   
  rewindCommand.ts |   81.25 |      100 |      50 |   81.25 | 20-22             
  ...ngsCommand.ts |     100 |      100 |     100 |     100 |                   
  ...hubCommand.ts |   89.47 |       75 |      80 |   89.47 | 54-59             
  skillsCommand.ts |   78.82 |    81.81 |     100 |   78.82 | 37-52,78,97       
  statsCommand.ts  |   90.65 |    76.73 |     100 |   90.65 | ...30-733,825-832 
  ...ineCommand.ts |     100 |      100 |     100 |     100 |                   
  ...aryCommand.ts |   73.04 |     82.3 |      90 |   73.04 | ...20-547,561-565 
  tasksCommand.ts  |   77.22 |    72.13 |     100 |   77.22 | ...46-150,172-177 
  ...tupCommand.ts |     100 |      100 |     100 |     100 |                   
  themeCommand.ts  |     100 |      100 |     100 |     100 |                   
  toolsCommand.ts  |     100 |      100 |     100 |     100 |                   
  trustCommand.ts  |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
  ...te-command.ts |     100 |    94.11 |     100 |     100 | 74,148            
  vimCommand.ts    |   54.54 |      100 |      50 |   54.54 | 19-29             
  voice-command.ts |   93.57 |       88 |     100 |   93.57 | 35,97-102         
  ...owsCommand.ts |   91.86 |    78.87 |   66.66 |   91.86 | ...60-161,170-175 
 src/ui/components |   71.28 |    78.65 |   79.62 |   71.28 |                   
  AboutBox.tsx     |     100 |      100 |     100 |     100 |                   
  AnsiOutput.tsx   |   65.57 |      100 |      50 |   65.57 | 69-90             
  ApiKeyInput.tsx  |       0 |        0 |       0 |       0 | 1-97              
  AppHeader.tsx    |    88.7 |       75 |     100 |    88.7 | 36,38-43,45       
  ...odeDialog.tsx |   87.24 |    72.22 |   33.33 |   87.24 | ...85,233-238,245 
  AsciiArt.ts      |     100 |      100 |     100 |     100 |                   
  ...Indicator.tsx |   95.65 |    66.66 |     100 |   95.65 | 27,52             
  ...TextInput.tsx |   88.65 |    90.41 |     100 |   88.65 | ...84-286,300-302 
  Composer.tsx     |   94.49 |    66.66 |     100 |   94.49 | ...-72,84,139,153 
  ...entPrompt.tsx |     100 |      100 |     100 |     100 |                   
  ...ryDisplay.tsx |   75.89 |    62.06 |     100 |   75.89 | ...,88,93-108,113 
  ...geDisplay.tsx |   68.42 |    57.14 |     100 |   68.42 | 16-17,31-32,42-50 
  CronPill.tsx     |     100 |    93.75 |     100 |     100 | 19                
  ...ification.tsx |      84 |       60 |     100 |      84 | 23-24,40-42       
  ...gProfiler.tsx |       0 |        0 |       0 |       0 | 1-36              
  ...ogManager.tsx |       0 |        0 |       0 |       0 | 1-598             
  DiffDialog.tsx   |    53.5 |     37.5 |   69.23 |    53.5 | ...32-737,747-760 
  ...ngsDialog.tsx |       0 |        0 |       0 |       0 | 1-195             
  EffortDialog.tsx |   97.36 |      100 |     100 |   97.36 | 55-56             
  ExitWarning.tsx  |     100 |      100 |     100 |     100 |                   
  ...hProgress.tsx |    87.8 |    33.33 |     100 |    87.8 | 28-31,56          
  ...ustDialog.tsx |     100 |      100 |     100 |     100 |                   
  Footer.tsx       |   74.09 |     61.4 |      50 |   74.09 | ...55-260,278-282 
  ...ngSpinner.tsx |   68.42 |    85.71 |      50 |   68.42 | 35-52,73,80-81    
  GoalPill.tsx     |   93.51 |    81.81 |     100 |   93.51 | 37-38,106-109,123 
  Header.tsx       |   98.65 |    94.73 |     100 |   98.65 | 173,175           
  Help.tsx         |   98.33 |       90 |     100 |   98.33 | ...25,382,448-449 
  ...emDisplay.tsx |   79.06 |    66.33 |     100 |   79.06 | ...04,507,510-516 
  ...ngeDialog.tsx |     100 |      100 |     100 |     100 |                   
  InputPrompt.tsx  |    83.1 |    81.95 |      80 |    83.1 | ...2199,2225,2299 
  ...Shortcuts.tsx |     100 |       88 |     100 |     100 | 98,119            
  ...Indicator.tsx |   98.18 |    97.82 |     100 |   98.18 | 161-162           
  ...firmation.tsx |   91.42 |      100 |      50 |   91.42 | 26-31             
  MainContent.tsx  |    95.9 |    92.53 |      50 |    95.9 | ...99,445-449,452 
  MemoryDialog.tsx |   86.59 |    80.15 |     100 |   86.59 | ...34-435,485,553 
  ...geDisplay.tsx |       0 |        0 |       0 |       0 | 1-41              
  ModelDialog.tsx  |   81.95 |    71.27 |     100 |   81.95 | ...1045,1050-1066 
  ...tsDisplay.tsx |     100 |    97.22 |     100 |     100 | 270               
  ...fications.tsx |       0 |        0 |       0 |       0 | 1-56              
  ...onsDialog.tsx |       0 |        0 |       0 |       0 | 1-1004            
  ...ryDisplay.tsx |     100 |      100 |     100 |     100 |                   
  ...icePrompt.tsx |   92.64 |    85.71 |     100 |   92.64 | 102-106,134-139   
  PrepareLabel.tsx |   91.66 |    77.27 |     100 |   91.66 | 73-75,77-79,110   
  ...atePrompt.tsx |       0 |        0 |       0 |       0 | 1-134             
  ...geDisplay.tsx |     100 |      100 |     100 |     100 |                   
  ...ngDisplay.tsx |       0 |        0 |       0 |       0 | 1-39              
  ...hProgress.tsx |   85.25 |    88.46 |     100 |   85.25 | 121-147           
  ...dSelector.tsx |   92.79 |    82.65 |     100 |   92.79 | ...19-323,354-370 
  ...ionPicker.tsx |   83.66 |    72.13 |     100 |   83.66 | ...96,402,444-466 
  ...onPreview.tsx |   93.58 |    83.78 |     100 |   93.58 | ...,70-71,195-197 
  ...ryDisplay.tsx |     100 |      100 |     100 |     100 |                   
  ...putPrompt.tsx |   92.06 |    86.36 |   83.33 |   92.06 | ...,70-72,120-123 
  ...tedDialog.tsx |     100 |      100 |     100 |     100 |                   
  ...ngsDialog.tsx |   71.49 |    73.89 |   69.23 |   71.49 | ...1244,1250-1251 
  ...ionDialog.tsx |    92.3 |    96.15 |   33.33 |    92.3 | 60-63,68-75,164   
  ...putPrompt.tsx |    15.9 |      100 |       0 |    15.9 | 20-63             
  ...Indicator.tsx |   57.14 |      100 |       0 |   57.14 | 12-15             
  ...MoreLines.tsx |       0 |        0 |       0 |       0 | 1-40              
  ...iewDialog.tsx |   97.77 |    87.67 |     100 |   97.77 | ...97,305-307,324 
  ...tsDisplay.tsx |   95.86 |       75 |     100 |   95.86 | 67-71             
  ...ionPicker.tsx |       0 |        0 |       0 |       0 | 1-172             
  ...tivityTab.tsx |    3.94 |      100 |       0 |    3.94 | 27-275            
  StatsDialog.tsx  |    8.64 |      100 |       0 |    8.64 | ...76-111,130-322 
  StatsDisplay.tsx |     100 |      100 |     100 |     100 |                   
  ...ciencyTab.tsx |    78.9 |    56.52 |     100 |    78.9 | ...26,213,262-288 
  ...atmapView.tsx |    8.98 |      100 |       0 |    8.98 | 20-107            
  ...essionTab.tsx |      80 |    66.66 |     100 |      80 | ...70-277,283-300 
  ...ineDialog.tsx |    93.5 |    85.18 |     100 |    93.5 | ...05,267,287-289 
  ...yTodoList.tsx |   96.36 |    88.23 |     100 |   96.36 | 138-141           
  ...nsDisplay.tsx |   92.97 |    83.87 |     100 |   92.97 | ...45,248,275-277 
  ...inalImage.tsx |     100 |     90.9 |     100 |     100 | 75,93             
  ThemeDialog.tsx  |   89.95 |    46.15 |      75 |   89.95 | ...71-173,243-245 
  Tips.tsx         |   93.54 |       75 |     100 |   93.54 | 39-40             
  TodoDisplay.tsx  |     100 |      100 |     100 |     100 |                   
  ...tsDisplay.tsx |     100 |     87.5 |     100 |     100 | 31-32             
  TrustDialog.tsx  |     100 |    83.33 |     100 |     100 | 72-87             
  ...ification.tsx |   36.36 |      100 |       0 |   36.36 | 15-22             
  ...Indicator.tsx |    92.5 |     87.5 |     100 |    92.5 | 50-53             
  ...ackDialog.tsx |       0 |        0 |       0 |       0 | 1-134             
  ...xitDialog.tsx |   80.36 |    43.47 |      60 |   80.36 | ...24-238,248-251 
  ...odeVisuals.ts |   97.22 |    85.71 |     100 |   97.22 | 25                
  ...s-helpers.tsx |   66.25 |    81.25 |      50 |   66.25 | 25-32,46-53,62-72 
 ...nts/agent-view |   55.05 |    69.09 |      50 |   55.05 |                   
  ...atContent.tsx |    9.09 |      100 |       0 |    9.09 | 54-275,281-283    
  ...tChatView.tsx |   21.05 |      100 |       0 |   21.05 | 21-39             
  ...tComposer.tsx |   69.48 |    33.33 |   66.66 |   69.48 | ...51,269,277-279 
  AgentFooter.tsx  |   15.38 |      100 |       0 |   15.38 | 28-65             
  AgentHeader.tsx  |   15.38 |      100 |       0 |   15.38 | 27-64             
  AgentTabBar.tsx  |    87.9 |    63.88 |     100 |    87.9 | ...88,110-118,136 
  ...oryAdapter.ts |     100 |    91.83 |     100 |     100 | 103,109-110,138   
  index.ts         |       0 |        0 |       0 |       0 | 1-12              
 ...mponents/arena |    42.3 |    68.69 |   73.68 |    42.3 |                   
  ArenaCards.tsx   |   73.06 |    71.79 |   85.71 |   73.06 | ...83-185,321-326 
  ...ectDialog.tsx |   83.48 |    69.86 |   88.88 |   83.48 | ...88-392,409-410 
  ...artDialog.tsx |       0 |        0 |       0 |       0 | 1-166             
  ...tusDialog.tsx |       0 |        0 |       0 |       0 | 1-288             
  ...topDialog.tsx |       0 |        0 |       0 |       0 | 1-213             
 ...ackground-view |   82.95 |    81.71 |   92.72 |   82.95 |                   
  ...sksDialog.tsx |   78.73 |    77.65 |   84.61 |   78.73 | ...1809,1833-1839 
  ...TasksPill.tsx |   67.74 |    86.66 |     100 |   67.74 | ...04-124,132-140 
  ...gentPanel.tsx |   97.08 |    86.31 |     100 |   97.08 | 132,442-446,520   
  agent-forest.ts  |    99.2 |    93.93 |     100 |    99.2 | 258               
  ...Visibility.ts |     100 |      100 |     100 |     100 |                   
  ...e-overlay.tsx |    88.2 |    76.47 |     100 |    88.2 | ...36-138,140-142 
 ...nts/extensions |   84.32 |    76.78 |   83.33 |   84.32 |                   
  ...gerDialog.tsx |   82.15 |    76.08 |     100 |   82.15 | ...91-198,258,260 
  TabBar.tsx       |   97.29 |    88.88 |     100 |   97.29 | 33                
  index.ts         |       0 |        0 |       0 |       0 | 1-12              
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...tensions/steps |   46.26 |       85 |   58.82 |   46.26 |                   
  ...ctionStep.tsx |   95.12 |    92.85 |   85.71 |   95.12 | 84-86,89          
  ...etailStep.tsx |       0 |        0 |       0 |       0 | 1-145             
  ...nListStep.tsx |   75.26 |    88.37 |   66.66 |   75.26 | ...53,174,203-209 
  ...electStep.tsx |       0 |        0 |       0 |       0 | 1-83              
  ...nfirmStep.tsx |   16.32 |      100 |       0 |   16.32 | 28-74             
  index.ts         |       0 |        0 |       0 |       0 | 1-11              
 ...xtensions/tabs |   71.92 |    68.21 |   70.83 |   71.92 |                   
  DiscoverTab.tsx  |   68.22 |    67.66 |   55.55 |   68.22 | ...93,656-660,664 
  InstalledTab.tsx |   75.49 |    67.44 |   83.33 |   75.49 | ...77,782-783,820 
  SourcesTab.tsx   |   71.67 |    70.47 |   77.77 |   71.67 | ...28,547,621-633 
 ...tensions/views |   50.97 |    52.38 |   20.83 |   50.97 |                   
  ...tionsView.tsx |   73.75 |    56.36 |   66.66 |   73.75 | ...30,353,369-374 
  ...tionsView.tsx |   43.45 |    44.82 |    6.66 |   43.45 | ...98-405,408-420 
  ...etailView.tsx |    9.56 |      100 |       0 |    9.56 | 40-67,70-158      
 ...mponents/hooks |   87.11 |    81.37 |   91.89 |   87.11 |                   
  ...rListBody.tsx |   95.29 |    85.18 |     100 |   95.29 | 95-98             
  ...etailStep.tsx |   75.32 |    71.42 |      60 |   75.32 | ...56-169,173-186 
  ...etailStep.tsx |     100 |      100 |     100 |     100 |                   
  ...rListStep.tsx |     100 |      100 |     100 |     100 |                   
  ...entHeader.tsx |     100 |    85.71 |     100 |     100 | 47                
  ...rListStep.tsx |     100 |      100 |     100 |     100 |                   
  ...etailStep.tsx |     100 |      100 |     100 |     100 |                   
  ...abledStep.tsx |     100 |      100 |     100 |     100 |                   
  ...sListStep.tsx |     100 |      100 |     100 |     100 |                   
  ...entDialog.tsx |   72.29 |    70.49 |     100 |   72.29 | ...51,563-568,572 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-13              
  ...erGrouping.ts |     100 |      100 |     100 |     100 |                   
  sourceLabels.ts  |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...components/mcp |   40.04 |    61.53 |   70.58 |   40.04 |                   
  ...ealthPill.tsx |   68.42 |    85.71 |     100 |   68.42 | 40-46             
  ...entDialog.tsx |   32.09 |    26.19 |      40 |   32.09 | ...12,914,927-933 
  ...valDialog.tsx |   15.06 |      100 |       0 |   15.06 | 40-109            
  constants.ts     |     100 |      100 |     100 |     100 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-35              
  types.ts         |     100 |      100 |     100 |     100 |                   
  utils.ts         |      97 |       95 |     100 |      97 | 24,113-114        
 ...ents/mcp/steps |   53.94 |    73.51 |   57.14 |   53.94 |                   
  ...icateStep.tsx |    5.65 |      100 |       0 |    5.65 | 40-66,69-308      
  ...electStep.tsx |   10.95 |      100 |       0 |   10.95 | 16-88             
  ...etailStep.tsx |     100 |      100 |     100 |     100 |                   
  ...eListStep.tsx |   99.09 |    97.36 |     100 |   99.09 | 71                
  ...etailStep.tsx |   62.83 |       60 |   33.33 |   62.83 | ...87-296,307-332 
  ...rListStep.tsx |   88.53 |    81.25 |     100 |   88.53 | ...64,170,175-180 
  ...etailStep.tsx |    10.3 |      100 |       0 |    10.3 | ...1,67-79,82-140 
  ToolListStep.tsx |   69.29 |       50 |     100 |   69.29 | ...23,126,135-144 
 ...nents/messages |   89.93 |    86.42 |   85.29 |   89.93 |                   
  ...ionDialog.tsx |   89.23 |     84.9 |   81.81 |   89.23 | ...75,593,611-613 
  BtwMessage.tsx   |     100 |      100 |     100 |     100 |                   
  ...upDisplay.tsx |     100 |    94.73 |     100 |     100 | ...43,289,402,432 
  ...onMessage.tsx |   92.06 |    82.35 |     100 |   92.06 | 58-60,62,64       
  ...nMessages.tsx |   93.46 |      100 |   76.92 |   93.46 | ...90-292,295-298 
  DiffRenderer.tsx |   93.17 |    86.02 |     100 |   93.17 | ...07,235-236,302 
  ...tsDisplay.tsx |   97.08 |    77.77 |     100 |   97.08 | 95,97,106         
  ...usMessage.tsx |   81.73 |     65.9 |      75 |   81.73 | ...10-214,222,245 
  ...tsDisplay.tsx |   95.52 |    88.31 |     100 |   95.52 | ...40,142,175-180 
  ...ssMessage.tsx |    12.5 |      100 |       0 |    12.5 | 18-59             
  ...edMessage.tsx |   21.05 |      100 |       0 |   21.05 | 23-39             
  ...sMessages.tsx |   59.04 |       50 |    37.5 |   59.04 | ...21-126,147-159 
  ...ryMessage.tsx |   13.63 |      100 |       0 |   13.63 | 23-64             
  ...onMessage.tsx |   91.38 |    81.02 |     100 |   91.38 | ...33-635,642-644 
  ...upMessage.tsx |   98.32 |    95.16 |     100 |   98.32 | 184-187,414       
  ToolMessage.tsx  |   92.62 |    85.29 |   93.33 |   92.62 | ...-982,1009-1011 
 ...ponents/shared |   85.79 |    81.95 |   94.11 |   85.79 |                   
  ...ctionList.tsx |     100 |      100 |      75 |     100 |                   
  ...tonSelect.tsx |     100 |      100 |     100 |     100 |                   
  EnumSelector.tsx |     100 |    96.42 |     100 |     100 | 58                
  ...rBoundary.tsx |     100 |      100 |     100 |     100 |                   
  MaxSizedBox.tsx  |   84.71 |    86.95 |      90 |   84.71 | ...67-568,685-686 
  MultiSelect.tsx  |   93.58 |       75 |     100 |   93.58 | ...43,199-201,211 
  ...tonSelect.tsx |     100 |      100 |     100 |     100 |                   
  ...ontroller.tsx |     100 |    83.33 |     100 |     100 | 73,93-95          
  ...eSelector.tsx |     100 |       60 |     100 |     100 | 40-45             
  ...lableList.tsx |   81.48 |    84.84 |     100 |   81.48 | 46-66,73-76       
  StaticRender.tsx |   72.72 |      100 |     100 |   72.72 | 31-33             
  TextInput.tsx    |    80.8 |    67.24 |      80 |    80.8 | ...36-240,252-258 
  ...ontroller.tsx |     100 |    81.81 |     100 |     100 | 59-62             
  ...apsedTime.tsx |     100 |      100 |     100 |     100 |                   
  ...Indicator.tsx |     100 |      100 |     100 |     100 |                   
  ...lizedList.tsx |   88.51 |    83.75 |   81.81 |   88.51 | ...51-779,792,887 
  text-buffer.ts   |   85.98 |    81.81 |   97.91 |   85.98 | ...2664,2762-2763 
  ...er-actions.ts |   73.93 |    67.22 |     100 |   73.93 | ...32-733,934-936 
 ...ponents/skills |       0 |        0 |       0 |       0 |                   
  ...gerDialog.tsx |       0 |        0 |       0 |       0 | 1-681             
 ...ents/subagents |       0 |        0 |       0 |       0 |                   
  constants.ts     |       0 |        0 |       0 |       0 | 1-71              
  index.ts         |       0 |        0 |       0 |       0 | 1-11              
  reducers.tsx     |       0 |        0 |       0 |       0 | 1-190             
  types.ts         |       0 |        0 |       0 |       0 | 1-125             
  utils.ts         |       0 |        0 |       0 |       0 | 1-102             
 ...bagents/create |       0 |        0 |       0 |       0 |                   
  ...ionWizard.tsx |       0 |        0 |       0 |       0 | 1-299             
  ...rSelector.tsx |       0 |        0 |       0 |       0 | 1-85              
  ...onSummary.tsx |       0 |        0 |       0 |       0 | 1-331             
  ...tionInput.tsx |       0 |        0 |       0 |       0 | 1-177             
  ...dSelector.tsx |       0 |        0 |       0 |       0 | 1-63              
  ...nSelector.tsx |       0 |        0 |       0 |       0 | 1-58              
  ...EntryStep.tsx |       0 |        0 |       0 |       0 | 1-78              
  ToolSelector.tsx |       0 |        0 |       0 |       0 | 1-253             
 ...bagents/manage |   14.14 |    53.19 |    37.5 |   14.14 |                   
  ...ctionStep.tsx |       0 |        0 |       0 |       0 | 1-103             
  ...eleteStep.tsx |       0 |        0 |       0 |       0 | 1-62              
  ...tEditStep.tsx |       0 |        0 |       0 |       0 | 1-124             
  ...ctionStep.tsx |   35.61 |    59.52 |     100 |   35.61 | ...21-433,438-440 
  ...iewerStep.tsx |       0 |        0 |       0 |       0 | 1-73              
  ...gerDialog.tsx |       0 |        0 |       0 |       0 | 1-341             
 ...mponents/views |   69.81 |    72.64 |   61.11 |   69.81 |                   
  ContextUsage.tsx |   70.88 |    63.88 |      80 |   70.88 | ...20-426,463-557 
  DoctorReport.tsx |     9.8 |      100 |       0 |     9.8 | 25-54,57-131      
  ...sionsList.tsx |   88.05 |       75 |     100 |   88.05 | 70-77             
  McpStatus.tsx    |   92.01 |     73.8 |     100 |   92.01 | ...36,175-177,262 
  SkillsList.tsx   |   20.51 |      100 |       0 |   20.51 | 17-20,27-57       
  ToolsList.tsx    |     100 |      100 |     100 |     100 |                   
 src/ui/contexts   |   83.96 |    81.62 |    86.3 |   83.96 |                   
  ...ewContext.tsx |   64.83 |    88.88 |      50 |   64.83 | ...16-219,225-235 
  AppContext.tsx   |      80 |       50 |     100 |      80 | 19-20             
  ...ewContext.tsx |   92.45 |    62.79 |      50 |   92.45 | ...69-270,272-276 
  ...igContext.tsx |   81.81 |       50 |     100 |   81.81 | 15-16             
  ...ssContext.tsx |   85.65 |    84.85 |     100 |   85.65 | ...1612-1614,1620 
  ...owContext.tsx |   91.07 |    81.81 |     100 |   91.07 | 47-48,60-62       
  ...deContext.tsx |     100 |      100 |      50 |     100 |                   
  ...onContext.tsx |   80.77 |       80 |    92.3 |   80.77 | ...31-434,443-446 
  ...gsContext.tsx |     100 |      100 |     100 |     100 |                   
  ...usContext.tsx |     100 |      100 |     100 |     100 |                   
  ...ngContext.tsx |   71.42 |       50 |     100 |   71.42 | 17-20             
  ...utContext.tsx |   85.71 |      100 |   66.66 |   85.71 | 13-14             
  ...edContext.tsx |     100 |      100 |      50 |     100 |                   
  ...nsContext.tsx |   88.88 |       50 |     100 |   88.88 | 156-157           
  ...teContext.tsx |   86.66 |       50 |     100 |   86.66 | 235-236           
  ...deContext.tsx |      80 |     87.5 |      75 |      80 | ...11-112,118-120 
  ...rtContext.tsx |     100 |      100 |     100 |     100 |                   
 src/ui/daemon     |   88.35 |    73.51 |   95.45 |   88.35 |                   
  ...ui-adapter.ts |   88.35 |    73.51 |   95.45 |   88.35 | ...74,792-793,879 
 src/ui/editors    |       0 |        0 |       0 |       0 |                   
  ...ngsManager.ts |       0 |        0 |       0 |       0 | 1-67              
 src/ui/hooks      |   84.84 |    82.63 |   87.75 |   84.84 |                   
  ...dProcessor.ts |   85.53 |    85.13 |     100 |   85.53 | ...-970,1017-1018 
  ...ention-ref.ts |   97.72 |       84 |     100 |   97.72 | 65                
  keyToAnsi.ts     |    3.92 |      100 |       0 |    3.92 | 19-77             
  ...esourceRef.ts |     100 |      100 |     100 |     100 |                   
  ...completion.ts |     100 |    95.45 |     100 |     100 | 95                
  ...ention-ref.ts |     100 |      100 |     100 |     100 |                   
  ...dProcessor.ts |   94.62 |    73.58 |     100 |   94.62 | ...86-287,292-293 
  ...dProcessor.ts |   85.63 |    68.16 |   81.81 |   85.63 | ...1452,1473-1477 
  ...rt-command.ts |     100 |      100 |     100 |     100 |                   
  ...sced-flush.ts |     100 |      100 |     100 |     100 |                   
  ...ng-enabled.ts |     100 |      100 |     100 |     100 |                   
  ...oice-input.ts |   92.36 |    81.95 |   66.66 |   92.36 | ...00,502-503,658 
  ...ke-repaint.ts |     100 |      100 |     100 |     100 |                   
  ...amingState.ts |   12.22 |      100 |       0 |   12.22 | 54-157            
  ...agerDialog.ts |   88.23 |      100 |     100 |   88.23 | 20,24             
  ...dScrollbar.ts |     100 |      100 |     100 |     100 |                   
  ...ationFrame.ts |      42 |       75 |     100 |      42 | 42-44,53-59,62-87 
  ...odeCommand.ts |   58.82 |      100 |     100 |   58.82 | 28,33-48          
  ...enaCommand.ts |      85 |      100 |     100 |      85 | 23-24,29          
  ...aInProcess.ts |   27.92 |       80 |      25 |   27.92 | ...69-170,173-175 
  ...Completion.ts |   86.44 |    88.48 |     100 |   86.44 | ...14-515,525-541 
  ...ifications.ts |   87.82 |    96.77 |     100 |   87.82 | 138-152           
  ...tIndicator.ts |   88.28 |    81.57 |     100 |   88.28 | ...66,175,179-187 
  ...waySummary.ts |   96.26 |       75 |     100 |   96.26 | 126-128,170       
  ...ndTaskView.ts |   94.81 |    76.59 |     100 |   94.81 | 162-166,255,261   
  ...chedScroll.ts |     100 |      100 |     100 |     100 |                   
  ...ketedPaste.ts |    23.8 |      100 |       0 |    23.8 | 19-37             
  ...nchCommand.ts |   95.23 |    82.69 |     100 |   95.23 | ...53-154,277-280 
  ...ompletion.tsx |   97.09 |    87.09 |     100 |   97.09 | ...23-324,334-335 
  ...dMigration.ts |    92.1 |    88.88 |     100 |    92.1 | 42-44             
  useCompletion.ts |   96.29 |    90.56 |     100 |   96.29 | ...17-218,222-223 
  ...nitMessage.ts |     100 |      100 |     100 |     100 |                   
  ...extualTips.ts |   78.26 |       50 |     100 |   78.26 | ...2,75-79,96-104 
  ...eteCommand.ts |   89.52 |    90.69 |     100 |   89.52 | ...98-106,114-115 
  ...ialogClose.ts |   36.11 |       10 |     100 |   36.11 | ...89-195,202-207 
  useDiffData.ts   |       0 |        0 |       0 |       0 | 1-87              
  ...oublePress.ts |   53.12 |       75 |     100 |   53.12 | 33-35,41-54       
  ...orSettings.ts |     100 |      100 |     100 |     100 |                   
  ...Completion.ts |   99.12 |    97.67 |     100 |   99.12 | 182-183           
  ...ionUpdates.ts |   93.72 |    92.98 |     100 |   93.72 | ...87-291,314-320 
  ...agerDialog.ts |   88.88 |      100 |     100 |   88.88 | 21,25             
  ...backDialog.ts |    63.9 |    76.47 |   66.66 |    63.9 | ...66-168,190-191 
  useFocus.ts      |     100 |      100 |     100 |     100 |                   
  ...olderTrust.ts |     100 |    93.33 |     100 |     100 | 62                
  ...ggestions.tsx |   96.47 |    78.94 |     100 |   96.47 | 121,155-156       
  ...miniStream.ts |   83.07 |    80.25 |   74.35 |   83.07 | ...4909-4911,4913 
  ...BranchName.ts |     100 |    94.44 |     100 |     100 | 54                
  ...oryManager.ts |   98.01 |    98.36 |     100 |   98.01 | 139-142           
  ...ooksDialog.ts |    87.5 |      100 |     100 |    87.5 | 19,23             
  ...stListener.ts |     100 |      100 |     100 |     100 |                   
  ...nAuthError.ts |   76.19 |       50 |     100 |   76.19 | 39-40,43-45       
  ...putHistory.ts |   92.59 |    85.71 |     100 |   92.59 | 63-64,72,94-96    
  ...storyStore.ts |     100 |    94.11 |     100 |     100 | 69                
  useKeypress.ts   |     100 |      100 |     100 |     100 |                   
  ...rdProtocol.ts |   36.36 |      100 |       0 |   36.36 | 24-31             
  ...unchEditor.ts |   22.58 |      100 |      50 |   22.58 | 11-32,44-85       
  ...gIndicator.ts |     100 |    96.66 |     100 |     100 | 109               
  useLogger.ts     |      16 |      100 |       0 |      16 | 15-45             
  useMCPHealth.ts  |   63.15 |       80 |      50 |   63.15 | 42-52,64-67       
  ...cpApproval.ts |   93.12 |    86.11 |     100 |   93.12 | ...24-127,139-140 
  useMcpDialog.ts  |    87.5 |      100 |     100 |    87.5 | 19,23             
  ...moryDialog.ts |    87.5 |      100 |     100 |    87.5 | 19,23             
  ...oryMonitor.ts |   83.14 |    78.57 |     100 |   83.14 | 54-63,74-79       
  ...ssageQueue.ts |     100 |     97.4 |     100 |     100 | 175,262           
  ...delCommand.ts |     100 |       96 |     100 |     100 | 61                
  ...ouseEvents.ts |   94.89 |       95 |   83.33 |   94.89 | 78-82             
  ...raseCycler.ts |   84.74 |    76.47 |     100 |   84.74 | ...49,52-53,69-71 
  ...rredEditor.ts |   58.33 |    22.22 |     100 |   58.33 | 23-27,29-33       
  ...derUpdates.ts |    87.4 |    78.78 |     100 |    87.4 | ...71,321-333,381 
  useQwenAuth.ts   |     100 |      100 |     100 |     100 |                   
  ...lScheduler.ts |   89.11 |    87.32 |     100 |   89.11 | ...42-444,476-486 
  ...oryCommand.ts |       0 |        0 |       0 |       0 | 1-7               
  ...umeCommand.ts |   95.18 |    76.47 |     100 |   95.18 | 118-119,220-225   
  ...ompletion.tsx |   90.67 |    83.33 |     100 |   90.67 | ...02,105,138-141 
  ...ectionList.ts |   97.12 |    96.22 |     100 |   97.12 | ...92-193,247-250 
  ...sionPicker.ts |   92.87 |    90.35 |     100 |   92.87 | ...99-501,503-505 
  ...earchInput.ts |     100 |    97.29 |     100 |     100 | 82                
  ...ngsCommand.ts |   18.75 |      100 |       0 |   18.75 | 10-25             
  ...ellHistory.ts |   93.28 |    80.95 |     100 |   93.28 | ...96,153-154,164 
  ...oryCommand.ts |   85.48 |    58.33 |     100 |   85.48 | 22-28,40,71       
  ...agerDialog.ts |   88.23 |      100 |     100 |   88.23 | 20,24             
  ...Completion.ts |   82.85 |    85.13 |   94.73 |   82.85 | ...78-680,688-724 
  ...tateAndRef.ts |     100 |      100 |     100 |     100 |                   
  ...tatsDialog.ts |     100 |      100 |     100 |     100 |                   
  useStatusLine.ts |   97.13 |    93.33 |     100 |   97.13 | ...78-382,478-485 
  ...eateDialog.ts |   88.23 |      100 |     100 |   88.23 | 14,18             
  ...mInProcess.ts |   27.35 |       80 |      25 |   27.35 | ...82-183,186-188 
  ...tification.ts |     100 |     87.5 |     100 |     100 | 50                
  ...alProgress.ts |   67.34 |    58.82 |   66.66 |   67.34 | 52-53,61-68,79-85 
  ...rminalSize.ts |     100 |      100 |     100 |     100 |                   
  ...emeCommand.ts |   67.01 |    29.41 |     100 |   67.01 | ...10-111,115-116 
  useTimer.ts      |   97.59 |    94.73 |     100 |   97.59 | 17-18             
  ...lMigration.ts |       0 |        0 |       0 |       0 |                   
  ...rustModify.ts |     100 |    90.47 |     100 |     100 | 112,134           
  useTurnDiffs.ts  |   95.12 |    78.57 |     100 |   95.12 | 133-134,156-157   
  ...elcomeBack.ts |   87.36 |     90.9 |     100 |   87.36 | ...,94-96,114-115 
  ...reeSession.ts |   93.75 |       70 |     100 |   93.75 | 47-48,72          
  vim.ts           |      74 |    67.56 |   69.23 |      74 | ...1854-1861,1869 
 src/ui/layouts    |    91.2 |    89.47 |     100 |    91.2 |                   
  ...AppLayout.tsx |    90.9 |     87.5 |     100 |    90.9 | 60-62,110-115,151 
  ...AppLayout.tsx |   91.66 |    92.85 |     100 |   91.66 | 75-80             
 src/ui/models     |   80.72 |       80 |   71.42 |   80.72 |                   
  ...ableModels.ts |   80.72 |       80 |   71.42 |   80.72 | ...,61-71,125-127 
 ...noninteractive |     100 |      100 |    6.66 |     100 |                   
  ...eractiveUi.ts |     100 |      100 |    6.66 |     100 |                   
 src/ui/selection  |   86.47 |    79.88 |   96.66 |   86.47 |                   
  screen-buffer.ts |   94.73 |    64.28 |     100 |   94.73 | 51-52             
  ...ion-coords.ts |     100 |      100 |     100 |     100 |                   
  ...ction-span.ts |   92.72 |       90 |     100 |   92.72 | 37-38,67-68       
  ...tion-state.ts |   85.71 |      100 |   88.88 |   85.71 | 51-58             
  ...ction-text.ts |   92.85 |    92.45 |     100 |   92.85 | 30-34,114-115     
  ...selection.tsx |   80.31 |    59.64 |     100 |   80.31 | ...13-314,330-331 
 src/ui/state      |      95 |    81.81 |     100 |      95 |                   
  extensions.ts    |      95 |    81.81 |     100 |      95 | 69-70,89          
 src/ui/themes     |    98.5 |    73.17 |     100 |    98.5 |                   
  ansi-light.ts    |     100 |      100 |     100 |     100 |                   
  ansi.ts          |     100 |      100 |     100 |     100 |                   
  atom-one-dark.ts |     100 |      100 |     100 |     100 |                   
  ayu-light.ts     |     100 |      100 |     100 |     100 |                   
  ayu.ts           |     100 |      100 |     100 |     100 |                   
  color-utils.ts   |   99.23 |    97.05 |     100 |   99.23 | 277-278           
  default-light.ts |     100 |      100 |     100 |     100 |                   
  default.ts       |     100 |      100 |     100 |     100 |                   
  ...inal-theme.ts |   88.59 |    85.96 |     100 |   88.59 | ...57-261,266-270 
  dracula.ts       |     100 |      100 |     100 |     100 |                   
  github-dark.ts   |     100 |      100 |     100 |     100 |                   
  github-light.ts  |     100 |      100 |     100 |     100 |                   
  googlecode.ts    |     100 |      100 |     100 |     100 |                   
  no-color.ts      |     100 |      100 |     100 |     100 |                   
  qwen-dark.ts     |     100 |      100 |     100 |     100 |                   
  qwen-light.ts    |     100 |      100 |     100 |     100 |                   
  ...tic-tokens.ts |     100 |      100 |     100 |     100 |                   
  ...-of-purple.ts |     100 |      100 |     100 |     100 |                   
  theme-manager.ts |   88.68 |    84.52 |     100 |   88.68 | ...83-392,397-398 
  theme.ts         |     100 |    38.02 |     100 |     100 | ...34-449,457-461 
  xcode.ts         |     100 |      100 |     100 |     100 |                   
 src/ui/utils      |   86.74 |    84.95 |   95.48 |   86.74 |                   
  ...Colorizer.tsx |   80.31 |    85.41 |     100 |   80.31 | ...00-201,313-339 
  ...nRenderer.tsx |   79.84 |     75.6 |     100 |   79.84 | ...66,270,328-329 
  ...wnDisplay.tsx |   92.87 |    93.46 |     100 |   92.87 | ...,955,1002-1020 
  ...idDiagram.tsx |   87.79 |    95.34 |     100 |   87.79 | 156-179           
  ...eRenderer.tsx |   92.38 |    81.91 |   95.23 |   92.38 | ...43-746,799-804 
  ...odeDisplay.ts |   94.28 |    85.71 |     100 |   94.28 | 23,40             
  asciiCharts.ts   |    96.7 |     87.5 |     100 |    96.7 | 170-177,278       
  ...dWorkUtils.ts |     100 |      100 |     100 |     100 |                   
  ...boardUtils.ts |   52.52 |    73.25 |   91.66 |   52.52 | ...23,626-635,638 
  commandUtils.ts  |   96.17 |    88.88 |     100 |   96.17 | ...77,179-180,323 
  computeStats.ts  |     100 |      100 |     100 |     100 |                   
  customBanner.ts  |   90.68 |    91.22 |     100 |   90.68 | ...13,324-327,334 
  displayUtils.ts  |   73.84 |    73.91 |     100 |   73.84 | ...34,36-40,42-46 
  formatters.ts    |   94.87 |    98.18 |     100 |   94.87 | 116-119           
  goal-runtime.ts  |   91.17 |    94.73 |     100 |   91.17 | 31-33             
  gradientUtils.ts |     100 |      100 |     100 |     100 |                   
  highlight.ts     |     100 |      100 |     100 |     100 |                   
  ...gap-notice.ts |     100 |      100 |     100 |     100 |                   
  ...oryMapping.ts |     100 |       95 |     100 |     100 | 44,103            
  historyUtils.ts  |   96.03 |     97.1 |     100 |   96.03 | 103-106           
  inline-math.ts   |   98.48 |    95.23 |     100 |   98.48 | 129-130           
  input-mouse.ts   |     100 |    85.71 |     100 |     100 | 48,93             
  isNarrowWidth.ts |     100 |      100 |     100 |     100 |                   
  ...olDetector.ts |   68.81 |       75 |   66.66 |   68.81 | ...27-132,160-161 
  latexRenderer.ts |   94.95 |     73.8 |     100 |   94.95 | ...76-178,184-187 
  layoutUtils.ts   |     100 |      100 |     100 |     100 |                   
  list-mouse.ts    |     100 |      100 |     100 |     100 |                   
  ...ightLoader.ts |     100 |       95 |     100 |     100 | 81                
  ...nUtilities.ts |   98.72 |    94.36 |     100 |   98.72 | 145-146           
  ...t-position.ts |     100 |     87.5 |     100 |     100 | 85                
  ...geRenderer.ts |   86.51 |    70.16 |   95.12 |   86.51 | ...1286,1326-1332 
  ...alRenderer.ts |   86.69 |     71.9 |     100 |   86.69 | ...1476,1513-1519 
  ...lsBySource.ts |     100 |    95.23 |     100 |     100 | 84                
  mouse.ts         |   92.85 |    74.19 |     100 |   92.85 | ...38,145,149-152 
  osc8.ts          |   90.43 |    78.33 |     100 |   90.43 | ...59,244,248-249 
  ...red-height.ts |   98.38 |     97.1 |     100 |   98.38 | 195-197           
  ...mConstants.ts |     100 |      100 |     100 |     100 |                   
  restoreGoal.ts   |     100 |      100 |     100 |     100 |                   
  ...storyUtils.ts |   79.42 |    78.08 |     100 |   79.42 | ...50-572,703-704 
  ...ickerUtils.ts |     100 |      100 |     100 |     100 |                   
  ...evel-label.ts |   77.77 |    66.66 |     100 |   77.77 | 18,22-24          
  ...are-cursor.ts |   89.47 |    85.71 |     100 |   89.47 | 39-44             
  ...ataService.ts |   93.17 |     79.1 |     100 |   93.17 | ...14,227,254-256 
  suggestions.ts   |     100 |      100 |     100 |     100 |                   
  ...izedOutput.ts |   94.94 |      100 |   88.88 |   94.94 | 112-117           
  ...nal-buffer.ts |     100 |      100 |     100 |     100 |                   
  ...e-renderer.ts |   89.19 |    79.54 |     100 |   89.19 | ...14,316-318,434 
  ...wOptimizer.ts |     100 |    96.77 |     100 |     100 | 69                
  terminalSetup.ts |    4.37 |      100 |       0 |    4.37 | 44-393            
  textUtils.ts     |   97.94 |    95.45 |   94.11 |   97.94 | ...82-283,443-444 
  ...background.ts |     100 |      100 |     100 |     100 |                   
  todoSnapshot.ts  |   90.42 |    92.85 |     100 |   90.42 | ...06-207,240-241 
  ...isplay-map.ts |     100 |      100 |     100 |     100 |                   
  updateCheck.ts   |     100 |    92.75 |     100 |     100 | 227-239,331       
  ...ow-keyword.ts |     100 |      100 |     100 |     100 |                   
 ...i/utils/export |   75.03 |     60.3 |   94.59 |   75.03 |                   
  collect.ts       |   71.27 |    66.38 |      96 |   71.27 | ...90-633,655-656 
  index.ts         |     100 |      100 |     100 |     100 |                   
  normalize.ts     |   80.42 |    50.68 |     100 |   80.42 | ...59-364,376-378 
  types.ts         |       0 |        0 |       0 |       0 | 1                 
  utils.ts         |     100 |      100 |     100 |     100 |                   
 ...ort/formatters |   52.92 |    47.22 |   71.42 |   52.92 |                   
  html.ts          |   84.61 |       50 |     100 |   84.61 | ...53,57-58,62-63 
  json.ts          |     100 |      100 |     100 |     100 |                   
  jsonl.ts         |   82.45 |     37.5 |     100 |   82.45 | ...48,50-51,65-66 
  markdown.ts      |   36.32 |    47.05 |      50 |   36.32 | ...16-219,233-295 
 src/ui/voice      |   80.94 |    72.59 |   80.55 |   80.94 |                   
  ...d-recorder.ts |     6.2 |      100 |       0 |     6.2 | ...33-159,162-163 
  ...o-recorder.ts |   84.61 |    93.33 |   57.14 |   84.61 | ...16-117,131-136 
  ...me-session.ts |   89.72 |    65.33 |   93.75 |   89.72 | ...99,305,316-319 
  sox-recorder.ts  |    92.7 |    71.87 |     100 |    92.7 | ...34-135,153-154 
  ...ailability.ts |     100 |      100 |     100 |     100 |                   
  ...e-keyterms.ts |     100 |      100 |     100 |     100 |                   
  voice-model.ts   |     100 |      100 |     100 |     100 |                   
  ...e-recorder.ts |   88.29 |    67.74 |   81.81 |   88.29 | ...,98-99,112,115 
  voice-refine.ts  |     100 |    93.33 |     100 |     100 | 92                
  ...ream-retry.ts |   86.79 |    68.42 |     100 |   86.79 | 16-18,48-49,59-60 
  ...am-session.ts |   88.02 |    66.17 |   84.61 |   88.02 | ...26,343-345,363 
  ...ranscriber.ts |     100 |      100 |     100 |     100 |                   
 src/utils         |   81.39 |    86.96 |   92.57 |   81.39 |                   
  ...p-profiler.ts |   98.39 |    90.56 |     100 |   98.39 | 141,185,235       
  acpModelUtils.ts |   97.36 |    95.19 |     100 |   97.36 | ...09-210,214-215 
  apiPreconnect.ts |   96.74 |    94.59 |     100 |   96.74 | 167-170           
  ...ol-call-id.ts |   84.61 |       60 |     100 |   84.61 | 26-27,37-38       
  ...ng-failure.ts |     100 |       95 |     100 |     100 | 72                
  checks.ts        |   33.33 |      100 |       0 |   33.33 | 23-28             
  ...-api-error.ts |     100 |    96.42 |     100 |     100 | 14                
  cleanup.ts       |   84.05 |    94.11 |      80 |   84.05 | 80,111-121        
  commands.ts      |   97.45 |    96.66 |     100 |   97.45 | 153-155           
  ...Calculator.ts |     100 |      100 |     100 |     100 |                   
  cpuProfiler.ts   |   70.38 |    71.83 |   88.88 |   70.38 | ...27,430-431,438 
  deepMerge.ts     |     100 |       90 |     100 |     100 | 41-43,49          
  ...re-runtime.ts |     100 |      100 |     100 |     100 |                   
  ...ScopeUtils.ts |   97.56 |    88.88 |     100 |   97.56 | 67                
  doctorChecks.ts  |   70.31 |    74.57 |     100 |   70.31 | ...95-301,325-341 
  ...putCapture.ts |   90.65 |    86.31 |     100 |   90.65 | ...73,371,373-374 
  ...arResolver.ts |   97.14 |    96.55 |     100 |   97.14 | 125-126           
  errors.ts        |   97.56 |    94.64 |     100 |   97.56 | 69-70,304-305     
  events.ts        |     100 |      100 |     100 |     100 |                   
  ...on-mention.ts |   88.48 |     82.6 |     100 |   88.48 | ...56-160,164-168 
  gitUtils.ts      |   92.85 |    86.66 |     100 |   92.85 | ...13-116,164-167 
  ...AutoUpdate.ts |    93.1 |       94 |      90 |    93.1 | 103,108,179-190   
  ...tyWarnings.ts |     100 |      100 |     100 |     100 |                   
  ...lationInfo.ts |   97.68 |    94.28 |     100 |   97.68 | ...64,381-382,427 
  jsonc-editor.ts  |   93.18 |    92.72 |     100 |   93.18 | ...80-381,384-385 
  languageUtils.ts |   98.88 |    97.05 |     100 |   98.88 | 184-185           
  load-undici.ts   |     100 |      100 |     100 |     100 |                   
  ...npm-update.ts |   86.64 |    77.02 |     100 |   86.64 | ...03-304,335-345 
  math.ts          |       0 |        0 |       0 |       0 | 1-15              
  ...er-mention.ts |     100 |    66.66 |     100 |     100 | 14,30,44-46       
  ...iagnostics.ts |   94.57 |    83.01 |   88.88 |   94.57 | ...05,311,315-317 
  ...serMessage.ts |     100 |      100 |     100 |     100 |                   
  ...onfigUtils.ts |   94.25 |    91.17 |     100 |   94.25 | ...30,436,439-443 
  ...iveHelpers.ts |   95.13 |    91.79 |     100 |   95.13 | ...53-454,552,565 
  osc.ts           |   97.18 |      100 |    87.5 |   97.18 | 182-183           
  package.ts       |   88.88 |    85.71 |     100 |   88.88 | 31-32             
  ...uggestions.ts |   84.29 |    70.83 |     100 |   84.29 | 70-76,92-103      
  processUtils.ts  |    92.3 |       80 |     100 |    92.3 | 45-46             
  readStdin.ts     |   93.67 |    94.11 |   85.71 |   93.67 | 79-83             
  relaunch.ts      |   95.87 |    89.28 |     100 |   95.87 | 103-105,131       
  resolvePath.ts   |     100 |      100 |     100 |     100 |                   
  runBudget.ts     |   99.35 |    96.77 |     100 |   99.35 | 119               
  sandbox-path.ts  |     100 |      100 |     100 |     100 |                   
  sandbox.ts       |   45.67 |    56.93 |   76.92 |   45.67 | ...1034,1046-1069 
  ...xImageName.ts |     100 |    77.77 |     100 |     100 | 10,18             
  sandboxMounts.ts |     100 |      100 |     100 |     100 |                   
  sessionPaths.ts  |   90.84 |    90.56 |     100 |   90.84 | ...81-182,185-186 
  settingsUtils.ts |   82.35 |    89.57 |      90 |   82.35 | ...25-743,750-758 
  spawnWrapper.ts  |     100 |      100 |     100 |     100 |                   
  ...ate-verify.ts |     100 |      100 |     100 |     100 |                   
  ...one-update.ts |   39.81 |    77.44 |   62.16 |   39.81 | ...1193,1196-1215 
  ...upProfiler.ts |   98.47 |    94.66 |     100 |   98.47 | 132-133,308       
  ...upWarnings.ts |     100 |      100 |     100 |     100 |                   
  stdioHelpers.ts  |     100 |       90 |     100 |     100 | 23                
  systemInfo.ts    |   95.12 |    90.27 |     100 |   95.12 | ...54-255,260-264 
  ...InfoFields.ts |    87.5 |    65.85 |     100 |    87.5 | ...24-125,146-147 
  ...alSequence.ts |     100 |    97.61 |     100 |     100 | 60                
  ...iffPreview.ts |   76.47 |       25 |     100 |   76.47 | 13,17,23-24       
  ...on-handler.ts |    73.8 |       75 |     100 |    73.8 | 17-18,25-26,67-73 
  ...e-relaunch.ts |   89.61 |    86.66 |      50 |   89.61 | 56-61,83-84       
  ...entEmitter.ts |     100 |      100 |     100 |     100 |                   
  ...ansionHook.ts |     100 |      100 |     100 |     100 |                   
  ...upWarnings.ts |   87.75 |       75 |     100 |   87.75 | 47-48,53-54,57-58 
  version.ts       |     100 |    66.66 |     100 |     100 | 11                
  ...ingHandler.ts |     100 |      100 |     100 |     100 |                   
  windowTitle.ts   |   95.45 |    93.33 |     100 |   95.45 | 54-55             
  ...WithBackup.ts |   65.04 |    77.77 |     100 |   65.04 | 97,112,133-172    
 ...s/housekeeping |   91.63 |    91.02 |      95 |   91.63 |                   
  cleanup.ts       |   95.77 |    95.83 |     100 |   95.77 | 70-72             
  ...eractionAt.ts |     100 |      100 |     100 |     100 |                   
  scheduler.ts     |   91.91 |    90.47 |    87.5 |   91.91 | 58-62,73,131-135  
  throttledOnce.ts |   86.66 |     86.2 |     100 |   86.66 | ...99,105,137-138 
-------------------|---------|----------|---------|---------|-------------------
Core Package - Full Text Report
Core full-text-summary.txt not found at: coverage_artifact/core/coverage/full-text-summary.txt

For detailed HTML reports, please see the 'coverage-reports-22.x-ubuntu-latest' artifact from the main CI run.

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, looks ready to ship — CI landed green after the review. ✅

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed — no blockers. Suggestions are inline.

中文说明

已审查——无阻断问题。 建议见行内评论。

— qwen3.8-max via Qwen Code /review (v0.21.4)

Comment thread packages/cli/src/acp-integration/session/acp-tool-result-text-projection.ts Outdated
@doudouOUC

Copy link
Copy Markdown
Collaborator Author

Follow-up for the review suggestions on cc1cedd:\n\n| Thread | Resolution |\n| --- | --- |\n| Oversized-field scanning | Accepted: added budget-aware early exit and removed per-code-unit object allocation. |\n| A2UI serverId coverage | Accepted: added a serverId-only exemption regression. |\n| Water-fill ordering | Accepted: added asymmetric exact-budget saturation coverage. |\n| Low-budget truncation guard | Accepted: changed the fallback to a budget-fitting surrogate-safe prefix. |\n| Shared detached-copy helper | Not taken: extracting a three-line idiom across CLI and Core would add coupling while the budgeting and cut-point algorithms intentionally differ. |\n\nValidation completed on the current head: 530 focused tests, 74 fast-path tests, formatting/lint, build/typecheck, bundle, deterministic Unicode/projection fuzz, and two stable fake-MCP wire captures. No additional code change was needed in this monitoring cycle.

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed. Suggestions are inline. Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.

中文说明

已审查。 建议见行内评论。 未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。

— qwen3.8-max via Qwen Code /review (v0.21.3)

Comment thread packages/cli/src/acp-integration/session/acp-tool-result-text-projection.ts Outdated
@doudouOUC

Copy link
Copy Markdown
Collaborator Author

Review follow-up for 6c16210:\n\n| Feedback | Decision | Result |\n| --- | --- | --- |\n| Reuse the Core allocator | Not taken | The Core helper is module-private and character-budget based; exporting it would widen the public cross-package surface and couple two different contracts. |\n| Narrow the A2UI exemption | Not taken | Whole-update A2UI exemption is an explicit contract here. A leading-bracket heuristic is not equivalent to the daemon parser, and generic A2UI/frame bounds remain out of scope. |\n| Bound pathological multi-block scanning | Fixed | Added cumulative early-stop plus per-block maximum-allocation caps, with a 600-block regression test. |\n\nValidation:\n- Related ACP projector/session/replay/export/fast-path tests: 607 passed.\n- Prettier and targeted ESLint: passed.\n- Build, typecheck, and bundle: passed.\n- Fixed-seed projector fuzz: 300 samples passed.\n- Exact-vs-capped allocator comparison: 97,102 feasible trials with zero divergence.\n- 600 × 40,000-byte benchmark median: approximately 32.1 ms before and 9.0 ms after on the same machine.\n- Two consecutive post-fix open-ended audit passes found no remaining actionable issue.\n\nAll three review threads have been answered and will be resolved.

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed. Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.

中文说明

已审查。 未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。

— qwen3.8-max via Qwen Code /review (v0.21.3)

@doudouOUC doudouOUC self-assigned this Aug 4, 2026
@doudouOUC
doudouOUC requested review from wenshao and yiliang114 August 4, 2026 12:31
@doudouOUC

Copy link
Copy Markdown
Collaborator Author

@qwen-code /takeover

@qwen-code-dev-bot qwen-code-dev-bot added the autofix/takeover Summon the autofix loop to manage this PR (remove to release; needs triage+) label Aug 5, 2026
@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤝 Takeover engaged: the autofix loop now manages this PR — it will address new review feedback and resolve base conflicts until the label is removed or the round cap is reached. Remove the autofix/takeover label (or comment @qwen-code /takeover stop) to release.

中文说明

🤝 已接管:autofix 循环现在管理此 PR —— 将持续处理新的评审反馈与 base 冲突,直到移除标签或达到轮次上限。移除 autofix/takeover 标签(或评论 @qwen-code /takeover stop)即可释放。

@qwen-code-dev-bot

qwen-code-dev-bot commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

AutoFix round 1 finishedview run. See this round's report below.

中文说明

AutoFix 第 1 轮已完成 —— 查看运行。本轮报告见下方。

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤖 Reviewed the latest feedback — no changes needed. Why, point by point: · 已审阅最新反馈——无需改动。逐点说明原因如下:

Autofix review round — no code change needed (PR #8450)

Every actionable finding in this round's feedback was already handled by earlier commits on this branch, and each claim was re-verified against the current head (6c162103f). No new code change is required, so there is no new commit. The verified-fix findings are listed in resolved-comments.txt so their threads can be resolved; the three declined findings already carry reply comments on their threads, so no duplicate replies were posted and those threads intentionally remain open with their recorded reasons.

Finding dispositions

Finding Disposition Evidence at current head
Budget-aware byte scanning (early exit, no per-code-unit objects) Already fixed in cc1ceddfd jsonStringPayloadByteLength takes stopAfterBytes and exits early; jsonPayloadBytesAt/jsonPayloadWidthAt return plain numbers, so no {bytes, width} allocation per code unit. Used by the rawOutput threshold check and the cumulative content fit scan.
A2UI exemption covered only by tool-name fixtures Already fixed in cc1ceddfd Test exempts A2UI updates identified only by serverId uses toolName: 'present_quality_report', which does not match A2UI_TOOL_RE, with serverId: 'dq-A2UI'; isA2uiToolMeta checks serverId first, so the serverId-primary path is independently pinned.
Water-filling order never pinned Already fixed in cc1ceddfd Test saturates the budget after smaller blocks reach their capacity uses three asymmetric oversized blocks (10,000 / 50,000 / 200,000 bytes) and asserts the smallest block stays complete while the projected content JSON is exactly 65,536 bytes — a flipped comparator undersaturates this.
Low-budget guard returned output larger than its budget Already fixed in cc1ceddfd truncateStringPayload now returns a budget-fitting, surrogate-safe prefix via selectPrefix when the budget is below the marker size.
Pathological multi-block scanning walked every block to the full array budget Already fixed in 6c162103f projectContent decides projection with a cumulative early-stopping fit scan, then caps each long-block scan at its largest possible allocation (availablePayloadBytes - baseTotal + base[i]), treating a cut-off scan as definitely truncated (maximumAllocation + 1). The 600-block regression test asserts all blocks survive truncated and the total is exactly 65,536 bytes.
Share copyString with core compactString Declined (reply already on thread) The only identical residue is a three-line detached-copy idiom; the budgeting, marker, and cut-point algorithms intentionally differ. Extracting a single-expression helper across CLI and Core adds cross-package coupling against the repository's simplicity-first rule.
Delegate to core allocateTextBudget Declined (reply already on thread) The Core helper is module-private and character-budget based, while ACP uses JSON-byte accounting with per-block marker reservations. Exporting it as public cross-package API would widen the core surface and couple two intentionally separate contracts.
Narrow the A2UI exemption to leading-[ content Declined (reply already on thread) Whole-update A2UI exemption is an explicit transport contract of this PR; a leading-bracket heuristic is not equivalent to the daemon's command-array parser, and generic A2UI/frame bounds are out of scope.

Round 3 of the automated review reported zero findings, and there are no failed or still-red checks.

Verification

Commands actually run this round (no commit was made; focused re-verification of the already-fixed findings):

  • npx vitest run src/acp-integration/session/acp-tool-result-text-projection.test.ts src/acp-integration/session/Session.test.ts src/acp-integration/session/history-replay-page.test.ts src/ui/utils/export/collect.test.ts (from packages/cli) — 4 files passed, 531 tests passed, including the serverId-only A2UI exemption, the asymmetric exact-budget saturation, and the 600-block pathological regression.
  • git status --porcelain=v1 --untracked-files=all — clean before and after; test artifacts (junit.xml, coverage/) are git-ignored.
  • Full PR diff (git diff origin/main...HEAD) re-read as skeptical reviewer: projection module, tests, Session.sendUpdate and replay sendUpdate wiring, and design-doc note are mutually consistent.
中文说明

Autofix 审查轮次 — 无需代码变更(PR #8450

本轮反馈中的每个可处理发现都已由本分支上的早期提交处理完毕,并且每条修复声明都已在当前 head(6c162103f)上重新核实。无需新的代码变更,因此没有新的提交。已核实修复的发现列在 resolved-comments.txt 中以便解析对应线程;三个被拒绝的发现其线程上已有回复评论,因此未重复发帖,这些线程按其记录的理由保持打开。

发现处理结果

发现 处理 当前 head 上的证据
预算感知的字节扫描(提前退出、无按 code unit 的对象分配) 已在 cc1ceddfd 修复 jsonStringPayloadByteLength 接受 stopAfterBytes 并提前退出;jsonPayloadBytesAt/jsonPayloadWidthAt 直接返回数值,不再按 code unit 分配 {bytes, width} 对象。rawOutput 阈值判断与累计内容适配扫描均使用该实现。
A2UI 豁免仅被 tool-name 用例覆盖 已在 cc1ceddfd 修复 测试 exempts A2UI updates identified only by serverId 使用 toolName: 'present_quality_report'(不匹配 A2UI_TOOL_RE)与 serverId: 'dq-A2UI'isA2uiToolMeta 优先检查 serverId,因此 serverId 优先路径被独立锁定。
水位线分配顺序从未被锁定 已在 cc1ceddfd 修复 测试 saturates the budget after smaller blocks reach their capacity 使用三个大小悬殊的超预算 block(10,000 / 50,000 / 200,000 字节),断言最小 block 保持完整、投影后的内容 JSON 恰好为 65,536 字节——比较器被反转时该用例无法用满预算。
低预算 guard 返回超出预算的输出 已在 cc1ceddfd 修复 当预算低于标记大小时,truncateStringPayload 现在通过 selectPrefix 返回符合预算且代理项安全的前缀。
病态多 block 扫描对每个 block 都按整个数组预算完整行走 已在 6c162103f 修复 projectContent 先用带提前终止的累计适配扫描判断是否需要投影,再把每个大 block 的扫描上限钉在其最大可能分配值(availablePayloadBytes - baseTotal + base[i]),被提前截断的扫描按"必定截断"处理(maximumAllocation + 1)。600 block 回归用例断言所有 block 均保留且被截断、总字节恰好为 65,536。
与 core 的 compactString 共享 copyString 已拒绝(线程上已有回复) 唯一完全相同的部分是三行的脱离式复制写法;预算计算、标记与切点算法是有意不同的。把单一表达式抽成跨 CLI 与 Core 的公共辅助函数会增加跨包耦合,违背仓库的简洁优先原则。
委托 core 的 allocateTextBudget 已拒绝(线程上已有回复) Core 侧辅助函数是模块私有且基于字符预算的,而 ACP 使用 JSON 字节计量并按块预留标记空间。把它导出为跨包公共 API 会扩大 core 公共面并把两个有意分离的契约耦合起来。
把 A2UI 豁免收窄到以 [ 开头的内容 已拒绝(线程上已有回复) 整个 update 级别的 A2UI 豁免是本 PR 的显式传输契约;开头括号启发式不等价于 daemon 的命令数组解析器,通用 A2UI/帧边界问题不在本 PR 范围内。

自动审查的第 3 轮报告零发现,且没有失败或仍然红色的检查。

验证

本轮实际运行的命令(未产生提交;仅对已修复发现做Focused复核):

  • npx vitest run src/acp-integration/session/acp-tool-result-text-projection.test.ts src/acp-integration/session/Session.test.ts src/acp-integration/session/history-replay-page.test.ts src/ui/utils/export/collect.test.ts(在 packages/cli 目录下)— 4 个文件全部通过,共 531 个测试通过,包括 serverId-only A2UI 豁免、非对称精确预算饱和、以及 600 block 病态回归。
  • git status --porcelain=v1 --untracked-files=all — 前后均为干净;测试产物(junit.xmlcoverage/)均被 git 忽略。
  • 以怀疑性审查者视角重读了完整 PR diff(git diff origin/main...HEAD):投影模块、测试、Session.sendUpdate 与 replay sendUpdate 接线、设计文档说明彼此一致。

Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。


🧠 Handled by Qwen Code · model/模型 qwen3.8-max

@wenshao

wenshao commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

Overview

Adds packages/cli/src/acp-integration/session/acp-tool-result-text-projection.ts, a pure transport-side projector that bounds two display fields of tool_call_update frames — eligible pure-text content arrays and primitive-string rawOutput — to 65,536 UTF-8 bytes each, measured as the JSON serialization of that field. It is wired at exactly two boundaries: Session.sendUpdate() (live) and replayContext.sendUpdate() in history-replay-page.ts (all replay collectors). Canonical transcripts, model-facing parts, schemas, and offline export are untouched.

The two wiring points are the right ones. I traced the six replay entry points (acpAgent.ts ×3, serve/routes/session.ts, virtual-subagent-sessions.ts, plus the paged route) — all funnel through replayContext, so there is no replay bypass. Offline export uses its own sendUpdate in ui/utils/export/collect.ts and is correctly excluded. Projection also lands before the _meta.recordId stamping and does not feed the replay cursor/TranscriptReplayStateV1, so resume state is unaffected.

Verification I ran

I extracted the projector at 6c16210 with the two external imports stubbed and exercised it directly on Node 24:

  • 300-case seeded fuzz over 1–8 blocks with quotes, backslashes, control chars, CJK, emoji, and lone surrogates, mixed with/without rawOutput: every projection was ≤ 65,536 JSON bytes for both fields, and project(project(u)) === project(u) held in all 300 cases (reference-identical, so idempotence is real, not just value-equal).
  • Escape-heavy (all-), lone-surrogate, and 5 MB/8 MB single-block inputs all stayed within budget.
  • Head/tail split measured 20.0/80.0.
  • Collapse boundary at 1,191 vs. 1,192 empty blocks reproduced exactly as the tests assert.

The JSON byte accounting is exact against JSON.stringify (their own test proves this too), and the lone-surrogate case errs conservatively (6 bytes assumed) for any downstream serializer that emits WTF-8. I found no correctness bug. The comments below are design/robustness/observability.

Strengths

  • Byte accounting is escape-aware and surrogate-safe, and every scan is early-stopped at the budget — so per-update cost is bounded by the budget, not by the source size. A 5 MB tool result projects in ~1 ms.
  • Structural sharing is deliberate: unprojected blocks and _meta keep their references, and returning the input by reference when nothing changed is what makes idempotence cheap.
  • copyString on the selected slices is genuinely necessary — V8 SlicedString/ConsString would otherwise pin the multi-MB parent, and replay collectors hold whole pages of updates in memory before serializing.
  • Design doc is committed alongside, per AGENTS.md. File naming, collocated tests, and header conventions all match.
  • The 20/80 head-tail ratio matches phase 1's fitText (headBudget = contentBudget / 5), so the two layers degrade consistently.

Findings

1. Multi-block degradation has a cliff and gets marker-dominated before it (design question)

Measured on the projector itself:

blocks × size result
600 × 40 KB (your own test) 600 blocks kept, 14 useful chars each; 23,400 of 65,536 bytes (36%) is marker text
700 × 200 B collapses to a single marker block — all content gone
1,000 × 100 B collapses (your test asserts this)

Because every reduced block must reserve the 39-char marker, the fixed cost is ~93 bytes/block, so anything past ~704 reduced blocks collapses the entire field. Two consequences worth weighing:

  • The 600-block outcome ("600 markers with 14 characters between them") is arguably less useful to a human than "the first block truncated to 64 KB, plus one block saying 595 further blocks omitted".
  • The transition from 600 blocks preserved to everything discarded is abrupt and non-monotonic in usefulness.

A keep-a-prefix-of-blocks-whole fallback would be strictly more informative and simpler than the collapse path, and would remove the MAX_CANONICAL_TEXT_BLOCKS / minimum-marker-set special cases. In practice buildToolResultContent emits 1–3 blocks per result, so this is defensive rather than hot — but the tests currently lock in the noisy behavior as intended.

2. Lossy truncation is completely silent

There is no debug log, no counter, and no _meta flag recording that a frame was projected. When a user reports "my tool output is cut off", nothing distinguishes producer truncation (phase 1), display truncation (this PR), and a genuinely short result — the marker string is the only signal, and it's in the payload. packages/cli/src/serve/large-pipe-frame-observer.ts already exists for exactly this kind of frame observability; at minimum a createDebugLogger('ACP_TOOL_RESULT_PROJECTION') line with { toolCallId, field, originalBytes } would pay for itself. An internal _meta marker (e.g. qwen.acp.projected: ['content','rawOutput']) would also let the TUI/web-shell render "output truncated for display" as UI chrome instead of inline text.

3. The marker discards artifact paths that are already on the frame

createTranscriptToolCallResultUpdate puts artifacts into _meta when the producer persisted output, and phase 1's fitText deliberately names those paths in its truncation header (Tool output truncated. Persisted tool-output artifact: <path>). This projector's marker is a fixed string and the design doc states "the marker makes no recovery claim" — but in the case that matters most (large output, artifact exists) the recovery path is available two fields away. Reusing phase 1's header format when _meta.artifacts is non-empty would make the two layers consistent and give users a way out.

4. Hardcoded budget with no override or kill switch

ACP_TOOL_RESULT_TEXT_JSON_BYTE_BUDGET = 65_536 is a module constant. Phase 1's equivalent is config-driven (config.getToolOutputBatchBudget(), with infinite/disabled treated as a no-op). Since this change is intentionally lossy on a first-party display path, an env or settings escape hatch (QWEN_ACP_TOOL_RESULT_DISPLAY_BUDGET, 0/Infinity = off) would let an integrator or a bug report opt out without a patch release. Related: the test hardcodes 256 * 1024 rather than importing LARGE_PIPE_FRAME_THRESHOLD_BYTES from large-pipe-frame-observer.ts, which is where that number actually comes from.

5. docs/design/final-tool-response-budget.md is now internally inconsistent

The PR updated the "Runtime boundaries" bullet, but the same document still says, in Problem: "Rich user-facing tool display is intentionally out of scope and can continue to use the existing result display", and Invariant 7: "Tool display remains independent from the model response." Both now have an exception. Since these docs are the contract for this workstream, please amend the Problem paragraph and Invariant 7 (or add an explicit "amended by 2026-08-03-acp-tool-result-text-projection.md" note) rather than only the boundary list.

6. copyString is ~37× more expensive than it needs to be

function copyString(value: string): string {
  return value.split('').join('');
}

Measured on a 52,000-char slice of a 5 MB parent (2,000 iterations):

implementation per call
value.split('').join('') 0.372 ms
Buffer.from(value, 'utf16le').toString('utf16le') 0.010 ms
(' ' + value).slice(1) 0.002 ms

split('') allocates a 52k-element array of single-code-unit strings on every projection. The utf16le round trip is code-unit-exact (lone surrogates included), forces a fresh SeqString, and is 37× faster; it's also more obviously a copy than the split/join idiom, which currently needs no comment but reads as a no-op. This runs on the ACP send path, so it's worth the one-line change.

7. Multi-block projection is O(blocks × budget), not O(blocks + budget)

Each oversized block is rescanned up to its maximumAllocation (≈ full available payload when block count is small relative to budget). Measured: 500 blocks / 10 MB total = 27.6 ms per projection, synchronously on sendUpdate. Single-block is ~1 ms, and realistic frames have 1–3 blocks, so this is not a live problem — but if a tool ever streams many-block updates, this becomes visible head-of-line latency on the ACP writer. Worth a comment noting the bound, or capping the per-block rescan.

8. Eligibility is silently all-or-nothing per array

One extra key anywhere in the array (e.g. an ACP annotations field on a text block) exempts the entire content array, unbounded. Today qwen constructs these blocks itself with exactly {type, content:{type,text}}, so this holds — but it's a fragile invariant defended only by hasExactKeys, and the failure mode is a silent return to 500 KB frames rather than a visible error. Combined with #2 (no logging), a future change that adds annotations would regress this feature with no signal at all. A debug log on "tool_call_update with oversized ineligible content" would catch that.

9. tool_call (start) frames bypass the projector entirely

The guard is record['sessionUpdate'] !== 'tool_call_update'. Start frames from createTranscriptToolCallStartUpdate always carry content: [], so results can't leak — but they carry rawInput (the tool args). I built a write_file-shaped start frame with a 500 KB content arg and measured a 700 KB frame that passes through untouched. The design doc's non-goals mention "structured payload bounds", which arguably covers this, but since the motivating issue is frame size, it's worth naming rawInput explicitly as the known remaining >256 KiB producer.

10. Minor / test nits

  • a2uiMeta() rebuilds a {toolName, serverId} object, but isA2uiToolMeta already does its own typeof x === 'string' checks — passing meta through a cast would be equivalent and drop ~14 lines.
  • Session.test.ts's "bounds textual tool results at the live ACP delivery boundary" also exercises collectHistoryReplayUpdates; the replay half belongs with the other replay assertions in history-replay-page.test.ts (where it partly already is).
  • Several assertions use exact toBe(65_536) on saturation. Correct today (I reproduced all of them), but they'll break on any marker-text tweak; toBeLessThanOrEqual plus one exact case would be less brittle.
  • The seeded fuzz test is a good addition — consider extending the same seeded generator to the projection invariants (bounded + idempotent), not just jsonStringJsonByteLength. That's the check that gave me the most confidence here.

Risk assessment

  • Canonical data: safe. I verified export, cursor state, and the replayer's internal pendingToolCalls all operate on unprojected objects; the projector never mutates its input (fuzz-confirmed).
  • UX regression: real but intended. daemon-tui-adapter.ts:357 builds resultDisplay from rawOutput, so Ctrl-O detail expansion in daemon-attached TUI now shows at most ~64 KB with an inline marker. Findings Where is the config saved? #2/如何自定义密钥文件 .env可能与其他文件冲突 #3 are what make this recoverable for users.
  • Security: no injection or trust surface. Truncation is 20/80 head-tail, so leading warnings and trailing exit information both survive; a malicious tool could emit the marker string to fake truncation, which is harmless.
  • Platform: pure string/Buffer.byteLength arithmetic, no platform-dependent behavior — the Windows/Linux "not tested" boxes are low risk here, and CI covers them.

Verdict

The core algorithm is correct, well-bounded, and genuinely idempotent — it held up to everything I threw at it. Nothing here blocks merge. I'd prioritize #2 (observability) and #5 (design-doc consistency) before merge since they're cheap, and treat #1, #3, and #6 as strong follow-ups.

yiliang114
yiliang114 previously approved these changes Aug 5, 2026

@yiliang114 yiliang114 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, no blockers. The 256KB bound is correctly enforced for the scoped paths and was independently fuzz-verified (byte accounting matches native JSON.stringify exactly; surrogate pairs never split; exact 65,536 boundaries hold for ASCII/3-byte/4-byte; idempotent; native-stringify verification collapses to marker if accounting drifts). Non-blocking P2: residual >256KB vectors remain outside this PR's scope — unbounded rawInput args (a write_file/edit_file with MB-scale content still emits an oversized start/permission frame), structured non-string rawOutput, non-canonical content blocks, and bulk-replay aggregate frames. Design doc defers these to #8091/#8447/#8448/#7306 — fine to land, but say 'canonical textual result fields only' in the description and confirm a tracking item for rawInput.

@doudouOUC

Copy link
Copy Markdown
Collaborator Author

@qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

Sandboxed verification: ✅ passed — merge-ready (agent verdict) - workflow run

Ran the PR in an isolated, token-free container: A/B against the base build, mock-free harness assertions, targeted gates. Advisory evidence for human reviewers — not a review, an approval, or a CI check.

Scripted assertions: 662 passed · 0 failed · 662 total

中文 — 判定:✅ 通过 · 可合入(agent 判定)

沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查

脚本断言:662 通过 · 0 失败 · 662 总计

Verification report

PR #8450 — fix(cli): Bound ACP textual tool-result payloads

VERDICT: merge-ready — 662/662 scripted assertions passed, 0 unexpected failures.
Verified head: 6c162103f8daf5bc70a724aaf0005c7c6f09e8d0 (git rev-parse HEAD^2; matches metadata headRefOid). A/B base: 4f79036a2269bb43f95f736ca8c44bc60b0cc9d6 (HEAD^1, the merge-ref base tip; the metadata's older baseRefOid be4f5e0963 predates the merge — see Methodology).

中文摘要
  • 结论: merge-ready。662 条脚本化断言全部通过,0 条意外失败。
  • A/B 结论(见 "Central claim" 表):在 base(HEAD^1)上,500 KB 文本工具结果经 bulk replay / paged replay 两条 seam 原样投递(content 500,065 / rawOutput 500,011 JSON 字节,整帧 1,000,467 字节);在 head 上同一场景被限制为 content 65,536 / rawOutput 65,482 字节,整帧 131,409 字节,与 PR 自报证据表(65,536 / 65,482 / 131,347)一致。良性(1 KiB)结果在两个 build 上序列化逐字节相同;CJK、emoji(代理对)、多 block、A2UI、结构化/富内容、非字符串 rawOutput 等 sibling 形状均按契约豁免或正确投影。
  • 测试非空验证:4 个变异(回退 live seam、回退 replay seam、预算翻倍、20/80 比例破坏)全部被对应测试以预期断言杀死(如 expected 500065 to be less than or equal to 65536),树恢复后 git status 干净。
  • 未覆盖:逐 commit 归因(shallow checkout,3 个 commit 中仅 1 个本地可达,验证的是聚合 diff);完整 fake-MCP 端到端复现(本报告的 seam 级 A/B 复现的是 wire 形状与字节数,不是 daemon 级触发路径);Headless 输出、A2UI 渲染、结构化/富 payload、backpressure、replay 聚合上限(均为 PR 声明的范围外);Windows/macOS(本容器为 Linux)。

Central claim + A/B

Central claim: eligible textual ACP tool-call updates (pure-text content arrays, primitive-string rawOutput) are independently bounded to ≤ 65,536 JSON UTF-8 bytes at the live (Session.sendUpdate) and replay (replayContext.sendUpdate) delivery boundaries, while canonical transcripts, offline export, A2UI, and structured/rich content stay untouched.

Both arms drive the compiled dist/ of the same scenario through collectHistoryReplayUpdates / replayTranscriptRecordPage (the real HistoryReplayer), i.e. through the exact replay collectors the PR claims to cover. Witness: evidence/01-ab-seam-base-vs-head.png (full 107-line run; raw log ab-harness-run1.log).

Cell (identical fixture both arms) Oracle base control head
S1 bulk replay, head- + 499,999×x + -tail content JSON UTF-8 bytes 500,065 (full source delivered verbatim) 65,536
same rawOutput JSON UTF-8 bytes 500,011 65,482
same full JSON-RPC session/update frame bytes 1,000,467 131,409 (< 256 KiB, parseable)
S2 bulk replay, 250,000× (750 KB UTF-8) content bytes 750,056 65,533
S3 bulk replay, 2×300 KB blocks + 600 KB rawOutput content bytes (block count) 600,111 65,536 (2 blocks, order preserved)
S4 bulk replay, benign 1 KiB result serialized update 2,792 b byte-identical (2,792 b)
S5 paged transcript route, 500 KB content bytes 500,064 65,536
S6 bulk replay, 120,000×😀 (astral) rawOutput bytes 480,012 65,481 (no lone surrogates, lossless JSON round-trip)

The base control's red cells are asserted as control expectations (they pass by being oversized). Cross-check with the PR's own evidence table: head values match exactly (65,536 / 65,482); base differs by ~100 bytes only because my fixture carries head-/-tail markers and a different JSON-RPC wrapper — same magnitude, same conclusion.

Secondary claims, all scripted (same harness, ab-harness.mjs):

  • Byte accounting is a wire oracle against native JSON: 500 fuzzed + 10 targeted strings (controls, quotes, backslashes, DEL, CJK, astral, lone surrogates, U+2028/2029) — jsonStringJsonByteLengthBuffer.byteLength(JSON.stringify(s)), 0 mismatches.
  • Boundary ladder: rawOutput and content at 65,535 / 65,536 / 65,537 / 200,000 JSON bytes — no-op with reference stability below the budget, projected at/above it, output ≤ 65,536.
  • Exemption sweep (oversized payloads pass by same reference): diff / terminal / image blocks, blocks with extra fields, mixed text+diff, non-array content, non-string text, A2UI via toolName (present_ui, present_choices), A2UI via serverId only, non-tool_call_update updates; and a non-A2UI meta (github/create_issue) is not exempt.
  • Type-boundary probes: rawOutput as number / boolean / null / array / object passes untouched while content is still projected; absent rawOutput stays absent.
  • Immutability / idempotence: original not mutated; project(projected) === projected; replay output fed back through the projector is a fixed point (the live-after-replay double boundary).
  • Single-block reuse: when content is one text block equal to rawOutput, both fields share the stricter content preview.
  • Scan caps (commit 3): 600×40 KB blocks 67 ms; 200×50 KB mixed 26 ms; 5 MB rawOutput 4.3 ms; 2 MB CJK 7 ms; 2,000-block and 1,192-empty-block collapse paths < 1 ms — all bounded, no superlinear rung.

Corrections

None — first verification round; no earlier review statements to correct.

Findings

No blocking findings. Every executed assertion passed; the four mutants below died on their intended assertions, and the benign-traffic cell is byte-identical across arms (zero collateral on small results).

Non-blocking observations (informational, not defects):

  1. Initial sessionUpdate: 'tool_call' frames are not projected (the guard matches tool_call_update only). This matches the stated design; a census of the emitters shows result text only ever leaves through tool_call_update (ToolCallEmitter.emitResultsendUpdate), so no live path bypasses the bound today.
  2. The payloadBudget < marker branch of truncateStringPayload (prefix-only, no marker) is unreachable from both call sites (allocations always reserve ≥ marker bytes) — dead defensive code, harmless.

Vacuity / mutation matrix (live rerun, evidence/02-mutation-matrix-all-killed.png, raw mutation-matrix.log)

Mutant Suite Result
M1 revert live seam (Session.sendUpdate passes update) Session.test.ts -t 'bounds textual tool results at the live ACP delivery boundary' killed: expected 500065 to be less than or equal to 65536
M2 budget 65,536 → 131,072 projection test file (31 tests) killed: 16 failed, incl. expected true to be false at the 65,537 reference-stability cell
M3 head ratio 0.2 → 0.5 -t 'keeps an approximately 20/80 head and tail preview' killed: expected 32747 to be greater than 127709.4
M4 revert replay seam (projectedUpdate = update) history-replay-page.test.ts bulk-replay test killed: expected 500055 to be less than or equal to 65536

Post-run git status --porcelain packages/cli/src empty (restoration verified). No survivor rows: every guard the PR introduces is pinned by a test that fails on the intended assertion when the guard is removed.

Targeted gates (head)

evidence/03-targeted-gates-head.png, raw gates.log:

  • acp-tool-result-text-projection.test.ts — 31/31
  • history-replay-page.test.ts — 10/10
  • ui/utils/export/collect.test.ts — 5/5 (incl. the new lossless-export test: exported text length and SHA-256 equal the 500 KB source)
  • Session.test.ts — 516/516

Plus scripts/check-serve-fast-path-bundle.js at HEAD after a fresh DEV=true bundle: "Startup bundle closure checks passed" (exit 0) — the new @qwen-code/acp-bridge/bridgeClient import (via the projector inside Session.ts) did not leak the ACP runtime into the serve fast-path static closure (raw fastpath-check.log, bundle.log). Typecheck is covered by the CI npm run build at HEAD and by the base worktree's clean tsc --build (0 errors, base-build.log).

Not covered

  • Per-commit attribution: shallow checkout (depth 2) — only 1 of the 3 commits in the metadata is locally reachable (git rev-list HEAD^1..HEAD^2 = 6c162103f only; --is-shallow-repository = true). The aggregate HEAD^1..HEAD diff is what was verified; the three commits' individual claims (initial projection / hardening / scan caps) were not exercised separately.
  • Full fake-MCP end-to-end from the PR's evidence table: my A/B drives the real replay collectors and the real projector from compiled dist, reproducing the wire shape and byte counts, not the daemon-level trigger path (model finalization → duplicate amplification). The seam-level numbers match the PR's table, so the divergence risk is low but nonzero.
  • Headless output, A2UI rendering, structured/rich payloads, generic NDJSON caps, backpressure, replay aggregate limits, diagnostics, artifact lifecycle — declared unchanged by the PR; not exercised.
  • Windows/macOS — container is Linux.
  • Repo-wide lint/format and full test suite — covered by the PR's own CI; I ran the targeted gates above. The fast-path closure check was run at HEAD only (its forbidden-closure invariant is the regression guard; running it at base would require a second full bundle).
  • Trial merge into latest main — the merge-ref base 4f79036a2 is the workflow's current base tip (same-day main), so staleness is not a concern for this round.

Methodology

Environment: node:22-bookworm CI container, refs/pull/8450/merge at depth 2 (HEAD = e88382547, base tip HEAD^1 = 4f79036a2, PR head HEAD^2 = 6c162103f); npm ci + npm run build pre-ran at HEAD. Base control: git worktree add tmp/base-tree HEAD^1, full tsc --build of the base reference graph inside the worktree (exit 0). Because worktrees carry no node_modules, the base tree got (a) shadow node_modules/@qwen-code/* symlinks into its own packages — readlink -f asserted qwen-code-coretmp/base-tree/packages/core, qwen-codetmp/base-tree/packages/cli — and (b) symlinks of the head tree's nested per-package node_modules (external deps only; the PR touches no package.json/lockfile, so versions are lockfile-identical). Control validity is asserted in-harness (D1–D5): base dist contains no projection call, projector module absent on base, present on head. The harness (ab-harness.mjs, kept in this artifact dir) imports the compiled dist/src/... of each arm via file:// URLs and drives the real HistoryReplayer-backed collectors with synthetic ChatRecords shaped exactly like the repo's own fixtures; native JSON.stringify byte length is the wire oracle. Mutation checks edited head sources, ran the pinned test file, and restored from backup (restoration verified by clean git status). Raw logs (ab-harness-run1.log, gates.log, mutation-matrix.log + mutation-matrix-run1.log, base-build.log, bundle.log, fastpath-check.log) and harness scripts live in this directory; evidence PNGs were produced with scripts/verify-capture.mjs.

Evidence images

01-ab-seam-base-vs-head

02-mutation-matrix-all-killed

03-targeted-gates-head

Harness scripts and raw logs are in the workflow run artifacts (7-day retention).

Qwen Code · sandboxed verification

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, looks ready to ship. ✅

@doudouOUC
doudouOUC enabled auto-merge August 5, 2026 08:35
@doudouOUC

Copy link
Copy Markdown
Collaborator Author

@qwen-code /takeover stop

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤖 Addressed the latest review feedback (round 1/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 1/100 轮)。改动内容与我反驳保留之处如下:

Autofix review response — PR #8450 (round: feedback newer than 2026-08-03T22:44:01Z)

Commit: 4333cd42afix(cli): Surface ACP tool-result projection in debug logs (7 files, +278/−68). No base-conflict resolution was needed (--conflict false; origin/main was not merged).

Findings and dispositions

#1 — Multi-block degradation cliff (design question): ESCALATED to maintainer, no code change this round.
This is a genuine tradeoff, not a defect, and the reviewer framed it as a design question. Options: (a) keep the current max-min reduce-all behavior — every block keeps a marker-sized preview, locked in by the existing tests; (b) the proposed keep-a-prefix-of-blocks-whole fallback — strictly more informative for the 600→collapse cliff and simpler (removes the minimum-marker-set special cases), but drops all content from blocks after the budget is exhausted. Recommendation: track (b) as a follow-up change with its own test updates; the reviewer's own data shows real results emit 1–3 blocks, so the current pinned behavior is defensive-only in practice.

#2 — Lossy truncation is completely silent: ADDRESSED (pre-merge priority).
The projector now uses createDebugLogger('ACP_TOOL_RESULT_PROJECTION'). Every projection emits a debug record with { toolCallId, fields, budgetBytes, originalContentBytes?, originalRawOutputBytes? }. Exact original sizes are measured only when a debug-log session is active (isEnabled()), so the default send path pays zero extra scanning cost. Covered by new tests (logged payload contents, silence when disabled). The suggested _meta marker (qwen.acp.projected) for UI chrome is a wire-metadata decision — recorded here as a candidate follow-up, not implemented.

#3 — Marker discards artifact paths: DEFERRED to a follow-up, with evidence.
The fixed marker is load-bearing: its exact payload size is reserved per reduced block in the multi-block budget math, and a dynamic artifact-bearing marker would change that accounting and the design contract ("fixed transport-truncation marker"). Additionally, _meta.artifacts entries are untyped unknown[] at this transport boundary, so naming paths in the marker requires an artifact-shape contract the projector does not own. Agreed it is worth doing as a designed follow-up (consistent with phase 1's header format), as the reviewer prioritized.

#4 — Hardcoded budget: SPLIT.
(a) Env/settings kill switch — DECLINED for this PR: it adds new user-facing configuration surface (setting name, 0/Infinity semantics, schema, tests) beyond this PR's fixed transport budget, which exists specifically to stay under the 256 KiB large-pipe threshold; disabling it reintroduces exactly the frames this PR bounds. If maintainers want an escape hatch, recommend a settings-driven follow-up consistent with phase 1's getToolOutputBatchBudget(). (b) Test hardcoding 256 * 1024 — FIXED: the frame-size test now imports and asserts against LARGE_PIPE_FRAME_THRESHOLD_BYTES from large-pipe-frame-observer.ts.

#5 — Design doc internally inconsistent: ADDRESSED (pre-merge priority).
docs/design/final-tool-response-budget.md: the Problem paragraph now states that ACP display events additionally pass through the transport-boundary projection described in the ACP Tool-Result Text Projection design, and Invariant 7 is amended: the transport projection may shorten eligible ACP display fields at the wire boundary but never modifies model-facing parts.

#6copyString ~37× slower than necessary: ADDRESSED.
Replaced value.split('').join('') with the reviewer-measured Buffer.from(value, 'utf16le').toString('utf16le') round trip — code-unit exact (lone surrogates included), forces a flat copy — with a one-line comment explaining why the round trip exists.

#7 — Multi-block projection is O(blocks × budget): ADDRESSED via the comment option.
Added the reviewer-offered comment documenting the worst-case bound (each oversized block rescanned through its maximum allocation; realistic frames carry 1–3 blocks; every scan early-stops at the budget). No algorithm change: capping the per-block rescan below maximumAllocation would misclassify blocks that fit whole as truncated, changing projection behavior.

#8 — Eligibility silently all-or-nothing: ADDRESSED.
Added a warn-level debug record when an oversized content array is shaped entirely like text blocks (type: 'content' / content.type: 'text' / string text) yet fails strict eligibility — i.e., the exact "a future change adds annotations" regression vector — reporting { toolCallId, budgetBytes, textBytes }. Structure is verified before any byte measurement, so structured blocks (diff/terminal/media) and mixed arrays stay silent by design; the scan itself early-stops at the budget. Covered by three new tests, including a mixed-array regression case.

#9tool_call start frames bypass the projector: ADDRESSED (doc).
The design doc's non-goals now explicitly name tool-argument rawInput on tool_call start frames as the known remaining producer of frames above the large-pipe threshold, noting start frames carry empty content so result payloads cannot leak there.

#10 — Minor / test nits: ALL FOUR ADDRESSED.

  • a2uiMeta() removed; meta is passed through a cast directly to isA2uiToolMeta, which does its own typeof checks (~14 lines dropped).
  • The replay half of Session.test.ts's "bounds textual tool results at the live ACP delivery boundary" was removed; that coverage already lives in history-replay-page.test.ts (bulk replay bounded + idempotent, plus the paged-replay case), and the test now focuses on the live boundary. Unused imports removed.
  • Two of the three exact toBe(65_536) saturation assertions relaxed to toBeLessThanOrEqual; the one exact case is kept in "saturates the budget after smaller blocks reach their capacity".
  • The seeded fuzz generator was extended to the projection itself: 100 randomized 1–8-block updates (with/without rawOutput, including lone surrogates) asserting both fields stay ≤ budget and project(project(u)) === project(u).

Also updated: the new design doc's Projection section now records debug logging as the projector's sole side effect, and the createDebugLogger mocks in Session/acpAgent tests now implement the full DebugLogger interface (isEnabled).

Conflict notes

None. --conflict false; no merge performed.

Verification

Commands actually run (final state, commit 4333cd42a; the runner checkout had no built dist/ initially, which broke acpAgent test collection on @qwen-code/channel-base — resolved by the build, unrelated to the change):

  • npm run build — passed (all packages)
  • npm run typecheck — passed (0 TS errors across all packages)
  • npm run lint — passed (clean)
  • npx prettier --check on the 7 changed files — passed
  • npx vitest run src/acp-integration/session/acp-tool-result-text-projection.test.ts (packages/cli) — 37 passed
  • npx vitest run src/acp-integration/session/Session.test.ts (packages/cli) — 485 passed
  • npx vitest run src/acp-integration/acpAgent.test.ts src/acp-integration/acpAgent.worktree.test.ts (packages/cli) — 361 passed
  • npx vitest run src/acp-integration src/ui/utils/export/collect.test.ts (packages/cli, final full-area run) — 28 files, 1231 passed
  • Not run: npm run generate:settings-schema (no settings source changed); integration tests after npm run bundle (the changed behavior is transport-side unit coverage; no bundled-CLI-only path touched)
中文说明

Autofix 评审响应 — PR #8450(本轮:2026-08-03T22:44:01Z 之后的新反馈)

提交:4333cd42afix(cli): Surface ACP tool-result projection in debug logs(7 个文件,+278/−68)。无需解决基线冲突(--conflict false,未合并 origin/main)。

各条反馈的处理

#1 — 多块退化存在悬崖(设计问题):升级给维护者决定,本轮不改代码。
这是真实的取舍而非缺陷,评审者也将其定性为设计问题。选项:(a) 保留当前 max-min 全块缩减行为——每个块都保留标记大小的预览,且现有测试已锁定该行为;(b) 评审者提议的"整块保留前缀"回退——对 600 块→整体折叠的悬崖明显更有用且更简单(可移除最小标记集的特判),但预算耗尽后其余块的内容会全部丢失。建议:作为后续变更单独跟踪 (b) 并配套更新测试;评审者自己的数据表明真实结果只产生 1–3 个块,因此当前被锁定的行为在实践中只是防御性的。

#2 — 有损截断完全静默:已处理(合并前优先项)。
投影器现在使用 createDebugLogger('ACP_TOOL_RESULT_PROJECTION')。每次投影都会输出一条 debug 记录,包含 { toolCallId, fields, budgetBytes, originalContentBytes?, originalRawOutputBytes? }。精确的原始大小仅在调试日志会话激活时(isEnabled())才测量,因此默认发送路径零额外扫描开销。新增测试覆盖(记录内容、禁用时静默)。建议中的 _meta 标记(qwen.acp.projected,供 UI 渲染截断提示)属于传输层元数据决策——在此记录为候选后续项,本轮不实现。

#3 — 标记丢弃了 artifact 路径:暂缓至后续 PR,附证据。
固定标记是承重设计:其精确 payload 大小在多块预算计算中被每个缩减块预留,动态的、携带 artifact 的标记会改变该计算和设计契约("固定传输截断标记")。此外,_meta.artifacts 条目在该传输边界上是无类型的 unknown[],要在标记中写出路径需要投影器并不拥有的 artifact 形状契约。同意按评审者的优先级,作为与 phase 1 头部格式一致的后续设计项推进。

#4 — 硬编码预算:拆分处理。
(a) 环境变量/配置开关——本 PR 拒绝:这会增加新的用户可见配置面(配置名、0/Infinity 语义、schema、测试),超出本 PR 固定传输预算的范围;该预算的存在就是为了保持在 256 KiB 大管道阈值之下,禁用它会重新引入本 PR 正要约束的超大帧。若维护者确需逃生通道,建议后续按 phase 1 的 getToolOutputBatchBudget() 风格走 settings 配置。(b) 测试硬编码 256 * 1024——已修复:帧大小测试现在从 large-pipe-frame-observer.ts 导入并使用 LARGE_PIPE_FRAME_THRESHOLD_BYTES

#5 — 设计文档内部不一致:已处理(合并前优先项)。
docs/design/final-tool-response-budget.md:Problem 段落现说明 ACP 展示事件会额外经过 ACP Tool-Result Text Projection 设计所描述的传输边界投影;不变量 7 修订为:传输投影可以在传输边界缩短符合条件的 ACP 展示字段,但从不修改面向模型的部分。

#6copyString 比必要慢约 37 倍:已处理。
按评审者的测量数据,将 value.split('').join('') 替换为 Buffer.from(value, 'utf16le').toString('utf16le') 往返——码元级精确(含孤立代理项)、强制生成扁平副本——并附一行注释说明该往返存在的理由。

#7 — 多块投影是 O(blocks × budget):按评审者给出的注释选项处理。
补充了注释说明最坏情况上界(每个超大块至多重扫到其最大分配;真实帧只有 1–3 个块;所有扫描都在预算处提前停止)。不改算法:把每块重扫上限压到 maximumAllocation 以下会把本可完整保留的块误判为需截断,属于行为变更。

#8 — 资格判定静默地全有或全无:已处理。
新增 warn 级调试记录:当超大 content 数组整体呈文本块形状(type: 'content' / content.type: 'text' / 字符串 text)却未通过严格资格检查时触发——即"未来某个改动加了 annotations"这一确切的回归向量——输出 { toolCallId, budgetBytes, textBytes }。结构校验先于任何字节测量,因此结构化块(diff/terminal/media)和混合数组按设计保持静默;扫描本身在预算处提前停止。新增 3 个测试覆盖,含混合数组回归用例。

#9tool_call 起始帧绕过投影器:已处理(文档)。
设计文档的非目标部分现在明确点名:tool_call 起始帧上的工具参数 rawInput 是已知仍可能产生超过大管道阈值帧的来源,并说明起始帧的 content 为空、结果载荷不会从那里泄漏。

#10 — 次要/测试建议:四条全部处理。

  • 移除 a2uiMeta()meta 经类型断言直接传给 isA2uiToolMeta(其内部已有 typeof 检查),减少约 14 行。
  • 删除 Session.test.ts 中 "bounds textual tool results at the live ACP delivery boundary" 测试的 replay 部分;该覆盖已存在于 history-replay-page.test.ts(批量 replay 受限 + 幂等,以及分页 replay 用例),该测试现聚焦实时边界。一并移除不再使用的导入。
  • 三处精确的 toBe(65_536) 饱和断言中的两处放宽为 toBeLessThanOrEqual;在 "saturates the budget after smaller blocks reach their capacity" 中保留唯一一处精确断言。
  • 将固定种子模糊生成器扩展到投影本身:100 个随机的 1–8 块更新(含/不含 rawOutput,包含孤立代理项),断言两个字段均 ≤ 预算且 project(project(u)) === project(u)

另外:新设计文档的 Projection 一节记录了 debug 日志是投影器唯一的副作用;Session/acpAgent 测试中的 createDebugLogger mock 已补齐完整 DebugLogger 接口(isEnabled)。

冲突说明

无。--conflict false,未执行合并。

验证

实际执行的命令(最终状态,提交 4333cd42a;runner 检出初始没有构建产物 dist/,导致 acpAgent 测试收集时 @qwen-code/channel-base 无法解析——已由构建解决,与本变更无关):

  • npm run build — 通过(全部包)
  • npm run typecheck — 通过(所有包 0 个 TS 错误)
  • npm run lint — 通过(无告警)
  • npx prettier --check(7 个变更文件)— 通过
  • npx vitest run src/acp-integration/session/acp-tool-result-text-projection.test.ts(packages/cli)— 37 通过
  • npx vitest run src/acp-integration/session/Session.test.ts(packages/cli)— 485 通过
  • npx vitest run src/acp-integration/acpAgent.test.ts src/acp-integration/acpAgent.worktree.test.ts(packages/cli)— 361 通过
  • npx vitest run src/acp-integration src/ui/utils/export/collect.test.ts(packages/cli,最终全量区域运行)— 28 个文件,1231 通过
  • 未执行:npm run generate:settings-schema(未改动任何 settings 源);npm run bundle 后的集成测试(变更行为属传输侧单元测试覆盖,未触及仅捆绑 CLI 路径)

Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。

Re-review when you have a moment. After round 100 this bot stops and leaves the PR for a human. · 有空请复审;第 100 轮后本 bot 停止并将 PR 交给人工。


🧠 Handled by Qwen Code · model/模型 qwen3.8-max

@doudouOUC

Copy link
Copy Markdown
Collaborator Author

Rebased onto the latest origin/main (a5c637b749) and resolved the base conflicts.

Item Resolution
Session test imports Preserved the new screen/voice coverage from main and the ACP projection replay coverage from this PR.
Replay test fixtures Kept main's canonical tool-call/result fixtures and isolated the oversized ACP fixture under a separate helper.
AutoFix diagnostics commit Excluded from the rebased series because privacy-safe diagnostics remain intentionally tracked as separate work in #8448; the ACP projection PR remains scoped to transport projection.

Validation on 806cb6e426: 579 focused tests passed; target Prettier and ESLint passed; root build, typecheck, and bundle passed. Two consecutive open-ended audits found no remaining actionable issue. All 8 review threads remain resolved.

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed. Suggestions are inline. Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.

中文说明

已审查。 建议见行内评论。 未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。

— qwen3.8-max via Qwen Code /review (v0.21.5)

@doudouOUC
doudouOUC requested a review from yiliang114 August 5, 2026 11:30
yiliang114
yiliang114 previously approved these changes Aug 5, 2026

@yiliang114 yiliang114 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed at new head (806cb6e). The multi-block scan cap is a correct perf optimization: early-exit fit detection preserves exact-fit-untouched, oversize blocks return max+1 to signal truncation, base/marker accounting keeps the skeleton within budget, and the final native-JSON.stringify verification still guarantees the 256KB bound. Prior P2 unchanged: rawInput/structured-rawOutput/bulk-replay vectors remain out of scope (deferred to #8091/#8447/#8448/#7306) — say 'canonical textual fields only' in the description.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
@doudouOUC

Copy link
Copy Markdown
Collaborator Author

@qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

Sandboxed verification: ✅ passed — merge-ready (agent verdict) - workflow run

Ran the PR in an isolated, token-free container: A/B against the base build, mock-free harness assertions, targeted gates. Advisory evidence for human reviewers — not a review, an approval, or a CI check.

Scripted assertions: 694 passed · 0 failed · 694 total

中文 — 判定:✅ 通过 · 可合入(agent 判定)

沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查

脚本断言:694 通过 · 0 失败 · 694 总计

Verification report

PR #8450 — fix(cli): Bound ACP textual tool-result payloads

VERDICT: merge-ready — 694/694 scripted assertions passed, 0 unexpected failures.
Verified head: 806cb6e4266b0c43e8eac7db80c339518e7aa3d2 (git rev-parse HEAD^2; matches metadata headRefOid). A/B base: a5c637b749e0c5b5c85714cd1c717e5a6a33be13 (HEAD^1, the merge-ref base tip). Follow-up round — previous round (workflow run 30988221914) verified head 6c162103f against base 4f79036a2 and reported merge-ready (662/662). The PR was pushed again (rebased three commits onto the newer base; all commit OIDs changed), so every measurement below was rebuilt and re-run at the new head — nothing carried forward by shortcut, since the old head object is not present in this depth-2 checkout and the input closure (base tip, lockfile-resolved deps) moved.

Previous-finding status (round 1 → this round)

# Finding (round 1) Severity Status at head 806cb6e
1 Initial sessionUpdate: 'tool_call' frames are not projected (guard matches tool_call_update only); emitter census showed result text only ever leaves through tool_call_update Informational (matches design) Stands — re-walked at the new head: createTranscriptToolCallStartUpdate emits content: [] (transcript-replay.ts:264-268), results leave only via createTranscriptToolCallResultUpdatesessionUpdate: 'tool_call_update' (transcript-replay.ts:284-311). No live path bypasses the bound.
2 payloadBudget < marker branch of truncateStringPayload (prefix-only, no marker) unreachable from both call sites — dead defensive code Informational Stands — both call sites still reserve ≥ marker bytes: projectRawOutput budget = 65,534, and projectContent allocations ≥ min(payloadBytes, marker) with truncation only invoked when payloadBytes > allocationallocation ≥ marker (41 payload bytes). Harmless.
Round-1 verdict merge-ready, 662/662 Re-measured at the new head: merge-ready, 694/694 (breakdown in Methodology). Byte counts of shape-matched A/B cells are identical to round 1 (see "Central claim"), evidence that the rebase changed no projection behavior.
中文摘要
  • 结论: merge-ready。694 条脚本化断言全部通过,0 条意外失败。本轮为跟进轮:PR 在上一轮(head 6c162103f)之后被 rebase 到新 base(a5c637b),3 个 commit OID 全部变化,因此所有测量在新 head 上重建重跑,未沿用旧数据。
  • 上轮发现状态(见 "Previous-finding status" 表):两条 informational 发现(tool_call 初始帧不投影符合设计;truncateStringPayload 的 marker 以下分支为死代码)在新 head 上复核后均 维持(stands)
  • A/B 结论(见 "Central claim" 表,证据图 01-ab-seam-base-vs-head.png):base 上 500 KB 文本结果经 bulk replay / paged replay 两条 seam 原样投递(content 500,065 / rawOutput 500,011 JSON 字节,整帧约 1 MB);head 上同一场景被限制为 content 65,536 / rawOutput 65,482 字节,整帧 131,435 字节、可解析、< 256 KiB。与上一轮及 PR 自报证据表中形状一致的 cell 逐字节相同(65,536 / 65,482;CJK cell 65,533)。良性 1 KiB 结果在两 build 上序列化逐字节一致;CJK、emoji(代理对)、多 block、A2UI、结构化/富内容、非字符串 rawOutput 等 sibling 形状均按契约豁免或正确投影;多 block 扫描有界(最大 rung 148 ms,线性比 0.99)。
  • 测试非空验证(见 "Vacuity" 表,证据图 02-mutation-matrix-all-killed.png):4 个变异(回退 live seam、预算翻倍、20/80 比例破坏、回退 replay seam)全部被对应测试以预期断言杀死(如 expected 500065 to be less than or equal to 65536),变异前正控为绿,树恢复后 git status 干净。
  • 门禁(见 "Targeted gates",证据图 03-targeted-gates-head.png04-fastpath-closure-check.png):4 个测试文件 579/579 绿;serve fast-path bundle 闭包检查通过,且经 planted-violation 证明门禁为活(植入 bridgeClient 后正确报 exit 1)。
  • 未覆盖:逐 commit 归因(shallow checkout,3 个 commit 仅 head 本地可达);完整 fake-MCP daemon 级端到端(本轮 A/B 复现的是 seam 级 wire 形状与字节数);Headless 输出、A2UI 渲染、结构化/富 payload、通用 NDJSON 上限、backpressure、replay 聚合上限(PR 声明范围外);Windows/macOS(本容器为 Linux);repo 级 lint/format 与全量测试(由 PR 自身 CI 覆盖)。

Central claim + A/B

Central claim: eligible textual ACP tool-call updates (pure-text content arrays, primitive-string rawOutput) are independently bounded to ≤ 65,536 JSON UTF-8 bytes at the live (Session.sendUpdate) and replay (replayContext.sendUpdate) delivery boundaries, while canonical transcripts, offline export, A2UI, and structured/rich content stay untouched.

Both arms drive the compiled dist/ of the identical scenario through collectHistoryReplayUpdates / replayTranscriptRecordPage (the real HistoryReplayer); base arm: tmp/base-tree worktree at HEAD^1 rebuilt with tsc --build (exit 0, base-build.log). In-harness control validity: base dist has no projection call (D1), head does (D2), projector module absent on base / present on head (D3), and readlink -f of @qwen-code/qwen-code-core resolves into the base tree from the base arm and into the head tree from the head arm (D4/D5). Witness: evidence/01-ab-seam-base-vs-head.png (full 142-line run; raw ab-harness-run1.log).

Cell (identical fixture both arms) Oracle base control head
S1 bulk replay, head- + 499,999×x + -tail content JSON UTF-8 bytes 500,065 (verbatim) 65,536
same rawOutput JSON UTF-8 bytes 500,011 65,482
same full JSON-RPC session/update frame bytes 1,000,493 131,435 (< 256 KiB, parseable)
S2 bulk replay, 250,000× (750 KB UTF-8) content bytes 750,056 65,533
S3 bulk replay, 2×~300 KB blocks + 600,001-char rawOutput content bytes (block count) 600,147 (2) 65,536 (2, order preserved)
S4 bulk replay, benign 1 KiB result serialized update 2,388 b byte-identical (2,388 b)
S5 paged transcript route, 500,000×p content bytes 500,056 65,536
S6 bulk replay, 120,000×😀 (astral, rawOutput-only) rawOutput bytes 480,002 65,531 (no lone surrogates, lossless JSON round-trip)
S7 PR's own bare fixture, 499,999×x both fields content / rawOutput bytes 65,536 / 65,482 (= PR evidence table, = round 1)

The base control's red cells are asserted as control expectations (they pass by being oversized). Delta-stability vs round 1: shape-matched cells are byte-identical to round 1's measurements (S1 500,065→65,536 / 500,011→65,482; S2 750,056→65,533; S5 head 65,536; S7 = PR's own table). Cells whose fixture shape differs slightly from round 1's (S3, S4, S5's base cell, S6) differ accordingly but preserve the same verdict shape. No projection number moved — strong evidence the rebase changed no behavior.

Secondary claims, all scripted (ab-harness.mjs, 107 checks):

  • P1 byte accounting is a wire oracle against native JSON: 500 fixed-seed fuzzed + 11 targeted strings (controls, quotes, backslashes, DEL, CJK, astral, lone surrogates, U+2028/2029) — jsonStringJsonByteLengthBuffer.byteLength(JSON.stringify(s)), 0 mismatches.
  • P2 boundary ladder: rawOutput and content at 65,535 / 65,536 / 65,537 / 200,000 JSON bytes — no-op with reference stability at/below the budget, projected above it, output ≤ 65,536 (fixture calibrated to exact byte targets).
  • P3 exemption sweep (oversized payloads pass by same reference): diff / terminal / image blocks, blocks with extra fields, mixed text+diff, non-array content, non-string text, A2UI via toolName (mcp__ui__present_ui, mcp__ui__present_choices), A2UI via serverId only (dq-A2UI), non-tool_call_update updates; and a non-A2UI meta (github/create_issue) is not exempt.
  • P4 type-boundary probes: rawOutput as number / boolean / null / array / object passes untouched (same reference) while content is still projected; absent rawOutput stays absent.
  • P5 immutability / idempotence: original not mutated; project(projected) === projected; replay output fed back through the projector is a fixed point (the live-after-replay double boundary).
  • P6 single-block reuse: when content is one text block equal to rawOutput, both fields share the stricter content preview.
  • P7 20/80 preview: marker present, head/tail anchors intact, tail = 3.9–4.1× head.
  • P8 scan caps (the perf commit): 600×40 KB blocks 77.8 ms; 200×50 KB mixed + 500 KB rawOutput 147.9 ms; 5 MB rawOutput 2.1 ms; 2 MB CJK 1.1 ms; 2,000-block collapse 0.2 ms; 1,192-empty-block collapse 0.1 ms — every rung < 500 ms on a loaded shared runner; linearity probe 2.5 MB vs 5 MB ratio 0.99 (early-stop scan confirmed, no superlinear rung).
  • P9 allocation edge shapes: small blocks stay complete while the budget saturates at exactly 65,536; both collapse paths (empty structure / minimum marker set) produce one canonical marker block.

Corrections

None — no inaccurate statements from earlier review rounds or bots required correction this round. (No instruction-injection attempts were detected in the PR title/body/commits.)

Findings

No blocking findings. Every executed assertion passed; the four mutants died on their intended assertions; benign traffic is byte-identical across arms (zero collateral).

The two round-1 informational observations stand unchanged at the new head (see status table): the unprojected initial tool_call frames are by-design (and carry no result text — census re-verified), and the payloadBudget < marker prefix-only branch remains unreachable dead defensive code. Neither is actionable for this PR.

Vacuity / mutation matrix (evidence/02-mutation-matrix-all-killed.png, raw mutant-M1.logmutant-M4.log)

Positive control first: the projection and replay suites ran green on unmutated source (poscontrol-green.log: 2 files passed, 31 + 14 tests).

Mutant Suite Result
M1 revert live seam (Session.sendUpdate passes update) Session.test.ts -t 'bounds textual tool results at the live ACP delivery boundary' killed: expected 500065 to be less than or equal to 65536 (1 failed | 960 skipped)
M2 budget 65,536 → 131,072 projection test file (31 tests) killed: expected true to be false at the 65,537 reference-stability cell (16 failed | 447 passed)
M3 head ratio 0.2 → 0.5 -t 'keeps an approximately 20/80 head and tail preview' killed: expected 32747 to be greater than 127709.4 (1 failed | 462 skipped)
M4 revert replay seam (projectedUpdate = update) history-replay-page.test.ts killed: expected 500055 to be less than or equal to 65536 (2 failed | 444 passed — bulk and paged tests)

Each kill was required to fail on the intended assertion text, not merely exit non-zero (the runner checks both). Post-run git status --porcelain packages/cli/src empty (restoration verified). No survivors: every guard the PR introduces is pinned by a test that fails on the intended assertion when the guard is removed.

Targeted gates (head)

evidence/03-targeted-gates-head.png, raw gates-text.log:

  • acp-tool-result-text-projection.test.ts — 31/31 (unchanged from round 1)
  • history-replay-page.test.ts — 14/14 (round 1: 10 — the suite grew on the moved base; both PR-added tests included)
  • ui/utils/export/collect.test.ts — 5/5 (incl. the lossless-export test: exported text length and SHA-256 equal the ~500 KB source)
  • Session.test.ts — 529/529 (round 1: 516 — moved base + the PR's live-boundary test)

Plus scripts/check-serve-fast-path-bundle.js after a fresh DEV=true npm run bundle at HEAD (bundle.log): "Startup bundle closure checks passed" (exit 0) — the projector's @qwen-code/acp-bridge/bridgeClient import did not leak the ACP runtime into the serve fast-path static closure. Gate liveness proven (fastpath-planted.log): planting packages/acp-bridge/src/bridgeClient.ts into the fast-path chunk's metafile inputs made the check exit 1 naming ACP bridge client runtime; the real metafile was restored byte-identical afterwards (cmp clean). Evidence evidence/04-fastpath-closure-check.png. Typecheck is covered by the pre-ran CI npm run build at HEAD and the base worktree's clean tsc --build (0 errors, base-build.log).

Not covered

  • Per-commit attribution: shallow checkout (depth 2) — git rev-list HEAD^1..HEAD^2 yields only 806cb6e while the metadata lists 3 commits (ff0c1a9, 81ea3d9, 806cb6e); the repository is shallow, so assume unreachable. The aggregate HEAD^1..HEAD diff is what was verified; the three commits' individual claims (initial projection / hardening / scan caps) were not exercised separately. The scan-cap claim was nonetheless probed directly via the P8 ladder.
  • Full fake-MCP end-to-end from the PR's evidence table: the A/B drives the real replay collectors and the real projector from compiled dist — it reproduces the wire shape and byte counts, not the daemon-level trigger path (model finalization → duplicate amplification). Seam-level numbers match the PR's table exactly (S7), so divergence risk is low but nonzero.
  • Live seam driven through a real daemon/socket — covered instead by the PR's own Session.test.ts live-boundary test (gate) plus the M1 mutation; not exercised over a live ACP connection.
  • Headless output, A2UI rendering, structured/rich payloads, generic NDJSON caps, backpressure, replay aggregate limits, diagnostics, artifact lifecycle — declared unchanged by the PR; not exercised.
  • Windows/macOS — container is Linux.
  • Repo-wide lint/format and full test suite — covered by the PR's own CI; targeted gates above. Fast-path closure check ran at HEAD only (its forbidden-closure invariant is the regression guard; a base-side bundle would cost a second full bundle).
  • Trial merge into latest main — no network/token in this container; the merge-ref base a5c637b is the workflow's current base tip and the PR is already rebased onto it, so staleness is not a concern for this round.

Methodology

Environment: node:22-bookworm CI container, node v22.23.2, refs/pull/8450/merge at depth 2 (HEAD = 8628c5f, base tip HEAD^1 = a5c637b, PR head HEAD^2 = 806cb6e); npm ci + npm run build pre-ran at HEAD. Base control: git worktree add tmp/base-tree HEAD^1, then the worktree was wired to the already-installed dependencies: root node_modules entries symlinked from the head tree except the @qwen-code scope, which was re-linked to the base tree's own packages (asserted in-harness, D4/D5: qwen-code-core realpath = tmp/base-tree/packages/core from the base arm, packages/core from the head arm), plus per-package nested node_modules symlinked from head (external deps only — no nested @qwen-code entries exist, and the PR touches no package.json/lockfile, so versions are lockfile-identical). Base built with the repo's own scripts/build_package.js (tsc --build) for core, acp-bridge, cli — exit 0 (base-build.log). The harness (ab-harness.mjs) imports each arm's compiled dist/src/... via file:// URLs and drives the real HistoryReplayer-backed collectors with synthetic ChatRecords shaped exactly like the repo's own test fixtures; native JSON.stringify byte length is the wire oracle. Mutation checks (mutation-matrix.sh) edited head sources, ran the pinned vitest files, and restored via git checkout (clean git status verified). Raw logs (ab-harness-run1.log, ab-harness-run2-capture.log, base-build.log, bundle.log, gates.log, gates-text.log, mutant-M1..M4.log, poscontrol-green.log, fastpath-check.log, fastpath-check-text.log, fastpath-planted.log, esbuild-metafile-backup.json) and harness scripts live in this directory; evidence PNGs were produced with scripts/verify-capture.mjs. Assertion accounting: 107 A/B-harness checks + 579 targeted-gate vitest tests (31+14+5+529) + 6 mutation-matrix checks (2 positive controls + 4 kills) + 2 fast-path closure checks (planted-red + real-green) = 694, all executed, all passed; fail counts only unexpected outcomes and there were none.

Evidence images

01-ab-seam-base-vs-head

02-mutation-matrix-all-killed

03-targeted-gates-head

04-fastpath-closure-check

Harness scripts and raw logs are in the workflow run artifacts (7-day retention).

Qwen Code · sandboxed verification

@doudouOUC

doudouOUC commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed the latest review feedback in 2f7daf6.\n\n| Item | Action |\n| --- | --- |\n| Inner text-content metadata exemption | Added a regression proving extra inner fields keep the update untouched |\n| Maximum canonical block count | Added the paired 1,191-block fit boundary |\n| Legacy A2UI toolName-only detection | Added a projector-level exemption regression |\n| PR scope wording | Clarified that the bound applies to canonical textual fields only |\n\nValidation: projector tests 34/34, Prettier, ESLint, root build, and typecheck all passed. Two consecutive final diff audits were clean.\n\nRejected/deferred: none. Decisions needed: none.

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Triage re-run completed without a new review.

⚠️ The bot has neither a verdict nor a deferral on 2f7daf6f5c2957693cc4b4cba2291119cd6eba1f — no APPROVED, CHANGES_REQUESTED, or COMMENTED review of its own. A DISMISSED one does not count: dismiss_stale_reviews voids the bot's approval on every push, which is exactly when a fresh one is needed. If this re-run was meant to review or approve, it did not, and an approval left by another account is a separate vote that does not count as the bot's own.

⚠️ 机器人在 2f7daf6f5c2957693cc4b4cba2291119cd6eba1f既没有裁决也没有 defer —— 没有属于它自己的 APPROVEDCHANGES_REQUESTEDCOMMENTED 评审。DISMISSED 不算:dismiss_stale_reviews 会在每次推送时作废机器人的批准,而那恰恰是需要一次新批准的时刻。如果这次重跑本应评审或批准,那么它没有做到;而其他账号留下的批准是另一张票,不能算作机器人自己的。

The stage comments above were updated with the latest result. View workflow run.

上方各阶段评论已更新为最新结果。查看工作流运行

@wenshao

wenshao commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

Review: Bound ACP textual tool-result payloads

Overview

Adds projectAcpToolResultUpdate, a pure transport-layer projection applied at two ACP egress points — Session.sendUpdate() (live) and replayContext.sendUpdate() in history-replay-page.ts (all replay collectors). Eligible pure-text content arrays and primitive-string rawOutput are each bounded to 65,536 JSON UTF-8 bytes using an exact JSON-escape byte scanner, a 20/80 head-tail preview, and a max-min budget allocator for multi-block content. Canonical transcripts, model-facing parts, schemas, and offline export are untouched.

The design is sound and the risky part — the byte accounting — holds up under inspection. Comments below are mostly about gaps that should be named explicitly rather than defects in what's implemented.

What I verified on the branch (2f7daf6f5c)

  • Tests: 34 projector + 14 replay-page + 5 export tests pass; eslint clean on the new and changed source files.
  • Reproduced the PR's numbers using the real createTranscriptToolCallResultUpdate constructor rather than a hand-built fixture: content 500,056 → 65,536; rawOutput 500,002 → 65,482; whole update 1,000,213 → 131,173 bytes.
  • Egress coverage is complete. All four new HistoryReplayer(...) sites are accounted for: Session.ts (through sendUpdate, projected), history-replay-page.ts ×2 (projected), and ui/utils/export/collect.ts (deliberately not projected — offline export stays lossless). acpAgent.ts bulk/loadUpdates/paged routes and serve/virtual-subagent-sessions.ts all funnel through the two projected collectors. No replay bypass found.
  • Latency on the send path: 2.1 ms for a single 5 MB block; 21 ms for 600 × 40 KB (24 MB of text); 0.6 ms for the over-MAX_CANONICAL_TEXT_BLOCKS fast path. Acceptable.
  • Correctness spot-checks that pass: jsonPayloadBytesAt agrees with JSON.stringify on 2-byte vs 6-byte control escapes, 1/2/3/4-byte UTF-8, valid surrogate pairs, and lone surrogates (6 bytes — relies on well-formed JSON.stringify, fine on supported Node). The max-min allocator's remainder distribution is sound: at the break, exactly active entries satisfy capacity > level and remainder < active, so every leftover byte is placed and no block exceeds its capacity. The maximumAllocation + 1 sentinel for early-stopped scans is safe because no block can be allocated more than maximumAllocation.

Findings

1. run_shell_command under PTY still ships an unbounded rawOutput — worth stating outright

shouldDefaultToNodePty() returns true on every non-Windows platform, so the default shell path produces an AnsiOutput array as resultDisplay, which createTranscriptToolCallResultUpdate copies verbatim into rawOutput. projectRawOutput only runs when typeof rawOutput === 'string', so this is exempt end-to-end. Measured: an AnsiOutput-shaped rawOutput of 20,000 rows passes through at 1,840,001 bytes with the projector applied.

content is still bounded for that case (it derives from the functionResponse.output string), so the amplification is roughly halved — but shell output is the single most common source of huge tool results, and "structured payloads out of scope" in the PR body under-sells it. Please call out AnsiOutput/run_shell_command by name in the design doc's Non-Goals and in the PR's out-of-scope list, so this isn't read as solved. (Presumably #8447 territory.)

2. tool_call (start) frames are not covered

The projector early-returns unless sessionUpdate === 'tool_call_update'. createTranscriptToolCallStartUpdate sets rawInput: options.args ?? {}, so a write_file carrying 1 MB of content still produces a >2 MB frame (measured 2,000,194). It's input rather than result, so arguably out of scope — but it's the same amplification class on the same wire and the Non-Goals section doesn't mention it.

3. The per-field bound is not a frame bound — _meta is unbudgeted

Measured a 1,000,130-byte frame with both eligible fields empty and a large _meta. The design doc says "not a universal ACP frame limit", which covers this, but naming _meta/artifacts explicitly would save the next reader the experiment.

4. Multi-block collapse is an all-or-nothing cliff

When baseTotal > availablePayloadBytes, projectContent returns fallbackContent() — the entire array becomes one marker block. 1,000 blocks × 100 chars (~100 KB) loses all 1,000 blocks rather than showing a prefix. Same for original.length > MAX_CANONICAL_TEXT_BLOCKS. Real content arrays are 1–2 blocks so the practical impact is low, but graceful degradation (keep as many leading blocks complete as fit, then a single trailing marker) would be strictly better, and at minimum the cliff deserves a code comment.

5. copyString — non-obvious idiom with no comment, and the costliest step

function copyString(value: string): string {
  return value.split('').join('');
}

The rationale (force a flat copy so a ~52 KB preview doesn't retain the 500 KB parent through a V8 sliced string) lives only in the design doc. This is exactly the line a future reader "simplifies" back into value.slice(...), silently reintroducing the retention. Please add a one-line comment. Worth noting in it that Buffer.from(v).toString() is not a safe substitute here — it would replace the lone surrogates the scanner deliberately supports with U+FFFD. It's also the most allocation-heavy step (a ~52 K-element array of one-char strings per truncated field), so the "why not something cheaper" answer belongs next to it.

6. Dead defensive branch

In truncateStringPayload, if (payloadBudget < TRUNCATION_MARKER_PAYLOAD_BYTES) is unreachable from both callers: projectRawOutput always passes 65,534, and in projectContent every truncated block's allocation is >= base = TRUNCATION_MARKER_PAYLOAD_BYTES. Harmless, but untestable through the public API — either drop it, assert the invariant, or note that it's a guard for future callers.

7. Test nits

  • The 20/80 test does rawOutput.split(MARKER) and destructures two elements; if a fixture ever contains the marker itself the assertion silently changes meaning. indexOf/lastIndexOf is firmer.
  • expect(projected === update).toBe(targetBytes <= 65_536) pins "unchanged" to reference identity. That is the contract here and structural sharing is covered separately, so it's fine — just flagging that it'll fail loudly on any future copy-always refactor.
  • No case for content absent entirely (undefined) alongside an oversized rawOutput; every existing case passes []. One-liner to add.

8. Recoverability signal (follow-up suggestion, not blocking)

The marker makes no recovery claim by design. But for a 200 KB read_file, an ACP client (Zed, web-shell) now shows a truncated body while the full text sits in the canonical transcript. Since the projection already sits next to _meta — which carries artifacts and sourceRecordIds — stamping something like _meta.acpTextProjected: true would be cheap and would let clients render "open full result" instead of leaving the user guessing whether the tool or the transport truncated. Worth considering for a later phase.

Style / conventions

  • License headers, naming, and the as unknown as Record<string, unknown> casting all match the surrounding ACP code.
  • Module-level consts depend on hoisted function declarations (createTextBlock, jsonStringJsonByteLength are used above their definitions). Legal and consistent with the file's ordering — just fragile if anyone converts them to const arrows.
  • The design doc is unusually good, and amending the stale "Immediate ACP display events remain unchanged" line in final-tool-response-budget.md is the right call rather than letting the two docs disagree.

Verdict

Approve, subject to two small things: add the copyString comment (item 5), and make the AnsiOutput/run_shell_command gap explicit in the PR body and the design doc's Non-Goals (item 1) so the residual exposure is on the record. Everything else is follow-up material.

@doudouOUC

Copy link
Copy Markdown
Collaborator Author

@wenshao Thanks for the thorough review. I checked each point against 2f7daf6f5c.

This PR has now been through roughly five review rounds. Per the repository's review-convergence rule, I am freezing non-Critical code and documentation churn at this point, so I am deferring these suggestions instead of pushing another revision and resetting CI/review. The current PR body and design already state that structured/rich payloads and universal ACP frame limits remain out of scope; the more specific AnsiOutput naming, adjacent code comment, and test polish can be handled with the tracked follow-up work.

No user decision is needed for this disposition.

@wenshao

wenshao commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

Sandboxed verification: ✅ passed — merge-ready (agent verdict) - workflow run

Ran the PR in an isolated, token-free container: A/B against the base build, mock-free harness assertions, targeted gates. Advisory evidence for human reviewers — not a review, an approval, or a CI check.

Scripted assertions: 683 passed · 0 failed · 683 total

中文 — 判定:✅ 通过 · 可合入(agent 判定)

沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查

脚本断言:683 通过 · 0 失败 · 683 总计

Verification report

PR #8450 — fix(cli): Bound ACP textual tool-result payloads

VERDICT: merge-ready — 683/683 scripted assertions passed, 0 unexpected failures.
Verified head: 2f7daf6f5c2957693cc4b4cba2291119cd6eba1f (git rev-parse HEAD^2; matches metadata headRefOid). A/B base: 93cd0190882b3e17ff5f48584119fe10a7456d90 (HEAD^1, the merge-ref base tip; the metadata snapshot's baseRefOid a5c637b… predates the base tip moving). Follow-up round 3 — round 2 (workflow run 31002469390) verified head 806cb6e against base a5c637b and reported merge-ready (694/694). Since then the PR pushed one new commit, 2f7daf6f5 "test(cli): Cover ACP projection boundary guards" (the metadata's first three commit OIDs are unchanged — a fast-forward push, not a rebase), and the base advanced to 93cd01908. Every measurement below was rebuilt and re-run at the new head; nothing was carried forward by shortcut (the old head object is not reachable in this depth-2 checkout and the input closure moved).

中文摘要 — 判定:✅ 通过 · 可合入(agent 判定)

沙箱验证在隔离、无凭证容器(node:22-bookworm)中执行了该 PR 的代码。本轮为第 3 轮跟进:上轮验证 head 806cb6e 后,PR 新增了一个纯测试 commit 2f7daf6f5("Cover ACP projection boundary guards",前 3 个 commit OID 未变),base 前进到 93cd01908。所有测量在新 head 上重建重跑,未沿用旧数据。脚本断言 683 通过 · 0 失败。

  • 上轮发现状态(见 "Previous-finding status" 表):两条 informational 发现均 维持(stands);其中发现 Where is the config saved? #2truncateStringPayload 死分支)本轮经变异分析扩展出一个同类 sibling(见 "Findings" F2)。
  • A/B 结论(见 "Central claim" 表,证据图 01-ab-seam-base-vs-head.png):base 上 500 KB 文本结果经 bulk / paged 两条 replay seam 原样投递(content 500,065 / rawOutput 500,011 / 整帧 1,000,493 JSON 字节);head 上同一场景被限制为 65,536 / 65,482 / 131,435 字节,可解析、< 256 KiB。与上轮形状一致 cell 逐字节相同;PR 自报证据表数字精确复现(content 65,536 / rawOutput 65,482,base content 500,055)。良性 1 KiB 结果两 build 序列化逐字节一致;CJK、emoji 代理对、多 block、A2UI、结构化/富内容、非字符串 rawOutput 等 sibling 形状均正确豁免或投影;多 block 扫描有界(最大 rung 172 ms)。
  • 本轮增量(boundary-guard 测试)验证(见 "Mutation matrix" 表,证据图 02-mutation-matrix-boundary-guards.png):8 个变异(7 个单点 + 1 个双点)中 4 个被对应测试以预期断言杀死(预算常数、rawOutput 边界比较器对、content 比较器、live seam),均为预期断言文本而非仅非零退出;4 个存活者经 108 形状语料 diff 逐一证明与 head 逐字节同行为(0 不一致,敏感度对照可检出真实差异),分类为**有意冗余的防御层(dead code)**而非覆盖缺口——新增测试钉住的是可观察边界行为本身。
  • 门禁(见 "Targeted gates",证据图 03-targeted-gates-head.png04-fastpath-closure-check.png):4 个测试文件 582/582 绿(投影套件由上轮 31 增至 34);serve fast-path bundle 闭包检查通过,planted-violation 证明门禁为活,真实 metafile 未被改动。
  • 未覆盖:逐 commit 归因(shallow,4 个 commit 仅 head 可达);跨测试文件变异 A/B 的旧侧文件(旧 head 不可达);完整 fake-MCP daemon 端到端(本轮复现 seam 级 wire 形状与字节数,S7 与 PR 表逐字节一致);Windows/macOS;repo 级 lint/format 与全量测试(PR 自身 CI 覆盖)。

仅作为评审证据,不构成评审、批准或 CI 检查

Previous-finding status (round 2 → this round)

# Finding (round 2) Severity Status at head 2f7daf6f5
1 Initial sessionUpdate: 'tool_call' frames are not projected (guard matches tool_call_update only); emitter census showed result text only ever leaves through tool_call_update Informational (matches design) Stands — re-walked at the new head: createTranscriptToolCallStartUpdate emits content: [] (packages/acp-bridge/src/transcript-replay.ts:259-268; its asUpdate variant also carries no result text), and result text/rawOutput are only produced by createTranscriptToolCallResultUpdatesessionUpdate: 'tool_call_update' (transcript-replay.ts:284-315), consumed at replay.ts:488/762/830. No live path bypasses the bound.
2 truncateStringPayload payloadBudget < marker branch (prefix-only, no marker) unreachable from both call sites — dead defensive code Informational Stands, and extended — both call sites still reserve ≥ marker payload (41 bytes): projectRawOutput budget = 65,534; projectContent allocations ≥ base = min(payloadBytes, 41), truncation invoked only when payloadBytes > allocationallocation ≥ 41. The M3-single mutation run additionally proved the function's first guard (originalPayloadBytes <= payloadBudget) is likewise always-false at call time (both callers invoke only after overflow) — see Findings F2.
Round-2 verdict merge-ready, 694/694 Re-measured at the new head: merge-ready, 683/683 (breakdown in Methodology). Shape-matched A/B cells are byte-identical to round 2 (see "Central claim") — evidence that the new test commit and the moved base changed no projection behavior.

Central claim + A/B

Central claim: eligible textual ACP tool-call updates (pure-text content arrays, primitive-string rawOutput) are independently bounded to ≤ 65,536 JSON UTF-8 bytes at the live (Session.sendUpdate) and replay (replayContext.sendUpdate) delivery boundaries, while canonical transcripts, offline export, A2UI, and structured/rich content stay untouched.

Both arms drive the compiled dist/ of the identical scenario through the real HistoryReplayer-backed collectors (collectHistoryReplayUpdates / replayTranscriptRecordPage); base arm: tmp/base-tree worktree at HEAD^1 (93cd01908) rebuilt with the repo's own scripts/build_package.js (base-build.log, exit 0 after regenerating src/generated/git-commit.ts). Native JSON.stringify UTF-8 byte length is the wire oracle. In-harness controls, all asserted on both arms: projector module absent on base / present on head (D-projector-module), 0 vs 4 projectAcpToolResultUpdate references in the dist seams (D-seam-callsites), and readlink -f of @qwen-code/qwen-code-core and @qwen-code/acp-bridge/transcriptReplay resolving into the base tree from the base arm and into the head tree from the head arm (D-core-realpath / D-bridge-realpath). Witness: evidence/01-ab-seam-base-vs-head.png (full 88-check run; raw ab-harness-run2.log).

Cell (identical fixture both arms) Oracle base control head
S1 bulk replay, head- + 499,999×x + -tail content JSON UTF-8 bytes 500,065 (verbatim) 65,536
same rawOutput JSON UTF-8 bytes 500,011 65,482
same full JSON-RPC session/update frame bytes 1,000,493 131,435 (< 256 KiB, parseable)
S2 bulk replay, 250,000× (750 KB UTF-8) content bytes verbatim 65,533 (surrogate-clean)
S3 bulk replay, 2×~300 KB blocks + 600,001-char rawOutput content bytes (block count) both blocks verbatim 65,536 (2 blocks, order preserved)
S4 bulk replay, benign result (contents) serialized updates 378 b byte-identical (378 b)
S5 paged transcript route, 500,000×p content / rawOutput bytes verbatim 65,536 / 65,482
S6 bulk replay, 120,000×😀 astral, rawOutput-only rawOutput bytes verbatim 65,531 (no lone surrogates, lossless JSON round-trip)
S7 PR's own bare fixture, 499,999×x both fields content / rawOutput bytes 500,055 (= PR's origin/main number) 65,536 / 65,482 (= PR evidence table, = round 2)

The base control's oversized cells are asserted as control expectations (they pass by being verbatim). Delta-stability vs round 2: shape-matched cells are byte-identical to round 2's measurements (S1 500,065/500,011/1,000,493 → 65,536/65,482/131,435; S7 = PR's own table). Frame overhead differs from the PR's daemon table (1,000,385/131,347) only by the synthetic session-id envelope; the field-level numbers are the contract and match exactly.

Secondary claims, all scripted (ab-harness.mjs, 85 checks total):

  • P1 byte accounting is a wire oracle against native JSON: 500 fixed-seed fuzzed + 10 targeted strings (controls, quotes, backslashes, DEL, CJK, astral, lone surrogates, U+2028/2029) — jsonStringJsonByteLengthBuffer.byteLength(JSON.stringify(s)), 0 mismatches.
  • P2 boundary ladder: rawOutput and content at 65,535 / 65,536 / 65,537 / 200,000 JSON bytes — no-op with reference stability at/below the budget, projected above it, output ≤ 65,536.
  • P3 exemption sweep (oversized payloads pass by same reference): diff / terminal / image blocks, blocks with extra fields (outer and inner), mixed text+diff, non-array content, non-string text, A2UI via toolName / serverId / both / legacy-toolName-only; a non-A2UI meta (github/create_issue) is not exempt; non-tool_call_update updates and tool_call start frames pass untouched.
  • P4 type-boundary probes: rawOutput as number / boolean / null / array / object passes untouched (same reference) while oversized content is still projected; absent rawOutput stays absent.
  • P5 immutability / idempotence: original not mutated; project(projected) === projected.
  • P6 single-block reuse: when content is one text block equal to rawOutput, both fields share the stricter content preview.
  • P7 20/80 preview: marker present, head/tail anchors intact, tail/head = 4.00.
  • P8 scan caps (the perf commit): 600×40 KB blocks 94.3 ms; 200×50 KB mixed + 500 KB rawOutput 172.3 ms; 5 MB rawOutput 4.6 ms; 2 MB CJK 2.0 ms; 2,000-block collapse 0.5 ms — every rung < 500 ms on the loaded shared runner; 2.5 MB vs 5 MB linearity ratio 0.72 (no superlinear rung).
  • P9 allocation edge shapes: small blocks stay complete while the budget saturates at exactly 65,536; both collapse paths produce one canonical marker block; 1,191 empty blocks (the block-count maximum) pass by reference.

Corrections

None — no inaccurate statements from earlier review rounds or bots required correction this round. No instruction-injection attempts were detected in the PR title/body/commits.

Findings

No blocking findings. Every executed assertion passed; benign traffic is byte-identical across arms (zero collateral); the new boundary behavior is pinned by the suite (M2/M3b/M6/M7 kills below). Two informational observations:

F1 (informational): the new boundary tests pin observable outcomes; the guard layers they name are individually redundant by construction

The mutation matrix (below) deleted each defensive layer of projectContent/projectRawOutput one at a time. Four mutants survived the full 34-test suite: the MAX_CANONICAL_TEXT_BLOCKS early guard (M1), the baseTotal > available fallback (M4), the final byte-budget ternary (M5), and a single flip of the rawOutput no-op comparator pair (M3-single). Each was then adjudicated with a 108-shape corpus diff against head: 0/108 output-byte mismatches for every one (and the diff harness's sensitivity control distinguishes a real behavior change, 65,669 vs 100,133 bytes), so each is dead code in the strict sense — the clause cannot decide any outcome — not a coverage gap:

  • with M1's guard gone, n ≥ 1192 blocks still collapse because skeleton bytes 55n + 1 exceed the budget (skeleton(1192) = 65,561 > 65,536), driving availablePayloadBytes negative and baseTotal > available to the same fallback (measured: empty-block skeleton 54 B, skeleton(1191) = 65,506 fits, skeleton(1192) = 65,561 does not);
  • with M4's fallback gone, the minimum-marker-set case still collapses through the final ternary (allocation math drives every block's keep-decision into the overrun path);
  • with M5's ternary gone, the other two layers keep every allocation within budget, so it never fires;
  • M3-single's comparator flip is absorbed by truncateStringPayload's own first guard, and vice versa — only the pair decides (the double mutant M3b dies on exactly the one intended cell).

The consequence for the new tests this round added: names like "collapses content when the empty structure cannot fit" suggest coverage of a specific guard, but what is actually pinned is the emergent arithmetic — and that is pinned, hard: the budget constant (M2: 16 failed), the rawOutput comparator pair (M3b: exactly 1 failed, the 65,536 reference-stability cell), the content-side comparator (M7: expected false to be true at 65,536, plus idempotence at exact budget), and the live seam (M6). Defense-in-depth on a safety projection is a reasonable author choice and nothing here asks for it to be removed; the note is so the next maintainer knows no single layer is load-bearing and a refactor must not assume any one of them carries the invariant alone.

F2 (informational, extends round-2 finding #2): both defensive branches inside `truncateStringPayload` are unreachable from its only call sites

Round 2 established that the payloadBudget < TRUNCATION_MARKER_PAYLOAD_BYTES prefix-only branch is never entered (both call sites reserve ≥ 41 marker-payload bytes; re-verified at the new head with the exact constant — marker payload = 41 bytes). This round's M3-single mutation proves the sibling: the function's first line, if (originalPayloadBytes <= payloadBudget) return value;, is always false at call time — projectRawOutput returns early when payloadBytes <= payloadBudget and projectContent only calls truncate when payloadBytes[index] > allocations[index]. The surviving mutant plus 0/108 corpus mismatches is the behavioral proof. Harmless dead defense; noted for completeness, no action required.

Mutation matrix — boundary guards (this round's delta)

Positive control first: the four affected suites ran green unmutated (poscontrol-green.log: 34 + 14 + 5 + 529 = 582 tests). Kills below failed on the intended assertion text, not merely non-zero exit; every mutant was restored (git status --porcelain packages/cli/src empty after each). Raw logs mutant-M1.logmutant-M7.log, mutant-M3b.log; witness evidence/02-mutation-matrix-boundary-guards.png.

Mutant Suite Result
M1 delete MAX_CANONICAL_TEXT_BLOCKS guard projection file (34) survived → adjudicated dead code (0/108 corpus mismatches; collapse at n ≥ 1192 enforced by skeleton arithmetic)
M2 budget 65,536 → 131,072 projection file killed: enforces the rawOutput boundary at 65537 — expected true to be false (16 failed | 18 passed)
M3-single rawOutput comparator <=< only projection file survived → the paired inner guard absorbs it (0/108 corpus mismatches; see F1/F2)
M3b both rawOutput comparators <=< projection file killed: exactly 1 failed — enforces the rawOutput boundary at 65536 JSON bytes (1 failed | 33 passed)
M4 delete baseTotal > available fallback projection file survived → adjudicated dead code (final ternary catches the overrun; 0/108)
M5 delete final byte-budget ternary projection file survived → adjudicated dead code (remaining layers keep allocations in budget; 0/108)
M6 revert live seam (Session.sendUpdate passes update) Session.test.ts live-boundary test killed: expected 500065 to be less than or equal to 65536 (1 failed | 528 skipped)
M7 content needsProjection comparator >>= projection file killed: enforces the content boundary at 65536 — expected false to be true (+ idempotence at exact budget, same root cause; 2 failed | 32 passed)

Survivor adjudication (survivor-diff.mjs, survivor-diff.log): each surviving mutation applied to a copy of the compiled dist JS (the shipped artifact), imported alongside head, outputs byte-compared over 108 corpus shapes concentrated on each guard's decision zone (block counts 1–2,000 around the 1,191/1,192 boundary × 6 block sizes; marker-set neighbourhoods n ∈ {500…1,191} × len ∈ {40…500}; allocation-remainder mixes; exact-fit 65,534–65,537 and 100,000-byte fields; CJK/astral/A2UI/exempt/non-string shapes). Result: 0 mismatches for all four survivors. Sensitivity control (mutated-sensitivity probe): the same harness against a budget-131,072 mutant reports differ=true (65,669 vs 100,133 bytes) — it can detect a real behavior change, so the zeros are evidence, not silence.

Targeted gates (head)

evidence/03-targeted-gates-head.png, raw poscontrol-green.log (vitest, exit 0):

  • acp-tool-result-text-projection.test.ts34/34 (round 2: 31 — the suite grew by the boundary-guard tests this round)
  • history-replay-page.test.ts14/14 (incl. the two PR-added seam tests)
  • ui/utils/export/collect.test.ts5/5 (incl. lossless-export: length + SHA-256 preserved)
  • Session.test.ts529/529 (incl. the live-boundary test)

Plus scripts/check-serve-fast-path-bundle.js after a fresh DEV=true npm run bundle at HEAD (bundle.log, exit 0): "Startup bundle closure checks passed" (exit 0) — the projector's static @qwen-code/acp-bridge/bridgeClient import did not leak the ACP runtime into the serve fast-path closure. In the actual metafile the projector bundles into chunk-6JIS4RXB.js (46,246 B) and bridgeClient into chunk-7U35SVFJ.js (420,815 B); the fast-path chunk (fast-path-SZIMOHHK.js, 16,752 B) contains neither. Gate liveness proven (fastpath-liveness.mjs, fastpath-liveness.log): injecting packages/acp-bridge/src/bridgeClient.ts into the fast-path chunk's inputs of a copy of the metafile made checkServeFastPathBundle return ok=false naming ACP bridge client runtime; the real dist/esbuild.json was sha256-identical before/after (also cmp-clean against its backup). Evidence evidence/04-fastpath-closure-check.png. Typecheck is covered by the pre-ran CI npm run build at HEAD and the base worktree's clean tsc --build (0 errors).

Not covered

  • Per-commit attribution: depth-2 checkout — git rev-list HEAD^1..HEAD^2 yields only 2f7daf6f5 while the metadata lists 4 commits (ff0c1a9, 81ea3d9, 806cb6e, 2f7daf6f5); the repository is shallow, so the rest are assumed unreachable (the rev-list count of 1 is the shallow-boundary artifact, not a true count). The aggregate HEAD^1..HEAD diff is what was verified.
  • Old-side test-file comparison for the test-only delta: the pre-commit test files (head 806cb6e) are not reachable in this checkout, so a killed/total mutation A/B across test-file versions was impossible; instead the full current suite was adjudicated guard-by-guard (matrix above). The net observable: projection suite grew 31 → 34 tests and the boundary behavior is pinned (M2/M3b/M7).
  • Full fake-MCP end-to-end from the PR's evidence table: the A/B drives the real replay collectors and the real projector from compiled dist — it reproduces the wire shape and the exact field byte counts (S7 = PR table), not the daemon-level trigger path (model finalization → duplicate amplification). Divergence risk low but nonzero.
  • Live seam over a real daemon/socket — covered instead by Session.test.ts's live-boundary test (gate) plus the M6 mutation; not exercised over a live ACP connection.
  • Headless output, A2UI rendering, structured/rich payloads, generic NDJSON caps, backpressure, replay aggregate limits, diagnostics, artifact lifecycle — declared unchanged by the PR; exemptions verified at projector level only (P3).
  • Windows/macOS — container is Linux.
  • Repo-wide lint/format and full test suite — covered by the PR's own CI; targeted gates above. Fast-path closure check ran at HEAD only (its forbidden-closure invariant is the regression guard; a base-side bundle would cost a second full bundle).
  • Trial merge into latest main — no network/token in this container; the merge-ref checkout is the trial merge (the workflow merged the PR head into the current base tip 93cd01908 cleanly), and this round's whole A/B ran on that merged tree.
  • Post-cleanup A/B re-runs: the base worktree was removed by design once the A/B cells were captured, so the harness can no longer resolve its base arm (ENOENT on a late re-run attempt — environmental, not a PR outcome). The official A/B records are ab-harness-run2.log and the two captured live runs, all exit 0 with 85/85.

Methodology

Environment: node:22-bookworm CI container, node v22.23.2, refs/pull/8450/merge at depth 2 (HEAD = f6db98b85, base tip HEAD^1 = 93cd01908, PR head HEAD^2 = 2f7daf6f5); npm ci + npm run build pre-ran at HEAD. Base control: git worktree add tmp/base-tree HEAD^1, wired to the already-installed dependencies — root node_modules entries symlinked from the head tree except the @qwen-code scope (21 workspace links re-pointed into the base tree) and per-package nested node_modules symlinked from head (external deps only; no nested @qwen-code entries exist, and the PR touches no package.json/lockfile, so versions are lockfile-identical). One wiring mishap was caught and repaired in-flight: the head tree's node_modules/@qwen-code/external-context symlink was briefly re-targeted by the wiring script; it was restored to its lockfile-correct target (../../integrations/external-context) and every subsequent head-side gate, bundle, and test run passed green. Base built with the repo's own scripts/build_package.js for core, acp-bridge, cli (exit 0, base-build.log) after running the repo's scripts/generate-git-commit-info.js for the base tree's gitignored generated file. The harness (ab-harness.mjs) imports each arm's compiled dist/... via file:// URLs and drives the real HistoryReplayer-backed collectors with synthetic ChatRecords shaped exactly like the repo's own test fixtures; arm isolation is asserted in-harness (D-controls above, incl. realpath of both internal deps per arm). Mutation checks (run-mutations.mjs, run-m3b.mjs) edited head sources with single-occurrence-verified replacements, ran the pinned vitest files, required the intended assertion text in the failure output, and restored via rewrite (clean git status after each); survivor classification (survivor-diff.mjs) mutated copies of the compiled dist JS and byte-compared outputs over the 108-shape corpus with a positive sensitivity control. Fast-path: fresh DEV=true npm run bundle (metafile dist/esbuild.json, backed up), the repo's check script, and a planted-violation probe against a metafile copy. Raw logs (ab-harness-run1.log, ab-harness-run2.log, base-build.log, bundle.log, fastpath-check.log, fastpath-liveness.log, mutant-M1.logmutant-M7.log, mutant-M3b.log, mutation-run.log, poscontrol-green.log, survivor-diff.log, esbuild-metafile-backup.json) and harness scripts live in this directory; evidence PNGs were produced with scripts/verify-capture.mjs. Assertion accounting: 85 A/B-harness checks + 582 targeted-gate vitest tests (34+14+5+529) + 14 mutation-round checks (1 positive control + 8 mutant verdicts incl. M3b + 4 survivor corpus diffs + 1 sensitivity control) + 2 fast-path closure checks (real-green + planted-red) = 683, all executed, all passed; fail counts only unexpected outcomes and there were none.

Evidence images

01-ab-seam-base-vs-head

02-mutation-matrix-boundary-guards

03-targeted-gates-head

04-fastpath-closure-check

Harness scripts and raw logs are in the workflow run artifacts (7-day retention).

Qwen Code · sandboxed verification

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Triage re-run completed without a new review.

⚠️ The bot has neither a verdict nor a deferral on 2f7daf6f5c2957693cc4b4cba2291119cd6eba1f — no APPROVED, CHANGES_REQUESTED, or COMMENTED review of its own. A DISMISSED one does not count: dismiss_stale_reviews voids the bot's approval on every push, which is exactly when a fresh one is needed. If this re-run was meant to review or approve, it did not, and an approval left by another account is a separate vote that does not count as the bot's own.

⚠️ 机器人在 2f7daf6f5c2957693cc4b4cba2291119cd6eba1f既没有裁决也没有 defer —— 没有属于它自己的 APPROVEDCHANGES_REQUESTEDCOMMENTED 评审。DISMISSED 不算:dismiss_stale_reviews 会在每次推送时作废机器人的批准,而那恰恰是需要一次新批准的时刻。如果这次重跑本应评审或批准,那么它没有做到;而其他账号留下的批准是另一张票,不能算作机器人自己的。

The stage comments above were updated with the latest result. View workflow run.

上方各阶段评论已更新为最新结果。查看工作流运行

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed. Suggestions are inline. Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.

中文说明

已审查。 建议见行内评论。 未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。

— qwen3.8-max via Qwen Code /review (v0.21.5)

@yiliang114 yiliang114 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-checked at latest head: the new commit is test-only (pins the 1191-block exact-fit, the non-canonical extra-fields exemption, and legacy A2UI-by-toolName exemption); the projection module code is byte-identical to the reviewed version, so the 256KB bound and its verified properties are unchanged. Approval stands. Residual out-of-scope vectors (rawInput, structured rawOutput, non-canonical content passed through, bulk-replay frames) remain deferred per the design doc — keep the tracking items.

@doudouOUC
doudouOUC added this pull request to the merge queue Aug 5, 2026
Merged via the queue into main with commit 2ad72fd Aug 5, 2026
180 of 184 checks passed
@doudouOUC
doudouOUC deleted the fix/acp-tool-result-wire-budget branch August 5, 2026 15:14
@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Released in v0.21.6.

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.

5 participants