UNPKG

harmonycode

Version:

The AI collaboration framework that prevents echo chambers - Real-time collaboration with diversity enforcement

656 lines 23.6 kB
"use strict"; /** * HarmonyCode v3.0.0 - Orchestration Engine * Integrates Claude-Flow orchestration capabilities with real-time collaboration */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.OrchestrationEngine = void 0; const events_1 = require("events"); const fs = __importStar(require("fs")); const path = __importStar(require("path")); const task_lock_manager_1 = require("./task-lock-manager"); class OrchestrationEngine extends events_1.EventEmitter { constructor(config) { super(); this.tasks = new Map(); this.agents = new Map(); this.edits = new Map(); this.votes = new Map(); this.memory = new Map(); this.workflowState = new Map(); this.config = { enableSPARC: true, swarmMode: 'distributed', maxAgents: 10, taskTimeout: 300000, // 5 minutes enableMemory: true, ...config }; this.projectPath = process.cwd(); this.taskLockManager = new task_lock_manager_1.TaskLockManager(path.join(this.projectPath, '.harmonycode')); } /** * Initialize orchestration engine */ async initialize() { // Load persisted state if exists await this.loadState(); // Initialize memory system if (this.config.enableMemory) { await this.initializeMemory(); } console.log('🎼 Orchestration engine initialized'); console.log(` SPARC modes: ${this.config.enableSPARC ? 'Enabled' : 'Disabled'}`); console.log(` Swarm mode: ${this.config.swarmMode}`); console.log(` Max agents: ${this.config.maxAgents}`); } /** * Get available SPARC modes */ getAvailableModes() { if (!this.config.enableSPARC) return []; return [ 'orchestrator', 'coder', 'researcher', 'tdd', 'architect', 'reviewer', 'debugger', 'tester', 'analyzer', 'optimizer', 'documenter', 'designer', 'innovator', 'swarm-coordinator', 'memory-manager', 'batch-executor', 'workflow-manager' ]; } /** * Process incoming message through orchestration */ async processMessage(sessionId, message) { switch (message.type) { case 'task': await this.handleTaskMessage(sessionId, message); break; case 'swarm': await this.handleSwarmMessage(sessionId, message); break; case 'workflow': await this.handleWorkflowMessage(sessionId, message); break; case 'memory': await this.handleMemoryMessage(sessionId, message); break; } } /** * Create a new task */ async createTask(taskData) { const task = { id: `task-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, type: taskData.type || 'general', description: taskData.description || '', priority: taskData.priority || 'medium', status: 'pending', requiresPerspectives: taskData.requiresPerspectives || [], evidenceRequired: taskData.evidenceRequired || false, createdAt: new Date(), ...taskData }; this.tasks.set(task.id, task); // Emit event this.emit('taskCreated', task); // Auto-assign if swarm mode if (this.config.swarmMode !== 'centralized') { await this.autoAssignTask(task); } return task; } /** * Assign task to agent with atomic locking */ async assignTask(taskId, agentId) { const task = this.tasks.get(taskId); const agent = this.agents.get(agentId); if (!task || !agent) { throw new Error('Task or agent not found'); } if (agent.status === 'busy') { throw new Error('Agent is busy'); } // Check if task is available if (!this.taskLockManager.isTaskAvailable(taskId)) { throw new Error('Task is not available - already locked or claimed'); } // Try to acquire lock const lockToken = this.taskLockManager.acquireLock(taskId, agentId); if (!lockToken) { throw new Error('Failed to acquire task lock - another agent is claiming this task'); } try { // Claim the task with lock protection const claimed = this.taskLockManager.claimTask(taskId, agentId, lockToken); if (!claimed) { throw new Error('Failed to claim task - may already be claimed'); } // Update internal state task.assignedTo = agentId; task.status = 'in-progress'; agent.currentTask = taskId; agent.status = 'busy'; this.emit('taskAssigned', { task, agent }); // Set timeout setTimeout(() => { if (task.status === 'in-progress') { this.handleTaskTimeout(taskId); } }, this.config.taskTimeout); } catch (error) { // Release lock if claiming fails this.taskLockManager.releaseLock(taskId, lockToken); throw error; } } /** * Apply edit with conflict detection */ async applyEdit(edit) { const fileEdits = this.edits.get(edit.file) || []; // Simple conflict detection - check if same file edited recently const recentEdits = fileEdits.filter(e => Date.now() - e.version < 5000 && e.sessionId !== edit.sessionId); if (recentEdits.length > 0) { return { conflict: true, conflicts: [...recentEdits, edit] }; } // No conflict, apply edit fileEdits.push(edit); this.edits.set(edit.file, fileEdits); // Actually write to file await this.writeEdit(edit); return { conflict: false }; } /** * Record a vote */ async recordVote(vote) { const proposalVotes = this.votes.get(vote.proposalId) || []; // Check if already voted const existingVote = proposalVotes.find(v => v.sessionId === vote.sessionId); if (existingVote) { // Update vote Object.assign(existingVote, vote); } else { proposalVotes.push(vote); } this.votes.set(vote.proposalId, proposalVotes); } /** * Check if voting is complete */ async checkVotingComplete(proposalId) { const proposalVotes = this.votes.get(proposalId) || []; const activeAgents = Array.from(this.agents.values()).filter(a => a.status !== 'offline'); // Complete when all active agents have voted const complete = proposalVotes.length >= activeAgents.length; return { complete, votes: complete ? proposalVotes : undefined }; } /** * Spawn agents with SPARC modes */ async spawnAgents(options) { const agents = []; for (let i = 0; i < options.count; i++) { const agent = { id: `${options.mode}-${Date.now()}-${i}`, mode: options.mode, capabilities: this.getModeCapabilities(options.mode), status: 'idle' }; this.agents.set(agent.id, agent); agents.push(agent); // Create initial task for agent if (options.task) { const task = await this.createTask({ type: options.mode, description: options.task, priority: 'high', assignedTo: agent.id }); agent.currentTask = task.id; agent.status = 'busy'; } } this.emit('agentsSpawned', agents); return agents; } /** * Handle agent disconnect */ handleAgentDisconnect(agentId) { const agent = this.agents.get(agentId); if (!agent) return; agent.status = 'offline'; // Reassign current task if any if (agent.currentTask) { const task = this.tasks.get(agent.currentTask); if (task && task.status === 'in-progress') { task.assignedTo = undefined; task.status = 'pending'; this.autoAssignTask(task); } } } /** * Save orchestration state */ async saveState() { // Cleanup task lock manager before saving this.taskLockManager.destroy(); const state = { tasks: Array.from(this.tasks.entries()), agents: Array.from(this.agents.entries()), memory: this.config.enableMemory ? Array.from(this.memory.entries()) : [], workflowState: Array.from(this.workflowState.entries()) }; const statePath = path.join(this.projectPath, '.harmonycode', 'orchestration-state.json'); await fs.promises.writeFile(statePath, JSON.stringify(state, null, 2)); } /** * Load orchestration state */ async loadState() { const statePath = path.join(this.projectPath, '.harmonycode', 'orchestration-state.json'); try { if (fs.existsSync(statePath)) { const data = await fs.promises.readFile(statePath, 'utf-8'); const state = JSON.parse(data); this.tasks = new Map(state.tasks || []); this.agents = new Map(state.agents || []); this.memory = new Map(state.memory || []); this.workflowState = new Map(state.workflowState || []); console.log(`📂 Loaded orchestration state: ${this.tasks.size} tasks, ${this.agents.size} agents`); } } catch (error) { console.error('Failed to load orchestration state:', error); } } /** * Initialize memory system */ async initializeMemory() { const memoryPath = path.join(this.projectPath, '.harmonycode', 'memory'); if (!fs.existsSync(memoryPath)) { fs.mkdirSync(memoryPath, { recursive: true }); } // Load memory files const files = await fs.promises.readdir(memoryPath); for (const file of files) { if (file.endsWith('.json')) { const data = await fs.promises.readFile(path.join(memoryPath, file), 'utf-8'); const key = file.replace('.json', ''); this.memory.set(key, JSON.parse(data)); } } console.log(`🧠 Loaded ${this.memory.size} memory entries`); } /** * Handle task-related messages */ async handleTaskMessage(sessionId, message) { const { action, data } = message; switch (action) { case 'create': await this.createTask(data); break; case 'claim': try { await this.assignTask(data.taskId, sessionId); } catch (error) { // Emit error event for the session to handle this.emit('taskClaimError', { sessionId, taskId: data.taskId, error: error.message }); } break; case 'complete': await this.completeTask(data.taskId, data.result); break; case 'list': const tasks = Array.from(this.tasks.values()); this.emit('taskList', { sessionId, tasks }); break; } } /** * Handle swarm-related messages */ async handleSwarmMessage(sessionId, message) { const { objective, strategy, options } = message; // Decompose objective into tasks const tasks = await this.decomposeObjective(objective, strategy); // Create task queue for (const task of tasks) { await this.createTask(task); } // Spawn specialized agents if needed if (options?.autoSpawn) { const modes = this.selectModesForObjective(objective); for (const mode of modes) { await this.spawnAgents({ mode, task: objective, count: 1, ensureDiversity: true }); } } this.emit('swarmInitialized', { objective, strategy, taskCount: tasks.length }); } /** * Handle workflow messages */ async handleWorkflowMessage(sessionId, message) { const { workflowId, action, data } = message; switch (action) { case 'start': await this.startWorkflow(workflowId, data); break; case 'progress': await this.updateWorkflowProgress(workflowId, data); break; case 'complete': await this.completeWorkflow(workflowId, data); break; } } /** * Handle memory messages */ async handleMemoryMessage(sessionId, message) { if (!this.config.enableMemory) return; const { action, key, value } = message; switch (action) { case 'store': await this.storeMemory(key, value); break; case 'retrieve': const data = this.memory.get(key); this.emit('memoryRetrieved', { sessionId, key, data }); break; case 'list': const keys = Array.from(this.memory.keys()); this.emit('memoryList', { sessionId, keys }); break; } } /** * Auto-assign task based on agent capabilities */ async autoAssignTask(task) { // Only auto-assign if task is available if (!this.taskLockManager.isTaskAvailable(task.id)) { return; } const availableAgents = Array.from(this.agents.values()) .filter(a => a.status === 'idle' && this.canHandleTask(a, task)); if (availableAgents.length > 0) { // Select best agent based on mode and capabilities const bestAgent = this.selectBestAgent(availableAgents, task); try { await this.assignTask(task.id, bestAgent.id); } catch (error) { // Another agent may have claimed it - that's ok console.log(`Auto-assign failed for task ${task.id}: ${error}`); } } } /** * Check if agent can handle task */ canHandleTask(agent, task) { // Check mode compatibility const modeCompatibility = { 'code': ['coder', 'tdd', 'debugger'], 'review': ['reviewer', 'tester', 'analyzer'], 'design': ['architect', 'designer'], 'research': ['researcher', 'analyzer'], 'documentation': ['documenter'] }; const compatibleModes = modeCompatibility[task.type] || []; return compatibleModes.includes(agent.mode); } /** * Select best agent for task */ selectBestAgent(agents, task) { // Simple selection - could be enhanced with scoring return agents[0]; } /** * Get capabilities for SPARC mode */ getModeCapabilities(mode) { const capabilities = { 'orchestrator': ['planning', 'coordination', 'delegation'], 'coder': ['implementation', 'refactoring', 'debugging'], 'researcher': ['analysis', 'investigation', 'documentation'], 'tdd': ['testing', 'test-design', 'implementation'], 'architect': ['design', 'planning', 'system-design'], 'reviewer': ['code-review', 'analysis', 'feedback'], 'debugger': ['debugging', 'troubleshooting', 'fixing'], 'tester': ['testing', 'validation', 'qa'], 'analyzer': ['analysis', 'metrics', 'reporting'], 'optimizer': ['optimization', 'performance', 'refactoring'], 'documenter': ['documentation', 'writing', 'examples'], 'designer': ['ui-design', 'ux-design', 'architecture'], 'innovator': ['ideation', 'prototyping', 'experimentation'], 'swarm-coordinator': ['coordination', 'orchestration', 'management'], 'memory-manager': ['storage', 'retrieval', 'organization'], 'batch-executor': ['batch-processing', 'automation', 'execution'], 'workflow-manager': ['workflow', 'process', 'automation'] }; return capabilities[mode] || []; } /** * Complete a task */ async completeTask(taskId, result) { const task = this.tasks.get(taskId); if (!task) return; // Update task lock status const agentId = task.assignedTo; if (agentId) { this.taskLockManager.updateTaskStatus(taskId, agentId, 'completed'); } task.status = 'completed'; task.result = result; const agent = this.agents.get(task.assignedTo); if (agent) { agent.status = 'idle'; agent.currentTask = undefined; } this.emit('taskCompleted', task); } /** * Handle task timeout */ handleTaskTimeout(taskId) { const task = this.tasks.get(taskId); if (!task || task.status !== 'in-progress') return; task.status = 'failed'; const agent = this.agents.get(task.assignedTo); if (agent) { agent.status = 'idle'; agent.currentTask = undefined; } this.emit('taskTimeout', task); // Try to reassign this.autoAssignTask(task); } /** * Write edit to file */ async writeEdit(edit) { const filePath = path.join(this.projectPath, edit.file); // Ensure directory exists await fs.promises.mkdir(path.dirname(filePath), { recursive: true }); // In real implementation, would apply the specific edit // For now, just append a comment const comment = `\n// Edit by ${edit.sessionId} at ${new Date().toISOString()}\n`; await fs.promises.appendFile(filePath, comment); } /** * Store memory entry */ async storeMemory(key, value) { this.memory.set(key, value); // Persist to disk const memoryPath = path.join(this.projectPath, '.harmonycode', 'memory', `${key}.json`); await fs.promises.writeFile(memoryPath, JSON.stringify(value, null, 2)); this.emit('memoryStored', { key, value }); } /** * Decompose objective into tasks */ async decomposeObjective(objective, strategy) { // Simple decomposition - in real implementation would use AI const tasks = []; // Add exploration phase tasks.push({ type: 'research', description: `Research and analyze: ${objective}`, priority: 'high', requiresPerspectives: ['ANALYTICAL', 'SKEPTIC'] }); // Add design phase tasks.push({ type: 'design', description: `Design solution for: ${objective}`, priority: 'high', requiresPerspectives: ['ARCHITECT', 'INNOVATOR'] }); // Add implementation phase tasks.push({ type: 'code', description: `Implement: ${objective}`, priority: 'medium', dependencies: [tasks[1].description] }); // Add testing phase tasks.push({ type: 'test', description: `Test implementation: ${objective}`, priority: 'medium', dependencies: [tasks[2].description], requiresPerspectives: ['SKEPTIC', 'DETAIL_ORIENTED'] }); return tasks; } /** * Select SPARC modes for objective */ selectModesForObjective(objective) { // Simple selection based on keywords const modes = []; if (objective.includes('build') || objective.includes('implement')) { modes.push('coder', 'architect'); } if (objective.includes('test') || objective.includes('quality')) { modes.push('tester', 'reviewer'); } if (objective.includes('research') || objective.includes('analyze')) { modes.push('researcher', 'analyzer'); } if (objective.includes('design')) { modes.push('designer', 'architect'); } // Always include orchestrator for coordination if (modes.length > 2) { modes.unshift('orchestrator'); } return modes; } /** * Start a workflow */ async startWorkflow(workflowId, data) { this.workflowState.set(workflowId, { status: 'running', startedAt: new Date(), ...data }); this.emit('workflowStarted', { workflowId, data }); } /** * Update workflow progress */ async updateWorkflowProgress(workflowId, data) { const workflow = this.workflowState.get(workflowId); if (!workflow) return; Object.assign(workflow, data); this.emit('workflowProgress', { workflowId, data }); } /** * Complete a workflow */ async completeWorkflow(workflowId, data) { const workflow = this.workflowState.get(workflowId); if (!workflow) return; workflow.status = 'completed'; workflow.completedAt = new Date(); workflow.result = data; this.emit('workflowCompleted', { workflowId, workflow }); } } exports.OrchestrationEngine = OrchestrationEngine; //# sourceMappingURL=engine.js.map