{"version":3,"file":"mode.d.ts","sourceRoot":"","sources":["../../../src/core/search/mode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEjE,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,kBAAkB,CAAC;IACzB,yEAAyE;IACzE,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAG9D;AAED,wBAAgB,iBAAiB,CAChC,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,UAAU,EACrB,cAAc,EAAE,OAAO,EACvB,sBAAsB,CAAC,EAAE,MAAM,GAC7B,cAAc,CAahB","sourcesContent":["/**\n * Availability-first search mode resolution\n * (docs/hybrid-retrieval-design.md, Decision 4).\n *\n * No clever query router: with a hot local daemon, running both retrievers\n * costs one extra embedding query, while misrouting costs recall. `auto`\n * therefore means hybrid whenever the index is available, dropping to lexical\n * only on strong lexical signals. Requested semantic/hybrid degrade to\n * lexical (with a recorded reason) when the index is unavailable — never an\n * error, unlike the old semantic_search tool.\n */\n\nimport type { ResolvedSearchMode, SearchMode } from \"./types.js\";\n\nexport interface ModeResolution {\n\tmode: ResolvedSearchMode;\n\t/** Set when the resolved mode is a forced degradation of the request. */\n\tdegradedReason?: string;\n}\n\n/**\n * Regex metacharacters *outside* any quoted segment — the only remaining\n * signal that the caller wants exact matching rather than ranked discovery.\n *\n * Being quoted is deliberately NOT such a signal any more. It used to route\n * every quoted query to lexical-only, which is where all ten error-fragment\n * queries in the gold set land, and lexical is the worst leg for them: R@1\n * 0.000 and MRR 0.483, against 0.600 and 0.800 for the same queries in\n * hybrid. Recall survived the routing but rank did not, and rank is what an\n * agent reads.\n *\n * Metacharacters inside a quoted segment carry no information either, because\n * `buildLexicalQueryPlan` searches a quoted segment verbatim — it escapes the\n * content, so `\"initTheme() first.\"` is matched literally and its parentheses\n * say nothing about the caller's intent. Strip quoted spans before looking.\n *\n * Path-like queries also do NOT count: the eval gate showed them scoring 0%\n * lexically (content grep cannot find a file by its own name) and far better\n * in hybrid, where the embedding side and the reranker's path-affinity signal\n * carry them.\n */\nexport function hasStrongLexicalSignals(query: string): boolean {\n\tconst unquoted = query.replace(/[\"'`][^\"'`]*[\"'`]/g, \" \");\n\treturn /[\\\\^$|()[\\]{}*+?]/.test(unquoted);\n}\n\nexport function resolveSearchMode(\n\tquery: string,\n\trequested: SearchMode,\n\tembedAvailable: boolean,\n\tembedUnavailableReason?: string,\n): ModeResolution {\n\tif (requested === \"lexical\") return { mode: \"lexical\" };\n\n\tif (!embedAvailable) {\n\t\tconst reason = embedUnavailableReason ?? \"semantic index unavailable\";\n\t\treturn requested === \"auto\"\n\t\t\t? { mode: \"lexical\" }\n\t\t\t: { mode: \"lexical\", degradedReason: `${requested} requested but ${reason}` };\n\t}\n\n\tif (requested === \"semantic\" || requested === \"hybrid\") return { mode: requested };\n\n\treturn hasStrongLexicalSignals(query) ? { mode: \"lexical\" } : { mode: \"hybrid\" };\n}\n"]}