Skip to content

feat(tui): add daemon adapter spike#4202

Merged
wenshao merged 8 commits into
mainfrom
feat/tui-daemon-adapter-draft
May 18, 2026
Merged

feat(tui): add daemon adapter spike#4202
wenshao merged 8 commits into
mainfrom
feat/tui-daemon-adapter-draft

Conversation

@chiga0

@chiga0 chiga0 commented May 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • What changed: Added a locally verifiable DaemonTuiAdapter spike that reduces daemon SSE frames into TUI-consumable updates and forwards prompt, cancel, model switch, and permission votes through a session-scoped daemon client.
  • Why it changed: TUI is one of the first Mode B clients we want to dogfood against qwen serve HTTP + SSE, but the migration needs a default-off adapter layer before touching the existing Ink runtime.
  • Reviewer focus: Event mapping scope, compatibility with current TUI history/control shapes, and the fact that this is not wired into the default interactive TUI path yet.

Validation

  • Commands run:
    cd packages/cli && npx vitest run src/ui/daemon/DaemonTuiAdapter.test.ts
    cd packages/cli && npm run typecheck
    cd packages/cli && npx eslint src/ui/daemon/DaemonTuiAdapter.ts src/ui/daemon/DaemonTuiAdapter.test.ts --max-warnings 0 --no-warn-ignored
    cd packages/cli && npm run build
    cd packages/cli && npx prettier --check src/ui/daemon/DaemonTuiAdapter.ts src/ui/daemon/DaemonTuiAdapter.test.ts ../../docs/developers/daemon-client-adapters/tui.md
    git diff --check
  • Prompts / inputs used: Unit tests use synthetic daemon events and a fake daemon session.
  • Expected result: Adapter maps supported daemon events and forwards client actions without changing existing TUI behavior.
  • Observed result: Targeted vitest passed with 4 tests. Typecheck, targeted ESLint, package build, Prettier check, and diff check passed.
  • Quickest reviewer verification path: cd packages/cli && npx vitest run src/ui/daemon/DaemonTuiAdapter.test.ts.
  • Evidence: Test output reports src/ui/daemon/DaemonTuiAdapter.test.ts (4 tests) and Test Files 1 passed.

Scope / Risk

  • Main risk or tradeoff: This is a reducer/transport spike only. It proves the daemon-facing TUI adapter surface but does not yet start an experimental Ink app path.
  • Not covered / not validated: No live qwen serve smoke, no flag/env parsing, no default TUI wiring, no JSONL/stream-json/dual-output behavior changes, no output sink migration.
  • Breaking changes / migration notes: None. Existing interactive TUI, JSONL, stream-json, and dual-output paths are unchanged.

Engineering principles checklist (Stage 1.5 wave PRs)

  • Independently mergeable (main stays releasable after merge)
  • Backward compatible (no removed routes / event fields / CLI behavior)
  • Default off (feature flag or dual-stack; old path unchanged)
  • qwen serve Stage 1 routes / SDK behavior preserved
  • Gradual migration (P0/P1 base before adapters; behind flag first)
  • Reversible (this PR can be individually rolled back)
  • Tests-first (unit / smoke / e2e where relevant)

Linked Issues / Bugs

Related to #4175 Wave 5 adapter track and #4201.

@github-actions

Copy link
Copy Markdown
Contributor

📋 Review Summary

This PR introduces a draft design document for a flag-gated TUI daemon adapter that enables the TUI to communicate with qwen serve via DaemonSessionClient (Mode B). The document is well-structured, clearly scoped as a non-implementation draft, and appropriately cautious about migration safety. Overall assessment: ready to merge as a planning artifact.

🔍 General Feedback

🎯 Specific Feedback

🔵 Low

File: docs/developers/daemon-client-adapters/tui.md:17-22 - Consider adding a note about how users would discover this experimental flag. Suggestion: Add a brief mention that --help output or a warning message should indicate this is experimental, e.g., "Experimental flag: may change or be removed without notice."

File: docs/developers/daemon-client-adapters/tui.md:26-33 - The capability check list (session_create, session_prompt, session_events) is good. Consider whether session_cancel, session_setModel, and session_respondToPermission should also be required capabilities, since they're used in the Minimal Flow (steps 6-8). Either add them to the requirement or clarify why they're optional.

File: docs/developers/daemon-client-adapters/tui.md:36-45 - The Minimal Flow section would benefit from explicit error handling paths. Consider adding:

  • What happens if capability check fails?
  • What happens if createOrAttach() fails (e.g., daemon not running)?
  • What's the user-facing error message in each case?

File: docs/developers/daemon-client-adapters/tui.md:47-59 - The rendering contract table is excellent. Consider adding one more row for error states:
| session_error or daemon disconnect | Show error toast/banner and offer reconnect |

This would make the failure mode explicit rather than relying only on session_died.

File: docs/developers/daemon-client-adapters/tui.md:80-85 - The validation plan mentions "reconnect sends the tracked Last-Event-ID" but this is the first mention of reconnection logic. Consider moving this to the "Minimal Flow" or adding a brief "Reconnection" subsection to make this requirement visible earlier.

✅ Highlights

  • Outstanding documentation quality: The document follows a clear, professional structure with appropriate sections for goals, entry points, flow, rendering, non-goals, safety, validation, and blockers
  • Defensive design: The "Merge Safety" checklist is exemplary—ensuring default-off, additive paths, no flag behavior changes, and graceful failure
  • Clear blocker identification: The "Blockers Before Default Migration" section provides excellent forward visibility into what needs to be stable before this becomes the default path
  • Appropriate draft status: This is exactly the right kind of work to submit as a draft PR—enables async design review before implementation begins
  • Good event mapping scope: The initial event mapping is appropriately minimal, focusing on core streaming and permission flows rather than trying to support all daemon features at once

@chiga0 chiga0 changed the title docs(tui): draft daemon adapter plan feat(tui): add daemon adapter spike May 16, 2026
Comment thread packages/cli/src/ui/daemon/DaemonTuiAdapter.ts Outdated
Comment thread packages/cli/src/ui/daemon/DaemonTuiAdapter.ts
Comment thread packages/cli/src/ui/daemon/DaemonTuiAdapter.ts
Comment thread packages/cli/src/ui/daemon/DaemonTuiAdapter.ts Outdated
Comment thread packages/cli/src/ui/daemon/DaemonTuiAdapter.ts
Comment thread packages/cli/src/ui/daemon/DaemonTuiAdapter.ts Outdated
Comment thread packages/cli/src/ui/daemon/DaemonTuiAdapter.ts
Comment thread packages/cli/src/ui/daemon/DaemonTuiAdapter.ts
Comment thread packages/cli/src/ui/daemon/DaemonTuiAdapter.ts
Comment thread packages/cli/src/ui/daemon/DaemonTuiAdapter.ts
Comment thread packages/cli/src/ui/daemon/DaemonTuiAdapter.ts Outdated

export function reduceDaemonEventToTuiUpdates(
event: DaemonTuiEvent,
): DaemonTuiUpdate[] {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] The daemon carries critical data in the _meta field: token usage (usage), turn duration (durationMs), and stop-hook-loop signals. The adapter never reads _meta. This means:

  1. Stop-hook-loop warnings are invisible — the safety mechanism that prevents infinite agent loops is silently dropped.
  2. Token usage stats are lost — the daemon TUI would never show usage data that the in-process TUI displays after every turn.
  3. The agent_message_chunk branch requires && text (falsy empty strings are dropped), so _meta-only events like stop-hook-loop are silently discarded.

Consider extracting _meta and surfacing it, or at minimum passing it through on the DaemonTuiUpdate type for consumers to handle.

— glm-5.1 via Qwen Code /review

Comment thread packages/cli/src/ui/daemon/DaemonTuiAdapter.ts
Comment thread packages/cli/src/ui/daemon/DaemonTuiAdapter.ts
Comment thread packages/cli/src/ui/daemon/DaemonTuiAdapter.ts
@chiga0 chiga0 requested a review from doudouOUC May 16, 2026 15:08
@chiga0 chiga0 force-pushed the feat/tui-daemon-adapter-draft branch from 718bd20 to ca12542 Compare May 16, 2026 15:09
@chiga0

chiga0 commented May 16, 2026

Copy link
Copy Markdown
Collaborator Author

处理了这轮 review:

  • fixed: lastEventId getter 优先使用动态 lastSeenEventId
  • fixed: stop() 等待 pump 退出,pump finally 清理 eventPump/eventController,避免 stop/start 双 pump
  • fixed: session.events() 传入 lastEventId/resume,支持断点续传
  • fixed: stream 正常结束和非 abort error 都会发 disconnected,不再僵死
  • fixed: sendPrompt 失败时发 disconnected,并且不再伪造 turn_complete
  • fixed: user_message_chunk 不再回灌到 TUI,避免本地输入 double echo
  • fixed: permission_request 校验 requestId/toolCall/options 后再下发
  • fixed: tool_call_update 按 callId 累积,新增 tool_group_update 给接入层更新已有 tool group,不再每个状态生成一个独立工具条目
  • fixed: structured resultDisplay 透传 FileDiff/Todo/Plan/Agent/Ansi/MCP progress 形态,未知 rawOutput 才 JSON stringify
  • fixed: unknown tool status 映射为 Error;session_died/error reason 做 ANSI/control char 清洗和长度限制

边界/后续项:

  • confirmationDetails 不是纯数据,带本地 callback,不能直接跨 daemon 序列化;当前通过 permission_request 驱动权限 UI,后续需要 daemon 定义可序列化 confirmation contract 后再完整映射。
  • _meta usage/duration/stop-hook-loop 和真正的 turn-complete/end-of-turn 事件还需要 daemon 协议补齐;本 PR 不伪造顺序,避免 RPC 先于 SSE 内容时出现错误 turn_complete。

验证:

  • cd packages/cli && npx vitest run src/ui/daemon/DaemonTuiAdapter.test.ts
  • cd packages/cli && npm run typecheck
  • cd packages/cli && npx eslint src/ui/daemon/DaemonTuiAdapter.ts src/ui/daemon/DaemonTuiAdapter.test.ts --max-warnings 0 --no-warn-ignored
  • cd packages/cli && npm run build
  • prettier check passed

@chiga0 chiga0 force-pushed the feat/daemon-session-client branch from 4f49825 to 1fe2b04 Compare May 16, 2026 15:29
Comment thread packages/sdk-typescript/src/daemon/DaemonSessionClient.ts
Comment thread packages/cli/src/ui/daemon/DaemonTuiAdapter.ts Outdated
Comment thread packages/cli/src/ui/daemon/DaemonTuiAdapter.ts Outdated
@chiga0 chiga0 force-pushed the feat/daemon-session-client branch from 1fe2b04 to da6a35c Compare May 16, 2026 16:40
@chiga0 chiga0 force-pushed the feat/tui-daemon-adapter-draft branch from 0481f0e to 56ece34 Compare May 17, 2026 02:03
@chiga0 chiga0 changed the base branch from feat/daemon-session-client to main May 17, 2026 02:03
@chiga0

chiga0 commented May 17, 2026

Copy link
Copy Markdown
Collaborator Author

Follow-up update after rebasing this PR to main:

  • Rebased/retargeted onto main; PR is now mergeable and remains draft while CI runs.
  • Addressed the valid adapter review comments that can be fixed client-side:
    • stop() waits for the event pump and stream failures clean up pump/controller state;
    • event subscription uses lastEventId/resume;
    • prompt failures emit a sanitized disconnect without fabricating turn completion;
    • user-message echo is ignored to avoid double rendering;
    • permission requests validate requestId/toolCall/options;
    • tool updates are accumulated by call id;
    • structured rawOutput is preserved, and tool failure text now falls back from daemon content when rawOutput is absent;
    • unknown tool statuses map to error;
    • session_died, client_evicted, and stream_error now surface sanitized disconnect reasons plus error history.

Local validation passed:

  • cd packages/cli && npx vitest run src/ui/daemon/DaemonTuiAdapter.test.ts
  • cd packages/cli && npm run typecheck
  • cd packages/cli && npx eslint src/ui/daemon/DaemonTuiAdapter.ts src/ui/daemon/DaemonTuiAdapter.test.ts --max-warnings 0 --no-warn-ignored
  • cd packages/cli && npx prettier --check src/ui/daemon/DaemonTuiAdapter.ts src/ui/daemon/DaemonTuiAdapter.test.ts
  • cd packages/cli && npm run build

