---
url: /wave-agent/specs/desktop/desktop-sessions.md
description: >-
  CodeWave IDE 桌面端会话：新建/切换/重命名/删除、侧栏搜索筛选（标题 +
  正文内容）、行状态位与后台确认提醒、会话状态看板、历史恢复、worktree 隔离、跨客户端 /resume
---

# 功能规格说明：桌面端会话

> 本规格描述「一条会话在桌面端怎么被创建、切换、命名、提醒与恢复」。SSH 远端主机接入、远端 CLI 安装与 daemon 常驻见 [desktop-remote.md](./desktop-remote.md)；会话呈现在哪个分屏、侧边栏怎么收起见 [desktop-split-pane.md](./desktop-split-pane.md) 与 [desktop-layout.md](./desktop-layout.md)。

## 用户场景与测试

### 用户故事：会话管理（优先级：P2）

作为用户，我希望在应用内新建会话并恢复上次的会话与工作目录，以便我可以在不同任务之间切换。

**为什么是这个优先级**：会话管理是日常使用的基础能力，且 sidebar 是 desktop 相对插件端的布局扩展。

**独立测试**：进行一个会话后点击"新对话"开始全新会话；重启应用验证（「最近打开」列表非空时）自动进入最近目录的新对话、（列表为空时）回到选择工作目录的初始状态且自动初始化不重复触发；从侧边栏会话树恢复一条历史会话；删除一条 worktree 会话并验证 worktree 与临时分支被一并清理；纯键盘操作：Tab 逐条遍历会话条目，`↑`/`↓`/Home/End 在条目间移动焦点、`→` 到达删除按钮、Enter 弹出确认对话框。

**验收场景**：

1. **假设**应用正在运行，**当**用户点击侧边栏"新对话"，**则**必须在当前窗口内开始一个全新会话（回到欢迎页 + 输入框状态）；**不中止任何正在生成的会话——原会话在后台继续运行，可从侧边栏切回查看结果**。
2. **假设**用户关闭并重新打开应用，**当**应用启动，**则**不得恢复上次的会话——每次全新开始（工作目录与主机选择不跨重启持久化）：**当**「最近打开」列表非空（用户曾主动选择过目录），**则**应用必须自动执行一次与点击侧边栏"新对话"完全一致的初始化（2026-09-08 拍板「刚打开桌面端就跟点击新对话的效果一样」）：在聚焦分屏以「最近打开」列表首项（最近主动选择的仓库根，见场景 8）为工作目录启动一个新的空会话，输入框可用、上下文栏显示该目录，用户无需先经目录选择步骤即可直接对话；**当**「最近打开」列表为空（用户从未主动选择过目录），**则**维持选择工作目录的欢迎/初始界面（按场景 10 不自动绑定任何工作目录）。自动初始化只在本次启动**首次界面就绪时执行一次**：webview 重新加载、新增分屏、设置页往返重挂载、聚焦分屏变化等后续 `webviewReady` 均不得重复触发（同一分屏不得出现第二个自动 agent）；自动初始化的 agent 生成期间用户点击侧边栏历史会话或以其它操作改变该分屏，**则**必须以用户最新操作为准——自动生成的新会话不得覆盖用户选择（若其生成已完成则转入后台或销毁，不得孤儿驻留），语义与「历史会话即时进入与恢复加载动画」场景 4 一致。自动初始化后用户仍可照常换目录（工作目录选择器保持可用）、点历史会话或再点"新对话"（场景 8/10 语义不变）。
3. **假设**侧边栏显示会话树，**当**用户查看侧边栏，**则**必须看到二级结构：一级为目录分组（可折叠，折叠状态按「主机 + 目录」记忆并在重启后保持，从未被折叠过的分组默认展开——见「侧栏分组折叠状态记忆」），二级为该目录下全部会话（按创建时间倒序，最新创建在上；会话的后续活跃——发消息、生成结束、被选中——不改变其位置）；会话树数据完全来自 desktop 自维护的会话索引，不向 CLI 查询。「同一目录」按归一化后的路径判定：Windows 上 `C:\a\repo` 与 `C:/a/repo` 是同一个目录（后者是 worktree 会话的仓库根，取自 `git worktree list` 的输出），只出一个分组；分组标题只显示该目录的目录名，不显示完整路径。
4. **假设**有一个或多个会话正在流式生成回复，**当**用户查看侧边栏，**则**每个正在生成中的会话条目的状态位都必须显示「运行中」标识（可同时存在多个；显示在条目的行状态位，视觉实现以 Figma 13656:5470 的 loading 环为准）；**运行中标识不受「仅后台会话」限制——正在激活对话中生成或已显示在其它分屏的会话同样在行上提示 loading（2026-09-09 拍板「选中的对话也应显示 loading」：即使运行状态在对话/分屏自身可见也不省略行上提示）**；对应会话生成结束后其标识消失。
5. **假设**某会话存在等待用户响应的确认请求（工具权限/编辑确认、计划确认、AskUserQuestion 等）且不在当前激活对话、也未显示在其它分屏，**当**用户查看侧边栏，**则**该会话条目必须显示「待确认」标识（与运行中标识以颜色区分；待确认与运行中同时成立时优先显示待确认；标识同样只在后台会话渲染——会话正显示在分屏时其确认对话框直接可见，不重复提示）；用户响应确认或打开会话处理后其标识消失。
6. **假设**侧边栏会话树已加载，**当**用户点击当前目录下的某条会话，**则**必须恢复该会话到当前窗口；点击其他目录下的会话时，必须切换到该会话的工作目录再恢复；**切换会话或目录不中止任何正在生成的后台会话**。
7. **假设**侧边栏会话树已加载，**当**用户点击某条会话的删除图标并确认，**则**必须从索引中移除该会话；若该会话是 worktree 会话，还必须一并删除其 worktree 目录与临时分支（删除确认必须先按「删除 worktree 会话前提示将丢失的改动」展示将丢失的改动）。
8. **假设**用户已主动选择并打开过某主仓库（该仓库出现在「最近打开」列表），**当**用户点击侧边栏"新对话"或用 `Cmd/Ctrl+Click`、`Cmd/Ctrl+Shift+N` 新建对话，**则**新对话的默认工作目录必须是「最近打开」列表中用户最近主动选择的仓库根，与上一会话的状态（是否为 worktree 会话、agent 工作目录是否被 bash 改写）完全无关；新会话页面的分支查询走该主仓库分支。
9. **假设**上一会话是 worktree 会话或其 agent 工作目录在会话中被 bash 工具 `cd` 改写到了某个 worktree 路径，**当**用户新建对话，**则**该 worktree 路径不得被写入或覆盖「最近打开」列表（**会话激活与 agent 工作目录变化均不触发「最近打开」写入**，该列表只由用户主动选择/切换目录的动作更新），新对话的默认目录仍为用户最近主动选择的主仓库根，分支查询与「最近打开」列表均不残留该 worktree 路径。
10. **假设**用户从未主动选择过任何工作目录（「最近打开」列表为空），**当**用户点击侧边栏"新对话"或用 `Cmd/Ctrl+Click`、`Cmd/Ctrl+Shift+N` 新建对话，**则**必须进入空白新对话界面（工作目录选择器显示"选择工作目录…"占位入口，不自动绑定任何工作目录，用户可稍后通过该入口选择目录），不得因列表为空而拒绝进入或静默无响应；**当**用户处于未选择工作目录的空白会话时，**则**发送入口整体禁用（输入区不可编辑、发送 / 附件 / 快捷指令 / 权限模式按钮一并置灰），并在输入框占位文案写明禁用原因「请先选择项目目录」（该原因与「未登录」互斥且登录优先，见 [sso-auth.md](../enterprise/sso-auth.md) 场景 8），不得在无工作目录的情况下启动会话。
11. **假设**用户在某会话的输入框中输入了尚未发送的内容（如"1"），**当**切换到另一个会话，**则**新会话的输入框必须显示其自身的草稿（此前未输入则为空），不得沿用上一会话的输入内容；**当**切回原会话，**则**输入框必须恢复此前未发送的内容——输入草稿按会话独立保存，同一分屏内切换会话不得共享草稿，会话在不同分屏间移动时草稿跟随会话。
12. **假设**用户在某会话输入内容后立即切换到另一个会话再切回（草稿保存尚未完成），**当**切回原会话，**则**仍必须恢复刚输入的内容；切换期间展示过的其他会话的输入框不得残留该内容，其草稿不得被写入该内容。
13. **假设**侧边栏会话树已加载且用户使用键盘，**当**用户连续按 Tab 键，**则**焦点必须按显示顺序逐条遍历每条会话条目主体（分组标题同为键盘可聚焦控件，保持可 Tab），不得把任何条目主体移出 Tab 序列；条目的删除按钮除外——它不得进入 Tab 序列，只能通过方向键到达。
14. **假设**焦点位于某条会话条目上，**当**用户按 `↓` 或 `↑`，**则**焦点必须在展开显示的会话条目之间移动（`↓` 下一条、`↑` 上一条，跨目录分组连续行进；到末条后继续 `↓` 回绕到首条，反之亦然），移动时列表自动滚动使目标条目完整可见。
15. **假设**焦点位于某条会话条目上，**当**用户按 Home 或 End，**则**焦点必须分别跳到第一条 / 最后一条会话条目上。
16. **假设**焦点位于某条会话条目主体，**当**用户按 `→`，**则**焦点必须移到该条目的删除按钮上，此时删除按钮变为可见并显示焦点指示；**当**焦点位于删除按钮上时按 `←`，**则**焦点回到该条目主体；删除按钮本身不得进入 Tab 序列，只能通过方向键到达。
17. **假设**焦点位于某条会话条目主体，**当**用户按 Enter 或 Space，**则**必须恢复该会话（与普通点击一致）；**假设**焦点位于删除按钮，**当**用户按 Enter 或 Space，**则**必须弹出与鼠标点击一致的删除确认对话框。
18. **假设**焦点位于某个目录分组标题（分组标题为键盘可聚焦控件），**当**用户按 Enter 或 Space，**则**必须切换该分组的折叠状态（与点击标题一致）。
19. **假设**用户在应用内任何位置按 Delete/Backspace 键，**则**不得因此触发任何会话的删除——删除会话不提供键盘快捷键，唯一路径是方向键到达条目的删除按钮后激活并经确认对话框完成。
20. **假设**应用刚启动且侧边栏无任何运行中会话——「最近打开」列表非空时应用已按场景 2 自动进入最近目录的新对话（聚焦分屏绑定了一个空会话，窗口处于新对话态）、列表为空时窗口处于选择工作目录的欢迎态；用户尚未主动点开任何历史会话，**当**用户查看侧边栏"新对话"按钮，**则**按钮必须保持可用（不得因当前尚无用户激活过的工作目录或会话而禁用）；**当**用户点击该按钮，**则**宿主自行决定新会话的工作目录——「最近打开」列表非空时取最近主动选择的仓库根（场景 8），为空时进入空白新对话界面（场景 10）——不得要求用户先点开某条历史会话才解锁"新对话"；自动初始化已绑定的空会话被再次"新对话"替换属预期（场景 1 语义，空 agent 被回收不残留）。
21. **假设**应用已通过侧边栏选中并激活某条会话（窗口进入会话态），**当**用户查看侧边栏"新对话"按钮，**则**按钮保持可用且行为与场景 1/8/10 一致（替换当前窗口开始全新会话，正在生成的原会话在后台继续）——按钮可用性不得在会话态与初始态之间跳变。
22. **假设**某目录下已存在会话（无论是否正在后台运行、是否显示在其它分屏），**当**用户在工作目录选择器中选择该目录（点「最近打开」列表项或「浏览…」选定本地目录）、或经远程目录浏览面板确认该远端目录，**则**必须在当前分屏**开始一个全新会话，不得复用该目录下已存在的会话**（不得把池中同目录的会话捞到当前分屏顶替新会话）；新会话的初始工作目录即所选目录，与该目录下既有会话的状态（agent 工作目录是否被 bash 改写等）无关；该目录下原有会话必须原样保留、不得被中止或销毁；回到某条已存在的会话只经侧边栏会话树（场景 6），选目录不承担该职责。
23. **假设**用户在某会话的输入框里输入了尚未发送的内容，**当**用户打开设置页或会话状态看板（两者都整页顶替对话区，输入框随之卸载）再返回对话窗口，**则**输入框必须恢复此前未发送的内容——包括刚敲进去、尚未上报宿主的最后一段输入：卸载前必须先把它补报给宿主（宿主按会话记着草稿），重挂载时再按宿主回放的快照回填，不得因为视图切换把这段输入丢掉；输入框里的引用标签（上传附件与 @ 提及文件的文件引用、选区引用、IDE 上下文标签）与文字同等对待，回填后必须仍是可识别的引用标签、按引用语义随消息发出，不得退化成纯文本。

