@remnic/core 10.0.84 → 10.0.85

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (399) hide show
  1. package/dist/access-admin-ops-surface.js +67 -66
  2. package/dist/access-authorization-probe.js +68 -67
  3. package/dist/access-boundary.js +67 -66
  4. package/dist/access-cli.js +146 -144
  5. package/dist/access-cli.js.map +1 -1
  6. package/dist/access-extraction-force-flush.js +67 -66
  7. package/dist/access-http-briefing.js +68 -67
  8. package/dist/access-http-query.js +69 -68
  9. package/dist/access-http.js +91 -89
  10. package/dist/access-identity-continuity-surface.js +67 -66
  11. package/dist/access-lcm-surface.js +67 -66
  12. package/dist/access-mcp-frames.d.ts +29 -0
  13. package/dist/access-mcp-frames.d.ts.map +1 -0
  14. package/dist/access-mcp-frames.js +10 -0
  15. package/dist/access-mcp.d.ts +1 -1
  16. package/dist/access-mcp.d.ts.map +1 -1
  17. package/dist/access-mcp.js +82 -80
  18. package/dist/access-namespace-preflight.js +67 -66
  19. package/dist/access-observe-write-category.js +9 -9
  20. package/dist/access-observe-write-surface.d.ts.map +1 -1
  21. package/dist/access-observe-write-surface.js +67 -66
  22. package/dist/access-offline-manifest.js +3 -3
  23. package/dist/access-operations-batch.js +71 -70
  24. package/dist/access-operations.js +78 -77
  25. package/dist/access-recall-concurrency.js +67 -66
  26. package/dist/access-recall-response.js +67 -66
  27. package/dist/access-recall-surface.js +67 -66
  28. package/dist/access-schema.js +4 -4
  29. package/dist/access-service.js +67 -66
  30. package/dist/access-surface-catalog.js +69 -68
  31. package/dist/access-surface-catalog.js.map +1 -1
  32. package/dist/active-recall.js +31 -31
  33. package/dist/{auto-sync-C7E6G7Y2.js → auto-sync-3AZW7MXI.js} +6 -6
  34. package/dist/briefing.js +25 -25
  35. package/dist/buffer-persistence.d.ts +8 -4
  36. package/dist/buffer-persistence.d.ts.map +1 -1
  37. package/dist/buffer-persistence.js +1 -1
  38. package/dist/buffer-turn-helpers.d.ts +5 -0
  39. package/dist/buffer-turn-helpers.d.ts.map +1 -1
  40. package/dist/buffer-turn-helpers.js +1 -1
  41. package/dist/buffer.d.ts +4 -1
  42. package/dist/buffer.d.ts.map +1 -1
  43. package/dist/buffer.js +9 -9
  44. package/dist/calibration.js +2 -2
  45. package/dist/{capsule-crypto-YO5QJ6L3.js → capsule-crypto-GWVG7LGC.js} +2 -2
  46. package/dist/capsule-gunzip-KGBDGF7P.js +11 -0
  47. package/dist/{capsule-merge-XVTQJJ6R.js → capsule-merge-2IIXFXN5.js} +5 -11
  48. package/dist/capsule-merge-2IIXFXN5.js.map +1 -0
  49. package/dist/causal-behavior.js +2 -2
  50. package/dist/causal-chain.js +2 -2
  51. package/dist/causal-consolidation.js +32 -32
  52. package/dist/causal-retrieval.js +2 -2
  53. package/dist/causal-trajectory-graph.js +2 -2
  54. package/dist/causal-trajectory.js +1 -1
  55. package/dist/{chunk-T6T2RMWW.js → chunk-23B4HLWG.js} +5 -5
  56. package/dist/{chunk-LECSS7EE.js → chunk-2ASEDFOA.js} +2 -2
  57. package/dist/{chunk-TPX7XXY4.js → chunk-2JAT4S7N.js} +2 -2
  58. package/dist/{chunk-XRBHIR7Q.js → chunk-2URKYLMK.js} +2 -2
  59. package/dist/{chunk-IN4OFAHX.js → chunk-2XYDL2FG.js} +2 -2
  60. package/dist/{chunk-HQGATKG2.js → chunk-3KGCHTTK.js} +2 -2
  61. package/dist/{chunk-YAUHSNPB.js → chunk-3LHPTBWG.js} +2 -2
  62. package/dist/{chunk-7PMOCQOL.js → chunk-3ODZLCHC.js} +192 -63
  63. package/dist/chunk-3ODZLCHC.js.map +1 -0
  64. package/dist/{chunk-XT45WCKX.js → chunk-3VU2FDTU.js} +8 -8
  65. package/dist/{chunk-CIIHIAJ2.js → chunk-433EXTC4.js} +3 -3
  66. package/dist/{chunk-QZSIDSB2.js → chunk-44FQCC5D.js} +2 -2
  67. package/dist/{chunk-YUTT4ZTI.js → chunk-45VUHDV2.js} +2 -2
  68. package/dist/{chunk-54FDG7K7.js → chunk-4DPLCYEK.js} +2 -2
  69. package/dist/{chunk-KO3YBHOV.js → chunk-4IWEPQEV.js} +2 -2
  70. package/dist/{chunk-H7BZBSP6.js → chunk-52II7PB6.js} +4 -4
  71. package/dist/{chunk-K6RTQZA4.js → chunk-6DANGCMO.js} +2 -2
  72. package/dist/{chunk-2IW5PT7U.js → chunk-6KFFRMPQ.js} +2 -2
  73. package/dist/{chunk-MSIRVXQ3.js → chunk-73YVMZ7D.js} +4 -4
  74. package/dist/{chunk-A7BM2JTH.js → chunk-AJHNXIQU.js} +2 -2
  75. package/dist/{chunk-2D7BTSDW.js → chunk-ANZZLRUX.js} +2 -2
  76. package/dist/{chunk-YOEYL6CH.js → chunk-AP3TSV6S.js} +2 -2
  77. package/dist/{chunk-GQZSGXR6.js → chunk-BATPQNRG.js} +2 -2
  78. package/dist/{chunk-D5SSBCYY.js → chunk-BLGQUZFB.js} +2 -2
  79. package/dist/{chunk-BDN3TDGV.js → chunk-BLMTOIBK.js} +2 -2
  80. package/dist/{chunk-B74NPUHT.js → chunk-BMIM6ESC.js} +43 -43
  81. package/dist/chunk-BMIM6ESC.js.map +1 -0
  82. package/dist/{chunk-R45L4TV6.js → chunk-BUKK5SWA.js} +6 -6
  83. package/dist/{chunk-TFLPDNQV.js → chunk-CCU7URRP.js} +2 -2
  84. package/dist/{chunk-2XJZLSXE.js → chunk-CMRHXPLQ.js} +20 -20
  85. package/dist/{chunk-BK4FBHZ3.js → chunk-D6THW7PA.js} +3 -3
  86. package/dist/{chunk-BFBS7OFQ.js → chunk-DC55RIEZ.js} +4 -4
  87. package/dist/{chunk-VKDAJJQP.js → chunk-DI5ET2IE.js} +5 -5
  88. package/dist/{chunk-QZBTIYKD.js → chunk-DM4RRTN7.js} +3 -3
  89. package/dist/{chunk-EBOHFZI4.js → chunk-E5QEF3JO.js} +2 -2
  90. package/dist/{chunk-K3B72DKJ.js → chunk-E7V5AK7I.js} +2 -2
  91. package/dist/{chunk-WAD32CZG.js → chunk-ELWI2R6H.js} +26 -13
  92. package/dist/chunk-ELWI2R6H.js.map +1 -0
  93. package/dist/{chunk-BV2BRHD4.js → chunk-FHGWKODN.js} +2 -2
  94. package/dist/{chunk-HPFVZPRH.js → chunk-FK4WC6VK.js} +3 -3
  95. package/dist/{chunk-VLMBSGQF.js → chunk-FRM4UTDR.js} +6 -6
  96. package/dist/{chunk-LTDLJCMQ.js → chunk-FZPDRCOB.js} +2 -2
  97. package/dist/{chunk-ZJJFUEPO.js → chunk-GBOYOL5L.js} +2 -2
  98. package/dist/{chunk-YUTG35M4.js → chunk-GJVGFKOU.js} +2 -2
  99. package/dist/{chunk-77R7BQZ2.js → chunk-GKYIRTNX.js} +33 -11
  100. package/dist/{chunk-77R7BQZ2.js.map → chunk-GKYIRTNX.js.map} +1 -1
  101. package/dist/{chunk-K36RMINQ.js → chunk-GOZXQNIM.js} +3 -3
  102. package/dist/{chunk-HWHNQVRV.js → chunk-GYNRNKMW.js} +2 -2
  103. package/dist/{chunk-LU66I56Q.js → chunk-H34INI3P.js} +29 -3
  104. package/dist/chunk-H34INI3P.js.map +1 -0
  105. package/dist/{chunk-HLKUZW42.js → chunk-H4D5YURJ.js} +80 -94
  106. package/dist/chunk-H4D5YURJ.js.map +1 -0
  107. package/dist/{chunk-6LCWKS62.js → chunk-HVFX2N2G.js} +60 -59
  108. package/dist/chunk-HVFX2N2G.js.map +1 -0
  109. package/dist/chunk-IGLJNHOX.js +62 -0
  110. package/dist/chunk-IGLJNHOX.js.map +1 -0
  111. package/dist/{chunk-KFLD7ZZG.js → chunk-IUKYFTGW.js} +3 -3
  112. package/dist/{chunk-YK7GOPIE.js → chunk-JFH6JAGY.js} +6 -4
  113. package/dist/chunk-JFH6JAGY.js.map +1 -0
  114. package/dist/{chunk-BNQJKWWZ.js → chunk-K3FH3LAQ.js} +4 -4
  115. package/dist/{chunk-FJ2VJTGA.js → chunk-KHZVAISH.js} +2 -2
  116. package/dist/{chunk-WHQXWFH7.js → chunk-LT3CMO65.js} +2 -2
  117. package/dist/{chunk-NUSAAN75.js → chunk-MHSBOEH7.js} +2 -2
  118. package/dist/{chunk-KJCL2FZO.js → chunk-MNYQVYJ4.js} +2 -2
  119. package/dist/{chunk-RZH5RVJC.js → chunk-MTTYIXSU.js} +2 -2
  120. package/dist/{chunk-FVRTCYZ3.js → chunk-MVOJDLXS.js} +2 -2
  121. package/dist/{chunk-KESJ5XX3.js → chunk-MYCCO6AA.js} +2 -2
  122. package/dist/{chunk-66JOLMWS.js → chunk-MZCGHPIJ.js} +2 -2
  123. package/dist/{chunk-B4ZU34JQ.js → chunk-N6II66E5.js} +3 -3
  124. package/dist/{chunk-BK7AJUWB.js → chunk-NIV7RPWQ.js} +6 -6
  125. package/dist/{chunk-TLHF75OR.js → chunk-O55AGDTI.js} +10 -10
  126. package/dist/{chunk-GGJFRVRD.js → chunk-OMYRPMQC.js} +2 -2
  127. package/dist/{chunk-3JPTUCD7.js → chunk-ONN2E5QD.js} +32 -23
  128. package/dist/chunk-ONN2E5QD.js.map +1 -0
  129. package/dist/{chunk-XHGYZRVW.js → chunk-OSTFDWZJ.js} +4 -4
  130. package/dist/{chunk-BXLOS5AJ.js → chunk-OWHERGF2.js} +2 -2
  131. package/dist/{chunk-WC475JOG.js → chunk-P225W4RQ.js} +6 -6
  132. package/dist/{chunk-TFA6HPOA.js → chunk-QCP75JGO.js} +5 -5
  133. package/dist/{chunk-JRVMCZE3.js → chunk-QEYTRFR3.js} +2 -2
  134. package/dist/chunk-QV2VZKD5.js +31 -0
  135. package/dist/chunk-QV2VZKD5.js.map +1 -0
  136. package/dist/{chunk-IZJQNDGI.js → chunk-R6HMAIKM.js} +2 -2
  137. package/dist/{chunk-VUROWXFL.js → chunk-RNGDKFY2.js} +2 -2
  138. package/dist/{chunk-FF6N7BXC.js → chunk-S45INSV3.js} +4 -4
  139. package/dist/{chunk-2RUTN7PR.js → chunk-S7GJNA3K.js} +2 -2
  140. package/dist/{chunk-XYP5WBDO.js → chunk-SGHV6LZ7.js} +13 -13
  141. package/dist/chunk-SGHV6LZ7.js.map +1 -0
  142. package/dist/{chunk-RJWL3RHB.js → chunk-TFEHSPGQ.js} +2 -2
  143. package/dist/{chunk-T77AX6DG.js → chunk-TQVECGUQ.js} +4 -4
  144. package/dist/{chunk-OQSSQOKQ.js → chunk-TV65NR7L.js} +2 -2
  145. package/dist/{chunk-KQUSP6LB.js → chunk-UDKWFDV2.js} +1 -1
  146. package/dist/chunk-UDKWFDV2.js.map +1 -0
  147. package/dist/{chunk-HQPFACXO.js → chunk-UH3FPX3L.js} +2 -2
  148. package/dist/{chunk-4EACCIPO.js → chunk-UHPHH74F.js} +2 -2
  149. package/dist/{chunk-DQEMWVMT.js → chunk-UVYI6VIX.js} +1 -1
  150. package/dist/{chunk-6QYPL6NI.js → chunk-V45TEVDA.js} +3 -3
  151. package/dist/{chunk-GCAQ3VUJ.js → chunk-VCVEMQJK.js} +2 -2
  152. package/dist/{chunk-6L63IN3M.js → chunk-VEFGHRKV.js} +1 -1
  153. package/dist/{chunk-E33UJHY3.js → chunk-WFDWLWDK.js} +2 -2
  154. package/dist/{chunk-4KLQI3L2.js → chunk-WLNWFHX3.js} +1 -1
  155. package/dist/chunk-WLNWFHX3.js.map +1 -0
  156. package/dist/{chunk-KQLBXUUQ.js → chunk-WYG5UNDY.js} +2 -2
  157. package/dist/{chunk-MPGLM2VU.js → chunk-X7REO4CH.js} +2 -2
  158. package/dist/{chunk-MPDLLLOG.js → chunk-XO23OKMK.js} +9 -5
  159. package/dist/chunk-XO23OKMK.js.map +1 -0
  160. package/dist/{chunk-Z3DDQXM4.js → chunk-XSFQN7TP.js} +35 -36
  161. package/dist/chunk-XSFQN7TP.js.map +1 -0
  162. package/dist/{chunk-FZVROZKC.js → chunk-XVZA6VXC.js} +4 -4
  163. package/dist/{chunk-WV254W4V.js → chunk-Y3IYKNBY.js} +2 -2
  164. package/dist/{chunk-FUOAIJKQ.js → chunk-Y43X3E24.js} +2 -2
  165. package/dist/{chunk-3T3VHXUH.js → chunk-YAAKVTZA.js} +2 -2
  166. package/dist/{chunk-VPEIWMUT.js → chunk-YLTU2DC4.js} +2 -2
  167. package/dist/{chunk-65SKM4BF.js → chunk-YNBMBL4C.js} +2 -2
  168. package/dist/{chunk-PJYK7HH2.js → chunk-ZWTHVUT5.js} +2 -2
  169. package/dist/cli.js +110 -108
  170. package/dist/coding/export-okf-codegraph.js +25 -25
  171. package/dist/coding/pre-action-gate.js +2 -2
  172. package/dist/compounding/engine.js +27 -27
  173. package/dist/config.js +28 -28
  174. package/dist/connectors/codex-materialize-runner.js +25 -25
  175. package/dist/connectors/index.js +25 -25
  176. package/dist/contradiction/index.js +4 -4
  177. package/dist/convergence-refresh.js +27 -27
  178. package/dist/conversation-index/backend.js +2 -2
  179. package/dist/corpus-watermark.js +27 -27
  180. package/dist/deep-recall-http-glue.js +5 -5
  181. package/dist/entity-retrieval.js +25 -25
  182. package/dist/event-order-recall.js +2 -2
  183. package/dist/explicit-capture.js +5 -5
  184. package/dist/external-wiki-access.js +68 -67
  185. package/dist/extraction-evidence-class.js +2 -2
  186. package/dist/extraction-quote-grounding.d.ts.map +1 -1
  187. package/dist/extraction-quote-grounding.js +1 -1
  188. package/dist/extraction-source-grounding-rules.js +2 -2
  189. package/dist/extraction-source-grounding.js +5 -5
  190. package/dist/extraction-span-materialize.js +2 -2
  191. package/dist/extraction.js +40 -40
  192. package/dist/{first-start-migration-QVD22WWS.js → first-start-migration-A6PHIBUQ.js} +4 -4
  193. package/dist/{graph-edge-decay-CXTNJ7IQ.js → graph-edge-decay-EKWXRBWF.js} +5 -5
  194. package/dist/graph-edge-decay-EKWXRBWF.js.map +1 -0
  195. package/dist/graph-jsonl.js +3 -3
  196. package/dist/graph-snapshot.js +3 -3
  197. package/dist/graph.js +2 -2
  198. package/dist/harmonic-retrieval.js +25 -25
  199. package/dist/index.js +195 -193
  200. package/dist/lcm/engine.js +2 -2
  201. package/dist/lcm/index.js +2 -2
  202. package/dist/local-llm.js +2 -2
  203. package/dist/location/index.js +28 -28
  204. package/dist/maintenance/memory-governance.js +25 -25
  205. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +25 -25
  206. package/dist/maintenance/rebuild-memory-projection-runner.js +27 -27
  207. package/dist/maintenance/rebuild-memory-projection.js +26 -26
  208. package/dist/memory-browse-operations.js +68 -67
  209. package/dist/memory-subject.js +2 -2
  210. package/dist/namespaces/migrate.js +26 -26
  211. package/dist/namespaces/search.d.ts +8 -0
  212. package/dist/namespaces/search.d.ts.map +1 -1
  213. package/dist/namespaces/search.js +10 -6
  214. package/dist/namespaces/storage.js +25 -25
  215. package/dist/operator-doctor-corpus.js +28 -28
  216. package/dist/operator-toolkit.js +39 -39
  217. package/dist/orchestration/backlog-scheduler.d.ts +3 -2
  218. package/dist/orchestration/backlog-scheduler.d.ts.map +1 -1
  219. package/dist/orchestration/backlog-scheduler.js +157 -155
  220. package/dist/orchestration/backlog-scheduler.js.map +1 -1
  221. package/dist/orchestration/buffer-drain-runner.d.ts +6 -1
  222. package/dist/orchestration/buffer-drain-runner.d.ts.map +1 -1
  223. package/dist/orchestration/buffer-drain-runner.js +147 -145
  224. package/dist/orchestration/extraction-persist.d.ts.map +1 -1
  225. package/dist/orchestration/fact-dedup-window.d.ts +1 -4
  226. package/dist/orchestration/fact-dedup-window.d.ts.map +1 -1
  227. package/dist/orchestration/maintenance.js +39 -39
  228. package/dist/orchestrator.js +146 -144
  229. package/dist/provenance.js +2 -2
  230. package/dist/recall-navigation-operations.js +68 -67
  231. package/dist/recall-navigation.js +67 -66
  232. package/dist/reconcile/manifest.js +2 -2
  233. package/dist/{resolution-BNWE5Y25.js → resolution-23ZBDM6A.js} +5 -5
  234. package/dist/resolution-23ZBDM6A.js.map +1 -0
  235. package/dist/resume-bundles.js +29 -29
  236. package/dist/salvage-envelope.js +4 -4
  237. package/dist/search/factory.js +5 -5
  238. package/dist/search/index.js +9 -9
  239. package/dist/semantic-consolidation.js +26 -26
  240. package/dist/semantic-rule-promotion.js +25 -25
  241. package/dist/semantic-rule-verifier.js +25 -25
  242. package/dist/source-agent-qualifier.js +31 -31
  243. package/dist/standup/build.js +25 -25
  244. package/dist/storage/buffer-drain-claims.d.ts +18 -6
  245. package/dist/storage/buffer-drain-claims.d.ts.map +1 -1
  246. package/dist/storage/buffer-drain-claims.js +2 -2
  247. package/dist/storage/buffer-session-files.js +2 -2
  248. package/dist/storage/content-hash-index.d.ts +8 -0
  249. package/dist/storage/content-hash-index.d.ts.map +1 -1
  250. package/dist/storage.d.ts +10 -1
  251. package/dist/storage.d.ts.map +1 -1
  252. package/dist/storage.js +24 -24
  253. package/dist/subject-config.js +3 -3
  254. package/dist/summarizer.js +3 -3
  255. package/dist/support-passport/index.js +70 -69
  256. package/dist/transfer/autodetect.js +1 -1
  257. package/dist/transfer/backup.js +2 -2
  258. package/dist/transfer/capsule-export.js +3 -3
  259. package/dist/transfer/capsule-gunzip.d.ts +14 -0
  260. package/dist/transfer/capsule-gunzip.d.ts.map +1 -0
  261. package/dist/transfer/capsule-import.d.ts.map +1 -1
  262. package/dist/transfer/capsule-import.js +3 -2
  263. package/dist/transfer/capsule-merge.d.ts.map +1 -1
  264. package/dist/transfer/export-okf.js +25 -25
  265. package/dist/verified-recall.js +25 -25
  266. package/dist/who-knows-cli.js +26 -26
  267. package/dist/who-knows.js +25 -25
  268. package/dist/write-envelope.js +3 -3
  269. package/package.json +2 -2
  270. package/src/access-mcp-frames.test.ts +47 -0
  271. package/src/access-mcp-frames.ts +74 -0
  272. package/src/access-mcp.test.ts +146 -0
  273. package/src/access-mcp.ts +30 -30
  274. package/src/access-observe-write-surface.ts +4 -4
  275. package/src/backlog-scheduler.test.ts +443 -3
  276. package/src/buffer-persistence.ts +15 -6
  277. package/src/buffer-turn-helpers.ts +6 -0
  278. package/src/buffer.ts +6 -6
  279. package/src/cli.ts +4 -4
  280. package/src/extraction-quote-grounding.test.ts +49 -0
  281. package/src/extraction-quote-grounding.ts +50 -12
  282. package/src/fact-dedup-window.test.ts +103 -20
  283. package/src/namespaces/search.test.ts +339 -0
  284. package/src/namespaces/search.ts +54 -1
  285. package/src/orchestration/backlog-scheduler.ts +13 -12
  286. package/src/orchestration/buffer-drain-runner.ts +40 -16
  287. package/src/orchestration/extraction-persist.ts +2 -10
  288. package/src/orchestration/fact-dedup-window.ts +5 -12
  289. package/src/storage/buffer-drain-claims.test.ts +330 -6
  290. package/src/storage/buffer-drain-claims.ts +226 -80
  291. package/src/storage/buffer-session-files.ts +2 -2
  292. package/src/storage/content-hash-index.ts +9 -0
  293. package/src/storage.ts +15 -0
  294. package/src/transfer/capsule-gunzip.ts +39 -0
  295. package/src/transfer/capsule-import.test.ts +73 -0
  296. package/src/transfer/capsule-import.ts +2 -2
  297. package/src/transfer/capsule-merge.test.ts +34 -0
  298. package/src/transfer/capsule-merge.ts +2 -10
  299. package/dist/capsule-merge-XVTQJJ6R.js.map +0 -1
  300. package/dist/chunk-3JPTUCD7.js.map +0 -1
  301. package/dist/chunk-4KLQI3L2.js.map +0 -1
  302. package/dist/chunk-6LCWKS62.js.map +0 -1
  303. package/dist/chunk-7PMOCQOL.js.map +0 -1
  304. package/dist/chunk-B74NPUHT.js.map +0 -1
  305. package/dist/chunk-HLKUZW42.js.map +0 -1
  306. package/dist/chunk-KQUSP6LB.js.map +0 -1
  307. package/dist/chunk-LU66I56Q.js.map +0 -1
  308. package/dist/chunk-MPDLLLOG.js.map +0 -1
  309. package/dist/chunk-WAD32CZG.js.map +0 -1
  310. package/dist/chunk-XYP5WBDO.js.map +0 -1
  311. package/dist/chunk-YK7GOPIE.js.map +0 -1
  312. package/dist/chunk-Z3DDQXM4.js.map +0 -1
  313. /package/dist/{capsule-crypto-YO5QJ6L3.js.map → access-mcp-frames.js.map} +0 -0
  314. /package/dist/{auto-sync-C7E6G7Y2.js.map → auto-sync-3AZW7MXI.js.map} +0 -0
  315. /package/dist/{graph-edge-decay-CXTNJ7IQ.js.map → capsule-crypto-GWVG7LGC.js.map} +0 -0
  316. /package/dist/{resolution-BNWE5Y25.js.map → capsule-gunzip-KGBDGF7P.js.map} +0 -0
  317. /package/dist/{chunk-T6T2RMWW.js.map → chunk-23B4HLWG.js.map} +0 -0
  318. /package/dist/{chunk-LECSS7EE.js.map → chunk-2ASEDFOA.js.map} +0 -0
  319. /package/dist/{chunk-TPX7XXY4.js.map → chunk-2JAT4S7N.js.map} +0 -0
  320. /package/dist/{chunk-XRBHIR7Q.js.map → chunk-2URKYLMK.js.map} +0 -0
  321. /package/dist/{chunk-IN4OFAHX.js.map → chunk-2XYDL2FG.js.map} +0 -0
  322. /package/dist/{chunk-HQGATKG2.js.map → chunk-3KGCHTTK.js.map} +0 -0
  323. /package/dist/{chunk-YAUHSNPB.js.map → chunk-3LHPTBWG.js.map} +0 -0
  324. /package/dist/{chunk-XT45WCKX.js.map → chunk-3VU2FDTU.js.map} +0 -0
  325. /package/dist/{chunk-CIIHIAJ2.js.map → chunk-433EXTC4.js.map} +0 -0
  326. /package/dist/{chunk-QZSIDSB2.js.map → chunk-44FQCC5D.js.map} +0 -0
  327. /package/dist/{chunk-YUTT4ZTI.js.map → chunk-45VUHDV2.js.map} +0 -0
  328. /package/dist/{chunk-54FDG7K7.js.map → chunk-4DPLCYEK.js.map} +0 -0
  329. /package/dist/{chunk-KO3YBHOV.js.map → chunk-4IWEPQEV.js.map} +0 -0
  330. /package/dist/{chunk-H7BZBSP6.js.map → chunk-52II7PB6.js.map} +0 -0
  331. /package/dist/{chunk-K6RTQZA4.js.map → chunk-6DANGCMO.js.map} +0 -0
  332. /package/dist/{chunk-2IW5PT7U.js.map → chunk-6KFFRMPQ.js.map} +0 -0
  333. /package/dist/{chunk-MSIRVXQ3.js.map → chunk-73YVMZ7D.js.map} +0 -0
  334. /package/dist/{chunk-A7BM2JTH.js.map → chunk-AJHNXIQU.js.map} +0 -0
  335. /package/dist/{chunk-2D7BTSDW.js.map → chunk-ANZZLRUX.js.map} +0 -0
  336. /package/dist/{chunk-YOEYL6CH.js.map → chunk-AP3TSV6S.js.map} +0 -0
  337. /package/dist/{chunk-GQZSGXR6.js.map → chunk-BATPQNRG.js.map} +0 -0
  338. /package/dist/{chunk-D5SSBCYY.js.map → chunk-BLGQUZFB.js.map} +0 -0
  339. /package/dist/{chunk-BDN3TDGV.js.map → chunk-BLMTOIBK.js.map} +0 -0
  340. /package/dist/{chunk-R45L4TV6.js.map → chunk-BUKK5SWA.js.map} +0 -0
  341. /package/dist/{chunk-TFLPDNQV.js.map → chunk-CCU7URRP.js.map} +0 -0
  342. /package/dist/{chunk-2XJZLSXE.js.map → chunk-CMRHXPLQ.js.map} +0 -0
  343. /package/dist/{chunk-BK4FBHZ3.js.map → chunk-D6THW7PA.js.map} +0 -0
  344. /package/dist/{chunk-BFBS7OFQ.js.map → chunk-DC55RIEZ.js.map} +0 -0
  345. /package/dist/{chunk-VKDAJJQP.js.map → chunk-DI5ET2IE.js.map} +0 -0
  346. /package/dist/{chunk-QZBTIYKD.js.map → chunk-DM4RRTN7.js.map} +0 -0
  347. /package/dist/{chunk-EBOHFZI4.js.map → chunk-E5QEF3JO.js.map} +0 -0
  348. /package/dist/{chunk-K3B72DKJ.js.map → chunk-E7V5AK7I.js.map} +0 -0
  349. /package/dist/{chunk-BV2BRHD4.js.map → chunk-FHGWKODN.js.map} +0 -0
  350. /package/dist/{chunk-HPFVZPRH.js.map → chunk-FK4WC6VK.js.map} +0 -0
  351. /package/dist/{chunk-VLMBSGQF.js.map → chunk-FRM4UTDR.js.map} +0 -0
  352. /package/dist/{chunk-LTDLJCMQ.js.map → chunk-FZPDRCOB.js.map} +0 -0
  353. /package/dist/{chunk-ZJJFUEPO.js.map → chunk-GBOYOL5L.js.map} +0 -0
  354. /package/dist/{chunk-YUTG35M4.js.map → chunk-GJVGFKOU.js.map} +0 -0
  355. /package/dist/{chunk-K36RMINQ.js.map → chunk-GOZXQNIM.js.map} +0 -0
  356. /package/dist/{chunk-HWHNQVRV.js.map → chunk-GYNRNKMW.js.map} +0 -0
  357. /package/dist/{chunk-KFLD7ZZG.js.map → chunk-IUKYFTGW.js.map} +0 -0
  358. /package/dist/{chunk-BNQJKWWZ.js.map → chunk-K3FH3LAQ.js.map} +0 -0
  359. /package/dist/{chunk-FJ2VJTGA.js.map → chunk-KHZVAISH.js.map} +0 -0
  360. /package/dist/{chunk-WHQXWFH7.js.map → chunk-LT3CMO65.js.map} +0 -0
  361. /package/dist/{chunk-NUSAAN75.js.map → chunk-MHSBOEH7.js.map} +0 -0
  362. /package/dist/{chunk-KJCL2FZO.js.map → chunk-MNYQVYJ4.js.map} +0 -0
  363. /package/dist/{chunk-RZH5RVJC.js.map → chunk-MTTYIXSU.js.map} +0 -0
  364. /package/dist/{chunk-FVRTCYZ3.js.map → chunk-MVOJDLXS.js.map} +0 -0
  365. /package/dist/{chunk-KESJ5XX3.js.map → chunk-MYCCO6AA.js.map} +0 -0
  366. /package/dist/{chunk-66JOLMWS.js.map → chunk-MZCGHPIJ.js.map} +0 -0
  367. /package/dist/{chunk-B4ZU34JQ.js.map → chunk-N6II66E5.js.map} +0 -0
  368. /package/dist/{chunk-BK7AJUWB.js.map → chunk-NIV7RPWQ.js.map} +0 -0
  369. /package/dist/{chunk-TLHF75OR.js.map → chunk-O55AGDTI.js.map} +0 -0
  370. /package/dist/{chunk-GGJFRVRD.js.map → chunk-OMYRPMQC.js.map} +0 -0
  371. /package/dist/{chunk-XHGYZRVW.js.map → chunk-OSTFDWZJ.js.map} +0 -0
  372. /package/dist/{chunk-BXLOS5AJ.js.map → chunk-OWHERGF2.js.map} +0 -0
  373. /package/dist/{chunk-WC475JOG.js.map → chunk-P225W4RQ.js.map} +0 -0
  374. /package/dist/{chunk-TFA6HPOA.js.map → chunk-QCP75JGO.js.map} +0 -0
  375. /package/dist/{chunk-JRVMCZE3.js.map → chunk-QEYTRFR3.js.map} +0 -0
  376. /package/dist/{chunk-IZJQNDGI.js.map → chunk-R6HMAIKM.js.map} +0 -0
  377. /package/dist/{chunk-VUROWXFL.js.map → chunk-RNGDKFY2.js.map} +0 -0
  378. /package/dist/{chunk-FF6N7BXC.js.map → chunk-S45INSV3.js.map} +0 -0
  379. /package/dist/{chunk-2RUTN7PR.js.map → chunk-S7GJNA3K.js.map} +0 -0
  380. /package/dist/{chunk-RJWL3RHB.js.map → chunk-TFEHSPGQ.js.map} +0 -0
  381. /package/dist/{chunk-T77AX6DG.js.map → chunk-TQVECGUQ.js.map} +0 -0
  382. /package/dist/{chunk-OQSSQOKQ.js.map → chunk-TV65NR7L.js.map} +0 -0
  383. /package/dist/{chunk-HQPFACXO.js.map → chunk-UH3FPX3L.js.map} +0 -0
  384. /package/dist/{chunk-4EACCIPO.js.map → chunk-UHPHH74F.js.map} +0 -0
  385. /package/dist/{chunk-DQEMWVMT.js.map → chunk-UVYI6VIX.js.map} +0 -0
  386. /package/dist/{chunk-6QYPL6NI.js.map → chunk-V45TEVDA.js.map} +0 -0
  387. /package/dist/{chunk-GCAQ3VUJ.js.map → chunk-VCVEMQJK.js.map} +0 -0
  388. /package/dist/{chunk-6L63IN3M.js.map → chunk-VEFGHRKV.js.map} +0 -0
  389. /package/dist/{chunk-E33UJHY3.js.map → chunk-WFDWLWDK.js.map} +0 -0
  390. /package/dist/{chunk-KQLBXUUQ.js.map → chunk-WYG5UNDY.js.map} +0 -0
  391. /package/dist/{chunk-MPGLM2VU.js.map → chunk-X7REO4CH.js.map} +0 -0
  392. /package/dist/{chunk-FZVROZKC.js.map → chunk-XVZA6VXC.js.map} +0 -0
  393. /package/dist/{chunk-WV254W4V.js.map → chunk-Y3IYKNBY.js.map} +0 -0
  394. /package/dist/{chunk-FUOAIJKQ.js.map → chunk-Y43X3E24.js.map} +0 -0
  395. /package/dist/{chunk-3T3VHXUH.js.map → chunk-YAAKVTZA.js.map} +0 -0
  396. /package/dist/{chunk-VPEIWMUT.js.map → chunk-YLTU2DC4.js.map} +0 -0
  397. /package/dist/{chunk-65SKM4BF.js.map → chunk-YNBMBL4C.js.map} +0 -0
  398. /package/dist/{chunk-PJYK7HH2.js.map → chunk-ZWTHVUT5.js.map} +0 -0
  399. /package/dist/{first-start-migration-QVD22WWS.js.map → first-start-migration-A6PHIBUQ.js.map} +0 -0
