Spurlock Studios
Contact
Durable Agent Runtimes: Survive Restarts Without Calling It "Memory"

A long-running agent that survives crashes and waits for humans needs a durable runtime: serializable control state outside the process, a resume path that continues from the last safe boundary, and tool writes that stay safe when a step re-runs. That is not “memory.” Memory is what the agent recalls; durability is whether the run still exists after the host dies.

This spoke sits under the Agentic Systems Operating Manual. Pair it with agent memory patterns (what to persist as knowledge) and state machines for agent loops (what states the loop may occupy). Durability is the substrate that keeps those designs alive across restarts.

The short answer

  • Durability ≠ memory. Memory stores facts and conversation. Durability stores execution progress so a new process can resume correctly.
  • In-process loops die under real ops. Deploys, OOM kills, Durable Object eviction, and overnight human approvals all outlive a single Node/Python process.
  • Pick the engine by the failure mode. LangGraph checkpointing for graph-shaped agents; Cloudflare Agents / Durable Objects for addressable, hibernating entities; Temporal-style engines for multi-day workflows with activity retries and signals.
  • Measure resume correctness, not “it remembered.” After a kill, does the same thread_id / fiber / workflow continue without double-sending email?
  • HITL pauses must be durable. A pause that lives only in RAM is a memory leak with a polite name.

What a durable agent runtime is (and is not)

ConcernDurable runtimeChat / agent “memory”
Question it answersWhere was this run when the process died?What facts should the model see next turn?
Typical storeCheckpoints, event history, DO SQLite, workflow DBVector store, profile rows, transcript slices
Success metricResume correctness, no duplicate side effectsAnswer quality, fewer re-asks
Survives human wait of 3 days?RequiredOptional

If you only keep a longer prompt history, you have memory. If you can kill the worker mid-approval and still resume the same job tomorrow without inventing a new run id, you have durability.

Why in-process loops die under real ops

A demo agent is a while loop in one process. Production invents ways to murder that process:

  1. Rolling deploys mid-tool-call
  2. Platform eviction (Cloudflare Durable Objects typically idle-evict after ~70–140 seconds without keep-alive / alarms)
  3. Spot / scale-to-zero on serverless
  4. Operator restart after a bad release
  5. Human approval that arrives after the original HTTP request is gone

Failure mode we see constantly: the agent emailed the customer, the process died before writing “done,” and a retry emailed again. The model did nothing wrong. The runtime treated “in memory” as “committed.”

LangGraph checkpointing: what problem it actually solves

LangGraph checkpointers persist graph state at super-step boundaries. With a durable saver (Postgres/SQLite — not InMemorySaver), you get:

  • Resume after crash on the same thread_id
  • Human-in-the-loop via interrupt() / Command(resume=...) (requires a checkpointer)
  • Time-travel / debug from checkpoint history

Durability modes trade safety for speed (sync / async / exit per current LangGraph docs). "exit" is faster for long graphs but does not protect mid-execution crashes the way "sync" does. Read that tradeoff before you claim “we checkpoint.”

When LangGraph checkpointing is enough: one graph owns the agent; state is typed and serializable; HITL is “pause this graph for a reviewer”; you already live in the LangGraph / LangSmith world.

When it is not: you need an addressable long-lived entity that wakes on email/WebSocket/cron; or you need Temporal-grade activity timers, sagas, and cross-service orchestration that outgrows a single graph process.

Resume note that teams miss: interrupting and resuming often re-enters the interrupted node. Side effects inside that node must be idempotent or gated. Durability without idempotent tool writes doubles the blast radius.

Cloudflare Agents / Durable Objects: what problem they actually solve

Per Cloudflare Agents long-running docs, agents are Durable Objects: globally addressable identities with SQLite-backed state that hibernate when idle and wake on events. They are not always-on processes.

Primitives that matter for durability (as of the 2026 Agents SDK docs):

