AgentGuard
Stop runaway agents with runtime checks in Python.

AgentGuard checks budgets, repeated tool calls, retries, and elapsed time in
instrumented Python code. Guards raise exceptions so your application can stop
the next operation. The base SDK has no runtime dependencies and needs no account.
Names: this repository is agent47, the PyPI package is agentguard47,
and the Python import is agentguard. Requires Python 3.9 or newer.
Getting started
Install in a virtual environment, then run the offline checks:
python -m pip install agentguard47
agentguard doctor
agentguard demo
doctor checks the installation and local trace writing. demo exercises
budget, loop, and retry stops without provider keys or network access. Follow
the trace path printed by the command to inspect its output.
agentguard demo --feedback prints a local redacted report; nothing is sent.
agentguard receipt agentguard_demo_traces.jsonl prints a receipt of each stop
with the trace's SHA-256 drawn as a barcode. Add --format markdown to paste it
into a PR or issue. The hash identifies the trace file; it is not a signature.
Guard a Claude Code session
agentguard hook claude-code --install --write
This installs a Claude Code hook that refuses the third identical tool call in
a row and a call that already failed twice. Refusals go to
.agentguard/claude-code/trace.jsonl. It checks tool calls, not tokens or
subscription quota. See the Claude Code hook guide.
Guard a script without editing it
agentguard run --budget-usd 5 agent.py
This patches the OpenAI and Anthropic clients, then runs agent.py in the same
interpreter. Settings come from flags, then environment variables, then
.agentguard.json. A guard stop exits 1. Every run ends with the trace path on
stderr, ready for agentguard receipt. agentguard run python -m mypkg works too. The bounds are
the same as patching the client yourself; see
enforcement boundary.
Stop before a third call
Save this as budget_demo.py and run python budget_demo.py. It makes no
network requests.
from agentguard import BudgetExceeded, BudgetGuard
budget = BudgetGuard(max_calls=2)
completed = 0
for _ in range(3):
try:
budget.check()
completed += 1
budget.consume(calls=1)
except BudgetExceeded:
print(f"Stopped before call {completed + 1}")
assert completed == 2
Expected output: Stopped before call 3.
Connect a provider
Install the provider's client separately. For OpenAI:
python -m pip install openai
from agentguard import BudgetGuard, JsonlFileSink, Tracer, patch_openai
budget = BudgetGuard(max_cost_usd=5.00)
tracer = Tracer(
service="my-agent",
sink=JsonlFileSink(".agentguard/traces.jsonl"),
)
patch_openai(tracer, budget_guard=budget)
The patch checks recorded usage before dispatch and records response usage
afterward, including streamed calls once the final usage arrives. A response
can exceed the remaining cost or token allowance. Concurrent requests do not
reserve capacity. Chat Completions streams request include_usage unless the
caller already set it.
The OpenAI Agents SDK runs on responses.create, so agentguard.init() before
the Runner puts every model call under the budget
(example). Hosted tools and
background=True responses are not covered. See the getting started guide
for setup, traces, and framework starters.
How enforcement works
flowchart TD
accTitle: AgentGuard operation checks
accDescr: Check a limit before an operation, then record usage.
A[Instrumented operation] --> B{Guard check}
B -->|Limit reached| C[Raise exception]
B -->|Allowed| D[Run operation]
D --> E[Record usage and trace]
E --> A
Text equivalent: check before an operation, run it if allowed, then record
usage. A guard exception returns control to your application's error handler.
| Guard | Checks | Raises |
|---|
BudgetGuard | Recorded calls, tokens, or estimated cost | BudgetExceeded |
LoopGuard | Repeated tool calls | LoopDetected |
FuzzyLoopGuard | Tool frequency and alternating patterns | LoopDetected |
RetryGuard | Retries per tool | RetryLimitExceeded |
TimeoutGuard | Elapsed time when checked | TimeoutExceeded |
RateLimitGuard | Calls within a sliding minute | BudgetExceeded |
X402SpendGuard | Payment amounts before the payment callback | BudgetExceeded |
For task budgets, use BudgetGuard.goal(...). For signatures and defaults,
read the guard source and
public exports.
Limits and security
- Guards cover operations you instrument. Installing the package does not
intercept every action in Cursor, Claude Code, or another agent.
- A guard is not a sandbox or permission system. A permitted operation can
still be destructive.
- Timeout checks do not interrupt an already blocked function or cancel an
agent running on a provider's server.
- Cost estimates are not invoices. Supply reported cost or use strict cost
resolution when an estimate is insufficient.
- Recorded-budget preflight refuses the next instrumented call when stored
usage is already at a cap. It does not reserve concurrent in-flight
requests, predict the next response, or cap a provider subscription.
See the enforcement boundary.
- The base SDK uses the standard library. Optional framework extras install
third-party dependencies and need their own security review.
- The optional
[crewai] extra pulls ChromaDB. The
2026-09-12 audit found four unresolved
advisories, including PYSEC-2026-311 / CVE-2026-45829.
Review that exposure before installing the extra. Base SDK installs do not
include ChromaDB.
- Trace content can contain application data. Review it before sharing or
configuring a remote sink.
See security reporting, the
dated dependency audit, and
release notes. Audit results describe their recorded date,
not a permanent clean bill of health.
Local traces and optional hosted ingest
The SDK is the free local proof path. Start local. Add hosted ingest only
when you need retained history, alerts, team visibility, spend trends,
hosted decision history, or dashboard-managed remote kill signals.
Local guards remain authoritative. HttpSink mirrors trace and decision events;
it does not execute remote kill signals by itself. See the
dashboard contract before configuring it.
Local use has no hosted event quota, retention period, or API-key allocation.
Network egress requires an integration you configure, such as HttpSink or
an OpenTelemetry exporter.
Nothing in the local SDK phones home. The
AgentGuard website
describes the optional hosted service.
Documentation
Help and maintenance
Maintained by Patrick Hughes.
Report a bug with the package
version, a minimal reproduction, and the expected result. Report vulnerabilities
through SECURITY.md.
The source metadata defines the branch version. The PyPI badge links to the
published version. Documentation examples and local links are tested in CI.
The PyPI README is generated from this README and the changelog.
MIT license.