---
title: Sandbox
description: Configure the isolated filesystem and command environment used by an eve agent.
url: "https://eve.dev/docs/sandbox"
docs_index: /llms.txt
---

> For an index of all documentation, see [/llms.txt](/llms.txt).

The sandbox is the isolated filesystem and command environment of an eve agent. Each agent has one, and the built-in `bash`, `read_file`, and `write_file` tools run in it. Define it in `agent/sandbox.ts`, or omit the file to let eve select an environment automatically.

## Define an environment

```text
agent/
├── agent.ts
└── sandbox.ts   # or agent/sandbox/sandbox.ts when agent/sandbox/ exists
```

```ts title="agent/sandbox.ts"
import { DefaultSandbox, defineSandbox } from "eve/sandbox";

// required: `eve build` prepares this export before any session exists
export const environment = DefaultSandbox.environment();
// required: the selector that opens the sandbox for a session
export default defineSandbox(() => environment.open());
```

- Put the file at `agent/sandbox/sandbox.ts` when the agent also has [workspace seeds](#managed-workspace-and-skills) or a Docker `Dockerfile` in `agent/sandbox/`. When the `agent/sandbox/` folder exists, put the definition there. A root `agent/sandbox.ts` beside that folder is a build error.
- `environment.open()` starts the sandbox for the current session.
- The selector runs until it succeeds once. After that, later workflow steps and process restarts resume the same sandbox without running the selector again.
- Commands start and relative paths resolve from the workspace root, `/workspace` on every built-in provider.

## Use the sandbox

Authored tools and callbacks get the live sandbox with `ctx.getSandbox()`:

```ts title="agent/tools/run_analysis.ts"
import { defineTool } from "eve/tools";
import { z } from "zod";

export default defineTool({
  description: "Run a Python script.",
  inputSchema: z.object({ script: z.string() }),
  async execute({ script }, ctx) {
    const sandbox = await ctx.getSandbox();
    await sandbox.writeTextFile({ path: "analysis.py", content: script });
    // run() doesn't throw on a nonzero exit code
    const result = await sandbox.run({ command: "python analysis.py" });
    if (result.exitCode !== 0) throw new Error(result.stderr);
    return result.stdout;
  },
});
```

- `run()` returns `{ exitCode, stdout, stderr }`. `sandbox.resolvePath(".")` returns the workspace root's absolute path.
- Use `spawn()` for a long-running process. On Vercel and Docker, pass `stdin: true` to get a writable `stdin` stream, for example to talk to a stdio server. Closing the stream sends EOF.
- Built-in environments set `AI_AGENT=eve` on every `run()` and `spawn()` call, including the `bash` tool and `prepare`. Agent-aware CLIs such as `vercel` then skip interactive prompts. To override it, set `AI_AGENT` in the environment's `env` option or in one call's `env`.

The handle from `ctx.getSandbox()` has only the methods every provider shares. Pass the configured environment to also get provider-specific methods, such as Vercel's `setNetworkPolicy()`:

```ts
import { environment } from "../sandbox";

const sandbox = await ctx.getSandbox(environment);
await sandbox.setNetworkPolicy("deny-all");
```

The environment you pass must match the one configured for the current sandbox.

## Providers

| Provider                             | Use it for                                                                                  |
| ------------------------------------ | ------------------------------------------------------------------------------------------- |
| [Default](/docs/sandbox/default)     | Vercel Sandbox on Vercel, Docker when reachable, and just-bash in development.              |
| [Vercel](/docs/sandbox/vercel)       | Persistent Vercel Sandboxes, prepared snapshots, domain network policies, and Drive mounts. |
| [Docker](/docs/sandbox/docker)       | Local containers from the default image, an existing OCI image, or a Dockerfile.            |
| [just-bash](/docs/sandbox/just-bash) | A pure-JavaScript shell and virtual filesystem without native process isolation.            |

`DefaultSandbox` accepts only `autoInstall` and `prepare` for just-bash. Use the [experimental options](/docs/sandbox/just-bash#experimental-options) of `JustBashSandbox.environment()` from `eve/sandbox/just-bash` for custom commands or filesystems.

## Initialize a session's sandbox

Pass live sandbox options to `open()`, then configure the sandbox before you return it. `open()` applies its options only when it creates the sandbox.

```ts
import { defineSandbox } from "eve/sandbox";
import { VercelSandbox } from "eve/sandbox/vercel";

export const environment = VercelSandbox.environment();

export default defineSandbox(async ({ session }) => {
  const sandbox = await environment.open({ networkPolicy: "deny-all" });
  await sandbox.writeTextFile({ path: ".eve/session", content: session.id });
  return sandbox;
});
```

Make initialization safe to repeat, because it can run more than once against the same sandbox:

- A subagent that shares the sandbox opens it before the parent does.
- A [workflow step](/docs/tools/workflows#use-the-session-sandbox-in-a-step) opens it before the session records it.
- The first turn of a prewarmed session opens it while prewarming is still starting it.
- A previous initialization failed.

A [prewarmed session](/docs/guides/frontend/use-eve-agent#prewarming), for example from `useEveAgent({ prewarm: true })`, runs the selector before the first message. The selector sees `session.auth` from the request that created the session. eve keeps the sandbox options and `defineState()` values from that run, even when the first message's `onMessage` hook returns a different identity. In an agent whose clients prewarm, don't derive either from `session.auth`.

## Prepare a reusable environment

Use the environment's `prepare` option for setup that every new sandbox inherits:

```ts
import { defineSandbox } from "eve/sandbox";
import { VercelSandbox } from "eve/sandbox/vercel";

export const environment = VercelSandbox.environment({
  prepare: async (sandbox) => {
    const result = await sandbox.run({
      command: "sudo apt-get update && sudo apt-get install -y jq",
    });
    if (result.exitCode !== 0) throw new Error(result.stderr);
  },
});

export default defineSandbox(() => environment.open());
```

`prepare` runs once per reusable template, not once per session. The template is a Vercel snapshot, a Docker image, or a just-bash filesystem template.

`eve build` prepares every environment. During `eve dev`, the first sandbox access after startup or a rebuild prepares all environments in the build, including those of subagents and extensions. That access can be slow, and a later access retries a failed preparation.

Production creates sandboxes only from the templates the build recorded, and never rebuilds a missing one. A missing image, snapshot, or template fails with rebuild or redeploy guidance; see [Sandbox troubleshooting](/docs/sandbox/troubleshooting).

## Reuse native resources across sessions

Built-in environments don't share sandboxes across sessions. To reuse native compute or storage, define a [custom provider](/docs/sandbox/custom-providers) that gives each session its own view of the shared resources. Keep the shared resources out of its `stop()` and `onSessionEnd()`. A session-specific path is not an isolation boundary, so sessions that don't trust each other need separate native compute or stronger isolation from the provider.

## Share a parent sandbox

A declared subagent can use the sandbox of the parent that called it:

```ts title="agent/subagents/reviewer/sandbox.ts"
import { defineParentSandbox } from "eve/sandbox";

export default defineParentSandbox();
```

- The parent and every subagent that shares its sandbox, including children of the built-in `agent` tool, use one sandbox that belongs to the parent's session.
- Whichever agent opens the sandbox first runs the parent's selector.
- eve deletes the sandbox when the parent session ends, even if only a subagent opened it.
- A subagent that shares its parent's sandbox can't declare its own managed workspace or skill files.

## Managed workspace and skills

Put workspace seed files in the sandbox directory:

```text
agent/sandbox/
  sandbox.ts
  workspace/
    schema.sql
```

Files under `agent/sandbox/workspace/` seed writable `/workspace`. Each new session sandbox starts with a copy of them. Later changes to the seeds don't overwrite existing sandboxes.

Agent skills appear at `$HOME/.agents/skills`, or at `skills/` under the workspace root when the sandbox has no usable home directory. Instructions and tools can refer to `/workspace` and `$HOME/.agents/skills` on every built-in provider. Don't refer to a concrete home directory or to `/eve/resources`, which is internal to providers.

A [custom provider](/docs/sandbox/custom-providers#bake-files-into-templates) can declare a different workspace root, so prefer relative paths in code that runs on any provider.

## Inbound attachments

eve copies files that reach the agent into `.eve/attachments/` in the workspace root, which is `/workspace/.eve/attachments/` on built-in providers. Session history keeps only a reference to each file. This covers:

- Files attached to user messages.
- Files that tools return with `toolOutputPart.file`.
- MCP image, audio, and blob content.

Files returned by URL with `toolOutputPart.fileUrl` aren't copied, because the model provider reads the URL directly; see [Send a file by URL](/docs/tools#send-a-file-by-url).

Every copied file reaches the model on every call with a label that names its sandbox path, such as `Attached file /workspace/.eve/attachments/<hash>/report.csv (text/csv)`. The agent can open that path with `read_file` or `bash`. The file's bytes follow the label only for formats every major provider reads natively:

- A PNG, JPEG, GIF, or WebP image up to 3 MiB and at most 8000 pixels per side.
- A PDF up to 20 MiB.

Other files, such as CSV, HEIC, audio, and video, reach the model as the label alone. An image or PDF whose bytes don't match its declared type is stored as `application/octet-stream`, so a provider never receives bytes it would reject.

To let the agent answer voice notes and other audio, add a tool that transcribes the copied file. See [Voice notes](/docs/patterns/voice-notes).

| What happens                                                           | What the model receives            |
| ---------------------------------------------------------------------- | ---------------------------------- |
| The copied file is missing from the sandbox, or something overwrote it | A `FileNotFound` note in its place |
| A channel's `fetchFile` fails                                          | A note that names the file         |
| A file part's `data` is not bytes, base64, a `data:` URL, or a URL     | A note that names the file         |

In the last two cases, the file never enters session history.

The model keeps seeing attachments from earlier turns. Each call sends the same bytes, so the provider's prompt cache stays valid. Compaction replaces files in older tool results with their text reference. `read_file` returns images and PDFs to the model as files. The agent can use it to reopen an attachment after compaction, or to view a PDF that `bash` wrote.

## Custom providers

To run sandboxes on a backend eve doesn't ship, define a provider with `defineSandboxProvider()` from `eve/sandbox/provider`. It implements a connection to one sandbox (`spawn`, `readFile`, `writeFiles`, `removePath`, and `stop`) and the methods `prepare`, `start`, `resume`, and `onSessionEnd`. See [Custom sandbox providers](/docs/sandbox/custom-providers) for a complete example.

## Lifecycle

Each session's sandbox persists across turns and deployments while the provider still has it.

| Event                                                                                                          | Result                                                                                                          |
| -------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `sandbox.stop()`                                                                                               | Compute stops and files persist. The next access resumes it.                                                    |
| `sandbox.delete()`                                                                                             | The sandbox and its files are deleted. The next `ctx.getSandbox()` runs the selector again for a fresh sandbox. |
| The native sandbox is gone on resume, such as after a removed container or deleted just-bash session directory | The session continues in a fresh sandbox, without the lost files.                                               |
| The session's sandbox came from another provider or state version, such as after you change providers          | The session continues in a fresh sandbox. eve doesn't delete the old one.                                       |
| The session ends                                                                                               | eve calls the provider's `onSessionEnd()` to delete the session's remaining sandbox.                            |

Session end doesn't wait for running subagent steps to stop. A subagent step still running at that moment can reopen the sandbox after cleanup, and eve doesn't remove a sandbox created that late.

New sessions get a new environment when you change the sandbox source, `prepare` code, Dockerfile, environment options, workspace seeds, or skills. Existing sessions keep their sandbox.

## What to read next

- [Sandbox troubleshooting](/docs/sandbox/troubleshooting) explains common sandbox errors and how to fix them.
- [Subagents](/docs/subagents) explains delegation and which agent's session a sandbox belongs to.
- [Security model](/docs/concepts/security-model) describes the app-runtime and sandbox trust boundary.
- [Vercel Sandbox](https://vercel.com/docs/sandbox) documents hosted sandbox behavior.

---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)