· 6 min read ·

The Security Layer Agent Frameworks Have Been Offloading to Developers

Source: openai

The engineering problem that sits beneath most production agent failures is not prompt design or model selection. It’s isolation. When you hand an AI agent access to a filesystem, a Python interpreter, and a set of tools, you need hard guarantees about what it can and cannot touch. OpenAI’s latest update to the Agents SDK addresses this directly with native sandbox execution and a model-native test harness, moving the SDK from a useful abstraction layer into something that can anchor production deployments.

The gap this fills is not subtle. Since the framework’s origins as Swarm, an experimental multi-agent library released in late 2024, through its formalization into the openai-agents Python package in early 2025, the SDK has deliberately deferred execution security to the developer. The abstractions for agent behavior, the Agent class, handoffs, guardrails, tracing, were well-designed from the start. The question of where the agent’s tools actually ran, and with what permissions, was left open.

Why Frameworks Leave Sandboxing Alone

This deference is not carelessness. Sandboxing involves deep platform tradeoffs, and a general-purpose SDK that bakes in a specific isolation primitive makes a choice that may not fit every deployment context. Docker containers, Linux namespaces, WebAssembly runtimes, and cloud-based execution environments like E2B each offer different latency profiles, security boundaries, and operational requirements. A framework that commits to one approach inherits its limitations.

The practical consequence for developers building serious agent workflows has been a recurring detour: before writing any agent logic, set up a sandboxed execution backend. Teams running code-generation agents typically reach for one of a few approaches. Docker containers with restricted network access and read-only filesystem mounts are the most common. E2B, which provides a managed sandboxed cloud interpreter, became popular because it handles the container lifecycle and gives you a clean SDK for driving code execution from the agent loop. Some teams go lower-level with nsjail or seccomp profiles for tighter control.

Each approach works, but each requires ongoing maintenance that has nothing to do with the agent’s actual task. Container images need updating, network egress rules need auditing, and cost models for managed sandbox services need to be tracked separately from the model API costs.

# Pre-update: a typical developer-managed sandbox setup
from e2b_code_interpreter import Sandbox
from agents import Agent, function_tool

@function_tool
def execute_python(code: str) -> str:
    with Sandbox() as sbx:
        execution = sbx.run_code(code)
        return execution.text

agent = Agent(
    name="data_analyst",
    instructions="Analyze data by writing and running Python code.",
    tools=[execute_python]
)

The sandbox setup above is not difficult, but it sits outside the framework. Developers maintain it, pay for it, debug it, and audit it separately from everything else the agent does.

Native Sandbox Execution: What Changes

By integrating sandbox execution at the SDK level, OpenAI is making a structural claim: execution security belongs inside the framework boundary, not outside it. The practical shift is that code execution initiated by the agent runs in an isolated environment by default, without the developer provisioning or maintaining that environment.

This matters most for the class of agents built around a REPL-style feedback loop: write code, execute it, observe the output, refine the approach, repeat. That loop is the backbone of data analysis agents, code generation assistants, and automated testing workflows. Each iteration is a potential security event, because the agent is running arbitrary code derived from model output, and without sandbox isolation, every iteration carries the risk of affecting the main process.

The long-running emphasis in the announcement connects directly to this. A short-lived agent, one that completes in a single exchange, accumulates limited surface area. An agent working through a multi-step task over minutes or hours, reading files, writing and executing scripts, calling external APIs, reaching back to a model repeatedly, has a much larger blast radius if something goes wrong at step 12 of 20. Native sandboxing is what makes that long-running pattern viable outside of environments where the developer has already invested in a hardened execution backend.

The Model-Native Harness

The test harness addition deserves as much attention as the sandboxing, and it will likely get less in most coverage.

