import type { Judge } from "@oh-my-pi/pi-ai"; import type { FindCoverage, FindHit, FindStats, FindTimeout } from "@oh-my-pi/pi-tui/tools/find"; import type { InternalUrlFilesystem } from "../../internal-urls/url-filesystem.js"; import { type SpotlightClient } from "../../spotlight/client.js"; import type { SpotlightThinking } from "../../spotlight/types.js"; import { type SearchRoot } from "./tree.js"; /** Ranks the filename shortlist with Spotlight instead of the judge's name batches. */ export interface CascadeSpotlight { client: Pick; thinking: SpotlightThinking; } /** How the filename shortlist was ranked, for the experiment journal. */ export interface CascadeRanking { /** The ranker whose scores chose the read set: `judge` also when Spotlight failed and the judge took over. */ ranker: "judge" | "spotlight"; /** Files on the shortlist. */ candidates: number; /** Root-relative paths whose content was read, in selection order. */ read: string[]; /** The Spotlight request, when one was sent. */ spotlight?: { ms: number; inputTokens: number; costUsd: number; error?: string; }; /** Filename batches the judge did not have to answer. */ judgeRequestsSaved: number; } export interface CascadeOptions { root: SearchRoot; /** Filesystem the root is listed, scanned, and read through: host paths and internal URLs alike. */ filesystem: InternalUrlFilesystem; query: string; /** Caller-supplied lexical keywords, added to those derived from the query. */ extraKeywords: readonly string[]; judge: Judge; /** Rank filenames with Spotlight; the judge ranks them when absent or when Spotlight fails. */ spotlight?: CascadeSpotlight; includeHidden: boolean; signal?: AbortSignal; /** * The call's wall-clock budget. Firing during verification ends the search * with the hits verified so far and {@link CascadeResult.timeout}; firing * earlier, or a `signal` abort, rejects. */ deadline?: AbortSignal; onProgress?: (message: string) => void; } export interface CascadeResult { /** Files whose verified passages cleared the threshold, strongest first. */ hits: FindHit[]; /** Whether the judge answered; an empty `hits` proves nothing when `unavailable`. */ coverage: FindCoverage; threshold: number; keywords: string[]; stats: FindStats; ranking: CascadeRanking; /** Set when the deadline cut verification short. */ timeout?: FindTimeout; } /** Run one cascade search over `options.root`. Judge failures degrade coverage and are reported in `stats.failures`, never thrown. */ export declare function runCascade(options: CascadeOptions): Promise;