refactor: retire @qwen-code/webui - #9812
Draft
yiliang114 wants to merge 132 commits into
Draft
Conversation
Establish a shared transcript model and document-mode projection so Web Shell, VS Code, and HTML export can consume the same stable conversation semantics without changing interactive rendering. - Preserve daemon and ACP segment identity across replay and normalization - Add export-safe previews and a versioned transcript document builder - Add document-mode Web Shell rendering with bounded Mermaid processing - Lock direct-daemon and ACP behavior with contract fixtures - Cover render and export equivalence in integration tests
Move transcript prevalidation onto real product paths while preserving the legacy VS Code timeline as the default fallback. - Add version-bound document HTML with strict CSP and schema checks - Add opt-in ACP projection with stable source identity - Reuse product export code across hosts and the integration runner - Remove test-only production probes and consolidate shared validation Note: Browser, host-action, VSIX, and packaging gates remain pending.
Keep the npm lockfile aligned with the VS Code and HTML export package manifests added by the transcript consumer migration. - Lock the VS Code Web Shell workspace dependency - Lock Web Templates SDK, Web Shell, and React build dependencies
…plit-20260818 # Conflicts: # packages/acp-bridge/src/transcript-replay.ts # packages/sdk-typescript/scripts/build.js # packages/sdk-typescript/src/daemon/ui/transcript.ts # packages/web-shell/client/components/MessageList.tsx # packages/web-shell/client/components/messages/GoalStatusMessage.tsx
…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
Resolve review findings across document export and the VS Code ACP timeline while preserving default interactive and readonly semantics. - Preserve stable text and non-text identity across live and replay - Harden export projection, budgets, URL handling, CSP, and nonces - Make document rendering complete, inert, and browser-validated - Keep VS Code transcript scope, theme, copy, and flags reactive - Restore fixture, schema, hash, and compatibility contract locks Note: The overall gate remains failed pending VSIX, host-action, and packaged-artifact evidence.
…eb-shell-adapter-before-split-20260818 # Conflicts: # packages/web-shell/client/components/MessageList.tsx # packages/web-shell/client/components/messages/AssistantMessage.tsx # packages/web-shell/client/components/messages/ToolGroup.tsx
…eb-shell-adapter-before-split-20260818
…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
Close the latest review findings across transcript identity, VS Code rehydration, and the HTML export security boundary. - Terminate discrete ACP segments and bind automatic turn provenance - Reset VS Code transcript scopes across replay and reconnect lifecycles - Align Markdown sanitization and envelope budgets with document rendering - Run browser gates with Chromium and complete third-party notices
…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).
Reduce the shared transcript review surface while keeping the VS Code and HTML Export consumers and their security gates intact. - Merge VS Code feature state into the ACP transcript hook - Collect source identity once and strip it at the compatibility boundary - Remove test-only export adapters, gate reports, and duplicate helpers - Keep shell output separated when stable producer segments change - Mark third-party notices as generated review content
…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.
The companion now drives Web Shell against a shared `qwen serve` daemon, so the CLI, the browser Web Shell, and this extension all create sessions in the same workspace catalog. Web Shell recorded `'default'` for every surface, leaving VS Code conversations indistinguishable from terminal and browser ones — the panel's history listed sessions the user never opened here, and nothing attributed a session back to the editor. Give Web Shell a `sessionSourceType` prop (defaulting to today's `'default'`) and have the companion stamp `'vscode'` on the sessions it creates, then scope the history dropdown to that source. The host also supplies a stable daemon `clientId`, which the bootstrap previously declared but never sent. Web Shell localizes its own surface from the `language` signal while the companion's chrome was hardcoded English, so a zh-CN panel rendered a Chinese transcript under an English header, history dropdown, onboarding screen, and account dialog. Route that chrome through a small string table driven by the same signal, including the host-only slash entries. Also fix accessibility defects in the history dropdown: rename and delete were revealed on hover alone and unreachable by keyboard, date headers sat inside `role="listbox"` as invalid non-option children, arrow-key roving stopped at group boundaries, `aria-modal` had no focus trap, and a primed "Delete?" survived both search changes and the pointer leaving the row. Formatting: `FileMessageHandler` and `SessionMessageHandler` were left unformatted earlier in this branch and failed the Prettier gate.
The webview entry now renders EmbeddedApp against the daemon, which left the ACP-era hook layer unreachable: nothing imports acpTranscriptAdapter, useWebViewMessages, useAcpTranscript, useToolCalls, useSessionManagement, useMessageHandling, useFileContext, useImage, or the permissionTypes added by this branch. A reachability walk from webview/index.tsx reaches eight modules; every reference to the rest comes from inside the orphaned set itself, so it deletes as a closed unit. EmbeddedWebShell goes with them. It was the host-driven entry point from the earlier stage of this branch, superseded when EmbeddedApp moved to WebShellWithProviders, and has had no consumer since — only its own DOM test and a barrel export. Also harden the daemon process lifecycle. `start()` returned the cached runtime without comparing the workspace, so in a multi-root window the second folder's chat silently reused a daemon bound to the first and scoped every session, history page, and prompt to the wrong root. Bind the daemon to its workspace and respawn on a change, keep a superseded child's late exit from tearing down its successor, and report a post-startup exit to the webview instead of leaving it fetching against a dead port.
…hips The design doc still recorded the plan this branch started from: keep ACP as the runtime boundary, add no daemon server or loopback port, and treat "replacing ACP with daemon HTTP/SSE" as a non-goal. The final stage did exactly that, so the document argued against the code beneath it. Record the decision and its consequences instead — two processes per workspace, a daemon shared with the CLI and browser Web Shell, the vscode source type that keeps the panel's history its own, workspace rebinding in multi-root windows, and the turn-driven host features that stopped firing.
…l-cutover The branch had gone conflicting against main, which stops GitHub from building the merge ref — so `Qwen Code CI` (triggered on `pull_request`) had not run since the previous main merge, leaving recent commits with no test signal at all. The single conflict was in skills-manager-logic.test.ts: this branch moved the daemon React import from `@qwen-code/webui/daemon-react-sdk` to `@qwen-code/web-shell/daemon-react-sdk`, while #10296 on main added `skillExtensionLabel` to the same import. Kept the branch's module path and main's new symbol.
…9811) - closeDiff now resolves workspace-relative paths the same way showDiff does, so permission-cycle diffs opened from daemon-relative paths can actually be matched and closed - a superseded or disposed daemon child no longer reports its exit as a crash of the live daemon - authCancelled no longer hides an already-authenticated session behind onboarding; only an unknown auth state settles to unauthenticated - selection-only activeEditorChanged events no longer undo an explicit active-file exclusion - prepareSubmit dedupes mentions in both path spaces and matches typed references on a whole-reference boundary - permission diffs open only from the SDK's authoritative file_diff preview (writes included, model-controlled toolCall mining removed) - the webview HTML carries VS Code's locale so chrome strings localize - discontinued qwen-oauth models are no longer re-applied through the new-session initial-model route
#9811) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
The cutover branch dropped the ./daemon-react-sdk export from @qwen-code/webui, but the TerminalPanel merged in from main still imports it, breaking the web-shell vite build (Missing "./daemon-react-sdk" specifier). Point the import and its test mock at @qwen-code/web-shell/daemon-react-sdk, which re-exports the same useWorkspace hook and matches every other web-shell call site. 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>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Resolve import-path conflicts in favor of the cutover: keep the @qwen-code/web-shell/daemon-react-sdk entrypoint for SessionOverviewPanel (adding main's new useWorkspace import), keep the findIndex rationale comment in App.tsx, and union the lucide icons in UserMessage. Convert main's new webui daemon-react-sdk imports (LocalControlQrButton, ChatContextHeader/SettingsMessage tests) to the web-shell entrypoint, which the cutover leaves as the only provider. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
The renderItem useCallback reads hasOlderHistory to gate the edit action but omitted it from its dependency array, failing CI's react-hooks/exhaustive-deps gate. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
…onError re-render loop (#10454) * fix(web-shell): report each connection error once to stop the onError re-render loop While a connection error persists (e.g. the daemon is unreachable), the error-notification effect re-fires whenever the onError callback identity changes. Hosts such as the VS Code embedded app pass an inline onError and update their own state when it fires, so every notification triggers a host re-render that hands the effect a fresh callback identity — re-notifying the same persistent error forever (#10406). Track the last reported connection.error value in a ref and notify only when the value changes, resetting the tracker once the connection recovers. This guards every inline-callback consumer, not just memoized hosts. Fixes #10406 Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(web-shell): only stamp the dedup ref once an onError handler exists Stamping lastReportedConnectionErrorRef before delivery meant a host that attaches onError after a persistent connection error appeared never received it: the no-op delivery already marked the error as reported. Guard on the handler first and add a regression test covering the late-attach case (red when the guard is removed). Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * docs(web-shell): document the onError dedup contract and fix comment wording Describe the reported-once-per-distinct-error semantics, the reset on recovery, and that replacing the handler mid-error does not re-deliver. Reword the effect and test comments to describe the host class instead of naming the VS Code embedded app, which passes a useCallback handler. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(vscode): mirror the web-shell value-dedup in the EmbeddedApp mock The WebShellWithProviders mock re-notified on every onError identity change, mirroring the loop App.tsx can no longer produce. Rewrite it to report each distinct error value once (resetting on recovery), keep the loop guard as a regression tripwire, exercise it with a changing callback identity plus a post-delivery effect re-run, and refresh the handleShellError comment that still cited the old loop as the memoization reason. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(vscode): cast the captured onError prop for the mock wrapper CapturedProps is an unknown index signature, so the destructured onError needs the same cast the previous mock applied inline to stay callable under tsc. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(vscode): bail the EmbeddedApp mock before stamping when no onError exists The mirrored dedup effect stamped lastReportedError and counted a notification even when no handler was attached, while App.tsx returns before stamping on that path. Add the same early return so a handler attached mid-error still receives the persistent error, and pin the no-handler no-stamp behavior with a test that fails if the guard is removed. 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>
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>
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>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
…into codex/remove-webui # Conflicts: # package-lock.json # packages/vscode-ide-companion/package.json # packages/webui/package.json # scripts/build.js
yiliang114
changed the base branch from
codex/vscode-web-shell-cutover
to
main
September 3, 2026 06:36
yiliang114
dismissed
qwen-code-ci-bot’s stale review
September 3, 2026 06:36
The base branch was changed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this PR does
This stacked draft implements the physical retirement of the legacy
@qwen-code/webuiworkspace package.packages/webuiand removes its build, release, visual-test, lint, workspace, and lockfile references.webui_client identifier prefix for persisted-session compatibility.This PR is stacked on #9811, which owns the VS Code adapter cutover. It reuses the hardened document-only export work from #9641 without pulling the VS Code adapter into that already-large PR.
Why it's needed
After #9811, the remaining package was still kept alive by exported HTML, release scripts, CI, documentation, fixtures, and the lockfile. Removing only the chat components would leave a dead workspace package and two export renderers. This PR removes the entire legacy package and leaves Web Shell as the single shared transcript UI implementation.
Reviewer Test Plan
How to verify
@qwen-code/webui, and the repository contains nopackages/webuiworkspace.Evidence (Before & After)
Pending the final consolidated verification pass. No intermediate VS Code launches or screenshot passes are being performed while the implementation is still a draft.
Tested on
Risk & Scope
packages/desktop/apps/webuiis a separately named desktop application and is not deleted; the user-facingqwen --webuicommand remains because it launches Web Shell.webui_client identifier prefix is intentionally retained as wire/storage data, even though active code and documentation now use Web Shell terminology.Linked Issues
Part of #5883. Stacked on #9811. Reuses the document-only export boundary from #9641.