memhouse 0.18.12 → 0.20.1

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 (169) hide show
  1. package/AGENTS.md +22 -151
  2. package/CHANGELOG.md +263 -29
  3. package/LICENSE +202 -22
  4. package/LICENSES/agentlytics-MIT.txt +21 -0
  5. package/LICENSES/memhouse-MIT-until-0.18.11.txt +22 -0
  6. package/LICENSES/models.dev-MIT.txt +22 -0
  7. package/NOTICE +28 -0
  8. package/README.md +42 -20
  9. package/SECURITY.md +21 -8
  10. package/bin/memhouse.js +2223 -311
  11. package/docs/README.md +11 -9
  12. package/docs/guides/admin-password.md +37 -10
  13. package/docs/guides/configuration.md +13 -6
  14. package/docs/guides/daemons.md +64 -3
  15. package/docs/guides/import-claude-ai.md +204 -0
  16. package/docs/guides/install.md +11 -6
  17. package/docs/guides/invite.md +21 -7
  18. package/docs/guides/operate.md +114 -7
  19. package/docs/guides/search-and-resume.md +72 -3
  20. package/docs/guides/share.md +91 -52
  21. package/docs/guides/storage.md +44 -4
  22. package/docs/guides/troubleshooting.md +121 -14
  23. package/docs/guides/uninstall.md +4 -3
  24. package/docs/guides/update.md +16 -0
  25. package/docs/guides/upgrade-to-0.20.md +188 -0
  26. package/docs/releases/README.md +2 -1
  27. package/docs/releases/v0.18.10.md +5 -5
  28. package/docs/releases/v0.18.11.md +3 -3
  29. package/docs/releases/v0.20.0.md +627 -0
  30. package/docs/releases/v0.20.1.md +114 -0
  31. package/docs/tutorials/01-first-house.md +10 -7
  32. package/docs/tutorials/02-join-a-house.md +24 -15
  33. package/docs/tutorials/03-share-your-memory.md +40 -31
  34. package/editors/antigravity.js +1 -80
  35. package/editors/base.js +463 -9
  36. package/editors/bridge.js +119 -0
  37. package/editors/claude.js +273 -105
  38. package/editors/codex.js +278 -52
  39. package/editors/copilot.js +1 -70
  40. package/editors/cursor.js +1 -75
  41. package/editors/derive-fingerprints.json +18 -0
  42. package/editors/harness-paths.js +69 -0
  43. package/editors/index.js +1 -20
  44. package/editors/normalise/agy.js +910 -0
  45. package/editors/normalise/copilot-cli.js +321 -0
  46. package/editors/normalise/cursor-agent.js +852 -0
  47. package/editors/normalise/cursor.js +474 -0
  48. package/editors/normalise/devin.js +575 -0
  49. package/editors/normalise/gemini-cli.js +602 -0
  50. package/editors/normalise/goose.js +673 -0
  51. package/editors/normalise/opencode.js +633 -0
  52. package/editors/normalise/vscode.js +549 -0
  53. package/editors/normalise/zed.js +473 -0
  54. package/editors/opencode-v4.js +119 -0
  55. package/editors/scope.js +14 -3
  56. package/editors/sessions-log.js +349 -0
  57. package/editors/sqlite.js +28 -1
  58. package/editors/vscode.js +1 -72
  59. package/editors/windsurf.js +1 -143
  60. package/examples/01-local-one-machine/README.md +24 -19
  61. package/examples/01-local-one-machine/run.sh +1 -1
  62. package/examples/02-existing-clickhouse/README.md +30 -22
  63. package/examples/02-existing-clickhouse/run.sh +5 -4
  64. package/examples/03-team-house/README.md +44 -37
  65. package/examples/03-team-house/run.sh +35 -18
  66. package/examples/04-self-hosted-team/README.md +29 -14
  67. package/examples/04-self-hosted-team/compose.yml +5 -2
  68. package/examples/04-self-hosted-team/smoke.sh +26 -12
  69. package/examples/README.md +8 -4
  70. package/examples/fixtures/README.md +4 -1
  71. package/examples/fixtures/make-sessions.js +8 -5
  72. package/memhouse/admin-secret.js +89 -24
  73. package/memhouse/argv-secrets.js +64 -0
  74. package/memhouse/channel.js +49 -9
  75. package/memhouse/codex-plugin.js +194 -0
  76. package/memhouse/delivery/AGENT-INSTALL-PROMPT.md +2 -1
  77. package/memhouse/delivery/AGENT-INSTALL.md +22 -11
  78. package/memhouse/delivery/INVITATION.md.tpl +44 -45
  79. package/memhouse/delivery/PROMPT.md +53 -5
  80. package/memhouse/delivery/UPGRADE.md.tpl +1 -1
  81. package/memhouse/delivery/codex/AGENTS-BLOCK.md +11 -0
  82. package/memhouse/delivery/codex/SKILL.md +50 -0
  83. package/memhouse/delivery/kernel-install.md +17 -9
  84. package/memhouse/delivery/plugin/.claude-plugin/plugin.json +2 -2
  85. package/memhouse/delivery/plugin/reference/HOUSE.md +168 -41
  86. package/memhouse/delivery/plugin/skills/access/SKILL.md +13 -5
  87. package/memhouse/delivery/plugin/skills/admin/SKILL.md +14 -6
  88. package/memhouse/delivery/plugin/skills/house/SKILL.md +25 -8
  89. package/memhouse/delivery/plugin/skills/recall/SKILL.md +66 -8
  90. package/memhouse/delivery/plugin/skills/sql/SKILL.md +66 -7
  91. package/memhouse/deploy.js +1 -1
  92. package/memhouse/flags.js +35 -18
  93. package/memhouse/help.js +37 -0
  94. package/memhouse/host.js +82 -2
  95. package/memhouse/house/HOUSE.md +2 -2
  96. package/memhouse/house/convert-profile.js +638 -0
  97. package/memhouse/house/convert-v3.js +742 -0
  98. package/memhouse/house/doctor-v3.js +324 -0
  99. package/memhouse/house/host-alias.js +85 -0
  100. package/memhouse/house/house.js +3 -3
  101. package/memhouse/house/migrate.js +2 -2
  102. package/memhouse/house/profile-dedupe.js +183 -0
  103. package/memhouse/house/profile-edges.js +554 -0
  104. package/memhouse/house/profile-house.js +430 -0
  105. package/memhouse/house/profile-rollup.js +190 -0
  106. package/memhouse/house/q-cli.js +863 -0
  107. package/memhouse/house/queries-v3.js +1013 -0
  108. package/memhouse/house/reconcile-v3.js +192 -0
  109. package/memhouse/house/records-to-profile.js +174 -0
  110. package/memhouse/house/records-to-rows.js +169 -0
  111. package/memhouse/house/relocate.js +2 -1
  112. package/memhouse/house/schema-choice.js +121 -0
  113. package/memhouse/house/schema-profile.js +290 -0
  114. package/memhouse/house/schema-profile.sql.tpl +398 -0
  115. package/memhouse/house/schema-v3.js +267 -0
  116. package/memhouse/house/schema-v3.sql.tpl +213 -0
  117. package/memhouse/import/claude-ai.js +420 -0
  118. package/memhouse/import/import.js +372 -0
  119. package/memhouse/import/pyjson.js +206 -0
  120. package/memhouse/import/records.js +64 -0
  121. package/memhouse/import/writer-profile.js +192 -0
  122. package/memhouse/import/writer-v3.js +240 -0
  123. package/memhouse/import/zip.js +112 -0
  124. package/memhouse/invitation.js +102 -0
  125. package/memhouse/invitefile.js +5 -2
  126. package/memhouse/plugin-copies.js +176 -0
  127. package/memhouse/provision.js +7 -2
  128. package/memhouse/release-hint.js +24 -0
  129. package/memhouse/resume.js +4 -4
  130. package/memhouse/server/api-v3.js +632 -0
  131. package/memhouse/server/dashboard-auth.js +181 -0
  132. package/memhouse/server/dashboard-port.js +91 -0
  133. package/memhouse/server/queries.js +30 -0
  134. package/memhouse/server/server.js +118 -20
  135. package/memhouse/service.js +1 -1
  136. package/memhouse/share.js +687 -1
  137. package/memhouse/shipper/adapter-capture.js +756 -0
  138. package/memhouse/shipper/capture.js +2571 -0
  139. package/memhouse/shipper/dedupe-profile.js +237 -0
  140. package/memhouse/shipper/dedupe-v3.js +1027 -0
  141. package/memhouse/shipper/forks.js +233 -0
  142. package/memhouse/shipper/log-adapter.js +219 -0
  143. package/memhouse/shipper/maintenance.js +49 -0
  144. package/memhouse/shipper/normalise-run.js +148 -0
  145. package/memhouse/shipper/passlog.js +68 -5
  146. package/memhouse/shipper/profile-capture.js +903 -0
  147. package/memhouse/shipper/ship.js +91 -8
  148. package/memhouse/shipper/snapshots.js +193 -0
  149. package/memhouse/shipper/source-roots.js +111 -0
  150. package/memhouse/version-notice.js +66 -0
  151. package/package.json +14 -7
  152. package/public/THIRD_PARTY_NOTICES.txt +2 -2
  153. package/public/assets/index-CcNnhfM3.js +78 -0
  154. package/public/index.html +1 -1
  155. package/ui/package-lock.json +56 -37
  156. package/ui/package.json +1 -0
  157. package/ui/src/App.jsx +29 -14
  158. package/ui/src/components/ChatSidebar.jsx +69 -12
  159. package/ui/src/lib/api.js +76 -0
  160. package/ui/src/lib/dashboard-token.js +41 -0
  161. package/ui/src/main.jsx +22 -5
  162. package/ui/src/pages/Dashboard.jsx +22 -36
  163. package/ui/third-party-notices.js +119 -0
  164. package/ui/vite.config.js +5 -1
  165. package/docs/releases/v0.18.12.md +0 -59
  166. package/memhouse/COMPETITION.md +0 -221
  167. package/memhouse/DESIGN.md +0 -222
  168. package/memhouse/RESUME-AND-UPDATE.md +0 -182
  169. package/public/assets/index-CnWzrjF3.js +0 -78
@@ -35,9 +35,12 @@ as a failure.
35
35
  member's. This happens after a `passwd` on another machine, or a reset by the admin. Put
36
36
  the current password into `MEMHOUSE_PASSWORD` in `~/.memhouse/env` (or run
37
37
  `memhouse setup`, which asks), then restart the shipper.
38
- - **A pass that keeps withholding a session:** an adapter returned nothing for a session
39
- the house already holds. The shipper refuses to replace a transcript with an empty one,
40
- and says so on every pass, until the store is readable again.
38
+ - **A pass that keeps withholding a session (0.18 rooms):** an adapter returned nothing for a
39
+ session the house already holds. The shipper refuses to replace a transcript with an empty
40
+ one, and says so on every pass, until the store is readable again.
41
+ - **`vanish refused` (schema 3):** a store's list left sessions out, and memhouse would not
42
+ record them as gone without the editor's word. See
43
+ [Reading `memhouse doctor` on schema 3](#reading-memhouse-doctor-on-schema-3).
41
44
  - **Nothing to ship:** sessions are only shipped once the editor has written them.
42
45
 
43
46
  ## Daemons don't survive a reboot
@@ -47,15 +50,23 @@ as a failure.
47
50
  user services stop at logout. A rootless podman house from `deploy --local` has the same
48
51
  exposure, and `deploy` prints the fix.
49
52
 
53
+ ## The dashboard says to open it with `memhouse dashboard`
54
+
55
+ The page has no token for this tab: it was opened by typing its address, in a new tab, after the
56
+ browser restarted, or the token was replaced. Run `memhouse dashboard`. If the dashboard runs
57
+ under another `MEMHOUSE_HOME`, run it with that home set. If it says "port N is not your
58
+ dashboard", something else answers on that port: restart yours (`memhouse stop && memhouse
59
+ start`), which moves it to a free port.
60
+
50
61
  ## The dashboard is empty, or stale
51
62
 
52
63
  - **It reads the house, not the machine.** Check `memhouse status` for the connection
53
64
  first.
54
- - **It reads precomputed stats,** refreshed at the end of each pass that shipped
55
- something. On a house where nothing has shipped for a day, the stats age out, and a
56
- running dashboard keeps reading them. Restart it (`memhouse stop && memhouse start`). It
57
- then reads the rooms directly, until the next pass that ships refreshes the stats.
58
- A fix is planned.
65
+ - **On schema 3 it reads the `sessions` rows directly,** so it is as current as the last pass.
66
+ - **On the 0.18 rooms it reads precomputed stats,** refreshed at the end of each pass that
67
+ shipped something. On a house where nothing has shipped for a day, the stats age out, and a
68
+ running dashboard keeps reading them. Restart it (`memhouse stop && memhouse start`). It then
69
+ reads the rooms directly, until the next pass that ships refreshes the stats.
59
70
  - **After an upgrade it shows the old version:** restart it (`memhouse update` does this).
60
71
 
61
72
  ## `EACCES` during `npm install -g`
@@ -65,14 +76,110 @@ owned by root.
65
76
 
66
77
  ## `refused`, `ACCESS_DENIED`, `Not enough privileges`
67
78
 
68
- - For a member, these are expected outside `mem.<you>_*` and the rooms shared with you.
69
- `memhouse whoami` shows what your credential may do.
79
+ - For a member, these are expected outside `mem.<you>_*`, your two capture tables in
80
+ `mem_archive`, and the tables shared with you. `memhouse whoami` shows what your credential
81
+ may do.
70
82
  - For `invite`, `members` or `convert`: you need the admin credential. See
71
83
  [Admin password](admin-password.md).
72
- - A room that should be yours but is refused usually means the house is older than this
73
- version. Run `memhouse doctor`, then `memhouse migrate` if it says so.
84
+ - A table that should be yours but is refused usually means the house is older than this
85
+ version. Run `memhouse doctor`, and do what it says: on the 0.18 rooms, `memhouse migrate`;
86
+ on schema 3, a missing capture table is created by an admin (the doctor line names the
87
+ command).
88
+
89
+ ## A schema-3 refusal (0.20)
90
+
91
+ Each of these stops before changing anything:
92
+
93
+ | Message | What it means | Do |
94
+ |---|---|---|
95
+ | `this house moved to schema 3 … memhouse migrate --v3-switch` | the admin converted your member; this machine still ships the 0.18 rooms | `memhouse migrate --v3-switch`, then `memhouse stop && memhouse start` |
96
+ | `this house requires writers at schema 3+` | a 0.18 memhouse on a converted house | `memhouse update` to 0.20, then `--v3-switch` |
97
+ | `'<m>' is being converted to schema 3` | the admin ran `--v3-prepare` but not yet `--v3-convert` | wait for the conversion, then install |
98
+ | `the house holds '<m>' on schema N; --schema M contradicts it` | an explicit `--schema` disagrees with the member's tables | leave `--schema` out; the install follows the house |
99
+ | `this house holds members on the 0.18 rooms and members on schema 3` | a house mid-conversion, and a new member | state which one this install joins: `--schema 2` or `--schema 3` |
100
+
101
+ The whole procedure is in [Upgrade to 0.20](upgrade-to-0.20.md).
102
+
103
+ `status`, `doctor` and `stats` name the same situations when the house answers but your
104
+ tables are not where this install looks. They say so instead of "not connected"; `status
105
+ --json` then has `"reachable": true`, `"connected": false` and a `rooms_problem`:
106
+
107
+ | `rooms_problem` | Message | Do |
108
+ |---|---|---|
109
+ | `moved` | `this house was moved to schema 3, and this machine still reads and ships the 0.18 rooms` | `memhouse migrate --v3-switch`, then `memhouse stop && memhouse start` |
110
+ | `not-moved` | `this machine reads schema 3, but '<m>' … is still on the 0.18 rooms` | the admin converts the house first; until then `MEMHOUSE_SCHEMA=2` in `~/.memhouse/env` |
111
+ | `admin` | `'<user>' administers this server and owns no rooms` | this install is configured with an administrator credential; make a member: `memhouse install --admin-user <user> --member <you>` |
112
+ | `none` | `no tables for '<m>' in '<house>'` | `memhouse install` builds them; or the house name in `~/.memhouse/env` is wrong |
113
+
114
+ ## Reading `memhouse doctor` on schema 3
115
+
116
+ On a schema-3 member, `doctor` adds these lines about the house. A failing line carries a hint
117
+ after it.
118
+
119
+ | Line | What it means | Do |
120
+ |---|---|---|
121
+ | `conversation tables: 6/6 …, keys correct` / `capture tables: 2/2 …` | your six tables in the house's database and your two in its archive exist, with the right sorting keys | a missing table: an admin creates it, with the command the hint names; a wrong key: the table has to be rebuilt from the template by its owner |
122
+ | `columns: every schema-3 table matches the template` | names, types and kinds match the version you run | a missing column, or one of the three columns retyped in harness-sessions-sql v1: `memhouse ship --ensure-schema` for your conversation tables. A member cannot alter its two archive tables: ask the house's admin, who can (for the three retyped columns, the statements are in the 0.20 release notes' Known issues). Any other changed type, or a lost `MATERIALIZED` clause: the table has to be rebuilt by its owner |
123
+ | `member name: no other member's tables within reach` / `member names collide: …` | whether your grant's prefix reaches another member's tables | see [Two member names that collide](#two-member-names-that-collide) |
124
+ | `N imported session(s) need a re-import` | the member is on the 0.20 development builds' layout (`<m>_turns`) and holds conversations `memhouse import` wrote there | keep the export, and import it again after the move: [Upgrade to 0.20](upgrade-to-0.20.md#imported-sessions-on-a-development-build) |
125
+ | `identity stamping: … all attributed` | every row carries the user that wrote it (`writer`) | an empty writer means a writer used `async_insert=1`; see [Operate a house](operate.md#server-settings-that-matter) |
126
+ | `ts stable: …` | no message part or call changed its time between versions | a moved time is an editor reader breaking a rule; report it with the sample the line shows |
127
+ | `dedupe: …` | captured sessions have absorbed the rows converted from 0.18 | it clears after a pass that captures the session to the end (`memhouse ship`) |
128
+ | `pricing: …` / `token capture: …` | every model has a price; every editor reports tokens | `memhouse update` (prices ship with each release; `memhouse q spend` lists unpriced models); an editor that reports no tokens shows cost as $0, not unknown |
129
+ | `removed content: …` | messages kept as removed versions (a revert, a rewind, a deleted message) | nothing; `memhouse q removed` lists them, and no total counts them |
130
+ | `forks: …` | fork links, and how many are billed once (their copied usage counted only in the origin) | nothing; a fork that could not be verified is counted in both sessions |
131
+ | `host identity …` | this machine's id, its sessions, and its 0.18 id if it had one | nothing |
132
+ | `the last pass refused to record vanished sessions in N store(s): …` | the store's list left sessions out, and memhouse would not record them as gone without the editor's word | read `~/.memhouse/logs/shipper.log` (its `vanish refused` lines). For a store whose list is empty, the line names a command: if its sessions really were deleted, run it, `memhouse doctor --confirm-empty-store <editor>:<store>` ([Daemons](daemons.md)) |
133
+
134
+ ## Two member names that collide
135
+
136
+ A member is granted its tables by one prefix: `bob` holds `mem.bob_*`. If another member's name
137
+ is `bob` plus an underscore and more (`bob_smith`), that prefix also matches `bob_smith`'s
138
+ tables, so `bob` can read and write them. ClickHouse 26.7 behaves this way (checked
139
+ 2026-10-22). Mostly the longer name's tables are exposed, to the shorter name. When the longer
140
+ name's extra part starts one of the shorter name's table names, the reverse holds too:
141
+ `bob_session` holds `mem.bob_session_*`, which matches `bob`'s `bob_session_parts`.
142
+
143
+ - **0.20 refuses to create such a member**, by install or invite: `'bob_smith' collides with
144
+ 'bob'`. Pick a name that neither starts with another member's name and an underscore, nor is
145
+ the start of one. The check finds the other member by its tables, so a member that has none
146
+ yet (provisioned by hand, never shipped) is not caught: check the users as below too.
147
+ - **`memhouse doctor`**, run as the shorter-named member, shows `member names collide: …`.
148
+ - **`memhouse migrate --v3-shares` refuses** to move shares in such a house, whichever of the
149
+ two members it is run for. It also finds the other member by its tables only.
150
+ - **`memhouse share --revoke` leaves a reader's 0.18 wildcard grant for the admin when it
151
+ matches another member's tables:** run by `bob`, a REVOKE on `mem.bob_*` would also strip the
152
+ reader's grants on `bob_smith`'s tables. Run by `bob_smith`, the revoke takes its own wildcard
153
+ back as usual, since `mem.bob_smith_*` matches none of `bob`'s tables (it would be left for
154
+ `bob_session`). A colliding name counts whether it is found by its tables or by its user, since
155
+ a member with no tables, or whose tables the revoking member cannot see, still holds its grants.
156
+ A user counts only when it holds a member's grant in this house or its archive (ALL, INSERT,
157
+ CREATE, DROP, ALTER, OPTIMIZE or TRUNCATE, or the right to grant, on its own tables or on a
158
+ broader target that covers them, such as an admin's `*.*`), holds a role, or its grants cannot
159
+ be read. A reader that holds no role, and the same person's account in another house on the
160
+ server, do not.
161
+ The wildcard is left too when it matches a table memhouse did not make for the member, or when
162
+ the house's tables or users cannot be listed. Two names can even share a table: `bob`'s 0.18
163
+ room `bob_tool_calls` is also `bob_tool`'s schema-3 `calls` table.
164
+ - **The admin takes a left wildcard back by hand**, because `--v3-shares` refuses here. A REVOKE
165
+ on it takes the reader's grants on every table it matches, so first note what the other
166
+ member had shared with that reader. `SHOW GRANTS` lists those grants merged into the wildcard,
167
+ so read the other member's record (`SELECT substring(key, 7) AS reader, argMax(value,
168
+ updated_at) AS state FROM mem.bob_smith_meta WHERE key LIKE 'share:%' GROUP BY key`), and grant
169
+ them again after the REVOKE, in the form that member's layout uses: its exact schema-3 tables,
170
+ or its 0.18 wildcard. If the reader is the other member itself (`bob` shared with
171
+ `bob_smith` in 0.18), a REVOKE on `mem.bob_*` from `bob_smith` also takes its SELECT on its own
172
+ tables: revoke only `bob`'s own tables from it instead.
173
+ - **An admin can list every member** to check a house: `SELECT name FROM system.users ORDER BY
174
+ name`, then look for one name that is another's plus `_`.
175
+
176
+ memhouse has no command yet that untangles an existing pair. Until it does, an admin decides
177
+ whether to stop one of the two members shipping, or to replace the shorter name's wildcard
178
+ grant with grants on its exact tables, by hand. Ask before changing either: it touches
179
+ another person's data.
74
180
 
75
181
  ## Still stuck
76
182
 
77
- Run `memhouse doctor` and `memhouse status --json`, and open an issue with both outputs.
78
- Check them before posting, and remove anything private, such as a host name.
183
+ Run `memhouse doctor` and `memhouse status --json`, and send both outputs to
184
+ security@memhouse.io (the only public contact for now). Check them before sending, and remove
185
+ anything private, such as a host name.
@@ -12,11 +12,12 @@ npm uninstall -g memhouse # then remove the package
12
12
  - **The default is the conservative one.** Stopping the shipper is routine: before an
13
13
  upgrade, while debugging, or when a laptop should go quiet for a week. It should not
14
14
  also mean "forget which house I use and who this machine is".
15
+ - **The dashboard's token goes with every tier.** The next `memhouse start` makes a new one.
15
16
  - **`--full-removal` asks first.** It deletes `host.json`. A later reinstall derives the
16
17
  same identity again from the machine's own id, so your history stays one machine, unless
17
18
  the machine was renamed in between or has no readable machine id. Then it comes back as
18
19
  a new host.
19
- - **Your memory stays.** The rooms in ClickHouse are untouched by every tier. What the
20
+ - **Your memory stays.** Your tables in ClickHouse are untouched by every tier. What the
20
21
  shipper wrote can be rebuilt from the local session stores with `memhouse ship --full`,
21
22
  for as long as the editors keep them.
22
23
 
@@ -24,8 +25,8 @@ npm uninstall -g memhouse # then remove the package
24
25
 
25
26
  - **A local house** from `deploy --local`: `memhouse deploy --down` removes the container
26
27
  **and its data volume**. That deletes the memory, and there is no undo.
27
- - **A shared server:** an administrator drops your rooms and user. See
28
- [Operate a house](operate.md).
28
+ - **A shared server:** an administrator drops your user and your tables, in the house's database
29
+ and its capture archive. See [Operate a house](operate.md#removing-a-member).
29
30
 
30
31
  ## Removing the skills
31
32
 
@@ -14,6 +14,12 @@ memhouse update --check # compare versions, change nothing
14
14
  3. **It stays on your channel** (`MEMHOUSE_CHANNEL`). See
15
15
  [Configuration](configuration.md#channels).
16
16
 
17
+ It asks the registry npm installs from: `npm config get registry`, or `npm_config_registry`
18
+ in the environment. Behind a mirror, that is the mirror; `update --check` prints which
19
+ registry it read and, when it could not, why (an HTTP status, a refused connection, no
20
+ answer). A registry that needs a login is read without one, so `update` cannot see its tags;
21
+ install by hand there (`npm install -g memhouse@<version>`, then `memhouse update --no-install`).
22
+
17
23
  Upgraded by hand already? Finish the job without reinstalling:
18
24
 
19
25
  ```bash
@@ -26,6 +32,16 @@ version it booted with, and hands over when they differ: under systemd or launch
26
32
  exits so the supervisor restarts it; with nothing supervising, it re-executes itself.
27
33
  `update` just makes the switch immediate.
28
34
 
35
+ The `/mem` skills follow on their own too. The first `memhouse` command after an upgrade, or
36
+ the shipper's restart, replaces every skill copy bound to this install with the new version's
37
+ (see [Search, resume and skills](search-and-resume.md#skills-for-your-agents)). Reload them in
38
+ a running Claude Code with `/reload-plugins`. Copies bound to another memhouse install on the
39
+ same machine are left to it, and a copy is never replaced by an older one.
40
+
41
+ **Coming from 0.18?** Updating to 0.20 keeps the house on the 0.18 rooms. The move to
42
+ schema 3 is a separate admin step, then one switch per machine:
43
+ [Upgrade to 0.20](upgrade-to-0.20.md).
44
+
29
45
  ## Several machines on one house
30
46
 
31
47
  Every machine of every member runs its own shipper. Upgrade them all.
@@ -0,0 +1,188 @@
1
+ # Upgrade a house from 0.18 to 0.20
2
+
3
+ 0.20 stores sessions in a new layout, schema 3: the harness-sessions-sql profile (`session_parts`, `session_usage`, `calls`, `sessions`, `session_links`, `session_lifecycle`, and the raw archive beside them). Each session is keyed by its harness store, tool output is kept in full, removed content is kept as removed versions, and a fork's copied prefix is billed once. A 0.18 house is in the old layout (the "rooms"), and moving it is a deliberate, one-time step. **`memhouse update` does not do it.**
4
+
5
+ It happens in three parts:
6
+
7
+ | Who | What | Command |
8
+ |---|---|---|
9
+ | every machine | install 0.20; it keeps shipping into the 0.18 rooms | `memhouse update` |
10
+ | the house's admin, once per member | fence the 0.18 writers off, rename the rooms, convert, check, move the shares | `memhouse migrate --v3-prepare`, `--v3-convert`, `--v3-reconcile`, `--v3-shares` |
11
+ | every machine, after the conversion | capture into schema 3 from now on | `memhouse migrate --v3-switch` |
12
+
13
+ Rehearse the move end to end on a disposable ClickHouse first, before you run it on a house that matters.
14
+
15
+ ## 1. Every machine: install 0.20
16
+
17
+ ```bash
18
+ memhouse update
19
+ ```
20
+
21
+ Behind a registry mirror, 0.18's `update` cannot see the new version: it asks
22
+ registry.npmjs.org, whatever npm is configured with (0.20's `update` reads npm's registry).
23
+ Install from the mirror with npm, then let memhouse finish the job:
24
+
25
+ ```bash
26
+ npm install -g memhouse@<version>
27
+ memhouse update --no-install
28
+ ```
29
+
30
+ The house is not converted. 0.20 keeps shipping the 0.18 rooms until the member is converted and the machine is switched; as every update does, it may create a missing room or column, or run a pending 0.18 migration (after asking). A machine can be upgraded days before the conversion.
31
+
32
+ ## 2. The admin: convert the member
33
+
34
+ **Back up first.** Before the first `--v3-prepare`, take a backup of the house and its accounts,
35
+ and check it ([Backups](operate.md#backups) has both statements). A 0.18 house has no
36
+ `<db>_archive` yet, so back up `<db>` and the accounts. The steps below keep the 0.18 rows
37
+ (renamed, never deleted), but a backup is the only way back from a mistake made by hand.
38
+
39
+ **Check the house for colliding member names.** `--v3-shares` refuses to move the shares of a
40
+ member whose name is another's plus an underscore, or the start of one (`bob`, `bob_smith`),
41
+ because each member's grant there reaches the other's tables. Check before you start:
42
+ [Two member names that collide](troubleshooting.md#two-member-names-that-collide).
43
+
44
+ These are admin steps, run with an administrator credential lent for the command only (never stored; see [the admin password guide](admin-password.md)). They take the administrator as `invite` and `members` do: `MEMHOUSE_ADMIN_USER` and `MEMHOUSE_ADMIN_PASSWORD` (or `--admin-user` and `--admin-password-file`). The house's address is `MEMHOUSE_URL`, or this machine's own env file when it has one; with no admin credential, or no address, a step refuses before it connects and names what is missing. `--member` names whose house is converted. `--v3-prepare` and `--v3-convert` take `--dry-run`, and refuse before changing anything when the house is not in the state they expect; `--v3-reconcile` only reads. The steps that change something ask first; `--yes` answers for a script, or an agent with no terminal (do not pipe `yes` into them).
45
+
46
+ ```bash
47
+ export MEMHOUSE_URL=<house-url> # unless this machine's own install points at the house
48
+ export MEMHOUSE_ADMIN_USER=<admin>
49
+ read -rs MEMHOUSE_ADMIN_PASSWORD && export MEMHOUSE_ADMIN_PASSWORD
50
+
51
+ memhouse migrate --v3-prepare --member <m> --dry-run # what it would do
52
+ memhouse migrate --v3-prepare --member <m> # fence + rename
53
+ memhouse migrate --v3-convert --member <m> --dry-run # what it would convert
54
+ memhouse migrate --v3-convert --member <m> # the rooms into the profile tables
55
+ memhouse migrate --v3-reconcile --member <m> # every converted row and bucket checked
56
+ memhouse migrate --v3-shares --member <m> --dry-run # the grants it would make
57
+ memhouse migrate --v3-shares --member <m> # the member's shares, onto schema 3
58
+
59
+ unset MEMHOUSE_ADMIN_PASSWORD
60
+ ```
61
+
62
+ `MEMHOUSE_USER` and `MEMHOUSE_PASSWORD`, which earlier drafts of this guide used for the admin, still
63
+ work for these steps in 0.20 when no admin variable is set, with a note that they are deprecated
64
+ there. They are the member's names; the admin steps stop reading them in the next minor release.
65
+ A credential the server refuses is named as one: `could not authenticate as '<admin>' at <url>`.
66
+
67
+ What each step does:
68
+
69
+ - **`--v3-prepare`** does two things:
70
+ - It writes `min_writer_schema = 3` into the member's `meta` room. Every 0.18 writer now refuses to ship ("this house requires writers at schema 3+"). A 0.20 machine that has not switched yet refuses too, and says how to switch.
71
+ - It renames the member's three 0.18 rooms (`<m>_messages`, `<m>_tool_calls`, `<m>_sessions`) into `<db>_archive` as `*_pre_v3`, in one statement. **Nothing is deleted**: the rooms are kept whole, and the conversion reads them from there.
72
+
73
+ Run a second time, it says the member is prepared already and changes nothing. It refuses a half-done state, for example one room renamed and another not.
74
+ - **`--v3-convert`** creates the schema-3 tables and the archive, grants the member its capture tables, and converts the 0.18 rows into `session_parts`, `session_usage`, `calls` and `sessions` (`adapter = 'legacy'`). Tokens are carried as 0.18 stored them, with flags that say how to read them: Gemini CLI's input includes its cached tokens, and the totals take them out. 0.18 never stored Gemini's thought tokens, and they cannot be recovered.
75
+ - Every row keeps its `host` as 0.18 wrote it (see [Host identity](#host-identity)).
76
+ - A session 0.18 holds more than once (under a prefixed and a bare id, from two users, or from one machine under several host ids) becomes one session, at the host of its newest row and the path of its transcript. The newest copy converts as it is. Rows of an older copy that the newest one holds again are not converted (duplicates). Rows only an older copy holds are content that left the session, and convert as removed versions: kept and searchable (`memhouse q removed`), never counted.
77
+ - Rows of one id under two hosts are one session when the id cannot be another session's: a UUID-like id of Claude Code, Codex, Gemini CLI, Cursor, Cursor agent, VS Code, Copilot CLI or OpenCode, or hosts a reported host pair names as one machine. Other editors' ids (Goose, Zed and the rest) can repeat across machines, so their rows under two hosts are one session only when they read as one machine whose 0.18 id changed: one 0.18 stream (one id, one user, imported or shipped but not both), each host holding its own run of positions, each run shipped after the one before it, and no reported host pairs naming the hosts as two machines. Two machines that used one id both started at the first position and wrote over each other's rows, so their runs interleave, or the lower run was shipped later. Those stay one session per host, as do rows in two streams (an import beside a shipped copy, two users) and two 0.18 ids (copies) on two hosts.
78
+ - A 0.18 session with no message rows, only its sessions row (a title and a path), converts as a session without messages: its title, folder and path, with its 0.18 times as its start and end and no totals. It is grouped by host as other sessions are. The session list finds it by its name or folder; a text search, which reads messages, does not.
79
+ - It is resumable: re-run it after an interruption, and finished buckets are skipped.
80
+ - Only a conversion that ran to the end opens the house: it writes a `profile_converted` mark into the member's `meta`, and installs and `--v3-switch` wait for it.
81
+ - Each converted session gets its store, `harness_path`, from the path 0.18 recorded. For
82
+ OpenCode the store is the database file: a row whose path names a channel DB
83
+ (`opencode-<channel>.db`) keeps that file; every other OpenCode row is keyed by the default
84
+ `<data folder>/opencode.db`, the only database 0.18 read.
85
+ - **`--v3-shares`** moves the member's shares. A 0.18 share is a grant on the old rooms, and
86
+ the rooms were renamed, so without this step a scoped reader loses access.
87
+ - It re-creates only the shares memhouse recorded in the member's `meta`.
88
+ - It prints every grant and row policy before applying it.
89
+ - It refuses, changing nothing, when the record and the grants ClickHouse holds disagree: a
90
+ grant made by hand, or a recorded share with no grant behind it. It lists each one.
91
+ - The new share is SELECT on exactly the six profile tables (`session_parts`, `session_usage`,
92
+ `session_links`, `session_lifecycle`, `calls`, `sessions`), never the archive. A 0.18 full
93
+ share's wildcard (`<m>_*`, which also reached `meta`, `events` and the stat tables) is taken
94
+ back.
95
+ - A scope keeps its meaning: `source=` becomes `harness=`; `since`/`until` filter parts, usage,
96
+ calls and lifecycle by their own time (a row with no time is outside a time scope), and
97
+ sessions by their start; a project or folder scope covers parts, calls and sessions (the
98
+ sessions row carries the totals).
99
+ - **`--v3-reconcile`** recomputes every converted row's content sha from its columns and checks that every bucket holds every key the 0.18 rows make. Exit 0 means every row and bucket checks out.
100
+
101
+ Once every member is converted, take a backup again, now of both databases, `<db>` and
102
+ `<db>_archive`, with the accounts ([Backups](operate.md#backups)).
103
+
104
+ ## 3. Every machine: switch
105
+
106
+ **Which machines.** Every machine that ships as the member switches, and the house does not
107
+ reach them: the member does it on each one. To know which machines those are, the admin reads
108
+ the member's last ship per machine from its `meta` room (still in `<db>` after the conversion):
109
+
110
+ ```sql
111
+ SELECT substring(key, 11) AS writer, value AS last_ship
112
+ FROM <db>.<m>_meta FINAL
113
+ WHERE startsWith(key, 'last_ship:')
114
+ ORDER BY last_ship DESC
115
+ ```
116
+
117
+ `writer` is `<member>@<machine>`, the 0.18 name of each machine that shipped. A 0.20 machine
118
+ that has not switched refuses to ship, and its `memhouse status` names the switch; a 0.18 machine
119
+ refuses until it is updated to 0.20.
120
+
121
+ ```bash
122
+ memhouse migrate --v3-switch --dry-run
123
+ memhouse migrate --v3-switch
124
+ memhouse stop && memhouse start
125
+ ```
126
+
127
+ `--v3-switch` sets `MEMHOUSE_SCHEMA=3` in this install's env file and changes nothing else. It refuses unless the house holds the member's schema-3 tables and, for a member fenced off its 0.18 rooms, the conversion ran to the end (its `profile_converted` mark): a machine never captures into a half-converted house.
128
+
129
+ After the restart, the shipper captures this machine's transcripts into schema 3. On its first pass it dedupes: each captured session absorbs the converted rows of the same session, so nothing is counted twice. Rows the transcript no longer has are kept and counted once.
130
+
131
+ Then check the result:
132
+
133
+ ```bash
134
+ memhouse doctor # every house check green
135
+ memhouse q recent # the sessions, once each
136
+ ```
137
+
138
+ ## Sessions deleted before the move
139
+
140
+ A session whose transcript was deleted from disk before the move keeps its converted rows, and
141
+ nothing in the house says the file is gone: 0.18 recorded no deletions, and the conversion
142
+ reads only the rows. On schema 3 a deletion is recorded when a capture sees it, which needs the
143
+ file to have been there at an earlier capture. Such a session therefore reads as current, under
144
+ the machine's 0.18 id.
145
+
146
+ ## Imported sessions on a development build
147
+
148
+ A member that ran a 0.20 development build has `<member>_turns`, the layout the profile replaces. If you used `memhouse import claude-ai` there, those conversations live only in `<member>_turns`. A recapture reads the editors' files, and an import has none, so moving the member does not bring them back.
149
+
150
+ - **Before the move.** `memhouse doctor` names them as "N imported session(s) need a re-import". `memhouse install`, `migrate --v3-prepare` and `migrate --v3-convert` refuse the member with the same count while this build cannot import into the profile tables. A `--dry-run` only says so.
151
+ - **Keep the export.** Keep the export you imported from: the zips, or the archive folder.
152
+ - **After the move, import it again,** with a build that writes imports into the profile tables. Run it as a dry run first:
153
+
154
+ ```bash
155
+ memhouse import claude-ai <the export> --update # dry run: every conversation reads as new
156
+ memhouse import claude-ai <the export> --update --write
157
+ ```
158
+
159
+ The keys are the same as before, so the re-import stores the same sessions under the same ids.
160
+
161
+ ## Host identity
162
+
163
+ 0.20 names a machine by `sessions-model/host/v0`: a fingerprint derived from the OS machine id, the same value for every reader on the machine. 0.18 used its own id (`<name>-<8 hex>`).
164
+
165
+ Converted rows keep the 0.18 id. On this machine it is an alias of the new one, so dedupe matches a converted session to its capture. Rows a machine shipped and whose transcripts are gone stay under its 0.18 id: they are history and are still counted.
166
+
167
+ The two ids cannot be computed from each other, so the conversion cannot join them; the machine can. When it switches, and on each capture pass, it records its 0.18 id as its own in the member's `meta` (`host_alias:<0.18 id>`), and `memhouse status`, `q machines`, `q hosts`, `q coverage`, `--host` and `memhouse doctor` read that id's rows as the machine: one writer, not two. No row is rewritten. A member's aliases come from its own `meta` and apply to its own rows only; a housemate reading a share sees the hosts as stored.
168
+
169
+ ## Rolling back
170
+
171
+ - **Before any machine has switched,** rename the rooms back, and lift the fence with a newer `meta` row (`meta` is latest-wins):
172
+
173
+ ```sql
174
+ RENAME TABLE <db>_archive.<m>_messages_pre_v3 TO <db>.<m>_messages,
175
+ <db>_archive.<m>_tool_calls_pre_v3 TO <db>.<m>_tool_calls,
176
+ <db>_archive.<m>_sessions_pre_v3 TO <db>.<m>_sessions;
177
+ INSERT INTO <db>.<m>_meta (key, value) VALUES ('min_writer_schema', '0');
178
+ ```
179
+
180
+ The converted tables stay; an admin may drop them.
181
+ - **After a machine has switched,** its captures since then exist only in schema 3. Rolling back means losing them from the 0.18 view, so do not, unless you are prepared to re-ship those sessions from their transcripts with 0.18.
182
+
183
+ ## What changes for readers
184
+
185
+ - `memhouse q …` reads schema 3: `q recent`, `q session <ref>`, `q search`, `q spend`, and the rest (`memhouse q --list`).
186
+ - A session's ref names its store (`<harness>:<id>~…`); a bare id still resolves when it is unique.
187
+ - `memhouse q removed` lists what is kept as removed versions: a revert, a rewind, a message deleted at the source. No other read shows them.
188
+ - The dashboard and the `/mem` skills read schema 3 once the install is switched.
@@ -5,7 +5,8 @@ do, how to upgrade, what is deprecated and when it goes, and what was deferred.
5
5
 
6
6
  | version | date | headline |
7
7
  |---|---|---|
8
- | [0.18.12](v0.18.12.md) | 2026-10-29 | skills: allowed tools restricted to read-only commands |
8
+ | [0.20.1](v0.20.1.md) | 2026-11-02 | invites expire; `memhouse plugins install codex`; per-command help and clearer CLI answers |
9
+ | [0.20.0](v0.20.0.md) | 2026-10-29 | schema 3: a session is its store, nothing cut or lost, forks billed once, ten editors through normalisers; Apache-2.0 |
9
10
  | [0.18.11](v0.18.11.md) | 2026-10-10 | sessions with subagents stop re-shipping; security: no stored credential sent to `invite --url` |
10
11
  | [0.18.10](v0.18.10.md) | 2026-10-08 | security: the admin password never on argv; no member password printed; a scoped share reaches only its scope |
11
12
 
@@ -82,7 +82,7 @@ memhouse ships into the house, where the archive is insert-only. (#20)
82
82
  - **Tests guard it.**
83
83
  - A unit test fails on any print call that evaluates a password: as an argument,
84
84
  concatenated, or interpolated.
85
- - `misc/invite-matrix.sh` runs every install, invite and passwd path against a real
85
+ - `misc/invite-matrix.sh` (repository) runs every install, invite and passwd path against a real
86
86
  ClickHouse and checks each output against the real passwords.
87
87
 
88
88
  ### A scoped share shares only what its scope filters (#13)
@@ -163,8 +163,8 @@ These do not change the installed package:
163
163
  - **memhouse.io describes 0.18** (#12): one layout, five skills, the honest editor count
164
164
  (16 adapters reading 18 apps).
165
165
  - `memhouse.io/.wrangler` (wrangler's local cache) is no longer tracked.
166
- - `misc/one-layout-matrix.sh` now asserts the scoped-share boundary, and
167
- `misc/invite-matrix.sh` the admin-password sources: environment, stdin, `--keep-admin`,
166
+ - `misc/one-layout-matrix.sh` (repository) now asserts the scoped-share boundary, and
167
+ `misc/invite-matrix.sh` (repository) the admin-password sources: environment, stdin, `--keep-admin`,
168
168
  and none. The file source is covered by unit tests. CI runs both matrices against
169
169
  ClickHouse 25.11 and 26.7.
170
170
 
@@ -172,7 +172,7 @@ These do not change the installed package:
172
172
 
173
173
  **The gentar kit v0.9.3 bump (#18) also moves to 0.18.11.** It changes CI only.
174
174
 
175
- **The subagent re-ship fix moves to 0.18.11.** It is on branch `claude/subagent-stable-seq`
175
+ **The subagent re-ship fix moves to 0.18.11.** It shipped there
176
176
  and stops a Claude Code session with subagents from re-shipping whole on every pass, which
177
177
  is what grew one house to 16.2 M message rows for about 295 k real ones. It was reviewed
178
178
  before release by four models. Three said "ship with fixes"; the fourth said "do not ship",
@@ -221,6 +221,6 @@ every tag and what it changed.
221
221
  - No live house was touched to prepare this release.
222
222
  - **No drill was run for this release; waived by the maintainer (2026-10-08). The drill runs before 0.19.0.**
223
223
  `AGENTS.md` asks for a drill before a release that changes a surface a person touches,
224
- and this one changes install, invite and share. A drill was prepared on the testbed, but
224
+ and this one changes install, invite and share. A drill was prepared on a test VM, but
225
225
  its setup could not run in the release environment, and the maintainer chose to release
226
226
  without it.
@@ -58,7 +58,7 @@ Now:
58
58
  - **An admin given for this command** (`--admin-user`, or an exported
59
59
  `MEMHOUSE_ADMIN_USER`, with its password from a file, stdin, the environment or a prompt)
60
60
  still provisions at `--url`. That is what the person typing the command asked for.
61
- - **`misc/invite-matrix.sh` points `--url` at a spy server** and asserts that it receives
61
+ - **`misc/invite-matrix.sh` (repository) points `--url` at a spy server** and asserts that it receives
62
62
  nothing but a ping. On 0.18.10 the spy received two authenticated requests.
63
63
 
64
64
  **Action:**
@@ -154,7 +154,7 @@ Earlier published packages carried those copies without the notices.
154
154
  only) instead of being dry-run on the CI host.
155
155
  - **The suite tears down only a house it created.** It refuses to deploy over an existing
156
156
  house, and a failed teardown fails it.
157
- - **`misc/skill-recipe.sh` runs the skills' queries verbatim, extracted from the docs.**
157
+ - **`misc/skill-recipe.sh` (repository) runs the skills' queries verbatim, extracted from the docs.**
158
158
  The fixture includes a superseded parse and a subagent block.
159
159
 
160
160
  ## Pre-tag list (0.18.10 review): final status
@@ -184,4 +184,4 @@ Earlier published packages carried those copies without the notices.
184
184
  - invite 58/58;
185
185
  - one-layout 62/62.
186
186
  - **Second looks:** a second model reviewed #21 and #22.
187
- - **Labs:** kept light, because the lab host's RAID was degraded. No live house was touched.
187
+ - **Labs:** kept light. No live house was touched.