@remnic/core 9.3.613 → 9.3.614

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 (376) hide show
  1. package/dist/access-cli.js +58 -57
  2. package/dist/access-cli.js.map +1 -1
  3. package/dist/access-http.d.ts +4 -2
  4. package/dist/access-http.js +22 -22
  5. package/dist/access-mcp.d.ts +9 -2
  6. package/dist/access-mcp.js +19 -19
  7. package/dist/access-schema.d.ts +12 -12
  8. package/dist/access-schema.js +3 -3
  9. package/dist/{access-service-D2J9dh_9.d.ts → access-service-DGG_2xPK.d.ts} +1 -1
  10. package/dist/access-service.d.ts +2 -2
  11. package/dist/access-service.js +16 -16
  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-CO7ZO4TU.js → chunk-5VDJMYTF.js} +2 -2
  48. package/dist/{chunk-BFBF3XEF.js → chunk-6BDVBBBY.js} +33 -25
  49. package/dist/{chunk-BFBF3XEF.js.map → chunk-6BDVBBBY.js.map} +1 -1
  50. package/dist/{chunk-EAZGEEG2.js → chunk-6L46YAEZ.js} +45 -9
  51. package/dist/chunk-6L46YAEZ.js.map +1 -0
  52. package/dist/{chunk-YFS5OEKO.js → chunk-7MLB4NCL.js} +2 -2
  53. package/dist/{chunk-IOTENEVL.js → chunk-7YQFWOF7.js} +57 -50
  54. package/dist/chunk-7YQFWOF7.js.map +1 -0
  55. package/dist/{chunk-2QANQKSQ.js → chunk-ADNZVFXG.js} +15 -15
  56. package/dist/{chunk-LZ3VEOU5.js → chunk-AL4RAJL5.js} +22 -5
  57. package/dist/chunk-AL4RAJL5.js.map +1 -0
  58. package/dist/{chunk-557IAFPD.js → chunk-APRRL26Q.js} +2 -2
  59. package/dist/{chunk-QDDHYAKV.js → chunk-AZDOWD2L.js} +2 -2
  60. package/dist/{chunk-TH67Q46T.js → chunk-B6FDZPCF.js} +17 -9
  61. package/dist/chunk-B6FDZPCF.js.map +1 -0
  62. package/dist/{chunk-MLT75J5S.js → chunk-B6SU7YSE.js} +3 -3
  63. package/dist/{chunk-FXKPZ3H6.js → chunk-BPSGLMQ4.js} +2 -2
  64. package/dist/{chunk-2NLLXCJG.js → chunk-BXLOS5AJ.js} +2 -2
  65. package/dist/{chunk-NOMEVTUD.js → chunk-C6C7XVKG.js} +5 -4
  66. package/dist/chunk-C6C7XVKG.js.map +1 -0
  67. package/dist/{chunk-XKIQZXUB.js → chunk-CI7RKSRE.js} +7 -1
  68. package/dist/chunk-CI7RKSRE.js.map +1 -0
  69. package/dist/{chunk-IK34DVAC.js → chunk-CIOMS6DI.js} +2 -2
  70. package/dist/{chunk-2I5JGH3M.js → chunk-CYEPCZN5.js} +2 -2
  71. package/dist/{chunk-2I5JGH3M.js.map → chunk-CYEPCZN5.js.map} +1 -1
  72. package/dist/{chunk-JHMFYY7L.js → chunk-DCGT4FPP.js} +13 -5
  73. package/dist/chunk-DCGT4FPP.js.map +1 -0
  74. package/dist/{chunk-7DZRO2DC.js → chunk-DEPRLVLK.js} +2 -2
  75. package/dist/{chunk-CSKLPDN6.js → chunk-DEVUWMME.js} +52 -19
  76. package/dist/chunk-DEVUWMME.js.map +1 -0
  77. package/dist/{chunk-DHGSZ3UD.js → chunk-DGNQRNLL.js} +2 -2
  78. package/dist/{chunk-X7Y7WX73.js → chunk-DQEMWVMT.js} +1 -1
  79. package/dist/chunk-FAV25DUZ.js +12 -0
  80. package/dist/chunk-FAV25DUZ.js.map +1 -0
  81. package/dist/{chunk-ETUPBUHB.js → chunk-GDASG7NC.js} +2 -2
  82. package/dist/{chunk-L227SKTB.js → chunk-GDB4J2H3.js} +17 -1
  83. package/dist/chunk-GDB4J2H3.js.map +1 -0
  84. package/dist/{chunk-IP73YCZP.js → chunk-GLPBYIXN.js} +4 -2
  85. package/dist/chunk-GLPBYIXN.js.map +1 -0
  86. package/dist/{chunk-4HP7HIE3.js → chunk-HP5FMB6L.js} +2 -2
  87. package/dist/{chunk-EVZFIAPG.js → chunk-IBTZEBUD.js} +23 -10
  88. package/dist/chunk-IBTZEBUD.js.map +1 -0
  89. package/dist/{chunk-DOX2CG6Y.js → chunk-IEUU7O4F.js} +2 -2
  90. package/dist/{chunk-JNANKJLN.js → chunk-JOASJWQR.js} +2 -2
  91. package/dist/chunk-JOASJWQR.js.map +1 -0
  92. package/dist/{chunk-WSGF57U2.js → chunk-JQDZQ4TB.js} +2 -2
  93. package/dist/{chunk-HINSGUA7.js → chunk-KBL3JJR6.js} +9 -13
  94. package/dist/chunk-KBL3JJR6.js.map +1 -0
  95. package/dist/{chunk-W7L6HXUC.js → chunk-LXOM6IQU.js} +2 -2
  96. package/dist/{chunk-G6R5UD3Q.js → chunk-MGN7VHWQ.js} +42 -1
  97. package/dist/{chunk-G6R5UD3Q.js.map → chunk-MGN7VHWQ.js.map} +1 -1
  98. package/dist/{chunk-DLJ4IR6M.js → chunk-MHQC2WU2.js} +2 -2
  99. package/dist/chunk-MHQC2WU2.js.map +1 -0
  100. package/dist/{chunk-6JGNHWCI.js → chunk-OBIRVF36.js} +3 -3
  101. package/dist/{chunk-CHCA44C3.js → chunk-ODPLEWB6.js} +3 -3
  102. package/dist/chunk-ODPLEWB6.js.map +1 -0
  103. package/dist/{chunk-HENLZHIT.js → chunk-OIF36KGD.js} +7 -4
  104. package/dist/chunk-OIF36KGD.js.map +1 -0
  105. package/dist/{chunk-GUPISBV2.js → chunk-PP2JH3GP.js} +2 -2
  106. package/dist/{chunk-OXJBNGBK.js → chunk-PSUB67YB.js} +2 -2
  107. package/dist/{chunk-UWY7GIVS.js → chunk-PYIFUBRK.js} +45 -13
  108. package/dist/chunk-PYIFUBRK.js.map +1 -0
  109. package/dist/{chunk-KIB7SDIJ.js → chunk-Q6YIJGXJ.js} +2 -2
  110. package/dist/{chunk-PPPZY2EU.js → chunk-QEMCQFDW.js} +2 -2
  111. package/dist/{chunk-ZT3EGNLR.js → chunk-QPD426WT.js} +2 -2
  112. package/dist/{chunk-RLV3PQGH.js → chunk-QVO4YOB7.js} +6 -6
  113. package/dist/{chunk-GMAG2HS4.js → chunk-RG3LBSGH.js} +46 -9
  114. package/dist/chunk-RG3LBSGH.js.map +1 -0
  115. package/dist/{chunk-XSWKORGM.js → chunk-S53OYO3F.js} +3 -1
  116. package/dist/chunk-S53OYO3F.js.map +1 -0
  117. package/dist/{chunk-YCN4BVDK.js → chunk-SCPFRKIT.js} +4 -2
  118. package/dist/chunk-SCPFRKIT.js.map +1 -0
  119. package/dist/{chunk-HJNQQICM.js → chunk-T5XWMMU2.js} +107 -50
  120. package/dist/chunk-T5XWMMU2.js.map +1 -0
  121. package/dist/{chunk-NZPF2SYV.js → chunk-T7N6KQGS.js} +138 -5
  122. package/dist/chunk-T7N6KQGS.js.map +1 -0
  123. package/dist/{chunk-VJXSUAO7.js → chunk-TNOWU6RP.js} +13 -10
  124. package/dist/chunk-TNOWU6RP.js.map +1 -0
  125. package/dist/{chunk-PCI747N2.js → chunk-TZVQQTG4.js} +48 -19
  126. package/dist/chunk-TZVQQTG4.js.map +1 -0
  127. package/dist/{chunk-KQAFEZQX.js → chunk-VDX2J7OX.js} +2 -2
  128. package/dist/{chunk-IK7DCC5H.js → chunk-VMGLYN42.js} +2 -2
  129. package/dist/{chunk-5RPTH6AU.js → chunk-VPGUMLBA.js} +8 -7
  130. package/dist/chunk-VPGUMLBA.js.map +1 -0
  131. package/dist/{chunk-KM2A35EO.js → chunk-WB3LYXC5.js} +11 -7
  132. package/dist/chunk-WB3LYXC5.js.map +1 -0
  133. package/dist/{chunk-NSKYFGDL.js → chunk-X4QQB7O6.js} +2 -2
  134. package/dist/{chunk-HPWVAEET.js → chunk-X6IRLNOO.js} +3 -7
  135. package/dist/chunk-X6IRLNOO.js.map +1 -0
  136. package/dist/{chunk-46GJIW5M.js → chunk-XAZOWLW4.js} +5 -5
  137. package/dist/{chunk-46GJIW5M.js.map → chunk-XAZOWLW4.js.map} +1 -1
  138. package/dist/{chunk-XPSVGJYA.js → chunk-YRMKDTKF.js} +12 -9
  139. package/dist/chunk-YRMKDTKF.js.map +1 -0
  140. package/dist/{chunk-6ZZP4EJF.js → chunk-ZJR7VG5L.js} +3 -3
  141. package/dist/{chunk-6ZZP4EJF.js.map → chunk-ZJR7VG5L.js.map} +1 -1
  142. package/dist/{cli-OrfKXNU4.d.ts → cli-DWeu7eTY.d.ts} +6 -2
  143. package/dist/cli.d.ts +3 -3
  144. package/dist/cli.js +60 -59
  145. package/dist/compounding/engine.js +3 -3
  146. package/dist/compounding/preference-consolidator.js +39 -11
  147. package/dist/compounding/preference-consolidator.js.map +1 -1
  148. package/dist/config.js +1 -1
  149. package/dist/connectors/codex-materialize-runner.js +3 -3
  150. package/dist/connectors/index.js +3 -3
  151. package/dist/consolidation-provenance-check.js +1 -1
  152. package/dist/contradiction/index.js +4 -4
  153. package/dist/conversation-index/backend.js +2 -2
  154. package/dist/conversation-index/indexer.js +1 -1
  155. package/dist/cross-namespace-budget.js +1 -1
  156. package/dist/enrichment/index.js +1 -1
  157. package/dist/entity-retrieval.js +3 -3
  158. package/dist/evals.js +1 -1
  159. package/dist/explicit-capture.d.ts +1 -1
  160. package/dist/extraction-judge.js +8 -1
  161. package/dist/extraction.js +2 -2
  162. package/dist/fallback-llm.d.ts +23 -6
  163. package/dist/fallback-llm.js +5 -3
  164. package/dist/{first-start-migration-GYJWIH36.js → first-start-migration-FF7YFGRP.js} +6 -6
  165. package/dist/index.d.ts +3 -3
  166. package/dist/index.js +94 -93
  167. package/dist/index.js.map +1 -1
  168. package/dist/lcm/archive.js +2 -2
  169. package/dist/lcm/engine.js +5 -5
  170. package/dist/lcm/index.js +7 -7
  171. package/dist/lcm/summarizer.js +3 -3
  172. package/dist/maintenance/memory-governance-cron.d.ts +6 -4
  173. package/dist/maintenance/memory-governance-cron.js +1 -1
  174. package/dist/maintenance/memory-governance.js +3 -3
  175. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +3 -3
  176. package/dist/maintenance/rebuild-memory-projection.js +4 -4
  177. package/dist/mcp-memory-inspector-app.d.ts +2 -2
  178. package/dist/mcp-memory-inspector-app.js +1 -1
  179. package/dist/migrate/from-engram.js +1 -1
  180. package/dist/namespaces/migrate.js +16 -15
  181. package/dist/namespaces/search.js +12 -11
  182. package/dist/namespaces/storage.js +3 -3
  183. package/dist/network/webdav.d.ts +2 -0
  184. package/dist/network/webdav.js +1 -1
  185. package/dist/objective-state-writers.js +2 -2
  186. package/dist/operator-toolkit.d.ts +3 -1
  187. package/dist/operator-toolkit.js +21 -20
  188. package/dist/{orchestrator-DTRQG75J.d.ts → orchestrator-CqWOjfgl.d.ts} +46 -3
  189. package/dist/orchestrator.d.ts +1 -1
  190. package/dist/orchestrator.js +47 -44
  191. package/dist/patterns-cli.js +1 -1
  192. package/dist/qmd-recall-cache.d.ts +2 -0
  193. package/dist/qmd-recall-cache.js +1 -1
  194. package/dist/qmd.d.ts +37 -2
  195. package/dist/qmd.js +4 -1
  196. package/dist/recall-explain-renderer.js +3 -3
  197. package/dist/recall-planner-llm.d.ts +57 -0
  198. package/dist/recall-planner-llm.js +167 -0
  199. package/dist/recall-planner-llm.js.map +1 -0
  200. package/dist/recall-xray-cli.js +4 -4
  201. package/dist/recall-xray-renderer.js +3 -3
  202. package/dist/recall-xray.js +2 -2
  203. package/dist/resume-bundles.js +2 -2
  204. package/dist/retrieval-agents.js +2 -2
  205. package/dist/routing/store.js +1 -1
  206. package/dist/schemas.d.ts +22 -22
  207. package/dist/search/factory.js +11 -10
  208. package/dist/search/index.js +11 -10
  209. package/dist/search/lancedb-backend.d.ts +1 -1
  210. package/dist/search/lancedb-backend.js +3 -2
  211. package/dist/search/meilisearch-backend.d.ts +1 -1
  212. package/dist/search/meilisearch-backend.js +3 -2
  213. package/dist/search/noop-backend.d.ts +1 -1
  214. package/dist/search/noop-backend.js +1 -1
  215. package/dist/search/orama-backend.d.ts +1 -1
  216. package/dist/search/orama-backend.js +3 -2
  217. package/dist/search/port.d.ts +6 -1
  218. package/dist/search/port.js +7 -0
  219. package/dist/search/remote-backend.d.ts +1 -1
  220. package/dist/search/remote-backend.js +1 -1
  221. package/dist/semantic-consolidation.js +4 -4
  222. package/dist/semantic-rule-promotion.js +3 -3
  223. package/dist/semantic-rule-verifier.js +3 -3
  224. package/dist/session-observer-state.js +1 -1
  225. package/dist/storage.js +2 -2
  226. package/dist/summarizer.js +2 -2
  227. package/dist/temporal-index.js +1 -1
  228. package/dist/{tier-stats-SKML2OSF.js → tier-stats-3LYQ3VV5.js} +3 -3
  229. package/dist/transfer/backup.js +2 -2
  230. package/dist/transfer/capsule-export.js +2 -2
  231. package/dist/transfer/capsule-import.js +2 -2
  232. package/dist/transfer/export-sqlite.js +1 -1
  233. package/dist/transfer/types.d.ts +12 -12
  234. package/dist/types.d.ts +32 -0
  235. package/dist/types.js +1 -1
  236. package/dist/utility-learner.js +1 -1
  237. package/dist/utility-runtime.js +2 -2
  238. package/dist/verified-recall.js +3 -3
  239. package/dist/work/board.js +2 -2
  240. package/dist/work/storage.d.ts +2 -0
  241. package/dist/work/storage.js +1 -1
  242. package/package.json +1 -1
  243. package/src/access-http.ts +3 -0
  244. package/src/access-mcp.test.ts +51 -0
  245. package/src/access-mcp.ts +26 -5
  246. package/src/active-recall.test.ts +40 -0
  247. package/src/active-recall.ts +19 -2
  248. package/src/behavior-learner.ts +5 -3
  249. package/src/buffer-session.test.ts +58 -0
  250. package/src/buffer-surprise-trigger.test.ts +4 -18
  251. package/src/buffer.ts +39 -11
  252. package/src/calibration.ts +10 -4
  253. package/src/causal-consolidation.test.ts +47 -2
  254. package/src/causal-consolidation.ts +13 -9
  255. package/src/cli.ts +19 -4
  256. package/src/compounding/engine.ts +2 -0
  257. package/src/compounding/preference-consolidator.test.ts +292 -0
  258. package/src/compounding/preference-consolidator.ts +55 -19
  259. package/src/config.test.ts +213 -0
  260. package/src/config.ts +175 -4
  261. package/src/connectors/codex-materialize-runner.ts +7 -4
  262. package/src/consolidation-provenance-check.ts +24 -5
  263. package/src/conversation-index/indexer.test.ts +22 -0
  264. package/src/conversation-index/indexer.ts +7 -3
  265. package/src/cross-namespace-budget.test.ts +44 -21
  266. package/src/cross-namespace-budget.ts +2 -2
  267. package/src/enrichment/pipeline.ts +11 -16
  268. package/src/evals.ts +1 -1
  269. package/src/extraction-judge-chain.test.ts +55 -0
  270. package/src/extraction-judge.ts +7 -9
  271. package/src/extraction.ts +16 -5
  272. package/src/fallback-llm.test.ts +600 -1
  273. package/src/fallback-llm.ts +91 -22
  274. package/src/maintenance/memory-governance-cron.ts +39 -29
  275. package/src/mcp-memory-inspector-app.ts +54 -12
  276. package/src/message-parts/index.ts +6 -0
  277. package/src/message-parts/message-parts.test.ts +30 -0
  278. package/src/migrate/from-engram.ts +19 -5
  279. package/src/namespaces/search.test.ts +15 -2
  280. package/src/namespaces/search.ts +1 -1
  281. package/src/network/webdav.ts +61 -21
  282. package/src/operator-toolkit.ts +6 -2
  283. package/src/orchestrator.ts +173 -20
  284. package/src/qmd-client.test.ts +85 -0
  285. package/src/qmd-recall-cache.test.ts +16 -0
  286. package/src/qmd-recall-cache.ts +7 -0
  287. package/src/qmd.test.ts +54 -0
  288. package/src/qmd.ts +119 -19
  289. package/src/recall-planner-llm.test.ts +224 -0
  290. package/src/recall-planner-llm.ts +289 -0
  291. package/src/routing/store.ts +4 -8
  292. package/src/search/factory.ts +3 -0
  293. package/src/search/lancedb-backend.ts +15 -3
  294. package/src/search/meilisearch-backend.ts +70 -7
  295. package/src/search/noop-backend.ts +5 -1
  296. package/src/search/orama-backend.ts +15 -3
  297. package/src/search/port.ts +15 -0
  298. package/src/search/remote-backend.ts +5 -1
  299. package/src/session-observer-state.ts +1 -1
  300. package/src/summarizer.ts +3 -3
  301. package/src/temporal-index.test.ts +18 -0
  302. package/src/temporal-index.ts +45 -0
  303. package/src/training-export/cli-date-validation.test.ts +36 -0
  304. package/src/training-export/date-parse.ts +21 -2
  305. package/src/transfer/export-sqlite.ts +3 -0
  306. package/src/types.ts +35 -0
  307. package/src/utility-learner.ts +1 -0
  308. package/src/work/storage.ts +23 -0
  309. package/dist/chunk-5RPTH6AU.js.map +0 -1
  310. package/dist/chunk-AJA46VX5.js.map +0 -1
  311. package/dist/chunk-C4SQJZAF.js.map +0 -1
  312. package/dist/chunk-CHCA44C3.js.map +0 -1
  313. package/dist/chunk-CSKLPDN6.js.map +0 -1
  314. package/dist/chunk-DLJ4IR6M.js.map +0 -1
  315. package/dist/chunk-EAZGEEG2.js.map +0 -1
  316. package/dist/chunk-EVZFIAPG.js.map +0 -1
  317. package/dist/chunk-G3Z3QEF5.js.map +0 -1
  318. package/dist/chunk-GMAG2HS4.js.map +0 -1
  319. package/dist/chunk-HENLZHIT.js.map +0 -1
  320. package/dist/chunk-HINSGUA7.js.map +0 -1
  321. package/dist/chunk-HJNQQICM.js.map +0 -1
  322. package/dist/chunk-HPWVAEET.js.map +0 -1
  323. package/dist/chunk-IOTENEVL.js.map +0 -1
  324. package/dist/chunk-IP73YCZP.js.map +0 -1
  325. package/dist/chunk-JHMFYY7L.js.map +0 -1
  326. package/dist/chunk-JNANKJLN.js.map +0 -1
  327. package/dist/chunk-KGK2QKWL.js.map +0 -1
  328. package/dist/chunk-KM2A35EO.js.map +0 -1
  329. package/dist/chunk-KVEVLBKC.js.map +0 -1
  330. package/dist/chunk-L227SKTB.js.map +0 -1
  331. package/dist/chunk-LZ3VEOU5.js.map +0 -1
  332. package/dist/chunk-NOMEVTUD.js.map +0 -1
  333. package/dist/chunk-NZPF2SYV.js.map +0 -1
  334. package/dist/chunk-PCI747N2.js.map +0 -1
  335. package/dist/chunk-TH67Q46T.js.map +0 -1
  336. package/dist/chunk-UWY7GIVS.js.map +0 -1
  337. package/dist/chunk-VJXSUAO7.js.map +0 -1
  338. package/dist/chunk-XKIQZXUB.js.map +0 -1
  339. package/dist/chunk-XPSVGJYA.js.map +0 -1
  340. package/dist/chunk-XSWKORGM.js.map +0 -1
  341. package/dist/chunk-YCN4BVDK.js.map +0 -1
  342. package/dist/chunk-ZDTVJXIP.js.map +0 -1
  343. /package/dist/{capsule-crypto-7FJQINUR.js.map → capsule-crypto-YO5QJ6L3.js.map} +0 -0
  344. /package/dist/{chunk-AU7Q3LSC.js.map → chunk-2QSZNTDO.js.map} +0 -0
  345. /package/dist/{chunk-HSVJGWYS.js.map → chunk-2ROPI5OE.js.map} +0 -0
  346. /package/dist/{chunk-CF3ZF2YU.js.map → chunk-3QSU4NFF.js.map} +0 -0
  347. /package/dist/{chunk-OI27U2HT.js.map → chunk-5BTCT236.js.map} +0 -0
  348. /package/dist/{chunk-CO7ZO4TU.js.map → chunk-5VDJMYTF.js.map} +0 -0
  349. /package/dist/{chunk-YFS5OEKO.js.map → chunk-7MLB4NCL.js.map} +0 -0
  350. /package/dist/{chunk-2QANQKSQ.js.map → chunk-ADNZVFXG.js.map} +0 -0
  351. /package/dist/{chunk-557IAFPD.js.map → chunk-APRRL26Q.js.map} +0 -0
  352. /package/dist/{chunk-QDDHYAKV.js.map → chunk-AZDOWD2L.js.map} +0 -0
  353. /package/dist/{chunk-MLT75J5S.js.map → chunk-B6SU7YSE.js.map} +0 -0
  354. /package/dist/{chunk-FXKPZ3H6.js.map → chunk-BPSGLMQ4.js.map} +0 -0
  355. /package/dist/{chunk-2NLLXCJG.js.map → chunk-BXLOS5AJ.js.map} +0 -0
  356. /package/dist/{chunk-IK34DVAC.js.map → chunk-CIOMS6DI.js.map} +0 -0
  357. /package/dist/{chunk-7DZRO2DC.js.map → chunk-DEPRLVLK.js.map} +0 -0
  358. /package/dist/{chunk-DHGSZ3UD.js.map → chunk-DGNQRNLL.js.map} +0 -0
  359. /package/dist/{chunk-X7Y7WX73.js.map → chunk-DQEMWVMT.js.map} +0 -0
  360. /package/dist/{chunk-ETUPBUHB.js.map → chunk-GDASG7NC.js.map} +0 -0
  361. /package/dist/{chunk-4HP7HIE3.js.map → chunk-HP5FMB6L.js.map} +0 -0
  362. /package/dist/{chunk-DOX2CG6Y.js.map → chunk-IEUU7O4F.js.map} +0 -0
  363. /package/dist/{chunk-WSGF57U2.js.map → chunk-JQDZQ4TB.js.map} +0 -0
  364. /package/dist/{chunk-W7L6HXUC.js.map → chunk-LXOM6IQU.js.map} +0 -0
  365. /package/dist/{chunk-6JGNHWCI.js.map → chunk-OBIRVF36.js.map} +0 -0
  366. /package/dist/{chunk-GUPISBV2.js.map → chunk-PP2JH3GP.js.map} +0 -0
  367. /package/dist/{chunk-OXJBNGBK.js.map → chunk-PSUB67YB.js.map} +0 -0
  368. /package/dist/{chunk-KIB7SDIJ.js.map → chunk-Q6YIJGXJ.js.map} +0 -0
  369. /package/dist/{chunk-PPPZY2EU.js.map → chunk-QEMCQFDW.js.map} +0 -0
  370. /package/dist/{chunk-ZT3EGNLR.js.map → chunk-QPD426WT.js.map} +0 -0
  371. /package/dist/{chunk-RLV3PQGH.js.map → chunk-QVO4YOB7.js.map} +0 -0
  372. /package/dist/{chunk-KQAFEZQX.js.map → chunk-VDX2J7OX.js.map} +0 -0
  373. /package/dist/{chunk-IK7DCC5H.js.map → chunk-VMGLYN42.js.map} +0 -0
  374. /package/dist/{chunk-NSKYFGDL.js.map → chunk-X4QQB7O6.js.map} +0 -0
  375. /package/dist/{first-start-migration-GYJWIH36.js.map → first-start-migration-FF7YFGRP.js.map} +0 -0
  376. /package/dist/{tier-stats-SKML2OSF.js.map → tier-stats-3LYQ3VV5.js.map} +0 -0
