-`run_shell` accepts `background: true` (shell-guard plugin, after the permission chain — a denied command spawns nothing). The starting call resolves `cwd` as usual and applies a per-call `timeout` only when passed (no 120s default on background), skips the pwd probe, spawns a detached process group via the registry in `src/shell/background-shell.ts`, and returns `{shell_id, status: "running"}` immediately; the retained shell cwd is never mutated by a background run. Limits: 8 running, 8 completed entries (ring; evicted ids collect as not-found — truncated output is spilled to a `tool-output:///bg-shell-<id>` blob named in the completion message). Completion is process-exit, not stdio-close: a grandchild holding the pipe does not delay the completion message. On process exit the host delivers `buildShellBackgroundMessage(exit)` (exit status, timed-out marker, ~2KB output preview, spill URI) through the same continuation channel as compaction — wired in all three loop hosts (TUI, exec, sub-agent). `run_shell` with `stop: <shell_id>` kills the process group; it is ungated by design (cancel only kills the session's own child), and a finished run needs no poll because its result is delivered on its own. Timeout keeps its meaning: expiry kills the group and reports exit code 124 with `timed_out: true`. The tool watchdog exempts background starts because the process outlives the turn. A worker whose capability filter drops `run_shell` has no background registry wired — background starts fail closed. Parent abort, deadline, and `close_agent` dispose live background groups; `interrupt_agent` leaves them so the session (and its children) stay resumable. Toolset dispose calls `disposeAll("session closed")` before the posix teardown, so `/clear`, interrupt, and reload kill every live background process group.
0 commit comments