Files
deepseek-harness/.agents/notes/implemented/feature/2026-09-09-web-sidebar-terminal.zh.md
T

8.3 KiB
Raw Blame History

Agent Note: Web sidebar terminals

Status: implemented

English | 中文

问题

Web 用户需要在 Session 旁使用交互式 shell 检查工作区和运行命令。Agent 的持久终端工具控制提示符并等待语义结果;人工终端需要原始键盘输入、正常 shell 配置和完整屏幕。命令仍在运行时,浏览器渲染和网络连接可能消失。

决定

开始页入口声明 provider 内稳定的 id。keyed sidebar.right.tab.guide.entry slot 按当前生效的 provider id 分发,因此替代 builtin 的 extension 也控制对应入口的渲染。侧栏负责卡片排列和默认回退,provider 组件负责自己的控件,并通过框架 hook 读取所在标签页。入口 slot 可以承载 shell 菜单,无需替换整个开始页或把交互按钮嵌套在另一个按钮内。

应用主题提供终端的默认颜色。终端正文读取解析后的 CSS 令牌,仅在颜色变化时更新 xterm。公开的 OSC 解析观察器将索引色和默认颜色覆盖与 DSH 默认值分开保存;重置命令先删除对应覆盖,再恢复当前主题。观察器将查询和颜色处理交给 xterm。xterm 的最小对比度调整改善文字可读性,同时不重新映射 ANSI 背景色。DOM 光标在每次渲染后读取单元格实际背景,通过局部 CSS 变量使用有足够对比度的填充色,因此光标移动不会重置调色板。浏览器检查覆盖索引色、真彩色、反色单元格、明暗主题切换、OSC 保留和重置,以及闪烁光标样式。

api-terminal-controller 按 Session 管理用户终端并提供 terminal Remote namespace。ui-sidebar-terminal 注册原生右侧栏标签页,使用 xterm.js 渲染和 FitAddon 测量尺寸。终端开始页卡片的主操作打开上次选择且仍可用的 shell,独立菜单提供已安装 shell。选择菜单项会记录路径并立即打开新终端;仅探测 shell 不分配进程。每个标签页拥有自己的启动和关闭生命周期。Host 探测会验证配置的候选,并把执行环境默认项放在首位;创建只接受当前探测返回的路径。浏览器在当前站点 localStorage 中记住上次选择的 shell 路径,该路径不可用时回到当前默认项。终端类型声明独立实例,因此打开或停靠标签页时,普通页面的去重规则不会合并不同进程。已有侧栏控件负责打开更多标签页,双击标签页标题可重命名终端。终端进程使用组合的 subprocess provider;用户终端权限规定其独立于 Agent 的执行权限。shell 在探测和创建时解析;读取限制和重新连接已有进程不依赖默认可执行文件仍然可用。交互式 shell 配置提供 Tab 补全和可选的内联建议。

关闭和替换会同步移除标签页,并在后台清理进程。Client 先以终端独立的 localStorage key 保存未完成的关闭请求;成功后删除,启动时重试剩余请求。清理失败没有通知或手动重试操作。保存布局之外的 Host 终端不会自动重新打开,也没有 UI 恢复入口。独立 key 避免其他窗口覆盖无关的清理请求。折叠、切换标签页或 Session、浮动、全屏和浏览器断线均保留进程。组件清理和 TabDomain.signal 只停止浏览器工作,因为插件重新加载也会结束这些生命周期。进程清理失败时保留所有权,包括分配完成但 create 尚未发布时的失败。Session owner 和 Host 插件卸载也会清理终端。 明确的 Session 不存在响应会清除已保存的关闭请求,因为进程清理由 Session 负责;传输失败仍可重试。Client 插件卸载等待所有断开的流结束,避免替换插件继承未完成的 Client 清理。

