NodeOps
CN

Run Claude Managed Agents on CreateOS Sandbox

With Claude Managed Agents, Anthropic runs the agent loop — the model, the context, the decision about which tool to call next. It does not run the tool calls. Those need a machine, and that machine is yours.

CreateOS Sandbox is that machine: a hardware-isolated sandbox that boots in about a second, holds state for as long as you want it to, and whose network egress you control from the outside. Anthropic keeps the orchestration; you keep the execution boundary — its filesystem, its network, and its logs.

Two ways to wire it up

Most sandbox vendors document exactly one of these. CreateOS supports both, and the choice is a real one, so pick deliberately.

Self-hosted environmentSandbox as a tool
Who runs the agent's basha worker inside your sandboxyour orchestrator, in a sandbox it owns
Anthropic environment typeself_hostedcloud
Agent's toolsthe built-in agent toolset (bash, file edit, …)one custom tool you define
Credentials on your hostorg key + environment keyorg key only
Who reaches out to whomyour worker polls Anthropic's work queueAnthropic streams you a tool call; you answer it
Best forfull agentic coding — the agent wants a real filesystem it can live ina narrow, auditable execution surface: the agent may run this, and nothing else

Both keep the agent's execution inside CreateOS. They differ in how much of the agent's world lives there.

Self-hosted environment

The agent's own toolset runs in your sandbox. Anthropic hands work to a queue; a worker process inside the sandbox claims it, runs the tool call locally, and posts the result back. The agent gets a real machine — a filesystem it can build up over a session, a shell, a network — and none of it leaves the sandbox.

Claude Managed Agent (Anthropic)   ──▶  work queue  ◀── worker polls
                                                          │
                                          ┌───────────────┴───────────────┐
                                          │  CreateOS sandbox             │
                                          │  ant beta:worker poll         │
                                          │  → the agent's bash runs here │
                                          └───────────────────────────────┘

This is the topology Anthropic's self-hosted sandboxes guide describes, and the one every other sandbox vendor's integration page documents.

Sandbox as a tool

Anthropic hosts the agent loop (a cloud environment), and CreateOS is a discrete tool the agent reaches for. The agent emits a run_command tool call; your orchestrator runs it in a sandbox it owns and hands the output back.

Claude Managed Agent (Anthropic)
        │  agent.custom_tool_use  { command: "…" }
        ▼
your orchestrator  (holds the CreateOS API key — the agent never sees it)
        │  sandbox.runCommand("bash", ["-lc", command])
        ▼
CreateOS sandbox   ──── output ────▶  user.custom_tool_result

Give the agent only the custom tool — omit the built-in agent toolset — and CreateOS becomes its single execution path. There is no second shell for it to fall back to. The control-plane key stays on your host, and every command the agent runs passes through code you wrote, where you can log it, rewrite it, or refuse it.

Prerequisites

  • A CreateOS Sandbox API key and control-plane URL.
  • An Anthropic organization API key with the Managed Agents beta enabled (Console → API keys).
  • For a self-hosted environment only: an environment and an environment key (Console → Environments → Generate environment key). The environment key authenticates the worker to its queue and is a different credential — and a different auth scheme — from the org key. A cloud environment needs neither: nothing of yours polls the queue.

Set up a self-hosted environment

On the Anthropic side. Create a self_hosted environment and generate an environment key for it.

TypeScript
1const environment = await anthropic.beta.environments.create({
2 name: "createos-workers",
3 config: { type: "self_hosted" },
4});

On the CreateOS side. Boot a sandbox with the environment credentials in its environment, install the ant CLI, and start the worker as a daemon. It polls for work until you tear the sandbox down.

TypeScript
1const sandbox = await Sandbox.create({
2 shape: "s-4vcpu-4gb",
3 rootfs: "devbox:1",
4 envs: {
5 ANTHROPIC_BASE_URL: "https://api.anthropic.com",
6 ANTHROPIC_ENVIRONMENT_ID: environment.id,
7 ANTHROPIC_ENVIRONMENT_KEY: environmentKey, // never the org key
8 },
9});
10
11await sandbox.sh(`curl -fsSL "$ANT_RELEASE_URL" | tar -xz -C /usr/local/bin ant`);
12await sandbox.sh(
13 "nohup setsid ant beta:worker poll --workdir /workspace > /tmp/worker.log 2>&1 &",
14);

Every session you point at that environment now executes inside the sandbox.

TypeScript
1const agent = await anthropic.beta.agents.create({
2 name: "createos-worker",
3 model: "claude-haiku-4-5",
4 tools: [{ type: "agent_toolset_20260401" }],
5});
6const session = await anthropic.beta.sessions.create({
7 agent: agent.id,
8 environment_id: environment.id,
9});

Only the environment key is inside the sandbox. The org key stays on your host — so a prompt injection that fully compromises the agent still cannot spend your organization's credits or read your other sessions.

Set up sandbox-as-a-tool

Create a cloud environment and an agent whose only tool is yours.

