@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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +2 -1
- package/.codex-plugin/mcp.json +7 -0
- package/.codex-plugin/plugin.json +6 -0
- package/AGENTS.md +18 -8
- package/README.de.md +13 -11
- package/README.md +25 -12
- package/README.zh-TW.md +25 -12
- package/dashboard/dist/index.html +3 -3
- package/dist/core/agent-message-inbox.d.ts +3 -2
- package/dist/core/agent-message-inbox.d.ts.map +1 -1
- package/dist/core/agent-message-inbox.js +30 -6
- package/dist/core/agent-message-inbox.js.map +1 -1
- package/dist/core/agent-messaging.d.ts +7 -0
- package/dist/core/agent-messaging.d.ts.map +1 -1
- package/dist/core/agent-messaging.js +51 -24
- package/dist/core/agent-messaging.js.map +1 -1
- package/dist/core/agent-router.d.ts +27 -20
- package/dist/core/agent-router.d.ts.map +1 -1
- package/dist/core/agent-router.js +114 -43
- package/dist/core/agent-router.js.map +1 -1
- package/dist/core/agent-scope-id.d.ts +11 -0
- package/dist/core/agent-scope-id.d.ts.map +1 -0
- package/dist/core/agent-scope-id.js +40 -0
- package/dist/core/agent-scope-id.js.map +1 -0
- package/dist/core/briefing.d.ts +1 -1
- package/dist/core/briefing.d.ts.map +1 -1
- package/dist/core/briefing.js +25 -7
- package/dist/core/briefing.js.map +1 -1
- package/dist/core/citation-rule.d.ts.map +1 -1
- package/dist/core/citation-rule.js +5 -2
- package/dist/core/citation-rule.js.map +1 -1
- package/dist/core/doctor.d.ts +9 -0
- package/dist/core/doctor.d.ts.map +1 -1
- package/dist/core/doctor.js +461 -24
- package/dist/core/doctor.js.map +1 -1
- package/dist/core/dreamer.d.ts.map +1 -1
- package/dist/core/dreamer.js +3 -1
- package/dist/core/dreamer.js.map +1 -1
- package/dist/core/embedder.d.ts.map +1 -1
- package/dist/core/embedder.js +4 -1
- package/dist/core/embedder.js.map +1 -1
- package/dist/core/install-channel.d.ts +3 -0
- package/dist/core/install-channel.d.ts.map +1 -1
- package/dist/core/install-channel.js +79 -2
- package/dist/core/install-channel.js.map +1 -1
- package/dist/core/install-hooks.d.ts.map +1 -1
- package/dist/core/install-hooks.js +10 -6
- package/dist/core/install-hooks.js.map +1 -1
- package/dist/core/lesson-slug.d.ts.map +1 -1
- package/dist/core/lesson-slug.js +6 -4
- package/dist/core/lesson-slug.js.map +1 -1
- package/dist/core/lifecycle.d.ts.map +1 -1
- package/dist/core/lifecycle.js +8 -5
- package/dist/core/lifecycle.js.map +1 -1
- package/dist/core/llm-client.d.ts.map +1 -1
- package/dist/core/llm-client.js +4 -1
- package/dist/core/llm-client.js.map +1 -1
- package/dist/core/llm-validator.d.ts.map +1 -1
- package/dist/core/llm-validator.js +6 -15
- package/dist/core/llm-validator.js.map +1 -1
- package/dist/core/memory-tool.d.ts.map +1 -1
- package/dist/core/memory-tool.js +3 -1
- package/dist/core/memory-tool.js.map +1 -1
- package/dist/core/ollama-host.d.ts +6 -0
- package/dist/core/ollama-host.d.ts.map +1 -0
- package/dist/core/ollama-host.js +62 -0
- package/dist/core/ollama-host.js.map +1 -0
- package/dist/core/operations.d.ts +1 -1
- package/dist/core/operations.d.ts.map +1 -1
- package/dist/core/operations.js +1 -1
- package/dist/core/operations.js.map +1 -1
- package/dist/core/project-tags.d.ts +2 -0
- package/dist/core/project-tags.d.ts.map +1 -1
- package/dist/core/project-tags.js +29 -1
- package/dist/core/project-tags.js.map +1 -1
- package/dist/core/schema-export.d.ts.map +1 -1
- package/dist/core/schema-export.js +10 -8
- package/dist/core/schema-export.js.map +1 -1
- package/dist/core/version-check.d.ts +1 -0
- package/dist/core/version-check.d.ts.map +1 -1
- package/dist/core/version-check.js +35 -0
- package/dist/core/version-check.js.map +1 -1
- package/dist/core/work-topology.d.ts +3 -0
- package/dist/core/work-topology.d.ts.map +1 -1
- package/dist/core/work-topology.js +24 -2
- package/dist/core/work-topology.js.map +1 -1
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +5 -2
- package/dist/db.js.map +1 -1
- package/dist/host-adapters/claude-channel.d.ts +0 -58
- package/dist/host-adapters/claude-channel.d.ts.map +1 -1
- package/dist/host-adapters/claude-channel.js +0 -242
- package/dist/host-adapters/claude-channel.js.map +1 -1
- package/dist/host-adapters/codex-cli-queue.d.ts.map +1 -1
- package/dist/host-adapters/codex-cli-queue.js +15 -12
- package/dist/host-adapters/codex-cli-queue.js.map +1 -1
- package/dist/host-runtime/acp.d.ts +2 -0
- package/dist/host-runtime/acp.d.ts.map +1 -1
- package/dist/host-runtime/acp.js +6 -1
- package/dist/host-runtime/acp.js.map +1 -1
- package/dist/host-runtime/claude.d.ts +2 -0
- package/dist/host-runtime/claude.d.ts.map +1 -1
- package/dist/host-runtime/claude.js +12 -13
- package/dist/host-runtime/claude.js.map +1 -1
- package/dist/host-runtime/codex-session.d.ts +3 -1
- package/dist/host-runtime/codex-session.d.ts.map +1 -1
- package/dist/host-runtime/codex-session.js +7 -5
- package/dist/host-runtime/codex-session.js.map +1 -1
- package/dist/host-runtime/codex.d.ts +2 -0
- package/dist/host-runtime/codex.d.ts.map +1 -1
- package/dist/host-runtime/codex.js +6 -1
- package/dist/host-runtime/codex.js.map +1 -1
- package/dist/host-runtime/entry.d.ts +5 -0
- package/dist/host-runtime/entry.d.ts.map +1 -0
- package/dist/host-runtime/entry.js +11 -0
- package/dist/host-runtime/entry.js.map +1 -0
- package/dist/host-runtime/router-client.d.ts +2 -0
- package/dist/host-runtime/router-client.d.ts.map +1 -1
- package/dist/host-runtime/router-client.js.map +1 -1
- package/dist/knowledge-graph.d.ts +0 -1
- package/dist/knowledge-graph.d.ts.map +1 -1
- package/dist/knowledge-graph.js +27 -19
- package/dist/knowledge-graph.js.map +1 -1
- package/dist/skills-manifest.json +40 -25
- package/dist/storage/entity-index.d.ts +4 -0
- package/dist/storage/entity-index.d.ts.map +1 -0
- package/dist/storage/entity-index.js +15 -0
- package/dist/storage/entity-index.js.map +1 -0
- package/dist/storage/fts-index.d.ts.map +1 -1
- package/dist/storage/fts-index.js +5 -0
- package/dist/storage/fts-index.js.map +1 -1
- package/dist/storage/graph-repairs.d.ts +11 -1
- package/dist/storage/graph-repairs.d.ts.map +1 -1
- package/dist/storage/graph-repairs.js +241 -30
- package/dist/storage/graph-repairs.js.map +1 -1
- package/dist/transports/agent-messaging.d.ts +10 -1
- package/dist/transports/agent-messaging.d.ts.map +1 -1
- package/dist/transports/agent-messaging.js +92 -9
- package/dist/transports/agent-messaging.js.map +1 -1
- package/dist/transports/cli/cli.d.ts +4 -0
- package/dist/transports/cli/cli.d.ts.map +1 -1
- package/dist/transports/cli/cli.js +88 -38
- package/dist/transports/cli/cli.js.map +1 -1
- package/dist/transports/mcp/handlers.d.ts +12 -8
- package/dist/transports/mcp/handlers.d.ts.map +1 -1
- package/dist/transports/mcp/handlers.js +23 -11
- package/dist/transports/mcp/handlers.js.map +1 -1
- package/dist/transports/schemas.d.ts +22 -17
- package/dist/transports/schemas.d.ts.map +1 -1
- package/dist/transports/schemas.js +25 -11
- package/dist/transports/schemas.js.map +1 -1
- package/docs/platforms/README.md +2 -2
- package/docs/platforms/agent-messaging.md +246 -36
- package/hooks/hooks.json +10 -0
- package/llms-install.md +29 -6
- package/package.json +13 -5
- package/scripts/hooks/_generated/agent-message-inbox.js +30 -6
- package/scripts/hooks/_generated/citation-rule.js +5 -2
- package/scripts/hooks/_generated/fts-index.js +5 -0
- package/scripts/hooks/_generated/work-topology.js +24 -2
- package/scripts/hooks/_shared.js +52 -4
- package/scripts/hooks/decision-nudge.js +152 -0
- package/scripts/hooks/post-commit.js +11 -0
- package/scripts/hooks/pre-compact.js +12 -4
- package/scripts/hooks/session-start.js +61 -26
- package/scripts/upgrade-plugin.sh +600 -81
- package/skills/memesh/SKILL.md +24 -5
- /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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
92
|
-
cursor recovery, `poll`, or
|
|
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
|
|
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
|
|
161
|
-
polling
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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
|
|
210
|
-
persistence, or fetch does not promise exactly-once cognition, 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 |
|
|
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
|
|
235
|
-
|
|
236
|
-
`poll`/`watch` client may
|
|
237
|
-
compatibility or diagnosis.
|
|
238
|
-
3.
|
|
239
|
-
|
|
240
|
-
|
|
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.
|
|
133
|
-
|
|
134
|
-
|
|
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
|
|
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.
|
|
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.
|
|
4
|
-
"description": "MeMesh
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
|
29
|
-
|
|
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
|
|
32
|
-
|
|
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
|
-
|
|
29
|
-
|
|
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) {
|