mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
The three-entry cordis.yml (dsh-sandbox-local + dsh-bash-sandbox at a read-only default + dsh-approval) served over ACP: the first live approval composition. Recorded snapshot scenarios pin the wire end to end — config-options advertisement, the mode-switching arc as the suite pinned header (both switches, the prompt-section delta, one changed-by-the-user notice per knob, a confined write landing under the switched mode), and both escalation branches over scripted permissionAnswers (a grant runs confined under workspace-write; a rejection executes nothing and pins the fail-closed text). The with-key escalation e2e drives a real model + real runner + the real bridge answerer, world-verified; ci.yml snapshot lane and e2e.yml install bubblewrap so the confined replays actually execute. Both RFCs move to implemented/ (Decision/Consequences form, deferred phases tracked in their own sections), with every cross-reference flipped.
@deepseek-ai/dsh-bash-local
Local-subprocess implementation of the @deepseek-ai/dsh-bash executor seam: LocalBashExecutor spawns bash -c <command> per call in its own process group, collects bounded output with full-stream spill files, and escalates kills SIGTERM→SIGKILL across the whole group.
Config
- id: bash
name: '@deepseek-ai/dsh-bash-local'
config:
cwd: /path/to/workspace # default: process.cwd()
timeoutMs: 120000 # default foreground timeout
maxTimeoutMs: 600000 # cap for per-call overrides
maxOutputBytes: 64000 # per-stream in-memory cap; overflow spills to disk
graceMs: 3000 # SIGTERM→SIGKILL escalation grace on kills
Behavior (and where it came from)
Design surveyed against the bash tools of Claude Code, OpenCode, Codex, and pi; the notable choices:
- Spawn per call, no shell state — every call is a fresh non-login
bash -c(deterministic; no rc files). All four surveyed tools spawn per call.XXX(stateful-shell)insrc/run.tsrecords the two proven stateful designs (Claude Code's cwd-only persistence; Codex's PTY exec sessions) for when real workflows demand them. - Process-group kills with escalation — children are spawned
detached(own process group); kills send SIGTERM to the group, then SIGKILL after thegraceMsgrace (default 3s — OpenCode's escalation; pipelines and subshells die with the parent). ESRCH is tolerated; daemons that re-parent away from the group can still survive — same caveat as the surveyed tools. - Tail-keep truncation + spill files — output beyond
maxOutputByteskeeps the in-memory TAIL (errors/results cluster at the end — pi/OpenCode rationale) while the FULL stream is appended to a temp file whose path is reported when available. If the final spill close reports a delayed writeback failure, the executor still returns the tail but withholds the path rather than advertising a possibly incomplete file. - Model-friendly env + credential scrub —
process.envminus credential-shaped vars (*KEY*/*SECRET*/*TOKEN*), thenNO_COLOR=1 TERM=dumb PAGER=cat GIT_PAGER=cat(Codex's hardcoded set) so pagers and ANSI color don't garble results. This scrub is the security control that keeps the harness's ambient credentials out of a spawned command. A spec'senvis merged LAST (after the scrub), so a caller's explicit entry — a value it already holds — wins even on a credential-shaped name. The spec'sstdin, when supplied, is written to the child and closed; with none supplied, fd 0 is/dev/null— the exact pre-seam default, so a command that probes stdin's file type is unaffected. Bothenv/stdinare set by in-process plugins (the hooks bridges); the model-facing tool doesn't expose them. See the bash-stdin-env RFC. - Background tasks —
start()returns immediately, no timeout applies (Claude Code detaches timeouts when backgrounding),readOutput()is incremental with whole-stream byte offsets, and disposal kills everything. The spec's opaqueownertoken is stored on the tracked task and returned byownerOf(id)— the executor never interprets it (the consumer's access policy does), and because it lives with the task here it survives atool-bashHMR reload.
Sandboxing
Execution policy does NOT belong in this package: this executor always runs commands unconfined. Confinement is dsh-bash-sandbox, which extends this executor verbatim and confines commands under the ctx.sandbox seam's bwrap/Landlock/Seatbelt backends (sandbox RFC); per-call allow/deny/ask policy belongs on the tools/pre-execute gate.