# Agent Note: 独立的全屏 TUI 入口

Status: implemented

[English](2026-07-17-dedicated-full-screen-tui-front-door.md) | 中文

## 问题

在本入口引入时，面向行的 agent 负责 pipe 与普通终端，但全屏 coding 界面必须负责原始输入、差分绘制、光标状态、浮层和终端恢复。把这两类契约合并到一个 UI 插件中，会迫使面向 stream 的路径依赖仅适用于 TTY 的生命周期。后续的[移除重复 agent 决策](../simplification/2026-07-20-remove-stdio-and-echo-agents.md)移除了这个面向行 agent；本 Note 继续负责 TUI 设计。

交互通道必须继续作为 Cordis 插件，使用与其他入口相同的 agent（智能体）、会话、工具和用户交互服务。它需要恢复持久历史、跟随压缩替换、显示工具自有的呈现内容，并在启动失败和资源释放时恢复终端。独立聊天应用或第二套 agent 组合会在插件图之外重复实现这些行为。

## 决策

DeepSeek Harness 将 [`@deepseek-ai/dsh-tui`](../../../../packages/ui/tui/README.md) 作为独立的 Cordis 插件交付。该插件只负责终端输入与呈现；agent 生命周期、会话持久化、工具执行以及模型可见的提问工具仍由不同组合项负责。插件要求 stdin 和 stdout 均为 TTY；条件不满足时会失败，不会静默切换为逐行输出。

应用组合层只有一个终端入口。`@deepseek-ai/dsh-tui-demo` 在已配置 agent 之前挂载 TUI，`examples/tui-agent` 直接拥有交互式 coding 组装及其 Code Mode overlay。非交互任务使用 `@deepseek-ai/dsh-cli-demo`；ACP 仍是独立的编辑器协议。

所选入口接收预创建 agent 使用的同一个新建或恢复 `SessionId`。入口先于 agent 组合挂载，等待相符的根 agent 出现，然后才进入全屏模式。因此，相符的 `agent-loop/config-start-failed` 事件会在接管屏幕前报告，并以状态码 1 退出。

### 会话投影与交互

TUI 从活跃的 `session.surface` 重建 transcript（文本记录），并在事件携带 `surfaceOp` 时重新投影，因此恢复或压缩后的历史与模型可见会话保持一致。TUI 渲染 Markdown 文本与推理、token 用量、最新 `todo/write` 计划，以及各工具定义通过 `presentCall` 和 `presentResult` 方法生成的工具卡片。较长的工具卡片正文会保留可配置的头尾预览，并显示隐藏行数；一个终端控制可以展开或收起全部卡片。进行中的分片与工具调用会更新同一组组件，随后由完成事件收束状态。

agent 空闲时，编辑器输入调用 `agent.send()`；轮次运行中则调用 `agent.steer()`。取消、推理显隐、工具卡片展开、重绘、清空 transcript 和退出都只是终端控制。空闲态页脚根据 `tokenMeter` 得出上下文占用率，并显示所选模型；agent 运行期间，该摘要会替换为带已用时长的活动指示和 Escape 中断提示。`/status` 在这两种状态下均可用，并会追加一份仅在终端显示的详细快照，其中包括会话标识与时间戳、所选模型及推理显隐状态、从事件日志归并得出的生命周期计数、与页脚一致的去重用量分项和 KV 缓存命中率，以及 `tokenMeter` 给出的上下文用量和所选模型公布的容量。插件注册共享的 `userInteraction` 提供方，在左下角宽幅键盘操作面板中呈现排队的问题，面板显示批次进度、带编号的选项和对齐的描述；agent 行为和答案日志仍由既有服务负责。

`/model` 命令将建议性的 `ctx.llm` 目录呈现为键盘选择器，并且只更改当前 TUI 会话的目标；带参数的形式仍可直接选择目标。agent 作用域内的 prompt 组装和请求两条 waterfall（瀑布式事件）会为每个 step 快照一次同一个提供方/模型字段组合，因此即使命令在组装期间到达，`{{provider}}` / `{{model}}` 插值与请求路由也不会分裂。系统通过日志中最新的请求头恢复已经使用过的目标；未被请求使用的选择只保留在当前进程中。

### 终端所有权

在模型输出、会话数据、工具呈现、问题、配置或诊断信息进入 pi-tui 或终端标题前，`displayText()` 会把换行之外的 C0 和 C1 控制字符显示为十六进制转义文本。只有 TUI 和 pi-tui 可以生成 ANSI 控制序列。

内置配色仅使用标准 16 色 ANSI 前景色和 SGR 属性，正文文字和背景沿用终端默认值，选中项使用反显。因此，宿主终端可以直接按浅色或深色主题重映射界面，无需 TUI 专用主题设置；`color: false` 会移除样式。

## 验证

已实现的 [TUI 终端状态快照 Agent Note](../testing/2026-07-18-tui-terminal-state-snapshots.md) 规定四层验证契约：直接行为测试、瞬态语义终端快照、通过生产工具执行的已录制 JSONL 流程，以及 Loader/PTY 冒烟测试。包（package）README 负责记录配置、命令、模型可见效果和当前限制。

## 曾考虑的替代方案

- **把 readline 与全屏模式都保留在 `@deepseek-ai/dsh-stdio` 中**：不予采纳，因为逐行输出和差分 TTY 渲染具有不同的依赖、输入规则、日志所有权和资源清理义务。拆分为独立包可以让管道安全契约保持精简、明确。
- **当任一进程流不是 TTY 时，让 TUI 插件静默降级**：不予采纳，因为回退会掩盖部署错误并改变交互语义。应用包可以通过 `auto` 选择入口；明确挂载的 TUI 会快速失败。
- **把 TUI 接线与测试保留在 readline `repl-agent` 叶节点下**：不予采纳，因为一个叶节点会代表两个不同入口，也会破坏它与 `acp-agent` 的对称性。独立的 `tui-agent` 叶节点负责 TUI 浮层和测试，同时复用 repl-agent 的后端组合。
- **在 `/model` 运行时修改 `agent.options`**：不予采纳，因为创建选项无法在异步 prompt 组装与请求路由之间提供原子边界。agent 作用域内的 waterfall 会在保持创建输入不可变的同时，为每个 step 快照一次选中的字段组合。

## 后果

- 交互式终端拥有带状态的 Markdown、卡片、计划和提问界面，无需再对齐第二套终端协议。
- TUI 会引入 pi-tui 依赖并严格要求 TTY；非 TTY 部署使用 Headless app 或结构化协议。
- 会话投影使恢复和压缩与持久会话保持一致，但只有一个已配置会话拥有 transcript 和编辑器。
- 工具包通过既有呈现方法扩展终端卡片，无需在 TUI 中增加工具专用分支。
- 模型选择使用适配器提供的目录元数据，但不会把目录成员关系变成请求校验；未使用的选择不属于持久化状态。
