@vorionsys/atsf-core 0.2.3 → 0.2.4

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 (325) hide show
  1. package/dist/api/index.d.ts +1 -1
  2. package/dist/api/index.js +1 -1
  3. package/dist/api/server.d.ts +2 -2
  4. package/dist/api/server.d.ts.map +1 -1
  5. package/dist/api/server.js +147 -184
  6. package/dist/api/server.js.map +1 -1
  7. package/dist/arbitration/index.d.ts +4 -4
  8. package/dist/arbitration/index.d.ts.map +1 -1
  9. package/dist/arbitration/index.js +41 -46
  10. package/dist/arbitration/index.js.map +1 -1
  11. package/dist/arbitration/types.d.ts +10 -10
  12. package/dist/arbitration/types.d.ts.map +1 -1
  13. package/dist/basis/evaluator.d.ts +1 -1
  14. package/dist/basis/evaluator.d.ts.map +1 -1
  15. package/dist/basis/evaluator.js +54 -56
  16. package/dist/basis/evaluator.js.map +1 -1
  17. package/dist/basis/index.d.ts +3 -3
  18. package/dist/basis/index.js +3 -3
  19. package/dist/basis/parser.d.ts +2 -2
  20. package/dist/basis/parser.d.ts.map +1 -1
  21. package/dist/basis/parser.js +25 -32
  22. package/dist/basis/parser.js.map +1 -1
  23. package/dist/basis/types.d.ts +2 -2
  24. package/dist/chain/index.d.ts.map +1 -1
  25. package/dist/chain/index.js +16 -16
  26. package/dist/chain/index.js.map +1 -1
  27. package/dist/cognigate/index.d.ts +1 -1
  28. package/dist/cognigate/index.d.ts.map +1 -1
  29. package/dist/cognigate/index.js +33 -44
  30. package/dist/cognigate/index.js.map +1 -1
  31. package/dist/common/adapters.d.ts +4 -4
  32. package/dist/common/adapters.d.ts.map +1 -1
  33. package/dist/common/adapters.js +52 -62
  34. package/dist/common/adapters.js.map +1 -1
  35. package/dist/common/config.d.ts +69 -68
  36. package/dist/common/config.d.ts.map +1 -1
  37. package/dist/common/config.js +50 -50
  38. package/dist/common/config.js.map +1 -1
  39. package/dist/common/index.d.ts +4 -4
  40. package/dist/common/index.js +4 -4
  41. package/dist/common/logger.d.ts +1 -1
  42. package/dist/common/logger.js +8 -8
  43. package/dist/common/types.d.ts +5 -5
  44. package/dist/common/types.js +5 -5
  45. package/dist/containment/index.d.ts +3 -3
  46. package/dist/containment/index.d.ts.map +1 -1
  47. package/dist/containment/index.js +105 -119
  48. package/dist/containment/index.js.map +1 -1
  49. package/dist/containment/types.d.ts +11 -11
  50. package/dist/containment/types.d.ts.map +1 -1
  51. package/dist/contracts/index.d.ts +9 -9
  52. package/dist/contracts/index.d.ts.map +1 -1
  53. package/dist/contracts/index.js +54 -59
  54. package/dist/contracts/index.js.map +1 -1
  55. package/dist/contracts/types.d.ts +12 -12
  56. package/dist/contracts/types.d.ts.map +1 -1
  57. package/dist/crewai/callback.d.ts +2 -2
  58. package/dist/crewai/callback.d.ts.map +1 -1
  59. package/dist/crewai/callback.js +27 -27
  60. package/dist/crewai/callback.js.map +1 -1
  61. package/dist/crewai/executor.d.ts +95 -4
  62. package/dist/crewai/executor.d.ts.map +1 -1
  63. package/dist/crewai/executor.js +457 -16
  64. package/dist/crewai/executor.js.map +1 -1
  65. package/dist/crewai/index.d.ts +4 -4
  66. package/dist/crewai/index.js +4 -4
  67. package/dist/crewai/tools.d.ts +1 -1
  68. package/dist/crewai/tools.d.ts.map +1 -1
  69. package/dist/crewai/tools.js +38 -39
  70. package/dist/crewai/tools.js.map +1 -1
  71. package/dist/crewai/types.d.ts +66 -3
  72. package/dist/crewai/types.d.ts.map +1 -1
  73. package/dist/enforce/index.d.ts +229 -7
  74. package/dist/enforce/index.d.ts.map +1 -1
  75. package/dist/enforce/index.js +52 -80
  76. package/dist/enforce/index.js.map +1 -1
  77. package/dist/enforce/trust-aware-enforcement-service.d.ts +8 -8
  78. package/dist/enforce/trust-aware-enforcement-service.d.ts.map +1 -1
  79. package/dist/enforce/trust-aware-enforcement-service.js +107 -125
  80. package/dist/enforce/trust-aware-enforcement-service.js.map +1 -1
  81. package/dist/governance/fluid-workflow.d.ts +8 -8
  82. package/dist/governance/fluid-workflow.d.ts.map +1 -1
  83. package/dist/governance/fluid-workflow.js +86 -114
  84. package/dist/governance/fluid-workflow.js.map +1 -1
  85. package/dist/governance/index.d.ts +7 -7
  86. package/dist/governance/index.d.ts.map +1 -1
  87. package/dist/governance/index.js +74 -81
  88. package/dist/governance/index.js.map +1 -1
  89. package/dist/governance/proof-bridge.d.ts +6 -6
  90. package/dist/governance/proof-bridge.d.ts.map +1 -1
  91. package/dist/governance/proof-bridge.js +5 -5
  92. package/dist/governance/proof-bridge.js.map +1 -1
  93. package/dist/governance/types.d.ts +9 -16
  94. package/dist/governance/types.d.ts.map +1 -1
  95. package/dist/governance/types.js.map +1 -1
  96. package/dist/index.d.ts +27 -29
  97. package/dist/index.d.ts.map +1 -1
  98. package/dist/index.js +25 -31
  99. package/dist/index.js.map +1 -1
  100. package/dist/intent/index.d.ts +55 -5
  101. package/dist/intent/index.d.ts.map +1 -1
  102. package/dist/intent/index.js +21 -24
  103. package/dist/intent/index.js.map +1 -1
  104. package/dist/intent/persistent-intent-service.d.ts +2 -2
  105. package/dist/intent/persistent-intent-service.d.ts.map +1 -1
  106. package/dist/intent/persistent-intent-service.js +31 -43
  107. package/dist/intent/persistent-intent-service.js.map +1 -1
  108. package/dist/intent/supabase-intent-repository.d.ts +124 -0
  109. package/dist/intent/supabase-intent-repository.d.ts.map +1 -0
  110. package/dist/intent/supabase-intent-repository.js +404 -0
  111. package/dist/intent/supabase-intent-repository.js.map +1 -0
  112. package/dist/langchain/callback.d.ts +2 -2
  113. package/dist/langchain/callback.d.ts.map +1 -1
  114. package/dist/langchain/callback.js +30 -30
  115. package/dist/langchain/callback.js.map +1 -1
  116. package/dist/langchain/executor.d.ts +4 -4
  117. package/dist/langchain/executor.d.ts.map +1 -1
  118. package/dist/langchain/executor.js +80 -82
  119. package/dist/langchain/executor.js.map +1 -1
  120. package/dist/langchain/index.d.ts +5 -5
  121. package/dist/langchain/index.js +5 -5
  122. package/dist/langchain/tools.d.ts +1 -1
  123. package/dist/langchain/tools.d.ts.map +1 -1
  124. package/dist/langchain/tools.js +34 -36
  125. package/dist/langchain/tools.js.map +1 -1
  126. package/dist/langchain/types.d.ts +3 -3
  127. package/dist/langchain/types.d.ts.map +1 -1
  128. package/dist/layers/implementations/L0-request-format.d.ts +2 -2
  129. package/dist/layers/implementations/L0-request-format.d.ts.map +1 -1
  130. package/dist/layers/implementations/L0-request-format.js +52 -54
  131. package/dist/layers/implementations/L0-request-format.js.map +1 -1
  132. package/dist/layers/implementations/L1-input-size.d.ts +2 -2
  133. package/dist/layers/implementations/L1-input-size.d.ts.map +1 -1
  134. package/dist/layers/implementations/L1-input-size.js +39 -49
  135. package/dist/layers/implementations/L1-input-size.js.map +1 -1
  136. package/dist/layers/implementations/L2-charset-sanitizer.d.ts +2 -2
  137. package/dist/layers/implementations/L2-charset-sanitizer.d.ts.map +1 -1
  138. package/dist/layers/implementations/L2-charset-sanitizer.js +71 -81
  139. package/dist/layers/implementations/L2-charset-sanitizer.js.map +1 -1
  140. package/dist/layers/implementations/L3-schema-conformance.d.ts +3 -3
  141. package/dist/layers/implementations/L3-schema-conformance.d.ts.map +1 -1
  142. package/dist/layers/implementations/L3-schema-conformance.js +73 -82
  143. package/dist/layers/implementations/L3-schema-conformance.js.map +1 -1
  144. package/dist/layers/implementations/L4-injection-detector.d.ts +4 -4
  145. package/dist/layers/implementations/L4-injection-detector.d.ts.map +1 -1
  146. package/dist/layers/implementations/L4-injection-detector.js +81 -85
  147. package/dist/layers/implementations/L4-injection-detector.js.map +1 -1
  148. package/dist/layers/implementations/L5-rate-limiter.d.ts +2 -2
  149. package/dist/layers/implementations/L5-rate-limiter.d.ts.map +1 -1
  150. package/dist/layers/implementations/L5-rate-limiter.js +20 -20
  151. package/dist/layers/implementations/L5-rate-limiter.js.map +1 -1
  152. package/dist/layers/implementations/index.d.ts +6 -6
  153. package/dist/layers/implementations/index.d.ts.map +1 -1
  154. package/dist/layers/implementations/index.js +6 -6
  155. package/dist/layers/implementations/index.js.map +1 -1
  156. package/dist/layers/index.d.ts +3 -3
  157. package/dist/layers/index.d.ts.map +1 -1
  158. package/dist/layers/index.js +71 -99
  159. package/dist/layers/index.js.map +1 -1
  160. package/dist/layers/types.d.ts +16 -16
  161. package/dist/layers/types.d.ts.map +1 -1
  162. package/dist/persistence/file.d.ts +3 -3
  163. package/dist/persistence/file.d.ts.map +1 -1
  164. package/dist/persistence/file.js +28 -32
  165. package/dist/persistence/file.js.map +1 -1
  166. package/dist/persistence/index.d.ts +7 -7
  167. package/dist/persistence/index.d.ts.map +1 -1
  168. package/dist/persistence/index.js +18 -18
  169. package/dist/persistence/index.js.map +1 -1
  170. package/dist/persistence/memory.d.ts +3 -3
  171. package/dist/persistence/memory.d.ts.map +1 -1
  172. package/dist/persistence/memory.js +8 -10
  173. package/dist/persistence/memory.js.map +1 -1
  174. package/dist/persistence/sqlite.d.ts +3 -3
  175. package/dist/persistence/sqlite.d.ts.map +1 -1
  176. package/dist/persistence/sqlite.js +40 -39
  177. package/dist/persistence/sqlite.js.map +1 -1
  178. package/dist/persistence/supabase.d.ts +3 -3
  179. package/dist/persistence/supabase.d.ts.map +1 -1
  180. package/dist/persistence/supabase.js +45 -43
  181. package/dist/persistence/supabase.js.map +1 -1
  182. package/dist/persistence/types.d.ts +5 -5
  183. package/dist/phase6/ceiling.d.ts +5 -5
  184. package/dist/phase6/ceiling.d.ts.map +1 -1
  185. package/dist/phase6/ceiling.js +36 -69
  186. package/dist/phase6/ceiling.js.map +1 -1
  187. package/dist/phase6/context.d.ts +3 -3
  188. package/dist/phase6/context.d.ts.map +1 -1
  189. package/dist/phase6/context.js +47 -93
  190. package/dist/phase6/context.js.map +1 -1
  191. package/dist/phase6/index.d.ts +12 -12
  192. package/dist/phase6/index.d.ts.map +1 -1
  193. package/dist/phase6/index.js +15 -15
  194. package/dist/phase6/index.js.map +1 -1
  195. package/dist/phase6/presets.d.ts +2 -2
  196. package/dist/phase6/presets.d.ts.map +1 -1
  197. package/dist/phase6/presets.js +33 -39
  198. package/dist/phase6/presets.js.map +1 -1
  199. package/dist/phase6/provenance.d.ts +4 -4
  200. package/dist/phase6/provenance.d.ts.map +1 -1
  201. package/dist/phase6/provenance.js +35 -42
  202. package/dist/phase6/provenance.js.map +1 -1
  203. package/dist/phase6/role-gates/index.d.ts +2 -2
  204. package/dist/phase6/role-gates/index.js +2 -2
  205. package/dist/phase6/role-gates/kernel.d.ts.map +1 -1
  206. package/dist/phase6/role-gates/kernel.js +16 -16
  207. package/dist/phase6/role-gates/kernel.js.map +1 -1
  208. package/dist/phase6/role-gates/policy.d.ts +2 -2
  209. package/dist/phase6/role-gates/policy.js +6 -6
  210. package/dist/phase6/role-gates.d.ts +4 -4
  211. package/dist/phase6/role-gates.d.ts.map +1 -1
  212. package/dist/phase6/role-gates.js +58 -80
  213. package/dist/phase6/role-gates.js.map +1 -1
  214. package/dist/phase6/types.d.ts +20 -19
  215. package/dist/phase6/types.d.ts.map +1 -1
  216. package/dist/phase6/types.js +82 -177
  217. package/dist/phase6/types.js.map +1 -1
  218. package/dist/phase6/weight-presets/canonical.d.ts.map +1 -1
  219. package/dist/phase6/weight-presets/canonical.js +10 -10
  220. package/dist/phase6/weight-presets/canonical.js.map +1 -1
  221. package/dist/phase6/weight-presets/deltas.d.ts +2 -2
  222. package/dist/phase6/weight-presets/deltas.d.ts.map +1 -1
  223. package/dist/phase6/weight-presets/deltas.js +27 -27
  224. package/dist/phase6/weight-presets/deltas.js.map +1 -1
  225. package/dist/phase6/weight-presets/index.d.ts +3 -3
  226. package/dist/phase6/weight-presets/index.js +3 -3
  227. package/dist/phase6/weight-presets/merger.d.ts +2 -2
  228. package/dist/phase6/weight-presets/merger.d.ts.map +1 -1
  229. package/dist/phase6/weight-presets/merger.js +43 -39
  230. package/dist/phase6/weight-presets/merger.js.map +1 -1
  231. package/dist/proof/index.d.ts +3 -3
  232. package/dist/proof/index.d.ts.map +1 -1
  233. package/dist/proof/index.js +38 -44
  234. package/dist/proof/index.js.map +1 -1
  235. package/dist/proof/merkle.d.ts +24 -3
  236. package/dist/proof/merkle.d.ts.map +1 -1
  237. package/dist/proof/merkle.js +116 -32
  238. package/dist/proof/merkle.js.map +1 -1
  239. package/dist/proof/zk-proofs.d.ts +6 -6
  240. package/dist/proof/zk-proofs.d.ts.map +1 -1
  241. package/dist/proof/zk-proofs.js +43 -42
  242. package/dist/proof/zk-proofs.js.map +1 -1
  243. package/dist/provenance/index.d.ts +3 -3
  244. package/dist/provenance/index.d.ts.map +1 -1
  245. package/dist/provenance/index.js +17 -19
  246. package/dist/provenance/index.js.map +1 -1
  247. package/dist/provenance/types.d.ts +4 -4
  248. package/dist/provenance/types.d.ts.map +1 -1
  249. package/dist/sandbox-training/challenges.d.ts +1 -1
  250. package/dist/sandbox-training/challenges.d.ts.map +1 -1
  251. package/dist/sandbox-training/challenges.js +228 -228
  252. package/dist/sandbox-training/challenges.js.map +1 -1
  253. package/dist/sandbox-training/graduation.d.ts +1 -1
  254. package/dist/sandbox-training/graduation.d.ts.map +1 -1
  255. package/dist/sandbox-training/graduation.js +15 -14
  256. package/dist/sandbox-training/graduation.js.map +1 -1
  257. package/dist/sandbox-training/index.d.ts +9 -9
  258. package/dist/sandbox-training/index.d.ts.map +1 -1
  259. package/dist/sandbox-training/index.js +6 -6
  260. package/dist/sandbox-training/index.js.map +1 -1
  261. package/dist/sandbox-training/promotion-service.d.ts +4 -4
  262. package/dist/sandbox-training/promotion-service.d.ts.map +1 -1
  263. package/dist/sandbox-training/promotion-service.js +5 -5
  264. package/dist/sandbox-training/promotion-service.js.map +1 -1
  265. package/dist/sandbox-training/runner.d.ts +1 -1
  266. package/dist/sandbox-training/runner.d.ts.map +1 -1
  267. package/dist/sandbox-training/runner.js +73 -74
  268. package/dist/sandbox-training/runner.js.map +1 -1
  269. package/dist/sandbox-training/scorer.d.ts +4 -4
  270. package/dist/sandbox-training/scorer.js +5 -5
  271. package/dist/sandbox-training/types.d.ts +4 -4
  272. package/dist/sandbox-training/types.d.ts.map +1 -1
  273. package/dist/sandbox-training/types.js +7 -11
  274. package/dist/sandbox-training/types.js.map +1 -1
  275. package/dist/trust-engine/ceiling-enforcement/audit.d.ts +1 -1
  276. package/dist/trust-engine/ceiling-enforcement/audit.d.ts.map +1 -1
  277. package/dist/trust-engine/ceiling-enforcement/audit.js +4 -3
  278. package/dist/trust-engine/ceiling-enforcement/audit.js.map +1 -1
  279. package/dist/trust-engine/ceiling-enforcement/index.d.ts +2 -2
  280. package/dist/trust-engine/ceiling-enforcement/index.js +2 -2
  281. package/dist/trust-engine/ceiling-enforcement/kernel.d.ts +12 -10
  282. package/dist/trust-engine/ceiling-enforcement/kernel.d.ts.map +1 -1
  283. package/dist/trust-engine/ceiling-enforcement/kernel.js +26 -20
  284. package/dist/trust-engine/ceiling-enforcement/kernel.js.map +1 -1
  285. package/dist/trust-engine/context-policy/enforcement.d.ts.map +1 -1
  286. package/dist/trust-engine/context-policy/factory.d.ts +1 -1
  287. package/dist/trust-engine/context-policy/factory.d.ts.map +1 -1
  288. package/dist/trust-engine/context-policy/factory.js +1 -1
  289. package/dist/trust-engine/context-policy/factory.js.map +1 -1
  290. package/dist/trust-engine/context-policy/index.d.ts +2 -2
  291. package/dist/trust-engine/context-policy/index.js +2 -2
  292. package/dist/trust-engine/creation-modifiers/index.d.ts +1 -1
  293. package/dist/trust-engine/creation-modifiers/index.js +1 -1
  294. package/dist/trust-engine/creation-modifiers/types.d.ts.map +1 -1
  295. package/dist/trust-engine/creation-modifiers/types.js +3 -2
  296. package/dist/trust-engine/creation-modifiers/types.js.map +1 -1
  297. package/dist/trust-engine/decay-profiles.d.ts +37 -136
  298. package/dist/trust-engine/decay-profiles.d.ts.map +1 -1
  299. package/dist/trust-engine/decay-profiles.js +68 -178
  300. package/dist/trust-engine/decay-profiles.js.map +1 -1
  301. package/dist/trust-engine/index.d.ts +135 -168
  302. package/dist/trust-engine/index.d.ts.map +1 -1
  303. package/dist/trust-engine/index.js +239 -525
  304. package/dist/trust-engine/index.js.map +1 -1
  305. package/dist/trust-engine/phase6-types.d.ts +18 -11
  306. package/dist/trust-engine/phase6-types.d.ts.map +1 -1
  307. package/dist/trust-engine/phase6-types.js +33 -29
  308. package/dist/trust-engine/phase6-types.js.map +1 -1
  309. package/package.json +1 -1
  310. package/dist/enforce/types.d.ts +0 -234
  311. package/dist/enforce/types.d.ts.map +0 -1
  312. package/dist/enforce/types.js +0 -10
  313. package/dist/enforce/types.js.map +0 -1
  314. package/dist/intent/types.d.ts +0 -69
  315. package/dist/intent/types.d.ts.map +0 -1
  316. package/dist/intent/types.js +0 -10
  317. package/dist/intent/types.js.map +0 -1
  318. package/dist/intent-gateway/index.d.ts +0 -522
  319. package/dist/intent-gateway/index.d.ts.map +0 -1
  320. package/dist/intent-gateway/index.js +0 -1499
  321. package/dist/intent-gateway/index.js.map +0 -1
  322. package/dist/trust-engine/types.d.ts +0 -77
  323. package/dist/trust-engine/types.d.ts.map +0 -1
  324. package/dist/trust-engine/types.js +0 -20
  325. package/dist/trust-engine/types.js.map +0 -1