@@ -21,7 +21,7 @@ import {
21
21
  import {
22
22
  CONTENT_HASH_INDEX_RETRY_MAX_DELAY_MS,
23
23
  ContentHashIndex
24
- } from "./chunk-KQUSP6LB.js";
24
+ } from "./chunk-UDKWFDV2.js";
25
25
  import {
26
26
  withHeldFileLock
27
27
  } from "./chunk-EBV6VXKF.js";
@@ -2297,4 +2297,4 @@ export {
2297
2297
  parseTombstoneBlockedOfflineSyncMemory,
2298
2298
  TombstoneBlockedCaptureIndexHost
2299
2299
  };
2300
- //# sourceMappingURL=chunk-BV2BRHD4.js.map
2300
+ //# sourceMappingURL=chunk-FHGWKODN.js.map
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  NamespaceStorageRouter
3
- } from "./chunk-KESJ5XX3.js";
3
+ } from "./chunk-MYCCO6AA.js";
4
4
  import {
5
5
  namespaceCollectionName
6
- } from "./chunk-LU66I56Q.js";
6
+ } from "./chunk-H34INI3P.js";
7
7
  import {
8
8
  isSafeRouteNamespace
9
9
  } from "./chunk-PCXDUHQH.js";
@@ -213,4 +213,4 @@ export {
213
213
  verifyNamespaces,
214
214
  runNamespaceMigration
215
215
  };
216
- //# sourceMappingURL=chunk-HPFVZPRH.js.map
216
+ //# sourceMappingURL=chunk-FK4WC6VK.js.map
@@ -3,7 +3,7 @@ import {
3
3
  headerConnector,
4
4
  renderExtractionConversation,
5
5
  resolveSourceConnector
6
- } from "./chunk-QZBTIYKD.js";
6
+ } from "./chunk-DM4RRTN7.js";
7
7
  import {
8
8
  normalizeReasoningTrace
9
9
  } from "./chunk-54V4BZWP.js";
@@ -29,10 +29,10 @@ import {
29
29
  buildSpanMaterializeTurns,
30
30
  stripUntrustedFactSpans,
31
31
  truncateLocalExtractionConversation
32
- } from "./chunk-FZVROZKC.js";
32
+ } from "./chunk-XVZA6VXC.js";
33
33
  import {
34
34
  attachExtractionProvenance
35
- } from "./chunk-CIIHIAJ2.js";
35
+ } from "./chunk-433EXTC4.js";
36
36
  import {
37
37
  containsExtractionPlaceholder,
38
38
  extractionAttributes,
@@ -85,7 +85,7 @@ import {
85
85
  } from "./chunk-QNUNAXBE.js";
86
86
  import {
87
87
  LocalLlmClient
88
- } from "./chunk-MSIRVXQ3.js";
88
+ } from "./chunk-73YVMZ7D.js";
89
89
  import {
90
90
  normalizeProcedureSteps
91
91
  } from "./chunk-QDW3E4RD.js";
@@ -105,7 +105,7 @@ import {
105
105
  } from "./chunk-YBA52OKG.js";
106
106
  import {
107
107
  isMemoryCategory
108
- } from "./chunk-JRVMCZE3.js";
108
+ } from "./chunk-QEYTRFR3.js";
109
109
  import {
110
110
  sanitizeMemoryContent
111
111
  } from "./chunk-5GKPFL3S.js";
@@ -2441,4 +2441,4 @@ export {
2441
2441
  shouldEnableLocalExtractionThinking,
2442
2442
  ExtractionEngine
2443
2443
  };
2444
- //# sourceMappingURL=chunk-VLMBSGQF.js.map
2444
+ //# sourceMappingURL=chunk-FRM4UTDR.js.map
@@ -3,7 +3,7 @@ import {
3
3
  getOperation,
4
4
  operationRequiresAuthorizedNamespace,
5
5
  operationUsesPrincipalNamespace
6
- } from "./chunk-6LCWKS62.js";
6
+ } from "./chunk-HVFX2N2G.js";
7
7
  import {
8
8
  assertOperationAuthorizationAllowed
9
9
  } from "./chunk-YO2JEX2Z.js";
@@ -60,4 +60,4 @@ export {
60
60
  authorizationProbeNamespaces,
61
61
  authorizationProbeRequiresPrincipalNamespace
62
62
  };
63
- //# sourceMappingURL=chunk-LTDLJCMQ.js.map
63
+ //# sourceMappingURL=chunk-FZPDRCOB.js.map
@@ -11,7 +11,7 @@ import {
11
11
  } from "./chunk-LXATZLSC.js";
12
12
  import {
13
13
  StorageManager
14
- } from "./chunk-WAD32CZG.js";
14
+ } from "./chunk-ELWI2R6H.js";
15
15
  import {
16
16
  isValidityExpiredNow
17
17
  } from "./chunk-6CIAGZ6X.js";
@@ -416,4 +416,4 @@ export {
416
416
  readProjectedHarmonicGraph,
417
417
  searchHarmonicRetrieval
418
418
  };
419
- //# sourceMappingURL=chunk-ZJJFUEPO.js.map
419
+ //# sourceMappingURL=chunk-GBOYOL5L.js.map
@@ -10,7 +10,7 @@ import {
10
10
  } from "./chunk-EKUI7I2X.js";
11
11
  import {
12
12
  StorageManager
13
- } from "./chunk-WAD32CZG.js";
13
+ } from "./chunk-ELWI2R6H.js";
14
14
  import {
15
15
  normalizeEntityName
16
16
  } from "./chunk-YXZG5TVN.js";
@@ -974,4 +974,4 @@ export {
974
974
  resolveBriefingSaveDir,
975
975
  briefingFilename
976
976
  };
977
- //# sourceMappingURL=chunk-YUTG35M4.js.map
977
+ //# sourceMappingURL=chunk-GJVGFKOU.js.map
@@ -75,17 +75,39 @@ function tokenPool(tokens) {
75
75
  function hasNegation(text) {
76
76
  return NEGATOR_PATTERN.test(text.replace(/[\u2018\u2019]/gu, "'")) || hasDenialReporting(text);
77
77
  }
78
+ var CLAUSE_PATTERN = /[^;.!?\n]+[;.!?]*/gu;
79
+ var CLAIM_COORD_SPLIT = /,\s{1,8}(?:and|but|or|nor)\s{1,8}/iu;
80
+ function splitClaimClauses(content) {
81
+ return (content.match(CLAUSE_PATTERN) ?? [content]).flatMap((clause) => {
82
+ const parts = clause.split(CLAIM_COORD_SPLIT).map((part) => part.trim()).filter((part) => part.length > 0);
83
+ return parts.length > 0 ? parts : [clause];
84
+ });
85
+ }
86
+ function clauseOverlap(clauseTokens, target) {
87
+ const has = tokenPool(tokenize(target));
88
+ const shared = clauseTokens.filter(has).length;
89
+ return { shared, ratio: clauseTokens.length === 0 ? 0 : shared / clauseTokens.length };
90
+ }
91
+ function negatedScope(clause) {
92
+ const match = NEGATOR_PATTERN.exec(clause.replace(/[\u2018\u2019]/gu, "'"));
93
+ return match ? clause.slice(match.index + match[0].length) : clause;
94
+ }
78
95
  function misstatesQuote(content, quote) {
79
- const keepsNegation = hasNegation(content) || EXCLUSION_PATTERN.test(content);
80
- const keepsReporting = hasNonAssertiveReporting(content) || content.trim().endsWith("?");
81
- const contentHas = tokenPool(tokenize(content));
82
- return (quote.match(/[^;.!?\n]+[;.!?]*/gu) ?? []).some((clause) => {
83
- const negated = hasNegation(clause) && !keepsNegation;
84
- const unasserted = (clause.trim().endsWith("?") || hasNonAssertiveReporting(clause)) && !keepsReporting;
85
- if (!negated && !unasserted) return false;
86
- const clauseTokens = [...tokenize(clause)];
87
- const shared = clauseTokens.filter(contentHas).length;
88
- return shared >= GROUNDING_MIN_SHARED_TOKENS && shared / clauseTokens.length >= 0.5;
96
+ const claimClauses = splitClaimClauses(content);
97
+ return (quote.match(CLAUSE_PATTERN) ?? []).some((quoteClause) => {
98
+ const needsNegation = hasNegation(quoteClause);
99
+ const needsReporting = quoteClause.trim().endsWith("?") || hasNonAssertiveReporting(quoteClause);
100
+ if (!needsNegation && !needsReporting) return false;
101
+ const clauseTokens = [...tokenize(quoteClause)];
102
+ const whole = clauseOverlap(clauseTokens, content);
103
+ if (whole.shared < GROUNDING_MIN_SHARED_TOKENS || whole.ratio < 0.5) return false;
104
+ const clausesSharing = (tokens) => claimClauses.filter((claimClause) => clauseOverlap(tokens, claimClause).shared > 0);
105
+ if (needsNegation) {
106
+ const scopeTokens = [...tokenize(negatedScope(quoteClause))];
107
+ const related = clausesSharing(scopeTokens.length > 0 ? scopeTokens : clauseTokens);
108
+ if (!related.some((text) => hasNegation(text) || EXCLUSION_PATTERN.test(text))) return true;
109
+ }
110
+ return needsReporting && !clausesSharing(clauseTokens).some((text) => hasNonAssertiveReporting(text) || text.trim().endsWith("?"));
89
111
  });
90
112
  }
91
113
  function isQuoteAnchoredClaim(content, anchor, sourceHas) {
@@ -149,4 +171,4 @@ export {
149
171
  quoteAnchoredEntitySupport,
150
172
  isQuoteAnchoredRelationship
151
173
  };
152
- //# sourceMappingURL=chunk-77R7BQZ2.js.map
174
+ //# sourceMappingURL=chunk-GKYIRTNX.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/extraction-quote-grounding.ts"],"sourcesContent":["/**\n * Quote-anchored source grounding.\n *\n * An extracted fact carries the exact words that support it in `quote`. A quote\n * that is a verbatim substring of the source (whitespace-normalized) is real\n * evidence: the claim only has to be supported by the text around that span,\n * not by word overlap against the whole conversation. Items without such a\n * quote never get an anchor here and stay on the whole-source rules.\n */\n\nimport type { ExtractionResult } from \"./types.js\";\nimport {\n GROUNDING_MIN_SHARED_TOKENS,\n areGroundingTokensCompatible,\n entityNameStart,\n hasDenialReporting,\n hasNonAssertiveReporting,\n isGroundedEntityName,\n normalizedEntityIdentifierTokens,\n sourceSentences,\n tokenize,\n} from \"./extraction-source-grounding-helpers.js\";\n\n/** Shortest normalized quote that counts as evidence; \"the\" or \"I\" prove nothing. */\nexport const QUOTE_ANCHOR_MIN_CHARS = 12;\n/** Characters of context kept on each side of the quote inside its turn. */\nexport const QUOTE_ANCHOR_CONTEXT_CHARS = 400;\n/** Share of a claim's content words that must occur in the quote's window. */\nexport const QUOTE_ANCHOR_MIN_WINDOW_COVERAGE = 0.35;\n/** Share of a claim's content words that must occur anywhere in the source; the rest is invented. */\nexport const QUOTE_ANCHOR_MIN_SOURCE_COVERAGE = 0.75;\nconst QUOTE_ANCHOR_MAX_OCCURRENCES = 8;\nconst TURN_SEPARATOR = \"\\n\\n\";\nconst NEGATOR_PATTERN = /\\b(?:not|no|never|cannot|without|neither|nor)\\b|n't\\b/iu;\nconst EXCLUSION_PATTERN =\n /\\b(?:exclu\\w*|avoid\\w*|omit\\w*|skip\\w*|ignor\\w*|forbid\\w*|prohibit\\w*|disallow\\w*|prevent\\w*|reject\\w*|refus\\w*|insufficient|instead|rather|unless|except)\\b/iu;\n\n/** A verbatim quote and the text around it inside its turn. */\nexport interface QuoteAnchor {\n /** Whitespace-normalized quote, exactly as it appears in the source. */\n quote: string;\n /** `quote` with its turn-local context; `;` inside the quote is rewritten to `,`. */\n window: string;\n}\n\nexport type TokenPool = (token: string) => boolean;\n\ntype Entity = ExtractionResult[\"entities\"][number];\ntype Relationship = NonNullable<ExtractionResult[\"relationships\"]>[number];\n\nfunction normalizeWhitespaceWithMap(source: string): { text: string; originalIndex: number[] } {\n const chars: string[] = [];\n const originalIndex: number[] = [];\n let inSpace = false;\n for (let index = 0; index < source.length; index += 1) {\n const char = source[index]!;\n if (/\\s/u.test(char)) {\n if (inSpace) continue;\n inSpace = true;\n chars.push(\" \");\n } else {\n inSpace = false;\n chars.push(char);\n }\n originalIndex.push(index);\n }\n return { text: chars.join(\"\"), originalIndex };\n}\n\nfunction findQuoteAnchors(quote: string, source: string): QuoteAnchor[] {\n const needle = quote.replace(/\\s+/gu, \" \").trim();\n if (needle.length < QUOTE_ANCHOR_MIN_CHARS) return [];\n const normalized = normalizeWhitespaceWithMap(source);\n const anchors: QuoteAnchor[] = [];\n let from = 0;\n while (anchors.length < QUOTE_ANCHOR_MAX_OCCURRENCES) {\n const at = normalized.text.indexOf(needle, from);\n if (at < 0) break;\n from = at + 1;\n const start = normalized.originalIndex[at]!;\n const end = normalized.originalIndex[at + needle.length - 1]! + 1;\n if (source.slice(start, end).includes(TURN_SEPARATOR)) continue;\n\n let before = source.slice(Math.max(0, start - QUOTE_ANCHOR_CONTEXT_CHARS), start);\n const turnStart = before.lastIndexOf(TURN_SEPARATOR);\n if (turnStart >= 0) before = before.slice(turnStart + TURN_SEPARATOR.length);\n else if (start > QUOTE_ANCHOR_CONTEXT_CHARS) before = before.replace(/^\\S+/u, \"\");\n\n let after = source.slice(end, end + QUOTE_ANCHOR_CONTEXT_CHARS);\n const turnEnd = after.indexOf(TURN_SEPARATOR);\n if (turnEnd >= 0) after = after.slice(0, turnEnd);\n else if (source.length - end > QUOTE_ANCHOR_CONTEXT_CHARS) after = after.replace(/\\S+$/u, \"\");\n\n anchors.push({ quote: needle, window: `${before}${needle.replace(/;/gu, \",\")}${after}` });\n }\n return anchors;\n}\n\n/**\n * Returns one anchor per place `quote` occurs verbatim in `source`, or [] when\n * the quote is absent, too short, or not an exact substring. A quote that\n * spans a turn separator is not one turn's words and yields no anchor; windows\n * never extend past their turn.\n *\n * The quote is one evidence unit: a `;` inside it is rewritten to `,` in the\n * window so sentence splitting cannot cut it, or a claim that merges two of\n * its clauses could never be grounded in a single sentence.\n *\n * `source` may have been rewritten before grounding (temporal anchoring,\n * pronoun resolution) while the model quoted the original words. When the raw\n * quote is not found, `transformQuote` applies the same rewrite to the quote.\n */\nexport function quoteAnchors(\n quote: string | undefined,\n source: string,\n transformQuote?: (quote: string) => string,\n): QuoteAnchor[] {\n if (typeof quote !== \"string\") return [];\n const exact = findQuoteAnchors(quote, source);\n if (exact.length > 0 || transformQuote === undefined) return exact;\n return findQuoteAnchors(transformQuote(quote), source);\n}\n\nexport function tokenPool(tokens: ReadonlySet<string>): TokenPool {\n const list = [...tokens];\n return (token) => tokens.has(token) || list.some((poolToken) => areGroundingTokensCompatible(token, poolToken));\n}\n\nfunction hasNegation(text: string): boolean {\n return NEGATOR_PATTERN.test(text.replace(/[\\u2018\\u2019]/gu, \"'\")) || hasDenialReporting(text);\n}\n\n/**\n * True when the claim asserts what a quote clause denies or does not assert:\n * \"did not approve the budget\" or \"denied approving it\" restated as \"approved\n * the budget\", \"suspects Bob stole funds\" or \"Is Alice the CEO?\" restated as a\n * plain statement. A claim that keeps a negator, exclusion word, or reporting\n * marker (\"do not X\" reworded as \"X is excluded\") is not a misstatement.\n */\nfunction misstatesQuote(content: string, quote: string): boolean {\n const keepsNegation = hasNegation(content) || EXCLUSION_PATTERN.test(content);\n const keepsReporting = hasNonAssertiveReporting(content) || content.trim().endsWith(\"?\");\n const contentHas = tokenPool(tokenize(content));\n return (quote.match(/[^;.!?\\n]+[;.!?]*/gu) ?? []).some((clause) => {\n const negated = hasNegation(clause) && !keepsNegation;\n const unasserted = (clause.trim().endsWith(\"?\") || hasNonAssertiveReporting(clause)) && !keepsReporting;\n if (!negated && !unasserted) return false;\n const clauseTokens = [...tokenize(clause)];\n const shared = clauseTokens.filter(contentHas).length;\n return shared >= GROUNDING_MIN_SHARED_TOKENS && shared / clauseTokens.length >= 0.5;\n });\n}\n\n/**\n * A claim is supported by a verbatim quote when it shares at least\n * GROUNDING_MIN_SHARED_TOKENS content words with the quote, a good part of its\n * words sit in the text around the quote, nearly all of them occur in the\n * source at all, and it does not invert a negated clause of the quote. Reworded\n * and lightly elaborated claims pass; claims that bring words the conversation\n * never used, or that have nothing to do with the quote, do not.\n */\nexport function isQuoteAnchoredClaim(content: string, anchor: QuoteAnchor, sourceHas: TokenPool): boolean {\n const contentTokens = [...tokenize(content)];\n if (contentTokens.length === 0) return false;\n const quoteHas = tokenPool(tokenize(anchor.quote));\n if (contentTokens.filter(quoteHas).length < GROUNDING_MIN_SHARED_TOKENS) return false;\n const windowHas = tokenPool(tokenize(anchor.window));\n if (contentTokens.filter(windowHas).length / contentTokens.length < QUOTE_ANCHOR_MIN_WINDOW_COVERAGE) {\n return false;\n }\n return contentTokens.filter(sourceHas).length / contentTokens.length >= QUOTE_ANCHOR_MIN_SOURCE_COVERAGE\n && !misstatesQuote(content, anchor.quote);\n}\n\n/** Keeps the entity facts (flat and sectioned) `isSupported` accepts; undefined when none remain. */\nexport function filterEntityFacts(entity: Entity, isSupported: (fact: string) => boolean): Entity | undefined {\n const facts = entity.facts.filter(isSupported);\n const structuredSections = entity.structuredSections?.flatMap((section) => {\n const groundedFacts = section.facts.filter(isSupported);\n return groundedFacts.length > 0 ? [{ ...section, facts: groundedFacts }] : [];\n });\n if (facts.length === 0 && (structuredSections === undefined || structuredSections.length === 0)) {\n return undefined;\n }\n const { structuredSections: _structuredSections, ...entityWithoutSections } = entity;\n return {\n ...entityWithoutSections,\n facts,\n ...(structuredSections && structuredSections.length > 0 ? { structuredSections } : {}),\n };\n}\n\n/**\n * Entities carry no quote of their own; the anchors of facts that kept support\n * them. Returns a fact predicate, or undefined when no anchor window names the entity.\n */\nexport function quoteAnchoredEntitySupport(\n entity: Entity,\n anchors: readonly QuoteAnchor[],\n sourceHas: TokenPool,\n): ((fact: string) => boolean) | undefined {\n const nameAnchors = anchors.filter((anchor) => isGroundedEntityName(entity.name, anchor.window));\n if (nameAnchors.length === 0) return undefined;\n return (fact) => nameAnchors.some((anchor) => isQuoteAnchoredClaim(fact, anchor, sourceHas));\n}\n\n/**\n * The label words and both names must sit in one anchor-window sentence, source\n * before target, so an invented label or a reversed edge never passes.\n */\nexport function isQuoteAnchoredRelationship(\n relationship: Relationship,\n anchors: readonly QuoteAnchor[],\n sourceHas: TokenPool,\n): boolean {\n const labelTokens = [...tokenize(relationship.label)];\n if (labelTokens.length === 0) return false;\n const relationText = [\n normalizedEntityIdentifierTokens(relationship.source).join(\" \"),\n relationship.label,\n normalizedEntityIdentifierTokens(relationship.target).join(\" \"),\n ].join(\" \");\n return anchors.some((anchor) => {\n const windowHas = tokenPool(tokenize(anchor.window));\n return labelTokens.every(windowHas)\n && sourceSentences(anchor.window).some((sentence) => {\n const sourceStart = entityNameStart(relationship.source, sentence);\n return sourceStart >= 0 && entityNameStart(relationship.target, sentence) > sourceStart;\n })\n && isQuoteAnchoredClaim(relationText, anchor, sourceHas);\n });\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/extraction-quote-grounding.ts"],"sourcesContent":["/**\n * Quote-anchored source grounding.\n *\n * An extracted fact carries the exact words that support it in `quote`. A quote\n * that is a verbatim substring of the source (whitespace-normalized) is real\n * evidence: the claim only has to be supported by the text around that span,\n * not by word overlap against the whole conversation. Items without such a\n * quote never get an anchor here and stay on the whole-source rules.\n */\n\nimport type { ExtractionResult } from \"./types.js\";\nimport {\n GROUNDING_MIN_SHARED_TOKENS,\n areGroundingTokensCompatible,\n entityNameStart,\n hasDenialReporting,\n hasNonAssertiveReporting,\n isGroundedEntityName,\n normalizedEntityIdentifierTokens,\n sourceSentences,\n tokenize,\n} from \"./extraction-source-grounding-helpers.js\";\n\n/** Shortest normalized quote that counts as evidence; \"the\" or \"I\" prove nothing. */\nexport const QUOTE_ANCHOR_MIN_CHARS = 12;\n/** Characters of context kept on each side of the quote inside its turn. */\nexport const QUOTE_ANCHOR_CONTEXT_CHARS = 400;\n/** Share of a claim's content words that must occur in the quote's window. */\nexport const QUOTE_ANCHOR_MIN_WINDOW_COVERAGE = 0.35;\n/** Share of a claim's content words that must occur anywhere in the source; the rest is invented. */\nexport const QUOTE_ANCHOR_MIN_SOURCE_COVERAGE = 0.75;\nconst QUOTE_ANCHOR_MAX_OCCURRENCES = 8;\nconst TURN_SEPARATOR = \"\\n\\n\";\nconst NEGATOR_PATTERN = /\\b(?:not|no|never|cannot|without|neither|nor)\\b|n't\\b/iu;\nconst EXCLUSION_PATTERN =\n /\\b(?:exclu\\w*|avoid\\w*|omit\\w*|skip\\w*|ignor\\w*|forbid\\w*|prohibit\\w*|disallow\\w*|prevent\\w*|reject\\w*|refus\\w*|insufficient|instead|rather|unless|except)\\b/iu;\n\n/** A verbatim quote and the text around it inside its turn. */\nexport interface QuoteAnchor {\n /** Whitespace-normalized quote, exactly as it appears in the source. */\n quote: string;\n /** `quote` with its turn-local context; `;` inside the quote is rewritten to `,`. */\n window: string;\n}\n\nexport type TokenPool = (token: string) => boolean;\n\ntype Entity = ExtractionResult[\"entities\"][number];\ntype Relationship = NonNullable<ExtractionResult[\"relationships\"]>[number];\n\nfunction normalizeWhitespaceWithMap(source: string): { text: string; originalIndex: number[] } {\n const chars: string[] = [];\n const originalIndex: number[] = [];\n let inSpace = false;\n for (let index = 0; index < source.length; index += 1) {\n const char = source[index]!;\n if (/\\s/u.test(char)) {\n if (inSpace) continue;\n inSpace = true;\n chars.push(\" \");\n } else {\n inSpace = false;\n chars.push(char);\n }\n originalIndex.push(index);\n }\n return { text: chars.join(\"\"), originalIndex };\n}\n\nfunction findQuoteAnchors(quote: string, source: string): QuoteAnchor[] {\n const needle = quote.replace(/\\s+/gu, \" \").trim();\n if (needle.length < QUOTE_ANCHOR_MIN_CHARS) return [];\n const normalized = normalizeWhitespaceWithMap(source);\n const anchors: QuoteAnchor[] = [];\n let from = 0;\n while (anchors.length < QUOTE_ANCHOR_MAX_OCCURRENCES) {\n const at = normalized.text.indexOf(needle, from);\n if (at < 0) break;\n from = at + 1;\n const start = normalized.originalIndex[at]!;\n const end = normalized.originalIndex[at + needle.length - 1]! + 1;\n if (source.slice(start, end).includes(TURN_SEPARATOR)) continue;\n\n let before = source.slice(Math.max(0, start - QUOTE_ANCHOR_CONTEXT_CHARS), start);\n const turnStart = before.lastIndexOf(TURN_SEPARATOR);\n if (turnStart >= 0) before = before.slice(turnStart + TURN_SEPARATOR.length);\n else if (start > QUOTE_ANCHOR_CONTEXT_CHARS) before = before.replace(/^\\S+/u, \"\");\n\n let after = source.slice(end, end + QUOTE_ANCHOR_CONTEXT_CHARS);\n const turnEnd = after.indexOf(TURN_SEPARATOR);\n if (turnEnd >= 0) after = after.slice(0, turnEnd);\n else if (source.length - end > QUOTE_ANCHOR_CONTEXT_CHARS) after = after.replace(/\\S+$/u, \"\");\n\n anchors.push({ quote: needle, window: `${before}${needle.replace(/;/gu, \",\")}${after}` });\n }\n return anchors;\n}\n\n/**\n * Returns one anchor per place `quote` occurs verbatim in `source`, or [] when\n * the quote is absent, too short, or not an exact substring. A quote that\n * spans a turn separator is not one turn's words and yields no anchor; windows\n * never extend past their turn.\n *\n * The quote is one evidence unit: a `;` inside it is rewritten to `,` in the\n * window so sentence splitting cannot cut it, or a claim that merges two of\n * its clauses could never be grounded in a single sentence.\n *\n * `source` may have been rewritten before grounding (temporal anchoring,\n * pronoun resolution) while the model quoted the original words. When the raw\n * quote is not found, `transformQuote` applies the same rewrite to the quote.\n */\nexport function quoteAnchors(\n quote: string | undefined,\n source: string,\n transformQuote?: (quote: string) => string,\n): QuoteAnchor[] {\n if (typeof quote !== \"string\") return [];\n const exact = findQuoteAnchors(quote, source);\n if (exact.length > 0 || transformQuote === undefined) return exact;\n return findQuoteAnchors(transformQuote(quote), source);\n}\n\nexport function tokenPool(tokens: ReadonlySet<string>): TokenPool {\n const list = [...tokens];\n return (token) => tokens.has(token) || list.some((poolToken) => areGroundingTokensCompatible(token, poolToken));\n}\n\nfunction hasNegation(text: string): boolean {\n return NEGATOR_PATTERN.test(text.replace(/[\\u2018\\u2019]/gu, \"'\")) || hasDenialReporting(text);\n}\n\nconst CLAUSE_PATTERN = /[^;.!?\\n]+[;.!?]*/gu;\n/** Split \"X, and Y\" claim coordination without touching list commas. */\nconst CLAIM_COORD_SPLIT = /,\\s{1,8}(?:and|but|or|nor)\\s{1,8}/iu;\n\nfunction splitClaimClauses(content: string): string[] {\n return (content.match(CLAUSE_PATTERN) ?? [content]).flatMap((clause) => {\n const parts = clause\n .split(CLAIM_COORD_SPLIT)\n .map((part) => part.trim())\n .filter((part) => part.length > 0);\n return parts.length > 0 ? parts : [clause];\n });\n}\n\nfunction clauseOverlap(clauseTokens: readonly string[], target: string) {\n const has = tokenPool(tokenize(target));\n const shared = clauseTokens.filter(has).length;\n return { shared, ratio: clauseTokens.length === 0 ? 0 : shared / clauseTokens.length };\n}\n\n/** What a quote clause's first negator governs: the words after it. */\nfunction negatedScope(clause: string): string {\n const match = NEGATOR_PATTERN.exec(clause.replace(/[\\u2018\\u2019]/gu, \"'\"));\n return match ? clause.slice(match.index + match[0].length) : clause;\n}\n\n/**\n * True when the claim asserts what a quote clause denies or does not assert:\n * \"did not approve the budget\" or \"denied approving it\" restated as \"approved\n * the budget\", \"suspects Bob stole funds\" or \"Is Alice the CEO?\" restated as a\n * plain statement. The claim must restate the quote clause, and none of its\n * clauses that share a word with what the negator governs may keep the\n * negator, exclusion word, or reporting marker (\"do not X\" reworded as \"X is\n * excluded\", or \"Alice reviewed it, but she did not approve it\"). A negator in\n * a clause about something else does not count, so a claim that keeps one\n * quote negation can still misstate another quote clause, and a claim clause\n * that shares only the subject does not satisfy the check.\n */\nfunction misstatesQuote(content: string, quote: string): boolean {\n const claimClauses = splitClaimClauses(content);\n return (quote.match(CLAUSE_PATTERN) ?? []).some((quoteClause) => {\n const needsNegation = hasNegation(quoteClause);\n const needsReporting = quoteClause.trim().endsWith(\"?\") || hasNonAssertiveReporting(quoteClause);\n if (!needsNegation && !needsReporting) return false;\n const clauseTokens = [...tokenize(quoteClause)];\n const whole = clauseOverlap(clauseTokens, content);\n if (whole.shared < GROUNDING_MIN_SHARED_TOKENS || whole.ratio < 0.5) return false;\n const clausesSharing = (tokens: readonly string[]) =>\n claimClauses.filter((claimClause) => clauseOverlap(tokens, claimClause).shared > 0);\n if (needsNegation) {\n const scopeTokens = [...tokenize(negatedScope(quoteClause))];\n const related = clausesSharing(scopeTokens.length > 0 ? scopeTokens : clauseTokens);\n if (!related.some((text) => hasNegation(text) || EXCLUSION_PATTERN.test(text))) return true;\n }\n return needsReporting\n && !clausesSharing(clauseTokens).some((text) => hasNonAssertiveReporting(text) || text.trim().endsWith(\"?\"));\n });\n}\n\n/**\n * A claim is supported by a verbatim quote when it shares at least\n * GROUNDING_MIN_SHARED_TOKENS content words with the quote, a good part of its\n * words sit in the text around the quote, nearly all of them occur in the\n * source at all, and it does not invert a negated clause of the quote. Reworded\n * and lightly elaborated claims pass; claims that bring words the conversation\n * never used, or that have nothing to do with the quote, do not.\n */\nexport function isQuoteAnchoredClaim(content: string, anchor: QuoteAnchor, sourceHas: TokenPool): boolean {\n const contentTokens = [...tokenize(content)];\n if (contentTokens.length === 0) return false;\n const quoteHas = tokenPool(tokenize(anchor.quote));\n if (contentTokens.filter(quoteHas).length < GROUNDING_MIN_SHARED_TOKENS) return false;\n const windowHas = tokenPool(tokenize(anchor.window));\n if (contentTokens.filter(windowHas).length / contentTokens.length < QUOTE_ANCHOR_MIN_WINDOW_COVERAGE) {\n return false;\n }\n return contentTokens.filter(sourceHas).length / contentTokens.length >= QUOTE_ANCHOR_MIN_SOURCE_COVERAGE\n && !misstatesQuote(content, anchor.quote);\n}\n\n/** Keeps the entity facts (flat and sectioned) `isSupported` accepts; undefined when none remain. */\nexport function filterEntityFacts(entity: Entity, isSupported: (fact: string) => boolean): Entity | undefined {\n const facts = entity.facts.filter(isSupported);\n const structuredSections = entity.structuredSections?.flatMap((section) => {\n const groundedFacts = section.facts.filter(isSupported);\n return groundedFacts.length > 0 ? [{ ...section, facts: groundedFacts }] : [];\n });\n if (facts.length === 0 && (structuredSections === undefined || structuredSections.length === 0)) {\n return undefined;\n }\n const { structuredSections: _structuredSections, ...entityWithoutSections } = entity;\n return {\n ...entityWithoutSections,\n facts,\n ...(structuredSections && structuredSections.length > 0 ? { structuredSections } : {}),\n };\n}\n\n/**\n * Entities carry no quote of their own; the anchors of facts that kept support\n * them. Returns a fact predicate, or undefined when no anchor window names the entity.\n */\nexport function quoteAnchoredEntitySupport(\n entity: Entity,\n anchors: readonly QuoteAnchor[],\n sourceHas: TokenPool,\n): ((fact: string) => boolean) | undefined {\n const nameAnchors = anchors.filter((anchor) => isGroundedEntityName(entity.name, anchor.window));\n if (nameAnchors.length === 0) return undefined;\n return (fact) => nameAnchors.some((anchor) => isQuoteAnchoredClaim(fact, anchor, sourceHas));\n}\n\n/**\n * The label words and both names must sit in one anchor-window sentence, source\n * before target, so an invented label or a reversed edge never passes.\n */\nexport function isQuoteAnchoredRelationship(\n relationship: Relationship,\n anchors: readonly QuoteAnchor[],\n sourceHas: TokenPool,\n): boolean {\n const labelTokens = [...tokenize(relationship.label)];\n if (labelTokens.length === 0) return false;\n const relationText = [\n normalizedEntityIdentifierTokens(relationship.source).join(\" \"),\n relationship.label,\n normalizedEntityIdentifierTokens(relationship.target).join(\" \"),\n ].join(\" \");\n return anchors.some((anchor) => {\n const windowHas = tokenPool(tokenize(anchor.window));\n return labelTokens.every(windowHas)\n && sourceSentences(anchor.window).some((sentence) => {\n const sourceStart = entityNameStart(relationship.source, sentence);\n return sourceStart >= 0 && entityNameStart(relationship.target, sentence) > sourceStart;\n })\n && isQuoteAnchoredClaim(relationText, anchor, sourceHas);\n });\n}\n"],"mappings":"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","names":[]}
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  writeGraphJsonlAtomic
3
- } from "./chunk-HQPFACXO.js";
3
+ } from "./chunk-UH3FPX3L.js";
4
4
  import {
5
5
  graphFilePath,
6
6
  graphsDir,
7
7
  readEdgesStrict
8
- } from "./chunk-BFBS7OFQ.js";
8
+ } from "./chunk-DC55RIEZ.js";
9
9
  import {
10
10
  assertGraphLockHeld,
11
11
  withGraphWriteLock,
@@ -215,4 +215,4 @@ export {
215
215
  discoverGraphNamespaceRoots,
216
216
  runGraphEdgeDecayMaintenanceAcrossNamespaces
217
217
  };
218
- //# sourceMappingURL=chunk-K36RMINQ.js.map
218
+ //# sourceMappingURL=chunk-GOZXQNIM.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  isValidCapsuleSince
3
- } from "./chunk-R45L4TV6.js";
3
+ } from "./chunk-BUKK5SWA.js";
4
4
  import {
5
5
  CAPSULE_ID_PATTERN
6
6
  } from "./chunk-WEHSQBFR.js";
