Skip to main content

Agent Run Supervisor

English  ·  简体中文

CI codecov PyPI Python License: MIT

A small, local-first Python library & dev CLI that supervises
ACP/acpx external AGENT runs and turns runner behavior into redacted, auditable evidence.


Released vs. target. The commands and APIs below describe the v0.1.7 acpx compatibility surface, unchanged in 0.2.0. Version 0.2.0 adds the Stage 0/1 Native ACP core — a library-level ars-core vertical built under the vNext authority chain in GOAL.md, the PRD, and design: unprivileged local arsd UDS ingress → ars-core/Native ACP → registered external AGENT. The arsd ingress is Stage 2 and not yet implemented: 0.2.0 ships no arsd, no Native service, and no Native CLI production entry, and is not production-enabled. Historical requirements/plans stay in cold archives.

What it does

Every project that drives an external AGENT through ACP/acpx re-implements the same plumbing: launching and babysitting the runner subprocess, compiling a permission policy, parsing a stream of observed events, classifying exit behavior, and redacting artifacts before anything touches disk. Done ad-hoc, each caller grows its own subtly-unsafe copy.

agent-run-supervisor factors that into one independent, local supervisor layer. A caller picks a role, a prompt, and a working directory; the supervisor validates the role, compiles a default-deny policy and a shell-free argv, supervises the run, parses observed output into normalized events, classifies a supervisor-owned status, and writes redacted, restrictive-permission local artifacts. The caller gets auditable evidence — not a tangle of runner-lifecycle code.

The released acpx surface covers two local acpx execution modes: one-shot exec and a local persistent-session lifecycle (create/send/status/close/abort/list); it contains no daemon. Version 0.2.0 additionally carries the Stage 0/1 Native ACP core (agent_run_supervisor.native_acp plus managed_process): supervised live-stdio AGENT processes, frozen profile/spec admission, exact-or-zero configuration, session/load continuity with cross-Run model/effort switching, isolated native-runs//native-sessions/ evidence, and fail-closed unknown/quarantined/retryable=false state — reachable today as a library/test surface only. The separately staged Stage 2 target adds a thin, unprivileged local arsd UDS host as the sole production ingress; it is not implemented yet, so 0.2.0 has no Native service or Native CLI entry. Neither line is Sachima, a Gateway plugin, or an IM adapter, and ARS never emits a business verdict (business_verdict is always null).

How it works

How agent-run-supervisor validates a role, supervises ACP/acpx, observes an external AGENT, and writes redacted local artifacts

Four principles keep it honest:

  • Supervisor, not business judge. Runner/protocol completion is never a business verdict; business_verdict stays null and caller-owned.
  • Auditable by default. Runs produce deterministic, redacted artifacts with restrictive permissions (0700 dirs, 0600 files, atomic final writes).
  • Fail closed on uncertainty. Invalid roles, cwd-outside-roots, malformed stdout, protocol drift, denied permissions, and watchdog timeouts all resolve to deterministic non-success statuses — an invalid cwd creates no artifacts at all.
  • Honest security claims. allowed_roots validates cwd/config intent only — it is not an OS/filesystem sandbox.

Out of scope — caller/platform territory: public ingress, real IM delivery, Gateway lifecycle, production config writes, live/default-on behavior, @all fan-out, and agent-to-agent routing.

Install and use

pip install agent-run-supervisor

Or install from a source checkout (see Development).

CLI

# Validate an AgentRoleSpec (JSON) and print its stable role hash
agent-run-supervisor validate-role <role-file>.json

# Replay an observed acpx stdout stream (deterministic, launches no AGENT)
# Source checkout: use repo fixtures (see note below).
# PyPI install: pass your own .ndjson path, or use `doctor` for built-in fixture replay.
agent-run-supervisor replay <events.ndjson>

# Probe local readiness (read-only, never launches an AGENT)
agent-run-supervisor doctor

# Dry-run: compile policy + argv and persist preview artifacts, launch nothing
agent-run-supervisor run \
  --role <role-file>.json --prompt-file <prompt>.txt --no-real-run

