A SandboxAgent is a specialized agent for tasks that require isolated file-system access; it extends Agent with sandbox-specific fields (default_manifest, capabilities, run_as, base_instructions) while keeping transport details transport-agnostic via RunConfig. The sandbox manifests, environment resolution, and entry types form a declarative system for configuring workspace mounts, file permissions, environment variables, and user identity — all validated against exposure policies and deserialized polymorphically at runtime.
The recommended pattern is to use a plain Agent plus Runner for prompt/tool/conversation tasks, and switch to SandboxAgent only when the agent must inspect or modify real files in an isolated workspace.[1]
SandboxAgent, defined in src/agents/sandbox/sandbox_agent.py, is a subclass of Agent[TContext] that adds sandbox-specific fields (default_manifest, base_instructions, capabilities, run_as) to the base Agent interface.[2] Runtime transport details — sandbox client, client options, and live session — are NOT stored on SandboxAgent itself; they are provided at run time through RunConfig(sandbox=...), keeping the agent declaration transport-agnostic.[2]
SandboxAgent.default_manifest holds the default sandbox Manifest applied when Runner creates a new session; it defaults to None.[2] SandboxAgent.capabilities is a Sequence[Capability] (defaulting to Capabilities.default()) whose entries can mutate the manifest, add instructions, and expose tools.[2] SandboxAgent.run_as sets the user identity presented to model-facing sandbox tools such as shell, file reads, and patches; it accepts a User object, a plain string, or None.[2] SandboxAgent.base_instructions overrides the SDK sandbox base prompt and accepts a string, an async or sync callable (RunContextWrapper, Agent) -> str | None, or None; most callers should use instructions instead.[2]
SandboxAgent.__post_init__ coerces a raw dict passed as default_manifest into a typed Manifest via _coerce_manifest, so callers may supply either type.[2] SandboxAgent.__post_init__ coerces a raw dict passed as run_as into a User Pydantic model via coerce_pydantic_config, so callers may pass a plain dict.[2] SandboxAgent.__post_init__ raises TypeError if base_instructions is not a string, callable, or None, giving an explicit error message that names the invalid type.[2] SandboxAgent.__post_init__ raises TypeError if run_as is not a string, User, or None, giving an explicit error message that names the invalid type.[2] SandboxAgent._sandbox_concurrency_guard is an internal field excluded from __init__ and repr, used to guard concurrent sandbox access; it is not part of the public API.[2]
Manifest in src/agents/sandbox/manifest.py is a Pydantic BaseModel with a fixed version: Literal[1] = 1, a root defaulting to /workspace, and fields for entries, environment, users, groups, extra_path_grants, and remote_mount_command_allowlist.[3] Manifest.remote_mount_command_allowlist defaults to a fixed list of safe read/write commands: ls, find, stat, cat, less, head, tail, du, grep, rg, wc, sort, cut, cp, tee, echo, mkdir, rm.[3] Manifest entries are deserialized from raw mappings using BaseEntry.parse(entry) inside the _parse_entries field validator, supporting polymorphic entry types.[3] JSON serialization of Manifest entries converts Path keys to their POSIX string representation via .as_posix(), ensuring cross-platform compatibility in serialized output.[3]
Manifest actively rejects any input containing mount-credential-exposure policy keys (e.g. mount_credential_exposure_policy, inContainerMountCredentialExposureAllowedPaths) via a model_validator, enforcing that this policy can only be set on a trusted Manifest instance in code, not via untrusted input.[3]
EnvValue is an abstract Pydantic BaseModel in src/agents/sandbox/manifest.py serving as an extension point for environment variable sources; concrete subtypes register themselves via __pydantic_init_subclass__ under a string type discriminator.[3] EnvValue.parse() accepts an existing EnvValue instance unchanged, a plain {"value": str} mapping (coerced to StrEnvValue), or a typed mapping dispatched to the registered subclass; it raises ValueError for unknown type strings.[3] StrEnvValue is the built-in EnvValue subtype registered under type="str" in src/agents/sandbox/manifest.py; its resolve() simply returns self.value as a string.[3] EnvEntry in src/agents/sandbox/manifest.py wraps an EnvValue with optional description and ephemeral (default False) metadata fields.[3] Environment.resolve() in src/agents/sandbox/manifest.py concurrently resolves all env values using gather_with_cancel rather than a bare asyncio.gather, so that if one lookup fails the remaining sibling coroutines are cancelled rather than left running.[3]
UnixLocalSandboxClient is supported only on macOS and Linux; on Windows, DockerSandboxClient (with the openai-agents[docker] extra) or a hosted sandbox client must be used instead.[4] SandboxConcurrencyLimits in src/agents/run_config.py defaults to 4 concurrent manifest entries (manifest_entries=4) and 4 concurrent file copies per local_dir entry (local_dir_files=4); either can be set to None to remove the limit.[5] SandboxArchiveLimits in src/agents/run_config.py defaults to a 1 GiB input size limit, a 4 GiB extracted-bytes limit, and a 100,000-member limit; all three can be set to None to disable that specific limit.[5]
Example: minimal sandbox agent run using SandboxAgent, UnixLocalSandboxClient, and Runner.run_sync with a SandboxRunConfig.
from agents import Runner
from agents.run import RunConfig
from agents.sandbox import Manifest, SandboxAgent, SandboxRunConfig
from agents.sandbox.entries import GitRepo
from agents.sandbox.sandboxes import UnixLocalSandboxClient
agent = SandboxAgent(
name="Workspace Assistant",
instructions="Inspect the sandbox workspace before answering.",
default_manifest=Manifest(entries={"repo": GitRepo(repo="openai/openai-agents-python", ref="main")}),
)
result = Runner.run_sync(
agent,
"Inspect the repo README and summarize what this project does.",
run_config=RunConfig(sandbox=SandboxRunConfig(client=UnixLocalSandboxClient())),
)
print(result.final_output)
Sources