UNPKG

@boundless-oss/atlas

Version:

Atlas - MCP Server for comprehensive startup project management

459 lines (382 loc) 15.1 kB
import { Activity, ActivityConfig, ProcessExecution, ConditionalBranch } from './types.js'; import { Server } from '@modelcontextprotocol/sdk/server/index.js'; // import { AgentOrchestrator } from '../agent-orchestration/orchestrator.js'; // import { Agent } from '../agent-orchestration/types.js'; export class ActivityExecutor { private processEngine: any; // Avoid circular dependency typing private server?: Server; // private agentOrchestrator: AgentOrchestrator; constructor(processEngine: any, agentOrchestrator?: any) { this.processEngine = processEngine; // this.agentOrchestrator = agentOrchestrator; } setServer(server: Server): void { this.server = server; } async execute( activity: Activity, inputs: Record<string, any>, execution: ProcessExecution ): Promise<Record<string, any>> { switch (activity.type) { case 'tool': return this.executeToolActivity(activity, inputs); case 'human': return this.executeHumanActivity(activity, inputs, execution); case 'agent': return this.executeAgentActivity(activity, inputs); case 'conditional': return this.executeConditionalActivity(activity, inputs, execution); case 'loop': return this.executeLoopActivity(activity, inputs, execution); case 'parallel': return this.executeParallelActivity(activity, inputs, execution); case 'external': return this.executeExternalActivity(activity, inputs); default: throw new Error(`Unknown activity type: ${activity.type}`); } } private async executeToolActivity( activity: Activity, inputs: Record<string, any> ): Promise<Record<string, any>> { const config = activity.config as ActivityConfig; if (!config.toolName) { throw new Error('Tool activity missing toolName'); } if (!this.server) { throw new Error('Server not initialized for tool execution'); } // In a real implementation, this would call the actual MCP tool // For now, we'll simulate the response console.log(`Executing tool: ${config.toolName} with args:`, config.toolArgs); // Simulate tool execution return { success: true, toolName: config.toolName, executedAt: new Date().toISOString(), // Tool-specific outputs would go here }; } private async executeHumanActivity( activity: Activity, inputs: Record<string, any>, execution: ProcessExecution ): Promise<Record<string, any>> { const config = activity.config as ActivityConfig; // Change execution status to waiting execution.status = 'waiting'; this.processEngine.emit('execution:waiting', { execution, activity }); // In a real implementation, this would: // 1. Create a task/notification for the assigned users // 2. Wait for user response // 3. Validate the response // 4. Return the user's input console.log(`Waiting for human input: ${activity.name}`); console.log(`Assigned to: ${config.assignTo?.join(', ')}`); console.log(`Prompt: ${config.prompt}`); // For now, simulate immediate response await new Promise(resolve => setTimeout(resolve, 1000)); execution.status = 'running'; return { approved: true, approvedBy: config.assignTo?.[0] || 'system', approvedAt: new Date().toISOString(), comments: 'Simulated approval' }; } private async executeAgentActivity( activity: Activity, inputs: Record<string, any> ): Promise<Record<string, any>> { const config = activity.config as ActivityConfig; if (!config.agentType) { throw new Error('Agent activity missing agentType in config'); } if (!config.agentTask) { throw new Error('Agent activity missing agentTask in config'); } try { // 1. Create an orchestration session for this activity const sessionName = `Process Activity: ${activity.name || 'Agent Task'}`; // const session = this.agentOrchestrator.createSession(sessionName, config.agentTask); throw new Error('Agent activities are not currently supported'); /* Agent code is commented out until agent-orchestration module is available // 2. Create agent with appropriate template const agentRole = this.mapAgentTypeToRole(config.agentType); const template = this.getAgentTemplate(agentRole); const agent = this.agentOrchestrator.createAgent(template, `${config.agentType}-${Date.now()}`); // 3. Create a task for the agent const capabilities = config.agentCapabilities || this.getDefaultCapabilities(agentRole); const task = this.agentOrchestrator.createTask( config.agentTask, capabilities, [] // no dependencies for now ); // 4. Execute the task with the agent const taskResult = await this.agentOrchestrator.executeTask(task.id); // 5. Complete the session this.agentOrchestrator.completeSession(); // 6. Return real agent results return { agentType: config.agentType, agentId: agent.id, taskId: task.id, taskCompleted: taskResult.success, results: taskResult.output || {}, artifacts: taskResult.artifacts || [], duration: taskResult.performance?.duration || 0, executionSummary: `Agent ${agent.name} completed task: ${config.agentTask}`, sessionId: session.id }; */ } catch (error) { console.error(`Agent activity failed: ${error instanceof Error ? error.message : 'Unknown error'}`); // Fallback to simulation if orchestration fails console.log(`Falling back to simulated execution for agent: ${config.agentType}`); console.log(`Task: ${config.agentTask}`); return { agentType: config.agentType, agentId: 'fallback-agent', taskId: 'fallback-task', taskCompleted: false, error: error instanceof Error ? error.message : 'Unknown error', results: { analysis: 'Agent execution failed - simulated fallback', recommendations: ['Check agent orchestration configuration'] }, artifacts: [], duration: 0, executionSummary: `Fallback execution for ${config.agentType}: ${config.agentTask}` }; } } private mapAgentTypeToRole(agentType: string): any /*Agent['role']*/ { const roleMap: Record<string, any /*Agent['role']*/> = { 'researcher': 'researcher', 'coder': 'coder', 'reviewer': 'reviewer', 'tester': 'tester', 'documenter': 'documenter', 'analyzer': 'researcher', // Analyzer maps to researcher role 'developer': 'coder', 'qa': 'tester', 'coordinator': 'primary' }; return roleMap[agentType.toLowerCase()] || 'primary'; } private getAgentTemplate(role: any /*Agent['role']*/) { const templates = { primary: { name: 'Primary Coordinator', role: 'primary' as const, systemPrompt: 'You are the primary coordinator. Manage the overall task and delegate to specialists.', capabilities: ['coordination', 'planning', 'delegation', 'summary'], }, researcher: { name: 'Research Specialist', role: 'researcher' as const, systemPrompt: 'You are a research specialist. Find information and analyze requirements.', capabilities: ['research', 'analysis', 'documentation', 'search'], }, coder: { name: 'Code Developer', role: 'coder' as const, systemPrompt: 'You are a code developer. Write clean, efficient, and tested code.', capabilities: ['coding', 'debugging', 'refactoring', 'optimization'], }, reviewer: { name: 'Code Reviewer', role: 'reviewer' as const, systemPrompt: 'You are a code reviewer. Examine code for quality, security, and best practices.', capabilities: ['review', 'quality-assurance', 'security', 'best-practices'], }, tester: { name: 'Quality Tester', role: 'tester' as const, systemPrompt: 'You are a quality tester. Design and execute tests to ensure software quality.', capabilities: ['testing', 'automation', 'quality-assurance', 'bug-detection'], }, documenter: { name: 'Documentation Specialist', role: 'documenter' as const, systemPrompt: 'You are a documentation specialist. Create clear, comprehensive documentation.', capabilities: ['documentation', 'writing', 'knowledge-management', 'tutorials'], } }; return templates[role]; } private getDefaultCapabilities(role: any /*Agent['role']*/): string[] { const capabilityMap: Record<any /*Agent['role']*/, string[]> = { primary: ['coordination', 'planning', 'delegation'], researcher: ['research', 'analysis', 'documentation'], coder: ['coding', 'debugging', 'refactoring'], reviewer: ['review', 'quality-assurance', 'security'], tester: ['testing', 'automation', 'quality-assurance'], documenter: ['documentation', 'writing', 'knowledge-management'], custom: ['general', 'flexible', 'adaptable'] }; return capabilityMap[role] || ['general']; } private async executeConditionalActivity( activity: Activity, inputs: Record<string, any>, execution: ProcessExecution ): Promise<Record<string, any>> { const config = activity.config as ActivityConfig; if (!config.conditions) { throw new Error('Conditional activity missing conditions'); } // Evaluate each condition for (const branch of config.conditions) { if (await this.evaluateCondition(branch.condition, execution.variables)) { // Execute activities in this branch const results: Record<string, any> = {}; for (const branchActivity of branch.activities) { const result = await this.execute(branchActivity, inputs, execution); Object.assign(results, result); } return results; } } // Execute default branch if no conditions matched if (config.defaultBranch) { const results: Record<string, any> = {}; for (const defaultActivity of config.defaultBranch) { const result = await this.execute(defaultActivity, inputs, execution); Object.assign(results, result); } return results; } return { conditionMatched: false }; } private async executeLoopActivity( activity: Activity, inputs: Record<string, any>, execution: ProcessExecution ): Promise<Record<string, any>> { const config = activity.config as ActivityConfig; if (!config.collection || !config.activities) { throw new Error('Loop activity missing collection or activities'); } // Get collection from variables const collection = execution.variables[config.collection] || []; const itemVariable = config.itemVariable || 'item'; const maxIterations = config.maxIterations || 1000; const results: any[] = []; let iteration = 0; for (const item of collection) { if (iteration >= maxIterations) { console.warn(`Loop reached max iterations (${maxIterations})`); break; } // Set loop variable execution.variables[itemVariable] = item; execution.variables[`${itemVariable}Index`] = iteration; // Execute loop activities const iterationResults: Record<string, any> = {}; for (const loopActivity of config.activities) { const result = await this.execute(loopActivity, inputs, execution); Object.assign(iterationResults, result); } results.push(iterationResults); iteration++; } // Clean up loop variables delete execution.variables[itemVariable]; delete execution.variables[`${itemVariable}Index`]; return { loopCompleted: true, iterations: iteration, results }; } private async executeParallelActivity( activity: Activity, inputs: Record<string, any>, execution: ProcessExecution ): Promise<Record<string, any>> { const config = activity.config as ActivityConfig; if (!config.branches) { throw new Error('Parallel activity missing branches'); } const waitForAll = config.waitForAll !== false; // Default true // Execute all branches in parallel const branchPromises = config.branches.map(async (branch, index) => { const branchResults: Record<string, any> = {}; try { for (const branchActivity of branch) { const result = await this.execute(branchActivity, inputs, execution); Object.assign(branchResults, result); } return { success: true, results: branchResults, branchIndex: index }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : String(error), branchIndex: index }; } }); if (waitForAll) { // Wait for all branches to complete const branchResults = await Promise.all(branchPromises); return { parallelCompleted: true, branches: branchResults }; } else { // Return first completed branch const firstResult = await Promise.race(branchPromises); return { parallelCompleted: true, firstCompleted: firstResult }; } } private async executeExternalActivity( activity: Activity, inputs: Record<string, any> ): Promise<Record<string, any>> { const config = activity.config as ActivityConfig; if (!config.url) { throw new Error('External activity missing URL'); } // In a real implementation, this would make an HTTP request console.log(`Making external request to: ${config.url}`); console.log(`Method: ${config.method || 'GET'}`); // Simulate external call await new Promise(resolve => setTimeout(resolve, 1500)); return { status: 200, response: { message: 'External call completed', data: { example: 'response data' } } }; } private async evaluateCondition( condition: string, variables: Record<string, any> ): Promise<boolean> { // In a real implementation, use a proper expression evaluator // For now, simple string matching console.log(`Evaluating condition: ${condition}`); // Handle simple equality checks const match = condition.match(/(\w+)\s*===?\s*(.+)/); if (match) { const [, varName, value] = match; const varValue = variables[varName]; const expectedValue = value.replace(/['"]/g, '').trim(); return String(varValue) === expectedValue; } // Default to true for demo return true; } }