# How the Agent-Sandbox Separation Architecture Works in Open Agents

> Discover how the agent sandbox separation architecture in Open Agents isolates logic from execution. Learn how Vercel Firecracker VMs enable secure LLM loops and delegated operations.

- Repository: [Vercel Labs/open-agents](https://github.com/vercel-labs/open-agents)
- Tags: architecture
- Published: 2026-04-16

---

**The agent-sandbox separation architecture in Open Agents isolates the decision-making logic (agent) from the execution environment (sandbox) through a generic interface, allowing the agent to run LLM loops while delegating file operations, command execution, and networking to an isolated Vercel Firecracker VM.**

Open Agents by Vercel Labs implements a clean architectural boundary between the *agent* that reasons and plans, and the *sandbox* where code actually executes. This separation ensures security, enables persistence through snapshots, and keeps the agent core portable across different execution backends. The architecture relies on an abstract `Sandbox` interface that the agent consumes, while concrete implementations handle the actual isolation mechanics.

## Core Components of the Architecture

### The Agent Layer

The agent logic lives in [`packages/agent/open-harness-agent.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/agent/open-harness-agent.ts) and implements a `ToolLoopAgent`. This component is responsible for building system prompts, managing the LLM conversation loop, and deciding which tools to invoke. Crucially, the agent does not know it is running inside a Vercel VM—it only knows that it has access to a `sandbox` object passed via call options.

The `prepareCall` hook (lines 115-125) injects sandbox context—such as the working directory and current branch—into the system prompt. When the LLM decides to read a file, the agent invokes the `read` tool defined in [`packages/agent/tools/read.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/agent/tools/read.ts), which simply forwards the request to the sandbox interface.

### The Sandbox Interface

The contract between agent and execution environment is defined in [`packages/sandbox/interface.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/sandbox/interface.ts). This file exports the abstract `Sandbox` type, along with supporting types like `SandboxHooks`, `SandboxStatus`, and `SandboxState`. The interface declares methods such as `readFile`, `writeFile`, `exec`, `snapshot`, and `stop`.

By programming against this interface, the agent remains backend-agnostic. The interface is re-exported through [`packages/sandbox/index.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/sandbox/index.ts), which also exposes the `connectSandbox` factory function that dispatches to concrete implementations based on a `type` discriminator.

### The Vercel Sandbox Implementation

The concrete implementation resides in [`packages/sandbox/vercel/sandbox.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/sandbox/vercel/sandbox.ts). This file implements the `Sandbox` interface using the `@vercel/sandbox` SDK, which orchestrates Firecracker microVMs. Key methods include:

- `create()` (lines 93-146): Provisions a new VM with specified CPU, memory, and timeout.
- `connect()` (lines 202-260): Reattaches to an existing sandbox using persisted state.
- `extendTimeout()` (lines 325-349): Adds time to a running session.
- `snapshot()` (lines 410-424): Captures disk state for later resumption.
- `stop()` (lines 446-461): Terminates the VM.

The implementation handles credential brokering, port exposure for preview servers, and file system operations via the SDK.

## How the Agent Communicates with the Sandbox

When the agent requires environmental interaction, it invokes a tool from `packages/agent/tools/*.ts` (e.g., [`read.ts`](https://github.com/vercel-labs/open-agents/blob/main/read.ts), [`write.ts`](https://github.com/vercel-labs/open-agents/blob/main/write.ts), [`bash.ts`](https://github.com/vercel-labs/open-agents/blob/main/bash.ts)). Each tool receives the sandbox instance through the `options.sandbox` parameter injected during the agent call.

**Data flow example:**

1. **User code** initializes the connection:

   ```typescript
   import { openHarnessAgent } from "@open-harness/agent";
   import { connectVercelSandbox } from "@open-harness/sandbox";

   async function runExample() {
     const sandbox = await connectVercelSandbox({
       name: "demo-session",
       source: { url: "https://github.com/vercel/next.js", branch: "main" },
       timeout: 300_000,
       ports: [3000],
       githubToken: process.env.GITHUB_TOKEN,
     });

     const result = await openHarnessAgent.call({
       sandbox,
       model: "anthropic/claude-opus-4.6",
     });
   }
   ```

2. **Agent reasoning**: Inside [`open-harness-agent.ts`](https://github.com/vercel-labs/open-agents/blob/main/open-harness-agent.ts), the `prepareCall` hook builds a system prompt containing sandbox context (working directory, git branch).

3. **Tool invocation**: When the LLM decides to read a file, the agent calls the `read` tool. The tool implementation in [`packages/agent/tools/read.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/agent/tools/read.ts) forwards the call:

   ```typescript
   async function readFileTool(sandbox: Sandbox, path: string) {
     return await sandbox.readFile(path, "utf-8");
   }
   ```

4. **Sandbox execution**: The call reaches `VercelSandbox.readFile` (lines 662-672 of [`packages/sandbox/vercel/sandbox.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/sandbox/vercel/sandbox.ts)), which uses the `@vercel/sandbox` SDK to fetch the file from the Firecracker VM.

5. **Response chain**: The result flows back through the tool to the agent, then to the LLM as a conversation message.

## Lifecycle Management and Persistence

The architecture supports long-running and resumable sessions through the factory pattern and state management.

### Factory and Connection Logic

The [`packages/sandbox/factory.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/sandbox/factory.ts) file exports `connectSandbox()`, which acts as a dispatcher. It inspects the `type` discriminator in the provided state or options (e.g., `type: "vercel"`) and routes to the appropriate connector. For Vercel sandboxes, this delegates to [`packages/sandbox/vercel/connect.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/sandbox/vercel/connect.ts).

### Reconnecting to Persisted Sessions

Sandboxes can be snapshotted and later resumed. The `VercelSandbox.snapshot()` method captures the VM disk state, while `connect()` reattaches to an existing session using persisted `SandboxState`.

```typescript
import { connectSandbox } from "@open-harness/sandbox";
import { VercelState } from "@open-harness/sandbox";

async function reconnect(state: VercelState) {
  const sandbox = await connectSandbox({ state });
  // Continue work with the restored file system and running processes
}

```

### Timeout Management

The Vercel implementation supports extending session timeouts via `extendTimeout()` (lines 325-349). This allows the agent to request additional compute time for long-running tasks without provisioning a new VM.

```typescript
// Inside an agent tool or external controller
if ("extendTimeout" in sandbox) {
  await sandbox.extendTimeout(120_000); // Add 2 minutes
}

```

## Key Implementation Files

| File | Role |
|------|------|
| [`packages/agent/open-harness-agent.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/agent/open-harness-agent.ts) | Core `ToolLoopAgent`; receives sandbox in call options and injects context into prompts. |
| [`packages/agent/tools/read.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/agent/tools/read.ts) | Tool forwarding file read requests to the sandbox interface. |
| [`packages/agent/tools/write.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/agent/tools/write.ts) | Tool forwarding file write requests. |
| [`packages/agent/tools/bash.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/agent/tools/bash.ts) | Tool forwarding command execution requests. |
| [`packages/sandbox/interface.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/sandbox/interface.ts) | Abstract `Sandbox` API definition (`readFile`, `writeFile`, `exec`, `snapshot`, etc.). |
| [`packages/sandbox/index.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/sandbox/index.ts) | Public exports including `connectSandbox` factory. |
| [`packages/sandbox/factory.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/sandbox/factory.ts) | Dispatcher that routes to concrete implementations based on type discriminator. |
| [`packages/sandbox/vercel/sandbox.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/sandbox/vercel/sandbox.ts) | Vercel Firecracker VM implementation with lifecycle management. |
| [`packages/sandbox/vercel/connect.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/sandbox/vercel/connect.ts) | Connection helper for creating or resuming Vercel sandboxes. |
| [`packages/sandbox/vercel/config.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/sandbox/vercel/config.ts) | Configuration types for the Vercel SDK. |

## Summary

- **Clean separation**: The agent in [`packages/agent/open-harness-agent.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/agent/open-harness-agent.ts) operates solely against the abstract `Sandbox` interface defined in [`packages/sandbox/interface.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/sandbox/interface.ts), remaining agnostic to whether execution happens in a Firecracker VM, Docker container, or local subprocess.

- **Interface-driven design**: Tools in `packages/agent/tools/*.ts` receive a sandbox instance via call options and forward operations (read, write, exec) to the interface methods, ensuring consistent error handling and resource management.

- **Vercel implementation**: The concrete sandbox in [`packages/sandbox/vercel/sandbox.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/sandbox/vercel/sandbox.ts) wraps the `@vercel/sandbox` SDK to provision Firecracker microVMs, supporting file system operations, command execution, port exposure, snapshotting, and timeout extension.

- **Factory routing**: [`packages/sandbox/factory.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/sandbox/factory.ts) dispatches to the correct implementation based on a type discriminator, enabling multi-backend support and seamless reconnection to persisted sessions via `connectSandbox()`.

## Frequently Asked Questions

### What is the agent-sandbox separation architecture in Open Agents?

The agent-sandbox separation architecture is a design pattern used in the vercel-labs/open-agents repository that isolates the LLM reasoning logic (agent) from the code execution environment (sandbox). The agent, implemented in [`packages/agent/open-harness-agent.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/agent/open-harness-agent.ts), makes decisions and calls tools, but all file operations, command execution, and network access are delegated to a sandbox object that implements the abstract `Sandbox` interface defined in [`packages/sandbox/interface.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/sandbox/interface.ts).

### How does the agent access files in the sandbox?

The agent accesses files through thin tool wrappers located in [`packages/agent/tools/read.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/agent/tools/read.ts) and [`packages/agent/tools/write.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/agent/tools/write.ts). When the LLM decides to read a file, the agent's `readFileTool()` function receives the sandbox instance from the call options and invokes `sandbox.readFile(path, "utf-8")`. This call travels through the abstract interface to the concrete implementation in [`packages/sandbox/vercel/sandbox.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/sandbox/vercel/sandbox.ts), which uses the `@vercel/sandbox` SDK to fetch the file from the Firecracker VM.

### Can I use a different sandbox backend with Open Agents?

Yes, the architecture supports swapping sandbox backends without modifying agent code. The agent depends only on the abstract `Sandbox` interface in [`packages/sandbox/interface.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/sandbox/interface.ts), not on the Vercel-specific implementation. To add a new backend, implement the `Sandbox` interface in a new package (e.g., `packages/sandbox/docker/`), then update [`packages/sandbox/factory.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/sandbox/factory.ts) to route to your implementation based on a new type discriminator. The `connectSandbox()` function will then return your custom sandbox when the corresponding state type is provided.

### How does sandbox persistence and reconnection work?

The Vercel sandbox supports persistence through the `snapshot()` and `connect()` methods defined in [`packages/sandbox/vercel/sandbox.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/sandbox/vercel/sandbox.ts). When `snapshot()` is called, the current Firecracker VM state is captured and returned as a `SandboxState` object containing a unique identifier and metadata. To reconnect, pass this state to `connectSandbox()` in [`packages/sandbox/factory.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/sandbox/factory.ts), which detects the `type:"vercel"` discriminator and delegates to the connection logic in [`packages/sandbox/vercel/connect.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/sandbox/vercel/connect.ts). This reattaches to the existing VM, preserving the file system and any running processes.