***

### 用户故事：侧栏分组折叠状态记忆（优先级：P2）

作为桌面端用户，我希望侧边栏里每个项目文件夹分组的展开/折叠状态被记住，以便重启应用后不用每次都把不想看的分组重新折一遍。

**为什么是这个优先级**：分组默认全部展开，项目一多每次启动都要逐个人工折叠（每次重启都回到全展开是本条的唯一来源）；折叠状态是纯前端的窗口级展示偏好，不碰会话数据、不改会话索引，改动面小。

**独立测试**：折叠某个目录分组后关闭应用再打开，验证该分组仍是折叠态、其余从未动过的分组仍展开；折叠分组 A 并把原本折叠的分组 B 展开后重启，验证两者各自保持；切到一台远端主机（同一目录路径在本机也有一组），验证两侧分组的折叠状态互不影响；折叠后重启，验证该分组下会话行不渲染、分组标题仍在；在搜索框输入落在折叠分组内的会话名，验证搜索不改变折叠状态。

**验收场景**：

1. **假设**侧边栏渲染出若干目录分组，**当**用户折叠其中一个（点击分组标题，或在标题上按 Enter/Space），**则**该分组立即折叠（其会话行不再渲染、分组标题保留）；**当**用户随后关闭并重新打开应用（含 webview 重新加载），**则**该分组必须仍为折叠态，其余从未被改动过的分组按默认展开。
2. **假设**用户对同一分组多次切换展开/折叠，**当**应用重启，**则**以最后一次操作为准（最后一次是展开则该分组展开，是折叠则折叠）。
3. **假设**同一目录路径在本机与某远端主机上各有一个会话分组，**当**用户只折叠了本机那份，**则**远端主机那份必须保持它自己的折叠状态——折叠状态按「主机 + 目录」分别记忆，不按目录名跨主机共享。
4. **假设**某分组在重启后已无任何会话（该目录不再出现在会话树），**当**该目录此后重新有会话、分组再次出现，**则**必须沿用上次记忆的折叠状态，而不是重置为展开。
5. **假设**某分组当前处于折叠态，**当**该分组下新建了会话，或该分组内的会话开始生成、进入待确认，**则**不得自动展开该分组（折叠是用户的显式选择；状态聚合提示仍不上卷到分组头部，见非目标）。
6. **假设**侧栏搜索/筛选命中折叠分组内的会话，**则**该分组保持折叠、命中会话不显示，用户展开该分组后即可看到；搜索与筛选不改变记忆的折叠状态。

***

### 用户故事：会话重命名（优先级：P2）

作为桌面端用户，我希望从会话行的「⋯」菜单打开一个重命名弹窗改标题，以便不用重开会话就能把某段对话标成自己认得出的名字。

**为什么是这个优先级**：标题目前不可改，只能靠首条消息截断；侧边栏会话树是高频入口，且行菜单已有「并排打开 / 删除会话」两项，加第三项成本低。存储侧（会话文件里的 `custom-title` 保留条目、列表读取与显示优先级）见 [ui/session-management.md](../ui/session-management.md) 的「会话自定义标题（重命名）」；CLI 与插件端的入口见同文件的「会话重命名入口：CLI 与插件端」。

**独立测试**：在侧边栏某会话行上打开行菜单点「重命名」，验证弹出的模态里输入框预填原标题且全选；输入新标题点「保存」后弹窗关闭、行标题立即变化、重启应用后仍是新标题；再试一次按 Esc，验证弹窗关闭且标题未变。

**验收场景**：

