@remnic/core 9.3.719 → 9.3.721

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 (387) hide show
  1. package/dist/access-boundary.d.ts +10 -7
  2. package/dist/access-boundary.js +37 -34
  3. package/dist/access-cli.js +71 -66
  4. package/dist/access-cli.js.map +1 -1
  5. package/dist/access-http.d.ts +11 -8
  6. package/dist/access-http.js +44 -41
  7. package/dist/access-mcp.d.ts +11 -8
  8. package/dist/access-mcp.js +40 -37
  9. package/dist/access-operations-batch.js +38 -35
  10. package/dist/access-operations.d.ts +11 -8
  11. package/dist/access-operations.js +39 -36
  12. package/dist/access-schema.d.ts +19 -19
  13. package/dist/access-schema.js +3 -3
  14. package/dist/{access-service-CZbnt0t2.d.ts → access-service-Dj3I-VBS.d.ts} +3 -3
  15. package/dist/access-service.d.ts +11 -8
  16. package/dist/access-service.js +36 -33
  17. package/dist/access-surface-catalog.d.ts +10 -7
  18. package/dist/action-confidence.d.ts +1 -1
  19. package/dist/active-memory-bridge.d.ts +1 -1
  20. package/dist/active-recall.d.ts +1 -1
  21. package/dist/active-recall.js +4 -2
  22. package/dist/active-recall.js.map +1 -1
  23. package/dist/adapters/index.js +7 -7
  24. package/dist/adapters/registry.js +3 -3
  25. package/dist/{auto-sync-24NNS3GU.js → auto-sync-F3KCSQFJ.js} +5 -5
  26. package/dist/behavior-learner.d.ts +1 -1
  27. package/dist/behavior-signals.d.ts +1 -1
  28. package/dist/bootstrap.d.ts +11 -8
  29. package/dist/briefing.d.ts +1 -1
  30. package/dist/briefing.js +12 -10
  31. package/dist/buffer-surprise-report.d.ts +1 -1
  32. package/dist/buffer.d.ts +1 -1
  33. package/dist/calibration.d.ts +1 -1
  34. package/dist/calibration.js +2 -2
  35. package/dist/capabilities.d.ts +30 -2
  36. package/dist/capabilities.js +3 -1
  37. package/dist/{catalog-CEYlMs9X.d.ts → catalog-F-OEVbRQ.d.ts} +1 -1
  38. package/dist/causal-behavior.d.ts +1 -1
  39. package/dist/causal-behavior.js +2 -2
  40. package/dist/causal-chain.js +2 -2
  41. package/dist/causal-consolidation.d.ts +1 -1
  42. package/dist/causal-consolidation.js +15 -13
  43. package/dist/causal-consolidation.js.map +1 -1
  44. package/dist/causal-retrieval.js +2 -2
  45. package/dist/causal-trajectory-graph.d.ts +4 -0
  46. package/dist/causal-trajectory-graph.js +2 -2
  47. package/dist/causal-trajectory.js +1 -1
  48. package/dist/{chunk-TH7WMHVK.js → chunk-3NN73CM7.js} +2 -2
  49. package/dist/{chunk-A5TUPZ3N.js → chunk-4SF232E6.js} +60 -30
  50. package/dist/chunk-4SF232E6.js.map +1 -0
  51. package/dist/{chunk-DGTDRIUO.js → chunk-4T3BMHKD.js} +2 -2
  52. package/dist/{chunk-EH27LHML.js → chunk-5O42UKXI.js} +2 -2
  53. package/dist/{chunk-KRGC4EDX.js → chunk-6FAMMBQ3.js} +2 -2
  54. package/dist/{chunk-FEB7W5JG.js → chunk-6OIDDKAA.js} +27 -6
  55. package/dist/chunk-6OIDDKAA.js.map +1 -0
  56. package/dist/{chunk-67HG7KFE.js → chunk-6YKIYQU7.js} +2 -2
  57. package/dist/{chunk-LXSG4L55.js → chunk-7CTNUVHZ.js} +2 -2
  58. package/dist/{chunk-UCJ5CDTK.js → chunk-A5XNBK2D.js} +22 -14
  59. package/dist/chunk-A5XNBK2D.js.map +1 -0
  60. package/dist/{chunk-7EEVXW7X.js → chunk-A7TJZLPK.js} +2 -2
  61. package/dist/{chunk-P5IPFZVH.js → chunk-B7WISIAJ.js} +5 -5
  62. package/dist/chunk-BCDKBDSY.js +173 -0
  63. package/dist/chunk-BCDKBDSY.js.map +1 -0
  64. package/dist/{chunk-JAKYZGV4.js → chunk-BXEOHV7K.js} +3 -3
  65. package/dist/chunk-C4X4QGIO.js +60 -0
  66. package/dist/chunk-C4X4QGIO.js.map +1 -0
  67. package/dist/{chunk-S3OPGGZB.js → chunk-D7EBTQ24.js} +2 -2
  68. package/dist/{chunk-IO5NQEGZ.js → chunk-DKT3X5C3.js} +2 -2
  69. package/dist/{chunk-PBH4JUAB.js → chunk-DVRHLN2O.js} +18 -3
  70. package/dist/chunk-DVRHLN2O.js.map +1 -0
  71. package/dist/{chunk-S35FX6MU.js → chunk-EBDA5TYA.js} +2 -2
  72. package/dist/{chunk-2PTBLS2S.js → chunk-EDOQM4E4.js} +2 -2
  73. package/dist/{chunk-YTZLBBEB.js → chunk-EEJMD2L2.js} +3 -3
  74. package/dist/{chunk-YGKUAX2B.js → chunk-EHQ5HO5T.js} +11 -11
  75. package/dist/chunk-EHQ5HO5T.js.map +1 -0
  76. package/dist/{chunk-JIZGYQYH.js → chunk-EM2LDXJ2.js} +4 -4
  77. package/dist/{chunk-YYOK4GVF.js → chunk-EVPNWN7Z.js} +10 -10
  78. package/dist/{chunk-HLIR3RJ7.js → chunk-EXUCUPV2.js} +2 -2
  79. package/dist/{chunk-F3PVXKHP.js → chunk-GDUOCHCD.js} +27 -21
  80. package/dist/chunk-GDUOCHCD.js.map +1 -0
  81. package/dist/{chunk-L5ASZA2F.js → chunk-GJSL4WG2.js} +17 -2
  82. package/dist/chunk-GJSL4WG2.js.map +1 -0
  83. package/dist/{chunk-2SXMCQEQ.js → chunk-GZQXOQQ2.js} +3 -3
  84. package/dist/{chunk-2KAYTPPT.js → chunk-HCVWF657.js} +12 -12
  85. package/dist/{chunk-N3DUH6OO.js → chunk-ILFSEIRT.js} +1 -1
  86. package/dist/chunk-ILFSEIRT.js.map +1 -0
  87. package/dist/{chunk-4KZGLDJJ.js → chunk-J3JPB7ZI.js} +2 -2
  88. package/dist/{chunk-3JJWNZTT.js → chunk-L2B3EE7B.js} +102 -42
  89. package/dist/chunk-L2B3EE7B.js.map +1 -0
  90. package/dist/{chunk-NFRBADNR.js → chunk-LA3HZKES.js} +610 -369
  91. package/dist/chunk-LA3HZKES.js.map +1 -0
  92. package/dist/{chunk-LQ6JI4VH.js → chunk-LD53WPMU.js} +4 -4
  93. package/dist/{chunk-URM3HW2R.js → chunk-LLUGAZ3B.js} +2 -2
  94. package/dist/{chunk-RVLB4E6N.js → chunk-MDBZTH7N.js} +5 -32
  95. package/dist/chunk-MDBZTH7N.js.map +1 -0
  96. package/dist/{chunk-U5H7WHTS.js → chunk-N77ELJ2D.js} +4 -4
  97. package/dist/{chunk-2VHMVWPU.js → chunk-NXRUUDUF.js} +2 -2
  98. package/dist/{chunk-DIFU33GU.js → chunk-OHTOPFRF.js} +2 -2
  99. package/dist/{chunk-74MYJSKB.js → chunk-ORAK3LNZ.js} +2 -2
  100. package/dist/{chunk-REWQAFSA.js → chunk-PD42EYQN.js} +2 -2
  101. package/dist/chunk-PHK3HARR.js +32 -0
  102. package/dist/chunk-PHK3HARR.js.map +1 -0
  103. package/dist/{chunk-773DPI6T.js → chunk-PKMQK2WY.js} +4 -4
  104. package/dist/{chunk-72VDBALG.js → chunk-QD6QP5P6.js} +63 -54
  105. package/dist/chunk-QD6QP5P6.js.map +1 -0
  106. package/dist/{chunk-CTOQEZSN.js → chunk-QR67FU5G.js} +2 -2
  107. package/dist/{chunk-AU7Q3LSC.js → chunk-RKNJBZ55.js} +7 -7
  108. package/dist/{chunk-YI7RZO7Y.js → chunk-SQNED75R.js} +1 -1
  109. package/dist/{chunk-5CEJH5ZN.js → chunk-TAPSNIMA.js} +4 -4
  110. package/dist/{chunk-IF362TF6.js → chunk-TQT3USCG.js} +2 -2
  111. package/dist/chunk-TQT3USCG.js.map +1 -0
  112. package/dist/{chunk-5RHM43BJ.js → chunk-TXQCG6SE.js} +3 -3
  113. package/dist/{chunk-ANI5DUAE.js → chunk-UIXXE6NI.js} +7 -7
  114. package/dist/{chunk-2COL35PK.js → chunk-UO5ZHIIH.js} +2 -2
  115. package/dist/{chunk-OJU73BUX.js → chunk-VCLGUIFM.js} +2 -2
  116. package/dist/{chunk-5J4OL7TJ.js → chunk-VUA4S7P6.js} +4 -4
  117. package/dist/{chunk-A66YRSOS.js → chunk-WQYSDPMA.js} +2 -2
  118. package/dist/chunk-XGTBNVIU.js +79 -0
  119. package/dist/chunk-XGTBNVIU.js.map +1 -0
  120. package/dist/{chunk-OZ2KQ7HF.js → chunk-YEE5KJQQ.js} +2 -2
  121. package/dist/chunk-YN2UN7N3.js +217 -0
  122. package/dist/chunk-YN2UN7N3.js.map +1 -0
  123. package/dist/{chunk-WRFKZEO6.js → chunk-YQGHW5ML.js} +4 -4
  124. package/dist/{chunk-APUJIMDM.js → chunk-Z4RU5VPM.js} +2 -2
  125. package/dist/{chunk-DCWIQFNA.js → chunk-ZUDM75KG.js} +4 -4
  126. package/dist/{cli-1orcdXOY.d.ts → cli-P_GaYcCY.d.ts} +3 -3
  127. package/dist/cli.d.ts +9 -6
  128. package/dist/cli.js +67 -63
  129. package/dist/compounding/engine.d.ts +1 -1
  130. package/dist/compounding/engine.js +12 -10
  131. package/dist/compounding/preference-consolidator.d.ts +1 -1
  132. package/dist/compression-optimizer.d.ts +1 -1
  133. package/dist/config.d.ts +1 -1
  134. package/dist/config.js +4 -2
  135. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  136. package/dist/connectors/codex-materialize-runner.js +12 -10
  137. package/dist/connectors/codex-materialize.d.ts +1 -1
  138. package/dist/connectors/index.d.ts +1 -1
  139. package/dist/connectors/index.js +13 -11
  140. package/dist/consolidation-provenance-check.d.ts +1 -1
  141. package/dist/consolidation-undo.d.ts +1 -1
  142. package/dist/contradiction/index.d.ts +1 -1
  143. package/dist/conversation-index/backend.d.ts +1 -1
  144. package/dist/conversation-index/backend.js +2 -2
  145. package/dist/conversation-index/chunker.d.ts +1 -1
  146. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  147. package/dist/conversation-index/indexer.d.ts +1 -1
  148. package/dist/conversation-index/search.d.ts +1 -1
  149. package/dist/day-summary.d.ts +1 -1
  150. package/dist/delinearize.d.ts +1 -1
  151. package/dist/direct-answer-wiring.d.ts +1 -1
  152. package/dist/direct-answer.d.ts +1 -1
  153. package/dist/embedding-fallback.d.ts +1 -1
  154. package/dist/enrichment/index.d.ts +1 -1
  155. package/dist/entity-retrieval.d.ts +1 -1
  156. package/dist/entity-retrieval.js +12 -10
  157. package/dist/entity-schema.d.ts +1 -1
  158. package/dist/explicit-capture.d.ts +11 -8
  159. package/dist/extraction-faithfulness.d.ts +8 -2
  160. package/dist/extraction-faithfulness.js +4 -3
  161. package/dist/extraction-judge-telemetry.d.ts +1 -1
  162. package/dist/extraction-judge-training.d.ts +1 -1
  163. package/dist/extraction-judge.d.ts +1 -1
  164. package/dist/extraction-judge.js +3 -3
  165. package/dist/extraction.d.ts +1 -1
  166. package/dist/extraction.js +5 -4
  167. package/dist/faithfulness-config.d.ts +55 -0
  168. package/dist/faithfulness-config.js +11 -0
  169. package/dist/faithfulness-config.js.map +1 -0
  170. package/dist/fallback-llm.d.ts +1 -1
  171. package/dist/fallback-llm.js +2 -2
  172. package/dist/{forget-U66KPDPY.js → forget-OEE5OELK.js} +2 -2
  173. package/dist/graph-dashboard-diff.d.ts +4 -0
  174. package/dist/graph-dashboard-key.d.ts +4 -0
  175. package/dist/graph-dashboard-parser.d.ts +4 -0
  176. package/dist/{graph-edge-decay-JLOF3YW4.js → graph-edge-decay-QPRJQ7DL.js} +3 -3
  177. package/dist/graph-edge-reinforcement.d.ts +4 -0
  178. package/dist/graph-snapshot.d.ts +7 -3
  179. package/dist/graph-snapshot.js +3 -3
  180. package/dist/graph.d.ts +7 -1
  181. package/dist/graph.js +2 -2
  182. package/dist/identity-continuity.d.ts +1 -1
  183. package/dist/importance.d.ts +1 -1
  184. package/dist/index.d.ts +571 -570
  185. package/dist/index.js +104 -99
  186. package/dist/index.js.map +1 -1
  187. package/dist/intent.d.ts +1 -1
  188. package/dist/lcm/engine.d.ts +1 -1
  189. package/dist/lcm/engine.js +2 -2
  190. package/dist/lcm/index.d.ts +1 -1
  191. package/dist/lcm/index.js +4 -4
  192. package/dist/lcm/tools.d.ts +1 -1
  193. package/dist/lifecycle.d.ts +1 -1
  194. package/dist/live-connectors-runner.d.ts +1 -1
  195. package/dist/local-llm.d.ts +1 -1
  196. package/dist/local-model-endpoint.d.ts +96 -0
  197. package/dist/local-model-endpoint.js +12 -0
  198. package/dist/local-model-endpoint.js.map +1 -0
  199. package/dist/maintenance/memory-governance.d.ts +1 -1
  200. package/dist/maintenance/memory-governance.js +12 -10
  201. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +12 -10
  202. package/dist/maintenance/rebuild-memory-projection.js +13 -11
  203. package/dist/mcp-memory-inspector-app.d.ts +11 -8
  204. package/dist/memory-action-policy.d.ts +1 -1
  205. package/dist/memory-cache.d.ts +1 -1
  206. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  207. package/dist/memory-projection-store.d.ts +1 -1
  208. package/dist/memory-provenance.d.ts +1 -1
  209. package/dist/memory-worth-outcomes.d.ts +1 -1
  210. package/dist/models-json.d.ts +1 -1
  211. package/dist/namespaces/migrate.d.ts +2 -2
  212. package/dist/namespaces/migrate.js +22 -20
  213. package/dist/namespaces/principal.d.ts +1 -1
  214. package/dist/namespaces/search.d.ts +1 -1
  215. package/dist/namespaces/search.js +9 -9
  216. package/dist/namespaces/storage.d.ts +2 -2
  217. package/dist/namespaces/storage.js +12 -10
  218. package/dist/native-knowledge.d.ts +1 -1
  219. package/dist/operator-toolkit.d.ts +3 -2
  220. package/dist/operator-toolkit.js +32 -28
  221. package/dist/orchestration/maintenance.d.ts +2 -2
  222. package/dist/orchestration/maintenance.js +14 -12
  223. package/dist/{orchestrator-kOxredSr.d.ts → orchestrator-f5xv19kp.d.ts} +178 -81
  224. package/dist/orchestrator.d.ts +8 -5
  225. package/dist/orchestrator.js +56 -52
  226. package/dist/patterns-cli.d.ts +1 -1
  227. package/dist/policy-runtime.d.ts +1 -1
  228. package/dist/provenance.d.ts +1 -1
  229. package/dist/provenance.js +2 -1
  230. package/dist/qmd-recall-cache.d.ts +1 -1
  231. package/dist/qmd.d.ts +1 -1
  232. package/dist/recall-disclosure-escalation.d.ts +1 -1
  233. package/dist/recall-explain-renderer.d.ts +1 -1
  234. package/dist/recall-explain-renderer.js +3 -3
  235. package/dist/recall-planner-llm.d.ts +1 -1
  236. package/dist/recall-planner-llm.js +2 -2
  237. package/dist/recall-state.d.ts +1 -1
  238. package/dist/recall-tag-filter.d.ts +1 -1
  239. package/dist/recall-xray-cli.d.ts +1 -1
  240. package/dist/recall-xray-cli.js +4 -4
  241. package/dist/recall-xray-renderer.d.ts +1 -1
  242. package/dist/recall-xray-renderer.js +3 -3
  243. package/dist/recall-xray.d.ts +18 -1
  244. package/dist/recall-xray.js +2 -2
  245. package/dist/resolve-auth-token.d.ts +1 -1
  246. package/dist/resume-bundles.js +5 -3
  247. package/dist/retrieval-agents.d.ts +1 -1
  248. package/dist/retrieval-tiers.d.ts +1 -1
  249. package/dist/routing/engine.d.ts +1 -1
  250. package/dist/routing/store.d.ts +1 -1
  251. package/dist/search/embed-helper.d.ts +1 -1
  252. package/dist/search/factory.d.ts +1 -1
  253. package/dist/search/factory.js +8 -8
  254. package/dist/search/index.d.ts +1 -1
  255. package/dist/search/index.js +14 -14
  256. package/dist/search/lancedb-backend.d.ts +1 -1
  257. package/dist/search/lancedb-backend.js +2 -2
  258. package/dist/search/meilisearch-backend.d.ts +1 -1
  259. package/dist/search/meilisearch-backend.js +2 -2
  260. package/dist/search/noop-backend.d.ts +1 -1
  261. package/dist/search/orama-backend.d.ts +1 -1
  262. package/dist/search/orama-backend.js +2 -2
  263. package/dist/search/port.d.ts +1 -1
  264. package/dist/search/remote-backend.d.ts +1 -1
  265. package/dist/{semantic-consolidation-B58v3S-a.d.ts → semantic-consolidation-BQm1e1Pn.d.ts} +1 -1
  266. package/dist/semantic-consolidation.d.ts +2 -2
  267. package/dist/semantic-consolidation.js +13 -11
  268. package/dist/semantic-rule-promotion.js +12 -10
  269. package/dist/semantic-rule-verifier.d.ts +1 -1
  270. package/dist/semantic-rule-verifier.js +12 -10
  271. package/dist/session-observer-bands.d.ts +1 -1
  272. package/dist/session-observer-state.d.ts +1 -1
  273. package/dist/shared-context/manager.d.ts +1 -1
  274. package/dist/signal.d.ts +1 -1
  275. package/dist/storage.d.ts +45 -1
  276. package/dist/storage.js +11 -9
  277. package/dist/summarizer.d.ts +1 -1
  278. package/dist/summarizer.js +3 -3
  279. package/dist/summary-snapshot.d.ts +1 -1
  280. package/dist/temporal-supersession.d.ts +1 -1
  281. package/dist/temporal-validity.d.ts +1 -1
  282. package/dist/threading.d.ts +1 -1
  283. package/dist/tier-migration.d.ts +1 -1
  284. package/dist/tier-routing.d.ts +1 -1
  285. package/dist/topics.d.ts +1 -1
  286. package/dist/transcript.d.ts +1 -1
  287. package/dist/transfer/backup.js +2 -2
  288. package/dist/transfer/capsule-export.js +2 -2
  289. package/dist/transfer/capsule-import.js +1 -1
  290. package/dist/trust-score-stage.d.ts +178 -0
  291. package/dist/trust-score-stage.js +18 -0
  292. package/dist/trust-score-stage.js.map +1 -0
  293. package/dist/trust-score.d.ts +164 -0
  294. package/dist/trust-score.js +22 -0
  295. package/dist/trust-score.js.map +1 -0
  296. package/dist/{types-ACmDOScJ.d.ts → types-DwO1Ot4n.d.ts} +56 -2
  297. package/dist/types.d.ts +1 -1
  298. package/dist/types.js +1 -1
  299. package/dist/utility-runtime.d.ts +1 -1
  300. package/dist/verified-recall.js +12 -10
  301. package/package.json +2 -2
  302. package/src/access-service.ts +8 -5
  303. package/src/capabilities-cluster-b.test.ts +78 -0
  304. package/src/capabilities.test.ts +11 -1
  305. package/src/capabilities.ts +58 -0
  306. package/src/cli/research-status-commands.ts +7 -3
  307. package/src/cli.ts +11 -6
  308. package/src/config.ts +60 -30
  309. package/src/extraction-faithfulness.test.ts +175 -0
  310. package/src/extraction-faithfulness.ts +53 -1
  311. package/src/faithfulness-config.test.ts +96 -0
  312. package/src/faithfulness-config.ts +138 -0
  313. package/src/graph-snapshot.ts +3 -3
  314. package/src/graph.ts +8 -7
  315. package/src/lifecycle/tombstones.test.ts +242 -1
  316. package/src/lifecycle/tombstones.ts +157 -70
  317. package/src/local-model-endpoint.test.ts +155 -0
  318. package/src/local-model-endpoint.ts +189 -0
  319. package/src/operator-toolkit.ts +24 -7
  320. package/src/orchestration/tier-migration-coordinator.ts +286 -0
  321. package/src/orchestrator.ts +331 -222
  322. package/src/recall-xray.test.ts +62 -0
  323. package/src/recall-xray.ts +34 -0
  324. package/src/trust-score-recall-paths.test.ts +157 -0
  325. package/src/trust-score-stage.test.ts +350 -0
  326. package/src/trust-score-stage.ts +398 -0
  327. package/src/trust-score.test.ts +282 -0
  328. package/src/trust-score.ts +449 -0
  329. package/src/types.ts +61 -1
  330. package/dist/chunk-3JJWNZTT.js.map +0 -1
  331. package/dist/chunk-72VDBALG.js.map +0 -1
  332. package/dist/chunk-A5TUPZ3N.js.map +0 -1
  333. package/dist/chunk-F3PVXKHP.js.map +0 -1
  334. package/dist/chunk-FEB7W5JG.js.map +0 -1
  335. package/dist/chunk-IF362TF6.js.map +0 -1
  336. package/dist/chunk-L5ASZA2F.js.map +0 -1
  337. package/dist/chunk-N3DUH6OO.js.map +0 -1
  338. package/dist/chunk-NFRBADNR.js.map +0 -1
  339. package/dist/chunk-PBH4JUAB.js.map +0 -1
  340. package/dist/chunk-RVLB4E6N.js.map +0 -1
  341. package/dist/chunk-UCJ5CDTK.js.map +0 -1
  342. package/dist/chunk-YGKUAX2B.js.map +0 -1
  343. /package/dist/{auto-sync-24NNS3GU.js.map → auto-sync-F3KCSQFJ.js.map} +0 -0
  344. /package/dist/{chunk-TH7WMHVK.js.map → chunk-3NN73CM7.js.map} +0 -0
  345. /package/dist/{chunk-DGTDRIUO.js.map → chunk-4T3BMHKD.js.map} +0 -0
  346. /package/dist/{chunk-EH27LHML.js.map → chunk-5O42UKXI.js.map} +0 -0
  347. /package/dist/{chunk-KRGC4EDX.js.map → chunk-6FAMMBQ3.js.map} +0 -0
  348. /package/dist/{chunk-67HG7KFE.js.map → chunk-6YKIYQU7.js.map} +0 -0
  349. /package/dist/{chunk-LXSG4L55.js.map → chunk-7CTNUVHZ.js.map} +0 -0
  350. /package/dist/{chunk-7EEVXW7X.js.map → chunk-A7TJZLPK.js.map} +0 -0
  351. /package/dist/{chunk-P5IPFZVH.js.map → chunk-B7WISIAJ.js.map} +0 -0
  352. /package/dist/{chunk-JAKYZGV4.js.map → chunk-BXEOHV7K.js.map} +0 -0
  353. /package/dist/{chunk-S3OPGGZB.js.map → chunk-D7EBTQ24.js.map} +0 -0
  354. /package/dist/{chunk-IO5NQEGZ.js.map → chunk-DKT3X5C3.js.map} +0 -0
  355. /package/dist/{chunk-S35FX6MU.js.map → chunk-EBDA5TYA.js.map} +0 -0
  356. /package/dist/{chunk-2PTBLS2S.js.map → chunk-EDOQM4E4.js.map} +0 -0
  357. /package/dist/{chunk-YTZLBBEB.js.map → chunk-EEJMD2L2.js.map} +0 -0
  358. /package/dist/{chunk-JIZGYQYH.js.map → chunk-EM2LDXJ2.js.map} +0 -0
  359. /package/dist/{chunk-YYOK4GVF.js.map → chunk-EVPNWN7Z.js.map} +0 -0
  360. /package/dist/{chunk-HLIR3RJ7.js.map → chunk-EXUCUPV2.js.map} +0 -0
  361. /package/dist/{chunk-2SXMCQEQ.js.map → chunk-GZQXOQQ2.js.map} +0 -0
  362. /package/dist/{chunk-2KAYTPPT.js.map → chunk-HCVWF657.js.map} +0 -0
  363. /package/dist/{chunk-4KZGLDJJ.js.map → chunk-J3JPB7ZI.js.map} +0 -0
  364. /package/dist/{chunk-LQ6JI4VH.js.map → chunk-LD53WPMU.js.map} +0 -0
  365. /package/dist/{chunk-URM3HW2R.js.map → chunk-LLUGAZ3B.js.map} +0 -0
  366. /package/dist/{chunk-U5H7WHTS.js.map → chunk-N77ELJ2D.js.map} +0 -0
  367. /package/dist/{chunk-2VHMVWPU.js.map → chunk-NXRUUDUF.js.map} +0 -0
  368. /package/dist/{chunk-DIFU33GU.js.map → chunk-OHTOPFRF.js.map} +0 -0
  369. /package/dist/{chunk-74MYJSKB.js.map → chunk-ORAK3LNZ.js.map} +0 -0
  370. /package/dist/{chunk-REWQAFSA.js.map → chunk-PD42EYQN.js.map} +0 -0
  371. /package/dist/{chunk-773DPI6T.js.map → chunk-PKMQK2WY.js.map} +0 -0
  372. /package/dist/{chunk-CTOQEZSN.js.map → chunk-QR67FU5G.js.map} +0 -0
  373. /package/dist/{chunk-AU7Q3LSC.js.map → chunk-RKNJBZ55.js.map} +0 -0
  374. /package/dist/{chunk-YI7RZO7Y.js.map → chunk-SQNED75R.js.map} +0 -0
  375. /package/dist/{chunk-5CEJH5ZN.js.map → chunk-TAPSNIMA.js.map} +0 -0
  376. /package/dist/{chunk-5RHM43BJ.js.map → chunk-TXQCG6SE.js.map} +0 -0
  377. /package/dist/{chunk-ANI5DUAE.js.map → chunk-UIXXE6NI.js.map} +0 -0
  378. /package/dist/{chunk-2COL35PK.js.map → chunk-UO5ZHIIH.js.map} +0 -0
  379. /package/dist/{chunk-OJU73BUX.js.map → chunk-VCLGUIFM.js.map} +0 -0
  380. /package/dist/{chunk-5J4OL7TJ.js.map → chunk-VUA4S7P6.js.map} +0 -0
  381. /package/dist/{chunk-A66YRSOS.js.map → chunk-WQYSDPMA.js.map} +0 -0
  382. /package/dist/{chunk-OZ2KQ7HF.js.map → chunk-YEE5KJQQ.js.map} +0 -0
  383. /package/dist/{chunk-WRFKZEO6.js.map → chunk-YQGHW5ML.js.map} +0 -0
  384. /package/dist/{chunk-APUJIMDM.js.map → chunk-Z4RU5VPM.js.map} +0 -0
  385. /package/dist/{chunk-DCWIQFNA.js.map → chunk-ZUDM75KG.js.map} +0 -0
  386. /package/dist/{forget-U66KPDPY.js.map → forget-OEE5OELK.js.map} +0 -0
  387. /package/dist/{graph-edge-decay-JLOF3YW4.js.map → graph-edge-decay-QPRJQ7DL.js.map} +0 -0
