English | 中文
Local Service Provider for the @deepseek-ai/dsh-subprocess seam. LocalSubprocessRuntime resolves local executables, gives ordinary Linux and Windows commands an OS-owned managed range when the host supports it, and implements terminal processes through node-pty plus platform process inspection. It has no config: every disposition, limit, terminal dimension, grace, and directory arrives from the calling capability seams (dsh-bash-local, dsh-lsp-stdio, and dsh-terminal-bash).
ActiveProcesses reaches zero; the parent never opens either native object. Linux scopes and POSIX process-group fallbacks receive TERM and then KILL after graceMs; Windows Job and taskkill owners force-terminate on the first request. waitForExit() succeeds only after the selected owner proves the range empty and rejects when that proof is unavailable. .done remains the direct command result, and only collected pipes retain the existing bounded drain grace.taskkill /T path. The provider warns once before the first affected command. It never retries through fallback after a native runner may have started the user command.'pipe' hands the raw stream to the caller untouched (protocol framing stays consumer-owned); 'inherit' passes the parent descriptor through; collect mode keeps the in-memory TAIL beyond its cap (errors and results cluster at the end — pi/OpenCode rationale) while the FULL stream is appended to a private temp file when a spill cap is configured — omitting spill keeps only the tail, the diagnostic shape. A stream larger than the spill cap discards its now-incomplete spill and returns only the marked truncated tail; spill fds are sealed at settlement, and a failed final close withholds the path rather than advertising an incomplete file. Spill files are 0600 with random names under a lazily-created 0700 per-process directory.process.env minus credential-shaped vars (*KEY*/*PASSWORD*/*SECRET*/*TOKEN*) and all ambient DSH_* names; the spec's explicit env merges after that scrub with no namespace validation, so a deliberately supplied credential or current DSH_* fact wins while stale nested-harness identity cannot leak in ambiently. Supplied stdin is written and closed; otherwise fd 0 is /dev/null. See the stdin/env Agent Note and managed environment Agent Note.resolveExecutable checks absolute files or searches the scrubbed effective PATH with platform-aware executable extensions; relative paths containing separators are rejected at the seam, and relative PATH entries resolve from the host process cwd.spawnTerminal allocates node-pty, bridges UTF-8 terminal text, inspects and signals the current foreground process group, and exposes one awaited termination operation that sweeps descendants before and after terminating the top-level shell. Each foreground inspection retains exact identities from the rooted tree; Linux also enumerates the POSIX session after its leader exits. A previously observed macOS descendant and any same-session Linux member therefore remain fenced after reparenting, while pid/start identity prevents cleanup from following PID reuse. On Windows the koffi-backed inspector enumerates the process table through Toolhelp32, combines GetProcessTimes start identities with zero-time process-handle waits for liveness, reports the shell pid as the pseudo foreground group (Windows has no POSIX groups), and teardown verifies the shell's termination because externally taskkilled shells may never fire node-pty's exit notification. The higher PTY backend owns prompt readiness, buffers, and model-facing operations.exit listener synchronously signals every ordinary managed range and observable terminal session still in the live sets. Linux issues the scope KILL request; the Windows runner treats parent IPC disconnect as Job termination; fallback and terminal paths retain their PGID, taskkill, and captured-identity behavior. The listener creates no promise or timer, preserves the host exit code and diagnostic, contains each target failure, and does not claim quiescence. Normal disposal keeps the awaited managed-range path above. See the host-exit cleanup decision.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.
systemd-run --expand-environment=no; older systemd versions use the warned PGID fallback. macOS always uses that fallback because no supported public persistent owner exists.\x03 Ctrl-C input write that conhost turns into a console-wide CTRL_C event; SIGTSTP and SIGHUP are rejected as unavailable; a taskkill without /F does not terminate console processes, so the teardown TERM tier is a grace wait before the /F escalation. Windows readiness has no exact stdin-wait tier: the prompt-marker fast path compares the shell pid as the pseudo foreground group, and silence/timing tiers cover the rest.node-pty root; on Linux, a child that calls setsid leaves both the tree and owned terminal session. The local provider does not add a continuous process-table monitor.process.exit(), default uncaught exceptions, and default unhandled rejections emit Node's synchronous exit event. The default OS disposition for an unhandled SIGTERM, SIGINT, or SIGHUP bypasses that event; an application covers those signals only by installing a handler that performs normal disposal or calls process.exit(). SIGKILL, fatal OOM, process.abort(), native crashes, power loss, and any failure that cannot run JavaScript require an external supervisor, container init, or equivalent OS owner.*KEY*/*PASSWORD*/*SECRET*/*TOKEN* only; differently-named secrets (e.g. *PASSPHRASE*) pass through, and a whitelist for over-scrubbed vars is noted future work.Common process handling lives in src/spawn.ts; Linux scopes, Windows Jobs, and the private runner live in their platform modules; src/index.ts owns selection and service wiring.