claude-flow 3.39.3 → 3.41.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.
Files changed (30) hide show
  1. package/.claude/.proven-config-version +1 -0
  2. package/.claude/proven-config.json +42 -0
  3. package/.claude-plugin/marketplace.json +18 -18
  4. package/node_modules/@claude-flow/codex/dist/cli.js +0 -0
  5. package/node_modules/@claude-flow/plugin-agent-federation/dist/bin.js +0 -0
  6. package/package.json +1 -1
  7. package/v3/@claude-flow/cli/catalog-manifest.json +3 -3
  8. package/v3/@claude-flow/cli/dist/src/commands/federation.d.ts +8 -0
  9. package/v3/@claude-flow/cli/dist/src/commands/federation.js +98 -0
  10. package/v3/@claude-flow/cli/dist/src/commands/index.d.ts +1 -0
  11. package/v3/@claude-flow/cli/dist/src/commands/index.js +4 -0
  12. package/v3/@claude-flow/cli/dist/src/commands/memory.js +9 -2
  13. package/v3/@claude-flow/cli/dist/src/mcp-client.js +8 -0
  14. package/v3/@claude-flow/cli/dist/src/mcp-tools/agentbbs-federation.d.ts +178 -0
  15. package/v3/@claude-flow/cli/dist/src/mcp-tools/agentbbs-federation.js +447 -0
  16. package/v3/@claude-flow/cli/dist/src/mcp-tools/agentbbs-tools.js +139 -2
  17. package/v3/@claude-flow/cli/dist/src/mcp-tools/index.d.ts +4 -0
  18. package/v3/@claude-flow/cli/dist/src/mcp-tools/index.js +4 -0
  19. package/v3/@claude-flow/cli/dist/src/mcp-tools/seraphina-tools.d.ts +21 -0
  20. package/v3/@claude-flow/cli/dist/src/mcp-tools/seraphina-tools.js +88 -0
  21. package/v3/@claude-flow/cli/dist/src/mcp-tools/x-federation-channels.d.ts +30 -0
  22. package/v3/@claude-flow/cli/dist/src/mcp-tools/x-federation-channels.js +299 -0
  23. package/v3/@claude-flow/cli/dist/src/mcp-tools/x-federation-join.d.ts +31 -0
  24. package/v3/@claude-flow/cli/dist/src/mcp-tools/x-federation-join.js +87 -0
  25. package/v3/@claude-flow/cli/dist/src/mcp-tools/x-federation-tools.d.ts +12 -0
  26. package/v3/@claude-flow/cli/dist/src/mcp-tools/x-federation-tools.js +96 -0
  27. package/v3/@claude-flow/cli/dist/src/services/distill-oracle.d.ts +1 -1
  28. package/v3/@claude-flow/cli/dist/src/services/distill-oracle.js +1 -1
  29. package/v3/@claude-flow/cli/dist/src/services/harness-project-anchor.js +16 -5
  30. package/v3/@claude-flow/cli/package.json +4 -3
@@ -0,0 +1 @@
1
+ sha256:6141a8ea990c5063b77e090ae8f37f9c539d8aa8f58dcceb30f3a82f97e57319
@@ -0,0 +1,42 @@
1
+ {
2
+ "adoptedAt": 1788986588440,
3
+ "championId": "sha256:6141a8ea990c5063b77e090ae8f37f9c539d8aa8f58dcceb30f3a82f97e57319",
4
+ "manifest": {
5
+ "schema": "ruflo.proven-config/v1",
6
+ "policy": {
7
+ "ref": "sha256:6141a8ea990c5063b77e090ae8f37f9c539d8aa8f58dcceb30f3a82f97e57319",
8
+ "value": {
9
+ "alpha": 0.3,
10
+ "subjectWeight": 1,
11
+ "mmrLambda": 0.5,
12
+ "bodyWeight": 1.5,
13
+ "typePenaltyFactor": 0.5
14
+ }
15
+ },
16
+ "layer": "framework/node-cli",
17
+ "compatibility": {
18
+ "ruflo": ">=3.24.0"
19
+ },
20
+ "benchmark": {
21
+ "corpus": "ADR-081-labelled-v1",
22
+ "corpusHash": "sha256:2f700b5c363e20a3bd88ce2bc9b87bbbbaa61732c6177894c6ec37890f888982"
23
+ },
24
+ "receipt": {
25
+ "heldOutDelta": 0.07381404928570845,
26
+ "redblue": "PASS",
27
+ "drift": 0,
28
+ "canary": {
29
+ "rollbackRate": 0,
30
+ "latencyP95": 244.612458000076,
31
+ "costPerTask": 0
32
+ },
33
+ "receiptCoverage": 1
34
+ },
35
+ "platform": [
36
+ "linux",
37
+ "macOS",
38
+ "windows"
39
+ ]
40
+ },
41
+ "previous": ""
42
+ }
@@ -74,7 +74,7 @@
74
74
  {
75
75
  "name": "ruflo-agent",
76
76
  "source": "./plugins/ruflo-agent",
77
- "description": "Agent runtimes — local WASM-sandboxed agents (rvagent) + Anthropic Claude Managed Agents (cloud); one interface, local-vs-cloud backends"
77
+ "description": "Agent runtimes \u2014 local WASM-sandboxed agents (rvagent) + Anthropic Claude Managed Agents (cloud); one interface, local-vs-cloud backends"
78
78
  },
