Delegate CLI Live Example

Copy Markdown View Source

This example shows how another coding agent can call Pixir as a local Subagents-as-a-Service runtime through pixir delegate.

The files here are deterministic examples, but running them without --dry-run uses the configured Pixir Provider. Use pixir for an installed escript and ./pixir from a source checkout.

Contract

Streams

  • stdout is one final JSON envelope.
  • stderr is empty unless progress is explicitly requested.
  • --progress=stderr-jsonl is currently for delegate attach; attached pixir delegate --spec ... --json rejects --progress until it can emit real progress frames.
  • The Log under .pixir/sessions/ is the durable source of truth; diagnose and tree are projections derived from that Log.

Exit Codes Are Path-Scoped

Do not generalize one exit code across all pixir subcommands. Branch on the command path first, then parse stdout JSON.

PathExitMeaning for that pathCaller action
pixir delegate --spec ... --dry-run --json0Spec accepted; no Provider, Subagent, Workflow, host command, or artifact execution started.Safe to run without --dry-run if the plan matches intent.
pixir delegate --spec ... --dry-run --json2Invalid args/spec, unreadable stdin/spec, unsupported mode, or unknown role.Inspect kind, details, field / json_pointer, and next_actions; fix the spec before runtime.
pixir delegate --spec ... --dry-run --json3Bounded write policy rejected the planned write scope.Narrow the child write_set or explicitly expand write_policy.allow_writes.
attached pixir delegate --spec ... --json0Delegate work reached a clean success state; expect ok: true and status: "completed".Consume children, diagnostics commands, artifacts, and summaries as evidence pointers.
attached pixir delegate --spec ... --json3Permission, workspace, read-confinement, disabled-shell, or bounded-write policy denial.Distinguish kind: "outside_workspace", kind: "write_policy_denied", kind: "permission_denied", and kind: "bash_disabled" before retrying.
attached pixir delegate --spec ... --json4Provider/auth/network class failure.Inspect kind, provider diagnostics, and retry or re-auth guidance.
attached pixir delegate --spec ... --json5Runner-level failure before delegated work reached a terminal state: backpressure, unavailable manager, or daemon requirement.Inspect the error kind and details; retry with adjusted budgets or daemon setup.
attached pixir delegate --spec ... --json6Domain work reached an incomplete terminal state such as partial, timed_out, failed, or cancelled — attached child timeouts normalize to status: "timed_out" and exit here, not 5.Parse the envelope; inspect children[*].status, workflow buckets, write_destination, and diagnostics before deciding whether the result is usable. Non-completed subagent-strategy children carry ready-made resume_command / diagnose_command (recover per child, never re-run the whole spec); workflow-strategy steps carry buckets and evidence instead, so recover those via diagnostics and the step logs.
one-shot pixir "prompt" / pixir resume ...6The turn completed without a final assistant message.Use the emitted resume/diagnose guidance; do not infer Delegate partial semantics.
delegate status / delegate attach / delegate cancelpath-specificLiveness commands reuse the same JSON envelope vocabulary, but status/snapshot acceptance is not proof that delegate work succeeded.Inspect ok, status, owner/runtime fields, and child evidence.

Reading Delegate Results

Treat ok, status, and the shell exit code as the first branch only. A top-level status: "partial" means "not cleanly completed"; it is an incomplete umbrella, not proof that useful partial work exists. Before acting, inspect:

  • children[*].status, children[*].checkpoint_status, and child child_session_id;
  • for subagents spawned from delegate tasks[], children[*].index is the zero-based task-array position. Treat the children array order as unspecified; join results to requested tasks by children[*].index, not by display position;
  • workflow buckets such as held_steps, failed_steps, partial_steps, needs_orchestrator_steps, and safe_next_actions;
  • bounded-write write_destination;
  • diagnostics.diagnose_command and diagnostics.tree_command.

For direct child Subagents from a root delegate Session, use max_depth: 1. max_depth: 0 is a useful rejection fixture, not a working fanout setting.

Bounded Write Results

