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.
- package/dist/{chunk-VFQAGA3Y.js → chunk-6IBZN5NI.js} +2 -2
- package/dist/{chunk-XZRPE3LX.js → chunk-CIHUGM4T.js} +3 -3
- package/dist/{chunk-U6R6EULS.js → chunk-DR2ZJHCO.js} +2 -2
- package/dist/{chunk-6PRVSB5B.js → chunk-FTDEM664.js} +2 -2
- package/dist/{chunk-VX6OWQLN.js → chunk-M2327EET.js} +227 -69
- package/dist/chunk-M2327EET.js.map +1 -0
- package/dist/{chunk-KYKGBKUC.js → chunk-SA7P7PYY.js} +7 -7
- package/dist/cli.d.ts +3 -1
- package/dist/cli.js +60 -43
- package/dist/cli.js.map +1 -1
- package/dist/{consensus-vote-HXSN4QDA.js → consensus-vote-NBU47KVJ.js} +4 -4
- package/dist/{consensus-vote-types-DQGFfLnp.d.ts → consensus-vote-types-BPGgMg6d.d.ts} +17 -0
- package/dist/{improvement-review-XIY4ASVE.js → improvement-review-HAK57UYN.js} +4 -4
- package/dist/index.d.ts +7 -2
- package/dist/index.js +6 -6
- package/dist/{setup-data-dir-TH7JEOW3.js → setup-data-dir-A7EY6JOZ.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-VX6OWQLN.js.map +0 -1
- /package/dist/{chunk-VFQAGA3Y.js.map → chunk-6IBZN5NI.js.map} +0 -0
- /package/dist/{chunk-XZRPE3LX.js.map → chunk-CIHUGM4T.js.map} +0 -0
- /package/dist/{chunk-U6R6EULS.js.map → chunk-DR2ZJHCO.js.map} +0 -0
- /package/dist/{chunk-6PRVSB5B.js.map → chunk-FTDEM664.js.map} +0 -0
- /package/dist/{chunk-KYKGBKUC.js.map → chunk-SA7P7PYY.js.map} +0 -0
- /package/dist/{consensus-vote-HXSN4QDA.js.map → consensus-vote-NBU47KVJ.js.map} +0 -0
- /package/dist/{improvement-review-XIY4ASVE.js.map → improvement-review-HAK57UYN.js.map} +0 -0
- /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.
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
1357
|
+
//# sourceMappingURL=chunk-CIHUGM4T.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
VERSION
|
|
3
|
-
} from "./chunk-
|
|
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-
|
|
2363
|
+
//# sourceMappingURL=chunk-DR2ZJHCO.js.map
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
toolStructuredError,
|
|
8
8
|
toolSuccessStructured,
|
|
9
9
|
wrapToolWithTimeout
|
|
10
|
-
} from "./chunk-
|
|
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-
|
|
2278
|
+
//# sourceMappingURL=chunk-FTDEM664.js.map
|
|
@@ -23,10 +23,10 @@ import {
|
|
|
23
23
|
toolSuccessStructured,
|
|
24
24
|
withProgressHeartbeat,
|
|
25
25
|
wrapToolWithTimeout
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-DR2ZJHCO.js";
|
|
27
27
|
import {
|
|
28
28
|
VERSION
|
|
29
|
-
} from "./chunk-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
471
|
+
anti-sprawl policy in ${governanceRulesRef(project)} is specifically the rule this role
|
|
467
472
|
enforces.
|
|
468
473
|
|
|
469
|
-
${
|
|
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 =
|
|
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(
|
|
1007
|
+
function buildVoteRequest({
|
|
1008
|
+
role,
|
|
1009
|
+
proposal,
|
|
1010
|
+
timeoutMs,
|
|
1011
|
+
withResponseFormat,
|
|
1012
|
+
options,
|
|
1013
|
+
project
|
|
1014
|
+
}) {
|
|
1003
1015
|
const base = {
|
|
1004
1016
|
messages: [
|
|
1005
|
-
|
|
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
|
-
|
|
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,
|
|
1066
|
-
const
|
|
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,
|
|
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((
|
|
1189
|
+
const timeoutP = new Promise((resolve5) => {
|
|
1178
1190
|
timer = setTimeout(() => {
|
|
1179
|
-
|
|
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
|
|
1297
|
-
|
|
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
|
-
|
|
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
|
|
5287
|
-
import { dirname, isAbsolute, relative, resolve as
|
|
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 (
|
|
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 =
|
|
5769
|
-
const resolvedTarget =
|
|
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("..") && !
|
|
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 =
|
|
5778
|
-
if (!
|
|
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 (!
|
|
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(
|
|
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(
|
|
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
|
|
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 =
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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
|
|
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-
|
|
8313
|
+
//# sourceMappingURL=chunk-M2327EET.js.map
|