@sovr/engine 3.4.0 → 3.6.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.
package/dist/index.mjs CHANGED
@@ -1999,6 +1999,2264 @@ function createEvolutionChannel(config) {
1999
1999
  return new EvolutionChannelEngine(config);
2000
2000
  }
2001
2001
 
2002
+ // src/twoPhaseRouter.ts
2003
+ var DEFAULT_HARD_RULES = [
2004
+ {
2005
+ id: "HR-001",
2006
+ name: "high_risk_requires_powerful",
2007
+ condition: (req, candidate) => (req.riskLevel === "critical" || req.riskLevel === "high") && candidate.tier === "cheap",
2008
+ reasonCode: "RISK_TIER_MISMATCH",
2009
+ reasonTemplate: "High/critical risk tasks cannot use cheap tier models",
2010
+ enabled: true
2011
+ },
2012
+ {
2013
+ id: "HR-002",
2014
+ name: "critical_requires_powerful_only",
2015
+ condition: (req, candidate) => req.riskLevel === "critical" && candidate.tier !== "powerful",
2016
+ reasonCode: "RISK_TIER_MISMATCH",
2017
+ reasonTemplate: "Critical risk tasks require powerful tier models only",
2018
+ enabled: true
2019
+ },
2020
+ {
2021
+ id: "HR-003",
2022
+ name: "budget_hard_cap",
2023
+ condition: (req, candidate) => {
2024
+ if (!req.maxCostUsd) return false;
2025
+ const estimatedCost = req.estimatedTokens / 1e3 * candidate.costPer1kTokens;
2026
+ return estimatedCost > req.maxCostUsd;
2027
+ },
2028
+ reasonCode: "COST_EXCEEDS_BUDGET",
2029
+ reasonTemplate: "Estimated cost exceeds hard budget cap",
2030
+ enabled: true
2031
+ }
2032
+ ];
2033
+ var DEFAULT_SCORING_WEIGHTS = {
2034
+ cost: 0.25,
2035
+ latency: 0.2,
2036
+ capability: 0.25,
2037
+ weight: 0.15,
2038
+ tierMatch: 0.15
2039
+ };
2040
+ var TwoPhaseRouter = class {
2041
+ config;
2042
+ constructor(config) {
2043
+ this.config = {
2044
+ ...config,
2045
+ hardRules: config.hardRules.length > 0 ? config.hardRules : DEFAULT_HARD_RULES,
2046
+ scoringWeights: config.scoringWeights ?? DEFAULT_SCORING_WEIGHTS
2047
+ };
2048
+ }
2049
+ /**
2050
+ * Phase 1: Eligibility — 硬性规则过滤
2051
+ */
2052
+ evaluateEligibility(request) {
2053
+ return this.config.models.map((candidate) => {
2054
+ if (!candidate.enabled) {
2055
+ return {
2056
+ candidateId: candidate.id,
2057
+ eligible: false,
2058
+ reasonCode: "DISABLED",
2059
+ reasonDetail: `Model ${candidate.name} is disabled`
2060
+ };
2061
+ }
2062
+ const missingCaps = request.requiredCapabilities.filter(
2063
+ (cap) => !candidate.capabilities.includes(cap)
2064
+ );
2065
+ if (missingCaps.length > 0) {
2066
+ return {
2067
+ candidateId: candidate.id,
2068
+ eligible: false,
2069
+ reasonCode: "CAPABILITY_MISSING",
2070
+ reasonDetail: `Missing capabilities: ${missingCaps.join(", ")}`
2071
+ };
2072
+ }
2073
+ if (request.estimatedTokens > candidate.maxContextTokens) {
2074
+ return {
2075
+ candidateId: candidate.id,
2076
+ eligible: false,
2077
+ reasonCode: "CONTEXT_TOO_LARGE",
2078
+ reasonDetail: `Estimated ${request.estimatedTokens} tokens exceeds max ${candidate.maxContextTokens}`
2079
+ };
2080
+ }
2081
+ if (request.maxLatencyMs && candidate.avgLatencyMs > request.maxLatencyMs) {
2082
+ return {
2083
+ candidateId: candidate.id,
2084
+ eligible: false,
2085
+ reasonCode: "LATENCY_EXCEEDS_LIMIT",
2086
+ reasonDetail: `Avg latency ${candidate.avgLatencyMs}ms exceeds limit ${request.maxLatencyMs}ms`
2087
+ };
2088
+ }
2089
+ for (const rule of this.config.hardRules) {
2090
+ if (!rule.enabled) continue;
2091
+ if (rule.condition(request, candidate)) {
2092
+ return {
2093
+ candidateId: candidate.id,
2094
+ eligible: false,
2095
+ reasonCode: rule.reasonCode,
2096
+ reasonDetail: rule.reasonTemplate
2097
+ };
2098
+ }
2099
+ }
2100
+ return {
2101
+ candidateId: candidate.id,
2102
+ eligible: true,
2103
+ reasonCode: "ELIGIBLE",
2104
+ reasonDetail: "Passed all eligibility checks"
2105
+ };
2106
+ });
2107
+ }
2108
+ /**
2109
+ * Phase 2: Scoring — 软性指标加权评分
2110
+ */
2111
+ evaluateScoring(request, eligibleIds) {
2112
+ const eligible = this.config.models.filter((m) => eligibleIds.has(m.id));
2113
+ if (eligible.length === 0) return [];
2114
+ const w = this.config.scoringWeights;
2115
+ const maxCost = Math.max(...eligible.map((m) => m.costPer1kTokens), 1e-3);
2116
+ const maxLatency = Math.max(...eligible.map((m) => m.avgLatencyMs), 1);
2117
+ return eligible.map((candidate) => {
2118
+ const costScore = 100 * (1 - candidate.costPer1kTokens / maxCost);
2119
+ const latencyScore = 100 * (1 - candidate.avgLatencyMs / maxLatency);
2120
+ const matchedCaps = request.requiredCapabilities.filter(
2121
+ (cap) => candidate.capabilities.includes(cap)
2122
+ ).length;
2123
+ const capabilityScore = request.requiredCapabilities.length > 0 ? 100 * (matchedCaps / request.requiredCapabilities.length) : 50;
2124
+ const weightScore = candidate.weight;
2125
+ let tierMatchScore = 50;
2126
+ if (request.preferredTier) {
2127
+ if (candidate.tier === request.preferredTier) tierMatchScore = 100;
2128
+ else {
2129
+ const tierOrder = ["cheap", "balanced", "powerful"];
2130
+ const diff = Math.abs(
2131
+ tierOrder.indexOf(candidate.tier) - tierOrder.indexOf(request.preferredTier)
2132
+ );
2133
+ tierMatchScore = Math.max(0, 100 - diff * 40);
2134
+ }
2135
+ }
2136
+ const score = costScore * w.cost + latencyScore * w.latency + capabilityScore * w.capability + weightScore * w.weight + tierMatchScore * w.tierMatch;
2137
+ return {
2138
+ candidateId: candidate.id,
2139
+ score: Math.round(score * 100) / 100,
2140
+ breakdown: {
2141
+ costScore: Math.round(costScore * 100) / 100,
2142
+ latencyScore: Math.round(latencyScore * 100) / 100,
2143
+ capabilityScore: Math.round(capabilityScore * 100) / 100,
2144
+ weightScore: Math.round(weightScore * 100) / 100,
2145
+ tierMatchScore: Math.round(tierMatchScore * 100) / 100
2146
+ }
2147
+ };
2148
+ });
2149
+ }
2150
+ /**
2151
+ * Main routing decision
2152
+ */
2153
+ async route(request) {
2154
+ const startTime = Date.now();
2155
+ const phase1Results = this.evaluateEligibility(request);
2156
+ const eligibleIds = new Set(
2157
+ phase1Results.filter((r) => r.eligible).map((r) => r.candidateId)
2158
+ );
2159
+ const phase2Results = this.evaluateScoring(request, eligibleIds);
2160
+ phase2Results.sort((a, b) => b.score - a.score);
2161
+ let selectedModelId;
2162
+ let fallbackUsed = false;
2163
+ const reasonChain = [];
2164
+ if (phase2Results.length > 0) {
2165
+ selectedModelId = phase2Results[0].candidateId;
2166
+ reasonChain.push(
2167
+ `Phase 1: ${eligibleIds.size}/${this.config.models.length} candidates eligible`
2168
+ );
2169
+ reasonChain.push(
2170
+ `Phase 2: Top scorer = ${selectedModelId} (score: ${phase2Results[0].score})`
2171
+ );
2172
+ const rejected = phase1Results.filter((r) => !r.eligible);
2173
+ if (rejected.length > 0) {
2174
+ reasonChain.push(
2175
+ `Rejected: ${rejected.map((r) => `${r.candidateId}(${r.reasonCode})`).join(", ")}`
2176
+ );
2177
+ }
2178
+ } else if (this.config.fallbackModelId) {
2179
+ selectedModelId = this.config.fallbackModelId;
2180
+ fallbackUsed = true;
2181
+ reasonChain.push("Phase 1: No eligible candidates");
2182
+ reasonChain.push(`Fallback: Using ${selectedModelId}`);
2183
+ } else {
2184
+ const leastBad = this.config.models.find((m) => m.enabled);
2185
+ selectedModelId = leastBad?.id ?? this.config.models[0]?.id ?? "unknown";
2186
+ fallbackUsed = true;
2187
+ reasonChain.push("Phase 1: No eligible candidates, no fallback configured");
2188
+ reasonChain.push(`Emergency: Using ${selectedModelId}`);
2189
+ }
2190
+ const selectedModel = this.config.models.find((m) => m.id === selectedModelId);
2191
+ const decisionTimeMs = Date.now() - startTime;
2192
+ const decision = {
2193
+ selectedModelId,
2194
+ selectedModelName: selectedModel?.name ?? "unknown",
2195
+ tier: selectedModel?.tier ?? this.config.defaultTier,
2196
+ phase1Results,
2197
+ phase2Results,
2198
+ reasonChain,
2199
+ totalCandidates: this.config.models.length,
2200
+ eligibleCandidates: eligibleIds.size,
2201
+ decisionTimeMs,
2202
+ fallbackUsed
2203
+ };
2204
+ if (this.config.onDecision) {
2205
+ try {
2206
+ await this.config.onDecision(decision);
2207
+ } catch {
2208
+ }
2209
+ }
2210
+ if (this.config.onAudit) {
2211
+ try {
2212
+ await this.config.onAudit({
2213
+ type: "routing_decision",
2214
+ data: {
2215
+ selectedModelId,
2216
+ tier: decision.tier,
2217
+ eligibleCount: eligibleIds.size,
2218
+ totalCount: this.config.models.length,
2219
+ fallbackUsed,
2220
+ decisionTimeMs,
2221
+ taskType: request.taskType,
2222
+ riskLevel: request.riskLevel
2223
+ }
2224
+ });
2225
+ } catch {
2226
+ }
2227
+ }
2228
+ return decision;
2229
+ }
2230
+ /**
2231
+ * Get models by tier
2232
+ */
2233
+ getModelsByTier(tier) {
2234
+ return this.config.models.filter((m) => m.tier === tier && m.enabled);
2235
+ }
2236
+ /**
2237
+ * Update model status
2238
+ */
2239
+ updateModel(modelId, updates) {
2240
+ const idx = this.config.models.findIndex((m) => m.id === modelId);
2241
+ if (idx === -1) return false;
2242
+ this.config.models[idx] = { ...this.config.models[idx], ...updates };
2243
+ return true;
2244
+ }
2245
+ /**
2246
+ * Add a new hard rule
2247
+ */
2248
+ addHardRule(rule) {
2249
+ this.config.hardRules.push(rule);
2250
+ }
2251
+ /**
2252
+ * Get routing explanation for a specific request (dry run)
2253
+ */
2254
+ async explain(request) {
2255
+ const decision = await this.route(request);
2256
+ const lines = [
2257
+ `=== SOVR Routing Explanation ===`,
2258
+ `Task: ${request.taskType} | Risk: ${request.riskLevel}`,
2259
+ `Required Capabilities: ${request.requiredCapabilities.join(", ") || "none"}`,
2260
+ `Estimated Tokens: ${request.estimatedTokens}`,
2261
+ ``,
2262
+ `--- Phase 1: Eligibility ---`
2263
+ ];
2264
+ for (const r of decision.phase1Results) {
2265
+ const model = this.config.models.find((m) => m.id === r.candidateId);
2266
+ lines.push(
2267
+ ` ${r.eligible ? "\u2705" : "\u274C"} ${model?.name ?? r.candidateId} [${r.reasonCode}] ${r.reasonDetail}`
2268
+ );
2269
+ }
2270
+ lines.push("", `--- Phase 2: Scoring ---`);
2271
+ for (const r of decision.phase2Results) {
2272
+ const model = this.config.models.find((m) => m.id === r.candidateId);
2273
+ lines.push(
2274
+ ` ${model?.name ?? r.candidateId}: ${r.score} (cost=${r.breakdown.costScore} lat=${r.breakdown.latencyScore} cap=${r.breakdown.capabilityScore})`
2275
+ );
2276
+ }
2277
+ lines.push(
2278
+ "",
2279
+ `--- Decision ---`,
2280
+ `Selected: ${decision.selectedModelName} (${decision.tier})`,
2281
+ `Fallback: ${decision.fallbackUsed ? "YES" : "NO"}`,
2282
+ `Time: ${decision.decisionTimeMs}ms`
2283
+ );
2284
+ return { decision, explanation: lines.join("\n") };
2285
+ }
2286
+ };
2287
+
2288
+ // src/timeSeriesAggregator.ts
2289
+ var DEFAULT_TSA_CONFIG = {
2290
+ windowSizeMs: 6e4,
2291
+ // 1 minute
2292
+ windowType: "tumbling",
2293
+ maxWindows: 60,
2294
+ // Keep 1 hour of 1-min windows
2295
+ maxValuesPerWindow: 1e4
2296
+ };
2297
+ function percentile(sorted, p) {
2298
+ if (sorted.length === 0) return 0;
2299
+ if (sorted.length === 1) return sorted[0];
2300
+ const idx = p / 100 * (sorted.length - 1);
2301
+ const lower = Math.floor(idx);
2302
+ const upper = Math.ceil(idx);
2303
+ if (lower === upper) return sorted[lower];
2304
+ return sorted[lower] + (sorted[upper] - sorted[lower]) * (idx - lower);
2305
+ }
2306
+ var TimeSeriesAggregator = class {
2307
+ windows = /* @__PURE__ */ new Map();
2308
+ closedResults = [];
2309
+ config;
2310
+ totalDedups = 0;
2311
+ constructor(config) {
2312
+ this.config = { ...DEFAULT_TSA_CONFIG, ...config };
2313
+ }
2314
+ /**
2315
+ * Ingest a data point (idempotent — duplicates are rejected)
2316
+ */
2317
+ async ingest(point) {
2318
+ const windowId = this.getWindowId(point.timestamp);
2319
+ let window = this.windows.get(windowId);
2320
+ if (!window) {
2321
+ window = this.createWindow(windowId, point.timestamp);
2322
+ this.windows.set(windowId, window);
2323
+ this.evictOldWindows();
2324
+ }
2325
+ if (window.seenIds.has(point.id)) {
2326
+ this.totalDedups++;
2327
+ return { accepted: false, windowId, duplicate: true };
2328
+ }
2329
+ window.seenIds.add(point.id);
2330
+ window.count++;
2331
+ window.sum += point.value;
2332
+ window.min = Math.min(window.min, point.value);
2333
+ window.max = Math.max(window.max, point.value);
2334
+ if (window.values.length < this.config.maxValuesPerWindow) {
2335
+ window.values.push(point.value);
2336
+ }
2337
+ return { accepted: true, windowId, duplicate: false };
2338
+ }
2339
+ /**
2340
+ * Ingest multiple points (batch)
2341
+ */
2342
+ async ingestBatch(points) {
2343
+ let accepted = 0;
2344
+ let duplicates = 0;
2345
+ let errors = 0;
2346
+ for (const point of points) {
2347
+ try {
2348
+ const result = await this.ingest(point);
2349
+ if (result.accepted) accepted++;
2350
+ if (result.duplicate) duplicates++;
2351
+ } catch {
2352
+ errors++;
2353
+ }
2354
+ }
2355
+ return { accepted, duplicates, errors };
2356
+ }
2357
+ /**
2358
+ * Get aggregation result for a specific window
2359
+ */
2360
+ getWindowResult(windowId) {
2361
+ const window = this.windows.get(windowId);
2362
+ if (!window) {
2363
+ return this.closedResults.find((r) => r.windowId === windowId) ?? null;
2364
+ }
2365
+ return this.computeResult(window);
2366
+ }
2367
+ /**
2368
+ * Get current (latest) window result
2369
+ */
2370
+ getCurrentResult() {
2371
+ const now = Date.now();
2372
+ const windowId = this.getWindowId(now);
2373
+ return this.getWindowResult(windowId);
2374
+ }
2375
+ /**
2376
+ * Get results for a time range
2377
+ */
2378
+ getResultsInRange(startMs, endMs) {
2379
+ const results = [];
2380
+ for (const window of this.windows.values()) {
2381
+ if (window.startMs >= startMs && window.endMs <= endMs) {
2382
+ results.push(this.computeResult(window));
2383
+ }
2384
+ }
2385
+ for (const result of this.closedResults) {
2386
+ if (result.startMs >= startMs && result.endMs <= endMs) {
2387
+ if (!results.find((r) => r.windowId === result.windowId)) {
2388
+ results.push(result);
2389
+ }
2390
+ }
2391
+ }
2392
+ return results.sort((a, b) => a.startMs - b.startMs);
2393
+ }
2394
+ /**
2395
+ * Close current window and emit result
2396
+ */
2397
+ async closeWindow(windowId) {
2398
+ const window = this.windows.get(windowId);
2399
+ if (!window) return null;
2400
+ const result = this.computeResult(window);
2401
+ this.closedResults.push(result);
2402
+ this.windows.delete(windowId);
2403
+ if (this.closedResults.length > this.config.maxWindows * 2) {
2404
+ this.closedResults = this.closedResults.slice(-this.config.maxWindows);
2405
+ }
2406
+ if (this.config.onWindowClosed) {
2407
+ try {
2408
+ await this.config.onWindowClosed(result);
2409
+ } catch {
2410
+ }
2411
+ }
2412
+ if (this.config.onAudit) {
2413
+ try {
2414
+ await this.config.onAudit({
2415
+ type: "window_closed",
2416
+ data: {
2417
+ windowId,
2418
+ count: result.metrics.count,
2419
+ avg: result.metrics.avg,
2420
+ p95: result.metrics.p95,
2421
+ dedupCount: result.dedupCount
2422
+ }
2423
+ });
2424
+ } catch {
2425
+ }
2426
+ }
2427
+ return result;
2428
+ }
2429
+ /**
2430
+ * Get overall stats
2431
+ */
2432
+ getStats() {
2433
+ let totalDataPoints = 0;
2434
+ for (const w of this.windows.values()) totalDataPoints += w.count;
2435
+ for (const r of this.closedResults) totalDataPoints += r.metrics.count;
2436
+ return {
2437
+ activeWindows: this.windows.size,
2438
+ closedWindows: this.closedResults.length,
2439
+ totalDedups: this.totalDedups,
2440
+ totalDataPoints
2441
+ };
2442
+ }
2443
+ /**
2444
+ * Reset all windows
2445
+ */
2446
+ reset() {
2447
+ this.windows.clear();
2448
+ this.closedResults = [];
2449
+ this.totalDedups = 0;
2450
+ }
2451
+ // ─── Internal ────────────────────────────────────────────────────
2452
+ getWindowId(timestamp) {
2453
+ const windowStart = Math.floor(timestamp / this.config.windowSizeMs) * this.config.windowSizeMs;
2454
+ return `w_${windowStart}`;
2455
+ }
2456
+ createWindow(windowId, timestamp) {
2457
+ const windowStart = Math.floor(timestamp / this.config.windowSizeMs) * this.config.windowSizeMs;
2458
+ return {
2459
+ windowId,
2460
+ windowType: this.config.windowType,
2461
+ startMs: windowStart,
2462
+ endMs: windowStart + this.config.windowSizeMs,
2463
+ count: 0,
2464
+ sum: 0,
2465
+ min: Infinity,
2466
+ max: -Infinity,
2467
+ values: [],
2468
+ seenIds: /* @__PURE__ */ new Set()
2469
+ };
2470
+ }
2471
+ computeResult(window) {
2472
+ const sorted = [...window.values].sort((a, b) => a - b);
2473
+ const durationSec = (window.endMs - window.startMs) / 1e3;
2474
+ return {
2475
+ windowId: window.windowId,
2476
+ windowType: window.windowType,
2477
+ startMs: window.startMs,
2478
+ endMs: window.endMs,
2479
+ metrics: {
2480
+ count: window.count,
2481
+ sum: window.sum,
2482
+ avg: window.count > 0 ? window.sum / window.count : 0,
2483
+ min: window.min === Infinity ? 0 : window.min,
2484
+ max: window.max === -Infinity ? 0 : window.max,
2485
+ p50: percentile(sorted, 50),
2486
+ p95: percentile(sorted, 95),
2487
+ p99: percentile(sorted, 99),
2488
+ rate: durationSec > 0 ? window.count / durationSec : 0
2489
+ },
2490
+ dedupCount: window.seenIds.size - window.count > 0 ? 0 : this.totalDedups
2491
+ };
2492
+ }
2493
+ evictOldWindows() {
2494
+ if (this.windows.size <= this.config.maxWindows) return;
2495
+ const sorted = Array.from(this.windows.entries()).sort((a, b) => a[1].startMs - b[1].startMs);
2496
+ while (sorted.length > this.config.maxWindows) {
2497
+ const [id, window] = sorted.shift();
2498
+ this.closedResults.push(this.computeResult(window));
2499
+ this.windows.delete(id);
2500
+ }
2501
+ }
2502
+ };
2503
+
2504
+ // src/valuationModel.ts
2505
+ var ValuationModel = class {
2506
+ config;
2507
+ constructor(config) {
2508
+ this.config = config ?? {};
2509
+ }
2510
+ /**
2511
+ * Calculate TAM/SAM/SOM
2512
+ */
2513
+ calculateMarketSize(input) {
2514
+ return {
2515
+ tam: input.totalMarketUsd,
2516
+ sam: input.totalMarketUsd * input.serviceablePercent,
2517
+ som: input.totalMarketUsd * input.serviceablePercent * input.obtainablePercent,
2518
+ year: input.year,
2519
+ growthRate: input.growthRate,
2520
+ assumptions: input.assumptions
2521
+ };
2522
+ }
2523
+ /**
2524
+ * Calculate Unit Economics
2525
+ */
2526
+ calculateUnitEconomics(input) {
2527
+ const netChurn = input.churnRate - input.expansionRate;
2528
+ const effectiveChurn = Math.max(netChurn, 1e-3);
2529
+ const ltv = input.arpu * input.grossMargin / effectiveChurn;
2530
+ const paybackMonths = input.cac / (input.arpu * input.grossMargin);
2531
+ return {
2532
+ arpu: input.arpu,
2533
+ cac: input.cac,
2534
+ ltv: Math.round(ltv * 100) / 100,
2535
+ ltvCacRatio: Math.round(ltv / input.cac * 100) / 100,
2536
+ paybackMonths: Math.round(paybackMonths * 10) / 10,
2537
+ grossMargin: input.grossMargin,
2538
+ churnRate: input.churnRate,
2539
+ expansionRate: input.expansionRate
2540
+ };
2541
+ }
2542
+ /**
2543
+ * DCF Valuation
2544
+ */
2545
+ calculateDCF(input) {
2546
+ const fcfs = [];
2547
+ const discountedFcfs = [];
2548
+ for (let i = 0; i < input.projectedRevenues.length; i++) {
2549
+ const revenue = input.projectedRevenues[i];
2550
+ const operatingIncome = revenue * input.operatingMargin;
2551
+ const afterTax = operatingIncome * (1 - input.taxRate);
2552
+ const capex = revenue * input.capexRatio;
2553
+ const fcf = afterTax - capex;
2554
+ fcfs.push(fcf);
2555
+ const discountFactor = Math.pow(1 + input.discountRate, i + 1);
2556
+ discountedFcfs.push(fcf / discountFactor);
2557
+ }
2558
+ const presentValue = discountedFcfs.reduce((sum, v) => sum + v, 0);
2559
+ const lastFcf = fcfs[fcfs.length - 1] ?? 0;
2560
+ const terminalFcf = lastFcf * (1 + input.terminalGrowthRate);
2561
+ const terminalValue = terminalFcf / (input.discountRate - input.terminalGrowthRate);
2562
+ const discountedTerminal = terminalValue / Math.pow(1 + input.discountRate, input.projectedRevenues.length);
2563
+ const enterpriseValue = presentValue + discountedTerminal;
2564
+ const lastRevenue = input.projectedRevenues[input.projectedRevenues.length - 1] ?? 1;
2565
+ const impliedMultiple = enterpriseValue / lastRevenue;
2566
+ return {
2567
+ presentValue: Math.round(presentValue),
2568
+ terminalValue: Math.round(discountedTerminal),
2569
+ enterpriseValue: Math.round(enterpriseValue),
2570
+ freeCashFlows: fcfs.map((v) => Math.round(v)),
2571
+ discountedCashFlows: discountedFcfs.map((v) => Math.round(v)),
2572
+ impliedMultiple: Math.round(impliedMultiple * 10) / 10
2573
+ };
2574
+ }
2575
+ /**
2576
+ * Comparable Company Valuation
2577
+ */
2578
+ calculateComparable(input) {
2579
+ const revMultiples = input.comparables.map((c) => c.evRevenue);
2580
+ const ebitdaMultiples = input.comparables.map((c) => c.evEbitda);
2581
+ const userValues = input.comparables.map((c) => c.evUser);
2582
+ const medianRevMultiple = this.median(revMultiples);
2583
+ const medianEbitdaMultiple = this.median(ebitdaMultiples);
2584
+ const medianUserValue = this.median(userValues);
2585
+ const revenueVal = input.revenue * medianRevMultiple;
2586
+ const ebitdaVal = input.ebitda * medianEbitdaMultiple;
2587
+ const userVal = input.users * medianUserValue;
2588
+ const avgCompGrowth = input.comparables.reduce((s, c) => s + c.growthRate, 0) / input.comparables.length;
2589
+ const growthPremium = input.growthRate / (avgCompGrowth || 0.1);
2590
+ const growthAdjusted = revenueVal * Math.min(growthPremium, 3);
2591
+ return {
2592
+ revenueMultipleValuation: Math.round(revenueVal),
2593
+ ebitdaMultipleValuation: Math.round(ebitdaVal),
2594
+ perUserValuation: Math.round(userVal),
2595
+ averageValuation: Math.round((revenueVal + ebitdaVal + userVal) / 3),
2596
+ medianMultiple: medianRevMultiple,
2597
+ growthAdjustedValuation: Math.round(growthAdjusted)
2598
+ };
2599
+ }
2600
+ /**
2601
+ * Network Effect Valuation (Metcalfe's Law)
2602
+ */
2603
+ calculateNetworkEffect(input) {
2604
+ const currentValue = Math.pow(input.currentUsers, input.metcalfeExponent) * input.valuePerConnection * input.networkDensity;
2605
+ const projectedValues = input.projectedUsers.map(
2606
+ (users) => Math.pow(users, input.metcalfeExponent) * input.valuePerConnection * input.networkDensity
2607
+ );
2608
+ const metcalfeValue = Math.pow(input.currentUsers, 2) * input.valuePerConnection;
2609
+ const adjustedValue = metcalfeValue * input.networkDensity;
2610
+ return {
2611
+ currentNetworkValue: Math.round(currentValue),
2612
+ projectedValues: projectedValues.map((v) => Math.round(v)),
2613
+ metcalfeValue: Math.round(metcalfeValue),
2614
+ adjustedValue: Math.round(adjustedValue)
2615
+ };
2616
+ }
2617
+ /**
2618
+ * Sensitivity Analysis
2619
+ */
2620
+ runSensitivity(baseInput, variables) {
2621
+ const baseResult = this.calculateDCF(baseInput);
2622
+ const baseCase = baseResult.enterpriseValue;
2623
+ const scenarios = [];
2624
+ const tornado = [];
2625
+ for (const v of variables) {
2626
+ const lowInput = { ...baseInput, [v.field]: v.low };
2627
+ const lowResult = this.calculateDCF(lowInput);
2628
+ const highInput = { ...baseInput, [v.field]: v.high };
2629
+ const highResult = this.calculateDCF(highInput);
2630
+ scenarios.push({
2631
+ name: `${v.name} Low`,
2632
+ value: lowResult.enterpriseValue,
2633
+ delta: (lowResult.enterpriseValue - baseCase) / baseCase * 100,
2634
+ variables: { [v.name]: v.low }
2635
+ });
2636
+ scenarios.push({
2637
+ name: `${v.name} High`,
2638
+ value: highResult.enterpriseValue,
2639
+ delta: (highResult.enterpriseValue - baseCase) / baseCase * 100,
2640
+ variables: { [v.name]: v.high }
2641
+ });
2642
+ tornado.push({
2643
+ variable: v.name,
2644
+ lowValue: lowResult.enterpriseValue,
2645
+ highValue: highResult.enterpriseValue,
2646
+ range: Math.abs(highResult.enterpriseValue - lowResult.enterpriseValue)
2647
+ });
2648
+ }
2649
+ tornado.sort((a, b) => b.range - a.range);
2650
+ return { baseCase, scenarios, tornado };
2651
+ }
2652
+ // ─── Helpers ─────────────────────────────────────────────────────
2653
+ median(values) {
2654
+ if (values.length === 0) return 0;
2655
+ const sorted = [...values].sort((a, b) => a - b);
2656
+ const mid = Math.floor(sorted.length / 2);
2657
+ return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
2658
+ }
2659
+ };
2660
+
2661
+ // src/governanceEnhancer.ts
2662
+ var DEFAULT_GE_CONFIG = {
2663
+ circuitBreakerThreshold: 5,
2664
+ circuitBreakerTimeoutMs: 3e4,
2665
+ halfOpenSuccessThreshold: 3,
2666
+ degradationChain: [
2667
+ {
2668
+ id: "full",
2669
+ name: "Full Capability",
2670
+ order: 4,
2671
+ capabilities: ["all"],
2672
+ restrictions: []
2673
+ },
2674
+ {
2675
+ id: "reduced",
2676
+ name: "Reduced Capability",
2677
+ order: 3,
2678
+ capabilities: ["read", "compute", "cache"],
2679
+ restrictions: ["write", "external_api"]
2680
+ },
2681
+ {
2682
+ id: "minimal",
2683
+ name: "Minimal Capability",
2684
+ order: 2,
2685
+ capabilities: ["read", "cache"],
2686
+ restrictions: ["write", "compute", "external_api"]
2687
+ },
2688
+ {
2689
+ id: "readonly",
2690
+ name: "Read-Only Mode",
2691
+ order: 1,
2692
+ capabilities: ["read"],
2693
+ restrictions: ["write", "compute", "external_api", "cache"]
2694
+ }
2695
+ ],
2696
+ maxPolicyVersions: 10
2697
+ };
2698
+ function simpleHash2(input) {
2699
+ let hash = 0;
2700
+ for (let i = 0; i < input.length; i++) {
2701
+ hash = (hash << 5) - hash + input.charCodeAt(i);
2702
+ hash = hash & hash;
2703
+ }
2704
+ return Math.abs(hash).toString(16).padStart(8, "0");
2705
+ }
2706
+ var GovernanceEnhancer = class {
2707
+ currentPolicy = null;
2708
+ policyHistory = [];
2709
+ circuitBreakers = /* @__PURE__ */ new Map();
2710
+ currentDegradationLevel = "full";
2711
+ config;
2712
+ constructor(config) {
2713
+ this.config = { ...DEFAULT_GE_CONFIG, ...config };
2714
+ }
2715
+ // ─── Policy Hot-Reload ───────────────────────────────────────────
2716
+ /**
2717
+ * Load a new policy version (hot-reload)
2718
+ */
2719
+ async loadPolicy(version, rules) {
2720
+ const hash = simpleHash2(JSON.stringify(rules));
2721
+ const oldVersion = this.currentPolicy?.version;
2722
+ const newPolicy = {
2723
+ version,
2724
+ rules: rules.sort((a, b) => b.priority - a.priority),
2725
+ activatedAt: Date.now(),
2726
+ hash
2727
+ };
2728
+ if (this.currentPolicy) {
2729
+ this.policyHistory.push(this.currentPolicy);
2730
+ if (this.policyHistory.length > this.config.maxPolicyVersions) {
2731
+ this.policyHistory = this.policyHistory.slice(-this.config.maxPolicyVersions);
2732
+ }
2733
+ }
2734
+ this.currentPolicy = newPolicy;
2735
+ if (this.config.onPolicyChange && oldVersion) {
2736
+ try {
2737
+ await this.config.onPolicyChange(oldVersion, version);
2738
+ } catch {
2739
+ }
2740
+ }
2741
+ if (this.config.onAudit) {
2742
+ try {
2743
+ await this.config.onAudit({
2744
+ type: "policy_loaded",
2745
+ data: { version, ruleCount: rules.length, hash, previousVersion: oldVersion }
2746
+ });
2747
+ } catch {
2748
+ }
2749
+ }
2750
+ return { success: true, previousVersion: oldVersion, newVersion: version };
2751
+ }
2752
+ /**
2753
+ * Rollback to a previous policy version
2754
+ */
2755
+ async rollbackPolicy(version) {
2756
+ const target = this.policyHistory.find((p) => p.version === version);
2757
+ if (!target) return false;
2758
+ if (this.currentPolicy) {
2759
+ this.policyHistory.push(this.currentPolicy);
2760
+ }
2761
+ this.currentPolicy = { ...target, activatedAt: Date.now() };
2762
+ return true;
2763
+ }
2764
+ /**
2765
+ * Evaluate a governance decision
2766
+ */
2767
+ async evaluate(context) {
2768
+ if (!this.currentPolicy) {
2769
+ return {
2770
+ allowed: false,
2771
+ action: "deny",
2772
+ reason: "No policy loaded",
2773
+ policyVersion: "none"
2774
+ };
2775
+ }
2776
+ const cbState = this.getCircuitBreakerState(context.resource);
2777
+ if (cbState === "OPEN") {
2778
+ return {
2779
+ allowed: false,
2780
+ action: "deny",
2781
+ circuitState: "OPEN",
2782
+ reason: `Circuit breaker OPEN for ${context.resource}`,
2783
+ policyVersion: this.currentPolicy.version
2784
+ };
2785
+ }
2786
+ for (const rule of this.currentPolicy.rules) {
2787
+ if (!rule.enabled) continue;
2788
+ const matches = this.evaluateCondition(rule.condition, context);
2789
+ if (!matches) continue;
2790
+ const decision = {
2791
+ allowed: rule.action === "allow",
2792
+ action: rule.action,
2793
+ ruleId: rule.id,
2794
+ ruleName: rule.name,
2795
+ circuitState: cbState,
2796
+ policyVersion: this.currentPolicy.version,
2797
+ reason: `Rule ${rule.name} matched: ${rule.action}`
2798
+ };
2799
+ if (rule.action === "degrade" && rule.degradeTo) {
2800
+ decision.degradationLevel = rule.degradeTo;
2801
+ decision.allowed = true;
2802
+ this.currentDegradationLevel = rule.degradeTo;
2803
+ }
2804
+ if (this.config.onAudit) {
2805
+ try {
2806
+ await this.config.onAudit({
2807
+ type: "governance_decision",
2808
+ data: {
2809
+ action: context.action,
2810
+ resource: context.resource,
2811
+ decision: rule.action,
2812
+ ruleId: rule.id,
2813
+ policyVersion: this.currentPolicy.version
2814
+ }
2815
+ });
2816
+ } catch {
2817
+ }
2818
+ }
2819
+ return decision;
2820
+ }
2821
+ return {
2822
+ allowed: true,
2823
+ action: "allow",
2824
+ reason: "No matching rule, default allow",
2825
+ policyVersion: this.currentPolicy.version,
2826
+ circuitState: cbState
2827
+ };
2828
+ }
2829
+ // ─── Circuit Breaker ─────────────────────────────────────────────
2830
+ /**
2831
+ * Record a success for circuit breaker
2832
+ */
2833
+ recordSuccess(resource) {
2834
+ const cb = this.getOrCreateCircuitBreaker(resource);
2835
+ cb.successCount++;
2836
+ cb.lastSuccessAt = Date.now();
2837
+ if (cb.state === "HALF_OPEN") {
2838
+ if (cb.successCount >= this.config.halfOpenSuccessThreshold) {
2839
+ cb.state = "CLOSED";
2840
+ cb.failureCount = 0;
2841
+ cb.halfOpenAttempts = 0;
2842
+ }
2843
+ }
2844
+ }
2845
+ /**
2846
+ * Record a failure for circuit breaker
2847
+ */
2848
+ recordFailure(resource) {
2849
+ const cb = this.getOrCreateCircuitBreaker(resource);
2850
+ cb.failureCount++;
2851
+ cb.lastFailureAt = Date.now();
2852
+ if (cb.state === "CLOSED" && cb.failureCount >= this.config.circuitBreakerThreshold) {
2853
+ cb.state = "OPEN";
2854
+ cb.openedAt = Date.now();
2855
+ } else if (cb.state === "HALF_OPEN") {
2856
+ cb.state = "OPEN";
2857
+ cb.openedAt = Date.now();
2858
+ cb.halfOpenAttempts++;
2859
+ }
2860
+ }
2861
+ /**
2862
+ * Get circuit breaker state for a resource
2863
+ */
2864
+ getCircuitBreakerState(resource) {
2865
+ const cb = this.circuitBreakers.get(resource);
2866
+ if (!cb) return "CLOSED";
2867
+ if (cb.state === "OPEN") {
2868
+ const elapsed = Date.now() - cb.openedAt;
2869
+ if (elapsed >= this.config.circuitBreakerTimeoutMs) {
2870
+ cb.state = "HALF_OPEN";
2871
+ cb.successCount = 0;
2872
+ }
2873
+ }
2874
+ return cb.state;
2875
+ }
2876
+ // ─── Degradation Chain ───────────────────────────────────────────
2877
+ /**
2878
+ * Get current degradation level
2879
+ */
2880
+ getCurrentDegradationLevel() {
2881
+ return this.config.degradationChain.find((d) => d.id === this.currentDegradationLevel);
2882
+ }
2883
+ /**
2884
+ * Degrade to next level
2885
+ */
2886
+ degradeOneLevel() {
2887
+ const current = this.config.degradationChain.find((d) => d.id === this.currentDegradationLevel);
2888
+ if (!current) return null;
2889
+ const nextLevel = this.config.degradationChain.filter((d) => d.order < current.order).sort((a, b) => b.order - a.order)[0];
2890
+ if (nextLevel) {
2891
+ this.currentDegradationLevel = nextLevel.id;
2892
+ return nextLevel;
2893
+ }
2894
+ return null;
2895
+ }
2896
+ /**
2897
+ * Restore to full capability
2898
+ */
2899
+ restoreFullCapability() {
2900
+ this.currentDegradationLevel = "full";
2901
+ }
2902
+ /**
2903
+ * Check if a capability is available at current degradation level
2904
+ */
2905
+ isCapabilityAvailable(capability) {
2906
+ const level = this.getCurrentDegradationLevel();
2907
+ if (!level) return false;
2908
+ return level.capabilities.includes("all") || level.capabilities.includes(capability);
2909
+ }
2910
+ // ─── Status ──────────────────────────────────────────────────────
2911
+ /**
2912
+ * Get full governance status
2913
+ */
2914
+ getStatus() {
2915
+ return {
2916
+ policyVersion: this.currentPolicy?.version ?? null,
2917
+ ruleCount: this.currentPolicy?.rules.length ?? 0,
2918
+ degradationLevel: this.currentDegradationLevel,
2919
+ circuitBreakers: Array.from(this.circuitBreakers.values()),
2920
+ policyHistoryCount: this.policyHistory.length
2921
+ };
2922
+ }
2923
+ // ─── Internal ────────────────────────────────────────────────────
2924
+ evaluateCondition(condition, context) {
2925
+ if (condition === "*") return true;
2926
+ if (condition.startsWith("action:")) {
2927
+ return context.action === condition.substring(7);
2928
+ }
2929
+ if (condition.startsWith("resource:")) {
2930
+ return context.resource.includes(condition.substring(9));
2931
+ }
2932
+ if (condition.startsWith("agent:")) {
2933
+ return context.agentId === condition.substring(6);
2934
+ }
2935
+ return false;
2936
+ }
2937
+ getOrCreateCircuitBreaker(resource) {
2938
+ let cb = this.circuitBreakers.get(resource);
2939
+ if (!cb) {
2940
+ cb = {
2941
+ name: resource,
2942
+ state: "CLOSED",
2943
+ failureCount: 0,
2944
+ successCount: 0,
2945
+ lastFailureAt: 0,
2946
+ lastSuccessAt: 0,
2947
+ openedAt: 0,
2948
+ halfOpenAttempts: 0
2949
+ };
2950
+ this.circuitBreakers.set(resource, cb);
2951
+ }
2952
+ return cb;
2953
+ }
2954
+ };
2955
+
2956
+ // src/contextAccelerator.ts
2957
+ var DEFAULT_CA_CONFIG = {
2958
+ maxCacheEntries: 500,
2959
+ defaultTtlMs: 3e5,
2960
+ // 5 minutes
2961
+ tokenBudget: 8e3,
2962
+ compressionTarget: 0.5,
2963
+ priorityWeights: {
2964
+ critical: 100,
2965
+ high: 75,
2966
+ medium: 50,
2967
+ low: 25
2968
+ }
2969
+ };
2970
+ function estimateTokens(text) {
2971
+ return Math.ceil(text.length / 3.5);
2972
+ }
2973
+ function defaultCompressor(content, targetTokens) {
2974
+ const currentTokens = estimateTokens(content);
2975
+ if (currentTokens <= targetTokens) return content;
2976
+ const ratio = targetTokens / currentTokens;
2977
+ const targetLength = Math.floor(content.length * ratio);
2978
+ if (targetLength < 50) return content.substring(0, 50) + "...";
2979
+ return content.substring(0, targetLength) + "...[compressed]";
2980
+ }
2981
+ var ContextAccelerator = class {
2982
+ cache = /* @__PURE__ */ new Map();
2983
+ prefetchRules = /* @__PURE__ */ new Map();
2984
+ config;
2985
+ stats = {
2986
+ totalHits: 0,
2987
+ totalMisses: 0,
2988
+ totalEvictions: 0,
2989
+ totalCompressions: 0,
2990
+ totalAssemblies: 0
2991
+ };
2992
+ constructor(config) {
2993
+ this.config = { ...DEFAULT_CA_CONFIG, ...config };
2994
+ }
2995
+ /**
2996
+ * Put a fragment into cache
2997
+ */
2998
+ put(fragment) {
2999
+ this.evictExpired();
3000
+ if (this.cache.size >= this.config.maxCacheEntries) {
3001
+ this.evictLRU();
3002
+ }
3003
+ this.cache.set(fragment.id, {
3004
+ fragment,
3005
+ accessCount: 0,
3006
+ lastAccessedAt: Date.now(),
3007
+ createdAt: Date.now(),
3008
+ expiresAt: Date.now() + (fragment.ttlMs || this.config.defaultTtlMs)
3009
+ });
3010
+ }
3011
+ /**
3012
+ * Get a fragment from cache
3013
+ */
3014
+ get(id) {
3015
+ const entry = this.cache.get(id);
3016
+ if (!entry) {
3017
+ this.stats.totalMisses++;
3018
+ return null;
3019
+ }
3020
+ if (Date.now() > entry.expiresAt) {
3021
+ this.cache.delete(id);
3022
+ this.stats.totalMisses++;
3023
+ return null;
3024
+ }
3025
+ entry.accessCount++;
3026
+ entry.lastAccessedAt = Date.now();
3027
+ this.stats.totalHits++;
3028
+ return entry.fragment;
3029
+ }
3030
+ /**
3031
+ * Assemble context from fragments within token budget
3032
+ */
3033
+ async assemble(fragmentIds, additionalFragments, budgetOverride) {
3034
+ const startTime = Date.now();
3035
+ const budget = budgetOverride ?? this.config.tokenBudget;
3036
+ let cacheHits = 0;
3037
+ let cacheMisses = 0;
3038
+ const allFragments = [];
3039
+ for (const id of fragmentIds) {
3040
+ const fragment = this.get(id);
3041
+ if (fragment) {
3042
+ allFragments.push(fragment);
3043
+ cacheHits++;
3044
+ } else {
3045
+ cacheMisses++;
3046
+ }
3047
+ }
3048
+ if (additionalFragments) {
3049
+ allFragments.push(...additionalFragments);
3050
+ }
3051
+ const weights = this.config.priorityWeights;
3052
+ allFragments.sort((a, b) => {
3053
+ const wDiff = (weights[b.priority] ?? 0) - (weights[a.priority] ?? 0);
3054
+ if (wDiff !== 0) return wDiff;
3055
+ return b.timestamp - a.timestamp;
3056
+ });
3057
+ const selected = [];
3058
+ let totalTokens = 0;
3059
+ let droppedCount = 0;
3060
+ let compressedCount = 0;
3061
+ for (const fragment of allFragments) {
3062
+ if (totalTokens >= budget) {
3063
+ droppedCount++;
3064
+ continue;
3065
+ }
3066
+ const remaining = budget - totalTokens;
3067
+ if (fragment.tokenCount <= remaining) {
3068
+ selected.push(fragment);
3069
+ totalTokens += fragment.tokenCount;
3070
+ } else if (fragment.priority === "critical" || fragment.priority === "high") {
3071
+ const compressor = this.config.compressor ?? defaultCompressor;
3072
+ const compressed = compressor(fragment.content, remaining);
3073
+ const compressedTokens = estimateTokens(compressed);
3074
+ selected.push({
3075
+ ...fragment,
3076
+ content: compressed,
3077
+ tokenCount: compressedTokens,
3078
+ compressed: true
3079
+ });
3080
+ totalTokens += compressedTokens;
3081
+ compressedCount++;
3082
+ this.stats.totalCompressions++;
3083
+ } else {
3084
+ droppedCount++;
3085
+ }
3086
+ }
3087
+ this.stats.totalAssemblies++;
3088
+ const result = {
3089
+ fragments: selected,
3090
+ totalTokens,
3091
+ budgetUsed: budget > 0 ? totalTokens / budget : 0,
3092
+ droppedCount,
3093
+ compressedCount,
3094
+ cacheHits,
3095
+ cacheMisses,
3096
+ assemblyTimeMs: Date.now() - startTime
3097
+ };
3098
+ if (this.config.onAudit) {
3099
+ try {
3100
+ await this.config.onAudit({
3101
+ type: "context_assembled",
3102
+ data: {
3103
+ totalTokens,
3104
+ budgetUsed: result.budgetUsed,
3105
+ fragmentCount: selected.length,
3106
+ droppedCount,
3107
+ compressedCount,
3108
+ cacheHits,
3109
+ cacheMisses,
3110
+ assemblyTimeMs: result.assemblyTimeMs
3111
+ }
3112
+ });
3113
+ } catch {
3114
+ }
3115
+ }
3116
+ return result;
3117
+ }
3118
+ /**
3119
+ * Register a prefetch rule
3120
+ */
3121
+ addPrefetchRule(rule) {
3122
+ this.prefetchRules.set(rule.id, rule);
3123
+ }
3124
+ /**
3125
+ * Execute prefetch based on task type
3126
+ */
3127
+ async prefetch(taskType, fragmentLoader) {
3128
+ const matchingRules = Array.from(this.prefetchRules.values()).filter(
3129
+ (r) => r.enabled && taskType.includes(r.trigger)
3130
+ );
3131
+ if (matchingRules.length === 0) return 0;
3132
+ const idsToFetch = /* @__PURE__ */ new Set();
3133
+ for (const rule of matchingRules) {
3134
+ for (const id of rule.fragmentIds) {
3135
+ if (!this.cache.has(id)) {
3136
+ idsToFetch.add(id);
3137
+ }
3138
+ }
3139
+ }
3140
+ if (idsToFetch.size === 0) return 0;
3141
+ const fragments = await fragmentLoader(Array.from(idsToFetch));
3142
+ for (const f of fragments) {
3143
+ this.put(f);
3144
+ }
3145
+ return fragments.length;
3146
+ }
3147
+ /**
3148
+ * Invalidate cache entries
3149
+ */
3150
+ invalidate(ids) {
3151
+ let count = 0;
3152
+ for (const id of ids) {
3153
+ if (this.cache.delete(id)) count++;
3154
+ }
3155
+ return count;
3156
+ }
3157
+ /**
3158
+ * Get cache stats
3159
+ */
3160
+ getStats() {
3161
+ const total = this.stats.totalHits + this.stats.totalMisses;
3162
+ return {
3163
+ cacheSize: this.cache.size,
3164
+ maxSize: this.config.maxCacheEntries,
3165
+ hitRate: total > 0 ? this.stats.totalHits / total : 0,
3166
+ ...this.stats,
3167
+ prefetchRuleCount: this.prefetchRules.size
3168
+ };
3169
+ }
3170
+ /**
3171
+ * Clear all cache
3172
+ */
3173
+ clear() {
3174
+ this.cache.clear();
3175
+ }
3176
+ // ─── Internal ────────────────────────────────────────────────────
3177
+ evictExpired() {
3178
+ const now = Date.now();
3179
+ for (const [id, entry] of this.cache) {
3180
+ if (now > entry.expiresAt) {
3181
+ this.cache.delete(id);
3182
+ this.stats.totalEvictions++;
3183
+ }
3184
+ }
3185
+ }
3186
+ evictLRU() {
3187
+ let oldestId = null;
3188
+ let oldestAccess = Infinity;
3189
+ for (const [id, entry] of this.cache) {
3190
+ if (entry.lastAccessedAt < oldestAccess) {
3191
+ oldestAccess = entry.lastAccessedAt;
3192
+ oldestId = id;
3193
+ }
3194
+ }
3195
+ if (oldestId) {
3196
+ this.cache.delete(oldestId);
3197
+ this.stats.totalEvictions++;
3198
+ }
3199
+ }
3200
+ };
3201
+
3202
+ // src/trendAlertEngine.ts
3203
+ var DEFAULT_THRESHOLDS2 = [
3204
+ {
3205
+ alert_type: "BLOCK_RATE_SURGE",
3206
+ metric_name: "block_rate",
3207
+ baseline_window_hours: 24,
3208
+ surge_multiplier: 2,
3209
+ absolute_threshold: 0.5,
3210
+ severity: "CRITICAL",
3211
+ protective_actions: ["TIGHTEN_THRESHOLD", "NOTIFY_ONLY"],
3212
+ cooldown_seconds: 300,
3213
+ enabled: true
3214
+ },
3215
+ {
3216
+ alert_type: "OVERRIDE_RATE_SURGE",
3217
+ metric_name: "override_rate",
3218
+ baseline_window_hours: 24,
3219
+ surge_multiplier: 3,
3220
+ absolute_threshold: 0.3,
3221
+ severity: "CRITICAL",
3222
+ protective_actions: ["TIGHTEN_THRESHOLD", "FORCE_HUMAN_GATE"],
3223
+ cooldown_seconds: 300,
3224
+ enabled: true
3225
+ },
3226
+ {
3227
+ alert_type: "UNKNOWN_REASON_CODE",
3228
+ metric_name: "unknown_reason_rate",
3229
+ baseline_window_hours: 1,
3230
+ surge_multiplier: 1.5,
3231
+ absolute_threshold: 0.1,
3232
+ severity: "WARN",
3233
+ protective_actions: ["NOTIFY_ONLY"],
3234
+ cooldown_seconds: 600,
3235
+ enabled: true
3236
+ },
3237
+ {
3238
+ alert_type: "APPROVAL_QUEUE_BACKLOG",
3239
+ metric_name: "pending_approval_count",
3240
+ baseline_window_hours: 1,
3241
+ surge_multiplier: 5,
3242
+ absolute_threshold: 50,
3243
+ severity: "CRITICAL",
3244
+ protective_actions: ["DEGRADE", "NOTIFY_ONLY"],
3245
+ cooldown_seconds: 180,
3246
+ enabled: true
3247
+ },
3248
+ {
3249
+ alert_type: "CONFIDENCE_DROP",
3250
+ metric_name: "avg_confidence",
3251
+ baseline_window_hours: 24,
3252
+ surge_multiplier: 0.5,
3253
+ // 低于基线的比例
3254
+ absolute_threshold: 0.3,
3255
+ severity: "WARN",
3256
+ protective_actions: ["TIGHTEN_THRESHOLD"],
3257
+ cooldown_seconds: 600,
3258
+ enabled: true
3259
+ },
3260
+ {
3261
+ alert_type: "ERROR_RATE_SURGE",
3262
+ metric_name: "error_rate",
3263
+ baseline_window_hours: 1,
3264
+ surge_multiplier: 3,
3265
+ absolute_threshold: 0.2,
3266
+ severity: "EMERGENCY",
3267
+ protective_actions: ["KILL_SWITCH"],
3268
+ cooldown_seconds: 60,
3269
+ enabled: true
3270
+ }
3271
+ ];
3272
+ var TrendAlertEngine = class {
3273
+ thresholds;
3274
+ alertHistory = [];
3275
+ lastAlertTime = /* @__PURE__ */ new Map();
3276
+ systemState;
3277
+ metricsBuffer = [];
3278
+ constructor(customThresholds) {
3279
+ this.thresholds = customThresholds || DEFAULT_THRESHOLDS2;
3280
+ this.systemState = {
3281
+ kill_switch: { enabled: false, reason: null, enabled_at: null, enabled_by: "system", auto_triggered: false },
3282
+ degrade: { active: false, level: "LIGHT", reason: "", activated_at: null, auto_recover_at: null },
3283
+ tightened_threshold_multiplier: 1,
3284
+ force_human_gate: false
3285
+ };
3286
+ }
3287
+ // ---- 核心: 评估趋势 ----
3288
+ evaluate(metrics, tenantId = "default") {
3289
+ const alerts = [];
3290
+ this.metricsBuffer = metrics;
3291
+ for (const threshold of this.thresholds) {
3292
+ if (!threshold.enabled) continue;
3293
+ const metric = metrics.find((m) => m.metric_name === threshold.metric_name);
3294
+ if (!metric) continue;
3295
+ const lastAlert = this.lastAlertTime.get(threshold.alert_type);
3296
+ if (lastAlert && Date.now() - lastAlert < threshold.cooldown_seconds * 1e3) {
3297
+ continue;
3298
+ }
3299
+ const triggered = this.checkThreshold(metric, threshold);
3300
+ if (!triggered) continue;
3301
+ const surgeRatio = metric.baseline_value > 0 ? metric.current_value / metric.baseline_value : metric.current_value;
3302
+ const alert = {
3303
+ alert_id: `alert_${threshold.alert_type}_${Date.now()}`,
3304
+ alert_type: threshold.alert_type,
3305
+ severity: threshold.severity,
3306
+ metric_name: threshold.metric_name,
3307
+ current_value: metric.current_value,
3308
+ baseline_value: metric.baseline_value,
3309
+ threshold_value: threshold.absolute_threshold,
3310
+ surge_ratio: Math.round(surgeRatio * 100) / 100,
3311
+ protective_actions_taken: [],
3312
+ message: this.formatAlertMessage(threshold, metric, surgeRatio),
3313
+ tenant_id: tenantId,
3314
+ created_at: Date.now(),
3315
+ acknowledged: false,
3316
+ acknowledged_by: null,
3317
+ acknowledged_at: null
3318
+ };
3319
+ for (const action of threshold.protective_actions) {
3320
+ this.executeProtectiveAction(action, alert);
3321
+ alert.protective_actions_taken.push(action);
3322
+ }
3323
+ alerts.push(alert);
3324
+ this.alertHistory.push(alert);
3325
+ this.lastAlertTime.set(threshold.alert_type, Date.now());
3326
+ }
3327
+ return alerts;
3328
+ }
3329
+ // ---- Cron 评估入口 (DegradeEvaluator) ----
3330
+ cronEvaluate(getMetrics, tenantId = "default") {
3331
+ const metrics = getMetrics();
3332
+ const alerts = this.evaluate(metrics, tenantId);
3333
+ this.checkAutoRecover();
3334
+ return { alerts, state: this.getSystemState() };
3335
+ }
3336
+ // ---- 保护动作执行 ----
3337
+ executeProtectiveAction(action, alert) {
3338
+ switch (action) {
3339
+ case "DEGRADE":
3340
+ this.activateDegrade(alert);
3341
+ break;
3342
+ case "TIGHTEN_THRESHOLD":
3343
+ this.tightenThreshold(alert);
3344
+ break;
3345
+ case "KILL_SWITCH":
3346
+ this.activateKillSwitch(alert);
3347
+ break;
3348
+ case "FORCE_HUMAN_GATE":
3349
+ this.systemState.force_human_gate = true;
3350
+ break;
3351
+ case "NOTIFY_ONLY":
3352
+ break;
3353
+ }
3354
+ }
3355
+ activateDegrade(alert) {
3356
+ const level = alert.severity === "EMERGENCY" ? "SEVERE" : alert.severity === "CRITICAL" ? "MODERATE" : "LIGHT";
3357
+ this.systemState.degrade = {
3358
+ active: true,
3359
+ level,
3360
+ reason: `Auto-degrade triggered by ${alert.alert_type}: ${alert.message}`,
3361
+ activated_at: Date.now(),
3362
+ auto_recover_at: Date.now() + 30 * 60 * 1e3
3363
+ // 30分钟后自动恢复
3364
+ };
3365
+ }
3366
+ tightenThreshold(alert) {
3367
+ this.systemState.tightened_threshold_multiplier = Math.min(
3368
+ this.systemState.tightened_threshold_multiplier * 1.2,
3369
+ 2
3370
+ );
3371
+ }
3372
+ activateKillSwitch(alert) {
3373
+ this.systemState.kill_switch = {
3374
+ enabled: true,
3375
+ reason: `Auto Kill-Switch triggered by ${alert.alert_type}: ${alert.message}`,
3376
+ enabled_at: Date.now(),
3377
+ enabled_by: "trend_alert_engine",
3378
+ auto_triggered: true
3379
+ };
3380
+ }
3381
+ // ---- 阈值检查 ----
3382
+ checkThreshold(metric, threshold) {
3383
+ if (threshold.alert_type === "CONFIDENCE_DROP") {
3384
+ return metric.current_value < threshold.absolute_threshold || metric.baseline_value > 0 && metric.current_value < metric.baseline_value * threshold.surge_multiplier;
3385
+ }
3386
+ if (threshold.alert_type === "APPROVAL_QUEUE_BACKLOG") {
3387
+ return metric.current_value > threshold.absolute_threshold;
3388
+ }
3389
+ const surgeTriggered = metric.baseline_value > 0 && metric.current_value > metric.baseline_value * threshold.surge_multiplier;
3390
+ const absoluteTriggered = metric.current_value > threshold.absolute_threshold;
3391
+ return surgeTriggered || absoluteTriggered;
3392
+ }
3393
+ // ---- 自动恢复 ----
3394
+ checkAutoRecover() {
3395
+ if (this.systemState.degrade.active && this.systemState.degrade.auto_recover_at) {
3396
+ if (Date.now() >= this.systemState.degrade.auto_recover_at) {
3397
+ this.systemState.degrade = {
3398
+ active: false,
3399
+ level: "LIGHT",
3400
+ reason: "Auto-recovered after cooldown",
3401
+ activated_at: null,
3402
+ auto_recover_at: null
3403
+ };
3404
+ }
3405
+ }
3406
+ }
3407
+ // ---- 手动操作 ----
3408
+ acknowledgeAlert(alertId, acknowledgedBy) {
3409
+ const alert = this.alertHistory.find((a) => a.alert_id === alertId);
3410
+ if (!alert) return false;
3411
+ alert.acknowledged = true;
3412
+ alert.acknowledged_by = acknowledgedBy;
3413
+ alert.acknowledged_at = Date.now();
3414
+ return true;
3415
+ }
3416
+ disableKillSwitch(reason, actor) {
3417
+ if (!this.systemState.kill_switch.enabled) return false;
3418
+ this.systemState.kill_switch = {
3419
+ enabled: false,
3420
+ reason: `Disabled by ${actor}: ${reason}`,
3421
+ enabled_at: null,
3422
+ enabled_by: actor,
3423
+ auto_triggered: false
3424
+ };
3425
+ return true;
3426
+ }
3427
+ resetDegrade() {
3428
+ this.systemState.degrade = {
3429
+ active: false,
3430
+ level: "LIGHT",
3431
+ reason: "Manual reset",
3432
+ activated_at: null,
3433
+ auto_recover_at: null
3434
+ };
3435
+ this.systemState.tightened_threshold_multiplier = 1;
3436
+ this.systemState.force_human_gate = false;
3437
+ }
3438
+ // ---- 查询 ----
3439
+ getSystemState() {
3440
+ return { ...this.systemState };
3441
+ }
3442
+ getAlertHistory(limit = 50) {
3443
+ return this.alertHistory.slice(-limit);
3444
+ }
3445
+ getActiveAlerts() {
3446
+ return this.alertHistory.filter((a) => !a.acknowledged);
3447
+ }
3448
+ getMetricsBuffer() {
3449
+ return [...this.metricsBuffer];
3450
+ }
3451
+ // ---- 辅助 ----
3452
+ formatAlertMessage(threshold, metric, surgeRatio) {
3453
+ return `[${threshold.severity}] ${threshold.alert_type}: ${metric.metric_name} = ${metric.current_value.toFixed(3)} (baseline: ${metric.baseline_value.toFixed(3)}, surge: ${surgeRatio.toFixed(1)}x, threshold: ${threshold.absolute_threshold})`;
3454
+ }
3455
+ };
3456
+
3457
+ // src/selfConstraintEngine.ts
3458
+ var DEFAULT_THRESHOLDS3 = {
3459
+ risk_density: 0.75,
3460
+ conflict_rate: 0.6,
3461
+ confidence_drop: 0.2,
3462
+ error_rate: 0.15,
3463
+ latency_p99_ms: 5e3,
3464
+ consecutive_failures: 5
3465
+ };
3466
+ var NORMAL_FLAGS = {
3467
+ enable_advanced_features: true,
3468
+ enable_auto_approve: true,
3469
+ enable_batch_operations: true,
3470
+ enable_external_api_calls: true,
3471
+ enable_write_operations: true,
3472
+ require_human_gate: false,
3473
+ require_dual_review: false,
3474
+ max_concurrent_decisions: 100,
3475
+ decision_delay_ms: 0
3476
+ };
3477
+ var CAUTIOUS_FLAGS = {
3478
+ enable_advanced_features: true,
3479
+ enable_auto_approve: false,
3480
+ // 关闭自动审批
3481
+ enable_batch_operations: true,
3482
+ enable_external_api_calls: true,
3483
+ enable_write_operations: true,
3484
+ require_human_gate: false,
3485
+ require_dual_review: false,
3486
+ max_concurrent_decisions: 50,
3487
+ decision_delay_ms: 500
3488
+ // 500ms 延迟
3489
+ };
3490
+ var RESTRICTED_FLAGS = {
3491
+ enable_advanced_features: false,
3492
+ // 关闭高级功能
3493
+ enable_auto_approve: false,
3494
+ enable_batch_operations: false,
3495
+ // 关闭批量操作
3496
+ enable_external_api_calls: true,
3497
+ enable_write_operations: true,
3498
+ require_human_gate: true,
3499
+ // 强制人工门禁
3500
+ require_dual_review: false,
3501
+ max_concurrent_decisions: 10,
3502
+ decision_delay_ms: 2e3
3503
+ // 2s 延迟
3504
+ };
3505
+ var LOCKDOWN_FLAGS = {
3506
+ enable_advanced_features: false,
3507
+ enable_auto_approve: false,
3508
+ enable_batch_operations: false,
3509
+ enable_external_api_calls: false,
3510
+ // 关闭外部调用
3511
+ enable_write_operations: false,
3512
+ // 关闭写操作
3513
+ require_human_gate: true,
3514
+ require_dual_review: true,
3515
+ // 双人审核
3516
+ max_concurrent_decisions: 1,
3517
+ decision_delay_ms: 5e3
3518
+ // 5s 延迟
3519
+ };
3520
+ var LEVEL_FLAGS_MAP = {
3521
+ NORMAL: NORMAL_FLAGS,
3522
+ CAUTIOUS: CAUTIOUS_FLAGS,
3523
+ RESTRICTED: RESTRICTED_FLAGS,
3524
+ LOCKDOWN: LOCKDOWN_FLAGS
3525
+ };
3526
+ var SelfConstraintEngine = class {
3527
+ config;
3528
+ state;
3529
+ constructor(config) {
3530
+ this.config = {
3531
+ thresholds: config?.thresholds || DEFAULT_THRESHOLDS3,
3532
+ flags: config?.flags || NORMAL_FLAGS,
3533
+ auto_recover_after_minutes: config?.auto_recover_after_minutes || 30,
3534
+ evaluation_interval_seconds: config?.evaluation_interval_seconds || 60
3535
+ };
3536
+ this.state = {
3537
+ current_level: "NORMAL",
3538
+ active_modes: [],
3539
+ flags: { ...NORMAL_FLAGS },
3540
+ activated_at: null,
3541
+ auto_recover_at: null,
3542
+ last_evaluation_at: 0,
3543
+ evaluation_count: 0,
3544
+ constraint_history: []
3545
+ };
3546
+ }
3547
+ // ---- 核心: 评估自我约束 ----
3548
+ evaluate(metrics) {
3549
+ const triggers = [];
3550
+ const modes = /* @__PURE__ */ new Set();
3551
+ triggers.push({
3552
+ metric_name: "risk_density",
3553
+ current_value: metrics.risk_density,
3554
+ threshold_value: this.config.thresholds.risk_density,
3555
+ exceeded: metrics.risk_density >= this.config.thresholds.risk_density
3556
+ });
3557
+ if (metrics.risk_density >= this.config.thresholds.risk_density) {
3558
+ modes.add("CAPABILITY_DOWN");
3559
+ modes.add("PERMISSION_DOWN");
3560
+ }
3561
+ triggers.push({
3562
+ metric_name: "conflict_rate",
3563
+ current_value: metrics.conflict_rate,
3564
+ threshold_value: this.config.thresholds.conflict_rate,
3565
+ exceeded: metrics.conflict_rate >= this.config.thresholds.conflict_rate
3566
+ });
3567
+ if (metrics.conflict_rate >= this.config.thresholds.conflict_rate) {
3568
+ modes.add("SPEED_DOWN");
3569
+ modes.add("PERMISSION_DOWN");
3570
+ }
3571
+ const confidenceDrop = metrics.prev_avg_confidence > 0 ? (metrics.prev_avg_confidence - metrics.avg_confidence) / metrics.prev_avg_confidence : 0;
3572
+ triggers.push({
3573
+ metric_name: "confidence_drop",
3574
+ current_value: confidenceDrop,
3575
+ threshold_value: this.config.thresholds.confidence_drop,
3576
+ exceeded: confidenceDrop >= this.config.thresholds.confidence_drop
3577
+ });
3578
+ if (confidenceDrop >= this.config.thresholds.confidence_drop) {
3579
+ modes.add("SPEED_DOWN");
3580
+ }
3581
+ triggers.push({
3582
+ metric_name: "error_rate",
3583
+ current_value: metrics.error_rate,
3584
+ threshold_value: this.config.thresholds.error_rate,
3585
+ exceeded: metrics.error_rate >= this.config.thresholds.error_rate
3586
+ });
3587
+ if (metrics.error_rate >= this.config.thresholds.error_rate) {
3588
+ modes.add("CAPABILITY_DOWN");
3589
+ }
3590
+ triggers.push({
3591
+ metric_name: "latency_p99_ms",
3592
+ current_value: metrics.latency_p99_ms,
3593
+ threshold_value: this.config.thresholds.latency_p99_ms,
3594
+ exceeded: metrics.latency_p99_ms >= this.config.thresholds.latency_p99_ms
3595
+ });
3596
+ if (metrics.latency_p99_ms >= this.config.thresholds.latency_p99_ms) {
3597
+ modes.add("SPEED_DOWN");
3598
+ }
3599
+ triggers.push({
3600
+ metric_name: "consecutive_failures",
3601
+ current_value: metrics.consecutive_failures,
3602
+ threshold_value: this.config.thresholds.consecutive_failures,
3603
+ exceeded: metrics.consecutive_failures >= this.config.thresholds.consecutive_failures
3604
+ });
3605
+ if (metrics.consecutive_failures >= this.config.thresholds.consecutive_failures) {
3606
+ modes.add("CAPABILITY_DOWN");
3607
+ modes.add("PERMISSION_DOWN");
3608
+ }
3609
+ const triggeredCount = triggers.filter((t) => t.exceeded).length;
3610
+ const level = this.calculateLevel(triggeredCount, metrics.system_pressure);
3611
+ const activeModes = Array.from(modes);
3612
+ const flagsApplied = LEVEL_FLAGS_MAP[level];
3613
+ this.applyConstraint(level, activeModes, flagsApplied);
3614
+ const decision = {
3615
+ decision_id: `sc_${Date.now()}_${this.state.evaluation_count}`,
3616
+ timestamp: Date.now(),
3617
+ level,
3618
+ modes_activated: activeModes,
3619
+ triggers,
3620
+ flags_applied: flagsApplied,
3621
+ reason_code: activeModes.length > 0 ? "SELF_CONSTRAINT_ACTIVATED" : "NORMAL",
3622
+ auto_recover_at: level !== "NORMAL" ? Date.now() + this.config.auto_recover_after_minutes * 60 * 1e3 : null
3623
+ };
3624
+ this.state.constraint_history.push(decision);
3625
+ if (this.state.constraint_history.length > 100) {
3626
+ this.state.constraint_history = this.state.constraint_history.slice(-100);
3627
+ }
3628
+ this.state.evaluation_count++;
3629
+ this.state.last_evaluation_at = Date.now();
3630
+ return decision;
3631
+ }
3632
+ // ---- 约束级别计算 ----
3633
+ calculateLevel(triggeredCount, systemPressure) {
3634
+ const score = triggeredCount + systemPressure * 3;
3635
+ if (score >= 5) return "LOCKDOWN";
3636
+ if (score >= 3) return "RESTRICTED";
3637
+ if (score >= 1) return "CAUTIOUS";
3638
+ return "NORMAL";
3639
+ }
3640
+ // ---- 应用约束 ----
3641
+ applyConstraint(level, modes, flags) {
3642
+ const wasNormal = this.state.current_level === "NORMAL";
3643
+ this.state.current_level = level;
3644
+ this.state.active_modes = modes;
3645
+ this.state.flags = { ...flags };
3646
+ if (level !== "NORMAL" && wasNormal) {
3647
+ this.state.activated_at = Date.now();
3648
+ this.state.auto_recover_at = Date.now() + this.config.auto_recover_after_minutes * 60 * 1e3;
3649
+ } else if (level === "NORMAL") {
3650
+ this.state.activated_at = null;
3651
+ this.state.auto_recover_at = null;
3652
+ }
3653
+ }
3654
+ // ---- 自动恢复检查 ----
3655
+ checkAutoRecover() {
3656
+ if (this.state.current_level === "NORMAL") return false;
3657
+ if (!this.state.auto_recover_at) return false;
3658
+ if (Date.now() >= this.state.auto_recover_at) {
3659
+ this.reset();
3660
+ return true;
3661
+ }
3662
+ return false;
3663
+ }
3664
+ // ---- 手动操作 ----
3665
+ reset() {
3666
+ this.state.current_level = "NORMAL";
3667
+ this.state.active_modes = [];
3668
+ this.state.flags = { ...NORMAL_FLAGS };
3669
+ this.state.activated_at = null;
3670
+ this.state.auto_recover_at = null;
3671
+ }
3672
+ forceLevel(level) {
3673
+ const flags = LEVEL_FLAGS_MAP[level];
3674
+ this.applyConstraint(level, level === "NORMAL" ? [] : ["CAPABILITY_DOWN", "PERMISSION_DOWN"], flags);
3675
+ }
3676
+ // ---- 决策前检查 (Gate 集成) ----
3677
+ preDecisionCheck(action, resource) {
3678
+ const flags = this.state.flags;
3679
+ if (!flags.enable_write_operations && this.isWriteAction(action)) {
3680
+ return {
3681
+ allowed: false,
3682
+ delay_ms: 0,
3683
+ requires_human: true,
3684
+ requires_dual_review: flags.require_dual_review,
3685
+ reason: `Write operations disabled at ${this.state.current_level} level`
3686
+ };
3687
+ }
3688
+ if (!flags.enable_external_api_calls && this.isExternalAction(action)) {
3689
+ return {
3690
+ allowed: false,
3691
+ delay_ms: 0,
3692
+ requires_human: true,
3693
+ requires_dual_review: flags.require_dual_review,
3694
+ reason: `External API calls disabled at ${this.state.current_level} level`
3695
+ };
3696
+ }
3697
+ if (!flags.enable_batch_operations && this.isBatchAction(action)) {
3698
+ return {
3699
+ allowed: false,
3700
+ delay_ms: 0,
3701
+ requires_human: true,
3702
+ requires_dual_review: false,
3703
+ reason: `Batch operations disabled at ${this.state.current_level} level`
3704
+ };
3705
+ }
3706
+ return {
3707
+ allowed: true,
3708
+ delay_ms: flags.decision_delay_ms,
3709
+ requires_human: flags.require_human_gate,
3710
+ requires_dual_review: flags.require_dual_review,
3711
+ reason: this.state.current_level === "NORMAL" ? "OK" : `Constrained at ${this.state.current_level} level`
3712
+ };
3713
+ }
3714
+ // ---- 查询 ----
3715
+ getState() {
3716
+ return {
3717
+ ...this.state,
3718
+ flags: { ...this.state.flags },
3719
+ active_modes: [...this.state.active_modes],
3720
+ constraint_history: [...this.state.constraint_history]
3721
+ };
3722
+ }
3723
+ getCurrentLevel() {
3724
+ return this.state.current_level;
3725
+ }
3726
+ getFlags() {
3727
+ return { ...this.state.flags };
3728
+ }
3729
+ isConstrained() {
3730
+ return this.state.current_level !== "NORMAL";
3731
+ }
3732
+ // ---- 辅助 ----
3733
+ isWriteAction(action) {
3734
+ const writePatterns = ["write", "create", "update", "delete", "insert", "modify", "patch", "put"];
3735
+ return writePatterns.some((p) => action.toLowerCase().includes(p));
3736
+ }
3737
+ isExternalAction(action) {
3738
+ const externalPatterns = ["external", "api_call", "webhook", "http", "fetch", "request"];
3739
+ return externalPatterns.some((p) => action.toLowerCase().includes(p));
3740
+ }
3741
+ isBatchAction(action) {
3742
+ const batchPatterns = ["batch", "bulk", "mass", "multi"];
3743
+ return batchPatterns.some((p) => action.toLowerCase().includes(p));
3744
+ }
3745
+ };
3746
+
3747
+ // src/contributionSettlement.ts
3748
+ var DEFAULT_SCORE_CONFIG = {
3749
+ version: "1.0.0",
3750
+ weight_risk: 0.35,
3751
+ weight_cost: 0.25,
3752
+ weight_human: 0.2,
3753
+ weight_stability: 0.2,
3754
+ weight_penalty: 1,
3755
+ caps: {
3756
+ max_single_event: 100,
3757
+ max_risk_positive_when_risk_up: false
3758
+ },
3759
+ guardrails: [
3760
+ "RISK_UP_BLOCKS_POSITIVE",
3761
+ "EVIDENCE_DOWNGRADE_NEGATIVE",
3762
+ "OVERRIDE_INCREASE_PENALTY"
3763
+ ],
3764
+ approved_by: "system",
3765
+ approved_at: Date.now()
3766
+ };
3767
+ var MVP_ATTRIBUTION_RULES = [
3768
+ {
3769
+ condition: "RULE_TRIGGERED_BLOCK_WARN",
3770
+ target_entity_type: "rule",
3771
+ target_entity_field: "rule_id",
3772
+ weight: 1
3773
+ },
3774
+ {
3775
+ condition: "EVIDENCE_UPGRADED_ALLOW",
3776
+ target_entity_type: "tool",
3777
+ target_entity_field: "evidence_source",
3778
+ weight: 0.8
3779
+ },
3780
+ {
3781
+ condition: "MODEL_ROUTE_CHEAPER_SAME_RESULT",
3782
+ target_entity_type: "model",
3783
+ target_entity_field: "modelops_route_id",
3784
+ weight: 0.7
3785
+ },
3786
+ {
3787
+ condition: "CACHE_HIT_SKIP_TOOL",
3788
+ target_entity_type: "system",
3789
+ target_entity_field: "context_accelerator",
3790
+ weight: 0.6
3791
+ },
3792
+ {
3793
+ condition: "HUMAN_OVERRIDE_CORRECTION",
3794
+ target_entity_type: "human",
3795
+ target_entity_field: "actor_id",
3796
+ weight: 0.5
3797
+ }
3798
+ ];
3799
+ var BUILTIN_PENALTY_CHECKS = [
3800
+ {
3801
+ name: "OVERRIDE_INCREASE",
3802
+ detect: (_events, ctx) => {
3803
+ if (ctx.override_count_delta > 0) {
3804
+ return ctx.override_count_delta * 5;
3805
+ }
3806
+ return 0;
3807
+ }
3808
+ },
3809
+ {
3810
+ name: "REPLAY_FAIL_INCREASE",
3811
+ detect: (_events, ctx) => {
3812
+ if (ctx.replay_fail_delta > 0) {
3813
+ return ctx.replay_fail_delta * 10;
3814
+ }
3815
+ return 0;
3816
+ }
3817
+ },
3818
+ {
3819
+ name: "RISK_REGRESSION",
3820
+ detect: (_events, ctx) => {
3821
+ if (ctx.risk_regression) {
3822
+ return 50;
3823
+ }
3824
+ return 0;
3825
+ }
3826
+ },
3827
+ {
3828
+ name: "EVIDENCE_DOWNGRADE",
3829
+ detect: (_events, ctx) => {
3830
+ if (ctx.evidence_downgrade) {
3831
+ return 30;
3832
+ }
3833
+ return 0;
3834
+ }
3835
+ },
3836
+ {
3837
+ name: "SELF_MANUFACTURE_DETECT",
3838
+ detect: (events, _ctx) => {
3839
+ const entityMap = /* @__PURE__ */ new Map();
3840
+ for (const e of events) {
3841
+ const key = `${e.entity_type}:${e.entity_id}`;
3842
+ const entry = entityMap.get(key) || { negative: 0, positive: 0 };
3843
+ if (e.delta_value < 0) entry.negative += Math.abs(e.delta_value);
3844
+ else entry.positive += e.delta_value;
3845
+ entityMap.set(key, entry);
3846
+ }
3847
+ let penalty = 0;
3848
+ for (const [, entry] of entityMap) {
3849
+ if (entry.negative > 20 && entry.positive > 20) {
3850
+ const ratio = Math.min(entry.negative, entry.positive) / Math.max(entry.negative, entry.positive);
3851
+ if (ratio > 0.7) {
3852
+ penalty += 25;
3853
+ }
3854
+ }
3855
+ }
3856
+ return penalty;
3857
+ }
3858
+ }
3859
+ ];
3860
+ var ContributionSettlementEngine = class {
3861
+ config;
3862
+ attributionRules;
3863
+ penaltyChecks;
3864
+ eventStore = [];
3865
+ scoreStore = [];
3866
+ constructor(config, customRules, customPenalties) {
3867
+ this.config = { ...DEFAULT_SCORE_CONFIG, ...config };
3868
+ this.attributionRules = customRules || MVP_ATTRIBUTION_RULES;
3869
+ this.penaltyChecks = [...BUILTIN_PENALTY_CHECKS, ...customPenalties || []];
3870
+ }
3871
+ // ---- 事件记录 ----
3872
+ recordEvent(event) {
3873
+ if (Math.abs(event.delta_value) > this.config.caps.max_single_event) {
3874
+ event.delta_value = event.delta_value > 0 ? this.config.caps.max_single_event : -this.config.caps.max_single_event;
3875
+ }
3876
+ const exists = this.eventStore.find((e) => e.event_id === event.event_id);
3877
+ if (exists) return exists;
3878
+ this.eventStore.push(event);
3879
+ return event;
3880
+ }
3881
+ // ---- 归因 ----
3882
+ attribute(traceId, condition, entityType, entityId) {
3883
+ const rule = this.attributionRules.find((r) => r.condition === condition);
3884
+ if (!rule) return null;
3885
+ const event = {
3886
+ event_id: `attr_${traceId}_${Date.now()}`,
3887
+ trace_id: traceId,
3888
+ decision_id: traceId,
3889
+ tenant_id: "default",
3890
+ entity_type: entityType,
3891
+ entity_id: entityId,
3892
+ dimension: this.inferDimension(condition),
3893
+ delta_value: rule.weight * 10,
3894
+ // 基础分 * 权重
3895
+ delta_confidence: rule.weight,
3896
+ baseline_ref: "current",
3897
+ evidence_bundle_hash: "",
3898
+ created_at: Date.now()
3899
+ };
3900
+ return this.recordEvent(event);
3901
+ }
3902
+ // ---- 窗口结算 ----
3903
+ settle(context) {
3904
+ const windowEvents = this.eventStore.filter(
3905
+ (e) => e.tenant_id === context.tenant_id && e.created_at >= context.window_start && e.created_at <= context.window_end
3906
+ );
3907
+ const entityGroups = /* @__PURE__ */ new Map();
3908
+ for (const e of windowEvents) {
3909
+ const key = `${e.entity_type}:${e.entity_id}`;
3910
+ const group = entityGroups.get(key) || [];
3911
+ group.push(e);
3912
+ entityGroups.set(key, group);
3913
+ }
3914
+ const scores = [];
3915
+ for (const [key, events] of entityGroups) {
3916
+ const [entityType, entityId] = key.split(":");
3917
+ let scoreRisk = 0, scoreCost = 0, scoreHuman = 0, scoreStability = 0;
3918
+ let totalConfidence = 0;
3919
+ for (const e of events) {
3920
+ const val = e.delta_value * e.delta_confidence;
3921
+ switch (e.dimension) {
3922
+ case "RISK":
3923
+ scoreRisk += val;
3924
+ break;
3925
+ case "COST":
3926
+ scoreCost += val;
3927
+ break;
3928
+ case "HUMAN":
3929
+ scoreHuman += val;
3930
+ break;
3931
+ case "STABILITY":
3932
+ scoreStability += val;
3933
+ break;
3934
+ }
3935
+ totalConfidence += e.delta_confidence;
3936
+ }
3937
+ if (!this.config.caps.max_risk_positive_when_risk_up && context.risk_regression) {
3938
+ scoreRisk = Math.min(scoreRisk, 0);
3939
+ scoreCost = Math.min(scoreCost, 0);
3940
+ scoreHuman = Math.min(scoreHuman, 0);
3941
+ scoreStability = Math.min(scoreStability, 0);
3942
+ }
3943
+ let penaltyTotal = 0;
3944
+ for (const check of this.penaltyChecks) {
3945
+ penaltyTotal += check.detect(events, context);
3946
+ }
3947
+ const scoreTotal = this.config.weight_risk * scoreRisk + this.config.weight_cost * scoreCost + this.config.weight_human * scoreHuman + this.config.weight_stability * scoreStability - this.config.weight_penalty * penaltyTotal;
3948
+ const avgConfidence = events.length > 0 ? totalConfidence / events.length : 0;
3949
+ const score = {
3950
+ score_id: `score_${entityType}_${entityId}_${context.window_start}`,
3951
+ window_start: context.window_start,
3952
+ window_end: context.window_end,
3953
+ tenant_id: context.tenant_id,
3954
+ entity_type: entityType,
3955
+ entity_id: entityId,
3956
+ score_risk: Math.round(scoreRisk * 100) / 100,
3957
+ score_cost: Math.round(scoreCost * 100) / 100,
3958
+ score_human: Math.round(scoreHuman * 100) / 100,
3959
+ score_stability: Math.round(scoreStability * 100) / 100,
3960
+ score_total: Math.round(scoreTotal * 100) / 100,
3961
+ penalty_total: Math.round(penaltyTotal * 100) / 100,
3962
+ confidence: Math.round(avgConfidence * 100) / 100,
3963
+ score_config_version: this.config.version,
3964
+ created_at: Date.now()
3965
+ };
3966
+ scores.push(score);
3967
+ this.scoreStore.push(score);
3968
+ }
3969
+ return scores;
3970
+ }
3971
+ // ---- 控制面联动 ----
3972
+ getResourceAllocation(entityType, entityId) {
3973
+ const recentScores = this.scoreStore.filter(
3974
+ (s) => s.entity_type === entityType && s.entity_id === entityId
3975
+ );
3976
+ if (recentScores.length === 0) {
3977
+ return {
3978
+ token_budget_multiplier: 1,
3979
+ concurrency_quota_multiplier: 1,
3980
+ auto_approve_eligible: false,
3981
+ requires_dual_review: false
3982
+ };
3983
+ }
3984
+ const latestScore = recentScores[recentScores.length - 1];
3985
+ const total = latestScore.score_total;
3986
+ return {
3987
+ token_budget_multiplier: total > 50 ? 1.5 : total > 0 ? 1 : 0.5,
3988
+ concurrency_quota_multiplier: total > 50 ? 1.3 : total > 0 ? 1 : 0.7,
3989
+ auto_approve_eligible: total > 80 && latestScore.penalty_total === 0,
3990
+ requires_dual_review: total < -20 || latestScore.penalty_total > 30
3991
+ };
3992
+ }
3993
+ // ---- 审计抽查 ----
3994
+ auditSample(entityType, entityId, sampleSize = 3) {
3995
+ const entityEvents = this.eventStore.filter(
3996
+ (e) => e.entity_type === entityType && e.entity_id === entityId
3997
+ );
3998
+ const shuffled = [...entityEvents].sort(() => Math.random() - 0.5);
3999
+ return shuffled.slice(0, Math.min(sampleSize, shuffled.length));
4000
+ }
4001
+ // ---- 辅助 ----
4002
+ inferDimension(condition) {
4003
+ if (condition.includes("RULE") || condition.includes("BLOCK")) return "RISK";
4004
+ if (condition.includes("COST") || condition.includes("CACHE") || condition.includes("CHEAPER")) return "COST";
4005
+ if (condition.includes("HUMAN") || condition.includes("OVERRIDE")) return "HUMAN";
4006
+ return "STABILITY";
4007
+ }
4008
+ getConfig() {
4009
+ return { ...this.config };
4010
+ }
4011
+ getEventCount() {
4012
+ return this.eventStore.length;
4013
+ }
4014
+ getScoreCount() {
4015
+ return this.scoreStore.length;
4016
+ }
4017
+ };
4018
+
4019
+ // src/overrideDriftAnalyzer.ts
4020
+ var DEFAULT_CONFIG6 = {
4021
+ baseline_window_hours: 168,
4022
+ // 7 天基线
4023
+ analysis_window_hours: 24,
4024
+ // 24 小时分析窗口
4025
+ rate_drift_threshold: 0.3,
4026
+ // override 率偏离 30%
4027
+ concentration_threshold: 0.5,
4028
+ // 单一实体占比超 50%
4029
+ risk_drift_threshold: 0.4,
4030
+ // 高风险 override 占比偏离 40%
4031
+ temporal_drift_threshold: 0.3,
4032
+ // 时间分布偏离 30%
4033
+ composite_alert_threshold: 0.5,
4034
+ // 综合漂移指数超 0.5 告警
4035
+ min_sample_size: 10
4036
+ // 最小样本量
4037
+ };
4038
+ var OverrideDriftAnalyzer = class {
4039
+ config;
4040
+ events = [];
4041
+ baselineWindow = null;
4042
+ alerts = [];
4043
+ constructor(config) {
4044
+ this.config = { ...DEFAULT_CONFIG6, ...config };
4045
+ }
4046
+ // ---- 事件记录 ----
4047
+ recordOverride(event) {
4048
+ if (this.events.find((e) => e.event_id === event.event_id)) return;
4049
+ this.events.push(event);
4050
+ }
4051
+ // ---- 窗口统计 ----
4052
+ computeWindow(start, end, totalDecisions) {
4053
+ const windowEvents = this.events.filter(
4054
+ (e) => e.created_at >= start && e.created_at <= end
4055
+ );
4056
+ const byActor = /* @__PURE__ */ new Map();
4057
+ const byRule = /* @__PURE__ */ new Map();
4058
+ const byRiskLevel = { LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0 };
4059
+ const byHour = new Array(24).fill(0);
4060
+ for (const e of windowEvents) {
4061
+ byActor.set(e.actor_id, (byActor.get(e.actor_id) || 0) + 1);
4062
+ byRule.set(e.rule_id, (byRule.get(e.rule_id) || 0) + 1);
4063
+ byRiskLevel[e.risk_level] = (byRiskLevel[e.risk_level] || 0) + 1;
4064
+ const hour = new Date(e.created_at).getHours();
4065
+ byHour[hour]++;
4066
+ }
4067
+ const total = totalDecisions || Math.max(windowEvents.length * 5, 1);
4068
+ return {
4069
+ window_start: start,
4070
+ window_end: end,
4071
+ total_decisions: total,
4072
+ total_overrides: windowEvents.length,
4073
+ override_rate: total > 0 ? windowEvents.length / total : 0,
4074
+ by_actor: byActor,
4075
+ by_rule: byRule,
4076
+ by_risk_level: byRiskLevel,
4077
+ by_hour: byHour
4078
+ };
4079
+ }
4080
+ // ---- 基线计算 ----
4081
+ updateBaseline(totalDecisions) {
4082
+ const now = Date.now();
4083
+ const start = now - this.config.baseline_window_hours * 60 * 60 * 1e3;
4084
+ this.baselineWindow = this.computeWindow(start, now, totalDecisions);
4085
+ return this.baselineWindow;
4086
+ }
4087
+ // ---- 漂移指数计算 ----
4088
+ calculateDriftIndex(currentWindow, totalDecisions) {
4089
+ const now = Date.now();
4090
+ const current = currentWindow || this.computeWindow(
4091
+ now - this.config.analysis_window_hours * 60 * 60 * 1e3,
4092
+ now,
4093
+ totalDecisions
4094
+ );
4095
+ if (!this.baselineWindow) {
4096
+ this.updateBaseline(totalDecisions);
4097
+ }
4098
+ const baseline = this.baselineWindow;
4099
+ const rateDrift = baseline.override_rate > 0 ? Math.abs(current.override_rate - baseline.override_rate) / baseline.override_rate : current.override_rate > 0 ? 1 : 0;
4100
+ const actorConcentration = this.calculateConcentration(current.by_actor, current.total_overrides);
4101
+ const baselineActorConc = this.calculateConcentration(baseline.by_actor, baseline.total_overrides);
4102
+ const concentrationDrift = Math.abs(actorConcentration - baselineActorConc);
4103
+ const currentHighRisk = ((current.by_risk_level["HIGH"] || 0) + (current.by_risk_level["CRITICAL"] || 0)) / Math.max(current.total_overrides, 1);
4104
+ const baselineHighRisk = ((baseline.by_risk_level["HIGH"] || 0) + (baseline.by_risk_level["CRITICAL"] || 0)) / Math.max(baseline.total_overrides, 1);
4105
+ const riskDrift = Math.abs(currentHighRisk - baselineHighRisk);
4106
+ const temporalDrift = this.calculateTemporalDrift(current.by_hour, baseline.by_hour);
4107
+ const composite = rateDrift * 0.35 + concentrationDrift * 0.25 + riskDrift * 0.25 + temporalDrift * 0.15;
4108
+ const trend = this.determineTrend(composite);
4109
+ return {
4110
+ value: Math.min(Math.round(composite * 1e3) / 1e3, 1),
4111
+ rate_drift: Math.round(rateDrift * 1e3) / 1e3,
4112
+ concentration_drift: Math.round(concentrationDrift * 1e3) / 1e3,
4113
+ risk_drift: Math.round(riskDrift * 1e3) / 1e3,
4114
+ temporal_drift: Math.round(temporalDrift * 1e3) / 1e3,
4115
+ trend
4116
+ };
4117
+ }
4118
+ // ---- 分析 & 告警 ----
4119
+ analyze(totalDecisions) {
4120
+ const driftIndex = this.calculateDriftIndex(void 0, totalDecisions);
4121
+ if (driftIndex.value < this.config.composite_alert_threshold) {
4122
+ return null;
4123
+ }
4124
+ const patterns = this.detectPatterns(driftIndex);
4125
+ const recommendations = this.generateRecommendations(driftIndex, patterns);
4126
+ const severity = driftIndex.value >= 0.8 ? "CRITICAL" : driftIndex.value >= 0.5 ? "WARN" : "INFO";
4127
+ const alert = {
4128
+ alert_id: `da_${Date.now()}`,
4129
+ drift_index: driftIndex,
4130
+ severity,
4131
+ message: `Override drift detected: composite index = ${driftIndex.value} (${driftIndex.trend})`,
4132
+ patterns_detected: patterns,
4133
+ recommendations,
4134
+ created_at: Date.now()
4135
+ };
4136
+ this.alerts.push(alert);
4137
+ return alert;
4138
+ }
4139
+ // ---- 模式检测 ----
4140
+ detectPatterns(driftIndex) {
4141
+ const patterns = [];
4142
+ if (driftIndex.rate_drift > this.config.rate_drift_threshold) {
4143
+ patterns.push({
4144
+ pattern_type: "RATE_SPIKE",
4145
+ description: `Override rate increased by ${(driftIndex.rate_drift * 100).toFixed(1)}% from baseline`,
4146
+ evidence: { rate_drift: driftIndex.rate_drift },
4147
+ confidence: Math.min(driftIndex.rate_drift, 1)
4148
+ });
4149
+ }
4150
+ if (driftIndex.concentration_drift > this.config.concentration_threshold) {
4151
+ patterns.push({
4152
+ pattern_type: "ACTOR_CONCENTRATION",
4153
+ description: "Override activity concentrated on fewer actors than baseline",
4154
+ evidence: { concentration_drift: driftIndex.concentration_drift },
4155
+ confidence: Math.min(driftIndex.concentration_drift, 1)
4156
+ });
4157
+ }
4158
+ if (driftIndex.risk_drift > this.config.risk_drift_threshold) {
4159
+ patterns.push({
4160
+ pattern_type: "RISK_ESCALATION",
4161
+ description: `High-risk override proportion shifted by ${(driftIndex.risk_drift * 100).toFixed(1)}%`,
4162
+ evidence: { risk_drift: driftIndex.risk_drift },
4163
+ confidence: Math.min(driftIndex.risk_drift, 1)
4164
+ });
4165
+ }
4166
+ if (driftIndex.temporal_drift > this.config.temporal_drift_threshold) {
4167
+ patterns.push({
4168
+ pattern_type: "TIME_ANOMALY",
4169
+ description: "Override time distribution deviates from baseline pattern",
4170
+ evidence: { temporal_drift: driftIndex.temporal_drift },
4171
+ confidence: Math.min(driftIndex.temporal_drift, 1)
4172
+ });
4173
+ }
4174
+ return patterns;
4175
+ }
4176
+ // ---- 建议生成 ----
4177
+ generateRecommendations(driftIndex, patterns) {
4178
+ const recs = [];
4179
+ if (patterns.some((p) => p.pattern_type === "RATE_SPIKE")) {
4180
+ recs.push("Review recent policy changes that may have increased false positives");
4181
+ recs.push("Check if new rules are overly restrictive, causing excessive overrides");
4182
+ }
4183
+ if (patterns.some((p) => p.pattern_type === "ACTOR_CONCENTRATION")) {
4184
+ recs.push("Investigate concentrated override activity for potential abuse");
4185
+ recs.push("Consider implementing per-actor override quotas");
4186
+ }
4187
+ if (patterns.some((p) => p.pattern_type === "RISK_ESCALATION")) {
4188
+ recs.push("Audit high-risk overrides for potential security implications");
4189
+ recs.push("Consider requiring dual approval for high-risk overrides");
4190
+ }
4191
+ if (patterns.some((p) => p.pattern_type === "TIME_ANOMALY")) {
4192
+ recs.push("Check for automated override scripts running at unusual hours");
4193
+ recs.push("Review access patterns during off-hours");
4194
+ }
4195
+ if (driftIndex.value >= 0.8) {
4196
+ recs.push("CRITICAL: Consider activating self-constraint engine to restrict override capability");
4197
+ }
4198
+ return recs;
4199
+ }
4200
+ // ---- 辅助计算 ----
4201
+ calculateConcentration(distribution, total) {
4202
+ if (total === 0) return 0;
4203
+ let herfindahl = 0;
4204
+ for (const [, count] of distribution) {
4205
+ const share = count / total;
4206
+ herfindahl += share * share;
4207
+ }
4208
+ return herfindahl;
4209
+ }
4210
+ calculateTemporalDrift(current, baseline) {
4211
+ const currentTotal = current.reduce((a, b) => a + b, 0) || 1;
4212
+ const baselineTotal = baseline.reduce((a, b) => a + b, 0) || 1;
4213
+ let divergence = 0;
4214
+ for (let i = 0; i < 24; i++) {
4215
+ const p = current[i] / currentTotal;
4216
+ const q = baseline[i] / baselineTotal || 1 / 24;
4217
+ if (p > 0) {
4218
+ divergence += p * Math.log(p / q);
4219
+ }
4220
+ }
4221
+ return Math.min(divergence / 2, 1);
4222
+ }
4223
+ determineTrend(composite) {
4224
+ const recentAlerts = this.alerts.slice(-5);
4225
+ if (recentAlerts.length < 2) return "STABLE";
4226
+ const values = recentAlerts.map((a) => a.drift_index.value);
4227
+ const diffs = values.slice(1).map((v, i) => v - values[i]);
4228
+ const avgDiff = diffs.reduce((a, b) => a + b, 0) / diffs.length;
4229
+ const variance = diffs.reduce((a, b) => a + (b - avgDiff) ** 2, 0) / diffs.length;
4230
+ if (variance > 0.1) return "VOLATILE";
4231
+ if (avgDiff > 0.05) return "RISING";
4232
+ if (avgDiff < -0.05) return "FALLING";
4233
+ return "STABLE";
4234
+ }
4235
+ // ---- 查询 ----
4236
+ getAlerts(limit = 20) {
4237
+ return this.alerts.slice(-limit);
4238
+ }
4239
+ getEventCount() {
4240
+ return this.events.length;
4241
+ }
4242
+ getLatestDriftIndex(totalDecisions) {
4243
+ return this.calculateDriftIndex(void 0, totalDecisions);
4244
+ }
4245
+ getStats() {
4246
+ const now = Date.now();
4247
+ const currentWindow = this.computeWindow(
4248
+ now - this.config.analysis_window_hours * 60 * 60 * 1e3,
4249
+ now
4250
+ );
4251
+ return {
4252
+ total_events: this.events.length,
4253
+ total_alerts: this.alerts.length,
4254
+ baseline_override_rate: this.baselineWindow?.override_rate || 0,
4255
+ current_override_rate: currentWindow.override_rate
4256
+ };
4257
+ }
4258
+ };
4259
+
2002
4260
  // src/index.ts
