AG2
Integrate Intutic governance with AG2 (ag2, 1.0+) — a fork/continuation of pre-Microsoft AutoGen with its own, from-scratch architecture.
AG2 vs AutoGen — which page do I want?
AG2 and AutoGen are not the same framework, even though AG2 started life as a fork of the original (pre-Microsoft) AutoGen project and older tutorials/blog posts sometimes still describe it in ConversableAgent/GroupChat terms. That framing is stale: the ag2 package on PyPI (ag2==1.0.2, installed live to write this adapter) no longer imports as autogen at all and shares no API surface with the ConversableAgent/GroupChat shape most AG2/pyautogen tutorials still describe. It is a from-scratch architecture built around typed Events flowing through a Stream, agents (ag2.Agent) driven by a Context, and a BaseMiddleware chain intercepting on_turn/on_llm_call/on_tool_execution/on_human_input.
If your project imports autogen_core/autogen_agentchat/autogen_ext (Microsoft's actively-maintained framework, post-fork), you want the AutoGen page instead — its hook surface (InterventionHandler.on_send) and its coverage gap (AssistantAgent's own tool calls bypass it entirely) are unrelated to AG2's. This page covers ag2's own, distinct BaseMiddleware.on_tool_execution hook.
How it works
The ag2 adapter is detected when pyproject.toml, requirements.txt, or uv.lock declares an ag2 dependency — a boundary-aware regex is used rather than a plain substring test, since "ag2" is short enough to false-positive inside "flag2" or a version string. Like LangGraph, it writes a .env.intutic file with proxy base-URL env vars plus a comment block pointing at the SDK gate.
AG2 is governed on two independent surfaces:
- LLM egress — point the model client's
base_urlat the Intutic proxy (or launch underintutic exec). - Local tool execution — AG2's own
BaseMiddleware.on_tool_executionhook, confirmed by readingag2/middleware/base.py,ag2/tools/executor.py, andag2/tools/final/function_tool.pydirectly against a realag2==1.0.2install.
Setup
1. Initialize Intutic
intutic init2. Route LLM traffic through the proxy
Source .env.intutic, launch under intutic exec, or set base_url explicitly on your LLM client — same as LangGraph.
3. Gate local tool execution (SDK)
pip install intutic-clawde[ag2]BaseMiddleware.on_tool_execution(self, call_next, event, context) is AG2's own documented veto point — the base implementation is the identity middleware (return await call_next(event, context)), and FunctionTool.register folds every entry in middleware=[...] right-to-left over that chain. IntuticMiddleware overrides it:
from ag2 import Agent
from intutic_clawde.gate import Gate, GateConfig, install
from intutic_clawde.gate.adapters.ag2 import IntuticMiddleware
install(Gate(GateConfig()))
agent = Agent("my-agent", middleware=[IntuticMiddleware])Pass the class, not an instance — AG2 constructs one per turn via IntuticMiddleware(event, context), matching MiddlewareFactory's documented Protocol.__call__(self, event, context) -> BaseMiddleware shape. For an explicit Gate (tests, multi-gate processes), use make_intutic_middleware(gate=...) instead:
from intutic_clawde.gate.adapters.ag2 import make_intutic_middleware
agent = Agent("my-agent", middleware=[make_intutic_middleware(gate=my_gate)])On a deny, on_tool_execution returns ToolErrorEvent.from_call(event, exc) — AG2's own constructor for "this tool call failed" — without calling call_next, so the tool body never runs.
4. Trace attribution
from intutic_clawde.gate import intutic_headers
llm = ChatOpenAI(
base_url="http://localhost:4000/v1",
default_headers=intutic_headers(session_id=run_id, harness="ag2"),
)What gets written
Same shape as LangGraph's .env.intutic — proxy URLs plus a pointer at intutic_clawde.gate.adapters.ag2.IntuticMiddleware.
To undo what intutic gates install writes here, run intutic disconnect --harness ag2: each file goes back to what it held before Intutic first wrote it, or is deleted if Intutic created it, and edits you made since are kept. .env.intutic stays while another harness that writes it is still connected. See intutic disconnect.
What the adapter does NOT do
Same structural gaps as every SDK-gated framework — see LangGraph's "What the adapter does NOT do" — plus one AG2-specific note:
- AG2's own dispatcher fails closed on a gate exception; the adapter's catch-all is kept for a clearer message.
IntuticMiddleware.on_tool_executionconverts every exception raised while evaluating the gate (not onlyIntuticGateRefusal) into a returnedToolErrorEvent, the same "fail closed on principle" posturecrewai.pyuses. For CrewAI that catch-all is load-bearing: a realcrewai==1.15.16install was driven and its dispatcher confirmed to swallow any non-HookAbortedhook exception and report the call as allowed. AG2 behaves the opposite way: a tool call driven through AG2's realToolExecutordispatcher (ag2==1.0.2) shows that an exception escapingon_tool_executionbecomes aToolErrorEventand the tool body never runs. The catch-all is therefore not strictly necessary for AG2; it stays so a misconfiguration (no gate installed, or a gate error) reaches the model as[Intutic Governance] BLOCKED: gate error, failing closed: ..., the message the other adapters give, rather than as AG2's traceback of the raw exception. The same line is written to stderr.
Config details
| Property | Value |
|---|---|
| Harness type | ag2 |
| Config file | .env.intutic |
| Detection | ag2 (boundary-aware match — not a plain substring, to avoid false positives like "flag2") in pyproject.toml, requirements.txt, or uv.lock |
| Format | Shell environment variables |
| Write strategy | Atomic (write to .intutic-tmp, then rename) |
| Tool gate | SDK-side (intutic_clawde.gate.adapters.ag2.IntuticMiddleware, a BaseMiddleware.on_tool_execution veto) — fails closed on gate errors (AG2's own dispatcher also fails closed); no sync-daemon hook file |