1. **假设**侧边栏会话树已加载，**当**用户在某会话行上打开行菜单，**则**菜单必须含三项，顺序为「并排打开 / 重命名 / 删除会话」（在原两项之间插入）；「重命名」不得使用删除项的危险配色。
2. **假设**行菜单打开且用户激活「重命名」，**则**必须打开居中模态（不是行内编辑）：输入框预填当前标题并**全选**，弹窗含「取消」「保存」两个按钮；后台列表不得进入 loading / 禁用 / 骨架状态（弹窗是覆盖层，不替换列表）。
3. **假设**重命名弹窗已打开，**当**用户点「保存」或按 Enter，**则**必须保存新标题并关闭弹窗；保存进行中「保存」按钮不得重复提交。
4. **假设**重命名弹窗已打开，**当**用户按 Esc 或点「取消」，**则**必须关闭弹窗且不提交（标题不变、不发请求）。
5. **假设**输入内容 trim 后为空（仅含空格），**当**用户点「保存」或按 Enter，**则**必须不予保存（不发请求），并在输入框下方提示「会话名称不能为空」，弹窗保持打开供用户继续修改；**当**用户重新输入，**则**提示必须随之消失。
6. **假设**用户使用中文/日文等输入法，**当**处于组合输入中（`isComposing`），**则**不得把 Enter / Esc 当作保存 / 取消处理。
7. **假设**焦点在弹窗输入框，**当**用户按键，**则**输入框必须拦住按键冒泡，不得触发行上的裸键或行菜单快捷键。
8. **假设**用户提交新标题，**当**保存进行中，**则**必须乐观更新——侧边栏行标题、会话看板卡片、该会话（若在当前分屏）的头部标题立即一起变为新标题；**假设**保存失败，**则**必须回滚为原标题，弹窗不关闭并就地显示宿主的失败原因（不得只有静默回滚）。
9. **假设**新标题保存成功，**当**用户重启应用，**则**侧边栏该行必须仍显示自定义标题（标题随会话文件持久化，不依赖重启前的内存状态）。
10. **假设**用户把某会话重命名后继续在该会话中对话，**当**列表刷新或应用重启，**则**标题必须保持自定义标题，不得被「首条用户消息截断」的既有 first-wins 逻辑重新覆盖。
11. **假设**新标题长于会话行宽度，**则**列表中必须单行截断并显示省略号，不换行、不撑高行高。
12. **假设**用户完成重命名，**则**只改会话名称本身：会话内容、会话顺序、所属工作目录分组均不变化。

***

### 用户故事：侧栏会话搜索与筛选（优先级：P2）

作为桌面端用户，我希望在侧边栏按会话名搜索、按「会话状态 / 创建日期」筛选会话列表，以便会话攒到几十上百条后还能快速找到那一段。

**为什么是这个优先级**：会话树目前只有分组折叠，没有检索能力；搜索与筛选都是纯前端对已下发快照的过滤，不改会话数据与排序，实现成本低而收益直接。

**独立测试**：在侧栏搜索框输入某会话名的一部分（大小写混写），验证列表即时只剩命中的会话、无命中的分组整组消失；输入一个都不命中的词，验证列表显示「未找到匹配的对话」；清空后列表恢复。再打开筛选面板勾选「运行中」并点「确定」，验证只留运行中的会话、筛选按钮变红；点「重置」验证列表不变，再点一次「确定」验证列表恢复。

**验收场景**：

1. **假设**侧边栏已加载会话树，**则**搜索框必须位于「新对话」按钮上方的同一列，右侧为筛选按钮。
2. **假设**用户在搜索框输入关键词，**当**输入内容变化，**则**必须即时过滤（无需回车、无「搜索」按钮）：按会话名称做子串匹配、不区分大小写；匹配范围不包含工作目录名。
3. **假设**某个分组内没有任何会话命中关键词，**则**必须整组隐藏（分组标题一并隐藏），而不是留一个空分组。
4. **假设**所有分组都没有命中，**则**会话列表区域必须显示提示「未找到匹配的对话」。
5. **假设**用户清空搜索框，**则**必须恢复为过滤前的会话列表（搜索期间不改变会话数据本身与组内顺序）。
6. **假设**焦点在搜索框，**当**用户按 Esc，**则**先清空关键词；关键词已空时再按一次才让输入框失焦；输入框内的按键不得冒泡触发行上的裸键或行菜单快捷键。
7. **假设**用户点筛选按钮，**则**必须展开筛选面板，含「会话状态」（等待中 / 运行中 / 已完成，可多选）与「创建日期」（今天 / 近7天 / 近30天，单选）两组。
8. **假设**筛选面板已展开，**当**用户勾选或取消勾选，**则**列表不得立即变化——必须点「确定」才生效并关闭面板；按 Esc 或点面板外区域关闭面板时，草稿勾选全部丢弃、已生效的筛选保持原样；再次打开面板时，勾选状态必须回填当前已生效的筛选。
9. **假设**用户选中多个会话状态，**则**必须取并集（命中任一即显示）；状态口径与「会话状态看板」一致：等待确认 > 运行中 > 其余为已完成；侧栏行上的「完成且未查看」绿点不是状态判据。
10. **假设**用户选择某个创建日期档位，**则**必须按会话创建时间过滤：「今天」为本地时区当天 0 点起，「近7天」「近30天」为含今天在内的最近 7 / 30 天；会话缺少创建时间时按最近活动时间归类。
11. **假设**筛选已生效，**则**筛选按钮必须显示为激活态（红色），再次进入面板可看到同样的勾选。
12. **假设**筛选面板里已有勾选，**当**用户点「重置」，**则**必须只清空面板内的勾选，不改变当前已生效的筛选；需再点「确定」才把筛选清空。
13. **假设**搜索关键词与筛选条件同时存在，**则**两者必须取交集（先按名称命中，再按状态/日期命中）。
14. **假设**搜索或筛选生效中，**当**用户点某个命中的会话，**则**必须照常进入该会话；进入后搜索关键词与筛选条件保持（不自动清空）。

***

### 用户故事：侧栏会话内容搜索（优先级：P2）

作为桌面端用户，我希望在同一个侧栏搜索框里输入一段正文就能找到「哪条会话里说过这句话」，点一下直接跳到那条消息，以便几十条会话攒下来后不再靠一条条点开往回翻。

**为什么是这个优先级**：现有搜索只匹配会话标题（[`sessionFilter.ts`](../../../packages/webview/src/utils/sessionFilter.ts) 的实现与其注释都明确「只匹配会话名称、不匹配工作目录名」，消息内容自然也不参与）。而标题往往是自动生成的摘要，用户真正记得住的却是当时的结论、报错原文或某段代码。对齐 Claude 桌面端的会话搜索：它同样是**零索引、每次流式全量扫 jsonl**，靠「候选裁剪 + 每文件首命中即停」控制成本，没有落盘索引也没有增量。

**独立测试**：造两条会话，一条标题含目标词、一条只有正文含目标词；在侧栏搜索框输入该词，验证标题命中的会话仍在树里、正文命中的会话出现在下方「内容命中」区并带片段高亮；点正文命中项验证进入该会话、滚到那条消息、会话内查找条以该词预填打开；输入单字符验证不发起内容搜索；清空关键词验证「内容命中」区整体消失；在有会话正在流式生成时发起搜索，验证流式内容不会把已经显示的结果清空。

**验收场景**：

