Local Agent

Shell & Sandbox

Configure shell execution, sandboxing, and approval flows for the local Agent

Shell & Sandbox

The Agent uses @downcity/shell for local command execution, sandboxed runners, shell session management, and interactive approval flows.

Install

Shell core and platform implementations are separate packages. Install only the adapter for the current OS:

# macOS
pnpm add @downcity/shell @downcity/sandbox-macos

# Linux
pnpm add @downcity/shell @downcity/sandbox-linux

# Windows (MXC Development)
pnpm add @downcity/shell @downcity/sandbox-windows-mxc

# Windows (Anthropic SRT Alpha)
pnpm add @downcity/shell @downcity/sandbox-windows-srt
npx @downcity/sandbox-windows-srt setup

Shell

The Shell class manages command execution and the approval runtime. Application-level approvals belong to a specific Session.

Minimal usage

import { Shell } from "@downcity/shell";
import { Agent, Workspace } from "@downcity/agent";
import { MacOsSeatbeltSandbox } from "@downcity/sandbox-macos";

const shell = new Shell({
  sandbox: new MacOsSeatbeltSandbox(),
});

const agent = new Agent({
  id: "demo",
  workspace: new Workspace({
    path: process.cwd(),
    shell,
  }),
});

Workspace binds its project root to Shell; Agent no longer configures or owns Shell directly. Pass root_path only when using Shell independently.

Resource disposal

  • When Workspace is bound to Agent, call agent.dispose(); it releases Shell as well.
  • Call workspace.dispose() directly only when Workspace has not been bound to Agent.
  • shell.set_safe_read_only_paths(paths) — Replace host-approved read-only directories.

Approval requests appear as pending Interaction Parts while their Tools are waiting-user. Read the complete Shell request from the Interaction request, then submit the user's decision with session.respond(...). A reconnected client can recover pending requests with session.interactions(). Do not call internal Shell Runtime approval methods directly.

Model tools

Workspace always provides file and search tools; configuring Shell adds two command tools:

ToolPurpose
shell_execRun one non-interactive command
shell_sessionManage long-running or interactive PTY commands
grepSearch project file contents directly with ripgrep
findDiscover project files with POSIX glob patterns
readRead paginated text and attach images to the model
writeCreate UTF-8 files or atomically replace them with overwrite: true
editApply unique, non-overlapping exact-text replacements to one file

read returns up to 500 lines by default, with a maximum of 2,000 lines or 256KB per call. Continue with truncated and next_offset. write accepts up to 1MB and creates parent directories automatically.

For PNG, JPEG, GIF, WebP, BMP, and PDF files, read.output contains metadata only and the Tool injects a User File Part pointing to the local file into the next model step. The Tool Result does not contain base64. Other binary files return metadata only.

edit accepts up to ten operations. Every operation is matched against the original file, and the file is written only when all operations are valid. Pass read.sha256 as edit.expected_sha256 to reject stale concurrent edits.

grep runs rg --json directly without a shell. It searches literal text case-insensitively by default; set literal: false for regular expressions or case_sensitive: true for exact casing. Use glob to limit candidate files.

find accepts a POSIX glob pattern, includes dotfiles, respects .gitignore, and does not follow symbolic links. Both search tools return up to 200 results by default and accept max_results up to 2,000.

shell_exec and shell_session belong to Shell; the other five tools belong to Workspace. All file and search tools are fixed to the Workspace root and reject symbolic-link escapes. They never request unrestricted access.

Sandbox

Shell commands use the safe sandbox by default. The SDK does not select or install a platform backend automatically; the application injects one adapter into Shell:

  • macos-seatbelt — macOS sandbox
  • linux-bubblewrap — Linux sandbox
  • windows-mxc-dev — Microsoft MXC Windows process sandbox; Development / unstable
  • windows-srt-alpha — Anthropic Sandbox Runtime native Windows sandbox; Alpha
  • unrestricted-host — No sandboxing

@downcity/shell owns the workspace write boundary, path validation, environment convergence, and policy fingerprint. Independent adapters own system paths, host preflight, policy compilation, and process startup. MXC and Anthropic SRT belong only to their Windows packages, so installing @downcity/shell does not download a Windows runtime on macOS or Linux. The Downcity CLI keeps MXC as its default and enables SRT only when DC_WINDOWS_SANDBOX=srt is set. SDK applications install and inject their selected adapter explicitly.