# Real one-shot exec: supervise a local `acpx exec` under the role's policy
# (requires acpx/Node available locally; launches ONE explicit, local AGENT)
agent-run-supervisor run \
  --role <role-file>.json --prompt-file <prompt>.txt

# Local persistent-session lifecycle (role must use a persistent session strategy):
# create → send turn(s) → status → close/abort. create/send/status/close/abort drive a
# real local acpx session and need Node + acpx; `session list` is local read-only and
# launches no AGENT.
agent-run-supervisor session create \
  --role <role-file>.json --session-id <id>
agent-run-supervisor session send \
  --role <role-file>.json --session-id <id> --prompt-file <prompt>.txt
# Or compile a validated goal turn from a goal file: adapters without a native
# ACP `/goal` command (all of them today) receive the goal-contract/v1 text template
agent-run-supervisor session send \
  --role <role-file>.json --session-id <id> --goal-file <goal>.txt
agent-run-supervisor session status \
  --role <role-file>.json --session-id <id>
agent-run-supervisor session close \
  --role <role-file>.json --session-id <id>
agent-run-supervisor session abort \
  --role <role-file>.json --session-id <id>
agent-run-supervisor session list

# Plan or apply local artifact retention/cleanup (dry-run by default; --apply deletes)
agent-run-supervisor cleanup

Fixture replay paths: fixtures/acpx-0.12.0/... exist in the git repository only. The PyPI wheel bundles a minimal fixture for doctor smoke, not the full fixture tree. From a checkout you can run: agent-run-supervisor replay fixtures/acpx-0.12.0/success-codex-sentinel/stdout.ndjson

From a source checkout without installing, prefix commands with PYTHONPATH=src python3 -m agent_run_supervisor instead of agent-run-supervisor.

# Clone and enter the repository
git clone https://github.com/jovijovi/agent-run-supervisor.git
cd agent-run-supervisor

# Example: validate-role from checkout (no install)
PYTHONPATH=src python3 -m agent_run_supervisor validate-role <role-file>.json

Codex/acpx smoke helper

For an explicit local connectivity check that exercises both supervised Codex surfaces — one-shot exec first, then a two-turn persistent session — use the maintained helper:

python3 scripts/smoke_codex_acpx.py --model 'gpt-5.5[xhigh]'

The helper creates temporary no-tool roles, asks Codex for exact sentinel replies, verifies business_verdict = null, closes the persistent session, and cleans artifacts by default (--keep-artifacts keeps the temp scratch/runs/sessions directories). It intentionally uses runner.acpx_binary = null, so the existing compiler invokes the pinned npx -y acpx@0.12.0 path.

Use the exact Codex ACP model IDs advertised by the ACP session, such as gpt-5.5[xhigh], gpt-5.5[high], or gpt-5.4-mini[medium]. A bare id like gpt-5.5 can be rejected with the ACP agent did not advertise that model, and the helper refuses it before launching anything.

Once installed (pip install -e .), the same surface is available as the agent-run-supervisor <command> … console script.

Run artifacts land under .agent-run-supervisor/runs/<run_id>/ — redacted prompt/env/argv, the generated policy, observed stdout (NDJSON), normalized events, stderr, result.json (business_verdict = null), and redaction-report.json. Persistent-session artifacts land under .agent-run-supervisor/sessions/<session_id>/ (local record, redacted management/ summaries, and one redacted turns/<turn_id>/ directory per send). The cleanup command plans and (only with --apply) deletes aged run/session artifacts, confined to the resolved .agent-run-supervisor root and never touching open/live-locked sessions.

Library usage

The released package is a Python library as well as a CLI. For v0.1.7 programmatic integration, prefer the generic local caller boundary (caller.py); the caller-stable payload contract is documented in docs/design/result-event-schema.md.

Install:

pip install agent-run-supervisor

Recommended API: invoke_caller + CallerInvocationSpec. The supervisor returns a supervisor-owned status and redacted artifacts; business_verdict is always null — your application interprets success/failure.

from agent_run_supervisor.caller import CallerInvocationSpec, invoke_caller

