nexus-agents 8.89.3 → 8.90.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 (132) hide show
  1. package/dist/{ast-rule-runner-AMZCLKRV.js → ast-rule-runner-SD665GB7.js} +3 -3
  2. package/dist/{child-mcp-config-7DSYI325.js → child-mcp-config-BG3QNALD.js} +2 -2
  3. package/dist/{chunk-5UCGVAEZ.js → chunk-2N7XWZPW.js} +4 -4
  4. package/dist/{chunk-BJXSG5YL.js → chunk-4KMNLWAC.js} +4 -4
  5. package/dist/{chunk-QX42N7TE.js → chunk-4PMYNW3G.js} +2 -2
  6. package/dist/{chunk-3PQIIMHN.js → chunk-5GI6Z5VE.js} +63 -23
  7. package/dist/chunk-5GI6Z5VE.js.map +1 -0
  8. package/dist/{chunk-TLFZPJOY.js → chunk-C6Q2NUIH.js} +4 -4
  9. package/dist/{chunk-OQB3XC5O.js → chunk-EZNIYU5T.js} +7 -2
  10. package/dist/{chunk-OQB3XC5O.js.map → chunk-EZNIYU5T.js.map} +1 -1
  11. package/dist/{chunk-NZN3SWSZ.js → chunk-F2H3MCEV.js} +32 -8
  12. package/dist/{chunk-NZN3SWSZ.js.map → chunk-F2H3MCEV.js.map} +1 -1
  13. package/dist/{chunk-EKZ6V7SE.js → chunk-FPX55ON5.js} +7 -7
  14. package/dist/{chunk-KQE7EFBF.js → chunk-HBXAYO3C.js} +5 -5
  15. package/dist/{chunk-JYKRCQSU.js → chunk-HD3FTCV4.js} +5 -5
  16. package/dist/{chunk-AXBEG6VW.js → chunk-HPE3UQLT.js} +4 -4
  17. package/dist/{chunk-UDWZJY3V.js → chunk-HUCILYVO.js} +2 -2
  18. package/dist/{chunk-MS5VFQE3.js → chunk-I3SCYF5R.js} +27 -13
  19. package/dist/chunk-I3SCYF5R.js.map +1 -0
  20. package/dist/{chunk-4IV25ZUM.js → chunk-IDASIJE6.js} +2 -2
  21. package/dist/{chunk-MYZGLD7Y.js → chunk-J3HT4YDM.js} +11 -11
  22. package/dist/{chunk-V76TXFRC.js → chunk-JG4DC2TZ.js} +5 -3
  23. package/dist/chunk-JG4DC2TZ.js.map +1 -0
  24. package/dist/{chunk-SCXCJSTT.js → chunk-JWHIFEI3.js} +2 -2
  25. package/dist/{chunk-LZNYQYDU.js → chunk-KCWEMSU2.js} +2 -2
  26. package/dist/{chunk-6FFBX665.js → chunk-NFP7WQ22.js} +2 -2
  27. package/dist/{chunk-ILA4UGXI.js → chunk-NMZ5ZKEO.js} +2 -2
  28. package/dist/{chunk-LFFXUHX3.js → chunk-O3MSZUQM.js} +2 -2
  29. package/dist/{chunk-Z5VUMKF5.js → chunk-OJM75DDY.js} +3 -3
  30. package/dist/{chunk-KSCQRZJZ.js → chunk-OX4L42RL.js} +3 -3
  31. package/dist/{chunk-5GZ4UN5F.js → chunk-QCWY3GTZ.js} +2 -2
  32. package/dist/{chunk-PENEGF75.js → chunk-RMDG3OFS.js} +2 -2
  33. package/dist/{chunk-CG7OSI5P.js → chunk-S6FK47MZ.js} +2 -2
  34. package/dist/{chunk-XU2SJKFK.js → chunk-SMKBETIX.js} +2 -2
  35. package/dist/{chunk-ZTTBGME2.js → chunk-VI5TF3KF.js} +2 -2
  36. package/dist/{chunk-JYZNPK62.js → chunk-VIDSDHSQ.js} +8 -8
  37. package/dist/{chunk-3IBALX36.js → chunk-VLBXRQIA.js} +2 -2
  38. package/dist/{chunk-TRCYUQ4M.js → chunk-WDGEJIWW.js} +2 -2
  39. package/dist/{chunk-NNHRMVZU.js → chunk-XEYZRY4C.js} +49 -48
  40. package/dist/{chunk-NNHRMVZU.js.map → chunk-XEYZRY4C.js.map} +1 -1
  41. package/dist/{chunk-XJAGCUMD.js → chunk-ZP7LYABJ.js} +2 -2
  42. package/dist/{chunk-VM4HZT3B.js → chunk-ZVHJMROO.js} +4 -4
  43. package/dist/{cli-circuit-breaker-MCQG3PQK.js → cli-circuit-breaker-GOH6U32K.js} +3 -3
  44. package/dist/cli.d.ts +1 -1
  45. package/dist/cli.js +89 -85
  46. package/dist/cli.js.map +1 -1
  47. package/dist/{composite-router-QAYCHEFG.js → composite-router-RMEHBA3J.js} +2 -2
  48. package/dist/{consensus-vote-EIV5TDPY.js → consensus-vote-23XXJUDP.js} +17 -17
  49. package/dist/{consensus-vote-types-DhmacUE7.d.ts → consensus-vote-types-BdptpuQK.d.ts} +6 -0
  50. package/dist/{context-retriever-UXQCDDRY.js → context-retriever-TWAN22PW.js} +8 -8
  51. package/dist/{doctor-deep-QRLRDFM4.js → doctor-deep-HRZ77NYS.js} +4 -3
  52. package/dist/{doctor-live-CUKBQSSH.js → doctor-live-VGOABDSJ.js} +10 -10
  53. package/dist/{expert-bridge-ZG2IA3RJ.js → expert-bridge-PFR7ZNWK.js} +4 -4
  54. package/dist/{factory-RQEHJ4AW.js → factory-DJPYG3WR.js} +9 -9
  55. package/dist/{factory-FPNYFKXU.js → factory-JCVKAI7P.js} +5 -5
  56. package/dist/{improvement-review-CUBUAU2D.js → improvement-review-RKHKBKDU.js} +5 -5
  57. package/dist/index.d.ts +6 -2
  58. package/dist/index.js +31 -31
  59. package/dist/{init-opencode-4QECASK4.js → init-opencode-KTIHOYIU.js} +5 -5
  60. package/dist/issue-triage-OCTW33O3.js +16 -0
  61. package/dist/{pr-reviewer-helpers-RK5IT4US.js → pr-reviewer-helpers-V67TZHNM.js} +5 -5
  62. package/dist/{registry-command-MBZFFDWS.js → registry-command-43IZQOAP.js} +2 -2
  63. package/dist/{repo-security-plan-W76YLC2M.js → repo-security-plan-OXMSNBOS.js} +3 -3
  64. package/dist/{research-helpers-synthesize-GI5JFAQM.js → research-helpers-synthesize-IBBRMZEY.js} +4 -4
  65. package/dist/{routing-memory-WLCUOPDE.js → routing-memory-S3H3D6Z2.js} +2 -2
  66. package/dist/{session-memory-HU3XMQEH.js → session-memory-UO42CU2H.js} +3 -3
  67. package/dist/{setup-config-XA756R2O.js → setup-config-RGTKEVTD.js} +3 -3
  68. package/dist/{setup-custom-api-ETBGOE7O.js → setup-custom-api-PQ3PR2RR.js} +4 -4
  69. package/dist/setup-data-dir-AMMF6TWR.js +32 -0
  70. package/dist/{tool-memory-7XMQAQQM.js → tool-memory-ZMLCOQH6.js} +5 -5
  71. package/dist/{unified-registry-LR4OCUFI.js → unified-registry-HWBSGSOM.js} +10 -10
  72. package/dist/{weather-report-UYKQUSBB.js → weather-report-MZCA7Y6K.js} +2 -2
  73. package/package.json +1 -1
  74. package/dist/chunk-3PQIIMHN.js.map +0 -1
  75. package/dist/chunk-MS5VFQE3.js.map +0 -1
  76. package/dist/chunk-V76TXFRC.js.map +0 -1
  77. package/dist/issue-triage-BIEZBWV2.js +0 -16
  78. package/dist/setup-data-dir-XJWO2WOU.js +0 -32
  79. /package/dist/{ast-rule-runner-AMZCLKRV.js.map → ast-rule-runner-SD665GB7.js.map} +0 -0
  80. /package/dist/{child-mcp-config-7DSYI325.js.map → child-mcp-config-BG3QNALD.js.map} +0 -0
  81. /package/dist/{chunk-5UCGVAEZ.js.map → chunk-2N7XWZPW.js.map} +0 -0
  82. /package/dist/{chunk-BJXSG5YL.js.map → chunk-4KMNLWAC.js.map} +0 -0
  83. /package/dist/{chunk-QX42N7TE.js.map → chunk-4PMYNW3G.js.map} +0 -0
  84. /package/dist/{chunk-TLFZPJOY.js.map → chunk-C6Q2NUIH.js.map} +0 -0
  85. /package/dist/{chunk-EKZ6V7SE.js.map → chunk-FPX55ON5.js.map} +0 -0
  86. /package/dist/{chunk-KQE7EFBF.js.map → chunk-HBXAYO3C.js.map} +0 -0
  87. /package/dist/{chunk-JYKRCQSU.js.map → chunk-HD3FTCV4.js.map} +0 -0
  88. /package/dist/{chunk-AXBEG6VW.js.map → chunk-HPE3UQLT.js.map} +0 -0
  89. /package/dist/{chunk-UDWZJY3V.js.map → chunk-HUCILYVO.js.map} +0 -0
  90. /package/dist/{chunk-4IV25ZUM.js.map → chunk-IDASIJE6.js.map} +0 -0
  91. /package/dist/{chunk-MYZGLD7Y.js.map → chunk-J3HT4YDM.js.map} +0 -0
  92. /package/dist/{chunk-SCXCJSTT.js.map → chunk-JWHIFEI3.js.map} +0 -0
  93. /package/dist/{chunk-LZNYQYDU.js.map → chunk-KCWEMSU2.js.map} +0 -0
  94. /package/dist/{chunk-6FFBX665.js.map → chunk-NFP7WQ22.js.map} +0 -0
  95. /package/dist/{chunk-ILA4UGXI.js.map → chunk-NMZ5ZKEO.js.map} +0 -0
  96. /package/dist/{chunk-LFFXUHX3.js.map → chunk-O3MSZUQM.js.map} +0 -0
  97. /package/dist/{chunk-Z5VUMKF5.js.map → chunk-OJM75DDY.js.map} +0 -0
  98. /package/dist/{chunk-KSCQRZJZ.js.map → chunk-OX4L42RL.js.map} +0 -0
  99. /package/dist/{chunk-5GZ4UN5F.js.map → chunk-QCWY3GTZ.js.map} +0 -0
  100. /package/dist/{chunk-PENEGF75.js.map → chunk-RMDG3OFS.js.map} +0 -0
  101. /package/dist/{chunk-CG7OSI5P.js.map → chunk-S6FK47MZ.js.map} +0 -0
  102. /package/dist/{chunk-XU2SJKFK.js.map → chunk-SMKBETIX.js.map} +0 -0
  103. /package/dist/{chunk-ZTTBGME2.js.map → chunk-VI5TF3KF.js.map} +0 -0
  104. /package/dist/{chunk-JYZNPK62.js.map → chunk-VIDSDHSQ.js.map} +0 -0
  105. /package/dist/{chunk-3IBALX36.js.map → chunk-VLBXRQIA.js.map} +0 -0
  106. /package/dist/{chunk-TRCYUQ4M.js.map → chunk-WDGEJIWW.js.map} +0 -0
  107. /package/dist/{chunk-XJAGCUMD.js.map → chunk-ZP7LYABJ.js.map} +0 -0
  108. /package/dist/{chunk-VM4HZT3B.js.map → chunk-ZVHJMROO.js.map} +0 -0
  109. /package/dist/{cli-circuit-breaker-MCQG3PQK.js.map → cli-circuit-breaker-GOH6U32K.js.map} +0 -0
  110. /package/dist/{composite-router-QAYCHEFG.js.map → composite-router-RMEHBA3J.js.map} +0 -0
  111. /package/dist/{consensus-vote-EIV5TDPY.js.map → consensus-vote-23XXJUDP.js.map} +0 -0
  112. /package/dist/{context-retriever-UXQCDDRY.js.map → context-retriever-TWAN22PW.js.map} +0 -0
  113. /package/dist/{doctor-deep-QRLRDFM4.js.map → doctor-deep-HRZ77NYS.js.map} +0 -0
  114. /package/dist/{doctor-live-CUKBQSSH.js.map → doctor-live-VGOABDSJ.js.map} +0 -0
  115. /package/dist/{expert-bridge-ZG2IA3RJ.js.map → expert-bridge-PFR7ZNWK.js.map} +0 -0
  116. /package/dist/{factory-FPNYFKXU.js.map → factory-DJPYG3WR.js.map} +0 -0
  117. /package/dist/{factory-RQEHJ4AW.js.map → factory-JCVKAI7P.js.map} +0 -0
  118. /package/dist/{improvement-review-CUBUAU2D.js.map → improvement-review-RKHKBKDU.js.map} +0 -0
  119. /package/dist/{init-opencode-4QECASK4.js.map → init-opencode-KTIHOYIU.js.map} +0 -0
  120. /package/dist/{issue-triage-BIEZBWV2.js.map → issue-triage-OCTW33O3.js.map} +0 -0
  121. /package/dist/{pr-reviewer-helpers-RK5IT4US.js.map → pr-reviewer-helpers-V67TZHNM.js.map} +0 -0
  122. /package/dist/{registry-command-MBZFFDWS.js.map → registry-command-43IZQOAP.js.map} +0 -0
  123. /package/dist/{repo-security-plan-W76YLC2M.js.map → repo-security-plan-OXMSNBOS.js.map} +0 -0
  124. /package/dist/{research-helpers-synthesize-GI5JFAQM.js.map → research-helpers-synthesize-IBBRMZEY.js.map} +0 -0
  125. /package/dist/{routing-memory-WLCUOPDE.js.map → routing-memory-S3H3D6Z2.js.map} +0 -0
  126. /package/dist/{session-memory-HU3XMQEH.js.map → session-memory-UO42CU2H.js.map} +0 -0
  127. /package/dist/{setup-config-XA756R2O.js.map → setup-config-RGTKEVTD.js.map} +0 -0
  128. /package/dist/{setup-custom-api-ETBGOE7O.js.map → setup-custom-api-PQ3PR2RR.js.map} +0 -0
  129. /package/dist/{setup-data-dir-XJWO2WOU.js.map → setup-data-dir-AMMF6TWR.js.map} +0 -0
  130. /package/dist/{tool-memory-7XMQAQQM.js.map → tool-memory-ZMLCOQH6.js.map} +0 -0
  131. /package/dist/{unified-registry-LR4OCUFI.js.map → unified-registry-HWBSGSOM.js.map} +0 -0
  132. /package/dist/{weather-report-UYKQUSBB.js.map → weather-report-MZCA7Y6K.js.map} +0 -0