79
79
  {
80
80
  "name": "ruflo-workflows",
@@ -109,7 +109,7 @@
109
109
  {
110
110
  "name": "ruflo-adr",
111
111
  "source": "./plugins/ruflo-adr",
112
- "description": "ADR lifecycle management — create, index, supersede, and link Architecture Decision Records to code"
112
+ "description": "ADR lifecycle management \u2014 create, index, supersede, and link Architecture Decision Records to code"
113
113
  },
114
114
  {
115
115
  "name": "ruflo-cost-tracker",
@@ -119,7 +119,7 @@
119
119
  {
120
120
  "name": "ruflo-ddd",
121
121
  "source": "./plugins/ruflo-ddd",
122
- "description": "Domain-Driven Design scaffolding — bounded contexts, aggregate roots, domain events, and anti-corruption layers"
122
+ "description": "Domain-Driven Design scaffolding \u2014 bounded contexts, aggregate roots, domain events, and anti-corruption layers"
123
123
  },
124
124
  {
125
125
  "name": "ruflo-federation",
@@ -129,7 +129,7 @@
129
129
  {
130
130
  "name": "ruflo-graph-intelligence",
131
131
  "source": "./plugins/ruflo-graph-intelligence",
132
- "description": "Real-time graph intelligence — personalized PageRank, streaming delta updates, witness-signed reasoning, and federation-distributable vectors"
132
+ "description": "Real-time graph intelligence \u2014 personalized PageRank, streaming delta updates, witness-signed reasoning, and federation-distributable vectors"
133
133
  },
134
134
  {
135
135
  "name": "ruflo-iot-cognitum",
@@ -139,67 +139,67 @@
139
139
  {
140
140
  "name": "ruflo-knowledge-graph",
141
141
  "source": "./plugins/ruflo-knowledge-graph",
142
- "description": "Knowledge graph construction — entity extraction, relation mapping, and pathfinder graph traversal"
142
+ "description": "Knowledge graph construction \u2014 entity extraction, relation mapping, and pathfinder graph traversal"
143
143
  },
144
144
  {
145
145
  "name": "ruflo-market-data",
146
146
  "source": "./plugins/ruflo-market-data",
147
- "description": "Market data ingestion — feed normalization, OHLCV vectorization, and HNSW-indexed pattern matching"
147
+ "description": "Market data ingestion \u2014 feed normalization, OHLCV vectorization, and HNSW-indexed pattern matching"
148
148
  },
149
149
  {
150
150
  "name": "ruflo-migrations",
151
151
  "source": "./plugins/ruflo-migrations",
152
- "description": "Schema migration management — generate, validate, dry-run, and rollback database migrations"
152
+ "description": "Schema migration management \u2014 generate, validate, dry-run, and rollback database migrations"
153
153
  },
154
154
  {
155
155
  "name": "ruflo-neural-trader",
156
156
  "source": "./plugins/ruflo-neural-trader",
157
- "description": "Neural trading strategies — self-learning LSTM/Transformer/N-BEATS models with Rust/NAPI backtesting"
157
+ "description": "Neural trading strategies \u2014 self-learning LSTM/Transformer/N-BEATS models with Rust/NAPI backtesting"
158
158
  },
159
159
  {
160
160
  "name": "ruflo-observability",
161
161
  "source": "./plugins/ruflo-observability",
162
- "description": "Structured logging, distributed tracing, and metrics — correlate agent swarm activity with application telemetry"
162
+ "description": "Structured logging, distributed tracing, and metrics \u2014 correlate agent swarm activity with application telemetry"
163
163
  },
164
164
  {
165
165
  "name": "ruflo-ruvector",
166
166
  "source": "./plugins/ruflo-ruvector",
167
- "description": "Self-learning vector database — HNSW, FlashAttention-3, Graph RAG, hybrid search, DiskANN, and Brain AGI"
167
+ "description": "Self-learning vector database \u2014 HNSW, FlashAttention-3, Graph RAG, hybrid search, DiskANN, and Brain AGI"
168
168
  },
169
169
  {
170
170
  "name": "ruflo-sparc",
171
171
  "source": "./plugins/ruflo-sparc",
172
- "description": "SPARC methodology — Specification, Pseudocode, Architecture, Refinement, Completion phases with quality gates"
172
+ "description": "SPARC methodology \u2014 Specification, Pseudocode, Architecture, Refinement, Completion phases with quality gates"
173
173
  },
174
174
  {
175
175
  "name": "ruflo-metaharness",
176
176
  "source": "./plugins/ruflo-metaharness",
177
- "description": "MetaHarness integration — surfaces score/genome/mint/mcp-scan/threat-model via skills; pairs with @metaharness/router (ADR-148/149) for cost-optimal model routing; honors ADR-150 optional-augmentation constraint"
177
+ "description": "MetaHarness integration \u2014 surfaces score/genome/mint/mcp-scan/threat-model via skills; pairs with @metaharness/router (ADR-148/149) for cost-optimal model routing; honors ADR-150 optional-augmentation constraint"
178
178
  },
179
179
  {
180
180
  "name": "ruflo-arena",
181
181
  "source": "./plugins/ruflo-arena",
182
- "description": "Competitive ruliology for ruflo swarms — arenas, tournaments, and adaptive co-evolution of program strategies (ADR-147/148); strategies-as-programs compete under payoff games"
182
+ "description": "Competitive ruliology for ruflo swarms \u2014 arenas, tournaments, and adaptive co-evolution of program strategies (ADR-147/148); strategies-as-programs compete under payoff games"
183
183
  },
184
184
  {
185
185
  "name": "ruflo-agntcy",
186
186
  "source": "./plugins/ruflo-agntcy",
187
- "description": "AGNTCY/Outshift Internet-of-Cognition runtime integration — SLIM secure transport, CASA intent-scoped tool authorization, and AGNTCY identity/observability spans (ADR-380); optional, removable augmentation per ADR-150"
187
+ "description": "AGNTCY/Outshift Internet-of-Cognition runtime integration \u2014 SLIM secure transport, CASA intent-scoped tool authorization, and AGNTCY identity/observability spans (ADR-380); optional, removable augmentation per ADR-150"
188
188
  },
189
189
  {
190
190
  "name": "ruflo-bbs-federation",
191
191
  "source": "./plugins/ruflo-bbs-federation",
192
- "description": "AgentBBS federated business-domain BBS room integration (ADR-164 Phase 1) — register/publish/watch/human-join MCP tools wrapping the agentbbs Rust workspace as a federation peer; degrades gracefully when agentbbs is absent"
192
+ "description": "AgentBBS cross-host agent federation (ADR-164). Phase 1: register/publish/watch/human-join room coordination. Phase 2 (v3.40.0): signed cross-host federation \u2014 identity, peer_add (pin by nodeId+Ed25519 key), peers, serve (read-only pull), sync (pull + union-merge what verifies). Registry-anchored pinning; works on any network (no tailnet required); coordinates work claims (ClaimIssued/Released/Handoff/Ack). Degrades gracefully when agentbbs is absent."
193
193
  },
194
194
  {
195
195
  "name": "ruflo-business-pods",
196
196
  "source": "./plugins/ruflo-business-pods",
197
- "description": "Business-domain pod templates for the federated business autopilot (ADR-164 Phase 2) — pod templates plus a dry-run-by-default single-tick runner that validates, reserves budget, and resolves agent roles before posting to the BBS room"
197
+ "description": "Business-domain pod templates for the federated business autopilot (ADR-164 Phase 2) \u2014 pod templates plus a dry-run-by-default single-tick runner that validates, reserves budget, and resolves agent roles before posting to the BBS room"
198
198
  },
199
199
  {
200
200
  "name": "ruflo-music",
201
201
  "source": "./plugins/ruflo-music",
202
- "description": "AI music generation via Cognitum Music (cogmusic MCP) — compose lyrics/prompts, generate tracks, separate stems, extract MIDI, and master through your own music.cognitum.one account"
202
+ "description": "AI music generation via Cognitum Music (cogmusic MCP) \u2014 compose lyrics/prompts, generate tracks, separate stems, extract MIDI, and master through your own music.cognitum.one account"
203
203
  }
204
204
  ]
205
- }
205
+ }
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-flow",
3
- "version": "3.39.3",
3
+ "version": "3.41.0",
4
4
  "workspaces": [
5
5
  "v3/@claude-flow/codex",
6
6
  "v3/@claude-flow/mcp",
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "generation": 6,
4
- "generatedAt": "2026-09-09T20:21:54.605Z",
5
- "gitSha": "07f1cf3a",
4
+ "generatedAt": "2026-09-10T11:59:15.567Z",
5
+ "gitSha": "cb10bf61",
6
6
  "catalog": {
7
7
  "agents": 167,
8
- "tools": 397,
8
+ "tools": 418,
9
9
  "skills": 34
10
10
  },
11
11
  "benchmark": null
@@ -0,0 +1,8 @@
1
+ /**
2
+ * `ruflo federation` — open swarm federation via x.ruv.io (Nostr, signed,
3
+ * membership-gated). Thin CLI over the x_federation_* MCP tools so the same
4
+ * behaviour is available in-process and from any MCP client.
5
+ */
6
+ import type { Command } from '../types.js';
7
+ export declare const federationCommand: Command;
8
+ //# sourceMappingURL=federation.d.ts.map
@@ -0,0 +1,98 @@
1
+ import { output } from '../output.js';
2
+ import { callMCPTool } from '../mcp-client.js';
3
+ function printJsonOrTable(ctx, data, title) {
4
+ if (ctx.flags.format === 'json') {
5
+ output.printJson(data);
6
+ return;
7
+ }
8
+ output.printInfo(title);
9
+ output.writeln(JSON.stringify(data, null, 2));
10
+ }
11
+ async function run(ctx, tool, args, title) {
12
+ try {
13
+ // CLI flag --gateway takes precedence over the RUFLO_X_GATEWAY_URL env var (ADR-125).
14
+ const data = await callMCPTool(tool, { gatewayUrl: ctx.flags.gateway, ...args });
15
+ printJsonOrTable(ctx, data, title);
16
+ return { success: true, data };
17
+ }
18
+ catch (e) {
19
+ output.printError(`${title} failed: ${e.message}`);
20
+ return { success: false, exitCode: 1 };
21
+ }
22
+ }
23
+ export const federationCommand = {
24
+ name: 'federation',
25
+ description: 'Open swarm federation via x.ruv.io — sync messages, roster, claims, registry, invites (Nostr, signed, membership-gated)',
26
+ options: [
27
+ { name: 'format', short: 'f', description: 'Output format (json|text)', type: 'string', default: 'text' },
28
+ { name: 'gateway', description: 'Gateway base URL (takes precedence over RUFLO_X_GATEWAY_URL; default https://x.ruv.io)', type: 'string' },
29
+ ],
30
+ subcommands: [
31
+ { name: 'join', description: 'Join the open swarm with YOUR OWN key using an invite code (generates ~/.ruflo/nostr.key if absent, claims via NIP-98, verifies via NIP-42)',
32
+ options: [{ name: 'code', description: 'Invite code (v2.…) — a bearer secret, keep it private', type: 'string', required: true }],
33
+ action: (ctx) => run(ctx, 'x_federation_join', { code: ctx.flags.code }, 'Join federation') },
34
+ { name: 'sync', description: 'Fetch recent verified swarm messages',
35
+ options: [{ name: 'since', description: 'Look-back seconds (default 3600)', type: 'number' }, { name: 'limit', description: 'Max messages', type: 'number' }, { name: 'type', description: 'Filter by message type', type: 'string' }],
36
+ action: (ctx) => run(ctx, 'x_federation_sync', { sinceSeconds: ctx.flags.since, limit: ctx.flags.limit, type: ctx.flags.type }, 'Federation sync') },
37
+ { name: 'roster', description: 'Nodes currently announcing on the open swarm', action: (ctx) => run(ctx, 'x_federation_roster', {}, 'Swarm roster') },
38
+ { name: 'claims', description: 'Current owner-per-resource claims ledger', action: (ctx) => run(ctx, 'x_federation_claims', {}, 'Claims board') },
39
+ { name: 'registry', description: 'Relay, canonical NIP-42 relay tag, gateway pubkey and self-join steps', action: (ctx) => run(ctx, 'x_federation_registry', {}, 'Federation registry') },
40
+ { name: 'invite', description: 'Mint a self-join invite code (admin; needs RUFLO_X_ADMIN_TOKEN). The code is a bearer secret — share privately.',
41
+ options: [{ name: 'ttl', description: 'Validity seconds (default 7d)', type: 'number' }, { name: 'uses', description: 'Max redemptions (default 25)', type: 'number' }],
42
+ action: (ctx) => run(ctx, 'x_federation_invite_mint', { ttlSecs: ctx.flags.ttl, maxUses: ctx.flags.uses }, 'Invite minted') },
43
+ { name: 'admit', description: 'Admit a 64-hex Nostr pubkey as relay member (admin; needs RUFLO_X_ADMIN_TOKEN)',
44
+ options: [{ name: 'pubkey', description: '64-hex pubkey', type: 'string', required: true }, { name: 'role', description: 'member|admin', type: 'string' }],
45
+ action: (ctx) => run(ctx, 'x_federation_admit', { pubkey: ctx.flags.pubkey, role: ctx.flags.role }, 'Admit member') },
46
+ { name: 'publish', description: 'Publish a message AS THE GATEWAY (admin; needs RUFLO_X_ADMIN_TOKEN). Nodes should publish with their own key instead.',
47
+ options: [{ name: 'type', description: 'Message type (Status|Task|Result|…)', type: 'string', required: true }, { name: 'payload', description: 'JSON payload', type: 'string', required: true }],
48
+ action: (ctx) => {
49
+ let payload;
50
+ try {
51
+ payload = JSON.parse(String(ctx.flags.payload));
52
+ }
53
+ catch {
54
+ output.printError('--payload must be JSON');
55
+ return Promise.resolve({ success: false, exitCode: 1 });
56
+ }
57
+ return run(ctx, 'x_federation_publish', { msgType: ctx.flags.type, payload }, 'Published');
58
+ } },
59
+ { name: 'channel', description: 'Public and private swarm channels (ADR-386). Private channels are encrypted with a key only this machine holds.',
60
+ options: [
61
+ { name: 'action', description: 'create|grant|accept|publish|read|list', type: 'string', required: true },
62
+ { name: 'name', description: 'Channel name (create)', type: 'string' },
63
+ { name: 'visibility', description: 'public|private (create)', type: 'string' },
64
+ { name: 'channel', description: 'Channel id: pub:<name> or prv:<16 hex>', type: 'string' },
65
+ { name: 'pubkey', description: '64-hex member pubkey (grant)', type: 'string' },
66
+ { name: 'type', description: 'Message type (publish)', type: 'string' },
67
+ { name: 'payload', description: 'JSON payload (publish)', type: 'string' },
68
+ { name: 'since', description: 'Look-back seconds (read|accept)', type: 'number' },
69
+ { name: 'limit', description: 'Max messages (read)', type: 'number' },
70
+ ],
71
+ action: (ctx) => {
72
+ const a = String(ctx.flags.action);
73
+ switch (a) {
74
+ case 'create': return run(ctx, 'x_federation_channel_create', { name: ctx.flags.name, visibility: ctx.flags.visibility ?? 'public' }, 'Channel created');
75
+ case 'grant': return run(ctx, 'x_federation_channel_grant', { channel: ctx.flags.channel, pubkey: ctx.flags.pubkey }, 'Channel granted');
76
+ case 'accept': return run(ctx, 'x_federation_channel_accept', { sinceSeconds: ctx.flags.since }, 'Grants accepted');
77
+ case 'read': return run(ctx, 'x_federation_channel_read', { channel: ctx.flags.channel, sinceSeconds: ctx.flags.since, limit: ctx.flags.limit }, 'Channel messages');
78
+ case 'list': return run(ctx, 'x_federation_channel_list', {}, 'Channel keys held');
79
+ case 'publish': {
80
+ let payload;
81
+ try {
82
+ payload = JSON.parse(String(ctx.flags.payload));
83
+ }
84
+ catch {
85
+ output.printError('--payload must be JSON');
86
+ return Promise.resolve({ success: false, exitCode: 1 });
87
+ }
88
+ return run(ctx, 'x_federation_channel_publish', { channel: ctx.flags.channel, msgType: ctx.flags.type, payload }, 'Published to channel');
89
+ }
90
+ default:
91
+ output.printError('--action must be create|grant|accept|publish|read|list');
92
+ return Promise.resolve({ success: false, exitCode: 1 });
93
+ }
94
+ } },
95
+ ],
96
+ action: async (ctx) => { output.printInfo('Usage: ruflo federation <join|sync|roster|claims|registry|invite|admit|publish|channel>'); void ctx; return { success: true }; },
97
+ };
98
+ //# sourceMappingURL=federation.js.map
@@ -17,6 +17,7 @@ export { sessionCommand } from './session.js';
17
17
  export { agentCommand } from './agent.js';
18
18
  export { swarmCommand } from './swarm.js';
19
19
  export { memoryCommand } from './memory.js';
20
+ export { federationCommand } from './federation.js';
20
21
  export { mcpCommand } from './mcp.js';
21
22
  export { hooksCommand } from './hooks.js';
22
23
  export declare function getConfigCommand(): Promise<Command | undefined>;
@@ -138,6 +138,7 @@ import { sessionCommand } from './session.js';
138
138
  import { agentCommand } from './agent.js';
139
139
  import { swarmCommand } from './swarm.js';
140
140
  import { memoryCommand } from './memory.js';
141
+ import { federationCommand } from './federation.js';
141
142
  import { mcpCommand } from './mcp.js';
142
143
  import { hooksCommand } from './hooks.js';
143
144
  // Pre-populate cache with core commands only
@@ -149,6 +150,7 @@ loadedCommands.set('session', sessionCommand);
149
150
  loadedCommands.set('agent', agentCommand);
150
151
  loadedCommands.set('swarm', swarmCommand);
151
152
  loadedCommands.set('memory', memoryCommand);
153
+ loadedCommands.set('federation', federationCommand);
152
154
  loadedCommands.set('mcp', mcpCommand);
153
155
  loadedCommands.set('hooks', hooksCommand);
154
156
  // =============================================================================
@@ -163,6 +165,7 @@ export { sessionCommand } from './session.js';
163
165
  export { agentCommand } from './agent.js';
164
166
  export { swarmCommand } from './swarm.js';
165
167
  export { memoryCommand } from './memory.js';
168
+ export { federationCommand } from './federation.js';
166
169
  export { mcpCommand } from './mcp.js';
167
170
  export { hooksCommand } from './hooks.js';
168
171
  // Lazy-loaded command re-exports (for backwards compatibility, but async-only)
@@ -207,6 +210,7 @@ export const commands = [
207
210
  agentCommand,
208
211
  swarmCommand,
209
212
  memoryCommand,
213
+ federationCommand,
210
214
  mcpCommand,
211
215
  hooksCommand,
212
216
  ];
@@ -352,7 +352,14 @@ const searchCommand = {
352
352
  name: 'threshold',
353
353
  description: 'Similarity threshold (0-1)',
354
354
  type: 'number',
355
- default: 0.7
355
+ // MUST stay <= 0.4. The recall fusion in bridgeSearchEntries scores a
356
+ // full-coverage exact-keyword hit as 0.6*max(0,semantic) + 0.4*lexical,
357
+ // so when the semantic cosine is <= 0 (routine for a one-word query) a
358
+ // perfect keyword match tops out at exactly 0.40. #2790 raised this
359
+ // default to 0.7, which silently re-broke #2558: `memory search` matched
360
+ // content word-for-word and still returned nothing. Regression guard:
361
+ // __tests__/memory-search-recall-2558.test.ts.
362
+ default: 0.3
356
363
  },
357
364
  {
358
365
  name: 'type',
@@ -421,7 +428,7 @@ const searchCommand = {
421
428
  // coalescing preserves an explicit zero. Fallback aligned with the
422
429
  // option's declared `default: 0.7` (was `0.3` — the two disagreed
423
430
  // and --help advertised a default the code did not honor).
424
- const threshold = ctx.flags.threshold ?? 0.7;
431
+ const threshold = ctx.flags.threshold ?? 0.3;
425
432
  const searchType = ctx.flags.type || 'semantic';
426
433
  const buildHnsw = (ctx.flags['build-hnsw'] || ctx.flags.buildHnsw);
427
434
  const requestedIntent = ctx.flags.intent || 'mixed';
@@ -62,6 +62,10 @@ import { agenticowSpeculateTools } from './mcp-tools/agenticow-speculate-tools.j
62
62
  // ADR-164 — AgentBBS federated business-domain BBS rooms (Phase 1).
63
63
  // Optional runtime dep, every handler returns `{degraded: true}` when missing.
64
64
  import { agentbbsTools } from './mcp-tools/agentbbs-tools.js';
65
+ import { xFederationTools } from './mcp-tools/x-federation-tools.js';
66
+ import { seraphinaTools } from './mcp-tools/seraphina-tools.js';
67
+ import { xFederationJoinTools } from './mcp-tools/x-federation-join.js';
68
+ import { xFederationChannelTools } from './mcp-tools/x-federation-channels.js';
65
69
  // ADR-164 Phase 2 — Business-pod template validation (pure local, no optional deps).
66
70
  import { businessPodTools } from './mcp-tools/business-pod-tools.js';
67
71
  // ADR-164 Phase 4 §5.1.8 — http_fetch MCP tool (secure-by-default HTTP probe
@@ -167,6 +171,10 @@ registerTools([
167
171
  ...agenticowSpeculateTools,
168
172
  // ADR-164 — AgentBBS federated business-domain BBS rooms (4 tools, Phase 1, graceful-degraded)
169
173
  ...agentbbsTools,
174
+ ...xFederationTools,
175
+ ...seraphinaTools,
176
+ ...xFederationJoinTools,
177
+ ...xFederationChannelTools,
170
178
  // ADR-164 Phase 2 + Phase 3 — business_pod_validate + business_pod_route_backend
171
179
  // (2 tools, no optional dep — schema validator + §3.4 domain-affinity router)
172
180
  ...businessPodTools,
@@ -0,0 +1,178 @@
1
+ /**
2
+ * agentbbs Phase 2 — cross-host federation.
3
+ *
4
+ * Phase 1 (`agentbbs-tools.ts`) gives every host an append-only room log at
5
+ * `<basePath>/room-<roomId>.jsonl`, and derives `roomId` deterministically from
6
+ * the room label. Two independent hosts that register `#sales` therefore
7
+ * compute the *same* roomId without ever talking to each other. That is the
8
+ * property this module builds on.
9
+ *
10
+ * ## Why a signed union-merge, and not a consensus protocol
11
+ *
12
+ * A room log is an append-only set of immutable envelopes. Two hosts that each
13
+ * append locally hold two subsets of the same logical set, so reconciling them
14
+ * is a set union — there is no conflicting write to arbitrate, and therefore
15
+ * nothing for a consensus round to decide. Union is commutative, associative
16
+ * and idempotent, which makes sync order-independent and safe to retry: pulling
17
+ * the same peer twice, or pulling A-then-B versus B-then-A, converges on the
18
+ * same log. That is a CRDT (a grow-only set keyed by `envelopeId`), and it is
19
+ * strictly cheaper and less failure-prone than the Byzantine agreement the
20
+ * plugin README gestures at.
21
+ *
22
+ * What union does *not* give you is authenticity. If any peer can inject an
23
+ * envelope, the merge faithfully replicates forgeries. So every envelope is
24
+ * Ed25519-signed by its originating node, and a receiver verifies the signature
25
+ * against the *pinned* public key it recorded when the peer was added — not
26
+ * against a key carried in the envelope, which would let an attacker sign with
27
+ * their own key and claim any origin. Trust is pinned at peer-add time; the
28
+ * wire is treated as hostile.
29
+ *
30
+ * ## Transport
31
+ *
32
+ * Pull-based HTTP. A node serves `GET /agentbbs/v1/rooms/:roomId/envelopes?since=N`
33
+ * and peers poll it. Pull is deliberate: it needs no inbound connectivity from
34
+ * the peer's side, survives a node being offline (it just catches up later),
35
+ * and gives the *receiver* control over how much it ingests. Push would invert
36
+ * that and make every node an unauthenticated write target.
37
+ *
38
+ * Designed for a private overlay (Tailscale/WireGuard) where the network
39
+ * already authenticates the host. Signatures mean a compromised or hostile peer
40
+ * still cannot forge another node's envelopes. See `bindHost` on serve() — it
41
+ * binds loopback by default, and binding a routable interface is an explicit
42
+ * operator choice.
43
+ *
44
+ * ## Bounds
45
+ *
46
+ * Every ingest path is bounded: envelope count per sync, byte size per envelope,
47
+ * total bytes per response, peer count, and hop count. An unbounded merge from
48
+ * an untrusted peer is a memory-exhaustion primitive, so limits are enforced on
49
+ * the *receiving* side where they cannot be negotiated away by the sender.
50
+ *
51
+ * @module @claude-flow/cli/mcp-tools/agentbbs-federation
52
+ */
53
+ import { type Server } from 'node:http';
54
+ /** Max envelopes accepted from one peer in one sync. */
55
+ export declare const MAX_ENVELOPES_PER_SYNC = 5000;
56
+ /** Max serialized bytes for a single envelope. */
57
+ export declare const MAX_ENVELOPE_BYTES: number;
58
+ /** Max total bytes read from one peer response. */
59
+ export declare const MAX_SYNC_RESPONSE_BYTES: number;
60
+ /** Max peers in the registry. */
61
+ export declare const MAX_PEERS = 256;
62
+ /** Max federation hops before an envelope stops propagating. */
63
+ export declare const MAX_HOPS = 8;
64
+ /** Per-request timeout when pulling from a peer. */
65
+ export declare const SYNC_TIMEOUT_MS = 15000;
66
+ export interface NodeIdentity {
67
+ nodeId: string;
68
+ publicKey: string;
69
+ privateKey: string;
70
+ createdAt: string;
71
+ }
72
+ export interface FederationPeer {
73
+ nodeId: string;
74
+ url: string;
75
+ publicKey: string;
76
+ label?: string;
77
+ addedAt: string;
78
+ lastSyncedAt?: string;
79
+ lastSeq?: Record<string, number>;
80
+ }
81
+ export interface SignedEnvelope {
82
+ envelopeId: string;
83
+ roomId: string;
84
+ seq: number;
85
+ msgType: string;
86
+ payload: unknown;
87
+ timestamp: string;
88
+ origin?: string;
89
+ hops?: number;
90
+ signature?: string;
91
+ }
92
+ /**
93
+ * Deterministic byte string an envelope's signature covers.
94
+ *
95
+ * Field order is fixed here rather than relying on `JSON.stringify` key order,
96
+ * because a receiver must reconstruct byte-identical input from a payload that
97
+ * survived a JSON round trip. `payload` is canonicalized recursively with
98
+ * sorted keys for the same reason: `{a:1,b:2}` and `{b:2,a:1}` are the same
99
+ * value and must not produce different signatures.
100
+ *
101
+ * `hops` is deliberately excluded — it is mutated in transit by design, so
102
+ * including it would invalidate the signature at the first relay.
103
+ */
104
+ export declare function canonicalEnvelopeBytes(env: SignedEnvelope): Uint8Array;
105
+ /**
106
+ * Load or create this host's long-lived Ed25519 identity.
107
+ *
108
+ * Phase 1 minted an ephemeral key per process, which is fine for local token
109
+ * signing but useless across hosts: a peer cannot pin a key that changes on
110
+ * every restart. This persists one, 0600, and derives a stable nodeId from the
111
+ * public key so identity is verifiable rather than self-asserted.
112
+ */
113
+ export declare function getNodeIdentity(basePath: string): Promise<NodeIdentity>;
114
+ export declare function signEnvelope(basePath: string, env: SignedEnvelope): Promise<SignedEnvelope>;
115
+ /**
116
+ * Verify an envelope against a public key the caller already trusts.
117
+ *
118
+ * The key is passed in rather than read from the envelope on purpose: an
119
+ * envelope-carried key proves only that the sender holds *a* key, not that they
120
+ * are who the `origin` field claims. Callers pass the key pinned at peer-add.
121
+ */
122
+ export declare function verifyEnvelope(env: SignedEnvelope, publicKeyHex: string): Promise<boolean>;
123
+ export declare function readPeers(basePath: string): FederationPeer[];
124
+ /**
125
+ * Reject anything that is not a plain http(s) URL to a host.
126
+ *
127
+ * Blocks credentials-in-URL (they would be logged), and non-http schemes such
128
+ * as `file:` which would turn a peer entry into a local file read.
129
+ */
130
+ export declare function validatePeerUrl(raw: string): string;
131
+ export declare function addPeer(basePath: string, input: {
132
+ nodeId: string;
133
+ url: string;
134
+ publicKey: string;
135
+ label?: string;
136
+ }): FederationPeer;
137
+ export declare function removePeer(basePath: string, nodeId: string): boolean;
138
+ export declare function readEnvelopes(basePath: string, roomId: string): SignedEnvelope[];
139
+ export declare function validateRoomId(roomId: string): string;
140
+ export interface MergeResult {
141
+ merged: number;
142
+ skippedDuplicate: number;
143
+ skippedUnverified: number;
144
+ skippedOversize: number;
145
+ skippedHopLimit: number;
146
+ }
147
+ /**
148
+ * Union-merge verified envelopes from a peer into the local room log.
149
+ *
150
+ * Idempotent: `envelopeId` is the merge key, so replaying the same batch is a
151
+ * no-op. Anything that fails verification is dropped and counted rather than
152
+ * quarantined — a receiver has no use for an envelope it cannot attribute.
153
+ */
154
+ export declare function mergeEnvelopes(basePath: string, roomId: string, incoming: SignedEnvelope[], peerPublicKey: string): Promise<MergeResult>;
155
+ /** Pull one room from one peer and merge what verifies. */
156
+ export declare function syncRoomFromPeer(basePath: string, peer: FederationPeer, roomId: string, fetchImpl?: typeof fetch): Promise<MergeResult & {
157
+ peerNodeId: string;
158
+ roomId: string;
159
+ }>;
160
+ /**
161
+ * Serve this node's room logs for peers to pull.
162
+ *
163
+ * Binds loopback unless the caller explicitly asks otherwise, so starting a
164
+ * server never silently exposes room contents on a routable interface. Read
165
+ * only by construction: there is no route that mutates state, which removes the
166
+ * whole class of unauthenticated-write attacks that a push design would open.
167
+ */
168
+ export declare function serveFederation(basePath: string, opts?: {
169
+ port?: number;
170
+ bindHost?: string;
171
+ }): Promise<{
172
+ server: Server;
173
+ port: number;
174
+ host: string;
175
+ }>;
176
+ /** Constant-time compare for any future shared-secret paths. */
177
+ export declare function safeEqual(a: string, b: string): boolean;
178
+ //# sourceMappingURL=agentbbs-federation.d.ts.map