1. **假设**侧栏搜索框已输入关键词，**当**关键词长度 ≥ 2 个字符，**则**在标题过滤（即时）之外，必须于输入停顿后（防抖，约 300ms）发起一次会话**正文**搜索；关键词不足 2 个字符时不发起正文搜索，只做标题过滤。
2. **假设**正文搜索已返回结果，**则**侧栏必须分成两段展示：上半段保持现有树状标题过滤结果（分组与折叠语义不变，命中逻辑见上一故事的场景 2–5），下半段为「内容命中」平铺列表——每行含会话标题、一段命中片段（命中词前后各截断，命中词高亮）与所属目录名；两段之间有明显分隔，不得把内容命中的会话混进目录分组里。
3. **假设**标题没有任何命中、但正文有命中，**则**不得显示「未找到匹配的对话」空态——两段各自判断空态，标题段为空只影响标题段。
4. **假设**用户点某个内容命中项，**则**必须进入该会话、滚动到命中的那条消息并高亮，同时会话内查找条以该关键词预填打开（能力见 [transcript-find.md](../ui/transcript-find.md)）——落点必须是**那条消息**，不得只打开会话首屏。
5. **假设**正文搜索正在执行，**则**「内容命中」区必须显示「正在搜索会话内容…」；搜索失败时显示「内容搜索暂不可用」并提供重试，不得静默显示成「无结果」。
6. **假设**命中的会话条数或片段条数超过上限（会话数上限 50、每会话片段上限 3），**则**必须只展示到上限并提示「结果过多，仅显示前 N 条」；上限口径固定，不做「加载更多」。
7. **假设**侧栏同时存在生效中的状态/日期筛选，**则**正文搜索的结果必须与筛选取交集（被筛掉的会话不参与正文搜索）。
8. **假设**用户清空关键词或把关键词改到不足 2 个字符，**则**「内容命中」区必须立即整体消失，已发出的搜索请求作废（迟到的响应不得回填）。
9. **假设**某条会话正在后台流式生成，**当**用户发起正文搜索，**则**搜索结果必须基于该会话**已落盘**的转录；流式追加不得让已展示的结果中途清空或重排。
10. **假设**搜索范围覆盖的会话运行在远端主机上，**则**该会话不参与正文搜索（转录在远端 daemon 上、宿主读不到），且不因此报错——远端会话仍可被标题过滤命中。

## 边界情况

* **转录文件不可读 / 损坏**：单条会话读失败必须跳过并继续搜索其余会话，不得整次搜索失败；跳过数量不需要向用户逐个说明。
* **超大转录**：单文件读取必须有上限（超过部分不参与本次搜索），并保证单次搜索有总时间上限——超限时返回已找到的部分并标注「结果可能不完整」。
* **非消息行**：转录里的非消息记录（会话边界、状态事件等）不参与匹配，只有用户与助手的消息正文可搜。
* **大小写与非 ASCII**：匹配不区分大小写，使用完整 Unicode 折叠（中文无大小写，不影响命中）。
* **正在搜索时切换会话**：搜索继续执行、结果照常展示（搜索框的内容不随会话切换清空）。

## 非目标（明确排除）

* **语义搜索 / 向量检索**：只做字面子串匹配，不引入 embedding 或模型参与。
* **正则与整词开关**：侧栏搜索框保持「字面子串」一种模式（这些开关只在会话内查找条上提供）。
* **落盘全文索引**：保持零索引（每次现扫 + 候选裁剪），避免索引失效、磁盘占用与首启预热成本——这是与 Claude 桌面端一致的取舍，代价是「更早的会话可能搜不到」，上限口径见场景 6。
* **重建 / 编辑历史消息**：搜索结果只用于定位，不提供跳转后的内容改写。

***

### 用户故事：历史会话即时进入与恢复加载动画（优先级：P2）

作为用户，我希望点击侧边栏中的历史会话（尤其是远程主机上的会话）时，对话界面立即切换过去，并以已有的扫光加载动画示意会话正在恢复，以便我不再面对点击后数秒无反馈的空白等待。

**为什么是这个优先级**：远程会话的恢复需先建立 SSH 连接、再加载远端会话转录，耗时数秒；本地历史会话的恢复也走同一流程（只是更快）。即时进入 + 加载动画消除「点了没反应」的等待体验，不改变会话切换的任何既有语义。

**独立测试**：在远程主机完成一条会话后切走，再点击该会话，验证对话界面立即切换并以扫光动画显示加载，数秒后消息完整出现；加载期间点击另一条会话，验证最终显示最新点击的会话；断网后点击远程会话，验证加载动画消失并显示错误提示。

**验收场景**：

1. **假设** 侧边栏存在一条未在任何分屏展示的历史会话，**当** 用户点击该会话，**则** 目标分屏必须立即切换为该会话（不等待会话数据加载完成），消息与输入区域显示扫光加载动画示意正在恢复；恢复完成后动画消失、该会话消息完整显示。
2. **假设** 目标历史会话位于远程主机，**当** 用户点击该会话，**则** 行为与本地一致：分屏立即切换并显示加载动画，SSH 连接建立与远端会话转录加载均在动画期间完成，不得让用户在动画出现之前等待连接建立。
3. **假设** 用户以 `Cmd/Ctrl+Click` 或拖拽将历史会话打开到新分屏，**当** 新分屏出现，**则** 新分屏必须立即显示该会话的加载动画直至恢复完成，不得先显示空白新会话界面。
4. **假设** 会话恢复进行中，**当** 用户又点击或切换到另一条会话，**则** 最终必须显示最新一次操作的会话；先前仍在恢复中的会话完成后不得覆盖当前显示（以最新操作为准）。
5. **假设** 会话恢复失败（远端不可达、连接中断、转录损坏等），**当** 恢复结束，**则** 加载动画必须消失，并在该分屏显示可操作的错误提示（与现状「恢复会话失败」一致），不得无限停留于加载动画。
6. **假设** 切换的目标会话已有活跃 agent 在后台运行（正在生成或挂起待确认），**当** 用户点击该会话，**则** 保持现状瞬时切换、不显示加载动画（无需恢复）。
7. **假设** 加载动画显示期间，**当** 用户查看该分屏，**则** 分屏头部与会话树选中态必须已切换为目标会话；动画覆盖消息与输入区域，用户不可输入、不可发送；VSCE/JetBrains 宿主无分屏概念，不受影响。
8. **假设** 会话恢复完成、消息首次渲染，**当** 用户查看该分屏，**则** 消息列表必须自动滚动到底部（最新消息完整可见）；若吸顶用户消息随后在消息流顶部出现（滚动视口已滚出该用户消息），**则** 落点不得被其推高，仍须停在真底部（异步布局变化后补钉，VSCE/JetBrains 初始加载共用此行为）。
9. **假设** 加载动画显示期间（尤其是首次恢复一台从未连接过的远程主机 / WSL 发行版上的会话），**当** 用户查看该分屏，**则** 动画正下方必须按 [stdio-transport.md](../ui/stdio-transport.md)「首次启动的进度文案」显示当前阶段（连接主机、检查环境、下载 / 安装运行时、同步 CLI、下载工具依赖、启动会话），本地与远程一视同仁——本地各阶段通常都在阈值内完成，因此多数时候一个字都不出现；切换会话时文案归属最新的恢复目标，恢复结束（成功或失败）后文案随动画一起消失。

***

### 用户故事：基于分支的 worktree 隔离会话（优先级：P2）

作为用户，我希望在新会话页面选择分支并勾选 worktree，让会话在基于所选分支创建的临时 worktree 中进行，以便我并行试验而不污染主工作区。

**为什么是这个优先级**：worktree 隔离让用户在不污染主工作区的前提下并行试验，是桌面端"边改边试"的核心场景；创建与命名逻辑参考 CLI `wave -w` 的既有实现。

**独立测试**：选择一个 git 仓库作为工作目录，选择某分支并勾选 worktree 后开始会话，验证会话实际在新建的 worktree 目录中进行；不勾选时验证会话直接在所选目录中进行。在创建耗时较长（大型仓库）的场景下验证：发送首条消息后勾选处显示「worktree 创建中…」且勾选框不可操作，创建完成后文案消失、会话在 worktree 中正常开始。在远程主机（分支列表获取耗时较长）场景下验证：切换主机/目录后，分支列表获取超过约 300ms 未返回时，分支选择器与 worktree 勾选项位置显示加载文案，列表返回并确认该目录是 git 仓库后文案消失、控件出现；本地等快速返回的切换不出现加载文案（不闪烁）；切到非 git 目录时最终不显示该组控件、加载文案一并消失。

**验收场景**：

1. **假设**用户选择的工作目录是 git 仓库，**当**处于新会话状态（**这条会话从未产生过任何消息**：宿主按会话自身有无消息判定，不看渲染端列表当前是否为空），**则**工作目录选择器旁必须显示分支选择器与「worktree」勾选项，分支选择器默认选中当前分支，worktree 勾选项默认勾选；**已开始的会话即使列表为空（`/rewind` 回到第一条消息、或 `/clear`）也不得算作新会话状态**——那两处都必须隐藏这组控件，并在发送时按普通消息处理。

2. **假设**用户勾选了 worktree，**当**开始会话，**则**主进程必须基于所选分支创建随机命名的临时分支与 worktree（命名方案与 `wave -w` 一致），会话实际工作目录为该 worktree 路径。

