CLAUDE LABJP
FORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtaskLIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its ownMCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MSPLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callbackSONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as OctoberFORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtaskLIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its ownMCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MSPLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callbackSONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as October
Articles/API & SDK
API & SDK/2026-04-12Advanced

Claude Managed Agents Sandbox Design: Running Autonomous Agents Safely in Production

A deep dive into the sandbox architecture of Claude Managed Agents, with production-ready security patterns and implementation code for running autonomous agents safely.

managed-agents5sandbox7security12production111api38

When embedding autonomous AI agents into a production service, the first wall you hit is drawing the line on how much freedom to grant them.

In April 2026, Anthropic released Claude Managed Agents in public beta, and it comes with a clear answer to this problem. Through a combination of container-based sandboxing, server-sent event (SSE) streaming, and built-in tools, the design balances agent autonomy with security.

But the official documentation alone doesn't cover the practical decisions you face in production: how to set container resource limits, what network access scope is appropriate, or how to handle runaway agents. This article draws on hands-on experience deploying Managed Agents in production to walk through sandbox security design patterns with concrete code.

The Three-Layer Sandbox Architecture

The Managed Agents sandbox isn't just a Docker container. It achieves defense-in-depth through three distinct layers.

import anthropic
 
client = anthropic.Anthropic()
 
# Specify sandbox settings when creating an agent
agent = client.agents.create(
    model="claude-sonnet-4-6",
    instructions="An agent that performs data analysis tasks",
    tools=[
        {"type": "code_execution"},
        {"type": "file_read"},
        {"type": "file_write"},
    ],
    # Sandbox configuration
    sandbox={
        "type": "managed",
        "memory_mb": 512,
        "timeout_seconds": 300,
        "network_access": "restricted",
        "allowed_domains": [
            "api.example.com",
            "storage.googleapis.com"
        ]
    }
)

Layer 1: Container isolation means each agent session runs in an independent Linux container. Filesystems are completely isolated—files created in one session are invisible to another.

Layer 2: Resource limits cap memory, CPU, and execution time. The code above specifies 512MB of memory and a 300-second timeout, but choosing the right values takes some thought.

Layer 3: Network policy is the most critical. Setting network_access to "restricted" blocks all outbound traffic except to domains explicitly listed in allowed_domains. There's almost never a reason to use "full" in production.

Practical Resource Limit Configuration

The official docs state that the default memory_mb is 1024MB, but in practice, the optimal value varies dramatically by task type.

# Resource configuration templates by task type
 
SANDBOX_PROFILES = {
    # Text processing (summarization, classification, transformation)
    "text_processing": {
        "memory_mb": 256,
        "timeout_seconds": 120,
        "network_access": "none",
    },
    # Data analysis (CSV/JSON parsing, chart generation)
    "data_analysis": {
        "memory_mb": 1024,
        "timeout_seconds": 600,
        "network_access": "restricted",
        "allowed_domains": ["storage.googleapis.com"],
    },
    # Code generation and execution
    "code_execution": {
        "memory_mb": 2048,
        "timeout_seconds": 900,
        "network_access": "restricted",
        "allowed_domains": ["pypi.org", "registry.npmjs.org"],
    },
    # External API integration
    "api_integration": {
        "memory_mb": 512,
        "timeout_seconds": 300,
        "network_access": "restricted",
        # allowed_domains set dynamically per task
    },
}
 
def create_agent_with_profile(profile_name: str, extra_domains: list[str] | None = None):
    """Create an agent based on a predefined profile"""
    config = SANDBOX_PROFILES[profile_name].copy()
 
    if extra_domains and config.get("network_access") == "restricted":
        config.setdefault("allowed_domains", [])
        config["allowed_domains"].extend(extra_domains)
 
    return client.agents.create(
        model="claude-sonnet-4-6",
        instructions=f"Profile: {profile_name}",
        tools=[{"type": "code_execution"}],
        sandbox={"type": "managed", **config},
    )

Pay attention to the "text_processing" profile's "network_access": "none". Text processing tasks rarely need external communication, and completely blocking the network minimizes security risk. The principle of least privilege applies just as strongly to agent design.

Real-Time Monitoring with SSE Streaming

A standout feature of Managed Agents is the ability to get real-time execution status via SSE (Server-Sent Events). This enables anomaly detection and automatic shutdown mechanisms.

import json
from collections import defaultdict
 
class AgentMonitor:
    """Monitors agent execution and detects anomalies"""
 
    def __init__(self, max_tool_calls: int = 50, max_errors: int = 3):
        self.max_tool_calls = max_tool_calls
        self.max_errors = max_errors
        self.tool_call_count = 0
        self.error_count = 0
        self.accessed_files: list[str] = []
 
    def process_event(self, event) -> dict:
        """Process an SSE event and return warnings if anomalies detected"""
        result = {"action": "continue", "warnings": []}
 
        if event.type == "tool_use":
            self.tool_call_count += 1
 
            # Check tool call count limit
            if self.tool_call_count > self.max_tool_calls:
                result["action"] = "stop"
                result["warnings"].append(
                    f"Tool call limit exceeded: {self.tool_call_count}/{self.max_tool_calls}"
                )
                return result
 
            # Track file access
            if event.tool_name in ("file_read", "file_write"):
                path = event.tool_input.get("path", "")
                self.accessed_files.append(path)
 
                # Detect access to sensitive paths
                sensitive_patterns = ["/etc/", "/proc/", "/sys/", ".env", "credentials"]
                for pattern in sensitive_patterns:
                    if pattern in path:
                        result["warnings"].append(
                            f"Sensitive path access detected: {path}"
                        )
 
        elif event.type == "error":
            self.error_count += 1
            if self.error_count >= self.max_errors:
                result["action"] = "stop"
                result["warnings"].append(
                    f"Error limit reached: {self.error_count}/{self.max_errors}"
                )
 
        return result
 
