> ## 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 a Tensorlake sandbox as the self-hosted execution environment for Claude Managed Agents. Anthropic runs the agent loop. Every tool call runs in a sandbox you control.

Claude Managed Agents can run its tools in an environment you host. This guide shows how to make that environment a Tensorlake sandbox: Anthropic runs the agent loop, an orchestrator you run creates one sandbox per session, and a worker inside that sandbox executes each tool call. You'll learn how the parts fit together, where to run the orchestrator, how sessions suspend and resume, and how to run the reference integration.

## How a managed agent splits between Anthropic and your sandbox

A managed agent has two halves, and only one of them runs on Anthropic's infrastructure. Anthropic hosts the model, the agent loop, session state, and a work queue. It decides which tool to call but never executes one. Every `bash`, `read`, `write`, `edit`, `glob`, and `grep` call runs in an execution environment you own.

When you set a Claude Environment's hosting type to **Self-hosted**, Anthropic doesn't run tools itself. Each time a session run starts, it adds a work item to the environment's queue. Your orchestrator reads that queue and creates a Tensorlake sandbox for the session. A worker inside the sandbox connects back to Anthropic and executes tool calls until the session ends.

```mermaid theme={null}
flowchart LR
    app["Your app (Anthropic SDK)"] <-->|events| anthropic["Anthropic<br/>agent loop, session state, work queue"]
    anthropic -->|"one work item per session run"| orch["Orchestrator<br/>drains the queue, one sandbox per session"]
    orch -->|"get_or_create + start_process"| sbx["Tensorlake sandbox<br/>one per session"]
    sbx -->|"worker connects back and executes tool calls"| anthropic
```

## Where to run the orchestrator

The orchestrator does the same work in every mode: get or create a sandbox for the session, launch the worker, then drain the queue. The reference implementation keeps that logic in one module, `src/orchestrator_lib.py`, and ships three entrypoints. The modes differ in where the code runs and how it learns about new work:

| Mode | Where it runs | How it learns about work | What you operate |
| - | - | - | - |
| Webhook-in-sandbox (recommended) | A Tensorlake sandbox with port 5051 exposed | Anthropic pushes a webhook to the sandbox's public URL | Nothing. The sandbox suspends when idle and resumes on the next request |
| Polling | Your machine or server | Long-polls the work queue | A process that stays running |
| Webhook | Your machine or server | Anthropic pushes a webhook to your endpoint | A public HTTPS endpoint with TLS |

<Warning>Run exactly one orchestrator per `ANTHROPIC_ENVIRONMENT_ID`: one host process or one webhook sandbox, never both.</Warning>

## How webhook-in-sandbox mode works

In this mode the webhook receiver runs inside a Tensorlake sandbox, and Tensorlake serves port 5051 at a public HTTPS URL. Anthropic pushes webhooks to that URL. You run no host process and manage no TLS certificate.

The launcher creates the receiver sandbox with an idle timeout, set by `WEBHOOK_SANDBOX_TIMEOUT_SECONDS` (default 600s). When no inbound request arrives within that window, the sandbox suspends, and Tensorlake preserves its memory and the running uvicorn process. The next inbound webhook resumes it. Tensorlake doesn't bill a suspended sandbox.

Resume is a memory-snapshot restore, not a cold boot. In a test on tensorlake 0.5.30, a sandbox in the `suspended` state answered `GET /healthz` in about 0.6s and moved to `running`. Only inbound traffic through the exposed port counts as activity. The receiver's own outbound polling doesn't keep the sandbox awake, so it suspends on schedule.

## How session sandboxes suspend and resume

Each session's sandbox follows the same cycle. An agent session works in bursts, separated by waits for a human approval, a slow CI job, or model think time. After `SANDBOX_TIMEOUT_SECONDS` without activity, the session's sandbox suspends, and Tensorlake freezes its filesystem and processes in a snapshot. Tensorlake doesn't bill a suspended sandbox.

When the next work item arrives for that session, the orchestrator calls `Sandbox.get_or_create()` with the same name. One call handles every case:

* **No sandbox exists**: creates it from the image and blocks until it's running
* **The sandbox is running**: attaches to it
* **The sandbox is suspended**: resumes it from its snapshot, with `/workspace`, installed dependencies, and warm caches intact

The `bind_outcome` attribute on the returned sandbox reports which path ran: `created`, `attached`, or `resumed`. The orchestrator then relaunches the worker with the session's environment variables. On attach or resume, the previous worker may have exited at its idle limit, but the filesystem stays intact, so only the worker process restarts.

