Appearance
功能规格说明:桌面端会话
本规格描述「一条会话在桌面端怎么被创建、切换、命名、提醒与恢复」。SSH 远端主机接入、远端 CLI 安装与 daemon 常驻见 desktop-remote.md;会话呈现在哪个分屏、侧边栏怎么收起见 desktop-split-pane.md 与 desktop-layout.md。
用户场景与测试
用户故事:会话管理(优先级:P2)
作为用户,我希望在应用内新建会话并恢复上次的会话与工作目录,以便我可以在不同任务之间切换。
为什么是这个优先级:会话管理是日常使用的基础能力,且 sidebar 是 desktop 相对插件端的布局扩展。
独立测试:进行一个会话后点击"新对话"开始全新会话;重启应用验证(「最近打开」列表非空时)自动进入最近目录的新对话、(列表为空时)回到选择工作目录的初始状态且自动初始化不重复触发;从侧边栏会话树恢复一条历史会话;删除一条 worktree 会话并验证 worktree 与临时分支被一并清理;纯键盘操作:Tab 逐条遍历会话条目,↑/↓/Home/End 在条目间移动焦点、→ 到达删除按钮、Enter 弹出确认对话框。
验收场景:
- 假设应用正在运行,当用户点击侧边栏"新对话",则必须在当前窗口内开始一个全新会话(回到欢迎页 + 输入框状态);不中止任何正在生成的会话——原会话在后台继续运行,可从侧边栏切回查看结果。
- 假设用户关闭并重新打开应用,当应用启动,则不得恢复上次的会话——每次全新开始(工作目录与主机选择不跨重启持久化):当「最近打开」列表非空(用户曾主动选择过目录),则应用必须自动执行一次与点击侧边栏"新对话"完全一致的初始化(2026-09-08 拍板「刚打开桌面端就跟点击新对话的效果一样」):在聚焦分屏以「最近打开」列表首项(最近主动选择的仓库根,见场景 8)为工作目录启动一个新的空会话,输入框可用、上下文栏显示该目录,用户无需先经目录选择步骤即可直接对话;当「最近打开」列表为空(用户从未主动选择过目录),则维持选择工作目录的欢迎/初始界面(按场景 10 不自动绑定任何工作目录)。自动初始化只在本次启动首次界面就绪时执行一次:webview 重新加载、新增分屏、设置页往返重挂载、聚焦分屏变化等后续
webviewReady均不得重复触发(同一分屏不得出现第二个自动 agent);自动初始化的 agent 生成期间用户点击侧边栏历史会话或以其它操作改变该分屏,则必须以用户最新操作为准——自动生成的新会话不得覆盖用户选择(若其生成已完成则转入后台或销毁,不得孤儿驻留),语义与「历史会话即时进入与恢复加载动画」场景 4 一致。自动初始化后用户仍可照常换目录(工作目录选择器保持可用)、点历史会话或再点"新对话"(场景 8/10 语义不变)。 - 假设侧边栏显示会话树,当用户查看侧边栏,则必须看到二级结构:一级为目录分组(可折叠,折叠状态按「主机 + 目录」记忆并在重启后保持,从未被折叠过的分组默认展开——见「侧栏分组折叠状态记忆」),二级为该目录下全部会话(按创建时间倒序,最新创建在上;会话的后续活跃——发消息、生成结束、被选中——不改变其位置);会话树数据完全来自 desktop 自维护的会话索引,不向 CLI 查询。「同一目录」按归一化后的路径判定:Windows 上
C:\a\repo与C:/a/repo是同一个目录(后者是 worktree 会话的仓库根,取自git worktree list的输出),只出一个分组;分组标题只显示该目录的目录名,不显示完整路径。 - 假设有一个或多个会话正在流式生成回复,当用户查看侧边栏,则每个正在生成中的会话条目的状态位都必须显示「运行中」标识(可同时存在多个;显示在条目的行状态位,视觉实现以 Figma 13656:5470 的 loading 环为准);运行中标识不受「仅后台会话」限制——正在激活对话中生成或已显示在其它分屏的会话同样在行上提示 loading(2026-09-09 拍板「选中的对话也应显示 loading」:即使运行状态在对话/分屏自身可见也不省略行上提示);对应会话生成结束后其标识消失。
- 假设某会话存在等待用户响应的确认请求(工具权限/编辑确认、计划确认、AskUserQuestion 等)且不在当前激活对话、也未显示在其它分屏,当用户查看侧边栏,则该会话条目必须显示「待确认」标识(与运行中标识以颜色区分;待确认与运行中同时成立时优先显示待确认;标识同样只在后台会话渲染——会话正显示在分屏时其确认对话框直接可见,不重复提示);用户响应确认或打开会话处理后其标识消失。
- 假设侧边栏会话树已加载,当用户点击当前目录下的某条会话,则必须恢复该会话到当前窗口;点击其他目录下的会话时,必须切换到该会话的工作目录再恢复;切换会话或目录不中止任何正在生成的后台会话。
- 假设侧边栏会话树已加载,当用户点击某条会话的删除图标并确认,则必须从索引中移除该会话;若该会话是 worktree 会话,还必须一并删除其 worktree 目录与临时分支(删除确认必须先按「删除 worktree 会话前提示将丢失的改动」展示将丢失的改动)。
- 假设用户已主动选择并打开过某主仓库(该仓库出现在「最近打开」列表),当用户点击侧边栏"新对话"或用
Cmd/Ctrl+Click、Cmd/Ctrl+Shift+N新建对话,则新对话的默认工作目录必须是「最近打开」列表中用户最近主动选择的仓库根,与上一会话的状态(是否为 worktree 会话、agent 工作目录是否被 bash 改写)完全无关;新会话页面的分支查询走该主仓库分支。 - 假设上一会话是 worktree 会话或其 agent 工作目录在会话中被 bash 工具
cd改写到了某个 worktree 路径,当用户新建对话,则该 worktree 路径不得被写入或覆盖「最近打开」列表(会话激活与 agent 工作目录变化均不触发「最近打开」写入,该列表只由用户主动选择/切换目录的动作更新),新对话的默认目录仍为用户最近主动选择的主仓库根,分支查询与「最近打开」列表均不残留该 worktree 路径。 - 假设用户从未主动选择过任何工作目录(「最近打开」列表为空),当用户点击侧边栏"新对话"或用
Cmd/Ctrl+Click、Cmd/Ctrl+Shift+N新建对话,则必须进入空白新对话界面(工作目录选择器显示"选择工作目录…"占位入口,不自动绑定任何工作目录,用户可稍后通过该入口选择目录),不得因列表为空而拒绝进入或静默无响应;当用户处于未选择工作目录的空白会话时,则发送入口整体禁用(输入区不可编辑、发送 / 附件 / 快捷指令 / 权限模式按钮一并置灰),并在输入框占位文案写明禁用原因「请先选择项目目录」(该原因与「未登录」互斥且登录优先,见 sso-auth.md 场景 8),不得在无工作目录的情况下启动会话。 - 假设用户在某会话的输入框中输入了尚未发送的内容(如"1"),当切换到另一个会话,则新会话的输入框必须显示其自身的草稿(此前未输入则为空),不得沿用上一会话的输入内容;当切回原会话,则输入框必须恢复此前未发送的内容——输入草稿按会话独立保存,同一分屏内切换会话不得共享草稿,会话在不同分屏间移动时草稿跟随会话。
- 假设用户在某会话输入内容后立即切换到另一个会话再切回(草稿保存尚未完成),当切回原会话,则仍必须恢复刚输入的内容;切换期间展示过的其他会话的输入框不得残留该内容,其草稿不得被写入该内容。
- 假设侧边栏会话树已加载且用户使用键盘,当用户连续按 Tab 键,则焦点必须按显示顺序逐条遍历每条会话条目主体(分组标题同为键盘可聚焦控件,保持可 Tab),不得把任何条目主体移出 Tab 序列;条目的删除按钮除外——它不得进入 Tab 序列,只能通过方向键到达。
- 假设焦点位于某条会话条目上,当用户按
↓或↑,则焦点必须在展开显示的会话条目之间移动(↓下一条、↑上一条,跨目录分组连续行进;到末条后继续↓回绕到首条,反之亦然),移动时列表自动滚动使目标条目完整可见。 - 假设焦点位于某条会话条目上,当用户按 Home 或 End,则焦点必须分别跳到第一条 / 最后一条会话条目上。
- 假设焦点位于某条会话条目主体,当用户按
→,则焦点必须移到该条目的删除按钮上,此时删除按钮变为可见并显示焦点指示;当焦点位于删除按钮上时按←,则焦点回到该条目主体;删除按钮本身不得进入 Tab 序列,只能通过方向键到达。 - 假设焦点位于某条会话条目主体,当用户按 Enter 或 Space,则必须恢复该会话(与普通点击一致);假设焦点位于删除按钮,当用户按 Enter 或 Space,则必须弹出与鼠标点击一致的删除确认对话框。
- 假设焦点位于某个目录分组标题(分组标题为键盘可聚焦控件),当用户按 Enter 或 Space,则必须切换该分组的折叠状态(与点击标题一致)。
- 假设用户在应用内任何位置按 Delete/Backspace 键,则不得因此触发任何会话的删除——删除会话不提供键盘快捷键,唯一路径是方向键到达条目的删除按钮后激活并经确认对话框完成。
- 假设应用刚启动且侧边栏无任何运行中会话——「最近打开」列表非空时应用已按场景 2 自动进入最近目录的新对话(聚焦分屏绑定了一个空会话,窗口处于新对话态)、列表为空时窗口处于选择工作目录的欢迎态;用户尚未主动点开任何历史会话,当用户查看侧边栏"新对话"按钮,则按钮必须保持可用(不得因当前尚无用户激活过的工作目录或会话而禁用);当用户点击该按钮,则宿主自行决定新会话的工作目录——「最近打开」列表非空时取最近主动选择的仓库根(场景 8),为空时进入空白新对话界面(场景 10)——不得要求用户先点开某条历史会话才解锁"新对话";自动初始化已绑定的空会话被再次"新对话"替换属预期(场景 1 语义,空 agent 被回收不残留)。
- 假设应用已通过侧边栏选中并激活某条会话(窗口进入会话态),当用户查看侧边栏"新对话"按钮,则按钮保持可用且行为与场景 1/8/10 一致(替换当前窗口开始全新会话,正在生成的原会话在后台继续)——按钮可用性不得在会话态与初始态之间跳变。
- 假设某目录下已存在会话(无论是否正在后台运行、是否显示在其它分屏),当用户在工作目录选择器中选择该目录(点「最近打开」列表项或「浏览…」选定本地目录)、或经远程目录浏览面板确认该远端目录,则必须在当前分屏开始一个全新会话,不得复用该目录下已存在的会话(不得把池中同目录的会话捞到当前分屏顶替新会话);新会话的初始工作目录即所选目录,与该目录下既有会话的状态(agent 工作目录是否被 bash 改写等)无关;该目录下原有会话必须原样保留、不得被中止或销毁;回到某条已存在的会话只经侧边栏会话树(场景 6),选目录不承担该职责。
- 假设用户在某会话的输入框里输入了尚未发送的内容,当用户打开设置页或会话状态看板(两者都整页顶替对话区,输入框随之卸载)再返回对话窗口,则输入框必须恢复此前未发送的内容——包括刚敲进去、尚未上报宿主的最后一段输入:卸载前必须先把它补报给宿主(宿主按会话记着草稿),重挂载时再按宿主回放的快照回填,不得因为视图切换把这段输入丢掉;输入框里的引用标签(上传附件与 @ 提及文件的文件引用、选区引用、IDE 上下文标签)与文字同等对待,回填后必须仍是可识别的引用标签、按引用语义随消息发出,不得退化成纯文本。
用户故事:侧栏分组折叠状态记忆(优先级:P2)
作为桌面端用户,我希望侧边栏里每个项目文件夹分组的展开/折叠状态被记住,以便重启应用后不用每次都把不想看的分组重新折一遍。
为什么是这个优先级:分组默认全部展开,项目一多每次启动都要逐个人工折叠(每次重启都回到全展开是本条的唯一来源);折叠状态是纯前端的窗口级展示偏好,不碰会话数据、不改会话索引,改动面小。
独立测试:折叠某个目录分组后关闭应用再打开,验证该分组仍是折叠态、其余从未动过的分组仍展开;折叠分组 A 并把原本折叠的分组 B 展开后重启,验证两者各自保持;切到一台远端主机(同一目录路径在本机也有一组),验证两侧分组的折叠状态互不影响;折叠后重启,验证该分组下会话行不渲染、分组标题仍在;在搜索框输入落在折叠分组内的会话名,验证搜索不改变折叠状态。
验收场景:
- 假设侧边栏渲染出若干目录分组,当用户折叠其中一个(点击分组标题,或在标题上按 Enter/Space),则该分组立即折叠(其会话行不再渲染、分组标题保留);当用户随后关闭并重新打开应用(含 webview 重新加载),则该分组必须仍为折叠态,其余从未被改动过的分组按默认展开。
- 假设用户对同一分组多次切换展开/折叠,当应用重启,则以最后一次操作为准(最后一次是展开则该分组展开,是折叠则折叠)。
- 假设同一目录路径在本机与某远端主机上各有一个会话分组,当用户只折叠了本机那份,则远端主机那份必须保持它自己的折叠状态——折叠状态按「主机 + 目录」分别记忆,不按目录名跨主机共享。
- 假设某分组在重启后已无任何会话(该目录不再出现在会话树),当该目录此后重新有会话、分组再次出现,则必须沿用上次记忆的折叠状态,而不是重置为展开。
- 假设某分组当前处于折叠态,当该分组下新建了会话,或该分组内的会话开始生成、进入待确认,则不得自动展开该分组(折叠是用户的显式选择;状态聚合提示仍不上卷到分组头部,见非目标)。
- 假设侧栏搜索/筛选命中折叠分组内的会话,则该分组保持折叠、命中会话不显示,用户展开该分组后即可看到;搜索与筛选不改变记忆的折叠状态。
用户故事:会话重命名(优先级:P2)
作为桌面端用户,我希望从会话行的「⋯」菜单打开一个重命名弹窗改标题,以便不用重开会话就能把某段对话标成自己认得出的名字。
为什么是这个优先级:标题目前不可改,只能靠首条消息截断;侧边栏会话树是高频入口,且行菜单已有「并排打开 / 删除会话」两项,加第三项成本低。存储侧(会话文件里的 custom-title 保留条目、列表读取与显示优先级)见 ui/session-management.md 的「会话自定义标题(重命名)」;CLI 与插件端的入口见同文件的「会话重命名入口:CLI 与插件端」。
独立测试:在侧边栏某会话行上打开行菜单点「重命名」,验证弹出的模态里输入框预填原标题且全选;输入新标题点「保存」后弹窗关闭、行标题立即变化、重启应用后仍是新标题;再试一次按 Esc,验证弹窗关闭且标题未变。
验收场景:
- 假设侧边栏会话树已加载,当用户在某会话行上打开行菜单,则菜单必须含三项,顺序为「并排打开 / 重命名 / 删除会话」(在原两项之间插入);「重命名」不得使用删除项的危险配色。
- 假设行菜单打开且用户激活「重命名」,则必须打开居中模态(不是行内编辑):输入框预填当前标题并全选,弹窗含「取消」「保存」两个按钮;后台列表不得进入 loading / 禁用 / 骨架状态(弹窗是覆盖层,不替换列表)。
- 假设重命名弹窗已打开,当用户点「保存」或按 Enter,则必须保存新标题并关闭弹窗;保存进行中「保存」按钮不得重复提交。
- 假设重命名弹窗已打开,当用户按 Esc 或点「取消」,则必须关闭弹窗且不提交(标题不变、不发请求)。
- 假设输入内容 trim 后为空(仅含空格),当用户点「保存」或按 Enter,则必须不予保存(不发请求),并在输入框下方提示「会话名称不能为空」,弹窗保持打开供用户继续修改;当用户重新输入,则提示必须随之消失。
- 假设用户使用中文/日文等输入法,当处于组合输入中(
isComposing),则不得把 Enter / Esc 当作保存 / 取消处理。 - 假设焦点在弹窗输入框,当用户按键,则输入框必须拦住按键冒泡,不得触发行上的裸键或行菜单快捷键。
- 假设用户提交新标题,当保存进行中,则必须乐观更新——侧边栏行标题、会话看板卡片、该会话(若在当前分屏)的头部标题立即一起变为新标题;假设保存失败,则必须回滚为原标题,弹窗不关闭并就地显示宿主的失败原因(不得只有静默回滚)。
- 假设新标题保存成功,当用户重启应用,则侧边栏该行必须仍显示自定义标题(标题随会话文件持久化,不依赖重启前的内存状态)。
- 假设用户把某会话重命名后继续在该会话中对话,当列表刷新或应用重启,则标题必须保持自定义标题,不得被「首条用户消息截断」的既有 first-wins 逻辑重新覆盖。
- 假设新标题长于会话行宽度,则列表中必须单行截断并显示省略号,不换行、不撑高行高。
- 假设用户完成重命名,则只改会话名称本身:会话内容、会话顺序、所属工作目录分组均不变化。
用户故事:侧栏会话搜索与筛选(优先级:P2)
作为桌面端用户,我希望在侧边栏按会话名搜索、按「会话状态 / 创建日期」筛选会话列表,以便会话攒到几十上百条后还能快速找到那一段。
为什么是这个优先级:会话树目前只有分组折叠,没有检索能力;搜索与筛选都是纯前端对已下发快照的过滤,不改会话数据与排序,实现成本低而收益直接。
独立测试:在侧栏搜索框输入某会话名的一部分(大小写混写),验证列表即时只剩命中的会话、无命中的分组整组消失;输入一个都不命中的词,验证列表显示「未找到匹配的对话」;清空后列表恢复。再打开筛选面板勾选「运行中」并点「确定」,验证只留运行中的会话、筛选按钮变红;点「重置」验证列表不变,再点一次「确定」验证列表恢复。
验收场景:
- 假设侧边栏已加载会话树,则搜索框必须位于「新对话」按钮上方的同一列,右侧为筛选按钮。
- 假设用户在搜索框输入关键词,当输入内容变化,则必须即时过滤(无需回车、无「搜索」按钮):按会话名称做子串匹配、不区分大小写;匹配范围不包含工作目录名。
- 假设某个分组内没有任何会话命中关键词,则必须整组隐藏(分组标题一并隐藏),而不是留一个空分组。
- 假设所有分组都没有命中,则会话列表区域必须显示提示「未找到匹配的对话」。
- 假设用户清空搜索框,则必须恢复为过滤前的会话列表(搜索期间不改变会话数据本身与组内顺序)。
- 假设焦点在搜索框,当用户按 Esc,则先清空关键词;关键词已空时再按一次才让输入框失焦;输入框内的按键不得冒泡触发行上的裸键或行菜单快捷键。
- 假设用户点筛选按钮,则必须展开筛选面板,含「会话状态」(等待中 / 运行中 / 已完成,可多选)与「创建日期」(今天 / 近7天 / 近30天,单选)两组。
- 假设筛选面板已展开,当用户勾选或取消勾选,则列表不得立即变化——必须点「确定」才生效并关闭面板;按 Esc 或点面板外区域关闭面板时,草稿勾选全部丢弃、已生效的筛选保持原样;再次打开面板时,勾选状态必须回填当前已生效的筛选。
- 假设用户选中多个会话状态,则必须取并集(命中任一即显示);状态口径与「会话状态看板」一致:等待确认 > 运行中 > 其余为已完成;侧栏行上的「完成且未查看」绿点不是状态判据。
- 假设用户选择某个创建日期档位,则必须按会话创建时间过滤:「今天」为本地时区当天 0 点起,「近7天」「近30天」为含今天在内的最近 7 / 30 天;会话缺少创建时间时按最近活动时间归类。
- 假设筛选已生效,则筛选按钮必须显示为激活态(红色),再次进入面板可看到同样的勾选。
- 假设筛选面板里已有勾选,当用户点「重置」,则必须只清空面板内的勾选,不改变当前已生效的筛选;需再点「确定」才把筛选清空。
- 假设搜索关键词与筛选条件同时存在,则两者必须取交集(先按名称命中,再按状态/日期命中)。
- 假设搜索或筛选生效中,当用户点某个命中的会话,则必须照常进入该会话;进入后搜索关键词与筛选条件保持(不自动清空)。
用户故事:侧栏会话内容搜索(优先级:P2)
作为桌面端用户,我希望在同一个侧栏搜索框里输入一段正文就能找到「哪条会话里说过这句话」,点一下直接跳到那条消息,以便几十条会话攒下来后不再靠一条条点开往回翻。
为什么是这个优先级:现有搜索只匹配会话标题(sessionFilter.ts 的实现与其注释都明确「只匹配会话名称、不匹配工作目录名」,消息内容自然也不参与)。而标题往往是自动生成的摘要,用户真正记得住的却是当时的结论、报错原文或某段代码。对齐 Claude 桌面端的会话搜索:它同样是零索引、每次流式全量扫 jsonl,靠「候选裁剪 + 每文件首命中即停」控制成本,没有落盘索引也没有增量。
独立测试:造两条会话,一条标题含目标词、一条只有正文含目标词;在侧栏搜索框输入该词,验证标题命中的会话仍在树里、正文命中的会话出现在下方「内容命中」区并带片段高亮;点正文命中项验证进入该会话、滚到那条消息、会话内查找条以该词预填打开;输入单字符验证不发起内容搜索;清空关键词验证「内容命中」区整体消失;在有会话正在流式生成时发起搜索,验证流式内容不会把已经显示的结果清空。
验收场景:
- 假设侧栏搜索框已输入关键词,当关键词长度 ≥ 2 个字符,则在标题过滤(即时)之外,必须于输入停顿后(防抖,约 300ms)发起一次会话正文搜索;关键词不足 2 个字符时不发起正文搜索,只做标题过滤。
- 假设正文搜索已返回结果,则侧栏必须分成两段展示:上半段保持现有树状标题过滤结果(分组与折叠语义不变,命中逻辑见上一故事的场景 2–5),下半段为「内容命中」平铺列表——每行含会话标题、一段命中片段(命中词前后各截断,命中词高亮)与所属目录名;两段之间有明显分隔,不得把内容命中的会话混进目录分组里。
- 假设标题没有任何命中、但正文有命中,则不得显示「未找到匹配的对话」空态——两段各自判断空态,标题段为空只影响标题段。
- 假设用户点某个内容命中项,则必须进入该会话、滚动到命中的那条消息并高亮,同时会话内查找条以该关键词预填打开(能力见 transcript-find.md)——落点必须是那条消息,不得只打开会话首屏。
- 假设正文搜索正在执行,则「内容命中」区必须显示「正在搜索会话内容…」;搜索失败时显示「内容搜索暂不可用」并提供重试,不得静默显示成「无结果」。
- 假设命中的会话条数或片段条数超过上限(会话数上限 50、每会话片段上限 3),则必须只展示到上限并提示「结果过多,仅显示前 N 条」;上限口径固定,不做「加载更多」。
- 假设侧栏同时存在生效中的状态/日期筛选,则正文搜索的结果必须与筛选取交集(被筛掉的会话不参与正文搜索)。
- 假设用户清空关键词或把关键词改到不足 2 个字符,则「内容命中」区必须立即整体消失,已发出的搜索请求作废(迟到的响应不得回填)。
- 假设某条会话正在后台流式生成,当用户发起正文搜索,则搜索结果必须基于该会话已落盘的转录;流式追加不得让已展示的结果中途清空或重排。
- 假设搜索范围覆盖的会话运行在远端主机上,则该会话不参与正文搜索(转录在远端 daemon 上、宿主读不到),且不因此报错——远端会话仍可被标题过滤命中。
边界情况
- 转录文件不可读 / 损坏:单条会话读失败必须跳过并继续搜索其余会话,不得整次搜索失败;跳过数量不需要向用户逐个说明。
- 超大转录:单文件读取必须有上限(超过部分不参与本次搜索),并保证单次搜索有总时间上限——超限时返回已找到的部分并标注「结果可能不完整」。
- 非消息行:转录里的非消息记录(会话边界、状态事件等)不参与匹配,只有用户与助手的消息正文可搜。
- 大小写与非 ASCII:匹配不区分大小写,使用完整 Unicode 折叠(中文无大小写,不影响命中)。
- 正在搜索时切换会话:搜索继续执行、结果照常展示(搜索框的内容不随会话切换清空)。
非目标(明确排除)
- 语义搜索 / 向量检索:只做字面子串匹配,不引入 embedding 或模型参与。
- 正则与整词开关:侧栏搜索框保持「字面子串」一种模式(这些开关只在会话内查找条上提供)。
- 落盘全文索引:保持零索引(每次现扫 + 候选裁剪),避免索引失效、磁盘占用与首启预热成本——这是与 Claude 桌面端一致的取舍,代价是「更早的会话可能搜不到」,上限口径见场景 6。
- 重建 / 编辑历史消息:搜索结果只用于定位,不提供跳转后的内容改写。
用户故事:历史会话即时进入与恢复加载动画(优先级:P2)
作为用户,我希望点击侧边栏中的历史会话(尤其是远程主机上的会话)时,对话界面立即切换过去,并以已有的扫光加载动画示意会话正在恢复,以便我不再面对点击后数秒无反馈的空白等待。
为什么是这个优先级:远程会话的恢复需先建立 SSH 连接、再加载远端会话转录,耗时数秒;本地历史会话的恢复也走同一流程(只是更快)。即时进入 + 加载动画消除「点了没反应」的等待体验,不改变会话切换的任何既有语义。
独立测试:在远程主机完成一条会话后切走,再点击该会话,验证对话界面立即切换并以扫光动画显示加载,数秒后消息完整出现;加载期间点击另一条会话,验证最终显示最新点击的会话;断网后点击远程会话,验证加载动画消失并显示错误提示。
验收场景:
- 假设 侧边栏存在一条未在任何分屏展示的历史会话,当 用户点击该会话,则 目标分屏必须立即切换为该会话(不等待会话数据加载完成),消息与输入区域显示扫光加载动画示意正在恢复;恢复完成后动画消失、该会话消息完整显示。
- 假设 目标历史会话位于远程主机,当 用户点击该会话,则 行为与本地一致:分屏立即切换并显示加载动画,SSH 连接建立与远端会话转录加载均在动画期间完成,不得让用户在动画出现之前等待连接建立。
- 假设 用户以
Cmd/Ctrl+Click或拖拽将历史会话打开到新分屏,当 新分屏出现,则 新分屏必须立即显示该会话的加载动画直至恢复完成,不得先显示空白新会话界面。 - 假设 会话恢复进行中,当 用户又点击或切换到另一条会话,则 最终必须显示最新一次操作的会话;先前仍在恢复中的会话完成后不得覆盖当前显示(以最新操作为准)。
- 假设 会话恢复失败(远端不可达、连接中断、转录损坏等),当 恢复结束,则 加载动画必须消失,并在该分屏显示可操作的错误提示(与现状「恢复会话失败」一致),不得无限停留于加载动画。
- 假设 切换的目标会话已有活跃 agent 在后台运行(正在生成或挂起待确认),当 用户点击该会话,则 保持现状瞬时切换、不显示加载动画(无需恢复)。
- 假设 加载动画显示期间,当 用户查看该分屏,则 分屏头部与会话树选中态必须已切换为目标会话;动画覆盖消息与输入区域,用户不可输入、不可发送;VSCE/JetBrains 宿主无分屏概念,不受影响。
- 假设 会话恢复完成、消息首次渲染,当 用户查看该分屏,则 消息列表必须自动滚动到底部(最新消息完整可见);若吸顶用户消息随后在消息流顶部出现(滚动视口已滚出该用户消息),则 落点不得被其推高,仍须停在真底部(异步布局变化后补钉,VSCE/JetBrains 初始加载共用此行为)。
- 假设 加载动画显示期间(尤其是首次恢复一台从未连接过的远程主机 / WSL 发行版上的会话),当 用户查看该分屏,则 动画正下方必须按 stdio-transport.md「首次启动的进度文案」显示当前阶段(连接主机、检查环境、下载 / 安装运行时、同步 CLI、下载工具依赖、启动会话),本地与远程一视同仁——本地各阶段通常都在阈值内完成,因此多数时候一个字都不出现;切换会话时文案归属最新的恢复目标,恢复结束(成功或失败)后文案随动画一起消失。
用户故事:基于分支的 worktree 隔离会话(优先级:P2)
作为用户,我希望在新会话页面选择分支并勾选 worktree,让会话在基于所选分支创建的临时 worktree 中进行,以便我并行试验而不污染主工作区。
为什么是这个优先级:worktree 隔离让用户在不污染主工作区的前提下并行试验,是桌面端"边改边试"的核心场景;创建与命名逻辑参考 CLI wave -w 的既有实现。
独立测试:选择一个 git 仓库作为工作目录,选择某分支并勾选 worktree 后开始会话,验证会话实际在新建的 worktree 目录中进行;不勾选时验证会话直接在所选目录中进行。在创建耗时较长(大型仓库)的场景下验证:发送首条消息后勾选处显示「worktree 创建中…」且勾选框不可操作,创建完成后文案消失、会话在 worktree 中正常开始。在远程主机(分支列表获取耗时较长)场景下验证:切换主机/目录后,分支列表获取超过约 300ms 未返回时,分支选择器与 worktree 勾选项位置显示加载文案,列表返回并确认该目录是 git 仓库后文案消失、控件出现;本地等快速返回的切换不出现加载文案(不闪烁);切到非 git 目录时最终不显示该组控件、加载文案一并消失。
验收场景:
假设用户选择的工作目录是 git 仓库,当处于新会话状态(这条会话从未产生过任何消息:宿主按会话自身有无消息判定,不看渲染端列表当前是否为空),则工作目录选择器旁必须显示分支选择器与「worktree」勾选项,分支选择器默认选中当前分支,worktree 勾选项默认勾选;已开始的会话即使列表为空(
/rewind回到第一条消息、或/clear)也不得算作新会话状态——那两处都必须隐藏这组控件,并在发送时按普通消息处理。假设用户勾选了 worktree,当开始会话,则主进程必须基于所选分支创建随机命名的临时分支与 worktree(命名方案与
wave -w一致),会话实际工作目录为该 worktree 路径。假设用户未勾选 worktree,当开始会话,则会话必须直接在所选工作目录中进行(与现状一致)。
假设用户选择的工作目录不是 git 仓库或 git 命令不可用,当处于新会话状态,则不得显示分支选择器与 worktree 勾选项。
假设项目配置了 WorktreeCreate 钩子,当勾选 worktree 开始会话,则 worktree 创建由钩子接管(hook 自行执行
git worktree add并在 stdout 输出 worktree 路径,与wave -w行为一致);复用已存在的同名 worktree 时钩子仍执行(由 hook 自行决定如何处理已存在的路径)。假设用户已勾选 worktree 并发送首条消息,当 worktree 创建进行中(大型仓库的
git worktree add可能持续数秒到数十秒,期间首条消息尚未进入会话、新会话页保持可见),则 worktree 勾选处必须显示「worktree 创建中…」文案向用户说明创建正在进行,且勾选框在此期间不可操作;无论创建成功(会话在 worktree 中开始)还是失败(错误已提示),创建中状态都必须被清除、文案消失。假设用户处于新会话状态并切换/选定了某个工作目录(本地或远程主机),当新目录的分支查询正在进行或尚未返回(首次连接远程主机时需建立 SSH 隧道与远端 daemon,可能耗时数秒到数十秒),则分支选择器与 worktree 勾选项位置必须在查询超过约 300ms 仍未返回时显示加载文案(如「分支加载中…」)——查询在该阈值内返回则不得显示,避免本地快速切换闪烁;加载期间不得显示旧目录的分支名,且占位不得引起控件位置跳动;查询返回并确认新目录是 git 仓库后文案消失、显示控件;若新目录不是 git 仓库或 git 不可用,则文案一并消失、按场景 4 处理(不显示控件)。
假设用户处于新会话状态且当前工作目录是 git 仓库(分支选择器与 worktree 勾选项正在显示),当用户把工作目录切换到非 git 仓库目录,则旧目录的分支名不得闪现——分支查询结果按所查询的目录区分,控件显隐最终只由当前目录是否 git 仓库决定:目录一切换即隐藏旧控件,确认非 git 后保持隐藏。新目录的分支查询若在约 300ms 内返回(本地快速切换),则切换过程及完成后均不得出现加载占位(无闪烁);当查询超过该阈值仍未返回,则按场景 7 在该位置显示加载文案,返回确认非 git 后文案消失。
假设用户在一个已开始的会话里
/rewind回到第一条用户消息(转录被清空、列表回到空态)或执行了/clear,当用户在输入框再次发送消息,则必须在同一个会话里继续:侧边栏不得多出一条会话、不得按新会话规则创建 worktree、也不得另起一个wave --stdio子进程;发送后的列表只包含这一轮新消息。假设用户在一个已开始的会话里
/rewind回到第一条用户消息(转录被清空、列表回到空态)或执行了/clear,当用户在输入框再次发送消息,则必须在同一个会话里继续:不得在侧边栏多出一条会话、不得按新会话规则创建 worktree、也不得另起一个wave --stdio子进程;发送后的列表只包含这一轮新消息。
用户故事:删除 worktree 会话前提示将丢失的改动(优先级:P1)
作为用户,我希望在删除 worktree 会话前看到该 worktree 里将丢失的未提交文件与未合并提交,以便我不会在毫不知情的情况下丢掉工作。
为什么是这个优先级:删除会话会连同 worktree 目录与临时分支一起删除,是不可撤销的破坏性操作,而在侧边栏只需两次点击即可触发;CLI 退出对话框与 ExitWorktree 工具都已有改动提示(见 multi-agent/worktree.md),桌面端删除路径此前是唯一没有任何保护的入口。
独立测试:在 worktree 会话中留下未提交改动与未合并提交,从侧边栏删除该会话,验证确认对话框列出未提交文件数与未合并提交数;取消后会话、worktree 目录与临时分支全部保留;确认后按既有语义删除。再在干净的 worktree 会话上重复,验证不出现「改动将丢失」提示。
验收场景:
- 假设待删除的 worktree 会话存在 N(N>0)个未提交改动的文件,当用户点击该会话的删除入口,则确认对话框必须列明未提交文件数量(如「N 个未提交文件」)并说明删除后不可恢复。
- 假设待删除的 worktree 会话的临时分支上有 M(M>0)个未合并到基线的提交,当用户点击删除入口,则确认对话框必须列明未合并提交数量,并说明该临时分支将被删除。
- 假设待删除的 worktree 会话既无未提交改动也无未合并提交,当用户点击删除入口,则确认对话框不得出现「改动将丢失」类提示,仅保留常规删除确认。
- 假设确认对话框已列明将丢失的改动,当用户取消,则会话、worktree 目录与临时分支三者都必须原样保留。
- 假设用户确认删除,则按既有删除语义移除会话索引、worktree 目录与临时分支。
- 假设待删除会话位于远程主机,当用户点击删除入口,则改动检查必须在远端执行且结论与本地一致(见「远程会话删除/清理」边界情况);远端不可达或检查无法完成时,则必须退回为通用提示「该会话的 worktree 目录与临时分支将一并删除,未提交的改动将丢失」,不得阻塞删除流程,也不得静默省略提示。
- 假设改动检查尚未返回(远程主机或大型仓库可能耗时),当用户查看确认对话框,则对话框必须处于「正在检查」状态且此时不可确认删除(不得在状态未知时一键删除);检查返回后按场景 1-3 呈现结果,用户此时取消则关闭对话框、不执行任何删除。
用户故事:会话切换快捷键(优先级:P3)
作为用户,我希望用键盘快捷键在会话之间循环切换,以便无需鼠标点击侧边栏即可在多个并行会话间移动(与 Claude Code Desktop 的 Ctrl+Tab 体验一致)。
为什么是这个优先级:多会话并行后切换频次上升,快捷键是效率增强,非基础能力。
独立测试:创建多条会话(含跨目录分组),按 Ctrl+Tab / Ctrl+Shift+Tab 验证正/反向按侧边栏树顺序循环激活会话;macOS 上另按 Cmd+Shift+] / Cmd+Shift+[ 验证等效;查看应用菜单栏验证存在对应菜单项并显示快捷键。
验收场景:
- 假设侧边栏会话树有多条会话,当用户按下「下一个对话」快捷键,则必须按会话树的展平顺序激活当前会话的下一条(跨目录分组),当前为末尾时回卷到第一条;「上一个对话」反向循环,当前为第一条时回卷到末尾。
- 假设目标会话属于其他目录分组,当快捷键切换发生,则行为与鼠标点击该条目完全一致:切换到该会话的工作目录(worktree 会话恢复到其 worktree 路径),且不中止任何后台生成中的会话。
- 假设折叠分组中包含会话,当循环经过该分组,则其会话照常参与循环(折叠仅影响展示,不影响循环顺序)。
- 假设会话树为空或仅当前一条会话,当用户按下快捷键,则无任何效果。
- 假设用户查看应用菜单栏,则必须存在「下一个对话」「上一个对话」菜单项并显示对应快捷键,点击菜单项与按快捷键等效。
用户故事:后台会话确认提醒(优先级: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。
验收场景:
- 假设某会话未在任何分屏显示(如分屏被关闭后会话继续在后台运行、远端会话未打开,侧边栏条目出现等待确认的琥珀色状态点),当确认请求到达,则弹出应用内 toast,文案包含会话标题与确认类型(如「命令执行待确认」),并带「查看」按钮。
- 假设会话已显示在某个分屏(无论是否当前焦点分屏——多屏并排时用户能直接看到该分屏),当确认请求在该分屏弹出对话框,则不弹 toast(避免重复打扰)。
- 假设后台会话的确认 toast 已弹出,当用户点击「查看」按钮,则应用切换到该会话所在分屏并展示对应的确认对话框(该会话无分屏展示时按既有「历史会话即时进入」逻辑在新分屏打开)。
- 假设同一会话在等待确认期间连续发起多个确认请求,当后续请求到达,则不重复弹 toast(一个等待周期只弹一次;该会话所有待确认请求全部清除后再有新请求,视为新周期可再弹)。
- 假设多个后台会话同时等待确认,当确认请求到达,则各会话独立弹出 toast,互不覆盖。
- 假设远端后台会话(无本地分屏)产生确认请求,当请求到达,则同样弹出应用内 toast(应用内呈现,不发送系统级通知)。
- 假设后台会话的确认 toast 弹出后用户未做任何操作,当超过既有自动消失时长,则toast 按 ToastStack 既有规则处理——确认 toast 带「查看」按钮,不自动消失,等待用户处理或手动关闭。
- 假设后台会话的确认 toast 已弹出,当用户通过分屏切换(
Ctrl+Tab/Ctrl+Shift+Tab、点击分屏、侧边栏选择等)使该会话进入当前焦点分屏,则对应 toast 移除——确认对话框随会话可见弹出(场景 3),toast 不残留重复提示;该会话随后离开焦点分屏不重新弹 toast(同一等待周期只提示一次)。 - 假设某后台会话在后台退出计划模式(
ExitPlanMode待确认,仅弹 toast、无 UI 计划面板),当用户通过侧边栏点击/分屏切换/「查看」使该会话回到前台,则该会话的 Plan 面板自动打开并渲染计划全文(与前台会话触发ExitPlanMode时一致);无论宿主推送给 webview 的desktopPanes(分屏重新绑定)与携带待确认请求的会话快照是被 React 合批为一次提交还是分次提交,Plan 面板都必须出现,不得因会话切换的重置被抹掉。
用户故事:会话状态看板(优先级:P2)
作为用户,我希望点击侧边栏品牌行的「活动」按钮打开会话状态看板,按状态(等待中/运行中/已完成)三列总览全部会话,并按项目筛选,以便快速定位某状态下的会话并跳转。
为什么是这个优先级:看板是桌面端独有视图(VSCE/JetBrains 无侧边栏不提供),依赖已有的会话树数据,实现成本低,是批次 2 的原型功能之一。
独立测试:desktop 点击侧边栏「活动」按钮进入看板,验证三列分类(等待中=待确认、运行中=正在生成、已完成=其余)、列头计数、项目筛选联动、点击会话卡片跳回对应会话、点击左侧会话列表的会话同样跳回并退出看板、右上「返回当前会话」回到原视图、再次点击「活动」按钮关闭看板、侧边栏「活动」按钮高亮态。
验收场景:
- 假设desktop 侧边栏品牌行可见,当用户点击「活动」按钮,则会话区切换为会话状态看板视图,品牌行「活动」按钮呈高亮态;看板顶部显示标题「会话状态」与项目筛选下拉,左上提供「返回当前会话」按钮。
- 假设看板已打开,当用户查看三列,则必须为「等待中 / 运行中 / 已完成」三列,列头各带状态色圆点、列名与计数(状态语义对齐视觉设计语言:等待=琥珀、运行中=中性灰、成功=绿)。
- 假设会话树中存在会话,当看板渲染,则每张会话卡片显示标题与所属项目(目录分组名),点击卡片必须恢复该会话并退出看板视图;某列无会话时显示「暂无会话」。
- 假设用户选择项目筛选下拉中的某项目,当筛选生效,则三列仅显示该项目(目录分组)下的会话,计数同步更新;选择「全部项目」恢复全量。
- 假设用户在会话进行中(有会话正在生成/待确认)打开看板,当状态变化,则看板必须实时反映最新状态(会话从「运行中」移入「已完成」等),无需手动刷新。
- 假设用户在看板视图点击「返回当前会话」,当操作生效,则回到打开看板前的会话视图(分屏布局与选中会话保持),「活动」按钮高亮态取消。
- 假设看板已打开,当用户再次点击侧边栏品牌行的「活动」按钮,则看板关闭并回到会话视图(分屏布局与选中会话保持),「活动」按钮高亮态取消——与点击「返回当前会话」等价(按钮为开关:未打开则打开,已打开则关闭)。
- 假设看板已打开(用户正在等某个会话跑完、同时看其他会话的进展),当用户点击左侧会话列表中的某条会话,则必须进入该会话(切换聚焦会话)并退出看板视图、「活动」按钮高亮态取消——左侧列表是看板页里回到某个会话的入口,不得要求用户先关看板再选会话;侧栏的其它「换会话」入口同样先退出看板再动作(Cmd/Ctrl 点击在新分屏打开、新建对话),因为看板替换了 pane rows,动作不退出整页就看不到结果。
用户故事:跨客户端会话来源(/resume 的磁盘会话列表)(优先级:P1)
作为桌面端用户,我希望 /resume 能列出当前对话所属主机磁盘上存在的会话——包括命令行或其它客户端创建的会话,以便不必回到终端就能继续任意一段对话(当前对话在本地就列本地,在某个远程主机上就列那台主机)。
为什么是这个优先级:桌面端会话树只收录本应用创建并登记的会话(见「会话管理」),CLI 那边开的会话在桌面端完全不可见;/resume 的磁盘来源是让桌面端与 CLI / 插件端行为对齐的最小改动,也是"在桌面端接着昨天那段 CLI 对话"的唯一入口。
为什么只列当前主机:桌面端其余与主机相关的设置(登录态、最近目录、会话索引)都是"当前主机"语义,会话列表沿用同一规则可以避免"列表里混着三台机器的同名项目";跨主机查看会话不属于本故事,用户切换主机后再次 /resume 即得该主机的列表。
独立测试:用 CLI 在某个项目目录创建一段会话(桌面端从未登记过它),在本地对话中输入 /resume,验证列表中该会话存在且标注项目路径、选中后当前分屏显示该会话历史、后续消息追加到该会话原文件;切到一台远程主机的对话后再次 /resume,验证列表换成该主机磁盘上的会话(不出现本地会话);断开该主机后重新打开选择器,验证给出主机不可达提示、当前会话不受影响。
验收场景:
- 假设当前对话在本地主机且磁盘上存在会话(含桌面端、CLI、其它客户端创建的),当
/resume打开会话选择器时,则以居中模态对话框承载(含搜索框与列表,渲染在应用根层,分屏状态下同样可用),本地全部项目目录下的会话都被列出,每行标注项目路径;会话索引中不存在的会话同样出现。 - 假设当前对话属于某台远程主机,当
/resume列出会话时,则列表为该主机磁盘上的会话(经该主机客户端读取),每行标注项目路径,不混入本地或其它主机的会话。 - 假设用户在多个分屏间切换焦点(各分屏可能属于不同主机),当再次打开
/resume时,则列表随之切换为当前焦点分屏所属主机的会话——范围始终由"当前对话所属主机"决定。 - 假设当前主机不可达(远端连接失败、超时、daemon 未启动),当
/resume列出会话时,则给出可见的失败提示(无法连接主机/无法读取会话),不得长时间挂起、也不得静默显示空列表让用户以为没有历史会话;当前会话与内容不受影响。 - 假设用户选中的会话属于当前主机,当确认选择时,则经该主机恢复该会话(与从会话树进入历史会话的既有路径一致),消息与后台任务状态按既有语义加载。
- 假设选中的会话所属目录在该主机上已不存在(
ssh test -d判定目录确实不存在),当确认选择时,则不切换、提示「该会话的目录已不存在」,当前分屏与内容保持原状;仅探测失败(主机不可达)时不得据此判定目录缺失。 - 假设会话记录在磁盘上缺失或损坏(CLI 报
Session … not found on disk),当用户确认选择时,则与既有历史会话恢复行为一致:给出提示,并按既有规则处理该条目(本地条目从会话索引移除)。 - 假设用户成功恢复一条会话索引中不存在的会话(CLI / 其它客户端创建的),当该会话进入分屏后,则该会话被登记进桌面端会话索引(按当前主机登记)——出现在侧边栏会话树与状态看板中,并在此后重启应用(且仍连接该主机)时仍可再次进入;登记使用该会话磁盘记录中的工作目录等元数据,不改写会话文件本身。
- 假设被恢复(或重新接入)的会话在该主机上仍处于运行中,且会话内执行过
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)后再删本地回退缓存,否则下一次读取会把条目带回来(旧版机器/主机不可达的降级口径同上)。