Files
wifi-densepose/vendor/ruvector/.claude/intelligence/swarm.js

372 lines
10 KiB
JavaScript

/**
* Swarm Optimization Module
*
* Implements hive-mind coordination patterns inspired by:
* - ruvector-mincut: Graph partitioning for optimal agent allocation
* - Collective intelligence: Emergent behavior from local interactions
* - Self-healing networks: Dynamic reconfiguration on failure
*/
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const DATA_DIR = join(__dirname, 'data');
const SWARM_STATE_FILE = join(DATA_DIR, 'swarm-state.json');
const COORDINATION_FILE = join(DATA_DIR, 'coordination-graph.json');
if (!existsSync(DATA_DIR)) mkdirSync(DATA_DIR, { recursive: true });
/**
* Agent coordination graph - models relationships between agents
* Edges represent coordination strength (higher = more interaction needed)
*/
class CoordinationGraph {
constructor() {
this.nodes = new Map(); // agentId -> { type, capabilities, load }
this.edges = new Map(); // "src:dst" -> { weight, interactions }
this.load();
}
load() {
if (existsSync(COORDINATION_FILE)) {
try {
const data = JSON.parse(readFileSync(COORDINATION_FILE, 'utf-8'));
this.nodes = new Map(Object.entries(data.nodes || {}));
this.edges = new Map(Object.entries(data.edges || {}));
} catch { /* fresh start */ }
}
}
save() {
const data = {
nodes: Object.fromEntries(this.nodes),
edges: Object.fromEntries(this.edges),
lastUpdated: new Date().toISOString()
};
writeFileSync(COORDINATION_FILE, JSON.stringify(data, null, 2));
}
addAgent(id, type, capabilities = []) {
this.nodes.set(id, { type, capabilities, load: 0, active: true });
this.save();
}
removeAgent(id) {
this.nodes.delete(id);
// Remove all edges involving this agent
for (const key of this.edges.keys()) {
if (key.startsWith(id + ':') || key.endsWith(':' + id)) {
this.edges.delete(key);
}
}
this.save();
}
recordInteraction(srcId, dstId, weight = 1) {
const key = `${srcId}:${dstId}`;
const edge = this.edges.get(key) || { weight: 0, interactions: 0 };
edge.weight += weight;
edge.interactions++;
this.edges.set(key, edge);
this.save();
}
/**
* Find the minimum cut between agent groups
* Uses a simplified Karger-like approach for demonstration
* In production, this would use ruvector-mincut's subpolynomial algorithm
*/
findMinCut() {
if (this.nodes.size < 2) return { cut: 0, groups: [[...this.nodes.keys()], []] };
const nodes = [...this.nodes.keys()];
const edges = [...this.edges.entries()].map(([key, val]) => {
const [src, dst] = key.split(':');
return { src, dst, weight: val.weight };
});
// Simple greedy cut: separate high-load agents
const loads = nodes.map(id => ({
id,
load: this.nodes.get(id)?.load || 0
})).sort((a, b) => b.load - a.load);
const midpoint = Math.ceil(nodes.length / 2);
const groupA = loads.slice(0, midpoint).map(n => n.id);
const groupB = loads.slice(midpoint).map(n => n.id);
// Calculate cut weight
let cutWeight = 0;
for (const edge of edges) {
const srcInA = groupA.includes(edge.src);
const dstInA = groupA.includes(edge.dst);
if (srcInA !== dstInA) {
cutWeight += edge.weight;
}
}
return { cut: cutWeight, groups: [groupA, groupB] };
}
/**
* Find critical agents (high betweenness centrality approximation)
*/
findCriticalAgents() {
const centrality = new Map();
for (const nodeId of this.nodes.keys()) {
let score = 0;
for (const [key, edge] of this.edges.entries()) {
if (key.includes(nodeId)) {
score += edge.weight * edge.interactions;
}
}
centrality.set(nodeId, score);
}
return [...centrality.entries()]
.sort((a, b) => b[1] - a[1])
.slice(0, 5)
.map(([id, score]) => ({ id, score, ...this.nodes.get(id) }));
}
/**
* Recommend optimal agent for a task based on graph structure
*/
recommendAgent(taskType, requiredCapabilities = []) {
const candidates = [];
for (const [id, node] of this.nodes.entries()) {
if (!node.active) continue;
// Score based on capabilities match
let score = 0;
for (const cap of requiredCapabilities) {
if (node.capabilities?.includes(cap)) score += 10;
}
// Prefer agents with lower load
score -= node.load * 0.5;
// Prefer agents with more connections (more coordination experience)
for (const key of this.edges.keys()) {
if (key.includes(id)) score += 0.1;
}
candidates.push({ id, score, ...node });
}
return candidates.sort((a, b) => b.score - a.score);
}
getStats() {
const activeAgents = [...this.nodes.values()].filter(n => n.active).length;
const totalEdges = this.edges.size;
const avgWeight = totalEdges > 0
? [...this.edges.values()].reduce((sum, e) => sum + e.weight, 0) / totalEdges
: 0;
return {
agents: this.nodes.size,
activeAgents,
edges: totalEdges,
avgEdgeWeight: avgWeight.toFixed(2),
criticalAgents: this.findCriticalAgents().slice(0, 3)
};
}
}
/**
* Hive Mind Coordinator - emergent collective intelligence
*/
class HiveMind {
constructor(graph) {
this.graph = graph;
this.consensus = new Map(); // decision -> votes
this.history = [];
}
/**
* Propose a decision to the hive mind
*/
propose(decision, agentId, confidence = 0.5) {
const key = this.decisionKey(decision);
const votes = this.consensus.get(key) || { yes: 0, no: 0, voters: [] };
if (!votes.voters.includes(agentId)) {
votes.voters.push(agentId);
if (confidence > 0.5) {
votes.yes += confidence;
} else {
votes.no += (1 - confidence);
}
this.consensus.set(key, votes);
}
return this.getConsensus(decision);
}
/**
* Get current consensus on a decision
*/
getConsensus(decision) {
const key = this.decisionKey(decision);
const votes = this.consensus.get(key) || { yes: 0, no: 0, voters: [] };
const total = votes.yes + votes.no;
if (total === 0) return { approved: null, confidence: 0, voters: 0 };
const approval = votes.yes / total;
return {
approved: approval > 0.5,
confidence: Math.abs(approval - 0.5) * 2, // 0-1 scale
approval: approval.toFixed(3),
voters: votes.voters.length
};
}
/**
* Self-healing: redistribute load when agent fails
*/
healPartition(failedAgentId) {
const node = this.graph.nodes.get(failedAgentId);
if (!node) return { healed: false, reason: 'Agent not found' };
// Mark as inactive
node.active = false;
this.graph.nodes.set(failedAgentId, node);
// Find replacement candidates
const candidates = this.graph.recommendAgent(node.type, node.capabilities);
const activeCandidate = candidates.find(c => c.id !== failedAgentId);
if (activeCandidate) {
// Redistribute load
const redistributed = Math.floor(node.load / Math.max(1, candidates.length - 1));
for (const candidate of candidates.filter(c => c.id !== failedAgentId)) {
const candNode = this.graph.nodes.get(candidate.id);
if (candNode) {
candNode.load += redistributed;
this.graph.nodes.set(candidate.id, candNode);
}
}
this.graph.save();
return {
healed: true,
failedAgent: failedAgentId,
replacedBy: activeCandidate.id,
loadRedistributed: redistributed * (candidates.length - 1)
};
}
return { healed: false, reason: 'No suitable replacement found' };
}
decisionKey(decision) {
if (typeof decision === 'string') return decision;
return JSON.stringify(decision);
}
}
/**
* Swarm Optimizer - coordinates multiple agents efficiently
*/
class SwarmOptimizer {
constructor() {
this.graph = new CoordinationGraph();
this.hiveMind = new HiveMind(this.graph);
this.loadState();
}
loadState() {
if (existsSync(SWARM_STATE_FILE)) {
try {
this.state = JSON.parse(readFileSync(SWARM_STATE_FILE, 'utf-8'));
} catch {
this.state = { tasks: [], optimizations: 0 };
}
} else {
this.state = { tasks: [], optimizations: 0 };
}
}
saveState() {
this.state.lastUpdated = new Date().toISOString();
writeFileSync(SWARM_STATE_FILE, JSON.stringify(this.state, null, 2));
}
/**
* Register an agent in the swarm
*/
registerAgent(id, type, capabilities = []) {
this.graph.addAgent(id, type, capabilities);
return { registered: true, id, type };
}
/**
* Record coordination between agents
*/
recordCoordination(srcAgent, dstAgent, weight = 1) {
this.graph.recordInteraction(srcAgent, dstAgent, weight);
return { recorded: true, edge: `${srcAgent} -> ${dstAgent}` };
}
/**
* Get optimal task distribution across agents
*/
optimizeTaskDistribution(tasks) {
const { cut, groups } = this.graph.findMinCut();
const distribution = { groups: [], cut, optimizations: ++this.state.optimizations };
for (let i = 0; i < groups.length; i++) {
const groupTasks = tasks.filter((_, idx) => idx % groups.length === i);
distribution.groups.push({
agents: groups[i],
tasks: groupTasks,
load: groupTasks.length
});
}
this.saveState();
return distribution;
}
/**
* Get best agent recommendation for a task
*/
recommendForTask(taskType, capabilities = []) {
const candidates = this.graph.recommendAgent(taskType, capabilities);
return {
recommended: candidates[0] || null,
alternatives: candidates.slice(1, 4),
reason: candidates[0]
? `Best match for ${taskType} with ${candidates[0].score.toFixed(1)} score`
: 'No suitable agents found'
};
}
/**
* Handle agent failure with self-healing
*/
handleFailure(agentId) {
return this.hiveMind.healPartition(agentId);
}
/**
* Get swarm statistics
*/
getStats() {
return {
graph: this.graph.getStats(),
optimizations: this.state.optimizations,
lastUpdated: this.state.lastUpdated
};
}
}
export { SwarmOptimizer, CoordinationGraph, HiveMind };
export default SwarmOptimizer;