nexus-agents 8.55.8 → 8.56.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-WL336Y4R.js → chunk-5W55WXLY.js} +2 -2
- package/dist/{chunk-MUSPT4J4.js → chunk-BWP3RTR2.js} +2 -2
- package/dist/{chunk-DZHGR5UO.js → chunk-HXITPFTR.js} +7 -7
- package/dist/{chunk-IEF3YTEH.js → chunk-M2ZQXDSO.js} +3 -3
- package/dist/{chunk-4XRGCSFR.js → chunk-MO6FSEFJ.js} +265 -148
- package/dist/{chunk-4XRGCSFR.js.map → chunk-MO6FSEFJ.js.map} +1 -1
- package/dist/{chunk-ZBZSSNRB.js → chunk-UURJGJTW.js} +2 -2
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +86 -27
- package/dist/cli.js.map +1 -1
- package/dist/{consensus-vote-CJKMBM47.js → consensus-vote-JHCRRDH5.js} +4 -4
- package/dist/{improvement-review-JIKJHIBZ.js → improvement-review-MTQWL4BT.js} +4 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.js +6 -6
- package/dist/{setup-data-dir-YKEJYHAI.js → setup-data-dir-RXY2PWDX.js} +3 -3
- package/package.json +1 -1
- /package/dist/{chunk-WL336Y4R.js.map → chunk-5W55WXLY.js.map} +0 -0
- /package/dist/{chunk-MUSPT4J4.js.map → chunk-BWP3RTR2.js.map} +0 -0
- /package/dist/{chunk-DZHGR5UO.js.map → chunk-HXITPFTR.js.map} +0 -0
- /package/dist/{chunk-IEF3YTEH.js.map → chunk-M2ZQXDSO.js.map} +0 -0
- /package/dist/{chunk-ZBZSSNRB.js.map → chunk-UURJGJTW.js.map} +0 -0
- /package/dist/{consensus-vote-CJKMBM47.js.map → consensus-vote-JHCRRDH5.js.map} +0 -0
- /package/dist/{improvement-review-JIKJHIBZ.js.map → improvement-review-MTQWL4BT.js.map} +0 -0
- /package/dist/{setup-data-dir-YKEJYHAI.js.map → setup-data-dir-RXY2PWDX.js.map} +0 -0
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
toolStructuredError,
|
|
8
8
|
toolSuccessStructured,
|
|
9
9
|
wrapToolWithTimeout
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-BWP3RTR2.js";
|
|
11
11
|
import {
|
|
12
12
|
JsonlStore,
|
|
13
13
|
SECURITY_KEYWORDS,
|
|
@@ -2278,4 +2278,4 @@ export {
|
|
|
2278
2278
|
runImprovementReview,
|
|
2279
2279
|
registerImprovementReviewTool
|
|
2280
2280
|
};
|
|
2281
|
-
//# sourceMappingURL=chunk-
|
|
2281
|
+
//# sourceMappingURL=chunk-5W55WXLY.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
VERSION
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-UURJGJTW.js";
|
|
4
4
|
import {
|
|
5
5
|
emitClawGuardViolation
|
|
6
6
|
} from "./chunk-VZSMNIRN.js";
|
|
@@ -2378,4 +2378,4 @@ export {
|
|
|
2378
2378
|
DEFAULT_CONSENSUS_CONFIG,
|
|
2379
2379
|
ConsensusMetricsSchema
|
|
2380
2380
|
};
|
|
2381
|
-
//# sourceMappingURL=chunk-
|
|
2381
|
+
//# sourceMappingURL=chunk-BWP3RTR2.js.map
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
import {
|
|
22
22
|
DEFAULT_TASK_TTL_MS,
|
|
23
23
|
clampTaskTtl
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-M2ZQXDSO.js";
|
|
25
25
|
import {
|
|
26
26
|
executeExpert
|
|
27
27
|
} from "./chunk-QMGMD6JB.js";
|
|
@@ -62,7 +62,7 @@ import {
|
|
|
62
62
|
toJobSummary,
|
|
63
63
|
updateStage,
|
|
64
64
|
writeJobCancelled
|
|
65
|
-
} from "./chunk-
|
|
65
|
+
} from "./chunk-MO6FSEFJ.js";
|
|
66
66
|
import {
|
|
67
67
|
allOf,
|
|
68
68
|
anyOf
|
|
@@ -82,7 +82,7 @@ import {
|
|
|
82
82
|
} from "./chunk-RNWUIQTZ.js";
|
|
83
83
|
import {
|
|
84
84
|
withPrerequisite
|
|
85
|
-
} from "./chunk-
|
|
85
|
+
} from "./chunk-5W55WXLY.js";
|
|
86
86
|
import {
|
|
87
87
|
NOOP_NOTIFIER,
|
|
88
88
|
RateLimiter,
|
|
@@ -107,7 +107,7 @@ import {
|
|
|
107
107
|
withAuditTrail,
|
|
108
108
|
withProgressHeartbeat,
|
|
109
109
|
wrapToolWithTimeout
|
|
110
|
-
} from "./chunk-
|
|
110
|
+
} from "./chunk-BWP3RTR2.js";
|
|
111
111
|
import {
|
|
112
112
|
createAuditTrail,
|
|
113
113
|
createGraphAuditBridge,
|
|
@@ -46182,7 +46182,7 @@ function buildVotingInput(plan, config) {
|
|
|
46182
46182
|
}
|
|
46183
46183
|
async function executeSingleVote(plan, config, log) {
|
|
46184
46184
|
try {
|
|
46185
|
-
const { executeVoting } = await import("./consensus-vote-
|
|
46185
|
+
const { executeVoting } = await import("./consensus-vote-JHCRRDH5.js");
|
|
46186
46186
|
const input = buildVotingInput(plan, config);
|
|
46187
46187
|
const result = await executeVoting(input, log);
|
|
46188
46188
|
return parseVotingResult(result);
|
|
@@ -47980,7 +47980,7 @@ ${contextBlock}`;
|
|
|
47980
47980
|
const strategy = config.votingStrategy ?? "higher_order";
|
|
47981
47981
|
await postProgress(config, "Vote", `Running consensus with ${strategy} strategy...`);
|
|
47982
47982
|
try {
|
|
47983
|
-
const { executeVoting } = await import("./consensus-vote-
|
|
47983
|
+
const { executeVoting } = await import("./consensus-vote-JHCRRDH5.js");
|
|
47984
47984
|
const votingResult = await executeVoting(
|
|
47985
47985
|
{
|
|
47986
47986
|
proposal: buildVoteProposal(plan, research),
|
|
@@ -57179,4 +57179,4 @@ export {
|
|
|
57179
57179
|
startPipelineEventBridge,
|
|
57180
57180
|
shutdownPipelineEventBridge
|
|
57181
57181
|
};
|
|
57182
|
-
//# sourceMappingURL=chunk-
|
|
57182
|
+
//# sourceMappingURL=chunk-HXITPFTR.js.map
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
NODE_ENGINE_RANGE,
|
|
12
12
|
VERSION,
|
|
13
13
|
isNodeVersionSupported
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-UURJGJTW.js";
|
|
15
15
|
import {
|
|
16
16
|
claimGlobalRegistry
|
|
17
17
|
} from "./chunk-BXP5K5TU.js";
|
|
@@ -1314,7 +1314,7 @@ async function runDoctorFix(result) {
|
|
|
1314
1314
|
writeLine2("\u2500".repeat(40));
|
|
1315
1315
|
let fixCount = 0;
|
|
1316
1316
|
if (!result.dataDirectory.rootExists || result.dataDirectory.subdirectories.some((d) => !d.exists || !d.writable)) {
|
|
1317
|
-
const { initDataDirectories: initDataDirectories2, describeDataDirFix: describeDataDirFix2 } = await import("./setup-data-dir-
|
|
1317
|
+
const { initDataDirectories: initDataDirectories2, describeDataDirFix: describeDataDirFix2 } = await import("./setup-data-dir-RXY2PWDX.js");
|
|
1318
1318
|
const outcome = describeDataDirFix2(initDataDirectories2());
|
|
1319
1319
|
writeLine2(outcome.line);
|
|
1320
1320
|
if (outcome.counted) fixCount++;
|
|
@@ -1416,4 +1416,4 @@ export {
|
|
|
1416
1416
|
startStdioServer,
|
|
1417
1417
|
closeServer
|
|
1418
1418
|
};
|
|
1419
|
-
//# sourceMappingURL=chunk-
|
|
1419
|
+
//# sourceMappingURL=chunk-M2ZQXDSO.js.map
|
|
@@ -25,10 +25,10 @@ import {
|
|
|
25
25
|
toolSuccessStructured,
|
|
26
26
|
withProgressHeartbeat,
|
|
27
27
|
wrapToolWithTimeout
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-BWP3RTR2.js";
|
|
29
29
|
import {
|
|
30
30
|
VERSION
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-UURJGJTW.js";
|
|
32
32
|
import {
|
|
33
33
|
UNRESOLVED_MODEL_ID,
|
|
34
34
|
assessPanelIndependence,
|
|
@@ -6765,31 +6765,164 @@ function warnOnce(toolName, logger11) {
|
|
|
6765
6765
|
}
|
|
6766
6766
|
|
|
6767
6767
|
// src/mcp/jobs/job-result-store.ts
|
|
6768
|
-
import {
|
|
6768
|
+
import {
|
|
6769
|
+
existsSync as existsSync5,
|
|
6770
|
+
readFileSync as readFileSync6,
|
|
6771
|
+
readdirSync,
|
|
6772
|
+
unlinkSync as unlinkSync2,
|
|
6773
|
+
writeFileSync as writeFileSync4,
|
|
6774
|
+
chmodSync
|
|
6775
|
+
} from "node:fs";
|
|
6776
|
+
import { z as z10 } from "zod";
|
|
6777
|
+
|
|
6778
|
+
// src/mcp/jobs/job-idempotency.ts
|
|
6779
|
+
import { createHash as createHash2, randomUUID } from "node:crypto";
|
|
6780
|
+
import { existsSync as existsSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "node:fs";
|
|
6769
6781
|
import { z as z9 } from "zod";
|
|
6770
|
-
var logger7 = createLogger({ component: "job-
|
|
6771
|
-
var
|
|
6772
|
-
var JobResultSchema = z9.object({
|
|
6773
|
-
/** Format version. Currently `1` — bump if the shape changes. */
|
|
6782
|
+
var logger7 = createLogger({ component: "job-idempotency" });
|
|
6783
|
+
var IdempotencyIndexEntrySchema = z9.object({
|
|
6774
6784
|
v: z9.literal(1),
|
|
6785
|
+
/** MCP tool name (e.g. `orchestrate`, `run_workflow`, `consensus_vote`). */
|
|
6786
|
+
tool: z9.string().min(1),
|
|
6787
|
+
/** Caller-supplied idempotency key. */
|
|
6788
|
+
key: z9.string().min(1),
|
|
6789
|
+
/** sha256 of the canonical input JSON. */
|
|
6790
|
+
inputsHash: z9.string().length(64),
|
|
6791
|
+
/** Existing jobId for this (tool, key, inputs) tuple. */
|
|
6775
6792
|
jobId: z9.string().min(1),
|
|
6793
|
+
/** ISO timestamp of first dispatch. */
|
|
6794
|
+
createdAt: z9.iso.datetime()
|
|
6795
|
+
});
|
|
6796
|
+
function sha256Hex(input) {
|
|
6797
|
+
return createHash2("sha256").update(input).digest("hex");
|
|
6798
|
+
}
|
|
6799
|
+
function canonicalJsonStringify(value) {
|
|
6800
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
6801
|
+
if (Array.isArray(value)) return `[${value.map(canonicalJsonStringify).join(",")}]`;
|
|
6802
|
+
const entries = Object.entries(value).filter(([, v]) => v !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
|
|
6803
|
+
return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${canonicalJsonStringify(v)}`).join(",")}}`;
|
|
6804
|
+
}
|
|
6805
|
+
function computeInputsHash(inputs) {
|
|
6806
|
+
return sha256Hex(canonicalJsonStringify(inputs));
|
|
6807
|
+
}
|
|
6808
|
+
function indexFilePath(tool, key) {
|
|
6809
|
+
const filename = `key-${sha256Hex(`${tool}:${key}`)}.json`;
|
|
6810
|
+
return nexusDataPathEnsure("jobs", filename);
|
|
6811
|
+
}
|
|
6812
|
+
function indexFilePathReadOnly(tool, key) {
|
|
6813
|
+
const filename = `key-${sha256Hex(`${tool}:${key}`)}.json`;
|
|
6814
|
+
return nexusDataPath("jobs", filename);
|
|
6815
|
+
}
|
|
6816
|
+
function resolveIdempotency(tool, idempotencyKey, inputs, randomFallback = randomUUID) {
|
|
6817
|
+
if (idempotencyKey === void 0 || idempotencyKey === "") {
|
|
6818
|
+
return { kind: "fresh", jobId: randomFallback() };
|
|
6819
|
+
}
|
|
6820
|
+
const incomingInputsHash = computeInputsHash(inputs);
|
|
6821
|
+
const path2 = indexFilePathReadOnly(tool, idempotencyKey);
|
|
6822
|
+
if (existsSync4(path2)) {
|
|
6823
|
+
const entry = readIndexEntry(path2);
|
|
6824
|
+
if (entry !== null) {
|
|
6825
|
+
if (entry.inputsHash === incomingInputsHash) {
|
|
6826
|
+
return { kind: "replay", jobId: entry.jobId, entry };
|
|
6827
|
+
}
|
|
6828
|
+
return {
|
|
6829
|
+
kind: "collision",
|
|
6830
|
+
existingInputsHash: entry.inputsHash,
|
|
6831
|
+
incomingInputsHash,
|
|
6832
|
+
existingJobId: entry.jobId
|
|
6833
|
+
};
|
|
6834
|
+
}
|
|
6835
|
+
logger7.warn("Idempotency index file unreadable; treating as fresh dispatch", { tool, path: path2 });
|
|
6836
|
+
}
|
|
6837
|
+
const jobId = `job-${tool}-${sha256Hex(`${tool}:${idempotencyKey}:${incomingInputsHash}`).slice(0, 16)}`;
|
|
6838
|
+
return { kind: "fresh", jobId };
|
|
6839
|
+
}
|
|
6840
|
+
function registerIdempotentJob(params) {
|
|
6841
|
+
const path2 = indexFilePath(params.tool, params.idempotencyKey);
|
|
6842
|
+
if (existsSync4(path2)) {
|
|
6843
|
+
logger7.debug("Idempotency index entry already present; skipping write", {
|
|
6844
|
+
tool: params.tool,
|
|
6845
|
+
jobId: params.jobId
|
|
6846
|
+
});
|
|
6847
|
+
return;
|
|
6848
|
+
}
|
|
6849
|
+
const entry = {
|
|
6850
|
+
v: 1,
|
|
6851
|
+
tool: params.tool,
|
|
6852
|
+
key: params.idempotencyKey,
|
|
6853
|
+
inputsHash: computeInputsHash(params.inputs),
|
|
6854
|
+
jobId: params.jobId,
|
|
6855
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6856
|
+
};
|
|
6857
|
+
writeFileSync3(path2, JSON.stringify(entry, null, 2));
|
|
6858
|
+
logger7.debug("Wrote idempotency index entry", { tool: params.tool, jobId: params.jobId });
|
|
6859
|
+
}
|
|
6860
|
+
function shortCircuitOrFreshJobId(params) {
|
|
6861
|
+
const r = resolveIdempotency(
|
|
6862
|
+
params.tool,
|
|
6863
|
+
params.idempotencyKey,
|
|
6864
|
+
params.inputs,
|
|
6865
|
+
params.freshJobId
|
|
6866
|
+
);
|
|
6867
|
+
if (r.kind === "collision") {
|
|
6868
|
+
return { kind: "shortCircuit", value: params.collisionEnvelope(r.existingJobId) };
|
|
6869
|
+
}
|
|
6870
|
+
if (r.kind === "replay") {
|
|
6871
|
+
return { kind: "shortCircuit", value: params.replayEnvelope(r.jobId) };
|
|
6872
|
+
}
|
|
6873
|
+
return { kind: "continue", jobId: r.jobId };
|
|
6874
|
+
}
|
|
6875
|
+
function readIndexEntry(path2) {
|
|
6876
|
+
try {
|
|
6877
|
+
const raw = JSON.parse(readFileSync5(path2, "utf-8"));
|
|
6878
|
+
const parsed = IdempotencyIndexEntrySchema.safeParse(raw);
|
|
6879
|
+
if (!parsed.success) {
|
|
6880
|
+
logger7.warn("Idempotency index file failed schema check", { path: path2 });
|
|
6881
|
+
return null;
|
|
6882
|
+
}
|
|
6883
|
+
return parsed.data;
|
|
6884
|
+
} catch (err2) {
|
|
6885
|
+
logger7.warn("Idempotency index file read failed", {
|
|
6886
|
+
path: path2,
|
|
6887
|
+
error: err2 instanceof Error ? err2.message : String(err2)
|
|
6888
|
+
});
|
|
6889
|
+
return null;
|
|
6890
|
+
}
|
|
6891
|
+
}
|
|
6892
|
+
|
|
6893
|
+
// src/mcp/jobs/job-result-store.ts
|
|
6894
|
+
var logger8 = createLogger({ component: "job-result-store" });
|
|
6895
|
+
var JobStatusSchema = z10.enum(["pending", "complete", "failed", "cancelled"]);
|
|
6896
|
+
var JobResultSchema = z10.object({
|
|
6897
|
+
/** Format version. Currently `1` — bump if the shape changes. */
|
|
6898
|
+
v: z10.literal(1),
|
|
6899
|
+
jobId: z10.string().min(1),
|
|
6776
6900
|
/** Tool that was invoked (e.g. `orchestrate`). */
|
|
6777
|
-
toolName:
|
|
6901
|
+
toolName: z10.string().min(1),
|
|
6778
6902
|
status: JobStatusSchema,
|
|
6779
|
-
createdAt:
|
|
6903
|
+
createdAt: z10.iso.datetime(),
|
|
6780
6904
|
/** Set when the job leaves `pending` — either via `complete` or `failed` or `cancelled`. */
|
|
6781
|
-
completedAt:
|
|
6905
|
+
completedAt: z10.iso.datetime().optional(),
|
|
6782
6906
|
/**
|
|
6783
6907
|
* Structured payload the synchronous mode would have returned. Present
|
|
6784
6908
|
* only when `status === 'complete'`. Shape is tool-specific — readers
|
|
6785
6909
|
* cast to the tool's known output type after status check.
|
|
6786
6910
|
*/
|
|
6787
|
-
result:
|
|
6911
|
+
result: z10.unknown().optional(),
|
|
6788
6912
|
/**
|
|
6789
6913
|
* Failure message when `status === 'failed'`. Cannot be paired with
|
|
6790
6914
|
* `result` — the discriminator is `status`.
|
|
6791
6915
|
*/
|
|
6792
|
-
error:
|
|
6916
|
+
error: z10.string().optional(),
|
|
6917
|
+
/**
|
|
6918
|
+
* Machine-readable reason for a `failed` record that was NOT settled by the
|
|
6919
|
+
* process that ran the job (#6224). `abandoned` is written only by
|
|
6920
|
+
* {@link pruneJobRecords}: the record was `pending` past the runaway guard
|
|
6921
|
+
* (so no live process could still own it) AND past the retention window, and
|
|
6922
|
+
* the sweep rewrote it rather than deleting it. Absent on every record the
|
|
6923
|
+
* job's own process wrote — `error` alone carries those failures.
|
|
6924
|
+
*/
|
|
6925
|
+
errorKind: z10.literal("abandoned").optional(),
|
|
6793
6926
|
/**
|
|
6794
6927
|
* Whether the tool's `run` callback accepts `runAsJob`'s `AbortSignal`
|
|
6795
6928
|
* (#4972).
|
|
@@ -6805,7 +6938,7 @@ var JobResultSchema = z9.object({
|
|
|
6805
6938
|
* "cancellation can reach this tool", not "the work stopped". Absent means
|
|
6806
6939
|
* the writer did not report it, which is not the same as `false`.
|
|
6807
6940
|
*/
|
|
6808
|
-
signalAccepted:
|
|
6941
|
+
signalAccepted: z10.boolean().optional(),
|
|
6809
6942
|
/**
|
|
6810
6943
|
* `VERSION` of the nexus-agents process that WROTE this record (#5008) —
|
|
6811
6944
|
* i.e. the build that ran the job, not the build that reads it back.
|
|
@@ -6826,28 +6959,36 @@ var JobResultSchema = z9.object({
|
|
|
6826
6959
|
* `'dev'` — readers MUST run it through {@link isMeasuredBuildVersion}
|
|
6827
6960
|
* before treating two stamps as comparable.
|
|
6828
6961
|
*/
|
|
6829
|
-
producerVersion:
|
|
6962
|
+
producerVersion: z10.string().optional()
|
|
6830
6963
|
});
|
|
6831
6964
|
var UNMEASURED_BUILD_VERSION = "dev";
|
|
6832
6965
|
function isMeasuredBuildVersion(version) {
|
|
6833
6966
|
return version !== void 0 && version !== "" && version !== UNMEASURED_BUILD_VERSION;
|
|
6834
6967
|
}
|
|
6968
|
+
var ASYNC_JOB_BODY_CLASS = "async-job-body";
|
|
6969
|
+
var ABANDONED_TERMINAL_WRITE_SLACK_MS = 3e4;
|
|
6835
6970
|
function isAbandonedJob(record, nowMs) {
|
|
6971
|
+
return isAbandonedAt(record, nowMs, abandonedAfterMs());
|
|
6972
|
+
}
|
|
6973
|
+
function abandonedAfterMs() {
|
|
6974
|
+
return resolveClassGuardMs(ASYNC_JOB_BODY_CLASS) + ABANDONED_TERMINAL_WRITE_SLACK_MS;
|
|
6975
|
+
}
|
|
6976
|
+
function isAbandonedAt(record, nowMs, abandonedAfter) {
|
|
6836
6977
|
if (record.status !== "pending") return false;
|
|
6837
6978
|
const startedMs = Date.parse(record.createdAt);
|
|
6838
|
-
return nowMs - startedMs >
|
|
6979
|
+
return nowMs - startedMs > abandonedAfter;
|
|
6839
6980
|
}
|
|
6840
6981
|
function jobResultPath(jobId) {
|
|
6841
6982
|
return nexusDataPathEnsure("jobs", `result-${jobId}.json`);
|
|
6842
6983
|
}
|
|
6843
6984
|
function persistJobRecord(path2, record) {
|
|
6844
|
-
|
|
6985
|
+
writeFileSync4(path2, JSON.stringify(record, null, 2), { mode: 384 });
|
|
6845
6986
|
chmodSync(path2, 384);
|
|
6846
6987
|
}
|
|
6847
6988
|
function writeJobPending(jobId, toolName, signalAccepted, producerVersion = VERSION) {
|
|
6848
6989
|
const path2 = jobResultPath(jobId);
|
|
6849
|
-
if (
|
|
6850
|
-
|
|
6990
|
+
if (existsSync5(path2)) {
|
|
6991
|
+
logger8.debug("Job result file already exists \u2014 leaving in place", { jobId });
|
|
6851
6992
|
return;
|
|
6852
6993
|
}
|
|
6853
6994
|
const record = {
|
|
@@ -6860,12 +7001,12 @@ function writeJobPending(jobId, toolName, signalAccepted, producerVersion = VERS
|
|
|
6860
7001
|
producerVersion
|
|
6861
7002
|
};
|
|
6862
7003
|
persistJobRecord(path2, record);
|
|
6863
|
-
|
|
7004
|
+
logger8.debug("Wrote pending job record", { jobId, toolName, signalAccepted });
|
|
6864
7005
|
}
|
|
6865
7006
|
function writeJobComplete(jobId, toolName, result, producerVersion = VERSION) {
|
|
6866
7007
|
const existing = readJobResult(jobId);
|
|
6867
7008
|
if (existing?.status === "cancelled") {
|
|
6868
|
-
|
|
7009
|
+
logger8.debug("Skipping complete write \u2014 job already cancelled (preserving cancellation)", {
|
|
6869
7010
|
jobId,
|
|
6870
7011
|
toolName
|
|
6871
7012
|
});
|
|
@@ -6882,12 +7023,12 @@ function writeJobComplete(jobId, toolName, result, producerVersion = VERSION) {
|
|
|
6882
7023
|
producerVersion
|
|
6883
7024
|
};
|
|
6884
7025
|
persistJobRecord(jobResultPath(jobId), record);
|
|
6885
|
-
|
|
7026
|
+
logger8.debug("Wrote complete job record", { jobId, toolName });
|
|
6886
7027
|
}
|
|
6887
7028
|
function writeJobFailed(jobId, toolName, error, producerVersion = VERSION) {
|
|
6888
7029
|
const existing = readJobResult(jobId);
|
|
6889
7030
|
if (existing?.status === "cancelled") {
|
|
6890
|
-
|
|
7031
|
+
logger8.debug("Skipping failed write \u2014 job already cancelled (preserving cancellation)", {
|
|
6891
7032
|
jobId,
|
|
6892
7033
|
toolName
|
|
6893
7034
|
});
|
|
@@ -6904,7 +7045,7 @@ function writeJobFailed(jobId, toolName, error, producerVersion = VERSION) {
|
|
|
6904
7045
|
producerVersion
|
|
6905
7046
|
};
|
|
6906
7047
|
persistJobRecord(jobResultPath(jobId), record);
|
|
6907
|
-
|
|
7048
|
+
logger8.debug("Wrote failed job record", { jobId, toolName, error });
|
|
6908
7049
|
}
|
|
6909
7050
|
function writeJobCancelled(jobId, toolName, reason, producerVersion = VERSION) {
|
|
6910
7051
|
const record = {
|
|
@@ -6918,21 +7059,21 @@ function writeJobCancelled(jobId, toolName, reason, producerVersion = VERSION) {
|
|
|
6918
7059
|
producerVersion
|
|
6919
7060
|
};
|
|
6920
7061
|
persistJobRecord(jobResultPath(jobId), record);
|
|
6921
|
-
|
|
7062
|
+
logger8.debug("Wrote cancelled job record", { jobId, toolName, reason });
|
|
6922
7063
|
}
|
|
6923
7064
|
function readJobResult(jobId) {
|
|
6924
7065
|
const path2 = nexusDataPath("jobs", `result-${jobId}.json`);
|
|
6925
|
-
if (!
|
|
7066
|
+
if (!existsSync5(path2)) return null;
|
|
6926
7067
|
try {
|
|
6927
|
-
const raw = JSON.parse(
|
|
7068
|
+
const raw = JSON.parse(readFileSync6(path2, "utf-8"));
|
|
6928
7069
|
const parsed = JobResultSchema.safeParse(raw);
|
|
6929
7070
|
if (!parsed.success) {
|
|
6930
|
-
|
|
7071
|
+
logger8.warn("Job result file failed schema check", { jobId, path: path2 });
|
|
6931
7072
|
return null;
|
|
6932
7073
|
}
|
|
6933
7074
|
return parsed.data;
|
|
6934
7075
|
} catch (err2) {
|
|
6935
|
-
|
|
7076
|
+
logger8.warn("Job result file unreadable", {
|
|
6936
7077
|
jobId,
|
|
6937
7078
|
path: path2,
|
|
6938
7079
|
error: err2 instanceof Error ? err2.message : String(err2)
|
|
@@ -6952,14 +7093,14 @@ function toJobSummary(record) {
|
|
|
6952
7093
|
}
|
|
6953
7094
|
function listJobsWithDiagnostics() {
|
|
6954
7095
|
const dir = nexusDataPath("jobs");
|
|
6955
|
-
if (!
|
|
7096
|
+
if (!existsSync5(dir)) {
|
|
6956
7097
|
return { jobs: [], diagnostics: { dirUnreadable: false, unparseableRecords: 0 } };
|
|
6957
7098
|
}
|
|
6958
7099
|
let entries;
|
|
6959
7100
|
try {
|
|
6960
7101
|
entries = readdirSync(dir);
|
|
6961
7102
|
} catch (err2) {
|
|
6962
|
-
|
|
7103
|
+
logger8.warn("jobs directory unreadable", {
|
|
6963
7104
|
dir,
|
|
6964
7105
|
error: err2 instanceof Error ? err2.message : String(err2)
|
|
6965
7106
|
});
|
|
@@ -6984,6 +7125,94 @@ function listJobsWithDiagnostics() {
|
|
|
6984
7125
|
diagnostics: { dirUnreadable: false, unparseableRecords }
|
|
6985
7126
|
};
|
|
6986
7127
|
}
|
|
7128
|
+
var JOB_RECORD_RETENTION_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
7129
|
+
var JOB_PRUNE_SWEEP_INTERVAL_MS = 36e5;
|
|
7130
|
+
function pruneJobRecords(options) {
|
|
7131
|
+
const dir = nexusDataPath("jobs");
|
|
7132
|
+
if (!existsSync5(dir)) {
|
|
7133
|
+
return { deleted: 0, markedAbandoned: 0, kept: 0, unreadable: 0, deletedKeys: 0 };
|
|
7134
|
+
}
|
|
7135
|
+
const entries = readdirSync(dir);
|
|
7136
|
+
const sweep = {
|
|
7137
|
+
nowMs: options.nowMs,
|
|
7138
|
+
retentionMs: options.retentionMs,
|
|
7139
|
+
dryRun: options.dryRun === true,
|
|
7140
|
+
abandonedAfter: abandonedAfterMs()
|
|
7141
|
+
};
|
|
7142
|
+
const counts = { deleted: 0, markedAbandoned: 0, kept: 0, unreadable: 0, deletedKeys: 0 };
|
|
7143
|
+
const removedJobIds = /* @__PURE__ */ new Set();
|
|
7144
|
+
const keyEntries = entries.filter((entry) => /^key-.+\.json$/.test(entry));
|
|
7145
|
+
for (const entry of entries) {
|
|
7146
|
+
const jobId = /^result-(.+)\.json$/.exec(entry)?.[1];
|
|
7147
|
+
if (jobId === void 0) continue;
|
|
7148
|
+
const verdict = sweepRecord(jobId, entry, sweep);
|
|
7149
|
+
counts[verdict] += 1;
|
|
7150
|
+
if (verdict === "deleted") removedJobIds.add(jobId);
|
|
7151
|
+
}
|
|
7152
|
+
for (const entry of keyEntries) {
|
|
7153
|
+
const verdict = sweepKeyEntry(entry, removedJobIds, sweep);
|
|
7154
|
+
if (verdict !== null) counts[verdict] += 1;
|
|
7155
|
+
}
|
|
7156
|
+
logger8.debug("Job record sweep", { dir, ...counts, dryRun: sweep.dryRun });
|
|
7157
|
+
return counts;
|
|
7158
|
+
}
|
|
7159
|
+
function sweepRecord(jobId, entry, sweep) {
|
|
7160
|
+
const record = readJobResult(jobId);
|
|
7161
|
+
if (record === null) return "unreadable";
|
|
7162
|
+
const path2 = nexusDataPath("jobs", entry);
|
|
7163
|
+
if (record.status === "pending") {
|
|
7164
|
+
const pastGuard = isAbandonedAt(record, sweep.nowMs, sweep.abandonedAfter);
|
|
7165
|
+
const pastWindow = isOlderThan(record.createdAt, sweep.nowMs, sweep.retentionMs);
|
|
7166
|
+
if (!(pastGuard && pastWindow)) return "kept";
|
|
7167
|
+
if (!sweep.dryRun) persistJobRecord(path2, abandonedRecord(record, sweep.nowMs));
|
|
7168
|
+
return "markedAbandoned";
|
|
7169
|
+
}
|
|
7170
|
+
const settledAt = record.completedAt ?? record.createdAt;
|
|
7171
|
+
if (!isOlderThan(settledAt, sweep.nowMs, sweep.retentionMs)) return "kept";
|
|
7172
|
+
if (!sweep.dryRun) unlinkSync2(path2);
|
|
7173
|
+
return "deleted";
|
|
7174
|
+
}
|
|
7175
|
+
function sweepKeyEntry(entry, removedJobIds, sweep) {
|
|
7176
|
+
const path2 = nexusDataPath("jobs", entry);
|
|
7177
|
+
const indexEntry = readIndexEntry(path2);
|
|
7178
|
+
if (indexEntry === null) return "unreadable";
|
|
7179
|
+
if (!isOlderThan(indexEntry.createdAt, sweep.nowMs, sweep.retentionMs)) return null;
|
|
7180
|
+
const recordGone = removedJobIds.has(indexEntry.jobId) || !existsSync5(nexusDataPath("jobs", `result-${indexEntry.jobId}.json`));
|
|
7181
|
+
if (!recordGone) return null;
|
|
7182
|
+
if (!sweep.dryRun) unlinkSync2(path2);
|
|
7183
|
+
return "deletedKeys";
|
|
7184
|
+
}
|
|
7185
|
+
function isOlderThan(isoTimestamp, nowMs, windowMs) {
|
|
7186
|
+
return nowMs - Date.parse(isoTimestamp) > windowMs;
|
|
7187
|
+
}
|
|
7188
|
+
function abandonedRecord(record, nowMs) {
|
|
7189
|
+
const producer = record.producerVersion ?? "unrecorded";
|
|
7190
|
+
return {
|
|
7191
|
+
...record,
|
|
7192
|
+
status: "failed",
|
|
7193
|
+
completedAt: new Date(nowMs).toISOString(),
|
|
7194
|
+
error: `abandoned: pending since ${record.createdAt} under producer ${producer}; no process settled it within the runaway guard, and the retention sweep marked it failed rather than deleting the evidence`,
|
|
7195
|
+
errorKind: "abandoned",
|
|
7196
|
+
producerVersion: VERSION
|
|
7197
|
+
};
|
|
7198
|
+
}
|
|
7199
|
+
var lastSweepAtMsByDir = /* @__PURE__ */ new Map();
|
|
7200
|
+
function pruneJobRecordsIfDue(nowMs) {
|
|
7201
|
+
const dir = nexusDataPath("jobs");
|
|
7202
|
+
const lastSweepAtMs = lastSweepAtMsByDir.get(dir);
|
|
7203
|
+
if (lastSweepAtMs !== void 0 && nowMs - lastSweepAtMs < JOB_PRUNE_SWEEP_INTERVAL_MS) {
|
|
7204
|
+
return null;
|
|
7205
|
+
}
|
|
7206
|
+
lastSweepAtMsByDir.set(dir, nowMs);
|
|
7207
|
+
try {
|
|
7208
|
+
return pruneJobRecords({ nowMs, retentionMs: JOB_RECORD_RETENTION_MS });
|
|
7209
|
+
} catch (err2) {
|
|
7210
|
+
logger8.warn("Job record sweep failed", {
|
|
7211
|
+
error: err2 instanceof Error ? err2.message : String(err2)
|
|
7212
|
+
});
|
|
7213
|
+
return null;
|
|
7214
|
+
}
|
|
7215
|
+
}
|
|
6987
7216
|
|
|
6988
7217
|
// src/mcp/jobs/job-abort-registry.ts
|
|
6989
7218
|
var controllers = /* @__PURE__ */ new Map();
|
|
@@ -7003,121 +7232,6 @@ function unregisterJobAbort(jobId) {
|
|
|
7003
7232
|
controllers.delete(jobId);
|
|
7004
7233
|
}
|
|
7005
7234
|
|
|
7006
|
-
// src/mcp/jobs/job-idempotency.ts
|
|
7007
|
-
import { createHash as createHash2, randomUUID } from "node:crypto";
|
|
7008
|
-
import { existsSync as existsSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
|
|
7009
|
-
import { z as z10 } from "zod";
|
|
7010
|
-
var logger8 = createLogger({ component: "job-idempotency" });
|
|
7011
|
-
var IdempotencyIndexEntrySchema = z10.object({
|
|
7012
|
-
v: z10.literal(1),
|
|
7013
|
-
/** MCP tool name (e.g. `orchestrate`, `run_workflow`, `consensus_vote`). */
|
|
7014
|
-
tool: z10.string().min(1),
|
|
7015
|
-
/** Caller-supplied idempotency key. */
|
|
7016
|
-
key: z10.string().min(1),
|
|
7017
|
-
/** sha256 of the canonical input JSON. */
|
|
7018
|
-
inputsHash: z10.string().length(64),
|
|
7019
|
-
/** Existing jobId for this (tool, key, inputs) tuple. */
|
|
7020
|
-
jobId: z10.string().min(1),
|
|
7021
|
-
/** ISO timestamp of first dispatch. */
|
|
7022
|
-
createdAt: z10.iso.datetime()
|
|
7023
|
-
});
|
|
7024
|
-
function sha256Hex(input) {
|
|
7025
|
-
return createHash2("sha256").update(input).digest("hex");
|
|
7026
|
-
}
|
|
7027
|
-
function canonicalJsonStringify(value) {
|
|
7028
|
-
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
7029
|
-
if (Array.isArray(value)) return `[${value.map(canonicalJsonStringify).join(",")}]`;
|
|
7030
|
-
const entries = Object.entries(value).filter(([, v]) => v !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
|
|
7031
|
-
return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${canonicalJsonStringify(v)}`).join(",")}}`;
|
|
7032
|
-
}
|
|
7033
|
-
function computeInputsHash(inputs) {
|
|
7034
|
-
return sha256Hex(canonicalJsonStringify(inputs));
|
|
7035
|
-
}
|
|
7036
|
-
function indexFilePath(tool, key) {
|
|
7037
|
-
const filename = `key-${sha256Hex(`${tool}:${key}`)}.json`;
|
|
7038
|
-
return nexusDataPathEnsure("jobs", filename);
|
|
7039
|
-
}
|
|
7040
|
-
function indexFilePathReadOnly(tool, key) {
|
|
7041
|
-
const filename = `key-${sha256Hex(`${tool}:${key}`)}.json`;
|
|
7042
|
-
return nexusDataPath("jobs", filename);
|
|
7043
|
-
}
|
|
7044
|
-
function resolveIdempotency(tool, idempotencyKey, inputs, randomFallback = randomUUID) {
|
|
7045
|
-
if (idempotencyKey === void 0 || idempotencyKey === "") {
|
|
7046
|
-
return { kind: "fresh", jobId: randomFallback() };
|
|
7047
|
-
}
|
|
7048
|
-
const incomingInputsHash = computeInputsHash(inputs);
|
|
7049
|
-
const path2 = indexFilePathReadOnly(tool, idempotencyKey);
|
|
7050
|
-
if (existsSync5(path2)) {
|
|
7051
|
-
const entry = readIndexEntry(path2);
|
|
7052
|
-
if (entry !== null) {
|
|
7053
|
-
if (entry.inputsHash === incomingInputsHash) {
|
|
7054
|
-
return { kind: "replay", jobId: entry.jobId, entry };
|
|
7055
|
-
}
|
|
7056
|
-
return {
|
|
7057
|
-
kind: "collision",
|
|
7058
|
-
existingInputsHash: entry.inputsHash,
|
|
7059
|
-
incomingInputsHash,
|
|
7060
|
-
existingJobId: entry.jobId
|
|
7061
|
-
};
|
|
7062
|
-
}
|
|
7063
|
-
logger8.warn("Idempotency index file unreadable; treating as fresh dispatch", { tool, path: path2 });
|
|
7064
|
-
}
|
|
7065
|
-
const jobId = `job-${tool}-${sha256Hex(`${tool}:${idempotencyKey}:${incomingInputsHash}`).slice(0, 16)}`;
|
|
7066
|
-
return { kind: "fresh", jobId };
|
|
7067
|
-
}
|
|
7068
|
-
function registerIdempotentJob(params) {
|
|
7069
|
-
const path2 = indexFilePath(params.tool, params.idempotencyKey);
|
|
7070
|
-
if (existsSync5(path2)) {
|
|
7071
|
-
logger8.debug("Idempotency index entry already present; skipping write", {
|
|
7072
|
-
tool: params.tool,
|
|
7073
|
-
jobId: params.jobId
|
|
7074
|
-
});
|
|
7075
|
-
return;
|
|
7076
|
-
}
|
|
7077
|
-
const entry = {
|
|
7078
|
-
v: 1,
|
|
7079
|
-
tool: params.tool,
|
|
7080
|
-
key: params.idempotencyKey,
|
|
7081
|
-
inputsHash: computeInputsHash(params.inputs),
|
|
7082
|
-
jobId: params.jobId,
|
|
7083
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7084
|
-
};
|
|
7085
|
-
writeFileSync4(path2, JSON.stringify(entry, null, 2));
|
|
7086
|
-
logger8.debug("Wrote idempotency index entry", { tool: params.tool, jobId: params.jobId });
|
|
7087
|
-
}
|
|
7088
|
-
function shortCircuitOrFreshJobId(params) {
|
|
7089
|
-
const r = resolveIdempotency(
|
|
7090
|
-
params.tool,
|
|
7091
|
-
params.idempotencyKey,
|
|
7092
|
-
params.inputs,
|
|
7093
|
-
params.freshJobId
|
|
7094
|
-
);
|
|
7095
|
-
if (r.kind === "collision") {
|
|
7096
|
-
return { kind: "shortCircuit", value: params.collisionEnvelope(r.existingJobId) };
|
|
7097
|
-
}
|
|
7098
|
-
if (r.kind === "replay") {
|
|
7099
|
-
return { kind: "shortCircuit", value: params.replayEnvelope(r.jobId) };
|
|
7100
|
-
}
|
|
7101
|
-
return { kind: "continue", jobId: r.jobId };
|
|
7102
|
-
}
|
|
7103
|
-
function readIndexEntry(path2) {
|
|
7104
|
-
try {
|
|
7105
|
-
const raw = JSON.parse(readFileSync6(path2, "utf-8"));
|
|
7106
|
-
const parsed = IdempotencyIndexEntrySchema.safeParse(raw);
|
|
7107
|
-
if (!parsed.success) {
|
|
7108
|
-
logger8.warn("Idempotency index file failed schema check", { path: path2 });
|
|
7109
|
-
return null;
|
|
7110
|
-
}
|
|
7111
|
-
return parsed.data;
|
|
7112
|
-
} catch (err2) {
|
|
7113
|
-
logger8.warn("Idempotency index file read failed", {
|
|
7114
|
-
path: path2,
|
|
7115
|
-
error: err2 instanceof Error ? err2.message : String(err2)
|
|
7116
|
-
});
|
|
7117
|
-
return null;
|
|
7118
|
-
}
|
|
7119
|
-
}
|
|
7120
|
-
|
|
7121
7235
|
// src/mcp/jobs/job-concurrency.ts
|
|
7122
7236
|
var logger9 = createLogger({ component: "job-concurrency" });
|
|
7123
7237
|
var DEFAULT_JOB_CAPS = {
|
|
@@ -7793,6 +7907,7 @@ function runAsJob(params) {
|
|
|
7793
7907
|
return env.busy(suggestRetryAfterMs(params.toolName), params.toolName);
|
|
7794
7908
|
}
|
|
7795
7909
|
const jobId = idempotency.jobId;
|
|
7910
|
+
pruneJobRecordsIfDue(getTimeProvider().now());
|
|
7796
7911
|
writeJobPending(jobId, params.toolName, params.run.length >= 3, params.producerVersion);
|
|
7797
7912
|
if (params.idempotencyKey !== void 0 && params.idempotencyKey !== "") {
|
|
7798
7913
|
registerIdempotentJob({
|
|
@@ -8532,6 +8647,8 @@ function registerConsensusVoteTool(server, deps) {
|
|
|
8532
8647
|
|
|
8533
8648
|
export {
|
|
8534
8649
|
VOTER_ROLES,
|
|
8650
|
+
resolveIdempotency,
|
|
8651
|
+
registerIdempotentJob,
|
|
8535
8652
|
JobStatusSchema,
|
|
8536
8653
|
isMeasuredBuildVersion,
|
|
8537
8654
|
isAbandonedJob,
|
|
@@ -8539,9 +8656,9 @@ export {
|
|
|
8539
8656
|
readJobResult,
|
|
8540
8657
|
toJobSummary,
|
|
8541
8658
|
listJobsWithDiagnostics,
|
|
8659
|
+
JOB_RECORD_RETENTION_MS,
|
|
8660
|
+
pruneJobRecords,
|
|
8542
8661
|
abortJob,
|
|
8543
|
-
resolveIdempotency,
|
|
8544
|
-
registerIdempotentJob,
|
|
8545
8662
|
listTaskStateIds,
|
|
8546
8663
|
initTaskState,
|
|
8547
8664
|
appendBlocker,
|
|
@@ -8630,4 +8747,4 @@ export {
|
|
|
8630
8747
|
CONSENSUS_VOTE_TOOL_SCHEMA,
|
|
8631
8748
|
registerConsensusVoteTool
|
|
8632
8749
|
};
|
|
8633
|
-
//# sourceMappingURL=chunk-
|
|
8750
|
+
//# sourceMappingURL=chunk-MO6FSEFJ.js.map
|