nexus-agents 2.162.1 → 2.164.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/dist/{child-mcp-config-VXXBVJY3.js → child-mcp-config-Q3XDLAVG.js} +3 -3
  2. package/dist/{chunk-EZHYOWWO.js → chunk-2O4TMKUN.js} +2 -2
  3. package/dist/{chunk-DXEYJAGO.js → chunk-4WZWUONK.js} +2 -2
  4. package/dist/{chunk-HJZ3RES7.js → chunk-5HLHFSTH.js} +2 -2
  5. package/dist/{chunk-MJOHZFJP.js → chunk-6RIGFUZF.js} +15 -8
  6. package/dist/chunk-6RIGFUZF.js.map +1 -0
  7. package/dist/{chunk-7QWGLBCE.js → chunk-A57TGFAC.js} +5 -5
  8. package/dist/{chunk-NL7SZQPW.js → chunk-AXTD4TFK.js} +5 -1
  9. package/dist/{chunk-NL7SZQPW.js.map → chunk-AXTD4TFK.js.map} +1 -1
  10. package/dist/{chunk-RN5ISNYK.js → chunk-BE7MNC7B.js} +3 -3
  11. package/dist/{chunk-V7CIVDNO.js → chunk-BOE7RCND.js} +3 -3
  12. package/dist/{chunk-6ILEOLNW.js → chunk-FQNXEOL3.js} +2 -2
  13. package/dist/{chunk-FFH35IKD.js → chunk-FTYO3F2S.js} +3 -3
  14. package/dist/{chunk-23ZSFBBM.js → chunk-GATA7PDR.js} +9 -9
  15. package/dist/{chunk-YUAQRJRF.js → chunk-GMLPL5P2.js} +4 -4
  16. package/dist/{chunk-JB37LBSG.js → chunk-HLP36YBZ.js} +7 -7
  17. package/dist/{chunk-WP6L2PTV.js → chunk-IH7A6MNH.js} +7 -7
  18. package/dist/{chunk-ZGSAYLPE.js → chunk-JAGAZMUT.js} +2 -2
  19. package/dist/{chunk-DBHK6EDT.js → chunk-K54QBTB6.js} +2 -2
  20. package/dist/{chunk-THUX4X43.js → chunk-K6II2MEP.js} +3 -3
  21. package/dist/{chunk-EQJR6WMY.js → chunk-KCVJPSZF.js} +2 -2
  22. package/dist/{chunk-JYR6SBUB.js → chunk-KWYMR424.js} +4 -4
  23. package/dist/{chunk-TJJW4BGX.js → chunk-M2BRITA3.js} +4 -4
  24. package/dist/{chunk-XWAV3UC2.js → chunk-M5LNQ6DM.js} +5 -5
  25. package/dist/{chunk-WGDZFYP3.js → chunk-N6WRMZYA.js} +297 -38
  26. package/dist/chunk-N6WRMZYA.js.map +1 -0
  27. package/dist/{chunk-W3ZTVYZN.js → chunk-NSZVUPZH.js} +2 -2
  28. package/dist/{chunk-GJSJRJ6Y.js → chunk-NWIN6KOK.js} +2 -2
  29. package/dist/{chunk-VWV34IMA.js → chunk-P2BP7MJS.js} +2 -2
  30. package/dist/{chunk-754RDQAS.js → chunk-P67JWFPG.js} +2 -2
  31. package/dist/{chunk-RDYGX6MQ.js → chunk-RYUMBYYR.js} +2 -2
  32. package/dist/{chunk-JIXCOX7D.js → chunk-SOWWAZ5C.js} +3 -3
  33. package/dist/{chunk-MJLN336B.js → chunk-UFWFHXDP.js} +3 -3
  34. package/dist/{chunk-ZIPTXCET.js → chunk-UGNUHVZP.js} +37 -37
  35. package/dist/{chunk-N67NSSQT.js → chunk-V2PFGYOM.js} +2 -2
  36. package/dist/{chunk-EVYIQ6N5.js → chunk-VGOPIPDV.js} +6 -6
  37. package/dist/{chunk-GMSYKYFA.js → chunk-WEIYBIV3.js} +2 -2
  38. package/dist/{chunk-U4KMGGGF.js → chunk-XYA7W5IQ.js} +2 -2
  39. package/dist/{chunk-6FWMNTPO.js → chunk-YLA7NOSK.js} +4 -4
  40. package/dist/{cli-circuit-breaker-B4RH2ELX.js → cli-circuit-breaker-G2P42V3H.js} +5 -5
  41. package/dist/cli.js +42 -42
  42. package/dist/{composite-router-4PYFIB2G.js → composite-router-NZPVPGSN.js} +3 -3
  43. package/dist/{consensus-vote-MM5YG3A5.js → consensus-vote-BTN37MRV.js} +16 -16
  44. package/dist/{context-retriever-YGPEAEPO.js → context-retriever-4SWGSINU.js} +9 -9
  45. package/dist/{doctor-deep-N576AXQE.js → doctor-deep-SYEDHAPX.js} +4 -4
  46. package/dist/{expert-bridge-YEE7F6TA.js → expert-bridge-6I34TGL7.js} +5 -5
  47. package/dist/factory-2BGHWTMC.js +15 -0
  48. package/dist/factory-KKRPWZ2A.js +22 -0
  49. package/dist/{improvement-review-5CZJIOEH.js → improvement-review-4RX6YUVD.js} +6 -6
  50. package/dist/index.d.ts +33 -0
  51. package/dist/index.js +31 -31
  52. package/dist/{init-opencode-R3PEVUUI.js → init-opencode-CXUIMAUP.js} +7 -7
  53. package/dist/issue-triage-4VXHABBD.js +18 -0
  54. package/dist/{learning-persistence-M3OTCCNI.js → learning-persistence-6D5GGT7D.js} +4 -2
  55. package/dist/{pr-reviewer-helpers-TOMMTISB.js → pr-reviewer-helpers-5BOB2KK4.js} +5 -5
  56. package/dist/{registry-command-EBGQHFT3.js → registry-command-4PDAF4XP.js} +3 -3
  57. package/dist/{repo-security-plan-CWJ3Z4CC.js → repo-security-plan-UMNW5FP6.js} +4 -4
  58. package/dist/{research-helpers-synthesize-UNLGVEND.js → research-helpers-synthesize-WYFSSCKX.js} +5 -5
  59. package/dist/{routing-memory-MTGIZZUO.js → routing-memory-IMJPZUKI.js} +3 -3
  60. package/dist/{session-memory-QGUF5TNU.js → session-memory-XKJFKI6R.js} +4 -4
  61. package/dist/{setup-command-6QSWQMBJ.js → setup-command-BPNKRJB2.js} +12 -12
  62. package/dist/setup-config-DHGAMWSI.js +11 -0
  63. package/dist/{setup-custom-api-QON4YKJ3.js → setup-custom-api-ESPG7XTX.js} +4 -4
  64. package/dist/{tool-memory-SNH4B5Q7.js → tool-memory-VGEHRRGE.js} +6 -6
  65. package/dist/{unified-registry-XEA2GEOZ.js → unified-registry-GLVZZGSC.js} +10 -10
  66. package/dist/{weather-report-QQ42WBNZ.js → weather-report-ITGCNNPJ.js} +3 -3
  67. package/package.json +1 -1
  68. package/dist/chunk-MJOHZFJP.js.map +0 -1
  69. package/dist/chunk-WGDZFYP3.js.map +0 -1
  70. package/dist/factory-NQEQY7NV.js +0 -15
  71. package/dist/factory-TWK2UKNH.js +0 -22
  72. package/dist/issue-triage-Y35K4BUV.js +0 -18
  73. package/dist/setup-config-4TGGV7AQ.js +0 -11
  74. /package/dist/{child-mcp-config-VXXBVJY3.js.map → child-mcp-config-Q3XDLAVG.js.map} +0 -0
  75. /package/dist/{chunk-EZHYOWWO.js.map → chunk-2O4TMKUN.js.map} +0 -0
  76. /package/dist/{chunk-DXEYJAGO.js.map → chunk-4WZWUONK.js.map} +0 -0
  77. /package/dist/{chunk-HJZ3RES7.js.map → chunk-5HLHFSTH.js.map} +0 -0
  78. /package/dist/{chunk-7QWGLBCE.js.map → chunk-A57TGFAC.js.map} +0 -0
  79. /package/dist/{chunk-RN5ISNYK.js.map → chunk-BE7MNC7B.js.map} +0 -0
  80. /package/dist/{chunk-V7CIVDNO.js.map → chunk-BOE7RCND.js.map} +0 -0
  81. /package/dist/{chunk-6ILEOLNW.js.map → chunk-FQNXEOL3.js.map} +0 -0
  82. /package/dist/{chunk-FFH35IKD.js.map → chunk-FTYO3F2S.js.map} +0 -0
  83. /package/dist/{chunk-23ZSFBBM.js.map → chunk-GATA7PDR.js.map} +0 -0
  84. /package/dist/{chunk-YUAQRJRF.js.map → chunk-GMLPL5P2.js.map} +0 -0
  85. /package/dist/{chunk-JB37LBSG.js.map → chunk-HLP36YBZ.js.map} +0 -0
  86. /package/dist/{chunk-WP6L2PTV.js.map → chunk-IH7A6MNH.js.map} +0 -0
  87. /package/dist/{chunk-ZGSAYLPE.js.map → chunk-JAGAZMUT.js.map} +0 -0
  88. /package/dist/{chunk-DBHK6EDT.js.map → chunk-K54QBTB6.js.map} +0 -0
  89. /package/dist/{chunk-THUX4X43.js.map → chunk-K6II2MEP.js.map} +0 -0
  90. /package/dist/{chunk-EQJR6WMY.js.map → chunk-KCVJPSZF.js.map} +0 -0
  91. /package/dist/{chunk-JYR6SBUB.js.map → chunk-KWYMR424.js.map} +0 -0
  92. /package/dist/{chunk-TJJW4BGX.js.map → chunk-M2BRITA3.js.map} +0 -0
  93. /package/dist/{chunk-XWAV3UC2.js.map → chunk-M5LNQ6DM.js.map} +0 -0
  94. /package/dist/{chunk-W3ZTVYZN.js.map → chunk-NSZVUPZH.js.map} +0 -0
  95. /package/dist/{chunk-GJSJRJ6Y.js.map → chunk-NWIN6KOK.js.map} +0 -0
  96. /package/dist/{chunk-VWV34IMA.js.map → chunk-P2BP7MJS.js.map} +0 -0
  97. /package/dist/{chunk-754RDQAS.js.map → chunk-P67JWFPG.js.map} +0 -0
  98. /package/dist/{chunk-RDYGX6MQ.js.map → chunk-RYUMBYYR.js.map} +0 -0
  99. /package/dist/{chunk-JIXCOX7D.js.map → chunk-SOWWAZ5C.js.map} +0 -0
  100. /package/dist/{chunk-MJLN336B.js.map → chunk-UFWFHXDP.js.map} +0 -0
  101. /package/dist/{chunk-ZIPTXCET.js.map → chunk-UGNUHVZP.js.map} +0 -0
  102. /package/dist/{chunk-N67NSSQT.js.map → chunk-V2PFGYOM.js.map} +0 -0
  103. /package/dist/{chunk-EVYIQ6N5.js.map → chunk-VGOPIPDV.js.map} +0 -0
  104. /package/dist/{chunk-GMSYKYFA.js.map → chunk-WEIYBIV3.js.map} +0 -0
  105. /package/dist/{chunk-U4KMGGGF.js.map → chunk-XYA7W5IQ.js.map} +0 -0
  106. /package/dist/{chunk-6FWMNTPO.js.map → chunk-YLA7NOSK.js.map} +0 -0
  107. /package/dist/{cli-circuit-breaker-B4RH2ELX.js.map → cli-circuit-breaker-G2P42V3H.js.map} +0 -0
  108. /package/dist/{composite-router-4PYFIB2G.js.map → composite-router-NZPVPGSN.js.map} +0 -0
  109. /package/dist/{consensus-vote-MM5YG3A5.js.map → consensus-vote-BTN37MRV.js.map} +0 -0
  110. /package/dist/{context-retriever-YGPEAEPO.js.map → context-retriever-4SWGSINU.js.map} +0 -0
  111. /package/dist/{doctor-deep-N576AXQE.js.map → doctor-deep-SYEDHAPX.js.map} +0 -0
  112. /package/dist/{expert-bridge-YEE7F6TA.js.map → expert-bridge-6I34TGL7.js.map} +0 -0
  113. /package/dist/{factory-NQEQY7NV.js.map → factory-2BGHWTMC.js.map} +0 -0
  114. /package/dist/{factory-TWK2UKNH.js.map → factory-KKRPWZ2A.js.map} +0 -0
  115. /package/dist/{improvement-review-5CZJIOEH.js.map → improvement-review-4RX6YUVD.js.map} +0 -0
  116. /package/dist/{init-opencode-R3PEVUUI.js.map → init-opencode-CXUIMAUP.js.map} +0 -0
  117. /package/dist/{issue-triage-Y35K4BUV.js.map → issue-triage-4VXHABBD.js.map} +0 -0
  118. /package/dist/{learning-persistence-M3OTCCNI.js.map → learning-persistence-6D5GGT7D.js.map} +0 -0
  119. /package/dist/{pr-reviewer-helpers-TOMMTISB.js.map → pr-reviewer-helpers-5BOB2KK4.js.map} +0 -0
  120. /package/dist/{registry-command-EBGQHFT3.js.map → registry-command-4PDAF4XP.js.map} +0 -0
  121. /package/dist/{repo-security-plan-CWJ3Z4CC.js.map → repo-security-plan-UMNW5FP6.js.map} +0 -0
  122. /package/dist/{research-helpers-synthesize-UNLGVEND.js.map → research-helpers-synthesize-WYFSSCKX.js.map} +0 -0
  123. /package/dist/{routing-memory-MTGIZZUO.js.map → routing-memory-IMJPZUKI.js.map} +0 -0
  124. /package/dist/{session-memory-QGUF5TNU.js.map → session-memory-XKJFKI6R.js.map} +0 -0
  125. /package/dist/{setup-command-6QSWQMBJ.js.map → setup-command-BPNKRJB2.js.map} +0 -0
  126. /package/dist/{setup-config-4TGGV7AQ.js.map → setup-config-DHGAMWSI.js.map} +0 -0
  127. /package/dist/{setup-custom-api-QON4YKJ3.js.map → setup-custom-api-ESPG7XTX.js.map} +0 -0
  128. /package/dist/{tool-memory-SNH4B5Q7.js.map → tool-memory-VGEHRRGE.js.map} +0 -0
  129. /package/dist/{unified-registry-XEA2GEOZ.js.map → unified-registry-GLVZZGSC.js.map} +0 -0
  130. /package/dist/{weather-report-QQ42WBNZ.js.map → weather-report-ITGCNNPJ.js.map} +0 -0