@@ -9,8 +9,8 @@ import {
9
9
  getBuiltInAstRulesPath,
10
10
  loadRules,
11
11
  runAstQaRules
12
- } from "./chunk-LFFXUHX3.js";
13
- import "./chunk-NZN3SWSZ.js";
12
+ } from "./chunk-O3MSZUQM.js";
13
+ import "./chunk-F2H3MCEV.js";
14
14
  import "./chunk-UE7A2XHM.js";
15
15
  import "./chunk-LWOX65CP.js";
16
16
  import "./chunk-PX6F3LHL.js";
@@ -26,4 +26,4 @@ export {
26
26
  loadRules,
27
27
  runAstQaRules
28
28
  };
29
- //# sourceMappingURL=ast-rule-runner-AMZCLKRV.js.map
29
+ //# sourceMappingURL=ast-rule-runner-SD665GB7.js.map
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-OITVVSWO.js";
4
4
  import {
5
5
  createLogger
6
- } from "./chunk-NZN3SWSZ.js";
6
+ } from "./chunk-F2H3MCEV.js";
7
7
  import "./chunk-UE7A2XHM.js";
8
8
  import "./chunk-LWOX65CP.js";
9
9
  import "./chunk-PX6F3LHL.js";
@@ -56,4 +56,4 @@ async function generateMcpConfig(options) {
56
56
  export {
57
57
  generateMcpConfig
58
58
  };
59
- //# sourceMappingURL=child-mcp-config-7DSYI325.js.map
59
+ //# sourceMappingURL=child-mcp-config-BG3QNALD.js.map
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  GitHubProvider,
3
3
  ScmError
4
- } from "./chunk-SCXCJSTT.js";
4
+ } from "./chunk-JWHIFEI3.js";
5
5
  import {
6
6
  resolveToken
7
- } from "./chunk-UDWZJY3V.js";
7
+ } from "./chunk-HUCILYVO.js";
8
8
  import {
9
9
  err,
10
10
  ok
11
- } from "./chunk-NZN3SWSZ.js";
11
+ } from "./chunk-F2H3MCEV.js";
12
12
 
