| description | The model-facing bash tool for users and maintainers choosing, configuring, or debugging one-shot command execution, background jobs, and sandbox escalation. |
|---|---|
| kind | package-reference |
English | 中文
dsh-tool-bash gives the agent a bash tool that runs commands through the mounted shell executor and returns stdout, stderr, and exit markers. Each call runs in a fresh shell — no cwd, variables, or functions survive — and run_in_background turns long-running commands into background jobs the agent collects with job_output and stops with job_kill. Every call runs with the managed DSH_* environment from dsh-shell-env, and under a sandboxing executor a denied command may be retried once with a wider sandbox_permissions mode plus a justification through user approval. Non-zero exits are reported, not failed, so the agent decides how to react. Mount it together with an executor provider such as dsh-bash-local or dsh-bash-sandbox and the dsh-shell-env plugin.
- Use this package
- Understand the implementation
- Further Exploration
- Model Experience
- Known Limitations and Deferred Work
- Dev Note
Load this plugin in any composition where the agent should run bash commands: it registers the bash tool once an executor provider and the dsh-shell-env registry are mounted, and stays pending until the tools, shell, systemPrompt, and shellEnv services exist.
The common path is an executor provider, the environment registry, and this tool; add the job runtime when the agent may run commands in the background.
- name: '@deepseek-ai/dsh-bash-local'
- name: '@deepseek-ai/dsh-shell-env'
- name: '@deepseek-ai/dsh-tool-bash'
# Optional: background jobs
- name: '@deepseek-ai/dsh-jobs-local'
- name: '@deepseek-ai/dsh-tool-jobs'The single config field toggles background support.
| Field | Default | Meaning |
|---|---|---|
enableRunInBackground |
true |
Expose run_in_background; when false, forced background calls are rejected |
The generated configuration catalog is the exhaustive source for every accepted field and its JSDoc; the generated tool catalog carries the full argument schema.
The tool executes bash -c <command> and returns the combined output. Commands run in a fresh shell every call, so state never persists — pass workdir instead of cd. A non-zero exit is reported as [exit code: N] for the agent to interpret, not surfaced as a tool error. A description in active voice (5–10 words) labels the call in the UI; timeoutMs overrides the executor's default and cap. Output beyond the executor's stream caps is truncated to its tail, with the full output saved to a spill file whose path is reported.
Passing run_in_background: true returns a job id immediately and no timeout applies; the command keeps running while the agent works on something else. The agent reads its output with job_output (non-blocking unless wait: true), lists jobs with job_list, and stops it with job_kill; a finished job notifies the owning agent in-session. Background support needs the generic job runtime (dsh-jobs-local) and its control tools (dsh-tool-jobs) mounted.
When the mounted executor confines commands (for example dsh-bash-sandbox), a blocked file operation is reported as [sandbox: file access denied under <mode> mode] — a policy denial, not a command failure. The model may then retry the exact same command once in the same turn with sandbox_permissions (the narrowest wider mode that suffices) and a one-sentence justification; the approval prompt raised by that retry is how the user consents. Escalation is never speculative: a request with no real prior denial, or one that is not strictly wider than the current mode, fails closed without running anything, and a rejected escalation is final for that command.
A composition with no executor provider never activates the tool. Background calls without the job runtime fail with background jobs unavailable: load @deepseek-ai/dsh-jobs and @deepseek-ai/dsh-tool-jobs, and sandbox_permissions without a sandboxing executor fails with sandbox_permissions is not available in this composition (no sandboxing executor to escalate). enableRunInBackground: false removes the parameter and rejects a forced background call at execution time.
Implementation internals — click to expand
This section explains the design decisions behind the tool and points at the code that realizes them; the observable behavior is fully covered in Use this package.
- Model-facing consumer of the shell seam. The tool is the Consumer role of the bash capability: it registers the
bashschema, renders results, and resolves per-call policy, while the executor seam owns process mechanics. - Request from named args only. The tool never exposes
stdin,env, orstdoutMaxBytes; it builds each request from command/workdir/timeout/signal fields plus the registry-collecteddshEnv, so model-supplied keys cannot replace managed values (bash stdin/env Agent Note). - Non-zero exits are reported, not errored. Only infrastructure failures (spawn errors, aborts) surface as tool errors; the model interprets exit codes and markers.
- Background work belongs to the job runtime. A background call registers a process handle with
ctx.jobs; ids, ownership, completion notices, and disposal are the runtime's, and this tool only maps bash exit and sandbox facts into job output.
| File | Role |
|---|---|
src/index.ts |
Plugin entry: tool registration, prompt section, arg validation, escalation, request assembly |
src/background.ts |
Map a settled background process onto generic job outcome vocabulary |
src/render.ts |
Model-facing result text: streams, markers, truncation notices |
src/invariant.ts |
Invariant companion (no runtime invariant; execution relations are owned by the capability seam) |
The tool resolves the workdir before ctx.shell.resolve() runs: an explicit relative workdir is resolved against the session cwd, and a sandbox policy's canonical workspace root wins so confinement and launch use the same identity. Sandbox policy resolves per call through ctx.sandboxPolicy; an escalation request goes through ctx.approval before anything executes, and the tool fails at load if the executor confines but no policy service is mounted.
The result text is stdout, then a marked [stderr] section, then conditional markers: truncation notice, sandbox denial (plus the same-turn escalation hint when the composition advertises escalation), timeout, signal, and exit code — each on its own line. The exit marker doubles as the UI card's exit-status pill: the shared parseExitStatus from dsh-shell consumes it from the output body, so replay shows the pill without duplicating the marker.
Read these pages when the package-level contract is not enough. They move from the shell family to the executor seam, the job runtime, and the decision notes behind the behavior.
- shell package map — the bash capability family and its roles.
- Bash executor subsystem — request/spec vocabulary, results, and background processes.
- shell-env — the managed
DSH_*environment every call receives. - tool-jobs —
job_output,job_list, andjob_killcontrols for background runs. - bash stdin/env Agent Note — why the tool exposes no stdin or env.
- sandbox Agent Note — escalation and mode-switching rationale.
- Generated tool catalog — the exact
bashargument schema. - Generated configuration catalog — every accepted config field and its source declaration.
Every request in this plugin's registration scope contains the bash guidance below at first-party order 1000. The policy owner contributes current sandbox state through its cache-safe runtime context rather than changing this section. Scoped tool restrictions can hide the schema without removing this independently registered section.
Check the [exit code: N] marker on every bash result; investigate failures before moving on.Small fixed input cost per request while the plugin is active, unchanged by sandbox mode or mode switches.
Prefix-stable while the registration scope and prompt text are unchanged. Plugin activation or disposal may invalidate reuse from this prompt section; sandbox mode switches do not.
The model sees the generated bash schema. run_in_background appears only when this producer enables it; sandbox_permissions and justification appear only when the mounted executor advertises sandboxing. Agent-scoped tool restrictions can remove the definition for that agent.
Fixed schema cost on every request where the tools are visible; sandbox support adds the escalation fields and its conditional description paragraph.
Prefix-stable while visibility, background support, and executor sandbox capabilities are unchanged. A restriction, config change, or executor change may invalidate reuse from the first changed tool definition.
The renderer emits the data-dependent stdout tail, then optional [stderr] and the stderr tail. With no output it emits exactly (no output). Conditional lines are exactly [output truncated; full output: <path-or-(unavailable)>], [sandbox: file access denied under <mode> mode], [timed out after <timeoutMs>ms], [killed by signal: <signal>], and [exit code: <exitCode>]; the sandbox escalation and runner-failure lines are quoted in dsh-bash-sandbox.
Zero result tokens before a call. Output is bounded per stream, while each emitted line remains in history until compaction.
Append-only; newly visible content follows the reusable request prefix and does not invalidate existing KV-cache entries.
Start returns exactly started background job <jobId>. This producer supplies incremental process output, optional [some output was dropped from memory; full output: <paths-or-(unavailable)>], sandbox facts, and terminal detail such as exit code: <exitCode> or signal: <signal> to the generic job runtime. dsh-tool-jobs owns the visible status line, completion notice, listing, and cancellation response.
The start acknowledgement is small and retained; collected output is data-dependent and bounded by the executor's stream buffers. Consuming reads do not repeat prior output.
Append-only; newly visible content follows the reusable request prefix and does not invalidate existing KV-cache entries.
Validation and policy failures are normalized as Error: <message>. This package's stable messages are invalid command: expected a non-empty string, invalid description: expected a non-empty string, invalid timeoutMs: expected a positive number, got <value>, the escalation pairing failures, run_in_background is disabled for this deployment (enableRunInBackground: false), background jobs unavailable: load @deepseek-ai/dsh-jobs and @deepseek-ai/dsh-tool-jobs, sandbox_permissions is not available in this composition (no sandboxing executor to escalate), the approval availability/rejection/cancellation variants, and tool call aborted.
Only the failing call adds these retained tokens; a rejected escalation does not add command output because the command does not run.
Append-only; newly visible content follows the reusable request prefix and does not invalidate existing KV-cache entries.
These limits define when the tool is a poor fit or needs special care. They are current package constraints, not a task backlog.
- Replay exit pills parse from result text — output whose final line happens to be exactly
[exit code: N]/[killed by signal: …]shows a wrong pill on session replay and loses that line from the card body, because the parse treats it as the marker it consumes; a display-only known residual. - The
bashtool opts out oftimeout-policybudgets — it keeps the executor-ownedBASH_TIMEOUTpath, per the tool-call timeout-policy Agent Note. - Background processes have no executor timeout — callers must use
job_kill, or rely on owner/service disposal, when work no longer matters.
Working context for maintainers — click to expand
None.