@@ -1,8 +1,9 @@
1
1
  import {
2
2
  ensureLearningDir,
3
3
  getDecisionCostFile,
4
+ getModelSelectionShadowFile,
4
5
  isPersistenceEnabled
5
- } from "./chunk-NL7SZQPW.js";
6
+ } from "./chunk-AXTD4TFK.js";
6
7
  import {
7
8
  nexusDataPath
8
9
  } from "./chunk-DHVMSIT5.js";
@@ -1499,6 +1500,12 @@ var DEFAULT_MODEL_PER_CLI = {
1499
1500
  codex: "gpt-5.5",
1500
1501
  opencode: "opencode-default"
1501
1502
  };
1503
+ var STATIC_CLI_COST_PER_1M = {
1504
+ claude: { input: 3, output: 15 },
1505
+ gemini: { input: 0.075, output: 0.3 },
1506
+ codex: { input: 2.5, output: 10 },
1507
+ opencode: { input: 2, output: 8 }
1508
+ };
1502
1509
 
1503
1510
  // src/config/model-derivation.ts
1504
1511
  var DEFAULT_ENTRY = {
@@ -2612,7 +2619,7 @@ function setDefaultRegistry(registry) {
2612
2619
  }
2613
2620
  async function reloadDefaultRegistry() {
2614
2621
  globalRegistry = buildDefaultRegistry();
2615
- const { resetGlobalRegistry } = await import("./unified-registry-XEA2GEOZ.js");
2622
+ const { resetGlobalRegistry } = await import("./unified-registry-GLVZZGSC.js");
2616
2623
  resetGlobalRegistry();
2617
2624
  return globalRegistry;
2618
2625
  }
@@ -2642,6 +2649,14 @@ function getDefaultModelForCli(cli) {
2642
2649
  if (registry === void 0) return staticDefault;
2643
2650
  return registry.getEntry(staticDefault).id;
2644
2651
  }
2652
+ function resolveModelCostPer1M(modelId, fallback) {
2653
+ const pricing = getModelPricing(modelId);
2654
+ if (pricing === void 0) return fallback;
2655
+ return { input: pricing.inputPer1M, output: pricing.outputPer1M };
2656
+ }
2657
+ function resolveCliCostPer1M(cli) {
2658
+ return resolveModelCostPer1M(getDefaultModelForCli(cli), STATIC_CLI_COST_PER_1M[cli]);
2659
+ }
2645
2660
  function getCliModelName(modelId) {
2646
2661
  const entry = lookupInTree(modelId);
2647
2662
  return entry?.cliModelName ?? entry?.cliAlias ?? modelId;
@@ -2692,9 +2707,10 @@ function buildTopsisProfiles() {
2692
2707
  for (const [cli, modelId] of Object.entries(DEFAULT_MODEL_PER_CLI)) {
2693
2708
  const entry = byId.get(modelId);
2694
2709
  const q = entry?.qualityScores;
2695
- const p = entry?.pricing;
2696
- if (entry === void 0 || q === void 0 || p === void 0) continue;
2710
+ if (entry === void 0 || q === void 0) continue;
2697
2711
  if (entry.contextWindow === void 0) continue;
2712
+ const p = entry.pricing;
2713
+ const price = p !== void 0 ? { input: p.inputPer1M, output: p.outputPer1M } : STATIC_CLI_COST_PER_1M[cli];
2698
2714
  profiles.push({
2699
2715
  cliName: cli,
2700
2716
  capabilities: {
@@ -2704,8 +2720,8 @@ function buildTopsisProfiles() {
2704
2720
  speed: q.speed,
2705
2721
  cost: q.cost
2706
2722
  },
2707
- costPerMillionInput: p.inputPer1M,
2708
- costPerMillionOutput: p.outputPer1M,
2723
+ costPerMillionInput: price.input,
2724
+ costPerMillionOutput: price.output,
2709
2725
  averageLatencyMs: cliAvgLatency()[cli],
2710
2726
  qualityScore: (q.reasoning + q.codeGeneration) / 2
2711
2727
  });
@@ -5356,17 +5372,11 @@ var RoutingMemoryError = class extends Error {
5356
5372
  };
5357
5373
 
5358
5374
  // src/cli-adapters/budget-utils.ts
5359
- var TOKEN_COSTS = {
5360
- claude: { input: 3, output: 15 },
5361
- gemini: { input: 0.075, output: 0.3 },
5362
- codex: { input: 2.5, output: 10 },
5363
- opencode: { input: 2, output: 8 }
5364
- };
5365
5375
  function estimateTokens2(content) {
5366
5376
  return Math.ceil(content.length / 4);
5367
5377
  }
5368
5378
  function estimateCost(model, inputTokens, outputTokens) {
5369
- const costs = TOKEN_COSTS[model];
5379
+ const costs = resolveCliCostPer1M(model);
5370
5380
  const inputCost = inputTokens / 1e6 * costs.input;
5371
5381
  const outputCost = outputTokens / 1e6 * costs.output;
5372
5382
  return inputCost + outputCost;
@@ -9018,14 +9028,14 @@ function detectSuccessPatterns(groups, threshold) {
9018
9028
  const patterns = [];
9019
9029
  for (const g of groups) {
9020
9030
  const successCount = g.outcomes.filter((o) => o.success).length;
9021
- const successRate2 = successCount / g.outcomes.length;
9022
- if (successRate2 >= threshold) {
9031
+ const successRate3 = successCount / g.outcomes.length;
9032
+ if (successRate3 >= threshold) {
9023
9033
  patterns.push({
9024
9034
  cli: g.cli,
9025
9035
  category: g.category,
9026
9036
  patternType: "success-rate",
9027
9037
  action: "boost",
9028
- metric: successRate2,
9038
+ metric: successRate3,
9029
9039
  observationCount: g.outcomes.length
9030
9040
  });
9031
9041
  }
@@ -9218,10 +9228,10 @@ var StrategyDistiller = class {
9218
9228
  }
9219
9229
  /** Convert rule metrics into ModelPerformance for RoutingMemory. */
9220
9230
  ruleToPerformance(rule) {
9221
- const successRate2 = rule.patternType === "success-rate" ? rule.metric : 1 - rule.metric;
9231
+ const successRate3 = rule.patternType === "success-rate" ? rule.metric : 1 - rule.metric;
9222
9232
  return {
9223
9233
  avgQuality: rule.confidence,
9224
- successRate: Math.max(0, Math.min(1, successRate2)),
9234
+ successRate: Math.max(0, Math.min(1, successRate3)),
9225
9235
  avgLatencyMs: 0,
9226
9236
  avgTokens: 0,
9227
9237
  observations: rule.observationCount
@@ -12419,9 +12429,9 @@ function getAdaptiveBonus(cli, category, config) {
12419
12429
  const cliName = cli;
12420
12430
  const outcomes = queryWithLookback(store, cliName, category, cfg);
12421
12431
  if (outcomes.length < cfg.coldStartThreshold) return 0;
12422
- const successRate2 = outcomes.filter((o) => o.success).length / outcomes.length;
12432
+ const successRate3 = outcomes.filter((o) => o.success).length / outcomes.length;
12423
12433
  const FIXED_BASELINE = 0.7;
12424
- const delta = successRate2 - FIXED_BASELINE;
12434
+ const delta = successRate3 - FIXED_BASELINE;
12425
12435
  const FULL_CONFIDENCE_SAMPLES2 = 50;
12426
12436
  const windowedConfidence = Math.min(1, outcomes.length / FULL_CONFIDENCE_SAMPLES2);
12427
12437
  const maxBonus = Math.min(cfg.maxBonusAdjustment * windowedConfidence, cfg.maxBonusAdjustment);
@@ -12696,17 +12706,17 @@ function buildExpertPerformance() {
12696
12706
  const successes = outcomes.filter((o) => o.success).length;
12697
12707
  const totalDuration = outcomes.reduce((s, o) => s + o.durationMs, 0);
12698
12708
  const dominantErrorPattern = findDominantError(outcomes.filter((o) => !o.success));
12699
- const successRate2 = successes / outcomes.length;
12709
+ const successRate3 = successes / outcomes.length;
12700
12710
  const consecutiveFailures = countTrailingFailures(outcomes);
12701
12711
  const lastSuccess = [...outcomes].reverse().find((o) => o.success);
12702
12712
  const lastSuccessAt = lastSuccess !== void 0 ? new Date(lastSuccess.timestamp).toISOString() : void 0;
12703
12713
  entries.push({
12704
12714
  role,
12705
12715
  totalTasks: outcomes.length,
12706
- successRate: successRate2,
12716
+ successRate: successRate3,
12707
12717
  avgDurationMs: Math.round(totalDuration / outcomes.length),
12708
12718
  consecutiveFailures,
12709
- degraded: successRate2 < 0.5,
12719
+ degraded: successRate3 < 0.5,
12710
12720
  ...dominantErrorPattern !== void 0 ? { dominantErrorPattern } : {},
12711
12721
  ...lastSuccessAt !== void 0 ? { lastSuccessAt } : {}
12712
12722
  });
@@ -13686,6 +13696,169 @@ function resolveModelForTier(cliName, tier, opts = {}) {
13686
13696
  return scored[0]?.id ?? getDefaultModelForCli(cliName);
13687
13697
  }
13688
13698
 
13699
+ // src/cli-adapters/model-selection-shadow.ts
13700
+ import { appendFileSync as appendFileSync2, existsSync as existsSync7, readFileSync as readFileSync6 } from "fs";
13701
+ import { z as z19 } from "zod";
13702
+ function isRouteModelShadowEnabled() {
13703
+ return process.env["NEXUS_ROUTE_MODEL_SHADOW"] === "1" && isPersistenceEnabled();
13704
+ }
13705
+ var MODEL_SELECTION_SHADOW_SCHEMA_VERSION = 1;
13706
+ var READ_LOOKBACK_DAYS = 30;
13707
+ var READ_LOOKBACK_MS = READ_LOOKBACK_DAYS * 24 * 60 * 60 * 1e3;
13708
+ var ModelTierSchema = z19.enum(["fast", "balanced", "powerful"]);
13709
+ var ModelSelectionShadowRecordSchema = z19.object({
13710
+ schema: z19.literal(MODEL_SELECTION_SHADOW_SCHEMA_VERSION),
13711
+ /** ISO timestamp of the outcome join. */
13712
+ timestamp: z19.string().min(1),
13713
+ /** Display slot of the routed CLI (api:* arms collapse to their slot). */
13714
+ cli: z19.string().min(1).max(40),
13715
+ /** Difficulty tier the ZeroRouter computed for the task. */
13716
+ tier: ModelTierSchema,
13717
+ /** Model the live decision actually used (or the CLI default when route-time selection is off). */
13718
+ actualModel: z19.string().min(1).max(120),
13719
+ /** Model `resolveModelForTier` WOULD have picked (never executed). */
13720
+ shadowModel: z19.string().min(1).max(120),
13721
+ /** Whether shadow and actual agree. */
13722
+ agree: z19.boolean(),
13723
+ /** Whether the actually-executed decision succeeded. */
13724
+ success: z19.boolean(),
13725
+ /** Measured cost of the actual execution, when available (unmeasured today on the routing path). */
13726
+ costUsd: z19.number().nonnegative().optional()
13727
+ });
13728
+ function computeModelSelectionShadow(cli, tier, actualModel) {
13729
+ const shadowModel = resolveModelForTier(cli, tier);
13730
+ const actual = actualModel ?? getDefaultModelForCli(cli);
13731
+ return { cli, tier, actualModel: actual, shadowModel, agree: shadowModel === actual };
13732
+ }
13733
+ var shadowFailureCount = 0;
13734
+ function recordModelSelectionShadowFailure() {
13735
+ return ++shadowFailureCount;
13736
+ }
13737
+ var persistLogger = createLogger({ component: "ModelSelectionShadow" });
13738
+ function persistModelSelectionShadowRecord(record) {
13739
+ try {
13740
+ ensureLearningDir();
13741
+ appendFileSync2(getModelSelectionShadowFile(), JSON.stringify(record) + "\n", "utf-8");
13742
+ } catch (err2) {
13743
+ recordModelSelectionShadowFailure();
13744
+ persistLogger.warn("Failed to persist model-selection shadow record (ignored)", {
13745
+ error: getErrorMessage(err2)
13746
+ });
13747
+ }
13748
+ }
13749
+ function readModelSelectionShadowRecords() {
13750
+ const file = getModelSelectionShadowFile();
13751
+ if (!existsSync7(file)) return [];
13752
+ const records = [];
13753
+ try {
13754
+ const cutoff = Date.now() - READ_LOOKBACK_MS;
13755
+ const lines = readFileSync6(file, "utf-8").split("\n").filter((l) => l.trim().length > 0);
13756
+ for (const line of lines) {
13757
+ let parsed;
13758
+ try {
13759
+ parsed = JSON.parse(line);
13760
+ } catch {
13761
+ continue;
13762
+ }
13763
+ const result = ModelSelectionShadowRecordSchema.safeParse(parsed);
13764
+ if (!result.success) continue;
13765
+ const ts = Date.parse(result.data.timestamp);
13766
+ if (Number.isNaN(ts) || ts < cutoff) continue;
13767
+ records.push(result.data);
13768
+ }
13769
+ } catch (err2) {
13770
+ persistLogger.warn("Failed to read model-selection shadow records (ignored)", {
13771
+ error: getErrorMessage(err2)
13772
+ });
13773
+ }
13774
+ return records;
13775
+ }
13776
+
13777
+ // src/cli-adapters/model-selection-readiness.ts
13778
+ var DEFAULT_MODEL_SELECTION_READINESS_CONFIG = {
13779
+ minDivergingDecisions: 50,
13780
+ minSuccessDelta: 0.05,
13781
+ minCostSamples: 10
13782
+ };
13783
+ function successRate2(t) {
13784
+ return t.count === 0 ? 0 : t.successes / t.count;
13785
+ }
13786
+ function summarizeModelSelectionShadow(records) {
13787
+ const agree = { count: 0, successes: 0 };
13788
+ const diverge = { count: 0, successes: 0 };
13789
+ let costSamples = 0;
13790
+ let costTotal = 0;
13791
+ for (const r of records) {
13792
+ const cohort = r.agree ? agree : diverge;
13793
+ cohort.count++;
13794
+ if (r.success) cohort.successes++;
13795
+ if (r.costUsd !== void 0) {
13796
+ costSamples++;
13797
+ costTotal += r.costUsd;
13798
+ }
13799
+ }
13800
+ const agreeSuccessRate = successRate2(agree);
13801
+ const divergeSuccessRate = successRate2(diverge);
13802
+ const successDelta = agree.count === 0 || diverge.count === 0 ? 0 : agreeSuccessRate - divergeSuccessRate;
13803
+ return {
13804
+ total: records.length,
13805
+ agreeing: agree.count,
13806
+ diverging: diverge.count,
13807
+ agreeSuccessRate,
13808
+ divergeSuccessRate,
13809
+ successDelta,
13810
+ costSamples,
13811
+ meanCostUsd: costSamples === 0 ? 0 : costTotal / costSamples
13812
+ };
13813
+ }
13814
+ function evaluateModelSelectionReadiness(records, config = DEFAULT_MODEL_SELECTION_READINESS_CONFIG) {
13815
+ const s = summarizeModelSelectionShadow(records);
13816
+ const criteria = [
13817
+ {
13818
+ name: "volume",
13819
+ met: s.diverging >= config.minDivergingDecisions,
13820
+ detail: `${String(s.diverging)} shadow-diverging decisions (need \u2265 ${String(config.minDivergingDecisions)})`
13821
+ },
13822
+ {
13823
+ name: "success-delta",
13824
+ met: s.successDelta >= config.minSuccessDelta,
13825
+ detail: `agree\u2212diverge success delta ${s.successDelta.toFixed(2)} (agree ${s.agreeSuccessRate.toFixed(2)} over ${String(s.agreeing)}, diverge ${s.divergeSuccessRate.toFixed(2)} over ${String(s.diverging)}; need \u2265 ${config.minSuccessDelta.toFixed(2)})`
13826
+ },
13827
+ {
13828
+ name: "cost-measured",
13829
+ met: s.costSamples >= config.minCostSamples,
13830
+ detail: `${String(s.costSamples)} decisions with measured costUsd, mean $${s.meanCostUsd.toFixed(4)} (need \u2265 ${String(config.minCostSamples)})`
13831
+ }
13832
+ ];
13833
+ const blockers = criteria.filter((c) => !c.met).map((c) => c.name);
13834
+ return { ready: blockers.length === 0, criteria, blockers };
13835
+ }
13836
+ var readinessLogged = false;
13837
+ function logModelSelectionReadinessOnce(logger14) {
13838
+ if (readinessLogged) return;
13839
+ readinessLogged = true;
13840
+ try {
13841
+ const records = readModelSelectionShadowRecords();
13842
+ const summary = summarizeModelSelectionShadow(records);
13843
+ const verdict = evaluateModelSelectionReadiness(records);
13844
+ logger14.info("route-model-selection shadow readiness", {
13845
+ ready: verdict.ready,
13846
+ blockers: verdict.blockers,
13847
+ total: summary.total,
13848
+ diverging: summary.diverging,
13849
+ successDelta: summary.successDelta,
13850
+ agreeSuccessRate: summary.agreeSuccessRate,
13851
+ divergeSuccessRate: summary.divergeSuccessRate,
13852
+ costSamples: summary.costSamples,
13853
+ meanCostUsd: summary.meanCostUsd
13854
+ });
13855
+ } catch (err2) {
13856
+ logger14.warn("route-model-selection readiness signal failed (non-fatal)", {
13857
+ error: getErrorMessage(err2)
13858
+ });
13859
+ }
13860
+ }
13861
+
13689
13862
  // src/cli-adapters/composite-router-outcome.ts
13690
13863
  var sharedAnalyzer3 = createSharedTaskAnalyzer();
13691
13864
  function recordBanditOutcome(cliName, task, reward, deps) {
@@ -13894,6 +14067,13 @@ var CompositeRouter = class _CompositeRouter {
13894
14067
  lastRoutedTask;
13895
14068
  // Track last traceId for metrics correlation (Issue #559)
13896
14069
  lastTraceId;
14070
+ /**
14071
+ * Pending model-selection shadow comparison awaiting its outcome join
14072
+ * (#4197). Keyed by task content like {@link lastRoutedTask}; persisted with
14073
+ * `success` when `recordDifficultyOutcome` reports the matching outcome.
14074
+ * The task content itself is never persisted.
14075
+ */
14076
+ pendingModelShadow;
13897
14077
  constructor(adapters, config, logger14) {
13898
14078
  const {
13899
14079
  preferenceRouterConfig,
@@ -14078,7 +14258,7 @@ var CompositeRouter = class _CompositeRouter {
14078
14258
  */
14079
14259
  async consultUnifiedContext(task) {
14080
14260
  try {
14081
- const { getContextForTask, inferTaskCategory } = await import("./context-retriever-YGPEAEPO.js");
14261
+ const { getContextForTask, inferTaskCategory } = await import("./context-retriever-4SWGSINU.js");
14082
14262
  const ctx = await getContextForTask({
14083
14263
  task: task.content,
14084
14264
  category: inferTaskCategory(task.content),
@@ -14202,12 +14382,16 @@ var CompositeRouter = class _CompositeRouter {
14202
14382
  return pipelineResult;
14203
14383
  }
14204
14384
  this.trackLastRoutedTask(task, pipelineResult.value);
14205
- return this.buildRoutingDecision({
14385
+ const decisionResult = this.buildRoutingDecision({
14206
14386
  ...pipelineResult.value,
14207
14387
  taskProfile,
14208
14388
  stagesExecuted,
14209
14389
  startTime
14210
14390
  });
14391
+ if (decisionResult.ok) {
14392
+ this.trackModelSelectionShadow(task, decisionResult.value);
14393
+ }
14394
+ return decisionResult;
14211
14395
  } catch (error) {
14212
14396
  return this.handleRoutingError(error, stagesExecuted);
14213
14397
  }
@@ -14293,6 +14477,80 @@ var CompositeRouter = class _CompositeRouter {
14293
14477
  };
14294
14478
  }
14295
14479
  }
14480
+ /**
14481
+ * Compute the model-selection SHADOW comparison for a routed decision
14482
+ * (#4197): what `resolveModelForTier` WOULD have picked vs the model the
14483
+ * decision actually carries (or the CLI default the adapter will resolve).
14484
+ * Held pending until `recordDifficultyOutcome` supplies the outcome, then
14485
+ * persisted to the dedicated shadow log. Gated behind
14486
+ * `NEXUS_ROUTE_MODEL_SHADOW=1` (default OFF). NEVER affects the live
14487
+ * decision — any failure increments the shadow-failure counter and is
14488
+ * logged, not thrown into the routing path.
14489
+ *
14490
+ * Tasks with a PINNED model (`CliTask.model`) are SKIPPED entirely: the
14491
+ * adapter executes the pinned model (base-adapter), not the CLI default the
14492
+ * comparison would otherwise assume, so a pinned run says nothing about the
14493
+ * tier selector — sampling it would mislabel the agree/diverge cohorts and
14494
+ * pad the volume criterion with garbage (#4218 review).
14495
+ */
14496
+ trackModelSelectionShadow(task, decision) {
14497
+ try {
14498
+ if (!isRouteModelShadowEnabled() || decision.difficultyTier === void 0) return;
14499
+ if (task.model !== void 0) return;
14500
+ logModelSelectionReadinessOnce(this.logger);
14501
+ const comparison = computeModelSelectionShadow(
14502
+ routingArmDisplaySlot(decision.cliName),
14503
+ decision.difficultyTier,
14504
+ decision.model
14505
+ );
14506
+ this.pendingModelShadow = { ...comparison, taskContent: task.content };
14507
+ this.logger.debug("Model-selection shadow computed (#4197)", {
14508
+ cli: comparison.cli,
14509
+ tier: comparison.tier,
14510
+ actualModel: comparison.actualModel,
14511
+ shadowModel: comparison.shadowModel,
14512
+ agree: comparison.agree
14513
+ });
14514
+ } catch (error) {
14515
+ const failures = recordModelSelectionShadowFailure();
14516
+ this.logger.warn("Model-selection shadow failed (non-fatal, #4197)", {
14517
+ error: getErrorMessage(error),
14518
+ failures
14519
+ });
14520
+ }
14521
+ }
14522
+ /**
14523
+ * Join a pending model-selection shadow comparison with its task outcome and
14524
+ * persist the completed record (#4197). Matches by task content, mirroring
14525
+ * {@link getDifficultyInfo}'s lastRoutedTask join. `costUsd` is deliberately
14526
+ * absent: the routing outcome path measures no per-decision cost today, and
14527
+ * the readiness gate's cost criterion stays fail-closed until it does.
14528
+ * Exception-guarded — an outcome-join failure never breaks outcome recording.
14529
+ */
14530
+ joinModelSelectionShadowOutcome(task, success) {
14531
+ const pending = this.pendingModelShadow;
14532
+ if (pending === void 0) return;
14533
+ if (pending.taskContent !== task.content) return;
14534
+ this.pendingModelShadow = void 0;
14535
+ try {
14536
+ persistModelSelectionShadowRecord({
14537
+ schema: MODEL_SELECTION_SHADOW_SCHEMA_VERSION,
14538
+ timestamp: new Date(getTimeProvider().now()).toISOString(),
14539
+ cli: pending.cli,
14540
+ tier: pending.tier,
14541
+ actualModel: pending.actualModel,
14542
+ shadowModel: pending.shadowModel,
14543
+ agree: pending.agree,
14544
+ success
14545
+ });
14546
+ } catch (error) {
14547
+ const failures = recordModelSelectionShadowFailure();
14548
+ this.logger.warn("Model-selection shadow outcome join failed (non-fatal, #4197)", {
14549
+ error: getErrorMessage(error),
14550
+ failures
14551
+ });
14552
+ }
14553
+ }
14296
14554
  buildRoutingDecision(params) {
14297
14555
  const selectedAdapter = this.adapters.get(params.selectedCli);
14298
14556
  if (selectedAdapter === void 0) {
@@ -14373,6 +14631,7 @@ var CompositeRouter = class _CompositeRouter {
14373
14631
  }
14374
14632
  recordDifficultyOutcome(task, success, qualityScore) {
14375
14633
  recordZeroRouterOutcome(task, success, qualityScore, this.getOutcomeDependencies());
14634
+ this.joinModelSelectionShadowOutcome(task, success);
14376
14635
  }
14377
14636
  hasMinimumPreferenceData() {
14378
14637
  return hasMinimumPreferenceData(this.getOutcomeDependencies());
@@ -14503,7 +14762,7 @@ import { dirname as dirname5 } from "path";
14503
14762
  import { mkdirSync as mkdirSync3 } from "fs";
14504
14763
 
14505
14764
  // src/context/mobimem-types.ts
14506
- import { z as z19 } from "zod";
14765
+ import { z as z20 } from "zod";
14507
14766
  var DEFAULT_MOBIMEM_CONFIG = {
14508
14767
  dbPath: ":memory:",
14509
14768
  maxProfileEntries: 100,
@@ -14515,15 +14774,15 @@ var DEFAULT_MOBIMEM_CONFIG = {
14515
14774
  minExperienceSuccessRate: 0.7,
14516
14775
  autoEviction: true
14517
14776
  };
14518
- var MobiMemConfigSchema = z19.object({
14519
- dbPath: z19.string(),
14520
- maxProfileEntries: z19.number().int().positive().default(100),
14521
- maxExperiencePatterns: z19.number().int().positive().default(500),
14522
- maxActionCacheEntries: z19.number().int().positive().default(1e3),
14523
- actionCacheTtlMs: z19.number().int().positive().default(36e5),
14524
- minProfileConfidence: z19.number().min(0).max(1).default(0.6),
14525
- minExperienceSuccessRate: z19.number().min(0).max(1).default(0.7),
14526
- autoEviction: z19.boolean().default(true)
14777
+ var MobiMemConfigSchema = z20.object({
14778
+ dbPath: z20.string(),
14779
+ maxProfileEntries: z20.number().int().positive().default(100),
14780
+ maxExperiencePatterns: z20.number().int().positive().default(500),
14781
+ maxActionCacheEntries: z20.number().int().positive().default(1e3),
14782
+ actionCacheTtlMs: z20.number().int().positive().default(36e5),
14783
+ minProfileConfidence: z20.number().min(0).max(1).default(0.6),
14784
+ minExperienceSuccessRate: z20.number().min(0).max(1).default(0.7),
14785
+ autoEviction: z20.boolean().default(true)
14527
14786
  });
14528
14787
 
14529
14788
  // src/context/mobimem-impl.ts
@@ -14543,9 +14802,9 @@ function generatePatternKey(taskType, actionSequence, contextSignature) {
14543
14802
  function hashInput(input) {
14544
14803
  return createHash2("sha256").update(JSON.stringify(input)).digest("hex");
14545
14804
  }
14546
- function calculatePatternScore(successRate2, contextMatches, attemptCount) {
14805
+ function calculatePatternScore(successRate3, contextMatches, attemptCount) {
14547
14806
  const contextMatch = contextMatches ? 1 : 0.5;
14548
- return successRate2 * contextMatch * Math.log10(attemptCount + 1);
14807
+ return successRate3 * contextMatch * Math.log10(attemptCount + 1);
14549
14808
  }
14550
14809
  function computeSuccessRate(successCount, attemptCount) {
14551
14810
  return attemptCount > 0 ? successCount / attemptCount : 0;
@@ -15588,4 +15847,4 @@ export {
15588
15847
  AgentCapability,
15589
15848
  OrchestratorError
15590
15849
  };
15591
- //# sourceMappingURL=chunk-WGDZFYP3.js.map
15850
+ //# sourceMappingURL=chunk-N6WRMZYA.js.map