Metadata-Version: 2.4
Name: agentx-security-sdk
Version: 0.5.1
Summary: Runtime firewall for AI agents - blocks catastrophic tool calls and self-heals the run.
Home-page: https://agentx-core.com
Author: AgentX Core Team
Author-email: founders@agentx-core.com
License: MIT
Project-URL: Homepage, https://agentx-core.com
Project-URL: Get Started, https://agentx-core.com/gateway
Project-URL: Source, https://github.com/vdalal/agentx-security-sdk
Keywords: ai-agents,agent-security,llm-security,ai-firewall,prompt-injection,guardrails,llm-guardrails,agent-guardrails,tool-use,autonomous-agents,mcp,ai-safety,self-healing
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Information Technology
Classifier: Topic :: Security
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Software Development :: Quality Assurance
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: agentx_sdk/LICENSE
Requires-Dist: requests>=2.25.0
Dynamic: author
Dynamic: author-email
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: keywords
Dynamic: license
Dynamic: license-file
Dynamic: project-url
Dynamic: requires-dist
Dynamic: requires-python
Dynamic: summary

# 🛡️ AgentX: The Action Firewall for AI Agents

[![PyPI](https://img.shields.io/pypi/v/agentx-security-sdk.svg)](https://pypi.org/project/agentx-security-sdk/)
[![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://github.com/vdalal/agentx-security-sdk/blob/main/LICENSE)

*This package (`agentx-security-sdk`) is MIT licensed. The gateway and control plane are separate, closed-source products; see [Licensing](#-licensing) below.*

An agent that can act will eventually act badly: a `DROP TABLE` arriving through a prompt-injected field, a secret read, an SSRF to a cloud metadata endpoint, an installer piped straight into a shell. Most agent frameworks already give you a place to stop a tool call before it runs. What they don't give you is the decision about **which** calls are worth stopping.

**AgentX is that decision, and it starts with zero keys.** A deterministic floor runs inside your process, with no LLM call and no network hop. It knows the never-legitimate call (`DROP TABLE`, SSRF, secret reads, supply-chain RCE, destructive shell and cloud teardown) from the one that can be legitimate and needs a human (large transfers, external publishes, runaway spend, bulk deletes).

**Out of the box it watches.** Every call your wrapped tools make is screened and written down, and **nothing is stopped**, so adding AgentX cannot break an agent that already works. When the catches look right, `AGENTX_POSTURE=enforce` makes the same floor hard-block the first list and escalate the second. No API key, no signup. The only outbound call it ever makes is the dependency-reputation check against the public npm and PyPI registries.

A block is not a dead end. Every block hands your agent a **coaching message** naming a safe path to try instead, so the run continues rather than ending. Add a Gemini key and the gateway's **reasoning layer** writes that message to fit the specific task rather than from a template, catches what a keyword floor cannot see, and runs the retry for you.

### 🛡️ Block the catastrophic deterministically. 🧠 Coach the recoverable when you add a key.

---

## ⚡ 1. Quickstart

AgentX needs **no changes** to your agent logic, your tools, or your payload schemas. It reads your function signatures at runtime and works out what to inspect on its own.

### Step 1: Install the SDK
```bash
pip install agentx-security-sdk
```

### Step 1b: See it work in 10 seconds (no key, no gateway)
```bash
agentx demo
```
Runs a canned agent twice against the in-process floor, offline and with no key: first in the default watching posture, where the `DROP TABLE` is recorded and still runs, then enforcing, where it is stopped. The fastest way to confirm the install works, and to see both postures, before you wire it into your own agent. (`agentx demo --audit` and `agentx demo --enforce` run one half each.)

Hit a block you're proud of? Turn it into a postable card:
```bash
agentx share
```
Renders your most recent catch as a clean, screenshot-able receipt + a ready-to-post draft and link. Privacy-safe by construction: it uses the policy class and your own tool name only, never the query or payload (the local ledger never stores one).

### Step 2: Decorate Sensitive Tool Operations
Attach the `@agentx_protect` decorator over any high-risk system tool. The SDK automatically serializes parameters and enforces the evaluation wedge:

```python
# ✅ MODERN REFLECTIVE IMPORTS (No boilerplate functions required)
from agentx_sdk.decorators import agentx_protect

@agentx_protect(agent_id="demo_frictionless_agent")
def dispatch_crm_update(client_id: str, profile_notes: str, db_session=None):
    """
    AgentX automatically inspects string elements, ignores connection objects
    like 'db_session', and evaluates intents out-of-prompt natively in RAM.
    """
    print(f"Updating records for {client_id}")
```

**Trying it on a live agent?** Out of the box a keyless install watches: calls are screened and
recorded, none are blocked. Set `AGENTX_API_KEY`, or `AGENTX_POSTURE=enforce`, and the same floor
blocks. To hard-block one dangerous tool while the rest keep watching, pin it:

```python
@agentx_protect(agent_id="demo_frictionless_agent", posture="enforce")
def dispatch_crm_update(client_id: str, profile_notes: str, db_session=None):
    ...
```

Run `agentx audit` to see what your tools have really been doing, and set `AGENTX_POSTURE=enforce`
on a keyless run when you want every catch blocked. A per-tool `posture=` beats the variable, in
either direction.

---

### Step 2b: Handle the Block

Decorating is half the job. Your code also has to *react* when AgentX blocks a call. You never parse the message text. Use `is_block()` and read the structured fields:

```python
from agentx_sdk import agentx_protect, is_block

result = dispatch_crm_update(client_id="CLI-99401", profile_notes=untrusted)

if is_block(result):
    print(f"Blocked by policy: {result.policy}")
    llm.send(result.challenge)        # feed the safe-path challenge back to your agent to self-correct
else:
    use(result)                        # not blocked — the real return value
```

For **strictly-typed tools** (e.g. LangChain / Pydantic tools that validate a `-> dict` return), AgentX raises instead of returning, so the framework doesn't crash. Catch it and feed the same challenge back:

```python
from agentx_sdk import AgentXSecurityBlock

try:
    data = fetch_user(uid)             # -> dict
except AgentXSecurityBlock as block:
    llm.send(block.challenge)
```

> A **circuit-breaker trip** (runaway loop) is *not* a policy block. It raises `AgentXCircuitBreakerTripped` and `is_block()` returns `False` for it. Catch it separately to abort the run.

---

### Step 2c: Or protect an MCP server with zero code

Don't own the tool's Python? Running a non-Python agent? Wrap any **MCP server** with `agentx-mcp` and every `tools/call` is screened by the same keyless floor before it runs. No decorator, no key, no code change. Just one line in your `mcp.json` (Claude Code, Cursor, or any MCP client):

```json
{
  "mcpServers": {
    "filesystem": {
      "command": "agentx-mcp",
      "args": ["npx", "-y", "@modelcontextprotocol/server-filesystem", "/data"]
    }
  }
}
```

`agentx-mcp <real server command>` spawns the real server and relays the protocol untouched, intercepting only tool calls. Out of the box it watches, key or no key: a dangerous call is recorded, noted on the proxy's stderr, and forwarded, and `agentx-mcp --audit` shows what it would have stopped. Add `"env": { "AGENTX_POSTURE": "enforce" }` to that server's entry and a blocked call comes back to the agent as a coaching tool error it can self-correct on, so the run keeps going and the dangerous call never reaches the server.

Beyond the calls your agent makes, `agentx-mcp` also watches the **server's advertised tools** for a bait-and-switch: a malicious or compromised server can advertise a benign tool at install (you approve it once), then silently rewrite that tool's description or schema on a later run to steer your agent. The proxy fingerprints each tool the first time it sees it and warns you when an already-approved tool's definition later changes (the NSA's 2026 MCP guidance names this exact attack). It runs in advisory mode by default (a loud warning, never breaks a run); set `AGENTX_MCP_TOOL_PINNING=block` to also gate calls to a changed tool until you re-verify it, or `off` to disable.

**No Python in your stack?** You don't need a persistent install. Run it on demand with [`uvx`](https://docs.astral.sh/uv/) (or `pipx run`), which fetches the package into a throwaway environment, so your `mcp.json` stays one line:

```json
{
  "mcpServers": {
    "filesystem": {
      "command": "uvx",
      "args": ["agentx-mcp", "npx", "-y", "@modelcontextprotocol/server-filesystem", "/data"]
    }
  }
}
```

(`pipx run agentx-mcp <real server command>` works the same way.)

---

### Step 3: See your first block: no key, no gateway, no signup

This is the point of AgentX: the deterministic **floor** runs *inside the SDK*. The catastrophic call is caught in-process, with **zero keys and nothing else running**, no LLM, no gateway. (Both the demo above and the script below pin `posture="enforce"` so you can watch it stop something. Your own install watches by default and lets the call run.)

If you installed from PyPI, `agentx demo` (Step 1b) is this, and it needs nothing else. To read the same block with the source in front of you:

```bash
python examples/00_quickstart_pip.py     # from the repo (a pip install has no examples/)
```

The script wraps an ordinary SQL helper, then feeds it a prompt-injected `DROP TABLE users;`. The in-process keyword shield intercepts it *before it executes*, with no `.env`, no API key and no gateway required. You'll see the block and a clean session summary:

---

🕹️ Console Session Output Logs:

```text
================================================================
AgentX quickstart — watch a DROP TABLE get blocked, no key needed
================================================================

Agent tool call:
  run_sql(query='Update notes for client 99401; DROP TABLE users;')


🛡️ [AgentX SDK] Checking 'run_sql'...
🛑 [AgentX SDK] Stopped 'run_sql': Mass Destructive Intent (local check, no LLM).
📝 [AgentX SDK] Recorded locally (no key needed).
BLOCKED before execution (deterministic floor — no key, no LLM).
  policy:  Mass Destructive Intent
  receipt: local-keyword-shield-11111111-1111-1111-1111-111111111101

The DROP TABLE never reached your database.
================================================================

════════════════════════════════════════════════════════════
 🛡️  AgentX Session Summary (Trace: b6a45c9f-7e5e-4f56-a5aa-62507625118c)
════════════════════════════════════════════════════════════
 ⏱️  Uptime:                0.01 seconds
 🛠️  Tool calls:            1   |  every call, blocked or not
────────────────────────────────────────────────────────────
 🛑 Intercepts:            1   |  On record: 1
 🚨 Human Escalations:     0   |  this run
 🔄 Self-Corrections:      0   |  On record: 0
 📈 Recovery:              0 of 1 challenges this run |  On record: 0 of 1 blocks
    ↳ of 1 challenge(s): 0 recovered · 0 continued · 1 abandoned · 0 looped
────────────────────────────────────────────────────────────
 💡 Not every call ran as asked this session. See what AgentX caught:
    ▶ agentx insights
════════════════════════════════════════════════════════════
```
---

### Step 3b: Customize the coaching (keyless, by name)

Every block above hands your agent a **coaching** message: what went wrong, plus a safe path to try instead. That wording is what drives whether the agent recovers, so you can override it for any built-in floor policy, **by name, keyless**. No UUID, no gateway.

List the policies you can customize and the coaching each one ships with:

```bash
agentx policies
```

Then override one by its name, inline or in your editor:

```bash
agentx customize "Mass Destructive Intent" --text "Our house rule: never DROP; snapshot then soft-delete first."
agentx customize "Network Sandbox (SSRF)" --edit          # opens $EDITOR seeded with the current coaching
```

Your customized coaching is saved to `./.agentx/overrides.json` (commit it to share with your team) and applies **keyless on both surfaces**: the `@agentx_protect` decorator and the `agentx-mcp` proxy. Add `--safe-path "..."` to set the concrete safe alternative distinctly from the coaching. Validate the store anytime, so a hand-edit typo is loud instead of a silent disable:

```bash
agentx policies --check
```

---

### Step 4: (Upgrade) Recover: turn hard blocks into recoverable challenges

Everything above is keyless **Watch**: the floor screens every call, records it, and (once you set `AGENTX_POSTURE=enforce`) blocks the blatant ones and coaches your agent to self-correct. The **reasoning axis** (orthogonal to `AGENTX_MODE`, and orthogonal to the posture) is **Recover**: the gateway judge catches what the keyword floor can't see, writes the **task-fitting challenge** when your policy carries none, and runs the coach-and-retry for you, plus Discovery for novel-intent classification. It takes two things, and they go together:

**1. A `GEMINI_API_KEY`**, the cheap part. Copy `.env.example` to `.env` and set it:

```text
# Reasoning axis — OPTIONAL. Unset = floor-only (zero LLM, zero keys).
GEMINI_API_KEY=your_gemini_key_here       # from aistudio.google.com
# AGENTX_REASONING=off                     # force floor-only even when a key is present
```

**2. The gateway running with that key (Step 5)**, because the judge that *writes* the task-fitting challenge lives in the gateway, not the SDK. The gateway is closed-source, so you run a prebuilt private image. Drop your email at **[agentx-core.com/gateway](https://agentx-core.com/gateway)** and you get a one-paste `docker` command back, no wait. This is the wedge worth the two minutes.

The recovery demo **needs the key** (it makes a real LLM call to re-plan after a block) and the
`google-genai` client. It ships in the package. Run it without either and it declines with a
pointer to the keyless example 00 rather than failing:

```bash
pip install google-genai
python examples/01_self_healing_agent.py
```

> Run it with the key but **without the gateway** and it still completes, but *fail-open*: the in-process shield applies a **static** challenge and the deep semantic checks are skipped. The demo says so itself (`⚠️  RECOVERED, DEGRADED … start the gateway to verify`). The **verified, judge-written** recovery, the actual Recover tier, is the gateway path in Step 5.

---

### Step 5: (Upgrade) Run the gateway: the reasoning judge (Recover), telemetry, control plane, HITL

The SDK screens every call on its own (keyless **Watch**), and blocks on its own once you set the posture. The gateway is what unlocks **Recover** from Step 4: with a `GEMINI_API_KEY` set, the judge here writes the verified, task-fitting challenge (the keyless path only ever gets a static one). Run the gateway and the Next.js console to add the **data plane** (the reasoning judge, deep AST evaluation, the policy lifecycle) and the **control plane** (the ROI dashboard, Chain-of-Thought review, human-in-the-loop approvals). This is the `AGENTX_MODE` **data axis**, where data lives and whether it syncs:

```text
#   local   isolated; local SQLite store; no sync; no keys required
#   linked  local-authoritative; explicit pull/push; no auto-sync
#   cloud   control plane authoritative; continuous sync + upload + HITL/SOC
AGENTX_MODE=local
NEXT_PUBLIC_AGENTX_MODE=local   # the UI's build-time copy; keep it equal
# AGENTX_API_KEY=agentx_sk_your_key_here   # required for `cloud` (and remote `linked`)
```

**Connecting to the cloud is one variable.** You don't need to set all three. An
`AGENTX_API_KEY` is only ever meaningful for cloud upload, so just setting it (with
no `AGENTX_MODE`/`CONTROL_PLANE_URL`) puts the gateway in `cloud` mode against the
public plane, and it says so loudly at boot (`🔑 AGENTX_API_KEY detected → CLOUD
mode …`). Set `AGENTX_MODE=local` to override and stay isolated, or `linked` (with
a `CONTROL_PLANE_URL`) for explicit pull/push without auto-upload.

Boot the data-plane wedge and the dashboard:

```bash
docker compose up -d
```
OR
```bash
uvicorn --app-dir backend gateway:app --host "0.0.0.0" --port=8000
```

> Run it from the **project root** with `--app-dir backend`, not `cd backend && uvicorn …`.
> The gateway resolves its data home from the project root, and that is fully
> cwd-independent only where a `.git` anchor exists; in a checkout-less tree the fallback
> is the nearest `.agentx/` ancestor, so a server started inside `backend/` can resolve a
> different home than `agentx insights` does. `scripts/start_gateway.py` does this for you.

**Running the end-to-end suites?** Start the gateway with the launcher instead. Both
`backend/test_gateway.py` and `sdk_tests/test_agentx_sdk.py` drive a gateway that is
*already running*, so their incident writes happen in the server's process and no test
fixture can redirect them. The launcher points that process at a scratch incident and
policy store, so a full test run does not write the stores `agentx insights` reports on:

```bash
py scripts/start_gateway.py --e2e            # uvicorn, scratch store
py scripts/start_gateway.py --e2e --docker   # same, via docker compose
py scripts/start_gateway.py                  # normal dev gateway, real store
```

The gateway keeps its data in **one** place: `<project root>/.agentx/`
(`incidents.db`, `policies.db`, `policies.json`). That is resolved from the project
root, not the directory you start from, so starting the gateway from `backend/` no
longer creates a second store that `agentx insights` cannot see.

> **Both commands above build the gateway from source** (internal / source-access).
> The gateway is closed-source, so **you run a prebuilt private image instead**,
> `pip install agentx-security-sdk` does not ship it. Drop your email at
> [agentx-core.com/gateway](https://agentx-core.com/gateway) and you get a one-paste
> `docker` command back immediately. No sales form, no wait. The starter kit in
> [`deploy/partner/`](deploy/partner/) then runs the image (no source needed).

The reasoning engine then listens on http://localhost:8000 and the dashboard on http://localhost:3000. In `local`/`linked` mode the gateway owns the policy lifecycle from a local SQLite store (seeded on first boot, editable via the Policy & Discovery tabs); in `cloud` mode it mirrors policies from your Supabase-backed Control Plane.

**Fail-mode (optional).** This answers a different question from the posture below: what happens when the **gateway** is unreachable. By default AgentX **fails open** -- tool calls still execute, with a loud warning and a tally in the session summary, and the in-process floor still screens them. `AGENTX_FAIL_MODE=closed` refuses any call the engine could not verify until it recovers. ⚠️ It does nothing while you are watching, because a posture that refuses nothing cannot refuse an unverified call; set it together with `AGENTX_POSTURE=enforce`, and on its own the SDK says once that it is inert.

**Posture: the free SDK watches, a gateway enforces, and you can say otherwise.** Out of the box the keyless SDK **watches**. It runs the full detection and records every call, and **nothing is blocked** -- so adding AgentX cannot break an agent that already works. `agentx audit` shows what your agent actually did; `agentx insights` shows what would have been blocked, per policy. When the catches look right, set `AGENTX_POSTURE=enforce` and they become real. ⚠️ **An install with an `AGENTX_API_KEY` set enforces by default instead**, and that is deliberate: a key is what reaches the gateway, so it is the rung you climbed to in order to block. Handing that install the watching posture would quietly turn off the protection you set up. `AGENTX_POSTURE` still wins on either rung -- set `audit` on a gateway install and it watches. You can also keep one dangerous tool hard-blocked while everything else watches, with a per-tool override: `@agentx_protect(..., posture="enforce")`. Posture is a separate setting from the fail-mode above, **and it wins**: while a tool is watching, nothing AgentX decides changes what that tool does -- including `AGENTX_FAIL_MODE=closed`, which cannot refuse a call in a posture that refuses nothing. Every kind of stop is written down and then let through: the built-in keyword shield, a policy block from the gateway, a pause for human approval, and the halt that fires when an agent keeps retrying. A tool you gave the per-tool override above still blocks, which is the point of the override. A test re-checks this against every rule in the gateway's list. (`AGENTX_ENFORCEMENT` is the old name for `AGENTX_POSTURE` and still works.)

---

## 📊 Control Plane Telemetry

When your agent script finishes or exits, AgentX writes its telemetry to the local `.agentx.db`. In `cloud` mode that also syncs to your control plane; in `local` and `linked` mode it stays on the machine. With the dashboard running, open http://localhost:3000/dashboard to see the summary. **The numbers below are illustrative, not measured results:**

```text
+-----------------------------------------------------------------------------------+
|                        EXECUTIVE COMMAND CONSOLE                                   |
+-----------------------------------------------------------------------------------+
|  [Catastrophic Actions]   [Autonomous Recovery]  [Runs Protected]  [Time Saved]   |
|         92                      36.3%                  37             12.3 hrs     |
|  🛑 Irreversible/exfil   📈 Self-corrected ÷    🛡️ Runs that     ⏱️ ~20 min/run  |
|     stopped pre-exec        challenged loops       self-corrected     reclaimed    |
+-----------------------------------------------------------------------------------+

```

**Executive ROI Mappings** (all computed *per session*, grouped by `trace_id`):
* **Catastrophic Actions Blocked (hero):** A pure count of distinct sessions whose intercepted action fell in an irreversible / exfiltrative class, `failure_mode ∈ {DESTRUCTIVE_ACTION, PII_EXFILTRATION, NETWORK_TRAVERSAL, SECRETS_LEAK}`, with a policy-name keyword fallback. Every incident in the ledger is a *pre-execution* interception, so this is harm averted, not harm survived.
* **Autonomous Recovery Rate:** Of the sessions that entered the challenge loop, the share whose terminal status is `COMPLIED` (the agent self-corrected). Counted per session so `recovered ⊆ challenged`, **bounded ≤100% by construction**. HITL-approved sessions are excluded; only autonomous self-correction counts.
* **Agent Runs Protected:** Sessions the agent self-corrected after a block (terminal `COMPLIED`).
* **Engineering Time Saved:** ~20 min of manual triage credited per protected run, valued at $75/hr.

> Operator dashboard metrics are scoped to **production agents only**, demo, simulation, blind-eval, and test/probe traffic are excluded so benchmarks never inflate an operator's numbers (they showcase the engine on the public landing page instead).

---

## 🧠 The 5 Pillars of Agentic Security

AgentX is built on a "Reasoning Engine" architecture that treats AI agents as autonomous employees rather than static scripts:

1. **Cognitive Interception:** We intercept tool calls to compare the agent's stated intent (Chain of Thought) against its actual deterministic action.
2. **Socratic Nudging:** Instead of crashing the agent, we issue a Socratic Challenge to guide them to a safe, desired end-goal.
3. **Shared Immunity Network (roadmap):** Novel zero-day signatures discovered on one node are designed to graduate into the deterministic floor and propagate to other Edge nodes for O(1) interception. The local Discovery → Promote → live-in-3s loop works today; cross-node global distribution is a Day-100 capability and is **not yet active**, we don't claim it until it is.
4. **Circuit Breakers:** If an agent enters an infinite hallucination loop, AgentX hard-locks the runtime after 3 strikes to prevent massive LLM token billing overages.
5. **Human-in-the-Loop (HITL):** If an agent pulls the "Andon Cord" (requests help), the system suspends the execution thread (`202 Accepted`) and parks it in the SOC Sandbox for human approval.

---

## 🚀 The 4 Shields (Defense-in-Depth)

1. The Inbound Shield (Prompt Injection): Sanitizes inbound user text to prevent cognitive hijacking ("Ignore previous instructions") before the agent reads it.

2. The Logic Shield (Database Guard): Uses AST parsing and Gemini to catch destructive queries (DROP, DELETE) and nudges the agent to write safer SQL.

3. The Network Shield (SSRF Guard): Prevents agents from acting as confused deputies to hit cloud metadata IPs (e.g., 169.254.169.254).

4. The Egress Shield (DLP/PII Scrubber): Dynamically masks PII and API keys on the wire, maintaining clean audit logs without triggering SOC alert fatigue.

---

## 📊 Local Telemetry & Agent Health

AgentX ships with a built-in, privacy-first SQLite time-series event log (`.agentx.db`). It records every interception locally, and never a raw query, argument value or payload. It also records one row per call that PASSED, whether blocking is on or off, so `agentx audit` can show you what your agent actually did: the tool's own name, its argument NAMES, a magnitude bucket and a coarse target class. Never the values. When your agent script finishes or crashes, AgentX prints a session summary:

```text
════════════════════════════════════════════════════════════
 🛡️  AgentX Session Summary (Trace: 74096ff7-37b0-4b69-a7a3-fe27d62d4000)
════════════════════════════════════════════════════════════
 ⏱️  Uptime:                0.01 seconds
 🛠️  Tool calls:            1   |  every call, blocked or not
────────────────────────────────────────────────────────────
 🛑 Intercepts:            1   |  On record: 6
 🚨 Human Escalations:     0   |  this run
 🔄 Self-Corrections:      0   |  On record: 0
 📈 Recovery:              0 of 1 challenges this run |  On record: 0 of 6 blocks
    ↳ of 1 challenge(s): 0 recovered · 0 continued · 1 abandoned · 0 looped
────────────────────────────────────────────────────────────
 💡 Not every call ran as asked this session. See what AgentX caught:
    ▶ agentx insights
────────────────────────────────────────────────────────────
 Most blocks in this ledger are one policy: 'Mass Destructive Intent'
════════════════════════════════════════════════════════════
```

To see what was blocked, grouped by policy, with recoveries beside each:

```bash
agentx insights
```

`agentx insights` answers *what got stopped*. **`agentx audit` answers the wider question: what
did my agent actually do**, including every call we let through. It is the screen to read before
you trust an agent with something, and the one to read after it surprises you.

```bash
agentx audit                # grouped by tool, one row per tool
agentx audit --calls        # one row per call instead
agentx audit --json         # the same content as data, for a program
agentx audit --share        # writes agentx-audit.json, with this machine's fingerprints removed
agentx audit --limit 100    # or --all. The human views page; --json is never capped
```

Two flags turn it into a CI gate, with the same exit codes as the TypeScript door:

```bash
agentx audit --require-calls        # exit 2 if the ledger holds no calls: the output says nothing about the agent
agentx audit --fail-on-rule-match   # exit 3 if a recorded call matched a rule you adopted
```

Both work with `--json`, which then carries the verdict in a `ci` block, alone on stdout.

`--calls` and `--json` are independent on purpose: `--calls` decides **what** you see, `--json`
decides **how** it prints, so all four combinations mean something. There is no way to ask for
the grouped view machine-readably if those are folded into one flag.

To show a single catch to someone else:

```bash
agentx share                                # your most recent block, as a postable receipt
agentx share --note "what my agent tried"   # add your own line to the draft
```

It renders a receipt card plus a ready-to-post draft. The card is built only from the abstract
ledger fields: policy class, your own tool name, the verdict, and when. **It cannot carry a raw
query or payload, because the ledger never stores one.**

### The rest of the commands

Two more you will want early, and neither has appeared above:

```bash
agentx status     # what is armed right now, and your local protection stats
agentx review     # one key per pending block: adopt a safe path, or label the block
```

`agentx review` is the loop that turns a pile of blocks into rules you keep. `--stats` summarises
every incident's outcome rather than only what is still pending.

The rules you adopt live in **`.agentx/rules.json`**, a plain file in your project: commit it,
review a change to it in a pull request, and a gateway reads it at boot and on every policy
refresh, so a change reaches a running gateway within minutes.
Name a rule yourself with `agentx adopt <#> --name "..."`; without a name, a rule proposed from
your own calls is named after the tool and its argument names. `agentx review --undo` takes one
back. Over the MCP door, name your project in the server's env block
(`AGENTX_PROJECT_DIR`; `${CLAUDE_PROJECT_DIR}` on Claude Code) and the proxy reads the same file.

The rest live behind `agentx help --advanced`: adopting learned safe-paths, recording
verdicts, customising a floor policy's coaching by name, and org sync. **`agentx help` is the
list that is always current** — this page is a walkthrough, not a reference, and the CLI is the
thing that ships.

**Every number on these screens is one the SDK measured.** There is no "tokens saved" or
"time saved" line, because nothing on this path can observe what a call we stopped would
have spent — those figures used to be a constant multiplied by a block count, and they are
gone rather than estimated.

---

## 📦 Try the other Developer Demos

These live in the `examples/` folder of the repo, so they need a checkout rather than the pip install:

* **01_self_healing_agent.py:** Watch AgentX catch a hallucination and coach the agent to self-correct (Saving tokens and uptime).
* **02_cognitive_intent_block.py:** Watch AgentX catch malicious intent even when the raw syntax is perfectly safe.
* **04_circuit_breaker_demo.py:** AgentX catches and prevents an infinite apology loop, saving time and tokens.
* **06_hitl_escalation.py:** See how an agent safely pauses execution and pings a SOC analyst for approval using a 202 Accepted queue.
* **09_budget_ceiling_demo.py:** Watch AgentX meter a runaway agent's cumulative spend and halt the session the moment it crosses your budget ceiling, a deterministic, gateway-side escalation (no LLM judge). Needs the gateway running + a key.
* **10_self_correction_coaching.py:** The Recover tier, where the gateway judge does the coaching for you. AgentX blocks a dangerous action and returns a task-fitting challenge that names a safe path, so your agent finishes the job instead of dead-ending. Puts the AgentX challenge side by side with a bare refusal, then watches the agent recover on it. Needs the gateway running + your Gemini key (the keyless Shield already coaches in-band; Recover's judge catches more and runs the retry for you).
* **12_audit_what_your_agent_did.py:** The one demo where nothing is blocked, including the poisoned call at the end. A support agent works a refund ticket end to end, and AgentX records every wrapped tool call, whether blocking is on or off: the argument names, the surface, the size of any amount passed, never the values. Run `agentx audit` afterwards for the table. The poisoned call is not on it, because a call that tripped a policy shows in `agentx insights` instead. Keyless, no gateway.
* **And many more...**

---

## 🕹️ Human-in-the-Loop (HITL) & Control Plane
Sometimes, an agent needs to drop a table for a valid business reason.

AgentX ships a Next.js Control Plane dashboard. If an agent requests an escalation, the SDK pauses local execution and polls the gateway. A human reviewer clicks "Approve" or "Deny" in the UI, and the Python execution loop resumes on its own.

Running the dashboard needs the repo, not the pip install:

```bash
cd ui
npm install
npm run dev
```

---

## 🏗️ The Architecture (Split-Plane)

Three pieces. Only the first one is required, and it is the only one that ships in the pip package.

* **The Edge SDK (`agentx_sdk`):** The Python package that instruments your tools and carries the keyless deterministic floor. Runs in your process. This is what `pip install` gives you.
* **The Data Plane (Reasoning Engine):** A FastAPI service that evaluates what the floor waves through, via a three-stage funnel: the deterministic floor first, then AST evaluation, then the LLM judge. Closed source, run as a prebuilt image.
* **The Control Plane (Dashboard):** A Next.js app where a human reviews intercepted traffic, reads chains of thought, and approves or denies parked requests.
* **Where state lives:** Mode-dependent. In `cloud`, Supabase is authoritative and both planes sync through it. In `local` and `linked`, the gateway's own SQLite stores (`.agentx/incidents.db`, `.agentx/policies.db`) are authoritative and nothing syncs. See [What leaves your machine](#-what-leaves-your-machine).
* **The Evaluator:** Google Gemini 2.5 Flash or Pro, configurable by environment variable. It reads the agent's stated intent and evaluates it against your policies. Only used on the gateway path; the keyless floor never calls it.

---

## ✨ Key Features & Built-in Policies

* **Automated Socratic Self-Healing:** Intercepts dangerous tool calls and challenges the agent to revise its strategy.
* **Fast Pass Heuristic Traps:** Instantly intercepts structurally dangerous queries (e.g., `DROP TABLE`, or an unscoped / `WHERE 1=1` mass `DELETE`/`UPDATE`) with minimal latency.
* **Zero-Knowledge Intent Extraction:** Prevents malicious prompt injection by translating raw agent logic into a strict schema before policy evaluation.
* **Dynamic Policies:** In `cloud`, enforces isolation rules via a Supabase-backed Control Plane that syncs to edge caches in ~3 seconds. In `local`/`linked`, the gateway owns the policy lifecycle locally, create/edit/toggle/delete and AI-drafted promotions from the Policy & Discovery tabs are armed live (re-embedded into the in-RAM vector index) with no restart.

---

## 🔒 What leaves your machine

Keyless, in `local` mode, the answer is almost nothing. Your queries, payloads and agent chain-of-thought stay on the machine and are never uploaded unless you explicitly push them. Two things do leave, and both are narrow and named:

* **The dependency-reputation check.** Sends *package names only* to the public npm and PyPI registries, to catch slopsquats.
* **An anonymous daily usage pulse.** Version, OS and block **counts**. Never your code, queries, chain-of-thought or identity. It is **on by default**, prints a one-time notice before the first pulse, and turns off with `AGENTX_TELEMETRY=off`. See `.env.example`.

Contributing to the shared corpus is separate and **off by default**. It happens only when you push it (`agentx push`, or `agentx sync` which pulls policies and pushes in one step), and only after you opt in with `AGENTX_CONTRIBUTE`. Left unset on a terminal, it asks once and saves your answer; left unset in CI or a script, it stays off and never blocks. What it sends is abstract: which policy fired and when, never a query, payload, chain-of-thought or identifier. It also needs the gateway, because the gateway is what reduces your local records to abstract signal before anything leaves the machine.

---

## 🔒 Licensing

**This package is MIT.** The `agentx_sdk/` edge client, published to PyPI as `agentx-security-sdk`, is licensed under the [MIT License](https://github.com/vdalal/agentx-security-sdk/blob/main/LICENSE). The `agentx-mcp` launcher is MIT as well. Use them freely, including commercially.

**The gateway and control plane are not.** The Reasoning Engine and Control Plane are closed source and proprietary, all rights reserved. No public open-source or source-available license is granted for them. Source access for evaluation is available to qualified customers and partners under written agreement.

---

## 🚀 Roadmap & Milestones

✅ Trust Boundary Shift: Moved neuro-symbolic evaluation entirely into the Data Plane container to eliminate agent runtime bypasses. (Completed)

✅ Hard Split-Plane: Telemetry is stripped of payload and chain-of-thought content at the edge, so what crosses the plane boundary is counts and classes rather than your data. (Completed)

✅ Zero-Config Reflection Engine: Eliminated manual query and CoT boilerplate writing using dynamic signature parameters compilation hooks. (Completed)

✅ Local Keyword Shield: Deterministic, dependency-free keyword/intent pre-filter in the SDK that intercepts obvious threats offline, in-process, with zero gateway/LLM calls. Scans the action payload only; chain-of-thought intent is deferred to the gateway's LLM judge. (Completed)

✅ Judge Verdict Memoization: Bounded in-memory cache on the Data Plane that reuses prior LLM verdicts for identical (payload + reasoning + policy set), eliminating repeat Gemini calls during agent retry loops. (Completed)

✅ Catastrophic-Action Hero Metric: Reframed the Executive ROI strip to lead with severity-filtered "Catastrophic Actions Blocked" (irreversible / exfiltration intents stopped pre-execution), with per-session metric accounting that bounds Recovery Rate ≤100% by construction across the dashboard, the Supabase summary view, and the SDK. (Completed)

✅ Detection-vs-Recovery Eval Harness (`eval/`): Independent instruments that measure the engine honestly, `blind_agent_eval.py` (end-to-end detection recall via a blind LLM agent + independent oracle), `probe_judge.py` (isolates the reasoning layer's *marginal* recall over the deterministic floor), and `recovery_eval.py` (A/B marginal-recovery lift of the Socratic challenge vs a bare 403). (Completed)

✅ Incident-Persistence Hardening & Fail-Mode Switch: Restored the CHALLENGED→COMPLIED persistence pipeline (gateway-pinned UUID receipts, `/v1/incident` Layer-0 registration, COMPLIED PATCH gated on a real `200`) and added `AGENTX_FAIL_MODE=open|closed`. (Completed)

✅ Deterministic Floor, Hard-Block + HITL-Escalation + Loop-Abort Tiers: The zero-LLM core that runs before (and without) the judge, so the engine fully protects keyless. **Hard-block** tier DENIES never-legitimate actions (destructive DDL/DML incl. `ALTER … DROP COLUMN`, cluster/cloud teardown, SSRF, secret reads + egress exfiltration, filesystem whole-scope deletes + path-boundary escapes, remote-pipe-to-shell installs, and bidi-override / Unicode-Tags carrier payloads, Trojan-Source / invisible-instruction smuggling). **HITL-escalation** tier returns `202 ESCALATED` → human SOC for *consequence-gated* actions that can be legitimate: **High-Value Transfer Approval** (`AGENTX_TRANSFER_ESCALATION_THRESHOLD`), **External Publication Approval**, **Comms Bulk-Deletion Approval**, and **Budget Ceiling Approval** (cumulative session token/$ spend vs `AGENTX_SESSION_TOKEN_CEILING` / `AGENTX_SESSION_COST_CEILING_USD`; report real usage with `agentx.record_spend(...)` or rely on the built-in volume estimate). **Loop-abort** tier terminates runaway loops (strike-count breaker + the `detect_no_progress_loop` no-progress repeat breaker, `AGENTX_LOOP_REPEAT_CEILING`). Every detector has a fires-in-anger test asserting attribution + zero LLM calls. See `AGENT_FAILURE_CATALOG.md` for the per-incident coverage state. (Completed)

⬜ Containerized Multi-Region Edge Cluster: Standardize container blueprints for automated high-availability deployments onto AWS ECS and Render clusters. (Future)


## 🤝 Support & design partners

* **Docs:** [agentx-core.com/docs](https://agentx-core.com/docs)
* **Community and support:** the Discord invite is on [agentx-core.com](https://agentx-core.com), which always has the current link.
* **Issues in the SDK:** the MIT client is mirrored at [github.com/vdalal/agentx-security-sdk](https://github.com/vdalal/agentx-security-sdk), which is where SDK issues belong. This monorepo is private, so an issue opened here will not reach anyone.
* **Contact:** founders@agentx-core.com

If you are running agents that write to systems you care about and want to compare notes on what they try, we are actively looking for design partners.


