nexus-agents 2.131.1 → 2.132.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 (158) hide show
  1. package/README.md +1 -1
  2. package/dist/{child-mcp-config-WREL6AH5.js → child-mcp-config-R67B2KM6.js} +4 -4
  3. package/dist/{chunk-K3CES6CG.js → chunk-27DRQWON.js} +7 -123
  4. package/dist/chunk-27DRQWON.js.map +1 -0
  5. package/dist/{chunk-HM77WNJG.js → chunk-2CLFX73Y.js} +2 -2
  6. package/dist/{chunk-ZLQCJRYK.js → chunk-2JZ55G3X.js} +607 -332
  7. package/dist/chunk-2JZ55G3X.js.map +1 -0
  8. package/dist/{chunk-4SNH5EMQ.js → chunk-4CLBJ2NA.js} +3 -3
  9. package/dist/{chunk-DRYU676G.js → chunk-4DLHM6LI.js} +6 -2
  10. package/dist/{chunk-DRYU676G.js.map → chunk-4DLHM6LI.js.map} +1 -1
  11. package/dist/{chunk-JPBPQN4U.js → chunk-7BONPRR2.js} +2 -2
  12. package/dist/{chunk-O563YLBH.js → chunk-CI5KGCX5.js} +3 -3
  13. package/dist/{chunk-PARI43LB.js → chunk-E4IYX7LD.js} +2 -2
  14. package/dist/{chunk-JSYFPXID.js → chunk-ECDJM6UB.js} +2 -2
  15. package/dist/{chunk-WUJCVZIQ.js → chunk-EFDXX5MC.js} +3 -3
  16. package/dist/{chunk-3P252QNW.js → chunk-FKTLAPXJ.js} +3 -3
  17. package/dist/{chunk-PID22PMZ.js → chunk-FRHC77VK.js} +8 -8
  18. package/dist/{chunk-TRTCKEHS.js → chunk-GET2MTLW.js} +2 -2
  19. package/dist/{chunk-NINNWQ42.js → chunk-GYDO2WOR.js} +13 -5
  20. package/dist/{chunk-NINNWQ42.js.map → chunk-GYDO2WOR.js.map} +1 -1
  21. package/dist/{chunk-DEL26PXS.js → chunk-I2SW34K6.js} +403 -84
  22. package/dist/chunk-I2SW34K6.js.map +1 -0
  23. package/dist/{chunk-3TTMRJUQ.js → chunk-J435FU54.js} +6 -4
  24. package/dist/{chunk-3TTMRJUQ.js.map → chunk-J435FU54.js.map} +1 -1
  25. package/dist/{chunk-IPPWZNTH.js → chunk-K5WBFAR4.js} +4 -4
  26. package/dist/{chunk-KMNAZ5RC.js → chunk-MHIJSCPR.js} +446 -162
  27. package/dist/chunk-MHIJSCPR.js.map +1 -0
  28. package/dist/{chunk-PGGSGUO4.js → chunk-NZGNOXXW.js} +3 -2
  29. package/dist/{chunk-PGGSGUO4.js.map → chunk-NZGNOXXW.js.map} +1 -1
  30. package/dist/{chunk-UZWZ72X7.js → chunk-PM63X3QG.js} +3 -3
  31. package/dist/{chunk-4KBG4ICZ.js → chunk-QJIHSQZX.js} +790 -27
  32. package/dist/chunk-QJIHSQZX.js.map +1 -0
  33. package/dist/{chunk-HS3K2B7Q.js → chunk-QLRH6KOT.js} +2 -2
  34. package/dist/{chunk-DI6SRBCT.js → chunk-QMCHVYZB.js} +2 -2
  35. package/dist/{chunk-GVBEF6LQ.js → chunk-RAWF4JOT.js} +8 -8
  36. package/dist/{chunk-RF7QCPNA.js → chunk-RLYXX3FQ.js} +2 -2
  37. package/dist/{chunk-STIX3VTX.js → chunk-RR7Q2HFH.js} +6 -6
  38. package/dist/{chunk-ZGLIHPGJ.js → chunk-RUSP5PJR.js} +9 -1
  39. package/dist/chunk-RUSP5PJR.js.map +1 -0
  40. package/dist/{chunk-FCPANF6R.js → chunk-S4U3Z6ZS.js} +9 -6
  41. package/dist/{chunk-FCPANF6R.js.map → chunk-S4U3Z6ZS.js.map} +1 -1
  42. package/dist/chunk-TGGR6ST5.js +126 -0
  43. package/dist/chunk-TGGR6ST5.js.map +1 -0
  44. package/dist/{chunk-2QFUW2SP.js → chunk-VZCQRLXT.js} +7 -7
  45. package/dist/{chunk-JTECUWWI.js → chunk-WHZD2W6Q.js} +2 -2
  46. package/dist/{chunk-BDERGLIX.js → chunk-XIFOAZ5B.js} +4 -4
  47. package/dist/{chunk-PR4QN5HX.js → chunk-ZPWHCABL.js} +6 -2
  48. package/dist/{chunk-4DJNJGIB.js → chunk-ZQEQVCIU.js} +2 -2
  49. package/dist/{chunk-VZ3TTUWP.js → chunk-ZTRBMKXK.js} +5 -2
  50. package/dist/{chunk-VZ3TTUWP.js.map → chunk-ZTRBMKXK.js.map} +1 -1
  51. package/dist/cli-circuit-breaker-J6UFONNE.js +13 -0
  52. package/dist/cli.d.ts +1 -1
  53. package/dist/cli.js +258 -150
  54. package/dist/cli.js.map +1 -1
  55. package/dist/{codebase-search-ZFJUVMVR.js → codebase-search-PWMWLU7O.js} +2 -2
  56. package/dist/{composite-router-Y6UCDBDE.js → composite-router-HQUSCJO4.js} +4 -4
  57. package/dist/consensus-vote-E2WJFEIJ.js +37 -0
  58. package/dist/{consensus-vote-types-DdruG9-X.d.ts → consensus-vote-types-CYBHW8vY.d.ts} +99 -2
  59. package/dist/{context-retriever-GRMH7WSS.js → context-retriever-5OVRAWNZ.js} +10 -10
  60. package/dist/{dist-QZLOLGIK.js → dist-A5STUO26.js} +792 -622
  61. package/dist/dist-A5STUO26.js.map +1 -0
  62. package/dist/{dist-OFXI4GYH.js → dist-C4457XNQ.js} +2 -2
  63. package/dist/doctor-deep-HJ3NQ365.js +12 -0
  64. package/dist/expert-bridge-MINXBC73.js +17 -0
  65. package/dist/{expert-config-OIIUMJFB.js → expert-config-53EBA7DT.js} +2 -2
  66. package/dist/factory-KC7BYHAP.js +21 -0
  67. package/dist/factory-ZG2FZYVA.js +14 -0
  68. package/dist/{improvement-review-N2KSQSNW.js → improvement-review-TDC4QH5P.js} +6 -6
  69. package/dist/index.d.ts +731 -339
  70. package/dist/index.js +31 -30
  71. package/dist/index.js.map +1 -1
  72. package/dist/{init-opencode-6K6QNJWF.js → init-opencode-L3NT524E.js} +8 -7
  73. package/dist/{init-opencode-6K6QNJWF.js.map → init-opencode-L3NT524E.js.map} +1 -1
  74. package/dist/issue-triage-CZPFNGB7.js +16 -0
  75. package/dist/{learning-persistence-C737Q6EP.js → learning-persistence-QFCQ5POD.js} +7 -3
  76. package/dist/{outcome-store-adapter-D3UDQFNQ.js → outcome-store-adapter-K4L3RUJE.js} +2 -2
  77. package/dist/{pr-reviewer-helpers-GC6DEIBT.js → pr-reviewer-helpers-PLKDVWYF.js} +6 -6
  78. package/dist/{registry-command-7RRDCDAU.js → registry-command-HPKNLYUL.js} +4 -4
  79. package/dist/{repo-analyze-JZEMBE6R.js → repo-analyze-UJOWFGNM.js} +2 -2
  80. package/dist/{repo-security-plan-5V43DEGA.js → repo-security-plan-HSYGGVEJ.js} +5 -5
  81. package/dist/research-helpers-synthesize-IQHDU27S.js +14 -0
  82. package/dist/{routing-memory-BQ6MUC5Y.js → routing-memory-OGUOL7UF.js} +4 -4
  83. package/dist/{session-memory-MJHN5FXI.js → session-memory-HQWAXH3C.js} +5 -5
  84. package/dist/{setup-command-YAQE2KRH.js → setup-command-CNYIKBY7.js} +13 -13
  85. package/dist/setup-config-HYX3BRSE.js +10 -0
  86. package/dist/{setup-custom-api-3JEENEKA.js → setup-custom-api-W7EDVKWR.js} +5 -5
  87. package/dist/{tool-memory-NKBBVA7O.js → tool-memory-Z2B7CAH7.js} +7 -7
  88. package/dist/unified-registry-UFNSWKZA.js +24 -0
  89. package/dist/{weather-report-7WC3RKE7.js → weather-report-GX7OIUL2.js} +4 -4
  90. package/package.json +12 -12
  91. package/dist/chunk-4KBG4ICZ.js.map +0 -1
  92. package/dist/chunk-DEL26PXS.js.map +0 -1
  93. package/dist/chunk-K3CES6CG.js.map +0 -1
  94. package/dist/chunk-KMNAZ5RC.js.map +0 -1
  95. package/dist/chunk-ZGLIHPGJ.js.map +0 -1
  96. package/dist/chunk-ZLQCJRYK.js.map +0 -1
  97. package/dist/cli-circuit-breaker-WLYICYAZ.js +0 -13
  98. package/dist/consensus-vote-PEWNN4WK.js +0 -36
  99. package/dist/dist-QZLOLGIK.js.map +0 -1
  100. package/dist/doctor-deep-HXKGXUI4.js +0 -12
  101. package/dist/expert-bridge-BW55FRAH.js +0 -17
  102. package/dist/factory-4EV6EDZF.js +0 -21
  103. package/dist/factory-YKXAMYW6.js +0 -14
  104. package/dist/issue-triage-PYHGDQHK.js +0 -16
  105. package/dist/research-helpers-synthesize-EDT6REQJ.js +0 -14
  106. package/dist/setup-config-OFC6SD7H.js +0 -10
  107. package/dist/unified-registry-XXAVF4AQ.js +0 -24
  108. /package/dist/{child-mcp-config-WREL6AH5.js.map → child-mcp-config-R67B2KM6.js.map} +0 -0
  109. /package/dist/{chunk-HM77WNJG.js.map → chunk-2CLFX73Y.js.map} +0 -0
  110. /package/dist/{chunk-4SNH5EMQ.js.map → chunk-4CLBJ2NA.js.map} +0 -0
  111. /package/dist/{chunk-JPBPQN4U.js.map → chunk-7BONPRR2.js.map} +0 -0
  112. /package/dist/{chunk-O563YLBH.js.map → chunk-CI5KGCX5.js.map} +0 -0
  113. /package/dist/{chunk-PARI43LB.js.map → chunk-E4IYX7LD.js.map} +0 -0
  114. /package/dist/{chunk-JSYFPXID.js.map → chunk-ECDJM6UB.js.map} +0 -0
  115. /package/dist/{chunk-WUJCVZIQ.js.map → chunk-EFDXX5MC.js.map} +0 -0
  116. /package/dist/{chunk-3P252QNW.js.map → chunk-FKTLAPXJ.js.map} +0 -0
  117. /package/dist/{chunk-PID22PMZ.js.map → chunk-FRHC77VK.js.map} +0 -0
  118. /package/dist/{chunk-TRTCKEHS.js.map → chunk-GET2MTLW.js.map} +0 -0
  119. /package/dist/{chunk-IPPWZNTH.js.map → chunk-K5WBFAR4.js.map} +0 -0
  120. /package/dist/{chunk-UZWZ72X7.js.map → chunk-PM63X3QG.js.map} +0 -0
  121. /package/dist/{chunk-HS3K2B7Q.js.map → chunk-QLRH6KOT.js.map} +0 -0
  122. /package/dist/{chunk-DI6SRBCT.js.map → chunk-QMCHVYZB.js.map} +0 -0
  123. /package/dist/{chunk-GVBEF6LQ.js.map → chunk-RAWF4JOT.js.map} +0 -0
  124. /package/dist/{chunk-RF7QCPNA.js.map → chunk-RLYXX3FQ.js.map} +0 -0
  125. /package/dist/{chunk-STIX3VTX.js.map → chunk-RR7Q2HFH.js.map} +0 -0
  126. /package/dist/{chunk-2QFUW2SP.js.map → chunk-VZCQRLXT.js.map} +0 -0
  127. /package/dist/{chunk-JTECUWWI.js.map → chunk-WHZD2W6Q.js.map} +0 -0
  128. /package/dist/{chunk-BDERGLIX.js.map → chunk-XIFOAZ5B.js.map} +0 -0
  129. /package/dist/{chunk-PR4QN5HX.js.map → chunk-ZPWHCABL.js.map} +0 -0
  130. /package/dist/{chunk-4DJNJGIB.js.map → chunk-ZQEQVCIU.js.map} +0 -0
  131. /package/dist/{cli-circuit-breaker-WLYICYAZ.js.map → cli-circuit-breaker-J6UFONNE.js.map} +0 -0
  132. /package/dist/{codebase-search-ZFJUVMVR.js.map → codebase-search-PWMWLU7O.js.map} +0 -0
  133. /package/dist/{composite-router-Y6UCDBDE.js.map → composite-router-HQUSCJO4.js.map} +0 -0
  134. /package/dist/{consensus-vote-PEWNN4WK.js.map → consensus-vote-E2WJFEIJ.js.map} +0 -0
  135. /package/dist/{context-retriever-GRMH7WSS.js.map → context-retriever-5OVRAWNZ.js.map} +0 -0
  136. /package/dist/{dist-OFXI4GYH.js.map → dist-C4457XNQ.js.map} +0 -0
  137. /package/dist/{doctor-deep-HXKGXUI4.js.map → doctor-deep-HJ3NQ365.js.map} +0 -0
  138. /package/dist/{expert-bridge-BW55FRAH.js.map → expert-bridge-MINXBC73.js.map} +0 -0
  139. /package/dist/{expert-config-OIIUMJFB.js.map → expert-config-53EBA7DT.js.map} +0 -0
  140. /package/dist/{factory-4EV6EDZF.js.map → factory-KC7BYHAP.js.map} +0 -0
  141. /package/dist/{factory-YKXAMYW6.js.map → factory-ZG2FZYVA.js.map} +0 -0
  142. /package/dist/{improvement-review-N2KSQSNW.js.map → improvement-review-TDC4QH5P.js.map} +0 -0
  143. /package/dist/{issue-triage-PYHGDQHK.js.map → issue-triage-CZPFNGB7.js.map} +0 -0
  144. /package/dist/{learning-persistence-C737Q6EP.js.map → learning-persistence-QFCQ5POD.js.map} +0 -0
  145. /package/dist/{outcome-store-adapter-D3UDQFNQ.js.map → outcome-store-adapter-K4L3RUJE.js.map} +0 -0
  146. /package/dist/{pr-reviewer-helpers-GC6DEIBT.js.map → pr-reviewer-helpers-PLKDVWYF.js.map} +0 -0
  147. /package/dist/{registry-command-7RRDCDAU.js.map → registry-command-HPKNLYUL.js.map} +0 -0
  148. /package/dist/{repo-analyze-JZEMBE6R.js.map → repo-analyze-UJOWFGNM.js.map} +0 -0
  149. /package/dist/{repo-security-plan-5V43DEGA.js.map → repo-security-plan-HSYGGVEJ.js.map} +0 -0
  150. /package/dist/{research-helpers-synthesize-EDT6REQJ.js.map → research-helpers-synthesize-IQHDU27S.js.map} +0 -0
  151. /package/dist/{routing-memory-BQ6MUC5Y.js.map → routing-memory-OGUOL7UF.js.map} +0 -0
  152. /package/dist/{session-memory-MJHN5FXI.js.map → session-memory-HQWAXH3C.js.map} +0 -0
  153. /package/dist/{setup-command-YAQE2KRH.js.map → setup-command-CNYIKBY7.js.map} +0 -0
  154. /package/dist/{setup-config-OFC6SD7H.js.map → setup-config-HYX3BRSE.js.map} +0 -0
  155. /package/dist/{setup-custom-api-3JEENEKA.js.map → setup-custom-api-W7EDVKWR.js.map} +0 -0
  156. /package/dist/{tool-memory-NKBBVA7O.js.map → tool-memory-Z2B7CAH7.js.map} +0 -0
  157. /package/dist/{unified-registry-XXAVF4AQ.js.map → unified-registry-UFNSWKZA.js.map} +0 -0
  158. /package/dist/{weather-report-7WC3RKE7.js.map → weather-report-GX7OIUL2.js.map} +0 -0
