Skip to content

fix(cli): auto-restore prompt and preserve queue on cancel#4023

Merged
wenshao merged 20 commits into
mainfrom
fix/cancel-auto-restore
May 13, 2026
Merged

fix(cli): auto-restore prompt and preserve queue on cancel#4023
wenshao merged 20 commits into
mainfrom
fix/cancel-auto-restore

Conversation

@wenshao

@wenshao wenshao commented May 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

When a user pressed ESC immediately after submitting a prompt (before the model produced any meaningful output), qwen-code left the cancelled prompt stranded in the transcript and in cross-session ↑-history. Cancelling during tool execution also silently dropped any queued follow-up input.

This PR mirrors Claude Code's auto-restore-on-interrupt behaviour:

  • Drain the queue back into the input buffer on EVERY cancel path, including tool-execution cancels — replaces the unconditional clearQueue() (added for ESC后经常默认输入了上一次的输入 #3204) with a non-destructive pop, so queued follow-ups are preserved (editable in the buffer) rather than silently lost.
  • Auto-restore on bare cancel: when the user cancels with no draft text, no queued input, and no meaningful pending or committed assistant content, truncate the user item and trailing INFO from history and pull the prompt text back into the input box for editing.
  • Cross-session ↑-history is also cleaned: new Logger.removeLastUserMessage undoes the just-persisted disk entry from getPreviousUserMessages so the cancelled prompt doesn't resurrect next session.

Reproduction (before this PR)

  1. Open qwen-code in an interactive session.
  2. Type what time is it? and press Enter.
  3. Press ESC immediately, before the model emits its first token.
  4. Observe: transcript shows > what time is it? followed by User cancelled the request.; ↑ recall lists the cancelled prompt; input box is empty (the user must retype to retry).
  5. Reopen qwen-code in a new session — ↑ recall still surfaces the cancelled prompt from the previous session.

After this PR: the transcript shows nothing for that turn (rewound), the input box is pre-filled with what time is it? ready to edit or resubmit, and ↑ recall doesn't show the cancelled prompt in the current or next session.

A second variant: submit slow command, queue next thing while it streams, hit ESC during a long-running tool. Before: queued next thing is silently dropped. After: next thing lands in the input buffer for review.

Behaviour change (qwen-code)

Behaviour Before After
ESC right after submit → transcript shows orphan user msg + "Request cancelled." yes no (rewound)
ESC right after submit → ↑-history (this session) shows cancelled prompt yes no
ESC right after submit → ↑-history (next session) shows cancelled prompt yes no
ESC right after submit → prompt restored to input box for editing no yes
ESC during tool execution drops queued follow-up input yes no (preserved)
Model produced meaningful content before cancel → keep transcript yes yes (still bails)
User typed during loading then cancelled → preserve draft yes yes (still bails)
User queued another message then cancelled → preserve queue partial (lost on tool-cancel) yes (always)

Out of scope / unchanged

  • Cancels after the model produced text or ran a tool: transcript untouched (existing behavior — guarded by messagesAfterAreOnlySynthetic-style trailing check + pending non-synthetic check).
  • Programmatic aborts (background turn takeover, generation switching, etc.): no rewind triggered — only user-initiated ESC/Ctrl+C reaches cancelOngoingRequest → onCancelSubmit.
  • Tool confirmation cancel (StreamingState.WaitingForConfirmation): different code path, not affected.
  • Persisted session JSONL transcripts (used by resume): unchanged.
  • Slash-submitted prompts (/some-shortcut → submit_prompt) and Cron / Notification submits: these never add a user history item to begin with, so the auto-restore guard naturally bails (no lastUserItem belongs to the cancelled turn).

Context on #3204

#3204 was a regression where items the user had queued during a tool run would auto-fire after the tool settled, even though the user had pressed ESC to cancel. The fix at the time was the unconditional clearQueue() in the tool-cancel branch — correct for the auto-fire problem but blunt: any queued text the user might have wanted to edit was lost.

This PR replaces clearQueue() with popAllMessages() for the same path. The auto-fire bug stays fixed (the queue still ends up empty after cancel — drainQueue mid-turn returns nothing), but the text lands in the input buffer for the user to edit or discard, matching the response-cancel branch's behaviour and Claude Code's popAllEditable.

Implementation notes

The "meaningful content" check matches Claude Code's messagesAfterAreOnlySynthetic (REPL.tsx / MessageSelector.tsx): assistant text and tool runs are meaningful; info/error/warning/retry/notification/tool_use_summary/thoughts are synthetic. The new helper lives at packages/cli/src/ui/utils/historyUtils.ts.

Auto-restore uses historyManager.truncateToItem (functional setState) so the slice batches with the INFO addItem queued by cancelOngoingRequest in the same render pass — no flicker, no double-render.

Logger.removeLastUserMessage tracks the most recently persisted USER entry (mirroring claude-code's lastAddedEntry in history.ts) and identifies it by (sessionId, messageId, timestamp, message, type) for safe undo. It is one-shot, no-ops cleanly on uninitialized loggers and on external rotation, and never touches MODEL_SWITCH entries.

The auto-restore guards are intentionally conservative — any of buffer non-empty, queue had items, pending stream has meaningful content, committed trailing items aren't all synthetic, or no last-user item causes a bail. Better to leave the cancelled prompt in the transcript than to wrongly clobber a draft or a real response.

Test plan

  • packages/cli/src/ui/utils/historyUtils.test.ts — 9 cases covering synthetic / meaningful classification, trailing-only-synthetic detection, last-user-index lookup, including thought-as-synthetic parity with Claude Code.
  • packages/cli/src/ui/AppContainer.test.tsx — 6 cancel-handler cases covering: auto-restore happy path, bail when assistant produced content, bail when buffer has draft text, bail when queue had items (queue still drained to buffer), bail when tool_group is pending, queue drained on tool-execution cancel.
  • packages/core/src/core/logger.test.ts — 7 removeLastUserMessage cases: basic undo, no-op when nothing tracked, one-shot semantic, MODEL_SWITCH not undone, external rotation handled, uninitialized-logger no-op, tracker cleared on transient writeFile error.
  • tsc --noEmit clean across cli + core (apart from pre-existing diffCommand errors).
  • eslint clean on all touched files.
  • All touched-area test suites pass: 181/181 (cli) + 45/45 (core).

🇨🇳 中文版本(点击展开)

概述

当用户提交 prompt 后立即按 ESC(在模型产出任何有意义输出之前),qwen-code 会把已取消的 prompt 残留在 transcript 和跨会话 ↑ 历史中。在 tool 执行期间取消还会静默丢弃已排队的后续输入。

本 PR 复刻 Claude Code 的 auto-restore-on-interrupt 行为:

  • 所有取消路径都把队列回填到输入框(包括 tool 执行中取消)——把当年为修 ESC后经常默认输入了上一次的输入 #3204 加的 clearQueue() 替换为非破坏性的 pop:队列中的后续指令保留下来(落到输入框可编辑),而不是被无声丢弃。
  • 裸取消时自动回滚:当用户取消时,输入框为空、队列为空、且没有任何有意义的 pending 或已提交的 assistant 内容,则把 user 项 + 后续 INFO 从 history 中切除,并把 prompt 文本回填到输入框供编辑。
  • 跨会话 ↑ 历史也会清理:新增 Logger.removeLastUserMessage,撤销刚刚写入磁盘的条目,避免下一次会话从 getPreviousUserMessages 复活已取消的 prompt。

复现步骤(PR 之前)

  1. 打开 qwen-code 进入交互式会话。
  2. 输入 what time is it? 并回车。
  3. 立刻按 ESC(模型还没吐出第一个 token 之前)。
  4. 观察:transcript 显示 > what time is it? + User cancelled the request.;↑ 回溯能看到这条已取消的 prompt;输入框为空(用户必须重新打字才能重试)。
  5. 重新打开 qwen-code 启动新会话 —— ↑ 回溯仍会显示上一次会话取消掉的 prompt。

应用本 PR 之后:transcript 对这一轮干干净净(已回滚),输入框预填了 what time is it? 可直接编辑或重发,本会话和下一次会话的 ↑ 回溯都不再出现这条已取消的 prompt。

第二种场景:提交 slow command,在它流式响应期间排入 next thing,然后在 long-running tool 中按 ESC。改动前:next thing 被静默丢弃;改动后:next thing 落到输入框供查看。

行为变化(qwen-code)

行为 改前 改后
提交后立即 ESC → transcript 残留孤立 user 消息 + "Request cancelled." 否(已回滚)
提交后立即 ESC → ↑ 历史(本会话)出现已取消 prompt
提交后立即 ESC → ↑ 历史(下次会话)出现已取消 prompt
提交后立即 ESC → prompt 回填到输入框可编辑
Tool 执行中 ESC 丢失队列中后续输入 否(保留)
模型已产出有意义内容才取消 → 保留 transcript 是(仍然 bail)
Loading 中用户打字后取消 → 保留草稿 是(仍然 bail)
用户排队下一条后取消 → 保留队列 部分(tool-cancel 时丢失) 是(始终保留)

不在范围 / 不变更

  • 模型已产出文本或运行过 tool 之后再取消:transcript 保持原样(由 messagesAfterAreOnlySynthetic 风格的尾部检查 + pending 非 synthetic 检查共同守卫)。
  • 程序化 abort(后台 turn 接管、生成切换等):不触发回滚 —— 只有用户主动按 ESC/Ctrl+C 走 cancelOngoingRequest → onCancelSubmit 的路径才会触发。
  • Tool 确认对话框中的取消(StreamingState.WaitingForConfirmation):走另一条代码路径,不受影响。
  • 持久化的会话 JSONL transcript(resume 时使用):不变。
  • Slash 提交的 prompt(/some-shortcut → submit_prompt)以及 Cron / Notification 类提交:这些路径本就不会写入 user 类型的 history 项,所以 auto-restore 守卫自然 bail(找不到属于这一轮的 lastUserItem)。

#3204 背景

#3204 报的是一个回归:用户在 tool 运行期间排队的指令,在 tool 结束、状态恢复 idle 后会自动触发,即便用户按过 ESC 想取消。当时的修复是在 tool-cancel 分支无条件 clearQueue() —— 解决了 auto-fire,但代价较大:用户可能想编辑/复用的排队文本被一并丢弃。

本 PR 把这一处的 clearQueue() 换成 popAllMessages():auto-fire 仍然不会发生(取消后队列依旧为空,drainQueue 在轮中读到的就是空),但文本会落到输入框供用户编辑或丢弃,与 response-cancel 分支的行为以及 Claude Code 的 popAllEditable 保持一致。

实现要点

"有意义内容" 的判定对齐 Claude Code 的 messagesAfterAreOnlySynthetic(REPL.tsx / MessageSelector.tsx):assistant 文本和 tool 运行算 meaningful;info/error/warning/retry/notification/tool_use_summary/thoughts 算 synthetic。新工具函数放在 packages/cli/src/ui/utils/historyUtils.ts

Auto-restore 走 historyManager.truncateToItem(函数式 setState),所以这次切片会跟 cancelOngoingRequest 刚 enqueue 的 INFO addItem 在同一次 React render 中合批 —— 无闪烁、无双 render。

Logger.removeLastUserMessage 追踪最近一次成功持久化的 USER 条目(对应 claude-code history.tslastAddedEntry),并以 (sessionId, messageId, timestamp, message, type) 五元组精确匹配,避免误删。一次性语义;logger 未初始化或外部已轮转时安全 no-op;不会动 MODEL_SWITCH 条目。

Auto-restore 的守卫有意保守 —— buffer 非空队列原本有内容pending 流有 meaningful 内容committed 尾部不全是 synthetic找不到 last-user 项 任一为真就 bail。宁可让已取消的 prompt 留在 transcript 里,也不要错误地覆盖用户草稿或真实回答。

测试计划

  • packages/cli/src/ui/utils/historyUtils.test.ts —— 9 个用例,覆盖 synthetic / meaningful 分类、尾部全 synthetic 判定、last-user 索引查找,包含 thought-as-synthetic 与 Claude Code 对齐的判定。
  • packages/cli/src/ui/AppContainer.test.tsx —— 6 个 cancel-handler 用例:auto-restore 正常路径;assistant 已产出内容时 bail;buffer 有草稿时 bail;队列有内容时 bail(队列仍回填 buffer);pending tool_group 时 bail;tool 执行中取消时队列回填 buffer。
  • packages/core/src/core/logger.test.ts —— 7 个 removeLastUserMessage 用例:基础撤销、空时 no-op、一次性语义、不误删 MODEL_SWITCH、外部轮转处理、未初始化 no-op、瞬态 writeFile 异常时清空 tracker。
  • tsc --noEmit 干净(cli + core,pre-existing diffCommand 噪声除外)。
  • eslint 在所有触动文件上干净。
  • 涉及测试套件全绿:181/181 (cli) + 45/45 (core)。

…h Claude Code

When a user pressed ESC immediately after submitting a prompt (before the
model produced any meaningful output), qwen-code left the cancelled prompt
stranded in the transcript and in cross-session ↑-history. Cancelling
during tool execution also silently dropped any queued follow-up input.

Mirror Claude Code's auto-restore-on-interrupt:

  - Drain the queue back into the input buffer on EVERY cancel path,
    including tool-execution cancels (replaces the unconditional
    clearQueue() that motivated #3204 with a non-destructive pop).
  - When the user cancels with no draft text, no queued input, and no
    meaningful pending/committed assistant content, truncate the user
    item and trailing INFO from history and pull the prompt text back
    into the input box for editing.
  - Add Logger.removeLastUserMessage so the disk-backed cross-session
    ↑-history (getPreviousUserMessages) is also cleaned on cancel.

The "meaningful content" check matches Claude Code's
messagesAfterAreOnlySynthetic: gemini text and tool runs are meaningful;
info/error/warning/retry/notification/tool_use_summary/thoughts are
synthetic. truncateToItem uses functional setState so it batches with
the INFO addItem from cancelOngoingRequest in the same render pass —
no flicker.

Tests cover all five guard branches and the logger undo across normal,
no-op, one-shot, MODEL_SWITCH-interleaved, disk-rotation, and
uninitialized cases.
@github-actions

github-actions Bot commented May 10, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Summary

Package Lines Statements Functions Branches
CLI 75.61% 75.61% 76.58% 80.34%
Core 78.26% 78.26% 80.84% 82.62%
CLI Package - Full Text Report
-------------------|---------|----------|---------|---------|-------------------
File               | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------------|---------|----------|---------|---------|-------------------
All files          |   75.61 |    80.34 |   76.58 |   75.61 |                   
 src               |   73.64 |    67.35 |   76.47 |   73.64 |                   
  gemini.tsx       |   61.11 |    59.13 |   66.66 |   61.11 | ...18,835-838,846 
  ...ractiveCli.ts |   80.02 |    68.61 |   78.57 |   80.02 | ...1021,1059,1162 
  ...liCommands.ts |   76.17 |    73.33 |     100 |   76.17 | ...50-274,299,401 
  ...ActiveAuth.ts |     100 |     87.5 |     100 |     100 | 66-80             
 ...cp-integration |   53.94 |    66.66 |   58.82 |   53.94 |                   
  acpAgent.ts      |   56.25 |    67.01 |   65.51 |   56.25 | ...71-873,887-895 
  authMethods.ts   |   12.19 |      100 |       0 |   12.19 | 11-31,34-38,41-50 
  errorCodes.ts    |       0 |        0 |       0 |       0 | 1-22              
  ...DirContext.ts |     100 |      100 |     100 |     100 |                   
 ...ration/service |   68.65 |    83.33 |   66.66 |   68.65 |                   
  filesystem.ts    |   68.65 |    83.33 |   66.66 |   68.65 | ...32,77-94,97-98 
 ...ration/session |   76.02 |    70.59 |      84 |   76.02 |                   
  ...ryReplayer.ts |   65.93 |    75.67 |   81.81 |   65.93 | ...40-255,268-269 
  Session.ts       |   75.12 |    68.89 |    85.1 |   75.12 | ...2456,2462-2465 
  ...entTracker.ts |   90.85 |    84.84 |      90 |   90.85 | ...35,199,251-260 
  index.ts         |       0 |        0 |       0 |       0 | 1-40              
  ...ssionUtils.ts |   84.21 |    77.77 |     100 |   84.21 | ...37-153,209-211 
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 ...ssion/emitters |   96.01 |    90.75 |    92.3 |   96.01 |                   
  BaseEmitter.ts   |   76.92 |    66.66 |      80 |   76.92 | 23-24,39-40,55-56 
  ...ageEmitter.ts |     100 |    89.47 |     100 |     100 | 109,111           
  PlanEmitter.ts   |     100 |      100 |     100 |     100 |                   
  ...allEmitter.ts |   98.06 |     92.3 |     100 |   98.06 | 227-228,327,335   
  index.ts         |       0 |        0 |       0 |       0 | 1-10              
 ...ession/rewrite |   90.36 |    87.83 |   94.11 |   90.36 |                   
  LlmRewriter.ts   |      81 |       84 |     100 |      81 | ...,88-89,155-159 
  ...Middleware.ts |   95.83 |    85.71 |     100 |   95.83 | 119,127-129       
  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/auth          |   97.68 |    94.85 |   95.45 |   97.68 |                   
  allProviders.ts  |     100 |      100 |     100 |     100 |                   
  ...iderConfig.ts |    97.6 |    95.04 |     100 |    97.6 | ...61,411,433-434 
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 src/auth/install  |   98.57 |    88.88 |     100 |   98.57 |                   
  ...nstallPlan.ts |   98.57 |    88.88 |     100 |   98.57 | 80,93             
 ...viders/alibaba |   96.96 |    66.66 |   66.66 |   96.96 |                   
  ...baStandard.ts |     100 |      100 |     100 |     100 |                   
  codingPlan.ts    |   93.67 |    66.66 |   66.66 |   93.67 | 83,87-89,94       
  tokenPlan.ts     |     100 |      100 |     100 |     100 |                   
 ...oviders/custom |     100 |      100 |     100 |     100 |                   
  ...omProvider.ts |     100 |      100 |     100 |     100 |                   
 ...roviders/oauth |    91.5 |    77.03 |   97.05 |    91.5 |                   
  openrouter.ts    |   84.37 |    33.33 |     100 |   84.37 | 43-48             
  ...outerOAuth.ts |    91.9 |    79.06 |   96.87 |    91.9 | ...53-655,699-701 
 ...ers/thirdParty |     100 |      100 |     100 |     100 |                   
  deepseek.ts      |     100 |      100 |     100 |     100 |                   
  idealab.ts       |     100 |      100 |     100 |     100 |                   
  minimax.ts       |     100 |      100 |     100 |     100 |                   
  zai.ts           |     100 |      100 |     100 |     100 |                   
 src/commands      |   80.92 |    85.71 |      50 |   80.92 |                   
  auth.ts          |     100 |    83.33 |     100 |     100 | 11,14             
  channel.ts       |   56.66 |      100 |       0 |   56.66 | 15-19,27-34       
  extensions.tsx   |   96.55 |      100 |      50 |   96.55 | 37                
  hooks.tsx        |   66.66 |      100 |       0 |   66.66 | 20-24             
  mcp.ts           |   94.73 |      100 |      50 |   94.73 | 28                
  review.ts        |   51.85 |      100 |       0 |   51.85 | 24-35,38          
 ...mmands/channel |   39.25 |    79.45 |      50 |   39.25 |                   
  ...l-registry.ts |    8.57 |      100 |       0 |    8.57 | 6-21,24-42        
  config-utils.ts  |      92 |      100 |   66.66 |      92 | 21-26             
  configure.ts     |    14.7 |      100 |       0 |    14.7 | 18-21,23-84       
  pairing.ts       |   26.31 |      100 |       0 |   26.31 | ...30,40-50,52-65 
  pidfile.ts       |   96.34 |    86.95 |     100 |   96.34 | 49,59,91          
  start.ts         |   30.98 |       52 |   69.23 |   30.98 | ...72-475,484-486 
  status.ts        |   17.85 |      100 |       0 |   17.85 | 15-26,32-76       
  stop.ts          |      20 |      100 |       0 |      20 | 14-48             
 ...nds/extensions |   84.53 |    88.95 |   81.81 |   84.53 |                   
  consent.ts       |   71.65 |    89.28 |   42.85 |   71.65 | ...85-141,156-162 
  disable.ts       |     100 |      100 |     100 |     100 |                   
  enable.ts        |     100 |      100 |     100 |     100 |                   
  install.ts       |    75.6 |    66.66 |   66.66 |    75.6 | ...39-142,145-153 
  link.ts          |     100 |      100 |     100 |     100 |                   
  list.ts          |     100 |      100 |     100 |     100 |                   
  new.ts           |     100 |      100 |     100 |     100 |                   
  settings.ts      |   99.15 |      100 |   83.33 |   99.15 | 151               
  uninstall.ts     |    37.5 |      100 |   33.33 |    37.5 | 23-45,57-64,67-70 
  update.ts        |   96.32 |      100 |     100 |   96.32 | 101-105           
  utils.ts         |   60.24 |    28.57 |     100 |   60.24 | ...81,83-87,89-93 
 ...les/mcp-server |       0 |        0 |       0 |       0 |                   
  example.ts       |       0 |        0 |       0 |       0 | 1-60              
 src/commands/mcp  |   92.29 |    86.08 |   88.88 |   92.29 |                   
  add.ts           |     100 |    98.03 |     100 |     100 | 293               
  list.ts          |   91.22 |    80.76 |      80 |   91.22 | ...19-121,146-147 
  reconnect.ts     |   76.72 |    71.42 |   85.71 |   76.72 | 35-48,153-175     
  remove.ts        |     100 |       80 |     100 |     100 | 21-25             
 ...ommands/review |   11.57 |      100 |       0 |   11.57 |                   
  cleanup.ts       |   17.94 |      100 |       0 |   17.94 | ...01-106,108-109 
  deterministic.ts |   13.75 |      100 |       0 |   13.75 | ...22-738,740-741 
  fetch-pr.ts      |   11.36 |      100 |       0 |   11.36 | ...80-201,203-204 
  load-rules.ts    |   11.32 |      100 |       0 |   11.32 | ...41-153,155-156 
  pr-context.ts    |    6.22 |      100 |       0 |    6.22 | ...97-312,314-315 
  presubmit.ts     |    9.35 |      100 |       0 |    9.35 | ...62-287,289-290 
 ...nds/review/lib |      30 |      100 |       0 |      30 |                   
  gh.ts            |   22.58 |      100 |       0 |   22.58 | ...49,53-54,62-69 
  git.ts           |   22.72 |      100 |       0 |   22.72 | 15-18,29-39,43-44 
  paths.ts         |   52.94 |      100 |       0 |   52.94 | ...26,37-38,42-43 
 src/config        |   92.72 |    85.31 |   85.71 |   92.72 |                   
  auth.ts          |   86.98 |    80.32 |     100 |   86.98 | ...26-227,243-244 
  config.ts        |    88.3 |    85.52 |      76 |    88.3 | ...1713,1737-1738 
  keyBindings.ts   |   96.11 |       50 |     100 |   96.11 | 169-172           
  ...idersScope.ts |      92 |       90 |     100 |      92 | 11-12             
  sandboxConfig.ts |    58.9 |    61.53 |   66.66 |    58.9 | ...54-68,73,77-89 
  settings.ts      |   85.51 |    87.19 |   86.48 |   85.51 | ...1148,1153-1156 
  ...ingsSchema.ts |     100 |      100 |     100 |     100 |                   
  ...tedFolders.ts |   96.22 |       94 |     100 |   96.22 | ...88-190,205-206 
 ...nfig/migration |   94.89 |    78.94 |   83.33 |   94.89 |                   
  index.ts         |   94.87 |    88.88 |     100 |   94.87 | 91-92             
  scheduler.ts     |   96.55 |    77.77 |     100 |   96.55 | 19-20             
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 ...ation/versions |   94.74 |       96 |     100 |   94.74 |                   
  ...-v2-shared.ts |     100 |      100 |     100 |     100 |                   
  v1-to-v2.ts      |   81.75 |    90.19 |     100 |   81.75 | ...28-229,231-247 
  v2-to-v3.ts      |     100 |      100 |     100 |     100 |                   
  v3-to-v4.ts      |     100 |      100 |     100 |     100 |                   
 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    |   63.09 |    64.51 |   55.55 |   63.09 |                   
  ...tputBridge.ts |   62.94 |    65.51 |   56.25 |   62.94 | ...22-323,331-334 
  ...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/i18n          |   84.88 |    78.88 |   66.66 |   84.88 |                   
  index.ts         |   70.44 |       74 |   53.84 |   70.44 | ...71-272,282-287 
  languages.ts     |   95.33 |    86.48 |     100 |   95.33 | ...67,195-198,213 
  ...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 |   72.75 |    72.14 |   74.07 |   72.75 |                   
  session.ts       |   76.94 |    70.45 |   85.71 |   76.94 | ...80-781,790-800 
  types.ts         |    42.5 |      100 |   33.33 |    42.5 | ...80-581,584-585 
 ...active/control |   77.04 |    88.23 |      80 |   77.04 |                   
  ...rolContext.ts |    7.14 |        0 |       0 |    7.14 | 49-84             
  ...Dispatcher.ts |   91.66 |    91.83 |   88.88 |   91.66 | ...54-372,388,391 
  ...rolService.ts |       8 |        0 |       0 |       8 | 46-179            
 ...ol/controllers |    7.04 |       80 |   13.33 |    7.04 |                   
  ...Controller.ts |   19.32 |      100 |      60 |   19.32 | 81-118,127-210    
  ...Controller.ts |       0 |        0 |       0 |       0 | 1-56              
  ...Controller.ts |    3.96 |      100 |   11.11 |    3.96 | ...61-379,389-494 
  ...Controller.ts |   14.06 |      100 |       0 |   14.06 | ...82-117,130-133 
  ...Controller.ts |     5.2 |      100 |       0 |     5.2 | ...21-433,442-472 
 .../control/types |       0 |        0 |       0 |       0 |                   
  serviceAPIs.ts   |       0 |        0 |       0 |       0 | 1                 
 ...Interactive/io |   97.98 |    93.72 |   95.18 |   97.98 |                   
  ...putAdapter.ts |   97.89 |    92.82 |   98.07 |   97.89 | ...1303,1398-1399 
  ...putAdapter.ts |      96 |    91.66 |   85.71 |      96 | 51-52             
  ...nputReader.ts |     100 |    94.73 |     100 |     100 | 67                
  ...putAdapter.ts |   98.28 |      100 |      90 |   98.28 | 81-82,122-123     
  index.ts         |     100 |      100 |     100 |     100 |                   
 src/patches       |       0 |        0 |       0 |       0 |                   
  is-in-ci.ts      |       0 |        0 |       0 |       0 | 1-17              
 src/remoteInput   |   86.98 |       75 |   85.71 |   86.98 |                   
  ...utContext.tsx |     100 |      100 |     100 |     100 |                   
  ...putWatcher.ts |   88.12 |    76.08 |   91.66 |   88.12 | ...21-222,233-236 
  index.ts         |       0 |        0 |       0 |       0 | 1-8               
 src/services      |   92.84 |     90.9 |   98.36 |   92.84 |                   
  ...mandLoader.ts |     100 |     92.3 |     100 |     100 | 91                
  ...killLoader.ts |     100 |    96.15 |     100 |     100 | 45                
  ...andService.ts |   98.75 |      100 |     100 |   98.75 | 111               
  ...ionService.ts |   97.19 |    89.77 |     100 |   97.19 | ...85,423-424,428 
  ...mandLoader.ts |   86.83 |    83.87 |     100 |   86.83 | ...30-335,340-345 
  ...omptLoader.ts |   76.05 |    80.64 |   83.33 |   76.05 | ...12-213,279-280 
  ...mandLoader.ts |     100 |      100 |     100 |     100 |                   
  ...nd-factory.ts |    91.5 |    91.66 |     100 |    91.5 | 129,138-145       
  ...ation-tool.ts |     100 |    95.45 |     100 |     100 | 125               
  ...ndMetadata.ts |   98.21 |    96.66 |     100 |   98.21 | 83,87             
  commandUtils.ts  |      96 |    91.66 |     100 |      96 | 48                
  ...and-parser.ts |   90.69 |    85.71 |     100 |   90.69 | 63-66             
  ...ionService.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...ght/generators |    85.9 |    85.61 |   90.47 |    85.9 |                   
  DataProcessor.ts |   85.63 |     85.6 |   92.85 |   85.63 | ...1122,1126-1133 
  ...tGenerator.ts |   98.21 |    85.71 |     100 |   98.21 | 46                
  ...teRenderer.ts |   45.45 |      100 |       0 |   45.45 | 13-51             
 .../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.35 |    83.07 |     100 |   97.35 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  tipHistory.ts    |   92.45 |       70 |     100 |   92.45 | ...22,144,151,160 
  tipRegistry.ts   |     100 |    95.23 |     100 |     100 | 33                
  tipScheduler.ts  |     100 |    91.66 |     100 |     100 | 55                
 src/test-utils    |   93.75 |    83.33 |      80 |   93.75 |                   
  ...omMatchers.ts |   69.69 |       50 |      50 |   69.69 | 32-35,37-39,45-47 
  ...andContext.ts |     100 |      100 |     100 |     100 |                   
  render.tsx       |     100 |      100 |     100 |     100 |                   
 src/ui            |   65.79 |    69.34 |   54.76 |   65.79 |                   
  App.tsx          |     100 |      100 |     100 |     100 |                   
  AppContainer.tsx |   68.63 |     64.7 |      75 |   68.63 | ...2589,2593-2597 
  ...tionNudge.tsx |    9.58 |      100 |       0 |    9.58 | 24-94             
  ...ackDialog.tsx |   29.23 |      100 |       0 |   29.23 | 25-75             
  ...tionNudge.tsx |    7.69 |      100 |       0 |    7.69 | 25-103            
  colors.ts        |   52.72 |      100 |   23.52 |   52.72 | ...52,54-55,60-61 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  keyMatchers.ts   |   95.91 |    96.42 |     100 |   95.91 | 25-26             
  ...tic-colors.ts |     100 |      100 |     100 |     100 |                   
  textConstants.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/ui/auth       |   48.01 |    58.73 |   21.42 |   48.01 |                   
  AuthDialog.tsx   |   64.26 |    44.44 |   16.66 |   64.26 | ...59,366-388,392 
  ...nProgress.tsx |       0 |        0 |       0 |       0 | 1-64              
  ...etupSteps.tsx |    9.61 |      100 |       0 |    9.61 | ...35-352,391-476 
  useAuth.ts       |   76.63 |    68.29 |     100 |   76.63 | ...48,493-499,560 
  ...rSetupFlow.ts |   44.61 |    33.33 |      50 |   44.61 | ...57-378,395-438 
 src/ui/commands   |   70.19 |    79.57 |   80.45 |   70.19 |                   
  aboutCommand.ts  |     100 |    85.71 |     100 |     100 | 36                
  agentsCommand.ts |   83.78 |      100 |      60 |   83.78 | 30-32,42-44       
  ...odeCommand.ts |     100 |      100 |     100 |     100 |                   
  arenaCommand.ts  |   62.81 |    58.73 |   65.21 |   62.81 | ...91-596,681-689 
  authCommand.ts   |     100 |      100 |     100 |     100 |                   
  branchCommand.ts |     100 |      100 |     100 |     100 |                   
  btwCommand.ts    |   95.59 |    71.42 |     100 |   95.59 | 72,154-159        
  bugCommand.ts    |   81.13 |    71.42 |     100 |   81.13 | 60-69             
  clearCommand.ts  |   92.94 |       75 |     100 |   92.94 | 45-46,74-75,93-94 
  ...essCommand.ts |    64.7 |       50 |      75 |    64.7 | ...48-149,163-166 
  ...extCommand.ts |   34.78 |    22.22 |   45.45 |   34.78 | ...86-521,532-533 
  copyCommand.ts   |   98.28 |    94.89 |     100 |   98.28 | ...80,280,321,327 
  deleteCommand.ts |     100 |      100 |     100 |     100 |                   
  diffCommand.ts   |   99.02 |    86.11 |     100 |   99.02 | 222,226           
  ...ryCommand.tsx |   68.09 |    77.77 |   77.77 |   68.09 | ...56-261,315-323 
  docsCommand.ts   |     100 |    88.88 |     100 |     100 | 25                
  doctorCommand.ts |     100 |    93.33 |     100 |     100 | 21                
  dreamCommand.ts  |      75 |    66.66 |   66.66 |      75 | 22-27,44-47       
  editorCommand.ts |     100 |      100 |     100 |     100 |                   
  exportCommand.ts |      60 |    92.85 |   77.77 |      60 | 176-317           
  ...onsCommand.ts |   48.66 |     90.9 |   63.63 |   48.66 | ...05-109,159-211 
  forgetCommand.ts |   26.82 |      100 |      50 |   26.82 | 18-51             
  helpCommand.ts   |     100 |      100 |     100 |     100 |                   
  hooksCommand.ts  |    20.4 |       40 |      40 |    20.4 | ...48-180,204-205 
  ideCommand.ts    |   60.75 |    64.28 |   41.17 |   60.75 | ...05-306,310-324 
  initCommand.ts   |   84.33 |    72.72 |     100 |   84.33 | 68,82-87,89-94    
  ...ghtCommand.ts |   74.56 |    68.42 |     100 |   74.56 | ...31-245,250-273 
  ...ageCommand.ts |   85.76 |    82.82 |     100 |   85.76 | ...51-658,687-694 
  ...elsCommand.ts |     100 |      100 |     100 |     100 |                   
  mcpCommand.ts    |     100 |      100 |     100 |     100 |                   
  memoryCommand.ts |     100 |      100 |     100 |     100 |                   
  modelCommand.ts  |   74.56 |    79.06 |   71.42 |   74.56 | ...91-200,223-228 
  ...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             
  ...berCommand.ts |   32.43 |      100 |      50 |   32.43 | 23-57             
  renameCommand.ts |   85.29 |    77.77 |     100 |   85.29 | ...06-313,320-325 
  ...oreCommand.ts |    92.3 |    87.87 |     100 |    92.3 | ...,83-88,129-130 
  resumeCommand.ts |     100 |      100 |     100 |     100 |                   
  rewindCommand.ts |      80 |      100 |      50 |      80 | 19-21             
  ...ngsCommand.ts |     100 |      100 |     100 |     100 |                   
  ...hubCommand.ts |   81.43 |    65.21 |      80 |   81.43 | ...70-173,176-179 
  skillsCommand.ts |   15.04 |      100 |      25 |   15.04 | ...90-106,109-136 
  statsCommand.ts  |   88.19 |    84.21 |     100 |   88.19 | ...,58-61,143-146 
  ...ineCommand.ts |     100 |      100 |     100 |     100 |                   
  ...aryCommand.ts |    6.46 |      100 |      50 |    6.46 | 31-329            
  tasksCommand.ts  |   77.45 |    73.43 |     100 |   77.45 | ...55-159,181-186 
  ...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 |                   
  vimCommand.ts    |   54.54 |      100 |      50 |   54.54 | 19-29             
 src/ui/components |   61.48 |    75.66 |   66.66 |   61.48 |                   
  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    |   89.39 |       75 |     100 |   89.39 | 35,37-42,44       
  ...odeDialog.tsx |     9.7 |      100 |       0 |     9.7 | 35-47,50-182      
  AsciiArt.ts      |     100 |      100 |     100 |     100 |                   
  ...Indicator.tsx |   14.63 |      100 |       0 |   14.63 | 18-56             
  ...TextInput.tsx |   77.01 |       76 |     100 |   77.01 | ...20,234-236,263 
  Composer.tsx     |    80.8 |     64.7 |     100 |    80.8 | ...85,103,154,167 
  ...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 
  ...ification.tsx |   28.57 |      100 |       0 |   28.57 | 16-36             
  ...gProfiler.tsx |       0 |        0 |       0 |       0 | 1-36              
  ...ogManager.tsx |   12.53 |      100 |       0 |   12.53 | 63-470            
  ...ngsDialog.tsx |    8.44 |      100 |       0 |    8.44 | 37-195            
  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       |   79.67 |    58.06 |     100 |   79.67 | ...98-102,104-108 
  ...ngSpinner.tsx |   68.42 |       80 |      50 |   68.42 | 35-52,73,80-81    
  Header.tsx       |   98.62 |    94.28 |     100 |   98.62 | 162,164           
  Help.tsx         |   98.32 |    89.88 |     100 |   98.32 | ...24,381,447-448 
  ...emDisplay.tsx |   63.27 |    36.73 |     100 |   63.27 | ...29-338,341,344 
  ...ngeDialog.tsx |     100 |      100 |     100 |     100 |                   
  InputPrompt.tsx  |   82.25 |    77.43 |   83.33 |   82.25 | ...1347,1412,1462 
  ...Shortcuts.tsx |   20.87 |      100 |       0 |   20.87 | ...6,49-51,67-125 
  ...Indicator.tsx |     100 |    91.42 |     100 |     100 | 65,74             
  ...firmation.tsx |   91.42 |      100 |      50 |   91.42 | 26-31             
  MainContent.tsx  |   81.75 |       75 |     100 |   81.75 | ...70-274,282-286 
  ...elsDialog.tsx |   16.07 |    89.18 |      50 |   16.07 | ...58-159,162-648 
  MemoryDialog.tsx |   53.21 |    51.21 |   57.14 |   53.21 | ...54,366,379-381 
  ...geDisplay.tsx |       0 |        0 |       0 |       0 | 1-41              
  ModelDialog.tsx  |   76.31 |    54.94 |     100 |   76.31 | ...05-521,578-582 
  ...tsDisplay.tsx |     100 |    97.22 |     100 |     100 | 270               
  ...fications.tsx |   18.18 |      100 |       0 |   18.18 | 15-58             
  ...onsDialog.tsx |    2.13 |      100 |       0 |    2.13 | 62-133,148-1004   
  ...ryDisplay.tsx |     100 |      100 |     100 |     100 |                   
  ...icePrompt.tsx |   88.14 |    83.87 |     100 |   88.14 | ...01-105,133-138 
  PrepareLabel.tsx |   91.66 |    77.27 |     100 |   91.66 | 73-75,77-79,110   
  ...atePrompt.tsx |    8.57 |      100 |       0 |    8.57 | 24-55,58-134      
  ...geDisplay.tsx |     100 |      100 |     100 |     100 |                   
  ...ngDisplay.tsx |   21.42 |      100 |       0 |   21.42 | 13-39             
  ...hProgress.tsx |   85.25 |    88.46 |     100 |   85.25 | 121-147           
  ...dSelector.tsx |    4.45 |      100 |       0 |    4.45 | 28-92,100-328     
  ...ionPicker.tsx |      86 |    81.25 |     100 |      86 | ...98-310,344-346 
  ...onPreview.tsx |   92.42 |    84.37 |     100 |   92.42 | ...,70-71,143-145 
  ...ryDisplay.tsx |     100 |      100 |     100 |     100 |                   
  ...putPrompt.tsx |   72.56 |       80 |      40 |   72.56 | ...06-109,114-117 
  ...ngsDialog.tsx |    69.1 |    73.65 |     100 |    69.1 | ...11-819,825-826 
  ...ionDialog.tsx |    87.8 |      100 |   33.33 |    87.8 | 36-39,44-51       
  ...putPrompt.tsx |    15.9 |      100 |       0 |    15.9 | 20-63             
  ...Indicator.tsx |   57.14 |      100 |       0 |   57.14 | 12-15             
  ...MoreLines.tsx |      28 |      100 |       0 |      28 | 18-40             
  ...ionPicker.tsx |   17.59 |      100 |       0 |   17.59 | 55-172            
  StatsDisplay.tsx |     100 |      100 |     100 |     100 |                   
  ...yTodoList.tsx |   94.17 |       80 |     100 |   94.17 | 56-57,131-134     
  ...nsDisplay.tsx |   87.25 |       64 |     100 |   87.25 | ...45-147,154-156 
  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 |    81.81 |     100 |     100 | 71-86             
  ...ification.tsx |   36.36 |      100 |       0 |   36.36 | 15-22             
  ...ackDialog.tsx |    7.84 |      100 |       0 |    7.84 | 24-134            
 ...nts/agent-view |    25.2 |       90 |      10 |    25.2 |                   
  ...atContent.tsx |    8.79 |      100 |       0 |    8.79 | 53-265,271-273    
  ...tChatView.tsx |   21.05 |      100 |       0 |   21.05 | 21-39             
  ...tComposer.tsx |    9.95 |      100 |       0 |    9.95 | 57-308            
  AgentFooter.tsx  |   17.07 |      100 |       0 |   17.07 | 28-66             
  AgentHeader.tsx  |   15.38 |      100 |       0 |   15.38 | 27-64             
  AgentTabBar.tsx  |    8.13 |      100 |       0 |    8.13 | 39-59,64-187      
  ...oryAdapter.ts |     100 |    91.83 |     100 |     100 | 103,109-110,138   
  index.ts         |       0 |        0 |       0 |       0 | 1-12              
 ...mponents/arena |   45.72 |    70.53 |   60.86 |   45.72 |                   
  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 |   10.15 |      100 |       0 |   10.15 | 27-161            
  ...tusDialog.tsx |    5.63 |      100 |       0 |    5.63 | 33-75,80-288      
  ...topDialog.tsx |    6.17 |      100 |       0 |    6.17 | 33-213            
 ...ackground-view |   75.44 |     83.6 |   85.29 |   75.44 |                   
  ...sksDialog.tsx |   70.05 |       79 |   76.19 |   70.05 | ...1119,1195-1197 
  ...TasksPill.tsx |   70.83 |    86.95 |     100 |   70.83 | 44,84-96,104-112  
  ...gentPanel.tsx |   99.52 |    93.18 |     100 |   99.52 | 123               
 ...nts/extensions |   45.28 |    33.33 |      60 |   45.28 |                   
  ...gerDialog.tsx |   44.31 |    34.14 |      75 |   44.31 | ...71-480,483-488 
  index.ts         |       0 |        0 |       0 |       0 | 1-9               
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...tensions/steps |   54.77 |    94.23 |   66.66 |   54.77 |                   
  ...ctionStep.tsx |   95.12 |    92.85 |   85.71 |   95.12 | 84-86,89          
  ...etailStep.tsx |    6.18 |      100 |       0 |    6.18 | 17-128            
  ...nListStep.tsx |   88.35 |    94.73 |      80 |   88.35 | 51-52,58-71,105   
  ...electStep.tsx |   13.46 |      100 |       0 |   13.46 | 20-70             
  ...nfirmStep.tsx |   19.56 |      100 |       0 |   19.56 | 23-65             
  index.ts         |     100 |      100 |     100 |     100 |                   
 ...mponents/hooks |   72.24 |    70.52 |      80 |   72.24 |                   
  ...etailStep.tsx |   96.52 |       75 |     100 |   96.52 | 33,37,50,59       
  ...etailStep.tsx |   93.27 |    73.68 |     100 |   93.27 | 41-42,99-104,110  
  ...abledStep.tsx |     100 |      100 |     100 |     100 |                   
  ...sListStep.tsx |     100 |      100 |     100 |     100 |                   
  ...entDialog.tsx |   36.09 |    47.05 |      50 |   36.09 | ...49,453-466,470 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-13              
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...components/mcp |   20.83 |    83.72 |   83.33 |   20.83 |                   
  ...ealthPill.tsx |   68.42 |    85.71 |     100 |   68.42 | 40-46             
  ...entDialog.tsx |    3.64 |      100 |       0 |    3.64 | 41-717            
  constants.ts     |     100 |      100 |     100 |     100 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-30              
  types.ts         |     100 |      100 |     100 |     100 |                   
  utils.ts         |   94.79 |    85.71 |     100 |   94.79 | 16,20,35,109-110  
 ...ents/mcp/steps |    6.65 |      100 |       0 |    6.65 |                   
  ...icateStep.tsx |     5.1 |      100 |       0 |     5.1 | 34-95,98-334      
  ...electStep.tsx |   10.95 |      100 |       0 |   10.95 | 16-88             
  ...etailStep.tsx |    5.26 |      100 |       0 |    5.26 | 31-247            
  ...rListStep.tsx |    5.88 |      100 |       0 |    5.88 | 20-176            
  ...etailStep.tsx |   10.41 |      100 |       0 |   10.41 | ...1,67-79,82-139 
  ToolListStep.tsx |    7.14 |      100 |       0 |    7.14 | 16-146            
 ...nents/messages |   82.15 |    80.23 |   72.85 |   82.15 |                   
  ...ionDialog.tsx |   77.35 |    74.54 |    62.5 |   77.35 | ...90,508,526-528 
  BtwMessage.tsx   |     100 |      100 |     100 |     100 |                   
  ...upDisplay.tsx |   97.67 |    83.72 |     100 |   97.67 | 119,142,150       
  ...onMessage.tsx |   91.93 |    82.35 |     100 |   91.93 | 57-59,61,63       
  ...nMessages.tsx |   79.06 |      100 |      70 |   79.06 | ...51-264,268-280 
  DiffRenderer.tsx |   93.19 |    86.17 |     100 |   93.19 | ...09,237-238,304 
  ...tsDisplay.tsx |   97.82 |    77.27 |     100 |   97.82 | 87,89             
  ...ssMessage.tsx |    12.5 |      100 |       0 |    12.5 | 18-59             
  ...edMessage.tsx |   16.66 |      100 |       0 |   16.66 | 22-38             
  ...sMessages.tsx |   55.67 |       40 |   28.57 |   55.67 | ...20-125,133-145 
  ...ryMessage.tsx |   14.28 |      100 |       0 |   14.28 | 23-62             
  ...onMessage.tsx |   81.02 |    69.23 |   33.33 |   81.02 | ...24-426,433-435 
  ...upMessage.tsx |      84 |    93.61 |     100 |      84 | ...56-383,405-420 
  ToolMessage.tsx  |   88.84 |    75.71 |    92.3 |   88.84 | ...44-749,776-778 
 ...ponents/shared |   82.37 |    77.36 |   92.75 |   82.37 |                   
  ...ctionList.tsx |   99.03 |    95.65 |     100 |   99.03 | 85                
  ...tonSelect.tsx |     100 |      100 |     100 |     100 |                   
  EnumSelector.tsx |     100 |    96.42 |     100 |     100 | 58                
  MaxSizedBox.tsx  |   83.01 |    86.25 |   88.88 |   83.01 | ...12-513,618-619 
  MultiSelect.tsx  |    6.29 |      100 |       0 |    6.29 | 35-42,45-176      
  ...tonSelect.tsx |     100 |      100 |     100 |     100 |                   
  ...eSelector.tsx |     100 |       60 |     100 |     100 | 40-45             
  TextInput.tsx    |   72.98 |    55.55 |      80 |   72.98 | ...08-212,224-230 
  ...apsedTime.tsx |     100 |      100 |     100 |     100 |                   
  ...Indicator.tsx |     100 |      100 |     100 |     100 |                   
  text-buffer.ts   |   83.62 |    75.62 |   97.61 |   83.62 | ...2272,2300,2368 
  ...er-actions.ts |   86.71 |    67.79 |     100 |   86.71 | ...07-608,809-811 
 ...ents/subagents |   30.87 |        0 |       0 |   30.87 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-11              
  reducers.tsx     |    12.1 |      100 |       0 |    12.1 | 33-190            
  types.ts         |     100 |      100 |     100 |     100 |                   
  utils.ts         |   10.95 |      100 |       0 |   10.95 | ...1,56-57,60-102 
 ...bagents/create |    9.13 |      100 |       0 |    9.13 |                   
  ...ionWizard.tsx |    7.28 |      100 |       0 |    7.28 | 34-299            
  ...rSelector.tsx |   14.75 |      100 |       0 |   14.75 | 26-85             
  ...onSummary.tsx |    4.26 |      100 |       0 |    4.26 | 27-331            
  ...tionInput.tsx |    8.63 |      100 |       0 |    8.63 | 23-177            
  ...dSelector.tsx |   33.33 |      100 |       0 |   33.33 | 20-21,26-27,36-63 
  ...nSelector.tsx |    37.5 |      100 |       0 |    37.5 | 20-21,26-27,36-58 
  ...EntryStep.tsx |   12.76 |      100 |       0 |   12.76 | 34-78             
  ToolSelector.tsx |    4.16 |      100 |       0 |    4.16 | 31-253            
 ...bagents/manage |    8.39 |      100 |       0 |    8.39 |                   
  ...ctionStep.tsx |   10.25 |      100 |       0 |   10.25 | 21-103            
  ...eleteStep.tsx |   20.93 |      100 |       0 |   20.93 | 23-62             
  ...tEditStep.tsx |   25.53 |      100 |       0 |   25.53 | ...2,37-38,51-124 
  ...ctionStep.tsx |    2.29 |      100 |       0 |    2.29 | 28-449            
  ...iewerStep.tsx |   13.72 |      100 |       0 |   13.72 | 18-73             
  ...gerDialog.tsx |    6.74 |      100 |       0 |    6.74 | 35-341            
 ...mponents/views |   42.16 |    69.23 |   21.42 |   42.16 |                   
  ContextUsage.tsx |     4.7 |      100 |       0 |     4.7 | ...52-167,170-456 
  DoctorReport.tsx |     9.8 |      100 |       0 |     9.8 | 25-54,57-131      
  ...sionsList.tsx |   87.69 |    73.68 |     100 |   87.69 | 65-72             
  McpStatus.tsx    |   89.53 |    60.52 |     100 |   89.53 | ...72,175-177,262 
  SkillsList.tsx   |   27.27 |      100 |       0 |   27.27 | 18-35             
  ToolsList.tsx    |     100 |      100 |     100 |     100 |                   
 src/ui/contexts   |   77.05 |    78.24 |   82.14 |   77.05 |                   
  ...ewContext.tsx |   65.77 |      100 |      75 |   65.77 | ...22-225,231-241 
  AppContext.tsx   |      80 |       50 |     100 |      80 | 19-20             
  ...ewContext.tsx |   93.37 |    68.57 |      50 |   93.37 | ...94-195,222-226 
  ...deContext.tsx |     100 |      100 |     100 |     100 |                   
  ...igContext.tsx |   81.81 |       50 |     100 |   81.81 | 15-16             
  ...ssContext.tsx |   81.88 |    82.26 |     100 |   81.88 | ...1153,1159-1161 
  ...owContext.tsx |   89.28 |       80 |   66.66 |   89.28 | 34,47-48,60-62    
  ...deContext.tsx |     100 |      100 |      50 |     100 |                   
  ...onContext.tsx |   43.28 |     62.5 |    62.5 |   43.28 | ...56-259,263-266 
  ...gsContext.tsx |   83.33 |       50 |     100 |   83.33 | 17-18             
  ...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             
  ...nsContext.tsx |   88.23 |       50 |     100 |   88.23 | 108-109           
  ...teContext.tsx |   86.66 |       50 |     100 |   86.66 | 173-174           
  ...deContext.tsx |   76.08 |    72.72 |     100 |   76.08 | 47-48,52-59,77-78 
 src/ui/editors    |   93.33 |    85.71 |   66.66 |   93.33 |                   
  ...ngsManager.ts |   93.33 |    85.71 |   66.66 |   93.33 | 49,63-64          
 src/ui/hooks      |   81.45 |    81.25 |   86.47 |   81.45 |                   
  ...dProcessor.ts |   83.12 |    82.56 |     100 |   83.12 | ...88-389,408-435 
  keyToAnsi.ts     |    3.92 |      100 |       0 |    3.92 | 19-77             
  ...dProcessor.ts |    94.8 |    70.58 |     100 |    94.8 | ...76-277,282-283 
  ...dProcessor.ts |    75.9 |    63.44 |   61.53 |    75.9 | ...84,908,927-931 
  ...amingState.ts |   12.22 |      100 |       0 |   12.22 | 54-158            
  ...agerDialog.ts |   88.23 |      100 |     100 |   88.23 | 20,24             
  ...ationFrame.ts |      32 |       60 |     100 |      32 | 42-44,51-90       
  ...odeCommand.ts |   58.82 |      100 |     100 |   58.82 | 28,33-48          
  ...enaCommand.ts |      85 |      100 |     100 |      85 | 23-24,29          
  ...aInProcess.ts |   19.81 |    66.66 |      25 |   19.81 | 57-175            
  ...Completion.ts |   92.77 |    89.09 |     100 |   92.77 | ...86-187,220-223 
  ...ifications.ts |   92.07 |    96.29 |     100 |   92.07 | 116-124           
  ...tIndicator.ts |     100 |    93.75 |     100 |     100 | 63                
  ...waySummary.ts |   96.22 |    69.69 |     100 |   96.22 | 125-127,169       
  ...ndTaskView.ts |   94.11 |    76.92 |     100 |   94.11 | 119-123,216,222   
  ...ketedPaste.ts |    23.8 |      100 |       0 |    23.8 | 19-37             
  ...nchCommand.ts |   93.75 |    73.17 |     100 |   93.75 | ...68-169,221-222 
  ...ompletion.tsx |   95.95 |    82.75 |     100 |   95.95 | ...22-223,225-226 
  ...dMigration.ts |   90.62 |       75 |     100 |   90.62 | 38-40             
  useCompletion.ts |    92.4 |     87.5 |     100 |    92.4 | 68-69,93-94,98-99 
  ...nitMessage.ts |     100 |      100 |     100 |     100 |                   
  ...extualTips.ts |   76.92 |       50 |     100 |   76.92 | 55,68,71-75,88-96 
  ...eteCommand.ts |   33.33 |       50 |     100 |   33.33 | 30,34,41-90       
  ...ialogClose.ts |   16.66 |      100 |     100 |   16.66 | 79-139            
  ...oublePress.ts |   53.12 |       75 |     100 |   53.12 | 33-35,41-54       
  ...orSettings.ts |     100 |      100 |     100 |     100 |                   
  ...Completion.ts |   99.12 |     97.7 |     100 |   99.12 | 182-183           
  ...ionUpdates.ts |   93.45 |     92.3 |     100 |   93.45 | ...83-287,300-306 
  ...agerDialog.ts |   88.88 |      100 |     100 |   88.88 | 21,25             
  ...backDialog.ts |   54.47 |       50 |   33.33 |   54.47 | ...69-171,193-194 
  useFocus.ts      |     100 |      100 |     100 |     100 |                   
  ...olderTrust.ts |     100 |      100 |     100 |     100 |                   
  ...ggestions.tsx |   89.15 |     62.5 |      50 |   89.15 | ...22-124,149-150 
  ...miniStream.ts |   76.64 |    73.93 |   91.66 |   76.64 | ...2425,2438-2446 
  ...BranchName.ts |    90.9 |     92.3 |     100 |    90.9 | 19-20,55-58       
  ...oryManager.ts |   93.15 |    93.75 |     100 |   93.15 | 44,107-110        
  ...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 |    9.67 |      100 |       0 |    9.67 | 11-32,39-90       
  ...gIndicator.ts |     100 |      100 |     100 |     100 |                   
  useLogger.ts     |   21.05 |      100 |       0 |   21.05 | 15-37             
  useMCPHealth.ts  |   63.15 |       75 |      50 |   63.15 | 42-52,64-67       
  ...elsCommand.ts |     100 |      100 |     100 |     100 |                   
  useMcpDialog.ts  |    87.5 |      100 |     100 |    87.5 | 19,23             
  ...moryDialog.ts |    87.5 |      100 |     100 |    87.5 | 19,23             
  ...oryMonitor.ts |     100 |      100 |     100 |     100 |                   
  ...ssageQueue.ts |     100 |      100 |     100 |     100 |                   
  ...delCommand.ts |     100 |       75 |     100 |     100 | 22                
  ...raseCycler.ts |   84.74 |    76.47 |     100 |   84.74 | ...49,52-53,69-71 
  ...derUpdates.ts |   86.38 |    77.19 |     100 |   86.38 | ...22,281-293,341 
  useQwenAuth.ts   |     100 |      100 |     100 |     100 |                   
  ...lScheduler.ts |    84.7 |    93.33 |     100 |    84.7 | ...71-276,372-382 
  ...oryCommand.ts |       0 |        0 |       0 |       0 | 1-7               
  ...umeCommand.ts |   97.24 |    76.92 |     100 |   97.24 | 104-105,145       
  ...ompletion.tsx |   90.59 |    83.33 |     100 |   90.59 | ...01,104,137-140 
  ...ectionList.ts |   96.96 |    95.69 |     100 |   96.96 | ...82-183,237-240 
  ...sionPicker.ts |   79.79 |    61.19 |     100 |   79.79 | ...02-404,413-415 
  ...earchInput.ts |     100 |      100 |     100 |     100 |                   
  ...ngsCommand.ts |   18.75 |      100 |       0 |   18.75 | 10-25             
  ...ellHistory.ts |   91.74 |    79.41 |     100 |   91.74 | ...74,122-123,133 
  ...oryCommand.ts |       0 |        0 |       0 |       0 | 1-73              
  ...Completion.ts |   82.67 |    85.41 |   94.73 |   82.67 | ...68-670,678-714 
  ...tateAndRef.ts |     100 |      100 |     100 |     100 |                   
  useStatusLine.ts |     100 |    98.79 |     100 |     100 | 257               
  ...eateDialog.ts |   88.23 |      100 |     100 |   88.23 | 14,18             
  ...tification.ts |     100 |    85.71 |     100 |     100 | 47                
  ...alProgress.ts |   53.06 |       50 |   66.66 |   53.06 | ...53,61-68,79-85 
  ...rminalSize.ts |   76.19 |      100 |      50 |   76.19 | 21-25             
  ...emeCommand.ts |   67.01 |    29.41 |     100 |   67.01 | ...10-111,115-116 
  useTimer.ts      |   88.09 |    85.71 |     100 |   88.09 | 44-45,51-53       
  ...lMigration.ts |       0 |        0 |       0 |       0 |                   
  ...rustModify.ts |     100 |      100 |     100 |     100 |                   
  ...elcomeBack.ts |   87.36 |     90.9 |     100 |   87.36 | ...,94-96,114-115 
  vim.ts           |   83.77 |    80.31 |     100 |   83.77 | ...55,759-767,776 
 src/ui/layouts    |   89.72 |     87.5 |     100 |   89.72 |                   
  ...AppLayout.tsx |   89.88 |     87.5 |     100 |   89.88 | 51-53,93-98       
  ...AppLayout.tsx |   89.47 |     87.5 |     100 |   89.47 | 58-63             
 ...i/manageModels |   93.61 |       48 |     100 |   93.61 |                   
  manageModels.ts  |   93.61 |       48 |     100 |   93.61 | ...63-166,179,209 
 src/ui/models     |   80.24 |    79.16 |   71.42 |   80.24 |                   
  ...ableModels.ts |   80.24 |    79.16 |   71.42 |   80.24 | ...,61-71,123-125 
 ...noninteractive |     100 |      100 |    7.14 |     100 |                   
  ...eractiveUi.ts |     100 |      100 |    7.14 |     100 |                   
 src/ui/state      |   94.91 |    81.81 |     100 |   94.91 |                   
  extensions.ts    |   94.91 |    81.81 |     100 |   94.91 | 68-69,88          
 src/ui/themes     |   98.53 |    70.58 |     100 |   98.53 |                   
  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   |     100 |      100 |     100 |     100 |                   
  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 |   87.98 |    82.89 |     100 |   87.98 | ...48-357,362-363 
  theme.ts         |     100 |    38.02 |     100 |     100 | ...34-449,457-461 
  xcode.ts         |     100 |      100 |     100 |     100 |                   
 src/ui/utils      |   83.16 |    82.21 |      92 |   83.16 |                   
  ...Colorizer.tsx |   82.78 |    88.23 |     100 |   82.78 | ...10-111,197-223 
  ...nRenderer.tsx |   57.89 |    55.31 |      50 |   57.89 | ...86-188,208-227 
  ...wnDisplay.tsx |   86.01 |    87.41 |     100 |   86.01 | ...87,704,729-754 
  ...idDiagram.tsx |   87.79 |    95.34 |     100 |   87.79 | 156-179           
  ...eRenderer.tsx |   93.18 |    81.43 |      95 |   93.18 | ...20-623,667-672 
  ...dWorkUtils.ts |     100 |      100 |     100 |     100 |                   
  ...boardUtils.ts |   59.61 |    58.82 |     100 |   59.61 | ...,86-88,107-149 
  commandUtils.ts  |    95.9 |    88.29 |     100 |    95.9 | ...62,164-165,289 
  computeStats.ts  |     100 |      100 |     100 |     100 |                   
  customBanner.ts  |   90.68 |    91.22 |     100 |   90.68 | ...13,324-327,334 
  displayUtils.ts  |   88.37 |    72.22 |     100 |   88.37 | 23,25,29,31,33    
  formatters.ts    |   95.23 |    98.27 |     100 |   95.23 | 117-120           
  gradientUtils.ts |     100 |      100 |     100 |     100 |                   
  highlight.ts     |     100 |      100 |     100 |     100 |                   
  ...oryMapping.ts |     100 |    94.28 |     100 |     100 | 29,51             
  historyUtils.ts  |   94.02 |    93.87 |     100 |   94.02 | 93-96             
  isNarrowWidth.ts |     100 |      100 |     100 |     100 |                   
  ...olDetector.ts |    8.23 |      100 |       0 |    8.23 | ...31-132,135-136 
  latexRenderer.ts |   94.95 |     73.8 |     100 |   94.95 | ...76-178,184-187 
  layoutUtils.ts   |     100 |      100 |     100 |     100 |                   
  ...nUtilities.ts |   69.84 |    85.71 |     100 |   69.84 | 75-91,100-101     
  ...ToolGroups.ts |   98.66 |    96.77 |     100 |   98.66 | 48-49             
  ...geRenderer.ts |   86.23 |    69.06 |   95.12 |   86.23 | ...1284,1324-1330 
  ...alRenderer.ts |   86.69 |     71.9 |     100 |   86.69 | ...1476,1513-1519 
  ...lsBySource.ts |     100 |    95.23 |     100 |     100 | 84                
  ...mConstants.ts |     100 |      100 |     100 |     100 |                   
  ...storyUtils.ts |   61.06 |    69.62 |      90 |   61.06 | ...64,412,417-439 
  ...ickerUtils.ts |     100 |      100 |     100 |     100 |                   
  ...izedOutput.ts |   94.94 |      100 |   88.88 |   94.94 | 112-117           
  ...wOptimizer.ts |     100 |    96.77 |     100 |     100 | 69                
  terminalSetup.ts |    4.37 |      100 |       0 |    4.37 | 44-393            
  textUtils.ts     |   97.35 |    94.38 |   91.66 |   97.35 | ...50-251,386-387 
  todoSnapshot.ts  |   89.11 |    93.33 |     100 |   89.11 | ...,66-78,180-181 
  updateCheck.ts   |     100 |    80.95 |     100 |     100 | 30-42             
 ...i/utils/export |   56.77 |     40.8 |   79.41 |   56.77 |                   
  collect.ts       |   55.92 |    50.58 |   86.36 |   55.92 | ...25-640,642-647 
  index.ts         |     100 |      100 |     100 |     100 |                   
  normalize.ts     |   57.47 |    20.51 |      80 |   57.47 | ...09-310,324-359 
  types.ts         |       0 |        0 |       0 |       0 | 1                 
  utils.ts         |      40 |      100 |       0 |      40 | 11-13             
 ...ort/formatters |    3.38 |      100 |       0 |    3.38 |                   
  html.ts          |    9.61 |      100 |       0 |    9.61 | ...28,34-76,82-84 
  json.ts          |      50 |      100 |       0 |      50 | 14-15             
  jsonl.ts         |     3.5 |      100 |       0 |     3.5 | 14-76             
  markdown.ts      |    0.94 |      100 |       0 |    0.94 | 13-295            
 src/utils         |   73.31 |       90 |   93.68 |   73.31 |                   
  acpModelUtils.ts |     100 |      100 |     100 |     100 |                   
  apiPreconnect.ts |   96.52 |    97.05 |     100 |   96.52 | 164-167           
  checks.ts        |   33.33 |      100 |       0 |   33.33 | 23-28             
  cleanup.ts       |   84.12 |    93.33 |      80 |   84.12 | 75,106-115        
  commands.ts      |     100 |      100 |     100 |     100 |                   
  commentJson.ts   |   87.17 |    90.47 |     100 |   87.17 | 64-73             
  ...Calculator.ts |     100 |      100 |     100 |     100 |                   
  deepMerge.ts     |     100 |       90 |     100 |     100 | 41-43,49          
  ...ScopeUtils.ts |   97.56 |    88.88 |     100 |   97.56 | 67                
  doctorChecks.ts  |   71.06 |       75 |     100 |   71.06 | ...95-301,325-341 
  ...putCapture.ts |   90.65 |    86.17 |     100 |   90.65 | ...72,370,372-373 
  ...arResolver.ts |   94.28 |       88 |     100 |   94.28 | 28-29,125-126     
  errors.ts        |   98.67 |    96.36 |     100 |   98.67 | 67-68             
  events.ts        |     100 |      100 |     100 |     100 |                   
  gitUtils.ts      |   91.91 |    84.61 |     100 |   91.91 | 78-81,124-127     
  ...AutoUpdate.ts |   90.76 |    93.33 |   88.88 |   90.76 | 103-114           
  ...lationInfo.ts |     100 |      100 |     100 |     100 |                   
  languageUtils.ts |   97.89 |    96.42 |     100 |   97.89 | 132-133           
  math.ts          |       0 |        0 |       0 |       0 | 1-15              
  ...onfigUtils.ts |     100 |      100 |     100 |     100 |                   
  ...iveHelpers.ts |   96.82 |    93.28 |     100 |   96.82 | ...84-485,583,596 
  osc.ts           |    97.5 |      100 |   88.88 |    97.5 | 195-196           
  package.ts       |   88.88 |       80 |     100 |   88.88 | 33-34             
  processUtils.ts  |     100 |      100 |     100 |     100 |                   
  readStdin.ts     |   79.62 |       90 |      80 |   79.62 | 33-40,52-54       
  relaunch.ts      |   98.07 |    76.92 |     100 |   98.07 | 70                
  resolvePath.ts   |   66.66 |       25 |     100 |   66.66 | 12-13,16,18-19    
  sandbox.ts       |       0 |        0 |       0 |       0 | 1-1047            
  settingsUtils.ts |   82.89 |    90.67 |   89.47 |   82.89 | ...52-663,670-678 
  spawnWrapper.ts  |     100 |      100 |     100 |     100 |                   
  ...upProfiler.ts |     100 |       96 |     100 |     100 | 110               
  ...upWarnings.ts |     100 |      100 |     100 |     100 |                   
  stdioHelpers.ts  |     100 |       60 |     100 |     100 | 23,32             
  systemInfo.ts    |   92.52 |     90.9 |   83.33 |   92.52 | 63-69,184         
  ...InfoFields.ts |    87.5 |     64.1 |     100 |    87.5 | ...21-122,143-144 
  ...iffPreview.ts |   94.11 |    83.33 |     100 |   94.11 | 13                
  ...entEmitter.ts |     100 |      100 |     100 |     100 |                   
  ...upWarnings.ts |   91.17 |    82.35 |     100 |   91.17 | 67-68,73-74,77-78 
  version.ts       |     100 |       50 |     100 |     100 | 11                
  windowTitle.ts   |     100 |      100 |     100 |     100 |                   
  ...WithBackup.ts |   63.15 |    81.25 |     100 |   63.15 | 93,118-157        
-------------------|---------|----------|---------|---------|-------------------
Core Package - Full Text Report
-------------------|---------|----------|---------|---------|-------------------
File               | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------------|---------|----------|---------|---------|-------------------
All files          |   78.26 |    82.62 |   80.84 |   78.26 |                   
 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        |   86.11 |    76.88 |   91.66 |   86.11 |                   
  ...transcript.ts |   88.92 |    76.66 |     100 |   88.92 | ...82,306-307,438 
  ...ent-resume.ts |   81.23 |    69.89 |   77.41 |   81.23 | ...1021,1024-1026 
  ...ound-tasks.ts |   95.13 |    86.61 |     100 |   95.13 | ...06-707,733-734 
  index.ts         |     100 |      100 |     100 |     100 |                   
 src/agents/arena  |   76.98 |    67.72 |   78.72 |   76.98 |                   
  ...gentClient.ts |   79.47 |    88.88 |   81.81 |   79.47 | ...68-183,189-204 
  ArenaManager.ts  |   75.92 |    64.19 |   78.26 |   75.92 | ...1860,1866-1867 
  arena-events.ts  |   64.44 |      100 |      50 |   64.44 | ...71-175,178-183 
  diff-summary.ts  |    87.5 |    73.46 |     100 |    87.5 | ...32-133,137-138 
  index.ts         |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...gents/backends |   76.29 |    86.15 |   73.04 |   76.29 |                   
  ITermBackend.ts  |   97.97 |    93.93 |     100 |   97.97 | ...78-180,255,307 
  ...essBackend.ts |   91.25 |    90.62 |   86.66 |   91.25 | ...94,249-269,328 
  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 |   81.13 |     76.7 |   71.42 |   81.13 |                   
  agent-context.ts |     100 |      100 |     100 |     100 |                   
  agent-core.ts    |   76.45 |    72.35 |   60.86 |   76.45 | ...1604,1631-1677 
  agent-events.ts  |     100 |      100 |     100 |     100 |                   
  ...t-headless.ts |   81.19 |    71.73 |   60.86 |   81.19 | ...98-399,402-403 
  ...nteractive.ts |   79.71 |    79.62 |      75 |   79.71 | ...54,456,458,461 
  ...statistics.ts |   98.19 |    82.35 |     100 |   98.19 | 127,151,192,225   
  agent-types.ts   |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
 src/config        |   75.73 |    78.23 |   62.08 |   75.73 |                   
  config.ts        |   73.51 |    75.93 |   56.93 |   73.51 | ...3084,3095-3107 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  models.ts        |     100 |      100 |     100 |     100 |                   
  storage.ts       |   95.07 |    93.44 |   89.47 |   95.07 | ...66-267,270-271 
 ...nfirmation-bus |   98.29 |    97.14 |     100 |   98.29 |                   
  message-bus.ts   |   98.14 |    97.05 |     100 |   98.14 | 42-43             
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/core          |   84.58 |    82.72 |   89.05 |   84.58 |                   
  baseLlmClient.ts |   91.63 |    84.37 |   84.61 |   91.63 | ...91,299-313,380 
  client.ts        |   78.38 |    77.21 |   85.18 |   78.38 | ...1504,1541-1544 
  ...tGenerator.ts |    72.1 |    61.11 |     100 |    72.1 | ...63,365,372-375 
  ...lScheduler.ts |   81.13 |    82.25 |   93.33 |   81.13 | ...2332,2384-2388 
  geminiChat.ts    |   88.81 |    84.36 |    87.5 |   88.81 | ...1304,1371-1372 
  geminiRequest.ts |     100 |      100 |     100 |     100 |                   
  ...htProtocol.ts |    9.09 |      100 |       0 |    9.09 | 34-42,45-49,52-87 
  logger.ts        |   87.33 |    87.02 |     100 |   87.33 | ...61-565,611-625 
  ...tyDefaults.ts |     100 |      100 |     100 |     100 |                   
  ...olExecutor.ts |   92.59 |       75 |      50 |   92.59 | 41-42             
  ...on-helpers.ts |   85.71 |    70.58 |     100 |   85.71 | ...90-191,205-214 
  ...issionFlow.ts |   98.59 |    94.73 |     100 |   98.59 | 93                
  prompts.ts       |   89.16 |    86.41 |   76.92 |   89.16 | ...-965,1168-1169 
  tokenLimits.ts   |     100 |    89.47 |     100 |     100 | 51-52             
  ...okTriggers.ts |   99.31 |    90.41 |     100 |   99.31 | 124,135           
  turn.ts          |   96.42 |    88.88 |     100 |   96.42 | ...00,413-414,462 
 ...ntentGenerator |   95.12 |    81.91 |   93.61 |   95.12 |                   
  ...tGenerator.ts |   97.13 |    83.58 |    92.3 |   97.13 | ...22,714,870,926 
  converter.ts     |   94.51 |    80.62 |     100 |   94.51 | ...06-607,617,816 
  index.ts         |       0 |        0 |       0 |       0 | 1-21              
 ...ntentGenerator |   91.53 |    71.64 |   93.33 |   91.53 |                   
  ...tGenerator.ts |      90 |    70.96 |   92.85 |      90 | ...80-286,304-305 
  index.ts         |     100 |       80 |     100 |     100 | 50                
 ...ntentGenerator |      94 |    84.23 |      90 |      94 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...tGenerator.ts |   93.98 |    84.23 |      90 |   93.98 | ...88,798-799,827 
 ...ntentGenerator |   79.71 |       84 |   89.47 |   79.71 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  converter.ts     |   74.92 |    80.85 |   86.95 |   74.92 | ...1410,1431-1437 
  errorHandler.ts  |     100 |      100 |     100 |     100 |                   
  index.ts         |   52.38 |    44.44 |      50 |   52.38 | ...77,81-85,89-93 
  ...tGenerator.ts |   48.78 |    91.66 |   77.77 |   48.78 | ...10-163,166-167 
  pipeline.ts      |   93.65 |     84.9 |     100 |   93.65 | ...79-480,488,553 
  ...ureContext.ts |     100 |      100 |     100 |     100 |                   
  ...ingOptions.ts |       0 |        0 |       0 |       0 | 1                 
  ...CallParser.ts |   90.66 |     88.4 |     100 |   90.66 | ...15-319,349-350 
  ...kingParser.ts |     100 |    96.87 |     100 |     100 | 42                
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 ...rator/provider |   96.56 |    88.46 |   95.45 |   96.56 |                   
  dashscope.ts     |   97.02 |    88.15 |   93.33 |   97.02 | ...37-238,314-315 
  deepseek.ts      |   95.55 |    90.56 |     100 |   95.55 | ...31-132,145-146 
  default.ts       |   94.62 |    86.36 |   85.71 |   94.62 | 85-86,156-158     
  index.ts         |     100 |      100 |     100 |     100 |                   
  minimax.ts       |     100 |      100 |     100 |     100 |                   
  mistral.ts       |   96.07 |    73.33 |     100 |   96.07 | 32-33             
  modelscope.ts    |     100 |      100 |     100 |     100 |                   
  openrouter.ts    |     100 |      100 |     100 |     100 |                   
  types.ts         |       0 |        0 |       0 |       0 |                   
 src/extension     |   60.56 |    79.46 |    78.4 |   60.56 |                   
  ...-converter.ts |   62.35 |    47.82 |      90 |   62.35 | ...90-791,800-832 
  ...ionManager.ts |   47.04 |    82.06 |    65.9 |   47.04 | ...1398,1408-1427 
  ...onSettings.ts |   93.46 |    93.05 |     100 |   93.46 | ...17-221,228-232 
  ...-converter.ts |   54.88 |    94.44 |      60 |   54.88 | ...35-146,158-192 
  github.ts        |   44.94 |    88.52 |      60 |   44.94 | ...53-359,398-451 
  index.ts         |     100 |      100 |     100 |     100 |                   
  marketplace.ts   |   97.29 |    93.75 |     100 |   97.29 | ...64,184-185,274 
  npm.ts           |   48.66 |    76.08 |      75 |   48.66 | ...18-420,427-431 
  override.ts      |   94.11 |    88.88 |     100 |   94.11 | 63-64,81-82       
  settings.ts      |   66.26 |      100 |      50 |   66.26 | 81-108,143-149    
  storage.ts       |     100 |      100 |     100 |     100 |                   
  ...ableSchema.ts |     100 |      100 |     100 |     100 |                   
  variables.ts     |   88.75 |    83.33 |     100 |   88.75 | ...28-231,234-237 
 src/followup      |   46.91 |     92.3 |   71.87 |   46.91 |                   
  followupState.ts |      96 |    89.74 |     100 |      96 | 159-161,218-219   
  index.ts         |     100 |      100 |     100 |     100 |                   
  overlayFs.ts     |   95.06 |       84 |     100 |   95.06 | 78,108,122,133    
  speculation.ts   |   13.22 |      100 |   16.66 |   13.22 | 88-458,518-568    
  ...onToolGate.ts |     100 |    96.29 |     100 |     100 | 93                
  ...nGenerator.ts |    38.4 |    95.12 |   33.33 |    38.4 | ...16-318,353-383 
 src/generated     |       0 |        0 |       0 |       0 |                   
  git-commit.ts    |       0 |        0 |       0 |       0 | 1-10              
 src/hooks         |   80.63 |    84.35 |   84.16 |   80.63 |                   
  ...okRegistry.ts |   86.48 |    77.08 |     100 |   86.48 | ...41-344,362-369 
  ...bortSignal.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.37 |    90.54 |     100 |   96.37 | ...89,291-292,365 
  ...entHandler.ts |   95.58 |    84.37 |   92.59 |   95.58 | ...29,682-683,693 
  hookPlanner.ts   |   84.13 |    76.59 |      90 |   84.13 | ...38,144,162-173 
  hookRegistry.ts  |   88.83 |    86.36 |     100 |   88.83 | ...21,326,330,334 
  hookRunner.ts    |   53.94 |     72.6 |   61.11 |   53.94 | ...27-728,737-738 
  hookSystem.ts    |   75.47 |      100 |   56.41 |   75.47 | ...75-576,582-583 
  ...HookRunner.ts |   75.51 |     61.9 |      80 |   75.51 | ...05-406,424-425 
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...SkillHooks.ts |   78.75 |       75 |   66.66 |   78.75 | 62-66,137-152     
  ...oksManager.ts |    96.5 |     91.8 |     100 |    96.5 | ...90,209-210,223 
  ssrfGuard.ts     |   77.22 |    85.36 |     100 |   77.22 | ...57,261-267,273 
  trustedHooks.ts  |       0 |        0 |       0 |       0 | 1-124             
  types.ts         |   90.18 |    90.78 |   85.18 |   90.18 | ...91-392,452-456 
  urlValidator.ts  |     100 |      100 |     100 |     100 |                   
 src/ide           |   74.28 |    83.39 |   78.33 |   74.28 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  detect-ide.ts    |     100 |      100 |     100 |     100 |                   
  ide-client.ts    |    64.2 |    81.48 |   66.66 |    64.2 | ...9-970,999-1007 
  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           |   33.92 |    45.16 |   45.76 |   33.92 |                   
  ...nfigLoader.ts |   70.27 |    35.89 |   94.73 |   70.27 | ...20-422,426-432 
  ...ionFactory.ts |    4.29 |      100 |       0 |    4.29 | ...20-371,377-394 
  ...Normalizer.ts |   23.09 |    13.72 |   30.43 |   23.09 | ...04-905,909-924 
  ...verManager.ts |   13.52 |    81.25 |   29.16 |   13.52 | ...75-694,700-730 
  ...eLspClient.ts |   17.89 |      100 |       0 |   17.89 | ...37-244,254-258 
  ...LspService.ts |   45.87 |    62.13 |   66.66 |   45.87 | ...1282,1299-1309 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/mcp           |   78.69 |    75.34 |   75.92 |   78.69 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  ...h-provider.ts |   86.95 |      100 |   33.33 |   86.95 | ...,93,97,101-102 
  ...h-provider.ts |   73.82 |    53.92 |     100 |   73.82 | ...88-895,902-904 
  ...en-storage.ts |   98.62 |    97.72 |     100 |   98.62 | 87-88             
  oauth-utils.ts   |   70.58 |    85.29 |    90.9 |   70.58 | ...70-290,315-344 
  ...n-provider.ts |   89.83 |    95.83 |   45.45 |   89.83 | ...43,147,151-152 
 .../token-storage |   79.52 |    86.66 |   86.36 |   79.52 |                   
  ...en-storage.ts |     100 |      100 |     100 |     100 |                   
  ...en-storage.ts |   82.87 |    82.35 |   92.85 |   82.87 | ...63-173,181-182 
  ...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        |   67.44 |       76 |   65.62 |   67.44 |                   
  const.ts         |     100 |      100 |     100 |     100 |                   
  dream.ts         |   65.65 |    73.33 |      50 |   65.65 | 50,107-148        
  ...entPlanner.ts |   57.84 |    72.72 |   33.33 |   57.84 | ...35,140-147,152 
  entries.ts       |   63.77 |    79.16 |      50 |   63.77 | ...72-180,183-189 
  extract.ts       |    95.2 |    79.16 |     100 |    95.2 | 81-86,125         
  ...entPlanner.ts |   63.08 |    65.71 |   41.17 |   63.08 | ...17,222-223,332 
  ...ionPlanner.ts |       0 |        0 |       0 |       0 | 1                 
  forget.ts        |    45.8 |    61.53 |   44.44 |    45.8 | ...04,211,214-346 
  indexer.ts       |   83.87 |    45.45 |     100 |   83.87 | ...50,56-57,69-70 
  manager.ts       |   75.31 |    81.04 |    75.6 |   75.31 | ...1278,1291-1293 
  memoryAge.ts     |   90.47 |    77.77 |     100 |   90.47 | 50-51             
  paths.ts         |   55.47 |    89.47 |   85.71 |   55.47 | ...,89-90,106-114 
  prompt.ts        |   93.36 |    71.42 |     100 |   93.36 | ...58,161,228-229 
  recall.ts        |   79.56 |    69.38 |   88.88 |   79.56 | ...40-245,269-280 
  ...ceSelector.ts |   91.95 |    77.27 |     100 |   91.95 | ...08,110-111,119 
  scan.ts          |   87.91 |    68.42 |     100 |   87.91 | ...47-48,58,82-87 
  ...entPlanner.ts |    11.5 |      100 |       0 |    11.5 | ...57-192,210-298 
  status.ts        |   10.52 |      100 |       0 |   10.52 | 41-98             
  store.ts         |   94.44 |    83.33 |     100 |   94.44 | 56-57,92-93       
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/mocks         |       0 |        0 |       0 |       0 |                   
  msw.ts           |       0 |        0 |       0 |       0 | 1-9               
 src/models        |   89.31 |    85.47 |    87.5 |   89.31 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  ...tor-config.ts |   90.24 |    91.42 |     100 |   90.24 | 142,148,151-160   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...nfigErrors.ts |   74.22 |       44 |   84.61 |   74.22 | ...,67-74,106-117 
  ...igResolver.ts |   98.63 |    92.53 |     100 |   98.63 | 161,323,329       
  modelRegistry.ts |     100 |    98.59 |     100 |     100 | 222               
  modelsConfig.ts  |   84.57 |    81.92 |   81.57 |   84.57 | ...1223,1252-1253 
  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   |   71.18 |    88.73 |   48.57 |   71.18 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...on-manager.ts |   81.42 |    86.66 |      80 |   81.42 | ...29-830,837-846 
  rule-parser.ts   |   95.99 |    93.18 |     100 |   95.99 | ...-864,1013-1015 
  ...-semantics.ts |   58.28 |    85.27 |    30.2 |   58.28 | ...1604-1614,1643 
  types.ts         |     100 |      100 |     100 |     100 |                   
 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/qwen          |   86.01 |    79.48 |   97.18 |   86.01 |                   
  ...tGenerator.ts |   98.64 |    98.18 |     100 |   98.64 | 105-106           
  qwenOAuth2.ts    |   84.99 |    74.81 |   93.33 |   84.99 | ...,985-1001,1031 
  ...kenManager.ts |   83.76 |    76.22 |     100 |   83.76 | ...62-767,788-793 
 src/services      |   86.81 |    84.92 |   90.06 |   86.81 |                   
  ...ionTrailer.ts |     100 |      100 |     100 |     100 |                   
  ...llRegistry.ts |   97.82 |    94.73 |     100 |   97.82 | 172-173           
  ...ionService.ts |   95.53 |    95.14 |     100 |   95.53 | ...92,354,356-360 
  ...ingService.ts |    84.1 |    84.35 |   82.85 |    84.1 | ...1240,1257-1258 
  ...ttribution.ts |   91.73 |    87.71 |      90 |   91.73 | ...80-685,826-827 
  cronScheduler.ts |   97.56 |    92.98 |     100 |   97.56 | 62-63,77,155      
  ...eryService.ts |   80.43 |    95.45 |      75 |   80.43 | ...19-134,140-141 
  fileReadCache.ts |     100 |      100 |     100 |     100 |                   
  ...temService.ts |   89.76 |     85.1 |   88.88 |   89.76 | ...89,191,266-273 
  ...ratedFiles.ts |      96 |    88.23 |     100 |      96 | 119-120,146-147   
  gitInit.ts       |     100 |      100 |     100 |     100 |                   
  gitService.ts    |   68.75 |     92.3 |   55.55 |   68.75 | ...12-122,125-129 
  ...reeService.ts |   71.83 |    68.47 |    91.3 |   71.83 | ...89-790,806,822 
  ...ionService.ts |   98.13 |     97.8 |   95.45 |   98.13 | ...32-333,380-381 
  ...orRegistry.ts |   96.34 |    91.66 |     100 |   96.34 | ...90-391,542-543 
  sessionRecap.ts  |   12.34 |      100 |       0 |   12.34 | 49-158            
  ...ionService.ts |   89.42 |     78.1 |   96.42 |   89.42 | ...1221,1225-1226 
  sessionTitle.ts  |   93.91 |    71.15 |     100 |   93.91 | ...34-237,268-269 
  ...ionService.ts |   83.01 |    78.66 |   87.75 |   83.01 | ...1482,1488-1493 
  ...UseSummary.ts |   94.63 |    88.67 |     100 |   94.63 | ...69-171,221-222 
 ...icrocompaction |   98.62 |    86.44 |     100 |   98.62 |                   
  microcompact.ts  |   98.62 |    86.44 |     100 |   98.62 | 138,142           
 src/skills        |    87.5 |     83.8 |   94.23 |    87.5 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...activation.ts |     100 |     93.1 |     100 |     100 | 93,112            
  skill-load.ts    |   92.94 |    81.63 |     100 |   92.94 | ...06,226,238-240 
  skill-manager.ts |   83.31 |    79.66 |   90.32 |   83.31 | ...1115,1122-1126 
  skill-paths.ts   |   86.74 |    77.77 |     100 |   86.74 | ...00-101,106-107 
  symlinkScope.ts  |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/subagents     |   82.84 |    79.74 |   95.23 |   82.84 |                   
  ...tin-agents.ts |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...-selection.ts |     100 |      100 |     100 |     100 |                   
  ...nt-manager.ts |   76.74 |    71.42 |   92.85 |   76.74 | ...1155,1177-1178 
  types.ts         |     100 |      100 |     100 |     100 |                   
  validation.ts    |   92.46 |    95.18 |     100 |   92.46 | 51-56,69-74,78-83 
 src/telemetry     |    73.5 |    85.47 |   77.56 |    73.5 |                   
  config.ts        |     100 |      100 |     100 |     100 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  ...-exporters.ts |   46.37 |      100 |   44.44 |   46.37 | ...85,88-89,92-93 
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...t.circular.ts |       0 |        0 |       0 |       0 | 1-111             
  ...-processor.ts |   93.89 |    90.21 |   94.11 |   93.89 | ...70-275,294-295 
  ...t.circular.ts |       0 |        0 |       0 |       0 | 1-128             
  loggers.ts       |    51.9 |       64 |   57.77 |    51.9 | ...1214,1231-1251 
  metrics.ts       |    74.9 |    82.95 |   74.54 |    74.9 | ...58-978,981-992 
  sanitize.ts      |      80 |    83.33 |     100 |      80 | 35-36,41-42       
  sdk.ts           |   90.42 |    83.56 |   76.92 |   90.42 | ...16-317,337-341 
  ...on-context.ts |     100 |      100 |     100 |     100 |                   
  ...on-tracing.ts |   92.77 |     87.3 |     100 |   92.77 | 79-93,379-383     
  ...etry-utils.ts |     100 |      100 |     100 |     100 |                   
  ...l-decision.ts |     100 |      100 |     100 |     100 |                   
  ...e-id-utils.ts |     100 |      100 |     100 |     100 |                   
  tracer.ts        |   99.24 |    88.88 |     100 |   99.24 | 53                
  types.ts         |   79.17 |    85.83 |   83.33 |   79.17 | ...1149,1152-1181 
  uiTelemetry.ts   |   92.97 |    96.96 |   81.25 |   92.97 | ...93-194,200-207 
 ...ry/qwen-logger |   68.24 |    79.56 |   64.91 |   68.24 |                   
  event-types.ts   |       0 |        0 |       0 |       0 |                   
  qwen-logger.ts   |   68.24 |    79.34 |   64.28 |   68.24 | ...1055,1093-1094 
 src/test-utils    |   93.16 |    95.83 |   73.52 |   93.16 |                   
  config.ts        |     100 |      100 |     100 |     100 |                   
  ...st-helpers.ts |   94.11 |       90 |     100 |   94.11 | 69-70             
  index.ts         |     100 |      100 |     100 |     100 |                   
  mock-tool.ts     |   91.19 |    97.05 |   68.96 |   91.19 | ...38,202-203,216 
  ...aceContext.ts |     100 |      100 |     100 |     100 |                   
 src/tools         |   76.49 |    81.35 |   84.43 |   76.49 |                   
  ...erQuestion.ts |   88.93 |    76.74 |    90.9 |   88.93 | ...39-340,347-348 
  cron-create.ts   |   97.75 |    88.88 |   83.33 |   97.75 | 30-31             
  cron-delete.ts   |   96.82 |      100 |   83.33 |   96.82 | 26-27             
  cron-list.ts     |   96.66 |      100 |   83.33 |   96.66 | 25-26             
  diffOptions.ts   |     100 |      100 |     100 |     100 |                   
  edit.ts          |   78.01 |    84.76 |   73.33 |   78.01 | ...86-687,774-824 
  exitPlanMode.ts  |   85.09 |    85.71 |     100 |   85.09 | ...60-163,177-189 
  glob.ts          |   90.63 |    88.33 |   84.61 |   90.63 | ...28,171,302,305 
  grep.ts          |   79.19 |    85.71 |   78.94 |   79.19 | ...20,560,569-576 
  ls.ts            |   96.74 |    90.27 |     100 |   96.74 | 176-181,212,216   
  lsp.ts           |   72.77 |    60.09 |   90.32 |   72.77 | ...1211,1213-1214 
  ...nt-manager.ts |   51.95 |     65.9 |   47.36 |   51.95 | ...03-525,528-565 
  mcp-client.ts    |   32.44 |    75.28 |   63.63 |   32.44 | ...1462,1466-1469 
  mcp-tool.ts      |   90.98 |    88.88 |   96.42 |   90.98 | ...95-596,646-647 
  memory-config.ts |       0 |        0 |       0 |       0 | 1-47              
  ...iable-tool.ts |     100 |    84.61 |     100 |     100 | 102,109           
  monitor.ts       |   92.27 |    83.94 |      92 |   92.27 | ...18,547-550,563 
  ...nforcement.ts |   82.44 |       90 |     100 |   82.44 | 174-185,234-247   
  read-file.ts     |   95.07 |     88.6 |      90 |   95.07 | ...99,290-293,296 
  ripGrep.ts       |   94.59 |    85.71 |   93.33 |   94.59 | ...60,463,541-542 
  ...-transport.ts |    6.34 |        0 |       0 |    6.34 | 47-145            
  send-message.ts  |   89.32 |    91.66 |   83.33 |   89.32 | 44-45,68-76       
  shell.ts         |   72.18 |    80.23 |   89.65 |   72.18 | ...3659,3708-3714 
  skill-utils.ts   |     100 |      100 |     100 |     100 |                   
  skill.ts         |   88.11 |    91.17 |   84.61 |   88.11 | ...95,399,422-444 
  ...eticOutput.ts |   95.12 |      100 |      80 |   95.12 | 87-88             
  task-stop.ts     |   93.14 |    96.15 |   85.71 |   93.14 | 39-40,54-64       
  todoWrite.ts     |   85.42 |    84.09 |   84.61 |   85.42 | ...05-410,432-433 
  tool-error.ts    |     100 |      100 |     100 |     100 |                   
  tool-names.ts    |     100 |      100 |     100 |     100 |                   
  tool-registry.ts |   74.64 |    75.24 |      80 |   74.64 | ...82-783,791-792 
  tool-search.ts   |   95.19 |    86.48 |    92.3 |   95.19 | ...47-153,208-213 
  tools.ts         |   87.76 |       90 |   88.23 |   87.76 | ...50-451,467-473 
  web-fetch.ts     |   88.59 |    79.48 |    92.3 |   88.59 | ...12-313,315-316 
  write-file.ts    |    79.2 |    79.26 |   83.33 |    79.2 | ...39-642,654-689 
 src/tools/agent   |   83.39 |    84.95 |   83.92 |   83.39 |                   
  agent.ts         |   83.69 |    85.38 |   84.31 |   83.69 | ...1668,1677-1681 
  fork-subagent.ts |   78.26 |    71.42 |      80 |   78.26 | 54-72,104-105     
 src/utils         |   88.62 |    87.14 |   93.15 |   88.62 |                   
  LruCache.ts      |       0 |        0 |       0 |       0 | 1-41              
  ...ssageQueue.ts |     100 |      100 |     100 |     100 |                   
  ...cFileWrite.ts |   76.08 |    44.44 |     100 |   76.08 | 61-70,72          
  bareMode.ts      |   27.27 |      100 |       0 |   27.27 | 9-15,18-19        
  browser.ts       |    7.69 |      100 |       0 |    7.69 | 17-56             
  ...igResolver.ts |     100 |      100 |     100 |     100 |                   
  ...engthError.ts |   89.11 |    86.66 |     100 |   89.11 | ...28-129,132-133 
  cronDisplay.ts   |   42.85 |    23.07 |     100 |   42.85 | 26-31,33-45,47-54 
  cronParser.ts    |   89.74 |    85.71 |     100 |   89.74 | ...,63-64,183-186 
  debugLogger.ts   |    95.9 |    93.84 |   94.73 |    95.9 | 106-107,214-218   
  editHelper.ts    |   93.63 |    83.52 |     100 |   93.63 | ...28-429,463-464 
  editor.ts        |   97.61 |    95.71 |     100 |   97.61 | ...70-271,273-274 
  ...arResolver.ts |   94.28 |    88.88 |     100 |   94.28 | 28-29,125-126     
  ...entContext.ts |     100 |    95.45 |     100 |     100 | 83                
  errorParsing.ts  |    97.7 |    97.05 |     100 |    97.7 | 72-73             
  ...rReporting.ts |   88.46 |       90 |     100 |   88.46 | 69-74             
  errors.ts        |   70.92 |    79.59 |   53.33 |   70.92 | ...03-219,223-229 
  fetch.ts         |   70.18 |    71.42 |   71.42 |   70.18 | ...42,148,161,186 
  fileUtils.ts     |   91.41 |    86.13 |      95 |   91.41 | ...1182,1186-1192 
  forkedAgent.ts   |    78.5 |    70.73 |   85.71 |    78.5 | ...30-436,441-447 
  formatters.ts    |   54.54 |       50 |     100 |   54.54 | 12-16             
  ...eUtilities.ts |   89.21 |    86.66 |     100 |   89.21 | 16-17,49-55,65-66 
  ...rStructure.ts |   94.36 |    94.28 |     100 |   94.36 | ...17-120,330-335 
  getPty.ts        |    12.5 |      100 |       0 |    12.5 | 21-34             
  gitDiff.ts       |   92.36 |    79.53 |     100 |   92.36 | ...55-856,928-929 
  ...noreParser.ts |    92.3 |    89.36 |     100 |    92.3 | ...15-116,186-187 
  gitUtils.ts      |   56.66 |    85.71 |      75 |   56.66 | ...2,72-73,97-148 
  iconvHelper.ts   |     100 |      100 |     100 |     100 |                   
  ...rePatterns.ts |     100 |      100 |     100 |     100 |                   
  ...ionManager.ts |     100 |     90.9 |     100 |     100 | 26                
  ...lPromptIds.ts |     100 |      100 |     100 |     100 |                   
  jsonl-utils.ts   |    74.1 |    90.76 |   58.33 |    74.1 | ...23-326,336-342 
  ...-detection.ts |     100 |      100 |     100 |     100 |                   
  ...yDiscovery.ts |    83.9 |    79.36 |     100 |    83.9 | ...16,319,411-414 
  ...tProcessor.ts |   93.63 |       90 |     100 |   93.63 | ...96-302,384-385 
  ...Inspectors.ts |   61.53 |      100 |      50 |   61.53 | 18-23             
  ...kerChecker.ts |   82.55 |    78.57 |     100 |   82.55 | 68-69,79-84,92-98 
  notebook.ts      |   94.35 |    84.78 |     100 |   94.35 | ...10,122,174-176 
  openaiLogger.ts  |   86.27 |    82.14 |     100 |   86.27 | ...05-107,130-135 
  partUtils.ts     |     100 |      100 |     100 |     100 |                   
  pathReader.ts    |     100 |      100 |     100 |     100 |                   
  paths.ts         |   93.21 |    91.86 |     100 |   93.21 | ...89-390,392-394 
  pdf.ts           |   93.68 |    87.05 |     100 |   93.68 | ...96-297,321-325 
  projectPath.ts   |     100 |      100 |     100 |     100 |                   
  ...ectSummary.ts |   89.39 |    72.41 |     100 |   89.39 | ...37-142,193-196 
  ...tIdContext.ts |     100 |      100 |     100 |     100 |                   
  proxyUtils.ts    |     100 |      100 |     100 |     100 |                   
  ...rDetection.ts |   58.57 |       76 |     100 |   58.57 | ...4,88-89,95-100 
  ...noreParser.ts |   85.45 |    85.18 |     100 |   85.45 | ...59,65-66,72-73 
  rateLimit.ts     |   92.55 |    85.92 |     100 |   92.55 | ...70-272,309-310 
  readManyFiles.ts |   87.96 |    86.95 |     100 |   87.96 | ...05-207,223-234 
  retry.ts         |   89.81 |    88.05 |     100 |   89.81 | ...29,350,357-358 
  ripgrepUtils.ts  |   46.53 |    84.37 |   66.66 |   46.53 | ...32-233,245-322 
  ...sDiscovery.ts |   97.42 |    92.85 |     100 |   97.42 | ...04,182-183,202 
  ...tchOptions.ts |   63.85 |    64.28 |   83.33 |   63.85 | ...29-130,187-188 
  runtimeStatus.ts |   85.58 |    82.05 |     100 |   85.58 | ...81,231-237,239 
  safeJsonParse.ts |   74.07 |    83.33 |     100 |   74.07 | 40-46             
  ...nStringify.ts |     100 |      100 |     100 |     100 |                   
  ...aConverter.ts |   90.78 |    88.23 |     100 |   90.78 | ...41-42,93,95-96 
  ...aValidator.ts |   94.57 |    80.26 |     100 |   94.57 | ...04,213-216,270 
  ...r-launcher.ts |   76.92 |     91.3 |   66.66 |   76.92 | ...34,136,157-195 
  ...orageUtils.ts |   96.89 |    85.84 |     100 |   96.89 | ...51,367,447,466 
  shell-utils.ts   |   82.93 |    89.55 |     100 |   82.93 | ...1522,1529-1533 
  ...lAstParser.ts |   95.58 |    85.79 |     100 |   95.58 | ...1059-1061,1071 
  ...nlyChecker.ts |   95.75 |    92.39 |     100 |   95.75 | ...00-301,313-314 
  sideQuery.ts     |   98.71 |    97.14 |     100 |   98.71 | 106               
  ...tGenerator.ts |     100 |      100 |     100 |     100 |                   
  ...ameContext.ts |     100 |      100 |     100 |     100 |                   
  symlink.ts       |   77.77 |       50 |     100 |   77.77 | 44,54-59          
  ...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 
  textUtils.ts     |      60 |      100 |   66.66 |      60 | 36-55             
  thoughtUtils.ts  |     100 |    92.85 |     100 |     100 | 71                
  ...-converter.ts |   94.59 |    85.71 |     100 |   94.59 | 35-36             
  tool-utils.ts    |    93.6 |     91.3 |     100 |    93.6 | ...58-159,162-163 
  truncation.ts    |     100 |       92 |     100 |     100 | 52,71             
  windowsPath.ts   |   89.47 |    79.31 |     100 |   89.47 | ...57-58,62,90-91 
  ...aceContext.ts |   93.71 |    89.28 |   93.33 |   93.71 | ...24-225,249-251 
  xml.ts           |     100 |      100 |     100 |     100 |                   
  yaml-parser.ts   |      92 |    84.31 |     100 |      92 | 49-53,65-69       
 ...ils/filesearch |   85.77 |    81.06 |   96.42 |   85.77 |                   
  crawlCache.ts    |     100 |      100 |     100 |     100 |                   
  crawler.ts       |   82.84 |    77.49 |   94.82 |   82.84 | ...1451,1485-1486 
  fileSearch.ts    |   93.58 |    87.32 |     100 |   93.58 | ...46-247,249-250 
  ignore.ts        |     100 |      100 |     100 |     100 |                   
  result-cache.ts  |     100 |     92.3 |     100 |     100 | 46                
 ...uest-tokenizer |   56.63 |    74.52 |   74.19 |   56.63 |                   
  ...eTokenizer.ts |   41.86 |    76.47 |   69.23 |   41.86 | ...70-443,453-507 
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...tTokenizer.ts |   68.39 |    69.49 |    90.9 |   68.39 | ...24-325,327-328 
  ...ageFormats.ts |      76 |      100 |   33.33 |      76 | 45-48,55-56       
  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.

@wenshao wenshao changed the title fix(cli): auto-restore prompt and preserve queue on cancel; align with Claude Code fix(cli): auto-restore prompt and preserve queue on cancel May 10, 2026
Comment thread packages/core/src/core/logger.ts
Without this, a transient writeFile error during a USER logMessage left
the undo tracker pointing at the previous successful entry. A subsequent
removeLastUserMessage (e.g., from auto-restore on cancel) would then
silently delete an unrelated earlier row from disk-backed history.

Add a regression test that mocks a writeFile rejection and asserts the
tracker is null and the prior entry survives.

Reported in PR review.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@wenshao wenshao requested a review from Copilot May 10, 2026 16:35

@wenshao wenshao left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

⚠️ Downgraded from Request changes to Comment: self-PR; CI failing: Agent, Agent.

Comment thread packages/cli/src/ui/AppContainer.tsx Outdated

@wenshao wenshao left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

⚠️ Downgraded from Approve to Comment: self-PR; CI failing (Agent ×2). No blocking issues found.

Overall: solid implementation of auto-restore-on-cancel, well-tested (117/117 pass), good code quality. One Suggestion inline.

— DeepSeek/deepseek-v4-pro via Qwen Code /review

Comment thread packages/cli/src/ui/AppContainer.tsx Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

…serialize writes

PR-review follow-up addressing two issues in the cancel-undo path.

1. Logger instance mismatch (Critical):
   `useGeminiStream` and `AppContainer` each called `useLogger()`, which
   instantiates a fresh `Logger` per call. `lastLoggedUserEntry` lives on
   the instance, so the undo invoked from `AppContainer` was always a
   no-op — the cancelled prompt still surfaced via cross-session
   `getPreviousUserMessages`. Move the `useLogger` ownership to
   `AppContainer` and pass the same instance into `useGeminiStream` via a
   new optional `logger` parameter.

2. Logger write ordering:
   Both `logMessage` and `removeLastUserMessage` do read → splice/append
   → writeFile without a lock. A fast cancel-then-resubmit could let
   `removeLast` clobber a just-appended new entry. Add a per-instance
   `serialize()` helper (a Promise-chained write queue) and route both
   mutating ops through it. Reset the queue on `close()`. New regression
   test fires removeLast and a fresh logMessage in parallel and asserts
   the resubmitted entry survives.

3. Stale React-state race in cancel guard (Suggestion):
   The auto-restore guard read `pendingGeminiHistoryItems` from React
   state, which can lag a stream chunk that just set
   `pendingHistoryItemRef.current`. Snapshot the pending item at the
   start of `cancelOngoingRequest` and pass it through the new
   `onCancelSubmit({ pendingItem })` info parameter. The guard combines
   it with the React-state items so any meaningful in-flight content
   blocks auto-restore even before re-render. New test covers the case
   where pendingHistoryItems is empty but info.pendingItem carries
   `gemini_content`.

All touched-area suites pass: 64 cli AppContainer, 9 historyUtils,
85 useGeminiStream, 46 core logger.
…cel-handler test types

The pre-commit eslint --fix on the previous commit collapsed the two
consecutive `import { ... } from '@qwen-code/qwen-code-core'` blocks in
useGeminiStream.ts into a single statement, but kept the `import type`
modifier from the first block — silently turning every runtime symbol
(SendMessageType, MessageSenderType, GitService, ApprovalMode, …) into
type-only imports. tsc rejected with TS2206 + a wave of TS1361 errors
that only surfaced on CI.

Restore the two separate imports: pure-type symbols (Logger included)
in `import type { ... }`, runtime symbols in plain `import { ... }`.

Also: the AppContainer cancel-handler tests captured `onCancelSubmit`
as `() => void`, but the hook signature now takes an optional info
arg. Widen the captured-callback type so passing `{ pendingItem }`
typechecks (TS2554 on line 1053).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@wenshao wenshao left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

⚠️ Downgraded from Approve to Comment: self-PR. No Critical issues found. 4 Suggestions below.

— deepseek-v4-pro via Qwen Code /review

Comment thread packages/core/src/core/logger.ts Outdated
Comment thread packages/cli/src/ui/hooks/useGeminiStream.ts
Comment thread packages/cli/src/ui/utils/historyUtils.ts
Comment thread packages/cli/src/ui/AppContainer.tsx
Four follow-ups from a /review pass on the auto-restore-on-cancel path.

* logger.ts — only invalidate `lastLoggedUserEntry` when the failed
  write was itself a USER attempt. A failed non-USER write (MODEL_SWITCH
  on a transient disk error, etc.) doesn't change which row was the
  most recent user prompt, so the prior undo target is still valid.
  Without this, MODEL_SWITCH disk hiccups silently disabled cancel-undo.

* useGeminiStream.ts — wrap `onCancelSubmit` in try/finally so a throw
  in AppContainer's cancel handler can't strand the stream in
  Responding (the UI would lock — Esc would no-op until process
  restart). `setIsResponding(false)` and `setShellInputFocused(false)`
  always run.

* useGeminiStream.ts — also document the three-way coupling between
  the INFO `addItem` here and AppContainer's auto-restore guard:
  the guard reads `historyRef.current` which doesn't yet contain
  this INFO (React batches), and the guard's correctness depends on
  the items added here staying synthetic.

* historyUtils.ts — make `isSyntheticHistoryItem` exhaustive over the
  35-member `HistoryItemWithoutId` union. Every case is explicit; the
  default branch carries a `_exhaustive: never` so adding a new
  HistoryItem variant without classifying it triggers a compile-time
  error rather than silently disabling auto-restore. Runtime fallback
  is "meaningful" (safe — bail rather than wipe content).

Tests: +1 logger case (non-USER failure preserves the USER tracker),
+1 useGeminiStream case (throwing handler still flushes Responding).

All touched suites pass: 47 logger, 9 historyUtils, 86 useGeminiStream,
64 AppContainer.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Comment thread packages/core/src/core/logger.ts Outdated
…eckpoints)

Reword the comment above `writeQueue` and the `serialize()` JSDoc to
state explicitly that the queue only serializes log-history mutations
(`logMessage` / `removeLastUserMessage`). Checkpoint ops
(saveCheckpoint / deleteCheckpoint / loadCheckpoint) touch separate
files and intentionally don't share this queue, so the previous
"every disk-mutating op chains here" wording overstated the
guarantee.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Comment thread packages/cli/src/ui/hooks/useGeminiStream.ts Outdated
…m on cancel

Stream content/thought events are throttled into a per-turn `bufferedEvents`
array; only when `flushBufferedStreamEvents` runs do they reach
`pendingHistoryItemRef.current`. Snapshotting BEFORE the flush meant cancels
that fired inside the throttle window (60ms) saw a null `pendingItem` even
when meaningful text was sitting in the buffer. AppContainer's auto-restore
guard then read null, decided "model produced nothing", and called
`truncateToItem` — which silently wiped the very content that the
subsequent `addItem(pendingHistoryItemRef.current)` had just committed.

Move the snapshot to AFTER the flush so it sees the same value as the
addItem call directly below it.

Regression test: yields a content event and cancels without advancing
fake timers, asserts `info.pendingItem` carries the buffered "partial
response" text rather than null.

@wenshao wenshao left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

⚠️ Downgraded from Approve to Comment: self-PR.

No blocking issues found. One Suggestion inline for minor code clarity improvement.

— DeepSeek/deepseek-v4-pro via Qwen Code /review

Comment thread packages/core/src/core/logger.ts Outdated
…tore

The auto-restore branch was cleaning up two of the three places a
cancelled prompt lives — the UI transcript via `truncateToItem` and
the disk-backed ↑-history via `Logger.removeLastUserMessage`. The
third — the in-memory chat history on `GeminiChat` — was left
untouched. `sendMessageStream` appends the user content to
`chat.history` BEFORE the stream generator runs and the abort path
doesn't pop it. After a successful auto-restore the next request's
wire payload still carried the cancelled prompt as a leading user
turn alongside the new prompt, so the model saw context the user
believed had been undone (and in some shapes the API would reject
two consecutive user turns).

Mirror the existing strip the Retry submit path uses
(`GeminiClient.sendMessageStream` at the `Retry` branch): make
`GeminiClient.stripOrphanedUserEntriesFromHistory` public and call
it from the auto-restore success path, sitting next to the UI
truncate and the disk-log undo. The method already pops trailing
user entries and clears the `FileReadCache` (which can otherwise
hold dangling `read_file` results from the stripped turn).

End-to-end reproduction from the PR review:
1. Submit `what time is it?` → ESC during pre-token delay →
   auto-restore (UI rewound, buffer pre-filled).
2. Edit buffer to `what year is it?` → submit.
3. Pre-fix: outbound `messages` carried both prompts as consecutive
   user turns. Post-fix: only the new prompt.

Test: extend the auto-restore-success AppContainer test with a
mock `stripOrphanedUserEntriesFromHistory` spy and assert it fires.
The non-restore branches don't install the spy (it's optionally
chained at the call site).
@wenshao wenshao requested a review from Copilot May 12, 2026 05:12
@wenshao

wenshao commented May 12, 2026

Copy link
Copy Markdown
Collaborator Author

@tanzhenxin 已采纳第三处清理,commit 1238206

改动

  • GeminiClient.stripOrphanedUserEntriesFromHistoryprivate 改为 public,JSDoc 加上两条调用方说明(Retry 路径 + 新增的 auto-restore 路径)。
  • AppContainer auto-restore 成功分支在 truncateToItem + buffer.setText 之后、logger.removeLastUserMessage 之前,调用 geminiClient?.stripOrphanedUserEntriesFromHistory?.()。三处清理并列:UI transcript / chat 内存历史 / 跨会话 ↑-history。

为什么沿用现成函数:它本就在 Retry submit 路径用过同样的语义 —— pop trailing user entries 并 clear FileReadCache(避免 stripped turn 留下的 read_file functionResponse 让后续 Read 命中 file_unchanged 占位符)。

回归测试:扩展 auto-restore-success 用例,在 mockConfig.getGeminiClient 上挂一个 stripOrphanedUserEntriesFromHistory spy,断言 truncate / setText / removeLast / stripOrphans 四个动作都 fire。

按你给的端到端场景再走一遍:第二次请求的 wire payload 现在只剩新 prompt,被取消的那一条不再出现。

Re: your review

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.

@wenshao wenshao left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

⚠️ Downgraded from Request changes to Comment: self-PR; CI still running. The two Critical findings could not both be anchored exactly at their logical source lines after GitHub rejected those line anchors, so they are included in this review body.

[Critical] SendMessageType.Retry bypasses prepareQueryForGemini, so lastTurnUserItemRef.current is not reset for retry turns. If a prior normal prompt populated the ref, then a Ctrl+Y retry is cancelled before meaningful output, cancelOngoingRequest can pass stale ownership info and AppContainer may rewind the older user prompt even though retry did not add a new user history item. Reset the ownership ref before the retry fast path, or make retry cancellation explicitly report lastTurnUserItem: null, so retry cancels cannot target a user item from an earlier turn.

[Critical] lastTurnUserItemRef.current is set after addItem({ type: USER, text }), but useHistory.addItem silently skips consecutive duplicate user messages with the same text. Manually submitting the same prompt again and cancelling immediately can make auto-restore treat the older identical user item as belonging to the cancelled turn and truncate it. The ownership token should come from the actual inserted history item, not just the text: have addItem report whether it appended, set lastTurnUserItemRef only when insertion happened, and have CancelSubmitInfo/AppContainer verify that id instead of relying on text equality.

— gpt-5.5 via Qwen Code /review

Comment thread packages/cli/src/ui/hooks/useGeminiStream.test.tsx
Comment thread packages/core/src/core/logger.test.ts
…Item and dup-skip identity contracts

Three follow-ups from PR review batch:

* core/logger.ts — `serialize()` was `this.writeQueue.then(op, op)`.
  The second callback was dead code: `writeQueue` is seeded with
  `Promise.resolve()` and reassigned through `.catch(() => undefined)`,
  so the queue tail can never reject. Worse, `then(op, op)` reads as
  "retry op on rejection" — wrong intent. Switch to `.then(() => op())`
  with a comment spelling out the no-reject invariant.

* cli/useGeminiStream.test.tsx — add ownership-contract tests at the
  PRODUCER side of `info.lastTurnUserItem`. Until now only the
  AppContainer tests pinned the contract, and they fabricate the
  value, so a regression that drops `lastTurnUserItemRef.current = {
  text: trimmedQuery }` in `prepareQueryForGemini` would slip
  through. New tests:
    - normal `UserQuery` submit → cancel → assert
      `info.lastTurnUserItem === { text: 'what time is it?' }`.
    - `SendMessageType.Notification` submit → cancel → assert
      `info.lastTurnUserItem === null` (path doesn't push a user
      history item, the ref reset at the top of
      prepareQueryForGemini must keep it null).

* core/logger.test.ts — strengthen the duplicate-skip regression.
  The previous test only checked the tracker advanced text; the
  important identity contract is that the recalculated 5-tuple
  matches the disk row, so a subsequent `removeLastUserMessage()`
  removes the duplicate-skipped row rather than the older USER.
  New test seeds disk with [first, second], stubs `_updateLogFile`
  for the second call to mimic the duplicate-skip branch (mutate
  newEntryObject's messageId+timestamp to align with the disk row,
  return null), then asserts removeLastUserMessage() leaves
  ['first'] on disk and removes 'second'.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.

@wenshao wenshao left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

⚠️ Downgraded from Approve to Comment: self-PR.

No blocking issues found. Second opinion with glm-5.1 after 5+ prior review rounds (DeepSeek-v4-pro × 4, Copilot, human @tanzhenxin).

Changes since last review (2 commits):

  • Fix serialize to use then(() => op()) instead of then(op, op) — eliminates the misleading retry semantics on error recovery.
  • Make stripOrphanedUserEntriesFromHistory public and add it as the third cleanup leg in auto-restore — prevents cancelled prompt from riding along on the next API request as an orphan user turn.
  • Pin lastTurnUserItem producer-side contract with tests (UserQuery emits text, Notification emits null).
  • Pin duplicate-skip identity contract with regression test.

Assessment: Deterministic analysis clean (tsc 0, eslint 0). Build PASS, Tests PASS (216/216). 9 review agents + 1 reverse-audit round found no high-confidence Critical or Suggestion issues. Three low-confidence observations in terminal output only (not posted inline to avoid noise).

— glm-5.1 via Qwen Code /review

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

The latest wave — the in-memory chat-history strip (the third cleanup leg), the duplicate-skip shift in the user-log writer, and the write-queue signature cleanup — all look correct and well-tested. The third cleanup leg is the right call, and the comment explaining the orphan-user-turn risk on the next request is excellent. One correctness gap remains.

1. The cancelled prompt stays printed in the terminal after auto-restore (severity: medium · confidence: very high)

Reproduced on the PR's stated golden path. Type what time is it?, press Enter, press ESC before any output. The transcript still shows > what time is it? in the static-rendered region, AND the input buffer is pre-filled with the same text — the user sees the prompt twice. Reproduced at both ~0.5s and ~2s Enter-to-ESC delays, so it's not a timing edge case.

The auto-restore branch truncates React history state but never refreshes Ink's static transcript region. Static is append-only — once an item has been printed, changing React state alone won't unprint it. The existing Ctrl+O rewind path does the same kind of truncate but follows it with an explicit static-refresh (which writes the terminal-clear escape and remounts) for exactly this reason. Auto-restore omits that refresh, so the React tree, the disk log, and the in-memory chat history all rewind correctly while the visible transcript does not. The new tests don't catch it because they mock the history manager and never exercise a real Ink render.

Verdict

REQUEST_CHANGES — visible regression on the PR's stated golden path, reproduced on a real terminal. The fix is small.

@wenshao wenshao left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

⚠️ Downgraded from Request Changes to Comment: self-PR.

Comment thread packages/cli/src/ui/hooks/useGeminiStream.ts
Comment thread packages/cli/src/ui/AppContainer.tsx Outdated
Comment thread packages/core/src/core/client.ts
Comment thread packages/cli/src/ui/hooks/useGeminiStream.ts
…PR review

Four critical findings from gpt-5.5 /review pass:

1. **Retry skipped the lastTurnUserItem reset** (useGeminiStream.ts)
   `Retry` bypasses `prepareQueryForGemini`, which is where the
   `lastTurnUserItemRef.current = null` reset lived. A retry that
   followed a normal `UserQuery` carried the stale ownership snapshot
   into `onCancelSubmit`, and cancelling the retry before any
   meaningful output let `AppContainer` auto-restore truncate the
   original failed prompt. Move the reset (and the new content-seen
   reset, see #4) to the top of `submitQuery`, gated only on
   "this is a top-level submit" — covers Retry, Cron, Notification,
   and ordinary UserQuery alike.

2. **Text-only ownership matched dedup'd duplicates** (AppContainer.tsx,
   useGeminiStream.ts) `useHistoryManager.addItem` skips inserting a
   consecutive-duplicate user message while still returning a freshly
   generated id. The text-only ownership check would match the OLDER
   identical-text USER row, so a re-submitted same prompt + cancel
   would wrongly truncate the prior turn. Carry id+text in
   `CancelSubmitInfo.lastTurnUserItem` (using `addItem`'s return
   value) and require both id AND text to match before truncating.

3. **stripOrphan left IDE context state advanced** (client.ts) Other
   history-mutating paths (`setHistory`, `truncateHistory`) set
   `forceFullIdeContext = true` after mutating; the orphan-strip
   didn't, so a subsequent request could send a diff against a
   removed baseline. Gate cache-clear + IDE-context invalidation on
   an actual before/after length drop, so no-op strips don't churn
   state.

4. **Flush-then-thought race let auto-restore wipe committed content**
   (useGeminiStream.ts, AppContainer.tsx) `cancelOngoingRequest`'s
   pre-cancel flush can `addItem` a meaningful `gemini_content` (via
   handleContentEvent's split path) and then a later thought event
   overwrites `pendingHistoryItem` with a synthetic value. The
   AppContainer guard's React history snapshot is stale, so the
   trailing-only-synthetic check passes and the just-committed text
   gets truncated. Track a synchronous `turnSawContentEventRef` set
   in handleContentEvent, ship it through `CancelSubmitInfo`, and
   make the guard bail when set.

Tests:
- core/client.test.ts: stripOrphan only forces full IDE context on
  actual removal; existing retry tests updated to mock
  `getHistoryLength`.
- cli/useGeminiStream.test.tsx: ownership uses { id, text }, Retry
  reset works after a prior UserQuery cancel,
  turnProducedMeaningfulContent flips true when content lands.
- cli/AppContainer.test.tsx: guard bails on `turnProducedMeaningfulContent: true`,
  guard bails on id mismatch (catches addItem dedup case).

cli 162/162 + core client 99/99 + core logger 51/51.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.

Reported by @tanzhenxin: auto-restore truncated React `history` state
but the cancelled `> prompt` and `Request cancelled.` lines stayed
printed in the terminal — Ink's `<Static>` region is append-only, so
shrinking the underlying array doesn't unprint already-flushed lines.
On the PR's golden path (type prompt → Enter → ESC) the user sees the
prompt twice: once in scrollback, once pre-filled in the input buffer.
Confirmed at multiple Enter-to-ESC delays, so it's not a timing fluke.

Call `refreshStatic()` immediately after `truncateToItem(...)` in the
auto-restore success path. `refreshStatic` writes the ANSI
clear-terminal escape AND bumps the static remount key — the exact
recipe `/clear` (`handleClearScreen`) already uses for the same
reason. The targeted-repaint helper used for terminal resizes is
intentionally NOT used here: it preserves scrollback, which would
leave the cancelled prompt visible above the new viewport.

Test: extend the existing auto-restore happy-path AppContainer test
to assert `mockStdout.write` was called with `ansiEscapes.clearTerminal`.
The other auto-restore-bail tests don't install the assertion so they
naturally verify the negative case (no clear when guard rejects).
@wenshao wenshao requested a review from Copilot May 13, 2026 01:02
@wenshao

wenshao commented May 13, 2026

Copy link
Copy Markdown
Collaborator Author

@tanzhenxin 已采纳,commit fdd6b06 修复。

Root cause 你描述得很准:Ink <Static> 是 append-only,只 setState 不会 unprint。/clear 路径用 handleClearScreenclearScreen() + remountStaticHistory() 解决同样问题,我没在 auto-restore 那条路径跟进同样的 repaint。

Fix:auto-restore 成功分支里 historyManager.truncateToItem(...) 之后立刻调 refreshStatic()(已有的辅助函数,写 ansiEscapes.clearTerminal + bump 静态区段 remount key)—— 与 /clear 走同一种 repaint。

不用更轻量的 repaintStaticViewport(resize 时用的、不抹 scrollback 的那个)—— 它会让被取消的 prompt 留在 viewport 上方的 scrollback 里,刚好破坏 "看不到这一轮" 的契约。

Test:在 auto-restores the just-submitted prompt when cancelling before any meaningful output 用例里加 expect(mockStdout.write).toHaveBeenCalledWith(ansiEscapes.clearTerminal)。其他 auto-restore-bail 路径不安装该断言,自然形成 negative case(守卫拒绝时不重绘)。

Re: your review

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.

Comment on lines 866 to 874
// Track output chars for real-time token estimation & mark as receiving.
streamingResponseLengthRef.current += eventValue.length;
setIsReceivingContent(true);
// Pin "this turn produced meaningful content" so the cancel
// handler's snapshot reflects content events even when they land
// during the pre-cancel flush (their addItem hasn't re-rendered
// React history by the time AppContainer's guard runs).
turnSawContentEventRef.current = true;
let newGeminiMessageBuffer = currentGeminiMessageBuffer + eventValue;
@wenshao

wenshao commented May 13, 2026

Copy link
Copy Markdown
Collaborator Author

null

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

Both critical findings from the prior round land cleanly. The retry-cancel disk erasure is gone — the ownership-ref reset now sits at the top of every top-level submit, so Retry no longer carries a stale snapshot into its cancel info. The Ink <Static> repaint also matches the rewind handler's recipe, so the cancelled prompt actually disappears from the terminal on the golden path. The three additional mechanisms in the same wave (the content-event flag for the flush race, id-based ownership for the consecutive-duplicate dedup, and length-gated cache invalidation in the orphan strip) each address a distinct gap and come with focused tests.

Verdict

APPROVE — both critical findings closed.

@wenshao wenshao merged commit d07daa3 into main May 13, 2026
12 checks passed

@wenshao wenshao left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

⚠️ Downgraded from Request Changes to Comment: self-PR (GitHub rejects APPROVE/REQUEST_CHANGES on own PRs).

The following two findings could not be anchored to a diff hunk (the affected lines are pre-existing context that this PR's new auto-restore path now critically depends on); recording in the body instead.

F4 (Suggestion) — packages/cli/src/ui/AppContainer.tsx:271-272

historyRef.current = historyManager.history is assigned during the render phase. The sibling pattern at packages/cli/src/ui/hooks/useGeminiStream.ts:343-346 uses useLayoutEffect and explicitly warns that "writing refs in the render phase is unsafe under React's concurrent rendering". The PR's cancel handler now reads historyRef.current to compute findLastUserItemIndex and itemsAfterAreOnlySynthetic — these decisions are safety-critical (they decide whether to truncate the user's transcript), so the asymmetry is a latent landmine.

Fix: move to useLayoutEffect(() => { historyRef.current = historyManager.history; }, [historyManager.history]);, mirroring the useGeminiStream pattern.

F9 (Suggestion) — packages/cli/src/ui/hooks/useGeminiStream.ts:745

Duplicate consecutive submits leak a disk USER entry. await logger?.logMessage(MessageSenderType.USER, trimmedQuery) runs unconditionally for every USER submit, but addItem(USER, ...) (~L798) skips consecutive duplicates and returns a fresh phantom id (useHistoryManager.ts:57-63). The disk gets a new entry every time; the UI history does not. lastTurnUserItemRef.current.id is then the phantom id, so on cancel the AppContainer guard's identity check lastUserItem.id !== cancelledTurnUserItem.id fails → bail "lastUserItem identity does not match" → removeLastUserMessage is never called.

Fix: either call removeLastUserMessage() before the identity bail (the disk persisted regardless), or short-circuit logMessage when addItem reports a dedup. Update the bail comment to acknowledge the dedup-leaked row.

— claude-opus-4-7 via Claude Code /qreview

// this — paths that don't add a USER history item (Cron /
// Notification / slash submit_prompt) leave it null so cancel
// never wrongly targets an older user item.
lastTurnUserItemRef.current = null;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

[Critical] Concurrent /btw mid-stream clobbers the in-flight main turn's ownership snapshot.

submitQuery's reset at line 1629 is correctly gated on !allowConcurrentBtwDuringResponse so the btw path skips it. But prepareQueryForGemini is invoked unconditionally at line 1676 (only Retry skips), and this un-gated reset (followed by addItem(USER, '/btw …') at line 807) overwrites the main turn's snapshot to point at the btw row. Same applies to await logger?.logMessage(USER, '/btw …') at line 745, which advances lastLoggedUserEntry to the btw entry.

User submits "long task" → submits /btw … mid-throttle → ESC-cancels long task. info.lastTurnUserItem now holds the btw row's id and text. Either (a) the auto-restore guard's identity check matches the btw row → truncateToItem truncates the btw user item from the UI and removeLastUserMessage deletes the wrong entry from disk; or (b) it doesn't match → bail, but lastLoggedUserEntry still points at btw, so any later removeLastUserMessage would target the wrong row. Untested.

Fix: drop this reset and rely solely on submitQuery's gated L1629 reset. Add a regression test that submits a UserQuery, then a /btw … mid-stream, then cancels — assert info.lastTurnUserItem still holds the original UserQuery's id/text.

— claude-opus-4-7 via Claude Code /qreview

// contradict what the UI told the user was rewound. Mirrors the
// existing strip in the Retry submit path
// (GeminiClient.sendMessageStream).
geminiClient?.stripOrphanedUserEntriesFromHistory?.();

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

[Critical] geminiClient?.stripOrphanedUserEntriesFromHistory?.() runs without try/catch. The method delegates to getChat() (packages/core/src/core/client.ts:294-298) which throws 'Chat not initialized' when this.chat is undefined. Reachable when the user submits before chat init completes (addItem(USER) is synchronous and sets lastTurnUserItem; chat = new GeminiChat(...) runs later in the same flow), then immediately ESC-cancels.

By the time line 1516 runs, the cancel handler has already executed historyManager.truncateToItem, refreshStatic, and buffer.setText — so a throw skips the next-line logger?.removeLastUserMessage() (cancelled prompt resurrects in next session's ↑-history) and the error bubbles into the Ink keypress handler. The success-path debug message ("auto-restore: rewinding…") still prints, which is misleading — the operation did NOT fully succeed.

Suggested change
geminiClient?.stripOrphanedUserEntriesFromHistory?.();
try {
geminiClient?.stripOrphanedUserEntriesFromHistory?.();
} catch (err) {
debugLogger.debug('auto-restore: stripOrphanedUserEntriesFromHistory failed', err);
}

— claude-opus-4-7 via Claude Code /qreview

// Entry already gone from disk (concurrent rotation/clear).
// Adopt disk state as truth so the in-memory cache doesn't
// diverge from a freshly-rotated file.
this.logs = currentLogsOnDisk;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

[Critical] Corrupt logs.json silently wipes the entire in-memory ↑-history beyond the optimistically-removed target.

_readLogFile silently rewrites a malformed-array (line ~130) or invalid-JSON (line ~151) file as [] and returns [] (no throw). The idx === -1 branch then sets this.logs = currentLogsOnDisk (= []), wiping more than just the target. The caller already optimistically removed the target before this; this branch then clobbers everything else too. The .invalid_json.<ts>.bak file on disk is the only forensic trail; no signal reaches AppContainer (the false return is swallowed at the call site — see the AppContainer:1525 finding).

Fix: distinguish the two [] cases inside _readLogFile (return a tagged result) so removeLastUserMessage can choose between "external rotation, adopt disk truth" and "corruption, preserve in-memory unchanged". Failing that, at minimum wrap the idx === -1 corruption case with restoreOptimistic() instead of overwriting this.logs wholesale, and emit a distinguishable debug breadcrumb so oncall can correlate.

— claude-opus-4-7 via Claude Code /qreview

// handler's snapshot reflects content events even when they land
// during the pre-cancel flush (their addItem hasn't re-rendered
// React history by the time AppContainer's guard runs).
turnSawContentEventRef.current = true;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

[Suggestion] turnSawContentEventRef.current = true is set BEFORE the whitespace-only short-circuit at line 879-881 (newGeminiMessageBuffer.trim().length === 0 → return).

A model that streams only whitespace tokens (\n\n, ) before the user cancels still flips the ref to true, which causes info.turnProducedMeaningfulContent: true and the AppContainer auto-restore guard at line ~1437 to bail — even though no addItem or setPendingHistoryItem actually ran. This duplicates the open Copilot inline comment on this line that has no reply.

False-positive on the auto-restore guard. The "model emitted only whitespace before cancel" scenario silently regresses to pre-PR behavior (cancelled prompt left in transcript, not restored to buffer).

Fix: move the turnSawContentEventRef.current = true assignment AFTER the early-return so it only fires when content actually advances to a gemini/gemini_content pending item — e.g., place it just before the splitPoint/addItem work that follows the trim guard. Add a useGeminiStream regression test feeding [{ type: Content, value: '\n\n' }] and asserting info.turnProducedMeaningfulContent === false.

— claude-opus-4-7 via Claude Code /qreview

// errors and returns false, but attach a .catch as defence so a
// future code path that throws doesn't surface as an
// UnhandledPromiseRejection.
void logger?.removeLastUserMessage().catch((err: unknown) => {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

[Suggestion] void logger?.removeLastUserMessage().catch(...) — the .catch only fires on thrown errors. removeLastUserMessage returns false on five separate failure modes (uninitialized, no tracker, external rotation, read-fail with rollback, write-fail with rollback) and none of those false returns are observable from this call site.

"Cancelled prompt resurrects next session" repro path is silently undebuggable. Oncall cannot tell from a single debug log whether (a) Logger was uninitialized, (b) tracker was null because the prior write failed, (c) the file got rotated externally, (d) read threw, or (e) write threw — each has different remediation.

Fix: capture the boolean return:

void logger?.removeLastUserMessage()
  .then((ok) => {
    if (!ok) debugLogger.debug('auto-restore: removeLastUserMessage returned false');
  })
  .catch((err: unknown) => {
    debugLogger.debug('Failed to undo cancelled prompt from log:', err);
  });

Optionally, change removeLastUserMessage's return type to a discriminated union ('ok' | 'no-tracker' | 'rotated' | 'read-fail' | 'write-fail') so reasons are machine-queryable.

— claude-opus-4-7 via Claude Code /qreview

// Always drain the queue back into the buffer (claude-code parity:
// popAllEditable preserves queued text on every cancel path, including
// tool-execution cancels — never silently drop the user's queued work).
const popped = popAllMessages();

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

[Suggestion] popAllMessages() joins all queued messages with \n\n including any queued slash commands. drainQueue filters them (useMessageQueue.ts:57) but the cancel path uses popAllMessages. Slash commands ARE allowed into the queue (the cancel-time fall-through adds them when streamingState !== Idle).

Queued /clear (or /quit, /auth, etc.) lands verbatim in the input buffer on cancel. If the user hits Enter without scrolling, the slash command runs — /clear wipes the session, /quit exits. Pre-PR clearQueue() dropped the queue so this couldn't happen; the new "preserve queue text" path is more user-friendly for plain text but unsafe for slash commands.

Fix: reuse the slash/non-slash split that drainQueue already implements — drop slash commands from the popped text (or surface them as a discarded-notification), and only push plain-text fragments into the buffer.

— claude-opus-4-7 via Claude Code /qreview

TaimoorSiddiquiOfficial added a commit to TaimoorSiddiquiOfficial/HopCode that referenced this pull request May 13, 2026
…) [upstream cherry-pick]

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
wenshao added a commit that referenced this pull request May 17, 2026
* fix(cli): auto-restore prompt and preserve queue on cancel; align with Claude Code

When a user pressed ESC immediately after submitting a prompt (before the
model produced any meaningful output), qwen-code left the cancelled prompt
stranded in the transcript and in cross-session ↑-history. Cancelling
during tool execution also silently dropped any queued follow-up input.

Mirror Claude Code's auto-restore-on-interrupt:

  - Drain the queue back into the input buffer on EVERY cancel path,
    including tool-execution cancels (replaces the unconditional
    clearQueue() that motivated #3204 with a non-destructive pop).
  - When the user cancels with no draft text, no queued input, and no
    meaningful pending/committed assistant content, truncate the user
    item and trailing INFO from history and pull the prompt text back
    into the input box for editing.
  - Add Logger.removeLastUserMessage so the disk-backed cross-session
    ↑-history (getPreviousUserMessages) is also cleaned on cancel.

The "meaningful content" check matches Claude Code's
messagesAfterAreOnlySynthetic: gemini text and tool runs are meaningful;
info/error/warning/retry/notification/tool_use_summary/thoughts are
synthetic. truncateToItem uses functional setState so it batches with
the INFO addItem from cancelOngoingRequest in the same render pass —
no flicker.

Tests cover all five guard branches and the logger undo across normal,
no-op, one-shot, MODEL_SWITCH-interleaved, disk-rotation, and
uninitialized cases.

* fix(core): clear lastLoggedUserEntry on logMessage write failure

Without this, a transient writeFile error during a USER logMessage left
the undo tracker pointing at the previous successful entry. A subsequent
removeLastUserMessage (e.g., from auto-restore on cancel) would then
silently delete an unrelated earlier row from disk-backed history.

Add a regression test that mocks a writeFile rejection and asserts the
tracker is null and the prior entry survives.

Reported in PR review.

* fix(cli, core): share Logger across AppContainer/useGeminiStream and serialize writes

PR-review follow-up addressing two issues in the cancel-undo path.

1. Logger instance mismatch (Critical):
   `useGeminiStream` and `AppContainer` each called `useLogger()`, which
   instantiates a fresh `Logger` per call. `lastLoggedUserEntry` lives on
   the instance, so the undo invoked from `AppContainer` was always a
   no-op — the cancelled prompt still surfaced via cross-session
   `getPreviousUserMessages`. Move the `useLogger` ownership to
   `AppContainer` and pass the same instance into `useGeminiStream` via a
   new optional `logger` parameter.

2. Logger write ordering:
   Both `logMessage` and `removeLastUserMessage` do read → splice/append
   → writeFile without a lock. A fast cancel-then-resubmit could let
   `removeLast` clobber a just-appended new entry. Add a per-instance
   `serialize()` helper (a Promise-chained write queue) and route both
   mutating ops through it. Reset the queue on `close()`. New regression
   test fires removeLast and a fresh logMessage in parallel and asserts
   the resubmitted entry survives.

3. Stale React-state race in cancel guard (Suggestion):
   The auto-restore guard read `pendingGeminiHistoryItems` from React
   state, which can lag a stream chunk that just set
   `pendingHistoryItemRef.current`. Snapshot the pending item at the
   start of `cancelOngoingRequest` and pass it through the new
   `onCancelSubmit({ pendingItem })` info parameter. The guard combines
   it with the React-state items so any meaningful in-flight content
   blocks auto-restore even before re-render. New test covers the case
   where pendingHistoryItems is empty but info.pendingItem carries
   `gemini_content`.

All touched-area suites pass: 64 cli AppContainer, 9 historyUtils,
85 useGeminiStream, 46 core logger.

* fix(cli): unbreak build after import-merge regression and tighten cancel-handler test types

The pre-commit eslint --fix on the previous commit collapsed the two
consecutive `import { ... } from '@qwen-code/qwen-code-core'` blocks in
useGeminiStream.ts into a single statement, but kept the `import type`
modifier from the first block — silently turning every runtime symbol
(SendMessageType, MessageSenderType, GitService, ApprovalMode, …) into
type-only imports. tsc rejected with TS2206 + a wave of TS1361 errors
that only surfaced on CI.

Restore the two separate imports: pure-type symbols (Logger included)
in `import type { ... }`, runtime symbols in plain `import { ... }`.

Also: the AppContainer cancel-handler tests captured `onCancelSubmit`
as `() => void`, but the hook signature now takes an optional info
arg. Widen the captured-callback type so passing `{ pendingItem }`
typechecks (TS2554 on line 1053).

* fix(cli, core): tighten cancel-undo robustness from PR review batch 3

Four follow-ups from a /review pass on the auto-restore-on-cancel path.

* logger.ts — only invalidate `lastLoggedUserEntry` when the failed
  write was itself a USER attempt. A failed non-USER write (MODEL_SWITCH
  on a transient disk error, etc.) doesn't change which row was the
  most recent user prompt, so the prior undo target is still valid.
  Without this, MODEL_SWITCH disk hiccups silently disabled cancel-undo.

* useGeminiStream.ts — wrap `onCancelSubmit` in try/finally so a throw
  in AppContainer's cancel handler can't strand the stream in
  Responding (the UI would lock — Esc would no-op until process
  restart). `setIsResponding(false)` and `setShellInputFocused(false)`
  always run.

* useGeminiStream.ts — also document the three-way coupling between
  the INFO `addItem` here and AppContainer's auto-restore guard:
  the guard reads `historyRef.current` which doesn't yet contain
  this INFO (React batches), and the guard's correctness depends on
  the items added here staying synthetic.

* historyUtils.ts — make `isSyntheticHistoryItem` exhaustive over the
  35-member `HistoryItemWithoutId` union. Every case is explicit; the
  default branch carries a `_exhaustive: never` so adding a new
  HistoryItem variant without classifying it triggers a compile-time
  error rather than silently disabling auto-restore. Runtime fallback
  is "meaningful" (safe — bail rather than wipe content).

Tests: +1 logger case (non-USER failure preserves the USER tracker),
+1 useGeminiStream case (throwing handler still flushes Responding).

All touched suites pass: 47 logger, 9 historyUtils, 86 useGeminiStream,
64 AppContainer.

* docs(core): clarify Logger writeQueue scope (log-history only, not checkpoints)

Reword the comment above `writeQueue` and the `serialize()` JSDoc to
state explicitly that the queue only serializes log-history mutations
(`logMessage` / `removeLastUserMessage`). Checkpoint ops
(saveCheckpoint / deleteCheckpoint / loadCheckpoint) touch separate
files and intentionally don't share this queue, so the previous
"every disk-mutating op chains here" wording overstated the
guarantee.

* fix(cli): flush buffered stream events before snapshotting pendingItem on cancel

Stream content/thought events are throttled into a per-turn `bufferedEvents`
array; only when `flushBufferedStreamEvents` runs do they reach
`pendingHistoryItemRef.current`. Snapshotting BEFORE the flush meant cancels
that fired inside the throttle window (60ms) saw a null `pendingItem` even
when meaningful text was sitting in the buffer. AppContainer's auto-restore
guard then read null, decided "model produced nothing", and called
`truncateToItem` — which silently wiped the very content that the
subsequent `addItem(pendingHistoryItemRef.current)` had just committed.

Move the snapshot to AFTER the flush so it sees the same value as the
addItem call directly below it.

Regression test: yields a content event and cancels without advancing
fake timers, asserts `info.pendingItem` carries the buffered "partial
response" text rather than null.

* fix(core): apply Logger.removeLastUserMessage in-memory removal synchronously

AppContainer's `userMessages` effect calls `getPreviousUserMessages()`
on the same render that history truncation fires (it depends on
`historyManager.history`). The previous implementation only updated
`this.logs` after `await fs.writeFile(...)` settled, so the effect
read stale logs and ↑-history surfaced the cancelled prompt until
some unrelated future history change re-ran the effect.

Move the cache filter ahead of the serialize queue so consumers see
the removal immediately. The async serialize op continues to read,
splice, and write disk, then re-syncs `this.logs` from disk on
success or rotation.

Regression test fires removeLast without awaiting, then asserts the
very next `getPreviousUserMessages()` returns [] (no cancelled
prompt), and that the background promise still resolves to true.

* docs(core, cli): clarify removeLastUserMessage contract; observability for cancel-undo

* logger.ts — extend the JSDoc on `removeLastUserMessage` to spell
  out the two-phase semantics (sync optimistic in-memory removal +
  async serialized disk reconciliation), and explicitly document that
  the boolean return value reflects the *disk* outcome while the
  in-memory cache is updated unconditionally. Also explain why disk
  failures are NOT rolled back: rolling back would resurrect the
  cancelled prompt in ↑-history, which is worse UX than a temporary
  cache/disk divergence (which converges on next op or on
  `initialize()` of the next session).

* AppContainer.tsx — wrap the fire-and-forget
  `logger.removeLastUserMessage()` in `.catch(debugLogger.debug)`.
  The Logger's internal try/catches mean the Promise should never
  reject today, but a future code-path change shouldn't surface as
  an UnhandledPromiseRejection — and a debug-level log is the right
  observability hook for "cancel succeeded in UI but disk-undo
  failed silently".

* fix(core,cli): #4023 review wave — logger atomicity + observable undo failure

3 #4023 review threads addressed:

- core/logger.ts: `removeLastUserMessage` now ROLLS BACK the
  optimistic in-memory removal when the disk read or write fails.
  Previously the JSDoc/return contract was violated: the method
  returned `false` on failure but `this.logs` already showed the
  entry removed — callers (AppContainer's `userMessages` effect)
  saw the inconsistency and the cancelled prompt vanished from
  ↑-history despite the disk still carrying it. The rollback
  re-inserts the target at its original index when no concurrent
  mutation took its place, and restores `lastLoggedUserEntry` so
  a follow-up retry has a target. Regression test pinned: spy on
  fs.writeFile to throw, assert `removed === false` AND
  getPreviousUserMessages() still surfaces the entry.

- cli/AppContainer.tsx: `void logger?.removeLastUserMessage()` no
  longer silently swallows failures. Added `.catch` that routes
  through `debugLogger.debug` so a disk-write failure leaves a
  diagnostic trail; without it the cancelled prompt would
  resurrect next session via ↑-history with no observability into
  why.

- cli/historyUtils.ts: `gemini_thought` / `gemini_thought_content`
  classification reaffirmed as SYNTHETIC with explicit JSDoc on
  WHY (Claude Code parity + auto-restore is most valuable in the
  cancel-during-thinking case which is exactly the case where
  thoughts have appeared but no committed `gemini_content`).
  Future readers won't re-litigate the classification by accident.

Tests: 49/49 logger.test.ts pass; tsc + ESLint clean.

* docs(core): align removeLastUserMessage JSDoc with rollback-on-failure behaviour

The previous commit added a rollback path to `removeLastUserMessage`
(re-insert the optimistically-removed entry and restore
`lastLoggedUserEntry` when the disk read or write throws), but the
JSDoc still said the in-memory removal is "intentionally NOT rolled
back" — a copy-paste leftover from the earlier design that picked
optimistic-and-diverge. Rewrite the failure-handling paragraph and
`@returns` line to describe the rollback contract instead.

No code change.

* fix(cli, core): scope auto-restore to the cancelled turn + tighten typings/tests

Three follow-ups from PR #4023 review batch 5.

* cli — `CancelSubmitInfo` gains `lastTurnUserItem` carrying the user
  prompt text that THIS turn's `prepareQueryForGemini` added (or
  `null` for paths that don't push a user history item: Cron /
  Notification / slash `submit_prompt`). `cancelOngoingRequest`
  snapshots `lastTurnUserItemRef.current` and ships it through. The
  AppContainer auto-restore guard now requires
  `info.lastTurnUserItem` to be present AND match the candidate
  user item's text before truncating/rewinding — closing the case
  where an older user item happens to be followed by only-synthetic
  trailing content and the current cancelled turn never owned a
  user item to begin with.

  Two new regression tests pin both halves: cancel of a non-USER
  turn bails despite trailing-synthetic, and a deliberate text
  mismatch also bails.

* cli — `.catch((err)` widened to `(err: unknown)` on the
  fire-and-forget `logger.removeLastUserMessage()` call. Belt-and-
  braces: `Promise.catch`'s lib typing is `(reason: any) =>` so
  this is not currently TS7006, but tightening keeps the codebase
  ready for `@typescript-eslint/no-implicit-any-catch`-style rules
  and matches the rest of the codebase's strict-error patterns.

* core — Added a `removeLastUserMessage` regression test pinning
  the `_readLogFile` failure branch (mocks `fs.readFile` to throw
  Permission denied). The symmetric `writeFile` failure case was
  already covered; this closes the gap on the read leg.

Tests: AppContainer 67/67 (+2), useGeminiStream 87/87, historyUtils 11/11,
logger 50/50 (+1). Type-check and lint clean.

* chore(cli): add debug observability for each auto-restore-on-cancel bail-out

The cancel handler in AppContainer has seven independent guards that
silently `return` when auto-restore is unsafe (buffer non-empty, queue
non-empty, pending meaningful content, no last-turn user item, no user
in history, trailing items not all synthetic, candidate-text mismatch).
Until now, users reporting "I pressed ESC but my prompt didn't come
back" had no way to know which guard tripped without a debugger.

Log a specific `debugLogger.debug(...)` line at each bail-out and one
on the success path. Debug level keeps production output silent;
re-enableable by running with `DEBUG=1` (per existing convention in
this file). No control-flow change.

* docs(core): scope removeLastUserMessage's "false ⇒ observable in-memory" guarantee

The previous JSDoc implied the guarantee held for every `false` return,
but it only really holds on the disk read/write THROW path (where we
roll back the optimistic in-memory removal). Two other `false`-paths
behave differently:

  - Initial guards (logger uninitialized / no tracked entry): nothing
    was ever removed, nothing to restore — entry stays in whatever
    state it was already in.
  - Disk read succeeds but the tracked row is missing on disk (e.g. a
    concurrent rotation/clear): we adopt disk state into `this.logs`,
    so both sides agree the entry is gone — `false` is returned but
    the entry is NOT observable in-memory either.

Rewrite the failure-handling paragraph and `@returns` line to spell
out both branches explicitly. No code change.

* fix(core): shift lastLoggedUserEntry on USER logMessage duplicate-skip

When `_updateLogFile` detects another instance already wrote an
identical (sessionId, messageId, timestamp, message) row and returns
null, the previous logMessage code path left `lastLoggedUserEntry`
pointing at the prior USER entry. A subsequent cancel/auto-restore
would then call `removeLastUserMessage()` and silently delete the
wrong row — typically an older prompt that the user did not intend
to undo.

The fix: when the duplicate skip happens on a USER attempt, advance
`lastLoggedUserEntry` to the entry object we just tried to write.
`_updateLogFile` mutates that object's `messageId` in-place to align
with the disk row before the duplicate check, so the 5-tuple matches
the row that's actually on disk and an undo correctly targets it.

The natural race (`max+1` colliding with an existing `messageId`)
is not reachable by sequential awaits — the snapshot used for the
duplicate check is always max+1-strict. The regression test drives
the contract directly by mocking `_updateLogFile` to resolve to
null and asserting `lastLoggedUserEntry` shifts to the new entry.

* fix(cli, core): strip orphan user entry from chat history on auto-restore

The auto-restore branch was cleaning up two of the three places a
cancelled prompt lives — the UI transcript via `truncateToItem` and
the disk-backed ↑-history via `Logger.removeLastUserMessage`. The
third — the in-memory chat history on `GeminiChat` — was left
untouched. `sendMessageStream` appends the user content to
`chat.history` BEFORE the stream generator runs and the abort path
doesn't pop it. After a successful auto-restore the next request's
wire payload still carried the cancelled prompt as a leading user
turn alongside the new prompt, so the model saw context the user
believed had been undone (and in some shapes the API would reject
two consecutive user turns).

Mirror the existing strip the Retry submit path uses
(`GeminiClient.sendMessageStream` at the `Retry` branch): make
`GeminiClient.stripOrphanedUserEntriesFromHistory` public and call
it from the auto-restore success path, sitting next to the UI
truncate and the disk-log undo. The method already pops trailing
user entries and clears the `FileReadCache` (which can otherwise
hold dangling `read_file` results from the stripped turn).

End-to-end reproduction from the PR review:
1. Submit `what time is it?` → ESC during pre-token delay →
   auto-restore (UI rewound, buffer pre-filled).
2. Edit buffer to `what year is it?` → submit.
3. Pre-fix: outbound `messages` carried both prompts as consecutive
   user turns. Post-fix: only the new prompt.

Test: extend the auto-restore-success AppContainer test with a
mock `stripOrphanedUserEntriesFromHistory` spy and assert it fires.
The non-restore branches don't install the spy (it's optionally
chained at the call site).

* fix(core, cli): tighten Logger.serialize signature + pin lastTurnUserItem and dup-skip identity contracts

Three follow-ups from PR review batch:

* core/logger.ts — `serialize()` was `this.writeQueue.then(op, op)`.
  The second callback was dead code: `writeQueue` is seeded with
  `Promise.resolve()` and reassigned through `.catch(() => undefined)`,
  so the queue tail can never reject. Worse, `then(op, op)` reads as
  "retry op on rejection" — wrong intent. Switch to `.then(() => op())`
  with a comment spelling out the no-reject invariant.

* cli/useGeminiStream.test.tsx — add ownership-contract tests at the
  PRODUCER side of `info.lastTurnUserItem`. Until now only the
  AppContainer tests pinned the contract, and they fabricate the
  value, so a regression that drops `lastTurnUserItemRef.current = {
  text: trimmedQuery }` in `prepareQueryForGemini` would slip
  through. New tests:
    - normal `UserQuery` submit → cancel → assert
      `info.lastTurnUserItem === { text: 'what time is it?' }`.
    - `SendMessageType.Notification` submit → cancel → assert
      `info.lastTurnUserItem === null` (path doesn't push a user
      history item, the ref reset at the top of
      prepareQueryForGemini must keep it null).

* core/logger.test.ts — strengthen the duplicate-skip regression.
  The previous test only checked the tracker advanced text; the
  important identity contract is that the recalculated 5-tuple
  matches the disk row, so a subsequent `removeLastUserMessage()`
  removes the duplicate-skipped row rather than the older USER.
  New test seeds disk with [first, second], stubs `_updateLogFile`
  for the second call to mimic the duplicate-skip branch (mutate
  newEntryObject's messageId+timestamp to align with the disk row,
  return null), then asserts removeLastUserMessage() leaves
  ['first'] on disk and removes 'second'.

* fix(cli, core): close four cancel-auto-restore correctness gaps from PR review

Four critical findings from gpt-5.5 /review pass:

1. **Retry skipped the lastTurnUserItem reset** (useGeminiStream.ts)
   `Retry` bypasses `prepareQueryForGemini`, which is where the
   `lastTurnUserItemRef.current = null` reset lived. A retry that
   followed a normal `UserQuery` carried the stale ownership snapshot
   into `onCancelSubmit`, and cancelling the retry before any
   meaningful output let `AppContainer` auto-restore truncate the
   original failed prompt. Move the reset (and the new content-seen
   reset, see #4) to the top of `submitQuery`, gated only on
   "this is a top-level submit" — covers Retry, Cron, Notification,
   and ordinary UserQuery alike.

2. **Text-only ownership matched dedup'd duplicates** (AppContainer.tsx,
   useGeminiStream.ts) `useHistoryManager.addItem` skips inserting a
   consecutive-duplicate user message while still returning a freshly
   generated id. The text-only ownership check would match the OLDER
   identical-text USER row, so a re-submitted same prompt + cancel
   would wrongly truncate the prior turn. Carry id+text in
   `CancelSubmitInfo.lastTurnUserItem` (using `addItem`'s return
   value) and require both id AND text to match before truncating.

3. **stripOrphan left IDE context state advanced** (client.ts) Other
   history-mutating paths (`setHistory`, `truncateHistory`) set
   `forceFullIdeContext = true` after mutating; the orphan-strip
   didn't, so a subsequent request could send a diff against a
   removed baseline. Gate cache-clear + IDE-context invalidation on
   an actual before/after length drop, so no-op strips don't churn
   state.

4. **Flush-then-thought race let auto-restore wipe committed content**
   (useGeminiStream.ts, AppContainer.tsx) `cancelOngoingRequest`'s
   pre-cancel flush can `addItem` a meaningful `gemini_content` (via
   handleContentEvent's split path) and then a later thought event
   overwrites `pendingHistoryItem` with a synthetic value. The
   AppContainer guard's React history snapshot is stale, so the
   trailing-only-synthetic check passes and the just-committed text
   gets truncated. Track a synchronous `turnSawContentEventRef` set
   in handleContentEvent, ship it through `CancelSubmitInfo`, and
   make the guard bail when set.

Tests:
- core/client.test.ts: stripOrphan only forces full IDE context on
  actual removal; existing retry tests updated to mock
  `getHistoryLength`.
- cli/useGeminiStream.test.tsx: ownership uses { id, text }, Retry
  reset works after a prior UserQuery cancel,
  turnProducedMeaningfulContent flips true when content lands.
- cli/AppContainer.test.tsx: guard bails on `turnProducedMeaningfulContent: true`,
  guard bails on id mismatch (catches addItem dedup case).

cli 162/162 + core client 99/99 + core logger 51/51.

* fix(cli): repaint static transcript after auto-restore truncate

Reported by @tanzhenxin: auto-restore truncated React `history` state
but the cancelled `> prompt` and `Request cancelled.` lines stayed
printed in the terminal — Ink's `<Static>` region is append-only, so
shrinking the underlying array doesn't unprint already-flushed lines.
On the PR's golden path (type prompt → Enter → ESC) the user sees the
prompt twice: once in scrollback, once pre-filled in the input buffer.
Confirmed at multiple Enter-to-ESC delays, so it's not a timing fluke.

Call `refreshStatic()` immediately after `truncateToItem(...)` in the
auto-restore success path. `refreshStatic` writes the ANSI
clear-terminal escape AND bumps the static remount key — the exact
recipe `/clear` (`handleClearScreen`) already uses for the same
reason. The targeted-repaint helper used for terminal resizes is
intentionally NOT used here: it preserves scrollback, which would
leave the cancelled prompt visible above the new viewport.

Test: extend the existing auto-restore happy-path AppContainer test
to assert `mockStdout.write` was called with `ansiEscapes.clearTerminal`.
The other auto-restore-bail tests don't install the assertion so they
naturally verify the negative case (no clear when guard rejects).
xaelistic pushed a commit to xaelistic/qwen-code that referenced this pull request Jun 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type/bug Something isn't working as expected

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants