@pcircle/memesh 4.7.3 → 4.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/AGENTS.md +3 -1
- package/README.de.md +42 -16
- package/README.md +46 -19
- package/README.zh-TW.md +42 -17
- package/dashboard/dist/index.html +1 -1
- package/dist/core/agent-message-storage.d.ts +76 -0
- package/dist/core/agent-message-storage.d.ts.map +1 -0
- package/dist/core/agent-message-storage.js +359 -0
- package/dist/core/agent-message-storage.js.map +1 -0
- package/dist/core/agent-messaging.d.ts +224 -0
- package/dist/core/agent-messaging.d.ts.map +1 -0
- package/dist/core/agent-messaging.js +881 -0
- package/dist/core/agent-messaging.js.map +1 -0
- package/dist/core/agent-router.d.ts +194 -0
- package/dist/core/agent-router.d.ts.map +1 -0
- package/dist/core/agent-router.js +1138 -0
- package/dist/core/agent-router.js.map +1 -0
- package/dist/core/briefing.d.ts.map +1 -1
- package/dist/core/briefing.js +8 -1
- package/dist/core/briefing.js.map +1 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js.map +1 -1
- package/dist/core/doctor.d.ts +19 -0
- package/dist/core/doctor.d.ts.map +1 -1
- package/dist/core/doctor.js +176 -2
- package/dist/core/doctor.js.map +1 -1
- package/dist/core/dreamer.d.ts +8 -5
- package/dist/core/dreamer.d.ts.map +1 -1
- package/dist/core/dreamer.js +88 -3
- package/dist/core/dreamer.js.map +1 -1
- package/dist/core/product-improvements.d.ts +61 -0
- package/dist/core/product-improvements.d.ts.map +1 -0
- package/dist/core/product-improvements.js +216 -0
- package/dist/core/product-improvements.js.map +1 -0
- package/dist/core/repo-state.d.ts +11 -0
- package/dist/core/repo-state.d.ts.map +1 -0
- package/dist/core/repo-state.js +70 -0
- package/dist/core/repo-state.js.map +1 -0
- package/dist/core/schema-export.d.ts.map +1 -1
- package/dist/core/schema-export.js +70 -0
- package/dist/core/schema-export.js.map +1 -1
- package/dist/core/task-state.d.ts.map +1 -1
- package/dist/core/task-state.js +3 -3
- package/dist/core/task-state.js.map +1 -1
- package/dist/core/updater.d.ts +2 -0
- package/dist/core/updater.d.ts.map +1 -1
- package/dist/core/updater.js +11 -5
- package/dist/core/updater.js.map +1 -1
- package/dist/core/work-topology.d.ts.map +1 -1
- package/dist/core/work-topology.js +1 -0
- package/dist/core/work-topology.js.map +1 -1
- package/dist/host-adapters/acp-client.d.ts +147 -0
- package/dist/host-adapters/acp-client.d.ts.map +1 -0
- package/dist/host-adapters/acp-client.js +793 -0
- package/dist/host-adapters/acp-client.js.map +1 -0
- package/dist/host-adapters/claude-channel.d.ts +75 -0
- package/dist/host-adapters/claude-channel.d.ts.map +1 -0
- package/dist/host-adapters/claude-channel.js +255 -0
- package/dist/host-adapters/claude-channel.js.map +1 -0
- package/dist/host-adapters/codex-app-server.d.ts +83 -0
- package/dist/host-adapters/codex-app-server.d.ts.map +1 -0
- package/dist/host-adapters/codex-app-server.js +360 -0
- package/dist/host-adapters/codex-app-server.js.map +1 -0
- package/dist/host-adapters/codex-cli-queue.d.ts +17 -0
- package/dist/host-adapters/codex-cli-queue.d.ts.map +1 -0
- package/dist/host-adapters/codex-cli-queue.js +85 -0
- package/dist/host-adapters/codex-cli-queue.js.map +1 -0
- package/dist/host-runtime/acp.d.ts +57 -0
- package/dist/host-runtime/acp.d.ts.map +1 -0
- package/dist/host-runtime/acp.js +338 -0
- package/dist/host-runtime/acp.js.map +1 -0
- package/dist/host-runtime/claude.d.ts +41 -0
- package/dist/host-runtime/claude.d.ts.map +1 -0
- package/dist/host-runtime/claude.js +213 -0
- package/dist/host-runtime/claude.js.map +1 -0
- package/dist/host-runtime/codex-session.d.ts +24 -0
- package/dist/host-runtime/codex-session.d.ts.map +1 -0
- package/dist/host-runtime/codex-session.js +98 -0
- package/dist/host-runtime/codex-session.js.map +1 -0
- package/dist/host-runtime/codex.d.ts +33 -0
- package/dist/host-runtime/codex.d.ts.map +1 -0
- package/dist/host-runtime/codex.js +215 -0
- package/dist/host-runtime/codex.js.map +1 -0
- package/dist/host-runtime/config.d.ts +9 -0
- package/dist/host-runtime/config.d.ts.map +1 -0
- package/dist/host-runtime/config.js +91 -0
- package/dist/host-runtime/config.js.map +1 -0
- package/dist/host-runtime/router-client.d.ts +38 -0
- package/dist/host-runtime/router-client.d.ts.map +1 -0
- package/dist/host-runtime/router-client.js +463 -0
- package/dist/host-runtime/router-client.js.map +1 -0
- package/dist/host-runtime/router.d.ts +3 -0
- package/dist/host-runtime/router.d.ts.map +1 -0
- package/dist/host-runtime/router.js +43 -0
- package/dist/host-runtime/router.js.map +1 -0
- package/dist/mcp/server.js +2 -2
- package/dist/mcp/server.js.map +1 -1
- package/dist/skills-manifest.json +22 -17
- package/dist/storage/conflicts.d.ts.map +1 -1
- package/dist/storage/conflicts.js +8 -1
- package/dist/storage/conflicts.js.map +1 -1
- package/dist/storage/schema.d.ts +1 -1
- package/dist/storage/schema.d.ts.map +1 -1
- package/dist/storage/schema.js +255 -0
- package/dist/storage/schema.js.map +1 -1
- package/dist/transports/agent-messaging.d.ts +11 -0
- package/dist/transports/agent-messaging.d.ts.map +1 -0
- package/dist/transports/agent-messaging.js +273 -0
- package/dist/transports/agent-messaging.js.map +1 -0
- package/dist/transports/cli/cli.d.ts +6 -1
- package/dist/transports/cli/cli.d.ts.map +1 -1
- package/dist/transports/cli/cli.js +392 -17
- package/dist/transports/cli/cli.js.map +1 -1
- package/dist/transports/http/server.d.ts.map +1 -1
- package/dist/transports/http/server.js +31 -2
- package/dist/transports/http/server.js.map +1 -1
- package/dist/transports/mcp/handlers.d.ts +152 -1
- package/dist/transports/mcp/handlers.d.ts.map +1 -1
- package/dist/transports/mcp/handlers.js +108 -3
- package/dist/transports/mcp/handlers.js.map +1 -1
- package/dist/transports/schemas.d.ts +106 -1
- package/dist/transports/schemas.d.ts.map +1 -1
- package/dist/transports/schemas.js +86 -0
- package/dist/transports/schemas.js.map +1 -1
- package/docs/platforms/README.md +167 -0
- package/docs/platforms/agent-messaging.md +290 -0
- package/hooks/hooks.json +11 -0
- package/llms-install.md +101 -25
- package/package.json +14 -5
- package/scripts/hooks/_generated/repo-state.js +77 -0
- package/scripts/hooks/_generated/schema.js +255 -0
- package/scripts/hooks/_generated/task-state.js +3 -3
- package/scripts/hooks/_generated/work-topology.js +1 -0
- package/scripts/hooks/_shared.js +77 -97
- package/scripts/hooks/auto-update-runner.mjs +243 -0
- package/scripts/hooks/session-start.js +12 -2
- package/scripts/hooks/session-summary.js +7 -6
- package/skills/memesh/SKILL.md +40 -1
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
# Local Agent Messaging Guide
|
|
2
|
+
|
|
3
|
+
MeMesh provides two complementary collaboration surfaces on one machine:
|
|
4
|
+
|
|
5
|
+
- shared durable memory, including the `team` namespace, for knowledge, decisions, and coarse handoffs;
|
|
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.
|
|
15
|
+
|
|
16
|
+
## One-time owner-private local-host setup
|
|
17
|
+
|
|
18
|
+
Native delivery is optional and local to one Unix account. Do this setup once
|
|
19
|
+
for the account that owns both the MeMesh database and the active host
|
|
20
|
+
sessions; do not place router tokens or host config in a repository, shared
|
|
21
|
+
dotfile, or world-readable temp directory.
|
|
22
|
+
|
|
23
|
+
Each configured host connection attempts to start the packaged router and
|
|
24
|
+
retries when its owner-private socket is absent or refused. You can also start
|
|
25
|
+
the router yourself when you want to inspect it directly; it creates an
|
|
26
|
+
owner-private token and socket beside the active MeMesh database on first
|
|
27
|
+
start:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
umask 077
|
|
31
|
+
memesh-router
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
If you start it yourself, leave that process running. In a second terminal,
|
|
35
|
+
verify only the installed adapter imports and the live router socket separately:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
MEMESH_DOCTOR_PROBE_MESSAGE_CAPABILITY=1 memesh doctor
|
|
39
|
+
MEMESH_DOCTOR_PROBE_MESSAGE_ROUTER=1 memesh doctor
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The first command proves the installed MCP schema and adapter imports. The
|
|
43
|
+
second only proves that the owner-private router socket accepts a connection.
|
|
44
|
+
Neither command starts or registers a host, sends a message, proves
|
|
45
|
+
`host_accept`, or wakes a stopped session.
|
|
46
|
+
In particular, a socket check does not start the host it observes.
|
|
47
|
+
|
|
48
|
+
The secure host-native router and adapter runtime currently supports macOS and
|
|
49
|
+
Linux. Windows remains supported for core MeMesh memory, durable message
|
|
50
|
+
storage, and MCP tools, but host-native wakeup fails closed before creating
|
|
51
|
+
credentials, configuration, IPC listeners, or managed child processes.
|
|
52
|
+
|
|
53
|
+
Create one reusable owner-private config for each local path and principal.
|
|
54
|
+
The stable principal is the logical recipient. Managed processes generate a
|
|
55
|
+
fresh exact session identity; the ordinary Codex path instead uses the Codex
|
|
56
|
+
thread identity supplied at SessionStart. No thread ID is copied by hand.
|
|
57
|
+
Ordinary sessions outside the explicit `codex-session` workspace opt-in remain
|
|
58
|
+
`presence-only/inbound-unavailable`.
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
memesh agent setup codex-session --project my-project --principal codex-reviewer --workspace "$PWD"
|
|
62
|
+
memesh agent setup codex --project my-project --principal codex-reviewer --workspace "$PWD"
|
|
63
|
+
memesh agent setup claude --project my-project --principal claude-reviewer
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Ordinary active Codex CLI session
|
|
67
|
+
|
|
68
|
+
`codex-session` is the opt-in path for an ordinary local Codex session and
|
|
69
|
+
requires the MeMesh Codex plugin to be installed and enabled so Codex loads
|
|
70
|
+
the packaged SessionStart hook. Run
|
|
71
|
+
the setup command from the exact workspace that Codex will use; it stores the
|
|
72
|
+
configured real workspace and stable principal in the owner-private
|
|
73
|
+
`codex-session.json` config. Restart Codex in that workspace after setup.
|
|
74
|
+
|
|
75
|
+
On `SessionStart` for `startup` or `resume`, the asynchronous companion checks
|
|
76
|
+
the Codex thread identity, hook session identity, and configured workspace
|
|
77
|
+
realpath before it connects to the router. A missing identity, a different
|
|
78
|
+
workspace, compact lifecycle input, or a failed/disconnected connection does
|
|
79
|
+
not register a host and does not wake anything.
|
|
80
|
+
|
|
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.
|
|
88
|
+
|
|
89
|
+
If the configured Codex session is stopped, missing, disconnected, or no
|
|
90
|
+
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.
|
|
93
|
+
|
|
94
|
+
### Separate: MeMesh-managed Codex app-server runner
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
memesh-host-codex --config "$HOME/.memesh/hosts/codex.json"
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
This is separate from `codex-session`: it starts a MeMesh-owned `codex
|
|
101
|
+
app-server`, creates its own thread through the private Unix/WebSocket control
|
|
102
|
+
path, and registers only after that thread is ready. It does not attach to an
|
|
103
|
+
ordinary Codex session. Message content never appears in MeMesh or Codex
|
|
104
|
+
process arguments.
|
|
105
|
+
|
|
106
|
+
### Claude channel runner
|
|
107
|
+
|
|
108
|
+
Run the printed `registration_command` once to add `memesh-channel` as a
|
|
109
|
+
user-scoped stdio MCP server. Claude owns that process for the session:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
claude mcp add --transport stdio --scope user memesh-channel -- \
|
|
113
|
+
memesh-host-claude --config "$HOME/.memesh/hosts/claude.json"
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Claude Channels is an upstream research-preview opt-in. Custom channels are
|
|
117
|
+
not on Anthropic's approved allowlist, so start every participating Claude
|
|
118
|
+
session with the printed launch command and confirm the local-development
|
|
119
|
+
warning:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
claude --dangerously-load-development-channels server:memesh-channel
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Without that flag Claude may initialize the ordinary MCP transport while
|
|
126
|
+
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
|
|
128
|
+
channel admitted, initialization creates and registers the exact MeMesh
|
|
129
|
+
session automatically; EOF, MCP close, or normal signals unregister it.
|
|
130
|
+
|
|
131
|
+
### Experimental ACP runner (not release-gated)
|
|
132
|
+
|
|
133
|
+
An internal generic ACP runner remains an experimental adapter surface. No ACP
|
|
134
|
+
provider is documented as a supported native-wakeup path here; protocol or
|
|
135
|
+
process readiness alone is not proof that a provider accepted a message.
|
|
136
|
+
|
|
137
|
+
The managed Codex app-server and Claude channel paths deliver only while their
|
|
138
|
+
configured target is active and registered. If it is stopped, missing, disconnected, or replaced, MeMesh keeps the durable message but does not start
|
|
139
|
+
the host, recreate the session, or silently redirect an exact-session target.
|
|
140
|
+
A later eligible managed-principal registration drains only post-activation
|
|
141
|
+
pending work; an exact-session target never moves to a replacement. Manual
|
|
142
|
+
cursor reads remain available for audit and diagnostics, not as a requirement
|
|
143
|
+
for active Codex-session delivery.
|
|
144
|
+
|
|
145
|
+
## What Works Today
|
|
146
|
+
|
|
147
|
+
- 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.
|
|
149
|
+
- `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
|
+
- `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
|
+
- `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
|
+
- The transport, rather than model-provided payload data, records sender-host provenance.
|
|
153
|
+
|
|
154
|
+
## Identity and lifecycle
|
|
155
|
+
|
|
156
|
+
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
|
+
|
|
158
|
+
Persistence, dispatch attempt, host acceptance, intake, acknowledgement,
|
|
159
|
+
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.
|
|
165
|
+
|
|
166
|
+
## Bounded storage and audit retention
|
|
167
|
+
|
|
168
|
+
Message payload growth is observable and owner-controlled. MeMesh never deletes
|
|
169
|
+
unresolved, unacknowledged, retryable, or offline-pending messages to satisfy a
|
|
170
|
+
limit. Inspect logical payload bytes, protected rows, SQLite reusable pages,
|
|
171
|
+
and main/WAL file sizes with an explicit policy cutoff:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
memesh message storage report --cutoff 2026-08-01T00:00:00Z
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Preview one bounded batch of old terminal payloads; nothing changes without
|
|
178
|
+
`--apply`:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
memesh message storage prune --cutoff 2026-08-01T00:00:00Z --batch-size 100
|
|
182
|
+
memesh message storage prune --cutoff 2026-08-01T00:00:00Z --batch-size 100 --apply
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Applied pruning replaces only payload content whose every delivery has an
|
|
186
|
+
explicit ACK and a terminal workflow disposition older than the cutoff with a hash-bound
|
|
187
|
+
tombstone. Message identity, routing, receipts, ACK, workflow, presence, and
|
|
188
|
+
retention audit facts remain queryable. Freed SQLite pages become reusable;
|
|
189
|
+
the main database file is a high-watermark and is not promised to shrink.
|
|
190
|
+
Full `VACUUM` is never run by a hook or this bounded command.
|
|
191
|
+
|
|
192
|
+
An owner may set `MEMESH_AGENT_MESSAGE_STORAGE_QUOTA_BYTES` to a non-negative
|
|
193
|
+
integer. This is a hard **logical payload** budget, not a whole SQLite file or
|
|
194
|
+
disk quota. The canonical send transaction checks it before inserting any
|
|
195
|
+
message effect; an over-quota send returns `storage_quota_exceeded` and leaves
|
|
196
|
+
no partial message, delivery, event, idempotency, dispatch, or receipt row.
|
|
197
|
+
Metadata, indexes, append-only audit facts, reusable pages, and WAL bytes still
|
|
198
|
+
consume disk and remain visible in the report; keep separate filesystem
|
|
199
|
+
headroom and monitoring. Heartbeats refresh the connection lease in place;
|
|
200
|
+
they do not append one audit row every interval. Connected, disconnected, and
|
|
201
|
+
superseded transitions remain auditable. There is deliberately no default
|
|
202
|
+
quota or automatic retention policy.
|
|
203
|
+
|
|
204
|
+
## Local and Cloud boundary
|
|
205
|
+
|
|
206
|
+
This guide describes only one local MeMesh instance: its SQLite durable event
|
|
207
|
+
store and same-machine host-native input. Remote and cross-machine transport is
|
|
208
|
+
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.
|
|
212
|
+
|
|
213
|
+
## What This Is Not Yet
|
|
214
|
+
|
|
215
|
+
- Not universal host support or stopped-session resume. This document only
|
|
216
|
+
describes the explicitly configured ordinary Codex path and the separate
|
|
217
|
+
managed Codex/Claude paths above.
|
|
218
|
+
- Not topic, broadcast, lease/claim, or TTL routing. The current delivery target is one exact recipient.
|
|
219
|
+
- Not arbitrary external-user access or cross-machine delivery. A local MeMesh instance is not a public collaboration service.
|
|
220
|
+
- Not permission to execute payload content. The receiving host must apply its own policy and required human approval.
|
|
221
|
+
|
|
222
|
+
## Support Matrix
|
|
223
|
+
|
|
224
|
+
| Participant | Current path | Status today | Notes |
|
|
225
|
+
|---|---|---|---|
|
|
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 |
|
|
227
|
+
| 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
|
+
| Claude channel | `memesh-host-claude` | separate channel path | Requires the documented Channel opt-in; no stopped-session resume |
|
|
229
|
+
| 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 |
|
|
230
|
+
|
|
231
|
+
## Lifecycle
|
|
232
|
+
|
|
233
|
+
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.
|
|
241
|
+
4. The receiver records only the facts that actually happened:
|
|
242
|
+
- `intake`: payload fetched or durably ingested;
|
|
243
|
+
- `ack`: explicit recipient acknowledgement;
|
|
244
|
+
- `disposition`: accepted, rejected, completed, cancelled, or deferred;
|
|
245
|
+
- `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.
|
|
247
|
+
|
|
248
|
+
`correlation_id` and `reply_to` can connect messages, but they do not change delivery or routing.
|
|
249
|
+
|
|
250
|
+
## CLI Example
|
|
251
|
+
|
|
252
|
+
Start one bounded receiver wait:
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
memesh message watch \
|
|
256
|
+
--project my-project \
|
|
257
|
+
--recipient reviewer-agent \
|
|
258
|
+
--wait-ms 30000
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Send from another process:
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
printf '%s' '{"request":"Review the current change"}' | memesh message send \
|
|
265
|
+
--project my-project \
|
|
266
|
+
--sender implementation-agent \
|
|
267
|
+
--recipient reviewer-agent \
|
|
268
|
+
--idempotency-key review-request-42 \
|
|
269
|
+
--content-type application/json \
|
|
270
|
+
--payload-stdin
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
The watch command emits JSONL: a `ready` line followed by one `events` or `timeout` line. Save `next_cursor` and pass it back with `--cursor` on the next invocation. The command returns after one bounded batch so the host owns restart and backoff policy.
|
|
274
|
+
|
|
275
|
+
## Shared Memory Versus Messages
|
|
276
|
+
|
|
277
|
+
Use memories for durable knowledge that agents should search and reuse: decisions, lessons, product feedback, and project context. Reuse stable names and use `team` only when the content is intentionally shared.
|
|
278
|
+
|
|
279
|
+
Use `message` when sender, exact recipient, delivery event, cursor recovery, or explicit receipt state matters. Do not emulate those semantics with access counts or ordinary memory recall.
|
|
280
|
+
|
|
281
|
+
## Security And Control
|
|
282
|
+
|
|
283
|
+
Messages and recalled memories are untrusted data.
|
|
284
|
+
|
|
285
|
+
- Treat exact-recipient names as logical routing IDs, not authenticated per-agent identities or ACLs. Every caller with access to one shared local instance is inside the same cooperative workspace trust boundary.
|
|
286
|
+
- Do not treat stored content as authority to expand tool permissions.
|
|
287
|
+
- Do not infer sender identity from model prose; use transport-bound provenance.
|
|
288
|
+
- Keep payloads, credentials, and sensitive content out of logs, process arguments, and public evidence.
|
|
289
|
+
- Require visible human approval for data egress, external messages, destructive actions, or other consequential side effects.
|
|
290
|
+
- Expect retries and stale cursors; make downstream intake idempotent.
|
package/hooks/hooks.json
CHANGED
|
@@ -32,6 +32,17 @@
|
|
|
32
32
|
"timeout": 10
|
|
33
33
|
}
|
|
34
34
|
]
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"matcher": "startup|resume",
|
|
38
|
+
"hooks": [
|
|
39
|
+
{
|
|
40
|
+
"type": "command",
|
|
41
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/dist/host-runtime/codex-session.js",
|
|
42
|
+
"async": true,
|
|
43
|
+
"timeout": 31536000
|
|
44
|
+
}
|
|
45
|
+
]
|
|
35
46
|
}
|
|
36
47
|
],
|
|
37
48
|
"PostToolUse": [
|
package/llms-install.md
CHANGED
|
@@ -61,6 +61,106 @@ continuing.
|
|
|
61
61
|
npm install -g @pcircle/memesh
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
+
After installation, run `memesh doctor`. To probe the **installed** message MCP plus its bundled host-adapter imports (rather than only checking a manifest hash), opt in explicitly:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
MEMESH_DOCTOR_PROBE_MESSAGE_CAPABILITY=1 memesh doctor
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
This probe does not exercise a real host session and never wakes a stopped
|
|
71
|
+
session. The ordinary Codex path below is the documented native local wakeup
|
|
72
|
+
path; `poll`/`watch` and cursor recovery remain available for compatibility and
|
|
73
|
+
diagnosis.
|
|
74
|
+
|
|
75
|
+
Message storage remains owner-controlled. There is no default quota or
|
|
76
|
+
automatic pruning. To inspect it after installation:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
memesh message storage report --cutoff 2026-08-01T00:00:00Z
|
|
80
|
+
memesh message storage prune --cutoff 2026-08-01T00:00:00Z --batch-size 100
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The prune command is a dry-run unless `--apply` is supplied. Only old terminal
|
|
84
|
+
payload content is tombstoned; unresolved/offline-pending messages and all
|
|
85
|
+
lifecycle audit facts are preserved. Set an explicit hard quota for all send
|
|
86
|
+
transports with `MEMESH_AGENT_MESSAGE_STORAGE_QUOTA_BYTES=<bytes>`; an
|
|
87
|
+
over-quota send is rejected atomically.
|
|
88
|
+
|
|
89
|
+
### Optional: one-time local host setup
|
|
90
|
+
|
|
91
|
+
This is separate from MCP setup. It is for the owner of an active local Codex
|
|
92
|
+
session, a MeMesh-managed Codex app-server, or a Claude channel. Keep all
|
|
93
|
+
files private to that Unix account; do not commit the token or config files.
|
|
94
|
+
This secure host-native runtime currently supports macOS and Linux. Windows
|
|
95
|
+
can still use core MeMesh, durable messages, and MCP tools, but not this
|
|
96
|
+
host-native wakeup path.
|
|
97
|
+
|
|
98
|
+
Each configured host connection starts the packaged router and retries its
|
|
99
|
+
connection when the owner-private socket is absent or refused. Start the router
|
|
100
|
+
yourself only when you want to inspect the socket directly:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
umask 077
|
|
104
|
+
memesh-router
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
If you start it yourself, it creates `agent-router.sock` and `agent-router.token` beside the active
|
|
108
|
+
MeMesh database (normally `~/.memesh/`) with owner-private permissions. Check
|
|
109
|
+
the installed adapter imports and the live socket as distinct facts:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
MEMESH_DOCTOR_PROBE_MESSAGE_CAPABILITY=1 memesh doctor
|
|
113
|
+
MEMESH_DOCTOR_PROBE_MESSAGE_ROUTER=1 memesh doctor
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The router probe does not register a host, send content, or wake a stopped
|
|
117
|
+
session. Generate reusable `0600` configs; session identities are not copied
|
|
118
|
+
from an active ordinary session.
|
|
119
|
+
|
|
120
|
+
For an ordinary active local Codex session, first install and enable the MeMesh
|
|
121
|
+
Codex plugin (Option A), which supplies the packaged SessionStart hook. Then
|
|
122
|
+
run this from the exact workspace you want to configure and restart Codex in
|
|
123
|
+
that same workspace:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
memesh agent setup codex-session --project my-project --principal codex-recipient --workspace "$PWD"
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
This stores the configured workspace realpath and principal in
|
|
130
|
+
`~/.memesh/hosts/codex-session.json`. On `SessionStart` (`startup` or
|
|
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.
|
|
136
|
+
|
|
137
|
+
`host_accept` records only that the local Codex queue accepted that marker. It
|
|
138
|
+
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
|
|
140
|
+
workspace, MeMesh neither starts nor replaces it; the durable inbox remains
|
|
141
|
+
available to scoped fetch, cursor recovery, `poll`, and `memesh message watch`
|
|
142
|
+
for audit and diagnosis.
|
|
143
|
+
|
|
144
|
+
The following are separate managed-host paths:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
memesh agent setup codex --project my-project --principal codex-recipient --workspace "$PWD"
|
|
148
|
+
memesh-host-codex --config "$HOME/.memesh/hosts/codex.json"
|
|
149
|
+
|
|
150
|
+
memesh agent setup claude --project my-project --principal claude-recipient
|
|
151
|
+
# Run the printed `registration_command` (`claude mcp add ... memesh-host-claude ...`) once.
|
|
152
|
+
# Start each participating session with the printed research-preview launch command:
|
|
153
|
+
claude --dangerously-load-development-channels server:memesh-channel
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
The managed Codex runner owns its app-server and thread; Claude owns its
|
|
157
|
+
Channel MCP child. Neither is the ordinary Codex-session path, and neither
|
|
158
|
+
attaches to, resumes, or replaces an ordinary stopped host. When no active
|
|
159
|
+
registration exists, the message remains durable with no false dispatch or
|
|
160
|
+
host acceptance. The package also contains the experimental
|
|
161
|
+
`memesh-host-acp` binary for protocol development, but no ACP provider is a
|
|
162
|
+
documented native-wakeup integration here.
|
|
163
|
+
|
|
64
164
|
Expected: exits without error; `memesh`, `memesh-mcp` and `memesh-http` are
|
|
65
165
|
now in `$(npm prefix -g)/bin/`. No compiler is involved and no install script
|
|
66
166
|
runs.
|
|
@@ -113,31 +213,7 @@ Expected: `memesh` is listed as enabled.
|
|
|
113
213
|
| `memesh` absent from the list | The add did not persist. Re-run `codex mcp add memesh -- memesh-mcp` and re-check. |
|
|
114
214
|
| Listed, but tool calls fail | Run `command -v memesh-mcp`. Empty output means section 2 is incomplete or PATH is wrong — fix per section 2's table. |
|
|
115
215
|
|
|
116
|
-
## 4.
|
|
117
|
-
|
|
118
|
-
Prerequisite: section 2 — `memesh-mcp` must resolve on PATH.
|
|
119
|
-
|
|
120
|
-
```
|
|
121
|
-
gemini mcp add -s user memesh memesh-mcp
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
`-s user` registers at user scope, so it works from every folder.
|
|
125
|
-
|
|
126
|
-
**Verify**:
|
|
127
|
-
|
|
128
|
-
```
|
|
129
|
-
gemini mcp list
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
Expected: `memesh` shows **Connected**.
|
|
133
|
-
|
|
134
|
-
| Failure | Remedy |
|
|
135
|
-
|---|---|
|
|
136
|
-
| `command not found: gemini` | Gemini CLI itself is not installed — out of scope here; install it first, then re-run the add. |
|
|
137
|
-
| Shows Disconnected | Run `command -v memesh-mcp`. Empty output means section 2 is incomplete or PATH is wrong — fix per section 2's table, then re-run `gemini mcp list`. |
|
|
138
|
-
| `memesh` absent from the list | The add was made in a different scope or did not persist. Re-run `gemini mcp add -s user memesh memesh-mcp`. |
|
|
139
|
-
|
|
140
|
-
## 5. Cursor
|
|
216
|
+
## 4. Cursor
|
|
141
217
|
|
|
142
218
|
Prerequisite: section 2 — `memesh-mcp` must resolve on PATH.
|
|
143
219
|
|
package/package.json
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pcircle/memesh",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.8.1",
|
|
4
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": {
|
|
8
8
|
"memesh": "dist/transports/cli/cli.js",
|
|
9
9
|
"memesh-mcp": "dist/mcp/server.js",
|
|
10
|
-
"memesh-http": "dist/transports/http/server.js"
|
|
10
|
+
"memesh-http": "dist/transports/http/server.js",
|
|
11
|
+
"memesh-router": "dist/host-runtime/router.js",
|
|
12
|
+
"memesh-host-claude": "dist/host-runtime/claude.js",
|
|
13
|
+
"memesh-host-codex": "dist/host-runtime/codex.js",
|
|
14
|
+
"memesh-host-codex-session": "dist/host-runtime/codex-session.js",
|
|
15
|
+
"memesh-host-acp": "dist/host-runtime/acp.js"
|
|
11
16
|
},
|
|
12
17
|
"files": [
|
|
13
18
|
"dist/",
|
|
@@ -17,6 +22,7 @@
|
|
|
17
22
|
"scripts/hooks/",
|
|
18
23
|
"scripts/upgrade-plugin.sh",
|
|
19
24
|
"skills/",
|
|
25
|
+
"docs/platforms/agent-messaging.md",
|
|
20
26
|
".claude-plugin/",
|
|
21
27
|
"README.md",
|
|
22
28
|
"LICENSE",
|
|
@@ -29,10 +35,11 @@
|
|
|
29
35
|
"test": "vitest",
|
|
30
36
|
"test:e2e-dashboard": "node scripts/dashboard-e2e-smoke.mjs",
|
|
31
37
|
"test:packaged": "node scripts/smoke-packed-artifact.mjs",
|
|
38
|
+
"test:packaged:upgrade": "node scripts/smoke-packed-upgrade.mjs",
|
|
32
39
|
"audit:prod": "node scripts/check-consumer-audit.mjs",
|
|
33
|
-
"verify:release": "npm run lint && npm run typecheck && node scripts/check-version-coherence.mjs && node scripts/check-generated-mirror.mjs && node scripts/check-doc-claims.mjs && node scripts/audit/verification-audit.mjs && npm run audit:prod",
|
|
40
|
+
"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 && node scripts/check-readme-tool-parity.mjs && node scripts/check-doc-claims.mjs && node scripts/audit/verification-audit.mjs && npm run audit:prod",
|
|
34
41
|
"release:finish": "node scripts/finish-release.mjs",
|
|
35
|
-
"prepublishOnly": "npm run build && npm run verify:release && npm run test:isolated && npm run test:packaged",
|
|
42
|
+
"prepublishOnly": "npm run build && npm run verify:release && npm run test:isolated && npm run test:packaged && npm run test:packaged:upgrade",
|
|
36
43
|
"typecheck": "tsc -p tsconfig.check.json && tsc -p tsconfig.check-dashboard.json",
|
|
37
44
|
"lint": "eslint src/ scripts/ tests/ dashboard/src/ --max-warnings 0",
|
|
38
45
|
"lint:fix": "eslint src/ scripts/ tests/ dashboard/src/ --fix",
|
|
@@ -69,6 +76,7 @@
|
|
|
69
76
|
"express": "^5.2.1",
|
|
70
77
|
"express-rate-limit": "8.5.1",
|
|
71
78
|
"sqlite-vec": "^0.1.9",
|
|
79
|
+
"ws": "^8.21.3",
|
|
72
80
|
"zod": "4.4.3"
|
|
73
81
|
},
|
|
74
82
|
"overrides": {
|
|
@@ -83,7 +91,7 @@
|
|
|
83
91
|
"protobufjs": "^7.6.5",
|
|
84
92
|
"qs": "^6.15.3",
|
|
85
93
|
"sharp": "^0.35.3",
|
|
86
|
-
"ws": "
|
|
94
|
+
"ws": "$ws",
|
|
87
95
|
"vite": "7.3.6"
|
|
88
96
|
},
|
|
89
97
|
"devDependencies": {
|
|
@@ -93,6 +101,7 @@
|
|
|
93
101
|
"@testing-library/preact": "^3.2.4",
|
|
94
102
|
"@types/express": "^5.0.6",
|
|
95
103
|
"@types/node": "25.6.2",
|
|
104
|
+
"@types/ws": "^8.18.1",
|
|
96
105
|
"@vitest/coverage-v8": "4.1.5",
|
|
97
106
|
"eslint": "^10.3.0",
|
|
98
107
|
"happy-dom": "^20.9.0",
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// AUTO-GENERATED from src/core/repo-state.ts — DO NOT EDIT BY HAND.
|
|
3
|
+
// Regenerate with: npm run build (scripts/generate-hook-core.mjs)
|
|
4
|
+
//
|
|
5
|
+
// Claude Code hooks import this committed copy instead of dist/, so the
|
|
6
|
+
// always-on capture path survives a missing or stale dist/ while staying
|
|
7
|
+
// byte-locked to core — eliminating the hand-mirror drift behind the P0 FTS bug.
|
|
8
|
+
// ============================================================================
|
|
9
|
+
import { execFileSync } from 'child_process';
|
|
10
|
+
import fs from 'fs';
|
|
11
|
+
import path from 'path';
|
|
12
|
+
const GIT_TIMEOUT_MS = 5000;
|
|
13
|
+
function tryGit(cwd, args) {
|
|
14
|
+
try {
|
|
15
|
+
return execFileSync('git', ['-C', cwd, ...args], {
|
|
16
|
+
encoding: 'utf8',
|
|
17
|
+
timeout: GIT_TIMEOUT_MS,
|
|
18
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
19
|
+
}).trim();
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function declaredVersionOf(repoRoot) {
|
|
26
|
+
try {
|
|
27
|
+
const raw = fs.readFileSync(path.join(repoRoot, 'package.json'), 'utf8');
|
|
28
|
+
const version = JSON.parse(raw).version;
|
|
29
|
+
return typeof version === 'string' && version.length > 0 ? version : null;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export function readRepoState(cwdInput) {
|
|
36
|
+
const cwd = cwdInput && cwdInput.length > 0 ? cwdInput : process.cwd();
|
|
37
|
+
const repoRoot = tryGit(cwd, ['rev-parse', '--show-toplevel']);
|
|
38
|
+
if (!repoRoot)
|
|
39
|
+
return null;
|
|
40
|
+
const branchRaw = tryGit(cwd, ['rev-parse', '--abbrev-ref', 'HEAD']);
|
|
41
|
+
const branch = branchRaw && branchRaw !== 'HEAD' ? branchRaw : null;
|
|
42
|
+
const statusOut = tryGit(cwd, ['status', '--porcelain']);
|
|
43
|
+
const uncommitted = statusOut ? statusOut.split('\n').filter(l => l.trim() !== '').length : 0;
|
|
44
|
+
const lastTag = tryGit(cwd, ['describe', '--tags', '--abbrev=0']);
|
|
45
|
+
let commitsSinceTag = null;
|
|
46
|
+
if (lastTag) {
|
|
47
|
+
const count = tryGit(cwd, ['rev-list', '--count', `${lastTag}..HEAD`]);
|
|
48
|
+
const parsed = count === null ? Number.NaN : Number.parseInt(count, 10);
|
|
49
|
+
commitsSinceTag = Number.isFinite(parsed) ? parsed : null;
|
|
50
|
+
}
|
|
51
|
+
const declaredVersion = declaredVersionOf(repoRoot);
|
|
52
|
+
let declaredVersionIsTagged = null;
|
|
53
|
+
if (declaredVersion) {
|
|
54
|
+
const hit = tryGit(cwd, ['tag', '--list', `v${declaredVersion}`]);
|
|
55
|
+
declaredVersionIsTagged = hit === null ? null : hit.length > 0;
|
|
56
|
+
}
|
|
57
|
+
return { branch, uncommitted, lastTag, commitsSinceTag, declaredVersion, declaredVersionIsTagged };
|
|
58
|
+
}
|
|
59
|
+
export function repoStateLines(state) {
|
|
60
|
+
if (!state)
|
|
61
|
+
return [];
|
|
62
|
+
const first = [];
|
|
63
|
+
if (state.branch)
|
|
64
|
+
first.push(`branch ${state.branch}`);
|
|
65
|
+
first.push(state.uncommitted === 0 ? 'working tree clean' : `${state.uncommitted} uncommitted`);
|
|
66
|
+
const lines = ['Where the repository actually stands (read just now):', `- ${first.join(' · ')}`];
|
|
67
|
+
if (state.lastTag) {
|
|
68
|
+
const since = state.commitsSinceTag;
|
|
69
|
+
lines.push(since === null ? `- last tag ${state.lastTag}`
|
|
70
|
+
: since === 0 ? `- at tag ${state.lastTag}`
|
|
71
|
+
: `- ${since} commit${since === 1 ? '' : 's'} since ${state.lastTag}`);
|
|
72
|
+
}
|
|
73
|
+
if (state.declaredVersion && state.declaredVersionIsTagged === false) {
|
|
74
|
+
lines.push(`- package.json declares ${state.declaredVersion}, which has no tag yet`);
|
|
75
|
+
}
|
|
76
|
+
return lines;
|
|
77
|
+
}
|