子进程与终端
一句话版:
ctx.subprocess是 DSH 的进程底子:负责任何 child-process 的可执行查找、受管进程树、原始或收集 stdio,以及一个深度终端原语(PTY、前台组、会话清理)。命令默认语义、shell、超时、framing 归消费者。
会话里的 bash 工具、LSP、PTY、ACP 都建立在它之上。这一篇讲清"DSH 怎么跑进程"。
一、是什么
ctx.subprocess 是进程半边的执行世界:抽象 SubprocessRuntime 暴露可执行查找、普通受管 spawn,和一个终端进程原语。它的词汇覆盖原始/收集 stdio、进程与终端句柄、退出事实、树/会话清理、受管的 DSH_* 环境命名空间。
| 包 | ctx key | 角色 |
|---|---|---|
subprocess | ctx.subprocess | 服务定义(可执行查找、受管 spawn、终端原语、句柄生命周期) |
subprocess-local | : | 本地 provider(分离进程树、有界收集/spill、node-pty、树信号、terminate-and-join) |
消费它的是 shell executor、LSP host、terminal 后端、ACP backend。
ctx.subprocess不替代ctx.shell:bash 是更上层、面向模型的封装。
二、普通受管 spawn
spawn(spec) 立即返回活句柄;done 在进程关闭时 resolve 退出事实(SubprocessOutcome 无输出、无原因分类,只报 spawn 级失败)。
- spec 完全显式:argv、cwd、每流 stdio、grace:部署相关默认归调用方 config,不藏在 subprocess 默认里
- argv 永不被 shell 解释;要 shell 就自己传
['bash','-c',command] - stdio Node 形状:
'pipe'给你原始流自己 framing(如 LSP JSON-RPC、ACP ndjson);'inherit'透传父描述子;collect 模式({maxBytes, spill?})缓冲有界尾部 + 可选全流 spill 文件
三、收集输出(collect)
- collect readers 取整流字节偏移,从不消费:独立 reader 不会偷走彼此的 delta
- 读时偏移滑出内存尾部 →
lossy,有 spill 则指向 spill 文件 - 收集输出在 settle 后仍可读
四、终止与清理(树级)
- 所有平台树作用域终止(POSIX detached group + direct-child fallback;Windows
taskkill /T) terminate()是唯一终止动词:升级 SIGTERM → grace → SIGKILL(幂等;也由 spec 的 abort 信号驱动;树没了就是 no-op)waitForExit(signal?)观察整树存活,让调用方 teardown 阶梯每层等真实 quiescence- manager 只反应,不分类原因:deadline、teardown 阶梯、原因分类归调用方
五、终端原语(spawnTerminal)
spawnTerminal(spec) 是唯一非 pipe 原语,句柄拥有真实 PTY:
- UTF-8 文本 I/O、前台进程组检查/信号、一次 awaited
terminate() terminate对 provider 仍能观察到的每个会话成员达到 quiescence,并 settle 在途句柄调用- spec signal 只取消分配;已发布句柄自持生命周期
- 顶部进程退出后输出流在排空队列后结束;live transport 失败 reject
done
普通 pipe 无法分配控制终端/清理终端会话成员,所以这是单独一个 substrate 原语。
六、环境净化
scrubbedParentEnv() / SENSITIVE_ENV_PATTERN:丢弃凭据形状和 DSH_* 名字的环境,显式 env 在净化后 merge。本地普通与终端 spawn 都应用它;SDK 管理的传输可以直接 import。
七、终端会话专节(ctx.terminals 与 terminal-bash)
spawnTerminal 是"分配一个 PTY"的底层原语;真正给模型一个持久交互式 shell的是 ctx.terminals 服务缝 + 其 terminal-bash 后端:
| 包 | ctx key | 角色 |
|---|---|---|
terminal | ctx.terminals | owner 作用域的持久终端服务缝:铸不透明 session id、经命名后端路由创建、每个操作栅栏到确切的 live Agent、dispose 时等后端 quiescence |
terminal-bash | —(后端 type: shell) | ctx.subprocess.spawnTerminal 上的持久 shell 后端 |
ctx.terminals本身不含node-pty/沙箱/工具 schema/提示词/任务/终端渲染策略:实现管终端机制,消费者管模型呈现terminal-bash在共享ctx.sandboxPolicy下启动交互 shell:danger-full-access直接起;受限模式要求一个 same-worldctx.sandbox,把 shell argv 包进 confine,没挂就 spawn 前失败- readiness 是组合证据:前台验证的 bash 私有提示符 marker + provider 报的前台 stdin-wait 事实 + 静默回退 + 绝对超时;echo 的输入或更早提示符不能误判为本次 send 已就绪
- send 取消把排队输入标记为取消,再向当前前台进程组发真实
SIGINT;收尾靠terminal_close - 会话进程本地,harness 重启不恢复;同一后端组合本地或远程 execution-world provider
挂载:terminal / terminal-bash 在 base 里不默认挂载;
minimalpreset 以 entry-local realm 把它们连同tool-bash-persistent一起挂(持久 shell 是这个精简组合刻意保留的两个工具之一)。
八、验证
# 看 subprocess 是否装载
dsh web --dump-config | grep -iE "subprocess"
# 会话里看 bash/lsp 子进程(透传的)
zstdcat ~/.dsh/sessions/*/*/session.jsonl.zstd | grep -E '"bash|"tool/' | head