@remnic/core 9.3.613 → 9.3.615

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 (386) hide show
  1. package/dist/access-cli.js +59 -58
  2. package/dist/access-cli.js.map +1 -1
  3. package/dist/access-http.d.ts +4 -2
  4. package/dist/access-http.js +23 -23
  5. package/dist/access-mcp.d.ts +9 -2
  6. package/dist/access-mcp.js +20 -20
  7. package/dist/access-schema.d.ts +26 -14
  8. package/dist/access-schema.js +3 -3
  9. package/dist/{access-service-D2J9dh_9.d.ts → access-service-CBNEKjzN.d.ts} +71 -6
  10. package/dist/access-service.d.ts +2 -2
  11. package/dist/access-service.js +17 -17
  12. package/dist/active-recall.js +20 -3
  13. package/dist/active-recall.js.map +1 -1
  14. package/dist/adapters/index.js +4 -4
  15. package/dist/adapters/registry.js +2 -2
  16. package/dist/behavior-learner.js +2 -3
  17. package/dist/behavior-learner.js.map +1 -1
  18. package/dist/bootstrap.d.ts +1 -1
  19. package/dist/briefing.js +3 -3
  20. package/dist/buffer.d.ts +1 -1
  21. package/dist/buffer.js +1 -1
  22. package/dist/calibration.d.ts +5 -2
  23. package/dist/calibration.js +7 -5
  24. package/dist/calibration.js.map +1 -1
  25. package/dist/{capsule-crypto-7FJQINUR.js → capsule-crypto-YO5QJ6L3.js} +2 -2
  26. package/dist/causal-consolidation.d.ts +8 -2
  27. package/dist/causal-consolidation.js +13 -11
  28. package/dist/causal-consolidation.js.map +1 -1
  29. package/dist/{chunk-3BP57I6J.js → chunk-2F6NP3NT.js} +2 -1
  30. package/dist/{chunk-3BP57I6J.js.map → chunk-2F6NP3NT.js.map} +1 -1
  31. package/dist/{chunk-AU7Q3LSC.js → chunk-2QSZNTDO.js} +4 -4
  32. package/dist/{chunk-HSVJGWYS.js → chunk-2ROPI5OE.js} +2 -2
  33. package/dist/{chunk-C4SQJZAF.js → chunk-2SGJY2UY.js} +6 -3
  34. package/dist/chunk-2SGJY2UY.js.map +1 -0
  35. package/dist/{chunk-ZDTVJXIP.js → chunk-3MAONBX3.js} +13 -5
  36. package/dist/chunk-3MAONBX3.js.map +1 -0
  37. package/dist/{chunk-G3Z3QEF5.js → chunk-3PY7VHV7.js} +2 -2
  38. package/dist/chunk-3PY7VHV7.js.map +1 -0
  39. package/dist/{chunk-CF3ZF2YU.js → chunk-3QSU4NFF.js} +3 -3
  40. package/dist/{chunk-AJA46VX5.js → chunk-3T74IZB3.js} +11 -2
  41. package/dist/chunk-3T74IZB3.js.map +1 -0
  42. package/dist/{chunk-KVEVLBKC.js → chunk-4HFJQCJZ.js} +13 -8
  43. package/dist/chunk-4HFJQCJZ.js.map +1 -0
  44. package/dist/{chunk-KGK2QKWL.js → chunk-4R4KTDIE.js} +1 -1
  45. package/dist/chunk-4R4KTDIE.js.map +1 -0
  46. package/dist/{chunk-OI27U2HT.js → chunk-5BTCT236.js} +2 -2
  47. package/dist/{chunk-TH67Q46T.js → chunk-5OHHEORR.js} +64 -21
  48. package/dist/chunk-5OHHEORR.js.map +1 -0
  49. package/dist/{chunk-CO7ZO4TU.js → chunk-5VDJMYTF.js} +2 -2
  50. package/dist/{chunk-BFBF3XEF.js → chunk-6BDVBBBY.js} +33 -25
  51. package/dist/{chunk-BFBF3XEF.js.map → chunk-6BDVBBBY.js.map} +1 -1
  52. package/dist/{chunk-EAZGEEG2.js → chunk-6L46YAEZ.js} +45 -9
  53. package/dist/chunk-6L46YAEZ.js.map +1 -0
  54. package/dist/{chunk-YFS5OEKO.js → chunk-7MLB4NCL.js} +2 -2
  55. package/dist/{chunk-LZ3VEOU5.js → chunk-AL4RAJL5.js} +22 -5
  56. package/dist/chunk-AL4RAJL5.js.map +1 -0
  57. package/dist/{chunk-557IAFPD.js → chunk-APRRL26Q.js} +2 -2
  58. package/dist/{chunk-QDDHYAKV.js → chunk-AZDOWD2L.js} +2 -2
  59. package/dist/{chunk-MLT75J5S.js → chunk-B6SU7YSE.js} +3 -3
  60. package/dist/{chunk-FXKPZ3H6.js → chunk-BPSGLMQ4.js} +2 -2
  61. package/dist/{chunk-2NLLXCJG.js → chunk-BXLOS5AJ.js} +2 -2
  62. package/dist/{chunk-NOMEVTUD.js → chunk-C6C7XVKG.js} +5 -4
  63. package/dist/chunk-C6C7XVKG.js.map +1 -0
  64. package/dist/{chunk-XKIQZXUB.js → chunk-CI7RKSRE.js} +7 -1
  65. package/dist/chunk-CI7RKSRE.js.map +1 -0
  66. package/dist/{chunk-IK34DVAC.js → chunk-CIOMS6DI.js} +2 -2
  67. package/dist/{chunk-2I5JGH3M.js → chunk-CYEPCZN5.js} +2 -2
  68. package/dist/{chunk-2I5JGH3M.js.map → chunk-CYEPCZN5.js.map} +1 -1
  69. package/dist/{chunk-JHMFYY7L.js → chunk-DCGT4FPP.js} +13 -5
  70. package/dist/chunk-DCGT4FPP.js.map +1 -0
  71. package/dist/{chunk-7DZRO2DC.js → chunk-DEPRLVLK.js} +2 -2
  72. package/dist/{chunk-CSKLPDN6.js → chunk-DEVUWMME.js} +52 -19
  73. package/dist/chunk-DEVUWMME.js.map +1 -0
  74. package/dist/{chunk-DHGSZ3UD.js → chunk-DGNQRNLL.js} +2 -2
  75. package/dist/{chunk-X7Y7WX73.js → chunk-DQEMWVMT.js} +1 -1
  76. package/dist/{chunk-HJNQQICM.js → chunk-EXUAP5LH.js} +108 -51
  77. package/dist/chunk-EXUAP5LH.js.map +1 -0
  78. package/dist/chunk-FAV25DUZ.js +12 -0
  79. package/dist/chunk-FAV25DUZ.js.map +1 -0
  80. package/dist/{chunk-ETUPBUHB.js → chunk-GDASG7NC.js} +2 -2
  81. package/dist/{chunk-L227SKTB.js → chunk-GDB4J2H3.js} +17 -1
  82. package/dist/chunk-GDB4J2H3.js.map +1 -0
  83. package/dist/{chunk-IP73YCZP.js → chunk-GLPBYIXN.js} +4 -2
  84. package/dist/chunk-GLPBYIXN.js.map +1 -0
  85. package/dist/{chunk-4HP7HIE3.js → chunk-HP5FMB6L.js} +2 -2
  86. package/dist/{chunk-EVZFIAPG.js → chunk-IBTZEBUD.js} +23 -10
  87. package/dist/chunk-IBTZEBUD.js.map +1 -0
  88. package/dist/{chunk-DOX2CG6Y.js → chunk-IEUU7O4F.js} +2 -2
  89. package/dist/{chunk-EUML3N6B.js → chunk-IMA6GU4Y.js} +3 -3
  90. package/dist/chunk-IMA6GU4Y.js.map +1 -0
  91. package/dist/{chunk-JNANKJLN.js → chunk-JOASJWQR.js} +2 -2
  92. package/dist/chunk-JOASJWQR.js.map +1 -0
  93. package/dist/{chunk-WSGF57U2.js → chunk-JQDZQ4TB.js} +2 -2
  94. package/dist/{chunk-HINSGUA7.js → chunk-KBL3JJR6.js} +9 -13
  95. package/dist/chunk-KBL3JJR6.js.map +1 -0
  96. package/dist/{chunk-IOTENEVL.js → chunk-KGLPJROV.js} +57 -50
  97. package/dist/chunk-KGLPJROV.js.map +1 -0
  98. package/dist/{chunk-W7L6HXUC.js → chunk-LXOM6IQU.js} +2 -2
  99. package/dist/{chunk-G6R5UD3Q.js → chunk-MGN7VHWQ.js} +42 -1
  100. package/dist/{chunk-G6R5UD3Q.js.map → chunk-MGN7VHWQ.js.map} +1 -1
  101. package/dist/{chunk-DLJ4IR6M.js → chunk-MHQC2WU2.js} +2 -2
  102. package/dist/chunk-MHQC2WU2.js.map +1 -0
  103. package/dist/{chunk-5RPTH6AU.js → chunk-NM5NQYJE.js} +20 -19
  104. package/dist/chunk-NM5NQYJE.js.map +1 -0
  105. package/dist/{chunk-6JGNHWCI.js → chunk-OBIRVF36.js} +3 -3
  106. package/dist/{chunk-CHCA44C3.js → chunk-ODPLEWB6.js} +3 -3
  107. package/dist/chunk-ODPLEWB6.js.map +1 -0
  108. package/dist/{chunk-HENLZHIT.js → chunk-OIF36KGD.js} +7 -4
  109. package/dist/chunk-OIF36KGD.js.map +1 -0
  110. package/dist/{chunk-GUPISBV2.js → chunk-PP2JH3GP.js} +2 -2
  111. package/dist/{chunk-OXJBNGBK.js → chunk-PSUB67YB.js} +2 -2
  112. package/dist/{chunk-UWY7GIVS.js → chunk-PYIFUBRK.js} +45 -13
  113. package/dist/chunk-PYIFUBRK.js.map +1 -0
  114. package/dist/{chunk-KIB7SDIJ.js → chunk-Q6YIJGXJ.js} +2 -2
  115. package/dist/{chunk-ZT3EGNLR.js → chunk-QPD426WT.js} +2 -2
  116. package/dist/{chunk-RLV3PQGH.js → chunk-QVO4YOB7.js} +6 -6
  117. package/dist/{chunk-GMAG2HS4.js → chunk-RG3LBSGH.js} +46 -9
  118. package/dist/chunk-RG3LBSGH.js.map +1 -0
  119. package/dist/{chunk-XSWKORGM.js → chunk-S53OYO3F.js} +3 -1
  120. package/dist/chunk-S53OYO3F.js.map +1 -0
  121. package/dist/{chunk-YCN4BVDK.js → chunk-SCPFRKIT.js} +4 -2
  122. package/dist/chunk-SCPFRKIT.js.map +1 -0
  123. package/dist/{chunk-NZPF2SYV.js → chunk-T7N6KQGS.js} +138 -5
  124. package/dist/chunk-T7N6KQGS.js.map +1 -0
  125. package/dist/{chunk-VJXSUAO7.js → chunk-TNOWU6RP.js} +13 -10
  126. package/dist/chunk-TNOWU6RP.js.map +1 -0
  127. package/dist/{chunk-PCI747N2.js → chunk-TZVQQTG4.js} +48 -19
  128. package/dist/chunk-TZVQQTG4.js.map +1 -0
  129. package/dist/{chunk-KQAFEZQX.js → chunk-VDX2J7OX.js} +2 -2
  130. package/dist/{chunk-IK7DCC5H.js → chunk-VMGLYN42.js} +2 -2
  131. package/dist/{chunk-KM2A35EO.js → chunk-WB3LYXC5.js} +11 -7
  132. package/dist/chunk-WB3LYXC5.js.map +1 -0
  133. package/dist/{chunk-PPPZY2EU.js → chunk-WD2W4234.js} +9 -3
  134. package/dist/chunk-WD2W4234.js.map +1 -0
  135. package/dist/{chunk-NSKYFGDL.js → chunk-X4QQB7O6.js} +2 -2
  136. package/dist/{chunk-HPWVAEET.js → chunk-X6IRLNOO.js} +3 -7
  137. package/dist/chunk-X6IRLNOO.js.map +1 -0
  138. package/dist/{chunk-46GJIW5M.js → chunk-XAZOWLW4.js} +5 -5
  139. package/dist/{chunk-46GJIW5M.js.map → chunk-XAZOWLW4.js.map} +1 -1
  140. package/dist/{chunk-XPSVGJYA.js → chunk-YRMKDTKF.js} +12 -9
  141. package/dist/chunk-YRMKDTKF.js.map +1 -0
  142. package/dist/{chunk-6ZZP4EJF.js → chunk-ZJR7VG5L.js} +3 -3
  143. package/dist/{chunk-6ZZP4EJF.js.map → chunk-ZJR7VG5L.js.map} +1 -1
  144. package/dist/{chunk-2QANQKSQ.js → chunk-ZK32E74R.js} +156 -45
  145. package/dist/chunk-ZK32E74R.js.map +1 -0
  146. package/dist/{cli-OrfKXNU4.d.ts → cli-Cw729yLf.d.ts} +6 -2
  147. package/dist/cli.d.ts +3 -3
  148. package/dist/cli.js +61 -60
  149. package/dist/compounding/engine.js +3 -3
  150. package/dist/compounding/preference-consolidator.js +39 -11
  151. package/dist/compounding/preference-consolidator.js.map +1 -1
  152. package/dist/config.js +1 -1
  153. package/dist/connectors/codex-materialize-runner.js +3 -3
  154. package/dist/connectors/index.js +3 -3
  155. package/dist/consolidation-provenance-check.js +1 -1
  156. package/dist/contradiction/index.js +4 -4
  157. package/dist/conversation-index/backend.js +2 -2
  158. package/dist/conversation-index/indexer.js +1 -1
  159. package/dist/cross-namespace-budget.js +1 -1
  160. package/dist/enrichment/index.js +1 -1
  161. package/dist/entity-retrieval.js +3 -3
  162. package/dist/evals.js +1 -1
  163. package/dist/explicit-capture.d.ts +11 -1
  164. package/dist/explicit-capture.js +1 -1
  165. package/dist/extraction-judge.js +8 -1
  166. package/dist/extraction.js +2 -2
  167. package/dist/fallback-llm.d.ts +23 -6
  168. package/dist/fallback-llm.js +5 -3
  169. package/dist/{first-start-migration-GYJWIH36.js → first-start-migration-FF7YFGRP.js} +6 -6
  170. package/dist/index.d.ts +3 -3
  171. package/dist/index.js +95 -94
  172. package/dist/index.js.map +1 -1
  173. package/dist/lcm/archive.js +2 -2
  174. package/dist/lcm/engine.js +5 -5
  175. package/dist/lcm/index.js +7 -7
  176. package/dist/lcm/summarizer.js +3 -3
  177. package/dist/maintenance/memory-governance-cron.d.ts +6 -4
  178. package/dist/maintenance/memory-governance-cron.js +1 -1
  179. package/dist/maintenance/memory-governance.js +3 -3
  180. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +3 -3
  181. package/dist/maintenance/rebuild-memory-projection.js +4 -4
  182. package/dist/mcp-memory-inspector-app.d.ts +2 -2
  183. package/dist/mcp-memory-inspector-app.js +1 -1
  184. package/dist/migrate/from-engram.js +1 -1
  185. package/dist/namespaces/migrate.js +16 -15
  186. package/dist/namespaces/search.js +12 -11
  187. package/dist/namespaces/storage.js +3 -3
  188. package/dist/network/webdav.d.ts +2 -0
  189. package/dist/network/webdav.js +1 -1
  190. package/dist/objective-state-writers.js +2 -2
  191. package/dist/operator-toolkit.d.ts +3 -1
  192. package/dist/operator-toolkit.js +21 -20
  193. package/dist/{orchestrator-DTRQG75J.d.ts → orchestrator-CqWOjfgl.d.ts} +46 -3
  194. package/dist/orchestrator.d.ts +1 -1
  195. package/dist/orchestrator.js +48 -45
  196. package/dist/patterns-cli.js +1 -1
  197. package/dist/qmd-recall-cache.d.ts +2 -0
  198. package/dist/qmd-recall-cache.js +1 -1
  199. package/dist/qmd.d.ts +37 -2
  200. package/dist/qmd.js +4 -1
  201. package/dist/recall-explain-renderer.js +3 -3
  202. package/dist/recall-planner-llm.d.ts +57 -0
  203. package/dist/recall-planner-llm.js +167 -0
  204. package/dist/recall-planner-llm.js.map +1 -0
  205. package/dist/recall-xray-cli.js +4 -4
  206. package/dist/recall-xray-renderer.js +3 -3
  207. package/dist/recall-xray.js +2 -2
  208. package/dist/resume-bundles.js +2 -2
  209. package/dist/retrieval-agents.js +2 -2
  210. package/dist/routing/store.js +1 -1
  211. package/dist/schemas.d.ts +22 -22
  212. package/dist/search/factory.js +11 -10
  213. package/dist/search/index.js +11 -10
  214. package/dist/search/lancedb-backend.d.ts +1 -1
  215. package/dist/search/lancedb-backend.js +3 -2
  216. package/dist/search/meilisearch-backend.d.ts +1 -1
  217. package/dist/search/meilisearch-backend.js +3 -2
  218. package/dist/search/noop-backend.d.ts +1 -1
  219. package/dist/search/noop-backend.js +1 -1
  220. package/dist/search/orama-backend.d.ts +1 -1
  221. package/dist/search/orama-backend.js +3 -2
  222. package/dist/search/port.d.ts +6 -1
  223. package/dist/search/port.js +7 -0
  224. package/dist/search/remote-backend.d.ts +1 -1
  225. package/dist/search/remote-backend.js +1 -1
  226. package/dist/semantic-consolidation.js +4 -4
  227. package/dist/semantic-rule-promotion.js +3 -3
  228. package/dist/semantic-rule-verifier.js +3 -3
  229. package/dist/session-observer-state.js +1 -1
  230. package/dist/storage.js +2 -2
  231. package/dist/summarizer.js +2 -2
  232. package/dist/temporal-index.js +1 -1
  233. package/dist/{tier-stats-SKML2OSF.js → tier-stats-3LYQ3VV5.js} +3 -3
  234. package/dist/transfer/backup.js +2 -2
  235. package/dist/transfer/capsule-export.js +2 -2
  236. package/dist/transfer/capsule-import.js +2 -2
  237. package/dist/transfer/export-sqlite.js +1 -1
  238. package/dist/transfer/types.d.ts +12 -12
  239. package/dist/types.d.ts +32 -0
  240. package/dist/types.js +1 -1
  241. package/dist/utility-learner.js +1 -1
  242. package/dist/utility-runtime.js +2 -2
  243. package/dist/verified-recall.js +3 -3
  244. package/dist/work/board.js +2 -2
  245. package/dist/work/storage.d.ts +2 -0
  246. package/dist/work/storage.js +1 -1
  247. package/package.json +1 -1
  248. package/src/access-http.ts +24 -10
  249. package/src/access-mcp.test.ts +160 -0
  250. package/src/access-mcp.ts +72 -7
  251. package/src/access-schema.ts +11 -0
  252. package/src/access-service-coding-write.test.ts +478 -0
  253. package/src/access-service.ts +237 -32
  254. package/src/active-recall.test.ts +40 -0
  255. package/src/active-recall.ts +19 -2
  256. package/src/behavior-learner.ts +5 -3
  257. package/src/buffer-session.test.ts +58 -0
  258. package/src/buffer-surprise-trigger.test.ts +4 -18
  259. package/src/buffer.ts +39 -11
  260. package/src/calibration.ts +10 -4
  261. package/src/causal-consolidation.test.ts +47 -2
  262. package/src/causal-consolidation.ts +13 -9
  263. package/src/cli.ts +19 -4
  264. package/src/compounding/engine.ts +2 -0
  265. package/src/compounding/preference-consolidator.test.ts +292 -0
  266. package/src/compounding/preference-consolidator.ts +55 -19
  267. package/src/config.test.ts +213 -0
  268. package/src/config.ts +175 -4
  269. package/src/connectors/codex-materialize-runner.ts +7 -4
  270. package/src/consolidation-provenance-check.ts +24 -5
  271. package/src/conversation-index/indexer.test.ts +22 -0
  272. package/src/conversation-index/indexer.ts +7 -3
  273. package/src/cross-namespace-budget.test.ts +44 -21
  274. package/src/cross-namespace-budget.ts +2 -2
  275. package/src/enrichment/pipeline.ts +11 -16
  276. package/src/evals.ts +1 -1
  277. package/src/explicit-capture.ts +19 -2
  278. package/src/extraction-judge-chain.test.ts +55 -0
  279. package/src/extraction-judge.ts +7 -9
  280. package/src/extraction.ts +16 -5
  281. package/src/fallback-llm.test.ts +600 -1
  282. package/src/fallback-llm.ts +91 -22
  283. package/src/maintenance/memory-governance-cron.ts +39 -29
  284. package/src/mcp-memory-inspector-app.ts +54 -12
  285. package/src/message-parts/index.ts +6 -0
  286. package/src/message-parts/message-parts.test.ts +30 -0
  287. package/src/migrate/from-engram.ts +19 -5
  288. package/src/namespaces/search.test.ts +15 -2
  289. package/src/namespaces/search.ts +1 -1
  290. package/src/network/webdav.ts +61 -21
  291. package/src/operator-toolkit.ts +6 -2
  292. package/src/orchestrator.ts +173 -20
  293. package/src/qmd-client.test.ts +85 -0
  294. package/src/qmd-recall-cache.test.ts +16 -0
  295. package/src/qmd-recall-cache.ts +7 -0
  296. package/src/qmd.test.ts +54 -0
  297. package/src/qmd.ts +119 -19
  298. package/src/recall-planner-llm.test.ts +224 -0
  299. package/src/recall-planner-llm.ts +289 -0
  300. package/src/routing/store.ts +4 -8
  301. package/src/search/factory.ts +3 -0
  302. package/src/search/lancedb-backend.ts +15 -3
  303. package/src/search/meilisearch-backend.ts +70 -7
  304. package/src/search/noop-backend.ts +5 -1
  305. package/src/search/orama-backend.ts +15 -3
  306. package/src/search/port.ts +15 -0
  307. package/src/search/remote-backend.ts +5 -1
  308. package/src/session-observer-state.ts +1 -1
  309. package/src/summarizer.ts +3 -3
  310. package/src/temporal-index.test.ts +18 -0
  311. package/src/temporal-index.ts +45 -0
  312. package/src/training-export/cli-date-validation.test.ts +36 -0
  313. package/src/training-export/date-parse.ts +21 -2
  314. package/src/transfer/export-sqlite.ts +3 -0
  315. package/src/types.ts +35 -0
  316. package/src/utility-learner.ts +1 -0
  317. package/src/work/storage.ts +23 -0
  318. package/dist/chunk-2QANQKSQ.js.map +0 -1
  319. package/dist/chunk-5RPTH6AU.js.map +0 -1
  320. package/dist/chunk-AJA46VX5.js.map +0 -1
  321. package/dist/chunk-C4SQJZAF.js.map +0 -1
  322. package/dist/chunk-CHCA44C3.js.map +0 -1
  323. package/dist/chunk-CSKLPDN6.js.map +0 -1
  324. package/dist/chunk-DLJ4IR6M.js.map +0 -1
  325. package/dist/chunk-EAZGEEG2.js.map +0 -1
  326. package/dist/chunk-EUML3N6B.js.map +0 -1
  327. package/dist/chunk-EVZFIAPG.js.map +0 -1
  328. package/dist/chunk-G3Z3QEF5.js.map +0 -1
  329. package/dist/chunk-GMAG2HS4.js.map +0 -1
  330. package/dist/chunk-HENLZHIT.js.map +0 -1
  331. package/dist/chunk-HINSGUA7.js.map +0 -1
  332. package/dist/chunk-HJNQQICM.js.map +0 -1
  333. package/dist/chunk-HPWVAEET.js.map +0 -1
  334. package/dist/chunk-IOTENEVL.js.map +0 -1
  335. package/dist/chunk-IP73YCZP.js.map +0 -1
  336. package/dist/chunk-JHMFYY7L.js.map +0 -1
  337. package/dist/chunk-JNANKJLN.js.map +0 -1
  338. package/dist/chunk-KGK2QKWL.js.map +0 -1
  339. package/dist/chunk-KM2A35EO.js.map +0 -1
  340. package/dist/chunk-KVEVLBKC.js.map +0 -1
  341. package/dist/chunk-L227SKTB.js.map +0 -1
  342. package/dist/chunk-LZ3VEOU5.js.map +0 -1
  343. package/dist/chunk-NOMEVTUD.js.map +0 -1
  344. package/dist/chunk-NZPF2SYV.js.map +0 -1
  345. package/dist/chunk-PCI747N2.js.map +0 -1
  346. package/dist/chunk-PPPZY2EU.js.map +0 -1
  347. package/dist/chunk-TH67Q46T.js.map +0 -1
  348. package/dist/chunk-UWY7GIVS.js.map +0 -1
  349. package/dist/chunk-VJXSUAO7.js.map +0 -1
  350. package/dist/chunk-XKIQZXUB.js.map +0 -1
  351. package/dist/chunk-XPSVGJYA.js.map +0 -1
  352. package/dist/chunk-XSWKORGM.js.map +0 -1
  353. package/dist/chunk-YCN4BVDK.js.map +0 -1
  354. package/dist/chunk-ZDTVJXIP.js.map +0 -1
  355. /package/dist/{capsule-crypto-7FJQINUR.js.map → capsule-crypto-YO5QJ6L3.js.map} +0 -0
  356. /package/dist/{chunk-AU7Q3LSC.js.map → chunk-2QSZNTDO.js.map} +0 -0
  357. /package/dist/{chunk-HSVJGWYS.js.map → chunk-2ROPI5OE.js.map} +0 -0
  358. /package/dist/{chunk-CF3ZF2YU.js.map → chunk-3QSU4NFF.js.map} +0 -0
  359. /package/dist/{chunk-OI27U2HT.js.map → chunk-5BTCT236.js.map} +0 -0
  360. /package/dist/{chunk-CO7ZO4TU.js.map → chunk-5VDJMYTF.js.map} +0 -0
  361. /package/dist/{chunk-YFS5OEKO.js.map → chunk-7MLB4NCL.js.map} +0 -0
  362. /package/dist/{chunk-557IAFPD.js.map → chunk-APRRL26Q.js.map} +0 -0
  363. /package/dist/{chunk-QDDHYAKV.js.map → chunk-AZDOWD2L.js.map} +0 -0
  364. /package/dist/{chunk-MLT75J5S.js.map → chunk-B6SU7YSE.js.map} +0 -0
  365. /package/dist/{chunk-FXKPZ3H6.js.map → chunk-BPSGLMQ4.js.map} +0 -0
  366. /package/dist/{chunk-2NLLXCJG.js.map → chunk-BXLOS5AJ.js.map} +0 -0
  367. /package/dist/{chunk-IK34DVAC.js.map → chunk-CIOMS6DI.js.map} +0 -0
  368. /package/dist/{chunk-7DZRO2DC.js.map → chunk-DEPRLVLK.js.map} +0 -0
  369. /package/dist/{chunk-DHGSZ3UD.js.map → chunk-DGNQRNLL.js.map} +0 -0
  370. /package/dist/{chunk-X7Y7WX73.js.map → chunk-DQEMWVMT.js.map} +0 -0
  371. /package/dist/{chunk-ETUPBUHB.js.map → chunk-GDASG7NC.js.map} +0 -0
  372. /package/dist/{chunk-4HP7HIE3.js.map → chunk-HP5FMB6L.js.map} +0 -0
  373. /package/dist/{chunk-DOX2CG6Y.js.map → chunk-IEUU7O4F.js.map} +0 -0
  374. /package/dist/{chunk-WSGF57U2.js.map → chunk-JQDZQ4TB.js.map} +0 -0
  375. /package/dist/{chunk-W7L6HXUC.js.map → chunk-LXOM6IQU.js.map} +0 -0
  376. /package/dist/{chunk-6JGNHWCI.js.map → chunk-OBIRVF36.js.map} +0 -0
  377. /package/dist/{chunk-GUPISBV2.js.map → chunk-PP2JH3GP.js.map} +0 -0
  378. /package/dist/{chunk-OXJBNGBK.js.map → chunk-PSUB67YB.js.map} +0 -0
  379. /package/dist/{chunk-KIB7SDIJ.js.map → chunk-Q6YIJGXJ.js.map} +0 -0
  380. /package/dist/{chunk-ZT3EGNLR.js.map → chunk-QPD426WT.js.map} +0 -0
  381. /package/dist/{chunk-RLV3PQGH.js.map → chunk-QVO4YOB7.js.map} +0 -0
  382. /package/dist/{chunk-KQAFEZQX.js.map → chunk-VDX2J7OX.js.map} +0 -0
  383. /package/dist/{chunk-IK7DCC5H.js.map → chunk-VMGLYN42.js.map} +0 -0
  384. /package/dist/{chunk-NSKYFGDL.js.map → chunk-X4QQB7O6.js.map} +0 -0
  385. /package/dist/{first-start-migration-GYJWIH36.js.map → first-start-migration-FF7YFGRP.js.map} +0 -0
  386. /package/dist/{tier-stats-SKML2OSF.js.map → tier-stats-3LYQ3VV5.js.map} +0 -0
