Frameworks
The full, honest coverage matrix — 16 Python frameworks + 5 TypeScript surfaces, verified 2026-07-16.
raxit governs agent frameworks two ways:
- Zero-code (Python, and most of TypeScript):
raxit run -- python agent.py(or-- node agent.mjs) loads autopatch before your agent's code and hooks the point inside each framework where tool calls are dispatched — no per-tool changes. - Per-tool escape hatch: wrap individual tools with
governed_tool(Python) orgovernedTool()(TypeScript) when you want explicit, fine-grained control — or where a framework's autopatch has a documented gap (e.g. MODIFY on the Vercel AI SDK).
This page is the coverage matrix: all 16 Python frameworks and all 5 TypeScript surfaces are tested against the real, package-installed frameworks (verified 2026-07-16). Not every framework has a dedicated walkthrough page yet; the tables below say exactly which do, and note where a framework is covered by the test suite only.
MODIFY (redact/hash/drop) zero-code coverage is proven on ~20 framework surfaces (up from 4),
each backed by a real-entry mutation proof that asserts the tool body itself observed the redacted
value, not just the decision. 3 surfaces are genuinely disclosed-unsupported for zero-code
MODIFY, not silently missing: claude-agent-sdk (its interception point is a closed subprocess
boundary), openai-agents built-in Actions specifically (its FunctionTool custom-tool path is
proven — only the built-in <X>Action.execute/handoff dispatch carries no rewritable args), and
the Vercel AI SDK's autopatch (MODIFY there requires the explicit governedTool() wrapper — see
the TypeScript table below).
openai-agents needs an `openai<2.45` pin
openai-agents==0.17.7 resolves openai==2.45.0, whose InputTokensDetails model now
requires cache_write_tokens — breaking agents/usage.py's own default factory upstream
(a bug in openai/openai-agents-python, not a raxit bug). raxit's own examples pin
openai<2.45 to work around it; if you hit this combination in your own project, pin the same
way.
Python — 16 frameworks
Every row's "Zero-code" mechanism means raxit run -- python agent.py governs it with no raxit
import in the agent. MODIFY means the resource's arguments can be rewritten (e.g. field-level
redaction) before the tool body runs, at this specific interception point — each per-framework
adapter threads the rewrite into that point's own input shape (a bare positional scalar, a
JSON-string payload, a keyword-only context object, a nested envelope, …), proven live against the
real framework. Where a row says disclosed-unsupported, the interception point genuinely
carries no rewritable args (or is a closed boundary); the per-framework page explains the exact
reason.
| Framework | Mechanism | MODIFY at this interception point | Example |
|---|---|---|---|
langchain | Zero-code (BaseTool.run/.arun/.invoke/.ainvoke) | Yes — dict-shaped input, redaction demoed live | LangChain (Python) |
langgraph | Zero-code (ToolNode._execute_tool_sync/_execute_tool_async, or _run_one/_arun_one on older versions) | Yes — nested-envelope adapter, redaction proven live | LangGraph (Python) |
crewai | Zero-code (CrewStructuredTool.invoke/.ainvoke) | Yes — dict-shaped input, redaction demoed live | CrewAI |
openai-agents | Zero-code (agents.tool.invoke_function_tool, every built-in <X>Action.execute, execute_handoffs) | Yes on FunctionTool custom tools (JSON-string-args adapter, redaction proven live) — disclosed-unsupported on built-in <X>Action/handoff dispatch, which carries no rewritable args | OpenAI Agents SDK |
pydantic-ai | Zero-code (FunctionToolset.call_tool) | Yes — adapter handles the tool-name-first signature, redaction proven live | pydantic-ai |
google-adk | Zero-code (FunctionTool.run_async, BaseTool.run_async fallback) | Yes — adapter handles the keyword-only args, redaction proven live | google-adk |
llamaindex | Zero-code (FunctionTool.call/.acall) | Yes — adapter handles the single positional scalar, redaction proven live | LlamaIndex |
autogen (ag2) | Zero-code (ConversableAgent.execute_function/.a_execute_function) | Yes — adapter decodes the JSON-string func_call args, redaction proven live | autogen (ag2) |
dspy | Zero-code — two independent interception points: Tool.__call__/.acall (the ReAct path) and ToolCalls.ToolCall.execute (native tool calling) | Yes on the ReAct surface (Tool.__call__) — flat-kwargs adapter, redaction proven live; the native-tool-calling surface supports MODIFY via the shared engine but isn't separately exercised live | DSPy |
smolagents | Zero-code (Tool.__call__) | Yes — adapter handles the positional scalar, redaction proven live | smolagents |
strands-agents | Zero-code (ToolExecutor._stream) | Yes — nested-envelope adapter (tool_use["input"]), redaction proven live | Test suite only |
haystack | Zero-code (Pipeline.run, AsyncPipeline.run/.run_async — whole-pipeline governance, one resource id per pipeline class) | Yes — flat-kwargs adapter, redaction proven live | Test suite only |
agno | Zero-code (FunctionCall.execute/.aexecute) | Yes — redaction proven live | Test suite only |
claude-agent-sdk | Zero-code (ClaudeAgentOptions.__init__ auto-injects a raxit PreToolUse hook matcher) | Disclosed-unsupported — the interception point is a closed subprocess boundary; the hook contract (updatedInput) supports MODIFY in principle but it's not exercised live | Test suite only |
microsoft-agent-framework | Zero-code (RawAgent.__init__ auto-injects a raxit FunctionMiddleware) | Yes — redaction proven live | Test suite only |
camel | Zero-code (FunctionTool.__call__/.async_call) | Yes — redaction proven live | Test suite only |
agency-swarm rides entirely on the openai-agents patcher — its Agent class subclasses
agents.Agent directly, so it has no dedicated patcher entry and is not counted as a 17th
framework. It's proven live in our test suite as inherited coverage, not inferred.
TypeScript — 5 surfaces
raxit run --agent-id <id> -- node agent.mjs sets RAXIT_AUTOLOAD=1 and
NODE_OPTIONS=--import raxit/autopatch-preload, running the preload before your agent's
entrypoint. Four of the five surfaces are auto-patched by that preload alone; MCP is the one
opt-in exception.
| Framework | Mechanism | MODIFY at this interception point | Example |
|---|---|---|---|
| LangChain.js | Zero-code, auto (StructuredTool/BaseTool.prototype invoke/ainvoke/run/arun) | Yes — same BaseTool layer as Python | LangChain.js / LangGraph.js |
| LangGraph.js | Zero-code, auto (ToolNode.prototype.runTool — the per-call interception point, not invoke/run, which only see the whole batch) | Yes — call.args is the tool's real unwrapped argument object at this point, redaction proven live; no defer assertion on this path | LangGraph.js |
| Mastra | Zero-code, auto (post-wraps Agent.prototype.convertTools) | Yes — input is the first parameter to the converted tool's execute | Mastra |
| MCP servers | Opt-in, per-instance — installMcpAutoPatch(server) wraps server.setRequestHandler; must be called before the tools/call handler is registered, or that handler runs ungoverned, silently | Yes — the wrapper threads the redaction into request.params.arguments, redaction proven live | MCP servers |
| Vercel AI SDK | Zero-code, auto via registerTelemetry on the SDK's global telemetry registry, governing executeTool | No — fails closed here (input is captured before the wrapper runs); use governedTool() for MODIFY | Vercel AI SDK |
Vercel AI SDK: four documented, verbatim silent-fail-open bypasses
experimental_telemetry: { isEnabled: false } disables the dispatcher entirely;
experimental_telemetry: { integrations: [...] } replaces the global registry raxit registered
into; a direct tool.execute() call outside generateText/streamText/Agent bypasses the
dispatcher; providerExecuted tools run server-side with nothing local to intercept. All four
are documented on the Vercel AI SDK page, not hidden.
Mastra: narrow, documented coverage scope
Mastra's autopatch governs the regular Agent.generate()/stream() path only. Not
covered — needing an explicit governedTool() / wrapped createTool instead: the durable
agent's direct execution sites, direct makeCoreTool() users, direct tool.execute() calls
outside the agent loop, Mastra's internal AI-SDK-compat executeTools/runToolsTransformation
family, and clientTools (execute stripped server-side by design). convertTools is
internal-marked, so drift risk is high — raxit's fail-closed startup self-check (it aborts
rather than run ungoverned) is not optional in production.
How autopatch works
raxit run sets an env var that makes the language runtime load raxit's autopatch module
before your agent's own code (Python via sitecustomize on PYTHONPATH; Node via a
NODE_OPTIONS=--import ESM preload — it must be --import, not --require, or a
require()-loaded framework module ends up as a separate module instance from the one your
agent imports, and the patch wouldn't carry over). Autopatch then monkeypatches each detected
framework's real tool-dispatch interception point — the one function every tool call bottoms
out at, whether the caller went through .invoke(), .run(), a ToolNode, or a built-in action
type — so governance fires without touching your tool code. If a framework's target symbol has
moved (a library upgrade drifted the internal API raxit patches), raxit aborts rather than run
ungoverned: autopatch raises ChokepointDriftError and fails closed rather than silently leaving
that framework ungoverned. The per-tool escape hatch — governed_tool (Python) / governedTool()
(TypeScript) — supports every decision type (permit / defer / deny / modify) on every framework,
including the ones whose autopatch can't carry MODIFY.
Related
- Decisions — the permit / defer / deny / modify vocabulary and reason-code catalog referenced throughout the pages above.
- Known ungoverned paths — the calls autopatch does not intercept, and how to detect and close them.
- Limitations & honesty — the honest caveats that don't fit neatly into a single framework's page (throughput ceiling, signing defaults, macOS egress, and more).
Running the daemon
The managed auto-spawn default, and the advanced explicit-daemon path: raxit dev vs raxit serve, the port trap, and how clients find the daemon.
LangChain (Python)
Governing LangChain agents in Python — permit, deny, defer, and modify from one security.yaml, shown with a real executed demo.

