Skip to content

How Ironflow Fits with Agent Frameworks

Ironflow is a durable runtime for AI agents and event-driven systems. It does not choose prompts, models, planning strategies, or graph edges for you.

Use Ironflow to protect the work that must survive: model calls, tool calls, external waits, events, and derived state. Keep the reasoning layer you already use, or build a straightforward agent directly with Ironflow’s native API.

LayerOwns
Reasoning framework or provider SDKPrompts, model access, planning, routing, graph structure, and model-specific tool formats
Ironflow runtimeDurable steps, crash-resume, external waits, entity streams, projections, and optional recorded execution
Your applicationDomain rules, tool implementations, permissions, and the boundary between model output and side effects

This boundary matters. A framework can decide what an agent should do next. Ironflow makes the protected work resumable and connects it to the same runtime as the rest of your event-driven application.

The Node package exports agent(), defineTool(), and exposeMcp() from @ironflow/node/agent. The agent context provides durable wrappers for tools, model calls, approvals, memory operations, and child agents.

Go exposes the same agent-shaped concepts through its agent package.

@ironflow/browser can invoke agents, subscribe to progress, and read agent memory from a browser application while the worker and provider credentials remain server-side.

invoke() is keyed by a function ID. It blocks until the run finishes and returns that one run’s result. Abort the call and the server cancels the run. Because the call returns the runId only when it settles, the sync path cannot drive a live progress UI: to watch a run as it executes, use emit() and subscribe to the run. Read ADR 0067 for the full decision, including the emitSync() fan-out contract and the deprecation of the onRunStarted hook.

@ironflow/langgraph ships an IronflowSaver implementation of LangGraph’s checkpoint saver interface. It stores checkpoints and pending writes in an Ironflow entity stream for each LangGraph thread.

Claude SDK and CrewAI adapters are planned. Until those adapters ship, use their SDKs inside your application and put Ironflow durable boundaries around the calls and side effects you need to protect. Do not assume automatic instrumentation.

Use the native API when your loop is straightforward and you want agent-shaped durable primitives without adopting a graph framework.

import { agent, defineTool } from "@ironflow/node/agent";
import { z } from "zod";
const fetchDiff = defineTool({
name: "fetch-diff",
input: z.object({ pr: z.number() }),
handler: async ({ pr }) => github.fetchDiff(pr),
});
export const reviewAgent = agent(
{
id: "code-review",
triggers: [{ event: "pr.opened" }],
tools: [fetchDiff],
recording: true,
},
async ({ event, tool, llm, approve }) => {
const { pr } = event.data as { pr: number };
const diff = await tool(fetchDiff, { pr });
const findings = await llm.complete({
messages: [{ role: "user", content: `Review:\n${diff}` }],
call: () => provider.complete(diff),
});
const decision = await approve("post-review", {
ttl: "24h",
payload: findings,
});
return { approved: decision.approved, findings };
},
);

Each tool() and llm.complete() call runs through an Ironflow durable step. After a worker crash, completed step results are restored instead of repeated. An approval can wait without holding the worker.

recording: true in this example is intentional. Durable step memoization does not mean recorded execution is enabled by default.

Use the shipped saver when LangGraph should keep ownership of the graph while Ironflow persists its checkpoints.

import { StateGraph } from "@langchain/langgraph";
import { IronflowClient } from "@ironflow/node";
import { IronflowSaver } from "@ironflow/langgraph";
const client = new IronflowClient({
serverUrl: process.env.IRONFLOW_URL!,
});
const saver = new IronflowSaver({ client });
const graph = new StateGraph(State)
.addNode("plan", planNode)
.addNode("act", actNode)
.addEdge("plan", "act")
.compile({ checkpointer: saver });
await graph.invoke(input, {
configurable: { thread_id: "thread-1" },
});

The saver:

  • writes LangGraph checkpoints to an entity stream keyed by thread_id;
  • stores pending writes;
  • supports checkpoint lookup and listing within a thread;
  • resumes from the last persisted checkpoint when you invoke the graph again with the same thread ID;
  • handles LangGraph checkpoint namespaces and logical thread deletion.

The saver does not:

  • turn every LangGraph node into an Ironflow durable step;
  • expose LangGraph tools over MCP;
  • add Ironflow approval waits to graph nodes;
  • inject scoped values at node boundaries;
  • create parent-child Ironflow run links for subgraphs;
  • enable Ironflow execution recording for the graph.

Add those behaviors explicitly with the native agent or workflow APIs when your application needs them.

For a framework without a shipped adapter, place durable boundaries at the side-effecting parts of your application:

  1. Run model access through llm.complete() and provide the actual provider call in call.
  2. Register external side effects with defineTool() and invoke them through tool().
  3. Use approve() or a workflow event wait when input may arrive later.
  4. Append domain facts to entity streams after the side effect succeeds.
  5. Enable recording explicitly when you need an inspectable execution-step history.

This is manual integration, not automatic framework hosting.

Your applicationStart here
A straightforward model and tool loopNative Node or Go agent API
An existing LangGraph application that needs persistent checkpoints@ironflow/langgraph saver
A graph that also needs Ironflow durable tools or approvalsLangGraph saver plus explicit Ironflow agent or workflow boundaries
Claude SDK, CrewAI, or another framework todayManual durable boundaries around provider calls, tools, and waits
A non-agent business processIronflow durable function and workflow APIs
A browser UI that calls one agent and waits for the answer@ironflow/browser invoke()
A browser UI that follows a run as it executes@ironflow/browser emit() plus a run subscription

Local Ironflow runs as one binary with SQLite and embedded NATS. That is enough to develop and to recover work from a worker crash while the server remains available.

For durable recovery across server failures or a multi-node deployment, use Postgres and persistent external NATS. See Self Hosting for the topology and operational requirements.