Documentation · v0.2.0

Read the whole
request path.

Install the harness, connect a provider, choose a policy, and keep work alive across restarts. Every behavior described here maps to a module you can open and change.

Python 3.10+ Zero runtime dependencies MIT license Six providers

Install

Argus is a standard Python package with no runtime dependencies. You need Python 3.10 or newer and Git. A virtual environment keeps the command isolated from the rest of your system.

  1. Check your interpreter

    Any supported Python 3.10–3.13 release works.

    terminal
    python --version
  2. Clone and install

    The distribution is named argus-harness; the installed command is argus.

    terminal
    git clone https://github.com/PRINCE2-AI/Argus.git
    cd Argus
    python -m venv .venv
    source .venv/bin/activate        # Windows: .venv\Scripts\activate
    pip install -e ".[dev]"
  3. Add one provider key

    Copy the example environment file and add a key for the model you want to use. .env is ignored by Git and by the filesystem tools.

    terminal
    cp .env.example .env             # Windows: copy .env.example .env
    .env
    GEMINI_API_KEY=your-key-here

    Argus looks for .env in the current directory first, then in the repository root. Existing environment variables are never overwritten.

  4. Verify the command

    terminal
    argus --version
    argus --help

    argus --version prints argus 0.2.0. Use python -m argus anywhere in place of the console script.

Package status. The project builds and validates the argus-harness sdist and wheel in CI. The release workflow uses PyPI trusted publishing and runs only when a GitHub release is published; until a release is live, install from source.

Docker

The repository includes a minimal Dockerfile that installs the package, creates a non-root user, and starts in /workspace. Build it locally and mount the directory you want Argus to work in.

terminal
docker build -t argus-harness .
docker run --rm -it -v "$PWD:/workspace" -w /workspace \
  -e GEMINI_API_KEY argus-harness \
  --prompt "Map this repository"

When a GitHub release is published, the release workflow also builds and pushes the image to ghcr.io/prince2-ai/argus. Container isolation is still your responsibility: the policy is not an operating-system sandbox.

Quick start

Start interactive mode with no arguments. The banner shows the provider, model, workspace, and safety mode.

terminal
argus

For a single task, pass --prompt. Headless safe runs deny mutating tools unless you explicitly pass --yes.

terminal
# Auto-approve within the safe boundary
argus --workdir ./scratch --yes \
  --prompt "Create fib.py with an iterative fib(n), run it, and confirm fib(30)"

# Explore without writing or running shell commands
argus --mode read-only \
  --prompt "Map this repository and flag risky shell usage"

# Resume the newest durable session
argus --resume

# See what can be resumed
argus --list-sessions

Inside interactive mode, these slash commands control the running harness:

CommandAction
/helpShow the interactive command summary
/mode read-onlyChange the policy mode for subsequent turns
/sessionPrint the current session file path
/tokensShow accumulated input and output usage
/lang esSwitch the interface language (en, es, hi, fr)
/exitLeave interactive mode

Approvals happen at the tool boundary. In interactive safe mode, Argus asks before a mutating call. In a headless run there is no approver, so the call is denied unless --yes is present. Deny patterns and the path jail still apply either way.

Providers

Six adapters share one neutral message format. Explicit selection wins; otherwise Argus infers a provider from ARGUS_PROVIDER, the base URL, or the model name.

Provider--providerDefault modelAPI key
Google Geminigeminigemini-3.1-pro-previewGEMINI_API_KEY
Anthropicanthropicclaude-sonnet-4-5ANTHROPIC_API_KEY
OpenAIopenaigpt-4o-miniOPENAI_API_KEY
OpenRouteropenrouteropenai/gpt-4o-miniOPENROUTER_API_KEY
Groqgroqllama-3.3-70b-versatileGROQ_API_KEY
Ollamaollamallama3.2none — local

Every cloud provider also accepts ARGUS_API_KEY as a shared fallback. Ollama deliberately does not: it reads only OLLAMA_API_KEY, so a cloud key is never sent to localhost.

Selection order

  • --provider selects an adapter explicitly.
  • ARGUS_PROVIDER provides the default when the flag is absent.
  • A base URL (flag or ARGUS_BASE_URL) selects the OpenAI-compatible adapter.
  • A claude* model selects Anthropic; gpt*, o1*, o3*, o4*, o5*, and chatgpt* select OpenAI; a model containing / selects OpenRouter; anything else defaults to Gemini.

Model overrides follow the same idea: --model wins, then the provider-specific variable such as ANTHROPIC_MODEL, then ARGUS_MODEL, then the adapter default. Base URLs resolve through --base-url, the provider-specific variable, ARGUS_BASE_URL, and finally the default endpoint.

terminal
argus --provider anthropic --model claude-sonnet-4-5 \
  --prompt "Review the policy boundary in security.py"

argus --provider openai --model gpt-4o-mini \
  --prompt "Explain how interrupted tool calls are repaired"

argus --provider ollama --model llama3.2 \
  --prompt "Summarize this repository"

Adapter details that matter

  • Gemini. Thought signatures on tool calls are carried through the neutral format and sent back with the next request.
  • Anthropic. Tool calls become tool_use blocks and results become tool_result blocks; consecutive results merge into one valid user turn.
  • OpenAI and compatible APIs. Tool arguments are JSON-encoded at the edge. Reasoning model families switch to max_completion_tokens and omit temperature.
  • All adapters. HTTP retries are bounded, Retry-After is honored, malformed payloads raise a typed ProviderError, and usage is normalized to input, output, and total tokens.

Safety model

Safety is a code path, not a sentence in a prompt. Every tool call crosses Policy.check before execution, and every filesystem tool resolves through the same workspace boundary.

ModeBehaviorTypical use
read-onlyOnly read-oriented tools: read_file, list_files, grep, and use_skill. Shell and writes are denied.Repository review, incident inspection
safe (default)Mutating tools require an interactive approval. Headless runs deny them unless --yes is passed.Normal development
yoloApproval prompts are bypassed. Deterministic deny patterns and the path jail still run.Trusted, disposable workspaces

Layers that apply in every mode

  • Path jail. Filesystem paths are resolved with realpath and must stay inside the workspace. Escapes through .. or symlinks fail. Names .env, .env.*, .git, and .argus are protected.
  • Deny patterns. Shell commands are checked against deterministic rules for root/home deletion, parent traversal, protected directory deletion, privilege escalation, disk formatting, reverse shells, cloud metadata access, pipe-to-shell downloads, force pushes, hard resets, and fork bombs.
  • No silent approval. The CLI installs an approver only in interactive safe mode. Without one, the policy returns a denial.
  • Secret redaction. Tool output masks common key formats and environment values whose names look like credentials. Shell subprocesses receive an environment with secret-named variables removed.
  • Bounded operations. Reads stop at 4,000 lines, grep skips files over 1 MB, shell output is capped, and the default shell timeout is 120 seconds.

Argus is not an OS sandbox. These boundaries reduce accidental damage; they do not isolate hostile code. Run untrusted tasks in a container or virtual machine.

Optional semantic review

For deployments that need another check on shell commands, construct a policy with a decision_model. The optional Jev layer classifies commands as safe, review, or dangerous. Low confidence, malformed responses, unavailable service, and dangerous results all fail closed rather than silently authorizing a command.

python
from argus import Harness, Jev, Policy

policy = Policy("safe", decision_model=Jev())
harness = Harness(workdir=".", policy=policy)

Jev requires JEV_API_KEY. It sends the candidate command to the configured decision endpoint, so do not enable it for workspaces whose commands cannot leave the machine.

Sessions

Every assistant message and tool result is appended to a JSONL file under .argus/sessions/ in the workspace. The file is plain text and belongs to that workspace.

terminal
argus --resume                  # load the newest session
argus --session PATH            # load a specific file
argus --list-sessions           # list files with size and a short preview

What load repairs

  • A torn final line from a crash is ignored instead of failing the entire session.
  • Tool calls interrupted by a missing result receive an explicit error result so the next provider request remains valid.
  • Duplicate adjacent tool rows produced by older versions are removed.
  • The newest compaction marker is restored: the summary plus the recent verbatim turns, with leading orphaned tool messages dropped.

Compaction and limits

When the estimated transcript exceeds the token budget, Argus asks the provider for a dense, factual summary of older turns, keeps the six most recent messages verbatim, and records the summary as a normal user message with meta.compaction. The summary is persisted, so a resumed session reloads the compacted history instead of the original transcript.

The default budget is 600,000 tokens and the default turn limit is 120. Both are configurable with --budget-tokens and --max-turns. At the turn limit, Argus requests one final tool-free wrap-up instead of silently stopping mid-task.

Transcripts are not encrypted. They contain conversation text and tool output after redaction. Keep the workspace and its .argus directory at the same sensitivity as the work being performed.

Architecture

A small composition layer, one turn loop, and named boundaries. The harness wires the pieces together; the loop never learns a provider dialect or a policy's approval rules.

ModuleResponsibility
argus/harness.pyComposes provider, tools, policy, memory, skills, sessions, and sub-agents
argus/loop.pyRuns turns, executes tools, emits events, and requests a wrap-up at the limit
argus/providers/Translates the neutral message format to and from six wire formats
argus/security.pyPolicy modes, deterministic deny patterns, and approval decisions
argus/tools.pyConfined filesystem tools, bounded shell, redaction, and the tool decorator
argus/session.pyAppend-only JSONL storage, load repair, listing, and latest-session lookup
argus/context.pyToken estimation and provider-generated compaction
argus/memory.pySystem prompt and durable ARGUS.md project memory
argus/skills.pyWorkspace skill catalog and confined loading
argus/subagent.pyBounded delegation to a fresh child context
argus/fleet.pyOrdered parallel execution of independent jobs
argus/cli.pyInteractive, headless, session, and fleet entry points
argus/i18n.pyEnglish, Spanish, Hindi, and French interface strings

One turn, step by step

  1. The harness records the user task and asks the provider for the next assistant message.
  2. If the assistant returns tool calls, each call crosses the policy before execution. A denial becomes a tool result instead of an exception.
  3. Tool results are appended to the transcript and returned to the provider on the next turn.
  4. The loop continues until the assistant returns text without tool calls, the provider fails, or the turn limit is reached.
  5. Usage, persistence, and context compaction happen in the harness around the loop, not inside any provider adapter.

Provider adapters implement one method, complete(system, messages, tools), and return normalized text, tool calls, usage, and a finish reason. A new tool implements a typed callable and is passed through extra_tools; no loop change is required.

For the full design rationale, read docs/ARCHITECTURE.md.

CLI reference

FlagDefaultPurpose
-p, --prompt TEXTinteractiveRun one headless task and exit
-d, --workdir DIR.Workspace directory
-m, --model NAMEprovider defaultOverride the model
--provider NAMEinferredSelect gemini, anthropic, openai, openrouter, groq, or ollama
--base-url URLprovider defaultOverride the API endpoint
--mode MODEsaferead-only, safe, or yolo
--yesoffAuto-approve mutating tools in safe mode
--max-turns N120Maximum model turns per task
--budget-tokens N600000Context budget before compaction
--lang CODEenvironmentInterface language: en, es, hi, fr
--resumeoffResume the newest session in the workspace
--session PATH—Resume a specific session file
--list-sessionsoffList saved sessions and exit
--fleet FILE—Run a JSON fleet file and exit
--workers N4Fleet concurrency
--version—Print the version and exit

--resume and --session are mutually exclusive. --fleet cannot be combined with --prompt. The CLI exits non-zero when a provider request fails, a fleet job fails, or the session cannot be loaded.

terminal
# Review while denying every write and shell command
argus --mode read-only --prompt "Audit the session repair code"

# Auto-approve in safe mode, then resume the same work later
argus --yes --workdir ./scratch --prompt "Build a small CLI and test it"
argus --workdir ./scratch --resume

Python API

Harness is the composition layer. It accepts a provider name or a provider instance, a policy, extra tools, an event callback, and session options.

python
from argus import Harness, Policy

harness = Harness(
    workdir="./scratch",
    provider="gemini",
    model="gemini-3.1-flash-lite",
    policy=Policy("safe"),
)

print(harness.run("Create a short project status file and verify it."))

Resume durable work

python
harness = Harness(workdir="./scratch")
if harness.resume():
    print(harness.run("Continue the interrupted task."))

resume(path=None) returns False when no session exists or the chosen file is empty. run(task) returns the assistant's final text. Set persist=False for ephemeral harnesses; sub-agents use that mode.

Add a tool

The decorator reads the function signature, builds a provider schema, and returns a tool descriptor. Pass it through extra_tools; the loop does not need to change.

python
from argus import Harness, tool

@tool("Look up a customer by email", email="Customer email address")
def find_customer(email):
    return {"email": email, "plan": "pro"}

harness = Harness(workdir=".", extra_tools=[find_customer])
print(harness.run("Find ada@example.com and report the plan."))

Inject a custom approval policy

The approver receives the call and a reason string. Returning True allows it; anything else keeps the denial. Read-only tools and yolo mode bypass the approver, while deny patterns still run first.

python
from argus import Harness, Policy

def approve(call, reason):
    return call.get("name") == "write_file"

policy = Policy("safe", approver=approve)
harness = Harness(workdir="./scratch", policy=policy)

Observe events

on_event(kind, payload) receives assistant, tool_start, tool_end, usage, and turn_limit. This is an event hook, not token streaming.

Use a custom provider

Subclass Provider, implement complete(system, messages, tools), and return a dictionary with text, tool_calls, usage, and finish_reason. Register the class in argus/providers/__init__.py or pass an instance as provider=.

Tools & skills

The base harness exposes six workspace tools plus memory and delegation. Every filesystem path passes through the workspace jail.

ToolWhat it doesMode boundary
read_fileRead a text file with line numbers; stops at 4,000 linesRead-oriented
list_filesList workspace files by path or basename patternRead-oriented
grepSearch files with a regular expressionRead-oriented
write_fileWrite a file atomically, creating parent directoriesMutating
edit_fileReplace one unique snippet, or return a precise errorMutating
bashRun a bounded shell command in the workspaceMutating
rememberAppend one durable fact to ARGUS.mdMutating
spawn_agentDelegate a self-contained task to a fresh child contextMutating
use_skillLoad a workspace skill when a catalog existsRead-oriented

Project memory

ARGUS.md in the workspace is injected into the system prompt, capped at 20,000 characters. The remember tool adds a normalized bullet and refuses exact duplicates. Memory is reference data: it cannot override the user's request or the policy.

Workspace skills

Create skills/<name>/SKILL.md with optional front matter. When at least one skill exists, the catalog is added to the system prompt and a use_skill tool becomes available. Skill names are validated, resolution is confined to the workspace, and content is capped at 64,000 characters.

skills/release/SKILL.md
---
description: Cut and verify a release
---

1. Run the offline test suite.
2. Build the sdist and wheel.
3. Inspect the wheel outside the source tree.

Sub-agents

When enabled, spawn_agent creates a child harness with the same provider and policy but a fresh context. Child sessions are not persisted and delegation is capped at two levels. The child returns only its final report, keeping the parent transcript small.

Fleet

A fleet runs independent tasks in parallel while preserving input order in the final report. Each job has its own workspace, so sessions and file writes do not collide.

jobs.json
{
  "jobs": [
    {
      "name": "docs",
      "workdir": "./scratch/docs",
      "task": "Write a README and verify every relative link."
    },
    {
      "name": "api",
      "workdir": "./scratch/api",
      "task": "Add request validation and run the tests."
    }
  ]
}
terminal
argus --fleet jobs.json --workers 2 --yes

The file may be a list of jobs or an object with a jobs key. Every job requires name, workdir, and task. A failed job is reported with its exception text and does not cancel the others; the process exits non-zero if any job fails.

The Python form returns results in input order, each with name, ok, report, and seconds:

python
from argus import Harness, run_fleet

jobs = [
    {"name": "docs", "workdir": "./scratch/docs", "task": "Write a README."},
    {"name": "api", "workdir": "./scratch/api", "task": "Add validation."},
]

results = run_fleet(
    jobs,
    lambda workdir: Harness(workdir=workdir, persist=False),
    max_workers=2,
)

Shared policy, separate workspaces. The CLI uses one policy for all jobs. Because a fleet is non-interactive, safe mode needs --yes for mutating work, or the jobs will be denied.

Development

Contributions are expected to keep the harness offline-testable and dependency-free at runtime. The test suite uses a scripted provider; it never calls a model or needs a credential.

terminal
pip install -e ".[dev]"
python -m unittest discover -s tests -v
ruff check argus tests
python -m build
python -m twine check dist/*

The suite currently contains 102 tests covering the loop, policy, path confinement, secret redaction, every provider adapter, session repair, compaction, memory, skills, fleet ordering, and i18n key parity. CI runs the suite on Python 3.10, 3.11, 3.12, and 3.13, with separate lint and packaging jobs.

Release path

  1. Update the version and changelog, run the checks, and publish a GitHub release.
  2. The release workflow builds and verifies the sdist and wheel.
  3. PyPI trusted publishing uploads the distributions from the pypi environment.
  4. The same release builds and pushes the container image to GHCR and attaches the distributions to the release.

Read CONTRIBUTING.md before opening a pull request, and report vulnerabilities through the private channel described in SECURITY.md.

FAQ

Is Argus production-ready?

Argus is at 0.2 and intentionally small. It is suitable for trusted workspaces and teams that value an auditable boundary. It is not an operating-system sandbox, and the public API may still change before 1.0. Run untrusted code in a container or VM.

Does it stream model responses?

No. Version 0.2 requests complete provider responses and exposes turn-level events. Token streaming is not implemented. The event hook is useful for transcripts and logging, but it is not a delta stream.

Does it support MCP tools?

There is no built-in MCP client in 0.2. You can wrap an MCP call in a Python tool and pass it through extra_tools, but that integration is not shipped or covered by the test suite.

Why zero dependencies?

A small dependency surface makes the harness easier to audit, vendor, and embed. Providers use the standard library HTTP client; the CLI, sessions, policy, and tests use only the standard library. Development tools such as ruff and build are optional extras.

Which operating systems are supported?

Anywhere Python 3.10+ runs. The code uses portable standard-library primitives and is developed on Windows; CI currently verifies Linux across Python 3.10–3.13.

How are secrets kept out of the model context?

Protected paths are rejected by the filesystem tools, tool output is redacted, and shell subprocesses receive a sanitized environment. Secrets can still appear in prompts or unredacted content, so do not place credentials in the workspace. Argus is not a secret manager.

What is the license?

MIT. See LICENSE.