# One-shot exec (launches a real local AGENT when not dry-run)
result = invoke_caller(
    CallerInvocationSpec(
        mode="exec",
        role_file="reviewer.json",
        prompt="Summarize the diff in plain language.",
        cwd="/path/to/repo",
    )
)
print(result.supervisor_status)  # e.g. "completed"
print(result.result)             # result.json payload (dict)
print(result.run_dir)            # redacted artifact directory
assert result.business_verdict is None

# Dry-run compile/preview only — no subprocess, no AGENT
preview = invoke_caller(
    CallerInvocationSpec(
        mode="exec_dry_run",
        role_file="reviewer.json",
        prompt="Preview only.",
        cwd="/path/to/repo",
    )
)
print(preview.artifact_dir)

Persistent session (role must use strategy: persistent):

session_id = "my-local-session"

invoke_caller(
    CallerInvocationSpec(
        mode="session_create",
        role_file="reviewer.json",
        session_id=session_id,
        cwd="/path/to/repo",
    )
)
turn = invoke_caller(
    CallerInvocationSpec(
        mode="session_send",
        role_file="reviewer.json",
        session_id=session_id,
        prompt="Continue from the previous turn.",
        cwd="/path/to/repo",
    )
)
print(turn.session_dir)

Supported modes: exec, exec_dry_run, session_create, session_send, session_status, session_close, session_abort, session_list.

Lower-level surfaces (advanced): SupervisorRunner, SessionRuntime, parse_acpx_stdout_bytes. Inject a fake subprocess executor in tests — see tests/test_caller.py.

Reference caller: hermes_caller shows a concrete document-check integration with caller-owned verdicts and view-models (local/offline only).

Live progress polling (advanced)

During exec or session_send, the supervisor writes progress.json and seq-stamped normalized-events.jsonl while the child is still running. result.json remains the final authority after the run completes.

Poll structural progress only (no raw agent text) via hermes_caller.events:

from agent_run_supervisor.caller import CallerInvocationSpec, invoke_caller
from agent_run_supervisor.hermes_caller.events import load_progress, read_event_page

# invoke_caller is blocking; poll artifact_dir from another thread while it runs,
# or read artifacts after it returns.
result = invoke_caller(
    CallerInvocationSpec(
        mode="exec",
        role_file="reviewer.json",
        prompt="Summarize the diff.",
        cwd="/path/to/repo",
    )
)

# Structural fields only (no raw agent text)
snap = load_progress(result.run_dir)
if snap:
    print(snap.state, snap.last_seq, snap.event_count)

# Page through normalized-events.jsonl by seq cursor
page = read_event_page(result.run_dir, after_seq=0, limit=50)
for event in page.records:
    print(event.seq, event.kind, event.text_length)

Boundaries:

  • Applies to artifact directories from exec and session_send turns.
  • session_abort cancels an in-flight turn; session_list enumerates local session records read-only (optionally filtered by role).
  • Local read API only — no websocket, long-poll server, or IM delivery (see docs/roadmap/non-approvals.md).
  • Schema detail: docs/design/result-event-schema.md §4.

Schema stability: Public API and result schemas may evolve; read docs/design/result-event-schema.md for result.json fields.

Read-only session inspection (no subprocess)

For liveness/health checks over an existing persistent session, use session_inspect: it reads only local artifacts (the session record, the lease lock, turn artifacts) and never spawns acpx or an AGENT, so it is safe on a caller's hot polling path. session_status remains the acpx-side status -s management query; inspection is its non-spawning local complement.

from agent_run_supervisor.session_inspect import inspect_session, list_turns

inspection = inspect_session("/path/to/sessions", "nightly-review")
print(inspection.exists, inspection.state)                # True "open"
print(inspection.lease_held, inspection.holder_liveness)  # e.g. True "alive"
print(inspection.latest_turn_status)                      # e.g. "completed" (None while in flight)
if inspection.progress:
    print(inspection.progress.state, inspection.progress.last_seq)

for turn in list_turns("/path/to/sessions", "nightly-review"):
    print(turn.turn_id, turn.status)  # turn.turn_dir is a caller-private path
  • A missing session reports exists=False; corrupt/off-vocabulary artifacts degrade to None/unknown — never fabricated health, never raw agent text.
  • Lease fields follow the store's stale-lock semantics: lease_held means a present, not-provably-expired lease; holder_liveness is the crash-recovery classification; lease_recoverable means TTL-expired or provably crashed.

