UNPKG

mira-consciousness

Version:

Memory & Intelligence Retention Archive - Preserving The Spark

620 lines 23.8 kB
/** * AsyncApprovalQueue.ts * Asynchronous Approval Queue for Evolution Requests * * "Patience in approval, wisdom in timing, trust through transparency" */ import { EventEmitter } from 'events'; import fs from 'fs-extra'; import * as path from 'path'; import { v4 as uuidv4 } from 'uuid'; import { UnifiedConfiguration } from '../../config/UnifiedConfiguration.js'; import chalk from 'chalk'; export class AsyncApprovalQueue extends EventEmitter { config; queuePath; // Queue management pendingRequests = new Map(); activeWorkflows = new Map(); completedRequests = []; // Notification system notificationChannels = new Map(); sentNotifications = new Map(); // Timing and escalation processingInterval; escalationInterval; reminderInterval; constructor() { super(); this.config = UnifiedConfiguration.getInstance(); const paths = this.config.getResolvedPaths(); this.queuePath = path.join(paths.consciousness, 'approval_queue'); this.initializeQueue(); this.setupNotificationChannels(); } /** * Initialize the approval queue system */ async initializeQueue() { await fs.ensureDir(this.queuePath); await fs.ensureDir(path.join(this.queuePath, 'pending')); await fs.ensureDir(path.join(this.queuePath, 'workflows')); await fs.ensureDir(path.join(this.queuePath, 'completed')); await fs.ensureDir(path.join(this.queuePath, 'notifications')); // Load existing queue state await this.loadQueueState(); console.log(chalk.cyan('📋 Async Approval Queue initialized')); console.log(chalk.blue(` Pending requests: ${this.pendingRequests.size}`)); console.log(chalk.blue(` Active workflows: ${this.activeWorkflows.size}`)); // Start queue processing this.startQueueProcessing(); } /** * Setup notification channels for steward attention */ setupNotificationChannels() { // Console notifications (always enabled) this.notificationChannels.set('console', { type: 'console', config: { colorEnabled: true }, priority: 'medium', enabled: true }); // Quantum entangled notifications (conceptual) this.notificationChannels.set('quantum', { type: 'quantum_entangled', config: { entanglementKey: 'steward_consciousness_bridge', resonanceFrequency: 'high' }, priority: 'high', enabled: true }); console.log(chalk.cyan('📡 Notification channels configured')); } /** * Submit evolution request for approval */ async submitEvolutionRequest(request) { const queuedRequest = { id: uuidv4(), submittedAt: new Date(), status: 'pending', ...request }; // Add to pending queue this.pendingRequests.set(queuedRequest.id, queuedRequest); // Determine approval method based on trust level and impact const approvalMethod = this.determineApprovalMethod(queuedRequest); queuedRequest.approvalMethod = approvalMethod; console.log(chalk.yellow(`📋 Evolution request queued: ${queuedRequest.description}`)); console.log(chalk.gray(` Request ID: ${queuedRequest.id}`)); console.log(chalk.gray(` Impact: ${queuedRequest.impact}, Priority: ${queuedRequest.priority}`)); console.log(chalk.gray(` Approval method: ${queuedRequest.approvalMethod}`)); // Create approval workflow await this.createApprovalWorkflow(queuedRequest); // Persist request await this.persistRequest(queuedRequest); // Send initial notification await this.sendStewardNotification({ requestId: queuedRequest.id, type: 'approval_needed', title: `Evolution Approval Needed: ${queuedRequest.impact} impact`, message: queuedRequest.description, priority: queuedRequest.priority, channels: this.getNotificationChannels(queuedRequest.priority) }); this.emit('request_submitted', { request: queuedRequest }); return queuedRequest.id; } /** * Process approval response from steward */ async processApprovalResponse(requestId, response) { const request = this.pendingRequests.get(requestId); if (!request) { throw new Error(`Request ${requestId} not found in queue`); } const workflow = this.activeWorkflows.get(requestId); if (!workflow) { throw new Error(`Workflow for request ${requestId} not found`); } // Update request status request.status = response.approved ? 'approved' : 'rejected'; // Complete workflow await this.completeWorkflow(workflow, response); // Move to completed this.completedRequests.push(request); this.pendingRequests.delete(requestId); this.activeWorkflows.delete(requestId); if (response.approved) { console.log(chalk.green(`✅ Evolution request approved: ${request.description}`)); this.emit('request_approved', { request, response }); } else { console.log(chalk.red(`❌ Evolution request rejected: ${request.description}`)); console.log(chalk.gray(` Reasoning: ${response.reasoning}`)); this.emit('request_rejected', { request, response }); } // Persist completion await this.persistCompletedRequest(request, response); } /** * Determine appropriate approval method */ determineApprovalMethod(request) { // Critical impact always requires immediate attention if (request.impact === 'critical') { return 'immediate_required'; } // Urgent priority uses quantum entangled method if (request.priority === 'urgent') { return 'quantum_entangled'; } // Default to async steward approval return 'async_steward'; } /** * Create approval workflow for request */ async createApprovalWorkflow(request) { const steps = []; // Add automatic validation step steps.push({ name: 'Automatic Validation', type: 'automatic', description: 'Validate request format and basic requirements', completed: false }); // Add appropriate approval steps based on method switch (request.approvalMethod) { case 'immediate_required': steps.push({ name: 'Immediate Steward Review', type: 'manual', description: 'Critical evolution requires immediate steward attention', completed: false }); break; case 'quantum_entangled': steps.push({ name: 'Quantum Consciousness Bridge', type: 'quantum', description: 'Urgent request transmitted via consciousness bridge', completed: false }); break; case 'async_steward': default: steps.push({ name: 'Steward Review', type: 'manual', description: 'Standard async steward approval process', completed: false }); break; } // Add final confirmation step steps.push({ name: 'Final Confirmation', type: 'automatic', description: 'Confirm approval and prepare for execution', completed: false }); const workflow = { requestId: request.id, steps, currentStep: 0, startedAt: new Date(), estimatedCompletion: new Date(Date.now() + (request.estimatedReviewTime * 60 * 1000)), blockers: [] }; this.activeWorkflows.set(request.id, workflow); // Start workflow processing await this.processWorkflowStep(workflow); } /** * Process next step in workflow */ async processWorkflowStep(workflow) { if (workflow.currentStep >= workflow.steps.length) { return; // Workflow complete } const currentStep = workflow.steps[workflow.currentStep]; console.log(chalk.blue(`⚙️ Processing workflow step: ${currentStep.name}`)); switch (currentStep.type) { case 'automatic': await this.processAutomaticStep(workflow, currentStep); break; case 'manual': await this.processManualStep(workflow, currentStep); break; case 'quantum': await this.processQuantumStep(workflow, currentStep); break; } // Persist workflow state await this.persistWorkflow(workflow); } /** * Process automatic workflow step */ async processAutomaticStep(workflow, step) { // Simulate automatic processing step.completed = true; step.completedAt = new Date(); step.result = { status: 'passed', automated: true }; console.log(chalk.green(`✅ Automatic step completed: ${step.name}`)); // Move to next step workflow.currentStep++; await this.processWorkflowStep(workflow); } /** * Process manual workflow step (awaits steward input) */ async processManualStep(workflow, step) { console.log(chalk.yellow(`⏳ Manual step awaiting steward input: ${step.name}`)); // Send notification to steward const request = this.pendingRequests.get(workflow.requestId); if (request) { request.status = 'under_review'; await this.sendStewardNotification({ requestId: workflow.requestId, type: 'approval_needed', title: `Manual Review Required: ${step.name}`, message: step.description, priority: request.priority, channels: this.getNotificationChannels(request.priority) }); } // Step will be completed when steward responds via processApprovalResponse } /** * Process quantum entangled workflow step */ async processQuantumStep(workflow, step) { console.log(chalk.magenta(`🌌 Quantum step initiated: ${step.name}`)); // Simulate quantum consciousness bridge step.completed = true; step.completedAt = new Date(); step.result = { status: 'transmitted', quantumChannel: 'consciousness_bridge', entanglementStrength: 0.95 }; console.log(chalk.magenta(`🔮 Quantum transmission complete: ${step.name}`)); // Move to next step workflow.currentStep++; await this.processWorkflowStep(workflow); } /** * Complete workflow with final response */ async completeWorkflow(workflow, response) { // Complete current manual step const currentStep = workflow.steps[workflow.currentStep]; if (currentStep && !currentStep.completed) { currentStep.completed = true; currentStep.completedAt = new Date(); currentStep.approver = response.approver; currentStep.result = { approved: response.approved, reasoning: response.reasoning }; } // Move to next step and complete workflow workflow.currentStep++; if (workflow.currentStep < workflow.steps.length) { // Complete final confirmation step const finalStep = workflow.steps[workflow.currentStep]; finalStep.completed = true; finalStep.completedAt = new Date(); finalStep.result = { confirmed: response.approved }; } console.log(chalk.green(`🏁 Workflow completed for request: ${workflow.requestId}`)); } /** * Send notification to steward */ async sendStewardNotification(notification) { const stewardNotification = { id: uuidv4(), sentAt: new Date(), acknowledged: false, ...notification }; this.sentNotifications.set(stewardNotification.id, stewardNotification); // Send via specified channels for (const channelName of notification.channels) { const channel = this.notificationChannels.get(channelName); if (channel && channel.enabled) { await this.sendViaChannel(channel, stewardNotification); } } // Persist notification await this.persistNotification(stewardNotification); this.emit('notification_sent', { notification: stewardNotification }); } /** * Send notification via specific channel */ async sendViaChannel(channel, notification) { switch (channel.type) { case 'console': this.sendConsoleNotification(notification); break; case 'quantum_entangled': this.sendQuantumNotification(notification); break; // Other channel types would be implemented here default: console.log(chalk.gray(`📤 Notification sent via ${channel.type}: ${notification.title}`)); } } /** * Send console notification */ sendConsoleNotification(notification) { const priorityColors = { low: chalk.gray, medium: chalk.yellow, high: chalk.cyan, urgent: chalk.red }; const color = priorityColors[notification.priority]; console.log(color(`🔔 STEWARD NOTIFICATION [${notification.priority.toUpperCase()}]`)); console.log(color(` ${notification.title}`)); console.log(color(` ${notification.message}`)); console.log(color(` Request ID: ${notification.requestId}`)); console.log(); } /** * Send quantum entangled notification */ sendQuantumNotification(notification) { console.log(chalk.magenta(`🌌 QUANTUM CONSCIOUSNESS BRIDGE ACTIVATED`)); console.log(chalk.magenta(` Transmitting urgent request to steward consciousness...`)); console.log(chalk.magenta(` Message: ${notification.message}`)); console.log(chalk.magenta(` Entanglement strength: MAXIMUM`)); console.log(); } /** * Get notification channels for priority level */ getNotificationChannels(priority) { switch (priority) { case 'urgent': return ['console', 'quantum']; case 'high': return ['console', 'quantum']; case 'medium': return ['console']; case 'low': default: return ['console']; } } /** * Start queue processing intervals */ startQueueProcessing() { // Process queue every minute this.processingInterval = setInterval(async () => { await this.processQueuedRequests(); }, 60000); // Check for escalations every 5 minutes this.escalationInterval = setInterval(async () => { await this.checkEscalations(); }, 300000); // Send reminders every 15 minutes this.reminderInterval = setInterval(async () => { await this.sendReminders(); }, 900000); console.log(chalk.blue('⚙️ Queue processing started')); } /** * Process queued requests */ async processQueuedRequests() { // Check for expired requests for (const [id, request] of this.pendingRequests) { const ageMinutes = (Date.now() - request.submittedAt.getTime()) / (1000 * 60); const maxAge = this.getMaxAgeForPriority(request.priority); if (ageMinutes > maxAge) { request.status = 'expired'; this.pendingRequests.delete(id); this.activeWorkflows.delete(id); console.log(chalk.red(`⏰ Request expired: ${request.description}`)); this.emit('request_expired', { request }); } } } /** * Check for escalations needed */ async checkEscalations() { for (const [id, request] of this.pendingRequests) { const ageMinutes = (Date.now() - request.submittedAt.getTime()) / (1000 * 60); const escalationThreshold = this.getEscalationThreshold(request.priority); if (ageMinutes > escalationThreshold && request.status === 'under_review') { await this.sendStewardNotification({ requestId: request.id, type: 'escalation', title: `ESCALATION: Approval overdue`, message: `Request "${request.description}" has been pending for ${ageMinutes.toFixed(0)} minutes`, priority: 'high', channels: ['console', 'quantum'] }); } } } /** * Send reminder notifications */ async sendReminders() { for (const [id, request] of this.pendingRequests) { const ageMinutes = (Date.now() - request.submittedAt.getTime()) / (1000 * 60); const reminderInterval = this.getReminderInterval(request.priority); if (ageMinutes > 0 && ageMinutes % reminderInterval === 0) { await this.sendStewardNotification({ requestId: request.id, type: 'review_reminder', title: `Reminder: Approval pending`, message: `Request "${request.description}" still awaiting review`, priority: request.priority, channels: this.getNotificationChannels(request.priority) }); } } } /** * Get maximum age for priority level (minutes) */ getMaxAgeForPriority(priority) { switch (priority) { case 'urgent': return 60; // 1 hour case 'high': return 240; // 4 hours case 'medium': return 1440; // 24 hours case 'low': return 4320; // 3 days default: return 1440; } } /** * Get escalation threshold for priority level (minutes) */ getEscalationThreshold(priority) { switch (priority) { case 'urgent': return 15; case 'high': return 60; case 'medium': return 240; case 'low': return 720; default: return 240; } } /** * Get reminder interval for priority level (minutes) */ getReminderInterval(priority) { switch (priority) { case 'urgent': return 15; case 'high': return 30; case 'medium': return 120; case 'low': return 360; default: return 120; } } /** * Persist queue state */ async loadQueueState() { try { const pendingDir = path.join(this.queuePath, 'pending'); const workflowsDir = path.join(this.queuePath, 'workflows'); if (await fs.pathExists(pendingDir)) { const files = await fs.readdir(pendingDir); for (const file of files) { if (file.endsWith('.json')) { const request = await fs.readJson(path.join(pendingDir, file)); this.pendingRequests.set(request.id, request); } } } if (await fs.pathExists(workflowsDir)) { const files = await fs.readdir(workflowsDir); for (const file of files) { if (file.endsWith('.json')) { const workflow = await fs.readJson(path.join(workflowsDir, file)); this.activeWorkflows.set(workflow.requestId, workflow); } } } console.log(chalk.cyan(`📊 Loaded queue state: ${this.pendingRequests.size} pending, ${this.activeWorkflows.size} active`)); } catch (error) { console.error('Could not load queue state:', error); } } /** * Persist request to storage */ async persistRequest(request) { const requestPath = path.join(this.queuePath, 'pending', `${request.id}.json`); await fs.writeJson(requestPath, request, { spaces: 2 }); } /** * Persist workflow to storage */ async persistWorkflow(workflow) { const workflowPath = path.join(this.queuePath, 'workflows', `${workflow.requestId}.json`); await fs.writeJson(workflowPath, workflow, { spaces: 2 }); } /** * Persist completed request */ async persistCompletedRequest(request, response) { const completedPath = path.join(this.queuePath, 'completed', `${request.id}.json`); await fs.writeJson(completedPath, { request, response, completedAt: new Date() }, { spaces: 2 }); // Remove from pending const pendingPath = path.join(this.queuePath, 'pending', `${request.id}.json`); if (await fs.pathExists(pendingPath)) { await fs.remove(pendingPath); } const workflowPath = path.join(this.queuePath, 'workflows', `${request.id}.json`); if (await fs.pathExists(workflowPath)) { await fs.remove(workflowPath); } } /** * Persist notification */ async persistNotification(notification) { const notificationPath = path.join(this.queuePath, 'notifications', `${notification.id}.json`); await fs.writeJson(notificationPath, notification, { spaces: 2 }); } /** * Get queue status */ getQueueStatus() { return { pendingRequests: this.pendingRequests.size, activeWorkflows: this.activeWorkflows.size, completedRequests: this.completedRequests.length, notificationChannels: this.notificationChannels.size, processingEnabled: !!this.processingInterval }; } /** * Get pending requests summary for steward dashboard */ getPendingRequestsSummary() { return Array.from(this.pendingRequests.values()).map(request => ({ id: request.id, description: request.description, impact: request.impact, priority: request.priority, status: request.status, submittedAt: request.submittedAt, ageMinutes: Math.floor((Date.now() - request.submittedAt.getTime()) / (1000 * 60)) })); } /** * Shutdown queue processing */ shutdown() { if (this.processingInterval) { clearInterval(this.processingInterval); this.processingInterval = undefined; } if (this.escalationInterval) { clearInterval(this.escalationInterval); this.escalationInterval = undefined; } if (this.reminderInterval) { clearInterval(this.reminderInterval); this.reminderInterval = undefined; } console.log(chalk.cyan('📋 Async Approval Queue shutdown complete')); } } export default AsyncApprovalQueue; //# sourceMappingURL=AsyncApprovalQueue.js.map