Skip to content

Guides

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-agents 0.22.3. The sandbox base classes are not a stable API upstream, so the package pins >=0.22.3,<0.23.

Install#

Terminal
pip install promigence-openai-agents
export PROMIGENCE_API_KEY=prom_api_...
export PROMIGENCE_SPEND_CAP=5
Language: bash

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

Python
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: python

You can also drive a session yourself:

Python
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: python

Pass 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() is False, so pty_exec_start is 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, and tar for the default persistence mode.

Next#

Read the agent briefing for what a sandbox gives an agent, or start with the quickstart.

Get started

Leave your work email and we will get you started.

See our Privacy Policy for how we use your email.