@@ -288,11 +288,21 @@ var WebDavServer = class _WebDavServer {
288
288
  if (!this.isPathInside(root.absolute, canonicalCandidate)) {
289
289
  return { ok: false, code: 403, message: "path escaped allowlist via symlink" };
290
290
  }
291
- return { ok: true, absolutePath: canonicalCandidate, displayPath: `/${segments.join("/")}`, rootAbsolute: root.absolute };
291
+ return {
292
+ ok: true,
293
+ absolutePath: canonicalCandidate,
294
+ displayPath: `/${segments.join("/")}`,
295
+ rootAbsolute: root.absolute
296
+ };
292
297
  } catch (err) {
293
298
  const code = err.code;
294
299
  if (code === "ENOENT") {
295
- return { ok: true, absolutePath: candidate, displayPath: `/${segments.join("/")}`, rootAbsolute: root.absolute };
300
+ return {
301
+ ok: true,
302
+ absolutePath: candidate,
303
+ displayPath: `/${segments.join("/")}`,
304
+ rootAbsolute: root.absolute
305
+ };
296
306
  }
297
307
  if (code === "ENOTDIR" || code === "ELOOP") {
298
308
  return { ok: false, code: 400, message: "invalid path" };
@@ -349,24 +359,46 @@ var WebDavServer = class _WebDavServer {
349
359
  if (info.isDirectory()) {
350
360
  const children = await readdir(absolutePath, { withFileTypes: true });
351
361
  for (const child of children) {
352
- const childHref = toEncodedHref(`${displayPath.replace(/\/$/, "")}/${child.name}`);
353
- entries.push(`
354
- <d:response>
355
- <d:href>${xmlEscape(childHref)}</d:href>
356
- <d:propstat><d:prop><d:resourcetype>${child.isDirectory() ? "<d:collection/>" : ""}</d:resourcetype></d:prop><d:status>HTTP/1.1 200 OK</d:status></d:propstat>
357
- </d:response>`);
362
+ const entry = await this.renderPropfindChildEntry(absolutePath, rootAbsolute, displayPath, child.name);
363
+ if (entry) {
364
+ entries.push(entry);
365
+ }
358
366
  }
359
367
  }
360
368
  const xml = `<?xml version="1.0" encoding="utf-8"?>
361
369
  <d:multistatus xmlns:d="DAV:">
362
- <d:response>
363
- <d:href>${xmlEscape(toEncodedHref(displayPath))}</d:href>
364
- <d:propstat><d:prop><d:resourcetype>${info.isDirectory() ? "<d:collection/>" : ""}</d:resourcetype></d:prop><d:status>HTTP/1.1 200 OK</d:status></d:propstat>
365
- </d:response>${entries.join("")}
370
+ ${this.renderPropfindResponse(toEncodedHref(displayPath), info.isDirectory())}${entries.join("")}
366
371
  </d:multistatus>`;
367
372
  res.writeHead(207, { "Content-Type": "application/xml; charset=utf-8" });
368
373
  res.end(xml);
369
374
  }
375
+ async renderPropfindChildEntry(parentAbsolutePath, rootAbsolute, displayPath, childName) {
376
+ const childAbsolutePath = path.join(parentAbsolutePath, childName);
377
+ let revalidated;
378
+ try {
379
+ revalidated = await this.revalidatePathInsideRoot(childAbsolutePath, rootAbsolute);
380
+ } catch {
381
+ return null;
382
+ }
383
+ if (!revalidated.ok) {
384
+ return null;
385
+ }
386
+ let info;
387
+ try {
388
+ info = await stat(revalidated.absolutePath);
389
+ } catch {
390
+ return null;
391
+ }
392
+ const childHref = toEncodedHref(`${displayPath.replace(/\/$/, "")}/${childName}`);
393
+ return this.renderPropfindResponse(childHref, info.isDirectory());
394
+ }
395
+ renderPropfindResponse(href, isDirectory) {
396
+ return ` <d:response>
397
+ <d:href>${xmlEscape(href)}</d:href>
398
+ <d:propstat><d:prop><d:resourcetype>${isDirectory ? "<d:collection/>" : ""}</d:resourcetype></d:prop><d:status>HTTP/1.1 200 OK</d:status></d:propstat>
399
+ </d:response>
400
+ `;
401
+ }
370
402
  isPathInside(root, target) {
371
403
  if (target === root) return true;
372
404
  if (root === path.parse(root).root) {
@@ -405,4 +437,4 @@ export {
405
437
  openWebDavFileForRead,
406
438
  WebDavServer
407
439
  };
408
- //# sourceMappingURL=chunk-UWY7GIVS.js.map
440
+ //# sourceMappingURL=chunk-PYIFUBRK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/network/webdav.ts"],"sourcesContent":["import { constants } from \"node:fs\";\nimport type { FileHandle } from \"node:fs/promises\";\nimport { mkdir, open, readdir, realpath, stat } from \"node:fs/promises\";\nimport { createServer, type IncomingMessage, type Server, type ServerResponse } from \"node:http\";\nimport { timingSafeEqual } from \"node:crypto\";\nimport path from \"node:path\";\nimport { pipeline } from \"node:stream/promises\";\nimport { URL } from \"node:url\";\n\nexport function hostToUrlAuthority(host: string): string {\n if (host.includes(\":\") && !host.startsWith(\"[\") && !host.endsWith(\"]\")) {\n return `[${host}]`;\n }\n return host;\n}\n\nexport interface WebDavAuth {\n username: string;\n password: string;\n}\n\nexport interface WebDavServerOptions {\n enabled?: boolean;\n host?: string;\n port: number;\n allowlistDirs: string[];\n auth?: WebDavAuth;\n}\n\nexport interface WebDavServerStatus {\n running: boolean;\n host: string;\n port: number;\n rootCount: number;\n}\n\ninterface AllowedRoot {\n absolute: string;\n name: string;\n}\n\ntype WebDavReadOpenResult =\n | { ok: true; handle: FileHandle; size: number }\n | { ok: false; code: number; message: string };\n\nfunction validateWebDavAuth(auth: WebDavAuth): WebDavAuth {\n if (typeof auth.username !== \"string\" || auth.username.trim().length === 0) {\n throw new Error(\"webdav auth.username must be a non-empty string\");\n }\n if (typeof auth.password !== \"string\" || auth.password.trim().length === 0) {\n throw new Error(\"webdav auth.password must be a non-empty string\");\n }\n return auth;\n}\n\nexport async function openWebDavFileForRead(absolutePath: string): Promise<WebDavReadOpenResult> {\n let handle: FileHandle | null = null;\n try {\n handle = await open(absolutePath, constants.O_RDONLY | constants.O_NOFOLLOW);\n const info = await handle.stat();\n if (!info.isFile()) {\n await handle.close().catch(() => {});\n return { ok: false, code: 403, message: \"path is not a file\" };\n }\n return { ok: true, handle, size: info.size };\n } catch (err) {\n await handle?.close().catch(() => {});\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ELOOP\") {\n return { ok: false, code: 403, message: \"path escaped allowlist via symlink\" };\n }\n if (code === \"ENOENT\" || code === \"ENOTDIR\") {\n return { ok: false, code: 404, message: \"not found\" };\n }\n return { ok: false, code: 404, message: \"not found\" };\n }\n}\n\nexport class WebDavServer {\n private readonly options: Required<Omit<WebDavServerOptions, \"auth\">> & Pick<WebDavServerOptions, \"auth\">;\n private readonly allowedRoots: AllowedRoot[];\n private server: Server | null = null;\n private startPromise: Promise<WebDavServerStatus> | null = null;\n private listening = false;\n private boundPort: number;\n\n private constructor(\n options: Required<Omit<WebDavServerOptions, \"auth\">> & Pick<WebDavServerOptions, \"auth\">,\n allowedRoots: AllowedRoot[]\n ) {\n this.options = options;\n this.allowedRoots = allowedRoots;\n this.boundPort = options.port;\n }\n\n static async create(input: WebDavServerOptions): Promise<WebDavServer> {\n const options: Required<Omit<WebDavServerOptions, \"auth\">> & Pick<WebDavServerOptions, \"auth\"> = {\n enabled: input.enabled ?? false,\n host: input.host ?? \"127.0.0.1\",\n port: input.port,\n allowlistDirs: input.allowlistDirs,\n auth: input.auth ? validateWebDavAuth(input.auth) : undefined,\n };\n\n if (!Array.isArray(options.allowlistDirs) || options.allowlistDirs.length === 0) {\n throw new Error(\"webdav allowlistDirs must include at least one directory\");\n }\n if (!Number.isInteger(options.port) || options.port < 0 || options.port > 65535) {\n throw new Error(`invalid webdav port: ${options.port}`);\n }\n\n const allowedRoots: AllowedRoot[] = [];\n const aliasSet = new Set<string>();\n for (const dir of options.allowlistDirs) {\n const resolved = path.resolve(dir);\n await mkdir(resolved, { recursive: true });\n const canonical = await realpath(resolved);\n const alias = path.basename(canonical) || \"root\";\n if (aliasSet.has(alias)) {\n throw new Error(`duplicate webdav allowlist alias: ${alias}`);\n }\n aliasSet.add(alias);\n allowedRoots.push({ absolute: canonical, name: alias });\n }\n\n return new WebDavServer(options, allowedRoots);\n }\n\n async start(): Promise<WebDavServerStatus> {\n if (!this.options.enabled) {\n throw new Error(\"webdav server is disabled; set enabled=true to start\");\n }\n if (this.server && this.listening) {\n return this.status();\n }\n if (this.startPromise) return this.startPromise;\n\n const server = createServer((req, res) => {\n this.handle(req, res).catch((err) => {\n if (res.headersSent) {\n res.destroy(err as Error);\n return;\n }\n res.writeHead(500, { \"Content-Type\": \"text/plain; charset=utf-8\" });\n res.end(\"webdav error\");\n });\n });\n this.server = server;\n this.listening = false;\n\n this.startPromise = (async () => {\n try {\n await new Promise<void>((resolve, reject) => {\n const onError = (err: Error) => {\n server.removeListener(\"listening\", onListening);\n server.removeListener(\"close\", onClose);\n reject(err);\n };\n const onListening = () => {\n server.removeListener(\"error\", onError);\n server.removeListener(\"close\", onClose);\n resolve();\n };\n const onClose = () => {\n server.removeListener(\"error\", onError);\n server.removeListener(\"listening\", onListening);\n reject(new Error(\"webdav server closed before listening\"));\n };\n server.once(\"error\", onError);\n server.once(\"listening\", onListening);\n server.once(\"close\", onClose);\n server.listen(this.options.port, this.options.host);\n });\n } catch (err) {\n if (this.server === server) {\n this.server = null;\n }\n this.listening = false;\n server.close();\n throw err;\n }\n\n const address = server.address();\n if (address && typeof address !== \"string\") {\n this.boundPort = address.port;\n }\n this.listening = true;\n\n return this.status();\n })();\n\n try {\n return await this.startPromise;\n } finally {\n this.startPromise = null;\n }\n }\n\n async stop(): Promise<void> {\n if (!this.server) return;\n const server = this.server;\n const pendingStart = this.startPromise;\n await new Promise<void>((resolve, reject) => {\n server.close((err) => {\n if (err && (err as NodeJS.ErrnoException).code !== \"ERR_SERVER_NOT_RUNNING\") {\n reject(err);\n return;\n }\n resolve();\n });\n });\n await pendingStart?.catch(() => undefined);\n if (this.server === server) {\n this.server = null;\n }\n this.listening = false;\n this.boundPort = this.options.port;\n }\n\n status(): WebDavServerStatus {\n return {\n running: this.server !== null && this.listening,\n host: this.options.host,\n port: this.boundPort,\n rootCount: this.allowedRoots.length,\n };\n }\n\n private async handle(req: IncomingMessage, res: ServerResponse): Promise<void> {\n if (!this.isAuthorized(req)) {\n res.writeHead(401, {\n \"WWW-Authenticate\": 'Basic realm=\"Engram WebDAV\"',\n \"Content-Type\": \"text/plain; charset=utf-8\",\n });\n res.end(\"authentication required\");\n return;\n }\n\n const method = (req.method ?? \"GET\").toUpperCase();\n if (method === \"OPTIONS\") {\n res.writeHead(204, {\n Allow: \"OPTIONS, PROPFIND, GET, HEAD\",\n DAV: \"1\",\n });\n res.end();\n return;\n }\n\n const parsed = new URL(req.url ?? \"/\", `http://${hostToUrlAuthority(this.options.host)}`);\n const resolved = await this.resolvePath(parsed.pathname);\n if (!resolved.ok) {\n res.writeHead(resolved.code, { \"Content-Type\": \"text/plain; charset=utf-8\" });\n res.end(resolved.message);\n return;\n }\n\n if (method === \"PROPFIND\") {\n await this.handlePropfind(resolved.absolutePath, resolved.rootAbsolute, resolved.displayPath, res);\n return;\n }\n\n if (method === \"GET\" || method === \"HEAD\") {\n await this.handleRead(method, resolved.absolutePath, resolved.rootAbsolute, res);\n return;\n }\n\n res.writeHead(405, {\n Allow: \"OPTIONS, PROPFIND, GET, HEAD\",\n \"Content-Type\": \"text/plain; charset=utf-8\",\n });\n res.end(\"method not allowed\");\n }\n\n private isAuthorized(req: IncomingMessage): boolean {\n if (!this.options.auth) return true;\n const raw = req.headers.authorization;\n if (!raw) return false;\n const separator = raw.indexOf(\" \");\n if (separator <= 0) return false;\n const scheme = raw.slice(0, separator).toLowerCase();\n if (scheme !== \"basic\") return false;\n const encodedPart = raw.slice(separator + 1).trim();\n if (!encodedPart) return false;\n\n try {\n const decoded = Buffer.from(encodedPart, \"base64\").toString(\"utf-8\");\n const credentialSeparator = decoded.indexOf(\":\");\n if (credentialSeparator < 0) return false;\n const username = decoded.slice(0, credentialSeparator);\n const password = decoded.slice(credentialSeparator + 1);\n const usernameOk = this.timingSafeStringEqual(username, this.options.auth.username);\n const passwordOk = this.timingSafeStringEqual(password, this.options.auth.password);\n return Boolean((usernameOk ? 1 : 0) & (passwordOk ? 1 : 0));\n } catch {\n return false;\n }\n }\n\n private timingSafeStringEqual(a: string, b: string): boolean {\n const left = this.encodeAuthField(a);\n const right = this.encodeAuthField(b);\n if (!left || !right) return false;\n return timingSafeEqual(left, right);\n }\n\n private encodeAuthField(value: string): Buffer | null {\n const maxBytes = 512;\n const encoded = Buffer.from(value, \"utf-8\");\n if (encoded.length > maxBytes) return null;\n const out = Buffer.alloc(2 + maxBytes);\n out.writeUInt16BE(encoded.length, 0);\n encoded.copy(out, 2);\n return out;\n }\n\n private async resolvePath(\n requestPathname: string\n ): Promise<\n | { ok: true; absolutePath: string; displayPath: string; rootAbsolute: string }\n | { ok: false; code: number; message: string }\n > {\n let decodedPath: string;\n try {\n decodedPath = decodeURIComponent(requestPathname || \"/\");\n } catch {\n return { ok: false, code: 400, message: \"invalid path encoding\" };\n }\n if (decodedPath.includes(\"\\0\")) {\n return { ok: false, code: 400, message: \"invalid path\" };\n }\n\n const normalized = path.posix.normalize(decodedPath);\n const segments = normalized.split(\"/\").filter((segment) => segment.length > 0);\n\n if (segments.length === 0) {\n return { ok: false, code: 403, message: \"root listing is not allowed\" };\n }\n\n const rootName = segments[0];\n const root = this.allowedRoots.find((entry) => entry.name === rootName);\n if (!root) {\n return { ok: false, code: 403, message: \"path is outside allowlist\" };\n }\n\n const relative = segments.slice(1);\n if (relative.some((segment) => segment === \"..\" || segment.includes(\"\\\\\"))) {\n return { ok: false, code: 403, message: \"path traversal is not allowed\" };\n }\n\n const candidate = path.resolve(root.absolute, ...relative);\n if (!this.isPathInside(root.absolute, candidate)) {\n return { ok: false, code: 403, message: \"path escaped allowlist\" };\n }\n\n try {\n const canonicalCandidate = await realpath(candidate);\n if (!this.isPathInside(root.absolute, canonicalCandidate)) {\n return { ok: false, code: 403, message: \"path escaped allowlist via symlink\" };\n }\n return {\n ok: true,\n absolutePath: canonicalCandidate,\n displayPath: `/${segments.join(\"/\")}`,\n rootAbsolute: root.absolute,\n };\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ENOENT\") {\n return {\n ok: true,\n absolutePath: candidate,\n displayPath: `/${segments.join(\"/\")}`,\n rootAbsolute: root.absolute,\n };\n }\n if (code === \"ENOTDIR\" || code === \"ELOOP\") {\n return { ok: false, code: 400, message: \"invalid path\" };\n }\n throw err;\n }\n }\n\n private async handleRead(\n method: \"GET\" | \"HEAD\",\n absolutePath: string,\n rootAbsolute: string,\n res: ServerResponse\n ): Promise<void> {\n const revalidated = await this.revalidatePathInsideRoot(absolutePath, rootAbsolute);\n if (!revalidated.ok) {\n res.writeHead(revalidated.code, { \"Content-Type\": \"text/plain; charset=utf-8\" });\n res.end(revalidated.message);\n return;\n }\n const opened = await openWebDavFileForRead(revalidated.absolutePath);\n if (!opened.ok) {\n res.writeHead(opened.code, { \"Content-Type\": \"text/plain; charset=utf-8\" });\n res.end(opened.message);\n return;\n }\n\n const { handle, size } = opened;\n\n try {\n res.writeHead(200, {\n \"Content-Length\": String(size),\n \"Content-Type\": \"application/octet-stream\",\n });\n\n if (method === \"HEAD\") {\n res.end();\n return;\n }\n\n await pipeline(handle.createReadStream({ autoClose: false }), res);\n } finally {\n await handle.close().catch(() => {});\n }\n }\n\n private async handlePropfind(\n absolutePath: string,\n rootAbsolute: string,\n displayPath: string,\n res: ServerResponse\n ): Promise<void> {\n const revalidated = await this.revalidatePathInsideRoot(absolutePath, rootAbsolute);\n if (!revalidated.ok) {\n res.writeHead(revalidated.code, { \"Content-Type\": \"text/plain; charset=utf-8\" });\n res.end(revalidated.message);\n return;\n }\n absolutePath = revalidated.absolutePath;\n let info;\n try {\n info = await stat(absolutePath);\n } catch {\n res.writeHead(404, { \"Content-Type\": \"text/plain; charset=utf-8\" });\n res.end(\"not found\");\n return;\n }\n\n const entries: string[] = [];\n if (info.isDirectory()) {\n const children = await readdir(absolutePath, { withFileTypes: true });\n for (const child of children) {\n const entry = await this.renderPropfindChildEntry(absolutePath, rootAbsolute, displayPath, child.name);\n if (entry) {\n entries.push(entry);\n }\n }\n }\n\n const xml = `<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<d:multistatus xmlns:d=\"DAV:\">\n${this.renderPropfindResponse(toEncodedHref(displayPath), info.isDirectory())}${entries.join(\"\")}\n</d:multistatus>`;\n\n res.writeHead(207, { \"Content-Type\": \"application/xml; charset=utf-8\" });\n res.end(xml);\n }\n\n private async renderPropfindChildEntry(\n parentAbsolutePath: string,\n rootAbsolute: string,\n displayPath: string,\n childName: string\n ): Promise<string | null> {\n const childAbsolutePath = path.join(parentAbsolutePath, childName);\n let revalidated;\n try {\n revalidated = await this.revalidatePathInsideRoot(childAbsolutePath, rootAbsolute);\n } catch {\n return null;\n }\n if (!revalidated.ok) {\n return null;\n }\n\n let info;\n try {\n info = await stat(revalidated.absolutePath);\n } catch {\n return null;\n }\n\n const childHref = toEncodedHref(`${displayPath.replace(/\\/$/, \"\")}/${childName}`);\n return this.renderPropfindResponse(childHref, info.isDirectory());\n }\n\n private renderPropfindResponse(href: string, isDirectory: boolean): string {\n return ` <d:response>\n <d:href>${xmlEscape(href)}</d:href>\n <d:propstat><d:prop><d:resourcetype>${isDirectory ? \"<d:collection/>\" : \"\"}</d:resourcetype></d:prop><d:status>HTTP/1.1 200 OK</d:status></d:propstat>\n </d:response>\n`;\n }\n\n private isPathInside(root: string, target: string): boolean {\n if (target === root) return true;\n if (root === path.parse(root).root) {\n return target.startsWith(root);\n }\n return target.startsWith(`${root}${path.sep}`);\n }\n\n private async revalidatePathInsideRoot(\n absolutePath: string,\n rootAbsolute: string\n ): Promise<{ ok: true; absolutePath: string } | { ok: false; code: number; message: string }> {\n try {\n const canonical = await realpath(absolutePath);\n if (!this.isPathInside(rootAbsolute, canonical)) {\n return { ok: false, code: 403, message: \"path escaped allowlist via symlink\" };\n }\n return { ok: true, absolutePath: canonical };\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ENOENT\" || code === \"ENOTDIR\") {\n return { ok: false, code: 404, message: \"not found\" };\n }\n if (code === \"ELOOP\") {\n return { ok: false, code: 403, message: \"path escaped allowlist via symlink\" };\n }\n throw err;\n }\n }\n}\n\nfunction xmlEscape(value: string): string {\n return value\n .replaceAll(\"&\", \"&amp;\")\n .replaceAll(\"<\", \"&lt;\")\n .replaceAll(\">\", \"&gt;\")\n .replaceAll('\"', \"&quot;\")\n .replaceAll(\"'\", \"&apos;\");\n}\n\nfunction toEncodedHref(pathname: string): string {\n return pathname\n .split(\"/\")\n .map((segment) => encodeURIComponent(segment))\n .join(\"/\");\n}\n"],"mappings":"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","names":[]}
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  rebuildConversationChunksFailOpen,
6
6
  upsertConversationChunksFailOpen
7
- } from "./chunk-HENLZHIT.js";
7
+ } from "./chunk-OIF36KGD.js";
8
8
  import {
9
9
  searchConversationIndex,
10
10
  searchConversationIndexFaissFailOpen
@@ -235,4 +235,4 @@ function createFaissBackend(getFaiss) {
235
235
  export {
236
236
  createConversationIndexBackend
237
237
  };
238
- //# sourceMappingURL=chunk-KIB7SDIJ.js.map
238
+ //# sourceMappingURL=chunk-Q6YIJGXJ.js.map
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-HQ6NIBL6.js";
4
4
  import {
5
5
  StorageManager
6
- } from "./chunk-YFS5OEKO.js";
6
+ } from "./chunk-7MLB4NCL.js";
7
7
  import {
8
8
  getCachedEpisodeMap,
9
9
  setCachedEpisodeMap
@@ -105,4 +105,4 @@ async function searchVerifiedEpisodes(options) {
105
105
  export {
106
106
  searchVerifiedEpisodes
107
107
  };
108
- //# sourceMappingURL=chunk-ZT3EGNLR.js.map
108
+ //# sourceMappingURL=chunk-QPD426WT.js.map
@@ -1,3 +1,6 @@
1
+ import {
2
+ LcmDag
3
+ } from "./chunk-7N4KAIGN.js";
1
4
  import {
2
5
  LcmWorkQueue
3
6
  } from "./chunk-TPDBFYEG.js";
@@ -10,13 +13,10 @@ import {
10
13
  } from "./chunk-7XYTQGCC.js";
11
14
  import {
12
15
  LcmSummarizer
13
- } from "./chunk-CO7ZO4TU.js";
16
+ } from "./chunk-5VDJMYTF.js";
14
17
  import {
15
18
  LcmArchive
16
- } from "./chunk-4HP7HIE3.js";
17
- import {
18
- LcmDag
19
- } from "./chunk-7N4KAIGN.js";
19
+ } from "./chunk-HP5FMB6L.js";
20
20
  import {
21
21
  log
22
22
  } from "./chunk-2ODBA7MQ.js";
@@ -513,4 +513,4 @@ export {
513
513
  extractLcmConfig,
514
514
  LcmEngine
515
515
  };
516
- //# sourceMappingURL=chunk-RLV3PQGH.js.map
516
+ //# sourceMappingURL=chunk-QVO4YOB7.js.map
@@ -1,10 +1,13 @@
1
+ import {
2
+ scanMemoryDir
3
+ } from "./chunk-Q4CAQGKQ.js";
1
4
  import {
2
5
  isSearchAborted,
3
6
  throwIfSearchAborted
4
7
  } from "./chunk-CINZGPSJ.js";
5
8
  import {
6
- scanMemoryDir
7
- } from "./chunk-Q4CAQGKQ.js";
9
+ resolveEnsureCollectionArgs
10
+ } from "./chunk-FAV25DUZ.js";
8
11
  import {
9
12
  log
10
13
  } from "./chunk-2ODBA7MQ.js";
@@ -104,6 +107,8 @@ var MeilisearchBackend = class {
104
107
  if (!this.available) return;
105
108
  if (isSearchAborted(execution)) return;
106
109
  try {
110
+ const ensured = await this.ensureCollection(memoryDir, collection, execution);
111
+ if (ensured === "skipped" || ensured === "missing") return;
107
112
  const client = await this.ensureClient();
108
113
  if (isSearchAborted(execution)) return;
109
114
  const docs = await scanMemoryDir(memoryDir);
@@ -151,19 +156,40 @@ var MeilisearchBackend = class {
151
156
  }
152
157
  async embedCollection(collection) {
153
158
  }
154
- async ensureCollection(_memoryDir, _execution) {
159
+ async ensureCollection(_memoryDir, collectionOrExecution, execution) {
160
+ const { collection, execution: effectiveExecution } = resolveEnsureCollectionArgs(
161
+ collectionOrExecution,
162
+ execution
163
+ );
155
164
  if (!this.available) return "skipped";
165
+ if (isSearchAborted(effectiveExecution)) return "skipped";
166
+ const targetCollection = collection ?? this.collection;
156
167
  try {
157
168
  const client = await this.ensureClient();
169
+ if (isSearchAborted(effectiveExecution)) return "skipped";
158
170
  try {
159
- await client.getIndex(this.collection);
171
+ await client.getIndex(targetCollection);
160
172
  return "present";
161
- } catch {
162
- await client.createIndex(this.collection, { primaryKey: "id" });
173
+ } catch (err) {
174
+ if (!isMeilisearchIndexNotFoundError(err)) {
175
+ log.debug(
176
+ `MeilisearchBackend collection check unavailable for "${targetCollection}" (will not skip update): ${err instanceof Error ? err.message : String(err)}`
177
+ );
178
+ return "unknown";
179
+ }
180
+ if (isSearchAborted(effectiveExecution)) return "skipped";
181
+ const createTask = await client.createIndex(targetCollection, { primaryKey: "id" });
182
+ if (isSearchAborted(effectiveExecution)) return "skipped";
183
+ if (createTask?.taskUid !== void 0 && createTask?.taskUid !== null) {
184
+ await client.waitForTask(createTask.taskUid, { timeOutMs: this.timeoutMs });
185
+ }
163
186
  return "present";
164
187
  }
165
- } catch {
166
- return "skipped";
188
+ } catch (err) {
189
+ log.debug(
190
+ `MeilisearchBackend collection check failed for "${targetCollection}" (will not disable updates): ${err instanceof Error ? err.message : String(err)}`
191
+ );
192
+ return "unknown";
167
193
  }
168
194
  }
169
195
  async ensureClient() {
@@ -204,8 +230,19 @@ var MeilisearchBackend = class {
204
230
  }));
205
231
  }
206
232
  };
233
+ function isMeilisearchIndexNotFoundError(err) {
234
+ if (!err || typeof err !== "object") {
235
+ return false;
236
+ }
237
+ const record = err;
238
+ const code = typeof record.code === "string" ? record.code.toLowerCase() : "";
239
+ const type = typeof record.type === "string" ? record.type.toLowerCase() : "";
240
+ const status = typeof record.status === "number" ? record.status : typeof record.statusCode === "number" ? record.statusCode : void 0;
241
+ const message = err instanceof Error ? err.message.toLowerCase() : typeof record.message === "string" ? record.message.toLowerCase() : "";
242
+ return code === "index_not_found" || type === "index_not_found" || status === 404 || message.includes("index_not_found") || /index .*not found/.test(message) || /index .*does not exist/.test(message);
243
+ }
207
244
 
208
245
  export {
209
246
  MeilisearchBackend
210
247
  };
211
- //# sourceMappingURL=chunk-GMAG2HS4.js.map
248
+ //# sourceMappingURL=chunk-RG3LBSGH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/search/meilisearch-backend.ts"],"sourcesContent":["import { log } from \"../logger.js\";\nimport {\n resolveEnsureCollectionArgs,\n type SearchBackend,\n type SearchExecutionOptions,\n type SearchQueryOptions,\n type SearchResult,\n} from \"./port.js\";\nimport { scanMemoryDir } from \"./document-scanner.js\";\nimport { isSearchAborted, throwIfSearchAborted } from \"./abort.js\";\n\nexport interface MeilisearchBackendOptions {\n host: string;\n apiKey?: string;\n collection: string;\n timeoutMs?: number;\n autoIndex?: boolean;\n memoryDir?: string;\n}\n\n/**\n * Meilisearch search backend — server-based SDK client.\n *\n * Requires a running Meilisearch instance. Uses the official `meilisearch` SDK.\n * When `autoIndex` is true, update() pushes docs from the local memory directory.\n */\nexport class MeilisearchBackend implements SearchBackend {\n private readonly host: string;\n private readonly apiKey?: string;\n private readonly collection: string;\n private readonly timeoutMs: number;\n private readonly autoIndex: boolean;\n private readonly memoryDir?: string;\n private available = false;\n private client: any = null;\n private meiliModule: any = null;\n\n constructor(opts: MeilisearchBackendOptions) {\n this.host = opts.host;\n this.apiKey = opts.apiKey;\n this.collection = opts.collection;\n this.timeoutMs = opts.timeoutMs ?? 30_000;\n this.autoIndex = opts.autoIndex ?? false;\n this.memoryDir = opts.memoryDir;\n }\n\n async probe(): Promise<boolean> {\n try {\n const client = await this.ensureClient();\n await client.health();\n this.available = true;\n return true;\n } catch (err) {\n log.debug(`MeilisearchBackend probe failed: ${err}`);\n this.available = false;\n return false;\n }\n }\n\n isAvailable(): boolean {\n return this.available;\n }\n\n debugStatus(): string {\n return `backend=meilisearch available=${this.available} host=${this.host}`;\n }\n\n async search(\n query: string,\n collection?: string,\n maxResults?: number,\n _options?: SearchQueryOptions,\n execution?: SearchExecutionOptions,\n ): Promise<SearchResult[]> {\n if (isSearchAborted(execution)) return [];\n // Try hybrid first; fall back to plain FTS only if hybrid throws (e.g. no embedder configured)\n try {\n return await this.doSearch(query, maxResults ?? 10, { hybrid: { semanticRatio: 0.5, embedder: \"default\" } }, collection, true, execution);\n } catch {\n if (isSearchAborted(execution)) return [];\n return this.bm25Search(query, collection, maxResults, execution);\n }\n }\n\n async searchGlobal(query: string, maxResults?: number, execution?: SearchExecutionOptions): Promise<SearchResult[]> {\n const limit = maxResults ?? 10;\n if (!this.available) return [];\n\n try {\n throwIfSearchAborted(execution, \"MeilisearchBackend global search aborted\");\n const client = await this.ensureClient();\n const indexes = await client.getIndexes();\n throwIfSearchAborted(execution, \"MeilisearchBackend global search aborted\");\n const queries = (indexes.results ?? []).map((idx: any) => ({\n indexUid: idx.uid,\n q: query,\n limit,\n showRankingScore: true,\n }));\n if (queries.length === 0) return [];\n\n const multiResult = await client.multiSearch({ queries });\n throwIfSearchAborted(execution, \"MeilisearchBackend global search aborted\");\n const allResults: SearchResult[] = [];\n for (const result of multiResult.results ?? []) {\n allResults.push(...this.mapHits(result.hits ?? []));\n }\n allResults.sort((a, b) => b.score - a.score);\n return allResults.slice(0, limit);\n } catch (err) {\n log.debug(`MeilisearchBackend searchGlobal failed: ${err}`);\n return [];\n }\n }\n\n async bm25Search(query: string, collection?: string, maxResults?: number, execution?: SearchExecutionOptions): Promise<SearchResult[]> {\n return this.doSearch(query, maxResults ?? 10, undefined, collection, false, execution);\n }\n\n async vectorSearch(query: string, collection?: string, maxResults?: number, execution?: SearchExecutionOptions): Promise<SearchResult[]> {\n return this.doSearch(query, maxResults ?? 10, { hybrid: { semanticRatio: 1.0, embedder: \"default\" } }, collection, false, execution);\n }\n\n async hybridSearch(query: string, collection?: string, maxResults?: number, execution?: SearchExecutionOptions): Promise<SearchResult[]> {\n return this.doSearch(query, maxResults ?? 10, { hybrid: { semanticRatio: 0.5, embedder: \"default\" } }, collection, false, execution);\n }\n\n async update(execution?: SearchExecutionOptions): Promise<void> {\n await this.updateCollection(this.collection, execution);\n }\n\n async updateCollection(collection: string, execution?: SearchExecutionOptions): Promise<void> {\n if (!this.memoryDir) return;\n await this.updateCollectionFromDir(collection, this.memoryDir, execution);\n }\n\n async updateCollectionFromDir(collection: string, memoryDir: string, execution?: SearchExecutionOptions): Promise<void> {\n if (!this.autoIndex) return;\n if (!this.available) return;\n if (isSearchAborted(execution)) return;\n\n try {\n const ensured = await this.ensureCollection(memoryDir, collection, execution);\n if (ensured === \"skipped\" || ensured === \"missing\") return;\n const client = await this.ensureClient();\n if (isSearchAborted(execution)) return;\n const docs = await scanMemoryDir(memoryDir);\n if (isSearchAborted(execution)) return;\n const index = client.index(collection);\n\n const meilDocs = docs.map((d) => ({\n id: d.docid,\n path: d.path,\n content: d.content,\n snippet: d.snippet,\n }));\n\n // Upsert current docs and wait for the task to complete\n if (isSearchAborted(execution)) return;\n const addTask = await index.addDocuments(meilDocs, { primaryKey: \"id\" });\n await client.waitForTask(addTask.taskUid, { timeOutMs: this.timeoutMs });\n if (isSearchAborted(execution)) return;\n\n // Remove docs that no longer exist on disk (paginated to handle large indexes)\n const currentIds = new Set(docs.map((d) => d.docid));\n try {\n const PAGE_SIZE = 1000;\n let offset = 0;\n let staleIds: string[] = [];\n let hasMore = true;\n while (hasMore) {\n if (isSearchAborted(execution)) return;\n const page = await index.getDocuments({ limit: PAGE_SIZE, offset, fields: [\"id\"] });\n const results = page.results ?? [];\n for (const doc of results) {\n const id = doc.id as string;\n if (!currentIds.has(id)) staleIds.push(id);\n }\n offset += results.length;\n hasMore = results.length === PAGE_SIZE;\n }\n if (staleIds.length > 0) {\n if (isSearchAborted(execution)) return;\n const delTask = await index.deleteDocuments(staleIds);\n await client.waitForTask(delTask.taskUid, { timeOutMs: this.timeoutMs });\n }\n } catch {\n // Deletion cleanup is best-effort\n }\n } catch (err) {\n log.debug(`MeilisearchBackend update failed: ${err}`);\n }\n }\n\n async embed(): Promise<void> {\n // Meilisearch handles its own embedding when configured with an embedder\n }\n\n async embedCollection(collection: string): Promise<void> {\n // Meilisearch handles its own embedding when configured with an embedder\n // The collection parameter is accepted for interface compliance but Meilisearch\n // manages embeddings server-side per index (collection).\n }\n\n async ensureCollection(\n _memoryDir: string,\n collectionOrExecution?: string | SearchExecutionOptions,\n execution?: SearchExecutionOptions,\n ): Promise<\"present\" | \"missing\" | \"unknown\" | \"skipped\"> {\n const { collection, execution: effectiveExecution } = resolveEnsureCollectionArgs(\n collectionOrExecution,\n execution,\n );\n if (!this.available) return \"skipped\";\n if (isSearchAborted(effectiveExecution)) return \"skipped\";\n const targetCollection = collection ?? this.collection;\n try {\n const client = await this.ensureClient();\n if (isSearchAborted(effectiveExecution)) return \"skipped\";\n try {\n await client.getIndex(targetCollection);\n return \"present\";\n } catch (err) {\n if (!isMeilisearchIndexNotFoundError(err)) {\n log.debug(\n `MeilisearchBackend collection check unavailable for \"${targetCollection}\" (will not skip update): ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n return \"unknown\";\n }\n // Index doesn't exist — create it\n if (isSearchAborted(effectiveExecution)) return \"skipped\";\n const createTask = await client.createIndex(targetCollection, { primaryKey: \"id\" });\n if (isSearchAborted(effectiveExecution)) return \"skipped\";\n if (createTask?.taskUid !== undefined && createTask?.taskUid !== null) {\n await client.waitForTask(createTask.taskUid, { timeOutMs: this.timeoutMs });\n }\n return \"present\";\n }\n } catch (err) {\n log.debug(\n `MeilisearchBackend collection check failed for \"${targetCollection}\" (will not disable updates): ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n return \"unknown\";\n }\n }\n\n private async ensureClient(): Promise<any> {\n if (this.client) return this.client;\n if (!this.meiliModule) {\n this.meiliModule = await import(\"meilisearch\");\n }\n const MeiliSearch = this.meiliModule.MeiliSearch ?? this.meiliModule.default?.MeiliSearch;\n this.client = new MeiliSearch({\n host: this.host,\n apiKey: this.apiKey,\n timeout: this.timeoutMs,\n });\n return this.client;\n }\n\n private async doSearch(\n query: string,\n limit: number,\n extra?: Record<string, unknown>,\n collection?: string,\n rethrow = false,\n execution?: SearchExecutionOptions,\n ): Promise<SearchResult[]> {\n if (!this.available) return [];\n if (isSearchAborted(execution)) return [];\n try {\n const client = await this.ensureClient();\n throwIfSearchAborted(execution, \"MeilisearchBackend search aborted\");\n const index = client.index(collection ?? this.collection);\n const result = await index.search(query, { limit, showRankingScore: true, ...extra });\n throwIfSearchAborted(execution, \"MeilisearchBackend search aborted\");\n return this.mapHits(result.hits ?? []);\n } catch (err) {\n log.debug(`MeilisearchBackend search failed: ${err}`);\n if (rethrow) throw err;\n return [];\n }\n }\n\n private mapHits(hits: any[]): SearchResult[] {\n return hits.map((hit) => ({\n docid: hit.id ?? \"\",\n path: hit.path ?? \"\",\n snippet: hit._formatted?.content ?? hit.snippet ?? hit.content?.slice(0, 200) ?? \"\",\n score: hit._rankingScore ?? 0.5,\n }));\n }\n}\n\nfunction isMeilisearchIndexNotFoundError(err: unknown): boolean {\n if (!err || typeof err !== \"object\") {\n return false;\n }\n const record = err as Record<string, unknown>;\n const code = typeof record.code === \"string\" ? record.code.toLowerCase() : \"\";\n const type = typeof record.type === \"string\" ? record.type.toLowerCase() : \"\";\n const status =\n typeof record.status === \"number\"\n ? record.status\n : typeof record.statusCode === \"number\"\n ? record.statusCode\n : undefined;\n const message = err instanceof Error\n ? err.message.toLowerCase()\n : typeof record.message === \"string\"\n ? record.message.toLowerCase()\n : \"\";\n\n return (\n code === \"index_not_found\"\n || type === \"index_not_found\"\n || status === 404\n || message.includes(\"index_not_found\")\n || /index .*not found/.test(message)\n || /index .*does not exist/.test(message)\n );\n}\n"],"mappings":"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","names":[]}
@@ -17,6 +17,7 @@ import {
17
17
 
18
18
  // src/transfer/export-sqlite.ts
19
19
  import path from "path";
20
+ import { mkdir } from "fs/promises";
20
21
  async function exportSqlite(opts) {
21
22
  const includeTranscripts = opts.includeTranscripts === true;
22
23
  const memDirAbs = path.resolve(opts.memoryDir);
@@ -30,6 +31,7 @@ async function exportSqlite(opts) {
30
31
  const { content, sha256, bytes } = await readUtf8FileStrict(abs);
31
32
  rows.push({ rel: relPosix, bytes, sha256, content });
32
33
  }
34
+ await mkdir(path.dirname(outAbs), { recursive: true });
33
35
  const db = openBetterSqlite3(outAbs);
34
36
  try {
35
37
  db.exec("PRAGMA journal_mode=WAL;");
@@ -56,4 +58,4 @@ async function exportSqlite(opts) {
56
58
  export {
57
59
  exportSqlite
58
60
  };
59
- //# sourceMappingURL=chunk-XSWKORGM.js.map
61
+ //# sourceMappingURL=chunk-S53OYO3F.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/transfer/export-sqlite.ts"],"sourcesContent":["import path from \"node:path\";\nimport { mkdir } from \"node:fs/promises\";\nimport { SQLITE_SCHEMA_VERSION, SQLITE_TABLES_SQL } from \"./sqlite-schema.js\";\nimport { listFilesRecursive, readUtf8FileStrict, toPosixRelPath } from \"./fs-utils.js\";\nimport { openBetterSqlite3 } from \"../runtime/better-sqlite.js\";\nimport { computeTransferOutputRel, isTransferPathExcluded } from \"./exclusions.js\";\n\nexport interface ExportSqliteOptions {\n memoryDir: string;\n outFile: string;\n includeTranscripts?: boolean;\n pluginVersion: string;\n}\n\nexport async function exportSqlite(opts: ExportSqliteOptions): Promise<void> {\n const includeTranscripts = opts.includeTranscripts === true;\n const memDirAbs = path.resolve(opts.memoryDir);\n const outAbs = path.resolve(opts.outFile);\n const outputRelPosix = computeTransferOutputRel(memDirAbs, outAbs);\n\n const filesAbs = await listFilesRecursive(memDirAbs);\n const rows: Array<{ rel: string; bytes: number; sha256: string; content: string }> = [];\n for (const abs of filesAbs) {\n const relPosix = toPosixRelPath(abs, memDirAbs);\n if (isTransferPathExcluded(relPosix, { includeTranscripts, outputRelPosix })) continue;\n const { content, sha256, bytes } = await readUtf8FileStrict(abs);\n rows.push({ rel: relPosix, bytes, sha256, content });\n }\n\n await mkdir(path.dirname(outAbs), { recursive: true });\n\n const db = openBetterSqlite3(outAbs);\n try {\n db.exec(\"PRAGMA journal_mode=WAL;\");\n db.exec(SQLITE_TABLES_SQL);\n\n const insertMeta = db.prepare(\"INSERT OR REPLACE INTO meta(key,value) VALUES (?,?)\");\n const insertFile = db.prepare(\n \"INSERT OR REPLACE INTO files(path_rel, bytes, sha256, content) VALUES (?,?,?,?)\",\n );\n\n const tx = db.transaction((rows: Array<{ rel: string; bytes: number; sha256: string; content: string }>) => {\n db.prepare(\"DELETE FROM meta\").run();\n db.prepare(\"DELETE FROM files\").run();\n insertMeta.run(\"schemaVersion\", String(SQLITE_SCHEMA_VERSION));\n insertMeta.run(\"createdAt\", new Date().toISOString());\n insertMeta.run(\"pluginVersion\", opts.pluginVersion);\n insertMeta.run(\"includesTranscripts\", includeTranscripts ? \"true\" : \"false\");\n for (const r of rows) insertFile.run(r.rel, r.bytes, r.sha256, r.content);\n });\n\n tx(rows);\n } finally {\n db.close();\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA,OAAO,UAAU;AACjB,SAAS,aAAa;AAatB,eAAsB,aAAa,MAA0C;AAC3E,QAAM,qBAAqB,KAAK,uBAAuB;AACvD,QAAM,YAAY,KAAK,QAAQ,KAAK,SAAS;AAC7C,QAAM,SAAS,KAAK,QAAQ,KAAK,OAAO;AACxC,QAAM,iBAAiB,yBAAyB,WAAW,MAAM;AAEjE,QAAM,WAAW,MAAM,mBAAmB,SAAS;AACnD,QAAM,OAA+E,CAAC;AACtF,aAAW,OAAO,UAAU;AAC1B,UAAM,WAAW,eAAe,KAAK,SAAS;AAC9C,QAAI,uBAAuB,UAAU,EAAE,oBAAoB,eAAe,CAAC,EAAG;AAC9E,UAAM,EAAE,SAAS,QAAQ,MAAM,IAAI,MAAM,mBAAmB,GAAG;AAC/D,SAAK,KAAK,EAAE,KAAK,UAAU,OAAO,QAAQ,QAAQ,CAAC;AAAA,EACrD;AAEA,QAAM,MAAM,KAAK,QAAQ,MAAM,GAAG,EAAE,WAAW,KAAK,CAAC;AAErD,QAAM,KAAK,kBAAkB,MAAM;AACnC,MAAI;AACF,OAAG,KAAK,0BAA0B;AAClC,OAAG,KAAK,iBAAiB;AAEzB,UAAM,aAAa,GAAG,QAAQ,qDAAqD;AACnF,UAAM,aAAa,GAAG;AAAA,MACpB;AAAA,IACF;AAEA,UAAM,KAAK,GAAG,YAAY,CAACA,UAAiF;AAC1G,SAAG,QAAQ,kBAAkB,EAAE,IAAI;AACnC,SAAG,QAAQ,mBAAmB,EAAE,IAAI;AACpC,iBAAW,IAAI,iBAAiB,OAAO,qBAAqB,CAAC;AAC7D,iBAAW,IAAI,cAAa,oBAAI,KAAK,GAAE,YAAY,CAAC;AACpD,iBAAW,IAAI,iBAAiB,KAAK,aAAa;AAClD,iBAAW,IAAI,uBAAuB,qBAAqB,SAAS,OAAO;AAC3E,iBAAW,KAAKA,MAAM,YAAW,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO;AAAA,IAC1E,CAAC;AAED,OAAG,IAAI;AAAA,EACT,UAAE;AACA,OAAG,MAAM;AAAA,EACX;AACF;","names":["rows"]}
@@ -24,7 +24,9 @@ function buildQmdRecallCacheKey(options) {
24
24
  maxResults: options.maxResults,
25
25
  memoryDir: normalizePathScope(options.memoryDir),
26
26
  collection: options.collection ?? "",
27
- searchOptions: normalizeSearchOptions(options.searchOptions)
27
+ searchOptions: normalizeSearchOptions(options.searchOptions),
28
+ searchStrategy: options.searchStrategy ?? "",
29
+ subprocessStrategy: options.subprocessStrategy ?? ""
28
30
  });
29
31
  }
30
32
  function getCachedQmdRecall(cacheKey, options) {
@@ -63,4 +65,4 @@ export {
63
65
  setCachedQmdRecall,
64
66
  clearQmdRecallCache
65
67
  };
66
- //# sourceMappingURL=chunk-YCN4BVDK.js.map
68
+ //# sourceMappingURL=chunk-SCPFRKIT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/qmd-recall-cache.ts"],"sourcesContent":["import type { RecallPlanMode } from \"./types.js\";\nimport type { SearchQueryOptions } from \"./search/port.js\";\n\ntype QmdRecallCacheEntry = {\n value: unknown;\n cachedAtMs: number;\n};\n\nexport type QmdRecallCacheSource = \"fresh\" | \"stale\";\n\nexport interface QmdRecallCacheHit<T> {\n value: T;\n source: QmdRecallCacheSource;\n ageMs: number;\n}\n\nexport interface QmdRecallCacheKeyOptions {\n query: string;\n namespaces: string[];\n recallMode: RecallPlanMode;\n maxResults: number;\n memoryDir?: string;\n collection?: string;\n searchOptions?: SearchQueryOptions;\n // QMD search/subprocess strategies change the recalled results, so they must\n // participate in the cache key — otherwise a different strategy's cached QMD\n // phase is served within the TTL (gotcha #37). Issue #1335 (codex review #1422).\n searchStrategy?: string;\n subprocessStrategy?: string;\n}\n\nconst qmdRecallCache = new Map<string, QmdRecallCacheEntry>();\n\nfunction cloneCacheValue<T>(value: T): T {\n return structuredClone(value);\n}\n\nfunction normalizeQuery(query: string): string {\n return query.trim().toLowerCase().replace(/\\s+/g, \" \");\n}\n\nfunction normalizePathScope(pathValue: string | undefined): string {\n if (typeof pathValue !== \"string\") return \"\";\n return pathValue.trim().replace(/\\\\/g, \"/\");\n}\n\nfunction normalizeSearchOptions(\n options: SearchQueryOptions | undefined,\n): Record<string, unknown> {\n if (!options) return {};\n return Object.fromEntries(\n Object.entries(options)\n .filter(([, value]) => value !== undefined)\n .sort(([left], [right]) => left.localeCompare(right)),\n );\n}\n\nexport function buildQmdRecallCacheKey(\n options: QmdRecallCacheKeyOptions,\n): string {\n return JSON.stringify({\n query: normalizeQuery(options.query),\n namespaces: [...options.namespaces].sort(),\n recallMode: options.recallMode,\n maxResults: options.maxResults,\n memoryDir: normalizePathScope(options.memoryDir),\n collection: options.collection ?? \"\",\n searchOptions: normalizeSearchOptions(options.searchOptions),\n searchStrategy: options.searchStrategy ?? \"\",\n subprocessStrategy: options.subprocessStrategy ?? \"\",\n });\n}\n\nexport function getCachedQmdRecall<T>(\n cacheKey: string,\n options: {\n freshTtlMs: number;\n staleTtlMs: number;\n },\n): QmdRecallCacheHit<T> | null {\n const entry = qmdRecallCache.get(cacheKey);\n if (!entry) return null;\n\n const ageMs = Date.now() - entry.cachedAtMs;\n if (ageMs <= options.freshTtlMs) {\n return { value: cloneCacheValue(entry.value as T), source: \"fresh\", ageMs };\n }\n if (ageMs <= options.staleTtlMs) {\n return { value: cloneCacheValue(entry.value as T), source: \"stale\", ageMs };\n }\n\n qmdRecallCache.delete(cacheKey);\n return null;\n}\n\nexport function setCachedQmdRecall<T>(\n cacheKey: string,\n value: T,\n options: { maxEntries: number },\n): void {\n qmdRecallCache.delete(cacheKey);\n if (options.maxEntries <= 0) return;\n\n qmdRecallCache.set(cacheKey, {\n value: cloneCacheValue(value),\n cachedAtMs: Date.now(),\n });\n\n while (qmdRecallCache.size > options.maxEntries) {\n const oldestKey = qmdRecallCache.keys().next().value;\n if (typeof oldestKey !== \"string\") break;\n qmdRecallCache.delete(oldestKey);\n }\n}\n\nexport function clearQmdRecallCache(): void {\n qmdRecallCache.clear();\n}\n"],"mappings":";AA+BA,IAAM,iBAAiB,oBAAI,IAAiC;AAE5D,SAAS,gBAAmB,OAAa;AACvC,SAAO,gBAAgB,KAAK;AAC9B;AAEA,SAAS,eAAe,OAAuB;AAC7C,SAAO,MAAM,KAAK,EAAE,YAAY,EAAE,QAAQ,QAAQ,GAAG;AACvD;AAEA,SAAS,mBAAmB,WAAuC;AACjE,MAAI,OAAO,cAAc,SAAU,QAAO;AAC1C,SAAO,UAAU,KAAK,EAAE,QAAQ,OAAO,GAAG;AAC5C;AAEA,SAAS,uBACP,SACyB;AACzB,MAAI,CAAC,QAAS,QAAO,CAAC;AACtB,SAAO,OAAO;AAAA,IACZ,OAAO,QAAQ,OAAO,EACnB,OAAO,CAAC,CAAC,EAAE,KAAK,MAAM,UAAU,MAAS,EACzC,KAAK,CAAC,CAAC,IAAI,GAAG,CAAC,KAAK,MAAM,KAAK,cAAc,KAAK,CAAC;AAAA,EACxD;AACF;AAEO,SAAS,uBACd,SACQ;AACR,SAAO,KAAK,UAAU;AAAA,IACpB,OAAO,eAAe,QAAQ,KAAK;AAAA,IACnC,YAAY,CAAC,GAAG,QAAQ,UAAU,EAAE,KAAK;AAAA,IACzC,YAAY,QAAQ;AAAA,IACpB,YAAY,QAAQ;AAAA,IACpB,WAAW,mBAAmB,QAAQ,SAAS;AAAA,IAC/C,YAAY,QAAQ,cAAc;AAAA,IAClC,eAAe,uBAAuB,QAAQ,aAAa;AAAA,IAC3D,gBAAgB,QAAQ,kBAAkB;AAAA,IAC1C,oBAAoB,QAAQ,sBAAsB;AAAA,EACpD,CAAC;AACH;AAEO,SAAS,mBACd,UACA,SAI6B;AAC7B,QAAM,QAAQ,eAAe,IAAI,QAAQ;AACzC,MAAI,CAAC,MAAO,QAAO;AAEnB,QAAM,QAAQ,KAAK,IAAI,IAAI,MAAM;AACjC,MAAI,SAAS,QAAQ,YAAY;AAC/B,WAAO,EAAE,OAAO,gBAAgB,MAAM,KAAU,GAAG,QAAQ,SAAS,MAAM;AAAA,EAC5E;AACA,MAAI,SAAS,QAAQ,YAAY;AAC/B,WAAO,EAAE,OAAO,gBAAgB,MAAM,KAAU,GAAG,QAAQ,SAAS,MAAM;AAAA,EAC5E;AAEA,iBAAe,OAAO,QAAQ;AAC9B,SAAO;AACT;AAEO,SAAS,mBACd,UACA,OACA,SACM;AACN,iBAAe,OAAO,QAAQ;AAC9B,MAAI,QAAQ,cAAc,EAAG;AAE7B,iBAAe,IAAI,UAAU;AAAA,IAC3B,OAAO,gBAAgB,KAAK;AAAA,IAC5B,YAAY,KAAK,IAAI;AAAA,EACvB,CAAC;AAED,SAAO,eAAe,OAAO,QAAQ,YAAY;AAC/C,UAAM,YAAY,eAAe,KAAK,EAAE,KAAK,EAAE;AAC/C,QAAI,OAAO,cAAc,SAAU;AACnC,mBAAe,OAAO,SAAS;AAAA,EACjC;AACF;AAEO,SAAS,sBAA4B;AAC1C,iBAAe,MAAM;AACvB;","names":[]}
@@ -47,6 +47,60 @@ function parseBoundedIntegerMs(value, fallback, min, max) {
47
47
  if (coerced === void 0) return fallback;
48
48
  return Math.min(max, Math.max(min, Math.floor(coerced)));
49
49
  }
50
+ function isQualifiedModelString(value) {
51
+ const slash = value.indexOf("/");
52
+ return slash > 0 && slash < value.length - 1;
53
+ }
54
+ function parseModelChainConfig(value, keyName) {
55
+ if (value === void 0 || value === null) return void 0;
56
+ if (typeof value !== "object" || Array.isArray(value)) {
57
+ throw new Error(
58
+ `${keyName} must be an object like { "primary": "provider/model", "fallbacks": ["provider/model", ...] }; got ${JSON.stringify(value)}`
59
+ );
60
+ }
61
+ const raw = value;
62
+ const unknownKeys = Object.keys(raw).filter((k) => k !== "primary" && k !== "fallbacks");
63
+ if (unknownKeys.length > 0) {
64
+ throw new Error(
65
+ `${keyName} has unknown propert${unknownKeys.length === 1 ? "y" : "ies"}: ${unknownKeys.join(", ")}. Allowed: "primary", "fallbacks".`
66
+ );
67
+ }
68
+ if (typeof raw.primary !== "string" || raw.primary.trim().length === 0) {
69
+ throw new Error(
70
+ `${keyName}.primary is required and must be a non-empty "provider/model" string; got ${JSON.stringify(raw.primary)}`
71
+ );
72
+ }
73
+ const primary = raw.primary.trim();
74
+ if (!isQualifiedModelString(primary)) {
75
+ throw new Error(
76
+ `${keyName}.primary must be in "provider/model" form (e.g. "zai/glm-4.7-flash"); got ${JSON.stringify(primary)}`
77
+ );
78
+ }
79
+ let dedupedFallbacks;
80
+ if (raw.fallbacks !== void 0) {
81
+ if (!Array.isArray(raw.fallbacks)) {
82
+ throw new Error(
83
+ `${keyName}.fallbacks must be an array of "provider/model" strings; got ${JSON.stringify(raw.fallbacks)}`
84
+ );
85
+ }
86
+ if (raw.fallbacks.some((item) => typeof item !== "string")) {
87
+ throw new Error(`${keyName}.fallbacks must contain only strings`);
88
+ }
89
+ const trimmed = raw.fallbacks.map((item) => item.trim()).filter((item) => item.length > 0 && item !== primary);
90
+ for (const fb of trimmed) {
91
+ if (!isQualifiedModelString(fb)) {
92
+ throw new Error(
93
+ `${keyName}.fallbacks entries must be in "provider/model" form; got ${JSON.stringify(fb)}`
94
+ );
95
+ }
96
+ }
97
+ dedupedFallbacks = [...new Set(trimmed)];
98
+ }
99
+ return {
100
+ primary,
101
+ ...dedupedFallbacks && dedupedFallbacks.length > 0 ? { fallbacks: dedupedFallbacks } : {}
102
+ };
103
+ }
50
104
  function parsePositiveInteger(value, keyName) {
51
105
  if (value === void 0 || value === null) return void 0;
52
106
  const coerced = coerceNumber(value);
@@ -96,6 +150,44 @@ function parseQmdChunkStrategy(value) {
96
150
  if (normalized === "auto" || normalized === "regex") return normalized;
97
151
  throw new Error(`qmdChunkStrategy must be "auto" or "regex"; got ${JSON.stringify(value)}`);
98
152
  }
153
+ function parseQmdSearchStrategy(value) {
154
+ if (value === void 0 || value === null) return "hybrid";
155
+ if (typeof value !== "string") {
156
+ throw new Error(
157
+ `qmdSearchStrategy must be one of "hybrid", "lex-vec", or "lex"; got ${JSON.stringify(value)}`
158
+ );
159
+ }
160
+ const normalized = value.trim().toLowerCase();
161
+ if (normalized === "hybrid" || normalized === "lex-vec" || normalized === "lex") {
162
+ return normalized;
163
+ }
164
+ throw new Error(
165
+ `qmdSearchStrategy must be one of "hybrid", "lex-vec", or "lex"; got ${JSON.stringify(value)}`
166
+ );
167
+ }
168
+ function parseQmdDaemonTimeoutMs(value) {
169
+ if (value === void 0 || value === null) return 8e3;
170
+ const coerced = coerceNumber(value);
171
+ if (coerced === void 0 || !Number.isInteger(coerced)) {
172
+ throw new Error(
173
+ `qmdDaemonTimeoutMs must be an integer number of milliseconds between 1000 and 120000; got ${JSON.stringify(value)}`
174
+ );
175
+ }
176
+ return Math.min(12e4, Math.max(1e3, coerced));
177
+ }
178
+ function parseQmdSubprocessStrategy(value) {
179
+ if (value === void 0 || value === null) return "query";
180
+ if (typeof value !== "string") {
181
+ throw new Error(
182
+ `qmdSubprocessStrategy must be one of "query" or "search"; got ${JSON.stringify(value)}`
183
+ );
184
+ }
185
+ const normalized = value.trim().toLowerCase();
186
+ if (normalized === "query" || normalized === "search") return normalized;
187
+ throw new Error(
188
+ `qmdSubprocessStrategy must be one of "query" or "search"; got ${JSON.stringify(value)}`
189
+ );
190
+ }
99
191
  function parseOptionalNonEmptyString(value) {
100
192
  if (typeof value !== "string") return void 0;
101
193
  const normalized = value.trim();
@@ -149,6 +241,29 @@ function coerceBooleanLike(value) {
149
241
  }
150
242
  return void 0;
151
243
  }
244
+ function resolveEmitLegacyTools(configValue) {
245
+ const ACCEPTED = "true/false/1/0/yes/no/on/off";
246
+ if (configValue !== void 0 && configValue !== null) {
247
+ const coerced = coerceBooleanLike(configValue);
248
+ if (coerced === void 0) {
249
+ throw new Error(
250
+ `emitLegacyTools must be a boolean-like value (${ACCEPTED}); got ${JSON.stringify(configValue)}`
251
+ );
252
+ }
253
+ return coerced;
254
+ }
255
+ const envRaw = readEnvVar("REMNIC_EMIT_LEGACY_TOOLS") ?? readEnvVar("ENGRAM_EMIT_LEGACY_TOOLS");
256
+ if (envRaw !== void 0) {
257
+ const coerced = coerceBooleanLike(envRaw);
258
+ if (coerced === void 0) {
259
+ throw new Error(
260
+ `REMNIC_EMIT_LEGACY_TOOLS must be a boolean-like value (${ACCEPTED}); got "${envRaw}"`
261
+ );
262
+ }
263
+ return coerced;
264
+ }
265
+ return true;
266
+ }
152
267
  function isOpenaiApiKeyDisabled(value) {
153
268
  return value === false || typeof value === "string" && value.trim().toLowerCase() === "false";
154
269
  }
@@ -755,6 +870,9 @@ function parseConfig(raw) {
755
870
  qmdChunkStrategy: parseQmdChunkStrategy(cfg.qmdChunkStrategy),
756
871
  qmdCandidateLimit: parsePositiveInteger(cfg.qmdCandidateLimit, "qmdCandidateLimit"),
757
872
  qmdQueryRerankEnabled: coerceBooleanLike(cfg.qmdQueryRerankEnabled) ?? true,
873
+ qmdSearchStrategy: parseQmdSearchStrategy(cfg.qmdSearchStrategy),
874
+ qmdSubprocessStrategy: parseQmdSubprocessStrategy(cfg.qmdSubprocessStrategy),
875
+ qmdDaemonTimeoutMs: parseQmdDaemonTimeoutMs(cfg.qmdDaemonTimeoutMs),
758
876
  qmdIndexName: parseOptionalNonEmptyString(cfg.qmdIndexName),
759
877
  qmdForceCpu: coerceBooleanLike(cfg.qmdForceCpu) ?? false,
760
878
  qmdGpuBackend: parseQmdGpuBackend(cfg.qmdGpuBackend),
@@ -1404,6 +1522,7 @@ function parseConfig(raw) {
1404
1522
  modelSource,
1405
1523
  gatewayAgentId: typeof cfg.gatewayAgentId === "string" && cfg.gatewayAgentId.length > 0 ? cfg.gatewayAgentId : "",
1406
1524
  fastGatewayAgentId: typeof cfg.fastGatewayAgentId === "string" && cfg.fastGatewayAgentId.length > 0 ? cfg.fastGatewayAgentId : "",
1525
+ taskModelChain: parseModelChainConfig(cfg.taskModelChain, "taskModelChain"),
1407
1526
  // v3.0 namespaces (default off)
1408
1527
  namespacesEnabled: cfg.namespacesEnabled === true,
1409
1528
  defaultNamespace: typeof cfg.defaultNamespace === "string" && cfg.defaultNamespace.length > 0 ? cfg.defaultNamespace : "default",
@@ -1602,14 +1721,24 @@ function parseConfig(raw) {
1602
1721
  behaviorLoopMaxDeltaPerCycle: typeof cfg.behaviorLoopMaxDeltaPerCycle === "number" ? Math.min(1, Math.max(0, cfg.behaviorLoopMaxDeltaPerCycle)) : 0.1,
1603
1722
  behaviorLoopProtectedParams: Array.isArray(cfg.behaviorLoopProtectedParams) ? cfg.behaviorLoopProtectedParams.filter((param) => typeof param === "string" && param.trim().length > 0) : [...DEFAULT_BEHAVIOR_LOOP_PROTECTED_PARAMS],
1604
1723
  // v8.0 phase 1
1605
- recallPlannerEnabled: cfg.recallPlannerEnabled !== false,
1724
+ // All recallPlanner boolean gates coerce boolean-like strings so CLI/env
1725
+ // surfaces (`--config recallPlanner*=true|false`) behave correctly — the
1726
+ // shadow-mode/telemetry/enable flags are documented rollout switches and
1727
+ // must not silently ignore string values (gotcha #36, #1428 review).
1728
+ recallPlannerEnabled: coerceBooleanLike(cfg.recallPlannerEnabled) ?? true,
1729
+ // Issue #1367 / Option C: LLM-based recall planning is opt-in so the
1730
+ // default recall path stays heuristic (no added latency / LLM call unless
1731
+ // the operator asks for it — gotcha #30). Coerce boolean-like strings so
1732
+ // CLI/env surfaces (`--config recallPlannerLlmEnabled=true`) actually
1733
+ // enable it (gotcha #36); defaults off.
1734
+ recallPlannerLlmEnabled: coerceBooleanLike(cfg.recallPlannerLlmEnabled) ?? false,
1606
1735
  recallPlannerModel: typeof cfg.recallPlannerModel === "string" && cfg.recallPlannerModel.trim().length > 0 ? cfg.recallPlannerModel.trim() : DEFAULT_REASONING_MODEL,
1607
1736
  recallPlannerTimeoutMs: typeof cfg.recallPlannerTimeoutMs === "number" ? cfg.recallPlannerTimeoutMs : 1500,
1608
- recallPlannerUseResponsesApi: cfg.recallPlannerUseResponsesApi !== false,
1737
+ recallPlannerUseResponsesApi: coerceBooleanLike(cfg.recallPlannerUseResponsesApi) ?? true,
1609
1738
  recallPlannerMaxPromptChars: typeof cfg.recallPlannerMaxPromptChars === "number" ? cfg.recallPlannerMaxPromptChars : 4e3,
1610
1739
  recallPlannerMaxMemoryHints: typeof cfg.recallPlannerMaxMemoryHints === "number" ? cfg.recallPlannerMaxMemoryHints : 24,
1611
- recallPlannerShadowMode: cfg.recallPlannerShadowMode === true,
1612
- recallPlannerTelemetryEnabled: cfg.recallPlannerTelemetryEnabled !== false,
1740
+ recallPlannerShadowMode: coerceBooleanLike(cfg.recallPlannerShadowMode) ?? false,
1741
+ recallPlannerTelemetryEnabled: coerceBooleanLike(cfg.recallPlannerTelemetryEnabled) ?? true,
1613
1742
  recallPlannerMaxQmdResultsMinimal: typeof cfg.recallPlannerMaxQmdResultsMinimal === "number" ? cfg.recallPlannerMaxQmdResultsMinimal : 4,
1614
1743
  recallPlannerMaxQmdResultsFull: typeof cfg.recallPlannerMaxQmdResultsFull === "number" ? cfg.recallPlannerMaxQmdResultsFull : 8,
1615
1744
  intentRoutingEnabled: cfg.intentRoutingEnabled === true,
@@ -2021,6 +2150,10 @@ function parseConfig(raw) {
2021
2150
  return "none";
2022
2151
  })(),
2023
2152
  binaryLifecycleBackendPath: typeof cfg.binaryLifecycleBackendPath === "string" ? cfg.binaryLifecycleBackendPath.trim() : "",
2153
+ // Legacy MCP tool aliases opt-out (issue #1427). Config field wins; then
2154
+ // the REMNIC_/ENGRAM_ env var (gotcha #9); default true for back-compat.
2155
+ // Malformed values fail fast rather than silently defaulting (gotcha #51).
2156
+ emitLegacyTools: resolveEmitLegacyTools(cfg.emitLegacyTools),
2024
2157
  // Codex citation parity (issue #379)
2025
2158
  citationsEnabled: cfg.citationsEnabled === true,
2026
2159
  citationsAutoDetect: cfg.citationsAutoDetect !== false,
@@ -2303,4 +2436,4 @@ export {
2303
2436
  VALID_MEMORY_CATEGORIES,
2304
2437
  parseConfig
2305
2438
  };
2306
- //# sourceMappingURL=chunk-NZPF2SYV.js.map
2439
+ //# sourceMappingURL=chunk-T7N6KQGS.js.map