OpenAI Agents
Wrap OpenAI Agents tools through Aira's policy engine. authorize() runs before the tool body. Denied calls never run. Python and TypeScript.
Kind: gate · Pre-execution gate: yes · Peer dep: openai-agents (Python) or @openai/agents (TypeScript)
What this integration actually does
OpenAI Agents SDK supports guardrail functions and explicit tool wrappers that run BEFORE a tool executes. Either path can throw to abort, so wrapping the tool is a real authorization gate.
AiraGuardrail.wrap_tool() (Python) and AiraGuardrail.wrapTool() (TypeScript) take a tool function, call aira.authorize() before invoking it, and aira.notarize() after. Denied calls throw AiraToolDenied (Python) or an Error (TypeScript). Failed calls report outcome="failed" so the action transitions correctly in Aira.
Install
pip install aira-sdk[openai-agents]npm install aira-sdk
# peer dep
npm install @openai/agentsFull example — gated tool call
from aira import Aira
from aira.extras.openai_agents import AiraGuardrail
from agents import Agent, Runner, function_tool
aira = Aira(api_key="aira_live_...")
guardrail = AiraGuardrail(
client=aira,
agent_id="payments-agent",
model_id="gpt-4o",
)
def send_wire_transfer_impl(amount: float, to: str) -> str:
"""Raw tool function — side-effect happens here."""
return f"Wire sent: €{amount} to {to}"
# Wrap it — this is where authorize() runs
send_wire_transfer = function_tool(
guardrail.wrap_tool(send_wire_transfer_impl, "send_wire_transfer")
)
agent = Agent(
name="payments",
instructions="You are a payments agent.",
tools=[send_wire_transfer],
)
result = Runner.run_sync(agent, "Send €75,000 to vendor-x")import { Aira } from "aira-sdk";
import { AiraGuardrail } from "aira-sdk/extras/openai-agents";
import { Agent, run } from "@openai/agents";
const aira = new Aira({ apiKey: "aira_live_..." });
const guardrail = new AiraGuardrail(aira, "payments-agent", {
modelId: "gpt-4o",
strict: false,
});
async function sendWireTransferImpl(args: { amount: number; to: string }) {
return `Wire sent: €${args.amount} to ${args.to}`;
}
// wrapTool() returns the wrapped execute function
const sendWireTransfer = {
name: "send_wire_transfer",
description: "Send a wire transfer",
parameters: {
type: "object",
properties: {
amount: { type: "number" },
to: { type: "string" },
},
required: ["amount", "to"],
},
execute: guardrail.wrapTool(sendWireTransferImpl, "send_wire_transfer"),
};
const agent = new Agent({
name: "payments",
instructions: "You are a payments agent.",
tools: [sendWireTransfer],
});
const result = await run(agent, "Send €75,000 to vendor-x");What happens when a policy denies
- The model decides to call
send_wire_transfer. - The wrapper calls
aira.authorize()before the tool body runs. - If the policy engine denies, the wrapper throws
AiraToolDenied(Python) or anError(TypeScript). - OpenAI Agents catches the exception, marks the tool call as failed, and surfaces the result to the model.
- The real transfer function never runs. The
PolicyEvaluationrow is persisted for audit.
strict mode (TypeScript only — Python always fails closed)
new AiraGuardrail(aira, "payments-agent", { strict: true });strict: false(default): ifauthorize()fails due to network/5xx, warn and let the tool run.strict: true: throw on any authorize failure. Use for deployments where missing a signature = don't act.
POLICY_DENIED always throws regardless.
When to use this vs calling authorize() inline
Use wrap_tool() when... | Call authorize() inline when... |
|---|---|
| You want every tool on the agent gated, no tool-body changes | You want to gate a specific branch inside a tool |
| You're building a standard OpenAI Agents agent | You need the action_uuid directly in the tool body |
You're OK with simple tool_call action types | You want custom action_type per tool or branch |
Known limits
- Notarize is non-blocking. Notarize failures are logged but don't wedge the agent. The receipt is missing, the agent keeps running.
- Input guardrails are not yet wired.
AiraGuardrailcurrently wraps tools but doesn't register as aninputGuardrail— if you want to gate the whole run before the model even sees the prompt, callaira.authorize(action_type="agent_run", ...)yourself at the top of your runner.
Proof it works
- Python:
tests/test_extras_openai_agents.py(132 lines) - TypeScript:
tests/extras-openai-agents.test.ts(175 lines, 14 tests) - Pinned in both SDKs'
INTEGRATIONSregistries askind="gate",pre_execution_gate=True.