# Sandboxes, Built-in Tools & Skills

> Eve's single per-agent sandbox with shipped harness tools (bash, read_file, load_skill, connection_search) and sandbox proxying, contrasted with Flue's adapter-based sandbox factories (local, Cloudflare, blueprint-backed remote) and explicit tool/skill composition in createAgent.

- Repository: vercel/eve-with-withastro-flue
- GitHub: https://github.com/vercel/eve
- Human wiki: https://www.grok-wiki.com/public/wiki/vercel-eve-with-withastro-flue-43b600348681
- Complete Markdown: https://www.grok-wiki.com/public/wiki/vercel-eve-with-withastro-flue-43b600348681/llms-full.txt

## Source Files

- `vercel-eve:docs/sandbox.mdx`
- `vercel-eve:docs/concepts/default-harness.md`
- `vercel-eve:packages/eve/src/shared/sandbox-backend.ts`
- `withastro-flue:packages/runtime/src/sandbox.ts`
- `withastro-flue:packages/runtime/src/node/local.ts`
- `withastro-flue:packages/runtime/src/cloudflare/cf-sandbox.ts`
- `withastro-flue:blueprints/sandbox--modal.md`
- `withastro-flue:packages/runtime/src/skill-frontmatter.ts`

---

<details>
<summary>Relevant source files</summary>

The following files were used as context for generating this wiki page:

- [vercel-eve/docs/sandbox.mdx](vercel-eve/docs/sandbox.mdx)
- [vercel-eve/docs/concepts/default-harness.md](vercel-eve/docs/concepts/default-harness.md)
- [vercel-eve/packages/eve/src/shared/sandbox-backend.ts](vercel-eve/packages/eve/src/shared/sandbox-backend.ts)
- [vercel-eve/packages/eve/src/runtime/framework-tools/bash.ts](vercel-eve/packages/eve/src/runtime/framework-tools/bash.ts)
- [vercel-eve/packages/eve/src/runtime/framework-tools/skill.ts](vercel-eve/packages/eve/src/runtime/framework-tools/skill.ts)
- [withastro-flue/packages/runtime/src/sandbox.ts](withastro-flue/packages/runtime/src/sandbox.ts)
- [withastro-flue/packages/runtime/src/node/local.ts](withastro-flue/packages/runtime/src/node/local.ts)
- [withastro-flue/packages/runtime/src/cloudflare/cf-sandbox.ts](withastro-flue/packages/runtime/src/cloudflare/cf-sandbox.ts)
- [withastro-flue/blueprints/sandbox--modal.md](withastro-flue/blueprints/sandbox--modal.md)
- [withastro-flue/packages/runtime/src/skill-frontmatter.ts](withastro-flue/packages/runtime/src/skill-frontmatter.ts)
- [withastro-flue/packages/runtime/src/agent-definition.ts](withastro-flue/packages/runtime/src/agent-definition.ts)
- [withastro-flue/packages/runtime/src/agent.ts](withastro-flue/packages/runtime/src/agent.ts)
- [withastro-flue/packages/runtime/src/session.ts](withastro-flue/packages/runtime/src/session.ts)

</details>

# Sandboxes, Built-in Tools & Skills

Eve and Flue both give agents an isolated execution environment and a default toolbelt for shell and file work, but they make different architectural bets. Eve ships a **single per-agent sandbox** with a **framework-owned harness**: built-in tools are always present, shell/file tools execute in the app runtime and **proxy** into the sandbox, and skills or connections add opt-in discovery tools. Flue treats the sandbox as a **pluggable adapter** wired explicitly in `createAgent`, normalizes every backend behind a `SessionEnv` interface, and **composes** tools and skills from the agent profile at initialization time.

Understanding this split matters when porting agents between frameworks or designing a provider-neutral harness: Eve optimizes for convention-over-configuration and a unified `/workspace` namespace; Flue optimizes for explicit wiring and backend interchangeability.

## Architectural contrast

