@trentapps/manager-protocol 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +639 -0
  3. package/dist/analyzers/ArchitectureDetector.d.ts +44 -0
  4. package/dist/analyzers/ArchitectureDetector.d.ts.map +1 -0
  5. package/dist/analyzers/ArchitectureDetector.js +218 -0
  6. package/dist/analyzers/ArchitectureDetector.js.map +1 -0
  7. package/dist/analyzers/CSSAnalyzer.d.ts +284 -0
  8. package/dist/analyzers/CSSAnalyzer.d.ts.map +1 -0
  9. package/dist/analyzers/CSSAnalyzer.js +1180 -0
  10. package/dist/analyzers/CSSAnalyzer.js.map +1 -0
  11. package/dist/analyzers/index.d.ts +5 -0
  12. package/dist/analyzers/index.d.ts.map +1 -0
  13. package/dist/analyzers/index.js +5 -0
  14. package/dist/analyzers/index.js.map +1 -0
  15. package/dist/cli.d.ts +8 -0
  16. package/dist/cli.d.ts.map +1 -0
  17. package/dist/cli.js +174 -0
  18. package/dist/cli.js.map +1 -0
  19. package/dist/design-system/index.d.ts +6 -0
  20. package/dist/design-system/index.d.ts.map +1 -0
  21. package/dist/design-system/index.js +6 -0
  22. package/dist/design-system/index.js.map +1 -0
  23. package/dist/design-system/tokens.d.ts +106 -0
  24. package/dist/design-system/tokens.d.ts.map +1 -0
  25. package/dist/design-system/tokens.js +554 -0
  26. package/dist/design-system/tokens.js.map +1 -0
  27. package/dist/engine/AuditLogger.d.ts +506 -0
  28. package/dist/engine/AuditLogger.d.ts.map +1 -0
  29. package/dist/engine/AuditLogger.js +1491 -0
  30. package/dist/engine/AuditLogger.js.map +1 -0
  31. package/dist/engine/GitHubApprovalManager.d.ts +123 -0
  32. package/dist/engine/GitHubApprovalManager.d.ts.map +1 -0
  33. package/dist/engine/GitHubApprovalManager.js +347 -0
  34. package/dist/engine/GitHubApprovalManager.js.map +1 -0
  35. package/dist/engine/GitHubClient.d.ts +183 -0
  36. package/dist/engine/GitHubClient.d.ts.map +1 -0
  37. package/dist/engine/GitHubClient.js +411 -0
  38. package/dist/engine/GitHubClient.js.map +1 -0
  39. package/dist/engine/RateLimiter.d.ts +81 -0
  40. package/dist/engine/RateLimiter.d.ts.map +1 -0
  41. package/dist/engine/RateLimiter.js +215 -0
  42. package/dist/engine/RateLimiter.js.map +1 -0
  43. package/dist/engine/RuleDependencyAnalyzer.d.ts +73 -0
  44. package/dist/engine/RuleDependencyAnalyzer.d.ts.map +1 -0
  45. package/dist/engine/RuleDependencyAnalyzer.js +475 -0
  46. package/dist/engine/RuleDependencyAnalyzer.js.map +1 -0
  47. package/dist/engine/RulesEngine.d.ts +176 -0
  48. package/dist/engine/RulesEngine.d.ts.map +1 -0
  49. package/dist/engine/RulesEngine.js +705 -0
  50. package/dist/engine/RulesEngine.js.map +1 -0
  51. package/dist/engine/TaskManager.d.ts +174 -0
  52. package/dist/engine/TaskManager.d.ts.map +1 -0
  53. package/dist/engine/TaskManager.js +663 -0
  54. package/dist/engine/TaskManager.js.map +1 -0
  55. package/dist/engine/index.d.ts +11 -0
  56. package/dist/engine/index.d.ts.map +1 -0
  57. package/dist/engine/index.js +13 -0
  58. package/dist/engine/index.js.map +1 -0
  59. package/dist/index.d.ts +21 -0
  60. package/dist/index.d.ts.map +1 -0
  61. package/dist/index.js +29 -0
  62. package/dist/index.js.map +1 -0
  63. package/dist/rules/architecture.d.ts +9 -0
  64. package/dist/rules/architecture.d.ts.map +1 -0
  65. package/dist/rules/architecture.js +322 -0
  66. package/dist/rules/architecture.js.map +1 -0
  67. package/dist/rules/azure.d.ts +7 -0
  68. package/dist/rules/azure.d.ts.map +1 -0
  69. package/dist/rules/azure.js +136 -0
  70. package/dist/rules/azure.js.map +1 -0
  71. package/dist/rules/compliance.d.ts +9 -0
  72. package/dist/rules/compliance.d.ts.map +1 -0
  73. package/dist/rules/compliance.js +286 -0
  74. package/dist/rules/compliance.js.map +1 -0
  75. package/dist/rules/condition-optimizer.d.ts +151 -0
  76. package/dist/rules/condition-optimizer.d.ts.map +1 -0
  77. package/dist/rules/condition-optimizer.js +479 -0
  78. package/dist/rules/condition-optimizer.js.map +1 -0
  79. package/dist/rules/css.d.ts +10 -0
  80. package/dist/rules/css.d.ts.map +1 -0
  81. package/dist/rules/css.js +1777 -0
  82. package/dist/rules/css.js.map +1 -0
  83. package/dist/rules/field-standards.d.ts +1172 -0
  84. package/dist/rules/field-standards.d.ts.map +1 -0
  85. package/dist/rules/field-standards.js +908 -0
  86. package/dist/rules/field-standards.js.map +1 -0
  87. package/dist/rules/flask.d.ts +7 -0
  88. package/dist/rules/flask.d.ts.map +1 -0
  89. package/dist/rules/flask.js +142 -0
  90. package/dist/rules/flask.js.map +1 -0
  91. package/dist/rules/index.d.ts +827 -0
  92. package/dist/rules/index.d.ts.map +1 -0
  93. package/dist/rules/index.js +556 -0
  94. package/dist/rules/index.js.map +1 -0
  95. package/dist/rules/ml-ai.d.ts +7 -0
  96. package/dist/rules/ml-ai.d.ts.map +1 -0
  97. package/dist/rules/ml-ai.js +148 -0
  98. package/dist/rules/ml-ai.js.map +1 -0
  99. package/dist/rules/operational.d.ts +9 -0
  100. package/dist/rules/operational.d.ts.map +1 -0
  101. package/dist/rules/operational.js +318 -0
  102. package/dist/rules/operational.js.map +1 -0
  103. package/dist/rules/patterns.d.ts +568 -0
  104. package/dist/rules/patterns.d.ts.map +1 -0
  105. package/dist/rules/patterns.js +1359 -0
  106. package/dist/rules/patterns.js.map +1 -0
  107. package/dist/rules/security.d.ts +9 -0
  108. package/dist/rules/security.d.ts.map +1 -0
  109. package/dist/rules/security.js +848 -0
  110. package/dist/rules/security.js.map +1 -0
  111. package/dist/rules/shared-patterns.d.ts +268 -0
  112. package/dist/rules/shared-patterns.d.ts.map +1 -0
  113. package/dist/rules/shared-patterns.js +556 -0
  114. package/dist/rules/shared-patterns.js.map +1 -0
  115. package/dist/rules/storage.d.ts +13 -0
  116. package/dist/rules/storage.d.ts.map +1 -0
  117. package/dist/rules/storage.js +672 -0
  118. package/dist/rules/storage.js.map +1 -0
  119. package/dist/rules/stripe.d.ts +7 -0
  120. package/dist/rules/stripe.d.ts.map +1 -0
  121. package/dist/rules/stripe.js +133 -0
  122. package/dist/rules/stripe.js.map +1 -0
  123. package/dist/rules/testing.d.ts +7 -0
  124. package/dist/rules/testing.d.ts.map +1 -0
  125. package/dist/rules/testing.js +135 -0
  126. package/dist/rules/testing.js.map +1 -0
  127. package/dist/rules/ux.d.ts +9 -0
  128. package/dist/rules/ux.d.ts.map +1 -0
  129. package/dist/rules/ux.js +280 -0
  130. package/dist/rules/ux.js.map +1 -0
  131. package/dist/rules/websocket.d.ts +7 -0
  132. package/dist/rules/websocket.d.ts.map +1 -0
  133. package/dist/rules/websocket.js +128 -0
  134. package/dist/rules/websocket.js.map +1 -0
  135. package/dist/server.d.ts +43 -0
  136. package/dist/server.d.ts.map +1 -0
  137. package/dist/server.js +1967 -0
  138. package/dist/server.js.map +1 -0
  139. package/dist/supervisor/AgentSupervisor.d.ts +195 -0
  140. package/dist/supervisor/AgentSupervisor.d.ts.map +1 -0
  141. package/dist/supervisor/AgentSupervisor.js +569 -0
  142. package/dist/supervisor/AgentSupervisor.js.map +1 -0
  143. package/dist/supervisor/ManagedServerRegistry.d.ts +185 -0
  144. package/dist/supervisor/ManagedServerRegistry.d.ts.map +1 -0
  145. package/dist/supervisor/ManagedServerRegistry.js +729 -0
  146. package/dist/supervisor/ManagedServerRegistry.js.map +1 -0
  147. package/dist/supervisor/ProjectTracker.d.ts +210 -0
  148. package/dist/supervisor/ProjectTracker.d.ts.map +1 -0
  149. package/dist/supervisor/ProjectTracker.js +709 -0
  150. package/dist/supervisor/ProjectTracker.js.map +1 -0
  151. package/dist/supervisor/index.d.ts +6 -0
  152. package/dist/supervisor/index.d.ts.map +1 -0
  153. package/dist/supervisor/index.js +6 -0
  154. package/dist/supervisor/index.js.map +1 -0
  155. package/dist/testing/index.d.ts +11 -0
  156. package/dist/testing/index.d.ts.map +1 -0
  157. package/dist/testing/index.js +12 -0
  158. package/dist/testing/index.js.map +1 -0
  159. package/dist/testing/rule-tester.d.ts +217 -0
  160. package/dist/testing/rule-tester.d.ts.map +1 -0
  161. package/dist/testing/rule-tester.examples.d.ts +57 -0
  162. package/dist/testing/rule-tester.examples.d.ts.map +1 -0
  163. package/dist/testing/rule-tester.examples.js +375 -0
  164. package/dist/testing/rule-tester.examples.js.map +1 -0
  165. package/dist/testing/rule-tester.js +381 -0
  166. package/dist/testing/rule-tester.js.map +1 -0
  167. package/dist/testing/rule-validator.d.ts +141 -0
  168. package/dist/testing/rule-validator.d.ts.map +1 -0
  169. package/dist/testing/rule-validator.js +640 -0
  170. package/dist/testing/rule-validator.js.map +1 -0
  171. package/dist/types/index.d.ts +1282 -0
  172. package/dist/types/index.d.ts.map +1 -0
  173. package/dist/types/index.js +386 -0
  174. package/dist/types/index.js.map +1 -0
  175. package/dist/utils/errors.d.ts +86 -0
  176. package/dist/utils/errors.d.ts.map +1 -0
  177. package/dist/utils/errors.js +171 -0
  178. package/dist/utils/errors.js.map +1 -0
  179. package/dist/utils/index.d.ts +7 -0
  180. package/dist/utils/index.d.ts.map +1 -0
  181. package/dist/utils/index.js +7 -0
  182. package/dist/utils/index.js.map +1 -0
  183. package/dist/utils/rate-limiting.d.ts +268 -0
  184. package/dist/utils/rate-limiting.d.ts.map +1 -0
  185. package/dist/utils/rate-limiting.js +403 -0
  186. package/dist/utils/rate-limiting.js.map +1 -0
  187. package/dist/utils/shared.d.ts +306 -0
  188. package/dist/utils/shared.d.ts.map +1 -0
  189. package/dist/utils/shared.js +464 -0
  190. package/dist/utils/shared.js.map +1 -0
  191. package/dist/utils/shell.d.ts +22 -0
  192. package/dist/utils/shell.d.ts.map +1 -0
  193. package/dist/utils/shell.js +29 -0
  194. package/dist/utils/shell.js.map +1 -0
  195. package/package.json +67 -0
