"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AgentOrchestrator = void 0;
/**
 * AgentOrchestrator
 *
 * Minimal version focused on:
 * - Sequential execution (no parallel DAG yet)
 * - Simple lifecycle management
 * - Progress tracking
 * - Error handling with retries
 */
class AgentOrchestrator {
    constructor(registry, messageBus, spawner, config) {
        this.registry = registry;
        this.messageBus = messageBus;
        this.spawner = spawner;
        this.config = {
            enableLogging: config?.enableLogging ?? true,
            stopOnError: config?.stopOnError ?? true,
            maxRetries: config?.maxRetries ?? 2,
            retryDelayMs: config?.retryDelayMs ?? 1000,
        };
        this.executionHistory = [];
        this.isRunning = false;
    }
    /**
     * Execute a task end-to-end
     */
    async execute(taskDescription, context) {
        if (this.isRunning) {
            throw new Error("Orchestrator is already running a task");
        }
        this.isRunning = true;
        const startTime = Date.now();
        try {
            // Step 1: Analyze and create plan
            this.log("Analyzing task and creating execution plan...");
            this.currentPlan = await this.spawner.analyzeAndPlan(taskDescription, context);
            this.log(`Plan created: ${this.currentPlan.steps.length} steps, estimated ${this.currentPlan.totalDurationMs}ms`);
            // Step 2: Execute plan sequentially
            const result = await this.executePlan(this.currentPlan, context);
            // Step 3: Record completion
            this.recordEvent({
                type: "orchestration-completed",
                timestamp: new Date().toISOString(),
            });
            return {
                ...result,
                durationMs: Date.now() - startTime,
            };
        }
        catch (error) {
            this.recordEvent({
                type: "orchestration-failed",
                timestamp: new Date().toISOString(),
                error: error instanceof Error ? error.message : String(error),
            });
            return {
                success: false,
                completedSteps: 0,
                totalSteps: this.currentPlan?.steps.length || 0,
                error: error instanceof Error ? error.message : String(error),
                durationMs: Date.now() - startTime,
                results: new Map(),
            };
        }
        finally {
            this.isRunning = false;
            this.currentPlan = undefined;
        }
    }
    /**
     * Execute plan sequentially
     */
    async executePlan(plan, globalContext) {
        const results = new Map();
        let completedSteps = 0;
        for (let i = 0; i < plan.steps.length; i++) {
            const step = plan.steps[i];
            if (!step)
                continue;
            this.log(`[Step ${i + 1}/${plan.steps.length}] Executing: ${step.description}`);
            try {
                // Execute step with retries
                const result = await this.executeStepWithRetry(step, globalContext, results);
                results.set(step.stepId, result);
                completedSteps++;
                this.log(`[Step ${i + 1}/${plan.steps.length}] ✓ Completed`);
            }
            catch (error) {
                this.log(`[Step ${i + 1}/${plan.steps.length}] ✗ Failed: ${error instanceof Error ? error.message : String(error)}`);
                if (this.config.stopOnError) {
                    return {
                        success: false,
                        completedSteps,
                        totalSteps: plan.steps.length,
                        failedStep: step.stepId,
                        error: error instanceof Error ? error.message : String(error),
                        durationMs: 0, // Will be set by caller
                        results,
                    };
                }
                else {
                    // Record error but continue
                    results.set(step.stepId, {
                        error: error instanceof Error ? error.message : String(error),
                    });
                    completedSteps++;
                }
            }
        }
        return {
            success: true,
            completedSteps,
            totalSteps: plan.steps.length,
            durationMs: 0, // Will be set by caller
            results,
        };
    }
    /**
     * Execute a single step with retry logic
     */
    async executeStepWithRetry(step, globalContext, previousResults) {
        let lastError;
        for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {
            if (attempt > 0) {
                this.log(`  Retry attempt ${attempt}/${this.config.maxRetries}...`);
                await this.delay(this.config.retryDelayMs);
            }
            try {
                return await this.executeStep(step, globalContext, previousResults);
            }
            catch (error) {
                lastError = error instanceof Error ? error : new Error(String(error));
                // Check if error is retryable
                if (!this.isRetryableError(lastError)) {
                    throw lastError;
                }
            }
        }
        throw lastError || new Error("Step execution failed");
    }
    /**
     * Execute a single step
     */
    async executeStep(step, globalContext, previousResults) {
        const stepStartTime = Date.now();
        // Record step start
        this.recordEvent({
            type: "step-started",
            timestamp: new Date().toISOString(),
            stepId: step.stepId,
            agentType: step.agentType,
        });
        // Broadcast step start
        await this.messageBus.broadcast("orchestrator:step:started", {
            stepId: step.stepId,
            agentType: step.agentType,
            description: step.description,
        });
        try {
            // Spawn agent instance
            const instances = await this.spawner.spawnAgents({
                steps: [step],
                totalDurationMs: step.estimatedDurationMs,
                totalContextBudgetKB: step.contextBudgetKB,
                canRunInParallel: false,
                warnings: [],
            });
            const instance = instances[0];
            if (!instance) {
                throw new Error(`Failed to spawn agent for step ${step.stepId}`);
            }
            // Build context for agent
            const agentContext = this.buildAgentContext(step, globalContext, previousResults);
            // Update instance with context
            this.spawner.updateInstanceStatus(instance.instanceId, {
                status: "running",
                allocatedContext: agentContext,
                contextSizeKB: this.estimateContextSize(agentContext),
            });
            // Execute agent (in production, this would call actual agent implementation)
            const result = await this.runAgent(instance, agentContext);
            // Update instance to completed
            this.spawner.updateInstanceStatus(instance.instanceId, {
                status: "completed",
                completedAt: new Date().toISOString(),
                progress: {
                    current: 100,
                    message: "Completed",
                },
            });
            // Record step completion
            this.recordEvent({
                type: "step-completed",
                timestamp: new Date().toISOString(),
                stepId: step.stepId,
                agentType: step.agentType,
                instanceId: instance.instanceId,
                result,
            });
            // Broadcast step completion
            await this.messageBus.broadcast("orchestrator:step:completed", {
                stepId: step.stepId,
                agentType: step.agentType,
                instanceId: instance.instanceId,
                durationMs: Date.now() - stepStartTime,
            });
            // Clean up instance
            this.spawner.removeInstance(instance.instanceId);
            return result;
        }
        catch (error) {
            // Record step failure
            this.recordEvent({
                type: "step-failed",
                timestamp: new Date().toISOString(),
                stepId: step.stepId,
                agentType: step.agentType,
                error: error instanceof Error ? error.message : String(error),
            });
            // Broadcast step failure
            await this.messageBus.broadcast("orchestrator:step:failed", {
                stepId: step.stepId,
                agentType: step.agentType,
                error: error instanceof Error ? error.message : String(error),
            });
            throw error;
        }
    }
    /**
     * Run agent (placeholder - will be implemented with actual agent execution)
     */
    async runAgent(instance, context) {
        // This is a placeholder that simulates agent execution
        // In production, this would:
        // 1. Load the actual agent implementation
        // 2. Pass context to the agent
        // 3. Execute agent's main logic
        // 4. Return agent's result
        // For now, just simulate with a delay
        await this.delay(100);
        return {
            agentType: instance.agentType,
            status: "success",
            message: `Agent ${instance.agentType} completed successfully`,
            context,
        };
    }
    /**
     * Build context for agent from global context and previous results
     */
    buildAgentContext(step, globalContext, previousResults) {
        const context = {
            ...globalContext,
        };
        // Add required context from capability
        for (const contextKey of step.agentMatch.capability.requiredContext) {
            if (globalContext?.[contextKey]) {
                context[contextKey] = globalContext[contextKey];
            }
        }
        // Add results from dependency steps
        if (previousResults) {
            for (const depStepId of step.dependencies) {
                const depResult = previousResults.get(depStepId);
                if (depResult) {
                    context[`dependency:${depStepId}`] = depResult;
                }
            }
        }
        return context;
    }
    /**
     * Estimate context size in KB
     */
    estimateContextSize(context) {
        const json = JSON.stringify(context);
        return Math.ceil(json.length / 1024);
    }
    /**
     * Check if error is retryable
     */
    isRetryableError(error) {
        const retryablePatterns = [
            /timeout/i,
            /network/i,
            /connection/i,
            /temporary/i,
            /rate limit/i,
        ];
        return retryablePatterns.some((pattern) => pattern.test(error.message));
    }
    /**
     * Delay helper
     */
    delay(ms) {
        return new Promise((resolve) => setTimeout(resolve, ms));
    }
    /**
     * Record execution event
     */
    recordEvent(event) {
        this.executionHistory.push(event);
        // Limit history size
        if (this.executionHistory.length > 1000) {
            this.executionHistory.shift();
        }
    }
    /**
     * Log message
     */
    log(message) {
        if (this.config.enableLogging) {
            console.log(`[Orchestrator] ${message}`);
        }
    }
    /**
     * Get execution history
     */
    getExecutionHistory() {
        return [...this.executionHistory];
    }
    /**
     * Get current plan
     */
    getCurrentPlan() {
        return this.currentPlan;
    }
    /**
     * Check if orchestrator is running
     */
    isExecuting() {
        return this.isRunning;
    }
    /**
     * Get statistics
     */
    getStats() {
        const completions = this.executionHistory.filter((e) => e.type === "orchestration-completed" || e.type === "orchestration-failed");
        const successful = this.executionHistory.filter((e) => e.type === "orchestration-completed").length;
        const failed = this.executionHistory.filter((e) => e.type === "orchestration-failed").length;
        const stepEvents = this.executionHistory.filter((e) => e.type === "step-completed");
        const avgSteps = completions.length > 0 ? stepEvents.length / completions.length : 0;
        return {
            totalExecutions: completions.length,
            successfulExecutions: successful,
            failedExecutions: failed,
            averageStepsPerExecution: Math.round(avgSteps * 10) / 10,
        };
    }
}
exports.AgentOrchestrator = AgentOrchestrator;
//# sourceMappingURL=AgentOrchestrator.js.map