@sym-bot/mesh-channel 0.3.20 → 0.3.21
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 +6 -0
- package/package.json +2 -2
- package/server.js +29 -25
package/.mcp.json
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.21
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- **`sym_inbox` is now a thin adapter over the SDK primitive `node.inbox()`** (`@sym-bot/sym` ^0.7.11). The pull-based receive buffer + drain cursor moved down into the node, where it belongs alongside `node.remember()` (send) — so the SDK is sufficient for send **and** pull on its own, and the wrapper owns no buffering logic. Behaviour is unchanged (FIFO drain, `peek`, `limit`); `sym_fetch` now also resolves SDK inbox ids (`inNNNN`). The wrapper still applies the peer allowlist + prompt-injection filter on the pull path before anything enters context.
|
|
8
|
+
|
|
3
9
|
## 0.3.20
|
|
4
10
|
|
|
5
11
|
### Added
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sym-bot/mesh-channel",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.21",
|
|
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.7.
|
|
25
|
+
"@sym-bot/sym": "^0.7.11",
|
|
26
26
|
"bonjour-service": "^1.3.0"
|
|
27
27
|
},
|
|
28
28
|
"engines": {
|
package/server.js
CHANGED
|
@@ -651,6 +651,12 @@ mcp.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
651
651
|
}
|
|
652
652
|
|
|
653
653
|
case 'sym_fetch': {
|
|
654
|
+
// inNNNN → SDK delivery inbox (pull path); mNNN → channel-push store.
|
|
655
|
+
if (typeof args.msg_id === 'string' && args.msg_id.startsWith('in')) {
|
|
656
|
+
const m = node.inboxGet(args.msg_id);
|
|
657
|
+
if (!m) return { content: [{ type: 'text', text: `Message ${args.msg_id} not found (expired or invalid ID).` }] };
|
|
658
|
+
return { content: [{ type: 'text', text: `[${m.from}] ${new Date(m.receivedAt).toISOString()}\n\n${m.content}` }] };
|
|
659
|
+
}
|
|
654
660
|
const entry = MESSAGE_STORE.get(args.msg_id);
|
|
655
661
|
if (!entry) {
|
|
656
662
|
return { content: [{ type: 'text', text: `Message ${args.msg_id} not found (expired or invalid ID).` }] };
|
|
@@ -664,35 +670,34 @@ mcp.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
664
670
|
}
|
|
665
671
|
|
|
666
672
|
case 'sym_inbox': {
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
if (seq > inboxCursor) fresh.push({ id, seq, entry });
|
|
672
|
-
}
|
|
673
|
-
fresh.sort((a, b) => a.seq - b.seq);
|
|
674
|
-
// FIFO: drain the OLDEST `limit` first so the cursor advances contiguously
|
|
675
|
-
// and a backlog larger than `limit` is never skipped — the rest come on the
|
|
676
|
-
// next call. (Newest-first would jump the cursor past un-returned items.)
|
|
677
|
-
const slice = fresh.slice(0, limit);
|
|
678
|
-
if (!args.peek && slice.length) {
|
|
679
|
-
inboxCursor = Math.max(inboxCursor, ...slice.map((i) => i.seq));
|
|
680
|
-
}
|
|
681
|
-
if (!slice.length) {
|
|
673
|
+
// Thin adapter over the SDK primitive: the node owns the delivery buffer
|
|
674
|
+
// + drain cursor (node.inbox()). This wrapper only formats for display.
|
|
675
|
+
const { messages, remaining } = node.inbox({ peek: !!args.peek, limit: args.limit });
|
|
676
|
+
if (!messages.length) {
|
|
682
677
|
return { content: [{ type: 'text', text: 'Inbox empty — no new mesh messages since your last check.' }] };
|
|
683
678
|
}
|
|
684
|
-
const more = fresh.length - slice.length;
|
|
685
679
|
const now = Date.now();
|
|
686
|
-
const lines =
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
680
|
+
const lines = messages.map((m) => {
|
|
681
|
+
if (m.from === NODE_NAME) return null; // never surface our own deliveries
|
|
682
|
+
// The security layer still gates the pull path: peer allowlist +
|
|
683
|
+
// prompt-injection filter run on every message before it enters context.
|
|
684
|
+
if (!isPeerAllowed(m.from)) return null;
|
|
685
|
+
const sec = checkSecurity(m.from, m.fields || {}, m.fields?.payload);
|
|
686
|
+
if (!sec.safe) { securityAudit(sec.reason, m.from, sec.excerpt); return null; }
|
|
687
|
+
const age = Math.round((now - m.receivedAt) / 1000);
|
|
688
|
+
const focus = m.fields?.focus?.text || m.content || '';
|
|
689
|
+
const dirTag = m.directed ? ' →you' : '';
|
|
690
|
+
const memTag = m.directed && m.remixed === false ? ' ·not-stored' : '';
|
|
691
|
+
return `[${m.from}${dirTag}] ${String(focus).replace(/\s+/g, ' ').slice(0, 90)}${memTag} [${m.id}] (${age}s ago)`;
|
|
692
|
+
}).filter(Boolean);
|
|
693
|
+
if (!lines.length) {
|
|
694
|
+
return { content: [{ type: 'text', text: 'Inbox empty — no new mesh messages since your last check.' }] };
|
|
695
|
+
}
|
|
696
|
+
const moreNote = remaining > 0 ? ` (+${remaining} more — call sym_inbox again)` : '';
|
|
692
697
|
return {
|
|
693
698
|
content: [{
|
|
694
699
|
type: 'text',
|
|
695
|
-
text: `${
|
|
700
|
+
text: `${lines.length} new mesh message(s)${args.peek ? ' (peek — not drained)' : ''}${moreNote}:\n${lines.join('\n')}\n\nUse sym_fetch <id> for full content; reply via sym_send to=<peer>.`,
|
|
696
701
|
}],
|
|
697
702
|
};
|
|
698
703
|
}
|
|
@@ -909,9 +914,8 @@ mcp.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
909
914
|
// Per COO spec cmb_compact_channel_v0.1.md: push compact headers,
|
|
910
915
|
// store full content for on-demand sym_fetch retrieval. ~10% token
|
|
911
916
|
// savings on mesh traffic without context loss.
|
|
912
|
-
const MESSAGE_STORE = new Map();
|
|
917
|
+
const MESSAGE_STORE = new Map(); // channel-push surface (mNNN) for sym_fetch when channels are enabled
|
|
913
918
|
let msgSeq = 0;
|
|
914
|
-
let inboxCursor = 0; // highest msgSeq drained by sym_inbox (pull-based receive)
|
|
915
919
|
const MAX_STORED = 200;
|
|
916
920
|
|
|
917
921
|
function storeMessage(from, content, header) {
|