PrimitiveJob
setState() / this.sqlPersist entity state across activations
schedule() / alarmsWake later (HITL timers, polls)
keepAlive() / keepAliveWhile()Reduce eviction during active work
runFiber() / stash()Checkpoint long work; recover via onFiberRecovered
startFiber()Durably accept jobs with idempotency + status
runWorkflow()Hand heavy multi-step work to Cloudflare Workflows

When Cloudflare Agents fit: the agent is an entity (per tenant, per ticket, per inbox); waits span hours/days; you want hibernation economics; tool work must survive DO eviction with fibers, not hope.

When they are the wrong hammer: you only need a short request/response graph with Postgres checkpoints, and you do not want to design around eviction. Then LangGraph + Postgres is less platform-specific.

Eviction is the design constraint. keepAlive lowers the chance; runFiber makes eviction survivable. Confusing the two is how “it worked in staging” becomes “lost the job overnight.”

Temporal-style engines: what problem they actually solve

Temporal (and cousins like AWS Step Functions for some shapes) own durable execution via event history: workflows that sleep for days, activities with retries/timeouts, signals for human input, and deterministic replay.

When to reach for Temporal / Step Functions:

  • Multi-day business processes with many external systems
  • Strict activity retry/timeout policies independent of the LLM loop
  • You already run Temporal for non-AI workflows and the agent is one activity graph among many
  • You need audit-grade “exactly what happened” from the history log

When not to: a five-day pilot with one agent job type and a single Postgres checkpoint table. Temporal tax is real; earn it.

Rough decision table:

You need…Start here
Graph agent + HITL inside one appLangGraph + durable checkpointer
Addressable hibernating agent entityCloudflare Agents / Durable Objects
Multi-day cross-service orchestrationTemporal-style engine
“Remember the customer’s prefs”Memory layer — not a runtime upgrade

How human-in-the-loop pauses stay durable

A durable HITL pause is three parts:

  1. Persist the run at a named boundary (checkpoint / fiber stash / workflow wait)
  2. Return a handle the UI/ops can load (thread_id, fiber id, workflow id)
  3. Resume by feeding the human decision into the same handle — not by starting a new chat

Checklist:

  • Pause state is in durable storage, not the request thread
  • Approval payload is bound to that run id (no “approve whatever is latest”)
  • Resume path is exercised in staging with a process kill mid-wait
  • Tool nodes after resume are idempotent
  • Timeout / escalate path exists if the human never answers

If the human waits three days, the original HTTP connection is already archaeology. Only the durable handle matters.

What state should never be stuffed into the prompt

Keep these out of the model context as your source of truth:

StateWhere it belongs
Run / thread / fiber idsRuntime ledger
Tool ledger (what already ran, keys, results hashes)Harness DB — see no-progress / idempotency spokes
Auth tokens and IAM scopeSecret store + policy gate
Approval decisionsDurable HITL record
Budget / kill-switch countersControl plane
Full raw tool dumpsArtifact store with redaction

The prompt may summarize some of this. The prompt must not be the only copy. Summarization is how agents re-call a tool that already succeeded.

How to measure recovery: resume correctness vs “it remembered”

Run this drill monthly:

  1. Start a real job that reaches a HITL pause or a slow tool
  2. Kill the worker / evict the DO / restart the pod
  3. Resume from the stored handle
  4. Score the outcome
MetricPassFail
Same run id continuesYesNew run invented
Side effects onceOne email / one chargeDuplicate
State machine positionSame state as before killRewound or skipped
Human sees prior contextApproval UI shows pending payloadEmpty / wrong job

“The model recalled the ticket number” is a memory win. It is not a durability win.

Worked failure: the overnight approval that double-booked

What broke: Support agent drafted a refund, paused for manager approval in an in-memory queue. Deploy restarted the API. Manager approved what looked like a stuck ticket. A new agent run also resumed from a stale Redis key. Two refunds.