Bounded-write envelopes report where Pixir believes writes landed. A partial bounded-write result can still have applied a subset of writes before a later denial or failure. observed_applied_writes is an at-least observation from child Logs when available; if it is absent, that is not proof of zero writes unless write_destination.contract_status explicitly says no workspace write was applied.

Illustrative non-golden partial bounded-write envelope:

{
  "ok": false,
  "status": "partial",
  "exit_code": 6,
  "strategy": "workflow",
  "write_destination": {
    "writes_applied_to": "indeterminate",
    "contract_status": "unverified_partial_writes",
    "workspace_modes": ["shared"],
    "observed_applied_writes": ["notes/e1.md"],
    "observed_writes_source": "child_log",
    "observed_writes_semantics": "at_least"
  },
  "children": [
    {
      "step_id": "write",
      "status": "failed",
      "checkpoint_status": "failed",
      "writes_applied_to": "indeterminate",
      "observed_applied_writes": ["notes/e1.md"],
      "next_actions": ["retry_failed_step"]
    }
  ],
  "safe_next_actions": ["retry_failed_steps"]
}

Denial Kinds

Do not collapse read/scope denials and write allowlist denials:

  • kind: "outside_workspace" with matched_rule: "outside_workspace" means a read/scope escape, such as a shell-shaped path token resolving outside the workspace. This is a tripwire, not a full POSIX sandbox.
  • kind: "write_policy_denied" means the requested write exceeded the bounded write policy.
  • kind: "permission_denied" means the permission mode (ADR 0006) rejected the request, independent of any bounded write policy.
  • kind: "bash_disabled" means the shell tool itself is disabled by the bounded write policy (v1 always disables it); the command's read/write nature is irrelevant. This denial does not end the child's Turn: the model is expected to adapt via next_actions (use_native_read_tools, use_edit_or_write_within_allowed_globs). Do not respond by widening write globs; they are unrelated.

Illustrative non-golden outside_workspace denial:

{
  "ok": false,
  "status": "rejected",
  "exit_code": 3,
  "kind": "outside_workspace",
  "message": "bash command references a path outside the workspace",
  "details": {
    "tool": "bash",
    "token": "$HOME/notes.txt",
    "requested_command": "cat $HOME/notes.txt",
    "matched_rule": "outside_workspace",
    "next_actions": [
      "use_workspace_relative_paths",
      "use_pixir_read_tool_for_file_access",
      "run_pixir_from_the_intended_workspace_root"
    ]
  }
}

Canonical permission_decision events may omit next_actions even when the underlying tool error details contain them. Prefer the terminal tool error/details and top-level next_actions / safe_next_actions when present.

host_boundary.external_process_spawns is scoped to the delegate entrypoint, not to host tools that child Sessions may call. Inspect child diagnostics for child host-command evidence.

Preflight

mix escript.build
./pixir doctor --json

For installed Pixir:

pixir doctor --json

Attached Call

Use this when the caller wants Pixir to return only after the child Subagents finish or reach a terminal partial state.

./pixir delegate --spec docs/examples/delegate-cli-live/attached-subagents.json --dry-run --json
./pixir delegate --spec docs/examples/delegate-cli-live/attached-subagents.json --json

For shell callers, treat a non-zero exit as actionable even though stdout remains parseable JSON:

if ! result="$(./pixir delegate --spec docs/examples/delegate-cli-live/attached-subagents.json --json)"; then
  printf '%s\n' "$result" | jq '{ok,status,kind,summary,next_actions}'
  exit 1
fi
printf '%s\n' "$result" | jq '{ok,status,delegate_id,parent_session_id,children}'

Optional transport preference: subagents.transport (or top-level transport) accepts auto, websocket, or http_sse; invalid values fail the dry-run with a structured error, and the effective value is surfaced as limits.transport in plan and envelope.

Optional provider knobs, mirroring ACP session/prompt _meta: subagents.model (a model id string) and subagents.reasoning_effort (low, medium, high, or xhigh) apply to every child of the delegation; a spec knob wins over config defaults. Effective use is evidenced by each child's provider_usage events, not echoed in the delegate envelope.

