memhouse 0.20.0 → 0.20.2
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/CHANGELOG.md +121 -0
- package/bin/memhouse.js +209 -30
- package/docs/guides/install.md +3 -0
- package/docs/guides/invite.md +7 -0
- package/docs/guides/search-and-resume.md +30 -0
- package/docs/guides/share.md +5 -0
- package/docs/releases/README.md +2 -0
- package/docs/releases/v0.20.1.md +114 -0
- package/docs/releases/v0.20.2.md +134 -0
- package/memhouse/codex-plugin.js +194 -0
- package/memhouse/delivery/INVITATION.md.tpl +1 -1
- package/memhouse/delivery/codex/AGENTS-BLOCK.md +11 -0
- package/memhouse/delivery/codex/SKILL.md +50 -0
- package/memhouse/delivery/plugin/reference/HOUSE.md +7 -9
- package/memhouse/delivery/plugin/skills/access/SKILL.md +1 -1
- package/memhouse/flags.js +1 -1
- package/memhouse/help.js +37 -0
- package/memhouse/house/q-cli.js +14 -1
- package/memhouse/house/records-to-profile.js +14 -3
- package/memhouse/house/schema-choice.js +15 -6
- package/memhouse/house/schema-profile-views.sql.tpl +53 -0
- package/memhouse/house/schema-profile.js +46 -6
- package/memhouse/house/schema-profile.sql.tpl +8 -1
- package/memhouse/invitation.js +8 -2
- package/memhouse/plugin-copies.js +13 -1
- package/memhouse/release-hint.js +24 -0
- package/memhouse/share.js +36 -4
- package/memhouse/shipper/capture.js +39 -9
- package/memhouse/shipper/log-adapter.js +18 -7
- package/memhouse/shipper/normalise-run.js +8 -2
- package/memhouse/version-notice.js +66 -0
- package/package.json +3 -3
- package/public/assets/{index-CQSJ3nhV.js → index-CcNnhfM3.js} +1 -1
- package/public/index.html +1 -1
- package/ui/package-lock.json +264 -318
- package/ui/package.json +5 -4
- package/ui/src/lib/dashboard-token.js +7 -2
package/docs/guides/install.md
CHANGED
|
@@ -128,3 +128,6 @@ The path proves nothing on its own: Homebrew's prefix is `/usr/local` on Intel a
|
|
|
128
128
|
- `memhouse plugins install claude`: the `/mem:*` skills, for Claude Code only. Codex and the
|
|
129
129
|
other editors' sessions ship all the same; an agent there reads the house with the CLI
|
|
130
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).
|
package/docs/guides/invite.md
CHANGED
|
@@ -27,6 +27,13 @@ memhouse invite alice --url https://house.example.com
|
|
|
27
27
|
- **Alice installs with `memhouse install --env invite-alice.env`.** It offers to rotate
|
|
28
28
|
the password to one only she knows, which works because she is granted `ALTER USER` on
|
|
29
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.
|
|
30
37
|
|
|
31
38
|
## Which admin credential
|
|
32
39
|
|
|
@@ -99,6 +99,36 @@ missing skill never announces itself.
|
|
|
99
99
|
For an agent that does not use Claude Code skills, `memhouse prompt` prints a snippet for
|
|
100
100
|
its system prompt: where memory lives and how to query it.
|
|
101
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
|
+
|
|
102
132
|
## Writing your own queries
|
|
103
133
|
|
|
104
134
|
On schema 3 (0.20), the tables are `mem.<you>_session_parts` (one row per part of a message:
|
package/docs/guides/share.md
CHANGED
|
@@ -26,6 +26,7 @@ Full or scoped, a share is `SELECT` on exactly your six conversation tables:
|
|
|
26
26
|
| rows | all | those matching the scope, through a row policy on each table |
|
|
27
27
|
| capture archive (`<db>_archive`) | not granted | not granted |
|
|
28
28
|
| `<you>_meta`, `<you>_events` | not granted | not granted |
|
|
29
|
+
| `<you>_session_turns_view` (your turns, a view) | not granted | not granted |
|
|
29
30
|
| tables added later | not granted | not granted |
|
|
30
31
|
|
|
31
32
|
## Scopes
|
|
@@ -106,6 +107,10 @@ included. A grant someone made by hand in SQL does not appear there. A member ca
|
|
|
106
107
|
`system.grants` or `system.row_policies`; an administrator can, with `SHOW GRANTS FOR <user>`
|
|
107
108
|
and the queries in [Operate](operate.md#who-is-here).
|
|
108
109
|
|
|
110
|
+
It also lists the other direction: each member whose sessions your credential can read, with
|
|
111
|
+
the command that reads them. A scoped share shows only the rows its owner chose, and its filter
|
|
112
|
+
is the owner's to see.
|
|
113
|
+
|
|
109
114
|
## Reading what was shared with you
|
|
110
115
|
|
|
111
116
|
Shared tables sit in the same database as yours, under the other member's name, for
|
package/docs/releases/README.md
CHANGED
|
@@ -5,6 +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.20.2](v0.20.2.md) | 2026-11-03 | harness-sessions-sql v1.1's columns and a turns view; Cursor sub-agent links; the first capture re-derives every session |
|
|
9
|
+
| [0.20.1](v0.20.1.md) | 2026-11-02 | invites expire; `memhouse plugins install codex`; per-command help and clearer CLI answers |
|
|
8
10
|
| [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
11
|
| [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
12
|
| [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 |
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# memhouse 0.20.1 — 2026-11-02
|
|
2
|
+
|
|
3
|
+
A patch release on 0.20.0, with no schema change. Install it with `memhouse update`, or
|
|
4
|
+
`npm install -g memhouse`.
|
|
5
|
+
|
|
6
|
+
- **An invite's password expires.** It stops working 7 days after the invite unless it is
|
|
7
|
+
installed first. A file that sits unused, or that was read where it should not have been,
|
|
8
|
+
stops working on its own.
|
|
9
|
+
- **Codex gets memhouse on request.** `memhouse plugins install codex` adds a short rules block
|
|
10
|
+
to Codex's `AGENTS.md` and a `memhouse` skill with the read recipes. `plugins remove codex`
|
|
11
|
+
takes out exactly those.
|
|
12
|
+
- **Clearer answers from the CLI:**
|
|
13
|
+
- `memhouse <command> --help` prints that command's help;
|
|
14
|
+
- a refused credential is named before anything is written;
|
|
15
|
+
- `share --list` also shows the shares you received;
|
|
16
|
+
- `members` tells a share from a member's own tables;
|
|
17
|
+
- `discover` and `update` stop reporting problems that belong to nobody.
|
|
18
|
+
- **A new minor version's first run says so,** and names the release notes to read.
|
|
19
|
+
|
|
20
|
+
## Read this before upgrading
|
|
21
|
+
|
|
22
|
+
Nothing in the house changes, and there is no migration. Three things behave differently:
|
|
23
|
+
|
|
24
|
+
- **Inviting: the password now expires.** `memhouse invite` sets the expiry on the new account
|
|
25
|
+
using the server's clock. The default is 7 days; `--expires <days>` takes 1 to 365. The
|
|
26
|
+
output, the invite file and `MEMHOUSE-INVITATION.md` all give the date. An invitee who
|
|
27
|
+
installs after that date is refused with "this invite expired at … ask whoever invited you
|
|
28
|
+
for a new one". To invite them again under the same name, remove the unused account first
|
|
29
|
+
with `DROP USER <name>`, as the administrator; their tables are kept and reused. Invites
|
|
30
|
+
made by 0.20.0 or earlier carry no expiry and keep working as before.
|
|
31
|
+
- **The first command after moving to a new minor version prints one line** to stderr. It
|
|
32
|
+
names the version it replaces and the release notes. Patch releases, downgrades and fresh
|
|
33
|
+
installs print nothing.
|
|
34
|
+
- **Codex is opt-in.** Nothing is written under `~/.codex` (or `CODEX_HOME`) unless you run
|
|
35
|
+
`memhouse plugins install codex`.
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
memhouse update # then: memhouse status — every line a check mark
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Invites
|
|
42
|
+
|
|
43
|
+
- **The expiry is set by the server**, as `VALID UNTIL` on the new account, so it holds no
|
|
44
|
+
matter what the file says. `install --env` also checks the date in the file and refuses
|
|
45
|
+
before contacting the server.
|
|
46
|
+
- **Installing lifts it.** A password change removes the expiry, and so does
|
|
47
|
+
`--keep-password`, which keeps the invited password.
|
|
48
|
+
- **`invite --print-sql`** prints the same `ALTER USER … VALID UNTIL` statement for an
|
|
49
|
+
administrator who runs the plan by hand. The date is left as a placeholder, so the
|
|
50
|
+
statement does not parse until a real date is put in.
|
|
51
|
+
- **Re-inviting an unused name** used to fail with the install message, "give their
|
|
52
|
+
password". It now says the invite was never installed, gives its expiry, and names the
|
|
53
|
+
remedy.
|
|
54
|
+
|
|
55
|
+
## Codex
|
|
56
|
+
|
|
57
|
+
Codex puts `AGENTS.md` into every prompt and lists each skill by its name and description,
|
|
58
|
+
reading the body only when the skill is used. memhouse uses both, and only when asked:
|
|
59
|
+
|
|
60
|
+
- **The block** in `AGENTS.md` holds what must always apply:
|
|
61
|
+
- never read or print the env file or a `MEMHOUSE_*` value;
|
|
62
|
+
- `MEMHOUSE_URL` is the house and `MEMHOUSE_PORT` the dashboard;
|
|
63
|
+
- questions about past sessions go to the skill.
|
|
64
|
+
|
|
65
|
+
It sits between `<!-- memhouse:begin … -->` and `<!-- memhouse:end -->` markers. A reinstall
|
|
66
|
+
replaces it where it stands. Remove takes it out and leaves the rest of the file
|
|
67
|
+
byte-identical. A file that held only the block is deleted, unless it is a symlink: then its
|
|
68
|
+
target is emptied and the link kept.
|
|
69
|
+
- **The skill**, `skills/memhouse/`, carries the read recipes:
|
|
70
|
+
- `memhouse q` for search, sessions, activity by hour, spend, models, tools and machines;
|
|
71
|
+
- `--member` for a housemate's shared sessions;
|
|
72
|
+
- `memhouse room-sql` for anything else.
|
|
73
|
+
|
|
74
|
+
It is memhouse's only if it holds `.memhouse.json`. A `skills/memhouse` without that file is
|
|
75
|
+
never replaced or removed.
|
|
76
|
+
- **An instance with its own home or binary** is named in both, as `MEMHOUSE_HOME=<home>
|
|
77
|
+
<binary>`.
|
|
78
|
+
- **After an upgrade,** the first command refreshes an installed copy, as it does the `/mem`
|
|
79
|
+
copies. A block you took out by hand stays out.
|
|
80
|
+
- **The `/mem` skills are still for Claude Code.**
|
|
81
|
+
|
|
82
|
+
## Smaller changes
|
|
83
|
+
|
|
84
|
+
- **`memhouse <command> --help`** prints that command's entry. A command with no entry of its
|
|
85
|
+
own still gets the whole page. `memhouse q --help` lists the named queries.
|
|
86
|
+
- **A refused credential is named up front.** When ClickHouse refuses the credential (code
|
|
87
|
+
516, or 194 for a missing password), `install`, `invite` and `deploy --local` stop before
|
|
88
|
+
writing anything, with "could not authenticate as '<user>' at <url>".
|
|
89
|
+
- **`share --list` also lists the shares you received,** with the command that reads each
|
|
90
|
+
one. `--json` adds `received`.
|
|
91
|
+
- **`members` tells a share from a member's own tables:**
|
|
92
|
+
- own tables;
|
|
93
|
+
- each member whose sessions this account reads, scoped or full;
|
|
94
|
+
- any other grant.
|
|
95
|
+
|
|
96
|
+
`--json` adds `own`, `shares` and `other`.
|
|
97
|
+
- **`discover`** shows an unreachable `http://localhost:8123` as a plain line when nothing is
|
|
98
|
+
configured to use it, instead of a ✗.
|
|
99
|
+
- **`update` on a machine with no member install** (an administrator's machine) skips the
|
|
100
|
+
schema heal and migration check, instead of printing the default member's authentication
|
|
101
|
+
failures.
|
|
102
|
+
- **`doctor`** names the release channel when it says a release is the latest. Offline, it no
|
|
103
|
+
longer says the install is current.
|
|
104
|
+
- **Dashboard:** a browser that refuses sessionStorage keeps the access token in memory for
|
|
105
|
+
that page load, instead of showing the "open it with `memhouse dashboard`" page.
|
|
106
|
+
- **/mem skills:** the schema-2 raw-SQL recipe runs `memhouse room-sql <<'SQL'` directly. It
|
|
107
|
+
used to wrap the call in a shell function, so every call asked for approval.
|
|
108
|
+
|
|
109
|
+
## Deprecated
|
|
110
|
+
|
|
111
|
+
Unchanged from 0.20.0: passwords on the command line (`--password`, `--member-password`,
|
|
112
|
+
`--to-password`), and `MEMHOUSE_USER` / `MEMHOUSE_PASSWORD` as the admin for `migrate --v3-*`,
|
|
113
|
+
still work with a warning and are refused from the next minor release. See
|
|
114
|
+
[0.20.0's Deprecated](v0.20.0.md#deprecated).
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# memhouse 0.20.2 — 2026-11-03
|
|
2
|
+
|
|
3
|
+
A patch release on 0.20.1. The profile tables gain five columns, which the schema step adds in
|
|
4
|
+
place, and the first capture after upgrading re-derives every session from its archive. Install
|
|
5
|
+
it with `memhouse update`, or `npm install -g memhouse`.
|
|
6
|
+
|
|
7
|
+
- **The profile tables gain harness-sessions-sql v1.1's five columns.** Each session and message
|
|
8
|
+
keeps the editor's own fields that its adapter declares, and the archived units it came from,
|
|
9
|
+
so a row joins back to its raw line. These notes claim no v1.1 conformance; the conformance
|
|
10
|
+
run's result is below.
|
|
11
|
+
- **A reading view of turns:** `<m>_session_turns_view`, one row per turn of a session (v1.1's
|
|
12
|
+
`session_turns`, still a plan there).
|
|
13
|
+
- **Cursor: a chat's sub-agents are captured as its links.**
|
|
14
|
+
- **Fixes:**
|
|
15
|
+
- `memhouse room-sql` runs `SHOW ROW POLICIES` on ClickHouse 25.11;
|
|
16
|
+
- a session log memhouse cannot read is never rewritten into an empty one;
|
|
17
|
+
- on the development layout, an adapter upgrade writes above the rows it replaces.
|
|
18
|
+
|
|
19
|
+
## Read this before upgrading
|
|
20
|
+
|
|
21
|
+
- **The first capture re-derives every session from its archive.** This happens once, on the
|
|
22
|
+
first pass after `memhouse update`. It reads the archive, not the editors' stores, and writes
|
|
23
|
+
every session's rows again, so it takes longer than a usual pass; later passes are as before.
|
|
24
|
+
- Measured on a synthetic house of 2,500 sessions (2,000 Claude Code and 500 Codex; 110 MiB
|
|
25
|
+
of session files, 193,814 message parts), in a 4-core container with 6 GiB of memory, that
|
|
26
|
+
pass took 18 minutes on ClickHouse 26.7.4.58 and 27 minutes on 25.11.9.34: 1.5 and 1.3
|
|
27
|
+
times as long as capturing the same house for the first time. The shipper's memory peaked
|
|
28
|
+
at 348 MB and ClickHouse's at 58 MiB per query, and the next pass took 7 seconds.
|
|
29
|
+
- The time grows with the number of sessions, 0.43 seconds a session on 26.7 and 0.64 on
|
|
30
|
+
25.11 in that measurement: a house with four times as many sessions takes about four times
|
|
31
|
+
as long.
|
|
32
|
+
- Claude Code and Codex are read through `@agent-realm/harness-session-adapters` 1.1.0, whose
|
|
33
|
+
adapters state `adapter_ver` 0.3.0.
|
|
34
|
+
- Every other editor is read through memhouse's own session logs, whose adapter,
|
|
35
|
+
`memhouse-bridge`, moves to 1.0.2 (the Cursor links below).
|
|
36
|
+
- A new adapter version is an upgrade (harness-sessions-sql §8, "Re-derive from the
|
|
37
|
+
archive"), so each session is replayed under the new version and written above the house's
|
|
38
|
+
revs.
|
|
39
|
+
- **The schema step adds five columns** to an existing house's `<m>_sessions` and
|
|
40
|
+
`<m>_session_parts`, at the end of each table (a new house has them at the specification's
|
|
41
|
+
positions). The member's own credential adds them; the archive tables do not change. A row
|
|
42
|
+
captured before 0.20.2 holds `''` and `[]` there until its session is re-derived, which the
|
|
43
|
+
first capture does.
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
memhouse update # then: memhouse status — every line a check mark
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## The profile tables: harness-sessions-sql v1.1's columns
|
|
50
|
+
|
|
51
|
+
This adds the v1.1 columns and the `session_turns` view (a plan in v1.1).
|
|
52
|
+
|
|
53
|
+
- `sessions.native`, `session_parts.message_native` and `part_native`: the fields of the
|
|
54
|
+
editor's own record that the adapter declares (a Claude Code message's `message.stop_reason`,
|
|
55
|
+
say), as canonical JSON, `''` when there are none.
|
|
56
|
+
- `sessions.units` and `session_parts.message_units`: the `(source_key, generation, unit_key)`
|
|
57
|
+
of each archived unit the record came from, so a row joins back to its raw line in
|
|
58
|
+
`<db>_archive.<m>_session_units`.
|
|
59
|
+
- The legacy conversion leaves the five columns out of its `content_sha` line, as v1.1 asks, so
|
|
60
|
+
a converted session keeps its shas.
|
|
61
|
+
|
|
62
|
+
These notes claim no conformance. The harness-sessions-sql conformance kit, run against this
|
|
63
|
+
release's code on ClickHouse 26.7 and 25.11 with the session adapters 1.1.0:
|
|
64
|
+
|
|
65
|
+
- **Native is declared** passes.
|
|
66
|
+
- **Units resolve** fails. memhouse numbers a source's first archived generation 0, where v1.1
|
|
67
|
+
numbers it 1, so each triple's generation is one lower than the specification's. Every triple
|
|
68
|
+
still names a unit of the session's own archive, so a row joins back to its raw line. memhouse
|
|
69
|
+
has numbered generations this way since 0.20.0. Aligning the two is 0.20.3 work.
|
|
70
|
+
- **Crash-safe** keeps the failure 0.20.0 reported: a stopped read whose source changed before
|
|
71
|
+
the re-run.
|
|
72
|
+
|
|
73
|
+
## A reading view of turns
|
|
74
|
+
|
|
75
|
+
`<m>_session_turns_view` (harness-sessions-sql v1.1 §6, `session_turns`, still a plan there)
|
|
76
|
+
gives one row per turn of one session, a turn running from a prompt to the next one, with its
|
|
77
|
+
messages, tool calls, failed tool results and the models that served it:
|
|
78
|
+
|
|
79
|
+
```sql
|
|
80
|
+
SELECT * FROM <db>.<m>_session_turns_view(host = '…', harness = '…', harness_path = '…', session_id = '…', incarnation = '')
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
- The schema step makes it (`CREATE OR REPLACE`).
|
|
84
|
+
- It runs as its reader (`SQL SECURITY INVOKER`), so it shows nobody a row the tables would not.
|
|
85
|
+
- A share does not reach it: a share is still the six tables.
|
|
86
|
+
- It is not named `<m>_session_turns`, because a table ending in `_turns` reads as a 0.18
|
|
87
|
+
member's room when memhouse looks for colliding member names.
|
|
88
|
+
|
|
89
|
+
## Cursor sub-agents
|
|
90
|
+
|
|
91
|
+
Cursor names a chat's sub-agents only in the chat's own record (`subagentComposerIds`), never in
|
|
92
|
+
a message, and the profile capture did not read them there, so the links from a chat to its
|
|
93
|
+
sub-agents were missing. `memhouse-bridge` 1.0.2 reads them as links stated on the chat, one per
|
|
94
|
+
sub-agent.
|
|
95
|
+
|
|
96
|
+
- No live Cursor store has shown a sub-agent yet; the tests use synthetic sessions.
|
|
97
|
+
- Each new sub-agent still rewrites the chat's normalised log. Placing it where it appeared
|
|
98
|
+
waits for a live sample (Deferred).
|
|
99
|
+
|
|
100
|
+
## Smaller changes
|
|
101
|
+
|
|
102
|
+
- **`memhouse room-sql` runs `SHOW ROW POLICIES` on ClickHouse 25.11.** It appended its output
|
|
103
|
+
format to the statement, and 25.11 parses no FORMAT after a bare `SHOW ROW POLICIES` or `SHOW
|
|
104
|
+
POLICIES`. The format now goes as the `default_format` setting; a FORMAT the statement names
|
|
105
|
+
itself still wins.
|
|
106
|
+
- **A session log memhouse cannot read is never rewritten into an empty one.** An old log that
|
|
107
|
+
was there but could not be read (a permission or disk error) counted as having no messages,
|
|
108
|
+
which let an empty read through. It now stops that session's pass with "cannot read the
|
|
109
|
+
existing log (<code>)", reported and retried.
|
|
110
|
+
- **The development layout (`<m>_turns`, `MEMHOUSE_LAYOUT=v3-dev`):** an adapter upgrade writes
|
|
111
|
+
one rev above the highest the house holds for the file, instead of tying with the rows it
|
|
112
|
+
replaces. The profile tables were not affected.
|
|
113
|
+
- **The site** no longer points readers at a repository-only document.
|
|
114
|
+
|
|
115
|
+
## Deferred
|
|
116
|
+
|
|
117
|
+
- **The normalisers' filesystem errors** (0.20.3): about thirty reads in the editor normalisers
|
|
118
|
+
take any error for "not there", so a permission denial (macOS privacy on
|
|
119
|
+
`~/Library/Application Support`, for one) shows as no sessions. 0.20.3 reports them by editor
|
|
120
|
+
and errno in `status` and `doctor`, with a Full Disk Access hint on macOS.
|
|
121
|
+
- **Generation numbering** (0.20.3): a source's first archived generation is 0 where v1.1 says 1
|
|
122
|
+
(above).
|
|
123
|
+
- **Cursor sub-agents as appended records.** An anchor would place each after the parent's last
|
|
124
|
+
message at or before the sub-agent's own `createdAt`; that waits for a live store to confirm
|
|
125
|
+
that a composer's and its bubbles' `createdAt` share one clock.
|
|
126
|
+
- **`text_len` on the development layout** counts UTF-16 units, not code points.
|
|
127
|
+
- **`memhouse reset` and `relocate` on schema 3**: 0.21, with a design note first.
|
|
128
|
+
|
|
129
|
+
## Deprecated
|
|
130
|
+
|
|
131
|
+
Unchanged from 0.20.0: passwords on the command line (`--password`, `--member-password`,
|
|
132
|
+
`--to-password`), and `MEMHOUSE_USER` / `MEMHOUSE_PASSWORD` as the admin for `migrate --v3-*`,
|
|
133
|
+
still work with a warning and are refused from the next minor release. See
|
|
134
|
+
[0.20.0's Deprecated](v0.20.0.md#deprecated).
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
// `memhouse plugins install codex`: what Codex reads of memhouse, and only on request.
|
|
2
|
+
//
|
|
3
|
+
// Codex (0.165, checked against the request it sends) puts <codex home>/AGENTS.md into every
|
|
4
|
+
// prompt in full, and lists each skill under <codex home>/skills by its name and description,
|
|
5
|
+
// reading the body only when the skill is used. So the rules that must hold in every session go
|
|
6
|
+
// in a short block in AGENTS.md, and the read recipes in one skill, skills/memhouse.
|
|
7
|
+
//
|
|
8
|
+
// The block is marker-delimited. Install puts it at the top of the file, followed by one blank
|
|
9
|
+
// line, or creates the file with the block alone; a reinstall replaces it where it stands. Remove
|
|
10
|
+
// takes out the block and that one line, so the rest of the file is what it was, byte for byte;
|
|
11
|
+
// a file left empty is deleted, unless it is a symlink: then the target is emptied through it and
|
|
12
|
+
// the link stays. The skill directory carries an ownership file (OWNER_FILE): a
|
|
13
|
+
// skills/memhouse without it is somebody else's and is never replaced or removed.
|
|
14
|
+
//
|
|
15
|
+
// Nothing here writes under the Codex home except install, remove, and the refresh of a copy
|
|
16
|
+
// that install already put there (plugin-copies.js).
|
|
17
|
+
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const os = require('os');
|
|
20
|
+
const path = require('path');
|
|
21
|
+
|
|
22
|
+
const SKILL = 'memhouse';
|
|
23
|
+
const OWNER_FILE = '.memhouse.json';
|
|
24
|
+
const BEGIN = '<!-- memhouse:begin';
|
|
25
|
+
const END = '<!-- memhouse:end -->';
|
|
26
|
+
const MARKERS = /^<!-- memhouse:(begin|end)\b[^\n]*$/gm;
|
|
27
|
+
|
|
28
|
+
/** The Codex home: CODEX_HOME when set, else ~/.codex. */
|
|
29
|
+
function codexHome(env = process.env) {
|
|
30
|
+
return env.CODEX_HOME || path.join(os.homedir(), '.codex');
|
|
31
|
+
}
|
|
32
|
+
const agentsFile = (cx) => path.join(cx, 'AGENTS.md');
|
|
33
|
+
const skillDir = (cx) => path.join(cx, 'skills', SKILL);
|
|
34
|
+
const real = (p) => { try { return fs.realpathSync(p); } catch { return path.resolve(p); } };
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Where the block sits in `text`: { start, end, version }, or null when there is none.
|
|
38
|
+
* Throws when the markers do not make exactly one block, so nothing is written over a file
|
|
39
|
+
* whose block was damaged by hand.
|
|
40
|
+
*/
|
|
41
|
+
function findBlock(text) {
|
|
42
|
+
const begins = []; const ends = [];
|
|
43
|
+
let m;
|
|
44
|
+
MARKERS.lastIndex = 0;
|
|
45
|
+
while ((m = MARKERS.exec(text))) (m[1] === 'begin' ? begins : ends).push(m.index);
|
|
46
|
+
if (!begins.length && !ends.length) return null;
|
|
47
|
+
if (begins.length !== 1 || ends.length !== 1 || ends[0] < begins[0]) {
|
|
48
|
+
throw new Error(`AGENTS.md holds ${begins.length} memhouse:begin and ${ends.length} memhouse:end markers, not one block — fix it by hand; nothing was changed`);
|
|
49
|
+
}
|
|
50
|
+
const nl = text.indexOf('\n', ends[0]);
|
|
51
|
+
const version = (/^<!-- memhouse:begin (\d+\.\d+\.\d+\S*)/.exec(text.slice(begins[0])) || [])[1] || null;
|
|
52
|
+
return { start: begins[0], end: nl === -1 ? text.length : nl + 1, version };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** `text` with `block` in it: replaced where it stands, or put first with one blank line after. */
|
|
56
|
+
function withBlock(text, block) {
|
|
57
|
+
const b = findBlock(text);
|
|
58
|
+
if (b) return text.slice(0, b.start) + block + text.slice(b.end);
|
|
59
|
+
return text ? `${block}\n${text}` : block;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** `text` without the block, and without the blank line install put after it. */
|
|
63
|
+
function withoutBlock(text) {
|
|
64
|
+
const b = findBlock(text);
|
|
65
|
+
if (!b) return text;
|
|
66
|
+
let rest = text.slice(b.end);
|
|
67
|
+
if (b.start === 0 && rest.startsWith('\n')) rest = rest.slice(1);
|
|
68
|
+
return text.slice(0, b.start) + rest;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** A path for the text, with the home directory as ~. */
|
|
72
|
+
function shown(p) {
|
|
73
|
+
const h = os.homedir();
|
|
74
|
+
return p === h || p.startsWith(h + path.sep) ? `~${p.slice(h.length)}` : p;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** A shell word: as is when it needs no quoting, else single-quoted. */
|
|
78
|
+
function shArg(s) {
|
|
79
|
+
return /^[A-Za-z0-9_@%+=:,./~-]+$/.test(s) ? s : `'${String(s).replace(/'/g, `'\\''`)}'`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** The `memhouse` on PATH, resolved, or null. */
|
|
83
|
+
function memhouseOnPath(env = process.env) {
|
|
84
|
+
for (const d of String(env.PATH || '').split(path.delimiter).filter(Boolean)) {
|
|
85
|
+
const f = path.join(d, 'memhouse');
|
|
86
|
+
try { fs.accessSync(f, fs.constants.X_OK); return real(f); } catch { /* not here */ }
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* How the block and the skill name the command: `memhouse` for the default instance, else with
|
|
93
|
+
* MEMHOUSE_HOME and the binary spelled out, each only where it differs from the default.
|
|
94
|
+
*/
|
|
95
|
+
function invocation({ home, bin, defaultHome = path.join(os.homedir(), '.memhouse'), pathBin = memhouseOnPath() }) {
|
|
96
|
+
const parts = [];
|
|
97
|
+
if (home && real(home) !== real(defaultHome)) parts.push(`MEMHOUSE_HOME=${shArg(shown(home))}`);
|
|
98
|
+
parts.push(!bin || (pathBin && real(bin) === pathBin) ? 'memhouse' : shArg(shown(bin)));
|
|
99
|
+
return parts.join(' ');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function render(tpl, { mh, envFile }) {
|
|
103
|
+
return tpl.replace(/\{\{MH\}\}/g, mh).replace(/\{\{ENV_FILE\}\}/g, envFile);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function renderBlock({ deliveryDir, version, mh, envFile }) {
|
|
107
|
+
const body = render(fs.readFileSync(path.join(deliveryDir, 'codex', 'AGENTS-BLOCK.md'), 'utf-8'), { mh, envFile }).trim();
|
|
108
|
+
return `${BEGIN} ${version} — managed by \`memhouse plugins install codex\`; \`memhouse plugins remove codex\` takes it out -->\n${body}\n${END}\n`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** The skill directory's ownership record, or null when it is not memhouse's (or absent). */
|
|
112
|
+
function readOwner(dir) {
|
|
113
|
+
try {
|
|
114
|
+
const o = JSON.parse(fs.readFileSync(path.join(dir, OWNER_FILE), 'utf-8'));
|
|
115
|
+
return o && o.owner === 'memhouse' ? o : null;
|
|
116
|
+
} catch { return null; }
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Write the block and the skill. Everything is checked before anything is written: a foreign
|
|
121
|
+
* skills/memhouse or a damaged block throws with the Codex home untouched.
|
|
122
|
+
* `onlyWhereInstalled`: the refresh — the block is rewritten only where it already is.
|
|
123
|
+
* @returns {{ file: string, created: boolean, block: boolean, dir: string, mh: string }}
|
|
124
|
+
*/
|
|
125
|
+
function install({ codexHome: cx, home, bin, version, deliveryDir, pathBin, onlyWhereInstalled = false }) {
|
|
126
|
+
const dir = skillDir(cx);
|
|
127
|
+
if (fs.existsSync(dir) && !readOwner(dir)) {
|
|
128
|
+
throw new Error(`${shown(dir)} is not memhouse's (it has no ${OWNER_FILE}) — not replacing it; move it aside and install again`);
|
|
129
|
+
}
|
|
130
|
+
const file = agentsFile(cx);
|
|
131
|
+
let text = null;
|
|
132
|
+
try { text = fs.readFileSync(file, 'utf-8'); } catch (e) { if (e.code !== 'ENOENT') throw e; }
|
|
133
|
+
const mh = invocation({ home, bin, ...(pathBin !== undefined ? { pathBin } : {}) });
|
|
134
|
+
const envFile = shown(path.join(home, 'env'));
|
|
135
|
+
const writeBlock = !onlyWhereInstalled || (text !== null && findBlock(text) !== null);
|
|
136
|
+
const next = writeBlock ? withBlock(text || '', renderBlock({ deliveryDir, version, mh, envFile })) : text;
|
|
137
|
+
const skill = render(fs.readFileSync(path.join(deliveryDir, 'codex', 'SKILL.md'), 'utf-8'), { mh, envFile });
|
|
138
|
+
|
|
139
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
140
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
141
|
+
fs.writeFileSync(path.join(dir, 'SKILL.md'), skill);
|
|
142
|
+
fs.writeFileSync(path.join(dir, OWNER_FILE), `${JSON.stringify({ owner: 'memhouse', version, home, bin: bin || null }, null, 2)}\n`);
|
|
143
|
+
// In place, not a rename: an AGENTS.md that is a symlink (a dotfiles checkout) stays one.
|
|
144
|
+
if (writeBlock && next !== text) fs.writeFileSync(file, next);
|
|
145
|
+
return { file, created: text === null && writeBlock, block: writeBlock, dir, mh };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Take out the block and the skill — the skill only when it is memhouse's.
|
|
150
|
+
* @returns {{ block: boolean, fileDeleted: boolean, emptiedLink: boolean, skill: boolean, foreignSkill: string|null }}
|
|
151
|
+
*/
|
|
152
|
+
function remove({ codexHome: cx }) {
|
|
153
|
+
const res = { block: false, fileDeleted: false, emptiedLink: false, skill: false, foreignSkill: null };
|
|
154
|
+
const file = agentsFile(cx);
|
|
155
|
+
let text = null;
|
|
156
|
+
try { text = fs.readFileSync(file, 'utf-8'); } catch (e) { if (e.code !== 'ENOENT') throw e; }
|
|
157
|
+
const next = text !== null && findBlock(text) ? withoutBlock(text) : null;
|
|
158
|
+
const dir = skillDir(cx);
|
|
159
|
+
const owned = !!readOwner(dir);
|
|
160
|
+
if (!owned && fs.existsSync(dir)) res.foreignSkill = dir;
|
|
161
|
+
if (next !== null) {
|
|
162
|
+
// Only a regular file is unlinked. Unlinking a symlink (a dotfiles checkout) would leave the
|
|
163
|
+
// block in its target, so the target is written through the link instead.
|
|
164
|
+
if (next === '' && !fs.lstatSync(file).isSymbolicLink()) { fs.rmSync(file); res.fileDeleted = true; }
|
|
165
|
+
else { fs.writeFileSync(file, next); res.emptiedLink = next === ''; }
|
|
166
|
+
res.block = true;
|
|
167
|
+
}
|
|
168
|
+
if (owned) { fs.rmSync(dir, { recursive: true }); res.skill = true; }
|
|
169
|
+
return res;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** What is installed: the skill's owner record (or why not), and whether the block is there. */
|
|
173
|
+
function status({ codexHome: cx }) {
|
|
174
|
+
const dir = skillDir(cx);
|
|
175
|
+
const owner = readOwner(dir);
|
|
176
|
+
let block = null; let damaged = null;
|
|
177
|
+
try { block = findBlock(fs.readFileSync(agentsFile(cx), 'utf-8')); } catch (e) { if (e.code !== 'ENOENT') damaged = e.message; }
|
|
178
|
+
return { codexHome: cx, installed: !!owner, owner, foreignSkill: !owner && fs.existsSync(dir) ? dir : null, block: !!block, blockVersion: block ? block.version : null, damaged };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* The copy to refresh for `version`, or null: installed, memhouse's, bound to `home`, and older.
|
|
183
|
+
*/
|
|
184
|
+
function staleCopy({ codexHome: cx, home, version, compareVersions }) {
|
|
185
|
+
const owner = readOwner(skillDir(cx));
|
|
186
|
+
if (!owner || !owner.home || real(owner.home) !== real(home)) return null;
|
|
187
|
+
const c = compareVersions(owner.version, version);
|
|
188
|
+
return c === -1 || c === null ? { codexHome: cx, have: owner.version || null, home: owner.home, bin: owner.bin || null } : null;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
module.exports = {
|
|
192
|
+
SKILL, OWNER_FILE, BEGIN, END, codexHome, agentsFile, skillDir, findBlock, withBlock, withoutBlock,
|
|
193
|
+
invocation, memhouseOnPath, renderBlock, readOwner, install, remove, status, staleCopy, shown,
|
|
194
|
+
};
|
|
@@ -46,7 +46,7 @@ them exist because an agent broke them:
|
|
|
46
46
|
|
|
47
47
|
## 0. Find your credential
|
|
48
48
|
|
|
49
|
-
The install needs `{{FILE}}`, the file delivered with this page. Confirm it is here first —
|
|
49
|
+
{{EXPIRY}}The install needs `{{FILE}}`, the file delivered with this page. Confirm it is here first —
|
|
50
50
|
run this from the directory this page is in now, and if it prints the filename you are ready:
|
|
51
51
|
|
|
52
52
|
```
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
## memhouse: past agent sessions
|
|
2
|
+
|
|
3
|
+
This machine's agent sessions (Codex, Claude Code and other editors) are kept in a memhouse
|
|
4
|
+
house. For a question about earlier work (what was done, when, in which hours, what it cost,
|
|
5
|
+
how something was fixed), use the `memhouse` skill before saying you do not know.
|
|
6
|
+
|
|
7
|
+
- Never read, print or echo `{{ENV_FILE}}`, and never print a `MEMHOUSE_*` value: they hold the
|
|
8
|
+
house's password, and this conversation may be shipped into the house. `{{MH}}` reads its
|
|
9
|
+
own configuration.
|
|
10
|
+
- `MEMHOUSE_URL` is the house (ClickHouse). `MEMHOUSE_PORT` is the local dashboard's port, not
|
|
11
|
+
the house's.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: memhouse
|
|
3
|
+
description: "Answer from past agent sessions kept in memhouse (Codex, Claude Code and other editors): what was worked on and when, which hours or days, spend and models, tools used, how something was fixed, a past session's transcript, a housemate's shared sessions. Read-only memhouse commands. Use before saying you do not know about earlier work."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# memhouse: read past sessions
|
|
7
|
+
|
|
8
|
+
Every command here only reads. `{{MH}}` reads its own configuration: never read or print
|
|
9
|
+
`{{ENV_FILE}}` or any `MEMHOUSE_*` value.
|
|
10
|
+
|
|
11
|
+
Start with `{{MH}} q schema`. The recipes below are for schema 3. On 2 (a house still on the
|
|
12
|
+
0.18 rooms), `{{MH}} prompt` prints the recipes for it.
|
|
13
|
+
|
|
14
|
+
## Named queries
|
|
15
|
+
|
|
16
|
+
`{{MH}} q <name> --json` prints one JSON object. A session is `<harness>:<session_id>`, the
|
|
17
|
+
`ref` every result carries.
|
|
18
|
+
|
|
19
|
+
- Sessions about a topic (every term in one message; a "quoted phrase" as written):
|
|
20
|
+
`{{MH}} q search clickhouse "authentication failed" --json`
|
|
21
|
+
- Around a hit: `{{MH}} q window <ref> --at <message_id> --json`. A whole session:
|
|
22
|
+
`{{MH}} q session <ref> --json`, paged with `--cursor`.
|
|
23
|
+
- Recent sessions: `{{MH}} q recent --limit 20 --since 7d --json`
|
|
24
|
+
- Which hours or days: `{{MH}} q activity --by hour --grain usage --tz <IANA zone> --json`
|
|
25
|
+
counts messages and tokens by the hour they happened; `--by day`, `--by dow`, `--by day,host`.
|
|
26
|
+
- Spend and models: `{{MH}} q spend --by model --since 30d --json`, `{{MH}} q models --json`
|
|
27
|
+
- Tools: `{{MH}} q tools --limit 25 --json`; one tool's calls: `{{MH}} q tool <name> --json`
|
|
28
|
+
- Machines: `{{MH}} q hosts --json`. What the house holds: `{{MH}} q coverage --json`
|
|
29
|
+
- Filters on most names: `--harness`, `--project`, `--folder`, `--host`, `--since`, `--until`.
|
|
30
|
+
`{{MH}} q` lists every name and flag.
|
|
31
|
+
|
|
32
|
+
## A housemate's sessions
|
|
33
|
+
|
|
34
|
+
Add `--member <name>` to a named query to read sessions a housemate shared with you.
|
|
35
|
+
`{{MH}} share --list` names who shared with you. An empty result means nothing is visible,
|
|
36
|
+
not that nothing happened.
|
|
37
|
+
|
|
38
|
+
## Raw SQL, when no named query fits
|
|
39
|
+
|
|
40
|
+
`{{MH}} room-sql "<SELECT …>"` runs one read-only statement. The bare names `sessions`,
|
|
41
|
+
`session_parts`, `session_usage` and `calls` resolve to your tables (`--member <name>` for a
|
|
42
|
+
housemate's). Read them `FINAL` with `is_deleted = 0 AND removed = 0`; a session is
|
|
43
|
+
`(host, harness, harness_path, session_id, incarnation)`; take totals from `sessions`, never by
|
|
44
|
+
summing parts or usage.
|
|
45
|
+
|
|
46
|
+
## Answering
|
|
47
|
+
|
|
48
|
+
Answer from what the commands return, cite the `ref` of each session used, and say so when a
|
|
49
|
+
search finds nothing. `{{MH}} resume <ref>` prints the command that reopens a session. Cost is
|
|
50
|
+
derived from token counts: an unpriced model is unknown, not free.
|
|
@@ -20,8 +20,8 @@ memhouse q schema # prints 3 or 2
|
|
|
20
20
|
- **3** — the house holds harness-sessions-sql's profile tables (`sessions`, `session_parts`,
|
|
21
21
|
`session_usage`, `calls`; memhouse 0.20). Read
|
|
22
22
|
[Schema 3](#schema-3--harness-sessions-sqls-profile) below and use **`memhouse q`**; nothing about
|
|
23
|
-
epochs, `seq` or the
|
|
24
|
-
need raw SQL, and then use `memhouse room-sql
|
|
23
|
+
epochs, `seq` or the 0.18 rooms' name rewrite applies. Skip everything from "Connect" on
|
|
24
|
+
unless you need raw SQL, and then use `memhouse room-sql`.
|
|
25
25
|
- **2** — the 0.18 rooms (`messages`, `tool_calls`, `sessions`). Everything from
|
|
26
26
|
"What this is" on is for you, unchanged.
|
|
27
27
|
|
|
@@ -161,8 +161,8 @@ Every member's rooms are named for them — `mem.alice_messages`, `mem.alice_ses
|
|
|
161
161
|
`mem.alice_tool_calls` — and one grant covers exactly those: `ON mem.alice_*`. Housemates
|
|
162
162
|
keep their own rooms beside yours and cannot read them, list them, or grant them onward;
|
|
163
163
|
you cannot read theirs. `SHOW GRANTS` shows you the one line you hold. **A bare
|
|
164
|
-
`FROM messages` names nothing you have** —
|
|
165
|
-
member name for you, and `memhouse rooms` prints what yours are called.
|
|
164
|
+
`FROM messages` names nothing you have** — `memhouse room-sql` (below) prefixes room names
|
|
165
|
+
with your member name for you, and `memhouse rooms` prints what yours are called.
|
|
166
166
|
|
|
167
167
|
An **operator** owns the ClickHouse itself; an **admin credential** is what proves it.
|
|
168
168
|
`memhouse whoami` says which you are holding.
|
|
@@ -189,13 +189,11 @@ instances on two channels; the top of this file says when to use it instead of p
|
|
|
189
189
|
Credentials resolve **flags > exported `MEMHOUSE_*` > `$MEMHOUSE_HOME/env`** (default
|
|
190
190
|
`~/.memhouse/env`), and the CLI does that itself: you never source the file, and the
|
|
191
191
|
password never appears in a command. Every read goes through `memhouse room-sql`, with the
|
|
192
|
-
SQL on stdin:
|
|
192
|
+
SQL on stdin. Run it as written, not through a shell function: the skills pre-approve
|
|
193
|
+
`memhouse room-sql`, and a wrapper would ask for approval every time.
|
|
193
194
|
|
|
194
195
|
```bash
|
|
195
|
-
|
|
196
|
-
memhouse room-sql "$@"
|
|
197
|
-
}
|
|
198
|
-
q <<'SQL'
|
|
196
|
+
memhouse room-sql <<'SQL'
|
|
199
197
|
SELECT 1 FORMAT PrettyCompact
|
|
200
198
|
SQL
|
|
201
199
|
```
|
|
@@ -116,7 +116,7 @@ each one:
|
|
|
116
116
|
memhouse share <user> everything you own
|
|
117
117
|
memhouse share <user> --only project=memhouse just one project
|
|
118
118
|
memhouse share <user> --only session=<id> just one conversation
|
|
119
|
-
memhouse share --list who can read it, and
|
|
119
|
+
memhouse share --list who can read it, how much, and whose you can read
|
|
120
120
|
memhouse share <user> --revoke withdraw
|
|
121
121
|
```
|
|
122
122
|
|