> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tensorlake.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Run Claude Managed Agents on Tensorlake Sandboxes

> Use Tensorlake sandboxes as the self-hosted execution environment for Claude Managed Agents — Anthropic runs the agent loop, every tool call runs in a sandbox you control.

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.

```mermaid theme={null}
flowchart LR
    app["Your app — Anthropic SDK"] <-->|events| anthropic["Anthropic<br/>agent loop · session state · work queue"]
    anthropic -->|"work item per session run"| orch["Orchestrator<br/>drain queue → sandbox per session"]
    orch -->|"Sandbox.create + start_process"| sbx["Tensorlake sandbox<br/>per session"]
    sbx -->|"worker attaches back · executes tool calls"| anthropic
```

## 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:

| Mode                                 | Where it runs                                      | Spawn latency             | Needs                                           |
| ------------------------------------ | -------------------------------------------------- | ------------------------- | ----------------------------------------------- |
| **Webhook-in-sandbox** (recommended) | Inside a Tensorlake sandbox, port exposed publicly | Sub-second, scale-to-zero | Nothing running on your side — wakes on request |
| **Polling**                          | Your machine / server                              | Seconds                   | A long-running host process                     |
| **Webhook**                          | Your machine / server                              | \~Instant                 | A public HTTPS endpoint + TLS                   |

<Warning>Run exactly one orchestrator per `ANTHROPIC_ENVIRONMENT_ID`.</Warning>

## 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 cost             | Zero (suspended)                                  | Zero (suspended) — identical                     |
| Resume latency        | Full create + image pull + setup                  | Memory-snapshot restore — sub-second             |
| Session working state | Lost — re-clone repo, re-install deps, redo setup | Intact — `/workspace`, deps, warm caches as left |
| Best for              | Independent, cheap-to-setup bursts                | A 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](https://github.com/tensorlakeai/claude-managed-agents-tensorlake-sandbox/blob/main/examples/managed-agent/README.md). 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. **Configure** — `cd 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:

```bash theme={null}
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](https://github.com/tensorlakeai/claude-managed-agents-tensorlake-sandbox/blob/main/examples/managed-agent/README.md#troubleshooting).

## When you want parallelism

The [parallel sub-agents example](/applications/parallel-sub-agents) 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

<CardGroup cols={2}>
  <Card title="Reference code" icon="github" href="https://github.com/tensorlakeai/claude-managed-agents-tensorlake-sandbox">
    The full integration: image build, in-sandbox worker, and one orchestrator in three runnable modes.
  </Card>

  <Card title="Claude Managed Agents" icon="book-open" href="https://platform.claude.com/docs/en/managed-agents/overview">
    Anthropic's docs for the agent harness, sessions, and self-hosted environments.
  </Card>

  <Card title="Sandbox lifecycle" icon="arrows-rotate" href="/sandboxes/lifecycle">
    The suspend/resume and snapshot model that powers wake-on-request.
  </Card>

  <Card title="Parallel sub-agents" icon="code-branch" href="/applications/parallel-sub-agents">
    Fork N sandboxes from one snapshot for best-of-N tool execution.
  </Card>
</CardGroup>
