OpenAI Agents API
The Node server exposes the OpenAI Agents API at /openai/v1, alongside
the Claude-compatible API at /v1. The adapter uses OpenMA’s
existing agents, sessions, encrypted credentials, and event history. Models
execute through OpenMA’s runtime and your configured provider.
Connect the SDK
Section titled “Connect the SDK”- Start a Node + Docker deployment.
- Sign in to its Console and create an OpenMA API key under API Keys.
- Add a Model Card with your provider credentials. Use its configured model
identifier in
agent.model. The client API key authenticates to OpenMA; the Model Card supplies the upstream model credential. - Install the pinned SDK in your application:
npm install openai@7.15.0export OPENMA_API_KEY="oma_..."export OPENMA_MODEL="your-configured-model"import OpenAI from "openai";
const client = new OpenAI({ apiKey: process.env.OPENMA_API_KEY, baseURL: "http://localhost:8787/openai/v1",});
const session = await client.beta.agents.sessions.create({ agent: { model: process.env.OPENMA_MODEL!, instructions: "Be concise.", }, environment: { type: "none" }, input: "Hello",});console.log(session.id);Creation accepts the initial input and starts execution asynchronously. Use
sessions.retrieve(session.id) to inspect status and sessions.items.list(session.id)
to read durable output. Items include messages and tool activity; Turns expose
execution status. A saved agent can be referenced with agent_id instead of
inline configuration; sessions retain their original agent snapshot after edits.
environment: { type: "none" } allocates no physical sandbox. It supports text
and client function tools; shell commands and environment file operations need
a connected sandbox. It does not run the agent on OpenAI’s hosted infrastructure.
Stream a turn
Section titled “Stream a turn”To stream the initial execution, add stream: true to the creation request:
const events = await client.beta.agents.sessions.create({ agent: { model: process.env.OPENMA_MODEL!, instructions: "Be concise." }, environment: { type: "none" }, input: "Explain durable sessions in two sentences.", stream: true,});for await (const event of events) { console.log(event);}For a later turn on an idle session, the SDK helper subscribes before sending input, avoiding a race with the first response event:
for await (const event of client.beta.agents.sessions.stream(session.id, { input: "Give me a concrete example.",})) { console.log(event);}The event stream is live-only. After a disconnect, retrieve Items and Turns
for missed history, then subscribe to new events with sessions.events.stream.
There is no OpenAI replay cursor in this pinned contract. Text-done events contain
the committed full text; clients should not depend on synthetic token deltas.
Aborting a subscription stops listening and leaves execution running. To cancel execution, send an explicit input event:
await client.beta.agents.sessions.events.create(session.id, { events: [{ type: "agent.session.input.cancel" }],});Client function tools
Section titled “Client function tools”Declare function tools on the agent. When a tool needs your application’s result,
the session enters requires_action; its pending calls are in required_actions.
The pending action survives a Node process restart when storage is retained.
For interactive applications, sessions.stream accepts toolHandlers keyed by
function name and returns their results automatically. For a backend that handles
results separately, retrieve the pending action and submit its identifiers:
const waiting = await client.beta.agents.sessions.retrieve(session.id);for (const action of waiting.required_actions) { if (action.type !== "function_call" || action.name !== "lookup") continue;
// Replace this sample result with your application's lookup implementation. await client.beta.agents.sessions.events.create(session.id, { events: [{ type: "agent.session.input.tool_result", turn_id: action.turn_id, call_id: action.call_id, success: true, output: "42", }], "Idempotency-Key": `lookup-result-${action.call_id}`, });}A function result resumes the same unfinished Turn. Retrying an accepted input with the same idempotency key and payload does not dispatch it again. Reusing a key with different input, or racing to consume a pending call already changed by another request, returns a conflict. Keep external function side effects idempotent in your application as well.
Subagents
Section titled “Subagents”Enable delegation in the agent configuration:
const delegated = await client.beta.agents.sessions.create({ agent: { model: process.env.OPENMA_MODEL!, instructions: "Delegate independent research tasks when useful.", multi_agent: { enabled: true, max_concurrent_subagents: 2 }, }, environment: { type: "none" }, input: "Ask two subagents to propose different approaches, then compare them.",});const children = await client.beta.agents.sessions.subagents.list(delegated.id);console.log(children.data); // Children appear after the agent creates them.The main agent receives tools to create, send input, wait, interrupt, close, and resume children. Each child has independent conversation history and, when an environment is connected, shares the parent’s sandbox and files. Child completion finishes its turn but leaves it available for further input. Closing and resuming change its lifecycle; they do not create a new conversation identity.
The current execution baseline is one level of delegation: children do not
receive tools to create more children. They inherit MCP and search configuration,
but client function tools stay on the main agent. Query child history through
sessions.subagents.items and sessions.subagents.turns.
Environment files and artifacts
Section titled “Environment files and artifacts”For a connected hosted environment, file writes and directory listings use the
actual sandbox provider. A disconnected environment returns an error. The
openai_hosted environment name is part of the SDK contract; OpenMA still uses
its own configured runtime.
Files deliberately written under /workspace/outputs are published as
immutable Artifacts after the corresponding root Turn completes successfully.
Artifacts record their session, environment, turn, and source path. Ordinary
uploads do not automatically become Artifacts. Publication is a separate step
following execution completion, so an empty artifact list is not proof that the
turn is still running; check the server diagnostics if publication fails.
const artifacts = await client.beta.agents.sessions.artifacts.list(session.id);for (const artifact of artifacts.data) { const response = await client.beta.agents.sessions.artifacts.content(artifact.id, { session_id: session.id, }); const bytes = await response.arrayBuffer(); console.log(artifact.id, bytes.byteLength);}This path differs from the Claude-compatible OpenMA output extension at
/v1/sessions/:id/outputs, which uses /mnt/session/outputs.
Resource map
Section titled “Resource map”All paths below are relative to the Node origin. The SDK baseURL already
includes /openai/v1; do not add another /v1 when constructing the client.
| Resource | HTTP path | SDK namespace under client.beta |
|---|---|---|
| Saved agents | /openai/v1/agents | agents |
| Sessions | /openai/v1/agents/sessions | agents.sessions |
| Events, Items, Turns | /openai/v1/agents/sessions/:id/{events,items,turns} | agents.sessions.events, .items, .turns |
| Subagents and history | /openai/v1/agents/sessions/:id/subagents | agents.sessions.subagents |
| Artifacts | /openai/v1/agents/sessions/:id/artifacts | agents.sessions.artifacts |
| Environment templates | /openai/v1/agents/environments/templates | agents.environments.templates |
| Environment files | /openai/v1/agents/environments/:id/files | agents.environments.files |
| Vaults and credentials | /openai/v1/vaults | agents.vaults |
Use an OpenMA API key as the SDK apiKey; the SDK sends the bearer credential.
Resources remain scoped to the authenticated workspace. Keep Claude and OpenAI
request bodies and event types separate even where resource names overlap.
Execution limits
Section titled “Execution limits”Saving a configuration does not prove the runtime can execute it. Unsupported execution settings are rejected at session creation instead of silently ignored.
| Area | Current Node limit |
|---|---|
| Advanced hosted environments | Package installation, environment variables, setup, input-file injection, plugins, skills, capability directories, and restricted networking configuration are not wired through this adapter |
| Model and output controls | Explicit reasoning effort / summary, fast service tier, structured output, and non-default verbosity are not supported through this execution path |
| Tool extensions | Tool search, deferred loading, and programmatic tool calling are not connected |
| MCP extensions | stdio, environment origin, explicit headers / credential selection, required initialization, and request metadata are not connected |
| Search | Cached search and non-default context size are not supported; native web search is unavailable without an environment |
| Subagents | Single-level execution; recursive delegation is outside the current baseline |
| Webhooks | Delivery and signing are not certified by this SDK contract suite |
These limits describe this adapter’s current execution path. Native OpenMA features may support settings that are not yet mapped from the OpenAI contract. See the implementation report for the detailed evidence and remaining work.
Verification
Section titled “Verification”From the repository root:
pnpm run test:openai-agents # Pinned SDK, HTTP contract, semantic mappingpnpm run test:openai-agents:node # Node runtime and subagent behaviorpnpm run test:e2e:openai-agents # Production Node process with the official SDKThe maintained E2E suite uses real Node / SQL persistence and a controlled local model service, including restart recovery, function continuation, streaming, and cancellation. It does not certify every physical sandbox provider or real model instruction following. See the E2E guide.