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/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')