Files
deepseek-harness/packages/bash/bash/README.md
Tianyi Cui 5717726835 subprocess: one explicit env channel on the spawn spec
Drop SubprocessSpawnSpec.dshEnv and splitEnvChannels(); childEnv() is now
scrubbed-base + explicit entries with no namespace validation. The invariant
dropped is the reserved-namespace check on explicit entries (DSH_* rejected
from env, non-DSH_* rejected from dshEnv). Explicit-entry trust already
covers it: an explicit credential-shaped entry has always merged after the
scrub as a deliberate caller opt-in, and an explicit DSH_* entry is the same
deliberate act — the staleness invariant lives entirely in scrubbedParentEnv
dropping AMBIENT credential-shaped and DSH_* names, which stays. The
validation's only observed effect was rejecting legitimate explicit entries:
both recent CI breakages (DSH_GATE_CONCURRENCY exported into every job
crashing lsp specs, DSH_PERMISSION_MODE in acp config.env crashing the
child spawn) were this check firing on values a caller meant to pass, each
fixed by routing around the bureaucracy the seam itself imposed.

The bash seam keeps its own request/spec dshEnv field: that is bash-owned
trusted-plugin vocabulary (the ctx.bashEnv collected overlay) whose merge-last
position guarantees a caller env entry cannot displace a managed fact;
bash-local now flattens ENV_OVERRIDES -> spec.env -> spec.dshEnv into the
seam's one env map. subagent-acp and lsp-local pass their single config env
map straight through. DshEnvironment/DshEnvironmentKey/DSH_ENV_PREFIX stay on
the subprocess seam as the namespace vocabulary (bash re-exports them;
scrubbedParentEnv filters on the prefix).

Tests: the two channel-rejection specs and the splitEnvChannels partition
spec are deleted; one spawn spec now proves an explicit DSH_* env entry
reaches the child while an ambient one is scrubbed; the acp/lsp forwarding
specs keep their MOCK_ECHO_ENV / LSP_FAKE_ECHO_ENV assertions with the split
comments rewritten to merge-after-scrub. Docs (en+zh, re-recorded) and the
owning Agent Notes updated; cordis api/services catalogs regenerated.
2026-07-27 04:14:51 +08:00

5.9 KiB

@deepseek-ai/dsh-bash

English | 中文

The bash executor seam: an abstract BashExecutor service (ctx.bash) defining WHAT a bash backend does — run foreground commands and start background processes — without saying HOW. Task ids, ownership, collection, cancellation, and notices belong to the generic ctx.tasks runtime.

This package is the interface quarter 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-bash-sandbox an implementation: dsh-bash-local's mechanics with every spawn confined via ctx.sandbox, denials reported as result facts
@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. dsh-bash-sandbox is exactly that swap in action — a sandboxing executor behind the same interface; the consumer detects its sandboxMode capability and adds escalation fields without importing the implementation. A containerized or remote executor slots in the same way.

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 task-free BashProcess handle immediately; no timeout applies. The caller may adapt it into ctx.tasks.
sandboxMode The capability fact for the tool layer: the default mode a SANDBOXING executor confines under (undefined in the base class — "this executor does not sandbox"). dsh-tool-bash reads it at registration to advertise the escalation fields only when the composition honors them.
BashProcess.readOutput() Incremental output read — consecutive reads never re-deliver. Reads that lost data to buffer bounds flag lossy and point at full-stream spill files.
BashProcess.kill() Kill the process group. Returns false when it already finished.

Implementations subclass BashExecutor and implement the abstract methods. Disposal must kill every running process and await its exit — see the HMR-safety tests.

Vocabulary

BashExecRequest (command, workdir?, timeoutMs?, stdoutMaxBytes?, signal?, stdin?, env?, dshEnv?, sandboxPolicy?) resolves to BashExecSpec (command, workdir, timeoutMs, stdoutMaxBytes, signal?, stdin?, env?, dshEnv?, sandboxPolicy) before execution. stdoutMaxBytes is a trusted foreground-run capture budget for consumers that must parse complete bounded stdout; the model-facing bash tool does not expose it. sandboxPolicy is optional on the request and required-but-nullable on the resolved spec: it carries the complete per-call mode and workspace root. The sandbox tool path resolves it from the calling session through ctx.sandboxPolicy; a direct sandbox-executor caller falls back to deployment policy, while a non-sandboxing executor carries the field and confines nothing.

The per-session sandbox-mode override vocabulary (the 'sandbox/mode' event, the effectiveSandboxMode(events) fold, and the setSandboxMode(session, mode) write path) is NOT here — it is policy state shared by every enforcing family, owned by @deepseek-ai/dsh-sandbox-policy. run() returns BashRunResult; start() returns BashProcess, whose incremental read and kill methods are adapted by dsh-tool-bash into a generic task registration. A sandboxing executor stamps BashSandboxInfo on foreground results and settled process handles. See src/types.ts and core-data-structures/bash.md.

stdin and ordinary env are set by in-process plugins (the hooks bridges, native plugins) to feed a hook command its JSON payload and CLAUDE_PROJECT_DIR/CLAUDE_PLUGIN_ROOT values. dshEnv is a separate trusted overlay restricted by type to managed keys; the exported DSH_ENV_PREFIX is the single source for that namespace, its DshEnvironmentKey template type, executor scrubbing, registry validation, derived built-in names, and model guidance. Model bash uses the current snapshot collected by ctx.bashEnv. Implementations remove inherited managed keys, then merge dshEnv after ordinary env, so an omitted current fact cannot fall back to stale ambient state and an env entry cannot displace a managed value. The model-facing tool exposes none of these as parameters. All three remain optional on the resolved spec; absent means no input/overlay. See the bash-stdin-env Agent Note and the session environment Agent Note.

Model Experience

Indirectly, through dsh-tool-bash, which turns executor output and sandbox facts into guidance and retained tool-result tokens.

KV Cache effect

No direct invalidation; the named consumer owns any request-prefix changes.

Known Limitations and Deferred Work

  • No interactive-input vocabularystdin is written once at spawn and closed; the seam has no channel to feed a running task and no PTY session concept.
  • Foreground timeouts are always executor-owned — a caller-owned-deadline mode on the seam is explicitly deferred by the tool-call timeout-policy Agent Note.