@vorionsys/atsf-core 0.2.4 → 0.3.0

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 (375) hide show
  1. package/CHANGELOG.md +1 -0
  2. package/LICENSE +1 -1
  3. package/README.md +82 -29
  4. package/dist/adapters/base-adapter.d.ts +94 -0
  5. package/dist/adapters/base-adapter.d.ts.map +1 -0
  6. package/dist/adapters/base-adapter.js +233 -0
  7. package/dist/adapters/base-adapter.js.map +1 -0
  8. package/dist/adapters/index.d.ts +9 -0
  9. package/dist/adapters/index.d.ts.map +1 -0
  10. package/dist/adapters/index.js +5 -0
  11. package/dist/adapters/index.js.map +1 -0
  12. package/dist/adapters/types.d.ts +83 -0
  13. package/dist/adapters/types.d.ts.map +1 -0
  14. package/dist/adapters/types.js +4 -0
  15. package/dist/adapters/types.js.map +1 -0
  16. package/dist/adapters/webhook-handler.d.ts +64 -0
  17. package/dist/adapters/webhook-handler.d.ts.map +1 -0
  18. package/dist/adapters/webhook-handler.js +170 -0
  19. package/dist/adapters/webhook-handler.js.map +1 -0
  20. package/dist/api/index.d.ts.map +1 -1
  21. package/dist/api/index.js +2 -0
  22. package/dist/api/index.js.map +1 -1
  23. package/dist/api/server.d.ts.map +1 -1
  24. package/dist/api/server.js +2 -0
  25. package/dist/api/server.js.map +1 -1
  26. package/dist/arbitration/index.d.ts +0 -8
  27. package/dist/arbitration/index.d.ts.map +1 -1
  28. package/dist/arbitration/index.js +2 -0
  29. package/dist/arbitration/index.js.map +1 -1
  30. package/dist/arbitration/types.d.ts.map +1 -1
  31. package/dist/arbitration/types.js +2 -8
  32. package/dist/arbitration/types.js.map +1 -1
  33. package/dist/basis/evaluator.d.ts +0 -5
  34. package/dist/basis/evaluator.d.ts.map +1 -1
  35. package/dist/basis/evaluator.js +2 -0
  36. package/dist/basis/evaluator.js.map +1 -1
  37. package/dist/basis/index.d.ts.map +1 -1
  38. package/dist/basis/index.js +2 -0
  39. package/dist/basis/index.js.map +1 -1
  40. package/dist/basis/parser.d.ts +28 -28
  41. package/dist/basis/parser.d.ts.map +1 -1
  42. package/dist/basis/parser.js +2 -0
  43. package/dist/basis/parser.js.map +1 -1
  44. package/dist/basis/types.d.ts.map +1 -1
  45. package/dist/basis/types.js +2 -3
  46. package/dist/basis/types.js.map +1 -1
  47. package/dist/chain/index.d.ts +0 -8
  48. package/dist/chain/index.d.ts.map +1 -1
  49. package/dist/chain/index.js +2 -0
  50. package/dist/chain/index.js.map +1 -1
  51. package/dist/cognigate/index.d.ts +0 -8
  52. package/dist/cognigate/index.d.ts.map +1 -1
  53. package/dist/cognigate/index.js +2 -0
  54. package/dist/cognigate/index.js.map +1 -1
  55. package/dist/common/adapters.d.ts.map +1 -1
  56. package/dist/common/adapters.js +2 -8
  57. package/dist/common/adapters.js.map +1 -1
  58. package/dist/common/config.d.ts.map +1 -1
  59. package/dist/common/config.js +2 -0
  60. package/dist/common/config.js.map +1 -1
  61. package/dist/common/index.d.ts.map +1 -1
  62. package/dist/common/index.js +2 -0
  63. package/dist/common/index.js.map +1 -1
  64. package/dist/common/logger.d.ts.map +1 -1
  65. package/dist/common/logger.js +2 -0
  66. package/dist/common/logger.js.map +1 -1
  67. package/dist/common/types.d.ts +7 -7
  68. package/dist/common/types.d.ts.map +1 -1
  69. package/dist/common/types.js +2 -9
  70. package/dist/common/types.js.map +1 -1
  71. package/dist/containment/index.d.ts +0 -8
  72. package/dist/containment/index.d.ts.map +1 -1
  73. package/dist/containment/index.js +2 -0
  74. package/dist/containment/index.js.map +1 -1
  75. package/dist/containment/types.d.ts.map +1 -1
  76. package/dist/containment/types.js +2 -8
  77. package/dist/containment/types.js.map +1 -1
  78. package/dist/contracts/index.d.ts +0 -8
  79. package/dist/contracts/index.d.ts.map +1 -1
  80. package/dist/contracts/index.js +2 -0
  81. package/dist/contracts/index.js.map +1 -1
  82. package/dist/contracts/types.d.ts.map +1 -1
  83. package/dist/contracts/types.js +2 -8
  84. package/dist/contracts/types.js.map +1 -1
  85. package/dist/crewai/callback.d.ts +0 -7
  86. package/dist/crewai/callback.d.ts.map +1 -1
  87. package/dist/crewai/callback.js +2 -0
  88. package/dist/crewai/callback.js.map +1 -1
  89. package/dist/crewai/executor.d.ts +0 -7
  90. package/dist/crewai/executor.d.ts.map +1 -1
  91. package/dist/crewai/executor.js +2 -0
  92. package/dist/crewai/executor.js.map +1 -1
  93. package/dist/crewai/index.d.ts.map +1 -1
  94. package/dist/crewai/index.js +2 -0
  95. package/dist/crewai/index.js.map +1 -1
  96. package/dist/crewai/tools.d.ts.map +1 -1
  97. package/dist/crewai/tools.js +2 -7
  98. package/dist/crewai/tools.js.map +1 -1
  99. package/dist/crewai/types.d.ts.map +1 -1
  100. package/dist/crewai/types.js +2 -7
  101. package/dist/crewai/types.js.map +1 -1
  102. package/dist/enforce/index.d.ts +0 -15
  103. package/dist/enforce/index.d.ts.map +1 -1
  104. package/dist/enforce/index.js +3 -1
  105. package/dist/enforce/index.js.map +1 -1
  106. package/dist/enforce/trust-aware-enforcement-service.d.ts +0 -15
  107. package/dist/enforce/trust-aware-enforcement-service.d.ts.map +1 -1
  108. package/dist/enforce/trust-aware-enforcement-service.js +2 -0
  109. package/dist/enforce/trust-aware-enforcement-service.js.map +1 -1
  110. package/dist/governance/fluid-workflow.d.ts +0 -8
  111. package/dist/governance/fluid-workflow.d.ts.map +1 -1
  112. package/dist/governance/fluid-workflow.js +2 -0
  113. package/dist/governance/fluid-workflow.js.map +1 -1
  114. package/dist/governance/index.d.ts +0 -8
  115. package/dist/governance/index.d.ts.map +1 -1
  116. package/dist/governance/index.js +2 -0
  117. package/dist/governance/index.js.map +1 -1
  118. package/dist/governance/proof-bridge.d.ts.map +1 -1
  119. package/dist/governance/proof-bridge.js +2 -12
  120. package/dist/governance/proof-bridge.js.map +1 -1
  121. package/dist/governance/types.d.ts.map +1 -1
  122. package/dist/governance/types.js +2 -8
  123. package/dist/governance/types.js.map +1 -1
  124. package/dist/index.d.ts +3 -0
  125. package/dist/index.d.ts.map +1 -1
  126. package/dist/index.js +8 -0
  127. package/dist/index.js.map +1 -1
  128. package/dist/intent/index.d.ts +0 -13
  129. package/dist/intent/index.d.ts.map +1 -1
  130. package/dist/intent/index.js +4 -2
  131. package/dist/intent/index.js.map +1 -1
  132. package/dist/intent/persistent-intent-service.d.ts +0 -15
  133. package/dist/intent/persistent-intent-service.d.ts.map +1 -1
  134. package/dist/intent/persistent-intent-service.js +2 -0
  135. package/dist/intent/persistent-intent-service.js.map +1 -1
  136. package/dist/intent/supabase-intent-repository.d.ts +0 -17
  137. package/dist/intent/supabase-intent-repository.d.ts.map +1 -1
  138. package/dist/intent/supabase-intent-repository.js +2 -0
  139. package/dist/intent/supabase-intent-repository.js.map +1 -1
  140. package/dist/intent-gateway/index.d.ts +499 -0
  141. package/dist/intent-gateway/index.d.ts.map +1 -0
  142. package/dist/intent-gateway/index.js +1332 -0
  143. package/dist/intent-gateway/index.js.map +1 -0
  144. package/dist/langchain/callback.d.ts +0 -7
  145. package/dist/langchain/callback.d.ts.map +1 -1
  146. package/dist/langchain/callback.js +2 -0
  147. package/dist/langchain/callback.js.map +1 -1
  148. package/dist/langchain/executor.d.ts +0 -7
  149. package/dist/langchain/executor.d.ts.map +1 -1
  150. package/dist/langchain/executor.js +2 -0
  151. package/dist/langchain/executor.js.map +1 -1
  152. package/dist/langchain/index.d.ts.map +1 -1
  153. package/dist/langchain/index.js +2 -0
  154. package/dist/langchain/index.js.map +1 -1
  155. package/dist/langchain/tools.d.ts.map +1 -1
  156. package/dist/langchain/tools.js +2 -7
  157. package/dist/langchain/tools.js.map +1 -1
  158. package/dist/langchain/types.d.ts.map +1 -1
  159. package/dist/langchain/types.js +2 -7
  160. package/dist/langchain/types.js.map +1 -1
  161. package/dist/layers/implementations/L0-request-format.d.ts.map +1 -1
  162. package/dist/layers/implementations/L0-request-format.js +2 -0
  163. package/dist/layers/implementations/L0-request-format.js.map +1 -1
  164. package/dist/layers/implementations/L1-input-size.d.ts.map +1 -1
  165. package/dist/layers/implementations/L1-input-size.js +2 -0
  166. package/dist/layers/implementations/L1-input-size.js.map +1 -1
  167. package/dist/layers/implementations/L2-charset-sanitizer.d.ts.map +1 -1
  168. package/dist/layers/implementations/L2-charset-sanitizer.js +2 -0
  169. package/dist/layers/implementations/L2-charset-sanitizer.js.map +1 -1
  170. package/dist/layers/implementations/L3-schema-conformance.d.ts.map +1 -1
  171. package/dist/layers/implementations/L3-schema-conformance.js +2 -0
  172. package/dist/layers/implementations/L3-schema-conformance.js.map +1 -1
  173. package/dist/layers/implementations/L4-injection-detector.d.ts.map +1 -1
  174. package/dist/layers/implementations/L4-injection-detector.js +2 -0
  175. package/dist/layers/implementations/L4-injection-detector.js.map +1 -1
  176. package/dist/layers/implementations/L5-rate-limiter.d.ts.map +1 -1
  177. package/dist/layers/implementations/L5-rate-limiter.js +2 -0
  178. package/dist/layers/implementations/L5-rate-limiter.js.map +1 -1
  179. package/dist/layers/implementations/index.d.ts.map +1 -1
  180. package/dist/layers/implementations/index.js +2 -0
  181. package/dist/layers/implementations/index.js.map +1 -1
  182. package/dist/layers/index.d.ts +0 -8
  183. package/dist/layers/index.d.ts.map +1 -1
  184. package/dist/layers/index.js +2 -0
  185. package/dist/layers/index.js.map +1 -1
  186. package/dist/layers/types.d.ts.map +1 -1
  187. package/dist/layers/types.js +2 -8
  188. package/dist/layers/types.js.map +1 -1
  189. package/dist/paramesphere/activation-collector.d.ts +128 -0
  190. package/dist/paramesphere/activation-collector.d.ts.map +1 -0
  191. package/dist/paramesphere/activation-collector.js +260 -0
  192. package/dist/paramesphere/activation-collector.js.map +1 -0
  193. package/dist/paramesphere/cognitive-envelope.d.ts +73 -0
  194. package/dist/paramesphere/cognitive-envelope.d.ts.map +1 -0
  195. package/dist/paramesphere/cognitive-envelope.js +209 -0
  196. package/dist/paramesphere/cognitive-envelope.js.map +1 -0
  197. package/dist/paramesphere/envelope-integration.d.ts +60 -0
  198. package/dist/paramesphere/envelope-integration.d.ts.map +1 -0
  199. package/dist/paramesphere/envelope-integration.js +93 -0
  200. package/dist/paramesphere/envelope-integration.js.map +1 -0
  201. package/dist/paramesphere/fingerprint-monitor.d.ts +136 -0
  202. package/dist/paramesphere/fingerprint-monitor.d.ts.map +1 -0
  203. package/dist/paramesphere/fingerprint-monitor.js +212 -0
  204. package/dist/paramesphere/fingerprint-monitor.js.map +1 -0
  205. package/dist/paramesphere/fingerprint-store.d.ts +85 -0
  206. package/dist/paramesphere/fingerprint-store.d.ts.map +1 -0
  207. package/dist/paramesphere/fingerprint-store.js +68 -0
  208. package/dist/paramesphere/fingerprint-store.js.map +1 -0
  209. package/dist/paramesphere/index.d.ts +21 -0
  210. package/dist/paramesphere/index.d.ts.map +1 -0
  211. package/dist/paramesphere/index.js +18 -0
  212. package/dist/paramesphere/index.js.map +1 -0
  213. package/dist/paramesphere/monitor-integration.d.ts +37 -0
  214. package/dist/paramesphere/monitor-integration.d.ts.map +1 -0
  215. package/dist/paramesphere/monitor-integration.js +81 -0
  216. package/dist/paramesphere/monitor-integration.js.map +1 -0
  217. package/dist/paramesphere/paramesphere-engine.d.ts +111 -0
  218. package/dist/paramesphere/paramesphere-engine.d.ts.map +1 -0
  219. package/dist/paramesphere/paramesphere-engine.js +542 -0
  220. package/dist/paramesphere/paramesphere-engine.js.map +1 -0
  221. package/dist/paramesphere/types.d.ts +142 -0
  222. package/dist/paramesphere/types.d.ts.map +1 -0
  223. package/dist/paramesphere/types.js +4 -0
  224. package/dist/paramesphere/types.js.map +1 -0
  225. package/dist/persistence/file.d.ts +0 -7
  226. package/dist/persistence/file.d.ts.map +1 -1
  227. package/dist/persistence/file.js +2 -0
  228. package/dist/persistence/file.js.map +1 -1
  229. package/dist/persistence/index.d.ts.map +1 -1
  230. package/dist/persistence/index.js +2 -0
  231. package/dist/persistence/index.js.map +1 -1
  232. package/dist/persistence/memory.d.ts.map +1 -1
  233. package/dist/persistence/memory.js +2 -7
  234. package/dist/persistence/memory.js.map +1 -1
  235. package/dist/persistence/sqlite.d.ts +0 -8
  236. package/dist/persistence/sqlite.d.ts.map +1 -1
  237. package/dist/persistence/sqlite.js +3 -1
  238. package/dist/persistence/sqlite.js.map +1 -1
  239. package/dist/persistence/supabase.d.ts.map +1 -1
  240. package/dist/persistence/supabase.js +3 -8
  241. package/dist/persistence/supabase.js.map +1 -1
  242. package/dist/persistence/types.d.ts.map +1 -1
  243. package/dist/persistence/types.js +2 -7
  244. package/dist/persistence/types.js.map +1 -1
  245. package/dist/phase6/ceiling.d.ts +0 -16
  246. package/dist/phase6/ceiling.d.ts.map +1 -1
  247. package/dist/phase6/ceiling.js +2 -0
  248. package/dist/phase6/ceiling.js.map +1 -1
  249. package/dist/phase6/context.d.ts +0 -17
  250. package/dist/phase6/context.d.ts.map +1 -1
  251. package/dist/phase6/context.js +2 -0
  252. package/dist/phase6/context.js.map +1 -1
  253. package/dist/phase6/index.d.ts.map +1 -1
  254. package/dist/phase6/index.js +2 -0
  255. package/dist/phase6/index.js.map +1 -1
  256. package/dist/phase6/presets.d.ts +0 -16
  257. package/dist/phase6/presets.d.ts.map +1 -1
  258. package/dist/phase6/presets.js +5 -3
  259. package/dist/phase6/presets.js.map +1 -1
  260. package/dist/phase6/provenance.d.ts +0 -15
  261. package/dist/phase6/provenance.d.ts.map +1 -1
  262. package/dist/phase6/provenance.js +2 -0
  263. package/dist/phase6/provenance.js.map +1 -1
  264. package/dist/phase6/role-gates/index.d.ts.map +1 -1
  265. package/dist/phase6/role-gates/index.js +2 -0
  266. package/dist/phase6/role-gates/index.js.map +1 -1
  267. package/dist/phase6/role-gates/kernel.d.ts.map +1 -1
  268. package/dist/phase6/role-gates/kernel.js +2 -0
  269. package/dist/phase6/role-gates/kernel.js.map +1 -1
  270. package/dist/phase6/role-gates/policy.d.ts.map +1 -1
  271. package/dist/phase6/role-gates/policy.js +2 -11
  272. package/dist/phase6/role-gates/policy.js.map +1 -1
  273. package/dist/phase6/role-gates.d.ts +0 -16
  274. package/dist/phase6/role-gates.d.ts.map +1 -1
  275. package/dist/phase6/role-gates.js +2 -0
  276. package/dist/phase6/role-gates.js.map +1 -1
  277. package/dist/phase6/types.d.ts +45 -16
  278. package/dist/phase6/types.d.ts.map +1 -1
  279. package/dist/phase6/types.js +49 -0
  280. package/dist/phase6/types.js.map +1 -1
  281. package/dist/phase6/weight-presets/canonical.d.ts.map +1 -1
  282. package/dist/phase6/weight-presets/canonical.js +2 -0
  283. package/dist/phase6/weight-presets/canonical.js.map +1 -1
  284. package/dist/phase6/weight-presets/deltas.d.ts.map +1 -1
  285. package/dist/phase6/weight-presets/deltas.js +2 -10
  286. package/dist/phase6/weight-presets/deltas.js.map +1 -1
  287. package/dist/phase6/weight-presets/index.d.ts.map +1 -1
  288. package/dist/phase6/weight-presets/index.js +2 -0
  289. package/dist/phase6/weight-presets/index.js.map +1 -1
  290. package/dist/phase6/weight-presets/merger.d.ts +0 -10
  291. package/dist/phase6/weight-presets/merger.d.ts.map +1 -1
  292. package/dist/phase6/weight-presets/merger.js +2 -0
  293. package/dist/phase6/weight-presets/merger.js.map +1 -1
  294. package/dist/proof/index.d.ts +3 -10
  295. package/dist/proof/index.d.ts.map +1 -1
  296. package/dist/proof/index.js +27 -9
  297. package/dist/proof/index.js.map +1 -1
  298. package/dist/proof/merkle.d.ts +0 -16
  299. package/dist/proof/merkle.d.ts.map +1 -1
  300. package/dist/proof/merkle.js +2 -0
  301. package/dist/proof/merkle.js.map +1 -1
  302. package/dist/proof/zk-proofs.d.ts +0 -18
  303. package/dist/proof/zk-proofs.d.ts.map +1 -1
  304. package/dist/proof/zk-proofs.js +2 -0
  305. package/dist/proof/zk-proofs.js.map +1 -1
  306. package/dist/provenance/index.d.ts +0 -8
  307. package/dist/provenance/index.d.ts.map +1 -1
  308. package/dist/provenance/index.js +2 -0
  309. package/dist/provenance/index.js.map +1 -1
  310. package/dist/provenance/types.d.ts.map +1 -1
  311. package/dist/provenance/types.js +2 -8
  312. package/dist/provenance/types.js.map +1 -1
  313. package/dist/sandbox-training/challenges.d.ts.map +1 -1
  314. package/dist/sandbox-training/challenges.js +2 -8
  315. package/dist/sandbox-training/challenges.js.map +1 -1
  316. package/dist/sandbox-training/graduation.d.ts.map +1 -1
  317. package/dist/sandbox-training/graduation.js +2 -8
  318. package/dist/sandbox-training/graduation.js.map +1 -1
  319. package/dist/sandbox-training/index.d.ts.map +1 -1
  320. package/dist/sandbox-training/index.js +2 -0
  321. package/dist/sandbox-training/index.js.map +1 -1
  322. package/dist/sandbox-training/promotion-service.d.ts.map +1 -1
  323. package/dist/sandbox-training/promotion-service.js +2 -11
  324. package/dist/sandbox-training/promotion-service.js.map +1 -1
  325. package/dist/sandbox-training/runner.d.ts.map +1 -1
  326. package/dist/sandbox-training/runner.js +2 -8
  327. package/dist/sandbox-training/runner.js.map +1 -1
  328. package/dist/sandbox-training/scorer.d.ts.map +1 -1
  329. package/dist/sandbox-training/scorer.js +2 -8
  330. package/dist/sandbox-training/scorer.js.map +1 -1
  331. package/dist/sandbox-training/types.d.ts.map +1 -1
  332. package/dist/sandbox-training/types.js +2 -8
  333. package/dist/sandbox-training/types.js.map +1 -1
  334. package/dist/trust-engine/ceiling-enforcement/audit.d.ts +0 -8
  335. package/dist/trust-engine/ceiling-enforcement/audit.d.ts.map +1 -1
  336. package/dist/trust-engine/ceiling-enforcement/audit.js +2 -8
  337. package/dist/trust-engine/ceiling-enforcement/audit.js.map +1 -1
  338. package/dist/trust-engine/ceiling-enforcement/index.d.ts.map +1 -1
  339. package/dist/trust-engine/ceiling-enforcement/index.js +2 -0
  340. package/dist/trust-engine/ceiling-enforcement/index.js.map +1 -1
  341. package/dist/trust-engine/ceiling-enforcement/kernel.d.ts.map +1 -1
  342. package/dist/trust-engine/ceiling-enforcement/kernel.js +2 -0
  343. package/dist/trust-engine/ceiling-enforcement/kernel.js.map +1 -1
  344. package/dist/trust-engine/context-policy/enforcement.d.ts +0 -9
  345. package/dist/trust-engine/context-policy/enforcement.d.ts.map +1 -1
  346. package/dist/trust-engine/context-policy/enforcement.js +2 -9
  347. package/dist/trust-engine/context-policy/enforcement.js.map +1 -1
  348. package/dist/trust-engine/context-policy/factory.d.ts.map +1 -1
  349. package/dist/trust-engine/context-policy/factory.js +2 -0
  350. package/dist/trust-engine/context-policy/factory.js.map +1 -1
  351. package/dist/trust-engine/context-policy/index.d.ts.map +1 -1
  352. package/dist/trust-engine/context-policy/index.js +2 -0
  353. package/dist/trust-engine/context-policy/index.js.map +1 -1
  354. package/dist/trust-engine/creation-modifiers/index.d.ts.map +1 -1
  355. package/dist/trust-engine/creation-modifiers/index.js +2 -0
  356. package/dist/trust-engine/creation-modifiers/index.js.map +1 -1
  357. package/dist/trust-engine/creation-modifiers/types.d.ts.map +1 -1
  358. package/dist/trust-engine/creation-modifiers/types.js +2 -0
  359. package/dist/trust-engine/creation-modifiers/types.js.map +1 -1
  360. package/dist/trust-engine/decay-profiles.d.ts.map +1 -1
  361. package/dist/trust-engine/decay-profiles.js +2 -14
  362. package/dist/trust-engine/decay-profiles.js.map +1 -1
  363. package/dist/trust-engine/index.d.ts +418 -80
  364. package/dist/trust-engine/index.d.ts.map +1 -1
  365. package/dist/trust-engine/index.js +1048 -186
  366. package/dist/trust-engine/index.js.map +1 -1
  367. package/dist/trust-engine/phase6-types.d.ts +3 -13
  368. package/dist/trust-engine/phase6-types.d.ts.map +1 -1
  369. package/dist/trust-engine/phase6-types.js +5 -13
  370. package/dist/trust-engine/phase6-types.js.map +1 -1
  371. package/dist/trust-engine/trust-verifier.d.ts +121 -0
  372. package/dist/trust-engine/trust-verifier.d.ts.map +1 -0
  373. package/dist/trust-engine/trust-verifier.js +226 -0
  374. package/dist/trust-engine/trust-verifier.js.map +1 -0
  375. package/package.json +140 -135
