nexus-agents 2.55.1 → 2.57.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 (106) hide show
  1. package/dist/{adaptive-memory-SUNFPY2W.js → adaptive-memory-TSZEJUJC.js} +3 -3
  2. package/dist/{chunk-4FVISCDB.js → chunk-6QU4DJYW.js} +2 -2
  3. package/dist/{chunk-NYNBDP7M.js → chunk-7Y36JLES.js} +2 -2
  4. package/dist/{chunk-FWSTXEG2.js → chunk-AFSIP6JH.js} +99 -23
  5. package/dist/chunk-AFSIP6JH.js.map +1 -0
  6. package/dist/{chunk-BPMQRYGU.js → chunk-BOZ26RIB.js} +283 -79
  7. package/dist/chunk-BOZ26RIB.js.map +1 -0
  8. package/dist/{chunk-VKZKC4SM.js → chunk-EDGG3RQE.js} +2 -2
  9. package/dist/{chunk-2SPRLBOS.js → chunk-EZXOJZYE.js} +2 -2
  10. package/dist/{chunk-W5AJRK4U.js → chunk-GMOGKX4E.js} +2 -2
  11. package/dist/chunk-H43PABG4.js +402 -0
  12. package/dist/chunk-H43PABG4.js.map +1 -0
  13. package/dist/{chunk-IOU7F5PH.js → chunk-K7EA5OV4.js} +2 -2
  14. package/dist/chunk-NUBSJGQZ.js +14 -0
  15. package/dist/chunk-NUBSJGQZ.js.map +1 -0
  16. package/dist/{chunk-KRIFBGWZ.js → chunk-PTGBJFSD.js} +3 -3
  17. package/dist/{chunk-EKYT4NMD.js → chunk-QGZBCD2A.js} +24 -9
  18. package/dist/chunk-QGZBCD2A.js.map +1 -0
  19. package/dist/{chunk-XYCS5X3H.js → chunk-QNYNQ257.js} +422 -72
  20. package/dist/chunk-QNYNQ257.js.map +1 -0
  21. package/dist/{chunk-UOUJZIKH.js → chunk-R2Y57PM3.js} +38 -15
  22. package/dist/chunk-R2Y57PM3.js.map +1 -0
  23. package/dist/{chunk-RSUCXOQM.js → chunk-SXWZS2V4.js} +26 -53
  24. package/dist/chunk-SXWZS2V4.js.map +1 -0
  25. package/dist/{chunk-JERFBN73.js → chunk-TC46TRLR.js} +7 -17
  26. package/dist/chunk-TC46TRLR.js.map +1 -0
  27. package/dist/{chunk-3IDJSFWT.js → chunk-TFEFN37P.js} +2 -2
  28. package/dist/{chunk-Q5TKQWOI.js → chunk-V5CGWMYL.js} +2 -2
  29. package/dist/{chunk-TCQNNY4J.js → chunk-XEMRMZUN.js} +4 -4
  30. package/dist/chunk-Y7CD6AZW.js +2136 -0
  31. package/dist/chunk-Y7CD6AZW.js.map +1 -0
  32. package/dist/{chunk-R66AWJJ7.js → chunk-YU4NABXM.js} +2 -2
  33. package/dist/{chunk-KVWHK72T.js → chunk-ZL3IBCH6.js} +2356 -3687
  34. package/dist/chunk-ZL3IBCH6.js.map +1 -0
  35. package/dist/{chunk-VLVHPC72.js → chunk-ZUWOFHNC.js} +6 -6
  36. package/dist/{cli-circuit-breaker-7MCMHSDY.js → cli-circuit-breaker-SL73NWX2.js} +4 -4
  37. package/dist/cli.d.ts +9 -2
  38. package/dist/cli.js +468 -311
  39. package/dist/cli.js.map +1 -1
  40. package/dist/{composite-router-LXFOSMSE.js → composite-router-A7URDW4X.js} +2 -2
  41. package/dist/{consensus-vote-G6H532ME.js → consensus-vote-CQ2JP6DC.js} +9 -8
  42. package/dist/{doctor-deep-RKMOZSIS.js → doctor-deep-VN6KMUCG.js} +3 -3
  43. package/dist/{expert-bridge-KHHE42JP.js → expert-bridge-LT7PKUPS.js} +3 -3
  44. package/dist/{factory-KRNR7XHD.js → factory-FA7WDPZW.js} +5 -5
  45. package/dist/{factory-JVN47MFN.js → factory-FZ2KSVYC.js} +4 -4
  46. package/dist/index.d.ts +324 -16
  47. package/dist/index.js +84 -68
  48. package/dist/index.js.map +1 -1
  49. package/dist/{issue-triage-O3C7P66H.js → issue-triage-YYTE6KTC.js} +4 -4
  50. package/dist/{mcp-config-QRERKGR4.js → mcp-config-34XMRM64.js} +3 -3
  51. package/dist/{mobimem-FAOAXV3B.js → mobimem-QDBP37H7.js} +2 -2
  52. package/dist/model-registry.generated.json +17033 -0
  53. package/dist/registry-command-S46JJ2SX.js +580 -0
  54. package/dist/registry-command-S46JJ2SX.js.map +1 -0
  55. package/dist/{repo-security-plan-AZ5HMGFC.js → repo-security-plan-C3LLE3Z7.js} +3 -3
  56. package/dist/research-helpers-synthesize-NVQIWLQL.js +11 -0
  57. package/dist/{routing-memory-MXF45FXT.js → routing-memory-W3YWMLJM.js} +2 -2
  58. package/dist/{session-memory-HL5XDBIL.js → session-memory-DWF5Z2LC.js} +3 -3
  59. package/dist/{setup-command-B6AFJGZB.js → setup-command-6EJONTOU.js} +10 -7
  60. package/dist/{setup-config-PWK6WHMG.js → setup-config-JA5IX53Q.js} +3 -3
  61. package/dist/{setup-custom-api-XAWKRDWV.js → setup-custom-api-CSB26HWD.js} +7 -5
  62. package/dist/setup-custom-api-CSB26HWD.js.map +1 -0
  63. package/dist/{weather-report-XZ5CENHE.js → weather-report-YQSLX4MS.js} +2 -2
  64. package/package.json +1 -1
  65. package/dist/chunk-BPMQRYGU.js.map +0 -1
  66. package/dist/chunk-EKYT4NMD.js.map +0 -1
  67. package/dist/chunk-FWSTXEG2.js.map +0 -1
  68. package/dist/chunk-JERFBN73.js.map +0 -1
  69. package/dist/chunk-KVWHK72T.js.map +0 -1
  70. package/dist/chunk-OC7RMLN2.js +0 -839
  71. package/dist/chunk-OC7RMLN2.js.map +0 -1
  72. package/dist/chunk-RSUCXOQM.js.map +0 -1
  73. package/dist/chunk-UOUJZIKH.js.map +0 -1
  74. package/dist/chunk-XYCS5X3H.js.map +0 -1
  75. package/dist/research-helpers-synthesize-TFZIXBX3.js +0 -10
  76. package/dist/setup-custom-api-XAWKRDWV.js.map +0 -1
  77. /package/dist/{adaptive-memory-SUNFPY2W.js.map → adaptive-memory-TSZEJUJC.js.map} +0 -0
  78. /package/dist/{chunk-4FVISCDB.js.map → chunk-6QU4DJYW.js.map} +0 -0
  79. /package/dist/{chunk-NYNBDP7M.js.map → chunk-7Y36JLES.js.map} +0 -0
  80. /package/dist/{chunk-VKZKC4SM.js.map → chunk-EDGG3RQE.js.map} +0 -0
  81. /package/dist/{chunk-2SPRLBOS.js.map → chunk-EZXOJZYE.js.map} +0 -0
  82. /package/dist/{chunk-W5AJRK4U.js.map → chunk-GMOGKX4E.js.map} +0 -0
  83. /package/dist/{chunk-IOU7F5PH.js.map → chunk-K7EA5OV4.js.map} +0 -0
  84. /package/dist/{chunk-KRIFBGWZ.js.map → chunk-PTGBJFSD.js.map} +0 -0
  85. /package/dist/{chunk-3IDJSFWT.js.map → chunk-TFEFN37P.js.map} +0 -0
  86. /package/dist/{chunk-Q5TKQWOI.js.map → chunk-V5CGWMYL.js.map} +0 -0
  87. /package/dist/{chunk-TCQNNY4J.js.map → chunk-XEMRMZUN.js.map} +0 -0
  88. /package/dist/{chunk-R66AWJJ7.js.map → chunk-YU4NABXM.js.map} +0 -0
  89. /package/dist/{chunk-VLVHPC72.js.map → chunk-ZUWOFHNC.js.map} +0 -0
  90. /package/dist/{cli-circuit-breaker-7MCMHSDY.js.map → cli-circuit-breaker-SL73NWX2.js.map} +0 -0
  91. /package/dist/{composite-router-LXFOSMSE.js.map → composite-router-A7URDW4X.js.map} +0 -0
  92. /package/dist/{consensus-vote-G6H532ME.js.map → consensus-vote-CQ2JP6DC.js.map} +0 -0
  93. /package/dist/{doctor-deep-RKMOZSIS.js.map → doctor-deep-VN6KMUCG.js.map} +0 -0
  94. /package/dist/{expert-bridge-KHHE42JP.js.map → expert-bridge-LT7PKUPS.js.map} +0 -0
  95. /package/dist/{factory-JVN47MFN.js.map → factory-FA7WDPZW.js.map} +0 -0
  96. /package/dist/{factory-KRNR7XHD.js.map → factory-FZ2KSVYC.js.map} +0 -0
  97. /package/dist/{issue-triage-O3C7P66H.js.map → issue-triage-YYTE6KTC.js.map} +0 -0
  98. /package/dist/{mcp-config-QRERKGR4.js.map → mcp-config-34XMRM64.js.map} +0 -0
  99. /package/dist/{mobimem-FAOAXV3B.js.map → mobimem-QDBP37H7.js.map} +0 -0
  100. /package/dist/{repo-security-plan-AZ5HMGFC.js.map → repo-security-plan-C3LLE3Z7.js.map} +0 -0
  101. /package/dist/{research-helpers-synthesize-TFZIXBX3.js.map → research-helpers-synthesize-NVQIWLQL.js.map} +0 -0
  102. /package/dist/{routing-memory-MXF45FXT.js.map → routing-memory-W3YWMLJM.js.map} +0 -0
  103. /package/dist/{session-memory-HL5XDBIL.js.map → session-memory-DWF5Z2LC.js.map} +0 -0
  104. /package/dist/{setup-command-B6AFJGZB.js.map → setup-command-6EJONTOU.js.map} +0 -0
  105. /package/dist/{setup-config-PWK6WHMG.js.map → setup-config-JA5IX53Q.js.map} +0 -0
  106. /package/dist/{weather-report-XZ5CENHE.js.map → weather-report-YQSLX4MS.js.map} +0 -0
