novitalabs/openai-agents-api-executor
TypeScript
Captured source
source ↗novitalabs/openai-agents-api-executor
Description: Novita Sandbox as a self-hosted execution environment for the OpenAI Agents API
Language: TypeScript
Stars: 2
Forks: 0
Open issues: 0
Created: 2026-09-11T04:01:09Z
Pushed: 2026-09-11T09:59:21Z
Default branch: main
Fork: no
Archived: no
README:
Novita Sandbox
Run sandbox tools in Novita while OpenAI runs the agent and maintains session state. OpenAI hosts the Codex harness; the sandbox runs codex exec-server, which dials out to OpenAI to receive commands and return results. Nothing listens on an inbound port.
application ──create session──▶ Agents API ◀──outbound ws── codex exec-server │ (Novita sandbox) └──start sandbox + executor───────────────────────────────────┘
Choose a provisioning mode:
- [Application-managed](#application-managed): your application creates the
sandbox and connects its executor directly.
- [Webhook-managed](#webhook-managed): a controller starts or reconnects
sandboxes from OpenAI webhooks, so your application never touches the sandbox SDK.
Both are implemented here. Sandbox lifecycle compares them.
Attaching an agent to a sandbox you already own is the common case in either mode; npm run example is that path as running code.
Before you begin
cp .env.example .env npm install
| Variable | Where it comes from | Scope | | --- | --- | --- | | NOVITA_API_KEY | Novita console | template build, sandbox lifecycle | | OPENAI_API_KEY | platform.openai.com/api-keys | your application; never enters the sandbox | | OPENAI_EXECUTOR_API_KEY | platform Agents tab → environment keys | the only credential that enters the sandbox |
The application key needs api.agents.read, api.agents.write, and api.responses.write. The executor key needs every other permission set to None — it can connect environments and nothing else, which is what makes it safe for agent-generated code to read. Both keys must belong to the same organization, project, and user or service account, or the session and its executor will not match up.
See executor authentication for OpenAI's own description of the split.
Application-managed
Your application owns the sandbox for the whole session: create it, start the executor, send input, then release both when finished. Two steps to a working session — the first needs only NOVITA_API_KEY.
1. Create a sandbox
The executor template is published, so there is nothing to build:
npm install -g novita-sandbox-cli export NOVITA_API_KEY=... novita sbx create openai-agents-api-executor --long-running --timeout 720h
Keep the sandbox id it prints — the agent attaches to this sandbox, and its files outlive any single turn.
2. Run one real session
npm run e2e
It creates a self-hosted session, starts a sandbox and the executor, waits for the environment to connect, asks the agent to write a file, and then checks the sandbox filesystem for that file rather than trusting the agent's summary. It deletes the session and kills the sandbox on the way out, including on failure.
A pass looks like this:
1. Creating self-hosted session session=sess_... environment=env_... 2. Starting sandbox and executor sandbox=i... 3. Waiting for the environment to connect agent.session.environment.pending agent.session.environment.connected 4. Sending a task 5. Checking the workspace for the written file file contents: novita executor ok Timing breakdown: ...ms session created ...ms executor started ...ms environment connected ...ms input accepted ...ms file observed PASS
The timing breakdown is the point of the exercise: the API allows five minutes for an input-time connection, and this shows which phase actually consumes it.
If it fails
The failing step number tells you where to look.
| Fails at | Likely cause | | --- | --- | | 1, 401/403 | Application key missing api.agents.* or api.responses.write. | | 1, 400 invalid_beta | The OpenAI-Beta: agents=v1 header is missing. openai@7.15.0 sends it; an older SDK does not. | | 2 | Template missing or not built — run npm run verify first. | | 3, stuck then timing out | Executor never registered. Read /tmp/codex-executor.log in the sandbox. Usually the executor key is wrong, or belongs to a different project than the application key. | | 3, environment.failed | Same log. Registration failures appear there, not in any HTTP status. | | 5, file never appears | The environment connected but commands are failing. Read the executor log; this is the case that would expose an exec-server filesystem-sandbox problem inside a microVM. |
The sandbox is killed on failure, so to inspect one, re-run with the teardown commented out or reproduce the step by hand against a sandbox you keep alive.
What each step does
The three commands above wrap the sequence below. Read it when adapting the flow to your own application rather than running the examples.
Build the executor template
The published template bakes in codex exec-server at a pinned version, so an executor boots with the CLI already present and every sandbox runs the same image. Rebuild only to change what is in it — a newer Codex, extra tooling:
npm run build:template # optional; about 1m15s cold, 30s warm
Two details matter if you do build your own:
- Pin the Codex version.
@openai/codex@alphafloats. Two templates built a
week apart would ship different executors and diverge with no visible cause.
- Do not set a start command. A template start command runs once at build
time and is snapshotted, so it cannot see the environment id, which only exists once a session is created. Start the executor at runtime instead.
Verify the image before using it in a session. This needs only NOVITA_API_KEY:
npm run verify
Create a self-hosted session
import OpenAI from 'openai'
const client = new OpenAI()
const session = await client.beta.agents.sessions.create({
agent: { model: 'gpt-6-astra', instructions: 'You are a coding assistant.' },
environment: { type:...Excerpt shown — open the source for the full document.