Skip to content

feat(channel): add daemon bridge spike#4203

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

feat(channel): add daemon bridge spike#4203
wenshao merged 8 commits into
mainfrom
feat/channel-web-daemon-adapter-draft

Conversation

@chiga0

@chiga0 chiga0 commented May 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • What changed: Added a locally verifiable DaemonChannelBridge in @qwen-code/channel-base for server-side channel/web backends to bind daemon sessions, consume daemon SSE events, collect assistant text, and route permission/cancel/model operations.
  • Why it changed: Channel bots and web chat backends can dogfood Mode B early, but the daemon token and session state must stay in the backend/BFF process rather than browser JavaScript or platform clients.
  • Reviewer focus: Server-side-only boundary, bridge compatibility with current channel event shapes, and the fact that qwen channel start still uses the existing ACP bridge by default.

Validation

  • Commands run:
    cd packages/channels/base && npx vitest run src/DaemonChannelBridge.test.ts
    cd packages/channels/base && npm run build
    npx eslint packages/channels/base/src/DaemonChannelBridge.ts packages/channels/base/src/DaemonChannelBridge.test.ts --max-warnings 0 --no-warn-ignored
    cd packages/channels/base && npx prettier --check src/DaemonChannelBridge.ts src/DaemonChannelBridge.test.ts ../../../docs/developers/daemon-client-adapters/channel-web.md
    git diff --check
  • Prompts / inputs used: Unit tests use a fake daemon session and synthetic daemon events.
  • Expected result: Bridge binds sessions, collects streamed assistant text, fans out daemon events, and forwards backend operations without changing existing channel behavior.
  • Observed result: Targeted vitest passed with 4 tests. Channel-base build, targeted ESLint, Prettier check, and diff check passed.
  • Quickest reviewer verification path: cd packages/channels/base && npx vitest run src/DaemonChannelBridge.test.ts.
  • Evidence: Test output reports src/DaemonChannelBridge.test.ts (4 tests) and Test Files 1 passed.

Scope / Risk

  • Main risk or tradeoff: This is a server-side bridge spike only. It proves the channel/web backend daemon surface but does not yet wire any built-in channel or browser BFF route to use it.
  • Not covered / not validated: No live qwen serve smoke, no qwen channel start flag/env wiring, no browser websocket/SSE BFF route, no per-request sessionScope, and no default channel migration.
  • Breaking changes / migration notes: None. Existing ACP channel bridge, Telegram, Weixin, Dingtalk, plugin channels, and browser behavior 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 is a well-structured draft design document for channel adapters and web backends to consume qwen serve through DaemonSessionClient. The document clearly articulates goals, boundaries, security constraints, and migration blockers. As a docs-only draft PR with no runtime changes, it successfully sets the foundation for future implementation work.

🔍 General Feedback

  • Clear separation between server-side clients (allowed) and browser direct-to-daemon (explicitly forbidden)
  • Good security posture throughout - daemon tokens kept server-side, browser isolation maintained
  • Well-organized structure with explicit non-goals and merge safety checklist
  • The event mapping contract table is particularly useful for implementers
  • Session isolation constraints are clearly documented with practical deployment patterns
  • Blockers section provides actionable prerequisites before default migration

🎯 Specific Feedback

🔵 Low

  • Line 22-25 (Proposed Entry Points) - Consider adding a note about port configuration flexibility. The hardcoded 4170 port may conflict in multi-daemon deployments. Suggest adding: "Port can be customized via QWEN_DAEMON_PORT or daemon configuration."

  • Line 40-47 (Minimal Channel Flow) - Step 1 mentions "Resolve channel sender/thread to a channel session key" but doesn't specify the key format or collision avoidance strategy. Consider adding a brief example: e.g., channelId:userId:threadId hash.

  • Line 68 (Session Isolation Constraint) - The three "safe shapes" are helpful, but consider adding guidance on which shape is recommended for common scenarios (e.g., "For Telegram bots with multiple channels, use one daemon per channel thread").

  • Line 75-82 (Event Mapping Contract) - Consider adding session_created event to the table if it exists, as backends may need to know when a new session starts for initialization/logging purposes.

  • Line 106-112 (Blockers Before Default Migration) - These blockers should ideally link to tracking issues. Consider adding GitHub issue references (e.g., "Per-request sessionScope (#XXXX)") to help readers track progress.

✅ Highlights

  • Excellent security boundaries: explicit prohibition of browser direct-to-daemon access, daemon token handling clearly server-side only
  • The "Explicit Non-Goals" section is well-scoped and prevents scope creep during implementation
  • Merge Safety checklist provides clear confidence that this draft won't accidentally change defaults
  • Event mapping contract table is comprehensive and includes proper error handling guidance ("Unknown daemon events must be ignored")
  • Validation Plan covers the right testing pyramid: unit tests → smoke tests → integration verification
  • Document correctly emphasizes that existing ACP channel bridge remains default, enabling gradual migration

@chiga0 chiga0 changed the title docs(channel): draft daemon adapter plan feat(channel): add daemon bridge spike May 16, 2026
Comment thread packages/channels/base/src/DaemonChannelBridge.ts
Comment thread packages/channels/base/src/DaemonChannelBridge.ts
const session = this.ensureSession(sessionId);
const chunks: string[] = [];
const onChunk = (sid: string, chunk: string) => {
if (sid === sessionId) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] prompt() SSE/HTTP race — text chunks silently dropped

session.prompt() resolves on the HTTP POST response channel while textChunk events arrive on the independent SSE stream via pumpEvents. When the POST resolves before all SSE chunks have been iterated by the for await loop, the finally block removes the onChunk listener and remaining chunks are emitted to no subscriber. The returned text is incomplete with no error signal.

The mock in tests masks this by pushing events synchronously inside session.prompt(), guaranteeing ordering. In production the POST and SSE travel on separate TCP connections with no ordering guarantee.

Suggested fix: Have prompt() await a completion signal from the SSE stream (e.g., a terminal stopReason-bearing session_update) rather than relying on the HTTP response lifetime. Or collect text in handleSessionUpdate into a per-session buffer that prompt() drains after observing the turn-end marker.

— glm-5.1 via Qwen Code /review

Comment thread packages/channels/base/src/DaemonChannelBridge.ts
Comment thread packages/channels/base/src/DaemonChannelBridge.ts
Comment thread packages/channels/base/src/DaemonChannelBridge.ts Outdated
Comment thread packages/channels/base/src/DaemonChannelBridge.ts

try {
await session.prompt({ prompt });
} finally {

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] prompt() discards stopReason from PromptResult

The method returns only chunks.join(''), losing the stopReason field that distinguishes end_turn from cancelled or tool_use. Channel/web backends that need to display a "response was interrupted" indicator or handle cancellation differently cannot tell the difference. This is a new API — now is the right time to enrich the return type.

Suggested fix: Return Promise<{ text: string; stopReason?: string }> instead of Promise<string>.

— glm-5.1 via Qwen Code /review

Comment thread packages/channels/base/src/DaemonChannelBridge.ts
Comment thread packages/channels/base/src/DaemonChannelBridge.ts
Comment thread packages/channels/base/src/DaemonChannelBridge.ts
Comment thread packages/channels/base/src/DaemonChannelBridge.ts
@chiga0 chiga0 force-pushed the feat/channel-web-daemon-adapter-draft branch from e61eac1 to 0c90189 Compare May 16, 2026 15:13
@chiga0

chiga0 commented May 16, 2026

Copy link
Copy Markdown
Collaborator Author