Windows adapters

Native Windows support is currently Development / unstable and requires Windows 11 24H2 build 26100 or newer. Agent preflight checks cmd.exe, the bundled Microsoft MXC runtime, and its selected isolation tier. A missing dependency, unsupported build, or failed probe refuses Safe Sandbox startup and never silently falls back to unrestricted execution.

Windows uses the native cmd.exe /d /s /c command model. Environment variables, pipes, and redirects must use cmd syntax such as %NAME%, |, and >. The SDK does not translate POSIX shell commands into cmd. Invoke powershell.exe explicitly from the command when PowerShell semantics are required.

The SDK delegates Windows isolation to the pinned @microsoft/mxc-sdk runtime. MXC selects its available process isolation tier, owns AppContainer and Job Object lifecycle, and restores temporary filesystem policy after execution. Downcity only maps its resolved read-only and read-write roots to the MXC 0.7.0-alpha policy schema.

Current Windows considerations:

  • MXC host or port allowlists and Windows deniedPaths are not part of Downcity's current Windows policy contract.
  • PTY and pipe execution are provided by MXC; console-sensitive programs may still differ from macOS or Linux.
  • MXC is Public Preview and its upstream documentation states that current profiles may be overly permissive. This backend must not be treated as a production security boundary.

The SRT adapter directly uses Anthropic's open-source @anthropic-ai/sandbox-runtime. It protects the host with a dedicated downcity-sandbox Windows user, Restricted Token, Job Object, NTFS ACLs, and WFP network filters. Run npx @downcity/sandbox-windows-srt setup once and approve UAC before use. Normal command execution never installs or elevates automatically.

import { Shell } from "@downcity/shell";
import { WindowsSrtSandbox } from "@downcity/sandbox-windows-srt";

const shell = new Shell({
  sandbox: new WindowsSrtSandbox(),
});

SRT's Windows backend is currently Alpha, and one Downcity process permits only one active workspace security domain. Do not run different workspaces concurrently under the shared downcity-sandbox SID; use MXC, WSL/a container, or separate host security domains for that case. Per-user tools installed through nvm, Scoop, or pip install --user are not readable by the dedicated sandbox user unless their directories are explicitly added as read-only paths.

SRT's Windows filesystem boundary relies on a dedicated user and additive NTFS ACLs. If a directory outside the workspace already grants write access to a broad principal such as Authenticated Users, SRT's allowWrite does not revoke that existing access. Do not treat the current Alpha backend as a complete default-deny boundary on arbitrary Windows hosts. Use a workspace parent with hardened host ACLs, or prefer MXC, WSL, or a container for stronger requirements.

Additional read-only directories

A host can expose a version-pinned CLI or runtime directory to the Safe Sandbox as read-only:

const shell = new Shell({
  sandbox: new MacOsSeatbeltSandbox(),
  safe_read_only_paths: [
    "/Users/user/.vibecape/tools/officecli/v1.0.136",
  ],
});

Each entry must be an existing absolute directory outside the project. macOS emits a Seatbelt file-read* rule, Linux uses Bubblewrap --ro-bind, and Windows maps it to either an MXC read-only root or an SRT READ ACL. This never expands project-external write access.

On macOS, Shell resolves xcode-select -p, adds the selected Xcode runtime as a system read-only path, and prepends the real Developer/usr/bin directory to PATH. Normal git calls use the Xcode binary directly instead of the xcrun entry that writes a host cache.

Policy

SandboxPolicy combines system read-only paths, the fixed writable workspace, and host read-only paths, then validates realpaths, overlaps, and directory permissions.

Each adapter exposes preflight() for its host-specific readiness checks.

Approval

ShellApprovalRuntime manages the interactive approval loop. Unrestricted requests use the current Session approval mode; the default ask mode requires an explicit approve or deny decision.

ShellTools

ShellTools, FileTools, SearchTools, and their schemas provide the AI-ready definitions used by the Agent. You do not normally need to use them directly; they are wired automatically when you pass shell to the Agent constructor.