/**
 * Replay-from-raw-events — turn every captured campaign run into a
 * re-runnable artifact.
 *
 * `RawProviderSink` captures every provider HTTP envelope; `runEvalCampaign`
 * makes that capture the default. Together they make every past run a
 * complete fingerprint of what happened on the wire — enough to replay
 * the run without burning new LLM cost.
 *
 * Three use cases this primitive enables:
 *
 *   1. **Post-hoc judging** — apply a new judge / rubric / scoring callback
 *      to last week's runs without re-calling any LLM. The cost of trying
 *      a new rubric drops from "another full sweep" to a CPU-bound replay.
 *   2. **Determinism audits** — replay the same campaign and verify the
 *      raw responses match byte-for-byte. Any drift is a non-determinism
 *      bug (in the harness, the prompt builder, the sandbox, …).
 *   3. **Free judge calibration** — run two judges on identical responses
 *      and measure inter-judge agreement without doubling LLM spend.
 *
 * The interface is deliberately fetch-shaped. Inject `createReplayFetch`
 * into `LlmClientOptions.fetch` and every `callLlm` transparently reads
 * from the cache instead of calling the network. No new code path through
 * the LLM client is needed; the cache hit is invisible to the runner.
 */
import { ReplayError } from './errors';
import type { RawProviderEvent, RawProviderSink } from './trace/raw-provider-sink';
export declare class ReplayCacheMissError extends ReplayError {
    readonly url: string;
    readonly requestKey: string;
    constructor(url: string, requestKey: string, message?: string);
}
export interface ReplayCacheEntry {
    request: RawProviderEvent;
    response: RawProviderEvent;
}
export interface ReplayCacheStats {
    total: number;
    byProvider: Record<string, number>;
    byModel: Record<string, number>;
    /** Spans for which we have a request but no response (run aborted mid-call). */
    orphanRequests: number;
}
/**
 * In-memory deterministic cache of (request → response) keyed on a stable
 * hash of the request body. Built from a `RawProviderSink` containing
 * paired `request` and `response` events from a previous run.
 *
 * The cache is the source of truth for replay; `createReplayFetch` is a
 * thin wrapper that reads from it.
 */
export declare class ReplayCache {
    private byKey;
    private orphans;
    private byProvider;
    private byModel;
    /**
     * Build a cache from a sink's events. The sink must implement `list()`.
     * Filter by `runId` / `spanId` to scope to a specific replay.
     */
    static fromSink(sink: RawProviderSink, filter?: {
        runId?: string;
        spanId?: string;
    }): Promise<ReplayCache>;
    /** Build a cache from an in-memory event list. */
    static fromEvents(events: RawProviderEvent[]): Promise<ReplayCache>;
    /** Number of cacheable (request, response) pairs in the cache. */
    size(): number;
    stats(): ReplayCacheStats;
    /** Iterate every cached `(request, response)` pair in insertion order. */
    entries(): IterableIterator<ReplayCacheEntry>;
    /**
     * Look up a cached response by hashing the (model, messages, temperature,
     * maxTokens, response_format) shape. Returns `undefined` on miss; the
     * caller decides whether to throw, fall back to the network, or skip.
     */
    lookup(requestBody: unknown): Promise<ReplayCacheEntry | undefined>;
}
export interface ReplayFetchOptions {
    /**
     * Behaviour on cache miss. Default `'throw'`. `'fallback'` calls the
     * `fallbackFetch` (typically `globalThis.fetch`) so a partial replay can
     * still complete; `'fail-closed'` returns a synthetic 599 response so the
     * call site sees a non-retriable failure.
     */
    onMiss?: 'throw' | 'fallback' | 'fail-closed';
    fallbackFetch?: typeof fetch;
    /** Optional callback fired once per replayed call (for telemetry / counters). */
    onHit?: (info: {
        url: string;
        provider: string;
        model: string;
    }) => void;
    /** Optional callback fired on cache miss before the `onMiss` policy applies. */
    onMissNotify?: (info: {
        url: string;
        requestBody: unknown;
    }) => void;
}
/**
 * Build a `fetch`-shaped function that serves cached responses out of a
 * `ReplayCache` for any URL ending in `/chat/completions`. Pass through
 * `LlmClientOptions.fetch` and `callLlm` becomes free.
 *
 * Non-`/chat/completions` URLs are passed straight to the fallback fetch
 * (default: `globalThis.fetch`). This matters because non-LLM HTTP work
 * (judge HTTP servers, sandbox callbacks) sometimes flows through the same
 * `fetch` and shouldn't be intercepted.
 */
export declare function createReplayFetch(cache: ReplayCache, opts?: ReplayFetchOptions): typeof fetch;
/**
 * Convenience iterator over `(request, response)` pairs in a sink — for
 * post-hoc scoring that doesn't need a `fetch` shim. The judge or scorer
 * runs purely in-process over cached LLM outputs.
 */
export declare function iterateRawCalls(sink: RawProviderSink, filter?: {
    runId?: string;
    spanId?: string;
}): AsyncGenerator<ReplayCacheEntry>;
//# sourceMappingURL=replay.d.ts.map