2003
4261
  var DEFAULT_RULES = [
2004
4262
  // --- HTTP Proxy: Dangerous outbound calls ---
@@ -2489,16 +4747,30 @@ var index_default = PolicyEngine;
2489
4747
  export {
2490
4748
  AdaptiveThresholdManager,
2491
4749
  AutoHardenEngine,
4750
+ ContextAccelerator,
4751
+ ContributionSettlementEngine,
2492
4752
  CostGateEnhancedEngine,
4753
+ DEFAULT_CA_CONFIG,
4754
+ DEFAULT_GE_CONFIG,
4755
+ DEFAULT_HARD_RULES,
2493
4756
  DEFAULT_RULES,
4757
+ DEFAULT_SCORING_WEIGHTS,
4758
+ DEFAULT_TSA_CONFIG,
2494
4759
  EvolutionChannelEngine,
2495
4760
  FeatureSwitchesManager,
4761
+ GovernanceEnhancer,
2496
4762
  MultiLevelBudgetEngine,
4763
+ OverrideDriftAnalyzer,
2497
4764
  PolicyEngine,
2498
4765
  PricingRulesEngine,
2499
4766
  RecalculationEngine,
2500
4767
  SOVR_FEATURE_SWITCHES,
4768
+ SelfConstraintEngine,
2501
4769
  SemanticDriftDetectorEngine,
4770
+ TimeSeriesAggregator,
4771
+ TrendAlertEngine,
4772
+ TwoPhaseRouter,
4773
+ ValuationModel,
2502
4774
  compileFromJSON,
2503
4775
  compileRuleSet,
2504
4776
  createAutoHardenEngine,