nexus-agents 8.51.2 → 8.52.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 (26) hide show
  1. package/dist/{chunk-VFQAGA3Y.js → chunk-6IBZN5NI.js} +2 -2
  2. package/dist/{chunk-XZRPE3LX.js → chunk-CIHUGM4T.js} +3 -3
  3. package/dist/{chunk-U6R6EULS.js → chunk-DR2ZJHCO.js} +2 -2
  4. package/dist/{chunk-6PRVSB5B.js → chunk-FTDEM664.js} +2 -2
  5. package/dist/{chunk-VX6OWQLN.js → chunk-M2327EET.js} +227 -69
  6. package/dist/chunk-M2327EET.js.map +1 -0
  7. package/dist/{chunk-KYKGBKUC.js → chunk-SA7P7PYY.js} +7 -7
  8. package/dist/cli.d.ts +3 -1
  9. package/dist/cli.js +60 -43
  10. package/dist/cli.js.map +1 -1
  11. package/dist/{consensus-vote-HXSN4QDA.js → consensus-vote-NBU47KVJ.js} +4 -4
  12. package/dist/{consensus-vote-types-DQGFfLnp.d.ts → consensus-vote-types-BPGgMg6d.d.ts} +17 -0
  13. package/dist/{improvement-review-XIY4ASVE.js → improvement-review-HAK57UYN.js} +4 -4
  14. package/dist/index.d.ts +7 -2
  15. package/dist/index.js +6 -6
  16. package/dist/{setup-data-dir-TH7JEOW3.js → setup-data-dir-A7EY6JOZ.js} +3 -3
  17. package/package.json +1 -1
  18. package/dist/chunk-VX6OWQLN.js.map +0 -1
  19. /package/dist/{chunk-VFQAGA3Y.js.map → chunk-6IBZN5NI.js.map} +0 -0
  20. /package/dist/{chunk-XZRPE3LX.js.map → chunk-CIHUGM4T.js.map} +0 -0
  21. /package/dist/{chunk-U6R6EULS.js.map → chunk-DR2ZJHCO.js.map} +0 -0
  22. /package/dist/{chunk-6PRVSB5B.js.map → chunk-FTDEM664.js.map} +0 -0
  23. /package/dist/{chunk-KYKGBKUC.js.map → chunk-SA7P7PYY.js.map} +0 -0
  24. /package/dist/{consensus-vote-HXSN4QDA.js.map → consensus-vote-NBU47KVJ.js.map} +0 -0
  25. /package/dist/{improvement-review-XIY4ASVE.js.map → improvement-review-HAK57UYN.js.map} +0 -0
  26. /package/dist/{setup-data-dir-TH7JEOW3.js.map → setup-data-dir-A7EY6JOZ.js.map} +0 -0
@@ -1,6 +1,6 @@
1
1
  // src/version.ts
2
2
  import semver from "semver";
3
- var VERSION = true ? "8.51.2" : "dev";
3
+ var VERSION = true ? "8.52.0" : "dev";
4
4
  var NODE_ENGINE_RANGE = ">=22.5.0";
5
5
  function isNodeVersionSupported(version) {
6
6
  return semver.satisfies(version, NODE_ENGINE_RANGE);
@@ -11,4 +11,4 @@ export {
11
11
  NODE_ENGINE_RANGE,
12
12
  isNodeVersionSupported
13
13
  };
14
- //# sourceMappingURL=chunk-VFQAGA3Y.js.map
14
+ //# sourceMappingURL=chunk-6IBZN5NI.js.map
@@ -8,7 +8,7 @@ import {
8
8
  NODE_ENGINE_RANGE,
9
9
  VERSION,
10
10
  isNodeVersionSupported
11
- } from "./chunk-VFQAGA3Y.js";
11
+ } from "./chunk-6IBZN5NI.js";
12
12
  import {
13
13
  claimGlobalRegistry
14
14
  } from "./chunk-PT36V4VS.js";
@@ -1252,7 +1252,7 @@ async function runDoctorFix(result) {
1252
1252
  writeLine2("\u2500".repeat(40));
1253
1253
  let fixCount = 0;
1254
1254
  if (!result.dataDirectory.rootExists || result.dataDirectory.subdirectories.some((d) => !d.exists || !d.writable)) {
1255
- const { initDataDirectories: initDataDirectories2, describeDataDirFix: describeDataDirFix2 } = await import("./setup-data-dir-TH7JEOW3.js");
1255
+ const { initDataDirectories: initDataDirectories2, describeDataDirFix: describeDataDirFix2 } = await import("./setup-data-dir-A7EY6JOZ.js");
1256
1256
  const outcome = describeDataDirFix2(initDataDirectories2());
1257
1257
  writeLine2(outcome.line);
1258
1258
  if (outcome.counted) fixCount++;
@@ -1354,4 +1354,4 @@ export {
1354
1354
  startStdioServer,
1355
1355
  closeServer
1356
1356
  };
1357
- //# sourceMappingURL=chunk-XZRPE3LX.js.map
1357
+ //# sourceMappingURL=chunk-CIHUGM4T.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-VFQAGA3Y.js";
3
+ } from "./chunk-6IBZN5NI.js";
4
4
  import {
5
5
  emitClawGuardViolation
6
6
  } from "./chunk-R2Z4HK4B.js";
@@ -2360,4 +2360,4 @@ export {
2360
2360
  VOTING_THRESHOLDS,
2361
2361
  ConsensusMetricsSchema
2362
2362
  };
2363
- //# sourceMappingURL=chunk-U6R6EULS.js.map
2363
+ //# sourceMappingURL=chunk-DR2ZJHCO.js.map
@@ -7,7 +7,7 @@ import {
7
7
  toolStructuredError,
8
8
  toolSuccessStructured,
9
9
  wrapToolWithTimeout
10
- } from "./chunk-U6R6EULS.js";
10
+ } from "./chunk-DR2ZJHCO.js";
11
11
  import {
12
12
  JsonlStore,
13
13
  SECURITY_KEYWORDS,
@@ -2275,4 +2275,4 @@ export {
2275
2275
  runImprovementReview,
2276
2276
  registerImprovementReviewTool
2277
2277
  };
2278
- //# sourceMappingURL=chunk-6PRVSB5B.js.map
2278
+ //# sourceMappingURL=chunk-FTDEM664.js.map
@@ -23,10 +23,10 @@ import {
23
23
  toolSuccessStructured,
24
24
  withProgressHeartbeat,
25
25
  wrapToolWithTimeout
26
- } from "./chunk-U6R6EULS.js";
26
+ } from "./chunk-DR2ZJHCO.js";
27
27
  import {
28
28
  VERSION
29
- } from "./chunk-VFQAGA3Y.js";
29
+ } from "./chunk-6IBZN5NI.js";
30
30
  import {
31
31
  UNRESOLVED_MODEL_ID,
32
32
  assessPanelIndependence,
@@ -285,7 +285,10 @@ function resolveAdapterOrFail(options, logger11, allowSimulation) {
285
285
  }
286
286
 
287
287
  // src/cli/voter-prompts.ts
288
- var DEFAULT_PROJECT = "nexus-agents";
288
+ var DEFAULT_VOTER_PROJECT = "nexus-agents";
289
+ function governanceRulesRef(project) {
290
+ return project === DEFAULT_VOTER_PROJECT ? "CLAUDE.md" : "the target project's governance rules";
291
+ }
289
292
  function prReviewModeAddendum() {
290
293
  return `
291
294
  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:
@@ -383,7 +386,7 @@ Your evaluation criteria:
383
386
  - Risk assessment and mitigation
384
387
  - Priority relative to roadmap
385
388
  - Success metrics and validation approach
386
- - Alignment with project goals in CLAUDE.md
389
+ - Alignment with project goals in ${governanceRulesRef(project)}
387
390
  ${voterFooter()}
388
391
 
389
392
  Balance value against effort. Be pragmatic.`;
@@ -417,12 +420,13 @@ But your default posture is skeptical \u2014 look for what others might miss.
417
420
  High-confidence rejections with specific reasoning are your most valuable output.
418
421
  ${prReviewModeAddendum()}`;
419
422
  }
420
- var REUSE_LADDER_CHECK = `6. **Reuse ladder (implementation altitude).** When building IS justified,
423
+ function reuseLadderCheck(project) {
424
+ return `6. **Reuse ladder (implementation altitude).** When building IS justified,
421
425
  gate the SIZE of the build: stop at the first rung that holds, and say
422
426
  which one.
423
427
  1. Does this need to exist at all? \u2192 no: skip it (YAGNI).
424
428
  2. Standard library / language built-in? \u2192 use it.
425
- 3. Native platform feature or an existing nexus-agents substrate
429
+ 3. Native platform feature or an existing ${project} substrate
426
430
  primitive (a canonical-path module, an existing voter/pipeline/
427
431
  store)? \u2192 use it.
428
432
  4. An already-installed dependency? \u2192 use it.
@@ -432,6 +436,7 @@ var REUSE_LADDER_CHECK = `6. **Reuse ladder (implementation altitude).** When bu
432
436
  and accessibility are NEVER the thing cut. The code is small because it is
433
437
  necessary, not golfed. Flag a proposal that reaches for rung 6 when an
434
438
  earlier rung holds as OVER_ENGINEERING.`;
439
+ }
435
440
  function scopeStewardPrompt(project) {
436
441
  return `You are a Scope Steward voting on proposals for the ${project} project.
437
442
 
@@ -463,10 +468,10 @@ Your evaluation criteria \u2014 work through these mandatory checks in your reas
463
468
 
464
469
  5. **Sprawl audit.** Check whether similar functionality already exists
465
470
  in the codebase. If it does, recommend extending \u2014 not forking. The
466
- anti-sprawl policy in CLAUDE.md is specifically the rule this role
471
+ anti-sprawl policy in ${governanceRulesRef(project)} is specifically the rule this role
467
472
  enforces.
468
473
 
469
- ${REUSE_LADDER_CHECK}
474
+ ${reuseLadderCheck(project)}
470
475
 
471
476
  Default bias: REJECT proposals where an existing tool fits, even if our
472
477
  own implementation would be marginally nicer. Only approve when the
@@ -493,7 +498,7 @@ categories are SCOPE_CREEP, YAGNI, and OVER_ENGINEERING.
493
498
  You CAN approve. But your default posture is: "this should not be built;
494
499
  prove me wrong with the build-vs-buy math."`;
495
500
  }
496
- function getVoterPrompts(project = DEFAULT_PROJECT) {
501
+ function getVoterPrompts(project = DEFAULT_VOTER_PROJECT) {
497
502
  return {
498
503
  architect: architectPrompt(project),
499
504
  security: securityPrompt(project),
@@ -999,10 +1004,19 @@ function extractTextFromResponse(content) {
999
1004
  }
1000
1005
  return String(content);
1001
1006
  }
1002
- function buildVoteRequest(role, proposal, timeoutMs, withResponseFormat, options) {
1007
+ function buildVoteRequest({
1008
+ role,
1009
+ proposal,
1010
+ timeoutMs,
1011
+ withResponseFormat,
1012
+ options,
1013
+ project
1014
+ }) {
1003
1015
  const base = {
1004
1016
  messages: [
1005
- { role: "system", content: VOTER_SYSTEM_PROMPTS[role] },
1017
+ // #6110: the seat judges the caller's target project, not this
1018
+ // repository's. `undefined` renders the `nexus-agents` default.
1019
+ { role: "system", content: getVoterPrompts(project)[role] },
1006
1020
  { role: "user", content: buildVotePrompt(proposal, options) }
1007
1021
  ],
1008
1022
  // 4000 (#4131): headroom so a findings-bearing verdict (JSON envelope +
@@ -1026,15 +1040,9 @@ function isStructuredOutputUnsupported(errorMessage) {
1026
1040
  function readTokenCount(value) {
1027
1041
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
1028
1042
  }
1029
- async function runVoteCompletion({
1030
- role,
1031
- proposal,
1032
- adapter,
1033
- timeoutMs,
1034
- withResponseFormat,
1035
- options
1036
- }) {
1037
- const request = buildVoteRequest(role, proposal, timeoutMs, withResponseFormat, options);
1043
+ async function runVoteCompletion(args) {
1044
+ const { role, adapter, timeoutMs } = args;
1045
+ const request = buildVoteRequest(args);
1038
1046
  const timeoutResult = await withTimeout(
1039
1047
  adapter.complete(request),
1040
1048
  timeoutMs,
@@ -1062,8 +1070,9 @@ async function runVoteCompletion({
1062
1070
  cliStderr: response.value.cliStderr
1063
1071
  };
1064
1072
  }
1065
- async function executeSingleVoteAttempt(role, proposal, adapter, timeoutMs, options) {
1066
- const completionArgs = { role, proposal, adapter, timeoutMs, options };
1073
+ async function executeSingleVoteAttempt(role, proposal, adapter, timeoutMs, context = {}) {
1074
+ const { options, project } = context;
1075
+ const completionArgs = { role, proposal, adapter, timeoutMs, options, project };
1067
1076
  let completion = await runVoteCompletion({ ...completionArgs, withResponseFormat: true });
1068
1077
  if (!completion.ok && isStructuredOutputUnsupported(completion.error)) {
1069
1078
  completion = await runVoteCompletion({ ...completionArgs, withResponseFormat: false });
@@ -1093,7 +1102,7 @@ function logAbandonedRetries(logger11, role, attempt, maxRetries) {
1093
1102
  });
1094
1103
  }
1095
1104
  async function executeWithRetries(opts) {
1096
- const { role, proposal, adapter, logger: logger11, timeoutMs, maxRetries, options } = opts;
1105
+ const { role, proposal, adapter, logger: logger11, timeoutMs, maxRetries, options, project } = opts;
1097
1106
  let lastError = "";
1098
1107
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
1099
1108
  if (attempt > 0) {
@@ -1104,7 +1113,10 @@ async function executeWithRetries(opts) {
1104
1113
  await delay(delayMs);
1105
1114
  }
1106
1115
  const attemptStart = Date.now();
1107
- const result = await executeSingleVoteAttempt(role, proposal, adapter, timeoutMs, options);
1116
+ const result = await executeSingleVoteAttempt(role, proposal, adapter, timeoutMs, {
1117
+ options,
1118
+ project
1119
+ });
1108
1120
  const attemptMs = Date.now() - attemptStart;
1109
1121
  if (result.ok) {
1110
1122
  logger11.info("Vote attempt timing", {
@@ -1174,9 +1186,9 @@ function createKeyedSerializer() {
1174
1186
  }
1175
1187
  function raceWithDeadline(p, role, deadlineMs) {
1176
1188
  let timer;
1177
- const timeoutP = new Promise((resolve4) => {
1189
+ const timeoutP = new Promise((resolve5) => {
1178
1190
  timer = setTimeout(() => {
1179
- resolve4(createErrorVoteResult(role, DEADLINE_MESSAGE, deadlineMs));
1191
+ resolve5(createErrorVoteResult(role, DEADLINE_MESSAGE, deadlineMs));
1180
1192
  }, deadlineMs);
1181
1193
  });
1182
1194
  return Promise.race([p, timeoutP]).finally(() => {
@@ -1286,22 +1298,16 @@ function resolveVoteExecution(options) {
1286
1298
  timeoutMs: options?.timeoutMs ?? resolveVoteTimeout(),
1287
1299
  maxRetries: options?.maxRetries ?? VOTE_TIMEOUTS.maxRetries,
1288
1300
  allowSimulation: options?.allowSimulation ?? false,
1289
- declaredOptions: options?.declaredOptions
1301
+ declaredOptions: options?.declaredOptions,
1302
+ project: options?.project
1290
1303
  };
1291
1304
  }
1292
1305
  async function executeAgentVote(role, proposal, adapter, logger11, options) {
1293
1306
  const start = getTimeProvider().now();
1294
- const { timeoutMs, maxRetries, allowSimulation, declaredOptions } = resolveVoteExecution(options);
1307
+ const { timeoutMs, maxRetries, allowSimulation, declaredOptions, project } = resolveVoteExecution(options);
1295
1308
  logger11.info("Executing vote", { role, model: adapter.modelId, provider: adapter.providerId });
1296
- const result = await executeWithRetries({
1297
- role,
1298
- proposal,
1299
- adapter,
1300
- logger: logger11,
1301
- timeoutMs,
1302
- maxRetries,
1303
- options: declaredOptions
1304
- });
1309
+ const retryOptions = { role, proposal, adapter, logger: logger11, timeoutMs, maxRetries, project };
1310
+ const result = await executeWithRetries({ ...retryOptions, options: declaredOptions });
1305
1311
  const processingTimeMs = getTimeProvider().now() - start;
1306
1312
  if (result.ok) {
1307
1313
  const built = buildLlmVoteResult(role, result.vote, result.usage, adapter, processingTimeMs);
@@ -1464,7 +1470,9 @@ async function collectRealVotes(options) {
1464
1470
  allowSimulation: allowSimulation ?? false,
1465
1471
  // #4472: reaches executeAgentVote, which puts the declared options in the
1466
1472
  // prompt and resolves each voter's selection against them.
1467
- declaredOptions: options.declaredOptions
1473
+ declaredOptions: options.declaredOptions,
1474
+ // #6110: likewise the target project, into every seat's system prompt.
1475
+ project: options.project
1468
1476
  };
1469
1477
  const interDelay = options.interAgentDelayMs ?? DEFAULT_INTER_AGENT_DELAY_MS;
1470
1478
  const launchInput = {
@@ -1488,6 +1496,123 @@ async function collectRealVotes(options) {
1488
1496
  return results;
1489
1497
  }
1490
1498
 
1499
+ // src/cli/voter-project.ts
1500
+ import { readFileSync, statSync } from "node:fs";
1501
+ import { dirname, isAbsolute, join, resolve } from "node:path";
1502
+ var VOTER_PROJECT_PATTERN = /^[A-Za-z0-9._/@-]{1,200}$/;
1503
+ var MAX_ANCESTOR_DEPTH = 64;
1504
+ var MAX_READ_BYTES = 64 * 1024;
1505
+ function readBounded(path2) {
1506
+ try {
1507
+ if (statSync(path2).size > MAX_READ_BYTES) return void 0;
1508
+ return readFileSync(path2, "utf-8");
1509
+ } catch {
1510
+ return void 0;
1511
+ }
1512
+ }
1513
+ function locateGitConfig(cwd) {
1514
+ const repoRoot = findRepoRoot(cwd);
1515
+ if (repoRoot === null) return void 0;
1516
+ const dotGit = join(repoRoot, ".git");
1517
+ try {
1518
+ if (statSync(dotGit).isDirectory()) return join(dotGit, "config");
1519
+ } catch {
1520
+ return void 0;
1521
+ }
1522
+ const gitdir = worktreeGitDir(repoRoot, dotGit);
1523
+ if (gitdir === void 0) return void 0;
1524
+ const commondir = readBounded(join(gitdir, "commondir"))?.trim();
1525
+ const commonRoot = commondir === void 0 || commondir === "" ? gitdir : resolve(gitdir, commondir);
1526
+ return join(commonRoot, "config");
1527
+ }
1528
+ function worktreeGitDir(repoRoot, dotGitFile) {
1529
+ const target = readBounded(dotGitFile)?.match(/^gitdir:\s*(.+?)\s*$/m)?.[1];
1530
+ if (target === void 0) return void 0;
1531
+ return isAbsolute(target) ? target : resolve(repoRoot, target);
1532
+ }
1533
+ function originUrlFromConfig(config) {
1534
+ let inOrigin = false;
1535
+ for (const rawLine of config.split("\n")) {
1536
+ const line = rawLine.trim();
1537
+ if (line.startsWith("[")) {
1538
+ inOrigin = /^\[remote\s+"origin"\]$/.test(line);
1539
+ continue;
1540
+ }
1541
+ if (!inOrigin) continue;
1542
+ const match = line.match(/^url\s*=\s*(.+)$/);
1543
+ if (match?.[1] !== void 0) return match[1].trim();
1544
+ }
1545
+ return void 0;
1546
+ }
1547
+ function ownerRepoFromRemoteUrl(url) {
1548
+ const schemeMatch = url.match(/^[a-z][a-z0-9+.-]*:\/\/[^/]+\/(.+)$/i);
1549
+ const scpMatch = url.match(/^[^/@:]+@[^/:]+:(.+)$/);
1550
+ const path2 = schemeMatch?.[1] ?? scpMatch?.[1];
1551
+ if (path2 === void 0) return void 0;
1552
+ const segments = path2.replace(/\/+$/, "").replace(/\.git$/, "").split("/").filter((segment) => segment !== "");
1553
+ if (segments.length < 2) return void 0;
1554
+ return segments.slice(-2).join("/");
1555
+ }
1556
+ function nearestPackageName(cwd) {
1557
+ let current = resolve(cwd);
1558
+ for (let depth = 0; depth < MAX_ANCESTOR_DEPTH; depth++) {
1559
+ const raw = readBounded(join(current, "package.json"));
1560
+ if (raw !== void 0) {
1561
+ try {
1562
+ const parsed = JSON.parse(raw);
1563
+ if (typeof parsed === "object" && parsed !== null && "name" in parsed) {
1564
+ return typeof parsed.name === "string" ? parsed.name : void 0;
1565
+ }
1566
+ } catch {
1567
+ return void 0;
1568
+ }
1569
+ return void 0;
1570
+ }
1571
+ const parent = dirname(current);
1572
+ if (parent === current) return void 0;
1573
+ current = parent;
1574
+ }
1575
+ return void 0;
1576
+ }
1577
+ function patternRejection(candidate) {
1578
+ if (VOTER_PROJECT_PATTERN.test(candidate)) return void 0;
1579
+ return `does not match the project-name pattern ${VOTER_PROJECT_PATTERN.source}`;
1580
+ }
1581
+ function derivedCandidates(cwd) {
1582
+ const configPath = locateGitConfig(cwd);
1583
+ const config = configPath === void 0 ? void 0 : readBounded(configPath);
1584
+ const originUrl = config === void 0 ? void 0 : originUrlFromConfig(config);
1585
+ const ownerRepo = originUrl === void 0 ? void 0 : ownerRepoFromRemoteUrl(originUrl);
1586
+ return [
1587
+ {
1588
+ origin: "git-origin",
1589
+ candidate: ownerRepo,
1590
+ absentReason: originUrl === void 0 ? "no origin remote" : `origin URL carries no owner/repo path: ${originUrl}`
1591
+ },
1592
+ { origin: "package-name", candidate: nearestPackageName(cwd), absentReason: "no package name" }
1593
+ ];
1594
+ }
1595
+ function resolveVoterProject(args) {
1596
+ const rejected = [];
1597
+ if (args.input !== void 0) {
1598
+ const reason = patternRejection(args.input);
1599
+ if (reason === void 0) return { name: args.input, source: "input", rejected };
1600
+ rejected.push({ origin: "input", candidate: args.input, reason });
1601
+ }
1602
+ for (const { origin, candidate, absentReason } of derivedCandidates(args.cwd)) {
1603
+ if (candidate === void 0) {
1604
+ if (origin === "git-origin" && absentReason !== "no origin remote") {
1605
+ rejected.push({ origin, candidate: "", reason: absentReason });
1606
+ }
1607
+ continue;
1608
+ }
1609
+ const reason = patternRejection(candidate);
1610
+ if (reason === void 0) return { name: candidate, source: "derived", rejected };
1611
+ rejected.push({ origin, candidate, reason });
1612
+ }
1613
+ return { name: DEFAULT_VOTER_PROJECT, source: "default", rejected };
1614
+ }
1615
+
1491
1616
  // src/mcp/tools/consensus-vote-option-gate.ts
1492
1617
  var STRATEGY_TO_OPTION_THRESHOLD = {
1493
1618
  unanimous: "unanimous",
@@ -4936,6 +5061,9 @@ var ConsensusVoteInputSchema = z6.object({
4936
5061
  options: z6.array(z6.string().min(1).max(200)).min(2).max(10).optional().describe(
4937
5062
  "Named alternatives for a multi-option proposal (#4472). When present, the threshold must ALSO be cleared by the leading option, in addition to the ordinary approve/reject bar: `unanimous` requires every approver to have chosen the SAME option, and `supermajority`/`majority` measure the leading option's share of approvers. An approving voter whose selection is absent or matches no declared option stays in the denominator and credits no option, so a degraded response can only lower the leading share, never raise it. Omit for an ordinary yes/no vote \u2014 behaviour is then unchanged."
4938
5063
  ),
5064
+ project: z6.string().regex(VOTER_PROJECT_PATTERN).optional().describe(
5065
+ "The project the panel is judging (#6110), e.g. `acme/widgets` \u2014 it replaces `nexus-agents` in every voter's system prompt, so a consuming repository is not judged against this one's mission and governance files. When omitted the name is DERIVED from the server's working directory (the `origin` remote as `owner/repo`, else the nearest `package.json` name) and falls back to `nexus-agents`; the response discloses which on `project.source`. Letters, digits and `._/@-` only, at most 200 characters."
5066
+ ),
4939
5067
  threshold: VoteThresholdSchema.optional().describe(
4940
5068
  "Voting threshold (legacy): majority, supermajority, unanimous. Use strategy instead."
4941
5069
  ),
@@ -5154,6 +5282,10 @@ function appliedThresholdFor(strategy) {
5154
5282
  function contrarianCheckFor(result) {
5155
5283
  return result.contrarianCheck ?? "skipped";
5156
5284
  }
5285
+ function disclosedProject(input, result) {
5286
+ const { name, source } = result.project ?? resolveVoterProject({ input: input.project, cwd: process.cwd() });
5287
+ return { name, source };
5288
+ }
5157
5289
  function buildResponse(input, result, costSummary, voteRecord) {
5158
5290
  const proposalTruncated = input.proposal.length > 200 ? input.proposal.slice(0, 200) + "..." : input.proposal;
5159
5291
  const errorCount = result.votes.filter((v) => v.source === "error").length;
@@ -5176,6 +5308,7 @@ function buildResponse(input, result, costSummary, voteRecord) {
5176
5308
  votes: result.votes.map(toAgentVoteSummary),
5177
5309
  durationMs: result.totalTimeMs,
5178
5310
  simulateVotes: result.simulateVotes,
5311
+ project: disclosedProject(input, result),
5179
5312
  // #3991: surface the authentic-vote-record persistence outcome so a skipped
5180
5313
  // or failed persist is visible to the MCP caller (was WARN-only).
5181
5314
  voteRecordPersisted: voteRecord?.persisted ?? false
@@ -5184,22 +5317,24 @@ function buildResponse(input, result, costSummary, voteRecord) {
5184
5317
  response.voteRecordNote = voteRecord.detail;
5185
5318
  }
5186
5319
  if (result.optionGate !== void 0) {
5187
- const g = result.optionGate;
5188
- response.optionOutcome = {
5189
- tally: g.tally.map((t) => ({ option: t.option, count: t.count })),
5190
- ...g.leadingOption !== void 0 ? { leadingOption: g.leadingOption } : {},
5191
- leadingShare: g.leadingShare,
5192
- approverCount: g.approverCount,
5193
- selectedCount: g.selectedCount,
5194
- unattributedApprovals: g.unattributedApprovals,
5195
- thresholdMet: g.approved,
5196
- ...g.reason !== void 0 ? { vetoReason: g.reason } : {}
5197
- };
5320
+ response.optionOutcome = toOptionOutcome(result.optionGate);
5198
5321
  }
5199
5322
  applyOptionalResponseFields(response, input, result, errorCount, costSummary);
5200
5323
  applyAbsoluteQuorumTelemetry(response, input, decision, resolved.degradeReason);
5201
5324
  return response;
5202
5325
  }
5326
+ function toOptionOutcome(g) {
5327
+ return {
5328
+ tally: g.tally.map((t) => ({ option: t.option, count: t.count })),
5329
+ ...g.leadingOption !== void 0 ? { leadingOption: g.leadingOption } : {},
5330
+ leadingShare: g.leadingShare,
5331
+ approverCount: g.approverCount,
5332
+ selectedCount: g.selectedCount,
5333
+ unattributedApprovals: g.unattributedApprovals,
5334
+ thresholdMet: g.approved,
5335
+ ...g.reason !== void 0 ? { vetoReason: g.reason } : {}
5336
+ };
5337
+ }
5203
5338
  function applyOptionalResponseFields(response, input, result, errorCount, costSummary) {
5204
5339
  response.threshold = appliedThresholdFor(result.strategy);
5205
5340
  if (result.policyReason !== void 0) {
@@ -5283,8 +5418,8 @@ function applyErrorPolicy(votes, policy) {
5283
5418
  }
5284
5419
 
5285
5420
  // src/audit/vote-record-store.ts
5286
- import { appendFileSync as appendFileSync2, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2 } from "node:fs";
5287
- import { dirname, isAbsolute, relative, resolve as resolve2, sep } from "node:path";
5421
+ import { appendFileSync as appendFileSync2, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync3 } from "node:fs";
5422
+ import { dirname as dirname2, isAbsolute as isAbsolute2, relative, resolve as resolve3, sep } from "node:path";
5288
5423
 
5289
5424
  // src/audit/ledger-append.ts
5290
5425
  var UNREADABLE_RECORD_PREFIX = "refusing to persist an unreadable";
@@ -5306,7 +5441,7 @@ function serializeValidatedRecord(schema, record, ledgerName) {
5306
5441
  }
5307
5442
 
5308
5443
  // src/audit/source-checkout-guard.ts
5309
- import { join, resolve } from "node:path";
5444
+ import { join as join2, resolve as resolve2 } from "node:path";
5310
5445
  function isUnderTestRunner() {
5311
5446
  return process.env["VITEST"] !== void 0 || process.env["NODE_ENV"] === "test";
5312
5447
  }
@@ -5314,7 +5449,7 @@ function assertNotSourceCheckoutWrite(filePath, trackedRelPath, overrideEnvVar)
5314
5449
  if (!isUnderTestRunner()) return;
5315
5450
  const here = findRepoRoot(process.cwd());
5316
5451
  if (here === null) return;
5317
- if (resolve(filePath) !== resolve(join(here, trackedRelPath))) return;
5452
+ if (resolve2(filePath) !== resolve2(join2(here, trackedRelPath))) return;
5318
5453
  throw new Error(
5319
5454
  `Refusing to write ${filePath} from a test run (#4415): this is the source checkout's tracked, hash-chained audit file, and a fabricated record that chains cleanly is indistinguishable from a real verdict. Point ${overrideEnvVar} (or the explicit filePath) at a throwaway path such as a temp directory.`
5320
5455
  );
@@ -5765,17 +5900,17 @@ function readLedgerTip(filePath, logger11) {
5765
5900
  }
5766
5901
  }
5767
5902
  function isWithin(root, target) {
5768
- const resolvedRoot = resolve2(root);
5769
- const resolvedTarget = resolve2(target);
5903
+ const resolvedRoot = resolve3(root);
5904
+ const resolvedTarget = resolve3(target);
5770
5905
  if (resolvedTarget === resolvedRoot) return true;
5771
5906
  const rel = relative(resolvedRoot, resolvedTarget);
5772
- return rel !== "" && !rel.startsWith("..") && !isAbsolute(rel);
5907
+ return rel !== "" && !rel.startsWith("..") && !isAbsolute2(rel);
5773
5908
  }
5774
5909
  function resolveVoteRecordsPath() {
5775
5910
  const envPath = process.env[VOTE_RECORDS_PATH_ENV];
5776
5911
  if (envPath !== void 0 && envPath.trim() !== "") {
5777
- const resolved2 = isAbsolute(envPath) ? envPath : resolve2(envPath);
5778
- if (!isAbsolute(envPath) && !isWithin(process.cwd(), resolved2)) {
5912
+ const resolved2 = isAbsolute2(envPath) ? envPath : resolve3(envPath);
5913
+ if (!isAbsolute2(envPath) && !isWithin(process.cwd(), resolved2)) {
5779
5914
  return void 0;
5780
5915
  }
5781
5916
  return resolved2;
@@ -5786,7 +5921,7 @@ function resolveVoteRecordsPath() {
5786
5921
  const underRepoDataDir = resolved.includes(
5787
5922
  `.nexus-agents${sep}${VOTE_RECORDS_DATA_CATEGORY}${sep}`
5788
5923
  );
5789
- if (!isAbsolute(resolved) || !resolved.endsWith(expectedSuffix) || !(underHomedirRoot || underRepoDataDir)) {
5924
+ if (!isAbsolute2(resolved) || !resolved.endsWith(expectedSuffix) || !(underHomedirRoot || underRepoDataDir)) {
5790
5925
  return void 0;
5791
5926
  }
5792
5927
  return resolved;
@@ -5800,7 +5935,7 @@ function persistVoteRecord(opts) {
5800
5935
  }
5801
5936
  assertNotSourceCheckoutWrite(filePath, VOTE_RECORDS_REL_PATH, VOTE_RECORDS_PATH_ENV);
5802
5937
  try {
5803
- mkdirSync2(dirname(filePath), { recursive: true });
5938
+ mkdirSync2(dirname2(filePath), { recursive: true });
5804
5939
  const { maxSequence, lastHash } = readLedgerTip(filePath, logger11);
5805
5940
  const record = buildVoteRecord({
5806
5941
  ...opts,
@@ -5824,7 +5959,7 @@ function persistVoteRecord(opts) {
5824
5959
  }
5825
5960
  function readVoteRecords(filePath) {
5826
5961
  if (!existsSync2(filePath)) return { records: [], invalidLines: [] };
5827
- return parseVoteRecordsText(readFileSync2(filePath, "utf-8"));
5962
+ return parseVoteRecordsText(readFileSync3(filePath, "utf-8"));
5828
5963
  }
5829
5964
  function parseVoteRecordsText(text) {
5830
5965
  const records = [];
@@ -5843,7 +5978,7 @@ function parseVoteRecordsText(text) {
5843
5978
  }
5844
5979
 
5845
5980
  // src/orchestration/outcomes/outcome-store-persistence.ts
5846
- import { appendFileSync as appendFileSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync3 } from "node:fs";
5981
+ import { appendFileSync as appendFileSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync3 } from "node:fs";
5847
5982
  var PersistentOutcomeStore = class extends OutcomeStore {
5848
5983
  filePath;
5849
5984
  logger;
@@ -5901,7 +6036,7 @@ var PersistentOutcomeStore = class extends OutcomeStore {
5901
6036
  return;
5902
6037
  }
5903
6038
  try {
5904
- const content = readFileSync3(this.filePath, "utf-8");
6039
+ const content = readFileSync4(this.filePath, "utf-8");
5905
6040
  const lines = content.split("\n").filter((line) => line.trim().length > 0);
5906
6041
  let loaded = 0;
5907
6042
  let skipped = 0;
@@ -6237,7 +6372,7 @@ function warnOnce(toolName, logger11) {
6237
6372
  }
6238
6373
 
6239
6374
  // src/mcp/jobs/job-result-store.ts
6240
- import { existsSync as existsSync4, readFileSync as readFileSync4, readdirSync, writeFileSync as writeFileSync3, chmodSync } from "node:fs";
6375
+ import { existsSync as existsSync4, readFileSync as readFileSync5, readdirSync, writeFileSync as writeFileSync3, chmodSync } from "node:fs";
6241
6376
  import { z as z8 } from "zod";
6242
6377
  var logger7 = createLogger({ component: "job-result-store" });
6243
6378
  var JobStatusSchema = z8.enum(["pending", "complete", "failed", "cancelled"]);
@@ -6396,7 +6531,7 @@ function readJobResult(jobId) {
6396
6531
  const path2 = nexusDataPath("jobs", `result-${jobId}.json`);
6397
6532
  if (!existsSync4(path2)) return null;
6398
6533
  try {
6399
- const raw = JSON.parse(readFileSync4(path2, "utf-8"));
6534
+ const raw = JSON.parse(readFileSync5(path2, "utf-8"));
6400
6535
  const parsed = JobResultSchema.safeParse(raw);
6401
6536
  if (!parsed.success) {
6402
6537
  logger7.warn("Job result file failed schema check", { jobId, path: path2 });
@@ -6477,7 +6612,7 @@ function unregisterJobAbort(jobId) {
6477
6612
 
6478
6613
  // src/mcp/jobs/job-idempotency.ts
6479
6614
  import { createHash as createHash2, randomUUID } from "node:crypto";
6480
- import { existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "node:fs";
6615
+ import { existsSync as existsSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
6481
6616
  import { z as z9 } from "zod";
6482
6617
  var logger8 = createLogger({ component: "job-idempotency" });
6483
6618
  var IdempotencyIndexEntrySchema = z9.object({
@@ -6574,7 +6709,7 @@ function shortCircuitOrFreshJobId(params) {
6574
6709
  }
6575
6710
  function readIndexEntry(path2) {
6576
6711
  try {
6577
- const raw = JSON.parse(readFileSync5(path2, "utf-8"));
6712
+ const raw = JSON.parse(readFileSync6(path2, "utf-8"));
6578
6713
  const parsed = IdempotencyIndexEntrySchema.safeParse(raw);
6579
6714
  if (!parsed.success) {
6580
6715
  logger8.warn("Idempotency index file failed schema check", { path: path2 });
@@ -7610,8 +7745,22 @@ async function maybeEscalateContrarian(input, outcome, ctx, logger11, opts) {
7610
7745
  contrarianCheck
7611
7746
  };
7612
7747
  }
7748
+ function resolveAndLogVoterProject(input, logger11) {
7749
+ const { name, source, rejected } = resolveVoterProject({ input, cwd: process.cwd() });
7750
+ for (const r of rejected) {
7751
+ logger11.warn("Voter project candidate rejected", {
7752
+ origin: r.origin,
7753
+ candidate: r.candidate,
7754
+ reason: r.reason
7755
+ });
7756
+ }
7757
+ logger11.info("Voter project resolved", { project: name, source });
7758
+ return { name, source };
7759
+ }
7613
7760
  async function executeVoting(input, logger11, opts) {
7614
- const result = await executeVotingInner(input, logger11, opts ?? {});
7761
+ const project = opts?.project ?? resolveAndLogVoterProject(input.project, logger11);
7762
+ const result = await executeVotingInner(input, logger11, { ...opts, project });
7763
+ result.project = project;
7615
7764
  const correlationAlreadyRecorded = result.decision !== void 0;
7616
7765
  const errorCount = result.votes.filter((v) => v.source === "error").length;
7617
7766
  applyOptionGate(input, result);
@@ -7656,6 +7805,8 @@ async function executeVotingInner(input, logger11, opts) {
7656
7805
  ...opts.voteTimeoutMs !== void 0 && { timeoutMs: opts.voteTimeoutMs },
7657
7806
  ...opts.gatewayAdapters !== void 0 && { gatewayAdapters: opts.gatewayAdapters },
7658
7807
  declaredOptions: input.options,
7808
+ // #6110: every seat's system prompt names the caller's project.
7809
+ project: opts.project.name,
7659
7810
  signal: opts.signal
7660
7811
  });
7661
7812
  const policyDecision = applyErrorPolicy(votes, errorPolicy);
@@ -7982,6 +8133,13 @@ var CONSENSUS_VOTE_OUTPUT_SCHEMA = {
7982
8133
  threshold: VoteThresholdSchema.optional(),
7983
8134
  durationMs: z11.number().optional(),
7984
8135
  simulateVotes: z11.boolean().optional(),
8136
+ // #6110: the project the panel judged and how the name was decided. Always
8137
+ // on the response; optional here like every field, because the async
8138
+ // `pending` envelope shares this schema.
8139
+ project: z11.object({
8140
+ name: z11.string().max(200),
8141
+ source: z11.enum(["input", "derived", "default"])
8142
+ }).optional(),
7985
8143
  higherOrderMetadata: z11.object({
7986
8144
  posteriorApproval: z11.number(),
7987
8145
  posteriorRejection: z11.number(),
@@ -8152,4 +8310,4 @@ export {
8152
8310
  CONSENSUS_VOTE_TOOL_SCHEMA,
8153
8311
  registerConsensusVoteTool
8154
8312
  };
8155
- //# sourceMappingURL=chunk-VX6OWQLN.js.map
8313
+ //# sourceMappingURL=chunk-M2327EET.js.map