I resolved the addressed threads. One real blocker remains open intentionally: daemon _meta / usage / stop-hook-loop / turn metadata needs a protocol-level event contract before the TUI adapter should claim parity with the in-process path.

@github-actions

github-actions Bot commented May 17, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Summary

Package Lines Statements Functions Branches
CLI 77.35% 77.35% 79.12% 80.33%
Core 79.27% 79.27% 81.92% 82.75%
CLI Package - Full Text Report
-------------------|---------|----------|---------|---------|-------------------
File               | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------------|---------|----------|---------|---------|-------------------
All files          |   77.35 |    80.33 |   79.12 |   77.35 |                   
 src               |   75.73 |    69.15 |   80.55 |   75.73 |                   
  gemini.tsx       |   68.53 |     66.4 |   76.47 |   68.53 | ...29,946-949,957 
  ...ractiveCli.ts |      80 |    68.61 |   78.57 |      80 | ...1020,1058,1161 
  ...liCommands.ts |   74.51 |     72.5 |     100 |   74.51 | ...41-265,290,391 
  ...ActiveAuth.ts |     100 |     87.5 |     100 |     100 | 66-80             
 ...cp-integration |   67.53 |    66.53 |   79.03 |   67.53 |                   
  acpAgent.ts      |   69.38 |    66.66 |   84.21 |   69.38 | ...1691,1705-1713 
  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.97 |    72.12 |   86.25 |   76.97 |                   
  ...ryReplayer.ts |   67.34 |     75.6 |   81.81 |   67.34 | ...54-269,282-283 
  Session.ts       |   76.32 |    70.86 |   88.46 |   76.32 | ...2537,2543-2546 
  ...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.7 |    94.81 |   95.45 |    97.7 |                   
  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 |                   
  modelscope.ts    |     100 |      100 |     100 |     100 |                   
  zai.ts           |     100 |      100 |     100 |     100 |                   
 src/commands      |   55.55 |    85.71 |   43.47 |   55.55 |                   
  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          
  serve.ts         |    10.3 |      100 |       0 |    10.3 | ...48-123,125-164 
 ...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.5 |    88.95 |   81.81 |    84.5 |                   
  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.89 |    85.31 |   88.09 |   92.89 |                   
  auth.ts          |   86.98 |    80.32 |     100 |   86.98 | ...26-227,243-244 
  config.ts        |   88.68 |     85.1 |      80 |   88.68 | ...1826,1828-1836 
  keyBindings.ts   |   96.55 |       50 |     100 |   96.55 | 193-196           
  ...idersScope.ts |      92 |       90 |     100 |      92 | 11-12             
  sandboxConfig.ts |   61.64 |    71.87 |   66.66 |   61.64 | ...54-68,73,77-89 
  settings.ts      |   85.76 |    87.25 |   89.18 |   85.76 | ...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          |   81.47 |    75.94 |   65.71 |   81.47 |                   
  index.ts         |   63.68 |    69.56 |   53.84 |   63.68 | ...70-271,281-286 
  languages.ts     |   96.92 |    86.66 |     100 |   96.92 | 134-135,167,184   
  ...nslateKeys.ts |     100 |      100 |     100 |     100 |                   
  ...lationDict.ts |   93.33 |    66.66 |     100 |   93.33 | 15                
 src/i18n/locales  |     100 |      100 |     100 |     100 |                   
  ca.js            |     100 |      100 |     100 |     100 |                   
  de.js            |     100 |      100 |     100 |     100 |                   
  en.js            |     100 |      100 |     100 |     100 |                   
  fr.js            |     100 |      100 |     100 |     100 |                   
  ja.js            |     100 |      100 |     100 |     100 |                   
  pt.js            |     100 |      100 |     100 |     100 |                   
  ru.js            |     100 |      100 |     100 |     100 |                   
  zh-TW.js         |     100 |      100 |     100 |     100 |                   
  zh.js            |     100 |      100 |     100 |     100 |                   
 ...nonInteractive |   72.57 |    71.12 |   74.07 |   72.57 |                   
  session.ts       |   76.64 |     69.4 |   85.71 |   76.64 | ...23-824,833-843 
  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.21 |      100 |       0 |    5.21 | ...21-433,442-471 
 .../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/serve         |   84.45 |    81.94 |   94.11 |   84.45 |                   
  auth.ts          |   88.49 |    88.37 |    87.5 |   88.49 | ...49-150,153-155 
  capabilities.ts  |     100 |     90.9 |     100 |     100 | 153               
  envSnapshot.ts   |    92.3 |       84 |     100 |    92.3 | 108-111,170-177   
  eventBus.ts      |   88.88 |    89.23 |   85.71 |   88.88 | ...38-446,524-526 
  httpAcpBridge.ts |    81.4 |    77.88 |   97.82 |    81.4 | ...4188,4219-4260 
  ...oryChannel.ts |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  loopbackBinds.ts |     100 |      100 |     100 |     100 |                   
  runQwenServe.ts  |   79.74 |    87.09 |   83.33 |   79.74 | ...51-467,492-494 
  server.ts        |   85.63 |    83.51 |    87.5 |   85.63 | ...1529,1594-1603 
  status.ts        |   98.33 |    96.66 |     100 |   98.33 | 365-366           
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/services      |   91.67 |    91.21 |   97.56 |   91.67 |                   
  ...mandLoader.ts |     100 |    93.75 |     100 |     100 | 93                
  ...killLoader.ts |     100 |    96.15 |     100 |     100 | 47                
  ...andService.ts |    98.7 |      100 |     100 |    98.7 | 107               
  ...mandLoader.ts |   86.83 |    83.87 |     100 |   86.83 | ...30-335,340-345 
  ...omptLoader.ts |   75.84 |    80.64 |   83.33 |   75.84 | ...10-211,277-278 
  ...mandLoader.ts |     100 |      100 |     100 |     100 |                   
  ...nd-factory.ts |   91.42 |    91.66 |     100 |   91.42 | 128,137-144       
  ...ation-tool.ts |     100 |    95.45 |     100 |     100 | 125               
  ...ndMetadata.ts |   98.21 |    96.66 |     100 |   98.21 | 83,87             
  commandUtils.ts  |      96 |     90.9 |     100 |      96 | 48                
  ...and-parser.ts |   90.69 |    85.71 |     100 |   90.69 | 63-66             
  ...ionService.ts |     100 |      100 |     100 |     100 |                   
  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            |   66.51 |    73.28 |   57.89 |   66.51 |                   
  App.tsx          |     100 |      100 |     100 |     100 |                   
  AppContainer.tsx |   65.03 |    64.98 |   52.94 |   65.03 | ...2951,2955-2959 
  ...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 |    97.05 |     100 |   95.91 | 25-26             
  ...tic-colors.ts |     100 |      100 |     100 |     100 |                   
  ...inePresets.ts |   98.17 |    88.88 |     100 |   98.17 | ...12,239,387-389 
  textConstants.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/ui/auth       |   55.06 |    51.13 |   35.48 |   55.06 |                   
  AuthDialog.tsx   |   64.26 |    44.44 |   16.66 |   64.26 | ...59,366-388,392 
  ...nProgress.tsx |       0 |        0 |       0 |       0 | 1-64              
  ...etupSteps.tsx |    39.5 |       32 |   38.46 |    39.5 | ...69,472,478,481 
  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   |   73.46 |    81.23 |   81.61 |   73.46 |                   
  aboutCommand.ts  |     100 |      100 |     100 |     100 |                   
  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 |    76.47 |     100 |      92 | 43-44,72-73,91-92 
  ...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 |   95.06 |    88.28 |     100 |   95.06 | ...92-293,320-321 
  dreamCommand.ts  |      75 |    66.66 |   66.66 |      75 | 22-27,44-47       
  editorCommand.ts |     100 |      100 |     100 |     100 |                   
  exportCommand.ts |   98.25 |    91.02 |     100 |   98.25 | ...81,198-199,364 
  ...onsCommand.ts |   48.66 |     90.9 |   63.63 |   48.66 | ...05-109,159-211 
  forgetCommand.ts |   26.82 |      100 |      50 |   26.82 | 18-51             
  goalCommand.ts   |   91.25 |    83.33 |      90 |   91.25 | ...83-186,198-201 
  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 |   92.17 |    82.69 |     100 |   92.17 | ...43,164,173-183 
  lspCommand.ts    |     100 |    86.95 |     100 |     100 | 31,101-102        
  ...elsCommand.ts |     100 |      100 |     100 |     100 |                   
  mcpCommand.ts    |     100 |      100 |     100 |     100 |                   
  memoryCommand.ts |     100 |      100 |     100 |     100 |                   
  modelCommand.ts  |   75.09 |    78.18 |      75 |   75.09 | ...20-225,262-267 
  ...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.71 |    86.04 |     100 |   85.71 | ...02-209,216-221 
  ...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.22 |    72.13 |     100 |   77.22 | ...46-150,172-177 
  ...tupCommand.ts |     100 |      100 |     100 |     100 |                   
  themeCommand.ts  |     100 |      100 |     100 |     100 |                   
  toolsCommand.ts  |     100 |      100 |     100 |     100 |                   
  trustCommand.ts  |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
  vimCommand.ts    |   54.54 |      100 |      50 |   54.54 | 19-29             
 src/ui/components |   65.53 |    75.02 |   70.76 |   65.53 |                   
  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.2 |      100 |       0 |    12.2 | 64-490            
  ...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.54 |    54.54 |     100 |   79.54 | ...05-109,133-134 
  ...ngSpinner.tsx |   68.42 |       80 |      50 |   68.42 | 35-52,73,80-81    
  GoalPill.tsx     |   76.19 |    81.81 |     100 |   76.19 | 24-30,46-50       
  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 |    61.7 |       36 |     100 |    61.7 | ...42,345,348-354 
  ...ngeDialog.tsx |     100 |      100 |     100 |     100 |                   
  InputPrompt.tsx  |   82.75 |    78.96 |   83.33 |   82.75 | ...1425,1490,1540 
  ...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 |   71.05 |    69.11 |   72.72 |   71.05 | ...77,590,601-603 
  MemoryDialog.tsx |    55.1 |    54.54 |   57.14 |    55.1 | ...56,368,381-383 
  ...geDisplay.tsx |       0 |        0 |       0 |       0 | 1-41              
  ModelDialog.tsx  |   80.12 |    63.55 |     100 |   80.12 | ...39-555,612-616 
  ...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 |   92.64 |    85.71 |     100 |   92.64 | 102-106,134-139   
  PrepareLabel.tsx |   91.66 |    77.27 |     100 |   91.66 | 73-75,77-79,110   
  ...atePrompt.tsx |    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 |   41.26 |    61.53 |   71.42 |   41.26 | ...74-472,476-520 
  ...ionPicker.tsx |   78.43 |    66.66 |     100 |   78.43 | ...20-422,444-466 
  ...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 |   66.92 |    73.21 |     100 |   66.92 | ...12-820,826-827 
  ...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 |                   
  ...ineDialog.tsx |   93.69 |    83.92 |     100 |   93.69 | ...11,273,293-295 
  ...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 |   38.33 |    70.83 |   36.36 |   38.33 |                   
  ...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  |    87.8 |    27.27 |     100 |    87.8 | ...,85,98-106,124 
  ...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.63 |    84.49 |   85.29 |   75.63 |                   
  ...sksDialog.tsx |   70.92 |    80.48 |   76.19 |   70.92 | ...1118,1194-1196 
  ...TasksPill.tsx |   63.75 |    86.95 |     100 |   63.75 | 44,86-106,114-122 
  ...gentPanel.tsx |   99.53 |    93.18 |     100 |   99.53 | 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.88 |    94.23 |   66.66 |   54.88 |                   
  ...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.43 |    94.73 |      80 |   88.43 | 52-53,59-72,106   
  ...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 |   68.67 |    69.07 |   69.56 |   68.67 |                   
  ...etailStep.tsx |   74.68 |    66.66 |   66.66 |   74.68 | ...71-184,188-201 
  ...etailStep.tsx |    87.4 |    73.68 |     100 |    87.4 | 41-42,99-113,119  
  ...abledStep.tsx |     100 |      100 |     100 |     100 |                   
  ...sListStep.tsx |     100 |      100 |     100 |     100 |                   
  ...entDialog.tsx |   34.51 |    47.05 |   42.85 |   34.51 | ...78,482-495,499 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-13              
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...components/mcp |   20.98 |    86.36 |   83.33 |   20.98 |                   
  ...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         |   95.83 |    88.88 |     100 |   95.83 | 16,20,109-110     
 ...ents/mcp/steps |   26.74 |    54.54 |   42.85 |   26.74 |                   
  ...icateStep.tsx |    5.88 |      100 |       0 |    5.88 | 40-55,58-296      
  ...electStep.tsx |   10.95 |      100 |       0 |   10.95 | 16-88             
  ...etailStep.tsx |    5.26 |      100 |       0 |    5.26 | 31-247            
  ...rListStep.tsx |   75.18 |    59.37 |     100 |   75.18 | ...53-158,169-173 
  ...etailStep.tsx |   10.41 |      100 |       0 |   10.41 | ...1,67-79,82-139 
  ToolListStep.tsx |   69.02 |       50 |     100 |   69.02 | ...22,125,134-143 
 ...nents/messages |   82.44 |    79.55 |    72.6 |   82.44 |                   
  ...ionDialog.tsx |   80.84 |     77.6 |    62.5 |   80.84 | ...98,516,534-536 
  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             
  ...usMessage.tsx |   76.31 |     42.1 |   66.66 |   76.31 | ...99,101,124,155 
  ...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 |   85.36 |    78.48 |   95.77 |   85.36 |                   
  ...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  |   84.31 |    74.19 |     100 |   84.31 | ...37,193-195,205 
  ...tonSelect.tsx |     100 |      100 |     100 |     100 |                   
  ...eSelector.tsx |     100 |       60 |     100 |     100 | 40-45             
  TextInput.tsx    |   77.01 |    48.78 |      80 |   77.01 | ...08-212,224-230 
  ...apsedTime.tsx |     100 |      100 |     100 |     100 |                   
  ...Indicator.tsx |     100 |      100 |     100 |     100 |                   
  text-buffer.ts   |   83.68 |    78.55 |   97.61 |   83.68 | ...2270-2272,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 |   21.51 |    59.52 |   27.27 |   21.51 |                   
  ...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 |   35.42 |    59.52 |     100 |   35.42 | ...20-432,437-439 
  ...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.11 |    77.66 |   80.35 |   77.11 |                   
  ...ewContext.tsx |    64.7 |    85.71 |      50 |    64.7 | ...22-225,231-241 
  AppContext.tsx   |      80 |       50 |     100 |      80 | 19-20             
  ...ewContext.tsx |   95.18 |    67.56 |      50 |   95.18 | ...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 | 113-114           
  ...teContext.tsx |   86.66 |       50 |     100 |   86.66 | 177-178           
  ...deContext.tsx |   76.08 |    72.72 |     100 |   76.08 | 47-48,52-59,77-78 
 src/ui/daemon     |   90.76 |    73.73 |   95.45 |   90.76 |                   
  ...TuiAdapter.ts |   90.76 |    73.73 |   95.45 |   90.76 | ...53,771-772,858 
 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      |   82.41 |    82.49 |   86.66 |   82.41 |                   
  ...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.75 |    63.01 |   61.53 |   75.75 | ...84,908,927-931 
  ...amingState.ts |   12.22 |      100 |       0 |   12.22 | 54-157            
  ...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.21 |    76.08 |     100 |   94.21 | 122-126,213,219   
  ...ketedPaste.ts |    23.8 |      100 |       0 |    23.8 | 19-37             
  ...nchCommand.ts |   94.36 |    74.35 |     100 |   94.36 | ...60,168-169,209 
  ...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 |   78.53 |    88.57 |     100 |   78.53 | ...96-104,112-113 
  ...ialogClose.ts |   15.38 |      100 |     100 |   15.38 | 83-148            
  ...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 |   77.38 |    74.63 |   91.66 |   77.38 | ...2465,2478-2486 
  ...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.08 |    83.33 |     100 |   97.08 | 103-104,133       
  ...ompletion.tsx |   90.59 |    83.33 |     100 |   90.59 | ...01,104,137-140 
  ...ectionList.ts |   96.98 |    95.69 |     100 |   96.98 | ...83-184,238-241 
  ...sionPicker.ts |   92.02 |    89.47 |     100 |   92.02 | ...99-501,503-505 
  ...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 |   97.67 |    91.66 |     100 |   97.67 | ...28-332,344-347 
  ...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.92 |    82.91 |   92.56 |   83.92 |                   
  ...Colorizer.tsx |   79.53 |    83.78 |     100 |   79.53 | ...51-152,249-275 
  ...nRenderer.tsx |   68.83 |    70.14 |      50 |   68.83 | ...52-254,274-293 
  ...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 |   92.08 |    80.45 |      95 |   92.08 | ...76-679,723-728 
  ...dWorkUtils.ts |     100 |      100 |     100 |     100 |                   
  ...boardUtils.ts |   59.61 |    58.82 |     100 |   59.61 | ...,86-88,107-149 
  commandUtils.ts  |    95.9 |    88.42 |     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.11 |       94 |     100 |   94.11 | 94-97             
  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 |                   
  ...ightLoader.ts |     100 |    89.47 |     100 |     100 | 81,110            
  ...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                
  osc8.ts          |   94.71 |    87.41 |     100 |   94.71 | ...43,428,432-433 
  ...mConstants.ts |     100 |      100 |     100 |     100 |                   
  restoreGoal.ts   |   98.98 |    97.05 |     100 |   98.98 | 98                
  ...storyUtils.ts |   61.89 |    69.87 |      90 |   61.89 | ...76,424,429-451 
  ...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         |   76.06 |    89.52 |   93.82 |   76.06 |                   
  acpModelUtils.ts |     100 |      100 |     100 |     100 |                   
  apiPreconnect.ts |   96.72 |    97.14 |     100 |   96.72 | 165-168           
  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.9 |     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              
  ...iagnostics.ts |   94.57 |    83.01 |   88.88 |   94.57 | ...05,311,315-317 
  ...onfigUtils.ts |     100 |      100 |     100 |     100 |                   
  ...iveHelpers.ts |   96.79 |    93.28 |     100 |   96.79 | ...76-477,575,588 
  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.75 |   89.47 |   82.89 | ...52-663,670-678 
  spawnWrapper.ts  |     100 |      100 |     100 |     100 |                   
  ...upProfiler.ts |   98.46 |    94.52 |     100 |   98.46 | 130-131,305       
  ...upWarnings.ts |     100 |      100 |     100 |     100 |                   
  stdioHelpers.ts  |     100 |       60 |     100 |     100 | 23,32             
  systemInfo.ts    |   95.12 |    89.06 |     100 |   95.12 | ...43-244,249-253 
  ...InfoFields.ts |   87.61 |       65 |     100 |   87.61 | ...22-123,144-145 
  ...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          |   79.27 |    82.75 |   81.92 |   79.27 |                   
 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        |   87.58 |    79.07 |   91.76 |   87.58 |                   
  ...transcript.ts |   92.25 |    85.71 |     100 |   92.25 | ...87,306-307,438 
  ...ent-resume.ts |    82.5 |     71.5 |   77.41 |    82.5 | ...1035-1039,1042 
  ...ound-tasks.ts |    95.4 |    86.48 |     100 |    95.4 | ...55-756,827-828 
  index.ts         |     100 |      100 |     100 |     100 |                   
 src/agents/arena  |   76.54 |    66.87 |   78.72 |   76.54 |                   
  ...gentClient.ts |   79.47 |    88.88 |   81.81 |   79.47 | ...68-183,189-204 
  ArenaManager.ts  |   75.37 |    63.37 |   78.26 |   75.37 | ...1860,1866-1867 
  arena-events.ts  |   64.44 |      100 |      50 |   64.44 | ...71-175,178-183 
  diff-summary.ts  |    87.5 |    72.34 |     100 |    87.5 | ...32-133,137-138 
  index.ts         |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...gents/backends |   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.14 |     76.7 |   71.42 |   81.14 |                   
  agent-context.ts |     100 |      100 |     100 |     100 |                   
  agent-core.ts    |   76.49 |    72.35 |   60.86 |   76.49 | ...1608,1635-1682 
  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/agents/tasks  |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/config        |   78.16 |    81.13 |      65 |   78.16 |                   
  config.ts        |   75.91 |    79.78 |   60.09 |   75.91 | ...3605,3616-3628 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  models.ts        |     100 |      100 |     100 |     100 |                   
  storage.ts       |   95.01 |     90.9 |   90.47 |   95.01 | ...71-372,375-376 
 ...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          |   86.68 |    82.18 |   89.86 |   86.68 |                   
  baseLlmClient.ts |   92.35 |    80.85 |   86.66 |   92.35 | ...34,342-356,495 
  client.ts        |   85.49 |    77.12 |   84.84 |   85.49 | ...1735,1774-1777 
  ...tGenerator.ts |    72.1 |    61.11 |     100 |    72.1 | ...63,365,372-375 
  ...lScheduler.ts |   82.97 |    81.44 |   93.47 |   82.97 | ...2431,2483-2487 
  geminiChat.ts    |   89.32 |     84.8 |   91.48 |   89.32 | ...1454,1521-1522 
  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 |   94.92 |    82.59 |   93.87 |   94.92 |                   
  ...tGenerator.ts |   96.48 |    84.28 |   92.59 |   96.48 | ...01,919-923,963 
  converter.ts     |   94.51 |    80.72 |     100 |   94.51 | ...06-607,617,823 
  index.ts         |       0 |        0 |       0 |       0 | 1-21              
  usage.ts         |     100 |      100 |     100 |     100 |                   
 ...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 |    92.1 |    80.38 |   90.32 |    92.1 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...tGenerator.ts |   92.08 |    80.38 |   90.32 |   92.08 | ...85,895-896,924 
 ...ntentGenerator |   81.66 |    84.08 |    90.9 |   81.66 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  converter.ts     |   76.88 |    82.25 |    87.5 |   76.88 | ...1589,1610-1616 
  errorHandler.ts  |     100 |      100 |     100 |     100 |                   
  index.ts         |   52.38 |    44.44 |      50 |   52.38 | ...77,81-85,89-93 
  ...tGenerator.ts |    66.4 |    70.58 |   88.88 |    66.4 | ...51-157,168-169 
  pipeline.ts      |   93.67 |     84.9 |     100 |   93.67 | ...80-481,489,554 
  ...ureContext.ts |     100 |      100 |     100 |     100 |                   
  ...ingOptions.ts |       0 |        0 |       0 |       0 | 1                 
  ...CallParser.ts |   90.66 |    88.57 |     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.69 |    89.17 |   95.45 |   96.69 |                   
  dashscope.ts     |   97.29 |    89.77 |   93.33 |   97.29 | ...81-282,358-359 
  deepseek.ts      |   95.55 |    90.56 |     100 |   95.55 | ...31-132,145-146 
  default.ts       |   94.62 |    86.36 |   85.71 |   94.62 | 86-87,157-159     
  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/goals         |   89.23 |    82.44 |   94.11 |   89.23 |                   
  ...eGoalStore.ts |   81.57 |    92.85 |   81.81 |   81.57 | ...43-146,154-162 
  goalHook.ts      |   97.26 |    91.48 |     100 |   97.26 | 100-105           
  goalJudge.ts     |   84.33 |    74.28 |     100 |   84.33 | ...57-358,366-368 
  index.ts         |     100 |      100 |     100 |     100 |                   
 src/hooks         |   83.48 |    84.87 |   86.83 |   83.48 |                   
  ...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.4 |    90.78 |     100 |    96.4 | ...91,293-294,367 
  ...entHandler.ts |   94.56 |    83.78 |   93.33 |   94.56 | ...38,795-796,806 
  hookPlanner.ts   |   84.13 |    76.59 |      90 |   84.13 | ...38,144,162-173 
  hookRegistry.ts  |   90.17 |    83.33 |     100 |   90.17 | ...33,352,356,360 
  hookRunner.ts    |   58.56 |    71.26 |   66.66 |   58.56 | ...48-749,758-759 
  hookSystem.ts    |   84.57 |      100 |   65.85 |   84.57 | ...21-622,628-629 
  ...HookRunner.ts |   75.51 |     61.9 |      80 |   75.51 | ...05-406,424-425 
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...HookRunner.ts |   93.63 |    89.47 |      90 |   93.63 | ...45-353,427-428 
  ...SkillHooks.ts |   78.75 |       75 |   66.66 |   78.75 | 62-66,137-152     
  ...oksManager.ts |   96.66 |    91.66 |     100 |   96.66 | ...90,209-210,223 
  ssrfGuard.ts     |   77.22 |    85.36 |     100 |   77.22 | ...57,261-267,273 
  stopHookCap.ts   |     100 |      100 |     100 |     100 |                   
  trustedHooks.ts  |       0 |        0 |       0 |       0 | 1-124             
  types.ts         |   91.18 |    92.04 |   85.71 |   91.18 | ...40-441,501-505 
  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           |   41.24 |    52.14 |   51.42 |   41.24 |                   
  ...nfigLoader.ts |   70.27 |    35.89 |   94.73 |   70.27 | ...20-422,426-432 
  ...ionFactory.ts |   42.69 |    79.16 |      50 |   42.69 | ...62-413,419-436 
  ...Normalizer.ts |   23.09 |    13.72 |   30.43 |   23.09 | ...04-905,909-924 
  ...verManager.ts |   25.31 |    62.06 |   41.66 |   25.31 | ...85-704,710-740 
  ...eLspClient.ts |   32.77 |       80 |   17.64 |   32.77 | ...84-288,294-295 
  ...LspService.ts |   48.49 |    67.16 |   65.71 |   48.49 | ...1352,1369-1379 
  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.43 |       76 |   65.62 |   67.43 |                   
  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.86 |    77.27 |     100 |   91.86 | ...07,109-110,118 
  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.55 |    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 |    82.14 |   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.76 |   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.22 |     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      |   85.27 |    83.54 |   90.86 |   85.27 |                   
  ...ionTrailer.ts |     100 |      100 |     100 |     100 |                   
  ...llRegistry.ts |   98.44 |    91.83 |     100 |   98.44 | 268-269           
  ...ionService.ts |    95.6 |    96.36 |     100 |    95.6 | ...32,400,402-406 
  ...ingService.ts |   83.91 |       83 |   83.33 |   83.91 | ...1267,1284-1285 
  ...ttribution.ts |   91.73 |    87.71 |      90 |   91.73 | ...80-685,826-827 
  ...utSlimming.ts |     100 |    96.77 |     100 |     100 | 133,182           
  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 
  ...oryService.ts |   86.25 |    74.35 |    92.3 |   86.25 | ...46-655,696-699 
  fileReadCache.ts |     100 |      100 |     100 |     100 |                   
  ...temService.ts |      90 |    84.44 |   88.88 |      90 | ...89,191,269-276 
  ...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 |   73.79 |       70 |   94.87 |   73.79 | ...1365,1393-1394 
  ...ionService.ts |   98.13 |     97.8 |   95.45 |   98.13 | ...32-333,380-381 
  ...orRegistry.ts |   96.54 |    91.73 |     100 |   96.54 | ...70-471,622-623 
  sessionRecap.ts  |   12.04 |      100 |       0 |   12.04 | 49-160            
  ...ionService.ts |   90.19 |     78.7 |   96.66 |   90.19 | ...1285,1289-1290 
  sessionTitle.ts  |   93.87 |    69.81 |     100 |   93.87 | ...33-236,267-268 
  ...ionService.ts |   81.07 |    77.92 |   89.28 |   81.07 | ...1923,1929-1934 
  ...UseSummary.ts |   94.73 |    87.71 |     100 |   94.73 | ...73-175,225-226 
  ...reeCleanup.ts |   14.56 |      100 |   33.33 |   14.56 | 58-185            
 ...icrocompaction |   97.69 |    89.79 |     100 |   97.69 |                   
  microcompact.ts  |   97.69 |    89.79 |     100 |   97.69 | ...68,229,233,314 
 src/skills        |    87.5 |    83.86 |   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 | ...1120,1127-1131 
  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     |   83.13 |    80.24 |   95.23 |   83.13 |                   
  ...tin-agents.ts |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...-selection.ts |     100 |      100 |     100 |     100 |                   
  ...nt-manager.ts |   77.21 |    72.09 |   92.85 |   77.21 | ...1180,1202-1203 
  types.ts         |     100 |      100 |     100 |     100 |                   
  validation.ts    |   92.46 |    95.18 |     100 |   92.46 | 51-56,69-74,78-83 
 src/telemetry     |   74.59 |     85.9 |   78.77 |   74.59 |                   
  config.ts        |     100 |      100 |     100 |     100 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  ...attributes.ts |   98.13 |       88 |     100 |   98.13 | 185-187           
  ...-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.93 |    90.21 |   94.11 |   93.93 | ...75-280,299-300 
  ...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.45 |    83.56 |   76.92 |   90.45 | ...17-318,338-342 
  ...on-context.ts |     100 |      100 |     100 |     100 |                   
  ...on-tracing.ts |   90.69 |    87.87 |     100 |   90.69 | ...67-471,482-485 
  ...etry-utils.ts |     100 |      100 |     100 |     100 |                   
  ...l-decision.ts |     100 |      100 |     100 |     100 |                   
  ...e-id-utils.ts |     100 |      100 |     100 |     100 |                   
  tracer.ts        |   98.61 |    89.36 |     100 |   98.61 | 53,104            
  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.91 |   76.47 |   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.14 |   72.41 |   91.19 | ...38,202-203,216 
  ...aceContext.ts |     100 |      100 |     100 |     100 |                   
 src/tools         |    77.8 |    81.31 |    86.3 |    77.8 |                   
  ...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          |   80.52 |    85.98 |   73.33 |   80.52 | ...15-716,803-853 
  ...r-worktree.ts |   82.43 |    68.75 |    87.5 |   82.43 | ...67-170,236-237 
  exit-worktree.ts |   83.47 |       84 |    90.9 |   83.47 | ...80-281,286-299 
  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 |   69.73 |    75.29 |   71.42 |   69.73 | ...29-732,749-786 
  mcp-client.ts    |   33.18 |    77.41 |   66.66 |   33.18 | ...1490,1494-1497 
  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.36 |    83.94 |      92 |   92.36 | ...29,558-561,574 
  ...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 |      100 |       0 |    6.34 | 47-145            
  send-message.ts  |   89.32 |    91.66 |   83.33 |   89.32 | 44-45,68-76       
  shell.ts         |   72.96 |     79.6 |    91.3 |   72.96 | ...4216,4265-4271 
  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     |   89.17 |    82.05 |   92.85 |   89.17 | ...41-546,568-569 
  tool-error.ts    |     100 |      100 |     100 |     100 |                   
  tool-names.ts    |     100 |      100 |     100 |     100 |                   
  tool-registry.ts |   74.79 |       75 |   80.48 |   74.79 | ...92-793,801-802 
  tool-search.ts   |   95.19 |    86.48 |    92.3 |   95.19 | ...47-153,208-213 
  tools.ts         |   91.98 |    90.19 |   88.88 |   91.98 | ...50-451,467-473 
  web-fetch.ts     |   88.59 |    79.48 |    92.3 |   88.59 | ...12-313,315-316 
  write-file.ts    |   82.23 |    81.17 |   83.33 |   82.23 | ...65-668,680-715 
 src/tools/agent   |   75.01 |    82.55 |   74.62 |   75.01 |                   
  agent.ts         |   75.29 |    82.86 |    75.4 |   75.29 | ...2203,2265-2272 
  fork-subagent.ts |   69.62 |    71.42 |   66.66 |   69.62 | ...04-105,140-151 
 src/utils         |   88.98 |    87.55 |   93.68 |   88.98 |                   
  LruCache.ts      |       0 |        0 |       0 |       0 | 1-41              
  ...ssageQueue.ts |     100 |      100 |     100 |     100 |                   
  ...cFileWrite.ts |   77.96 |    80.48 |     100 |   77.96 | ...35,156,173-176 
  bareMode.ts      |   27.27 |      100 |       0 |   27.27 | 9-15,18-19        
  browser.ts       |    7.69 |      100 |       0 |    7.69 | 17-56             
  bundlePaths.ts   |     100 |      100 |     100 |     100 |                   
  ...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    |   81.81 |       75 |     100 |   81.81 | 15-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 |                   
  ...iagnostics.ts |   96.87 |    91.83 |     100 |   96.87 | 214-219,272       
  ...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             
  modelId.ts       |   98.55 |    96.87 |     100 |   98.55 | 103               
  ...kerChecker.ts |   88.75 |    85.71 |     100 |   88.75 | 69-70,87-93       
  notebook.ts      |   94.35 |    84.78 |     100 |   94.35 | ...10,122,174-176 
  openaiLogger.ts  |   88.05 |    84.09 |     100 |   88.05 | ...44-146,169-174 
  partUtils.ts     |     100 |    98.61 |     100 |     100 | 206               
  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.79 |    84.37 |   66.66 |   46.79 | ...45-246,258-335 
  ...sDiscovery.ts |   97.42 |    92.85 |     100 |   97.42 | ...04,182-183,202 
  ...tchOptions.ts |   81.72 |    85.04 |   95.23 |   81.72 | ...11,536,565-574 
  runtimeStatus.ts |    97.5 |    88.57 |     100 |    97.5 | 167-168           
  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.89 |     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.73 |    94.59 |     100 |   98.73 | 111               
  ...pEventSink.ts |     100 |       80 |     100 |     100 | 61                
  ...tGenerator.ts |     100 |      100 |     100 |     100 |                   
  ...ameContext.ts |     100 |      100 |     100 |     100 |                   
  symlink.ts       |   77.77 |       50 |     100 |   77.77 | 44,54-59          
  ...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.61 |     100 |      92 | 49-53,65-69       
 ...ils/filesearch |   86.21 |    81.61 |   96.42 |   86.21 |                   
  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 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.

