/** * Fan-Out / Fan-In — Parallel agent spawning and result aggregation * * Launches multiple agents in parallel (fan-out) and combines their results * using pluggable strategies (fan-in). Supports concurrency limits, timeouts, * and custom aggregation. Inspired by Claude Code's parallel agent patterns. * * @module FanOutFanIn * @version 1.0.0 */ import type { AgentPayload, AgentContext, AgentResult } from '../types/agent-adapter'; import type { AdapterRegistry } from '../adapters/adapter-registry'; /** * A single agent invocation for the fan-out step. */ export interface FanOutStep { /** Agent to execute */ agentId: string; /** Payload to send */ payload: AgentPayload; /** Optional context overrides (merged with baseContext) */ context?: Partial; /** Per-step timeout in ms (overrides global timeout) */ timeoutMs?: number; /** Arbitrary label for this step (used in result mapping) */ label?: string; /** * Same-agent retries before falling back (default: 0). Budgeted per step, so * one sub-agent's retries never consume another's allowance. */ retries?: number; /** This sub-agent's OWN fallback agent, tried once after retries are exhausted. */ fallbackAgentId?: string; /** Payload for the fallback agent (defaults to the step's payload). */ fallbackPayload?: AgentPayload; } /** * Result of a single fan-out execution, tagged with its step info. */ export interface TaggedResult { /** The agent that produced this result */ agentId: string; /** Step label (if provided) */ label?: string; /** Index of the step in the original fan-out array */ index: number; /** Actual agent result */ result: AgentResult; /** Execution duration in ms */ durationMs: number; /** Total invocations (primary retries + fallback) when resilience is configured. */ retryAttempts?: number; /** The fallback agent that served this result, if the primary chain failed. */ fellBackTo?: string; } /** * Fan-in aggregation strategy. * * - `merge`: Collect all results into an array * - `firstSuccess`: Return the first successful result * - `vote`: Return the result that occurs most often (by data equality) * - `consensus`: Return result only if all agents agree * - `custom`: Use a custom reducer function */ export type FanInStrategy = 'merge' | 'firstSuccess' | 'vote' | 'consensus' | 'custom'; /** * Result of a fan-in aggregation. */ export interface FanInResult { /** Whether aggregation was successful */ success: boolean; /** Strategy that was used */ strategy: FanInStrategy; /** Aggregated data (depends on strategy) */ data: unknown; /** All individual tagged results */ results: TaggedResult[]; /** Total execution time in ms */ totalMs: number; /** Count of successful individual results */ successCount: number; /** Count of failed individual results */ failureCount: number; } /** * Custom reducer for the 'custom' fan-in strategy. */ export type FanInReducer = (results: TaggedResult[]) => { success: boolean; data: unknown; }; /** * Options for FanOutFanIn execution. */ export interface FanOutOptions { /** Max concurrent agent executions (default: unlimited) */ concurrency?: number; /** Global timeout in ms for all fan-out (default: none) */ timeoutMs?: number; /** If true, continue even when some agents fail (default: true) */ continueOnError?: boolean; } /** * Parallel agent execution with pluggable result aggregation. * * @example * ```typescript * const fanout = new FanOutFanIn(registry, { agentId: 'orchestrator' }); * * const steps: FanOutStep[] = [ * { agentId: 'researcher-a', payload: { action: 'search', params: { q: 'AI safety' } }, label: 'web' }, * { agentId: 'researcher-b', payload: { action: 'search', params: { q: 'AI safety' } }, label: 'papers' }, * { agentId: 'researcher-c', payload: { action: 'search', params: { q: 'AI safety' } }, label: 'news' }, * ]; * * const results = await fanout.fanOut(steps, { concurrency: 2 }); * const aggregated = fanout.fanIn(results, 'merge'); * ``` */ export declare class FanOutFanIn { private registry; private baseContext; /** * @param registry Adapter registry for agent execution * @param baseContext Default execution context (merged with step-level overrides) */ constructor(registry: AdapterRegistry, baseContext: AgentContext); /** * Execute agents in parallel (fan-out phase). * * Uses a true semaphore queue — as soon as one slot frees up the next * step starts, rather than waiting for an entire chunk to drain. * * @param steps Steps to execute * @param options Concurrency and timeout settings */ fanOut(steps: FanOutStep[], options?: FanOutOptions): Promise; /** * Aggregate results (fan-in phase). * * @param results Tagged results from fan-out * @param strategy Aggregation strategy to apply * @param customReducer Required when strategy is 'custom' */ fanIn(results: TaggedResult[], strategy?: FanInStrategy, customReducer?: FanInReducer): FanInResult; /** * Convenience: fan-out + fan-in in a single call. */ run(steps: FanOutStep[], strategy?: FanInStrategy, options?: FanOutOptions, customReducer?: FanInReducer): Promise; private executeStep; /** Invoke an agent with the step's timeout. Rejects on timeout. @internal */ private invoke; /** Like {@link invoke} but captures timeouts/throws as a failed result so a retry can follow. @internal */ private invokeSafe; } //# sourceMappingURL=fan-out.d.ts.map