How to Configure Sandbox Execution with SandboxBackendProtocol for Tool Execution in DeepAgents
Configure sandbox execution by selecting a provider (modal, runloop, daytona, or langsmith), initializing a sandbox via the create_sandbox() context manager, and calling execute() or aexecute() to run isolated tool commands.
The langchain-ai/deepagents repository provides a robust sandboxing system that isolates tool execution through the SandboxBackendProtocol. This protocol enables secure, backend-agnostic command execution across multiple cloud providers while maintaining a consistent interface for file operations and process management.
Understanding the SandboxBackendProtocol Architecture
The sandbox system centers on a protocol-based design that abstracts provider-specific implementations behind a unified interface.
Protocol Definition and Core Methods
SandboxBackendProtocol extends BackendProtocol in libs/deepagents/deepagents/backends/protocol.py to add sandbox-specific capabilities. The protocol requires implementations to provide:
execute(command, timeout=None): Synchronously run shell commands inside the isolated environmentaexecute(command, timeout=None): Asynchronous variant for non-blocking executionidproperty: A unique identifier for the sandbox instance
According to the source code in protocol.py (lines 12-30 and 112-124), these methods must return an ExecuteResponse object containing the combined stdout/stderr output, exit code, and a truncated flag indicating whether output exceeded limits.
Provider Factory Pattern
Concrete implementations are instantiated through a factory pattern defined in libs/cli/deepagents_cli/integrations/sandbox_factory.py. The _get_provider() method (lines 58-86) maps provider names to specific implementations:
modal: Cloud-based containerized executionrunloop: Alternative cloud provider integrationdaytona: Development environment sandboxinglangsmith: LangChain-specific execution backend
Each provider must implement the abstract SandboxProvider interface defined in libs/cli/deepagents_cli/integrations/sandbox_provider.py (lines 26-36), ensuring consistent lifecycle management across different backends.
Step-by-Step Configuration Guide
Select a Sandbox Provider
Choose a provider based on your infrastructure requirements. The provider name string determines which backend implementation the factory instantiates:
# Available providers: "modal", "runloop", "daytona", "langsmith"
provider_name = "modal"
Initialize the Sandbox with create_sandbox
Use the create_sandbox() context manager from sandbox_factory.py (lines 71-110) to handle setup, status reporting, and automatic cleanup:
from deepagents_cli.integrations.sandbox_factory import create_sandbox
# Context manager ensures proper resource cleanup
with create_sandbox("modal") as sandbox:
# sandbox implements SandboxBackendProtocol
pass
The context manager optionally accepts a setup_script_path parameter. When provided, the factory reads the script, substitutes environment variables (e.g., ${VAR}), and executes it inside the sandbox with a 5-minute timeout before yielding control.
Execute Tool Commands
Invoke commands through the execute() method for synchronous operations or aexecute() for async workflows. The BaseSandbox class in libs/deepagents/deepagents/backends/sandbox.py (lines 17-24) implements all file system operations (read, write, edit, grep_raw, glob_info) by delegating to shell snippets executed through these methods.
# Synchronous execution
result = sandbox.execute("python /workspace/analyze.py", timeout=60)
The optional timeout parameter is validated through the execute_accepts_timeout() helper in protocol.py (lines 70-89). If the concrete backend does not support timeout arguments, the parameter is safely ignored rather than raising an error.
Handle Execution Responses
Process the ExecuteResponse object returned by execution calls. Defined in sandbox.py (lines 95-106), this dataclass provides:
output: Combined stdout and stderr as a stringexit_code: Integer return code from the processtruncated: Boolean indicating whether output was truncated due to length limits
if result.exit_code == 0:
print("Output:", result.output)
else:
print("Error occurred, truncated:", result.truncated)
Practical Implementation Examples
Synchronous Tool Execution
Run commands in a Modal-hosted sandbox with immediate feedback:
from deepagents_cli.integrations.sandbox_factory import create_sandbox
with create_sandbox("modal") as sandbox:
# Execute a file listing command
result = sandbox.execute("ls -la /workspace")
print(f"Exit code: {result.exit_code}")
print(f"Output:\n{result.output}")
This example pulls the ModalProvider implementation and returns a SandboxBackendProtocol instance. The execute call runs inside the Modal container and returns an ExecuteResponse with full output capture.
Asynchronous Execution with Timeouts
Handle long-running tools without blocking the event loop:
import asyncio
from deepagents_cli.integrations.sandbox_factory import create_sandbox
async def run_long_task():
async with create_sandbox("runloop") as sandbox:
# 30-second timeout for the command
resp = await sandbox.aexecute(
"sleep 10 && echo 'Processing complete'",
timeout=30
)
if resp.exit_code == 0:
print("Success:", resp.output.strip())
else:
print(f"Failed with code {resp.exit_code}")
asyncio.run(run_long_task())
The aexecute method automatically falls back to synchronous execution if the backend lacks native async support, ensuring compatibility across all providers.
Setup Script Configuration
Initialize the sandbox environment with custom dependencies before tool execution:
from deepagents_cli.integrations.sandbox_factory import create_sandbox
with create_sandbox(
"daytona",
setup_script_path="scripts/install_deps.sh"
) as sandbox:
# Setup script runs first with env var substitution
result = sandbox.execute("python -c 'import pandas; print(pandas.__version__)'")
print(result.output)
The setup script execution occurs within the context manager initialization, ensuring the environment is ready before your tool commands run.
Accessing Sandbox Metadata
Inspect the sandbox identifier for logging or external monitoring:
with create_sandbox("langsmith") as sandbox:
print(f"Sandbox ID: {sandbox.id}")
# Use ID for external API calls or cleanup tracking
result = sandbox.execute("echo 'Task started'")
The id property is required by the SandboxBackendProtocol specification and uniquely identifies the instance throughout its lifecycle.
Key Implementation Files and Architecture
Understanding the source structure helps debug configuration issues:
libs/deepagents/deepagents/backends/protocol.py: DefinesSandboxBackendProtocol,ExecuteResponse, and timeout validation utilitieslibs/deepagents/deepagents/backends/sandbox.py: ContainsBaseSandboxwith default file operation implementations built onexecute()libs/cli/deepagents_cli/integrations/sandbox_factory.py: High-levelcreate_sandboxcontext manager and provider resolution logiclibs/cli/deepagents_cli/integrations/sandbox_provider.py: AbstractSandboxProviderinterface for concrete implementations- Provider implementations (
libs/partners/modal/.../sandbox.py, etc.): Backend-specific subclasses ofBaseSandbox
Summary
- SandboxBackendProtocol extends
BackendProtocolwithexecute(),aexecute(), and anidproperty for isolated command execution - Provider selection (modal, runloop, daytona, langsmith) occurs through
sandbox_factory.py's_get_provider()mapping - Initialization uses the
create_sandbox()context manager, which handles setup scripts and automatic resource cleanup - Execution returns
ExecuteResponseobjects containing output, exit_code, and truncation status - Timeout handling is optional and safely ignored if the backend implementation does not support it
- File operations in
BaseSandboxdelegate to shell commands executed through the protocol methods
Frequently Asked Questions
What providers are supported for SandboxBackendProtocol?
The deepagents CLI supports four providers: modal, runloop, daytona, and langsmith. Each implements the SandboxProvider interface and returns a concrete SandboxBackendProtocol instance. The mapping from provider name to implementation class is defined in sandbox_factory.py (lines 58-86).
How does the timeout parameter work in execute() calls?
The timeout parameter is optional and passed directly to the backend's execute() or aexecute() method. According to protocol.py (lines 70-89), the system checks whether the concrete implementation accepts timeout arguments using execute_accepts_timeout(). If the backend does not support timeouts, the parameter is silently ignored rather than raising a TypeError.
Can I run setup scripts before executing my tools?
Yes. Pass the setup_script_path parameter to create_sandbox(). The factory reads the specified file, substitutes environment variables (format ${VAR}), and executes the script inside the sandbox with a 5-minute hard timeout before yielding the backend instance. This occurs in sandbox_factory.py (lines 71-110).
What information does ExecuteResponse contain?
ExecuteResponse is a dataclass defined in sandbox.py (lines 95-106) containing three fields: output (combined stdout and stderr as a string), exit_code (integer process return code), and truncated (boolean indicating whether the output exceeded length limits and was cut off).
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →