@kontextmind/kxm 0.7.24 → 0.7.26
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/.claude-plugin/marketplace.json +1 -1
- package/package.json +1 -1
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/cli.js +117 -12
- package/plugins/kxm/dist/mcp-server.js +1 -1
- package/plugins/kxm/dist/runtime.js +265 -25
- package/plugins/kxm/dist/vnext-runtime-supervisor.js +265 -25
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/skills/kxm-runs/SKILL.md +4 -0
- package/plugins/kxm/src/cli/vnext.ts +133 -10
- package/plugins/kxm/src/cli.ts +14 -2
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/vnext-engine-fold.ts +63 -2
- package/plugins/kxm/src/vnext-engine-gate-records.ts +19 -6
- package/plugins/kxm/src/vnext-engine.ts +155 -9
- package/plugins/kxm/src/vnext-runtime-owner.ts +1 -0
- package/plugins/kxm/src/vnext-runtime-store.ts +22 -5
- package/plugins/kxm/src/vnext-runtime-supervisor.ts +62 -2
- package/plugins/kxm/src/vnext-runtime.ts +17 -3
- package/schemas/vnext/drive-receipt.schema.json +17 -1
- package/schemas/vnext/run-event.schema.json +29 -0
|
@@ -18764,9 +18764,15 @@ var VnextRunEventStore = class {
|
|
|
18764
18764
|
}
|
|
18765
18765
|
driveReceiptsForRun(runId, limit = 20) {
|
|
18766
18766
|
const rows = this.database.prepare(`
|
|
18767
|
-
SELECT receipt FROM drive_receipts WHERE run_id = ? ORDER BY closed_at DESC, drive_id DESC LIMIT ?
|
|
18767
|
+
SELECT drive_id, receipt FROM drive_receipts WHERE run_id = ? ORDER BY closed_at DESC, drive_id DESC LIMIT ?
|
|
18768
18768
|
`).all(runId, limit);
|
|
18769
|
-
return rows.map((row) =>
|
|
18769
|
+
return rows.map((row) => {
|
|
18770
|
+
try {
|
|
18771
|
+
return parseDriveReceipt(row.receipt);
|
|
18772
|
+
} catch {
|
|
18773
|
+
return { driveId: row.drive_id, divergence: "receipt unreadable" };
|
|
18774
|
+
}
|
|
18775
|
+
});
|
|
18770
18776
|
}
|
|
18771
18777
|
insertCapability(row) {
|
|
18772
18778
|
this.database.prepare(`
|
|
@@ -19397,6 +19403,16 @@ var ATTEMPT_EDGES = {
|
|
|
19397
19403
|
executing: /* @__PURE__ */ new Set(["settling"]),
|
|
19398
19404
|
settling: /* @__PURE__ */ new Set(["terminal"])
|
|
19399
19405
|
};
|
|
19406
|
+
function effectiveRunDurationBudget(planLimits, projectLimits) {
|
|
19407
|
+
const workflow = planLimits?.maxRunDurationMs;
|
|
19408
|
+
const project = projectLimits?.maxRunDurationMs;
|
|
19409
|
+
if (workflow === void 0 && project === void 0) return void 0;
|
|
19410
|
+
if (workflow !== void 0 && project !== void 0) {
|
|
19411
|
+
return { budgetMs: Math.min(workflow, project), source: "both" };
|
|
19412
|
+
}
|
|
19413
|
+
if (workflow !== void 0) return { budgetMs: workflow, source: "workflow" };
|
|
19414
|
+
return { budgetMs: project, source: "project" };
|
|
19415
|
+
}
|
|
19400
19416
|
function emptyPanel() {
|
|
19401
19417
|
return { order: [], assignments: /* @__PURE__ */ Object.create(null) };
|
|
19402
19418
|
}
|
|
@@ -19628,7 +19644,7 @@ function foldVnextRunState(run, plan, events, options = {}) {
|
|
|
19628
19644
|
foldRunStatus(state, plan, event);
|
|
19629
19645
|
break;
|
|
19630
19646
|
case "run.cancel_requested":
|
|
19631
|
-
foldCancelRequested(state, event);
|
|
19647
|
+
foldCancelRequested(state, event, plan, options);
|
|
19632
19648
|
break;
|
|
19633
19649
|
case "run.drive_opened":
|
|
19634
19650
|
foldDriveOpened(state, event);
|
|
@@ -19757,6 +19773,9 @@ function foldRunStatus(state, plan, event) {
|
|
|
19757
19773
|
if ((status === "running" || status === "cancelling") && !plan) {
|
|
19758
19774
|
throw runtimeError("run_plan_missing", state.runId, `status ${status} requires a pinned run plan`);
|
|
19759
19775
|
}
|
|
19776
|
+
if (status === "running" && state.runningSince === void 0) {
|
|
19777
|
+
state.runningSince = event.occurredAt;
|
|
19778
|
+
}
|
|
19760
19779
|
if (TERMINAL_RUN.has(status)) {
|
|
19761
19780
|
assertTerminalRunStatus(state, plan, status, event);
|
|
19762
19781
|
if (typeof event.payload.reason === "string") state.terminalReason = event.payload.reason;
|
|
@@ -19855,10 +19874,36 @@ function foldDriveOpened(state, event) {
|
|
|
19855
19874
|
}
|
|
19856
19875
|
state.drive = { driveId, mode, openedSequence: event.sequence };
|
|
19857
19876
|
}
|
|
19858
|
-
function foldCancelRequested(state, event) {
|
|
19877
|
+
function foldCancelRequested(state, event, plan, options) {
|
|
19859
19878
|
if (state.cancelRequested || TERMINAL_RUN.has(state.status)) {
|
|
19860
19879
|
throw runtimeError("run_events_illegal", state.runId, "duplicate run.cancel_requested is not legal");
|
|
19861
19880
|
}
|
|
19881
|
+
if (event.payload.reason === "budget_run_duration") {
|
|
19882
|
+
const budget = event.payload.budget;
|
|
19883
|
+
if (!budget || typeof budget !== "object" || Array.isArray(budget)) {
|
|
19884
|
+
throw runtimeError("run_events_illegal", state.runId, "budget_run_duration requires a budget payload");
|
|
19885
|
+
}
|
|
19886
|
+
const body = budget;
|
|
19887
|
+
const budgetMs = body.budgetMs;
|
|
19888
|
+
const elapsedMs = body.elapsedMs;
|
|
19889
|
+
const source = body.source;
|
|
19890
|
+
if (typeof budgetMs !== "number" || !Number.isInteger(budgetMs) || budgetMs < 0) {
|
|
19891
|
+
throw runtimeError("run_events_illegal", state.runId, "budget_run_duration budgetMs is invalid");
|
|
19892
|
+
}
|
|
19893
|
+
if (typeof elapsedMs !== "number" || !Number.isInteger(elapsedMs) || elapsedMs < 0) {
|
|
19894
|
+
throw runtimeError("run_events_illegal", state.runId, "budget_run_duration elapsedMs is invalid");
|
|
19895
|
+
}
|
|
19896
|
+
if (source !== "workflow" && source !== "project" && source !== "both") {
|
|
19897
|
+
throw runtimeError("run_events_illegal", state.runId, "budget_run_duration source is invalid");
|
|
19898
|
+
}
|
|
19899
|
+
const expected = effectiveRunDurationBudget(plan?.limits, options.projectLimits);
|
|
19900
|
+
if (!expected || expected.budgetMs !== budgetMs || expected.source !== source) {
|
|
19901
|
+
throw runtimeError("run_events_illegal", state.runId, "budget_run_duration budgetMs does not match the plan");
|
|
19902
|
+
}
|
|
19903
|
+
if (elapsedMs < budgetMs) {
|
|
19904
|
+
throw runtimeError("run_events_illegal", state.runId, "budget_run_duration elapsedMs is below budgetMs");
|
|
19905
|
+
}
|
|
19906
|
+
}
|
|
19862
19907
|
state.cancelRequested = true;
|
|
19863
19908
|
if (event.commandId) state.cancelCommandId = event.commandId;
|
|
19864
19909
|
}
|
|
@@ -20504,7 +20549,8 @@ function freezeState(state) {
|
|
|
20504
20549
|
cancelRequested: state.cancelRequested,
|
|
20505
20550
|
...state.terminalReason !== void 0 ? { terminalReason: state.terminalReason } : {},
|
|
20506
20551
|
...state.terminalReason !== void 0 && state.status === "failed" ? { failureReason: state.terminalReason } : {},
|
|
20507
|
-
...state.drive !== void 0 ? { drive: Object.freeze({ ...state.drive }) } : {}
|
|
20552
|
+
...state.drive !== void 0 ? { drive: Object.freeze({ ...state.drive }) } : {},
|
|
20553
|
+
...state.runningSince !== void 0 ? { runningSince: state.runningSince } : {}
|
|
20508
20554
|
};
|
|
20509
20555
|
return Object.freeze(frozen);
|
|
20510
20556
|
}
|
|
@@ -22063,6 +22109,9 @@ function clearVnextDriveSession(storePath, runId, token) {
|
|
|
22063
22109
|
if (!current || current.token !== token) return;
|
|
22064
22110
|
delete current.driveSession;
|
|
22065
22111
|
}
|
|
22112
|
+
function vnextDriveSession(storePath, runId) {
|
|
22113
|
+
return owners.get(storePath)?.admitted.get(runId)?.driveSession;
|
|
22114
|
+
}
|
|
22066
22115
|
function vnextOpenDriveSessions(storePath) {
|
|
22067
22116
|
const owner = owners.get(storePath);
|
|
22068
22117
|
if (!owner) return [];
|
|
@@ -22402,7 +22451,8 @@ function foldStoredVnextRun(context, run) {
|
|
|
22402
22451
|
const step = envelope.plan.steps[stepId];
|
|
22403
22452
|
if (!step || step.kind !== "gate") return void 0;
|
|
22404
22453
|
return envelope.gates.definitions[step.gate]?.kind;
|
|
22405
|
-
}
|
|
22454
|
+
},
|
|
22455
|
+
projectLimits: envelope.projectLimits
|
|
22406
22456
|
} : {});
|
|
22407
22457
|
if (envelope) verifyVnextGateEvidence(context.eventStore, run, envelope, events, state);
|
|
22408
22458
|
assertStoredProjection(context, run);
|
|
@@ -22512,10 +22562,17 @@ function cancelVnextRun(context, runId, options = {}) {
|
|
|
22512
22562
|
};
|
|
22513
22563
|
const cancelReason = options.reason ?? "operator_cancel";
|
|
22514
22564
|
const activeAttempt = Boolean(folded.currentStep?.attemptId) || vnextFoldPanelAttemptIds(folded.currentStep).length > 0;
|
|
22515
|
-
|
|
22565
|
+
const cancelPayload = {
|
|
22516
22566
|
actor: { kind: "runtime", id: context.homeRuntimeId },
|
|
22517
22567
|
reason: cancelReason
|
|
22518
|
-
}
|
|
22568
|
+
};
|
|
22569
|
+
if (cancelReason === "budget_run_duration") {
|
|
22570
|
+
if (!options.budget) {
|
|
22571
|
+
throw runtimeError("run_events_illegal", runId, "budget_run_duration requires a budget payload");
|
|
22572
|
+
}
|
|
22573
|
+
cancelPayload.budget = options.budget;
|
|
22574
|
+
}
|
|
22575
|
+
push("run.cancel_requested", cancelPayload);
|
|
22519
22576
|
let status = "cancelled";
|
|
22520
22577
|
if (folded.status === "running" && activeAttempt) {
|
|
22521
22578
|
status = "cancelling";
|
|
@@ -23892,7 +23949,7 @@ function recordGateNoStartInTransaction(context, run, envelope, stepId, observat
|
|
|
23892
23949
|
const cancelled = observation.stopCause === "cancel";
|
|
23893
23950
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
23894
23951
|
const seq = sequencer(context, run, now);
|
|
23895
|
-
|
|
23952
|
+
const cancelReason = cancelled ? ensureOperatorCancel(seq, ids.state, run, context) : void 0;
|
|
23896
23953
|
const observationId = newVnextObservationId();
|
|
23897
23954
|
const observed = seq.push("effect.observed", {
|
|
23898
23955
|
effect: { id: ids.effectId, policy: { class: ids.gateKind === "artifacts-exist" ? "read-only" : "unknown", sharedMutable: false } },
|
|
@@ -23924,7 +23981,7 @@ function recordGateNoStartInTransaction(context, run, envelope, stepId, observat
|
|
|
23924
23981
|
seq.push("attempt.status_changed", { attemptId: ids.attemptId, status: "terminal" });
|
|
23925
23982
|
seq.push("assignment.terminal", { assignmentId: ids.assignmentId, outcome: "cancelled", status: "terminal" });
|
|
23926
23983
|
seq.push("step.status_changed", { stepId, status: "cancelled", previousStatus: "running" });
|
|
23927
|
-
seq.push("run.status_changed", { status: "cancelled", reason: "operator_cancel", stepId });
|
|
23984
|
+
seq.push("run.status_changed", { status: "cancelled", reason: cancelReason ?? "operator_cancel", stepId });
|
|
23928
23985
|
} else {
|
|
23929
23986
|
seq.push("assignment.result_recorded", { assignmentId: ids.assignmentId, resultClass: "producer_rejected", status: "result_recorded" });
|
|
23930
23987
|
seq.push("attempt.status_changed", { attemptId: ids.attemptId, status: "terminal" });
|
|
@@ -23978,7 +24035,7 @@ function recordGateCancelObservedInTransaction(context, run, envelope, stepId, o
|
|
|
23978
24035
|
}
|
|
23979
24036
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
23980
24037
|
const seq = sequencer(context, run, now);
|
|
23981
|
-
ensureOperatorCancel(seq, ids.state, run);
|
|
24038
|
+
const cancelReason = ensureOperatorCancel(seq, ids.state, run, context);
|
|
23982
24039
|
if (ids.gateKind === "artifacts-exist") {
|
|
23983
24040
|
seq.push("assignment.executing", { assignmentId: ids.assignmentId, stepId, stepAttempt: ids.stepAttempt, attemptId: ids.attemptId, status: "executing" });
|
|
23984
24041
|
seq.push("attempt.status_changed", { attemptId: ids.attemptId, assignmentId: ids.assignmentId, stepId, stepAttempt: ids.stepAttempt, status: "executing" });
|
|
@@ -24021,7 +24078,7 @@ function recordGateCancelObservedInTransaction(context, run, envelope, stepId, o
|
|
|
24021
24078
|
seq.push("attempt.status_changed", { attemptId: ids.attemptId, status: "terminal" });
|
|
24022
24079
|
seq.push("assignment.terminal", { assignmentId: ids.assignmentId, outcome: "cancelled", status: "terminal" });
|
|
24023
24080
|
seq.push("step.status_changed", { stepId, status: "cancelled", previousStatus: "running" });
|
|
24024
|
-
seq.push("run.status_changed", { status: "cancelled", reason:
|
|
24081
|
+
seq.push("run.status_changed", { status: "cancelled", reason: cancelReason, stepId });
|
|
24025
24082
|
for (const event of seq.events) context.eventStore.appendEvent(event);
|
|
24026
24083
|
context.eventStore.insertGateObservation(observationRow);
|
|
24027
24084
|
context.eventStore.settleCapability(ids.attemptId, "settled");
|
|
@@ -24106,8 +24163,8 @@ function requireIntent(context, run, envelope, stepId) {
|
|
|
24106
24163
|
state: folded
|
|
24107
24164
|
};
|
|
24108
24165
|
}
|
|
24109
|
-
function ensureOperatorCancel(seq, state, run) {
|
|
24110
|
-
if (state.status === "cancelling") return;
|
|
24166
|
+
function ensureOperatorCancel(seq, state, run, context) {
|
|
24167
|
+
if (state.status === "cancelling") return cancelReasonFromLog(context, run.runId);
|
|
24111
24168
|
if (state.status !== "running") {
|
|
24112
24169
|
throw runtimeError("run_events_illegal", run.runId, "cancelled gate proof requires a running or cancelling run");
|
|
24113
24170
|
}
|
|
@@ -24116,6 +24173,17 @@ function ensureOperatorCancel(seq, state, run) {
|
|
|
24116
24173
|
reason: "operator_cancel"
|
|
24117
24174
|
});
|
|
24118
24175
|
seq.push("run.status_changed", { status: "cancelling", reason: "operator_cancel" });
|
|
24176
|
+
return "operator_cancel";
|
|
24177
|
+
}
|
|
24178
|
+
function cancelReasonFromLog(context, runId) {
|
|
24179
|
+
const events = context.eventStore.events(runId, 0, 1e6);
|
|
24180
|
+
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
24181
|
+
const event = events[index];
|
|
24182
|
+
if (event.eventType === "run.cancel_requested" && typeof event.payload.reason === "string") {
|
|
24183
|
+
return event.payload.reason;
|
|
24184
|
+
}
|
|
24185
|
+
}
|
|
24186
|
+
return "operator_cancel";
|
|
24119
24187
|
}
|
|
24120
24188
|
function sequencer(context, run, now) {
|
|
24121
24189
|
let sequence = context.eventStore.nextSequence(run.runId);
|
|
@@ -24454,6 +24522,8 @@ async function driveAdmitted(context, runId, producer, token, options = {}) {
|
|
|
24454
24522
|
}
|
|
24455
24523
|
let latest = { state: foldStoredVnextRun(context, requireRun(context, runId)) };
|
|
24456
24524
|
while (latest.state.status === "running") {
|
|
24525
|
+
const overBudget = cancelRunDurationIfExceeded(context, runId);
|
|
24526
|
+
if (overBudget) return overBudget;
|
|
24457
24527
|
const before = context.eventStore.runState(runId)?.lastSequence;
|
|
24458
24528
|
latest = await stepLocked(context, runId, producer, token);
|
|
24459
24529
|
if (latest.handoff) return latest;
|
|
@@ -24513,6 +24583,7 @@ function recordDriveReceipt(context, session, closeInfo) {
|
|
|
24513
24583
|
const last = events[events.length - 1];
|
|
24514
24584
|
const openedSequence = opened?.openedSequence ?? openedEvent?.sequence;
|
|
24515
24585
|
if (openedSequence === void 0) return;
|
|
24586
|
+
const closedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
24516
24587
|
const receipt = {
|
|
24517
24588
|
schema: VNEXT_DRIVE_RECEIPT_SCHEMA,
|
|
24518
24589
|
driveId: session.driveId,
|
|
@@ -24522,7 +24593,7 @@ function recordDriveReceipt(context, session, closeInfo) {
|
|
|
24522
24593
|
mode: session.mode,
|
|
24523
24594
|
openedAt: openedEvent?.occurredAt ?? session.openedAt,
|
|
24524
24595
|
openedSequence,
|
|
24525
|
-
closedAt
|
|
24596
|
+
closedAt,
|
|
24526
24597
|
lastSequence: last.sequence,
|
|
24527
24598
|
logHash: hashVnextDriveLog(events.filter((event) => event.sequence >= 1 && event.sequence <= last.sequence)),
|
|
24528
24599
|
settlement: {
|
|
@@ -24532,7 +24603,7 @@ function recordDriveReceipt(context, session, closeInfo) {
|
|
|
24532
24603
|
...closeInfo.kind === "handoff" && closeInfo.handoff ? { handoff: receiptHandoff(closeInfo.handoff) } : {},
|
|
24533
24604
|
...closeInfo.error !== void 0 ? { error: closeInfo.error } : {}
|
|
24534
24605
|
},
|
|
24535
|
-
budget:
|
|
24606
|
+
budget: driveReceiptBudget(context, run.runId, state, events, closedAt),
|
|
24536
24607
|
producer: { id: closeInfo.producerId, closed: closeInfo.producerClosed }
|
|
24537
24608
|
};
|
|
24538
24609
|
context.eventStore.insertDriveReceipt(receipt);
|
|
@@ -24721,6 +24792,8 @@ var VnextRunScheduler = class _VnextRunScheduler {
|
|
|
24721
24792
|
}
|
|
24722
24793
|
const driveId = newDriveId(runId, token, this.context.homeRuntimeId, vnextMonotonicNs());
|
|
24723
24794
|
const openedMeta = appendDriveOpened(this.context, runId, driveId, options.mode);
|
|
24795
|
+
const startedState = foldStoredVnextRun(this.context, requireRun(this.context, runId));
|
|
24796
|
+
const deadlineAt = driveDeadlineAt(this.context, runId, startedState);
|
|
24724
24797
|
const session = {
|
|
24725
24798
|
driveId,
|
|
24726
24799
|
runId,
|
|
@@ -24730,7 +24803,8 @@ var VnextRunScheduler = class _VnextRunScheduler {
|
|
|
24730
24803
|
openedAt: openedMeta.occurredAt,
|
|
24731
24804
|
producerId: producer.id,
|
|
24732
24805
|
controller: new AbortController(),
|
|
24733
|
-
settled
|
|
24806
|
+
settled,
|
|
24807
|
+
...deadlineAt !== void 0 ? { deadlineAt } : {}
|
|
24734
24808
|
};
|
|
24735
24809
|
attachVnextDriveSession(this.context.eventStore.path, runId, token, session);
|
|
24736
24810
|
opened = true;
|
|
@@ -24809,11 +24883,13 @@ async function stepLocked(context, runId, producer, token) {
|
|
|
24809
24883
|
throw runtimeError("run_events_illegal", prepared.run.runId, "prepared gate is not artifacts-exist");
|
|
24810
24884
|
}
|
|
24811
24885
|
registerVnextAttemptController(context.eventStore.path, runId, { attemptId: prepared.attemptId, controller: prepared.controller });
|
|
24886
|
+
const clearBudget = armRunDurationBudgetTimer(context, runId);
|
|
24812
24887
|
try {
|
|
24813
24888
|
const observation = evaluateArtifactsGate(context, definition);
|
|
24814
24889
|
vnextGateDispatchSeams.afterEvaluate?.(prepared);
|
|
24815
24890
|
return context.eventStore.transaction(() => settlePreparedGate(context, prepared, observation));
|
|
24816
24891
|
} finally {
|
|
24892
|
+
clearBudget();
|
|
24817
24893
|
unregisterVnextAttemptController(context.eventStore.path, runId, prepared.attemptId);
|
|
24818
24894
|
}
|
|
24819
24895
|
}
|
|
@@ -24844,6 +24920,7 @@ async function runPreparedCommandGate(context, prepared, definition, token) {
|
|
|
24844
24920
|
});
|
|
24845
24921
|
armVnextGateHold(storePath, prepared.run.runId, token, prepared.attemptId, () => observer.requestStop("cancel"));
|
|
24846
24922
|
registerVnextAttemptController(storePath, prepared.run.runId, { attemptId: prepared.attemptId, controller: prepared.controller });
|
|
24923
|
+
const clearBudget = armRunDurationBudgetTimer(context, prepared.run.runId);
|
|
24847
24924
|
const unhookClose = registerVnextRuntimeCloseHook(context, () => {
|
|
24848
24925
|
try {
|
|
24849
24926
|
markVnextGateHoldUnsettled(storePath, prepared.run.runId, token, prepared.attemptId);
|
|
@@ -24884,6 +24961,7 @@ async function runPreparedCommandGate(context, prepared, definition, token) {
|
|
|
24884
24961
|
return commitCommandUncertainty(context, prepared, token, outcome.reason, outcome.observation);
|
|
24885
24962
|
} finally {
|
|
24886
24963
|
unhookClose();
|
|
24964
|
+
clearBudget();
|
|
24887
24965
|
unregisterVnextAttemptController(storePath, prepared.run.runId, prepared.attemptId);
|
|
24888
24966
|
}
|
|
24889
24967
|
}
|
|
@@ -25509,8 +25587,10 @@ async function drivePanel(context, panel, producer) {
|
|
|
25509
25587
|
const pending = /* @__PURE__ */ new Map();
|
|
25510
25588
|
let stopBirths = false;
|
|
25511
25589
|
let settlementFailed = false;
|
|
25590
|
+
const budgetClears = /* @__PURE__ */ new Map();
|
|
25512
25591
|
const register = (member) => {
|
|
25513
25592
|
registerVnextAttemptController(context.eventStore.path, runId, { attemptId: member.attemptId, controller: member.controller });
|
|
25593
|
+
budgetClears.set(member.attemptId, armRunDurationBudgetTimer(context, runId));
|
|
25514
25594
|
owned.set(member.attemptId, member);
|
|
25515
25595
|
};
|
|
25516
25596
|
const abortOwned = () => {
|
|
@@ -25693,6 +25773,7 @@ async function drivePanel(context, panel, producer) {
|
|
|
25693
25773
|
throw error;
|
|
25694
25774
|
} finally {
|
|
25695
25775
|
for (const attemptId of owned.keys()) {
|
|
25776
|
+
budgetClears.get(attemptId)?.();
|
|
25696
25777
|
unregisterVnextAttemptController(context.eventStore.path, runId, attemptId);
|
|
25697
25778
|
}
|
|
25698
25779
|
}
|
|
@@ -25872,7 +25953,7 @@ function joinPanel(context, panel) {
|
|
|
25872
25953
|
};
|
|
25873
25954
|
const cancelRun = () => {
|
|
25874
25955
|
push("step.status_changed", { stepId: panel.stepId, status: "cancelled", previousStatus: "running" });
|
|
25875
|
-
push("run.status_changed", { status: "cancelled", reason:
|
|
25956
|
+
push("run.status_changed", { status: "cancelled", reason: cancelReasonFromLog2(context, run.runId) });
|
|
25876
25957
|
for (const event of events) context.eventStore.appendEvent(event);
|
|
25877
25958
|
const next2 = foldStoredVnextRun(context, run);
|
|
25878
25959
|
persistVnextRunState(context, run.runId, next2, events[events.length - 1].sequence);
|
|
@@ -25979,16 +26060,124 @@ function transitionBudgetFailure(plan, state, fromStepId, outcome) {
|
|
|
25979
26060
|
}
|
|
25980
26061
|
return void 0;
|
|
25981
26062
|
}
|
|
25982
|
-
|
|
25983
|
-
|
|
25984
|
-
|
|
26063
|
+
var MAX_TIMER_DELAY_MS = 2147483647;
|
|
26064
|
+
function elapsedMsBetween(fromIso, toIso) {
|
|
26065
|
+
const from = Date.parse(fromIso);
|
|
26066
|
+
const to = Date.parse(toIso);
|
|
26067
|
+
if (!Number.isFinite(from) || !Number.isFinite(to)) return 0;
|
|
26068
|
+
return Math.max(0, Math.trunc(to - from));
|
|
26069
|
+
}
|
|
26070
|
+
function declaredRunDurationBudget(context, runId) {
|
|
26071
|
+
const run = context.eventStore.run(runId);
|
|
26072
|
+
if (!run) return void 0;
|
|
26073
|
+
try {
|
|
26074
|
+
const envelope = loadVnextRunPlanEnvelope(context.eventStore, run);
|
|
26075
|
+
return effectiveRunDurationBudget(envelope.plan.limits, envelope.projectLimits);
|
|
26076
|
+
} catch {
|
|
26077
|
+
return void 0;
|
|
26078
|
+
}
|
|
26079
|
+
}
|
|
26080
|
+
function runDurationOverrunPayload(context, runId, nowIso2 = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
26081
|
+
const declared = declaredRunDurationBudget(context, runId);
|
|
26082
|
+
if (!declared) return void 0;
|
|
26083
|
+
const run = context.eventStore.run(runId);
|
|
26084
|
+
if (!run) return void 0;
|
|
26085
|
+
const state = foldStoredVnextRun(context, run);
|
|
26086
|
+
if (!state.runningSince) return void 0;
|
|
26087
|
+
if (isTerminalRunStatus(state.status) || state.status === "cancelling") return void 0;
|
|
26088
|
+
const elapsedMs = elapsedMsBetween(state.runningSince, nowIso2);
|
|
26089
|
+
if (elapsedMs < declared.budgetMs) return void 0;
|
|
26090
|
+
return { budgetMs: declared.budgetMs, elapsedMs, source: declared.source };
|
|
26091
|
+
}
|
|
26092
|
+
function cancelRunDurationIfExceeded(context, runId) {
|
|
26093
|
+
if (isVnextRuntimeContextClosed(context)) return void 0;
|
|
26094
|
+
const payload = runDurationOverrunPayload(context, runId);
|
|
26095
|
+
if (!payload) return void 0;
|
|
26096
|
+
cancelVnextRun(context, runId, { reason: "budget_run_duration", budget: payload });
|
|
26097
|
+
return { state: foldStoredVnextRun(context, requireRun(context, runId)) };
|
|
26098
|
+
}
|
|
26099
|
+
function driveDeadlineAt(context, runId, state) {
|
|
26100
|
+
const declared = declaredRunDurationBudget(context, runId);
|
|
26101
|
+
if (!declared || !state.runningSince) return void 0;
|
|
26102
|
+
const start = Date.parse(state.runningSince);
|
|
26103
|
+
if (!Number.isFinite(start)) return void 0;
|
|
26104
|
+
return new Date(start + declared.budgetMs).toISOString();
|
|
26105
|
+
}
|
|
26106
|
+
function driveReceiptBudget(context, runId, state, events, closedAt) {
|
|
26107
|
+
const declared = declaredRunDurationBudget(context, runId);
|
|
26108
|
+
if (!declared) return null;
|
|
26109
|
+
const elapsedMs = state.runningSince ? elapsedMsBetween(state.runningSince, closedAt) : 0;
|
|
26110
|
+
const overrun = events.some((event) => event.eventType === "run.cancel_requested" && event.payload.reason === "budget_run_duration");
|
|
26111
|
+
return {
|
|
26112
|
+
budgetMs: declared.budgetMs,
|
|
26113
|
+
source: declared.source,
|
|
26114
|
+
elapsedMs,
|
|
26115
|
+
overrun
|
|
26116
|
+
};
|
|
26117
|
+
}
|
|
26118
|
+
function cancelReasonFromLog2(context, runId) {
|
|
26119
|
+
const events = context.eventStore.events(runId, 0, 1e6);
|
|
26120
|
+
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
26121
|
+
const event = events[index];
|
|
26122
|
+
if (event.eventType === "run.cancel_requested" && typeof event.payload.reason === "string") {
|
|
26123
|
+
return event.payload.reason;
|
|
26124
|
+
}
|
|
25985
26125
|
}
|
|
26126
|
+
return "operator_cancel";
|
|
26127
|
+
}
|
|
26128
|
+
function armRunDurationBudgetTimer(context, runId) {
|
|
26129
|
+
const declared = declaredRunDurationBudget(context, runId);
|
|
26130
|
+
if (!declared) return () => void 0;
|
|
26131
|
+
const run = context.eventStore.run(runId);
|
|
26132
|
+
if (!run) return () => void 0;
|
|
26133
|
+
const state = foldStoredVnextRun(context, run);
|
|
26134
|
+
if (!state.runningSince) return () => void 0;
|
|
26135
|
+
const startMs = Date.parse(state.runningSince);
|
|
26136
|
+
if (!Number.isFinite(startMs)) return () => void 0;
|
|
26137
|
+
const deadlineMs = startMs + declared.budgetMs;
|
|
26138
|
+
let timer;
|
|
26139
|
+
let cleared = false;
|
|
26140
|
+
const fire = () => {
|
|
26141
|
+
timer = void 0;
|
|
26142
|
+
if (cleared || isVnextRuntimeContextClosed(context)) return;
|
|
26143
|
+
const nowIso2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
26144
|
+
const elapsedMs = elapsedMsBetween(state.runningSince, nowIso2);
|
|
26145
|
+
if (elapsedMs < declared.budgetMs) {
|
|
26146
|
+
arm(declared.budgetMs - elapsedMs);
|
|
26147
|
+
return;
|
|
26148
|
+
}
|
|
26149
|
+
try {
|
|
26150
|
+
cancelVnextRun(context, runId, {
|
|
26151
|
+
reason: "budget_run_duration",
|
|
26152
|
+
budget: { budgetMs: declared.budgetMs, elapsedMs, source: declared.source }
|
|
26153
|
+
});
|
|
26154
|
+
} catch {
|
|
26155
|
+
}
|
|
26156
|
+
};
|
|
26157
|
+
const arm = (remaining) => {
|
|
26158
|
+
if (cleared) return;
|
|
26159
|
+
const delay = Math.min(Math.max(0, remaining), MAX_TIMER_DELAY_MS);
|
|
26160
|
+
timer = setTimeout(fire, delay);
|
|
26161
|
+
timer.unref();
|
|
26162
|
+
};
|
|
26163
|
+
const unhook = registerVnextRuntimeCloseHook(context, () => {
|
|
26164
|
+
cleared = true;
|
|
26165
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
26166
|
+
timer = void 0;
|
|
26167
|
+
});
|
|
26168
|
+
arm(deadlineMs - Date.now());
|
|
26169
|
+
return () => {
|
|
26170
|
+
if (cleared) return;
|
|
26171
|
+
cleared = true;
|
|
26172
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
26173
|
+
timer = void 0;
|
|
26174
|
+
unhook();
|
|
26175
|
+
};
|
|
26176
|
+
}
|
|
26177
|
+
function unsupportedLimit(envelope) {
|
|
25986
26178
|
if (envelope.plan.limits.maxAgentTimeMs !== void 0) {
|
|
25987
26179
|
return { reason: "limit_unsupported", field: "limits.maxAgentTimeMs", detail: "agent-time budget enforcement is not available in this slice" };
|
|
25988
26180
|
}
|
|
25989
|
-
if (envelope.projectLimits.maxRunDurationMs !== void 0) {
|
|
25990
|
-
return { reason: "limit_unsupported", field: "project.limits.maxRunDurationMs", detail: "duration budget enforcement is not available in this slice" };
|
|
25991
|
-
}
|
|
25992
26181
|
if (envelope.projectLimits.maxAgentTimeMs !== void 0) {
|
|
25993
26182
|
return { reason: "limit_unsupported", field: "project.limits.maxAgentTimeMs", detail: "agent-time budget enforcement is not available in this slice" };
|
|
25994
26183
|
}
|
|
@@ -27165,7 +27354,7 @@ async function startVnextRuntimeSupervisorInner(paths, requestedPortOption, now)
|
|
|
27165
27354
|
const scheduler = VnextRunScheduler.for(context, bundle);
|
|
27166
27355
|
const opening = scheduler.openDriveSession(runId, {
|
|
27167
27356
|
mode: body.mode,
|
|
27168
|
-
allowLimits:
|
|
27357
|
+
allowLimits: false,
|
|
27169
27358
|
liveMode: body.mode === "live",
|
|
27170
27359
|
createProducer
|
|
27171
27360
|
});
|
|
@@ -27208,6 +27397,20 @@ async function startVnextRuntimeSupervisorInner(paths, requestedPortOption, now)
|
|
|
27208
27397
|
sendJson(response, 200, { ok: true, events });
|
|
27209
27398
|
return;
|
|
27210
27399
|
}
|
|
27400
|
+
if (request.method === "GET" && sub === "drive") {
|
|
27401
|
+
const run = context.eventStore.run(runId);
|
|
27402
|
+
if (!run) throw runtimeError("run_unknown", runId, "run not found");
|
|
27403
|
+
const activeSession = vnextDriveSession(context.eventStore.path, runId);
|
|
27404
|
+
const session = activeSession ? {
|
|
27405
|
+
driveId: activeSession.driveId,
|
|
27406
|
+
mode: activeSession.mode,
|
|
27407
|
+
openedAt: activeSession.openedAt,
|
|
27408
|
+
deadlineAt: activeSession.deadlineAt ?? null
|
|
27409
|
+
} : null;
|
|
27410
|
+
const receipts = context.eventStore.driveReceiptsForRun(runId, 20);
|
|
27411
|
+
sendJson(response, 200, { ok: true, session, receipts });
|
|
27412
|
+
return;
|
|
27413
|
+
}
|
|
27211
27414
|
if (request.method === "POST" && sub === "cancel") {
|
|
27212
27415
|
const body = await readJsonBody(request);
|
|
27213
27416
|
const result = cancelVnextRun(context, runId, {
|
|
@@ -27241,6 +27444,43 @@ async function startVnextRuntimeSupervisorInner(paths, requestedPortOption, now)
|
|
|
27241
27444
|
return;
|
|
27242
27445
|
}
|
|
27243
27446
|
}
|
|
27447
|
+
const driveMatch = /^\/v1\/drives\/([A-Za-z0-9_-]+)$/.exec(url.pathname);
|
|
27448
|
+
if (request.method === "GET" && driveMatch) {
|
|
27449
|
+
const driveId = driveMatch[1];
|
|
27450
|
+
const projectRoot = url.searchParams.get("projectRoot") ?? "";
|
|
27451
|
+
if (!projectRoot) {
|
|
27452
|
+
sendJson(response, 400, { ok: false, error: "runtime_request_invalid", message: "projectRoot query parameter is required" });
|
|
27453
|
+
return;
|
|
27454
|
+
}
|
|
27455
|
+
const context = contextFor(projectRoot);
|
|
27456
|
+
let receipt;
|
|
27457
|
+
try {
|
|
27458
|
+
receipt = context.eventStore.driveReceipt(driveId);
|
|
27459
|
+
} catch {
|
|
27460
|
+
sendJson(response, 200, { ok: true, receipt: null, verified: false, divergence: "receipt unreadable" });
|
|
27461
|
+
return;
|
|
27462
|
+
}
|
|
27463
|
+
if (!receipt) {
|
|
27464
|
+
sendJson(response, 404, { ok: false, error: "drive_receipt_missing", message: "receipt not found" });
|
|
27465
|
+
return;
|
|
27466
|
+
}
|
|
27467
|
+
const runId = receipt.runId;
|
|
27468
|
+
const stored = context.eventStore.run(runId);
|
|
27469
|
+
if (!stored) {
|
|
27470
|
+
sendJson(response, 200, { ok: true, receipt, verified: false, divergence: "run missing" });
|
|
27471
|
+
return;
|
|
27472
|
+
}
|
|
27473
|
+
const state = foldStoredVnextRun(context, stored);
|
|
27474
|
+
const events = context.eventStore.events(runId, 0, 1e6);
|
|
27475
|
+
const checked = verifyVnextDriveReceipt(receipt, events, state.status, { runId, driveId });
|
|
27476
|
+
sendJson(response, 200, {
|
|
27477
|
+
ok: true,
|
|
27478
|
+
receipt,
|
|
27479
|
+
verified: checked.verified,
|
|
27480
|
+
...checked.divergence !== void 0 ? { divergence: checked.divergence } : {}
|
|
27481
|
+
});
|
|
27482
|
+
return;
|
|
27483
|
+
}
|
|
27244
27484
|
const projectRunsMatch = /^\/v1\/projects\/(prj_[A-Za-z0-9_-]+)\/runs$/.exec(url.pathname);
|
|
27245
27485
|
if (request.method === "GET" && projectRunsMatch) {
|
|
27246
27486
|
const projectRoot = url.searchParams.get("projectRoot") ?? "";
|