@remnic/core 9.3.771 → 9.4.1

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 (491) hide show
  1. package/dist/access-admin-ops-surface.d.ts +5 -5
  2. package/dist/access-admin-ops-surface.js +36 -35
  3. package/dist/access-boundary.d.ts +5 -5
  4. package/dist/access-boundary.js +37 -36
  5. package/dist/access-cli.js +109 -108
  6. package/dist/access-cli.js.map +1 -1
  7. package/dist/access-http.d.ts +5 -5
  8. package/dist/access-http.js +48 -47
  9. package/dist/access-identity-continuity-surface.d.ts +4 -4
  10. package/dist/access-identity-continuity-surface.js +36 -35
  11. package/dist/access-lcm-surface.d.ts +5 -5
  12. package/dist/access-lcm-surface.js +36 -35
  13. package/dist/access-mcp.d.ts +5 -5
  14. package/dist/access-mcp.js +44 -43
  15. package/dist/access-observe-write-surface.d.ts +5 -5
  16. package/dist/access-observe-write-surface.js +36 -35
  17. package/dist/access-operations-batch.js +41 -40
  18. package/dist/access-operations.d.ts +5 -5
  19. package/dist/access-operations.js +43 -42
  20. package/dist/access-recall-surface.d.ts +5 -5
  21. package/dist/access-recall-surface.js +36 -35
  22. package/dist/access-schema.js +3 -3
  23. package/dist/{access-service-CdW6B2qQ.d.ts → access-service-m-2VO_Ig.d.ts} +3 -3
  24. package/dist/access-service.d.ts +5 -5
  25. package/dist/access-service.js +36 -35
  26. package/dist/access-surface-catalog.d.ts +5 -5
  27. package/dist/action-confidence.d.ts +1 -1
  28. package/dist/active-memory-bridge.d.ts +1 -1
  29. package/dist/active-recall.d.ts +1 -1
  30. package/dist/active-recall.js +2 -2
  31. package/dist/adapters/index.js +4 -4
  32. package/dist/adapters/registry.js +2 -2
  33. package/dist/{auto-sync-2SOUW4JW.js → auto-sync-HMHQHUAG.js} +9 -9
  34. package/dist/behavior-learner.d.ts +1 -1
  35. package/dist/behavior-signals.d.ts +1 -1
  36. package/dist/bootstrap.d.ts +4 -4
  37. package/dist/bootstrap.js +2 -2
  38. package/dist/boxes.js +2 -2
  39. package/dist/briefing.d.ts +1 -1
  40. package/dist/briefing.js +12 -12
  41. package/dist/buffer-surprise-report.d.ts +1 -1
  42. package/dist/buffer.d.ts +1 -1
  43. package/dist/buffer.js +2 -2
  44. package/dist/calibration.d.ts +1 -1
  45. package/dist/calibration.js +4 -4
  46. package/dist/capabilities.d.ts +1 -1
  47. package/dist/{catalog-bflYNWsM.d.ts → catalog-BxWokdC2.d.ts} +1 -1
  48. package/dist/causal-behavior.d.ts +1 -1
  49. package/dist/causal-behavior.js +4 -4
  50. package/dist/causal-chain.js +4 -4
  51. package/dist/causal-consolidation.d.ts +1 -1
  52. package/dist/causal-consolidation.js +17 -17
  53. package/dist/causal-retrieval.js +4 -4
  54. package/dist/causal-trajectory-graph.d.ts +1 -1
  55. package/dist/causal-trajectory-graph.js +1 -1
  56. package/dist/causal-trajectory.js +1 -1
  57. package/dist/{chunk-PSNMICC2.js → chunk-27FLVMDC.js} +14 -14
  58. package/dist/{chunk-7FL4CNPV.js → chunk-2A3OXAP2.js} +2 -2
  59. package/dist/{chunk-U5HD7BB2.js → chunk-2FOD2YY3.js} +10 -10
  60. package/dist/{chunk-GG6AJN7A.js → chunk-2IZAGAJR.js} +2 -2
  61. package/dist/{chunk-6Y4JTNX7.js → chunk-2MR3MFQB.js} +2 -2
  62. package/dist/{chunk-AU7Q3LSC.js → chunk-2QSZNTDO.js} +4 -4
  63. package/dist/{chunk-IBAU2RZN.js → chunk-2U2A5HF4.js} +11 -5
  64. package/dist/chunk-2U2A5HF4.js.map +1 -0
  65. package/dist/chunk-37CTGERM.js +62 -0
  66. package/dist/chunk-37CTGERM.js.map +1 -0
  67. package/dist/{chunk-MYNRJGOY.js → chunk-3F4N4WDH.js} +76 -111
  68. package/dist/chunk-3F4N4WDH.js.map +1 -0
  69. package/dist/{chunk-7OSULYN2.js → chunk-4BFRBP45.js} +2 -2
  70. package/dist/{chunk-DQC3ZZYM.js → chunk-4KJLYTLS.js} +2 -2
  71. package/dist/{chunk-DMKMQTBH.js → chunk-4P4626RY.js} +5 -5
  72. package/dist/{chunk-GY3SKOS4.js → chunk-5EZXHU36.js} +2 -2
  73. package/dist/{chunk-OW4UXZNP.js → chunk-5QZDMSSN.js} +61 -61
  74. package/dist/chunk-64UDOIMI.js +121 -0
  75. package/dist/chunk-64UDOIMI.js.map +1 -0
  76. package/dist/{chunk-OTMAYEY5.js → chunk-66FLU5BT.js} +3 -3
  77. package/dist/{chunk-J2QYB2D5.js → chunk-6BISDEN6.js} +7 -7
  78. package/dist/{chunk-CXBFYSQY.js → chunk-6ZIETXZ2.js} +3 -3
  79. package/dist/{chunk-KYYL4U6X.js → chunk-7N2RUBAP.js} +3 -3
  80. package/dist/{chunk-U3TRHUSJ.js → chunk-7QWMTOIF.js} +45 -45
  81. package/dist/{chunk-L24JROPR.js → chunk-7WSLR2CU.js} +2 -2
  82. package/dist/{chunk-T4AWNIXJ.js → chunk-7Z2DZNWN.js} +18 -9
  83. package/dist/{chunk-T4AWNIXJ.js.map → chunk-7Z2DZNWN.js.map} +1 -1
  84. package/dist/{chunk-EXM3CQTZ.js → chunk-A3GOMRJG.js} +2 -2
  85. package/dist/{chunk-DCWIQFNA.js → chunk-AKUCU7CI.js} +7 -7
  86. package/dist/{chunk-XHYGJVXL.js → chunk-AXTXN4T6.js} +2 -2
  87. package/dist/{chunk-L42FA4BU.js → chunk-BGTAL46Y.js} +2 -2
  88. package/dist/{chunk-5QANTTY2.js → chunk-BRDJ2HFP.js} +2 -2
  89. package/dist/{chunk-7VFZTJ7K.js → chunk-BV2TIZ4F.js} +2 -2
  90. package/dist/{chunk-QMN3CIFS.js → chunk-CGS7UKWM.js} +2 -2
  91. package/dist/{chunk-LZ73Y56P.js → chunk-CLX5VDNY.js} +8 -8
  92. package/dist/{chunk-KYC3RLYJ.js → chunk-CXLNZXG5.js} +1 -1
  93. package/dist/{chunk-YOELDDBF.js → chunk-CZAA5N5S.js} +4 -4
  94. package/dist/{chunk-XG4NBBRW.js → chunk-CZAZX7JP.js} +5 -5
  95. package/dist/{chunk-QOGXUNWL.js → chunk-CZGIIADK.js} +7 -7
  96. package/dist/{chunk-QRDOSYOR.js → chunk-DBRYEIGY.js} +2 -2
  97. package/dist/{chunk-6QODJKOV.js → chunk-DGYCWCQ7.js} +3 -3
  98. package/dist/{chunk-RFZW7NID.js → chunk-DR6PEJU3.js} +4 -4
  99. package/dist/{chunk-UC4YOAC2.js → chunk-E5BK6CGV.js} +2 -2
  100. package/dist/{chunk-TKZ3AL5V.js → chunk-EHK6KZHK.js} +2 -2
  101. package/dist/{chunk-X5NUL5N4.js → chunk-EPGWWCZF.js} +3 -3
  102. package/dist/{chunk-JZ7P26GF.js → chunk-F53UF7CI.js} +2 -2
  103. package/dist/{chunk-SJHM6I4J.js → chunk-FC77VUX5.js} +2 -2
  104. package/dist/{chunk-DKTSR7EK.js → chunk-FDPZVY6D.js} +2 -2
  105. package/dist/{chunk-OUXPVWTZ.js → chunk-FLVL4L3L.js} +5 -5
  106. package/dist/{chunk-ZI3YYOOV.js → chunk-FZGGZAAU.js} +2 -2
  107. package/dist/{chunk-TQT3USCG.js → chunk-HG32URJJ.js} +2 -2
  108. package/dist/{chunk-ZQNCHPTZ.js → chunk-HLTAPHZZ.js} +2 -2
  109. package/dist/{chunk-LU7ATMRV.js → chunk-HRCACVHL.js} +3 -3
  110. package/dist/{chunk-LOPZ2DWR.js → chunk-I73G4UH7.js} +2 -2
  111. package/dist/{chunk-MJE4XV4A.js → chunk-IC6QX5TC.js} +2 -2
  112. package/dist/{chunk-27TJRC2Y.js → chunk-ILFJ7AVS.js} +7 -7
  113. package/dist/{chunk-VUA4S7P6.js → chunk-JIKJPDQJ.js} +4 -4
  114. package/dist/{chunk-BKTC7JCA.js → chunk-JLR3JSTL.js} +3 -3
  115. package/dist/{chunk-VQ34TERH.js → chunk-JZHHREBL.js} +2 -2
  116. package/dist/{chunk-IQVQJJL7.js → chunk-K2ZLHESQ.js} +2 -2
  117. package/dist/{chunk-PWFWCGOO.js → chunk-KAWTOYAW.js} +2 -2
  118. package/dist/{chunk-YRZMCPC5.js → chunk-KCJ5H6XQ.js} +4 -4
  119. package/dist/{chunk-DJECGBBM.js → chunk-KQDNAXOM.js} +2 -2
  120. package/dist/{chunk-WIWPSQYU.js → chunk-KQPJLK3X.js} +2 -2
  121. package/dist/{chunk-XNWLD3RR.js → chunk-LOR6ZM5R.js} +3 -3
  122. package/dist/{chunk-EGLUVV6B.js → chunk-M7GGMGMZ.js} +10 -10
  123. package/dist/{chunk-YTU7WKYN.js → chunk-MNUHL34L.js} +3 -3
  124. package/dist/{chunk-ZCWIH4LH.js → chunk-MRHFJV44.js} +2 -2
  125. package/dist/{chunk-W4H5A3TJ.js → chunk-MTR7BZQN.js} +3 -3
  126. package/dist/{chunk-PNLCEFE4.js → chunk-MTWKDOVO.js} +2 -2
  127. package/dist/{chunk-TECYUPFB.js → chunk-NG7YBCJO.js} +4 -4
  128. package/dist/{chunk-OSMBC3JU.js → chunk-O2EL7EGG.js} +2 -2
  129. package/dist/{chunk-HW5EF6LV.js → chunk-O7BBFJLP.js} +2 -2
  130. package/dist/{chunk-EHQ5HO5T.js → chunk-OBMR4VIX.js} +1 -1
  131. package/dist/{chunk-VFHDUFH5.js → chunk-OHDJ34CC.js} +2 -2
  132. package/dist/{chunk-6SHAUM5S.js → chunk-OUMTBFSQ.js} +4 -4
  133. package/dist/{chunk-27CQIO7T.js → chunk-P6SYWE4G.js} +57 -10
  134. package/dist/chunk-P6SYWE4G.js.map +1 -0
  135. package/dist/{chunk-EEKDRAWX.js → chunk-PRTA6444.js} +2 -2
  136. package/dist/{chunk-IQ7WCZRW.js → chunk-PUBTHW5O.js} +3 -3
  137. package/dist/{chunk-AGNBY3VG.js → chunk-PYHOAXTI.js} +2 -2
  138. package/dist/{chunk-UUH4YQOF.js → chunk-QLULYKWZ.js} +2 -2
  139. package/dist/{chunk-DBSMLRTO.js → chunk-QT2RZNRT.js} +2 -2
  140. package/dist/{chunk-HM6UJPPO.js → chunk-QWGNDWKB.js} +2 -2
  141. package/dist/{chunk-KCQA46NR.js → chunk-R5VAVVHP.js} +2 -2
  142. package/dist/{chunk-SXN3LCUT.js → chunk-RHOZ5LLO.js} +100 -4
  143. package/dist/chunk-RHOZ5LLO.js.map +1 -0
  144. package/dist/{chunk-6KYMPV2O.js → chunk-RL6NEVXT.js} +45 -2
  145. package/dist/chunk-RL6NEVXT.js.map +1 -0
  146. package/dist/{chunk-O7GOFAM3.js → chunk-RP7YXA67.js} +2 -2
  147. package/dist/{chunk-BVKCV2ZY.js → chunk-SMX5267P.js} +2 -2
  148. package/dist/{chunk-XBZQRZ6G.js → chunk-TN7THW5U.js} +2 -2
  149. package/dist/{chunk-JRZV3767.js → chunk-UI4FVU6T.js} +2 -2
  150. package/dist/{chunk-VGUOEDTU.js → chunk-V3UE5I5F.js} +2 -2
  151. package/dist/{chunk-7WV3F5DQ.js → chunk-V7IZKJI3.js} +2 -2
  152. package/dist/{chunk-VL7DP3OW.js → chunk-VFSP73LP.js} +2 -2
  153. package/dist/{chunk-C2DDH5UN.js → chunk-X4A62ET7.js} +14 -14
  154. package/dist/{chunk-WRBDUNSW.js → chunk-XN3TUUX2.js} +3 -3
  155. package/dist/{chunk-NNL4LJAD.js → chunk-XZ5I7OZR.js} +5 -5
  156. package/dist/{chunk-K2I3AWBR.js → chunk-YDSWRD33.js} +5 -5
  157. package/dist/{chunk-3NN73CM7.js → chunk-YFBTVOMB.js} +3 -3
  158. package/dist/{chunk-44YXWE35.js → chunk-YPEMNNNY.js} +2 -2
  159. package/dist/{chunk-ZZMRXJFY.js → chunk-ZIDSD6VC.js} +2 -2
  160. package/dist/{chunk-BMANIB5B.js → chunk-ZPOROUTX.js} +3 -3
  161. package/dist/{cli-Dfnc66gs.d.ts → cli-EiBBOt3r.d.ts} +3 -3
  162. package/dist/cli.d.ts +6 -6
  163. package/dist/cli.js +74 -73
  164. package/dist/compounding/engine.d.ts +1 -1
  165. package/dist/compounding/engine.js +13 -13
  166. package/dist/compounding/preference-consolidator.d.ts +1 -1
  167. package/dist/compounding/preference-consolidator.js +1 -1
  168. package/dist/compression-optimizer.d.ts +1 -1
  169. package/dist/config.d.ts +1 -1
  170. package/dist/config.js +2 -2
  171. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  172. package/dist/connectors/codex-materialize-runner.js +13 -13
  173. package/dist/connectors/codex-materialize.d.ts +1 -1
  174. package/dist/connectors/codex-materialize.js +2 -2
  175. package/dist/connectors/index.d.ts +1 -1
  176. package/dist/connectors/index.js +14 -14
  177. package/dist/consolidation-provenance-check.d.ts +1 -1
  178. package/dist/consolidation-undo.d.ts +1 -1
  179. package/dist/contradiction/index.d.ts +1 -1
  180. package/dist/contradiction/index.js +3 -3
  181. package/dist/{contradiction-scan-3FJYWS3G.js → contradiction-scan-SSKCAMLX.js} +3 -3
  182. package/dist/conversation-index/backend.d.ts +1 -1
  183. package/dist/conversation-index/backend.js +5 -5
  184. package/dist/conversation-index/chunker.d.ts +1 -1
  185. package/dist/conversation-index/cleanup.js +2 -2
  186. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  187. package/dist/conversation-index/faiss-adapter.js +2 -2
  188. package/dist/conversation-index/indexer.d.ts +1 -1
  189. package/dist/conversation-index/indexer.js +2 -2
  190. package/dist/conversation-index/search.d.ts +1 -1
  191. package/dist/conversation-index/search.js +2 -2
  192. package/dist/day-summary.d.ts +1 -1
  193. package/dist/day-summary.js +2 -2
  194. package/dist/delinearize.d.ts +1 -1
  195. package/dist/direct-answer-wiring.d.ts +1 -1
  196. package/dist/direct-answer.d.ts +1 -1
  197. package/dist/embedding-fallback.d.ts +1 -1
  198. package/dist/embedding-fallback.js +2 -2
  199. package/dist/enrichment/index.d.ts +1 -1
  200. package/dist/entity-retrieval.d.ts +1 -1
  201. package/dist/entity-retrieval.js +13 -13
  202. package/dist/entity-schema.d.ts +1 -1
  203. package/dist/explicit-capture.d.ts +4 -4
  204. package/dist/extraction-faithfulness.d.ts +1 -1
  205. package/dist/extraction-faithfulness.js +5 -5
  206. package/dist/extraction-judge-telemetry.d.ts +1 -1
  207. package/dist/extraction-judge-telemetry.js +2 -2
  208. package/dist/extraction-judge-training.d.ts +1 -1
  209. package/dist/extraction-judge-training.js +2 -2
  210. package/dist/extraction-judge.d.ts +1 -1
  211. package/dist/extraction-judge.js +5 -5
  212. package/dist/extraction.d.ts +1 -1
  213. package/dist/extraction.js +9 -9
  214. package/dist/fallback-llm.d.ts +1 -1
  215. package/dist/fallback-llm.js +4 -4
  216. package/dist/{forget-OEE5OELK.js → forget-KHF7A44H.js} +3 -3
  217. package/dist/graph-dashboard-diff.d.ts +1 -1
  218. package/dist/graph-dashboard-key.d.ts +1 -1
  219. package/dist/graph-dashboard-parser.d.ts +1 -1
  220. package/dist/{graph-edge-decay-RXUCJIN4.js → graph-edge-decay-FO7AM4UR.js} +3 -3
  221. package/dist/graph-edge-reinforcement.d.ts +1 -1
  222. package/dist/graph-snapshot.d.ts +1 -1
  223. package/dist/graph-snapshot.js +2 -2
  224. package/dist/graph.d.ts +1 -1
  225. package/dist/graph.js +1 -1
  226. package/dist/identity-continuity.d.ts +1 -1
  227. package/dist/importance.d.ts +1 -1
  228. package/dist/index.d.ts +17 -12
  229. package/dist/index.js +124 -123
  230. package/dist/intent.d.ts +1 -1
  231. package/dist/lcm/archive.js +2 -2
  232. package/dist/lcm/engine.d.ts +1 -1
  233. package/dist/lcm/engine.js +7 -7
  234. package/dist/lcm/index.d.ts +1 -1
  235. package/dist/lcm/index.js +9 -9
  236. package/dist/lcm/schema.js +3 -3
  237. package/dist/lcm/summarizer.js +3 -3
  238. package/dist/lcm/tools.d.ts +1 -1
  239. package/dist/lifecycle.d.ts +1 -1
  240. package/dist/live-connectors-runner.d.ts +1 -1
  241. package/dist/local-llm.d.ts +1 -1
  242. package/dist/local-llm.js +2 -2
  243. package/dist/local-model-endpoint.d.ts +1 -1
  244. package/dist/logger.d.ts +8 -2
  245. package/dist/logger.js +1 -1
  246. package/dist/maintenance/memory-governance.d.ts +1 -1
  247. package/dist/maintenance/memory-governance.js +12 -12
  248. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +12 -12
  249. package/dist/maintenance/rebuild-memory-projection.js +14 -14
  250. package/dist/mcp-memory-inspector-app.d.ts +5 -5
  251. package/dist/memory-action-policy.d.ts +1 -1
  252. package/dist/memory-cache.d.ts +1 -1
  253. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  254. package/dist/memory-projection-store.d.ts +23 -3
  255. package/dist/memory-projection-store.js +9 -2
  256. package/dist/memory-provenance.d.ts +1 -1
  257. package/dist/memory-worth-outcomes.d.ts +1 -1
  258. package/dist/model-registry.js +2 -2
  259. package/dist/models-json.d.ts +1 -1
  260. package/dist/models-json.js +2 -2
  261. package/dist/namespaces/migrate.d.ts +2 -2
  262. package/dist/namespaces/migrate.js +27 -26
  263. package/dist/namespaces/principal.d.ts +1 -1
  264. package/dist/namespaces/search.d.ts +1 -1
  265. package/dist/namespaces/search.js +15 -14
  266. package/dist/namespaces/storage.d.ts +2 -2
  267. package/dist/namespaces/storage.js +12 -12
  268. package/dist/native-knowledge.d.ts +1 -1
  269. package/dist/native-knowledge.js +2 -2
  270. package/dist/negative.js +2 -2
  271. package/dist/operator-toolkit.d.ts +11 -2
  272. package/dist/operator-toolkit.js +36 -33
  273. package/dist/orchestration/compression-guideline-coordinator.d.ts +1 -1
  274. package/dist/orchestration/compression-guideline-coordinator.js +2 -2
  275. package/dist/orchestration/maintenance.d.ts +2 -2
  276. package/dist/orchestration/maintenance.js +14 -14
  277. package/dist/{orchestrator-Y9wyG4jK.d.ts → orchestrator-B2Y28Z9t.d.ts} +3 -3
  278. package/dist/orchestrator.d.ts +4 -4
  279. package/dist/orchestrator.js +109 -108
  280. package/dist/patterns-cli.d.ts +1 -1
  281. package/dist/policy-runtime.d.ts +1 -1
  282. package/dist/profiling.js +2 -2
  283. package/dist/provenance.d.ts +1 -1
  284. package/dist/{purge-IHRX4Z7X.js → purge-NDWV6WYD.js} +4 -3
  285. package/dist/{purge-IHRX4Z7X.js.map → purge-NDWV6WYD.js.map} +1 -1
  286. package/dist/qmd-Db9mOkzB.d.ts +330 -0
  287. package/dist/qmd-preflight.d.ts +5 -0
  288. package/dist/qmd-preflight.js +31 -0
  289. package/dist/qmd-recall-cache.d.ts +1 -1
  290. package/dist/qmd.d.ts +3 -305
  291. package/dist/qmd.js +11 -9
  292. package/dist/recall-disclosure-escalation.d.ts +1 -1
  293. package/dist/recall-explain-renderer.d.ts +1 -1
  294. package/dist/recall-planner-llm.d.ts +1 -1
  295. package/dist/recall-planner-llm.js +4 -4
  296. package/dist/recall-qos.js +2 -2
  297. package/dist/recall-state.d.ts +1 -1
  298. package/dist/recall-state.js +2 -2
  299. package/dist/recall-tag-filter.d.ts +1 -1
  300. package/dist/recall-timings.d.ts +1 -1
  301. package/dist/recall-xray-cli.d.ts +1 -1
  302. package/dist/recall-xray-renderer.d.ts +1 -1
  303. package/dist/recall-xray.d.ts +1 -1
  304. package/dist/relevance.js +2 -2
  305. package/dist/{resolution-33PIX3TT.js → resolution-VH4NGENZ.js} +3 -3
  306. package/dist/resolution-VH4NGENZ.js.map +1 -0
  307. package/dist/resolve-auth-token.d.ts +1 -1
  308. package/dist/resolve-provider-secret.js +2 -2
  309. package/dist/resume-bundles.js +4 -4
  310. package/dist/retrieval-agents.d.ts +2 -2
  311. package/dist/retrieval-agents.js +2 -2
  312. package/dist/retrieval-tiers.d.ts +1 -1
  313. package/dist/routing/engine.d.ts +1 -1
  314. package/dist/routing/store.d.ts +1 -1
  315. package/dist/routing/store.js +2 -2
  316. package/dist/runtime/better-sqlite.d.ts +17 -1
  317. package/dist/runtime/better-sqlite.js +5 -3
  318. package/dist/schemas.d.ts +22 -22
  319. package/dist/search/embed-helper.d.ts +1 -1
  320. package/dist/search/embed-helper.js +2 -2
  321. package/dist/search/factory.d.ts +1 -1
  322. package/dist/search/factory.js +14 -13
  323. package/dist/search/index.d.ts +1 -1
  324. package/dist/search/index.js +18 -17
  325. package/dist/search/lancedb-backend.d.ts +1 -1
  326. package/dist/search/lancedb-backend.js +3 -3
  327. package/dist/search/meilisearch-backend.d.ts +1 -1
  328. package/dist/search/meilisearch-backend.js +3 -3
  329. package/dist/search/noop-backend.d.ts +1 -1
  330. package/dist/search/orama-backend.d.ts +1 -1
  331. package/dist/search/orama-backend.js +3 -3
  332. package/dist/search/port.d.ts +1 -1
  333. package/dist/search/remote-backend.d.ts +1 -1
  334. package/dist/search/remote-backend.js +2 -2
  335. package/dist/{semantic-consolidation-bfXJNKVf.d.ts → semantic-consolidation-BDVIbLRw.d.ts} +1 -1
  336. package/dist/semantic-consolidation.d.ts +2 -2
  337. package/dist/semantic-consolidation.js +14 -14
  338. package/dist/semantic-rule-promotion.js +12 -12
  339. package/dist/semantic-rule-verifier.d.ts +1 -1
  340. package/dist/semantic-rule-verifier.js +12 -12
  341. package/dist/session-observer-bands.d.ts +1 -1
  342. package/dist/session-observer-state.d.ts +1 -1
  343. package/dist/session-observer-state.js +2 -2
  344. package/dist/session-transcript-migration.js +3 -3
  345. package/dist/shared-context/manager.d.ts +1 -1
  346. package/dist/shared-context/manager.js +2 -2
  347. package/dist/signal.d.ts +1 -1
  348. package/dist/storage-guards.js +2 -2
  349. package/dist/storage.d.ts +1 -1
  350. package/dist/storage.js +11 -11
  351. package/dist/summarizer.d.ts +1 -1
  352. package/dist/summarizer.js +7 -7
  353. package/dist/summary-snapshot.d.ts +1 -1
  354. package/dist/temporal-supersession.d.ts +1 -1
  355. package/dist/temporal-supersession.js +2 -2
  356. package/dist/temporal-validity.d.ts +1 -1
  357. package/dist/threading.d.ts +1 -1
  358. package/dist/threading.js +2 -2
  359. package/dist/tier-migration.d.ts +1 -1
  360. package/dist/tier-routing.d.ts +1 -1
  361. package/dist/topics.d.ts +1 -1
  362. package/dist/transcript.d.ts +1 -1
  363. package/dist/transcript.js +2 -2
  364. package/dist/transfer/autodetect.js +1 -1
  365. package/dist/transfer/backup.js +1 -1
  366. package/dist/transfer/capsule-export.js +2 -2
  367. package/dist/transfer/export-sqlite.js +2 -2
  368. package/dist/transfer/import-sqlite.js +2 -2
  369. package/dist/transfer/types.d.ts +12 -12
  370. package/dist/trust-score-stage.d.ts +1 -1
  371. package/dist/trust-score.d.ts +1 -1
  372. package/dist/{types-D1iBqhJA.d.ts → types-ljkd82rn.d.ts} +13 -1
  373. package/dist/types.d.ts +1 -1
  374. package/dist/utility-runtime.d.ts +1 -1
  375. package/dist/verified-recall.js +13 -13
  376. package/package.json +2 -2
  377. package/src/logger.test.ts +120 -0
  378. package/src/logger.ts +37 -9
  379. package/src/memory-projection-open-failure.test.ts +322 -0
  380. package/src/memory-projection-store.ts +179 -2
  381. package/src/operator-toolkit.ts +64 -0
  382. package/src/qmd-preflight.test.ts +197 -0
  383. package/src/qmd-preflight.ts +167 -0
  384. package/src/qmd.ts +115 -129
  385. package/src/runtime/better-sqlite.test.ts +54 -0
  386. package/src/runtime/better-sqlite.ts +83 -3
  387. package/src/wearables/cleanup.test.ts +56 -0
  388. package/src/wearables/cleanup.ts +22 -1
  389. package/src/wearables/config.test.ts +25 -0
  390. package/src/wearables/config.ts +31 -4
  391. package/src/wearables/types.ts +12 -1
  392. package/dist/chunk-27CQIO7T.js.map +0 -1
  393. package/dist/chunk-6KYMPV2O.js.map +0 -1
  394. package/dist/chunk-IBAU2RZN.js.map +0 -1
  395. package/dist/chunk-MYNRJGOY.js.map +0 -1
  396. package/dist/chunk-NU3CSQ4H.js +0 -48
  397. package/dist/chunk-NU3CSQ4H.js.map +0 -1
  398. package/dist/chunk-SXN3LCUT.js.map +0 -1
  399. /package/dist/{auto-sync-2SOUW4JW.js.map → auto-sync-HMHQHUAG.js.map} +0 -0
  400. /package/dist/{chunk-PSNMICC2.js.map → chunk-27FLVMDC.js.map} +0 -0
  401. /package/dist/{chunk-7FL4CNPV.js.map → chunk-2A3OXAP2.js.map} +0 -0
  402. /package/dist/{chunk-U5HD7BB2.js.map → chunk-2FOD2YY3.js.map} +0 -0
  403. /package/dist/{chunk-GG6AJN7A.js.map → chunk-2IZAGAJR.js.map} +0 -0
  404. /package/dist/{chunk-6Y4JTNX7.js.map → chunk-2MR3MFQB.js.map} +0 -0
  405. /package/dist/{chunk-AU7Q3LSC.js.map → chunk-2QSZNTDO.js.map} +0 -0
  406. /package/dist/{chunk-7OSULYN2.js.map → chunk-4BFRBP45.js.map} +0 -0
  407. /package/dist/{chunk-DQC3ZZYM.js.map → chunk-4KJLYTLS.js.map} +0 -0
  408. /package/dist/{chunk-DMKMQTBH.js.map → chunk-4P4626RY.js.map} +0 -0
  409. /package/dist/{chunk-GY3SKOS4.js.map → chunk-5EZXHU36.js.map} +0 -0
  410. /package/dist/{chunk-OW4UXZNP.js.map → chunk-5QZDMSSN.js.map} +0 -0
  411. /package/dist/{chunk-OTMAYEY5.js.map → chunk-66FLU5BT.js.map} +0 -0
  412. /package/dist/{chunk-J2QYB2D5.js.map → chunk-6BISDEN6.js.map} +0 -0
  413. /package/dist/{chunk-CXBFYSQY.js.map → chunk-6ZIETXZ2.js.map} +0 -0
  414. /package/dist/{chunk-KYYL4U6X.js.map → chunk-7N2RUBAP.js.map} +0 -0
  415. /package/dist/{chunk-U3TRHUSJ.js.map → chunk-7QWMTOIF.js.map} +0 -0
  416. /package/dist/{chunk-L24JROPR.js.map → chunk-7WSLR2CU.js.map} +0 -0
  417. /package/dist/{chunk-EXM3CQTZ.js.map → chunk-A3GOMRJG.js.map} +0 -0
  418. /package/dist/{chunk-DCWIQFNA.js.map → chunk-AKUCU7CI.js.map} +0 -0
  419. /package/dist/{chunk-XHYGJVXL.js.map → chunk-AXTXN4T6.js.map} +0 -0
  420. /package/dist/{chunk-L42FA4BU.js.map → chunk-BGTAL46Y.js.map} +0 -0
  421. /package/dist/{chunk-5QANTTY2.js.map → chunk-BRDJ2HFP.js.map} +0 -0
  422. /package/dist/{chunk-7VFZTJ7K.js.map → chunk-BV2TIZ4F.js.map} +0 -0
  423. /package/dist/{chunk-QMN3CIFS.js.map → chunk-CGS7UKWM.js.map} +0 -0
  424. /package/dist/{chunk-LZ73Y56P.js.map → chunk-CLX5VDNY.js.map} +0 -0
  425. /package/dist/{chunk-KYC3RLYJ.js.map → chunk-CXLNZXG5.js.map} +0 -0
  426. /package/dist/{chunk-YOELDDBF.js.map → chunk-CZAA5N5S.js.map} +0 -0
  427. /package/dist/{chunk-XG4NBBRW.js.map → chunk-CZAZX7JP.js.map} +0 -0
  428. /package/dist/{chunk-QOGXUNWL.js.map → chunk-CZGIIADK.js.map} +0 -0
  429. /package/dist/{chunk-QRDOSYOR.js.map → chunk-DBRYEIGY.js.map} +0 -0
  430. /package/dist/{chunk-6QODJKOV.js.map → chunk-DGYCWCQ7.js.map} +0 -0
  431. /package/dist/{chunk-RFZW7NID.js.map → chunk-DR6PEJU3.js.map} +0 -0
  432. /package/dist/{chunk-UC4YOAC2.js.map → chunk-E5BK6CGV.js.map} +0 -0
  433. /package/dist/{chunk-TKZ3AL5V.js.map → chunk-EHK6KZHK.js.map} +0 -0
  434. /package/dist/{chunk-X5NUL5N4.js.map → chunk-EPGWWCZF.js.map} +0 -0
  435. /package/dist/{chunk-JZ7P26GF.js.map → chunk-F53UF7CI.js.map} +0 -0
  436. /package/dist/{chunk-SJHM6I4J.js.map → chunk-FC77VUX5.js.map} +0 -0
  437. /package/dist/{chunk-DKTSR7EK.js.map → chunk-FDPZVY6D.js.map} +0 -0
  438. /package/dist/{chunk-OUXPVWTZ.js.map → chunk-FLVL4L3L.js.map} +0 -0
  439. /package/dist/{chunk-ZI3YYOOV.js.map → chunk-FZGGZAAU.js.map} +0 -0
  440. /package/dist/{chunk-TQT3USCG.js.map → chunk-HG32URJJ.js.map} +0 -0
  441. /package/dist/{chunk-ZQNCHPTZ.js.map → chunk-HLTAPHZZ.js.map} +0 -0
  442. /package/dist/{chunk-LU7ATMRV.js.map → chunk-HRCACVHL.js.map} +0 -0
  443. /package/dist/{chunk-LOPZ2DWR.js.map → chunk-I73G4UH7.js.map} +0 -0
  444. /package/dist/{chunk-MJE4XV4A.js.map → chunk-IC6QX5TC.js.map} +0 -0
  445. /package/dist/{chunk-27TJRC2Y.js.map → chunk-ILFJ7AVS.js.map} +0 -0
  446. /package/dist/{chunk-VUA4S7P6.js.map → chunk-JIKJPDQJ.js.map} +0 -0
  447. /package/dist/{chunk-BKTC7JCA.js.map → chunk-JLR3JSTL.js.map} +0 -0
  448. /package/dist/{chunk-VQ34TERH.js.map → chunk-JZHHREBL.js.map} +0 -0
  449. /package/dist/{chunk-IQVQJJL7.js.map → chunk-K2ZLHESQ.js.map} +0 -0
  450. /package/dist/{chunk-PWFWCGOO.js.map → chunk-KAWTOYAW.js.map} +0 -0
  451. /package/dist/{chunk-YRZMCPC5.js.map → chunk-KCJ5H6XQ.js.map} +0 -0
  452. /package/dist/{chunk-DJECGBBM.js.map → chunk-KQDNAXOM.js.map} +0 -0
  453. /package/dist/{chunk-WIWPSQYU.js.map → chunk-KQPJLK3X.js.map} +0 -0
  454. /package/dist/{chunk-XNWLD3RR.js.map → chunk-LOR6ZM5R.js.map} +0 -0
  455. /package/dist/{chunk-EGLUVV6B.js.map → chunk-M7GGMGMZ.js.map} +0 -0
  456. /package/dist/{chunk-YTU7WKYN.js.map → chunk-MNUHL34L.js.map} +0 -0
  457. /package/dist/{chunk-ZCWIH4LH.js.map → chunk-MRHFJV44.js.map} +0 -0
  458. /package/dist/{chunk-W4H5A3TJ.js.map → chunk-MTR7BZQN.js.map} +0 -0
  459. /package/dist/{chunk-PNLCEFE4.js.map → chunk-MTWKDOVO.js.map} +0 -0
  460. /package/dist/{chunk-TECYUPFB.js.map → chunk-NG7YBCJO.js.map} +0 -0
  461. /package/dist/{chunk-OSMBC3JU.js.map → chunk-O2EL7EGG.js.map} +0 -0
  462. /package/dist/{chunk-HW5EF6LV.js.map → chunk-O7BBFJLP.js.map} +0 -0
  463. /package/dist/{chunk-EHQ5HO5T.js.map → chunk-OBMR4VIX.js.map} +0 -0
  464. /package/dist/{chunk-VFHDUFH5.js.map → chunk-OHDJ34CC.js.map} +0 -0
  465. /package/dist/{chunk-6SHAUM5S.js.map → chunk-OUMTBFSQ.js.map} +0 -0
  466. /package/dist/{chunk-EEKDRAWX.js.map → chunk-PRTA6444.js.map} +0 -0
  467. /package/dist/{chunk-IQ7WCZRW.js.map → chunk-PUBTHW5O.js.map} +0 -0
  468. /package/dist/{chunk-AGNBY3VG.js.map → chunk-PYHOAXTI.js.map} +0 -0
  469. /package/dist/{chunk-UUH4YQOF.js.map → chunk-QLULYKWZ.js.map} +0 -0
  470. /package/dist/{chunk-DBSMLRTO.js.map → chunk-QT2RZNRT.js.map} +0 -0
  471. /package/dist/{chunk-HM6UJPPO.js.map → chunk-QWGNDWKB.js.map} +0 -0
  472. /package/dist/{chunk-KCQA46NR.js.map → chunk-R5VAVVHP.js.map} +0 -0
  473. /package/dist/{chunk-O7GOFAM3.js.map → chunk-RP7YXA67.js.map} +0 -0
  474. /package/dist/{chunk-BVKCV2ZY.js.map → chunk-SMX5267P.js.map} +0 -0
  475. /package/dist/{chunk-XBZQRZ6G.js.map → chunk-TN7THW5U.js.map} +0 -0
  476. /package/dist/{chunk-JRZV3767.js.map → chunk-UI4FVU6T.js.map} +0 -0
  477. /package/dist/{chunk-VGUOEDTU.js.map → chunk-V3UE5I5F.js.map} +0 -0
  478. /package/dist/{chunk-7WV3F5DQ.js.map → chunk-V7IZKJI3.js.map} +0 -0
  479. /package/dist/{chunk-VL7DP3OW.js.map → chunk-VFSP73LP.js.map} +0 -0
  480. /package/dist/{chunk-C2DDH5UN.js.map → chunk-X4A62ET7.js.map} +0 -0
  481. /package/dist/{chunk-WRBDUNSW.js.map → chunk-XN3TUUX2.js.map} +0 -0
  482. /package/dist/{chunk-NNL4LJAD.js.map → chunk-XZ5I7OZR.js.map} +0 -0
  483. /package/dist/{chunk-K2I3AWBR.js.map → chunk-YDSWRD33.js.map} +0 -0
  484. /package/dist/{chunk-3NN73CM7.js.map → chunk-YFBTVOMB.js.map} +0 -0
  485. /package/dist/{chunk-44YXWE35.js.map → chunk-YPEMNNNY.js.map} +0 -0
  486. /package/dist/{chunk-ZZMRXJFY.js.map → chunk-ZIDSD6VC.js.map} +0 -0
  487. /package/dist/{chunk-BMANIB5B.js.map → chunk-ZPOROUTX.js.map} +0 -0
  488. /package/dist/{contradiction-scan-3FJYWS3G.js.map → contradiction-scan-SSKCAMLX.js.map} +0 -0
  489. /package/dist/{forget-OEE5OELK.js.map → forget-KHF7A44H.js.map} +0 -0
  490. /package/dist/{graph-edge-decay-RXUCJIN4.js.map → graph-edge-decay-FO7AM4UR.js.map} +0 -0
  491. /package/dist/{resolution-33PIX3TT.js.map → qmd-preflight.js.map} +0 -0
@@ -12,7 +12,10 @@ import type {
12
12
  MemoryProjectionCurrentState,
13
13
  MemoryStatus,
14
14
  } from "./types.js";
15
+ import { log } from "./logger.js";
15
16
  import {
17
+ displayErrorDetail,
18
+ isLikelyBetterSqlite3NativeBindingError,
16
19
  openBetterSqlite3,
17
20
  type BetterSqlite3Database,
18
21
  } from "./runtime/better-sqlite.js";
@@ -314,14 +317,188 @@ export function initializeMemoryProjectionDb(db: BetterSqlite3Database): void {
314
317
  .run("schemaVersion", String(MEMORY_PROJECTION_SCHEMA_VERSION));
315
318
  }
316
319
 
320
+ // Test seam (issue #1829): the readonly projection opener is overridable so
321
+ // tests can inject an ABI-style throw against a PRESENT file without faking a
322
+ // corrupt native binding. Default delegates to the shared opener.
323
+ type ProjectionReadonlyOpener = typeof openBetterSqlite3;
324
+ let projectionReadonlyOpener: ProjectionReadonlyOpener = openBetterSqlite3;
325
+
326
+ /** @internal Test seam: override the readonly projection opener; returns a restore fn. */
327
+ export function __setProjectionReadonlyOpenerForTest(
328
+ opener: ProjectionReadonlyOpener | null,
329
+ ): () => void {
330
+ const previous = projectionReadonlyOpener;
331
+ projectionReadonlyOpener = opener ?? openBetterSqlite3;
332
+ return () => {
333
+ projectionReadonlyOpener = previous;
334
+ };
335
+ }
336
+
337
+ // Rate-limited suppression for open failures — mirrors warnProjectionFallback
338
+ // in storage-guards.ts (5-min dedup per memoryDir). A present-but-unopenable
339
+ // projection is a real failure that must surface, but it repeats on every
340
+ // browse, so the log is throttled to keep output actionable (issue #1829).
341
+ const PROJECTION_OPEN_FAILURE_LOG_INTERVAL_MS = 5 * 60_000;
342
+ const projectionOpenFailureLoggedAt = new Map<string, number>();
343
+
344
+ /** @internal Test seam: clear the rate-limit dedup map. */
345
+ export function __resetProjectionOpenFailureSuppressionForTest(): void {
346
+ projectionOpenFailureLoggedAt.clear();
347
+ }
348
+
349
+ function logProjectionOpenFailure(memoryDir: string, error: unknown): void {
350
+ const now = Date.now();
351
+ const warnedAt = projectionOpenFailureLoggedAt.get(memoryDir) ?? 0;
352
+ if (now - warnedAt < PROJECTION_OPEN_FAILURE_LOG_INTERVAL_MS) return;
353
+ projectionOpenFailureLoggedAt.set(memoryDir, now);
354
+ // Path-free detail only (rule 11 / CodeQL js/stack-trace-exposure): the raw
355
+ // message can embed absolute loader paths; displayErrorDetail returns class +
356
+ // code. This is the DISTINCT signal that was missing before #1829, when a
357
+ // wrong-ABI build collapsed into the same silent null as a missing file.
358
+ const detail = displayErrorDetail(error);
359
+ const nativeMismatch = isLikelyBetterSqlite3NativeBindingError(error);
360
+ const detailSuffix = detail ? ` (${detail})` : "";
361
+ const abiSuffix = nativeMismatch
362
+ ? " — native binding built for the wrong Node.js ABI; rebuild better-sqlite3 for this process"
363
+ : "";
364
+ log.warn(
365
+ `storage.memory-projection: index present but could not be opened${detailSuffix}${abiSuffix}; falling back to full-corpus scan`,
366
+ );
367
+ }
368
+
317
369
  function openProjectionReadonly(memoryDir: string): BetterSqlite3Database | null {
318
370
  const dbPath = getMemoryProjectionPath(memoryDir);
371
+ // A MISSING file is a normal fallback (cold install, never built). A file
372
+ // that EXISTS but cannot open (wrong ABI / corrupt / bad perms) is a real
373
+ // failure that must surface (issue #1829): previously both collapsed into
374
+ // the same silent null, hiding native-binding mismatches behind 17-27s
375
+ // full-corpus scans on every memory list.
376
+ if (!fs.existsSync(dbPath)) return null;
319
377
  try {
320
- return openBetterSqlite3(dbPath, { readonly: true, fileMustExist: true });
321
- } catch {
378
+ return projectionReadonlyOpener(dbPath, { readonly: true, fileMustExist: true });
379
+ } catch (error) {
380
+ logProjectionOpenFailure(memoryDir, error);
322
381
  return null;
323
382
  }
324
383
  }
