Cloudflare · Agents SDK · 10 min read

How Cloudflare Agent Memory Works

An LLM forgets everything the moment a request ends. Cloudflare gives agents memory in two distinct layers — and once you see they're separate, the whole picture clicks.

Verified against Cloudflare docs & agents@0.14.0 source · current as of 2026-06-03

01 Why agents need memory at all

A language model is stateless. Each call sees only what you put in its context windowThe fixed-size buffer of tokens the model can read in a single call — the system prompt, the conversation so far, and any retrieved data. Everything outside it is invisible.. Close the request and the model remembers nothing — not your name, not what it just promised, not the file it was editing.

The naive fix is to stuff the entire history back into every call. That breaks two ways:

→ You run out of window. Tokens are finite; a long-running agent overflows it.
Context rot — even when it fits, burying the 3 relevant facts under 50,000 tokens of chatter makes the model worse, not better. Signal drowns in noise.

The job of memory

Persist what matters across calls, restarts, and days — and at inference time inject only the relevant slice back into the window. Remember everything; recall just enough.

02 The one big idea: two layers, not one

"Cloudflare agent memory" is the single biggest source of confusion because it names two different things. Untangle them and everything else is detail.

LAYER 1 — IN-AGENT the agents SDK · inside each agent Durable Object one per agent · embedded SQLite this.setState() · this.state this.sql`…` · this.messages Session API (experimental) conversation history (tree) context memory → system prompt LAYER 2 — MANAGED "Agent Memory" service · private beta env.MEMORY / REST API long-term recall, outside the window ingest · remember · recall · forget Facts · Events · Instructions · Tasks Durable Objects + SQLite (per profile) + Vectorize + Workers AI 5-channel retrieval, fused (RRF) Layer 2 is the reference impl that plugs into Layer 1's Session memory →

Layer 1 — in-agent memory. Ships in the agents SDK. Every agent is a Durable ObjectA single-instance, stateful Cloudflare compute primitive with strongly-consistent storage attached. Each agent gets its own — code and data co-located. with its own embedded SQLite database. Memory lives inside the agent.

Layer 2 — "Agent Memory" (the product). A separate, managed service (announced 17 Apr 2026, private beta) that stores structured long-term memories outside any context window and serves back only the relevant ones on demand.

Walk away with this

Layer 1 is where state lives while the agent runs. Layer 2 is durable cross-session recall that fights context rot. Same foundations (Durable Objects + SQLite), different jobs. Everything below is one or the other.

03 Layer 1, foundation: state & SQL

Because each agent is a Durable Object with SQLite running in the same execution context, reads and writes are effectively zero-latency — no network hop. State survives requests, hibernation, eviction, restarts, and deploys, automatically. There are two tiers, and the docs are strict about which to reach for:

Use this.setState() / this.stateUse this.sql`…`
Small JSON. UI state, counters, active-session data, config. Auto-persisted to the cf_agents_state table and broadcast to every connected WebSocket client. Historical data, large collections, relationships, anything queryable. A tagged-template parameterized query over the agent's private SQLite — always returns an array.
Gotcha — there is no getState()

On the server Agent class, this.state is a lazy getter, not a method. First access applies initialState (new agents) or SELECTs the persisted row, then caches. And keep state light: every setState is broadcast to all clients, so big blobs belong in this.sql.

Drop a kind of data on the agent and see where the SDK wants it to live:

// where does this data belong?

↑ pick a data kind
It'll route to the right storage tier.

04 What you can inject into context

This is the heart of your question — what does agent memory accept, and what gets injected into the model? Layer 1's Session API (imported from agents/experimental/memory/session) manages two things: the conversation history (a branchable tree in SQLite) and, more interestingly, context memory — persistent blocks that are rendered straight into the system prompt.

There are exactly four kinds of context block. Each one accepts different data and auto-generates a different tool for the agent to use. Toggle them and watch the system prompt assemble itself — with live token accounting, just like the SDK's own [45% — 495/1100 tokens] [writable] tags.

// system prompt assembler