处理了这轮 review:

  • fixed: session_died 会清理 requestId -> sessionId 映射;listener 同步 respondToPermission 时现在返回 false,不抛错
  • fixed: pumpEvents 正常结束和非 abort error 都按 session death 清理 sessions/eventControllers/request mappings,避免 zombie session
  • fixed: EventEmitter 默认挂 no-op error listener,daemon stream error 不会因为无人监听 error 而打崩进程
  • fixed: 同一个 session 并发 prompt 现在明确拒绝,避免两个 prompt 共享 textChunk listener 串流
  • fixed: prompt 绑定 AbortController,sessionDied 会 abort 正在进行的 HTTP prompt
  • fixed: respondToPermission 遇到 stale/dead session 返回 false
  • fixed: permission_request 校验 requestId/toolCall/options
  • fixed: available_commands_update 改为按 session 保存,并保留 legacy availableCommands getter 的 latest 行为
  • tests: 补了 unknown session、并发 prompt、image prompt block、available commands、stream error/normal completion、session death stale permission response

有效但需要协议/roadmap 后续处理的点:

  • prompt 文本完整性不能只靠 client 侧彻底证明。现在 daemon 的 POST /prompt 和 SSE 是两条通道,且没有 terminal turn-complete SSE frame 或 prompt response lastEventId;因此真正解决“HTTP 先返回但 SSE 还没 drain”需要 daemon 事件契约补一个终止标记/水位。本 PR 已修本地可修的清理、并发和 abort 问题,但不假装 client-only 可以完全保证该语义。
  • prompt 返回 stopReason 的 API enrich 先不在本 PR 改,避免和当前 AcpBridge/ChannelBase 的 string 返回形态产生不必要破坏;等 daemon channel API 统一时一起调整。

验证:

  • cd packages/channels/base && npx vitest run src/DaemonChannelBridge.test.ts
  • cd packages/channels/base && npm run build
  • cd packages/channels/base && npx eslint src/DaemonChannelBridge.ts src/DaemonChannelBridge.test.ts --max-warnings 0 --no-warn-ignored
  • 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/channels/base/src/DaemonChannelBridge.ts
Comment thread packages/channels/base/src/DaemonChannelBridge.ts
@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/channel-web-daemon-adapter-draft branch from 0c90189 to 1f8ff59 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 mergeable and remains draft while CI runs.
  • Addressed the valid channel/web bridge review comments that can be fixed client-side:
    • session death and stream failures now clean sessions, controllers, permission mappings, active prompt state, and command state;
    • default no-op error listener prevents EventEmitter error crashes;
    • concurrent prompts for one session are rejected;
    • prompt calls are abortable on session death, dropSession, and bridge stop;
    • unknown/stale permission responses return false;
    • available commands are tracked per session while preserving the legacy latest getter;
    • stream_error / client_evicted drop the session with the daemon-provided reason;
    • model_switch_failed emits a dedicated failure event;
    • tests cover unknown sessions, concurrent prompt rejection, image prompt blocks, available commands, stream errors, normal stream completion, terminal stream frames, session-death stale permission responses, and stop-time prompt abort.

Local validation passed:

  • cd packages/channels/base && npx vitest run src/DaemonChannelBridge.test.ts
  • cd packages/channels/base && npm run build
  • cd packages/channels/base && npx eslint src/DaemonChannelBridge.ts src/DaemonChannelBridge.test.ts --max-warnings 0 --no-warn-ignored
  • cd packages/channels/base && npx prettier --check src/DaemonChannelBridge.ts src/DaemonChannelBridge.test.ts

I resolved the addressed review threads. Two items remain intentionally open:

  • prompt text completeness still needs daemon-side turn-complete / SSE waterline semantics, because HTTP /prompt and SSE events are independent channels;
  • changing prompt() to return { text, stopReason } is an API-shape decision and should be handled with the channel API migration instead of sneaking a breaking change into this draft.

@github-actions

github-actions Bot commented May 17, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Summary

Package Lines Statements Functions Branches
CLI 77.27% 77.27% 78.87% 80.4%
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.27 |     80.4 |   78.87 |   77.27 |                   
 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.44 |   85.29 |   75.63 |                   
  ...sksDialog.tsx |   70.92 |    80.39 |   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/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.51 |   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.67 |   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.

): Promise<boolean>;
}

export interface DaemonChannelSessionFactoryRequest {

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] Thread the daemon sessionScope override through this bridge. The SDK/daemon now support per-request sessionScope, and the production daemon default is still single, which coalesces same-workspace session creation. Since this bridge is intended for multi-user channel/web backends, newSession()/loadSession() currently have no way to request isolated daemon sessions even when the channel router maps different senders/threads to different logical sessions. That can silently attach unrelated channel users or web rooms to the same daemon session and leak conversation/tool/permission state across them.

Please add a sessionScope option to the factory request/bridge options and pass the appropriate isolated scope (for example thread) when creating channel/web sessions, with a regression test that two logical channel sessions pass the override instead of inheriting the daemon default.

— gpt-5.5 via Qwen Code /review

return this.connected;
}

private attachSession(session: DaemonChannelSessionClient): 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] Guard against stale pumps when replacing a session binding. attachSession() aborts the old event controller, but the old async iterator can still yield or finish during the abort race, and pumpEvents()/dropSession() operate only by sessionId. If the same sessionId has already been reattached, an old session_died, stream_error, completion, or error can delete the newly bound session, clear permissions/commands, and abort the new prompt state.

Before handling yielded events or dropping on completion/error, verify the pump is still current, e.g. this.sessions.get(session.sessionId) === session and the stored controller/signal still matches. When replacing a binding, also abort any prompt controllers for that session or reject replacement while a prompt is in flight.

— gpt-5.5 via Qwen Code /review

controller.abort();
}
};
this.on('textChunk', onChunk);

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] Avoid using bridge-wide EventEmitter listeners as the internal prompt collection mechanism. Each concurrent prompt on a different session adds its own textChunk and sessionDied listener to the same bridge; with more than 10 active sessions this will trigger Node's MaxListenersExceededWarning, and every streamed chunk fans out to every active prompt listener before being filtered.

Consider tracking active prompt state in a map keyed by sessionId and appending/aborting directly from handleSessionUpdate() / dropSession(). You can still emit textChunk for external observers, but the bridge's own collection should not depend on per-prompt global listeners.

— gpt-5.5 via Qwen Code /review


Channel backend:

```bash

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] Mark these entry points as future/illustrative instead of runnable commands. The document later says the daemon bridge is not wired into qwen channel start yet, and the PR does not add QWEN_CHANNEL_DAEMON_URL, QWEN_WEB_DAEMON_URL, or qwen web-chat-backend wiring. As written, readers can reasonably copy these commands and expect them to work.

Please rename this section to something like Future Entry Points or add a short note before the snippets that these commands are proposed examples, not implemented by this PR.

— gpt-5.5 via Qwen Code /review

this.sessions.clear();
this.requestToSession.clear();
for (const controllers of this.activePromptControllers.values()) {
for (const controller of controllers) {

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] stop() never emits sessionDied for active sessions

stop() aborts all event controllers and clears all maps, but never emits sessionDied for any active session. Consumers listening for sessionDied to trigger cleanup (reconnecting, notifying users, releasing resources) will never learn that sessions were terminated. The next inbound user message fails with "No daemon session bound" with no prior event or log.

Note: controller.abort() causes pumpEvents to skip the dropSession call (because signal.aborted is true), so neither path emits sessionDied during shutdown.

Suggested change
for (const controller of controllers) {
stop(): void {
// Emit sessionDied for active sessions before clearing
for (const [sessionId] of this.sessions) {
this.emit('sessionDied', { sessionId, reason: 'bridge_stopped' });
}
for (const controller of this.eventControllers.values()) {
controller.abort();
}

— glm-5.1 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: DaemonChannelBridge

Reviewed by mimo-v2.5-pro via qwen review.

Scope: packages/channels/base/src/DaemonChannelBridge.ts (480 lines, new file)
Test coverage: 8/8 tests pass. Build clean (tsc + eslint 0 findings).

Summary

Well-structured bridge design with defensive input validation (isRecord/getString guards), proper resource cleanup (dropSession, stop), and good test coverage for the core happy paths. No Critical issues found.

4 Suggestion-level findings below, all in DaemonChannelBridge.ts. Prior review items (SSE/HTTP race, stopReason discard, EventEmitter pattern, stop() no sessionDied, sessionScope threading, stale pump race, test gaps) are already tracked as open inline comments and not repeated here.

}
const requestId = data['requestId'];
this.requestToSession.set(requestId, sessionId);
this.emit('permissionRequest', {

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] handlePermissionRequest validates top-level shape (requestId is string, toolCall is record, options is array) but then casts the entire data as RequestPermissionRequest without validating inner structure.

A malformed permission_request event with an empty {} toolCall or entries missing optionId will produce a typed object with undefined inner fields. Consumers trusting the typed request.toolCall.toolCallId will hit runtime errors.

Suggested fix: Validate required inner fields before casting:

const tc = data['toolCall'] as Record<string, unknown>;
if (typeof tc['toolCallId'] !== 'string' || typeof tc['kind'] !== 'string') {
  return;
}

Alternatively, use a satisfies check on the emit (as you do for the event shape) to catch structural mismatches at compile time.

case 'available_commands_update': {
if (Array.isArray(update['availableCommands'])) {
const commands = update['availableCommands'] as AvailableCommand[];
this.availableCommandsBySession.set(sessionId, commands);

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] available_commands_update casts update['availableCommands'] as AvailableCommand[] without validating individual elements. If a daemon event sends [null, 42, {name: '__proto__', description: "..."}], these are stored verbatim in availableCommandsBySession and exposed via the typed public API (availableCommands / getAvailableCommands()).

Suggested fix: Filter to valid entries:

const commands = (update['availableCommands'] as unknown[]).filter(
  (c): c is AvailableCommand => isRecord(c) && typeof c['name'] === 'string'
);

private readonly options: DaemonChannelBridgeOptions;
private readonly sessions = new Map<string, DaemonChannelSessionClient>();
private readonly eventControllers = new Map<string, AbortController>();
private readonly requestToSession = new Map<string, string>();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] this.on('error', () => {}) prevents uncaught-exception crashes (which is good) but silently discards all error context. When pumpEvents catches a non-abort error (line 275), it emits 'error' then calls dropSession — but the error itself is swallowed. At 3AM you'd see "session died" with no idea why.

Suggested fix: Forward errors to a logger or emit a structured event:

this.on('error', (err) => {
  this.emit('bridgeError', err);
});

Or accept a logger in DaemonChannelBridgeOptions and log here. This gives consumers a way to observe daemon failures without crashing the process.

this.activePromptControllers.delete(sessionId);
}
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] prompt() returns chunks.join('') which is empty string for both: (1) a successful completion with no text chunks (tool-only response), and (2) session death mid-prompt before any chunks arrived. The stopReason from the daemon's prompt result is also discarded — only raw text is returned.

The caller cannot distinguish "the assistant said nothing" from "the session died". Channel/web backends will silently send blank replies to users when sessions die.

Suggested fix: Return a richer result or throw on session-death abort:

// After the try/finally block:
if (controller.signal.aborted) {
  throw new Error(`Session ${sessionId} died during prompt`);
}
return chunks.join('');

Or return { text: chunks.join(''), stopReason } instead of a bare string.

@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: DaemonChannelBridge — Follow-up Verification

Reviewed by mimo-v2.5-pro via qwen review.

Scope: DaemonChannelBridge.ts (480 lines), DaemonChannelBridge.test.ts (623 lines), channel-web.md (120 lines)

Test coverage: 8/8 tests pass. Build clean (tsc + eslint 0 findings).

Verified Fixes (9 of 25 prior findings)

The author addressed 5 critical lifecycle issues and 4 test gaps:

  1. session_died cleanupdropSession now iterates requestToSession and deletes entries for the dead session (line 461).
  2. pumpEvents normal completion — calls dropSession(session.sessionId, 'stream_ended') when SSE stream ends (line 283).
  3. pumpEvents error cleanup — catch block emits error AND calls dropSession (lines 280-288).
  4. stream_error/client_evicted — both terminal events handled in switch, calling dropSession (lines 319-328).
  5. stale pump guardattachSession calls existing?.abort() before registering new session (line 254).
  6. stop() aborts prompt controllers — iterates activePromptControllers and aborts each (lines 449-453).
  7. available_commands_update tested — test case 2 exercises the commands update path.
  8. ensureSession throw tested — test case 4 verifies the throw for unknown sessions.
  9. pumpEvents error path tested — test case 7 covers generator failure.

Still Open (6 issues from prior review)

These issues remain open and should be addressed before this spike graduates from draft:

  1. _availableCommands shared state (line 41) — single instance variable overwritten by any session's available_commands_update.
  2. prompt() discards stopReason (line 199) — callers cannot distinguish end_turn from cancelled or tool_use.
  3. Error swallowing (line 92) — this.on('error', () => {}) silently discards all error context.
  4. stop() never emits sessionDied (line 230) — consumers listening for cleanup are not notified during shutdown.
  5. Image prompt untested — the imageBase64 + imageMimeType code path remains uncovered.
  6. prompt() empty string ambiguity (line 199) — returns '' for both tool-only responses and session death.

Cross-file note

DaemonSessionClient.ts:145 has a lastSeenEventId timing issue (not in this PR's diff) — the property is advanced after yield, so for await...of loops see stale IDs on resume. Separate PR needed.

Verdict: COMMENT

Good progress on critical lifecycle fixes. Remaining issues are suggestions and test gaps that don't block the spike but should be addressed before graduation.

this.availableCommandsBySession.delete(sessionId);
this._availableCommands =
Array.from(this.availableCommandsBySession.values()).at(-1) ?? [];
for (const [requestId, mappedSessionId] of this.requestToSession) {

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.

[Verified Fixed] dropSession now iterates requestToSession and deletes all entries for the dead session. This resolves the prior critical finding about stale permission mappings after session death.

this.handleEvent(session, event);
}
if (!signal.aborted) {
this.dropSession(session.sessionId, 'stream_ended');

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.

[Verified Fixed] pumpEvents normal completion path now calls dropSession(session.sessionId, 'stream_ended'). This resolves the prior critical finding about zombie sessions after stream end.

lastEventId: session.lastEventId,
resume: true,
})) {
this.handleEvent(session, event);

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.

[Verified Fixed] pumpEvents catch block now emits error AND calls dropSession to clean up session state. This resolves the prior critical finding about leaked sessions after stream errors.

case 'session_died':
this.handleSessionDied(session.sessionId, event.data);
break;
case 'client_evicted':

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.

[Verified Fixed] Both stream_error and client_evicted are now handled in the switch statement, calling dropSession with the appropriate reason. This resolves the prior critical finding about unhandled terminal events.


private attachSession(session: DaemonChannelSessionClient): void {
const existing = this.eventControllers.get(session.sessionId);
existing?.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.

[Verified Fixed] attachSession now calls existing?.abort() before registering the new session, preventing stale pumps from interfering with the new binding.

}
this.eventControllers.get(sessionId)?.abort();
this.eventControllers.delete(sessionId);
this.sessions.delete(sessionId);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Verified Fixed] stop() now iterates activePromptControllers and aborts each controller, resolving the prior critical finding about orphaned prompt controllers during shutdown.

@chiga0

chiga0 commented May 17, 2026

Copy link
Copy Markdown
Collaborator Author

Updated #4203 with fix(channel): harden daemon bridge lifecycle (3efff65e3).

Handled the latest channel/web bridge review items without changing the existing prompt(): Promise<string> API shape:

  • kept commands scoped per session and made the legacy availableCommands getter derive from the latest live session instead of storing a separate stale copy;
  • added additive promptComplete event with { sessionId, text, stopReason }, so callers can distinguish empty text/tool-only turns from terminal reasons without a breaking return-type change;
  • changed the default error listener to retain lastDaemonError instead of silently discarding all context;
  • made stop() route live sessions through dropSession(..., "bridge_stopped"), so cleanup consumers receive sessionDied;
  • kept the existing image prompt coverage and added more assertions around prompt completion, command scoping, stop cleanup, and pending generator failure handling;
  • fixed the test EventQueue.fail() helper to reject pending waiters.

Validation passed locally:

cd packages/channels/base && npx vitest run src/DaemonChannelBridge.test.ts
cd packages/channels/base && npm run build
cd packages/channels/base && npx eslint src/DaemonChannelBridge.ts src/DaemonChannelBridge.test.ts --max-warnings 0 --no-warn-ignored
cd packages/channels/base && npx prettier --check src/DaemonChannelBridge.ts src/DaemonChannelBridge.test.ts

Still intentionally not changing prompt() to return { text, stopReason } in this spike, because that is a channel API migration decision and would be a breaking shape change.

Generated by GPT-5 model

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

⚠️ Zero logging across all 508 lines of business logic — no console.error, structured logs, or debug hooks. Error paths (pumpEvents catch, dropSession, respondToPermission returning false, unknown event types silently discarded) produce zero diagnostic output. At 3 AM in production, there is no way to answer: why did the event stream break? Which session is affected? What was the last successfully processed event?

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

mimeType: options.imageMimeType,
});
}
prompt.push({ type: 'text', 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] SSE/HTTP race — text chunks silently dropped

prompt() collects text via textChunk events on the SSE stream (through pumpEvents), but waits for completion via a separate HTTP POST (session.prompt()). When the HTTP POST resolves, there is no guarantee that the final textChunk SSE event has already been processed by pumpEvents. This is a race condition between two independent HTTP connections with no ordering guarantee.

Impact: Intermittently truncated or empty responses returned to users. Classic Heisenbug — depends on network timing, daemon load, and event loop scheduling.

Suggested change
prompt.push({ type: 'text', text });
// Option A: Wait for turn-complete signal on SSE stream before resolving
// Option B: Add a short yield/drain after session.prompt() resolves:
const result = await session.prompt({ prompt }, controller.signal);
await new Promise(resolve => setImmediate(resolve)); // drain microtask queue
const textResult = chunks.join('');

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

return this.connected;
}

private attachSession(session: DaemonChannelSessionClient): 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] attachSession with duplicate sessionId leaves zombie state

When a sessionId already bound gets a new binding, existing?.abort() is called but the old pumpEvents loop skips dropSession() due to the if (!signal.aborted) guard in both the normal completion and error paths. Stale entries in activePrompts, activePromptControllers, requestToSession, and availableCommandsBySession are never cleaned up.

Impact: Orphaned activePrompts entries permanently block new prompts on that sessionId. Orphaned requestToSession entries may route permissions to the wrong session.

Suggested change
private attachSession(session: DaemonChannelSessionClient): void {
private attachSession(session: DaemonChannelSessionClient): void {
+ this.dropSession(session.sessionId, 'session_replaced');
const existing = this.eventControllers.get(session.sessionId);
existing?.abort();

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

} satisfies DaemonPermissionRequestEvent);
}

private handlePermissionResolved(data: 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] handlePermissionResolved does not verify session ownership

handlePermissionResolved(data) receives only data, not the session parameter. It deletes from requestToSession and emits permissionResolved without verifying that the request belongs to the session whose event stream delivered the event. Cross-session permission interference is possible if the daemon routes events incorrectly.

Impact: A permission resolution from one session can silently clear a pending permission request from another session. respondToPermission() returns false with no indication that another session triggered the resolution.

Suggested change
private handlePermissionResolved(data: unknown): void {
private handlePermissionResolved(sessionId: string, data: unknown): void {
if (!isRecord(data) || typeof data['requestId'] !== 'string') {
return;
}
const requestId = data['requestId'];
+ if (this.requestToSession.get(requestId) !== sessionId) {
+ return;
+ }
this.requestToSession.delete(requestId);

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


async loadSession(sessionId: string, cwd: string): Promise<string> {
const session = await this.options.sessionFactory({
workspaceCwd: cwd || this.options.cwd,

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] loadSession returns factory sessionId — may differ from requested

loadSession(sessionId, cwd) returns session.sessionId from the factory, which may differ from the requested sessionId. The caller uses the original sessionId for subsequent operations and gets "No daemon session bound" errors. The test passes only because the fake session hardcodes the same ID.

Impact: Nearly impossible to debug without the original author present. No type safety or runtime check catches this divergence.

Suggested change
workspaceCwd: cwd || this.options.cwd,
this.attachSession(session);
+ if (session.sessionId !== sessionId) {
+ throw new Error(
+ `Session factory returned ${session.sessionId}, expected ${sessionId}`
+ );
+ }
return session.sessionId;

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

break;
}
case 'available_commands_update': {
if (Array.isArray(update['availableCommands'])) {

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] Silent malformed event drops — hanging states with no error signal

handlePermissionRequest (line 399-405), handleModelSwitched (line 443), and other handlers silently return when validation fails without emitting any error event. If the daemon sends malformed data (missing requestId, non-string modelId), the bridge drops it without notifying anyone.

Impact: Permission flows can hang indefinitely. The channel/web backend never sees the permission request and cannot signal a timeout. Zero observability.

Suggested change
if (Array.isArray(update['availableCommands'])) {
if (/* validation fails */) {
+ this.emit('error', new Error(
+ `Malformed permission_request: missing required fields`
+ ));
return;
}

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

this.lastError = 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] availableCommands getter returns non-deterministic cross-session data

The getter returns commands from latestAvailableCommandsSessionId — the most recently updated session — not the caller's session. In multi-session deployments, this silently returns the wrong session's commands. Use getAvailableCommands(sessionId) for scoped access.

Suggested change
get availableCommands(): AvailableCommand[] {
+ // NOTE: Only safe for single-session deployments.
+ // Use getAvailableCommands(sessionId) for multi-session.
if (this.latestAvailableCommandsSessionId) {

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

async setSessionModel(
sessionId: string,
modelId: string,
): Promise<Record<string, unknown>> {

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] cancelSession does not abort in-flight prompt's AbortController

cancelSession() calls session.cancel() but does not abort the AbortController that prompt() created for the same session. If a prompt is in flight, the daemon may cancel the turn but the bridge's local AbortController is not signalled, so the prompt() Promise continues waiting.

Suggested change
): Promise<Record<string, unknown>> {
async cancelSession(sessionId: string): Promise<void> {
+ const controllers = this.activePromptControllers.get(sessionId);
+ if (controllers) {
+ for (const ctrl of controllers) ctrl.abort();
+ }
await this.ensureSession(sessionId).cancel();
}

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

DaemonChannelEvent,
DaemonChannelSessionClient,
DaemonChannelSessionFactory,
DaemonChannelSessionFactoryRequest,

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] DaemonPromptCompleteEvent is not exported

The interface is defined in DaemonChannelBridge.ts and emitted via this.emit('promptComplete', ...) but not exported from index.ts. Consumers listening for promptComplete events cannot type the event payload without importing from the internal source file.

Suggested change
DaemonChannelSessionFactoryRequest,
+ DaemonPromptCompleteEvent,

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

@chiga0 chiga0 force-pushed the feat/channel-web-daemon-adapter-draft branch from 3efff65 to 83777ab Compare May 17, 2026 08:57
@chiga0

chiga0 commented May 17, 2026

Copy link
Copy Markdown
Collaborator Author

Generated by GPT-5 model

Updated #4203 after triaging the latest review comments. Valid fixes included in 83777ab22:

  • Replaced duplicate-session attach behavior with explicit session_replaced cleanup so old event pumps, active prompts, commands, and permission ownership state cannot leak.
  • Added loadSession() session id validation so a mismatched factory/daemon response is rejected before binding the wrong session.
  • Scoped permission_resolved handling to the owning session and surface non-owning/unknown resolution frames through the bridge error channel.
  • Made cancelSession() abort local in-flight prompt controllers while still forwarding cancel to the daemon session.
  • Surfaced malformed permission_request, permission_resolved, model_switched, model_switch_failed, malformed session_update, and malformed available_commands_update frames through the existing error channel / lastDaemonError instead of silently dropping them.
  • Exported DaemonPromptCompleteEvent from channel-base.
  • Added a bounded event-loop drain after prompt() resolves so SSE frames already queued around HTTP completion are collected before promptComplete.

Notes from triage:

  • The availableCommands getter is intentionally a latest-session compatibility convenience; session-scoped consumers should use getAvailableCommands(sessionId), which is already covered.
  • The prompt drain is a client-side mitigation for the HTTP/SSE race, not the final protocol guarantee. Fully closing that race still belongs to the typed terminal/completion event work in the daemon protocol roadmap.
  • I did not add console logging; malformed daemon frames are exposed as bridge error events so callers can decide how/where to log without affecting current channel behavior.

Local verification:

  • cd packages/channels/base && npx vitest run src/DaemonChannelBridge.test.ts
  • cd packages/channels/base && npm run build
  • cd packages/channels/base && npx eslint src/DaemonChannelBridge.ts src/DaemonChannelBridge.test.ts src/index.ts --max-warnings 0 --no-warn-ignored
  • npm run typecheck

@chiga0 chiga0 marked this pull request as ready for review May 17, 2026 09:21
@chiga0 chiga0 requested review from doudouOUC and wenshao May 17, 2026 09:21
@chiga0

chiga0 commented May 17, 2026

Copy link
Copy Markdown
Collaborator Author

Generated by GPT-5 model

Marked this PR ready for review after confirming the latest head is mergeable and CI is green across Classify PR, Lint, CodeQL, and macOS/Ubuntu/Windows tests. Requested review from wenshao and doudouOUC.

Merge-safety summary: this remains a default-off channel/web daemon bridge spike. It adds the adapter/export and tests, but does not switch existing channel/web runtime paths to daemon. The remaining HTTP/SSE prompt-completion waterline concern is explicitly a protocol-roadmap item, not a hidden default-path behavior change in this PR.

);
return;
}
if (mappedSessionId !== sessionId) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] handlePermissionResolved leaks requestToSession entries in the cross-session mismatch branch

When mappedSessionId !== sessionId, the method emits a protocol error and returns at line 472 — but never calls this.requestToSession.delete(requestId). The owning session stays alive, so the stale entry is never cleaned up. Repeated mismatched resolution events cause unbounded Map growth (memory leak).

Suggested change
if (mappedSessionId !== sessionId) {
if (mappedSessionId !== sessionId) {
this.requestToSession.delete(requestId);
this.emitProtocolError(
`Ignoring daemon permission_resolved for request ${requestId} from non-owning session ${sessionId}`,
data,
);
return;
}

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

this.off('sessionDied', onSessionDied);
this.activePrompts.delete(sessionId);
controllers.delete(controller);
if (controllers.size === 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.

[Critical] prompt() finally block race condition: stale Set reference can delete new prompt's abort controllers

The finally block uses controllers.size === 0 to decide whether to delete from this.activePromptControllers. When a session is replaced mid-prompt: (1) dropSessionabortActivePrompts deletes the old Set from the Map, (2) new prompt() inserts a new Set, (3) the old finally block's captured controllers (the old Set) is empty, so controllers.size === 0 passes — deleting the NEW prompt's controllers and orphaning its abort signal. The new prompt becomes uncancellable.

Suggested change
if (controllers.size === 0) {
if (this.activePromptControllers.get(sessionId) === controllers) {
this.activePromptControllers.delete(sessionId);
}

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

async cancelSession(sessionId: string): Promise<void> {
const session = this.ensureSession(sessionId);
this.abortActivePrompts(sessionId);
await session.cancel();

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] cancelSession does not clear activePrompts Set — mismatch with dropSession

cancelSession calls this.abortActivePrompts(sessionId) but never calls this.activePrompts.delete(sessionId). Compare with dropSession which does both. If the daemon ignores the abort signal and the prompt promise never settles, activePrompts permanently retains the session ID, blocking all future prompts for that session with "Prompt already in flight."

Suggested change
await session.cancel();
async cancelSession(sessionId: string): Promise<void> {
const session = this.ensureSession(sessionId);
this.abortActivePrompts(sessionId);
this.activePrompts.delete(sessionId);
await session.cancel();
}

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

this.activePromptControllers.delete(sessionId);
}

private emitProtocolError(message: string, details: 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] emitProtocolError leaks raw daemon payload to the error channel

The method attaches the original daemon event data as error.details. Daemon payloads may contain file contents, agent responses, or API error details from permission_request, session_update, or model_switch_failed events. If any downstream consumer logs these errors (via console.error or structured logging), sensitive data persists in logs. Consider stripping details or only including known-safe fields (event type, requestId) in the error message itself.

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

@chiga0

chiga0 commented May 17, 2026

Copy link
Copy Markdown
Collaborator Author

Generated by GPT-5 model

Handled the latest valid channel/web bridge review comments in 72555381e:

  • Added sessionScope to the daemon channel session factory request and bridge options. The bridge now requests thread scope by default for channel/web isolation, while still allowing callers to override the scope explicitly.
  • Added a current-pump guard before handling yielded SSE events and before stream completion/error cleanup, so an old pump from a replaced session binding cannot delete or mutate the newly attached session.
  • Strengthened permission_request validation to require inner toolCall.toolCallId, toolCall.kind, and option optionId values before emitting a typed permission request.
  • Filtered available_commands_update entries before exposing them via the typed public API.
  • Added regression coverage for default/custom session scope propagation, stale old-pump events after duplicate session replacement, and invalid available-command filtering.

Validation run locally:

  • cd packages/channels/base && npx vitest run src/DaemonChannelBridge.test.ts
  • cd packages/channels/base && npm run build
  • cd packages/channels/base && npx eslint src/DaemonChannelBridge.ts src/DaemonChannelBridge.test.ts src/index.ts --max-warnings 0 --no-warn-ignored

Notes / triage:

  • The comments about loadSession() id mismatch, cancel aborting local prompts, exported DaemonPromptCompleteEvent, malformed event observability, permission ownership, and duplicate-session cleanup are already handled in the current branch.
  • The availableCommands getter remains a latest-session compatibility convenience; multi-session callers should use getAvailableCommands(sessionId).
  • The HTTP/SSE prompt-completion race is still only mitigated client-side by the bounded drain. The complete fix needs a daemon protocol waterline / terminal turn event, so I am keeping that as a roadmap dependency instead of changing prompt() into a breaking API shape in this spike.

response: RequestPermissionResponse,
): Promise<boolean> {
const sessionId = this.requestToSession.get(requestId);
if (!sessionId) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] cancelSession calls abortActivePrompts before session.cancel() — wrong order

Aborting the prompt controller first causes session.prompt() to reject with AbortError immediately, before the daemon receives the cancel signal. If session.cancel() subsequently throws, the real cancellation error is masked — the caller already received AbortError and has no way to know the cancel failed, meaning the daemon session could remain running while the bridge believes it was cancelled.

Suggested change
if (!sessionId) {
async cancelSession(sessionId: string): Promise<void> {
const session = this.ensureSession(sessionId);
await session.cancel();
this.abortActivePrompts(sessionId);
}

— mimo-v2.5-pro via Qwen Code /review

}
return session;
}

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() never calls session.cancel() on underlying sessions

dropSession aborts the local event controller and cleans up internal maps, but never tells the daemon to stop the session. Any in-flight daemon-side agent work (prompt processing, tool execution) continues running orphaned on the server. Compare with cancelSession() which explicitly calls session.cancel().

Suggested change
stop(): void {
for (const sessionId of Array.from(this.sessions.keys())) {
const session = this.sessions.get(sessionId);
if (session) {
void session.cancel().catch(() => {});
}
this.dropSession(sessionId, 'bridge_stopped');
}
this.eventControllers.clear();
this.sessions.clear();
this.requestToSession.clear();

Using void ... .catch() because stop() is synchronous — the cancel is best-effort.

— mimo-v2.5-pro via Qwen Code /review

@wenshao wenshao dismissed their stale review May 17, 2026 11:19

Re-submitting with corrected line anchors

response: RequestPermissionResponse,
): Promise<boolean> {
const sessionId = this.requestToSession.get(requestId);
if (!sessionId) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] cancelSession calls abortActivePrompts before session.cancel() — wrong order

Aborting the prompt controller first causes session.prompt() to reject with AbortError immediately, before the daemon receives the cancel signal. If session.cancel() subsequently throws, the real cancellation error is masked — the caller already received AbortError and has no way to know the cancel failed, meaning the daemon session could remain running while the bridge believes it was cancelled.

Suggested change
if (!sessionId) {
async cancelSession(sessionId: string): Promise<void> {
const session = this.ensureSession(sessionId);
await session.cancel();
this.abortActivePrompts(sessionId);
}

— mimo-v2.5-pro via Qwen Code /review

}
return session;
}

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() never calls session.cancel() on underlying sessions

dropSession aborts the local event controller and cleans up internal maps, but never tells the daemon to stop the session. Any in-flight daemon-side agent work (prompt processing, tool execution) continues running orphaned on the server. Compare with cancelSession() which explicitly calls session.cancel().

Suggested change
stop(): void {
for (const sessionId of Array.from(this.sessions.keys())) {
const session = this.sessions.get(sessionId);
if (session) {
void session.cancel().catch(() => {});
}
this.dropSession(sessionId, "bridge_stopped");
}
this.eventControllers.clear();
this.sessions.clear();
this.requestToSession.clear();

Using void ... .catch() because stop() is synchronous — the cancel is best-effort.

— mimo-v2.5-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.

2 high-confidence Critical findings on session lifecycle management:

(1) cancelSession wrong orderingabortActivePrompts runs before session.cancel(), masking cancellation failures with AbortError.

(2) stop() leaks daemon sessionsdropSession cleans local state but never calls session.cancel(), leaving daemon-side work orphaned.

Both fixes are shown as inline suggestion blocks.

— mimo-v2.5-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.

All previous review findings (25 comments) have been addressed. 15/15 tests pass, build clean, tsc + eslint 0 findings. No new issues found. LGTM! ✅ — mimo-v2.5-pro via Qwen Code /review

@tanzhenxin tanzhenxin added the type/feature-request New feature or enhancement request label May 17, 2026

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

本地完整对账后发现:6 个 [Verified Fixed] 真修了;R6 的 5 条 critical (08:26 UTC) 由 83777ab22 修完;但 R7-R8 还有 4 条 critical 在 HEAD 72555381e 没修。逐条复用之前的 inline suggestion,请先合到一个修复 commit 再 merge。

本地实测见:验证报告(前次评审已贴)。本次 dismiss 之前的 APPROVED 是因为对账漏了。


async cancelSession(sessionId: string): Promise<void> {
const session = this.ensureSession(sessionId);
this.abortActivePrompts(sessionId);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical reaffirmed] cancelSession 顺序仍是先 abort 再 cancel(HEAD 72555381e)。先 abort 会让 session.prompt() 立刻以 AbortError reject,遮蔽随后 session.cancel() 抛出的真实错误 —— 调用方收到的是 AbortError,无从得知 daemon 端取消是否真的成功。

Suggested change
this.abortActivePrompts(sessionId);
async cancelSession(sessionId: string): Promise<void> {
const session = this.ensureSession(sessionId);
await session.cancel();
this.abortActivePrompts(sessionId);
}


stop(): void {
for (const sessionId of Array.from(this.sessions.keys())) {
this.dropSession(sessionId, 'bridge_stopped');

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 reaffirmed] stop()dropSession 通路从不发 daemon 端 session.cancel()。bridge 关闭后 daemon 侧 prompt/tool 仍在跑,得等 daemon 自己 health-check 兜底。cancelSession() 有调 session.cancel(),对照不一致。

Suggested change
this.dropSession(sessionId, 'bridge_stopped');
stop(): void {
for (const sessionId of Array.from(this.sessions.keys())) {
const session = this.sessions.get(sessionId);
if (session) {
void session.cancel().catch(() => {});
}
this.dropSession(sessionId, 'bridge_stopped');
}

stop() 是同步签名所以走 void ... .catch(),best-effort。

this.off('sessionDied', onSessionDied);
this.activePrompts.delete(sessionId);
controllers.delete(controller);
if (controllers.size === 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.

[Critical reaffirmed] finally 块用 controllers.size === 0 判断后按 key 删 activePromptControllers,但 controllers 是闭包里捕获的 Set。die-and-reattach 后新 prompt 写入了新 Set,旧 finally 触发时会按 sessionId key 误删新 Set,新 prompt 失去 cancel 通路。

Suggested change
if (controllers.size === 0) {
if (this.activePromptControllers.get(sessionId) === controllers) {
this.activePromptControllers.delete(sessionId);
}

用同一性比较守门,避免按 key 盲删。

);
return;
}
if (mappedSessionId !== sessionId) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical reaffirmed] mappedSessionId !== sessionId 分支只 emitProtocolError + return,从不 requestToSession.delete(requestId)。daemon 重复发错误归属的 permission_resolved 会让 Map 无界增长(虽然窄但可被反复触发)。把 delete 挪到 mismatch 分支里:

Suggested change
if (mappedSessionId !== sessionId) {
if (mappedSessionId !== sessionId) {
this.requestToSession.delete(requestId);
this.emitProtocolError(
`Ignoring daemon permission_resolved for request ${requestId} from non-owning session ${sessionId}`,
data,
);
return;
}

this.requestToSession.delete(requestId);
return false;
}
return await session.respondToPermission(requestId, response);

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] respondToPermissionsession.respondToPermission() 抛出异常时不清除 requestToSession 映射

当 daemon 的 respondToPermission 调用失败(网络错误、超时),requestToSession 中的条目会被保留。调用方的后续重试可能在陈旧映射仍然存在的情况下重新尝试操作,导致权限响应在 daemon 侧被重复处理。

Suggested change
return await session.respondToPermission(requestId, response);
try {
return await session.respondToPermission(requestId, response);
} catch {
this.requestToSession.delete(requestId);
throw;
}

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

return await session.respondToPermission(requestId, response);
}

stop(): 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] stop()dropSession 已执行每会话清理后仍存在冗余的 .clear() 调用

stop() 遍历所有会话并调用 dropSession(),后者已从 sessionseventControllersactivePromptsavailableCommandsBySessionrequestToSession 中删除条目。随后对 .clear() 的调用属于空操作;对 activePromptControllers 的 abort 循环更是死代码(控制器已在 abortActivePrompts 中被中止并移除)。这种冗余掩盖了 dropSession 中的潜在 bug:若 dropSession 在某条路径上遗漏了清理,则 stop() 路径会通过,而单独调用 dropSession 的路径(stream_endedsession_died)仍会泄漏状态。

建议:stop() 中移除冗余的 .clear() 调用(第 298–310 行),仅保留必要的批量控制器 abort。

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

this.emit('sessionUpdate', data);
}

private handlePermissionRequest(sessionId: string, data: 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] requestToSession Map 无驱逐机制 —— 未解析的权限请求会无限增长

handlePermissionRequest 每次权限请求都会向 requestToSession 写入条目,仅在 permission_resolveddropSessionrespondToPermission(仅限会话不存在时)时移除。没有超时、TTL 或容量限制。若 daemon 产生大量永不解析的权限请求,该 Map 将持续增长,导致缓慢的内存泄漏。

建议: 引入基于超时的驱逐机制(例如 5 分钟内未解析的请求自动清除并发出协议错误),或设置 Map 的最大容量限制。

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

controller.abort();
}
};
this.on('textChunk', onChunk);

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] prompt() 监听器注册窗口 —— loadSession 恢复时重放的数据块可能丢失

attachSession()resume: true 启动 pumpEvents,导致 daemon 重放历史事件。但 textChunk 监听器(第 230 行)仅在稍后的 prompt() 中注册。在 loadSession 返回与 prompt() 被调用之间,重放的 agent_message_chunk 事件通过 this.emit('textChunk', ...) 发出,但因尚无监听器注册而静默丢失。对于全新会话(newSession)影响较小,但对会话恢复(loadSession)存在实际风险。

建议:attachSession 时即为每个 session 预注册文本块缓冲区,在 prompt() 时排出已缓冲的块;或者延迟启动 pump 直到 prompt() 被调用时。

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

await new Promise<void>((resolve) => setTimeout(resolve, 0));
}

export class DaemonChannelBridge extends EventEmitter {

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] EventEmitter 缺少类型化事件映射

DaemonChannelBridge extends EventEmitter 未使用 Node.js 支持的泛型事件映射参数。导致所有 this.on('textChunk', cb) 中的 cb 推断为 (...args: any[]) => void 而非正确的 (sessionId: string, text: string) => void。事件名称拼写错误(如 'textChunkk')不会产生编译错误。

建议: 定义事件映射接口并使用 EventEmitter<DaemonChannelBridgeEvents>

interface DaemonChannelBridgeEvents {
  textChunk: (sessionId: string, text: string) => void;
  thoughtChunk: (sessionId: string, text: string) => void;
  sessionDied: (info: { sessionId: string; reason: string }) => void;
  // ... 其他事件
}

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

}
break;
}
case 'tool_call':

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] handleSessionUpdatetool_call/tool_call_update 错误格式事件缺少验证

此前几轮修复已为畸形事件添加了 emitProtocolError 调用,覆盖了 session_update 外部包装器、permission_requestpermission_resolvedmodel_switchedmodel_switch_failedavailable_commands_update。但 tool_call/tool_call_update 分支(第 437–450 行)在未进行任何验证的情况下静默构造 ToolCallEvent 对象——缺失的字段以空字符串回退。一个缺失 toolCallId 的 daemon 事件会生成 toolCallId: '' 并静默 emit,而非发出协议错误。这与其余处理程序现已一致的显式错误信号模式不符。

建议:toolCallIdkind 缺失时调用 emitProtocolError,与 permission_requestmodel_switched 的模式保持一致。

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

@chiga0 chiga0 force-pushed the feat/channel-web-daemon-adapter-draft branch from 7255538 to 1381074 Compare May 17, 2026 15:58
@chiga0

chiga0 commented May 17, 2026

Copy link
Copy Markdown
Collaborator Author

Generated by GPT-5 model

Handled the latest valid channel/web daemon bridge review comments in 13810748a after rebasing onto main.

Fixes included:

  • Reordered cancelSession() to call daemon-side session.cancel() before aborting local prompt controllers, so cancel failures are not masked by local AbortError.
  • Made stop() best-effort cancel live daemon sessions before dropping local bridge state.
  • Guarded prompt-controller cleanup with Set identity so an old prompt finally block cannot delete a newly attached session's controller set.
  • Cleared requestToSession on cross-session permission_resolved mismatches and on thrown respondToPermission() calls.
  • Added explicit protocol errors for malformed tool_call / tool_call_update frames missing required tool identity fields.
  • Redacted protocol-error details to known-safe identifiers instead of attaching raw daemon payloads.

Triage/deferred items:

  • Permission request TTL/cap, replay buffering for loadSession() historical chunks, and typed EventEmitter maps are reasonable follow-ups, but they are not required for this default-off bridge spike.
  • The HTTP/SSE prompt-completion waterline remains a daemon protocol roadmap item; this bridge keeps the existing bounded drain mitigation without changing prompt(): Promise<string>.

Local validation run after the change:

  • cd packages/channels/base && npx vitest run src/DaemonChannelBridge.test.ts
  • cd packages/channels/base && npm run build
  • cd packages/channels/base && npx eslint src/DaemonChannelBridge.ts src/DaemonChannelBridge.test.ts src/index.ts --max-warnings 0 --no-warn-ignored

CI status at this check: the new head has entered CI; Classify PR is queued, with downstream jobs pending.

@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。上一轮的修复(permission ownership 清理、并发 prompt 拒绝、abort 传播等)都很扎实,整体已经接近可合并基线。这里再挑出几条值得关注的协议契约空白和小毛病,详见 inline。

结论:approve with minor nits。中等级别的 #1 / #2 / #3 建议至少在合并前补 TODO 或 follow-up issue 跟踪,其它项可以在后续 wave 处理。


try {
const result = await session.prompt({ prompt }, controller.signal);
await drainDaemonEventLoop();

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 — drainDaemonEventLoop 只是单 tick 启发式

你在评论里已经点出真正解决方案要等 daemon 那边补 turn-complete SSE frame / prompt response 的 lastEventId waterline,但代码里没有 TODO 也没有链接,后人很容易误以为这是稳态实现。

建议加一行注释,挂上 #4201(或 daemon roadmap issue)说明这是临时方案、何时可移除。

// TODO(#4201): replace single-tick drain once daemon emits a terminal
// turn-complete SSE frame or prompt response carries a lastEventId.
await drainDaemonEventLoop();

return false;
}
try {
return await session.respondToPermission(requestId, response);

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 — 成功路径未立即清理 requestToSession

现在 success 只依赖后续 permission_resolved 事件来清 map:

try {
  return await session.respondToPermission(requestId, response);
} catch (error) {
  this.requestToSession.delete(requestId);
  throw error;
}

如果 daemon 漏发 permission_resolved(网络抖动 / daemon bug),同一个 requestId 第二次调用会再次走到 session.respondToPermission,可能拿到一个不该有的结果(mock 端返回 true,真 daemon 端可能 reject 也可能 idempotent 通过)。

两个备选:

  1. 成功也清掉 map(最简单);
  2. 引入 已响应未 resolved 中间态,二次调用直接返回 false。

倾向选 1,逻辑更对称。

this.requestToSession.delete(requestId);
this.emit('permissionResolved', {
requestId,
outcome: 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.

🟠 Medium — outcome 字段没有形状校验

这里仅校验了 requestId 是 string,outcome 直接透传 data['outcome'],可能是 undefined 或任意结构,会把 daemon 的 garbage 转手给消费方。

建议要么在这里做最小形状收敛(比如要求 outcome 是 record 且有 outcome 子字段),要么在 doc/type 上明确消费方需要自己处理 undefined / 任意 shape。

private readonly sessions = new Map<string, DaemonChannelSessionClient>();
private readonly eventControllers = new Map<string, AbortController>();
private readonly requestToSession = new Map<string, string>();
private readonly activePrompts = 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.

🟡 Low — activePrompts Set 与 activePromptControllers Map 冗余

既然实现已经拒绝同一 session 并发 prompt(line 226),activePromptControllers.get(sid).size 最多就是 1,外层再套一个 Set<AbortController> 是过度设计。

建议改成 Map<sessionId, AbortController>:

  • 并发判断直接 this.activePromptControllers.has(sessionId),可以删掉 activePrompts Set;
  • finally 清理变成 if (this.activePromptControllers.get(sessionId) === controller) this.activePromptControllers.delete(sessionId)

当前代码没有 bug,纯可读性改进。

}
}

private handleSessionUpdate(sessionId: string, data: 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.

🟡 Low — handleSessionUpdate 没校验 data.sessionId 与 pump 一致

这里的 sessionId 取自 pump 上下文(外层调用方传入),完全没看 event payload 里的 data.sessionId。daemon 受信,短期没问题;但加一行 defensive assert 能让 daemon 协议错乱时大声 fail,而不是静默把别的 session 的 update 当成本 session 的:

if (isRecord(data) && typeof data['sessionId'] === 'string' && data['sessionId'] !== sessionId) {
  this.emitProtocolError('session_update sessionId mismatch', data);
  return;
}

return summary;
}

async function drainDaemonEventLoop(): 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.

🔵 Nit — 单调用方的顶层函数

drainDaemonEventLoop 只有 line 267 一个调用点,可以内联或挪进类里。当前位置看起来像是个公共 utility,反而误导。


export interface DaemonChannelEvent {
id?: number;
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.

🔵 Nit — v: 1 字面量缺乏版本演进策略说明

写死 1 暗示有 v2、v3 演进,但没文档/注释说明协议版本的兼容策略。建议要么放一条注释指向 daemon 协议版本规范,要么改成 v: 1 | 2(等 v2 出现时)。

此外这个 interface 可以考虑放进 @qwen-code/daemon-protocol 之类的共享包,避免 channel-base 和 daemon client 各自 redeclare。

| ---------------------------------------- | -------------------------------------- |
| `session_update` / `agent_message_chunk` | Append assistant text |
| `session_update` / `agent_thought_chunk` | Optional hidden/debug stream |
| `session_update` / `tool_call` | Emit tool status card/message |

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 — 事件映射表与实现不一致

表格只列了 tool_call,但实现同时处理 tool_calltool_call_update(DaemonChannelBridge.ts:484-503)。建议补一行或合并表述:

| `session_update` / `tool_call` / `tool_call_update` | Emit tool status card/message |

Channel backend:

```bash
QWEN_CHANNEL_DAEMON_URL=http://127.0.0.1:4170 qwen channel start telegram

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 — 端口写死 4170