侧栏布局持久化与 provider 恢复负责浏览器刷新:先恢复布局和标签身份,再由 terminal provider 重连视图。列表仍直接使用 Session ID,因为历史记录可以比 Agent 和终端 owner 存活更久;离线 Session 没有需要恢复的保留终端。只有新视图可以分配进程,恢复目标消失时显示错误。进程状态、标题和屏幕内容仍以 Host 为准。

调用者在创建之前保留终端 ID。相同 Session 和未关闭 ID 的重复 create 不再分配进程。创建结果不确定时,关闭仍使用该 ID,即使没有收到创建响应。Host 在等待分配完成前记录已关闭 ID,防止迟到的 create 复活已关闭终端。每次连接先接收一致、有界的 xterm 序列化屏幕,后续有序输出使用 Gateway 已有的复用 Remote stream。输出和屏幕快照共享操作队列。订阅者正常关闭时保留末尾输出,缓存超限时明确失败。浏览器在完成渲染后确认帧,避免 React 批处理丢失增量。

最新连接控制输入和尺寸,其他连接保持只读。每次物理流建立都创建新的输入连接标识,包括传输自动恢复。输入 RPC 按序发送,旧连接的结果不能把新连接降级为失败。终端输出不产生模型输入、Agent 工具结果或 Session 事件。Agent 持久终端决策仍约束模型拥有的终端;此功能为可移植 subprocess provider增加执行环境事实和 resize。 控制权转移和进程退出导致的拒绝只禁用输入,保留健康的输出视图。Client 自产错误标识由终端 UI 翻译,包括名额用满时关闭已保留的退出终端的提示。

考虑过的替代方案

在浏览器保存完整 shell profile。 用户偏好只包含选中的路径。参数和可用性由 Host 探测决定;持久化这些信息会让过期 profile 绕过当前执行配置。终端列表和标签页控件仍由侧栏负责。

进程清理完成前保留标签页。 缓慢或失败的终止会拖延用户关闭操作。保存清理意图后,可以立即移除标签页,同时保留错误反馈与重试。

持久化作为权威的活跃进程注册表。 浏览器进程状态可能独立于 Host 状态失效。布局持久化决策取代了不保存标签关联的限定,同时保留 Host 对进程存活状态的决定权。

共用 Agent 终端注册表。 受控提示符和语义化 send/wait 会改变人工 shell 配置并混淆进程所有权。用户终端只共享 subprocess 能力。

增加专用终端 WebSocket。 现有 Remote stream 已管理认证、取消和重连。第二条传输通道会重复这些职责。

只重放有界原始字节尾部。 字节尾部可能从转义序列中间开始,或缺失备用屏幕切换。序列化终端屏幕能在保留有界历史的同时提供一致恢复点。

React 正文或 tab signal 卸载时 kill。 组件卸载、Session 切换和插件重载都可能结束这些生命周期,而用户并未关闭终端。清理必须跟随显式关闭操作。

自建 Web 补全引擎。 shell 原生补全通过普通 PTY 输入处理命令、路径和已配置插件。独立补全界面需要针对 shell 解析和同步,不属于此功能。

影响

保留终端会保留进程和有界屏幕内存。刷新恢复侧栏布局并重连 Host 保留的终端;Host 重启不恢复进程。shell 退出后保持可见,不自动重启。后台清理可能比标签页存活更久,浏览器存储不可用时只能在当前页面保留重试能力。原生 PTY 支持和后代进程清理保证仍由 provider 决定。单一可写连接避免竞争的输入和尺寸流,显式接管允许从另一页面恢复操作。Session 的沙箱模式改变时,用户终端保持打开。

Agent 终端和可移植执行环境两篇记录仍保留,其所有权与 provider 决策继续独立有效,不被浏览器终端取代。

无人连接终端的两小时回收在全部前端持有者断开后设置空闲宽限期,保留忙碌或状态不确定的工作。

相关

  • Remote 双工流:传输层上行,让一条 attach 流取代 unary write、resize 与 attachmentId 关联;该终端改动列在其「后续」中。