English | 中文
The subprocess seam (ctx.subprocess) is the process half of one execution world. The abstract SubprocessRuntime exposes executable lookup, ordinary managed spawn, and one terminal-process primitive; its vocabulary covers raw/collected stdio, process and terminal handles, exit facts, managed-range/session cleanup, and the managed DSH_* environment namespace. The local implementation lives in dsh-subprocess-local.
spawn(spec) returns a live handle synchronously. The provider owns the meaning and publication timing of pid, which remains -1 while unavailable or after startup fails. done resolves with the spawned command's exit facts (SubprocessOutcome carries no output and no cause classification) and rejects for spawn or provider failures.resolveExecutable(command, env?, signal?) verifies absolute commands or resolves bare names against that world's scrubbed PATH plus explicit overrides.dsh-shell request/spec split is the owning template). argv is never shell-interpreted; a consumer that wants a shell passes ['bash', '-c', command] itself.'pipe' hands the caller the raw stream for its own protocol framing (LSP JSON-RPC, ACP ndjson), 'inherit' passes the parent descriptor through for diagnostics, and collect mode ({ maxBytes, spill? }) buffers a bounded tail with an optional full-stream spill file. Collect readers take whole-stream byte offsets and never consume, so independent readers cannot steal one another's deltas; a read whose offset slid out of the in-memory tail is lossy and points at the spill file when one exists. Collected output stays readable after settlement.terminate() — the only termination verb — starts the provider's documented procedure (idempotent, driven by the spec's abort signal too, and a no-op once the range is empty), while waitForExit(signal?) observes that same range so consumer teardown can await real quiescence. Staged providers may use graceMs between graceful and forced steps; immediate providers do not delay. Each provider documents how it defines, signals, and observes the range, including weaker fallbacks; the local provider owns its systemd, Job, process-group, and taskkill details. The wait rejects when a selected owner can no longer observe its range; the manager reacts but never classifies why (callers own deadlines, teardown ladders, and cause classification).spawnTerminal(spec) is the only non-pipe primitive. Its handle owns a real PTY, UTF-8 text I/O, foreground-process-group inspection/signalling, and one awaited terminate() operation that reaches quiescence for every session member the provider can still observe and settles in-flight handle calls; providers document substrate-specific observability limits. The spec signal cancels allocation only; the published handle owns its lifetime. The output stream ends after queued output when the top-level process exits, and a live transport failure rejects done. These operations remain one substrate primitive because ordinary pipes cannot allocate a controlling terminal or clean terminal-session members; readiness, scrollback, and owner policy remain in the PTY consumer.scrubbedParentEnv() / SENSITIVE_ENV_PATTERN are the one shared scrub definition: ambient credential-shaped and DSH_* names are dropped, and explicit env merges after the scrub. The local ordinary and terminal spawns both apply it; SDK-managed transports that own their spawn may import it directly.See the subprocess subsystem page and the seam Agent Note.
Indirectly, through Consumers (today the bash executor family behind dsh-tool-bash), which own all model-facing rendering of process output and lifecycle.
No direct invalidation; the named consumers own any request-prefix changes.
scrubbedParentEnv so environment policy stays single-sourced.