0 / 8000 tokens injectedtools: none
How "injection" actually works

Read-only "soul" and writable blocks sit in the prompt verbatim. Searchable and loadable blocks put only a summary in the prompt (e.g. "42 entries indexed") — the bulk stays out of the window until the agent calls search_context or load_context to pull a slice in. That's how you give an agent a 200-page manual without paying 200 pages of tokens every call.

Why a frozen prompt matters

freezeSystemPrompt() renders the blocks once and caches the result; withCachedPrompt() persists it across hibernation. Crucially a set_context write does not re-render the frozen prompt — so the agent can update its own scratchpad without busting the provider's prompt cache.

05 Compaction: forgetting without losing

Conversations still grow. When history crosses a threshold (.compactAfter(100_000)), the Session compacts — but non-destructively. Older messages get summarized into an overlayA synthetic summary stored in a separate table, keyed by the range of messages it covers. It's applied at read time; the original rows are never deleted.; the originals stay in SQLite for audit and search. The model sees a short summary, but nothing is actually gone.

Press Next to watch a long thread get compacted.

Macro-compaction (above) summarizes ranges of old messages. Micro-compaction is the per-message cousin — truncateOlderMessages() shortens individual oversized tool outputs while keeping recent turns intact.

06 Layer 2: the managed memory pipeline

The Agent Memory service goes further: it doesn't just shorten history, it distills it. Conversations flow in; structured, deduplicated memories come out — stored outside any window and recalled on demand. Memories live in named profiles (grouped under namespaces), reached via a Worker binding (env.MEMORY.getProfile("project")) or a Bearer-authed REST API.

Step A — Ingest: distill conversation into typed memories

Feed it raw conversation lines. It classifies each into one of four types and assigns a content-addressed SHA-256 idA hash of session-id + role + content, truncated to 128 bits. Identical content yields the same id, so re-ingesting is idempotent — automatic dedup. for idempotent dedup. Click a sample line or write your own, then ingest:

// ingest → classify → store

Gotcha — Tasks are ephemeral

Facts, Events, and Instructions get vectorized for semantic recall. Tasks are excluded from the vector index by design — they're short-lived "do this next" items, not durable knowledge. Ingest a task line (e.g. "need to follow up with Acme on Friday") and watch its index flag flip.

Step B — Recall: five searches, one fused answer

Recall doesn't run one search — it runs five in parallel, each good at something different, then merges them with Reciprocal Rank FusionRRF: each result scores 1/(k + its rank) within each channel, summed across channels. A memory that ranks decently in several channels beats one that tops a single channel. k≈60.. Ingest a few memories above, then run a query:

// recall("…") → 5 channels → RRF

Under the hood the channels are: full-text (Porter stemming), exact fact-key lookup, raw message search, direct vector search, and HyDE — where a hypothetical answer is embedded and searched, catching matches that share meaning but not words. Extraction runs on Llama 4 Scout; final synthesis on Nemotron 3.

07 So which one do I reach for?

Five tools, one decision. Tell it what you're storing:

// I need to store…

↑ pick what you're storing
and get the right primitive.

08 Check yourself

A long-running agent's conversation gets compacted to fit the window. Later, an auditor asks for the exact original of a message that was summarized away. What happens?

Correct. Compaction writes a summary overlay in a separate table, keyed to the range it covers, and applies it only at read time. The original messages are never deleted — preserved for audit and search. That's the whole point of "remember everything, recall just enough."

09 The one sentence, earned

Remember this

Cloudflare agent memory is two layers on one foundation: in-agent state, history, and prompt-injected context blocks that live inside the Durable Object (Layer 1) — and a managed service that distills conversations into typed, deduplicated memories and recalls only the relevant ones from outside the window (Layer 2). You remember everything; you inject just enough.

Caveats worth keeping: the Session API is experimental (import paths may move) and Agent Memory is private beta (internals and REST paths may change). The state-management facts are verified against shipped agents@0.14.0 source; treat Layer 2 internals as accurate-as-of-announcement, not a stable contract.