How Deep Agents Implements Shell Command Execution: Inside FilesystemMiddleware

This article dissects Deep Agents' shell command execution via the FilesystemMiddleware's execute tool, detailing its parameter validation, backend resolution through SandboxBackendProtocol, LocalShellBackend's subprocess-based implementation, BaseSandbox's abstract interface for isolated sandboxes, and a practical example using a local workspace.

Code Archaeology
Code Archaeology
Code Archaeology
How Deep Agents Implements Shell Command Execution: Inside FilesystemMiddleware

1. The execute Tool

The FilesystemMiddleware creates an execute_tool that implements shell command execution. The tool accepts two parameters:

command: Annotated[str, "Shell command to execute in the sandbox environment."]
timeout: Annotated[int | None, "Optional timeout in seconds for this command. Overrides the default timeout. Use for long-running commands."] = None

Execution Flow

Parameter pre-validation

timeout <= 0 → return Error

timeout > max_execute_timeout → return Error

_get_backend(runtime)

If callable → factory call to get instance

Otherwise → use stored backend directly

_supports_execution(resolved_backend)

CompositeBackend → check .default

Others → isinstance(SandboxBackendProtocol)

If False → return "Error: Execution not available"

execute_accepts_timeout(type(executable))

Uses inspect.signature() to check if timeout parameter exists

If False and timeout provided → return Error

Backend execution

Sync: executable.execute(command, ...)

Async: await executable.aexecute(command, ...)

Exception handling

NotImplementedError → "Error: Execution not available."

ValueError → "Error: Invalid parameter."

Output formatting

result.output

[Command succeeded/failed with exit code N]

[Output was truncated due to size limits]

Key logic: execution capability is encapsulated in the backend, which must implement SandboxBackendProtocol. The backend internally enforces a maximum output limit.

2. FilesystemMiddleware Hooks

2.1 wrap_model_call: System Prompt Injection

The execute tool's prompt is appended only when backend_supports_execution is true:

## Execute Tool `execute`

You have access to an `execute` tool for running shell commands in a sandboxed environment.
Use this tool to run commands, scripts, tests, builds, and other shell operations.

- execute: run a shell command in the sandbox (returns output and exit code)

2.2 wrap_tool_call: Offloading Large Outputs to Filesystem

Oversized execute tool outputs are offloaded to /large_tool_results/{sanitized_id}.

3. SandboxBackendProtocol Implementation

SandboxBackendProtocol

specifies id and execute interfaces. Deep Agents core provides a non-isolated LocalShellBackend and an abstract sandbox base class BaseSandbox. Full isolated sandbox backends (daytona, modal, runloop) live in the partners directory.

class SandboxBackendProtocol(BackendProtocol):
    @property
    def id(self) -> str:
        """Unique identifier for the backend instance"""
        raise NotImplementedError

    def execute(self, command: str, *, timeout: int | None = None) -> ExecuteResponse:
        """Synchronously execute shell command"""
        raise NotImplementedError

    async def aexecute(self, command: str, *, timeout: int | None = None) -> ExecuteResponse:
        """Async execute (default wraps sync execute via asyncio.to_thread)"""
        if timeout is not None and execute_accepts_timeout(type(self)):
            return await asyncio.to_thread(self.execute, command, timeout=timeout)
        return await asyncio.to_thread(self.execute, command)

3.1 LocalShellBackend

LocalShellBackend

inherits from FilesystemBackend and runs commands locally via subprocess:

result = subprocess.run(
    command,
    check=False,
    shell=True,  # Intentional: designed for LLM-controlled shell execution
    capture_output=True,
    text=True,
    timeout=effective_timeout,
    env=self._env,
    cwd=str(self.cwd),  # Use the root_dir from FilesystemBackend
)

It has no sandbox isolation and must be used with HumanInTheLoopMiddleware.

Output handling:

stdout appended directly

stderr lines prefixed with [stderr] then appended

No output → return empty string

Exceeds max_output_bytes → truncate + append truncation notice

Non-zero exit code → append "Exit code: N" to output end

Timeout handling: Returns "Error: Command timed out after {effective_timeout} seconds..."

Exception handling: Returns "Error executing command ({type(e).__name__}): {e}"

3.2 BaseSandbox Abstract Base Class

BaseSandbox

is an abstract base for partner implementations. External sandboxes (containers, VMs, remote) expose only an execute interface; all filesystem operations are implemented as Python scripts executed via that interface:

ls_info() → execute("python3 -c '...os.scandir...'")

read() → execute("python3 -c '...open(file_path)...'")

write() → execute("python3 -c '...' <<'EOF'\n{payload}\nEOF")

edit() → execute("python3 -c '...' <<'EOF'\n{payload}\nEOF")

glob_info() → execute("python3 -c '...glob.glob...'")

grep_raw() → execute("grep -rHnF ...") BaseSandbox defines four abstract interfaces:

class BaseSandbox(SandboxBackendProtocol, ABC):
    @abstractmethod
    def execute(self, command: str, *, timeout: int | None = None) -> ExecuteResponse:
        """Execute command in sandbox, return ExecuteResponse"""

    @property
    @abstractmethod
    def id(self) -> str:
        """Backend unique identifier"""

    @abstractmethod
    def upload_files(self, files: list[tuple[str, bytes]]) -> list[FileUploadResponse]:
        """Upload files to sandbox"""

    @abstractmethod
    def download_files(self, paths: list[str]) -> list[FileDownloadResponse]:
        """Download files from sandbox"""

The last two are binary file transfer interfaces and are developer APIs, not LLM tools.

4. Simple Example

SYSTEM_PROMPT = """You are a coding assistant with access to a local shell workspace.

You can write files and run shell commands using the available tools:
- write_file / edit_file: create or modify files
- execute: run shell commands (python, bash, etc.)
- read_file / ls / glob / grep: explore files

Work in /workspace. When asked to run code, write the script first then execute it."""

# Workspace directory for this session
workspace = tempfile.mkdtemp()

# Backend with shell execution support (implements SandboxBackendProtocol)
backend = LocalShellBackend(root_dir=workspace, virtual_mode=True, inherit_env=True)

# Model
model = init_chat_model(model="deepseek:deepseek-chat", temperature=0.0)

# Create the agent
agent = create_deep_agent(
    model=model,
    system_prompt=SYSTEM_PROMPT,
    backend=backend,
)

Given a task like "calculate 43 * 52", the agent uses the execute tool to run a Python script. Detailed interaction trace: 34b1a642-e58b-4218-a26e-e70532d37467 (https://smith.langchain.com/public/34b1a642-e58b-4218-a26e-e70532d37467/r).

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

AI engineeringLLM AgentsDeep AgentsFilesystemMiddlewareLocalShellBackendBaseSandboxSandboxBackendProtocolShell Execution
Code Archaeology
Written by

Code Archaeology

Learn from the past to understand the present

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.