shipdeck
Version:
Ship MVPs in 48 hours. Fix bugs in 30 seconds. The command deck for developers who ship.
154 lines (136 loc) • 4.43 kB
JavaScript
/**
* Worker Thread Script for Parallel Agent Execution
* This script runs in isolated worker threads to execute agents concurrently
*/
const { parentPort, workerData } = require('worker_threads');
const path = require('path');
/**
* Execute agent in worker thread
*/
async function executeAgentInWorker() {
try {
const { agentPath, agentConfig, task, context } = workerData;
// Validate worker data
if (!agentPath || !task || !context) {
throw new Error('Missing required worker data: agentPath, task, or context');
}
// Load agent class dynamically
let AgentClass;
try {
const agentModule = require(path.resolve(agentPath));
// Handle different export patterns
if (agentModule.default) {
AgentClass = agentModule.default;
} else if (typeof agentModule === 'function') {
AgentClass = agentModule;
} else {
// Look for common agent class names
const possibleClasses = Object.values(agentModule).filter(
value => typeof value === 'function' && value.prototype
);
if (possibleClasses.length === 1) {
AgentClass = possibleClasses[0];
} else {
throw new Error(`Could not determine agent class from ${agentPath}`);
}
}
} catch (error) {
throw new Error(`Failed to load agent from ${agentPath}: ${error.message}`);
}
// Initialize agent with configuration
const agent = new AgentClass({
...agentConfig,
// Override some settings for worker execution
config: {
...agentConfig.config,
enableLogging: false, // Reduce logging in workers
maxRetries: agentConfig.config?.maxRetries || 1
}
});
// Validate agent has required methods
if (typeof agent.executeWithRetry !== 'function') {
throw new Error(`Agent from ${agentPath} does not implement executeWithRetry method`);
}
// Execute task with enhanced context
const executionContext = {
...context,
workerId: process.env.WORKER_ID || 'unknown',
workerStartTime: Date.now(),
isWorkerThread: true
};
const startTime = Date.now();
const result = await agent.executeWithRetry(task, executionContext);
const duration = Date.now() - startTime;
// Send success result back to main thread
parentPort.postMessage({
type: 'success',
result: {
...result,
duration,
workerId: executionContext.workerId,
executedAt: new Date().toISOString()
},
taskId: context.taskId
});
} catch (error) {
// Send error back to main thread
parentPort.postMessage({
type: 'error',
error: {
message: error.message,
stack: error.stack,
name: error.constructor.name,
code: error.code,
timestamp: new Date().toISOString()
},
taskId: workerData.context?.taskId || 'unknown'
});
}
}
// Handle uncaught exceptions in worker
process.on('uncaughtException', (error) => {
parentPort.postMessage({
type: 'error',
error: {
message: `Uncaught exception in worker: ${error.message}`,
stack: error.stack,
name: error.constructor.name,
fatal: true,
timestamp: new Date().toISOString()
},
taskId: workerData.context?.taskId || 'unknown'
});
process.exit(1);
});
// Handle unhandled promise rejections in worker
process.on('unhandledRejection', (reason, promise) => {
parentPort.postMessage({
type: 'error',
error: {
message: `Unhandled promise rejection in worker: ${reason}`,
stack: reason?.stack || 'No stack trace available',
name: 'UnhandledPromiseRejection',
fatal: true,
timestamp: new Date().toISOString()
},
taskId: workerData.context?.taskId || 'unknown'
});
process.exit(1);
});
// Start agent execution
executeAgentInWorker().catch((error) => {
// This should not happen as executeAgentInWorker handles its own errors,
// but adding as a safety net
parentPort.postMessage({
type: 'error',
error: {
message: `Fatal error in worker execution: ${error.message}`,
stack: error.stack,
name: error.constructor.name,
fatal: true,
timestamp: new Date().toISOString()
},
taskId: workerData.context?.taskId || 'unknown'
});
process.exit(1);
});