Delegate specs are validated fail-closed: an unknown top-level or subagents key is rejected as invalid_spec with field, json_pointer, path, and next_actions (the dry-run reports it exactly like the real run). Workflow step internals remain governed by the workflow strategy validator.

Useful fields for callers:

  • ok
  • status
  • delegate_id
  • parent_session_id
  • children[].status
  • children[].index - for subagents spawned from tasks[], the durable zero-based source task position; children array order is unspecified, so callers must not rely on it for task/result matching
  • children[].child_session_id
  • children[].retry_attempts / children[].retry_max_attempts / children[].current_attempt_index / children[].retry_history - present only when the runtime auto-retried that child (bounded retry for websocket-family and provider-declared-retryable server errors on read-only children); retry_history preserves each failed attempt's session id and error kind
  • diagnostics.diagnose_command
  • diagnostics.tree_command
  • host_boundary.external_process_spawns_scope

Async Service Call

Use this when the caller wants separate start, status, attach, and cancel invocations. Start the manual workspace-local daemon in one terminal:

./pixir delegate daemon --foreground --json

delegate start requires that resident daemon. Without it, Pixir rejects start instead of returning a running handle whose owner would die with the short-lived CLI process.

From another managed shell/process:

delegate_json="$(./pixir delegate start --spec docs/examples/delegate-cli-live/async-subagents.json --json)"
delegate_id="$(printf '%s\n' "$delegate_json" | jq -r '.delegate_id')"

./pixir delegate status "$delegate_id" --json
./pixir delegate attach "$delegate_id" --json --progress=stderr-jsonl --wait-horizon-ms 5000
./pixir delegate cancel "$delegate_id" --json
./pixir delegate attach "$delegate_id" --json
./pixir delegate daemon --stop --json

After the daemon stops, attach falls back to a durable snapshot from the local Session Log. That fallback is expected and should be treated as inspectable state, not as live owner capability. Async status, attach, and cancel project the same children[].index values from durable parent Log subagent_event data, even when snapshot children are sorted by subagent id.

Claude Code Pattern

Claude Code can call Pixir the same way Codex does: use the shell, write a bounded spec, run --dry-run --json, then choose attached or async execution.

Suggested Agent Instruction Block

Use this block for Claude Code, Codex, or another shell-driven agent:

Use Pixir delegate as a local audited executor. Do not edit files.
First run ./pixir doctor --json. Create a temporary delegate spec with
strategy "subagents", mode "read_only", max_depth 1, and bounded tasks. Run
./pixir delegate --spec <spec> --dry-run --json and inspect the JSON. If accepted,
run attached with ./pixir delegate --spec <spec> --json and treat a non-zero exit
as incomplete or failed work while still parsing stdout JSON for next_actions. Use
async service mode only when a managed resident daemon is already running or can be
started and later stopped explicitly: ./pixir delegate daemon --foreground --json.
Then call start/status/attach/cancel from another managed shell. Parse stdout as
the final JSON envelope. Parse stderr as JSONL only when --progress=stderr-jsonl is
used. Use diagnose/tree commands from the result for evidence. Do not treat prose
summaries as proof.

Minimal Claude shell flow:

cat > /tmp/pixir-delegate.json <<'JSON'
{
  "contract_version": 1,
  "strategy": "subagents",
  "mode": "read_only",
  "workspace_mode": "shared",
  "subagents": {
    "role": "explorer",
    "max_threads": 2,
    "max_depth": 1
  },
  "tasks": [
    "Read README.md only. Return 2 bullets about Pixir. Do not edit files.",
    "Read CONTEXT.md only. Return 2 bullets about the Log. Do not edit files."
  ],
  "limits": {
    "timeout_ms": 120000
  }
}
JSON

./pixir delegate --spec /tmp/pixir-delegate.json --dry-run --json
./pixir delegate --spec /tmp/pixir-delegate.json --json | jq '{ok,status,delegate_id,parent_session_id,children}'

For Claude, the practical rule is simple: let Pixir own fanout and evidence; Claude owns the outer decision loop and reads Pixir's JSON, diagnostics, and Session ids.