@kontextmind/kxm 0.7.24 → 0.7.25
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 +13 -1
- package/plugins/kxm/dist/mcp-server.js +1 -1
- package/plugins/kxm/dist/runtime.js +203 -23
- package/plugins/kxm/dist/vnext-runtime-supervisor.js +203 -23
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/src/cli/vnext.ts +20 -1
- 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 +8 -1
- package/plugins/kxm/src/vnext-runtime-supervisor.ts +1 -1
- 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
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
3
|
"name": "kxm",
|
|
4
4
|
"displayName": "KXM",
|
|
5
|
-
"version": "0.7.
|
|
5
|
+
"version": "0.7.25",
|
|
6
6
|
"description": "Headless multi-agent orchestration, durable workflows, and a live operator dashboard for Pi and Claude Code",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "KontextMind",
|
package/plugins/kxm/dist/cli.js
CHANGED
|
@@ -33642,6 +33642,9 @@ ${RUN_ENGINE_NOTICE}`);
|
|
|
33642
33642
|
return 1;
|
|
33643
33643
|
}
|
|
33644
33644
|
}
|
|
33645
|
+
function formatCancelledStatus(reason) {
|
|
33646
|
+
return reason.length > 0 ? `cancelled (${reason})` : "cancelled";
|
|
33647
|
+
}
|
|
33645
33648
|
function formatDriveStatusLine(runStatus, drive) {
|
|
33646
33649
|
if (!drive || typeof drive.driveId !== "string" || drive.driveId.length === 0) return void 0;
|
|
33647
33650
|
const receipt = drive.receipt ?? null;
|
|
@@ -33659,11 +33662,20 @@ function formatDriveStatusLine(runStatus, drive) {
|
|
|
33659
33662
|
if (kind === "handoff") {
|
|
33660
33663
|
return drive.verified === true ? `drive ${drive.driveId}: handoff (receipt verified)` : `drive ${drive.driveId}: handoff`;
|
|
33661
33664
|
}
|
|
33665
|
+
if (receipt.settlement?.status === "cancelled") {
|
|
33666
|
+
const label = formatCancelledStatus(reason);
|
|
33667
|
+
return drive.verified === true ? `drive ${drive.driveId}: ${label} (receipt verified)` : `drive ${drive.driveId}: ${label}`;
|
|
33668
|
+
}
|
|
33662
33669
|
if (drive.verified === true) {
|
|
33663
33670
|
return `drive ${drive.driveId}: completed (receipt verified)`;
|
|
33664
33671
|
}
|
|
33665
33672
|
return `drive ${drive.driveId}: completed`;
|
|
33666
33673
|
}
|
|
33674
|
+
function formatRunStatusLine(run, drive) {
|
|
33675
|
+
const reason = typeof drive?.receipt?.settlement?.reason === "string" ? drive.receipt.settlement.reason : "";
|
|
33676
|
+
const statusLabel = run.status === "cancelled" ? formatCancelledStatus(reason) : run.status;
|
|
33677
|
+
return `run ${run.runId}: ${statusLabel} (workflow ${run.workflowId}, updated ${run.updatedAt})`;
|
|
33678
|
+
}
|
|
33667
33679
|
async function cmdVnextRunStatus(runtime, runId) {
|
|
33668
33680
|
try {
|
|
33669
33681
|
const projectRoot = discoverVnextProjectRoot(runtime.cwd);
|
|
@@ -33680,7 +33692,7 @@ async function cmdVnextRunStatus(runtime, runId) {
|
|
|
33680
33692
|
runtime.io,
|
|
33681
33693
|
runtime.json,
|
|
33682
33694
|
{ ok: true, command: "runs status", run, ...drive !== void 0 ? { drive } : {} },
|
|
33683
|
-
|
|
33695
|
+
`${formatRunStatusLine(run, drive)}${driveLine ? `
|
|
33684
33696
|
${driveLine}` : ""}`
|
|
33685
33697
|
);
|
|
33686
33698
|
return 0;
|
|
@@ -17121,7 +17121,7 @@ async function deliverInboxNotification(messageId, delivered, notify) {
|
|
|
17121
17121
|
}
|
|
17122
17122
|
|
|
17123
17123
|
// plugins/kxm/src/mcp-server.ts
|
|
17124
|
-
var VERSION = "0.7.
|
|
17124
|
+
var VERSION = "0.7.25";
|
|
17125
17125
|
var inbox = /* @__PURE__ */ new Map();
|
|
17126
17126
|
var notifiedInbox = /* @__PURE__ */ new Set();
|
|
17127
17127
|
var meshClient;
|
|
@@ -19397,6 +19397,16 @@ var ATTEMPT_EDGES = {
|
|
|
19397
19397
|
executing: /* @__PURE__ */ new Set(["settling"]),
|
|
19398
19398
|
settling: /* @__PURE__ */ new Set(["terminal"])
|
|
19399
19399
|
};
|
|
19400
|
+
function effectiveRunDurationBudget(planLimits, projectLimits) {
|
|
19401
|
+
const workflow = planLimits?.maxRunDurationMs;
|
|
19402
|
+
const project = projectLimits?.maxRunDurationMs;
|
|
19403
|
+
if (workflow === void 0 && project === void 0) return void 0;
|
|
19404
|
+
if (workflow !== void 0 && project !== void 0) {
|
|
19405
|
+
return { budgetMs: Math.min(workflow, project), source: "both" };
|
|
19406
|
+
}
|
|
19407
|
+
if (workflow !== void 0) return { budgetMs: workflow, source: "workflow" };
|
|
19408
|
+
return { budgetMs: project, source: "project" };
|
|
19409
|
+
}
|
|
19400
19410
|
function emptyPanel() {
|
|
19401
19411
|
return { order: [], assignments: /* @__PURE__ */ Object.create(null) };
|
|
19402
19412
|
}
|
|
@@ -19628,7 +19638,7 @@ function foldVnextRunState(run, plan, events, options = {}) {
|
|
|
19628
19638
|
foldRunStatus(state, plan, event);
|
|
19629
19639
|
break;
|
|
19630
19640
|
case "run.cancel_requested":
|
|
19631
|
-
foldCancelRequested(state, event);
|
|
19641
|
+
foldCancelRequested(state, event, plan, options);
|
|
19632
19642
|
break;
|
|
19633
19643
|
case "run.drive_opened":
|
|
19634
19644
|
foldDriveOpened(state, event);
|
|
@@ -19757,6 +19767,9 @@ function foldRunStatus(state, plan, event) {
|
|
|
19757
19767
|
if ((status === "running" || status === "cancelling") && !plan) {
|
|
19758
19768
|
throw runtimeError("run_plan_missing", state.runId, `status ${status} requires a pinned run plan`);
|
|
19759
19769
|
}
|
|
19770
|
+
if (status === "running" && state.runningSince === void 0) {
|
|
19771
|
+
state.runningSince = event.occurredAt;
|
|
19772
|
+
}
|
|
19760
19773
|
if (TERMINAL_RUN.has(status)) {
|
|
19761
19774
|
assertTerminalRunStatus(state, plan, status, event);
|
|
19762
19775
|
if (typeof event.payload.reason === "string") state.terminalReason = event.payload.reason;
|
|
@@ -19855,10 +19868,36 @@ function foldDriveOpened(state, event) {
|
|
|
19855
19868
|
}
|
|
19856
19869
|
state.drive = { driveId, mode, openedSequence: event.sequence };
|
|
19857
19870
|
}
|
|
19858
|
-
function foldCancelRequested(state, event) {
|
|
19871
|
+
function foldCancelRequested(state, event, plan, options) {
|
|
19859
19872
|
if (state.cancelRequested || TERMINAL_RUN.has(state.status)) {
|
|
19860
19873
|
throw runtimeError("run_events_illegal", state.runId, "duplicate run.cancel_requested is not legal");
|
|
19861
19874
|
}
|
|
19875
|
+
if (event.payload.reason === "budget_run_duration") {
|
|
19876
|
+
const budget = event.payload.budget;
|
|
19877
|
+
if (!budget || typeof budget !== "object" || Array.isArray(budget)) {
|
|
19878
|
+
throw runtimeError("run_events_illegal", state.runId, "budget_run_duration requires a budget payload");
|
|
19879
|
+
}
|
|
19880
|
+
const body = budget;
|
|
19881
|
+
const budgetMs = body.budgetMs;
|
|
19882
|
+
const elapsedMs = body.elapsedMs;
|
|
19883
|
+
const source = body.source;
|
|
19884
|
+
if (typeof budgetMs !== "number" || !Number.isInteger(budgetMs) || budgetMs < 0) {
|
|
19885
|
+
throw runtimeError("run_events_illegal", state.runId, "budget_run_duration budgetMs is invalid");
|
|
19886
|
+
}
|
|
19887
|
+
if (typeof elapsedMs !== "number" || !Number.isInteger(elapsedMs) || elapsedMs < 0) {
|
|
19888
|
+
throw runtimeError("run_events_illegal", state.runId, "budget_run_duration elapsedMs is invalid");
|
|
19889
|
+
}
|
|
19890
|
+
if (source !== "workflow" && source !== "project" && source !== "both") {
|
|
19891
|
+
throw runtimeError("run_events_illegal", state.runId, "budget_run_duration source is invalid");
|
|
19892
|
+
}
|
|
19893
|
+
const expected = effectiveRunDurationBudget(plan?.limits, options.projectLimits);
|
|
19894
|
+
if (!expected || expected.budgetMs !== budgetMs || expected.source !== source) {
|
|
19895
|
+
throw runtimeError("run_events_illegal", state.runId, "budget_run_duration budgetMs does not match the plan");
|
|
19896
|
+
}
|
|
19897
|
+
if (elapsedMs < budgetMs) {
|
|
19898
|
+
throw runtimeError("run_events_illegal", state.runId, "budget_run_duration elapsedMs is below budgetMs");
|
|
19899
|
+
}
|
|
19900
|
+
}
|
|
19862
19901
|
state.cancelRequested = true;
|
|
19863
19902
|
if (event.commandId) state.cancelCommandId = event.commandId;
|
|
19864
19903
|
}
|
|
@@ -20504,7 +20543,8 @@ function freezeState(state) {
|
|
|
20504
20543
|
cancelRequested: state.cancelRequested,
|
|
20505
20544
|
...state.terminalReason !== void 0 ? { terminalReason: state.terminalReason } : {},
|
|
20506
20545
|
...state.terminalReason !== void 0 && state.status === "failed" ? { failureReason: state.terminalReason } : {},
|
|
20507
|
-
...state.drive !== void 0 ? { drive: Object.freeze({ ...state.drive }) } : {}
|
|
20546
|
+
...state.drive !== void 0 ? { drive: Object.freeze({ ...state.drive }) } : {},
|
|
20547
|
+
...state.runningSince !== void 0 ? { runningSince: state.runningSince } : {}
|
|
20508
20548
|
};
|
|
20509
20549
|
return Object.freeze(frozen);
|
|
20510
20550
|
}
|
|
@@ -22402,7 +22442,8 @@ function foldStoredVnextRun(context, run) {
|
|
|
22402
22442
|
const step = envelope.plan.steps[stepId];
|
|
22403
22443
|
if (!step || step.kind !== "gate") return void 0;
|
|
22404
22444
|
return envelope.gates.definitions[step.gate]?.kind;
|
|
22405
|
-
}
|
|
22445
|
+
},
|
|
22446
|
+
projectLimits: envelope.projectLimits
|
|
22406
22447
|
} : {});
|
|
22407
22448
|
if (envelope) verifyVnextGateEvidence(context.eventStore, run, envelope, events, state);
|
|
22408
22449
|
assertStoredProjection(context, run);
|
|
@@ -22512,10 +22553,17 @@ function cancelVnextRun(context, runId, options = {}) {
|
|
|
22512
22553
|
};
|
|
22513
22554
|
const cancelReason = options.reason ?? "operator_cancel";
|
|
22514
22555
|
const activeAttempt = Boolean(folded.currentStep?.attemptId) || vnextFoldPanelAttemptIds(folded.currentStep).length > 0;
|
|
22515
|
-
|
|
22556
|
+
const cancelPayload = {
|
|
22516
22557
|
actor: { kind: "runtime", id: context.homeRuntimeId },
|
|
22517
22558
|
reason: cancelReason
|
|
22518
|
-
}
|
|
22559
|
+
};
|
|
22560
|
+
if (cancelReason === "budget_run_duration") {
|
|
22561
|
+
if (!options.budget) {
|
|
22562
|
+
throw runtimeError("run_events_illegal", runId, "budget_run_duration requires a budget payload");
|
|
22563
|
+
}
|
|
22564
|
+
cancelPayload.budget = options.budget;
|
|
22565
|
+
}
|
|
22566
|
+
push("run.cancel_requested", cancelPayload);
|
|
22519
22567
|
let status = "cancelled";
|
|
22520
22568
|
if (folded.status === "running" && activeAttempt) {
|
|
22521
22569
|
status = "cancelling";
|
|
@@ -23892,7 +23940,7 @@ function recordGateNoStartInTransaction(context, run, envelope, stepId, observat
|
|
|
23892
23940
|
const cancelled = observation.stopCause === "cancel";
|
|
23893
23941
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
23894
23942
|
const seq = sequencer(context, run, now);
|
|
23895
|
-
|
|
23943
|
+
const cancelReason = cancelled ? ensureOperatorCancel(seq, ids.state, run, context) : void 0;
|
|
23896
23944
|
const observationId = newVnextObservationId();
|
|
23897
23945
|
const observed = seq.push("effect.observed", {
|
|
23898
23946
|
effect: { id: ids.effectId, policy: { class: ids.gateKind === "artifacts-exist" ? "read-only" : "unknown", sharedMutable: false } },
|
|
@@ -23924,7 +23972,7 @@ function recordGateNoStartInTransaction(context, run, envelope, stepId, observat
|
|
|
23924
23972
|
seq.push("attempt.status_changed", { attemptId: ids.attemptId, status: "terminal" });
|
|
23925
23973
|
seq.push("assignment.terminal", { assignmentId: ids.assignmentId, outcome: "cancelled", status: "terminal" });
|
|
23926
23974
|
seq.push("step.status_changed", { stepId, status: "cancelled", previousStatus: "running" });
|
|
23927
|
-
seq.push("run.status_changed", { status: "cancelled", reason: "operator_cancel", stepId });
|
|
23975
|
+
seq.push("run.status_changed", { status: "cancelled", reason: cancelReason ?? "operator_cancel", stepId });
|
|
23928
23976
|
} else {
|
|
23929
23977
|
seq.push("assignment.result_recorded", { assignmentId: ids.assignmentId, resultClass: "producer_rejected", status: "result_recorded" });
|
|
23930
23978
|
seq.push("attempt.status_changed", { attemptId: ids.attemptId, status: "terminal" });
|
|
@@ -23978,7 +24026,7 @@ function recordGateCancelObservedInTransaction(context, run, envelope, stepId, o
|
|
|
23978
24026
|
}
|
|
23979
24027
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
23980
24028
|
const seq = sequencer(context, run, now);
|
|
23981
|
-
ensureOperatorCancel(seq, ids.state, run);
|
|
24029
|
+
const cancelReason = ensureOperatorCancel(seq, ids.state, run, context);
|
|
23982
24030
|
if (ids.gateKind === "artifacts-exist") {
|
|
23983
24031
|
seq.push("assignment.executing", { assignmentId: ids.assignmentId, stepId, stepAttempt: ids.stepAttempt, attemptId: ids.attemptId, status: "executing" });
|
|
23984
24032
|
seq.push("attempt.status_changed", { attemptId: ids.attemptId, assignmentId: ids.assignmentId, stepId, stepAttempt: ids.stepAttempt, status: "executing" });
|
|
@@ -24021,7 +24069,7 @@ function recordGateCancelObservedInTransaction(context, run, envelope, stepId, o
|
|
|
24021
24069
|
seq.push("attempt.status_changed", { attemptId: ids.attemptId, status: "terminal" });
|
|
24022
24070
|
seq.push("assignment.terminal", { assignmentId: ids.assignmentId, outcome: "cancelled", status: "terminal" });
|
|
24023
24071
|
seq.push("step.status_changed", { stepId, status: "cancelled", previousStatus: "running" });
|
|
24024
|
-
seq.push("run.status_changed", { status: "cancelled", reason:
|
|
24072
|
+
seq.push("run.status_changed", { status: "cancelled", reason: cancelReason, stepId });
|
|
24025
24073
|
for (const event of seq.events) context.eventStore.appendEvent(event);
|
|
24026
24074
|
context.eventStore.insertGateObservation(observationRow);
|
|
24027
24075
|
context.eventStore.settleCapability(ids.attemptId, "settled");
|
|
@@ -24106,8 +24154,8 @@ function requireIntent(context, run, envelope, stepId) {
|
|
|
24106
24154
|
state: folded
|
|
24107
24155
|
};
|
|
24108
24156
|
}
|
|
24109
|
-
function ensureOperatorCancel(seq, state, run) {
|
|
24110
|
-
if (state.status === "cancelling") return;
|
|
24157
|
+
function ensureOperatorCancel(seq, state, run, context) {
|
|
24158
|
+
if (state.status === "cancelling") return cancelReasonFromLog(context, run.runId);
|
|
24111
24159
|
if (state.status !== "running") {
|
|
24112
24160
|
throw runtimeError("run_events_illegal", run.runId, "cancelled gate proof requires a running or cancelling run");
|
|
24113
24161
|
}
|
|
@@ -24116,6 +24164,17 @@ function ensureOperatorCancel(seq, state, run) {
|
|
|
24116
24164
|
reason: "operator_cancel"
|
|
24117
24165
|
});
|
|
24118
24166
|
seq.push("run.status_changed", { status: "cancelling", reason: "operator_cancel" });
|
|
24167
|
+
return "operator_cancel";
|
|
24168
|
+
}
|
|
24169
|
+
function cancelReasonFromLog(context, runId) {
|
|
24170
|
+
const events = context.eventStore.events(runId, 0, 1e6);
|
|
24171
|
+
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
24172
|
+
const event = events[index];
|
|
24173
|
+
if (event.eventType === "run.cancel_requested" && typeof event.payload.reason === "string") {
|
|
24174
|
+
return event.payload.reason;
|
|
24175
|
+
}
|
|
24176
|
+
}
|
|
24177
|
+
return "operator_cancel";
|
|
24119
24178
|
}
|
|
24120
24179
|
function sequencer(context, run, now) {
|
|
24121
24180
|
let sequence = context.eventStore.nextSequence(run.runId);
|
|
@@ -24454,6 +24513,8 @@ async function driveAdmitted(context, runId, producer, token, options = {}) {
|
|
|
24454
24513
|
}
|
|
24455
24514
|
let latest = { state: foldStoredVnextRun(context, requireRun(context, runId)) };
|
|
24456
24515
|
while (latest.state.status === "running") {
|
|
24516
|
+
const overBudget = cancelRunDurationIfExceeded(context, runId);
|
|
24517
|
+
if (overBudget) return overBudget;
|
|
24457
24518
|
const before = context.eventStore.runState(runId)?.lastSequence;
|
|
24458
24519
|
latest = await stepLocked(context, runId, producer, token);
|
|
24459
24520
|
if (latest.handoff) return latest;
|
|
@@ -24513,6 +24574,7 @@ function recordDriveReceipt(context, session, closeInfo) {
|
|
|
24513
24574
|
const last = events[events.length - 1];
|
|
24514
24575
|
const openedSequence = opened?.openedSequence ?? openedEvent?.sequence;
|
|
24515
24576
|
if (openedSequence === void 0) return;
|
|
24577
|
+
const closedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
24516
24578
|
const receipt = {
|
|
24517
24579
|
schema: VNEXT_DRIVE_RECEIPT_SCHEMA,
|
|
24518
24580
|
driveId: session.driveId,
|
|
@@ -24522,7 +24584,7 @@ function recordDriveReceipt(context, session, closeInfo) {
|
|
|
24522
24584
|
mode: session.mode,
|
|
24523
24585
|
openedAt: openedEvent?.occurredAt ?? session.openedAt,
|
|
24524
24586
|
openedSequence,
|
|
24525
|
-
closedAt
|
|
24587
|
+
closedAt,
|
|
24526
24588
|
lastSequence: last.sequence,
|
|
24527
24589
|
logHash: hashVnextDriveLog(events.filter((event) => event.sequence >= 1 && event.sequence <= last.sequence)),
|
|
24528
24590
|
settlement: {
|
|
@@ -24532,7 +24594,7 @@ function recordDriveReceipt(context, session, closeInfo) {
|
|
|
24532
24594
|
...closeInfo.kind === "handoff" && closeInfo.handoff ? { handoff: receiptHandoff(closeInfo.handoff) } : {},
|
|
24533
24595
|
...closeInfo.error !== void 0 ? { error: closeInfo.error } : {}
|
|
24534
24596
|
},
|
|
24535
|
-
budget:
|
|
24597
|
+
budget: driveReceiptBudget(context, run.runId, state, events, closedAt),
|
|
24536
24598
|
producer: { id: closeInfo.producerId, closed: closeInfo.producerClosed }
|
|
24537
24599
|
};
|
|
24538
24600
|
context.eventStore.insertDriveReceipt(receipt);
|
|
@@ -24721,6 +24783,8 @@ var VnextRunScheduler = class _VnextRunScheduler {
|
|
|
24721
24783
|
}
|
|
24722
24784
|
const driveId = newDriveId(runId, token, this.context.homeRuntimeId, vnextMonotonicNs());
|
|
24723
24785
|
const openedMeta = appendDriveOpened(this.context, runId, driveId, options.mode);
|
|
24786
|
+
const startedState = foldStoredVnextRun(this.context, requireRun(this.context, runId));
|
|
24787
|
+
const deadlineAt = driveDeadlineAt(this.context, runId, startedState);
|
|
24724
24788
|
const session = {
|
|
24725
24789
|
driveId,
|
|
24726
24790
|
runId,
|
|
@@ -24730,7 +24794,8 @@ var VnextRunScheduler = class _VnextRunScheduler {
|
|
|
24730
24794
|
openedAt: openedMeta.occurredAt,
|
|
24731
24795
|
producerId: producer.id,
|
|
24732
24796
|
controller: new AbortController(),
|
|
24733
|
-
settled
|
|
24797
|
+
settled,
|
|
24798
|
+
...deadlineAt !== void 0 ? { deadlineAt } : {}
|
|
24734
24799
|
};
|
|
24735
24800
|
attachVnextDriveSession(this.context.eventStore.path, runId, token, session);
|
|
24736
24801
|
opened = true;
|
|
@@ -24809,11 +24874,13 @@ async function stepLocked(context, runId, producer, token) {
|
|
|
24809
24874
|
throw runtimeError("run_events_illegal", prepared.run.runId, "prepared gate is not artifacts-exist");
|
|
24810
24875
|
}
|
|
24811
24876
|
registerVnextAttemptController(context.eventStore.path, runId, { attemptId: prepared.attemptId, controller: prepared.controller });
|
|
24877
|
+
const clearBudget = armRunDurationBudgetTimer(context, runId);
|
|
24812
24878
|
try {
|
|
24813
24879
|
const observation = evaluateArtifactsGate(context, definition);
|
|
24814
24880
|
vnextGateDispatchSeams.afterEvaluate?.(prepared);
|
|
24815
24881
|
return context.eventStore.transaction(() => settlePreparedGate(context, prepared, observation));
|
|
24816
24882
|
} finally {
|
|
24883
|
+
clearBudget();
|
|
24817
24884
|
unregisterVnextAttemptController(context.eventStore.path, runId, prepared.attemptId);
|
|
24818
24885
|
}
|
|
24819
24886
|
}
|
|
@@ -24844,6 +24911,7 @@ async function runPreparedCommandGate(context, prepared, definition, token) {
|
|
|
24844
24911
|
});
|
|
24845
24912
|
armVnextGateHold(storePath, prepared.run.runId, token, prepared.attemptId, () => observer.requestStop("cancel"));
|
|
24846
24913
|
registerVnextAttemptController(storePath, prepared.run.runId, { attemptId: prepared.attemptId, controller: prepared.controller });
|
|
24914
|
+
const clearBudget = armRunDurationBudgetTimer(context, prepared.run.runId);
|
|
24847
24915
|
const unhookClose = registerVnextRuntimeCloseHook(context, () => {
|
|
24848
24916
|
try {
|
|
24849
24917
|
markVnextGateHoldUnsettled(storePath, prepared.run.runId, token, prepared.attemptId);
|
|
@@ -24884,6 +24952,7 @@ async function runPreparedCommandGate(context, prepared, definition, token) {
|
|
|
24884
24952
|
return commitCommandUncertainty(context, prepared, token, outcome.reason, outcome.observation);
|
|
24885
24953
|
} finally {
|
|
24886
24954
|
unhookClose();
|
|
24955
|
+
clearBudget();
|
|
24887
24956
|
unregisterVnextAttemptController(storePath, prepared.run.runId, prepared.attemptId);
|
|
24888
24957
|
}
|
|
24889
24958
|
}
|
|
@@ -25509,8 +25578,10 @@ async function drivePanel(context, panel, producer) {
|
|
|
25509
25578
|
const pending = /* @__PURE__ */ new Map();
|
|
25510
25579
|
let stopBirths = false;
|
|
25511
25580
|
let settlementFailed = false;
|
|
25581
|
+
const budgetClears = /* @__PURE__ */ new Map();
|
|
25512
25582
|
const register = (member) => {
|
|
25513
25583
|
registerVnextAttemptController(context.eventStore.path, runId, { attemptId: member.attemptId, controller: member.controller });
|
|
25584
|
+
budgetClears.set(member.attemptId, armRunDurationBudgetTimer(context, runId));
|
|
25514
25585
|
owned.set(member.attemptId, member);
|
|
25515
25586
|
};
|
|
25516
25587
|
const abortOwned = () => {
|
|
@@ -25693,6 +25764,7 @@ async function drivePanel(context, panel, producer) {
|
|
|
25693
25764
|
throw error;
|
|
25694
25765
|
} finally {
|
|
25695
25766
|
for (const attemptId of owned.keys()) {
|
|
25767
|
+
budgetClears.get(attemptId)?.();
|
|
25696
25768
|
unregisterVnextAttemptController(context.eventStore.path, runId, attemptId);
|
|
25697
25769
|
}
|
|
25698
25770
|
}
|
|
@@ -25872,7 +25944,7 @@ function joinPanel(context, panel) {
|
|
|
25872
25944
|
};
|
|
25873
25945
|
const cancelRun = () => {
|
|
25874
25946
|
push("step.status_changed", { stepId: panel.stepId, status: "cancelled", previousStatus: "running" });
|
|
25875
|
-
push("run.status_changed", { status: "cancelled", reason:
|
|
25947
|
+
push("run.status_changed", { status: "cancelled", reason: cancelReasonFromLog2(context, run.runId) });
|
|
25876
25948
|
for (const event of events) context.eventStore.appendEvent(event);
|
|
25877
25949
|
const next2 = foldStoredVnextRun(context, run);
|
|
25878
25950
|
persistVnextRunState(context, run.runId, next2, events[events.length - 1].sequence);
|
|
@@ -25979,16 +26051,124 @@ function transitionBudgetFailure(plan, state, fromStepId, outcome) {
|
|
|
25979
26051
|
}
|
|
25980
26052
|
return void 0;
|
|
25981
26053
|
}
|
|
25982
|
-
|
|
25983
|
-
|
|
25984
|
-
|
|
26054
|
+
var MAX_TIMER_DELAY_MS = 2147483647;
|
|
26055
|
+
function elapsedMsBetween(fromIso, toIso) {
|
|
26056
|
+
const from = Date.parse(fromIso);
|
|
26057
|
+
const to = Date.parse(toIso);
|
|
26058
|
+
if (!Number.isFinite(from) || !Number.isFinite(to)) return 0;
|
|
26059
|
+
return Math.max(0, Math.trunc(to - from));
|
|
26060
|
+
}
|
|
26061
|
+
function declaredRunDurationBudget(context, runId) {
|
|
26062
|
+
const run = context.eventStore.run(runId);
|
|
26063
|
+
if (!run) return void 0;
|
|
26064
|
+
try {
|
|
26065
|
+
const envelope = loadVnextRunPlanEnvelope(context.eventStore, run);
|
|
26066
|
+
return effectiveRunDurationBudget(envelope.plan.limits, envelope.projectLimits);
|
|
26067
|
+
} catch {
|
|
26068
|
+
return void 0;
|
|
25985
26069
|
}
|
|
26070
|
+
}
|
|
26071
|
+
function runDurationOverrunPayload(context, runId, nowIso2 = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
26072
|
+
const declared = declaredRunDurationBudget(context, runId);
|
|
26073
|
+
if (!declared) return void 0;
|
|
26074
|
+
const run = context.eventStore.run(runId);
|
|
26075
|
+
if (!run) return void 0;
|
|
26076
|
+
const state = foldStoredVnextRun(context, run);
|
|
26077
|
+
if (!state.runningSince) return void 0;
|
|
26078
|
+
if (isTerminalRunStatus(state.status) || state.status === "cancelling") return void 0;
|
|
26079
|
+
const elapsedMs = elapsedMsBetween(state.runningSince, nowIso2);
|
|
26080
|
+
if (elapsedMs < declared.budgetMs) return void 0;
|
|
26081
|
+
return { budgetMs: declared.budgetMs, elapsedMs, source: declared.source };
|
|
26082
|
+
}
|
|
26083
|
+
function cancelRunDurationIfExceeded(context, runId) {
|
|
26084
|
+
if (isVnextRuntimeContextClosed(context)) return void 0;
|
|
26085
|
+
const payload = runDurationOverrunPayload(context, runId);
|
|
26086
|
+
if (!payload) return void 0;
|
|
26087
|
+
cancelVnextRun(context, runId, { reason: "budget_run_duration", budget: payload });
|
|
26088
|
+
return { state: foldStoredVnextRun(context, requireRun(context, runId)) };
|
|
26089
|
+
}
|
|
26090
|
+
function driveDeadlineAt(context, runId, state) {
|
|
26091
|
+
const declared = declaredRunDurationBudget(context, runId);
|
|
26092
|
+
if (!declared || !state.runningSince) return void 0;
|
|
26093
|
+
const start = Date.parse(state.runningSince);
|
|
26094
|
+
if (!Number.isFinite(start)) return void 0;
|
|
26095
|
+
return new Date(start + declared.budgetMs).toISOString();
|
|
26096
|
+
}
|
|
26097
|
+
function driveReceiptBudget(context, runId, state, events, closedAt) {
|
|
26098
|
+
const declared = declaredRunDurationBudget(context, runId);
|
|
26099
|
+
if (!declared) return null;
|
|
26100
|
+
const elapsedMs = state.runningSince ? elapsedMsBetween(state.runningSince, closedAt) : 0;
|
|
26101
|
+
const overrun = events.some((event) => event.eventType === "run.cancel_requested" && event.payload.reason === "budget_run_duration");
|
|
26102
|
+
return {
|
|
26103
|
+
budgetMs: declared.budgetMs,
|
|
26104
|
+
source: declared.source,
|
|
26105
|
+
elapsedMs,
|
|
26106
|
+
overrun
|
|
26107
|
+
};
|
|
26108
|
+
}
|
|
26109
|
+
function cancelReasonFromLog2(context, runId) {
|
|
26110
|
+
const events = context.eventStore.events(runId, 0, 1e6);
|
|
26111
|
+
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
26112
|
+
const event = events[index];
|
|
26113
|
+
if (event.eventType === "run.cancel_requested" && typeof event.payload.reason === "string") {
|
|
26114
|
+
return event.payload.reason;
|
|
26115
|
+
}
|
|
26116
|
+
}
|
|
26117
|
+
return "operator_cancel";
|
|
26118
|
+
}
|
|
26119
|
+
function armRunDurationBudgetTimer(context, runId) {
|
|
26120
|
+
const declared = declaredRunDurationBudget(context, runId);
|
|
26121
|
+
if (!declared) return () => void 0;
|
|
26122
|
+
const run = context.eventStore.run(runId);
|
|
26123
|
+
if (!run) return () => void 0;
|
|
26124
|
+
const state = foldStoredVnextRun(context, run);
|
|
26125
|
+
if (!state.runningSince) return () => void 0;
|
|
26126
|
+
const startMs = Date.parse(state.runningSince);
|
|
26127
|
+
if (!Number.isFinite(startMs)) return () => void 0;
|
|
26128
|
+
const deadlineMs = startMs + declared.budgetMs;
|
|
26129
|
+
let timer;
|
|
26130
|
+
let cleared = false;
|
|
26131
|
+
const fire = () => {
|
|
26132
|
+
timer = void 0;
|
|
26133
|
+
if (cleared || isVnextRuntimeContextClosed(context)) return;
|
|
26134
|
+
const nowIso2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
26135
|
+
const elapsedMs = elapsedMsBetween(state.runningSince, nowIso2);
|
|
26136
|
+
if (elapsedMs < declared.budgetMs) {
|
|
26137
|
+
arm(declared.budgetMs - elapsedMs);
|
|
26138
|
+
return;
|
|
26139
|
+
}
|
|
26140
|
+
try {
|
|
26141
|
+
cancelVnextRun(context, runId, {
|
|
26142
|
+
reason: "budget_run_duration",
|
|
26143
|
+
budget: { budgetMs: declared.budgetMs, elapsedMs, source: declared.source }
|
|
26144
|
+
});
|
|
26145
|
+
} catch {
|
|
26146
|
+
}
|
|
26147
|
+
};
|
|
26148
|
+
const arm = (remaining) => {
|
|
26149
|
+
if (cleared) return;
|
|
26150
|
+
const delay = Math.min(Math.max(0, remaining), MAX_TIMER_DELAY_MS);
|
|
26151
|
+
timer = setTimeout(fire, delay);
|
|
26152
|
+
timer.unref();
|
|
26153
|
+
};
|
|
26154
|
+
const unhook = registerVnextRuntimeCloseHook(context, () => {
|
|
26155
|
+
cleared = true;
|
|
26156
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
26157
|
+
timer = void 0;
|
|
26158
|
+
});
|
|
26159
|
+
arm(deadlineMs - Date.now());
|
|
26160
|
+
return () => {
|
|
26161
|
+
if (cleared) return;
|
|
26162
|
+
cleared = true;
|
|
26163
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
26164
|
+
timer = void 0;
|
|
26165
|
+
unhook();
|
|
26166
|
+
};
|
|
26167
|
+
}
|
|
26168
|
+
function unsupportedLimit(envelope) {
|
|
25986
26169
|
if (envelope.plan.limits.maxAgentTimeMs !== void 0) {
|
|
25987
26170
|
return { reason: "limit_unsupported", field: "limits.maxAgentTimeMs", detail: "agent-time budget enforcement is not available in this slice" };
|
|
25988
26171
|
}
|
|
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
26172
|
if (envelope.projectLimits.maxAgentTimeMs !== void 0) {
|
|
25993
26173
|
return { reason: "limit_unsupported", field: "project.limits.maxAgentTimeMs", detail: "agent-time budget enforcement is not available in this slice" };
|
|
25994
26174
|
}
|
|
@@ -27165,7 +27345,7 @@ async function startVnextRuntimeSupervisorInner(paths, requestedPortOption, now)
|
|
|
27165
27345
|
const scheduler = VnextRunScheduler.for(context, bundle);
|
|
27166
27346
|
const opening = scheduler.openDriveSession(runId, {
|
|
27167
27347
|
mode: body.mode,
|
|
27168
|
-
allowLimits:
|
|
27348
|
+
allowLimits: false,
|
|
27169
27349
|
liveMode: body.mode === "live",
|
|
27170
27350
|
createProducer
|
|
27171
27351
|
});
|