13
13
  // src/scm/factory.ts
14
14
  async function createScmProvider(config) {
@@ -41,4 +41,4 @@ export {
41
41
  createScmProvider,
42
42
  createGitHubProvider
43
43
  };
44
- //# sourceMappingURL=chunk-5UCGVAEZ.js.map
44
+ //# sourceMappingURL=chunk-2N7XWZPW.js.map
@@ -4,12 +4,12 @@ import {
4
4
  planOptionalParams,
5
5
  requireApiKey,
6
6
  validateApiKeyPresence
7
- } from "./chunk-ZTTBGME2.js";
7
+ } from "./chunk-VI5TF3KF.js";
8
8
  import {
9
9
  assertCustomApiHostResolvesPublic,
10
10
  hostnameOf,
11
11
  redactApiKey
12
- } from "./chunk-LZNYQYDU.js";
12
+ } from "./chunk-KCWEMSU2.js";
13
13
  import {
14
14
  sanitizeErrorDetails
15
15
  } from "./chunk-2NDA4DHW.js";
@@ -31,7 +31,7 @@ import {
31
31
  isEndpointArmId,
32
32
  ok,
33
33
  recordUsageEvent
34
- } from "./chunk-NZN3SWSZ.js";
34
+ } from "./chunk-F2H3MCEV.js";
35
35
 
36
36
  // src/adapters/openai-types.ts
