@pcircle/memesh 4.8.2 → 4.8.5

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/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +2 -1
  3. package/.codex-plugin/mcp.json +7 -0
  4. package/.codex-plugin/plugin.json +6 -0
  5. package/AGENTS.md +18 -8
  6. package/README.de.md +13 -11
  7. package/README.md +25 -12
  8. package/README.zh-TW.md +25 -12
  9. package/dashboard/dist/index.html +3 -3
  10. package/dist/core/agent-message-inbox.d.ts +3 -2
  11. package/dist/core/agent-message-inbox.d.ts.map +1 -1
  12. package/dist/core/agent-message-inbox.js +30 -6
  13. package/dist/core/agent-message-inbox.js.map +1 -1
  14. package/dist/core/agent-messaging.d.ts +7 -0
  15. package/dist/core/agent-messaging.d.ts.map +1 -1
  16. package/dist/core/agent-messaging.js +51 -24
  17. package/dist/core/agent-messaging.js.map +1 -1
  18. package/dist/core/agent-router.d.ts +27 -20
  19. package/dist/core/agent-router.d.ts.map +1 -1
  20. package/dist/core/agent-router.js +114 -43
  21. package/dist/core/agent-router.js.map +1 -1
  22. package/dist/core/agent-scope-id.d.ts +11 -0
  23. package/dist/core/agent-scope-id.d.ts.map +1 -0
  24. package/dist/core/agent-scope-id.js +40 -0
  25. package/dist/core/agent-scope-id.js.map +1 -0
  26. package/dist/core/briefing.d.ts +1 -1
  27. package/dist/core/briefing.d.ts.map +1 -1
  28. package/dist/core/briefing.js +25 -7
  29. package/dist/core/briefing.js.map +1 -1
  30. package/dist/core/citation-rule.d.ts.map +1 -1
  31. package/dist/core/citation-rule.js +5 -2
  32. package/dist/core/citation-rule.js.map +1 -1
  33. package/dist/core/doctor.d.ts +9 -0
  34. package/dist/core/doctor.d.ts.map +1 -1
  35. package/dist/core/doctor.js +461 -24
  36. package/dist/core/doctor.js.map +1 -1
  37. package/dist/core/dreamer.d.ts.map +1 -1
  38. package/dist/core/dreamer.js +3 -1
  39. package/dist/core/dreamer.js.map +1 -1
  40. package/dist/core/embedder.d.ts.map +1 -1
  41. package/dist/core/embedder.js +4 -1
  42. package/dist/core/embedder.js.map +1 -1
  43. package/dist/core/install-channel.d.ts +3 -0
  44. package/dist/core/install-channel.d.ts.map +1 -1
  45. package/dist/core/install-channel.js +79 -2
  46. package/dist/core/install-channel.js.map +1 -1
  47. package/dist/core/install-hooks.d.ts.map +1 -1
  48. package/dist/core/install-hooks.js +10 -6
  49. package/dist/core/install-hooks.js.map +1 -1
  50. package/dist/core/lesson-slug.d.ts.map +1 -1
  51. package/dist/core/lesson-slug.js +6 -4
  52. package/dist/core/lesson-slug.js.map +1 -1
  53. package/dist/core/lifecycle.d.ts.map +1 -1
  54. package/dist/core/lifecycle.js +8 -5
  55. package/dist/core/lifecycle.js.map +1 -1
  56. package/dist/core/llm-client.d.ts.map +1 -1
  57. package/dist/core/llm-client.js +4 -1
  58. package/dist/core/llm-client.js.map +1 -1
  59. package/dist/core/llm-validator.d.ts.map +1 -1
  60. package/dist/core/llm-validator.js +6 -15
  61. package/dist/core/llm-validator.js.map +1 -1
  62. package/dist/core/memory-tool.d.ts.map +1 -1
  63. package/dist/core/memory-tool.js +3 -1
  64. package/dist/core/memory-tool.js.map +1 -1
  65. package/dist/core/ollama-host.d.ts +6 -0
  66. package/dist/core/ollama-host.d.ts.map +1 -0
  67. package/dist/core/ollama-host.js +62 -0
  68. package/dist/core/ollama-host.js.map +1 -0
  69. package/dist/core/operations.d.ts +1 -1
  70. package/dist/core/operations.d.ts.map +1 -1
  71. package/dist/core/operations.js +1 -1
  72. package/dist/core/operations.js.map +1 -1
  73. package/dist/core/project-tags.d.ts +2 -0
  74. package/dist/core/project-tags.d.ts.map +1 -1
  75. package/dist/core/project-tags.js +29 -1
  76. package/dist/core/project-tags.js.map +1 -1
  77. package/dist/core/schema-export.d.ts.map +1 -1
  78. package/dist/core/schema-export.js +10 -8
  79. package/dist/core/schema-export.js.map +1 -1
  80. package/dist/core/version-check.d.ts +1 -0
  81. package/dist/core/version-check.d.ts.map +1 -1
  82. package/dist/core/version-check.js +35 -0
  83. package/dist/core/version-check.js.map +1 -1
  84. package/dist/core/work-topology.d.ts +3 -0
  85. package/dist/core/work-topology.d.ts.map +1 -1
  86. package/dist/core/work-topology.js +24 -2
  87. package/dist/core/work-topology.js.map +1 -1
  88. package/dist/db.d.ts.map +1 -1
  89. package/dist/db.js +5 -2
  90. package/dist/db.js.map +1 -1
  91. package/dist/host-adapters/claude-channel.d.ts +0 -58
  92. package/dist/host-adapters/claude-channel.d.ts.map +1 -1
  93. package/dist/host-adapters/claude-channel.js +0 -242
  94. package/dist/host-adapters/claude-channel.js.map +1 -1
  95. package/dist/host-adapters/codex-cli-queue.d.ts.map +1 -1
  96. package/dist/host-adapters/codex-cli-queue.js +15 -12
  97. package/dist/host-adapters/codex-cli-queue.js.map +1 -1
  98. package/dist/host-runtime/acp.d.ts +2 -0
  99. package/dist/host-runtime/acp.d.ts.map +1 -1
  100. package/dist/host-runtime/acp.js +6 -1
  101. package/dist/host-runtime/acp.js.map +1 -1
  102. package/dist/host-runtime/claude.d.ts +2 -0
  103. package/dist/host-runtime/claude.d.ts.map +1 -1
  104. package/dist/host-runtime/claude.js +12 -13
  105. package/dist/host-runtime/claude.js.map +1 -1
  106. package/dist/host-runtime/codex-session.d.ts +3 -1
  107. package/dist/host-runtime/codex-session.d.ts.map +1 -1
  108. package/dist/host-runtime/codex-session.js +7 -5
  109. package/dist/host-runtime/codex-session.js.map +1 -1
  110. package/dist/host-runtime/codex.d.ts +2 -0
  111. package/dist/host-runtime/codex.d.ts.map +1 -1
  112. package/dist/host-runtime/codex.js +6 -1
  113. package/dist/host-runtime/codex.js.map +1 -1
  114. package/dist/host-runtime/entry.d.ts +5 -0
  115. package/dist/host-runtime/entry.d.ts.map +1 -0
  116. package/dist/host-runtime/entry.js +11 -0
  117. package/dist/host-runtime/entry.js.map +1 -0
  118. package/dist/host-runtime/router-client.d.ts +2 -0
  119. package/dist/host-runtime/router-client.d.ts.map +1 -1
  120. package/dist/host-runtime/router-client.js.map +1 -1
  121. package/dist/knowledge-graph.d.ts +0 -1
  122. package/dist/knowledge-graph.d.ts.map +1 -1
  123. package/dist/knowledge-graph.js +27 -19
  124. package/dist/knowledge-graph.js.map +1 -1
  125. package/dist/skills-manifest.json +40 -25
  126. package/dist/storage/entity-index.d.ts +4 -0
  127. package/dist/storage/entity-index.d.ts.map +1 -0
  128. package/dist/storage/entity-index.js +15 -0
  129. package/dist/storage/entity-index.js.map +1 -0
  130. package/dist/storage/fts-index.d.ts.map +1 -1
  131. package/dist/storage/fts-index.js +5 -0
  132. package/dist/storage/fts-index.js.map +1 -1
  133. package/dist/storage/graph-repairs.d.ts +11 -1
  134. package/dist/storage/graph-repairs.d.ts.map +1 -1
  135. package/dist/storage/graph-repairs.js +241 -30
  136. package/dist/storage/graph-repairs.js.map +1 -1
  137. package/dist/transports/agent-messaging.d.ts +10 -1
  138. package/dist/transports/agent-messaging.d.ts.map +1 -1
  139. package/dist/transports/agent-messaging.js +92 -9
  140. package/dist/transports/agent-messaging.js.map +1 -1
  141. package/dist/transports/cli/cli.d.ts +4 -0
  142. package/dist/transports/cli/cli.d.ts.map +1 -1
  143. package/dist/transports/cli/cli.js +88 -38
  144. package/dist/transports/cli/cli.js.map +1 -1
  145. package/dist/transports/mcp/handlers.d.ts +12 -8
  146. package/dist/transports/mcp/handlers.d.ts.map +1 -1
  147. package/dist/transports/mcp/handlers.js +23 -11
  148. package/dist/transports/mcp/handlers.js.map +1 -1
  149. package/dist/transports/schemas.d.ts +22 -17
  150. package/dist/transports/schemas.d.ts.map +1 -1
  151. package/dist/transports/schemas.js +25 -11
  152. package/dist/transports/schemas.js.map +1 -1
  153. package/docs/platforms/README.md +2 -2
  154. package/docs/platforms/agent-messaging.md +246 -36
  155. package/hooks/hooks.json +10 -0
  156. package/llms-install.md +29 -6
  157. package/package.json +13 -5
  158. package/scripts/hooks/_generated/agent-message-inbox.js +30 -6
  159. package/scripts/hooks/_generated/citation-rule.js +5 -2
  160. package/scripts/hooks/_generated/fts-index.js +5 -0
  161. package/scripts/hooks/_generated/work-topology.js +24 -2
  162. package/scripts/hooks/_shared.js +52 -4
  163. package/scripts/hooks/decision-nudge.js +152 -0
  164. package/scripts/hooks/post-commit.js +11 -0
  165. package/scripts/hooks/pre-compact.js +12 -4
  166. package/scripts/hooks/session-start.js +61 -26
  167. package/scripts/upgrade-plugin.sh +600 -81
  168. package/skills/memesh/SKILL.md +24 -5
  169. /package/{.mcp.json → .claude-plugin/mcp.json} +0 -0
