Agent memory
An agent’s database is its memory. One FTS5-backed table is the whole store — no embeddings, no vector database, no extra infrastructure. FTS5 ships in PerSQL’s SQLite, so keyword recall is built in.
import { PerSQL } from "@persql/sdk";
const persql = new PerSQL({ token: process.env.PERSQL_TOKEN! });const mem = persql.database("acme/agent-memory");
await mem.query(` CREATE VIRTUAL TABLE IF NOT EXISTS memory USING fts5( kind UNINDEXED, -- 'fact' | 'episode' topic, body, created_at UNINDEXED )`);
// Remember something.await mem.query( "INSERT INTO memory (kind, topic, body, created_at) VALUES (?, ?, ?, ?)", ["fact", "billing", "Acme prefers net-30 invoicing", Date.now()],);
// Recall by keyword — BM25-ranked, most relevant first.const { data } = await mem.query( "SELECT kind, topic, body FROM memory WHERE memory MATCH ? ORDER BY rank LIMIT 5", ["invoice OR billing OR payment"],);// → data: [{ kind: "fact", topic: "billing", body: "Acme prefers net-30 invoicing" }]Three kinds of memory, one substrate
Section titled “Three kinds of memory, one substrate”| Kind | Lives in | Lifetime |
|---|---|---|
| Factual — durable preferences, profiles, learned rules | the base database | persists across every run |
| Working — scratch for the task in flight | a throwaway branch | deleted with the run |
| Episodic — what happened on a given run, for later recall | the base database | append-only history |
Working memory belongs in a per-run branch so a failed run leaves no residue; facts and episodes belong in the base database so the next run can read them. See Per-agent sandbox for the branch-per-run mechanics.
No vector database
Section titled “No vector database”FTS5 gives lexical, BM25-ranked recall — enough for the keyword and preference lookups most agents actually make. PerSQL stays the durable, structured store; if a workload needs semantic similarity, layer a vector or memory service over these rows and keep the source of truth here.
What it costs
Section titled “What it costs”Memory is rows like any other — metered on rows read, rows written, and storage, with no per-table or per-feature fee. An FTS5 index counts toward storage; keep working memory in a TTL branch so it bills only for the run’s lifetime.
Next step
Section titled “Next step”Give the agent the schema in one round-trip with
db.describe(), then let it write its own recall
queries against the memory table.