Skip to main content
Run Anthropic’s agent loop on Anthropic’s infrastructure, and run every tool call inside a Tensorlake sandbox you control.

The model: brain vs. hands

Claude Managed Agents splits an agent into two halves. Claude is the brain — the LLM, the agent loop, session state, and the work queue all live on Anthropic’s infrastructure. It decides which tool to call but never executes one. The sandbox is the hands — every bash, read, write, edit, glob, grep call actually runs inside an execution environment you own. A Claude Environment with hosting type Self-hosted doesn’t run tools itself. Instead it places a work item on a queue. Your orchestrator consumes that queue and turns each session into a Tensorlake sandbox. The sandbox runs a thin worker that attaches back to Anthropic and executes tool calls for the life of the session.

Why Tensorlake for this role

  • Fast boot / sub-second wake. An agent loop is a tight decide→execute→decide cycle: many short tool calls (bash, read, grep) separated by model think-time. A suspended sandbox resumes from its memory snapshot in ~0.6s — a restore, not a cold app boot — so the hands are ready the instant the brain calls a tool. Low per-tool-call latency without keeping a sandbox warm between turns, which is what makes Tensorlake a good home for executing the agent loop’s tool calls.
  • Snapshots & fork-from-snapshot. sandbox.checkpoint() captures filesystem (or full memory) state; Sandbox.create(snapshot_id=...) boots a fresh sandbox from it. Run it N times → N children exploring in parallel from one known-good state. Basis for best-of-N tool execution, parallel sub-agents, and retry-with-divergence.
  • Suspend / resume. Named sandboxes suspend when idle and resume with state intact — no cold-start on every turn, and the basis for the wake-on-request trick below.
  • Public port exposure. expose_ports(...) serves a process inside the sandbox at https://{port}-{id}.sandbox.tensorlake.ai with TLS terminated by Tensorlake’s proxy — no reverse proxy of your own.

Three orchestrator modes

The orchestrator’s job is identical in all three (orchestrator_lib.py: get-or-create a sandbox per session, drain the queue). Only where it runs differs:
ModeWhere it runsSpawn latencyNeeds
Webhook-in-sandbox (recommended)Inside a Tensorlake sandbox, port exposed publiclySub-second, scale-to-zeroNothing running on your side — wakes on request
PollingYour machine / serverSecondsA long-running host process
WebhookYour machine / server~InstantA public HTTPS endpoint + TLS
Run exactly one orchestrator per ANTHROPIC_ENVIRONMENT_ID.

Webhook-in-sandbox: scale-to-zero push

In the recommended mode the FastAPI receiver runs inside a Tensorlake sandbox, with port 5051 exposed at a public HTTPS URL. Anthropic pushes webhooks directly to Tensorlake — no host process, no TLS of yours. The sandbox is created with a short idle timeout, so when no inbound traffic arrives it suspends (memory and the running uvicorn process preserved). The next inbound webhook resumes it automatically. Measured: a confirmed-suspended sandbox served GET /healthz in ~0.6s and flipped to running — a memory-snapshot restore, not a cold app boot. Outbound polling from inside the sandbox does not keep it awake, so it still suspends on schedule. The result is push latency with nothing running and nothing billed while idle, which suits bursty workloads better than a long-running host process.

Long-running sessions: resume instead of recreate

The same suspend/resume primitive applies one layer down, to the per-session sandbox. A long-running agent is rarely busy throughout — it’s bursts of work separated by idle gaps (waiting on a human approval, a slow CI job, or think-time between turns). At SANDBOX_TIMEOUT_SECONDS an idle per-session sandbox auto-suspends: filesystem and processes frozen to a snapshot, nothing billed. The question on the next burst is whether you rebuild or restore:
Recreate from base (default)Resume the suspended sandbox
Idle costZero (suspended)Zero (suspended) — identical
Resume latencyFull create + image pull + setupMemory-snapshot restore — sub-second
Session working stateLost — re-clone repo, re-install deps, redo setupIntact — /workspace, deps, warm caches as left
Best forIndependent, cheap-to-setup burstsA session whose accumulated state is the work
In the reference code this is one decision in orchestrator_lib.py: _find_live_sandbox treats a suspended sandbox as not-live and recreates it. Set RESUME_SUSPENDED_SESSIONS=true and it instead connects to the suspended sandbox; the runner relaunch is the inbound op that resumes it (the relaunch path already exists for the max-idle case). The clean-slate default stays available because a fresh sandbox can’t inherit a half-broken state from a previous burst — resume is opt-in for when continuity matters more than a guaranteed clean slate.

Quickstart

The full, working integration — with the exact credentials, the Console-only steps, mode-by-mode setup, verification, and troubleshooting — lives in the reference repo’s examples/managed-agent README. That README is the source of truth; this section is just the shape of it so you know what you’re signing up for. The setup has four stages:
  1. Configurecd examples/managed-agent, copy .env.example.env and .env.local.example.env.local, then uv sync.
  2. Tensorlake — set TENSORLAKE_API_KEY in .env, uv run tl login, then make build to build the per-session sandbox image. (Keep the SDK key and the tl login session pointed at the same project — see the gotchas below.)
  3. Claude Platform — in a non-default workspace: create an Agent (make agent), create a Self-hosted Environment, and generate its environment key (Console-only). This populates ANTHROPIC_API_KEY + ANTHROPIC_AGENT_ID in .env.local and ANTHROPIC_ENVIRONMENT_ID + ANTHROPIC_ENVIRONMENT_KEY in .env.
  4. Orchestrator — pick one mode. For webhook-in-sandbox: make build-webhook, register the printed URL as a Webhook (Session lifecycle → Run started) with its signing secret in ANTHROPIC_WEBHOOK_SIGNING_KEY, then make webhook-sandbox (the secret is baked in at launch, so it must be set first).
Then drive a session from anywhere:
make session PROMPT="create hello.txt with 'hi' then read it back"
Success looks like a stream of running / thinking / tool calls (→ write, → read) ending in · done. If a session never streams, the README’s work.stats check and troubleshooting section diagnose the common causes (import-order 401, mismatched Tensorlake projects, workers_polling: 0 in webhook modes).

Injecting credentials and naming sandboxes

Two Tensorlake SDK facts shape how the orchestrator passes secrets and names sandboxes:
  • **Inject env vars per command, not on create. Pass every credential and per-session var (ANTHROPIC_ENVIRONMENT_KEY, and the session, work, and environment IDs) via start_process(env={...}), which merges on top of the sandbox base environment.
  • Sandbox names must be slugs — lowercase letters, digits, and hyphens only. Derive a sandbox name from a session id by slugifying it (e.g. agent-<slug>).
Setup footguns specific to the reference code — .env precedence, the order in which you import the SDK relative to loading credentials, and keeping the Python SDK key and the tl CLI session on the same project — are covered in the repo README’s troubleshooting section.

When you want parallelism

The parallel sub-agents example forks N sandboxes from one snapshot so a single agent session can explore N candidate solutions at once — parent.checkpoint() then Sandbox.create(snapshot_id=...) × N. Expose it to the agent as a bundled CLI helper (callable via bash), an MCP tool, or an orchestrator-side action gated by session metadata.

Next steps

Reference code

The full integration: image build, in-sandbox worker, and one orchestrator in three runnable modes.

Claude Managed Agents

Anthropic’s docs for the agent harness, sessions, and self-hosted environments.

Sandbox lifecycle

The suspend/resume and snapshot model that powers wake-on-request.

Parallel sub-agents

Fork N sandboxes from one snapshot for best-of-N tool execution.