@@ -0,0 +1,705 @@
1
+ /**
2
+ * Enterprise Agent Supervisor - Rules Engine Core
3
+ *
4
+ * The heart of the governance system that evaluates actions against business rules.
5
+ */
6
+ import { v4 as uuidv4 } from 'uuid';
7
+ import { RuleDependencyAnalyzer } from './RuleDependencyAnalyzer.js';
8
+ /**
9
+ * Task #34: Condition evaluation cost weights for optimization
10
+ * Lower numbers = faster operations, should be evaluated first
11
+ */
12
+ const CONDITION_COST_WEIGHTS = {
13
+ exists: 1,
14
+ not_exists: 1,
15
+ equals: 2,
16
+ not_equals: 2,
17
+ in: 3,
18
+ not_in: 3,
19
+ greater_than: 4,
20
+ less_than: 4,
21
+ contains: 5,
22
+ not_contains: 5,
23
+ matches_regex: 10, // Expensive - evaluate last
24
+ custom: 8 // Custom evaluators can be expensive
25
+ };
26
+ export class RulesEngine {
27
+ rules = new Map();
28
+ customEvaluators = new Map();
29
+ // Task #53: Cache for compiled evaluators - keyed by evaluator name/string representation
30
+ evaluatorCache = new Map();
31
+ // Task #34: Cache for compiled RegExp objects - keyed by pattern string
32
+ regexCache = new Map();
33
+ // Task #34: Cache for optimized 'in' operator Sets - keyed by JSON.stringify of value array
34
+ inOperatorSetCache = new Map();
35
+ // Task #37: Dependency analyzer for rule ordering
36
+ dependencyAnalyzer;
37
+ // Task #37: Cache for dependency-sorted execution order
38
+ executionOrderCache = null;
39
+ // Task #37: Whether to respect rule dependencies in evaluation order
40
+ respectDependencies = true;
41
+ constructor(options) {
42
+ this.dependencyAnalyzer = new RuleDependencyAnalyzer();
43
+ this.respectDependencies = options?.respectDependencies ?? true;
44
+ this.registerDefaultEvaluators();
45
+ }
46
+ /**
47
+ * Register a business rule
48
+ * Task #53: Clear evaluator cache when rules change to ensure fresh compilation
49
+ */
50
+ registerRule(rule) {
51
+ this.rules.set(rule.id, rule);
52
+ this.clearEvaluatorCache();
53
+ }
54
+ /**
55
+ * Register multiple business rules
56
+ */
57
+ registerRules(rules) {
58
+ for (const rule of rules) {
59
+ this.registerRule(rule);
60
+ }
61
+ }
62
+ /**
63
+ * Unregister a rule by ID
64
+ * Task #53: Clear evaluator cache when rules change
65
+ */
66
+ unregisterRule(ruleId) {
67
+ const result = this.rules.delete(ruleId);
68
+ if (result) {
69
+ this.clearEvaluatorCache();
70
+ }
71
+ return result;
72
+ }
73
+ /**
74
+ * Task #53: Clear the evaluator cache
75
+ * Task #34: Also clear regex and 'in' operator caches
76
+ * Task #37: Also clear execution order cache
77
+ * Called when rules are modified to ensure fresh compilation
78
+ */
79
+ clearEvaluatorCache() {
80
+ this.evaluatorCache.clear();
81
+ this.regexCache.clear();
82
+ this.inOperatorSetCache.clear();
83
+ this.executionOrderCache = null;
84
+ }
85
+ /**
86
+ * Get all registered rules
87
+ */
88
+ getRules() {
89
+ return Array.from(this.rules.values());
90
+ }
91
+ /**
92
+ * Get enabled rules sorted by priority (higher priority first)
93
+ * Task #39: In strict mode, filters out deprecated rules
94
+ * Task #37: When respectDependencies is true, returns rules in dependency-aware order
95
+ */
96
+ getActiveRules(strictMode = false) {
97
+ const enabledRules = this.getRules()
98
+ .filter(rule => rule.enabled)
99
+ .filter(rule => !strictMode || !rule.deprecated);
100
+ // Task #37: Return rules in dependency order if enabled
101
+ if (this.respectDependencies) {
102
+ return this.getDependencySortedRules(enabledRules);
103
+ }
104
+ // Default: sort by priority (higher priority first)
105
+ return enabledRules.sort((a, b) => b.priority - a.priority);
106
+ }
107
+ /**
108
+ * Task #37: Get rules sorted by dependency order (dependencies first, then by priority)
109
+ */
110
+ getDependencySortedRules(rules) {
111
+ // Use cached execution order if available
112
+ if (this.executionOrderCache) {
113
+ const ruleMap = new Map();
114
+ for (const rule of rules) {
115
+ ruleMap.set(rule.id, rule);
116
+ }
117
+ const orderedRules = [];
118
+ for (const ruleId of this.executionOrderCache) {
119
+ const rule = ruleMap.get(ruleId);
120
+ if (rule) {
121
+ orderedRules.push(rule);
122
+ }
123
+ }
124
+ // Add any rules not in cache (shouldn't happen, but safety)
125
+ const cachedIds = new Set(this.executionOrderCache);
126
+ for (const rule of rules) {
127
+ if (!cachedIds.has(rule.id)) {
128
+ orderedRules.push(rule);
129
+ }
130
+ }
131
+ return orderedRules;
132
+ }
133
+ // Calculate and cache execution order
134
+ const sortedRules = this.dependencyAnalyzer.getSortedRulesForExecution(rules);
135
+ this.executionOrderCache = sortedRules.map(r => r.id);
136
+ return sortedRules;
137
+ }
138
+ /**
139
+ * Task #39: Get all deprecated rules
140
+ */
141
+ getDeprecatedRules() {
142
+ return this.getRules().filter(rule => rule.deprecated === true);
143
+ }
144
+ /**
145
+ * Task #39: Analyze rules and suggest migrations for deprecated rules
146
+ * Returns migration suggestions with replacement rule information
147
+ */
148
+ migrateRules(rules) {
149
+ const migrations = [];
150
+ for (const rule of rules) {
151
+ if (rule.deprecated) {
152
+ const replacementRule = rule.replacedBy ? this.rules.get(rule.replacedBy) : undefined;
153
+ let suggestion = `Rule '${rule.name}' (${rule.id}) is deprecated.`;
154
+ if (rule.deprecatedMessage) {
155
+ suggestion += ` ${rule.deprecatedMessage}`;
156
+ }
157
+ if (rule.replacedBy) {
158
+ if (replacementRule) {
159
+ suggestion += ` Migrate to '${replacementRule.name}' (${rule.replacedBy}).`;
160
+ }
161
+ else {
162
+ suggestion += ` Recommended replacement: ${rule.replacedBy} (not found in current rules).`;
163
+ }
164
+ }
165
+ migrations.push({
166
+ ruleId: rule.id,
167
+ ruleName: rule.name,
168
+ deprecatedMessage: rule.deprecatedMessage,
169
+ replacedBy: rule.replacedBy,
170
+ replacementRule,
171
+ suggestion
172
+ });
173
+ }
174
+ }
175
+ return migrations;
176
+ }
177
+ /**
178
+ * Task #39: Check if a rule is compatible with the current supervisor version
179
+ */
180
+ isRuleCompatible(rule, currentVersion) {
181
+ if (!rule.minVersion) {
182
+ return true; // No version requirement
183
+ }
184
+ return this.compareVersions(currentVersion, rule.minVersion) >= 0;
185
+ }
186
+ /**
187
+ * Task #37: Validate all rule dependencies
188
+ * Returns validation result with errors and warnings
189
+ */
190
+ validateDependencies() {
191
+ return this.dependencyAnalyzer.validateDependencies(this.getRules());
192
+ }
193
+ /**
194
+ * Task #37: Get the dependency analyzer for advanced analysis
195
+ */
196
+ getDependencyAnalyzer() {
197
+ return this.dependencyAnalyzer;
198
+ }
199
+ /**
200
+ * Task #37: Enable or disable dependency-aware rule ordering
201
+ */
202
+ setRespectDependencies(enabled) {
203
+ if (this.respectDependencies !== enabled) {
204
+ this.respectDependencies = enabled;
205
+ this.executionOrderCache = null; // Clear cache when toggling
206
+ }
207
+ }
208
+ /**
209
+ * Task #37: Check if dependency-aware ordering is enabled
210
+ */
211
+ isRespectingDependencies() {
212
+ return this.respectDependencies;
213
+ }
214
+ /**
215
+ * Task #37: Get rules that depend on a specific rule
216
+ */
217
+ getRuleDependents(ruleId) {
218
+ return this.dependencyAnalyzer.getDependents(ruleId, this.getRules());
219
+ }
220
+ /**
221
+ * Task #37: Get rules that a specific rule depends on
222
+ */
223
+ getRuleDependencies(ruleId) {
224
+ return this.dependencyAnalyzer.getDependencies(ruleId, this.getRules());
225
+ }
226
+ /**
227
+ * Task #37: Get all rules that would be affected if a rule is disabled
228
+ */
229
+ getRulesAffectedByDisabling(ruleId) {
230
+ return this.dependencyAnalyzer.getAffectedByDisabling(ruleId, this.getRules());
231
+ }
232
+ /**
233
+ * Task #39: Compare semantic versions
234
+ * Returns: 1 if a > b, -1 if a < b, 0 if equal
235
+ */
236
+ compareVersions(a, b) {
237
+ const partsA = a.split('.').map(Number);
238
+ const partsB = b.split('.').map(Number);
239
+ for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) {
240
+ const numA = partsA[i] || 0;
241
+ const numB = partsB[i] || 0;
242
+ if (numA > numB)
243
+ return 1;
244
+ if (numA < numB)
245
+ return -1;
246
+ }
247
+ return 0;
248
+ }
249
+ /**
250
+ * Task #39: Warn about deprecated rule usage
251
+ */
252
+ warnDeprecatedRule(rule) {
253
+ let message = `[DEPRECATED] Rule '${rule.name}' (${rule.id}) is deprecated.`;
254
+ if (rule.deprecatedMessage) {
255
+ message += ` ${rule.deprecatedMessage}`;
256
+ }
257
+ if (rule.replacedBy) {
258
+ message += ` Consider migrating to: ${rule.replacedBy}`;
259
+ }
260
+ console.warn(message);
261
+ }
262
+ /**
263
+ * Register a custom condition evaluator
264
+ * Task #53: Clear evaluator cache when custom evaluators change
265
+ */
266
+ registerCustomEvaluator(name, evaluator) {
267
+ this.customEvaluators.set(name, evaluator);
268
+ this.clearEvaluatorCache();
269
+ }
270
+ /**
271
+ * Evaluate an agent action against all active rules
272
+ */
273
+ evaluateAction(action, context) {
274
+ const actionId = action.id || uuidv4();
275
+ const violations = [];
276
+ const warnings = [];
277
+ const appliedRules = [];
278
+ let totalRiskWeight = 0;
279
+ let requiresHumanApproval = false;
280
+ let approvalReason;
281
+ let isDenied = false;
282
+ let isRateLimited = false;
283
+ // Build evaluation context
284
+ const evalContext = this.buildEvaluationContext(action, context);
285
+ // Get active rules and evaluate
286
+ const activeRules = this.getActiveRules();
287
+ // Task #57: Track total priority weight for normalization
288
+ let totalPriorityWeight = 0;
289
+ let weightedRiskSum = 0;
290
+ for (const rule of activeRules) {
291
+ const ruleMatches = this.evaluateRuleConditions(rule, evalContext);
292
+ if (ruleMatches) {
293
+ appliedRules.push(rule.id);
294
+ // Task #39: Warn when deprecated rules are triggered
295
+ if (rule.deprecated) {
296
+ this.warnDeprecatedRule(rule);
297
+ }
298
+ // Task #57: Weight risk by rule priority (0-1000 scale)
299
+ // Higher priority rules have more impact on final score
300
+ const priorityMultiplier = (rule.priority + 100) / 100; // Ensure minimum weight of 1
301
+ const weightedRisk = rule.riskWeight * priorityMultiplier;
302
+ weightedRiskSum += weightedRisk;
303
+ totalPriorityWeight += priorityMultiplier;
304
+ totalRiskWeight += rule.riskWeight;
305
+ // Process rule actions
306
+ for (const ruleAction of rule.actions) {
307
+ switch (ruleAction.type) {
308
+ case 'deny':
309
+ isDenied = true;
310
+ violations.push({
311
+ ruleId: rule.id,
312
+ ruleName: rule.name,
313
+ severity: this.getRuleSeverity(rule),
314
+ message: ruleAction.message || `Action denied by rule: ${rule.name}`,
315
+ recommendation: this.getRecommendation(rule, ruleAction)
316
+ });
317
+ break;
318
+ case 'require_approval':
319
+ requiresHumanApproval = true;
320
+ approvalReason = ruleAction.message || `Requires approval due to rule: ${rule.name}`;
321
+ break;
322
+ case 'warn':
323
+ warnings.push(ruleAction.message || `Warning from rule: ${rule.name}`);
324
+ break;
325
+ case 'rate_limit':
326
+ isRateLimited = true;
327
+ break;
328
+ case 'escalate':
329
+ requiresHumanApproval = true;
330
+ approvalReason = `Escalated: ${ruleAction.message || rule.name}`;
331
+ break;
332
+ default:
333
+ // Allow, log, transform, notify - don't affect approval status
334
+ break;
335
+ }
336
+ }
337
+ }
338
+ }
339
+ // Task #57: Calculate risk score using priority-weighted average (0-100)
340
+ // If no rules matched, use raw total; otherwise use weighted calculation
341
+ const riskScore = totalPriorityWeight > 0
342
+ ? Math.min(100, weightedRiskSum / totalPriorityWeight)
343
+ : Math.min(100, totalRiskWeight);
344
+ const riskLevel = this.calculateRiskLevel(riskScore);
345
+ // Determine final status
346
+ let status;
347
+ if (isDenied) {
348
+ status = 'denied';
349
+ }
350
+ else if (isRateLimited) {
351
+ status = 'rate_limited';
352
+ }
353
+ else if (requiresHumanApproval) {
354
+ status = 'pending_approval';
355
+ }
356
+ else if (warnings.length > 0) {
357
+ status = 'requires_review';
358
+ }
359
+ else {
360
+ status = 'approved';
361
+ }
362
+ return {
363
+ actionId,
364
+ status,
365
+ riskScore,
366
+ riskLevel,
367
+ allowed: !isDenied && !isRateLimited,
368
+ violations,
369
+ warnings,
370
+ appliedRules,
371
+ requiresHumanApproval,
372
+ approvalReason,
373
+ rateLimitInfo: isRateLimited ? { limited: true } : undefined,
374
+ evaluatedAt: new Date().toISOString(),
375
+ metadata: {
376
+ action: action.name,
377
+ category: action.category,
378
+ rulesEvaluated: activeRules.length
379
+ }
380
+ };
381
+ }
382
+ /**
383
+ * Apply business rules to a context and return recommendations
384
+ */
385
+ applyBusinessRules(context) {
386
+ const contextId = uuidv4();
387
+ const rulesApplied = [];
388
+ const recommendations = [];
389
+ const constraints = [];
390
+ let totalRiskScore = 0;
391
+ const evalContext = context.customAttributes || {};
392
+ const activeRules = this.getActiveRules();
393
+ for (const rule of activeRules) {
394
+ const matched = this.evaluateRuleConditions(rule, {
395
+ ...evalContext,
396
+ environment: context.environment,
397
+ userRole: context.userRole,
398
+ dataClassification: context.dataClassification,
399
+ department: context.department
400
+ });
401
+ rulesApplied.push({
402
+ ruleId: rule.id,
403
+ ruleName: rule.name,
404
+ matched,
405
+ actions: matched ? rule.actions : []
406
+ });
407
+ if (matched) {
408
+ totalRiskScore += rule.riskWeight;
409
+ // Generate recommendations and constraints
410
+ for (const action of rule.actions) {
411
+ if (action.type === 'deny') {
412
+ constraints.push({
413
+ type: 'prohibition',
414
+ description: action.message || `Prohibited by ${rule.name}`,
415
+ enforced: true
416
+ });
417
+ }
418
+ else if (action.type === 'require_approval') {
419
+ constraints.push({
420
+ type: 'approval_required',
421
+ description: action.message || `Requires approval per ${rule.name}`,
422
+ enforced: true
423
+ });
424
+ }
425
+ else if (action.type === 'warn') {
426
+ recommendations.push(action.message || `Consider: ${rule.name}`);
427
+ }
428
+ }
429
+ }
430
+ }
431
+ return {
432
+ contextId,
433
+ rulesApplied,
434
+ aggregateRiskScore: Math.min(100, totalRiskScore),
435
+ recommendations,
436
+ constraints,
437
+ processedAt: new Date().toISOString()
438
+ };
439
+ }
440
+ /**
441
+ * Build evaluation context from action and business context
442
+ */
443
+ buildEvaluationContext(action, context) {
444
+ return {
445
+ // Action fields
446
+ actionName: action.name,
447
+ actionCategory: action.category,
448
+ actionDescription: action.description,
449
+ ...action.parameters,
450
+ ...action.metadata,
451
+ // Context fields
452
+ environment: context?.environment,
453
+ agentId: context?.agentId || action.agentId,
454
+ agentType: context?.agentType,
455
+ userId: context?.userId,
456
+ userRole: context?.userRole,
457
+ sessionId: context?.sessionId || action.sessionId,
458
+ organizationId: context?.organizationId,
459
+ department: context?.department,
460
+ costCenter: context?.costCenter,
461
+ dataClassification: context?.dataClassification,
462
+ complianceFrameworks: context?.complianceFrameworks,
463
+ ...context?.customAttributes
464
+ };
465
+ }
466
+ /**
467
+ * Task #34: Sort conditions by evaluation cost for optimal short-circuit evaluation
468
+ * Cheaper operations (exists, equals) go first, expensive ones (regex, custom) last
469
+ */
470
+ sortConditionsByEvaluationCost(conditions) {
471
+ return [...conditions].sort((a, b) => {
472
+ const costA = CONDITION_COST_WEIGHTS[a.operator] ?? 5;
473
+ const costB = CONDITION_COST_WEIGHTS[b.operator] ?? 5;
474
+ return costA - costB;
475
+ });
476
+ }
477
+ /**
478
+ * Evaluate if all/any conditions of a rule match
479
+ * Task #34: Optimized with condition sorting for short-circuit evaluation
480
+ */
481
+ evaluateRuleConditions(rule, context) {
482
+ if (rule.conditions.length === 0) {
483
+ return true; // No conditions means always match
484
+ }
485
+ // Task #34: Sort conditions by evaluation cost for optimal short-circuit behavior
486
+ const sortedConditions = this.sortConditionsByEvaluationCost(rule.conditions);
487
+ if (rule.conditionLogic === 'any') {
488
+ // For 'any' logic, evaluate until first true (cheaper conditions first)
489
+ for (const condition of sortedConditions) {
490
+ if (this.evaluateCondition(condition, context)) {
491
+ return true; // Short-circuit: found a match
492
+ }
493
+ }
494
+ return false;
495
+ }
496
+ // For 'all' logic (default), evaluate until first false (cheaper conditions first)
497
+ for (const condition of sortedConditions) {
498
+ if (!this.evaluateCondition(condition, context)) {
499
+ return false; // Short-circuit: found a non-match
500
+ }
501
+ }
502
+ return true;
503
+ }
504
+ /**
505
+ * Task #34: Get cached RegExp or compile and cache it
506
+ */
507
+ getCachedRegex(pattern) {
508
+ let regex = this.regexCache.get(pattern);
509
+ if (!regex) {
510
+ try {
511
+ regex = new RegExp(pattern);
512
+ this.regexCache.set(pattern, regex);
513
+ }
514
+ catch {
515
+ return null;
516
+ }
517
+ }
518
+ return regex;
519
+ }
520
+ /**
521
+ * Task #34: Get cached Set for 'in' operator or create and cache it
522
+ * Using Set provides O(1) lookup instead of O(n) array.includes()
523
+ */
524
+ getCachedInOperatorSet(values) {
525
+ const cacheKey = JSON.stringify(values);
526
+ let valueSet = this.inOperatorSetCache.get(cacheKey);
527
+ if (!valueSet) {
528
+ valueSet = new Set(values);
529
+ this.inOperatorSetCache.set(cacheKey, valueSet);
530
+ }
531
+ return valueSet;
532
+ }
533
+ /**
534
+ * Evaluate a single condition
535
+ * Task #34: Optimized with regex caching and Set-based 'in' operator
536
+ */
537
+ evaluateCondition(condition, context) {
538
+ const fieldValue = this.getNestedValue(context, condition.field);
539
+ switch (condition.operator) {
540
+ case 'equals':
541
+ return fieldValue === condition.value;
542
+ case 'not_equals':
543
+ return fieldValue !== condition.value;
544
+ case 'contains':
545
+ if (typeof fieldValue === 'string' && typeof condition.value === 'string') {
546
+ return fieldValue.includes(condition.value);
547
+ }
548
+ if (Array.isArray(fieldValue)) {
549
+ return fieldValue.includes(condition.value);
550
+ }
551
+ return false;
552
+ case 'not_contains':
553
+ if (typeof fieldValue === 'string' && typeof condition.value === 'string') {
554
+ return !fieldValue.includes(condition.value);
555
+ }
556
+ if (Array.isArray(fieldValue)) {
557
+ return !fieldValue.includes(condition.value);
558
+ }
559
+ return true;
560
+ case 'greater_than':
561
+ return typeof fieldValue === 'number' && typeof condition.value === 'number'
562
+ && fieldValue > condition.value;
563
+ case 'less_than':
564
+ return typeof fieldValue === 'number' && typeof condition.value === 'number'
565
+ && fieldValue < condition.value;
566
+ case 'in':
567
+ // Task #34: Use Set-based lookup for O(1) performance on large value arrays
568
+ if (Array.isArray(condition.value)) {
569
+ const valueSet = this.getCachedInOperatorSet(condition.value);
570
+ return valueSet.has(fieldValue);
571
+ }
572
+ return false;
573
+ case 'not_in':
574
+ // Task #34: Use Set-based lookup for O(1) performance on large value arrays
575
+ if (Array.isArray(condition.value)) {
576
+ const valueSet = this.getCachedInOperatorSet(condition.value);
577
+ return !valueSet.has(fieldValue);
578
+ }
579
+ return true;
580
+ case 'matches_regex':
581
+ // Task #34: Use cached regex compilation to avoid repeated new RegExp() calls
582
+ if (typeof fieldValue === 'string' && typeof condition.value === 'string') {
583
+ const regex = this.getCachedRegex(condition.value);
584
+ return regex ? regex.test(fieldValue) : false;
585
+ }
586
+ return false;
587
+ case 'exists':
588
+ return fieldValue !== undefined && fieldValue !== null;
589
+ case 'not_exists':
590
+ return fieldValue === undefined || fieldValue === null;
591
+ case 'custom':
592
+ if (condition.customEvaluator) {
593
+ // Task #53: Check cache first for compiled evaluator
594
+ const cacheKey = condition.customEvaluator;
595
+ let evaluator = this.evaluatorCache.get(cacheKey);
596
+ if (!evaluator) {
597
+ evaluator = this.customEvaluators.get(condition.customEvaluator);
598
+ if (evaluator) {
599
+ // Cache the evaluator for future use
600
+ this.evaluatorCache.set(cacheKey, evaluator);
601
+ }
602
+ }
603
+ if (evaluator) {
604
+ // Task #56: Add try-catch around custom evaluator execution
605
+ try {
606
+ return evaluator(context, condition);
607
+ }
608
+ catch (error) {
609
+ // Log error and treat failed evaluator as non-matching condition
610
+ console.error(`Custom evaluator '${condition.customEvaluator}' threw an exception:`, error instanceof Error ? error.message : String(error));
611
+ return false;
612
+ }
613
+ }
614
+ }
615
+ return false;
616
+ default:
617
+ return false;
618
+ }
619
+ }
620
+ /**
621
+ * Get nested value from object using dot notation
622
+ */
623
+ getNestedValue(obj, path) {
624
+ return path.split('.').reduce((current, key) => {
625
+ if (current && typeof current === 'object' && key in current) {
626
+ return current[key];
627
+ }
628
+ return undefined;
629
+ }, obj);
630
+ }
631
+ /**
632
+ * Calculate risk level from score
633
+ */
634
+ calculateRiskLevel(score) {
635
+ if (score >= 80)
636
+ return 'critical';
637
+ if (score >= 60)
638
+ return 'high';
639
+ if (score >= 40)
640
+ return 'medium';
641
+ if (score >= 20)
642
+ return 'low';
643
+ return 'minimal';
644
+ }
645
+ /**
646
+ * Get severity level from a rule
647
+ */
648
+ getRuleSeverity(rule) {
649
+ if (rule.riskWeight >= 40)
650
+ return 'critical';
651
+ if (rule.riskWeight >= 30)
652
+ return 'high';
653
+ if (rule.riskWeight >= 20)
654
+ return 'medium';
655
+ if (rule.riskWeight >= 10)
656
+ return 'low';
657
+ return 'minimal';
658
+ }
659
+ /**
660
+ * Generate recommendation based on rule and action
661
+ */
662
+ getRecommendation(rule, action) {
663
+ const baseRecs = {
664
+ compliance: 'Review compliance requirements and ensure proper authorization',
665
+ security: 'Consult with security team before proceeding',
666
+ operational: 'Follow standard operating procedures',
667
+ financial: 'Obtain financial approval before proceeding',
668
+ ux: 'Review UX guidelines and user impact',
669
+ architecture: 'Consult architecture review board',
670
+ data_governance: 'Ensure data handling complies with policies',
671
+ rate_limit: 'Reduce request frequency or request limit increase',
672
+ custom: 'Review custom policy requirements'
673
+ };
674
+ return action.params?.recommendation
675
+ || baseRecs[rule.type]
676
+ || 'Review policy and obtain necessary approvals';
677
+ }
678
+ /**
679
+ * Register default custom evaluators
680
+ */
681
+ registerDefaultEvaluators() {
682
+ // Time-based evaluator
683
+ this.registerCustomEvaluator('businessHours', (_context) => {
684
+ const hour = new Date().getHours();
685
+ return hour >= 9 && hour < 17;
686
+ });
687
+ // Weekday evaluator
688
+ this.registerCustomEvaluator('weekday', (_context) => {
689
+ const day = new Date().getDay();
690
+ return day >= 1 && day <= 5;
691
+ });
692
+ // Production environment evaluator
693
+ this.registerCustomEvaluator('isProduction', (context) => {
694
+ return context.environment === 'production';
695
+ });
696
+ // High privilege evaluator
697
+ this.registerCustomEvaluator('isHighPrivilege', (context) => {
698
+ const highPrivRoles = ['admin', 'superuser', 'root', 'system'];
699
+ return highPrivRoles.includes(context.userRole);
700
+ });
701
+ }
702
+ }
703
+ // Export singleton instance
704
+ export const rulesEngine = new RulesEngine();
705
+ //# sourceMappingURL=RulesEngine.js.map