@@ -0,0 +1,289 @@
1
+ import { z } from "zod";
2
+
3
+ import type { PluginConfig, RecallPlanMode } from "./types.js";
4
+ import { planRecallMode } from "./intent.js";
5
+ import {
6
+ FallbackLlmClient,
7
+ fallbackLlmRuntimeContextFromConfig,
8
+ gatewayTaskChainOptions,
9
+ type FallbackLlmOptions,
10
+ } from "./fallback-llm.js";
11
+ import { log } from "./logger.js";
12
+
13
+ /**
14
+ * LLM-based recall planning (issue #1367, Option C).
15
+ *
16
+ * Classifies an incoming prompt into a {@link RecallPlanMode} using an LLM
17
+ * instead of (or alongside) the regex heuristic in {@link planRecallMode}.
18
+ *
19
+ * Provider-agnostic by construction: it routes through {@link FallbackLlmClient},
20
+ * which resolves the model chain from gateway providers (OpenAI, Anthropic,
21
+ * Ollama, Codex, …) or gateway agent personas / `taskModelChain`. Nothing here
22
+ * is hard-coded to a single provider or to OpenAI's Responses API — the API
23
+ * dialect is chosen per-provider by the client based on each provider's `api`
24
+ * field. The configured `recallPlannerModel` is tried first, with the broader
25
+ * task chain (and the gateway default) as resilient fallbacks.
26
+ *
27
+ * Invariants:
28
+ * - Never throws to the caller (gotcha #13). Any LLM failure, timeout, empty
29
+ * response, or unavailable backend falls back to the heuristic result and
30
+ * sets `fallbackUsed: true` (gotcha #34 — failures are distinct from a valid
31
+ * classification).
32
+ * - When `recallPlannerLlmEnabled` is false the LLM is never contacted.
33
+ */
34
+
35
+ export type RecallPlannerSource = "llm" | "heuristic" | "heuristic-fallback";
36
+
37
+ export interface RecallPlannerLlmResult {
38
+ /** The mode to act on. */
39
+ mode: RecallPlanMode;
40
+ /** The heuristic mode, always computed (the fallback floor / shadow baseline). */
41
+ heuristicMode: RecallPlanMode;
42
+ /** Where `mode` came from. */
43
+ source: RecallPlannerSource;
44
+ /** Short human-readable rationale (LLM reason, or why we fell back). */
45
+ reason: string;
46
+ /** Model that actually served the classification, when an LLM was used. */
47
+ modelUsed?: string;
48
+ /** Wall-clock spent in the LLM call (0 when no call was made). */
49
+ latencyMs: number;
50
+ /** True when the LLM was enabled but we had to fall back to the heuristic. */
51
+ fallbackUsed: boolean;
52
+ }
53
+
54
+ const PLANNER_SCHEMA = z.object({
55
+ // gotcha #2: optional fields use .optional().nullable()
56
+ mode: z.enum(["no_recall", "minimal", "full", "graph_mode"]),
57
+ reason: z.string().max(280).optional().nullable(),
58
+ });
59
+
60
+ const SYSTEM_PROMPT = [
61
+ "You are a recall-planning classifier for a long-term memory system.",
62
+ "Given the user's latest message, decide how much stored memory should be retrieved before the assistant responds.",
63
+ "Reply with a single JSON object: {\"mode\": <one of no_recall|minimal|full|graph_mode>, \"reason\": <short string>}.",
64
+ "",
65
+ "Modes:",
66
+ '- "no_recall": low-information acknowledgements or chit-chat with nothing to look up (e.g. "ok", "thanks", "sounds good"). Retrieve nothing.',
67
+ '- "minimal": short, self-contained operational directives that rarely need history (e.g. "restart the service", "run the tests", "show status"). Retrieve a little.',
68
+ '- "full": anything memory-seeking, analytical, or a question that benefits from prior context, decisions, or facts. This is the safe default when unsure.',
69
+ '- "graph_mode": queries about timelines, sequences, history, causal chains, or root cause ("how did we get here", "what led to this regression"). Retrieve relationship/graph context.',
70
+ "",
71
+ "When uncertain, prefer \"full\" over dropping recall. Never invent facts; only classify intent.",
72
+ ].join("\n");
73
+
74
+ /** Clamp a planner prompt to the configured character budget. */
75
+ function clampPrompt(prompt: string, maxChars: number): string {
76
+ const safeMax = Number.isFinite(maxChars) && maxChars > 0 ? Math.floor(maxChars) : 4000;
77
+ if (prompt.length <= safeMax) return prompt;
78
+ return prompt.slice(0, safeMax);
79
+ }
80
+
81
+ /** Trim and cap the optional memory hints used to ground the classification. */
82
+ function clampHints(hints: string[] | undefined, maxHints: number): string[] {
83
+ if (!Array.isArray(hints) || hints.length === 0) return [];
84
+ const safeMax = Number.isFinite(maxHints) && maxHints > 0 ? Math.floor(maxHints) : 0;
85
+ if (safeMax <= 0) return [];
86
+ const cleaned: string[] = [];
87
+ for (const hint of hints) {
88
+ if (typeof hint !== "string") continue;
89
+ const trimmed = hint.trim();
90
+ if (trimmed.length === 0) continue;
91
+ cleaned.push(trimmed);
92
+ if (cleaned.length >= safeMax) break;
93
+ }
94
+ return cleaned;
95
+ }
96
+
97
+ function buildMessages(
98
+ prompt: string,
99
+ hints: string[],
100
+ config: PluginConfig,
101
+ ): Array<{ role: "system" | "user" | "assistant"; content: string }> {
102
+ const clampedPrompt = clampPrompt(prompt, config.recallPlannerMaxPromptChars);
103
+ const userParts = [`User message:\n${clampedPrompt}`];
104
+ if (hints.length > 0) {
105
+ userParts.push(
106
+ `\nRecent memory topics (for grounding only, do not treat as the message):\n- ${hints.join("\n- ")}`,
107
+ );
108
+ }
109
+ userParts.push('\nRespond with JSON only: {"mode": "...", "reason": "..."}.');
110
+ return [
111
+ { role: "system", content: SYSTEM_PROMPT },
112
+ { role: "user", content: userParts.join("\n") },
113
+ ];
114
+ }
115
+
116
+ /**
117
+ * Resolve the FallbackLlmClient routing options for the recall planner.
118
+ *
119
+ * - The dedicated `recallPlannerModel` is tried first (as the `model`
120
+ * override — it is prepended to the chain by FallbackLlmClient). If it does
121
+ * not resolve to a configured provider it is silently skipped, so a stale
122
+ * default never breaks routing.
123
+ * - In gateway mode the shared `gatewayTaskChainOptions` (taskModelChain >
124
+ * gatewayAgentId, gotcha #22) is layered in as the fallback chain, plus the
125
+ * implicit gateway default appended by the client.
126
+ * - In plugin mode only the explicit model + gateway providers apply.
127
+ */
128
+ /**
129
+ * A `recallPlannerModel` value is only usable as a FallbackLlmClient `model`
130
+ * override when it is provider-qualified (`provider/model`). The client's
131
+ * `parseModelString` rejects bare names, so forwarding a bare value (e.g. the
132
+ * legacy default `"gpt-5.5"`) would log "invalid model format" on every call
133
+ * and never resolve. Bare values are dropped so routing falls through to the
134
+ * gateway chain / agent / default instead (issue #1367 review on PR #1428).
135
+ */
136
+ function qualifiedPlannerModel(recallPlannerModel: string | undefined): string | undefined {
137
+ if (typeof recallPlannerModel !== "string") return undefined;
138
+ const trimmed = recallPlannerModel.trim();
139
+ return trimmed.includes("/") ? trimmed : undefined;
140
+ }
141
+
142
+ export function resolveRecallPlannerLlmOptions(
143
+ config: Pick<
144
+ PluginConfig,
145
+ "modelSource" | "taskModelChain" | "gatewayAgentId" | "recallPlannerModel" | "recallPlannerTimeoutMs"
146
+ >,
147
+ ): FallbackLlmOptions {
148
+ const chainOptions =
149
+ config.modelSource === "gateway" ? gatewayTaskChainOptions(config) : {};
150
+ return {
151
+ ...chainOptions,
152
+ model: qualifiedPlannerModel(config.recallPlannerModel),
153
+ temperature: 0,
154
+ maxTokens: 64,
155
+ timeoutMs:
156
+ typeof config.recallPlannerTimeoutMs === "number" && config.recallPlannerTimeoutMs > 0
157
+ ? config.recallPlannerTimeoutMs
158
+ : 1500,
159
+ };
160
+ }
161
+
162
+ // One-time warning per distinct routing signature so an opted-in operator with
163
+ // no usable model learns why planning silently uses the heuristic, without
164
+ // spamming a line on every recall.
165
+ const warnedNoRoutingSignatures = new Set<string>();
166
+
167
+ function heuristicResult(
168
+ heuristicMode: RecallPlanMode,
169
+ source: RecallPlannerSource,
170
+ reason: string,
171
+ latencyMs: number,
172
+ fallbackUsed: boolean,
173
+ ): RecallPlannerLlmResult {
174
+ return { mode: heuristicMode, heuristicMode, source, reason, latencyMs, fallbackUsed };
175
+ }
176
+
177
+ /**
178
+ * Plan the recall mode for `prompt`, optionally consulting an LLM.
179
+ *
180
+ * Always safe to call: returns the heuristic result when the LLM is disabled,
181
+ * unavailable, or fails.
182
+ *
183
+ * @param llm injectable client (tests pass a stub); constructed from gateway
184
+ * config when omitted.
185
+ */
186
+ export async function planRecallModeLLM(
187
+ prompt: string,
188
+ hints: string[] | undefined,
189
+ config: PluginConfig,
190
+ llm?: FallbackLlmClient,
191
+ signal?: AbortSignal,
192
+ ): Promise<RecallPlannerLlmResult> {
193
+ const heuristicMode = planRecallMode(prompt);
194
+
195
+ if (!config.recallPlannerLlmEnabled) {
196
+ return heuristicResult(heuristicMode, "heuristic", "llm-disabled", 0, false);
197
+ }
198
+
199
+ // Participate in the recall cancellation contract: if the outer recall is
200
+ // already aborted (outer timeout / reset / session abort), don't start an LLM
201
+ // round-trip — fall back to the heuristic immediately (#1428 review).
202
+ if (signal?.aborted) {
203
+ return heuristicResult(heuristicMode, "heuristic-fallback", "aborted", 0, true);
204
+ }
205
+
206
+ const safePrompt = typeof prompt === "string" ? prompt.trim() : "";
207
+ if (safePrompt.length === 0) {
208
+ // Empty prompts never need an LLM round-trip.
209
+ return heuristicResult(heuristicMode, "heuristic", "empty-prompt", 0, false);
210
+ }
211
+
212
+ const client =
213
+ llm ??
214
+ new FallbackLlmClient(
215
+ config.gatewayConfig,
216
+ fallbackLlmRuntimeContextFromConfig(config),
217
+ );
218
+
219
+ // Forward the recall abort signal so an aborted/timed-out outer recall can
220
+ // cancel an in-flight planner call (FallbackLlmClient honors `signal`).
221
+ const options = { ...resolveRecallPlannerLlmOptions(config), signal };
222
+
223
+ // Availability check uses the same routing options so plugin-mode / empty
224
+ // chains short-circuit to the heuristic without a network attempt. `model`
225
+ // here is already provider-qualified (bare names were dropped), so a present
226
+ // model means the override is genuinely routable.
227
+ const availabilityProbe = {
228
+ agentId: options.agentId,
229
+ modelChain: options.modelChain,
230
+ };
231
+ if (!client.isAvailable(availabilityProbe) && !options.model) {
232
+ // Opted-in but nothing routable resolves (e.g. plugin mode with the bare
233
+ // default `recallPlannerModel` and no gateway chain). Warn once so it's not
234
+ // a silent no-op, then fall back to the heuristic.
235
+ const signature = `${config.modelSource}:${config.recallPlannerModel ?? ""}`;
236
+ if (!warnedNoRoutingSignatures.has(signature)) {
237
+ warnedNoRoutingSignatures.add(signature);
238
+ log.warn(
239
+ "[recall-planner] recallPlannerLlmEnabled is on but no routable model resolves — " +
240
+ "set recallPlannerModel to a 'provider/model' value or configure a gateway model chain. " +
241
+ "Falling back to the heuristic planner.",
242
+ );
243
+ }
244
+ return heuristicResult(heuristicMode, "heuristic-fallback", "llm-no-model", 0, true);
245
+ }
246
+
247
+ const clampedHints = clampHints(hints, config.recallPlannerMaxMemoryHints);
248
+ const messages = buildMessages(safePrompt, clampedHints, config);
249
+
250
+ const start = Date.now();
251
+ try {
252
+ const detailed = await client.parseWithSchemaDetailed(messages, PLANNER_SCHEMA, options);
253
+ const latencyMs = Date.now() - start;
254
+ if (!detailed?.result) {
255
+ // Distinguish failure from a valid empty (gotcha #34): a null here means
256
+ // no parseable classification, so fall back to the heuristic.
257
+ return heuristicResult(heuristicMode, "heuristic-fallback", "llm-empty", latencyMs, true);
258
+ }
259
+ const mode = detailed.result.mode;
260
+ const reason =
261
+ typeof detailed.result.reason === "string" && detailed.result.reason.trim().length > 0
262
+ ? detailed.result.reason.trim()
263
+ : "llm-classified";
264
+ return {
265
+ mode,
266
+ heuristicMode,
267
+ source: "llm",
268
+ reason,
269
+ modelUsed: detailed.modelUsed,
270
+ latencyMs,
271
+ fallbackUsed: false,
272
+ };
273
+ } catch (err) {
274
+ const latencyMs = Date.now() - start;
275
+ if (signal?.aborted) {
276
+ // Cancelled by the outer recall — expected, not an error worth warning on.
277
+ return heuristicResult(heuristicMode, "heuristic-fallback", "aborted", latencyMs, true);
278
+ }
279
+ const message = err instanceof Error ? err.message : String(err);
280
+ log.warn(`[recall-planner] LLM failed, falling back to heuristic: ${message}`);
281
+ return heuristicResult(
282
+ heuristicMode,
283
+ "heuristic-fallback",
284
+ `llm-error:${message}`,
285
+ latencyMs,
286
+ true,
287
+ );
288
+ }
289
+ }
@@ -85,14 +85,10 @@ export class RoutingRulesStore {
85
85
  }
