/**
 * Running the processor chains, and the stream buffering an output gate requires. See
 * `./spi/processors.ts` for the seams themselves and the boundary against `HistoryWindow`.
 */
import { type InputProcessor, type ModelAnswer, type OutputProcessor, type ProcessedPrompt, type ProcessorContext } from './spi/processors.js';
import type { SinkWriter, StreamFrame } from './spi/token-stream-sink.js';
/**
 * Holds a model call's stream frames instead of letting them reach the subscriber. `end` is
 * swallowed because the loop owns the run's stream lifecycle across however many steps the turn
 * takes, and the model call is one step of it.
 *
 * A {@link StreamFrame} is a plain typed object, so the buffer rides a durable checkpoint as-is —
 * which it has to, since the gate must survive a suspend between the model call and the verdict.
 */
export interface FrameBuffer {
    writer: SinkWriter;
    /** Everything written so far, in write order. */
    frames(): StreamFrame[];
}
export declare function createFrameBuffer(): FrameBuffer;
/**
 * The frames a passed gate releases to the live stream: the held ones, with every text frame
 * collapsed into ONE carrying `text` — the answer as the chain left it, which is the only version
 * anything downstream is allowed to see. The substitution happens in place, so the answer keeps its
 * position relative to any non-text frame the provider interleaved with it.
 */
export declare function releaseGatedFrames(args: {
    frames: readonly StreamFrame[];
    text: string;
}): StreamFrame[];
/**
 * Fold the prompt through each processor in order, every one seeing what the previous one produced.
 * A throw is wrapped so it cannot read as the model call failing — see {@link ProcessorFailedError}.
 */
export declare function runInputProcessors(args: {
    processors: readonly InputProcessor[];
    prompt: ProcessedPrompt;
    ctx: ProcessorContext;
}): Promise<ProcessedPrompt>;
/** Who refused an answer, and why. Carried out of a checkpoint, so it stays JSON-round-trippable. */
export interface GateRejection {
    processor: string;
    reason: string;
}
/** A settled output chain: the answer as the chain left it, plus who refused it if anyone did. */
export interface OutputGateResult {
    /** The text every downstream consumer sees — the stream, the persisted message, the next step. */
    text: string;
    /** Set only on a refusal; the run ends with an `OutputRejectedError` naming these. */
    rejection?: GateRejection;
}
/**
 * Fold the answer through each processor in order. The FIRST rejection ends the chain: a later
 * processor has nothing to add about text that is never going anywhere, and running it would bill a
 * moderation call for a turn already refused.
 */
export declare function runOutputProcessors(args: {
    processors: readonly OutputProcessor[];
    answer: ModelAnswer;
    ctx: ProcessorContext;
}): Promise<OutputGateResult>;
/**
 * How much of a turn's stream an output chain costs the reader.
 *
 * `off` — nothing registered, the model writes straight to the run's sink.
 * `whole` — at least one processor needs the complete answer, so every frame is held until the chain
 * has passed and the answer arrives as one `text` frame.
 * `incremental` — every processor declared {@link import('./spi/processors.js').IncrementalGating},
 * so a lookback-bounded prefix is released while the call streams.
 */
export type OutputGateMode = 'off' | 'whole' | 'incremental';
/**
 * ALL or nothing: one undeclared processor puts the whole chain on the whole-answer path. Its author
 * wrote `process` against the complete text, and a chain that ran it on a prefix because a neighbour
 * opted in would be handing it an input it never agreed to read.
 */
export declare function resolveOutputGateMode(processors: readonly OutputProcessor[]): OutputGateMode;
/**
 * The chain's window is the WIDEST any member asked for: a release safe for the shortest-sighted
 * processor is not safe for the one that matches longer patterns, and the gate makes one release
 * decision for all of them.
 *
 * An undeclared processor contributes nothing rather than the default — it has no window, because a
 * chain containing one never reaches the incremental path at all.
 */
export declare function resolveGateLookback(processors: readonly OutputProcessor[]): number;
/** A live gate over one model call: the sink the model writes to, plus what it let through. */
export interface IncrementalGate {
    /** Hand this to the model instead of the run's writer. */
    writer: SinkWriter;
    /** Resolves once every frame handed to {@link IncrementalGate.writer} has been ruled on. */
    settled(): Promise<void>;
    /** The transformed prefix already written to the run's sink. */
    released(): string;
    /** Set once a prefix was refused; nothing further was released after that. */
    rejection(): GateRejection | undefined;
}
/**
 * Releases a model call's answer to `writer` as it arrives, holding back the last `lookbackChars`
 * characters of what the chain produces so a processor can still change them.
 *
 * The chain runs over the whole PREFIX accumulated so far rather than over each new frame, so a
 * processor always sees well-formed text and never has to reassemble a pattern split across frames —
 * which is what makes {@link import('./spi/processors.js').IncrementalGating}'s promise a claim
 * about prefixes. It runs once per streamed text frame.
 *
 * Only an EXTENSION of what the reader already has is ever written: a chain whose output stops
 * agreeing with its earlier output has broken its promise, and the loop's whole-answer pass reports
 * that rather than this gate papering over it by re-sending a different answer.
 *
 * Frames that are not text are forwarded live, in arrival order — they are not the answer, so the
 * gate does not own them.
 */
export declare function createIncrementalGate(args: {
    processors: readonly OutputProcessor[];
    ctx: ProcessorContext;
    lookbackChars: number;
    writer: SinkWriter;
}): IncrementalGate;
/**
 * The tail an incremental gate still owes the reader once the whole-answer pass has settled: the
 * authoritative text minus the prefix already released.
 *
 * Throws when the settled answer is not an extension of that prefix. That check is the reason the
 * final pass stays authoritative for both the stream and the store rather than the stream being
 * stitched together from per-prefix results: agreement between what was streamed and what was stored
 * becomes structural instead of assumed.
 */
export declare function gateTail(args: {
    processors: readonly OutputProcessor[];
    released: string;
    text: string;
}): string;
//# sourceMappingURL=processors.d.ts.map