claude-flow 3.7.0-alpha.43 → 3.7.0-alpha.45

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/README.md CHANGED
@@ -6,6 +6,11 @@
6
6
  [![Goal Planner — goal.ruv.io](https://img.shields.io/badge/_Goal_Planner-goal.ruv.io-8b5cf6?style=for-the-badge&logoColor=white&logo=react)](https://goal.ruv.io/)
7
7
  [![Live Agents — goal.ruv.io/agents](https://img.shields.io/badge/_Live_Agents-goal.ruv.io%2Fagents-10b981?style=for-the-badge&logoColor=white&logo=react)](https://goal.ruv.io/agents)
8
8
 
9
+ [![npm version (ruflo)](https://img.shields.io/npm/v/ruflo?label=ruflo&style=for-the-badge&logo=npm&color=cb3837)](https://www.npmjs.com/package/ruflo)
10
+ [![npm downloads (ruflo)](https://img.shields.io/npm/dm/ruflo?label=ruflo%20downloads&style=for-the-badge&logo=npm&color=cb3837)](https://www.npmjs.com/package/ruflo)
11
+ [![npm version (claude-flow)](https://img.shields.io/npm/v/claude-flow?label=claude-flow&style=for-the-badge&logo=npm&color=blue)](https://www.npmjs.com/package/claude-flow)
12
+ [![npm downloads (claude-flow)](https://img.shields.io/npm/dm/claude-flow?label=claude-flow%20downloads&style=for-the-badge&logo=npm&color=blue)](https://www.npmjs.com/package/claude-flow)
13
+
9
14
  [![Star on GitHub](https://img.shields.io/github/stars/ruvnet/claude-flow?style=for-the-badge&logo=github&color=gold)](https://github.com/ruvnet/claude-flow)
10
15
  [![MIT License](https://img.shields.io/badge/License-MIT-yellow?style=for-the-badge)](https://opensource.org/licenses/MIT)
11
16
  [![Claude Code](https://img.shields.io/badge/Claude%20Code-Plugin-D97757?style=for-the-badge&logoColor=white&logo=anthropic)](https://github.com/ruvnet/claude-flow)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-flow",
3
- "version": "3.7.0-alpha.43",
3
+ "version": "3.7.0-alpha.45",
4
4
  "description": "Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -6,6 +6,11 @@
6
6
  [![Goal Planner — goal.ruv.io](https://img.shields.io/badge/_Goal_Planner-goal.ruv.io-8b5cf6?style=for-the-badge&logoColor=white&logo=react)](https://goal.ruv.io/)
7
7
  [![Live Agents — goal.ruv.io/agents](https://img.shields.io/badge/_Live_Agents-goal.ruv.io%2Fagents-10b981?style=for-the-badge&logoColor=white&logo=react)](https://goal.ruv.io/agents)
8
8
 
9
+ [![npm version (ruflo)](https://img.shields.io/npm/v/ruflo?label=ruflo&style=for-the-badge&logo=npm&color=cb3837)](https://www.npmjs.com/package/ruflo)
10
+ [![npm downloads (ruflo)](https://img.shields.io/npm/dm/ruflo?label=ruflo%20downloads&style=for-the-badge&logo=npm&color=cb3837)](https://www.npmjs.com/package/ruflo)
11
+ [![npm version (claude-flow)](https://img.shields.io/npm/v/claude-flow?label=claude-flow&style=for-the-badge&logo=npm&color=blue)](https://www.npmjs.com/package/claude-flow)
12
+ [![npm downloads (claude-flow)](https://img.shields.io/npm/dm/claude-flow?label=claude-flow%20downloads&style=for-the-badge&logo=npm&color=blue)](https://www.npmjs.com/package/claude-flow)
13
+
9
14
  [![Star on GitHub](https://img.shields.io/github/stars/ruvnet/claude-flow?style=for-the-badge&logo=github&color=gold)](https://github.com/ruvnet/claude-flow)
10
15
  [![MIT License](https://img.shields.io/badge/License-MIT-yellow?style=for-the-badge)](https://opensource.org/licenses/MIT)
11
16
  [![Claude Code](https://img.shields.io/badge/Claude%20Code-Plugin-D97757?style=for-the-badge&logoColor=white&logo=anthropic)](https://github.com/ruvnet/claude-flow)
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Hive-Mind Consensus Runtime — ADR-095 G2.2.
3
+ *
4
+ * Before this module, `hive-mind_*` MCP tools were a JSON-file state machine
5
+ * that hand-rolled raft/byzantine/quorum voting on top of `state.consensus`
6
+ * and never touched `@claude-flow/swarm`'s real `ConsensusEngine`. That worked
7
+ * for a single MCP server but ignored every cross-host machinery G2 was meant
8
+ * to unlock: real RequestVote/AppendEntries RPCs, Ed25519-signed PBFT
9
+ * messages, FederationTransport over ADR-104 WS, BFT quorum derived from
10
+ * actual cluster size.
11
+ *
12
+ * This module owns a process-level `ConsensusEngine` plus a pluggable
13
+ * `ConsensusTransport`. `hive-mind_init` calls `init()` here; the consensus /
14
+ * broadcast / shutdown MCP tools delegate to it.
15
+ *
16
+ * Transport selection:
17
+ * - `transport: 'local'` → LocalTransport (in-process registry).
18
+ * Matches the legacy single-process path;
19
+ * real engine, just no WS.
20
+ * - `transport: 'federation'` → FederationTransport over an
21
+ * agentic-flow/transport/loader wire.
22
+ * Real cross-host messaging. Requires
23
+ * `agentic-flow` to be resolvable; if
24
+ * it's not, init returns
25
+ * `transport: 'local'` with `degraded:
26
+ * true` and a fallbackReason — the
27
+ * engine still works locally, the caller
28
+ * just knows the cross-host wire didn't
29
+ * come up.
30
+ * - `transport: 'auto'` (default)→ Probe for `agentic-flow`; if loadable
31
+ * AND `peers` were supplied with
32
+ * addresses, use federation; else local.
33
+ *
34
+ * Why peers are passed in (not auto-discovered): the federation plugin's
35
+ * DiscoveryService is the canonical source of peers, but wiring it into the
36
+ * MCP runtime is a separate concern (it has its own lifecycle / consent
37
+ * gates). Until that lands as G2.3, callers explicitly supply the peer
38
+ * list; the runtime is honest about what it received.
39
+ */
40
+ import type { ConsensusEngine as ConsensusEngineType, ConsensusTransport as ConsensusTransportType, ConsensusVote, ConsensusProposal, ConsensusResult, ConsensusAlgorithm } from '@claude-flow/swarm';
41
+ export type HiveTransportKind = 'local' | 'federation' | 'auto';
42
+ export type HiveAlgorithm = 'raft' | 'byzantine' | 'gossip';
43
+ export interface HivePeerConfig {
44
+ /** Consensus node id used by Raft/PBFT/Gossip as `from`. */
45
+ readonly nodeId: string;
46
+ /**
47
+ * Wire-level address (e.g. `wss://host:port`). Required for
48
+ * `transport: 'federation'`; ignored for `transport: 'local'`.
49
+ */
50
+ readonly address?: string;
51
+ /**
52
+ * Ed25519 SPKI PEM public key for inbound signature verification.
53
+ * When provided, FederationTransport will fail-closed on unverifiable
54
+ * messages from this peer.
55
+ */
56
+ readonly publicKeyPem?: string;
57
+ }
58
+ export interface HiveRuntimeInitOptions {
59
+ readonly nodeId: string;
60
+ readonly algorithm?: HiveAlgorithm;
61
+ readonly transport?: HiveTransportKind;
62
+ readonly peers?: readonly HivePeerConfig[];
63
+ /** Default per-send timeout for transports. */
64
+ readonly timeoutMs?: number;
65
+ /** Consensus threshold (0-1). Forwarded to ConsensusEngine. */
66
+ readonly threshold?: number;
67
+ }
68
+ export interface HiveRuntimeInitResult {
69
+ readonly initialized: true;
70
+ readonly nodeId: string;
71
+ readonly algorithm: HiveAlgorithm;
72
+ /** What we actually got — may differ from requested if federation degraded. */
73
+ readonly transport: 'local' | 'federation';
74
+ /** True when the caller asked for federation but we fell back to local. */
75
+ readonly degraded: boolean;
76
+ /** Human-readable reason for the degradation. */
77
+ readonly fallbackReason?: string;
78
+ readonly peerCount: number;
79
+ readonly source: 'engine';
80
+ }
81
+ export interface HiveRuntimeStatus {
82
+ readonly initialized: boolean;
83
+ readonly nodeId?: string;
84
+ readonly algorithm?: HiveAlgorithm;
85
+ readonly transport: 'local' | 'federation' | null;
86
+ readonly degraded: boolean;
87
+ readonly peers: readonly string[];
88
+ readonly engine: {
89
+ readonly algorithm: ConsensusAlgorithm;
90
+ readonly totalProposals: number;
91
+ readonly pendingProposals: number;
92
+ readonly acceptedProposals: number;
93
+ readonly rejectedProposals: number;
94
+ readonly expiredProposals: number;
95
+ } | null;
96
+ }
97
+ declare class HiveConsensusRuntime {
98
+ private engine;
99
+ private transport;
100
+ private wire;
101
+ private transportKind;
102
+ private nodeId;
103
+ private algorithm;
104
+ private peers;
105
+ private degraded;
106
+ private fallbackReason;
107
+ isInitialized(): boolean;
108
+ /**
109
+ * Lazy-initialize the consensus engine + transport. Idempotent on the
110
+ * same nodeId/algorithm/transport tuple — subsequent calls return the
111
+ * existing state. Different parameters trigger a clean shutdown + reinit.
112
+ */
113
+ init(opts: HiveRuntimeInitOptions): Promise<HiveRuntimeInitResult>;
114
+ /**
115
+ * Probe for the agentic-flow QUIC/WS transport loader. Returns the wire
116
+ * when loadable; an explanatory reason when not. Never throws — failure
117
+ * is data, not control flow, so the runtime can degrade cleanly.
118
+ */
119
+ private tryLoadFederationWire;
120
+ /** Propose a value through the real ConsensusEngine. */
121
+ propose(value: unknown, proposerId?: string): Promise<ConsensusProposal>;
122
+ /** Record a vote against an active proposal. */
123
+ vote(proposalId: string, vote: ConsensusVote): Promise<void>;
124
+ /**
125
+ * Block until a proposal resolves (accepted / rejected / expired).
126
+ * Used by the MCP tool when callers want a synchronous result instead
127
+ * of polling `proposal-status`.
128
+ */
129
+ awaitConsensus(proposalId: string): Promise<ConsensusResult>;
130
+ /**
131
+ * Broadcast a free-form payload to all known peers via the transport.
132
+ * Gossip-style, no reply expected. Wraps the payload so peers don't
133
+ * confuse it with protocol messages — `type: 'hive-broadcast'`.
134
+ */
135
+ broadcast(payload: unknown, priority?: 'low' | 'normal' | 'high' | 'critical'): Promise<{
136
+ delivered: number;
137
+ transport: 'local' | 'federation';
138
+ }>;
139
+ status(): HiveRuntimeStatus;
140
+ shutdown(): Promise<void>;
141
+ /** Test seam: replace the singleton's underlying engine + transport. */
142
+ __setForTest(engine: ConsensusEngineType, transport: ConsensusTransportType, nodeId: string, kind?: 'local' | 'federation'): void;
143
+ private toInitResult;
144
+ }
145
+ /** Process-level singleton — one engine per MCP server. */
146
+ export declare const hiveConsensusRuntime: HiveConsensusRuntime;
147
+ /** Re-export the singleton's type for tests / advanced consumers. */
148
+ export type { HiveConsensusRuntime };
149
+ //# sourceMappingURL=hive-consensus-runtime.d.ts.map
@@ -0,0 +1,296 @@
1
+ /**
2
+ * Hive-Mind Consensus Runtime — ADR-095 G2.2.
3
+ *
4
+ * Before this module, `hive-mind_*` MCP tools were a JSON-file state machine
5
+ * that hand-rolled raft/byzantine/quorum voting on top of `state.consensus`
6
+ * and never touched `@claude-flow/swarm`'s real `ConsensusEngine`. That worked
7
+ * for a single MCP server but ignored every cross-host machinery G2 was meant
8
+ * to unlock: real RequestVote/AppendEntries RPCs, Ed25519-signed PBFT
9
+ * messages, FederationTransport over ADR-104 WS, BFT quorum derived from
10
+ * actual cluster size.
11
+ *
12
+ * This module owns a process-level `ConsensusEngine` plus a pluggable
13
+ * `ConsensusTransport`. `hive-mind_init` calls `init()` here; the consensus /
14
+ * broadcast / shutdown MCP tools delegate to it.
15
+ *
16
+ * Transport selection:
17
+ * - `transport: 'local'` → LocalTransport (in-process registry).
18
+ * Matches the legacy single-process path;
19
+ * real engine, just no WS.
20
+ * - `transport: 'federation'` → FederationTransport over an
21
+ * agentic-flow/transport/loader wire.
22
+ * Real cross-host messaging. Requires
23
+ * `agentic-flow` to be resolvable; if
24
+ * it's not, init returns
25
+ * `transport: 'local'` with `degraded:
26
+ * true` and a fallbackReason — the
27
+ * engine still works locally, the caller
28
+ * just knows the cross-host wire didn't
29
+ * come up.
30
+ * - `transport: 'auto'` (default)→ Probe for `agentic-flow`; if loadable
31
+ * AND `peers` were supplied with
32
+ * addresses, use federation; else local.
33
+ *
34
+ * Why peers are passed in (not auto-discovered): the federation plugin's
35
+ * DiscoveryService is the canonical source of peers, but wiring it into the
36
+ * MCP runtime is a separate concern (it has its own lifecycle / consent
37
+ * gates). Until that lands as G2.3, callers explicitly supply the peer
38
+ * list; the runtime is honest about what it received.
39
+ */
40
+ class HiveConsensusRuntime {
41
+ engine = null;
42
+ transport = null;
43
+ wire = null;
44
+ transportKind = null;
45
+ nodeId = null;
46
+ algorithm = 'raft';
47
+ peers = [];
48
+ degraded = false;
49
+ fallbackReason;
50
+ isInitialized() {
51
+ return this.engine !== null;
52
+ }
53
+ /**
54
+ * Lazy-initialize the consensus engine + transport. Idempotent on the
55
+ * same nodeId/algorithm/transport tuple — subsequent calls return the
56
+ * existing state. Different parameters trigger a clean shutdown + reinit.
57
+ */
58
+ async init(opts) {
59
+ const requestedAlgorithm = opts.algorithm ?? 'raft';
60
+ const requestedTransport = opts.transport ?? 'auto';
61
+ const peers = [...(opts.peers ?? [])];
62
+ // If already initialized with the same shape, return existing state.
63
+ if (this.engine && this.nodeId === opts.nodeId && this.algorithm === requestedAlgorithm) {
64
+ return this.toInitResult();
65
+ }
66
+ // Different shape — tear down before re-instantiating.
67
+ if (this.engine) {
68
+ await this.shutdown();
69
+ }
70
+ const swarm = await import('@claude-flow/swarm');
71
+ // Resolve transport. 'auto' prefers federation when the loader is
72
+ // available AND we have peer addresses; otherwise local.
73
+ let resolvedKind;
74
+ let resolvedTransport;
75
+ let degraded = false;
76
+ let fallbackReason;
77
+ if (requestedTransport === 'local') {
78
+ resolvedKind = 'local';
79
+ resolvedTransport = new swarm.LocalTransport(opts.nodeId, {
80
+ defaultTimeoutMs: opts.timeoutMs,
81
+ });
82
+ }
83
+ else {
84
+ // 'federation' or 'auto' → probe agentic-flow loader.
85
+ const probe = await this.tryLoadFederationWire(opts);
86
+ if (probe.wire) {
87
+ // Build the addressOf map from peers config.
88
+ const addressMap = new Map();
89
+ const pubkeyMap = new Map();
90
+ for (const p of peers) {
91
+ if (p.address)
92
+ addressMap.set(p.nodeId, p.address);
93
+ if (p.publicKeyPem)
94
+ pubkeyMap.set(p.nodeId, p.publicKeyPem);
95
+ }
96
+ this.wire = probe.wire;
97
+ resolvedKind = 'federation';
98
+ resolvedTransport = new swarm.FederationTransport(probe.wire, {
99
+ nodeId: opts.nodeId,
100
+ addressOf: (id) => addressMap.get(id),
101
+ peerIds: () => peers.map(p => p.nodeId),
102
+ defaultTimeoutMs: opts.timeoutMs,
103
+ resolvePeerPublicKey: pubkeyMap.size > 0 ? (id) => pubkeyMap.get(id) : undefined,
104
+ });
105
+ }
106
+ else {
107
+ // Couldn't load federation wire. 'federation' requested → degrade
108
+ // honestly. 'auto' → silently fall back to local.
109
+ if (requestedTransport === 'federation') {
110
+ degraded = true;
111
+ fallbackReason = probe.reason ?? 'agentic-flow loader unavailable';
112
+ }
113
+ resolvedKind = 'local';
114
+ resolvedTransport = new swarm.LocalTransport(opts.nodeId, {
115
+ defaultTimeoutMs: opts.timeoutMs,
116
+ });
117
+ }
118
+ }
119
+ // Build the engine with the resolved transport. We pass `transport`
120
+ // through `config.transport` (typed `unknown` on `ConsensusConfig` in
121
+ // swarm's types.ts — the engine narrows it via a structural check).
122
+ const engine = new swarm.ConsensusEngine(opts.nodeId, {
123
+ algorithm: requestedAlgorithm,
124
+ threshold: opts.threshold ?? 0.66,
125
+ timeoutMs: opts.timeoutMs ?? 30_000,
126
+ maxRounds: 10,
127
+ requireQuorum: true,
128
+ transport: resolvedTransport,
129
+ });
130
+ await engine.initialize();
131
+ // Register peers with the consensus engine so its protocol-internal
132
+ // bookkeeping (Raft's `peers` map, BFT's node set, gossip neighbors)
133
+ // matches the transport's peer list.
134
+ for (const p of peers) {
135
+ if (p.nodeId === opts.nodeId)
136
+ continue;
137
+ engine.addNode(p.nodeId);
138
+ }
139
+ this.engine = engine;
140
+ this.transport = resolvedTransport;
141
+ this.transportKind = resolvedKind;
142
+ this.nodeId = opts.nodeId;
143
+ this.algorithm = requestedAlgorithm;
144
+ this.peers = peers;
145
+ this.degraded = degraded;
146
+ this.fallbackReason = fallbackReason;
147
+ return this.toInitResult();
148
+ }
149
+ /**
150
+ * Probe for the agentic-flow QUIC/WS transport loader. Returns the wire
151
+ * when loadable; an explanatory reason when not. Never throws — failure
152
+ * is data, not control flow, so the runtime can degrade cleanly.
153
+ */
154
+ async tryLoadFederationWire(_opts) {
155
+ // agentic-flow is an *optional* peer dep — when it isn't installed,
156
+ // dynamic import throws and we degrade. The bare-string specifier
157
+ // is wrapped in a `new Function(...)` so the TS compiler doesn't try
158
+ // to resolve types at build time (the same pattern the federation
159
+ // plugin's loader uses, see ADR-120 step 2 comments).
160
+ const importDynamic = new Function('s', 'return import(s)');
161
+ let mod;
162
+ try {
163
+ mod = await importDynamic('agentic-flow/transport/loader');
164
+ }
165
+ catch (err) {
166
+ return { wire: null, reason: `agentic-flow not installed (${err.message ?? 'unknown'})` };
167
+ }
168
+ const m = mod;
169
+ const fn = typeof m.loadQuicTransport === 'function'
170
+ ? m.loadQuicTransport
171
+ : m.default?.loadQuicTransport;
172
+ if (typeof fn !== 'function') {
173
+ return { wire: null, reason: 'agentic-flow loader does not expose loadQuicTransport' };
174
+ }
175
+ try {
176
+ const wire = await fn();
177
+ if (!wire || typeof wire.send !== 'function' || typeof wire.onMessage !== 'function') {
178
+ return { wire: null, reason: 'agentic-flow loader returned a wire without send/onMessage' };
179
+ }
180
+ return { wire };
181
+ }
182
+ catch (err) {
183
+ return { wire: null, reason: `agentic-flow loader threw: ${err.message ?? 'unknown'}` };
184
+ }
185
+ }
186
+ /** Propose a value through the real ConsensusEngine. */
187
+ async propose(value, proposerId) {
188
+ if (!this.engine)
189
+ throw new Error('hive-consensus-runtime: not initialized');
190
+ return this.engine.propose(value, proposerId ?? this.nodeId ?? 'unknown');
191
+ }
192
+ /** Record a vote against an active proposal. */
193
+ async vote(proposalId, vote) {
194
+ if (!this.engine)
195
+ throw new Error('hive-consensus-runtime: not initialized');
196
+ await this.engine.vote(proposalId, vote);
197
+ }
198
+ /**
199
+ * Block until a proposal resolves (accepted / rejected / expired).
200
+ * Used by the MCP tool when callers want a synchronous result instead
201
+ * of polling `proposal-status`.
202
+ */
203
+ async awaitConsensus(proposalId) {
204
+ if (!this.engine)
205
+ throw new Error('hive-consensus-runtime: not initialized');
206
+ return this.engine.awaitConsensus(proposalId);
207
+ }
208
+ /**
209
+ * Broadcast a free-form payload to all known peers via the transport.
210
+ * Gossip-style, no reply expected. Wraps the payload so peers don't
211
+ * confuse it with protocol messages — `type: 'hive-broadcast'`.
212
+ */
213
+ async broadcast(payload, priority = 'normal') {
214
+ if (!this.transport)
215
+ throw new Error('hive-consensus-runtime: not initialized');
216
+ const peers = this.transport.peers();
217
+ await this.transport.broadcast({
218
+ type: 'hive-broadcast',
219
+ payload: { value: payload, priority, sentAt: new Date().toISOString() },
220
+ });
221
+ return { delivered: peers.length, transport: this.transportKind };
222
+ }
223
+ status() {
224
+ if (!this.engine || !this.transport) {
225
+ return {
226
+ initialized: false,
227
+ transport: null,
228
+ degraded: false,
229
+ peers: [],
230
+ engine: null,
231
+ };
232
+ }
233
+ return {
234
+ initialized: true,
235
+ nodeId: this.nodeId,
236
+ algorithm: this.algorithm,
237
+ transport: this.transportKind,
238
+ degraded: this.degraded,
239
+ peers: [...this.transport.peers()],
240
+ engine: this.engine.getStats(),
241
+ };
242
+ }
243
+ async shutdown() {
244
+ const engine = this.engine;
245
+ const transport = this.transport;
246
+ const wire = this.wire;
247
+ this.engine = null;
248
+ this.transport = null;
249
+ this.wire = null;
250
+ this.transportKind = null;
251
+ this.nodeId = null;
252
+ this.peers = [];
253
+ this.degraded = false;
254
+ this.fallbackReason = undefined;
255
+ if (engine) {
256
+ try {
257
+ await engine.shutdown();
258
+ }
259
+ catch { /* best-effort */ }
260
+ }
261
+ if (transport) {
262
+ try {
263
+ await transport.close();
264
+ }
265
+ catch { /* best-effort */ }
266
+ }
267
+ if (wire && typeof wire.close === 'function') {
268
+ try {
269
+ await wire.close();
270
+ }
271
+ catch { /* best-effort */ }
272
+ }
273
+ }
274
+ /** Test seam: replace the singleton's underlying engine + transport. */
275
+ __setForTest(engine, transport, nodeId, kind = 'local') {
276
+ this.engine = engine;
277
+ this.transport = transport;
278
+ this.nodeId = nodeId;
279
+ this.transportKind = kind;
280
+ }
281
+ toInitResult() {
282
+ return {
283
+ initialized: true,
284
+ nodeId: this.nodeId,
285
+ algorithm: this.algorithm,
286
+ transport: this.transportKind,
287
+ degraded: this.degraded,
288
+ fallbackReason: this.fallbackReason,
289
+ peerCount: this.peers.length,
290
+ source: 'engine',
291
+ };
292
+ }
293
+ }
294
+ /** Process-level singleton — one engine per MCP server. */
295
+ export const hiveConsensusRuntime = new HiveConsensusRuntime();
296
+ //# sourceMappingURL=hive-consensus-runtime.js.map
@@ -2,6 +2,13 @@
2
2
  * Hive-Mind MCP Tools for CLI
3
3
  *
4
4
  * Tool definitions for collective intelligence and swarm coordination.
5
+ *
6
+ * ADR-095 G2.2 — these tools now delegate inter-node consensus to the real
7
+ * `@claude-flow/swarm` ConsensusEngine via `hive-consensus-runtime`. The
8
+ * legacy JSON-state-machine voting (in `hive-mind_consensus`'s 'propose' /
9
+ * 'vote' / 'status' / 'list' actions) remains as a fallback when the
10
+ * runtime isn't initialized — preserves backward compatibility for callers
11
+ * that skip `hive-mind_init` or run in a swarm-less context.
5
12
  */
6
13
  import { type MCPTool } from './types.js';
7
14
  export declare const hiveMindTools: MCPTool[];
@@ -2,11 +2,19 @@
2
2
  * Hive-Mind MCP Tools for CLI
3
3
  *
4
4
  * Tool definitions for collective intelligence and swarm coordination.
5
+ *
6
+ * ADR-095 G2.2 — these tools now delegate inter-node consensus to the real
7
+ * `@claude-flow/swarm` ConsensusEngine via `hive-consensus-runtime`. The
8
+ * legacy JSON-state-machine voting (in `hive-mind_consensus`'s 'propose' /
9
+ * 'vote' / 'status' / 'list' actions) remains as a fallback when the
10
+ * runtime isn't initialized — preserves backward compatibility for callers
11
+ * that skip `hive-mind_init` or run in a swarm-less context.
5
12
  */
6
13
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
7
14
  import { join } from 'node:path';
8
15
  import { getProjectCwd } from './types.js';
9
16
  import { validateIdentifier, validateText } from './validate-input.js';
17
+ import { hiveConsensusRuntime } from './hive-consensus-runtime.js';
10
18
  // Storage paths
11
19
  const STORAGE_DIR = '.claude-flow';
12
20
  const HIVE_DIR = 'hive-mind';
@@ -227,6 +235,33 @@ export const hiveMindTools = [
227
235
  description: 'Consensus strategy. Default: raft (anti-drift). Use byzantine for f<n/3 fault tolerance.',
228
236
  },
229
237
  queenId: { type: 'string', description: 'Initial queen agent ID' },
238
+ // ADR-095 G2.2 — pluggable transport for the underlying consensus
239
+ // engine. 'local' (default) preserves single-process behavior;
240
+ // 'federation' wires Raft/PBFT/Gossip over ADR-104 WS via
241
+ // agentic-flow; 'auto' picks federation when reachable + peers
242
+ // supplied, else local.
243
+ transport: {
244
+ type: 'string',
245
+ enum: ['local', 'federation', 'auto'],
246
+ description: 'Consensus transport. local=in-process; federation=ADR-104 WS (cross-host); auto=federation if reachable, else local. Default: auto.',
247
+ },
248
+ // ADR-095 G2.2 — peers for the consensus engine. When transport is
249
+ // 'federation' or 'auto', each peer should include a wire address
250
+ // (e.g. wss://host:8443) and optionally an Ed25519 public key PEM
251
+ // for inbound signature verification.
252
+ peers: {
253
+ type: 'array',
254
+ description: 'Consensus peer list. Each entry: { nodeId, address?, publicKeyPem? }.',
255
+ items: {
256
+ type: 'object',
257
+ properties: {
258
+ nodeId: { type: 'string' },
259
+ address: { type: 'string' },
260
+ publicKeyPem: { type: 'string' },
261
+ },
262
+ required: ['nodeId'],
263
+ },
264
+ },
230
265
  },
231
266
  },
232
267
  handler: async (input) => {
@@ -249,6 +284,39 @@ export const hiveMindTools = [
249
284
  term: 1,
250
285
  };
251
286
  saveHiveState(state);
287
+ // ADR-095 G2.2 — bring up the real consensus runtime so subsequent
288
+ // hive-mind_consensus / hive-mind_broadcast calls go through the
289
+ // engine + transport instead of the JSON state machine. Done after
290
+ // saveHiveState so a runtime failure doesn't leave persisted state
291
+ // inconsistent — we surface the runtime error in the response but
292
+ // the hive itself stays initialized at the file-state layer.
293
+ const requestedTransport = input.transport || 'auto';
294
+ // Map the wider consensus strategy enum to the runtime's algorithm.
295
+ // 'crdt' and 'quorum' don't have engine implementations; they keep
296
+ // the legacy state-machine voting and we tag the runtime as 'raft'
297
+ // (the safe default — quorum semantics are layered on by the
298
+ // state-machine code in hive-mind_consensus below).
299
+ const runtimeAlgorithm = requestedConsensus === 'byzantine' ? 'byzantine'
300
+ : requestedConsensus === 'gossip' ? 'gossip'
301
+ : 'raft';
302
+ const peers = Array.isArray(input.peers)
303
+ ? input.peers.filter((p) => {
304
+ return !!p && typeof p === 'object' && typeof p.nodeId === 'string';
305
+ })
306
+ : [];
307
+ let runtimeResult = null;
308
+ let runtimeError;
309
+ try {
310
+ runtimeResult = await hiveConsensusRuntime.init({
311
+ nodeId: queenId,
312
+ algorithm: runtimeAlgorithm,
313
+ transport: requestedTransport,
314
+ peers,
315
+ });
316
+ }
317
+ catch (err) {
318
+ runtimeError = err instanceof Error ? err.message : String(err);
319
+ }
252
320
  return {
253
321
  success: true,
254
322
  hiveId,
@@ -264,6 +332,22 @@ export const hiveMindTools = [
264
332
  memoryBackend: input.memoryBackend || 'hybrid',
265
333
  },
266
334
  createdAt: state.createdAt,
335
+ // ADR-095 G2.2 — runtime observability so callers know which
336
+ // transport / algorithm is actually live.
337
+ runtime: runtimeResult
338
+ ? {
339
+ engine: 'enabled',
340
+ algorithm: runtimeResult.algorithm,
341
+ transport: runtimeResult.transport,
342
+ degraded: runtimeResult.degraded,
343
+ fallbackReason: runtimeResult.fallbackReason,
344
+ peerCount: runtimeResult.peerCount,
345
+ }
346
+ : {
347
+ engine: 'unavailable',
348
+ error: runtimeError,
349
+ note: 'Consensus engine could not be brought up — legacy state-machine voting will be used for hive-mind_consensus / broadcast.',
350
+ },
267
351
  };
268
352
  },
269
353
  },
@@ -442,7 +526,7 @@ export const hiveMindTools = [
442
526
  inputSchema: {
443
527
  type: 'object',
444
528
  properties: {
445
- action: { type: 'string', enum: ['propose', 'vote', 'status', 'list'], description: 'Consensus action' },
529
+ action: { type: 'string', enum: ['propose', 'vote', 'status', 'list', 'engine-stats'], description: 'Consensus action' },
446
530
  proposalId: { type: 'string', description: 'Proposal ID (for vote/status)' },
447
531
  type: { type: 'string', description: 'Proposal type (for propose)' },
448
532
  value: { description: 'Proposal value (for propose)' },
@@ -452,6 +536,12 @@ export const hiveMindTools = [
452
536
  quorumPreset: { type: 'string', enum: ['unanimous', 'majority', 'supermajority'], description: 'Quorum threshold preset (for quorum strategy, default: majority)' },
453
537
  term: { type: 'number', description: 'Term number (for raft strategy)' },
454
538
  timeoutMs: { type: 'number', description: 'Timeout in ms for raft re-proposal (default: 30000)' },
539
+ // ADR-095 G2.2 — route through the real consensus engine when set.
540
+ // Default false to preserve backward compatibility for callers that
541
+ // depend on the JSON-state-machine semantics (BFT detection across
542
+ // proposals, quorum presets, etc.).
543
+ useEngine: { type: 'boolean', description: 'Route propose/vote through the real ConsensusEngine (ADR-095 G2.2). Requires hive-mind_init to have brought up the runtime.' },
544
+ confidence: { type: 'number', description: 'Vote confidence 0-1 (only used when useEngine is true).' },
455
545
  },
456
546
  required: ['action'],
457
547
  },
@@ -475,6 +565,63 @@ export const hiveMindTools = [
475
565
  const action = input.action;
476
566
  const strategy = input.strategy || 'raft';
477
567
  const totalNodes = state.workers.length || 1;
568
+ // ADR-095 G2.2 — engine-stats surfaces the live runtime state.
569
+ // Returns a 'not-initialized' shape (not an error) when the runtime
570
+ // is offline so monitoring tools don't have to special-case startup.
571
+ if (action === 'engine-stats') {
572
+ const status = hiveConsensusRuntime.status();
573
+ return { action, ...status };
574
+ }
575
+ // ADR-095 G2.2 — when useEngine is true and the runtime is up,
576
+ // propose/vote go through the real ConsensusEngine. Falls back to
577
+ // the JSON-state-machine path below when the runtime isn't
578
+ // initialized, so callers that don't ask for engine routing get
579
+ // the legacy behavior.
580
+ const useEngine = input.useEngine === true && hiveConsensusRuntime.isInitialized();
581
+ if (useEngine && action === 'propose') {
582
+ try {
583
+ const proposal = await hiveConsensusRuntime.propose(input.value, input.voterId || (state.queen?.agentId ?? 'queen'));
584
+ return {
585
+ action,
586
+ proposalId: proposal.id,
587
+ type: input.type || 'general',
588
+ strategy: hiveConsensusRuntime.status().algorithm,
589
+ status: proposal.status,
590
+ term: proposal.term,
591
+ engine: 'enabled',
592
+ transport: hiveConsensusRuntime.status().transport,
593
+ };
594
+ }
595
+ catch (err) {
596
+ return { action, error: `consensus engine propose failed: ${err instanceof Error ? err.message : String(err)}` };
597
+ }
598
+ }
599
+ if (useEngine && action === 'vote') {
600
+ const voterId = input.voterId;
601
+ if (!voterId)
602
+ return { action, error: 'voterId is required for voting' };
603
+ if (!input.proposalId)
604
+ return { action, error: 'proposalId is required for voting' };
605
+ try {
606
+ await hiveConsensusRuntime.vote(input.proposalId, {
607
+ voterId,
608
+ approve: input.vote,
609
+ confidence: typeof input.confidence === 'number' ? input.confidence : 1.0,
610
+ timestamp: new Date(),
611
+ });
612
+ return {
613
+ action,
614
+ proposalId: input.proposalId,
615
+ voterId,
616
+ vote: input.vote,
617
+ engine: 'enabled',
618
+ transport: hiveConsensusRuntime.status().transport,
619
+ };
620
+ }
621
+ catch (err) {
622
+ return { action, error: `consensus engine vote failed: ${err instanceof Error ? err.message : String(err)}` };
623
+ }
624
+ }
478
625
  if (action === 'propose') {
479
626
  const proposalId = `proposal-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
480
627
  const quorumPreset = input.quorumPreset || 'majority';
@@ -750,24 +897,46 @@ export const hiveMindTools = [
750
897
  return { success: false, error: v.error };
751
898
  }
752
899
  const messageId = `msg-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
753
- // Store in shared memory
900
+ const priority = input.priority || 'normal';
901
+ // Store in shared memory (always — this is the durable record).
754
902
  const messages = state.sharedMemory.broadcasts || [];
755
903
  messages.push({
756
904
  messageId,
757
905
  message: input.message,
758
- priority: input.priority || 'normal',
906
+ priority,
759
907
  fromId: input.fromId || 'system',
760
908
  timestamp: new Date().toISOString(),
761
909
  });
762
910
  // Keep only last 100 broadcasts
763
911
  state.sharedMemory.broadcasts = messages.slice(-100);
764
912
  saveHiveState(state);
913
+ // ADR-095 G2.2 — when the consensus runtime is up, also push the
914
+ // broadcast over its transport so cross-process / cross-host
915
+ // peers actually receive it. Local-only runtimes still benefit
916
+ // (in-process peers get a real handler invocation).
917
+ let transportDelivered;
918
+ let transportKind;
919
+ let transportError;
920
+ if (hiveConsensusRuntime.isInitialized()) {
921
+ try {
922
+ const res = await hiveConsensusRuntime.broadcast({ messageId, message: input.message, fromId: input.fromId || 'system' }, priority);
923
+ transportDelivered = res.delivered;
924
+ transportKind = res.transport;
925
+ }
926
+ catch (err) {
927
+ transportError = err instanceof Error ? err.message : String(err);
928
+ }
929
+ }
765
930
  return {
766
931
  success: true,
767
932
  messageId,
768
933
  recipients: state.workers.length,
769
- priority: input.priority || 'normal',
934
+ priority,
770
935
  broadcastAt: new Date().toISOString(),
936
+ transport: transportKind
937
+ ? { kind: transportKind, delivered: transportDelivered }
938
+ : { kind: 'state-machine-only', note: 'runtime offline — broadcast persisted to sharedMemory only' },
939
+ ...(transportError ? { transportError } : {}),
771
940
  };
772
941
  },
773
942
  },
@@ -818,6 +987,17 @@ export const hiveMindTools = [
818
987
  // Keep history for reference
819
988
  state.sharedMemory = {};
820
989
  saveHiveState(state);
990
+ // ADR-095 G2.2 — tear down the consensus runtime alongside the
991
+ // file-state. Best-effort: a runtime that's already offline is
992
+ // not an error.
993
+ let runtimeShutdown = false;
994
+ try {
995
+ if (hiveConsensusRuntime.isInitialized()) {
996
+ await hiveConsensusRuntime.shutdown();
997
+ runtimeShutdown = true;
998
+ }
999
+ }
1000
+ catch { /* best-effort */ }
821
1001
  return {
822
1002
  success: true,
823
1003
  shutdownAt: shutdownTime,
@@ -825,6 +1005,7 @@ export const hiveMindTools = [
825
1005
  workersTerminated: workerCount,
826
1006
  previousQueen,
827
1007
  consensusCleared: pendingConsensus,
1008
+ runtimeShutdown,
828
1009
  message: `Hive-mind shutdown complete. ${workerCount} workers terminated.`,
829
1010
  };
830
1011
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claude-flow/cli",
3
- "version": "3.7.0-alpha.43",
3
+ "version": "3.7.0-alpha.45",
4
4
  "type": "module",
5
5
  "description": "Ruflo CLI - Enterprise AI agent orchestration with 60+ specialized agents, swarm coordination, MCP server, self-learning hooks, and vector memory for Claude Code",
6
6
  "main": "dist/src/index.js",
@@ -105,7 +105,8 @@
105
105
  "yaml": "^2.8.0",
106
106
  "@claude-flow/memory": "^3.0.0-alpha.17",
107
107
  "@claude-flow/embeddings": "^3.0.0-alpha.18",
108
- "@claude-flow/security": "^3.0.0-alpha.8"
108
+ "@claude-flow/security": "^3.0.0-alpha.8",
109
+ "@claude-flow/swarm": "^3.0.0-alpha.8"
109
110
  },
110
111
  "optionalDependencies": {
111
112
  "@claude-flow/aidefence": "^3.0.2",