86
86
 
87
87
  async read(options?: RoutingEngineOptions): Promise<RouteRule[]> {
88
- try {
89
- const persisted = await this.readPersistedRules();
90
- return persisted
91
- .map((rule) => normalizeRule(rule, options))
92
- .filter((rule): rule is RouteRule => rule !== null);
93
- } catch {
94
- return [];
95
- }
88
+ const persisted = await this.readPersistedRules();
89
+ return persisted
90
+ .map((rule) => normalizeRule(rule, options))
91
+ .filter((rule): rule is RouteRule => rule !== null);
96
92
  }
97
93
 
98
94
  async write(rules: RouteRule[], options?: RoutingEngineOptions): Promise<RouteRule[]> {
@@ -105,6 +105,9 @@ function qmdOptions(config: PluginConfig): QmdClientOptions {
105
105
  qmdEmbedModel: config.qmdEmbedModel,
106
106
  qmdRerankModel: config.qmdRerankModel,
107
107
  qmdGenerateModel: config.qmdGenerateModel,
108
+ qmdSearchStrategy: config.qmdSearchStrategy,
109
+ qmdSubprocessStrategy: config.qmdSubprocessStrategy,
110
+ qmdDaemonTimeoutMs: config.qmdDaemonTimeoutMs,
108
111
  };
109
112
  }
