/** * Streaming Interceptor — scan-before-delivery for LLM response streams. * * Replaces the old `stream.tee()` pattern (which delivered bytes to the caller * before scanning finished) with a TransformStream-based interceptor that * buffers chunks, scans at token-window thresholds, and only yields chunks to * the caller *after* they pass the scan. * * When a critical finding triggers enforcement, the stream is terminated with * a `[BLOCKED]` marker and no further chunks are delivered. */ import type { CapturedRequest, DynamicFinding, EnforcementMode, ModelMetadata } from "./types"; export type InterceptorOptions = { /** Request metadata forwarded to the scanner. */ request: CapturedRequest; /** Model metadata for finding attribution. */ model: ModelMetadata; /** * Enforcement mode. When the interceptor detects a block-worthy finding * it terminates the stream immediately. * Default: read from the global SDK config. */ enforcementMode?: EnforcementMode; /** * Estimated tokens to accumulate before running a scan window. * Default: from SDK config (100). */ scanWindowTokens?: number; /** * Called for every finding emitted during streaming scan windows. * Useful for logging / metrics without blocking the stream. */ onFinding?: (finding: DynamicFinding) => void; }; export type InterceptedStreamResult = { /** The guarded readable stream — pass this to the caller instead of the original. */ stream: ReadableStream; /** All findings collected during the stream's lifetime. */ findings: DynamicFinding[]; /** True if the stream was terminated early by a block decision. */ wasBlocked: boolean; }; /** * Creates a guarded ReadableStream that scans content before delivery. * * ## How it works * * 1. The original LLM response stream is consumed internally. * 2. Chunks are buffered until the token window threshold is reached. * 3. The buffered text is scanned via `scanStreamChunk`. * 4. If the scan is clean, the buffered chunks are enqueued to the caller. * 5. If a critical finding triggers enforcement, a `[BLOCKED]` marker is * enqueued and the stream is terminated. * 6. On stream end, the full buffered response is scanned one final time. * * ## Failure mode * * If the scanner throws (network error, ONNX crash, etc.), the interceptor * **fails open** — the original chunk is passed through and scanning * continues on the next window. A finding is never allowed to block the * stream when the scanner itself is unhealthy. */ export declare function createGuardedStream(source: ReadableStream | AsyncIterable, options: InterceptorOptions): InterceptedStreamResult; /** * Wraps an existing stream (AsyncIterable or ReadableStream) behind the * guarded interceptor. Use this in provider wrappers to replace the old * `wrapAsyncIterableStream` / `wrapReadableStreamForScanning` helpers. * * Returns the guarded stream directly — the caller should return this * to the LLM consumer instead of the original stream. */ export declare function guardStream(source: ReadableStream | AsyncIterable, options: InterceptorOptions): ReadableStream;