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.
-
Check your interpreter
Any supported Python 3.10–3.13 release works.
terminalpython --version -
Clone and install
The distribution is named
argus-harness; the installed command isargus.terminalgit 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]" -
Add one provider key
Copy the example environment file and add a key for the model you want to use.
.envis ignored by Git and by the filesystem tools.terminalcp .env.example .env # Windows: copy .env.example .env.envGEMINI_API_KEY=your-key-hereArgus looks for
.envin the current directory first, then in the repository root. Existing environment variables are never overwritten. -
Verify the command
terminalargus --version argus --helpargus --versionprintsargus 0.2.0. Usepython -m argusanywhere 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.
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.
argus
For a single task, pass --prompt. Headless safe runs deny mutating tools unless you explicitly pass --yes.
# 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:
| Command | Action |
|---|---|
/help | Show the interactive command summary |
/mode read-only | Change the policy mode for subsequent turns |
/session | Print the current session file path |
/tokens | Show accumulated input and output usage |
/lang es | Switch the interface language (en, es, hi, fr) |
/exit | Leave 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 | --provider | Default model | API key |
|---|---|---|---|
| Google Gemini | gemini | gemini-3.1-pro-preview | GEMINI_API_KEY |
| Anthropic | anthropic | claude-sonnet-4-5 | ANTHROPIC_API_KEY |
| OpenAI | openai | gpt-4o-mini | OPENAI_API_KEY |
| OpenRouter | openrouter | openai/gpt-4o-mini | OPENROUTER_API_KEY |
| Groq | groq | llama-3.3-70b-versatile | GROQ_API_KEY |
| Ollama | ollama | llama3.2 | none — 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
--providerselects an adapter explicitly.ARGUS_PROVIDERprovides 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*, andchatgpt*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.
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_useblocks and results becometool_resultblocks; 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_tokensand omittemperature. - All adapters. HTTP retries are bounded,
Retry-Afteris honored, malformed payloads raise a typedProviderError, 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.
| Mode | Behavior | Typical use |
|---|---|---|
read-only | Only 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 |
yolo | Approval 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
realpathand must stay inside the workspace. Escapes through..or symlinks fail. Names.env,.env.*,.git, and.argusare 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
safemode. 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.
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.
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.
| Module | Responsibility |
|---|---|
argus/harness.py | Composes provider, tools, policy, memory, skills, sessions, and sub-agents |
argus/loop.py | Runs 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.py | Policy modes, deterministic deny patterns, and approval decisions |
argus/tools.py | Confined filesystem tools, bounded shell, redaction, and the tool decorator |
argus/session.py | Append-only JSONL storage, load repair, listing, and latest-session lookup |
argus/context.py | Token estimation and provider-generated compaction |
argus/memory.py | System prompt and durable ARGUS.md project memory |
argus/skills.py | Workspace skill catalog and confined loading |
argus/subagent.py | Bounded delegation to a fresh child context |
argus/fleet.py | Ordered parallel execution of independent jobs |
argus/cli.py | Interactive, headless, session, and fleet entry points |
argus/i18n.py | English, Spanish, Hindi, and French interface strings |
One turn, step by step
- The harness records the user task and asks the provider for the next assistant message.
- If the assistant returns tool calls, each call crosses the policy before execution. A denial becomes a tool result instead of an exception.
- Tool results are appended to the transcript and returned to the provider on the next turn.
- The loop continues until the assistant returns text without tool calls, the provider fails, or the turn limit is reached.
- 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
| Flag | Default | Purpose |
|---|---|---|
-p, --prompt TEXT | interactive | Run one headless task and exit |
-d, --workdir DIR | . | Workspace directory |
-m, --model NAME | provider default | Override the model |
--provider NAME | inferred | Select gemini, anthropic, openai, openrouter, groq, or ollama |
--base-url URL | provider default | Override the API endpoint |
--mode MODE | safe | read-only, safe, or yolo |
--yes | off | Auto-approve mutating tools in safe mode |
--max-turns N | 120 | Maximum model turns per task |
--budget-tokens N | 600000 | Context budget before compaction |
--lang CODE | environment | Interface language: en, es, hi, fr |
--resume | off | Resume the newest session in the workspace |
--session PATH | — | Resume a specific session file |
--list-sessions | off | List saved sessions and exit |
--fleet FILE | — | Run a JSON fleet file and exit |
--workers N | 4 | Fleet 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.
# 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.
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
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.
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.
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.
| Tool | What it does | Mode boundary |
|---|---|---|
read_file | Read a text file with line numbers; stops at 4,000 lines | Read-oriented |
list_files | List workspace files by path or basename pattern | Read-oriented |
grep | Search files with a regular expression | Read-oriented |
write_file | Write a file atomically, creating parent directories | Mutating |
edit_file | Replace one unique snippet, or return a precise error | Mutating |
bash | Run a bounded shell command in the workspace | Mutating |
remember | Append one durable fact to ARGUS.md | Mutating |
spawn_agent | Delegate a self-contained task to a fresh child context | Mutating |
use_skill | Load a workspace skill when a catalog exists | Read-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.
---
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": [
{
"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."
}
]
}
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:
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.
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
- Update the version and changelog, run the checks, and publish a GitHub release.
- The release workflow builds and verifies the sdist and wheel.
- PyPI trusted publishing uploads the distributions from the
pypienvironment. - 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.