Expand description
Unified agent runner — one-shot Claude Code spawn for the drone Agent block.
Spawns claude --print --output-format=stream-json as a non-
interactive subprocess, drains its stdout through
ClaudeTranslator, forwards each AgentEvent on the caller’s
tx, and resolves the handle’s final_result with the
structured AgentRunResult once the stream emits Done.
Headless and one-shot by design — the drone Agent block’s
contract is “send task, wait for done, return result.” The
interactive agent pane has its own PTY-based controller in
blockcontroller/shell/lifecycle.rs; that path is NOT routed
through this runner’s spawn — what’s shared is the translator +
event shape, not the spawn function. (This used to cite a
SPEC_UNIFIED_AGENT_TYPES spec that no longer exists anywhere in
docs/; the pointer was dropped rather than replaced — see
scripts/check-spec-citations.sh.)
It DOES reuse this module’s commit-aware admission gate
(admit_spawn / agent_commit_reserve_gb, pub(crate)) — see
blockcontroller::shell::lifecycle::ShellController::start,
which calls them right before spawning a claude/codex/
gemini/qwen interactive pane, mirroring this file’s
run_agent gate.
Structs§
- Agent
RunHandle - Handle returned by
run_agent. The caller already holds thempsc::UnboundedReceiver<AgentEvent>they paired with thetxpassed intorun_agent; this handle adds the structured terminal value viafinal_result(drone Agent block’s downstream output).
Enums§
- Agent
Error - Error returned by the runner.
Constants§
- DEFAULT_
AGENT_ 🔒COMMIT_ RESERVE_ GB - Minimum free system commit (GB) required to admit a new agent spawn. Below
this, launching another
claude.exerisks pushing the system commit charge to its limit, where a failed allocation aborts the CEF host (Chromium OOM). - DEFAULT_
CLAUDE_ 🔒BIN - ENV_
CLAUDE_ 🔒BIN - Override the default
claudebinary name. Set to a full path (or a different binary) for testing or non-PATH installs.
Functions§
- admit_
spawn 🔒 - Pure admission decision: is there enough free system commit to spawn another
agent?
Noneavailable (non-Windows, or the read failed) ⇒ admit — there’s no cheap commit limit to enforce. Strict<so a value exactly at the reserve is admitted. CEF-free / OS-free so it is fully unit-testable. - agent_
commit_ 🔒reserve_ gb - append_
capped_ 🔒tail - Append
chunkto a rolling tail buffer, compacting to the lastcapbytes whenever it grows past2 * cap(amortized O(1)). The exact final trim happens at EOF viatrim_to_tail. Keeping the tail (not a prefix) matters because the CLI’s terminal error line is at the end of stderr. - drain_
and_ 🔒collect - Drain
stdoutline-by-line throughClaudeTranslator, forward every emitted event ontx, accumulate the terminalCost/Doneevent payloads into anAgentRunResult, wait for the child to exit, and return the result. - drain_
async_ 🔒reader - Drain an arbitrary async reader of newline-delimited stream-json
frames, forward every emitted
AgentEventontx, and return an accumulator capturing the terminalCostandDonevalues. - exit_
signal 🔒 - Extract the terminating signal (Unix only). On non-Unix there is no
signal concept, so this is always
None. - explain_
failure 🔒 - Await the captured stderr, classify the exit, log a warning, and
render the human-readable explanation that becomes the run’s
terminal error string. See
docs/specs/SPEC_AGENT_FAILURE_DIAGNOSTICS_2026_06_11.md. - run_
agent - Spawn
claude --print --output-format=stream-jsonper the givenAgentTaskandAgentRef, drain its stdout through the sharedClaudeTranslator, forward eachAgentEventontx, and resolve the returned handle’sfinal_resultwith anAgentRunResultconstructed from the terminal Cost + Done events. - run_
agent_ 🔒with_ bin - Internal entry point — same as
run_agentbut takes theclaudebinary path explicitly. Lets tests inject a known-nonexistent path to exercise the spawn-failure path without touching env vars (Rust 1.81+ flagsstd::env::set_varas unsound under concurrent test execution). The publicrun_agentis a thin shim that resolves the binary from$AGENTMUX_CLAUDE_BINor the default. - trim_
to_ 🔒tail - Drop all but the last
capbytes ofbuf.