```mermaid
flowchart TB
  subgraph Eve["Eve (vercel-eve)"]
    Harness["Default harness\n(built-in tools)"]
    AppRT["App runtime\n(tool executors)"]
    SandboxBE["SandboxBackend\n(vercel / docker / microsandbox / just-bash)"]
    WS["/workspace\n(single sandbox per agent)"]
    Harness --> AppRT
    AppRT -->|"proxy: bash, read_file, …"| SandboxBE
    SandboxBE --> WS
  end

  subgraph Flue["Flue (withastro-flue)"]
    CA["createAgent(() => ({ sandbox, tools, skills }))"]
    SF["SandboxFactory\n(local / bash / cloudflare / blueprint)"]
    SE["SessionEnv\n(exec, readFile, writeFile, …)"]
    CT["createTools(env)\nor adapter.tools"]
    CA --> SF
    SF --> SE
    SE --> CT
  end
```

Sources: [vercel-eve/docs/concepts/default-harness.md:25-40](), [vercel-eve/packages/eve/src/runtime/framework-tools/bash.ts:50-52](), [withastro-flue/packages/runtime/src/types.ts:835-850](), [withastro-flue/packages/runtime/src/agent.ts:39-49]()

| Dimension | Eve | Flue |
|-----------|-----|------|
| Sandbox count | Exactly one per agent | One `SessionEnv` per initialized harness; task sessions can scope `cwd` |
| Wiring | Automatic; override via `agent/sandbox/sandbox.ts` | Explicit: `createAgent(() => ({ sandbox: local() }))` |
| Backend model | `SandboxBackend` with `prewarm` + `create` lifecycle | `SandboxFactory` → `SessionEnv`; remote providers implement `SandboxApi` |
| Built-in tools | Shipped with harness; override/disable by filename | `createTools(env)` unless adapter supplies `tools` factory |
| Skills tool | `load_skill` (reads from sandbox) | `activate_skill` (lazy instruction load) |
| External integrations | `connection_search` when connections declared | MCP tools merged via `tools` array; no built-in connection search |

## Eve: one sandbox, proxied harness tools

### Single sandbox per agent

Every Eve agent has exactly one sandbox rooted at `/workspace`. The built-in `bash`, `read_file`, `write_file`, `glob`, and `grep` tools target it automatically, and authored runtime code reaches the same environment through `ctx.getSandbox()`. The path namespace is stable across backends so `/workspace/foo` resolves consistently whether the backend is local Docker or hosted Vercel Sandbox.

Sources: [vercel-eve/docs/sandbox.mdx:6-21](), [vercel-eve/docs/sandbox.mdx:39-41]()

### Sandbox backends and lifecycle

Eve selects where the sandbox runs through a pluggable `SandboxBackend` interface. Built-in factories include `vercel()`, `docker()`, `microsandbox()`, `justbash()`, and availability-aware `defaultBackend()`. The backend implements a two-phase lifecycle: `prewarm` captures reusable template state at build time (bootstrap hooks, seed files), and `create` opens or reattaches a durable session at runtime.

Authors customize via `defineSandbox` with optional `bootstrap` (template-scoped) and `onSession` (per-session) hooks, seed files under `agent/sandbox/workspace/`, and network policy on the backend factory or in `onSession`.

Sources: [vercel-eve/packages/eve/src/shared/sandbox-backend.ts:121-164](), [vercel-eve/docs/sandbox.mdx:111-131](), [vercel-eve/docs/sandbox.mdx:160-182]()

### Built-in tools and sandbox proxying

The default harness registers a fixed tool set. Shell and filesystem tools are notable because their **executors live in the app runtime** but their **effects land in the sandbox**:

| Tool | Effect location | Notes |
|------|-----------------|-------|
| `bash`, `read_file`, `write_file`, `glob`, `grep` | Sandbox | Proxied via `requireSandboxSession()` / `ctx.getSandbox()` |
| `web_fetch`, `todo`, `ask_question`, `agent` | App runtime | No sandbox involvement |
| `web_search` | Provider | No local executor |
| `load_skill` | App runtime executor; reads sandbox FS | Present when agent declares skills |
| `connection_search` | App runtime | Present when agent declares connections |

The `bash` tool illustrates the proxy pattern: the framework executor calls `executeBashOnSandbox(await requireSandboxSession(), input)` rather than running shell commands in the host process.

Sources: [vercel-eve/docs/concepts/default-harness.md:25-46](), [vercel-eve/packages/eve/src/runtime/framework-tools/bash.ts:50-56]()

