Metadata-Version: 2.5
Name: agentgovern
Version: 2.7.2
Summary: Compliance-as-code middleware for agentic AI workflows.
Project-URL: Homepage, https://agentgovern.zirahn.com
Project-URL: Support, https://zirahn.com/contact
Author-email: Ahmed Khan <ahmed.khan@zirahn.com>
License: MIT
License-File: LICENSE
Keywords: agent-governance,agents,ai,compliance,eu-ai-act,governance,langchain,nist
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Monitoring
Requires-Python: >=3.10
Requires-Dist: httpx>=0.27.0
Requires-Dist: pydantic>=2.10.0
Provides-Extra: crewai
Requires-Dist: crewai>=0.80.0; extra == 'crewai'
Provides-Extra: dev
Requires-Dist: langchain>=1.0.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24.0; extra == 'dev'
Requires-Dist: pytest-cov>=6.0.0; extra == 'dev'
Requires-Dist: pytest>=8.3.0; extra == 'dev'
Requires-Dist: respx>=0.21.0; extra == 'dev'
Requires-Dist: ruff>=0.8.0; extra == 'dev'
Provides-Extra: langchain
Requires-Dist: langchain-core>=0.3.0; extra == 'langchain'
Provides-Extra: langgraph
Requires-Dist: langchain-core>=0.3.0; extra == 'langgraph'
Requires-Dist: langgraph<2.0.0,>=0.2.0; extra == 'langgraph'
Provides-Extra: openai
Requires-Dist: openai>=1.50.0; extra == 'openai'
Provides-Extra: sdk
Description-Content-Type: text/markdown

# AgentGovern Python SDK

**Compliance-as-code for agentic AI workflows.**

