/** * ollama_corpus_search — FLAGSHIP persistent concept search. * * Tier: Embed. Loads the named corpus from disk, embeds the query once, * cosine-ranks against stored chunk vectors, returns ranked hits. Raw * vectors never cross the MCP boundary. * * Use this to search memory/, canon/, handbook/, doctrine/, etc. by * concept — the filename-to-idea bridge the handoff called out as the * flagship user story. * * Optional refinements: * - `filter.path_glob` — minimatch-style glob against chunk.path. Supports * `**` (recursive), `*` (single segment, no separator), `?` (single char). * No brace expansion — keep patterns simple. * - `filter.since` — ISO timestamp. Only chunks whose source file's * recorded mtime is ≥ this value survive the filter. * - `explain: true` — after retrieval, call the Instant tier per top-5 * hit with "why does this chunk match the query?" and attach the result * as `why_matched`. Capped at 5 hits to bound cost; LLM failures * degrade gracefully (no `why_matched`, a warnings[] entry appended). */ import { z } from "zod"; import type { Envelope } from "../envelope.js"; import { type CorpusFile } from "../corpus/storage.js"; import { type CorpusHit, type SearchMode } from "../corpus/searcher.js"; import type { RunContext } from "../runContext.js"; export declare const corpusSearchSchema: z.ZodObject<{ corpus: z.ZodString; query: z.ZodString; mode: z.ZodOptional>; top_k: z.ZodOptional; preview_chars: z.ZodOptional; filter: z.ZodOptional; since: z.ZodOptional; }, z.core.$strip>>; explain: z.ZodOptional; }, z.core.$strip>; export type CorpusSearchInput = z.infer; export interface CorpusHitExplained extends CorpusHit { why_matched?: string; } export interface CorpusSearchResult { hits: CorpusHitExplained[]; corpus_name: string; model_version: string; total_chunks: number; mode: SearchMode; /** * True when retrieval was skipped or degenerate — e.g. empty query. * Absent on the happy path. Pairs with `reason` so callers can tell * "zero hits because of a degenerate query" apart from "zero matches". */ weak?: boolean; /** Plain-English explanation when `weak: true`. */ reason?: string; /** Populated when the caller used filter.* — how many chunks survived. */ filter_applied?: { total_before: number; kept: number; path_glob?: string; since?: string; }; } /** * Minimal glob → regex converter. * * Supports: * - `**` (any number of segments, including zero) * - `*` (any chars except separator within a single segment) * - `?` (single char except separator) * - `\` and `/` are treated as equivalent separators so Windows paths * match POSIX-style globs (the common case: a caller types * "F:/AI/**" and the corpus stored "F:\AI\..."). * * No brace expansion, no character classes. Keep the matcher honest * and document the limitations on the tool description. */ export declare function globToRegex(glob: string): RegExp; /** * Apply filter.path_glob / filter.since to a corpus by producing a * filtered copy (shared metadata, reduced chunks). Returns the original * corpus untouched when no filter is set — no wasted allocation. */ export declare function applyFilter(corpus: CorpusFile, filter: { path_glob?: string; since?: string; } | undefined): { corpus: CorpusFile; kept: number; total_before: number; }; export declare function handleCorpusSearch(input: CorpusSearchInput, ctx: RunContext): Promise>; //# sourceMappingURL=corpusSearch.d.ts.map