Metadata-Version: 2.4
Name: agentguard47
Version: 2.0.0
Summary: Zero-dependency runtime control for production Python agents - stop loops, retry storms, and budget burn
Author-email: BMD PAT LLC <pat@bmdpat.com>
License-Expression: MIT
Project-URL: Homepage, https://agentguard47.com
Project-URL: Documentation, https://github.com/bmdhodl/agent47#readme
Project-URL: Repository, https://github.com/bmdhodl/agent47
Project-URL: Issues, https://github.com/bmdhodl/agent47/issues
Project-URL: Changelog, https://github.com/bmdhodl/agent47/releases
Keywords: agents,coding-agents,ai-agents,multi-agent,llm,guardrails,runtime-guardrails,loop-detection,budget-guard,retry-guard,runtime-enforcement,runtime-control,production-agents,coding-agent-safety,local-first,retry-storms,budget-control,langchain,openai,anthropic
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Software Development :: Testing
Classifier: Topic :: System :: Monitoring
Classifier: Typing :: Typed
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: langchain
Requires-Dist: langchain-core>=1.6.3; extra == "langchain"
Provides-Extra: langgraph
Requires-Dist: langgraph>=1.2.11; extra == "langgraph"
Requires-Dist: langgraph-checkpoint>=4.2.0; extra == "langgraph"
Requires-Dist: langgraph-sdk>=0.4.4; extra == "langgraph"
Provides-Extra: crewai
Requires-Dist: crewai>=1.15.21; extra == "crewai"
Provides-Extra: otel
Requires-Dist: opentelemetry-api>=1.44.0; extra == "otel"
Requires-Dist: opentelemetry-sdk>=1.44.0; extra == "otel"
Dynamic: license-file

<!-- Generated by scripts/generate_pypi_readme.py. Edit README.md and CHANGELOG.md instead. -->

# AgentGuard

Stop runaway agents with runtime checks in Python.