37
37
  var OPENAI_MODELS = {
@@ -886,4 +886,4 @@ export {
886
886
  discoverModels,
887
887
  buildOpenAICompatAdapters
888
888
  };
889
- //# sourceMappingURL=chunk-BJXSG5YL.js.map
889
+ //# sourceMappingURL=chunk-4KMNLWAC.js.map
@@ -1,6 +1,6 @@
1
1
  // src/version.ts
2
2
  import semver from "semver";
3
- var VERSION = true ? "8.89.3" : "dev";
3
+ var VERSION = true ? "8.90.0" : "dev";
4
4
  var NODE_ENGINE_RANGE = ">=22.5.0";
5
5
  function isNodeVersionSupported(version) {
6
6
  return semver.satisfies(version, NODE_ENGINE_RANGE);
@@ -11,4 +11,4 @@ export {
11
11
  NODE_ENGINE_RANGE,
12
12
  isNodeVersionSupported
13
13
  };
14
- //# sourceMappingURL=chunk-QX42N7TE.js.map
14
+ //# sourceMappingURL=chunk-4PMYNW3G.js.map
@@ -1,12 +1,19 @@
1
1
  import {
2
+ allOf,
3
+ verdictOver
4
+ } from "./chunk-EZNIYU5T.js";
5
+ import {
6
+ ApiArmIdSchema,
2
7
  TASK_CATEGORIES,
3
8
  getAdaptiveBonus,
4
- getOutcomeStore
5
- } from "./chunk-NZN3SWSZ.js";
9
+ getOutcomeStore,
10
+ hasMeasuredCategory
11
+ } from "./chunk-F2H3MCEV.js";
6
12
 
7
13
  // src/cli/doctor-deep.ts
8
14
  var CLI_NAMES = ["claude", "gemini", "codex", "opencode"];
9
15
  var COLD_START_THRESHOLD = 3;
16
+ var ROUTED_ARMS = [...CLI_NAMES, ...ApiArmIdSchema.options];
10
17
  function checkLearningLoop() {
11
18
  const store = getOutcomeStore();
12
19
  const outcomes = store.query();
@@ -39,34 +46,50 @@ function checkDataSufficiency() {
39
46
  const categoriesWithData = /* @__PURE__ */ new Set();
40
47
  const allOutcomes = store.query();
41
48
  for (const o of allOutcomes) {
42
- categoriesWithData.add(o.category);
49
+ if (hasMeasuredCategory(o)) categoriesWithData.add(o.category);
43
50
  }
44
51
  const missing = TASK_CATEGORIES.filter((c) => !categoriesWithData.has(c));
45
52
  return { cliStatus, missingCategories: missing, coldStartThreshold: COLD_START_THRESHOLD };
46
53
  }
54
+ function roundRate(rate) {
55
+ return Math.round(rate * 1e3) / 1e3;
56
+ }
57
+ function tallyRowsByArm() {
58
+ const byArm = /* @__PURE__ */ new Map();
59
+ for (const o of getOutcomeStore().query()) {
60
+ if (!ROUTED_ARMS.includes(o.cli)) continue;
61
+ const tally = byArm.get(o.cli) ?? { total: 0, successes: 0 };
62
+ tally.total++;
63
+ if (o.success) tally.successes++;
64
+ byArm.set(o.cli, tally);
65
+ }
66
+ return byArm;
67
+ }
47
68
  function checkConvergence() {
48
- const store = getOutcomeStore();
69
+ const rowsByArm = tallyRowsByArm();
70
+ const measured = [];
49
71
  const rates = /* @__PURE__ */ new Map();
50
- let totalRate = 0;
51
- for (const cli of CLI_NAMES) {
52
- const outcomes = store.query({ cli });
53
- if (outcomes.length > 0) {
54
- const rate = outcomes.filter((o) => o.success).length / outcomes.length;
55
- rates.set(cli, Math.round(rate * 1e3) / 1e3);
56
- totalRate += rate;
57
- } else {
58
- rates.set(cli, 0);
72
+ for (const arm of ROUTED_ARMS) {
73
+ const tally = rowsByArm.get(arm);
74
+ if (tally === void 0) {
75
+ rates.set(arm, { status: "unmeasured" });
76
+ continue;
59
77
  }
78
+ const rate = tally.successes / tally.total;
79
+ measured.push({ rate, sampleCount: tally.total });
80
+ rates.set(arm, { status: "measured", rate: roundRate(rate), sampleCount: tally.total });
60
81
  }
61
- const avgRate = totalRate / CLI_NAMES.length;
62
- const allAboveThreshold = CLI_NAMES.every((cli) => {
63
- const outcomes = store.query({ cli });
64
- return outcomes.length >= COLD_START_THRESHOLD;
65
- });
82
+ const avgSuccessRate = verdictOver(
83
+ measured,
84
+ (arms) => roundRate(arms.reduce((sum, a) => sum + a.rate, 0) / arms.length),
85
+ "unmeasured"
86
+ );
87
+ const converged = allOf(measured, (a) => a.sampleCount >= COLD_START_THRESHOLD, false);
66
88
  return {
67
- avgSuccessRate: Math.round(avgRate * 1e3) / 1e3,
68
- cliSuccessRates: rates,
69
- converged: allAboveThreshold
89
+ avgSuccessRate,
90
+ armSuccessRates: rates,
91
+ measuredArmCount: measured.length,
92
+ converged
70
93
  };
71
94
  }
72
95
  function runDeepDiagnostics() {
@@ -76,6 +99,23 @@ function runDeepDiagnostics() {
76
99
  routingConvergence: checkConvergence()
77
100
  };
78
101
  }
102
+ function formatConvergenceRates(rc) {
103
+ const pct = (rate) => `${(rate * 100).toFixed(1)}%`;
104
+ if (rc.avgSuccessRate === "unmeasured") {
105
+ return [" Avg success rate: unmeasured (no arm has outcome rows)"];
106
+ }
107
+ const noun = rc.measuredArmCount === 1 ? "arm" : "arms";
108
+ const lines = [
109
+ ` Avg success rate: ${pct(rc.avgSuccessRate)} over ${String(rc.measuredArmCount)} measured ${noun}`
110
+ ];
111
+ const unmeasured = [];
112
+ for (const [arm, r] of rc.armSuccessRates) {
113
+ if (r.status === "unmeasured") unmeasured.push(arm);
114
+ else lines.push(` ${arm}: ${pct(r.rate)} (${String(r.sampleCount)} runs)`);
115
+ }
116
+ if (unmeasured.length > 0) lines.push(` Unmeasured (no rows): ${unmeasured.join(", ")}`);
117
+ return lines;
118
+ }
79
119
  function formatDeepDiagnostics(diag) {
80
120
  const lines = ["\n=== Deep Diagnostics ===\n"];
81
121
  const ll = diag.learningLoop;
@@ -97,7 +137,7 @@ function formatDeepDiagnostics(diag) {
97
137
  }
98
138
  const rc = diag.routingConvergence;
99
139
  lines.push("\nRouting Convergence:");
100
- lines.push(` Avg success rate: ${(rc.avgSuccessRate * 100).toFixed(1)}%`);
140
+ lines.push(...formatConvergenceRates(rc));
101
141
  lines.push(` Converged: ${rc.converged ? "yes" : "no (still below cold-start threshold)"}`);
102
142
  return lines.join("\n");
103
143
  }
@@ -106,4 +146,4 @@ export {
106
146
  runDeepDiagnostics,
107
147
  formatDeepDiagnostics
108
148
  };
109
- //# sourceMappingURL=chunk-3PQIIMHN.js.map
149
+ //# sourceMappingURL=chunk-5GI6Z5VE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/doctor-deep.ts"],"sourcesContent":["/**\n * Deep diagnostics for the doctor command.\n *\n * Surfaces learning loop health, data sufficiency, routing convergence,\n * and memory system status. Opt-in via `--deep` flag.\n *\n * @module cli/doctor-deep\n * (Source: Issue #1031 — Enhanced doctor --deep diagnostics)\n */\n\nimport { getOutcomeStore } from '../orchestration/outcomes/outcome-store.js';\nimport { hasMeasuredCategory } from '../orchestration/outcomes/outcome-types.js';\nimport { TASK_CATEGORIES, type TaskCategory } from '../config/task-specialization-types.js';\nimport { getAdaptiveBonus } from '../mcp/tools/weather-report.js';\nimport { ApiArmIdSchema } from '../cli-adapters/types-core.js';\nimport { allOf, verdictOver } from '../utils/verdict-aggregation.js';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/** Per-CLI data sufficiency snapshot. */\nexport interface CliDataStatus {\n readonly cli: string;\n readonly taskCount: number;\n readonly aboveThreshold: boolean;\n}\n\n/** Deep diagnostics result. */\nexport interface DeepDiagnostics {\n readonly learningLoop: LearningLoopHealth;\n readonly dataSufficiency: DataSufficiency;\n readonly routingConvergence: RoutingConvergence;\n}\n\nexport interface LearningLoopHealth {\n readonly totalOutcomes: number;\n readonly latestTimestamp: string | null;\n readonly activeBonuses: number;\n readonly totalBonusPairs: number;\n}\n\nexport interface DataSufficiency {\n readonly cliStatus: readonly CliDataStatus[];\n readonly missingCategories: readonly string[];\n readonly coldStartThreshold: number;\n}\n\n/**\n * One arm's success rate. An arm with no outcome rows is `unmeasured` — not a\n * 0% rate, which would claim every attempt failed (#6557).\n */\nexport type ArmSuccessRate =\n | { readonly status: 'measured'; readonly rate: number; readonly sampleCount: number }\n | { readonly status: 'unmeasured' };\n\nexport interface RoutingConvergence {\n /**\n * Mean success rate over MEASURED arms only. `'unmeasured'` when no arm has\n * a row — the empty case, which is neither 0 nor NaN (#6557).\n */\n readonly avgSuccessRate: number | 'unmeasured';\n /** Every routed arm (CLI slots and `api:*` arms), measured or not. */\n readonly armSuccessRates: ReadonlyMap<string, ArmSuccessRate>;\n /** Number of arms with at least one outcome row: the average's divisor. */\n readonly measuredArmCount: number;\n /** Every measured arm has cleared the cold-start threshold; false when none is measured. */\n readonly converged: boolean;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst CLI_NAMES = ['claude', 'gemini', 'codex', 'opencode'] as const;\nconst COLD_START_THRESHOLD = 3;\n\n/**\n * CLI slots plus API arms: every `cli` an attributed outcome row can carry.\n * Routed rows record the arm that ran (`api:anthropic`), not its slot, since\n * #6554. `unknown` is deliberately absent: an unattributed row is no arm's\n * measurement.\n */\nconst ROUTED_ARMS: readonly string[] = [...CLI_NAMES, ...ApiArmIdSchema.options];\n\n// ============================================================================\n// Diagnostics\n// ============================================================================\n\n/** Check learning loop health: outcome count, latest timestamp, bonuses. */\nfunction checkLearningLoop(): LearningLoopHealth {\n const store = getOutcomeStore();\n const outcomes = store.query();\n const latestTimestamp =\n outcomes.length > 0 ? (outcomes[outcomes.length - 1]?.timestamp ?? null) : null;\n\n let activeBonuses = 0;\n const totalPairs = CLI_NAMES.length * TASK_CATEGORIES.length;\n for (const cli of CLI_NAMES) {\n for (const cat of TASK_CATEGORIES) {\n if (getAdaptiveBonus(cli, cat) !== 0) activeBonuses++;\n }\n }\n\n return {\n totalOutcomes: outcomes.length,\n latestTimestamp,\n activeBonuses,\n totalBonusPairs: totalPairs,\n };\n}\n\n/** Check per-CLI data sufficiency against cold-start threshold. */\nfunction checkDataSufficiency(): DataSufficiency {\n const store = getOutcomeStore();\n const cliStatus: CliDataStatus[] = [];\n\n for (const cli of CLI_NAMES) {\n const outcomes = store.query({ cli });\n cliStatus.push({\n cli,\n taskCount: outcomes.length,\n aboveThreshold: outcomes.length >= COLD_START_THRESHOLD,\n });\n }\n\n const categoriesWithData = new Set<TaskCategory>();\n const allOutcomes = store.query();\n for (const o of allOutcomes) {\n // A defaulted category is not coverage of that category (#6549).\n if (hasMeasuredCategory(o)) categoriesWithData.add(o.category);\n }\n const missing = TASK_CATEGORIES.filter((c) => !categoriesWithData.has(c));\n\n return { cliStatus, missingCategories: missing, coldStartThreshold: COLD_START_THRESHOLD };\n}\n\n/** Round a rate to three decimals for display-stable output. */\nfunction roundRate(rate: number): number {\n return Math.round(rate * 1000) / 1000;\n}\n\ninterface MeasuredArm {\n readonly rate: number;\n readonly sampleCount: number;\n}\n\n/** Tally rows per routed arm; rows carrying no routed arm (`unknown`) are skipped. */\nfunction tallyRowsByArm(): Map<string, { total: number; successes: number }> {\n const byArm = new Map<string, { total: number; successes: number }>();\n for (const o of getOutcomeStore().query()) {\n if (!ROUTED_ARMS.includes(o.cli)) continue;\n const tally = byArm.get(o.cli) ?? { total: 0, successes: 0 };\n tally.total++;\n if (o.success) tally.successes++;\n byArm.set(o.cli, tally);\n }\n return byArm;\n}\n\n/**\n * Check routing convergence from outcome success rates (#6557).\n *\n * Covers the arms rows actually carry, CLI and `api:*` alike. An arm with no\n * rows is `unmeasured` and stays out of the average, whose divisor is the\n * number of measured arms, never a fixed list.\n */\nfunction checkConvergence(): RoutingConvergence {\n const rowsByArm = tallyRowsByArm();\n const measured: MeasuredArm[] = [];\n const rates = new Map<string, ArmSuccessRate>();\n for (const arm of ROUTED_ARMS) {\n const tally = rowsByArm.get(arm);\n if (tally === undefined) {\n rates.set(arm, { status: 'unmeasured' });\n continue;\n }\n const rate = tally.successes / tally.total;\n measured.push({ rate, sampleCount: tally.total });\n rates.set(arm, { status: 'measured', rate: roundRate(rate), sampleCount: tally.total });\n }\n\n // Empty case named: with no measured arm there is no rate to average.\n const avgSuccessRate = verdictOver<MeasuredArm, number | 'unmeasured'>(\n measured,\n (arms) => roundRate(arms.reduce((sum, a) => sum + a.rate, 0) / arms.length),\n 'unmeasured'\n );\n // Nothing measured has not converged on anything.\n const converged = allOf(measured, (a) => a.sampleCount >= COLD_START_THRESHOLD, false);\n\n return {\n avgSuccessRate,\n armSuccessRates: rates,\n measuredArmCount: measured.length,\n converged,\n };\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/** Run all deep diagnostics. */\nexport function runDeepDiagnostics(): DeepDiagnostics {\n return {\n learningLoop: checkLearningLoop(),\n dataSufficiency: checkDataSufficiency(),\n routingConvergence: checkConvergence(),\n };\n}\n\n/** Render convergence rates, naming unmeasured arms rather than showing them as 0%. */\nfunction formatConvergenceRates(rc: RoutingConvergence): string[] {\n const pct = (rate: number): string => `${(rate * 100).toFixed(1)}%`;\n if (rc.avgSuccessRate === 'unmeasured') {\n return [' Avg success rate: unmeasured (no arm has outcome rows)'];\n }\n const noun = rc.measuredArmCount === 1 ? 'arm' : 'arms';\n const lines = [\n ` Avg success rate: ${pct(rc.avgSuccessRate)} over ${String(rc.measuredArmCount)} measured ${noun}`,\n ];\n const unmeasured: string[] = [];\n for (const [arm, r] of rc.armSuccessRates) {\n if (r.status === 'unmeasured') unmeasured.push(arm);\n else lines.push(` ${arm}: ${pct(r.rate)} (${String(r.sampleCount)} runs)`);\n }\n if (unmeasured.length > 0) lines.push(` Unmeasured (no rows): ${unmeasured.join(', ')}`);\n return lines;\n}\n\n/** Format deep diagnostics for CLI output. */\nexport function formatDeepDiagnostics(diag: DeepDiagnostics): string {\n const lines: string[] = ['\\n=== Deep Diagnostics ===\\n'];\n\n // Learning Loop\n const ll = diag.learningLoop;\n lines.push('Learning Loop:');\n const outcomeIcon = ll.totalOutcomes > 0 ? '+' : '-';\n lines.push(` ${outcomeIcon} OutcomeStore: ${String(ll.totalOutcomes)} entries`);\n const bonusIcon = ll.activeBonuses > 0 ? '+' : '-';\n lines.push(\n ` ${bonusIcon} Adaptive bonuses: ${String(ll.activeBonuses)}/${String(ll.totalBonusPairs)} active`\n );\n\n // Data Sufficiency\n lines.push('\\nData Sufficiency:');\n for (const cs of diag.dataSufficiency.cliStatus) {\n const icon = cs.aboveThreshold ? '+' : '!';\n const label = cs.aboveThreshold ? 'above threshold' : 'below threshold';\n lines.push(` ${icon} ${cs.cli}: ${String(cs.taskCount)} tasks (${label})`);\n }\n if (diag.dataSufficiency.missingCategories.length > 0) {\n lines.push(` Missing categories: ${diag.dataSufficiency.missingCategories.join(', ')}`);\n }\n\n // Routing Convergence\n const rc = diag.routingConvergence;\n lines.push('\\nRouting Convergence:');\n lines.push(...formatConvergenceRates(rc));\n lines.push(` Converged: ${rc.converged ? 'yes' : 'no (still below cold-start threshold)'}`);\n\n return lines.join('\\n');\n}\n"],"mappings":"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","names":[]}
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  createAutoAdapter
3
- } from "./chunk-JYZNPK62.js";
3
+ } from "./chunk-VIDSDHSQ.js";
4
4
  import {
5
5
  getDefaultCliCircuitBreakerRegistry,
6
6
  mapModelErrorToCategory
7
- } from "./chunk-XU2SJKFK.js";
7
+ } from "./chunk-SMKBETIX.js";
8
8
  import {
9
9
  CircularBuffer,
10
10
  ConfigError,
@@ -28,7 +28,7 @@ import {
28
28
  resolveModelIdentitySync,
29
29
  toRateLimitError,
30
30
  warnIfGatewayCostUndeclared
31
- } from "./chunk-NZN3SWSZ.js";
31
+ } from "./chunk-F2H3MCEV.js";
32
32
 
33
33
  // src/config/model-equivalence.ts
34
34
  function canonicalModelKey(modelId) {
@@ -975,4 +975,4 @@ export {
975
975
  getGlobalRegistry,
976
976
  resetGlobalRegistry
977
977
  };
978
- //# sourceMappingURL=chunk-TLFZPJOY.js.map
978
+ //# sourceMappingURL=chunk-C6Q2NUIH.js.map
@@ -7,9 +7,14 @@ function anyOf(items, predicate, whenEmpty) {
7
7
  if (items.length === 0) return whenEmpty;
8
8
  return items.some(predicate);
9
9
  }
10
+ function verdictOver(items, aggregate, whenEmpty) {
11
+ if (items.length === 0) return whenEmpty;
12
+ return aggregate(items);
13
+ }
10
14
 
11
15
  export {
12
16
  allOf,
13
- anyOf
17
+ anyOf,
18
+ verdictOver
14
19
  };
15
- //# sourceMappingURL=chunk-OQB3XC5O.js.map
20
+ //# sourceMappingURL=chunk-EZNIYU5T.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/verdict-aggregation.ts"],"sourcesContent":["/**\n * Aggregating a verdict over a collection that might be empty (#4580).\n *\n * `[].every(p)` is `true` in JavaScript. So `checks.every((c) => c.passed)`\n * reports **pass** when `checks` is empty — absence rendered as health, on\n * exactly the code paths where that is most dangerous.\n *\n * Confirmed instances of the shape:\n * - `verify_audit_chain` returns `ok: true` over zero events, so deleting\n * every audit log file makes a tamper-evident chain verify clean (#4579)\n * - a quality gate with zero checks reported `pass` (#4544)\n * - the QA pipeline stage reported `success` after reviewing zero tasks,\n * letting the graph advance as though implementations had been reviewed\n *\n * ## Why a helper rather than a lint or a type\n *\n * Measured before choosing: 64 non-test `.every()` calls, and most are fine —\n * `github-provider.ts:178` guards with `length > 0` and correctly leaves an\n * empty check set `pending`. A wide surface with sparse defects, so a blanket\n * lint would be mostly false positives, and false positives are what teach\n * people to bypass a gate.\n *\n * What was missing is not enforcement but a *decision point*: nothing made the\n * author say what empty means. `whenEmpty` is a required argument, so they\n * must. Chosen by a 7-voter `higher_order` panel at the supermajority bar.\n *\n * ## Choosing `whenEmpty`\n *\n * Ask what an empty collection is evidence OF. Usually nothing — in which case\n * the honest answer is the non-committal verdict (`pending`, `unmeasured`,\n * `skip`), not the optimistic one. Reserve `true` for cases where vacuous truth\n * is genuinely the contract, and say so at the call site.\n *\n * @module utils/verdict-aggregation\n * (Source: Issue #4580)\n */\n\n/**\n * `predicate` holds for every item — with the empty case named, not defaulted.\n *\n * @param items - The collection to judge.\n * @param predicate - Must hold for each item.\n * @param whenEmpty - The verdict when there is nothing to judge. Required.\n */\nexport function allOf<T>(\n items: readonly T[],\n predicate: (item: T) => boolean,\n whenEmpty: boolean\n): boolean {\n if (items.length === 0) return whenEmpty;\n return items.every(predicate);\n}\n\n/**\n * `predicate` holds for at least one item — with the empty case named.\n *\n * `[].some(p)` is already `false`, which is usually right, but not always: a\n * \"did anything fail?\" check over zero results should often be `unmeasured`\n * rather than a clean `false`. Naming it keeps the reasoning visible.\n *\n * @param items - The collection to judge.\n * @param predicate - Must hold for at least one item.\n * @param whenEmpty - The verdict when there is nothing to judge. Required.\n */\nexport function anyOf<T>(\n items: readonly T[],\n predicate: (item: T) => boolean,\n whenEmpty: boolean\n): boolean {\n if (items.length === 0) return whenEmpty;\n return items.some(predicate);\n}\n\n/**\n * Reduce a collection to an arbitrary verdict, with the empty case named.\n *\n * For verdicts richer than a boolean — a severity, a tri-state gate result —\n * where the empty case is usually `unmeasured` rather than the best value.\n *\n * @param items - The collection to judge.\n * @param aggregate - Folds a non-empty collection into a verdict.\n * @param whenEmpty - The verdict when there is nothing to judge. Required.\n */\nexport function verdictOver<T, V>(\n items: readonly T[],\n aggregate: (items: readonly T[]) => V,\n whenEmpty: V\n): V {\n if (items.length === 0) return whenEmpty;\n return aggregate(items);\n}\n"],"mappings":";AA4CO,SAAS,MACd,OACA,WACA,WACS;AACT,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,SAAO,MAAM,MAAM,SAAS;AAC9B;AAaO,SAAS,MACd,OACA,WACA,WACS;AACT,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,SAAO,MAAM,KAAK,SAAS;AAC7B;","names":[]}
1
+ {"version":3,"sources":["../src/utils/verdict-aggregation.ts"],"sourcesContent":["/**\n * Aggregating a verdict over a collection that might be empty (#4580).\n *\n * `[].every(p)` is `true` in JavaScript. So `checks.every((c) => c.passed)`\n * reports **pass** when `checks` is empty — absence rendered as health, on\n * exactly the code paths where that is most dangerous.\n *\n * Confirmed instances of the shape:\n * - `verify_audit_chain` returns `ok: true` over zero events, so deleting\n * every audit log file makes a tamper-evident chain verify clean (#4579)\n * - a quality gate with zero checks reported `pass` (#4544)\n * - the QA pipeline stage reported `success` after reviewing zero tasks,\n * letting the graph advance as though implementations had been reviewed\n *\n * ## Why a helper rather than a lint or a type\n *\n * Measured before choosing: 64 non-test `.every()` calls, and most are fine —\n * `github-provider.ts:178` guards with `length > 0` and correctly leaves an\n * empty check set `pending`. A wide surface with sparse defects, so a blanket\n * lint would be mostly false positives, and false positives are what teach\n * people to bypass a gate.\n *\n * What was missing is not enforcement but a *decision point*: nothing made the\n * author say what empty means. `whenEmpty` is a required argument, so they\n * must. Chosen by a 7-voter `higher_order` panel at the supermajority bar.\n *\n * ## Choosing `whenEmpty`\n *\n * Ask what an empty collection is evidence OF. Usually nothing — in which case\n * the honest answer is the non-committal verdict (`pending`, `unmeasured`,\n * `skip`), not the optimistic one. Reserve `true` for cases where vacuous truth\n * is genuinely the contract, and say so at the call site.\n *\n * @module utils/verdict-aggregation\n * (Source: Issue #4580)\n */\n\n/**\n * `predicate` holds for every item — with the empty case named, not defaulted.\n *\n * @param items - The collection to judge.\n * @param predicate - Must hold for each item.\n * @param whenEmpty - The verdict when there is nothing to judge. Required.\n */\nexport function allOf<T>(\n items: readonly T[],\n predicate: (item: T) => boolean,\n whenEmpty: boolean\n): boolean {\n if (items.length === 0) return whenEmpty;\n return items.every(predicate);\n}\n\n/**\n * `predicate` holds for at least one item — with the empty case named.\n *\n * `[].some(p)` is already `false`, which is usually right, but not always: a\n * \"did anything fail?\" check over zero results should often be `unmeasured`\n * rather than a clean `false`. Naming it keeps the reasoning visible.\n *\n * @param items - The collection to judge.\n * @param predicate - Must hold for at least one item.\n * @param whenEmpty - The verdict when there is nothing to judge. Required.\n */\nexport function anyOf<T>(\n items: readonly T[],\n predicate: (item: T) => boolean,\n whenEmpty: boolean\n): boolean {\n if (items.length === 0) return whenEmpty;\n return items.some(predicate);\n}\n\n/**\n * Reduce a collection to an arbitrary verdict, with the empty case named.\n *\n * For verdicts richer than a boolean — a severity, a tri-state gate result —\n * where the empty case is usually `unmeasured` rather than the best value.\n *\n * @param items - The collection to judge.\n * @param aggregate - Folds a non-empty collection into a verdict.\n * @param whenEmpty - The verdict when there is nothing to judge. Required.\n */\nexport function verdictOver<T, V>(\n items: readonly T[],\n aggregate: (items: readonly T[]) => V,\n whenEmpty: V\n): V {\n if (items.length === 0) return whenEmpty;\n return aggregate(items);\n}\n"],"mappings":";AA4CO,SAAS,MACd,OACA,WACA,WACS;AACT,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,SAAO,MAAM,MAAM,SAAS;AAC9B;AAaO,SAAS,MACd,OACA,WACA,WACS;AACT,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,SAAO,MAAM,KAAK,SAAS;AAC7B;AAYO,SAAS,YACd,OACA,WACA,WACG;AACH,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,SAAO,UAAU,KAAK;AACxB;","names":[]}
@@ -2696,7 +2696,7 @@ function setDefaultRegistry(registry) {
2696
2696
  }
2697
2697
  async function reloadDefaultRegistry() {
2698
2698
  globalRegistry = buildDefaultRegistry();
2699
- const { resetGlobalRegistry } = await import("./unified-registry-LR4OCUFI.js");
2699
+ const { resetGlobalRegistry } = await import("./unified-registry-HWBSGSOM.js");
2700
2700
  resetGlobalRegistry();
2701
2701
  return globalRegistry;
2702
2702
  }
@@ -10293,6 +10293,7 @@ var OutcomeFailureCategorySchema = z14.enum([
10293
10293
  var OutcomeCliSchema = z14.union([CliNameSchema, ApiArmIdSchema, z14.literal("unknown")]);
10294
10294
  var OutcomeCliSourceSchema = z14.enum(["executed", "category-default"]);
10295
10295
  var OutcomeRoutedBySchema = z14.enum(["composite-router"]);
10296
+ var OutcomeCategorySourceSchema = z14.enum(["detected", "defaulted"]);
10296
10297
  var TaskOutcomeSchema = z14.object({
10297
10298
  id: z14.string().min(1),
10298
10299
  cli: OutcomeCliSchema,
@@ -10301,6 +10302,8 @@ var TaskOutcomeSchema = z14.object({
10301
10302
  /** Set only when `CompositeRouter` selected the CLI for this task (#6521). */
10302
10303
  routedBy: OutcomeRoutedBySchema.optional(),
10303
10304
  category: TaskCategorySchema,
10305
+ /** Whether `category` was detected or defaulted (#6549). Absent on legacy rows. */
10306
+ categorySource: OutcomeCategorySourceSchema.optional(),
10304
10307
  model: z14.string().min(1),
10305
10308
  success: z14.boolean(),
10306
10309
  durationMs: z14.number().nonnegative(),
@@ -10357,6 +10360,16 @@ var OutcomeQuerySchema = z14.object({
10357
10360
  /** Restrict to outcomes recorded against a specific baseline (#2697). */
10358
10361
  baselineId: z14.string().min(1).max(64).optional()
10359
10362
  });
10363
+ var UNDETECTED_CATEGORY_FALLBACK = "exploration";
10364
+ function resolveOutcomeCategory(detected) {
10365
+ if (detected === void 0) {
10366
+ return { category: UNDETECTED_CATEGORY_FALLBACK, categorySource: "defaulted" };
10367
+ }
10368
+ return { category: detected, categorySource: "detected" };
10369
+ }
10370
+ function hasMeasuredCategory(outcome) {
10371
+ return outcome.categorySource !== "defaulted";
10372
+ }
10360
10373
  var TIMEOUT_PATTERNS = ["timeout", "timed out", "deadline exceeded", "socket hang up", "aborted"];
10361
10374
  var AUTH_PATTERNS = [
10362
10375
  "auth",
@@ -10561,6 +10574,7 @@ function isDistillerEligible(outcome) {
10561
10574
  if (outcome.routedBy !== "composite-router") return false;
10562
10575
  if (outcome.source === "consensus") return false;
10563
10576
  if (!(outcome.durationMs > 0)) return false;
10577
+ if (!hasMeasuredCategory(outcome)) return false;
10564
10578
  return ROUTABLE_CLIS.has(outcome.cli);
10565
10579
  }
10566
10580
  function countEligibleSince(outcomes, sinceMs) {
@@ -10579,7 +10593,8 @@ function getFileFieldsSchema() {
10579
10593
  cli: true,
10580
10594
  routedBy: true,
10581
10595
  durationMs: true,
10582
- timestamp: true
10596
+ timestamp: true,
10597
+ categorySource: true
10583
10598
  });
10584
10599
  return fileFieldsSchema;
10585
10600
  }
@@ -11074,7 +11089,9 @@ var OutcomeStore = class {
11074
11089
  successRate: successCount / outcomes.length,
11075
11090
  avgDurationMs: totalDuration / outcomes.length,
11076
11091
  byCli: groupBy(outcomes, (o) => o.cli),
11077
- byCategory: groupBy(outcomes, (o) => o.category)
11092
+ // A defaulted category (#6549) is no category: totals count the row,
11093
+ // the per-category breakdown does not.
11094
+ byCategory: groupBy(outcomes.filter(hasMeasuredCategory), (o) => o.category)
11078
11095
  };
11079
11096
  }
11080
11097
  /** Number of stored outcomes. */
@@ -11194,7 +11211,9 @@ ${failLines.join("\n")}` : "";
11194
11211
  function buildPredicates(filter) {
11195
11212
  const preds = [];
11196
11213
  if (filter.cli !== void 0) preds.push((o) => o.cli === filter.cli);
11197
- if (filter.category !== void 0) preds.push((o) => o.category === filter.category);
11214
+ if (filter.category !== void 0) {
11215
+ preds.push((o) => hasMeasuredCategory(o) && o.category === filter.category);
11216
+ }
11198
11217
  if (filter.source !== void 0) preds.push((o) => o.source === filter.source);
11199
11218
  if (filter.success !== void 0) preds.push((o) => o.success === filter.success);
11200
11219
  if (filter.failureCategory !== void 0) {
@@ -14111,7 +14130,7 @@ function buildCliWeather(summary, input) {
14111
14130
  const cliOutcomes = store.query({ cli });
14112
14131
  const byCategory = /* @__PURE__ */ new Map();
14113
14132
  for (const cat of TASK_CATEGORIES) {
14114
- const catOutcomes = cliOutcomes.filter((o) => o.category === cat);
14133
+ const catOutcomes = cliOutcomes.filter((o) => hasMeasuredCategory(o) && o.category === cat);
14115
14134
  if (catOutcomes.length > 0) {
14116
14135
  const sc = catOutcomes.filter((o) => o.success).length;
14117
14136
  const td = catOutcomes.reduce((s, o) => s + o.durationMs, 0);
@@ -14276,7 +14295,9 @@ function buildSwarmHealth(expertPerf) {
14276
14295
  let observedCategories = 0;
14277
14296
  let analyzedCategories = 0;
14278
14297
  for (const category of TASK_CATEGORIES) {
14279
- const catOutcomes = allOutcomes.filter((o) => o.category === category);
14298
+ const catOutcomes = allOutcomes.filter(
14299
+ (o) => hasMeasuredCategory(o) && o.category === category
14300
+ );
14280
14301
  if (catOutcomes.length < ROUTING_MIN_SAMPLES) continue;
14281
14302
  observedCategories++;
14282
14303
  const stats = analyzeCategoryRouting(catOutcomes);
@@ -16100,7 +16121,7 @@ var CompositeRouter = class _CompositeRouter {
16100
16121
  */
16101
16122
  async consultUnifiedContext(task) {
16102
16123
  try {
16103
- const { getContextForTask, inferTaskCategory } = await import("./context-retriever-UXQCDDRY.js");
16124
+ const { getContextForTask, inferTaskCategory } = await import("./context-retriever-TWAN22PW.js");
16104
16125
  const ctx = await getContextForTask({
16105
16126
  task: task.content,
16106
16127
  category: inferTaskCategory(task.content),
@@ -17527,6 +17548,7 @@ export {
17527
17548
  loadGeneratedRegistryEntries,
17528
17549
  getFallbackChainForCategory,
17529
17550
  isCliName,
17551
+ ApiArmIdSchema,
17530
17552
  apiArmId,
17531
17553
  routingArmDisplaySlot,
17532
17554
  isEndpointArmId,
@@ -17582,6 +17604,8 @@ export {
17582
17604
  validateTimeout,
17583
17605
  OutcomeFailureCategorySchema,
17584
17606
  TaskOutcomeSchema,
17607
+ resolveOutcomeCategory,
17608
+ hasMeasuredCategory,
17585
17609
  extractNonErrorMessage,
17586
17610
  categorizeOutcomeError,
17587
17611
  categorizeOutcomeErrorMessage,
@@ -17758,4 +17782,4 @@ export {
17758
17782
  AgentCapability,
17759
17783
  OrchestratorError
17760
17784
  };
17761
- //# sourceMappingURL=chunk-NZN3SWSZ.js.map
17785
+ //# sourceMappingURL=chunk-F2H3MCEV.js.map