@obversa/engine-openai-agents makes an OpenAI Agents SDK agent one
engine: a node the runtime can review, send back with findings, pause,
resume and record. The agent’s tools, handoffs and guardrails stay the
SDK’s.
Install
@obversa/obversa; install it on its own, beside the SDK.
Requirements
- Node.js 22.12 or later.
@openai/agents0.18, withzod4, which the SDK needs beside it.- Whatever your agent needs to run, such as an OpenAI API key.
A seat for a team
Build the agent the way you already do. This one has its own instructions, its own model and its own tool, which saves a file:examples/engine-openai-agents.ts (excerpt)
openaiAgent(agent) makes it a seat, the way claude('claude-sonnet-4-5')
makes a Claude seat. Give the seat a role in a workflow(). Here the agent
writes, Codex reviews, and a judge decides whether another round runs:
examples/engine-openai-agents.ts (excerpt)
What crosses the boundary
Each attempt is one call to the SDK’s runner for the agent. The engine sends three things:- The prompt of the request.
- The system text, when the request has some. It goes as one system message before the prompt, or, when the request replaces the system prompt, as the instructions of a copy of the agent.
- An abort signal that fires when the run aborts or the request’s timeout passes.
- The agent’s final output, as the result. A final output that is not text is returned as JSON.
- The usage the SDK reports, or
unknownwhen the SDK counted no model request. - The seat’s identity: adapter
openai-agents, with the provider and model the agent is built with.
run. To run the agent with a
Runner you configured, pass it: openaiAgent(agent, { runner }).
What stays the SDK’s
The agent’s tools, handoffs and guardrails stay the SDK’s. The engine does not turn the agent’s tools into Obversa tools, and it passes no working directory to the agent. The engine passes no session. The SDK takes a session as an option of each run, not as part of the agent, so each attempt starts without session history. To give the agent a session, pass a runner that adds it to the options the engine gives:openaiAgent(agent, { runner: { run: (a, input, options) => run(a, input, { ...options, session }) } }),
with run from @openai/agents.
So a step’s declared Obversa tools and read-only mode do not limit the
agent. It uses the tools it was built with, wherever those tools act. In
the example, the agent’s own tool saves the page into the directory the run
starts in.
An agent that hands off to another agent is still one engine attempt. The
review, send-back, pause, resume and record around the agent work as for
any engine. An OpenAI agent seat declares no Obversa tools, so the runtime
does not accept it as a reviewer: a reviewer must declare a tool that reads
the workspace.
Identity
The seat reads the model name the agent is built with. The SDK sends a model name to its default model provider, OpenAI, so the provider isopenai. An agent with no model has the SDK’s default model. The model
family is the model name up to its first hyphen, so gpt-5 is the family
gpt.
When the agent holds a model object, the SDK does not expose the model’s
name, and openaiAgent() throws. Name the model yourself:
openaiAgent(agent, { model: 'openai/gpt-5' }). Do the same when the model
is set somewhere else, such as on a Runner.
Errors
- A rate limit: a thrown error with status 429. The provider’s
retry-afterheader, when there is one, is kept. - A quota: a 429 with the error code
insufficient_quota. - Everything else goes through the shared classification, so an authentication or billing message is typed as one.
invalid-configwhen the run stops to wait for a tool approval: the engine cannot give the approval.abortedwhen the run aborts, andtimeoutwhen the request’stimeoutMs, plustimeoutGraceMswhen set, passes.
What the run did
Run offline, with the agent’s model replaying its turns fromwriter.json, a stand-in for the Codex command line tool, and the judge’s
answers replayed from judge.json, the example printed:
Output
save_file tool, then
replied. The first reader pass found two blocks, so the page went back to
the agent. The second found one nice-to-have, and the judge chose
holds. The record holds both runs of the agent with the usage the SDK
reported for each.
API
openaiAgent: the seat for a team workflow.OpenAIAgentSeatandOpenAIAgentSeatOptions: its types. A seat for a team.OpenAIAgentsEngine: the engine the seat holds.OpenAIAgentsRunner: the part of the SDK’s runner the engine calls. The SDK’sRunnerfits it.
Next steps
- Runtime: the roles a seat fills.
- Know when to stop: the stopping rule this example uses.
- API: the engine contract the plugin implements.