@@ -2,15 +2,15 @@
2
2
  * Trust Engine - Behavioral Trust Scoring
3
3
  *
4
4
  * Calculates and maintains trust scores for entities based on behavioral signals.
5
- * Features 6-tier trust system with event emission for observability.
5
+ * Features 8-tier trust system (T0-T7) with event emission for observability.
6
6
  *
7
7
  * @packageDocumentation
8
8
  */
9
- import { EventEmitter } from "events";
10
- import { createLogger } from "../common/logger.js";
11
- import { READINESS_EXCEPTION_REASON_CODES, } from "./types.js";
12
- export * from "./types.js";
13
- const logger = createLogger({ component: "trust-engine" });
9
+ import { EventEmitter } from 'events';
10
+ 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
+ const logger = createLogger({ component: 'trust-engine' });
14
14
  /**
15
15
  * Trust level thresholds (8 tiers T0-T7) - per BASIS specification
16
16
  */
@@ -28,16 +28,22 @@ export const TRUST_THRESHOLDS = {
28
28
  * Trust level names (8 tiers T0-T7) - per BASIS specification
29
29
  */
30
30
  export const TRUST_LEVEL_NAMES = {
31
- 0: "Sandbox",
32
- 1: "Observed",
33
- 2: "Provisional",
34
- 3: "Monitored",
35
- 4: "Standard",
36
- 5: "Trusted",
37
- 6: "Certified",
38
- 7: "Autonomous",
31
+ 0: 'Sandbox',
32
+ 1: 'Observed',
33
+ 2: 'Provisional',
34
+ 3: 'Monitored',
35
+ 4: 'Standard',
36
+ 5: 'Trusted',
37
+ 6: 'Certified',
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 };
40
45
  /**
46
+ * @deprecated Use FACTOR_WEIGHTS for 16-factor scoring. Kept for backwards compatibility.
41
47
  * Signal weights for score calculation
42
48
  */
43
49
  export const SIGNAL_WEIGHTS = {
@@ -46,36 +52,12 @@ export const SIGNAL_WEIGHTS = {
46
52
  identity: 0.2,
47
53
  context: 0.15,
48
54
  };
49
- const DEFAULT_READINESS_CHECKPOINT_DAYS = [
50
- 7, 14, 28, 42, 56, 84, 112, 140, 182,
51
- ];
52
- const DEFAULT_READINESS_REDUCTIONS = [
53
- 0.06, 0.06, 0.06, 0.06, 0.06, 0.05, 0.05, 0.05, 0.05,
54
- ];
55
- const DEFAULT_READINESS_EXCEPTION_MAX_DURATION_MS = 30 * 24 * 60 * 60 * 1000;
56
- function toMs(days) {
57
- return days * 24 * 60 * 60 * 1000;
58
- }
59
- function clampReductionScale(value) {
60
- if (value === undefined || Number.isNaN(value))
61
- return 1;
62
- if (value < 0)
63
- return 0;
64
- if (value > 1)
65
- return 1;
66
- return value;
67
- }
68
55
  /**
69
56
  * Trust Engine service with event emission and subscription limits
70
57
  */
71
58
  export class TrustEngine extends EventEmitter {
72
59
  records = new Map();
73
- _decayRate;
74
- _decayIntervalMs;
75
- _failureThreshold;
76
- _acceleratedDecayMultiplier;
77
- _failureWindowMs;
78
- _minFailuresForAcceleration;
60
+ _decayCheckIntervalMs;
79
61
  _persistence;
80
62
  _autoPersist;
81
63
  // Recovery configuration
@@ -91,26 +73,15 @@ export class TrustEngine extends EventEmitter {
91
73
  _listenerWarningThreshold;
92
74
  _listenerCounts = new Map();
93
75
  _totalListeners = 0;
94
- _readinessMode;
95
- _readinessCheckpointDays;
96
- _readinessCheckpointReductions;
97
- _allowedReadinessExceptionReasons;
98
- _readinessExceptionMaxDurationMs;
99
76
  constructor(config = {}) {
100
77
  super();
101
- this._decayRate = config.decayRate ?? 0.01;
102
- this._decayIntervalMs = config.decayIntervalMs ?? 60000;
103
- this._failureThreshold = config.failureThreshold ?? 0.3;
104
- this._acceleratedDecayMultiplier = config.acceleratedDecayMultiplier ?? 1.0;
105
- this._failureWindowMs = config.failureWindowMs ?? 3600000; // 1 hour
106
- this._minFailuresForAcceleration = config.minFailuresForAcceleration ?? 2;
78
+ this._decayCheckIntervalMs = config.decayCheckIntervalMs ?? 60000;
107
79
  this._persistence = config.persistence;
108
- this._autoPersist = config.autoPersist ?? config.persistence !== undefined;
80
+ this._autoPersist = config.autoPersist ?? (config.persistence !== undefined);
109
81
  // Recovery configuration
110
82
  this._successThreshold = config.successThreshold ?? 0.7;
111
83
  this._recoveryRate = config.recoveryRate ?? 0.02;
112
- this._acceleratedRecoveryMultiplier =
113
- config.acceleratedRecoveryMultiplier ?? 1.5;
84
+ this._acceleratedRecoveryMultiplier = config.acceleratedRecoveryMultiplier ?? 1.5;
114
85
  this._minSuccessesForAcceleration = config.minSuccessesForAcceleration ?? 3;
115
86
  this._successWindowMs = config.successWindowMs ?? 3600000; // 1 hour
116
87
  this._maxRecoveryPerSignal = config.maxRecoveryPerSignal ?? 50;
@@ -118,219 +89,9 @@ export class TrustEngine extends EventEmitter {
118
89
  this._maxListenersPerEvent = config.maxListenersPerEvent ?? 100;
119
90
  this._maxTotalListeners = config.maxTotalListeners ?? 1000;
120
91
  this._listenerWarningThreshold = config.listenerWarningThreshold ?? 0.8;
121
- this._readinessMode =
122
- config.readinessMode ??
123
- config.freshnessMode ??
124
- (config.decayIntervalMs !== undefined || config.decayRate !== undefined
125
- ? "legacy_interval"
126
- : "checkpoint_schedule");
127
- this._readinessCheckpointDays = config.readinessCheckpointDays ??
128
- config.freshnessCheckpointDays ?? [...DEFAULT_READINESS_CHECKPOINT_DAYS];
129
- this._readinessCheckpointReductions =
130
- config.readinessCheckpointReductions ??
131
- config.freshnessCheckpointReductions ?? [
132
- ...DEFAULT_READINESS_REDUCTIONS,
133
- ];
134
- if (this._readinessCheckpointDays.length !==
135
- this._readinessCheckpointReductions.length) {
136
- throw new Error("freshnessCheckpointDays and freshnessCheckpointReductions must have equal length");
137
- }
138
- this._allowedReadinessExceptionReasons = new Set(config.readinessExceptionAllowedReasons ??
139
- config.freshnessExceptionAllowedReasons ?? [
140
- ...READINESS_EXCEPTION_REASON_CODES,
141
- ]);
142
- this._readinessExceptionMaxDurationMs =
143
- config.readinessExceptionMaxDurationMs ??
144
- config.freshnessExceptionMaxDurationMs ??
145
- DEFAULT_READINESS_EXCEPTION_MAX_DURATION_MS;
146
92
  // Set default max listeners on EventEmitter
147
93
  this.setMaxListeners(this._maxListenersPerEvent);
148
94
  }
149
- validateReadinessExceptionOptions(options) {
150
- if (!this._allowedReadinessExceptionReasons.has(options.reason)) {
151
- throw new Error(`Unsupported readiness exception reason: ${options.reason}. ` +
152
- `Allowed reasons: ${Array.from(this._allowedReadinessExceptionReasons).join(", ")}`);
153
- }
154
- const now = Date.now();
155
- const expiresAtMs = new Date(options.expiresAt).getTime();
156
- if (Number.isNaN(expiresAtMs)) {
157
- throw new Error("Invalid expiresAt timestamp for readiness exception");
158
- }
159
- if (expiresAtMs <= now) {
160
- throw new Error("Readiness exception expiresAt must be in the future");
161
- }
162
- if (expiresAtMs - now > this._readinessExceptionMaxDurationMs) {
163
- throw new Error(`Readiness exception duration exceeds configured maximum (${this._readinessExceptionMaxDurationMs} ms)`);
164
- }
165
- }
166
- getCheckpointIntervalMs(index) {
167
- if (index <= 0) {
168
- return toMs(this._readinessCheckpointDays[0] ?? 7);
169
- }
170
- const current = this._readinessCheckpointDays[index] ??
171
- this._readinessCheckpointDays[this._readinessCheckpointDays.length - 1] ??
172
- 7;
173
- const previous = this._readinessCheckpointDays[index - 1] ?? 0;
174
- return toMs(Math.max(1, current - previous));
175
- }
176
- ensureReadinessState(record) {
177
- record.readinessCheckpointIndex ??= record.freshnessCheckpointIndex ?? 0;
178
- record.deferredReadinessMultiplier ??=
179
- record.deferredFreshnessMultiplier ?? 1;
180
- record.readinessBaselineScore ??=
181
- record.freshnessBaselineScore ?? record.score;
182
- record.freshnessCheckpointIndex = record.readinessCheckpointIndex;
183
- record.deferredFreshnessMultiplier = record.deferredReadinessMultiplier;
184
- record.freshnessBaselineScore = record.readinessBaselineScore;
185
- record.freshnessException =
186
- record.readinessException ?? record.freshnessException;
187
- }
188
- isUsingDefaultReadinessSchedule() {
189
- if (this._readinessCheckpointDays.length !==
190
- DEFAULT_READINESS_CHECKPOINT_DAYS.length) {
191
- return false;
192
- }
193
- if (this._readinessCheckpointReductions.length !==
194
- DEFAULT_READINESS_REDUCTIONS.length) {
195
- return false;
196
- }
197
- return (this._readinessCheckpointDays.every((v, i) => v === DEFAULT_READINESS_CHECKPOINT_DAYS[i]) &&
198
- this._readinessCheckpointReductions.every((v, i) => v === DEFAULT_READINESS_REDUCTIONS[i]));
199
- }
200
- isReadinessExceptionActive(record, now) {
201
- const exception = record.readinessException ?? record.freshnessException;
202
- if (!exception)
203
- return false;
204
- const issuedAt = new Date(exception.issuedAt).getTime();
205
- const expiresAt = new Date(exception.expiresAt).getTime();
206
- return now >= issuedAt && now < expiresAt;
207
- }
208
- async applyDeferredReadinessCatchupIfExpired(record) {
209
- const exception = record.readinessException ?? record.freshnessException;
210
- if (!exception)
211
- return;
212
- const now = Date.now();
213
- const expiresAt = new Date(exception.expiresAt).getTime();
214
- if (now < expiresAt)
215
- return;
216
- this.ensureReadinessState(record);
217
- const deferredMultiplier = record.deferredReadinessMultiplier ?? 1;
218
- if (deferredMultiplier < 1) {
219
- const previousScore = record.score;
220
- const adjustedScore = Math.max(0, Math.round(record.score * deferredMultiplier));
221
- record.score = adjustedScore;
222
- record.level = this.scoreToLevel(adjustedScore);
223
- record.deferredReadinessMultiplier = 1;
224
- record.deferredFreshnessMultiplier = 1;
225
- this.emitReadinessAdjustmentEvents(record.entityId, {
226
- previousScore,
227
- newScore: record.score,
228
- stalenessMs: 0,
229
- accelerated: false,
230
- });
231
- }
232
- delete record.readinessException;
233
- delete record.freshnessException;
234
- }
235
- async applyScheduledReadinessAdjustment(record) {
236
- this.ensureReadinessState(record);
237
- await this.applyDeferredReadinessCatchupIfExpired(record);
238
- while ((record.readinessCheckpointIndex ?? 0) <
239
- this._readinessCheckpointDays.length) {
240
- const checkpointIndex = record.readinessCheckpointIndex ?? 0;
241
- const intervalMs = this.getCheckpointIntervalMs(checkpointIndex);
242
- const stalenessMs = Date.now() - new Date(record.lastCalculatedAt).getTime();
243
- if (stalenessMs < intervalMs) {
244
- break;
245
- }
246
- const previousScore = record.score;
247
- const previousLevel = record.level;
248
- const fullReduction = this._readinessCheckpointReductions[checkpointIndex] ?? 0;
249
- const activeException = this.isReadinessExceptionActive(record, Date.now());
250
- const scale = activeException
251
- ? clampReductionScale((record.readinessException ?? record.freshnessException)
252
- ?.reductionScale)
253
- : 1;
254
- const appliedReduction = fullReduction * scale;
255
- const appliedMultiplier = 1 - appliedReduction;
256
- const fullMultiplier = 1 - fullReduction;
257
- record.score = Math.max(0, Math.round(record.score * appliedMultiplier));
258
- const isFinalCheckpoint = checkpointIndex === this._readinessCheckpointDays.length - 1;
259
- if (isFinalCheckpoint &&
260
- !activeException &&
261
- this.isUsingDefaultReadinessSchedule()) {
262
- const baseline = record.readinessBaselineScore ??
263
- record.freshnessBaselineScore ??
264
- record.score;
265
- record.score = Math.max(0, Math.round(baseline * 0.5));
266
- }
267
- record.level = this.scoreToLevel(record.score);
268
- record.readinessCheckpointIndex = checkpointIndex + 1;
269
- record.freshnessCheckpointIndex = record.readinessCheckpointIndex;
270
- const lastCalculatedMs = new Date(record.lastCalculatedAt).getTime();
271
- record.lastCalculatedAt = new Date(lastCalculatedMs + intervalMs).toISOString();
272
- if (activeException && appliedMultiplier > 0 && fullMultiplier >= 0) {
273
- const debtFactor = fullMultiplier / appliedMultiplier;
274
- record.deferredReadinessMultiplier =
275
- (record.deferredReadinessMultiplier ?? 1) * debtFactor;
276
- record.deferredFreshnessMultiplier = record.deferredReadinessMultiplier;
277
- }
278
- if (previousScore !== record.score) {
279
- this.emitReadinessAdjustmentEvents(record.entityId, {
280
- previousScore,
281
- newScore: record.score,
282
- stalenessMs,
283
- accelerated: false,
284
- });
285
- if (previousLevel !== record.level) {
286
- this.emitTrustEvent({
287
- type: "trust:tier_changed",
288
- entityId: record.entityId,
289
- timestamp: new Date().toISOString(),
290
- previousLevel,
291
- newLevel: record.level,
292
- previousLevelName: TRUST_LEVEL_NAMES[previousLevel],
293
- newLevelName: TRUST_LEVEL_NAMES[record.level],
294
- direction: record.level < previousLevel ? "demoted" : "promoted",
295
- });
296
- }
297
- await this.autoPersistRecord(record);
298
- }
299
- }
300
- }
301
- emitReadinessAdjustmentEvents(entityId, details) {
302
- const adjustmentAmount = details.previousScore - details.newScore;
303
- this.emitTrustEvent({
304
- type: "trust:readiness_adjusted",
305
- entityId,
306
- timestamp: new Date().toISOString(),
307
- previousScore: details.previousScore,
308
- newScore: details.newScore,
309
- adjustmentAmount,
310
- stalenessMs: details.stalenessMs,
311
- accelerated: details.accelerated,
312
- });
313
- this.emitTrustEvent({
314
- type: "trust:freshness_adjusted",
315
- entityId,
316
- timestamp: new Date().toISOString(),
317
- previousScore: details.previousScore,
318
- newScore: details.newScore,
319
- adjustmentAmount,
320
- stalenessMs: details.stalenessMs,
321
- accelerated: details.accelerated,
322
- });
323
- this.emitTrustEvent({
324
- type: "trust:decay_applied",
325
- entityId,
326
- timestamp: new Date().toISOString(),
327
- previousScore: details.previousScore,
328
- newScore: details.newScore,
329
- decayAmount: adjustmentAmount,
330
- stalenessMs: details.stalenessMs,
331
- accelerated: details.accelerated,
332
- });
333
- }
334
95
  /**
335
96
  * Add event listener with subscription limits
336
97
  * @throws Error if listener limits are exceeded
@@ -401,14 +162,10 @@ export class TrustEngine extends EventEmitter {
401
162
  const eventThreshold = this._maxListenersPerEvent * this._listenerWarningThreshold;
402
163
  const totalThreshold = this._maxTotalListeners * this._listenerWarningThreshold;
403
164
  if (currentEventCount >= eventThreshold) {
404
- logger.warn({
405
- eventName,
406
- current: currentEventCount,
407
- max: this._maxListenersPerEvent,
408
- }, `Approaching listener limit for event "${eventName}"`);
165
+ logger.warn({ eventName, current: currentEventCount, max: this._maxListenersPerEvent }, `Approaching listener limit for event "${eventName}"`);
409
166
  }
410
167
  if (this._totalListeners >= totalThreshold) {
411
- logger.warn({ current: this._totalListeners, max: this._maxTotalListeners }, "Approaching total listener limit");
168
+ logger.warn({ current: this._totalListeners, max: this._maxTotalListeners }, 'Approaching total listener limit');
412
169
  }
413
170
  }
414
171
  /**
@@ -441,28 +198,10 @@ export class TrustEngine extends EventEmitter {
441
198
  };
442
199
  }
443
200
  /**
444
- * Get the current decay rate
445
- */
446
- get decayRate() {
447
- return this._decayRate;
448
- }
449
- /**
450
- * Get the decay interval in milliseconds
451
- */
452
- get decayIntervalMs() {
453
- return this._decayIntervalMs;
454
- }
455
- /**
456
- * Get the failure threshold
457
- */
458
- get failureThreshold() {
459
- return this._failureThreshold;
460
- }
461
- /**
462
- * Get the accelerated decay multiplier
201
+ * Get the decay check interval in milliseconds
463
202
  */
464
- get acceleratedDecayMultiplier() {
465
- return this._acceleratedDecayMultiplier;
203
+ get decayCheckIntervalMs() {
204
+ return this._decayCheckIntervalMs;
466
205
  }
467
206
  /**
468
207
  * Get the persistence provider
@@ -493,14 +232,14 @@ export class TrustEngine extends EventEmitter {
493
232
  */
494
233
  async loadFromPersistence() {
495
234
  if (!this._persistence) {
496
- throw new Error("No persistence provider configured");
235
+ throw new Error('No persistence provider configured');
497
236
  }
498
237
  const records = await this._persistence.query();
499
238
  this.records.clear();
500
239
  for (const record of records) {
501
240
  this.records.set(record.entityId, record);
502
241
  }
503
- logger.info({ count: records.length }, "Loaded trust records from persistence");
242
+ logger.info({ count: records.length }, 'Loaded trust records from persistence');
504
243
  return records.length;
505
244
  }
506
245
  /**
@@ -508,14 +247,14 @@ export class TrustEngine extends EventEmitter {
508
247
  */
509
248
  async saveToPersistence() {
510
249
  if (!this._persistence) {
511
- throw new Error("No persistence provider configured");
250
+ throw new Error('No persistence provider configured');
512
251
  }
513
252
  let count = 0;
514
253
  for (const record of this.records.values()) {
515
254
  await this._persistence.save(record);
516
255
  count++;
517
256
  }
518
- logger.info({ count }, "Saved trust records to persistence");
257
+ logger.info({ count }, 'Saved trust records to persistence');
519
258
  return count;
520
259
  }
521
260
  /**
@@ -540,8 +279,8 @@ export class TrustEngine extends EventEmitter {
540
279
  */
541
280
  emitTrustEvent(event) {
542
281
  this.emit(event.type, event);
543
- this.emit("trust:*", event); // Wildcard for all events
544
- logger.debug({ event }, "Trust event emitted");
282
+ this.emit('trust:*', event); // Wildcard for all events
283
+ logger.debug({ event }, 'Trust event emitted');
545
284
  }
546
285
  /**
547
286
  * Calculate trust score for an entity
@@ -549,41 +288,34 @@ export class TrustEngine extends EventEmitter {
549
288
  async calculate(entityId) {
550
289
  const record = this.records.get(entityId);
551
290
  const signals = record?.signals ?? [];
552
- const currentLevel = record?.level ?? 1;
553
- // Calculate component scores
554
- const components = this.calculateComponents(signals, currentLevel);
555
- // Calculate weighted total
556
- const score = Math.round(components.behavioral * SIGNAL_WEIGHTS.behavioral * 1000 +
557
- components.compliance * SIGNAL_WEIGHTS.compliance * 1000 +
558
- components.identity * SIGNAL_WEIGHTS.identity * 1000 +
559
- components.context * SIGNAL_WEIGHTS.context * 1000);
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
+ }
560
304
  // Clamp to valid range
561
305
  const clampedScore = Math.max(0, Math.min(1000, score));
562
306
  const level = this.scoreToLevel(clampedScore);
307
+ // Backwards compat: also compute legacy 4-bucket components
308
+ const components = this.calculateComponents(signals);
563
309
  const factors = this.getSignificantFactors(components);
564
- logger.debug({ entityId, score: clampedScore, level, components }, "Trust calculated");
310
+ logger.debug({ entityId, score: clampedScore, level, factorScores }, 'Trust calculated');
565
311
  return {
566
312
  score: clampedScore,
567
313
  level,
568
314
  components,
315
+ factorScores,
569
316
  factors,
570
317
  };
571
318
  }
572
- /**
573
- * Check if an entity has accelerated decay active
574
- */
575
- hasAcceleratedDecay(record) {
576
- const now = Date.now();
577
- const recentFailures = record.recentFailures.filter((timestamp) => now - new Date(timestamp).getTime() < this._failureWindowMs);
578
- return recentFailures.length >= this._minFailuresForAcceleration;
579
- }
580
- /**
581
- * Clean up old failure timestamps outside the window
582
- */
583
- cleanupFailures(record) {
584
- const now = Date.now();
585
- record.recentFailures = record.recentFailures.filter((timestamp) => now - new Date(timestamp).getTime() < this._failureWindowMs);
586
- }
587
319
  /**
588
320
  * Clean up old success timestamps outside the window
589
321
  */
@@ -628,7 +360,7 @@ export class TrustEngine extends EventEmitter {
628
360
  record.lastCalculatedAt = new Date().toISOString();
629
361
  // Emit recovery event
630
362
  this.emitTrustEvent({
631
- type: "trust:recovery_applied",
363
+ type: 'trust:recovery_applied',
632
364
  entityId: record.entityId,
633
365
  timestamp: new Date().toISOString(),
634
366
  signal,
@@ -641,10 +373,10 @@ export class TrustEngine extends EventEmitter {
641
373
  // Check for milestones
642
374
  if (previousLevel !== record.level && record.level > previousLevel) {
643
375
  this.emitTrustEvent({
644
- type: "trust:recovery_milestone",
376
+ type: 'trust:recovery_milestone',
645
377
  entityId: record.entityId,
646
378
  timestamp: new Date().toISOString(),
647
- milestone: "tier_restored",
379
+ milestone: 'tier_restored',
648
380
  previousScore,
649
381
  newScore: record.score,
650
382
  details: `Promoted from ${TRUST_LEVEL_NAMES[previousLevel]} to ${TRUST_LEVEL_NAMES[record.level]}`,
@@ -653,10 +385,10 @@ export class TrustEngine extends EventEmitter {
653
385
  // Check if accelerated recovery was just earned
654
386
  if (record.consecutiveSuccesses === this._minSuccessesForAcceleration) {
655
387
  this.emitTrustEvent({
656
- type: "trust:recovery_milestone",
388
+ type: 'trust:recovery_milestone',
657
389
  entityId: record.entityId,
658
390
  timestamp: new Date().toISOString(),
659
- milestone: "accelerated_recovery_earned",
391
+ milestone: 'accelerated_recovery_earned',
660
392
  previousScore,
661
393
  newScore: record.score,
662
394
  details: `Earned accelerated recovery after ${this._minSuccessesForAcceleration} consecutive successes`,
@@ -665,10 +397,10 @@ export class TrustEngine extends EventEmitter {
665
397
  // Check for full recovery
666
398
  if (record.score >= record.peakScore && previousScore < record.peakScore) {
667
399
  this.emitTrustEvent({
668
- type: "trust:recovery_milestone",
400
+ type: 'trust:recovery_milestone',
669
401
  entityId: record.entityId,
670
402
  timestamp: new Date().toISOString(),
671
- milestone: "full_recovery",
403
+ milestone: 'full_recovery',
672
404
  previousScore,
673
405
  newScore: record.score,
674
406
  details: `Fully recovered to peak score of ${record.peakScore}`,
@@ -680,7 +412,7 @@ export class TrustEngine extends EventEmitter {
680
412
  newScore: record.score,
681
413
  recoveryAmount,
682
414
  accelerated,
683
- }, "Trust recovery applied");
415
+ }, 'Trust recovery applied');
684
416
  }
685
417
  /**
686
418
  * Get trust score for an entity (with automatic decay)
@@ -688,25 +420,14 @@ export class TrustEngine extends EventEmitter {
688
420
  async getScore(entityId) {
689
421
  const record = this.records.get(entityId);
690
422
  if (record) {
691
- // Clean up old failures
692
- this.cleanupFailures(record);
693
- if (this._readinessMode === "checkpoint_schedule") {
694
- await this.applyScheduledReadinessAdjustment(record);
695
- return record;
696
- }
697
- // Apply decay if stale
423
+ // Apply decay if enough time has passed since last check
698
424
  const staleness = Date.now() - new Date(record.lastCalculatedAt).getTime();
699
- if (staleness > this._decayIntervalMs) {
425
+ if (staleness > this._decayCheckIntervalMs) {
700
426
  const previousScore = record.score;
701
427
  const previousLevel = record.level;
702
- // Check if accelerated decay should apply
703
- const accelerated = this.hasAcceleratedDecay(record);
704
- const effectiveDecayRate = accelerated
705
- ? this._decayRate * this._acceleratedDecayMultiplier
706
- : this._decayRate;
707
- // Apply decay based on staleness
708
- const decayPeriods = Math.floor(staleness / this._decayIntervalMs);
709
- const decayMultiplier = Math.pow(1 - effectiveDecayRate, decayPeriods);
428
+ // Milestone-based decay: convert staleness to days, apply stepped multiplier
429
+ const daysSinceActivity = staleness / (24 * 60 * 60 * 1000);
430
+ const decayMultiplier = calculateDecayMultiplier(daysSinceActivity);
710
431
  const decayedScore = Math.round(record.score * decayMultiplier);
711
432
  const clampedScore = Math.max(0, decayedScore);
712
433
  record.score = clampedScore;
@@ -715,26 +436,25 @@ export class TrustEngine extends EventEmitter {
715
436
  // Emit decay event
716
437
  if (previousScore !== record.score) {
717
438
  this.emitTrustEvent({
718
- type: "trust:decay_applied",
439
+ type: 'trust:decay_applied',
719
440
  entityId,
720
441
  timestamp: new Date().toISOString(),
721
442
  previousScore,
722
443
  newScore: record.score,
723
444
  decayAmount: previousScore - record.score,
724
445
  stalenessMs: staleness,
725
- accelerated,
726
446
  });
727
447
  // Emit tier change if applicable
728
448
  if (previousLevel !== record.level) {
729
449
  this.emitTrustEvent({
730
- type: "trust:tier_changed",
450
+ type: 'trust:tier_changed',
731
451
  entityId,
732
452
  timestamp: new Date().toISOString(),
733
453
  previousLevel,
734
454
  newLevel: record.level,
735
455
  previousLevelName: TRUST_LEVEL_NAMES[previousLevel],
736
456
  newLevelName: TRUST_LEVEL_NAMES[record.level],
737
- direction: record.level < previousLevel ? "demoted" : "promoted",
457
+ direction: record.level < previousLevel ? 'demoted' : 'promoted',
738
458
  });
739
459
  }
740
460
  // Auto-persist after decay
@@ -744,10 +464,35 @@ export class TrustEngine extends EventEmitter {
744
464
  }
745
465
  return record;
746
466
  }
467
+ /**
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
472
+ */
473
+ validateSignal(signal) {
474
+ if (!signal) {
475
+ throw new Error('Signal is required');
476
+ }
477
+ if (!signal.entityId) {
478
+ throw new Error('Signal must have an entityId');
479
+ }
480
+ if (!signal.type || typeof signal.type !== 'string') {
481
+ throw new Error('Signal must have a non-empty string type');
482
+ }
483
+ if (!Number.isFinite(signal.value)) {
484
+ throw new Error(`Invalid signal value: ${signal.value} (must be a finite number)`);
485
+ }
486
+ if (signal.value < 0 || signal.value > 1) {
487
+ throw new Error(`Signal value out of range: ${signal.value} (must be 0.0–1.0)`);
488
+ }
489
+ }
747
490
  /**
748
491
  * Record a trust signal
492
+ * @throws Error if signal is invalid (NaN, out of range, missing fields)
749
493
  */
750
494
  async recordSignal(signal) {
495
+ this.validateSignal(signal);
751
496
  let record = this.records.get(signal.entityId);
752
497
  let isNewEntity = false;
753
498
  if (!record) {
@@ -757,28 +502,10 @@ export class TrustEngine extends EventEmitter {
757
502
  }
758
503
  const previousScore = record.score;
759
504
  const previousLevel = record.level;
760
- // Detect failure signals
761
- if (signal.value < this._failureThreshold) {
762
- record.recentFailures.push(signal.timestamp);
763
- this.cleanupFailures(record);
764
- // Reset consecutive successes on failure
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) {
765
508
  record.consecutiveSuccesses = 0;
766
- const acceleratedDecayActive = this.hasAcceleratedDecay(record);
767
- this.emitTrustEvent({
768
- type: "trust:failure_detected",
769
- entityId: signal.entityId,
770
- timestamp: new Date().toISOString(),
771
- signal,
772
- failureCount: record.recentFailures.length,
773
- acceleratedDecayActive,
774
- });
775
- logger.warn({
776
- entityId: signal.entityId,
777
- signalType: signal.type,
778
- signalValue: signal.value,
779
- failureCount: record.recentFailures.length,
780
- acceleratedDecayActive,
781
- }, "Failure signal detected");
782
509
  }
783
510
  // Detect success signals and apply recovery
784
511
  if (signal.value >= this._successThreshold) {
@@ -796,7 +523,7 @@ export class TrustEngine extends EventEmitter {
796
523
  signalValue: signal.value,
797
524
  consecutiveSuccesses: record.consecutiveSuccesses,
798
525
  recoveryAmount,
799
- }, "Success signal detected");
526
+ }, 'Success signal detected');
800
527
  }
801
528
  // Add signal
802
529
  record.signals.push(signal);
@@ -810,13 +537,8 @@ export class TrustEngine extends EventEmitter {
810
537
  record.score = calculation.score;
811
538
  record.level = calculation.level;
812
539
  record.components = calculation.components;
540
+ record.factorScores = calculation.factorScores;
813
541
  record.lastCalculatedAt = new Date().toISOString();
814
- record.readinessCheckpointIndex = 0;
815
- record.deferredReadinessMultiplier = 1;
816
- record.readinessBaselineScore = calculation.score;
817
- record.freshnessCheckpointIndex = 0;
818
- record.deferredFreshnessMultiplier = 1;
819
- record.freshnessBaselineScore = calculation.score;
820
542
  // Record history if significant change
821
543
  if (Math.abs(calculation.score - previousScore) >= 10) {
822
544
  record.history.push({
@@ -832,7 +554,7 @@ export class TrustEngine extends EventEmitter {
832
554
  }
833
555
  // Emit signal recorded event
834
556
  this.emitTrustEvent({
835
- type: "trust:signal_recorded",
557
+ type: 'trust:signal_recorded',
836
558
  entityId: signal.entityId,
837
559
  timestamp: new Date().toISOString(),
838
560
  signal,
@@ -842,7 +564,7 @@ export class TrustEngine extends EventEmitter {
842
564
  // Emit score changed event if significant
843
565
  if (Math.abs(calculation.score - previousScore) >= 5) {
844
566
  this.emitTrustEvent({
845
- type: "trust:score_changed",
567
+ type: 'trust:score_changed',
846
568
  entityId: signal.entityId,
847
569
  timestamp: new Date().toISOString(),
848
570
  previousScore,
@@ -854,14 +576,14 @@ export class TrustEngine extends EventEmitter {
854
576
  // Emit tier changed event if applicable
855
577
  if (previousLevel !== calculation.level && !isNewEntity) {
856
578
  this.emitTrustEvent({
857
- type: "trust:tier_changed",
579
+ type: 'trust:tier_changed',
858
580
  entityId: signal.entityId,
859
581
  timestamp: new Date().toISOString(),
860
582
  previousLevel,
861
583
  newLevel: calculation.level,
862
584
  previousLevelName: TRUST_LEVEL_NAMES[previousLevel],
863
585
  newLevelName: TRUST_LEVEL_NAMES[calculation.level],
864
- direction: calculation.level > previousLevel ? "promoted" : "demoted",
586
+ direction: calculation.level > previousLevel ? 'promoted' : 'demoted',
865
587
  });
866
588
  }
867
589
  // Auto-persist if enabled
@@ -870,13 +592,18 @@ export class TrustEngine extends EventEmitter {
870
592
  entityId: signal.entityId,
871
593
  signalType: signal.type,
872
594
  newScore: calculation.score,
873
- }, "Signal recorded");
595
+ }, 'Signal recorded');
874
596
  }
875
597
  /**
876
598
  * Initialize trust for a new entity
877
599
  */
878
600
  async initializeEntity(entityId, initialLevel = 1) {
879
601
  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
+ }
880
607
  const record = {
881
608
  entityId,
882
609
  score,
@@ -887,39 +614,33 @@ export class TrustEngine extends EventEmitter {
887
614
  identity: 0.5,
888
615
  context: 0.5,
889
616
  },
617
+ factorScores: initialFactorScores,
890
618
  signals: [],
891
619
  lastCalculatedAt: new Date().toISOString(),
892
620
  history: [
893
621
  {
894
622
  score,
895
623
  level: initialLevel,
896
- reason: "Initial registration",
624
+ reason: 'Initial registration',
897
625
  timestamp: new Date().toISOString(),
898
626
  },
899
627
  ],
900
- recentFailures: [],
901
628
  recentSuccesses: [],
902
629
  peakScore: score,
903
630
  consecutiveSuccesses: 0,
904
- readinessCheckpointIndex: 0,
905
- deferredReadinessMultiplier: 1,
906
- readinessBaselineScore: score,
907
- freshnessCheckpointIndex: 0,
908
- deferredFreshnessMultiplier: 1,
909
- freshnessBaselineScore: score,
910
631
  };
911
632
  this.records.set(entityId, record);
912
633
  // Auto-persist if enabled
913
634
  await this.autoPersistRecord(record);
914
635
  // Emit initialized event
915
636
  this.emitTrustEvent({
916
- type: "trust:initialized",
637
+ type: 'trust:initialized',
917
638
  entityId,
918
639
  timestamp: new Date().toISOString(),
919
640
  initialScore: score,
920
641
  initialLevel,
921
642
  });
922
- logger.info({ entityId, initialLevel }, "Entity trust initialized");
643
+ logger.info({ entityId, initialLevel }, 'Entity trust initialized');
923
644
  return record;
924
645
  }
925
646
  /**
@@ -934,6 +655,76 @@ export class TrustEngine extends EventEmitter {
934
655
  getLevelName(level) {
935
656
  return TRUST_LEVEL_NAMES[level];
936
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
+ * ```
671
+ */
672
+ async explainScore(entityId) {
673
+ const record = this.records.get(entityId);
674
+ if (!record) {
675
+ throw new Error(`Entity not found: ${entityId}`);
676
+ }
677
+ const signals = record.signals ?? [];
678
+ const factorScores = this.calculateFactorScores(signals);
679
+ // Build factor breakdown
680
+ 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 };
685
+ });
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;
708
+ // 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;
712
+ return {
713
+ entityId,
714
+ 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,
720
+ factorBreakdown,
721
+ topPositiveFactors,
722
+ topNegativeFactors,
723
+ decayMultiplier: Math.round(decayMultiplier * 1000) / 1000,
724
+ daysSinceLastSignal: daysSinceLastSignal !== null ? Math.round(daysSinceLastSignal * 100) / 100 : null,
725
+ generatedAt: new Date().toISOString(),
726
+ };
727
+ }
937
728
  /**
938
729
  * Convert score to trust level
939
730
  */
@@ -946,25 +737,61 @@ export class TrustEngine extends EventEmitter {
946
737
  return 0;
947
738
  }
948
739
  /**
740
+ * @deprecated Use calculateFactorScores for 16-factor model. Kept for backwards compatibility.
949
741
  * Calculate component scores from signals
950
742
  */
951
- calculateComponents(signals, currentLevel) {
743
+ calculateComponents(signals) {
952
744
  // Group signals by type
953
- const behavioral = signals.filter((s) => s.type.startsWith("behavioral."));
954
- const compliance = signals.filter((s) => s.type.startsWith("compliance."));
955
- const identity = signals.filter((s) => s.type.startsWith("identity."));
956
- const context = signals.filter((s) => s.type.startsWith("context."));
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.'));
957
749
  return {
958
- behavioral: this.averageSignalValue(behavioral, 0.5, currentLevel),
959
- compliance: this.averageSignalValue(compliance, 0.5, currentLevel),
960
- identity: this.averageSignalValue(identity, 0.5, currentLevel),
961
- context: this.averageSignalValue(context, 0.5, currentLevel),
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),
962
754
  };
963
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);
773
+ continue;
774
+ }
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);
781
+ }
782
+ }
783
+ }
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;
790
+ }
964
791
  /**
965
792
  * Calculate average signal value with default
966
793
  */
967
- averageSignalValue(signals, defaultValue, currentLevel) {
794
+ averageSignalValue(signals, defaultValue) {
968
795
  if (signals.length === 0)
969
796
  return defaultValue;
970
797
  // Weight recent signals more heavily
@@ -974,66 +801,27 @@ export class TrustEngine extends EventEmitter {
974
801
  for (const signal of signals) {
975
802
  const age = now - new Date(signal.timestamp).getTime();
976
803
  const weight = Math.exp(-age / (7 * 24 * 60 * 60 * 1000)); // 7-day half-life
977
- const adjustedValue = this.adjustSignalValueForTier(signal, defaultValue, currentLevel);
978
- weightedSum += adjustedValue * weight;
804
+ weightedSum += signal.value * weight;
979
805
  totalWeight += weight;
980
806
  }
981
807
  return totalWeight > 0 ? weightedSum / totalWeight : defaultValue;
982
808
  }
983
- /**
984
- * Human/manual positive approvals carry less upward impact at higher tiers.
985
- */
986
- adjustSignalValueForTier(signal, defaultValue, currentLevel) {
987
- if (!this.isHumanApprovalSignal(signal) || signal.value <= defaultValue) {
988
- return signal.value;
989
- }
990
- const assistFactor = this.getHumanApprovalAssistFactor(currentLevel);
991
- return defaultValue + (signal.value - defaultValue) * assistFactor;
992
- }
993
- isHumanApprovalSignal(signal) {
994
- const source = signal.source?.toLowerCase() ?? "";
995
- return (source === "manual" ||
996
- source === "human" ||
997
- source === "human_review" ||
998
- source === "human-approval");
999
- }
1000
- getHumanApprovalAssistFactor(level) {
1001
- switch (level) {
1002
- case 0:
1003
- case 1:
1004
- return 1.0;
1005
- case 2:
1006
- return 0.85;
1007
- case 3:
1008
- return 0.7;
1009
- case 4:
1010
- return 0.55;
1011
- case 5:
1012
- return 0.4;
1013
- case 6:
1014
- return 0.3;
1015
- case 7:
1016
- return 0.2;
1017
- default:
1018
- return 1.0;
1019
- }
1020
- }
1021
809
  /**
1022
810
  * Get significant factors affecting the score
1023
811
  */
1024
812
  getSignificantFactors(components) {
1025
813
  const factors = [];
1026
814
  if (components.behavioral < 0.3) {
1027
- factors.push("Low behavioral trust");
815
+ factors.push('Low behavioral trust');
1028
816
  }
1029
817
  if (components.compliance < 0.3) {
1030
- factors.push("Low compliance score");
818
+ factors.push('Low compliance score');
1031
819
  }
1032
820
  if (components.identity < 0.3) {
1033
- factors.push("Weak identity verification");
821
+ factors.push('Weak identity verification');
1034
822
  }
1035
823
  if (components.context < 0.3) {
1036
- factors.push("Unusual context signals");
824
+ factors.push('Unusual context signals');
1037
825
  }
1038
826
  return factors;
1039
827
  }
@@ -1042,6 +830,11 @@ export class TrustEngine extends EventEmitter {
1042
830
  */
1043
831
  createInitialRecord(entityId) {
1044
832
  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
+ }
1045
838
  return {
1046
839
  entityId,
1047
840
  score: initialScore, // Start at L1 (Provisional) minimum
@@ -1052,84 +845,15 @@ export class TrustEngine extends EventEmitter {
1052
845
  identity: 0.5,
1053
846
  context: 0.5,
1054
847
  },
848
+ factorScores: initialFactorScores,
1055
849
  signals: [],
1056
850
  lastCalculatedAt: new Date().toISOString(),
1057
851
  history: [],
1058
- recentFailures: [],
1059
852
  recentSuccesses: [],
1060
853
  peakScore: initialScore,
1061
854
  consecutiveSuccesses: 0,
1062
- readinessCheckpointIndex: 0,
1063
- deferredReadinessMultiplier: 1,
1064
- readinessBaselineScore: initialScore,
1065
- freshnessCheckpointIndex: 0,
1066
- deferredFreshnessMultiplier: 1,
1067
- freshnessBaselineScore: initialScore,
1068
855
  };
1069
856
  }
1070
- /**
1071
- * Configure a time-bound Readiness Degree exception for an entity.
1072
- */
1073
- setReadinessException(entityId, options) {
1074
- const record = this.records.get(entityId);
1075
- if (!record) {
1076
- throw new Error(`Entity not found: ${entityId}`);
1077
- }
1078
- this.validateReadinessExceptionOptions(options);
1079
- this.ensureReadinessState(record);
1080
- record.readinessException = {
1081
- reason: options.reason,
1082
- issuedAt: new Date().toISOString(),
1083
- expiresAt: options.expiresAt,
1084
- reductionScale: clampReductionScale(options.reductionScale),
1085
- };
1086
- record.freshnessException = record.readinessException;
1087
- }
1088
- /**
1089
- * @deprecated Use setReadinessException.
1090
- */
1091
- setFreshnessException(entityId, options) {
1092
- this.setReadinessException(entityId, options);
1093
- }
1094
- /**
1095
- * Clear Readiness Degree exception for an entity.
1096
- */
1097
- clearReadinessException(entityId) {
1098
- const record = this.records.get(entityId);
1099
- if (!record) {
1100
- return;
1101
- }
1102
- delete record.readinessException;
1103
- delete record.freshnessException;
1104
- }
1105
- /**
1106
- * @deprecated Use clearReadinessException.
1107
- */
1108
- clearFreshnessException(entityId) {
1109
- this.clearReadinessException(entityId);
1110
- }
1111
- /**
1112
- * Get active Readiness Degree exception for an entity.
1113
- */
1114
- getReadinessException(entityId) {
1115
- const record = this.records.get(entityId);
1116
- return record?.readinessException ?? record?.freshnessException;
1117
- }
1118
- /**
1119
- * @deprecated Use getReadinessException.
1120
- */
1121
- getFreshnessException(entityId) {
1122
- return this.getReadinessException(entityId);
1123
- }
1124
- /**
1125
- * Check if accelerated decay is currently active for an entity
1126
- */
1127
- isAcceleratedDecayActive(entityId) {
1128
- const record = this.records.get(entityId);
1129
- if (!record)
1130
- return false;
1131
- return this.hasAcceleratedDecay(record);
1132
- }
1133
857
  /**
1134
858
  * Check if accelerated recovery is currently active for an entity
1135
859
  */
@@ -1139,16 +863,6 @@ export class TrustEngine extends EventEmitter {
1139
863
  return false;
1140
864
  return this.hasAcceleratedRecovery(record);
1141
865
  }
1142
- /**
1143
- * Get current failure count for an entity
1144
- */
1145
- getFailureCount(entityId) {
1146
- const record = this.records.get(entityId);
1147
- if (!record)
1148
- return 0;
1149
- this.cleanupFailures(record);
1150
- return record.recentFailures.length;
1151
- }
1152
866
  /**
1153
867
  * Get consecutive success count for an entity
1154
868
  */