nexus-agents 2.150.3 → 2.151.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.
@@ -42,7 +42,7 @@ import {
42
42
  } from "./chunk-DHVMSIT5.js";
43
43
 
44
44
  // src/version.ts
45
- var VERSION = true ? "2.150.3" : "dev";
45
+ var VERSION = true ? "2.151.0" : "dev";
46
46
 
47
47
  // src/config/schemas-core.ts
48
48
  import { z } from "zod";
@@ -2132,7 +2132,7 @@ async function runDoctorFix(result) {
2132
2132
  writeLine2("\u2500".repeat(40));
2133
2133
  let fixCount = 0;
2134
2134
  if (!result.dataDirectory.rootExists || result.dataDirectory.subdirectories.some((d) => !d.exists || !d.writable)) {
2135
- const { runSetup } = await import("./setup-command-PPHPJCSN.js");
2135
+ const { runSetup } = await import("./setup-command-JERVOJ5Z.js");
2136
2136
  const setupResult = runSetup({
2137
2137
  skipMcp: true,
2138
2138
  skipRules: true,
@@ -2245,4 +2245,4 @@ export {
2245
2245
  startStdioServer,
2246
2246
  closeServer
2247
2247
  };
2248
- //# sourceMappingURL=chunk-DFK6H3KT.js.map
2248
+ //# sourceMappingURL=chunk-O6PPXOH6.js.map
@@ -550,16 +550,63 @@ Respond with a JSON object containing:
550
550
 
551
551
  ${VOTE_PROMPT_EXAMPLES}`;
552
552
  }
553
+ function stepInString(state, ch) {
554
+ if (ch === "\\") state.escaped = true;
555
+ else if (ch === '"') state.inString = false;
556
+ }
557
+ function stepStructural(state, ch) {
558
+ if (ch === '"') state.inString = true;
559
+ else if (ch === "{") state.closers.push("}");
560
+ else if (ch === "[") state.closers.push("]");
561
+ else if (ch === "}" || ch === "]") {
562
+ if (state.closers.pop() === void 0) return "unbalanced";
563
+ if (state.closers.length === 0) return "complete";
564
+ }
565
+ return "continue";
566
+ }
567
+ function stepJsonScan(state, ch) {
568
+ if (state.escaped) {
569
+ state.escaped = false;
570
+ return "continue";
571
+ }
572
+ if (state.inString) {
573
+ stepInString(state, ch);
574
+ return "continue";
575
+ }
576
+ return stepStructural(state, ch);
577
+ }
578
+ function repairTruncatedObject(fragment, state) {
579
+ let repaired = state.inString ? `${fragment}"` : fragment;
580
+ for (let i = state.closers.length - 1; i >= 0; i--) {
581
+ const closer = state.closers[i];
582
+ if (closer !== void 0) repaired += closer;
583
+ }
584
+ return repaired;
585
+ }
586
+ function extractFirstJsonObject(text) {
587
+ const start = text.indexOf("{");
588
+ if (start === -1) return void 0;
589
+ const state = { closers: [], inString: false, escaped: false };
590
+ for (let i = start; i < text.length; i++) {
591
+ const result = stepJsonScan(state, text[i]);
592
+ if (result === "complete") return text.slice(start, i + 1);
593
+ if (result === "unbalanced") return void 0;
594
+ }
595
+ return state.closers.length > 0 ? repairTruncatedObject(text.slice(start), state) : void 0;
596
+ }
553
597
  function extractJsonFromResponse(text) {
554
- const codeBlockMatch = /```(?:json)?\s*([\s\S]*?)```/i.exec(text);
555
- if (codeBlockMatch?.[1] !== void 0) {
556
- return codeBlockMatch[1].trim();
598
+ const jsonFence = /```json\s*([\s\S]*?)```/i.exec(text);
599
+ if (jsonFence?.[1] !== void 0) {
600
+ return extractFirstJsonObject(jsonFence[1]) ?? jsonFence[1].trim();
557
601
  }
558
- const jsonMatch = /\{[\s\S]*\}/i.exec(text);
559
- if (jsonMatch?.[0] !== void 0) {
560
- return jsonMatch[0];
602
+ for (const match of text.matchAll(/```[a-zA-Z0-9]*\s*([\s\S]*?)```/g)) {
603
+ const inner = match[1];
604
+ if (inner?.trimStart().startsWith("{") === true) {
605
+ const obj = extractFirstJsonObject(inner);
606
+ if (obj !== void 0) return obj;
607
+ }
561
608
  }
562
- return text.trim();
609
+ return extractFirstJsonObject(text) ?? text.trim();
563
610
  }
564
611
  function createFallbackVote(output, _role, reason) {
565
612
  const lower = output.toLowerCase();
@@ -581,6 +628,30 @@ function createFallbackVote(output, _role, reason) {
581
628
  // Mark as synthetic
582
629
  };
583
630
  }
631
+ var REASONING_MAX_CHARS = 4e3;
632
+ var CLAIM_MAX_CHARS = 2e3;
633
+ var TRUNCATION_MARKER = " \u2026[truncated]";
634
+ function clampWithMarker(s, max) {
635
+ if (s.length <= max) return s;
636
+ return s.slice(0, Math.max(0, max - TRUNCATION_MARKER.length)) + TRUNCATION_MARKER;
637
+ }
638
+ function clampOversizeVoteStrings(parsed) {
639
+ if (typeof parsed !== "object" || parsed === null) return parsed;
640
+ const obj = { ...parsed };
641
+ if (typeof obj["reasoning"] === "string") {
642
+ obj["reasoning"] = clampWithMarker(obj["reasoning"], REASONING_MAX_CHARS);
643
+ }
644
+ if (Array.isArray(obj["findings"])) {
645
+ obj["findings"] = obj["findings"].map((finding) => {
646
+ if (typeof finding === "object" && finding !== null && typeof finding["claim"] === "string") {
647
+ const f = finding;
648
+ return { ...f, claim: clampWithMarker(f["claim"], CLAIM_MAX_CHARS) };
649
+ }
650
+ return finding;
651
+ });
652
+ }
653
+ return obj;
654
+ }
584
655
  function buildParsedVote(data) {
585
656
  return {
586
657
  decision: data.decision,
@@ -597,7 +668,7 @@ function parseVoteResponse(output, role, options) {
597
668
  try {
598
669
  const jsonStr = extractJsonFromResponse(output);
599
670
  const parsed = JSON.parse(jsonStr);
600
- const validated = VoteResponseSchema.safeParse(parsed);
671
+ const validated = VoteResponseSchema.safeParse(clampOversizeVoteStrings(parsed));
601
672
  if (validated.success) {
602
673
  return buildParsedVote(validated.data);
603
674
  }
@@ -721,8 +792,12 @@ function buildVoteRequest(role, proposal, timeoutMs, withResponseFormat) {
721
792
  { role: "system", content: VOTER_SYSTEM_PROMPTS[role] },
722
793
  { role: "user", content: buildVotePrompt(proposal) }
723
794
  ],
724
- // 2000 (#2245): JSON envelope + reasoning + YAML findings exceed the old 500.
725
- maxTokens: 2e3,
795
+ // 4000 (#4131): headroom so a findings-bearing verdict (JSON envelope +
796
+ // reasoning + structured findings) isn't cut mid-JSON by the token cap and
797
+ // silently dropped. Was 2000 (#2245, up from 500); large contrarian findings
798
+ // still overflowed it. Non-findings votes stop at natural completion, so the
799
+ // higher cap adds no cost for them.
800
+ maxTokens: 4e3,
726
801
  temperature: 0.3,
727
802
  // Low temperature for consistent evaluations
728
803
  // Thread the vote budget so the CLI timeout doesn't fire first (#3304); pass
@@ -4230,7 +4305,12 @@ var HIGHER_ORDER_ESCALATION_POSTERIOR_FLOOR = 0.65;
4230
4305
  function shouldEscalateLowPosterior(strategy, outcome, quickMode, posteriorApproval) {
4231
4306
  return quickMode && outcome === "approved" && isHigherOrderStrategy(strategy) && posteriorApproval !== void 0 && posteriorApproval < HIGHER_ORDER_ESCALATION_POSTERIOR_FLOOR;
4232
4307
  }
4233
- var ErrorPolicySchema = z6.enum(["reduce_denominator", "count_as_abstain", "fail_closed"]);
4308
+ var ErrorPolicySchema = z6.enum([
4309
+ "reduce_denominator",
4310
+ "count_as_abstain",
4311
+ "fail_closed",
4312
+ "absolute_quorum"
4313
+ ]);
4234
4314
  var VoteThresholdSchema = z6.enum(["majority", "supermajority", "unanimous"]);
4235
4315
  var ERROR_FLOOR_FRACTION = 0.5;
4236
4316
  function getDefaultErrorPolicy(strategy) {
@@ -4248,7 +4328,7 @@ var ConsensusVoteInputSchema = z6.object({
4248
4328
  "Voting strategy: simple_majority (default), supermajority, unanimous, proof_of_learning, or higher_order (Bayesian-optimal)"
4249
4329
  ),
4250
4330
  errorPolicy: ErrorPolicySchema.optional().describe(
4251
- "How to treat voters that errored or timed out (#2630). Default: fail_closed for unanimous only; reduce_denominator for all other strategies incl. higher_order/opinion_wise (#3138 \u2014 a single infra timeout should not void an otherwise-unanimous vote). Regardless of policy, errors > 50% always fails."
4331
+ "How to treat voters that errored or timed out (#2630). Default: fail_closed for unanimous only; reduce_denominator for all other strategies incl. higher_order/opinion_wise (#3138 \u2014 a single infra timeout should not void an otherwise-unanimous vote). Opt-in absolute_quorum (#4132): an errored voter \u2014 especially the contrarian (catfish) \u2014 degrades the verdict to no_quorum (recoverable re-run) instead of being dropped from the denominator; never manufactures approved/rejected from an induced error. Regardless of policy, errors > 50% always fails."
4252
4332
  ),
4253
4333
  quickMode: z6.boolean().optional().default(false).describe("Use 3 agents instead of the full 7-role panel for faster execution"),
4254
4334
  simulateVotes: z6.boolean().optional().default(false).describe(
@@ -4331,12 +4411,69 @@ function panelDegradationWarning(errorCount, total) {
4331
4411
  if (errorCount <= 0 || errorCount >= total) return void 0;
4332
4412
  return `Panel degraded: ${String(errorCount)} of ${String(total)} voters errored; decision rests on ${String(total - errorCount)} voter(s).`;
4333
4413
  }
4414
+ var degradedPanelCount = 0;
4415
+ function absoluteQuorumFraction(strategy) {
4416
+ switch (strategy) {
4417
+ case "supermajority":
4418
+ return 2 / 3;
4419
+ case "unanimous":
4420
+ return 1;
4421
+ default:
4422
+ return 0.5;
4423
+ }
4424
+ }
4425
+ function absoluteQuorumDegradeReason(result, errorCount) {
4426
+ const contrarianVote = result.votes.find((v) => v.role === "catfish");
4427
+ const contrarianOk = contrarianVote !== void 0 && contrarianVote.source !== "error";
4428
+ const contrarianDegraded = result.contrarianRequested === true && !contrarianOk;
4429
+ if (errorCount === 0 && !contrarianDegraded) return void 0;
4430
+ const erroredRoles = result.votes.filter((v) => v.source === "error").map((v) => v.role);
4431
+ const named = contrarianDegraded && !erroredRoles.includes("catfish") ? [...erroredRoles, "catfish"] : erroredRoles;
4432
+ const list = named.length > 0 ? named.join(", ") : "contrarian";
4433
+ return `no_quorum: re-run \u2014 voter(s) [${list}] errored (absolute_quorum)`;
4434
+ }
4435
+ function computeAbsoluteQuorumDecision(result, errorCount, allErrors) {
4436
+ const degradeReason = absoluteQuorumDegradeReason(result, errorCount);
4437
+ if (degradeReason !== void 0) return { decision: "no_quorum", degradeReason };
4438
+ const panel = result.panelSize ?? result.votes.length;
4439
+ const needed = Math.ceil(absoluteQuorumFraction(result.strategy) * panel);
4440
+ const approveCount = result.votes.filter(
4441
+ (v) => v.source !== "error" && v.vote.decision === "approve"
4442
+ ).length;
4443
+ if (result.result.outcome === "approved" && approveCount >= needed) {
4444
+ return { decision: "approved" };
4445
+ }
4446
+ if (result.result.outcome === "rejected" && !allErrors) {
4447
+ return { decision: "rejected" };
4448
+ }
4449
+ return {
4450
+ decision: "no_quorum",
4451
+ degradeReason: `no_quorum: absolute quorum not met (${String(approveCount)}/${String(needed)} approvals over ${String(panel)}-voter panel, absolute_quorum)`
4452
+ };
4453
+ }
4454
+ function resolveVoteDecision(input, result, errorCount) {
4455
+ const allErrors = errorCount === result.votes.length && errorCount > 0;
4456
+ if (result.policyReason !== void 0 || !result.result.quorumReached && allErrors) {
4457
+ return { decision: "no_quorum" };
4458
+ }
4459
+ if (input.errorPolicy === "absolute_quorum") {
4460
+ return computeAbsoluteQuorumDecision(result, errorCount, allErrors);
4461
+ }
4462
+ return { decision: mapOutcomeToDecision(result.result.outcome) };
4463
+ }
4464
+ function applyAbsoluteQuorumTelemetry(response, input, decision, degradeReason) {
4465
+ if (input.errorPolicy === "absolute_quorum" && decision === "no_quorum") {
4466
+ degradedPanelCount++;
4467
+ }
4468
+ if (degradeReason !== void 0) {
4469
+ response.policyReason ??= degradeReason;
4470
+ response.panelWarning ??= degradeReason;
4471
+ }
4472
+ }
4334
4473
  function buildResponse(input, result, costSummary, voteRecord) {
4335
4474
  const proposalTruncated = input.proposal.length > 200 ? input.proposal.slice(0, 200) + "..." : input.proposal;
4336
4475
  const errorCount = result.votes.filter((v) => v.source === "error").length;
4337
- const allErrors = errorCount === result.votes.length && errorCount > 0;
4338
- const errorVoidedVote = result.policyReason !== void 0;
4339
- const decision = errorVoidedVote || !result.result.quorumReached && allErrors ? "no_quorum" : mapOutcomeToDecision(result.result.outcome);
4476
+ const { decision, degradeReason } = resolveVoteDecision(input, result, errorCount);
4340
4477
  const response = {
4341
4478
  proposal: proposalTruncated,
4342
4479
  strategy: result.strategy,
@@ -4359,6 +4496,7 @@ function buildResponse(input, result, costSummary, voteRecord) {
4359
4496
  response.voteRecordNote = voteRecord.detail;
4360
4497
  }
4361
4498
  applyOptionalResponseFields(response, input, result, errorCount, costSummary);
4499
+ applyAbsoluteQuorumTelemetry(response, input, decision, degradeReason);
4362
4500
  return response;
4363
4501
  }
4364
4502
  function applyOptionalResponseFields(response, input, result, errorCount, costSummary) {
@@ -4415,7 +4553,7 @@ function applyErrorPolicy(votes, policy) {
4415
4553
  engineVotes: []
4416
4554
  };
4417
4555
  }
4418
- if (policy === "count_as_abstain") {
4556
+ if (policy === "count_as_abstain" || policy === "absolute_quorum") {
4419
4557
  return {
4420
4558
  shortCircuit: false,
4421
4559
  engineVotes: votes.map(
@@ -5750,9 +5888,10 @@ async function runContrarianCheck(proposal, log) {
5750
5888
  '{"decision":"approve","confidence":0.0-1.0,"reasoning":"why it is acceptable"}'
5751
5889
  ].join("\n");
5752
5890
  const result = await executeExpert("architecture", prompt);
5753
- if (!result.success) return { shouldEscalate: false, reason: "", confidence: 0 };
5891
+ if (!result.success) return { shouldEscalate: false, reason: "", confidence: 0, errored: true };
5754
5892
  const jsonMatch = result.text.match(/\{[\s\S]*\}/);
5755
- if (jsonMatch === null) return { shouldEscalate: false, reason: "", confidence: 0 };
5893
+ if (jsonMatch === null)
5894
+ return { shouldEscalate: false, reason: "", confidence: 0, errored: false };
5756
5895
  const parsed = JSON.parse(jsonMatch[0]);
5757
5896
  const isRejection = parsed.decision === "reject";
5758
5897
  const confidence = typeof parsed.confidence === "number" ? parsed.confidence : 0;
@@ -5762,13 +5901,13 @@ async function runContrarianCheck(proposal, log) {
5762
5901
  confidence,
5763
5902
  reasoning: reasoning.slice(0, 200)
5764
5903
  });
5765
- return { shouldEscalate: true, reason: reasoning, confidence };
5904
+ return { shouldEscalate: true, reason: reasoning, confidence, errored: false };
5766
5905
  }
5767
- return { shouldEscalate: false, reason: "", confidence };
5906
+ return { shouldEscalate: false, reason: "", confidence, errored: false };
5768
5907
  } catch (error) {
5769
5908
  const message = error instanceof Error ? error.message : String(error);
5770
5909
  log.warn("Contrarian check failed; defaulting to no escalation", { error: message });
5771
- return { shouldEscalate: false, reason: "", confidence: 0 };
5910
+ return { shouldEscalate: false, reason: "", confidence: 0, errored: true };
5772
5911
  }
5773
5912
  }
5774
5913
  function buildPolicyShortCircuitResult(args) {
@@ -5785,27 +5924,37 @@ function buildPolicyShortCircuitResult(args) {
5785
5924
  totalTimeMs,
5786
5925
  simulateVotes: args.input.simulateVotes,
5787
5926
  strategy: args.strategy,
5927
+ // #4132: thread the requested panel shape so the absolute_quorum predicate
5928
+ // in buildResponse has PANEL_SIZE + contrarian-presence even on a short-circuit.
5929
+ panelSize: args.roles.length,
5930
+ contrarianRequested: args.roles.includes("catfish"),
5788
5931
  // #3124: surface WHY a high-approval result is still 'rejected' so callers
5789
5932
  // don't mistake a fail-closed policy short-circuit for a genuine rejection.
5790
5933
  policyReason: args.reason
5791
5934
  };
5792
5935
  }
5793
5936
  async function maybeEscalateContrarian(input, outcome, ctx, logger10, opts) {
5794
- if (!input.quickMode || outcome !== "approved" || input.simulateVotes) return void 0;
5937
+ if (!input.quickMode || outcome !== "approved" || input.simulateVotes) return {};
5795
5938
  if (shouldEscalateLowPosterior(ctx.strategy, outcome, input.quickMode, ctx.posteriorApproval)) {
5796
5939
  logger10.warn("Posterior-confidence escalation: re-running with full vote (#3174)", {
5797
5940
  strategy: ctx.strategy,
5798
5941
  posteriorApproval: ctx.posteriorApproval
5799
5942
  });
5800
- return executeVoting({ ...input, quickMode: false }, logger10, opts);
5943
+ return { escalated: await executeVoting({ ...input, quickMode: false }, logger10, opts) };
5801
5944
  }
5802
5945
  const escalation = await runContrarianCheck(input.proposal, logger10);
5803
- if (!escalation.shouldEscalate) return void 0;
5946
+ if (escalation.errored && input.errorPolicy === "absolute_quorum") {
5947
+ logger10.warn("Contrarian check errored under absolute_quorum \u2014 degrading to no_quorum (#4132)");
5948
+ return {
5949
+ degradeReason: "no_quorum: re-run \u2014 contrarian check errored (absolute_quorum quick-mode)"
5950
+ };
5951
+ }
5952
+ if (!escalation.shouldEscalate) return {};
5804
5953
  logger10.warn("Contrarian escalation: re-running with full vote", {
5805
5954
  reason: escalation.reason,
5806
5955
  confidence: escalation.confidence
5807
5956
  });
5808
- return executeVoting({ ...input, quickMode: false }, logger10, opts);
5957
+ return { escalated: await executeVoting({ ...input, quickMode: false }, logger10, opts) };
5809
5958
  }
5810
5959
  async function executeVoting(input, logger10, opts) {
5811
5960
  const strategy = resolveStrategy(input);
@@ -5832,6 +5981,7 @@ async function executeVoting(input, logger10, opts) {
5832
5981
  input,
5833
5982
  strategy,
5834
5983
  algorithm,
5984
+ roles,
5835
5985
  votes,
5836
5986
  errorPolicy,
5837
5987
  reason: policyDecision.reason ?? "error policy short-circuit",
@@ -5850,18 +6000,19 @@ async function executeVoting(input, logger10, opts) {
5850
6000
  }
5851
6001
  );
5852
6002
  recordVotesToTracker(votes, outcome, logger10);
5853
- const escalated = await maybeEscalateContrarian(
6003
+ const escalation = await maybeEscalateContrarian(
5854
6004
  input,
5855
6005
  outcome,
5856
6006
  { strategy, posteriorApproval: higherOrderResult?.posteriorApproval },
5857
6007
  logger10,
5858
6008
  opts
5859
6009
  );
5860
- if (escalated !== void 0) return escalated;
5861
- return finalizeVotingResult({
6010
+ if (escalation.escalated !== void 0) return escalation.escalated;
6011
+ const finalized = finalizeVotingResult({
5862
6012
  input,
5863
6013
  strategy,
5864
6014
  algorithm,
6015
+ roles,
5865
6016
  engineResult,
5866
6017
  higherOrderResult,
5867
6018
  votes,
@@ -5870,6 +6021,11 @@ async function executeVoting(input, logger10, opts) {
5870
6021
  startTime,
5871
6022
  logger: logger10
5872
6023
  });
6024
+ return applyContrarianDegrade(finalized, escalation.degradeReason);
6025
+ }
6026
+ function applyContrarianDegrade(result, degradeReason) {
6027
+ if (degradeReason === void 0 || result.policyReason !== void 0) return result;
6028
+ return { ...result, policyReason: degradeReason };
5873
6029
  }
5874
6030
  async function runConsensusForGoal(goal, logger10 = createLogger({ tool: "consensus_vote" }), gatewayAdapters) {
5875
6031
  return executeVoting(ConsensusVoteInputSchema.parse({ proposal: goal }), logger10, {
@@ -5891,7 +6047,10 @@ function finalizeVotingResult(args) {
5891
6047
  votes: args.votes,
5892
6048
  totalTimeMs,
5893
6049
  simulateVotes: args.input.simulateVotes,
5894
- strategy: args.strategy
6050
+ strategy: args.strategy,
6051
+ // #4132: PANEL_SIZE + contrarian-presence for the absolute_quorum predicate.
6052
+ panelSize: args.roles.length,
6053
+ contrarianRequested: args.roles.includes("catfish")
5895
6054
  };
5896
6055
  if (args.higherOrderResult !== void 0) result.higherOrderResult = args.higherOrderResult;
5897
6056
  return result;
@@ -6108,6 +6267,9 @@ var CONSENSUS_VOTE_TOOL_SCHEMA = {
6108
6267
  strategy: VotingStrategySchema.optional().describe(
6109
6268
  "Voting strategy: simple_majority (default), supermajority, unanimous, proof_of_learning, or higher_order"
6110
6269
  ),
6270
+ errorPolicy: ErrorPolicySchema.optional().describe(
6271
+ "How to treat errored/timed-out voters (#2630): reduce_denominator (default non-strict) | count_as_abstain | fail_closed (default unanimous) | absolute_quorum (#4132 opt-in \u2014 an errored voter, esp. the contrarian, degrades the verdict to no_quorum instead of being dropped; never manufactures approved/rejected from an induced error). Errors > 50% always fails."
6272
+ ),
6111
6273
  quickMode: z10.boolean().optional().default(false).describe("Use 3 agents instead of 7"),
6112
6274
  simulateVotes: z10.boolean().optional().default(false).describe("TESTS ONLY \u2014 random output, must not be used for real decisions (#2319)"),
6113
6275
  ratifies: z10.string().min(1).max(256).optional().describe(
@@ -6212,9 +6374,10 @@ export {
6212
6374
  warnIfSimulatedOutsideTests,
6213
6375
  resetCorrelationTracker,
6214
6376
  createPolicyFailedResult,
6377
+ maybeEscalateContrarian,
6215
6378
  executeVoting,
6216
6379
  runConsensusForGoal,
6217
6380
  CONSENSUS_VOTE_OUTPUT_SCHEMA,
6218
6381
  registerConsensusVoteTool
6219
6382
  };
6220
- //# sourceMappingURL=chunk-SF3AM3SY.js.map
6383
+ //# sourceMappingURL=chunk-UIL37D2V.js.map