@@ -560,4 +560,4 @@ export {
560
560
  actionConfidenceRequestSchema,
561
561
  validateRequest
562
562
  };
563
- //# sourceMappingURL=chunk-HWHNQVRV.js.map
563
+ //# sourceMappingURL=chunk-GYNRNKMW.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createSearchBackend
3
- } from "./chunk-VKDAJJQP.js";
3
+ } from "./chunk-DI5ET2IE.js";
4
4
  import {
5
5
  namespaceIdentityToken,
6
6
  normalizeNamespaceIdentity
@@ -17,6 +17,7 @@ import path from "path";
17
17
  import { realpathSync } from "fs";
18
18
  var NESTED_NAMESPACE_FILTER_OVERFETCH_FACTOR = 4;
19
19
  var NESTED_NAMESPACE_FILTER_OVERFETCH_MIN = 50;
20
+ var HEALTH_RESULT_CACHE_TTL_MS = 1e4;
20
21
  function namespaceCollectionName(baseCollection, namespace, options) {
21
22
  const trimmed = normalizeNamespaceIdentity(namespace);
22
23
  const defaultNamespace = normalizeNamespaceIdentity(options?.defaultNamespace ?? "") || "default";
@@ -25,6 +26,13 @@ function namespaceCollectionName(baseCollection, namespace, options) {
25
26
  }
26
27
  return `${baseCollection}--${namespaceIdentityToken(trimmed || defaultNamespace)}`;
27
28
  }
29
+ function embeddingBacklogExceeds(pending, threshold) {
30
+ return threshold > 0 && pending !== null && pending > threshold;
31
+ }
32
+ function isSearchHealthDegraded(health, backlogThreshold) {
33
+ const active = health.available && health.collectionState !== "missing";
34
+ return !active || health.collectionState === "unknown" || embeddingBacklogExceeds(health.pendingEmbeddings, backlogThreshold);
35
+ }
28
36
  var NamespaceSearchRouter = class {
29
37
  constructor(config, storageRouter, createBackend = createSearchBackend) {
30
38
  this.config = config;
@@ -35,6 +43,7 @@ var NamespaceSearchRouter = class {
35
43
  storageRouter;
36
44
  createBackend;
37
45
  cache = /* @__PURE__ */ new Map();
46
+ healthResultCache = /* @__PURE__ */ new Map();
38
47
  pendingDisposals = /* @__PURE__ */ new Set();
39
48
  async collectionForNamespace(namespace) {
40
49
  return (await this.backendRecordFor(namespace)).collection;
@@ -168,6 +177,10 @@ var NamespaceSearchRouter = class {
168
177
  }
169
178
  async healthForNamespace(namespace, execution) {
170
179
  const key = namespace.trim() || this.config.defaultNamespace;
180
+ const cached = this.healthResultCache.get(key);
181
+ if (cached && cached.expiresAt > Date.now()) {
182
+ return { ...cached.value };
183
+ }
171
184
  const record = await this.createBackendRecordFor(
172
185
  key,
173
186
  execution,
@@ -187,7 +200,7 @@ var NamespaceSearchRouter = class {
187
200
  new Promise((resolve) => setTimeout(() => resolve(null), 5e3).unref?.())
188
201
  ]) : null;
189
202
  const collectionState = liveRecord?.collectionState === "missing" ? "missing" : record.collectionState;
190
- return {
203
+ const health = {
191
204
  collection: record.collection,
192
205
  memoryDir: record.memoryDir,
193
206
  available: liveRecord?.available ?? record.available,
@@ -203,6 +216,14 @@ var NamespaceSearchRouter = class {
203
216
  oldestPendingAgeMs: backendStatus?.oldestPendingAgeMs ?? null,
204
217
  embeddingBacklogThreshold: this.config.qmdEmbeddingBacklogThreshold
205
218
  };
219
+ const statusMissing = backendStatus === null && "status" in diagnosticBackend;
220
+ if (execution?.signal?.aborted !== true && !statusMissing && !isSearchHealthDegraded(health, health.embeddingBacklogThreshold)) {
221
+ this.healthResultCache.set(key, {
222
+ expiresAt: Date.now() + HEALTH_RESULT_CACHE_TTL_MS,
223
+ value: { ...health }
224
+ });
225
+ }
226
+ return { ...health };
206
227
  } finally {
207
228
  const dispose = record.backend.dispose;
208
229
  await dispose?.call(record.backend);
@@ -227,6 +248,7 @@ var NamespaceSearchRouter = class {
227
248
  }
228
249
  /** Clear cached backend records so the next access re-probes availability. */
229
250
  clearCache() {
251
+ this.healthResultCache.clear();
230
252
  const pendingRecords = Array.from(this.cache.values());
231
253
  this.cache.clear();
232
254
  const disposal = Promise.allSettled(pendingRecords).then(async (settled) => {
@@ -245,6 +267,7 @@ var NamespaceSearchRouter = class {
245
267
  /** Release any per-namespace backend handles held by cached records. */
246
268
  async dispose() {
247
269
  await this.drainPendingDisposals();
270
+ this.healthResultCache.clear();
248
271
  const pendingRecords = Array.from(this.cache.values());
249
272
  this.cache.clear();
250
273
  const settled = await Promise.allSettled(pendingRecords);
@@ -259,6 +282,7 @@ var NamespaceSearchRouter = class {
259
282
  * has been switched to NoopSearchBackend for a missing collection.
260
283
  */
261
284
  async disableSearchBackends() {
285
+ this.healthResultCache.clear();
262
286
  const settled = await Promise.allSettled(this.cache.values());
263
287
  await Promise.allSettled(
264
288
  settled.flatMap((entry) => {
@@ -515,8 +539,10 @@ function mergeNamespaceSearchResults(lists, maxResults) {
515
539
 
516
540
  export {
517
541
  namespaceCollectionName,
542
+ embeddingBacklogExceeds,
543
+ isSearchHealthDegraded,
518
544
  NamespaceSearchRouter,
519
545
  normalizeQmdResultPath,
520
546
  resolveNamespaceResultPath
521
547
  };
522
- //# sourceMappingURL=chunk-LU66I56Q.js.map
548
+ //# sourceMappingURL=chunk-H34INI3P.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/namespaces/search.ts"],"sourcesContent":["import { resolveNamespaceCapabilities } from \"../capabilities.js\";\nimport path from \"node:path\";\nimport { realpathSync } from \"node:fs\";\nimport type { PluginConfig, QmdSearchResult } from \"../types.js\";\nimport type {\n SearchBackend,\n SearchExecutionOptions,\n SearchQueryOptions,\n} from \"../search/port.js\";\nimport { createSearchBackend } from \"../search/factory.js\";\nimport { namespaceIdentityToken, normalizeNamespaceIdentity } from \"./identity.js\";\nimport { ALL_CATEGORY_DIRS } from \"../utils/category-dir.js\";\n\nconst NESTED_NAMESPACE_FILTER_OVERFETCH_FACTOR = 4;\nconst NESTED_NAMESPACE_FILTER_OVERFETCH_MIN = 50;\n\n// Short TTL so repeated health polls skip a full QMD re-probe (~1.5s).\nconst HEALTH_RESULT_CACHE_TTL_MS = 10_000;\n\nexport function namespaceCollectionName(\n baseCollection: string,\n namespace: string,\n options?: {\n defaultNamespace?: string;\n useLegacyDefaultCollection?: boolean;\n },\n): string {\n const trimmed = normalizeNamespaceIdentity(namespace);\n const defaultNamespace = normalizeNamespaceIdentity(options?.defaultNamespace ?? \"\") || \"default\";\n if (\n options?.useLegacyDefaultCollection === true &&\n trimmed === defaultNamespace\n ) {\n return baseCollection;\n }\n\n return `${baseCollection}--${namespaceIdentityToken(trimmed || defaultNamespace)}`;\n}\n\ntype StorageRouterLike = {\n storageFor(namespace: string): Promise<{ dir: string }>;\n};\n\ntype NamespaceBackendRecord = {\n backend: SearchBackend;\n collection: string;\n memoryDir: string;\n available: boolean;\n collectionState: CollectionState;\n filtersNestedNamespaces: boolean;\n disposed: boolean;\n};\n\nexport interface NamespaceUpdateResult {\n backendCount: number;\n eligibleNamespaces: string[];\n}\n\nexport type CollectionState = \"present\" | \"missing\" | \"unknown\" | \"skipped\";\n\nexport interface NamespaceSearchHealth {\n collection: string;\n memoryDir: string;\n available: boolean;\n collectionState: CollectionState;\n debugStatus: string;\n installedVersion: string | null;\n supportedVersion: string | null;\n supported: boolean | null;\n upgradeAvailable: boolean | null;\n doctorAvailable: boolean | null;\n daemonMode: boolean | null;\n pendingEmbeddings: number | null;\n oldestPendingAgeMs: number | null;\n embeddingBacklogThreshold: number;\n}\n\n/** True when the pending-embedding count is above a positive backlog threshold. */\nexport function embeddingBacklogExceeds(pending: number | null, threshold: number): boolean {\n return threshold > 0 && pending !== null && pending > threshold;\n}\n\n/**\n * The one definition of a degraded search answer. The readiness consumer\n * reports it, and `healthForNamespace` refuses to cache it.\n */\nexport function isSearchHealthDegraded(\n health: Pick<NamespaceSearchHealth, \"available\" | \"collectionState\" | \"pendingEmbeddings\">,\n backlogThreshold: number,\n): boolean {\n const active = health.available && health.collectionState !== \"missing\";\n return (\n !active ||\n health.collectionState === \"unknown\" ||\n embeddingBacklogExceeds(health.pendingEmbeddings, backlogThreshold)\n );\n}\n\ntype NamespaceScopedSearchConfig = PluginConfig & {\n hostEmbeddingProviderScope?: string;\n};\n\ntype BackendRecordOptions = {\n autoCreateCollection: boolean;\n abortAsUnavailable: boolean;\n failOpenMissingGuardedCollection: boolean;\n};\n\nexport class NamespaceSearchRouter {\n private readonly cache = new Map<string, Promise<NamespaceBackendRecord>>();\n private readonly healthResultCache = new Map<\n string,\n { expiresAt: number; value: NamespaceSearchHealth }\n >();\n\n private readonly pendingDisposals = new Set<Promise<void>>();\n\n constructor(\n private readonly config: PluginConfig,\n private readonly storageRouter: StorageRouterLike,\n private readonly createBackend: (config: PluginConfig) => SearchBackend = createSearchBackend,\n ) {}\n\n async collectionForNamespace(namespace: string): Promise<string> {\n return (await this.backendRecordFor(namespace)).collection;\n }\n\n /**\n * Resolve the search backend for a namespace (#2019). Returns `unknown` so\n * callers (e.g. the prioritized-embed path) duck-type for capability like\n * `embedFiles` rather than depending on a concrete QMD shape. Reuses the\n * cached backend record, so this never re-creates a backend. Returns `null`\n * when the namespace backend is unavailable.\n */\n async backendForNamespace(namespace: string): Promise<unknown> {\n const record = await this.backendRecordFor(namespace);\n return record.available ? record.backend : null;\n }\n\n async searchAcrossNamespaces(options: {\n query: string;\n namespaces: string[];\n maxResults?: number;\n mode?: \"search\" | \"hybrid\" | \"bm25\" | \"vector\";\n searchOptions?: SearchQueryOptions;\n execution?: SearchExecutionOptions;\n }): Promise<QmdSearchResult[]> {\n const query = options.query.trim();\n if (!query) return [];\n const maxResults = Math.max(0, Math.floor(options.maxResults ?? this.config.qmdMaxResults));\n if (maxResults === 0) return [];\n\n const method = options.mode ?? \"search\";\n const namespaces = Array.from(new Set(options.namespaces.map((value) => value.trim()).filter(Boolean)));\n if (namespaces.length === 0) return [];\n\n const resultsByNamespace = await Promise.all(\n namespaces.map(async (namespace) => {\n const record = await this.backendRecordFor(namespace);\n if (!record.available || record.collectionState === \"missing\") {\n // A per-namespace backend that is unavailable (or missing its\n // collection) must report like any other QMD skip (#1536): its\n // empty contribution is otherwise indistinguishable from a true\n // no-matches for that namespace (codex round-5 review on #1544).\n try {\n options.execution?.onDegradation?.({\n backend: \"qmd\",\n code: \"backend_unavailable\",\n detail:\n record.collectionState === \"missing\"\n ? `namespace collection missing: ${namespace}`\n : `namespace backend unavailable: ${namespace}`,\n });\n } catch {\n // Observers must never break search.\n }\n return { namespace, results: [] as QmdSearchResult[] };\n }\n const backendLimit = backendSearchLimit(record, maxResults);\n let results: QmdSearchResult[];\n switch (method) {\n case \"hybrid\":\n results = await record.backend.hybridSearch(\n query,\n record.collection,\n backendLimit,\n options.execution,\n );\n break;\n case \"bm25\":\n results = await record.backend.bm25Search(\n query,\n record.collection,\n backendLimit,\n options.execution,\n );\n break;\n case \"vector\":\n results = await record.backend.vectorSearch(\n query,\n record.collection,\n backendLimit,\n options.execution,\n );\n break;\n default:\n results = await record.backend.search(\n query,\n record.collection,\n backendLimit,\n options.searchOptions,\n options.execution,\n );\n break;\n }\n results = filterNamespaceSubtreeResults(record, results)\n .map((result) => ({\n ...result,\n namespace,\n // Resolve to an absolute path so the (namespace, path) identity is\n // globally unique — same-relative-path hits from different namespaces\n // stay distinct across every downstream consumer with no special\n // handling. Display/citation surfaces relativize for portability (#2020).\n path: resolveNamespaceResultPath(record.memoryDir, record.collection, result.path),\n }))\n // Reject hits whose resolved path escapes the queried namespace's\n // storage root (#2077). A stale/corrupt per-namespace collection can\n // return a path owned by another namespace; stamping it would let\n // downstream scope checks (which trust the stamped namespace) and\n // absolute-path resolution surface a foreign-namespace memory.\n .filter((result) => resolvedPathIsInsideNamespaceRoot(record.memoryDir, result.path));\n return { namespace, results };\n }),\n );\n\n return mergeNamespaceSearchResults(resultsByNamespace, maxResults);\n }\n\n /**\n * Update all namespace backends.\n * Returns the number of backends for which an update was attempted\n * (i.e., available and collection present). Callers can treat 0 as a\n * signal that no backend was eligible — useful for success-verification in\n * startup-sync when namespacesEnabled is true.\n */\n async updateNamespaces(\n namespaces: string[],\n execution?: SearchExecutionOptions,\n options?: { strict?: boolean; force?: boolean },\n ): Promise<number> {\n return (await this.updateNamespacesDetailed(namespaces, execution, options)).backendCount;\n }\n\n async updateNamespacesDetailed(\n namespaces: string[],\n execution?: SearchExecutionOptions,\n options?: { strict?: boolean; force?: boolean },\n ): Promise<NamespaceUpdateResult> {\n const unique = Array.from(new Set(namespaces.map((value) => value.trim()).filter(Boolean)));\n const eligible = (await Promise.all(\n unique.map(async (namespace) => {\n const record = await this.backendRecordFor(namespace);\n return record.available && record.collectionState !== \"missing\"\n ? { namespace, record }\n : null;\n }),\n )).filter((entry): entry is { namespace: string; record: NamespaceBackendRecord } => entry !== null);\n\n const globalEntry = eligible.find(({ record }) => record.backend.updatesAllCollections?.() === true);\n const scopedEntries = globalEntry\n ? eligible.filter(({ record }) => record.backend.updatesAllCollections?.() !== true)\n : eligible;\n\n await Promise.all([\n globalEntry\n ? updateBackendRecord(globalEntry.record, execution, options)\n : Promise.resolve(),\n ...scopedEntries.map(({ record }) => updateBackendRecord(record, execution, options)),\n ]);\n\n return {\n backendCount: (globalEntry ? 1 : 0) + scopedEntries.length,\n eligibleNamespaces: eligible.map(({ namespace }) => namespace),\n };\n }\n\n async embedNamespaces(namespaces: string[], options?: { strict?: boolean }): Promise<void> {\n const unique = Array.from(new Set(namespaces.map((value) => value.trim()).filter(Boolean)));\n await Promise.all(\n unique.map(async (namespace) => {\n const record = await this.backendRecordFor(namespace);\n if (!record.available || record.collectionState === \"missing\") return;\n await embedBackendRecord(record, options);\n }),\n );\n }\n\n async ensureNamespaceCollection(\n namespace: string,\n execution?: SearchExecutionOptions,\n ): Promise<\"present\" | \"missing\" | \"unknown\" | \"skipped\"> {\n const record = await this.backendRecordFor(namespace, execution);\n return record.collectionState;\n }\n\n async healthForNamespace(\n namespace: string,\n execution?: SearchExecutionOptions,\n ): Promise<NamespaceSearchHealth> {\n const key = namespace.trim() || this.config.defaultNamespace;\n const cached = this.healthResultCache.get(key);\n if (cached && cached.expiresAt > Date.now()) {\n return { ...cached.value };\n }\n\n const record = await this.createBackendRecordFor(\n key,\n execution,\n {\n autoCreateCollection: false,\n abortAsUnavailable: true,\n failOpenMissingGuardedCollection: false,\n },\n );\n try {\n const liveRecord = await this.liveCachedRecordForHealth(key, record, execution);\n const diagnosticBackend = liveRecord?.backend ?? record.backend;\n const versionStatus =\n \"getVersionStatus\" in diagnosticBackend &&\n typeof diagnosticBackend.getVersionStatus === \"function\"\n ? diagnosticBackend.getVersionStatus()\n : null;\n const daemonMode = daemonModeForBackend(diagnosticBackend);\n const backendStatus =\n \"status\" in diagnosticBackend &&\n typeof diagnosticBackend.status === \"function\"\n ? await Promise.race([\n diagnosticBackend.status().catch(() => null),\n new Promise<null>((resolve) => setTimeout(() => resolve(null), 5_000).unref?.()),\n ])\n : null;\n const collectionState =\n liveRecord?.collectionState === \"missing\"\n ? \"missing\"\n : record.collectionState;\n\n const health: NamespaceSearchHealth = {\n collection: record.collection,\n memoryDir: record.memoryDir,\n available: liveRecord?.available ?? record.available,\n collectionState,\n debugStatus: diagnosticBackend.debugStatus(),\n installedVersion: versionStatus?.installedVersion ?? null,\n supportedVersion: versionStatus?.supportedVersion ?? null,\n supported: versionStatus?.supported ?? null,\n upgradeAvailable: versionStatus?.upgradeAvailable ?? null,\n doctorAvailable: versionStatus?.capabilities?.doctor ?? null,\n daemonMode,\n pendingEmbeddings: backendStatus?.pendingEmbeddings ?? null,\n oldestPendingAgeMs: backendStatus?.oldestPendingAgeMs ?? null,\n embeddingBacklogThreshold: this.config.qmdEmbeddingBacklogThreshold,\n };\n\n // Cache only a healthy answer that carries backlog data when the backend\n // reports status. A degraded or incomplete answer must re-probe so a\n // readiness poll sees recovery on the next call, not one TTL late.\n const statusMissing = backendStatus === null && \"status\" in diagnosticBackend;\n if (\n execution?.signal?.aborted !== true &&\n !statusMissing &&\n !isSearchHealthDegraded(health, health.embeddingBacklogThreshold)\n ) {\n this.healthResultCache.set(key, {\n expiresAt: Date.now() + HEALTH_RESULT_CACHE_TTL_MS,\n value: { ...health },\n });\n }\n return { ...health };\n } finally {\n const dispose = (record.backend as { dispose?: () => void | Promise<void> }).dispose;\n await dispose?.call(record.backend);\n }\n }\n\n private async liveCachedRecordForHealth(\n key: string,\n disposableRecord: NamespaceBackendRecord,\n execution?: SearchExecutionOptions,\n ): Promise<NamespaceBackendRecord | null> {\n const pending = this.cache.get(key);\n if (!pending) return null;\n const cachedRecord = await awaitWithAbort(pending, execution?.signal).catch(() => null);\n if (!cachedRecord) return null;\n if (cachedRecord.collection !== disposableRecord.collection) return null;\n if (cachedRecord.memoryDir !== disposableRecord.memoryDir) return null;\n return cachedRecord;\n }\n\n private trackDisposal(promise: Promise<void>): void {\n this.pendingDisposals.add(promise);\n void promise.finally(() => this.pendingDisposals.delete(promise));\n }\n\n private async drainPendingDisposals(): Promise<void> {\n if (this.pendingDisposals.size === 0) return;\n await Promise.allSettled(this.pendingDisposals);\n }\n\n /** Clear cached backend records so the next access re-probes availability. */\n clearCache(): void {\n this.healthResultCache.clear();\n const pendingRecords = Array.from(this.cache.values());\n this.cache.clear();\n const disposal = Promise.allSettled(pendingRecords).then(async (settled) => {\n await Promise.allSettled(\n settled.flatMap((entry) =>\n entry.status === \"fulfilled\" ? [this.disposeRecord(entry.value)] : []),\n );\n });\n this.trackDisposal(disposal);\n }\n\n private async disposeRecord(record: NamespaceBackendRecord): Promise<void> {\n if (record.disposed) return;\n record.disposed = true;\n const dispose = (record.backend as { dispose?: () => void | Promise<void> }).dispose;\n await dispose?.call(record.backend);\n }\n\n /** Release any per-namespace backend handles held by cached records. */\n async dispose(): Promise<void> {\n await this.drainPendingDisposals();\n this.healthResultCache.clear();\n const pendingRecords = Array.from(this.cache.values());\n this.cache.clear();\n const settled = await Promise.allSettled(pendingRecords);\n await Promise.allSettled(\n settled.flatMap((entry) =>\n entry.status === \"fulfilled\" ? [this.disposeRecord(entry.value)] : []),\n );\n }\n\n /**\n * Disable namespace search for this runtime while retaining the resolved\n * records as unavailable/missing. This releases concrete QMD/daemon handles\n * without letting later reads recreate a search backend after the root path\n * has been switched to NoopSearchBackend for a missing collection.\n */\n async disableSearchBackends(): Promise<void> {\n this.healthResultCache.clear();\n const settled = await Promise.allSettled(this.cache.values());\n await Promise.allSettled(\n settled.flatMap((entry) => {\n if (entry.status !== \"fulfilled\") return [];\n entry.value.available = false;\n entry.value.collectionState = \"missing\";\n return [this.disposeRecord(entry.value)];\n }),\n );\n }\n\n private async backendRecordFor(\n namespace: string,\n execution?: SearchExecutionOptions,\n ): Promise<NamespaceBackendRecord> {\n await this.drainPendingDisposals();\n const key = namespace.trim() || this.config.defaultNamespace;\n const existing = this.cache.get(key);\n if (existing) return await existing;\n\n const pending = this.createBackendRecordFor(key, execution, {\n autoCreateCollection: true,\n abortAsUnavailable: false,\n failOpenMissingGuardedCollection: true,\n }).catch((error) => {\n this.cache.delete(key);\n throw error;\n });\n\n this.cache.set(key, pending);\n return await pending;\n }\n\n private async createBackendRecordFor(\n namespace: string,\n execution: SearchExecutionOptions | undefined,\n options: BackendRecordOptions,\n ): Promise<NamespaceBackendRecord> {\n const key = namespace.trim() || this.config.defaultNamespace;\n const storage = await this.storageRouter.storageFor(key);\n const useLegacyDefaultCollection =\n key === this.config.defaultNamespace && storage.dir === this.config.memoryDir;\n const filtersNestedNamespaces =\n resolveNamespaceCapabilities(this.config).namespaces === true && useLegacyDefaultCollection;\n const rootHostEmbeddingScope =\n (this.config as NamespaceScopedSearchConfig).hostEmbeddingProviderScope ??\n this.config.memoryDir;\n const scopedConfig: NamespaceScopedSearchConfig = {\n ...this.config,\n memoryDir: storage.dir,\n hostEmbeddingProviderScope: rootHostEmbeddingScope,\n qmdCollection: namespaceCollectionName(this.config.qmdCollection, key, {\n defaultNamespace: this.config.defaultNamespace,\n useLegacyDefaultCollection,\n }),\n };\n\n const backend = this.createBackend(scopedConfig);\n try {\n const availabilityProbe =\n options.autoCreateCollection || typeof backend.checkAvailability !== \"function\"\n ? backend.probe(execution)\n : backend.checkAvailability({ signal: execution?.signal });\n const available = await awaitWithAbort(availabilityProbe, execution?.signal).catch((error) => {\n if (error instanceof NamespaceSearchAbortError && !options.abortAsUnavailable) {\n throw error;\n }\n return false;\n });\n const collectionState = available\n ? await awaitWithAbort(\n // The legacy default namespace at the flat root (`filtersNestedNamespaces`)\n // shares its `memoryDir` with the `namespaces/` container, so its base\n // collection is a \"broad root\" that also indexes nested-namespace files.\n // We STILL auto-create it (issue #1929): the search side already targets\n // this exact base collection and `filterNamespaceSubtreeResults` strips\n // the nested `namespaces/` subtree out of its results, so index and\n // search stay symmetric. Skipping creation left a configured default\n // namespace (e.g. `geek`) with maintenance \"ran\" but no collection and 0\n // recall results. `ensureCollection` is a no-op when the collection is\n // already present, so legacy installs that pre-created the broad root are\n // unaffected.\n this.collectionStateForBackend(backend, storage.dir, scopedConfig.qmdCollection, {\n autoCreate: options.autoCreateCollection,\n failOpenMissingGuardedCollection: options.failOpenMissingGuardedCollection,\n execution,\n }),\n execution?.signal,\n ).catch((error) => {\n if (error instanceof NamespaceSearchAbortError && !options.abortAsUnavailable) {\n throw error;\n }\n return \"unknown\" as const;\n })\n : \"unknown\";\n return {\n backend,\n collection: scopedConfig.qmdCollection,\n memoryDir: storage.dir,\n available,\n collectionState,\n filtersNestedNamespaces,\n disposed: false,\n };\n } catch (error) {\n const dispose = (backend as { dispose?: () => void | Promise<void> }).dispose;\n if (dispose) {\n await Promise.resolve(dispose.call(backend)).catch(() => {});\n }\n throw error;\n }\n }\n\n private async collectionStateForBackend(\n backend: SearchBackend,\n memoryDir: string,\n collection: string,\n options: {\n autoCreate: boolean;\n failOpenMissingGuardedCollection: boolean;\n execution?: SearchExecutionOptions;\n },\n ): Promise<CollectionState> {\n if (!options.autoCreate) {\n if (!backend.checkCollection) return \"unknown\";\n const collectionState = await backend\n .checkCollection(collection, options.execution)\n .catch(() => \"unknown\" as const);\n return options.failOpenMissingGuardedCollection && collectionState === \"missing\"\n ? \"unknown\"\n : collectionState;\n }\n return await backend.ensureCollection(memoryDir, collection, options.execution).catch(() => \"unknown\" as const);\n }\n}\n\nclass NamespaceSearchAbortError extends Error {\n constructor() {\n super(\"operation aborted\");\n }\n}\n\nfunction awaitWithAbort<T>(operation: Promise<T>, signal?: AbortSignal): Promise<T> {\n if (!signal) return operation;\n if (signal.aborted) return Promise.reject(new NamespaceSearchAbortError());\n\n return new Promise<T>((resolve, reject) => {\n const onAbort = () => {\n signal.removeEventListener(\"abort\", onAbort);\n reject(new NamespaceSearchAbortError());\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n operation.then(resolve, reject).finally(() => {\n signal.removeEventListener(\"abort\", onAbort);\n });\n });\n}\n\nfunction filterNamespaceSubtreeResults(\n record: NamespaceBackendRecord,\n results: QmdSearchResult[],\n): QmdSearchResult[] {\n if (!record.filtersNestedNamespaces) return results;\n return results.filter((result) =>\n !pathIsInsideNamespaceSubtree(record.memoryDir, record.collection, result.path)\n );\n}\n\nfunction backendSearchLimit(\n record: NamespaceBackendRecord,\n maxResults: number,\n): number {\n if (!record.filtersNestedNamespaces) return maxResults;\n return Math.max(\n maxResults,\n maxResults * NESTED_NAMESPACE_FILTER_OVERFETCH_FACTOR,\n NESTED_NAMESPACE_FILTER_OVERFETCH_MIN,\n );\n}\n\nasync function updateBackendRecord(\n record: NamespaceBackendRecord,\n execution?: SearchExecutionOptions,\n options?: { strict?: boolean; force?: boolean },\n): Promise<void> {\n if (options?.strict === true) {\n if (\n // Forced refreshes must skip the throttled all-collections update: its\n // success has to prove the current writes are indexed, which a throttle\n // skip cannot.\n options.force !== true &&\n record.backend.updatesAllCollections?.() === true &&\n typeof record.backend.updateStrict === \"function\"\n ) {\n await record.backend.updateStrict(execution);\n return;\n }\n if (typeof record.backend.updateCollectionStrict === \"function\") {\n await record.backend.updateCollectionStrict(record.collection, execution);\n return;\n }\n // Backends with no strict variant (Orama, LanceDB, Meilisearch, remote) have a fail-open\n // update(): it resolves when it returned early or failed. Their updateCompleted() says\n // whether the refresh ran, so strict callers get a failure instead of a fake success.\n const completed = await record.backend.updateCompleted?.(execution);\n if (completed !== undefined) {\n if (!completed) throw new Error(\"search backend update did not complete\");\n return;\n }\n }\n await record.backend.update(execution);\n}\n\nasync function embedBackendRecord(\n record: NamespaceBackendRecord,\n options?: { strict?: boolean },\n): Promise<void> {\n if (options?.strict === true) {\n if (typeof record.backend.embedCollectionStrict === \"function\") {\n await record.backend.embedCollectionStrict(record.collection);\n return;\n }\n if (typeof record.backend.embedStrict === \"function\") {\n await record.backend.embedStrict();\n return;\n }\n }\n if (typeof record.backend.embedCollection === \"function\") {\n await record.backend.embedCollection(record.collection);\n return;\n }\n await record.backend.embed();\n}\n\nfunction daemonModeForBackend(backend: SearchBackend): boolean | null {\n return \"isDaemonMode\" in backend && typeof backend.isDaemonMode === \"function\"\n ? backend.isDaemonMode() === true\n : null;\n}\n\nfunction isContainedPath(root: string, candidate: string): boolean {\n const relative = path.relative(root, candidate);\n return (\n relative === \"\" ||\n (relative !== \"..\" && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative))\n );\n}\n\nfunction pathIsInsideNamespaceSubtree(\n memoryDir: string,\n collection: string,\n resultPath: string | undefined,\n): boolean {\n if (!resultPath) return false;\n const normalizedResultPath = normalizeQmdResultPath(resultPath, collection);\n const memoryRoot = path.resolve(memoryDir);\n const namespacesRoot = path.join(memoryRoot, \"namespaces\");\n const candidate = path.isAbsolute(normalizedResultPath)\n ? path.normalize(normalizedResultPath)\n : path.resolve(memoryRoot, normalizedResultPath);\n return isContainedPath(namespacesRoot, candidate);\n}\n\n/**\n * Reject a QMD hit whose resolved path escapes the queried namespace's storage\n * root (#2077). `resolvedPath` is the already-resolved value produced by\n * `resolveNamespaceResultPath`; a stale/corrupt per-namespace collection can\n * yield an absolute path owned by another namespace, and stamping it with the\n * queried namespace would surface a foreign-namespace memory downstream.\n *\n * Lexical containment runs first — it is cheap and the only check possible when\n * the file is not yet on disk (a stale collection may cite a since-deleted\n * path). When both root and candidate exist, their filesystem realpaths are\n * canonicalized and re-checked so a symlink planted inside the namespace root\n * cannot resolve into another namespace or outside storage. An unresolved path\n * (ENOENT) keeps the lexical result rather than rejecting the stale-collection\n * case this guard targets.\n */\nfunction resolvedPathIsInsideNamespaceRoot(memoryDir: string, resolvedPath: string): boolean {\n const root = path.resolve(memoryDir);\n const candidate = path.isAbsolute(resolvedPath)\n ? path.normalize(resolvedPath)\n : path.resolve(root, resolvedPath);\n if (!isContainedPath(root, candidate)) return false;\n try {\n return isContainedPath(realpathSync.native(root), realpathSync.native(candidate));\n } catch {\n return true;\n }\n}\nexport function normalizeQmdResultPath(resultPath: string, collection: string): string {\n let value = resultPath.trim();\n if (value.startsWith(\"qmd://\")) {\n try {\n const parsed = new URL(value);\n if (parsed.protocol === \"qmd:\" && parsed.hostname === collection) {\n value = decodeURIComponent(parsed.pathname.replace(/^\\/+/, \"\"));\n }\n } catch {\n const remainder = value.slice(\"qmd://\".length);\n const slashIndex = remainder.indexOf(\"/\");\n if (slashIndex !== -1) {\n value = remainder.slice(slashIndex + 1);\n }\n }\n }\n\n // Only strip a genuine QMD collection prefix — never when the collection name\n // collides with a memory category dir (e.g. qmdCollection == \"facts\"), or a\n // bare \"facts/<id>.md\" hit would be corrupted to \"<id>.md\" and fail namespace\n // reads/access tracking (#2020).\n const collectionPrefix = `${collection}/`;\n if (value.startsWith(collectionPrefix) && !ALL_CATEGORY_DIRS.includes(collection)) {\n value = value.slice(collectionPrefix.length);\n }\n return value;\n}\nexport function resolveNamespaceResultPath(\n memoryDir: string,\n collection: string,\n resultPath: string,\n): string {\n const normalized = normalizeQmdResultPath(resultPath, collection);\n if (path.isAbsolute(normalized)) return normalized;\n const root = path.resolve(memoryDir);\n const resolved = path.resolve(root, normalized);\n const relative = path.relative(root, resolved);\n if (relative === \"\" || relative.startsWith(\"..\") || path.isAbsolute(relative)) {\n return resultPath;\n }\n return resolved;\n}\nfunction mergeNamespaceSearchResults(\n lists: Array<{ namespace: string; results: QmdSearchResult[] }>,\n maxResults: number,\n): QmdSearchResult[] {\n const merged = new Map<string, QmdSearchResult>();\n\n for (const { namespace, results } of lists) {\n for (const result of results) {\n const key = `${namespace}\\0${result.path || result.docid}`;\n const existing = merged.get(key);\n if (!existing) {\n merged.set(key, { ...result, namespace });\n continue;\n }\n if (result.score > existing.score) {\n merged.set(key, {\n ...result,\n namespace,\n snippet: existing.snippet || result.snippet || \"\",\n });\n }\n }\n }\n\n return [...merged.values()]\n .sort((a, b) => b.score - a.score)\n .slice(0, maxResults);\n}\n"],"mappings":"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","names":[]}