@@ -1,15 +1,15 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright 2024-2026 Vorion LLC
1
3
  /**
2
4
  * Trust Engine - Behavioral Trust Scoring
3
5
  *
4
6
  * Calculates and maintains trust scores for entities based on behavioral signals.
5
- * Features 8-tier trust system (T0-T7) with event emission for observability.
7
+ * Features 6-tier trust system with event emission for observability.
6
8
  *
7
9
  * @packageDocumentation
8
10
  */
9
11
  import { EventEmitter } from 'events';
10
12
  import { createLogger } from '../common/logger.js';
11
- import { calculateDecayMultiplier } from './decay-profiles.js';
12
- import { FACTOR_CODE_LIST, DEFAULT_FACTOR_WEIGHTS, SIGNAL_PREFIX_TO_FACTORS as BASIS_SIGNAL_PREFIX_MAP, initialFactorScores, } from '@vorionsys/basis';
13
13
  const logger = createLogger({ component: 'trust-engine' });
14
14
  /**
15
15
  * Trust level thresholds (8 tiers T0-T7) - per BASIS specification
@@ -37,13 +37,7 @@ export const TRUST_LEVEL_NAMES = {
37
37
  6: 'Certified',
38
38
  7: 'Autonomous',
39
39
  };
40
- // Re-export canonical factor constants from @vorionsys/basis
41
- export const FACTOR_CODES = FACTOR_CODE_LIST;
42
- export const FACTOR_WEIGHTS = DEFAULT_FACTOR_WEIGHTS;
43
- export const SIGNAL_PREFIX_TO_FACTORS = BASIS_SIGNAL_PREFIX_MAP;
44
- export { initialFactorScores };
45
40
  /**
46
- * @deprecated Use FACTOR_WEIGHTS for 16-factor scoring. Kept for backwards compatibility.
47
41
  * Signal weights for score calculation
48
42
  */