@@ -1,3 +1,6 @@
1
+ import {
2
+ computeCostUSD
3
+ } from "./chunk-TGGR6ST5.js";
1
4
  import {
2
5
  DEFAULT_CONSENSUS_CONFIG,
3
6
  DEFAULT_INCREMENTAL_QUORUM_CONFIG,
@@ -15,24 +18,25 @@ import {
15
18
  toolSuccess,
16
19
  withProgressHeartbeat,
17
20
  wrapToolWithTimeout
18
- } from "./chunk-4SNH5EMQ.js";
21
+ } from "./chunk-4CLBJ2NA.js";
19
22
  import {
20
23
  getToolMemory
21
- } from "./chunk-NINNWQ42.js";
24
+ } from "./chunk-GYDO2WOR.js";
22
25
  import {
23
26
  createEvent,
24
27
  getGlobalRegistry
25
- } from "./chunk-BDERGLIX.js";
28
+ } from "./chunk-XIFOAZ5B.js";
26
29
  import {
27
30
  authRemediation,
28
31
  delay,
29
32
  getAvailableClis,
30
33
  withTimeout
31
- } from "./chunk-2QFUW2SP.js";
34
+ } from "./chunk-VZCQRLXT.js";
32
35
  import {
33
36
  AgentError,
34
37
  CLI_NAMES,
35
38
  DEFAULT_CLI,
39
+ DecisionCostStore,
36
40
  OPERATION_CLASSES,
37
41
  OutcomeStore,
38
42
  TaskOutcomeSchema,
@@ -54,16 +58,17 @@ import {
54
58
  registerPersistentOutcomeStoreFactory,
55
59
  resolveClassGuardMs,
56
60
  resolveVoteTimeout
57
- } from "./chunk-4KBG4ICZ.js";
61
+ } from "./chunk-QJIHSQZX.js";
58
62
  import {
59
63
  ensureLearningDir,
64
+ findRepoRoot,
60
65
  getOutcomesFile,
61
66
  nexusDataPath,
62
67
  nexusDataPathEnsure
63
- } from "./chunk-ZGLIHPGJ.js";
68
+ } from "./chunk-RUSP5PJR.js";
64
69
 
65
70
  // src/mcp/tools/consensus-vote.ts
66
- import { z as z9 } from "zod";
71
+ import { z as z10 } from "zod";
67
72
 
68
73
  // src/cli/vote-types.ts
69
74
  var VOTER_ROLES = {
@@ -303,6 +308,7 @@ var SyntheticVoteError = class extends Error {
303
308
  this.rawOutput = rawOutput;
304
309
  this.name = "SyntheticVoteError";
305
310
  }
311
+ rawOutput;
306
312
  };
307
313
  var RawFindingSchema = z.object({
308
314
  summary: z.string().min(1).max(500).describe("One-line summary of the issue"),
@@ -677,6 +683,9 @@ function buildVoteRequest(role, proposal, timeoutMs, withResponseFormat) {
677
683
  function isStructuredOutputUnsupported(errorMessage) {
678
684
  return /support tool use/i.test(errorMessage);
679
685
  }
686
+ function readTokenCount(value) {
687
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
688
+ }
680
689
  async function runVoteCompletion(role, proposal, adapter, timeoutMs, withResponseFormat) {
681
690
  const request = buildVoteRequest(role, proposal, timeoutMs, withResponseFormat);
682
691
  const timeoutResult = await withTimeout(
@@ -687,7 +696,12 @@ async function runVoteCompletion(role, proposal, adapter, timeoutMs, withRespons
687
696
  if (!timeoutResult.ok) return { ok: false, error: timeoutResult.error };
688
697
  const response = timeoutResult.value;
689
698
  if (!response.ok) return { ok: false, error: response.error.message };
690
- return { ok: true, output: extractTextFromResponse(response.value.content) };
699
+ const reported = response.value.usage;
700
+ const usage = {
701
+ inputTokens: readTokenCount(reported?.inputTokens),
702
+ outputTokens: readTokenCount(reported?.outputTokens)
703
+ };
704
+ return { ok: true, output: extractTextFromResponse(response.value.content), usage };
691
705
  }
692
706
  async function executeSingleVoteAttempt(role, proposal, adapter, timeoutMs) {
693
707
  let completion = await runVoteCompletion(role, proposal, adapter, timeoutMs, true);
@@ -697,7 +711,7 @@ async function executeSingleVoteAttempt(role, proposal, adapter, timeoutMs) {
697
711
  if (!completion.ok) return { ok: false, error: completion.error };
698
712
  try {
699
713
  const vote = parseVoteResponse(completion.output, role);
700
- return { ok: true, vote, output: completion.output };
714
+ return { ok: true, vote, output: completion.output, usage: completion.usage };
701
715
  } catch (error) {
702
716
  if (error instanceof SyntheticVoteError) {
703
717
  return { ok: false, error: `Vote parsing failed: ${error.message}` };
@@ -726,7 +740,7 @@ async function executeWithRetries(opts) {
726
740
  attemptMs,
727
741
  succeeded: true
728
742
  });
729
- return { vote: result.vote, ok: true };
743
+ return { vote: result.vote, usage: result.usage, ok: true };
730
744
  }
731
745
  lastError = result.error;
732
746
  const rateLimited = isRateLimitError(lastError);
@@ -842,6 +856,18 @@ function computeOverallConsensusDeadlineMs(timeoutMs, maxRetries, roleCount, int
842
856
  }
843
857
  var DEFAULT_INTER_AGENT_DELAY_MS = 2e3;
844
858
  var defaultLogger = createLogger({ component: "voter-agents" });
859
+ function buildLlmVoteResult(role, vote, usage, adapter, processingTimeMs) {
860
+ return {
861
+ role,
862
+ vote,
863
+ processingTimeMs,
864
+ source: "llm",
865
+ cli: adapter.providerId,
866
+ model: adapter.modelId,
867
+ ...usage.inputTokens !== void 0 ? { inputTokens: usage.inputTokens } : {},
868
+ ...usage.outputTokens !== void 0 ? { outputTokens: usage.outputTokens } : {}
869
+ };
870
+ }
845
871
  async function executeAgentVote(role, proposal, adapter, logger10, options) {
846
872
  const start = getTimeProvider().now();
847
873
  const timeoutMs = options?.timeoutMs ?? resolveVoteTimeout();
@@ -859,7 +885,7 @@ async function executeAgentVote(role, proposal, adapter, logger10, options) {
859
885
  const processingTimeMs = getTimeProvider().now() - start;
860
886
  if (result.ok) {
861
887
  logger10.info("Vote completed", { role, model: adapter.modelId, decision: result.vote.decision });
862
- return { role, vote: result.vote, processingTimeMs, source: "llm", cli: adapter.providerId };
888
+ return buildLlmVoteResult(role, result.vote, result.usage, adapter, processingTimeMs);
863
889
  }
864
890
  logger10.error("Vote execution failed after all retries", void 0, {
865
891
  role,
@@ -4179,7 +4205,7 @@ function panelDegradationWarning(errorCount, total) {
4179
4205
  if (errorCount <= 0 || errorCount >= total) return void 0;
4180
4206
  return `Panel degraded: ${String(errorCount)} of ${String(total)} voters errored; decision rests on ${String(total - errorCount)} voter(s).`;
4181
4207
  }
4182
- function buildResponse(input, result) {
4208
+ function buildResponse(input, result, costSummary) {
4183
4209
  const proposalTruncated = input.proposal.length > 200 ? input.proposal.slice(0, 200) + "..." : input.proposal;
4184
4210
  const errorCount = result.votes.filter((v) => v.source === "error").length;
4185
4211
  const allErrors = errorCount === result.votes.length && errorCount > 0;
@@ -4199,6 +4225,10 @@ function buildResponse(input, result) {
4199
4225
  durationMs: result.totalTimeMs,
4200
4226
  simulateVotes: result.simulateVotes
4201
4227
  };
4228
+ applyOptionalResponseFields(response, input, result, errorCount, costSummary);
4229
+ return response;
4230
+ }
4231
+ function applyOptionalResponseFields(response, input, result, errorCount, costSummary) {
4202
4232
  if (input.threshold !== void 0) {
4203
4233
  response.threshold = input.threshold;
4204
4234
  }
@@ -4212,7 +4242,9 @@ function buildResponse(input, result) {
4212
4242
  if (isHigherOrderStrategy(result.strategy) && result.higherOrderResult) {
4213
4243
  response.higherOrderMetadata = toHigherOrderMetadata(result.higherOrderResult);
4214
4244
  }
4215
- return response;
4245
+ if (costSummary !== void 0) {
4246
+ response.costSummary = costSummary;
4247
+ }
4216
4248
  }
4217
4249
  function toHigherOrderMetadata(r) {
4218
4250
  return {
@@ -4264,8 +4296,178 @@ function applyErrorPolicy(votes, policy) {
4264
4296
  };
4265
4297
  }
4266
4298
 
4299
+ // src/audit/vote-record-store.ts
4300
+ import { appendFileSync as appendFileSync2, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2 } from "fs";
4301
+ import { dirname, join } from "path";
4302
+
4303
+ // src/audit/vote-record.ts
4304
+ import * as crypto from "crypto";
4305
+ import { z as z7 } from "zod";
4306
+ var VoteRecordDecisionSchema = z7.enum(["approved", "rejected", "no_quorum"]);
4307
+ var VoterSummarySchema = z7.object({
4308
+ role: z7.string().min(1).max(100),
4309
+ decision: z7.enum(["approve", "reject", "abstain"]),
4310
+ confidence: z7.number().min(0).max(1)
4311
+ }).strict();
4312
+ var VoteRecordCountsSchema = z7.object({
4313
+ approve: z7.number().int().nonnegative(),
4314
+ reject: z7.number().int().nonnegative(),
4315
+ abstain: z7.number().int().nonnegative(),
4316
+ total: z7.number().int().nonnegative()
4317
+ }).strict();
4318
+ var VoteRecordSchema = z7.object({
4319
+ /** Schema version for forward migrations. */
4320
+ version: z7.literal("1.0"),
4321
+ /** Unique record id (also usable as a `ratificationVoteRef`). */
4322
+ id: z7.string().min(1),
4323
+ /** ISO-8601 timestamp the vote was recorded. */
4324
+ recordedAt: z7.string().min(1),
4325
+ /**
4326
+ * SHA-256 of the FULL proposal text. The proposal itself is truncated for
4327
+ * the human record (`proposal`), but the hash binds the record to the exact
4328
+ * proposal voted on — a later edit of `proposal` that changes meaning is
4329
+ * detectable by recomputing this hash from the original.
4330
+ */
4331
+ proposalHash: z7.string().length(64),
4332
+ /** Truncated proposal text for the human/reviewer record. */
4333
+ proposal: z7.string(),
4334
+ /** The voting strategy used. */
4335
+ strategy: z7.enum([
4336
+ "simple_majority",
4337
+ "supermajority",
4338
+ "unanimous",
4339
+ "higher_order",
4340
+ "opinion_wise",
4341
+ "proof_of_learning"
4342
+ ]),
4343
+ /** The resolved decision. */
4344
+ decision: VoteRecordDecisionSchema,
4345
+ /** Approval fraction as a percentage (0-100). */
4346
+ approvalPercentage: z7.number().min(0).max(100),
4347
+ /** Vote-count breakdown. */
4348
+ voteCounts: VoteRecordCountsSchema,
4349
+ /** Per-voter {role, decision, confidence} summary. */
4350
+ voters: z7.array(VoterSummarySchema),
4351
+ /** Optional correlation/decision id linking to the cost rollup / trace. */
4352
+ correlationId: z7.string().min(1).optional(),
4353
+ /** Hash of the previous record in the chain (absent for the first). */
4354
+ previousHash: z7.string().length(64).optional(),
4355
+ /** SHA-256 over every field above + previousHash. */
4356
+ hash: z7.string().length(64)
4357
+ }).strict();
4358
+ function computeVoteRecordHash(payload) {
4359
+ const canonical = JSON.stringify({
4360
+ version: payload.version,
4361
+ id: payload.id,
4362
+ recordedAt: payload.recordedAt,
4363
+ proposalHash: payload.proposalHash,
4364
+ proposal: payload.proposal,
4365
+ strategy: payload.strategy,
4366
+ decision: payload.decision,
4367
+ approvalPercentage: payload.approvalPercentage,
4368
+ voteCounts: payload.voteCounts,
4369
+ voters: payload.voters.map((v) => ({
4370
+ role: v.role,
4371
+ decision: v.decision,
4372
+ confidence: v.confidence
4373
+ })),
4374
+ correlationId: payload.correlationId ?? null,
4375
+ previousHash: payload.previousHash ?? null
4376
+ });
4377
+ return crypto.createHash("sha256").update(canonical).digest("hex");
4378
+ }
4379
+ function hashProposal(proposal) {
4380
+ return crypto.createHash("sha256").update(proposal).digest("hex");
4381
+ }
4382
+
4383
+ // src/audit/vote-record-store.ts
4384
+ var VOTE_RECORDS_REL_PATH = "governance/vote-records.jsonl";
4385
+ var MAX_PROPOSAL_RECORD_CHARS = 500;
4386
+ function outcomeToDecision(result) {
4387
+ if (result.outcome === "approved") return "approved";
4388
+ if (!result.quorumReached && result.outcome !== "rejected") return "no_quorum";
4389
+ return "rejected";
4390
+ }
4391
+ function toVoterSummaries(votes) {
4392
+ const summaries = [];
4393
+ for (const v of votes) {
4394
+ if (v.source === "error") continue;
4395
+ const vote = v.vote;
4396
+ summaries.push({ role: v.role, decision: vote.decision, confidence: vote.confidence });
4397
+ }
4398
+ return summaries;
4399
+ }
4400
+ function buildVoteRecord(input) {
4401
+ const proposalTruncated = input.proposal.length > MAX_PROPOSAL_RECORD_CHARS ? input.proposal.slice(0, MAX_PROPOSAL_RECORD_CHARS) + "..." : input.proposal;
4402
+ const payload = {
4403
+ version: "1.0",
4404
+ id: input.id,
4405
+ recordedAt: input.recordedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
4406
+ proposalHash: hashProposal(input.proposal),
4407
+ proposal: proposalTruncated,
4408
+ strategy: input.strategy,
4409
+ decision: outcomeToDecision(input.result),
4410
+ approvalPercentage: input.result.approvalPercentage,
4411
+ voteCounts: {
4412
+ approve: input.result.voteCounts.approve,
4413
+ reject: input.result.voteCounts.reject,
4414
+ abstain: input.result.voteCounts.abstain,
4415
+ total: input.result.voteCounts.total
4416
+ },
4417
+ voters: toVoterSummaries(input.votes),
4418
+ ...input.correlationId !== void 0 ? { correlationId: input.correlationId } : {},
4419
+ ...input.previousHash !== void 0 ? { previousHash: input.previousHash } : {}
4420
+ };
4421
+ return { ...payload, hash: computeVoteRecordHash(payload) };
4422
+ }
4423
+ function readLastHash(filePath, logger10) {
4424
+ if (!existsSync2(filePath)) return void 0;
4425
+ try {
4426
+ const content = readFileSync2(filePath, "utf-8");
4427
+ const lines = content.split("\n").filter((l) => l.trim() !== "");
4428
+ const last = lines[lines.length - 1];
4429
+ if (last === void 0) return void 0;
4430
+ const parsed = VoteRecordSchema.safeParse(JSON.parse(last));
4431
+ return parsed.success ? parsed.data.hash : void 0;
4432
+ } catch (error) {
4433
+ logger10.warn("Failed to read prior vote record hash", { error: getErrorMessage(error) });
4434
+ return void 0;
4435
+ }
4436
+ }
4437
+ function resolveVoteRecordsPath() {
4438
+ const root = findRepoRoot(process.cwd());
4439
+ if (root === null) return void 0;
4440
+ return join(root, VOTE_RECORDS_REL_PATH);
4441
+ }
4442
+ function persistVoteRecord(opts) {
4443
+ const logger10 = opts.logger ?? createLogger({ component: "vote-record-store" });
4444
+ const filePath = opts.filePath ?? resolveVoteRecordsPath();
4445
+ if (filePath === void 0) {
4446
+ logger10.debug("No committable repo root; skipping authentic vote record persist");
4447
+ return void 0;
4448
+ }
4449
+ try {
4450
+ mkdirSync2(dirname(filePath), { recursive: true });
4451
+ const previousHash = readLastHash(filePath, logger10);
4452
+ const record = buildVoteRecord({ ...opts, previousHash });
4453
+ appendFileSync2(filePath, JSON.stringify(record) + "\n", "utf-8");
4454
+ logger10.info("Persisted authentic vote record", {
4455
+ id: record.id,
4456
+ decision: record.decision,
4457
+ path: filePath
4458
+ });
4459
+ return record;
4460
+ } catch (error) {
4461
+ logger10.warn("Failed to persist authentic vote record", {
4462
+ error: getErrorMessage(error),
4463
+ path: filePath
4464
+ });
4465
+ return void 0;
4466
+ }
4467
+ }
4468
+
4267
4469
  // src/orchestration/outcomes/outcome-store-persistence.ts
4268
- import { appendFileSync as appendFileSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync2 } from "fs";
4470
+ import { appendFileSync as appendFileSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync3 } from "fs";
4269
4471
  var PersistentOutcomeStore = class extends OutcomeStore {
4270
4472
  filePath;
4271
4473
  logger;
@@ -4316,14 +4518,14 @@ var PersistentOutcomeStore = class extends OutcomeStore {
4316
4518
  }
4317
4519
  }
4318
4520
  hydrate() {
4319
- if (!existsSync2(this.filePath)) {
4521
+ if (!existsSync3(this.filePath)) {
4320
4522
  this.logger.debug("No outcomes file found, starting fresh", {
4321
4523
  path: this.filePath
4322
4524
  });
4323
4525
  return;
4324
4526
  }
4325
4527
  try {
4326
- const content = readFileSync2(this.filePath, "utf-8");
4528
+ const content = readFileSync3(this.filePath, "utf-8");
4327
4529
  const lines = content.split("\n").filter((line) => line.trim().length > 0);
4328
4530
  let loaded = 0;
4329
4531
  let skipped = 0;
@@ -4375,7 +4577,7 @@ var PersistentOutcomeStore = class extends OutcomeStore {
4375
4577
  }
4376
4578
  persistLine(outcome) {
4377
4579
  try {
4378
- appendFileSync2(this.filePath, JSON.stringify(outcome) + "\n", "utf-8");
4580
+ appendFileSync3(this.filePath, JSON.stringify(outcome) + "\n", "utf-8");
4379
4581
  } catch (error) {
4380
4582
  const msg = getErrorMessage(error);
4381
4583
  this.logger.warn("Failed to persist outcome to disk", {
@@ -4418,6 +4620,34 @@ function recordVoteSuccess(proposal, strategy, outcome, duration, votes) {
4418
4620
  recordVoteOutcomes(votes);
4419
4621
  }
4420
4622
  }
4623
+ var VOTE_RECORD_STRATEGIES = /* @__PURE__ */ new Set([
4624
+ "simple_majority",
4625
+ "supermajority",
4626
+ "unanimous",
4627
+ "higher_order",
4628
+ "opinion_wise",
4629
+ "proof_of_learning"
4630
+ ]);
4631
+ function toRecordStrategy(strategy) {
4632
+ return VOTE_RECORD_STRATEGIES.has(strategy) ? strategy : "simple_majority";
4633
+ }
4634
+ function recordAuthenticVote(args) {
4635
+ const allSimulated = args.votes.length > 0 && args.votes.every((v) => v.source === "simulation");
4636
+ if (allSimulated) {
4637
+ logger6.debug("Skipping authentic vote record \u2014 all votes simulated");
4638
+ return void 0;
4639
+ }
4640
+ const id = `vote-${String(getTimeProvider().now())}-${getRandomProvider().random().toString(36).slice(2, 9)}`;
4641
+ return persistVoteRecord({
4642
+ id,
4643
+ proposal: args.proposal,
4644
+ strategy: toRecordStrategy(args.strategy),
4645
+ result: args.result,
4646
+ votes: args.votes,
4647
+ ...args.correlationId !== void 0 ? { correlationId: args.correlationId } : {},
4648
+ logger: logger6
4649
+ });
4650
+ }
4421
4651
  function recordVoteError(proposal, errorMessage) {
4422
4652
  try {
4423
4653
  const memory = getToolMemory();
@@ -4461,6 +4691,75 @@ function recordVoteOutcomes(votes) {
4461
4691
  }
4462
4692
  }
4463
4693
 
4694
+ // src/mcp/tools/decision-cost-recording.ts
4695
+ function resolveBillingMode() {
4696
+ return process.env["NEXUS_BILLING_MODE"] === "api" ? "api" : "plan";
4697
+ }
4698
+ function votesToCostInputs(votes) {
4699
+ return votes.map((v) => {
4700
+ const hasTokens = v.inputTokens !== void 0 || v.outputTokens !== void 0;
4701
+ const input = {
4702
+ role: v.role,
4703
+ model: v.model,
4704
+ ...v.inputTokens !== void 0 ? { inputTokens: v.inputTokens } : {},
4705
+ ...v.outputTokens !== void 0 ? { outputTokens: v.outputTokens } : {},
4706
+ ...hasTokens && v.model !== void 0 ? { costUsd: computeCostUSD(v.model, v.inputTokens ?? 0, v.outputTokens ?? 0) } : {}
4707
+ };
4708
+ return input;
4709
+ });
4710
+ }
4711
+ var defaultLogger2 = createLogger({ component: "decision-cost-recording" });
4712
+ var droppedCostRecordCount = 0;
4713
+ var WARN_BURST = 5;
4714
+ var WARN_PERIOD = 1e3;
4715
+ var WARN_MAX_SILENCE_MS = 6e4;
4716
+ var warnCountSinceReset = 0;
4717
+ var lastWarnAtMs = 0;
4718
+ var warnClockOverrideMs = null;
4719
+ function warnNowMs() {
4720
+ return warnClockOverrideMs ?? Date.now();
4721
+ }
4722
+ function shouldWarnForDrop(dropIndex) {
4723
+ const now = warnNowMs();
4724
+ const countTrigger = dropIndex <= WARN_BURST || (dropIndex - WARN_BURST) % WARN_PERIOD === 0;
4725
+ const timeTrigger = dropIndex > WARN_BURST && now - lastWarnAtMs >= WARN_MAX_SILENCE_MS;
4726
+ if (countTrigger || timeTrigger) {
4727
+ lastWarnAtMs = now;
4728
+ return true;
4729
+ }
4730
+ return false;
4731
+ }
4732
+ function recordDecisionCost(options) {
4733
+ const billingMode = options.billingMode ?? resolveBillingMode();
4734
+ const voters = votesToCostInputs(options.votes);
4735
+ const store = options.store ?? new DecisionCostStore();
4736
+ const logger10 = options.logger ?? defaultLogger2;
4737
+ const timestamp = new Date(getTimeProvider().now()).toISOString();
4738
+ const { record, persisted } = store.record({
4739
+ decisionId: options.decisionId,
4740
+ gate: options.gate,
4741
+ voters,
4742
+ billingMode,
4743
+ timestamp
4744
+ });
4745
+ if (!persisted) {
4746
+ droppedCostRecordCount += 1;
4747
+ if (shouldWarnForDrop(droppedCostRecordCount)) {
4748
+ const suppressedSinceLastWarn = droppedCostRecordCount - warnCountSinceReset - 1;
4749
+ warnCountSinceReset += 1;
4750
+ logger10.warn("Decision-cost rollup dropped (failed to persist) \u2014 billing telemetry lost", {
4751
+ decisionId: options.decisionId,
4752
+ gate: options.gate,
4753
+ totalCostUsd: record.summary.totalCostUsd,
4754
+ totalTokens: record.summary.totalTokens,
4755
+ droppedCostRecordCount,
4756
+ suppressedSinceLastWarn
4757
+ });
4758
+ }
4759
+ }
4760
+ return record.summary;
4761
+ }
4762
+
4464
4763
  // src/mcp/tools/consensus-vote-signals.ts
4465
4764
  function rejectionRulesFrom(result) {
4466
4765
  const rules = /* @__PURE__ */ new Set();
@@ -4502,31 +4801,31 @@ function warnIfSimulatedOutsideTests(toolName, logger10) {
4502
4801
  }
4503
4802
 
4504
4803
  // src/mcp/jobs/job-result-store.ts
4505
- import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync, writeFileSync as writeFileSync3, chmodSync } from "fs";
4506
- import { z as z7 } from "zod";
4804
+ import { existsSync as existsSync4, readFileSync as readFileSync4, readdirSync, writeFileSync as writeFileSync3, chmodSync } from "fs";
4805
+ import { z as z8 } from "zod";
4507
4806
  var logger7 = createLogger({ component: "job-result-store" });
4508
- var JobStatusSchema = z7.enum(["pending", "complete", "failed", "cancelled"]);
4509
- var JobResultSchema = z7.object({
4807
+ var JobStatusSchema = z8.enum(["pending", "complete", "failed", "cancelled"]);
4808
+ var JobResultSchema = z8.object({
4510
4809
  /** Format version. Currently `1` — bump if the shape changes. */
4511
- v: z7.literal(1),
4512
- jobId: z7.string().min(1),
4810
+ v: z8.literal(1),
4811
+ jobId: z8.string().min(1),
4513
4812
  /** Tool that was invoked (e.g. `orchestrate`). */
4514
- toolName: z7.string().min(1),
4813
+ toolName: z8.string().min(1),
4515
4814
  status: JobStatusSchema,
4516
- createdAt: z7.iso.datetime(),
4815
+ createdAt: z8.iso.datetime(),
4517
4816
  /** Set when the job leaves `pending` — either via `complete` or `failed` or `cancelled`. */
4518
- completedAt: z7.iso.datetime().optional(),
4817
+ completedAt: z8.iso.datetime().optional(),
4519
4818
  /**
4520
4819
  * Structured payload the synchronous mode would have returned. Present
4521
4820
  * only when `status === 'complete'`. Shape is tool-specific — readers
4522
4821
  * cast to the tool's known output type after status check.
4523
4822
  */
4524
- result: z7.unknown().optional(),
4823
+ result: z8.unknown().optional(),
4525
4824
  /**
4526
4825
  * Failure message when `status === 'failed'`. Cannot be paired with
4527
4826
  * `result` — the discriminator is `status`.
4528
4827
  */
4529
- error: z7.string().optional()
4828
+ error: z8.string().optional()
4530
4829
  });
4531
4830
  function jobResultPath(jobId) {
4532
4831
  return nexusDataPathEnsure("jobs", `result-${jobId}.json`);
@@ -4537,7 +4836,7 @@ function persistJobRecord(path, record) {
4537
4836
  }
4538
4837
  function writeJobPending(jobId, toolName) {
4539
4838
  const path = jobResultPath(jobId);
4540
- if (existsSync3(path)) {
4839
+ if (existsSync4(path)) {
4541
4840
  logger7.debug("Job result file already exists \u2014 leaving in place", { jobId });
4542
4841
  return;
4543
4842
  }
@@ -4592,9 +4891,9 @@ function writeJobCancelled(jobId, toolName, reason) {
4592
4891
  }
4593
4892
  function readJobResult(jobId) {
4594
4893
  const path = nexusDataPath("jobs", `result-${jobId}.json`);
4595
- if (!existsSync3(path)) return null;
4894
+ if (!existsSync4(path)) return null;
4596
4895
  try {
4597
- const raw = JSON.parse(readFileSync3(path, "utf-8"));
4896
+ const raw = JSON.parse(readFileSync4(path, "utf-8"));
4598
4897
  const parsed = JobResultSchema.safeParse(raw);
4599
4898
  if (!parsed.success) {
4600
4899
  logger7.warn("Job result file failed schema check", { jobId, path });
@@ -4622,7 +4921,7 @@ function toJobSummary(record) {
4622
4921
  }
4623
4922
  function listJobs() {
4624
4923
  const dir = nexusDataPath("jobs");
4625
- if (!existsSync3(dir)) return [];
4924
+ if (!existsSync4(dir)) return [];
4626
4925
  let entries;
4627
4926
  try {
4628
4927
  entries = readdirSync(dir);
@@ -4647,25 +4946,25 @@ function listJobs() {
4647
4946
  }
4648
4947
 
4649
4948
  // src/mcp/jobs/job-idempotency.ts
4650
- import { createHash, randomUUID } from "crypto";
4651
- import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
4652
- import { z as z8 } from "zod";
4949
+ import { createHash as createHash2, randomUUID } from "crypto";
4950
+ import { existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
4951
+ import { z as z9 } from "zod";
4653
4952
  var logger8 = createLogger({ component: "job-idempotency" });
4654
- var IdempotencyIndexEntrySchema = z8.object({
4655
- v: z8.literal(1),
4953
+ var IdempotencyIndexEntrySchema = z9.object({
4954
+ v: z9.literal(1),
4656
4955
  /** MCP tool name (e.g. `orchestrate`, `run_workflow`, `consensus_vote`). */
4657
- tool: z8.string().min(1),
4956
+ tool: z9.string().min(1),
4658
4957
  /** Caller-supplied idempotency key. */
4659
- key: z8.string().min(1),
4958
+ key: z9.string().min(1),
4660
4959
  /** sha256 of the canonical input JSON. */
4661
- inputsHash: z8.string().length(64),
4960
+ inputsHash: z9.string().length(64),
4662
4961
  /** Existing jobId for this (tool, key, inputs) tuple. */
4663
- jobId: z8.string().min(1),
4962
+ jobId: z9.string().min(1),
4664
4963
  /** ISO timestamp of first dispatch. */
4665
- createdAt: z8.iso.datetime()
4964
+ createdAt: z9.iso.datetime()
4666
4965
  });
4667
4966
  function sha256Hex(input) {
4668
- return createHash("sha256").update(input).digest("hex");
4967
+ return createHash2("sha256").update(input).digest("hex");
4669
4968
  }
4670
4969
  function canonicalJsonStringify(value) {
4671
4970
  if (value === null || typeof value !== "object") return JSON.stringify(value);
@@ -4690,7 +4989,7 @@ function resolveIdempotency(tool, idempotencyKey, inputs, randomFallback = rando
4690
4989
  }
4691
4990
  const incomingInputsHash = computeInputsHash(inputs);
4692
4991
  const path = indexFilePathReadOnly(tool, idempotencyKey);
4693
- if (existsSync4(path)) {
4992
+ if (existsSync5(path)) {
4694
4993
  const entry = readIndexEntry(path);
4695
4994
  if (entry !== null) {
4696
4995
  if (entry.inputsHash === incomingInputsHash) {
@@ -4710,7 +5009,7 @@ function resolveIdempotency(tool, idempotencyKey, inputs, randomFallback = rando
4710
5009
  }
4711
5010
  function registerIdempotentJob(params) {
4712
5011
  const path = indexFilePath(params.tool, params.idempotencyKey);
4713
- if (existsSync4(path)) {
5012
+ if (existsSync5(path)) {
4714
5013
  logger8.debug("Idempotency index entry already present; skipping write", {
4715
5014
  tool: params.tool,
4716
5015
  jobId: params.jobId
@@ -4745,7 +5044,7 @@ function shortCircuitOrFreshJobId(params) {
4745
5044
  }
4746
5045
  function readIndexEntry(path) {
4747
5046
  try {
4748
- const raw = JSON.parse(readFileSync4(path, "utf-8"));
5047
+ const raw = JSON.parse(readFileSync5(path, "utf-8"));
4749
5048
  const parsed = IdempotencyIndexEntrySchema.safeParse(raw);
4750
5049
  if (!parsed.success) {
4751
5050
  logger8.warn("Idempotency index file failed schema check", { path });
@@ -5153,7 +5452,7 @@ async function processVotesWithCascade(engineVotes, opts) {
5153
5452
  var CONTRARIAN_ESCALATION_THRESHOLD = 0.8;
5154
5453
  async function runContrarianCheck(proposal, log) {
5155
5454
  try {
5156
- const { executeExpert } = await import("./expert-bridge-BW55FRAH.js");
5455
+ const { executeExpert } = await import("./expert-bridge-MINXBC73.js");
5157
5456
  const prompt = [
5158
5457
  "You are a contrarian analyst. Your job is to find reasons this proposal should be REJECTED.",
5159
5458
  "Look for: YAGNI (not needed), MISALIGNED (wrong tech/architecture), SECURITY_RISK, SCOPE_CREEP.",
@@ -5309,6 +5608,24 @@ function finalizeVotingResult(args) {
5309
5608
  if (args.higherOrderResult !== void 0) result.higherOrderResult = args.higherOrderResult;
5310
5609
  return result;
5311
5610
  }
5611
+ function recordVoteSideEffects(proposal, strategy, result, logger10) {
5612
+ const decisionId = `consensus-${String(getTimeProvider().now())}-${randomUUID2().slice(0, 8)}`;
5613
+ recordAuthenticVote({
5614
+ proposal,
5615
+ strategy,
5616
+ result: result.result,
5617
+ votes: result.votes,
5618
+ correlationId: decisionId
5619
+ });
5620
+ try {
5621
+ return recordDecisionCost({ decisionId, gate: "consensus_vote", votes: result.votes });
5622
+ } catch (costError) {
5623
+ logger10.warn("Per-decision cost rollup failed (non-fatal)", {
5624
+ error: getErrorMessage(costError)
5625
+ });
5626
+ return void 0;
5627
+ }
5628
+ }
5312
5629
  async function handleConsensusVote(deps, args) {
5313
5630
  const logger10 = deps.logger ?? createLogger({ tool: "consensus_vote" });
5314
5631
  if (args.simulateVotes) warnIfSimulatedOutsideTests("consensus_vote", logger10);
@@ -5332,8 +5649,9 @@ async function handleConsensusVote(deps, args) {
5332
5649
  result.totalTimeMs,
5333
5650
  result.votes
5334
5651
  );
5652
+ const costSummary = recordVoteSideEffects(args.proposal, strategy, result, logger10);
5335
5653
  emitVoteRejectedSignal(result.result, getPipelineEventBus(), logger10);
5336
- return { ok: true, value: buildResponse(args, result) };
5654
+ return { ok: true, value: buildResponse(args, result, costSummary) };
5337
5655
  } catch (error) {
5338
5656
  const message = getErrorMessage(error);
5339
5657
  const cause = error instanceof Error ? error : new Error(message);
@@ -5414,27 +5732,27 @@ function createConsensusVoteHandler(deps) {
5414
5732
  };
5415
5733
  }
5416
5734
  var CONSENSUS_VOTE_OUTPUT_SCHEMA = {
5417
- proposal: z9.string(),
5735
+ proposal: z10.string(),
5418
5736
  strategy: VotingStrategySchema,
5419
- decision: z9.enum(["approved", "rejected", "no_quorum"]),
5420
- approvalPercentage: z9.number(),
5421
- voteCounts: z9.object({
5422
- approve: z9.number(),
5423
- reject: z9.number(),
5424
- abstain: z9.number(),
5425
- error: z9.number()
5737
+ decision: z10.enum(["approved", "rejected", "no_quorum"]),
5738
+ approvalPercentage: z10.number(),
5739
+ voteCounts: z10.object({
5740
+ approve: z10.number(),
5741
+ reject: z10.number(),
5742
+ abstain: z10.number(),
5743
+ error: z10.number()
5426
5744
  }),
5427
- votes: z9.array(
5428
- z9.object({
5429
- role: z9.string().max(100),
5430
- decision: z9.enum(["approve", "reject", "abstain"]),
5431
- confidence: z9.number(),
5432
- reasoning: z9.string().max(4e3),
5433
- simulated: z9.boolean(),
5434
- error: z9.boolean(),
5435
- modelUsed: z9.string().max(100).optional(),
5436
- rejectionCategories: z9.array(
5437
- z9.enum([
5745
+ votes: z10.array(
5746
+ z10.object({
5747
+ role: z10.string().max(100),
5748
+ decision: z10.enum(["approve", "reject", "abstain"]),
5749
+ confidence: z10.number(),
5750
+ reasoning: z10.string().max(4e3),
5751
+ simulated: z10.boolean(),
5752
+ error: z10.boolean(),
5753
+ modelUsed: z10.string().max(100).optional(),
5754
+ rejectionCategories: z10.array(
5755
+ z10.enum([
5438
5756
  "YAGNI",
5439
5757
  "DRY_VIOLATION",
5440
5758
  "OVER_ENGINEERING",
@@ -5447,34 +5765,34 @@ var CONSENSUS_VOTE_OUTPUT_SCHEMA = {
5447
5765
  })
5448
5766
  ),
5449
5767
  threshold: VoteThresholdSchema.optional(),
5450
- durationMs: z9.number(),
5451
- simulateVotes: z9.boolean(),
5452
- higherOrderMetadata: z9.object({
5453
- posteriorApproval: z9.number(),
5454
- posteriorRejection: z9.number(),
5455
- effectiveVoteCount: z9.number(),
5456
- method: z9.enum(["ow", "isp", "simple"]),
5457
- usedCorrelationData: z9.boolean(),
5458
- improvementOverBaseline: z9.number(),
5459
- downweightedAgents: z9.array(z9.string().max(100)).max(10),
5460
- reasoning: z9.string().max(2e3)
5768
+ durationMs: z10.number(),
5769
+ simulateVotes: z10.boolean(),
5770
+ higherOrderMetadata: z10.object({
5771
+ posteriorApproval: z10.number(),
5772
+ posteriorRejection: z10.number(),
5773
+ effectiveVoteCount: z10.number(),
5774
+ method: z10.enum(["ow", "isp", "simple"]),
5775
+ usedCorrelationData: z10.boolean(),
5776
+ improvementOverBaseline: z10.number(),
5777
+ downweightedAgents: z10.array(z10.string().max(100)).max(10),
5778
+ reasoning: z10.string().max(2e3)
5461
5779
  }).optional(),
5462
5780
  // #3124: explains a `rejected` decision that coexists with a high
5463
5781
  // approvalPercentage (an error-policy short-circuit, e.g. fail_closed).
5464
- policyReason: z9.string().max(200).optional()
5782
+ policyReason: z10.string().max(200).optional()
5465
5783
  };
5466
5784
  function registerConsensusVoteTool(server, deps) {
5467
5785
  const logger10 = deps.logger ?? createLogger({ tool: "consensus_vote" });
5468
5786
  const notifier = deps.notifier ?? createMcpNotifier(server);
5469
5787
  const depsWithNotifier = { ...deps, notifier };
5470
5788
  const toolSchema = {
5471
- proposal: z9.string().min(1).max(MAX_PROPOSAL_LENGTH).describe("Proposal text to vote on"),
5472
- threshold: z9.enum(["majority", "supermajority", "unanimous"]).optional().describe("Voting threshold (legacy). Use strategy instead."),
5789
+ proposal: z10.string().min(1).max(MAX_PROPOSAL_LENGTH).describe("Proposal text to vote on"),
5790
+ threshold: z10.enum(["majority", "supermajority", "unanimous"]).optional().describe("Voting threshold (legacy). Use strategy instead."),
5473
5791
  strategy: VotingStrategySchema.optional().describe(
5474
5792
  "Voting strategy: simple_majority (default), supermajority, unanimous, proof_of_learning, or higher_order"
5475
5793
  ),
5476
- quickMode: z9.boolean().optional().default(false).describe("Use 3 agents instead of 7"),
5477
- simulateVotes: z9.boolean().optional().default(false).describe("TESTS ONLY \u2014 random output, must not be used for real decisions (#2319)")
5794
+ quickMode: z10.boolean().optional().default(false).describe("Use 3 agents instead of 7"),
5795
+ simulateVotes: z10.boolean().optional().default(false).describe("TESTS ONLY \u2014 random output, must not be used for real decisions (#2319)")
5478
5796
  };
5479
5797
  const description = "Execute multi-model consensus voting on a proposal. Uses 7 specialized agent roles (architect, security, devex, ai_ml, pm, catfish, scope_steward) to vote on proposals with configurable strategies. Supports higher_order strategy for Bayesian-optimal aggregation with correlation awareness (Issue #514).";
5480
5798
  const secureHandler = createSecureHandler(createConsensusVoteHandler(depsWithNotifier), {
@@ -5565,6 +5883,7 @@ export {
5565
5883
  ErrorPolicySchema,
5566
5884
  VoteThresholdSchema,
5567
5885
  ConsensusVoteInputSchema,
5886
+ recordDecisionCost,
5568
5887
  warnIfSimulatedOutsideTests,
5569
5888
  resetCorrelationTracker,
5570
5889
  createPolicyFailedResult,
@@ -5573,4 +5892,4 @@ export {
5573
5892
  CONSENSUS_VOTE_OUTPUT_SCHEMA,
5574
5893
  registerConsensusVoteTool
5575
5894
  };
5576
- //# sourceMappingURL=chunk-DEL26PXS.js.map
5895
+ //# sourceMappingURL=chunk-I2SW34K6.js.map