GuideAgents in Sandbox

Claude Code

Run Claude Code inside a Cua cloud sandbox

Hand off coding tasks to Claude Code running in an isolated Cua cloud sandbox with full filesystem, terminal, and git access.

Setup

Install the SDK

pip install cua-sandbox

Set your keys

export CUA_API_KEY="your-cua-key"
export ANTHROPIC_API_KEY="your-anthropic-key"

Run Claude Code in a cloud sandbox

This spins up an ephemeral cloud VM via the Cua API. You'll need a CUA_API_KEY — get one from cua.ai/dashboard if you don't have one yet.

import asyncio, os
from cua_sandbox import Sandbox, Image

async def main():
    api_key = os.environ["ANTHROPIC_API_KEY"]

    # CUA_API_KEY is read from your environment automatically
    async with Sandbox.ephemeral(Image.linux("ubuntu", "24.04")) as sb:
        # Install Node.js + Claude Code
        await sb.shell.run("sudo apt-get update && sudo apt-get install -y curl", timeout=60)
        await sb.shell.run("curl -fsSL https://deb.nodesource.com/setup_20.x | sudo bash -", timeout=60)
        await sb.shell.run("sudo apt-get install -y nodejs", timeout=60)
        await sb.shell.run("sudo npm install -g @anthropic-ai/claude-code", timeout=120)

        # Hand off a task
        result = await sb.shell.run(
            f"ANTHROPIC_API_KEY='{api_key}' claude -p 'Create a hello world index.html' "
            f"--dangerously-skip-permissions --output-format stream-json --verbose",
            timeout=300,
        )
        print(result.stdout)

asyncio.run(main())

--dangerously-skip-permissions is required for headless execution. -p passes the task as a prompt.

Connect to an existing sandbox

Skip provisioning by connecting to a sandbox that's already running.

sb = await Sandbox.connect("my-sandbox")
async with sb:
    result = await sb.shell.run(
        f"ANTHROPIC_API_KEY='{api_key}' claude -p 'List all TODO comments' "
        f"--dangerously-skip-permissions",
        timeout=300,
    )
    print(result.stdout)

Work on a repository

Clone a repo, run a task, and inspect the diff.

await sb.shell.run("git clone https://github.com/user/repo.git /tmp/repo", timeout=60)

result = await sb.shell.run(
    f"cd /tmp/repo && ANTHROPIC_API_KEY='{api_key}' claude -p 'Fix the failing tests' "
    f"--dangerously-skip-permissions --output-format stream-json --verbose",
    timeout=300,
)
print(result.stdout)

diff = await sb.shell.run("cd /tmp/repo && git diff", timeout=30)
print(diff.stdout)

Structured output

--output-format stream-json --verbose emits one JSON object per line.

Event typeDescription
systemSession init — tools, model, config
assistantClaude's responses and tool calls
resultFinal summary — cost, duration, tokens
import json

for line in result.stdout.strip().splitlines():
    event = json.loads(line)
    if event["type"] == "result":
        print(f"Cost: ${event['total_cost_usd']:.4f}")
        print(f"Result: {event['result']}")

Resume a session

Extract the session_id from the init event and pass it back.

init = json.loads(result.stdout.strip().splitlines()[0])
session_id = init["session_id"]

followup = await sb.shell.run(
    f"ANTHROPIC_API_KEY='{api_key}' claude -p 'Now add CSS styling' "
    f"--dangerously-skip-permissions --output-format stream-json --verbose "
    f"--session-id {session_id}",
    timeout=300,
)

Custom system prompt

Write a CLAUDE.md into the repo before running Claude Code.

await sb.shell.run(
    "echo '# Rules\n- Use TypeScript\n- Write tests for all changes' > /tmp/repo/CLAUDE.md",
    timeout=10,
)

Claude Code automatically reads CLAUDE.md from the working directory.

Was this page helpful?