Native Sandboxes and the Orchestration Philosophy Behind OpenAI's Agents SDK Update
Source: openai
The OpenAI Agents SDK has been an interesting framework to watch since it launched in early 2025 as a more opinionated successor to the experimental Swarm project. Where Swarm was deliberately minimal and educational, the Agents SDK made real architectural commitments: an explicit agent loop, structured handoffs between agents, typed tool definitions, and a tracing system baked in from the start. The latest update pushes further with native sandbox execution and what OpenAI calls a model-native harness, both of which deserve more examination than a feature announcement usually gets.
The Orchestration Problem
Most agent frameworks share a common pattern: the framework holds the loop, parses model outputs, routes decisions, and calls tools. The model is a component inside the framework’s orchestration logic. This is sensible when model outputs are unpredictable or when you need deterministic routing between steps. The tradeoff is that the framework ends up encoding assumptions about how the model behaves, and those assumptions drift as models improve.
A model-native harness inverts some of this. Rather than the framework trying to interpret what the model wants to do, it hands the model a structured environment and lets the model’s native tool-calling machinery drive the execution. The model is not parsing instructions about how to use tools; it is calling typed functions with parameters the SDK validates. The Agents SDK’s harness exposes the model’s actual control flow as first-class API surface rather than bolting orchestration on top of completion outputs.
In practice, an agent definition in the current SDK looks like this:
from agents import Agent, Runner, function_tool
@function_tool
def search_codebase(query: str) -> str:
"""Search the codebase for relevant files and symbols."""
...
@function_tool
def write_file(path: str, content: str) -> str:
"""Write content to a file at the given path."""
...
reviewer = Agent(
name="code_reviewer",
instructions="Review code changes and suggest improvements.",
tools=[search_codebase, write_file],
)
result = await Runner.run(reviewer, "Review the changes in src/auth.py")
The @function_tool decorator generates a JSON schema from the function’s type annotations and docstring, which gets passed directly to the model as a tool definition. The model calls the function; the harness validates inputs against the schema and returns structured output. There is no prompt engineering in the middle trying to coerce the model into formatting its tool calls correctly, because the tool calling protocol handles that at the API level.
This is where the “model-native” framing makes sense. Frameworks like LangChain historically wrapped completion endpoints and tried to parse structured information out of raw text. The Agents SDK builds on the Responses API, which treats tool calls as a first-class primitive rather than a parsing problem.
Native Sandbox Execution
The sandbox piece is where things get practically significant for production use. Before native sandbox support, running a code-executing agent meant wiring up your own isolated environment: E2B, Modal, a Docker container with restricted capabilities, or a custom subprocess runner with filesystem and network constraints. Each option required separate configuration, separate error handling, and a security model you had to maintain and audit yourself.
Native sandbox execution means the SDK provides the isolation layer directly. For agents that write and run code, analyze data files, or manipulate the local filesystem as part of their task loop, this changes the threat model considerably. The sandbox constrains what the agent can reach: which system calls are available, whether it can make outbound network requests, what paths it can read and write. These constraints are enforced by the runtime, not by the agent’s instructions.
This distinction matters because instructions are not a security boundary. An agent instructed to “only read files in /tmp” can still be prompted into reading arbitrary paths if the underlying execution is unrestricted. Prompt injection, malicious content in files the agent reads, and ambiguous instruction interpretation can all lead the model somewhere its instructions nominally forbade. A native sandbox enforces the constraint at the system level. The capability simply does not exist, regardless of what the model decides to do.
For long-running agents in particular, this shifts the operational model. An agent running for minutes or hours, making filesystem changes and running subprocesses across dozens of tool calls, needs a durable security perimeter, not just a well-crafted system prompt.
Long-Running Agents and State
Long-running agents expose problems that short-lived request/response agents never encounter. Context windows fill up. The model needs access to work it did twenty tool calls ago. A network interruption or API timeout can kill a multi-hour run and lose all intermediate state. These are individually solvable problems, but when the SDK addresses them natively, developers are not rebuilding the same bespoke state management for every agent project.
The Responses API already provided conversation state persistence through previous_response_id, letting you resume a conversation without resending the full history on each turn. Extending this for agent loops is more complex than resuming a conversation, because the challenge is not just replaying message history but resuming a stateful execution that has created files, run code, and accumulated intermediate results across many steps.
Checkpointing an agent mid-task requires tracking more than messages: which tools have been called, what their outputs were, what filesystem changes are in progress, and what the agent was attempting when it was interrupted. A model-native harness has structural visibility into this state because it owns the execution loop. A framework that sits outside the loop and orchestrates through text has to reconstruct that state from outputs, which is both fragile and lossy.
How This Compares to LangGraph
LangGraph takes a different architectural bet. It models agent logic as an explicit graph: nodes are steps, edges are transitions, and you define the control flow in code before any model interaction happens. The model is one node among many. This gives deterministic routing at the cost of upfront design work and a graph definition that needs to be kept in sync with what the model is actually capable of.
For workflows where the steps are known in advance and the model’s job is to fill in parameters at each step, LangGraph’s model fits well. You can see the entire control flow in the graph definition, reason about it statically, and test individual nodes in isolation.
The Agents SDK’s loop-driven approach is better suited to tasks where the control flow itself is uncertain, where you want the model to decide what to do next rather than execute a predefined graph. The tradeoff is less deterministic routing and more surface area for the model to make unexpected decisions. Native sandbox execution contains the blast radius of those decisions.
From experience building Discord bots where tasks are usually short-lived and well-scoped, LangGraph’s explicit graph model often fits cleanly: the bot receives a command, executes a defined sequence of steps, responds. For longer, open-ended tasks like “investigate this failing test, find the root cause, and open a PR with a fix,” the Agents SDK’s approach handles the uncertainty better because the right sequence of steps is not knowable in advance.
What the Update Signals
The original Agents SDK release was explicit about being lightweight orchestration, deliberately avoiding the accumulation problem that made LangChain difficult to reason about at scale. The native sandbox and model-native harness additions move the SDK toward being a more complete runtime for agents, not just a thin coordination layer.
This is a reasonable direction given where real-world agent use cases are landing. Agents that execute code, manipulate files, and run for extended periods need runtime support that a lightweight library does not provide. The question is whether the added surface area stays coherent as the SDK grows, or whether the complexity begins to look like what it originally positioned itself against.
For now, native sandbox execution is a concrete improvement for anyone building agents that run code or handle sensitive files. When isolation is enforced by the runtime rather than negotiated through instructions, the security story is meaningfully stronger. That is a real change, not just a packaging shift, and it addresses one of the more persistent problems in deploying agents outside of controlled demos.