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
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.
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.
"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 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.
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.
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.state | Use 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. |
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:
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.
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.
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.
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.
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.
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.
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:
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.
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:
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.
Five tools, one decision. Tell it what you're storing:
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?
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.