← ALL POSTS

Engineering · Oct 11, 2026 · 11 min read

Durable AI Agents: What They Are and How They Work

IR
Illiana Reed
CEO & Product, camelAI
Durable AI Agents: What They Are and How They Work

TL;DR: Durable AI agents keep their history, files and unfinished work outside the process that runs them. They recover recorded progress after deploys and crashes, wait days for a person or a schedule without holding compute, and resume from another process. Two common approaches are to write the agent loop as a workflow on a durable execution engine such as Temporal or Inngest, or use a durable agent runtime that owns the loop and the state, such as camelRun or Claude Managed Agents. Neither makes external side effects happen exactly once; for that you need idempotency keys and a plan for "outcome unknown."

What are durable AI agents?

An agent is a loop: send the conversation to a model, run the tools it asks for, append the results, and repeat until it answers. (Our guide on how to build an AI agent covers the parts.) In a prototype, the loop and the conversation live in one process's memory, which works until that process stops.

A durable AI agent keeps its state outside whichever process happens to be running it. That buys you four things:

  • It survives deploys, crashes and restarts mid-turn. Progress is recorded as it happens, so an interrupted turn is continued, or at least reported honestly, instead of vanishing.
  • It keeps its history and files for days or months, not just for one connection.
  • It waits without holding compute, for an approval or a scheduled wake-up, while nothing runs.
  • It resumes from any process. You address it by an id, not by the machine that started it.

Stateful, long-running and durable are not the same

  • A stateful AI agent remembers earlier turns. Saving the chat log to Postgres makes an agent stateful, but it doesn't save the turn that was running when the process died.
  • A long-running AI agent works or waits for minutes, hours or days. Without durability that's fragile: the longer a turn runs, the likelier a deploy lands in the middle of it.
  • A durable agent is both, plus recovery: its state and its pending work outlive any one process.

What goes wrong without durability

  1. A deploy kills a turn halfway. The model asked for three tool calls and two ran. The turn lived in memory, so the user waits for a reply that never comes.
  2. A blind retry repeats a side effect. The user or your job queue retries, the model plans again, and send_invoice runs twice.
  3. A wait holds a process. Waiting for a manager's approval means keeping a request, socket or serverless function open until something times out.
  4. The agent is pinned to one machine. The next message lands on an instance that has never seen the conversation.
  5. Follow-ups disappear. "Check the build again in an hour" was a timer in memory.

Durable execution for AI agents: how it works

Every durable system records progress somewhere that outlives the process, and after a failure rebuilds from that record instead of starting over. There are two common ways to rebuild.

Replay. Durable execution engines record the result of each step (a model call, a tool call) in an event history or journal. After a crash they run your code again from the top and hand back recorded results for the steps that already finished, so the code reaches the same point without redoing them. Temporal does this instead of restoring a memory snapshot. The price is determinism: given the same recorded results, code outside steps must make the same calls in the same order. Temporal and DBOS both require it, and Inngest warns that code outside a step can run again on every replay.

Resume from saved state. Much of an agent loop's state is its conversation, alongside pending work and control state. If each model response and tool result is saved as it's produced, another process can load that state and carry on. Managed agent runtimes handle this recovery without requiring you to write a replayable workflow; your tools still need to handle retries and interrupted calls. (Trigger.dev takes a third route: during long waits it checkpoints the whole running process and restores it later.)

Recorded model outputs should be reused during recovery: a model can answer the same prompt differently. If a response or tool result was never committed, recovery still needs to handle that uncertainty before repeating an action.

Two approaches: durable workflow engines vs durable agent runtimes

Durable workflow engines

You write the agent loop as a workflow. Each model call and tool call becomes a step (an "activity" in Temporal), which the engine checkpoints and replays.

  • Temporal replays an event history. Its OpenAI Agents SDK integration for Python has been generally available since March 23, 2026 and runs each agent invocation as an Activity.
  • Inngest checkpoints each step.run() result and replays the handler around them.
  • Restate journals every step and deduplicates requests that carry an idempotency key.
  • DBOS is a library that checkpoints workflows and steps to Postgres, with no separate orchestration server.
  • Vercel Workflows uses 'use workflow' and 'use step' directives, and runs can pause for minutes to months. The AI SDK's WorkflowAgent makes your agent a workflow and your tools steps.
  • Trigger.dev pauses tasks that wait for more than a few seconds.

You get control of the loop, retries and observability, and agents that sit inside larger business processes on the same engine. You still provide or integrate the loop, context management, conversation storage across workflow runs, the approval inbox and a way to address one agent per user. Engines that replay workflow code also require it to be deterministic.

