DSH plugin 怎么接终端子系统:持久终端 ID、后端约定、有界读取与 owner 快照
DSH plugin 的终端子系统是「一层服务 + 多种可替换 PTY 后端」:服务铸造 id、管授权与清理,后端只负责把某种类型跑起来。 两条规则最容易踩:授权比较的是确切拥有者 Agent,不是名称也不是猜到的 id;一次发送返回不等于 shell 退出——waitReason 可能只是静默或超时,顶层状态要看 sessionStatus。再加上「同一会话同时只允许一个活动发送」,就构成了这套接口的全部硬约束。
DSH plugin 的终端服务:id 由服务铸造,授权看确切 owner
一句话结论:id 与授权都在服务侧,名称只是展示;owner 不对,操作直接拒绝。
- id 来源:
TerminalSessionId由服务铸造,调用方不自行生成。 - 名称定位:可选名称是拥有者本地的显示元数据,不参与授权。
- 授权对象:比较的是拥有该会话的确切
Agent。 - 活动窗口:
hasOwnerActivity(owner)在 spawn 到 close 全程为真,没有发布空窗。 - 可见性:
list(owner)只返回该 owner 可见的快照,按发布顺序排列。
DSH plugin 的后端约定:spawn 只发布成功的会话
一句话结论:初始化成功才发布;清理失败用专门错误保留,不顶替取消原因。
- 类型稳定:
TerminalBackend.type是稳定类型名,由TerminalSpawnRequest.type选择。 - spawn 语义:返回未发布的会话,或在清理部分资源后拒绝。
- 清理失败:以
TerminalBackendCleanupError拒绝启动,调用方的取消原因仍然保留。 - 后端职责:拥有终端状态,并负责让已捕获的资源完全停稳。
- 服务职责:发布后负责 id 授权与清理;
close(reason)幂等,重复调用共享同一次拆除。 - 初始输出:
motd是terminal_open返回的有界初始输出,别把它当成完整 scrollback。
DSH plugin 的发送与等待:waitReason 与 sessionStatus 是两件事
一句话结论:发送返回只解释「为什么还给你」,会话状态单独说「shell 还在不在」。
| waitReason | 含义 |
|---|---|
stdin_read | 进程在等输入 |
inferred_idle | 推断已空闲 |
timeout | 等待超时 |
session_exit | 顶层 shell 退出 |
- 正交关系:一次发送可能因静默或超时返回,而顶层 shell 仍然存活。
session_exit的范围:表示该 shell 退出,不是任意前台子进程退出。- 会话状态:
{ kind: 'running' }或{ kind: 'exited', exitCode, signal }。 - 发送结果:
viewport是敲定时剩余的界面上有界增量,truncated说明输出是否被丢弃。 - 取消:
cancel()请求SIGINT,操作敲定后返回 false。 - 信号:
signal(owner, id, allowedSignal)只投向已校验的前台进程组。
DSH plugin 的有界读取与输出游标:前台与后台分开
一句话结论:前台拿最终结果,后台拿「读后即推进」的游标;scrollback 另走有界分页。
- 活动发送唯一:每个会话同时只允许一个活动发送。
- 后台任务形态:操作向通用后台任务提供读取后即推进的输出游标,适合长跑进程。
- 前台形态:
done敲定后给viewport、waitReason、sessionStatus与truncated。 - scrollback 分页:
read(owner, id, { offset, count })从保留输出里取有界一页,与发送游标互不影响。 - 上限意识:两处都是有界返回,不要假设能一次拿全量输出。
- 验证:长跑命令用后台游标读增量,再单独翻 scrollback 对比,确认没有重复消费。
DSH plugin 的归属、重载与持久性:PTY 字节不出进程
一句话结论:清理挂在 owner 作用域上,后端与工具插件重载时会话仍存活;原始字节不落日志。
- 清理归属:服务把等待完成的清理附加到确切拥有者作用域,其它 owner 的操作被拒。
- 重载存活:后端或工具插件重载期间,已发布会话保持存活。
- 进程内边界:PTY 状态与原始字节局限在进程内,不随日志持久化。
- 只持久有界内容:模型输入与有界返回输出走现有
tool/call、tool/result与任务结果路径,不重复记录 PTY 会话事件。 - 关闭要等停稳:
kill只有在后端清理完成并静默后才移除会话;重复调用返回 false。 - 诊断:
close(reason)的 reason 会进清理诊断,写清触发来源便于事后追。
DSH plugin 排查注意事项
一句话结论:终端问题先分「授权被拒」「发送被占」「等待误判」「输出被截断」四类。
- 操作被拒 → 检查是否用了确切的 owner Agent,名称与 id 猜测都不行。
- 第二个发送卡住 → 同一会话只允许一个活动发送,先等
done或cancel。 - 以为进程退出了 → 看
waitReason与sessionStatus,别只看发送返回。 - 输出不全 →
truncated为真或读取有界,改用 scrollback 分页。 - 重载后会话丢了 → 检查是否在后端清理阶段被关闭,正常情况下会话应存活。
- 修复状态如实:本文的 id 与授权规则、后端约定、等待原因与持久性边界均取自官方「持久 PTY 会话」页面;接口签名以该页 Cordis API 与你所用版本为准。
来源:持久 PTY 会话、terminal 源码、子进程。
常见问题
不行,DSH plugin 的 TerminalSessionId 由服务铸造,可选名称只是拥有者本地的显示元数据。授权比较的是拥有该会话的确切 Agent,按名称或猜测 id 都不会通过。
不一定,DSH plugin 的 waitReason 说明这次发送为何返回,可能是静默、超时或收到输入;只有 session_exit 才表示顶层 shell 退出。会话状态由 sessionStatus 单独报告,与 waitReason 无关。
不能,DSH plugin 的活跃会话同一时间只接受一个活动发送。第二个发送需要等前一个 done 敲定,或先 cancel;输出游标由该操作内部推进。
不会,DSH plugin 的后端只有在清理完部分资源后才会以 TerminalBackendCleanupError 拒绝启动,清理失败被保留而不替换取消原因。服务也只在初始化成功后才发布会话。
不会,DSH plugin 的 PTY 状态与原始字节局限在进程内。模型输入与有界返回输出通过现有的 tool/call、tool/result 与任务结果路径持久保存,不会重复记录 PTY 会话事件。
相关术语
- TerminalSessionId(终端会话 id)
- TerminalSessionId 是由 DSH plugin 终端服务铸造的品牌化标识,用于授权、读取与关闭。可选名称只是拥有者本地的展示信息,不参与授权判断。— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/terminal
- TerminalWaitReason(等待原因)
- TerminalWaitReason 说明一次交互发送为何把控制权还给调用方,取值为 stdin_read、inferred_idle、timeout 或 session_exit。它与会话顶层状态正交,不代表任意前台子进程退出。— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/terminal
- TerminalBackend(终端后端)
- TerminalBackend 是可替换的单一 PTY 类型提供方,按稳定 type 被 spawn 请求选择。它负责启动会话与检测就绪,并在无法清理部分资源时以专门错误拒绝。— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/terminal
- TerminalSendOperation(发送操作)
- TerminalSendOperation 是一次活动发送,提供 done 结果、readOutput 输出游标与 cancel。每个 PTY 会话同时只允许一个这样的操作。— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/terminal
来源
- DeepSeek Harness 文档 — 持久 PTY 会话(标识、后端、发送、归属与持久性)· deepseek-harness.github.io
- terminal 包源码(跨包词汇与类型)· deepseek-ai(GitHub)
- DeepSeek Harness 文档 — 子进程(PTY 依赖的进程原语)· deepseek-harness.github.io