> **Beta** — API may change before 1.0. Report issues through [zirahn.com/contact](https://zirahn.com/contact).

AgentGovern intercepts AI agent actions, evaluates them against configurable compliance policies (the EU AI Act today; NIST AI RMF and ISO 42001 are on the roadmap — see the table below), and generates audit-ready evidence in near real-time. Tracking runs in a background thread and fails open. An `enforce`-mode block raises in your code (see *Enforcement modes* below for exactly what it stops). *(Corrected again 2026-10-03: the first correction said "the SDK blocks only if you enable Gate 2 `enforce` mode, where a block verdict raises `OutputBlocked`", which reads as if enforce mode halts an agent; through the integrations it does not — NAGT-380.)* This SDK auto-instruments LangChain and LangGraph; any other framework (CrewAI, OpenAI Agents and the rest) can report actions with `track_action()`. *(Corrected 2026-10-03: this said NIST AI RMF and ISO 42001 were supported, that the SDK never blocks, and that it instruments CrewAI and OpenAI Agents code.)*

## Install

Needs **Python 3.10 or later**. The macOS system Python is 3.9, which is too old: install a newer one first, from python.org or with Homebrew (`brew install python@3.12`). On 3.9 this command fails with "No matching distribution found" rather than installing an old release.

```bash
python3 -m pip install "agentgovern[langchain]>=2.7.0"
```

## Quickstart — LangChain

```python
import agentgovern
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI

# 1. Initialize once at startup
agentgovern.init(
    api_key="ag_prod_...",           # from https://agentgovern.zirahn.com/settings/api-keys
    base_url="https://agentgovern.zirahn.com",
    environment="development",       # "production" | "staging" | "development"
)

# 2. Register your agent
agentgovern.register_agent(
    external_id="credit-scoring-v2",
    name="Credit Scoring Agent v2",
    framework="langchain",
)

# 3. Get the callback handler — binds to credit-scoring-v2 automatically
handler = agentgovern.instrument_langchain()

# 4. Build the agent and pass the handler via config — no other changes needed
agent = create_agent(model=ChatOpenAI(model="gpt-4o"), tools=tools)

result = agent.invoke(
    {"messages": [{"role": "user", "content": "Evaluate loan application for customer #12345"}]},
    config={"callbacks": [handler]},
)
```

`instrument_langchain()` binds to the most recently registered agent. Every tool call,
LLM invocation, and agent step is captured, evaluated against your compliance policies,
and visible in the dashboard.

## Enforcement modes

AgentGovern supports four enforcement modes per policy:

| Mode     | Behavior                                                   |
|----------|------------------------------------------------------------|
| warn     | Records the violation; the agent continues                 |
| log      | Records the violation silently; the agent continues        |
| disabled | Policy not evaluated                                       |
| enforce  | Records the violation and **raises** in your code (below)  |

### What enforce stops, through the LangChain and LangGraph integrations

When an enforce-mode policy blocks, the exception reaches the code that called
`invoke()`, `ainvoke()`, `stream()` or `astream()`:

- **Gate 1 (input) raises `PolicyViolation` before the model call runs.** This is the
  control that stops an agent before it acts.
- **Gate 2 (output) raises `OutputBlocked` (block) or `OutputEscalated` (escalate)**
  when the run finishes. Gate 2 stops `invoke()` from **returning** a blocked answer.
  **It does not undo anything that already happened:** tool calls have already run, a
  LangGraph checkpointer has already saved the final state, and a `stream()` caller has
  already received the streamed output. On LangChain, a run whose LLM streamed tokens skips the synchronous Gate 2 entirely.
- **Redact** replaces the answer on LangChain; LangGraph does not apply it.

**What never stops your agent:** warn, log and allow verdicts; AgentGovern being
unreachable, slow, or returning an error (unless you configured `fail_open=False`, in
which case Gate 1 raises `GateUnavailable`, or `gate_2_failure_mode="fail_closed"`, in
which case Gate 2 raises `OutputBlocked`); and any error inside the SDK itself.

**Pass AgentGovern's handler last.** When an enforce block raises, LangChain can stop
delivering that event to the handlers after ours in the `callbacks` list (on synchronous
runs it always does). A tracing or logging handler placed after AgentGovern's might not
see the event that was blocked.
Put AgentGovern's handler at the end of the list:
`config={"callbacks": [your_tracer, agentgovern_handler]}`.

**How:** an instrumentor sets LangChain's `raise_error` when an enforce gate is
configured (Gate 2 `enforce`, or Gate 1 auto-evaluation on), and lets only the three
verdict exceptions above out of its callbacks.

*(Changed in SDK 2.6.0, NAGT-380. Earlier releases recorded enforce-mode violations
but did not stop the agent: LangChain caught the exception.)*

You can also check input yourself before invoking an agent:

```python
result = agentgovern.evaluate_input(agent_external_id="my-agent", prompt=user_prompt)
if result.action_taken == "block":
    raise HTTPException(status_code=403, detail="Prompt blocked by compliance policy")
agent.invoke({"input": user_prompt})
```

## Multiple agents in one process

If you run more than one agent in the same process, pass the agent ID explicitly to
avoid ambiguity:

```python
agentgovern.register_agent("fraud-detector", name="Fraud Detector")
agentgovern.register_agent("kyc-agent", name="KYC Agent")

handler_fraud = agentgovern.instrument_langchain("fraud-detector")
handler_kyc   = agentgovern.instrument_langchain("kyc-agent")

fraud_agent = create_agent(model=..., tools=...)
kyc_agent   = create_agent(model=..., tools=...)

fraud_agent.invoke(inputs, config={"callbacks": [handler_fraud]})
kyc_agent.invoke(inputs, config={"callbacks": [handler_kyc]})
```

## Manual instrumentation (all frameworks)

```python
from agentgovern.types import ActionType, ActionStatus

agentgovern.track_action(
    agent_external_id="my-agent-id",
    action_type=ActionType.TOOL_CALL,
    action_name="fetch_credit_bureau_data",
    status=ActionStatus.COMPLETED,
    duration_ms=312,
    input_payload={"bureau": "experian", "customer_id": "..."},
    output_payload={"fico_score": 720},
)
```

## Supported frameworks

| Framework | Auto-instrumentation | Status |
|-----------|---------------------|--------|
| LangChain | `instrument_langchain()` — wraps tool and LLM callbacks | Stable |
| CrewAI | Manual via `track_action()` | Beta |
| OpenAI Agents API | Manual via `track_action()` | Beta |

Auto-instrumentation for CrewAI and OpenAI Agents is on the roadmap.

## Compliance frameworks

| Framework | Status |
|-----------|--------|
| EU AI Act (High-Risk Systems) | Available |
| NIST AI RMF | Coming soon |
| ISO 42001 | Coming soon |

Enable policy packs from the [AgentGovern dashboard](https://agentgovern.zirahn.com).

## Configuration

| Parameter | Default | Description |
|-----------|---------|-------------|
| `api_key` | required | SDK ingest key from the dashboard |
| `base_url` | `https://agentgovern.zirahn.com` | API endpoint |
| `environment` | `"production"` | `"production"` \| `"staging"` \| `"development"` |
| `policy_engine_url` | `https://engine.zirahn.com` | Policy engine endpoint for Gate 1 (`evaluate_input`) and Gate 2 checks. Override with this argument or the `AGENTGOVERN_POLICY_ENGINE_URL` env var |
| `fail_silently` | `True` | If `True`, SDK errors never raise into your agent |

**Policy engine address (2.4.0).** The default engine address is now the stable name
`https://engine.zirahn.com`. Versions 2.3.0 and earlier call `https://agentgovern.onrender.com`
directly. That address keeps working, so nothing breaks if you stay on an older version, but only
the stable name can follow the engine if it moves. Upgrade with `python3 -m pip install --upgrade "agentgovern>=2.7.0"`.
If you set `policy_engine_url` or `AGENTGOVERN_POLICY_ENGINE_URL` yourself, it still takes precedence.

## Claude Code Hook

The SDK ships a built-in CLI for installing the AgentGovern hook into Claude Code. The hook
checks every tool call against your tenant's policies before the tool runs. A policy in
**enforce** mode blocks the call and Claude Code shows the reason; **warn** lets it run and
shows a warning; **log** only records it. A new organization starts with no policy in enforce
mode. A tool AgentGovern doesn't yet have a specific classification for is still captured and
evaluated, tagged `action_type=unmapped_tool` rather than silently skipped.

### Install

**The simplest way is the dashboard:** on **Settings → Developers**, **Add Developer** gives an
install snippet for bash and PowerShell that checks for Python 3.10 or later, installs this
package into its own virtual environment, saves your two settings for new terminals, and runs
the command below. To do it by hand:

```bash
agentgovern hook install
```

This deploys the hook script to `~/.agentgovern/hook.py`, merges the correct entry into
`~/.claude/settings.json` (with an atomic backup), and prints instructions for any missing
env vars.

### Upgrading an existing install

Upgrading the package does **not** update a hook that is already installed. `hook install`
copies the hook script to `~/.agentgovern/hook.py` and writes its entries into
`~/.claude/settings.json`, and a plain `agentgovern hook install` against an existing install
reports it as already installed and changes nothing. Upgrade, read what is installed now, then
reinstall with `--force`:

```bash
python3 -m pip install --upgrade "agentgovern>=2.7.0"   # or the extra you installed with, e.g. "agentgovern[langchain]>=2.7.0"
agentgovern hook status                        # note each line's cmd= value and the "Install mode" line
agentgovern hook install --force               # plus the flags below, if status calls for them
agentgovern hook status                        # reports no [DRIFT] lines once the hook is current
```

**`--force` writes the current defaults. It does not remember options from the earlier install**,
so repeat what `hook status` showed:

- **`cmd=` is anything other than plain `python3`** (for example `cmd=/opt/local/bin/python3.12`):
  add `--python-command <that exact value>`. Without it, `--force` resets the hook to `python3`.
- **`Install mode: minimal`:** add `--capture-mode=minimal`. Without it, `--force` turns a minimal
  install into a full one.
- **You once passed `--matcher`:** pass it again.

Your environment variables are not touched. Restart any running Claude Code session afterwards.

**If a command isn't found:**

- **`pip: command not found`:** use `python3 -m pip`, as above.
- **`agentgovern: command not found`** (the command was installed somewhere that isn't on your
  `PATH`), or pip refuses with `externally-managed-environment`: install the CLI into its own
  virtual environment and call it by its full path:

  ```bash
  python3 -m venv ~/.agentgovern-venv
  ~/.agentgovern-venv/bin/python -m pip install --upgrade "agentgovern>=2.7.0"
  ~/.agentgovern-venv/bin/agentgovern hook status
  ~/.agentgovern-venv/bin/agentgovern hook install --force   # plus the flags status called for
  ```

  **The venv holds only the CLI.** The hook itself runs with the interpreter recorded in
  `~/.claude/settings.json` (the `cmd=` value in `hook status`; `python3` unless you chose
  otherwise). That interpreter needs no packages, because the hook script uses only the standard
  library, and **installing from a venv does not change it** unless you pass `--python-command`.

**If your hook was installed by 2.2.1 or earlier, this step matters for coverage, not just
freshness.** Those versions wrote a `PreToolUse` matcher naming a fixed list of tools (ten, in 2.2.1), so a call
through any other tool (`PowerShell`, for example) never reached the hook. Starting with 2.3.0
the matcher is `*`, every tool — but only once `hook install --force` has rewritten the entry.

### Required env vars

```bash
export AGENTGOVERN_API_KEY="ag_prod_..."           # Settings → API Keys
export AGENTGOVERN_ACTOR_USER_ID="<your-actor-id>"  # Settings → Developers: your install snippet, or "Your actor ID" (see the hook README)
```

*(Corrected 2026-10-05, NAGT-379: the comment above named only the install snippet. Settings → Developers also shows the signed-in user's own **Your actor ID**, for running the hook yourself, and is shown when the workspace's workflow is Guard or Mixed.)*

### Subcommands

| Command | Description |
|---|---|
| `agentgovern hook install` | Install the hook (flags: `--dry-run`, `--force`, `--matcher`, `--python-command`) |
| `agentgovern hook uninstall` | Remove the hook entry from `settings.json` (script file preserved) |
| `agentgovern hook status` | Show installation state, script path, and env var status |
| `agentgovern hook test` | Fire a synthetic event to verify end-to-end connectivity |

### Bypass

To skip governance for a session without removing the hook:

```bash
AGENTGOVERN_HOOK_MODE=off claude
```

In PowerShell (the variable is removed again when claude exits):

```powershell
try { $env:AGENTGOVERN_HOOK_MODE='off'; claude } finally { Remove-Item Env:AGENTGOVERN_HOOK_MODE }
```

`agentgovern hook install --help` lists the install options, and `agentgovern hook status` shows what is installed. The environment variables are in *Required env vars* and *Bypass* above.

## Design guarantees

- `track_action()` does not wait on the network — all I/O happens in a background thread. *(Corrected 2026-10-03: this said "returns in < 5 ms", a figure no benchmark in this repository measures.)*
- Buffer cap: 10,000 actions; oldest dropped when full
- Retry: 3 attempts with exponential backoff (1 s → 30 s max)
- If AgentGovern is unreachable, your agent continues unaffected

## Links

- **Dashboard:** https://agentgovern.zirahn.com
- **Documentation:** this README
- **Support and issues:** https://zirahn.com/contact

## License

MIT — Copyright (c) 2026 Zirahn
