@vorionsys/atsf-core 0.2.2 → 0.2.3
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.
- package/CHANGELOG.md +3 -3
- package/README.md +77 -11
- package/dist/api/index.d.ts +1 -1
- package/dist/api/index.js +1 -1
- package/dist/api/server.d.ts +5 -2
- package/dist/api/server.d.ts.map +1 -1
- package/dist/api/server.js +186 -149
- package/dist/api/server.js.map +1 -1
- package/dist/arbitration/index.d.ts +4 -4
- package/dist/arbitration/index.d.ts.map +1 -1
- package/dist/arbitration/index.js +46 -41
- package/dist/arbitration/index.js.map +1 -1
- package/dist/arbitration/types.d.ts +10 -10
- package/dist/arbitration/types.d.ts.map +1 -1
- package/dist/basis/evaluator.d.ts +1 -1
- package/dist/basis/evaluator.d.ts.map +1 -1
- package/dist/basis/evaluator.js +56 -54
- package/dist/basis/evaluator.js.map +1 -1
- package/dist/basis/index.d.ts +3 -3
- package/dist/basis/index.js +3 -3
- package/dist/basis/parser.d.ts +16 -16
- package/dist/basis/parser.d.ts.map +1 -1
- package/dist/basis/parser.js +32 -25
- package/dist/basis/parser.js.map +1 -1
- package/dist/basis/types.d.ts +2 -2
- package/dist/chain/index.d.ts.map +1 -1
- package/dist/chain/index.js +16 -16
- package/dist/chain/index.js.map +1 -1
- package/dist/cognigate/index.d.ts +1 -1
- package/dist/cognigate/index.d.ts.map +1 -1
- package/dist/cognigate/index.js +44 -33
- package/dist/cognigate/index.js.map +1 -1
- package/dist/common/adapters.d.ts +18 -11
- package/dist/common/adapters.d.ts.map +1 -1
- package/dist/common/adapters.js +100 -79
- package/dist/common/adapters.js.map +1 -1
- package/dist/common/config.d.ts +67 -67
- package/dist/common/config.js +49 -49
- package/dist/common/config.js.map +1 -1
- package/dist/common/index.d.ts +4 -4
- package/dist/common/index.js +4 -4
- package/dist/common/logger.d.ts +1 -1
- package/dist/common/logger.js +8 -8
- package/dist/common/types.d.ts +8 -8
- package/dist/common/types.js +5 -5
- package/dist/containment/index.d.ts +3 -3
- package/dist/containment/index.d.ts.map +1 -1
- package/dist/containment/index.js +119 -105
- package/dist/containment/index.js.map +1 -1
- package/dist/containment/types.d.ts +11 -11
- package/dist/containment/types.d.ts.map +1 -1
- package/dist/contracts/index.d.ts +9 -9
- package/dist/contracts/index.d.ts.map +1 -1
- package/dist/contracts/index.js +59 -54
- package/dist/contracts/index.js.map +1 -1
- package/dist/contracts/types.d.ts +12 -12
- package/dist/contracts/types.d.ts.map +1 -1
- package/dist/crewai/callback.d.ts +91 -0
- package/dist/crewai/callback.d.ts.map +1 -0
- package/dist/crewai/callback.js +271 -0
- package/dist/crewai/callback.js.map +1 -0
- package/dist/crewai/executor.d.ts +135 -0
- package/dist/crewai/executor.d.ts.map +1 -0
- package/dist/crewai/executor.js +381 -0
- package/dist/crewai/executor.js.map +1 -0
- package/dist/crewai/index.d.ts +12 -0
- package/dist/crewai/index.d.ts.map +1 -0
- package/dist/crewai/index.js +12 -0
- package/dist/crewai/index.js.map +1 -0
- package/dist/crewai/tools.d.ts +21 -0
- package/dist/crewai/tools.d.ts.map +1 -0
- package/dist/crewai/tools.js +164 -0
- package/dist/crewai/tools.js.map +1 -0
- package/dist/crewai/types.d.ts +139 -0
- package/dist/crewai/types.d.ts.map +1 -0
- package/dist/crewai/types.js +9 -0
- package/dist/crewai/types.js.map +1 -0
- package/dist/enforce/index.d.ts +48 -222
- package/dist/enforce/index.d.ts.map +1 -1
- package/dist/enforce/index.js +144 -47
- package/dist/enforce/index.js.map +1 -1
- package/dist/enforce/trust-aware-enforcement-service.d.ts +121 -0
- package/dist/enforce/trust-aware-enforcement-service.d.ts.map +1 -0
- package/dist/enforce/trust-aware-enforcement-service.js +601 -0
- package/dist/enforce/trust-aware-enforcement-service.js.map +1 -0
- package/dist/enforce/types.d.ts +234 -0
- package/dist/enforce/types.d.ts.map +1 -0
- package/dist/enforce/types.js +10 -0
- package/dist/enforce/types.js.map +1 -0
- package/dist/governance/fluid-workflow.d.ts +8 -8
- package/dist/governance/fluid-workflow.d.ts.map +1 -1
- package/dist/governance/fluid-workflow.js +114 -86
- package/dist/governance/fluid-workflow.js.map +1 -1
- package/dist/governance/index.d.ts +7 -7
- package/dist/governance/index.d.ts.map +1 -1
- package/dist/governance/index.js +81 -74
- package/dist/governance/index.js.map +1 -1
- package/dist/governance/proof-bridge.d.ts +6 -6
- package/dist/governance/proof-bridge.d.ts.map +1 -1
- package/dist/governance/proof-bridge.js +5 -5
- package/dist/governance/proof-bridge.js.map +1 -1
- package/dist/governance/types.d.ts +16 -9
- package/dist/governance/types.d.ts.map +1 -1
- package/dist/governance/types.js.map +1 -1
- package/dist/index.d.ts +29 -25
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +33 -23
- package/dist/index.js.map +1 -1
- package/dist/intent/index.d.ts +21 -56
- package/dist/intent/index.d.ts.map +1 -1
- package/dist/intent/index.js +58 -24
- package/dist/intent/index.js.map +1 -1
- package/dist/intent/persistent-intent-service.d.ts +68 -0
- package/dist/intent/persistent-intent-service.d.ts.map +1 -0
- package/dist/intent/persistent-intent-service.js +277 -0
- package/dist/intent/persistent-intent-service.js.map +1 -0
- package/dist/intent/types.d.ts +69 -0
- package/dist/intent/types.d.ts.map +1 -0
- package/dist/intent/types.js +10 -0
- package/dist/intent/types.js.map +1 -0
- package/dist/intent-gateway/index.d.ts +522 -0
- package/dist/intent-gateway/index.d.ts.map +1 -0
- package/dist/intent-gateway/index.js +1499 -0
- package/dist/intent-gateway/index.js.map +1 -0
- package/dist/langchain/callback.d.ts +2 -2
- package/dist/langchain/callback.d.ts.map +1 -1
- package/dist/langchain/callback.js +30 -30
- package/dist/langchain/callback.js.map +1 -1
- package/dist/langchain/executor.d.ts +4 -4
- package/dist/langchain/executor.d.ts.map +1 -1
- package/dist/langchain/executor.js +82 -80
- package/dist/langchain/executor.js.map +1 -1
- package/dist/langchain/index.d.ts +5 -5
- package/dist/langchain/index.js +5 -5
- package/dist/langchain/tools.d.ts +1 -1
- package/dist/langchain/tools.d.ts.map +1 -1
- package/dist/langchain/tools.js +33 -33
- package/dist/langchain/tools.js.map +1 -1
- package/dist/langchain/types.d.ts +3 -3
- package/dist/langchain/types.d.ts.map +1 -1
- package/dist/layers/implementations/L0-request-format.d.ts +37 -0
- package/dist/layers/implementations/L0-request-format.d.ts.map +1 -0
- package/dist/layers/implementations/L0-request-format.js +218 -0
- package/dist/layers/implementations/L0-request-format.js.map +1 -0
- package/dist/layers/implementations/L1-input-size.d.ts +36 -0
- package/dist/layers/implementations/L1-input-size.d.ts.map +1 -0
- package/dist/layers/implementations/L1-input-size.js +160 -0
- package/dist/layers/implementations/L1-input-size.js.map +1 -0
- package/dist/layers/implementations/L2-charset-sanitizer.d.ts +28 -0
- package/dist/layers/implementations/L2-charset-sanitizer.d.ts.map +1 -0
- package/dist/layers/implementations/L2-charset-sanitizer.js +230 -0
- package/dist/layers/implementations/L2-charset-sanitizer.js.map +1 -0
- package/dist/layers/implementations/L3-schema-conformance.d.ts +47 -0
- package/dist/layers/implementations/L3-schema-conformance.d.ts.map +1 -0
- package/dist/layers/implementations/L3-schema-conformance.js +267 -0
- package/dist/layers/implementations/L3-schema-conformance.js.map +1 -0
- package/dist/layers/implementations/L4-injection-detector.d.ts +47 -0
- package/dist/layers/implementations/L4-injection-detector.d.ts.map +1 -0
- package/dist/layers/implementations/L4-injection-detector.js +260 -0
- package/dist/layers/implementations/L4-injection-detector.js.map +1 -0
- package/dist/layers/implementations/L5-rate-limiter.d.ts +51 -0
- package/dist/layers/implementations/L5-rate-limiter.d.ts.map +1 -0
- package/dist/layers/implementations/L5-rate-limiter.js +183 -0
- package/dist/layers/implementations/L5-rate-limiter.js.map +1 -0
- package/dist/layers/implementations/index.d.ts +16 -0
- package/dist/layers/implementations/index.d.ts.map +1 -0
- package/dist/layers/implementations/index.js +16 -0
- package/dist/layers/implementations/index.js.map +1 -0
- package/dist/layers/index.d.ts +3 -3
- package/dist/layers/index.d.ts.map +1 -1
- package/dist/layers/index.js +99 -71
- package/dist/layers/index.js.map +1 -1
- package/dist/layers/types.d.ts +16 -16
- package/dist/layers/types.d.ts.map +1 -1
- package/dist/persistence/file.d.ts +3 -3
- package/dist/persistence/file.d.ts.map +1 -1
- package/dist/persistence/file.js +32 -28
- package/dist/persistence/file.js.map +1 -1
- package/dist/persistence/index.d.ts +7 -7
- package/dist/persistence/index.d.ts.map +1 -1
- package/dist/persistence/index.js +18 -18
- package/dist/persistence/index.js.map +1 -1
- package/dist/persistence/memory.d.ts +3 -3
- package/dist/persistence/memory.d.ts.map +1 -1
- package/dist/persistence/memory.js +10 -8
- package/dist/persistence/memory.js.map +1 -1
- package/dist/persistence/sqlite.d.ts +3 -3
- package/dist/persistence/sqlite.d.ts.map +1 -1
- package/dist/persistence/sqlite.js +36 -36
- package/dist/persistence/sqlite.js.map +1 -1
- package/dist/persistence/supabase.d.ts +3 -3
- package/dist/persistence/supabase.d.ts.map +1 -1
- package/dist/persistence/supabase.js +41 -43
- package/dist/persistence/supabase.js.map +1 -1
- package/dist/persistence/types.d.ts +5 -5
- package/dist/phase6/ceiling.d.ts +5 -5
- package/dist/phase6/ceiling.d.ts.map +1 -1
- package/dist/phase6/ceiling.js +67 -34
- package/dist/phase6/ceiling.js.map +1 -1
- package/dist/phase6/context.d.ts +3 -3
- package/dist/phase6/context.d.ts.map +1 -1
- package/dist/phase6/context.js +91 -45
- package/dist/phase6/context.js.map +1 -1
- package/dist/phase6/index.d.ts +13 -13
- package/dist/phase6/index.d.ts.map +1 -1
- package/dist/phase6/index.js +16 -16
- package/dist/phase6/index.js.map +1 -1
- package/dist/phase6/presets.d.ts +2 -2
- package/dist/phase6/presets.d.ts.map +1 -1
- package/dist/phase6/presets.js +39 -33
- package/dist/phase6/presets.js.map +1 -1
- package/dist/phase6/provenance.d.ts +4 -4
- package/dist/phase6/provenance.d.ts.map +1 -1
- package/dist/phase6/provenance.js +42 -35
- package/dist/phase6/provenance.js.map +1 -1
- package/dist/phase6/role-gates/index.d.ts +2 -2
- package/dist/phase6/role-gates/index.js +2 -2
- package/dist/phase6/role-gates/kernel.d.ts.map +1 -1
- package/dist/phase6/role-gates/kernel.js +16 -16
- package/dist/phase6/role-gates/kernel.js.map +1 -1
- package/dist/phase6/role-gates/policy.d.ts +2 -2
- package/dist/phase6/role-gates/policy.js +6 -6
- package/dist/phase6/role-gates.d.ts +4 -4
- package/dist/phase6/role-gates.d.ts.map +1 -1
- package/dist/phase6/role-gates.js +80 -58
- package/dist/phase6/role-gates.js.map +1 -1
- package/dist/phase6/types.d.ts +35 -35
- package/dist/phase6/types.d.ts.map +1 -1
- package/dist/phase6/types.js +166 -66
- package/dist/phase6/types.js.map +1 -1
- package/dist/phase6/weight-presets/canonical.d.ts +2 -2
- package/dist/phase6/weight-presets/canonical.d.ts.map +1 -1
- package/dist/phase6/weight-presets/canonical.js +12 -12
- package/dist/phase6/weight-presets/canonical.js.map +1 -1
- package/dist/phase6/weight-presets/deltas.d.ts +2 -2
- package/dist/phase6/weight-presets/deltas.d.ts.map +1 -1
- package/dist/phase6/weight-presets/deltas.js +27 -27
- package/dist/phase6/weight-presets/deltas.js.map +1 -1
- package/dist/phase6/weight-presets/index.d.ts +4 -4
- package/dist/phase6/weight-presets/index.js +4 -4
- package/dist/phase6/weight-presets/merger.d.ts +3 -3
- package/dist/phase6/weight-presets/merger.d.ts.map +1 -1
- package/dist/phase6/weight-presets/merger.js +40 -44
- package/dist/phase6/weight-presets/merger.js.map +1 -1
- package/dist/proof/index.d.ts +3 -3
- package/dist/proof/index.d.ts.map +1 -1
- package/dist/proof/index.js +44 -38
- package/dist/proof/index.js.map +1 -1
- package/dist/proof/merkle.d.ts +3 -3
- package/dist/proof/merkle.d.ts.map +1 -1
- package/dist/proof/merkle.js +26 -25
- package/dist/proof/merkle.js.map +1 -1
- package/dist/proof/zk-proofs.d.ts +6 -6
- package/dist/proof/zk-proofs.d.ts.map +1 -1
- package/dist/proof/zk-proofs.js +42 -43
- package/dist/proof/zk-proofs.js.map +1 -1
- package/dist/provenance/index.d.ts +3 -3
- package/dist/provenance/index.d.ts.map +1 -1
- package/dist/provenance/index.js +19 -17
- package/dist/provenance/index.js.map +1 -1
- package/dist/provenance/types.d.ts +4 -4
- package/dist/provenance/types.d.ts.map +1 -1
- package/dist/sandbox-training/challenges.d.ts +1 -1
- package/dist/sandbox-training/challenges.d.ts.map +1 -1
- package/dist/sandbox-training/challenges.js +228 -228
- package/dist/sandbox-training/challenges.js.map +1 -1
- package/dist/sandbox-training/graduation.d.ts +1 -1
- package/dist/sandbox-training/graduation.d.ts.map +1 -1
- package/dist/sandbox-training/graduation.js +14 -15
- package/dist/sandbox-training/graduation.js.map +1 -1
- package/dist/sandbox-training/index.d.ts +9 -9
- package/dist/sandbox-training/index.d.ts.map +1 -1
- package/dist/sandbox-training/index.js +6 -6
- package/dist/sandbox-training/index.js.map +1 -1
- package/dist/sandbox-training/promotion-service.d.ts +4 -4
- package/dist/sandbox-training/promotion-service.d.ts.map +1 -1
- package/dist/sandbox-training/promotion-service.js +5 -5
- package/dist/sandbox-training/promotion-service.js.map +1 -1
- package/dist/sandbox-training/runner.d.ts +1 -1
- package/dist/sandbox-training/runner.d.ts.map +1 -1
- package/dist/sandbox-training/runner.js +74 -73
- package/dist/sandbox-training/runner.js.map +1 -1
- package/dist/sandbox-training/scorer.d.ts +4 -4
- package/dist/sandbox-training/scorer.js +5 -5
- package/dist/sandbox-training/types.d.ts +4 -4
- package/dist/sandbox-training/types.d.ts.map +1 -1
- package/dist/sandbox-training/types.js +11 -7
- package/dist/sandbox-training/types.js.map +1 -1
- package/dist/trust-engine/ceiling-enforcement/audit.d.ts +1 -1
- package/dist/trust-engine/ceiling-enforcement/audit.d.ts.map +1 -1
- package/dist/trust-engine/ceiling-enforcement/audit.js +3 -4
- package/dist/trust-engine/ceiling-enforcement/audit.js.map +1 -1
- package/dist/trust-engine/ceiling-enforcement/index.d.ts +2 -2
- package/dist/trust-engine/ceiling-enforcement/index.js +2 -2
- package/dist/trust-engine/ceiling-enforcement/kernel.d.ts +1 -1
- package/dist/trust-engine/ceiling-enforcement/kernel.d.ts.map +1 -1
- package/dist/trust-engine/ceiling-enforcement/kernel.js +1 -1
- package/dist/trust-engine/context-policy/enforcement.d.ts.map +1 -1
- package/dist/trust-engine/context-policy/factory.d.ts +1 -1
- package/dist/trust-engine/context-policy/factory.d.ts.map +1 -1
- package/dist/trust-engine/context-policy/factory.js +1 -1
- package/dist/trust-engine/context-policy/factory.js.map +1 -1
- package/dist/trust-engine/context-policy/index.d.ts +2 -2
- package/dist/trust-engine/context-policy/index.js +2 -2
- package/dist/trust-engine/creation-modifiers/index.d.ts +1 -1
- package/dist/trust-engine/creation-modifiers/index.js +1 -1
- package/dist/trust-engine/creation-modifiers/types.d.ts.map +1 -1
- package/dist/trust-engine/creation-modifiers/types.js +2 -3
- package/dist/trust-engine/creation-modifiers/types.js.map +1 -1
- package/dist/trust-engine/decay-profiles.d.ts +1 -1
- package/dist/trust-engine/decay-profiles.d.ts.map +1 -1
- package/dist/trust-engine/decay-profiles.js +4 -4
- package/dist/trust-engine/decay-profiles.js.map +1 -1
- package/dist/trust-engine/index.d.ts +111 -45
- package/dist/trust-engine/index.d.ts.map +1 -1
- package/dist/trust-engine/index.js +418 -61
- package/dist/trust-engine/index.js.map +1 -1
- package/dist/trust-engine/phase6-types.d.ts +10 -10
- package/dist/trust-engine/phase6-types.d.ts.map +1 -1
- package/dist/trust-engine/phase6-types.js +25 -23
- package/dist/trust-engine/phase6-types.js.map +1 -1
- package/dist/trust-engine/types.d.ts +77 -0
- package/dist/trust-engine/types.d.ts.map +1 -0
- package/dist/trust-engine/types.js +20 -0
- package/dist/trust-engine/types.js.map +1 -0
- package/package.json +5 -4
|
@@ -6,9 +6,11 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @packageDocumentation
|
|
8
8
|
*/
|
|
9
|
-
import { EventEmitter } from
|
|
10
|
-
import { createLogger } from
|
|
11
|
-
|
|
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" });
|
|
12
14
|
/**
|
|
13
15
|
* Trust level thresholds (8 tiers T0-T7) - per BASIS specification
|
|
14
16
|
*/
|
|
@@ -26,14 +28,14 @@ export const TRUST_THRESHOLDS = {
|
|
|
26
28
|
* Trust level names (8 tiers T0-T7) - per BASIS specification
|
|
27
29
|
*/
|
|
28
30
|
export const TRUST_LEVEL_NAMES = {
|
|
29
|
-
0:
|
|
30
|
-
1:
|
|
31
|
-
2:
|
|
32
|
-
3:
|
|
33
|
-
4:
|
|
34
|
-
5:
|
|
35
|
-
6:
|
|
36
|
-
7:
|
|
31
|
+
0: "Sandbox",
|
|
32
|
+
1: "Observed",
|
|
33
|
+
2: "Provisional",
|
|
34
|
+
3: "Monitored",
|
|
35
|
+
4: "Standard",
|
|
36
|
+
5: "Trusted",
|
|
37
|
+
6: "Certified",
|
|
38
|
+
7: "Autonomous",
|
|
37
39
|
};
|
|
38
40
|
/**
|
|
39
41
|
* Signal weights for score calculation
|
|
@@ -44,6 +46,25 @@ export const SIGNAL_WEIGHTS = {
|
|
|
44
46
|
identity: 0.2,
|
|
45
47
|
context: 0.15,
|
|
46
48
|
};
|
|
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
|
+
}
|
|
47
68
|
/**
|
|
48
69
|
* Trust Engine service with event emission and subscription limits
|
|
49
70
|
*/
|
|
@@ -70,20 +91,26 @@ export class TrustEngine extends EventEmitter {
|
|
|
70
91
|
_listenerWarningThreshold;
|
|
71
92
|
_listenerCounts = new Map();
|
|
72
93
|
_totalListeners = 0;
|
|
94
|
+
_readinessMode;
|
|
95
|
+
_readinessCheckpointDays;
|
|
96
|
+
_readinessCheckpointReductions;
|
|
97
|
+
_allowedReadinessExceptionReasons;
|
|
98
|
+
_readinessExceptionMaxDurationMs;
|
|
73
99
|
constructor(config = {}) {
|
|
74
100
|
super();
|
|
75
101
|
this._decayRate = config.decayRate ?? 0.01;
|
|
76
102
|
this._decayIntervalMs = config.decayIntervalMs ?? 60000;
|
|
77
103
|
this._failureThreshold = config.failureThreshold ?? 0.3;
|
|
78
|
-
this._acceleratedDecayMultiplier = config.acceleratedDecayMultiplier ??
|
|
104
|
+
this._acceleratedDecayMultiplier = config.acceleratedDecayMultiplier ?? 1.0;
|
|
79
105
|
this._failureWindowMs = config.failureWindowMs ?? 3600000; // 1 hour
|
|
80
106
|
this._minFailuresForAcceleration = config.minFailuresForAcceleration ?? 2;
|
|
81
107
|
this._persistence = config.persistence;
|
|
82
|
-
this._autoPersist = config.autoPersist ??
|
|
108
|
+
this._autoPersist = config.autoPersist ?? config.persistence !== undefined;
|
|
83
109
|
// Recovery configuration
|
|
84
110
|
this._successThreshold = config.successThreshold ?? 0.7;
|
|
85
111
|
this._recoveryRate = config.recoveryRate ?? 0.02;
|
|
86
|
-
this._acceleratedRecoveryMultiplier =
|
|
112
|
+
this._acceleratedRecoveryMultiplier =
|
|
113
|
+
config.acceleratedRecoveryMultiplier ?? 1.5;
|
|
87
114
|
this._minSuccessesForAcceleration = config.minSuccessesForAcceleration ?? 3;
|
|
88
115
|
this._successWindowMs = config.successWindowMs ?? 3600000; // 1 hour
|
|
89
116
|
this._maxRecoveryPerSignal = config.maxRecoveryPerSignal ?? 50;
|
|
@@ -91,9 +118,219 @@ export class TrustEngine extends EventEmitter {
|
|
|
91
118
|
this._maxListenersPerEvent = config.maxListenersPerEvent ?? 100;
|
|
92
119
|
this._maxTotalListeners = config.maxTotalListeners ?? 1000;
|
|
93
120
|
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;
|
|
94
146
|
// Set default max listeners on EventEmitter
|
|
95
147
|
this.setMaxListeners(this._maxListenersPerEvent);
|
|
96
148
|
}
|
|
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
|
+
}
|
|
97
334
|
/**
|
|
98
335
|
* Add event listener with subscription limits
|
|
99
336
|
* @throws Error if listener limits are exceeded
|
|
@@ -164,10 +401,14 @@ export class TrustEngine extends EventEmitter {
|
|
|
164
401
|
const eventThreshold = this._maxListenersPerEvent * this._listenerWarningThreshold;
|
|
165
402
|
const totalThreshold = this._maxTotalListeners * this._listenerWarningThreshold;
|
|
166
403
|
if (currentEventCount >= eventThreshold) {
|
|
167
|
-
logger.warn({
|
|
404
|
+
logger.warn({
|
|
405
|
+
eventName,
|
|
406
|
+
current: currentEventCount,
|
|
407
|
+
max: this._maxListenersPerEvent,
|
|
408
|
+
}, `Approaching listener limit for event "${eventName}"`);
|
|
168
409
|
}
|
|
169
410
|
if (this._totalListeners >= totalThreshold) {
|
|
170
|
-
logger.warn({ current: this._totalListeners, max: this._maxTotalListeners },
|
|
411
|
+
logger.warn({ current: this._totalListeners, max: this._maxTotalListeners }, "Approaching total listener limit");
|
|
171
412
|
}
|
|
172
413
|
}
|
|
173
414
|
/**
|
|
@@ -252,14 +493,14 @@ export class TrustEngine extends EventEmitter {
|
|
|
252
493
|
*/
|
|
253
494
|
async loadFromPersistence() {
|
|
254
495
|
if (!this._persistence) {
|
|
255
|
-
throw new Error(
|
|
496
|
+
throw new Error("No persistence provider configured");
|
|
256
497
|
}
|
|
257
498
|
const records = await this._persistence.query();
|
|
258
499
|
this.records.clear();
|
|
259
500
|
for (const record of records) {
|
|
260
501
|
this.records.set(record.entityId, record);
|
|
261
502
|
}
|
|
262
|
-
logger.info({ count: records.length },
|
|
503
|
+
logger.info({ count: records.length }, "Loaded trust records from persistence");
|
|
263
504
|
return records.length;
|
|
264
505
|
}
|
|
265
506
|
/**
|
|
@@ -267,14 +508,14 @@ export class TrustEngine extends EventEmitter {
|
|
|
267
508
|
*/
|
|
268
509
|
async saveToPersistence() {
|
|
269
510
|
if (!this._persistence) {
|
|
270
|
-
throw new Error(
|
|
511
|
+
throw new Error("No persistence provider configured");
|
|
271
512
|
}
|
|
272
513
|
let count = 0;
|
|
273
514
|
for (const record of this.records.values()) {
|
|
274
515
|
await this._persistence.save(record);
|
|
275
516
|
count++;
|
|
276
517
|
}
|
|
277
|
-
logger.info({ count },
|
|
518
|
+
logger.info({ count }, "Saved trust records to persistence");
|
|
278
519
|
return count;
|
|
279
520
|
}
|
|
280
521
|
/**
|
|
@@ -299,8 +540,8 @@ export class TrustEngine extends EventEmitter {
|
|
|
299
540
|
*/
|
|
300
541
|
emitTrustEvent(event) {
|
|
301
542
|
this.emit(event.type, event);
|
|
302
|
-
this.emit(
|
|
303
|
-
logger.debug({ event },
|
|
543
|
+
this.emit("trust:*", event); // Wildcard for all events
|
|
544
|
+
logger.debug({ event }, "Trust event emitted");
|
|
304
545
|
}
|
|
305
546
|
/**
|
|
306
547
|
* Calculate trust score for an entity
|
|
@@ -308,8 +549,9 @@ export class TrustEngine extends EventEmitter {
|
|
|
308
549
|
async calculate(entityId) {
|
|
309
550
|
const record = this.records.get(entityId);
|
|
310
551
|
const signals = record?.signals ?? [];
|
|
552
|
+
const currentLevel = record?.level ?? 1;
|
|
311
553
|
// Calculate component scores
|
|
312
|
-
const components = this.calculateComponents(signals);
|
|
554
|
+
const components = this.calculateComponents(signals, currentLevel);
|
|
313
555
|
// Calculate weighted total
|
|
314
556
|
const score = Math.round(components.behavioral * SIGNAL_WEIGHTS.behavioral * 1000 +
|
|
315
557
|
components.compliance * SIGNAL_WEIGHTS.compliance * 1000 +
|
|
@@ -319,7 +561,7 @@ export class TrustEngine extends EventEmitter {
|
|
|
319
561
|
const clampedScore = Math.max(0, Math.min(1000, score));
|
|
320
562
|
const level = this.scoreToLevel(clampedScore);
|
|
321
563
|
const factors = this.getSignificantFactors(components);
|
|
322
|
-
logger.debug({ entityId, score: clampedScore, level, components },
|
|
564
|
+
logger.debug({ entityId, score: clampedScore, level, components }, "Trust calculated");
|
|
323
565
|
return {
|
|
324
566
|
score: clampedScore,
|
|
325
567
|
level,
|
|
@@ -386,7 +628,7 @@ export class TrustEngine extends EventEmitter {
|
|
|
386
628
|
record.lastCalculatedAt = new Date().toISOString();
|
|
387
629
|
// Emit recovery event
|
|
388
630
|
this.emitTrustEvent({
|
|
389
|
-
type:
|
|
631
|
+
type: "trust:recovery_applied",
|
|
390
632
|
entityId: record.entityId,
|
|
391
633
|
timestamp: new Date().toISOString(),
|
|
392
634
|
signal,
|
|
@@ -399,10 +641,10 @@ export class TrustEngine extends EventEmitter {
|
|
|
399
641
|
// Check for milestones
|
|
400
642
|
if (previousLevel !== record.level && record.level > previousLevel) {
|
|
401
643
|
this.emitTrustEvent({
|
|
402
|
-
type:
|
|
644
|
+
type: "trust:recovery_milestone",
|
|
403
645
|
entityId: record.entityId,
|
|
404
646
|
timestamp: new Date().toISOString(),
|
|
405
|
-
milestone:
|
|
647
|
+
milestone: "tier_restored",
|
|
406
648
|
previousScore,
|
|
407
649
|
newScore: record.score,
|
|
408
650
|
details: `Promoted from ${TRUST_LEVEL_NAMES[previousLevel]} to ${TRUST_LEVEL_NAMES[record.level]}`,
|
|
@@ -411,10 +653,10 @@ export class TrustEngine extends EventEmitter {
|
|
|
411
653
|
// Check if accelerated recovery was just earned
|
|
412
654
|
if (record.consecutiveSuccesses === this._minSuccessesForAcceleration) {
|
|
413
655
|
this.emitTrustEvent({
|
|
414
|
-
type:
|
|
656
|
+
type: "trust:recovery_milestone",
|
|
415
657
|
entityId: record.entityId,
|
|
416
658
|
timestamp: new Date().toISOString(),
|
|
417
|
-
milestone:
|
|
659
|
+
milestone: "accelerated_recovery_earned",
|
|
418
660
|
previousScore,
|
|
419
661
|
newScore: record.score,
|
|
420
662
|
details: `Earned accelerated recovery after ${this._minSuccessesForAcceleration} consecutive successes`,
|
|
@@ -423,10 +665,10 @@ export class TrustEngine extends EventEmitter {
|
|
|
423
665
|
// Check for full recovery
|
|
424
666
|
if (record.score >= record.peakScore && previousScore < record.peakScore) {
|
|
425
667
|
this.emitTrustEvent({
|
|
426
|
-
type:
|
|
668
|
+
type: "trust:recovery_milestone",
|
|
427
669
|
entityId: record.entityId,
|
|
428
670
|
timestamp: new Date().toISOString(),
|
|
429
|
-
milestone:
|
|
671
|
+
milestone: "full_recovery",
|
|
430
672
|
previousScore,
|
|
431
673
|
newScore: record.score,
|
|
432
674
|
details: `Fully recovered to peak score of ${record.peakScore}`,
|
|
@@ -438,7 +680,7 @@ export class TrustEngine extends EventEmitter {
|
|
|
438
680
|
newScore: record.score,
|
|
439
681
|
recoveryAmount,
|
|
440
682
|
accelerated,
|
|
441
|
-
},
|
|
683
|
+
}, "Trust recovery applied");
|
|
442
684
|
}
|
|
443
685
|
/**
|
|
444
686
|
* Get trust score for an entity (with automatic decay)
|
|
@@ -448,6 +690,10 @@ export class TrustEngine extends EventEmitter {
|
|
|
448
690
|
if (record) {
|
|
449
691
|
// Clean up old failures
|
|
450
692
|
this.cleanupFailures(record);
|
|
693
|
+
if (this._readinessMode === "checkpoint_schedule") {
|
|
694
|
+
await this.applyScheduledReadinessAdjustment(record);
|
|
695
|
+
return record;
|
|
696
|
+
}
|
|
451
697
|
// Apply decay if stale
|
|
452
698
|
const staleness = Date.now() - new Date(record.lastCalculatedAt).getTime();
|
|
453
699
|
if (staleness > this._decayIntervalMs) {
|
|
@@ -469,7 +715,7 @@ export class TrustEngine extends EventEmitter {
|
|
|
469
715
|
// Emit decay event
|
|
470
716
|
if (previousScore !== record.score) {
|
|
471
717
|
this.emitTrustEvent({
|
|
472
|
-
type:
|
|
718
|
+
type: "trust:decay_applied",
|
|
473
719
|
entityId,
|
|
474
720
|
timestamp: new Date().toISOString(),
|
|
475
721
|
previousScore,
|
|
@@ -481,14 +727,14 @@ export class TrustEngine extends EventEmitter {
|
|
|
481
727
|
// Emit tier change if applicable
|
|
482
728
|
if (previousLevel !== record.level) {
|
|
483
729
|
this.emitTrustEvent({
|
|
484
|
-
type:
|
|
730
|
+
type: "trust:tier_changed",
|
|
485
731
|
entityId,
|
|
486
732
|
timestamp: new Date().toISOString(),
|
|
487
733
|
previousLevel,
|
|
488
734
|
newLevel: record.level,
|
|
489
735
|
previousLevelName: TRUST_LEVEL_NAMES[previousLevel],
|
|
490
736
|
newLevelName: TRUST_LEVEL_NAMES[record.level],
|
|
491
|
-
direction: record.level < previousLevel ?
|
|
737
|
+
direction: record.level < previousLevel ? "demoted" : "promoted",
|
|
492
738
|
});
|
|
493
739
|
}
|
|
494
740
|
// Auto-persist after decay
|
|
@@ -519,7 +765,7 @@ export class TrustEngine extends EventEmitter {
|
|
|
519
765
|
record.consecutiveSuccesses = 0;
|
|
520
766
|
const acceleratedDecayActive = this.hasAcceleratedDecay(record);
|
|
521
767
|
this.emitTrustEvent({
|
|
522
|
-
type:
|
|
768
|
+
type: "trust:failure_detected",
|
|
523
769
|
entityId: signal.entityId,
|
|
524
770
|
timestamp: new Date().toISOString(),
|
|
525
771
|
signal,
|
|
@@ -532,7 +778,7 @@ export class TrustEngine extends EventEmitter {
|
|
|
532
778
|
signalValue: signal.value,
|
|
533
779
|
failureCount: record.recentFailures.length,
|
|
534
780
|
acceleratedDecayActive,
|
|
535
|
-
},
|
|
781
|
+
}, "Failure signal detected");
|
|
536
782
|
}
|
|
537
783
|
// Detect success signals and apply recovery
|
|
538
784
|
if (signal.value >= this._successThreshold) {
|
|
@@ -550,7 +796,7 @@ export class TrustEngine extends EventEmitter {
|
|
|
550
796
|
signalValue: signal.value,
|
|
551
797
|
consecutiveSuccesses: record.consecutiveSuccesses,
|
|
552
798
|
recoveryAmount,
|
|
553
|
-
},
|
|
799
|
+
}, "Success signal detected");
|
|
554
800
|
}
|
|
555
801
|
// Add signal
|
|
556
802
|
record.signals.push(signal);
|
|
@@ -565,6 +811,12 @@ export class TrustEngine extends EventEmitter {
|
|
|
565
811
|
record.level = calculation.level;
|
|
566
812
|
record.components = calculation.components;
|
|
567
813
|
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;
|
|
568
820
|
// Record history if significant change
|
|
569
821
|
if (Math.abs(calculation.score - previousScore) >= 10) {
|
|
570
822
|
record.history.push({
|
|
@@ -580,7 +832,7 @@ export class TrustEngine extends EventEmitter {
|
|
|
580
832
|
}
|
|
581
833
|
// Emit signal recorded event
|
|
582
834
|
this.emitTrustEvent({
|
|
583
|
-
type:
|
|
835
|
+
type: "trust:signal_recorded",
|
|
584
836
|
entityId: signal.entityId,
|
|
585
837
|
timestamp: new Date().toISOString(),
|
|
586
838
|
signal,
|
|
@@ -590,7 +842,7 @@ export class TrustEngine extends EventEmitter {
|
|
|
590
842
|
// Emit score changed event if significant
|
|
591
843
|
if (Math.abs(calculation.score - previousScore) >= 5) {
|
|
592
844
|
this.emitTrustEvent({
|
|
593
|
-
type:
|
|
845
|
+
type: "trust:score_changed",
|
|
594
846
|
entityId: signal.entityId,
|
|
595
847
|
timestamp: new Date().toISOString(),
|
|
596
848
|
previousScore,
|
|
@@ -602,14 +854,14 @@ export class TrustEngine extends EventEmitter {
|
|
|
602
854
|
// Emit tier changed event if applicable
|
|
603
855
|
if (previousLevel !== calculation.level && !isNewEntity) {
|
|
604
856
|
this.emitTrustEvent({
|
|
605
|
-
type:
|
|
857
|
+
type: "trust:tier_changed",
|
|
606
858
|
entityId: signal.entityId,
|
|
607
859
|
timestamp: new Date().toISOString(),
|
|
608
860
|
previousLevel,
|
|
609
861
|
newLevel: calculation.level,
|
|
610
862
|
previousLevelName: TRUST_LEVEL_NAMES[previousLevel],
|
|
611
863
|
newLevelName: TRUST_LEVEL_NAMES[calculation.level],
|
|
612
|
-
direction: calculation.level > previousLevel ?
|
|
864
|
+
direction: calculation.level > previousLevel ? "promoted" : "demoted",
|
|
613
865
|
});
|
|
614
866
|
}
|
|
615
867
|
// Auto-persist if enabled
|
|
@@ -618,7 +870,7 @@ export class TrustEngine extends EventEmitter {
|
|
|
618
870
|
entityId: signal.entityId,
|
|
619
871
|
signalType: signal.type,
|
|
620
872
|
newScore: calculation.score,
|
|
621
|
-
},
|
|
873
|
+
}, "Signal recorded");
|
|
622
874
|
}
|
|
623
875
|
/**
|
|
624
876
|
* Initialize trust for a new entity
|
|
@@ -641,7 +893,7 @@ export class TrustEngine extends EventEmitter {
|
|
|
641
893
|
{
|
|
642
894
|
score,
|
|
643
895
|
level: initialLevel,
|
|
644
|
-
reason:
|
|
896
|
+
reason: "Initial registration",
|
|
645
897
|
timestamp: new Date().toISOString(),
|
|
646
898
|
},
|
|
647
899
|
],
|
|
@@ -649,19 +901,25 @@ export class TrustEngine extends EventEmitter {
|
|
|
649
901
|
recentSuccesses: [],
|
|
650
902
|
peakScore: score,
|
|
651
903
|
consecutiveSuccesses: 0,
|
|
904
|
+
readinessCheckpointIndex: 0,
|
|
905
|
+
deferredReadinessMultiplier: 1,
|
|
906
|
+
readinessBaselineScore: score,
|
|
907
|
+
freshnessCheckpointIndex: 0,
|
|
908
|
+
deferredFreshnessMultiplier: 1,
|
|
909
|
+
freshnessBaselineScore: score,
|
|
652
910
|
};
|
|
653
911
|
this.records.set(entityId, record);
|
|
654
912
|
// Auto-persist if enabled
|
|
655
913
|
await this.autoPersistRecord(record);
|
|
656
914
|
// Emit initialized event
|
|
657
915
|
this.emitTrustEvent({
|
|
658
|
-
type:
|
|
916
|
+
type: "trust:initialized",
|
|
659
917
|
entityId,
|
|
660
918
|
timestamp: new Date().toISOString(),
|
|
661
919
|
initialScore: score,
|
|
662
920
|
initialLevel,
|
|
663
921
|
});
|
|
664
|
-
logger.info({ entityId, initialLevel },
|
|
922
|
+
logger.info({ entityId, initialLevel }, "Entity trust initialized");
|
|
665
923
|
return record;
|
|
666
924
|
}
|
|
667
925
|
/**
|
|
@@ -690,23 +948,23 @@ export class TrustEngine extends EventEmitter {
|
|
|
690
948
|
/**
|
|
691
949
|
* Calculate component scores from signals
|
|
692
950
|
*/
|
|
693
|
-
calculateComponents(signals) {
|
|
951
|
+
calculateComponents(signals, currentLevel) {
|
|
694
952
|
// Group signals by type
|
|
695
|
-
const behavioral = signals.filter((s) => s.type.startsWith(
|
|
696
|
-
const compliance = signals.filter((s) => s.type.startsWith(
|
|
697
|
-
const identity = signals.filter((s) => s.type.startsWith(
|
|
698
|
-
const context = signals.filter((s) => s.type.startsWith(
|
|
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."));
|
|
699
957
|
return {
|
|
700
|
-
behavioral: this.averageSignalValue(behavioral, 0.5),
|
|
701
|
-
compliance: this.averageSignalValue(compliance, 0.5),
|
|
702
|
-
identity: this.averageSignalValue(identity, 0.5),
|
|
703
|
-
context: this.averageSignalValue(context, 0.5),
|
|
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),
|
|
704
962
|
};
|
|
705
963
|
}
|
|
706
964
|
/**
|
|
707
965
|
* Calculate average signal value with default
|
|
708
966
|
*/
|
|
709
|
-
averageSignalValue(signals, defaultValue) {
|
|
967
|
+
averageSignalValue(signals, defaultValue, currentLevel) {
|
|
710
968
|
if (signals.length === 0)
|
|
711
969
|
return defaultValue;
|
|
712
970
|
// Weight recent signals more heavily
|
|
@@ -716,27 +974,66 @@ export class TrustEngine extends EventEmitter {
|
|
|
716
974
|
for (const signal of signals) {
|
|
717
975
|
const age = now - new Date(signal.timestamp).getTime();
|
|
718
976
|
const weight = Math.exp(-age / (7 * 24 * 60 * 60 * 1000)); // 7-day half-life
|
|
719
|
-
|
|
977
|
+
const adjustedValue = this.adjustSignalValueForTier(signal, defaultValue, currentLevel);
|
|
978
|
+
weightedSum += adjustedValue * weight;
|
|
720
979
|
totalWeight += weight;
|
|
721
980
|
}
|
|
722
981
|
return totalWeight > 0 ? weightedSum / totalWeight : defaultValue;
|
|
723
982
|
}
|
|
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
|
+
}
|
|
724
1021
|
/**
|
|
725
1022
|
* Get significant factors affecting the score
|
|
726
1023
|
*/
|
|
727
1024
|
getSignificantFactors(components) {
|
|
728
1025
|
const factors = [];
|
|
729
1026
|
if (components.behavioral < 0.3) {
|
|
730
|
-
factors.push(
|
|
1027
|
+
factors.push("Low behavioral trust");
|
|
731
1028
|
}
|
|
732
1029
|
if (components.compliance < 0.3) {
|
|
733
|
-
factors.push(
|
|
1030
|
+
factors.push("Low compliance score");
|
|
734
1031
|
}
|
|
735
1032
|
if (components.identity < 0.3) {
|
|
736
|
-
factors.push(
|
|
1033
|
+
factors.push("Weak identity verification");
|
|
737
1034
|
}
|
|
738
1035
|
if (components.context < 0.3) {
|
|
739
|
-
factors.push(
|
|
1036
|
+
factors.push("Unusual context signals");
|
|
740
1037
|
}
|
|
741
1038
|
return factors;
|
|
742
1039
|
}
|
|
@@ -762,7 +1059,67 @@ export class TrustEngine extends EventEmitter {
|
|
|
762
1059
|
recentSuccesses: [],
|
|
763
1060
|
peakScore: initialScore,
|
|
764
1061
|
consecutiveSuccesses: 0,
|
|
1062
|
+
readinessCheckpointIndex: 0,
|
|
1063
|
+
deferredReadinessMultiplier: 1,
|
|
1064
|
+
readinessBaselineScore: initialScore,
|
|
1065
|
+
freshnessCheckpointIndex: 0,
|
|
1066
|
+
deferredFreshnessMultiplier: 1,
|
|
1067
|
+
freshnessBaselineScore: initialScore,
|
|
1068
|
+
};
|
|
1069
|
+
}
|
|
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),
|
|
765
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);
|
|
766
1123
|
}
|
|
767
1124
|
/**
|
|
768
1125
|
* Check if accelerated decay is currently active for an entity
|