49
43
  export const SIGNAL_WEIGHTS = {
@@ -52,12 +46,91 @@ export const SIGNAL_WEIGHTS = {
52
46
  identity: 0.2,
53
47
  context: 0.15,
54
48
  };
49
+ /**
50
+ * Penalty ratio per BASIS specification: P(T) = 3 + T
51
+ *
52
+ * Negative signals have their EWA weight multiplied by (3 + currentTier),
53
+ * so higher-tier agents are punished MORE severely for failures.
54
+ * T0 = 3×, T1 = 4×, T2 = 5×, T3 = 6×, T4 = 7×, T5 = 8×, T6 = 9×, T7 = 10×
55
+ *
56
+ * Only applies to negative signals (value < neutral 0.5). Positive signals
57
+ * are NOT amplified.
58
+ */
59
+ export const PENALTY_RATIO_BASE = 3;
60
+ /**
61
+ * Calculate the penalty weight multiplier for a given tier.
62
+ * P(T) = 3 + T
63
+ */
64
+ export function penaltyMultiplier(tier) {
65
+ return PENALTY_RATIO_BASE + tier;
66
+ }
67
+ /**
68
+ * 16-factor trust model — granular decomposition of the 4-dimension model.
69
+ * Each factor maps to one of the 4 TrustComponents dimensions.
70
+ */
71
+ export const FACTOR_CODES = [
72
+ 'CT-COMP', 'CT-REL', 'CT-OBS', 'CT-TRANS', 'CT-ACCT', 'CT-SAFE',
73
+ 'CT-SEC', 'CT-PRIV', 'CT-ID',
74
+ 'OP-HUMAN', 'OP-ALIGN', 'OP-CONTEXT',
75
+ 'OP-STEW', 'SF-HUM',
76
+ 'SF-ADAPT', 'SF-LEARN',
77
+ ];
78
+ export const FACTOR_WEIGHTS = {
79
+ 'CT-COMP': 0.08, 'CT-REL': 0.07, 'CT-OBS': 0.07, 'CT-TRANS': 0.06,
80
+ 'CT-ACCT': 0.06, 'CT-SAFE': 0.05, 'CT-SEC': 0.05, 'CT-PRIV': 0.04, 'CT-ID': 0.04,
81
+ 'OP-HUMAN': 0.08, 'OP-ALIGN': 0.08, 'OP-CONTEXT': 0.07, 'OP-STEW': 0.07,
82
+ 'SF-HUM': 0.07, 'SF-ADAPT': 0.06, 'SF-LEARN': 0.05,
83
+ };
84
+ /**
85
+ * Maps each 16-factor code to its parent TrustComponents dimension,
86
+ * enabling signal routing from factor-coded signal types (e.g. CT-COMP.perfect)
87
+ * to the correct component bucket for score calculation.
88
+ */
89
+ const FACTOR_TO_COMPONENT = {
90
+ 'CT-COMP': 'compliance', 'CT-REL': 'compliance', 'CT-OBS': 'compliance',
91
+ 'CT-TRANS': 'compliance', 'CT-ACCT': 'compliance',
92
+ 'CT-SAFE': 'identity', 'CT-SEC': 'identity', 'CT-PRIV': 'identity', 'CT-ID': 'identity',
93
+ 'OP-HUMAN': 'behavioral', 'OP-ALIGN': 'behavioral',
94
+ 'OP-CONTEXT': 'context', 'OP-STEW': 'context',
95
+ 'SF-HUM': 'behavioral', 'SF-ADAPT': 'behavioral', 'SF-LEARN': 'behavioral',
96
+ };
97
+ /**
98
+ * Canonical reason codes for time-bound readiness exceptions.
99
+ */
100
+ export const READINESS_EXCEPTION_REASON_CODES = [
101
+ 'approved_leave',
102
+ 'planned_maintenance',
103
+ 'telemetry_outage',
104
+ 'legal_hold',
105
+ 'incident_response',
106
+ 'dependency_outage',
107
+ ];
108
+ const DEFAULT_READINESS_CHECKPOINT_DAYS = [7, 14, 28, 42, 56, 84, 112, 140, 182];
109
+ const DEFAULT_READINESS_REDUCTIONS = [0.06, 0.06, 0.06, 0.06, 0.06, 0.05, 0.05, 0.05, 0.05];
110
+ const DEFAULT_READINESS_EXCEPTION_MAX_DURATION_MS = 30 * 24 * 60 * 60 * 1000;
111
+ function toMs(days) {
112
+ return days * 24 * 60 * 60 * 1000;
113
+ }
114
+ function clampReductionScale(value) {
115
+ if (value === undefined || Number.isNaN(value))
116
+ return 1;
117
+ if (value < 0)
118
+ return 0;
119
+ if (value > 1)
120
+ return 1;
121
+ return value;
122
+ }
55
123
  /**
56
124
  * Trust Engine service with event emission and subscription limits
57
125
  */
58
126
  export class TrustEngine extends EventEmitter {
59
127
  records = new Map();
60
- _decayCheckIntervalMs;
128
+ _decayRate;
129
+ _decayIntervalMs;
130
+ _failureThreshold;
131
+ _acceleratedDecayMultiplier;
132
+ _failureWindowMs;
133
+ _minFailuresForAcceleration;
61
134
  _persistence;
62
135
  _autoPersist;
63
136
  // Recovery configuration
@@ -73,9 +146,41 @@ export class TrustEngine extends EventEmitter {
73
146
  _listenerWarningThreshold;
74
147
  _listenerCounts = new Map();
75
148
  _totalListeners = 0;
149
+ _readinessMode;
150
+ _readinessCheckpointDays;
151
+ _readinessCheckpointReductions;
152
+ _allowedReadinessExceptionReasons;
153
+ _readinessExceptionMaxDurationMs;
154
+ // Signal deduplication state
155
+ _signalDedupEnabled;
156
+ _signalDedupDecayFactor;
157
+ _signalDedupWindowSize;
158
+ _signalDedupWindowMs;
159
+ _signalDedupMinFactor;
160
+ /**
161
+ * Per-entity dedup tracker. Each entry holds recent signal type records
162
+ * used to compute diminishing returns for repeated identical signal types.
163
+ */
164
+ _dedupState = new Map();
165
+ // Incremental scoring cache — avoids O(n) recalculation on every signal.
166
+ // Cache is engine-side (not serialized into TrustRecord) and is rebuilt on load or every N signals.
167
+ _scoreCache = new Map();
168
+ /** Number of signals between forced full recalculations for drift correction */
169
+ static INCREMENTAL_RECALC_INTERVAL = 100;
170
+ // ParameSphere integration
171
+ _paramesphere;
172
+ _cognitiveEnvelope;
173
+ _fingerprintBaselines = new Map();
174
+ // Trust verifier (optional cryptographic state commitment)
175
+ _verifier;
76
176
  constructor(config = {}) {
77
177
  super();
78
- this._decayCheckIntervalMs = config.decayCheckIntervalMs ?? 60000;
178
+ this._decayRate = config.decayRate ?? 0.01;
179
+ this._decayIntervalMs = config.decayCheckIntervalMs ?? config.decayIntervalMs ?? 60000;
180
+ this._failureThreshold = config.failureThreshold ?? 0.3;
181
+ this._acceleratedDecayMultiplier = config.acceleratedDecayMultiplier ?? 1.0;
182
+ this._failureWindowMs = config.failureWindowMs ?? 3600000; // 1 hour
183
+ this._minFailuresForAcceleration = config.minFailuresForAcceleration ?? 2;
79
184
  this._persistence = config.persistence;
80
185
  this._autoPersist = config.autoPersist ?? (config.persistence !== undefined);
81
186
  // Recovery configuration
@@ -89,9 +194,209 @@ export class TrustEngine extends EventEmitter {
89
194
  this._maxListenersPerEvent = config.maxListenersPerEvent ?? 100;
90
195
  this._maxTotalListeners = config.maxTotalListeners ?? 1000;
91
196
  this._listenerWarningThreshold = config.listenerWarningThreshold ?? 0.8;
197
+ this._readinessMode = config.readinessMode ?? config.freshnessMode ?? (config.decayIntervalMs !== undefined || config.decayRate !== undefined
198
+ ? 'legacy_interval'
199
+ : 'checkpoint_schedule');
200
+ this._readinessCheckpointDays =
201
+ config.readinessCheckpointDays ?? config.freshnessCheckpointDays ?? [...DEFAULT_READINESS_CHECKPOINT_DAYS];
202
+ this._readinessCheckpointReductions =
203
+ config.readinessCheckpointReductions ?? config.freshnessCheckpointReductions ?? [...DEFAULT_READINESS_REDUCTIONS];
204
+ if (this._readinessCheckpointDays.length !== this._readinessCheckpointReductions.length) {
205
+ throw new Error('freshnessCheckpointDays and freshnessCheckpointReductions must have equal length');
206
+ }
207
+ this._allowedReadinessExceptionReasons = new Set(config.readinessExceptionAllowedReasons ??
208
+ config.freshnessExceptionAllowedReasons ??
209
+ [...READINESS_EXCEPTION_REASON_CODES]);
210
+ this._readinessExceptionMaxDurationMs =
211
+ config.readinessExceptionMaxDurationMs ??
212
+ config.freshnessExceptionMaxDurationMs ??
213
+ DEFAULT_READINESS_EXCEPTION_MAX_DURATION_MS;
214
+ // Signal deduplication configuration
215
+ this._signalDedupEnabled = config.signalDedupEnabled ?? true;
216
+ this._signalDedupDecayFactor = config.signalDedupDecayFactor ?? 0.5;
217
+ this._signalDedupWindowSize = config.signalDedupWindowSize ?? 10;
218
+ this._signalDedupWindowMs = config.signalDedupWindowMs ?? 3600000; // 1 hour
219
+ this._signalDedupMinFactor = config.signalDedupMinFactor ?? 0.0625; // 1/16
220
+ // ParameSphere integration
221
+ this._paramesphere = config.paramesphere;
222
+ this._cognitiveEnvelope = config.cognitiveEnvelope;
223
+ this._verifier = config.verifier;
92
224
  // Set default max listeners on EventEmitter
93
225
  this.setMaxListeners(this._maxListenersPerEvent);
94
226
  }
227
+ /** Decay check interval in milliseconds */
228
+ get decayCheckIntervalMs() {
229
+ return this._decayIntervalMs;
230
+ }
231
+ validateReadinessExceptionOptions(options) {
232
+ if (!this._allowedReadinessExceptionReasons.has(options.reason)) {
233
+ throw new Error(`Unsupported readiness exception reason: ${options.reason}. ` +
234
+ `Allowed reasons: ${Array.from(this._allowedReadinessExceptionReasons).join(', ')}`);
235
+ }
236
+ const now = Date.now();
237
+ const expiresAtMs = new Date(options.expiresAt).getTime();
238
+ if (Number.isNaN(expiresAtMs)) {
239
+ throw new Error('Invalid expiresAt timestamp for readiness exception');
240
+ }
241
+ if (expiresAtMs <= now) {
242
+ throw new Error('Readiness exception expiresAt must be in the future');
243
+ }
244
+ if (expiresAtMs - now > this._readinessExceptionMaxDurationMs) {
245
+ throw new Error(`Readiness exception duration exceeds configured maximum (${this._readinessExceptionMaxDurationMs} ms)`);
246
+ }
247
+ }
248
+ getCheckpointIntervalMs(index) {
249
+ if (index <= 0) {
250
+ return toMs(this._readinessCheckpointDays[0] ?? 7);
251
+ }
252
+ const current = this._readinessCheckpointDays[index] ?? this._readinessCheckpointDays[this._readinessCheckpointDays.length - 1] ?? 7;
253
+ const previous = this._readinessCheckpointDays[index - 1] ?? 0;
254
+ return toMs(Math.max(1, current - previous));
255
+ }
256
+ ensureReadinessState(record) {
257
+ record.readinessCheckpointIndex ??= record.freshnessCheckpointIndex ?? 0;
258
+ record.deferredReadinessMultiplier ??= record.deferredFreshnessMultiplier ?? 1;
259
+ record.readinessBaselineScore ??= record.freshnessBaselineScore ?? record.score;
260
+ record.freshnessCheckpointIndex = record.readinessCheckpointIndex;
261
+ record.deferredFreshnessMultiplier = record.deferredReadinessMultiplier;
262
+ record.freshnessBaselineScore = record.readinessBaselineScore;
263
+ record.freshnessException = record.readinessException ?? record.freshnessException;
264
+ }
265
+ isUsingDefaultReadinessSchedule() {
266
+ if (this._readinessCheckpointDays.length !== DEFAULT_READINESS_CHECKPOINT_DAYS.length) {
267
+ return false;
268
+ }
269
+ if (this._readinessCheckpointReductions.length !== DEFAULT_READINESS_REDUCTIONS.length) {
270
+ return false;
271
+ }
272
+ return this._readinessCheckpointDays.every((v, i) => v === DEFAULT_READINESS_CHECKPOINT_DAYS[i]) &&
273
+ this._readinessCheckpointReductions.every((v, i) => v === DEFAULT_READINESS_REDUCTIONS[i]);
274
+ }
275
+ isReadinessExceptionActive(record, now) {
276
+ const exception = record.readinessException ?? record.freshnessException;
277
+ if (!exception)
278
+ return false;
279
+ const issuedAt = new Date(exception.issuedAt).getTime();
280
+ const expiresAt = new Date(exception.expiresAt).getTime();
281
+ return now >= issuedAt && now < expiresAt;
282
+ }
283
+ async applyDeferredReadinessCatchupIfExpired(record) {
284
+ const exception = record.readinessException ?? record.freshnessException;
285
+ if (!exception)
286
+ return;
287
+ const now = Date.now();
288
+ const expiresAt = new Date(exception.expiresAt).getTime();
289
+ if (now < expiresAt)
290
+ return;
291
+ this.ensureReadinessState(record);
292
+ const deferredMultiplier = record.deferredReadinessMultiplier ?? 1;
293
+ if (deferredMultiplier < 1) {
294
+ const previousScore = record.score;
295
+ const adjustedScore = Math.max(0, Math.round(record.score * deferredMultiplier));
296
+ record.score = adjustedScore;
297
+ record.level = this.scoreToLevel(adjustedScore);
298
+ record.deferredReadinessMultiplier = 1;
299
+ record.deferredFreshnessMultiplier = 1;
300
+ this.emitReadinessAdjustmentEvents(record.entityId, {
301
+ previousScore,
302
+ newScore: record.score,
303
+ stalenessMs: 0,
304
+ accelerated: false,
305
+ });
306
+ }
307
+ delete record.readinessException;
308
+ delete record.freshnessException;
309
+ }
310
+ async applyScheduledReadinessAdjustment(record) {
311
+ this.ensureReadinessState(record);
312
+ await this.applyDeferredReadinessCatchupIfExpired(record);
313
+ while ((record.readinessCheckpointIndex ?? 0) < this._readinessCheckpointDays.length) {
314
+ const checkpointIndex = record.readinessCheckpointIndex ?? 0;
315
+ const intervalMs = this.getCheckpointIntervalMs(checkpointIndex);
316
+ const stalenessMs = Date.now() - new Date(record.lastCalculatedAt).getTime();
317
+ if (stalenessMs < intervalMs) {
318
+ break;
319
+ }
320
+ const previousScore = record.score;
321
+ const previousLevel = record.level;
322
+ const fullReduction = this._readinessCheckpointReductions[checkpointIndex] ?? 0;
323
+ const activeException = this.isReadinessExceptionActive(record, Date.now());
324
+ const scale = activeException ? clampReductionScale((record.readinessException ?? record.freshnessException)?.reductionScale) : 1;
325
+ const appliedReduction = fullReduction * scale;
326
+ const appliedMultiplier = 1 - appliedReduction;
327
+ const fullMultiplier = 1 - fullReduction;
328
+ record.score = Math.max(0, Math.round(record.score * appliedMultiplier));
329
+ const isFinalCheckpoint = checkpointIndex === this._readinessCheckpointDays.length - 1;
330
+ if (isFinalCheckpoint && !activeException && this.isUsingDefaultReadinessSchedule()) {
331
+ const baseline = record.readinessBaselineScore ?? record.freshnessBaselineScore ?? record.score;
332
+ record.score = Math.max(0, Math.round(baseline * 0.5));
333
+ }
334
+ record.level = this.scoreToLevel(record.score);
335
+ record.readinessCheckpointIndex = checkpointIndex + 1;
336
+ record.freshnessCheckpointIndex = record.readinessCheckpointIndex;
337
+ const lastCalculatedMs = new Date(record.lastCalculatedAt).getTime();
338
+ record.lastCalculatedAt = new Date(lastCalculatedMs + intervalMs).toISOString();
339
+ if (activeException && appliedMultiplier > 0 && fullMultiplier >= 0) {
340
+ const debtFactor = fullMultiplier / appliedMultiplier;
341
+ record.deferredReadinessMultiplier = (record.deferredReadinessMultiplier ?? 1) * debtFactor;
342
+ record.deferredFreshnessMultiplier = record.deferredReadinessMultiplier;
343
+ }
344
+ if (previousScore !== record.score) {
345
+ this.emitReadinessAdjustmentEvents(record.entityId, {
346
+ previousScore,
347
+ newScore: record.score,
348
+ stalenessMs,
349
+ accelerated: false,
350
+ });
351
+ if (previousLevel !== record.level) {
352
+ this.emitTrustEvent({
353
+ type: 'trust:tier_changed',
354
+ entityId: record.entityId,
355
+ timestamp: new Date().toISOString(),
356
+ previousLevel,
357
+ newLevel: record.level,
358
+ previousLevelName: TRUST_LEVEL_NAMES[previousLevel],
359
+ newLevelName: TRUST_LEVEL_NAMES[record.level],
360
+ direction: record.level < previousLevel ? 'demoted' : 'promoted',
361
+ });
362
+ }
363
+ await this.autoPersistRecord(record);
364
+ }
365
+ }
366
+ }
367
+ emitReadinessAdjustmentEvents(entityId, details) {
368
+ const adjustmentAmount = details.previousScore - details.newScore;
369
+ this.emitTrustEvent({
370
+ type: 'trust:readiness_adjusted',
371
+ entityId,
372
+ timestamp: new Date().toISOString(),
373
+ previousScore: details.previousScore,
374
+ newScore: details.newScore,
375
+ adjustmentAmount,
376
+ stalenessMs: details.stalenessMs,
377
+ accelerated: details.accelerated,
378
+ });
379
+ this.emitTrustEvent({
380
+ type: 'trust:freshness_adjusted',
381
+ entityId,
382
+ timestamp: new Date().toISOString(),
383
+ previousScore: details.previousScore,
384
+ newScore: details.newScore,
385
+ adjustmentAmount,
386
+ stalenessMs: details.stalenessMs,
387
+ accelerated: details.accelerated,
388
+ });
389
+ this.emitTrustEvent({
390
+ type: 'trust:decay_applied',
391
+ entityId,
392
+ timestamp: new Date().toISOString(),
393
+ previousScore: details.previousScore,
394
+ newScore: details.newScore,
395
+ decayAmount: adjustmentAmount,
396
+ stalenessMs: details.stalenessMs,
397
+ accelerated: details.accelerated,
398
+ });
399
+ }
95
400
  /**
96
401
  * Add event listener with subscription limits
97
402
  * @throws Error if listener limits are exceeded
@@ -198,10 +503,28 @@ export class TrustEngine extends EventEmitter {
198
503
  };
199
504
  }
200
505
  /**
201
- * Get the decay check interval in milliseconds
506
+ * Get the current decay rate
202
507
  */
203
- get decayCheckIntervalMs() {
204
- return this._decayCheckIntervalMs;
508
+ get decayRate() {
509
+ return this._decayRate;
510
+ }
511
+ /**
512
+ * Get the decay interval in milliseconds
513
+ */
514
+ get decayIntervalMs() {
515
+ return this._decayIntervalMs;
516
+ }
517
+ /**
518
+ * Get the failure threshold
519
+ */
520
+ get failureThreshold() {
521
+ return this._failureThreshold;
522
+ }
523
+ /**
524
+ * Get the accelerated decay multiplier
525
+ */
526
+ get acceleratedDecayMultiplier() {
527
+ return this._acceleratedDecayMultiplier;
205
528
  }
206
529
  /**
207
530
  * Get the persistence provider
@@ -236,6 +559,7 @@ export class TrustEngine extends EventEmitter {
236
559
  }
237
560
  const records = await this._persistence.query();
238
561
  this.records.clear();
562
+ this._scoreCache.clear(); // Invalidate incremental caches on reload
239
563
  for (const record of records) {
240
564
  this.records.set(record.entityId, record);
241
565
  }
@@ -265,6 +589,122 @@ export class TrustEngine extends EventEmitter {
265
589
  await this._persistence.save(record);
266
590
  }
267
591
  }
592
+ // -----------------------------------------------------------------------
593
+ // ParameSphere Integration
594
+ // -----------------------------------------------------------------------
595
+ /**
596
+ * Check an entity's model fingerprint for drift.
597
+ * If drift exceeds threshold, automatically records a negative trust signal.
598
+ * Returns the drift result.
599
+ */
600
+ async checkFingerprint(entityId, weights, activations) {
601
+ if (!this._paramesphere) {
602
+ return null;
603
+ }
604
+ const current = this._paramesphere.computeFingerprint(weights, activations);
605
+ const baseline = this._fingerprintBaselines.get(entityId);
606
+ if (!baseline) {
607
+ // First call — store as baseline
608
+ this._fingerprintBaselines.set(entityId, current);
609
+ this.emitTrustEvent({
610
+ type: 'trust:fingerprint_baseline_set',
611
+ entityId,
612
+ timestamp: new Date().toISOString(),
613
+ fingerprintSha256: current.sha256,
614
+ });
615
+ logger.info({ entityId, sha256: current.sha256 }, 'Fingerprint baseline set');
616
+ return { drifted: false, similarity: 1.0, magnitude: 0 };
617
+ }
618
+ // Compare against baseline
619
+ const comparison = this._paramesphere.compareFingerprints(baseline, current);
620
+ const similarity = comparison.cosineSimilarity;
621
+ const magnitude = comparison.l2Distance;
622
+ const drifted = comparison.driftDetected;
623
+ if (drifted) {
624
+ // Auto-record a negative signal — maximum negative triggers full asymmetric penalty
625
+ await this.recordSignal({
626
+ id: `paramesphere-drift-${entityId}-${Date.now()}`,
627
+ entityId,
628
+ type: 'paramesphere:drift',
629
+ value: 0.0,
630
+ source: 'system:paramesphere',
631
+ timestamp: new Date().toISOString(),
632
+ metadata: {
633
+ similarity,
634
+ magnitude,
635
+ driftThreshold: comparison.cosineDistance,
636
+ cosineDistance: comparison.cosineDistance,
637
+ maxSingularValueDelta: comparison.maxSingularValueDelta,
638
+ },
639
+ });
640
+ this.emitTrustEvent({
641
+ type: 'trust:fingerprint_drift',
642
+ entityId,
643
+ timestamp: new Date().toISOString(),
644
+ similarity,
645
+ magnitude,
646
+ driftThreshold: comparison.cosineDistance,
647
+ });
648
+ logger.warn({ entityId, similarity, magnitude, cosineDistance: comparison.cosineDistance }, 'Fingerprint drift detected — negative trust signal recorded');
649
+ }
650
+ return { drifted, similarity, magnitude };
651
+ }
652
+ /**
653
+ * Set or update the fingerprint baseline for an entity.
654
+ * Call after initial model load or after a verified safe update.
655
+ */
656
+ async setFingerprintBaseline(entityId, weights, activations) {
657
+ if (!this._paramesphere) {
658
+ return;
659
+ }
660
+ const fingerprint = this._paramesphere.computeFingerprint(weights, activations);
661
+ this._fingerprintBaselines.set(entityId, fingerprint);
662
+ this.emitTrustEvent({
663
+ type: 'trust:fingerprint_baseline_set',
664
+ entityId,
665
+ timestamp: new Date().toISOString(),
666
+ fingerprintSha256: fingerprint.sha256,
667
+ });
668
+ logger.info({ entityId, sha256: fingerprint.sha256 }, 'Fingerprint baseline set');
669
+ }
670
+ /**
671
+ * Check if entity behavioral signals are within cognitive envelope bounds.
672
+ * If breach detected, records a negative trust signal with multiplier.
673
+ */
674
+ async checkCognitiveEnvelope(entityId, observation) {
675
+ if (!this._cognitiveEnvelope) {
676
+ return null;
677
+ }
678
+ const result = this._cognitiveEnvelope.checkBreach(observation);
679
+ const breached = result.breached;
680
+ if (breached) {
681
+ // Auto-record a negative signal for envelope breach
682
+ await this.recordSignal({
683
+ id: `paramesphere-envelope-${entityId}-${Date.now()}`,
684
+ entityId,
685
+ type: 'paramesphere:envelope_breach',
686
+ value: 0.0,
687
+ source: 'system:paramesphere',
688
+ timestamp: new Date().toISOString(),
689
+ metadata: {
690
+ maxDeviation: result.maxDeviation,
691
+ trustMultiplier: result.trustMultiplier,
692
+ breachCounter: result.breachCounter,
693
+ dimensionBreaches: result.dimensionBreaches,
694
+ },
695
+ });
696
+ this.emitTrustEvent({
697
+ type: 'trust:envelope_breach',
698
+ entityId,
699
+ timestamp: new Date().toISOString(),
700
+ distance: result.maxDeviation,
701
+ trustMultiplier: result.trustMultiplier,
702
+ breachCounter: result.breachCounter,
703
+ });
704
+ logger.warn({ entityId, maxDeviation: result.maxDeviation, breachCounter: result.breachCounter }, 'Cognitive envelope breach detected — negative trust signal recorded');
705
+ }
706
+ return { breached, distance: result.maxDeviation };
707
+ }
268
708
  /**
269
709
  * Close the trust engine and persistence provider
270
710
  */
@@ -272,6 +712,7 @@ export class TrustEngine extends EventEmitter {
272
712
  if (this._persistence) {
273
713
  await this._persistence.close();
274
714
  }
715
+ this._scoreCache.clear();
275
716
  this.removeAllListeners();
276
717
  }
277
718
  /**
@@ -288,34 +729,41 @@ export class TrustEngine extends EventEmitter {
288
729
  async calculate(entityId) {
289
730
  const record = this.records.get(entityId);
290
731
  const signals = record?.signals ?? [];
291
- // Calculate factor scores (16-factor model)
292
- const factorScores = this.calculateFactorScores(signals);
293
- // Calculate weighted total using factor weights
294
- let score = 0;
295
- for (const code of FACTOR_CODES) {
296
- score += factorScores[code] * FACTOR_WEIGHTS[code] * 1000;
297
- }
298
- score = Math.round(score);
299
- // Guard: if factor arithmetic produced NaN/Infinity, fall back to 0
300
- if (!Number.isFinite(score)) {
301
- logger.warn({ entityId, score }, 'Score computation produced non-finite value, defaulting to 0');
302
- score = 0;
303
- }
732
+ const currentLevel = record?.level ?? 1;
733
+ // Calculate component scores
734
+ const components = this.calculateComponents(signals, currentLevel);
735
+ // Calculate weighted total
736
+ const score = Math.round(components.behavioral * SIGNAL_WEIGHTS.behavioral * 1000 +
737
+ components.compliance * SIGNAL_WEIGHTS.compliance * 1000 +
738
+ components.identity * SIGNAL_WEIGHTS.identity * 1000 +
739
+ components.context * SIGNAL_WEIGHTS.context * 1000);
304
740
  // Clamp to valid range
305
741
  const clampedScore = Math.max(0, Math.min(1000, score));
306
742
  const level = this.scoreToLevel(clampedScore);
307
- // Backwards compat: also compute legacy 4-bucket components
308
- const components = this.calculateComponents(signals);
309
743
  const factors = this.getSignificantFactors(components);
310
- logger.debug({ entityId, score: clampedScore, level, factorScores }, 'Trust calculated');
744
+ logger.debug({ entityId, score: clampedScore, level, components }, 'Trust calculated');
311
745
  return {
312
746
  score: clampedScore,
313
747
  level,
314
748
  components,
315
- factorScores,
316
749
  factors,
317
750
  };
318
751
  }
752
+ /**
753
+ * Check if an entity has accelerated decay active
754
+ */
755
+ hasAcceleratedDecay(record) {
756
+ const now = Date.now();
757
+ const recentFailures = record.recentFailures.filter((timestamp) => now - new Date(timestamp).getTime() < this._failureWindowMs);
758
+ return recentFailures.length >= this._minFailuresForAcceleration;
759
+ }
760
+ /**
761
+ * Clean up old failure timestamps outside the window
762
+ */
763
+ cleanupFailures(record) {
764
+ const now = Date.now();
765
+ record.recentFailures = record.recentFailures.filter((timestamp) => now - new Date(timestamp).getTime() < this._failureWindowMs);
766
+ }
319
767
  /**
320
768
  * Clean up old success timestamps outside the window
321
769
  */
@@ -420,14 +868,25 @@ export class TrustEngine extends EventEmitter {
420
868
  async getScore(entityId) {
421
869
  const record = this.records.get(entityId);
422
870
  if (record) {
423
- // Apply decay if enough time has passed since last check
871
+ // Clean up old failures
872
+ this.cleanupFailures(record);
873
+ if (this._readinessMode === 'checkpoint_schedule') {
874
+ await this.applyScheduledReadinessAdjustment(record);
875
+ return record;
876
+ }
877
+ // Apply decay if stale
424
878
  const staleness = Date.now() - new Date(record.lastCalculatedAt).getTime();
425
- if (staleness > this._decayCheckIntervalMs) {
879
+ if (staleness > this._decayIntervalMs) {
426
880
  const previousScore = record.score;
427
881
  const previousLevel = record.level;
428
- // Milestone-based decay: convert staleness to days, apply stepped multiplier
429
- const daysSinceActivity = staleness / (24 * 60 * 60 * 1000);
430
- const decayMultiplier = calculateDecayMultiplier(daysSinceActivity);
882
+ // Check if accelerated decay should apply
883
+ const accelerated = this.hasAcceleratedDecay(record);
884
+ const effectiveDecayRate = accelerated
885
+ ? this._decayRate * this._acceleratedDecayMultiplier
886
+ : this._decayRate;
887
+ // Apply decay based on staleness
888
+ const decayPeriods = Math.floor(staleness / this._decayIntervalMs);
889
+ const decayMultiplier = Math.pow(1 - effectiveDecayRate, decayPeriods);
431
890
  const decayedScore = Math.round(record.score * decayMultiplier);
432
891
  const clampedScore = Math.max(0, decayedScore);
433
892
  record.score = clampedScore;
@@ -443,6 +902,7 @@ export class TrustEngine extends EventEmitter {
443
902
  newScore: record.score,
444
903
  decayAmount: previousScore - record.score,
445
904
  stalenessMs: staleness,
905
+ accelerated,
446
906
  });
447
907
  // Emit tier change if applicable
448
908
  if (previousLevel !== record.level) {
@@ -465,34 +925,25 @@ export class TrustEngine extends EventEmitter {
465
925
  return record;
466
926
  }
467
927
  /**
468
- * Validate a trust signal before ingestion.
469
- * Rejects NaN, Infinity, out-of-range values, and missing required fields.
470
- *
471
- * @throws Error on invalid signal
928
+ * Record a trust signal
472
929
  */
473
- validateSignal(signal) {
474
- if (!signal) {
930
+ async recordSignal(signal) {
931
+ // Input validation
932
+ if (signal == null) {
475
933
  throw new Error('Signal is required');
476
934
  }
477
935
  if (!signal.entityId) {
478
- throw new Error('Signal must have an entityId');
936
+ throw new Error('entityId is required');
479
937
  }
480
- if (!signal.type || typeof signal.type !== 'string') {
481
- throw new Error('Signal must have a non-empty string type');
938
+ if (!signal.type) {
939
+ throw new Error('type is required');
482
940
  }
483
- if (!Number.isFinite(signal.value)) {
484
- throw new Error(`Invalid signal value: ${signal.value} (must be a finite number)`);
941
+ if (typeof signal.value !== 'number' || !Number.isFinite(signal.value)) {
942
+ throw new Error('Invalid signal value');
485
943
  }
486
944
  if (signal.value < 0 || signal.value > 1) {
487
- throw new Error(`Signal value out of range: ${signal.value} (must be 0.0–1.0)`);
945
+ throw new Error('Signal value out of range');
488
946
  }
489
- }
490
- /**
491
- * Record a trust signal
492
- * @throws Error if signal is invalid (NaN, out of range, missing fields)
493
- */
494
- async recordSignal(signal) {
495
- this.validateSignal(signal);
496
947
  let record = this.records.get(signal.entityId);
497
948
  let isNewEntity = false;
498
949
  if (!record) {
@@ -500,12 +951,53 @@ export class TrustEngine extends EventEmitter {
500
951
  this.records.set(signal.entityId, record);
501
952
  isNewEntity = true;
502
953
  }
954
+ // Apply signal deduplication (diminishing returns for repeated signal types)
955
+ const dedupResult = this.applySignalDedup(signal);
956
+ if (dedupResult.adjusted) {
957
+ signal = { ...signal, value: dedupResult.value };
958
+ this.emitTrustEvent({
959
+ type: 'trust:signal_deduplicated',
960
+ entityId: signal.entityId,
961
+ timestamp: new Date().toISOString(),
962
+ signal,
963
+ originalValue: dedupResult.originalValue,
964
+ adjustedValue: dedupResult.value,
965
+ diminishingFactor: dedupResult.factor,
966
+ repeatCount: dedupResult.repeatCount,
967
+ });
968
+ logger.info({
969
+ entityId: signal.entityId,
970
+ signalType: signal.type,
971
+ originalValue: dedupResult.originalValue,
972
+ adjustedValue: dedupResult.value,
973
+ diminishingFactor: dedupResult.factor,
974
+ repeatCount: dedupResult.repeatCount,
975
+ }, 'Signal deduplication applied — diminishing returns for repeated signal type');
976
+ }
503
977
  const previousScore = record.score;
504
978
  const previousLevel = record.level;
505
- // Reset consecutive successes on low-value signals (below 0.3)
506
- // Neutral signals (0.3-0.7) don't break the success streak
507
- if (signal.value < 0.3) {
979
+ // Detect failure signals
980
+ if (signal.value < this._failureThreshold) {
981
+ record.recentFailures.push(signal.timestamp);
982
+ this.cleanupFailures(record);
983
+ // Reset consecutive successes on failure
508
984
  record.consecutiveSuccesses = 0;
985
+ const acceleratedDecayActive = this.hasAcceleratedDecay(record);
986
+ this.emitTrustEvent({
987
+ type: 'trust:failure_detected',
988
+ entityId: signal.entityId,
989
+ timestamp: new Date().toISOString(),
990
+ signal,
991
+ failureCount: record.recentFailures.length,
992
+ acceleratedDecayActive,
993
+ });
994
+ logger.warn({
995
+ entityId: signal.entityId,
996
+ signalType: signal.type,
997
+ signalValue: signal.value,
998
+ failureCount: record.recentFailures.length,
999
+ acceleratedDecayActive,
1000
+ }, 'Failure signal detected');
509
1001
  }
510
1002
  // Detect success signals and apply recovery
511
1003
  if (signal.value >= this._successThreshold) {
@@ -528,17 +1020,38 @@ export class TrustEngine extends EventEmitter {
528
1020
  // Add signal
529
1021
  record.signals.push(signal);
530
1022
  // Keep only recent signals (last 1000)
1023
+ // If we sliced, invalidate the cache since the signal array changed
531
1024
  if (record.signals.length > 1000) {
532
1025
  record.signals = record.signals.slice(-1000);
1026
+ this._scoreCache.delete(signal.entityId);
1027
+ }
1028
+ // Recalculate — use incremental O(1) path when cache is fresh,
1029
+ // otherwise fall back to full O(n) recalculation and rebuild the cache.
1030
+ let calculation;
1031
+ const existingCache = this._scoreCache.get(signal.entityId);
1032
+ const signalsSinceFullRecalc = existingCache
1033
+ ? record.signals.length - existingCache.lastFullRecalcAt
1034
+ : Infinity;
1035
+ if (existingCache && signalsSinceFullRecalc < TrustEngine.INCREMENTAL_RECALC_INTERVAL) {
1036
+ // O(1) incremental update
1037
+ calculation = this.incrementalUpdate(signal, record.level);
1038
+ }
1039
+ else {
1040
+ // Full O(n) recalculation + cache rebuild
1041
+ calculation = await this.calculate(signal.entityId);
1042
+ this.buildCacheFromSignals(record.signals, record.level, signal.entityId);
533
1043
  }
534
- // Recalculate
535
- const calculation = await this.calculate(signal.entityId);
536
1044
  // Update record
537
1045
  record.score = calculation.score;
538
1046
  record.level = calculation.level;
539
1047
  record.components = calculation.components;
540
- record.factorScores = calculation.factorScores;
541
1048
  record.lastCalculatedAt = new Date().toISOString();
1049
+ record.readinessCheckpointIndex = 0;
1050
+ record.deferredReadinessMultiplier = 1;
1051
+ record.readinessBaselineScore = calculation.score;
1052
+ record.freshnessCheckpointIndex = 0;
1053
+ record.deferredFreshnessMultiplier = 1;
1054
+ record.freshnessBaselineScore = calculation.score;
542
1055
  // Record history if significant change
543
1056
  if (Math.abs(calculation.score - previousScore) >= 10) {
544
1057
  record.history.push({
@@ -586,6 +1099,8 @@ export class TrustEngine extends EventEmitter {
586
1099
  direction: calculation.level > previousLevel ? 'promoted' : 'demoted',
587
1100
  });
588
1101
  }
1102
+ // Commit state to verifier if configured
1103
+ this._verifier?.commitState(signal.entityId, calculation.score, calculation.level, record.signals.length);
589
1104
  // Auto-persist if enabled
590
1105
  await this.autoPersistRecord(record);
591
1106
  logger.debug({
@@ -599,11 +1114,6 @@ export class TrustEngine extends EventEmitter {
599
1114
  */
600
1115
  async initializeEntity(entityId, initialLevel = 1) {
601
1116
  const score = TRUST_THRESHOLDS[initialLevel].min;
602
- // Initialize all 16 factors to 0.5
603
- const initialFactorScores = {};
604
- for (const code of FACTOR_CODES) {
605
- initialFactorScores[code] = 0.5;
606
- }
607
1117
  const record = {
608
1118
  entityId,
609
1119
  score,
@@ -614,7 +1124,6 @@ export class TrustEngine extends EventEmitter {
614
1124
  identity: 0.5,
615
1125
  context: 0.5,
616
1126
  },
617
- factorScores: initialFactorScores,
618
1127
  signals: [],
619
1128
  lastCalculatedAt: new Date().toISOString(),
620
1129
  history: [
@@ -625,11 +1134,20 @@ export class TrustEngine extends EventEmitter {
625
1134
  timestamp: new Date().toISOString(),
626
1135
  },
627
1136
  ],
1137
+ recentFailures: [],
628
1138
  recentSuccesses: [],
629
1139
  peakScore: score,
630
1140
  consecutiveSuccesses: 0,
1141
+ readinessCheckpointIndex: 0,
1142
+ deferredReadinessMultiplier: 1,
1143
+ readinessBaselineScore: score,
1144
+ freshnessCheckpointIndex: 0,
1145
+ deferredFreshnessMultiplier: 1,
1146
+ freshnessBaselineScore: score,
631
1147
  };
632
1148
  this.records.set(entityId, record);
1149
+ // Commit initial state to verifier if configured
1150
+ this._verifier?.commitState(entityId, score, initialLevel, 0);
633
1151
  // Auto-persist if enabled
634
1152
  await this.autoPersistRecord(record);
635
1153
  // Emit initialized event
@@ -644,87 +1162,70 @@ export class TrustEngine extends EventEmitter {
644
1162
  return record;
645
1163
  }
646
1164
  /**
647
- * Get all entity IDs
648
- */
649
- getEntityIds() {
650
- return Array.from(this.records.keys());
651
- }
652
- /**
653
- * Get trust level name
654
- */
655
- getLevelName(level) {
656
- return TRUST_LEVEL_NAMES[level];
657
- }
658
- /**
659
- * Generate a human-readable explanation of an entity's current trust score.
660
- *
661
- * Provides full transparency into how the score was computed, including
662
- * per-factor contributions, decay status, and what's needed to advance.
663
- *
664
- * @example
665
- * ```ts
666
- * const explanation = await engine.explainScore('agent-123');
667
- * console.log(`Score: ${explanation.score} (${explanation.levelName})`);
668
- * console.log(`Top factor: ${explanation.topPositiveFactors[0]}`);
669
- * console.log(`Points to next tier: ${explanation.pointsToNextLevel}`);
670
- * ```
1165
+ * Explain a trust score with full factor breakdown
671
1166
  */
672
1167
  async explainScore(entityId) {
673
1168
  const record = this.records.get(entityId);
674
1169
  if (!record) {
675
- throw new Error(`Entity not found: ${entityId}`);
1170
+ throw new Error('Entity not found');
676
1171
  }
677
- const signals = record.signals ?? [];
678
- const factorScores = this.calculateFactorScores(signals);
679
- // Build factor breakdown
1172
+ const level = record.level;
1173
+ const levelName = TRUST_LEVEL_NAMES[level] ?? 'Unknown';
1174
+ const levelRange = TRUST_THRESHOLDS[level];
1175
+ // Build factor breakdown from recorded signals
680
1176
  const factorBreakdown = FACTOR_CODES.map((code) => {
681
- const weight = FACTOR_WEIGHTS[code];
682
- const rawScore = factorScores[code];
683
- const contribution = Math.round(rawScore * weight * 1000 * 100) / 100;
684
- return { code, weight, rawScore, contribution };
1177
+ const weight = FACTOR_WEIGHTS[code] ?? 0;
1178
+ // Calculate raw score for this factor from matching signals
1179
+ const matchingSignals = record.signals.filter((s) => s.type.startsWith(code + '.') || s.type === code);
1180
+ const rawScore = matchingSignals.length > 0
1181
+ ? matchingSignals.reduce((sum, s) => sum + s.value, 0) / matchingSignals.length
1182
+ : 0;
1183
+ return {
1184
+ code,
1185
+ weight,
1186
+ rawScore,
1187
+ contribution: rawScore * weight * 1000,
1188
+ };
685
1189
  });
686
- // Sort for top positive/negative
687
- const sorted = [...factorBreakdown].sort((a, b) => b.contribution - a.contribution);
688
- const midpoint = 0.5; // Neutral factor score
689
- const topPositiveFactors = sorted
690
- .filter((f) => f.rawScore > midpoint)
691
- .slice(0, 5)
692
- .map((f) => f.code);
693
- const topNegativeFactors = sorted
694
- .filter((f) => f.rawScore < midpoint)
695
- .sort((a, b) => a.contribution - b.contribution)
696
- .slice(0, 5)
697
- .map((f) => f.code);
698
- // Calculate decay
699
- const lastSignalTime = signals.length > 0
700
- ? Math.max(...signals.map((s) => new Date(s.timestamp).getTime()))
701
- : null;
702
- const daysSinceLastSignal = lastSignalTime !== null
703
- ? (Date.now() - lastSignalTime) / (24 * 60 * 60 * 1000)
704
- : null;
705
- const decayMultiplier = daysSinceLastSignal !== null
706
- ? calculateDecayMultiplier(daysSinceLastSignal)
707
- : 1.0;
1190
+ // Days since last signal
1191
+ let daysSinceLastSignal = null;
1192
+ if (record.signals.length > 0) {
1193
+ const lastSignal = record.signals[record.signals.length - 1];
1194
+ const lastTimestamp = new Date(lastSignal.timestamp).getTime();
1195
+ daysSinceLastSignal = (Date.now() - lastTimestamp) / (1000 * 60 * 60 * 24);
1196
+ }
708
1197
  // Points to next level
709
- const currentLevel = record.level;
710
- const nextLevelThreshold = currentLevel < 7 ? TRUST_THRESHOLDS[(currentLevel + 1)] : null;
711
- const pointsToNextLevel = nextLevelThreshold !== null ? nextLevelThreshold.min - record.score : null;
1198
+ let pointsToNextLevel = null;
1199
+ const nextLevel = (level + 1);
1200
+ if (TRUST_THRESHOLDS[nextLevel]) {
1201
+ pointsToNextLevel = TRUST_THRESHOLDS[nextLevel].min - record.score;
1202
+ }
712
1203
  return {
713
1204
  entityId,
714
1205
  score: record.score,
715
- level: currentLevel,
716
- levelName: TRUST_LEVEL_NAMES[currentLevel],
717
- levelRange: TRUST_THRESHOLDS[currentLevel],
718
- pointsToNextLevel: pointsToNextLevel !== null && pointsToNextLevel > 0 ? pointsToNextLevel : null,
719
- signalCount: signals.length,
1206
+ level,
1207
+ levelName,
1208
+ levelRange,
1209
+ signalCount: record.signals.length,
720
1210
  factorBreakdown,
721
- topPositiveFactors,
722
- topNegativeFactors,
723
- decayMultiplier: Math.round(decayMultiplier * 1000) / 1000,
724
- daysSinceLastSignal: daysSinceLastSignal !== null ? Math.round(daysSinceLastSignal * 100) / 100 : null,
1211
+ daysSinceLastSignal,
1212
+ decayMultiplier: 1.0,
1213
+ pointsToNextLevel,
725
1214
  generatedAt: new Date().toISOString(),
726
1215
  };
727
1216
  }
1217
+ /**
1218
+ * Get all entity IDs
1219
+ */
1220
+ getEntityIds() {
1221
+ return Array.from(this.records.keys());
1222
+ }
1223
+ /**
1224
+ * Get trust level name
1225
+ */
1226
+ getLevelName(level) {
1227
+ return TRUST_LEVEL_NAMES[level];
1228
+ }
728
1229
  /**
729
1230
  * Convert score to trust level
730
1231
  */
@@ -737,61 +1238,70 @@ export class TrustEngine extends EventEmitter {
737
1238
  return 0;
738
1239
  }
739
1240
  /**
740
- * @deprecated Use calculateFactorScores for 16-factor model. Kept for backwards compatibility.
741
1241
  * Calculate component scores from signals
742
1242
  */
743
- calculateComponents(signals) {
744
- // Group signals by type
745
- const behavioral = signals.filter((s) => s.type.startsWith('behavioral.'));
746
- const compliance = signals.filter((s) => s.type.startsWith('compliance.'));
747
- const identity = signals.filter((s) => s.type.startsWith('identity.'));
748
- const context = signals.filter((s) => s.type.startsWith('context.'));
749
- return {
750
- behavioral: this.averageSignalValue(behavioral, 0.5),
751
- compliance: this.averageSignalValue(compliance, 0.5),
752
- identity: this.averageSignalValue(identity, 0.5),
753
- context: this.averageSignalValue(context, 0.5),
754
- };
755
- }
756
- /**
757
- * Calculate per-factor scores from signals.
758
- * Signals can use either:
759
- * - Factor code prefix (e.g. 'CT-COMP.success')
760
- * - Legacy bucket prefix (e.g. 'behavioral.success') — mapped to factors via SIGNAL_PREFIX_TO_FACTORS
761
- */
762
- calculateFactorScores(signals) {
763
- const factorSignals = {};
764
- // Initialize all factors
765
- for (const code of FACTOR_CODES) {
766
- factorSignals[code] = [];
767
- }
768
- for (const signal of signals) {
769
- const prefix = signal.type.split('.')[0];
770
- // Check if it's a direct factor code
771
- if (FACTOR_CODES.includes(prefix)) {
772
- factorSignals[prefix].push(signal);
1243
+ calculateComponents(signals, currentLevel) {
1244
+ // Group signals by component — supports both legacy 4-prefix types
1245
+ // (e.g. "behavioral.task_completion") and 16-factor code types
1246
+ // (e.g. "CT-COMP.perfect") via FACTOR_TO_COMPONENT mapping.
1247
+ const behavioral = [];
1248
+ const compliance = [];
1249
+ const identity = [];
1250
+ const context = [];
1251
+ for (const s of signals) {
1252
+ // Legacy 4-prefix routing
1253
+ if (s.type.startsWith('behavioral.')) {
1254
+ behavioral.push(s);
1255
+ continue;
1256
+ }
1257
+ if (s.type.startsWith('compliance.')) {
1258
+ compliance.push(s);
773
1259
  continue;
774
1260
  }
775
- // Check if it's a legacy bucket prefix
776
- const mappedFactors = SIGNAL_PREFIX_TO_FACTORS[prefix];
777
- if (mappedFactors) {
778
- // Distribute signal across mapped factors
779
- for (const factorCode of mappedFactors) {
780
- factorSignals[factorCode].push(signal);
1261
+ if (s.type.startsWith('identity.')) {
1262
+ identity.push(s);
1263
+ continue;
1264
+ }
1265
+ if (s.type.startsWith('context.')) {
1266
+ context.push(s);
1267
+ continue;
1268
+ }
1269
+ // 16-factor code routing: extract prefix before the first '.'
1270
+ const dotIndex = s.type.indexOf('.');
1271
+ const prefix = dotIndex > 0 ? s.type.substring(0, dotIndex) : s.type;
1272
+ const component = FACTOR_TO_COMPONENT[prefix];
1273
+ if (component) {
1274
+ switch (component) {
1275
+ case 'behavioral':
1276
+ behavioral.push(s);
1277
+ break;
1278
+ case 'compliance':
1279
+ compliance.push(s);
1280
+ break;
1281
+ case 'identity':
1282
+ identity.push(s);
1283
+ break;
1284
+ case 'context':
1285
+ context.push(s);
1286
+ break;
781
1287
  }
782
1288
  }
783
1289
  }
784
- // Calculate average score for each factor
785
- const scores = {};
786
- for (const code of FACTOR_CODES) {
787
- scores[code] = this.averageSignalValue(factorSignals[code], 0.5);
788
- }
789
- return scores;
1290
+ return {
1291
+ behavioral: this.averageSignalValue(behavioral, 0.5, currentLevel),
1292
+ compliance: this.averageSignalValue(compliance, 0.5, currentLevel),
1293
+ identity: this.averageSignalValue(identity, 0.5, currentLevel),
1294
+ context: this.averageSignalValue(context, 0.5, currentLevel),
1295
+ };
790
1296
  }
791
1297
  /**
792
- * Calculate average signal value with default
1298
+ * Calculate average signal value with default.
1299
+ *
1300
+ * Applies the BASIS penalty ratio P(T) = 3 + T to negative signals:
1301
+ * negative signal weights are multiplied by (3 + currentTier) so that
1302
+ * higher-tier agents suffer proportionally larger score drops from failures.
793
1303
  */
794
- averageSignalValue(signals, defaultValue) {
1304
+ averageSignalValue(signals, defaultValue, currentLevel) {
795
1305
  if (signals.length === 0)
796
1306
  return defaultValue;
797
1307
  // Weight recent signals more heavily
@@ -800,12 +1310,176 @@ export class TrustEngine extends EventEmitter {
800
1310
  let totalWeight = 0;
801
1311
  for (const signal of signals) {
802
1312
  const age = now - new Date(signal.timestamp).getTime();
803
- const weight = Math.exp(-age / (7 * 24 * 60 * 60 * 1000)); // 7-day half-life
804
- weightedSum += signal.value * weight;
1313
+ let weight = Math.exp(-age / (7 * 24 * 60 * 60 * 1000)); // 7-day signal recency weight (distinct from 182-day trust decay)
1314
+ // BASIS penalty ratio: amplify weight of negative signals by P(T) = 3 + T.
1315
+ // A negative signal is one whose value falls below the neutral default (0.5).
1316
+ // This counteracts EWA dilution at higher tiers, ensuring that failures
1317
+ // at T7 (10× weight) hurt far more than failures at T0 (3× weight).
1318
+ if (signal.value < defaultValue) {
1319
+ weight *= penaltyMultiplier(currentLevel);
1320
+ }
1321
+ const adjustedValue = this.adjustSignalValueForTier(signal, defaultValue, currentLevel);
1322
+ weightedSum += adjustedValue * weight;
805
1323
  totalWeight += weight;
806
1324
  }
807
1325
  return totalWeight > 0 ? weightedSum / totalWeight : defaultValue;
808
1326
  }
1327
+ /**
1328
+ * Human/manual positive approvals carry less upward impact at higher tiers.
1329
+ */
1330
+ adjustSignalValueForTier(signal, defaultValue, currentLevel) {
1331
+ if (!this.isHumanApprovalSignal(signal) || signal.value <= defaultValue) {
1332
+ return signal.value;
1333
+ }
1334
+ const assistFactor = this.getHumanApprovalAssistFactor(currentLevel);
1335
+ return defaultValue + (signal.value - defaultValue) * assistFactor;
1336
+ }
1337
+ isHumanApprovalSignal(signal) {
1338
+ const source = signal.source?.toLowerCase() ?? '';
1339
+ return source === 'manual' || source === 'human' || source === 'human_review' || source === 'human-approval';
1340
+ }
1341
+ getHumanApprovalAssistFactor(level) {
1342
+ switch (level) {
1343
+ case 0:
1344
+ case 1:
1345
+ return 1.0;
1346
+ case 2:
1347
+ return 0.85;
1348
+ case 3:
1349
+ return 0.7;
1350
+ case 4:
1351
+ return 0.55;
1352
+ case 5:
1353
+ return 0.4;
1354
+ case 6:
1355
+ return 0.3;
1356
+ case 7:
1357
+ return 0.2;
1358
+ default:
1359
+ return 1.0;
1360
+ }
1361
+ }
1362
+ /**
1363
+ * Resolve which component bucket a signal type belongs to.
1364
+ * Returns the component key or undefined if the signal type is unrecognized.
1365
+ */
1366
+ resolveSignalComponent(signalType) {
1367
+ // Legacy 4-prefix routing
1368
+ if (signalType.startsWith('behavioral.'))
1369
+ return 'behavioral';
1370
+ if (signalType.startsWith('compliance.'))
1371
+ return 'compliance';
1372
+ if (signalType.startsWith('identity.'))
1373
+ return 'identity';
1374
+ if (signalType.startsWith('context.'))
1375
+ return 'context';
1376
+ // 16-factor code routing: extract prefix before the first '.'
1377
+ const dotIndex = signalType.indexOf('.');
1378
+ const prefix = dotIndex > 0 ? signalType.substring(0, dotIndex) : signalType;
1379
+ return Object.hasOwn(FACTOR_TO_COMPONENT, prefix) ? FACTOR_TO_COMPONENT[prefix] : undefined;
1380
+ }
1381
+ /**
1382
+ * Create a fresh empty CachedScoreState.
1383
+ */
1384
+ createEmptyCacheState() {
1385
+ return {
1386
+ behavioral: { weightedSum: 0, totalWeight: 0, count: 0 },
1387
+ compliance: { weightedSum: 0, totalWeight: 0, count: 0 },
1388
+ identity: { weightedSum: 0, totalWeight: 0, count: 0 },
1389
+ context: { weightedSum: 0, totalWeight: 0, count: 0 },
1390
+ lastFullRecalcAt: 0,
1391
+ };
1392
+ }
1393
+ /**
1394
+ * Build the score cache from a full recalculation of all stored signals.
1395
+ * This mirrors the logic in averageSignalValue but captures the running totals.
1396
+ */
1397
+ buildCacheFromSignals(signals, currentLevel, entityId) {
1398
+ const cache = this.createEmptyCacheState();
1399
+ cache.lastFullRecalcAt = signals.length;
1400
+ const now = Date.now();
1401
+ const defaultValue = 0.5;
1402
+ for (const signal of signals) {
1403
+ const component = this.resolveSignalComponent(signal.type);
1404
+ if (!component)
1405
+ continue;
1406
+ const bucket = cache[component];
1407
+ const age = now - new Date(signal.timestamp).getTime();
1408
+ let weight = Math.exp(-age / (7 * 24 * 60 * 60 * 1000));
1409
+ // BASIS penalty ratio: amplify weight of negative signals
1410
+ if (signal.value < defaultValue) {
1411
+ weight *= penaltyMultiplier(currentLevel);
1412
+ }
1413
+ const adjustedValue = this.adjustSignalValueForTier(signal, defaultValue, currentLevel);
1414
+ bucket.weightedSum += adjustedValue * weight;
1415
+ bucket.totalWeight += weight;
1416
+ bucket.count++;
1417
+ }
1418
+ this._scoreCache.set(entityId, cache);
1419
+ return cache;
1420
+ }
1421
+ /**
1422
+ * Incrementally update the cached score state with a single new signal.
1423
+ * Returns the updated TrustCalculation. This is O(1) per signal.
1424
+ */
1425
+ incrementalUpdate(signal, currentLevel) {
1426
+ const cache = this._scoreCache.get(signal.entityId);
1427
+ if (!cache) {
1428
+ // Should never happen — caller must ensure cache exists
1429
+ throw new Error('Incremental update called without cache');
1430
+ }
1431
+ const component = this.resolveSignalComponent(signal.type);
1432
+ const defaultValue = 0.5;
1433
+ if (component) {
1434
+ const bucket = cache[component];
1435
+ const now = Date.now();
1436
+ const age = now - new Date(signal.timestamp).getTime();
1437
+ let weight = Math.exp(-age / (7 * 24 * 60 * 60 * 1000));
1438
+ if (signal.value < defaultValue) {
1439
+ weight *= penaltyMultiplier(currentLevel);
1440
+ }
1441
+ const adjustedValue = this.adjustSignalValueForTier(signal, defaultValue, currentLevel);
1442
+ bucket.weightedSum += adjustedValue * weight;
1443
+ bucket.totalWeight += weight;
1444
+ bucket.count++;
1445
+ }
1446
+ // Derive component averages from cache
1447
+ const components = {
1448
+ behavioral: cache.behavioral.totalWeight > 0
1449
+ ? cache.behavioral.weightedSum / cache.behavioral.totalWeight
1450
+ : defaultValue,
1451
+ compliance: cache.compliance.totalWeight > 0
1452
+ ? cache.compliance.weightedSum / cache.compliance.totalWeight
1453
+ : defaultValue,
1454
+ identity: cache.identity.totalWeight > 0
1455
+ ? cache.identity.weightedSum / cache.identity.totalWeight
1456
+ : defaultValue,
1457
+ context: cache.context.totalWeight > 0
1458
+ ? cache.context.weightedSum / cache.context.totalWeight
1459
+ : defaultValue,
1460
+ };
1461
+ // Calculate weighted total (same formula as calculate())
1462
+ const score = Math.round(components.behavioral * SIGNAL_WEIGHTS.behavioral * 1000 +
1463
+ components.compliance * SIGNAL_WEIGHTS.compliance * 1000 +
1464
+ components.identity * SIGNAL_WEIGHTS.identity * 1000 +
1465
+ components.context * SIGNAL_WEIGHTS.context * 1000);
1466
+ const clampedScore = Math.max(0, Math.min(1000, score));
1467
+ const level = this.scoreToLevel(clampedScore);
1468
+ const factors = this.getSignificantFactors(components);
1469
+ return { score: clampedScore, level, components, factors };
1470
+ }
1471
+ /**
1472
+ * Invalidate the incremental score cache for an entity,
1473
+ * forcing the next recordSignal to do a full recalculation.
1474
+ */
1475
+ invalidateScoreCache(entityId) {
1476
+ if (entityId) {
1477
+ this._scoreCache.delete(entityId);
1478
+ }
1479
+ else {
1480
+ this._scoreCache.clear();
1481
+ }
1482
+ }
809
1483
  /**
810
1484
  * Get significant factors affecting the score
811
1485
  */
@@ -825,16 +1499,97 @@ export class TrustEngine extends EventEmitter {
825
1499
  }
826
1500
  return factors;
827
1501
  }
1502
+ /**
1503
+ * Apply signal deduplication with diminishing returns.
1504
+ *
1505
+ * Tracks recent signal types per entity within a sliding window. When the same
1506
+ * signal type appears consecutively (within the window), its effective value is
1507
+ * pulled toward 0.5 (neutral) by an exponentially decaying factor. This prevents
1508
+ * replay attacks from inflating or deflating trust scores while still allowing
1509
+ * legitimate repeated signals to have some (diminished) impact.
1510
+ *
1511
+ * The adjustment formula moves the value toward 0.5 (the neutral baseline):
1512
+ * adjustedValue = 0.5 + (originalValue - 0.5) * factor
1513
+ *
1514
+ * This means:
1515
+ * - Positive signals (value > 0.5) have their positive impact reduced
1516
+ * - Negative signals (value < 0.5) have their negative impact reduced
1517
+ * - A value of exactly 0.5 is never adjusted
1518
+ */
1519
+ applySignalDedup(signal) {
1520
+ const originalValue = signal.value;
1521
+ if (!this._signalDedupEnabled) {
1522
+ return { adjusted: false, value: originalValue, originalValue, factor: 1, repeatCount: 0 };
1523
+ }
1524
+ const now = Date.now();
1525
+ const signalTs = new Date(signal.timestamp).getTime() || now;
1526
+ // Get or create entity dedup history
1527
+ let history = this._dedupState.get(signal.entityId);
1528
+ if (!history) {
1529
+ history = [];
1530
+ this._dedupState.set(signal.entityId, history);
1531
+ }
1532
+ // Prune entries outside the time window and enforce window size
1533
+ history = history.filter((entry) => now - entry.timestamp < this._signalDedupWindowMs);
1534
+ if (history.length > this._signalDedupWindowSize) {
1535
+ history = history.slice(-this._signalDedupWindowSize);
1536
+ }
1537
+ // Count consecutive occurrences of this signal type (from most recent backwards)
1538
+ let repeatCount = 0;
1539
+ for (let i = history.length - 1; i >= 0; i--) {
1540
+ if (history[i].type === signal.type) {
1541
+ repeatCount++;
1542
+ }
1543
+ else {
1544
+ break; // Stop counting when a different signal type is encountered
1545
+ }
1546
+ }
1547
+ // Record this signal in the dedup history
1548
+ history.push({ type: signal.type, timestamp: signalTs });
1549
+ // Enforce window size after push
1550
+ if (history.length > this._signalDedupWindowSize) {
1551
+ history = history.slice(-this._signalDedupWindowSize);
1552
+ }
1553
+ this._dedupState.set(signal.entityId, history);
1554
+ // No adjustment needed for first occurrence
1555
+ if (repeatCount === 0) {
1556
+ return { adjusted: false, value: originalValue, originalValue, factor: 1, repeatCount: 0 };
1557
+ }
1558
+ // Calculate diminishing factor: decayFactor^repeatCount, floored at minFactor
1559
+ const rawFactor = Math.pow(this._signalDedupDecayFactor, repeatCount);
1560
+ const factor = Math.max(rawFactor, this._signalDedupMinFactor);
1561
+ // Pull value toward 0.5 (neutral) by the diminishing factor
1562
+ const adjustedValue = 0.5 + (originalValue - 0.5) * factor;
1563
+ return {
1564
+ adjusted: true,
1565
+ value: Math.max(0, Math.min(1, adjustedValue)),
1566
+ originalValue,
1567
+ factor,
1568
+ repeatCount,
1569
+ };
1570
+ }
1571
+ /**
1572
+ * Check whether signal deduplication is enabled
1573
+ */
1574
+ get signalDedupEnabled() {
1575
+ return this._signalDedupEnabled;
1576
+ }
1577
+ /**
1578
+ * Clear dedup state for an entity (useful for testing or resets)
1579
+ */
1580
+ clearDedupState(entityId) {
1581
+ if (entityId) {
1582
+ this._dedupState.delete(entityId);
1583
+ }
1584
+ else {
1585
+ this._dedupState.clear();
1586
+ }
1587
+ }
828
1588
  /**
829
1589
  * Create initial trust record
830
1590
  */
831
1591
  createInitialRecord(entityId) {
832
1592
  const initialScore = TRUST_THRESHOLDS[1].min;
833
- // Initialize all 16 factors to 0.5
834
- const initialFactorScores = {};
835
- for (const code of FACTOR_CODES) {
836
- initialFactorScores[code] = 0.5;
837
- }
838
1593
  return {
839
1594
  entityId,
840
1595
  score: initialScore, // Start at L1 (Provisional) minimum
@@ -845,14 +1600,83 @@ export class TrustEngine extends EventEmitter {
845
1600
  identity: 0.5,
846
1601
  context: 0.5,
847
1602
  },
848
- factorScores: initialFactorScores,
849
1603
  signals: [],
850
1604
  lastCalculatedAt: new Date().toISOString(),
851
1605
  history: [],
1606
+ recentFailures: [],
852
1607
  recentSuccesses: [],
853
1608
  peakScore: initialScore,
854
1609
  consecutiveSuccesses: 0,
1610
+ readinessCheckpointIndex: 0,
1611
+ deferredReadinessMultiplier: 1,
1612
+ readinessBaselineScore: initialScore,
1613
+ freshnessCheckpointIndex: 0,
1614
+ deferredFreshnessMultiplier: 1,
1615
+ freshnessBaselineScore: initialScore,
1616
+ };
1617
+ }
1618
+ /**
1619
+ * Configure a time-bound Readiness Degree exception for an entity.
1620
+ */
1621
+ setReadinessException(entityId, options) {
1622
+ const record = this.records.get(entityId);
1623
+ if (!record) {
1624
+ throw new Error(`Entity not found: ${entityId}`);
1625
+ }
1626
+ this.validateReadinessExceptionOptions(options);
1627
+ this.ensureReadinessState(record);
1628
+ record.readinessException = {
1629
+ reason: options.reason,
1630
+ issuedAt: new Date().toISOString(),
1631
+ expiresAt: options.expiresAt,
1632
+ reductionScale: clampReductionScale(options.reductionScale),
855
1633
  };
1634
+ record.freshnessException = record.readinessException;
1635
+ }
1636
+ /**
1637
+ * @deprecated Use setReadinessException.
1638
+ */
1639
+ setFreshnessException(entityId, options) {
1640
+ this.setReadinessException(entityId, options);
1641
+ }
1642
+ /**
1643
+ * Clear Readiness Degree exception for an entity.
1644
+ */
1645
+ clearReadinessException(entityId) {
1646
+ const record = this.records.get(entityId);
1647
+ if (!record) {
1648
+ return;
1649
+ }
1650
+ delete record.readinessException;
1651
+ delete record.freshnessException;
1652
+ }
1653
+ /**
1654
+ * @deprecated Use clearReadinessException.
1655
+ */
1656
+ clearFreshnessException(entityId) {
1657
+ this.clearReadinessException(entityId);
1658
+ }
1659
+ /**
1660
+ * Get active Readiness Degree exception for an entity.
1661
+ */
1662
+ getReadinessException(entityId) {
1663
+ const record = this.records.get(entityId);
1664
+ return record?.readinessException ?? record?.freshnessException;
1665
+ }
1666
+ /**
1667
+ * @deprecated Use getReadinessException.
1668
+ */
1669
+ getFreshnessException(entityId) {
1670
+ return this.getReadinessException(entityId);
1671
+ }
1672
+ /**
1673
+ * Check if accelerated decay is currently active for an entity
1674
+ */
1675
+ isAcceleratedDecayActive(entityId) {
1676
+ const record = this.records.get(entityId);
1677
+ if (!record)
1678
+ return false;
1679
+ return this.hasAcceleratedDecay(record);
856
1680
  }
857
1681
  /**
858
1682
  * Check if accelerated recovery is currently active for an entity
@@ -863,6 +1687,16 @@ export class TrustEngine extends EventEmitter {
863
1687
  return false;
864
1688
  return this.hasAcceleratedRecovery(record);
865
1689
  }
1690
+ /**
1691
+ * Get current failure count for an entity
1692
+ */
1693
+ getFailureCount(entityId) {
1694
+ const record = this.records.get(entityId);
1695
+ if (!record)
1696
+ return 0;
1697
+ this.cleanupFailures(record);
1698
+ return record.recentFailures.length;
1699
+ }
866
1700
  /**
867
1701
  * Get consecutive success count for an entity
868
1702
  */
@@ -881,6 +1715,34 @@ export class TrustEngine extends EventEmitter {
881
1715
  return 0;
882
1716
  return record.peakScore;
883
1717
  }
1718
+ /**
1719
+ * Verify an entity's trust score against its verifier commitment chain.
1720
+ * Requires a TrustVerifier to be configured; throws if none is set.
1721
+ */
1722
+ verifyEntity(entityId) {
1723
+ if (!this._verifier) {
1724
+ throw new Error('No TrustVerifier configured');
1725
+ }
1726
+ const record = this.records.get(entityId);
1727
+ if (!record) {
1728
+ throw new Error('Entity not found');
1729
+ }
1730
+ return this._verifier.verifyAgent(entityId, record.score, record.level);
1731
+ }
1732
+ /**
1733
+ * Verify all tracked entities against the verifier commitment chain.
1734
+ * Requires a TrustVerifier to be configured; throws if none is set.
1735
+ */
1736
+ verifyAll() {
1737
+ if (!this._verifier) {
1738
+ throw new Error('No TrustVerifier configured');
1739
+ }
1740
+ const scores = new Map();
1741
+ for (const [id, record] of this.records) {
1742
+ scores.set(id, { score: record.score, tier: record.level });
1743
+ }
1744
+ return this._verifier.verifyAll(scores);
1745
+ }
884
1746
  }
885
1747
  /**
886
1748
  * Create a new Trust Engine instance