Skip to content
41 changes: 40 additions & 1 deletion libs/deepagents/backends/composite.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
"""CompositeBackend: Route operations to different backends based on path prefix."""

from deepagents.backends.protocol import BackendProtocol, EditResult, FileInfo, GrepMatch, WriteResult
from deepagents.backends.protocol import (
BackendProtocol,
EditResult,
ExecuteResponse,
FileInfo,
GrepMatch,
SandboxBackendProtocol,
WriteResult,
)
from deepagents.backends.state import StateBackend


Expand Down Expand Up @@ -211,3 +219,34 @@ def edit(
except Exception:
pass
return res

def execute(
self,
command: str,
*,
timeout: int = 30 * 60,
) -> ExecuteResponse:
"""Execute a command via the default backend.

Execution is not path-specific, so it always delegates to the default backend.
The default backend must implement SandboxBackendProtocol for this to work.

Args:
command: Full shell command string to execute.
timeout: Maximum execution time in seconds (default: 30 minutes).

Returns:
ExecuteResponse with combined output, exit code, and truncation flag.

Raises:
NotImplementedError: If default backend doesn't support execution.
"""
if isinstance(self.default, SandboxBackendProtocol):
return self.default.execute(command, timeout=timeout)

# This shouldn't be reached if the runtime check in the execute tool works correctly,
# but we include it as a safety fallback.
raise NotImplementedError(
"Default backend doesn't support command execution (SandboxBackendProtocol). "
"To enable execution, provide a default backend that implements SandboxBackendProtocol."
)
45 changes: 45 additions & 0 deletions libs/deepagents/backends/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,4 +145,49 @@ def edit(
...


@dataclass
class ExecuteResponse:
"""Result of code execution.
Simplified schema optimized for LLM consumption.
"""

output: str
"""Combined stdout and stderr output of the executed command."""

exit_code: int | None = None
"""The process exit code. 0 indicates success, non-zero indicates failure."""

truncated: bool = False
"""Whether the output was truncated due to backend limitations."""


@runtime_checkable
class SandboxBackendProtocol(BackendProtocol, Protocol):
"""Protocol for sandboxed backends with isolated runtime.
Sandboxed backends run in isolated environments (e.g., separate processes,
containers) and communicate via defined interfaces.
"""

def execute(
self,
command: str,
*,
timeout: int = 30 * 60,
) -> ExecuteResponse:
"""Execute a command in the process.
Simplified interface optimized for LLM consumption.
Args:
command: Full shell command string to execute.
timeout: Maximum execution time in seconds (default: 30 minutes).
Returns:
ExecuteResponse with combined output, exit code, optional signal, and truncation flag.
"""
...


BackendFactory: TypeAlias = Callable[[ToolRuntime], BackendProtocol]
10 changes: 7 additions & 3 deletions libs/deepagents/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,12 @@ def create_deep_agent(
"""Create a deep agent.

This agent will by default have access to a tool to write todos (write_todos),
six file editing tools: write_file, ls, read_file, edit_file, glob_search, grep_search,
seven file and execution tools: ls, read_file, write_file, edit_file, glob, grep, execute,
and a tool to call subagents.

The execute tool allows running shell commands if the backend implements SandboxBackendProtocol.
For non-sandbox backends, the execute tool will return an error message.

Args:
model: The model to use. Defaults to Claude Sonnet 4.
tools: The tools the agent should have access to.
Expand All @@ -80,8 +83,9 @@ def create_deep_agent(
context_schema: The schema of the deep agent.
checkpointer: Optional checkpointer for persisting agent state between runs.
store: Optional store for persistent storage (required if backend uses StoreBackend).
backend: Optional backend for file storage. Pass either a Backend instance or a
callable factory like `lambda rt: StateBackend(rt)`.
backend: Optional backend for file storage and execution. Pass either a Backend instance
or a callable factory like `lambda rt: StateBackend(rt)`. For execution support,
use a backend that implements SandboxBackendProtocol.
interrupt_on: Optional Dict[str, bool | InterruptOnConfig] mapping tool names to
interrupt configs.
debug: Whether to enable debug mode. Passed through to create_agent.
Expand Down
Loading