Code Review: feat(tui): add daemon adapter spike

Verdict: REQUEST_CHANGES (7 Critical findings, default-off spike)

阻塞问题集中在 adapter 层的状态一致性安全输入清理上。这些问题不影响 MCP 协议本身,但会导致 TUI 层出现不可恢复的状态偏差。

Critical Issues

# 行号 问题 影响
1 82 turn_complete 是 dead code type variant 编译器无法对未处理事件报错
2 164 sanitizeReason 仅处理 ANSI ESC,漏掉 OSC/DCS/控制字符 注入风险(标题/链接操控)
3 284 toolCallsById 无限增长,无淘汰 长会话内存泄漏
4 313 agent_message_chunk/thought_chunk 未经过滤直接进 React 标签 任意字符串注入
5 484 sendPrompt 出错后无状态清理 busy=true 卡死,后续 prompt 永久阻塞
6 495 cancel/setModel/approve/reject 无 try/catch 未捕获异常 + 无事件上报
7 554 pumpEvents catch 块中 emit() 可能抛出异常 捕获异常再抛 → 事件循环终止

Positive Notes

  • EventQueue 设计正确:mutex + linked list,容量上限防止 OOM
  • pumpEvents 断连处理和 abort/timeout 机制可靠
  • 测试覆盖合理:4 个 test case 覆盖核心路径
  • MCP turn_complete 移除方向正确(避免双重信号)

