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
package/docs/README.md CHANGED
@@ -22,12 +22,14 @@ One common operation each.
22
22
  | [Install and onboard](guides/install.md) | install against a local, existing, or invited house; fix `EACCES` |
23
23
  | [Configuration](guides/configuration.md) | choose which editors and directories ship; run several instances; follow a channel |
24
24
  | [Update](guides/update.md) | stay current, and upgrade every machine of a house |
25
- | [Daemons and services](guides/daemons.md) | keep the shipper and dashboard running, across reboots |
25
+ | [Upgrade to 0.20](guides/upgrade-to-0.20.md) | move a 0.18 house to schema 3: prepare, convert, reconcile, then switch each machine |
26
+ | [Daemons and services](guides/daemons.md) | keep the shipper and dashboard running, across reboots; open the dashboard |
26
27
  | [Invite members](guides/invite.md) | bring a teammate into a house you administer |
27
- | [Share and revoke](guides/share.md) | let someone read your rooms, scoped or whole |
28
+ | [Share and revoke](guides/share.md) | let someone read your memory, scoped or whole |
28
29
  | [Admin password](guides/admin-password.md) | run an admin command without the password reaching `ps`, history, or a transcript |
29
30
  | [Search, resume and skills](guides/search-and-resume.md) | find an old session, reopen it, let an agent answer from history |
30
- | [How memory is stored](guides/storage.md) | houses, rooms, epochs, machine identity, what is never deleted |
31
+ | [Import claude.ai conversations](guides/import-claude-ai.md) | bring a claude.ai data export into your house, and the next export after it |
32
+ | [How memory is stored](guides/storage.md) | houses, a member's tables, machine identity, what is never deleted |
31
33
  | [Operate a house](guides/operate.md) | members, migrations, moving a house, backups |
32
34
  | [Troubleshooting](guides/troubleshooting.md) | an editor ships nothing, daemons stop, a dashboard looks empty |
33
35
  | [Uninstall](guides/uninstall.md) | stop, forget the house, or remove everything |
@@ -37,10 +39,10 @@ One common operation each.
37
39
  - `memhouse <command> --help`: every command's own usage. It always prints usage and
38
40
  never runs the command.
39
41
  - [Release notes](releases/): what changed in each version, and what to do about it.
40
- - [`../memhouse/house/HOUSE.md`](../memhouse/house/HOUSE.md): the layout and the schema,
41
- column by column.
42
- - [`../memhouse/DESIGN.md`](../memhouse/DESIGN.md): the design, and why it is shaped this
43
- way.
42
+ - [`../memhouse/house/HOUSE.md`](../memhouse/house/HOUSE.md): the 0.18 rooms, column by
43
+ column. Schema 3's tables are in
44
+ [`../memhouse/house/schema-profile.sql.tpl`](../memhouse/house/schema-profile.sql.tpl).
45
+ - `memhouse/DESIGN.md` (repository, not yet public): the design, and why it is shaped
46
+ this way.
44
47
  - [`../SECURITY.md`](../SECURITY.md): what holds, and what does not.
45
- - [`docs/design/`](https://github.com/agent-realm/memhouse/tree/main/docs/design) (in the
46
- repository): designs captured before they are built.
48
+ - `docs/design/` (repository, not yet public): designs captured before they are built.
@@ -6,9 +6,13 @@ A few commands need the ClickHouse server's administrator:
6
6
  - `invite`;
7
7
  - `passwd --admin-user …`, for a member whose account predates self-rotation (0.11);
8
8
  - `members`;
9
- - `convert`.
9
+ - `convert`;
10
+ - `migrate --v3-prepare`, `--v3-convert`, `--v3-reconcile` and `--v3-shares`, which move a 0.18
11
+ house to schema 3 ([Upgrade to 0.20](upgrade-to-0.20.md)). `--v3-switch` is the member's own
12
+ step.
10
13
 
11
- Shipping, searching and sharing never need it.
14
+ They name the admin with `--admin-user` or `MEMHOUSE_ADMIN_USER` (or the admin this install
15
+ keeps); `MEMHOUSE_USER` names the member. Shipping, searching and sharing never need it.
12
16
 
13
17
  **The admin password owns the whole server.** Keep it out of three places:
14
18
 
@@ -16,7 +20,9 @@ Shipping, searching and sharing never need it.
16
20
  runs, and shell history keeps it afterwards;
17
21
  - **an agent's conversation**, because memhouse ships that conversation into the house,
18
22
  and the archive is insert-only;
19
- - **disk**, unless you chose that: `--keep-admin`, or `deploy --local`.
23
+ - **disk**, unless you chose that: `--keep-admin`, or `deploy --local`. `memhouse setup`
24
+ given a credential that administers the server writes nothing without `--keep-admin` (a
25
+ terminal is asked, default no), and refuses that password on the command line outright.
20
26
 
21
27
  ## Where memhouse takes it from
22
28
 
@@ -26,14 +32,19 @@ In order, first answer wins:
26
32
  |---|---|---|
27
33
  | 1 | `--admin-password-file <file>` | a file you control, mode 600. One trailing newline is dropped |
28
34
  | 1 | `--admin-password-file -` | piping it in: `printf '%s' "$P" \| memhouse … --admin-password-file -` |
29
- | 2 | `--admin-password <value>` | **deprecated**: works in 0.18.x with a warning, **removed in 0.19.0** |
30
- | 3 | `MEMHOUSE_ADMIN_PASSWORD` | a secret manager lends it to one command (below) |
31
- | 4 | the credential this install keeps | only when it belongs to the same admin user |
32
- | 5 | a prompt | when there is a terminal; input is hidden, and pasting works |
35
+ | 2 | `MEMHOUSE_ADMIN_PASSWORD` | a secret manager lends it to one command (below) |
36
+ | 3 | the credential this install keeps | only when it belongs to the same admin user |
37
+ | 4 | a prompt | when there is a terminal; input is hidden, and pasting works |
33
38
 
34
- The two forms of `--admin-password-file` are one option, and it cannot be combined with
35
- `--admin-password`. With no source and no terminal, the command stops and prints these
36
- options. It never hangs, and it never guesses.
39
+ The two forms of `--admin-password-file` are one option. A file that group or other users
40
+ can read or write is refused, like an ssh key, with the `chmod 600` that fixes it; the
41
+ secret mounts of a container (`/run/secrets/`, `/var/run/secrets/`) are the exception.
42
+ With no source and no terminal, the command stops and prints these options. It never
43
+ hangs, and it never guesses.
44
+
45
+ `--admin-password <value>` is refused. It was deprecated in 0.18.10 and removed in 0.20.0:
46
+ the command stops before doing anything, exits 2, names the forms above, and never prints
47
+ the value.
37
48
 
38
49
  ## With a secret manager
39
50
 
@@ -93,3 +104,19 @@ it before a scripted run, rather than guessing from a grant string.
93
104
  Treat it as burned. Rotate it on the server (`ALTER USER … IDENTIFIED BY …`, as the
94
105
  admin) and update every place that legitimately holds it. Deleting the transcript does
95
106
  not help: the house is insert-only, and it may already have shipped.
107
+
108
+ ## The other passwords a command takes
109
+
110
+ The same rule holds for `--password` (install, setup, onboard; for `passwd`, the new
111
+ password), `--member-password` (install, invite) and relocate's `--to-password`. Each has a
112
+ file form that takes a path at mode 600 or `-` for stdin, and the two that are not the
113
+ member's own have an environment variable:
114
+
115
+ | flag on the command line | instead |
116
+ |---|---|
117
+ | `--password <value>` | `MEMHOUSE_PASSWORD`, or `--password-file <file>` / `--password-file -` |
118
+ | `--member-password <value>` | `MEMHOUSE_MEMBER_PASSWORD`, or `--member-password-file …` |
119
+ | `--to-password <value>` | `MEMHOUSE_TO_PASSWORD`, or `--to-password-file …` |
120
+
121
+ The command-line forms still work in 0.20, with a warning on every run, and are refused
122
+ from the next minor release. Only one password can come from stdin per command.
@@ -12,14 +12,15 @@ default house.
12
12
 
13
13
  | Variable | Meaning |
14
14
  |---|---|
15
- | `MEMHOUSE_URL` | the house's HTTP(S) endpoint |
15
+ | `MEMHOUSE_URL` | the house: its ClickHouse HTTP(S) endpoint, port included |
16
16
  | `MEMHOUSE_USER` / `MEMHOUSE_PASSWORD` | the member credential the shipper writes as |
17
+ | `MEMHOUSE_ADMIN_USER` / `MEMHOUSE_ADMIN_PASSWORD` | the server's administrator, for the admin commands only ([admin password](admin-password.md)); never passed to the shipper or the dashboard |
17
18
  | `MEMHOUSE_DB` | the database holding the rooms, normally `mem` |
18
- | `MEMHOUSE_PORT` | the dashboard port, default `4640` |
19
+ | `MEMHOUSE_PORT` | this machine's dashboard port, default `4640`; never the house's port, which is part of `MEMHOUSE_URL`. When it is in use the dashboard takes the next free one, up to 10 above |
19
20
  | `MEMHOUSE_CHANNEL` | the npm dist-tag `update` follows |
20
21
  | `MEMHOUSE_EDITORS` | which adapters run (default: all) |
21
22
  | `MEMHOUSE_<EDITOR>_ROOTS` | where one adapter looks |
22
- | `MEMHOUSE_HOME` | where the env file, host identity, logs and pidfiles live |
23
+ | `MEMHOUSE_HOME` | where the env file, host identity, logs, pidfiles and the dashboard's token live |
23
24
  | `MEMHOUSE_ENGINE` | `docker` or `podman`, for `deploy --local` |
24
25
 
25
26
  Never print this file or these variables in a terminal an agent is reading. memhouse ships
@@ -46,7 +47,11 @@ MEMHOUSE_EDITORS='claude'
46
47
  MEMHOUSE_CLAUDE_ROOTS='~/.claude-playbooks/work-team'
47
48
  ```
48
49
 
49
- - `MEMHOUSE_EDITORS` names the adapters to run.
50
+ - `MEMHOUSE_EDITORS` names the adapters to run. On schema 3 it also names the normalisers,
51
+ which read ten editors' stores directly: `agy` (also answers to `antigravity`),
52
+ `copilot-cli`, `cursor`, `cursor-agent`, `devin`, `gemini-cli`, `goose`, `opencode`,
53
+ `vscode`, `zed`. An editor left out is never read, and logs its normaliser wrote earlier
54
+ are no longer captured.
50
55
  - `MEMHOUSE_<EDITOR>_ROOTS` replaces one adapter's location. The variable name is the
51
56
  adapter's name upper-cased, with dashes turned into underscores.
52
57
  - Claude is the one adapter with many roots, one per Claude Code config directory, so its
@@ -90,8 +95,10 @@ MEMHOUSE_HOME=~/.memhouse-work memhouse instance # which memhouse this is
90
95
  `plugins install claude`, run from an instance, installs the skills into exactly the
91
96
  playbooks that instance ships, and **binds** them. It writes `MEMHOUSE_HOME` and
92
97
  `MEMHOUSE_BIN` into each playbook's `settings.json` `env`, and Claude Code applies that to
93
- every session there. So the `/mem:*` skills in a bound playbook read that instance's house
94
- and run that instance's binary, whatever the launching shell had set. `plugins list` shows
98
+ every session there. So the `/mem:*` skills in a bound playbook read that instance's house,
99
+ whatever the launching shell had set. The skills run plain `memhouse`, so the read commands they
100
+ pre-approve match without a prompt; `MEMHOUSE_BIN` is the instance's binary, which they use
101
+ instead only when `memhouse` is not on PATH or is a different binary. `plugins list` shows
95
102
  the binding; `plugins remove claude` takes it out.
96
103
 
97
104
  ## Channels
@@ -3,7 +3,8 @@
3
3
  memhouse runs two long-lived processes:
4
4
 
5
5
  - the **shipper**, which runs an incremental pass every 300 seconds;
6
- - the **dashboard**, on `http://localhost:4640` (`MEMHOUSE_PORT` changes it).
6
+ - the **dashboard**, on port 4640 of `127.0.0.1` (`MEMHOUSE_PORT` changes it). Open it with
7
+ `memhouse dashboard`; see [The dashboard](#the-dashboard).
7
8
 
8
9
  ## Until the next reboot
9
10
 
@@ -34,6 +35,37 @@ memhouse service status | stop | start | restart | uninstall
34
35
  upgrade, run `memhouse service uninstall`, then `memhouse service install`, so the unit
35
36
  points at the new one.
36
37
 
38
+ ## The dashboard
39
+
40
+ ```bash
41
+ memhouse dashboard # open it in your browser
42
+ memhouse dashboard --no-open # write the page, open nothing
43
+ ```
44
+
45
+ - **Every /api request needs this machine's token.** It is a random secret in
46
+ `$MEMHOUSE_HOME/dashboard-token` (default `~/.memhouse/dashboard-token`), mode 600, made by
47
+ the first `memhouse start`. A token file that group or other users can read is refused, with
48
+ the `chmod 600` that fixes it. To replace the token, delete the file and restart the
49
+ dashboard (`memhouse stop && memhouse start`).
50
+ - **`memhouse dashboard` checks the dashboard, then opens a file.** It first asks the server on
51
+ `127.0.0.1:<port>` to prove it holds the token, without sending it; anything else listening
52
+ there gets nothing, and the command exits 1 with "port N is not your dashboard". Then it
53
+ writes `$MEMHOUSE_HOME/dashboard.html` (mode 600), a page that sends the browser to
54
+ `http://127.0.0.1:<port>/` with the token in the URL's fragment, and opens that file (`open` on
55
+ macOS, `xdg-open` on Linux). The page keeps the token for that browser tab only and removes it
56
+ from the address bar; no cookie is set. A new tab, or a restarted browser, needs
57
+ `memhouse dashboard` again. The URL with the token is printed only when standard output is a
58
+ terminal, never with `--json` or into a pipe or file.
59
+ - **Scripts send the token as a header:** `Authorization: Bearer <token>`, read from the file.
60
+ - **Without it,** `/api/*` answers 401, and a page opened without the token says to open the
61
+ dashboard with `memhouse dashboard`. The page itself holds no house data.
62
+ - **When port 4640 is in use** (possibly by another account on this machine), the dashboard takes
63
+ the next free port, up to 4650, and `memhouse start` says which. `memhouse status` and
64
+ `memhouse dashboard` use the port it took. Set `MEMHOUSE_PORT` to choose the port.
65
+ - **From another machine,** forward the port over SSH (`ssh -L 4640:127.0.0.1:4640 <host>`)
66
+ and keep the default bind. `MEMHOUSE_HOST=0.0.0.0` makes the dashboard reachable from other
67
+ machines over plain HTTP, token included; prefer the tunnel.
68
+
37
69
  ## One pass by hand
38
70
 
39
71
  ```bash
@@ -49,5 +81,34 @@ never deletes anything: see [How memory is stored](storage.md).
49
81
  ## Logs
50
82
 
51
83
  The daemons log to `$MEMHOUSE_HOME/logs/` (default `~/.memhouse/logs/`): `shipper.log` and
52
- `dashboard.log`. `memhouse status` reads the shipper's last pass from its log, and reports
53
- a failing pass as a failure even while the process is alive.
84
+ `dashboard.log`. `memhouse start` exits 1 when the shipper exits right after starting; a
85
+ dashboard that does is reported as a warning, with the reason from its log. `memhouse status`
86
+ reads the shipper's last pass from its log, and reports a failing pass as a failure even while
87
+ the process is alive.
88
+
89
+ A pass that would not record an editor's deleted sessions as gone prints one line per store
90
+ and cause after its own line, `[memhouse] vanish refused: <editor> <store>: <why>`: the store's
91
+ list named none of the sessions the house holds of it (an empty or unreadable store), or left
92
+ out a session the store still has, or one that could not be read. Nothing is recorded as
93
+ vanished or removed, and `memhouse doctor` fails a line naming the store until a later pass
94
+ has no such line.
95
+
96
+ A store whose every session was deleted is refused in the same way, because an empty list is
97
+ also what an unreadable store gives. Its line ends with the command for that case:
98
+
99
+ ```bash
100
+ memhouse doctor --confirm-empty-store <editor>:<store>
101
+ ```
102
+
103
+ Copy it from the line as printed: a store path with spaces is quoted there. It confirms the
104
+ sessions that pass refused (sessions recorded as gone already replay without it), and records
105
+ the confirmation in this machine's capture state, beside the store's sweep state under
106
+ `$MEMHOUSE_HOME/state/derive-profile/stores/`; it needs no house. Removing that folder drops a
107
+ confirmation not yet used. The next pass that finds the store there, its list still empty and
108
+ no other session held takes each as gone where the editor's reader says it is gone, and no
109
+ longer refuses the store. A list that names a session again, or a session held that the
110
+ confirmation does not name, cancels it; a store with more than 100 sessions is taken over
111
+ several passes on the one confirmation.
112
+
113
+ A `[memhouse] vanish warning: …` line names a listed session's source the list left out that
114
+ still reads: it is captured as usual, and doctor shows the warning without failing.
@@ -0,0 +1,204 @@
1
+ # Import claude.ai conversations
2
+
3
+ claude.ai keeps your conversations on its servers, not on your machine, so the shipper never
4
+ sees them. `memhouse import claude-ai` reads a claude.ai data export and stores its conversations
5
+ in your house beside your editor sessions. They then show up in search, in `memhouse q` and in
6
+ the `/mem` skills.
7
+
8
+ Each run is one **batch**. A later export adds only the conversations the house does not hold
9
+ yet, so there is nothing to clean up between exports.
10
+
11
+ ```bash
12
+ memhouse import claude-ai ~/Downloads/claude-export.zip # dry run: counts only
13
+ memhouse import claude-ai ~/Downloads/claude-export.zip --write # import
14
+ memhouse import claude-ai --list # the batches in your house
15
+ memhouse import claude-ai --undo <batch> --write # take a batch back out
16
+ ```
17
+
18
+ The command needs schema 3: the profile tables (`<member>_session_parts`, `_calls`,
19
+ `_sessions`), or a development house's layout 3.2 tables (`<member>_turns`). A member with both is
20
+ written on the profile, which its readers read. It refuses any other layout before it reads or
21
+ writes anything.
22
+
23
+ ## 1. Get an export
24
+
25
+ In claude.ai, go to Settings, then Privacy, and choose **Export data**.
26
+
27
+ The export arrives as **part zips**, one per kind: `conversations-000.zip`,
28
+ `design_chats-000.zip`, `memories-000.zip`, `projects-000.zip` and so on. A large export can have
29
+ more than one part of a kind (`conversations-001.zip`). Pass them all, or the folder that holds
30
+ them:
31
+
32
+ ```bash
33
+ memhouse import claude-ai ~/Downloads/*-000.zip # every part, by name
34
+ memhouse import claude-ai ~/Downloads/claude-export/ # a folder holding the parts
35
+ ```
36
+
37
+ - **What is read.** Every `conversations` part and every `design_chats` part, in index order. The
38
+ other parts are listed as ignored: they hold no conversations.
39
+ - **A conversation in two parts** is stored once, from the first part, and counted as a duplicate.
40
+ - **Refused before anything is read from the house:** no `conversations` part among the inputs,
41
+ the same part twice (a re-downloaded `conversations-000 (1).zip`, say), or a damaged zip.
42
+
43
+ `memhouse import claude-ai` takes these forms:
44
+
45
+ | Input | What it is | `project` of a chat |
46
+ |---|---|---|
47
+ | part zips, or their folder | the export as downloaded now, read in place | `claude-ai` |
48
+ | one `.zip` | an older export, everything in one zip | `claude-ai` |
49
+ | an unpacked folder | an export unpacked: `conversations.json`, `design_chats/` | `claude-ai` |
50
+ | an archive folder | one JSON file per conversation: `projects/<project>/chats/`, `chats-unfiled/<year>/`, `design-chats/` | the folder name under `projects/`, or `claude-ai-unfiled` |
51
+
52
+ - **Projects come only from an archive folder.** The export does not record which claude.ai
53
+ project a conversation belongs to. If an archive folder files conversations by project, the
54
+ import uses that folder's name as `project` and the folder's path as `folder`. From zips or an
55
+ unpacked export, every chat gets project `claude-ai` and an empty folder.
56
+ - **Design chats** get project `claude-ai-design` from any of the three inputs.
57
+
58
+ ## 2. Dry run
59
+
60
+ ```bash
61
+ memhouse import claude-ai ~/Downloads/claude-export.zip
62
+ ```
63
+
64
+ The dry run connects to the house, and reads it, but writes nothing. It prints counts only, no
65
+ titles or text:
66
+
67
+ - conversations found, by category;
68
+ - how many are **new**, how many are **already imported**, and how many have **changed since**
69
+ they were imported;
70
+ - how many are **empty** (no message with any text), and therefore skipped;
71
+ - what a `--write` would add: sessions, turns and tool calls;
72
+ - blocks that are never stored (empty text, token budget markers and the like), by reason.
73
+
74
+ Add `--json` to get the same as one JSON object.
75
+
76
+ ## 3. Import
77
+
78
+ ```bash
79
+ memhouse import claude-ai ~/Downloads/claude-export.zip --write
80
+ ```
81
+
82
+ It writes the new conversations as one batch, reads the batch back, and checks that the counts
83
+ match. It prints the batch id and the command that undoes it. Batch ids look like
84
+ `claude-ai-2026-01-15-160405`. `--batch <id>` names one yourself, and an id the house already has
85
+ is refused.
86
+
87
+ Check the result:
88
+
89
+ ```bash
90
+ memhouse q coverage # a claude-ai row: sessions, span, origin import
91
+ memhouse q recent --harness claude-ai
92
+ memhouse q search <words> --harness claude-ai
93
+ ```
94
+
95
+ ## 4. The next export
96
+
97
+ Run the same two commands on the new export. A conversation already in the house is skipped.
98
+ Only conversations the house has never held are written.
99
+
100
+ **Changed conversations** are listed, but not written, unless you ask:
101
+
102
+ - **How a change is detected.** A conversation counts as *changed since* in two cases:
103
+ - **newer:** the export's `updated_at` is later than the one stored. claude.ai moves it when a
104
+ chat gets a new message or a new name;
105
+ - **same time, other content:** `updated_at` is the same, but what the conversation says
106
+ differs. claude.ai can add a summary, or a later export can carry what an earlier copy lacked,
107
+ without moving `updated_at`. The import compares the stored rows with the export's, row by
108
+ row: the conversation's name, mode and start, and each message part, tool call and result.
109
+ It ignores what only says where and when the conversation was imported: project, folder,
110
+ batch, `rev`.
111
+ - **Older is never written.** A conversation whose `updated_at` in the export is earlier than the
112
+ stored one is counted as already imported, and as *older*. Its content is not compared, and
113
+ `--update` leaves the newer stored version in place.
114
+ - **`--update` replaces it whole.** Every stored row of the conversation is tombstoned, and the
115
+ export's version is written above them. A message that is no longer in the export is gone
116
+ from reads. It is not kept as a removed version, because the export you keep is the record.
117
+ - **The project stays.** From zips or an unpacked export, a replaced conversation keeps the
118
+ project the house already has, since the export carries none. From an archive folder it takes
119
+ the folder's.
120
+ - **Without `--update`** the stored version stays as it is.
121
+ - **A stopped `--update` hides what it was replacing.** The old rows are tombstoned before the
122
+ new ones are written. If the run stops in between, those conversations are missing from reads
123
+ until you run the same command again, which writes them above the tombstones.
124
+
125
+ ```bash
126
+ memhouse import claude-ai ~/Downloads/claude-export-2.zip --update # dry run
127
+ memhouse import claude-ai ~/Downloads/claude-export-2.zip --update --write
128
+ ```
129
+
130
+ ## 5. Batches and undo
131
+
132
+ ```bash
133
+ memhouse import claude-ai --list
134
+ memhouse import claude-ai --undo claude-ai-2026-01-15-160405 # dry run: what it would remove
135
+ memhouse import claude-ai --undo claude-ai-2026-01-15-160405 --write
136
+ ```
137
+
138
+ - **What `--list` shows.** Every batch with live sessions in the house, with its span and what
139
+ the house recorded about it. Each batch is recorded in the member's `meta` table under
140
+ `import:<batch>`:
141
+ - `applying`: the write started and has not finished;
142
+ - `applied`: the write finished;
143
+ - `undoing`: an undo started and has not finished (run the same `--undo ... --write` again; it
144
+ keeps the counts the first run found);
145
+ - `undone`: the undo finished.
146
+ - **What `--undo` writes.** A tombstone for every live row of the batch: a full copy at the next
147
+ `rev`, with `is_deleted = 1`. Nothing is deleted outright. Reads hide the rows at once, and the
148
+ shipper's maintenance removes them later.
149
+ - **What an undo leaves.** A conversation that a later batch replaced with `--update` belongs to
150
+ that later batch. Undoing the earlier batch leaves it alone. Undoing the later one removes the
151
+ conversation; import it again to bring it back.
152
+ - **Re-importing after an undo works.** A new import writes above any tombstones it finds, so
153
+ a conversation is never hidden behind its own undo.
154
+ - **A run that stops part way (profile tables).** A session whose rows went in but whose totals
155
+ were not filled yet (`started` empty) is listed as `repair` by the next run, and a `--write`
156
+ fills it, with or without anything new to import. A batch whose run stopped after all its rows
157
+ went in is listed as `stopped`, and that `--write` records it `applied`, whatever it writes
158
+ again with `--update`. A batch whose insert stopped part way stays `applying`: the sessions it
159
+ did not write are new to the next run, which writes them in its own batch.
160
+ - **One at a time per member.** Imports and undos take no lock, so run them one after another on
161
+ a member. Two imports at once can split a new conversation's rows between their two batches
162
+ (undo both and import again). An import beside an undo can record the undo's batch `applied`:
163
+ re-running the undo puts that right while any of its rows are still live.
164
+
165
+ ## What is stored
166
+
167
+ Each conversation is one session: harness `claude-ai`, origin `import`, and the conversation's
168
+ uuid as its id (ref `claude-ai:<uuid>`). Its store is the service, not a machine:
169
+
170
+ - **On the profile tables:** host `s-` plus the first 32 hex of the sha256 of
171
+ `sessions-model/service-host/v0:claude.ai` (sessions-model's service host), `harness_path`
172
+ `https://claude.ai`, `host_label` `claude.ai`, adapter `memhouse-import`. A block is a part of its
173
+ message (`text`, `reasoning` for thinking, `tool_call`, `tool_result`; the summary is a `summary`
174
+ part in a system message of its own), and a tool use is also a row in `calls`.
175
+ - **On layout 3.2:** host and data folder `claude.ai`, and a block is a turn, as the table below
176
+ shows.
177
+
178
+ | From the export | Stored as |
179
+ |---|---|
180
+ | text and thinking blocks | one turn each (`text`, `thinking`) |
181
+ | a tool use | a `tool_call` turn, `Name(args)`, and a row in `calls` |
182
+ | a tool result | a `tool_result` turn with the result as text; its call gets `is_error` and `result_len` |
183
+ | an attachment with extracted text | an extra user `text` turn, `[attachment: <name>]` and the text |
184
+ | attached file names | the turn's `extra` |
185
+ | the conversation's summary | one `summary` turn at the time of the last message |
186
+ | a design chat's messages | one `text` turn each |
187
+
188
+ - **Keys are stable.** A turn's key is the message uuid plus the block's position in the
189
+ message, and a call's is the tool use id. When a tool use has no id, the key is
190
+ `<message uuid>:<position>`, and its result is the next result without an id in the same
191
+ message. Importing the same conversation again produces the same keys.
192
+ - **No tokens or models.** The export carries neither. On the profile no usage row is written. On
193
+ layout 3.2 they read as 0, with `usage_basis` set to
194
+ `none`: unmeasured, not free.
195
+ - **Not imported:** project documents, memories, account data and artifact files. They are not
196
+ conversations.
197
+
198
+ ## How it fits with the rest of memhouse
199
+
200
+ - **The shipper never touches imported sessions.** It captures only the stores on your
201
+ machines, and a `claude.ai` session is in none of them.
202
+ - **`memhouse resume` refuses an imported session.** There is no local session to reopen.
203
+ - **A full share includes them.** A share of your whole house reaches every row of your
204
+ tables, so a teammate you share it with sees your imported conversations too.
@@ -41,8 +41,9 @@ memhouse deploy --local --tag 26.7 # another ClickHouse version
41
41
 
42
42
  It runs ClickHouse in a container bound to loopback, with the data in a named volume. The
43
43
  container's superuser is `memhouse_root`, which administers the house. You ship as a
44
- **member** named after your OS user (the command asks; `--user` sets it). The member holds
45
- one grant, on `mem.<you>_*`. Both credentials go into `~/.memhouse/env`, mode 600, so that
44
+ **member** named after your OS user (the command asks; `--user` sets it). A new house is
45
+ schema 3: the member holds `mem.<you>_*`, its tables in the house, and `SELECT, INSERT` on its two
46
+ capture tables in `mem_archive`. Both credentials go into `~/.memhouse/env`, mode 600, so that
46
47
  inviting someone later needs no flags. It then installs and ships.
47
48
 
48
49
  The user name and password are fixed when the volume is first initialised and reused on
@@ -81,7 +82,7 @@ Useful flags:
81
82
  | `--claude-roots DIR[,DIR]` | ship only these Claude Code config directories |
82
83
  | `--channel team` | follow this npm dist-tag on `update` |
83
84
  | `--keep-admin` | keep the admin credential in the env file (not done by default) |
84
- | `--ensure-schema` | create any room or stats table the house is missing |
85
+ | `--schema 2` / `--schema 3` | for a member not yet in the house: which layout its tables take. A house holding members on both (mid-upgrade) needs it; otherwise the install follows the house |
85
86
 
86
87
  The admin password never needs to be on the command line. See
87
88
  [Admin password](admin-password.md).
@@ -93,7 +94,7 @@ what an invite install ships, pass `--editors` / `--claude-roots` as flags.
93
94
  ### What install checks
94
95
 
95
96
  Before it writes any config, install connects, confirms who the credential is, and checks
96
- that it can reach its rooms. The config is written last, so a refused install leaves no
97
+ that it can reach its tables. The config is written last, so a refused install leaves no
97
98
  half-configured machine behind.
98
99
 
99
100
  Configuration resolves in this order: flags, then exported `MEMHOUSE_*` variables, then
@@ -124,5 +125,9 @@ The path proves nothing on its own: Homebrew's prefix is `/usr/local` on Intel a
124
125
 
125
126
  - `memhouse status`: connection, counts, freshness.
126
127
  - `memhouse start` or `memhouse service install`: keep shipping. See [Daemons](daemons.md).
127
- - `memhouse plugins install claude`: the `/mem:*` skills. See
128
- [Search, resume and skills](search-and-resume.md).
128
+ - `memhouse plugins install claude`: the `/mem:*` skills, for Claude Code only. Codex and the
129
+ other editors' sessions ship all the same; an agent there reads the house with the CLI
130
+ (`memhouse q`, `memhouse room-sql`). See [Search, resume and skills](search-and-resume.md).
131
+ - `memhouse plugins install codex`: for Codex, a short block in its `AGENTS.md` (the rules) and
132
+ a `memhouse` skill (the read recipes). Nothing is written under the Codex home unless you run
133
+ it. See [Codex](search-and-resume.md#codex).
@@ -1,6 +1,6 @@
1
1
  # Invite members
2
2
 
3
- Inviting creates a ClickHouse user and their rooms, and writes one file the invitee
3
+ Inviting creates a ClickHouse user and their tables, and writes one file the invitee
4
4
  installs from. It needs a credential that can create users, which means the server's
5
5
  admin.
6
6
 
@@ -13,15 +13,27 @@ memhouse invite alice --url https://house.example.com
13
13
  loopback: `localhost` on their machine is their machine. Use a LAN address, a hostname
14
14
  or a tunnel. `--allow-local` overrides this, for the case where the invitee is on the
15
15
  same machine.
16
- - **The rooms are named for them:** `mem.alice_sessions`, `mem.alice_messages`, and so on.
17
- Alice is granted exactly `mem.alice_*`, nothing else. A different database:
18
- `--db <name>`.
16
+ - **The tables are named for them:** `mem.alice_session_parts`, `mem.alice_sessions` and four
17
+ more conversation tables, and her capture archive `mem_archive.alice_session_units` and `_session_sources`.
18
+ Alice is granted `mem.alice_*` and her two archive tables, nothing else. A different
19
+ database: `--db <name>`.
20
+ - **The house decides the layout,** as it does for an install: a new house is schema 3, a
21
+ house still on the 0.18 rooms gets rooms (`mem.alice_messages`, …), and a house with
22
+ members on both is refused until `--schema 3` or `--schema 2` says which.
19
23
  - **The file is a credential.** It holds the house URL, alice's user name, a generated
20
24
  password and the database. Hand it over a channel you trust: croc, a password manager.
21
- Not chat or email. `--out <path>` writes it somewhere else.
25
+ Not chat or email. `--out <path>` writes it somewhere else. The invitation page,
26
+ `MEMHOUSE-INVITATION.md`, is written beside it and names that file and the invitee's tables.
22
27
  - **Alice installs with `memhouse install --env invite-alice.env`.** It offers to rotate
23
28
  the password to one only she knows, which works because she is granted `ALTER USER` on
24
29
  herself. Then it deletes the file. Once she rotates, the password you saw stops working.
30
+ - **The invite expires.** The password in the file stops working 7 days after the invite
31
+ (`--expires <days>` sets 1 to 365), counted by the server's clock. The output, the file and
32
+ the invitation page say when. Installing lifts the expiry, whether she changes the password
33
+ or keeps it with `--keep-password`. After the date, `install` refuses the file and tells her
34
+ to ask for a new invite. Her account is still there, so remove it first (`DROP USER alice`, as
35
+ the administrator; `invite` says so when it meets it), then invite her again. Her tables are
36
+ kept and reused.
25
37
 
26
38
  ## Which admin credential
27
39
 
@@ -83,5 +95,7 @@ memhouse members --db other # another database
83
95
  ## Removing a member
84
96
 
85
97
  There is no `memhouse` verb for it yet. With the admin credential, `DROP USER <name>`
86
- stops every machine shipping as them. Their rooms stay until you drop them too. See
87
- [Operate a house](operate.md) before you drop anything: there is no undo.
98
+ stops every machine shipping as them. Their shares and tables stay until you remove them too,
99
+ in `mem` and in `mem_archive`, shares first: [Operate a house](operate.md#removing-a-member) has
100
+ the order and lists the tables by exact name. Read it before you drop anything: there is no
101
+ undo.