例子里 http://127.0.0.1:4170 是字面量,没提端口可配置。在多 daemon 部署(比如 per-channel-thread / per-workspace)下容易冲突。

建议加一句:端口可通过 daemon 启动参数 / 配置文件调整,这里只用 4170 作为示例默认值。


## Blockers Before Default Migration

- Per-request `sessionScope`.

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 链接

这些 blocker 在 PR description 里其实已经引用了 #4175 / #4201,但 doc 里是纯文字。建议把每条 blocker 挂上对应 issue(per-request sessionScope (#XXXX) 等),方便读者跟进进度。

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

No new high-confidence findings. All previously-raised critical issues (cancelSession ordering, stop() calling session.cancel(), dropSession cleanup, pumpEvents error handling, stale pump guards, sessionScope threading, stream_error/client_evicted handling) have been addressed in the latest commit.

Build clean, 15/15 tests pass, tsc + eslint 0 findings.

⚠️ Downgraded from Approve to Comment: CI still running.

Remaining open items are suggestions and test gaps (not blockers for a draft spike PR).

— mimo-v2.5-pro via Qwen Code /review

@chiga0

chiga0 commented May 18, 2026

Copy link
Copy Markdown
Collaborator Author

Generated by GPT-5 Codex

Processed the latest valid #4203 channel/web daemon bridge review nits with a narrow bridge-only change:

  • Added an explicit daemon-roadmap TODO on the current single-tick drainDaemonEventLoop() heuristic, pointing to the future terminal turn event / SSE waterline contract.
  • respondToPermission() now clears the active requestToSession route on successful daemon acceptance. To avoid turning the normal follow-up SSE permission_resolved into an unknown-request false positive, the bridge keeps a capped responded-request ownership map only for validating that follow-up event, then clears it when the event arrives or the session dies.
  • Added shape validation for permission_resolved.outcome; malformed outcomes now emit a redacted protocol error and do not fire permissionResolved.

