docs: reserve seam for complete capabilities

This commit is contained in:
Turtle
2026-08-09 15:34:32 +08:00
parent 27ac49e687
commit dda02250f5
966 changed files with 2166 additions and 2159 deletions

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 packages/subprocess/README.md
README.md: 9a8628e2b6d16852895be33ae516c8b33b078502
README.zh.md: 734a2a5a27f0337a3015989556e571f4d18228e0
README.md: 23c8ba44916379c032991bc92df557157c2e66c4
README.zh.md: d3400b8b6cc5e839d77858f0684125b54b2daadd

View File

@@ -6,8 +6,8 @@ The shared process substrate for one execution world: executable lookup, fully-s
| Package | ctx key | Role |
|---|---|---|
| [`subprocess`](subprocess/README.md) (`@deepseek-ai/dsh-subprocess`) | `ctx.subprocess` | The seam: executable lookup, ordinary managed spawns, the terminal-process primitive, handle lifecycles, and shared environment/output vocabulary |
| [`subprocess-local`](subprocess-local/README.md) (`@deepseek-ai/dsh-subprocess-local`) | — | The local implementation: detached process trees, bounded collection/spill, `node-pty`, foreground/session inspection, tree signalling, and terminate-and-join disposal |
| [`subprocess`](subprocess/README.md) (`@deepseek-ai/dsh-subprocess`) | `ctx.subprocess` | Service Definition: executable lookup, ordinary managed spawns, the terminal-process primitive, handle lifecycles, and shared environment/output vocabulary |
| [`subprocess-local`](subprocess-local/README.md) (`@deepseek-ai/dsh-subprocess-local`) | — | Local Service provider: detached process trees, bounded collection/spill, `node-pty`, foreground/session inspection, tree signalling, and terminate-and-join disposal |
The service owns process lifetime across consumer reloads; consumers own what a process means (a bash command, a future non-shell runner) and every default that shapes one.

View File

@@ -6,8 +6,8 @@
| 包package | ctx 键 | 角色 |
|---|---|---|
| [`subprocess`](subprocess/README.md)`@deepseek-ai/dsh-subprocess` | `ctx.subprocess` | seam 本体:可执行文件查找、普通受管 spawn、终端进程原语、句柄生命周期以及共享的环境输出词汇 |
| [`subprocess-local`](subprocess-local/README.md)`@deepseek-ai/dsh-subprocess-local` | 无 | 本地实现detached 进程树、有界收集spill、`node-pty`、前台/会话检查、进程树信号发送,以及先终止再等待退出的资源释放 |
| [`subprocess`](subprocess/README.md)`@deepseek-ai/dsh-subprocess` | `ctx.subprocess` | Service Definition:可执行文件查找、普通受管 spawn、终端进程原语、句柄生命周期以及共享的环境输出词汇 |
| [`subprocess-local`](subprocess-local/README.md)`@deepseek-ai/dsh-subprocess-local` | 无 | 本地 Service providerdetached 进程树、有界收集spill、`node-pty`、前台/会话检查、进程树信号发送,以及先终止再等待退出的资源释放 |
即使消费方重载,进程生命周期仍由服务负责管理;消费方负责定义进程的含义(一条 bash 命令、未来的非 shell 运行器),以及决定塑造该进程的每一项默认值。

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 packages/subprocess/subprocess-local/README.md
README.md: 087ca24a3207cb8cb1568769a462fbbb010aaa35
README.zh.md: 4d400906f71b653ce2be95a22751fd9893f447bd
README.md: ed2b4c3558aa034c8d9fa1049c81d6628c86dff7
README.zh.md: 832a82dc6a12096e85a1b939f5021767a3c5cb68

View File

