raxit
Frameworks

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) or governedTool() (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.

FrameworkMechanismMODIFY at this interception pointExample
langchainZero-code (BaseTool.run/.arun/.invoke/.ainvoke)Yes — dict-shaped input, redaction demoed liveLangChain (Python)
langgraphZero-code (ToolNode._execute_tool_sync/_execute_tool_async, or _run_one/_arun_one on older versions)Yes — nested-envelope adapter, redaction proven liveLangGraph (Python)
crewaiZero-code (CrewStructuredTool.invoke/.ainvoke)Yes — dict-shaped input, redaction demoed liveCrewAI
openai-agentsZero-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 argsOpenAI Agents SDK
pydantic-aiZero-code (FunctionToolset.call_tool)Yes — adapter handles the tool-name-first signature, redaction proven livepydantic-ai
google-adkZero-code (FunctionTool.run_async, BaseTool.run_async fallback)Yes — adapter handles the keyword-only args, redaction proven livegoogle-adk
llamaindexZero-code (FunctionTool.call/.acall)Yes — adapter handles the single positional scalar, redaction proven liveLlamaIndex
autogen (ag2)Zero-code (ConversableAgent.execute_function/.a_execute_function)Yes — adapter decodes the JSON-string func_call args, redaction proven liveautogen (ag2)
dspyZero-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 liveDSPy
smolagentsZero-code (Tool.__call__)Yes — adapter handles the positional scalar, redaction proven livesmolagents
strands-agentsZero-code (ToolExecutor._stream)Yes — nested-envelope adapter (tool_use["input"]), redaction proven liveTest suite only
haystackZero-code (Pipeline.run, AsyncPipeline.run/.run_async — whole-pipeline governance, one resource id per pipeline class)Yes — flat-kwargs adapter, redaction proven liveTest suite only
agnoZero-code (FunctionCall.execute/.aexecute)Yes — redaction proven liveTest suite only
claude-agent-sdkZero-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 liveTest suite only
microsoft-agent-frameworkZero-code (RawAgent.__init__ auto-injects a raxit FunctionMiddleware)Yes — redaction proven liveTest suite only
camelZero-code (FunctionTool.__call__/.async_call)Yes — redaction proven liveTest 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.

FrameworkMechanismMODIFY at this interception pointExample
LangChain.jsZero-code, auto (StructuredTool/BaseTool.prototype invoke/ainvoke/run/arun)Yes — same BaseTool layer as PythonLangChain.js / LangGraph.js
LangGraph.jsZero-code, auto (ToolNode.prototype.runTool — the per-call interception point, not invoke/run, which only see the whole batch)Yescall.args is the tool's real unwrapped argument object at this point, redaction proven live; no defer assertion on this pathLangGraph.js
MastraZero-code, auto (post-wraps Agent.prototype.convertTools)Yesinput is the first parameter to the converted tool's executeMastra
MCP serversOpt-in, per-instanceinstallMcpAutoPatch(server) wraps server.setRequestHandler; must be called before the tools/call handler is registered, or that handler runs ungoverned, silentlyYes — the wrapper threads the redaction into request.params.arguments, redaction proven liveMCP servers
Vercel AI SDKZero-code, auto via registerTelemetry on the SDK's global telemetry registry, governing executeToolNo — fails closed here (input is captured before the wrapper runs); use governedTool() for MODIFYVercel 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.

  • 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).
Coverage claims on this page are generated from the test suite and checked in CI — they can't silently drift.

On this page