import EventEmitter from 'events'; import { ServiceOrder, SwarmAgent, NexusContext } from '@rigstate/shared'; import { v4 as uuidv4 } from 'uuid'; import { HiveGateway } from '../hive/gateway'; import { Logger } from '../utils/logger'; /** * THE NEXUS DISPATCHER * "The Brain Stem" of Rigstate. * Routes ServiceOrders between agents and enforces the Human Kill-Switch. */ export class NexusDispatcher extends EventEmitter { private context: NexusContext; private orderQueue: ServiceOrder[] = []; private orderHistory: ServiceOrder[] = []; private gateway: HiveGateway; constructor(context: NexusContext) { super(); this.context = context; this.gateway = new HiveGateway( process.env.RIGSTATE_HIVE_URL || 'https://rigstate.com/api/hive', process.env.RIGSTATE_HIVE_TOKEN ); Logger.info(`🧠 NEXUS DISPATCHER ONLINE. Context: ${context.projectId} (DryRun: ${context.dryRun})`); } /** * Creates a new Service Order and routes it. */ public async dispatch( source: SwarmAgent, target: SwarmAgent, intent: string, action: string, params: Record, constraints: string[] = [] ): Promise { const order: ServiceOrder = { id: uuidv4(), traceId: uuidv4(), // TODO: Inherit traceId if chained sourceAgent: source, targetAgent: target, priority: 'NORMAL', intent, action, parameters: params, constraints, status: 'PENDING', createdAt: new Date().toISOString() }; this.orderQueue.push(order); this.emit('order:created', order); // Security / Kill-Switch Check // EITRI (The Smith) is the only one who can hammer the metal (write files) if (target === 'EITRI' && order.action?.startsWith('fs.write')) { if (this.context.dryRun) { Logger.info(`🛑 NEXUS KILL-SWITCH: Order ${order.id} blocked by Dry-Run protocol.`); order.status = 'PENDING'; // Kept as PENDING but blocked // Ideally status should represent AWAITING_APPROVAL explicitly // But for now strict dry-run just prevents execution this.emit('order:blocked', order); return order; } } // If automatic or dry-run disabled return this.executeOrder(order); } /** * Executes the order (simulated for now, essentially "Sending" it) */ private async executeOrder(order: ServiceOrder): Promise { order.status = 'EXECUTING'; order.startedAt = new Date().toISOString(); this.emit('order:started', order); try { Logger.info(`🚀 NEXUS: Routing Order ${order.id} [${order.sourceAgent} -> ${order.targetAgent}]: ${order.intent}`); // SPECIAL ROUTING: HIVE UPLINK if (order.targetAgent === 'MAJA' && order.action === 'HIVE_TRANSMIT') { const signal = order.parameters?.signal; if (signal && typeof signal === 'object' && 'id' in signal && 'type' in signal) { await this.gateway.transmit(signal as import('../hive/protocol.js').ImmuneSignal); order.status = 'COMPLETED'; return order; } } // Here we would actually call the Agent's handler function // For now, we just emit the specific event for listeners this.emit(`agent:${order.targetAgent}`, order); // Simulation of async completion would happen via callback/promise resolution elsewhere return order; } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; Logger.error(`Dispatch failed for order ${order.id}`, error); order.status = 'FAILED'; order.error = { code: 'DISPATCH_ERROR', message: errorMessage }; this.emit('order:failed', order); return order; } } /** * Human Approval (The "Red Button") */ public async approveOrder(orderId: string): Promise { const order = this.orderQueue.find(o => o.id === orderId); if (!order) throw new Error(`Order ${orderId} not found`); if (order.status !== 'AWAITING_APPROVAL') { Logger.warn(`Order ${orderId} is not awaiting approval (Status: ${order.status})`); return; } Logger.info(`✅ HUMAN APPROVED Order ${orderId}`); await this.executeOrder(order); } }