Image and resource arguments apply only on create. An existing sandbox keeps its own. See [Get or create a named sandbox](/sandboxes/lifecycle#get-or-create-a-named-sandbox) for the API.

## Run the reference integration

The [reference repo](https://github.com/tensorlakeai/claude-managed-agents-tensorlake-sandbox) ships a working integration: an image build, the in-sandbox worker, and one orchestrator in three modes. The steps below set up the recommended webhook-in-sandbox mode. Two steps happen in the Claude Console and have no CLI equivalent. The [repo README](https://github.com/tensorlakeai/claude-managed-agents-tensorlake-sandbox/blob/main/examples/managed-agent/README.md) covers those Console steps, the other two modes, and troubleshooting.

<Steps>
  <Step title="Copy the environment files and install dependencies">
    ```bash theme={null}
    cd examples/managed-agent
    cp .env.example .env
    cp .env.local.example .env.local
    uv sync
    ```

    Credentials the orchestrator needs live in `.env`. Your Anthropic API key, workspace ID, and agent ID live in `.env.local`.
  </Step>

  <Step title="Log in to Tensorlake and build the session image">
    Set `TENSORLAKE_API_KEY` in `.env`, then run:

    ```bash theme={null}
    uv run tl login
    make build
    ```

    `make build` builds the image each session sandbox starts from. The Python SDK reads `TENSORLAKE_API_KEY` and the `tl` CLI uses your login session. Point both at the same Tensorlake project. If they differ, the build registers the image in one project and the launcher looks for it in another.
  </Step>

  <Step title="Create the agent and a self-hosted environment">
    In a non-default Claude Platform workspace, create the agent:

    ```bash theme={null}
    make agent NAME="Managed Agent"
    ```

    In the Claude Console, create an Environment with hosting type **Self-hosted** and generate its environment key. Set `ANTHROPIC_WORKSPACE_ID`, `ANTHROPIC_API_KEY`, and `ANTHROPIC_AGENT_ID` in `.env.local`. Set `ANTHROPIC_ENVIRONMENT_ID` and `ANTHROPIC_ENVIRONMENT_KEY` in `.env`.
  </Step>

  <Step title="Build and launch the webhook receiver">
    ```bash theme={null}
    make build-webhook
    make webhook-sandbox
    ```

    The launcher prints a public URL of the form `https://5051-<sandbox-id>.sandbox.tensorlake.ai`. The URL is keyed by sandbox ID, so recreating the sandbox changes it.
  </Step>

  <Step title="Register the webhook and pass the signing secret to the receiver">
    In Claude Platform, create a **Webhook** pointed at the printed URL and subscribe it to **Session lifecycle → Run started**. Put the signing secret in `ANTHROPIC_WEBHOOK_SIGNING_KEY` in `.env`, then relaunch the receiver in place:

    ```bash theme={null}
    make webhook-sandbox-restart
    ```

    The receiver reads the secret at launch, so it needs a restart after you set or rotate it. The sandbox ID and URL don't change on restart.
  </Step>

  <Step title="Drive a session">
    ```bash theme={null}
    make session PROMPT="create hello.txt with 'hi' then read it back"
    ```

    A working session streams `running`, `thinking`, and tool calls such as `→ write` and `→ read`, and ends with `· done`. If nothing streams, run the `work.stats` check in the README. In the webhook modes, `workers_polling` reads 0 between events because the receiver polls only on demand. Watch `depth` and `pending` rise and drain back to 0 instead.
  </Step>
</Steps>

## Pass credentials per process and name sandboxes as slugs

Two Tensorlake SDK rules shape how the orchestrator handles secrets and names:

* **Pass environment variables on `start_process`, not on create**: the orchestrator passes `ANTHROPIC_ENVIRONMENT_KEY` and the session, work, and environment IDs through `start_process(env={...})`. The values merge on top of the sandbox's base environment.
* **Use slug names**: sandbox names allow lowercase letters, digits, and hyphens only. Anthropic session IDs contain underscores and uppercase letters, so the orchestrator lowercases the ID, replaces each disallowed character with a hyphen, and prefixes `agent-`. The name is deterministic, so every work item for a session resolves to the same sandbox.

The reference code has three setup traps. Watch `.env` precedence, import `config` before the Tensorlake SDK so the API key loads first, and keep the SDK key and the `tl` CLI on one project. The [README's troubleshooting section](https://github.com/tensorlakeai/claude-managed-agents-tensorlake-sandbox/blob/main/examples/managed-agent/README.md#troubleshooting) covers each one.

## Run tool calls in parallel from one snapshot

The [parallel sub-agents example](/applications/parallel-sub-agents) forks N sandboxes from one snapshot, so a single session can explore N candidate solutions at once. The parent calls `checkpoint()`, then the orchestrator calls `Sandbox.create(snapshot_id=...)` N times. Expose it to the agent in one of three ways:

* A CLI helper in the image that the agent calls through `bash`
* A Model Context Protocol (MCP) tool
* 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 behind 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>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.