Cloudflare Agents SDK
Add per-user FishMem memory to a Cloudflare Agent (Durable Object) — search and persist on each message.
The @fishmem/cloudflare-agents
adapter wires FishMem into the Cloudflare Agents SDK (agents package). Each
Agent is backed by a Durable Object, which is a natural per-user boundary — bind
a FishMem session to it and the agent recalls and persists memory on every
message.
FishMem Cloud runs on Cloudflare (Workers + D1 + Vectorize). Calls from your own
Worker to https://fishmem.com stay on Cloudflare's network, so recall and
persistence are low-latency.
Install
npm install agents @fishmem/cloudflare-agentsBind the API key as a secret
Don't hardcode the key. Set it as a Worker secret so it's available as an environment binding inside the Durable Object.
npx wrangler secret put FISHMEM_API_KEY
# paste your fm_... key when prompted{
"name": "memory-agent",
"main": "src/index.ts",
"compatibility_date": "2026-06-01",
"durable_objects": {
"bindings": [{ "name": "MemoryAgent", "class_name": "MemoryAgent" }]
},
"migrations": [{ "tag": "v1", "new_sqlite_classes": ["MemoryAgent"] }]
}The Agent
Subclass Agent and create a FishMem session keyed to the Durable Object's
name, which here is the user's id. On each message the agent searches FishMem,
generates a reply grounded in the recalled facts, then adds the exchange back.
import { Agent, type AgentNamespace, routeAgentRequest } from "agents";
import { createFishMemSession } from "@fishmem/cloudflare-agents";
interface Env {
MemoryAgent: AgentNamespace<MemoryAgent>;
FISHMEM_API_KEY: string;
}
export class MemoryAgent extends Agent<Env> {
async onMessage(text: string): Promise<string> {
// The DO name is the per-user key; scope all memory to it.
const memory = createFishMemSession({
apiKey: this.env.FISHMEM_API_KEY,
userId: this.name,
});
// Search relevant memories for this user.
const { results } = await memory.search(text, { top_k: 5 });
const context = results.map((m) => `- ${m.memory}`).join("\n");
const reply = await this.generate(text, context);
// Persist the new facts from the exchange.
await memory.add([
{ role: "user", content: text },
{ role: "assistant", content: reply },
]);
return reply;
}
private async generate(text: string, context: string): Promise<string> {
// Call your model of choice here, grounding it in `context`.
return `(reply grounded in:\n${context}\n)`;
}
}
export default {
fetch(request: Request, env: Env): Promise<Response> {
return routeAgentRequest(request, env).then(
(res) => res ?? new Response("Not found", { status: 404 }),
);
},
};Because each user maps to a distinct Durable Object instance, this.name is a
stable per-user key — passing it as userId keeps every user's memory isolated.
See scoping for the full model.