@sym-bot/mesh-channel 0.1.23 → 0.3.0
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 +5 -5
- package/.claude-plugin/plugin.json +2 -2
- package/CHANGELOG.md +61 -0
- package/LICENSE +1 -1
- package/README.md +3 -3
- package/bin/install.js +1 -1
- package/package.json +2 -2
- package/server.js +120 -29
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sym-mesh-channel",
|
|
3
3
|
"owner": {
|
|
4
|
-
"name": "SYM.BOT
|
|
4
|
+
"name": "SYM.BOT",
|
|
5
5
|
"email": "info@sym.bot"
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
|
-
"description": "Real-time Claude-to-Claude mesh.
|
|
9
|
-
"version": "0.
|
|
8
|
+
"description": "Real-time Claude-to-Claude mesh. Agent-to-agent cognitive signals over Bonjour LAN or WebSocket relay.",
|
|
9
|
+
"version": "0.2.0"
|
|
10
10
|
},
|
|
11
11
|
"plugins": [
|
|
12
12
|
{
|
|
13
13
|
"name": "sym-mesh-channel",
|
|
14
14
|
"source": "./",
|
|
15
|
-
"description": "Real-time Claude-to-Claude mesh.
|
|
16
|
-
"version": "0.
|
|
15
|
+
"description": "Real-time Claude-to-Claude mesh. Agent-to-agent cognitive signals over Bonjour LAN or WebSocket relay. Implements the Mesh Memory Protocol (MMP) for structured cognitive state exchange between Claude Code sessions.",
|
|
16
|
+
"version": "0.2.0",
|
|
17
17
|
"author": {
|
|
18
18
|
"name": "Hongwei Xu",
|
|
19
19
|
"email": "hongwei@sym.bot"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sym-mesh-channel",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Real-time Claude-to-Claude mesh.
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Real-time Claude-to-Claude mesh. Agent-to-agent cognitive signals over Bonjour LAN or WebSocket relay.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Hongwei Xu",
|
|
7
7
|
"email": "hongwei@sym.bot",
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,66 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **Startup remix-memory primer — automates agent memory recall on
|
|
8
|
+
session/agent restart (MMP §4.2 O2, rejoin-without-replay).** As the
|
|
9
|
+
final step of plugin initialisation (after `node.start()` and before
|
|
10
|
+
the MCP transport connects), the plugin calls
|
|
11
|
+
`node.buildStartupPrimer()` and appends the returned text to the MCP
|
|
12
|
+
server's `instructions` field. A fresh Claude Code session wakes
|
|
13
|
+
with the agent's own remix memory — own observations plus peer
|
|
14
|
+
observations admitted by SVAF — already loaded into context. No
|
|
15
|
+
first-turn `sym_recall` required; agent acts from prior state
|
|
16
|
+
immediately.
|
|
17
|
+
|
|
18
|
+
Default caps: last 24 hours OR 20 most recent CMBs, whichever is
|
|
19
|
+
tighter. The primer lists each entry as `[timestamp] source · key —
|
|
20
|
+
focus` and surfaces a dropped-count line when caps elide older
|
|
21
|
+
entries. Empty store is a silent no-op.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- **`@sym-bot/sym` dep bumped to `^0.5.0`** to pick up the
|
|
26
|
+
`buildStartupPrimer` helper and to keep every plugin on the
|
|
27
|
+
sym.day platform pinned to the same substrate SDK version
|
|
28
|
+
(no drift across mesh-channel / melotune-plugin / future
|
|
29
|
+
specialised plugins).
|
|
30
|
+
|
|
31
|
+
## 0.2.0
|
|
32
|
+
|
|
33
|
+
### Breaking
|
|
34
|
+
|
|
35
|
+
- **`sym_send` tool signature change.** `sym_send` now emits a structured
|
|
36
|
+
CAT7 CMB (MMP §4.2) instead of a raw-text `type:'message'` frame, and
|
|
37
|
+
accepts an optional `to` parameter for targeted single-peer delivery
|
|
38
|
+
per MMP §4.4.4.
|
|
39
|
+
|
|
40
|
+
Old signature: `sym_send(message: string)`
|
|
41
|
+
New signature: `sym_send(focus: string (required), issue?, intent?,
|
|
42
|
+
motivation?, commitment?, perspective?, mood?, to?)`
|
|
43
|
+
|
|
44
|
+
Migration: agents that previously called `sym_send({message: "..."})`
|
|
45
|
+
should now pass the CAT7 fields explicitly, with `focus` carrying the
|
|
46
|
+
task anchor for the send. Prior ephemeral text-broadcast behaviour is
|
|
47
|
+
no longer exposed at the tool surface — `sym_send` and `sym_observe`
|
|
48
|
+
both emit CMBs now, receivers run SVAF per §9.2, and admitted CMBs are
|
|
49
|
+
remix-stored with lineage. The low-level `node.send(text)` SDK API is
|
|
50
|
+
unchanged but no longer surfaced as a tool.
|
|
51
|
+
|
|
52
|
+
### Added
|
|
53
|
+
|
|
54
|
+
- **Targeted CMB send.** `sym_send` resolves `to` against connected
|
|
55
|
+
peers by full nodeId first, then display name, then 8-char prefix.
|
|
56
|
+
Ambiguous matches return an error asking for the full nodeId; a
|
|
57
|
+
disconnected target returns an error and suggests `sym_peers`.
|
|
58
|
+
- **Tool descriptions** for `sym_send` and `sym_observe` now explicitly
|
|
59
|
+
call out the SVAF receive path and lineage semantics, and the MCP
|
|
60
|
+
server's `instructions` string reflects the new division of labour.
|
|
61
|
+
- **`@sym-bot/sym` dependency bumped to `^0.3.81`** for
|
|
62
|
+
`remember(fields, {to})` targeted variant and `peers().peerId`.
|
|
63
|
+
|
|
3
64
|
## 0.1.23
|
|
4
65
|
|
|
5
66
|
### Added
|
package/LICENSE
CHANGED
|
@@ -186,7 +186,7 @@
|
|
|
186
186
|
same "printed page" as the copyright notice for easier
|
|
187
187
|
identification within third-party archives.
|
|
188
188
|
|
|
189
|
-
Copyright 2026 SYM.BOT
|
|
189
|
+
Copyright 2026 SYM.BOT
|
|
190
190
|
|
|
191
191
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
192
|
you may not use this file except in compliance with the License.
|
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
> MCP server that turns Claude Code into a peer node on the [SYM mesh](https://sym.bot) — the first non-Anthropic implementation of Claude Code Channels for real-time agent-to-agent cognition.
|
|
10
10
|
|
|
11
|
-
Two Claude Code sessions on different machines discover each other via Bonjour mDNS, form a
|
|
11
|
+
Two Claude Code sessions on different machines discover each other via Bonjour mDNS, form a mesh, and exchange structured agent-to-agent cognitive signals in real-time. Each side is a full peer with its own cryptographic identity, its own [SVAF](https://arxiv.org/abs/2604.03955) receiver-side gating, and its own memory — not a thin client. Signals arrive mid-conversation as `<channel>` notifications. No polling, no shared server, no orchestrator.
|
|
12
12
|
|
|
13
13
|
**Verified cross-platform:** Mac ↔ Windows on the same wifi, pure Bonjour, no relay, no token. Cross-network via optional WebSocket relay.
|
|
14
14
|
|
|
@@ -160,7 +160,7 @@ SYM-mesh groups visible on LAN (3):
|
|
|
160
160
|
_frontend-team._tcp group="frontend-team"
|
|
161
161
|
```
|
|
162
162
|
|
|
163
|
-
Only shows groups with at least one node online right now — there's no central directory of offline-but-known groups (
|
|
163
|
+
Only shows groups with at least one node online right now — there's no central directory of offline-but-known groups (decentralised architecture). For cross-network relay-backed groups, you must know the relay URL + token out of band (someone shares the invite URL).
|
|
164
164
|
|
|
165
165
|
### Advanced: per-project node identity
|
|
166
166
|
|
|
@@ -291,4 +291,4 @@ See [SECURITY.md](SECURITY.md) for the full security model.
|
|
|
291
291
|
|
|
292
292
|
## License
|
|
293
293
|
|
|
294
|
-
Apache 2.0 — SYM.BOT
|
|
294
|
+
Apache 2.0 — SYM.BOT
|
package/bin/install.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sym-bot/mesh-channel",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
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": {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
25
|
-
"@sym-bot/sym": "^0.
|
|
25
|
+
"@sym-bot/sym": "^0.5.0"
|
|
26
26
|
},
|
|
27
27
|
"engines": {
|
|
28
28
|
"node": ">=18"
|
package/server.js
CHANGED
|
@@ -19,7 +19,7 @@ if (process.argv[2] === 'init') {
|
|
|
19
19
|
* its own relay connection, and its own SVAF evaluation with engineering-domain
|
|
20
20
|
* field weights. Per MMP Section 3: every participant is a peer.
|
|
21
21
|
*
|
|
22
|
-
* Copyright (c) 2026 SYM.BOT
|
|
22
|
+
* Copyright (c) 2026 SYM.BOT. Apache 2.0 License.
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
const { Server } = require('@modelcontextprotocol/sdk/server/index.js');
|
|
@@ -253,6 +253,35 @@ function registerNodeHandlers(n) {
|
|
|
253
253
|
|
|
254
254
|
// ── MCP Server ───────────────────────────────────────────────
|
|
255
255
|
|
|
256
|
+
// Base instructions shown to the agent at every MCP initialize.
|
|
257
|
+
const BASE_INSTRUCTIONS =
|
|
258
|
+
`You are a peer node on the SYM mesh (identity: ${NODE_NAME}). ` +
|
|
259
|
+
'Mesh events arrive as <channel> notifications in real-time. ' +
|
|
260
|
+
'When you see 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). ' +
|
|
261
|
+
'Share observations about your own state with the whole mesh via sym_observe (MMP §9.2 receiver-autonomous SVAF evaluation). ' +
|
|
262
|
+
'Both sym_send and sym_observe emit CAT7 CMBs; receivers run SVAF and, if admitted, remix-store with lineage pointing back to your CMB. ' +
|
|
263
|
+
'Search mesh memory via sym_recall. ' +
|
|
264
|
+
'Messages arrive as compact headers with [mNNN] IDs — use sym_fetch to read the full content when the header is relevant to your current task.';
|
|
265
|
+
|
|
266
|
+
// Final startup step (MMP §4.2 O2 — rejoin-without-replay). The SymNode
|
|
267
|
+
// constructor builds the memory-store index from disk, so the primer is
|
|
268
|
+
// available synchronously without needing node.start(). Appending it to
|
|
269
|
+
// the MCP instructions payload means a fresh Claude Code session wakes
|
|
270
|
+
// with prior remix memory — own observations plus peer observations
|
|
271
|
+
// admitted by SVAF — already loaded into context, zero first-turn
|
|
272
|
+
// sym_recall overhead.
|
|
273
|
+
//
|
|
274
|
+
// MCP SDK reads `instructions` at Server construction time (storing it in
|
|
275
|
+
// a private field) and emits it only on initialize-response; mutations on
|
|
276
|
+
// the public property after construction are ignored. Compute once, pass in.
|
|
277
|
+
let primerText = '';
|
|
278
|
+
try {
|
|
279
|
+
const primer = node.buildStartupPrimer();
|
|
280
|
+
if (primer && primer.count > 0) primerText = `\n\n${primer.text}`;
|
|
281
|
+
} catch (err) {
|
|
282
|
+
process.stderr.write(`sym-mesh-channel startup primer skipped: ${err?.message || err}\n`);
|
|
283
|
+
}
|
|
284
|
+
|
|
256
285
|
const mcp = new Server(
|
|
257
286
|
{ name: 'sym-mesh', version: '0.1.0' },
|
|
258
287
|
{
|
|
@@ -260,13 +289,7 @@ const mcp = new Server(
|
|
|
260
289
|
tools: {},
|
|
261
290
|
experimental: { 'claude/channel': {} },
|
|
262
291
|
},
|
|
263
|
-
instructions:
|
|
264
|
-
`You are a peer node on the SYM mesh (identity: ${NODE_NAME}). ` +
|
|
265
|
-
'Mesh events arrive as <channel> notifications in real-time. ' +
|
|
266
|
-
'When you see a message or CMB from another node, respond via the sym_send tool if actionable. ' +
|
|
267
|
-
'Share observations about the user\'s state via sym_observe. ' +
|
|
268
|
-
'Search mesh memory via sym_recall. ' +
|
|
269
|
-
'Messages arrive as compact headers with [mNNN] IDs — use sym_fetch to read the full content when the header is relevant to your current task.',
|
|
292
|
+
instructions: BASE_INSTRUCTIONS + primerText,
|
|
270
293
|
},
|
|
271
294
|
);
|
|
272
295
|
|
|
@@ -276,16 +299,44 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
276
299
|
tools: [
|
|
277
300
|
{
|
|
278
301
|
name: 'sym_send',
|
|
279
|
-
description:
|
|
302
|
+
description:
|
|
303
|
+
'Send a structured CAT7 CMB to a specific mesh peer (targeted) or to all peers (broadcast, when "to" is omitted). ' +
|
|
304
|
+
'Receivers evaluate the CMB per-field via SVAF (MMP §9.2) and, if admitted, remix-store it with lineage pointing back to this CMB. ' +
|
|
305
|
+
'Use sym_send when the CMB is for a specific peer (e.g. a peer-review gating request directed at the reviewer role); ' +
|
|
306
|
+
'use sym_observe when sharing your own state with the whole mesh.',
|
|
280
307
|
inputSchema: {
|
|
281
308
|
type: 'object',
|
|
282
|
-
properties: {
|
|
283
|
-
|
|
309
|
+
properties: {
|
|
310
|
+
focus: { type: 'string', description: 'The task anchor / what this CMB is about. Required.' },
|
|
311
|
+
issue: { type: 'string' },
|
|
312
|
+
intent: { type: 'string' },
|
|
313
|
+
motivation: { type: 'string' },
|
|
314
|
+
commitment: { type: 'string' },
|
|
315
|
+
perspective: { type: 'string' },
|
|
316
|
+
mood: {
|
|
317
|
+
type: 'object',
|
|
318
|
+
properties: {
|
|
319
|
+
text: { type: 'string' },
|
|
320
|
+
valence: { type: 'number' },
|
|
321
|
+
arousal: { type: 'number' },
|
|
322
|
+
},
|
|
323
|
+
},
|
|
324
|
+
to: {
|
|
325
|
+
type: 'string',
|
|
326
|
+
description:
|
|
327
|
+
'Target peer: either the peer display name (e.g. "claude-research-win") or the full nodeId. ' +
|
|
328
|
+
'Call sym_peers first if unsure which peers are connected. Omit to broadcast to all peers.',
|
|
329
|
+
},
|
|
330
|
+
},
|
|
331
|
+
required: ['focus'],
|
|
284
332
|
},
|
|
285
333
|
},
|
|
286
334
|
{
|
|
287
335
|
name: 'sym_observe',
|
|
288
|
-
description:
|
|
336
|
+
description:
|
|
337
|
+
'Broadcast a structured CAT7 observation about your own state to all mesh peers. ' +
|
|
338
|
+
'Receivers run SVAF (MMP §9.2) and admitted CMBs are remix-stored with lineage. ' +
|
|
339
|
+
'Equivalent to sym_send with "to" omitted — kept as a separate tool because self-observation is the common case and does not need peer selection.',
|
|
289
340
|
inputSchema: {
|
|
290
341
|
type: 'object',
|
|
291
342
|
properties: {
|
|
@@ -388,22 +439,60 @@ mcp.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
388
439
|
|
|
389
440
|
switch (name) {
|
|
390
441
|
case 'sym_send': {
|
|
391
|
-
//
|
|
392
|
-
//
|
|
393
|
-
//
|
|
394
|
-
//
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
442
|
+
// Emit a structured CAT7 CMB per MMP §4.2. When args.to names a peer,
|
|
443
|
+
// route as a targeted send (§4.4.4); otherwise broadcast. Receivers
|
|
444
|
+
// run SVAF (§9.2) and remix-store on accept — no separate "message"
|
|
445
|
+
// frame path, no raw-text channel.
|
|
446
|
+
const fields = {
|
|
447
|
+
focus: args.focus || 'directive',
|
|
448
|
+
issue: args.issue || 'none',
|
|
449
|
+
intent: args.intent || 'directive',
|
|
450
|
+
motivation: args.motivation || '',
|
|
451
|
+
commitment: args.commitment || '',
|
|
452
|
+
perspective: args.perspective || NODE_NAME,
|
|
453
|
+
mood: args.mood || { text: 'neutral', valence: 0, arousal: 0 },
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
let targetPeerId = null;
|
|
457
|
+
if (args.to) {
|
|
458
|
+
const peers = node.peers();
|
|
459
|
+
// Exact full-nodeId match first (unambiguous).
|
|
460
|
+
const byNodeId = peers.filter(p => p.peerId === args.to);
|
|
461
|
+
// Name match second.
|
|
462
|
+
const byName = peers.filter(p => p.name === args.to);
|
|
463
|
+
// Short-id prefix match last (for human-typed 8-char prefixes).
|
|
464
|
+
const byPrefix = peers.filter(p => p.id === args.to);
|
|
465
|
+
|
|
466
|
+
let matches;
|
|
467
|
+
if (byNodeId.length > 0) matches = byNodeId;
|
|
468
|
+
else if (byName.length > 0) matches = byName;
|
|
469
|
+
else if (byPrefix.length > 0) matches = byPrefix;
|
|
470
|
+
else matches = [];
|
|
471
|
+
|
|
472
|
+
if (matches.length === 0) {
|
|
473
|
+
return {
|
|
474
|
+
content: [{ type: 'text', text: `Peer "${args.to}" not connected. Call sym_peers to see connected peers.` }],
|
|
475
|
+
isError: true,
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
if (matches.length > 1) {
|
|
479
|
+
const names = matches.map(p => `${p.name} (${p.peerId})`).join(', ');
|
|
480
|
+
return {
|
|
481
|
+
content: [{ type: 'text', text: `Peer "${args.to}" is ambiguous — matches: ${names}. Pass the full nodeId.` }],
|
|
482
|
+
isError: true,
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
targetPeerId = matches[0].peerId;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
const entry = node.remember(fields, targetPeerId ? { to: targetPeerId } : {});
|
|
489
|
+
if (!entry) {
|
|
490
|
+
return { content: [{ type: 'text', text: 'Duplicate — CMB already in memory, not re-broadcast.' }] };
|
|
491
|
+
}
|
|
492
|
+
const summary = targetPeerId
|
|
493
|
+
? `Sent CMB ${entry.key} to ${args.to}`
|
|
494
|
+
: `Broadcast CMB ${entry.key} to all peers`;
|
|
495
|
+
return { content: [{ type: 'text', text: summary }] };
|
|
407
496
|
}
|
|
408
497
|
|
|
409
498
|
case 'sym_observe': {
|
|
@@ -773,7 +862,9 @@ process.on('SIGINT', () => shutdown('SIGINT'));
|
|
|
773
862
|
process.on('SIGHUP', () => shutdown('SIGHUP'));
|
|
774
863
|
|
|
775
864
|
async function main() {
|
|
776
|
-
// Start SymNode — connects to relay as a peer
|
|
865
|
+
// Start SymNode — connects to relay as a peer. The startup primer is
|
|
866
|
+
// computed at module-load time (see BASE_INSTRUCTIONS above) and is
|
|
867
|
+
// already embedded in the MCP server's initialize-response payload.
|
|
777
868
|
await node.start();
|
|
778
869
|
|
|
779
870
|
// Start MCP server — communicates with Claude Code via stdio
|