# Agent Note: 产品级 TUI 会话恢复

Status: implemented
Archived: 2026-08-04

[English](2026-07-21-tui-resume-command.md) | 中文

## Problem

原有 `/resume` 只会打印 shell 命令。使用键盘操作的用户无法查看标题或结果、区分日志损坏与适配器缺失，也无法安全移交终端。退出 TUI 后手动启动命令还掩盖了必要的操作顺序：等待当前工作结束并将其刷写，释放 UI 和应用，再恢复持久化的原有身份，绝不能静默创建替代会话。

## Decision

`/resume` 使用 TUI 现有的交互式浮层接口，但以占满 viewport 的选择页呈现，而不是居中弹窗。这个扁平页面把搜索框、workspace 作用域行、候选项和快捷键页脚放在稳定的屏幕区域，只有当前行使用强调色。搜索编辑器紧跟搜索图标起始，并输出 pi-tui 的光标标记，因此终端输入法的组合文本会锚定在输入框中。查询非空时，第一次按 Escape 会清空查询，第二次才关闭选择页。页面按日志记录的最后活动时间排列候选项，并支持按日志内标题或 id 搜索。每个候选项都会显示是否为当前会话、是否活跃、是否已持久化，最近一个轮次的结果，最近使用的提供方／模型，以及可用时的持久化目标阶段；id 作为次要信息显示。当前会话和已在本运行时中处于活跃状态的会话仍会显示，但不可选择。选择页打开时位于当前 workspace，并通过[跨 workspace 恢复](2026-07-28-cross-workspace-resume.md)记录所拥有的作用域切换到达其他每一个 workspace。

`session-query.readSession()` 提供一份脱离运行时的完整日志，并通过恢复流程所用的同一核心回放边界完成验证。TUI 从该日志中折叠出标题和目标状态。候选项加载失败时只影响该行；选择候选项后会复查日志、workspace、路由、当前 agent 的空闲状态，以及针对当前会话和已在本运行时中处于活跃状态的会话的排除规则，避免陈旧列表绕过预检。适配器缺失时会报告会话完整但路由不可用。该预检不会锁定目标，也不会排除其他进程。

预检通过后，TUI 会刷写当前会话，再次确认其 agent 仍处于空闲状态，然后停止终端并以经过验证的 id 和目标 workspace 调用 `TuiRuntime.handoffResume`。已交付的 `dsh` 宿主会释放根应用，并使用带有规范化 `--resume` 参数的 `process.execve` 原子替换当前进程，而不是启动子进程。恢复后的应用发布相同的 `SessionId`；常规回放会还原 transcript（文本记录）、标题、待办事项和持久化目标状态。系统会有意解除目标的激活状态，TUI 则要求用户确认继续或执行 `/goal resume`。

退出时打印的行是启动器拥有的上下文插槽，而非配置模板；不支持原地交接的宿主会说明会话仍可恢复，而不再给出它无法构造的命令。[由启动器持有的会话身份与退出行](../architecture/2026-07-28-launcher-owned-resume-identity.md)记录了这次所有权迁移，并取代本记录最初交付的 `resumeCommand` 配置键。TUI 仍然绝不执行 shell 文本。

## Alternatives considered

**让 TUI 创建恢复命令进程。** 否决：该文本是展示用文案，不是可信的参数列表，且 TUI 不拥有应用拆卸或进程生命周期。受约束的宿主接口只接收经过验证的 `SessionId`。

**在现有 TUI 内构造恢复后的 agent。** 否决：在表现层替换由配置创建的 agent，会跨越 Loader 所有权、作用域插件初始化、持久化资源释放和终端生命周期。释放根应用并替换进程可以复用受支持的启动路径。

**把适配器缺失视为会话缺失。** 否决：存储有效性和当前路由可用性是相互独立的事实。选择器会保留该行，并指出不可用的提供方／模型。

**恢复会话时延续目标激活状态。** 否决：持久意图并不代表跨越用户或进程边界后仍获授权继续执行。目标阶段会保留，但不会自动续跑。

## Consequences

- 预检不会串行化不同进程；多个进程可以并发选择或恢复同一个持久化会话。
- `/resume` 依赖 `session-query` 发现会话并读取完整日志，但持久化和宿主交接仍是可选功能；没有宿主时，命令回退仍可使用。
- 进程替换会有意重启 Loader 组合。系统会重建仅存在于运行时的状态，而只有日志或会话头部记录的会话状态能够保留。

## Testing

TUI 包（package）测试覆盖键盘导航、标题／id 搜索、清空搜索后再取消、agent 运行期间拒绝恢复、拒绝恢复当前会话和已在本运行时中处于活跃状态的会话、路由缺失、损坏的候选行、预检复查、无宿主时的告警，以及停止终端先于宿主交接的顺序。session-query 测试固定脱离运行时的完整日志验证。agent-loop 恢复测试固定会话身份和历史完全一致；标题、待办事项和目标回放测试套件固定这些投影均可恢复，且目标激活状态已经解除。包级语义快照固定全屏选择页和输入法光标锚点；交付 TUI 的部署负责其进程与 PTY 交接验收。