async def run_monitored_agent(agent_id: str, task: str):
    """Run an agent with monitoring"""
    monitor = AgentMonitor(max_tool_calls=30, max_errors=2)
 
    session = client.agents.sessions.create(agent_id=agent_id)
 
    with client.agents.sessions.turn_stream(
        session_id=session.id,
        messages=[{"role": "user", "content": task}],
    ) as stream:
        for event in stream:
            check = monitor.process_event(event)
 
            if check["warnings"]:
                for w in check["warnings"]:
                    print(f"⚠️ {w}")
                    # In production, send notifications to Slack/PagerDuty here
 
            if check["action"] == "stop":
                print("🛑 Force-stopping agent")
                client.agents.sessions.cancel(session_id=session.id)
                break
 
    return {
        "tool_calls": monitor.tool_call_count,
        "errors": monitor.error_count,
        "files_accessed": monitor.accessed_files,
    }

The key here is the process_event method, which runs three checks on every tool call: count limits, file path safety validation, and cumulative error tracking. The moment any threshold is crossed, the session is immediately cancelled.

In production, we layer on additional logic: monitoring the moving average of tool execution time to detect sudden latency spikes, and detecting repeated calls to the same tool (a sign of infinite loops).

Failsafe Design: When Agents Go Off the Rails

No matter how carefully you design your sandbox, unexpected behavior will happen. What matters is having mechanisms ready to minimize the blast radius.

import asyncio
from contextlib import asynccontextmanager
from dataclasses import dataclass, field
from datetime import datetime
 
@dataclass
class SessionGuard:
    """Guards managing session lifecycle"""
    session_id: str
    started_at: datetime = field(default_factory=datetime.now)
    max_duration_seconds: int = 600
    max_output_bytes: int = 10 * 1024 * 1024  # 10MB
    total_output_bytes: int = 0
 
    @property
    def elapsed_seconds(self) -> float:
        return (datetime.now() - self.started_at).total_seconds()
 
    @property
    def is_expired(self) -> bool:
        return self.elapsed_seconds > self.max_duration_seconds
 
    def track_output(self, data: str) -> bool:
        """Track output data, return False if limit exceeded"""
        self.total_output_bytes += len(data.encode("utf-8"))
        return self.total_output_bytes <= self.max_output_bytes
 
@asynccontextmanager
async def guarded_session(agent_id: str, **guard_kwargs):
    """Context manager for guarded agent sessions"""
    session = client.agents.sessions.create(agent_id=agent_id)
    guard = SessionGuard(session_id=session.id, **guard_kwargs)
 
    # Launch timeout watchdog as a concurrent task
    async def _watchdog():
        while not guard.is_expired:
            await asyncio.sleep(5)
        print(f"⏰ Session {session.id} timed out")
        try:
            client.agents.sessions.cancel(session_id=session.id)
        except Exception:
            pass  # Already terminated
 
    watchdog_task = asyncio.create_task(_watchdog())
 
    try:
        yield session, guard
    finally:
        watchdog_task.cancel()
        try:
            client.agents.sessions.delete(session_id=session.id)
        except Exception:
            pass

SessionGuard manages both time limits and output volume limits simultaneously. The output volume limit is easy to overlook, but it's essential for the scenario where an agent keeps generating massive files.

The _watchdog coroutine checks for timeout every 5 seconds and force-cancels the session the moment the limit is exceeded. This is separate from the Managed Agents API's own timeout_seconds—having an independent timeout on the application side acts as a safety net in case the API-side control fails for any reason.

Production Deployment Checklist

Based on the patterns above, here's what to verify when deploying Managed Agents to production.

Network controls: Ensure network_access is set to "none" or "restricted" based on the task. Keep allowed_domains to the absolute minimum. When internal API access is needed, route it through a dedicated API gateway for agents.

Resource limits: Set memory_mb and timeout_seconds based on actual workload measurements. Run load tests to measure peak memory usage, then add a 20% margin.

Monitoring and alerts: Log SSE events and detect anomaly patterns (tool call spikes, consecutive errors). Visualize average session duration and success rates on a dashboard.

Failsafes: Application-side timeouts must function independently from the API side. Agent output volume must be capped. Emergency shutdown procedures (bulk session cancellation) must be included in runbooks.

These principles aren't specific to Managed Agents—they're universal for operating any autonomous AI agent. Thinking about what an agent shouldn't do is just as important as thinking about what it can do when it comes to production reliability.


This article is published in full as a free sample of our premium content. Premium articles on Claude Lab feature practical implementation patterns and production know-how like this, with working code examples. To access all premium articles, consider joining our membership.

Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

API & SDK2026-04-10
Designing Production Architecture for Claude Managed Agents — Sandboxed Execution, Persistent Memory, Credential Management, and Cost Optimization Patterns
A practical guide to designing production-grade architectures with Claude Managed Agents. Covers sandboxed execution, persistent memory, credential management, multi-agent orchestration, and cost optimization.
API & SDK2026-06-27
When Claude API Streaming Stops Without an Error: Detecting Silent Stalls and Resuming Mid-Stream
How to catch the 'silent stall' where Claude API streaming stops with no exception at all, using a content-level watchdog that times the gap between tokens, plus a resume path that carries received text forward as an assistant prefill, and a four-layer timeout budget for long-running automation.
API & SDK2026-06-23
When Claude API Prompt Caching Quietly Stops Hitting in Production — Field Notes on TTL and Measured Savings
Prompt caching works beautifully the day you ship it, then quietly stops hitting in production. The five things that break the prefix, how to choose between 5-minute and 1-hour TTL, and how to measure real savings from usage instead of guessing.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →