Agentic memory: a shared brain for my coding agents
A local, no-cost memory that Claude Code, Codex, and my assistant all share, kept current with hooks and wired up in Nix.
The thing that makes coding agents feel disposable is that they forget everything. Every session starts from nothing. Claude Code works out some detail about my setup, a port already taken by another container, a quirk in how a service boots, and then the session ends and that knowledge is gone. Next time it rediscovers the same thing from scratch, burning tokens and my patience. If I switch to Codex, it never knew in the first place. Three agents, three separate cases of amnesia.
I wanted one memory they all share. Local, so it stays private and free. Persistent, so what one agent learns sticks around. Shared, so a fact Claude figures out is there for Codex and Hermes too. This was the bigger sibling to the scheduler bridge I wrote about separately, and the part I learned the most from.
The store
The memory itself is unglamorous, which is the point. It’s a vector database (Qdrant) running on a box I already keep on, with a small server in front of it that speaks MCP, the protocol these CLIs use to reach external tools. It exposes two operations: store a piece of text, and search for the text most relevant to a query. Each of my agents points at the same endpoint. One writes, the others read.
The question I expected to wrestle with was how to decide what’s worth storing. The common answer is to run a language model over each session and have it pull out the important facts. I didn’t want a paid API in the loop, and then I realized I didn’t need one. The agent doing the writing is already a language model. Whatever it chooses to save is already distilled. The memory layer doesn’t have to be smart, it has to store text and find it again by meaning. The intelligence is the agent I’m already paying for, so the memory itself costs nothing to run.
What a hook is, and why the memory needs them
A memory the agents can reach is not automatically a memory they use. Agents are focused on the task; they don’t stop to take notes. To make the memory part of the loop, I leaned on hooks.
A hook is a script your CLI runs on its own at a specific moment: when a session starts, when the agent finishes a turn, before it runs a tool, and so on. The CLI passes the script some JSON about what’s going on, and the script can pass text back that becomes part of the conversation. It wraps the model in a bit of scripted scaffolding without touching the model itself, which is how you get deterministic behavior out of something that’s otherwise improvising.
I started with two. One runs when a session starts: it asks the memory for anything relevant to the project I’m in and drops it into the context, so the agent opens already knowing what earlier sessions worked out. The other runs when the agent finishes a turn: it reads the final message and saves anything I marked with a <remember>...</remember> tag. The agent chooses what’s worth keeping, and the hook guarantees the saving actually happens instead of relying on the agent to call a tool it might forget.
My first marker was [REMEMBER: ...]. It broke instantly, because facts are full of brackets, an array index here, a regex there, and the script kept slicing each fact off at the first ]. The XML-style tag has no such problem, so I switched.
A single tagged sentence turned out to be a thin slice of what an agent actually knows. Claude keeps a richer per-project memory on disk, small Markdown files with a name, a description, and a few paragraphs of detail. Those are the notes I’d actually want a future session to read, so I added a third path that mirrors them into the same store whenever one gets written or edited. Codex does its own thing: it consolidates a summary file in the background hours after a session ends, so a turn-end hook never catches it. A file watch does, and pushes that summary in too. The store stopped being a list of one-liners and started holding the structured notes the agents were already keeping for themselves.
All of those writes funnel through one path with a couple of guards. There’s a length floor so trivial fragments never land, and an exact-duplicate check: before a write, it searches the store and skips if the same text is already there. That kills the obvious case, the same fact saved verbatim twice. It does nothing for the harder case, which I’ll get to.
Why Nix makes this practical
Wiring a memory into three different agents on every machine I own sounds like the kind of fiddly per-host setup that never stays consistent. In Nix it’s a single description. One entry in my config defines the memory server, and it emits the correct configuration for both Claude Code and Codex on its own. The hooks are scripts pinned to exact versions of their dependencies, so they behave the same everywhere instead of depending on whatever Python happens to be on the machine. I commit it once, and every host wires its agents to the same brain.
The store itself, the database on my always-on box, is the one stateful piece that lives outside Nix. Everything that connects to it, every agent, every hook, every credential, is declared and version-controlled. If I rebuild a laptop from scratch, it rejoins the shared memory automatically, because rejoining is just part of what the config already says the machine is.
A fourth agent joined this way without much fuss. I’ve been trying out pi as a daily driver, and it doesn’t use MCP the way the others do, so I gave it an in-process extension instead: a small TypeScript port of the same store-and-recall logic, pointed at the same collection, tagged with its own agent ID. Same brain, different door. Adding it was a few files and one flake entry, and now a fact pi learns is there for Claude, and the reverse.
What it actually does, a couple weeks in
The open question was whether this compounds into something useful or just fills with noise. A real shared base of knowledge about my systems, or a junk drawer of half-duplicated facts. The store has 65 facts in it now, and the answer is mostly the first one, with a specific catch I didn’t predict.
The recall is the part that earns its keep. When I open a session in the right repo, the facts that surface are dense and genuinely load-bearing: the full shape of a deploy pipeline, a hard-won gotcha about which port a service really listens on, a decision I made weeks ago and the reasoning behind it. These aren’t one-line breadcrumbs anymore, because the memory-file sync means whole paragraphs of context flow in. I scroll past some of it, but the hit rate is high enough that I’d notice if it stopped.
The catch is duplication, and it’s not the kind I planned for. The exact-duplicate guard works fine: nothing gets saved verbatim twice. What it can’t see is the same fact rewritten over time. I keep some memories as living files, and as a project moves the file gets edited. One of my deploy notes shows up in the store twice right now, once saying the pipeline is “still manual” and once saying it’s “fully automated,” because both versions got synced on the day they were true. They’re not duplicates by string match, they’re the same fact at two points in its life, and a search for that topic returns both. Multiply that across a few actively-changing projects and you get a quiet pile of stale-next-to-current.
That’s exactly the job the planned consolidation pass would do, and looking at the actual mess has made it concrete instead of theoretical: a batch that runs a local model over the store, finds clusters of near-dups, and collapses each one to the current truth. Same idea as before, same hardware, still free, but now I know precisely what it needs to fix rather than guessing. In the meantime there’s a small admin skill I can point an agent at to inspect and prune the store by hand, which is enough to keep it honest for now.
The foundation held up better than the housekeeping. Four agents share one local memory, it costs nothing to run, and the whole thing is described in a config I can read top to bottom. Recall works. The thing that needs the next layer of effort isn’t getting facts in or out, it’s keeping the pile from rotting, and at least now I’ve seen the exact shape of the rot.
Keep Reading
A kernel sandbox under my YOLO coding agents, declared in Nix
I run Claude Code and Codex with permissions disabled, so I put a kernel-enforced floor under them with nono. Capability profil...
Free Datadog for the fleet in one Nix module
Netdata + SigNoz on Unraid, host telemetry from every NixOS box. A Sunday morning coffee project; Nix and comin made it absurdl...
A scheduler skill: delegating to my assistant from the terminal
Letting my coding CLIs hand reminders and tasks to my Telegram assistant, wired up declaratively with Nix.