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.
@deepseek-ai/dsh-bash
The bash executor seam: an abstract BashExecutor service (ctx.bash)
defining WHAT a bash backend does — run commands, manage background tasks —
without saying HOW.
This package is one third of the bash capability, split so each concern can evolve (and be swapped) independently:
| Package | Role |
|---|---|
@deepseek-ai/dsh-bash (this) |
the interface: abstract service + vocabulary types |
@deepseek-ai/dsh-bash-local |
an implementation: local subprocesses |
@deepseek-ai/dsh-tool-bash |
the model-facing tool schemas over ctx.bash |
The split mirrors the LLM seam (LlmService/LlmAdapter) and the agent-tool
survey: pi hides execution behind a BashOperations interface (local shell /
SSH / VM backends), Codex behind an exec-server protocol. A future sandboxed,
containerized, or remote executor implements this interface and the tool
schemas don't change.
Service API (ctx.bash)
| Member | Semantics |
|---|---|
run(spec) |
Foreground execution. Resolves when the command finishes. Rejects only for infrastructure failures (unusable workdir, missing shell, pre-aborted signal); nonzero exits, timeout kills, and abort kills resolve with a descriptive BashRunResult. |
start(spec) |
Background execution. Returns a BashTask handle immediately; no timeout applies (stop tasks via kill). |
get(id) / list() |
Task lookup. |
readOutput(id) |
Incremental output read — consecutive reads never re-deliver. Reads that lost data to buffer bounds flag lossy and point at full-stream spill files. Throws for unknown ids. |
kill(id) |
Kill a running task. Returns false when it already finished; throws for unknown ids. |
onTaskDone(listener) |
Completion listener (effect-based, disposer returned). Fires exactly once per task; never after the service is disposed. |
Implementations subclass BashExecutor, implement the abstract methods, and
call notifyTaskDone(task) on background completion. Disposal must kill every
running task (no orphan processes) — see the HMR-safety tests.
Vocabulary
BashExecSpec (command, workdir?, timeoutMs?, signal?) →
BashRunResult (exitCode, signal, timedOut, aborted, timeoutMs, stdout/stderr
as CollectedOutput) and BashTask/BashTaskRead for the background side.
See src/types.ts for the full contracts.