# Conflicts: # docs/config-catalog.md # docs/development.i18n.yaml # docs/development.zh.md # docs/rfc/implemented/feature/2026-06-14-acp-agent-client-protocol.md # docs/rfc/implemented/feature/2026-07-06-sandbox.md # examples/AGENTS.md # examples/README.md # examples/acp-agent/README.md # examples/acp-agent/cordis.yml # examples/acp-agent/tests/acp.e2e.ts # examples/acp-agent/tests/escalation.e2e.ts # examples/sandbox-acp-agent/README.md # examples/sandbox-acp-agent/cordis.snapshot.yml # examples/sandbox-acp-agent/cordis.yml # examples/sandbox-acp-agent/tests/acp.snapshot.ts # packages/ui/acp-agent/src/bin.ts # packages/ui/acp/README.md # packages/ui/jsonrpc-agent/README.md # packages/ui/jsonrpc-agent/src/bin.ts # scripts/verify-translation-pairing.ts
@deepseek-ai/dsh-bash-sandbox
Sandbox-consuming implementation of the @deepseek-ai/dsh-bash executor seam. Load it instead of @deepseek-ai/dsh-bash-local, together with a ctx.sandbox provider (e.g. @deepseek-ai/dsh-sandbox-local) — the model-facing tool layer (dsh-tool-bash) is untouched; that swap is exactly what the seams exist for.
Every command is confined by handing the provider the exact ['bash', '-c', command] argv this executor is about to spawn and spawning the returned (wrapped) argv instead. 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.
| Mode | File effects |
|---|---|
read-only (default) |
No writes anywhere (of /dev, only the /dev/null node is writable, so >/dev/null keeps working) |
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) |
danger-full-access |
No confinement; the provider is never consulted. Execution is dsh-bash-local's verbatim — foreground results still carry sandbox: { mode, denied: false } (no enforcement: nothing was confined), background tasks carry no sandbox facts |
Semantics:
- 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
BashRunResult.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, orpartialon an older Landlock ABI). - Runner failures are sandbox failures, never task failures. A failed run matching the wrap's
runnerFailureSignatures(the runner's own error prefix — also what the shell prints for a missing runner) means the sandbox itself broke and the command NEVER RAN; the check outranks denial classification because a runner's error text can contain denial words. The foreground path re-throws it as the structured fail-closedSANDBOX_UNAVAILABLEerror, with the runner's first stderr line as the cause; a settled background task stampstask.sandbox.runnerFailedinstead (no error channel remains after settle), whichbash_outputrenders as its own marker. - Config default, per-call override.
resolve()stamps the configured sandbox mode onto each spec unless an approved request supplies a wider mode. That override affects only its call or background task.ctx.bash.sandboxModereports the default so the tool advertises escalation only when supported; results report the effective mode. The model learns standing mode only from tool/result facts, not a system-prompt announcement. - File effects only. Network and process visibility are deliberately not restricted — the mode vocabulary does not pretend to cover what the backend does not enforce.
- Process mechanics (spawn, process-group kills, output collection/spill, background tasks, credential scrub) are inherited verbatim from
dsh-bash-local; the runner ladder, probes, and the per-platform Landlock launcher packages live withdsh-sandbox-local.
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.
- id: sandbox
name: '@deepseek-ai/dsh-sandbox-local'
- id: bash
name: '@deepseek-ai/dsh-bash-sandbox'
config:
mode: read-only
workspaceRoot: !!js process.cwd()
The keyless consumer-integration proofs are tests/bwrap.e2e.ts, tests/landlock.e2e.ts, and tests/seatbelt.e2e.ts (the real provider + real runner driven through ctx.bash, world-verified, each self-skipping where its runner is absent); see the acp-agent example's default composition for the runnable demo.