Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/docs/src/content/docs/ecosystem/deploy/cloudflare.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ export const cloudflare = extend({
});
```

This is an advanced Cloudflare-only extension point. Flue applies `base` first, then defines its own Durable Object subclass with the generated binding and class identity. For the `Heartbeat` agent, authored Worker code can access the namespace as `env.FLUE_HEARTBEAT_AGENT`, and Wrangler binds that name to `FlueHeartbeatAgent`. Use `base` for native SDK lifecycle hooks and additional named methods. Do not override `fetch()`, `onRequest()`, `onFiberRecovered()`, or `alarm()`: Flue and the Agents SDK use those methods for routing, interruption recovery, and alarm multiplexing.
This is an advanced Cloudflare-only extension point. Flue applies `base` first, then defines its own Durable Object subclass with the generated binding and class identity. For the `Heartbeat` agent, authored Worker code can access the namespace as `env.FLUE_HEARTBEAT_AGENT`, and Wrangler binds that name to `FlueHeartbeatAgent`. Use `base` for native SDK lifecycle hooks and additional named methods. Do not override `fetch()`, `onRequest()`, `onError()`, `alarm()`, or `taskDefinitions`: Flue and the Agents SDK use them for routing, Task failure recovery, alarm multiplexing, and the durable execution definitions. A `base` that declares its own `taskDefinitions` keeps them; Flue merges its two definitions over them.

Use `wrap` when an integration needs to wrap the final Flue-generated Durable Object class:

Expand Down
4 changes: 2 additions & 2 deletions apps/docs/src/content/docs/guide/cloudflare-target.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,10 @@ dispatch(...) input ────────────────────

The submitting connection observes the work but does not own it. If a client disconnects after admission, backend work can continue. Agent events are durably stored and can be replayed from any offset via the Durable Streams protocol.

Accepted work runs inside the Durable Object itself, as one platform-visible unit per response: after admission answers, Flue schedules a zero-delay wake, and the object's alarm invocation claims the queued submission and awaits the full response — every model turn and tool call — before completing. Admission always returns first; the response begins on the next alarm tick, typically within tens of milliseconds. Three consequences:
Accepted work runs inside the Durable Object itself, as one durable state machine on the Agents SDK's [Tasks](https://github.com/cloudflare/agents/blob/main/docs/agents/tasks.md) engine. Every conversation has one `flue:conversation@v1` run at a fixed address: its `idle` phase parks on the run's mailbox — holding no alarm at all — until admission sends it a wake, then claims the next runnable submission from the durable queue; its `turn` phase awaits that submission's full response — every model turn and tool call — and settles it, and the machine returns to `idle`, where the next queued head is claimed in the same invocation. Admission always returns first; the response begins in the same invocation when the object is warm, or on the next wake otherwise. The SDK owns the durable claim while a turn is live (a backstop that survives eviction), replays the turn on a fresh isolate after an interruption, and bounds a turn that stops making progress with its transition watchdog; the submission's own [durability timeout](/docs/guide/durability/#retry-budget-and-timeout) aborts the turn's work from inside it. Three consequences:

- **Platform observability sees agent work.** Workers Logs, Workers Traces, and invocation-wrapping integrations (the Sentry `wrap` pattern in [Extending Agents](#extending-agents-on-cloudflare)) attribute the whole response to the one invocation that ran it. [`createCloudflareTracing()`](#createcloudflaretracing) (installed by default) adds agent-level spans to those traces. See [Observability](/docs/guide/observability/#cloudflare) for the two views and [Deploy on Cloudflare](/docs/ecosystem/deploy/cloudflare/#observability) for enabling them.
- **Scheduled callbacks wait for a running response.** A Durable Object runs one alarm at a time, so `schedule()`/`scheduleEvery()` callbacks that come due mid-response fire after it settles — delivery is durable; timeliness is not guaranteed while the agent is busy. `queue()` is not alarm-driven and is unaffected. Steering is also unaffected: a message that arrives mid-response still joins it at the next turn boundary.
- **Scheduled callbacks share the alarm with running responses.** The SDK drives due work from one alarm loop and hands a long attempt off after a short dispatch budget, so `schedule()`/`scheduleEvery()` callbacks that come due mid-response still fire — delivery is durable, and timeliness is best-effort while the agent is busy. `queue()` is not alarm-driven and is unaffected. Steering is also unaffected: a message that arrives mid-response still joins it at the next turn boundary.
- **CPU limits are per invocation.** Agent responses are I/O-bound and sit far below the default 30-second active-CPU limit; raise [`limits.cpu_ms`](https://developers.cloudflare.com/durable-objects/platform/limits/) in `wrangler.jsonc` if an agent computes heavily.

When a Durable Object resumes after interruption, Flue decides what to do next from the stored input and canonical conversation progress. It requeues only when it can prove the input was not applied, recognizes already-completed output, and records an interruption instead of blindly repeating uncertain model or tool work.
Expand Down
8 changes: 4 additions & 4 deletions apps/docs/src/content/docs/guide/durability.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ A recovered conversation always comes to rest in a state where the next message

Each interruption consumes one attempt. When a submission exhausts its attempts, or exceeds its wall-clock timeout, retrying stops: the conversation is settled to a rest state, a `submission_interrupted` advisory lands in the timeline, and the submission settles `failed` — waiters reject with the structured error, including which tool calls were left with unknown outcomes.

The timeout is enforced preemptively, not just between attempts. The coordinator supervises running attempts on its wake cadence: at the deadline it fires the attempt's abort signal, so work suspended on a signal-aware await (a provider call, a sandbox command, any tool — a `run` that ignores its signal is abandoned rather than awaited) unwinds and settles through the normal paths — and an attempt hung below the abandonable layer is settled `failed` over the hung fiber after a short grace, its late writes fenced off. A hang delays settlement by at most the deadline plus that grace; it can never strand the submission. Most stalls never reach the deadline at all: a model stream that goes silent past its [idle timeout](/docs/reference/provider-api/#cloudflarebindingprovider) fails as a transient provider error and the turn retries under the [error budget](#recovery-after-an-interruption).
The timeout is enforced preemptively, not just between attempts. At the deadline the running attempt's abort signal fires, so work suspended on a signal-aware await (a provider call, a sandbox command, any tool — a `run` that ignores its signal is abandoned rather than awaited) unwinds and settles through the normal paths — and an attempt hung below the abandonable layer is settled `failed` over it, its late writes fenced off. A hang delays settlement by at most the deadline plus one wake; it can never strand the submission. Most stalls never reach the deadline at all: a model stream that goes silent past its [idle timeout](/docs/reference/provider-api/#cloudflarebindingprovider) fails as a transient provider error and the turn retries under the [error budget](#recovery-after-an-interruption).

The defaults are 10 attempts and one hour per submission. Override them per agent with the `durability` static:

Expand Down Expand Up @@ -134,10 +134,10 @@ See the [Node.js target guide](/docs/guide/node-target/#state-and-durability) fo

### Cloudflare recovery

On Cloudflare, every agent conversation is a Durable Object with its own SQLite storage, so ownership is structural — the platform guarantees one live instance per conversation, and there is no lease protocol to operate. Recovery is wake-driven:
On Cloudflare, every agent conversation is a Durable Object with its own SQLite storage, so ownership is structural — the platform guarantees one live instance per conversation, and there is no lease protocol to operate. Execution is one durable state machine per conversation on the Agents SDK's Tasks engine, and recovery is wake-driven:

- **Wake on start.** Whenever the Durable Object starts — after an eviction, a code deploy, or a platform reset — Flue immediately flags any attempt that was running when the previous instance died and reconciles it before serving new work. The platform's fiber-recovery callback triggers the same reconciliation path.
- **A durable wake schedule.** While unsettled work exists, the object keeps a short self-renewing wake scheduled, so an interrupted submission recovers promptly even if no external request ever arrives to wake the object. Each wake runs a bounded supervision pass — reconcile, enforce deadlines, start work — and re-arms its successor before doing anything that can fail, so a hung attempt or a failed pass can delay supervision by at most one wake, never break it. Attempt execution runs detached from the wake that started it.
- **Wake on start.** Whenever the Durable Object starts — after an eviction, a code deploy, or a platform reset — Flue wakes the conversation machine if any submission is unsettled. Its `idle` pass reconciles first: an attempt that was running when the previous instance died is classified from the canonical stream and settled or given a replacement attempt before new work is claimed. A `turn` interrupted mid-response replays on the same start and reconciles from the same evidence.
- **A durable claim.** While a turn is live, the SDK keeps a claim deadline in storage and wakes the object at it, so an interrupted submission recovers promptly even if no external request ever arrives. The submission's wall-clock timeout aborts the turn's work from inside it; a turn that then stops making progress trips the SDK's transition watchdog, which abandons that attempt — its later writes to the conversation are refused — and returns the machine to `idle`, where the submission settles through the timeout classification above.

Abort intents, attempt bookkeeping, and settlement records all live in the object's own storage, so an abort requested while the object was evicted is honored on the next wake. See the [Cloudflare target guide](/docs/guide/cloudflare-target/#durable-agent-execution) for the target's execution model.

Expand Down
Loading