daemonEventId?: number;
}
| {
type: 'turn_complete';

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] turn_complete 是 dead code type variant。DaemonEventType 定义了它,但事件分发循环(line 379)从未处理此类型——所有 turn_complete 事件都会落入 default 分支被静默丢弃。如果未来有人在 daemon 端发送 turn_complete,adapter 不会通知 TUI 层,导致 UI 无法知道 turn 已结束。

建议:移除类型定义或添加对应的处理分支(后者需要决定与 MCP response_finished 的优先级关系)。

}
}

function sanitizeReason(reason: string): string {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] sanitizeReason 只剥离 ANSI escape sequences(ESC[...]m),但不处理 OSC(ESC]...BEL/ESC\\)和 DCS(ESC P...ST)序列。终端模拟器(iTerm2、xterm.js)会解释这些序列,可用于设置窗口标题、创建超链接、甚至执行 iTerm2 特定的 shell integration 命令。

此外,控制字符(\x00-\x1F 除了 \n/\r/\t)也未被过滤。

建议:改用更全面的 ANSI 剥离方案,或限制为 [\x20-\x7E\n\r\t] 白名单。

state?.toolCallsById.set(toolCallId, tool);
return {
type: 'tool_group',
tools: Array.from(state?.toolCallsById.values() ?? [tool]),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] toolCallsById Map 没有容量上限或淘汰策略。每次 tool_call_started 都会插入新条目,只有 sendPrompt 开始时清空一次。如果 daemon 在一次 turn 内发送大量 tool_call_update(例如一个长 chain-of-thought 工具调用序列),或如果 tool_call_finished 事件丢失(网络分区、daemon bug),Map 会无限增长。