110
113
 
@@ -1,5 +1,11 @@
1
1
  import { log } from "../logger.js";
2
- import type { SearchBackend, SearchExecutionOptions, SearchQueryOptions, SearchResult } from "./port.js";
2
+ import {
3
+ resolveEnsureCollectionArgs,
4
+ type SearchBackend,
5
+ type SearchExecutionOptions,
6
+ type SearchQueryOptions,
7
+ type SearchResult,
8
+ } from "./port.js";
3
9
  import type { EmbedHelper, EmbedProviderIdentity, EmbedWithProviderResult } from "./embed-helper.js";
4
10
  import { scanMemoryDir } from "./document-scanner.js";
5
11
  import { isSearchAborted, throwIfSearchAborted } from "./abort.js";
@@ -305,10 +311,16 @@ export class LanceDbBackend implements SearchBackend {
305
311
 
306
312
  async ensureCollection(
307
313
  _memoryDir: string,
308
- _execution?: SearchExecutionOptions,
314
+ collectionOrExecution?: string | SearchExecutionOptions,
315
+ execution?: SearchExecutionOptions,
309
316
  ): Promise<"present" | "missing" | "unknown" | "skipped"> {
317
+ const { collection, execution: effectiveExecution } = resolveEnsureCollectionArgs(
318
+ collectionOrExecution,
319
+ execution,
320
+ );
321
+ if (isSearchAborted(effectiveExecution)) return "skipped";
310
322
  try {
311
- await this.ensureTable();
323
+ await this.ensureTableForCollection(collection ?? this.collection);
312
324
  return "present";
313
325
  } catch {
314
326
  return "missing";
@@ -1,5 +1,11 @@
1
1
  import { log } from "../logger.js";
2
- import type { SearchBackend, SearchExecutionOptions, SearchQueryOptions, SearchResult } from "./port.js";
2
+ import {
3
+ resolveEnsureCollectionArgs,
4
+ type SearchBackend,
5
+ type SearchExecutionOptions,
6
+ type SearchQueryOptions,
7
+ type SearchResult,
8
+ } from "./port.js";
3
9
  import { scanMemoryDir } from "./document-scanner.js";
4
10
  import { isSearchAborted, throwIfSearchAborted } from "./abort.js";
5
11
 
@@ -134,6 +140,8 @@ export class MeilisearchBackend implements SearchBackend {
134
140
  if (isSearchAborted(execution)) return;
135
141
 
136
142
  try {
143
+ const ensured = await this.ensureCollection(memoryDir, collection, execution);
144
+ if (ensured === "skipped" || ensured === "missing") return;
137
145
  const client = await this.ensureClient();
138
146
  if (isSearchAborted(execution)) return;
139
147
  const docs = await scanMemoryDir(memoryDir);
@@ -196,21 +204,47 @@ export class MeilisearchBackend implements SearchBackend {
196
204
 
197
205
  async ensureCollection(
198
206
  _memoryDir: string,
199
- _execution?: SearchExecutionOptions,
207
+ collectionOrExecution?: string | SearchExecutionOptions,
208
+ execution?: SearchExecutionOptions,
200
209
  ): Promise<"present" | "missing" | "unknown" | "skipped"> {
210
+ const { collection, execution: effectiveExecution } = resolveEnsureCollectionArgs(
211
+ collectionOrExecution,
212
+ execution,
213
+ );
201
214
  if (!this.available) return "skipped";
215
+ if (isSearchAborted(effectiveExecution)) return "skipped";
216
+ const targetCollection = collection ?? this.collection;
202
217
  try {
203
218
  const client = await this.ensureClient();
219
+ if (isSearchAborted(effectiveExecution)) return "skipped";
204
220
  try {
205
- await client.getIndex(this.collection);
221
+ await client.getIndex(targetCollection);
206
222
  return "present";
207
- } catch {
223
+ } catch (err) {
224
+ if (!isMeilisearchIndexNotFoundError(err)) {
225
+ log.debug(
226
+ `MeilisearchBackend collection check unavailable for "${targetCollection}" (will not skip update): ${
227
+ err instanceof Error ? err.message : String(err)
228
+ }`,
229
+ );
230
+ return "unknown";
231
+ }
208
232
  // Index doesn't exist — create it
209
- await client.createIndex(this.collection, { primaryKey: "id" });
233
+ if (isSearchAborted(effectiveExecution)) return "skipped";
234
+ const createTask = await client.createIndex(targetCollection, { primaryKey: "id" });
235
+ if (isSearchAborted(effectiveExecution)) return "skipped";
236
+ if (createTask?.taskUid !== undefined && createTask?.taskUid !== null) {
237
+ await client.waitForTask(createTask.taskUid, { timeOutMs: this.timeoutMs });
238
+ }
210
239
  return "present";
211
240
  }
212
- } catch {
213
- return "skipped";
241
+ } catch (err) {
242
+ log.debug(
243
+ `MeilisearchBackend collection check failed for "${targetCollection}" (will not disable updates): ${
244
+ err instanceof Error ? err.message : String(err)
245
+ }`,
246
+ );
247
+ return "unknown";
214
248
  }
215
249
  }
216
250
 
@@ -261,3 +295,32 @@ export class MeilisearchBackend implements SearchBackend {
261
295
  }));
262
296
  }
263
297
  }
298
+
299
+ function isMeilisearchIndexNotFoundError(err: unknown): boolean {
300
+ if (!err || typeof err !== "object") {
301
+ return false;
302
+ }
303
+ const record = err as Record<string, unknown>;
304
+ const code = typeof record.code === "string" ? record.code.toLowerCase() : "";
305
+ const type = typeof record.type === "string" ? record.type.toLowerCase() : "";
306
+ const status =
307
+ typeof record.status === "number"
308
+ ? record.status
309
+ : typeof record.statusCode === "number"
310
+ ? record.statusCode
311
+ : undefined;
312
+ const message = err instanceof Error
313
+ ? err.message.toLowerCase()
314
+ : typeof record.message === "string"
315
+ ? record.message.toLowerCase()
316
+ : "";
317
+
318
+ return (
319
+ code === "index_not_found"
320
+ || type === "index_not_found"
321
+ || status === 404
322
+ || message.includes("index_not_found")
323
+ || /index .*not found/.test(message)
324
+ || /index .*does not exist/.test(message)
325
+ );
326
+ }
@@ -51,7 +51,11 @@ export class NoopSearchBackend implements SearchBackend {
51
51
  async embed(): Promise<void> {}
52
52
  async embedCollection(_collection: string): Promise<void> {}
53
53
 
54
- async ensureCollection(_memoryDir: string, _execution?: SearchExecutionOptions): Promise<"skipped"> {
54
+ async ensureCollection(
55
+ _memoryDir: string,
56
+ _collectionOrExecution?: string | SearchExecutionOptions,
57
+ _execution?: SearchExecutionOptions,
58
+ ): Promise<"skipped"> {
55
59
  return "skipped";
56
60
  }
57
61
  }
@@ -1,7 +1,13 @@
1
1
  import path from "node:path";
2
2
  import { mkdir, readdir, readFile, rename, rm, writeFile } from "node:fs/promises";
3
3
  import { log } from "../logger.js";
4
- import type { SearchBackend, SearchExecutionOptions, SearchQueryOptions, SearchResult } from "./port.js";
4
+ import {
5
+ resolveEnsureCollectionArgs,
6
+ type SearchBackend,
7
+ type SearchExecutionOptions,
8
+ type SearchQueryOptions,
9
+ type SearchResult,
10
+ } from "./port.js";
5
11
  import type { EmbedHelper, EmbedProviderIdentity, EmbedWithProviderResult } from "./embed-helper.js";
6
12
  import { scanMemoryDir } from "./document-scanner.js";
7
13
  import { isSearchAborted, throwIfSearchAborted } from "./abort.js";
@@ -357,11 +363,17 @@ export class OramaBackend implements SearchBackend {
357
363
 
358
364
  async ensureCollection(
359
365
  _memoryDir: string,
360
- _execution?: SearchExecutionOptions,
366
+ collectionOrExecution?: string | SearchExecutionOptions,
367
+ execution?: SearchExecutionOptions,
361
368
  ): Promise<"present" | "missing" | "unknown" | "skipped"> {
369
+ const { collection, execution: effectiveExecution } = resolveEnsureCollectionArgs(
370
+ collectionOrExecution,
371
+ execution,
372
+ );
373
+ if (isSearchAborted(effectiveExecution)) return "skipped";
362
374
  try {
363
375
  await this.ensureModules();
364
- await this.ensureDb();
376
+ await this.ensureDbForCollection(collection ?? this.collection);
365
377
  return "present";
366
378
  } catch {
367
379
  return "missing";
@@ -16,6 +16,16 @@ export interface SearchExecutionOptions {
16
16
  signal?: AbortSignal;
17
17
  }
18
18
 
19
+ export function resolveEnsureCollectionArgs(
20
+ collectionOrExecution?: string | SearchExecutionOptions,
21
+ execution?: SearchExecutionOptions,
22
+ ): { collection?: string; execution?: SearchExecutionOptions } {
23
+ if (typeof collectionOrExecution === "string") {
24
+ return { collection: collectionOrExecution, execution };
25
+ }
26
+ return { collection: undefined, execution: collectionOrExecution ?? execution };
27
+ }
28
+
19
29
  /**
20
30
  * Abstract search backend interface.
21
31
  *
@@ -87,4 +97,9 @@ export interface SearchBackend {
87
97
  memoryDir: string,
88
98
  execution?: SearchExecutionOptions,
89
99
  ): Promise<"present" | "missing" | "unknown" | "skipped">;
100
+ ensureCollection(
101
+ memoryDir: string,
102
+ collection?: string,
103
+ execution?: SearchExecutionOptions,
104
+ ): Promise<"present" | "missing" | "unknown" | "skipped">;
90
105
  }
@@ -82,7 +82,11 @@ export class RemoteSearchBackend implements SearchBackend {
82
82
  async embed(): Promise<void> {}
83
83
  async embedCollection(_collection: string): Promise<void> {}
84
84
 
85
- async ensureCollection(_memoryDir: string, _execution?: SearchExecutionOptions): Promise<"skipped"> {
85
+ async ensureCollection(
86
+ _memoryDir: string,
87
+ _collectionOrExecution?: string | SearchExecutionOptions,
88
+ _execution?: SearchExecutionOptions,
89
+ ): Promise<"skipped"> {
86
90
  return "skipped";
87
91
  }
88
92
 
@@ -242,7 +242,7 @@ export class SessionObserverState {
242
242
  }
243
243
 
244
244
  private async writeSessions(sessionsMap: Map<string, SessionObserverCursor>): Promise<void> {
245
- const sessions: Record<string, SessionObserverCursor> = {};
245
+ const sessions = Object.create(null) as Record<string, SessionObserverCursor>;
246
246
  for (const [key, value] of sessionsMap.entries()) {
247
247
  sessions[key] = value;
248
248
  }
package/src/summarizer.ts CHANGED
@@ -3,7 +3,7 @@ import path from "node:path";
3
3
  import { z } from "zod";
4
4
  import { log } from "./logger.js";
5
5
  import { LocalLlmClient } from "./local-llm.js";
6
- import { FallbackLlmClient, fallbackLlmRuntimeContextFromConfig } from "./fallback-llm.js";
6
+ import { FallbackLlmClient, fallbackLlmRuntimeContextFromConfig, gatewayTaskChainOptions } from "./fallback-llm.js";
7
7
  import { ModelRegistry } from "./model-registry.js";
8
8
  import { extractJsonCandidates } from "./json-extract.js";
9
9
  import type { HourlySummary, TranscriptEntry, PluginConfig, GatewayConfig } from "./types.js";
@@ -85,8 +85,8 @@ export class HourlySummarizer {
85
85
 
86
86
  private withGatewayAgent(options: import("./fallback-llm.js").FallbackLlmOptions): import("./fallback-llm.js").FallbackLlmOptions {
87
87
  if (!this.useGatewayModelSource) return options;
88
- const agentId = this.config.gatewayAgentId || undefined;
89
- return agentId ? { ...options, agentId } : options;
88
+ // Shared resolution (taskModelChain > gatewayAgentId) gotcha #22. Issue #1365.
89
+ return { ...options, ...gatewayTaskChainOptions(this.config) };
90
90
  }
91
91
 
92
92
  async initialize(): Promise<void> {
@@ -218,3 +218,21 @@ test("tag queries distinguish missing index from valid no-match results", async
218
218
  assert.deepEqual(noFilterPrefilter.expandedTags, []);
219
219
  assert.equal(noFilterPrefilter.paths, null);
220
220
  });
221
+
222
+ test("indexMemory replaces stale date and tag memberships for an existing path", async () => {
223
+ const memoryDir = await mkdtemp(join(tmpdir(), "remnic-temporal-index-update-"));
224
+ const memoryPath = "/tmp/remnic-temporal-updated-memory.md";
225
+
226
+ indexMemory(memoryDir, memoryPath, "2026-01-01T00:00:00.000Z", ["alpha"]);
227
+ indexMemory(memoryDir, memoryPath, "2026-02-01T00:00:00.000Z", ["beta"]);
228
+
229
+ const januaryMatches = await queryByDateRangeAsync(memoryDir, "2026-01-01", "2026-01-02");
230
+ const februaryMatches = await queryByDateRangeAsync(memoryDir, "2026-02-01", "2026-02-02");
231
+ const alphaMatches = await queryByTagsAsync(memoryDir, ["alpha"]);
232
+ const betaMatches = await queryByTagsAsync(memoryDir, ["beta"]);
233
+
234
+ assert.deepEqual(januaryMatches, new Set());
235
+ assert.deepEqual(februaryMatches, new Set([memoryPath]));
236
+ assert.deepEqual(alphaMatches, new Set());
237
+ assert.deepEqual(betaMatches, new Set([memoryPath]));
238
+ });