UNPKG

permamind

Version:

An MCP server that provides an immortal memory layer for AI agents and clients

972 lines (971 loc) 38.6 kB
// Workflow template library const WORKFLOW_TEMPLATES = { "software-development": { category: "development", description: "Complete software development workflow from requirements to deployment", id: "software-development", name: "Software Development Lifecycle", presets: [ { description: "Configuration for small projects", name: "Small Project", tags: ["small", "quick"], variables: { complexity: "simple", teamSize: 3 }, }, { description: "Configuration for large enterprise projects", name: "Enterprise Project", tags: ["enterprise", "large"], variables: { complexity: "complex", teamSize: 15 }, }, ], template: { agentAssignments: [], dependencies: [], description: "Standard software development workflow", id: "software-dev-template", metadata: { author: "BMAD System", category: "development", complexity: "moderate", createdAt: new Date().toISOString(), estimatedDuration: 3360, // 56 hours total status: "active", tags: ["software", "development", "lifecycle"], updatedAt: new Date().toISOString(), version: "1.0.0", }, name: "Software Development", stages: [ { assignedAgents: ["analyst", "pm"], deliverables: ["requirements-document"], description: "Gather and analyze requirements", estimatedDuration: 480, // 8 hours exitCriteria: ["requirements-approved"], id: "requirements", name: "Requirements Analysis", order: 1, parallelExecution: false, prerequisites: [], tasks: [], }, { assignedAgents: ["architect", "ux-expert"], deliverables: ["architecture-document", "ui-mockups"], description: "Create system architecture and design", estimatedDuration: 720, // 12 hours exitCriteria: ["design-approved"], id: "design", name: "System Design", order: 2, parallelExecution: false, prerequisites: ["requirements"], tasks: [], }, { assignedAgents: ["developer"], deliverables: ["source-code", "unit-tests"], description: "Implement the designed solution", estimatedDuration: 1440, // 24 hours exitCriteria: ["code-review-passed", "tests-passing"], id: "implementation", name: "Code Implementation", order: 3, parallelExecution: true, prerequisites: ["design"], tasks: [], }, { assignedAgents: ["qa"], deliverables: ["test-results", "bug-reports"], description: "Test the implemented solution", estimatedDuration: 480, // 8 hours exitCriteria: ["qa-approved"], id: "testing", name: "Quality Assurance", order: 4, parallelExecution: false, prerequisites: ["implementation"], tasks: [], }, { assignedAgents: ["developer", "pm"], deliverables: ["deployment-package", "deployment-logs"], description: "Deploy the solution to production", estimatedDuration: 240, // 4 hours exitCriteria: ["deployment-successful"], id: "deployment", name: "Deployment", order: 5, parallelExecution: false, prerequisites: ["testing"], tasks: [], }, ], triggers: [], version: "1.0.0", }, variables: [ { description: "Name of the project", name: "projectName", required: true, type: "string", }, { defaultValue: 5, description: "Number of team members", name: "teamSize", required: false, type: "number", }, { defaultValue: "moderate", description: "Project complexity level", name: "complexity", required: false, type: "string", }, ], }, }; export class WorkflowAutomationServiceImpl { teamAgentService; processService; memoryService; constructor(teamAgentService, processService, memoryService) { this.teamAgentService = teamAgentService; this.processService = processService; this.memoryService = memoryService; } async cancelWorkflowExecution(executionId, hubId) { try { const execution = await this.getWorkflowExecution(executionId, hubId); if (!execution) { return false; } execution.status = "cancelled"; execution.completedAt = new Date().toISOString(); await this.storeWorkflowExecution(execution, {}, hubId); return true; } catch (error) { return false; } } async createWorkflow(workflowDef, signer, hubId) { try { // Validate workflow definition this.validateWorkflowDefinition(workflowDef); // Store workflow in memory service const workflowMemory = { content: JSON.stringify(workflowDef), context: { domain: "workflow_definition", sessionId: `workflow_${workflowDef.id}`, topic: workflowDef.name, }, importance: 0.9, memoryType: "workflow", }; await this.memoryService.addEnhanced(signer, hubId, workflowMemory); return workflowDef.id; } catch (error) { throw new Error(`Failed to create workflow: ${error instanceof Error ? error.message : "Unknown error"}`); } } async createWorkflowTemplate(template, hubId) { try { const templateMemory = { content: JSON.stringify(template), context: { domain: "workflow_template", topic: template.name, }, importance: 0.8, memoryType: "workflow", }; await this.memoryService.addEnhanced({}, hubId, templateMemory); return template.id; } catch (error) { throw new Error(`Failed to create workflow template: ${error instanceof Error ? error.message : "Unknown error"}`); } } async deleteWorkflow(workflowId, hubId) { try { const workflow = await this.getWorkflow(workflowId, hubId); if (!workflow) { return false; } // Mark workflow as archived return await this.updateWorkflow(workflowId, { metadata: { ...workflow.metadata, status: "archived" } }, hubId); } catch (error) { return false; } } async executeWorkflow(workflowId, context, signer, hubId) { const startTime = new Date().toISOString(); const executionId = `exec_${workflowId}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; try { const workflow = await this.getWorkflow(workflowId, hubId); if (!workflow) { return { error: { code: "WORKFLOW_NOT_FOUND", message: `Workflow ${workflowId} not found`, retryable: false, }, executionId, startedAt: startTime, status: "failed", success: false, }; } // Create workflow execution record const execution = { context, currentStage: workflow.stages[0]?.id, id: executionId, participants: [], progress: { completedStages: 0, completedTasks: 0, estimatedTimeRemaining: workflow.metadata.estimatedDuration, overallPercent: 0, totalStages: workflow.stages.length, totalTasks: workflow.stages.reduce((total, stage) => total + stage.tasks.length, 0), }, results: [], startedAt: startTime, status: "running", workflowId, }; // Store execution record await this.storeWorkflowExecution(execution, signer, hubId); // Execute workflow stages const results = await this.executeWorkflowStages(workflow, execution, signer, hubId); // Update execution status execution.status = results.length > 0 ? "completed" : "failed"; execution.completedAt = new Date().toISOString(); execution.results = results; await this.storeWorkflowExecution(execution, signer, hubId); return { executionId, results, startedAt: startTime, status: execution.status, success: execution.status === "completed", }; } catch (error) { return { error: { code: "EXECUTION_FAILED", message: error instanceof Error ? error.message : "Unknown error", retryable: true, }, executionId, startedAt: startTime, status: "failed", success: false, }; } } async executeWorkflowOnAO(workflowId, processId, parameters, signer, hubId) { const startTime = Date.now(); try { // Get workflow and integration configuration const workflow = await this.getWorkflow(workflowId, hubId); if (!workflow) { return { duration: Date.now() - startTime, error: { code: "WORKFLOW_NOT_FOUND", message: "Workflow not found", retryable: false, }, processId, success: false, }; } // Execute workflow through process communication service const processResult = await this.processService.executeProcessRequest(JSON.stringify(workflow), processId, `Execute workflow: ${workflow.name}`, signer); return { duration: Date.now() - startTime, error: processResult.success !== false ? undefined : { code: "AO_EXECUTION_FAILED", message: processResult.error || "AO execution failed", retryable: true, }, processId, result: processResult.data, success: processResult.success !== false, }; } catch (error) { return { duration: Date.now() - startTime, error: { code: "EXECUTION_ERROR", message: error instanceof Error ? error.message : "Unknown error", retryable: true, }, processId, success: false, }; } } async executeWorkflowRequest(naturalLanguageRequest, workflowId, signer, hubId) { try { const workflow = await this.getWorkflow(workflowId, hubId); if (!workflow) { return { error: `Workflow ${workflowId} not found`, estimatedDuration: 0, interpretation: "Workflow not found", parameters: {}, requiredApprovals: [], success: false, }; } // Parse natural language request const interpretation = this.interpretWorkflowRequest(naturalLanguageRequest); const parameters = this.extractParametersFromRequest(naturalLanguageRequest); const estimatedDuration = this.estimateRequestDuration(naturalLanguageRequest, workflow); // Execute the workflow const executionContext = { constraints: [], environment: "development", parameters, resources: [], triggeredBy: "natural_language_request", }; const result = await this.executeWorkflow(workflowId, executionContext, signer, hubId); return { error: result.error?.message, estimatedDuration, executionId: result.executionId, interpretation, parameters, requiredApprovals: [], // Would be determined based on workflow requirements success: result.success, }; } catch (error) { return { error: error instanceof Error ? error.message : "Unknown error", estimatedDuration: 0, interpretation: "Failed to process request", parameters: {}, requiredApprovals: [], success: false, }; } } async generateWorkflowReport(executionId, reportType, hubId) { try { const execution = await this.getWorkflowExecution(executionId, hubId); if (!execution) { throw new Error("Execution not found"); } const workflow = await this.getWorkflow(execution.workflowId, hubId); if (!workflow) { throw new Error("Workflow not found"); } const reportId = `report_${executionId}_${Date.now()}`; const content = await this.generateReportContent(execution, workflow, reportType); return { content, executionId, generatedAt: new Date().toISOString(), reportId, reportType, workflowId: execution.workflowId, }; } catch (error) { throw new Error(`Failed to generate workflow report: ${error instanceof Error ? error.message : "Unknown error"}`); } } async getWorkflow(workflowId, hubId) { try { const memories = await this.memoryService.searchAdvanced(hubId, `workflow_${workflowId}`, { memoryType: "workflow" }); if (memories.length === 0) { return null; } return JSON.parse(memories[0].content); } catch (error) { throw new Error(`Failed to get workflow: ${error instanceof Error ? error.message : "Unknown error"}`); } } async getWorkflowAnalytics(workflowId, hubId) { try { // Get all executions for this workflow const executions = await this.getWorkflowExecutions(workflowId, hubId); const analytics = { averageDuration: this.calculateAverageDuration(executions), errorPatterns: this.analyzeErrorPatterns(executions), executionTrends: this.calculateExecutionTrends(executions), performanceInsights: this.generatePerformanceInsights(executions), popularStages: this.calculateStageUsage(executions), successRate: executions.filter((e) => e.status === "completed").length / executions.length, totalExecutions: executions.length, workflowId, }; return analytics; } catch (error) { throw new Error(`Failed to get workflow analytics: ${error instanceof Error ? error.message : "Unknown error"}`); } } async getWorkflowExecution(executionId, hubId) { try { const memories = await this.memoryService.searchAdvanced(hubId, `execution_${executionId}`, { memoryType: "workflow" }); if (memories.length === 0) { return null; } return JSON.parse(memories[0].content); } catch (error) { return null; } } async getWorkflowProgress(executionId, hubId) { try { const execution = await this.getWorkflowExecution(executionId, hubId); return execution?.progress || null; } catch (error) { return null; } } async getWorkflowResults(executionId, hubId) { try { const execution = await this.getWorkflowExecution(executionId, hubId); return execution?.results || []; } catch (error) { return []; } } async instantiateFromTemplate(templateId, variables, hubId) { try { // Check built-in templates first const builtInTemplate = WORKFLOW_TEMPLATES[templateId]; let template; if (builtInTemplate) { template = builtInTemplate; } else { // Search for custom templates const memories = await this.memoryService.searchAdvanced(hubId, `template_${templateId}`, { memoryType: "workflow" }); if (memories.length === 0) { throw new Error(`Template ${templateId} not found`); } template = JSON.parse(memories[0].content); } // Instantiate workflow from template const workflow = { ...template.template }; workflow.id = `${templateId}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; workflow.metadata.createdAt = new Date().toISOString(); workflow.metadata.updatedAt = new Date().toISOString(); // Apply variable substitution this.applyVariableSubstitution(workflow, template.variables, variables); return workflow; } catch (error) { throw new Error(`Failed to instantiate from template: ${error instanceof Error ? error.message : "Unknown error"}`); } } async integrateWithAOProcesses(workflowId, processConfig, hubId) { try { const workflow = await this.getWorkflow(workflowId, hubId); if (!workflow) { return { error: "Workflow not found", integrationId: "", mappings: processConfig, success: false, validatedHandlers: [], }; } // Validate AO process handlers const validatedHandlers = []; for (const [stageId, handler] of Object.entries(processConfig.handlerMapping)) { try { // Validate that stage exists in workflow const stage = workflow.stages.find((s) => s.id === stageId); if (stage) { validatedHandlers.push(handler); } } catch (error) { // Skip invalid handlers } } const integrationId = `integration_${workflowId}_${Date.now()}`; // Store integration configuration const integrationMemory = { content: JSON.stringify({ processConfig, validatedHandlers, workflowId, }), context: { domain: "ao_integration", sessionId: integrationId, topic: `workflow_${workflowId}_ao`, }, importance: 0.8, memoryType: "workflow", }; await this.memoryService.addEnhanced({}, hubId, integrationMemory); return { integrationId, mappings: processConfig, success: true, validatedHandlers, }; } catch (error) { return { error: error instanceof Error ? error.message : "Unknown error", integrationId: "", mappings: processConfig, success: false, validatedHandlers: [], }; } } async listWorkflows(hubId, category) { try { const searchQuery = category ? `workflow_definition category:${category}` : "workflow_definition"; const memories = await this.memoryService.searchAdvanced(hubId, searchQuery, { memoryType: "workflow" }); const workflows = []; for (const memory of memories) { try { const workflowData = JSON.parse(memory.content); if (workflowData.stages && (!category || workflowData.metadata.category === category)) { workflows.push(workflowData); } } catch (parseError) { continue; } } return workflows.sort((a, b) => a.metadata.updatedAt.localeCompare(b.metadata.updatedAt)); } catch (error) { throw new Error(`Failed to list workflows: ${error instanceof Error ? error.message : "Unknown error"}`); } } async listWorkflowTemplates(hubId) { try { // Include built-in templates const builtInTemplates = Object.values(WORKFLOW_TEMPLATES); // Get custom templates const memories = await this.memoryService.searchAdvanced(hubId, "workflow_template", { memoryType: "workflow" }); const customTemplates = []; for (const memory of memories) { try { const template = JSON.parse(memory.content); if (template.template && template.variables) { customTemplates.push(template); } } catch (parseError) { continue; } } return [...builtInTemplates, ...customTemplates]; } catch (error) { throw new Error(`Failed to list workflow templates: ${error instanceof Error ? error.message : "Unknown error"}`); } } async parseNaturalLanguageWorkflow(description, context) { try { // Simple natural language parsing implementation // In a real implementation, this would use more sophisticated NLP const workflowId = `nl_workflow_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; // Extract workflow components from description const stages = this.extractStagesFromDescription(description); const estimatedDuration = this.estimateDurationFromDescription(description); const workflow = { agentAssignments: [], dependencies: [], description, id: workflowId, metadata: { author: "Natural Language Parser", category: "custom", complexity: "moderate", createdAt: new Date().toISOString(), estimatedDuration, status: "draft", tags: ["natural-language", "auto-generated"], updatedAt: new Date().toISOString(), version: "1.0.0", }, name: `Natural Language Workflow: ${description.substring(0, 50)}...`, stages, triggers: [], version: "1.0.0", }; return workflow; } catch (error) { throw new Error(`Failed to parse natural language workflow: ${error instanceof Error ? error.message : "Unknown error"}`); } } async pauseWorkflowExecution(executionId, hubId) { try { const execution = await this.getWorkflowExecution(executionId, hubId); if (!execution || execution.status !== "running") { return false; } execution.status = "paused"; await this.storeWorkflowExecution(execution, {}, hubId); return true; } catch (error) { return false; } } async resumeWorkflowExecution(executionId, hubId) { try { const execution = await this.getWorkflowExecution(executionId, hubId); if (!execution || execution.status !== "paused") { return false; } execution.status = "running"; await this.storeWorkflowExecution(execution, {}, hubId); return true; } catch (error) { return false; } } async updateWorkflow(workflowId, updates, hubId) { try { const workflow = await this.getWorkflow(workflowId, hubId); if (!workflow) { return false; } const updatedWorkflow = { ...workflow, ...updates }; updatedWorkflow.metadata.updatedAt = new Date().toISOString(); // Store updated workflow const workflowMemory = { content: JSON.stringify(updatedWorkflow), context: { domain: "workflow_definition", sessionId: `workflow_${workflowId}`, topic: updatedWorkflow.name, }, importance: 0.9, memoryType: "workflow", }; await this.memoryService.addEnhanced({}, hubId, workflowMemory); return true; } catch (error) { return false; } } async updateWorkflowState(executionId, state, hubId) { try { const execution = await this.getWorkflowExecution(executionId, hubId); if (!execution) { return false; } if (state.currentStage) execution.currentStage = state.currentStage; if (state.status) execution.status = state.status; if (state.progress) execution.progress = { ...execution.progress, ...state.progress }; if (state.results) execution.results.push(...state.results); await this.storeWorkflowExecution(execution, {}, hubId); return true; } catch (error) { return false; } } analyzeErrorPatterns(executions) { // Analyze common error patterns return []; } applyVariableSubstitution(workflow, templateVars, values) { // Apply variable substitution to workflow definition // This would replace template variables with actual values } calculateAverageDuration(executions) { const completedExecutions = executions.filter((e) => e.completedAt); if (completedExecutions.length === 0) return 0; const totalDuration = completedExecutions.reduce((sum, e) => { const duration = new Date(e.completedAt).getTime() - new Date(e.startedAt).getTime(); return sum + duration; }, 0); return totalDuration / completedExecutions.length; } calculateExecutionTrends(executions) { // Group executions by date and calculate trends const trends = {}; executions.forEach((execution) => { const date = execution.startedAt.split("T")[0]; if (!trends[date]) { trends[date] = { averageDuration: 0, date, executions: 0, successRate: 0, }; } trends[date].executions++; }); return Object.values(trends); } calculateStageUsage(executions) { // Calculate stage usage statistics return []; } estimateDurationFromDescription(description) { // Extract duration estimates from description const hourMatches = description.match(/(\d+)\s+hours?/gi); const dayMatches = description.match(/(\d+)\s+days?/gi); let totalMinutes = 0; if (hourMatches) { hourMatches.forEach((match) => { const hours = parseInt(match.match(/\d+/)?.[0] || "0"); totalMinutes += hours * 60; }); } if (dayMatches) { dayMatches.forEach((match) => { const days = parseInt(match.match(/\d+/)?.[0] || "0"); totalMinutes += days * 8 * 60; // 8 hours per day }); } return totalMinutes || 480; // Default 8 hours } estimateRequestDuration(request, workflow) { // Estimate duration for the request return workflow.metadata.estimatedDuration; } async executeWorkflowStage(stage, execution, signer, hubId) { // Simulate stage execution // In a real implementation, this would coordinate with assigned agents const stageResult = { approved: true, notes: `Stage ${stage.name} completed successfully`, output: { deliverables: stage.deliverables, duration: stage.estimatedDuration, exitCriteria: stage.exitCriteria, }, quality: 0.9, stageId: stage.id, timestamp: new Date().toISOString(), }; return stageResult; } async executeWorkflowStages(workflow, execution, signer, hubId) { const results = []; for (const stage of workflow.stages.sort((a, b) => a.order - b.order)) { try { // Update current stage execution.currentStage = stage.id; await this.storeWorkflowExecution(execution, signer, hubId); // Execute stage const stageResult = await this.executeWorkflowStage(stage, execution, signer, hubId); results.push(stageResult); // Update progress execution.progress.completedStages++; execution.progress.overallPercent = (execution.progress.completedStages / execution.progress.totalStages) * 100; await this.storeWorkflowExecution(execution, signer, hubId); } catch (error) { // Handle stage execution error const errorResult = { approved: false, notes: `Stage execution failed: ${error instanceof Error ? error.message : "Unknown error"}`, output: { error: error instanceof Error ? error.message : "Unknown error", }, quality: 0, stageId: stage.id, timestamp: new Date().toISOString(), }; results.push(errorResult); break; // Stop execution on error } } return results; } extractParametersFromRequest(request) { // Extract parameters from natural language request return {}; } extractStagesFromDescription(description) { // Extract workflow stages from natural language description // This is a simplified implementation const stages = []; // Look for numbered steps or bullet points const stepMatches = description.match(/\d+\.\s+([^.]+)/g) || []; stepMatches.forEach((match, index) => { const stageName = match.replace(/\d+\.\s+/, "").trim(); stages.push({ assignedAgents: [], deliverables: [], description: stageName, estimatedDuration: 60, // 1 hour default exitCriteria: [], id: `stage_${index + 1}`, name: stageName, order: index + 1, parallelExecution: false, prerequisites: index > 0 ? [`stage_${index}`] : [], tasks: [], }); }); return stages; } generatePerformanceInsights(executions) { // Generate performance insights return []; } generateRecommendations(execution, workflow) { const recommendations = []; if (execution.status !== "completed") { recommendations.push("Consider reviewing workflow prerequisites and dependencies"); } if (execution.progress.overallPercent < 50) { recommendations.push("Workflow progress is below 50% - review stage definitions"); } return recommendations; } async generateReportContent(execution, workflow, reportType) { // Generate report content based on type const summary = { outputsGenerated: execution.results.length, participantCount: execution.participants.length, stagesCompleted: execution.progress.completedStages, successRate: execution.status === "completed" ? 1 : 0, tasksCompleted: execution.progress.completedTasks, totalDuration: execution.completedAt ? new Date(execution.completedAt).getTime() - new Date(execution.startedAt).getTime() : 0, }; const stages = workflow.stages.map((stage) => ({ duration: stage.estimatedDuration, issues: [], outputs: stage.deliverables, stageId: stage.id, stageName: stage.name, status: execution.results.find((r) => r.stageId === stage.id) ? "completed" : "in_progress", tasksCompleted: stage.tasks.length, tasksTotal: stage.tasks.length, })); const performance = { averageStageTime: workflow.metadata.estimatedDuration / workflow.stages.length, bottlenecks: [], efficiency: summary.successRate, resourceUtilization: {}, throughput: execution.results.length / (summary.totalDuration / 1000 / 60 / 60), // per hour }; const errors = execution.results .filter((r) => !r.approved) .map((r) => ({ errorId: `error_${r.stageId}`, errorType: "execution", impact: "medium", message: r.notes || "Stage execution failed", stage: r.stageId, timestamp: r.timestamp, })); return { errors, performance, recommendations: this.generateRecommendations(execution, workflow), stages, summary, }; } async getWorkflowExecutions(workflowId, hubId) { try { const memories = await this.memoryService.searchAdvanced(hubId, `workflow_execution ${workflowId}`, { memoryType: "workflow" }); const executions = []; for (const memory of memories) { try { const execution = JSON.parse(memory.content); if (execution.workflowId === workflowId) { executions.push(execution); } } catch (parseError) { continue; } } return executions; } catch (error) { return []; } } interpretWorkflowRequest(request) { // Interpret natural language request return `Interpreted request: ${request}`; } async storeWorkflowExecution(execution, signer, hubId) { const executionMemory = { content: JSON.stringify(execution), context: { domain: "workflow_execution", sessionId: execution.id, topic: `execution_${execution.id}`, }, importance: 0.9, memoryType: "workflow", }; await this.memoryService.addEnhanced(signer, hubId, executionMemory); } // Private helper methods validateWorkflowDefinition(workflow) { if (!workflow.id || !workflow.name || !workflow.stages) { throw new Error("Workflow must have id, name, and stages"); } if (workflow.stages.length === 0) { throw new Error("Workflow must have at least one stage"); } // Validate stage order const orders = workflow.stages.map((s) => s.order); const uniqueOrders = new Set(orders); if (uniqueOrders.size !== orders.length) { throw new Error("Stage orders must be unique"); } } } // Export singleton service factory export const createWorkflowAutomationService = (teamAgentService, processService, memoryService) => { return new WorkflowAutomationServiceImpl(teamAgentService, processService, memoryService); };