/**
 * AnalystRegistry — orchestrate N analysts against one run.
 *
 * Owns three responsibilities and only three:
 *   1. Registration — ids must be unique; bad registrations fail loudly
 *      at register-time, not run-time.
 *   2. Routing — each analyst declares its `inputKind`; the registry
 *      picks the matching field from AnalystRunInputs and skips the
 *      analyst with a logged reason if it's missing.
 *   3. Isolation — one analyst's exception MUST NOT stop other analysts.
 *      Failed analysts produce zero findings + a 'failed' summary row.
 *
 * Cross-cutting concerns (telemetry, error → finding conversion, cost
 * ingestion, storage rotation) live in `AnalystHooks`. Budget shaping
 * (equal split vs weighted vs custom) lives in `BudgetPolicy`. Both
 * have sensible defaults; consumers override only what they need.
 */
import type { ChatClient } from './chat-client';
import type { Analyst, AnalystContext, AnalystFinding, AnalystRunEvent, AnalystRunInputs, AnalystRunResult, AnalystRunSummary } from './types';
export interface AnalystHooks {
    /** Before analyze() — last chance to mutate ctx (e.g. inject tags, override budget). */
    onBeforeAnalyze?(args: {
        analyst: Analyst;
        ctx: AnalystContext;
        runId: string;
    }): void | Promise<void>;
    /** After every analyst (ok | failed | skipped). Use for telemetry, ingestion, rotation. */
    onAfterAnalyze?(args: {
        analyst: Analyst;
        summary: AnalystRunSummary;
        findings: AnalystFinding[];
        runId: string;
    }): void | Promise<void>;
    /**
     * On analyst exception. Hook MAY return findings to convert the
     * error into structured findings; the summary still reports 'failed'.
     * Return void to keep the default empty-findings behavior.
     */
    onError?(args: {
        analyst: Analyst;
        error: Error;
        runId: string;
    }): AnalystFinding[] | undefined | Promise<AnalystFinding[] | undefined>;
    /** Once after registry.run() completes. Use for final aggregation, persistence. */
    onComplete?(args: {
        result: AnalystRunResult;
    }): void | Promise<void>;
}
export interface BudgetPolicy {
    /** Overall USD cap across the registry.run(). */
    totalUsd?: number;
    /** Per-analyst weight for the default allocator. Missing ids get weight 1. */
    weights?: Record<string, number>;
    /**
     * Custom allocator — receives the analyst, remaining/total budget, and
     * the count of analysts that will run. Returns the per-analyst budget
     * (or undefined to leave it uncapped). Overrides weights when set.
     */
    allocate?: (args: {
        analyst: Analyst;
        totalUsd: number | undefined;
        remainingUsd: number | undefined;
        runningCount: number;
    }) => number | undefined;
}
export interface AnalystRegistryOptions {
    /** Shared chat client passed to every LLM analyst via AnalystContext. */
    chat?: ChatClient;
    /** Logger callback. Defaults to a no-op. */
    log?: (msg: string, fields?: Record<string, unknown>) => void;
    /** Hooks invoked around analyze() — observability + customization seam. */
    hooks?: AnalystHooks;
    /** Default budget when run() doesn't override. */
    defaultBudget?: BudgetPolicy;
}
export interface RegistryRunOpts {
    /** Restrict to a subset of registered analysts by id. */
    only?: string[];
    /** Skip these analysts even if registered. Useful for cheap iteration. */
    skip?: string[];
    /** Budget policy — totalUsd + optional weights/allocator. Falls back to options.defaultBudget. */
    budget?: BudgetPolicy;
    /** Wall-clock cap. Analysts SHOULD honor `ctx.deadlineMs`. */
    timeoutMs?: number;
    /** Abort signal — forwarded into every analyst's context. */
    signal?: AbortSignal;
    /** Tags echoed into AnalystContext.tags — useful for tracking environment/version in findings. */
    tags?: Record<string, string>;
    /**
     * Prior-run findings made available as retrieval context to every
     * analyst via `ctx.priorFindings`. The registry forwards the slice
     * whose `analyst_id` matches each registered analyst so a kind sees
     * only its own history. Pass `{ '*': findings }` to broadcast to
     * every analyst (useful for cross-kind chaining where the improvement
     * analyst consumes upstream failure findings).
     */
    priorFindings?: ReadonlyArray<AnalystFinding> | Record<string, ReadonlyArray<AnalystFinding>>;
}
export declare class AnalystRegistry {
    private readonly analysts;
    private readonly options;
    constructor(options?: AnalystRegistryOptions);
    register(analyst: Analyst): void;
    list(): ReadonlyArray<{
        id: string;
        description: string;
        version: string;
        cost: Analyst['cost'];
    }>;
    run(runId: string, inputs: AnalystRunInputs, runOpts?: RegistryRunOpts): Promise<AnalystRunResult>;
    /**
     * Streaming counterpart to `run()`. Emits `AnalystRunEvent` values
     * in real time — `run-started`, then per-analyst `skipped` /
     * `started` / `completed`, then a terminal `run-completed` whose
     * payload is the full `AnalystRunResult`. UIs use this to render
     * progress; persistence consumers use `run()` and read the result.
     *
     * Hooks (`onBeforeAnalyze` / `onAfterAnalyze` / `onError` /
     * `onComplete`) fire as before — streaming is additive, not a hook
     * replacement.
     */
    runStream(runId: string, inputs: AnalystRunInputs, runOpts?: RegistryRunOpts): AsyncGenerator<AnalystRunEvent, void, void>;
    private selectAnalysts;
    private routeInput;
}
//# sourceMappingURL=registry.d.ts.map