Runtime Overview
A runtime is what actually executes your agents. Helix Agents provides multiple runtimes, each suited to different deployment scenarios. Your agent definitions work identically across all runtimes - only the execution environment changes.
What Is a Runtime?
The runtime orchestrates the agent execution loop:
- Initialize agent state
- Call the LLM with messages and tools
- Execute any tool calls
- Handle sub-agents
- Persist state
- Stream events
- Repeat until completion
Different runtimes handle these steps differently based on their execution model.
Available Runtimes
| Runtime | Package | Best For |
|---|---|---|
| JavaScript | @helix-agents/runtime-js | Development, testing, simple deployments |
| Temporal | @helix-agents/runtime-temporal | Production, long-running agents, durability |
| Cloudflare | @helix-agents/runtime-cloudflare | Edge deployment, serverless, global scale |
| DBOS | @helix-agents/runtime-dbos | Postgres-backed durable execution |
v7 Feature Parity Matrix
All major v7 capabilities are supported across runtimes:
| Feature | JS | Temporal | CFW Workflows | CF DO | DBOS |
|---|---|---|---|---|---|
| HITL — client-executed tools | ✅ | ✅ | ✅ | ✅ | ✅ |
Approval gates (requireApproval) | ✅ | ✅ | ✅ | ✅ | ✅ |
| Persistent sub-agents | ✅ | ✅ | ✅ | ✅ | ✅ |
| Persistent-companion continuation² | ✅ | ✅ | ✅ | ✅ | ✅ |
| Stateless suspension model | ✅ | ✅ | ✅ | ✅ | ✅ |
Multi-turn continuation via execute(sessionId)¹ | ✅ | ✅ | ❌ | ✅ | ✅ |
| Workspaces | ✅ | ✅ | ❌ | ✅ | ❌ |
| Cross-process / cross-replica interrupt | ✅ | ✅ | ✅ | ✅ | ✅ |
Prompt caching (LLMConfig.cache) | ✅ | ✅ | ✅ | ✅ | ❌ |
| Memory auto-injection / extraction | ✅ | ❌ | ✅ | ✅ | ❌ |
¹ A SECOND execute(agent, msg, { sessionId }) against an already-completed session continues the conversation into a new turn. On CFW Workflows this is blocked by the write-once workflow-instance-id model (agent__<name>__<sessionId> cannot be recreated after the run closes) — tracked in GitLab #109. On all other runtimes, see Concepts → Sessions. The HTTP path (POST /chat → ai-sdk handle-chat-stream "continuing session" branch → execute(sessionId)) inherits this support; agent-server's /start (startAgent) is fresh-start-only by design (remote-sub-agent protocol).
² Persistent-companion continuation — re-consulting an already-completed persistent child (via companion__spawnAgent / companion__sendMessage) continues on its preserved session (memory retained, fresh typed output) instead of throwing/deleting. This is the maker→critic loop. It is a different mechanism from root multi-turn continuation (row above): it works on all five runtimes including CFW Workflows, because each round runs as a fresh child workflow/instance with a per-continuation suffix id (…__continue__<stepCount>__<toolCallId>) — the write-once base instance id that blocks root execute(sessionId) on CFW Workflows is sidestepped. See Sub-Agents → Re-consulting a persistent companion and the v0.40 migration guide.
Cloudflare Has Two Approaches
The Cloudflare runtime offers two execution models: Workflows (step-level durability with D1) and Durable Objects (unlimited streaming with built-in SQLite). See Cloudflare Runtime for comparison.
Comparison
Execution Model
| Feature | JS Runtime | Temporal Runtime | Cloudflare Workflows | Cloudflare DO | DBOS |
|---|---|---|---|---|---|
| Durability | None (in-memory) | Full (workflow persistence) | Step-level | Checkpoint-based | Full (Postgres-backed) |
| Crash Recovery | No | Yes | Yes (step-level) | Yes (checkpoint) | Yes (durable workflow) |
| Timeout Handling | Manual | Per-activity timeouts | Per-step timeouts | Manual | Per-step timeouts |
| Retry Logic | Manual | Automatic with config | Automatic per step | Manual | Automatic per step |
| Multi-Process | No | Yes (workers) | Yes (edge nodes) | Yes (edge nodes) | Yes (Postgres coordinated) |
| Concurrency | Status check + OCC | Workflow ID uniqueness | Workflow instance | DO isolation | Workflow ID + Postgres |
State & Streaming
| Feature | JS Runtime | Temporal Runtime | Cloudflare Workflows | Cloudflare DO | DBOS |
|---|---|---|---|---|---|
| State Store | Memory/Redis | Memory/Redis/PG | D1 Database | DO SQLite | Postgres |
| Stream Manager | Memory/Redis | Memory/Redis | Durable Objects | Direct WebSocket/SSE | Memory/Redis |
| Streaming Limit | None | None | 1000 subrequests | Unlimited (FREE) | None |
| Stream Resumption | handleChatStream | handleChatStream | handleChatStream | handleChatStream | handleChatStream |
| Content Replay | Built-in | Built-in | Built-in | Built-in | Built-in |
| Sub-Agent Execution | In-process | Child workflows | Nested workflow calls | Sibling DOs (via subAgentNamespace) | Child workflows |
Operational
| Feature | JS Runtime | Temporal Runtime | Cloudflare Workflows | Cloudflare DO | DBOS |
|---|---|---|---|---|---|
| Infrastructure | None | Temporal Server | D1 + Workflow + DO | DO only | Postgres |
| Complexity | Low | Medium-High | Medium | Low-Medium | Medium |
| Cost | Hosting only | Temporal Cloud or self-host | D1 + subrequests | DO duration only | Postgres + hosting |
| Scaling | Vertical | Horizontal (workers) | Automatic (edge) | Automatic (edge) | Horizontal (workers) |
| Hibernation | N/A | N/A | Yes (v7 stateless) | Yes (cost savings) | N/A |
Method Availability
All runtimes support the same executor methods:
| Method | JS | Temporal | Cloudflare | DBOS |
|---|---|---|---|---|
execute() | ✅ | ✅ | ✅ | ✅ |
resume() | ✅ | ✅ | ✅ | ✅ |
retry() | ✅ | ✅ | ✅ | ✅ |
getHandle() | ✅ | ✅ | ✅ | ✅ |
submitToolResult() | ✅ | ✅ | ✅ | ✅ |
interrupt() | ✅ | ✅ | ✅ | ✅ |
abort() | ✅ | ✅ | ✅ | ✅ |
submitToolResult, interrupt, and abort are also available as durable HTTP routes when running through @helix-agents/agent-server (see POST /chat/{id}/submit-tool-result, POST /chat/{id}/interrupt, POST /chat/{id}/abort).
Choosing a Runtime
Use JavaScript Runtime When:
- Development and testing - Fast iteration without infrastructure
- Simple deployments - Single-process, short-lived agents
- Prototyping - Explore ideas before production setup
- Serverless functions - Lambda/Cloud Functions where execution is short
import { JSAgentExecutor, InMemoryStateStore, InMemoryStreamManager } from '@helix-agents/sdk';
const executor = new JSAgentExecutor(
new InMemoryStateStore(),
new InMemoryStreamManager(),
llmAdapter
);Use Temporal Runtime When:
- Production workloads - Need reliability and observability
- Long-running agents - Execution may take hours or days
- Crash recovery - Agent must survive process restarts
- Complex orchestration - Multi-agent systems with dependencies
import { TemporalAgentExecutor } from '@helix-agents/runtime-temporal';
const executor = new TemporalAgentExecutor({
client: temporalClient,
stateStore: redisStateStore,
streamManager: redisStreamManager,
workflowName: 'agent_workflow',
taskQueue: 'agents',
});Use Cloudflare Runtime When:
- Global edge deployment - Low latency worldwide
- Serverless architecture - No servers to manage
- Cloudflare ecosystem - Already using Workers, D1, DO
- Cost optimization - Pay per request, automatic scaling
Workflows approach - Step-level durability, D1 integration:
import { CloudflareAgentExecutor } from '@helix-agents/runtime-cloudflare';
const executor = new CloudflareAgentExecutor({
workflowBinding: env.AGENT_WORKFLOW,
stateStore: d1StateStore,
streamManager: doStreamManager,
});Durable Objects approach - Unlimited streaming, simpler architecture:
import { createAgentServer, DOWorkflowExecutor } from '@helix-agents/runtime-cloudflare';
export const AgentServer = createAgentServer<Env>({
llmAdapter: (env) => new VercelAIAdapter({ model: openai('gpt-4o') }),
agents: registry,
});
const executor = new DOWorkflowExecutor({
agentNamespace: env.AGENTS,
});See Cloudflare Runtime for detailed comparison of both approaches.
Use DBOS Runtime When:
- Postgres-backed durability - You already run Postgres and don't want Temporal infrastructure
- Durable execution - Long-running agents that must survive restarts
- Postgres ecosystem - Leverage existing PG tooling for observability and querying
import { DBOSAgentExecutor } from '@helix-agents/runtime-dbos';
const executor = new DBOSAgentExecutor({ stateStore, streamManager, llmAdapter });DBOS parallel-tool state visibility
When using the DBOS runtime, each tool call runs as an independent durable step that loads state.customState fresh from Postgres at the top of its execution. This means a parallel sibling tool that calls ctx.getState() to read a key written by another sibling within the same LLM step will see the pre-sibling value, not the cumulative state written by the other sibling. This is by design — DBOS steps must be idempotent for workflow replay determinism.
Tools that need to read sibling writes within a single step should be:
- Sequenced via a single parent tool that orchestrates the work, or
- Run on the JS or Cloudflare DO runtimes, which apply in-memory state merging between parallel tool calls before persisting.
Common API
All runtimes implement the AgentExecutor interface:
interface AgentExecutor {
// Execute an agent with input
execute<TState, TOutput>(
agent: AgentConfig<...>,
input: string | { message: string; state?: Partial<TState> },
options?: ExecuteOptions
): Promise<AgentExecutionHandle<TOutput>>;
// Reconnect to existing session
getHandle<TState, TOutput>(
agent: AgentConfig<...>,
sessionId: string
): Promise<AgentExecutionHandle<TOutput> | null>;
}The returned handle provides:
interface AgentExecutionHandle<TOutput> {
runId: string;
// Stream real-time events
stream(): Promise<AsyncIterable<StreamChunk> | null>;
// Wait for final result
result(): Promise<AgentResult<TOutput>>;
// Cancel execution
abort(reason?: string): Promise<void>;
// Get current state
getState(): Promise<AgentState<unknown, TOutput>>;
// Check if resumable
canResume(): Promise<CanResumeResult>;
// Resume execution
resume(options?: ResumeOptions): Promise<AgentExecutionHandle<TOutput>>;
}Swapping Runtimes
Because all runtimes share the same interface, swapping is straightforward:
// Development
const executor =
process.env.NODE_ENV === 'production'
? new TemporalAgentExecutor({
/* production config */
})
: new JSAgentExecutor(memoryStore, memoryStream, llmAdapter);
// Same usage regardless of runtime
const handle = await executor.execute(myAgent, 'Research AI agents');
const result = await handle.result();This is the core philosophy of Helix Agents: define once, execute anywhere.
Next Steps
- JavaScript Runtime - In-process execution details
- Temporal Runtime - Durable workflow execution
- DBOS Runtime - Postgres-backed durable execution
- Cloudflare Runtime - Overview and comparison
- Workflows Runtime - D1 + Workflows approach
- Durable Objects Runtime - Direct DO approach
- Storage Overview - State stores and stream managers