100 Commits
Author SHA1 Message Date
ytahdnand钉萁 255d5cd2ae feat(web-shell): polish composer action menu presentation (#16181)
* feat(web-shell): polish composer action menu presentation

* style(web-shell): use regular composer menu labels

* fix(web-shell): address composer menu review feedback

---------

Co-authored-by: 钉萁 <[email protected]>
2026-11-06 06:47:28 +00:00
ytahdnand钉萁 76ab3ad4d6 fix(web-shell): avoid composer focus after touch navigation (#16198)
* fix(web-shell): avoid composer focus after touch navigation

* fix(web-shell): preserve newer focus owners during session load

---------

Co-authored-by: 钉萁 <[email protected]>
2026-11-03 19:26:34 +08:00
ytahdnand钉萁 3a4c7dd0e9 fix(web-shell): handle image URL pastes and unavailable files (#16179)
* fix(web-shell): preserve images pasted with source URLs

Keep editable office text first and warn when advertised clipboard files are unavailable.

* fix(web-shell): keep command URL pastes as text

---------

Co-authored-by: 钉萁 <[email protected]>
2026-11-03 15:14:12 +08:00
ytahdnand钉萁 8de81eb209 fix(web-shell): tighten mobile composer layout (#16103)
Co-authored-by: 钉萁 <[email protected]>
2026-11-02 14:59:09 +08:00
591ebc5740 fix(web-shell): pause requests offline and recover automatically (#14980)
* fix(web-shell): pause daemon traffic and recover automatically

* fix(web-shell): use service wording in connection messages

* fix(web-shell): recover loading retries through health checks

* test(web-shell): align mock health response with service contract

* fix(build): account for recovery transcript renderer size

* fix(web-shell): preserve recovery state and offline feedback

* fix(web-shell): address daemon recovery review findings (#14980)

Co-authored-by: Qwen-Coder <[email protected]>

* fix(web-shell): address daemon recovery review findings (#14980)

Co-authored-by: Qwen-Coder <[email protected]>

* fix(web-shell): address daemon recovery review findings (#14980)

* fix(web-shell): keep local slash commands usable during an outage (#14980)

Co-authored-by: Qwen-Coder <[email protected]>

---------

Co-authored-by: 钉萁 <[email protected]>
Co-authored-by: Qwen Autofix <[email protected]>
Co-authored-by: Qwen-Coder <[email protected]>
2026-10-29 11:02:10 +08:00
ytahdnand钉萁 6f53133d6d feat(web-shell): stream resumable BTW answers (#15709)
Co-authored-by: 钉萁 <[email protected]>
2026-10-27 12:19:00 +00:00
ytahdnand钉萁 9df10f873b fix(web-shell): hide turn navigation without sufficient gutters (#15604)
* fix(web-shell): hide turn navigation without sufficient gutters

* test(web-shell): cover navigation visibility across desktop layouts

---------

Co-authored-by: 钉萁 <[email protected]>
2026-10-26 17:17:17 +08:00
ytahdnand钉萁 1ce1731171 fix(web-shell): match cancellation duration to turn summary (#15615)
Co-authored-by: 钉萁 <[email protected]>
2026-10-26 15:50:13 +08:00
ytahdnand钉萁 958410f5d2 feat(web-shell): add Markdown copy and refine reply actions (#15345)
* feat(web-shell): add Markdown copy and keep latest reply actions visible

* feat(web-shell): refine reply action tooltips and icons

---------

Co-authored-by: 钉萁 <[email protected]>
2026-10-26 14:18:51 +08:00
ytahdnand钉萁 06dc528555 fix(web-shell): honor loading phrase visibility setting (#15337)
Co-authored-by: 钉萁 <[email protected]>
2026-10-23 16:11:51 +08:00
ytahdnand钉萁 69df02bb9a fix(web-shell): preserve draft attachments through refresh and allocation (#15303)
Fix two correctness issues left by PR #15025 after its merge into main:
Managed summary reloads could delete unsent attachment archives, and
session allocation could retain an already accepted draft after removing
a submitted attachment or normalizing touch references.

Keep same-session capability knowledge during reload, reuse the existing
removal-tolerant acceptance predicate for migration, and preserve reference
identity when there is nothing to hoist. Keep strict host continuation
checks and protection for newer input.

Add focused regression coverage and synchronize the bilingual design.

Co-authored-by: 钉萁 <[email protected]>
2026-10-23 14:13:03 +08:00
ytahdnand钉萁 c5f317548b fix(web-shell): prevent artifact card overlap on narrow screens (#15300)
Co-authored-by: 钉萁 <[email protected]>
2026-10-23 03:51:17 +00:00
ytahdnand钉萁 f18decc7cc fix(web-shell): preserve composer draft attachments across views (#15025)
* fix(web-shell): preserve composer draft attachments across views

* fix(web-shell): preserve draft edits during restore and acceptance

* test: stabilize durable commit and standalone runtime checks

* revert: drop unrelated core extension test change

---------

Co-authored-by: 钉萁 <[email protected]>
2026-10-23 10:06:10 +08:00
ytahdnand钉萁 ba0813ba93 fix(web-shell): avoid page locks in table controls (#15179)
Co-authored-by: 钉萁 <[email protected]>
2026-10-22 17:42:52 +08:00
ytahdnand钉萁 952c6de739 fix(core): remove duplicate prompt metadata import in chat test (#15146)
Co-authored-by: 钉萁 <[email protected]>
2026-10-22 11:25:23 +08:00
ytahdnand钉萁 257b9f5413 fix(core): avoid false recovery after directory changes (#15012)
Co-authored-by: 钉萁 <[email protected]>
2026-10-22 10:38:24 +08:00
ytahdnand钉萁 a0e4149455 fix(web-shell): preserve reading pause across SSE reconnects (#15009)
Co-authored-by: 钉萁 <[email protected]>
2026-10-21 09:53:47 +00:00
ytahdnand钉萁 4b136b527f fix(core): preserve compressed context during recovery (#14702)
* fix(core): preserve compressed context during recovery

* fix(core): address compression recovery review findings

---------

Co-authored-by: 钉萁 <[email protected]>
2026-10-21 07:49:23 +08:00
ytahdnand钉萁 b08e41b25e feat(web-shell): preview DOCX files across file entry points (#14868)
Co-authored-by: 钉萁 <[email protected]>
2026-10-20 22:40:46 +08:00
ytahdnand钉萁 dc8b523d39 feat(web-shell): show background shell task runtime details (#14831)
* feat(web-shell): show background shell task runtime details

* fix(build): budget shared shell task runtime labels

* test(serve): sync shell runtime telemetry route counts

---------

Co-authored-by: 钉萁 <[email protected]>
2026-10-20 16:18:17 +08:00
ytahdnand钉萁 9c59535db6 feat(web-shell): improve token usage hierarchy (#14648)
* feat(web-shell): improve token usage hierarchy

* fix(web-shell): exclude token usage copy from transcripts

* fix(web-shell): refine token usage tooltips and coverage

* test(web-shell): use artifact-safe token screenshot names

---------

Co-authored-by: 钉萁 <[email protected]>
2026-10-20 16:00:51 +08:00
ytahdnand钉萁 f78c9700bc feat(web-shell): preview PDFs with the native browser viewer (#14825)
Co-authored-by: 钉萁 <[email protected]>
2026-10-20 14:03:11 +08:00
ytahdnand钉萁 158ff26aeb feat(web-shell): Preview collapsed Home content on hover (#14826)
Co-authored-by: 钉萁 <[email protected]>
2026-10-20 13:49:07 +08:00
6ec1b83044 feat(web-shell): delete selected message groups from session history (#14297)
* feat(web-shell): add grouped message deletion with durable history updates

* fix(web-shell): repair message deletion CI contracts

* docs(serve): index message deletion protocol routes

* fix(web-shell): harden grouped deletion recovery and export build

* fix(web-shell): allow selecting all message groups for deletion

* test(cli): observe hook cancellation rejection before waiting

---------

Co-authored-by: 钉萁 <[email protected]>
Co-authored-by: qwen-code-dev-bot <[email protected]>
2026-10-20 13:06:23 +08:00
8906b3d827 feat(web-shell): Polish advanced table controls and copying (#14707)
Co-authored-by: 钉萁 <[email protected]>
Co-authored-by: qwen-code-dev-bot <[email protected]>
2026-10-20 10:35:29 +08:00
ytahdnand钉萁 c7b2d6b685 test(web-shell): remove fixed transcript bundle size ceiling (#14703)
Co-authored-by: 钉萁 <[email protected]>
2026-10-19 20:36:40 +08:00
c4e8831dff feat(web-shell): add approval mode allowlist (#14345)
* feat(web-shell): add approval mode allowlist

* fix(web-shell): complete allowlist i18n and test mocks

* fix(web-shell): harden approval mode allowlist per review

- Restrict ui.allowedApprovalModes to operator scopes: a repository's
  workspace settings can no longer remove an operator's mode
  restriction, and workspace-scope writes are refused.
- Report when a valid-but-hidden mode is selected instead of a silent
  no-op, in both App and ChatPane, and pin the plan exemption.
- Close an open mode dropdown when the offered list becomes empty.
- Stabilize the host allowedApprovalModes array so inline literals do
  not churn the composer and customization memos.
- Share the cycle step as nextApprovalMode in planMode.ts so the App
  and ChatPane copies cannot drift.
- Complete DaemonSettingDescriptor fixtures with the required default
  field and scope the README live-refresh claim to host Prop changes.

* fix(web-shell): fail closed on approval-mode read errors and pin review follow-ups

* fix(web-shell): scope the approval-mode focus handoff and gate its settings read

Round-4 review follow-ups:

- The focus handoff matched the shared toolbar-popover attribute on the
  document-root active element, so it could steal focus from the model
  dropdown or another editor instance. It now fires only for this
  instance's own mode control, via a dedicated data-web-shell-mode-popover
  marker plus toolbar containment, and only once the control is gone.
- The trigger case never fired in a real browser because a focused button
  blurs to the body the moment it becomes disabled. Focus ownership is now
  recorded by the control's wrapper, so the handoff works after that loss
  without an ungated body check that would steal focus on mount.
- The handoff now also covers a narrowing list unmounting the focused row
  while the picker stays open, and its disabled-state fallback skips
  disabled targets, falling back to the enabled composer.
- The allowlist read is gated on the daemon advertising the
  workspace-qualified settings features, so an older daemon keeps the
  default list instead of failing closed for the whole session; a
  reconnect retries a failed read.
- The approval dialog re-anchors the highlight by row identity, so a
  same-length list swap cannot leave Enter committing a row the user
  never highlighted.
- The e2e stub answers the scoped and primary routes with different lists
  so applying the wrong workspace's read fails, and a new e2e empties the
  allowlist under an open picker to pin the browser-level handoff.
- The design record (both languages) now documents the capability gate
  and the fail-closed first-read outcome.

* fix(web-shell): repair approval-mode focus handoff and dialog highlight re-anchor (#14345)

Co-authored-by: Qwen-Coder <[email protected]>

---------

Co-authored-by: 钉萁 <[email protected]>
Co-authored-by: qwen-code-autofix <[email protected]>
Co-authored-by: qwen-code-dev-bot <[email protected]>
Co-authored-by: Qwen-Coder <[email protected]>
2026-10-19 10:48:45 +08:00
a8eb74fdf5 fix(web-shell): polish files and trim preview resources (#14143)
* fix(web-shell): polish files and trim preview resources

* test(cli): observe pending child Hook promises

* fix(web-shell): address Files browser review findings

* fix(vscode): sync Pierre modification notices

* chore: ignore the repo-local .pnpm-store fallback directory

pnpm falls back to a project-local store at .pnpm-store/ when the global
store directory is not writable (e.g. sandboxed CI runners). It is a
regenerable cache that node_modules hard-links into; its thousands of
untracked files otherwise dirty the working tree and fail deterministic
workspace-clean verification.

Co-authored-by: Qwen-Coder <[email protected]>

* fix(web-shell): address Pierre preview and file display review findings

- getPreviewLanguage folds case on the extension only, so README.MD and
  config.YML highlight while whole-name detector keys (Gemfile, BUILD)
  keep their case-sensitive behavior
- formatFileName strips the full Default_Ignorable_Code_Point class so
  zero-advance Hangul fillers and variation selectors cannot desync the
  tree search projection from the rendered name
- pin the previously unwitnessed pierre-build guards: redirected Shiki
  subpaths count toward the 4 MiB budget, getModuleInfo may return null
  for redirected ids, emitted assets are skipped, and entry-module
  resolveId calls carry no importer
- pin the theme name->module mapping with exact token colors and give
  the formatFileName display sites discriminating coverage with
  repeated-space names

---------

Co-authored-by: 钉萁 <[email protected]>
Co-authored-by: qwen-code[bot] <qwen-code[bot]@users.noreply.github.com>
Co-authored-by: Qwen-Coder <[email protected]>
Co-authored-by: qwen-code-dev-bot <[email protected]>
2026-10-16 16:45:56 +08:00
87dcc4a708 feat(web-shell): add file-tree deletion with confirmation (#14158)
* feat(web-shell): add confirmed file deletion from file tree

* test(serve): include file deletion in capability baseline

* fix(web-shell): address file deletion review suggestions

* test(cli): synchronize child cancellation tests

* test(web-shell): refresh transcript budget after file actions

---------

Co-authored-by: 钉萁 <[email protected]>
Co-authored-by: Shaojin Wen <[email protected]>
2026-10-16 14:41:34 +08:00
ytahdnand钉萁 4fda3367b6 feat(web-shell): open workspace files from the chat header (#14344)
Co-authored-by: 钉萁 <[email protected]>
2026-10-16 13:58:08 +08:00
17d5f774ea feat(web-shell): add file creation and confirmed folder deletion (#14169)
* feat(web-shell): add file creation and confirmed folder deletion

* fix(web-shell): address file tree review and transcript regression

---------

Co-authored-by: 钉萁 <[email protected]>
Co-authored-by: Shaojin Wen <[email protected]>
Co-authored-by: 易良 <[email protected]>
2026-10-16 02:43:59 +00:00
ytahdnand钉萁 cebf5719bc feat(web-shell): upload files by dropping into the file tree (#14145)
* feat(web-shell): upload files by dropping into the file tree

* test(serve): allow hosted replay setup to settle under CI load

* fix(web-shell): address file tree upload review feedback

---------

Co-authored-by: 钉萁 <[email protected]>
2026-10-15 07:51:49 +00:00
ytahdnand钉萁 a0e6678d66 feat(web-shell): show a reduced header on the new-task page (#14159)
* feat(web-shell): show a reduced header on the new-task page

The new-task page rendered no header at all. Show a reduced one with
only workspace/daemon-scoped entries (right-panel toggle, Local Control
QR, workspace location); the workspace icon stays hidden because the
composer below already names the workspace. The right panel offers only
workspace-scoped entries (files, terminal, web preview) before a
session exists, and its state on the new-task page now persists under a
per-workspace draft key, surviving reloads and session switches, and
migrates onto the session the first prompt creates.

* fix(web-shell): preserve new-task panel ownership and migration

---------

Co-authored-by: 钉萁 <[email protected]>
2026-10-15 07:25:44 +00:00
ytahdn ec37cec0f3 feat(web-shell): pulse the header environment entry while tasks run (#14048)
The environment panel's header entry gave no hint that a subagent or background task was still running inside it, so the only way to find out was to open the panel and look.

Light a breathing dot on that entry whenever the panel holds an in-progress task, reusing the sidebar's existing background-running indicator so one running task reads the same in both places. The dot keys off the same status predicate the panel's own spinner uses and respects the host's section configuration, so it cannot promise a row the panel would show as settled.
2026-10-13 14:56:02 +00:00
ytahdnand钉萁 ce6361268b fix(web-shell): Ignore standalone whitespace assistant chunks (#14031)
Co-authored-by: 钉萁 <[email protected]>
2026-10-13 11:07:03 +00:00
ytahdnand钉萁 df5470e430 feat(web-shell): Add workspace file browser and previews (#13878)
* feat(web-shell): Add workspace file browser and previews

* fix(web-shell): Restore source preview CI fixtures and notices

* fix(web-shell): Address file pane review blockers

* fix(web-shell): Resolve review regressions and strengthen file checks

---------

Co-authored-by: 钉萁 <[email protected]>
2026-10-13 09:38:47 +00:00
ef38ad7753 feat(web-shell): add read-only Excel artifact previews (#13664)
* feat(web-shell): add read-only Excel artifact previews

* fix(vscode): refresh notices for Excel preview dependencies

* fix(web-shell): bound Excel merge loading and preserve preview content

* fix(web-shell): bound Excel preview work and preserve recovery

* fix(web-shell): harden Excel preview values and recovery

* fix(web-shell): include bundled ExcelJS dependency licenses

* fix(web-shell): guard column expansion and complete ExcelJS notices

* fix(web-shell): complete Excel color fallback and review guards

* test(web-shell): follow artifact filename labels in Excel previews

---------

Co-authored-by: 钉萁 <[email protected]>
Co-authored-by: qwen-code-dev-bot <[email protected]>
2026-10-13 02:43:10 +00:00
ytahdnand钉萁 364593cb25 feat(web-shell): add action reminders and in-panel notification access (#13832)
* feat(web-shell): notify for approvals and user questions

* fix(web-shell): isolate notification help and authorization attempts

* fix(web-shell): preserve notification authorization and keyboard focus

---------

Co-authored-by: 钉萁 <[email protected]>
2026-10-12 10:51:11 +00:00
ytahdnand钉萁 f3f5e005a3 feat(web-shell): support copying selected reply content (#13833)
* feat(web-shell): support copying selected reply content

* fix(web-shell): preserve selected reply data and menu lifecycle

* fix(web-shell): keep selection copying faithful to visible content

---------

Co-authored-by: 钉萁 <[email protected]>
2026-10-12 09:43:56 +00:00
ytahdnand钉萁 e9b87acd73 fix(web-shell): localize and dismiss voice feedback (#13938)
* fix(web-shell): localize and dismiss voice feedback

* fix(web-shell): exclude dictation messages from transcript bundle

---------

Co-authored-by: 钉萁 <[email protected]>
2026-10-12 07:50:44 +00:00
ytahdnand钉萁 7aebfb8d4d fix(web-shell): collapse background results with turn steps (#13825)
Co-authored-by: 钉萁 <[email protected]>
2026-10-10 10:10:11 +00:00
ytahdnand钉萁 7e3de59d3a feat(core): record prompt execution context (#13712)
Co-authored-by: 钉萁 <[email protected]>
2026-10-10 01:52:45 +00:00
ytahdnand钉萁 5ddd43815b fix(web-shell): allow downloading changed workspace artifacts (#13718)
Co-authored-by: 钉萁 <[email protected]>
2026-10-09 06:22:35 +00:00
ytahdnand钉萁 82ac13cc0e fix(web-shell): preserve artifact cards during reconnect (#13714)
Co-authored-by: 钉萁 <[email protected]>
2026-10-09 06:18:07 +00:00
ytahdnand钉萁 95f6aa9c0f fix(acp): include stored attachment paths in model context (#13668)
Co-authored-by: 钉萁 <[email protected]>
2026-10-08 11:35:28 +00:00
f2e069061a feat(web-shell): add adaptive navigation rail and unified Live settings (#12943)
* feat(web-shell): add adaptive navigation rail and unified Live settings

* style(web-shell): add space below the Home action section

* Revert "style(web-shell): add space below the Home action section"

This reverts commit a2e1c76223.

* fix(web-shell): Preserve navigation and Live drafts across refreshes

* fix(web-shell): keep rail navigation state, drag widths, and Live settings drafts consistent

Address the Critical findings from the latest review rounds:

- Build the standalone shell sidebar config from the sidebar's exported
  defaults so the Agents entry can no longer silently drop out.
- Gate rail navigation's persisted uncollapse behind restoresColumn so
  opening a full page (Settings, Plugins, ...) no longer rewrites the
  user's collapse preference; only Home/Channels/Live restore the column.
- Reset the sidebar section when a panel closes and stop leaking 'split'
  as a sidebar page, so a wide split keeps the home column and a working
  collapse control.
- Clamp drag widths to the container-aware cap the CSS already renders,
  floor the visual drag at the layout minimum, and restore the persisted
  width from state instead of a mid-animation/container-capped rect.
- Cap the compact drawer's min-width at the container width.
- Keep the Live voice trigger reachable from the Live page header when
  the sidebar's slot is hidden.
- Settle Live settings draft fields (and their conflict baselines) that a
  refresh converges onto, so a later edit is not blocked by a stale
  baseline.

* fix(web-shell): keep rail panel sections, status dots, and Live drafts consistent

Address review findings on the navigation-rail change: retain the
Channels/Live sidebar section when its panel closes (an explicit Home
choice sticks on the first click), load secondary-workspace attention
status whenever the Home column is hidden (idling the poll when nothing
runs), ignore zero-displacement presses on the resize handle, settle
padded Live settings drafts on trimmed refresh convergence, disable the
shortcut capture until status loads, key the empty-chat welcome chrome
off the shell container signal, and document /live in the pre-auth path
lists.

* fix(web-shell): close rail-era gaps in shortcut, dock, and status handling

Address the round-5 review findings on the navigation-rail change. The
Cmd/Ctrl+B handler resolved its target with e.target, which shadow-DOM
retargeting replaces with the portal host, so the editable-target
exemption never fired for portaled dialogs; it now reads
composedPath()[0] like its siblings. The same handler toggled the raw
sidebarCollapsed state, silently inverting the persisted preference in
the split-view fold band where the sidebar renders folded regardless;
the shortcut, the sidebar prop, and the environment-panel dock budget
now share one effective-collapsed expression. The rail's 56px is
excluded from the dock budget so the environment panel keeps docking at
1440px windows instead of being force-closed on load, with the e2e step
re-pinned at 1440 and the consequence recorded in the sidebar docs. The
secondary-workspace status queries now stay active while the Live
section column is open (the rail Home button is the only status surface
there), and the rail branch resets the project search when Live unmounts
its autoFocus field so a Home round-trip cannot steal focus. The Live
Voice card gates key removal and the required marker on the saved key
state, so a model edit leaving a route can no longer offer removal of a
key that was never stored.

* fix(web-shell): stop silent collapse-preference and drag-width rewrites

Address the round-5 review findings on the navigation-rail change, plus
the residual direction of the round-4 fold-band fix:

- The Cmd/Ctrl+B toggle computed from the effective folded state is a
  constant expansion in the split-view fold band; it now rewrites the
  unchanged preference there instead of flipping a stored `true` to
  `false` on a keypress that changes nothing on screen.
- The rail's column restore fires `onCollapsedChange(false)` only when
  the sidebar is actually collapsed, so a drawer tap on Channels/Live no
  longer silently clears the desktop preference from the forced-open
  drawer.
- The Channels rail entry restores the column only when the Channels
  column exists (`channelNavigationEnabled`), matching openNavigation's
  rule for full-page entries.
- The App's rail-Home callback drops its redundant preference write; the
  sidebar's openNavigation already owns column restoration.
- A drag released at or past the container cap while the cap already
  bound the start width no longer persists the capped width over a wider
  stored preference, and releasing at the drag start restores the
  pre-drag state width as well.
- The Live settings settle effect now runs only on a genuine status
  change, so typing a value that briefly trims onto the saved one no
  longer loses the just-typed characters.

Each behaviour is pinned by a unit case that goes red without its fix.

* fix(web-shell): settle Live drafts only on genuinely changed values

Follow-up to the round-5 settle fix: the daemon SDK returns a fresh
status object per fetch, so keying the settle effect on object identity
still let a 1s install poll or focus refresh with unchanged data
converge a padded draft the user was typing. Compare the settle-relevant
saved values against the previous status instead: only a refresh that
actually moved a saved value settles, while draft-only re-renders and
unchanged polls leave the in-progress text alone. Pinned by `does not
settle a padded draft when a poll returns unchanged values`, red against
the identity comparison and green with value comparison; the convergence
cases stay green.

* fix(web-shell): keep capped drags window-scoped and drop detached rail hints

Address the two round-6 Critical findings:

- The container-cap guard added last round only covered a release past the
  cap. While the cap binds, the rendered edge cannot follow the pointer, so
  the rect-based delta is container-scoped in both directions: a 2px
  leftward jitter stored 298 over a 420 preference, and a later host
  widening rendered the narrow value. Map the pointer delta onto the state
  width whenever the cap bound at drag start, so persistence stays
  window-scoped (418), while the past-cap branch keeps priority and an
  uncapped drag still persists what the user saw.
- navigationHint held a live rail button and was only cleared by DOM
  handlers on the nav. Removed elements fire neither pointerout nor blur,
  so an unmounted rail - or just an unmounted hinted button, when its
  feature gate flips - left the tooltip Popover open against a detached
  node, painting the previous icon label near the viewport origin when the
  rail returned. Clear the hint when the rail is gone or its anchor is
  detached; the check runs every render because a dependency list cannot
  observe a child unmounting while both deps stay unchanged.

Both are pinned by cases that go red without their fix: a leftward jitter
stores 418 (298 without the mapping, with the two neighbouring width cases
still green), and a rail tooltip does not survive a rail -> single -> rail
round trip.

* fix(web-shell): re-baseline hidden-surface and failed-save trackers

Address the two round-7 Critical findings:

- The secondary-workspace completion tracker only re-baselined while the
  status surface was hidden, leaving a frozen running snapshot across a
  visible Home period. Comparing the next refetch against it repainted a
  completion the user watched happen as an unread one. The visible path now
  nulls the per-source baseline, so the first hidden cycle after a visible
  period is baseline-only: only completions actually observed while hidden
  produce a dot. The always-hidden sibling path (and its existing test in
  WebShellSidebar.collapse-persist.test.tsx) is untouched.
- A save rejected after the daemon persisted some of its writes (its
  settings writes land before setEnabled, which rolls back only `enabled`)
  was reported as settings.liveSetup.conflict: the stale apiKey baseline
  compared against the refreshed keyConfigured/storedKey flipped by the
  card's own request. The rejected save now queues a re-baseline of the
  request keys and refreshes, and conflict with those keys is suspended
  until the refreshed status lands and re-anchors them (state, not a ref,
  so the re-baseline provokes the render that clears it).

Both pinned by cases that go red without the fix: no completed badge after
a plugins -> home -> plugins round trip while the sibling always-hidden
case stays green, and no conflict plus a retryable Save after a partially
landed save.

* fix(web-shell): stop certifying unfreshed secondary workspace status

Address the round-8 Critical (body-level, independently confirmed by
qqqys): the secondary catalog page is loaded and polled only while the
Home column is hidden, but the rail Home button folded its retained
snapshots into the indicator unconditionally. An approval resolved
elsewhere kept painting the amber dot and the "Waiting for approval"
accessible name until the user hid the Home column again — two render
sites for one stale certification, and a regression over the pre-PR
surface, which could only render the dot from a live, fetchable path.

build the rail indicator from the primary page plus only those secondary
sessions whose queries are currently armed. While the Home column is
visible the secondary contribution is dropped entirely; hiding the Home
column re-arms the queries and the still-waiting state comes back. On
live-state daemons the secondary catalog path is unloaded regardless, so
nothing live is hidden by this gate. Pinned by a plugins -> home ->
plugins round trip asserting both the dot attribute and the accessible
name, red without the gate while the always-hidden attention case stays
green.

* fix(web-shell): gate the completion tracker baseline on page freshness

Address round-9 Critical R9-1: nulling the secondary baseline on the
visible path closes only half the leak. On re-entering the hidden
surface the tracker installed the store's retained, never-refetched
catalog page as the new baseline (the guard rejected only loading and
error, never a stale page), so the poll arriving seconds later was
diffed against a snapshot taken before the visible window and a
completion the user watched was repainted as unread — exactly what the
visible-path null was supposed to prevent.

Stamp the moment the surface becomes hidden and refuse to install or
compare any secondary snapshot that is not provably fresh
(`(snapshot.updatedAt ?? 0) < hiddenSince`, an unstamped page counting
as not-fresh). The first after-hidden poll then installs the baseline
from truly fresh data. The strengthened round-trip case drives retained
running -> stale re-entry -> fresh idle; it is red without the gate
(the completion badge and the Finished accessible name both appear) and
green with it, while the always-hidden completion case stays green.

* fix(web-shell): clamp every collapse entrance and clear More on rail teardown

Address round-9 Criticals R9-2 and R9-3 (body-level):

- The split-view fold-band clamp lived only at the Cmd+B entrance; the
  rail's Collapse/Expand button reached handleSidebarCollapsedChange
  unclamped, so a click that visibly does nothing still overwrote the
  stored preference and a stored `true` could never be re-written inside
  the band. The clamp now lives once in the shared writer: in the fold
  band it re-stores the current raw preference regardless of which value
  was requested. (`sidebarCollapsed` and `splitFoldedSidebar` reach the
  stable []-callback through refs — the former would close over the
  mount-time value and the latter is declared later, TDZ.) The Cmd+B
  site shrinks back to passing the toggle intent. Pinned by a third arm
  in the fold-band App test that clicks the collapse toggle at 1100px
  and finds the stored `true` untouched — red with the clamp removed.
- The More reset effect did not watch layout, so a rail -> single ->
  rail round trip left moreOpen=true against an unmounted owner and the
  rail returned with a phantom open menu (content in the document,
  focus inside, never reopened). `layout` joins the dependency list and
  a round-trip case in WebShellSidebar.collapse-persist.test.tsx goes
  red without it.

* fix(web-shell): let exit-split navigation restore the column under the fold clamp

Address maintainer P2 inline: after the shared-writer clamp landed, a
Home click from the split-view fold band was still intercepted — the
rail's restoresColumn `onCollapsedChange(false)` wrote the folded raw
value back (correct for taps that stay in split view), and `onOpenHome`
then closed split view with the raw preference unchanged, leaving the
Home column folded until a second click.

Split the intent at the shared writer: `exitFoldBand` marks entrances
that reorganize the layout away from split view, bypassing preservation,
and the rail Home handler uses it when the fold band was the overriding
state at click time. Taps that stay in the fold (Cmd/Ctrl+B, the rail
Collapse/Expand button) keep their preference protection untouched, and
the write still never happens inside the mobile drawer, where
splitFoldedSidebar is forced false by construction — so the drawer case
is unaffected (drawer protection is preserved).

Pinned by a fourth arm in `does not invert the stored collapse
preference in the split-view fold band`: stored `true` at 1100px split
view, click `[data-testid="open-home"]` once, and the column is expanded
with the stored key `false`; dropping the bypass inverts that arm while
the three protected arms stay green.

* fix(web-shell): cover Channels and Live in the exit-split fold exception

Address maintainer P2 follow-up: the exitFoldBand carve-out applied only
to Home, so Channels and Live — whose `openPanel` call exits split view
through the web-shell page set (`setMainView('chat')`) the same way —
still had their restoresColumn write clamped when the fold band owned
the sidebar, leaving the section column folded until a second rail tap.

Extent the same bypass to those two entrances, with each entry's own
column-existing gate: Channels applies it only when channelSidebarEnabled
would actually produce a column (mirroring the rail's
channelNavigationEnabled rule, which still leaves the full-page channels
variant folded), and the Live handler inherits the liveSidebarEnabled
guard it's already defined under.

Pinned by two parallel cases beside the fold-band keyboard/click arms:
stored `true` at 1100px split view → one rail tap on `[data-testid=
"open-channels"]` (with `session_source_metadata`) and one on
`[data-testid="open-live"]` (with showLive) — each expands the column and
writes `false`; dropping both bypasses turns both cases red while the
protected Home arm stays green.

---------

Co-authored-by: 钉萁 <[email protected]>
Co-authored-by: qwen-code-dev-bot <[email protected]>
Co-authored-by: qwen-code-dev-bot <[email protected]>
Co-authored-by: Shaojin Wen <[email protected]>
2026-10-05 08:13:30 +00:00
8fb6ade2e4 feat(web-shell): inspect session tool calls by prompt (#12466)
* feat(web-shell): present structured shell execution results

* fix(shell): preserve cleanup notices and clarify result fallbacks

* fix(test): sync shell result source mappings

* fix(web-shell): address shell command card review findings (#12311)

- Documents: stop duplicating the command above the fallback text and
  label an empty legacy result as 'No output' (maintainer N1/N2)
- Escape control and bidi characters at every shell-card render site
  while keeping clipboard copies byte-exact
- Render a string rawOutput display instead of model-facing content so
  background-promotion and refusal messages stay visible
- Show a finished command's elapsed time from startTime/endTime
- Suppress the Command section when the legacy envelope already leads
  with the same command, keeping it once per expanded card
- Pin the wasCancelled status, the background timeout gates, the
  synthetic exit-zero headline and the status icons; freeze the
  running-elapsed fixture's clock
- Tag the card smoke test and wait for the SSE connection before
  driving live frames
- Drop the dead .expandedBash selector and align both design docs with
  the undisclosed live-frame transport for line/byte counts

Co-authored-by: Qwen-Coder <[email protected]>

* test(core): isolate gitPush tests from host git config (#12311)

The deterministic verification gate ran the core suite on a persistent
runner whose real ~/.gitconfig carries remote.pushDefault=gd, and the
three gitPush tests that resolve the push remote failed with
'fatal: gd does not appear to be a git repository'. gitEnv strips only
the GIT_CONFIG_* env overrides, so the code under test still honors the
host HOME config — the exact gap the file's hermeticEnv() helper
documents and every gitPull test already guards against.

Pass hermeticEnv() at all six gitPush call sites so host config can no
longer steer push resolution. Reproduced with a poisoned HOME
(remote.pushDefault=gd): the three gate failures appear on the pre-fix
tree and the file is 107/107 green after, in both clean and poisoned
environments.

Co-authored-by: Qwen-Coder <[email protected]>

Co-authored-by: Qwen-Coder <[email protected]>

* fix(core): stage browser-use runtime on demand in core prebuild (#12311)

The deterministic verification gate rebuilds core with a scoped
npm run build --workspace packages/core, but the review-address job
installs with QWEN_SKIP_PREPARE=1 and restores only the root and core
dist artifacts, so packages/browser-use/dist is absent and the core
build dies in copyBrowserUseAssets. The dependency on the built
browser-use runtime was only expressed through the root build's
ordering, leaving every scoped core build (the gate's, or a
developer's on a prepare-skipped install) broken.

Add a prebuild that stages the browser-use runtime only when its
dist/index.js sentinel is missing. The root build keeps building
browser-use first, so the prebuild is a no-op skip there and the
fallback fires exactly when the runtime is absent.

Probe matrix: scoped core build fails without the runtime pre-fix
(the gate rejection, reproduced locally), passes with the runtime
absent (fallback stages it), and passes unchanged with the runtime
present (skip path).

Co-authored-by: Qwen-Coder <[email protected]>

* fix(web-shell): treat empty string shell display as authoritative (#12311)

A persisted empty display string from a silent command is falsy, so the
card fell back to the model-facing envelope in content and the
legacyOutputRepeatsCommand guard then suppressed the Command panel,
rendering a bare envelope with no copy action. Gate the string branch on
the type alone so an empty string renders as empty output next to the
command.

* fix(core): preserve shell display and recording diagnostics

* feat(web-shell): add session tool calls panel with prompt selection

* fix(web-shell): address tool calls review findings

* fix(web-shell): keep prompt text out of persisted dock state

* fix(web-shell): restore sender tool call identity and review fixes

* docs(web-shell): record real daemon sender verification

---------

Co-authored-by: 钉萁 <[email protected]>
Co-authored-by: qwen-code-dev-bot <[email protected]>
Co-authored-by: Qwen Autofix <[email protected]>
Co-authored-by: Qwen-Coder <[email protected]>
2026-09-24 08:19:53 +00:00
ytahdnand钉萁 906418aa9b fix(web-shell): make mobile access opt-in and simplify split composer (#12537)
Co-authored-by: 钉萁 <[email protected]>
2026-09-24 01:37:22 +00:00
a64d8decc1 fix(web-shell): preserve reference tags across reloads (#12404)
* fix(web-shell): preserve reference tags across reloads

* fix(acp): validate daemon input annotations at record, replay, and render

Address review findings on the user-message tag replay PR:

- Skip non-object annotation elements when recording (new
  readDaemonInputAnnotations reader), replaying (per-element filter),
  and rendering (null guard in splitComposerTagContentByAnnotations),
  so one malformed element can no longer poison a persisted session on
  every reload.
- Cap caller-supplied annotations at MAX_DAEMON_INPUT_ANNOTATIONS = 256,
  matching the sibling attachment-references bound.
- Export DAEMON_INPUT_ANNOTATIONS_META_KEY from bridgeTypes and route
  the recorder, replay, and live-echo read sites through it so the
  wire/persist key cannot drift silently.
- Restore the deferred /advisor branch's no-payload test pin and split
  its annotation assertions into a dedicated case.
- Document the annotations payload trigger in hooks.md and the
  intentional queued-strip/composer styling scope in both design docs.

* test(cli): make post-index-change hook cases deterministic across filesystem ticks (#12404)

The ungated-status control assumed `git status` always rewrites the
index on a dirty tree, firing the tree-shipped post-index-change hook.
The rewrite only happens when an entry is racily clean, which held only
because beforeEach's add and commit usually land in one filesystem tick;
a loaded runner straddles the tick, status writes nothing, and the
control fails. Force raciness by re-adding README.md with a future-dated
mtime (resetting the canary the re-add's own index write fires), and
share the fixture with the guarded case so its negative assertion is
exercised against a hook that would genuinely fire ungated.

---------

Co-authored-by: 钉萁 <[email protected]>
Co-authored-by: qwen-code-dev-bot <[email protected]>
2026-09-22 06:05:57 +00:00
eda1c1d8b6 feat(web-shell): present structured shell execution results (#12311)
* feat(web-shell): present structured shell execution results

* fix(shell): preserve cleanup notices and clarify result fallbacks

* fix(test): sync shell result source mappings

* fix(web-shell): address shell command card review findings (#12311)

- Documents: stop duplicating the command above the fallback text and
  label an empty legacy result as 'No output' (maintainer N1/N2)
- Escape control and bidi characters at every shell-card render site
  while keeping clipboard copies byte-exact
- Render a string rawOutput display instead of model-facing content so
  background-promotion and refusal messages stay visible
- Show a finished command's elapsed time from startTime/endTime
- Suppress the Command section when the legacy envelope already leads
  with the same command, keeping it once per expanded card
- Pin the wasCancelled status, the background timeout gates, the
  synthetic exit-zero headline and the status icons; freeze the
  running-elapsed fixture's clock
- Tag the card smoke test and wait for the SSE connection before
  driving live frames
- Drop the dead .expandedBash selector and align both design docs with
  the undisclosed live-frame transport for line/byte counts

Co-authored-by: Qwen-Coder <[email protected]>

* test(core): isolate gitPush tests from host git config (#12311)

The deterministic verification gate ran the core suite on a persistent
runner whose real ~/.gitconfig carries remote.pushDefault=gd, and the
three gitPush tests that resolve the push remote failed with
'fatal: gd does not appear to be a git repository'. gitEnv strips only
the GIT_CONFIG_* env overrides, so the code under test still honors the
host HOME config — the exact gap the file's hermeticEnv() helper
documents and every gitPull test already guards against.

Pass hermeticEnv() at all six gitPush call sites so host config can no
longer steer push resolution. Reproduced with a poisoned HOME
(remote.pushDefault=gd): the three gate failures appear on the pre-fix
tree and the file is 107/107 green after, in both clean and poisoned
environments.

Co-authored-by: Qwen-Coder <[email protected]>

Co-authored-by: Qwen-Coder <[email protected]>

* fix(core): stage browser-use runtime on demand in core prebuild (#12311)

The deterministic verification gate rebuilds core with a scoped
npm run build --workspace packages/core, but the review-address job
installs with QWEN_SKIP_PREPARE=1 and restores only the root and core
dist artifacts, so packages/browser-use/dist is absent and the core
build dies in copyBrowserUseAssets. The dependency on the built
browser-use runtime was only expressed through the root build's
ordering, leaving every scoped core build (the gate's, or a
developer's on a prepare-skipped install) broken.

Add a prebuild that stages the browser-use runtime only when its
dist/index.js sentinel is missing. The root build keeps building
browser-use first, so the prebuild is a no-op skip there and the
fallback fires exactly when the runtime is absent.

Probe matrix: scoped core build fails without the runtime pre-fix
(the gate rejection, reproduced locally), passes with the runtime
absent (fallback stages it), and passes unchanged with the runtime
present (skip path).

Co-authored-by: Qwen-Coder <[email protected]>

* fix(web-shell): treat empty string shell display as authoritative (#12311)

A persisted empty display string from a silent command is falsy, so the
card fell back to the model-facing envelope in content and the
legacyOutputRepeatsCommand guard then suppressed the Command panel,
rendering a bare envelope with no copy action. Gate the string branch on
the type alone so an empty string renders as empty output next to the
command.

* fix(core): preserve shell display and recording diagnostics

---------

Co-authored-by: 钉萁 <[email protected]>
Co-authored-by: qwen-code-dev-bot <[email protected]>
Co-authored-by: Qwen Autofix <[email protected]>
Co-authored-by: Qwen-Coder <[email protected]>
2026-09-21 11:32:42 +00:00
ytahdn a28ccc26cc fix(web-shell): stop the header title from showing the new-session placeholder (#12379)
A history session's name lives only in the session catalog: the load
response and the metadata events never carry it, and the session status
route reports the daemon's in-memory entry, which is never hydrated from
disk. The header therefore fell back to `session.new` for the whole load
round-trip, and a turn-start or turn-end refresh could blank a title the
catalog had already resolved — the placeholder flashed back.

Seed the title from the catalog cache as soon as a session becomes active,
and keep resolved titles from being overwritten by an empty status
response.
2026-09-21 07:19:19 +00:00
ytahdnand钉萁 b85264be79 fix(web-shell): avoid unnecessary table horizontal scrolling (#12312)
Co-authored-by: 钉萁 <[email protected]>
2026-09-20 09:43:45 +00:00
ytahdn 99214d5308 feat(web-shell): move the workspace and git chips out of the composer toolbar (#12299)
The workspace selector and the git branch chip both say where a prompt goes,
not how it runs, yet they sat inside the composer toolbar between the "+" menu
and the mode/model controls. In an existing session that was worse than
cosmetic: the workspace selector only ever acts on the next session, so it sat
among controls that act on the current one.

ChatEditor takes a contextChipPlacement prop. While the chat is empty it moves
both chips into their own row under the composer box, so the composer stays
about the prompt; once the session exists the chat header reports the workspace
as a single leading folder icon, with the folder name and full path on hover,
and the composer keeps only git. The header icon uses the same glyph whether or
not a workspace exists -- only the tooltip differs -- because a single icon is
the one place that always answers which workspace the session is in.
2026-09-20 09:19:53 +00:00
ytahdn c3e86ea1bd feat(web-shell): open link artifacts in a new page (#12317)
A recorded link artifact is an entry to a resource rather than a document the
side panel can render, so its turn card now opens the address instead of a
metadata tab: the control becomes an anchor routed through the shell's external
opener, which hands the URL to the OS browser in the packaged desktop window and
falls back to a native new tab in a plain one. Destinations that opener cannot
take, such as a redacted locator, keep the metadata panel, and Artifact-published
deliveries keep the panel's live preview.

The live-preview design doc records the split in both languages, and the e2e case
that pinned the old metadata behaviour now asserts the new one.
2026-09-20 09:08:52 +00:00
8589f71331 feat(web-shell): expose slash-command exports as artifacts (#12050)
* feat(web-shell): expose slash-command exports as artifacts

* docs(web-shell): clarify export preview loading and CSP scope

* fix(web-shell): address export-artifact review findings (#12050)

- Localize the registration-failure toast on the main view: the hook
  mounted from App's own body runs above the I18nProvider App renders,
  so it received the key-passthrough default translator.
- Subscribe the registration hook to the throttled animation-frame
  transcript snapshot instead of the raw per-delta store, restoring the
  shell's input-latency protection during streaming.
- Treat the cross-client session_artifact_forbidden 403 as already
  registered — the daemon only raises it after resolving an existing
  artifact, so a failed catalog resync no longer surfaces a false
  conflict toast; refresh() reverts to Promise<void>.
- Abort the sibling renderer-asset fetch when one arm rejects, and
  localize the rendered-preview loading placeholder.
- Pin the stylesheet arm of the preview allow-list matrix, the
  secondary-workspace preview read window, and restore the DOMParser
  spy the 1 MiB boundary matrix leaked.

* fix(web-shell): address deferred export-artifact review findings (#12050)

- Memoize the built preview document across source/rendered toggles so
  switching views no longer re-fetches and re-encodes the renderer assets.
- Measure the 1 MiB gate with TextEncoder instead of allocating a Blob
  copy of the content, and thread the artifact catalog sizeBytes through
  the detail view so the fallback stops being the common path.
- Pin the producer-to-Web-Shell descriptor wire contract and the renderer
  URL/version lockstep with cross-package integration tests, and pin the
  reader's per-guard rejections, the preview failure branch, and the
  stale-owner registration guard with focused unit tests.

---------

Co-authored-by: 钉萁 <[email protected]>
Co-authored-by: qwen-code-dev-bot <[email protected]>
Co-authored-by: Qwen-Coder <[email protected]>
2026-09-20 02:37:25 +00:00
ytahdn 2d49eff011 feat(web-shell): add per-turn satisfied / not satisfied marks (#12174)
A reader had no way to tell Web Shell that one particular answer was
satisfactory, and a host embedding it had nowhere to hook that signal:
the existing answer-footer extension point renders in its own block
above the action row, so it cannot put controls beside Copy.

Add two mutually exclusive marks to the right of Copy on a completed
turn's final answer, plus an opt-in `assistantFeedback` customization
item that notifies a host about every transition. The host is a
listener only: the mark lands first, and throwing, rejecting, or
cancelling a dialog in the callback never changes it.

The mark is deliberately local — kept in localStorage, not in the
session record — so no daemon route, record type, or transcript
projection is added and the wire stays untouched. It is keyed by the
daemon's promptId, the only value that survives a reload, since a
block id is a per-projection ordinal and a persisted replay stamps the
prompt's block rather than the answer's.

No telemetry: marking emits no UserFeedbackEvent and is not gated by
the usage-statistics setting.
2026-09-18 10:12:27 +00:00
ytahdn 1ef024ebf1 fix(web-shell): keep the mermaid canvas fixed while zooming out (#12177)
Zoom was a transform on the box that also carried the canvas background and
the diagram's own horizontal scrollbar. A transform moves paint, not layout,
so zooming out scaled the scroll viewport along with the diagram and left the
pan range computed from the unscaled width: at half size a 3223px diagram
painted 1612px inside a 489px sliding strip in the middle of a 978px row,
while the scrollable range stayed 2277px and matched nothing on screen.
Panning was a second translate on the same box on top of that — unbounded
horizontally, so dragging past the content origin pushed the leading edge of
the diagram out of the scrollable region, where no scroll brought it back.

Give the three roles their own boxes: the wrapper is the canvas and the only
scroller, the inner box carries nothing but the diagram, scaling uses `zoom`
so layout moves with the paint and the reachable range tracks the diagram
rather than its full-size box, and dragging drives the scroll position instead
of a transform. The canvas takes the diagram's own size, read from its svg:
mermaid marks everything outside flowchart `width="100%"`, so those diagrams
keep shrinking to the canvas width while the rest sit at their design size — a
`fit-content` box cannot resolve that percentage and would fall back to the
300x150 default object size. The canvas holds the diagram's unscaled height so
zooming never reflows the transcript, and it offers the grab cursor only while
part of the diagram is out of view: a diagram that already fits cannot be
moved by dragging, and the cursor should not promise otherwise.
2026-09-18 09:56:24 +00:00
ytahdn a9ff4567f7 feat(web-shell): render the compression outcome from structured meta (#12141)
* feat(web-shell): render the compression outcome from structured meta

The compression commands stream their outcome as English sentences over ACP,
which a host that owns its own UI language cannot render. They now carry the
outcome as machine-readable `_meta` next to the sentence, the transcript record
and replay pass it through, and the web shell renders it in the user's language
with the token counts formatted for that language.

A compression that finds nothing to strip reports a terminal no-op phase rather
than reusing the pending one: the payload-driven row has to state the outcome,
and a pending row is dropped once the turn ends.

The invocation note rides its own `_meta` key rather than sharing the result's.
The daemon UI reducer folds a turn's text frames into one block and spreads
`_meta` key by key, so a shared key would be overwritten by the frame that
follows. Nothing else reads the new keys and no frame gains the
discrete-message marker, so a host that renders text as-is sees the same
sentences it saw before.

Opening a session whose last action was a compression also reconciles the
composer's context counters once. They are seeded from the last usage frame in
the replay, and a compression emits none, so they described the context it
replaced; a read is only written back while the session is live and its model
and context window have settled, which is what the reconcile waits for.

* docs(design): describe the structured compression outcome

The design records why the outcome travels as metadata beside the sentence, why
the invocation note needs its own key, why the no-op is a terminal phase, and
how the composer counters reconcile on an attach that ends on a compression. It
also keeps the compatibility analysis for other clients, which is a constraint
of this change rather than a follow-up.
2026-09-18 07:06:37 +00:00
ytahdnand钉萁 91a868d9c1 feat(web-shell): fold oversized pastes into an attachment card (#12084)
A paste carrying text/plain fell through to the editor and was inserted at any
size, so a long paste became a wall of text the user could neither review nor
edit, and three limits downstream failed with nothing said at paste time: the
daemon rejects a prompt body above 10 MB, a mid-turn insertion carries at most
16,384 characters, and the text-only session draft stops being restorable after
a reload.

A paste of at least 200 lines or 8,000 characters now folds into an attachment
card when its UTF-8 size is within the attachment upload ceiling; a larger paste
stays inline, because an attachment that size could never be sent. The card is
the existing chip, named and titled by the beginning of the paste itself so the
sent message shows the string the card showed, carrying a size and one action
that moves the text into the editor as a single undoable edit. The name keeps
its .txt suffix: the bridge re-derives a stored attachment's type from its name,
so a name without one would resolve the text as an opaque blob.

Exemptions keep today's behavior wherever there is no prompt to attach to: a
disabled composer, shell mode, a host with attachments turned off, a paste that
would replace selected text, and a paste that would leave a `!` or `/` command
leading the draft. A folded paste also outranks a pending follow-up suggestion,
which an empty draft would otherwise submit in its place.

Folding does not reduce what the model sees — an uploaded attachment is resolved
back into prompt text before the turn is built. The gain is in composing and
transport. Design notes and the accepted Risks are recorded in
docs/design/web-shell/web-shell-paste-as-attachment.md.

Co-authored-by: 钉萁 <[email protected]>
2026-09-17 11:03:27 +00:00
ytahdnand钉萁 b17658525f fix(web-shell): restore context usage from persisted replay (#12070)
Co-authored-by: 钉萁 <[email protected]>
2026-09-17 07:35:31 +00:00
ytahdnand钉萁 293a5b8112 fix(core): exclude completed local slash commands from recovery (#12052)
Co-authored-by: 钉萁 <[email protected]>
2026-09-17 05:52:03 +00:00
ytahdnand钉萁 e4755ef6ab feat(web-shell): customize context details and collapse snapshots (#11996)
Co-authored-by: 钉萁 <[email protected]>
2026-09-16 08:01:01 +00:00
ytahdnand钉萁 712874d0f5 feat(web-shell): preview files from expanded tool details (#11982)
Co-authored-by: 钉萁 <[email protected]>
2026-09-16 06:17:07 +00:00
ytahdnand钉萁 5f5f4ea013 fix(web-shell): keep the recap below a turn's outputs and dedupe it (#11986)
The session recap had two triggers that rendered differently: `/recap`
wrote a local anchored message, while the automatic recap (fired when the
user returns after being away) appended a status block to the transcript
store. Two consequences: repeated automatic recaps accumulated instead of
replacing each other, and every recap landed above the "Edited N files"
card a turn produces, because that card is appended after all items.

The automatic recap now writes the same local annotation the manual one
does — one slot, latest request wins — and a turn's outputs card is
placed above a recap that trails the turn. Requests carry the generation
they were issued under, so a slow answer cannot replace a newer recap,
and a superseded request still drops its own "Generating recap"
placeholder. Clearing the screen drops the recap too, on screen or in
flight, which the clear handler previously left to `store.reset()`.

Co-authored-by: 钉萁 <[email protected]>
2026-09-16 06:06:49 +00:00
ytahdnand钉萁 1a980b5846 fix(web-shell): preserve separators in tool description paths (#11977)
Co-authored-by: 钉萁 <[email protected]>
2026-09-16 03:17:43 +00:00
ytahdnand钉萁 e1fde66f19 fix(web-shell): stop the approval sheet resizing while reading (#11930)
* fix(web-shell): stop the approval sheet resizing while reading

Hovering the command or content block released its 3-line clamp. Because
the sheet is an in-flow bottom sheet, the released clamp grew the sheet
and shifted the transcript above it: on a six-line command the sheet went
222.5px -> 281px and its top edge moved up 58.5px.

The block now shows the whole command by default and scrolls past 300px,
so the sheet's height no longer depends on where the pointer is.

The sheet also becomes a flex column with its chrome pinned, because
unclamping let a long command grow the content past the sheet's own
max-height, pushing Allow below the visible edge by 66px. The content
region now absorbs the overflow, so the decision stays on screen at every
viewport.

* fix(web-shell): keep the approval sheet's fallback scroller

Pausing the sheet's own scroller while pinning every chrome child with
flex-shrink: 0 left the command region as the only shrinkable item, with
no floor under it. Once the pinned chrome alone exceeded the sheet's
max-height, the command collapsed to zero height and the decision buttons
were clipped inside a box that wheel, touch and drag could no longer
scroll. Measured at 844x390 with a three-line description, the explain
row and four wrapping options: the command block rendered at 0px, all
four buttons sat below the sheet's visible bottom, and a wheel gesture
left the sheet's scrollTop at 0. The base was scrollable there, so this
was a regression rather than a pre-existing limit.

The flex budget, not the overflow value, is what bounds the sheet, so the
ordinary case is untouched: at 1280x720, 1600x1200 and 1440x900 a 30-line
command leaves the sheet's scrollHeight equal to its clientHeight and
scrolls inside its own block. The sheet's scroller now engages only when
the pinned chrome cannot fit, which is exactly the case that used to
clip, and at 844x390 the command keeps 33px with every button back inside
the sheet.

The command and content regions get a 3.5em floor in place of a zero
minimum so neither can collapse to a sliver; the exit-plan content keeps
its own zero minimum.

---------

Co-authored-by: 钉萁 <[email protected]>
2026-09-16 02:37:46 +00:00
ytahdnand钉萁 3fc1133d44 feat(daemon): support summary projection for prompt and replay APIs (#11920)
Co-authored-by: 钉萁 <[email protected]>
2026-09-15 15:16:38 +00:00
7157cdea7d feat: track background result execution across daemon and web shell (#11636)
* feat: track background result execution across daemon and web shell

* fix(cli): preserve runtime imports during lint normalization

* fix: preserve background execution ownership across clients

* fix: reconcile background completion across waiting and replay

* fix: close background-turn lifecycle gaps from review (#11636)

Four correctness fixes from the round-3 review, each with a
regression test that fails without it:

- Session: race the _qwencode/start_turn admission RPC against the
  mid-turn deadline so a host that silently drops it defers the
  notification (existing retry path) instead of wedging the pipeline.
- bridgeClient: finishBackgroundTurn now cancels the turn's pending
  permission requests via a per-prompt mediator cancel, so an orphaned
  approval resolves cancelled instead of pending for the session's
  life; a live user prompt's approvals are untouched.
- qwen-live adaptor: background-stamped updates no longer adopt the
  session's active job, and background terminals are dropped
  unconditionally, so a background execution is never reported as an
  orchestrator job's completion.
- DaemonSessionProvider: a background turn's terminal no longer
  settles a restored foreground prompt while the daemon live-state
  authority reports the prompt in flight.
- live-task-service: the wait_threads wake-suppression guard fails
  open when the session summary is gone mid-stream, so a delivered
  wake is not swallowed as neither-woke-nor-timed-out.

* test(cli): synchronize output-style dialog test on the selection marker (#11636)

The deterministic verification gate rejected the previous round:
'clears the configured style only after default is selected' timed out
waiting for setOutputStyle(undefined) in the full packages/cli changed
suite.

The test waited only for the 'Concise' row text, which renders in the
catalog commit with the mount-time selection (index 0 = default). The
pre-selection of the active style (index 1) lands in a later
passive-effect commit. Under CI worker load the interleaving up ->
derive(1) -> return picks Concise instead of default, so the expected
undefined call never happens. Wait for the '>' selection marker on the
Concise row instead, the same synchronization the sibling tests use, so
the selection effect has committed before the keys are sent.

* fix: close background-turn attribution and admission gaps from review (#11636)

Round-4 review follow-ups, each pinned by a witness test that fails
without the fix:

- acp-bridge: resolve a prompt's activePromptTerminal record at install
  when its terminal was already published during attachment resolution
  (deadline expiry, teardown flush); otherwise the terminalPublished
  latch suppresses the resolve and a background admission ordering on
  that record hangs.
- acp-bridge: keep the queueOnly rejection while a background turn
  runs — an automatic execution has no coordinator collector, so Live
  steering queued into it is silently consumed without a response.
- qwen-live: background-turn updates no longer stamp the unowned
  background turnId as an activity's jobRef, and a background terminal
  now releases busy state the turn seeded (attach during the turn, or
  a mid-turn drain it consumed) instead of latching isBusy().
- web-shell: a single top-level background agent now shows the
  awaiting-processing badge in its ToolGroup row meta (its SubAgentPanel
  header is hidden), and the sidebar's background-dot action-hide is
  scoped to hover-capable devices so touch rows keep their
  always-visible actions.

* test(acp-bridge): remove duplicate SessionAttachmentStore import (#11636)

The prior round added a second import at the top of bridge.test.ts while
the file already imported the symbol further down, failing tsc --build
with TS2300 and the deterministic build gate. Drop the duplicate; the
surviving import covers all usage sites.

* fix: close background-turn collector contamination and lifecycle gaps from review (#11636)

Co-authored-by: Qwen-Coder <[email protected]>

* test(cli): fix async callback typing in background ALS frame witness (#11636)

runWithAgentContext's callback is typed () => Promise<T>; the witness for
the runOutsideAgentContext drain wrap passed a sync arrow returning void,
which fails tsc --build (TS2322). Make the arrow async and await the call;
the callback body still runs synchronously inside the subagent ALS frame,
so the test exercises the identical path (probe: still red with the
Session.ts guard bypassed, green with it restored).

Co-authored-by: Qwen-Coder <[email protected]>

* fix(daemon): isolate background handoffs and foreground replies

---------

Co-authored-by: 钉萁 <[email protected]>
Co-authored-by: qwen-code-dev-bot <[email protected]>
Co-authored-by: Qwen Autofix <[email protected]>
Co-authored-by: Qwen-Coder <[email protected]>
2026-09-14 22:45:46 +00:00
ytahdnand钉萁 31fd929250 feat(web-shell): restore the workspace Git picker in the sidebar hover details (#11818)
#11644 removed the sidebar's always-visible Git control and left only a
plain-text status summary in the folder hover popover, which also made branch
checkout, pull/push, View Changes and Commit unreachable for any workspace that
was not the selected one. That conflated a request-reduction goal with a
capability loss; the two are separable, and this separates them.

The hover popover's branch row is that workspace's Git entry point again: it
carries a chevron, opens the branch picker as a nested layer, and shows the
branch's severity as a dot beside the name. The picker keeps the details
pinned while it is open and collapses them when it closes, unless the pointer
is still on the row. Rows with no Git wiring keep the plain-text summary.

When Git is read does not change: the polling gate is untouched, and only a
trusted workspace with a real path becomes interactive.

Co-authored-by: 钉萁 <[email protected]>
2026-09-14 07:14:00 +00:00
35a9c5c62d perf(web-shell): load metadata on demand and reuse capability checks (#11644)
* perf(web-shell): load metadata on demand and reuse capability checks

* fix(web-shell): address demand-loading review regressions

* fix(web-shell): address demand-loading review suggestions

Co-authored-by: Qwen-Coder <[email protected]>

* fix(web-shell): pass explicit initial value to git poll target ref

React 19 typings require useRef to receive an initial value; the
zero-argument call failed tsc -p tsconfig.lib.json (TS2554) and
rejected the previous round's build.

Co-authored-by: Qwen-Coder <[email protected]>

* fix(web-shell): restore slash input after pre-admission failure

---------

Co-authored-by: 钉萁 <[email protected]>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Qwen-Coder <[email protected]>
Co-authored-by: Shaojin Wen <[email protected]>
Co-authored-by: qwen-code-dev-bot <[email protected]>
2026-09-13 06:18:28 +00:00
ytahdnand钉萁 9969640815 feat(web-shell): Edit and resend the latest user message (#11650)
Co-authored-by: 钉萁 <[email protected]>
2026-09-11 12:06:38 +00:00
ytahdnand钉萁 53e6196c6b fix(web-shell): persist cancelled responses and elapsed time (#11608)
* fix(web-shell): preserve cancelled responses and elapsed time

* fix(core): retain cancelled text during reactive compression

---------

Co-authored-by: 钉萁 <[email protected]>
2026-09-11 04:30:09 +00:00
ytahdnand钉萁 0b2ba2d2c6 fix(web-shell): load global resets before standalone component styles (#11517)
Co-authored-by: 钉萁 <[email protected]>
2026-09-10 03:09:15 +00:00
ytahdnand钉萁 2c5250daa6 feat(web-shell): keep question answers visible as user-style messages (#11461)
Co-authored-by: 钉萁 <[email protected]>
2026-09-09 12:05:57 +00:00
ytahdnand钉萁 13e11dc29b fix(web-shell): gate subagent details until session is ready (#11434)
Co-authored-by: 钉萁 <[email protected]>
2026-09-09 08:50:26 +00:00
ytahdnand钉萁 20402a65aa fix(web-shell): show and wire composer quick keys on mobile textarea (#11454)
The mobile composer's "more actions" panel hid its keyboard-hint column
entirely on the touch textarea backend, leaving a blank right side and
hint buttons that could never work (no CodeMirror to dispatch into).
Always render the hint grid and apply each key's desktop effect directly
to the textarea: arrows browse prompt history (now touch-capable in
useComposerCore) or move the caret, Esc exits shell mode or cancels the
running turn. Editing after a history walk re-arms draft persistence.

Co-authored-by: 钉萁 <[email protected]>
2026-09-09 08:39:49 +00:00
5df0d99f0e fix(web-shell): make light-theme terminal text selection visible (#11445)
Co-authored-by: 钉萁 <[email protected]>
Co-authored-by: Qwen-Coder <[email protected]>
2026-09-09 07:01:25 +00:00
ytahdnand钉萁 7bf0cafab4 feat(web-shell): separate Plan workflow from execution permissions (#11423)
Co-authored-by: 钉萁 <[email protected]>
2026-09-09 06:39:57 +00:00
ytahdnand钉萁 dcbb0b5f9c fix(web-shell): restore transcript layout and navigation presentation (#11430)
* fix(web-shell): restore transcript alignment and navigation preview

* fix(web-shell): align navigation visibility with chat width and turn count

* test(web-shell): cover docked environment panel alignment

---------

Co-authored-by: 钉萁 <[email protected]>
2026-09-09 06:07:22 +00:00
0fde526891 fix(web-shell): require answers before submitting questions (#11341)
Co-authored-by: 钉萁 <[email protected]>
Co-authored-by: Shaojin Wen <[email protected]>
2026-09-08 12:00:57 +00:00
ytahdnand钉萁 7dedcbfd71 fix(web-shell): stop surfacing SSE backpressure warning in transcript (#11340)
The daemon's slow_client_warning frame reports this SSE subscriber's event
queue backing up — a transport diagnostic, not a user-facing failure.
Rendering it as a chat status read as a broken connection mid-turn and sent
users refreshing or abandoning sessions during normal tool execution. Log the
queue watermarks to the console instead; client_evicted remains the real
disconnect signal and keeps its own error path.

Co-authored-by: 钉萁 <[email protected]>
2026-09-08 09:12:00 +00:00
ytahdnand钉萁 e35b8c2235 fix(web-shell): prefer clipboard text over image attachments (#11334)
Co-authored-by: 钉萁 <[email protected]>
2026-09-08 07:41:45 +00:00
ytahdnand钉萁 6ba04fc2d0 fix(web-shell): preserve running subagent timer across replay (#11332)
Co-authored-by: 钉萁 <[email protected]>
2026-09-08 07:00:53 +00:00
ytahdnand钉萁 663e55bd29 feat(web-shell): refine file cards and configure drop destination (#11298)
* feat(web-shell): customize artifact icons

* fix(web-shell): preserve artifact icon fallbacks

* fix(web-shell): improve artifact fallbacks

* feat(web-shell): refine file cards and configure drop destination

* fix(web-shell): address file card and drop review feedback

---------

Co-authored-by: 钉萁 <[email protected]>
2026-09-08 02:56:51 +00:00
ytahdnand钉萁 668df522c0 feat(web-shell): customize artifact icons (#11247)
* feat(web-shell): customize artifact icons

* fix(web-shell): preserve artifact icon fallbacks

* fix(web-shell): improve artifact fallbacks

---------

Co-authored-by: 钉萁 <[email protected]>
2026-09-07 09:27:02 +00:00
ytahdnand钉萁 56da7ac4d7 feat(web-shell): explain shell approval commands (#11266)
Co-authored-by: 钉萁 <[email protected]>
2026-09-07 08:06:55 +00:00
ytahdnand钉萁 36f2c14f0e feat(web-shell): align sidebar session scopes (#11240)
* feat(web-shell): add composer add menu

* test(web-shell): target composer history search

* feat(web-shell): align sidebar session scopes

* test(web-shell): stabilize sidebar smoke coverage

---------

Co-authored-by: 钉萁 <[email protected]>
2026-09-07 05:51:29 +00:00
0b8a926348 feat(serve): add workspace-scoped Skills runtime (#10697)
* feat(serve): add workspace-scoped Skills runtime

* test(serve): update Skills capability baseline

* fix(serve): address workspace Skills review findings

* fix(serve): address follow-up Skills review findings

* fix(serve): harden workspace skill management

* fix(serve): harden workspace skills management

* refactor(web-shell): remove inert skill management gate

* fix(web-shell): scope skill actions to active workspace

* fix(web-shell): keep workspace skills state scoped

* fix(web-shell): retry ready skills catalogs

* fix(serve): fail closed on unreadable skill directories

* fix(serve): invalidate global skills cache on errors

---------

Co-authored-by: 钉萁 <[email protected]>
Co-authored-by: qwen-code-dev-bot <[email protected]>
Co-authored-by: Shaojin Wen <[email protected]>
2026-09-05 08:03:24 +00:00
ytahdnand钉萁 80497a74d0 feat(web-shell): restore environment panel state (#10627)
* feat(web-shell): persist environment panel state

* fix(web-shell): address PR review regressions

* fix(web-shell): address remaining panel review issues

* test(web-shell): update system image preview expectation

* fix(web-shell): harden restored panel state

* fix(web-shell): preserve deferred panel tabs

* fix(web-shell): preserve panel state across async restores

* fix(web-shell): close panel restoration races

* fix(web-shell): order restored panel requests

* test(web-shell): pin completed agent polling

* fix(web-shell): preserve live panel state during restore

* fix(daemon): refresh live panel snapshots reliably

* fix(web-shell): isolate session panel state

* fix(web-shell): release detached terminals

* fix(web-shell): restore cached panel artifacts

---------

Co-authored-by: 钉萁 <[email protected]>
2026-09-04 08:49:43 +00:00
ytahdnand钉萁 38d532cdba feat(serve): add workspace-scoped MCP management (#10679)
* feat(serve): establish workspace runtime ownership

* feat(serve): add workspace-scoped MCP management

* fix(serve): address workspace MCP review findings

* fix(serve): address follow-up MCP review findings

* test(serve): stabilize MCP drain race coverage

---------

Co-authored-by: 钉萁 <[email protected]>
2026-09-02 13:33:37 +00:00
ytahdnand钉萁 ceb5b007ef fix(web-shell): preserve slash command attachments (#10730)
* fix(web-shell): preserve slash command attachments

* fix(web-shell): align attachment command resolution

* fix(web-shell): preserve attachments for slash-like text

---------

Co-authored-by: 钉萁 <[email protected]>
2026-09-02 13:30:35 +00:00
ytahdnand钉萁 77d41f48d3 fix(web-shell): reconcile queued prompts across clients (#10704)
* fix(web-shell): reconcile daemon-owned prompt lifecycle

* fix(web-shell): preserve queued prompt action feedback

* fix(web-shell): preserve queued prompt actions

* fix(web-shell): bound queued action retention

---------

Co-authored-by: 钉萁 <[email protected]>
2026-09-02 00:32:45 +00:00
20fe29be8c feat(serve): allow relocating session attachment storage via env var (#10066)
* feat(serve): allow relocating session attachment storage via env var

Adds QWEN_SERVE_SESSION_ATTACHMENTS_ROOT, which stores session attachments
under <root>/<projectHash>/attachments instead of the runtime temp dir so
operators can pin them to a dedicated volume. Reads and removes that miss
the configured root fall back to the default dir so pre-switch attachments
stay readable and removable; archive cleanup removes both roots. New uploads
never shadow a legacy fallback name, and both roots are removed via the same
tombstone dance so a deletion racing a session restore cannot sweep up a
successor directory.

* fix(serve): keep attachment root resolver off fast path

* fix(serve): harden session attachment fallback against degraded roots

read() and remove() no longer force-create the configured root before
consulting the fallback, so a degraded configured volume serves and
removes pre-switch attachments from the healthy default dir instead of
failing; delete() removes the fallback root first, mirroring remove(),
so a failure on the legacy root keeps the primary copy intact;
QWEN_SERVE_SESSION_ATTACHMENTS_ROOT is trimmed before use. Corrects the
docs to say attachment cleanup happens on session delete, not archive.

* fix(serve): address round-2 review findings on session attachment storage (#10066)

Co-authored-by: Qwen-Coder <[email protected]>

* fix(cli): stop restore-probe tests depending on ambient host git state (#10066)

* fix(acp): stabilize attachment fallback handling

* fix(acp): skip attachments being removed during copy

---------

Co-authored-by: 钉萁 <[email protected]>
Co-authored-by: Qwen Code Autofix <[email protected]>
Co-authored-by: Qwen-Coder <[email protected]>
Co-authored-by: qwen-code-dev-bot <[email protected]>
Co-authored-by: wenshao <[email protected]>
2026-09-01 01:38:15 +00:00
ytahdnand钉萁 8fcf9e76f9 feat(serve): establish workspace runtime ownership (#10593)
* feat(serve): establish workspace runtime ownership

* fix(serve): harden workspace runtime lifecycle

---------

Co-authored-by: 钉萁 <[email protected]>
2026-08-31 09:45:40 +00:00
b306bd2d46 feat(web-shell): optimize session overview table (#10115)
* feat(web-shell): optimize session overview table

* fix(web-shell): address session overview review feedback

* fix(web-shell): sync session overview catalog mutations

* fix(web-shell): address session overview round-2 review feedback

Co-authored-by: Qwen-Coder <[email protected]>

* fix(web-shell): address session overview round-3 review feedback

* chore: remove unintended core formatting diff

* fix(web-shell): stabilize session overview navigation

* fix(web-shell): restore session overview page scrolling

* fix(web-shell): preserve session overview safeguards

---------

Co-authored-by: 钉萁 <[email protected]>
Co-authored-by: Qwen-Coder <[email protected]>
2026-08-29 06:24:09 +00:00
ytahdnand钉萁 d6533785bd test(ci): make release classifier stub module-safe (#10402)
Co-authored-by: 钉萁 <[email protected]>
2026-08-28 12:29:06 +00:00
d853f09f52 feat(web-shell): add opt-in interactive browser terminal (#9984)
* feat(web-shell): add opt-in interactive browser terminal

Adds a browser terminal to the web-shell right panel, backed by shared node-pty sessions exposed through an authenticated /terminal WebSocket route on the serve daemon. The feature is opt-in (QWEN_WEB_TERMINAL=1 plus bearer auth) and gated behind a web_terminal capability so default deployments never expose the entry point.

Terminals are workspace-scoped: the daemon resolves the client workspace selector against the live registry, binds each PTY to that runtime's cwd and effective environment, and releases sessions when the workspace drains. Reconnects claim the same session id and replay bounded scrollback (including exit state) without duplicating output; controls resize/release ride text frames while PTY data stays binary. A session with no listeners is reclaimed after 15 minutes.

Co-authored-by: Qwen-Coder <[email protected]>

* chore(vscode): update third-party notices

* test(cli): include web terminal capability

* test(integration): include web terminal capability

* fix(web-shell): harden terminal lifecycle

* docs(serve): document web terminal capability

* fix(web-shell): address terminal review findings

* fix(web-shell): preserve terminal sessions and key events

* fix(web-shell): harden terminal session cleanup

* fix(web-shell): make terminal limits recoverable

* fix(web-shell): isolate terminal sessions

---------

Co-authored-by: 钉萁 <[email protected]>
Co-authored-by: Qwen-Coder <[email protected]>
Co-authored-by: Shaojin Wen <[email protected]>
2026-08-28 10:15:27 +00:00
ytahdnand钉萁 f470b122fa feat(web-shell): add opt-in composer add menu (#10379)
* feat(web-shell): add composer add menu

* test(web-shell): target composer history search

---------

Co-authored-by: 钉萁 <[email protected]>
2026-08-28 09:21:54 +00:00
ytahdnand钉萁 8beaa8d9e3 fix(web-shell): make tool summaries consistently collapsible (#10231)
Co-authored-by: 钉萁 <[email protected]>
2026-08-27 05:10:48 +00:00
3ca906ba7f fix(web-shell): keep pagination-completed turns expanded (#10086)
* fix(web-shell): keep pagination-completed turns expanded

Co-authored-by: Qwen-Coder <[email protected]>

* fix(web-shell): harden pagination keep-open against streaming cache and compact ids

* fix(web-shell): restore anchored scroll and per-load split-turn detection (#10086)

* fix(web-shell): reset pagination state across superseded loads and session switches (#10086)

Co-authored-by: Qwen-Coder <[email protected]>

* fix(web-shell): skip pinned recap in split-turn head-change check (#10086)

Co-authored-by: Qwen-Coder <[email protected]>

---------

Co-authored-by: 钉萁 <[email protected]>
Co-authored-by: Qwen-Coder <[email protected]>
Co-authored-by: qwen-code-dev-bot <[email protected]>
2026-08-27 02:19:46 +00:00