OpenAI Agents SDK
Native sandbox providers for the OpenAI Agents SDK, Python and TypeScript.
Native inis.run sandbox providers for the OpenAI Agents SDK's SandboxAgent path — agents.sandbox in Python, @openai/agents-core/sandbox in TypeScript.
Beta, upstream and here. The Agents SDK documents its own sandbox surface as beta. Verified against openai-agents==0.19.1 (Python) and @openai/agents-core==0.14.1/@openai/agents==0.14.1 (TypeScript).
Install
pip install inis-openai-agents # Python
npm install @inis-run/openai-agents # TypeScript / NodeQuickstart: attached (recommended)
One inis.run session for the whole conversation, owned by your app.
from inis import AsyncClient
from agents import Runner, RunConfig
from agents.run_config import SandboxRunConfig
from agents.sandbox import SandboxAgent
from inis_openai_agents import InisSandboxSession
client = AsyncClient()
inis_session = await client.sessions.create(egress_default="deny")
sandbox_session = InisSandboxSession.wrap(inis_session)
agent = SandboxAgent(name="assistant", model="gpt-5.1")
run_config = RunConfig(sandbox=SandboxRunConfig(session=sandbox_session))
try:
result = await Runner.run(agent, "list files in /workspace", run_config=run_config)
result2 = await Runner.run(agent, "now write hello.txt", run_config=run_config)
finally:
await inis_session.destroy() # this app owns the session; the SDK never willimport { Client } from "@inis-run/sdk";
import { run } from "@openai/agents";
import { SandboxAgent } from "@openai/agents-core/sandbox";
import { InisSandboxSession } from "@inis-run/openai-agents";
const client = new Client();
const inisSession = await client.sessions.create({ egress: { mode: "deny" } });
const sandboxSession = InisSandboxSession.wrap(inisSession);
const agent = new SandboxAgent({ name: "assistant", model: "gpt-5.1" });
try {
const result = await run(agent, "list files in /workspace", {
sandbox: { session: sandboxSession },
});
} finally {
await inisSession.destroy(); // this app owns the session; the SDK never will
}The Agents SDK's own sandbox runtime never destroys a session passed via sandbox.session — that lifecycle stays entirely host-controlled: one persistent session across a whole conversation, not one sandbox per model turn.
Owned: one run at a time
The SDK can also create and destroy a fresh session for a single Runner.run()/run() call:
from agents.run_config import SandboxRunConfig
from inis_openai_agents import InisSandboxClient, InisSandboxClientOptions
run_config = RunConfig(
sandbox=SandboxRunConfig(
client=InisSandboxClient(),
options=InisSandboxClientOptions(egress_default="deny"),
)
)
result = await Runner.run(agent, "...", run_config=run_config)To resume a conversation in a later process, pass result._sandbox_resume_state back in — the SDK creates a new session and restores the workspace from a tar snapshot; it does not reattach to the original session ID. Prefer the attached path above for a real multi-turn agent.
import { InisSandboxClient, InisSandboxClientOptions } from "@inis-run/openai-agents";
const result = await run(agent, "...", {
sandbox: {
client: new InisSandboxClient(),
options: { egressDefault: "deny" } satisfies InisSandboxClientOptions,
},
});InisSandboxClientOptions also accepts connections — inline Connections to create alongside the session.
What it does
exec/execCommand(shell), with a real interactive PTY (tty: true) andwriteStdinfor a running session- File read/write, symlink-safe path validation (Python);
readFileand plainfile/dirmanifest entries (TypeScript) - Exposed ports, via
session.expose() - Owned/attached lifetime split — see above
- Snapshot persist/hydrate (owned path's resume) — tar+base64 over
exec()
Notes
Docker-volume-mount manifests are not implemented (supports_docker_volume_mounts() stays False in Python; nested Dir/local_file/local_dir/git_repo manifest entries throw in TypeScript). True remote cancellation of an in-flight command is not implemented in either language — the sandbox contract is timeout-only, with no abort handle to route through.
InisSandboxSession.exec() returns a result type extending upstream's own with one added field, truncated — set directly from inis.run's own per-stream capture-limit signal (never derived from stream content, so it can't be spoofed by a guest command printing fake marker text).
Fallback: plain function tools
Not using SandboxAgent? Use an ordinary Agent with function tools against the Python or TypeScript SDK directly.