Skip to content

功能规格说明:桌面端会话 ​

本规格描述「一条会话在桌面端怎么被创建、切换、命名、提醒与恢复」。SSH 远端主机接入、远端 CLI 安装与 daemon 常驻见 desktop-remote.md;会话呈现在哪个分屏、侧边栏怎么收起见 desktop-split-pane.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 场景 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 的「会话自定义标题(重命名)」;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 的实现与其注释都明确「只匹配会话名称、不匹配工作目录名」,消息内容自然也不参与)。而标题往往是自动生成的摘要,用户真正记得住的却是当时的结论、报错原文或某段代码。对齐 Claude 桌面端的会话搜索:它同样是零索引、每次流式全量扫 jsonl,靠「候选裁剪 + 每文件首命中即停」控制成本,没有落盘索引也没有增量。

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

验收场景:

  1. 假设侧栏搜索框已输入关键词,当关键词长度 ≥ 2 个字符,则在标题过滤(即时)之外,必须于输入停顿后(防抖,约 300ms)发起一次会话正文搜索;关键词不足 2 个字符时不发起正文搜索,只做标题过滤。
  2. 假设正文搜索已返回结果,则侧栏必须分成两段展示:上半段保持现有树状标题过滤结果(分组与折叠语义不变,命中逻辑见上一故事的场景 2–5),下半段为「内容命中」平铺列表——每行含会话标题、一段命中片段(命中词前后各截断,命中词高亮)与所属目录名;两段之间有明显分隔,不得把内容命中的会话混进目录分组里。
  3. 假设标题没有任何命中、但正文有命中,则不得显示「未找到匹配的对话」空态——两段各自判断空态,标题段为空只影响标题段。
  4. 假设用户点某个内容命中项,则必须进入该会话、滚动到命中的那条消息并高亮,同时会话内查找条以该关键词预填打开(能力见 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「首次启动的进度文案」显示当前阶段(连接主机、检查环境、下载 / 安装运行时、同步 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「首次启动的进度文案」,桌面端只负责把它放在扫光动画下方。它是阶段说明而不是进度条:不显示百分比或剩余时间。

  • 「新会话状态」的判据是会话有没有消息,不是列表空不空:/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)。

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

  • 恢复加载期间删除会话:历史会话恢复加载中用户在侧边栏删除该会话,恢复完成后不得重新出现(会话索引以删除为准);加载动画随分屏状态一并清除。

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

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

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

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

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

  • 对正在流式生成的会话改名:允许(改标题不影响生成),行状态位的「运行中」标识保持不变。

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

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

  • 会话索引 title 与 SDK 自定义标题的关系:侧边栏、会话看板与桌面头部读的仍是 desktop 会话索引的 title(既有语义不变,索引是 desktop 自维护的缓存);重命名必须同时把标题写进会话文件的 custom-title 保留条目(唯一权威来源,见 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「会话重命名入口:CLI 与插件端」);不提供全局快捷键(不照抄 Claude Code 的 ⌘/Ctrl+⌥+R),不在会话看板卡片与 /resume(含头部「历史对话」)列表上提供改名入口。

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

  • AI 自动生成标题:不做(无额外模型调用、无 ai-title 保留条目),没有自定义标题时继续用首条用户消息截断兜底。

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

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