建议:添加容量上限(例如 256),超出时丢弃最旧条目并 emit warning event。

return [];
}

if (sessionUpdate === 'agent_message_chunk' && text) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] agent_message_chunkthought_chunkdata.text 未经过任何过滤直接传入 AssistantMessage标签.content。与 sanitizeReason(至少处理了 ANSI)不同,这些内容完全没有清理。

如果 daemon 返回恶意或畸形的文本(例如极长字符串、控制字符、null bytes),会直接进入 React 渲染管线,可能导致:

  • 渲染异常或崩溃
  • 终端控制字符注入(如果 AssistantMessage标签 使用 dangerouslySetInnerHTML 或直接打印)
  • OOM(如果 thought_chunk 是一个超长字符串)

建议:对所有用户可见的文本字段应用统一的清理管道。

: prompt;
try {
return await this.session.prompt({ prompt: promptBlocks });
} catch (error) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] sendPrompt 出错时 emit disconnected + turn_complete,但不清理 busy 状态和 pending request。如果 session.prompt() 抛出异常(daemon 崩溃、网络超时),adapter 会:

  1. emit disconnected(将 TUI 置为 disconnected 状态)
  2. emit turn_complete(但 busy 仍为 true,因为 busy=false 只在事件循环的 response_finished 分支设置)
  3. currentPendingRequestId 不会被清理

结果:即使 TUI 显示 disconnected,内部状态认为仍在处理中。后续 prompt() 调用会因 busy 检查被拒绝。

建议:在 catch 块中显式设置 busy = false 并清除 currentPendingRequestId

}
}

async cancel(): Promise<void> {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] cancel()setModel()approve()reject() 四个方法都没有 try/catch。如果底层 session 方法抛出异常(连接已断、超时、协议错误),未捕获的异常会向上传播到 TUI 调用方,可能导致 UI 层未处理的 promise rejection 或进程崩溃。

建议:用 try/catch 包裹,捕获异常后 emit error event 并返回 false(而非让异常逃逸)。

reason: 'event stream ended',
});
}
} catch (error) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] emit(disconnected, ...) 调用位于 pumpEvents 的 catch 块内。但 EventEmitter.emit() 在有 error listener 时可能抛出异常(如果没有 error listener,Node.js 会直接抛出未捕获异常)。如果 onUpdate 回调(注册为 disconnected listener)内部抛出异常,这个异常会逃逸到 catch 块,导致整个事件循环终止且无清理。

建议:在 catch 块内用 try/catch 包裹 emit 调用,或将 emit 移到 catch 块外部(先设置状态,再通知)。

@chiga0

chiga0 commented May 17, 2026

Copy link
Copy Markdown
Collaborator Author

Updated #4202 with fix(tui): harden daemon adapter state handling (e3b071db7).

Handled the latest valid review findings that are fixable inside this default-off TUI adapter spike:

  • removed the dead turn_complete update variant from the adapter contract;
  • expanded sanitization to strip CSI/OSC/DCS-style escape/control sequences and applied it to daemon agent/thought text chunks as well as disconnect reasons;
  • bounded the reducer tool-call cache to 128 entries to prevent long-session growth;
  • made prompt/control-path failures (sendPrompt, cancel, setModel, permission votes) emit a sanitized disconnected update and abort the event pump before rethrowing;
  • routed event-pump updates through a safe emitter so UI callback failures cannot rethrow from the pump catch path;
  • fixed the test EventQueue.fail() helper to reject pending waiters;
  • added tests for tool-cache eviction, OSC/DCS sanitization, update-handler isolation, and daemon control failures.

Validation passed locally:

cd packages/cli && npx vitest run src/ui/daemon/DaemonTuiAdapter.test.ts
cd packages/cli && npm run typecheck
cd packages/cli && npx eslint src/ui/daemon/DaemonTuiAdapter.ts src/ui/daemon/DaemonTuiAdapter.test.ts --max-warnings 0 --no-warn-ignored
cd packages/cli && npx prettier --check src/ui/daemon/DaemonTuiAdapter.ts src/ui/daemon/DaemonTuiAdapter.test.ts

Still intentionally not claiming TUI parity/default migration here: daemon-side turn metadata / _meta / usage / stop-hook-loop still needs the protocol-level event contract called out in the roadmap.

Generated by GPT-5 model

type: 'history',
item: {
type: 'info',
text: `Model switched to ${event.data['modelId']}`,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] model_switched 的 info 文本未经过清洗:Model switched to ${event.data['modelId']} 直接将 daemon 提供的 modelId 拼接到展示文本中。而 agent_message_chunkagent_thought_chunk 路径都使用了 sanitizeDisplayText。daemon 注入的终端转义序列可能通过此路径在 TUI 中被解释执行。

Suggested change
text: `Model switched to ${event.data['modelId']}`,
text: `Model switched to ${sanitizeDisplayText(event.data['modelId'])}`,

同理,formatPlan(~168-183)、formatToolContentText(~230-246)和 formatToolResultDisplay 的字符串分支也需统一清洗。

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

}
}

private reportDaemonFailure(error: unknown): void {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] reportDaemonFailure 同步调用 eventController?.abort() 并发出 disconnected,但不清理 eventPump / eventController。泵的 finally 块是异步的,在此窗口内 pumpEvents 可能仍在 for await 循环中运行,发出额外的事件。此外 start()eventPump 仍非空而静默返回,直到泵的 finally 执行完毕。

Suggested change
private reportDaemonFailure(error: unknown): void {
private reportDaemonFailure(error: unknown): void {
this.eventController?.abort();
this.eventController = null;
this.eventPump = null;
const message = sanitizeReason(
error instanceof Error ? error.message : String(error),
);
this.emit({ type: 'disconnected', reason: message });
}

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

constructor(options: DaemonTuiAdapterOptions) {
this.session = options.session;
this.onUpdate = options.onUpdate;
this.lastSeenEventId = options.session.lastEventId;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] start() 通过 if (this.eventPump) { return; } 防止重复启动。但如果在 stop() 正在 await this.eventPump 时(泵的 finally 尚未运行)调用 start(),则 start() 静默返回。随后 finally 清除状态,adapter 进入僵尸状态——无泵运行、无法重启。

建议使用显式状态机(idle | running | stopping)替代对 eventPump 是否为 null 的检查,或在 start() 中等待当前泵完成后再重启。

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

async cancel(): Promise<void> {
try {
await this.session.cancel();
} catch (error) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] sendPrompt 从未清除 this.reducerStatetoolCallsById/toolCallOrder)。上一轮的工具调用会在新轮次中累积并出现在 tool_group_update 中。MAX_TOOL_CALLS=128 的淘汰上限是跨轮次共享的,新工具调用可能被陈旧条目挤出。测试仅覆盖单轮次场景。

Suggested change
} catch (error) {
async sendPrompt(
prompt: string | ContentBlock[],
): Promise<DaemonTuiPromptResult> {
this.reducerState.toolCallsById.clear();
this.reducerState.toolCallOrder.length = 0;
const promptBlocks =

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

}
}
}
return {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] toolUpdateToHistoryItem 每次 tool_call_update 都通过 Array.from(state?.toolCallsById.values() ?? [tool]) 返回完整工具列表(最多 128 条)。TUI 渲染器在每次状态转换时都收到完整数组,无法区分哪个工具发生了变化,导致不必要的数组分配和协调开销。

建议发送增量更新——仅包含变化的工具——或添加 changed 字段指示哪些 toolCallId 发生了变化,让 TUI 渲染器自行维护累积状态。

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

}

function stripControlSequences(value: string): string {
const esc = String.fromCharCode(27);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] stripControlSequences 在每次调用时创建 4 个 new RegExp()。由于此函数是每条 agent_message_chunk / agent_thought_chunk 流式块的必经之路,这些模式是静态的(ESC 字符不变),应提升为模块级常量。

Suggested change
const esc = String.fromCharCode(27);
const ESC = String.fromCharCode(27);
const OSC_RE = new RegExp(`${ESC}\\][\\s\\S]*?(?:\\x07|${ESC}\\\\)`, 'g');
const DCS_RE = new RegExp(`${ESC}[P^_][\\s\\S]*?${ESC}\\\\`, 'g');
const CSI_RE = new RegExp(`${ESC}\\[[0-?]*[ -/]*[@-~]`, 'g');
const C1_RE = new RegExp(`${ESC}[@-Z\\\\-_]`, 'g');
function stripControlSequences(value: string): string {
return value
.replace(OSC_RE, '')
.replace(DCS_RE, '')
.replace(CSI_RE, '')
.replace(C1_RE, '');
}

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

@chiga0 chiga0 force-pushed the feat/tui-daemon-adapter-draft branch from e3b071d to 514654d Compare May 17, 2026 08:51
@chiga0

chiga0 commented May 17, 2026

Copy link
Copy Markdown
Collaborator Author

Updated #4202 with another TUI adapter hardening pass (514654dcd).

Handled the latest valid review findings:

  • sanitized daemon-controlled model switch text, plan text/status, tool kind/title, textual raw output, and textual content fallbacks through the same display-text cleanup path;
  • hoisted OSC/DCS/CSI/C1 stripping regexes to module constants so streaming chunks do not allocate regexes per chunk;
  • added an explicit idle | running | stopping pump lifecycle so start() during a draining stop() restarts cleanly instead of leaving the adapter idle with no pump;
  • moved replay cursor advancement before update emission so processed event ids are retained even if UI callbacks stop or abort around the update boundary;
  • clear accumulated tool state before each sendPrompt() so stale tool calls from a previous turn cannot leak into the next turn;
  • added focused tests for plan/model/tool sanitization, stop/start restart during pump drain, and per-turn tool-state reset.

Latest local validation passed:

cd packages/cli && npx vitest run src/ui/daemon/DaemonTuiAdapter.test.ts
cd packages/cli && npm run typecheck
cd packages/cli && npx eslint src/ui/daemon/DaemonTuiAdapter.ts src/ui/daemon/DaemonTuiAdapter.test.ts --max-warnings 0 --no-warn-ignored
cd packages/cli && npm run build

One suggestion remains intentionally not implemented in this PR: switching tool_group_update from full snapshot to delta-only updates. The adapter contract currently emits a bounded 128-tool snapshot so a future TUI wiring layer can replace its current pending tool group deterministically; changing that to delta semantics should be done together with the actual renderer integration, not inside this isolated default-off adapter spike.

Generated by GPT-5 model

@chiga0 chiga0 marked this pull request as ready for review May 17, 2026 09:39
@chiga0 chiga0 requested a review from wenshao May 17, 2026 09:39
@chiga0

chiga0 commented May 17, 2026

Copy link
Copy Markdown
Collaborator Author

Generated by GPT-5 model

Status update after re-running the failed jobs:

  • Current CI is green across classify, lint, CodeQL, coverage, and macOS/Ubuntu/Windows test jobs.
  • The earlier macOS timeout was in standalone release packaging symlink handling, outside this TUI daemon adapter change.
  • The earlier Windows failure was in InputPrompt prompt suggestion submission, also outside the daemon adapter surface. The rerun passed, so I am treating both as unrelated/flaky rather than actionable findings for this PR.

Merge-safety check:

  • This remains an additive/default-off TUI daemon adapter spike. It does not switch the current TUI main path to daemon by default.
  • The implementation is aligned with the Mode B roadmap direction: clients consume daemon HTTP/SSE session events through an adapter layer rather than binding directly to the internal event bus.
  • Remaining parity gaps, such as richer typed daemon event metadata and terminal-output fidelity, are still roadmap items for follow-up PRs rather than blockers for this spike.

I marked the PR ready for review and requested review from @wenshao and @doudouOUC.

const withoutAnsi = stripControlSequences(reason);
let sanitized = '';
for (const char of withoutAnsi) {
const code = char.charCodeAt(0);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] formatPlan 中的 contentstatus 来自 daemon 事件,直接拼接到 TUI 展示文本(${index + 1}. [${status}] ${content}),未经过 sanitizeDisplayText 清洗。其他路径(agent_message_chunkagent_thought_chunk)使用了清洗,但 plan 路径遗漏了。