[![PyPI version](https://img.shields.io/pypi/v/agentguard47)](https://pypi.org/project/agentguard47/)
[![Python versions](https://img.shields.io/pypi/pyversions/agentguard47)](https://pypi.org/project/agentguard47/)
[![CI](https://github.com/bmdhodl/agent47/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/bmdhodl/agent47/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/github/license/bmdhodl/agent47)](https://github.com/bmdhodl/agent47/blob/v2.0.0/LICENSE)

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`. AgentGuard 2.0.0 requires Python 3.11 or newer.
On Python 3.9 or 3.10, pin `agentguard47==1.4.0`. See the
[Python migration guide](https://github.com/bmdhodl/agent47/blob/v2.0.0/docs/guides/python-311-migration.md) before upgrading.

## Getting started

Install in a virtual environment, then run the offline checks:

```bash
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.

For a fresh PowerShell install and a runnable raw starter, follow the
[published-release walkthrough](https://github.com/bmdhodl/agent47/blob/main/docs/guides/try-release.md#windows-powershell).

### Share a stop receipt (new in 2.0.0)

`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 (new in 2.0.0)

```bash
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](https://github.com/bmdhodl/agent47/blob/main/docs/guides/claude-code-hook.md).

### Guard a script without editing it (new in 2.0.0)

```bash
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](https://github.com/bmdhodl/agent47/blob/main/docs/enforcement-boundary.md).

### Stop before a third call

Save this as `budget_demo.py` and run `python budget_demo.py`. It makes no
network requests.

```python
from agentguard import BudgetExceeded, BudgetGuard

budget = BudgetGuard(max_calls=2)
completed = 0

for _ in range(3):
    try:
        budget.check()  # Check before the operation.
        # Put your provider or tool call here.
        completed += 1
        budget.consume(calls=1)  # Record the completed operation.
    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:

```bash
python -m pip install openai
```

```python
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)
# Make your OpenAI chat.completions.create or responses.create calls after this setup.
```

Standard OpenAI clients created earlier are covered too, including resource
objects obtained before the patch. Activate the patch before sending requests
or saving bound methods and raw/streaming response helpers. Those saved
references can retain the original callable; recreate them after activation.
`patch_openai_async()` covers async clients, and `agentguard.init()` enables
both. See the [enforcement boundary](https://github.com/bmdhodl/agent47/blob/main/docs/enforcement-boundary.md).

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](https://github.com/bmdhodl/agent47/blob/v2.0.0/examples/openai_agents_sdk_budget.py)). Hosted tools and
`background=True` responses are not covered. See the [getting started guide](https://github.com/bmdhodl/agent47/blob/main/docs/guides/getting-started.md)
for setup, traces, and framework starters.

## How enforcement works

```mermaid
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](https://github.com/bmdhodl/agent47/blob/v2.0.0/sdk/agentguard/guards.py) and
[public exports](https://github.com/bmdhodl/agent47/blob/v2.0.0/sdk/agentguard/__init__.py).

## 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](https://github.com/bmdhodl/agent47/blob/main/docs/enforcement-boundary.md).
- 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](https://github.com/bmdhodl/agent47/blob/v2.0.0/proof/audit-20260912/README.md) found four unresolved
  advisories, including [PYSEC-2026-311 / CVE-2026-45829](https://github.com/advisories/GHSA-f4j7-r4q5-qw2c).
  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](https://github.com/bmdhodl/agent47/blob/v2.0.0/SECURITY.md), the
[dated dependency audit](https://github.com/bmdhodl/agent47/blob/v2.0.0/proof/audit-20260912/README.md), and
[release notes](https://github.com/bmdhodl/agent47/blob/v2.0.0/CHANGELOG.md). 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](https://github.com/bmdhodl/agent47/blob/main/docs/guides/dashboard-contract.md) 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](https://bmdpat.com/tools/agentguard?utm_source=agentguard47&utm_medium=readme&utm_campaign=touchpoints)
describes the optional hosted service.

## Documentation

| You want to | Start here |
| --- | --- |
| See which paths actually stop a call | [Enforcement boundary](https://github.com/bmdhodl/agent47/blob/main/docs/enforcement-boundary.md) |
| Install and trace a first run | [Getting started](https://github.com/bmdhodl/agent47/blob/main/docs/guides/getting-started.md) |
| Find guides and source references | [Documentation index](https://github.com/bmdhodl/agent47/blob/v2.0.0/docs/README.md) |
| Try a runnable example | [Examples](https://github.com/bmdhodl/agent47/tree/v2.0.0/examples) |
| Connect LangChain, LangGraph, or CrewAI | [Integration guides](https://github.com/bmdhodl/agent47/tree/v2.0.0/docs/integrations) |
| Inspect hosted data through MCP | [Read-only TypeScript MCP server](https://github.com/bmdhodl/agent47/tree/v2.0.0/mcp-server) |
| Use local budget tools through MCP | [Python budget MCP server](https://github.com/bmdhodl/agent47/tree/v2.0.0/agentguard-mcp) |
| Navigate with an AI assistant | [AI documentation index](https://github.com/bmdhodl/agent47/blob/main/llms.txt) |
| Contribute a fix | [Contributing](https://github.com/bmdhodl/agent47/blob/v2.0.0/CONTRIBUTING.md) |
| Check what changed | [Changelog](https://github.com/bmdhodl/agent47/blob/v2.0.0/CHANGELOG.md) |

## Help and maintenance

Maintained by [Patrick Hughes](https://github.com/bmdhodl).
[Report a bug](https://github.com/bmdhodl/agent47/issues) with the package
version, a minimal reproduction, and the expected result. Report vulnerabilities
through [SECURITY.md](https://github.com/bmdhodl/agent47/blob/v2.0.0/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](https://github.com/bmdhodl/agent47/blob/v2.0.0/LICENSE).

## Latest Release Notes (2.0.0)

This major release replaces the planned 1.4.1 patch.

### Breaking
- The SDK requires Python 3.11 or newer. Python 3.9 and 3.10 are retired.
  Recreate your virtual environment with Python 3.11+ before upgrading, or pin
  `agentguard47==1.4.0` on an older interpreter. See the
  [migration guide](https://github.com/bmdhodl/agent47/blob/v2.0.0/docs/guides/python-311-migration.md).
- CI tests Python 3.11 and 3.12 and uses patched pytest 9.0.3. The CI-tools
  lock is generated with its configured Python 3.11 interpreter. Other tool
  versions and hashes remain unchanged.


### Added
- `agentguard receipt <trace.jsonl>` prints each guard stop, the recorded
  cost, and the trace's SHA-256 as a barcode. `--format markdown` wraps it for
  PRs and issues; `--format json` is for CI. The receipt counts cost the same
  way as `report`, so the call that tripped a budget is counted once.

- `agentguard hook claude-code` is a Claude Code hook. It refuses the third
  identical tool call in a row, a call that already failed twice, and, with
  `--max-calls`, calls past a per-session cap. `--install --write` adds it to
  `.claude/settings.local.json` and keeps existing hooks; `--uninstall` removes
  only its own. Refusals are logged for `agentguard receipt`. See
  [the guide](https://github.com/bmdhodl/agent47/blob/main/docs/guides/claude-code-hook.md).

- `agentguard run [--budget-usd N] agent.py` runs an unmodified script with the
  OpenAI and Anthropic clients patched. Flags, environment variables, and
  `.agentguard.json` set the limits. A guard stop exits 1. Every run ends by
  printing the trace path to stderr for `agentguard receipt`.

- `patch_openai` and `patch_openai_async` (and so `agentguard.init()` and
  `agentguard run`) now cover the OpenAI Responses API: `responses.create`,
  `responses.parse`, `responses.stream()`, and the raw and streaming response
  wrappers. That puts the OpenAI Agents SDK under the budget: `Runner.run` and
  `Runner.run_streamed` stop before the next model call once the budget is
  spent. Store-backed guards reserve on `max_output_tokens`. Hosted tools,
  `background=True`, and the WebSocket transport are not covered; see the
  [enforcement boundary](https://github.com/bmdhodl/agent47/blob/main/docs/enforcement-boundary.md) and
  `examples/openai_agents_sdk_budget.py`. Both are Supported in the
  [compatibility matrix](https://github.com/bmdhodl/agent47/blob/v2.0.0/docs/compatibility.md): a separate Responses/Agents
  floor job runs OpenAI 1.66.3 / Agents 0.0.3 without skips. Chat Completions
  retains its separate OpenAI 1.40.0 floor.

### Fixes
- `report`, `incident`, and `summarize_trace()` now calculate elapsed time
  across the recorded span timeline instead of labelling the longest single
  span as the whole run. Sequential gaps are included; overlapping or nested
  spans are not summed. Duration-only legacy traces keep the longest-span
  fallback. `EvalSuite.assert_completes_within()` keeps its existing contract.

- Native Ollama `/api/chat` and `/api/generate` responses now contribute
  `prompt_eval_count` and `eval_count` to `resolve_billable_cost()` and
  `consume_billable()`, including response objects with attributes. Cached
  prompt tokens are retained without adding them to the total twice. Token
  budgets previously stayed at zero. This is manual accounting, not an Ollama
  client patch; see the [cost guide](https://github.com/bmdhodl/agent47/blob/main/docs/cost-guardrails.md).

- OpenAI clients created before `patch_openai()`, `patch_openai_async()`, or
  `init()` now trace and enforce their budget through shared resource methods.
  Previously these calls silently bypassed both. Methods and raw/streaming
  helpers saved before activation still need to be recreated; see the
  [enforcement boundary](https://github.com/bmdhodl/agent47/blob/main/docs/enforcement-boundary.md).

- One price table. `estimate_cost` and the patched clients now read the same
  rows. Before, the patched clients billed a `gpt-5.5` prompt over 272k
  tokens at half its price, and priced every Claude 5 model, `claude-haiku-4-5`,
  and the real Claude Opus 4 id (`claude-opus-4-20250514`) as unknown, 8x to
  137x too high. Claude, OpenAI, and Gemini rows match each provider's
  pricing page as of 2026-09-26. Cached-input rates for gpt-5.5, gpt-5.4,
  and Gemini 2.5 were 2x to 5x too high. GPT-6, GPT-5.6, GPT-5.2, gpt-5,
  gpt-4.1, o3, o4-mini, o1-pro, and Gemini 3.x were priced as unknown; they
  now have rows. Gemini 3.6, 3.7, and 3.8 Flash double on 2027-01-01 as
  Google has published. Retired Gemini 1.5 and 2.0 rows are removed.
- Gemini thinking tokens were not billed, and Gemini cache reads were billed at
  the full input rate. `thoughts_token_count` now bills as output, and cached
  tokens are taken out of `prompt_token_count`, which includes them.
- Dated model ids price as their base model. An unknown Anthropic or OpenAI
  model is priced at that provider's highest listed rates instead of a flat
  $150 per million tokens. For OpenAI that is o1-pro, $150 in and $600 out
  per million, so an output-heavy call to an unknown OpenAI model can trip a
  dollar budget sooner than in 1.4.0. `sdk_release_guard.py --check-price-table-age`,
  run by the publish workflow, fails a release when any provider's prices are
  more than 90 days old.
- Usage that reports only `total_tokens` was priced at $0 for a known model.
  It is now priced at the model's output rate, or the high-water rate for an
  unknown model.
- The LangChain callback priced `on_llm_end` with `estimate_cost`, which
  returns $0 for an unknown model and ignores cache tokens, so a dollar budget
  never tripped on an unknown model. It now uses the same resolver as the
  patched clients: unknown models are overestimated and Anthropic cache reads
  are billed. `llm.end` events carry `source_of_cost`.
- Every `AsyncOpenAI` and `AsyncAnthropic` call failed with `AttributeError`
  after `agentguard.init()`, because the async patches expected an
  `AsyncTracer`. They now accept the `Tracer` that `init()` creates.
- `agentguard --version` prints the installed version and exits 0. In 1.4.0
  it exited 2, often on the first command after install.
- `agentguard report`, `summarize_trace`, `incident`, and
  `EvalSuite.assert_cost_under` no longer count the call that tripped a
  budget twice. `guard.budget_exceeded` echoes that call's cost, which its
  `llm.result` already carries. Savings baselines skip guard events too.
- Reasoning and thinking tokens were billed twice: once inside output tokens,
  where OpenAI and Anthropic already count them, and again on top. That
  overstated cost for o-series, gpt-5, and extended-thinking calls and could
  stop a dollar budget early. They now bill once, at the output rate. A price
  row with `reasoning_per_1m` reprices only that slice.

### Docs
- The PyPI README again states that the optional `[crewai]` extra pulls
  ChromaDB with unresolved advisories, including PYSEC-2026-311 /
  CVE-2026-45829. Base installs do not include ChromaDB.
- CONTRIBUTING shows how to add a provider usage fixture in one session.

### Release checks
- Every stable publish now runs the exact PyPI wheel offline on Windows,
  macOS, and Linux. No SDK runtime behavior changed.
- New [compatibility matrix](https://github.com/bmdhodl/agent47/blob/v2.0.0/docs/compatibility.md). CI now runs the full
  suite against the real OpenAI, Anthropic, LangChain, LangGraph, and
  OpenTelemetry packages at the oldest supported versions and at current
  releases. Missing packages fail the job instead of skipping. CrewAI stays
  experimental (#644). The current-release job also runs the OpenAI Agents
  SDK.

Full changelog: [CHANGELOG.md](https://github.com/bmdhodl/agent47/blob/v2.0.0/CHANGELOG.md)
