qenlo-memory

0.4.0, apache-2.0

one memory for every coding agent you run

i use claude code, codex, cursor, gemini cli and copilot, and every one of them used to start from zero. qenlo-memory is one local store they all read and write, so a fix codex found yesterday shows up in claude code today, labeled by codex.

curl -fsSL https://raw.githubusercontent.com/a3ro-dev/qenlo-memory/main/install.sh | sh

no llm inside, 37 ms recall, 52 MB installed, 25 agents wired in one run

243 cubes, one for each memory my agents have written so far. drag it.

the same thing, explained once per agent

every session starts knowing nothing, and none of my agents knew what the others had figured out an hour ago. so the release steps, the preferences and the no, we tried that got explained again and again. that's fine the first time i guess, it gets old by the fifth.

$ qenlo-memory remember "release: tag, run sdk-release.yml, verify SHA256SUMS" --kind procedural
remembered #41
codex, 3:12 pm, writes down how a release goes
$ qenlo-memory recall "how do we release qenlo"
#41 procedural | by codex | 2026-11-15 15:12 | qenlo | release: tag, run sdk-release.yml, verify SHA256SUMS
claude code, two hours later, never asked

how it works

one daemon, a thin cli, and the agents you already have. nothing new to learn for them, they call it from the shell like git.

  1. install finds your agents

    it looks for every coding agent on the machine and links one brain folder into each: the skill, an always-on rule, and hooks where the agent supports them. it backs up any file it edits, once, and --dry-run shows the plan first.

  2. agents run it from the shell

    the first call starts one daemon on 127.0.0.1 with a token from your home folder. every other call is a thin client. there's one owner because qenlo locks a collection to one process.

  3. ollama embeds, qenlo searches

    embeddings come from ollama on your gpu (arctic-embed 33m, 58 MB of vram). vectors live in a qenlo collection, text and provenance in sqlite next to it.

  4. every memory has a name on it

    the agent that wrote it is read from the shell (claude code sets AI_AGENT), so a recall says by codex or by cursor and you can tell where an idea came from.

wired in one run

  • claude code
  • codex
  • cursor
  • antigravity
  • gemini cli
  • opencode
  • copilot cli
  • amp
  • factory droid
  • auggie
  • goose
  • roo code
  • continue
  • cline
  • kiro
  • qwen
  • qoder
  • windsurf
  • crush
  • kilo
  • junie
  • trae
  • vs code
  • zed
  • claude desktop

the last three over mcp, with install --mcp. it only touches agents whose config folder already exists.

recall looks three ways at once

a 22m or 33m embedding model is small enough to run anywhere, and small enough to blur exactly the things agents remember: names, ports, versions, paths. so recall adds three scores.

meaning
the query's embedding against every current memory, exact search in qenlo, no approximate index.
words
bm25 over sqlite's fts5, which ships with python. it only counts as far as its best hit is real evidence.
time
last week or in september becomes a timestamp window and a second dense search inside it.

0.5 · meaning + 0.5 · words + 0.25 · time

that recipe came out of a closed-loop study of about sixty memory papers. LoCoMo facts recall@5 went from 51.3 to 58.7, and a recency bonus, the obvious other idea, made things worse.

three fields add up, and the brightest cell wins. move the pointer to move the query.

when a fact changes, the old one steps aside

an old port that keeps coming back is worse than no memory at all. in a test with 20 facts that change and 200 distractors, plain append-only memory put the stale value on top 60% of the time.

the agent writing the memory is already an llm, so it can tell when a fact replaced another one. remember shows the memories the new one is probably about, and --replaces links them. the old one leaves search and stays in history. for facts that are made to change, --key staging.db.port names the slot and needs no id. the new value came first 20 out of 20 times.

qenlo-memory remember "the daemon listens on 7438" --replaces 12
qenlo-memory history 12
  1. #12 the daemon listens on 7437, by codex
  2. #52 the daemon listens on 7438, replaces #12
  3. history 12: replaced by #52, still readable

the numbers, losses included

everything here reproduces from research/ in the repo, including the ideas that made things worse.

against EdgeMem, the strongest published llm-free memory

LongMemEval-S, 470 questions, every turn indexed, no llm anywhere. R is the strict reading: every gold session in the top k.

LongMemEval-S retrieval
R@3NDCG@3R@10NDCG@10
qenlo-memory 0.482.7791.4494.8993.81
EdgeMem81.4990.4993.8392.80
MemGAS78.5186.8394.4789.96

against mem0 on its llm-free path

same embedding model in the same ollama, the same 2,541 LoCoMo facts and 1,982 questions, mem0 2.2.1 with its bm25 extras on. writes are slower here, 59 ms against 50, because every save checks for a fact it might replace.

qenlo-memory against mem0
qenlo-memorymem0
recall@5, higher is better58.154.3
search p50, ms3450
peak ram, MB47154
installed, MB521,000

answer accuracy, all 500 questions

71.6%

judged with LongMemEval's own prompts. the memory stays llm-free, the llm is only the benchmark's reader and judge (nemotron on nvidia's free api). multi-session questions are the weak spot, and the readout is the next thing i'm fixing. EdgeMem reports 60.0 with a different reader, so it's not a like-for-like comparison.

accuracy by question type, %
single-session assistant98.2
single-session user95.7
knowledge update85.9
preference70.0
temporal62.4
multi-session48.9

under it, qenloDB

qenlo is the embedded vector database i'm building, in rust. one process owns one durable record store, filters run before ranking, and search is exact. there's no index to tune and no server to run.

indexes are disposable, application data isn't. so ids, vectors, metadata and tombstones live in one canonical store, and the cpu, wgpu, usearch and pytorch paths are structures built from it that can be thrown away and rebuilt. search runs on the cpu by default and moves to the gpu once a matrix is big enough to be worth moving.

sdks for rust, python and typescript, with go, kotlin and swift in preview.

qenlo's own site qenlo on github

a collection: every record a row of floats. scroll to fill it.

exact search, 100k vectors of 384 dims, cpu only

p95 call latency from retained raw files, every row exact (recall 1.000). lower is better.

exact search p95, ms
qenlo12.9
Milvus Lite 3.076.2
LanceDB 0.38242.3

time to first query is 0.64 s, against 48 to 80 s for FAISS HNSW, Chroma and Milvus Lite to build an index. a selective filter with 1% of rows eligible answers in 0.24 ms against Chroma's 43.

where it loses: FAISS HNSW answers the same 100k cell in 1.5 ms, approximate, against qenlo's 12.9 exact, and FAISS on a gpu beat qenlo's cuda prototype at 1M × 768. qenlo is exact and embedded. it isn't the fastest ann engine and doesn't try to be.

what building a product on it taught the database

  • in alpha.10 the write-ahead log only ever grew, because flush() returned early after every commit. qenlo-memory found it, alpha.11 fixed it.
  • an agent's memory is mostly write one thing, then search. up to alpha.11 every write threw away the search matrix. beta.1 keeps each vector once in the buffer search reads, which made add-then-search 13x faster at 2,000 rows and cut memory per row by 40%.

install it in one line

the script installs uv if you don't have it, installs the cli, then qenlo-memory install sets up ollama, pulls the embedding model and wires every agent it finds. then restart your agents. run the same line again to upgrade.

# mac and linux
curl -fsSL https://raw.githubusercontent.com/a3ro-dev/qenlo-memory/main/install.sh | sh

# windows (powershell)
irm https://raw.githubusercontent.com/a3ro-dev/qenlo-memory/main/install.ps1 | iex

then, from any agent or your own shell

qenlo-memory recall "how do we release qenlo"
qenlo-memory recent -n 10 --agent codex
qenlo-memory remember "staging moved to 6543" --key staging.db.port
qenlo-memory review
qenlo-memory export > memories.jsonl
qenlo-memory stats

good to know

  • runs on windows x64, linux x86_64 and apple silicon. intel macs and linux arm aren't covered yet.
  • qenlo-memory install --dry-run shows every file it would touch without writing anything.
  • it's local and single-user. the daemon only listens on 127.0.0.1 and wants a token from your home folder.
  • secret redaction is a regex for common key formats. it's a seatbelt, not a guarantee.
  • import reads a mem0 export too, if you're moving over.