Suggested change
const code = char.charCodeAt(0);
const content = sanitizeDisplayText(getString(entry['content']) ?? '');
const status = sanitizeDisplayText(getString(entry['status']) ?? 'pending');

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

if (typeof value === 'string') {
return value;
}
if (

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] formatToolResultDisplaytypeof value === 'string' 分支直接返回原始字符串作为 resultDisplay,未经过清洗。工具输出可能包含终端转义序列,会在 TUI 渲染时被解释。

Suggested change
if (
if (typeof value === 'string') {
return sanitizeDisplayText(value);
}

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

}
try {
return JSON.stringify(value);
} catch {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] formatToolContentText 从 daemon 事件的 tool call content block 中提取文本后用 \n 直接拼接,未经过 sanitizeDisplayText 清洗。这些文本来自工具 content 输出,可能携带终端转义序列。

Suggested change
} catch {
.filter((part): part is string => part !== undefined && part.length > 0)
.map((part) => sanitizeDisplayText(part));

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

}
}

function sanitizeReason(reason: string): string {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] sanitizeReason(line 164-178)和 sanitizeDisplayText(line 180-209)共享约 90% 逻辑:stripControlSequences + 逐字符过滤 + 长度截断。区别仅在于 max length(500 vs 20000)和保留的空白字符(仅 \n vs \t/\n/\r)。建议提取参数化函数以减少维护负担和分支发散风险。

Suggested change
function sanitizeReason(reason: string): string {
function sanitizeText(
text: string,
maxLength: number,
preserveChars: Set<number>,
): string {
const stripped = stripControlSequences(text);
const chars: string[] = [];
for (const char of stripped) {
const code = char.charCodeAt(0);
if ((code < 32 && !preserveChars.has(code)) || code === 127) continue;
chars.push(char);
if (chars.length >= maxLength) break;
}
return chars.join('');
}

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

const kind = getString(update['kind']);
const rawOutput = formatToolResultDisplay(update['rawOutput']);
const contentOutput = formatToolContentText(update['content']);
const previous = state?.toolCallsById.get(toolCallId);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] toolUpdateToHistoryItem 中用 update['status'] === undefined(三等号)检查状态是否存在。如果 daemon 发送 status: null,会落入 mapToolStatus(null)ToolCallStatus.Error,而实际上可能只是无状态字段的普通更新。建议用 == null 同时捕获 nullundefined

Suggested change
const previous = state?.toolCallsById.get(toolCallId);
update['status'] == null
? (previous?.status ?? ToolCallStatus.Pending)
: mapToolStatus(update['status']),

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

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

Code Review: DaemonTuiAdapter

Verdict: REQUEST_CHANGES

4 Critical findings related to unsanitized daemon-sourced strings flowing into Ink/TUI rendering. The daemon is an untrusted source — any string it provides can contain ANSI escape sequences, control characters, or terminal manipulation payloads. The existing sanitizeDisplayText() helper is already defined in this file but only applied in a few code paths.

# Severity Location Issue
1 Critical formatPlan L135 content and status from plan entries rendered without sanitization
2 Critical formatToolResultDisplay L215 String rawOutput passed through without sanitizeDisplayText
3 Critical toolUpdateToHistoryItem L292-293 kind, title, toolCallId flow into name/description unsanitized
4 Critical formatToolContentText L246 Tool content text from daemon rendered without sanitization

These findings complement @wenshao's existing review comments covering agent_message_chunk/thought_chunk (L364), model_switched, and formatUnknown — the same class of sanitization gap exists in several additional code paths.

— reviewed by qwen-code / mimo-v2.5-pro

.map((entry, index) => {
const content = getString(entry['content']) ?? '';
const status = getString(entry['status']) ?? 'pending';
return `${index + 1}. [${status}] ${content}`;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] Unsanitized daemon string in formatPlan

content (L133) and status (L134) are extracted via getString() but never passed through sanitizeDisplayText() before being interpolated into rendered text. A malicious daemon can inject ANSI escape sequences or control characters through plan entry content.

Suggested change
return `${index + 1}. [${status}] ${content}`;
return `${index + 1}. [${sanitizeDisplayText(status)}] ${sanitizeDisplayText(content)}`;

— qwen-code / mimo-v2.5-pro

return undefined;
}
if (typeof value === 'string') {
return value;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] String rawOutput passes through unsanitized

When rawOutput is a string, it's returned directly without sanitizeDisplayText(). This is the most common path for tool output and the most dangerous — a daemon-controlled string can contain terminal escape sequences that manipulate the TUI.

Suggested change
return value;
return sanitizeDisplayText(value);

— qwen-code / mimo-v2.5-pro

const previous = state?.toolCallsById.get(toolCallId);
const tool: IndividualToolCallDisplay = {
callId: toolCallId,
name: kind ?? title ?? previous?.name ?? toolCallId,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] Unsanitized kind/title/toolCallId in tool name and description

kind, title, and toolCallId are daemon-sourced strings that flow directly into the name and description fields of IndividualToolCallDisplay, which are rendered in the TUI. None pass through sanitizeDisplayText().

Suggested change
name: kind ?? title ?? previous?.name ?? toolCallId,
name: sanitizeDisplayText(kind ?? title ?? previous?.name ?? toolCallId),
description: sanitizeDisplayText(title ?? kind ?? previous?.description ?? toolCallId),

— qwen-code / mimo-v2.5-pro

}
const content = item['content'];
if (isRecord(content)) {
return getString(content['text']);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] Tool content text rendered without sanitization

getString(content['text']) and getString(item['text']) extract daemon-sourced strings that are joined and returned without sanitizeDisplayText(). This text flows directly into the TUI's tool result display. The sanitizeDisplayText helper is already available in this file.

Suggested change
return getString(content['text']);
const text = getString(content['text']);
return text !== undefined ? sanitizeDisplayText(text) : undefined;
}
const text = getString(item['text']);
return text !== undefined ? sanitizeDisplayText(text) : undefined;

— qwen-code / mimo-v2.5-pro

.map((entry, index) => {
const content = getString(entry['content']) ?? '';
const status = getString(entry['status']) ?? 'pending';
return `${index + 1}. [${status}] ${content}`;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] Unsanitized daemon string in formatPlan

content (L133) and status (L134) are extracted via getString() but never passed through sanitizeDisplayText() before being interpolated into rendered text. A malicious daemon can inject ANSI escape sequences or control characters through plan entry content.

Suggested change
return `${index + 1}. [${status}] ${content}`;
return `${index + 1}. [${sanitizeDisplayText(status)}] ${sanitizeDisplayText(content)}`;

— qwen-code / mimo-v2.5-pro

return undefined;
}
if (typeof value === 'string') {
return value;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] String rawOutput passes through unsanitized

When rawOutput is a string, it is returned directly without sanitizeDisplayText(). This is the most common path for tool output and the most dangerous — a daemon-controlled string can contain terminal escape sequences that manipulate the TUI.

Suggested change
return value;
return sanitizeDisplayText(value);

— qwen-code / mimo-v2.5-pro

const previous = state?.toolCallsById.get(toolCallId);
const tool: IndividualToolCallDisplay = {
callId: toolCallId,
name: kind ?? title ?? previous?.name ?? toolCallId,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] Unsanitized kind/title/toolCallId in tool name and description

kind, title, and toolCallId are daemon-sourced strings that flow directly into the name and description fields of IndividualToolCallDisplay, which are rendered in the TUI. None pass through sanitizeDisplayText().

Suggested change
name: kind ?? title ?? previous?.name ?? toolCallId,
name: sanitizeDisplayText(kind ?? title ?? previous?.name ?? toolCallId),
description: sanitizeDisplayText(title ?? kind ?? previous?.description ?? toolCallId),

— qwen-code / mimo-v2.5-pro

}
const content = item['content'];
if (isRecord(content)) {
return getString(content['text']);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] Tool content text rendered without sanitization

getString(content["text"]) and getString(item["text"]) extract daemon-sourced strings that are joined and returned without sanitizeDisplayText(). This text flows directly into the TUI tool result display. The sanitizeDisplayText helper is already available in this file.

Suggested change
return getString(content['text']);
const text = getString(content["text"]);
return text !== undefined ? sanitizeDisplayText(text) : undefined;
}
const text = getString(item["text"]);
return text !== undefined ? sanitizeDisplayText(text) : undefined;

— qwen-code / mimo-v2.5-pro

@tanzhenxin tanzhenxin added the type/feature-request New feature or enhancement request label May 17, 2026
this.start();
}
}
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] emit() silently swallows all onUpdate callback exceptions. If the TUI renderer throws (e.g. after a data structure change), all daemon events are silently dropped forever — no log, no crash, no signal. The event pump keeps running normally but every onUpdate call is discarded, creating a frozen TUI with zero observability. This is the hardest bug to debug at 3 AM.

Suggested change
}
private emitFailureCount = 0;
private emit(update: DaemonTuiUpdate): void {
try {
this.onUpdate(update);
} catch (error) {
if (++this.emitFailureCount === 1 || this.emitFailureCount % 100 === 0) {
console.error('[DaemonTuiAdapter] onUpdate failed', error);
}
}
}

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

}
}

private reportDaemonFailure(error: unknown): void {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] reportDaemonFailure emits disconnected but the adapter has no setSession() — it's permanently broken after any RPC failure. The TUI sees disconnected, tries to recover by calling start(), which uses the same broken session, fails immediately, and triggers another disconnected. This creates an infinite loop: disconnect → reconnect → immediate disconnect → reconnect → …

Every daemon restart, network blip, or RPC timeout permanently bricks the adapter. The only recovery path is destroying and recreating the entire adapter instance, which the current architecture doesn't expose.

Suggested change
private reportDaemonFailure(error: unknown): void {
private sessionHealth: 'ok' | 'broken' = 'ok';
start(): void {
if (this.sessionHealth === 'broken') {
throw new Error('Session is broken; create a new DaemonTuiAdapter with a fresh session');
}
// ... rest of start()
}
private reportDaemonFailure(error: unknown): void {
this.sessionHealth = 'broken';
// ... rest
}

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


async sendPrompt(
prompt: string | ContentBlock[],
): Promise<DaemonTuiPromptResult> {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] All 5 RPC methods (sendPrompt, cancel, setModel, approvePermission, rejectPermission) re-throw the raw daemon Error after reportDaemonFailure. reportDaemonFailure sanitizes the message for the internal disconnected update, but the Error object propagated to the TUI caller retains the unsanitized message property. If the caller displays error.message, daemon-injected ANSI/OSC/DCS escape sequences execute in the terminal, bypassing the adapter's sanitization layer entirely.

Suggested change
): Promise<DaemonTuiPromptResult> {
} catch (error) {
this.reportDaemonFailure(error);
const msg = sanitizeReason(
error instanceof Error ? error.message : String(error),
);
throw new Error(`Daemon RPC failed: ${msg}`);
}

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

.replace(C1_RE, '');
}

function formatToolResultDisplay(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] formatToolResultDisplay passes structured objects (fileDiff, ansiOutput, todo_list, plan_summary, task_execution, mcp_tool_progress) through with an unchecked cast without sanitizing their internal string fields. A malicious daemon can embed ANSI/OSC/DCS sequences inside FileDiff.fileName, PlanResultDisplay.message, AnsiOutputDisplay.ansiOutput, or any nested string field, and they flow directly into the TUI rendering pipeline.

Suggested change
function formatToolResultDisplay(
if (typeof value['fileDiff'] === 'string') {
return {
...value,
fileDiff: sanitizeDisplayText(value['fileDiff']),
fileName: typeof value['fileName'] === 'string'
? sanitizeDisplayText(value['fileName']) : value['fileName'],
originalContent: typeof value['originalContent'] === 'string'
? sanitizeDisplayText(value['originalContent']) : value['originalContent'],
newContent: typeof value['newContent'] === 'string'
? sanitizeDisplayText(value['newContent']) : value['newContent'],
} as unknown as IndividualToolCallDisplay['resultDisplay'];
}

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

return [];
}
return [
{

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] The permission_request handler passes event.data through directly without sanitizing daemon-controlled display strings. toolCall.title, toolCall.kind, and options[].name flow unsanitized into the TUI permission confirmation dialog, creating an injection vector distinct from the tool call sanitization path.

Suggested change
{
case 'permission_request': {
if (!isPermissionRequestData(event.data)) return [];
const sanitized = {
...event.data,
toolCall: {
...event.data.toolCall,
title: typeof event.data.toolCall.title === 'string'
? sanitizeDisplayText(event.data.toolCall.title) : event.data.toolCall.title,
kind: typeof event.data.toolCall.kind === 'string'
? sanitizeDisplayText(event.data.toolCall.kind) : event.data.toolCall.kind,
},
options: event.data.options.map((opt) => ({
...opt,
name: typeof opt.name === 'string' ? sanitizeDisplayText(opt.name) : opt.name,
})),
};
return [{ type: 'permission_request', requestId: sanitized.requestId, request: sanitized, daemonEventId: event.id }];
}

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

}
}
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] sendPrompt returns session.prompt() result directly, including daemon-controlled stopReason, without sanitization. If the TUI renders stopReason (e.g. in footer or status display), it receives unsanitized daemon-controlled text.