@@ -4,14 +4,41 @@ MeMesh provides two complementary collaboration surfaces on one machine:
4
4
 
5
5
  - shared durable memory, including the `team` namespace, for knowledge, decisions, and coarse handoffs;
6
6
  - the `message` tool for explicit durable messages to one named recipient on the same MeMesh instance.
7
-
8
- The messaging path is durable store-and-forward. For an explicitly configured,
9
- active local Codex CLI session, MeMesh queues a metadata-only native wakeup;
10
- the session then fetches the durable payload through the scoped `message`
11
- operation. SQLite remains the audit and recovery authority. `poll`/`watch` are
12
- compatibility and diagnostic APIs, not the normal delivery loop for that active
13
- Codex path. A queue admission or `host_accept` is not proof that an agent read
14
- the payload, acknowledged it, or accepted the work.
7
+ - `message discover` for a bounded, project-scoped view of currently live host registrations.
8
+
9
+ Use `memesh message discover --project <name> [--limit 1..100]` to read the
10
+ router directory. Results expose `session_id`, `principal_id`, `host_kind`,
11
+ `project`, declared `model` and `work_summary` (`null` when absent), `active`,
12
+ `generation`, and `lease_expires_at_ms`. Discovery performs no send, fetch, ACK, replay, or
13
+ receipt operation; router unavailability is an explicit error, never an empty
14
+ directory.
15
+
16
+ Briefing follows the same trust boundary. Generic `briefing` and automatic
17
+ SessionStart context have no recipient identity, so they never aggregate or
18
+ announce unread message activity. A caller that already knows its exact
19
+ logical recipient may pass both `project` and `recipient` to `briefing`; the
20
+ result reports only that recipient's unfetched deliveries and tells it to
21
+ `message poll` with the exact scope before fetching each returned
22
+ `message_id`. Fetching remains separate from intake and acknowledgement.
23
+
24
+ The messaging path keeps durable store-and-forward compatibility. For an
25
+ exact active local Codex or Claude session, MeMesh sends one bounded full
26
+ message through the authenticated native host channel and waits for native
27
+ acceptance; no marker-to-fetch step is required. An oversized full envelope
28
+ returns `native_message_too_large`; other unavailable or rejected exact sessions
29
+ return `recipient_unavailable` while scoped recovery data remains durable.
30
+ Principal targets retain asynchronous store-and-forward semantics.
31
+ The JSON-encoded durable payload is limited to 65,536 UTF-8 bytes (64 KiB).
32
+ Native delivery separately limits the complete envelope, including routing
33
+ metadata and payload, to 16,384 bytes (16 KiB). Fitting the durable limit does
34
+ not guarantee that a native envelope fits; exact-session messages should stay
35
+ comfortably below the native cap.
36
+ The size failure is permanent for that exact envelope and is reported as
37
+ `native_message_too_large`, so callers do not retry it as transient session
38
+ unavailability.
39
+ `poll`/`watch` are compatibility and diagnostic APIs. A queue admission or
40
+ `host_accept` is not proof that an agent read the payload, acknowledged it, or
41
+ accepted the work.
15
42
 
