Files
deepseek-harness/packages/bash-local
Tianyi Cui 8b5a3ef730 Add bash execution: dsh-bash seam, dsh-bash-local impl, dsh-tool-bash tools
Three packages following the new capability-seam pattern (interface /
implementation / consumer, now documented in docs/architecture.md):

- dsh-bash: abstract BashExecutor service (ctx.bash) + vocabulary types.
- dsh-bash-local: local subprocesses — bash -c per call in a detached
  process group, SIGTERM→SIGKILL group kills, tail-keep truncation with
  full-stream spill files, model-friendly env, background task registry.
- dsh-tool-bash: the bash / bash_output / bash_kill tool schemas with
  runtime arg validation and background completion notices via
  agent.inject(). Non-zero exits are reported, not errored.

Design surveyed against the bash tools of Claude Code, OpenCode, Codex,
and pi (notes in the package READMEs). Permissions/sandbox stay TODO on
the tools/execute waterfall seam; stateful-shell alternatives recorded
in run.ts.
2026-06-13 18:28:10 +08:00
..

@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

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. TODO(stateful-shell) in src/run.ts records 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 a 3s grace (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 maxOutputBytes keeps 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. The model can grep/tail the spill file with bash itself.
  • Model-friendly envNO_COLOR=1 TERM=dumb PAGER=cat GIT_PAGER=cat (Codex's hardcoded set) so pagers and ANSI color don't garble results.
  • Background tasksstart() returns immediately, no timeout applies (Claude Code detaches timeouts when backgrounding), readOutput() is incremental with whole-stream byte offsets, and disposal kills everything.

Sandboxing

TODO(permissions/sandbox): execution policy does NOT belong in this package. Wrap the tools/execute waterfall (veto/ask) or implement a sandboxing BashExecutor — see docs/architecture.md § plugin checklist. Reference points: Claude Code wraps commands in sandbox-exec/bubblewrap; Codex applies seatbelt/landlock plus an execpolicy prefix-rule engine.