Suggested change
// Either sanitize stopReason before returning, or document that callers
// must sanitize all string fields from the returned DaemonTuiPromptResult.
const result = await this.session.prompt({ prompt: promptBlocks });
if (typeof result.stopReason === 'string') {
result.stopReason = sanitizeReason(result.stopReason);
}
return result;

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

export interface DaemonTuiEvent {
id?: number;
v: 1;
type: string;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] DaemonTuiEvent.v: 1 is declared as a required field but never validated anywhere. When the daemon protocol upgrades from v1 to v2 and changes event data shapes, the adapter silently processes v2 events with v1 logic, producing corrupted TUI updates without any "protocol incompatible" error. This is the most subtle trap during protocol evolution.

Suggested change
type: string;
if (event.v !== 1) {
this.emit({
type: 'history',
item: { type: 'error', text: `Unsupported daemon protocol version: ${event.v}` },
});
continue;
}

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


const title = getString(update['title']);
const kind = getString(update['kind']);
const safeToolCallId = sanitizeDisplayText(toolCallId);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] toolUpdateToHistoryItem never populates renderOutputAsMarkdown on the constructed IndividualToolCallDisplay. The in-process tool scheduler sets this field from tool metadata; without it, all daemon tool output renders as plain text even when markdown rendering is appropriate (e.g. web_fetch results).

Suggested change
const safeToolCallId = sanitizeDisplayText(toolCallId);
renderOutputAsMarkdown: kind === 'web_fetch' ? true : previous?.renderOutputAsMarkdown,

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

{
type: 'history',
item: {
type: 'gemini_thought_content',

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] reduceDaemonEventToTuiUpdates is an exported function that directly mutates its state parameter (toolCallsById.set, toolCallOrder.push/shift). Its name and signature suggest a pure reducer, but it has side effects. Future maintainers writing unit tests may accidentally share state between test cases or be surprised by in-place mutation.

Suggested change
type: 'gemini_thought_content',
// Make it a private method of DaemonTuiAdapter instead of an exported function
private reduceWithState(event: DaemonTuiEvent): DaemonTuiUpdate[] {
// directly uses this.reducerState, avoiding reference-passing issues
}

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

item: { type: 'info', text },
daemonEventId: event.id,
},
]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] tool_call and tool_call_update share the exact same code path, but semantically tool_call is the initial creation (carrying full kind/title/rawInput) while tool_call_update is an incremental delta (may only carry status or rawOutput). Tests only cover tool_call_update. If the daemon evolves to send richer initial data on tool_call, those fields will be silently ignored.

Suggested change
]
// tool_call (initial) and tool_call_update (delta) share the same reducer
// path because the daemon currently sends full snapshots for both.
// If this changes, split the handling here.
if (sessionUpdate === 'tool_call' || sessionUpdate === 'tool_call_update') {

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

for (const char of stripped) {
const code = char.charCodeAt(0);
if (
(code < 32 && code !== 9 && code !== 10 && code !== 13) ||

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] sanitizeDisplayText and sanitizeReason do not strip C1 control characters (0x80-0x9F), including single-byte CSI (\x9B), DCS (\x90), and ST (\x9C). A malicious daemon can send single-byte CSI (\x9B31m) to inject terminal colors/controls, bypassing stripControlSequences (which only matches ESC[, not \x9B). The project's own packages/core/src/utils/terminalSafe.ts already handles this range with [\x00-\x1f\x7f-\x9f].

Additionally, neither sanitizer strips Unicode bidirectional control characters (U+200E/F, U+202A-U+202E, U+2066-U+2069) — the CVE-2021-42574 "Trojan Source" attack surface. A malicious daemon can inject \u202E to reverse-display model IDs, tool names, or error messages. The project's sanitizeForOsc already handles this.

Suggested change
(code < 32 && code !== 9 && code !== 10 && code !== 13) ||
// In sanitizeDisplayText and sanitizeReason char loop, add:
if (code >= 0x80 && code <= 0x9f) {
continue;
}
// In stripControlSequences, add:
.replace(/[\u200e\u200f\u202a-\u202e\u2066-\u2069]/g, '')

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

return terminalUpdates(event, reason);
}

default:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] reduceDaemonEventToTuiUpdates default: return [] silently drops unknown daemon event types with zero diagnostic output. If the daemon is upgraded and emits a new event type the adapter doesn't recognize, every such event is swallowed silently — no warning, no counter, nothing. The symptom is "TUI is missing some information" rather than a clear error, making this extremely hard to diagnose.

Suggested change
default:
// Track unknown event types and warn once per type:
private static unknownEventTypes = new Set<string>();
// in default case:
if (!DaemonTuiAdapter.unknownEventTypes.has(event.type)) {
DaemonTuiAdapter.unknownEventTypes.add(event.type);
console.warn(`[DaemonTuiAdapter] unknown event type: ${sanitizeDisplayText(event.type)}`);
}
return [];

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

this.eventController?.abort();
if (this.eventPump) {
try {
await this.eventPump;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] stop() calls await this.eventPump with no timeout. If the daemon's events() async generator does not respect the AbortSignal and continues yielding events, the for await loop never exits and stop() blocks forever. At this point lifecycle === 'stopping', so start() only sets restartAfterStop = true and returns — the adapter is permanently bricked. The only recovery is killing the process.

Suggested change
await this.eventPump;
if (this.eventPump) {
const timeout = new Promise<void>((resolve) => setTimeout(resolve, 5000));
try {
await Promise.race([this.eventPump, timeout]);
} catch {}
if (this.lifecycle === 'stopping') {
console.error('[DaemonTuiAdapter] pump did not drain within 5s, forcing idle');
this.lifecycle = 'idle';
}
}

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

}
}

async sendPrompt(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] sendPrompt has no lifecycle guard. When lifecycle === 'stopping', clearDaemonTuiReducerState() wipes reducer state while the pump may still be processing buffered events from the previous turn, and session.prompt() will fail because the controller is being aborted — triggering a spurious reportDaemonFailuredisconnected event. When lifecycle === 'idle', the prompt may succeed but there's no pump to consume the response events, causing silent prompt loss. cancel(), setModel(), approvePermission(), and rejectPermission() have the same issue.

Suggested change
async sendPrompt(
async sendPrompt(prompt: string | ContentBlock[]): Promise<DaemonTuiPromptResult> {
if (this.lifecycle !== 'running') {
throw new Error('Adapter is not running');
}
clearDaemonTuiReducerState(this.reducerState);
// ...
}

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

const MAX_DISPLAY_TEXT_LENGTH = 20_000;
const ESC = String.fromCharCode(27);
const OSC_RE = new RegExp(`${ESC}\\][\\s\\S]*?(?:\\x07|${ESC}\\\\)`, 'g');
const DCS_RE = new RegExp(`${ESC}[P^_][\\s\\S]*?${ESC}\\\\`, 'g');

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] DCS_RE uses the character class [P^_] which matches only the literal characters P, ^, and _ — not the ASCII range P through _. This misses SOS (ESC X, Start of String) sequences. ECMA-48 requires all string sequences terminated by ST (ESC ) to be stripped: DCS (ESC P), SOS (ESC X), PM (ESC ^), and APC (ESC _). The missing X means ESC X ... ESC \ sequences bypass all four regexes.

Suggested change
const DCS_RE = new RegExp(`${ESC}[P^_][\\s\\S]*?${ESC}\\\\`, 'g');
const DCS_RE = new RegExp(`${ESC}[PX^_][\\s\\S]*?${ESC}\\\\`, 'g');

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

@chiga0 chiga0 force-pushed the feat/tui-daemon-adapter-draft branch from 514654d to 4f9658f Compare May 17, 2026 15:56
@chiga0

chiga0 commented May 17, 2026

Copy link
Copy Markdown
Collaborator Author

Generated by GPT-5 model

Handled the latest valid TUI daemon adapter review comments in 4f9658f85 after rebasing onto main.

Fixes included:

  • Hardened text sanitization for C1 controls, single-byte CSI/string controls, Unicode bidi controls, SOS (ESC X) sequences, and carriage returns.
  • Deep-sanitized structured tool result displays and sanitized daemon-controlled permission request display fields after stricter permission request validation.
  • Added sanitized RPC error rethrows so callers do not receive raw daemon-controlled error messages.
  • Added running-state guards, prompt concurrency guard, stop timeout recovery with pump generation protection, and sanitized returned stopReason.
  • Warn once for unknown daemon event types through the debug logger and surface unsupported protocol versions as history errors.
  • Added plan entry bounds and an event-stream-ended history info update.

Triage/deferred items:

  • _meta / usage / stop-hook-loop / richer turn metadata remains a daemon protocol/schema roadmap dependency, not something this default-off adapter should invent locally.
  • tool_group_update delta semantics, renderOutputAsMarkdown, and reducer purity are renderer-integration/API-shape follow-ups rather than blockers for this spike.

Local validation run after the change:

  • cd packages/cli && npx vitest run src/ui/daemon/DaemonTuiAdapter.test.ts
  • cd packages/cli && npm run typecheck
  • cd packages/cli && npx eslint src/ui/daemon/DaemonTuiAdapter.ts src/ui/daemon/DaemonTuiAdapter.test.ts --max-warnings 0 --no-warn-ignored
  • cd packages/cli && npm run build

CI status at this check: latest Classify PR is green; Lint, CodeQL, and macOS/Ubuntu/Windows tests are queued for the new head.

}

private forceIdleAfterPumpTimeout(): void {
this.pumpGeneration += 1;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] forceIdleAfterPumpTimeout does not abort the old eventController — only the new one it just created. After the timeout fires, pumpEvents re-enters its loop with a fresh eventController while the stale generator from the previous iteration is still alive. This produces two concurrent pumps sharing the same session.events() stream, which double-processes events and corrupts TUI state.

Fix: capture this.eventController before the await, then abort it inside the timeout callback:

const stale = this.eventController;
const t = setTimeout(() => { stale?.abort();  }, );

Also guard pumpEvents with a pumpGeneration counter so that after forceIdleAfterPumpTimeout creates a new controller, the old pump's finally block doesn't tear down the new one.

...request,
toolCall: {
...sanitizedToolCall,
toolCallId: request.toolCall.toolCallId,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] sanitizePermissionRequest sanitizes toolCall via sanitizeDaemonValue (line 408) but then overwrites the sanitized toolCallId with the raw unsanitized value on line 410. The requestId is also passed through unsanitized from the caller (line 508). Both should be stripped through sanitizeDisplayText to match the defense-in-depth pattern used elsewhere.

];
}

case 'permission_resolved': {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] The permission_resolved branch passes event.data['outcome'] directly into the update object without sanitization. The outcome value is daemon-controlled and should be validated against the expected PermissionOutcome shape ('selected' / 'cancelled') rather than forwarded opaquely, which could carry unexpected keys or crafted strings that reach the TUI permission UI.

const C1_CSI_RE = /\x9b[0-?]*[ -/]*[@-~]/g;
const C1_STRING_RE = /[\x90\x98\x9e\x9f][\s\S]*?\x9c/g;
const BIDI_CONTROL_RE = /[\u200e\u200f\u202a-\u202e\u2066-\u2069]/g;
const UNKNOWN_EVENT_TYPES = new Set<string>();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] UNKNOWN_EVENT_TYPES is a module-level Set that grows without bound. In a long-lived daemon session that emits many distinct unknown event types (or a malicious daemon that randomizes type names), this set accumulates indefinitely. Consider capping it (e.g. if (UNKNOWN_EVENT_TYPES.size < 100) UNKNOWN_EVENT_TYPES.add(…) or using a bounded cache with eviction).