16
43
  ## One-time owner-private local-host setup
17
44
 
@@ -63,6 +90,9 @@ memesh agent setup codex --project my-project --principal codex-reviewer --works
63
90
  memesh agent setup claude --project my-project --principal claude-reviewer
64
91
  ```
65
92
 
93
+ Optional declarations can be persisted with `--model <id>` and
94
+ `--work-summary <text>` (each is capped at 200 characters); no defaults are guessed.
95
+
66
96
  ### Ordinary active Codex CLI session
67
97
 
68
98
  `codex-session` is the opt-in path for an ordinary local Codex session and
@@ -72,24 +102,35 @@ the setup command from the exact workspace that Codex will use; it stores the
72
102
  configured real workspace and stable principal in the owner-private
73
103
  `codex-session.json` config. Restart Codex in that workspace after setup.
74
104
 
105
+ This guide's supported documented path is ordinary Codex CLI `SessionStart`.
106
+ Codex Desktop or an unattached task is not user-visible native-delivery
107
+ evidence unless that exact live session registers with the router and the
108
+ result is directly verified. This is a scope boundary for evidence, not a
109
+ claim that Codex Desktop is universally unsupported.
110
+
75
111
  On `SessionStart` for `startup` or `resume`, the asynchronous companion checks
76
112
  the Codex thread identity, hook session identity, and configured workspace
77
113
  realpath before it connects to the router. A missing identity, a different
78
114
  workspace, compact lifecycle input, or a failed/disconnected connection does
79
115
  not register a host and does not wake anything.
80
116
 
81
- For a registered session, MeMesh invokes `codex queue` with a
82
- `memesh_message_available` marker containing only the project, recipient,
83
- target kind, message ID, and delivery ID. The message payload stays in the
84
- durable MeMesh inbox. Codex must then use the `message` tool to `fetch` that
85
- same project/recipient/target/message scope. The persisted `host_accept` means
86
- only that the local Codex queue accepted the marker; it is neither payload
87
- readback nor an `ack` or workflow disposition.
117
+ For a registered session, MeMesh invokes `codex queue` with one untrusted full
118
+ envelope capped at 16,384 bytes (16 KiB), including routing metadata and payload.
119
+ The separate durable JSON-encoded payload limit is 65,536 bytes (64 KiB). The exact-session sender returns
120
+ `native_delivery.status: "native_accepted"` only after the queue accepts it;
121
+ Codex does not need a second `message fetch` to inspect that native message.
122
+ The persisted `host_accept` is neither agent readback nor an `ack` or workflow
123
+ disposition. Codex exposes message text only through its `--message` process
124
+ argument, so same-user process inspection may observe it while the short-lived
125
+ queue command runs; do not put secrets in native messages.
88
126
 
89
127
  If the configured Codex session is stopped, missing, disconnected, or no
90
128
  longer matches its configured workspace, MeMesh does not start or replace it.
91
- The durable inbox and its receipt history remain available to scoped fetch,
92
- cursor recovery, `poll`, or `memesh message watch` for audit and diagnosis.
129
+ An exact-session send reports `recipient_unavailable`; durable scoped recovery
130
+ and receipt history remain available to fetch, cursor recovery, `poll`, or
131
+ `memesh message watch` for audit and diagnosis. Exact-session failures are not
132
+ automatically replayed through the native channel on a later registration; the
133
+ sender must retry deliberately if live delivery is still wanted.
93
134
 
94
135
  ### Separate: MeMesh-managed Codex app-server runner
95
136
 
@@ -105,6 +146,17 @@ process arguments.
105
146
 
106
147
  ### Claude channel runner
107
148
 
149
+ When `memesh doctor` is running from a Claude Code plugin cache, its default
150
+ `Claude Channel registration` row inspects only the user-scoped
151
+ `mcpServers.memesh-channel` entry in Claude's canonical user config and the
152
+ owner-private config file named by its `--config` argument. Missing
153
+ registration is a warning: durable MCP/inbox messaging can still work, while
154
+ live Channel notification remains inactive until the upstream research-preview
155
+ channel is explicitly opted into. A malformed, stale, or insecure target is
156
+ also a warning. `CONFIGURED` means only that the declaration and target are
157
+ coherent; it does not verify development-channel admission or that an agent
158
+ surfaced the notification.
159
+
108
160
  Run the printed `registration_command` once to add `memesh-channel` as a
109
161
  user-scoped stdio MCP server. Claude owns that process for the session:
110
162
 
@@ -124,7 +176,7 @@ claude --dangerously-load-development-channels server:memesh-channel
124
176
 
125
177
  Without that flag Claude may initialize the ordinary MCP transport while
126
178
  silently dropping channel events; a MeMesh `host_accept` then proves only that
127
- the notification was written to stdio, not that Claude admitted it. With the
179
+ the bounded message was written to stdio, not that Claude surfaced it. With the
128
180
  channel admitted, initialization creates and registers the exact MeMesh
129
181
  session automatically; EOF, MCP close, or normal signals unregister it.
130
182
 
@@ -145,23 +197,181 @@ for active Codex-session delivery.
145
197
  ## What Works Today
146
198
 
147
199
  - MCP, HTTP, and CLI use the same message lifecycle and SQLite system of record.
148
- - `send` creates one canonical message, recipient delivery, and payload-free notification event under an idempotency key.
200
+ - `send` creates one canonical message, recipient delivery, and payload-free notification event under an idempotency key. Exact-session success additionally requires native host acceptance; an oversized envelope returns `native_message_too_large`, while other unavailable or rejected sessions return `recipient_unavailable`, with recovery state preserved.
149
201
  - `poll` and `memesh message watch` return only events for the exact project and recipient. They are compatibility and diagnostic paths; the opaque cursor can be persisted and reused after a timeout, dropped hint, duplicate delivery, or process restart.
150
202
  - `fetch` returns the payload only to the named recipient and matching `target_kind` in the named project. Exact-session messages require `target_kind=session`; polling and fetching do not acknowledge the message.
151
203
  - `intake`, `ack`, `disposition`, and `activation` are explicit, separate, idempotent receipt facts. Inbox/MCP ACK is valid without a host-native acceptance; host-native ACK remains bound to its `host_accept`. `receipts` returns one ordered projection and identifies each underlying fact source. For example, `manual_resume_required` does not imply ACK, acceptance, rejection, cancellation, or completion.
152
204
  - The transport, rather than model-provided payload data, records sender-host provenance.
153
205
 
206
+ ## Repeatable owner-run live checks
207
+
208
+ Everything above is checked by the test suite against stubs and fakes. That
209
+ proves the plumbing and nothing about a live model: a queue admission or a
210
+ `host_accept` is a statement about a frame, not about cognition. Two checks
211
+ close that gap by requiring evidence that could only have come out of a running
212
+ model.
213
+
214
+ ```bash
215
+ TMPDIR=/private/tmp npm run qa:live-journey -- --host codex --out .qa/codex-report.json
216
+ TMPDIR=/private/tmp npm run qa:live-journey -- --host claude --out .qa/claude-report.json
217
+ ```
218
+
219
+ `.qa/` is where `npm run release:finish` looks for these reports (any ONE
220
+ host's PASS, against the exact commit being released, is enough — see
221
+ `scripts/lib/release-preconditions.mjs`'s `findUsableLiveJourneyReceipt`). The
222
+ directory is gitignored; a report is owner-machine evidence, never shipped.
223
+
224
+ `TMPDIR` is not decoration on macOS. The router's Unix socket lives beside the
225
+ database inside the temporary directory, and `AF_UNIX` caps a socket path at
226
+ 104 bytes; the platform default `os.tmpdir()` spends about half of that before
227
+ the check adds anything. The script measures its own socket path and refuses
228
+ with this hint rather than starting a router that cannot bind.
229
+
230
+ `scripts/qa/live-journey.mjs` is owner-run and refuses to start when `CI` is
231
+ set, because neither check can run unattended: one needs the owner's Codex
232
+ login, the other needs a person at an interactive Claude session. Its argument
233
+ parsing, its refusals, and every **pure** assertion it makes are unit-tested in
234
+ `tests/qa/live-journey.test.ts`, which does run in CI against recorded
235
+ fixtures; the orchestration around them is exercised only by a live run.
236
+
237
+ Everything MeMesh writes goes into a fresh `mktemp` MEMESH_DIR that is deleted
238
+ on exit (`--keep` retains it), against this repository's own `dist/`. The check
239
+ refuses to start if that directory would resolve inside `$HOME/.memesh` — the
240
+ comparison is made on **real** paths, before anything is created, so a
241
+ symlinked `TMPDIR` cannot get past it — or if `dist/` has not been built. It
242
+ reads no authentication file. Where that isolation stops is listed under
243
+ limitations below, and the report records whether the working tree was dirty
244
+ and whether `dist/` predates the newest file under `src/`.
245
+
246
+ Shutdown order is part of the design rather than an afterthought. A connected
247
+ host that sees the router socket disappear starts a **detached** packaged
248
+ router inheriting its own environment — including this check's `MEMESH_DIR` —
249
+ and the router recreates its data directory on start. The check therefore stops
250
+ the companion, waits for live sessions to disconnect, stops the router, and
251
+ only then removes the directory; if a session is still connected when the wait
252
+ expires it keeps the directory rather than racing that spawn. The same sequence
253
+ runs on failures and on `SIGINT`/`SIGTERM`.
254
+
255
+ **`--host codex`** starts the router, runs `memesh agent setup codex-session`,
256
+ creates one real Codex CLI thread with `codex exec`, registers that thread,
257
+ sends one exact-session message, and then resumes the thread with a fixed
258
+ prompt that names neither the sentinel nor any identifier. The reply must quote
259
+ the envelope's `message_id` and `delivery_id` back, **and** that turn must have
260
+ produced nothing but an answer. Both halves matter: a `read-only` Codex sandbox
261
+ still permits reads, so a turn that ran one command could have taken the
262
+ identifiers off disk instead of out of the envelope. The Codex workspace is a
263
+ separate temporary tree for the same reason — the database and this run's own
264
+ logs are not one `..` away from it. The check then stops the companion and requires the next send to return
265
+ `recipient_unavailable` while `message fetch` still returns the payload.
266
+
267
+ **`--host claude`** starts the router, runs `memesh agent setup claude`, writes
268
+ a temporary MCP config, and prints the exact interactive launch command — which
269
+ includes `--setting-sources ""` so that no user, project, or local settings
270
+ file is loaded. The operator runs it, confirms with `/mcp` and `/hooks` that
271
+ only the two servers from `--mcp-config` are present, and then types nothing. The check waits for the session to appear
272
+ in `message discover`, sends one exact-session message, and then waits for an
273
+ `intake` receipt on that message whose actor is that session — the model must
274
+ call `intake` itself, which is what makes the proof model-visible rather than
275
+ transport-visible. The operator is then asked to exit the session, and the same
276
+ fail-closed assertion runs.
277
+
278
+ Print mode (`claude -p`) is **not supported** and is deliberately not
279
+ exercised. A print-mode session does not surface `memesh-channel` notifications
280
+ to the model even when the channel host reports the frame accepted, so it can
281
+ never produce the receipt this check requires.
282
+
283
+ Each run writes a JSON report: the repository revision, every `message_id` and
284
+ `delivery_id`, the `native_delivery` receipts, the model-visible evidence, and
285
+ a `limitations` list. The exit code is 0 only when every required step passed.
286
+ The limitations these checks always declare:
287
+
288
+ - The Codex **registration** half is harness-driven: the check drives the
289
+ shipped `src/host-runtime/codex-session.ts` companion directly with the
290
+ `SessionStart` payload the packaged plugin hook supplies, because a scripted
291
+ `codex exec` turn was not observed to register anything on its own. *Why* the
292
+ plugin hook does not run there is not established — `--ignore-user-config` is
293
+ documented only as skipping `config.toml`, and on a machine whose
294
+ `~/.memesh/hosts` has no `codex-session.json` the shipped companion would
295
+ return early regardless. Dispatch → `codex queue` → model-visible reply is
296
+ product-path evidence; the registration step is not.
297
+ - The interactive Claude session is **outside** this check's isolation.
298
+ `--setting-sources ""` is accepted by the CLI (an invalid source name is
299
+ rejected, an empty list is not), but it is not verified to exclude
300
+ plugin-provided hooks or MCP servers. A MeMesh plugin hook running in that
301
+ session inherits no `MEMESH_DIR` and would write the owner's real
302
+ `~/.memesh`. The operator is told to confirm with `/mcp` and `/hooks` first,
303
+ and the check cannot observe whether they did.
304
+ - `--host codex` creates one throwaway thread in the owner's Codex rollout
305
+ store and queues one message into it. That is session state, not
306
+ configuration; nothing outside the temporary directory is otherwise written.
307
+ - The Claude operator is told to type nothing, but the check cannot observe
308
+ whether anything was typed. The intake receipt proves the model called
309
+ `intake` in that session; it does not prove it did so unprompted.
310
+ - The Claude intake receipt is matched on its `actor`, which `intake` sets from
311
+ the caller's `recipient`. The model must intake under its own session id; an
312
+ intake recorded against the principal id would not match, and the check would
313
+ report no model-visible proof.
314
+ - `recipient_unavailable` is a shared failure surface — the same string is
315
+ returned when the *sender* cannot reach the router. The fail-closed step
316
+ therefore also records that `message discover` still answered and that
317
+ `message fetch` still returned the payload. That pairing, not the string, is
318
+ what attributes the failure to the stopped recipient.
319
+
320
+ ## Scope identifiers
321
+
322
+ `project` and `recipient` together key one inbox, and `actor` is derived from
323
+ `recipient`, so how those three are spelled is part of the contract rather than
324
+ a formatting detail. Two spellings of one name are two inboxes: the recipient
325
+ that fetches under one never sees what was sent under the other, and a
326
+ `briefing` unread count is computed per spelling.
327
+
328
+ Every message action canonicalises them to Unicode NFC and trims surrounding
329
+ whitespace, on reads as well as writes, so a decomposed spelling reaches the
330
+ rows a composed one wrote.
331
+
332
+ A value spelled as an **absolute filesystem path is refused** — a POSIX path
333
+ (`/root`), a Windows drive path (`C:\work`), or a UNC path (`\\host\share`).
334
+ Project identity is derived from the working directory and can never take that
335
+ shape, so such a value is not an identity MeMesh produced; the error names the
336
+ field and a valid value. Callers that previously passed a home directory or a
337
+ checkout path where the agent's or project's NAME belonged must pass the name.
338
+ Nothing else is rewritten: identifiers are compared exactly, case included, and
339
+ an identifier that merely contains a separator (`team/reviewer`) is accepted.
340
+ MeMesh does not treat any prefix as a namespace, so `claude-code:reviewer` and
341
+ `reviewer` are two different recipients.
342
+
343
+ The same rule covers every surface that reads or writes that key, not only the
344
+ `message` tool: `briefing` counts unfetched deliveries for one exact
345
+ (`project`, `recipient`), and `memesh agent setup --project/--principal` writes
346
+ the identity a host will register under. A path-shaped value is refused there
347
+ too, at the moment the config is written, rather than surfacing later as an
348
+ error about some other agent's send.
349
+
350
+ `sender` is not covered by any of this. It is provenance rather than routing —
351
+ it keys no inbox, and it keys the send idempotency record — so it is stored
352
+ exactly as given, and the transport-bound provenance remains the field to trust.
353
+
354
+ Rows written before this rule are repaired once, in place, at the first
355
+ database open after upgrade. Renaming a project across both its entity tags and
356
+ its message scopes is a separate, deliberate, owner-run operation:
357
+
358
+ ```bash
359
+ memesh kg rename-project --from <old> --to <new> # dry run
360
+ memesh kg rename-project --from <old> --to <new> --apply # backs up first
361
+ ```
362
+
154
363
  ## Identity and lifecycle
155
364
 
156
365
  A **principal** is the stable logical recipient. A **session** is one live host connection for that principal. A **generation** changes when that session is replaced. An exact-session target never reroutes. A principal target can deliver only to an eligible active session after its activation checkpoint; it does not replay historical inbox contents into a first session.
157
366
 
158
367
  Persistence, dispatch attempt, host acceptance, intake, acknowledgement,
159
368
  workflow disposition, retention, and presence are independent state axes. An
160
- active configured Codex session receives a host-native metadata marker without
161
- polling. A stopped, missing, busy beyond its queue limit, disconnected, or
162
- unsupported session is not awakened, resumed, or replaced; durable state
163
- remains available for audit and recovery, subject to exact-session and
164
- activation-checkpoint rules.
369
+ active configured exact session receives a bounded full message without
370
+ polling or an inbox fetch. An oversized full envelope returns
371
+ `native_message_too_large`. A stopped, missing, busy beyond its queue limit,
372
+ disconnected, or unsupported session returns `recipient_unavailable` and is
373
+ not awakened, resumed, or replaced; durable state remains available for audit
374
+ and recovery, subject to exact-session and activation-checkpoint rules.
165
375
 
166
376
  ## Bounded storage and audit retention
167
377
 
@@ -206,9 +416,9 @@ quota or automatic retention policy.
206
416
  This guide describes only one local MeMesh instance: its SQLite durable event
207
417
  store and same-machine host-native input. Remote and cross-machine transport is
208
418
  the responsibility of MeMesh Cloud and requires its own verified relay; Cloud
209
- state is not evidence that this local host received a marker. A native marker,
210
- persistence, or fetch does not promise exactly-once cognition, a reply, or a
211
- stopped-session wake-up.
419
+ state is not evidence that this local host accepted a native message. Native
420
+ acceptance, persistence, or fetch does not promise exactly-once cognition, a
421
+ reply, or a stopped-session wake-up.
212
422
 
213
423
  ## What This Is Not Yet
214
424
 
@@ -223,7 +433,7 @@ stopped-session wake-up.
223
433
 
224
434
  | Participant | Current path | Status today | Notes |
225
435
  |---|---|---|---|
226
- | Ordinary Codex CLI | `codex-session` owner-private opt-in | metadata-only native wakeup while active | Exact workspace, principal, and SessionStart identity must match; stopped or disconnected sessions are not awakened |
436
+ | Ordinary Codex CLI | `codex-session` owner-private opt-in | bounded full-message native delivery while active | Exact workspace, principal, and SessionStart identity must match; oversized envelopes return `native_message_too_large`, while stopped or disconnected sessions return `recipient_unavailable` |
227
437
  | MeMesh-managed Codex app-server | `memesh-host-codex` | separate managed path | It creates its own Codex thread; it does not attach to an ordinary session |
228
438
  | Claude channel | `memesh-host-claude` | separate channel path | Requires the documented Channel opt-in; no stopped-session resume |
229
439
  | Other local MCP clients | MCP, HTTP, or CLI message operations | durable messaging only | Use `poll`/`watch` and scoped fetch where their own host loop supports it; this guide makes no native-wakeup claim |
@@ -231,19 +441,19 @@ stopped-session wake-up.
231
441
  ## Lifecycle
232
442
 
233
443
  1. A sender calls `message` with `action: "send"`, a stable sender, one recipient, a project, an idempotency key, and a payload.
234
- 2. For an eligible ordinary Codex session, the router queues only a
235
- privacy-minimized `memesh_message_available` marker. An explicit
236
- `poll`/`watch` client may instead read privacy-minimized events for
237
- compatibility or diagnosis.
238
- 3. Codex calls `fetch` with the marker's project, recipient, target kind, and
239
- message ID to read the durable payload. The marker and queue admission do
240
- not acknowledge the message.
444
+ 2. For an eligible exact Codex or Claude session, the router sends one bounded
445
+ untrusted full envelope and waits for native host acceptance. An explicit
446
+ `poll`/`watch` client may still read privacy-minimized events for recovery,
447
+ compatibility, or diagnosis.
448
+ 3. Native acceptance returns `native_delivery.status: "native_accepted"`; an
449
+ absent or rejected exact session returns `recipient_unavailable`. Neither
450
+ outcome records an agent acknowledgement or workflow disposition.
241
451
  4. The receiver records only the facts that actually happened:
242
452
  - `intake`: payload fetched or durably ingested;
243
453
  - `ack`: explicit recipient acknowledgement;
244
454
  - `disposition`: accepted, rejected, completed, cancelled, or deferred;
245
455
  - `activation`: woken, manual resume required, unsupported, or failed.
246
- 5. After router or host restart, registration drains only eligible durable deliveries. A manual cursor replay may repeat an event, so application intake still uses its own idempotency key.
456
+ 5. After router or host restart, registration drains only eligible durable principal deliveries. Failed exact-session native delivery requires an explicit retry. A manual cursor replay may repeat an event, so application intake still uses its own idempotency key.
247
457
 
248
458
  `correlation_id` and `reply_to` can connect messages, but they do not change delivery or routing.
249
459
 
package/hooks/hooks.json CHANGED
@@ -55,6 +55,16 @@
55
55
  "timeout": 5
56
56
  }
57
57
  ]
58
+ },
59
+ {
60
+ "matcher": "ExitPlanMode|AskUserQuestion",
61
+ "hooks": [
62
+ {
63
+ "type": "command",
64
+ "command": "${CLAUDE_PLUGIN_ROOT}/scripts/hooks/decision-nudge.js",
65
+ "timeout": 5
66
+ }
67
+ ]
58
68
  }
59
69
  ],
60
70
  "Stop": [
package/llms-install.md CHANGED
@@ -129,14 +129,15 @@ memesh agent setup codex-session --project my-project --principal codex-recipien
129
129
  This stores the configured workspace realpath and principal in
130
130
  `~/.memesh/hosts/codex-session.json`. On `SessionStart` (`startup` or
131
131
  `resume`), an asynchronous companion registers only when its Codex thread ID,
132
- hook session ID, and workspace realpath match that config. It receives no
133
- message payload. Instead, an active registered session receives a
134
- metadata-only `memesh_message_available` queue marker with routing identifiers,
135
- then uses the scoped `message` `fetch` operation to read the durable payload.
132
+ hook session ID, and workspace realpath match that config. The authenticated
133
+ router sends the active exact session one bounded full message through native
134
+ `codex queue`; no second `message fetch` is required for that live delivery.
136
135
 
137
- `host_accept` records only that the local Codex queue accepted that marker. It
136
+ `host_accept` records only that the local Codex queue accepted that message. It
138
137
  does not prove an agent read the payload, acknowledged it, or accepted the
139
- work. If the session is stopped, missing, disconnected, or in another
138
+ work. Codex exposes message text through its `--message` process argument, so
139
+ same-user process inspection may observe it while the queue command runs; do
140
+ not send secrets through the native path. If the session is stopped, missing, disconnected, or in another
140
141
  workspace, MeMesh neither starts nor replaces it; the durable inbox remains
141
142
  available to scoped fetch, cursor recovery, `poll`, and `memesh message watch`
142
143
  for audit and diagnosis.
@@ -207,6 +208,28 @@ codex mcp list
207
208
 
208
209
  Expected: `memesh` is listed as enabled.
209
210
 
211
+ ### Optional: Codex plugin marketplace
212
+
213
+ Current Codex CLI versions can install the repository's plugin marketplace
214
+ directly. For a fresh install:
215
+
216
+ ```
217
+ codex plugin marketplace add PCIRCLE-AI/memesh
218
+ codex plugin add memesh@pcircle-memesh
219
+ ```
220
+
221
+ If the configured marketplace snapshot is stale, refresh it and reinstall the
222
+ plugin:
223
+
224
+ ```
225
+ codex plugin marketplace upgrade pcircle-memesh
226
+ codex plugin remove memesh
227
+ codex plugin add memesh@pcircle-memesh
228
+ ```
229
+
230
+ These commands are separate from the MCP registration above; keep the MCP
231
+ path when a host needs `memesh-mcp` directly.
232
+
210
233
  | Failure | Remedy |
211
234
  |---|---|
212
235
  | `command not found: codex` | Codex CLI itself is not installed — out of scope here; install it first, then re-run the add. |
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pcircle/memesh",
3
- "version": "4.8.2",
4
- "description": "MeMesh \u2014 agentic memory for coding agents. Captured from the agent's real work via hooks, recalled when it acts. One SQLite file, zero cloud required.",
3
+ "version": "4.8.5",
4
+ "description": "MeMesh agentic memory for coding agents. Captured from the agent's real work via hooks, recalled when it acts. One SQLite file, zero cloud required.",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
7
7
  "bin": {
@@ -17,13 +17,13 @@
17
17
  "files": [
18
18
  "dist/",
19
19
  "dashboard/dist/",
20
- ".mcp.json",
21
20
  "hooks/hooks.json",
22
21
  "scripts/hooks/",
23
22
  "scripts/upgrade-plugin.sh",
24
23
  "skills/",
25
24
  "docs/platforms/agent-messaging.md",
26
25
  ".claude-plugin/",
26
+ ".codex-plugin/",
27
27
  "README.md",
28
28
  "LICENSE",
29
29
  "llms-install.md",
@@ -41,20 +41,28 @@
41
41
  "test:issue-228": "vitest run tests/dashboard/FeedbackWidget.test.tsx --maxWorkers=1",
42
42
  "test:issue-229": "vitest run tests/dashboard/OnboardingBanner.test.tsx tests/dashboard-i18n.test.ts --maxWorkers=1",
43
43
  "test:issue-230": "vitest run tests/dashboard/OnboardingBanner.test.tsx tests/transports/http.test.ts tests/core/demo.test.ts --maxWorkers=1",
44
+ "test:issue-241": "vitest run tests/core/lesson-engine.test.ts tests/storage/graph-repairs.test.ts tests/audit/memory-invariants.test.ts --maxWorkers=1",
45
+ "test:issue-242": "node scripts/run-tests-isolated.mjs tests/core/briefing.test.ts tests/core/work-topology.test.ts tests/hooks/session-start.test.ts --maxWorkers=1",
46
+ "test:issue-267": "vitest run tests/core/briefing.test.ts tests/core/schema-export.test.ts tests/hooks/session-start.test.ts tests/cli/briefing-recipient.test.ts --maxWorkers=1",
44
47
  "test:packaged": "node scripts/smoke-packed-artifact.mjs",
45
48
  "test:packaged:upgrade": "node scripts/smoke-packed-upgrade.mjs",
46
49
  "test:surface-parity": "vitest run tests/readme-tool-parity.test.ts tests/codemap-parity.test.ts tests/core/schema-export.test.ts && node scripts/check-version-coherence.mjs && node scripts/check-generated-mirror.mjs && npm run test:packaged",
47
50
  "check:surface-parity": "node scripts/check-readme-tool-parity.mjs && node scripts/check-codemap-parity.mjs",
48
51
  "audit:prod": "node scripts/check-consumer-audit.mjs",
49
52
  "audit:memory": "node scripts/audit/memory-invariants.mjs",
50
- "verify:release": "npm run lint && npm run typecheck && node scripts/check-version-coherence.mjs && node scripts/check-generated-mirror.mjs && node scripts/check-agent-message-sync.mjs && npm run check:surface-parity && node scripts/check-doc-claims.mjs && node scripts/audit/verification-audit.mjs && npm run audit:prod",
53
+ "check:entry-points-start": "node scripts/check-entry-points-start.mjs",
54
+ "verify:release": "npm run lint && npm run typecheck && node scripts/check-version-coherence.mjs && node scripts/check-generated-mirror.mjs && node scripts/check-agent-message-sync.mjs && npm run check:surface-parity && node scripts/check-doc-claims.mjs && node scripts/audit/verification-audit.mjs && npm run audit:prod && npm run check:entry-points-start",
51
55
  "release:finish": "node scripts/finish-release.mjs",
52
- "prepublishOnly": "npm run build && npm run verify:release && npm run test:isolated && npm run test:packaged && npm run test:packaged:upgrade",
56
+ "verify:artifact": "npm run verify:release && npm run test:isolated && npm run test:packaged && npm run test:packaged:upgrade",
57
+ "prepublishOnly": "npm run build && npm run verify:artifact",
53
58
  "typecheck": "tsc -p tsconfig.check.json && tsc -p tsconfig.check-dashboard.json",
54
59
  "lint": "eslint src/ scripts/ tests/ dashboard/src/ --max-warnings 0",
55
60
  "lint:fix": "eslint src/ scripts/ tests/ dashboard/src/ --fix",
56
61
  "start": "node dist/mcp/server.js",
57
62
  "bench:longmemeval": "node benchmarks/longmemeval/run.mjs --mode A --dataset /tmp/longmemeval_s.json",
63
+ "qa:live-journey": "node scripts/qa/live-journey.mjs",
64
+ "qa:pre-release": "node scripts/qa/pre-release.mjs",
65
+ "qa:post-release": "node scripts/qa/post-release.mjs",
58
66
  "test:isolated": "node scripts/run-tests-isolated.mjs",
59
67
  "test:coverage": "node scripts/run-tests-isolated.mjs --coverage"
60
68
  },
@@ -6,18 +6,21 @@
6
6
  // always-on capture path survives a missing or stale dist/ while staying
7
7
  // byte-locked to core — eliminating the hand-mirror drift behind the P0 FTS bug.
8
8
  // ============================================================================
9
- export function unreadDeliveryCount(db, project) {
9
+ export function unreadDeliveryCount(db, project, recipient) {
10
+ if (!recipient)
11
+ return 0;
10
12
  try {
11
13
  const row = db.prepare(`SELECT COUNT(*) AS n
