@deepseek-ai/dsh-bash-sandbox 0.1.1-rc.2 → 0.1.2-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/shell/bash-sandbox/README.md
5
- README.md: dd0f6dd80394ee8bf0349f706e8fd77cd3c14189
6
- README.zh.md: 00b146ea88ffb449c7eaca1de6185657592fe774
5
+ README.md: 8ec918ed3e361f6a2c6345d1cda25e5c0bd39560
6
+ README.zh.md: 4c44a0ad6d09d16c18f75fe237f91116f7a62fcd
package/README.md CHANGED
@@ -1,28 +1,47 @@
1
+ ---
2
+ description: "The sandbox-consuming Bash executor for deployments and maintainers choosing, configuring, or debugging confined command execution with denial and escalation facts."
3
+ kind: "package-reference"
4
+ ---
5
+
1
6
  # @deepseek-ai/dsh-bash-sandbox
2
7
 
3
8
  English | [中文](README.zh.md)
4
9
 
5
- Sandbox-consuming Service Provider for the [`@deepseek-ai/dsh-shell`](../shell/) executor seam. Load it **instead of** `@deepseek-ai/dsh-bash-local`, together with a [`ctx.sandbox`](../../sandbox/sandbox/) provider (e.g. [`@deepseek-ai/dsh-sandbox-local`](../../sandbox/sandbox-local/)) and a [`ctx.sandboxPolicy`](../../sandbox/sandbox-policy/) (which owns the default mode + workspace root, shared with the sandboxed filesystem) — no alternate tool plugin is needed; `dsh-tool-bash` detects the executor's `sandboxMode` capability and adds the escalation fields.
10
+ ## Summary
11
+
12
+ `dsh-bash-sandbox` is the sandbox-consuming Bash executor: every command runs as a fresh `bash -c` process confined through the `ctx.sandbox` capability instead of with the harness process's full file authority. Each settled result carries the mode the command ran under, whether the sandbox denied a file operation, and how completely the selected runner enforced the requested mode. When no runner can enforce a confined mode, the call fails closed with a structured `SANDBOX_UNAVAILABLE` error rather than running unconfined. It is the confining sibling of `dsh-bash-local` — sharing its process mechanics — and the tool layer's escalation fields appear only while it is mounted.
13
+
14
+ ## Table of Contents
15
+
16
+ - [Use this package](#use-this-package)
17
+ - [Understand the implementation](#understand-the-implementation)
18
+ - [Further Exploration](#further-exploration)
19
+ - [Model Experience](#model-experience)
20
+ - [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
21
+ - [Dev Note](#dev-note)
22
+
23
+ -----
6
24
 
7
- The package root exports the default and named `SandboxBashExecutor` plugin plus its `Config`; result-classification helpers stay internal.
25
+ <a id="use-this-package"></a>
26
+ ## Use this package
8
27
 
9
- Every command is confined by handing the provider the exact `['bash', '-c', command]` argv this executor is about to spawn and spawning the returned argv directly. With the shipped native runners, the inner Bash retains shell semantics and evaluates `BASH_ENV` only after the runner establishes confinement. WHICH platform runner confines it and whether one is usable at all (fail closed with a structured `SANDBOX_UNAVAILABLE` error, never a silent unconfined run) — is the provider's concern; this package owns the bash side only.
28
+ Mount this executor instead of `dsh-bash-local` when commands must not run with the harness process's full file authority. It registers as `ctx.shell` and requires a `ctx.sandbox` provider plus `ctx.sandboxPolicy`; the model-facing `bash` tool works over it unchanged and advertises the `sandbox_permissions`/`justification` escalation fields.
29
+
30
+ ### When to choose it
31
+
32
+ Choose it when a deployment needs file-level confinement for Bash commands: the configured policy decides the default mode and workspace root, and each session can run under a different mode per call through the tool's escalation flow. The modes govern file effects only — network stays unrestricted and process visibility is backend-specific. For unconfined execution, or when no sandbox backend is available on the platform, mount `dsh-bash-local` instead.
33
+
34
+ ### Modes and file effects
10
35
 
11
36
  | Mode | File effects |
12
37
  |---|---|
13
- | `read-only` (default) | No writes anywhere (of `/dev`, only the `/dev/null` node is writable, so `>/dev/null` keeps working) |
14
- | `workspace-write` | Writes only under `workspaceRoot` + `/tmp` (ephemeral under bwrap, the host `/tmp` under Landlock, `/private/tmp` plus the per-user temp dir under Seatbelt) |
15
- | `danger-full-access` | No confinement; the provider is never consulted. Foreground results carry `sandbox: { mode, denied: false }`; background process handles carry no sandbox facts. |
38
+ | `read-only` (default) | No writes anywhere; of `/dev`, only the `/dev/null` node is writable, so `>/dev/null` keeps working |
39
+ | `workspace-write` | Writes only under the policy's workspace root plus `/tmp` (ephemeral under bwrap, the host `/tmp` under Landlock, `/private/tmp` plus the per-user temp dir under Seatbelt) |
40
+ | `danger-full-access` | No confinement; the provider is never consulted, and results carry `sandbox: { mode, denied: false }` |
16
41
 
17
- Semantics:
42
+ ### Minimal configuration
18
43
 
19
- - **Denials are result facts.** A failed run whose stderr carries the selected backend's own denial dialect — the signatures the provider stamps on every wrap (EROFS text under bwrap, EACCES under Landlock, EPERM under Seatbelt) — is reported as `ShellRunResult.sandbox.denied: true` (conservative classification, read from the collected stderr tail); every CONFINED run also carries the mode it executed under (`result.sandbox.mode`) and the provider's enforcement completeness (`result.sandbox.enforcement`: `full`, or `partial` on an older Landlock ABI).
20
- - **The runner path or syscall must match.** Before a process starts, a rejection is attributed to the runner only when the caller-owned workdir is independently usable and Node reports `ENOENT` or `EACCES` with either an `error.path` equal to provider argv[0] or, when `error.path` is absent, an exact `syscall: 'spawn <runner>'`. A present path also requires `syscall: 'spawn'` or the exact `spawn <runner>`. This covers a missing runner, a non-executable runner, or an executable script whose shebang interpreter is unavailable. A bare `syscall: 'spawn'` without an exact error path, any other code, an invalid or unusable workdir, a resource failure, an unrelated syscall, or an unstructured rejection retains the local executor's command-start failure semantics. Foreground execution throws `SANDBOX_UNAVAILABLE` with the original spawn detail, while asynchronous background settlement stamps `runnerFailed: true` and `denied: false`. If a `SubprocessRuntime` synchronously throws the same runner-identifying `ENOENT`/`EACCES` shape, background start throws `SANDBOX_UNAVAILABLE`; other synchronous errors propagate unchanged. After a process starts, a rule's optional exit-code check and a remaining fatal stderr line must both match after exact informational-line exclusions. A match takes priority over denial; foreground execution throws `SANDBOX_UNAVAILABLE` with the matched fatal line, while a settled background process stamps `process.sandbox.runnerFailed`, which the bash producer renders through generic `job_output`. Confined background handles retain their mode/enforcement facts and release per-process accounting in either path.
21
- - **Deployment fallback, per-call policy.** [`ctx.sandboxPolicy`](../../sandbox/sandbox-policy/) resolves a complete `SandboxExecutionPolicy` for every tool call: the calling session supplies its mode override and immutable cwd root, while deployment config supplies the fallbacks for agentless calls. An approved escalation changes only that policy's mode; its session root stays attached. `resolve()` carries the policy onto the spec, so overlapping commands from different projects run, classify, and report under their own roots and modes. The capability fact `ctx.shell.sandboxMode` reports the configured default so the tool layer advertises escalation only when this executor is mounted; the static bash tool description separately owns denial and escalation guidance.
22
- - **File effects only.** The mode vocabulary claims only file effects. Network stays unrestricted; process visibility is backend-specific and documented by [`dsh-sandbox-local`](../../sandbox/sandbox-local/).
23
- - Process mechanics (spawn, process-group kills, output collection/spill, background handles, credential scrub) are inherited from [`dsh-bash-local`](../bash-local/); runner selection lives in [`dsh-sandbox-local`](../../sandbox/sandbox-local/).
24
-
25
- Deny-only at the seam: a denial is a reported fact, and this executor never negotiates permissions itself — the approval question lives in the tool layer (`dsh-tool-bash`), which drives the override this package honors.
44
+ The executor takes no sandbox configuration of its own: the default mode and workspace root come from `ctx.sandboxPolicy`, and the runner choice belongs to the `ctx.sandbox` provider. Its own config is the local executor's knobs verbatim; the generated [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-bash-sandbox) is the exhaustive source.
26
45
 
27
46
  ```yaml
28
47
  - id: sandbox
@@ -36,13 +55,75 @@ Deny-only at the seam: a denial is a reported fact, and this executor never nego
36
55
  name: '@deepseek-ai/dsh-bash-sandbox'
37
56
  ```
38
57
 
58
+ ### Denials are result facts
59
+
60
+ A denied command is reported, not retried silently: the result carries `sandbox: { mode, denied: true }` and the model-facing tool appends the denial marker. When escalation is available, the model may retry the exact command once with the narrowest wider mode and a one-sentence justification; the approval prompt asks the user, and nothing executes before approval. This executor never negotiates permissions itself — the tool layer drives the override.
61
+
62
+ ### Failures and recovery
63
+
64
+ If no runner can enforce a confined mode, the foreground call fails with `SANDBOX_UNAVAILABLE` and a background process records a runner-failure fact — never a silent unconfined run. A runner-attributable spawn failure carries the original spawn error as detail; other spawn rejections keep the local executor's ordinary command-start semantics.
65
+
66
+ -----
67
+
68
+ <a id="understand-the-implementation"></a>
69
+ ## Understand the implementation
70
+
71
+ <details>
72
+ <summary>Implementation internals — click to expand</summary>
73
+
74
+ This section explains the design of the executor and points at the code that realizes it; the observable behavior is fully covered in [Use this package](#use-this-package).
75
+
76
+ ### Design concept
77
+
78
+ The executor is the sandboxing Service Provider for the `ctx.shell` seam: it inherits `dsh-bash-local`'s process mechanics and re-wraps each command's exact `['bash', '-c', command]` argv through `ctx.sandbox.confine()`, spawning the returned argv directly. Which platform runner confines the command — and whether one is usable at all — is the provider's concern; this package owns the bash side only: the selected mode, enforcement completeness, and denial classification on results.
79
+
80
+ ### Source map
81
+
82
+ | File | Role |
83
+ |---|---|
84
+ | [`src/index.ts`](src/index.ts) | Plugin entry: `SandboxBashExecutor`, per-process fact retention, run/start wrapping |
85
+ | [`src/helpers.ts`](src/helpers.ts) | Denial, runner-failure, and runner-spawn-failure classification |
86
+ | [`src/invariant.ts`](src/invariant.ts) | Invariant companion (no runtime invariant; classification is observable in results) |
87
+ | `tests/` | Exercised behavior across the bwrap, Landlock, and Seatbelt runners |
88
+
89
+ ### Main flow
90
+
91
+ For a confined mode, `resolve()` stamps the per-call policy (the session's mode override, or the deployment fallback); `run` and `start` wrap the bash argv through the provider and hand the confined argv to the inherited subprocess path. At settlement the executor classifies the outcome: a runner failure outranks a denial because the command never ran, a failed run whose stderr carries the backend's denial dialect is reported `denied: true`, and every confined run carries its mode and enforcement facts. `danger-full-access` bypasses the provider entirely and stamps `denied: false`.
92
+
93
+ ### Invariants
94
+
95
+ - **Fail closed** — a confined mode with no usable runner throws `SANDBOX_UNAVAILABLE`; unconfined passthrough never happens for a confined policy.
96
+ - **Deny-only at the seam** — this executor never grants permission; the approval flow lives in the tool layer.
97
+ - **Per-process facts** — confinement facts are retained per handle until settlement, because a provider may vary enforcement between overlapping calls.
98
+ - **File effects only** — the mode vocabulary claims only file effects.
99
+
100
+ </details>
101
+
102
+ -----
103
+
104
+ <a id="further-exploration"></a>
105
+ ## Further Exploration
106
+
107
+ Read these pages when the executor contract is not enough. They move from the seam to the sandbox capability this executor consumes.
108
+
109
+ - [shell seam](../shell/README.md) — the executor contract this provider implements, including the request/spec split.
110
+ - [bash-local](../bash-local/README.md) — the process mechanics this executor inherits.
111
+ - [sandbox seam](../../sandbox/sandbox/README.md) — the confinement capability, its modes, and its fail-closed contract.
112
+ - [sandbox-policy](../../sandbox/sandbox-policy/README.md) — the per-session mode and workspace root this executor honors.
113
+ - [sandbox-local](../../sandbox/sandbox-local/README.md) — the shipped runner backends: bwrap, Landlock, and Seatbelt.
114
+ - [tool-bash](../tool-bash/README.md) — the model-facing `bash` tool and its escalation surface.
115
+ - [Sandbox Agent Note](../../../.agents/notes/implemented/feature/2026-07-06-sandbox.md) — the sandbox design, escalation, and switching contract.
116
+
117
+ -----
118
+
119
+ <a id="model-experience"></a>
39
120
  ## Model Experience
40
121
 
41
122
  ### Bash tool schema, indirectly
42
123
 
43
124
  #### What the model sees
44
125
 
45
- The generated [`dsh-tool-bash` schemas](../../../docs/tool-catalog.md#deepseek-aidsh-tool-bash) are the baseline. By advertising a confining `sandboxMode`, this backend augments `bash` with `sandbox_permissions` using enum `workspace-write` | `danger-full-access` and with `justification`. The policy owner separately contributes the current capability-neutral `sandbox:policy` context.
126
+ The generated [`dsh-tool-bash` schemas](../../../docs/tool-catalog.md#deepseek-aidsh-tool-bash) are the baseline. By advertising a confining `sandboxMode`, this backend augments `bash` with `sandbox_permissions` (enum `workspace-write` | `danger-full-access`) and `justification`. The policy owner separately contributes the current capability-neutral `sandbox:policy` context.
46
127
 
47
128
  #### Token effect
48
129
 
@@ -70,7 +151,7 @@ Append-only; newly visible content follows the reusable request prefix and does
70
151
 
71
152
  #### What the model sees
72
153
 
73
- If no runner can enforce a confined mode, the foreground call propagates the [`SANDBOX_UNAVAILABLE` error owned by `dsh-sandbox`](../../sandbox/sandbox/README.md#confinement-error-indirectly). A runner-attributable spawn failure supplies the original spawn error as detail; a rejection without `ENOENT`/`EACCES` path or syscall evidence that names argv[0] remains an ordinary command-start error. A settled runner failure supplies the matched fatal stderr line and preserves the original stderr collection. When present, the appended `Runner failure: <detail>` is the authoritative diagnosis; the preceding backend-install text is the generic `SANDBOX_UNAVAILABLE` prefix.
154
+ If no runner can enforce a confined mode, the foreground call propagates the `SANDBOX_UNAVAILABLE` error from the sandbox seam. A runner-attributable spawn failure supplies the original spawn error as detail; a rejection without `ENOENT`/`EACCES` path or syscall evidence that names `argv[0]` remains an ordinary command-start error. A settled runner failure supplies the matched fatal stderr line and preserves the original stderr collection; the appended `Runner failure: <detail>` is the authoritative diagnosis over the generic `SANDBOX_UNAVAILABLE` prefix.
74
155
 
75
156
  #### Token effect
76
157
 
@@ -82,7 +163,22 @@ Append-only; newly visible content follows the reusable request prefix and does
82
163
 
83
164
  ## Known Limitations and Deferred Work
84
165
 
166
+ <a id="known-limitations-and-deferred-work"></a>
167
+
168
+
169
+ These limits define when this executor is not a general security boundary. They are current package constraints, not a roadmap.
170
+
85
171
  - **Confinement covers file effects only** — network restriction and a uniform process-visibility guarantee are absent, so the modes are not a general-purpose security sandbox.
86
172
  - **Denials are inferred from failed-command stderr** — backend signatures make the inference portable, but a matching application error can be classified as a denial and a denial omitted from the retained tail can be missed.
87
- - **An asynchronously observed background runner failure has no immediate error channel** — it is recorded on the settled process and surfaces when the caller reads the generic task with `job_output`; a synchronous `SubprocessRuntime` throw that names the runner path instead fails `start()` immediately.
173
+ - **An asynchronously observed background runner failure has no immediate error channel** — it is recorded on the settled process and surfaces when the caller reads the generic task with `job_output`; a synchronous subprocess throw that names the runner path instead fails `start()` immediately.
88
174
  - **`danger-full-access` deliberately bypasses `ctx.sandbox`** — it is an explicit unconfined mode, not a wider sandbox profile.
175
+
176
+ <a id="dev-note"></a>
177
+ ### Dev Note
178
+
179
+ <details>
180
+ <summary>Working context for maintainers — click to expand</summary>
181
+
182
+ None.
183
+
184
+ </details>
package/README.zh.md CHANGED
@@ -1,28 +1,47 @@
1
+ ---
2
+ description: "面向部署方与维护者的沙箱 Bash 执行器说明,用于选择、配置或排查受限命令执行及其拒绝与升权事实。"
3
+ kind: "package-reference"
4
+ ---
5
+
1
6
  # @deepseek-ai/dsh-bash-sandbox
2
7
 
3
8
  [English](README.md) | 中文
4
9
 
5
- 这是使用沙箱能力的 [`@deepseek-ai/dsh-shell`](../shell/) 执行器 seam 的 Service Provider。加载它时,应**用它替代** `@deepseek-ai/dsh-bash-local`,并同时加载 [`ctx.sandbox`](../../sandbox/sandbox/) 提供方(例如 [`@deepseek-ai/dsh-sandbox-local`](../../sandbox/sandbox-local/))及 [`ctx.sandboxPolicy`](../../sandbox/sandbox-policy/);默认模式和工作区根目录由后者负责,并与受沙箱约束的文件系统共享这些设置。无需使用替代工具插件;`dsh-tool-bash` 会检测执行器的 `sandboxMode` 能力并添加升权字段。
10
+ ## 概述
11
+
12
+ `dsh-bash-sandbox` 是沙箱消费型 Bash 执行器:每条命令都以全新的 `bash -c` 进程运行,经 `ctx.sandbox` 能力隔离,而不是以 harness 进程的完整文件权限运行。每个已结算的结果都携带命令运行时的模式、沙箱是否拒绝了文件操作,以及所选 runner 对请求模式的强制执行完整度。当没有 runner 能强制执行受限模式时,调用按失败关闭原则抛结构化 `SANDBOX_UNAVAILABLE` 错误,绝不无隔离地运行。它是 `dsh-bash-local` 的受限兄弟包——共享其进程机制——工具层的升权字段也只在挂载它时才出现。
13
+
14
+ ## 目录
15
+
16
+ - [使用本包](#use-this-package)
17
+ - [理解实现](#understand-the-implementation)
18
+ - [进一步探索](#further-exploration)
19
+ - [模型体验](#model-experience)
20
+ - [已知限制与延期工作](#known-limitations-and-deferred-work)
21
+ - [开发备注](#dev-note)
22
+
23
+ -----
6
24
 
7
- 包根目录导出默认与具名的 `SandboxBashExecutor` 插件及其 `Config`;结果分类 helper 保留在内部。
25
+ <a id="use-this-package"></a>
26
+ ## 使用本包
8
27
 
9
- 每条命令的限制方式都是:把本执行器即将 spawn 的精确 `['bash', '-c', command]` argv 交给提供方,并直接 spawn 返回的 argv。使用随附的原生 runner 时,内层 Bash 保留 shell 语义,并且只在 runner 建立约束后才求值 `BASH_ENV`。由哪种平台 runner 执行限制,以及是否有 runner 可用,属于提供方职责;若无可用 runner,则按失败关闭原则拒绝执行并返回结构化 `SANDBOX_UNAVAILABLE` 错误,绝不能静默地无约束运行。本包只负责 bash 侧。
28
+ 当命令不得以 harness 进程的完整文件权限运行时,用本执行器替代 `dsh-bash-local`。它注册为 `ctx.shell`,并要求一个 `ctx.sandbox` 提供方加上 `ctx.sandboxPolicy`;面向模型的 `bash` 工具基于它不加改动地工作,并公布 `sandbox_permissions`/`justification` 升权字段。
29
+
30
+ ### 何时选择
31
+
32
+ 当部署需要为 Bash 命令提供文件级隔离时选择它:已配置的策略决定默认模式与工作区根目录,每个会话还可以通过工具的升权流程按调用使用不同模式。模式只约束文件影响——网络仍不受限制,进程可见性因后端而异。需要非隔离执行,或平台没有可用沙箱后端时,请改为挂载 `dsh-bash-local`。
33
+
34
+ ### 模式与文件影响
10
35
 
11
36
  | 模式 | 文件影响 |
12
37
  |---|---|
13
- | `read-only`(默认) | 任何位置都不可写(在 `/dev` 中只有 `/dev/null` 节点可写,因此 `>/dev/null` 仍可正常工作) |
14
- | `workspace-write` | 只能写入 `workspaceRoot` + `/tmp`(在 bwrap 下为临时目录,在 Landlock 下为宿主 `/tmp`,在 Seatbelt 下为 `/private/tmp` 加每用户临时目录) |
15
- | `danger-full-access` | 不作限制;绝不咨询提供方。前台结果携带 `sandbox: { mode, denied: false }`;后台进程句柄不携带沙箱事实。 |
38
+ | `read-only`(默认) | 任何位置都不可写;在 `/dev` 中只有 `/dev/null` 节点可写,因此 `>/dev/null` 仍可正常工作 |
39
+ | `workspace-write` | 只能写入策略的工作区根目录加 `/tmp`(bwrap 下为临时目录,Landlock 下为宿主 `/tmp`,Seatbelt 下为 `/private/tmp` 加每用户临时目录) |
40
+ | `danger-full-access` | 不作限制;绝不咨询提供方,结果携带 `sandbox: { mode, denied: false }` |
16
41
 
17
- 语义:
42
+ ### 最小配置
18
43
 
19
- - **拒绝是结果事实。** 如果一次失败运行的 stderr 包含所选后端自身的拒绝方言,即提供方在每次包装时加上的特征(bwrap 下的 EROFS 文本、Landlock 下的 EACCES、Seatbelt 下的 EPERM),则结果报告 `ShellRunResult.sandbox.denied: true`(从已收集的 stderr 尾部进行保守分类)。每次受限制运行还会携带执行时模式(`result.sandbox.mode`)与提供方强制执行完整性(`result.sandbox.enforcement`:`full`,或在较旧 Landlock ABI 上为 `partial`)。
20
- - **Runner 路径或 syscall 必须匹配。** 进程启动前,调用方拥有的 workdir 必须经独立验证可用,Node 必须报告 `ENOENT` 或 `EACCES`,并且错误必须符合以下一种形态:`error.path` 等于提供方返回的 `argv[0]`,同时 `syscall` 为 `'spawn'` 或精确的 `'spawn <runner>'`;或者 `error.path` 不存在,同时 `syscall` 为精确的 `'spawn <runner>'`。这样可以识别缺失的 runner、不可执行的 runner,或 shebang 解释器不可用的可执行脚本。没有精确错误路径的裸 `syscall: 'spawn'`、任何其他错误码、无效或不可用的 workdir、资源失败、无关 syscall 或无结构拒绝仍保留本地执行器的命令启动失败语义。前台执行会抛出 `SANDBOX_UNAVAILABLE` 并附带原始 spawn 错误详情,异步后台结算则会标记 `runnerFailed: true` 和 `denied: false`。如果 `SubprocessRuntime` 同步抛出同样能指明 runner 的 `ENOENT`/`EACCES` 形态,后台启动会抛出 `SANDBOX_UNAVAILABLE`;其他同步错误原样传播。进程启动后,先按整行精确匹配排除信息性行,随后规则的可选退出码检查和余下 stderr 中的一行致命诊断必须同时匹配。匹配结果优先于拒绝;前台执行会抛出 `SANDBOX_UNAVAILABLE` 并附带匹配到的致命行,已结算的后台进程则会标记 `process.sandbox.runnerFailed`,Bash 结果生成方通过通用 `job_output` 渲染它。无论走哪条路径,受限制的后台句柄都会保留自身的模式/强制执行事实,并释放每进程计数。
21
- - **部署回退,每次调用策略。** [`ctx.sandboxPolicy`](../../sandbox/sandbox-policy/) 为每次工具调用解析完整的 `SandboxExecutionPolicy`:调用会话提供自身的模式覆盖与不可变 cwd 根目录,部署配置则为无 agent(智能体)调用提供回退。已批准的升权只更改该策略的模式,会话根目录仍然附着其上。`resolve()` 把策略带入 spec,因此来自不同项目的重叠命令会在各自的根目录与模式下运行、分类和报告。能力事实 `ctx.shell.sandboxMode` 报告已配置的默认值,因此工具层只在装载该执行器时才公布升权;静态 bash 工具描述则单独负责拒绝与升权引导。
22
- - **只限制文件影响。** 模式词汇只声称文件影响。网络仍不受限制;进程可见性因后端而异,具体见 [`dsh-sandbox-local`](../../sandbox/sandbox-local/)。
23
- - 进程机制(spawn、进程组终止、输出收集/spill、后台句柄、凭证清理)继承自 [`dsh-bash-local`](../bash-local/);runner 选择位于 [`dsh-sandbox-local`](../../sandbox/sandbox-local/)。
24
-
25
- 该 seam 只报告拒绝:拒绝是一项结果事实,本执行器绝不自行协商权限。批准问题位于工具层(`dsh-tool-bash`),由它设置本包所遵守的模式覆盖值。
44
+ 本执行器自身不携带任何沙箱配置:默认模式与工作区根目录来自 `ctx.sandboxPolicy`,runner 选择属于 `ctx.sandbox` 提供方。它自己的配置就是本地执行器的旋钮,逐字继承;生成的[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-bash-sandbox)是穷尽式真源。
26
45
 
27
46
  ```yaml
28
47
  - id: sandbox
@@ -36,13 +55,75 @@
36
55
  name: '@deepseek-ai/dsh-bash-sandbox'
37
56
  ```
38
57
 
58
+ ### 拒绝是结果事实
59
+
60
+ 被拒绝的命令会被报告,而不是静默重试:结果携带 `sandbox: { mode, denied: true }`,面向模型的工具会追加拒绝标记。当升权可用时,模型可以用最窄的充分宽模式与一句理由重试同一条命令一次;批准提示会询问用户,未经批准绝不执行任何东西。本执行器自身绝不协商权限——覆盖值由工具层驱动。
61
+
62
+ ### 失败与恢复
63
+
64
+ 如果没有 runner 能强制执行受限模式,前台调用以 `SANDBOX_UNAVAILABLE` 失败,后台进程则记录 runner 失败事实——绝不会静默无隔离运行。可归因于 runner 的 spawn 失败以原始 spawn 错误作为详情;其他 spawn 拒绝保持本地执行器普通的命令启动语义。
65
+
66
+ -----
67
+
68
+ <a id="understand-the-implementation"></a>
69
+ ## 理解实现
70
+
71
+ <details>
72
+ <summary>实现细节——点击展开</summary>
73
+
74
+ 本节解释执行器的设计并指出实现它们的代码位置;可观察行为已在[使用本包](#use-this-package)中完整说明。
75
+
76
+ ### 设计概念
77
+
78
+ 本执行器是 `ctx.shell` seam 的沙箱 Service Provider:它继承 `dsh-bash-local` 的进程机制,把每条命令的精确 `['bash', '-c', command]` argv 经 `ctx.sandbox.confine()` 重新包装,并直接 spawn 返回的 argv。由哪种平台 runner 限制命令、以及是否有 runner 可用,属于提供方职责;本包只负责 bash 侧:所选模式、强制执行完整度,以及结果上的拒绝分类。
79
+
80
+ ### 源码地图
81
+
82
+ | 文件 | 职责 |
83
+ |---|---|
84
+ | [`src/index.ts`](src/index.ts) | 插件入口:`SandboxBashExecutor`、按进程保留事实、run/start 包装 |
85
+ | [`src/helpers.ts`](src/helpers.ts) | 拒绝、runner 失败与 runner spawn 失败分类 |
86
+ | [`src/invariant.ts`](src/invariant.ts) | 不变式伴生插件(无运行时不变式;分类在结果中可观察) |
87
+ | `tests/` | 跨 bwrap、Landlock 与 Seatbelt runner 演练的行为 |
88
+
89
+ ### 主要流程
90
+
91
+ 对受限模式,`resolve()` 标记每次调用的策略(会话的模式覆盖值,或部署回退);`run` 与 `start` 把 bash argv 经提供方包装,再把受限 argv 交给继承的 subprocess 路径。结算时执行器对结果分类:runner 失败优先于拒绝(命令从未运行),stderr 携带后端拒绝方言的失败运行报告 `denied: true`,每次受限运行都携带模式与强制执行事实。`danger-full-access` 完全绕过提供方,并标记 `denied: false`。
92
+
93
+ ### 不变式
94
+
95
+ - **失败关闭**——受限模式没有可用 runner 时抛 `SANDBOX_UNAVAILABLE`;受限策略绝不会出现无隔离直通。
96
+ - **seam 只报告拒绝**——本执行器从不授予权限;批准流程位于工具层。
97
+ - **按进程保留事实**——隔离事实在结算前按句柄保留,因为提供方可能在重叠调用之间改变强制执行方式。
98
+ - **只约束文件影响**——模式词汇只声称文件影响。
99
+
100
+ </details>
101
+
102
+ -----
103
+
104
+ <a id="further-exploration"></a>
105
+ ## 进一步探索
106
+
107
+ 当执行器约定不够用时阅读以下页面。它们从 seam 进入本执行器所消费的沙箱能力。
108
+
109
+ - [shell seam](../shell/README.zh.md) —— 本提供方实现的执行器约定,包括请求/spec 拆分。
110
+ - [bash-local](../bash-local/README.zh.md) —— 本执行器继承的进程机制。
111
+ - [sandbox seam](../../sandbox/sandbox/README.zh.md) —— 隔离能力、其模式与失败关闭约定。
112
+ - [sandbox-policy](../../sandbox/sandbox-policy/README.zh.md) —— 本执行器遵守的每会话模式与工作区根目录。
113
+ - [sandbox-local](../../sandbox/sandbox-local/README.zh.md) —— 随附的 runner 后端:bwrap、Landlock 与 Seatbelt。
114
+ - [tool-bash](../tool-bash/README.zh.md) —— 面向模型的 `bash` 工具及其升权面。
115
+ - [沙箱 Agent Note](../../../.agents/notes/implemented/feature/2026-07-06-sandbox.zh.md) —— 沙箱设计、升权与切换约定。
116
+
117
+ -----
118
+
119
+ <a id="model-experience"></a>
39
120
  ## 模型体验
40
121
 
41
122
  ### 间接的 Bash 工具 schema
42
123
 
43
124
  #### 模型看到的内容
44
125
 
45
- 基线是生成的 [`dsh-tool-bash` schema](../../../docs/tool-catalog.zh.md#deepseek-aidsh-tool-bash)。通过公布表明启用隔离的 `sandboxMode` 能力,此后端会为 `bash` 增加 `sandbox_permissions`,其 enum 为 `workspace-write` | `danger-full-access`,并增加 `justification`。策略归属方会另行贡献当前且不区分具体能力的 `sandbox:policy` 上下文。
126
+ 基线是生成的 [`dsh-tool-bash` schema](../../../docs/tool-catalog.zh.md#deepseek-aidsh-tool-bash)。通过公布表明启用隔离的 `sandboxMode` 能力,此后端会为 `bash` 增加 `sandbox_permissions`(enum 为 `workspace-write` | `danger-full-access`)与 `justification`。策略归属方会另行贡献当前且不区分具体能力的 `sandbox:policy` 上下文。
46
127
 
47
128
  #### Token 影响
48
129
 
@@ -60,7 +141,7 @@
60
141
 
61
142
  #### Token 影响
62
143
 
63
- 除普通输出外,正常允许的运行不会增加 token。拒绝或失败会增加上述有条件标记,并保留到上下文压缩(context compaction)。
144
+ 除普通输出外,正常允许的运行不会增加 token。拒绝或失败会增加上述有条件标记,并保留到上下文压缩。
64
145
 
65
146
  #### KV Cache 影响
66
147
 
@@ -70,7 +151,7 @@
70
151
 
71
152
  #### 模型看到的内容
72
153
 
73
- 如果没有 runner 能强制执行受限模式,前台调用会传播 [`SANDBOX_UNAVAILABLE` 错误](../../sandbox/sandbox/README.zh.md#confinement-error-indirectly);该错误由 `dsh-sandbox` 定义。判定为 runner 失败的 spawn 错误会以原始 spawn 错误作为详细信息;如果拒绝没有通过 `ENOENT`/`EACCES` 的 `path` 或 `syscall` 证据指明 `argv[0]`,它仍是普通的命令启动错误。已结算的 runner 失败则以匹配到的致命 stderr 行作为详细信息,并保留原始 stderr 收集结果。如果追加了 `Runner failure: <detail>`,它就是权威诊断;前面的后端安装文本只是通用的 `SANDBOX_UNAVAILABLE` 前缀。
154
+ 如果没有 runner 能强制执行受限模式,前台调用会传播来自 sandbox seam 的 `SANDBOX_UNAVAILABLE` 错误。可归因于 runner spawn 失败以原始 spawn 错误作为详情;没有 `ENOENT`/`EACCES` 的 `path` 或 `syscall` 证据指明 `argv[0]` 的拒绝仍是普通的命令启动错误。已结算的 runner 失败以匹配到的致命 stderr 行作为详情,并保留原始 stderr 收集结果;追加的 `Runner failure: <detail>` 是权威诊断,优先于通用的 `SANDBOX_UNAVAILABLE` 前缀。
74
155
 
75
156
  #### Token 影响
76
157
 
@@ -80,9 +161,24 @@
80
161
 
81
162
  仅追加;新可见内容位于可复用请求前缀之后,不会使现有 KV Cache 条目失效。
82
163
 
83
- ## 已知限制与暂缓事项
164
+ ## 已知限制与延期工作
165
+
166
+ <a id="known-limitations-and-deferred-work"></a>
167
+
168
+
169
+ 这些限制说明本执行器何时不是通用安全边界。它们是当前包约束,不是路线图。
170
+
171
+ - **限制只覆盖文件影响**——不提供网络限制和统一的进程可见性保证,因此这些模式不是通用安全沙箱。
172
+ - **拒绝从失败命令的 stderr 推断**——后端特征使该推断可跨平台使用,但包含相同特征的应用错误可能被分类为拒绝,也可能遗漏未出现在保留尾部中的拒绝。
173
+ - **异步观测到的后台 runner 失败没有即时错误通道**——它记录在已结算进程上,并在调用方用 `job_output` 读取通用任务时呈现;同步抛出且指明 runner 路径的 subprocess 错误则会让 `start()` 立即失败。
174
+ - **`danger-full-access` 有意绕过 `ctx.sandbox`**——它是显式无约束模式,不是更宽的沙箱 profile。
175
+
176
+ <a id="dev-note"></a>
177
+ ### 开发备注
178
+
179
+ <details>
180
+ <summary>维护者的工作上下文——点击展开</summary>
181
+
182
+ None.
84
183
 
85
- - **限制只覆盖文件影响**:不提供网络限制和统一的进程可见性保证,因此这些模式不是通用安全沙箱。
86
- - **拒绝从失败命令的 stderr 推断**:后端特征使该推断可跨平台使用,但包含相同后端特征的应用错误可能被分类为拒绝,也可能遗漏未出现在保留尾部中的拒绝。
87
- - **异步观测到的后台 runner 失败没有即时错误通道**:它记录在已结算进程上,并在调用方使用 `job_output` 读取通用任务时呈现;`SubprocessRuntime` 同步抛出的错误包含 runner 路径时,则会使 `start()` 立即失败。
88
- - **`danger-full-access` 有意绕过 `ctx.sandbox`**:它是显式无约束模式,不是更宽的沙箱 profile。
184
+ </details>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-bash-sandbox",
3
3
  "description": "Sandbox-consuming implementation of the DeepSeek Harness bash executor seam (confines every command via ctx.sandbox, reports denial/enforcement result facts)",
4
- "version": "0.1.1-rc.2",
4
+ "version": "0.1.2-alpha.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -32,22 +32,23 @@
32
32
  ],
33
33
  "license": "MIT",
34
34
  "peerDependencies": {
35
- "@deepseek-ai/dsh-shell": "^0.1.1-rc.2",
36
- "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
37
- "@deepseek-ai/dsh-sandbox": "^0.1.1-rc.2",
38
- "@deepseek-ai/dsh-sandbox-policy": "^0.1.1-rc.2",
39
- "@deepseek-ai/cordis": "^4.0.1",
40
- "@deepseek-ai/dsh-bash-local": "^0.1.1-rc.2"
35
+ "@deepseek-ai/dsh-shell": "^0.1.2-alpha.2",
36
+ "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.2",
37
+ "@deepseek-ai/dsh-sandbox-policy": "^0.1.2-alpha.2",
38
+ "@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.2",
39
+ "@deepseek-ai/cordis": "^4.0.2",
40
+ "@deepseek-ai/dsh-bash-local": "^0.1.2-alpha.2"
41
41
  },
42
42
  "devDependencies": {
43
- "@deepseek-ai/dsh-shell": "^0.1.1-rc.2",
44
- "@deepseek-ai/dsh-bash-local": "^0.1.1-rc.2",
45
- "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
46
- "@deepseek-ai/dsh-subprocess-local": "^0.1.1-rc.2",
47
- "@deepseek-ai/dsh-sandbox": "^0.1.1-rc.2",
48
- "@deepseek-ai/dsh-sandbox-local": "^0.1.1-rc.2",
49
- "@deepseek-ai/dsh-sandbox-policy": "^0.1.1-rc.2",
50
- "@deepseek-ai/cordis": "^4.0.1",
51
- "@deepseek-ai/node-addon-landlock-run": "^0.1.1"
43
+ "@deepseek-ai/dsh-bash-local": "^0.1.2-alpha.2",
44
+ "@deepseek-ai/dsh-shell": "^0.1.2-alpha.2",
45
+ "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.2",
46
+ "@deepseek-ai/dsh-subprocess-local": "^0.1.2-alpha.2",
47
+ "@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.2",
48
+ "@deepseek-ai/dsh-sandbox-local": "^0.1.2-alpha.2",
49
+ "@deepseek-ai/cordis": "^4.0.2",
50
+ "@deepseek-ai/node-addon-landlock-run": "^0.1.1",
51
+ "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.2",
52
+ "@deepseek-ai/dsh-sandbox-policy": "^0.1.2-alpha.2"
52
53
  }
53
54
  }