Merge commit 'd803bfe2b1fe7f5e219e50ac20d6801a0a58ac75' as 'vendor/ruvector'
This commit is contained in:
110
vendor/ruvector/npm/packages/ruvbot/src/swarm/ByzantineConsensus.d.ts
vendored
Normal file
110
vendor/ruvector/npm/packages/ruvbot/src/swarm/ByzantineConsensus.d.ts
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* ByzantineConsensus - Byzantine Fault Tolerant Consensus
|
||||
*
|
||||
* Implements PBFT-style consensus for distributed decision making.
|
||||
* Tolerates f faulty nodes where f < n/3.
|
||||
*/
|
||||
import { EventEmitter } from 'events';
|
||||
export type ConsensusPhase = 'pre-prepare' | 'prepare' | 'commit' | 'decided' | 'failed';
|
||||
export interface ConsensusConfig {
|
||||
replicas: number;
|
||||
timeout: number;
|
||||
retries: number;
|
||||
requireSignatures: boolean;
|
||||
}
|
||||
export interface Proposal<T = unknown> {
|
||||
id: string;
|
||||
value: T;
|
||||
proposerId: string;
|
||||
timestamp: Date;
|
||||
}
|
||||
export interface Vote {
|
||||
proposalId: string;
|
||||
voterId: string;
|
||||
phase: ConsensusPhase;
|
||||
accept: boolean;
|
||||
signature?: string;
|
||||
}
|
||||
export interface ConsensusResult<T = unknown> {
|
||||
proposalId: string;
|
||||
value: T;
|
||||
phase: ConsensusPhase;
|
||||
votes: Vote[];
|
||||
decided: boolean;
|
||||
timestamp: Date;
|
||||
}
|
||||
export interface ReplicaInfo {
|
||||
id: string;
|
||||
isLeader: boolean;
|
||||
lastActivity: Date;
|
||||
status: 'active' | 'suspected' | 'faulty';
|
||||
}
|
||||
export declare class ByzantineConsensus<T = unknown> extends EventEmitter {
|
||||
private readonly config;
|
||||
private readonly replicaId;
|
||||
private replicas;
|
||||
private proposals;
|
||||
private votes;
|
||||
private decided;
|
||||
private leaderId;
|
||||
private viewNumber;
|
||||
constructor(config?: Partial<ConsensusConfig>);
|
||||
/**
|
||||
* Get maximum tolerable faulty nodes
|
||||
*/
|
||||
get maxFaulty(): number;
|
||||
/**
|
||||
* Get quorum size required for consensus
|
||||
*/
|
||||
get quorumSize(): number;
|
||||
/**
|
||||
* Initialize replicas
|
||||
*/
|
||||
initializeReplicas(replicaIds: string[]): void;
|
||||
/**
|
||||
* Propose a value for consensus
|
||||
*/
|
||||
propose(value: T, proposerId?: string): Promise<ConsensusResult<T>>;
|
||||
/**
|
||||
* Vote on a proposal (called by replicas)
|
||||
*/
|
||||
vote(proposalId: string, voterId: string, phase: ConsensusPhase, accept: boolean): void;
|
||||
/**
|
||||
* Get consensus result for a proposal
|
||||
*/
|
||||
getResult(proposalId: string): ConsensusResult<T> | undefined;
|
||||
/**
|
||||
* Get all decided proposals
|
||||
*/
|
||||
getDecided(): ConsensusResult<T>[];
|
||||
/**
|
||||
* Get replica status
|
||||
*/
|
||||
getReplicaStatus(): ReplicaInfo[];
|
||||
/**
|
||||
* Mark a replica as faulty
|
||||
*/
|
||||
markFaulty(replicaId: string): void;
|
||||
/**
|
||||
* Get consensus statistics
|
||||
*/
|
||||
getStats(): {
|
||||
totalReplicas: number;
|
||||
activeReplicas: number;
|
||||
faultyReplicas: number;
|
||||
maxFaulty: number;
|
||||
quorumSize: number;
|
||||
totalProposals: number;
|
||||
decidedProposals: number;
|
||||
viewNumber: number;
|
||||
leaderId: string | null;
|
||||
};
|
||||
private prePrepare;
|
||||
private prepare;
|
||||
private commit;
|
||||
private waitForPhase;
|
||||
private viewChange;
|
||||
}
|
||||
export declare function createByzantineConsensus<T = unknown>(config?: Partial<ConsensusConfig>): ByzantineConsensus<T>;
|
||||
export default ByzantineConsensus;
|
||||
//# sourceMappingURL=ByzantineConsensus.d.ts.map
|
||||
1
vendor/ruvector/npm/packages/ruvbot/src/swarm/ByzantineConsensus.d.ts.map
vendored
Normal file
1
vendor/ruvector/npm/packages/ruvbot/src/swarm/ByzantineConsensus.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ByzantineConsensus.d.ts","sourceRoot":"","sources":["ByzantineConsensus.ts"],"names":[],"mappings":"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"}
|
||||
270
vendor/ruvector/npm/packages/ruvbot/src/swarm/ByzantineConsensus.js
vendored
Normal file
270
vendor/ruvector/npm/packages/ruvbot/src/swarm/ByzantineConsensus.js
vendored
Normal file
@@ -0,0 +1,270 @@
|
||||
"use strict";
|
||||
/**
|
||||
* ByzantineConsensus - Byzantine Fault Tolerant Consensus
|
||||
*
|
||||
* Implements PBFT-style consensus for distributed decision making.
|
||||
* Tolerates f faulty nodes where f < n/3.
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ByzantineConsensus = void 0;
|
||||
exports.createByzantineConsensus = createByzantineConsensus;
|
||||
const uuid_1 = require("uuid");
|
||||
const events_1 = require("events");
|
||||
// ============================================================================
|
||||
// ByzantineConsensus Implementation
|
||||
// ============================================================================
|
||||
class ByzantineConsensus extends events_1.EventEmitter {
|
||||
constructor(config = {}) {
|
||||
super();
|
||||
this.replicas = new Map();
|
||||
this.proposals = new Map();
|
||||
this.votes = new Map();
|
||||
this.decided = new Map();
|
||||
this.leaderId = null;
|
||||
this.viewNumber = 0;
|
||||
this.config = {
|
||||
replicas: config.replicas ?? 5,
|
||||
timeout: config.timeout ?? 30000,
|
||||
retries: config.retries ?? 3,
|
||||
requireSignatures: config.requireSignatures ?? false,
|
||||
};
|
||||
this.replicaId = (0, uuid_1.v4)();
|
||||
// Validate Byzantine tolerance requirement
|
||||
const maxFaulty = Math.floor((this.config.replicas - 1) / 3);
|
||||
if (maxFaulty < 1 && this.config.replicas > 1) {
|
||||
console.warn('ByzantineConsensus: Minimum 4 replicas recommended for fault tolerance');
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get maximum tolerable faulty nodes
|
||||
*/
|
||||
get maxFaulty() {
|
||||
return Math.floor((this.config.replicas - 1) / 3);
|
||||
}
|
||||
/**
|
||||
* Get quorum size required for consensus
|
||||
*/
|
||||
get quorumSize() {
|
||||
return Math.ceil((2 * this.config.replicas) / 3);
|
||||
}
|
||||
/**
|
||||
* Initialize replicas
|
||||
*/
|
||||
initializeReplicas(replicaIds) {
|
||||
this.replicas.clear();
|
||||
for (let i = 0; i < replicaIds.length; i++) {
|
||||
this.replicas.set(replicaIds[i], {
|
||||
id: replicaIds[i],
|
||||
isLeader: i === 0,
|
||||
lastActivity: new Date(),
|
||||
status: 'active',
|
||||
});
|
||||
if (i === 0) {
|
||||
this.leaderId = replicaIds[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Propose a value for consensus
|
||||
*/
|
||||
async propose(value, proposerId) {
|
||||
const proposal = {
|
||||
id: (0, uuid_1.v4)(),
|
||||
value,
|
||||
proposerId: proposerId ?? this.replicaId,
|
||||
timestamp: new Date(),
|
||||
};
|
||||
this.proposals.set(proposal.id, proposal);
|
||||
this.votes.set(proposal.id, []);
|
||||
this.emit('proposal:created', proposal);
|
||||
try {
|
||||
// Phase 1: Pre-prepare
|
||||
await this.prePrepare(proposal);
|
||||
// Phase 2: Prepare
|
||||
await this.prepare(proposal);
|
||||
// Phase 3: Commit
|
||||
const result = await this.commit(proposal);
|
||||
return result;
|
||||
}
|
||||
catch (error) {
|
||||
const failedResult = {
|
||||
proposalId: proposal.id,
|
||||
value,
|
||||
phase: 'failed',
|
||||
votes: this.votes.get(proposal.id) ?? [],
|
||||
decided: false,
|
||||
timestamp: new Date(),
|
||||
};
|
||||
this.emit('consensus:failed', { proposal, error });
|
||||
return failedResult;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Vote on a proposal (called by replicas)
|
||||
*/
|
||||
vote(proposalId, voterId, phase, accept) {
|
||||
const vote = {
|
||||
proposalId,
|
||||
voterId,
|
||||
phase,
|
||||
accept,
|
||||
};
|
||||
let proposalVotes = this.votes.get(proposalId);
|
||||
if (!proposalVotes) {
|
||||
proposalVotes = [];
|
||||
this.votes.set(proposalId, proposalVotes);
|
||||
}
|
||||
proposalVotes.push(vote);
|
||||
// Update replica activity
|
||||
const replica = this.replicas.get(voterId);
|
||||
if (replica) {
|
||||
replica.lastActivity = new Date();
|
||||
}
|
||||
this.emit('vote:received', vote);
|
||||
}
|
||||
/**
|
||||
* Get consensus result for a proposal
|
||||
*/
|
||||
getResult(proposalId) {
|
||||
return this.decided.get(proposalId);
|
||||
}
|
||||
/**
|
||||
* Get all decided proposals
|
||||
*/
|
||||
getDecided() {
|
||||
return Array.from(this.decided.values());
|
||||
}
|
||||
/**
|
||||
* Get replica status
|
||||
*/
|
||||
getReplicaStatus() {
|
||||
return Array.from(this.replicas.values());
|
||||
}
|
||||
/**
|
||||
* Mark a replica as faulty
|
||||
*/
|
||||
markFaulty(replicaId) {
|
||||
const replica = this.replicas.get(replicaId);
|
||||
if (replica) {
|
||||
replica.status = 'faulty';
|
||||
this.emit('replica:faulty', replica);
|
||||
// If leader is faulty, trigger view change
|
||||
if (replica.isLeader) {
|
||||
this.viewChange();
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get consensus statistics
|
||||
*/
|
||||
getStats() {
|
||||
let activeReplicas = 0;
|
||||
let faultyReplicas = 0;
|
||||
for (const replica of this.replicas.values()) {
|
||||
if (replica.status === 'active')
|
||||
activeReplicas++;
|
||||
if (replica.status === 'faulty')
|
||||
faultyReplicas++;
|
||||
}
|
||||
return {
|
||||
totalReplicas: this.replicas.size,
|
||||
activeReplicas,
|
||||
faultyReplicas,
|
||||
maxFaulty: this.maxFaulty,
|
||||
quorumSize: this.quorumSize,
|
||||
totalProposals: this.proposals.size,
|
||||
decidedProposals: this.decided.size,
|
||||
viewNumber: this.viewNumber,
|
||||
leaderId: this.leaderId,
|
||||
};
|
||||
}
|
||||
// ==========================================================================
|
||||
// Private Methods - PBFT Phases
|
||||
// ==========================================================================
|
||||
async prePrepare(proposal) {
|
||||
this.emit('phase:pre-prepare', proposal);
|
||||
// Leader broadcasts pre-prepare
|
||||
// In a real implementation, this would be sent to all replicas
|
||||
// Here we simulate immediate local acceptance
|
||||
// Wait for pre-prepare acceptance (simulated)
|
||||
await this.waitForPhase(proposal.id, 'pre-prepare', 1);
|
||||
}
|
||||
async prepare(proposal) {
|
||||
this.emit('phase:prepare', proposal);
|
||||
// Broadcast prepare message to all replicas
|
||||
// Replicas validate and send prepare messages
|
||||
// Simulate self-vote
|
||||
this.vote(proposal.id, this.replicaId, 'prepare', true);
|
||||
// Wait for quorum of prepare messages
|
||||
await this.waitForPhase(proposal.id, 'prepare', this.quorumSize);
|
||||
}
|
||||
async commit(proposal) {
|
||||
this.emit('phase:commit', proposal);
|
||||
// Broadcast commit message
|
||||
// Simulate self-vote
|
||||
this.vote(proposal.id, this.replicaId, 'commit', true);
|
||||
// Wait for quorum of commit messages
|
||||
await this.waitForPhase(proposal.id, 'commit', this.quorumSize);
|
||||
// Decision reached
|
||||
const result = {
|
||||
proposalId: proposal.id,
|
||||
value: proposal.value,
|
||||
phase: 'decided',
|
||||
votes: this.votes.get(proposal.id) ?? [],
|
||||
decided: true,
|
||||
timestamp: new Date(),
|
||||
};
|
||||
this.decided.set(proposal.id, result);
|
||||
this.emit('consensus:decided', result);
|
||||
return result;
|
||||
}
|
||||
async waitForPhase(proposalId, phase, required) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const timeout = setTimeout(() => {
|
||||
reject(new Error(`Timeout waiting for ${phase} phase`));
|
||||
}, this.config.timeout);
|
||||
const checkVotes = () => {
|
||||
const votes = this.votes.get(proposalId) ?? [];
|
||||
const phaseVotes = votes.filter(v => v.phase === phase && v.accept);
|
||||
if (phaseVotes.length >= required) {
|
||||
clearTimeout(timeout);
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
// Check again
|
||||
setTimeout(checkVotes, 100);
|
||||
};
|
||||
checkVotes();
|
||||
});
|
||||
}
|
||||
viewChange() {
|
||||
this.viewNumber++;
|
||||
// Select new leader (round-robin)
|
||||
const replicaIds = Array.from(this.replicas.keys());
|
||||
const activeReplicas = replicaIds.filter(id => {
|
||||
const replica = this.replicas.get(id);
|
||||
return replica && replica.status === 'active';
|
||||
});
|
||||
if (activeReplicas.length === 0) {
|
||||
this.emit('consensus:no-quorum');
|
||||
return;
|
||||
}
|
||||
const newLeaderIndex = this.viewNumber % activeReplicas.length;
|
||||
const newLeaderId = activeReplicas[newLeaderIndex];
|
||||
// Update leader status
|
||||
for (const [id, replica] of this.replicas) {
|
||||
replica.isLeader = id === newLeaderId;
|
||||
}
|
||||
this.leaderId = newLeaderId;
|
||||
this.emit('view:changed', { viewNumber: this.viewNumber, leaderId: newLeaderId });
|
||||
}
|
||||
}
|
||||
exports.ByzantineConsensus = ByzantineConsensus;
|
||||
// ============================================================================
|
||||
// Factory Function
|
||||
// ============================================================================
|
||||
function createByzantineConsensus(config) {
|
||||
return new ByzantineConsensus(config);
|
||||
}
|
||||
exports.default = ByzantineConsensus;
|
||||
//# sourceMappingURL=ByzantineConsensus.js.map
|
||||
1
vendor/ruvector/npm/packages/ruvbot/src/swarm/ByzantineConsensus.js.map
vendored
Normal file
1
vendor/ruvector/npm/packages/ruvbot/src/swarm/ByzantineConsensus.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
385
vendor/ruvector/npm/packages/ruvbot/src/swarm/ByzantineConsensus.ts
vendored
Normal file
385
vendor/ruvector/npm/packages/ruvbot/src/swarm/ByzantineConsensus.ts
vendored
Normal file
@@ -0,0 +1,385 @@
|
||||
/**
|
||||
* ByzantineConsensus - Byzantine Fault Tolerant Consensus
|
||||
*
|
||||
* Implements PBFT-style consensus for distributed decision making.
|
||||
* Tolerates f faulty nodes where f < n/3.
|
||||
*/
|
||||
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
// ============================================================================
|
||||
// Types
|
||||
// ============================================================================
|
||||
|
||||
export type ConsensusPhase = 'pre-prepare' | 'prepare' | 'commit' | 'decided' | 'failed';
|
||||
|
||||
export interface ConsensusConfig {
|
||||
replicas: number; // Total number of replicas
|
||||
timeout: number; // Timeout for each phase (ms)
|
||||
retries: number; // Number of retries before failing
|
||||
requireSignatures: boolean;
|
||||
}
|
||||
|
||||
export interface Proposal<T = unknown> {
|
||||
id: string;
|
||||
value: T;
|
||||
proposerId: string;
|
||||
timestamp: Date;
|
||||
}
|
||||
|
||||
export interface Vote {
|
||||
proposalId: string;
|
||||
voterId: string;
|
||||
phase: ConsensusPhase;
|
||||
accept: boolean;
|
||||
signature?: string;
|
||||
}
|
||||
|
||||
export interface ConsensusResult<T = unknown> {
|
||||
proposalId: string;
|
||||
value: T;
|
||||
phase: ConsensusPhase;
|
||||
votes: Vote[];
|
||||
decided: boolean;
|
||||
timestamp: Date;
|
||||
}
|
||||
|
||||
export interface ReplicaInfo {
|
||||
id: string;
|
||||
isLeader: boolean;
|
||||
lastActivity: Date;
|
||||
status: 'active' | 'suspected' | 'faulty';
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// ByzantineConsensus Implementation
|
||||
// ============================================================================
|
||||
|
||||
export class ByzantineConsensus<T = unknown> extends EventEmitter {
|
||||
private readonly config: ConsensusConfig;
|
||||
private readonly replicaId: string;
|
||||
private replicas: Map<string, ReplicaInfo> = new Map();
|
||||
private proposals: Map<string, Proposal<T>> = new Map();
|
||||
private votes: Map<string, Vote[]> = new Map();
|
||||
private decided: Map<string, ConsensusResult<T>> = new Map();
|
||||
private leaderId: string | null = null;
|
||||
private viewNumber: number = 0;
|
||||
|
||||
constructor(config: Partial<ConsensusConfig> = {}) {
|
||||
super();
|
||||
|
||||
this.config = {
|
||||
replicas: config.replicas ?? 5,
|
||||
timeout: config.timeout ?? 30000,
|
||||
retries: config.retries ?? 3,
|
||||
requireSignatures: config.requireSignatures ?? false,
|
||||
};
|
||||
|
||||
this.replicaId = uuidv4();
|
||||
|
||||
// Validate Byzantine tolerance requirement
|
||||
const maxFaulty = Math.floor((this.config.replicas - 1) / 3);
|
||||
if (maxFaulty < 1 && this.config.replicas > 1) {
|
||||
console.warn('ByzantineConsensus: Minimum 4 replicas recommended for fault tolerance');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get maximum tolerable faulty nodes
|
||||
*/
|
||||
get maxFaulty(): number {
|
||||
return Math.floor((this.config.replicas - 1) / 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get quorum size required for consensus
|
||||
*/
|
||||
get quorumSize(): number {
|
||||
return Math.ceil((2 * this.config.replicas) / 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize replicas
|
||||
*/
|
||||
initializeReplicas(replicaIds: string[]): void {
|
||||
this.replicas.clear();
|
||||
|
||||
for (let i = 0; i < replicaIds.length; i++) {
|
||||
this.replicas.set(replicaIds[i], {
|
||||
id: replicaIds[i],
|
||||
isLeader: i === 0,
|
||||
lastActivity: new Date(),
|
||||
status: 'active',
|
||||
});
|
||||
|
||||
if (i === 0) {
|
||||
this.leaderId = replicaIds[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Propose a value for consensus
|
||||
*/
|
||||
async propose(value: T, proposerId?: string): Promise<ConsensusResult<T>> {
|
||||
const proposal: Proposal<T> = {
|
||||
id: uuidv4(),
|
||||
value,
|
||||
proposerId: proposerId ?? this.replicaId,
|
||||
timestamp: new Date(),
|
||||
};
|
||||
|
||||
this.proposals.set(proposal.id, proposal);
|
||||
this.votes.set(proposal.id, []);
|
||||
|
||||
this.emit('proposal:created', proposal);
|
||||
|
||||
try {
|
||||
// Phase 1: Pre-prepare
|
||||
await this.prePrepare(proposal);
|
||||
|
||||
// Phase 2: Prepare
|
||||
await this.prepare(proposal);
|
||||
|
||||
// Phase 3: Commit
|
||||
const result = await this.commit(proposal);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
const failedResult: ConsensusResult<T> = {
|
||||
proposalId: proposal.id,
|
||||
value,
|
||||
phase: 'failed',
|
||||
votes: this.votes.get(proposal.id) ?? [],
|
||||
decided: false,
|
||||
timestamp: new Date(),
|
||||
};
|
||||
|
||||
this.emit('consensus:failed', { proposal, error });
|
||||
|
||||
return failedResult;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Vote on a proposal (called by replicas)
|
||||
*/
|
||||
vote(proposalId: string, voterId: string, phase: ConsensusPhase, accept: boolean): void {
|
||||
const vote: Vote = {
|
||||
proposalId,
|
||||
voterId,
|
||||
phase,
|
||||
accept,
|
||||
};
|
||||
|
||||
let proposalVotes = this.votes.get(proposalId);
|
||||
if (!proposalVotes) {
|
||||
proposalVotes = [];
|
||||
this.votes.set(proposalId, proposalVotes);
|
||||
}
|
||||
|
||||
proposalVotes.push(vote);
|
||||
|
||||
// Update replica activity
|
||||
const replica = this.replicas.get(voterId);
|
||||
if (replica) {
|
||||
replica.lastActivity = new Date();
|
||||
}
|
||||
|
||||
this.emit('vote:received', vote);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get consensus result for a proposal
|
||||
*/
|
||||
getResult(proposalId: string): ConsensusResult<T> | undefined {
|
||||
return this.decided.get(proposalId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all decided proposals
|
||||
*/
|
||||
getDecided(): ConsensusResult<T>[] {
|
||||
return Array.from(this.decided.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get replica status
|
||||
*/
|
||||
getReplicaStatus(): ReplicaInfo[] {
|
||||
return Array.from(this.replicas.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a replica as faulty
|
||||
*/
|
||||
markFaulty(replicaId: string): void {
|
||||
const replica = this.replicas.get(replicaId);
|
||||
if (replica) {
|
||||
replica.status = 'faulty';
|
||||
this.emit('replica:faulty', replica);
|
||||
|
||||
// If leader is faulty, trigger view change
|
||||
if (replica.isLeader) {
|
||||
this.viewChange();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get consensus statistics
|
||||
*/
|
||||
getStats(): {
|
||||
totalReplicas: number;
|
||||
activeReplicas: number;
|
||||
faultyReplicas: number;
|
||||
maxFaulty: number;
|
||||
quorumSize: number;
|
||||
totalProposals: number;
|
||||
decidedProposals: number;
|
||||
viewNumber: number;
|
||||
leaderId: string | null;
|
||||
} {
|
||||
let activeReplicas = 0;
|
||||
let faultyReplicas = 0;
|
||||
|
||||
for (const replica of this.replicas.values()) {
|
||||
if (replica.status === 'active') activeReplicas++;
|
||||
if (replica.status === 'faulty') faultyReplicas++;
|
||||
}
|
||||
|
||||
return {
|
||||
totalReplicas: this.replicas.size,
|
||||
activeReplicas,
|
||||
faultyReplicas,
|
||||
maxFaulty: this.maxFaulty,
|
||||
quorumSize: this.quorumSize,
|
||||
totalProposals: this.proposals.size,
|
||||
decidedProposals: this.decided.size,
|
||||
viewNumber: this.viewNumber,
|
||||
leaderId: this.leaderId,
|
||||
};
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
// Private Methods - PBFT Phases
|
||||
// ==========================================================================
|
||||
|
||||
private async prePrepare(proposal: Proposal<T>): Promise<void> {
|
||||
this.emit('phase:pre-prepare', proposal);
|
||||
|
||||
// Leader broadcasts pre-prepare
|
||||
// In a real implementation, this would be sent to all replicas
|
||||
// Here we simulate immediate local acceptance
|
||||
|
||||
// Wait for pre-prepare acceptance (simulated)
|
||||
await this.waitForPhase(proposal.id, 'pre-prepare', 1);
|
||||
}
|
||||
|
||||
private async prepare(proposal: Proposal<T>): Promise<void> {
|
||||
this.emit('phase:prepare', proposal);
|
||||
|
||||
// Broadcast prepare message to all replicas
|
||||
// Replicas validate and send prepare messages
|
||||
|
||||
// Simulate self-vote
|
||||
this.vote(proposal.id, this.replicaId, 'prepare', true);
|
||||
|
||||
// Wait for quorum of prepare messages
|
||||
await this.waitForPhase(proposal.id, 'prepare', this.quorumSize);
|
||||
}
|
||||
|
||||
private async commit(proposal: Proposal<T>): Promise<ConsensusResult<T>> {
|
||||
this.emit('phase:commit', proposal);
|
||||
|
||||
// Broadcast commit message
|
||||
// Simulate self-vote
|
||||
this.vote(proposal.id, this.replicaId, 'commit', true);
|
||||
|
||||
// Wait for quorum of commit messages
|
||||
await this.waitForPhase(proposal.id, 'commit', this.quorumSize);
|
||||
|
||||
// Decision reached
|
||||
const result: ConsensusResult<T> = {
|
||||
proposalId: proposal.id,
|
||||
value: proposal.value,
|
||||
phase: 'decided',
|
||||
votes: this.votes.get(proposal.id) ?? [],
|
||||
decided: true,
|
||||
timestamp: new Date(),
|
||||
};
|
||||
|
||||
this.decided.set(proposal.id, result);
|
||||
this.emit('consensus:decided', result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async waitForPhase(
|
||||
proposalId: string,
|
||||
phase: ConsensusPhase,
|
||||
required: number
|
||||
): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const timeout = setTimeout(() => {
|
||||
reject(new Error(`Timeout waiting for ${phase} phase`));
|
||||
}, this.config.timeout);
|
||||
|
||||
const checkVotes = () => {
|
||||
const votes = this.votes.get(proposalId) ?? [];
|
||||
const phaseVotes = votes.filter(v => v.phase === phase && v.accept);
|
||||
|
||||
if (phaseVotes.length >= required) {
|
||||
clearTimeout(timeout);
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check again
|
||||
setTimeout(checkVotes, 100);
|
||||
};
|
||||
|
||||
checkVotes();
|
||||
});
|
||||
}
|
||||
|
||||
private viewChange(): void {
|
||||
this.viewNumber++;
|
||||
|
||||
// Select new leader (round-robin)
|
||||
const replicaIds = Array.from(this.replicas.keys());
|
||||
const activeReplicas = replicaIds.filter(id => {
|
||||
const replica = this.replicas.get(id);
|
||||
return replica && replica.status === 'active';
|
||||
});
|
||||
|
||||
if (activeReplicas.length === 0) {
|
||||
this.emit('consensus:no-quorum');
|
||||
return;
|
||||
}
|
||||
|
||||
const newLeaderIndex = this.viewNumber % activeReplicas.length;
|
||||
const newLeaderId = activeReplicas[newLeaderIndex];
|
||||
|
||||
// Update leader status
|
||||
for (const [id, replica] of this.replicas) {
|
||||
replica.isLeader = id === newLeaderId;
|
||||
}
|
||||
|
||||
this.leaderId = newLeaderId;
|
||||
this.emit('view:changed', { viewNumber: this.viewNumber, leaderId: newLeaderId });
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Factory Function
|
||||
// ============================================================================
|
||||
|
||||
export function createByzantineConsensus<T = unknown>(
|
||||
config?: Partial<ConsensusConfig>
|
||||
): ByzantineConsensus<T> {
|
||||
return new ByzantineConsensus<T>(config);
|
||||
}
|
||||
|
||||
export default ByzantineConsensus;
|
||||
141
vendor/ruvector/npm/packages/ruvbot/src/swarm/SwarmCoordinator.d.ts
vendored
Normal file
141
vendor/ruvector/npm/packages/ruvbot/src/swarm/SwarmCoordinator.d.ts
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* SwarmCoordinator - Multi-Agent Swarm Orchestration
|
||||
*
|
||||
* Provides distributed task coordination using agentic-flow patterns.
|
||||
* Supports multiple topologies and consensus protocols.
|
||||
*/
|
||||
import { EventEmitter } from 'events';
|
||||
export type SwarmTopology = 'hierarchical' | 'mesh' | 'hierarchical-mesh' | 'adaptive';
|
||||
export type ConsensusProtocol = 'byzantine' | 'raft' | 'gossip' | 'crdt';
|
||||
export type WorkerType = 'ultralearn' | 'optimize' | 'consolidate' | 'predict' | 'audit' | 'map' | 'preload' | 'deepdive' | 'document' | 'refactor' | 'benchmark' | 'testgaps';
|
||||
export type WorkerPriority = 'low' | 'normal' | 'high' | 'critical';
|
||||
export interface SwarmConfig {
|
||||
topology: SwarmTopology;
|
||||
maxAgents: number;
|
||||
strategy: 'specialized' | 'balanced' | 'adaptive';
|
||||
consensus: ConsensusProtocol;
|
||||
heartbeatInterval?: number;
|
||||
taskTimeout?: number;
|
||||
}
|
||||
export interface WorkerConfig {
|
||||
type: WorkerType;
|
||||
priority: WorkerPriority;
|
||||
concurrency: number;
|
||||
timeout: number;
|
||||
retries: number;
|
||||
backoff: 'exponential' | 'linear';
|
||||
}
|
||||
export interface SwarmTask {
|
||||
id: string;
|
||||
worker: WorkerType;
|
||||
type: string;
|
||||
content: unknown;
|
||||
priority: WorkerPriority;
|
||||
status: 'pending' | 'running' | 'completed' | 'failed';
|
||||
assignedAgent?: string;
|
||||
result?: unknown;
|
||||
error?: string;
|
||||
createdAt: Date;
|
||||
startedAt?: Date;
|
||||
completedAt?: Date;
|
||||
}
|
||||
export interface SwarmAgent {
|
||||
id: string;
|
||||
type: WorkerType;
|
||||
status: 'idle' | 'busy' | 'offline';
|
||||
currentTask?: string;
|
||||
completedTasks: number;
|
||||
failedTasks: number;
|
||||
lastHeartbeat: Date;
|
||||
}
|
||||
export interface DispatchOptions {
|
||||
worker: WorkerType;
|
||||
task: {
|
||||
type: string;
|
||||
content: unknown;
|
||||
};
|
||||
priority?: WorkerPriority;
|
||||
timeout?: number;
|
||||
}
|
||||
export declare const WORKER_DEFAULTS: Record<WorkerType, WorkerConfig>;
|
||||
export declare class SwarmCoordinator extends EventEmitter {
|
||||
private readonly config;
|
||||
private agents;
|
||||
private tasks;
|
||||
private taskQueue;
|
||||
private heartbeatTimer?;
|
||||
private started;
|
||||
constructor(config?: Partial<SwarmConfig>);
|
||||
/**
|
||||
* Start the swarm coordinator
|
||||
*/
|
||||
start(): Promise<void>;
|
||||
/**
|
||||
* Stop the swarm coordinator
|
||||
*/
|
||||
stop(): Promise<void>;
|
||||
/**
|
||||
* Spawn a new worker agent
|
||||
*/
|
||||
spawnAgent(type: WorkerType): Promise<SwarmAgent>;
|
||||
/**
|
||||
* Remove an agent
|
||||
*/
|
||||
removeAgent(agentId: string): Promise<boolean>;
|
||||
/**
|
||||
* Dispatch a task to the swarm
|
||||
*/
|
||||
dispatch(options: DispatchOptions): Promise<SwarmTask>;
|
||||
/**
|
||||
* Wait for a task to complete
|
||||
*/
|
||||
waitForTask(taskId: string, timeout?: number): Promise<SwarmTask>;
|
||||
/**
|
||||
* Complete a task (called by worker)
|
||||
*/
|
||||
completeTask(taskId: string, result?: unknown, error?: string): void;
|
||||
/**
|
||||
* Get swarm status
|
||||
*/
|
||||
getStatus(): {
|
||||
topology: SwarmTopology;
|
||||
consensus: ConsensusProtocol;
|
||||
agentCount: number;
|
||||
maxAgents: number;
|
||||
idleAgents: number;
|
||||
busyAgents: number;
|
||||
pendingTasks: number;
|
||||
runningTasks: number;
|
||||
completedTasks: number;
|
||||
failedTasks: number;
|
||||
};
|
||||
/**
|
||||
* Get all agents
|
||||
*/
|
||||
getAgents(): SwarmAgent[];
|
||||
/**
|
||||
* Get agent by ID
|
||||
*/
|
||||
getAgent(agentId: string): SwarmAgent | undefined;
|
||||
/**
|
||||
* Get all tasks
|
||||
*/
|
||||
getTasks(): SwarmTask[];
|
||||
/**
|
||||
* Get task by ID
|
||||
*/
|
||||
getTask(taskId: string): SwarmTask | undefined;
|
||||
/**
|
||||
* Update agent heartbeat
|
||||
*/
|
||||
heartbeat(agentId: string): void;
|
||||
private enqueueTask;
|
||||
private dequeueTask;
|
||||
private processQueue;
|
||||
private findTaskForAgent;
|
||||
private assignTask;
|
||||
private checkHeartbeats;
|
||||
}
|
||||
export declare function createSwarmCoordinator(config?: Partial<SwarmConfig>): SwarmCoordinator;
|
||||
export default SwarmCoordinator;
|
||||
//# sourceMappingURL=SwarmCoordinator.d.ts.map
|
||||
1
vendor/ruvector/npm/packages/ruvbot/src/swarm/SwarmCoordinator.d.ts.map
vendored
Normal file
1
vendor/ruvector/npm/packages/ruvbot/src/swarm/SwarmCoordinator.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"SwarmCoordinator.d.ts","sourceRoot":"","sources":["SwarmCoordinator.ts"],"names":[],"mappings":"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"}
|
||||
384
vendor/ruvector/npm/packages/ruvbot/src/swarm/SwarmCoordinator.js
vendored
Normal file
384
vendor/ruvector/npm/packages/ruvbot/src/swarm/SwarmCoordinator.js
vendored
Normal file
@@ -0,0 +1,384 @@
|
||||
"use strict";
|
||||
/**
|
||||
* SwarmCoordinator - Multi-Agent Swarm Orchestration
|
||||
*
|
||||
* Provides distributed task coordination using agentic-flow patterns.
|
||||
* Supports multiple topologies and consensus protocols.
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.SwarmCoordinator = exports.WORKER_DEFAULTS = void 0;
|
||||
exports.createSwarmCoordinator = createSwarmCoordinator;
|
||||
const uuid_1 = require("uuid");
|
||||
const events_1 = require("events");
|
||||
// ============================================================================
|
||||
// Default Worker Configurations
|
||||
// ============================================================================
|
||||
exports.WORKER_DEFAULTS = {
|
||||
ultralearn: { type: 'ultralearn', priority: 'normal', concurrency: 2, timeout: 60000, retries: 3, backoff: 'exponential' },
|
||||
optimize: { type: 'optimize', priority: 'high', concurrency: 4, timeout: 30000, retries: 2, backoff: 'exponential' },
|
||||
consolidate: { type: 'consolidate', priority: 'low', concurrency: 1, timeout: 120000, retries: 1, backoff: 'linear' },
|
||||
predict: { type: 'predict', priority: 'normal', concurrency: 2, timeout: 15000, retries: 2, backoff: 'exponential' },
|
||||
audit: { type: 'audit', priority: 'critical', concurrency: 1, timeout: 45000, retries: 3, backoff: 'exponential' },
|
||||
map: { type: 'map', priority: 'normal', concurrency: 2, timeout: 60000, retries: 2, backoff: 'linear' },
|
||||
preload: { type: 'preload', priority: 'low', concurrency: 4, timeout: 10000, retries: 1, backoff: 'linear' },
|
||||
deepdive: { type: 'deepdive', priority: 'normal', concurrency: 2, timeout: 90000, retries: 2, backoff: 'exponential' },
|
||||
document: { type: 'document', priority: 'normal', concurrency: 2, timeout: 30000, retries: 2, backoff: 'linear' },
|
||||
refactor: { type: 'refactor', priority: 'normal', concurrency: 2, timeout: 60000, retries: 2, backoff: 'exponential' },
|
||||
benchmark: { type: 'benchmark', priority: 'normal', concurrency: 1, timeout: 120000, retries: 1, backoff: 'linear' },
|
||||
testgaps: { type: 'testgaps', priority: 'normal', concurrency: 2, timeout: 45000, retries: 2, backoff: 'linear' },
|
||||
};
|
||||
// ============================================================================
|
||||
// SwarmCoordinator Implementation
|
||||
// ============================================================================
|
||||
class SwarmCoordinator extends events_1.EventEmitter {
|
||||
constructor(config = {}) {
|
||||
super();
|
||||
this.agents = new Map();
|
||||
this.tasks = new Map();
|
||||
this.taskQueue = new Map();
|
||||
this.started = false;
|
||||
this.config = {
|
||||
topology: config.topology ?? 'hierarchical',
|
||||
maxAgents: config.maxAgents ?? 8,
|
||||
strategy: config.strategy ?? 'specialized',
|
||||
consensus: config.consensus ?? 'raft',
|
||||
heartbeatInterval: config.heartbeatInterval ?? 5000,
|
||||
taskTimeout: config.taskTimeout ?? 60000,
|
||||
};
|
||||
// Initialize priority queues
|
||||
this.taskQueue.set('critical', []);
|
||||
this.taskQueue.set('high', []);
|
||||
this.taskQueue.set('normal', []);
|
||||
this.taskQueue.set('low', []);
|
||||
}
|
||||
/**
|
||||
* Start the swarm coordinator
|
||||
*/
|
||||
async start() {
|
||||
if (this.started)
|
||||
return;
|
||||
// Start heartbeat monitoring
|
||||
this.heartbeatTimer = setInterval(() => this.checkHeartbeats(), this.config.heartbeatInterval);
|
||||
this.started = true;
|
||||
this.emit('started');
|
||||
}
|
||||
/**
|
||||
* Stop the swarm coordinator
|
||||
*/
|
||||
async stop() {
|
||||
if (!this.started)
|
||||
return;
|
||||
if (this.heartbeatTimer) {
|
||||
clearInterval(this.heartbeatTimer);
|
||||
this.heartbeatTimer = undefined;
|
||||
}
|
||||
// Mark all agents as offline
|
||||
for (const agent of this.agents.values()) {
|
||||
agent.status = 'offline';
|
||||
}
|
||||
this.started = false;
|
||||
this.emit('stopped');
|
||||
}
|
||||
/**
|
||||
* Spawn a new worker agent
|
||||
*/
|
||||
async spawnAgent(type) {
|
||||
if (this.agents.size >= this.config.maxAgents) {
|
||||
throw new Error(`Max agents (${this.config.maxAgents}) reached`);
|
||||
}
|
||||
const agent = {
|
||||
id: (0, uuid_1.v4)(),
|
||||
type,
|
||||
status: 'idle',
|
||||
completedTasks: 0,
|
||||
failedTasks: 0,
|
||||
lastHeartbeat: new Date(),
|
||||
};
|
||||
this.agents.set(agent.id, agent);
|
||||
this.emit('agent:spawned', agent);
|
||||
// Try to assign pending tasks
|
||||
await this.processQueue();
|
||||
return agent;
|
||||
}
|
||||
/**
|
||||
* Remove an agent
|
||||
*/
|
||||
async removeAgent(agentId) {
|
||||
const agent = this.agents.get(agentId);
|
||||
if (!agent)
|
||||
return false;
|
||||
// If agent has a current task, re-queue it
|
||||
if (agent.currentTask) {
|
||||
const task = this.tasks.get(agent.currentTask);
|
||||
if (task && task.status === 'running') {
|
||||
task.status = 'pending';
|
||||
task.assignedAgent = undefined;
|
||||
this.enqueueTask(task);
|
||||
}
|
||||
}
|
||||
this.agents.delete(agentId);
|
||||
this.emit('agent:removed', agent);
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Dispatch a task to the swarm
|
||||
*/
|
||||
async dispatch(options) {
|
||||
const workerConfig = exports.WORKER_DEFAULTS[options.worker];
|
||||
const priority = options.priority ?? workerConfig.priority;
|
||||
const task = {
|
||||
id: (0, uuid_1.v4)(),
|
||||
worker: options.worker,
|
||||
type: options.task.type,
|
||||
content: options.task.content,
|
||||
priority,
|
||||
status: 'pending',
|
||||
createdAt: new Date(),
|
||||
};
|
||||
this.tasks.set(task.id, task);
|
||||
this.enqueueTask(task);
|
||||
this.emit('task:created', task);
|
||||
// Try to assign immediately
|
||||
await this.processQueue();
|
||||
return task;
|
||||
}
|
||||
/**
|
||||
* Wait for a task to complete
|
||||
*/
|
||||
async waitForTask(taskId, timeout) {
|
||||
const effectiveTimeout = timeout ?? this.config.taskTimeout;
|
||||
return new Promise((resolve, reject) => {
|
||||
const timer = setTimeout(() => {
|
||||
reject(new Error(`Task ${taskId} timed out`));
|
||||
}, effectiveTimeout);
|
||||
const checkTask = () => {
|
||||
const task = this.tasks.get(taskId);
|
||||
if (!task) {
|
||||
clearTimeout(timer);
|
||||
reject(new Error(`Task ${taskId} not found`));
|
||||
return;
|
||||
}
|
||||
if (task.status === 'completed' || task.status === 'failed') {
|
||||
clearTimeout(timer);
|
||||
resolve(task);
|
||||
return;
|
||||
}
|
||||
// Check again soon
|
||||
setTimeout(checkTask, 100);
|
||||
};
|
||||
checkTask();
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Complete a task (called by worker)
|
||||
*/
|
||||
completeTask(taskId, result, error) {
|
||||
const task = this.tasks.get(taskId);
|
||||
if (!task)
|
||||
return;
|
||||
task.completedAt = new Date();
|
||||
if (error) {
|
||||
task.status = 'failed';
|
||||
task.error = error;
|
||||
}
|
||||
else {
|
||||
task.status = 'completed';
|
||||
task.result = result;
|
||||
}
|
||||
// Update agent stats
|
||||
if (task.assignedAgent) {
|
||||
const agent = this.agents.get(task.assignedAgent);
|
||||
if (agent) {
|
||||
agent.status = 'idle';
|
||||
agent.currentTask = undefined;
|
||||
if (error) {
|
||||
agent.failedTasks++;
|
||||
}
|
||||
else {
|
||||
agent.completedTasks++;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.emit(error ? 'task:failed' : 'task:completed', task);
|
||||
// Process queue for next task
|
||||
this.processQueue();
|
||||
}
|
||||
/**
|
||||
* Get swarm status
|
||||
*/
|
||||
getStatus() {
|
||||
let idleAgents = 0;
|
||||
let busyAgents = 0;
|
||||
let pendingTasks = 0;
|
||||
let runningTasks = 0;
|
||||
let completedTasks = 0;
|
||||
let failedTasks = 0;
|
||||
for (const agent of this.agents.values()) {
|
||||
if (agent.status === 'idle')
|
||||
idleAgents++;
|
||||
if (agent.status === 'busy')
|
||||
busyAgents++;
|
||||
}
|
||||
for (const task of this.tasks.values()) {
|
||||
switch (task.status) {
|
||||
case 'pending':
|
||||
pendingTasks++;
|
||||
break;
|
||||
case 'running':
|
||||
runningTasks++;
|
||||
break;
|
||||
case 'completed':
|
||||
completedTasks++;
|
||||
break;
|
||||
case 'failed':
|
||||
failedTasks++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return {
|
||||
topology: this.config.topology,
|
||||
consensus: this.config.consensus,
|
||||
agentCount: this.agents.size,
|
||||
maxAgents: this.config.maxAgents,
|
||||
idleAgents,
|
||||
busyAgents,
|
||||
pendingTasks,
|
||||
runningTasks,
|
||||
completedTasks,
|
||||
failedTasks,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Get all agents
|
||||
*/
|
||||
getAgents() {
|
||||
return Array.from(this.agents.values());
|
||||
}
|
||||
/**
|
||||
* Get agent by ID
|
||||
*/
|
||||
getAgent(agentId) {
|
||||
return this.agents.get(agentId);
|
||||
}
|
||||
/**
|
||||
* Get all tasks
|
||||
*/
|
||||
getTasks() {
|
||||
return Array.from(this.tasks.values());
|
||||
}
|
||||
/**
|
||||
* Get task by ID
|
||||
*/
|
||||
getTask(taskId) {
|
||||
return this.tasks.get(taskId);
|
||||
}
|
||||
/**
|
||||
* Update agent heartbeat
|
||||
*/
|
||||
heartbeat(agentId) {
|
||||
const agent = this.agents.get(agentId);
|
||||
if (agent) {
|
||||
agent.lastHeartbeat = new Date();
|
||||
}
|
||||
}
|
||||
// ==========================================================================
|
||||
// Private Methods
|
||||
// ==========================================================================
|
||||
enqueueTask(task) {
|
||||
const queue = this.taskQueue.get(task.priority);
|
||||
if (queue) {
|
||||
queue.push(task);
|
||||
}
|
||||
}
|
||||
dequeueTask() {
|
||||
// Priority order: critical > high > normal > low
|
||||
const priorities = ['critical', 'high', 'normal', 'low'];
|
||||
for (const priority of priorities) {
|
||||
const queue = this.taskQueue.get(priority);
|
||||
if (queue && queue.length > 0) {
|
||||
return queue.shift();
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
async processQueue() {
|
||||
// Find idle agents matching pending tasks
|
||||
for (const agent of this.agents.values()) {
|
||||
if (agent.status !== 'idle')
|
||||
continue;
|
||||
// Find a matching task
|
||||
const task = this.findTaskForAgent(agent);
|
||||
if (!task)
|
||||
continue;
|
||||
// Assign task
|
||||
await this.assignTask(task, agent);
|
||||
}
|
||||
}
|
||||
findTaskForAgent(agent) {
|
||||
const priorities = ['critical', 'high', 'normal', 'low'];
|
||||
for (const priority of priorities) {
|
||||
const queue = this.taskQueue.get(priority);
|
||||
if (!queue)
|
||||
continue;
|
||||
// Find task matching agent type (for specialized strategy)
|
||||
// or any task (for balanced strategy)
|
||||
const taskIndex = queue.findIndex(task => {
|
||||
if (this.config.strategy === 'specialized') {
|
||||
return task.worker === agent.type;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (taskIndex >= 0) {
|
||||
return queue.splice(taskIndex, 1)[0];
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
async assignTask(task, agent) {
|
||||
task.status = 'running';
|
||||
task.assignedAgent = agent.id;
|
||||
task.startedAt = new Date();
|
||||
agent.status = 'busy';
|
||||
agent.currentTask = task.id;
|
||||
this.emit('task:assigned', { task, agent });
|
||||
// Set timeout for task
|
||||
const workerConfig = exports.WORKER_DEFAULTS[task.worker];
|
||||
setTimeout(() => {
|
||||
const currentTask = this.tasks.get(task.id);
|
||||
if (currentTask && currentTask.status === 'running') {
|
||||
this.completeTask(task.id, undefined, 'Task timed out');
|
||||
}
|
||||
}, workerConfig.timeout);
|
||||
}
|
||||
checkHeartbeats() {
|
||||
const now = Date.now();
|
||||
const threshold = this.config.heartbeatInterval * 3; // 3 missed heartbeats
|
||||
for (const agent of this.agents.values()) {
|
||||
if (agent.status === 'offline')
|
||||
continue;
|
||||
const lastHeartbeat = agent.lastHeartbeat.getTime();
|
||||
if (now - lastHeartbeat > threshold) {
|
||||
agent.status = 'offline';
|
||||
this.emit('agent:offline', agent);
|
||||
// Re-queue any running task
|
||||
if (agent.currentTask) {
|
||||
const task = this.tasks.get(agent.currentTask);
|
||||
if (task && task.status === 'running') {
|
||||
task.status = 'pending';
|
||||
task.assignedAgent = undefined;
|
||||
this.enqueueTask(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.SwarmCoordinator = SwarmCoordinator;
|
||||
// ============================================================================
|
||||
// Factory Function
|
||||
// ============================================================================
|
||||
function createSwarmCoordinator(config) {
|
||||
return new SwarmCoordinator(config);
|
||||
}
|
||||
exports.default = SwarmCoordinator;
|
||||
//# sourceMappingURL=SwarmCoordinator.js.map
|
||||
1
vendor/ruvector/npm/packages/ruvbot/src/swarm/SwarmCoordinator.js.map
vendored
Normal file
1
vendor/ruvector/npm/packages/ruvbot/src/swarm/SwarmCoordinator.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
534
vendor/ruvector/npm/packages/ruvbot/src/swarm/SwarmCoordinator.ts
vendored
Normal file
534
vendor/ruvector/npm/packages/ruvbot/src/swarm/SwarmCoordinator.ts
vendored
Normal file
@@ -0,0 +1,534 @@
|
||||
/**
|
||||
* SwarmCoordinator - Multi-Agent Swarm Orchestration
|
||||
*
|
||||
* Provides distributed task coordination using agentic-flow patterns.
|
||||
* Supports multiple topologies and consensus protocols.
|
||||
*/
|
||||
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
// ============================================================================
|
||||
// Types
|
||||
// ============================================================================
|
||||
|
||||
export type SwarmTopology =
|
||||
| 'hierarchical' // Queen-worker (anti-drift)
|
||||
| 'mesh' // Peer-to-peer network
|
||||
| 'hierarchical-mesh' // Hybrid for scalability
|
||||
| 'adaptive'; // Dynamic switching
|
||||
|
||||
export type ConsensusProtocol =
|
||||
| 'byzantine' // BFT (f < n/3 faulty)
|
||||
| 'raft' // Leader-based (f < n/2)
|
||||
| 'gossip' // Eventually consistent
|
||||
| 'crdt'; // Conflict-free replication
|
||||
|
||||
export type WorkerType =
|
||||
| 'ultralearn' // Deep knowledge acquisition
|
||||
| 'optimize' // Performance optimization
|
||||
| 'consolidate' // Memory consolidation (EWC++)
|
||||
| 'predict' // Predictive preloading
|
||||
| 'audit' // Security analysis
|
||||
| 'map' // Codebase mapping
|
||||
| 'preload' // Resource preloading
|
||||
| 'deepdive' // Deep code analysis
|
||||
| 'document' // Auto-documentation
|
||||
| 'refactor' // Refactoring suggestions
|
||||
| 'benchmark' // Performance benchmarking
|
||||
| 'testgaps'; // Test coverage analysis
|
||||
|
||||
export type WorkerPriority = 'low' | 'normal' | 'high' | 'critical';
|
||||
|
||||
export interface SwarmConfig {
|
||||
topology: SwarmTopology;
|
||||
maxAgents: number;
|
||||
strategy: 'specialized' | 'balanced' | 'adaptive';
|
||||
consensus: ConsensusProtocol;
|
||||
heartbeatInterval?: number;
|
||||
taskTimeout?: number;
|
||||
}
|
||||
|
||||
export interface WorkerConfig {
|
||||
type: WorkerType;
|
||||
priority: WorkerPriority;
|
||||
concurrency: number;
|
||||
timeout: number;
|
||||
retries: number;
|
||||
backoff: 'exponential' | 'linear';
|
||||
}
|
||||
|
||||
export interface SwarmTask {
|
||||
id: string;
|
||||
worker: WorkerType;
|
||||
type: string;
|
||||
content: unknown;
|
||||
priority: WorkerPriority;
|
||||
status: 'pending' | 'running' | 'completed' | 'failed';
|
||||
assignedAgent?: string;
|
||||
result?: unknown;
|
||||
error?: string;
|
||||
createdAt: Date;
|
||||
startedAt?: Date;
|
||||
completedAt?: Date;
|
||||
}
|
||||
|
||||
export interface SwarmAgent {
|
||||
id: string;
|
||||
type: WorkerType;
|
||||
status: 'idle' | 'busy' | 'offline';
|
||||
currentTask?: string;
|
||||
completedTasks: number;
|
||||
failedTasks: number;
|
||||
lastHeartbeat: Date;
|
||||
}
|
||||
|
||||
export interface DispatchOptions {
|
||||
worker: WorkerType;
|
||||
task: {
|
||||
type: string;
|
||||
content: unknown;
|
||||
};
|
||||
priority?: WorkerPriority;
|
||||
timeout?: number;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Default Worker Configurations
|
||||
// ============================================================================
|
||||
|
||||
export const WORKER_DEFAULTS: Record<WorkerType, WorkerConfig> = {
|
||||
ultralearn: { type: 'ultralearn', priority: 'normal', concurrency: 2, timeout: 60000, retries: 3, backoff: 'exponential' },
|
||||
optimize: { type: 'optimize', priority: 'high', concurrency: 4, timeout: 30000, retries: 2, backoff: 'exponential' },
|
||||
consolidate: { type: 'consolidate', priority: 'low', concurrency: 1, timeout: 120000, retries: 1, backoff: 'linear' },
|
||||
predict: { type: 'predict', priority: 'normal', concurrency: 2, timeout: 15000, retries: 2, backoff: 'exponential' },
|
||||
audit: { type: 'audit', priority: 'critical', concurrency: 1, timeout: 45000, retries: 3, backoff: 'exponential' },
|
||||
map: { type: 'map', priority: 'normal', concurrency: 2, timeout: 60000, retries: 2, backoff: 'linear' },
|
||||
preload: { type: 'preload', priority: 'low', concurrency: 4, timeout: 10000, retries: 1, backoff: 'linear' },
|
||||
deepdive: { type: 'deepdive', priority: 'normal', concurrency: 2, timeout: 90000, retries: 2, backoff: 'exponential' },
|
||||
document: { type: 'document', priority: 'normal', concurrency: 2, timeout: 30000, retries: 2, backoff: 'linear' },
|
||||
refactor: { type: 'refactor', priority: 'normal', concurrency: 2, timeout: 60000, retries: 2, backoff: 'exponential' },
|
||||
benchmark: { type: 'benchmark', priority: 'normal', concurrency: 1, timeout: 120000, retries: 1, backoff: 'linear' },
|
||||
testgaps: { type: 'testgaps', priority: 'normal', concurrency: 2, timeout: 45000, retries: 2, backoff: 'linear' },
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// SwarmCoordinator Implementation
|
||||
// ============================================================================
|
||||
|
||||
export class SwarmCoordinator extends EventEmitter {
|
||||
private readonly config: SwarmConfig;
|
||||
private agents: Map<string, SwarmAgent> = new Map();
|
||||
private tasks: Map<string, SwarmTask> = new Map();
|
||||
private taskQueue: Map<WorkerPriority, SwarmTask[]> = new Map();
|
||||
private heartbeatTimer?: ReturnType<typeof setInterval>;
|
||||
private started: boolean = false;
|
||||
|
||||
constructor(config: Partial<SwarmConfig> = {}) {
|
||||
super();
|
||||
|
||||
this.config = {
|
||||
topology: config.topology ?? 'hierarchical',
|
||||
maxAgents: config.maxAgents ?? 8,
|
||||
strategy: config.strategy ?? 'specialized',
|
||||
consensus: config.consensus ?? 'raft',
|
||||
heartbeatInterval: config.heartbeatInterval ?? 5000,
|
||||
taskTimeout: config.taskTimeout ?? 60000,
|
||||
};
|
||||
|
||||
// Initialize priority queues
|
||||
this.taskQueue.set('critical', []);
|
||||
this.taskQueue.set('high', []);
|
||||
this.taskQueue.set('normal', []);
|
||||
this.taskQueue.set('low', []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the swarm coordinator
|
||||
*/
|
||||
async start(): Promise<void> {
|
||||
if (this.started) return;
|
||||
|
||||
// Start heartbeat monitoring
|
||||
this.heartbeatTimer = setInterval(
|
||||
() => this.checkHeartbeats(),
|
||||
this.config.heartbeatInterval
|
||||
);
|
||||
|
||||
this.started = true;
|
||||
this.emit('started');
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the swarm coordinator
|
||||
*/
|
||||
async stop(): Promise<void> {
|
||||
if (!this.started) return;
|
||||
|
||||
if (this.heartbeatTimer) {
|
||||
clearInterval(this.heartbeatTimer);
|
||||
this.heartbeatTimer = undefined;
|
||||
}
|
||||
|
||||
// Mark all agents as offline
|
||||
for (const agent of this.agents.values()) {
|
||||
agent.status = 'offline';
|
||||
}
|
||||
|
||||
this.started = false;
|
||||
this.emit('stopped');
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawn a new worker agent
|
||||
*/
|
||||
async spawnAgent(type: WorkerType): Promise<SwarmAgent> {
|
||||
if (this.agents.size >= this.config.maxAgents) {
|
||||
throw new Error(`Max agents (${this.config.maxAgents}) reached`);
|
||||
}
|
||||
|
||||
const agent: SwarmAgent = {
|
||||
id: uuidv4(),
|
||||
type,
|
||||
status: 'idle',
|
||||
completedTasks: 0,
|
||||
failedTasks: 0,
|
||||
lastHeartbeat: new Date(),
|
||||
};
|
||||
|
||||
this.agents.set(agent.id, agent);
|
||||
this.emit('agent:spawned', agent);
|
||||
|
||||
// Try to assign pending tasks
|
||||
await this.processQueue();
|
||||
|
||||
return agent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an agent
|
||||
*/
|
||||
async removeAgent(agentId: string): Promise<boolean> {
|
||||
const agent = this.agents.get(agentId);
|
||||
if (!agent) return false;
|
||||
|
||||
// If agent has a current task, re-queue it
|
||||
if (agent.currentTask) {
|
||||
const task = this.tasks.get(agent.currentTask);
|
||||
if (task && task.status === 'running') {
|
||||
task.status = 'pending';
|
||||
task.assignedAgent = undefined;
|
||||
this.enqueueTask(task);
|
||||
}
|
||||
}
|
||||
|
||||
this.agents.delete(agentId);
|
||||
this.emit('agent:removed', agent);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch a task to the swarm
|
||||
*/
|
||||
async dispatch(options: DispatchOptions): Promise<SwarmTask> {
|
||||
const workerConfig = WORKER_DEFAULTS[options.worker];
|
||||
const priority = options.priority ?? workerConfig.priority;
|
||||
|
||||
const task: SwarmTask = {
|
||||
id: uuidv4(),
|
||||
worker: options.worker,
|
||||
type: options.task.type,
|
||||
content: options.task.content,
|
||||
priority,
|
||||
status: 'pending',
|
||||
createdAt: new Date(),
|
||||
};
|
||||
|
||||
this.tasks.set(task.id, task);
|
||||
this.enqueueTask(task);
|
||||
|
||||
this.emit('task:created', task);
|
||||
|
||||
// Try to assign immediately
|
||||
await this.processQueue();
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for a task to complete
|
||||
*/
|
||||
async waitForTask(taskId: string, timeout?: number): Promise<SwarmTask> {
|
||||
const effectiveTimeout = timeout ?? this.config.taskTimeout;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const timer = setTimeout(() => {
|
||||
reject(new Error(`Task ${taskId} timed out`));
|
||||
}, effectiveTimeout);
|
||||
|
||||
const checkTask = () => {
|
||||
const task = this.tasks.get(taskId);
|
||||
if (!task) {
|
||||
clearTimeout(timer);
|
||||
reject(new Error(`Task ${taskId} not found`));
|
||||
return;
|
||||
}
|
||||
|
||||
if (task.status === 'completed' || task.status === 'failed') {
|
||||
clearTimeout(timer);
|
||||
resolve(task);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check again soon
|
||||
setTimeout(checkTask, 100);
|
||||
};
|
||||
|
||||
checkTask();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete a task (called by worker)
|
||||
*/
|
||||
completeTask(taskId: string, result?: unknown, error?: string): void {
|
||||
const task = this.tasks.get(taskId);
|
||||
if (!task) return;
|
||||
|
||||
task.completedAt = new Date();
|
||||
|
||||
if (error) {
|
||||
task.status = 'failed';
|
||||
task.error = error;
|
||||
} else {
|
||||
task.status = 'completed';
|
||||
task.result = result;
|
||||
}
|
||||
|
||||
// Update agent stats
|
||||
if (task.assignedAgent) {
|
||||
const agent = this.agents.get(task.assignedAgent);
|
||||
if (agent) {
|
||||
agent.status = 'idle';
|
||||
agent.currentTask = undefined;
|
||||
if (error) {
|
||||
agent.failedTasks++;
|
||||
} else {
|
||||
agent.completedTasks++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.emit(error ? 'task:failed' : 'task:completed', task);
|
||||
|
||||
// Process queue for next task
|
||||
this.processQueue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get swarm status
|
||||
*/
|
||||
getStatus(): {
|
||||
topology: SwarmTopology;
|
||||
consensus: ConsensusProtocol;
|
||||
agentCount: number;
|
||||
maxAgents: number;
|
||||
idleAgents: number;
|
||||
busyAgents: number;
|
||||
pendingTasks: number;
|
||||
runningTasks: number;
|
||||
completedTasks: number;
|
||||
failedTasks: number;
|
||||
} {
|
||||
let idleAgents = 0;
|
||||
let busyAgents = 0;
|
||||
let pendingTasks = 0;
|
||||
let runningTasks = 0;
|
||||
let completedTasks = 0;
|
||||
let failedTasks = 0;
|
||||
|
||||
for (const agent of this.agents.values()) {
|
||||
if (agent.status === 'idle') idleAgents++;
|
||||
if (agent.status === 'busy') busyAgents++;
|
||||
}
|
||||
|
||||
for (const task of this.tasks.values()) {
|
||||
switch (task.status) {
|
||||
case 'pending': pendingTasks++; break;
|
||||
case 'running': runningTasks++; break;
|
||||
case 'completed': completedTasks++; break;
|
||||
case 'failed': failedTasks++; break;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
topology: this.config.topology,
|
||||
consensus: this.config.consensus,
|
||||
agentCount: this.agents.size,
|
||||
maxAgents: this.config.maxAgents,
|
||||
idleAgents,
|
||||
busyAgents,
|
||||
pendingTasks,
|
||||
runningTasks,
|
||||
completedTasks,
|
||||
failedTasks,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all agents
|
||||
*/
|
||||
getAgents(): SwarmAgent[] {
|
||||
return Array.from(this.agents.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get agent by ID
|
||||
*/
|
||||
getAgent(agentId: string): SwarmAgent | undefined {
|
||||
return this.agents.get(agentId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all tasks
|
||||
*/
|
||||
getTasks(): SwarmTask[] {
|
||||
return Array.from(this.tasks.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get task by ID
|
||||
*/
|
||||
getTask(taskId: string): SwarmTask | undefined {
|
||||
return this.tasks.get(taskId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update agent heartbeat
|
||||
*/
|
||||
heartbeat(agentId: string): void {
|
||||
const agent = this.agents.get(agentId);
|
||||
if (agent) {
|
||||
agent.lastHeartbeat = new Date();
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
// Private Methods
|
||||
// ==========================================================================
|
||||
|
||||
private enqueueTask(task: SwarmTask): void {
|
||||
const queue = this.taskQueue.get(task.priority);
|
||||
if (queue) {
|
||||
queue.push(task);
|
||||
}
|
||||
}
|
||||
|
||||
private dequeueTask(): SwarmTask | undefined {
|
||||
// Priority order: critical > high > normal > low
|
||||
const priorities: WorkerPriority[] = ['critical', 'high', 'normal', 'low'];
|
||||
|
||||
for (const priority of priorities) {
|
||||
const queue = this.taskQueue.get(priority);
|
||||
if (queue && queue.length > 0) {
|
||||
return queue.shift();
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private async processQueue(): Promise<void> {
|
||||
// Find idle agents matching pending tasks
|
||||
for (const agent of this.agents.values()) {
|
||||
if (agent.status !== 'idle') continue;
|
||||
|
||||
// Find a matching task
|
||||
const task = this.findTaskForAgent(agent);
|
||||
if (!task) continue;
|
||||
|
||||
// Assign task
|
||||
await this.assignTask(task, agent);
|
||||
}
|
||||
}
|
||||
|
||||
private findTaskForAgent(agent: SwarmAgent): SwarmTask | undefined {
|
||||
const priorities: WorkerPriority[] = ['critical', 'high', 'normal', 'low'];
|
||||
|
||||
for (const priority of priorities) {
|
||||
const queue = this.taskQueue.get(priority);
|
||||
if (!queue) continue;
|
||||
|
||||
// Find task matching agent type (for specialized strategy)
|
||||
// or any task (for balanced strategy)
|
||||
const taskIndex = queue.findIndex(task => {
|
||||
if (this.config.strategy === 'specialized') {
|
||||
return task.worker === agent.type;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (taskIndex >= 0) {
|
||||
return queue.splice(taskIndex, 1)[0];
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private async assignTask(task: SwarmTask, agent: SwarmAgent): Promise<void> {
|
||||
task.status = 'running';
|
||||
task.assignedAgent = agent.id;
|
||||
task.startedAt = new Date();
|
||||
|
||||
agent.status = 'busy';
|
||||
agent.currentTask = task.id;
|
||||
|
||||
this.emit('task:assigned', { task, agent });
|
||||
|
||||
// Set timeout for task
|
||||
const workerConfig = WORKER_DEFAULTS[task.worker];
|
||||
setTimeout(() => {
|
||||
const currentTask = this.tasks.get(task.id);
|
||||
if (currentTask && currentTask.status === 'running') {
|
||||
this.completeTask(task.id, undefined, 'Task timed out');
|
||||
}
|
||||
}, workerConfig.timeout);
|
||||
}
|
||||
|
||||
private checkHeartbeats(): void {
|
||||
const now = Date.now();
|
||||
const threshold = this.config.heartbeatInterval! * 3; // 3 missed heartbeats
|
||||
|
||||
for (const agent of this.agents.values()) {
|
||||
if (agent.status === 'offline') continue;
|
||||
|
||||
const lastHeartbeat = agent.lastHeartbeat.getTime();
|
||||
if (now - lastHeartbeat > threshold) {
|
||||
agent.status = 'offline';
|
||||
this.emit('agent:offline', agent);
|
||||
|
||||
// Re-queue any running task
|
||||
if (agent.currentTask) {
|
||||
const task = this.tasks.get(agent.currentTask);
|
||||
if (task && task.status === 'running') {
|
||||
task.status = 'pending';
|
||||
task.assignedAgent = undefined;
|
||||
this.enqueueTask(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Factory Function
|
||||
// ============================================================================
|
||||
|
||||
export function createSwarmCoordinator(config?: Partial<SwarmConfig>): SwarmCoordinator {
|
||||
return new SwarmCoordinator(config);
|
||||
}
|
||||
|
||||
export default SwarmCoordinator;
|
||||
1
vendor/ruvector/npm/packages/ruvbot/src/swarm/index.d.ts.map
vendored
Normal file
1
vendor/ruvector/npm/packages/ruvbot/src/swarm/index.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,eAAe,EACf,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,SAAS,EACd,KAAK,UAAU,EACf,KAAK,eAAe,GACrB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,kBAAkB,EAClB,wBAAwB,EACxB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,QAAQ,EACb,KAAK,IAAI,EACT,KAAK,eAAe,EACpB,KAAK,WAAW,GACjB,MAAM,yBAAyB,CAAC"}
|
||||
1
vendor/ruvector/npm/packages/ruvbot/src/swarm/index.js.map
vendored
Normal file
1
vendor/ruvector/npm/packages/ruvbot/src/swarm/index.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;AAEH,6DAa+B;AAZ7B,uHAAA,gBAAgB,OAAA;AAChB,6HAAA,sBAAsB,OAAA;AACtB,sHAAA,eAAe,OAAA;AAYjB,iEASiC;AAR/B,2HAAA,kBAAkB,OAAA;AAClB,iIAAA,wBAAwB,OAAA"}
|
||||
31
vendor/ruvector/npm/packages/ruvbot/src/swarm/index.ts
vendored
Normal file
31
vendor/ruvector/npm/packages/ruvbot/src/swarm/index.ts
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Swarm module exports
|
||||
*
|
||||
* Multi-agent swarm coordination with agentic-flow patterns.
|
||||
*/
|
||||
|
||||
export {
|
||||
SwarmCoordinator,
|
||||
createSwarmCoordinator,
|
||||
WORKER_DEFAULTS,
|
||||
type SwarmTopology,
|
||||
type ConsensusProtocol,
|
||||
type WorkerType,
|
||||
type WorkerPriority,
|
||||
type SwarmConfig,
|
||||
type WorkerConfig,
|
||||
type SwarmTask,
|
||||
type SwarmAgent,
|
||||
type DispatchOptions,
|
||||
} from './SwarmCoordinator.js';
|
||||
|
||||
export {
|
||||
ByzantineConsensus,
|
||||
createByzantineConsensus,
|
||||
type ConsensusPhase,
|
||||
type ConsensusConfig,
|
||||
type Proposal,
|
||||
type Vote,
|
||||
type ConsensusResult,
|
||||
type ReplicaInfo,
|
||||
} from './ByzantineConsensus.js';
|
||||
Reference in New Issue
Block a user