@sym-bot/mesh-channel 0.3.13 → 0.3.15
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 +33 -19
- package/bin/install.js +120 -6
- package/package.json +1 -1
- package/server.js +4 -2
- package/.claude-plugin/marketplace.json +0 -46
package/.mcp.json
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.15
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **`start` now finds `claude` on Windows.** The launch used `spawnSync('claude', …)` with no shell, which does an exact-filename lookup that ignores Windows `PATHEXT` — so `start` failed with `ENOENT` even when `claude` ran fine in the shell (there it resolves a `.cmd`/`.ps1` shim or `.exe`, never bare `claude`). The launch now routes through a shell on Windows so `PATHEXT` resolution applies; whitespace args are quoted since `shell: true` forwards them unquoted. The POSIX launch path is unchanged.
|
|
8
|
+
- **`start --name` no longer silently reverts identity on a stale entry.** `start` auto-injected `--force` only on a *live* entry mismatch, but `npx` rotates its cached `server.js` path on every version resolve, so the persisted entry is routinely stale yet still holds the node's name/group. On a re-run, `start` saw no live entry, pushed no `--force`, and `init`'s preserve-over-request precedence dropped the requested `--name` — reverting the node's identity to the stale name. `start` now reconciles against the persisted entry whether or not it's stale and forces the rewrite when an explicit `--name`/`--group` differs. The group is preserved when no `--group` is passed.
|
|
9
|
+
|
|
10
|
+
## 0.3.14
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`sym-mesh-channel start` — one command to a live mesh session.** Configures the MCP server if needed, then launches Claude Code with the real-time Channels flag already on, so users never type `--dangerously-load-development-channels …` or have to choose between the `plugin:` and `server:` handle. `start --project --name <node> --group <team>` stands up a named mesh agent; `start --print` is a dry run; everything after `--` is forwarded to `claude`. Co-resident sessions don't collide (server.js auto-suffixes a live-identity clash since 0.3.10), so `start` in several terminals just works.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- **CLI subcommand dispatch via the published bin.** The `bin` entrypoint (`server.js`) only routed `init` to the installer, so `npx @sym-bot/mesh-channel doctor` silently fell through and started the MCP server instead. Now `init`, `doctor`, and `start` all route to the installer/launcher.
|
|
19
|
+
- **`init --force` with an explicit `SYM_NODE_NAME` now relabels the entry** instead of always preserving the prior name (symmetric with how `--group` already behaves). A routine reinstall with no explicit name still preserves identity.
|
|
20
|
+
|
|
3
21
|
## 0.3.13
|
|
4
22
|
|
|
5
23
|
### Changed
|
package/README.md
CHANGED
|
@@ -66,37 +66,39 @@ They're **not alternatives** — the channel is built *on* sym and speaks the sa
|
|
|
66
66
|
|
|
67
67
|
## Quick start
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
**One command — it configures the mesh and launches Claude Code with real-time push already on:**
|
|
70
70
|
|
|
71
71
|
```
|
|
72
|
-
|
|
73
|
-
/plugin install sym-mesh-channel@claude-community
|
|
72
|
+
npx @sym-bot/mesh-channel@latest start
|
|
74
73
|
```
|
|
75
74
|
|
|
76
|
-
|
|
75
|
+
Run it in any repo, in as many terminals as you like — the sessions discover each other over loopback (or the same wifi) and **think together in real time**. No flag to remember, no `plugin:` vs `server:` to choose — `start` wires the channel for you. (First run configures the MCP server; after that it just launches. Co-resident sessions don't collide — each becomes its own peer.)
|
|
77
76
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
Want the always-latest build straight from the source repo? Add it as its own marketplace instead:
|
|
77
|
+
Stand up a persistent **named** agent, or join a team room:
|
|
81
78
|
|
|
82
79
|
```
|
|
83
|
-
|
|
84
|
-
/plugin install sym-mesh-channel@sym-mesh-channel
|
|
80
|
+
npx @sym-bot/mesh-channel@latest start --name cto --group my-team
|
|
85
81
|
```
|
|
86
82
|
|
|
87
|
-
|
|
83
|
+
(`start --print` shows the exact `claude …` command without launching; anything after `--` is passed straight to `claude`, e.g. `… start -- --resume`.)
|
|
84
|
+
|
|
85
|
+
### Prefer the Claude Code plugin UI?
|
|
88
86
|
|
|
89
|
-
|
|
87
|
+
Install the plugin and you get the 11 MCP tools immediately:
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
/plugin install sym-mesh-channel@claude-community
|
|
91
|
+
```
|
|
90
92
|
|
|
91
|
-
|
|
93
|
+
For real-time push on this path, launch with the channel flag (the handle must match where you installed from):
|
|
92
94
|
|
|
93
95
|
```
|
|
94
96
|
claude --dangerously-load-development-channels plugin:sym-mesh-channel@claude-community
|
|
95
97
|
```
|
|
96
98
|
|
|
97
|
-
|
|
99
|
+
Want the latest build before the community directory syncs? Add SYM.BOT's own catalog instead — `/plugin marketplace add sym-bot/marketplace`, then `/plugin install sym-mesh-channel@sym-bot` (and launch with `plugin:sym-mesh-channel@sym-bot`).
|
|
98
100
|
|
|
99
|
-
The flag
|
|
101
|
+
> **The dev flag is temporary** — an Anthropic-side gate while the channel awaits the approved-channels allowlist ([anthropics/claude-plugins-official#1512](https://github.com/anthropics/claude-plugins-official/issues/1512)). `start` passes it under the hood today; once the channel is allowlisted it disappears. Curious when to use the npm/server install directly (named agents, repo-committed team config, the `sym` CLI)? See [Advanced](#advanced-named-agents-teams--the-cli).
|
|
100
102
|
|
|
101
103
|
## What you get
|
|
102
104
|
|
|
@@ -234,7 +236,9 @@ The plugin composes two open specs:
|
|
|
234
236
|
|
|
235
237
|
## Advanced: per-project node identity
|
|
236
238
|
|
|
237
|
-
|
|
239
|
+
*(npm / MCP-server install only — plugin sessions already get a distinct peer identity each.)*
|
|
240
|
+
|
|
241
|
+
With the global npm install, every Claude Code session on the machine shares one mesh identity (set in `~/.claude.json`). To give each project directory its own stable peer name instead — e.g. a "research" session and a "strategy" session on the same laptop — install per-project:
|
|
238
242
|
|
|
239
243
|
```bash
|
|
240
244
|
cd path/to/your/project
|
|
@@ -345,7 +349,7 @@ Some corporate networks block mDNS multicast entirely — try a hotspot or home
|
|
|
345
349
|
|
|
346
350
|
The 11 tools work without any flag. The real-time `<channel>` **push** is separate: Claude Code only delivers channel notifications for channels on its **approved-channels allowlist**, and sym-mesh-channel isn't on it yet — so push requires the development-channels flag matching your install path:
|
|
347
351
|
|
|
348
|
-
- plugin install: `--dangerously-load-development-channels plugin:sym-mesh-channel@claude-community` (or `@sym-
|
|
352
|
+
- plugin install: `--dangerously-load-development-channels plugin:sym-mesh-channel@claude-community` (or `@sym-bot` if you installed from the source-repo marketplace — the handle must match your install source)
|
|
349
353
|
- npm install: `--dangerously-load-development-channels server:claude-sym-mesh`
|
|
350
354
|
|
|
351
355
|
This is an Anthropic-side gate, not a bug here — once the channel is allowlisted the flag is no longer needed. Tracked in [anthropics/claude-plugins-official#1512](https://github.com/anthropics/claude-plugins-official/issues/1512).
|
|
@@ -358,15 +362,25 @@ Your shell profile (`~/.zshrc`, `~/.bashrc`) exports `SYM_RELAY_URL`. Claude Cod
|
|
|
358
362
|
|
|
359
363
|
Don't. Each session should have a distinct `SYM_NODE_NAME`. The SymNode acquires an exclusive lockfile on its identity (`~/.sym/nodes/<name>/lock.pid`) and refuses to start a second process with the same name. If you see `EIDENTITYLOCK`, kill the other process or pick a different name. For multiple parallel sessions with their own identities, use the per-project install above.
|
|
360
364
|
|
|
361
|
-
##
|
|
365
|
+
## Advanced: named agents, teams & the CLI
|
|
362
366
|
|
|
363
|
-
|
|
367
|
+
Everything above uses the plugin — all most users need. Install the engine as an npm package / MCP server instead **only** when you want one of these:
|
|
368
|
+
|
|
369
|
+
- **A persistent, *named* agent.** The plugin auto-names each session (`claude-myrepo-3f9a`); the server install lets you pin `SYM_NODE_NAME=cto` so the node always appears under the same name in `sym_peers`, mesh memory, and CMB lineage — a durable agent identity, not an anonymous session.
|
|
370
|
+
- **Team config committed to a repo.** Config lives in a project `.mcp.json`, so you can commit `SYM_GROUP=backend-team` (plus relay URL/token) into the repo — anyone who opens it in Claude Code auto-joins the team room with zero per-person setup.
|
|
371
|
+
- **The `sym` CLI and non-Claude agents.** This also installs [`@sym-bot/sym`](https://github.com/sym-bot/sym), giving you `sym ask` / `sym groups` in your terminal and an engine that other agents, scripts, and languages can share.
|
|
364
372
|
|
|
365
373
|
```bash
|
|
366
374
|
npm install -g @sym-bot/mesh-channel
|
|
367
375
|
```
|
|
368
376
|
|
|
369
|
-
|
|
377
|
+
This registers a raw MCP server keyed `claude-sym-mesh`. Because it's a server (not a plugin), its real-time channel handle is **`server:claude-sym-mesh`** — so launch with:
|
|
378
|
+
|
|
379
|
+
```bash
|
|
380
|
+
claude --dangerously-load-development-channels server:claude-sym-mesh
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
Pin a fixed per-session identity with [per-project node identity](#advanced-per-project-node-identity); set relay credentials in the env block per [cross-network setup](#cross-network-setup-own-hosted-relay).
|
|
370
384
|
|
|
371
385
|
## References
|
|
372
386
|
|
package/bin/install.js
CHANGED
|
@@ -55,8 +55,8 @@ const cmd = args.find((a) => !a.startsWith('--')) || 'init';
|
|
|
55
55
|
const groupArgIdx = args.indexOf('--group');
|
|
56
56
|
const groupArg = groupArgIdx !== -1 ? args[groupArgIdx + 1] : null;
|
|
57
57
|
|
|
58
|
-
if (cmd !== 'init' && cmd !== 'doctor') {
|
|
59
|
-
process.stderr.write(`Unknown command: ${cmd}\nUsage: sym-mesh-channel init [--project] [--force] [--group <name>]\n sym-mesh-channel doctor\n`);
|
|
58
|
+
if (cmd !== 'init' && cmd !== 'doctor' && cmd !== 'start') {
|
|
59
|
+
process.stderr.write(`Unknown command: ${cmd}\nUsage: sym-mesh-channel start [--project] [--name <node>] [--group <name>] [-- <claude args>]\n sym-mesh-channel init [--project] [--force] [--group <name>]\n sym-mesh-channel doctor\n`);
|
|
60
60
|
process.exit(1);
|
|
61
61
|
}
|
|
62
62
|
|
|
@@ -111,6 +111,115 @@ function preserveGroup(entry) {
|
|
|
111
111
|
return g || null;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
// ── start: one command to a live mesh session ─────────────────────
|
|
115
|
+
// `sym-mesh-channel start` configures the MCP server (only if needed)
|
|
116
|
+
// and then launches Claude Code with the real-time Channels flag
|
|
117
|
+
// already on — so the user never types `--dangerously-load-development-
|
|
118
|
+
// channels …` or has to choose between the plugin: and server: handle.
|
|
119
|
+
// The npx / MCP-server install path always exposes the channel as a raw
|
|
120
|
+
// server, so the handle is deterministically `server:claude-sym-mesh`.
|
|
121
|
+
//
|
|
122
|
+
// sym-mesh-channel start # this dir, real-time push on
|
|
123
|
+
// sym-mesh-channel start --project --name cto --group my-team
|
|
124
|
+
// sym-mesh-channel start --print # show the command, don't launch
|
|
125
|
+
// sym-mesh-channel start -- --resume # pass args through to claude
|
|
126
|
+
//
|
|
127
|
+
// Co-resident sessions sharing one config don't collide: server.js
|
|
128
|
+
// auto-suffixes a live-identity clash (since 0.3.10), so each session
|
|
129
|
+
// becomes its own peer.
|
|
130
|
+
if (cmd === 'start') {
|
|
131
|
+
const { spawnSync } = require('child_process');
|
|
132
|
+
|
|
133
|
+
const nameArgIdx = args.indexOf('--name');
|
|
134
|
+
const nameArg = nameArgIdx !== -1 ? args[nameArgIdx + 1] : null;
|
|
135
|
+
const printOnly = args.includes('--print') || args.includes('--dry-run');
|
|
136
|
+
|
|
137
|
+
// Everything after `--` is forwarded verbatim to `claude`.
|
|
138
|
+
const dashDash = args.indexOf('--');
|
|
139
|
+
const passthrough = dashDash !== -1 ? args.slice(dashDash + 1) : [];
|
|
140
|
+
|
|
141
|
+
const launchDir = process.cwd();
|
|
142
|
+
|
|
143
|
+
const handle = 'server:claude-sym-mesh';
|
|
144
|
+
const claudeArgs = ['--dangerously-load-development-channels', handle, ...passthrough];
|
|
145
|
+
|
|
146
|
+
// --print is a pure dry-run: show the launch command, change nothing.
|
|
147
|
+
if (printOnly) {
|
|
148
|
+
console.log(`claude ${claudeArgs.join(' ')}`);
|
|
149
|
+
process.exit(0);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Is the scope Claude Code will actually read already configured with a
|
|
153
|
+
// LIVE entry? --project → <cwd>/.mcp.json ; otherwise → ~/.claude.json
|
|
154
|
+
// Reconcile identity against the persisted entry whether or not its
|
|
155
|
+
// server.js path is stale. npx rotates the cached server.js path on every
|
|
156
|
+
// version resolve (…/_npx/<hash>/…), so the entry is routinely stale yet
|
|
157
|
+
// still carries the node's name/group. Comparing only against a *live*
|
|
158
|
+
// entry let an explicit --name lose to the stale name on the heal path:
|
|
159
|
+
// start saw no live entry, pushed no --force, and init's non-force
|
|
160
|
+
// precedence (preserve-over-request) kept the old identity.
|
|
161
|
+
function rawEntryInScope() {
|
|
162
|
+
try {
|
|
163
|
+
const p = isProject
|
|
164
|
+
? path.join(launchDir, '.mcp.json')
|
|
165
|
+
: path.join(os.homedir(), '.claude.json');
|
|
166
|
+
if (!fs.existsSync(p)) return null;
|
|
167
|
+
const j = JSON.parse(fs.readFileSync(p, 'utf8'));
|
|
168
|
+
return (j && j.mcpServers && j.mcpServers['claude-sym-mesh']) || null;
|
|
169
|
+
} catch { return null; }
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const existing = rawEntryInScope();
|
|
173
|
+
const stale = existing ? isStaleEntry(existing) : false;
|
|
174
|
+
const wantName = nameArg || null;
|
|
175
|
+
const wantGroup = groupArg || null;
|
|
176
|
+
const mismatch = !!existing && (
|
|
177
|
+
(wantName && preserveNodeName(existing) !== wantName) ||
|
|
178
|
+
(wantGroup && (preserveGroup(existing) || 'default') !== wantGroup)
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
// Configure when there's nothing persisted yet, the persisted server.js
|
|
182
|
+
// path is stale (heal), an explicit --name/--group differs, or --force.
|
|
183
|
+
// Otherwise launch straight away — `start` stays cheap to run every
|
|
184
|
+
// session.
|
|
185
|
+
if (!existing || stale || mismatch || force) {
|
|
186
|
+
const initArgs = ['init'];
|
|
187
|
+
if (isProject) initArgs.push('--project');
|
|
188
|
+
if (groupArg) initArgs.push('--group', groupArg);
|
|
189
|
+
// --force makes init honor the explicit --name/--group over the
|
|
190
|
+
// persisted value. Required on the rename path AND on stale-heal with
|
|
191
|
+
// an explicit --name, where init would otherwise preserve the old name
|
|
192
|
+
// and silently drop the request. Force alone never clobbers an
|
|
193
|
+
// unspecified group: resolveGroup() still preserves when no --group.
|
|
194
|
+
if (existing && (mismatch || force)) initArgs.push('--force');
|
|
195
|
+
const childEnv = Object.assign({}, process.env);
|
|
196
|
+
if (nameArg) childEnv.SYM_NODE_NAME = nameArg;
|
|
197
|
+
const r = spawnSync(process.execPath, [__filename, ...initArgs], {
|
|
198
|
+
stdio: 'inherit', env: childEnv, cwd: launchDir,
|
|
199
|
+
});
|
|
200
|
+
if (r.status !== 0) process.exit(r.status == null ? 1 : r.status);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
console.log(`\n▶ Launching Claude Code on the SYM mesh — real-time push on.\n (channel: ${handle}; the dev flag is temporary until Anthropic allowlists it)\n`);
|
|
204
|
+
// On Windows the `claude` CLI is a `.cmd`/`.ps1` shim (npm) or `.exe`
|
|
205
|
+
// (native installer). Node's spawn does an exact-filename lookup that
|
|
206
|
+
// ignores PATHEXT, so bare `spawnSync('claude', …)` returns ENOENT even
|
|
207
|
+
// when `claude` runs fine in the user's shell. Route through a shell on
|
|
208
|
+
// Windows so PATHEXT resolution kicks in; quote args that contain spaces
|
|
209
|
+
// since shell:true forwards the args unquoted.
|
|
210
|
+
const isWindows = process.platform === 'win32';
|
|
211
|
+
const spawnArgs = isWindows
|
|
212
|
+
? claudeArgs.map((a) => (/\s/.test(a) ? `"${a}"` : a))
|
|
213
|
+
: claudeArgs;
|
|
214
|
+
const run = spawnSync('claude', spawnArgs, { stdio: 'inherit', cwd: launchDir, shell: isWindows });
|
|
215
|
+
if (run.error && run.error.code === 'ENOENT') {
|
|
216
|
+
process.stderr.write('ERROR: `claude` was not found on your PATH.\n');
|
|
217
|
+
process.stderr.write('Install Claude Code (https://claude.com/code), make sure `claude` runs in your terminal, then re-run `sym-mesh-channel start`.\n');
|
|
218
|
+
process.exit(127);
|
|
219
|
+
}
|
|
220
|
+
process.exit(run.status == null ? 0 : run.status);
|
|
221
|
+
}
|
|
222
|
+
|
|
114
223
|
// --postinstall always runs global install (npm postinstall runs from
|
|
115
224
|
// npm's staging directory, not the user's project dir). If both flags
|
|
116
225
|
// are passed, the --project flag is ignored during postinstall.
|
|
@@ -219,9 +328,12 @@ if (useProjectMode) {
|
|
|
219
328
|
process.exit(2);
|
|
220
329
|
}
|
|
221
330
|
|
|
222
|
-
//
|
|
223
|
-
//
|
|
224
|
-
|
|
331
|
+
// --force + an explicit SYM_NODE_NAME deliberately relabels this entry
|
|
332
|
+
// (symmetric with resolveGroup). Otherwise preserve the prior name so the
|
|
333
|
+
// mesh identity doesn't drift back to the hostname default on a reinstall.
|
|
334
|
+
const projectNodeName = (force && process.env.SYM_NODE_NAME)
|
|
335
|
+
? process.env.SYM_NODE_NAME
|
|
336
|
+
: (preserveNodeName(existingProjectEntry) || nodeName);
|
|
225
337
|
|
|
226
338
|
// Group resolution priority — see resolveGroup() at top of file.
|
|
227
339
|
// Summary: --force + explicit flag/env wins; otherwise preserve, then
|
|
@@ -469,7 +581,9 @@ if (existingTopEntry && !force && !topEntryIsStale) {
|
|
|
469
581
|
}
|
|
470
582
|
|
|
471
583
|
// Preserve the prior node name on rewrite so mesh identity doesn't drift.
|
|
472
|
-
const topNodeName =
|
|
584
|
+
const topNodeName = (force && process.env.SYM_NODE_NAME)
|
|
585
|
+
? process.env.SYM_NODE_NAME
|
|
586
|
+
: (preserveNodeName(existingTopEntry) || nodeName);
|
|
473
587
|
|
|
474
588
|
// Resolve SYM_GROUP for the global entry — see resolveGroup() at top.
|
|
475
589
|
// Heal-path default preserves; --force lets the user explicitly switch
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
// Subcommand dispatch:
|
|
5
|
-
|
|
4
|
+
// Subcommand dispatch: CLI subcommands run the installer/launcher, not the
|
|
5
|
+
// MCP server. (When Claude Code spawns the server there is no subcommand, so
|
|
6
|
+
// argv[2] is undefined and this falls through to the MCP server below.)
|
|
7
|
+
if (['init', 'doctor', 'start'].includes(process.argv[2])) {
|
|
6
8
|
require('./bin/install.js');
|
|
7
9
|
return;
|
|
8
10
|
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "sym-mesh-channel",
|
|
3
|
-
"owner": {
|
|
4
|
-
"name": "SYM.BOT",
|
|
5
|
-
"email": "info@sym.bot"
|
|
6
|
-
},
|
|
7
|
-
"metadata": {
|
|
8
|
-
"description": "Real-time communication and collaboration among Claude Code sessions — agent-to-agent cognitive signals over Bonjour LAN or WebSocket relay, on the Mesh Memory Protocol (MMP).",
|
|
9
|
-
"version": "0.3.11"
|
|
10
|
-
},
|
|
11
|
-
"plugins": [
|
|
12
|
-
{
|
|
13
|
-
"name": "sym-mesh-channel",
|
|
14
|
-
"source": "./",
|
|
15
|
-
"description": "Real-time communication and collaboration among Claude Code sessions. Turns Claude Code into a peer node on the SYM mesh — two or more sessions discover each other via Bonjour (LAN) or a WebSocket relay (cross-network) and exchange structured cognitive signals as channel notifications. Each peer has its own Ed25519 identity, SVAF content gating, and local memory. Built on the Mesh Memory Protocol (MMP), an open peer-to-peer protocol for multi-agent collective intelligence.",
|
|
16
|
-
"version": "0.3.11",
|
|
17
|
-
"author": {
|
|
18
|
-
"name": "Hongwei Xu",
|
|
19
|
-
"email": "hongwei@sym.bot"
|
|
20
|
-
},
|
|
21
|
-
"homepage": "https://meshcognition.org/spec/mmp",
|
|
22
|
-
"repository": "https://github.com/sym-bot/sym-mesh-channel",
|
|
23
|
-
"license": "Apache-2.0",
|
|
24
|
-
"keywords": [
|
|
25
|
-
"mesh",
|
|
26
|
-
"p2p",
|
|
27
|
-
"mcp",
|
|
28
|
-
"channel",
|
|
29
|
-
"agents",
|
|
30
|
-
"multi-agent",
|
|
31
|
-
"bonjour",
|
|
32
|
-
"cognitive",
|
|
33
|
-
"svaf",
|
|
34
|
-
"mmp"
|
|
35
|
-
],
|
|
36
|
-
"category": "agents",
|
|
37
|
-
"tags": [
|
|
38
|
-
"mesh",
|
|
39
|
-
"cognitive",
|
|
40
|
-
"channel",
|
|
41
|
-
"mcp",
|
|
42
|
-
"multi-agent"
|
|
43
|
-
]
|
|
44
|
-
}
|
|
45
|
-
]
|
|
46
|
-
}
|