@@ -2,7 +2,7 @@
English | [中文](README.zh.md)
Local implementation of the [`@deepseek-ai/dsh-subprocess`](../subprocess/README.md) seam. `LocalSubprocessService` resolves local executables, spawns ordinary detached process trees with explicit stdio, and implements terminal processes through `node-pty` plus platform process inspection. It has no config: every disposition, limit, terminal dimension, grace, and directory arrives from the calling seams ([`dsh-bash-local`](../../bash/bash-local/README.md), [`dsh-lsp-local`](../../lsp/lsp-local/README.md), and [`dsh-pty-local`](../../pty/pty-local/README.md)).
Local Service provider for the [`@deepseek-ai/dsh-subprocess`](../subprocess/README.md) seam. `LocalSubprocessService` resolves local executables, spawns ordinary detached process trees with explicit stdio, and implements terminal processes through `node-pty` plus platform process inspection. It has no config: every disposition, limit, terminal dimension, grace, and directory arrives from the calling capability seams ([`dsh-bash-local`](../../bash/bash-local/README.md), [`dsh-lsp-local`](../../lsp/lsp-local/README.md), and [`dsh-pty-local`](../../pty/pty-local/README.md)).
## Behavior (and where it came from)
@@ -16,7 +16,7 @@ Local implementation of the [`@deepseek-ai/dsh-subprocess`](../subprocess/README
## Model Experience
Indirectly, through consumer seams (today the bash executor family behind `dsh-tool-bash`), which own all model-facing rendering of process output and lifecycle.
Indirectly, through Consumers (today the bash executor family behind `dsh-tool-bash`), which own all model-facing rendering of process output and lifecycle.
#### KV Cache effect

View File

@@ -2,7 +2,7 @@
[English](README.md) | 中文
[`@deepseek-ai/dsh-subprocess`](../subprocess/README.md) seam 的本地实现`LocalSubprocessService` 解析本地可执行文件,以显式 stdio spawn 普通 detached 进程树,并通过 `node-pty` 加平台进程检查实现终端进程。该实现没有任何配置:每项处置方式、限制、终端尺寸、宽限期与目录都来自调用方 seam[`dsh-bash-local`](../../bash/bash-local/README.md)、[`dsh-lsp-local`](../../lsp/lsp-local/README.md) 和 [`dsh-pty-local`](../../pty/pty-local/README.md))。
[`@deepseek-ai/dsh-subprocess`](../subprocess/README.md) seam 的本地 Service provider`LocalSubprocessService` 解析本地可执行文件,以显式 stdio spawn 普通 detached 进程树,并通过 `node-pty` 加平台进程检查实现终端进程。该实现没有任何配置:每项处置方式、限制、终端尺寸、宽限期与目录都来自调用方能力 seam[`dsh-bash-local`](../../bash/bash-local/README.md)、[`dsh-lsp-local`](../../lsp/lsp-local/README.md) 和 [`dsh-pty-local`](../../pty/pty-local/README.md))。
## 行为(以及设计来源)
@@ -10,13 +10,13 @@
- **按流划分的处置方式**`'pipe'` 把原始流原样交给调用方(协议分帧仍归消费方所有);`'inherit'` 直通父进程的描述符收集模式collect在输出超过上限后于内存中保留尾部错误与结果通常聚集在末尾沿用 pi/OpenCode 的理由),并在配置了 spill 上限时把完整流追加到一个私有临时文件;省略 `spill` 则只保留用于诊断的尾部。某条流大于 spill 上限时,会丢弃已不完整的 spill仅返回带截断标记的尾部spill 文件描述符在结算时封存最终关闭失败时则不公布路径以免声称存在不完整的文件。spill 文件权限为 `0600`、名称随机,位于按需创建、权限为 `0700` 的每进程目录之下。
- **凭据清除 + 显式合并**:以 `process.env` 为基础,移除形似凭据的变量(`*KEY*``*PASSWORD*``*SECRET*``*TOKEN*`)和所有环境中已有的 `DSH_*` 名称spec 的显式 `env` 在该清除之后合并且不做命名空间校验,因此有意提供的凭据或当前 `DSH_*` 事实会胜出,而陈旧的嵌套 harness 身份无法从环境中隐式漏入。提供的 stdin 会被写入后关闭;否则 fd 0 指向 `/dev/null`。参见 [stdin/env Agent Note](../../../.agents/notes/implemented/architecture/2026-06-30-bash-stdin-env-trusted-plugin-surface.md)与[受管环境 Agent Note](../../../.agents/notes/implemented/feature/2026-07-10-agent-session-identity-and-log-location.md)。
- **基于偏移量的读取**收集模式的读取器按完整流的字节坐标返回增量服务自身从不持有游标因此消费方自有的游标bash 的后台读取路径)与完整流重读可以共存,结算前后皆然。
- **可执行文件查找**`resolveExecutable` 检查绝对文件,或根据平台可执行文件扩展名在清理后的有效 PATH 中搜索;含分隔符的相对路径在接缝处被拒绝,相对 PATH 条目从宿主进程 cwd 解析。
- **可执行文件查找**`resolveExecutable` 检查绝对文件,或根据平台可执行文件扩展名在清理后的有效 PATH 中搜索;含分隔符的相对路径在该能力入口被拒绝,相对 PATH 条目从宿主进程 cwd 解析。
- **终端进程所有权**`spawnTerminal` 分配 `node-pty`,桥接 UTF-8 终端文本,检查当前前台进程组并向其发送信号,还会公开一项须等待的终止操作,在终止顶层 shell 前后清理后代进程。每次前台检查都会保留根进程树中的精确身份Linux 还会在 POSIX 会话 leader 退出后枚举该会话。因此,之前观察到的 macOS 后代以及同会话 Linux 成员在重新设定父进程后仍受围栏保护pid/start 身份则防止清理跟随 PID 复用。上层 PTY 后端负责提示符就绪、缓冲区与面向模型的操作。
- **先终止再等待退出的 dispose资源释放**:服务保留存活句柄,只为让自身的 dispose 能对每个仍在运行的进程树执行升级并等待其退出;已结算与 spawn 失败的句柄在结算时即离开存活集合。
## 模型体验
通过消费方 seam 间接影响(目前是 `dsh-tool-bash` 背后的 bash 执行器家族);进程输出与生命周期面向模型的全部渲染归消费方所有。
通过 Consumer 间接影响(目前是 `dsh-tool-bash` 背后的 bash 执行器家族);进程输出与生命周期面向模型的全部渲染归 Consumer 所有。
#### KV Cache 影响

View File

@@ -1,9 +1,9 @@
/**
* Local implementation of the subprocess seam. Each spawn is a detached
* Local Service provider for the subprocess capability seam. Each spawn is a detached
* process tree with the spec's per-stream stdio dispositions; disposal
* terminates and joins live trees. It has no config: every disposition and
* limit arrives on the spec, so the deployment-varying choices stay with the
* calling seam's config (the bash executor's, the LSP host's, …).
* caller's config (the bash executor's, the LSP host's, …).
* @module @deepseek-ai/dsh-subprocess-local
*/
@@ -37,9 +37,9 @@ export class LocalSubprocessService extends SubprocessService {
private live = new Set<SubprocessHandle>()
/** Live terminal sessions retained through whole-session quiescence. */
private terminals = new Set<SubprocessTerminalHandle>()
/** Test seam: spill and platform knobs forwarded to spawnSubprocess. */
/** Test hook: spill and platform knobs forwarded to spawnSubprocess. */
internals: SpawnInternals = {}
/** Test seam for platform process inspection; production resolves lazily on terminal spawn. */
/** Test hook for platform process inspection; production resolves lazily on terminal spawn. */
terminalInspector: ProcessInspector | undefined
constructor(ctx: Context) {
@@ -123,8 +123,8 @@ export class LocalSubprocessService extends SubprocessService {
return handle
}
// Local PTY allocation is synchronous, but the provider seam permits remote asynchronous allocation.
// oxlint-disable-next-line typescript/require-await -- Preserve promise rejection semantics at the async provider seam.
// Local PTY allocation is synchronous, but the provider contract permits remote asynchronous allocation.
// oxlint-disable-next-line typescript/require-await -- Preserve promise rejection semantics at the async provider contract.
async spawnTerminal(spec: SubprocessTerminalSpawnSpec): Promise<SubprocessTerminalHandle> {
const file = spec.argv[0]
if (file === undefined || file.length === 0) {

View File

@@ -72,14 +72,14 @@ export class LocalTerminalHandle implements SubprocessTerminalHandle {
}
// node-pty writes synchronously; the seam returns a promise for remote transports.
// oxlint-disable-next-line typescript/require-await -- Preserve promise rejection semantics at the async provider seam.
// oxlint-disable-next-line typescript/require-await -- Preserve promise rejection semantics at the async provider contract.
async write(data: string): Promise<void> {
if (this.exited) throw new Error('terminal process has exited')
this.terminal.write(data)
}
// Local inspection is synchronous; the seam returns a promise for remote transports.
// oxlint-disable-next-line typescript/require-await -- Preserve promise rejection semantics at the async provider seam.
// oxlint-disable-next-line typescript/require-await -- Preserve promise rejection semantics at the async provider contract.
async inspectForeground(): Promise<SubprocessTerminalForeground | undefined> {
this.descendants()
const processGroupId = this.inspector.foregroundPgid(this.pid)

View File

@@ -329,7 +329,7 @@ describe('stdin and extra env (set by in-process plugins)', () => {
})
it('gives fd 0 the exact pre-seam type: /dev/null when no stdin, a pipe when supplied', async () => {
// With no bytes, fd 0 remains the pre-seam `ignore` default (/dev/null, a character device).
// With no bytes, fd 0 remains the pre-spawn `ignore` default (/dev/null, a character device).
// Supplied bytes use Node's spawn pipe, which is an AF_UNIX socket rather than a FIFO.
const none = await finish(spawnSubprocess(spec('test -c /dev/stdin && echo char || echo other')))
expect(none.stdout.text).toBe('char\n')

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 packages/subprocess/subprocess/README.md
README.md: c28361f5e767f81fe8d1ca4757aece83dccfea5c
README.zh.md: b5d0aac765e433dbaeb9b6e24638ffa6bea6cc51
README.md: 71ed60c6bdbecbddf45ee4c6a3d31e4a3fbb479b
README.zh.md: a3789954edf5cedfa96f26774250b2cec877c503

View File

@@ -8,7 +8,7 @@ The subprocess seam (`ctx.subprocess`) is the process half of one execution worl
- `spawn(spec)` returns immediately with a live handle; `done` resolves at process close with exit facts (`SubprocessOutcome` carries no output and no cause classification) and rejects only for spawn-level failures.
- Spawn working directories and executable paths belong to the provider's execution world. `resolveExecutable(command, env?, signal?)` verifies absolute commands or resolves bare names against that world's scrubbed PATH plus explicit overrides.
- The spec is fully explicit — argv, cwd, per-stream stdio dispositions, grace — because deployment-varying defaults belong to the calling seam's config, not to a hidden subprocess-service default (the `dsh-bash` request/spec split is the owning template). `argv` is never shell-interpreted; a consumer that wants a shell passes `['bash', '-c', command]` itself.
- The spec is fully explicit — argv, cwd, per-stream stdio dispositions, grace — because deployment-varying defaults belong to the caller's config, not to a hidden subprocess-service default (the `dsh-bash` request/spec split is the owning template). `argv` is never shell-interpreted; a consumer that wants a shell passes `['bash', '-c', command]` itself.
- Stdio is Node-shaped per stream: `'pipe'` hands the caller the raw stream for its own protocol framing (LSP JSON-RPC, ACP ndjson), `'inherit'` passes the parent descriptor through for diagnostics, and collect mode (`{ maxBytes, spill? }`) buffers a bounded tail with an optional full-stream spill file. Collect readers take whole-stream byte offsets and never consume, so independent readers cannot steal one another's deltas; a read whose offset slid out of the in-memory tail is `lossy` and points at the spill file when one exists. Collected output stays readable after settlement.
- Termination is tree-scoped on every platform (POSIX detached groups with direct-child fallback; Windows `taskkill /T`): `terminate()` — the only termination verb — escalates SIGTERM→grace→SIGKILL (idempotent, driven by the spec's abort signal too, a no-op once the tree is gone), and `waitForExit(signal?)` observes whole-tree liveness so a consumer-owned teardown ladder holds each tier on real quiescence — the manager reacts but never classifies why (callers own deadlines, teardown ladders, and cause classification).
- `spawnTerminal(spec)` is the only non-pipe primitive. Its handle owns a real PTY, UTF-8 text I/O, foreground-process-group inspection/signalling, and one awaited `terminate()` operation that reaches quiescence for every session member the provider can still observe and settles in-flight handle calls; providers document substrate-specific observability limits. The spec signal cancels allocation only; the published handle owns its lifetime. The output stream ends after queued output when the top-level process exits, and a live transport failure rejects `done`. These operations remain one substrate primitive because ordinary pipes cannot allocate a controlling terminal or clean terminal-session members; readiness, scrollback, and owner policy remain in the PTY consumer.
@@ -19,7 +19,7 @@ See the [subprocess subsystem page](../../../docs/subsystems/subprocess.md) and
## Model Experience
Indirectly, through consumer seams (today the bash executor family behind `dsh-tool-bash`), which own all model-facing rendering of process output and lifecycle.
Indirectly, through Consumers (today the bash executor family behind `dsh-tool-bash`), which own all model-facing rendering of process output and lifecycle.
#### KV Cache effect

View File

@@ -8,7 +8,7 @@
- `spawn(spec)` 立即返回一个活动句柄;`done` 在进程关闭时以退出事实 resolve`SubprocessOutcome` 不携带输出,也不携带原因分类),仅在 spawn 层面失败时 reject。
- spawn 工作目录和可执行文件路径属于提供方的执行世界。`resolveExecutable(command, env?, signal?)` 验证绝对命令,或根据该执行世界清理后的 PATH 加显式覆盖来解析裸名称。
- spec 完全显式argv、cwd、按流划分的 stdio 处置方式disposition、宽限期因为随部署变化的默认值属于调用方 seam 的配置,而不属于某个隐藏的子进程默认值(`dsh-bash` 的 request/spec 拆分是这条规则的所属模板)。`argv` 绝不经过 shell 解释;需要 shell 的消费方自行传入 `['bash', '-c', command]`
- spec 完全显式argv、cwd、按流划分的 stdio 处置方式disposition、宽限期因为随部署变化的默认值属于调用方的配置而不属于某个隐藏的子进程默认值`dsh-bash` 的 request/spec 拆分是这条规则的所属模板)。`argv` 绝不经过 shell 解释;需要 shell 的消费方自行传入 `['bash', '-c', command]`
- stdio 按流采用 Node 风格:`'pipe'` 把原始流交给调用方做自己的协议分帧LSP 的 JSON-RPC、ACPAgent Client Protocol的 ndjson`'inherit'` 直通父进程描述符以承载诊断输出收集模式collect`{ maxBytes, spill? }` 则缓冲一段有界尾部,外加可选的完整流 spill 文件。收集模式的读取器接受全流字节偏移量且从不消费,因此独立的读取器不会抢走彼此的增量;偏移量滑出内存尾部窗口的读取标记为 `lossy`,并在 spill 文件存在时指向它。收集到的输出在结算后仍可读取。
- 终止在每个平台上都以进程树为范围POSIX 用 detached 进程组并以直接子进程回退Windows 用 `taskkill /T``terminate()`(唯一的终止动词)执行 SIGTERM→宽限期→SIGKILL 升级(幂等,也由 spec 的 abort 信号驱动,进程树消亡后为空操作);`waitForExit(signal?)` 观察整棵进程树的存活状态使消费方自有的拆卸阶梯能在真正完全停稳后才进入下一层。管理器只响应中止但绝不判定原因deadline、拆卸阶梯与原因分类归调用方所有
- `spawnTerminal(spec)` 是唯一的非管道原语。其句柄负责真实 PTY、UTF-8 文本 I/O、前台进程组检查信号发送以及一项须等待的 `terminate()` 操作该操作会使提供方仍可观察到的每个会话成员完全停稳并结算在途句柄调用提供方会记录执行基底特有的可观察性限制。spec 信号只取消分配;句柄一经发布,便负责自身生命周期。顶层进程退出时,输出流在已排队输出之后结束;仍处于活动状态的传输若发生故障,会使 `done` 拒绝。这些操作保留为一项执行基底原语因为普通管道无法分配控制终端或清理终端会话成员就绪状态、scrollback 和所有者策略仍归 PTY 消费方所有。
@@ -19,7 +19,7 @@
## 模型体验
通过消费方 seam 间接影响(目前是 `dsh-tool-bash` 背后的 bash 执行器家族);进程输出和生命周期的全部面向模型渲染均由消费方负责。
通过 Consumer 间接影响(目前是 `dsh-tool-bash` 背后的 bash 执行器家族);进程输出和生命周期的全部面向模型渲染均由 Consumer 负责。
#### KV Cache 影响

View File

@@ -1,5 +1,5 @@
/**
* The subprocess seam (`ctx.subprocess`): execution-world executable lookup,
* Service Definition for the subprocess capability seam (`ctx.subprocess`): execution-world executable lookup,
* fully specified managed process trees with raw or
* collected stdio, and one terminal-process primitive. Command defaulting,
* shell semantics, deadlines, protocol framing, terminal readiness, and

View File

@@ -10,7 +10,7 @@ export const name = 'subprocess-invariant'
/** Service required before the companion can reserve package ownership. */
export const inject = ['invariants']
/** No runtime invariant: this stateless seam owns spawn-spec/handle types, while implementations own observations. */
/** No runtime invariant: this stateless Service Definition owns spawn-spec/handle types, while Service providers own observations. */
const install: InvariantInstaller = () => {}
/**

View File

@@ -1,5 +1,5 @@
/**
* Vocabulary for the subprocess seam: fully-specified spawn requests with
* Vocabulary for the subprocess Service Definition: fully-specified spawn requests with
* Node-shaped per-stream stdio modes, bounded collected output with spill
* recovery, raw piped streams, and tree-scoped termination. Command
* defaulting, shell semantics, protocol framing, and presentation belong to