@sovr/engine 3.4.0 → 3.5.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,1206 @@ 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
+
2002
3202
  // src/index.ts
2003
3203
  var DEFAULT_RULES = [
2004
3204
  // --- HTTP Proxy: Dangerous outbound calls ---
@@ -2489,16 +3689,26 @@ var index_default = PolicyEngine;
2489
3689
  export {
2490
3690
  AdaptiveThresholdManager,
2491
3691
  AutoHardenEngine,
3692
+ ContextAccelerator,
2492
3693
  CostGateEnhancedEngine,
3694
+ DEFAULT_CA_CONFIG,
3695
+ DEFAULT_GE_CONFIG,
3696
+ DEFAULT_HARD_RULES,
2493
3697
  DEFAULT_RULES,
3698
+ DEFAULT_SCORING_WEIGHTS,
3699
+ DEFAULT_TSA_CONFIG,
2494
3700
  EvolutionChannelEngine,
2495
3701
  FeatureSwitchesManager,
3702
+ GovernanceEnhancer,
2496
3703
  MultiLevelBudgetEngine,
2497
3704
  PolicyEngine,
2498
3705
  PricingRulesEngine,
2499
3706
  RecalculationEngine,
2500
3707
  SOVR_FEATURE_SWITCHES,
2501
3708
  SemanticDriftDetectorEngine,
3709
+ TimeSeriesAggregator,
3710
+ TwoPhaseRouter,
3711
+ ValuationModel,
2502
3712
  compileFromJSON,
2503
3713
  compileRuleSet,
2504
3714
  createAutoHardenEngine,