384
+
385
+ export type ProjectionHealthState = "absent" | "openable" | "present-but-invalid" | "unopenable";
386
+
387
+ export interface ProjectionHealthProbe {
388
+ state: ProjectionHealthState;
389
+ /** Path-free detail (class + code) when state !== "absent"/"openable"; "" otherwise. */
390
+ detail: string;
391
+ /** true when the open failure is classified as a native-binding ABI mismatch. */
392
+ nativeBindingMismatch: boolean;
393
+ }
394
+
395
+ /**
396
+ * Confirm the projection schema is present and queryable, not just that the
397
+ * sqlite file opens (issue #1848 round 2). A file that opens but has the
398
+ * projection tables missing — or is corrupt and only fails on first statement
399
+ * — previously reported `openable`/ok, so `remnic doctor` said healthy while
400
+ * browse silently fell back to full-corpus scans. `memory_current` is the
401
+ * core browse table; if it is absent or unreadable the projection cannot
402
+ * serve any query. Throws so the probe can report a distinct state.
403
+ */
404
+ // Named so displayErrorDetail surfaces a clear, path-free identifier in the
405
+ // doctor report rather than a generic "Error".
406
+ class ProjectionSchemaError extends Error {
407
+ constructor(message: string) {
408
+ super(message);
409
+ this.name = "ProjectionSchemaError";
410
+ }
411
+ }
412
+
413
+ /**
414
+ * Columns `readProjectedMemoryBrowse` SELECTs from `memory_current` with no
415
+ * graceful fallback. `tags_json`/`preview_text` fall back via
416
+ * memoryCurrentSelectExpressions and `path_valid` falls back via the legacy
417
+ * full-scan branch — none of those make a browse query throw. A projection
418
+ * whose `memory_current` lacks any of these columns cannot serve browse
419
+ * queries: the SELECT throws and browse silently falls back to a full-corpus
420
+ * scan. Enumerated directly from readProjectedMemoryBrowse's SELECT list +
421
+ * ORDER BY references (issue #1848 round 3).
422
+ */
423
+ const REQUIRED_MEMORY_CURRENT_COLUMNS = [
424
+ "memory_id",
425
+ "path_rel",
426
+ "category",
427
+ "status",
428
+ "created_at",
429
+ "updated_at",
430
+ "entity_ref",
431
+ ] as const;
432
+
433
+ function assertProjectionSchemaReadable(db: BetterSqlite3Database): void {
434
+ const row = db
435
+ .prepare(
436
+ "SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'memory_current'",
437
+ )
438
+ .get() as { name: string } | undefined;
439
+ if (!row?.name) {
440
+ throw new ProjectionSchemaError(
441
+ "projection schema is missing or was not initialized (memory_current table not found)",
442
+ );
443
+ }
444
+ // Round 3 (issue #1848): verify the columns readProjectedMemoryBrowse
445
+ // actually SELECT are present, not just that the table exists. A file like
446
+ // `CREATE TABLE memory_current (x INTEGER)` passed the round-2 table-existence
447
+ // + SELECT-1 check but cannot serve browse queries, so browse silently fell
448
+ // back to full-corpus scans while `remnic doctor` reported healthy. Classify
449
+ // such a stale/half-built table as present-but-invalid so the operator gets a
450
+ // rebuild hint instead of a false-clean bill of health.
451
+ const columns = listTableColumns(db, "memory_current");
452
+ const missing = REQUIRED_MEMORY_CURRENT_COLUMNS.filter((c) => !columns.has(c));
453
+ if (missing.length > 0) {
454
+ throw new ProjectionSchemaError(
455
+ `projection memory_current table is missing required column(s): ${missing.join(", ")}`,
456
+ );
457
+ }
458
+ // Also confirm the table is queryable — catches physical corruption that
459
+ // only surfaces on first read (the file opens but statements fail).
460
+ db.prepare("SELECT 1 FROM memory_current LIMIT 1").get();
461
+ }
462
+
463
+ /**
464
+ * Stat + attempt-open + schema-validate the memory projection WITHOUT keeping
465
+ * a handle, for the `remnic doctor` health check (issue #1829 / #1848).
466
+ * Distinguishes four states so operators can tell "not built yet" (normal) from
467
+ * "exists but broken" (needs a rebuild / native-binding rebuild). Never throws.
468
+ */
469
+ export function probeProjectionHealth(memoryDir: string): ProjectionHealthProbe {
470
+ const dbPath = getMemoryProjectionPath(memoryDir);
471
+ if (!fs.existsSync(dbPath)) {
472
+ return { state: "absent", detail: "", nativeBindingMismatch: false };
473
+ }
474
+ let db: BetterSqlite3Database | null = null;
475
+ try {
476
+ db = projectionReadonlyOpener(dbPath, { readonly: true, fileMustExist: true });
477
+ // Validate the projection is actually USABLE, not just that the file
478
+ // opens (issue #1848 round 2): a file that opens but has the schema
479
+ // missing/corrupt previously reported ok, so doctor said healthy while
480
+ // browse silently fell back to full-corpus scans.
481
+ assertProjectionSchemaReadable(db);
482
+ return { state: "openable", detail: "", nativeBindingMismatch: false };
483
+ } catch (error) {
484
+ // If the file OPENED (db assigned) but the schema query failed, the
485
+ // native binding is fine — the projection itself is stale/corrupt.
486
+ if (db) {
487
+ return {
488
+ state: "present-but-invalid",
489
+ detail: displayErrorDetail(error),
490
+ nativeBindingMismatch: false,
491
+ };
492
+ }
493
+ return {
494
+ state: "unopenable",
495
+ detail: displayErrorDetail(error),
496
+ nativeBindingMismatch: isLikelyBetterSqlite3NativeBindingError(error),
497
+ };
498
+ } finally {
499
+ db?.close();
500
+ }
501
+ }
325
502
  function updateProjectionBestEffort(
326
503
  memoryDir: string,
327
504
  sql: string,
@@ -11,6 +11,7 @@ import {
11
11
  resolveOpenClawWorkspaceStatePath,
12
12
  } from "./native-knowledge.js";
13
13
  import { StorageManager } from "./storage.js";
14
+ import { probeProjectionHealth } from "./memory-projection-store.js";
14
15
  import { listNamespaces } from "./namespaces/migrate.js";
15
16
  import { summarizeNamespaceMaintenanceHealth } from "./maintenance/namespace-maintenance-fanout.js";
16
17
  import {
@@ -1094,6 +1095,64 @@ export function summarizeProvenanceCoverage(
1094
1095
  return { counts, total: memories.length };
1095
1096
  }
1096
1097
 
1098
+ /**
1099
+ * Memory projection health (issue #1829). A present-but-unopenable projection
1100
+ * (wrong ABI / corrupt / bad perms) previously collapsed into the same silent
1101
+ * null as a missing file, hiding native-binding mismatches behind full-corpus
1102
+ * scans. This check stats + attempt-opens the projection so `remnic doctor`
1103
+ * distinguishes "not built yet" (normal) from "exists but broken" (needs a
1104
+ * rebuild / native-binding rebuild). Never throws.
1105
+ */
1106
+ export function summarizeProjectionHealth(
1107
+ config: Pick<PluginConfig, "memoryDir">,
1108
+ ): OperatorDoctorCheck {
1109
+ const probe = probeProjectionHealth(config.memoryDir);
1110
+ if (probe.state === "absent") {
1111
+ return {
1112
+ key: "memory_projection",
1113
+ status: "ok",
1114
+ summary: "Memory projection index not built yet (normal until first maintenance run).",
1115
+ details: { state: probe.state },
1116
+ };
1117
+ }
1118
+ if (probe.state === "openable") {
1119
+ return {
1120
+ key: "memory_projection",
1121
+ status: "ok",
1122
+ summary: "Memory projection index opens cleanly.",
1123
+ details: { state: probe.state },
1124
+ };
1125
+ }
1126
+ if (probe.state === "present-but-invalid") {
1127
+ const schemaDetailSuffix = probe.detail ? ` (${probe.detail})` : "";
1128
+ return {
1129
+ key: "memory_projection",
1130
+ status: "error",
1131
+ summary: `Memory projection index exists but its schema is missing or unreadable${schemaDetailSuffix}; memory browse is falling back to full-corpus scans.`,
1132
+ remediation:
1133
+ "Rebuild the projection with `remnic rebuild-memory-projection --write`.",
1134
+ details: { state: probe.state, detail: probe.detail },
1135
+ };
1136
+ }
1137
+ const nativeSuffix = probe.nativeBindingMismatch
1138
+ ? " Native binding appears built for the wrong Node.js ABI."
1139
+ : "";
1140
+ const detailSuffix = probe.detail ? ` (${probe.detail})` : "";
1141
+ return {
1142
+ key: "memory_projection",
1143
+ status: "error",
1144
+ summary: `Memory projection index exists but could not be opened${detailSuffix}.${nativeSuffix}`,
1145
+ remediation: probe.nativeBindingMismatch
1146
+ ? "Rebuild the better-sqlite3 native binding for this Node.js (`node scripts/ensure-better-sqlite3.mjs` or `pnpm rebuild better-sqlite3`), then restart."
1147
+ : "Rebuild the projection with `remnic rebuild-memory-projection --write`; check file permissions and integrity.",
1148
+ details: {
1149
+ state: probe.state,
1150
+ detail: probe.detail,
1151
+ nativeBindingMismatch: probe.nativeBindingMismatch,
1152
+ },
1153
+ };
1154
+ }
1155
+
1097
1156
  export async function runOperatorDoctor(options: OperatorDoctorOptions): Promise<OperatorDoctorReport> {
1098
1157
  const now = options.now ?? new Date();
1099
1158
  const configStatus = await loadCliPluginConfig(options.configPath);
@@ -1211,6 +1270,11 @@ export async function runOperatorDoctor(options: OperatorDoctorOptions): Promise
1211
1270
  details: meta,
1212
1271
  });
1213
1272
 
1273
+ // Memory projection health (issue #1829). Surfaces a present-but-unopenable
1274
+ // projection (wrong ABI / corrupt) that previously degraded every memory
1275
+ // list to a silent full-corpus scan. Absent and openable are both "ok".
1276
+ checks.push(summarizeProjectionHealth(config));
1277
+
1214
1278
  // Provenance coverage (issue #1575 PR 3). Operators need to see the
1215
1279
  // verified/unverified/none distribution grow as extraction backfills.
1216
1280
  // Best-effort: a read failure surfaces as a warn, not an error.
@@ -0,0 +1,197 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import { abortError } from "./abort-error.js";
4
+ import { QmdClient } from "./qmd.js";
5
+
6
+ /**
7
+ * Internals exposed only for preflight testing. Cast once to a NAMED type (not
8
+ * an inline shape) so the compiler never fabricates a one-off member type; the
9
+ * unchecked access is scoped to these deliberately-injected seams. Issue #1841.
10
+ */
11
+ type QmdPreflightInternals = {
12
+ configuredQmdPath: string | undefined;
13
+ runVersionProbe: (qmdPath: string, signal?: AbortSignal) => Promise<{ stdout: string; stderr: string }>;
14
+ logCliProbeWarning: (message: string) => void;
15
+ probeCli: (options?: {
16
+ allowAutoUpgrade?: boolean;
17
+ preserveStateOnFailure?: boolean;
18
+ signal?: AbortSignal;
19
+ }) => Promise<boolean>;
20
+ };
21
+
22
+ const CONFIGURED_PATH = "/opt/qmd/bin/qmd";
23
+
24
+ /** Build the Node spawn ENOENT error shape (Error carrying `code: "ENOENT"`). */
25
+ function spawnENOENT(qmdPath: string): Error & { code: string } {
26
+ return Object.assign(new Error(`spawn ${qmdPath} ENOENT`), {
27
+ code: "ENOENT" as const,
28
+ });
29
+ }
30
+
31
+ function timeoutError(): Error & { timedOut: true } {
32
+ // Mirrors the error runCommandWithTimeout rejects with on deadline: a plain
33
+ // Error flagged with `timedOut: true` — the structured signal classifyProbeFailure
34
+ // keys on (never the message text). Issue #1841.
35
+ return Object.assign(new Error("qmd --version timed out after 8000ms"), {
36
+ timedOut: true as const,
37
+ });
38
+ }
39
+
40
+ /** Wire a fresh client whose probe runner + warning sink are fully scripted. */
41
+ function harness(opts: {
42
+ configuredBehavior: () => Promise<{ stdout: string; stderr: string }>;
43
+ }): {
44
+ internals: QmdPreflightInternals;
45
+ warnings: string[];
46
+ configuredProbeCalls: () => number;
47
+ } {
48
+ const client = new QmdClient("memories", 3, {
49
+ qmdPath: CONFIGURED_PATH,
50
+ qmdFallbackPaths: [],
51
+ });
52
+ const internals = client as unknown as QmdPreflightInternals;
53
+ const warnings: string[] = [];
54
+ internals.logCliProbeWarning = (message: string) => {
55
+ warnings.push(message);
56
+ };
57
+ let configuredCalls = 0;
58
+ internals.runVersionProbe = async (qmdPath: string) => {
59
+ if (qmdPath === CONFIGURED_PATH) {
60
+ configuredCalls += 1;
61
+ return opts.configuredBehavior();
62
+ }
63
+ // Any non-configured path (PATH/fallback probing) is a hard miss here so
64
+ // probeCli returns false without spawning a real qmd binary.
65
+ throw spawnENOENT(qmdPath);
66
+ };
67
+ return {
68
+ internals,
69
+ warnings,
70
+ configuredProbeCalls: () => configuredCalls,
71
+ };
72
+ }
73
+
74
+ test("QmdClient preflight: transient timeout retries then succeeds with NO failure warning", async () => {
75
+ // Two timeouts, then success on the second retry.
76
+ const scripted: Array<"timeout" | "ok"> = ["timeout", "timeout", "ok"];
77
+ let i = 0;
78
+ const { internals, warnings, configuredProbeCalls } = harness({
79
+ configuredBehavior: async () => {
80
+ const step = scripted[i++];
81
+ if (step === "timeout") throw timeoutError();
82
+ return { stdout: "qmd 1.2.3\n", stderr: "" };
83
+ },
84
+ });
85
+
86
+ const ok = await internals.probeCli({ allowAutoUpgrade: false });
87
+
88
+ assert.equal(ok, true, "configured probe should succeed after retry");
89
+ assert.equal(configuredProbeCalls(), 3, "initial attempt + 2 retries for transient timeout");
90
+ assert.deepEqual(warnings, [], "no failure warning emitted when retry succeeds");
91
+ });
92
+
93
+ test("QmdClient preflight: persistent timeout emits the distinct under-load warning (not 'configured qmdPath failed')", async () => {
94
+ const { internals, warnings, configuredProbeCalls } = harness({
95
+ configuredBehavior: async () => {
96
+ throw timeoutError();
97
+ },
98
+ });
99
+
100
+ const ok = await internals.probeCli({ allowAutoUpgrade: false });
101
+
102
+ assert.equal(ok, false, "exhausted retries fall through to unavailable");
103
+ assert.equal(configuredProbeCalls(), 3, "initial attempt + 2 retries before declaring transient failure");
104
+ assert.equal(warnings.length, 1, "exactly one failure warning");
105
+ const msg = warnings[0] ?? "";
106
+ assert.match(
107
+ msg,
108
+ /version check timed out \(host may be under load\); retried 2 times/,
109
+ "transient failure uses the under-load wording"
110
+ );
111
+ assert.doesNotMatch(msg, /configured qmdPath failed/, "must not read as a generic misconfiguration");
112
+ });
113
+
114
+ test("QmdClient preflight: genuine ENOENT fails fast (no retry) with the not-found/not-executable warning", async () => {
115
+ const { internals, warnings, configuredProbeCalls } = harness({
116
+ configuredBehavior: async () => {
117
+ throw spawnENOENT(CONFIGURED_PATH);
118
+ },
119
+ });
120
+
121
+ const ok = await internals.probeCli({ allowAutoUpgrade: false });
122
+
123
+ assert.equal(ok, false, "missing binary is unavailable");
124
+ assert.equal(configuredProbeCalls(), 1, "ENOENT is a hard misconfiguration: never retried");
125
+ assert.equal(warnings.length, 1, "exactly one failure warning");
126
+ const msg = warnings[0] ?? "";
127
+ assert.match(msg, /configured qmdPath not found or not executable/, "missing binary uses the not-found wording");
128
+ assert.match(msg, /spawn .* ENOENT/, "underlying error detail preserved");
129
+ assert.doesNotMatch(msg, /timed out|under load/, "must not read as a transient timeout");
130
+ });
131
+
132
+ test("QmdClient preflight: non-timeout exit-code failure keeps the generic configured-failed message and does not retry", async () => {
133
+ const { internals, warnings, configuredProbeCalls } = harness({
134
+ configuredBehavior: async () => {
135
+ throw new Error("qmd --version exited with code 2");
136
+ },
137
+ });
138
+
139
+ const ok = await internals.probeCli({ allowAutoUpgrade: false });
140
+
141
+ assert.equal(ok, false, "exit-code failure is unavailable");
142
+ assert.equal(configuredProbeCalls(), 1, "non-transient, non-missing failures are not retried");
143
+ assert.equal(warnings.length, 1, "exactly one failure warning");
144
+ const msg = warnings[0] ?? "";
145
+ assert.match(msg, /configured qmdPath failed/, "ambiguous exit-code failures keep the historical generic wording");
146
+ assert.doesNotMatch(
147
+ msg,
148
+ /timed out|under load|not found or not executable/,
149
+ "must not be misclassified as timeout or missing"
150
+ );
151
+ });
152
+
153
+ test("QmdClient preflight: caller abort emits NO warning and does not retry", async () => {
154
+ // The caller cancelled (aborted signal + AbortError). Other QMD paths treat
155
+ // this as non-actionable noise via isCallerCancellation; the configured-path
156
+ // probe must do the same — no under-load warning, no transient retry. #1841.
157
+ const controller = new AbortController();
158
+ controller.abort();
159
+ const { internals, warnings, configuredProbeCalls } = harness({
160
+ configuredBehavior: async () => {
161
+ // A real probe rejects with an AbortError when the caller's signal fires.
162
+ throw abortError("qmd --version aborted");
163
+ },
164
+ });
165
+
166
+ const ok = await internals.probeCli({ allowAutoUpgrade: false, signal: controller.signal });
167
+
168
+ assert.equal(ok, false, "aborted configured probe falls through to unavailable");
169
+ assert.equal(configuredProbeCalls(), 1, "caller cancellation never retries");
170
+ assert.deepEqual(warnings, [], "caller cancellation is silent noise: no operator-facing warning");
171
+ });
172
+
173
+ test("QmdClient preflight: non-zero exit whose stderr says 'timed out'/'not found' is classified 'other' (not transient/missing)", async () => {
174
+ // Mirrors runCommandWithTimeout's non-zero-exit rejection: the child's stderr
175
+ // (here containing "timed out" and "not found") is embedded in the message.
176
+ // A healthy binary can emit those words in error text; they must NOT upgrade
177
+ // the failure to transient/missing. classifyProbeFailure keys on structured
178
+ // signals, so this generic exit-code failure stays 'other'. Issue #1841.
179
+ const { internals, warnings, configuredProbeCalls } = harness({
180
+ configuredBehavior: async () => {
181
+ throw new Error("qmd --version failed (code 2): error: model timed out while loading; config not found");
182
+ },
183
+ });
184
+
185
+ const ok = await internals.probeCli({ allowAutoUpgrade: false });
186
+
187
+ assert.equal(ok, false, "exit-code failure is unavailable");
188
+ assert.equal(configuredProbeCalls(), 1, "embedded-stderr failure is not transient — no retry");
189
+ assert.equal(warnings.length, 1, "exactly one failure warning");
190
+ const msg = warnings[0] ?? "";
191
+ assert.match(msg, /configured qmdPath failed/, "classified as generic 'other'");
192
+ assert.doesNotMatch(
193
+ msg,
194
+ /under load|not found or not executable/,
195
+ "must not misclassify embedded-stderr words as transient/missing"
196
+ );
197
+ });
@@ -0,0 +1,167 @@
1
+ /**
2
+ * QMD version-check PREFLIGHT / probe-classification helpers.
3
+ *
4
+ * Pure, self-contained functions extracted from qmd.ts (issue #1841) so the
5
+ * QmdClient class file stays under the structural-ratchet LOC ceiling. These
6
+ * cover two related concerns of the startup preflight:
7
+ *
8
+ * 1. VERSION parsing/comparison — turning `qmd --version` output into a tuple,
9
+ * comparing against the supported target, and resolving feature capability
10
+ * flags from the parsed version.
11
+ * 2. PROBE failure classification — distinguishing a slow/overloaded binary
12
+ * (transient → retry) from a genuine misconfiguration (ENOENT/EACCES → fail
13
+ * fast) using STRUCTURED signals, never the error message.
14
+ *
15
+ * No behavioral change from qmd.ts round 1 (issue #1841): structured-signal
16
+ * classification (code=ENOENT/EACCES=>missing, timedOut flag=>transient,
17
+ * caller-cancel silenced, non-zero-exit=>other); backoff [300,800].
18
+ */
19
+ import { isAbortError } from "./abort-error.js";
20
+ import type { QmdCapabilities, QmdVersionTuple } from "./qmd.js";
21
+
22
+ // Deadline (ms) for a single `qmd --version` probe.
23
+ export const QMD_PROBE_TIMEOUT_MS = 8_000;
24
+
25
+ // Backoff schedule (ms) for retrying a TRANSIENT (timeout/abort) version-check
26
+ // probe of a CONFIGURED qmdPath before declaring it failed. A binary that
27
+ // resolved and ran before is more likely slow under load than gone, so we retry
28
+ // transient failures but NOT ENOENT/EACCES (hard misconfiguration, fail fast).
29
+ // Length == number of retries after the initial attempt. Issue #1841.
30
+ export const QMD_PROBE_RETRY_BACKOFF_MS = [300, 800];
31
+
32
+ export const QMD_SUPPORTED_VERSION = "2.5.3";
33
+
34
+ /**
35
+ * Classify a `qmd --version` probe failure so the preflight can distinguish a
36
+ * slow/overloaded binary (a genuine deadline timeout) from a real
37
+ * misconfiguration (ENOENT/EACCES — the configured path is missing or not
38
+ * executable). Only transient failures are retried; missing binaries fail fast.
39
+ *
40
+ * Classification keys on STRUCTURED signals, never the error *message*: a
41
+ * non-zero-exit failure embeds the child's stderr in its message, so scanning
42
+ * that text for "abort"/"timed out"/"not found" would misclassify a healthy
43
+ * binary. The structured signals are: `code` (ENOENT/EACCES) for missing; the
44
+ * `timedOut` flag set by `runCommandWithTimeout` for a genuine timeout; an
45
+ * AbortError name for a deadline/abort under load. Issue #1841.
46
+ */
47
+ export type QmdProbeFailureKind = "transient" | "missing" | "other";
48
+ export function classifyProbeFailure(err: unknown): QmdProbeFailureKind {
49
+ if (err && typeof err === "object") {
50
+ // Node spawn failures carry a structured `code`: ENOENT (not found) /
51
+ // EACCES (not executable).
52
+ if ("code" in err) {
53
+ const code = err.code;
54
+ if (code === "ENOENT" || code === "EACCES") return "missing";
55
+ }
56
+ // runCommandWithTimeout flags a genuine deadline breach with `timedOut`.
57
+ if ("timedOut" in err && err.timedOut === true) return "transient";
58
+ }
59
+ // An AbortError reaching here is a deadline/abort under load → transient.
60
+ // (Caller cancellation is intercepted by the retry loop before this runs.)
61
+ if (isAbortError(err)) return "transient";
62
+ // Anything else — including a non-zero exit whose message embeds stderr — is
63
+ // a generic failure: never scan the message to upgrade/downgrade it.
64
+ return "other";
65
+ }
66
+
67
+ export function parseQmdVersion(version: string | null): QmdVersionTuple | null {
68
+ if (!version) return null;
69
+ const match = version.match(/v?(\d{1,10})\.(\d{1,10})\.(\d{1,10})/i);
70
+ if (!match) return null;
71
+ return [
72
+ Number.parseInt(match[1] ?? "0", 10),
73
+ Number.parseInt(match[2] ?? "0", 10),
74
+ Number.parseInt(match[3] ?? "0", 10),
75
+ ];
76
+ }
77
+
78
+ export function parseQmdVersionOutput(stdout: string, stderr: string): string | null {
79
+ const lines = `${stdout}\n${stderr}`
80
+ .split("\n")
81
+ .map((s) => s.trim())
82
+ .filter((s) => s.length > 0);
83
+ if (lines.length === 0) return null;
84
+ const semanticLines = lines.filter((line) => parseQmdVersion(line) !== null);
85
+ if (semanticLines.length === 0) return lines[0] ?? null;
86
+ return semanticLines.find((line) => /\bqmd\b/i.test(line)) ?? semanticLines[0] ?? null;
87
+ }
88
+
89
+ export function compareQmdVersions(left: QmdVersionTuple | null, right: QmdVersionTuple | null): number {
90
+ if (!left && !right) return 0;
91
+ if (!left) return -1;
92
+ if (!right) return 1;
93
+ for (let i = 0; i < 3; i += 1) {
94
+ if ((left[i] ?? 0) > (right[i] ?? 0)) return 1;
95
+ if ((left[i] ?? 0) < (right[i] ?? 0)) return -1;
96
+ }
97
+ return 0;
98
+ }
99
+
100
+ export function versionAtLeast(current: QmdVersionTuple | null, target: QmdVersionTuple): boolean {
101
+ return compareQmdVersions(current, target) >= 0;
102
+ }
103
+
104
+ export function resolveQmdCapabilities(version: string | null): QmdCapabilities {
105
+ const parsedVersion = parseQmdVersion(version);
106
+ const atLeast = (target: QmdVersionTuple): boolean => versionAtLeast(parsedVersion, target);
107
+ return {
108
+ version,
109
+ parsedVersion,
110
+ stableSdk: atLeast([2, 0, 0]),
111
+ unifiedSearch: atLeast([2, 0, 0]),
112
+ getDocumentBody: atLeast([2, 0, 0]),
113
+ maintenanceApi: atLeast([2, 0, 0]),
114
+ legacySkillInstall: atLeast([2, 0, 1]),
115
+ intentHints: atLeast([1, 1, 5]),
116
+ explainTraces: atLeast([1, 1, 2]),
117
+ candidateLimit: atLeast([1, 1, 2]),
118
+ v2McpQueryTool: atLeast([2, 0, 0]),
119
+ structuredSearches: atLeast([2, 0, 0]),
120
+ queryRerankToggle: atLeast([2, 1, 0]),
121
+ chunkStrategy: atLeast([2, 1, 0]),
122
+ qmdBench: atLeast([2, 1, 0]),
123
+ perCollectionModels: atLeast([2, 1, 0]),
124
+ jsonLineNumbers: atLeast([2, 1, 0]),
125
+ editorLinks: atLeast([2, 1, 0]),
126
+ doctor: atLeast([2, 5, 0]),
127
+ versionedSkills: atLeast([2, 5, 0]),
128
+ absoluteSnippetLines: atLeast([2, 5, 0]),
129
+ fullQueryOutput: atLeast([2, 5, 0]),
130
+ forceCpu: atLeast([2, 5, 0]),
131
+ gpuBackendOverride: atLeast([2, 5, 0]),
132
+ embedParallelism: atLeast([2, 5, 0]),
133
+ modelEnvConsistency: atLeast([2, 5, 0]),
134
+ scopedEmbed: atLeast([2, 5, 0]),
135
+ safeStatusDeviceProbe: atLeast([2, 5, 0]),
136
+ mcpIndexSelection: atLeast([2, 5, 0]),
137
+ outputFormatFlag: atLeast([2, 5, 3]),
138
+ };
139
+ }
140
+
141
+ export function shouldAutoUpgradeQmd(
142
+ installedVersion: string | null,
143
+ supportedVersion: string = QMD_SUPPORTED_VERSION
144
+ ): boolean {
145
+ const installed = parseQmdVersion(installedVersion);
146
+ const supported = parseQmdVersion(supportedVersion);
147
+ if (!installed || !supported) return false;
148
+ return compareQmdVersions(installed, supported) < 0;
149
+ }
150
+
151
+ export function getQmdPostInstallProbeTargets(
152
+ qmdPath: string,
153
+ qmdPathSource: "configured" | "auto-path" | "auto-fallback"
154
+ ): Array<{ qmdPath: string; source: "auto-path" | "auto-fallback" }> {
155
+ const targets: Array<{ qmdPath: string; source: "auto-path" | "auto-fallback" }> = [
156
+ { qmdPath: "qmd", source: "auto-path" },
157
+ ];
158
+ const normalizedPath = qmdPath.trim();
159
+ if (qmdPathSource === "auto-fallback" && normalizedPath.length > 0 && normalizedPath !== "qmd") {
160
+ targets.push({ qmdPath: normalizedPath, source: "auto-fallback" });
161
+ }
162
+ return targets;
163
+ }
164
+
165
+ export function qmdVersionToString(version: QmdVersionTuple): string {
166
+ return `${version[0]}.${version[1]}.${version[2]}`;
167
+ }