Metadata-Version: 2.4
Name: veragent
Version: 0.1.0
Summary: Vendor-neutral control plane for AI agents — governance, audit, and (soon) runtime enforcement.
License-Expression: Apache-2.0
Project-URL: Homepage, https://www.veragent.io
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Provides-Extra: langchain
Requires-Dist: langchain-core>=0.2; extra == "langchain"
Provides-Extra: openai-agents
Requires-Dist: openai-agents>=0.1; extra == "openai-agents"
Provides-Extra: mcp
Requires-Dist: mcp>=1.0; extra == "mcp"
Provides-Extra: crewai
Requires-Dist: crewai>=1.0; extra == "crewai"
Provides-Extra: dev
Requires-Dist: langchain-core>=0.2; extra == "dev"
Requires-Dist: openai-agents>=0.1; extra == "dev"
Requires-Dist: mcp>=1.0; extra == "dev"
Requires-Dist: crewai>=1.0; extra == "dev"

# Veragent Python SDK

The client library that makes Veragent a **vendor-neutral control plane** instead of a single bespoke integration. Instrument any agent — custom, LangChain, LangGraph, OpenAI Agents SDK, MCP, and CrewAI — in a couple of lines, and have its activity captured in one place under your policies.

The core SDK is **dependency-free** (standard library only). Framework adapters pull in just what they need.

## Install

```bash
pip install veragent                 # core
pip install "veragent[langchain]"    # + LangChain adapter
pip install "veragent[openai-agents]" # + OpenAI Agents SDK adapter
pip install "veragent[mcp]"           # + MCP interceptor
pip install "veragent[crewai]"        # + CrewAI adapter
```

## Quick start

```python
from veragent import Veragent

va = Veragent(agent="my-agent")      # reads VERAGENT_API_KEY from the environment
va.track("task completed", severity="info")
```

### Custom agents — decorator + run correlation

```python
@va.track_action("broker.place_order")
def place_order(symbol, size): ...

with va.run("nightly-cycle") as run:
    run.track("fetched markets", severity="info")
    place_order("AAPL", 10)          # captured automatically, errors included
```

### LangChain / LangGraph — no changes to your agent

```python
from veragent.integrations.langchain import VeragentCallback

chain.invoke(payload, config={"callbacks": [VeragentCallback(va)]})
```

Every LLM call, tool call, and error in the run is captured and normalized. Tool calls — the actions with side effects — are tracked as `event_type="tool_call"`, which is the surface that matters for governance.

### OpenAI Agents SDK — no changes to your agent

```python
from veragent.integrations.openai_agents import instrument

instrument(va)   # register once, before running any agents
# ... build Agents and call Runner.run() exactly as before ...
```

`instrument(va)` adds a tracing processor, so every run, generation, tool/function call, handoff, and guardrail is captured. It's additive — your existing OpenAI tracing keeps working. Function (tool) calls are tracked as `event_type="tool_call"`.

### MCP — capture (and optionally enforce) tool calls

```python
from veragent.integrations.mcp import instrument_mcp

instrument_mcp(session, va)                 # capture every tool call on this session
# instrument_mcp(session, va, enforce=True) # also gate each call via /api/authorize
```

MCP is the framework-agnostic tool layer, so instrumenting `session.call_tool` captures the governable surface no matter what drives the session. With `enforce=True`, each call is authorized first and a denied call is blocked before the real tool runs (it returns an `isError` result). Enforcement stays inert until the client's `enforcement_enabled` is on, so it's safe to wire in now.

### CrewAI — no changes to your crew

```python
from veragent.integrations.crewai import instrument

instrument(va)   # register once, before kicking off any crew
# ... build your Crew and call crew.kickoff() exactly as before ...
```

`instrument(va)` registers a listener on CrewAI's event bus, so every crew, task, agent, tool, and LLM event is captured. Tool calls are tracked as `event_type="tool_call"`.

## Design guarantees

- **Safe.** `track()` only enqueues; all network I/O is on a background worker. Instrumentation never blocks, slows, or crashes the host agent, and never raises into your code path. If Veragent is unreachable, events are dropped with a log line — your agent keeps running.
- **Redaction-first.** Inputs and outputs pass through a redactor *before they leave the process* — sensitive keys (passwords, tokens, emails, IBANs, card numbers, …) are stripped and long strings truncated. On by default. Pass `redact=False` to disable or supply your own callable. This is what keeps customer data out of governance telemetry.
- **Enforcement-ready.** `va.authorize(tool, inputs=...)` is the pre-action decision point. It is safe to wire in **today** — while enforcement is disabled it returns `allowed=True`; once the server-side decision endpoint ships it starts returning real allow/deny verdicts. Set `fail_closed=True` to deny when the decision point is unreachable.

## Event envelope

Backward-compatible with the current ingest contract. `agent` / `action` / `severity` stay at the top level (read by the server today); the structured envelope rides in `metadata`:

```json
{
  "agent": "my-agent",
  "action": "tool call: refund",
  "severity": "info",
  "metadata": {
    "event_type": "tool_call",
    "tool": "refund",
    "inputs": { "order_id": "A-1001", "amount": 49.0 },
    "run_id": "…", "span_id": "…",
    "ts": "2026-…", "sdk": "veragent-python/0.1.0"
  }
}
```

## Adapter roadmap

| Adapter | Status |
|---|---|
| Custom agents (decorator, `run()` context, `track()`) | ✅ shipped |
| LangChain / LangGraph (`VeragentCallback`) | ✅ shipped |
| OpenAI Agents SDK (tracing processor) | ✅ shipped |
| CrewAI (event-bus listener) | ✅ shipped |
| MCP interceptor (capture + enforce) | ✅ shipped |
| OpenTelemetry / OpenInference exporter (zero-code for OTel shops) | ▢ next |
| TypeScript / Node SDK (parity) | ▢ next |

## One server-side dependency

The SDK sends `agent` / `action` / `severity` (works against production now) plus a `metadata` object. To persist the structured fields, the `ingest-event` Supabase function needs to write the incoming `metadata` into `audit_logs.metadata` (the column already exists). Until then the richer data is sent but not stored — the SDK needs no change when that lands.
