/** * What the agent is actually asking about, and which lines answer it. * * THE HOLE THIS FILLS. Every engine here decides what to keep from the shape of * the content alone: head rows, anomalous rows, sentences that score well * positionally, function signatures over bodies. None of that knows what the * agent is DOING. Compressing a 900-line log the same way whether the question * is "why did the deploy fail" or "which worker handled request 4471" throws * away the answer half the time and keeps it by luck the other half. * * WHY BM25 AND NOT AN EMBEDDING MODEL, which is the whole competitive point. * HeadRoom's relevance engine is ModernBERT: it needs a Python runtime, model * weights, and enough RAM to hold them, and their own docs record the result -- * "incompatible with restricted sandboxes", a base-image RAM overhead, and * cross-agent memory that needs Docker. This is arithmetic over token counts. * It runs in the same process as the proxy, on any machine Node runs on, in * microseconds, and it is deterministic: the same block and the same question * always yield the same kept lines, so a benchmark number means something and a * regression is reproducible. * * THE SAME PRIMITIVE THE REST OF THE PACKAGE ALREADY USES. `hooks-core/ * lexical.mjs` ranks wiki findings by BM25 with this tokenizer's semantics -- * whole tokens plus their camelCase and letter-digit parts, so a query for * "skip lib check" finds `skipLibCheck`. It is reimplemented rather than * imported for two reasons, both structural: the engines are SYNCHRONOUS pure * functions and hooks-core is resolved by a path at call time behind an async * loader; and the scoring unit is different -- that module ranks whole findings * against a corpus, this ranks LINES INSIDE ONE BLOCK against each other. * * A FIXED BUDGET, NOT A BIGGER ONE. Relevance decides WHICH units survive, not * how many. Letting it keep extra would show up as better task outcomes and a * worse reduction number, which is the trade every competitor makes silently. * The one exception is bounded and stated where it happens. */ /** Whole tokens, lowercased, plus the parts of any compound identifier. */ export declare function tokenize(text: string): string[]; export interface Ranker { /** False when there is no usable question, in which case scoring is free. */ readonly active: boolean; /** * The indices of the highest-scoring units, at most `n` of them. Units that * score zero are never included: keeping an irrelevant line because the * budget allowed it is how a relevance filter turns into noise. */ top(units: readonly string[], n: number): Set; /** One unit's score against the question, for callers blending it in. */ score(unit: string, corpus: readonly string[]): number; } /** * Builds a ranker for one question. * * `query` is whatever the caller can honestly say the agent is asking -- the * instruction text of the most recent turn, typically. An empty or * all-stopword question yields an inactive ranker rather than a ranker that * matches everything, because "no information" and "everything is relevant" * must not be the same answer. */ export declare function ranker(query: string | undefined): Ranker; export declare function queryFrom(blocks: readonly { readonly text: string; readonly role?: string; }[]): string; //# sourceMappingURL=relevance.d.ts.map