UNPKG

claude-flow

Version:

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

297 lines (253 loc) 9.15 kB
/** * V3 Consensus Engine Factory * Unified interface for different consensus algorithms */ import { EventEmitter } from 'events'; import { ConsensusAlgorithm, ConsensusConfig, ConsensusProposal, ConsensusVote, ConsensusResult, IConsensusEngine, SWARM_CONSTANTS, } from '../types.js'; import { RaftConsensus, createRaftConsensus, RaftConfig } from './raft.js'; import { ByzantineConsensus, createByzantineConsensus, ByzantineConfig } from './byzantine.js'; import { GossipConsensus, createGossipConsensus, GossipConfig } from './gossip.js'; export { RaftConsensus, ByzantineConsensus, GossipConsensus }; export type { RaftConfig, ByzantineConfig, GossipConfig }; // ADR-095 G2 — pluggable consensus transport. Replaces the implicit // single-process EventEmitter messaging in the consensus protocols. // LocalTransport is the default (matches current behavior); FederationTransport // (separate file, ADR-104 wire) is the multi-host one. export { LocalTransport, LocalTransportRegistry, defaultLocalRegistry, generateNodeKeyPair, signMessage, verifyMessage, canonicalizeForSigning, messageDigest, } from './transport.js'; export type { ConsensusTransport, ConsensusMessage, ConsensusReply, ConsensusMessageHandler, NodeKeyPair, LocalTransportOptions, } from './transport.js'; // ADR-095 G2 — FederationTransport: ConsensusTransport over the federation // plugin's ADR-104 WS wire (agentic-flow/transport/loader). Structural — // swarm doesn't import agentic-flow; the caller passes a transport instance. export { FederationTransport } from './federation-transport.js'; export type { AgenticFlowTransportLike, FederationTransportOptions } from './federation-transport.js'; type ConsensusImplementation = RaftConsensus | ByzantineConsensus | GossipConsensus; export class ConsensusEngine extends EventEmitter implements IConsensusEngine { private config: ConsensusConfig; private nodeId: string; private implementation?: ConsensusImplementation; private proposals: Map<string, ConsensusProposal> = new Map(); constructor(nodeId: string, config: Partial<ConsensusConfig> = {}) { super(); this.nodeId = nodeId; this.config = { algorithm: config.algorithm ?? 'raft', threshold: config.threshold ?? SWARM_CONSTANTS.DEFAULT_CONSENSUS_THRESHOLD, timeoutMs: config.timeoutMs ?? SWARM_CONSTANTS.DEFAULT_CONSENSUS_TIMEOUT_MS, maxRounds: config.maxRounds ?? 10, requireQuorum: config.requireQuorum ?? true, }; } async initialize(config?: ConsensusConfig): Promise<void> { if (config) { this.config = { ...this.config, ...config }; } // Create implementation based on algorithm switch (this.config.algorithm) { case 'raft': this.implementation = createRaftConsensus(this.nodeId, { threshold: this.config.threshold, timeoutMs: this.config.timeoutMs, maxRounds: this.config.maxRounds, requireQuorum: this.config.requireQuorum, }); break; case 'byzantine': this.implementation = createByzantineConsensus(this.nodeId, { threshold: this.config.threshold, timeoutMs: this.config.timeoutMs, maxRounds: this.config.maxRounds, requireQuorum: this.config.requireQuorum, }); break; case 'gossip': this.implementation = createGossipConsensus(this.nodeId, { threshold: this.config.threshold, timeoutMs: this.config.timeoutMs, maxRounds: this.config.maxRounds, requireQuorum: this.config.requireQuorum, }); break; case 'paxos': // Fall back to Raft for Paxos (similar guarantees) this.implementation = createRaftConsensus(this.nodeId, { threshold: this.config.threshold, timeoutMs: this.config.timeoutMs, maxRounds: this.config.maxRounds, requireQuorum: this.config.requireQuorum, }); break; default: throw new Error(`Unknown consensus algorithm: ${this.config.algorithm}`); } await this.implementation.initialize(); // Forward events this.implementation.on('consensus.achieved', (data) => { this.emit('consensus.achieved', data); }); this.implementation.on('leader.elected', (data) => { this.emit('leader.elected', data); }); this.emit('initialized', { nodeId: this.nodeId, algorithm: this.config.algorithm }); } async shutdown(): Promise<void> { if (this.implementation) { await this.implementation.shutdown(); } this.emit('shutdown'); } addNode(nodeId: string, options?: { isPrimary?: boolean }): void { if (!this.implementation) { throw new Error('Consensus engine not initialized'); } if (this.implementation instanceof RaftConsensus) { this.implementation.addPeer(nodeId); } else if (this.implementation instanceof ByzantineConsensus) { this.implementation.addNode(nodeId, options?.isPrimary); } else if (this.implementation instanceof GossipConsensus) { this.implementation.addNode(nodeId); } } removeNode(nodeId: string): void { if (!this.implementation) { return; } if (this.implementation instanceof RaftConsensus) { this.implementation.removePeer(nodeId); } else if (this.implementation instanceof ByzantineConsensus) { this.implementation.removeNode(nodeId); } else if (this.implementation instanceof GossipConsensus) { this.implementation.removeNode(nodeId); } } async propose(value: unknown, proposerId?: string): Promise<ConsensusProposal> { if (!this.implementation) { throw new Error('Consensus engine not initialized'); } const proposal = await this.implementation.propose(value); this.proposals.set(proposal.id, proposal); return proposal; } async vote(proposalId: string, vote: ConsensusVote): Promise<void> { if (!this.implementation) { throw new Error('Consensus engine not initialized'); } await this.implementation.vote(proposalId, vote); } getProposal(proposalId: string): ConsensusProposal | undefined { return this.proposals.get(proposalId); } async awaitConsensus(proposalId: string): Promise<ConsensusResult> { if (!this.implementation) { throw new Error('Consensus engine not initialized'); } return this.implementation.awaitConsensus(proposalId); } getActiveProposals(): ConsensusProposal[] { return Array.from(this.proposals.values()).filter( p => p.status === 'pending' ); } // Algorithm-specific queries isLeader(): boolean { if (this.implementation instanceof RaftConsensus) { return this.implementation.isLeader(); } if (this.implementation instanceof ByzantineConsensus) { return this.implementation.isPrimary(); } return false; // Gossip has no leader } getLeaderId(): string | undefined { if (this.implementation instanceof RaftConsensus) { return this.implementation.getLeaderId(); } return undefined; } getAlgorithm(): ConsensusAlgorithm { return this.config.algorithm; } getConfig(): ConsensusConfig { return { ...this.config }; } // Metrics getStats(): { algorithm: ConsensusAlgorithm; totalProposals: number; pendingProposals: number; acceptedProposals: number; rejectedProposals: number; expiredProposals: number; } { const proposals = Array.from(this.proposals.values()); return { algorithm: this.config.algorithm, totalProposals: proposals.length, pendingProposals: proposals.filter(p => p.status === 'pending').length, acceptedProposals: proposals.filter(p => p.status === 'accepted').length, rejectedProposals: proposals.filter(p => p.status === 'rejected').length, expiredProposals: proposals.filter(p => p.status === 'expired').length, }; } } // Factory function export function createConsensusEngine( nodeId: string, algorithm: ConsensusAlgorithm = 'raft', config?: Partial<ConsensusConfig> ): ConsensusEngine { return new ConsensusEngine(nodeId, { ...config, algorithm }); } // Helper to select optimal algorithm based on requirements export function selectOptimalAlgorithm(requirements: { faultTolerance: 'crash' | 'byzantine'; consistency: 'strong' | 'eventual'; networkScale: 'small' | 'medium' | 'large'; latencyPriority: 'low' | 'medium' | 'high'; }): ConsensusAlgorithm { const { faultTolerance, consistency, networkScale, latencyPriority } = requirements; // Byzantine fault tolerance required if (faultTolerance === 'byzantine') { return 'byzantine'; } // Eventual consistency acceptable and large scale if (consistency === 'eventual' && networkScale === 'large') { return 'gossip'; } // Low latency priority with medium scale if (latencyPriority === 'high' && networkScale !== 'large') { return 'raft'; } // Strong consistency with small/medium scale if (consistency === 'strong') { return 'raft'; } // Default to Raft return 'raft'; }