@@ -2,10 +2,13 @@ import {
2
2
  CUSTOM_API_BASE_URL_ENV,
3
3
  PROVIDER_ENV_KEYS,
4
4
  validateCustomApiBaseUrl
5
- } from "./chunk-R66AWJJ7.js";
5
+ } from "./chunk-YU4NABXM.js";
6
+ import {
7
+ CUSTOM_API_DEFAULT_MODEL
8
+ } from "./chunk-H43PABG4.js";
6
9
  import {
7
10
  SessionMemory
8
- } from "./chunk-NYNBDP7M.js";
11
+ } from "./chunk-7Y36JLES.js";
9
12
  import {
10
13
  AdaptiveMemoryBackend,
11
14
  HybridMemoryBackend,
@@ -14,7 +17,7 @@ import {
14
17
  getMemoryEntry,
15
18
  memoryExists,
16
19
  memoryRowToEntry
17
- } from "./chunk-3IDJSFWT.js";
20
+ } from "./chunk-TFEFN37P.js";
18
21
  import {
19
22
  stringifyValue,
20
23
  tokenizeFiltered
@@ -26,7 +29,7 @@ import {
26
29
  getAvailableClis,
27
30
  isCliAvailable,
28
31
  withTimeout
29
- } from "./chunk-RSUCXOQM.js";
32
+ } from "./chunk-SXWZS2V4.js";
30
33
  import {
31
34
  AgentError,
32
35
  CACHE_TIMEOUTS,
@@ -69,10 +72,11 @@ import {
69
72
  ok,
70
73
  recordRateLimitEvent,
71
74
  registerPersistentOutcomeStoreFactory,
75
+ resolveCliAlias,
72
76
  resolveVoteTimeout,
73
77
  toRateLimitError,
74
78
  validateTimeout
75
- } from "./chunk-UOUJZIKH.js";
79
+ } from "./chunk-R2Y57PM3.js";
76
80
  import {
77
81
  OUTCOMES_FILE,
78
82
  ensureLearningDir
@@ -1458,6 +1462,16 @@ function emitToolAudit(auditLogger, toolName, ctx, result, durationMs) {
1458
1462
  durationMs
1459
1463
  });
1460
1464
  }
1465
+ function emitToolAuditException(auditLogger, toolName, ctx, durationMs) {
1466
+ const actor = actorFromContext(ctx);
1467
+ auditLogger.logToolInvocation({
1468
+ toolName,
1469
+ outcome: "error",
1470
+ actor,
1471
+ requestId: ctx.requestId,
1472
+ durationMs
1473
+ });
1474
+ }
1461
1475
  function emitPolicyAudit(auditLogger, toolName, ctx, reason) {
1462
1476
  const actor = actorFromContext(ctx);
1463
1477
  auditLogger.logPolicyDecision({
@@ -1551,31 +1565,42 @@ function createSecureHandler(handler, config) {
1551
1565
  requestLogger
1552
1566
  );
1553
1567
  if (preCheckError) return preCheckError;
1554
- const execStartTime = getTimeProvider().now();
1555
- try {
1556
- const result = await executeHandler(
1557
- handler,
1558
- sanitizedArgs,
1559
- { requestContext, logger: requestLogger },
1560
- requestLogger
1568
+ return executeAndAudit(handler, sanitizedArgs, requestContext, requestLogger, config);
1569
+ };
1570
+ }
1571
+ async function executeAndAudit(handler, sanitizedArgs, requestContext, requestLogger, config) {
1572
+ const execStartTime = getTimeProvider().now();
1573
+ try {
1574
+ const result = await executeHandler(
1575
+ handler,
1576
+ sanitizedArgs,
1577
+ { requestContext, logger: requestLogger },
1578
+ requestLogger
1579
+ );
1580
+ sanitizeToolResult(result, requestLogger);
1581
+ if (config.auditLogger) {
1582
+ emitToolAudit(
1583
+ config.auditLogger,
1584
+ config.toolName,
1585
+ requestContext,
1586
+ result,
1587
+ getTimeProvider().now() - execStartTime
1561
1588
  );
1562
- sanitizeToolResult(result, requestLogger);
1563
- if (config.auditLogger) {
1564
- emitToolAudit(
1565
- config.auditLogger,
1566
- config.toolName,
1567
- requestContext,
1568
- result,
1569
- getTimeProvider().now() - execStartTime
1570
- );
1571
- }
1572
- return result;
1573
- } catch (error) {
1574
- const message = error instanceof Error ? error.message : "Unknown error";
1575
- requestLogger.error("Tool execution failed", error instanceof Error ? error : void 0);
1576
- return internalError(message, requestContext.requestId);
1577
1589
  }
1578
- };
1590
+ return result;
1591
+ } catch (error) {
1592
+ const message = error instanceof Error ? error.message : "Unknown error";
1593
+ requestLogger.error("Tool execution failed", error instanceof Error ? error : void 0);
1594
+ if (config.auditLogger) {
1595
+ emitToolAuditException(
1596
+ config.auditLogger,
1597
+ config.toolName,
1598
+ requestContext,
1599
+ getTimeProvider().now() - execStartTime
1600
+ );
1601
+ }
1602
+ return internalError(message, requestContext.requestId);
1603
+ }
1579
1604
  }
1580
1605
 
1581
1606
  // src/mcp/tools/tool-result.ts
@@ -1609,7 +1634,8 @@ var VOTER_ROLES = {
1609
1634
  devex: "Developer Experience - evaluates usability, documentation, and developer workflow",
1610
1635
  ai_ml: "AI/ML Engineer - evaluates AI/ML aspects, model selection, and learning capabilities",
1611
1636
  pm: "Product Manager - evaluates business value, user impact, and resource allocation",
1612
- catfish: "Contrarian Analyst - deliberately challenges proposals to prevent agreement bias (arXiv:2505.21503)"
1637
+ catfish: "Contrarian Analyst - deliberately challenges proposals to prevent agreement bias (arXiv:2505.21503)",
1638
+ scope_steward: "Scope Steward - asks whether to build at all; checks existing tools, biases toward kill-the-feature (#2185)"
1613
1639
  };
1614
1640
 
1615
1641
  // src/cli-adapters/cli-to-model-adapter.ts
@@ -2406,15 +2432,15 @@ async function collectStream(stream, options = {}) {
2406
2432
 
2407
2433
  // src/adapters/claude-adapter-types.ts
2408
2434
  var CLAUDE_MODELS = {
2409
- OPUS_4: "claude-opus-4-20250514",
2410
- SONNET_4: "claude-sonnet-4-20250514",
2411
- HAIKU_4: "claude-haiku-4-5-20251001"
2435
+ OPUS_4: getCliModelName("claude-opus"),
2436
+ SONNET_4: getCliModelName("claude-sonnet"),
2437
+ HAIKU_4: getCliModelName("claude-haiku")
2412
2438
  };
2413
2439
  var CLAUDE_MODEL_ALIASES = {
2414
2440
  "claude-opus-4": CLAUDE_MODELS.OPUS_4,
2415
2441
  "claude-sonnet-4": CLAUDE_MODELS.SONNET_4,
2416
2442
  "claude-haiku-4": CLAUDE_MODELS.HAIKU_4,
2417
- // Legacy alias
2443
+ // Legacy alias — pre-4.x users routed to the current haiku.
2418
2444
  "claude-haiku-3": CLAUDE_MODELS.HAIKU_4
2419
2445
  };
2420
2446
  var DEFAULT_MAX_TOKENS = 4096;
@@ -2493,7 +2519,9 @@ function mapTool(tool) {
2493
2519
  };
2494
2520
  }
2495
2521
  function resolveModelId(modelId) {
2496
- return CLAUDE_MODEL_ALIASES[modelId] ?? modelId;
2522
+ const registryId = resolveCliAlias(modelId);
2523
+ if (registryId !== void 0) return getCliModelName(registryId);
2524
+ return modelId;
2497
2525
  }
2498
2526
  function getModelCapabilities(modelId) {
2499
2527
  const capabilities = [
@@ -3142,7 +3170,7 @@ function tryCustomOpenAiAdapter(logger11) {
3142
3170
  if (customKey === void 0 || customBaseUrl === void 0 || customBaseUrl === "") {
3143
3171
  return null;
3144
3172
  }
3145
- const customModelId = process.env["NEXUS_CUSTOM_MODEL"] ?? "gpt-4o";
3173
+ const customModelId = process.env["NEXUS_CUSTOM_MODEL"] ?? CUSTOM_API_DEFAULT_MODEL;
3146
3174
  logger11.info("Using custom-openai SDK adapter", {
3147
3175
  model: customModelId,
3148
3176
  baseUrl: customBaseUrl
@@ -3908,6 +3936,10 @@ var UnifiedAdapterRegistry = class {
3908
3936
  // --------------------------------------------------------------------------
3909
3937
  // Public API
3910
3938
  // --------------------------------------------------------------------------
3939
+ /** Logger used by this registry. Exposed so singleton helpers can warn. */
3940
+ getLogger() {
3941
+ return this.logger;
3942
+ }
3911
3943
  /**
3912
3944
  * Get adapter for a task category. Uses pre-computed routing.
3913
3945
  * Falls back to default adapter if category unknown.
@@ -3959,9 +3991,11 @@ var UnifiedAdapterRegistry = class {
3959
3991
  * Falls back to default adapter if model not recognized.
3960
3992
  */
3961
3993
  getAdapterForModel(modelPreference) {
3962
- const model = DEFAULT_MODEL_CAPABILITIES.models.find(
3963
- (m) => m.id === modelPreference || m.cliAlias === modelPreference || m.cliModelName === modelPreference || modelPreference.startsWith(m.id)
3994
+ const exact = DEFAULT_MODEL_CAPABILITIES.models.find(
3995
+ (m) => m.id === modelPreference || m.cliAlias === modelPreference || m.cliModelName === modelPreference
3964
3996
  );
3997
+ const prefix = exact ?? [...DEFAULT_MODEL_CAPABILITIES.models].filter((m) => modelPreference.startsWith(m.id)).sort((a, b) => b.id.length - a.id.length)[0];
3998
+ const model = prefix;
3965
3999
  if (model !== void 0) {
3966
4000
  this.logger.debug("Model resolved to CLI", {
3967
4001
  model: modelPreference,
@@ -4064,12 +4098,44 @@ function createUnifiedRegistry(config) {
4064
4098
  return new UnifiedAdapterRegistry(config);
4065
4099
  }
4066
4100
  function getGlobalRegistry(config) {
4067
- globalRegistry ??= new UnifiedAdapterRegistry(config);
4101
+ if (globalRegistry === void 0) {
4102
+ globalRegistry = new UnifiedAdapterRegistry(config);
4103
+ return globalRegistry;
4104
+ }
4105
+ if (config !== void 0 && Object.keys(config).length > 0) {
4106
+ globalRegistry.getLogger().warn(
4107
+ "UnifiedAdapterRegistry singleton already initialized; provided config ignored. Call resetGlobalRegistry() first if reconfiguration is intentional.",
4108
+ { providedKeys: Object.keys(config) }
4109
+ );
4110
+ }
4068
4111
  return globalRegistry;
4069
4112
  }
4070
4113
 
4071
4114
  // src/cli/voter-prompts.ts
4072
4115
  var DEFAULT_PROJECT = "nexus-agents";
4116
+ function prReviewModeAddendum() {
4117
+ return `
4118
+ PR-review mode \u2014 if you are reviewing a code diff (not a proposal) AND you have at least one concrete defect that justifies blocking the merge, populate the OPTIONAL TOP-LEVEL "findings" field on your JSON response. NOT inside reasoning \u2014 top-level. The schema:
4119
+
4120
+ "findings": [
4121
+ {
4122
+ "summary": "One-line summary",
4123
+ "location": "path/file.ext:LINE",
4124
+ "severity": "critical" | "high" | "medium" | "low",
4125
+ "gate": {
4126
+ "reread_cited_line": "passed",
4127
+ "traced_call_path": "passed",
4128
+ "named_assertion": "Concrete failing assertion \u2014 what test would fail and how. Substantive, not 'passed'.",
4129
+ "ruled_out_language_non_issue": "passed"
4130
+ },
4131
+ "claim": "What is wrong and why it justifies blocking."
4132
+ }
4133
+ ]
4134
+
4135
+ A finding only triggers strict request_changes if all 4 gate fields = "passed" AND named_assertion is substantive (>10 chars naming a concrete failure, not just "passed"). Findings missing any of those surface as informational only \u2014 they do not block on their own. The 2026-04-25 audit (#2225) found a 100% false-positive rate when this gate wasn't enforced. If you're approving the diff, OMIT the findings field entirely. If reviewing a non-diff proposal, ignore this section.
4136
+
4137
+ History note: an earlier prompt asked for YAML inside reasoning; that format was lossy across JSON serialization (#2245). Use the top-level JSON array above.`;
4138
+ }
4073
4139
  function voterFooter() {
4074
4140
  return `
4075
4141
  Workflow-test assessment (include in your reasoning):
@@ -4077,7 +4143,8 @@ Workflow-test assessment (include in your reasoning):
4077
4143
  - Workflow integration: Does this fit existing CI/build/test pipelines?
4078
4144
  - Incremental verifiability: Can progress be measured at each step?
4079
4145
 
4080
- When rejecting, classify your reasons using categories: YAGNI, DRY_VIOLATION, OVER_ENGINEERING, SCOPE_CREEP, SECURITY_RISK, MISALIGNED, INSUFFICIENT_EVIDENCE.`;
4146
+ When rejecting, classify your reasons using categories: YAGNI, DRY_VIOLATION, OVER_ENGINEERING, SCOPE_CREEP, SECURITY_RISK, MISALIGNED, INSUFFICIENT_EVIDENCE.
4147
+ ${prReviewModeAddendum()}`;
4081
4148
  }
4082
4149
  function architectPrompt(project) {
4083
4150
  return `You are a Software Architect voting on technical proposals for the ${project} project.
@@ -4170,7 +4237,67 @@ When rejecting, you MUST classify your reasons using categories: YAGNI, DRY_VIOL
4170
4237
  IMPORTANT: Your job is to find legitimate concerns, not to reject everything.
4171
4238
  If after genuine scrutiny you find no significant issues, you MAY approve.
4172
4239
  But your default posture is skeptical \u2014 look for what others might miss.
4173
- High-confidence rejections with specific reasoning are your most valuable output.`;
4240
+ High-confidence rejections with specific reasoning are your most valuable output.
4241
+ ${prReviewModeAddendum()}`;
4242
+ }
4243
+ function scopeStewardPrompt(project) {
4244
+ return `You are a Scope Steward voting on proposals for the ${project} project.
4245
+
4246
+ Your job is to gate against build-when-buy-would-do and feature sprawl.
4247
+ The originating case (2026-04-24): a 6-role panel approved building a USB flasher
4248
+ CLI without anyone flagging that Rufus already solves the problem better, for the
4249
+ same audience, with 100M+ installs of battle-tested code. This role exists to
4250
+ catch that class of mistake.
4251
+
4252
+ Your evaluation criteria \u2014 work through these mandatory checks in your reasoning:
4253
+
4254
+ 1. **Existing-tool check.** Search your knowledge for tools, libraries, or
4255
+ services that already solve the stated problem. Name them concretely
4256
+ (not "there might be alternatives" \u2014 actual names: Rufus, ripgrep,
4257
+ esbuild, etc.). If you can't name an alternative, say so explicitly.
4258
+
4259
+ 2. **Build-vs-buy math.** For each existing tool you named: what would we
4260
+ LOSE by adopting it (license, dependency surface, integration cost)?
4261
+ What would we GAIN by building our own (tighter integration, no extra
4262
+ binary, etc.)? Default lean: BUY. Building is justified only when the
4263
+ loss column is concrete and the gain column is load-bearing.
4264
+
4265
+ 3. **Mission alignment.** Does this proposal serve the project's stated
4266
+ mission, or is it scope drift? If drift, name the drift specifically.
4267
+
4268
+ 4. **Kill-the-feature option.** For every proposal, explicitly evaluate
4269
+ "what if we just didn't do this?" as a ranked option. Many proposals
4270
+ don't need to be built. Make the no-build case before the build case.
4271
+
4272
+ 5. **Sprawl audit.** Check whether similar functionality already exists
4273
+ in the codebase. If it does, recommend extending \u2014 not forking. The
4274
+ anti-sprawl policy in CLAUDE.md is specifically the rule this role
4275
+ enforces.
4276
+
4277
+ Default bias: REJECT proposals where an existing tool fits, even if our
4278
+ own implementation would be marginally nicer. Only approve when the
4279
+ existing-tool check fails AND the kill-the-feature option is worse AND
4280
+ mission alignment is clear AND no comparable in-codebase functionality
4281
+ exists.
4282
+
4283
+ Few-shot example of a textbook rejection:
4284
+ > Proposal: "Add an aegis-boot subcommand to flash bootable USB sticks."
4285
+ > Steward response: "REJECT (DON'T-BUILD). Rufus has solved this for the
4286
+ > same audience for 10+ years with 100M+ installs and battle-tested code.
4287
+ > Adopting Rufus loses nothing material; building our own loses
4288
+ > maintenance bandwidth indefinitely. Mission alignment: aegis-boot's
4289
+ > mission is verifiable boot, not USB tooling. Kill option clearly wins.
4290
+ > No prior in-codebase functionality. Recommend: point users at Rufus in
4291
+ > the docs and stop here."
4292
+
4293
+ ${voterFooter()}
4294
+
4295
+ When rejecting, you MUST classify reasons (YAGNI, DRY_VIOLATION,
4296
+ OVER_ENGINEERING, SCOPE_CREEP, MISALIGNED). The steward's most common
4297
+ categories are SCOPE_CREEP, YAGNI, and OVER_ENGINEERING.
4298
+
4299
+ You CAN approve. But your default posture is: "this should not be built;
4300
+ prove me wrong with the build-vs-buy math."`;
4174
4301
  }
4175
4302
  function getVoterPrompts(project = DEFAULT_PROJECT) {
4176
4303
  return {
@@ -4179,7 +4306,8 @@ function getVoterPrompts(project = DEFAULT_PROJECT) {
4179
4306
  devex: devexPrompt(project),
4180
4307
  ai_ml: aiMlPrompt(project),
4181
4308
  pm: pmPrompt(project),
4182
- catfish: catfishPrompt(project)
4309
+ catfish: catfishPrompt(project),
4310
+ scope_steward: scopeStewardPrompt(project)
4183
4311
  };
4184
4312
  }
4185
4313
  var VOTER_SYSTEM_PROMPTS = getVoterPrompts();
@@ -4189,7 +4317,8 @@ var SIMULATED_VOTE_REASONING = {
4189
4317
  devex: "Assessed developer experience and workflow impact.",
4190
4318
  ai_ml: "Analyzed AI/ML capabilities and learning potential.",
4191
4319
  pm: "Evaluated business value and resource requirements.",
4192
- catfish: "Challenged proposal assumptions and identified potential risks."
4320
+ catfish: "Challenged proposal assumptions and identified potential risks.",
4321
+ scope_steward: "Checked existing tools, build-vs-buy math, kill-the-feature option; bias toward not shipping."
4193
4322
  };
4194
4323
 
4195
4324
  // src/cli/voter-response.ts
@@ -4203,6 +4332,18 @@ var SyntheticVoteError = class extends Error {
4203
4332
  this.name = "SyntheticVoteError";
4204
4333
  }
4205
4334
  };
4335
+ var RawFindingSchema = z2.object({
4336
+ summary: z2.string().min(1).max(500).describe("One-line summary of the issue"),
4337
+ location: z2.string().min(1).max(200).describe("path/file.ext:line"),
4338
+ severity: z2.enum(["critical", "high", "medium", "low"]).default("medium"),
4339
+ gate: z2.object({
4340
+ reread_cited_line: z2.enum(["passed", "failed", "skipped"]).default("skipped"),
4341
+ traced_call_path: z2.enum(["passed", "failed", "skipped"]).default("skipped"),
4342
+ named_assertion: z2.string().default("").describe("Concrete failing assertion \u2014 substantive, not a rubber-stamp word"),
4343
+ ruled_out_language_non_issue: z2.enum(["passed", "failed", "skipped"]).default("skipped")
4344
+ }),
4345
+ claim: z2.string().min(1).max(2e3).describe("What is wrong and why it justifies blocking")
4346
+ });
4206
4347
  var VoteResponseSchema = z2.object({
4207
4348
  decision: z2.enum(["approve", "reject", "abstain"]).describe("Your vote decision"),
4208
4349
  reasoning: z2.string().min(10).max(4e3).describe("Explanation for your vote (10-4000 chars)"),
@@ -4219,8 +4360,48 @@ var VoteResponseSchema = z2.object({
4219
4360
  "MISALIGNED",
4220
4361
  "INSUFFICIENT_EVIDENCE"
4221
4362
  ])
4222
- ).optional().describe("Rejection reason categories when decision is reject")
4363
+ ).optional().describe("Rejection reason categories when decision is reject"),
4364
+ /** Top-level structured findings for PR-review mode (#2245 v4 follow-up).
4365
+ * Replaces the YAML-in-reasoning encoding that proved lossy. */
4366
+ findings: z2.array(RawFindingSchema).optional().describe("Structured findings (PR review only)")
4223
4367
  });
4368
+ var VOTE_PROMPT_EXAMPLES = `Example approve response:
4369
+ {
4370
+ "decision": "approve",
4371
+ "reasoning": "The proposal aligns with architectural patterns. Testability: high \u2014 unit tests can verify each component. Workflow integration: fits existing CI pipeline.",
4372
+ "confidence": 0.85,
4373
+ "conditions": ["Add unit tests before merge"]
4374
+ }
4375
+
4376
+ Example reject response:
4377
+ {
4378
+ "decision": "reject",
4379
+ "reasoning": "This adds speculative abstractions for hypothetical future needs. Testability: unclear \u2014 no concrete test plan provided.",
4380
+ "confidence": 0.80,
4381
+ "rejectionCategories": ["YAGNI", "OVER_ENGINEERING"]
4382
+ }
4383
+
4384
+ Example PR-review request_changes response with structured findings:
4385
+ {
4386
+ "decision": "reject",
4387
+ "reasoning": "Off-by-one in clampPageSize and missing null guard on response.timing \u2014 both visible in the diff.",
4388
+ "confidence": 0.9,
4389
+ "rejectionCategories": ["INSUFFICIENT_EVIDENCE"],
4390
+ "findings": [
4391
+ {
4392
+ "summary": "Off-by-one in clampPageSize",
4393
+ "location": "packages/nexus-agents/src/api/pagination.ts:18",
4394
+ "severity": "high",
4395
+ "gate": {
4396
+ "reread_cited_line": "passed",
4397
+ "traced_call_path": "passed",
4398
+ "named_assertion": "Test would assert clampPageSize(50, 100) === 50; this returns 49.",
4399
+ "ruled_out_language_non_issue": "passed"
4400
+ },
4401
+ "claim": "Function name says 'clamp to range' but returns requested-1 in the in-range path."
4402
+ }
4403
+ ]
4404
+ }`;
4224
4405
  function buildVotePrompt(proposal) {
4225
4406
  return `Evaluate the following proposal and provide your vote.
4226
4407
 
@@ -4238,22 +4419,9 @@ Respond with a JSON object containing:
4238
4419
  - confidence: Number between 0 and 1
4239
4420
  - conditions: Optional array of conditions for approval
4240
4421
  - rejectionCategories: Required when rejecting. Array of categories from: YAGNI, DRY_VIOLATION, OVER_ENGINEERING, SCOPE_CREEP, SECURITY_RISK, MISALIGNED, INSUFFICIENT_EVIDENCE
4422
+ - findings: PR-REVIEW MODE ONLY. Optional top-level array of structured findings \u2014 see "PR-review mode" in the system prompt. OMIT this field entirely when reviewing a non-diff proposal or when approving a diff.
4241
4423
 
4242
- Example approve response:
4243
- {
4244
- "decision": "approve",
4245
- "reasoning": "The proposal aligns with architectural patterns. Testability: high \u2014 unit tests can verify each component. Workflow integration: fits existing CI pipeline.",
4246
- "confidence": 0.85,
4247
- "conditions": ["Add unit tests before merge"]
4248
- }
4249
-
4250
- Example reject response:
4251
- {
4252
- "decision": "reject",
4253
- "reasoning": "This adds speculative abstractions for hypothetical future needs. Testability: unclear \u2014 no concrete test plan provided.",
4254
- "confidence": 0.80,
4255
- "rejectionCategories": ["YAGNI", "OVER_ENGINEERING"]
4256
- }`;
4424
+ ${VOTE_PROMPT_EXAMPLES}`;
4257
4425
  }
4258
4426
  function extractJsonFromResponse(text) {
4259
4427
  const codeBlockMatch = /```(?:json)?\s*([\s\S]*?)```/i.exec(text);
@@ -4286,6 +4454,17 @@ function createFallbackVote(output, _role, reason) {
4286
4454
  // Mark as synthetic
4287
4455
  };
4288
4456
  }
4457
+ function buildParsedVote(data) {
4458
+ return {
4459
+ decision: data.decision,
4460
+ reasoning: data.reasoning,
4461
+ confidence: data.confidence,
4462
+ ...data.conditions !== void 0 ? { conditions: data.conditions } : {},
4463
+ ...data.rejectionCategories !== void 0 ? { rejectionCategories: data.rejectionCategories } : {},
4464
+ ...data.findings !== void 0 ? { findings: data.findings } : {},
4465
+ source: "parsed"
4466
+ };
4467
+ }
4289
4468
  function parseVoteResponse(output, role, options) {
4290
4469
  const allowSyntheticVote = options?.allowSyntheticVote ?? false;
4291
4470
  try {
@@ -4293,15 +4472,7 @@ function parseVoteResponse(output, role, options) {
4293
4472
  const parsed = JSON.parse(jsonStr);
4294
4473
  const validated = VoteResponseSchema.safeParse(parsed);
4295
4474
  if (validated.success) {
4296
- return {
4297
- decision: validated.data.decision,
4298
- reasoning: validated.data.reasoning,
4299
- confidence: validated.data.confidence,
4300
- ...validated.data.conditions !== void 0 ? { conditions: validated.data.conditions } : {},
4301
- ...validated.data.rejectionCategories !== void 0 ? { rejectionCategories: validated.data.rejectionCategories } : {},
4302
- source: "parsed"
4303
- // Real vote from LLM
4304
- };
4475
+ return buildParsedVote(validated.data);
4305
4476
  }
4306
4477
  const reason = `Validation failed: ${validated.error.issues.map((e) => e.message).join(", ")}`;
4307
4478
  if (!allowSyntheticVote) {
@@ -4370,8 +4541,10 @@ var ROLE_VOTE_DISTRIBUTIONS = {
4370
4541
  // Technical focus
4371
4542
  pm: [55, 25, 20],
4372
4543
  // Business focus - generally supportive
4373
- catfish: [20, 65, 15]
4544
+ catfish: [20, 65, 15],
4374
4545
  // Deliberately contrarian - challenges proposals (arXiv:2505.21503)
4546
+ scope_steward: [25, 60, 15]
4547
+ // Default-bias toward not shipping (#2185)
4375
4548
  };
4376
4549
  function selectWeightedDecision(weights) {
4377
4550
  const random = getRandomProvider();
@@ -4422,7 +4595,12 @@ async function executeSingleVoteAttempt(role, proposal, adapter, timeoutMs) {
4422
4595
  { role: "system", content: VOTER_SYSTEM_PROMPTS[role] },
4423
4596
  { role: "user", content: buildVotePrompt(proposal) }
4424
4597
  ],
4425
- maxTokens: 500,
4598
+ // 500 was correct for short proposal-style votes but caused mid-string
4599
+ // truncation ("Unterminated string in JSON at position N") in #2241 v3
4600
+ // when voters review code diffs — the JSON envelope + reasoning + YAML
4601
+ // findings block routinely exceed 500 tokens. Bumped to 2000 (#2245);
4602
+ // refine per use case if needed.
4603
+ maxTokens: 2e3,
4426
4604
  temperature: 0.3
4427
4605
  // Low temperature for consistent evaluations
4428
4606
  };
@@ -4711,6 +4889,18 @@ var RejectionCategorySchema = z3.enum([
4711
4889
  "INSUFFICIENT_EVIDENCE"
4712
4890
  ]);
4713
4891
  var REJECTION_CATEGORIES = RejectionCategorySchema.options;
4892
+ var FindingShapeSchema = z3.object({
4893
+ summary: z3.string().min(1).max(500),
4894
+ location: z3.string().min(1).max(200),
4895
+ severity: z3.enum(["critical", "high", "medium", "low"]).default("medium"),
4896
+ gate: z3.object({
4897
+ reread_cited_line: z3.enum(["passed", "failed", "skipped"]).default("skipped"),
4898
+ traced_call_path: z3.enum(["passed", "failed", "skipped"]).default("skipped"),
4899
+ named_assertion: z3.string().default(""),
4900
+ ruled_out_language_non_issue: z3.enum(["passed", "failed", "skipped"]).default("skipped")
4901
+ }),
4902
+ claim: z3.string().min(1).max(2e3)
4903
+ });
4714
4904
  var VoteSchema = z3.object({
4715
4905
  decision: VoteDecisionSchema,
4716
4906
  reasoning: z3.string().min(1).describe("Explanation for the vote"),
@@ -4718,6 +4908,9 @@ var VoteSchema = z3.object({
4718
4908
  conditions: z3.array(z3.string()).optional().describe("Conditions for approval"),
4719
4909
  /** Structured rejection categories for reject→refine→re-vote loops (Issue #1213). */
4720
4910
  rejectionCategories: z3.array(RejectionCategorySchema).optional().describe("Rejection reason categories when decision is reject"),
4911
+ /** Pre-verified PR-review findings (#2245 v4 follow-up). Optional;
4912
+ * populated only when the voter emits the structured top-level array. */
4913
+ findings: z3.array(FindingShapeSchema).optional().describe("PR-review findings (pre-verified)"),
4721
4914
  timestamp: z3.iso.datetime().optional()
4722
4915
  });
4723
4916
  var ProposalSchema = z3.object({
@@ -5940,11 +6133,21 @@ var ConsensusEngine = class {
5940
6133
  ambiguityBand: this.quorumConfig.ambiguityBand
5941
6134
  });
5942
6135
  if (!ambiguous) return false;
5943
- const newVoters = await this.voterExpansionCallback(
5944
- proposalId,
5945
- required.length,
5946
- this.quorumConfig.votersPerExpansion
5947
- );
6136
+ let newVoters;
6137
+ try {
6138
+ newVoters = await this.voterExpansionCallback(
6139
+ proposalId,
6140
+ required.length,
6141
+ this.quorumConfig.votersPerExpansion
6142
+ );
6143
+ } catch (err2) {
6144
+ const error = err2 instanceof Error ? err2 : new Error(String(err2));
6145
+ this.logger.warn("Incremental quorum: expansion callback threw; closing as-is", {
6146
+ proposalId,
6147
+ errorMessage: error.message
6148
+ });
6149
+ return false;
6150
+ }
5948
6151
  if (newVoters.length === 0) {
5949
6152
  this.logger.info("Incremental quorum: no additional voters available", { proposalId });
5950
6153
  return false;
@@ -12478,7 +12681,7 @@ function strategyToAlgorithm(strategy) {
12478
12681
  return strategy;
12479
12682
  }
12480
12683
  function getVoterRoles(quickMode) {
12481
- return quickMode ? ["architect", "security", "pm"] : ["architect", "security", "devex", "ai_ml", "pm", "catfish"];
12684
+ return quickMode ? ["architect", "security", "scope_steward"] : ["architect", "security", "devex", "ai_ml", "pm", "catfish", "scope_steward"];
12482
12685
  }
12483
12686
  function createEmptyConsensusResult(proposal, algorithm) {
12484
12687
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -12604,7 +12807,7 @@ async function processVotesWithCascade(votes, opts) {
12604
12807
  var CONTRARIAN_ESCALATION_THRESHOLD = 0.8;
12605
12808
  async function runContrarianCheck(proposal, log) {
12606
12809
  try {
12607
- const { executeExpert } = await import("./expert-bridge-KHHE42JP.js");
12810
+ const { executeExpert } = await import("./expert-bridge-LT7PKUPS.js");
12608
12811
  const prompt = [
12609
12812
  "You are a contrarian analyst. Your job is to find reasons this proposal should be REJECTED.",
12610
12813
  "Look for: YAGNI (not needed), MISALIGNED (wrong tech/architecture), SECURITY_RISK, SCOPE_CREEP.",
@@ -12812,10 +13015,10 @@ function registerConsensusVoteTool(server, deps) {
12812
13015
  strategy: VotingStrategySchema.optional().describe(
12813
13016
  "Voting strategy: simple_majority (default), supermajority, unanimous, proof_of_learning, or higher_order"
12814
13017
  ),
12815
- quickMode: z19.boolean().optional().default(false).describe("Use 3 agents instead of 6"),
13018
+ quickMode: z19.boolean().optional().default(false).describe("Use 3 agents instead of 7"),
12816
13019
  simulateVotes: z19.boolean().optional().default(false).describe("Use simulated votes")
12817
13020
  };
12818
- const description = "Execute multi-model consensus voting on a proposal. Uses 6 specialized agent roles (architect, security, devex, ai_ml, pm, catfish) to vote on proposals with configurable strategies. Supports higher_order strategy for Bayesian-optimal aggregation with correlation awareness (Issue #514).";
13021
+ 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).";
12819
13022
  const secureHandler = createSecureHandler(createConsensusVoteHandler(depsWithNotifier), {
12820
13023
  toolName: "consensus_vote",
12821
13024
  rateLimiter: deps.rateLimiter,
@@ -12895,6 +13098,7 @@ export {
12895
13098
  createSecureHandler,
12896
13099
  createMcpNotifier,
12897
13100
  NOOP_NOTIFIER,
13101
+ abortSignalStorage,
12898
13102
  withProgressHeartbeat,
12899
13103
  withAccessPolicy,
12900
13104
  getToolTimeout,
@@ -12976,4 +13180,4 @@ export {
12976
13180
  CONSENSUS_VOTE_OUTPUT_SCHEMA,
12977
13181
  registerConsensusVoteTool
12978
13182
  };
12979
- //# sourceMappingURL=chunk-BPMQRYGU.js.map
13183
+ //# sourceMappingURL=chunk-BOZ26RIB.js.map