Skip to content

Sandboxes and environments

After this page you can reason about the compute your agent runs on without ever addressing it: you will know how to tell whether a session currently has a sandbox, what happens to the session when it goes away, and how to decide whether more work can still be done.

There is no sandbox object on this API. You cannot create one, list one, extend one, keep one warm, or delete one. Nothing in /v1 takes a sandbox identifier.

What you get instead is a projection on the session — the workspace field:

{
"session": {
"id": "3f7d4f4c-2b6a-4a2e-9c1a-1f2b3c4d5e6f",
"workspace": { "state": "attached", "expiresAt": "2026-08-11T10:04:00.000Z" }
}
}

Three states, and that is the entire vocabulary:

workspace.state Meaning expiresAt
unavailable No sandbox is associated with this session. The normal state of a session that has just been created, and of one whose compute was released long ago. absent
attached A sandbox is leased to this session and the lease has not passed its deadline. present
expired A sandbox was leased and its deadline has passed. present, in the past

expired and unavailable are both “there is no compute right now”. They differ only in whether one existed: expired is your signal that state may be recoverable from a checkpoint, which is what continuation reports.

Compute is allocated by work, not by request. Creating a session provisions nothing; submitting a turn is what causes a sandbox to be leased, and the 202 comes back before that has happened. So unavailable immediately after a submit is expected, not an error.

Leases are bounded, and the bound is not yours to raise

Section titled “Leases are bounded, and the bound is not yours to raise”

A single sandbox lease has a hard ceiling of one hour of wall-clock life. It is a fixed constant, not a parameter: there is no request field, no header and no capability that raises it, and no keepalive that extends a lease past it.

This is much less restrictive than it sounds, because the ceiling bounds the sandbox, not the session. When a lease reaches its deadline the session is untouched — its turns, event log, artifact manifests and publications are all still there — and the next turn runs on a fresh sandbox. A conversation that spans a week is ordinary; it simply spans many leases.

What you must not build is a state machine that assumes the workspace it saw a minute ago is still there. Read workspace.state when it matters, and treat expired as routine.

Sessions outlive sandboxes, and continuation says at what cost

Section titled “Sessions outlive sandboxes, and continuation says at what cost”

Because compute is disposable and the session is not, every session reports what a further turn would actually get:

{
"continuation": {
"canContinue": true,
"state": "restorable",
"conversation": { "fidelity": "exact" },
"workspace": { "fidelity": "portable_tree" },
"environment": { "fidelity": "current" },
"boundaryRunId": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d"
}
}

state is the summary, and there are four values:

state canContinue What a next turn gets
live true The lease is still attached. The agent continues in place, conversation and files intact.
fresh true Nothing has run yet on this session. A next turn starts clean, which is what you wanted anyway.
restorable true Compute is gone, but a checkpoint can be restored: the conversation exactly, and the working tree as a portable copy.
unavailable false No sandbox and no restorable checkpoint. Further work in this session cannot resume the previous state.

The two fidelity fields say what “restore” means, and they are worth reading rather than collapsing into a boolean:

  • conversation.fidelitylive, exact, result_only, or none. exact means the agent resumes with the same history. result_only means only the outcome of prior work survived, not the reasoning that produced it.
  • workspace.fidelitylive, portable_tree, or none. portable_tree means the files come back as a captured copy rather than the original machine.
  • environment.fidelity is always current: a restored session runs on today’s managed environment, never a pinned historical one.

When canContinue is false, reason names the cause — checkpoint_unavailable, conversation_not_restorable, or workspace_not_restorable. boundaryRunId, when present, is the run at which the recoverable history ends; it is the honest dividing line to draw in a UI between “the agent remembers this” and “it does not”.

Capture is bounded. GET /v1/capabilities advertises the ceilings — artifacts.conversationCheckpointMaxBytes (16 MiB) and artifacts.workspaceCheckpointMaxBytes (256 MiB, also reported as environments[0].workspace.maxCheckpointBytes). A working tree larger than that cannot be captured, which is one way a session ends up unavailable with reason: "workspace_not_restorable". Read the numbers from discovery rather than hardcoding them.

Losing a sandbox during a run is a designed-for case, not an outage:

  • The run reaches a terminal state — typically crashed, and its event is wamp.run.crashed.
  • Any open interaction expires with wamp.interaction.expired and reason: "workspace_lost". It can no longer be answered; submit a fresh turn instead.
  • Events already recorded stay recorded. The log is append-only and gap-free across sandbox boundaries — sequence numbers belong to the session.
  • Artifacts already registered keep their manifests. Whether their bytes are still fetchable is a separate question, answered in Artifacts.
  • continuation is recomputed. Read it before deciding whether to retry the same session or open a new one.

The errors that mean “compute, not you” are worth handling explicitly:

Status Code What to do
503 cloud_workspace_starting Retry, honoring Retry-After: 2. A sandbox is coming up.
503 cloud_workspace_unavailable No sandbox could be provided. Retry with backoff; surface it if it persists.
409 cloud_workspace_expired The lease you were counting on is gone. Re-read the session and decide from continuation.
409 cloud_workspace_expiring The lease is too close to its deadline for the requested work. Retry; a fresh lease follows.
409 cloud_resume_unavailable The previous state cannot be restored. Start a new session for a clean run.
409 cloud_turn_execution_lost The turn’s execution died with its sandbox. Submit a new turn.

Note the asymmetry in how the retry hint arrives: for these codes Retry-After is a header only, while a 429 rate-limit response carries retryAfterSeconds in the body as well. Read the header.

GET /v1/capabilities reports an environments array. Today it has exactly one entry, and it is fixed:

{
"capabilities": {
"environments": [
{
"id": "managed",
"label": "WAMP managed sandbox",
"default": true,
"runtimes": ["wamp", "claude-code"],
"workspace": { "checkpoint": "portable_tree", "maxCheckpointBytes": 268435456 }
}
]
}
}

There is no environment selector. No request body accepts an environment id, there is no environment resource to create or configure, and managed is the only value the server produces. Read the entry for its workspace ceilings and for the runtimes it lists as usable; do not build a picker for a list of one, and do not send an environment field — request bodies reject unknown keys with 400 invalid_request.

You also do not choose where in the sandbox your work happens. If the session has a repository source, the checkout is placed for you and the agent starts there; if it has none, the agent gets an empty workspace. Paths are not part of the contract.

One call answers “can I send another turn, and what will the agent remember”:

Terminal window
curl -sS "$WAMP_API/v1/sessions/$SESSION_ID" \
-H "Authorization: Bearer $WAMP_TOKEN" \
| jq '{
workspace: .session.workspace,
canContinue: .session.continuation.canContinue,
state: .session.continuation.state,
conversation: .session.continuation.conversation.fidelity,
files: .session.continuation.workspace.fidelity,
reason: .session.continuation.reason,
busy: (.session.activeRun != null)
}'

A reasonable policy in a backend: if busy, wait. Else if canContinue, submit the turn — provisioning is the server’s problem. Else open a new session and, if the previous one produced a repository change, say so in the new task rather than hoping the agent recalls it.