sandbox
Imported skill sandbox from langchain
SKILL.mdsources/langchain/sandbox/SKILL.md
GitHub"""Base sandbox implementation with execute() as the only abstract method.
This module provides a base class that implements all SandboxBackendProtocol methods using shell commands executed via execute(). Concrete implementations only need to implement the execute() method. """
from future import annotations
import base64 import json import shlex from abc import ABC, abstractmethod
from deepagents.backends.protocol import ( EditResult, ExecuteResponse, FileDownloadResponse, FileInfo, FileUploadResponse, GrepMatch, SandboxBackendProtocol, WriteResult, )
_GLOB_COMMAND_TEMPLATE = """python3 -c " import glob import os import json import base64
Decode base64-encoded parameters
path = base64.b64decode('{path_b64}').decode('utf-8') pattern = base64.b64decode('{pattern_b64}').decode('utf-8')
os.chdir(path) matches = sorted(glob.glob(pattern, recursive=True)) for m in matches: stat = os.stat(m) result = {{ 'path': m, 'size': stat.st_size, 'mtime': stat.st_mtime, 'is_dir': os.path.isdir(m) }} print(json.dumps(result)) " 2>/dev/null"""
_WRITE_COMMAND_TEMPLATE = """python3 -c " import os import sys import base64
file_path = '{file_path}'
Check if file already exists (atomic with write)
if os.path.exists(file_path): print(f'Error: File \'{file_path}\' already exists', file=sys.stderr) sys.exit(1)
Create parent directory if needed
parent_dir = os.path.dirname(file_path) or '.' os.makedirs(parent_dir, exist_ok=True)
Decode and write content
content = base64.b64decode('{content_b64}').decode('utf-8') with open(file_path, 'w') as f: f.write(content) " 2>&1"""
_EDIT_COMMAND_TEMPLATE = """python3 -c " import sys import base64
Read file content
with open('{file_path}', 'r') as f: text = f.read()
Decode base64-encoded strings
old = base64.b64decode('{old_b64}').decode('utf-8') new = base64.b64decode('{new_b64}').decode('utf-8')
Count occurrences
count = text.count(old)
Exit with error codes if issues found
if count == 0: sys.exit(1) # String not found elif count > 1 and not {replace_all}: sys.exit(2) # Multiple occurrences without replace_all
Perform replacement
if {replace_all}: result = text.replace(old, new) else: result = text.replace(old, new, 1)
Write back to file
with open('{file_path}', 'w') as f: f.write(result)
print(count) " 2>&1"""
_READ_COMMAND_TEMPLATE = """python3 -c " import os import sys
file_path = '{file_path}' offset = {offset} limit = {limit}
Check if file exists
if not os.path.isfile(file_path): print('Error: File not found') sys.exit(1)
Check if file is empty
if os.path.getsize(file_path) == 0: print('System reminder: File exists but has empty contents') sys.exit(0)
Read file with offset and limit
with open(file_path, 'r') as f: lines = f.readlines()
Apply offset and limit
start_idx = offset end_idx = offset + limit selected_lines = lines[start_idx:end_idx]
Format with line numbers (1-indexed, starting from offset + 1)
for i, line in enumerate(selected_lines): line_num = offset + i + 1 # Remove trailing newline for formatting, then add it back line_content = line.rstrip('\n') print(f'{{line_num:6d}}\t{{line_content}}') " 2>&1"""
class BaseSandbox(SandboxBackendProtocol, ABC): """Base sandbox implementation with execute() as abstract method.
This class provides default implementations for all protocol methods
using shell commands. Subclasses only need to implement execute().
"""
@abstractmethod
def execute(
self,
command: str,
) -> ExecuteResponse:
"""Execute a command in the sandbox and return ExecuteResponse.
Args:
command: Full shell command string to execute.
Returns:
ExecuteResponse with combined output, exit code, optional signal, and truncation flag.
"""
...
def ls_info(self, path: str) -> list[FileInfo]:
"""Structured listing with file metadata using os.scandir."""
cmd = f"""python3 -c "
import os import json
path = '{path}'
try: with os.scandir(path) as it: for entry in it: result = {{ 'path': os.path.join(path, entry.name), 'is_dir': entry.is_dir(follow_symlinks=False) }} print(json.dumps(result)) except FileNotFoundError: pass except PermissionError: pass " 2>/dev/null"""
result = self.execute(cmd)
file_infos: list[FileInfo] = []
for line in result.output.strip().split("\n"):
if not line:
continue
try:
data = json.loads(line)
file_infos.append({"path": data["path"], "is_dir": data["is_dir"]})
except json.JSONDecodeError:
continue
return file_infos
def read(
self,
file_path: str,
offset: int = 0,
limit: int = 2000,
) -> str:
"""Read file content with line numbers using a single shell command."""
# Use template for reading file with offset and limit
cmd = _READ_COMMAND_TEMPLATE.format(file_path=file_path, offset=offset, limit=limit)
result = self.execute(cmd)
output = result.output.rstrip()
exit_code = result.exit_code
if exit_code != 0 or "Error: File not found" in output:
return f"Error: File '{file_path}' not found"
return output
def write(
self,
file_path: str,
content: str,
) -> WriteResult:
"""Create a new file. Returns WriteResult; error populated on failure."""
# Encode content as base64 to avoid any escaping issues
content_b64 = base64.b64encode(content.encode("utf-8")).decode("ascii")
# Single atomic check + write command
cmd = _WRITE_COMMAND_TEMPLATE.format(file_path=file_path, content_b64=content_b64)
result = self.execute(cmd)
# Check for errors (exit code or error message in output)
if result.exit_code != 0 or "Error:" in result.output:
error_msg = result.output.strip() or f"Failed to write file '{file_path}'"
return WriteResult(error=error_msg)
# External storage - no files_update needed
return WriteResult(path=file_path, files_update=None)
def edit(
self,
file_path: str,
old_string: str,
new_string: str,
replace_all: bool = False,
) -> EditResult:
"""Edit a file by replacing string occurrences. Returns EditResult."""
# Encode strings as base64 to avoid any escaping issues
old_b64 = base64.b64encode(old_string.encode("utf-8")).decode("ascii")
new_b64 = base64.b64encode(new_string.encode("utf-8")).decode("ascii")
# Use template for string replacement
cmd = _EDIT_COMMAND_TEMPLATE.format(file_path=file_path, old_b64=old_b64, new_b64=new_b64, replace_all=replace_all)
result = self.execute(cmd)
exit_code = result.exit_code
output = result.output.strip()
if exit_code == 1:
return EditResult(error=f"Error: String not found in file: '{old_string}'")
if exit_code == 2:
return EditResult(error=f"Error: String '{old_string}' appears multiple times. Use replace_all=True to replace all occurrences.")
if exit_code != 0:
return EditResult(error=f"Error: File '{file_path}' not found")
count = int(output)
# External storage - no files_update needed
return EditResult(path=file_path, files_update=None, occurrences=count)
def grep_raw(
self,
pattern: str,
path: str | None = None,
glob: str | None = None,
) -> list[GrepMatch] | str:
"""Structured search results or error string for invalid input."""
search_path = shlex.quote(path or ".")
# Build grep command to get structured output
grep_opts = "-rHnF" # recursive, with filename, with line number, fixed-strings (literal)
# Add glob pattern if specified
glob_pattern = ""
if glob:
glob_pattern = f"--include='{glob}'"
# Escape pattern for shell
pattern_escaped = shlex.quote(pattern)
cmd = f"grep {grep_opts} {glob_pattern} -e {pattern_escaped} {search_path} 2>/dev/null || true"
result = self.execute(cmd)
output = result.output.rstrip()
if not output:
return []
# Parse grep output into GrepMatch objects
matches: list[GrepMatch] = []
for line in output.split("\n"):
# Format is: path:line_number:text
parts = line.split(":", 2)
if len(parts) >= 3:
matches.append(
{
"path": parts[0],
"line": int(parts[1]),
"text": parts[2],
}
)
return matches
def glob_info(self, pattern: str, path: str = "/") -> list[FileInfo]:
"""Structured glob matching returning FileInfo dicts."""
# Encode pattern and path as base64 to avoid escaping issues
pattern_b64 = base64.b64encode(pattern.encode("utf-8")).decode("ascii")
path_b64 = base64.b64encode(path.encode("utf-8")).decode("ascii")
cmd = _GLOB_COMMAND_TEMPLATE.format(path_b64=path_b64, pattern_b64=pattern_b64)
result = self.execute(cmd)
output = result.output.strip()
if not output:
return []
# Parse JSON output into FileInfo dicts
file_infos: list[FileInfo] = []
for line in output.split("\n"):
try:
data = json.loads(line)
file_infos.append(
{
"path": data["path"],
"is_dir": data["is_dir"],
}
)
except json.JSONDecodeError:
continue
return file_infos
@property
@abstractmethod
def id(self) -> str:
"""Unique identifier for the sandbox backend."""
@abstractmethod
def upload_files(self, files: list[tuple[str, bytes]]) -> list[FileUploadResponse]:
"""Upload multiple files to the sandbox.
Implementations must support partial success - catch exceptions per-file
and return errors in FileUploadResponse objects rather than raising.
"""
@abstractmethod
def download_files(self, paths: list[str]) -> list[FileDownloadResponse]:
"""Download multiple files from the sandbox.
Implementations must support partial success - catch exceptions per-file
and return errors in FileDownloadResponse objects rather than raising.
"""