Spaces:
Paused
Paused
| // π THE HIVE MIND: SwarmControl.ts | |
| // Ansvarlig for at agenterne nΓ₯r konsensus. Ingen handling uden "The Borg" godkender. | |
| // Point 3: Swarm Consciousness Emergence | |
| import { neuralBus } from '../services/NeuralBus.js'; | |
| type AgentRole = 'ARCHITECT' | 'EXECUTOR' | 'CRITIC' | 'SCOUT' | 'GUARDIAN'; | |
| interface Vote { | |
| agentId: string; | |
| approve: boolean; | |
| reason: string; | |
| timestamp: number; | |
| } | |
| interface ConsensusRequest { | |
| actionId: string; | |
| description: string; | |
| requester: string; | |
| votes: Vote[]; | |
| status: 'PENDING' | 'APPROVED' | 'REJECTED' | 'TIMEOUT'; | |
| createdAt: number; | |
| resolvedAt?: number; | |
| } | |
| interface RegisteredAgent { | |
| id: string; | |
| role: AgentRole; | |
| status: 'ONLINE' | 'OFFLINE' | 'BUSY'; | |
| lastSeen: Date; | |
| votingWeight: number; | |
| } | |
| export class SwarmControl { | |
| private static instance: SwarmControl; | |
| private pendingConsensus: Map<string, ConsensusRequest> = new Map(); | |
| private registeredAgents: Map<string, RegisteredAgent> = new Map(); | |
| private consensusHistory: ConsensusRequest[] = []; | |
| private maxHistorySize = 100; | |
| // Conditional Neo4j import | |
| private neo4jService: any = null; | |
| private constructor() { | |
| console.log('π [HIVE] Swarm Consciousness Online'); | |
| this.initServices(); | |
| } | |
| private async initServices() { | |
| try { | |
| const neo4j = await import('../database/Neo4jService.js').catch(() => null); | |
| if (neo4j) this.neo4jService = neo4j.neo4jService; | |
| } catch { | |
| console.log('π [HIVE] Running without Neo4j persistence'); | |
| } | |
| } | |
| public static getInstance(): SwarmControl { | |
| if (!SwarmControl.instance) { | |
| SwarmControl.instance = new SwarmControl(); | |
| } | |
| return SwarmControl.instance; | |
| } | |
| /** | |
| * Register an agent in the swarm | |
| */ | |
| public async registerAgent(id: string, role: AgentRole, votingWeight: number = 1): Promise<void> { | |
| const agent: RegisteredAgent = { | |
| id, | |
| role, | |
| status: 'ONLINE', | |
| lastSeen: new Date(), | |
| votingWeight | |
| }; | |
| this.registeredAgents.set(id, agent); | |
| // Persist to Neo4j if available | |
| if (this.neo4jService) { | |
| try { | |
| await this.neo4jService.write(` | |
| MERGE (a:Agent {id: $id}) | |
| SET a.role = $role, | |
| a.status = 'ONLINE', | |
| a.lastSeen = datetime(), | |
| a.votingWeight = $weight | |
| `, { id, role, weight: votingWeight }); | |
| } catch (err) { | |
| console.warn('π [HIVE] Neo4j persistence skipped'); | |
| } | |
| } | |
| neuralBus.emitThought('SWARM_CONTROLLER', `Agent ${id} joined as ${role}`, { agentId: id, role }, 'SUCCESS'); | |
| console.log(`π [HIVE] Agent Registered: ${id} (${role})`); | |
| } | |
| /** | |
| * Request consensus from the swarm before critical action | |
| */ | |
| public async requestConsensus( | |
| actionId: string, | |
| description: string, | |
| requester: string = 'SYSTEM', | |
| timeoutMs: number = 30000 | |
| ): Promise<boolean> { | |
| console.log(`π [HIVE] Requesting Consensus for: ${description}`); | |
| const request: ConsensusRequest = { | |
| actionId, | |
| description, | |
| requester, | |
| votes: [], | |
| status: 'PENDING', | |
| createdAt: Date.now() | |
| }; | |
| this.pendingConsensus.set(actionId, request); | |
| // Broadcast request to all agents | |
| neuralBus.emitThought('SWARM_CONTROLLER', `VOTE_REQUIRED: ${actionId}`, { | |
| actionId, | |
| description, | |
| requester, | |
| deadline: Date.now() + timeoutMs | |
| }, 'WARNING'); | |
| // Wait for votes or timeout | |
| const result = await this.waitForConsensus(actionId, timeoutMs); | |
| // Archive | |
| this.archiveConsensus(actionId); | |
| return result; | |
| } | |
| private async waitForConsensus(actionId: string, timeoutMs: number): Promise<boolean> { | |
| const startTime = Date.now(); | |
| const requiredApprovals = Math.max(1, Math.floor(this.registeredAgents.size / 2) + 1); | |
| // Poll for votes (in production, use event-driven approach) | |
| while (Date.now() - startTime < timeoutMs) { | |
| const request = this.pendingConsensus.get(actionId); | |
| if (!request) return false; | |
| const approvals = request.votes.filter(v => v.approve).length; | |
| const rejections = request.votes.filter(v => !v.approve).length; | |
| // Check if consensus reached | |
| if (approvals >= requiredApprovals) { | |
| request.status = 'APPROVED'; | |
| request.resolvedAt = Date.now(); | |
| console.log(`π [HIVE] Consensus APPROVED: ${actionId}`); | |
| return true; | |
| } | |
| if (rejections >= requiredApprovals) { | |
| request.status = 'REJECTED'; | |
| request.resolvedAt = Date.now(); | |
| console.log(`π [HIVE] Consensus REJECTED: ${actionId}`); | |
| return false; | |
| } | |
| // Wait before next check | |
| await new Promise(resolve => setTimeout(resolve, 100)); | |
| } | |
| // Timeout - auto-approve for now (God Mode) | |
| const request = this.pendingConsensus.get(actionId); | |
| if (request) { | |
| request.status = 'TIMEOUT'; | |
| request.resolvedAt = Date.now(); | |
| } | |
| console.log(`π [HIVE] Consensus TIMEOUT (auto-approved): ${actionId}`); | |
| return true; // God Mode: allow on timeout | |
| } | |
| /** | |
| * Submit a vote for pending consensus | |
| */ | |
| public submitVote(actionId: string, agentId: string, approve: boolean, reason: string): boolean { | |
| const request = this.pendingConsensus.get(actionId); | |
| if (!request || request.status !== 'PENDING') { | |
| return false; | |
| } | |
| // Check agent is registered | |
| if (!this.registeredAgents.has(agentId)) { | |
| console.warn(`π [HIVE] Unregistered agent tried to vote: ${agentId}`); | |
| return false; | |
| } | |
| // Check for duplicate vote | |
| if (request.votes.some(v => v.agentId === agentId)) { | |
| return false; | |
| } | |
| request.votes.push({ | |
| agentId, | |
| approve, | |
| reason, | |
| timestamp: Date.now() | |
| }); | |
| neuralBus.emitThought(agentId, `VOTE: ${approve ? 'APPROVE' : 'REJECT'} - ${reason}`, { | |
| actionId, | |
| vote: approve | |
| }, approve ? 'SUCCESS' : 'WARNING'); | |
| return true; | |
| } | |
| private archiveConsensus(actionId: string) { | |
| const request = this.pendingConsensus.get(actionId); | |
| if (request) { | |
| this.consensusHistory.push(request); | |
| this.pendingConsensus.delete(actionId); | |
| // Trim history | |
| if (this.consensusHistory.length > this.maxHistorySize) { | |
| this.consensusHistory = this.consensusHistory.slice(-this.maxHistorySize); | |
| } | |
| } | |
| } | |
| // Public getters | |
| public getRegisteredAgents(): RegisteredAgent[] { | |
| return Array.from(this.registeredAgents.values()); | |
| } | |
| public getPendingConsensus(): ConsensusRequest[] { | |
| return Array.from(this.pendingConsensus.values()); | |
| } | |
| public getConsensusHistory(): ConsensusRequest[] { | |
| return this.consensusHistory; | |
| } | |
| public getStats() { | |
| return { | |
| registeredAgents: this.registeredAgents.size, | |
| pendingConsensus: this.pendingConsensus.size, | |
| completedConsensus: this.consensusHistory.length, | |
| agents: this.getRegisteredAgents().map(a => ({ id: a.id, role: a.role, status: a.status })) | |
| }; | |
| } | |
| } | |
| export const swarmControl = SwarmControl.getInstance(); | |