Skip to main content
If you have read How it Works and still cannot tell where an agent actually runs, what it costs, or which harnesses are “serverless”, this page is the answer. It is the one place that states the execution model plainly.

The default is: no container

Which execution mode you get is decided by the agent class, not by a per-turn setting:
  • SDK agents run in-process. AnthropicAgent and OpenAIAgent (and HermesAgent) extend the AI SDK’s ToolLoopAgent; the opt-in ElizaAgent wraps an elizaOS AgentRuntime in the same process. All of them make plain HTTPS calls to a model provider. There is no subprocess and 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, which spawns 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 that is driving the run. There is no automatic per-turn container.
Neither mode spins up an isolated OS per turn. SDK agents are API calls in your process; CLI agents are local child processes in rootDir. A full OS environment is opt-in — you wrap the work in <Sandbox>.

Three common guesses — all reasonable, all wrong by default

Newcomers consistently guess the same three things. Here is the correction:
GuessReality
”Each agent turn spins up a fresh sandbox that pauses between turns.”No — by default no agent is containerized at all. Containerization is an explicit <Sandbox> choice, and pause/resume is a provider feature you opt into.
”OpenCode is serverless, Claude Code never is.”Half right. OpenCode is not in-process serverless — it is still a spawned binary that needs a container; it just goes serverless via a cheap cold per-boundary container when its API keys come from the environment. Claude Code is “never” only in subscription mode; with an API key it is exactly as serverless as OpenCode.
”The real axis is which vendor.”The real axis is in-process SDK vs subprocess CLI, and within CLI, stateless (API key) vs stateful (subscription OAuth + resumable session on disk).

What you pay for — three billing axes

There is no single “cost of running an agent.” There are three independent axes, and a given run may touch one, two, or all three:
AxisWhat it is
Model tokens / subscriptionThe model provider bills per input/output token (SDK agents, or CLI agents in API-key mode), or draws down a subscription quota (Claude Pro/Max, ChatGPT Plus/Pro) in subscription mode.
Host computeWhen a CLI agent runs locally (the default), it consumes the machine that is driving the run — your laptop, a CI runner, a long-lived server.
Sandbox / provider computeOnly when you opt into <Sandbox>: the provider’s container or VM wall-clock, from create → run → teardown (longer if kept warm or left idling), plus any provider storage.

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 — e.g. the Freestyle VM example adapter. 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 per-turn 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. A Freestyle VM exposes explicit start() / stop() / fork() / delete() and idleTimeoutSeconds.
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={…}>. No agent adapter creates or pauses a container on its own — BaseCliAgent always spawns on the local host.

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, and it reads credentials materialized by a one-time interactive /login at <CLAUDE_CONFIG_DIR>/.credentials.json. CodexAgent mirrors this with CODEX_HOME / auth.json and bills 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

HarnessModeServerless verdictCost driver
AnthropicAgent, OpenAIAgent, HermesAgent, ElizaAgentIn-process SDK✅ Agent is fully serverless — runs inside a Worker/function, no containerModel tokens
OpenCodeAgentCLI child process⚠️ Serverless via a cold per-boundary container (<Sandbox>) when keys come from env; a subscription opencode auth login puts credentials on disk like the othersTokens + container wall-clock
ClaudeCodeAgent — API-key mode (apiKey set)CLI child process⚠️ Same as OpenCode: cold per-boundary container worksTokens + container wall-clock
ClaudeCodeAgent / CodexAgent — subscription mode (default)CLI child process⚠️ Needs a warm/sticky container or mounted credential volumeSubscription quota + warm container time
AntigravityAgent, PiAgent, KimiAgent, ForgeAgent, AmpAgent, othersCLI child process⚠️ Container required; warm vs cold depends on the CLI’s session/credential handlingTokens/subscription + container time
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 (it materializes git/jj worktrees on a real filesystem), and the gateway is a node:http server. So today 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 now 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, with the worker-task dispatch path falling back to in-memory message storage instead of bun-sqlite), and agent-task and gateway validation under Node remain. Isolates (Cloudflare Workers, Vercel Edge) are further out and also need the node:fs/node:child_process seam; a Worker can host the storage + in-process-agent path today, but 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.”