Skip to content

feat(web-shell): manage sessions from the sidebar (archive, unarchive, delete) - #6293

Merged
wenshao merged 1 commit into
QwenLM:mainfrom
wenshao:feat/web-shell-session-archive
Jul 4, 2026
Merged

feat(web-shell): manage sessions from the sidebar (archive, unarchive, delete)#6293
wenshao merged 1 commit into
QwenLM:mainfrom
wenshao:feat/web-shell-session-archive

Conversation

@wenshao

@wenshao wenshao commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator

What this PR does

Adds session management to the web-shell sidebar. Each session row now has an Archive quick action plus a "…" overflow menu (Rename / Archive / Delete), and a new collapsible "Archived" section lazily lists archived sessions, each with Restore / Delete. Under the hood it threads the daemon's existing archiveState list filter and the POST /sessions/archive / POST /sessions/unarchive endpoints up through the webui workspace facade and the useDaemonSessions hook. The daemon, ACP, and TypeScript SDK layers already shipped archive/unarchive in #6058 — this PR is the missing UI + facade wiring, with no changes to the SDK, daemon, or core.

Why it's needed

Session archiving landed end-to-end in the daemon and TypeScript SDK (#6058) but was never surfaced in the web-shell UI, so browser users had no way to archive, restore, or manage past conversations from the sidebar — the active list only ever grew. This closes that last-mile gap so a growing workspace stays tidy without leaving the browser.

Reviewer Test Plan

How to verify

Run qwen serve --web in a workspace that already has a few past sessions and open the sidebar. Hover a session row: an Archive (box) button and a "…" button appear. Click Archive → the session leaves the active list. Click "…" → a menu with Rename / Archive / Delete. Expand the "Archived" section at the bottom of the list → archived sessions appear with a count badge, each offering Restore / Delete. Click Restore → the session returns to the active list.

Behavior tied to the daemon contract (worth confirming): rename is enabled only for the current (loaded) session — the daemon metadata endpoint is live-session-only; the current session's Archive/Delete are disabled (archiving would tear down its running turn); archived rows can't be opened directly (the daemon returns 409 session_archived), so their row offers Restore, not load; Delete uses the hard-delete endpoint (removes the transcript, behind a confirm dialog); after any archive/unarchive/delete both the active and archived lists refetch.

Automated: npx vitest run passes in packages/webui (251 tests) and packages/web-shell (806 tests, incl. 11 sidebar tests — 5 new covering the archive quick action, the current-session guard, the overflow menu contents, and the archived expand → restore flow). Typecheck: tsc -p tsconfig.lib.json (web-shell) and tsc --noEmit (webui) are clean.

Real end-to-end drive (macOS): seeded three sessions on disk, ran qwen serve --web, and drove the real built bundle with Playwright — the archive → archived → restore round-trip works and the browser genuinely issues POST /sessions/archive (200) and POST /sessions/unarchive (200), each followed by a re-list of both the active and archived directories (verified in the daemon log).

Evidence (Before & After)

Before: the sidebar listed sessions with Rename/Delete inline only — no archive action and no way to see or restore archived sessions.

After:

Overflow "…" menu on a session row (Rename disabled because no session is the current one; Delete in danger red):

Overflow action menu

Archived section expanded (count badge + archived session with a Restore action), active list now one shorter:

Archived section

Tested on

OS Status
🍏 macOS
🪟 Windows ⚠️
🐧 Linux ⚠️

Environment

qwen serve --web serving the built packages/web-shell/dist; Playwright (headless Chromium) for the browser drive; unit tests via vitest.

Risk & Scope

  • Main risk or tradeoff: purely additive web-shell UI plus a thin webui facade delegation; the archive/unarchive/delete data paths are the daemon endpoints already shipped and tested in feat(daemon): Add session archive support #6058, so the new surface area is the sidebar interactions and the two facade methods.
  • Not validated / out of scope: renaming archived or non-current ("cold") sessions (the daemon metadata endpoint is live-session-only, so rename stays limited to the current session); generating a concise auto-title for long first-prompt-derived names; and pinning sessions — the latter two would need new daemon APIs and are intentionally left for a follow-up.
  • Breaking changes / migration notes: none. Adds an optional archiveState to the webui listSessions facade option and two new facade/hook methods (archiveSession / unarchiveSession); no existing signatures change.

Linked Issues

Builds on #6058 (daemon + SDK session archive support). No issue to auto-close.

中文说明

这个 PR 做了什么

给 web-shell 侧边栏加上会话管理。每个会话行现在有一个归档快捷按钮,外加一个 "…" 溢出菜单(重命名 / 归档 / 删除);列表底部新增一个可折叠的**"已归档"区域,懒加载列出已归档会话,各带恢复 / 删除**。底层把 daemon 现成的 archiveState 列表过滤,以及 POST /sessions/archive / POST /sessions/unarchive 端点,透过 webui 的 workspace facade 和 useDaemonSessions hook 接上来。daemon、ACP、TypeScript SDK 这几层在 #6058 已经实现了归档/取消归档——本 PR 只是补上缺失的 UI + facade 接线,不改 SDK、daemon、core

为什么需要

会话归档在 daemon 和 TypeScript SDK 层已经端到端落地(#6058),但一直没有在 web-shell UI 暴露,所以浏览器用户无法从侧边栏归档、恢复或管理历史会话——活跃列表只会越积越长。本 PR 补上这最后一公里,让工作区增长时也能在浏览器里保持整洁。

评审验证方案

如何验证

在一个已有若干历史会话的工作区运行 qwen serve --web 并打开侧边栏。悬停某个会话行:出现归档(方框图标)按钮和 "…" 按钮。点归档 → 该会话从活跃列表消失。点 "…" → 弹出重命名 / 归档 / 删除菜单。展开列表底部的**"已归档"区域 → 已归档会话带计数徽章显示,各带恢复 / 删除**。点恢复 → 会话回到活跃列表。

与 daemon 契约相关、值得确认的行为:重命名仅对当前(已加载)会话可用(daemon 的元数据端点只支持活跃会话);当前会话的归档/删除置灰(归档会强制结束其正在运行的 turn);已归档行不能直接打开(daemon 返回 409 session_archived),所以它给的是恢复而非加载;删除走硬删端点(删除 transcript,带二次确认);任何归档/取消归档/删除之后,活跃和已归档两个列表都会重新拉取

自动化:npx vitest runpackages/webui(251 测试)和 packages/web-shell(806 测试,含 11 个侧边栏测试——其中 5 个新增,覆盖归档快捷键、当前会话守卫、溢出菜单内容、以及展开已归档→恢复的流程)全部通过。类型检查 tsc -p tsconfig.lib.json(web-shell)和 tsc --noEmit(webui)干净。

真实端到端(macOS):在磁盘上 seed 了三个会话,跑真 qwen serve --web,用 Playwright 驱动真实构建产物——归档 → 已归档 → 恢复往返正常,且浏览器确实发出 POST /sessions/archive(200)与 POST /sessions/unarchive(200),每次之后都重新列了活跃与归档两个目录(在 daemon 日志中确认)。

证据(前 / 后)

之前:侧边栏只在行内提供重命名/删除——没有归档动作,也无法查看或恢复已归档会话。

之后:见上方英文部分的两张截图(会话行的"…"溢出菜单;展开的已归档区域,带计数徽章与恢复动作)。

测试平台

仅本地 macOS 测试(✅);Windows / Linux 未本地测试(⚠️),由 CI 覆盖。

环境

qwen serve --web 服务构建后的 packages/web-shell/dist;Playwright(无头 Chromium)做浏览器驱动;单测用 vitest

风险与范围

  • 主要风险/取舍:纯增量的 web-shell UI + 一层很薄的 webui facade 委托;归档/取消归档/删除的数据路径是 feat(daemon): Add session archive support #6058 已发布并测试过的 daemon 端点,新增面主要是侧边栏交互与两个 facade 方法。
  • 未验证 / 范围外:重命名已归档或非当前("冷")会话(daemon 元数据端点只支持活跃会话,故重命名仍限当前会话);为冗长的首条 prompt 派生名生成简洁标题;以及会话置顶——后两者需要新的 daemon API,有意留到后续。
  • 破坏性变更 / 迁移说明:无。给 webuilistSessions facade 选项加了可选的 archiveState,并新增两个 facade/hook 方法(archiveSession / unarchiveSession);未改动任何既有签名。

关联 Issue

基于 #6058(daemon + SDK 会话归档支持)。无需自动关闭的 issue。

…, delete)

Add an Archive quick action and a "..." overflow menu (Rename / Archive / Delete) to each session row in the web-shell sidebar, plus a collapsible "Archived" section that lazily lists archived sessions with Restore / Delete. Thread the daemon's existing archiveState filter and archive/unarchive endpoints through the webui workspace facade and the useDaemonSessions hook; rename stays limited to the current live session.
@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Thanks for the PR, @wenshao!

Template looks good ✓

Problem: This is a real feature gap, not a theoretical concern. Session archive/unarchive shipped end-to-end in the daemon and SDK (#6058) but was never surfaced in the web-shell sidebar. The PR closes that last-mile gap — browser users had no way to archive or manage past sessions.

Direction: Clearly aligned with the session-management roadmap. Web-shell users managing their session history is a natural expectation once the backend supports it. No product-direction concerns.

Approach: The scope is tight — 7 files, purely additive UI wiring plus two thin facade methods. No core changes, no SDK changes, no scope creep. The overflow menu pattern (rename/archive/delete) and the collapsible archived section are standard UI conventions. One observation: the sidebar's quick-action button changed from Rename to Archive, with Rename moving into the "…" menu. This is a reasonable UX tradeoff (archive is likely more common than rename), but worth flagging for the maintainer to weigh in on.

Moving on to code review. 🔍

中文说明

感谢贡献,@wenshao

模板完整 ✓

问题: 这是一个真实的功能缺口,不是理论性的担忧。会话归档/取消归档在 daemon 和 SDK 层已经端到端落地(#6058),但 web-shell 侧边栏一直没有暴露。本 PR 补上了这最后一公里——浏览器用户之前无法归档或管理过去的会话。

方向: 与会话管理路线图明确对齐。一旦后端支持了归档功能,web-shell 用户管理自己的会话历史是理所当然的期望。没有产品方向上的顾虑。

方案: 范围很收敛——7 个文件,纯增量的 UI 接线加两个薄 facade 方法。不改 core,不改 SDK,没有范围蔓延。溢出菜单模式(重命名/归档/删除)和可折叠的已归档区域是标准 UI 范式。一个观察:侧边栏的快捷操作按钮从"重命名"换成了"归档","重命名"移入了"…"菜单。这是一个合理的 UX 取舍(归档可能比重命名更常用),但值得 maintainer 确认。

进入代码审查 🔍

Qwen Code · qwen3.7-max

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Code Review

Independent proposal: To surface session archive/unarchive in the web-shell sidebar, I'd add an archiveState filter to useDaemonSessions, wire archiveSession/unarchiveSession through the workspace facade, add per-row archive/restore buttons with a collapsible archived section, guard the current session, and add i18n + tests.

Comparison: The PR matches and exceeds this — instead of adding individual inline buttons, it introduces a polished overflow "…" menu pattern (rename / archive / delete) and a separate archive quick-action button. This is better UX than scattered individual buttons. No simpler path was missed.

Findings (non-blocking):

  • The sidebar's quick-action button changed from Rename to Archive. Rename moved into the "…" overflow menu. This is a reasonable UX tradeoff (archive is more common than rename), but the maintainer should confirm this feels right.
  • After archive/unarchive/delete, both active and archived lists refetch — correct, since the daemon's hard-delete endpoint removes from both directories.
  • Menu uses position: fixed with viewport-aware open-up/open-down logic, properly escaping the session list's overflow: auto clip. Event listeners (pointerdown, Escape, scroll, resize) are cleaned up correctly.
  • The useDaemonSessions hook's archiveSession/unarchiveSession properly chain reload() after the facade call, and the component additionally calls reloadArchived() — both lists stay in sync.
  • useCallback dependency arrays are correct throughout.
  • No reuse concerns — the changes are thin UI wiring over existing SDK methods.
  • Accessibility: proper role="menu", role="menuitem", aria-haspopup, aria-expanded, disabled titles. ✓

No critical blockers or AGENTS.md violations found.

Tests

$ cd packages/web-shell && npx vitest run client/components/sidebar/ --reporter=verbose

 ✓ WebShellSidebar — version footer > shows the qwen-code version in the footer when expanded
 ✓ WebShellSidebar — version footer > renders a non-semver fallback without a bogus "v" prefix
 ✓ WebShellSidebar — version footer > hides the version when the sidebar is collapsed
 ✓ WebShellSidebar — version footer > renders no version badge when the daemon reports none
 ✓ WebShellSidebar — daemon status entry > invokes onOpenDaemonStatus when the footer button is clicked
 ✓ WebShellSidebar — daemon status entry > still exposes the daemon status button when collapsed
 ✓ WebShellSidebar — archive actions > archives an active session from the quick action button
 ✓ WebShellSidebar — archive actions > disables archiving the current session
 ✓ WebShellSidebar — archive actions > opens the overflow menu with rename, archive, and delete
 ✓ WebShellSidebar — archive actions > archives a non-current session from the overflow menu
 ✓ WebShellSidebar — archive actions > reveals archived sessions on demand and restores them

 Test Files  1 passed (1)
      Tests  11 passed (11)
$ cd packages/webui && npx vitest run

 Test Files  18 passed (18)
      Tests  251 passed (251)

Typecheck (tsc -p tsconfig.lib.json for web-shell): no new errors. The 10 pre-existing errors in useQueuedPrompts.ts, main.tsx, etc. are identical to main — not introduced by this PR.

Real-Scenario Testing

This PR modifies a browser-based web-shell UI, not the CLI TUI. Tmux-based real-scenario testing does not apply here. The author provided Playwright evidence on macOS (archive → archived → restore round-trip verified, confirmed POST /sessions/archive and POST /sessions/unarchive both return 200 with re-list of both directories). Windows and Linux are marked ⚠️ (not locally tested, covered by CI).

中文说明

代码审查

独立方案: 要在 web-shell 侧边栏暴露会话归档/取消归档,我会给 useDaemonSessions 添加 archiveState 过滤,通过 workspace facade 接入 archiveSession/unarchiveSession,为每行添加归档/恢复按钮和可折叠的已归档区域,保护当前会话,并添加 i18n 和测试。

对比: PR 的实现匹配并超出了这个方案——不是添加单独的按钮,而是引入了一个精致的"…"溢出菜单模式(重命名/归档/删除)和单独的归档快捷按钮。比分散的单独按钮体验更好。没有遗漏更简单的路径。

发现(非阻塞):

  • 侧边栏快捷操作按钮从"重命名"换成了"归档","重命名"移入"…"溢出菜单。这是合理的 UX 取舍(归档比重命名更常用),但 maintainer 应确认这个设计是否合适。
  • 归档/取消归档/删除后,活跃和已归档两个列表都会重新拉取——正确,因为 daemon 的硬删端点会从两个目录中移除。
  • 菜单使用 position: fixed 和视口感知的上/下展开逻辑,正确避开会话列表的 overflow: auto 裁剪。事件监听器(pointerdown、Escape、scroll、resize)正确清理。
  • useCallback 依赖数组全部正确。
  • 无可复用性问题——变更是对已有 SDK 方法的薄 UI 接线。
  • 无障碍:正确的 role="menu"role="menuitem"aria-haspopuparia-expandeddisabled 标题。✓

未发现关键阻塞问题或 AGENTS.md 违规。

测试

侧边栏测试:11 个全部通过(5 个新增覆盖归档/恢复/溢出菜单)。webui 测试:251 个全部通过。类型检查无新增错误。

真实场景测试

本 PR 修改的是基于浏览器的 web-shell UI,不是 CLI TUI。基于 tmux 的真实场景测试不适用。作者提供了 macOS 上的 Playwright 证据(归档→已归档→恢复往返验证通过,确认 POST /sessions/archivePOST /sessions/unarchive 均返回 200 并触发两个目录的重新列举)。Windows 和 Linux 标记为 ⚠️(未本地测试,由 CI 覆盖)。

Qwen Code · qwen3.7-max

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

This PR closes a clear, real gap: session archive/unarchive landed in the backend (#6058) but was never surfaced in the web-shell sidebar. The implementation is focused, well-tested, and follows project conventions.

What I like: the overflow menu pattern is a clean UX improvement over scattered inline buttons. The code is thin wiring over existing SDK methods — no new abstractions, no scope creep. Both active and archived lists stay in sync after every mutation. Accessibility attributes are correct throughout. 11 sidebar tests (5 new) cover the core archive/restore/menu flows, and all 251 webui tests pass.

One thing for the maintainer to confirm: the sidebar quick-action button changed from Rename to Archive (Rename moved into the "…" menu). This seems like the right tradeoff — archive is probably more common — but worth a second look.

No blocking concerns. LGTM. ✅

中文说明

这个 PR 填补了一个明确、真实的功能缺口:会话归档/取消归档在后端(#6058)已经落地,但 web-shell 侧边栏一直没有暴露。实现聚焦、测试充分、遵循项目规范。

亮点:溢出菜单模式是对分散的按钮的一个干净的 UX 改进。代码是对现有 SDK 方法的薄接线——没有新的抽象,没有范围蔓延。每次操作后活跃和已归档列表保持同步。无障碍属性全部正确。11 个侧边栏测试(5 个新增)覆盖了核心的归档/恢复/菜单流程,251 个 webui 测试全部通过。

一个需要 maintainer 确认的点:侧边栏快捷操作按钮从"重命名"换成了"归档"("重命名"移入了"…"菜单)。这看起来是正确的取舍——归档可能比重命名更常用——但值得再确认一下。

无阻塞问题。LGTM. ✅

Qwen Code · qwen3.7-max

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM, looks ready to ship. ✅

@qwen-code-ci-bot qwen-code-ci-bot added category/ui User interface and display roadmap/session-management Roadmap: Session management features scope/session-management Session state and persistence scope/web-shell type/feature-request New feature or enhancement request labels Jul 4, 2026

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

No review findings. Downgraded from Approve to Comment: CI still running.

— qwen3.7-max via Qwen Code /review

@ytahdn ytahdn left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM — well-structured implementation with clean overflow menu pattern and proper accessibility attributes. All 262 tests pass.

A few non-blocking suggestions:

  • confirmDeleteSession missing busy guard: handleArchive/handleUnarchive check busySessionIdRef.current before proceeding, but confirmDeleteSession doesn't. Consider adding the same guard for consistency.
  • Test coverage gaps: Three new code paths lack coverage — isArchived delete routing, archived overflow menu items, and the delete-from-menu → confirm → delete full path.
  • Archive/unarchive button disabled state: These buttons don't reflect busy state during operations, unlike the delete flow which has a confirm dialog as natural barrier.

— qwen3.7-max via Qwen Code /review

@wenshao
wenshao added this pull request to the merge queue Jul 4, 2026
Merged via the queue into QwenLM:main with commit c37cb23 Jul 4, 2026
39 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

category/ui User interface and display roadmap/session-management Roadmap: Session management features scope/session-management Session state and persistence scope/web-shell type/feature-request New feature or enhancement request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants