Pixir.Subagents (pixir v0.1.17)

Copy Markdown View Source

Public facade for BEAM-native Subagent orchestration (ADR 0011).

Subagents move through a small lifecycle state machine. The usual path is queued -> running -> completed, but failures, timeouts, cancellation, and detached restored children must remain explicit so parents and diagnostics can tell "finished cleanly" apart from "needs operator attention".

A warm child keeps its copied replay Events as historical evidence. Its live authorization posture is the session-scoped posture written immediately after the most recent valid runtime lineage boundary; cold resume evaluates posture and mutation evidence only in that boundary's current segment. Logs without a valid boundary retain the legacy root/child/Fork fold rules.

max_depth is an absolute delegation-depth cap from the root Session. A child spawned by the root runs at depth 1; that child may only spawn another child when the configured cap is at least 2.

Summary

Functions

Application child spec.

Cancel a running Subagent, or close a queued/terminal Subagent as cleanup.

Default runtime limits.

Return a runtime diagnostics projection for one parent Session.

List Subagents for a parent Session.

Reconstruct Subagent relationships and terminal state from parent History.

Apply the common restrict-never-widen rules to a restored posture.

Rehydrate a Session's durable permission posture for a cold resume.

Send follow-up input to an idle Subagent.

All known Subagent lifecycle statuses.

Summarize agent maps for model-facing output.

Build model-facing text for a structured wait outcome.

Whether a status is terminal.

Statuses that no longer have a live child runtime.

Whether the public lifecycle contract allows a status transition.

Validate and normalize a Subagent spawn without creating runtime state.

Wait for selected Subagents to reach a terminal status.

Wait for selected Subagents and return a structured outcome.

Functions

child_spec(opts)

Application child spec.

close(parent_session_id, id, opts \\ [])

Cancel a running Subagent, or close a queued/terminal Subagent as cleanup.

Cancelling a running Subagent fences its active logical Turn without stopping the reusable child Session. It then appends a cancelled_by_parent :subagent_event with scope: "turn" to the child's own Log. The event follows the synchronous interrupt result, but a C6-classified late declaration may append after it, so it is not promised physically list-last. Callers that cancel on behalf of a Workflow may name the canceller with :workflow_id, :workflow_step_id, and :workflow_close_outcome; those fields are simply absent from the child event for a plain parent close. Running-close results include bounded cancellation_evidence; the ordinary path is complete, while missing durable Session/child evidence is partial without raw errors or exits. The existing lifecycle return fields remain.

default_limits()

Default runtime limits.

diagnostics(parent_session_id, opts \\ [])

Return a runtime diagnostics projection for one parent Session.

Diagnostics idempotently hydrates restorable parent state from durable evidence before projecting the snapshot. Hydration may reattach live children: it subscribes to child event buses through Pixir.Events and rearms child timeout timers. It may also fold the parent Log after a Manager restart, but it does not write canonical events. The returned data remains volatile process-health evidence; use the Session Log and Session tree for canonical lifecycle facts.

list(parent_session_id, opts \\ [])

List Subagents for a parent Session.

reconstruct(history)

Reconstruct Subagent relationships and terminal state from parent History.

restrict_resume_posture(posture, requested_mode, requested_policy)

@spec restrict_resume_posture(map() | nil, Pixir.Permissions.mode(), map() | nil) ::
  {:ok, map() | nil} | {:error, map()}

Apply the common restrict-never-widen rules to a restored posture.

resume_posture(session_id, opts \\ [])

@spec resume_posture(
  String.t(),
  keyword()
) :: {:ok, map()} | {:error, map()}

Rehydrate a Session's durable permission posture for a cold resume.

Root Sessions record their posture at creation (Pixir.Conversation.start/1, lineage root, trusted only in root position: first non-session_fork event, runtime-authored source) and restore the recorded capability ceiling — including unbounded auto when the marker declared it. Spawned children record theirs with lineage child and keep the stricter contract: write-capable history restores only with a bounded policy.

Warm-start Logs add a runtime-authored lineage_boundary_v1 marker. Only the permission posture and mutation evidence physically appended after the last valid marker govern that live warm segment; copied posture remains replay evidence. A valid boundary without current posture fails closed. Without a valid boundary, the existing full-history root/child/Fork behavior is preserved. Legacy Logs without posture evidence remain resumable only when they contain no write-capable evidence, and then restore an explicit read-only ceiling; otherwise they fail closed with reason missing, which is the one classification the operator may override via the CLI's explicit legacy-root attestation (never as unbounded auto).

send_input(parent_session_id, subagent_id, prompt, opts \\ [])

Send follow-up input to an idle Subagent.

spawn_agent(parent_session_id, args, opts \\ [])

Spawn or queue a Subagent.

statuses()

All known Subagent lifecycle statuses.

summarize(agents)

Summarize agent maps for model-facing output.

summarize_wait_outcome(arg1)

Build model-facing text for a structured wait outcome.

terminal?(status)

Whether a status is terminal.

terminal_statuses()

Statuses that no longer have a live child runtime.

transition_allowed?(from, to)

Whether the public lifecycle contract allows a status transition.

closed is retained as Pixir's local close/cleanup state. Completed, failed, timed-out, and cancelled Subagents may be restarted by send_input/4; detached children cannot be resumed because there is no live process handle.

validate_spawn(parent_session_id, args, opts \\ [])

Validate and normalize a Subagent spawn without creating runtime state.

wait(parent_session_id, ids, timeout_ms \\ 30000, opts \\ [])

Wait for selected Subagents to reach a terminal status.

wait_outcome(parent_session_id, ids, timeout_ms \\ 30000, opts \\ [])

Wait for selected Subagents and return a structured outcome.

Unlike wait/4, this keeps partial fanout visible: timed-out, failed, detached, cancelled, and still-incomplete children are bucketed instead of turning the parent tool call into an opaque failure. The returned "partial" boolean is true for any non-completed aggregate status; consumers should use "status" when they need to distinguish "partial" from "incomplete".