Run OpenAI Agents SDK agents in a Promigence sandbox
Give an OpenAI Agents SDK SandboxAgent a Promigence sandbox, so its shell and file tools run there instead of on your machine.
Also as markdown: /docs/guides/openai-agents-sdk.md
The OpenAI Agents SDK has a sandbox interface for agents that need a shell and a filesystem. The promigence-openai-agents package implements it with PromigenceSandboxClient, so a SandboxAgent runs its commands and file edits in a Promigence sandbox. Your agent code stays the same. Only the client you pass in changes.
What you need#
- A Promigence API key in
PROMIGENCE_API_KEY. - Python 3.10 or newer.
openai-agents0.22.3. The sandbox base classes are not a stable API upstream, so the package pins>=0.22.3,<0.23.
Install#
pip install promigence-openai-agents
export PROMIGENCE_API_KEY=prom_api_...
export PROMIGENCE_SPEND_CAP=5
Language: bashA minimal example#
Hand the client to the SDK through SandboxRunConfig. The SDK creates the session, starts it, gives the model its exec_command tool and ends the session when the run is over.
import asyncio
from agents import RunConfig, Runner
from agents.sandbox import SandboxAgent, SandboxRunConfig
from promigence_openai_agents import PromigenceSandboxClient, PromigenceSandboxClientOptions
agent = SandboxAgent(name="coder", instructions="Use the shell.")
async def main() -> None:
result = await Runner.run(
agent,
"Write a note to note.txt and read it back.",
run_config=RunConfig(sandbox=SandboxRunConfig(
client=PromigenceSandboxClient(),
options=PromigenceSandboxClientOptions(image="python:3.12-slim", spend_cap=1),
)),
)
print(result.final_output)
asyncio.run(main())
Language: pythonYou can also drive a session yourself:
client = PromigenceSandboxClient()
session = await client.create(options=PromigenceSandboxClientOptions(image="python:3.12-slim", spend_cap=1))
async with session:
result = await session.exec("python", "-c", "print(1 + 1)", shell=False)
print(result.stdout.decode())
await client.delete(session)
Language: pythonPass exactly one of image or snapshot. The first time you name an image, Promigence pins the tag to a digest, builds a snapshot from it and checks it; later sandboxes reuse that snapshot. That build is billed per second while it runs, whether it verifies or not, up to $0.17 at the default small size (quickstart), and the sandboxes that reuse the snapshot pay no further build charge. A snapshot (an id, an alias, or alias@commit7) is used as given. Other useful options: tier (small, medium or large), setup and verify to prepare and check the snapshot, allow_internet_access=False for no network and registry_secret for a private image. Preview URLs are not enabled on Promigence yet, so resolving a port in exposed_ports raises ExposedPortUnavailableError; to reach a port, forward it over SSH with promigence sandbox ssh <sandbox_id> -- -N -L <port>:localhost:<port>.
One sandbox per session#
Each SDK session is one sandbox, a fresh copy of the snapshot. Two runs never share a filesystem.
To carry a session's work across runs, the SDK persists the workspace. With workspace_persistence="tar" (the default) you get a plain tar of the workspace directory, which any other sandbox backend can read. With workspace_persistence="pause" the sandbox itself is parked: it bills no seconds while parked, and client.resume(state) brings the whole machine back, installed packages included. A parked reference only works on Promigence, so pick tar when state has to move.
Setting a spend cap#
spend_cap in PromigenceSandboxClientOptions is the most one sandbox may spend, in US dollars. It falls back to PROMIGENCE_SPEND_CAP, then to your organization's default. If none of the three is set, creating the session fails with a configuration error before anything starts. sandbox_timeout_sec (3600 by default) bounds the sandbox's lifetime.
Cleaning up#
await client.delete(session) ends the sandbox, or parks it when you set pause_on_exit=True. A session parked through workspace_persistence="pause" stays parked even after delete, because the saved reference is the only way back to it. End those with promigence sandbox kill <id> once you no longer need them.
Failures are kept apart. A command that exits non-zero comes back as a result, a timeout raises ExecTimeoutError, and a failure on the Promigence side raises ExecTransportError with context["failure_class"] == "env", marked retryable so the SDK's own retry logic can try again.
What is not supported yet#
- Interactive terminals.
supports_pty()isFalse, sopty_exec_startis unavailable. - Streaming output. A command's output arrives when it finishes.
- Manifest entries that need a cloud mount. Plain files, directories and git artifacts work.
- A JavaScript version. The JS SDK's sandbox contract differs and needs its own package.
- The image needs
/bin/sh, andtarfor the default persistence mode.
Next#
Read the agent briefing for what a sandbox gives an agent, or start with the quickstart.