Skip to main content
If How it Works left you unsure where an agent actually runs, what it costs, or which harnesses are “serverless”, this page states the execution model plainly.

The default is: no container

The agent class decides execution mode, not a per-turn setting:
  • SDK agents run in-process. AnthropicAgent, OpenAIAgent, and HermesAgent extend the AI SDK’s ToolLoopAgent; the opt-in ElizaAgent wraps an elizaOS AgentRuntime in the same process. All make plain HTTPS calls to a model provider: no subprocess, no container, the agent’s “environment” is your own process. These are the only agents that run unchanged inside a JS-only serverless runtime (a Cloudflare Worker, a Vercel function).
  • CLI / full-OS agents run as a child process. ClaudeCodeAgent, CodexAgent, AntigravityAgent, OpenCodeAgent, and every other CLI agent extend BaseCliAgent, spawning the vendor binary (claude, codex, opencode, …) via node:child_process. By default that child process runs on the host, in rootDir (this.cwd ?? options?.rootDir ?? process.cwd()), the same machine driving the run. No automatic per-turn container.
A full OS environment is always opt-in: wrap the work in <Sandbox>.

Three common guesses — all reasonable, all wrong by default

Newcomers guess the same three things wrong:

What you pay for — three billing axes

There is no single “cost of running an agent”: three independent axes, and a given run may touch one, two, or all three:

Opting into a full OS: <Sandbox>

<Sandbox> runs a child workflow (or a single step) inside an isolated runtime: whole-graph, per-step, or mixed, your choice. It renders as exactly one scheduler task per boundary (not per agent turn); children never become parent-run tasks. The runtime is pluggable (bubblewrap, docker, codeplane, cloudflare) or any custom provider you register, including the first-class Microsandbox provider. The lifecycle at a <Sandbox> boundary is: create the sandbox when the task starts → run the harness → capture a diff bundle of what changed → tear it down (unless you keep it). Cleanup, idle timeout, and reuse are the provider’s decision, not a Smithers-core guarantee. The Cloudflare provider, for example, creates one container keyed ${runId}-${sandboxId}, defaults cleanup: "destroy", and can keep the container or hold it warm with keepAlive. The Microsandbox provider exposes explicit create, start, stop, snapshot, and cleanup semantics.
Running a CLI agent in a fresh per-turn container is a composition you author, not a built-in switch: wrap the agent’s <Task> in a child workflow behind <Sandbox provider={…}>.

Statefulness — warm vs cold containers

CLI agents keep credentials and resumable sessions on disk, which decides whether a cold per-turn container is viable:
  • API-key billing → cold containers are fine. Pass apiKey and the agent forwards ANTHROPIC_API_KEY / OPENAI_API_KEY; sessions are stateless per turn, so a fresh (cold) container each turn works.
  • Subscription billing → needs a warm/persistent environment. ClaudeCodeAgent clears ANTHROPIC_API_KEY so the CLI bills your Claude Pro/Max subscription, reading credentials from a one-time interactive /login at <CLAUDE_CONFIG_DIR>/.credentials.json. CodexAgent mirrors this with CODEX_HOME / auth.json against the ChatGPT subscription. A cold container has none of that on disk, so subscription mode needs the credential/session directory to persist: a sticky/warm container or a mounted credential volume.

Serverless compatibility at a glance

Not fully serverless end-to-end today. The DB layer (dialect + Cloudflare Durable-Object-SQLite / D1 descriptors) and the SDK agents are Worker-native, but the core engine that advances every run uses node:fs and node:child_process (materializing git/jj worktrees on a real filesystem), and the gateway is a node:http server: the engine runs on a Bun host with a filesystem (a container on ECS/Cloud Run/GKE/a VM). Two efforts are in flight. The Node runtime (Vercel Node functions, Lambda) is closest: it has fs + child_process, the engine module imports cleanly under plain Node, and the platform layer is injectable (RunOptions.effectPlatformLayer accepts NodeContext.layer). A run now completes end-to-end under plain Node (regression-tested: a compute-task workflow on PGlite, worker-task dispatch falling back to in-memory message storage instead of bun-sqlite); agent-task and gateway validation under Node remain open. Isolates (Cloudflare Workers, Vercel Edge) are further out, needing the same node:fs/node:child_process seam; a Worker can host the storage + in-process-agent path today, not the run driver.

”Sandbox” means three different things

This overloading is the single biggest source of confusion — name the sense you mean:
  1. Tool sandbox — the built-in tools (read/write/edit/grep/bash) jailed to rootDir, with symlinks/network/timeouts denied by default. A path/permission jail, not an OS boundary.
  2. A CLI agent’s own internal policy — e.g. Codex’s sandbox: "read-only" | "workspace-write" | "danger-full-access" (seatbelt/seccomp inside the vendor binary). Passed straight through to the CLI; unrelated to the other two.
  3. The <Sandbox> component — compute isolation: a provider-backed container/VM that runs a child workflow. This is the only one that gives an agent “a real computer.”