TypeScript
1const environment = await anthropic.beta.environments.create({
2 name: "createos-tool",
3 config: { type: "cloud", networking: { type: "unrestricted" } },
4});
5
6const agent = await anthropic.beta.agents.create({
7 name: "createos-tool-agent",
8 model: "claude-haiku-4-5",
9 system:
10 "Your only way to run anything is the run_command tool, which runs a shell " +
11 "command on a remote Linux machine. Report its output verbatim.",
12 // No agent_toolset: the agent has no bash of its own, so every command it
13 // wants to run must go through CreateOS.
14 tools: [
15 {
16 type: "custom",
17 name: "run_command",
18 description: "Run a shell command on the remote Linux machine.",
19 input_schema: {
20 type: "object",
21 properties: { command: { type: "string" } },
22 required: ["command"],
23 },
24 },
25 ],
26});

Then run the loop. Open the event stream before sending the task — events emitted in between would otherwise be lost — and answer every tool call.

TypeScript
1const session = await anthropic.beta.sessions.create({
2 agent: agent.id,
3 environment_id: environment.id,
4});
5
6const stream = await anthropic.beta.sessions.events.stream(session.id);
7await anthropic.beta.sessions.events.send(session.id, {
8 events: [{ type: "user.message", content: [{ type: "text", text: task }] }],
9});
10
11for await (const event of stream) {
12 if (event.type === "agent.custom_tool_use") {
13 const output = await runInSandbox(String(event.input.command));
14 await anthropic.beta.sessions.events.send(session.id, {
15 events: [
16 {
17 type: "user.custom_tool_result",
18 custom_tool_use_id: event.id,
19 content: [{ type: "text", text: output }],
20 },
21 ],
22 });
23 } else if (event.type === "session.status_idle") {
24 // `requires_action` means the agent is idle *waiting on your tool result* —
25 // the run is not over. Only a real stop reason ends it.
26 if (event.stop_reason?.type === "requires_action") continue;
27 break;
28 }
29}

Sandbox lifecycle: per session, or per call

runInSandbox is where you choose what the agent's world looks like.

One sandbox per session — create it before the session starts, reuse it for every tool call, destroy it at the end. State persists: a file the agent writes in one call is there in the next, and the agent can build something up over a conversation.

A fresh sandbox per call — create, run, destroy, every time. Nothing persists, so nothing carries over: no cross-call contamination, no machine to clean up, and the blast radius of any single command is a sandbox that is already gone. The cost is a cold start per call, which on CreateOS measures 0.7–1.0 s end to end (create, run, destroy).

Pick per-call for untrusted or one-shot work, per-session for anything that needs to accumulate.

Why CreateOS

  • Egress you control from outside the sandbox. setEgress locks a sandbox to an allowlist; the rules are enforced in-kernel on the host, so code inside the sandbox cannot bypass them. An agent can reach your private service and still be unable to exfiltrate to the public internet — enforced, not merely prompted.
  • Hardware isolation. Every sandbox is its own kernel, not a shared-kernel container. That is the boundary you want between a language model's output and your infrastructure.
  • ~1 s cold start. Fast enough that a fresh sandbox per tool call is a real option, not a theoretical one.
  • State when you want it. Pause and resume a sandbox, snapshot it, or reattach a disk — a session can survive an idle user without burning compute.
  • Your keys stay yours. In both topologies the CreateOS API key lives on your host. In the sandbox-as-a-tool topology the agent's every command passes through your code first.

Gotchas

An empty tool result is rejected. Managed Agents rejects a tool result with no content. Always send something — (no output) is fine.

Report command failures, do not throw them. Sandbox.sh throws on a non-zero exit. Inside a tool body that is wrong: a failing command is a result the agent must see and reason about, not an orchestrator crash. Use runCommand and hand the exit code back with the output.

TypeScript
1const { result } = await sandbox.runCommand("bash", ["-lc", command]);
2const output = `${result.stdout}${result.stderr}`.trim() || "(no output)";
3return result.exit_code === 0 ? output : `exit ${result.exit_code}\n${output}`;

Hold the stream open. In the sandbox-as-a-tool topology the agent blocks on your tool result. If your orchestrator drops the stream mid-call, the session strands. A single-shot script is fine as-is; a long-lived service needs reconnect handling.

session.status_idle does not mean the run is over. It fires with stop_reason.type: "requires_action" while the agent waits on you. Break only on a real stop reason.

Anthropic does not stage files for you. A self-hosted sandbox starts empty — no repo, no session files. Pass what the agent needs through session.metadata and have your worker fetch it.

Working examples

All five run end to end against a live control plane.

ExampleTopologyWhat it shows
36 — self-hosted agent workerself-hostedone persistent sandbox running the worker
37 — sandbox per sessionself-hosteda fresh sandbox and worker per session
49 — egress-locked workerself-hostedthe agent reaches a private service but cannot exfiltrate
50 — sandbox as a tool, per sessionsandbox-as-a-toolone sandbox for the session; state survives between tool calls
51 — sandbox as a tool, per callsandbox-as-a-toola disposable sandbox per call; the same task, and the state is gone

Examples 50 and 51 run the same two-step task — write a file, then read it back in a separate tool call — so the difference between the two lifecycles is something you can watch happen.

100,000+ 构建者,同一个工作空间。

获取产品更新、构建者故事,以及帮助您更快交付的新功能抢先体验。

NodeOps is the agentic operating system for production AI. CreateOS is its flagship product.