docs(subprocess): bilingual pair for the consumer-migration Agent Note

Adds the zh counterpart, aligns the seam note's zh side with its updated
interface bullet and follow-up sentence, records both pairs, and ratchets
the translation-pairing manifest.
This commit is contained in:
Tianyi Cui
2026-07-26 15:51:24 +08:00
parent 3672cd25b4
commit 3c98ade986
5 changed files with 49 additions and 4 deletions

View File

@@ -0,0 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
2026-07-26-subprocess-consumer-migration.md: 805f27ba2e72a7f29d1b32053add95b8c33a62e2
2026-07-26-subprocess-consumer-migration.zh.md: 039d32d9c737970153e91cfa288e4c9616813f7d

View File

@@ -0,0 +1,38 @@
# Agent Note: 进程 seam 转向 Node 形状,所有具备条件的 spawn 调用点一并迁入
Status: implemented
[English](2026-07-26-subprocess-consumer-migration.md) | 中文
## 问题
[进程 seam](2026-07-26-subprocess-seam.md) 交付时恰好只为一个消费方家族塑形:批量收集的 stdout/stderr、批量 stdin、单一的升级式 `kill()`。那是有意的范围控制,其自身的 Agent Note 也把「迁移其余 spawn 调用点」记为暂缓否决项。引入该 seam 的 PRPull Request上的评审推翻了这一暂缓决定堆叠其上的后续变更应当把接口向 Node 的 API 方向重塑,并把其余运行进程之处迁到该服务上。其余各 spawn 调用点此前各自持有同一套机制中某个切片的私有副本——lsp-local 自带 detached 进程树信号发送POSIX 进程组 + Windows taskkill + 存活轮询subagent-subprocess 自带 dispose资源释放阶梯和自己的凭据清除mcp-client、pty-local 与 SDK helper 则各自持有凭据清除的第三、第四、第五份副本——而这一切既不可替换,也无法集中测试。
## 决策
这道 seam 的词汇如今已是 Node 形状,凡能搭乘该服务的 spawn 调用点均已迁入:
- **按流划分的 stdio 处置方式disposition**,位于 `SubprocessSpawnSpec` 上:`'pipe'`(裸的 `Readable`/`Writable`,供消费方自有的协议分帧使用)、`'inherit'`诊断输出直通父进程的流以及收集模式collect`{ maxBytes, spill? }`——即最初的有界尾部保留形状,只是 spill 文件改为可选,使诊断尾部(例如语言服务器的 stderr无需落盘即可缓冲。stdin 则为 `'ignore'``'pipe'``{ data }`(写完即关闭的批量形式)。
- **`SubprocessOutcome` 只承载退出事实**Node close 事件的词汇);收集到的输出在结算后仍可经 `handle.collected` 读取spill 文件描述符在结算边界封存),因此批量与流式调用方共用一条访问路径,也没有任何内容被复制进这份结果。
- **以进程树为范围的终止,按 Node 风格拆分**`kill(signal?)` 只发送一个信号,结算后为空操作;`terminate()` 拥有 SIGTERM→宽限期→SIGKILL 升级(并承接 spec 的 abort 信号);`waitForExit()` 轮询进程树存活状态POSIX 进程组探测Windows 上以直接子进程为界);`dispose(graces)` 是从 `subagent-subprocess` 吸收来的协作式 stdin EOF→SIGTERM→SIGKILL 阶梯,按句柄 memoize 化。Windows 进程树终止(`taskkill /T`,可注入)自 lsp-local 迁入,因此每个消费方拿到的进程树语义在各平台上都正确。
- **凭据清除只有一份定义**`scrubbedParentEnv()`/`SENSITIVE_ENV_PATTERN` 定义在 seam 上。无法把 spawn 本身路由到该服务的调用点——pty-localnode-pty 拥有 fork与 mcp-clientMCP SDK 拥有传输层的 spawn——改为导入该函数因此即便进程所有权无法统一环境策略仍是单一来源SDK helper 的 `scrubEnvironment()` 默认同样委托给它。
各项迁移随这次重塑一并落地:**bash-local/bash-sandbox**(收集模式 + 批量 stdinbash 的 `kill()` 映射到 `terminate()`,因此 `task_kill` 保有升级语义),**lsp-local**(管道化的协议流 + 无 spill 的 stderr 收集尾部;`LspConnection` 改为接收 seam 的 spawn 函数;其私有的进程树操作辅助函数已删除),**subagent-acp**(管道化的 ndjson 流 + inherit 的 stderrspawn 失败经 `done` 的 reject 汇入同一个启动竞态dispose 就是携带插件所配置宽限期的 `handle.dispose` 调用)。**`dsh-subagent-subprocess` 已删除**——dispose 阶梯与凭据清除归 seam 所有;无人使用的隔离配置目录辅助函数随之消亡(其消费方本就不存在)。
挂载 lsp-local 或 subagent-acp 的组合如今都要加载 `dsh-subprocess-local`(这两个插件注入 `'subprocess'`acp/lsp 测试 fixture测试前置数据补上了这一行组合配置。
## 曾考虑的替代方案
**保持只支持批量的 seam让流式消费方继续各自为政。**这正是引入该 seam 的 Agent Note 当初的立场,评审将其否决:这样会留下三份进程树信号发送的私有副本和五份凭据清除的私有副本,而未来任何运行器(容器化执行器、远程进程宿主)都得挑选去 fork 哪一份私有副本。Node 形状的处置方式恰好覆盖已观察到的全部三种流形状,既不拓宽结果类型,也不缓冲管道化的流。
**用单个 `stdio: 'pipe' | 'inherit' | 'collect'` 模式一次性统辖全部三条流。**否决真实消费方按流混用模式lsppipe/pipe/collectacppipe/pipe/inheritbashdata/collect/collect。按流划分的处置方式恰好就是 Node 的形状,也免去了混用场景的第二个 spawn 调用。
**把 pty-local 与 mcp-client 的 spawn 也一并迁移。**基于所有权而非范围否决node-pty 的 `fork()` 自行分配终端MCP SDK 的 `StdioClientTransport` 在内部完成 spawn——这两处调用点都不归我们路由。它们采纳共享的凭据清除那正是属于策略的部分并在各自的 README 中说明 spawn 为何留在原地。
**迁移 test-support 启动器acp-snapshot、loader-smoke与 SDK package-manager 运行器。**否决support 各包是刻意保持轻依赖的测试基础设施,不得依赖产品 seam而 SDK 向导那套附带重定向的 `stdio: 'inherit'` 语义,加上其完全脱离组合的生命周期(根本没有 cordis 上下文),使该服务并不合用;它改为共享凭据清除。
## 后果
换来的是进程树信号发送、升级、dispose 阶梯、有界收集与凭据清除各自只剩一份实现,且只在 `dsh-subprocess-local` 的测试套件中测试一次(其中包括 lsp-local 的私有副本从未有过的、以注入平台方式实现的 Windows 覆盖lsp-local 与 subagent-acp 卸下了自己的进程管道,其子进程如今像 bash 的一样,在插件重载后存活、随组合拆除而终止;一个完整的包(`dsh-subagent-subprocess`就此消失。seam README 中「只有一个消费方家族」的限制说明也随之退役。
代价是:这道 seam 变宽了stdio 模式从一种变为三种、终止动词从一个变为四个未来的后端因此要实现更宽的表面lsp-local/subagent-acp 的各组合如今都带上 subprocess 这一行配置;`SubprocessOutcome` 也不再承载输出这是仍未发布的堆叠变更内部的一次破坏性形状变更依照预发布立场PR2 那一层被就地更新,而非加 shim。pty-local/mcp-client/SDK/test-support 的 spawn 因所有权归属留在该服务之外,以凭据清除作为共享底线。

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
2026-07-26-subprocess-seam.md: abfc43c1a960edd498b0960a191153008e39834c
2026-07-26-subprocess-seam.zh.md: 7fba975181b7871b34a8bc0e77d826e1ee2647f5
2026-07-26-subprocess-seam.md: 5cf0e596603b4cd3240e5d9a114d95f413c6410b
2026-07-26-subprocess-seam.zh.md: 9cf38941953cf0348e6627e9bcb194cf231352d1

View File

@@ -12,7 +12,7 @@ Status: implemented
新的 `subprocess/` 能力家族拥有「运行并管理一个进程」bash 家族保留「运行一条 bash 命令」,并成为前者的消费方:
- **`@deepseek-ai/dsh-subprocess`(接口)**——拥有 `ctx.subprocess` 的抽象 `SubprocessService`(仅一个方法:`spawn(spec): SubprocessHandle`),以及共享词汇:完全显式的 `SubprocessSpawnSpec`argv、cwd、按流划分的上限、spill 上限、宽限期,一律不设默认值;随部署变化的旋钮依照 `dsh-bash` 的 request/spec 模板与无隐藏默认值规则,留在调用方 seam 的配置里)、携带基于偏移量的非消费式读取器的 `SubprocessHandle`、刻意不含超时/取消分类的 `SubprocessOutcome`,以及 `DSH_ENV_PREFIX`/`DshEnvironment`/`CollectedOutput` 类型。`argv` 绝不经过 shell 解释。
- **`@deepseek-ai/dsh-subprocess`(接口)**——拥有 `ctx.subprocess` 的抽象 `SubprocessService`(仅一个方法:`spawn(spec): SubprocessHandle`),以及共享词汇:完全显式的 `SubprocessSpawnSpec`argv、cwd、逐流 stdio 处置方式、宽限期,一律不设默认值;随部署变化的旋钮依照 `dsh-bash` 的 request/spec 模板与无隐藏默认值规则,留在调用方 seam 的配置里)、携带基于偏移量的非消费式读取器的 `SubprocessHandle`、刻意不含超时/取消分类的 `SubprocessOutcome`,以及共享的凭据清除加 `DSH_ENV_PREFIX`/`DshEnvironment`/`CollectedOutput` 类型。`argv` 绝不 shell 解释。[消费方迁移 Agent Note](2026-07-26-subprocess-consumer-migration.md)其后将 stdio 与终止词汇进一步拓宽为 Node 形状。)
- **`@deepseek-ai/dsh-subprocess-local`(实现)**——`LocalSubprocessService`,构建在原 `run.ts` 管道(现为 `spawn.ts`之上detached 进程组、带私有有界 spill 文件的尾部保留截断、带双通道 `DSH_*` 合并的凭据清除、进程组 kill 升级,以及会终止每个仍在运行的受管进程并等待其退出的 dispose。该实现没有任何配置每项限制都随 spec 到达。终端相关的 `ENV_OVERRIDES``TERM=dumb` 等)并未迁移:那是 bash 工具的呈现策略,留在 `dsh-bash-local` 里,经普通 env 通道合并。
- **`dsh-bash-local`(消费方)**——`inject: ['subprocess']`;把每个解析后的 `BashExecSpec` 映射为一个 `SubprocessSpawnSpec``['bash', '-c', command]`),并保留自身配置、`resolve()` 默认值补全、基于融合 deadline 的 `timedOut`/`aborted` 分类、带 `[stderr]` 标记的后台读取合并及其消费游标,以及 `onProcessDone` 子类钩子。`dsh-bash-sandbox` 除了重新声明继承来的 inject 之外没有变化;它仍在命令字符串层面做包装,并重新进入继承的 spawn 路径。
- **`dsh-bash`seam**——把迁走的词汇从 `dsh-subprocess` 重导出,因此没有任何 bash 消费方需要改动导入;`BashExecRequest`/`BashExecSpec`/`BashProcess` 与沙箱事实仍归 bash 所有。
@@ -25,7 +25,7 @@ Status: implemented
**把进程管道留在 `dsh-bash-local` 里(维持现状)。**否决的理由与[任务注册表拆分](2026-07-26-task-registry-seam.md)得以落地的理由相同:这条边界既稳定,也早已记录在代码里(`run.ts` 的模块文档曾写明「this layer reacts to an abort signal; the executor owns deadlines and classifies causes」而若继续将它保持私有未来每个非 shell 运行器就只能要么 fork 这套机制,要么为非 bash 工作去依赖一个以 bash 命名的包。这组堆叠变更对用户可见的动因正是这一拆分。
**在同一变更中把仓库其余 spawn 调用点lsp-local、pty-local、subagent-subprocess、sdk package-manager、test-support 各启动器)迁到 `ctx.subprocess` 上。**作为带有真实设计风险的范围蔓延否决。这些调用点在流与生命周期上的需求存在实质差异node-pty 所有权pty、长生命周期 stdio 上的 LSP 分帧加进程树终止回退lsp、以 stdin EOF 打头的 dispose 阶梯和完全不缓冲输出subagent 传输层)。把它们强行纳入一个按有界批量输出塑形的句柄之下,要么会让这道 seam 膨胀,要么会让句柄与消费方错配。依照「接口围绕当前消费方塑形」的规则,该 seam 在其唯一真实的消费方家族上得到验证后交付;其余调用点已在 seam README 中列为暂缓工作
**在同一变更中把仓库其余 spawn 调用点lsp-local、pty-local、subagent-subprocess、sdk package-manager、test-support 各启动器)迁到 `ctx.subprocess` 上。**在那个 PR 的规模下,作为带有真实设计风险的范围蔓延否决。这些调用点在流与生命周期上的需求存在实质差异node-pty 所有权pty、长生命周期 stdio 上的 LSP 分帧加进程树终止回退lsp、以 stdin EOF 打头的 dispose 阶梯和完全不缓冲输出subagent 传输层)。把它们强行纳入一个按有界批量输出塑形的句柄之下,要么会让这道 seam 膨胀,要么会让句柄与消费方错配。依照「接口围绕当前消费方塑形」的规则,该 seam 当时在其唯一真实的消费方家族上得到验证后交付。审阅随后恰恰要求以堆叠 PR 的形式完成这项后续工作;[消费方迁移 Agent Note](2026-07-26-subprocess-consumer-migration.md)记录了向 Node 形状的重塑,以及哪些调用点迁入(哪些因所有权归属而留守)
**改把 `run_in_background`/任务语义放进进程 seam。**否决:那条边界已经存在。`ctx.tasks` 拥有 id、所有权与通知bash 工具则把 `BashProcess` 适配成任务钩子。进程 seam 位于 bash 执行器*之下*,而不是与任务注册表并列。

View File

@@ -43,6 +43,7 @@
".agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.md",
".agents/notes/implemented/architecture/2026-07-22-slot-type-chain-implementation.md",
".agents/notes/implemented/architecture/2026-07-23-client-plugin-loading-model.md",
".agents/notes/implemented/architecture/2026-07-26-subprocess-consumer-migration.md",
".agents/notes/implemented/architecture/2026-07-26-task-registry-seam.md",
".agents/notes/implemented/feature/2026-06-14-acp-multi-session.md",
".agents/notes/implemented/feature/2026-06-15-code-mode.md",