Testing agent behavior has been awkward since the beginning. The options available before this update break into three approaches, each with significant drawbacks. Running tests against the live model gives you realistic behavior but at API cost, with non-determinism that makes assertions fragile and CI pipelines expensive. Mocking the model with canned responses gives you fast, deterministic tests, but the mocks diverge from real model behavior in exactly the edge cases that matter. Using a smaller or cheaper model as a proxy gives you something in between, but the behavioral gap between models means test results do not reliably predict production behavior.

A model-native harness understands the structure of model outputs: tool calls, structured responses, handoffs between agents. It can simulate that structure deterministically without requiring a live API call. You define the sequence of responses the mock model should produce, including tool invocations, and assert against the resulting agent behavior in a reproducible test suite.

# Conceptual model-native harness (structure based on SDK design patterns)
from agents.testing import AgentHarness, MockModel, ToolCallOutput, TextOutput

harness = AgentHarness(
    agent=analysis_agent,
    model=MockModel(responses=[
        ToolCallOutput(tool="read_file", args={"path": "data.csv"}),
        ToolCallOutput(tool="execute_python", args={"code": "import pandas as pd..."}),
        TextOutput("The dataset contains 1,200 rows with three outlier clusters.")
    ])
)

result = harness.run("Analyze the data in data.csv")
assert result.tool_calls[0].tool == "read_file"
assert result.tool_calls[1].tool == "execute_python"
assert "outlier" in result.final_output

This makes proper CI feasible for agent workflows. Regression tests for routing logic, guardrail behavior, and tool invocation sequences run in milliseconds rather than seconds, at near-zero cost, with deterministic assertions. For teams shipping agents as part of a product, that is a material change in how safely they can iterate.

Comparison with Other Frameworks

LangGraph, AutoGen, and Anthropic’s agent tooling each position differently on both dimensions.

LangGraph’s strength is in its explicit state machine representation. By modeling the agent as a directed graph of nodes and edges, it makes individual transition logic testable in isolation, which partially mitigates the harness problem. Sandbox execution remains the developer’s concern.

AutoGen’s newer agent runtime uses a distributed actor model, where agents run in separate processes and communicate via message passing. The process-level isolation provides natural sandboxing without a dedicated execution primitive, but the operational overhead of standing up an actor runtime is significant for teams that do not need distributed scale.

Anthropic’s SDK leans heavily on the tool schema system, with strongly typed tool definitions that constrain what the model can invoke. The execution environment is left to the caller, similar to OpenAI’s prior approach.

OpenAI’s position with this update is a pragmatic middle path: native sandboxing without requiring a distributed runtime, and native test harness without requiring a full mock framework. The tradeoff is that the framework makes more decisions for you, which reduces flexibility but raises the floor for what a typical implementation gets right by default.

What the Implementation Details Will Determine

A few open questions will shape how useful these additions are in practice. The sandboxing approach matters more than the feature’s existence. A container-per-run approach provides strong isolation but adds latency that may be unacceptable for interactive workflows. A lighter primitive, WebAssembly sandboxing or a persistent warm container pool, would have a different latency and cost profile. The security boundary also varies depending on the isolation mechanism; a network-restricted container and a WASM sandbox provide meaningfully different guarantees.

The tracing integration across multi-tool long-running runs is worth watching closely from a debugging standpoint. Building Discord bots that chain file reading, code execution, and API calls has consistently surfaced the same problem: when something fails mid-chain, reconstructing what happened requires trace data that frameworks usually do not capture at the right granularity. If the harness and sandbox are instrumented well, the trace output from a long-running agent should give you enough to diagnose failures without re-running the preceding steps.

Cross-agent state in long-running workflows remains a hard problem regardless of what the framework provides. Handoffs between agents work cleanly when the task is stateless, but agents that accumulate and pass mutable state across a session still require careful design at the application level. No SDK abstraction fully resolves that.

The progression from Swarm’s deliberately minimal API surface to a framework that ships with security and testing primitives reflects how quickly the production requirements for agents have clarified. Building toy agents exposed the model capability; building production agents exposed the execution infrastructure. This update addresses the infrastructure layer, which is the right direction regardless of where the implementation details land.

Was this interesting?