3. **假设**用户未勾选 worktree，**当**开始会话，**则**会话必须直接在所选工作目录中进行（与现状一致）。

4. **假设**用户选择的工作目录不是 git 仓库或 git 命令不可用，**当**处于新会话状态，**则**不得显示分支选择器与 worktree 勾选项。

5. **假设**项目配置了 WorktreeCreate 钩子，**当**勾选 worktree 开始会话，**则** worktree 创建由钩子接管（hook 自行执行 `git worktree add` 并在 stdout 输出 worktree 路径，与 `wave -w` 行为一致）；复用已存在的同名 worktree 时钩子仍执行（由 hook 自行决定如何处理已存在的路径）。

6. **假设**用户已勾选 worktree 并发送首条消息，**当** worktree 创建进行中（大型仓库的 `git worktree add` 可能持续数秒到数十秒，期间首条消息尚未进入会话、新会话页保持可见），**则** worktree 勾选处必须显示「worktree 创建中…」文案向用户说明创建正在进行，且勾选框在此期间不可操作；无论创建成功（会话在 worktree 中开始）还是失败（错误已提示），创建中状态都必须被清除、文案消失。

7. **假设**用户处于新会话状态并切换/选定了某个工作目录（本地或远程主机），**当**新目录的分支查询正在进行或尚未返回（首次连接远程主机时需建立 SSH 隧道与远端 daemon，可能耗时数秒到数十秒），**则**分支选择器与 worktree 勾选项位置必须在查询超过约 300ms 仍未返回时显示加载文案（如「分支加载中…」）——查询在该阈值内返回则不得显示，避免本地快速切换闪烁；加载期间不得显示旧目录的分支名，且占位不得引起控件位置跳动；查询返回并确认新目录是 git 仓库后文案消失、显示控件；若新目录不是 git 仓库或 git 不可用，则文案一并消失、按场景 4 处理（不显示控件）。

8. **假设**用户处于新会话状态且当前工作目录是 git 仓库（分支选择器与 worktree 勾选项正在显示），**当**用户把工作目录切换到非 git 仓库目录，**则**旧目录的分支名不得闪现——分支查询结果按所查询的目录区分，控件显隐最终只由当前目录是否 git 仓库决定：目录一切换即隐藏旧控件，确认非 git 后保持隐藏。新目录的分支查询若在约 300ms 内返回（本地快速切换），则切换过程及完成后均不得出现加载占位（无闪烁）；**当**查询超过该阈值仍未返回，**则**按场景 7 在该位置显示加载文案，返回确认非 git 后文案消失。

9. **假设**用户在一个已开始的会话里 `/rewind` 回到第一条用户消息（转录被清空、列表回到空态）**或**执行了 `/clear`，**当**用户在输入框再次发送消息，**则**必须在**同一个会话**里继续：侧边栏不得多出一条会话、不得按新会话规则创建 worktree、也不得另起一个 `wave --stdio` 子进程；发送后的列表只包含这一轮新消息。

10. **假设**用户在一个已开始的会话里 `/rewind` 回到第一条用户消息（转录被清空、列表回到空态）**或**执行了 `/clear`，**当**用户在输入框再次发送消息，**则**必须在**同一个会话**里继续：不得在侧边栏多出一条会话、不得按新会话规则创建 worktree、也不得另起一个 `wave --stdio` 子进程；发送后的列表只包含这一轮新消息。

***

### 用户故事：删除 worktree 会话前提示将丢失的改动（优先级：P1）

作为用户，我希望在删除 worktree 会话前看到该 worktree 里将丢失的未提交文件与未合并提交，以便我不会在毫不知情的情况下丢掉工作。

**为什么是这个优先级**：删除会话会连同 worktree 目录与临时分支一起删除，是不可撤销的破坏性操作，而在侧边栏只需两次点击即可触发；CLI 退出对话框与 `ExitWorktree` 工具都已有改动提示（见 `multi-agent/worktree.md`），桌面端删除路径此前是唯一没有任何保护的入口。

**独立测试**：在 worktree 会话中留下未提交改动与未合并提交，从侧边栏删除该会话，验证确认对话框列出未提交文件数与未合并提交数；取消后会话、worktree 目录与临时分支全部保留；确认后按既有语义删除。再在干净的 worktree 会话上重复，验证不出现「改动将丢失」提示。

**验收场景**：

1. **假设**待删除的 worktree 会话存在 N（N>0）个未提交改动的文件，**当**用户点击该会话的删除入口，**则**确认对话框必须列明未提交文件数量（如「N 个未提交文件」）并说明删除后不可恢复。
2. **假设**待删除的 worktree 会话的临时分支上有 M（M>0）个未合并到基线的提交，**当**用户点击删除入口，**则**确认对话框必须列明未合并提交数量，并说明该临时分支将被删除。
3. **假设**待删除的 worktree 会话既无未提交改动也无未合并提交，**当**用户点击删除入口，**则**确认对话框不得出现「改动将丢失」类提示，仅保留常规删除确认。
4. **假设**确认对话框已列明将丢失的改动，**当**用户取消，**则**会话、worktree 目录与临时分支三者都必须原样保留。
5. **假设**用户确认删除，**则**按既有删除语义移除会话索引、worktree 目录与临时分支。
6. **假设**待删除会话位于远程主机，**当**用户点击删除入口，**则**改动检查必须在远端执行且结论与本地一致（见「远程会话删除/清理」边界情况）；远端不可达或检查无法完成时，**则**必须退回为通用提示「该会话的 worktree 目录与临时分支将一并删除，未提交的改动将丢失」，不得阻塞删除流程，也不得静默省略提示。
7. **假设**改动检查尚未返回（远程主机或大型仓库可能耗时），**当**用户查看确认对话框，**则**对话框必须处于「正在检查」状态且此时不可确认删除（不得在状态未知时一键删除）；检查返回后按场景 1-3 呈现结果，用户此时取消则关闭对话框、不执行任何删除。

***

### 用户故事：会话切换快捷键（优先级：P3）

作为用户，我希望用键盘快捷键在会话之间循环切换，以便无需鼠标点击侧边栏即可在多个并行会话间移动（与 Claude Code Desktop 的 `Ctrl+Tab` 体验一致）。

**为什么是这个优先级**：多会话并行后切换频次上升，快捷键是效率增强，非基础能力。

**独立测试**：创建多条会话（含跨目录分组），按 `Ctrl+Tab` / `Ctrl+Shift+Tab` 验证正/反向按侧边栏树顺序循环激活会话；macOS 上另按 `Cmd+Shift+]` / `Cmd+Shift+[` 验证等效；查看应用菜单栏验证存在对应菜单项并显示快捷键。

**验收场景**：

1. **假设**侧边栏会话树有多条会话，**当**用户按下「下一个对话」快捷键，**则**必须按会话树的展平顺序激活当前会话的下一条（跨目录分组），当前为末尾时回卷到第一条；「上一个对话」反向循环，当前为第一条时回卷到末尾。
2. **假设**目标会话属于其他目录分组，**当**快捷键切换发生，**则**行为与鼠标点击该条目完全一致：切换到该会话的工作目录（worktree 会话恢复到其 worktree 路径），且不中止任何后台生成中的会话。
3. **假设**折叠分组中包含会话，**当**循环经过该分组，**则**其会话照常参与循环（折叠仅影响展示，不影响循环顺序）。
4. **假设**会话树为空或仅当前一条会话，**当**用户按下快捷键，**则**无任何效果。
5. **假设**用户查看应用菜单栏，**则**必须存在「下一个对话」「上一个对话」菜单项并显示对应快捷键，点击菜单项与按快捷键等效。

***

### 用户故事：后台会话确认提醒（优先级：P2）

作为 CodeWave IDE 桌面端用户，我希望后台会话需要我确认（侧边栏条目出现等待确认的琥珀色状态点）时，应用内弹出 toast 提示，以便我在查看其他会话时不会错过等待我操作的会话。设计依据参考 Claude Code Desktop：权限请求的提醒只在用户未查看该会话时出现（被查看的会话已有确认对话框，toast 只添乱）。toast 仅服务于完全没有分屏承载的会话——只要会话已显示在某个分屏（无论是否当前焦点分屏），其确认对话框都直接可见，不再额外弹 toast。后台会话完成任务的提醒由侧边栏「已完成未读」绿点承载，**不弹 toast**（2026-09-10 拍板：移除「已完成」toast，「已完成」的唯一提醒通道是绿点）。