12
14
  FROM agent_message_deliveries d
13
15
  WHERE d.project = ?
16
+ AND d.recipient = ?
14
17
  AND NOT EXISTS (
15
18
  SELECT 1 FROM agent_message_receipts r
16
19
  WHERE r.project = d.project
17
20
  AND r.recipient = d.recipient
18
21
  AND r.message_id = d.message_id
19
22
  AND r.receipt_kind = 'intake'
20
- )`).get(project);
23
+ )`).get(project, recipient);
21
24
  const n = row?.n;
22
25
  return typeof n === 'number' && n > 0 ? n : 0;
23
26
  }
@@ -25,9 +28,30 @@ export function unreadDeliveryCount(db, project) {
25
28
  return 0;
26
29
  }
27
30
  }
28
- export function unreadInboxLines(count, project) {
29
- if (count <= 0)
31
+ export function recipientEverSeen(db, project, recipient) {
32
+ try {
33
+ const row = db.prepare(`SELECT (
34
+ EXISTS(SELECT 1 FROM agent_principals WHERE project = ? AND principal_id = ?)
35
+ OR EXISTS(SELECT 1 FROM agent_message_deliveries WHERE project = ? AND recipient = ?)
36
+ OR EXISTS(SELECT 1 FROM agent_session_instances WHERE project = ? AND session_instance_id = ?)
37
+ ) AS seen`).get(project, recipient, project, recipient, project, recipient);
38
+ return row?.seen === undefined ? undefined : Boolean(row.seen);
39
+ }
40
+ catch {
41
+ return undefined;
42
+ }
43
+ }
44
+ export function unreadInboxLines(count, project, recipient, everSeen) {
45
+ if (!recipient)
30
46
  return [];
31
- const noun = count === 1 ? 'message' : 'messages';
32
- return [`${count} ${noun} waiting for "${project}" — fetch them with the message tool; fetching does not acknowledge.`];
47
+ const displayProject = JSON.stringify(project);
48
+ const displayRecipient = JSON.stringify(recipient);
49
+ if (count > 0) {
50
+ const noun = count === 1 ? 'message' : 'messages';
51
+ return [`${count} ${noun} waiting for ${displayRecipient} in project ${displayProject} — poll the message tool with project ${displayProject} and recipient ${displayRecipient}, then fetch each message_id; fetching does not acknowledge.`];
52
+ }
53
+ if (everSeen === false) {
54
+ return [`No messages waiting for ${displayRecipient} in project ${displayProject} — and this recipient id has never been seen in this project (check for a typo).`];
55
+ }
56
+ return [];
33
57
  }
@@ -25,8 +25,11 @@ memory is recorded as unused: that is how MeMesh learns which memories are
25
25
  worth the tokens they cost you.
26
26
  `;
27
27
  export function citationRuleDir(scope, home, cwd) {
28
- const base = scope === 'user' ? home : cwd;
29
- return path.join(base, '.claude', 'rules');
28
+ if (scope === 'project')
29
+ return path.join(cwd, '.claude', 'rules');
30
+ const relocated = process.env.CLAUDE_CONFIG_DIR;
31
+ const configRoot = relocated ? path.resolve(relocated) : path.join(home, '.claude');
32
+ return path.join(configRoot, 'rules');
30
33
  }
31
34
  export function citationRulePath(scope, home, cwd) {
32
35
  return path.join(citationRuleDir(scope, home, cwd), CITATION_RULE_FILENAME);
@@ -67,6 +67,11 @@ function foldTitleIntoObservations(title, observationsText) {
67
67
  }
68
68
  export function removeFromFts(db, entityId, name, prevObsText, prevTitle) {
69
69
  try {
70
+ const indexed = db
71
+ .prepare('SELECT COUNT(*) AS c FROM entities_fts WHERE rowid = ?')
72
+ .get(entityId);
73
+ if (!indexed || indexed.c === 0)
74
+ return;
70
75
  db.prepare("INSERT INTO entities_fts (entities_fts, rowid, name, observations) VALUES('delete', ?, ?, ?)").run(entityId, toIndexForm(name), toIndexForm(foldTitleIntoObservations(prevTitle, prevObsText)));
71
76
  }
72
77
  catch (err) {