Two SDKs, One Agent: Pairing OpenAI’s Reasoning with Cloudflare’s Runtime
The term “Agents SDK” covers a lot of ground these days, and the distinction matters. Many agent SDKs are really frameworks for orchestration: they define the reasoning loop, tool calling, and model interactions that form an agent’s "brain." But that brain needs a body — a place to run with persistent state, identity, and network access.
Cloudflare’s Agents SDK focuses on that execution layer, providing a persistent runtime built on Durable Objects and Workers. OpenAI’s Agents SDK handles the cognition side: planning, tool orchestration, and memory abstractions. The two are complementary, and combining them produces a clean separation of concerns: OpenAI owns how the agent thinks, while Cloudflare owns where the agent lives.
Why Split Cognition and Execution?
OpenAI’s Agents SDK gives you the agent logic but assumes you bring your own runtime and state. Cloudflare’s Agents SDK gives you the environment — a persistent object with a unique identity, built-in concurrency control, and storage — but doesn’t dictate agent behavior. Together, they remove the glue code:
- OpenAI: cognition, planning, tool orchestration
- Cloudflare: location, identity, memory, execution
The infrastructure side is not tied to OpenAI specifically. The execution layer is designed to accept any agent runtime that can run inside a Durable Object.
Architectural Patterns for Persistent Agents
Durable Objects let agents go beyond stateless functions. They can persist memory, coordinate across workflows, and respond in real time. Three patterns demonstrate how agents can be composed, guided, and connected:
- Multi-agent systems: divide responsibilities across specialized agents that collaborate.
- Human-in-the-loop: let agents plan independently but wait for human input at key decision points.
- Addressable agents: make agents reachable through real-world interfaces like phone calls or WebSockets.
Composing Specialized Agents
Multi-agent systems break a task into specialized agents, each handling distinct responsibilities with its own memory, logic, and instructions. Because agents live in Durable Objects, they persist across sessions and can coordinate responses. A triage agent, for instance, can route questions to a subject-matter expert based on the query, with each agent maintaining its own state and context.
export class MyAgent extends Agent {
async onRequest() {
const historyTutorAgent = new Agent({
instructions:
"You provide assistance with historical queries. Explain important events and context clearly.",
name: "History Tutor",
});
const mathTutorAgent = new Agent({
instructions:
"You provide help with math problems. Explain your reasoning at each step and include examples",
name: "Math Tutor",
});
const triageAgent = new Agent({
handoffs: [historyTutorAgent, mathTutorAgent],
instructions:
"You determine which agent to use based on the user's homework question",
name: "Triage Agent",
});
const result = await run(triageAgent, "What is the capital of France?");
return Response.json(result.finalOutput);
}
}
Inserting Human Approval into the Loop
A human-in-the-loop example implemented with both SDKs runs an OpenAI agent inside a Durable Object. The goal: let the agent plan multiple steps, then yield control after each one for a human to approve or intervene. State — including memory and intermediate steps — persists in this.state.
The flow works as follows:
- An OpenAI
Agentinstance runs inside a Durable Object. - The user submits a prompt, and the agent plans several steps.
- After each step, the agent pauses and waits for approval or modification.
- The client fetches the pending step via another route, reviews it, and sends approval or rejection.
- The agent resumes execution with the updated state.
export class MyAgent extends Agent {
// ...
async onStart() {
if (this.state.serialisedRunState) {
const runState = await RunState.fromString(
this.agent,
this.state.serialisedRunState
);
this.result = await run(this.agent, runState);
This design is only possible because the agent resides in a Durable Object — it has persistent memory and identity, enabling multi-turn interaction even across separate sessions.
Agents as Addressable Entities
Agents don’t have to be HTTP endpoints, even if they are served that way. Durable Objects assign each agent a global identity, and that address can be referenced through any network interface — phone, email, or pub/sub systems.
In one demo, Twilio routes a phone call to a WebSocket input connected to an agent. The call traverses Cloudflare’s network, keeping latency low and preserving identity. Real-time state updates are stored within the agent itself, so the same agent can serve a website displaying its own status. This makes an agent truly multimodal — accepting and outputting audio, video, text, or email — which suits use cases like customer service and education.
export class MyAgent extends Agent {
// receive phone calls via websocket
async onConnect(connection: Connection, ctx: ConnectionContext) {
if (ctx.request.url.includes("media-stream")) {
const agent = new RealtimeAgent({
instructions:
"You are a helpful assistant that starts every conversation with a creative greeting.",
name: "Triage Agent",
});
connection.send(`Welcome! You are connected with ID: ${connection.id}`);
const twilioTransportLayer = new TwilioRealtimeTransportLayer({
twilioWebSocket: connection,
});
const session = new RealtimeSession(agent, {
transport: twilioTransportLayer,
});
await session.connect({
apiKey: process.env.OPENAI_API_KEY as string,
});
session.on("history_updated", (history) => {
this.setState({ history });
});
}
}
}
What the Integration Taught Us
State Ownership Is Yours to Define
OpenAI’s SDK is stateless by default. You can attach memory abstractions, but the SDK doesn't specify where or how to persist them. Durable Objects are persistent by design: each instance has a unique identity and a storage API (this.ctx.storage). That enables a straightforward pattern: hydrate the agent’s memory before run(), then save any updates after run() completes.
Routing Is Your Agent Factory
routeAgentRequest may look like a simple dispatcher — mapping a request to a Durable Object based on a URL — but it actually defines the identity boundary for your agents. Durable Object identity is tied to an ID, and the routing decision determines how long an agent lives and what it remembers.
Calling idFromName() with the same name always returns the same agent instance, along with its memory and state. Calling newUniqueId() creates a new, isolated object each time. That’s a common early bug: if you skip idFromName() and just use newUniqueId(), you get a fresh agent on every request and your memory silently never persists.
Proper routing allows multiple agents per user (one per session or task), keeps memory and logic co-located, and prevents unintended state sharing between conversations.
Agents Compose Like Microservices
Agents can invoke each other using Durable Object routing, forming workflows where each agent owns its own memory and logic. The result is an architecture that feels like microservices — stateful, distributed, and built from specialized, cooperative parts.
Why the Combination Worked
Marrying OpenAI’s cognition with Cloudflare’s execution layer produced clear wins:
- Full planning and memory without building orchestration from scratch
- Ability to pause and resume agents asynchronously
- Composition of multiple agents into larger systems
The hardest parts were scoping the agent architecture correctly, ensuring only valid state gets persisted, and debugging with sufficient observability. But the underlying pattern — a reasoning engine housed in a persistent, addressable runtime — is one that generalizes well beyond any single SDK pairing.



