Skip to main content

Sandboxes

A sandbox is where the runtime runs and which files it can touch. You always pass one. There's no default that runs on your host.

Built-in sandboxes​

ConstructorRunsGood for
sandbox.local(path)host subprocess, working directory is pathyour laptop and trusted repos; the runtime has your user's permissions
sandbox.docker(image, mounts=, workdir="/workspace")a container started with the docker CLICI and code you don't trust

sandbox.local strips provider credentials from the child environment (ANTHROPIC_*, OPENAI_*, LITELLM_*, AZURE_*, AWS_*, GEMINI_*, GOOGLE_API_KEY, CODEX_*, CURSOR_*), so the runtime can't pick up your real keys. sandbox.docker reaches the host endpoint through host.docker.internal and needs no Python Docker SDK.

Runtime binaries​

In this release the runtime must already be installed in the sandbox. Build an image with the CLIs you need:

Dockerfile
FROM node:24
RUN npm install -g @anthropic-ai/claude-code @openai/codex opencode-ai
WORKDIR /workspace

For sandbox.local, the binary must be on your PATH. A missing binary raises HarnessInstallFailed naming it. Deep Agents runs in your process and needs nothing in the sandbox.

Setup before the first turn​

Do any setup in your own code. There are no lifecycle hooks.

box = sandbox.docker("my-agents:latest", mounts={"./repo": "/workspace"})
await box.run(["pip", "install", "-e", ".[dev]"])

async with litellm.aagent_session(Harness.CODEX, sandbox=box, model="litellm_proxy/coder") as s:
...

Your own sandbox​

Any object with these members is a sandbox. There's no base class to inherit from.

class Sandbox(Protocol):
workdir: str
async def exec(self, cmd: list[str], *, env=None, cwd=None) -> Process: ...
async def run(self, cmd: list[str], *, env=None, cwd=None, timeout=None) -> CompletedRun: ...
async def read(self, path: str) -> bytes: ...
async def write(self, path: str, data: bytes) -> None: ...
def host_url(self, port: int) -> str: ...
async def which(self, binary: str) -> str | None: ...
async def snapshot(self) -> dict[str, str]: ...
async def close(self) -> None: ...

host_url(port) returns a URL that code inside the sandbox can use to reach a port on your host; the runtime uses it to reach the local model endpoint. snapshot() returns a map of relative path to SHA-256, which is diffed before and after each turn to produce FileChange events.

LiteLLM Enterprise
SSO/SAML, audit logs, spend tracking, multi-team management, and guardrails, built for production.
Learn more →