Cost: Finance cleanup, customer trust hit, two days of “why agents suck” in Slack.

Instead:

  1. Persist pause under a single durable run_id
  2. Bind the approval button to that id + payload hash
  3. Issue refund tool with an idempotency key owned by the runtime
  4. Kill-test the pause path before soft-launch

Bravery is not a restore strategy.

Durability interacting with idempotent tool writes

Durability increases how often a step re-executes after a crash. That makes idempotency mandatory, not optional. On resume:

  1. Re-enter the node / fiber / activity
  2. Tool layer sees the same idempotency key
  3. Upstream returns the original receipt — no second charge

Treat durability and idempotency as one control loop. The operating manual frames the rest of that stack; this post only owns the resume substrate.

Pilot minimum for Spurlock Studios

A $1,500 · 5-day agentic pilot does not require Temporal on day one. It does require:

  1. One durable handle per job (thread_id or equivalent)
  2. One kill-and-resume test recorded in the handoff
  3. Idempotent write tools on the critical path
  4. HITL pause that survives process death

Framework fashion is optional. Resume correctness is not.

Anti-patterns

Calling a vector store “our durable agent.” That is memory.

In-memory checkpointer in production. Fine for unit tests; worthless for crashes.

HITL as “email the ops channel and hope.” No run handle, no resume.

Checkpointing the entire blob of secrets into Postgres. Redact; store pointers.

Assuming Cloudflare Agents are always-on processes. They hibernate; design for wake/sleep.

Choosing in one afternoon

  1. List the waits: seconds (tool), minutes (reviewer), days (customer).
  2. List the kill scenarios you accept as normal (deploy, eviction, scale-to-zero).
  3. Map each wait × kill to a store that survives it.
  4. Pick the smallest engine that covers the matrix.
  5. Prove resume correctness before you argue about model quality.

If the matrix is “short tools + same-day human,” LangGraph + Postgres usually wins. If the matrix is “entity sleeps for a week then wakes on email,” Cloudflare Agents fit. If the matrix is “saga across six services for a month,” Temporal earns its keep.

FAQ

When is LangGraph checkpointing enough?

When your agent is a graph you already own, state is serializable, and HITL is “interrupt this graph / resume with a decision.” Use a durable checkpointer (Postgres/SQLite), pick an explicit durability mode, and make interrupted nodes idempotent. Skip it when you need hibernating addressable entities or Temporal-scale cross-service workflows.

When do Cloudflare Agents / Durable Objects fit?

When the agent should be a long-lived identity that hibernates, wakes on events, and survives eviction with fibers/schedules. Use setState/sql for entity data, runFiber/startFiber for crash-recoverable work, and keepAlive during active LLM/tool stretches. They are a poor fit for a short-lived graph that only needs Postgres checkpoints.

When should I reach for Temporal/Step Functions instead?

When the business process outlives a single agent app: multi-day waits, activity timeouts/retries across many systems, signals from humans or other services, and audit via event history. Do not start here for a pilot with one job type — earn the orchestration tax.

How do human-in-the-loop pauses stay durable?

Persist the run at a boundary, return a stable handle, and resume that same handle with the human’s decision. The original HTTP request will be gone. Bind approvals to run id + payload hash, and kill-test the pause path in staging.

What state should never be stuffed into the prompt?

Run ids, tool ledgers, auth material, approval records, budgets, and raw secret-bearing tool dumps. Summaries may appear in context; the durable store remains authoritative. Prompt-only “state” evaporates on summarization and restart.

How does durability interact with idempotent tool writes?

Resume re-executes boundaries. Without idempotency keys outside the model, a correct resume becomes a duplicate side effect. Design durability and idempotent writes together; measure “side effects once” in the kill-and-resume drill.

CTA

Need a durable agent that survives the first real deploy — not just the demo loop? Start on /agentic or book the pilot at /contact?intent=agentic-pilot.

Start a pilot