**为什么是这个优先级**：多会话并排是桌面端核心场景，后台会话的琥珀色状态点只在侧边栏可见，窗口聚焦在其他应用或用户盯着别的分屏时极易错过；toast 复用既有 ToastStack 与 showToast 通道，改动集中在 host 触发点，风险低。系统级通知（macOS 通知中心等）不在本故事范围——既有远程会话约束（见「SSH 远程后台会话」场景 7）保持不变，本故事仅使用应用内 toast。

**独立测试**：在分屏 A 发起会触发确认的会话、切到分屏 B（两分屏并排显示），验证分屏 A 的会话出现确认请求时不弹 toast（对话框已在并排可见的分屏 A 弹出）；关闭分屏 A 使该会话转入后台，再次触发确认，验证弹出带「查看」按钮的 toast，点击「查看」重新打开分屏并展示确认对话框；对无分屏承载的后台会话跑一个短会话，等待其完成，验证侧边栏条目出现「已完成未读」绿点且**不弹 toast**。

**验收场景**：

1. **假设**某会话未在任何分屏显示（如分屏被关闭后会话继续在后台运行、远端会话未打开，侧边栏条目出现等待确认的琥珀色状态点），**当**确认请求到达，**则**弹出应用内 toast，文案包含会话标题与确认类型（如「命令执行待确认」），并带「查看」按钮。
2. **假设**会话已显示在某个分屏（无论是否当前焦点分屏——多屏并排时用户能直接看到该分屏），**当**确认请求在该分屏弹出对话框，**则**不弹 toast（避免重复打扰）。
3. **假设**后台会话的确认 toast 已弹出，**当**用户点击「查看」按钮，**则**应用切换到该会话所在分屏并展示对应的确认对话框（该会话无分屏展示时按既有「历史会话即时进入」逻辑在新分屏打开）。
4. **假设**同一会话在等待确认期间连续发起多个确认请求，**当**后续请求到达，**则**不重复弹 toast（一个等待周期只弹一次；该会话所有待确认请求全部清除后再有新请求，视为新周期可再弹）。
5. **假设**多个后台会话同时等待确认，**当**确认请求到达，**则**各会话独立弹出 toast，互不覆盖。
6. **假设**远端后台会话（无本地分屏）产生确认请求，**当**请求到达，**则**同样弹出应用内 toast（应用内呈现，不发送系统级通知）。
7. **假设**后台会话的确认 toast 弹出后用户未做任何操作，**当**超过既有自动消失时长，**则**toast 按 ToastStack 既有规则处理——确认 toast 带「查看」按钮，不自动消失，等待用户处理或手动关闭。
8. **假设**后台会话的确认 toast 已弹出，**当**用户通过分屏切换（`Ctrl+Tab`/`Ctrl+Shift+Tab`、点击分屏、侧边栏选择等）使该会话进入当前焦点分屏，**则**对应 toast 移除——确认对话框随会话可见弹出（场景 3），toast 不残留重复提示；该会话随后离开焦点分屏不重新弹 toast（同一等待周期只提示一次）。
9. **假设**某后台会话在后台退出计划模式（`ExitPlanMode` 待确认，仅弹 toast、无 UI 计划面板），**当**用户通过侧边栏点击/分屏切换/「查看」使该会话回到前台，**则**该会话的 Plan 面板自动打开并渲染计划全文（与前台会话触发 `ExitPlanMode` 时一致）；无论宿主推送给 webview 的 `desktopPanes`（分屏重新绑定）与携带待确认请求的会话快照是被 React 合批为一次提交还是分次提交，Plan 面板都必须出现，不得因会话切换的重置被抹掉。

***

### 用户故事：会话状态看板（优先级：P2）

作为用户，我希望点击侧边栏品牌行的「活动」按钮打开会话状态看板，按状态（等待中/运行中/已完成）三列总览全部会话，并按项目筛选，以便快速定位某状态下的会话并跳转。

**为什么是这个优先级**：看板是桌面端独有视图（VSCE/JetBrains 无侧边栏不提供），依赖已有的会话树数据，实现成本低，是批次 2 的原型功能之一。

**独立测试**：desktop 点击侧边栏「活动」按钮进入看板，验证三列分类（等待中=待确认、运行中=正在生成、已完成=其余）、列头计数、项目筛选联动、点击会话卡片跳回对应会话、点击左侧会话列表的会话同样跳回并退出看板、右上「返回当前会话」回到原视图、再次点击「活动」按钮关闭看板、侧边栏「活动」按钮高亮态。

**验收场景**：

1. **假设**desktop 侧边栏品牌行可见，**当**用户点击「活动」按钮，**则**会话区切换为会话状态看板视图，品牌行「活动」按钮呈高亮态；看板顶部显示标题「会话状态」与项目筛选下拉，左上提供「返回当前会话」按钮。
2. **假设**看板已打开，**当**用户查看三列，**则**必须为「等待中 / 运行中 / 已完成」三列，列头各带状态色圆点、列名与计数（状态语义对齐视觉设计语言：等待=琥珀、运行中=中性灰、成功=绿）。
3. **假设**会话树中存在会话，**当**看板渲染，**则**每张会话卡片显示标题与所属项目（目录分组名），点击卡片必须恢复该会话并退出看板视图；某列无会话时显示「暂无会话」。
4. **假设**用户选择项目筛选下拉中的某项目，**当**筛选生效，**则**三列仅显示该项目（目录分组）下的会话，计数同步更新；选择「全部项目」恢复全量。
5. **假设**用户在会话进行中（有会话正在生成/待确认）打开看板，**当**状态变化，**则**看板必须实时反映最新状态（会话从「运行中」移入「已完成」等），无需手动刷新。
6. **假设**用户在看板视图点击「返回当前会话」，**当**操作生效，**则**回到打开看板前的会话视图（分屏布局与选中会话保持），「活动」按钮高亮态取消。
7. **假设**看板已打开，**当**用户再次点击侧边栏品牌行的「活动」按钮，**则**看板关闭并回到会话视图（分屏布局与选中会话保持），「活动」按钮高亮态取消——与点击「返回当前会话」等价（按钮为开关：未打开则打开，已打开则关闭）。
8. **假设**看板已打开（用户正在等某个会话跑完、同时看其他会话的进展），**当**用户点击左侧会话列表中的某条会话，**则**必须进入该会话（切换聚焦会话）并退出看板视图、「活动」按钮高亮态取消——左侧列表是看板页里回到某个会话的入口，不得要求用户先关看板再选会话；侧栏的其它「换会话」入口同样先退出看板再动作（Cmd/Ctrl 点击在新分屏打开、新建对话），因为看板替换了 pane rows，动作不退出整页就看不到结果。

***

### 用户故事：跨客户端会话来源（`/resume` 的磁盘会话列表）（优先级：P1）

作为桌面端用户，我希望 `/resume` 能列出**当前对话所属主机**磁盘上存在的会话——包括命令行或其它客户端创建的会话，以便不必回到终端就能继续任意一段对话（当前对话在本地就列本地，在某个远程主机上就列那台主机）。

**为什么是这个优先级**：桌面端会话树只收录本应用创建并登记的会话（见「会话管理」），CLI 那边开的会话在桌面端完全不可见；`/resume` 的磁盘来源是让桌面端与 CLI / 插件端行为对齐的最小改动，也是"在桌面端接着昨天那段 CLI 对话"的唯一入口。

**为什么只列当前主机**：桌面端其余与主机相关的设置（登录态、最近目录、会话索引）都是"当前主机"语义，会话列表沿用同一规则可以避免"列表里混着三台机器的同名项目"；跨主机查看会话不属于本故事，用户切换主机后再次 `/resume` 即得该主机的列表。

**独立测试**：用 CLI 在某个项目目录创建一段会话（桌面端从未登记过它），在本地对话中输入 `/resume`，验证列表中该会话存在且标注项目路径、选中后当前分屏显示该会话历史、后续消息追加到该会话原文件；切到一台远程主机的对话后再次 `/resume`，验证列表换成该主机磁盘上的会话（不出现本地会话）；断开该主机后重新打开选择器，验证给出主机不可达提示、当前会话不受影响。