Triage notes:

  • The HTTP/SSE prompt completion waterline remains a daemon protocol/schema roadmap dependency, so this PR keeps the bounded client-side mitigation instead of changing channel prompt() API shape.
  • I did not expand scope into typed EventEmitter maps, permission TTL policy, or replay buffering; those remain follow-up items and are not needed for this default-off bridge spike.

Local validation:

  • cd packages/channels/base && npx vitest run src/DaemonChannelBridge.test.ts
  • cd packages/channels/base && npx eslint src/DaemonChannelBridge.ts src/DaemonChannelBridge.test.ts src/index.ts --max-warnings 0 --no-warn-ignored
  • cd packages/channels/base && npx prettier --check src/DaemonChannelBridge.ts src/DaemonChannelBridge.test.ts
  • cd packages/channels/base && npm run build

@chiga0 chiga0 requested a review from wenshao May 18, 2026 02:03

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

前次 CHANGES_REQUESTED 的 4 条 critical 在 HEAD b24c2e2ce 全部对账通过:

  • L316-321 cancelSession —— 顺序按建议改成 await session.cancel(); abortActivePrompts(...);,且多加了 activePrompts.delete(sessionId) 兜底
  • L360-372 stop() —— 每个 session 先 void session.cancel().catch(e => this.lastError = e),再 dropSessionstop() 同步签名下走 best-effort,错误存 lastError 而非丢弃,比我建议的还细
  • L307-311 prompt() finally —— 用 this.activePromptControllers.get(sessionId) === controllers 同一性比较守门,逐字采纳建议
  • L592-594 handlePermissionResolved mismatch —— mismatch 分支里 requestToSession.delete + respondedRequestToSession.delete 都做;外加引入二级 map respondedRequestToSession 让 ownership 判断在 delete 后仍可续,是建议范围外的额外健壮性

本地实测:

  • npx vitest run DaemonChannelBridge.test.ts17/17 passed(新加 "clears permission ownership when daemon permission responses fail" + "rejects malformed permission resolution outcomes" 两条回归)
  • npx eslint clean / npm run build exit 0
  • Dist-level smoke 重跑:SSE/HTTP race / 并发 prompt 拒绝 / unknown id 拒绝 / stop() 流程 / 错误通道 全部 PASS

LGTM。

@wenshao wenshao merged commit 11ba385 into main May 18, 2026
9 checks passed
@yiliang114 yiliang114 added the skip-changelog Exclude from release notes label May 20, 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