Authors can override a built-in by authoring `agent/tools/<slug>.ts` with the same slug, spread defaults from `eve/tools/defaults`, or disable one with `disableTool()`. A typo in the disable filename fails at build time.

Sources: [vercel-eve/docs/concepts/default-harness.md:49-78]()

### Skills: `load_skill`

Skills live under `agent/skills/`. Eve advertises each skill's description in the prompt and registers `load_skill` when skills are declared. Loading pulls the skill's markdown from the sandbox filesystem (skills are seeded under `/workspace/skills/`) and appends instructions to the active turn. Loading adds **instructions only** — no new execution surface.

Sources: [vercel-eve/docs/skills.mdx:8-18](), [vercel-eve/packages/eve/src/runtime/framework-tools/skill.ts:14-32](), [vercel-eve/docs/concepts/default-harness.md:44-45]()

### Connections: `connection_search`

When an agent declares connections, Eve registers a dynamic `connection__search` tool (model-facing name `connection_search`). Search surfaces connection tools by qualified name (e.g. `connection__linear__list_issues`), which the model can then call directly. This is separate from the static framework tool list.

Sources: [vercel-eve/docs/concepts/default-harness.md:39-46](), [vercel-eve/packages/eve/src/runtime/framework-tools/index.ts:33-38]()

## Flue: adapter factories and explicit composition

### `SandboxFactory` and `SessionEnv`

Flue does not assume a sandbox exists. The agent initializer returned from `createAgent` must supply a `sandbox` field — a `SandboxFactory` whose `createSessionEnv()` produces a `SessionEnv` with `exec`, filesystem operations, `cwd`, and `resolvePath`. All adapters — local host, just-bash, Cloudflare Sandbox, or blueprint-installed remote SDKs — converge on this interface.

```ts
// Local Node target
createAgent(() => ({ sandbox: local(), model: 'anthropic/claude-sonnet-4-6' }));

// Cloudflare Durable Object stub
createAgent(({ id, env }) => ({
  sandbox: cloudflareSandbox(getSandbox(env.Sandbox, id)),
}));

// just-bash in-process
createAgent(() => ({ sandbox: bash(() => new Bash({ fs })) }));
```

Sources: [withastro-flue/packages/runtime/src/node/local.ts:16-21](), [withastro-flue/packages/runtime/src/cloudflare/cf-sandbox.ts:37-56](), [withastro-flue/packages/runtime/src/sandbox.ts:116-124](), [withastro-flue/packages/runtime/src/types.ts:835-850]()

### Adapter layering

Flue's `sandbox.ts` module implements the adapter stack:

1. **`bash(factory)`** — wraps a just-bash `BashFactory` into `SessionEnv`
2. **`createSandboxSessionEnv(api, cwd)`** — wraps any `SandboxApi` (remote provider) with path resolution, parent-dir creation on write, and centralized abort checks
3. **`local()`** — thin factory over host `child_process` + filesystem (Node target)
4. **`cloudflareSandbox(stub)`** — maps Cloudflare Sandbox DO methods onto `SandboxApi`

Remote adapters implement `SandboxApi.exec` with `timeoutMs` as the primary cancellation contract; `AbortSignal` is optional and honored when the provider SDK supports it.

Sources: [withastro-flue/packages/runtime/src/sandbox.ts:198-312](), [withastro-flue/packages/runtime/src/cloudflare/cf-sandbox.ts:59-181]()

### Blueprint-backed remote sandboxes

For providers without a first-party Flue export, blueprints document how to write an adapter. The Modal blueprint, for example, wraps a user-owned Modal Sandbox into `SandboxFactory` by implementing `SandboxApi` — shelling out for `stat`, `readdir`, `mkdir`, and `rm` where the SDK only exposes `exec` and `open`. The user owns sandbox lifecycle (create, configure, tear down); Flue only adapts.

Sources: [withastro-flue/blueprints/sandbox--modal.md:15-33](), [withastro-flue/blueprints/sandbox--modal.md:96-279]()

### Built-in tools: composed from `SessionEnv`

Unlike Eve's always-on harness, Flue builds the default tool list in `createTools(env)` at session initialization:

| Tool | Backing |
|------|---------|
| `read` | `env.stat` / `env.readFile` (+ packaged skill paths) |
| `write` | `env.writeFile` |
| `edit` | read + write on `env` |
| `bash` | `env.exec` |
| `grep` | `env.exec` (probes for `rg`, falls back to `grep`) |
| `glob` | `env.exec` |
| `task` | subagent delegation (appended when subagents exist) |

A `SandboxFactory` may replace the entire default list via an optional `tools` factory. The session layer always appends `task` and, when skills exist, `activate_skill`. Adapter-supplied tools must not collide with reserved names.

Sources: [withastro-flue/packages/runtime/src/agent.ts:39-49](), [withastro-flue/packages/runtime/src/session.ts:1220-1264](), [withastro-flue/packages/runtime/src/types.ts:829-833]()

### Explicit tool and skill composition in `createAgent`

`createAgent` merges capabilities from an optional `profile` and per-init options:

```ts
createAgent(() => ({
  sandbox: local(),
  model: 'anthropic/claude-sonnet-4-6',
  instructions: '…',
  skills: [mySkill],
  tools: [myCustomTool, ...mcpTools],
  subagents: [researchProfile],
}));
```

`resolveAgentProfile` concatenates `skills`, `tools`, and `subagents` arrays from profile and runtime config. Nothing is implicit — if you omit `tools`, you get sandbox defaults plus framework additions (`task`, `activate_skill`), not a hidden web-search or connection layer.

Sources: [withastro-flue/packages/runtime/src/agent-definition.ts:72-104]()

### Skills: `activate_skill` and frontmatter validation

Flue discovers skills from `.agents/skills/<name>/SKILL.md` at session `cwd` and merges them with explicitly packaged skills. The system prompt instructs the model to call `activate_skill` before work that matches a skill description. `parseSkillMarkdown` enforces Agent Skills frontmatter rules: required `name` and `description`, name-directory matching, `allowed-tools` parsing, and tolerant handling of unknown metadata fields.

Sources: [withastro-flue/packages/runtime/src/context.ts:48-105](), [withastro-flue/packages/runtime/src/skill-frontmatter.ts:18-72](), [withastro-flue/packages/runtime/src/agent.ts:333-370]()

## Portable patterns and migration notes

**Unified workspace root.** Eve standardizes on `/workspace` across all backends. Flue adapters pick their own default `cwd` (`/workspace` for Cloudflare, configurable for Modal, host path for `local()`). When porting, align `cwd` and path resolution semantics.

**Proxy vs direct execution.** Eve's separation keeps tool policy (read-before-write, compaction resets) in the app runtime while isolating command effects. Flue's tools call `SessionEnv` directly — simpler stack, but the adapter must implement the full filesystem/exec contract.

**Skill activation.** Both frameworks use lazy skill loading and Agent Skills-compatible `SKILL.md` layout, but Eve reads skills from the sandbox (`load_skill` + `/workspace/skills/`) while Flue activates from discovered catalog paths (`activate_skill`). Skill *instructions* never add tools in either framework; behavior still comes from the agent's existing tool set.

**Backend extensibility.** Eve's `SandboxBackend` owns build-time prewarm and runtime reconnect metadata. Flue's `SandboxApi` + blueprint pattern pushes provider lifecycle to the user and focuses the adapter on conforming `SessionEnv`. A Flue Modal adapter and an Eve `defineSandbox({ backend: vercel() })` solve the same isolation problem with inverted ownership boundaries.

**Provider neutrality.** Both designs stay BYOC/BYOK friendly: Eve's `defaultBackend()` chain and custom `SandboxBackend` implementations avoid locking to one host; Flue's `SandboxFactory` slot accepts any adapter that satisfies `SessionEnv`, with skills sourced from local directories or packaged catalogs rather than a proprietary connector.

## Summary

Eve treats the sandbox as a first-class framework resource — one per agent, seeded, lifecycle-managed, and reached through proxied harness tools (`bash`, `read_file`, …) plus opt-in `load_skill` and `connection_search`. Flue treats the sandbox as an explicit `createAgent` dependency — adapter-selected, `SessionEnv`-normalized, with tools and skills composed from profile arrays and optional adapter overrides. Eve favors batteries-included defaults; Flue favors interchangeable backends and visible composition. Both keep skills as lazy instruction loads separate from the execution surface, making skill packs portable across file, repository, or catalog sources without tying them to a specific model provider.
