Skip to content

Cloudflare Runtime

Deploy Helix Agents on Cloudflare's edge network with two runtime options optimized for different use cases.

Two Approaches

The @helix-agents/runtime-cloudflare package provides two distinct runtime approaches:

ApproachBest ForStreaming CostState Storage
WorkflowsStep-level durability, D1 integration, automatic retriesCounts against 1000 subrequest limitD1 Database
Durable ObjectsHeavy streaming, real-time WebSocket/SSE, simpler architectureFREE (direct to connections)DO built-in SQLite

Quick Comparison

FeatureWorkflowsDurable Objects
Execution ModelCloudflare WorkflowsPartyServer-based DO
StreamingVia separate DO (subrequest per chunk)Direct WebSocket/SSE (FREE)
Subrequest Limit1000 limit appliesOnly LLM calls count
State StorageD1 DatabaseDO built-in SQLite
HibernationNot supportedSupported (cost efficient)
Step DurabilityAutomatic retry per stepManual checkpointing
ArchitectureWorker → Workflow → D1/DOWorker → AgentServer DO
Real-timePolling or separate WebSocketNative WebSocket/SSE
ComplexityMedium (multiple services)Lower (single DO)

Choosing an Approach

Use Workflows When:

  • You need step-level durability - Each workflow step is automatically retried on failure
  • You want to share D1 state - Other services can query the same D1 database
  • You're already using Cloudflare Workflows - Integrate with existing workflow infrastructure
  • Your agents don't require heavy streaming - Less than ~100 chunks per execution is safe
typescript
import { CloudflareAgentExecutor, AgentSteps, runAgentWorkflow } from '@helix-agents/runtime-cloudflare';
import { D1StateStore } from '@helix-agents/store-cloudflare';

// Uses Cloudflare Workflows + D1
const executor = new CloudflareAgentExecutor({
  workflowBinding: env.AGENT_WORKFLOW,
  stateStore: new D1StateStore(env.DB),
  streamManager: doStreamManager,
});

Use Durable Objects When:

  • You need unlimited streaming - Bypasses the 1000 subrequest limit entirely
  • You want real-time WebSocket/SSE - Native bidirectional communication
  • You prefer simpler architecture - One DO per agent run, no D1 needed
  • You want hibernation support - Cost-efficient sleep/wake with state preservation
  • Streaming is performance-critical - Direct writes are faster than subrequests
typescript
import { AgentServer, DOAgentExecutor } from '@helix-agents/runtime-cloudflare';

// Export DO class
export { AgentServer };

// Uses Durable Objects directly
const executor = new DOAgentExecutor({
  agentNamespace: env.AGENTS,
  createLLMAdapter: () => new VercelAIAdapter({ model: openai('gpt-4o') }),
});

The Subrequest Limit Problem

Cloudflare Workers have a 1000 subrequest limit per invocation. In the Workflows approach, each stream chunk write to the streaming Durable Object counts as a subrequest:

Worker → Workflow → Stream DO (subrequest!)
                 → Stream DO (subrequest!)
                 → Stream DO (subrequest!)
                 → ... (1000 limit reached!)

For streaming-heavy agents (chat responses, tool results, thinking tokens), this limit is easily reached. The DO runtime solves this by hosting execution inside the DO:

Worker → AgentServer DO
         ├── Stream to WebSocket (FREE!)
         ├── Stream to WebSocket (FREE!)
         └── Stream to SSE (FREE!)

Architecture Diagrams

Workflows Runtime

mermaid
graph TB
    subgraph Worker ["Worker"]
        Exec["CloudflareAgentExecutor"]
        subgraph WF ["Workflow (AGENT_WORKFLOW)"]
            Steps["AgentSteps"]
            D1["D1 Database (state)"]
            StreamDO["Stream DO (streaming)<br/><i>← subrequest limit</i>"]
        end
        Exec --> WF
    end

Durable Objects Runtime

mermaid
graph TB
    subgraph Worker ["Worker"]
        subgraph DO ["AgentServer (Durable Object)"]
            JSExec["JSAgentExecutor<br/>(in-DO execution)"]
            DOState["DOStateStore<br/>(SQLite, in-DO)"]
            DOStream["DOStreamManager<br/>→ WebSocket/SSE <b>(FREE!)</b>"]
        end
    end

Installation

Both approaches use the same package:

bash
npm install @helix-agents/runtime-cloudflare @helix-agents/core

For Workflows, you also need:

bash
npm install @helix-agents/store-cloudflare

Prerequisites

Both Approaches

  • Cloudflare account with Workers Paid plan
  • Wrangler CLI: npm install -g wrangler

Workflows Runtime

  • D1 database (for state storage)
  • Workflow binding configured
  • Durable Objects (for streaming)

Durable Objects Runtime

  • Durable Objects with SQLite enabled
  • No D1 database required

Configuration Examples

Workflows (wrangler.toml)

toml
[[d1_databases]]
binding = "DB"
database_name = "agent-state"
database_id = "your-database-id"

[[durable_objects.bindings]]
name = "STREAM_DO"
class_name = "StreamServer"

[[workflows]]
name = "agent_workflow"
binding = "AGENT_WORKFLOW"
class_name = "AgentWorkflow"

[[migrations]]
tag = "v1"
new_classes = ["StreamServer", "AgentWorkflow"]

Durable Objects (wrangler.toml)

toml
[[durable_objects.bindings]]
name = "AGENTS"
class_name = "AgentServer"

[[migrations]]
tag = "v1"
new_sqlite_classes = ["AgentServer"]  # Note: SQLite classes!

Feature Availability

FeatureWorkflowsDurable Objects
Agent Execution
Tool Execution
Sub-Agents
Streaming✅ (limited)✅ (unlimited)
WebSocketVia separate DO✅ Native
SSE
Multi-turn Conversations
Interrupt/Resume
Checkpoints
Hibernation
Step Retries✅ Automatic❌ Manual
D1 Integration✅ Native❌ Separate

Concurrency Handling

ApproachMechanism
WorkflowsWorkflow instance uniqueness
Durable ObjectsSingle-threaded DO execution

retry() Support

Both approaches support retry() for recovering from failures:

typescript
const result = await handle.result();
if (result.status === 'failed') {
  const retryHandle = await handle.retry();
}

Method Availability

MethodWorkflowsDurable Objects
execute()
resume()
retry()
getHandle()

Migration Between Approaches

Both approaches use compatible agent definitions. Migrating typically involves:

  1. State migration - Export from D1, import to DO SQLite (or vice versa)
  2. Configuration changes - Update wrangler.toml bindings
  3. Code changes - Switch executor class and configuration

Agent definitions (defineAgent, defineTool) work identically in both approaches.

Common Patterns

Hybrid Approach

Use Workflows for orchestration and DOs for streaming:

typescript
// Workflow for durable orchestration
const workflow = new AgentWorkflow();

// Dedicated DO for unlimited streaming
const streamDO = env.STREAM_DO.get(streamId);

External State Sharing

If you need to share state with other services while using DO runtime:

typescript
// DO runtime for execution
const executor = new DOAgentExecutor({ ... });

// Sync to D1 for external access
await syncToD1(result, env.DB);

Performance Considerations

AspectWorkflowsDurable Objects
Cold StartWorker + Workflow initWorker + DO init
Streaming LatencyHigher (subrequest)Lower (direct)
State AccessNetwork (D1)Local (SQLite)
MemoryWorkflow limitsDO limits
Concurrent ConnectionsVia pollingNative WebSocket

Cost Considerations

Cost FactorWorkflowsDurable Objects
SubrequestsHigh (streaming)Low (LLM only)
D1 Reads/WritesYesNo
DO DurationStreaming DOExecution DO
HibernationN/AReduces cost

Next Steps

Released under the MIT License.