Durable agent runtimes

Managed agent runtimes own the loop and the state. You supply a model, instructions and tools; they run each turn and retain state between turns. Features such as file persistence, scheduled wake-ups and approvals vary. Programmable runtimes such as Cloudflare Agents supply durable primitives and let you choose or write the harness.

  • camelRun. Agents are keyed by your own ids, and the same key is the same agent from any process. History, files, configuration and pending work live in the runtime, not your process. Schedules, channels (Slack, Telegram, Discord) or your own code can wake it. Runs have no timeout. When your app deploys, closing its agents on SIGTERM drains running tool calls for a bounded period and hands new ones to the next process. Calls that outlast the drain can still have an unknown outcome.
  • Cloudflare Agents. Each agent is a Durable Object with its own SQL storage and scheduling, and it hibernates when inactive. Fibers checkpoint work to SQLite; after an eviction, your onFiberRecovered hook decides how to recover. The Project Think harness on top is experimental.
  • Letta. Stateful agents whose memories, messages, reasoning and tool calls are all persisted in a database.
  • Claude Managed Agents (beta). Sessions that resume cleanly after pauses, with history, sandbox state and outputs stored server-side.
  • OpenAI's Agents API. OpenAI manages sessions, orchestration, context compaction and recovery; your application provides the tools.

With a managed harness, you get the loop and its state handling without writing them. When camelAI moved camelCode's agents onto camelRun, the team deleted about 72,000 lines of code (the camelRun intro post explains why). You give up control of that harness's internals, and you work within its models, sandbox and limits. Programmable runtimes leave more of those choices to you. For a product-by-product view, see AI agent runtimes and hosting platforms compared.

Side by side

Durable workflow engineDurable agent runtime
ExamplesTemporal, Inngest, Restate, DBOS, Vercel Workflows, Trigger.devcamelRun, Cloudflare Agents, Letta, Claude Managed Agents, OpenAI Agents API
Who writes the loopYou, or a workflow-compatible agent libraryA bundled harness, or your code on a programmable runtime
What gets recordedEach step's result (model call, tool call)The agent: history, files, config, pending work
RecoveryMost replay your code against recorded results, so it must be deterministicThe runtime reloads the agent's state; how an interrupted turn resumes varies by product
Waiting on a personA primitive (signal, event, hook, token); you build the inbox and UIVaries; camelRun has built-in approvals that suspend the run
A lost tool resultUsually retried (at-least-once), so steps must be idempotentVaries; camelRun never re-sends the call and tells the model the outcome is unknown
Best fitAgents inside larger business processes; full control of the loopMany long-lived agents per user, ticket or thread, mostly asleep

What durability doesn't guarantee: exactly-once side effects

Durable history keeps the agent's own state safe. It doesn't make the outside world transactional. Take one tool call:

  1. The model asks to refund order 123.
  2. Your tool sends the refund to the payment provider, which refunds it.
  3. Before the response is recorded, the process dies, the connection drops or the call's deadline passes.

Whatever recovers the agent now holds a call with no result, and the fact it needs (did the refund happen?) lives in someone else's system. It can send the call again (at-least-once: you may refund twice) or not (at-most-once: the refund may never happen). No durability layer can close that gap from its side. An external effect happens exactly once only when the receiving system deduplicates, which is what idempotency keys are for.

Systems pick different sides:

  • Workflow engines generally retry. Temporal's docs say activities may run more than once (say, a worker finishes one but crashes before reporting it) and suggest keying them by workflow run ID plus activity ID. DBOS asks for idempotent steps, and Vercel's WorkflowAgent retries failed tool calls up to 3 times by default.
  • camelRun never re-sends a lost call. When a tool call's answer is lost (the connection dropped or its deadline passed), the model is told the outcome is unknown so it can check before repeating anything, and the run lists the call in toolErrors. Every tool call also carries context.idempotencyKey, which stays the same across attempts of that call, such as when it runs again after a person answers.

Key every side effect

  • If the downstream API accepts an idempotency key, pass yours through.
  • If it doesn't, record the key yourself under a unique constraint, in the same transaction as the change, so a repeat becomes a no-op.
  • Prefer naturally idempotent operations. "Set ticket 123's status to closed" is safe to repeat; "append another closure event" needs deduplication.

This tool from camelRun's human input guide shows the pattern. context.confirm pauses the call for a person; once they answer, the runtime calls the tool again with the same arguments, so everything before the ask runs twice. Ask first, act after, and key the side effect:

const deleteApp = tool({
  description: "Delete an app", input: schema.Object({ app: schema.String() }),
  execute: async ({ app }, context) => {
    if (!await context.confirm(`Delete ${app}? Its URL stops working.`)) return { cancelled: true };
    return apps.delete(app, { idempotencyKey: context.idempotencyKey });
  },
});

Plan for "outcome unknown"

  • Give the model a way to check. A read tool such as get_refund_status(orderId) lets it confirm before trying again; say in its instructions that an unknown outcome means check first. Deduplicate on the business key (order 123, refund) too, so a second attempt the model decides on is also safe.
  • Handle uncertainty in your code. In camelRun, run.toolErrors lists tool calls that didn't complete, and run() throws a RunError with uncertain set when a restart cut the run short. Find out what happened before you retry.
  • Retry requests, not runs. Reuse the run's idempotencyKey when you retry after a timeout, and you get the original run back instead of a second one.
  • Reply from the result, not the stream. camelRun records a run's result durably, and it arrives even if your process restarts. The event stream is for showing progress.

Waiting days for a person

On a workflow engine you build approvals from a primitive: a Temporal signal, Inngest's step.waitForEvent(), a Restate awakeable, a Vercel Workflows hook or a Trigger.dev waitpoint token. The run pauses while your app stores the request, shows it to someone and sends the answer back. Waits are cheap: Inngest doesn't run your compute during one, Restate can suspend a function on a FaaS platform so you don't pay for the wait, and Trigger.dev doesn't count timed waits over 5 seconds toward compute.

In camelRun, approvals are part of the runtime. Mark a tool needsApproval: true (or give a definition's tool sources an approval policy) and the run suspends before the call: it ends with status: "input_required", the agent sleeps, and no run time is billed while it waits. When someone answers, minutes or days later and from any process, the turn resumes, and the model sees an ordinary tool result saying who answered and how long it took.

let run = await agent.run("Delete the staging database", { user: "alice" });
while (run.status === "input_required") {
  const input = run.inputs[0];
  console.log(input.kind, input.message);          // "approval", "Allow db__drop to run?"
  run = await input.answer(true, { from: "alice" }); // resolves with the resumed run
}
console.log(run.text);

Details that matter in production:

  • The approver sees the real call: the tool, its source and its arguments. An approved call runs with exactly those arguments; a declined call never runs.
  • Waits are bounded. Inputs expire after 7 days by default, configurable from 60 seconds to 30 days but never past the agent's own expiry.
  • A new message replaces a waiting input, and agent.abort() cancels it.
  • Approvals can go to an inbox. agents.runtime.inbox("pending") lists waiting inputs across all your agents, and the input.requested webhook announces new ones.
  • Sandboxed code can't wait for days. A call from the model's JavaScript that would ask a person fails, so tools that ask are always offered to the model directly.

Pricing and limits change often; check each vendor's pricing page before deciding.

When to choose a workflow engine or an agent runtime

Choose a durable workflow engine when…

  • The agent is one step in a larger business process you already run, and you want both on one engine.
  • You want to own every line of the loop: custom planning, multi-agent graphs, your own retry rules.
  • Your team already runs Temporal, Inngest, Restate, DBOS, Vercel Workflows or Trigger.dev.

Choose a durable agent runtime when…

  • You want many long-lived agents, one per customer, ticket, thread or pull request, most of them asleep at any moment. Our guide to multi-tenant AI agents covers that architecture.
  • You'd rather not build history storage, wake-ups, approvals and channels yourself.
  • Your agent's tools are your own app's APIs.

Where camelRun fits, and where it doesn't

camelRun is a durable agent runtime for agents inside a product. It works with any model and keeps your tools in your own code, with your credentials. The runtime is open source (AGPL-3.0), and you can self-host it with one Docker container and Postgres.

It isn't a Linux box. Its code sandbox runs JavaScript only: no shell, no Python, no package installs, no network. (Your tools run in your own code, so those limits don't apply to them.) If your agent needs to build and test code or run arbitrary CLIs, use a product with a full sandbox, such as Claude Managed Agents, or a coding harness in your own container.

When you don't need durability

  • A single model call that finishes in seconds and is safe to run again.
  • A prototype, where losing a turn costs you one retry.

Try camelRun

camelRun is camelAI's hosted runtime for durable agents. You define each agent's model, instructions, tools and channels; camelRun runs the loop, keeps each agent's history and files, and wakes agents when there's work. It costs $0.01 per agent-hour of active run time, plus model usage, and sleeping agents cost only storage. Start with the quickstart, or read more on the camelRun product page.

FROM THE TEAM BEHIND CAMELAI

Try what we're building.