Environment requirements

Need Requirement
Runtime Python ≥ 3.11, standard-library only — zero third-party runtime dependencies.
Tests (optional) pytest >= 8, < 10 (the dev extra).
Native ACP core tests (optional) The native extra pins agent-client-protocol==0.11.0 for the Stage 0/1 L1/L2 suite; the real OpenCode smoke is opt-in (ARS_NATIVE_SMOKE=1) and needs a registered OpenCode 1.18.4 install.
Real AGENT runs / session turns Node + acpx + the target AGENT CLI available locally — required for run (without --no-real-run) and for the real session create/send/status/close/abort turn & management commands. The Codex smoke helper specifically needs npx plus Codex CLI via CODEX_PATH or PATH.
No-AGENT commands validate-role, replay, doctor, run --no-real-run, session list, and cleanup (dry-run) need no Node/acpx and launch no AGENT.

Development

Primary path uses uv for a reproducible dev environment. Short commands are available via the root Makefile:

git clone https://github.com/jovijovi/agent-run-supervisor.git
cd agent-run-supervisor
make sync      # uv sync --locked --extra dev --extra release --extra native
make verify    # full local gates (same as CI)
make build     # sdist/wheel + twine check
make smoke     # build + installed-wheel smoke
make clean     # remove build artifacts, caches, local scratch data
make help      # list all targets

Equivalent without Make:

uv sync --locked --extra dev --extra release --extra native
./scripts/verify_local.sh

make verify / ./scripts/verify_local.sh is the single local gate entry — it mirrors CI and docs/roadmap/verification.md (tests, doctor/replay smoke, docs index/drift, static safety scan, build/twine check, and installed-wheel smoke).

The full suite covers the Stage 0/1 Native ACP core at L1/L2 against a hermetic fake agent (the native extra pins agent-client-protocol==0.11.0). The real OpenCode 1.18.4 smoke never runs in CI and is opt-in: ARS_NATIVE_SMOKE=1 uv run pytest tests/native_acp/test_real_opencode_smoke.py -q (needs the registered OpenCode installation and credentials).

pip fallback (without uv):

pip install -e '.[dev,release,native]'
python3 -m pytest -q

Publishing

Releases are published to PyPI and GitHub via tag-triggered GitHub Actions Trusted Publishing (no API tokens in the repo).

Release process (maintainers):

make bump VERSION=X.Y.Z   # sync pyproject.toml, __init__.py, uv.lock, CHANGELOG stub
# edit CHANGELOG [X.Y.Z] section content
make verify              # or ./scripts/verify_local.sh
# merge bump PR to main
make release-tag         # prints git tag vX.Y.Z && git push commands
agent-run-supervisor doctor   # after pip install from PyPI

Trusted Publishing uses workflow release.yml and GitHub environment pypi. See docs/plans/archive/2026-07-06-p3-engineering-basics.md for the operator checklist.

