Sandboxes provide isolated execution environments for deep agents that need to run shell commands, execute code, or interact with external systems safely.
Learn more: For patterns and deployment considerations, see the Backends documentation.
Sandboxes are essential when your agent needs to:
execute tool) in a controlled environmentDeep agents ships with built-in integrations for several sandbox providers:
| Provider | Package | Working Directory | Description |
|---|---|---|---|
| Modal | deepagents_cli.integrations |
/workspace |
Serverless sandbox environments |
| Daytona | deepagents_cli.integrations |
/home/daytona |
Cloud development environments |
| Runloop | deepagents_cli.integrations |
/home/user |
Devbox-based sandboxes |
| LangSmith | deepagents_cli.integrations |
/tmp |
LangSmith-managed sandboxes |
The simplest way to create a sandbox is via the factory function:
from deepagents_cli.integrations.sandbox_factory import create_sandbox
with create_sandbox(
provider="modal", # or "daytona", "runloop", "langsmith"
sandbox_id=None, # None to create new, or ID to reuse existing
setup_script_path=None, # Optional setup script to run on creation
) as sandbox:
result = sandbox.execute("echo hello")
print(result.output)
Configure your agent to use a sandbox by passing it as the backend:
from deepagents_cli.agent import create_cli_agent
from deepagents_cli.integrations.sandbox_factory import create_sandbox
with create_sandbox(provider="modal") as sandbox:
agent, backend = create_cli_agent(
model="anthropic:claude-sonnet-4-5-20250929",
assistant_id="my-agent",
sandbox=sandbox,
sandbox_type="modal",
)
When a sandbox backend is configured, the agent gains access to the execute tool for running shell commands and adapts its system prompt to the remote environment.
All sandbox implementations extend BaseSandbox which provides default implementations for file operations via shell commands. Subclasses only need to implement execute(), upload_files(), and download_files():
from deepagents.backends import BaseSandbox, ExecuteResponse
class MySandbox(BaseSandbox):
async def execute(self, command: str) -> ExecuteResponse:
"""Run command in isolated environment."""
return ExecuteResponse(
exit_code=0,
output="...",
)
async def upload_files(
self,
files: list[tuple[str, bytes]],
) -> list[FileUploadResponse]:
"""Upload files to sandbox."""
pass
async def download_files(
self,
paths: list[str],
) -> list[FileDownloadResponse]:
"""Download files from sandbox."""
pass
@property
def id(self) -> str:
"""Unique sandbox identifier."""
return "my-sandbox-id"
BaseSandbox provides the following operations out of the box (implemented via shell commands through execute()):
| Method | Description |
|---|---|
read |
Read file contents with line numbers, offset, and limit |
write |
Create new files (fails if file exists) |
edit |
String replacement editing with replace_all support |
ls_info |
List directory contents with file metadata |
grep_raw |
Literal string search with optional glob filtering |
glob_info |
Find files matching glob patterns |
The ExecuteResponse type provides structured output from command execution:
| Field | Type | Description |
|---|---|---|
exit_code |
int |
Exit code from the command (0 = success) |
output |
str |
Combined stdout and stderr output |
truncated |
bool |
Whether the output was truncated |
SandboxProvider manages sandbox lifecycle — creating, listing, and deleting sandboxes:
from deepagents.backends.sandbox import SandboxProvider
class MyProvider(SandboxProvider):
def get_or_create(
self,
sandbox_id: str | None = None,
**kwargs,
) -> SandboxBackendProtocol:
"""Create a new sandbox or connect to existing one."""
pass
def list(self, cursor=None, **kwargs) -> SandboxListResponse:
"""List available sandboxes."""
pass
def delete(self, sandbox_id: str, **kwargs) -> None:
"""Delete a sandbox (idempotent)."""
pass
All provider methods also have async versions: aget_or_create(), alist(), and adelete().
from deepagents_cli.integrations.modal import ModalProvider
provider = ModalProvider(app_name="my-app")
sandbox = provider.get_or_create(
sandbox_id=None, # None for new sandbox
workdir="/workspace",
timeout=180, # Startup timeout in seconds
)
Requires the Modal SDK to be installed and configured.
from deepagents_cli.integrations.daytona import DaytonaProvider
provider = DaytonaProvider(
api_key="...", # Or set DAYTONA_API_KEY env var
timeout=180,
)
sandbox = provider.get_or_create()
from deepagents_cli.integrations.runloop import RunloopProvider
provider = RunloopProvider(
api_key="...", # Or set RUNLOOP_API_KEY env var
timeout=180,
)
sandbox = provider.get_or_create()
from deepagents_cli.integrations.langsmith import LangSmithProvider
provider = LangSmithProvider(
api_key="...", # Or set LANGSMITH_API_KEY env var
timeout=180,
)
sandbox = provider.get_or_create()
Auto-creates a deepagents-cli template with a python:3 image if it doesn't exist.
Sandboxes support batch file upload and download with per-file error reporting:
# Upload files
results = sandbox.upload_files([
("/workspace/main.py", b"print('hello')"),
("/workspace/data.json", b'{"key": "value"}'),
])
# Download files
downloads = sandbox.download_files([
"/workspace/main.py",
"/workspace/output.txt",
])
for download in downloads:
if download.error is None:
print(download.content)
File operations support partial success — individual file failures don't abort the entire batch.
Sandbox file operations use standardized error codes rather than raising exceptions:
| Error Code | Description |
|---|---|
file_not_found |
The requested file does not exist |
permission_denied |
Insufficient permissions |
is_directory |
Path points to a directory, not a file |
invalid_path |
The provided path is invalid |