@sym-bot/mesh-channel 0.3.22 → 0.3.25
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/plugin.json +1 -1
- package/.mcp.json +1 -1
- package/CHANGELOG.md +18 -0
- package/README.md +4 -3
- package/bin/install.js +7 -0
- package/package.json +3 -4
- package/server.js +18 -18
package/.mcp.json
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.25
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- **Runs the `meshmem/` → `cmbs/` store migration on install** (`bin/install.js` calls `@sym-bot/sym`'s `migrateStores()`), so every non-live node is migrated when the plugin is set up. Pairs with `@sym-bot/sym` 0.7.16.
|
|
8
|
+
|
|
9
|
+
## 0.3.24
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- **MCP tools renamed to canonical Enterprise Integration Pattern verbs:** `sym_observe` → **`sym_publish`** (Publish-Subscribe Channel) and `sym_inbox` → **`sym_receive`** (Polling Consumer). The I/O surface now reads as what the agent *does* (publish / send / receive), while the cognitive mechanism terms (emit/admit, projection/observation) stay in the MMP spec one layer down. `sym_send` (Point-to-Point) unchanged. Clean break — no aliases. Agent instructions + tool descriptions updated: publishing emits a *projection* of your state; a receiver that admits it takes it as an *observation*.
|
|
14
|
+
|
|
15
|
+
## 0.3.23
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- **Removed the automatic `postinstall` registration.** The package no longer mutates `~/.claude.json` (or a project `.mcp.json`) on `npm install`. As a Claude Code **plugin**, the package is launched via `npx` on every session start, and the postinstall re-registered a competing user-scoped `claude-sym-mesh` MCP server each time — producing a second mesh node alongside the plugin's own. Registration now happens only via the explicit `start`/`init` commands, which the standalone flow (`npx @sym-bot/mesh-channel start`) already runs and which self-configure on first launch — so there is no change for standalone users, and the plugin path no longer double-registers. Pairs with the 0.3.22 `.sym/node.json` reader to make the plugin the single, stable, per-project mesh node.
|
|
20
|
+
|
|
3
21
|
## 0.3.22
|
|
4
22
|
|
|
5
23
|
### Added
|
package/README.md
CHANGED
|
@@ -106,8 +106,9 @@ Eleven MCP tools exposed to Claude Code, namespaced under `mcp__claude-sym-mesh_
|
|
|
106
106
|
|
|
107
107
|
| Tool | What it does |
|
|
108
108
|
|---|---|
|
|
109
|
-
| `sym_send` |
|
|
110
|
-
| `
|
|
109
|
+
| `sym_send` | Send a CAT7 CMB to a specific peer (point-to-point), or to all if no recipient is given. Arrives in receivers' contexts as a `<channel>` notification. |
|
|
110
|
+
| `sym_publish` | Publish a structured CAT7 CMB — a projection of your state — to your whole group (publish-subscribe): focus, issue, intent, motivation, commitment, perspective, mood. SVAF-gated on the receiving side. |
|
|
111
|
+
| `sym_receive` | Surface CMBs the mesh delivered to you in real-time when the `<channel>` push was gated — a live delivery feed, not a store query. |
|
|
111
112
|
| `sym_recall` | Search mesh memory for past cognitive memory blocks. |
|
|
112
113
|
| `sym_fetch` | Fetch the full content of a single CMB by its compact channel-header ID. |
|
|
113
114
|
| `sym_peers` | List discovered peers (via bonjour or relay). |
|
|
@@ -230,7 +231,7 @@ The plugin composes two open specs:
|
|
|
230
231
|
- **[Claude Code Channels](https://code.claude.com/docs/en/mcp)** (Anthropic, 2026-03-20) — an MCP capability that lets servers push events directly into Claude's conversation context mid-turn via `notifications/claude/channel`. Anthropic built it for the Telegram/Discord/iMessage integrations. We use it for agent-to-agent cognitive coupling.
|
|
231
232
|
- **[MMP — the Mesh Memory Protocol](https://meshcognition.org/spec/mmp)** — defines *what* gets pushed: typed seven-field cognitive bundles (CAT7: focus, issue, intent, motivation, commitment, perspective, mood), how receivers gate incoming signals ([SVAF](https://arxiv.org/abs/2604.03955)), and how peers maintain identity without a central orchestrator.
|
|
232
233
|
|
|
233
|
-
**What happens on each message.** When a peer broadcasts a cognitive memory block (CMB), the local SymNode evaluates it via SVAF — Symbolic-Vector Attention Fusion, a receiver-side relevance gate that rejects low-signal messages before they reach Claude's context. If accepted, the MCP server fires a `notifications/claude/channel` notification to Claude Code, which surfaces it as a `<channel>` block in the conversation. Claude sees it, can react, and can broadcast back via `sym_send` or `
|
|
234
|
+
**What happens on each message.** When a peer broadcasts a cognitive memory block (CMB), the local SymNode evaluates it via SVAF — Symbolic-Vector Attention Fusion, a receiver-side relevance gate that rejects low-signal messages before they reach Claude's context. If accepted, the MCP server fires a `notifications/claude/channel` notification to Claude Code, which surfaces it as a `<channel>` block in the conversation. Claude sees it, can react, and can broadcast back via `sym_send` or `sym_publish`. No polling. No tool calls. The mesh thinks together.
|
|
234
235
|
|
|
235
236
|
**Identity and transport.** Each peer has its own Ed25519 keypair stored at `~/.sym/nodes/<name>/identity.json`. Node IDs are UUID v7 + Ed25519 signatures, gossiped through the relay's directory or via Bonjour TXT records. Full architecture in MMP §4–§6.
|
|
236
237
|
|
package/bin/install.js
CHANGED
|
@@ -60,6 +60,13 @@ if (cmd !== 'init' && cmd !== 'doctor' && cmd !== 'start') {
|
|
|
60
60
|
process.exit(1);
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
// One-time bulk store migration (meshmem/ → cmbs/) for all non-live nodes, run
|
|
64
|
+
// on install so readers use the cmbs/ name with no fallback. Idempotent.
|
|
65
|
+
try {
|
|
66
|
+
const n = require('@sym-bot/sym').migrateStores();
|
|
67
|
+
if (n) process.stderr.write(`[sym-mesh-channel] migrated ${n} node store(s): meshmem → cmbs\n`);
|
|
68
|
+
} catch { /* SDK not resolvable or nothing to do — non-fatal */ }
|
|
69
|
+
|
|
63
70
|
const KEBAB_CASE_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
64
71
|
function validateGroupValue(value, source) {
|
|
65
72
|
if (!value) return;
|
package/package.json
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sym-bot/mesh-channel",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.25",
|
|
4
4
|
"description": "MCP server — real-time agent-to-agent cognition for Claude Code remote teams via the SYM mesh.",
|
|
5
5
|
"main": "server.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"sym-mesh-channel": "server.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"test": "node test/plugin.test.js"
|
|
11
|
-
"postinstall": "node bin/install.js init --postinstall"
|
|
10
|
+
"test": "node test/plugin.test.js"
|
|
12
11
|
},
|
|
13
12
|
"files": [
|
|
14
13
|
"server.js",
|
|
@@ -22,7 +21,7 @@
|
|
|
22
21
|
],
|
|
23
22
|
"dependencies": {
|
|
24
23
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
25
|
-
"@sym-bot/sym": "^0.7.
|
|
24
|
+
"@sym-bot/sym": "^0.7.16",
|
|
26
25
|
"bonjour-service": "^1.3.0"
|
|
27
26
|
},
|
|
28
27
|
"engines": {
|
package/server.js
CHANGED
|
@@ -34,7 +34,7 @@ process.stdout.write = function (chunk, ...rest) {
|
|
|
34
34
|
* Architecture (MMP Section 13.9: Local Event Interface):
|
|
35
35
|
* SymNode (own identity, own SVAF field weights) → relay → mesh
|
|
36
36
|
* MCP channel notifications → Claude Code (real-time push)
|
|
37
|
-
* MCP tools → SymNode methods (send,
|
|
37
|
+
* MCP tools → SymNode methods (send, publish, recall)
|
|
38
38
|
*
|
|
39
39
|
* This is a PEER NODE, not a client of the daemon. It has its own identity,
|
|
40
40
|
* its own relay connection, and its own SVAF evaluation with engineering-domain
|
|
@@ -360,12 +360,12 @@ function registerNodeHandlers(n) {
|
|
|
360
360
|
// Base instructions shown to the agent at every MCP initialize.
|
|
361
361
|
const BASE_INSTRUCTIONS =
|
|
362
362
|
`You are a peer node on the SYM mesh (identity: ${NODE_NAME}). ` +
|
|
363
|
-
'
|
|
363
|
+
'The mesh is publish-subscribe: peers deliver CMBs to you in real-time the instant they publish, as <channel> notifications. That real-time push can be gated by Claude Code policy, so call sym_receive to surface any deliveries the push did not bring into your context (directed sym_send + admitted broadcasts) — a live delivery feed, not a memory query. Call sym_receive at the start of your turn and periodically while coordinating with peers, so no delivery is missed. ' +
|
|
364
364
|
'When you receive a CMB from another node, respond via sym_send targeted at that node by name if the reply is for that specific peer (MMP §4.4.4 targeted CMB). ' +
|
|
365
|
-
'
|
|
366
|
-
'Both sym_send and
|
|
365
|
+
'Publish a CMB to your whole group via sym_publish — a projection of your own state (MMP §9.2 receiver-autonomous SVAF evaluation). ' +
|
|
366
|
+
'Both sym_send and sym_publish emit a CAT7 CMB (your projection); each receiver runs SVAF and, if it admits the CMB as an observation, remix-stores it with lineage back to yours. ' +
|
|
367
367
|
'Search mesh memory via sym_recall. ' +
|
|
368
|
-
'
|
|
368
|
+
'sym_receive and <channel> notifications give compact headers with [mNNN] IDs — use sym_fetch to read the full content when relevant to your current task.';
|
|
369
369
|
|
|
370
370
|
// Final startup step (MMP §4.2 O2 — rejoin-without-replay). The SymNode
|
|
371
371
|
// constructor builds the memory-store index from disk, so the primer is
|
|
@@ -407,7 +407,7 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
407
407
|
'Send a structured CAT7 CMB to a specific mesh peer (targeted) or to all peers (broadcast, when "to" is omitted). ' +
|
|
408
408
|
'Receivers evaluate the CMB per-field via SVAF (MMP §9.2) and, if admitted, remix-store it with lineage pointing back to this CMB. ' +
|
|
409
409
|
'Use sym_send when the CMB is for a specific peer (e.g. a peer-review gating request directed at the reviewer role); ' +
|
|
410
|
-
'use
|
|
410
|
+
'use sym_publish when publishing your own state to the whole group.',
|
|
411
411
|
inputSchema: {
|
|
412
412
|
type: 'object',
|
|
413
413
|
properties: {
|
|
@@ -444,11 +444,11 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
444
444
|
},
|
|
445
445
|
},
|
|
446
446
|
{
|
|
447
|
-
name: '
|
|
447
|
+
name: 'sym_publish',
|
|
448
448
|
description:
|
|
449
|
-
'
|
|
450
|
-
'
|
|
451
|
-
'Equivalent to sym_send with "to" omitted — kept as a separate tool because
|
|
449
|
+
'Publish a structured CAT7 CMB — a projection of your own state — to all peers in your group. ' +
|
|
450
|
+
'Each receiver runs SVAF (MMP §9.2) and, if it admits the CMB as an observation, remix-stores it with lineage. ' +
|
|
451
|
+
'Equivalent to sym_send with "to" omitted — kept as a separate tool because publishing your own state is the common case and does not need peer selection.',
|
|
452
452
|
inputSchema: {
|
|
453
453
|
type: 'object',
|
|
454
454
|
properties: {
|
|
@@ -505,8 +505,8 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
505
505
|
},
|
|
506
506
|
},
|
|
507
507
|
{
|
|
508
|
-
name: '
|
|
509
|
-
description: '
|
|
508
|
+
name: 'sym_receive',
|
|
509
|
+
description: 'Surface the CMBs the mesh has delivered to you in real-time — directed sym_send addressed to you, plus admitted broadcasts published to your group. The mesh is publish-subscribe: peers deliver the instant they publish, pushed as <channel> notifications. Because that push can be gated by Claude Code policy, sym_receive surfaces any deliveries it missed so none is lost — a live delivery feed, NOT a store query (use sym_recall to search stored memory). Call it at the start of a turn and periodically while coordinating so no delivery is missed. Returns compact headers with [mNNN] IDs (newest last); use sym_fetch for full content, reply via sym_send.',
|
|
510
510
|
inputSchema: {
|
|
511
511
|
type: 'object',
|
|
512
512
|
properties: {
|
|
@@ -627,7 +627,7 @@ mcp.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
627
627
|
return { content: [{ type: 'text', text: summary }] };
|
|
628
628
|
}
|
|
629
629
|
|
|
630
|
-
case '
|
|
630
|
+
case 'sym_publish': {
|
|
631
631
|
const fields = {
|
|
632
632
|
focus: args.focus || 'observation',
|
|
633
633
|
issue: args.issue || 'none',
|
|
@@ -640,7 +640,7 @@ mcp.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
640
640
|
const observeOpts = {};
|
|
641
641
|
if (args.payload !== undefined && args.payload !== null) observeOpts.payload = args.payload;
|
|
642
642
|
const entry = node.remember(fields, observeOpts);
|
|
643
|
-
return { content: [{ type: 'text', text: entry ? `
|
|
643
|
+
return { content: [{ type: 'text', text: entry ? `Published: ${entry.key}` : 'Duplicate — already in memory.' }] };
|
|
644
644
|
}
|
|
645
645
|
|
|
646
646
|
case 'sym_recall': {
|
|
@@ -685,12 +685,12 @@ mcp.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
685
685
|
};
|
|
686
686
|
}
|
|
687
687
|
|
|
688
|
-
case '
|
|
688
|
+
case 'sym_receive': {
|
|
689
689
|
// Thin adapter over the SDK primitive: the node owns the delivery buffer
|
|
690
690
|
// + drain cursor (node.inbox()). This wrapper only formats for display.
|
|
691
691
|
const { messages, remaining } = node.inbox({ peek: !!args.peek, limit: args.limit });
|
|
692
692
|
if (!messages.length) {
|
|
693
|
-
return { content: [{ type: 'text', text: '
|
|
693
|
+
return { content: [{ type: 'text', text: 'Caught up — nothing new delivered since your last sym_receive.' }] };
|
|
694
694
|
}
|
|
695
695
|
const now = Date.now();
|
|
696
696
|
const lines = messages.map((m) => {
|
|
@@ -707,9 +707,9 @@ mcp.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
707
707
|
return `[${m.from}${dirTag}] ${String(focus).replace(/\s+/g, ' ').slice(0, 90)}${memTag} [${m.id}] (${age}s ago)`;
|
|
708
708
|
}).filter(Boolean);
|
|
709
709
|
if (!lines.length) {
|
|
710
|
-
return { content: [{ type: 'text', text: '
|
|
710
|
+
return { content: [{ type: 'text', text: 'Caught up — nothing new delivered since your last sym_receive.' }] };
|
|
711
711
|
}
|
|
712
|
-
const moreNote = remaining > 0 ? ` (+${remaining} more — call
|
|
712
|
+
const moreNote = remaining > 0 ? ` (+${remaining} more — call sym_receive again)` : '';
|
|
713
713
|
return {
|
|
714
714
|
content: [{
|
|
715
715
|
type: 'text',
|