Every coding-agent session on every machine you install it on — Claude Code, Codex, Cursor, Gemini CLI, Devin, Zed and a dozen more — parsed on your machine and shipped to one typed ClickHouse store you own. Then your next agent can read what the last one already worked out.
npm install -g memhouse --allow-scripts=better-sqlite3
then memhouse onboard — discover, configure, ship, start.
keep --allow-scripts: five adapters read SQLite and need the native binding.
One shipper · seventeen adapters · whatever you switched to last month
// shown in dark mode — the dashboard ships both themes, like this page. It is agentlytics' UI, unforked: memhouse serves the same bundle over its own typed store
01 / The problem
Every agent you run already writes down what it did — into its own directory, in its own format, on whichever laptop you happened to use. Which means the answer you worked out three weeks ago is on disk and out of reach: not to you, and definitely not to the agent that is about to re-derive it.
| Session files as they ship | Export + index (RAG) | memhouse | |
|---|---|---|---|
| Where it lives | A dozen app directories, per machine | A vector store you now operate | One ClickHouse you already trust |
| Coverage | Whatever one editor wrote | Whatever exports cleanly to text | 17 adapters, JSONL and SQLite-backed editors |
| Shape of the data | Raw JSONL, or an app's private SQLite | Chunks and embeddings | Typed rows: messages, sessions, tool calls |
| Answering a question | grep, if you remember which folder | Approximate recall, no exact filters | SQL and full-text — exact, filterable, joinable |
| A second machine | A second pile of files | Re-export and re-index it | Install there too, same credential — it ships into your rooms, tagged with a stable host |
| Sharing with a teammate | Paste a transcript | Copy the whole index, or nothing | One GRANT — theirs stays theirs |
02 / Alternatives
Agent memory is a crowded field, and the projects in it do not disagree about the goal — they disagree about where you tap the stream. That choice decides everything downstream: what gets captured, whether your existing history counts, and who your prompts pass through on the way to the model.
| Nothing the files as they ship |
Proxy at the model boundary | Memory framework vector / graph store |
memhouse | |
|---|---|---|---|---|
| Where it taps in | Nowhere — each app writes its own files | In front of the LLM API; you point agents at it | SDK calls you add to your own app | Reads the session files each editor already writes |
| Your history from before you installed it | On disk, unread | Gone — only new traffic is seen | Gone, unless you write an importer | Ingested — that is the whole point |
| What it can see | Everything, unusably | The model conversation | Whatever you remembered to pass it | Tool calls, timing, project, model, cost — what actually ran |
| Where your prompts travel | Nowhere | Through the proxy, then upstream | To an embedding API, usually | Nowhere — parsing is local, rows go to your ClickHouse |
| Infrastructure it adds | None | Several services to run and keep up | A vector DB, often a graph DB too | One ClickHouse — or the one you already run |
| Who enforces isolation | The filesystem | Application code, over an app-level store | Application code, over an app-level store | The database — GRANT and currentUser(), not a service you have to trust |
| Cost of coverage | — | One integration catches every agent | One integration, but only your own app | 17 adapters to maintain — deliberately paid |
03 / How it works
memhouse is a house — a ClickHouse database — plus one resident: the shipper. Everything else in the system only reads, which is why there is so little of it.
The shipper runs all 17 adapters locally — JSONL transcripts and the private SQLite databases Cursor, Zed, Goose, OpenCode and Antigravity keep — and emits typed rows. Nothing raw leaves the disk.
messages, sessions, tool_calls as real columns, plus one
extra JSON escape hatch so no adapter field is ever lost. Incremental by default;
a re-ship of a grown session replaces the stale rows instead of duplicating them.
A dashboard for eyes, memhouse search for terms, plain SQL for everything else,
and a skills payload so an agent queries its own past sessions before it claims it doesn't know.
04 / Who it's for
Same install, same rooms. The only difference is who holds a grant.
You switch editors. You switch machines. You do not switch memory — point each machine at the same house with the same credential and every session lands in the same three rooms, each row tagged with the machine it came from. "How did I fix that" is then one query, not an archaeology dig through four app directories on two laptops.
memhouse deploy --local stands up a loopback-bound ClickHouse in one command.
Every member owns their own three rooms — messages_alice,
sessions_alice, tool_calls_alice. Isolation is a grant that is
simply absent, not a row policy that has to be right on every table and every
read path.
all_sessions) that reduces to whatever the caller can already read.A crew is not one memory. Each agent gets a credential, and a credential is three rooms — so a scout, a builder and a reviewer each hold their own history and none of them can read the others by default. What one agent may see another agent's work is a decision you make, not a filter you hope holds.
GRANTs, issued by the owner of
the rooms — no operator, no ticket. Measured end to end: a grant took what the recipient
could see from 0 rows to 22,413, and the revoke put it back to 0.SELECT, so sharing hands over reading — never writing or dropping.# one credential per agent — one isolated memory each $ memhouse install --yes --user scout … $ memhouse install --yes --user builder … ✓ messages_scout, sessions_scout, tool_calls_scout ✓ messages_builder, … # invisible to scout # let the builder read what the scout found $ GRANT SELECT ON mem.messages_scout TO builder; $ GRANT SELECT ON mem.sessions_scout TO builder; $ GRANT SELECT ON mem.tool_calls_scout TO builder; # and take it back $ REVOKE SELECT ON mem.messages_scout FROM builder;
05 / What you get
Because it is one.
Physical columns for what other tools derive at query time, a tool_calls table most don't have at all, and one extra JSON field per table so unnormalized adapter data is never dropped.
memhouse search <terms> across every session you have ever had, on any machine, in any editor — ClickHouse text indexes, not a grep over files that may already be rotated away.
Give an agent its own credential and it gets its own three rooms — it can see its memory and nobody else's. Isolation is a grant that is simply absent, so it fails closed rather than depending on a filter being right everywhere.
Every command is dual-mode: interactive for humans, --yes / flags / --json for agents. An agent can install its own memory and its own skills, unattended.
A cost engine over per-model token sums, and a dashboard showing sessions, spend, model mix, peak hours and tool usage across all your agents at once.
memhouse service install writes a real user service — systemd --user on Linux, a launchd agent on macOS — and takes over from the pidfile daemons.
06 / Where the memory lives
memhouse does not embed a database and does not run a cloud you have to trust. Point it at a ClickHouse — or let it stand one up for you.
| Path | What it runs | Who it's for |
|---|---|---|
| deploy --local | Stock ClickHouse in docker or podman, bound to loopback. It labels what it creates and refuses to touch a container or volume it did not create, so a name collision costs you an error rather than somebody else's data. | One machine — or several members later. |
| kernel install | An agency house on an ultimagent kernel: house, owner, credential, and members provisioned with grants on their own rooms. | A team, provisioned centrally. |
| your own | Any reachable ClickHouse — a server you run, ClickHouse Cloud, whatever you already pay for. | You already have one. |
07 / The CLI
Config resolves flags > MEMHOUSE_* env > ~/.memhouse/env > defaults.
# install (keep the flag — five adapters read SQLite) $ npm install -g memhouse --allow-scripts=better-sqlite3 # no ClickHouse yet? one command stands one up $ memhouse deploy --local # the wizard: discover → configure → ship → start $ memhouse onboard ✓ 17 adapters · 9 with sessions on this machine ✓ sessions shipped · dashboard on http://localhost:4640 # then, any time $ memhouse search clickhouse ttl $ memhouse stats $ memhouse status --json $ memhouse doctor
discover is a read-only preflight: which editors
it found, how many sessions are sitting on this disk, and which ClickHouses it can reach. It writes nothing.
npx --allow-scripts=better-sqlite3 -y memhouse discover
doctor diagnoses the whole pipeline, and discover says out loud when the SQLite binding is missing rather than silently shipping a partial history.08 / For agents
A house full of past sessions is only worth it if the next session can read it. memhouse ships the delivery kit that makes that the default behaviour rather than a thing you remember to do.
"Did we ever solve this before?" — asked before the answer is re-derived, not after.
/memhouse:search, /memhouse:sessions, /memhouse:sql — full text, session listing, free-form read-only SQL.memhouse plugins install claude drops them into Claude Code.memhouse prompt prints the system-prompt block that tells an agent to search memory before claiming ignorance.# give an agent its own memory, unattended $ memhouse install --yes \ --url https://ch.example.com:8443 \ --user alice --password ***** --db mem ✓ rooms minted: messages_alice, sessions_alice, tool_calls_alice ✓ verified as alice $ memhouse plugins install claude ✓ skills installed: search · sessions · sql $ memhouse prompt >> CLAUDE.md
One command to see what every agent on your machines has actually been doing — and to let the next one read it.
npm install -g memhouse --allow-scripts=better-sqlite3
09 / Questions
Only what you point it at. Parsing happens locally, and the shipper writes typed rows to the
ClickHouse you configure — which can be a container on the same laptop
(memhouse deploy --local, bound to loopback). There is no memhouse cloud and no phone-home.
--allow-scripts=better-sqlite3?Five adapters — Cursor, Goose, OpenCode, Zed and Antigravity — read sessions out of SQLite files,
and better-sqlite3 builds its native binding from an install script. npm 12 blocks
install scripts by default, so without the flag those five read nothing and you would silently ship
a partial history. discover and doctor both say so when the binding is missing.
EACCES?Check ls -ld "$(npm prefix -g)" — the prefix root is what decides, not the path
and not the failing file. Owned by root: a system-managed Node, so re-run with
sudo. Owned by you (Homebrew, fnm, nvm, volta): the prefix is yours and the root-owned
file npm tripped on is a stray from an earlier sudo npm — another sudo just
adds more. Fix that one path with chown instead.
memhouse doesn't chunk and embed — it parses. What lands in the house is typed rows you can filter, aggregate and join: by project, by model, by editor, by cost, by tool call. Full-text search is a ClickHouse text index over those rows, so a query is exact rather than approximate, and the same data also answers "what did I spend on Opus last month".
Yes, and sharing is the exception rather than the default. Every member owns their own three rooms; a shared read is a Merge room plus a GRANT, issued by the member themselves or the house owner. Nothing is visible by accident, because visibility has to be given rather than enforced.
Seventeen adapters, covering: Claude Code, Codex, Cursor, Cursor Agent, VS Code, VS Code Insiders, GitHub Copilot (CLI and JetBrains), Gemini CLI, Devin, Devin Next, Zed, Goose, OpenCode, Kiro, Antigravity, Codebuff, Command Code and GSD Workflow.
The adapters, dashboard and cost engine are built on agentlytics by Fatih Kadir Akın (MIT); the repo's history carries the full lineage. memhouse adds the typed store, per-member rooms, the shipper and the agent delivery kit. It is part of the Ultimagent constellation, where it installs as an agency.