memhouse 0.3.2 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +64 -6
  2. package/bin/memhouse.js +1014 -41
  3. package/editors/antigravity.js +11 -2
  4. package/editors/cursor.js +16 -7
  5. package/editors/opencode.js +12 -3
  6. package/editors/zed.js +13 -4
  7. package/index.js +2 -2
  8. package/mem-house/COMPETITION.md +217 -0
  9. package/mem-house/DESIGN.md +61 -32
  10. package/mem-house/delivery/AGENT-INSTALL-PROMPT.md +58 -0
  11. package/mem-house/delivery/AGENT-INSTALL.md +12 -4
  12. package/mem-house/delivery/PROMPT.md +23 -10
  13. package/mem-house/delivery/install.sh +9 -7
  14. package/mem-house/delivery/kernel-install.md +56 -25
  15. package/mem-house/delivery/skills/memhouse-search/SKILL.md +46 -16
  16. package/mem-house/delivery/skills/memhouse-sessions/SKILL.md +38 -7
  17. package/mem-house/delivery/skills/memhouse-sql/SKILL.md +48 -17
  18. package/mem-house/deploy.js +333 -0
  19. package/mem-house/envfile.js +49 -0
  20. package/mem-house/per-member/INSTALL.md +183 -0
  21. package/mem-house/per-member/PLAN.md +174 -0
  22. package/mem-house/per-member/PROVISIONING.md +177 -0
  23. package/mem-house/per-member/README.md +48 -0
  24. package/mem-house/per-member/SCHEMA.md +73 -0
  25. package/mem-house/per-member/SHARING.md +102 -0
  26. package/mem-house/per-member/provision.js +142 -0
  27. package/mem-house/per-member/rooms.js +132 -0
  28. package/mem-house/per-member/schema-member.sql.tpl +85 -0
  29. package/mem-house/per-member/schema-merge.sql.tpl +24 -0
  30. package/mem-house/server/queries.js +103 -53
  31. package/mem-house/service.js +400 -0
  32. package/mem-house/shipper/ship.js +61 -18
  33. package/package.json +3 -3
  34. package/pricing.js +20 -2
  35. package/pricing.json +216 -24
  36. package/public/assets/{index-CCPh7a2Z.js → index-CtH-5i95.js} +5 -5
  37. package/public/index.html +1 -1
  38. package/ui/src/App.jsx +3 -2
  39. package/ui/src/pages/SqlViewer.jsx +39 -21
  40. package/mem-house/rls.sql +0 -17
  41. package/mem-house/schema.sql +0 -145
@@ -2,7 +2,16 @@ const { execSync, execFileSync } = require('child_process');
2
2
  const fs = require('fs');
3
3
  const path = require('path');
4
4
  const os = require('os');
5
- const Database = require('better-sqlite3');
5
+ // Required lazily. A top-level require makes this native module a load-time dependency
6
+ // of the whole adapter set: editors/index.js imports every adapter, so one missing
7
+ // binding threw before any adapter ran and `discover` reported ZERO editors — losing the
8
+ // eleven that need no native code at all. The adapterErrors path exists to degrade one
9
+ // adapter at a time, and a top-level throw walks straight past it.
10
+ let Database = null;
11
+ function openDb(...a) {
12
+ if (!Database) Database = require('better-sqlite3');
13
+ return new Database(...a);
14
+ }
6
15
  const { getAppDataPath } = require('./base');
7
16
  const adapterErrors = require('./adapter-errors');
8
17
 
@@ -235,7 +244,7 @@ function readGlobalStateValue(key) {
235
244
 
236
245
  let db = null;
237
246
  try {
238
- db = new Database(ANTIGRAVITY_GLOBAL_STORAGE_DB, { readonly: true, fileMustExist: true });
247
+ db = openDb(ANTIGRAVITY_GLOBAL_STORAGE_DB, { readonly: true, fileMustExist: true });
239
248
  const row = db.prepare('SELECT value FROM ItemTable WHERE key = ?').get(key);
240
249
  if (!row) return null;
241
250
  const v = row.value;
package/editors/cursor.js CHANGED
@@ -1,4 +1,13 @@
1
- const Database = require('better-sqlite3');
1
+ // Required lazily. A top-level require makes this native module a load-time dependency
2
+ // of the whole adapter set: editors/index.js imports every adapter, so one missing
3
+ // binding threw before any adapter ran and `discover` reported ZERO editors — losing the
4
+ // eleven that need no native code at all. The adapterErrors path exists to degrade one
5
+ // adapter at a time, and a top-level throw walks straight past it.
6
+ let Database = null;
7
+ function openDb(...a) {
8
+ if (!Database) Database = require('better-sqlite3');
9
+ return new Database(...a);
10
+ }
2
11
  const path = require('path');
3
12
  const fs = require('fs');
4
13
  const os = require('os');
@@ -147,7 +156,7 @@ function getWorkspaceMap() {
147
156
 
148
157
  function getComposerHeaders(stateDbPath) {
149
158
  try {
150
- const db = new Database(stateDbPath, { readonly: true });
159
+ const db = openDb(stateDbPath, { readonly: true });
151
160
  const row = db.prepare("SELECT value FROM ItemTable WHERE key = 'composer.composerData'").get();
152
161
  db.close();
153
162
  if (!row) return [];
@@ -277,7 +286,7 @@ function getChats() {
277
286
  // Source 1: ~/.cursor/chats store.db
278
287
  for (const { workspace, chatId, dbPath } of getAgentStoreChats()) {
279
288
  try {
280
- const db = new Database(dbPath, { readonly: true });
289
+ const db = openDb(dbPath, { readonly: true });
281
290
  const meta = readStoreMeta(db);
282
291
  db.close();
283
292
  if (meta) {
@@ -316,7 +325,7 @@ function getChats() {
316
325
 
317
326
  // Source 2: workspaceStorage composers
318
327
  let globalDb = null;
319
- try { globalDb = new Database(GLOBAL_STORAGE_DB, { readonly: true }); }
328
+ try { globalDb = openDb(GLOBAL_STORAGE_DB, { readonly: true }); }
320
329
  catch (e) {
321
330
  // Absent is normal — Cursor may simply not be installed. Unreadable is not.
322
331
  if (fs.existsSync(GLOBAL_STORAGE_DB)) adapterErrors.record('cursor', e, GLOBAL_STORAGE_DB);
@@ -369,7 +378,7 @@ function getChats() {
369
378
 
370
379
  function getMessages(chat) {
371
380
  if (chat._type === 'agent-store') {
372
- const db = new Database(chat._dbPath, { readonly: true });
381
+ const db = openDb(chat._dbPath, { readonly: true });
373
382
  const msgs = collectStoreMessages(db, chat._rootBlobId);
374
383
  db.close();
375
384
  // Use lastUsedModel as fallback for assistant messages without model info
@@ -382,7 +391,7 @@ function getMessages(chat) {
382
391
  }
383
392
 
384
393
  let globalDb;
385
- try { globalDb = new Database(GLOBAL_STORAGE_DB, { readonly: true }); }
394
+ try { globalDb = openDb(GLOBAL_STORAGE_DB, { readonly: true }); }
386
395
  catch (e) {
387
396
  // Returning [] unreported would let a re-ship overwrite this session's stored
388
397
  // transcript with nothing.
@@ -407,7 +416,7 @@ function getMessages(chat) {
407
416
 
408
417
  function getCursorAccessToken() {
409
418
  try {
410
- const db = new Database(GLOBAL_STORAGE_DB, { readonly: true });
419
+ const db = openDb(GLOBAL_STORAGE_DB, { readonly: true });
411
420
  const row = db.prepare("SELECT value FROM ItemTable WHERE key = 'cursorAuth/accessToken'").get();
412
421
  db.close();
413
422
  return row ? row.value : null;
@@ -1,7 +1,16 @@
1
1
  const path = require('path');
2
2
  const fs = require('fs');
3
3
  const os = require('os');
4
- const Database = require('better-sqlite3');
4
+ // Required lazily. A top-level require makes this native module a load-time dependency
5
+ // of the whole adapter set: editors/index.js imports every adapter, so one missing
6
+ // binding threw before any adapter ran and `discover` reported ZERO editors — losing the
7
+ // eleven that need no native code at all. The adapterErrors path exists to degrade one
8
+ // adapter at a time, and a top-level throw walks straight past it.
9
+ let Database = null;
10
+ function openDb(...a) {
11
+ if (!Database) Database = require('better-sqlite3');
12
+ return new Database(...a);
13
+ }
5
14
  const adapterErrors = require('./adapter-errors');
6
15
 
7
16
  // OpenCode stores data in XDG-style paths across all platforms
@@ -28,7 +37,7 @@ const DB_PATH = getOpenCodeDbPath();
28
37
  function queryDb(sql) {
29
38
  if (!fs.existsSync(DB_PATH)) return [];
30
39
  try {
31
- const db = new Database(DB_PATH, { readonly: true });
40
+ const db = openDb(DB_PATH, { readonly: true });
32
41
  const rows = db.prepare(sql).all();
33
42
  db.close();
34
43
  return rows;
@@ -85,7 +94,7 @@ function getSqliteSessions() {
85
94
  function getSqliteMessages(sessionId) {
86
95
  if (!fs.existsSync(DB_PATH)) return [];
87
96
  try {
88
- const db = new Database(DB_PATH, { readonly: true });
97
+ const db = openDb(DB_PATH, { readonly: true });
89
98
  const messages = db.prepare(
90
99
  `SELECT m.id as msg_id, m.data as msg_data, m.time_created
91
100
  FROM message m WHERE m.session_id = ? ORDER BY m.time_created ASC`
package/editors/zed.js CHANGED
@@ -2,7 +2,16 @@ const path = require('path');
2
2
  const fs = require('fs');
3
3
  const os = require('os');
4
4
 
5
- const Database = require('better-sqlite3');
5
+ // Required lazily. A top-level require makes this native module a load-time dependency
6
+ // of the whole adapter set: editors/index.js imports every adapter, so one missing
7
+ // binding threw before any adapter ran and `discover` reported ZERO editors — losing the
8
+ // eleven that need no native code at all. The adapterErrors path exists to degrade one
9
+ // adapter at a time, and a top-level throw walks straight past it.
10
+ let Database = null;
11
+ function openDb(...a) {
12
+ if (!Database) Database = require('better-sqlite3');
13
+ return new Database(...a);
14
+ }
6
15
  const adapterErrors = require('./adapter-errors');
7
16
 
8
17
  // Zed stores data in different locations depending on the platform
@@ -69,7 +78,7 @@ function decompressZstd(buf) {
69
78
  function queryDb(sql) {
70
79
  if (!fs.existsSync(THREADS_DB)) return [];
71
80
  try {
72
- const db = new Database(THREADS_DB, { readonly: true });
81
+ const db = openDb(THREADS_DB, { readonly: true });
73
82
  const rows = db.prepare(sql).all();
74
83
  db.close();
75
84
  return rows;
@@ -91,7 +100,7 @@ function getThreadColumns() {
91
100
  if (threadColumns) return threadColumns;
92
101
  if (!fs.existsSync(THREADS_DB)) return new Set();
93
102
  try {
94
- const db = new Database(THREADS_DB, { readonly: true });
103
+ const db = openDb(THREADS_DB, { readonly: true });
95
104
  const cols = db.prepare('PRAGMA table_info(threads)').all().map((r) => r.name);
96
105
  db.close();
97
106
  // PRAGMA on a missing or renamed table SUCCEEDS with zero rows rather than
@@ -122,7 +131,7 @@ function resetCache() {
122
131
  function queryBlob(id) {
123
132
  if (!fs.existsSync(THREADS_DB)) return null;
124
133
  try {
125
- const db = new Database(THREADS_DB, { readonly: true });
134
+ const db = openDb(THREADS_DB, { readonly: true });
126
135
  const row = db.prepare('SELECT data FROM threads WHERE id = ?').get(id);
127
136
  db.close();
128
137
  return row ? row.data : null;
package/index.js CHANGED
@@ -121,10 +121,10 @@ if (isJoin) {
121
121
  // Fail fast: the relay join client still targets the removed synchronous SQLite
122
122
  // cache (relay-client.js pickProjects → cache.getDb), which the ClickHouse
123
123
  // cutover eliminated — it would crash after connecting. In memhouse, team
124
- // sharing is the kernel path (members + owner grants + RLS), not relay.
124
+ // sharing is grants on per-member rooms, not relay.
125
125
  console.error(chalk.red('\n ✗ Relay join is not supported in memhouse.'));
126
126
  console.error(chalk.dim(' Team sharing here is kernel-based: see mem-house/delivery/kernel-install.md'));
127
- console.error(chalk.dim(' (register-member + owner GRANT + own-only RLS).\n'));
127
+ console.error(chalk.dim(' (register-member + per-member rooms + a GRANT on your own rooms).\n'));
128
128
  process.exit(1);
129
129
  }
130
130
 
@@ -0,0 +1,217 @@
1
+ # mem-house — the competition
2
+
3
+ Companion to `DESIGN.md`. That file states **the four bets**; this one grades each
4
+ bet against the field as it actually stands.
5
+
6
+ **Surveyed 2026-08-07.** Every star/fork/date figure was pulled from the GitHub API
7
+ on that date and is a decaying fact — re-run before citing. Claims that could not be
8
+ verified are marked *unverified* rather than asserted. X/Twitter was not reachable
9
+ (HTTP 402 to the fetch tool), so nothing here reflects it.
10
+
11
+ The finding, in one line: **three of the four bets are already occupied by shipping
12
+ competitors, most of them free and further along. Bet 3 is the one that holds.**
13
+
14
+ ---
15
+
16
+ ## The field splits four ways
17
+
18
+ The space shares vocabulary but not architecture. Distinguishing the camps is the
19
+ whole analysis, because most "agent memory" products are not competitors at all.
20
+
21
+ | Camp | What it means | Where memory comes from | Competes with us? |
22
+ |---|---|---|---|
23
+ | **Transcript readers** | Index the JSONL/SQLite stores the agents already wrote | Passive — reads disk | **Yes, directly** |
24
+ | **Hook capturers** | Install lifecycle hooks, compress with an LLM, re-inject | Passive, but must live inside the agent | Yes, on the same users |
25
+ | **Memory SDKs** | Developer calls `add()` / `search()` | Active — the developer writes it | No — different product |
26
+ | **Observability** | OTel/proxy traces of model calls | Passive, at the API layer | Only on analytics |
27
+
28
+ mem-house is a transcript reader with the observability camp's analytics attached.
29
+ That combination is the most defensible thing about the product's *shape*; the
30
+ individual pieces are not scarce.
31
+
32
+ ---
33
+
34
+ ## The four bets, graded
35
+
36
+ ### Bet 1 — Parse on the client. **Occupied. Not differentiated.**
37
+
38
+ The reasoning in `DESIGN.md` is sound and the conclusion is correct — sqlite-backed
39
+ editors do force client-side parsing. But it is no longer a distinguishing choice.
40
+ At least five projects crack the same on-disk stores today, and one of them cracks
41
+ formats we do not touch (encrypted Protobuf sidecars for Antigravity CLI, Markdown
42
+ for aider).
43
+
44
+ Editor coverage — the number we lead the README with — is mid-pack:
45
+
46
+ | Project | Editors | Note |
47
+ |---|---:|---|
48
+ | AgentsView | **50+** | each with documented on-disk paths |
49
+ | lean-ctx | 30+ | claimed |
50
+ | CCHV | 28 | each with documented paths |
51
+ | **mem-house** | **17** | |
52
+ | deja-vu | 17 | different 17 — has Cline, aider, Qwen Code, Kimi Code, Roo Code |
53
+ | coding_agent_session_search | 11+ | |
54
+ | ctx | 8 | |
55
+ | Conare | 7 | |
56
+ | Contextify | 2 | Claude Code + Codex CLI only |
57
+
58
+ deja-vu's tagline is our pitch verbatim: *"it indexes the sessions your coding agents
59
+ already wrote to disk, months of history from before you installed it, across
60
+ seventeen harnesses."*
61
+
62
+ ### Bet 2 — Typed common schema. **Occupied on outcome, distinct on method.**
63
+
64
+ Everyone arrives at structured storage; the axis that still separates us is **when
65
+ the extraction happens and what survives it**.
66
+
67
+ - **SQLite + FTS5 is the category default**, reached independently by AgentsView,
68
+ Agent Sessions, claude-code-chat-explorer, claude-mem, ccrecall and mnemo.
69
+ - **ClickHouse for editor transcripts is unclaimed.** The only other ClickHouse-based
70
+ agent memory found is `clickmem` (57 stars) and it is embedded chDB, single-user,
71
+ so it inherits none of the multi-member properties. Langfuse is ClickHouse-backed,
72
+ MIT and self-hostable — but ingests API traces, not editor stores.
73
+ - **Verbatim-with-no-LLM-on-the-write-path has one serious co-traveller**:
74
+ MemPalace (58k stars in four months) sells exactly that — *"verbatim storage,
75
+ zero API calls."* We are not alone on this axis, but the company is good.
76
+
77
+ Honest caveat: at single-team scale SQLite demonstrably suffices. ClickHouse is a
78
+ bet on a scale we have not yet had to prove.
79
+
80
+ ### Bet 3 — Kernel-installable agency, own-only by absent grant. **This one holds.**
81
+
82
+ This is the finding worth acting on. Every competitor's multi-user story is a
83
+ **shared pool** or **metadata scoping**. Not one enforces isolation at the
84
+ datastore's own permission layer.
85
+
86
+ | Product | Team mechanism | Isolation |
87
+ |---|---|---|
88
+ | AgentsView | `pg push` / `pg serve` to shared PostgreSQL | **None.** Connection string is the whole boundary; `machine_name` is a label, not a fence |
89
+ | memsearch (Zilliz) | Docker + shared Milvus, documented "for team environments" | **None documented** — no RBAC, no per-user namespacing |
90
+ | mem0 | `user_id` + `agent_id` + `run_id` filters | Metadata filtering, not enforcement |
91
+ | Industry pattern | `tenant_id` column + row-level security | Policy layer, revocable by whoever owns the policy |
92
+ | Contextify | Cloud workspace, per-project opt-in | Real, but proprietary, cloud-first, 2 editors |
93
+ | **mem-house** | three `GRANT SELECT` on the member's own rooms | **A member cannot name another member's rooms.** `WITH GRANT OPTION` on `SELECT` and not on `ALL` makes sharing read-only by construction and operator-free |
94
+
95
+ Bet 3 is not a niche preference. It is a category-wide blind spot, and it is the
96
+ only thing in this document that nobody else is attempting.
97
+
98
+ ### Bet 4 — Borrowed UI, zero fork. **Occupied. Not differentiated.**
99
+
100
+ Analytics over the same files is a crowded, mature niche: ccusage (17.8k stars),
101
+ CodexBar (19.8k). Agent Sessions beats us on the analytics *idea* with a per-session
102
+ "Quota Meter / Session Runway" priced per model against 5-hour and weekly windows.
103
+ AgentsView ships cache-aware, LiteLLM-priced cost reporting plus a REST usage API.
104
+
105
+ Zero-fork was the right call for build cost. It buys no position.
106
+
107
+ ---
108
+
109
+ ## The field, ranked by how directly it competes
110
+
111
+ | # | Project | What it is | Source | Store | Team | Editors | Licence | Signal (2026-08-07) |
112
+ |---|---|---|---|---|---|---:|---|---|
113
+ | 1 | **AgentsView** | Go binary + Tauri app + Docker image | reads disk | SQLite/FTS5, opt. DuckDB, opt. **PostgreSQL** | shared pool, free | 50+ | MIT | **4,723★**, 574 forks, 100 contributors, six months old, MCP server, semantic search |
114
+ | 2 | **claude-mem** | npm plugin + MCP + web viewer | **hooks** | SQLite + Chroma, opt. cloud | none | ~8 | Apache-2.0 | **89,967★**, 7,831 forks, **62,940 npm dl/mo**. The category's centre of gravity |
115
+ | 3 | **MemPalace** | local-first memory, verbatim | mixed, mining transcripts | pluggable | none | — | MIT | **58,182★ in four months**, 696 open issues, contested benchmarks |
116
+ | 4 | **deja-vu** | zero-dep Go binary, CLI + MCP + hook | reads disk | `records.bin`, no server | none — personal SSH sync | 17 | MIT | 593★, HN 131 pts, redacts secrets at index |
117
+ | 5 | **Contextify** | macOS app + CLI, cloud **and** self-hosted | reads disk | local + optional cloud | **real**: seats, roles, per-project opt-in | 2 | proprietary / FSL | $8–15/seat/mo; self-host $132/seat/yr |
118
+ | 6 | **ctx** | Rust CLI, event-level provenance | reads disk | local SQLite | **ctx cloud**, hosted, private beta | 8 | Apache-2.0 | 1,013★ |
119
+ | 7 | **SpecStory** | IDE extensions + CLI | **writes its own** | local + cloud | team rollups | 9 | Apache-2.0 | 1,296★; **Team $300/mo**; "Lore" mines history into installable skills |
120
+ | 8 | **Conare** | CLI + hosted, MCP | reads disk | vendor cloud | shared memory, audit logs, SSO | 7 | proprietary | **Team $500/mo** |
121
+ | 9 | **memsearch** | Markdown + Milvus shadow index | Stop hooks | Markdown truth + Milvus | Docker, no isolation | 4 | MIT | 2,431★, Zilliz-backed |
122
+ | 10 | **CCHV** | Tauri app + **headless server** | reads disk | provider files + cache | server mode w/ accounts | 28 | MIT | 2,020★, 45 contributors |
123
+ | 11 | **Agent Sessions** | native macOS app | reads disk | SQLite/FTS5 | none | 10 | MIT | 762★; best-in-class quota metering; resume + write-back restore |
124
+ | 12 | **ContextPool** | CLI + MCP | reads disk | local + repo dir | shared pool, $7.99/mo | 4 | mixed | 188 PH upvotes |
125
+
126
+ Adjacent and **not** competitors, but they own the vocabulary and the capital:
127
+ mem0 (62.8k★, $24M), cognee (29.8k★), graphiti (29.6k★), supermemory (28.8k★),
128
+ letta (24.1k★, $10M seed), memori (15.7k★), OpenViking (28.1k★, ByteDance),
129
+ Hindsight (19.2k★, $3.6M seed). All of them require the developer to write the
130
+ memory. None ingest agent sessions.
131
+
132
+ ---
133
+
134
+ ## What they do better
135
+
136
+ - **Distribution.** mem-house: 0 stars, 655 npm downloads/month, no HN or search
137
+ presence. claude-mem: 89,967 stars and 62,940 downloads/month, free, no company.
138
+ This dominates every other consideration in the document.
139
+ - **Automatic recall.** deja-vu installs a SessionStart hook; claude-mem injects into
140
+ future sessions; ContextPool loads at session start; SpecStory converts history
141
+ into *installed skills*. Our skills must be chosen. One independent analysis of 270
142
+ real sessions found **only 4.8% involved an explicit history query** — a pull-only
143
+ model gets invoked rarely.
144
+ - **Install weight — we are the heaviest in the category.** AgentsView, deja-vu and
145
+ ctx are single dependency-free binaries via Homebrew, Docker, `go install` or curl.
146
+ We are Node, need `--allow-scripts=better-sqlite3` (five adapters silently read
147
+ nothing without it), and additionally need a ClickHouse to point at. Our README
148
+ spends paragraphs on `EACCES`/`sudo` triage. That is a real adoption tax.
149
+ - **Semantic search.** AgentsView (opt-in index), claude-mem (Chroma),
150
+ claude-history (local embedding model). We are FTS-only. Contested rather than
151
+ settled — deja-vu made the same choice and was criticised for it on HN.
152
+ - **Secret redaction at ingest.** deja-vu redacts credential patterns on index *and*
153
+ on sync export. Whether we do is *unverified* — and it matters more here than
154
+ elsewhere, because Claude Code caches unredacted secrets in transcripts
155
+ (anthropics/claude-code#43675) and we ship those rows to a shared server.
156
+ - **Resume and write-back.** Agent Sessions copies resume commands for six CLIs and
157
+ restores into the archive; claude-history resumes *and forks* worktree-aware. We
158
+ read but do not hand the pilot back in.
159
+ - **Price ceiling.** AgentsView gives the whole stack away under MIT. That caps what
160
+ a self-hosted OSS competitor can charge for.
161
+
162
+ ---
163
+
164
+ ## Unclaimed ground
165
+
166
+ Two positions nobody occupies, both reachable from where we already are.
167
+
168
+ **1. Evidence and claims as two layers.** The sharpest architectural critique found
169
+ anywhere in the survey, and no product in the field claims to do it: keep the capture
170
+ log and the memory layer distinct. The capture log is chronological evidence; memory
171
+ is a set of derived, revisable claims. Each claim cites the exact spans supporting
172
+ it, records when it was inferred, and marks itself current, disputed or superseded —
173
+ and deletion follows that lineage. This is available **only** to a system that keeps
174
+ verbatim transcripts with provenance. Every extract-only competitor has discarded the
175
+ evidence by the time the claim is written. We and MemPalace are the two systems
176
+ positioned to build it. Neither has.
177
+
178
+ **2. The 30-day deletion window.** Claude Code deletes JSONL transcripts after 30
179
+ days. Contextify was built because of it; vibe-replay documents it; **nobody markets
180
+ on it.** A durable server-side store is the strongest possible answer, and we are
181
+ better positioned than any local-index tool. This is free ground.
182
+
183
+ ---
184
+
185
+ ## Three cautions
186
+
187
+ **Do not compete on benchmark scores.** The benchmarks are broken. An independent
188
+ LOCOMO audit found 99 score-corrupting errors in 1,540 questions (6.4%), and the
189
+ standard LLM judge accepted 62.81% of adversarially-generated wrong-but-topical
190
+ answers. Nine vendors claim mutually incompatible SOTA on it. MemPalace's claimed
191
+ 100% was reverse-engineered publicly to 98.4%, achieved with `top-k=50` — which
192
+ exceeds the session count. deja-vu's 84.9% hit@1 carries the same asterisk. Entering
193
+ this contest means measuring the field against itself.
194
+
195
+ **Stars are not traction, and are farmed in this category.** `mindverse/Second-Me`
196
+ holds 15,648 stars and has been dead for ten months. MemPalace took 58k stars in four
197
+ months while accumulating 2,183 issues, one titled *"in short: This sucks shit."*
198
+ Screenpipe reportedly mails its stargazers.
199
+
200
+ **The free incumbent sets the floor.** claude-mem is Apache-2.0, free, no cloud tier,
201
+ no company, one dominant author. Any paid or heavier-install offering is measured
202
+ against that baseline, not against a vendor's price list.
203
+
204
+ ---
205
+
206
+ ## Net position
207
+
208
+ Not differentiated on parse-on-client, editor breadth, self-hosting, analytics, or
209
+ agent-facing skills — all matched or beaten, most comprehensively by AgentsView
210
+ (MIT, free, 50+ editors, 4,723 stars, six months old).
211
+
212
+ Two things hold: **grant-enforced per-member isolation**, which nobody attempts, and
213
+ **verbatim transcripts with provenance**, which is the prerequisite for the
214
+ evidence/claims separation the field has identified but not built.
215
+
216
+ The liabilities are zero distribution and the heaviest install in the category.
217
+ Both are fixable; neither is fixed by adding another editor adapter.
@@ -2,11 +2,16 @@
2
2
 
3
3
  **mem-house** is agent conversation memory as a product: every coding-agent session
4
4
  on your machines — across **all 17 editors** agentlytics supports — parsed locally,
5
- shipped to one typed ClickHouse store, shareable with a team, installable on the
5
+ shipped to a typed ClickHouse store, shareable with a team, installable on the
6
6
  ultimagent kernel as an **agency**, and visible through the agentlytics dashboard.
7
7
 
8
+ **One house, a set of rooms per member** — `sessions_<m>`, `messages_<m>`,
9
+ `tool_calls_<m>`. All of one member's machines ship into that member's rooms, told
10
+ apart by the `host` column; no member writes into another's. The shared-table layout
11
+ this once had was removed in 0.4.0; see `per-member/`.
12
+
8
13
  **Agency, precisely.** In constellation terms (`../TERMINOLOGY.md`) mem-house is a
9
- **house** — the `memhouse` database — **plus a resident**: the shipper. That pairing
14
+ **house** — the `mem` database — **plus a resident**: the shipper. That pairing
10
15
  is what the word *agency* means; a house on its own only holds.
11
16
 
12
17
  The test is mechanical — **residents write, routines read**. The shipper fires on a
@@ -18,27 +23,34 @@ kind = "worker" # deterministic code, no LLM
18
23
  on = "loop" # a daemon — not an insert trigger, not a schedule
19
24
  ```
20
25
 
21
- `sessions_v` is a plain view: computed during your query, for your query, writing
22
- nothing. It is a **routine** — house machinery, not a second resident. mem-house has
23
- **no materialized views at all**, so the shipper is not merely a resident, it is the
24
- only candidate in the tree. Strip it and every remaining moving part is a routine
25
- over rows nobody is writing any more.
26
+ The session rollup is a **routine** — house machinery, not a second resident. Since
27
+ 0.4.0 it is not even an object: it is SQL text (`per-member/rooms.js`), substituted with
28
+ the caller's own room names and computed during your query, for your query, writing
29
+ nothing. mem-house has **no materialized views at all** — in fact no stored views at all
30
+ — so the shipper is not merely a resident, it is the only candidate in the tree. Strip
31
+ it and every remaining moving part is a routine over rows nobody is writing any more.
26
32
 
27
33
  Note that `user_id MATERIALIZED currentUser()` is a materialized *column*: it computes
28
- during your own insert and is part of the table's definition. It makes own-only RLS
29
- work and it records who wrote each row, but it is an identity property, not what makes
30
- this an agency.
34
+ during your own insert and is part of the table's definition. It is not what isolates
35
+ members — the room they hold no grant on does that — but it records who wrote each row,
36
+ which keeps provenance across a share and keeps the `Merge` rooms meaningful. An identity
37
+ property, not what makes this an agency.
31
38
 
32
39
  **Positioning:** an alternative agency **competing with memory-house**. It borrows
33
- memory-house's proven ideas (server-stamped identity, own-only RLS, idempotent
34
- shipping, skills/plugin delivery) and agentlytics' proven assets (adapters, UI,
35
- cost engine). If it wins, it can become memory-house v4; until then the two run
36
- side by side as separate agencies, each in its own house. Naming: the product is
37
- **mem-house**; ClickHouse identifiers use **`memhouse`** (agency/house name, owner
38
- `memhouse_root`) since identifiers can't carry a dash.
40
+ memory-house's proven ideas (server-stamped identity, idempotent shipping,
41
+ skills/plugin delivery) and agentlytics' proven assets (adapters, UI, cost engine) —
42
+ but **not** its shared-table-plus-row-policy layout, which 0.4.0 removed in favour of
43
+ one set of rooms per member. If it wins, it can become memory-house v4; until then the
44
+ two run side by side as separate agencies, each in its own house. Naming: the product is
45
+ **mem-house**; the agency is **`memhouse`** and its house is the **`mem`** database
46
+ (identifiers can't carry a dash; the owner kept the longer name, `memhouse_root`).
39
47
 
40
48
  ## The four bets
41
49
 
50
+ `COMPETITION.md` grades each of these against the field as surveyed 2026-08-07.
51
+ Short version: bets 1, 2 and 4 are occupied by shipping competitors, several of
52
+ them free and further along; **bet 3 is the one nobody else attempts.**
53
+
42
54
  1. **Parse on the client.** memory-house ships raw transcript lines and parses in
43
55
  views — workable only because its 4 agents store sessions as JSON/JSONL files.
44
56
  agentlytics' breadth includes sqlite-backed editors (Cursor, VS Code, Zed,
@@ -48,19 +60,20 @@ side by side as separate agencies, each in its own house. Naming: the product is
48
60
  write axis this bet is precisely *moving work from routine to resident*:
49
61
  memory-house parses when you query, mem-house parses before anyone asks and
50
62
  writes the result down.
51
- 2. **Typed common schema** (`schema.sql`). Physical typed columns (what
52
- memory-house derives in views) + `tool_calls` (memory-house has none) + one
53
- `extra JSON` escape hatch per table so unnormalized adapter fields are never
54
- lost. `ReplacingMergeTree(ingested_at)`; `messages` keyed `(session_id, seq)` so
55
- a re-ship of a grown/changed session replaces stale rows (latest-wins). FTS text
56
- indexes built in (CH ≥ 26.2).
63
+ 2. **Typed common schema** (`per-member/schema-member.sql.tpl`, applied once per
64
+ member; `schema-merge.sql.tpl` for the team rooms). Physical typed columns (what
65
+ memory-house derives in views) + `tool_calls_<m>` (memory-house has no tool table)
66
+ + one `extra JSON` escape hatch per room so unnormalized adapter fields are never
67
+ lost. `ReplacingMergeTree(ingested_at)`; `messages_<m>` keyed
68
+ `(session_id, user_id, seq)` so a re-ship of a grown/changed session replaces stale
69
+ rows (latest-wins). FTS text indexes built in (CH ≥ 26.2).
57
70
  3. **Kernel-installable agency.** Same install path proven for agentlytics-agency:
58
71
  `install-agency{memhouse}` → house + `memhouse_root` + credential; members via
59
- `register-member` + owner `GRANT`; own-only visibility via the row policy
60
- (`rls.sql`) applied by the ego (the `kernel` user) or the mayor. The shipper is
72
+ `register-member` + owner `GRANT` on that member's own rooms. Own-only visibility
73
+ needs no policy: a member is granted their rooms and nobody else's. The shipper is
61
74
  the resident that lands with it. Identity is `user_id MATERIALIZED currentUser()`
62
- (requires `async_insert=0`). Sharing modes: own-only (policy TO member) or
63
- team-pool (no policy).
75
+ (requires `async_insert=0`). Sharing is a further GRANT, issued by the member
76
+ themselves (grant-option) or the owner.
64
77
  4. **Borrowed UI, zero fork.** The React SPA consumes REST JSON, not tables. The
65
78
  mem-house server implements the **same `/api/*` contract** as agentlytics'
66
79
  `server.js` (same routes, same response shapes) over the mem-house schema, and
@@ -70,11 +83,13 @@ side by side as separate agencies, each in its own house. Naming: the product is
70
83
 
71
84
  | Path | What | Notes |
72
85
  |---|---|---|
73
- | `schema.sql` | the house schema (typed) | unqualified names; owner applies in its house |
74
- | `rls.sql` | own-only row policies | the ego (`kernel`) or the mayor applies (owner lacks ACCESS MANAGEMENT) |
86
+ | `per-member/rooms.js` | room-name resolution + the session rollup | one naming rule for both transports |
87
+ | `per-member/schema-member.sql.tpl` | one member's three rooms (typed) | the owner via `provision.js`, or the member on a house they own |
88
+ | `per-member/schema-merge.sql.tpl` | the three team Merge rooms | owner-managed; reduce to the caller's grants |
75
89
  | `shipper/ship.js` | parse-on-client shipper CLI — the resident (`worker`) | reuses `../../editors`; incremental; idempotent |
76
90
  | `server/server.js` | REST API + dashboard | same API contract as agentlytics; serves `../../public` |
77
91
  | `delivery/` | delivery kit | installer, skills, plugin, AGENT-INSTALL.md, prompt |
92
+ | `COMPETITION.md` | the competitive landscape, graded against the four bets | surveyed 2026-08-07; figures decay, re-run before citing |
78
93
 
79
94
  ## Environment contract
80
95
 
@@ -83,7 +98,7 @@ side by side as separate agencies, each in its own house. Naming: the product is
83
98
  | `MEMHOUSE_URL` | `http://localhost:8123` | ClickHouse HTTP(S) endpoint |
84
99
  | `MEMHOUSE_USER` | `memhouse_root` | CH user (owner, or a member's own credential) |
85
100
  | `MEMHOUSE_PASSWORD` | *(empty)* | credential |
86
- | `MEMHOUSE_DB` | `memhouse` | the house |
101
+ | `MEMHOUSE_DB` | `mem` | the house |
87
102
  | `MEMHOUSE_PORT` | `4640` | dashboard/API port |
88
103
 
89
104
  Config file: `~/.memhouse/config.json` (`hiddenProjects`, future prefs).
@@ -102,6 +117,15 @@ Config file: `~/.memhouse/config.json` (`hiddenProjects`, future prefs).
102
117
  - Incremental shipping: read existing `(session_id, last_updated_at, message_count)`
103
118
  (FINAL) and skip chats that haven't grown/changed; a full re-ship must remain
104
119
  safe (ReplacingMergeTree collapses).
120
+ - Re-shipping a known session **deletes its message/tool rows first**: `ReplacingMergeTree`
121
+ collapses same-key rows but cannot remove a row the new parse no longer produces, so a
122
+ shrunken re-parse would leave a stale `seq` tail forever.
123
+ - **A mutation predicate must bind the user, never call `currentUser()`.** A mutation does
124
+ not evaluate it in the caller's context: measured on ClickHouse 25.11 and chdb, the same
125
+ `DELETE` matched 0 rows with `user_id = currentUser()` and 2000 with the value bound —
126
+ silently, both times. Read the identity once (`SELECT currentUser()`) and pass it as a
127
+ query parameter. This applies to `DELETE`/`ALTER … UPDATE`, not to `SELECT`, where
128
+ `currentUser()` behaves as expected.
105
129
  - Costs are computed in the server from `pricing.js` (repo root) over per-model
106
130
  token sums; `<synthetic>` and empty models excluded from model lists.
107
131
 
@@ -114,6 +138,11 @@ to use mem-house as memory), `kernel-install.md` (provision as agency `memhouse`
114
138
 
115
139
  ## Non-goals (v0)
116
140
 
117
- Relay/team-server (the kernel + RLS already covers sharing), launchd daemonization
118
- (ship `--loop` instead), migration tooling from memory-house's `memory` db, and
119
- per-adapter true message timestamps.
141
+ Relay/team-server (per-room grants plus the `Merge` rooms already cover sharing),
142
+ migration tooling from memory-house's `memory` db, and per-adapter true message
143
+ timestamps.
144
+
145
+ OS-level daemonization **was** a non-goal — "ship `--loop` instead" — and is no longer:
146
+ `memhouse service install` writes a systemd `--user` unit or a launchd LaunchAgent. The
147
+ reason the non-goal did not survive is that `--loop` outlives the shell and nothing else,
148
+ so a machine that reboots stops shipping until somebody notices.
@@ -0,0 +1,58 @@
1
+ # memhouse — agent install prompt (template)
2
+
3
+ `memhouse prompt --install` renders this with THIS machine's facts substituted and
4
+ prints it. Feed the output to a coding agent; it has everything needed to install and
5
+ onboard without asking the user to look anything up.
6
+
7
+ The point of rendering rather than shipping a static runbook: the CLI has already
8
+ probed the machine, so the prompt states the route that applies instead of making the
9
+ agent reason through a decision tree and possibly pick wrong. `AGENT-INSTALL.md` is the
10
+ static long-form version, and it predates `deploy --local`.
11
+
12
+ Placeholders are `{{NAME}}`. Everything below the marker is the prompt body.
13
+
14
+ <!-- PROMPT BODY BELOW -->
15
+ You are installing **memhouse** for the user on this machine.
16
+
17
+ memhouse gives a coding agent long-term memory of past sessions. It reads the session
18
+ transcripts your editors already write to disk — 17 of them — parses them locally, and
19
+ ships typed rows into ClickHouse. Nothing is intercepted or proxied; it reads files that
20
+ already exist.
21
+
22
+ ## What this machine looks like right now
23
+
24
+ - memhouse version: **{{VERSION}}**
25
+ - platform: {{PLATFORM}}, node {{NODE}}
26
+ - current state: **{{STATE}}**
27
+ - editors with sessions found: {{EDITORS}}
28
+ - reachable ClickHouse: {{HOUSE}}
29
+ - container engines available: {{ENGINES}}
30
+
31
+ ## Your task
32
+
33
+ {{PLAN}}
34
+
35
+ ## Rules that matter
36
+
37
+ - **Never invent a credential.** If a step needs a password for a house the user already
38
+ runs, ask them for it. Do not guess, do not read it out of a file they did not name.
39
+ - **Do not run `deploy --down`.** It destroys the container *and its volume*. It is never
40
+ part of an install.
41
+ - The install writes `~/.memhouse/env` at mode 0600. Do not print its contents; do not
42
+ echo a password into the transcript you are writing, which memhouse will then ship.
43
+ - If a command fails, read what it printed before retrying. memhouse's failures name the
44
+ cause and usually name the fix — it is not a generic tool that fails generically.
45
+ - Rooms are named for the ClickHouse user: `sessions_<them>`, `messages_<them>`,
46
+ `tool_calls_<them>`. There are no unsuffixed tables. If you write a query, suffix it.
47
+
48
+ ## When you are done
49
+
50
+ Run `memhouse doctor` and report its output to the user. Every line should be a check
51
+ mark. Then tell them:
52
+
53
+ - where the dashboard is (`http://localhost:{{PORT}}`) and that `memhouse start` runs it
54
+ - that `memhouse search <terms>` searches every past session from every editor
55
+ - that you can now search their history yourself, and offer to try one query
56
+
57
+ If `doctor` reports a failure, say which line failed and what memhouse suggested. Do not
58
+ declare success on a partial install.