Skip to main content

agent-safe-runner

CI Python 3.11+ License: MIT

agent-safe-runner is a small, local-first queue for automation commands proposed by AI agents. It stores jobs in SQLite, requires a separate approval decision and an explicit command policy before execution, and writes a redacted JSONL audit trail.

The project is intentionally narrow: it helps a local operator control which commands may run, when they may run, and what evidence is retained afterward.

[!WARNING] This project is an execution gate, not an operating-system sandbox. Run workers with a low-privilege account and use containers or OS isolation for untrusted code.

Why it exists

Agent workflows often grow from scripts into unattended queues. At that point, a plain subprocess.run() leaves important questions unanswered:

  • Was the command explicitly allowed?
  • Who reviewed it before execution?
  • Could two workers run the same job?
  • Did a retry happen, and why?
  • Did logs accidentally store a token?
  • Can the operator verify the event history?

This runner makes those controls explicit without adding a service, broker, or cloud dependency.

Features

  • Deny-by-default JSON policy with command-prefix and working-directory rules
  • Approval inbox, read-only assessment, and explicit approve/deny decisions
  • Dry run by default; real execution requires approval, policy allowance, and --execute
  • SQLite queue with idempotency keys and fail-closed schema migration from 0.1.x / 0.2.x
  • Atomic leases, expired-lease recovery, bounded retries, and exponential backoff
  • Job cancellation, manual retry, status filtering, and one-pass worker mode
  • Secret-like argument rejection before persistence
  • Minimal child-process environment and redacted output capture
  • Append-only JSONL audit events with a verifiable SHA-256 hash chain
  • Structured JSON output and errors for scripting
  • Optional stdio MCP adapter with proposal/read-only tools; no approval or execution tools
  • Standard-library runtime with no required third-party dependencies

Install

Python 3.11 or newer is required.

Install the latest version directly from GitHub:

python -m pip install "https://github.com/umefor-labs/agent-safe-runner/archive/refs/heads/main.zip"

Confirm that the command is available:

agent-safe --version

If your system does not expose the agent-safe command after installation, use python -m agent_safe_runner in its place.

Short installation command (after PyPI publication)

Once a release is listed on PyPI, users can install it in isolation with:

pipx install agent-safe-runner

Until the first PyPI upload is verified, use the GitHub installation above. A GitHub push alone does not publish to PyPI; maintainers can follow the publishing checklist.

For AI-agent integrations, see the optional MCP adapter.

For an isolated installation with pipx, see the getting-started guide. Contributors should use the development setup.

Quick start

Create a dedicated workspace so the queue, policy, and audit files stay together:

mkdir agent-safe-workspace
cd agent-safe-workspace
agent-safe init-policy

The generated agent-safe-policy.json contains a few example rules. Review it before use: python --version prints a version, but pytest executes project code and is appropriate only in a trusted workspace.

Queue a command that prints the installed Python version:

agent-safe submit --cwd . --timeout 30 -- python --version

The command returns a JSON object. Copy its id, then inspect the job and perform a dry run. Replace JOB_ID below with that value:

agent-safe show JOB_ID
agent-safe assess JOB_ID
agent-safe run JOB_ID

assess returns allowed, reason, and matched_rule. An allowed job is still pending approval. run without --execute remains a dry run.

After reviewing the exact command, directory, timeout, and retry limit, record your decision. Replace local-operator with a label meaningful to you:

agent-safe approve JOB_ID --by local-operator --reason "Reviewed version check"
agent-safe run JOB_ID --execute --worker local-1

--by is an audit label, not authentication. Anyone with access to the approval CLI or writable database can approve jobs; this is a workflow gate.

The final JSON should report "status": "succeeded". Verify the audit chain's integrity:

agent-safe audit-verify

See Getting started for installation isolation, upgrades, troubleshooting, and a complete first-run walkthrough.

Common commands

agent-safe list
agent-safe inbox
agent-safe list --approval pending
agent-safe list --status queued --status retry_wait
agent-safe show JOB_ID
agent-safe assess JOB_ID
agent-safe deny JOB_ID --by local-operator --reason "Not needed"
agent-safe cancel JOB_ID
agent-safe retry JOB_ID
agent-safe work --once --execute --worker local-1
agent-safe audit-verify

Global paths must appear before the subcommand:

agent-safe --db /path/to/jobs.sqlite3 --audit /path/to/audit.jsonl --policy /path/to/policy.json list

Everything after -- in submit is stored as an argument vector and is never passed through a shell parser. All commands emit JSON. Expected input, state, and policy errors return exit code 2 with a stable error code.

work --once --execute picks only approved jobs. Manual retry clears the old decision and requires fresh approval; automatic retries keep the existing approval for the unchanged job. deny cancels a pending job. To stop an already approved queued job, use cancel.

Upgrade and uninstall

Upgrading from 0.2.x or older? Stop all workers and back up your local state before installing. Old queued jobs become pending and will not run until reviewed. See the 0.3 migration guide.

Upgrade to the latest GitHub version:

python -m pip install --upgrade "https://github.com/umefor-labs/agent-safe-runner/archive/refs/heads/main.zip"

Remove the command-line application:

python -m pip uninstall agent-safe-runner

Uninstalling does not delete your queue, policy, or audit files.

Policy

Execution is denied when the policy file is absent. A policy contains:

{
  "version": 1,
  "allowed_commands": [
    {"program": "python", "args_prefix": ["--version"]},
    {"program": "python", "args_prefix": ["-m", "pytest"]},
    {"program": "git", "args_prefix": ["status"]}
  ],
  "allowed_working_roots": ["."],
  "denied_arguments": ["--force", "--hard"],
  "environment_allowlist": ["PATH", "PATHEXT", "SYSTEMROOT", "WINDIR", "TEMP", "TMP"],
  "max_timeout_seconds": 300,
  "max_output_chars": 8000
}

Rules compare resolved executable paths and the case-sensitive beginning of the argument list. Missing executables do not match. An empty args_prefix allows every argument for that executable and should be used cautiously.

Never place credentials in a command. The runner rejects common secret flags and token formats, but detection cannot identify every secret. Use a dedicated secret provider and grant the worker only the environment variables it needs.

Job states

queued -> running -> succeeded
                  -> retry_wait -> running
                  -> failed
queued/retry_wait -> cancelled -> queued (manual retry)
queued/retry_wait -> dead_letter (policy denial)

Approval is separate from execution status: pending, approved, denied, or legacy for historical records. New jobs start queued + pending. Policy-invalid approved jobs become dead_letter without spawning a process. Nonzero exits, timeouts, and process-start failures retry up to max_attempts.

Data files

  • agent-safe.sqlite3: queue state, commands, and redacted results
  • audit.jsonl: redacted event records and hash-chain metadata
  • agent-safe-policy.json: local execution policy

These runtime files are ignored by Git. SQLite commands are stored in plain text, so do not submit secrets or place the database in a public or broadly synchronized directory.

Current limits

  • The audit chain detects accidental edits; it is not a cryptographic signature and an attacker with write access can rebuild it.
  • Audit appends use advisory file locking on Windows and POSIX; filesystems that ignore advisory locks are unsupported for multi-process writers.
  • Running jobs cannot currently be interrupted by cancel; cancellation applies to queued and retry-wait jobs.
  • Approval records are not signatures or user authentication. This gate cannot constrain an agent that already has unrestricted terminal or file access.
  • SQLite state and JSONL audit are separate stores, not a single crash-atomic transaction. See Architecture.
  • Output limits bound stored text, not peak capture memory; lease recovery is at-least-once, not an exactly-once guarantee for external side effects.
  • MCP is local stdio only. There is no remote API, scheduler daemon, plugin system, or secret-provider integration yet.

See Architecture, Threat model, Roadmap, Contributing, and Security policy.

License

MIT

Download files

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

Source Distribution

agent_safe_runner-0.4.0.tar.gz (41.9 kB view details)

Uploaded Source

Built Distribution

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

agent_safe_runner-0.4.0-py3-none-any.whl (23.8 kB view details)

Uploaded Python 3

File details

Details for the file agent_safe_runner-0.4.0.tar.gz.

File metadata

  • Download URL: agent_safe_runner-0.4.0.tar.gz
  • Upload date:
  • Size: 41.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for agent_safe_runner-0.4.0.tar.gz
Algorithm Hash digest
SHA256 22e9605dbc666040f733287ba2dbf474a5f797ae6c38446a4d9b05e4635f5afc
MD5 d1a719111fbef1d991eb531bf8fd79fc
BLAKE2b-256 15dda24d325b53146f7ad13c508b15bf2357bc14e17ff2d634121f7ec106f897

See more details on using hashes here.

Provenance

The following attestation bundles were made for agent_safe_runner-0.4.0.tar.gz:

Publisher: publish.yml on umefor-labs/agent-safe-runner

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_safe_runner-0.4.0-py3-none-any.whl.

File metadata

File hashes

Hashes for agent_safe_runner-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 85169075c7a8b2b2c22e85563130635cb93d548d9fcbd3660d2e14df3cc88d54
MD5 d1ec04dc3a4101afb7c98dc156cad54f
BLAKE2b-256 6836f303c0f046c8407fbed5d62863041e6aed04d073926f37fb1406b981ea85

See more details on using hashes here.

Provenance

The following attestation bundles were made for agent_safe_runner-0.4.0-py3-none-any.whl:

Publisher: publish.yml on umefor-labs/agent-safe-runner

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.4.3

2 files

0.4.2

2 files

0.4.1

2 files

This release

0.4.0 This release

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