@pleach/sandbox
Vendor-neutral SandboxProvider contract plus four endpoint-shape adapters that ship inside the package — the seam @pleach/coding-agent reads.
@pleach/sandbox is the contract layer between @pleach/core
runtimes and whatever process isolation a host chooses to ship —
a Docker container, a Firecracker microVM, an E2B or Modal
sandbox, an in-process fixture. It ships the SandboxProvider
interface plus an in-memory fixture, and four adapters that ship
inside the package — keyed by the shape of the backing endpoint
(HTTP-stream, HTTP-poll, child-process, AWS Fargate), not by vendor
name. @pleach/coding-agent is the canonical consumer — its
file-read, file-write, and shell tools all dispatch through the
provider this package defines.
What it provides
- The
SandboxProvidercontract a host implements once and reuses across every sandboxed tool — read, write, list, exec — per the four-tool sketch incoding-agent.mdx. - An in-memory fixture suitable for tests and replay runs where the host hasn't wired a real backing process.
- The canonical name (
SandboxProvider) plus the legacySandboxAdapterretained as a@deprecatedalias, per the status row inpackages.mdx. - The peer-dep anchor
@pleach/coding-agentdeclares at^0.1.0(see Coding agent for the locked decisions).
Where it fits
@pleach/sandbox is the seam between the runtime and the
sandboxed tool surface — the low-level SandboxProvider contract
(execute, readFile, writeFile, listFiles, plus
acquire / release / capabilities). There is no ctx.sandbox:
core's ToolContext is sandbox-agnostic and carries only
toolCallId + signal?. Instead the host closes over a sandbox
handle when it constructs the tools; @pleach/coding-agent ships a
thin SandboxClient facade (exec, readFile, writeFile) built
over the low-level provider, and the tool handlers call that — see
Coding agent. The
direction lock keeps @pleach/core itself sandbox-agnostic:
consumers reach for @pleach/sandbox directly, and the coding-agent
SKU composes on top of both.
The SandboxProvider contract
A host implements this interface once, against Docker, Firecracker,
E2B, Modal, or its own runner, and every sandboxed tool reuses it.
Each I/O method is capability-gated — capabilities() drives graceful
degradation when a backing process doesn't support, say, network
egress.
interface SandboxProvider {
readonly type: SandboxProviderType;
acquire(sessionId: string, options?: AcquireOptions): Promise<SandboxHandle>;
execute(handle: SandboxHandle, command: SandboxCommand): Promise<SandboxExecResult>;
readFile(handle: SandboxHandle, path: string): Promise<string>;
writeFile(handle: SandboxHandle, path: string, content: string): Promise<void>;
listFiles(handle: SandboxHandle, path: string): Promise<readonly FileEntry[]>;
release(handle: SandboxHandle): Promise<void>;
isAlive(handle: SandboxHandle): Promise<boolean>; // runtime heartbeat
capabilities(): SandboxCapabilities;
}acquire is idempotent on (sessionId × chatId), so a re-entered
session reuses its sandbox rather than spawning a second one. An
optional streaming execute variant yields SandboxExecChunk events
for long-running commands. The exact option shapes
(AcquireOptions, SandboxCommand, SandboxCapabilities) are on the
npm README — see below.
Install
npm install @pleach/sandboxEndpoint-shape adapters
Four adapters ship inside @pleach/sandbox, each implementing
SandboxProvider against a different endpoint shape. The three
HTTP/child-process adapters carry their own subpaths (so you pull only
the one you wire); all four are also re-exported from the package root.
| Adapter | Import | Backing shape |
|---|---|---|
createHttpStreamSandboxProvider | @pleach/sandbox/adapters/httpStream | HTTP service with streaming responses (baseURL + auth) |
createHttpPollSandboxProvider | @pleach/sandbox/adapters/httpPoll | HTTP service you poll for completion (baseURL + auth) |
createChildProcessSandboxProvider | @pleach/sandbox/adapters/childProcess | local child process (workspaceDir, shell) |
createFargateSandboxProvider | @pleach/sandbox (root) | AWS ECS/Fargate task (region, cluster, taskDefinition, subnets) |
Vercel Sandbox, Modal, or E2B slot into whichever shape they present
— an HTTP-streaming service uses createHttpStreamSandboxProvider; you
don't need a per-vendor package. For tests, createInMemorySandboxProvider
ships at @pleach/sandbox/testing.
import { createChildProcessSandboxProvider } from "@pleach/sandbox/adapters/childProcess"
const provider = createChildProcessSandboxProvider({ workspaceDir: "/srv/agent-scratch" })API surface
The full SandboxProvider method set, per-adapter option shapes, and
the @deprecated alias surface live on the package's npm page:
@pleach/sandbox.
The npm README is the source of truth for the contract itself; if a
claim here disagrees with the published README, the README wins.
Where to go next
@pleach/base-tools
Domain-agnostic tool primitives — math, datetime, scratchpad, unit_convert, text_search, json_query, memory, and opt-in web_search, web_fetch, code_exec, ask_user, filesystem, and recall.
@pleach/replay
The `@pleach/replay` package — ReplayClient + ReplayHandle walk the canonical event log via runtime.events.iterate/fold, with tenantId required and a typed cache-miss policy.