Skip to content

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.

  1. Start a Node + Docker deployment.
  2. Sign in to its Console and create an OpenMA API key under API Keys.
  3. 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.
  4. Install the pinned SDK in your application:
Terminal window
npm install openai@7.15.0
export 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.

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" }],
});

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.

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.

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.

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.

ResourceHTTP pathSDK namespace under client.beta
Saved agents/openai/v1/agentsagents
Sessions/openai/v1/agents/sessionsagents.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/subagentsagents.sessions.subagents
Artifacts/openai/v1/agents/sessions/:id/artifactsagents.sessions.artifacts
Environment templates/openai/v1/agents/environments/templatesagents.environments.templates
Environment files/openai/v1/agents/environments/:id/filesagents.environments.files
Vaults and credentials/openai/v1/vaultsagents.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.

Saving a configuration does not prove the runtime can execute it. Unsupported execution settings are rejected at session creation instead of silently ignored.

AreaCurrent Node limit
Advanced hosted environmentsPackage installation, environment variables, setup, input-file injection, plugins, skills, capability directories, and restricted networking configuration are not wired through this adapter
Model and output controlsExplicit reasoning effort / summary, fast service tier, structured output, and non-default verbosity are not supported through this execution path
Tool extensionsTool search, deferred loading, and programmatic tool calling are not connected
MCP extensionsstdio, environment origin, explicit headers / credential selection, required initialization, and request metadata are not connected
SearchCached search and non-default context size are not supported; native web search is unavailable without an environment
SubagentsSingle-level execution; recursive delegation is outside the current baseline
WebhooksDelivery 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.

From the repository root:

Terminal window
pnpm run test:openai-agents # Pinned SDK, HTTP contract, semantic mapping
pnpm run test:openai-agents:node # Node runtime and subagent behavior
pnpm run test:e2e:openai-agents # Production Node process with the official SDK

The 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.