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