**验收场景**：

1. **假设**当前对话在本地主机且磁盘上存在会话（含桌面端、CLI、其它客户端创建的），**当**`/resume` 打开会话选择器时，**则**以居中模态对话框承载（含搜索框与列表，渲染在应用根层，分屏状态下同样可用），本地全部项目目录下的会话都被列出，每行标注项目路径；会话索引中不存在的会话同样出现。
2. **假设**当前对话属于某台远程主机，**当**`/resume` 列出会话时，**则**列表为该主机磁盘上的会话（经该主机客户端读取），每行标注项目路径，不混入本地或其它主机的会话。
3. **假设**用户在多个分屏间切换焦点（各分屏可能属于不同主机），**当**再次打开 `/resume` 时，**则**列表随之切换为当前焦点分屏所属主机的会话——范围始终由"当前对话所属主机"决定。
4. **假设**当前主机不可达（远端连接失败、超时、daemon 未启动），**当**`/resume` 列出会话时，**则**给出可见的失败提示（无法连接主机/无法读取会话），不得长时间挂起、也不得静默显示空列表让用户以为没有历史会话；当前会话与内容不受影响。
5. **假设**用户选中的会话属于当前主机，**当**确认选择时，**则**经该主机恢复该会话（与从会话树进入历史会话的既有路径一致），消息与后台任务状态按既有语义加载。
6. **假设**选中的会话所属目录在该主机上已不存在（`ssh test -d` 判定目录确实不存在），**当**确认选择时，**则**不切换、提示「该会话的目录已不存在」，当前分屏与内容保持原状；仅探测失败（主机不可达）时不得据此判定目录缺失。
7. **假设**会话记录在磁盘上缺失或损坏（CLI 报 `Session … not found on disk`），**当**用户确认选择时，**则**与既有历史会话恢复行为一致：给出提示，并按既有规则处理该条目（本地条目从会话索引移除）。
8. **假设**用户成功恢复一条会话索引中不存在的会话（CLI / 其它客户端创建的），**当**该会话进入分屏后，**则**该会话被登记进桌面端会话索引（按当前主机登记）——出现在侧边栏会话树与状态看板中，并在此后重启应用（且仍连接该主机）时仍可再次进入；登记使用该会话磁盘记录中的工作目录等元数据，不改写会话文件本身。
9. **假设**被恢复（或重新接入）的会话在该主机上仍处于运行中，且会话内执行过 `cd`（面板的当前目录已漂移到子目录），**当**该会话进入分屏时，**则**侧边栏目录分组、状态看板与项目级配置仍以该会话的**稳定根**（会话创建时的工作目录；worktree 会话为主仓库根）为准，与新建对话的显示口径一致——不得取会话当前的 cwd。已存在于会话索引中的会话，以索引记录的工作目录与工作目录本身（worktree 会话为其 worktree 路径）为准，而不是 `/resume` 列表携带的元数据。

## 边界情况

* **工作目录不存在或被删除**：会话绑定的 cwd 失效时，agent 文件操作应报错且 UI 能显示该错误。

* **非 git 工作目录或 git 不可用**：不显示分支选择器与 worktree 勾选项，会话直接在该目录进行。

* **首次连接的进度文案**：恢复远程 / WSL 会话时，扫光动画下方按阶段显示当前步骤（连接主机 → 检查远端环境 → 下载 / 安装托管 Node 运行时 → 同步 CLI → 下载工具依赖 → 启动会话），文案集合、阈值与失败口径统一在 [stdio-transport.md](../ui/stdio-transport.md)「首次启动的进度文案」，桌面端只负责把它放在扫光动画下方。它是阶段说明而不是进度条：不显示百分比或剩余时间。

* **「新会话状态」的判据是会话有没有消息，不是列表空不空**：`/rewind` 回到第一条消息或 `/clear` 之后，列表确实为空、界面也回到空态（欢迎页），但这条会话已经存在（有 sessionId、在侧边栏里、可能还有 worktree）——此时发送消息必须走普通发送，在同一会话里继续；只有「宿主刚 spawn、从未产生过任何消息」的会话才走新会话规则（含 worktree 创建）。把「列表为空」当判据的后果是：用户以为在续写原会话，实际多出一条同名会话、还多建一个 worktree。

* **worktree 创建耗时较长**：大型仓库的 `git worktree add` 可能耗时数十秒，期间首条消息尚未进入会话、新会话页保持可见，必须在 worktree 勾选处显示「worktree 创建中…」文案，避免用户误以为界面卡死；创建完成或失败后文案消失，勾选框在创建期间禁用。

* **worktree 创建失败**：分支名冲突、磁盘错误等导致 worktree 创建失败时，必须向用户显示错误，不得静默回退为在原目录开会话。

* **删除 worktree 会话时 worktree 已被外部删除**：git 清理命令失败不得阻塞删除流程，仅删除索引记录并向用户告警。

* **删除 worktree 会话时改动检查无法完成**（worktree 目录已被外部删除、git 不可用、远端主机不可达）：确认对话框退回为通用提示（「该会话的 worktree 目录与临时分支将一并删除，未提交的改动将丢失」），用户确认后照常执行既有删除逻辑——检查失败既不得阻塞删除，也不得省略提示。

* **删除 worktree 会话时改动检查迟迟无响应**（宿主假死、或宿主走了提前返回分支未回复 `desktopWorktreeChanges`）：确认对话框等待一个明确长于正常远端往返的时限后，必须自行退回为通用提示（「该会话的 worktree 目录与临时分支将一并删除，未提交的改动将丢失」）并放开确认按钮——不得因检查不回而永久停在「正在检查该 worktree 的改动…」、永久禁用确认，使该会话无法从 UI 删除。超时后到达的迟到回复必须被丢弃（不得把对话框打回「正在检查」状态或重新禁用已放开的确认按钮）；超时只是放弃精确统计，不等于「无改动」，因此落点是通用警告而非无提示。

* **删除 worktree 会话时会话内 checkout 了其它分支**：只删除该会话自己的临时分支；会话内 checkout 或新建的其它分支必须保留（见 `multi-agent/worktree.md` 的「删除 worktree 只删除自己的临时分支」）。

* **删除当前活跃会话**：允许删除；删除后回到新会话页（销毁该会话的 agent——若为 worktree 会话，须先切回原仓库目录再清理 worktree）。

* **快捷键触发时会话树为空或仅当前一条**：无效果（不报错、不切换）。

* **快捷键循环经过磁盘上已删除目录下的会话**：与点击行为一致——提示目录不存在，从会话索引移除该条记录。

* **后台会话请求权限确认**：后台（非活跃）会话发起工具确认/AskUserQuestion 时不弹窗打断当前视图，该会话挂起等待，侧边栏条目显示"待确认"标识；切回该会话时再弹出对应确认，用户响应后该会话继续。

* **CLI 侧创建的会话不出现在侧边栏**：会话索引仅收录 desktop 内创建的会话，CLI 侧历史会话不展示、也不受影响。

* **不可见会话的确认请求**：不在任何分屏展示的会话发起工具确认/AskUserQuestion 时维持后台挂起规则（见多会话并行的后台挂起规则），重新可见时再弹出；已在分屏中展示的会话的确认直接在该分屏内显示。

* **行状态位渲染规则**：会话条目行状态位（「运行中」loading 环、「待确认」琥珀点、「已完成未读」绿点，见 Figma 13656:5470）中，**「运行中」对任何正在生成的会话渲染**——含处于当前激活对话或已显示在其它分屏的会话（其运行状态虽在该对话/分屏内直接可见，行上仍提示 loading；2026-09-09 拍板「选中的对话也应显示 loading」，反转原「仅后台会话」限制）；**「待确认」与「已完成未读」仍仅对后台会话提示**——会话处于当前激活对话或已显示在其它分屏时不渲染这两类标记（其状态在该对话/分屏内直接可见，打开会话即视为已处理、不再重复提醒）；会话重新落入后台（切走、关闭分屏）后按当时状态恢复标记。三类标记共存时按「待确认 > 运行中 > 已完成未读」优先级提示（后台会话；激活/可见会话只可能出现「运行中」）。

