shipdeck
Version:
Ship MVPs in 48 hours. Fix bugs in 30 seconds. The command deck for developers who ship.
989 lines (846 loc) • 30.3 kB
JavaScript
/**
* Workflow Integration Module for Shipdeck Ultimate
*
* Bridges the agent system with the DAG workflow engine, providing:
* - Agent-to-workflow task conversion
* - Parallel agent execution in workflows
* - Quality gate integration
* - Rollback and checkpoint support
* - Error recovery and retry mechanisms
* - Comprehensive workflow status tracking
*
* This module serves as the orchestration layer between agents and workflows,
* enabling complex multi-agent deployments with full error handling and recovery.
*
* @author ShipDeck Team
* @version 2.0.0
*/
const EventEmitter = require('events');
const { DAGWorkflow } = require('../workflow/dag-workflow');
const { WorkflowExecutor } = require('../workflow/executor');
const { QualityGatesSystem } = require('../quality-gates');
const { CheckpointManager } = require('../rollback/checkpoint-manager');
const { registry: agentRegistry } = require('./agent-registry');
/**
* Agent Task Converter - Transforms agent tasks into workflow nodes
*/
class AgentTaskConverter {
constructor() {
// Task type mappings for different agent categories
this.agentTypeMap = {
'backend-architect': 'development',
'frontend-developer': 'development',
'ai-engineer': 'development',
'devops-automator': 'deployment',
'mobile-app-builder': 'development',
'rapid-prototyper': 'development',
'test-writer-fixer': 'testing',
'whimsy-injector': 'enhancement',
'ui-designer': 'design',
'ux-researcher': 'research',
'visual-storyteller': 'content',
'brand-guardian': 'validation',
'tiktok-strategist': 'marketing',
'growth-hacker': 'marketing',
'content-creator': 'content',
'instagram-curator': 'marketing',
'reddit-community-builder': 'marketing',
'twitter-engager': 'marketing',
'app-store-optimizer': 'marketing',
'feedback-synthesizer': 'analysis',
'sprint-prioritizer': 'planning',
'trend-researcher': 'research',
'analytics-reporter': 'analysis',
'finance-tracker': 'analysis',
'infrastructure-maintainer': 'maintenance',
'legal-compliance-checker': 'validation',
'support-responder': 'support',
'experiment-tracker': 'analysis',
'project-shipper': 'deployment',
'studio-producer': 'coordination',
'api-tester': 'testing',
'performance-benchmarker': 'testing',
'test-results-analyzer': 'analysis',
'tool-evaluator': 'evaluation',
'workflow-optimizer': 'optimization',
'task-coordinator': 'coordination',
'parallel-runner': 'execution',
'result-synthesizer': 'coordination',
'llm-judge': 'evaluation',
'variant-generator': 'generation',
'worktree-manager': 'coordination',
'conflict-detector': 'validation',
'orchestrator': 'coordination',
'joker': 'enhancement',
'studio-coach': 'support'
};
}
/**
* Convert agent task to workflow node
* @param {Object} agentTask - Agent task configuration
* @param {Object} options - Conversion options
* @returns {Object} Workflow node configuration
*/
convertToWorkflowNode(agentTask, options = {}) {
if (!agentTask.agent) {
throw new Error('Agent task must specify an agent type');
}
const agentType = this.agentTypeMap[agentTask.agent] || 'general';
const nodeId = options.nodeId || this.generateNodeId(agentTask.agent);
return {
id: nodeId,
name: agentTask.name || `${agentTask.agent} Task`,
description: agentTask.description || `Execute ${agentTask.agent} task`,
agent: agentTask.agent,
type: agentType,
prompt: this.buildAgentPrompt(agentTask),
inputs: agentTask.inputs || {},
outputs: agentTask.outputs || {},
dependencies: agentTask.dependencies || [],
condition: agentTask.condition || null,
retryConfig: {
maxRetries: agentTask.retries || 3,
retryDelay: agentTask.retryDelay || 1000,
retryMultiplier: agentTask.retryMultiplier || 2,
...agentTask.retryConfig
},
timeout: agentTask.timeout || 300000, // 5 minutes default
parallel: agentTask.parallel || false,
qualityGates: agentTask.qualityGates !== false, // Enabled by default
checkpoint: agentTask.checkpoint !== false, // Enabled by default
metadata: {
agentType,
originalTask: agentTask,
createdAt: new Date().toISOString(),
...agentTask.metadata
}
};
}
/**
* Convert multiple agent tasks to workflow nodes with dependency resolution
* @param {Array<Object>} agentTasks - Array of agent tasks
* @param {Object} options - Conversion options
* @returns {Array<Object>} Array of workflow nodes
*/
convertMultipleToWorkflowNodes(agentTasks, options = {}) {
const nodes = [];
const nodeMap = new Map();
// First pass: Create nodes
for (let i = 0; i < agentTasks.length; i++) {
const task = agentTasks[i];
const nodeId = options.nodeIdPrefix ?
`${options.nodeIdPrefix}-${i}` :
this.generateNodeId(task.agent, i);
const node = this.convertToWorkflowNode(task, { ...options, nodeId });
nodes.push(node);
nodeMap.set(task.id || i, node.id);
}
// Second pass: Resolve dependencies
for (let i = 0; i < agentTasks.length; i++) {
const task = agentTasks[i];
const node = nodes[i];
if (task.dependsOn && task.dependsOn.length > 0) {
node.dependencies = task.dependsOn.map(depId => {
const resolvedId = nodeMap.get(depId);
if (!resolvedId) {
throw new Error(`Dependency '${depId}' not found for task '${task.id || i}'`);
}
return resolvedId;
});
}
}
return nodes;
}
/**
* Build agent-specific prompt
* @param {Object} agentTask - Agent task configuration
* @returns {string} Agent prompt
*/
buildAgentPrompt(agentTask) {
let prompt = agentTask.prompt || '';
if (agentTask.context) {
prompt += `\n\nContext: ${JSON.stringify(agentTask.context, null, 2)}`;
}
if (agentTask.requirements) {
const requirements = Array.isArray(agentTask.requirements)
? agentTask.requirements.join('\n- ')
: agentTask.requirements;
prompt += `\n\nRequirements:\n- ${requirements}`;
}
return prompt.trim();
}
/**
* Generate unique node ID
* @param {string} agentType - Agent type
* @param {number} index - Optional index
* @returns {string} Node ID
*/
generateNodeId(agentType, index = 0) {
const timestamp = Date.now().toString(36);
const suffix = index > 0 ? `-${index}` : '';
return `${agentType}-${timestamp}${suffix}`;
}
}
/**
* Agent Workflow Status Tracker
*/
class AgentWorkflowStatusTracker extends EventEmitter {
constructor() {
super();
this.workflowStatuses = new Map();
this.nodeStatuses = new Map();
this.agentExecutionHistory = new Map();
}
/**
* Track workflow status
* @param {string} workflowId - Workflow ID
* @param {Object} status - Status information
*/
updateWorkflowStatus(workflowId, status) {
const previousStatus = this.workflowStatuses.get(workflowId);
this.workflowStatuses.set(workflowId, {
...status,
updatedAt: new Date().toISOString(),
previousStatus: previousStatus?.status
});
this.emit('workflow:status:updated', { workflowId, status, previousStatus });
}
/**
* Track node status
* @param {string} workflowId - Workflow ID
* @param {string} nodeId - Node ID
* @param {Object} status - Status information
*/
updateNodeStatus(workflowId, nodeId, status) {
const key = `${workflowId}:${nodeId}`;
const previousStatus = this.nodeStatuses.get(key);
const nodeStatus = {
workflowId,
nodeId,
...status,
updatedAt: new Date().toISOString(),
previousStatus: previousStatus?.status
};
this.nodeStatuses.set(key, nodeStatus);
// Track agent execution history
if (status.agent) {
this.trackAgentExecution(status.agent, nodeStatus);
}
this.emit('node:status:updated', { workflowId, nodeId, status: nodeStatus, previousStatus });
}
/**
* Track agent execution history
* @param {string} agentType - Agent type
* @param {Object} execution - Execution details
*/
trackAgentExecution(agentType, execution) {
if (!this.agentExecutionHistory.has(agentType)) {
this.agentExecutionHistory.set(agentType, []);
}
const history = this.agentExecutionHistory.get(agentType);
history.push({
...execution,
executedAt: new Date().toISOString()
});
// Keep only last 100 executions per agent
if (history.length > 100) {
history.splice(0, history.length - 100);
}
}
/**
* Get workflow status
* @param {string} workflowId - Workflow ID
* @returns {Object|null} Workflow status
*/
getWorkflowStatus(workflowId) {
return this.workflowStatuses.get(workflowId) || null;
}
/**
* Get all node statuses for a workflow
* @param {string} workflowId - Workflow ID
* @returns {Array<Object>} Node statuses
*/
getWorkflowNodeStatuses(workflowId) {
const statuses = [];
for (const [key, status] of this.nodeStatuses.entries()) {
if (key.startsWith(`${workflowId}:`)) {
statuses.push(status);
}
}
return statuses.sort((a, b) =>
new Date(a.updatedAt) - new Date(b.updatedAt)
);
}
/**
* Get agent execution statistics
* @param {string} agentType - Agent type (optional)
* @returns {Object} Execution statistics
*/
getAgentStatistics(agentType = null) {
if (agentType) {
const history = this.agentExecutionHistory.get(agentType) || [];
return this.calculateAgentStats(agentType, history);
}
const allStats = {};
for (const [agent, history] of this.agentExecutionHistory.entries()) {
allStats[agent] = this.calculateAgentStats(agent, history);
}
return allStats;
}
/**
* Calculate agent statistics from history
* @param {string} agentType - Agent type
* @param {Array<Object>} history - Execution history
* @returns {Object} Statistics
*/
calculateAgentStats(agentType, history) {
if (history.length === 0) {
return {
agent: agentType,
totalExecutions: 0,
successRate: 0,
averageExecutionTime: 0,
lastExecuted: null
};
}
const successful = history.filter(h => h.status === 'completed').length;
const totalTime = history.reduce((sum, h) => sum + (h.duration || 0), 0);
return {
agent: agentType,
totalExecutions: history.length,
successRate: (successful / history.length) * 100,
averageExecutionTime: totalTime / history.length,
lastExecuted: history[history.length - 1]?.executedAt || null,
recentFailures: history.filter(h =>
h.status === 'failed' &&
Date.now() - new Date(h.executedAt).getTime() < 3600000 // Last hour
).length
};
}
}
/**
* Parallel Agent Executor - Handles concurrent agent execution
*/
class ParallelAgentExecutor {
constructor(options = {}) {
this.maxConcurrency = options.maxConcurrency || 5;
this.defaultTimeout = options.defaultTimeout || 300000; // 5 minutes
}
/**
* Execute agents in parallel with proper isolation
* @param {Array<Object>} agentTasks - Agent tasks to execute
* @param {Object} context - Shared execution context
* @returns {Promise<Object>} Execution results
*/
async executeParallel(agentTasks, context = {}) {
const results = new Map();
const errors = new Map();
const startTime = Date.now();
// Group tasks by parallelization compatibility
const parallelGroups = this.groupTasksForParallelExecution(agentTasks);
console.log(`🚀 Executing ${agentTasks.length} agent tasks in ${parallelGroups.length} parallel groups`);
for (const group of parallelGroups) {
await this.executeGroup(group, context, results, errors);
}
const duration = Date.now() - startTime;
const successCount = results.size;
const errorCount = errors.size;
console.log(`✅ Parallel execution completed: ${successCount} succeeded, ${errorCount} failed (${duration}ms)`);
return {
success: errorCount === 0,
results: Object.fromEntries(results),
errors: Object.fromEntries(errors),
statistics: {
totalTasks: agentTasks.length,
successCount,
errorCount,
duration,
parallelGroups: parallelGroups.length
}
};
}
/**
* Group tasks for parallel execution based on dependencies
* @param {Array<Object>} agentTasks - Agent tasks
* @returns {Array<Array<Object>>} Groups of tasks that can run in parallel
*/
groupTasksForParallelExecution(agentTasks) {
const groups = [];
const processed = new Set();
const taskMap = new Map(agentTasks.map(task => [task.id || task.name, task]));
while (processed.size < agentTasks.length) {
const currentGroup = [];
for (const task of agentTasks) {
const taskId = task.id || task.name;
if (processed.has(taskId)) continue;
// Check if all dependencies are satisfied
const canExecute = !task.dependencies ||
task.dependencies.every(depId => processed.has(depId));
if (canExecute) {
currentGroup.push(task);
processed.add(taskId);
}
}
if (currentGroup.length === 0) {
throw new Error('Circular dependency detected in agent tasks');
}
groups.push(currentGroup);
}
return groups;
}
/**
* Execute a group of tasks in parallel
* @param {Array<Object>} group - Tasks to execute in parallel
* @param {Object} context - Execution context
* @param {Map} results - Results map
* @param {Map} errors - Errors map
*/
async executeGroup(group, context, results, errors) {
const groupPromises = group.map(task =>
this.executeAgentTask(task, context)
.then(result => ({ task, result, success: true }))
.catch(error => ({ task, error, success: false }))
);
const groupResults = await Promise.allSettled(groupPromises);
for (const result of groupResults) {
const { task, success } = result.value || result.reason;
const taskId = task.id || task.name;
if (success) {
results.set(taskId, result.value.result);
} else {
errors.set(taskId, result.value?.error || result.reason);
}
}
}
/**
* Execute a single agent task
* @param {Object} agentTask - Agent task configuration
* @param {Object} context - Execution context
* @returns {Promise<Object>} Execution result
*/
async executeAgentTask(agentTask, context = {}) {
const agentName = agentTask.agent;
if (!agentRegistry.has(agentName)) {
throw new Error(`Agent '${agentName}' not registered`);
}
const agent = agentRegistry.get(agentName);
const startTime = Date.now();
try {
console.log(`🤖 Executing ${agentName} task...`);
const result = await Promise.race([
agent.executeWithRetry(agentTask, context),
this.createTimeoutPromise(agentTask.timeout || this.defaultTimeout)
]);
const duration = Date.now() - startTime;
console.log(`✅ ${agentName} completed in ${duration}ms`);
return {
...result,
agent: agentName,
duration,
completedAt: new Date().toISOString()
};
} catch (error) {
const duration = Date.now() - startTime;
console.error(`❌ ${agentName} failed after ${duration}ms: ${error.message}`);
throw {
agent: agentName,
error: error.message,
duration,
failedAt: new Date().toISOString()
};
}
}
/**
* Create timeout promise
* @param {number} timeout - Timeout in milliseconds
* @returns {Promise} Timeout promise
*/
createTimeoutPromise(timeout) {
return new Promise((_, reject) => {
setTimeout(() => {
reject(new Error(`Agent task timed out after ${timeout}ms`));
}, timeout);
});
}
}
/**
* Main Workflow Integration Class
*/
class WorkflowIntegration extends EventEmitter {
constructor(options = {}) {
super();
this.options = {
enableQualityGates: options.enableQualityGates !== false,
enableCheckpoints: options.enableCheckpoints !== false,
enableParallelExecution: options.enableParallelExecution !== false,
maxConcurrency: options.maxConcurrency || 5,
defaultTimeout: options.defaultTimeout || 300000, // 5 minutes
autoRetry: options.autoRetry !== false,
autoRollback: options.autoRollback !== false,
...options
};
// Initialize components
this.taskConverter = new AgentTaskConverter();
this.statusTracker = new AgentWorkflowStatusTracker();
this.parallelExecutor = new ParallelAgentExecutor(this.options);
// Initialize optional components
if (this.options.enableQualityGates) {
this.qualityGates = new QualityGatesSystem(this.options.qualityGatesConfig);
}
if (this.options.enableCheckpoints) {
this.checkpointManager = new CheckpointManager(this.options.checkpointConfig);
}
this.workflowExecutor = new WorkflowExecutor(this.options.workflowConfig);
// Active workflows and their contexts
this.activeWorkflows = new Map();
this.workflowResults = new Map();
// Setup event forwarding
this._setupEventHandlers();
}
/**
* Initialize the workflow integration system
*/
async initialize() {
console.log('🔧 Initializing Workflow Integration System...');
// Initialize checkpoint manager if enabled
if (this.checkpointManager) {
await this.checkpointManager.initialize();
}
console.log('✅ Workflow Integration System initialized');
return this;
}
/**
* Create workflow from agent tasks
* @param {Object} config - Workflow configuration
* @returns {Object} DAG Workflow instance
*/
createWorkflowFromAgents(config) {
if (!config.agentTasks || !Array.isArray(config.agentTasks)) {
throw new Error('agentTasks array is required');
}
// Convert agent tasks to workflow nodes
const nodes = this.taskConverter.convertMultipleToWorkflowNodes(
config.agentTasks,
config.conversionOptions
);
// Create base workflow configuration
const workflowConfig = {
name: config.name || 'Agent Workflow',
description: config.description || 'Workflow generated from agent tasks',
nodes,
context: config.context || {},
maxConcurrency: config.maxConcurrency || this.options.maxConcurrency,
timeout: config.timeout || this.options.defaultTimeout,
...config
};
// Create DAG workflow
const workflow = this.workflowExecutor.createWorkflow(workflowConfig);
// Add quality gates if enabled
if (this.options.enableQualityGates && this.qualityGates) {
this.qualityGates.createWorkflowIntegration(workflow);
}
return workflow;
}
/**
* Execute workflow with full integration features
* @param {Object} config - Workflow configuration
* @returns {Promise<Object>} Execution result
*/
async executeWorkflowWithIntegration(config) {
const workflowId = config.id || this.generateWorkflowId();
const startTime = Date.now();
try {
console.log(`🚀 Starting integrated workflow execution: ${workflowId}`);
this.emit('workflow:started', { workflowId, config });
// Create checkpoint before starting if enabled
let initialCheckpoint = null;
if (this.checkpointManager && this.options.enableCheckpoints) {
const checkpointResult = await this.checkpointManager.createCheckpoint({
type: 'workflow-start',
description: `Pre-execution checkpoint for workflow ${workflowId}`,
workflow: workflowId,
tags: ['workflow', 'pre-execution']
});
if (checkpointResult.success) {
initialCheckpoint = checkpointResult.checkpoint;
console.log(`📝 Created initial checkpoint: ${initialCheckpoint.id}`);
}
}
// Track workflow
this.activeWorkflows.set(workflowId, {
id: workflowId,
config,
startTime,
initialCheckpoint,
status: 'running'
});
this.statusTracker.updateWorkflowStatus(workflowId, {
status: 'running',
startTime,
progress: 0
});
// Determine execution strategy
let result;
if (this.options.enableParallelExecution && config.parallelExecution) {
result = await this.executeParallelWorkflow(workflowId, config);
} else {
result = await this.executeSequentialWorkflow(workflowId, config);
}
// Create completion checkpoint if successful
if (result.success && this.checkpointManager) {
await this.checkpointManager.createCheckpoint({
type: 'workflow-complete',
description: `Completion checkpoint for workflow ${workflowId}`,
workflow: workflowId,
tags: ['workflow', 'success', 'completion']
});
}
const duration = Date.now() - startTime;
// Update tracking
this.statusTracker.updateWorkflowStatus(workflowId, {
status: result.success ? 'completed' : 'failed',
duration,
progress: 100,
result
});
this.workflowResults.set(workflowId, result);
this.activeWorkflows.delete(workflowId);
console.log(`${result.success ? '✅' : '❌'} Workflow ${workflowId} ${result.success ? 'completed' : 'failed'} in ${duration}ms`);
this.emit(result.success ? 'workflow:completed' : 'workflow:failed', {
workflowId,
result,
duration
});
return result;
} catch (error) {
console.error(`❌ Workflow execution failed: ${error.message}`);
// Attempt rollback if enabled and checkpoint exists
if (this.options.autoRollback && this.checkpointManager) {
await this.handleWorkflowFailure(workflowId, error);
}
const duration = Date.now() - startTime;
this.statusTracker.updateWorkflowStatus(workflowId, {
status: 'failed',
duration,
error: error.message
});
this.activeWorkflows.delete(workflowId);
this.emit('workflow:failed', { workflowId, error: error.message, duration });
throw error;
}
}
/**
* Execute workflow in parallel mode
* @param {string} workflowId - Workflow ID
* @param {Object} config - Workflow configuration
* @returns {Promise<Object>} Execution result
*/
async executeParallelWorkflow(workflowId, config) {
console.log(`⚡ Executing workflow ${workflowId} in parallel mode`);
return await this.parallelExecutor.executeParallel(
config.agentTasks,
config.context
);
}
/**
* Execute workflow in sequential mode using DAG executor
* @param {string} workflowId - Workflow ID
* @param {Object} config - Workflow configuration
* @returns {Promise<Object>} Execution result
*/
async executeSequentialWorkflow(workflowId, config) {
console.log(`🔄 Executing workflow ${workflowId} in sequential mode`);
const workflow = this.createWorkflowFromAgents(config);
// Setup workflow event tracking
this.setupWorkflowEventTracking(workflow, workflowId);
return await this.workflowExecutor.executeWorkflow({
...config,
id: workflowId
});
}
/**
* Handle workflow failure with rollback
* @param {string} workflowId - Workflow ID
* @param {Error} error - Error that caused failure
*/
async handleWorkflowFailure(workflowId, error) {
console.log(`🚨 Handling workflow failure: ${workflowId}`);
const workflowContext = this.activeWorkflows.get(workflowId);
if (workflowContext?.initialCheckpoint) {
console.log(`🔄 Rolling back to initial checkpoint: ${workflowContext.initialCheckpoint.id}`);
try {
await this.checkpointManager.rollback(
workflowContext.initialCheckpoint.id,
{
skipRecoveryPoint: false,
attemptRecovery: true
}
);
console.log('✅ Rollback completed successfully');
} catch (rollbackError) {
console.error(`❌ Rollback failed: ${rollbackError.message}`);
// Don't re-throw rollback errors, original error is more important
}
}
}
/**
* Setup workflow event tracking
* @param {Object} workflow - DAG workflow instance
* @param {string} workflowId - Workflow ID
*/
setupWorkflowEventTracking(workflow, workflowId) {
workflow.on('node:started', (data) => {
this.statusTracker.updateNodeStatus(workflowId, data.nodeId, {
status: 'running',
agent: data.agent,
startedAt: data.startedAt
});
});
workflow.on('node:completed', (data) => {
this.statusTracker.updateNodeStatus(workflowId, data.nodeId, {
status: 'completed',
agent: data.agent,
result: data.result,
duration: data.duration,
completedAt: data.completedAt
});
});
workflow.on('node:failed', (data) => {
this.statusTracker.updateNodeStatus(workflowId, data.nodeId, {
status: 'failed',
agent: data.agent,
error: data.error,
duration: data.duration,
failedAt: data.failedAt
});
});
}
/**
* Get workflow status and progress
* @param {string} workflowId - Workflow ID
* @returns {Object|null} Workflow status
*/
getWorkflowStatus(workflowId) {
const status = this.statusTracker.getWorkflowStatus(workflowId);
const nodeStatuses = this.statusTracker.getWorkflowNodeStatuses(workflowId);
if (!status) return null;
return {
...status,
nodes: nodeStatuses,
isActive: this.activeWorkflows.has(workflowId)
};
}
/**
* Get agent execution statistics
* @param {string} agentType - Optional agent type filter
* @returns {Object} Agent statistics
*/
getAgentStatistics(agentType = null) {
return this.statusTracker.getAgentStatistics(agentType);
}
/**
* Create checkpoint for current state
* @param {Object} metadata - Checkpoint metadata
* @returns {Promise<Object>} Checkpoint result
*/
async createCheckpoint(metadata = {}) {
if (!this.checkpointManager) {
throw new Error('Checkpoint manager not enabled');
}
return await this.checkpointManager.createCheckpoint(metadata);
}
/**
* Rollback to a specific checkpoint
* @param {string} checkpointId - Checkpoint ID
* @param {Object} options - Rollback options
* @returns {Promise<Object>} Rollback result
*/
async rollbackToCheckpoint(checkpointId, options = {}) {
if (!this.checkpointManager) {
throw new Error('Checkpoint manager not enabled');
}
return await this.checkpointManager.rollback(checkpointId, options);
}
/**
* Apply quality gates to artifact
* @param {Object} artifact - Artifact to validate
* @param {Object} context - Validation context
* @returns {Promise<Object>} Quality gate result
*/
async applyQualityGates(artifact, context = {}) {
if (!this.qualityGates) {
throw new Error('Quality gates not enabled');
}
return await this.qualityGates.applyQualityGates(artifact, context);
}
/**
* Generate unique workflow ID
* @returns {string} Workflow ID
*/
generateWorkflowId() {
const timestamp = Date.now().toString(36);
const random = Math.random().toString(36).substr(2, 5);
return `workflow-${timestamp}-${random}`;
}
/**
* Setup event handlers for internal components
* @private
*/
_setupEventHandlers() {
// Forward status tracker events
this.statusTracker.on('workflow:status:updated', (data) => {
this.emit('status:workflow', data);
});
this.statusTracker.on('node:status:updated', (data) => {
this.emit('status:node', data);
});
// Forward workflow executor events
this.workflowExecutor.on('workflow:started', (data) => {
this.emit('executor:workflow:started', data);
});
this.workflowExecutor.on('workflow:finished', (data) => {
this.emit('executor:workflow:finished', data);
});
this.workflowExecutor.on('node:completed', (data) => {
this.emit('executor:node:completed', data);
});
this.workflowExecutor.on('node:failed', (data) => {
this.emit('executor:node:failed', data);
});
// Forward quality gates events if enabled
if (this.qualityGates) {
this.qualityGates.on('gates:success', (data) => {
this.emit('quality:success', data);
});
this.qualityGates.on('gates:failed', (data) => {
this.emit('quality:failed', data);
});
}
// Forward checkpoint events if enabled
if (this.checkpointManager) {
this.checkpointManager.on('checkpoint:created', (data) => {
this.emit('checkpoint:created', data);
});
this.checkpointManager.on('rollback:success', (data) => {
this.emit('rollback:success', data);
});
this.checkpointManager.on('rollback:failed', (data) => {
this.emit('rollback:failed', data);
});
}
}
}
// Export all classes and main integration
module.exports = {
WorkflowIntegration,
AgentTaskConverter,
AgentWorkflowStatusTracker,
ParallelAgentExecutor,
// Factory function for easy initialization
createWorkflowIntegration: (options = {}) => new WorkflowIntegration(options),
// Convenience functions
convertAgentTasksToWorkflow: (agentTasks, options = {}) => {
const converter = new AgentTaskConverter();
return converter.convertMultipleToWorkflowNodes(agentTasks, options);
}
};