// Production Orchestrator - Coordinates all agents in the production pipeline
import { agentRegistry } from './agent-registry.js';
import { ReviewBoard } from './review-board.js';
import { DocControl } from './doc-control.js';
export class ProductionOrchestrator {
constructor() {
this.reviewBoard = new ReviewBoard();
this.docControl = new DocControl();
this.activeWorkflows = new Map();
this.taskQueue = [];
}
async initialize() {
await agentRegistry.initialize();
await this.reviewBoard.initialize();
await this.docControl.initialize();
console.log('🎭 Production Orchestrator initialized');
}
// Main entry point for automated production
async runProductionWorkflow(workflowSpec) {
console.log('🚀 Starting automated production workflow:', workflowSpec.name);
try {
// Create workflow in database
const workflow = await this.createWorkflow(workflowSpec);
// Plan production steps
const productionPlan = await this.planProduction(workflow, workflowSpec);
// Execute production pipeline with agent oversight
const results = await this.executeProductionPlan(workflow, productionPlan);
// Final quality assurance
const finalApproval = await this.reviewBoard.finalReview(workflow, results);
if (finalApproval.approved) {
await this.completeWorkflow(workflow, results);
console.log('✅ Production workflow completed successfully');
return {
success: true,
workflow: workflow,
results: results,
finalApproval: finalApproval
};
} else {
console.log('❌ Production workflow failed final review');
return {
success: false,
workflow: workflow,
errors: finalApproval.issues
};
}
} catch (error) {
console.error('❌ Production workflow failed:', error);
return {
success: false,
error: error.message
};
}
}
async createWorkflow(spec) {
const { Pool } = await import('pg');
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
try {
const orchestratorAgent = agentRegistry.getAgent('orchestrator');
const query = `
INSERT INTO workflows (name, description, storyline_id, created_by, metadata)
VALUES ($1, $2, $3, $4, $5)
RETURNING *
`;
const result = await pool.query(query, [
spec.name,
spec.description,
spec.storylineId || null,
orchestratorAgent.id,
JSON.stringify(spec)
]);
const workflow = result.rows[0];
this.activeWorkflows.set(workflow.id, workflow);
console.log(`📋 Created workflow "${spec.name}" with ID ${workflow.id}`);
return workflow;
} finally {
await pool.end();
}
}
// Use GPT-5 to analyze requirements and plan production steps
async planProduction(workflow, spec) {
const orchestrator = agentRegistry.getAgent('orchestrator');
const planningPrompt = `
You are the Production Orchestrator for an automated puppet production studio.
Plan the complete production pipeline for this project:
Project Details:
- Name: ${spec.name}
- Description: ${spec.description}
- Character: ${spec.characterName || 'To be created'}
- Type: ${spec.type || 'Full production'}
- Duration Target: ${spec.durationTarget || '30-60 seconds'}
- Style: ${spec.style || 'Puppet animation'}
Available Production Tools:
1. process_character_image - Create puppet character from image
2. generate_production_script - AI-generated script with dialogue
3. breakdown_script_to_scenes - Scene-by-scene breakdown
4. generate_scene_images - Create visual assets for each scene
5. generate_scene_audio - ElevenLabs voice synthesis
6. create_video_production_manifest - Final assembly instructions
Create a detailed production plan with:
1. Step-by-step task sequence
2. Dependencies between tasks
3. Quality control checkpoints
4. Resource requirements
5. Timeline estimates
Return a JSON production plan.
`;
const response = await agentRegistry.callAgent('orchestrator', planningPrompt, {
systemPrompt: 'You are an expert production planner. Always respond with valid JSON.',
jsonMode: true,
maxTokens: 4096
});
if (!response.success) {
throw new Error(`Production planning failed: ${response.error}`);
}
const plan = JSON.parse(response.response);
// Store plan in workflow metadata
await this.updateWorkflowMetadata(workflow.id, { productionPlan: plan });
console.log('📋 Production plan created:', plan.steps?.length || 0, 'steps');
return plan;
}
// Execute production plan with agent coordination
async executeProductionPlan(workflow, plan) {
const results = {
completedTasks: [],
artifacts: [],
approvals: [],
errors: []
};
for (const step of plan.steps) {
console.log(`\n🎬 Executing step: ${step.name}`);
try {
// Create task in database
const task = await this.createTask(workflow.id, step);
// Execute with agent coordination
const stepResult = await this.executeStepWithAgents(task, step);
if (stepResult.success) {
results.completedTasks.push(stepResult);
if (stepResult.artifacts) {
results.artifacts.push(...stepResult.artifacts);
}
console.log(`✅ Step "${step.name}" completed successfully`);
} else {
results.errors.push({
step: step.name,
error: stepResult.error
});
// Handle failure based on step criticality
if (step.critical !== false) {
throw new Error(`Critical step failed: ${step.name} - ${stepResult.error}`);
} else {
console.log(`⚠️ Non-critical step failed: ${step.name} - ${stepResult.error}`);
}
}
} catch (error) {
console.error(`❌ Step "${step.name}" failed:`, error);
results.errors.push({
step: step.name,
error: error.message
});
if (step.critical !== false) {
throw error; // Stop execution for critical steps
}
}
}
return results;
}
// Execute individual step with propose→critique→revise→approve workflow
async executeStepWithAgents(task, step) {
console.log(`🤖 Starting agent coordination for: ${step.name}`);
let iteration = 0;
const maxIterations = step.maxIterations || 3;
while (iteration < maxIterations) {
iteration++;
console.log(` 🔄 Iteration ${iteration}/${maxIterations}`);
try {
// 1. Automation Engineer executes the step
const automationResult = await this.executeAutomationStep(task, step);
if (!automationResult.success) {
return automationResult;
}
// 2. Creative Director reviews
const creativeReview = await this.reviewBoard.creativeReview(
task,
automationResult.artifact,
step.requirements
);
// 3. Brand Guardian checks compliance
const brandReview = await this.reviewBoard.brandReview(
task,
automationResult.artifact,
creativeReview
);
// 4. QA Auditor performs technical checks
const qaReview = await this.reviewBoard.qaReview(
task,
automationResult.artifact,
step.qualityChecks
);
// 5. Check if all reviews passed
if (creativeReview.approved && brandReview.approved && qaReview.approved) {
// Success! Document and return
await this.docControl.recordApproval(task, {
creative: creativeReview,
brand: brandReview,
qa: qaReview
});
console.log(` ✅ Step approved after ${iteration} iterations`);
return {
success: true,
task: task,
artifact: automationResult.artifact,
approvals: { creative: creativeReview, brand: brandReview, qa: qaReview },
iterations: iteration
};
}
// Collect feedback for next iteration
const combinedFeedback = this.combineFeedback([
creativeReview,
brandReview,
qaReview
]);
console.log(` 🔄 Revision needed:`, combinedFeedback.issues.length, 'issues');
// Update step with feedback for next iteration
step.feedback = combinedFeedback;
} catch (error) {
console.error(` ❌ Iteration ${iteration} failed:`, error);
if (iteration >= maxIterations) {
return {
success: false,
error: `Failed after ${maxIterations} iterations: ${error.message}`,
task: task
};
}
}
}
return {
success: false,
error: `Exceeded maximum iterations (${maxIterations})`,
task: task
};
}
async executeAutomationStep(task, step) {
// This will call the existing MCP production tools
const automationAgent = agentRegistry.getAgent('automation_engineer');
console.log(` 🔧 Automation Engineer executing: ${step.tool}`);
try {
// Map to existing production tools
const toolMapping = {
'process_character_image': 'process_character_image',
'generate_script': 'generate_production_script',
'breakdown_scenes': 'breakdown_script_to_scenes',
'generate_images': 'generate_scene_images',
'generate_audio': 'generate_scene_audio',
'create_manifest': 'create_video_production_manifest'
};
const mcpTool = toolMapping[step.tool];
if (!mcpTool) {
throw new Error(`Unknown tool: ${step.tool}`);
}
// Execute the MCP tool with the step parameters
const result = await this.executeMCPTool(mcpTool, step.parameters);
if (result.success) {
// Create artifact record
const artifact = await this.docControl.createArtifact(
task.id,
step.outputType || 'mixed',
result
);
return {
success: true,
result: result,
artifact: artifact
};
} else {
return {
success: false,
error: result.message || result.error || 'Tool execution failed'
};
}
} catch (error) {
return {
success: false,
error: error.message
};
}
}
// This will be the bridge to existing MCP tools
async executeMCPTool(toolName, parameters) {
// Import the existing tool handlers
const { getCharacterToolHandlers } = await import('../tools/character-tools.js');
const handlers = getCharacterToolHandlers();
const handler = handlers[toolName];
if (!handler) {
throw new Error(`MCP tool handler not found: ${toolName}`);
}
try {
const result = await handler(parameters);
return result;
} catch (error) {
throw new Error(`MCP tool execution failed: ${error.message}`);
}
}
combineFeedback(reviews) {
const allIssues = [];
const suggestions = [];
for (const review of reviews) {
if (!review.approved) {
allIssues.push(...(review.issues || []));
suggestions.push(...(review.suggestions || []));
}
}
return {
issues: allIssues,
suggestions: suggestions,
summary: `${allIssues.length} issues found across ${reviews.length} review stages`
};
}
async createTask(workflowId, step) {
const { Pool } = await import('pg');
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
try {
const query = `
INSERT INTO tasks (workflow_id, step, tool, payload, status)
VALUES ($1, $2, $3, $4, 'pending')
RETURNING *
`;
const result = await pool.query(query, [
workflowId,
step.name,
step.tool,
JSON.stringify(step.parameters || {})
]);
return result.rows[0];
} finally {
await pool.end();
}
}
async updateWorkflowMetadata(workflowId, metadata) {
const { Pool } = await import('pg');
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
try {
const query = `
UPDATE workflows
SET metadata = metadata || $1::jsonb,
updated_at = CURRENT_TIMESTAMP
WHERE id = $2
`;
await pool.query(query, [JSON.stringify(metadata), workflowId]);
} finally {
await pool.end();
}
}
async completeWorkflow(workflow, results) {
const { Pool } = await import('pg');
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
try {
const query = `
UPDATE workflows
SET status = 'completed',
completed_at = CURRENT_TIMESTAMP,
metadata = metadata || $1::jsonb
WHERE id = $2
`;
await pool.query(query, [JSON.stringify({ results: results }), workflow.id]);
this.activeWorkflows.delete(workflow.id);
} finally {
await pool.end();
}
}
}
// Singleton instance
export const productionOrchestrator = new ProductionOrchestrator();