/** * Embedding pre-filter for the slim-index. * * Kicks in only when: * * 1. The catalog can't fit in the slim-index budget AND * 2. A {@link SkillEmbedder} is configured on {@link SkillsConfig}. * * The embedder computes per-skill vectors of `name + description + * when_to_use`; the user message (or the consumer's "intent" hint) is * embedded into the same space; cosine similarity ranks the candidates * and the top-K survive. Caller-pinned skills and their transitive * `requires:` dependencies are always included regardless of score * — pinning is the human-in-the-loop signal that overrides ranking * heuristics. * * Embeddings are cached by `(content-hash, embedder.id)`. The hash is * computed over the same `name + description + when_to_use` string; * swapping embedders re-keys the cache so a stale vector from a * previous embedder doesn't bleed into the ranked set. * * Embedding is a cost optimization, not a correctness fix. * Embedder failures or absence fall back to truncation in `render.ts` * with the existing `sdk.skill.index_truncated` event. The pre-filter * never throws — it always returns a candidate set; the caller decides * how to handle empty / partial results. */ import type { SkillsManager } from './manager.js'; import type { ParsedSkill, SkillEmbedder, SkillEvent, SkillsActivationConfig } from './types.js'; /** * Caching surface for per-skill + per-embedder vectors. Maintained * across pre-filter calls so a query against the same catalog doesn't * re-embed the unchanged entries. * * The cache is keyed by `(content-hash, embedder.id)`. Content-hash is * a stable 32-bit FNV-1a hash over the embedded text — fast enough to * compute per call, small enough to use as a Map key. */ export declare class SkillEmbeddingCache { private readonly vectors; /** * Cached lookup. Returns `undefined` when the key has never been * stored; the caller is responsible for invoking the embedder and * `store`-ing the result. */ get(textHash: number, embedderId: string): readonly number[] | undefined; store(textHash: number, embedderId: string, vector: readonly number[]): void; /** Number of cached entries. Exposed for tests / observability. */ get size(): number; /** Drop every cached entry. Called when the catalog epoch advances. */ clear(): void; } /** * Build the embedded-text representation of a skill. Centralised here * so the hash key, the embed call, and (downstream) any "explain why * this skill scored" debug surface all agree on the source string. */ export declare function skillEmbedText(skill: ParsedSkill): string; /** * Fast deterministic hash for cache keying. 32-bit FNV-1a — no crypto * properties needed; the cache key just needs to collide with very * low probability across the per-process skill space (~hundreds of * skills max). */ export declare function fnv1a32(text: string): number; export interface PrefilterOptions { readonly query: string; readonly embedder: SkillEmbedder; readonly activation?: SkillsActivationConfig; /** * Caller-pinned FQNs / bare names. Pinned skills + their transitive * `requires:` dependencies are always included regardless of score. */ readonly pinned?: ReadonlySet; /** Optional cache; the caller owns its lifetime. */ readonly cache?: SkillEmbeddingCache; } export type PrefilterOutcome = { readonly outcome: 'ok'; readonly skills: readonly ParsedSkill[]; readonly events: readonly SkillEvent[]; } | { readonly outcome: 'embedder_failed'; readonly events: readonly SkillEvent[]; readonly error: Error; }; /** * Rank a catalog by cosine similarity to `query` and return the * top-K skills above `minScore`. Caller-pinned + transitive * `requires:` skills are floored in. * * On embedder failure: returns `{ outcome: 'embedder_failed', events, * error }`. The caller surfaces the existing `sdk.skill.index_truncated * { reason: 'embedder_failed' }` and falls back to render-side * truncation. The function itself does not throw. */ export declare function prefilterCatalog(catalog: readonly ParsedSkill[], opts: PrefilterOptions, manager: SkillsManager): Promise; /** * Cosine similarity. Both vectors must be the same dimension; mismatch * collapses to `0` (which `minScore` will filter out unless the floor * is negative). */ export declare function cosine(a: readonly number[], b: readonly number[]): number; //# sourceMappingURL=prefilter.d.ts.map