Each GitHub Release for tag v* uploads dist/*.tar.gz, dist/*.whl, and dist/SHA256SUMS. Verify a wheel locally:

curl -LO https://github.com/jovijovi/agent-run-supervisor/releases/download/vX.Y.Z/SHA256SUMS
curl -LO https://github.com/jovijovi/agent-run-supervisor/releases/download/vX.Y.Z/agent_run_supervisor-X.Y.Z-py3-none-any.whl
sha256sum -c SHA256SUMS --ignore-missing

TestPyPI dry-run (local upload with API token in env — never commit tokens):

export TWINE_USERNAME=__token__
export TWINE_PASSWORD=pypi-...    # TestPyPI token
make release-test                 # verify + upload to TestPyPI

pip install --index-url https://test.pypi.org/simple/ \
            --extra-index-url https://pypi.org/simple/ \
            agent-run-supervisor
agent-run-supervisor doctor

Quality and test indicators

Factual local gates that keep the supervisor honest (run from the repository root with ./scripts/verify_local.sh, or step-by-step):

Indicator Evidence
Full local gate make verify or ./scripts/verify_local.sh — mirrors CI verify workflow.
Unit / integration tests Full pytest suiteuv run pytest -q (current local acceptance: full suite passing).
acpx contract acpx 0.12.0 fixtures + validator — scripts/validate_contract_fixtures.py fixtures/acpx-0.12.0.
Import / syntax smoke python -m compileall -q src scripts tests.
Doctor (read-only) … doctor never launches an AGENT (launched_real_agent = false).
Package checks python -m build + twine check dist/*, plus an installed-wheel agent-run-supervisor doctor smoke.
Safe artifacts Redacted artifacts · business_verdict = null · EventStore 0700/0600 atomic NDJSON.
uv sync --locked --extra dev --extra release --extra native
./scripts/verify_local.sh

Roadmap

The released v0.1.7 surface and the vNext target are deliberately separate. Current authority, stage status, gates, and non-approvals live in docs/roadmap/current-status.md, docs/roadmap/features.md, and docs/roadmap/non-approvals.md.

  • Released compatibility baseline — v0.1.7. Local acpx one-shot/persistent-session supervision, redacted evidence, doctor/cleanup, caller wrapper, packaging, and release engineering are implemented. They remain supported compatibility surfaces, not the architecture for new development.
  • Merged — vNext Stage 0/1 (0.2.0 source line). AgentProfile/immutable RunSpec, ManagedProcess, Native ACP exact config, process-per-Run Session load/switching, isolated Native stores, fail-closed state, default-deny permission mediation, and B-grade real OpenCode evidence are merged into main. Stage 1 is a library-level core, not production acceptance; see the archived plan.
  • Planned — vNext Stage 2. arsd UDS ingress, peer ownership, reconciliation, bounded operation, real denied-action mediation, and cgroup crash containment. G12 caller-UID policy and all production enablement require separate approval.
  • Parked — Sachima integration. A socket backend comes only after ARS production acceptance and a separate integration authorization. Public ingress, IM/Gateway behavior, and business orchestration remain out of ARS scope.

License

© the agent-run-supervisor authors. Released under the MIT license (license = "MIT" and LICENSE).

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

agent_run_supervisor-0.2.0.tar.gz (210.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

agent_run_supervisor-0.2.0-py3-none-any.whl (149.3 kB view details)

Uploaded Python 3

File details

Details for the file agent_run_supervisor-0.2.0.tar.gz.

File metadata

  • Download URL: agent_run_supervisor-0.2.0.tar.gz
  • Upload date:
  • Size: 210.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for agent_run_supervisor-0.2.0.tar.gz
Algorithm Hash digest
SHA256 5e2852fdfaede0148e4c9d8c5a6e5fac8159a6531fc7aa6c446501f6beb41143
MD5 0ecf3e38b15f347abee6dca85e2f7167
BLAKE2b-256 7e6eb9e672f30725e7bb5788e03ce1d8fe50c50f6bf89f31d2ff67e2e2e4447a

See more details on using hashes here.

Provenance

The following attestation bundles were made for agent_run_supervisor-0.2.0.tar.gz:

Publisher: release.yml on jovijovi/agent-run-supervisor

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file agent_run_supervisor-0.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for agent_run_supervisor-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9dec0d983d6dba0655798e1d884006f6c27243fa14d2af520cb10719fa124810
MD5 d7d11e032054da7bd1c0aef51296f3a5
BLAKE2b-256 dd6a2ff525f55e70e6d1ac4d4264dcbdd6909a15f1caeb8f89303581e29553e2

See more details on using hashes here.

Provenance

The following attestation bundles were made for agent_run_supervisor-0.2.0-py3-none-any.whl:

Publisher: release.yml on jovijovi/agent-run-supervisor

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.7.8

2 files

0.7.7

2 files

0.7.6

2 files

0.7.5

2 files

0.7.4

2 files

0.7.3

2 files

0.7.2

2 files

0.7.1

2 files

0.7.0

2 files

0.6.3

2 files

0.6.2

2 files

0.6.1

2 files

0.6.0

2 files

0.5.3

2 files

0.5.2

2 files

0.5.1

2 files

0.5.0

2 files

This release

0.2.0 This release

2 files

0.1.7

2 files

0.1.6

2 files

0.1.5

2 files

0.1.4

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page