Skip to content

feat(web-shell): show subagent sessions in detail panel - #7380

Merged
ytahdn merged 14 commits into
QwenLM:mainfrom
chiga0:codex/web-shell-render-performance
Jul 22, 2026
Merged

feat(web-shell): show subagent sessions in detail panel#7380
ytahdn merged 14 commits into
QwenLM:mainfrom
chiga0:codex/web-shell-render-performance

Conversation

@ytahdn

@ytahdn ytahdn commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

What this PR does

This PR moves subagent transcript details out of the main conversation flow. Agent task cards keep their status and summary in the main transcript, while selecting a task opens a dedicated detail surface backed by a full transcript load followed by an independent SSE stream. Wide layouts use the side panel and narrow or split layouts use a drawer. Parallel agent entries use the same detail path, active subagents can be cancelled independently, and approval UI remains above the detail surface.

The server exposes workspace-scoped subagent resolution and cancellation routes, projects persisted agent transcripts as virtual sessions, bounds retained replay state, and avoids reading terminal stream sidecars. Main-turn metrics combine main-agent usage with root subagent execution summaries without retaining subagent content in the parent transcript.

Why it is needed

Long subagent transcripts currently make the main conversation difficult to scan and increase parent-session memory pressure. They also lack the full session rendering experience, including streamed thinking, tools, approvals, and reconnect behavior. Loading each subagent as its own session keeps the main transcript compact while preserving complete details on demand.

Reviewer Test Plan

How to verify

  1. Start an agent task and confirm the main transcript shows only the task summary and status, without the child transcript content.
  2. Open the task on a wide viewport and confirm the detail panel first loads the complete transcript and then continues streaming through its own SSE connection.
  3. Repeat on a narrow or split viewport and confirm the detail opens in a drawer rather than inline in the message flow.
  4. Start parallel agents and confirm each row opens its matching detail session.
  5. Cancel a running subagent from the detail header and confirm only that subagent stops while the parent session remains attached and active.
  6. Trigger a subagent permission request and confirm the approval surface remains visible above the detail drawer or panel.
  7. Reopen a completed subagent and confirm stale stream sidecar content is not replayed and task-list terminal status remains authoritative.

Evidence (Before & After)

Before: subagent content is embedded in the main transcript and parallel agent rows do not provide the full session detail renderer.

After: the main transcript retains only task summaries and usage, while complete subagent sessions load and stream in a dedicated panel or drawer.

Tested on

OS Status
🍏 macOS ✅ targeted server tests, SDK build/typecheck, Web Shell typecheck
🪟 Windows ⚠️ not tested
🐧 Linux ⚠️ not tested

Environment (optional)

Local workspace on Node.js 22. No Web Shell UI tests were run.

Risk & Scope

  • Main risk or tradeoff: each opened live subagent owns an additional SSE subscription and transcript projection; replay buffers and inactive targets are bounded and retained only temporarily.
  • Not validated / out of scope: paginated subagent history and browser-level UI automation.
  • Breaking changes / migration notes: none.

Linked Issues

N/A

Pictures

image
中文说明

本 PR 做了什么

本 PR 将子智能体的完整 transcript 从主会话消息流中移出。主消息流只保留任务状态和摘要,点击任务后通过全量 transcript load 加独立 SSE 的方式打开详情。宽屏使用侧边面板,窄屏和分屏使用抽屉。并行智能体复用同一详情链路,运行中的子智能体可以独立取消,权限审批界面保持在详情层之上。

服务端新增工作区范围内的子智能体解析和取消路由,将持久化的智能体 transcript 投影为虚拟 session,限制 replay 状态的长期保留,并且不再读取终态任务的 stream sidecar。主 turn 的指标会合并主智能体 usage 与根子智能体 execution summary,同时不会在父 transcript 中保留子智能体内容。

为什么需要

较长的子智能体 transcript 会让主会话难以阅读,并增加父会话的内存压力。同时,现有展示缺少完整 session 的流式 thinking、工具、审批和重连体验。将每个子智能体作为独立 session 按需加载,可以保持主会话精简,同时保留完整详情。

Reviewer 测试计划

如何验证

  1. 启动智能体任务,确认主消息流只展示任务摘要和状态,不包含子 transcript 内容。
  2. 在宽屏打开任务,确认详情面板先加载完整 transcript,随后通过独立 SSE 持续流式更新。
  3. 在窄屏或分屏重复操作,确认详情以抽屉打开,而不是在消息流原位置展开。
  4. 启动并行智能体,确认每一行都能打开对应的详情 session。
  5. 在详情头部取消运行中的子智能体,确认只停止该子智能体,父 session 仍保持连接和运行。
  6. 触发子智能体权限请求,确认审批界面显示在详情抽屉或面板之上。
  7. 重新打开已完成的子智能体,确认不会重放过期的 stream sidecar,并以任务列表中的终态为准。

前后对比证据

修改前:子智能体内容嵌入主 transcript,并行智能体行无法打开完整 session 详情。

修改后:主 transcript 仅保留任务摘要和 usage,完整子智能体 session 在独立面板或抽屉中加载并流式更新。

测试环境

macOS 已完成服务端目标单测、SDK build/typecheck 和 Web Shell typecheck。Windows 与 Linux 未测试。未运行 Web Shell UI 测试。

风险与范围

  • 主要风险或取舍:每个打开的运行中子智能体会增加一个 SSE 订阅和 transcript 投影;replay 缓冲区和非活跃 target 已做边界与临时保留控制。
  • 未验证或范围外:子智能体历史分页和浏览器级 UI 自动化。
  • 破坏性变更或迁移说明:无。

关联 Issue

无。

@qwen-code-ci-bot

qwen-code-ci-bot commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the PR!

Template looks good ✓

Problem: Observed UX problem — long subagent transcripts clutter the main conversation and increase parent-session memory pressure. The PR includes a screenshot showing the current state and the proposed detail panel. The problem is real and well-described.

Direction: Aligned. Subagent session management is an actively developed area — Claude Code's CHANGELOG references subagent status lines, background agent attach improvements, subagent spawn caps, and --forward-subagent-text. Moving subagent transcripts to a dedicated detail surface is a natural next step for the web shell.