* **后台会话「已完成未读」绿点（newCompleted）**：后台会话在无人查看期间完成任务（一轮生成结束且无待确认请求、该会话未显示在任何分屏）时，其条目状态位显示绿点；会话被打开/聚焦到任一分屏或开始新一轮生成后绿点消失。与「运行中/待确认」同会话同时成立时按既有优先级提示。**绿点是「已完成」的唯一提醒通道**——已完成不弹 toast（2026-09-10 拍板，移除既有「已完成」toast）。

* **后台会话确认 toast 与既有提示的关系**：确认 toast 是「侧边栏琥珀色状态点/运行标识」之外的补充提醒通道，只覆盖**确认请求**一种事件（「已完成」由绿点承载，不弹 toast）；toast 不改变既有后台挂起规则（会话仍需用户切换过去处理确认）；同一会话的确认对话框始终只在用户查看该会话时弹出，toast 的「查看」按钮只是把用户带过去的入口。该 toast 保留既有右下角通知形态（`position: "bottomRight"`），不并入应用级全局 toast 的顶部居中新形态（位置/形态与配色路由见 [desktop-settings.md](./desktop-settings.md)）。

* **恢复加载期间切换/关闭分屏**：历史会话恢复加载中用户切走或关闭该分屏时，先前的恢复完成后不得覆盖当前显示（以最新操作为准）；恢复完成推送时目标分屏已不存在则静默丢弃，不得报错或影响其他分屏。

* **恢复加载期间删除会话**：历史会话恢复加载中用户在侧边栏删除该会话，恢复完成后不得重新出现（会话索引以删除为准）；加载动画随分屏状态一并清除。

* **会话记录写入被中断（应用/CLI 在消息追加中途退出）**：会话 JSONL 末行若是不以换行结尾的残缺记录（中断的未完成写入），恢复时必须丢弃该残片并载入其前全部已完整写入的消息，不得把整个会话判为不可恢复（2026-09-09 拍板：容忍截断尾行）。完整消息批次恒以换行结尾（见 agent-sdk jsonlHandler），无尾换行即唯一标识中断场景；中段行损坏仍属真损坏。

* **`/resume` 的磁盘来源与会话树的关系**：磁盘扫描只服务 `/resume` 选择器本身，侧边栏会话树与状态看板沿用会话索引语义；被 `/resume` 恢复的索引外会话按故事场景 7 登记进索引（此后两条来源一致），未被恢复的磁盘会话不进入索引——两条来源并存是刻意设计，不是待清理的重复。

* **`/resume` 只列当前主机**：范围恒为"当前对话所属主机"（本地对话即本地，远程对话即该主机），与登录态、最近目录、会话索引的既有语义一致；不做跨主机聚合，也不因存在其它已配置主机而扩大范围。切换主机或焦点分屏后再次打开选择器即为新主机的列表。

* **同一段会话被多处同时使用**：`/resume` 不检测会话是否正在终端或另一客户端中被使用，也不因此拒绝切换（本轮不做存活检测，见 [ui/session-management.md](../ui/session-management.md) 边界情况）。

* **重命名弹窗按会话独立**：弹窗里的草稿与校验提示按会话 id 重挂载，一条会话的改名不影响其它会话，改名过程中切换会话不串标题。

* **对正在流式生成的会话改名**：允许（改标题不影响生成），行状态位的「运行中」标识保持不变。

* **自定义标题长度上限**：输入框与宿主持久化边界均限制 200 字符（口径见 [ui/session-management.md](../ui/session-management.md)）；超长输入不入库，不得出现「输入框显示一半、落库另一半」。

* **远端会话重命名**：走该会话所属主机的通道写入远端会话文件；主机不可达时按失败回滚处理，不得只把标题写进本机索引造成两端不一致。

* **会话索引 title 与 SDK 自定义标题的关系**：侧边栏、会话看板与桌面头部读的仍是 desktop 会话索引的 `title`（既有语义不变，索引是 desktop 自维护的缓存）；重命名必须同时把标题写进会话文件的 `custom-title` 保留条目（唯一权威来源，见 [ui/session-management.md](../ui/session-management.md)）。既有的「索引已有 title 就早退」逻辑正好保护手改标题不被首条消息重新推导覆盖；反过来重命名后若只写文件不更新索引，界面不会变化——两处必须一起更新。

* **分组折叠状态记在哪**：按「主机 + 目录」记在本机 webview 的本地存储里（与「侧边栏整条收起」状态同源），不进会话索引、不进 `wave-desktop.json`，也不同步到远端主机；换机器或清掉本地存储即回到默认全展开（没有记录的分组一律按展开渲染）。目录分组从侧边栏消失时不清理其记录——否则目录一空偏好就丢，重新出现时会变回展开。

* **折叠与搜索/筛选叠加**：过滤决定分组与会话是否进列表，折叠决定会话行是否渲染，两者叠加；搜索期间不自动展开命中的折叠分组，也不写入或清除折叠状态。

## 非目标（明确排除）

* **按序号直跳会话**（如 `Cmd+1~9` 跳到第 N 条会话）：首版不做，仅顺序循环。

* **快捷键清单面板**（如 `Cmd+/` 弹出快捷键速查）：首版不做。

* **VSCE/JetBrains 宿主的会话切换快捷键**：IDE 自身占用 `Ctrl+Tab`（编辑器切换器），不提供。

* **折叠分组头部的状态聚合提示**：运行中/待确认标识仅在会话条目上显示，分组折叠时不在分组头部做聚合上卷。

* **会话状态看板在插件端**：看板为 desktop 独有（依赖侧边栏「活动」入口），VSCE/JetBrains 不提供。

* **会话看板的拖拽/批量操作**：看板仅支持点击卡片跳转，不做拖拽排序、批量删除、暂停/恢复会话等操作。

* **`/resume` 的 fork / 参数形式 / transcript 预览**：本轮不做"复制为新会话"（fork）、不做 `/resume <id|搜索词>` 参数形式、不做会话内容预览；也不含会话存活检测与外部会话的信任确认（后两者另开议题）。

* **桌面端会话改名的入口**：只有侧边栏会话行的行菜单。聊天面板头部标题（VSCE / JB / desktop 共用的 webview 组件）在桌面端是**纯展示**——不传改名回调，因此没有 `role=button`、无 hover 高亮、也不是键盘停靠点，整片头部（含标题文字）都留给窗口拖拽（2026-10-30 设计评审拍板去掉头部入口，插件端行为见 [ui/session-management.md](../ui/session-management.md)「会话重命名入口：CLI 与插件端」）；不提供全局快捷键（不照抄 Claude Code 的 `⌘/Ctrl+⌥+R`），不在会话看板卡片与 `/resume`（含头部「历史对话」）列表上提供改名入口。

* **CLI 与插件端的入口分工**：CLI 走 `/rename` 命令 + 选择器内 `Ctrl+R`；插件端只有头部标题。三端共用同一份存储与显示优先级（见 [ui/session-management.md](../ui/session-management.md)）。

* **AI 自动生成标题**：不做（无额外模型调用、无 `ai-title` 保留条目），没有自定义标题时继续用首条用户消息截断兜底。

* **「最近打开」的数据来源（2026-10-21 起）**：这份列表由**该主机自己**持久化在 `~/.wave/recent-workdirs.json`，桌面端按当前主机读取——本机走 stdio 子进程、远端走该主机的 daemon，各自读自己机器上的那份（与远控 Web 页共用同一份，见 [core/recent-workdirs.md](../core/recent-workdirs.md)）。`wave-desktop.json` 里按主机分组的那份降为「该主机版本过旧、没有 `getRecentWorkdirs`」时的回退，并且在回退状态下仍随用户的选择继续更新（否则回退列表再也不长）。写入时机与既有契约不变（场景 9：只由用户主动选择目录更新，会话激活与 agent 工作目录变化都不写），本轮变的只是数据从哪来。

* **工作树路径与移除（2026-08-05）**：列表里不得出现工作树路径——SDK 在读写这份文件时两侧各归一一次（目录本身是 linked worktree 就换算成仓库根，历史条目读出来也会被纠正并与仓库根那条去重，详见 [core/recent-workdirs.md](../core/recent-workdirs.md)）。菜单里的「从列表移除」必须落到**那台机器**（`forgetRecentWorkdir`）后再删本地回退缓存，否则下一次读取会把条目带回来（旧版机器/主机不可达的降级口径同上）。
