/**
 * Agent Worker Pool Module
 *
 * Provides multi-agent parallelism with automatic load balancing,
 * work distribution, and result aggregation for AGI workflows.
 *
 * Principal Investigator: Bo Shang
 * Framework: agi-cli
 */
// ============================================================================
// Agent Worker
// ============================================================================
class AgentWorker {
    id;
    maxConcurrency;
    priority;
    tags;
    controller = null;
    createController;
    activeTasks = 0;
    _completedTasks = 0;
    _failedTasks = 0;
    _status = 'idle';
    constructor(config) {
        this.id = config.id;
        this.createController = config.createController;
        this.maxConcurrency = config.maxConcurrency ?? 1;
        this.priority = config.priority ?? 0;
        this.tags = new Set(config.tags ?? []);
    }
    get status() {
        return this._status;
    }
    get completedTasks() {
        return this._completedTasks;
    }
    get failedTasks() {
        return this._failedTasks;
    }
    get currentLoad() {
        return this.activeTasks;
    }
    get isAvailable() {
        return this._status !== 'offline' && this._status !== 'error' && this.activeTasks < this.maxConcurrency;
    }
    hasTags(requiredTags) {
        return requiredTags.every(tag => this.tags.has(tag));
    }
    async initialize() {
        if (this.controller)
            return;
        try {
            this.controller = await this.createController();
            this._status = 'idle';
        }
        catch (error) {
            this._status = 'error';
            throw error;
        }
    }
    async execute(task) {
        if (!this.controller) {
            await this.initialize();
        }
        if (!this.controller) {
            throw new Error(`Worker ${this.id} failed to initialize`);
        }
        this.activeTasks++;
        this._status = this.activeTasks >= this.maxConcurrency ? 'busy' : 'idle';
        try {
            let result = '';
            if (task.streaming && task.onEvent) {
                // Streaming mode: collect events and final result
                for await (const event of this.controller.send(task.message)) {
                    task.onEvent(event);
                    if (event.type === 'message.complete') {
                        result = event.content;
                    }
                    else if (event.type === 'message.delta') {
                        result += event.content;
                    }
                }
            }
            else {
                // Non-streaming: collect complete response
                for await (const event of this.controller.send(task.message)) {
                    if (event.type === 'message.complete') {
                        result = event.content;
                    }
                    else if (event.type === 'message.delta') {
                        result += event.content;
                    }
                }
            }
            this._completedTasks++;
            return task.transform ? task.transform(result) : result;
        }
        catch (error) {
            this._failedTasks++;
            throw error;
        }
        finally {
            this.activeTasks--;
            this._status = this.activeTasks > 0 ? 'busy' : 'idle';
        }
    }
    getInfo() {
        return {
            id: this.id,
            status: this._status,
            activeTasks: this.activeTasks,
            completedTasks: this._completedTasks,
            failedTasks: this._failedTasks,
            tags: Array.from(this.tags),
            priority: this.priority,
        };
    }
}
// ============================================================================
// Agent Worker Pool
// ============================================================================
export class AgentWorkerPool {
    workers = new Map();
    taskQueue = [];
    balanceStrategy;
    defaultTimeout;
    maxQueueSize;
    onEvent;
    roundRobinIndex = 0;
    totalTaskDuration = 0;
    completedTaskCount = 0;
    processing = false;
    constructor(config) {
        this.balanceStrategy = config.balanceStrategy ?? 'least-busy';
        this.defaultTimeout = config.defaultTimeout ?? 24 * 60 * 60 * 1000; // 24 hours default timeout
        this.maxQueueSize = config.maxQueueSize ?? 100;
        this.onEvent = config.onEvent ?? (() => { });
        for (const workerConfig of config.workers) {
            const worker = new AgentWorker(workerConfig);
            this.workers.set(worker.id, worker);
        }
    }
    /**
     * Initialize all workers
     */
    async initialize() {
        const initPromises = Array.from(this.workers.values()).map(async (worker) => {
            try {
                await worker.initialize();
                this.emit({ type: 'worker.ready', workerId: worker.id, timestamp: Date.now() });
            }
            catch (error) {
                this.emit({
                    type: 'worker.error',
                    workerId: worker.id,
                    timestamp: Date.now(),
                    data: { error: error instanceof Error ? error.message : String(error) },
                });
            }
        });
        await Promise.allSettled(initPromises);
    }
    /**
     * Submit a task to the pool
     */
    async submit(task) {
        if (this.taskQueue.length >= this.maxQueueSize) {
            throw new Error('Task queue is full');
        }
        return new Promise((resolve, reject) => {
            this.taskQueue.push({
                task: task,
                resolve: resolve,
                reject,
                queuedAt: Date.now(),
            });
            this.emit({
                type: 'task.queued',
                taskId: task.id,
                timestamp: Date.now(),
                data: { queueLength: this.taskQueue.length },
            });
            if (this.taskQueue.length >= this.maxQueueSize) {
                this.emit({ type: 'pool.saturated', timestamp: Date.now() });
            }
            // Trigger processing
            this.processQueue();
        });
    }
    /**
     * Submit multiple tasks and wait for all results
     */
    async submitAll(tasks) {
        const promises = tasks.map(task => this.submit(task));
        return Promise.all(promises);
    }
    /**
     * Submit multiple tasks and process results as they complete
     */
    async *submitStream(tasks) {
        const pending = tasks.map(task => this.submit(task));
        while (pending.length > 0) {
            const result = await Promise.race(pending.map((p, i) => p.then(r => ({ result: r, index: i }))));
            pending.splice(result.index, 1);
            yield result.result;
        }
    }
    /**
     * Process queued tasks
     */
    async processQueue() {
        if (this.processing)
            return;
        this.processing = true;
        try {
            while (this.taskQueue.length > 0) {
                const worker = this.selectWorker(this.taskQueue[0]?.task);
                if (!worker) {
                    // No available workers, wait and retry
                    await this.sleep(50);
                    continue;
                }
                const item = this.taskQueue.shift();
                if (!item)
                    break;
                // Execute task asynchronously (don't await)
                this.executeTask(worker, item.task, item.resolve, item.reject, item.queuedAt);
            }
            if (this.taskQueue.length === 0) {
                this.emit({ type: 'pool.drained', timestamp: Date.now() });
            }
        }
        finally {
            this.processing = false;
        }
    }
    /**
     * Execute a task on a worker
     */
    async executeTask(worker, task, resolve, reject, queuedAt) {
        const startedAt = Date.now();
        this.emit({
            type: 'task.started',
            taskId: task.id,
            workerId: worker.id,
            timestamp: startedAt,
            data: { queueWaitMs: startedAt - queuedAt },
        });
        this.emit({ type: 'worker.busy', workerId: worker.id, timestamp: startedAt });
        try {
            const timeout = task.timeout ?? this.defaultTimeout;
            const result = await Promise.race([
                worker.execute(task),
                new Promise((_, rej) => setTimeout(() => rej(new Error(`Task ${task.id} timed out after ${timeout}ms`)), timeout)),
            ]);
            const completedAt = Date.now();
            const durationMs = completedAt - startedAt;
            this.totalTaskDuration += durationMs;
            this.completedTaskCount++;
            this.emit({
                type: 'task.completed',
                taskId: task.id,
                workerId: worker.id,
                timestamp: completedAt,
                data: { durationMs },
            });
            resolve({
                taskId: task.id,
                workerId: worker.id,
                success: true,
                result,
                durationMs,
                startedAt,
                completedAt,
            });
        }
        catch (error) {
            const completedAt = Date.now();
            const err = error instanceof Error ? error : new Error(String(error));
            this.emit({
                type: 'task.failed',
                taskId: task.id,
                workerId: worker.id,
                timestamp: completedAt,
                data: { error: err.message },
            });
            resolve({
                taskId: task.id,
                workerId: worker.id,
                success: false,
                error: err,
                durationMs: completedAt - startedAt,
                startedAt,
                completedAt,
            });
        }
        finally {
            if (worker.isAvailable) {
                this.emit({ type: 'worker.idle', workerId: worker.id, timestamp: Date.now() });
            }
            // Continue processing queue
            this.processQueue();
        }
    }
    /**
     * Select a worker based on the balancing strategy
     */
    selectWorker(task) {
        const available = this.getAvailableWorkers(task);
        if (available.length === 0)
            return null;
        // Check for preferred worker
        if (task?.preferredWorker) {
            const preferred = available.find(w => w.id === task.preferredWorker);
            if (preferred)
                return preferred;
        }
        switch (this.balanceStrategy) {
            case 'round-robin': {
                const worker = available[this.roundRobinIndex % available.length];
                this.roundRobinIndex++;
                return worker;
            }
            case 'random': {
                const idx = Math.floor(Math.random() * available.length);
                return available[idx];
            }
            case 'priority': {
                // Sort by priority (descending) then by load (ascending)
                available.sort((a, b) => {
                    if (b.priority !== a.priority)
                        return b.priority - a.priority;
                    return a.currentLoad - b.currentLoad;
                });
                return available[0];
            }
            case 'least-busy':
            default: {
                // Sort by current load (ascending)
                available.sort((a, b) => a.currentLoad - b.currentLoad);
                return available[0];
            }
        }
    }
    /**
     * Get available workers filtered by task requirements
     */
    getAvailableWorkers(task) {
        const workers = Array.from(this.workers.values()).filter(w => w.isAvailable);
        if (task?.requiredTags && task.requiredTags.length > 0) {
            return workers.filter(w => w.hasTags(task.requiredTags));
        }
        return workers;
    }
    /**
     * Get information about all workers
     */
    getWorkerInfo() {
        return Array.from(this.workers.values()).map(w => w.getInfo());
    }
    /**
     * Get pool statistics
     */
    getStats() {
        const workerInfos = this.getWorkerInfo();
        const activeWorkers = workerInfos.filter(w => w.status === 'busy').length;
        const activeTasks = workerInfos.reduce((sum, w) => sum + w.activeTasks, 0);
        const completedTasks = workerInfos.reduce((sum, w) => sum + w.completedTasks, 0);
        const failedTasks = workerInfos.reduce((sum, w) => sum + w.failedTasks, 0);
        return {
            totalWorkers: workerInfos.length,
            activeWorkers,
            idleWorkers: workerInfos.length - activeWorkers,
            queuedTasks: this.taskQueue.length,
            activeTasks,
            completedTasks,
            failedTasks,
            averageTaskDurationMs: this.completedTaskCount > 0
                ? this.totalTaskDuration / this.completedTaskCount
                : 0,
        };
    }
    /**
     * Cancel all queued tasks
     */
    cancelAll() {
        const cancelled = this.taskQueue.length;
        while (this.taskQueue.length > 0) {
            const item = this.taskQueue.shift();
            if (item) {
                item.reject(new Error('Task cancelled'));
            }
        }
        return cancelled;
    }
    emit(event) {
        try {
            this.onEvent(event);
        }
        catch {
            // Ignore callback errors
        }
    }
    sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }
}
// ============================================================================
// Convenience Functions
// ============================================================================
/**
 * Create an agent worker pool with the given configuration
 */
export function createAgentWorkerPool(config) {
    return new AgentWorkerPool(config);
}
/**
 * Create a simple agent task
 */
export function createAgentTask(id, message, options) {
    return {
        id,
        message,
        ...options,
    };
}
// ============================================================================
// Exports
// ============================================================================
export default {
    AgentWorkerPool,
    createAgentWorkerPool,
    createAgentTask,
};
//# sourceMappingURL=agentWorkerPool.js.map