Skip to content

Sandbox Shell Tool#

SandboxShellTool lets an agent run shell commands inside an environment you choose — a local subprocess, a Docker container, a Daytona sandbox, or any custom backend. Unlike the provider-native ShellTool (which runs server-side and only on Anthropic/OpenAI), it executes client-side, so it works with any model provider.

The design has two orthogonal pieces:

  • The environment decides where commands run and carries all backend config (image, env vars, network, timeout, …).
  • The tool decides the agent-facing policy (allowed / blocked / ignore / readonly).

The same environment can back both a SandboxShellTool and a SandboxCodeTool.

Quick Start#

from ag2 import Agent
from ag2.config import AnthropicConfig
from ag2.tools import SandboxShellTool

agent = Agent(
    "coder",
    "You write and run Python code.",
    config=AnthropicConfig(model="claude-sonnet-4-6"),
    tools=[SandboxShellTool()],
)

reply = await agent.ask("Write a hello world script and run it.")
print(await reply.content())

With no arguments, SandboxShellTool uses a LocalEnvironment with a temporary working directory that is cleaned up when the process exits.

Choosing an Environment#

The first argument is the environment. Pass a LocalEnvironment, DockerEnvironment, or DaytonaEnvironment:

from ag2.tools import SandboxShellTool, LocalEnvironment
from ag2.extensions.docker import DockerEnvironment
from ag2.extensions.daytona import DaytonaEnvironment

# Local subprocess in a specific directory
sh = SandboxShellTool(LocalEnvironment("/tmp/my_project"))

# Docker container — configure the backend once
sh = SandboxShellTool(DockerEnvironment(image="python:3.12-slim", network_mode="none"))

# Daytona hosted sandbox
sh = SandboxShellTool(DaytonaEnvironment(image="python:3.12"))

A LocalEnvironment directory is created automatically if it does not exist, and is not deleted on exit when an explicit path is given.

Command Filtering#

Filtering policy lives on the tool, not the environment:

1
2
3
4
5
6
7
8
from ag2.tools import SandboxShellTool, LocalEnvironment

sh = SandboxShellTool(
    LocalEnvironment("/tmp/my_project"),
    allowed=["python", "uv run", "git"],
    blocked=["rm -rf", "curl", "wget"],
    ignore=["**/.env", "*.key", "secrets/**"],
)

Tool Parameters#

Parameter Default Description
environment None Backend: LocalEnvironment / DockerEnvironment / DaytonaEnvironment. NoneLocalEnvironment()
allowed None Whitelist of command prefixes. None → all commands allowed
blocked None Blacklist of command prefixes. None → nothing blocked
ignore None Gitignore-style path patterns. Commands referencing matching paths return "Access denied: <path>"
readonly False When True and allowed is not set, restricts to a built-in read-only list (cat, ls, grep, git log, …)

LocalEnvironment Parameters#

Parameter Default Description
path None Working directory. None → temporary dir prefixed ag2_sandbox_, deleted on exit
cleanup None None → auto (True when path=None, False otherwise)
timeout 60 Per-command timeout in seconds. Returns an exit code 124 result on expiry
max_output 100_000 Maximum characters in the returned output; truncated output gets a [truncated: …] suffix
env_vars None Environment variables merged into every command

Filter Order#

Filtering is applied in this order on every run_shell_command(command) call:

  1. allowed — if set, the command must match at least one prefix. Otherwise: "Command not allowed: <cmd>".
  2. blocked — if set, the command must not match any prefix. Otherwise: "Command not allowed: <cmd>".
  3. ignore — literal file paths parsed from the command string are resolved and checked against the patterns. On match: "Access denied: <path>".
  4. Execute — the command runs in the environment.

Note

ignore checks only literal path tokens in the command string. Paths computed dynamically inside the shell (variable substitution, command substitution, glob expansion) are not inspected.

Read-Only Mode#

Use readonly=True to let the agent inspect files without modifying anything:

1
2
3
from ag2.tools import SandboxShellTool, LocalEnvironment

sh = SandboxShellTool(LocalEnvironment("/my/codebase"), readonly=True)

This restricts commands to cat, head, tail, ls, grep, find, git log, git diff, git status, and a few others. Pass an explicit allowed list to override this set.

Accessing the Working Directory#

SandboxShellTool exposes the resolved working directory via the workdir property:

sh = SandboxShellTool(LocalEnvironment("/tmp/my_project"))
print(sh.workdir)  # PosixPath('/tmp/my_project')

Stateful Multi-Turn Conversations#

Because files persist in workdir across ask() calls, the agent can build on prior work in a chained conversation:

from ag2 import Agent
from ag2.config import AnthropicConfig
from ag2.tools import SandboxShellTool, LocalEnvironment

sh = SandboxShellTool(LocalEnvironment("/tmp/counter_demo"))
agent = Agent("coder", "You manage files.", config=AnthropicConfig(model="claude-sonnet-4-6"), tools=[sh])

reply1 = await agent.ask("Create counter.txt with value 0")
reply2 = await reply1.ask("Increment the counter by 1")
reply3 = await reply2.ask("Read the counter and tell me the value")

Warning

A LocalEnvironment gives the agent direct access to your filesystem and the ability to run arbitrary commands. Always set allowed, blocked, or readonly when exposing it to untrusted prompts, or use a DockerEnvironment / DaytonaEnvironment for real isolation.

SandboxShellTool vs ShellTool#

SandboxShellTool ShellTool
Execution Client-side, in your environment Provider-side (Anthropic / OpenAI)
Provider support Any provider Anthropic, OpenAI only
Environment control Full (allowed, blocked, ignore, backend choice) Limited (provider-dependent)
Import ag2.tools.SandboxShellTool ag2.tools.ShellTool