fix(process): close review gaps from Codex round 1

- Mount LocalProcessManager in the sandbox e2e compositions (bwrap/landlock/
  seatbelt + the spine multi-project e2e) and add the spine demo's
  dsh-process-local devDependency, so SandboxBashExecutor's new inject
  resolves when those suites are enabled.
- Extend the Windows test/coverage skip to packages/process/* — the POSIX
  process-group suite moved there from packages/bash.
- Update the stale disposal contract: the bash seam JSDoc, BashProcess JSDoc,
  and core bash doc (en+zh) now state that composition teardown (the process
  manager's disposal) owns kill-and-await, and an executor-only reload leaves
  background processes running.
- Record ctx.processes in the architecture capability table and extension map
  (en+zh) and the root AGENTS.md layout tree; reword the timeout-library note
  so it describes where the plumbing and classification live today.
This commit is contained in:
Tianyi Cui
2026-07-26 07:54:42 +08:00
parent 1f0b8cc4c1
commit fb0d4ba564
17 changed files with 39 additions and 17 deletions
+1
View File
@@ -15,6 +15,7 @@ packages/ @deepseek-ai/dsh-<pkg> workspaces at packages/<group>/<pkg>/
prompt/ workspace instructions
llm/ LLM seam + the DeepSeek adapters (hand-rolled + pi-ai design twin)
bash/ bash executor seam + local impl + model-facing bash tools
process/ child-process manager seam + local process-group impl
pty/ persistent PTY seam/backend/tools
fs/ filesystem seam + local impl + policy gate + read/write/edit tools
lsp/ language-server seam + local stdio provider + model-facing lsp tool
+2 -2
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
architecture.md: b426891c0483f42a64b597632cf1871aff79ca2d
architecture.zh.md: 13feefb6854e79ddee38602902d325a789fd7744
architecture.md: ca2d1c70342fcef35deca7e247863511fb7c35b8
architecture.zh.md: c3107b23f96497a5ab2184d660d085f3e8a5e527
+2 -1
View File
@@ -28,6 +28,7 @@ Harnesses are [Cordis](cordis-primer.md) contexts with package-contributed servi
| `ctx.llm` | [`llm/`](../packages/llm/README.md) | adapter registry and streaming model calls |
| `ctx.tokenMeter` | [`llm/token-meter`](../packages/llm/token-meter/README.md) | singleton replay-aware request/surface pressure |
| `ctx.bash` | [`bash/`](../packages/bash/README.md) | foreground/background command execution |
| `ctx.processes` | [`process/`](../packages/process/README.md) | managed child-process groups under the bash executors |
| `ctx.pty` | [`pty/`](../packages/pty/README.md) | owner-scoped persistent terminal sessions |
| `ctx.sandbox` | [`sandbox/`](../packages/sandbox/README.md) | same-world process confinement (argv wrapping, per-call policy) |
| `ctx.sandboxPolicy` | [`sandbox/`](../packages/sandbox/README.md) | shared sandbox policy home |
@@ -177,7 +178,7 @@ New behavior attaches to a documented extension point; a loop change updates thi
|---|---|
| Add a model provider | register an adapter on `ctx.llm` |
| Add a model-facing capability | register on `ctx.tools`; schemas enter prompt assembly |
| Add shell execution | implement and register a `ctx.bash` backend |
| Add shell execution | implement and register a `ctx.bash` backend (the local one spawns through `ctx.processes`) |
| Add persistent terminal execution | register a `ctx.pty` backend and `dsh-tool-pty` |
| Add a human command | register on `ctx.commands`; adapters discover and dispatch it without a model turn |
| Add background work | register on `ctx.tasks`; generic `task_*` tools collect or stop it |
+2 -1
View File
@@ -28,6 +28,7 @@
| `ctx.llm` | [`llm/`](../packages/llm/README.md) | 适配器注册表和模型流式调用 |
| `ctx.tokenMeter` | [`llm/token-meter`](../packages/llm/token-meter/README.md) | 感知回放的单实例请求压力和会话表面压力 |
| `ctx.bash` | [`bash/`](../packages/bash/README.md) | 前台和后台命令执行 |
| `ctx.processes` | [`process/`](../packages/process/README.md) | bash 执行器之下受管理的子进程组 |
| `ctx.pty` | [`pty/`](../packages/pty/README.md) | 按 owner 隔离的持久化终端会话 |
| `ctx.sandbox` | [`sandbox/`](../packages/sandbox/README.md) | 同一执行环境内的进程限制(argv 包装、逐调用策略) |
| `ctx.sandboxPolicy` | [`sandbox/`](../packages/sandbox/README.md) | 共享沙箱策略归属点 |
@@ -177,7 +178,7 @@ forever:
|---|---|
| 添加模型提供方 | 在 `ctx.llm` 上注册适配器 |
| 添加面向模型的功能 | 在 `ctx.tools` 上注册;schema 进入提示词组装流程 |
| 添加 shell 执行 | 实现并注册 `ctx.bash` 后端 |
| 添加 shell 执行 | 实现并注册 `ctx.bash` 后端(本地后端通过 `ctx.processes` 生成进程) |
| 添加持久化终端执行 | 注册 `ctx.pty` 后端和 `dsh-tool-pty` |
| 添加用户命令 | 在 `ctx.commands` 上注册;适配器无需模型轮次即可发现并分派该命令 |
| 添加后台工作 | 在 `ctx.tasks` 上注册;通用 `task_*` 工具负责收集或停止 |
+2 -2
View File
@@ -257,7 +257,7 @@ Implementations must honor these semantics:
- run rejects only for infrastructure failures. Nonzero exits, timeout kills, and abort kills resolve with a BashRunResult.
- start returns immediately; no timeout applies to background processes. `done` settles at process close and never rejects; spawn failures settle as `killed` with the error on stderr.
- BashProcess.readOutput is incremental: consecutive reads never repeat output. Lossy reads report truncation and available spill files.
- Disposal kills all running background processes and awaits their exit.
- A still-running background process is stopped and awaited when its owning composition tears down. With the process-manager seam that boundary is `ctx.processes` disposal, so a background process survives an executor-only reload.
```ts cordis-catalog
/**
@@ -286,7 +286,7 @@ abstract start(spec: BashExecSpec): BashProcess
Types: [BashExecRequest](../core-data-structures/bash.md) · [BashExecSpec](../core-data-structures/bash.md) · [BashProcess](../core-data-structures/bash.md) · [BashRunResult](../core-data-structures/bash.md)
Source: [`packages/bash/bash/src/index.ts:48`](../../packages/bash/bash/src/index.ts)
Source: [`packages/bash/bash/src/index.ts:51`](../../packages/bash/bash/src/index.ts)
## `ctx.bashEnv` — `BashEnvRegistry`
+2 -2
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
bash.md: e092bfb1bf1aa23f7eeeda41a16a1625d1698201
bash.zh.md: 7ae60b9f38e6ca5c72e349e141132ae6ec72913d
bash.md: 0c92addf4778fa780cd432444549bfc36eeb763c
bash.zh.md: cd9d94055627937d03107ead5a4ffaeaa945d187
+4 -3
View File
@@ -170,8 +170,9 @@ One more piece completes the vocabulary: the `SANDBOX_UNAVAILABLE` error code (o
```ts type-equiv
/**
* A background process handle returned by {@link BashExecutor.start}. It is the
* only access path; buffered output remains readable after exit. Executor
* disposal kills running processes and awaits {@link done}.
* only access path; buffered output remains readable after exit. Composition
* teardown (the process manager's disposal) kills running processes and
* awaits {@link done}; an executor-only reload leaves them running.
*/
interface BashProcess {
/** Process lifecycle state (settled exactly once). */
@@ -216,4 +217,4 @@ interface BashProcessRead {
## The service
`BashExecutor` owns `resolve`, foreground `run`, background-process `start`, and the `sandboxMode` capability fact. `dsh-bash-local` owns process groups, timeout/abort handling, bounded collectors, spill files, credential scrubbing, and disposal quiescence. `dsh-tool-bash` owns model-facing rendering and adapts background handles into the [generic task runtime](tasks.md).
`BashExecutor` owns `resolve`, foreground `run`, background-process `start`, and the `sandboxMode` capability fact. `dsh-bash-local` owns command defaulting, timeout/abort classification, the terminal environment, and the background read merge; process groups, bounded collectors, spill files, credential scrubbing, and disposal quiescence are the [process manager](process.md)'s. `dsh-tool-bash` owns model-facing rendering and adapts background handles into the [generic task runtime](tasks.md).
+4 -3
View File
@@ -170,8 +170,9 @@ interface BashSandboxInfo {
```ts type-equiv
/**
* A background process handle returned by {@link BashExecutor.start}. It is the
* only access path; buffered output remains readable after exit. Executor
* disposal kills running processes and awaits {@link done}.
* only access path; buffered output remains readable after exit. Composition
* teardown (the process manager's disposal) kills running processes and
* awaits {@link done}; an executor-only reload leaves them running.
*/
interface BashProcess {
/** Process lifecycle state (settled exactly once). */
@@ -216,4 +217,4 @@ interface BashProcessRead {
## 服务
`BashExecutor` 拥有 `resolve`、前台 `run`、后台进程 `start` 以及 `sandboxMode` 能力事实。`dsh-bash-local` 拥有进程组、超时/中止处理、有界收集器、spill 文件、凭据清除以及 dispose(资源释放)后完全停稳。`dsh-tool-bash` 拥有面向模型的渲染,并将后台句柄适配到[通用任务运行时](tasks.md)。
`BashExecutor` 拥有 `resolve`、前台 `run`、后台进程 `start` 以及 `sandboxMode` 能力事实。`dsh-bash-local` 拥有命令默认值补全、超时/中止分类、终端环境以及后台读取合并;进程组、有界收集器、spill 文件、凭据清除 dispose(资源释放)后完全停稳归[进程管理器](process.md)所有。`dsh-tool-bash` 拥有面向模型的渲染,并将后台句柄适配到[通用任务运行时](tasks.md)。
@@ -9,6 +9,7 @@ import { LocalSandboxProvider } from '@deepseek-ai/dsh-sandbox-local'
import { SandboxPolicyService } from '@deepseek-ai/dsh-sandbox-policy'
import { bwrapProfileArgs } from '@deepseek-ai/dsh-sandbox-local/src/profiles.ts'
import { SandboxBashExecutor } from '@deepseek-ai/dsh-bash-sandbox'
import LocalProcessManager from '@deepseek-ai/dsh-process-local'
/**
* Keyless integration of the real provider and executor through public run/start paths. With
@@ -42,6 +43,7 @@ async function sandboxedBash(workspace: string, mode: 'read-only' | 'workspace-w
ctx = new Context()
await ctx.plugin(LocalSandboxProvider, {})
await ctx.plugin(SandboxPolicyService, { mode, workspaceRoot: workspace })
await ctx.plugin(LocalProcessManager)
await ctx.plugin(SandboxBashExecutor, { cwd: workspace, timeoutMs: 30_000 })
return ctx.bash as SandboxBashExecutor
}
@@ -9,6 +9,7 @@ import { launcherPath } from 'node-addon-landlock-run'
import { LocalSandboxProvider } from '@deepseek-ai/dsh-sandbox-local'
import { SandboxPolicyService } from '@deepseek-ai/dsh-sandbox-policy'
import { SandboxBashExecutor } from '@deepseek-ai/dsh-bash-sandbox'
import LocalProcessManager from '@deepseek-ai/dsh-process-local'
/**
* KEYLESS consumer-integration proof: the REAL `LocalSandboxProvider` (bwrap
@@ -47,6 +48,7 @@ async function sandboxedBash(workspace: string, mode: 'read-only' | 'workspace-w
await ctx.plugin(LocalSandboxProvider, {})
;(ctx.sandbox as LocalSandboxProvider).internals = { probeBwrap: () => false }
await ctx.plugin(SandboxPolicyService, { mode, workspaceRoot: workspace })
await ctx.plugin(LocalProcessManager)
await ctx.plugin(SandboxBashExecutor, { cwd: workspace, timeoutMs: 30_000 })
return ctx.bash as SandboxBashExecutor
}
@@ -9,6 +9,7 @@ import { LocalSandboxProvider } from '@deepseek-ai/dsh-sandbox-local'
import { SandboxPolicyService } from '@deepseek-ai/dsh-sandbox-policy'
import { seatbeltProfileArgs } from '@deepseek-ai/dsh-sandbox-local/src/profiles.ts'
import { SandboxBashExecutor } from '@deepseek-ai/dsh-bash-sandbox'
import LocalProcessManager from '@deepseek-ai/dsh-process-local'
/**
* Keyless macOS integration of the real provider and executor through public run/start paths.
@@ -41,6 +42,7 @@ async function sandboxedBash(workspace: string, mode: 'read-only' | 'workspace-w
await ctx.plugin(LocalSandboxProvider, {})
;(ctx.sandbox as LocalSandboxProvider).internals = { probeBwrap: () => false, probeLandlock: () => 'unusable' }
await ctx.plugin(SandboxPolicyService, { mode, workspaceRoot: workspace })
await ctx.plugin(LocalProcessManager)
await ctx.plugin(SandboxBashExecutor, { cwd: workspace, timeoutMs: 30_000 })
return ctx.bash as SandboxBashExecutor
}
+4 -1
View File
@@ -43,7 +43,10 @@ declare module 'cordis' {
* failures settle as `killed` with the error on stderr.
* - {@link BashProcess.readOutput} is incremental: consecutive reads never
* repeat output. Lossy reads report truncation and available spill files.
* - Disposal kills all running background processes and awaits their exit.
* - A still-running background process is stopped and awaited when its
* owning composition tears down. With the process-manager seam that
* boundary is `ctx.processes` disposal, so a background process survives
* an executor-only reload.
*/
export abstract class BashExecutor extends Service {
constructor(ctx: Context) {
+3 -2
View File
@@ -153,8 +153,9 @@ export interface BashProcessRead {
/**
* A background process handle returned by {@link BashExecutor.start}. It is the
* only access path; buffered output remains readable after exit. Executor
* disposal kills running processes and awaits {@link done}.
* only access path; buffered output remains readable after exit. Composition
* teardown (the process manager's disposal) kills running processes and
* awaits {@link done}; an executor-only reload leaves them running.
*/
export interface BashProcess {
/** Process lifecycle state (settled exactly once). */
@@ -65,6 +65,7 @@
"@deepseek-ai/dsh-llm": "workspace:^",
"@deepseek-ai/dsh-paths": "workspace:^",
"@deepseek-ai/dsh-llm-retry": "workspace:^",
"@deepseek-ai/dsh-process-local": "workspace:^",
"@deepseek-ai/dsh-sandbox-local": "workspace:^",
"@deepseek-ai/dsh-sandbox-policy": "workspace:^",
"@deepseek-ai/dsh-scope": "workspace:^",
@@ -5,6 +5,7 @@ import { basename, join } from 'node:path'
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
import { Context } from 'cordis'
import { SandboxBashExecutor } from '@deepseek-ai/dsh-bash-sandbox'
import LocalProcessManager from '@deepseek-ai/dsh-process-local'
import * as FsPolicy from '@deepseek-ai/dsh-fs-policy'
import SandboxedFileSystem from '@deepseek-ai/dsh-fs-sandbox'
import { CallId } from '@deepseek-ai/dsh-llm'
@@ -52,6 +53,7 @@ beforeEach(async () => {
ctx = new Context()
await ctx.plugin(LocalSandboxProvider, {})
await ctx.plugin(SandboxPolicyService, { mode: 'workspace-write', workspaceRoot: fallbackRoot })
await ctx.plugin(LocalProcessManager)
await ctx.plugin(SandboxBashExecutor, { cwd: fallbackRoot, timeoutMs: 30_000 })
await ctx.plugin(SandboxedFileSystem, { cwd: fallbackRoot })
await ctx.plugin(agentSpine, {
+3
View File
@@ -1658,6 +1658,9 @@ importers:
'@deepseek-ai/dsh-paths':
specifier: workspace:^
version: link:../../util/paths
'@deepseek-ai/dsh-process-local':
specifier: workspace:^
version: link:../../process/process-local
'@deepseek-ai/dsh-sandbox-local':
specifier: workspace:^
version: link:../../sandbox/sandbox-local
+1
View File
@@ -11,6 +11,7 @@ const windowsUnsupportedPackages = process.platform === 'win32'
? [
'packages/bash/*',
'packages/hooks/*',
'packages/process/*',
'packages/pty/pty-local',
'packages/sandbox/sandbox-local',
'packages/sdk/create-sdk',