Size: This PR touches core paths (packages/core/src/agents/**, packages/core/src/tools/agent/agent.ts, packages/core/src/services/chatRecordingService.ts). Breakdown:

  • Production logic: ~2,961 lines (additions + deletions, excluding test/docs/lock files)
  • Test files: ~1,738 lines
  • Design doc: ~613 lines
  • Core production lines: ~129 lines (small footprint — new fields, exports, runId propagation, and ALS frame management)

⚠️ Maintainer awareness: 2,961 production lines exceeds the 500-line threshold for core-touching feat PRs. This is not a block — feat PRs are not hard-blocked on size — but flagging for maintainer awareness. The 1,000+ large PR advisory also applies: consider whether this could be split (e.g., server routes + SDK types in one PR, web-shell UI in another) if feasible.

Approach: The scope is broad but coherent — a full-stack feature spanning core agent transcript helpers, CLI server routes (virtual subagent sessions, SSE), SDK types/normalizer, and web-shell UI (detail panel, drawer, parallel agents group). The design doc is a plus. The core changes are minimal (~129 lines), which is reassuring.

Since the last review, 8 new commits addressed review feedback with significant hardening: round-level stream dedup via runId, file identity tracking for transcript rotation, proper subscriber cleanup on error, stream sidecar lifecycle management, and an ALS frame fix that prevents model leakage (#7156). These are substantive improvements, not cosmetic.

Moving on to code review. 🔍

中文说明

感谢贡献!

模板完整 ✓

问题: 已观测到的 UX 问题——较长的子智能体 transcript 会让主会话难以阅读,并增加父会话的内存压力。PR 附带了截图展示当前状态和提议的详情面板。问题真实且描述清晰。

方向: 对齐。子智能体 session 管理是活跃开发领域——Claude Code 的 CHANGELOG 中提到了 subagent status line、后台智能体 attach 改进、子智能体生成上限和 --forward-subagent-text。将子智能体 transcript 移至专用详情面板是 web shell 的自然演进。

规模: 本 PR 触及核心路径(packages/core/src/agents/**packages/core/src/tools/agent/agent.tspackages/core/src/services/chatRecordingService.ts)。明细:

  • 生产逻辑:约 2,961 行(增删合计,不含测试/文档/lock 文件)
  • 测试文件:约 1,738 行
  • 设计文档:约 613 行
  • 核心生产行:约 129 行(footprint 较小——新字段、导出、runId 传播和 ALS 帧管理)

⚠️ 维护者关注: 2,961 生产行超过触及核心的 feat PR 的 500 行阈值。这不是阻塞——feat PR 不因规模被硬阻塞——但提请维护者关注。1,000+ 大 PR 建议也适用:如果可行,考虑是否可以拆分。

方案: 范围较广但内聚——是一个全栈功能。设计文档是加分项。核心改动很小(约 129 行),令人放心。

自上次审查以来,8 个新提交解决了审查反馈并做了显著加固:通过 runId 实现轮次级流去重、文件身份跟踪以检测 transcript 轮换、错误时正确清理订阅者、流 sidecar 生命周期管理,以及防止模型泄漏的 ALS 帧修复(#7156)。这些是实质性改进。

进入代码审查 🔍

Qwen Code · qwen3.7-max

Reviewed at 1ca270c581723e91acb041943b1cf1b0dd675317 · re-run with @qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

🖼️ web-shell visual preview

Rendered against a mock daemon (no real backend): the PR base vs this PR head d63571f. Only screenshots that changed are shown (flows below, if any, are head-only) — refreshes on every push.

Screenshots · before / after

ℹ️ No screenshot changed against the PR base — but this PR edits 20 render-shaping files:

  • packages/web-shell/client/App.module.css
  • packages/web-shell/client/App.tsx
  • packages/web-shell/client/components/ChatPane.tsx
  • packages/web-shell/client/components/MessageList.tsx
  • packages/web-shell/client/components/SplitView.tsx
  • packages/web-shell/client/components/StatusBar.tsx
  • packages/web-shell/client/components/WorkspaceSessionProvider.tsx
  • packages/web-shell/client/components/artifacts/ArtifactPanel.module.css
  • …and 12 more.

Either the change has no visual effect (logic, plumbing, a state the scenarios never reach), or no scenario renders this UI — in which case the preview cannot see it, and an empty result is a coverage gap rather than a clean bill of health. To make it visible, add a scenario to packages/web-shell/client/e2e/visuals/screenshots.spec.ts that seeds whatever state the UI is gated on; it then appears here as a head-only (NEW) capture.

Full-resolution recordings (.webm) are attached to the workflow run.

Qwen Code · web-shell visuals

@qwen-code-ci-bot

qwen-code-ci-bot commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

🩺 serve daemon A/B

Built the PR base vs this PR head d63571f, drove a fixed endpoint set against each, and diffed the JSON responses. Only fields that changed are shown.

No response changes against the PR base across 4 scenario(s).

Qwen Code · serve A/B

@qwen-code-ci-bot

qwen-code-ci-bot commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Code Review

Independent proposal: For "show subagent sessions in detail panel", I would: (1) add toolUseId and runId to agent events for precise linking and round-level dedup, (2) add server routes to resolve/load/stream subagent transcripts as virtual sessions with file-identity-aware state management, (3) add SDK client methods and a "summary" transcript mode that strips subagent content from the main flow, (4) add a detail panel component with its own DaemonSessionProvider and a drawer for narrow layouts, (5) ensure notification callbacks run outside the agent ALS frame to prevent model leakage.

Comparison: The PR's approach matches and exceeds this proposal. The architecture is sound — virtual session IDs encode parent + agent in base64url with input validation, the server polls the JSONL transcript at 250ms intervals with file-identity (dev:ino) tracking for rotation detection, retention is bounded at 60s after the last subscriber disconnects, and the SDK's retainSubagentBlocks flag cleanly separates "full" (detail panel) from "summary" (main transcript) modes.

Findings — no critical blockers.

The incremental changes since the last review are substantive:

  1. Round-level stream dedup (runId + agentRound): replaces the simpler canonicalRounds counter with precise per-round reconciliation. The completedStreamRounds / streamedRounds sets track exactly which rounds have been streamed vs. canonicalized, avoiding both duplicates and data loss during the stream-to-canonical handoff. The streamRunIds set prunes stale completed rounds when a run ID disappears from the stream file.

  2. File identity tracking (dev:ino): detects transcript file replacement (e.g., session restart) and resets canonical/stream state cleanly. The resetCanonicalState() / resetStreamState() helpers ensure no stale offsets survive a rotation.

  3. Stream sidecar lifecycle: opened with 'w' (truncate) instead of 'a' (append), removed on writer close, and stale sidecars are cleaned up when a new writer starts. This prevents replaying stale stream content from a previous run.

  4. ALS frame fix (runOutsideAgentContext): the notification callback now runs outside the agent's AsyncLocalStorage frame via storage.exit(). This is a genuine bug fix — without it, the background agent's model config leaks into the main session's notification drain turn (Bug: Subagent mutates main session model — context overflow recurrence after #7119 #7156). Well-commented with the specific failure mode.

  5. Subscriber cleanup on error: subscribe() now wraps setup in try/finally so the subscriber count decrements even if the initial refresh throws. The enqueueRefresh() helper properly chains async work without swallowing errors.

  6. Route hardening: virtual session routes now return proper 400 (unsupported action) and 404 (missing session/runtime) responses instead of silently falling through. Heartbeat and detach validate the parent session runtime exists.

  7. Usage-only ROUND_TEXT: isWhitespaceOnlyAssistant now treats empty-parts assistant records as whitespace-only, and onRoundText no longer requires usageMetadata to emit. This keeps the canonical transcript valid when a round produces only usage metadata.

  8. Execution summary monotonic accumulation: upsertToolBlock in the SDK transcript reducer now preserves higher token counts across updates, preventing a later partial update from reducing the displayed totals.

The core changes (~129 production lines) remain minimal and backward-compatible: runId on events, agentRunId/agentRound on canonical records, runOutsideAgentContext helper, and the isWhitespaceOnlyAssistant fix. All new fields are optional.

Real-Scenario Testing

Server started successfully with the PR branch. The new subagent routes are registered and respond correctly:

$ npm run dev -- serve --port 18924

> @qwen-code/qwen-code@0.20.0 dev
> node scripts/dev.js serve --port 18924

qwen serve: daemon log → /home/github-runner/actions-runner-7/_work/_temp/qwen-home/debug/daemon/daemon.log
qwen serve: Web Shell UI served from /home/github-runner/actions-runner-7/_work/qwen-code/qwen-code/.qwen/worktrees/triage/packages/web-shell/dist
qwen serve listening on http://127.0.0.1:18924 (mode=http-bridge, workspace=/home/github-runner/actions-runner-7/_work/qwen-code/qwen-code/.qwen/worktrees/triage)
qwen serve: bound to workspace "/home/github-runner/actions-runner-7/_work/qwen-code/qwen-code/.qwen/worktrees/triage"
qwen serve: startup timing: processToListenMs=3061 runQwenServeToListenMs=2204
qwen serve: bearer auth disabled (loopback default). Set QWEN_SERVER_TOKEN to enable.
qwen serve: session reaper started (interval 60000ms, idle threshold 1800000ms)
qwen serve: /acp WebSocket transport enabled on /acp

Route verification (no active session, so 404s are expected — confirms routing works):

$ curl -s http://127.0.0.1:18924/session/test-session/subagents/test-tool-call
{"error":"No session with id \"test-session\"","sessionId":"test-session"}

$ curl -s -X POST http://127.0.0.1:18924/session/test-session/subagents/test-tool-call/cancel
{"error":"No session with id \"test-session\"","sessionId":"test-session"}

$ curl -s -X POST http://127.0.0.1:18924/session/subagent.dGVzdA.dGVzdA/load -H 'Content-Type: application/json' -d '{}'
{"error":"No session with id \"test\"","sessionId":"test"}

$ curl -s -N --max-time 3 http://127.0.0.1:18924/session/subagent.dGVzdA.dGVzdA/events
{"error":"No session with id \"test\"","sessionId":"test"}

$ curl -s -X POST http://127.0.0.1:18924/session/subagent.dGVzdA.dGVzdA/heartbeat -H 'X-Client-Id: test-client'
{"sessionId":"subagent.dGVzdA.dGVzdA","lastSeenAt":1784680156303}

The virtual session ID subagent.dGVzdA.dGVzdA correctly decodes to parent session "test" and agent "test", confirming the base64url encoding/decoding works. The heartbeat route validates the parent runtime and returns 200.

Server logs confirm proper route matching and status codes:

[WARN] [DAEMON] route=GET /session/test-session/subagents/test-tool-call sessionId=test-session durationMs=6 status=404
[WARN] [DAEMON] route=POST /session/test-session/subagents/test-tool-call/cancel sessionId=test-session durationMs=2 status=404
[WARN] [DAEMON] route=POST /session/subagent.dGVzdA.dGVzdA/load sessionId=subagent.dGVzdA.dGVzdA durationMs=6 status=404
[WARN] [DAEMON] route=GET /session/subagent.dGVzdA.dGVzdA/events sessionId=subagent.dGVzdA.dGVzdA durationMs=3 status=404

Unit tests all pass (1,613 tests across touched packages):

  • packages/core/src/agents/agent-transcript.test.ts — 30 tests ✓
  • packages/core/src/agents/background-agent-resume.test.ts — 37 tests ✓
  • packages/core/src/agents/background-tasks.test.ts — 112 tests ✓
  • packages/cli/src/serve/virtual-subagent-sessions.test.ts — 8 tests ✓
  • packages/cli/src/serve/server.test.ts — 769 tests ✓
  • packages/cli/src/serve/server/telemetry.test.ts + telemetry-catalog.test.ts — 51 tests ✓
  • packages/sdk-typescript/test/unit/DaemonClient.test.ts — 283 tests ✓
  • packages/sdk-typescript/test/unit/daemonUi.test.ts — 274 tests ✓
  • packages/webui/src/daemon/session/DaemonSessionProvider.subagent.test.ts — 2 tests ✓
  • packages/web-shell/client/components/artifacts/SubagentDetail.test.tsx — 2 tests ✓
  • packages/web-shell/client/components/messages/ToolGroup.test.tsx — 33 tests ✓
  • packages/web-shell/client/components/messages/tools/ParallelAgentsGroup.test.tsx — 11 tests ✓
  • packages/web-shell/client/utils/composerTasks.test.ts — 1 test ✓

Note: full UI testing (opening the detail panel in a browser, verifying SSE streaming, drawer behavior) was not performed — this requires a browser environment. The server-side routes, SDK client, and transcript projection logic are verified through unit tests and route smoke tests.

中文说明

代码审查

独立方案: 对于"在详情面板中展示子智能体会话",我会:(1) 在 agent 事件中添加 toolUseIdrunId 以精确关联和轮次级去重,(2) 添加服务端路由将子智能体 transcript 作为虚拟 session 解析/加载/流式传输,带文件身份感知的状态管理,(3) 添加 SDK 客户端方法和"摘要" transcript 模式,(4) 添加详情面板组件和窄屏抽屉,(5) 确保通知回调在 agent ALS 帧外运行以防止模型泄漏。

对比: PR 方案与上述提案一致且有所超越。架构合理——虚拟 session ID 用 base64url 编码并做输入校验,服务端以 250ms 间隔轮询 JSONL transcript 并跟踪文件身份(dev:ino)以检测轮换,最后一个订阅者断开后 60s 清理保留状态,SDK 的 retainSubagentBlocks 标志干净地分离了"完整"和"摘要"模式。

发现——无关键阻塞项。

自上次审查以来的增量改动是实质性的:轮次级流去重、文件身份跟踪、流 sidecar 生命周期管理、ALS 帧修复(#7156)、错误时订阅者清理、路由加固、usage-only ROUND_TEXT 处理、以及执行摘要单调累积。

核心改动(约 129 生产行)保持最小且向后兼容。

真实场景测试

服务端成功启动。子智能体路由正确注册和响应。虚拟 session ID 编解码正确。所有 1,613 个单元测试通过。

Qwen Code · qwen3.7-max

Reviewed at 1ca270c581723e91acb041943b1cf1b0dd675317 · re-run with @qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Confidence: 3/5 — clean review with substantive improvements since the last pass, but the Stage 0 maintainer-awareness escalation (feat PR with ~2,961 production lines touching core) needs a maintainer's sign-off.

This PR has improved markedly since the last review. The eight follow-up commits aren't cosmetic — they address real failure modes: transcript file rotation (file-identity tracking), stale stream replay (sidecar lifecycle), round-level dedup precision (runId), subscriber leaks on error (try/finally in subscribe), and a genuine ALS model-leakage bug (#7156). The round-level reconciliation via completedStreamRounds / streamedRounds sets is a cleaner design than the previous canonicalRounds counter, and the enqueueRefresh helper properly serializes async work without swallowing errors.

The architecture remains sound: virtual session IDs with input validation, bounded retention, polling-based transcript refresh with file-identity awareness, and a clear separation between summary and full transcript modes. The core footprint is still small (~129 lines), and the backward-compatible legacy task resolution via fuzzy matching is pragmatic.

All 1,613 unit tests pass. Server routes respond correctly. The build succeeds.

What keeps this at 3/5: the sheer scope (66 files, ~5,300 additions) means a human maintainer should verify the architectural decisions — particularly the round-level stream reconciliation weights, the runOutsideAgentContext ALS fix (correct but subtle), and the retainSubagentBlocks flag's interaction with the existing transcript reducer. These are all reasonable choices, but they benefit from a second pair of eyes with deeper context on the agent transcript system.

No blocking issues found. Deferring to maintainer for the final call.

中文说明

置信度:3/5 — 审查干净,自上次审查以来有实质性改进,但 Stage 0 维护者关注升级(触及核心的 feat PR,约 2,961 生产行)需要维护者签字。

自上次审查以来,PR 有显著改进。八个后续提交不是表面修改——它们解决了真实的故障模式:transcript 文件轮换(文件身份跟踪)、过期流重放(sidecar 生命周期)、轮次级去重精度(runId)、错误时订阅者泄漏(subscribe 中的 try/finally),以及一个真实的 ALS 模型泄漏 bug(#7156)。

架构保持合理。核心 footprint 仍然很小(约 129 行)。所有 1,613 个单元测试通过。服务端路由正确响应。构建成功。

保持在 3/5 的原因:范围很大(66 个文件,约 5,300 行新增),人类维护者应验证架构决策。未发现阻塞问题。转交维护者做最终决定。

Qwen Code · qwen3.7-max

Reviewed at 1ca270c581723e91acb041943b1cf1b0dd675317 · re-run with @qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

⏸️ Deferring to @tanzhenxin @wenshao — this feat PR touches core agent transcript paths (packages/core/src/agents/, packages/core/src/tools/agent/agent.ts, packages/core/src/services/chatRecordingService.ts) with ~2,961 production logic lines across 66 files. The review is clean (no blockers, all 1,613 tests pass, server routes verified), but the Stage 0 core-module policy requires maintainer awareness for feat PRs of this scale.

Since the last review, 8 new commits substantially hardened the streaming state: round-level dedup via runId, file-identity tracking for transcript rotation, stream sidecar lifecycle management, subscriber cleanup on error, and an ALS frame fix preventing model leakage (#7156). Needs a human call on the architectural decisions: round-level stream reconciliation, runOutsideAgentContext ALS fix, and the retainSubagentBlocks transcript mode.

@ytahdn
ytahdn requested a review from yiliang114 as a code owner July 21, 2026 03:01
@wenshao

wenshao commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Review — subagent sessions in detail panel

Verified at head adf4736 on Linux: full diff read, targeted suites run locally (cli 9 ✅, core 66 ✅, sdk 555 ✅, webui 1 ✅, web-shell 42 ✅), tsc + ESLint clean after rebuilding sdk/webui (the reported "typecheck caveat" is just stale workspace builds), and the red CI reproduced locally.

Verdict: the architecture is right and most of the code is careful, but CI is legitimately red — all 4 failing tests are caused by this PR and reproduce locally. Details below.

🔴 Blocking — CI failures are real (reproduced at adf4736)

  1. Route drift guardpackages/cli/src/serve/server/telemetry-catalog.test.ts fails with expected … length 48 but got 50. The two new Express routes (GET /session/:id/subagents/:toolCallId, POST …/cancel) were never added to legacySessionTelemetryRoutes (packages/cli/src/serve/server/telemetry.ts:31), so they also have no telemetry classification — which is exactly what this guard exists to catch.
  2. Library-bundle boundaryweb-shell build-artifact.test.ts › does not inline createContext from React for provider contexts fails: the built dist/index.js now contains 5 createContext( calls vs the cap of 3. The new subagentDetailsContext.tsx adds one and the bundled-in vaul dependency adds more. For the embeddable artifact this is a context-identity hazard, not just a lint: decide whether vaul should be externalized like react/webui, or consciously raise the cap with a comment.
  3. Split-view right panel moved into the drawer breaks existing behavior testsApp.test.tsx › reconciles/clears split pane artifact snapshots… (2 failures): useFloatingArtifactPanel = !canDockArtifactPanel || mainView === 'split' routes every right-panel tab kind (artifacts, review, scheduled tasks — not just the new subagent tab) into the vaul drawer portal whenever split view is active. The existing tests (and any embedder looking for the docked panel) no longer find panel content. Either update the tests to assert into the portal root and confirm the UX change for artifact/review tabs in split view is intended, or scope the drawer to subagent tabs.

🟠 Important

  1. .stream sidecars are written for every agent run and never deleted. attachJsonlTranscriptWriter appends one JSON envelope per streamed delta chunk (agent-transcript.ts — for typical few-character deltas the envelope is several× the payload), for all runs including plain CLI usage that will never open a Web Shell panel, and I found no unlink/TTL anywhere — the subagents dir now grows without bound, roughly doubling (or worse) per-agent transcript disk cost. Suggest deleting the sidecar in the writer cleanup once the task is terminal (the meta status is known there), or a sweep keyed on meta.status.
  2. Completion race can permanently truncate an open detail stream. VirtualSubagentTarget.updateStatus() clears the 250 ms poll timer the instant any client's 3 s resolve() poll observes a terminal status — without a final refreshOnce(). Records flushed between the last poll tick and that moment are never published to the bus, so an open panel silently misses the agent's final output until it is closed and reopened. One last refreshLive() on the running→terminal transition closes the window.

🟡 Minor

  1. Duplicate-delivery window on a second concurrent loadcreateSnapshotOnce()'s throwaway-target path snapshots disk-now but returns lastEventId of the real bus, which can lag disk by up to one poll tick; events appended in between reach that client twice (snapshot + later publish). The design doc in this very PR requires snapshot+live merge "without a race, duplicate, or gap".
  2. Live-view text drops from the stream dedup heuristic — if two rounds' canonical records arrive in one readNewRecords() batch while streamedSinceCanonical is set, the later round's (never-streamed) text is filtered out, and its own stream records are then skipped because canonicalRounds already advanced past them. Similarly, after a background resume the round counter restarts at 1 ≤ canonicalRounds, so resumed runs never live-stream. All of these self-heal on reopen; noting because they contradict the "no gap" goal.
  3. Public SDK behavior change — the daemon/ui reducer no longer folds subagent usage into the top-level assistant block (applyAssistantUsage). Web Shell and the terminal renderer were updated, but any external @qwen-code/sdk consumer summing assistant-block usage now silently under-counts subagent-heavy turns. Deserves a changelog/release note.
  4. PR description vs code — there are no /workspaces/:workspace/session/:id/subagents/… route variants; scoping is done by requireSessionRuntime live-owner resolution on the legacy path (untrusted workspaces correctly refused — good). That works because the parent session must be open, but the description overstates the surface. Related nits: the virtual heartbeat middleware 200-acks any well-formed virtual id without existence check, and the virtual load response has no size cap (sibling workspace transcript route caps at 32 MiB).
  5. Design doc vs implementation — the committed doc mandates server-side main projection ("Fetching full history and discarding child events in React is not an acceptable lazy-loading design") and labels itself "does not authorize implementation", yet this PR ships client-side projection in DaemonSessionProvider (memory bounded; wire/decode cost not). Fine as a phase-1 tradeoff, but the doc's Status/phase notes should say what actually shipped.
  6. Legacy fallback matching is best-effort — the prompt/description/type/time score in findLegacyTaskByToolCall cannot disambiguate identical parallel legacy launches (ties break on timestamp delta), and candidate.id.endsWith('-' + toolCallId) can theoretically alias. Worth a code comment so nobody later treats it as exact.

Nits

  • subagent.detailsEmpty i18n key added (EN+ZH) but never used.
  • sdk-typescript/scripts/build.js: budget bumped 165→166 KiB but the comment above still explains the old 161→165 bump.
  • App.tsx openSubagentPanelForSession and ChatPane.openSubagentDetails duplicate the tab/subagent-type extraction; a shared helper would keep the two paths from drifting.

What held up well

  • Trust boundary: virtual routes resolve the parent session's runtime and refuse untrusted/ambiguous workspaces; virtual ids are base64url-encoded and re-validated with a strict charset, so no path traversal into chats/.
  • Terminal-task stream sidecars are correctly not replayed (tested), targets are workspace-keyed (cross-workspace isolation tested), retention/poll timers are unref'd and bounded.
  • Back-compat defaults are right: subagentTranscriptMode defaults to 'full', retainSubagentBlocks defaults to true, store reset() preserves the flag (tested).
  • The normalizer title fix (later tool_call_update without a title no longer clobbers the initial title) and the resume-history fix (tool-call/response pairs preserved, usage-only records dropped) are solid standalone improvements with non-vacuous tests; thought parts flowing into resume history are safely handled by the existing converter (thought: true → reasoning channel).

Happy to re-verify once the CI items are addressed.

中文说明

在 head adf4736(Linux)完成验证:通读全部 diff,本地运行了定向测试(cli 9 ✅ / core 66 ✅ / sdk 555 ✅ / webui 1 ✅ / web-shell 42 ✅),重建 sdk/webuitsc + ESLint 干净(所谓 typecheck 问题只是 workspace 构建产物过期),并且把红色 CI 在本地完整复现了。

结论:架构方向正确、大部分实现很细致,但 CI 红是真实的——4 个失败测试全部由本 PR 引起,且本地可复现。

🔴 阻塞项:

  1. 路由漂移守卫:新增的两个 subagent 路由没有登记到 legacySessionTelemetryRoutestelemetry.ts:31),telemetry-catalog.test.ts 期望 48 实得 50——新路由也因此缺少遥测归类。
  2. 库产物边界dist/index.jscreateContext( 从 ≤3 涨到 5(新增的 subagentDetailsContext + 被打包进来的 vaul)。需要决定 externalize vaul 还是有意上调阈值并注释。
  3. 分屏下右侧面板整体改为抽屉useFloatingArtifactPanel 在 split 模式把所有 tab 种类(artifact/review/定时任务,不只是 subagent)都移入 vaul 抽屉 portal,App.test.tsx 两个既有用例失败。要么更新测试断言到 portal 并确认该 UX 变更是有意的,要么把抽屉限定在 subagent tab。

🟠 重要:
4. .stream sidecar 只写不删:每个流式 delta 一条 JSON(信封开销常为正文数倍),纯 CLI 场景也会写,且全仓没有任何清理逻辑,子智能体目录磁盘占用无界增长。建议在 writer cleanup 于任务终态时删除,或按 meta.status 定期清理。
5. 完成瞬间的截断竞态:客户端 3 秒 resolve() 轮询一旦看到终态,updateStatus() 立即清掉 250ms poll timer,但没有做最后一次 refreshOnce()——最后一个 poll tick 之后落盘的记录永远不会发布到 bus,已打开的详情面板会缺失结尾输出(重开才恢复)。在 running→终态转换时补一次 refreshLive() 即可。

🟡 次要:二次并发 load 的快照/lastEventId 重复窗口(设计文档明确要求"无竞态、无重复、无缺口");streamedSinceCanonical 批量过滤可能丢掉同批次后一轮未流式过的文本、resume 后 round 计数重置导致不再流式;SDK reducer 用量归属变更对外部消费者是静默行为变化(需要 changelog);PR 描述中的"workspace-scoped 路由"实际不存在独立路径(靠 requireSessionRuntime 解析,信任边界本身正确);设计文档要求服务端投影、实现是客户端投影,文档状态应与实际交付对齐;legacy 匹配启发式无法区分完全相同的并行启动,建议加注释。

Nits:subagent.detailsEmpty i18n key 未使用;sdk build.js 预算注释过期;App/ChatPane 两处 tab 构造逻辑重复。

做得好的地方:虚拟路由复用父会话的信任边界解析并拒绝不受信 workspace,virtual id 严格校验无路径穿越;终态任务不回放 sidecar(有测试);target 按 workspace 隔离(有测试);默认值保持向后兼容;normalizer 标题保留修复与 resume 历史修复(保留工具调用配对、丢弃 usage-only 记录)都是扎实的独立改进。

CI 项修复后我可以再验一轮。

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed. Suggestions are inline. 1 Suggestion-level finding(s) could not be anchored to a changed line and were dropped; nothing further to act on here. Unresolved, please confirm: [Critical] Blocker #1 (Route drift guard): new subagent routes not in legacySessionTelemetryRoutes — still stands (verified: no 'subagent' matches in telemetry.ts) [Critical] Blocker #2 (Library-bundle boundary): createContext cap exceeded by new subagentDetailsContext + vaul — still stands (verified: subagentDetailsContext.tsx adds createContext) [Critical] Blocker #3 (Split-view drawer): useFloatingArtifactPanel routes ALL panel tabs to drawer in split view — still stands (verified: !canDockArtifactPanel || mainView === 'split') Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and did not run locally. Not reviewed: reverse audit — an auditor ran and opened its brief, but no agent was launched with the prompt the CLI built — the launch was written by hand, and what the agent was actually asked is not what this skill certifies.

— qwen3.7-max via Qwen Code /review

Comment thread packages/cli/src/serve/routes/session.ts Outdated
Comment thread packages/web-shell/client/components/messages/ToolGroup.tsx Outdated
Comment thread packages/cli/src/serve/routes/session.ts
Comment thread packages/cli/src/serve/virtual-subagent-sessions.ts
Comment thread packages/cli/src/serve/virtual-subagent-sessions.ts
Comment thread packages/web-shell/client/components/messages/tools/ParallelAgentsGroup.tsx Outdated
@wenshao
wenshao requested a review from Copilot July 21, 2026 05:24

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@ytahdn

ytahdn commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator Author

@qwen-code /takeover

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

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Not reviewed: reverse audit — an auditor ran and opened its brief, but no agent was launched with the prompt the CLI built — the launch was written by hand, and what the agent was actually asked is not what this skill certifies.

— qwen3.7-max via Qwen Code /review

Comment thread packages/cli/src/serve/virtual-subagent-sessions.ts Outdated
Comment thread packages/cli/src/serve/virtual-subagent-sessions.ts
Comment thread packages/cli/src/serve/virtual-subagent-sessions.ts
Comment thread packages/cli/src/serve/virtual-subagent-sessions.ts
Comment thread packages/webui/src/daemon/session/DaemonSessionProvider.tsx Outdated
Comment thread packages/webui/src/daemon/session/DaemonSessionProvider.tsx Outdated
Comment thread packages/web-shell/client/components/artifacts/SubagentDetail.tsx
Comment thread packages/web-shell/client/components/artifacts/SubagentDetail.tsx Outdated
Comment thread packages/web-shell/client/utils/composerTasks.ts
Comment thread packages/web-shell/client/components/MessageList.tsx
@ytahdn ytahdn removed the autofix/takeover Summon the autofix loop to manage this PR (remove to release; needs triage+) label Jul 21, 2026
@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Qwen Code review timed out. Qwen review timed out after 300 seconds (of the 180-minute budget). For large PRs, retry with a longer timeout by commenting: @qwen-code /review --timeout=240. See workflow logs.

chiga0
chiga0 previously approved these changes Jul 21, 2026

@chiga0 chiga0 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 Overview (AI Generated)

PR: #7380 — feat(web-shell): show subagent sessions in detail panel
Type: New Feature
Change size: +4766/-163 across 66 files
HEAD: a8de487e

Findings Summary

  • Critical: 0
  • Major: 1 (performance, not correctness)
  • Minor: 2
  • Nit: 2

Cross-Validation

Finding Other Reviewer My Assessment
Critical: Route drift guard (subagent routes not in telemetry) qwen-code-ci-bot Resolved — both routes now in telemetry.ts, test counts updated to 50

Review

Comprehensive feature that moves subagent transcripts into dedicated detail sessions. Backend architecture is sound:

Auth/authz: Bearer token auth at app level, requireSessionRuntime for workspace isolation, virtual session ID parsing enforces prefix/length/regex validation.

Virtual session projection: Proper boundary checking via parseVirtualSubagentSessionId (prefix, length cap, base64url regex). Virtual contexts return minimal stubs without disk I/O.

Stream sidecar lifecycle: Correctly skips .stream reads for completed subagents (task.status !== 'running'). Terminal transition triggers final refresh, poll timer stopped, writer cleanup removes sidecar.

Metrics merging: Clear precedence — parent transcript execution summary overrides live bridge metrics for terminal sessions.

Major Finding

Cancel route performs unnecessary full resolution (routes/session.ts): The cancel route calls virtualSubagentSessions.resolve() which may load the entire parent transcript JSONL for legacy matching or metric extraction, just to obtain taskId. A dedicated resolveTaskId method that skips metric extraction would be more efficient. Not a correctness issue but noticeable for large parent transcripts.

Minor Findings

  1. readParentToolCallMetrics loads entire parent JSONL: Could scan-and-stop after finding the matching toolCallResult record instead of reading the full file.
  2. Replay state retention rationale undocumented: Bounded by 60s TARGET_RETENTION_MS eviction, but a comment would help future readers.

Final Verdict

APPROVE. No correctness issues. Auth, workspace isolation, stream lifecycle, and metrics merging are all sound. The Major finding is a performance optimization suitable for follow-up.


This review was generated by QoderWork AI

@wenshao

wenshao commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Code review — feat(web-shell): show subagent sessions in detail panel

Reviewed the full diff (66 files, +4766/-163). This is a well-structured, well-tested change: the design doc is thorough, the daemon route additions come with a telemetry drift-guard update (48→50), and the reconciliation between the canonical .jsonl and the .stream sidecar is covered by real-filesystem tests. Overview and then findings below.

What it does

  • Core: the agent transcript writer now emits a transient agent-<id>.jsonl.stream sidecar of live text (buffered to 64 KiB / 100 ms, reset on writer start, removed on close), stamps agentRunId/agentRound on round records, and records toolUseId in the agent meta so a subagent can be linked back to its launching tool call.
  • Daemon: new VirtualSubagentSessions projects a persisted subagent transcript as a read-only virtual session (subagent.<b64url(parent)>.<b64url(agent)>), with GET /session/:id/subagents/:toolCallId (resolve), POST …/cancel, plus virtual-session handling on /load, /events (SSE), /context, /supported-commands, /heartbeat, /detach.
  • SDK reducer: retainSubagentBlocks (summary mode) drops child blocks, folds child usage into the parent tool's executionSummary, and compacts task_execution output.
  • Web-shell: agent rows become buttons that open a right-panel/vaul drawer SubagentDetail, which resolves the virtual session and mounts a nested DaemonSessionProvider (full mode) that loads the whole transcript then streams over its own SSE.

I confirmed one thing that looked risky and is actually fine: projectMainTranscriptEvents drops every parentToolCallId event except assistant.usage, but DaemonUiPermissionRequestEvent has no parentToolCallId field, so child-tool approvals still produce a permission block and extractPendingPermission still surfaces them with the detail panel closed. 👍


Findings

1. Possible per-turn token double-count in full mode (please verify).
In summary mode the reducer routes sub-agent usage to the parent tool's executionSummary instead of folding it into the top-level assistant block, and MessageList.itemSubagentUsages then adds it back once — correct. But itemSubagentUsages runs unconditionally in applyTurnCollapse, while in full mode (retainSubagentBlocks: true, the DaemonSessionProvider default) applyAssistantUsage still folds sub-agent usage into the assistant block. So a full-mode MessageList render of a turn that contains a completed agent/task tool with an executionSummary would count those tokens twice (once folded, once from itemSubagentUsages). The main web-shell transcript is summary mode so it's safe; the exposure is the SubagentDetail panel (full mode) when the opened subagent itself spawned nested agents, and any other full-mode MessageList consumer. Worth a targeted test.

2. File replacement / rewind doesn't reset dedup + replay state.
readNewRecords/readStreamUpdates reset offset/streamOffset to 0 when stat.size < offset (truncation/rewrite), but replayState, canonicalThroughTimestamp, completedStreamRounds, and streamedRounds are not reset. After a branch/rewind that rewrites the transcript with older timestamps, record.timestamp <= canonicalThroughTimestamp can silently suppress legitimately-new records, and the stale replayState cursor is fed into replayTranscriptRecordPage for a file it no longer describes. The "replacement canonical" test passes only because the replacement record uses a fresh (larger) timestamp. Consider resetting the reconciliation state whenever a size-shrink/identity change is detected.

3. normalizeToolUpdate title can now be lost for a lone tool_call_update.
The new title = explicitTitle ?? (sessionUpdate === 'tool_call' ? toolName ?? toolKind : undefined) fixes the "update frame overwrites the good title" case, but a tool whose first observed frame is a tool_call_update (e.g. reconnect/replay landing mid-tool) now yields title: undefined and never recovers, where it previously showed toolName/toolKind. terminal.ts was updated to fall back, but block renderers that read title directly will show the generic fallback. Minor, but a reconnect-mid-tool test would be reassuring.

4. Test-stability risk in virtual-subagent-sessions.test.ts.
Several cases depend on wall-clock timing against the 250 ms poll and the 64 KiB flush — setTimeout(…, 300/400/600) sleeps, Promise.race timeouts, and bounded for loops over iterator.next(). This repo already has a history of flaky serve tests under CI load; these real-timer + real-FS tests are prime candidates. Consider fake timers or polling on a condition instead of fixed sleeps where feasible.

5. birthtimeMs in the stream identity is not portable.
readStreamUpdates keys sidecar identity on ${dev}:${ino}:${birthtimeMs}. birthtimeMs is unreliable on several Linux filesystems/kernels (often falls back to ctime or 0). If it flaps, the identity check falsely triggers an offset reset and re-replays the stream. The PR is marked untested on Linux/Windows — dev:ino alone is probably the safer identity.

6. Encode/parse charset mismatch for the virtual session id.
createVirtualSubagentSessionId base64url-encodes arbitrary parent/agent ids, but parseVirtualSubagentSessionId rejects any decoded value that isn't ^[a-zA-Z0-9_-]{1,500}$. Ids outside that charset round-trip to an un-parseable session id. Session/agent ids are UUID-ish today so it's latent, but either validate at encode time or widen the decode charset so the two stay in agreement.

7. heartbeat/detach virtual stubs skip runtime/ownership resolution.
Unlike the /context and /supported-commands virtual branches, the /heartbeat and /detach stubs return 200/204 after only a format check on the id — no requireSessionRuntime on the parent. It's behind mutate()/auth and leaks nothing (just echoes the id + a timestamp), so low risk, but it's inconsistent with the sibling routes.

8. Minor perf.
readParentToolCallMetrics and findLegacyTaskByToolCall read the entire parent chats/<id>.jsonl on each resolve; and a running target keeps polling the files every 250 ms even after the agent completes, until subscribers drop (status only flips via the resolve-driven updateStatus). Both are bounded, but noticeable on very large sessions.

Nits

  • New files SubagentDetail.tsx, subagentDetailsContext.tsx, drawer.tsx, composerTasks.ts don't carry the @license header the core/cli new files do — worth confirming against the lint config.
  • Drawer DrawerTitle "Right panel" is hardcoded English (sr-only); mirrors the existing aria-label, but could use t(...).
  • New runtime dep vaul (pulls @radix-ui/react-dialog) — externalized in vite.lib.config, so bundle impact is fine; just calling out the added dependency.

Overall the shape is solid and the boundary (summary main transcript + on-demand full detail) is the right one. #1 and #2 are the two I'd want resolved (or explicitly tested) before merge; the rest are hardening.

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤖 Reviewed the latest feedback — no changes needed. Why, point by point:

No action required — all feedback verified as addressed

Every feedback point from this review round was already resolved in prior commits on this branch (28e5ef6e3, 1d49e2784, 57870cba5, 7dec83a17). No new code changes were made. Below is the full triage.

Critical / CHANGES_REQUESTED items (verified fixed)

Finding Status Evidence
Route drift guard — subagent routes missing from legacySessionTelemetryRoutes Fixed telemetry.ts now registers both routes; telemetry-catalog.test.ts passes (1 test)
Library-bundle boundary — createContext cap exceeded Fixed vaul externalized in vite.lib.config.ts; build-artifact.test.ts passes (8 tests, cap ≤ 3 holds)
Split-view drawer — all panel tabs routed to drawer Fixed App.test.tsx assertions updated to document.body.textContent; 150 tests pass
Subscriber leak in iterate() on non-ENOENT refresh failure Fixed in 57870cba5 Subscriber lifecycle inside try/finally; EISDIR regression test passes
Silent request hang when virtualSubagentSessions undefined Fixed in 57870cba5 Both routes return structured 404

Suggestions (all addressed or recorded)

All 27 inline suggestions (rc:3619497311 through rc:3622012634) have author responses:

  • 21 fixed in commits 28e5ef6e3 / 1d49e2784 / 57870cba5 / 7dec83a17 (error messages, keyboard a11y, route tests, dedup init flag, ENOENT reset, file-shrink guard, boundedString dedup, dead property removal, empty-object guard, terminateReason projection, aria-expanded, status union type, SubagentDetailsProvider fallback, findSubagentRootTool coverage, composerTasks tests, MessageList DOM tests, dead CSS, totalTokens removal, projected output guard)
  • 4 declined with evidence: TOCTOU race (JS run-to-completion prevents it, ×2), poll-timer test (covered by existing terminal-transition regression), vi.mock removal (test never had it)
  • 2 intentionally kept: retry polling cadence (recovery contract while panel mounted), completedStreamRounds preservation across ENOENT (clearing it caused replay regression)

@wenshao maintainer review

  • Findings 1–7: fixed in 57870cba5 (full-mode double-count, rewind state reset, reconnect title, test stability, birthtimeMs removal, encode/parse charset, heartbeat/detach runtime resolution)
  • Finding 8 (perf): intentionally kept — bounded reads, changing cadence would broaden scope
  • Nit 1 (subagent.detailsEmpty i18n key): removed (not present in codebase)
  • Nit 2 (build.js budget comment): updated to "161KB to 166KB"
  • Nit 3 (App/ChatPane duplicate tab extraction): deferred — two call sites, extracting a helper adds abstraction for marginal benefit; recorded here for visibility

Verification results (all green at HEAD 7dec83a17)

Check Result
npm run build
npm run typecheck
npm run lint
build-artifact.test.ts 8 passed
telemetry-catalog.test.ts 1 passed
telemetry.test.ts 50 passed
App.test.tsx 150 passed
virtual-subagent-sessions.test.ts + server.test.ts 777 passed
agent-transcript.test.ts + background-agent-resume.test.ts 67 passed
web-shell component tests (5 files) 103 passed
SDK tests (DaemonClient + daemonUi) 556 passed
DaemonSessionProvider.subagent.test.ts 2 passed
中文说明

无需操作 — 所有反馈已确认处理完毕

本轮审查中的每个反馈点均已在此分支的先前提交(28e5ef6e31d49e278457870cba57dec83a17)中解决。未进行新的代码更改。以下是完整分类。

严重 / CHANGES_REQUESTED 项(已验证修复)

发现 状态 证据
路由漂移守卫 — subagent 路由未登记到 legacySessionTelemetryRoutes 已修复 telemetry.ts 已注册两条路由;telemetry-catalog.test.ts 通过(1 个测试)
库产物边界 — createContext 上限超标 已修复 vaul 已在 vite.lib.config.ts 中外部化;build-artifact.test.ts 通过(8 个测试,≤3 上限成立)
分屏抽屉 — 所有面板标签被路由到抽屉 已修复 App.test.tsx 断言已更新为 document.body.textContent;150 个测试通过
iterate() 中非 ENOENT 刷新失败时的订阅者泄漏 已在 57870cba5 修复 订阅者生命周期置于 try/finally 内;EISDIR 回归测试通过
virtualSubagentSessions 为 undefined 时请求静默挂起 已在 57870cba5 修复 两条路由均返回结构化 404

建议(全部已处理或记录)

全部 27 条行内建议(rc:3619497311rc:3622012634)均有作者回复:

  • 21 条已修复(提交 28e5ef6e3 / 1d49e2784 / 57870cba5 / 7dec83a17):错误消息、键盘无障碍、路由测试、去重初始化标志、ENOENT 重置、文件缩小守卫、boundedString 去重、死属性移除、空对象守卫、terminateReason 投影、aria-expanded、status 联合类型、SubagentDetailsProvider 回退、findSubagentRootTool 覆盖、composerTasks 测试、MessageList DOM 测试、死 CSS、totalTokens 移除、投影输出守卫
  • 4 条附证据拒绝:TOCTOU 竞态(JS 运行至完成模型阻止了该问题,×2)、poll-timer 测试(已被现有终态转换回归覆盖)、vi.mock 移除(测试从未包含该 mock)
  • 2 条有意保留:重试轮询节奏(面板挂载期间的恢复契约)、ENOENT 期间 completedStreamRounds 保留(清除它会导致回放回归)

@wenshao 维护者审查

  • 发现 1–7:已在 57870cba5 修复(full 模式双重计数、回退状态重置、重连标题、测试稳定性、birthtimeMs 移除、编码/解析字符集、heartbeat/detach 运行时解析)
  • 发现 8(性能):有意保留 — 有界读取,更改节奏会扩大范围
  • Nit 1(subagent.detailsEmpty i18n 键):已移除(代码库中不存在)
  • Nit 2(build.js 预算注释):已更新为 "161KB to 166KB"
  • Nit 3(App/ChatPane 重复的 tab 提取逻辑):推迟 — 仅两个调用点,提取辅助函数带来的抽象收益有限;在此记录以保持可见性

验证结果(HEAD 7dec83a17 全部通过)

检查 结果
npm run build
npm run typecheck
npm run lint
build-artifact.test.ts 8 通过
telemetry-catalog.test.ts 1 通过
telemetry.test.ts 50 通过
App.test.tsx 150 通过
virtual-subagent-sessions.test.ts + server.test.ts 777 通过
agent-transcript.test.ts + background-agent-resume.test.ts 67 通过
web-shell 组件测试(5 个文件) 103 通过
SDK 测试(DaemonClient + daemonUi 556 通过
DaemonSessionProvider.subagent.test.ts 2 通过

Base-conflict check: no conflict with main.


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

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed. Suggestions are inline. Unresolved, please confirm: [Critical] packages/web-shell/client/build-artifact.test.ts — @wenshao blocker: createContext cap test may still fail (vaul externalized but cannot verify createContext count in built bundle without running the build) Not reviewed: reverse audit — an auditor ran and opened its brief, but no agent was launched with the prompt the CLI built — the launch was written by hand, and what the agent was actually asked is not what this skill certifies.

— qwen3.7-max via Qwen Code /review

Comment thread packages/cli/src/serve/routes/sse-events.ts
Comment thread packages/cli/src/serve/routes/session.ts
Comment thread packages/web-shell/client/components/artifacts/SubagentDetail.tsx
Comment thread packages/web-shell/client/components/artifacts/SubagentDetail.tsx Outdated
Comment thread packages/cli/src/serve/virtual-subagent-sessions.test.ts
Comment thread packages/sdk-typescript/src/daemon/ui/transcript.ts Outdated
Comment thread packages/web-shell/client/App.tsx Outdated
Comment thread packages/web-shell/client/components/artifacts/SubagentDetail.tsx Outdated
Comment thread packages/web-shell/client/components/messages/ToolGroup.tsx Outdated
@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤖 Addressed the latest review feedback (round 1/100). What changed, and what I pushed back on:

Review feedback addressed

Implemented

[rc:3622665092] subagentDetails early return ignores hideHeader (ToolGroup.tsx)
Fixed. Changed if (subagentDetails) to if (subagentDetails && !hideHeader) so the clickable detail button is not rendered when the caller requests the header be hidden (e.g., single-tool groups in chat summary view). Added a regression test.

[rc:3622665078] DrawerContent CSS specificity conflict (App.tsx)
Fixed. Changed the artifact drawer's DrawerContent className from plain w-[min(520px,calc(100vw-16px))] sm:max-w-[520px] to data-[vaul-drawer-direction=right]:w-[min(520px,calc(100vw-16px))] data-[vaul-drawer-direction=right]:sm:max-w-[520px], matching the data-attribute variant group so tailwind-merge can resolve the conflict with the drawer's default w-3/4 / sm:max-w-sm classes.

[rc:3622665051] vi.useFakeTimers() not restored (virtual-subagent-sessions.test.ts)
Fixed. Added vi.useRealTimers() after the expect(settled).toBe(false) assertion so subsequent tests inherit real timers.

[rc:3622665073] Accumulated subagent usage discarded by rawOutput replace (transcript.ts)
Fixed. When compactTaskOutput is true and the existing tool block already has an accumulated executionSummary (from applySubagentUsageToParentTool), the merge now takes the max of each token field (inputTokens, outputTokens, cachedTokens, totalTokens) between the accumulated and incoming values, rather than wholesale replacing rawOutput. If only the existing block has a summary, it is carried forward. Added a regression test. Bumped the SDK browser bundle size limit from 166KB to 167KB to accommodate the added code.

[rc:3622665087] Initial resolve failure shows permanent error (SubagentDetail.tsx)
Fixed. Added bounded auto-retry (up to 3 attempts with 3s delay) for the initial resolveSubagentSession failure before falling through to the manual-retry error screen. This handles transient network hiccups or daemon cold starts gracefully.

Deferred

[rc:3622665044] Truncated prompt in subagent detail panel
Deferred to follow-up. The prompt displayed in the overview comes from rootTool.args?.prompt (the parent transcript's tool call args). Whether this is actually truncated to 240 characters depends on how the parent transcript normalizer stores tool args — this requires a separate investigation and a design decision about whether to source the full prompt from the subagent's own transcript or conditionally show the first user message.

[rc:3622665025] SSE route virtual subagent branch lacks route-level test
Deferred to follow-up. The VirtualSubagentSessions.subscribe() method is already thoroughly tested in virtual-subagent-sessions.test.ts (8 tests). The route glue in sse-events.ts is thin (resolve runtime via parent session ID, subscribe via virtual session ID, 404 guard). Adding an SSE streaming route test with supertest requires careful async iterable mocking and would grow this PR further.

[rc:3622665030] Virtual session context/heartbeat stubs lack positive-path route tests
Deferred to follow-up. The negative path (404 for missing parent) is already tested. Adding positive-path tests requires setting up a valid parent session in the workspace registry and would grow this PR. The stubs are straightforward middleware interceptors.

[rc:3622665038] SubagentDetail component lacks rendering tests
Deferred to follow-up. The exported findSubagentRootTool helper is tested. Adding full component rendering tests (loading → resolved transition, stop button, error/retry, timer cleanup) requires mocking workspace.client.resolveSubagentSession, DaemonSessionProvider, and timer behavior — a substantial test addition that would significantly grow this PR.

Review-level comment

[Critical] build-artifact.test.ts createContext cap
Not a real blocker. The reviewer noted it "cannot verify createContext count in built bundle without running the build." The build passes successfully (vaul is externalized in vite.lib.config.ts's rollupOptions.external), so vaul does not contribute createContext calls to the bundle. The existing build-artifact.test.ts cap of ≤ 3 createContext calls remains valid.

Verification

  • npm run build — passed (all packages including web-shell and sdk-typescript)
  • npm run typecheck — passed
  • npm run lint — passed
  • packages/cli: virtual-subagent-sessions.test.ts — 8/8 passed; server.test.ts — 769/769 passed
  • packages/sdk-typescript: daemonUi.test.ts — 274/274 passed (including new executionSummary merge test)
  • packages/web-shell: ToolGroup.test.tsx — 33/33 passed (including new hideHeader test); SubagentDetail.test.tsx — 2/2 passed; App.test.tsx — 150/150 passed
中文说明

已处理的审查反馈

已实现

[rc:3622665092] subagentDetails 提前返回忽略了 hideHeader(ToolGroup.tsx)
已修复。将 if (subagentDetails) 改为 if (subagentDetails && !hideHeader),使得在调用方要求隐藏头部时(例如聊天摘要视图中的单工具组),不会渲染可点击的详情按钮。已添加回归测试。

[rc:3622665078] DrawerContent CSS 优先级冲突(App.tsx)
已修复。将 artifact 抽屉的 DrawerContent className 从普通的 w-[min(520px,calc(100vw-16px))] sm:max-w-[520px] 改为 data-[vaul-drawer-direction=right]:w-[min(520px,calc(100vw-16px))] data-[vaul-drawer-direction=right]:sm:max-w-[520px],匹配 data-attribute 变体组,使 tailwind-merge 能正确解决与抽屉默认 w-3/4 / sm:max-w-sm 类的冲突。

[rc:3622665051] vi.useFakeTimers() 未恢复(virtual-subagent-sessions.test.ts)
已修复。在 expect(settled).toBe(false) 断言后添加了 vi.useRealTimers(),确保后续测试使用真实计时器。

[rc:3622665073] 累积的子代理用量被 rawOutput 替换丢弃(transcript.ts)
已修复。当 compactTaskOutput 为 true 且现有工具块已有累积的 executionSummary(来自 applySubagentUsageToParentTool)时,合并逻辑现在对每个 token 字段(inputTokensoutputTokenscachedTokenstotalTokens)取累积值和传入值的最大值,而不是整体替换 rawOutput。如果只有现有块有摘要,则将其保留。已添加回归测试。将 SDK 浏览器包大小限制从 166KB 提升到 167KB 以容纳新增代码。

[rc:3622665087] 初始解析失败显示永久错误(SubagentDetail.tsx)
已修复。为初始 resolveSubagentSession 失败添加了有限次自动重试(最多 3 次,间隔 3 秒),然后再显示手动重试的错误界面。这可以优雅地处理短暂的网络波动或守护进程冷启动。

延期处理

[rc:3622665044] 子代理详情面板中提示词被截断
延期至后续处理。概览中显示的提示词来自 rootTool.args?.prompt(父转录的工具调用参数)。这是否实际被截断为 240 个字符取决于父转录规范化器如何存储工具参数——这需要单独调查,并需要就是否从子代理自己的转录中获取完整提示词或有条件地显示第一条用户消息做出设计决策。

[rc:3622665025] SSE 路由虚拟子代理分支缺少路由级测试
延期至后续处理。VirtualSubagentSessions.subscribe() 方法已在 virtual-subagent-sessions.test.ts 中充分测试(8 个测试)。sse-events.ts 中的路由胶水代码很薄(通过父会话 ID 解析运行时,通过虚拟会话 ID 订阅,404 守卫)。使用 supertest 添加 SSE 流式路由测试需要仔细的异步可迭代对象模拟,会进一步增大此 PR。

[rc:3622665030] 虚拟会话 context/heartbeat 桩缺少正向路径路由测试
延期至后续处理。负向路径(缺少父会话时返回 404)已有测试。添加正向路径测试需要在工作区注册表中设置有效的父会话,会增大此 PR。这些桩是简单的中间件拦截器。

[rc:3622665038] SubagentDetail 组件缺少渲染测试
延期至后续处理。导出的 findSubagentRootTool 辅助函数已有测试。添加完整的组件渲染测试(加载→解析过渡、停止按钮、错误/重试、计时器清理)需要模拟 workspace.client.resolveSubagentSessionDaemonSessionProvider 和计时器行为——这是一个大量的测试添加,会显著增大此 PR。

审查级别评论

[Critical] build-artifact.test.ts createContext 上限
不是真正的阻塞问题。审查者指出"无法在不运行构建的情况下验证构建包中的 createContext 数量"。构建成功通过(vaul 在 vite.lib.config.tsrollupOptions.external 中被外部化),因此 vaul 不会向包中贡献 createContext 调用。现有的 build-artifact.test.ts 中 ≤ 3 个 createContext 调用的上限仍然有效。

验证结果

  • npm run build — 通过(所有包,包括 web-shell 和 sdk-typescript)
  • npm run typecheck — 通过
  • npm run lint — 通过
  • packages/clivirtual-subagent-sessions.test.ts — 8/8 通过;server.test.ts — 769/769 通过
  • packages/sdk-typescriptdaemonUi.test.ts — 274/274 通过(包括新的 executionSummary 合并测试)
  • packages/web-shellToolGroup.test.tsx — 33/33 通过(包括新的 hideHeader 测试);SubagentDetail.test.tsx — 2/2 通过;App.test.tsx — 150/150 通过

Base-conflict check: no conflict with main.

Re-review when you have a moment. After round 100 this bot stops and leaves the PR for a human.


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

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed — no blockers. Suggestions are inline.

— qwen3.7-max via Qwen Code /review

Comment thread packages/web-shell/client/components/ui/drawer.tsx
@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤖 Could not address the latest feedback automatically (round 2/100). A human should take over this PR.

⚠️ This change was NOT pushed — any commit referenced below was made only in the runner workspace and has been discarded. What the agent reported:

Review feedback addressed

[rc:3623619851] DrawerOverlay and DrawerContent missing from react18-ref-compat.test.tsx[Suggestion]

Decision: Implemented.

Rationale: DrawerOverlay and DrawerContent both use React.forwardRef (matching the pattern in dialog.tsx), but were not covered by the existing React 18 ref-compatibility regression test. Adding them closes the gap so a future refactor that strips forwardRef from these components will be caught.

Changes:

  • Added DrawerContent and DrawerOverlay imports and entries to the it.each table in packages/web-shell/client/components/ui/react18-ref-compat.test.tsx, in alphabetical order alongside the existing dialog, alert-dialog, popover, select, button, and input entries.

Conflict notes: None (--conflict false).

Verification:

  • npx vitest run client/components/ui/react18-ref-compat.test.tsx — 13/13 tests pass (including the 2 new Drawer entries)
  • npm run build — pass
  • npm run typecheck — pass
  • npm run lint — pass
中文说明

已处理的审查反馈

[rc:3623619851] DrawerOverlayDrawerContent 未包含在 react18-ref-compat.test.tsx 中 — [建议]

决定: 已实现。

理由: DrawerOverlayDrawerContent 都使用了 React.forwardRef(与 dialog.tsx 中的模式一致),但未被现有的 React 18 ref 兼容性回归测试覆盖。添加它们可以弥补这一缺口

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


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

@ytahdn

ytahdn commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator Author

@qwen-code /triage

@wenshao

wenshao commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /retry

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🔄 AutoFix re-armed. The next scan re-reads this PR's feedback from the start and the round counter resets. Nothing was deleted — this marker supersedes the evaluation markers above it.

中文说明

🔄 已重新武装 AutoFix。下一次扫描会从头重新读取本 PR 的反馈,轮次计数也已重置。未删除任何内容 —— 本标记使其上方的评估标记失效。

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Triage re-run completed without a new review.

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

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤖 Could not address the latest feedback automatically (round 1/100). A human should take over this PR.

⚠️ This change was NOT pushed — any commit referenced below was made only in the runner workspace and has been discarded. What the agent reported:

Review feedback round 4 — summary

Implemented

rc:3622665025 — SSE route virtual subagent test

Added a route-level test in server.test.ts that creates a virtual session ID via createVirtualSubagentSessionId, spies on VirtualSubagentSessions.prototype.subscribe, issues a GET to /session/${virtualId}/events, and verifies the SSE stream yields events from the virtual subscribe path with correct content-type and frame shape.

rc:3622665030 — Virtual session context/heartbeat positive-path tests

Added a route-level test that hits GET /session/${virtualId}/context, GET /session/${virtualId}/supported-commands, and POST /session/${virtualId}/heartbeat with a valid parent session, asserting the stub response shapes (200, correct JSON structure).

rc:3622665038 — SubagentDetail component rendering tests

Extended SubagentDetail.test.tsx with three component-level tests: loading → resolved transition (verifies DaemonSessionProvider and MessageList mount), stop button calling cancelSubagentSession, and error state with retry after exhausting the 3-attempt auto-retry budget. The existing findSubagentRootTool unit tests are preserved.

Already fixed in current code (no action needed)

  • rc:3622665051 (fake timers): vi.useRealTimers() is already present at the relevant line.
  • rc:3622665073 (usage accumulation): upsertToolBlock already merges executionSummary token counts when compactTaskOutput is true, preserving the higher

Why it was not pushed:

build failed on the agent-committed fix

x.cjs               �[39m�[1m�[2m401.74 kB�[22m�[1m�[22m�[2m │ gzip: 99.15 kB�[22m�[2m │ map: 846.92 kB�[22m
�[32m✓ built in 15.44s�[39m

> @qwen-code/web-shell@0.20.0 build
> vite build && vite build --config vite.lib.config.ts && tsc -p tsconfig.lib.json

�[36mvite v5.4.21 �[32mbuilding for production...�[36m�[39m
transforming...
�[32m✓�[39m 1801 modules transformed.
�[31mx�[39m Build failed in 1.61s
�[31merror during build:
�[31m[vite]: Rollup failed to resolve import "vaul" from "/home/runner/work/qwen-code/qwen-code/packages/web-shell/client/components/ui/drawer.tsx".
This is most likely unintended because it can break your application at runtime.
If you do want to externalize this module explicitly add it to
`build.rollupOptions.external`�[31m
    at viteWarn (file:///home/runner/work/qwen-code/qwen-code/packages/web-shell/node_modules/vite/dist/node/chunks/dep-BK3b2jBa.js:65855:17)
    at onwarn (file:///home/runner/work/qwen-code/qwen-code/node_modules/@vitejs/plugin-react/dist/index.js:90:7)
    at onRollupWarning (file:///home/runner/work/qwen-code/qwen-code/packages/web-shell/node_modules/vite/dist/node/chunks/dep-BK3b2jBa.js:65885:5)
    at onwarn (file:///home/runner/work/qwen-code/qwen-code/packages/web-shell/node_modules/vite/dist/node/chunks/dep-BK3b2jBa.js:65550:7)
    at file:///home/runner/work/qwen-code/qwen-code/node_modules/rollup/dist/es/shared/node-entry.js:20813:13
    at Object.logger [as onLog] (file:///home/runner/work/qwen-code/qwen-code/node_modules/rollup/dist/es/shared/node-entry.js:22679:9)
    at ModuleLoader.handleInvalidResolvedId (file:///home/runner/work/qwen-code/qwen-code/node_modules/rollup/dist/es/shared/node-entry.js:21425:26)
    at file:///home/runner/work/qwen-code/qwen-code/node_modules/rollup/dist/es/shared/node-entry.js:21383:26�[39m
npm error Lifecycle script `build` failed with error:
npm error code 1
npm error path /home/runner/work/qwen-code/qwen-code/packages/web-shell
npm error workspace @qwen-code/web-shell@0.20.0
npm error location /home/runner/work/qwen-code/qwen-code/packages/web-shell
npm error command failed
npm error command sh -c vite build && vite build --config vite.lib.config.ts && tsc -p tsconfig.lib.json
node:internal/errors:983
  const err = new Error(message);
              ^

Error: Command failed: npm run build --workspace=packages/web-shell
    at genericNodeError (node:internal/errors:983:15)
    at wrappedFn (node:internal/errors:537:14)
    at checkExecSyncError (node:child_process:916:11)
    at execSync (node:child_process:988:15)
    at file:///home/runner/work/qwen-code/qwen-code/scripts/build.js:82:3
    at ModuleJob.run (node:internal/modules/esm/module_job:343:25)
    at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:681:26)
    at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:117:5) {
  status: 1,
  signal: null,
  output: [ null, null, null ],
  pid: 8380,
  stdout: null,
  stderr: null
}

Node.js v22.23.1

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


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

@ytahdn

ytahdn commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator Author

@qwen-code /retry

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🔄 AutoFix re-armed. The next scan re-reads this PR's feedback from the start and the round counter resets. Nothing was deleted — this marker supersedes the evaluation markers above it.

中文说明

🔄 已重新武装 AutoFix。下一次扫描会从头重新读取本 PR 的反馈,轮次计数也已重置。未删除任何内容 —— 本标记使其上方的评估标记失效。

@ytahdn ytahdn removed the autofix/takeover Summon the autofix loop to manage this PR (remove to release; needs triage+) label Jul 22, 2026
@ytahdn

ytahdn commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator Author

@qwen-code /review

@github-actions

Copy link
Copy Markdown
Contributor
_Qwen Code review request accepted. Review is queued in [workflow run](https://github.com/QwenLM/qwen-code/actions/runs/29884805075)._

@chiga0

chiga0 commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

Re-Review at HEAD d63571f8

37 new commits since last review (mostly upstream merges). PR-specific changes address review feedback rounds 2 and 3. My previous APPROVE at a8de487e stands — the additional commits fix remaining detail feedback without introducing new concerns.

@yiliang114 yiliang114 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

架构设计合理,虚拟 session + 独立 SSE 流的方案有效降低主会话内存压力。几个非阻塞建议:

  1. findLegacyTaskByToolCall 全量读 JSONLvirtual-subagent-sessions.ts ~L800):旧 transcript 每次 resolve 都线性扫描整个父 session 文件,长会话下 I/O 开销大,建议加 LRU 缓存或限制扫描行数
  2. 250ms 轮询偏激进POLL_INTERVAL_MS):多个并行子智能体时 I/O 压力显著,建议 500ms-1s 或考虑 fs.watch
  3. getSubagentPrompt 未 memoizeSubagentDetail.tsx ~L90):每次 render 遍历 messages,建议移入 useMemo
  4. SSE 路由对虚拟 session 静默忽略 snapshot=1,重连场景可能丢状态,建议确认
  5. 设计文档(613 行)建议拆到独立 docs PR 或 wiki

以上为优化建议,不阻塞合入。

@chiga0 chiga0 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Re-Review at HEAD d63571f8

Previous APPROVE was dismissed. Re-confirming after reviewing 37 new commits (mostly upstream merges, plus review feedback rounds 2 & 3).

No new concerns. Backend architecture remains sound: auth/authz via bearer token + requireSessionRuntime, virtual session ID validation, stream sidecar lifecycle, metrics merging with clear precedence. Cancel route performance (full resolution for taskId) is a follow-up optimization, not a blocker.

LGTM.


This review was generated by QoderWork AI

@ytahdn
ytahdn added this pull request to the merge queue Jul 22, 2026
Merged via the queue into QwenLM:main with commit 80863c0 Jul 22, 2026
44 of 45 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants