Stateless by Default: Managing Agent Memory at the Edge with Cloudflare KV and Durable Objects
Edge functions are stateless by design — each request gets a fresh, short-lived execution with no memory of the last one. Conversations are the opposite: an agent that cannot remember what you said two messages ago is not an agent, it is a search box. Reconciling those two facts is the architectural puzzle that stops most developers from shipping a real agent on serverless. The resolution is simple once you name it: separate the compute from the memory.
The mistake: state in the function
The instinct from server-land is to hold conversation history in a variable, a session object, an in-process map. On the edge that is a category error. Your Worker may handle this turn in Frankfurt and the next in São Paulo; there is no "this process" that persists between turns. Anything you stash in module scope is either gone or, worse, belongs to a different user. The function must be treated as genuinely amnesiac.
The pattern: the worker forgets, the store remembers
Make memory an external resource the stateless function reads at the start of a turn and writes at the end. Every request follows the same liturgy: load the conversation by its id, append the new turn, do the work, persist the updated history, respond, and vanish. The Worker holds state only for the milliseconds it is alive. The continuity lives entirely in the store.
// load → work → persist → vanish. the function keeps nothing.
app.post("/chat/:id", async (c) => {
const id = c.req.param("id");
const history = await store.get(id) ?? []; // remember
history.push({ role: "user", content: await c.req.text() });
const reply = await runAgent(history, c.env);
history.push({ role: "assistant", content: reply });
await store.put(id, history); // persist for next turn
return c.json({ reply });
});KV vs Durable Objects: pick by contention
Cloudflare gives you two homes for that state, and the choice hinges on concurrency. KV is eventually consistent and perfect when a conversation is effectively single-threaded — one user, one tab, turns arriving in sequence. Durable Objects give you a single authoritative instance with strong consistency and serialised access, which is what you want the moment multiple clients can touch the same conversation at once. Start with KV; reach for a Durable Object when you have real contention, not before.
The payoff: scale and memory, no contradiction
Once state lives outside the compute, the apparent contradiction dissolves. The agent scales to zero between turns because there is nothing to keep running, yet it remembers everything because the store never went away. You get the cost profile of a static site and the continuity of a stateful service, and you never provisioned a server to get either. That is the whole reason to build agents on the edge.
Stateless compute and stateful conversation are only contradictory if you put the state in the function. Move it out, and the edge gives you both.
This completes the serverless agent stack alongside edge-native retrieval and streamed responses — stateless where it is cheap, stateful exactly where it matters.