@wenshao wenshao 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: DaemonTuiAdapter.ts (incremental from 514654d4f9658f)

Model: mimo-v2.5-pro
Verdict: REQUEST_CHANGES

Summary

The incremental diff adds ~250 lines of hardening (C1/bidi/DCS sanitization, pumpGeneration, busy flag, assertRunning, stop timeout, protocol version check, sanitizePermissionRequest, sanitizeDaemonValue, warnUnknownEventTypeOnce, MAX_PLAN_ENTRIES). The sanitization improvements are solid overall, but one critical concurrency bug and three sanitization gaps remain.

Findings

# Severity Location Summary
F1 Critical L837 forceIdleAfterPumpTimeout does not abort the old eventController — creates dual pumps sharing session.events(), corrupting TUI state. Needs to capture the stale controller before the await and use pumpGeneration to guard cleanup.
F3 Suggestion L410 sanitizePermissionRequest overwrites sanitized toolCallId with the raw unsanitized value. Same for requestId at L508.
F4 Suggestion L512 permission_resolved passes event.data['outcome'] unsanitized. Should validate against expected PermissionOutcome shape.
F8 Suggestion L119 UNKNOWN_EVENT_TYPES is an unbounded module-level Set. Should cap size (e.g. 100 entries).

Deterministic checks

  • tsc: 0 errors
  • eslint: 0 findings
  • vitest: 13/13 passed

See inline comments for details and suggested fixes.

@wenshao wenshao 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。上一轮的修复(stop drain、event resume、permission validation、tool reducer、sanitize)整体很扎实,control char / ANSI / OSC 防护尤其细致。这里再挑一些剩下的协议契约空白和工程细节,详见 inline。

结论:approve with minor nits。三条 Medium(timer 泄漏、reportDaemonFailure 太激进、v 不匹配每条都 emit)建议合并前补上 follow-up;其他在后续 wave 处理即可。

setTimeout(() => {
timedOut = true;
resolve();
}, STOP_TIMEOUT_MS);

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.

🟠 Medium — setTimeout 没有 clearTimeout,5s 内泄漏一个 timer

new Promise<void>((resolve) => {
  setTimeout(() => {
    timedOut = true;
    resolve();
  }, STOP_TIMEOUT_MS);
}),

pump 先完成时,这条 setTimeout 不会被取消,5s 后仍会 fire(只是没人 await 了)。长连续运行/频繁 start-stop 场景下每次 stop 都漏一个 timer 句柄,Node 的 active handles 会被它撑住延迟退出。

建议:

let timer: NodeJS.Timeout | undefined;
try {
  await Promise.race([
    pump.then(() => undefined, () => undefined),
    new Promise<void>((resolve) => {
      timer = setTimeout(() => { timedOut = true; resolve(); }, STOP_TIMEOUT_MS);
    }),
  ]);
  return !timedOut;
} finally {
  if (timer) clearTimeout(timer);
}

private reportDaemonFailure(error: unknown): void {
if (this.lifecycle === 'running') {
this.lifecycle = 'stopping';
this.eventController?.abort();

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.

🟠 Medium — 任何 RPC 失败都 abort 整条 SSE 流

reportDaemonFailure 把 lifecycle 切到 stopping 并 abort event controller。调用方包括 sendPrompt / cancel / setModel / approvePermission / rejectPermission

也就是说,一次 transient 的 setModel 失败(比如模型名拼错、HTTP 429)就会把整个 SSE 订阅干掉,用户后续什么都收不到,必须重启 adapter。

建议区分:

  • 致命错误(连接级、prompt 路径上的 abort)→ kill pump;
  • 单次 RPC 业务失败 → 只把这次错误 surface 给调用方,SSE 继续。

至少为 cancel / setModel / approve|rejectPermission 留一条非致命路径。

if (signal.aborted) {
break;
}
if ((event as { v?: unknown }).v !== 1) {

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.

🟠 Medium — v !== 1 对每条事件都 emit 一条 error history

假设 daemon 升到 v2 后,SSE 里每条事件都会触发这条 history error,TUI 会被刷屏。

建议改成一次性 protocol mismatch 后 disconnect:

if ((event as { v?: unknown }).v !== 1) {
  this.emit({ type: 'disconnected', reason: `unsupported protocol version: ${...}` });
  break; // exit the for-await
}

也可以在 start 之前的 capability 校验里就拒绝 v2 daemon。

private emit(update: DaemonTuiUpdate): void {
try {
this.onUpdate(update);
} catch {

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.

🟡 Low — emit() 完全静默吞掉 renderer 错误

private emit(update: DaemonTuiUpdate): void {
  try {
    this.onUpdate(update);
  } catch {
    /* isolate renderer callback failures from the daemon event pump */
  }
}

理由(隔离 renderer 抛错避免 pump 挂掉)成立,但完全没记录会让渲染器一直抛这种 bug 无法察觉,用户只看到 daemon 静默不响应。

至少加 debugLogger.warn 记录一下 error 和 update.type,运维和开发者能看到信号。

throw new Error('A prompt is already in progress');
}
this.busy = true;
clearDaemonTuiReducerState(this.reducerState);

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.

🟡 Low — prompt 成功与否都先清掉 tool reducer state

现在的语义:每次 sendPrompt 一进来就 clearDaemonTuiReducerState,无论 prompt 是否成功。如果 prompt 立刻失败,上一轮的 tool 状态已经清空,用户失去了上一轮跑到一半发生了什么的上下文。

改成 prompt 成功提交后再清更稳妥:

try {
  const result = await this.session.prompt(...);
  clearDaemonTuiReducerState(this.reducerState);
  return ...
}

或者放到 turn-complete 事件之后清(daemon 协议补齐后)。

...request,
toolCall: {
...sanitizedToolCall,
toolCallId: request.toolCall.toolCallId,

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.

🔵 Nit — toolCallId / requestId / optionId 不 sanitize 的理由可以写在注释里

这里把 sanitize 后的 toolCallId 又覆盖回原始值,理由是这些 id 用于回查/对齐,sanitize 会破坏相关性。但没写注释,后续读者很容易觉得是 bug 漏 sanitize。

建议加一两行注释,顺便提醒 downstream log 输出 id 时要自己再 sanitize,避免日志注入。

state.toolCallOrder.length = 0;
}

const MAX_TOOL_CALLS = 128;

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.

🔵 Nit — MAX_TOOL_CALLS = 128 写死

对 TUI 一次会话来说 128 一般够,但深度依赖工具链的 turn 会爆。建议在 DaemonTuiAdapterOptions 里暴露 maxToolCalls?: number,默认 128。

{
type: 'permission_resolved',
requestId: event.data['requestId'],
outcome: event.data['outcome'],

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.

🔵 Nit — outcome 字段直接透传

和 channel bridge 那条评论同源:outcome: event.data['outcome'] 没做任何 shape 校验,消费方拿到的可能是 undefined 或任意结构。

建议要么 narrow 一下,要么在 DaemonTuiUpdate.permission_resolved.outcome 的类型上明确写 unknown + 消费方自己处理。


## Minimal Flow

1. Create `DaemonClient` with daemon URL and token.

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.

🟡 Low — capability 校验列表与 Minimal Flow 不对齐

这里只要求 session_create / session_prompt / session_events,但 Minimal Flow 第 6-8 步用了 session.cancel() / setModel() / respondToPermission()
如果 daemon 不广告这些 capability,这些路径会在运行时静默崩。

要么补进必需 capability,要么在 doc 里说明它们可选(并解释为什么)。

- Session-scoped permission route.
- Output sink refactor for JSONL / stream-json / dual-output parity.
- Session lifecycle close/delete semantics.
- Runtime diagnostics for MCP, skills, providers, and workspace env.

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.

🟡 Low — Blockers 列表缺 issue 链接

PR description 已经引用了 #4175 / #4201,doc 里都是纯文字。建议把每条 blocker 挂上对应 issue(typed daemon event schema (#XXXX) 等),方便读者跟进进度。

@chiga0

chiga0 commented May 18, 2026

Copy link
Copy Markdown
Collaborator Author

Generated by GPT-5 Codex

Processed the latest #4202 TUI daemon adapter comments with scoped changes only inside the adapter spike:

  • Fixed the stop drain timer cleanup by clearing the timeout when the pump resolves first; timeout handles are also unref()ed where available.
  • Kept prompt RPC failures as disconnecting failures, but changed cancel/model/permission RPC failures to emit sanitized history errors instead of aborting the SSE event pump. This avoids dropping a healthy stream on a recoverable control-plane failure.
  • Added validation/sanitization for permission_resolved.outcome; malformed outcome payloads no longer pass through raw daemon data.
  • Capped unknown event type tracking and unsupported protocol-version tracking. Unsupported protocol versions now emit one visible history error per version and still advance lastEventId, avoiding replay/spam loops.
  • Re-aborted the stale controller in the forced-idle timeout path. The old controller was already aborted by stop(), but this makes the timeout path explicit and harmless.

Triage notes:

  • The broader _meta / usage / stop-hook-loop / true turn metadata gap remains a daemon protocol/schema roadmap dependency, not something this default-off adapter should invent locally.
  • Renderer integration details such as full tool_group_update delta semantics remain follow-up work and were intentionally not expanded in this PR.

Local validation:

  • cd packages/cli && npx vitest run src/ui/daemon/DaemonTuiAdapter.test.ts
  • cd packages/cli && npx eslint src/ui/daemon/DaemonTuiAdapter.ts src/ui/daemon/DaemonTuiAdapter.test.ts --max-warnings 0 --no-warn-ignored
  • cd packages/cli && npx prettier --check src/ui/daemon/DaemonTuiAdapter.ts src/ui/daemon/DaemonTuiAdapter.test.ts
  • npm run build --workspace @qwen-code/qwen-code-core && cd packages/cli && npm run typecheck
  • cd packages/cli && npm run build

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

本地完整验证通过(worktree + tmux,HEAD de6cb66f4):

  • npm ci
  • npx vitest run src/ui/daemon/DaemonTuiAdapter.test.ts ✅ 14/14 passed
  • npm run typecheck
  • npx eslint src/ui/daemon/DaemonTuiAdapter.{ts,test.ts} --max-warnings 0 --no-warn-ignored
  • npx prettier --check (adapter + test + docs/developers/daemon-client-adapters/tui.md) ✅
  • npm run build(cli 包 + 根 monorepo)✅
  • git diff --check origin/main...HEAD
  • Remote CI: Lint / Test (mac+ubuntu+win) / CodeQL 全绿

R7 review 的 4 条均在最新 commit de6cb66f4 落实:

  • F1 (Critical) forceIdleAfterPumpTimeout — 先 staleController?.abort()pumpGeneration += 1,避免 stale pump 与新 pump 并存。
  • F3 sanitizePermissionRequesttoolCallId 保留原始值(opaque identifier 不应被 sanitize 改写,display 字段 kind/title/options.name 已过滤)。
  • F4 permission_resolved.outcome — 通过 sanitizePermissionOutcome 严格校验 cancelled / selected 形状,非法 payload 返回 undefined
  • F8 UNKNOWN_EVENT_TYPESMAX_UNKNOWN_EVENT_TYPES = 100 容量上限。
  • 附带的 timer 泄漏 / cancel·setModel·permission RPC 失败不断连 SSE 也已修复。

改动范围 3 文件、+1970/-0,default-off TUI adapter spike,未接入默认交互 TUI 路径,符合 Stage 1.5 wave 工程原则(独立可合、向后兼容、默认关闭、可回滚、单测覆盖)。LGTM 👍

@wenshao wenshao merged commit d07c958 into main May 18, 2026
9 checks passed
doudouOUC added a commit that referenced this pull request May 18, 2026
Picks up MCP guardrails (PR 14) + TUI daemon adapter spike (PR #4202).
Two conflicts in status.ts / status.test.ts where PR 14's
`'budget_exhausted'` error kind lands alongside our PR 16
`'stat_failed'` addition; both kept as union additions to the closed
taxonomy, with the test comment updated to mention both PRs.

Local validation: typecheck OK; serve+memory **513/513**; SDK
**350/350**; eslint clean.
@yiliang114 yiliang114 added the skip-changelog Exclude from release notes label May 20, 2026
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

skip-changelog Exclude from release notes type/feature-request New feature or enhancement request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants