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.
- package/AGENTS.md +22 -151
- package/CHANGELOG.md +263 -29
- package/LICENSE +202 -22
- package/LICENSES/agentlytics-MIT.txt +21 -0
- package/LICENSES/memhouse-MIT-until-0.18.11.txt +22 -0
- package/LICENSES/models.dev-MIT.txt +22 -0
- package/NOTICE +28 -0
- package/README.md +42 -20
- package/SECURITY.md +21 -8
- package/bin/memhouse.js +2223 -311
- package/docs/README.md +11 -9
- package/docs/guides/admin-password.md +37 -10
- package/docs/guides/configuration.md +13 -6
- package/docs/guides/daemons.md +64 -3
- package/docs/guides/import-claude-ai.md +204 -0
- package/docs/guides/install.md +11 -6
- package/docs/guides/invite.md +21 -7
- package/docs/guides/operate.md +114 -7
- package/docs/guides/search-and-resume.md +72 -3
- package/docs/guides/share.md +91 -52
- package/docs/guides/storage.md +44 -4
- package/docs/guides/troubleshooting.md +121 -14
- package/docs/guides/uninstall.md +4 -3
- package/docs/guides/update.md +16 -0
- package/docs/guides/upgrade-to-0.20.md +188 -0
- package/docs/releases/README.md +2 -1
- package/docs/releases/v0.18.10.md +5 -5
- package/docs/releases/v0.18.11.md +3 -3
- package/docs/releases/v0.20.0.md +627 -0
- package/docs/releases/v0.20.1.md +114 -0
- package/docs/tutorials/01-first-house.md +10 -7
- package/docs/tutorials/02-join-a-house.md +24 -15
- package/docs/tutorials/03-share-your-memory.md +40 -31
- package/editors/antigravity.js +1 -80
- package/editors/base.js +463 -9
- package/editors/bridge.js +119 -0
- package/editors/claude.js +273 -105
- package/editors/codex.js +278 -52
- package/editors/copilot.js +1 -70
- package/editors/cursor.js +1 -75
- package/editors/derive-fingerprints.json +18 -0
- package/editors/harness-paths.js +69 -0
- package/editors/index.js +1 -20
- package/editors/normalise/agy.js +910 -0
- package/editors/normalise/copilot-cli.js +321 -0
- package/editors/normalise/cursor-agent.js +852 -0
- package/editors/normalise/cursor.js +474 -0
- package/editors/normalise/devin.js +575 -0
- package/editors/normalise/gemini-cli.js +602 -0
- package/editors/normalise/goose.js +673 -0
- package/editors/normalise/opencode.js +633 -0
- package/editors/normalise/vscode.js +549 -0
- package/editors/normalise/zed.js +473 -0
- package/editors/opencode-v4.js +119 -0
- package/editors/scope.js +14 -3
- package/editors/sessions-log.js +349 -0
- package/editors/sqlite.js +28 -1
- package/editors/vscode.js +1 -72
- package/editors/windsurf.js +1 -143
- package/examples/01-local-one-machine/README.md +24 -19
- package/examples/01-local-one-machine/run.sh +1 -1
- package/examples/02-existing-clickhouse/README.md +30 -22
- package/examples/02-existing-clickhouse/run.sh +5 -4
- package/examples/03-team-house/README.md +44 -37
- package/examples/03-team-house/run.sh +35 -18
- package/examples/04-self-hosted-team/README.md +29 -14
- package/examples/04-self-hosted-team/compose.yml +5 -2
- package/examples/04-self-hosted-team/smoke.sh +26 -12
- package/examples/README.md +8 -4
- package/examples/fixtures/README.md +4 -1
- package/examples/fixtures/make-sessions.js +8 -5
- package/memhouse/admin-secret.js +89 -24
- package/memhouse/argv-secrets.js +64 -0
- package/memhouse/channel.js +49 -9
- package/memhouse/codex-plugin.js +194 -0
- package/memhouse/delivery/AGENT-INSTALL-PROMPT.md +2 -1
- package/memhouse/delivery/AGENT-INSTALL.md +22 -11
- package/memhouse/delivery/INVITATION.md.tpl +44 -45
- package/memhouse/delivery/PROMPT.md +53 -5
- package/memhouse/delivery/UPGRADE.md.tpl +1 -1
- package/memhouse/delivery/codex/AGENTS-BLOCK.md +11 -0
- package/memhouse/delivery/codex/SKILL.md +50 -0
- package/memhouse/delivery/kernel-install.md +17 -9
- package/memhouse/delivery/plugin/.claude-plugin/plugin.json +2 -2
- package/memhouse/delivery/plugin/reference/HOUSE.md +168 -41
- package/memhouse/delivery/plugin/skills/access/SKILL.md +13 -5
- package/memhouse/delivery/plugin/skills/admin/SKILL.md +14 -6
- package/memhouse/delivery/plugin/skills/house/SKILL.md +25 -8
- package/memhouse/delivery/plugin/skills/recall/SKILL.md +66 -8
- package/memhouse/delivery/plugin/skills/sql/SKILL.md +66 -7
- package/memhouse/deploy.js +1 -1
- package/memhouse/flags.js +35 -18
- package/memhouse/help.js +37 -0
- package/memhouse/host.js +82 -2
- package/memhouse/house/HOUSE.md +2 -2
- package/memhouse/house/convert-profile.js +638 -0
- package/memhouse/house/convert-v3.js +742 -0
- package/memhouse/house/doctor-v3.js +324 -0
- package/memhouse/house/host-alias.js +85 -0
- package/memhouse/house/house.js +3 -3
- package/memhouse/house/migrate.js +2 -2
- package/memhouse/house/profile-dedupe.js +183 -0
- package/memhouse/house/profile-edges.js +554 -0
- package/memhouse/house/profile-house.js +430 -0
- package/memhouse/house/profile-rollup.js +190 -0
- package/memhouse/house/q-cli.js +863 -0
- package/memhouse/house/queries-v3.js +1013 -0
- package/memhouse/house/reconcile-v3.js +192 -0
- package/memhouse/house/records-to-profile.js +174 -0
- package/memhouse/house/records-to-rows.js +169 -0
- package/memhouse/house/relocate.js +2 -1
- package/memhouse/house/schema-choice.js +121 -0
- package/memhouse/house/schema-profile.js +290 -0
- package/memhouse/house/schema-profile.sql.tpl +398 -0
- package/memhouse/house/schema-v3.js +267 -0
- package/memhouse/house/schema-v3.sql.tpl +213 -0
- package/memhouse/import/claude-ai.js +420 -0
- package/memhouse/import/import.js +372 -0
- package/memhouse/import/pyjson.js +206 -0
- package/memhouse/import/records.js +64 -0
- package/memhouse/import/writer-profile.js +192 -0
- package/memhouse/import/writer-v3.js +240 -0
- package/memhouse/import/zip.js +112 -0
- package/memhouse/invitation.js +102 -0
- package/memhouse/invitefile.js +5 -2
- package/memhouse/plugin-copies.js +176 -0
- package/memhouse/provision.js +7 -2
- package/memhouse/release-hint.js +24 -0
- package/memhouse/resume.js +4 -4
- package/memhouse/server/api-v3.js +632 -0
- package/memhouse/server/dashboard-auth.js +181 -0
- package/memhouse/server/dashboard-port.js +91 -0
- package/memhouse/server/queries.js +30 -0
- package/memhouse/server/server.js +118 -20
- package/memhouse/service.js +1 -1
- package/memhouse/share.js +687 -1
- package/memhouse/shipper/adapter-capture.js +756 -0
- package/memhouse/shipper/capture.js +2571 -0
- package/memhouse/shipper/dedupe-profile.js +237 -0
- package/memhouse/shipper/dedupe-v3.js +1027 -0
- package/memhouse/shipper/forks.js +233 -0
- package/memhouse/shipper/log-adapter.js +219 -0
- package/memhouse/shipper/maintenance.js +49 -0
- package/memhouse/shipper/normalise-run.js +148 -0
- package/memhouse/shipper/passlog.js +68 -5
- package/memhouse/shipper/profile-capture.js +903 -0
- package/memhouse/shipper/ship.js +91 -8
- package/memhouse/shipper/snapshots.js +193 -0
- package/memhouse/shipper/source-roots.js +111 -0
- package/memhouse/version-notice.js +66 -0
- package/package.json +14 -7
- package/public/THIRD_PARTY_NOTICES.txt +2 -2
- package/public/assets/index-CcNnhfM3.js +78 -0
- package/public/index.html +1 -1
- package/ui/package-lock.json +56 -37
- package/ui/package.json +1 -0
- package/ui/src/App.jsx +29 -14
- package/ui/src/components/ChatSidebar.jsx +69 -12
- package/ui/src/lib/api.js +76 -0
- package/ui/src/lib/dashboard-token.js +41 -0
- package/ui/src/main.jsx +22 -5
- package/ui/src/pages/Dashboard.jsx +22 -36
- package/ui/third-party-notices.js +119 -0
- package/ui/vite.config.js +5 -1
- package/docs/releases/v0.18.12.md +0 -59
- package/memhouse/COMPETITION.md +0 -221
- package/memhouse/DESIGN.md +0 -222
- package/memhouse/RESUME-AND-UPDATE.md +0 -182
- package/public/assets/index-CnWzrjF3.js +0 -78
package/docs/guides/operate.md
CHANGED
|
@@ -26,8 +26,18 @@ GROUP BY table;
|
|
|
26
26
|
|
|
27
27
|
-- what someone may do
|
|
28
28
|
SHOW GRANTS FOR alice;
|
|
29
|
+
|
|
30
|
+
-- every scoped share's filter, and any left behind on a table that is gone
|
|
31
|
+
SELECT database, short_name, table, select_filter, apply_to_list,
|
|
32
|
+
(database, table) NOT IN (SELECT database, name FROM system.tables) AS table_gone
|
|
33
|
+
FROM system.row_policies
|
|
34
|
+
ORDER BY database, table;
|
|
29
35
|
```
|
|
30
36
|
|
|
37
|
+
A policy whose table is gone is dropped with `DROP ROW POLICY <short_name> ON <db>.<table>`:
|
|
38
|
+
dropping a table leaves its policies, and a table later made under the same name inherits them.
|
|
39
|
+
Members do not hold `SHOW ROW POLICIES`, so this listing is the admin's.
|
|
40
|
+
|
|
31
41
|
## Adding and removing people
|
|
32
42
|
|
|
33
43
|
- **Add:** [`memhouse invite`](invite.md).
|
|
@@ -35,8 +45,57 @@ SHOW GRANTS FOR alice;
|
|
|
35
45
|
member who is locked out there is no verb: run `ALTER USER <name> IDENTIFIED BY '…'` as
|
|
36
46
|
the admin, and hand them an env file with the new password. Every machine shipping as
|
|
37
47
|
them stops until its env file has it.
|
|
38
|
-
- **Remove:** there is no verb yet.
|
|
39
|
-
|
|
48
|
+
- **Remove:** there is no verb yet. Follow [Removing a member](#removing-a-member): their
|
|
49
|
+
shares first, then `DROP USER <name>`, which stops every machine shipping as them, then their
|
|
50
|
+
tables, in **both** databases.
|
|
51
|
+
|
|
52
|
+
### Removing a member
|
|
53
|
+
|
|
54
|
+
In this order. Each step is irreversible, and the last one deletes the member's memory.
|
|
55
|
+
|
|
56
|
+
1. **Their shares.** Run as the member, while the account still exists:
|
|
57
|
+
`memhouse share <reader> --revoke` for each reader `memhouse share --list` names. A share's
|
|
58
|
+
grants and row policies are kept by name, and neither is dropped with the tables: one left
|
|
59
|
+
behind reaches, or filters, a later table of the same name. If the member cannot run it, find
|
|
60
|
+
both as the admin (the list below is every table name the member can have), and remove each
|
|
61
|
+
with `REVOKE … ON <db>.<table> FROM <reader>` and `DROP ROW POLICY <name> ON <db>.<table>`:
|
|
62
|
+
|
|
63
|
+
```sql
|
|
64
|
+
WITH ['alice_session_parts', 'alice_session_usage', 'alice_session_links',
|
|
65
|
+
'alice_session_lifecycle', 'alice_calls', 'alice_sessions', 'alice_meta', 'alice_events',
|
|
66
|
+
'alice_session_stats', 'alice_session_model_stats', 'alice_session_tool_stats',
|
|
67
|
+
'alice_messages', 'alice_tool_calls', 'alice_turns',
|
|
68
|
+
'alice_session_units', 'alice_session_sources', 'alice_records', 'alice_files',
|
|
69
|
+
'alice_messages_pre_v3', 'alice_tool_calls_pre_v3', 'alice_sessions_pre_v3'] AS mine
|
|
70
|
+
SELECT 'grant' AS kind, user_name AS who, access_type AS what, database, table FROM system.grants
|
|
71
|
+
WHERE database IN ('mem', 'mem_archive') AND has(mine, table) AND user_name != 'alice'
|
|
72
|
+
UNION ALL
|
|
73
|
+
SELECT 'policy', short_name, '', database, table FROM system.row_policies
|
|
74
|
+
WHERE database IN ('mem', 'mem_archive') AND has(mine, table);
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
A 0.18 full share is a wildcard grant, `mem.alice_*`: `SHOW GRANTS FOR <reader>` shows it.
|
|
78
|
+
2. **The user:** `DROP USER alice`. Every machine shipping as them stops.
|
|
79
|
+
3. **Their tables.** List them **by exact name**, never by prefix: `LIKE 'alice_%'` also matches
|
|
80
|
+
another member called `alice_smith`. The same list as above:
|
|
81
|
+
|
|
82
|
+
```sql
|
|
83
|
+
WITH [ … the same names … ] AS mine
|
|
84
|
+
SELECT database, name, total_rows FROM system.tables
|
|
85
|
+
WHERE database IN ('mem', 'mem_archive') AND has(mine, name) ORDER BY database, name;
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Then drop each by its full name (`DROP TABLE mem.alice_session_parts`, …). Which of them
|
|
89
|
+
exist depends on the member's history:
|
|
90
|
+
- **on schema 3:** the six conversation tables in `mem`, plus `_meta` and `_events` once
|
|
91
|
+
the member has run `memhouse share` (step 1 does), and `_session_units` and
|
|
92
|
+
`_session_sources` in `mem_archive`;
|
|
93
|
+
- **from a 0.20 development build:** also `_turns` in `mem`, and `_records` and `_files` in
|
|
94
|
+
`mem_archive`;
|
|
95
|
+
- **converted from 0.18:** also `_meta`, `_events` and the three stats tables in `mem`, and
|
|
96
|
+
the three renamed rooms (`*_pre_v3`) in `mem_archive`;
|
|
97
|
+
- **still on the 0.18 rooms:** `_messages`, `_tool_calls`, `_sessions`, `_meta`, `_events` and
|
|
98
|
+
the stats tables, all in `mem`.
|
|
40
99
|
|
|
41
100
|
**Before any destructive statement, look first.**
|
|
42
101
|
|
|
@@ -85,10 +144,58 @@ Afterwards, point each member at the new URL with `memhouse setup --url …`.
|
|
|
85
144
|
|
|
86
145
|
## Backups
|
|
87
146
|
|
|
88
|
-
memhouse does not back up the house. Use ClickHouse's own `BACKUP
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
147
|
+
memhouse does not back up the house. Use ClickHouse's own `BACKUP`, to a disk or an object
|
|
148
|
+
store, on a schedule. **A schema-3 house is two databases:** `mem`, and `mem_archive`, the
|
|
149
|
+
capture archive every session is derived from. A backup of `mem` alone leaves the archive out.
|
|
150
|
+
The accounts (users, their grants, the row policies of scoped shares) live outside both, so
|
|
151
|
+
take them in the same statement:
|
|
152
|
+
|
|
153
|
+
```sql
|
|
154
|
+
BACKUP DATABASE mem, DATABASE mem_archive,
|
|
155
|
+
TABLE system.users, TABLE system.roles, TABLE system.row_policies,
|
|
156
|
+
TABLE system.settings_profiles, TABLE system.quotas
|
|
157
|
+
TO File('mem-2026-10-27.zip')
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
`File(…)` works on a stock server: the official image's configuration allows backups under
|
|
161
|
+
`backups/` in the server's data directory (`/var/lib/clickhouse/backups/` in the image), and
|
|
162
|
+
that is where the file lands. It is on the server's own disk, so copy it somewhere else. With a
|
|
163
|
+
backups disk configured, write `TO Disk('backups', '…')` instead; a stock server refuses
|
|
164
|
+
`Disk(…)` ("the 'backups.allowed_disk' configuration parameter is not set").
|
|
165
|
+
[`examples/04-self-hosted-team`](../../examples/04-self-hosted-team/README.md) shows such a
|
|
166
|
+
disk, with a nightly job, rotation, and a restore onto a fresh server. A house still on the
|
|
167
|
+
0.18 rooms has no `mem_archive` until its upgrade begins: leave `DATABASE mem_archive` out until
|
|
168
|
+
then.
|
|
169
|
+
|
|
170
|
+
**Check a backup** without touching the live house: restore its two databases under other names,
|
|
171
|
+
compare, and drop the copies.
|
|
172
|
+
|
|
173
|
+
```sql
|
|
174
|
+
RESTORE DATABASE mem AS mem_check, DATABASE mem_archive AS mem_archive_check
|
|
175
|
+
FROM File('mem-2026-10-27.zip');
|
|
176
|
+
|
|
177
|
+
-- every table on both sides
|
|
178
|
+
SELECT database, count() FROM system.tables
|
|
179
|
+
WHERE database IN ('mem', 'mem_check', 'mem_archive', 'mem_archive_check') GROUP BY database;
|
|
180
|
+
|
|
181
|
+
-- rows per table, live against restored
|
|
182
|
+
SELECT replaceRegexpOne(database, '_check$', '') AS db, table,
|
|
183
|
+
sumIf(rows, NOT endsWith(database, '_check')) AS live,
|
|
184
|
+
sumIf(rows, endsWith(database, '_check')) AS restored
|
|
185
|
+
FROM system.parts
|
|
186
|
+
WHERE active AND database IN ('mem', 'mem_check', 'mem_archive', 'mem_archive_check')
|
|
187
|
+
GROUP BY db, table ORDER BY db, table;
|
|
188
|
+
|
|
189
|
+
DROP DATABASE mem_check;
|
|
190
|
+
DROP DATABASE mem_archive_check;
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Each database and its `_check` copy should hold the same number of tables, and `restored` should
|
|
194
|
+
be close to `live` on every table (the house may have grown since the backup). No
|
|
195
|
+
`mem_archive_check` in the first result, or a table with no `restored` rows where `live` has
|
|
196
|
+
many, means the backup does not hold what you think. For a house still on the 0.18 rooms, leave
|
|
197
|
+
`DATABASE mem_archive AS mem_archive_check` out of the `RESTORE` too. These statements were
|
|
198
|
+
checked on ClickHouse 26.7.
|
|
92
199
|
|
|
93
200
|
- **Keep backups as private as the house.** They hold every member's transcripts.
|
|
94
201
|
- **Backups outlive deletions.** A row removed from the house remains in every backup
|
|
@@ -98,7 +205,7 @@ rotation, and a tested restore.
|
|
|
98
205
|
|
|
99
206
|
| setting | why |
|
|
100
207
|
|---|---|
|
|
101
|
-
| async inserts off for members |
|
|
208
|
+
| async inserts off for members | the writing user is stamped at insert (`writer` on schema 3, `user_id` on the 0.18 rooms); memhouse pins `async_insert=0` on each member, and the shipper sets it per insert too |
|
|
102
209
|
| `users_without_row_policies_can_read_rows` | scoped shares need it to behave as memhouse expects; `share` measures it and refuses if a policy would blind the owner |
|
|
103
210
|
| `old_parts_lifetime` | how long replaced parts stay on disk after merges and mutations (480 s by default) |
|
|
104
211
|
| HTTP behind TLS | members connect over HTTP(S); put the server behind TLS before exposing it beyond a LAN |
|
|
@@ -10,6 +10,26 @@ memhouse search clickhouse auth --limit 50
|
|
|
10
10
|
This is a full-text search across every session in your rooms, from every machine and
|
|
11
11
|
every editor. Each hit names the session id, project, date and the matching line.
|
|
12
12
|
|
|
13
|
+
### On schema 3: the named queries
|
|
14
|
+
|
|
15
|
+
A house on schema 3 (every new house from 0.20) also answers named queries, the same ones the
|
|
16
|
+
`/mem` skills use:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
memhouse q # the list, with each one's usage
|
|
20
|
+
memhouse q recent --limit 20 # the latest sessions
|
|
21
|
+
memhouse q search clickhouse auth
|
|
22
|
+
memhouse q session <ref> # one session, page by page
|
|
23
|
+
memhouse q spend --by model # tokens and cost
|
|
24
|
+
memhouse q removed # what is kept as removed versions
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
- **A session's ref names its store,** `<harness>:<session_id>~<token>`, because the same id
|
|
28
|
+
on another machine, or in another data folder, is another session. `q recent` and
|
|
29
|
+
`q search` print full refs; a bare id still works where only one session has it.
|
|
30
|
+
- **`q removed`** lists what every other read hides: a message an editor reverted, rewound
|
|
31
|
+
or deleted, kept as a removed version. Narrow it with a ref, `--harness` or `--days`.
|
|
32
|
+
|
|
13
33
|
## Resume
|
|
14
34
|
|
|
15
35
|
```bash
|
|
@@ -35,7 +55,7 @@ memhouse stats # sessions, messages and tokens per editor
|
|
|
35
55
|
memhouse sessions-query # the per-session rollup as SQL, to paste into your own queries
|
|
36
56
|
```
|
|
37
57
|
|
|
38
|
-
The dashboard (`memhouse start`, then
|
|
58
|
+
The dashboard (`memhouse start`, then `memhouse dashboard`) shows the same and more:
|
|
39
59
|
|
|
40
60
|
- cost by model, editor and project;
|
|
41
61
|
- tool-call rankings;
|
|
@@ -55,6 +75,11 @@ memhouse plugins list
|
|
|
55
75
|
memhouse plugins remove claude
|
|
56
76
|
```
|
|
57
77
|
|
|
78
|
+
Each copy is bound to the install that put it there, and kept at that install's version: after
|
|
79
|
+
an upgrade, by `memhouse update` or by `npm install -g`, the first `memhouse` command (or the
|
|
80
|
+
shipper's restart) replaces it with the new version's skills. `memhouse doctor` names any copy
|
|
81
|
+
it could not refresh.
|
|
82
|
+
|
|
58
83
|
The five skills:
|
|
59
84
|
|
|
60
85
|
| skill | for |
|
|
@@ -74,11 +99,55 @@ missing skill never announces itself.
|
|
|
74
99
|
For an agent that does not use Claude Code skills, `memhouse prompt` prints a snippet for
|
|
75
100
|
its system prompt: where memory lives and how to query it.
|
|
76
101
|
|
|
102
|
+
### Codex
|
|
103
|
+
|
|
104
|
+
The `/mem` skills are for Claude Code. For Codex:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
memhouse plugins install codex # under CODEX_HOME, or ~/.codex
|
|
108
|
+
memhouse plugins list # shows the codex copy and the command it runs
|
|
109
|
+
memhouse plugins remove codex
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
It writes two things, and only when you run it:
|
|
113
|
+
|
|
114
|
+
- **A block at the top of `AGENTS.md`**, between `<!-- memhouse:begin … -->` and
|
|
115
|
+
`<!-- memhouse:end -->`. Codex puts this file in every prompt, so the block holds only what must
|
|
116
|
+
always hold: never read or print `~/.memhouse/env` or a `MEMHOUSE_*` value; `MEMHOUSE_URL` is the
|
|
117
|
+
house and `MEMHOUSE_PORT` the dashboard; questions about past sessions go to the `memhouse`
|
|
118
|
+
skill. The file is created if it is missing. Installing again replaces the block where it
|
|
119
|
+
stands. Removing takes out the block and the blank line after it, so the rest of the file is
|
|
120
|
+
what it was, byte for byte; a file that held only the block is deleted, or, when `AGENTS.md`
|
|
121
|
+
is a symlink, its target is emptied and the link kept. Leave the markers
|
|
122
|
+
alone: with one of them missing, install and remove refuse and change nothing.
|
|
123
|
+
- **A skill, `skills/memhouse/`**, with the read recipes: `memhouse q`, `--member` for a
|
|
124
|
+
housemate's sessions, `memhouse room-sql`. Codex lists it by name and description and reads
|
|
125
|
+
the rest when it uses it. The directory carries `.memhouse.json`; a `skills/memhouse` without
|
|
126
|
+
it is someone else's, and install refuses to replace it and remove leaves it.
|
|
127
|
+
|
|
128
|
+
For an instance with its own home or binary, both name it: `MEMHOUSE_HOME=<home> <binary> q …`.
|
|
129
|
+
After an upgrade, the first `memhouse` command refreshes both, as it does the `/mem` copies; a
|
|
130
|
+
block you took out by hand stays out.
|
|
131
|
+
|
|
77
132
|
## Writing your own queries
|
|
78
133
|
|
|
79
|
-
|
|
134
|
+
On schema 3 (0.20), the tables are `mem.<you>_session_parts` (one row per part of a message:
|
|
135
|
+
text, thinking, a tool call or its result), `mem.<you>_session_usage` (one row per usage
|
|
136
|
+
record), `mem.<you>_calls` and `mem.<you>_sessions` (one row per session, with its totals),
|
|
137
|
+
with `_session_links` and `_session_lifecycle` beside them. Their columns are described in
|
|
138
|
+
[`memhouse/house/schema-profile.sql.tpl`](../../memhouse/house/schema-profile.sql.tpl). Three rules:
|
|
139
|
+
|
|
140
|
+
1. **Read with `FINAL`, and keep `is_deleted = 0 AND removed = 0`.** Removed content is
|
|
141
|
+
kept as removed versions (`memhouse q removed` lists them); it is not current.
|
|
142
|
+
2. **A session is its store:** `(host, harness, harness_path, session_id, incarnation)`.
|
|
143
|
+
A bare `session_id` can repeat across editors and machines.
|
|
144
|
+
3. **Count a session's messages as its text parts (`kind = 'text'`), as `sessions.messages`
|
|
145
|
+
does, and take its totals from `sessions`.** A forked session's copied prefix is billed once, in `sessions`;
|
|
146
|
+
summing the parts or the usage rows of a fork and its parent counts that prefix twice.
|
|
147
|
+
|
|
148
|
+
On the 0.18 rooms, the rooms are documented column by column in
|
|
80
149
|
[`memhouse/house/HOUSE.md`](../../memhouse/house/HOUSE.md). Two rules matter for every
|
|
81
|
-
query:
|
|
150
|
+
query there:
|
|
82
151
|
|
|
83
152
|
1. **Join on `(session_id, user_id)`**, never on `session_id` alone. Two members' editors
|
|
84
153
|
can produce the same local session id.
|
package/docs/guides/share.md
CHANGED
|
@@ -1,29 +1,32 @@
|
|
|
1
1
|
# Share and revoke
|
|
2
2
|
|
|
3
|
-
A share lets another member of the same server read your
|
|
3
|
+
A share lets another member of the same server read your sessions. It is a ClickHouse
|
|
4
4
|
`GRANT SELECT`, so it is read-only, and you can withdraw it. Run these on a machine
|
|
5
|
-
installed as yourself. Each command acts on your own
|
|
5
|
+
installed as yourself. Each command acts on your own tables only.
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
memhouse share bob # full:
|
|
9
|
-
memhouse share bob --only project=billing # scoped:
|
|
8
|
+
memhouse share bob # full: every session
|
|
9
|
+
memhouse share bob --only project=billing # scoped: the sessions that match
|
|
10
10
|
memhouse share --list # what memhouse has recorded
|
|
11
11
|
memhouse share bob --revoke # take it back
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
This page describes a 0.20 house (schema 3). For a house still on the 0.18 rooms, see
|
|
15
|
+
[the last section](#a-house-on-the-018-rooms).
|
|
16
|
+
|
|
17
|
+
## What a share reaches
|
|
18
|
+
|
|
19
|
+
Full or scoped, a share is `SELECT` on exactly your six conversation tables:
|
|
20
|
+
`<you>_session_parts`, `<you>_session_usage`, `<you>_session_links`, `<you>_session_lifecycle`,
|
|
21
|
+
`<you>_calls` and `<you>_sessions`. Never a wildcard, never the database.
|
|
15
22
|
|
|
16
23
|
| | full (`share bob`) | scoped (`share bob --only …`) |
|
|
17
24
|
|---|---|---|
|
|
18
|
-
| grant | `SELECT
|
|
19
|
-
| rows | all | those matching the scope, through row
|
|
20
|
-
|
|
|
21
|
-
| `<you>_meta`, `<you>_events` |
|
|
22
|
-
|
|
|
23
|
-
|
|
24
|
-
**A full share is for someone you would hand your laptop to.** It reaches every table in
|
|
25
|
-
your pattern, including ones memhouse adds in a later release. The command says so when
|
|
26
|
-
you run it.
|
|
25
|
+
| grant | `SELECT` on the six tables | `SELECT` on the six tables |
|
|
26
|
+
| rows | all | those matching the scope, through a row policy on each table |
|
|
27
|
+
| capture archive (`<db>_archive`) | not granted | not granted |
|
|
28
|
+
| `<you>_meta`, `<you>_events` | not granted | not granted |
|
|
29
|
+
| tables added later | not granted | not granted |
|
|
27
30
|
|
|
28
31
|
## Scopes
|
|
29
32
|
|
|
@@ -31,31 +34,20 @@ you run it.
|
|
|
31
34
|
|
|
32
35
|
| key | filters on |
|
|
33
36
|
|---|---|
|
|
34
|
-
| `project=` | the project name |
|
|
35
|
-
| `folder=` | the working directory |
|
|
36
|
-
| `session=` | one session id |
|
|
37
|
-
| `host=` | one machine
|
|
38
|
-
| `
|
|
39
|
-
| `since=` / `until=` | a date range: message
|
|
40
|
-
|
|
41
|
-
`
|
|
37
|
+
| `project=` | the project name. Only message parts, calls and sessions carry one: a project scope passes no usage, link or lifecycle rows, and the `sessions` row carries the totals |
|
|
38
|
+
| `folder=` | the working directory, on the same three tables as `project=` |
|
|
39
|
+
| `session=` | one session: a ref with its store (`<harness>:<id>~…`, as `memhouse q recent` prints it), or a bare id, which matches that id in every store |
|
|
40
|
+
| `host=` | one machine, by its id (`memhouse doctor` lists it under "host identity") |
|
|
41
|
+
| `harness=` | one editor, by the stored value (below); `source=` means the same |
|
|
42
|
+
| `since=` / `until=` | a date range: the row's own time for message parts, usage, calls and lifecycle records, the start for `sessions`. A row with no time, and every link, never matches |
|
|
43
|
+
|
|
44
|
+
`harness=` compares against the stored value, which is not always the adapter name you
|
|
42
45
|
use in `MEMHOUSE_EDITORS`. Claude Code rows, for example, say `claude-code`, not `claude`.
|
|
43
|
-
|
|
46
|
+
To see what your tables hold:
|
|
44
47
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
| Codex | `codex` |
|
|
49
|
-
| Cursor / Cursor Agent | `cursor` / `cursor-agent` |
|
|
50
|
-
| VS Code / VS Code Insiders | `vscode` / `vscode-insiders` |
|
|
51
|
-
| GitHub Copilot / Copilot for JetBrains | `copilot-cli` / `copilot-jetbrains` |
|
|
52
|
-
| Gemini CLI | `gemini-cli` |
|
|
53
|
-
| Zed, OpenCode, Goose, Kiro, Codebuff, Antigravity | `zed`, `opencode`, `goose`, `kiro`, `codebuff`, `antigravity` |
|
|
54
|
-
| Devin / Devin Next | `devin` / `devin-next` |
|
|
55
|
-
| Command Code | `commandcode` |
|
|
56
|
-
|
|
57
|
-
To see what your rooms actually hold:
|
|
58
|
-
`SELECT source, count() FROM mem.<you>_sessions FINAL GROUP BY source`.
|
|
48
|
+
```sql
|
|
49
|
+
SELECT harness, count() FROM mem.<you>_sessions FINAL GROUP BY harness
|
|
50
|
+
```
|
|
59
51
|
|
|
60
52
|
An empty scope (`--only ""`, or `--only "$SCOPE"` with the variable unset) is refused.
|
|
61
53
|
It never falls back to a full share.
|
|
@@ -68,35 +60,82 @@ your full-share grantees. Before it creates the first policy, `share` measures h
|
|
|
68
60
|
server behaves, and refuses if a policy would blind you. The fix is server configuration
|
|
69
61
|
(`users_without_row_policies_can_read_rows`), and it is the administrator's to make.
|
|
70
62
|
|
|
63
|
+
## The order, and what a failure leaves
|
|
64
|
+
|
|
65
|
+
- **Scoped:** the row policies are written first, then the grants. If a policy cannot be
|
|
66
|
+
written, nothing is granted.
|
|
67
|
+
- **Full:** the grants first, then any leftover policies are dropped. Until they are
|
|
68
|
+
dropped they only narrow.
|
|
69
|
+
- **A reader who still holds your 0.18 share** (`<you>_*`, from before the conversion): a
|
|
70
|
+
new share refuses, because it would sit beside that grant and narrow nothing. So does a
|
|
71
|
+
share when what the reader holds cannot be read. Revoke first, or, for a full share memhouse
|
|
72
|
+
recorded, the admin moves it with `migrate --v3-shares`. In a house where member names
|
|
73
|
+
collide, a revoke may leave the grant and `--v3-shares` refuses: see
|
|
74
|
+
[Troubleshooting](troubleshooting.md#two-member-names-that-collide).
|
|
75
|
+
- If a grant fails part-way, memhouse withdraws what it granted. If it cannot confirm that,
|
|
76
|
+
the filters stay, and it tells you to retry or revoke.
|
|
77
|
+
|
|
71
78
|
## Changing a share
|
|
72
79
|
|
|
73
80
|
- **Scoped to full:** `memhouse share bob`.
|
|
74
|
-
- **Full to scoped:** `memhouse share bob --only …`. The
|
|
75
|
-
|
|
81
|
+
- **Full to scoped:** `memhouse share bob --only …`. The filters are written first, so
|
|
82
|
+
bob's view narrows before anything else changes.
|
|
76
83
|
- **One scope to another:** run `--only` again with the new scope. It replaces the
|
|
77
84
|
policies.
|
|
78
85
|
|
|
79
|
-
**Scoped shares made before 0.18.10** granted the whole pattern while filtering only three
|
|
80
|
-
rooms. Re-run `memhouse share <user> --only <scope>` once for each, and it is narrowed to
|
|
81
|
-
the three rooms.
|
|
82
|
-
|
|
83
86
|
## Revoking
|
|
84
87
|
|
|
85
|
-
`--revoke` withdraws the grants
|
|
86
|
-
policies. If the server refuses a step, the policies are kept
|
|
87
|
-
|
|
88
|
+
`--revoke` withdraws the grants on the six tables and on your 0.18 rooms, then drops the
|
|
89
|
+
row policies. If the server refuses a step, the policies are kept, so a failed revoke never
|
|
90
|
+
leaves an unfiltered share behind.
|
|
91
|
+
|
|
92
|
+
A 0.18 wildcard grant the reader still holds (`<you>_*`) is taken back too, unless it also
|
|
93
|
+
matches another member's tables (see [Troubleshooting](troubleshooting.md#two-member-names-that-collide))
|
|
94
|
+
or a table memhouse did not make for you. A REVOKE on the wildcard would strip the reader's
|
|
95
|
+
grants on those too, so the revoke leaves it for the admin and says so. It does the same when
|
|
96
|
+
the reader's grants cannot be read, or the house's tables or users cannot be listed. The admin then
|
|
97
|
+
takes it back by hand: the share is recorded as revoked, and `migrate --v3-shares` refuses a
|
|
98
|
+
revoked share that still holds a grant.
|
|
88
99
|
|
|
89
100
|
A revoke cannot recall what was already read.
|
|
90
101
|
|
|
91
102
|
## What `--list` shows
|
|
92
103
|
|
|
93
|
-
`share --list` reads memhouse's own record, the `share:` keys in `<you>_meta
|
|
94
|
-
someone made by hand in SQL does not appear there. A member cannot read
|
|
95
|
-
an administrator can, with `SHOW GRANTS FOR <user
|
|
104
|
+
`share --list` reads memhouse's own record, the `share:` keys in `<you>_meta`, a share's scope
|
|
105
|
+
included. A grant someone made by hand in SQL does not appear there. A member cannot read
|
|
106
|
+
`system.grants` or `system.row_policies`; an administrator can, with `SHOW GRANTS FOR <user>`
|
|
107
|
+
and the queries in [Operate](operate.md#who-is-here).
|
|
108
|
+
|
|
109
|
+
It also lists the other direction: each member whose sessions your credential can read, with
|
|
110
|
+
the command that reads them. A scoped share shows only the rows its owner chose, and its filter
|
|
111
|
+
is the owner's to see.
|
|
96
112
|
|
|
97
113
|
## Reading what was shared with you
|
|
98
114
|
|
|
99
|
-
Shared
|
|
100
|
-
example `mem.
|
|
101
|
-
|
|
115
|
+
Shared tables sit in the same database as yours, under the other member's name, for
|
|
116
|
+
example `mem.alice_session_parts`. Query them with `/mem:sql` or any ClickHouse client. Read
|
|
117
|
+
`<m>_sessions` with `FINAL`, as you would your own. A table you were not granted is
|
|
102
118
|
refused, and a scope you are under returns only its rows.
|
|
119
|
+
|
|
120
|
+
## When a house is converted from 0.18
|
|
121
|
+
|
|
122
|
+
A 0.18 share is a grant on the old rooms, which the conversion renames into the archive.
|
|
123
|
+
The administrator moves the shares with `memhouse migrate --v3-shares`, part of
|
|
124
|
+
[Upgrade to 0.20](upgrade-to-0.20.md). It re-creates only the shares memhouse recorded,
|
|
125
|
+
narrows a 0.18 full share to the six tables, and refuses when the record and the live
|
|
126
|
+
grants disagree. Run it as part of the conversion: until it runs, readers hold whatever
|
|
127
|
+
their 0.18 grants still reach.
|
|
128
|
+
|
|
129
|
+
## A house on the 0.18 rooms
|
|
130
|
+
|
|
131
|
+
On a house not yet converted, `share` works as in 0.18:
|
|
132
|
+
|
|
133
|
+
- a full share is `SELECT ON mem.<you>_*`, which also reaches the stats tables, `meta`,
|
|
134
|
+
`events` and any room added later;
|
|
135
|
+
- a scoped share is `SELECT` on `<you>_sessions`, `<you>_messages` and
|
|
136
|
+
`<you>_tool_calls`, filtered by row policies;
|
|
137
|
+
- `source=` filters on the `source` column.
|
|
138
|
+
|
|
139
|
+
**Scoped shares made before 0.18.10** granted the whole pattern while filtering only three
|
|
140
|
+
rooms. Re-run `memhouse share <user> --only <scope>` once for each, and it is narrowed to
|
|
141
|
+
the three rooms.
|
package/docs/guides/storage.md
CHANGED
|
@@ -1,6 +1,46 @@
|
|
|
1
1
|
# How memory is stored
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A house created by memhouse 0.20 is on **schema 3**, described first. A house from 0.18 is on
|
|
4
|
+
the **rooms**, described after it, until its admin converts it
|
|
5
|
+
([Upgrade to 0.20](upgrade-to-0.20.md)).
|
|
6
|
+
|
|
7
|
+
## Schema 3 (memhouse 0.20)
|
|
8
|
+
|
|
9
|
+
A member's sessions live in six conversation tables, the harness-sessions-sql profile, plus two capture
|
|
10
|
+
tables only the shipper writes:
|
|
11
|
+
|
|
12
|
+
| table | holds |
|
|
13
|
+
|---|---|
|
|
14
|
+
| `<db>.<member>_session_parts` | one row per message part: text, thinking, a tool call and its full result, an attachment's hash |
|
|
15
|
+
| `<db>.<member>_session_usage` | one row per usage record, at the editor's granularity (a record, a message or a turn): tokens, model, and how the editor counted them |
|
|
16
|
+
| `<db>.<member>_session_links` | one row per link between sessions, such as a fork or a sub-agent, as a session states it or as the engine inferred or verified it |
|
|
17
|
+
| `<db>.<member>_session_lifecycle` | one row per lifecycle record a session states: an operation on a message, a range of messages or the whole session |
|
|
18
|
+
| `<db>.<member>_calls` | one row per tool call: name, arguments, whether it failed, the result's length (the result itself is a `session_parts` row) |
|
|
19
|
+
| `<db>.<member>_sessions` | one row per session: title, folder, counts, tokens per model and hour, links to forks and sub-agents |
|
|
20
|
+
| `<db>_archive.<member>_session_units` | the source as read: every unit (a line, a row, a file) of every transcript or normalised log, and a manifest for each read that changed something |
|
|
21
|
+
| `<db>_archive.<member>_session_sources` | the shipper's cursor per source |
|
|
22
|
+
|
|
23
|
+
- **A session is its store.** It is keyed by `host` (a fingerprint of the machine), `harness`,
|
|
24
|
+
`harness_path` (the harness's data folder; for OpenCode, its DB file), its `session_id`, and an `incarnation` where a
|
|
25
|
+
harness reuses ids. The same id on another machine, or in another data folder, is another
|
|
26
|
+
session.
|
|
27
|
+
- **Nothing is overwritten in place.** A row is replaced by a newer version (`rev`). When a
|
|
28
|
+
message leaves its source (a revert, a rewind, a deleted message), it is written once more
|
|
29
|
+
as a **removed version** (`removed = 1`). Every read hides it, `memhouse q removed` lists it,
|
|
30
|
+
and no total counts it.
|
|
31
|
+
- **The archive outlives the editors.** Its units keep every line, row or file as it was read,
|
|
32
|
+
and a manifest for each read that changed something says what that read delivered. So a session can be rebuilt from the
|
|
33
|
+
archive alone, including what later went away. A transcript the editor deleted stays in the
|
|
34
|
+
house.
|
|
35
|
+
- **A fork is billed once.** Where one session is a verified copy of another's beginning, the
|
|
36
|
+
copy's share of the tokens is subtracted on the fork. The copies stay messages of the fork.
|
|
37
|
+
|
|
38
|
+
`memhouse doctor` checks all of it: tables and sorting keys, identity stamping, duplicate
|
|
39
|
+
timestamps, removed content, forks billed once, and the machine's host identity.
|
|
40
|
+
|
|
41
|
+
## The 0.18 rooms
|
|
42
|
+
|
|
43
|
+
### A house, rooms, and members
|
|
4
44
|
|
|
5
45
|
- **A house is a ClickHouse database,** normally `mem`.
|
|
6
46
|
- **A member is a ClickHouse user** who ships into it.
|
|
@@ -29,7 +69,7 @@ database once leaked into each other. A member can read their isolation back wit
|
|
|
29
69
|
Reading across members is a `UNION ALL` over the rooms you hold: your own, and any shared
|
|
30
70
|
with you. Naming a room you were not granted is an error, never a silent omission.
|
|
31
71
|
|
|
32
|
-
|
|
72
|
+
### Every row says where it came from
|
|
33
73
|
|
|
34
74
|
- **`user_id`** is stamped by the server: `MATERIALIZED currentUser()`. Async inserts are
|
|
35
75
|
pinned off for members, so the stamp cannot be skipped. `WHERE user_id = 'alice'` is one
|
|
@@ -48,7 +88,7 @@ with you. Naming a room you were not granted is an error, never a silent omissio
|
|
|
48
88
|
- **`origin`** says who wrote the row. The shipper writes `ship`; an import writes its own
|
|
49
89
|
value.
|
|
50
90
|
|
|
51
|
-
|
|
91
|
+
### The house never destroys what it cannot rebuild
|
|
52
92
|
|
|
53
93
|
**The shipper is insert-only.** It runs no `DELETE`, no `TRUNCATE`, and no mutation of
|
|
54
94
|
any kind, so no privilege it holds can lose you a row.
|
|
@@ -83,7 +123,7 @@ a second row would make every rollup count its messages twice.
|
|
|
83
123
|
The shipper checks the rooms' sorting keys before it writes, and refuses if they are
|
|
84
124
|
wrong. See [Update](update.md#when-the-house-itself-must-change).
|
|
85
125
|
|
|
86
|
-
|
|
126
|
+
### What is not in the house
|
|
87
127
|
|
|
88
128
|
- Nothing is embedded, summarised or rewritten. Text is stored as the editor wrote it,
|
|
89
129
|
truncated at 50,000 characters per message and 20,000 per tool argument.
|