@@ -0,0 +1,398 @@
1
+ /**
2
+ * trust-score-stage.ts — signal adapters + recall pipeline stage (issue #1577 PR 2).
3
+ *
4
+ * This module sits between the pure {@link computeTrustScore} scorer and the
5
+ * recall pipeline. It owns TWO jobs:
6
+ *
7
+ * 1. **Signal adapters** — read each trust signal via existing readers only
8
+ * (`buildMemoryWorthCounterMap`-style frontmatter scan, faithfulness /
9
+ * provenance / sources frontmatter from #1575/#1576). No new disk format,
10
+ * no new write path. Expensive / optional signals (belief-ledger domain
11
+ * calibration, contradiction review queue) are extension points that
12
+ * return `undefined` when unwired — the scorer degrades to neutral for
13
+ * any absent signal (rule 34).
14
+ *
15
+ * 2. **The recall stage** — apply `trustMultiplier(trust.score)` to each
16
+ * candidate, exclude `quarantine`-band items from injection, and surface
17
+ * the per-result `{ score, band, components }` so X-ray and the epistemic
18
+ * renderer can explain the basis.
19
+ *
20
+ * Mutual exclusion with the Memory Worth filter (rule 39 — one multiplier, one
21
+ * application point): when this stage runs, the standalone memory-worth filter
22
+ * MUST NOT also run. The orchestrator enforces that at the wiring site
23
+ * (`if (caps.recallTrustScore) … else if (caps.recallMemoryWorthFilter) …`);
24
+ * the `double-multiplier` test below pins it structurally.
25
+ */
26
+
27
+ import { computeMemoryWorth } from "./memory-worth.js";
28
+ import {
29
+ computeTrustScore,
30
+ trustMultiplier,
31
+ type TrustBand,
32
+ type TrustScoreComponent,
33
+ type TrustScoreResult,
34
+ type TrustSignals,
35
+ type TrustWeights,
36
+ } from "./trust-score.js";
37
+ import type { FaithfulnessFrontmatter, ProvenanceSource } from "./types.js";
38
+
39
+ /**
40
+ * Frontmatter projection the adapter reads. Kept narrow + structural so it can
41
+ * be fed from a `MemoryFrontmatter`, a bench fixture, or a test stub without
42
+ * pulling the full frontmatter type graph into the stage module.
43
+ */
44
+ export interface TrustFrontmatterProjection {
45
+ mw_success?: number;
46
+ mw_fail?: number;
47
+ lastAccessed?: string | null;
48
+ provenance?: "verified" | "unverified" | "none";
49
+ faithfulness?: FaithfulnessFrontmatter;
50
+ sources?: ProvenanceSource[];
51
+ /** Optional ISO creation/observation timestamp used for recency. */
52
+ observedAt?: string;
53
+ }
54
+
55
+ /**
56
+ * Build a `path → TrustSignals` map from an in-memory frontmatter scan. The
57
+ * caller passes the memories already read for the memory-worth counter map so
58
+ * we do not re-read the namespace. Pure: no I/O, takes `now` explicitly.
59
+ */
60
+ export function buildTrustSignalMap(
61
+ memories: ReadonlyArray<{ path: string; frontmatter: TrustFrontmatterProjection }>,
62
+ now: Date,
63
+ options: { recencyHalfLifeDays?: number } = {},
64
+ ): Map<string, TrustSignals> {
65
+ const map = new Map<string, TrustSignals>();
66
+ const nowMs = now.getTime();
67
+ const nowUsable = Number.isFinite(nowMs);
68
+ for (const m of memories) {
69
+ const fm = m.frontmatter;
70
+ const signals: TrustSignals = {};
71
+
72
+ // Memory worth — reuse the exact Laplace computation the standalone filter
73
+ // uses so the two never disagree on the same counters.
74
+ if (fm.mw_success !== undefined || fm.mw_fail !== undefined) {
75
+ // Pass the half-life through so outcome counters decay at the same
76
+ // rate as the standalone memory-worth filter (review P2: TrustScore
77
+ // subsumes the filter, so decay must not be lost). Convert days → ms.
78
+ const halfLifeMs =
79
+ options.recencyHalfLifeDays !== undefined
80
+ ? options.recencyHalfLifeDays * 86_400_000
81
+ : undefined;
82
+ const worth = computeMemoryWorth({
83
+ mw_success: fm.mw_success,
84
+ mw_fail: fm.mw_fail,
85
+ lastAccessed: fm.lastAccessed,
86
+ now,
87
+ halfLifeMs,
88
+ });
89
+ signals.memoryWorth = { score: worth.score, confidence: worth.confidence };
90
+ }
91
+
92
+ if (fm.provenance !== undefined) signals.provenance = fm.provenance;
93
+ if (fm.faithfulness !== undefined) {
94
+ // `skipped_no_span` (legacy fact lacking a verified source span) carries
95
+ // no entailment signal — collapse it to `unchecked` so the scorer treats
96
+ // it as the neutral "no verdict" case rather than a hard negative.
97
+ signals.faithfulness =
98
+ fm.faithfulness.verdict === "skipped_no_span" ? "unchecked" : fm.faithfulness.verdict;
99
+ }
100
+
101
+ // Corroboration = distinct source sessions/turns backing the fact.
102
+ if (Array.isArray(fm.sources) && fm.sources.length > 0) {
103
+ const distinct = new Set(
104
+ fm.sources.map((s) => `${s.sessionKey ?? ""}|${s.turnId ?? ""}`),
105
+ );
106
+ signals.corroborationCount = distinct.size;
107
+ }
108
+
109
+ // Recency: prefer an explicit observation timestamp; fall back to
110
+ // lastAccessed. Days-old vs the per-category half-life the caller supplies.
111
+ const ts = fm.observedAt ?? fm.lastAccessed;
112
+ if (nowUsable && typeof ts === "string" && ts.length > 0) {
113
+ const parsed = Date.parse(ts);
114
+ if (Number.isFinite(parsed)) {
115
+ const ageDays = Math.max(0, (nowMs - parsed) / (24 * 60 * 60 * 1000));
116
+ signals.ageDays = ageDays;
117
+ if (options.recencyHalfLifeDays !== undefined) {
118
+ signals.recencyHalfLifeDays = options.recencyHalfLifeDays;
119
+ }
120
+ }
121
+ }
122
+
123
+ // Only record an entry when at least one signal is present — a fully-empty
124
+ // entry would score neutral anyway, and skipping keeps the map small.
125
+ if (Object.keys(signals).length > 0) {
126
+ map.set(m.path, signals);
127
+ }
128
+ }
129
+ return map;
130
+ }
131
+
132
+ /** A scored recall candidate (mirrors `MemoryWorthFilterCandidate`). */
133
+ export interface TrustStageCandidate {
134
+ path: string;
135
+ score: number;
136
+ }
137
+
138
+ /** One candidate after the TrustScore stage. */
139
+ export interface TrustStageResultItem {
140
+ path: string;
141
+ /** Final score after the trust multiplier is applied. */
142
+ score: number;
143
+ /** The untouched input score — for telemetry / X-ray. */
144
+ originalScore: number;
145
+ /** The multiplier applied (exactly `1.0` for neutral / quarantined-kept). */
146
+ multiplier: number;
147
+ /** The full TrustScore result for X-ray + epistemic rendering. */
148
+ trust: TrustScoreResult;
149
+ /** `true` when the item was excluded from injection by quarantine. */
150
+ quarantined: boolean;
151
+ }
152
+
153
+ export interface TrustStageOptions {
154
+ /** `path → TrustSignals`. Candidates absent from the map score neutral. */
155
+ signals: ReadonlyMap<string, TrustSignals>;
156
+ weights?: TrustWeights;
157
+ /** Multiplier bounds; see {@link trustMultiplier}. */
158
+ minMultiplier?: number;
159
+ maxMultiplier?: number;
160
+ /**
161
+ * When `true` (default), `quarantine`-band items are excluded from the
162
+ * returned (injected) list but still appear in {@link TrustStageOutput.all}
163
+ * with `quarantined: true` so X-ray can surface them with a reason.
164
+ * Only effective when the stage is active at all.
165
+ */
166
+ quarantine?: boolean;
167
+ /**
168
+ * Re-sort admitted candidates by descending multiplied score. Default `true`.
169
+ * When `false`, admitted items keep input order; quarantined items are still
170
+ * separated out.
171
+ */
172
+ reorder?: boolean;
173
+ }
174
+
175
+ /** Output of {@link applyTrustScoreStage}. */
176
+ export interface TrustStageOutput {
177
+ /** Admitted (injectable) candidates, score-multiplied and optionally sorted. */
178
+ admitted: TrustStageResultItem[];
179
+ /**
180
+ * Quarantined candidates — excluded from injection but visible to X-ray with
181
+ * the reason (rule 34 — exclusion must never look like "no result"). Callers
182
+ * that only need the injected list read `admitted`; X-ray reads `all`.
183
+ */
184
+ quarantined: TrustStageResultItem[];
185
+ /** Every candidate (admitted + quarantined) in input order, for X-ray. */
186
+ all: TrustStageResultItem[];
187
+ }
188
+
189
+ /**
190
+ * Apply the unified TrustScore stage: score each candidate, multiply its base
191
+ * score by `trustMultiplier(trust.score)`, and separate hard-negative
192
+ * (quarantine) items from the injectable set.
193
+ *
194
+ * Neutral candidates (no signals → multiplier `1.0`) are mathematically
195
+ * untouched, preserving byte-identical ranking for uninstrumented memories
196
+ * when the feature is ON (and the whole stage is skipped when OFF — rule 39).
197
+ */
198
+ export function applyTrustScoreStage(
199
+ candidates: readonly TrustStageCandidate[],
200
+ options: TrustStageOptions,
201
+ ): TrustStageOutput {
202
+ const reorder = options.reorder !== false;
203
+ const quarantine = options.quarantine !== false;
204
+ const minMul = options.minMultiplier;
205
+ const maxMul = options.maxMultiplier;
206
+
207
+ const all: TrustStageResultItem[] = [];
208
+ const admitted: TrustStageResultItem[] = [];
209
+ const quarantined: TrustStageResultItem[] = [];
210
+
211
+ for (let i = 0; i < candidates.length; i += 1) {
212
+ const c = candidates[i]!;
213
+ const signals = options.signals.get(c.path);
214
+ const trust = computeTrustScore(signals, options.weights);
215
+ const isQuarantined = quarantine && trust.band === "quarantine";
216
+ // Quarantined items are excluded from injection. Keep their score unchanged
217
+ // so X-ray shows what the pipeline WOULD have injected — never inflate or
218
+ // zero the score of a hidden item (rule 34).
219
+ const multiplier = isQuarantined ? 1 : trustMultiplier(trust.score, minMul, maxMul);
220
+ // Clamp the base score at zero before multiplying, mirroring the
221
+ // memory-worth filter: a negative base would invert the multiplier's
222
+ // intended direction (see memory-worth-filter.ts:145-153).
223
+ const safeBase = c.score < 0 ? 0 : c.score;
224
+ const scored = safeBase * multiplier;
225
+ const item: TrustStageResultItem = {
226
+ path: c.path,
227
+ score: scored,
228
+ originalScore: c.score,
229
+ multiplier,
230
+ trust,
231
+ quarantined: isQuarantined,
232
+ };
233
+ all.push(item);
234
+ if (isQuarantined) quarantined.push(item);
235
+ else admitted.push(item);
236
+ }
237
+
238
+ if (reorder) {
239
+ // Stable sort by descending multiplied score, with original input position
240
+ // as the deterministic tiebreaker (CLAUDE.md rule 19 / checklist §12).
241
+ admitted.sort((a, b) => {
242
+ if (b.score !== a.score) return b.score - a.score;
243
+ // Tiebreaker: original insertion order. We don't have the index handy on
244
+ // the item, so derive it from `all` ordering (input order is preserved
245
+ // there). Equal-score items keep first-seen-first order.
246
+ return 0;
247
+ });
248
+ // Node's sort is stable (ES2019+); the explicit `return 0` documents the
249
+ // contract for reviewers.
250
+ }
251
+
252
+ return { admitted, quarantined, all };
253
+ }
254
+
255
+ /**
256
+ * Compact X-ray projection of one stage item. Kept separate from the full
257
+ * `TrustScoreResult` so the X-ray snapshot type stays narrow and serializable,
258
+ * and so the renderer never depends on the scorer's internal component map
259
+ * shape beyond `{ value, weight }`.
260
+ */
261
+ export interface TrustXrayProjection {
262
+ score: number;
263
+ band: TrustBand;
264
+ components: Record<string, TrustScoreComponent>;
265
+ multiplier: number;
266
+ quarantined: boolean;
267
+ /** Human-readable exclusion reason, present only when quarantined. */
268
+ quarantineReason?: string;
269
+ }
270
+
271
+ /** Build the X-ray projection for one stage result item. */
272
+ export function projectTrustForXray(item: TrustStageResultItem): TrustXrayProjection {
273
+ const proj: TrustXrayProjection = {
274
+ score: item.trust.score,
275
+ band: item.trust.band,
276
+ components: item.trust.components,
277
+ multiplier: item.multiplier,
278
+ quarantined: item.quarantined,
279
+ };
280
+ if (item.quarantined) {
281
+ proj.quarantineReason = explainQuarantine(item.trust);
282
+ }
283
+ return proj;
284
+ }
285
+
286
+ /** Deterministic, component-derived reason a memory was quarantined. */
287
+ export function explainQuarantine(trust: TrustScoreResult): string {
288
+ const c = trust.components;
289
+ if (c.faithfulness && c.faithfulness.value === 0) return "faithfulness: contradicted";
290
+ if (c.contradiction && c.contradiction.value <= 0.2) return "contradiction: pending review";
291
+ return "hard-negative trust signal";
292
+ }
293
+
294
+
295
+ // ─── Recall-stage signal builder (I/O orchestration) ──────────────────────
296
+
297
+ /**
298
+ * Narrow callbacks the signal builder needs from its host (the orchestrator).
299
+ * Kept as interfaces so the module stays pure-testable: the host binds
300
+ * `getStorage` / `readQmdResultMemory` into these and the builder owns cache
301
+ * management, namespace iteration, and the cold-tier direct fallback.
302
+ */
303
+ export interface TrustScoreRerankDeps {
304
+ /** Read every memory in a namespace for the hot-tier signal scan. */
305
+ readNamespaceMemories(
306
+ namespace: string,
307
+ ): Promise<ReadonlyArray<{ path: string; frontmatter: TrustFrontmatterProjection }>>;
308
+ /** Read one memory's frontmatter by path (cold-tier direct fallback). */
309
+ readMemoryFrontmatter(path: string): Promise<TrustFrontmatterProjection | null>;
310
+ }
311
+
312
+ /** Per-namespace signal cache the host owns and the builder mutates. */
313
+ export interface TrustScoreSignalCache {
314
+ cache: Map<string, { at: number; signals: ReadonlyMap<string, TrustSignals> }>;
315
+ ttlMs: number;
316
+ }
317
+
318
+ /**
319
+ * Build the `path → TrustSignals` map for recall candidates using a
320
+ * per-namespace cache plus a direct-path fallback for cold-tier candidates
321
+ * absent from the hot scan. Extracted from the orchestrator (issue #1577) so
322
+ * the god file carries only thin wiring — the scoring itself is
323
+ * {@link applyTrustScoreStage}.
324
+ *
325
+ * Fail-open: a single unreadable namespace or memory is logged and skipped so
326
+ * a storage hiccup never breaks recall (mirrors `memory-worth-filter.ts`).
327
+ */
328
+ export async function buildTrustSignalsForRerank(
329
+ candidatePaths: readonly string[],
330
+ namespaces: readonly string[],
331
+ deps: TrustScoreRerankDeps,
332
+ signalCache: TrustScoreSignalCache,
333
+ now: Date,
334
+ options: {
335
+ recencyHalfLifeDays?: number;
336
+ logDebug?: (message: string, context: Record<string, unknown>) => void;
337
+ } = {},
338
+ ): Promise<Map<string, TrustSignals>> {
339
+ const nowMs = now.getTime();
340
+ const logDebug = options.logDebug ?? (() => {});
341
+ const signals = new Map<string, TrustSignals>();
342
+
343
+ // Evict expired cache entries (mirrors the worth-cache discipline).
344
+ for (const [key, entry] of signalCache.cache) {
345
+ if (nowMs - entry.at >= signalCache.ttlMs) signalCache.cache.delete(key);
346
+ }
347
+
348
+ // Per-namespace hot scan with cache.
349
+ const seenNamespaces = new Set<string>();
350
+ for (const ns of namespaces) {
351
+ if (seenNamespaces.has(ns)) continue;
352
+ seenNamespaces.add(ns);
353
+ try {
354
+ const cached = signalCache.cache.get(ns);
355
+ let nsMap: ReadonlyMap<string, TrustSignals> | undefined;
356
+ if (cached && nowMs - cached.at < signalCache.ttlMs) {
357
+ nsMap = cached.signals;
358
+ } else {
359
+ const memories = await deps.readNamespaceMemories(ns);
360
+ nsMap = buildTrustSignalMap(memories, now, {
361
+ recencyHalfLifeDays: options.recencyHalfLifeDays,
362
+ });
363
+ signalCache.cache.set(ns, { at: nowMs, signals: nsMap });
364
+ }
365
+ for (const [path, s] of nsMap) signals.set(path, s);
366
+ } catch (err) {
367
+ logDebug("trust-score: failed to read namespace, skipping", {
368
+ namespace: ns,
369
+ error: (err as Error).message,
370
+ });
371
+ }
372
+ }
373
+
374
+ // Direct per-path fallback for cold-tier candidates absent from the hot scan.
375
+ const missing = candidatePaths.filter((p) => !signals.has(p));
376
+ if (missing.length > 0) {
377
+ const fallbackMemories: Array<{ path: string; frontmatter: TrustFrontmatterProjection }> = [];
378
+ for (const path of missing) {
379
+ try {
380
+ const frontmatter = await deps.readMemoryFrontmatter(path);
381
+ if (frontmatter) fallbackMemories.push({ path, frontmatter });
382
+ } catch (err) {
383
+ logDebug("trust-score: direct path lookup failed", {
384
+ path,
385
+ error: (err as Error).message,
386
+ });
387
+ }
388
+ }
389
+ if (fallbackMemories.length > 0) {
390
+ const fallbackSignals = buildTrustSignalMap(fallbackMemories, now, {
391
+ recencyHalfLifeDays: options.recencyHalfLifeDays,
392
+ });
393
+ for (const [path, s] of fallbackSignals) signals.set(path, s);
394
+ }
395
+ }
396
+
397
+ return signals;
398
+ }
@@ -0,0 +1,282 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+
4
+ import {
5
+ DEFAULT_TRUST_WEIGHTS,
6
+ NEUTRAL_TRUST_SCORE,
7
+ TRUST_BAND_THRESHOLDS,
8
+ bandForScore,
9
+ computeTrustScore,
10
+ renderEpistemicHedge,
11
+ resolveTrustWeights,
12
+ trustMultiplier,
13
+ type TrustSignals,
14
+ } from "./trust-score.js";
15
+
16
+ /**
17
+ * Issue #1577 PR 1 — unit tests for the pure TrustScore module.
18
+ *
19
+ * Pins the contract rules from the module header:
20
+ * 1. No signals → neutral (0.5, medium, multiplier 1.0).
21
+ * 2. Corrupt / out-of-range component → that component collapses to neutral.
22
+ * 3. Deterministic.
23
+ * 4. Components echoed for explainability.
24
+ * 5. quarantine reserved for hard negatives.
25
+ * Plus monotonicity: more corroboration → ≥ score; contradicted → ≤ score.
26
+ */
27
+
28
+ test("no signals → neutral prior (score 0.5, band medium, neutral flag)", () => {
29
+ const r = computeTrustScore({});
30
+ assert.equal(r.score, NEUTRAL_TRUST_SCORE);
31
+ assert.equal(r.band, "medium");
32
+ assert.equal(r.neutral, true);
33
+ assert.deepEqual(r.components, {});
34
+ // The multiplier derived from a neutral score is exactly 1.0.
35
+ assert.equal(trustMultiplier(r.score), 1);
36
+ });
37
+
38
+ test("neutral multiplier is exactly 1.0 at score 0.5", () => {
39
+ assert.equal(trustMultiplier(0.5), 1);
40
+ });
41
+
42
+ test("multiplier is bounded by [min, max] and monotonic in score", () => {
43
+ // score 0 → min, score 1 → max, strictly increasing through 0.5=1.
44
+ assert.equal(trustMultiplier(0, 0.5, 1.25), 0.5);
45
+ assert.equal(trustMultiplier(1, 0.5, 1.25), 1.25);
46
+ assert.equal(trustMultiplier(0.5, 0.5, 1.25), 1);
47
+ // Monotonic non-decreasing across the range.
48
+ let prev = -Infinity;
49
+ for (let i = 0; i <= 20; i += 1) {
50
+ const s = i / 20;
51
+ const m = trustMultiplier(s, 0.5, 1.25);
52
+ assert.ok(m >= prev - 1e-12, `multiplier not monotonic at ${s}`);
53
+ prev = m;
54
+ }
55
+ });
56
+
57
+ test("multiplier stays in bounds when given an inverted (min>max) contract", () => {
58
+ // Defensive: pick the tighter bound rather than producing nonsense.
59
+ const m = trustMultiplier(0.2, 1.25, 0.5);
60
+ assert.ok(m >= 0.5 - 1e-9 && m <= 1.25 + 1e-9);
61
+ });
62
+
63
+ test("monotonicity: more corroboration → score does not decrease", () => {
64
+ const base: TrustSignals = { corroborationCount: 1 };
65
+ const more: TrustSignals = { corroborationCount: 5 };
66
+ const a = computeTrustScore(base);
67
+ const b = computeTrustScore(more);
68
+ assert.ok(b.score >= a.score - 1e-12, "more corroboration must not lower the score");
69
+ });
70
+
71
+ test("monotonicity: contradicted faithfulness → score <= unchecked", () => {
72
+ const unchecked = computeTrustScore({ faithfulness: "unchecked" });
73
+ const contradicted = computeTrustScore({ faithfulness: "contradicted" });
74
+ assert.ok(
75
+ contradicted.score <= unchecked.score + 1e-12,
76
+ "contradicted must not out-score unchecked",
77
+ );
78
+ });
79
+
80
+ test("contradicted faithfulness forces the quarantine band", () => {
81
+ const r = computeTrustScore({ faithfulness: "contradicted" });
82
+ assert.equal(r.band, "quarantine");
83
+ });
84
+
85
+ test("pending_review contradiction forces the quarantine band", () => {
86
+ const r = computeTrustScore({ contradiction: "pending_review" });
87
+ assert.equal(r.band, "quarantine");
88
+ });
89
+
90
+ test("corrupt component collapses to neutral, never to an extreme", () => {
91
+ // NaN domainCalibration must not push the score to 0 or 1.
92
+ const r = computeTrustScore({ domainCalibration: NaN });
93
+ // Only one component present and it is corrupt → neutral prior.
94
+ assert.equal(r.neutral, true);
95
+ assert.equal(r.score, NEUTRAL_TRUST_SCORE);
96
+ });
97
+
98
+ test("corrupt corroborationCount (negative / NaN) is dropped, not damning", () => {
99
+ const rNegative = computeTrustScore({ corroborationCount: -3 });
100
+ const rNaN = computeTrustScore({ corroborationCount: NaN });
101
+ assert.equal(rNegative.neutral, true);
102
+ assert.equal(rNaN.neutral, true);
103
+ });
104
+
105
+ test("weights: invalid weight rejected, valid override applied", () => {
106
+ const resolved = resolveTrustWeights({ memoryWorth: 0.9, faithfulness: NaN });
107
+ assert.equal(resolved.memoryWorth, 0.9);
108
+ // NaN faithfulness falls back to the documented default, not NaN.
109
+ assert.equal(resolved.faithfulness, DEFAULT_TRUST_WEIGHTS.faithfulness);
110
+ });
111
+
112
+ test("weights: out-of-range weight rejected", () => {
113
+ const resolved = resolveTrustWeights({ provenance: 5 });
114
+ assert.equal(resolved.provenance, DEFAULT_TRUST_WEIGHTS.provenance);
115
+ });
116
+
117
+ test("active weights are sum-normalized so absent components redistribute", () => {
118
+ // Only memoryWorth present → it carries 100% of the weight.
119
+ const r = computeTrustScore({ memoryWorth: { score: 1, confidence: 5 } });
120
+ assert.ok(r.components.memoryWorth);
121
+ assert.ok(Math.abs(r.components.memoryWorth.weight - 1) < 1e-12);
122
+ assert.ok(Math.abs(r.score - 1) < 1e-12);
123
+ });
124
+
125
+ test("components are echoed for explainability", () => {
126
+ const r = computeTrustScore({
127
+ memoryWorth: { score: 0.9, confidence: 3 },
128
+ provenance: "verified",
129
+ });
130
+ assert.ok(r.components.memoryWorth);
131
+ assert.ok(r.components.provenance);
132
+ // Each component carries value + weight.
133
+ for (const c of Object.values(r.components)) {
134
+ assert.ok(typeof c.value === "number" && c.value >= 0 && c.value <= 1);
135
+ assert.ok(typeof c.weight === "number" && c.weight >= 0 && c.weight <= 1);
136
+ }
137
+ });
138
+
139
+ test("determinism: same inputs → identical result (twice)", () => {
140
+ const signals: TrustSignals = {
141
+ memoryWorth: { score: 0.8, confidence: 4 },
142
+ provenance: "verified",
143
+ faithfulness: "entailed",
144
+ corroborationCount: 3,
145
+ };
146
+ const a = computeTrustScore(signals);
147
+ const b = computeTrustScore(signals);
148
+ assert.deepEqual(a, b);
149
+ });
150
+
151
+ test("band thresholds map scores to the documented bands", () => {
152
+ assert.equal(bandForScore(0.9, false), "high");
153
+ assert.equal(bandForScore(0.7, false), "high");
154
+ assert.equal(bandForScore(0.69, false), "medium");
155
+ assert.equal(bandForScore(0.45, false), "medium");
156
+ assert.equal(bandForScore(0.3, false), "low");
157
+ assert.equal(bandForScore(0.1, false), "low");
158
+ // Hard negative overrides the score-derived band.
159
+ assert.equal(bandForScore(0.95, true), "quarantine");
160
+ });
161
+
162
+ test("band thresholds constant matches the band mapping", () => {
163
+ assert.equal(TRUST_BAND_THRESHOLDS.high, 0.7);
164
+ assert.equal(TRUST_BAND_THRESHOLDS.medium, 0.45);
165
+ });
166
+
167
+ // ─── Epistemic rendering ──────────────────────────────────────────────────
168
+
169
+ test("epistemic hedge: neutral and high → empty string (no token waste)", () => {
170
+ const neutral = computeTrustScore({});
171
+ assert.equal(renderEpistemicHedge(neutral), "");
172
+ const high = computeTrustScore({
173
+ memoryWorth: { score: 1, confidence: 10 },
174
+ provenance: "verified",
175
+ faithfulness: "entailed",
176
+ corroborationCount: 4,
177
+ });
178
+ assert.equal(high.band, "high");
179
+ assert.equal(renderEpistemicHedge(high), "");
180
+ });
181
+
182
+ test("epistemic hedge: low band → non-empty, deterministic hedge", () => {
183
+ const low = computeTrustScore({ faithfulness: "unsupported" });
184
+ const hedge = renderEpistemicHedge(low);
185
+ assert.ok(hedge.length > 0);
186
+ assert.ok(hedge.startsWith("(low confidence"));
187
+ // Deterministic.
188
+ assert.equal(renderEpistemicHedge(low), hedge);
189
+ });
190
+
191
+ test("epistemic hedge: quarantine band → names the hard negative", () => {
192
+ const q = computeTrustScore({ faithfulness: "contradicted" });
193
+ const hedge = renderEpistemicHedge(q);
194
+ assert.ok(hedge.length > 0);
195
+ assert.match(hedge, /contradicted/);
196
+ });
197
+
198
+ test("epistemic hedge: medium band → names a single weakness", () => {
199
+ const medium = computeTrustScore({ corroborationCount: 1 });
200
+ // single mention alone lands around neutral-ish; force medium by checking it
201
+ // produced a hedge only when band is medium/low. Verify determinism either way.
202
+ const hedge = renderEpistemicHedge(medium);
203
+ if (medium.band === "medium") {
204
+ assert.ok(hedge.startsWith("(unconfirmed"));
205
+ }
206
+ // Always deterministic.
207
+ assert.equal(renderEpistemicHedge(medium), hedge);
208
+ });
209
+
210
+ test("disabled weight: zero-weight-only-signal falls back to neutral, not low", () => {
211
+ // Review P2: when an operator sets a component weight to 0 to disable it,
212
+ // and a memory has only that signal, the score must NOT collapse to 0 (low
213
+ // band). It should be neutral — a disabled signal carries no information.
214
+ const result = computeTrustScore(
215
+ { memoryWorth: { score: 0.9, confidence: 5 } },
216
+ { memoryWorth: 0, provenance: 1, faithfulness: 1 },
217
+ );
218
+ assert.equal(result.neutral, true, "zero-weight-only-signal must be neutral");
219
+ assert.equal(result.score, NEUTRAL_TRUST_SCORE);
220
+ assert.equal(result.band, "medium");
221
+ assert.equal(Object.keys(result.components).length, 0);
222
+ });
223
+
224
+ test("disabled weight: zero-weight component does not contribute to blend", () => {
225
+ // When a component has weight 0 but other components are active, the active
226
+ // ones still score normally. The zero-weight component stays in the echo
227
+ // (with weight 0) for explainability but contributes nothing to the blend.
228
+ const result = computeTrustScore(
229
+ { memoryWorth: { score: 0.9, confidence: 5 }, provenance: "verified" },
230
+ { memoryWorth: 0, provenance: 1, faithfulness: 1 },
231
+ );
232
+ assert.equal(result.neutral, false);
233
+ assert.ok(result.score > NEUTRAL_TRUST_SCORE, "verified provenance should boost");
234
+ assert.equal(result.components.memoryWorth?.weight, 0, "zero-weight component echoed with weight 0");
235
+ assert.ok(result.components.provenance?.weight > 0, "active component has non-zero weight");
236
+ });
237
+
238
+ // ─── Issue #1577 — scoring edges (review Oqg_0 / Op_0X / Op_0b) ─────────────
239
+
240
+ test("unchecked faithfulness as the ONLY signal → truly neutral (absent, not active 0.5)", () => {
241
+ // Review Oqg_0: "unchecked" means the gate ran but could not verify — it
242
+ // carries NO signal. A memory whose ONLY signal is unchecked faithfulness
243
+ // must score the neutral prior (neutral: true), NOT "active 0.5", so the
244
+ // epistemic hedge never spuriously fires on an unverifiable memory and the
245
+ // component never masquerades as measured evidence.
246
+ const result = computeTrustScore({ faithfulness: "unchecked" });
247
+ assert.equal(result.neutral, true, "unchecked-only must be truly neutral");
248
+ assert.equal(result.score, NEUTRAL_TRUST_SCORE);
249
+ assert.equal(result.band, "medium");
250
+ assert.equal(Object.keys(result.components).length, 0, "unchecked must not appear as a component");
251
+ assert.equal(renderEpistemicHedge(result), "", "no hedge on an unverifiable memory");
252
+ });
253
+
254
+ test("unchecked faithfulness mixed with another signal → dropped (does not dilute)", () => {
255
+ // When unchecked co-occurs with a real signal, unchecked is ABSENT (not a
256
+ // neutral 0.5 component pulling toward the middle). The memory scores purely
257
+ // on the present signal — identical to a memory that never had a faithfulness
258
+ // verdict at all.
259
+ const withUnchecked = computeTrustScore({ faithfulness: "unchecked", provenance: "verified" });
260
+ const justVerified = computeTrustScore({ provenance: "verified" });
261
+ assert.equal(withUnchecked.score, justVerified.score, "unchecked must not dilute a present signal");
262
+ assert.equal(withUnchecked.components.faithfulness, undefined, "unchecked dropped from components");
263
+ assert.ok(withUnchecked.components.provenance !== undefined, "verified provenance present");
264
+ });
265
+
266
+ test("disabled weight is neutral across all recall paths: present+disabled vs absent", () => {
267
+ // Review Op_0X: a disabled (weight 0) component must be neutral, not damning.
268
+ // A memory with provenance=verified AND a zero-weight memoryWorth signal must
269
+ // score identically to a memory with provenance=verified alone — the disabled
270
+ // component contributes nothing and the active one is sum-normalized to 1.0.
271
+ const disabled = computeTrustScore(
272
+ { memoryWorth: { score: 0.1, confidence: 5 }, provenance: "verified" },
273
+ { memoryWorth: 0, provenance: 1, faithfulness: 1 },
274
+ );
275
+ const absent = computeTrustScore(
276
+ { provenance: "verified" },
277
+ { memoryWorth: 0, provenance: 1, faithfulness: 1 },
278
+ );
279
+ assert.equal(disabled.score, absent.score, "disabled-weight signal must be neutral (== absent)");
280
+ assert.equal(disabled.components.memoryWorth?.weight, 0, "disabled component echoed with weight 0");
281
+ assert.equal(disabled.components.provenance?.weight, 1, "sole active component normalizes to full weight");
282
+ });