refactor(vscode-ide-companion): migrate chat to WebShell and qwen serve - #9811
Conversation
Code Coverage Summary
CLI Package - Full Text ReportCore Package - Full Text ReportFor detailed HTML reports, please see the 'coverage-reports-22.x-ubuntu-latest' artifact from the main CI run. |
|
Thanks for the PR! Template looks good ✓ Problem: real and directly observable — not theoretical. On Direction: aligned. Consolidating onto one shell so Size: 15,585 changed lines in total — ~10,480 generated ( Approach: the design doc — this PR's actual deliverable today — is disciplined: one controlled embedded-host entry point owned by Risk: no elevated risk signals — none of the changed files match the revert-correlated high-risk paths. Moving on to code review. 🔍 中文说明感谢贡献! 模板完整 ✓ 问题: 真实且可直接观察,不是理论问题。 方向: 对齐。收敛到单一外壳、为最终退役 规模: 共 15,585 行变更——约 10,480 行为生成文件( 方案: 设计文档——本 PR 目前真正的交付物——很克制:由 风险: 无升级风险信号——变更文件均未命中与 revert 相关的高风险路径。 进入代码审查 🔍 — Qwen Code · qwen3.8-max Reviewed at |
Code reviewReviewed against an independent proposal first: to finish this cutover without dragging VS Code into a runtime migration, I would keep ACP as the host boundary, expose one controlled embedded-host entry point from The structural finding matters more than anything in the code. This PR's entire code diff is #9719's: all 25 code commits are SHA-identical to #9719's first 25 commits, and the only commit unique to this branch is the design doc. Two consequences:
What my static pass over the shared code confirms (so a maintainer doesn't have to re-derive it): the migration is thorough and honest. Session boundaries are handled rigorously in Sandboxed verification would settle the one behavioural claim this diff carries, but run it against #9719's current head, not this stale snapshot: Files changed (30 of 32 shown)
Test evidenceBoth
The macOS/Windows unit legs and the CLI integration suite were skipped in this run while the run-level conclusions are success; I did not chase the skip conditions deeper than that. Not verified: live rendering of the transcript in a real VS Code webview — unit/DOM suites cannot show that, and no author evidence is provided either (all "Tested on" entries are 中文说明代码审查先按独立方案评估:要在不把 VS Code 拖入运行时迁移的前提下完成切换,我会保留 ACP 作为宿主边界、由 结构性发现比代码本身更重要。 本 PR 的全部代码 diff 就是 #9719:25 个代码提交与 #9719 的前 25 个提交 SHA 完全一致,本分支独有的提交只有设计文档。两个后果:
静态通读共享代码的确认结果(维护者不必重新推导):迁移彻底且诚实。 沙盒验证可以敲定这份 diff 唯一的行为性论断,但应该在 #9719 的当前头上运行,而不是这个过时快照:在那边运行 测试证据被审查提交上的两个 — Qwen Code · qwen3.8-max Reviewed at |
|
Confidence: 3/5 — strong design and solid stacked code, but this is a tracking draft whose implementation hasn't landed yet; there is nothing mergeable to approve until #9719 does. Stepping back: the design doc — this branch's only unique content — is the best part of this PR. My independent proposal for the cutover and the doc agree point for point, and I verified its claims about the current state against What keeps this at 3/5 and out of approval is where the PR sits in its own lifecycle, not the quality of anything in it:
⏸️ Deferring to @yiliang114 — as the author and a maintainer of this repo, three calls are yours:
Not requesting changes — the direction, the design, and the stacked code are all sound. Holding approval because there is nothing in this branch yet that isn't either reviewed elsewhere or still to be written. 中文说明置信度:3/5 —— 设计扎实、堆叠的代码质量也不错,但这是一个实现尚未落地的跟踪性草稿;在 #9719 合并之前没有可批准合入的内容。 整体来看:设计文档——本分支唯一的独有内容——是这个 PR 最好的部分。我对切换方案的独立构想与文档逐点一致,并且文档对现状的描述(WebUI 导入数量、web-shell 的依赖声明)我都在 让它停在 3/5、不予批准的,是 PR 在自身生命周期中所处的位置,而不是其中任何内容的质量:
⏸️ 转交 @yiliang114 —— 作为作者和本仓库维护者,有三件事需要你决定:
不请求修改——方向、设计和堆叠的代码都没有问题。暂不批准,是因为这个分支里目前的内容,要么正在别处接受审查,要么还没有写出来。 — Qwen Code · qwen3.8-max Reviewed at |
|
🤝 Takeover engaged: the autofix loop now manages this PR — it will address new review feedback and resolve base conflicts until the label is removed or the round cap is reached. Remove the 中文说明🤝 已接管:autofix 循环现在管理此 PR —— 将持续处理新的评审反馈与 base 冲突,直到移除标签或达到轮次上限。移除 |
…imental flag Bridge ACP session/update notifications into the shared SDK daemon transcript reducer and render the result with the WebShell transcript component, gated on qwen-code.experimental.webShellTranscript (default off). The WebShell renderer and its heavy transitive dependencies (echarts, mermaid, shiki, codemirror, katex) are lazily loaded via esbuild code splitting, so the default configuration keeps the ~700KB webview bundle unchanged.
… WebShell transcript enabled
…line Drop the experimental flag and the legacy MessageList renderer. The companion timeline now always renders through the shared WebShell transcript component, fed by ACP session/update notifications via the SDK daemon transcript reducer (lazy loaded through esbuild code splitting). The flag-gated wiring is removed: the qwen-code.experimental.webShellTranscript setting, the conditional CSP/body attribute in WebViewContent, and the legacy MessageList path in App.tsx (~850 lines). The webview CSP now grants wasm-unsafe-eval unconditionally for Shiki's Oniguruma WASM.
… switch The experimental useAcpTranscript hook only consumed transcriptUpdate messages, so its reducer state survived session boundaries. When the extension switched sessions it kept the webview mounted and replayed the newly-selected session through ACP, causing the previous session's blocks to merge with the new replay (e.g. user text "alpha" from session A leaked into session B as "alphabeta"). Reset both the reducer state and the rendered blocks on the same boundaries the legacy message flow uses: qwenSessionSwitched (sent before the ACP replay of the selected session) and conversationCleared (new session). Adds a regression test that replays two sessions with a switch between them.
- reset the transcript on `conversationLoaded` too, closing the same cross-session leak the previous commit fixed for `qwenSessionSwitched` and `conversationCleared` (agent reconnect posts only this boundary) - track the active session id and drop late `transcriptUpdate` frames whose `sessionId` no longer matches, so a previous session's trailing frames cannot contaminate the next session's timeline - seed the transcript from cached messages carried by `qwenSessionSwitched` so offline restores and load-failure fallbacks render their history instead of a blank timeline - dispatch `assistant.done` on `streamEnd`/`sessionLoadComplete` so the final assistant/thought block of a turn (or history replay) does not stay `streaming: true` forever
…r of the composer
…in the transcript area
…WebShell transcript
…link in the transcript UI
…n session-switch seeding guard Map streamEnd reasons timeout/session_expired onto the reducer's error reason so abandoned mid-tool turns no longer spin forever (ceuI). Add qwenSessionSwitched cases with no messages field and an empty cache array; the no-messages case fails when the seeding guard is forced true, pinning its false side (ceuN).
… dead submit options The user-message edit/rewind UI was dropped in the WebShell-transcript migration, leaving editTargetTurnIndex/onSubmitted options in useMessageSubmit and the full editMessage/rewind flow in SessionMessageHandler unreachable. Remove the dead options, the editMessage dispatch case, the rewind/snapshot flow with its recovery branches, and their tests (R1-8 direction b).
The waiting-message renderer was removed with the WebShell transcript migration and the user prompt is echoed into the timeline at send time (bd09e19), so the loadingMessage string was write-only dead state. Keep the isWaitingForResponse flag (submit gating / cancel) and pin its API surface (R1-19 direction b).
…imeline The prompt carries pasted/attached images as ACP resource_link blocks, which the transcript reducer cannot render (no inline data), so user images vanished from the timeline while the attach path stayed alive. Read each saved prompt image back from disk and echo it alongside the text echo as an inline user_message_chunk image part (the daemon-echo content shape), which the shared reducer folds into the user block and the WebShell renderer already displays. Unreadable images are skipped without breaking the send.
webShellTheme was snapshotted once at mount via useMemo with an empty dependency array, so switching the VS Code color theme left the timeline on the stale theme (VS Code updates data-vscode-theme-kind on <body> in place without reloading the webview). Hold the theme in state and refresh it with a MutationObserver on the body theme attributes.
…mbiguous row keys - Copy All Messages now includes tool, shell, user_shell, and status blocks via getBlockCopyText, matching the pre-PR copyAllMessages handler which included formatted tool calls (review 5001842059 S-1). - findBlockByRowKey prefers an exact id match and otherwise the longest matching block id, so one block id that dash-prefixes a sibling (e.g. `a` vs `a-1`) can no longer capture the sibling's row key (S-4).
|
Closed out the current review batch in The remaining Critical paths are now addressed: the VS Code host tracks only genuinely queued prompts, native permission diffs prefer the authoritative ACP The earlier auth-cancel, active-file boundary, history focus trap, session-switch timeout, locale, relative Current |
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Resolve directory-rename conflict: accept main's new daemon session-context files (#10418) at the renamed packages/web-shell/client path. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
|
Addressed the blockers from @doudouOUC's review and @qqqys's follow-up on the current head.
Focused verification: |
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
doudouOUC
left a comment
There was a problem hiding this comment.
Approving at exact head 5d7fa3d0.
Follow-up verification of my round-4 blockers, all read against the current head (the earlier rounds' 109 threads are all resolved):
- WebSocket origin / voice 403 — Resolved by removal:
COMPOSER_TOOLBAR_ACTIONSno longer lists'voice', and there are no remaining voice references anywhere in the companion source. The legacy UI never exposed voice, so there is no parity loss and no remaining webview-origin WebSocket.--allow-origin '*'stays for the webview's HTTP/SSE CORS and remains token-gated on a loopback bind — acceptable as documented. - Queued-prompt tracking — Fixed: the ref is now stored only for
queued: truesubmits and cleared on the next session event for that session (turn_completeincluded). The edit path above it now reads correctly for the normal submit → complete → edit-older-message flow. One non-blocking leftover: the regression test I asked for was not added — inline comment on the handler. - Permission diff content — Fixed host-side per the reviewed direction:
permissionDiffPreviewreads the authoritativetoolCall.content[type='diff'](fulloldText/newText) instead of the rawInput-derived preview, the SDK-side probe change was fully reverted, andEmbeddedApp.test.tsx› "opens permission diffs only from authoritative tool-call content" pins the unchanged prefix/suffix appearing inopenDiff. - Discontinued qwen-oauth model gate — The VS Code channel now filters discontinued ids out of the main model dialog via
mainModelFilterplus a companion-localisDiscontinuedModelmirroring the CLI rule (authType === 'qwen-oauth' && !isRuntime); daemon model ids carry the(authType)wrapper, so the filter is live. This closes the cutover regression for the panel. The maintainer-preferred daemon-side fail-closed (rejecting discontinued ids in the set-model route for every host, browser Web Shell included) remains open — that gap predates this PR on the browser side, so it is follow-up material, not a blocker here.
CI at review time: no failing checks at 5d7fa3d0. The previous Test (ubuntu-latest) ESLint failure was a single react-hooks/exhaustive-deps warning on hasOlderHistory in MessageList.tsx, now included in the dep array, and the orphaned useCompletionTrigger.test.tsx was removed. Java 11/17/21 lanes and the web-shell visuals lane are green; Test (ubuntu-latest, Node 22.x) and Real daemon E2E / Java 11 were still running — merge should wait for them to finish green.
| } else if ( | ||
| latestSubmittedPromptRef.current?.sessionId === event.sessionId | ||
| ) { | ||
| latestSubmittedPromptRef.current = undefined; |
There was a problem hiding this comment.
[Suggestion] Non-blocking follow-up to the round-4 fix: this now correctly records only queued: true submits and clears on the next session event, but the regression test requested earlier was not added — normal submit → turn_complete → Edit on an older message must keep the clicked turnIndex rather than selecting the max snapshot. Please add it here or in a follow-up; the rewind-target selection is exactly the kind of logic that regresses silently.
qwen-code-dev-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
qwen-code-review-bot
left a comment
There was a problem hiding this comment.
Reviewed the current head and the latest verification results. The previously raised blocking concerns appear to be addressed in the current implementation; the remaining regression-test suggestion is non-blocking and can be followed up separately. Approving based on the current scope and evidence.
- Standalone creation omits workspace-only fields (sourceType/worktree/branch)
- Capability read is tri-state: loading waits, only loaded-absent falls back
- Pending context stays authoritative until attach commits
- Live current-session New Chat routes through startLive('new')
- Repair offered only for missing directories; compromised is fail-closed
- Recents standalone view model never exposes the internal cwd
- Creating deep links poll to a terminal state with explicit Retry
- Refresh anchors for the #9811 WebShell cutover
The lockfile was generated before the WebShell cutover (#9811) removed @qwen-code/webui from web-shell and the tailwind tooling plus @qwen-code/webui from vscode-ide-companion, so frozen-lockfile installs fail on all three smoke platforms with ERR_PNPM_OUTDATED_LOCKFILE. Drop the five stale importer entries so the lockfile matches the current package.json manifests; verified with pnpm 11.24.0 install --frozen-lockfile --lockfile-only. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Standalone creation omits workspace-only fields (sourceType/worktree/branch)
- Capability read is tri-state: loading waits, only loaded-absent falls back
- Pending context stays authoritative until attach commits
- Live current-session New Chat routes through startLive('new')
- Repair offered only for missing directories; compromised is fail-closed
- Recents standalone view model never exposes the internal cwd
- Creating deep links poll to a terminal state with explicit Retry
- Refresh anchors for the #9811 WebShell cutover
- Standalone creation omits workspace-only fields (sourceType/worktree/branch)
- Capability read is tri-state: loading waits, only loaded-absent falls back
- Pending context stays authoritative until attach commits
- Live current-session New Chat routes through startLive('new')
- Repair offered only for missing directories; compromised is fail-closed
- Recents standalone view model never exposes the internal cwd
- Creating deep links poll to a terminal state with explicit Retry
- Refresh anchors for the #9811 WebShell cutover
- Standalone creation omits workspace-only fields (sourceType/worktree/branch)
- Capability read is tri-state: loading waits, only loaded-absent falls back
- Pending context stays authoritative until attach commits
- Live current-session New Chat routes through startLive('new')
- Repair offered only for missing directories; compromised is fail-closed
- Recents standalone view model never exposes the internal cwd
- Creating deep links poll to a terminal state with explicit Retry
- Refresh anchors for the #9811 WebShell cutover
- Standalone creation omits workspace-only fields (sourceType/worktree/branch)
- Capability read is tri-state: loading waits, only loaded-absent falls back
- Pending context stays authoritative until attach commits
- Live current-session New Chat routes through startLive('new')
- Repair offered only for missing directories; compromised is fail-closed
- Recents standalone view model never exposes the internal cwd
- Creating deep links poll to a terminal state with explicit Retry
- Refresh anchors for the #9811 WebShell cutover
What this PR does
This PR migrates the VS Code IDE Companion chat from the legacy WebUI/ACP-rendered path to WebShell backed by a workspace-scoped
qwen servedaemon.WebShell now owns the transcript, composer, permissions, Ask User Question, session history, attachments, model and approval controls, and insight cards. The extension retains the VS Code-specific responsibilities: daemon lifecycle, authentication, workspace trust, active-editor context, native file and diff actions, copy commands, and the custom session header.
Companion sessions are tagged with the
vscodesource, and history is filtered to that source so terminal and browser sessions do not appear in the VS Code panel.The companion's legacy renderer and its
@qwen-code/webuidependency are removed. Deleting the sharedpackages/webuipackage itself is handled separately in #9812.Why it's needed
#9719 adopted WebShell for transcript rendering but still left overlapping UI and state implementations across WebShell, the legacy companion UI, and ACP.
This change makes the daemon transcript the single source of truth and keeps VS Code-specific behavior in the host adapter.
Architecture notes
Reviewer Test Plan
How to verify
Use a dedicated VS Code Extension Development Host and verify conversation start and cancellation, streaming and tool states, Bash/Edit permissions, Ask User Question, history and session switching, model and approval changes, attachments, active-editor context, file opening, native diff actions, copy commands, authentication, daemon recovery, multi-root isolation, and narrow-panel layout.
Evidence (Before & After)
The screenshot reports below cover an earlier implementation stage. They are retained as historical evidence but do not validate the final daemon path:
A consolidated real-host regression report for the merged architecture will be published with #10534.
Tested on
Risk & Scope
packages/webuiis tracked by refactor: retire @qwen-code/webui #9812.Linked Issues
Part of #5883. Builds on #9719. Followed by #9812, #9911, and #10534.