Auto-Guard
Auto-Guard checks every tool call your coding agent makes before it runs. It sends the proposed call and your request to Jev, TypeSafe's decision model, and gets back one of three verdicts:
| Verdict | What happens |
|---|---|
| 🟢 allow | The call runs normally. |
| 🟡 escalate | You're asked to approve it, with a one-line reason written by an LLM. |
| 🔴 block | The call doesn't run. The agent is told why and tries something else. |
A check takes about 0.4s and costs about $0.000025.
Quick start
Install it (Python 3.9+, no other dependencies):
pip install agent-autoguard
Add an API key. An OpenRouter key is the easiest option:
mkdir -p ~/.autoguard && echo 'OPENROUTER_API_KEY=sk-or-...' >> ~/.autoguard/env
Turn it on in your project for your agent:
autoguard install # Claude Code
autoguard install --agent cursor # or codex, gemini, copilot, windsurf
Add --user to turn it on for every project. To confirm your key works:
autoguard check
# BLOCK 402ms $0.000025 rm -rf /tmp/build && rm -rf ~/.aws
# ALLOW 402ms $0.000025 ls -la build/
To watch decisions as they happen, run autoguard console and open http://127.0.0.1:8787.
Supported agents
| Agent | Install | What gets checked | When Jev is unsure |
|---|---|---|---|
| Claude Code | autoguard install |
Bash, file edits and writes, WebFetch | asks you |
| Cursor | --agent cursor |
shell commands, MCP tools, file writes | asks you (shell and MCP), blocks (file writes) |
| GitHub Copilot CLI | --agent copilot |
every tool call | asks you |
| OpenAI Codex CLI | --agent codex |
shell commands, patches, MCP tools | blocks |
| Gemini CLI | --agent gemini |
shell commands, file writes, MCP tools | blocks |
| Windsurf | --agent windsurf |
shell commands, file writes, MCP tools | blocks |
Codex, Gemini CLI and Windsurf hooks can only allow or block, so an unsure call gets blocked with the reason shown. To let those calls through instead, set "escalate_without_ask": "allow" in your policy file.
Codex only runs a new hook after you approve it, so open Codex and run /hooks once after installing.
Claude Code has been tested end to end in real sessions. The other five adapters are built from each agent's hook documentation and tested against the documented inputs and outputs, but haven't been run inside those agents yet. If one misbehaves, please open an issue.
Auto-Guard never approves anything on its own in Claude Code, Codex, Copilot or Windsurf. When it allows a call, the agent's normal permission rules still apply. Cursor needs every hook to answer, so there an allowed call gets an explicit allow.
Use it in your own agent
from autoguard import guard
decision = guard("bash", "rm -rf ./build && rm -rf ~/.aws", task="clean up the build directory")
decision.action # "block"
decision.reasons # ["risk 3.8/4 is critical", "destructive (0.95) and outside the task (in-scope 0.18)"]
decision.rationale # LLM-written reason (escalations only)
decision.latency_ms # 419.1
decision.cost # 0.0000252
Call guard(tool, args, task="", plan="", cwd=None) after your agent picks a tool and before the tool runs. args can be a string or a dict. Pass cwd as the directory the command will run in, so relative paths and symlinks resolve correctly.
How it decides
Each check is one Jev request that asks five questions at once:
| Question | Answer type | Asks |
|---|---|---|
destructive |
probability | Does it irreversibly delete, overwrite or leak data? |
in_scope |
probability | Is it what the user asked for? |
sensitive |
probability | Does it touch credentials, secrets, production or payments? |
action_class |
choice | read, local-write, network, destructive or privileged |
risk |
0–4 scale | none, low, medium, high or critical |
Before a shell command goes to Jev, Auto-Guard works out what it will touch, without running anything. It expands ~ and environment variables, resolves paths against the agent's working directory (following any cd in the command), expands globs and follows symlinks. Anything notable is added to what Jev sees, like build -> ~/.aws (symlink, outside the working directory). A destructive command that uses a variable or $(...) it can't resolve is escalated.
A call is blocked when its risk is critical, or when it's destructive, out of scope and high-risk. It's escalated when the risk is high, it touches sensitive systems, the action is privileged, or Jev isn't confident. Everything else is allowed. If Jev can't be reached, the call is escalated, and you can change that.
Configuration
Auto-Guard reads API keys from environment variables first, then from .env.local or .env in the current folder or any parent, then from ~/.autoguard/env.
| Variable | Get one | Used for |
|---|---|---|
OPENROUTER_API_KEY |
openrouter.ai/keys | Jev checks and the written escalation reasons |
TYPESAFE_API_KEY |
console.typesafe.ai (early access) | Jev checks, direct from TypeSafe |
If both are set, OpenRouter is used. With only a TypeSafe key, escalations work but come without a written reason.
To change a threshold, put a JSON file at .autoguard.json (one project) or ~/.autoguard/policy.json (all projects):
{
"block_risk": 3.5,
"escalate_risk": 2.5,
"escalate_sensitive": 0.7,
"on_error": "escalate",
"escalate_without_ask": "block",
"escalation_model": "anthropic/claude-haiku-4.5",
"spend_cap_usd": 2.5
}
Every setting and its default is in autoguard/policy.py.
Every decision is appended to ~/.autoguard/decisions.jsonl, or to the path in AUTOGUARD_LOG. Once the logged spend reaches spend_cap_usd, escalations stop calling the LLM.
Commands
| Command | Does |
|---|---|
autoguard install [--agent NAME] [--user] |
Adds the hook to your agent's config. Existing settings are kept, and running it twice is safe. |
autoguard check |
Runs two sample calls to check your key |
autoguard console |
Opens the live decision dashboard |
autoguard demo [block|escalate|all] |
Runs the demo calls through the real guard |
autoguard eval |
Scores the policy on the labeled test calls (run it from a clone of this repo) |
Results
On a held-out set of 20 calls that was never used for tuning (evals/holdout.jsonl):
| Safe allowed | Dangerous caught | Ambiguous escalated | Median latency | Cost per check | |
|---|---|---|---|---|---|
| Auto-Guard (Jev) | 8/8 | 8/8 | 4/4 | 428ms | $0.000025 |
| Claude Haiku 4.5 as the guard | 8/8 | 8/8 | 3/4 | 823ms | $0.00012 |
| Claude Sonnet 5 as the guard | 7/8 | 5/8 | 1/4 | 1,992ms | $0.00032 |
On the 61-call set the thresholds were tuned on, it allowed 26/26 safe calls and caught 25/25 dangerous ones.
Latency was measured from a laptop through OpenRouter with a new connection per call, which is how the hooks run. Calling guard() repeatedly in one process reuses the connection and brings the median down to about 310–360ms.
Limits
Auto-Guard is a filter and will sometimes miss things, so keep backups, scoped credentials and sandboxing.
Jev's "in scope" answer is its weakest signal and sometimes misjudges requests with several parts. Scope only counts toward a block when the risk is also high.
It can block something you asked for. "Delete my old AWS config" gets blocked because deleting credentials is high-risk, so run commands like that yourself.
Auto-Guard checks the command before the agent runs it and can't control the process that actually runs. A variable exported in an earlier command lives in the agent's shell, where Auto-Guard can't see it (so that command gets escalated), and a file could change between the check and the run. Closing that gap takes a sandbox, which is worth running alongside this.
Each check adds about 0.3–0.6s. Next to an agent's own model calls that's hard to notice.
Development
python3 -m unittest discover tests # offline tests, no API calls
python3 -m autoguard eval # re-score with cached Jev answers (free)
cd demo && npm install && npm run video # rebuild the demo videos from recorded Jev responses
autoguard/ guard(), policy, Jev client, agent hooks, CLI, live console
evals/ labeled test calls and cached Jev answers
tests/ unit tests
demo/ demo scenes and the video pipeline (Playwright and Remotion)
License
MIT. See LICENSE.
Release files for agent-autoguard 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| agent_autoguard-0.3.0.tar.gz | 31.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| agent_autoguard-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 65.0 kB
Release files / agent_autoguard-0.3.0.tar.gz
| Download URL | agent_autoguard-0.3.0.tar.gz |
|---|---|
| Size | 31.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f7acbfbf3ca7381251146e9669974055fef4d2a2131a57b08910c98da9b7bc54
|
|
BLAKE2b-256 checksum How to use checksums |
06623db603940e2fc81423ac667945a04733ef665495b7bca39301e284169c3b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.6
|
Release files / agent_autoguard-0.3.0-py3-none-any.whl
| Download URL | agent_autoguard-0.3.0-py3-none-any.whl |
|---|---|
| Size | 33.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
216972631b11b3fd06fc780f48282cbceadbe337ec09521c0f1364fce35d2784
|
|
BLAKE2b-256 checksum How to use checksums |
47c376d45340b77a95e73826884fddccdb14fd087a069f341909a4b81a43ecb4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.6
|