@kontextmind/kxm 0.7.23 → 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 +59 -5
- package/plugins/kxm/dist/mcp-server.js +1 -1
- package/plugins/kxm/dist/runtime.js +507 -27
- package/plugins/kxm/dist/vnext-runtime-supervisor.js +500 -26
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/src/cli/vnext.ts +65 -3
- package/plugins/kxm/src/cli.ts +1 -1
- package/plugins/kxm/src/database.ts +3 -1
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/vnext-config.ts +31 -0
- package/plugins/kxm/src/vnext-engine-fold.ts +87 -2
- package/plugins/kxm/src/vnext-engine-gate-records.ts +19 -6
- package/plugins/kxm/src/vnext-engine.ts +325 -9
- package/plugins/kxm/src/vnext-runtime-owner.ts +2 -0
- package/plugins/kxm/src/vnext-runtime-store.ts +158 -2
- package/plugins/kxm/src/vnext-runtime-supervisor.ts +24 -3
- package/plugins/kxm/src/vnext-runtime.ts +17 -3
- package/schemas/vnext/common.schema.json +1 -0
- package/schemas/vnext/drive-receipt.schema.json +95 -0
- package/schemas/vnext/run-event.schema.json +39 -0
|
@@ -16431,6 +16431,7 @@ var VnextSchemaRegistry = class {
|
|
|
16431
16431
|
migrationReceiptValidator;
|
|
16432
16432
|
permissionDiffValidator;
|
|
16433
16433
|
runEventValidator;
|
|
16434
|
+
driveReceiptValidator;
|
|
16434
16435
|
constructor(schemasDir = DEFAULT_SCHEMA_DIR) {
|
|
16435
16436
|
this.schemasDir = resolve(schemasDir);
|
|
16436
16437
|
this.ajv = new import__.Ajv2020({ allErrors: true, strict: true, strictRequired: false });
|
|
@@ -16447,6 +16448,7 @@ var VnextSchemaRegistry = class {
|
|
|
16447
16448
|
const migrationReceiptFile = "migration-receipt.schema.json";
|
|
16448
16449
|
const permissionDiffFile = "permission-diff.schema.json";
|
|
16449
16450
|
const runEventFile = "run-event.schema.json";
|
|
16451
|
+
const driveReceiptFile = "drive-receipt.schema.json";
|
|
16450
16452
|
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, localBindingsFile)));
|
|
16451
16453
|
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, templateProvenanceFile)));
|
|
16452
16454
|
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, initOperationFile)));
|
|
@@ -16455,6 +16457,7 @@ var VnextSchemaRegistry = class {
|
|
|
16455
16457
|
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, migrationReceiptFile)));
|
|
16456
16458
|
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, permissionDiffFile)));
|
|
16457
16459
|
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, runEventFile)));
|
|
16460
|
+
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, driveReceiptFile)));
|
|
16458
16461
|
for (const [kind, definition] of Object.entries(RESOURCE_SCHEMA)) {
|
|
16459
16462
|
const validator = this.ajv.getSchema(`https://schemas.kxm.dev/vnext/${definition.file}`);
|
|
16460
16463
|
if (!validator) throw new Error(`schema did not compile: ${definition.file}`);
|
|
@@ -16468,6 +16471,7 @@ var VnextSchemaRegistry = class {
|
|
|
16468
16471
|
const migrationReceiptValidator = this.ajv.getSchema(`https://schemas.kxm.dev/vnext/${migrationReceiptFile}`);
|
|
16469
16472
|
const permissionDiffValidator = this.ajv.getSchema(`https://schemas.kxm.dev/vnext/${permissionDiffFile}`);
|
|
16470
16473
|
const runEventValidator = this.ajv.getSchema(`https://schemas.kxm.dev/vnext/${runEventFile}`);
|
|
16474
|
+
const driveReceiptValidator = this.ajv.getSchema(`https://schemas.kxm.dev/vnext/${driveReceiptFile}`);
|
|
16471
16475
|
if (!localBindingsValidator) throw new Error(`schema did not compile: ${localBindingsFile}`);
|
|
16472
16476
|
if (!templateProvenanceValidator) throw new Error(`schema did not compile: ${templateProvenanceFile}`);
|
|
16473
16477
|
if (!initOperationValidator) throw new Error(`schema did not compile: ${initOperationFile}`);
|
|
@@ -16476,6 +16480,7 @@ var VnextSchemaRegistry = class {
|
|
|
16476
16480
|
if (!migrationReceiptValidator) throw new Error(`schema did not compile: ${migrationReceiptFile}`);
|
|
16477
16481
|
if (!permissionDiffValidator) throw new Error(`schema did not compile: ${permissionDiffFile}`);
|
|
16478
16482
|
if (!runEventValidator) throw new Error(`schema did not compile: ${runEventFile}`);
|
|
16483
|
+
if (!driveReceiptValidator) throw new Error(`schema did not compile: ${driveReceiptFile}`);
|
|
16479
16484
|
this.localBindingsValidator = localBindingsValidator;
|
|
16480
16485
|
this.templateProvenanceValidator = templateProvenanceValidator;
|
|
16481
16486
|
this.initOperationValidator = initOperationValidator;
|
|
@@ -16484,6 +16489,7 @@ var VnextSchemaRegistry = class {
|
|
|
16484
16489
|
this.migrationReceiptValidator = migrationReceiptValidator;
|
|
16485
16490
|
this.permissionDiffValidator = permissionDiffValidator;
|
|
16486
16491
|
this.runEventValidator = runEventValidator;
|
|
16492
|
+
this.driveReceiptValidator = driveReceiptValidator;
|
|
16487
16493
|
}
|
|
16488
16494
|
validate(kind, value, file) {
|
|
16489
16495
|
const definition = RESOURCE_SCHEMA[kind];
|
|
@@ -16516,6 +16522,9 @@ var VnextSchemaRegistry = class {
|
|
|
16516
16522
|
validatePermissionDiff(value, file) {
|
|
16517
16523
|
return this.validateAuxiliary(value, file, "kxm.permission-diff.v1", this.permissionDiffValidator);
|
|
16518
16524
|
}
|
|
16525
|
+
validateDriveReceipt(value, file) {
|
|
16526
|
+
return this.validateAuxiliary(value, file, "kxm.drive-receipt.v1", this.driveReceiptValidator);
|
|
16527
|
+
}
|
|
16519
16528
|
validateAuxiliary(value, file, identity, validator) {
|
|
16520
16529
|
if (value.schema !== identity) {
|
|
16521
16530
|
return [issue2("schema", "schema_identity_mismatch", file, `expected ${identity}, received ${String(value.schema)}`)];
|
|
@@ -16536,6 +16545,25 @@ function validateRunEvent(value, file) {
|
|
|
16536
16545
|
)]);
|
|
16537
16546
|
}
|
|
16538
16547
|
}
|
|
16548
|
+
function validateDriveReceipt(value, file) {
|
|
16549
|
+
const registry = cachedRunEventRegistry ??= new VnextSchemaRegistry();
|
|
16550
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || value.schema !== "kxm.drive-receipt.v1") {
|
|
16551
|
+
throw new VnextConfigError([issue2(
|
|
16552
|
+
"schema",
|
|
16553
|
+
"drive_receipt_invalid",
|
|
16554
|
+
file,
|
|
16555
|
+
"expected kxm.drive-receipt.v1"
|
|
16556
|
+
)]);
|
|
16557
|
+
}
|
|
16558
|
+
if (!registry.driveReceiptValidator(value)) {
|
|
16559
|
+
throw new VnextConfigError([issue2(
|
|
16560
|
+
"schema",
|
|
16561
|
+
"drive_receipt_invalid",
|
|
16562
|
+
file,
|
|
16563
|
+
registry.ajv.errorsText(registry.driveReceiptValidator.errors, { separator: "; " })
|
|
16564
|
+
)]);
|
|
16565
|
+
}
|
|
16566
|
+
}
|
|
16539
16567
|
function schemaIssue(file, error) {
|
|
16540
16568
|
const location = error.instancePath || "/";
|
|
16541
16569
|
const suffix = error.params && "additionalProperty" in error.params ? ` (${String(error.params.additionalProperty)})` : "";
|
|
@@ -17923,7 +17951,7 @@ function discoverProjectStores(projectRoot, options = {}) {
|
|
|
17923
17951
|
stores.push({
|
|
17924
17952
|
storeId: `events:${key}`,
|
|
17925
17953
|
sourcePath: join4(eventsDir, entry.name),
|
|
17926
|
-
maxSupportedVersion:
|
|
17954
|
+
maxSupportedVersion: 4
|
|
17927
17955
|
});
|
|
17928
17956
|
}
|
|
17929
17957
|
}
|
|
@@ -18013,6 +18041,8 @@ function restoreBackup(manifestPathOrDir, options = {}) {
|
|
|
18013
18041
|
let maxSupported = 3;
|
|
18014
18042
|
if (store.storeId === "registry" || store.storeId === "binding-store") {
|
|
18015
18043
|
maxSupported = 1;
|
|
18044
|
+
} else if (store.storeId.startsWith("events:")) {
|
|
18045
|
+
maxSupported = 4;
|
|
18016
18046
|
}
|
|
18017
18047
|
let targetPath = store.sourcePath;
|
|
18018
18048
|
if (options.projectRoot && manifest.projectRoot && targetPath.startsWith(manifest.projectRoot)) {
|
|
@@ -18255,7 +18285,36 @@ var VnextRuntimeRegistry = class {
|
|
|
18255
18285
|
};
|
|
18256
18286
|
var VNEXT_RUN_EVENT_SCHEMA = "kxm.run-event.v1";
|
|
18257
18287
|
var VNEXT_ABSENT_MEMORY_REVISION = "ctxrev_absent";
|
|
18258
|
-
|
|
18288
|
+
function hashVnextDriveLog(events) {
|
|
18289
|
+
const body = events.map((event) => `${event.eventId}:${event.sequence}`).join("\n");
|
|
18290
|
+
return `sha256:${createHash4("sha256").update(body, "utf8").digest("hex")}`;
|
|
18291
|
+
}
|
|
18292
|
+
function verifyVnextDriveReceipt(receipt, events, foldedStatus, binding) {
|
|
18293
|
+
const reasons = [];
|
|
18294
|
+
if (receipt.runId !== binding.runId) {
|
|
18295
|
+
reasons.push(`runId ${receipt.runId} != binding ${binding.runId}`);
|
|
18296
|
+
}
|
|
18297
|
+
if (receipt.driveId !== binding.driveId) {
|
|
18298
|
+
reasons.push(`driveId ${receipt.driveId} != binding ${binding.driveId}`);
|
|
18299
|
+
}
|
|
18300
|
+
const last = events[events.length - 1];
|
|
18301
|
+
const currentLast = last?.sequence ?? 0;
|
|
18302
|
+
if (receipt.lastSequence !== currentLast) {
|
|
18303
|
+
reasons.push(`lastSequence ${receipt.lastSequence} != log ${currentLast}`);
|
|
18304
|
+
}
|
|
18305
|
+
const prefix = events.filter((event) => event.sequence >= 1 && event.sequence <= receipt.lastSequence);
|
|
18306
|
+
if (hashVnextDriveLog(prefix) !== receipt.logHash) {
|
|
18307
|
+
reasons.push("logHash mismatch");
|
|
18308
|
+
}
|
|
18309
|
+
if (receipt.settlement.status !== foldedStatus) {
|
|
18310
|
+
reasons.push(`settlement.status ${receipt.settlement.status} != folded ${foldedStatus}`);
|
|
18311
|
+
}
|
|
18312
|
+
if (reasons.length === 0) return { verified: true };
|
|
18313
|
+
return { verified: false, divergence: reasons.join("; ") };
|
|
18314
|
+
}
|
|
18315
|
+
var VNEXT_EVENT_STORE_SCHEMA_VERSION = 4;
|
|
18316
|
+
var VNEXT_DRIVE_RECEIPT_SCHEMA = "kxm.drive-receipt.v1";
|
|
18317
|
+
var DRIVE_RECEIPT_MAX_BYTES = 8 * 1024;
|
|
18259
18318
|
var EVENT_STORE_TABLES = {
|
|
18260
18319
|
runs: ["run_id", "project_id", "home_runtime_id", "workflow_id", "prompt_sha256", "status", "config_revision", "memory_revision", "executor_policy_revision", "tool_policy_revision", "created_at", "updated_at"],
|
|
18261
18320
|
events: ["project_id", "run_id", "sequence", "event_id", "event_type", "command_id", "occurred_at", "recorded_at", "monotonic_ns", "config_revision", "memory_revision", "executor_policy_revision", "tool_policy_revision", "payload", "schema", "home_runtime_id"],
|
|
@@ -18334,7 +18393,8 @@ var EVENT_STORE_TABLES = {
|
|
|
18334
18393
|
"outcome",
|
|
18335
18394
|
"settled_event_id",
|
|
18336
18395
|
"content_hash"
|
|
18337
|
-
]
|
|
18396
|
+
],
|
|
18397
|
+
drive_receipts: ["drive_id", "run_id", "project_id", "opened_sequence", "last_sequence", "closed_at", "schema", "receipt"]
|
|
18338
18398
|
};
|
|
18339
18399
|
var EVENT_STORE_SCHEMA = `
|
|
18340
18400
|
CREATE TABLE runs (
|
|
@@ -18476,7 +18536,40 @@ CREATE TABLE gate_evidence (
|
|
|
18476
18536
|
) STRICT;
|
|
18477
18537
|
CREATE UNIQUE INDEX gate_evidence_key ON gate_evidence(run_id, step_id, step_attempt, evidence_key) WHERE evidence_key IS NOT NULL;
|
|
18478
18538
|
CREATE INDEX gate_evidence_run ON gate_evidence(run_id);
|
|
18539
|
+
CREATE TABLE drive_receipts (
|
|
18540
|
+
drive_id TEXT PRIMARY KEY,
|
|
18541
|
+
run_id TEXT NOT NULL,
|
|
18542
|
+
project_id TEXT NOT NULL,
|
|
18543
|
+
opened_sequence INTEGER NOT NULL,
|
|
18544
|
+
last_sequence INTEGER NOT NULL,
|
|
18545
|
+
closed_at TEXT NOT NULL,
|
|
18546
|
+
schema TEXT NOT NULL,
|
|
18547
|
+
receipt TEXT NOT NULL
|
|
18548
|
+
) STRICT;
|
|
18549
|
+
CREATE INDEX drive_receipts_run ON drive_receipts(run_id);
|
|
18479
18550
|
`;
|
|
18551
|
+
var DRIVE_RECEIPTS_DDL = `
|
|
18552
|
+
CREATE TABLE IF NOT EXISTS drive_receipts (
|
|
18553
|
+
drive_id TEXT PRIMARY KEY,
|
|
18554
|
+
run_id TEXT NOT NULL,
|
|
18555
|
+
project_id TEXT NOT NULL,
|
|
18556
|
+
opened_sequence INTEGER NOT NULL,
|
|
18557
|
+
last_sequence INTEGER NOT NULL,
|
|
18558
|
+
closed_at TEXT NOT NULL,
|
|
18559
|
+
schema TEXT NOT NULL,
|
|
18560
|
+
receipt TEXT NOT NULL
|
|
18561
|
+
) STRICT;
|
|
18562
|
+
CREATE INDEX IF NOT EXISTS drive_receipts_run ON drive_receipts(run_id);
|
|
18563
|
+
`;
|
|
18564
|
+
var EVENT_STORE_MIGRATIONS = [
|
|
18565
|
+
{
|
|
18566
|
+
fromVersion: 3,
|
|
18567
|
+
toVersion: 4,
|
|
18568
|
+
migrate(database) {
|
|
18569
|
+
database.exec(DRIVE_RECEIPTS_DDL);
|
|
18570
|
+
}
|
|
18571
|
+
}
|
|
18572
|
+
];
|
|
18480
18573
|
var VnextRunEventStore = class {
|
|
18481
18574
|
path;
|
|
18482
18575
|
database;
|
|
@@ -18485,7 +18578,8 @@ var VnextRunEventStore = class {
|
|
|
18485
18578
|
this.database = openDatabase(this.path, "run event store", {
|
|
18486
18579
|
schema: EVENT_STORE_SCHEMA,
|
|
18487
18580
|
version: VNEXT_EVENT_STORE_SCHEMA_VERSION,
|
|
18488
|
-
tables: EVENT_STORE_TABLES
|
|
18581
|
+
tables: EVENT_STORE_TABLES,
|
|
18582
|
+
migrations: EVENT_STORE_MIGRATIONS
|
|
18489
18583
|
});
|
|
18490
18584
|
}
|
|
18491
18585
|
close() {
|
|
@@ -18642,6 +18736,38 @@ var VnextRunEventStore = class {
|
|
|
18642
18736
|
ON CONFLICT(run_id) DO UPDATE SET last_sequence = excluded.last_sequence, state = excluded.state
|
|
18643
18737
|
`).run(row.runId, row.lastSequence, row.state);
|
|
18644
18738
|
}
|
|
18739
|
+
insertDriveReceipt(receipt) {
|
|
18740
|
+
validateDriveReceipt(receipt, "drive-receipt");
|
|
18741
|
+
const canonical = vnextCanonicalJson(receipt);
|
|
18742
|
+
if (Buffer.byteLength(canonical, "utf8") > DRIVE_RECEIPT_MAX_BYTES) {
|
|
18743
|
+
throw runtimeError("drive_receipt_invalid", receipt.driveId, `drive receipt exceeds ${DRIVE_RECEIPT_MAX_BYTES} bytes`);
|
|
18744
|
+
}
|
|
18745
|
+
const result = this.database.prepare(`
|
|
18746
|
+
INSERT INTO drive_receipts (drive_id, run_id, project_id, opened_sequence, last_sequence, closed_at, schema, receipt)
|
|
18747
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
18748
|
+
ON CONFLICT(drive_id) DO NOTHING
|
|
18749
|
+
`).run(
|
|
18750
|
+
receipt.driveId,
|
|
18751
|
+
receipt.runId,
|
|
18752
|
+
receipt.projectId,
|
|
18753
|
+
receipt.openedSequence,
|
|
18754
|
+
receipt.lastSequence,
|
|
18755
|
+
receipt.closedAt,
|
|
18756
|
+
receipt.schema,
|
|
18757
|
+
canonical
|
|
18758
|
+
);
|
|
18759
|
+
return { inserted: Number(result.changes) === 1 };
|
|
18760
|
+
}
|
|
18761
|
+
driveReceipt(driveId) {
|
|
18762
|
+
const row = this.database.prepare("SELECT receipt FROM drive_receipts WHERE drive_id = ?").get(driveId);
|
|
18763
|
+
return row ? parseDriveReceipt(row.receipt) : void 0;
|
|
18764
|
+
}
|
|
18765
|
+
driveReceiptsForRun(runId, limit = 20) {
|
|
18766
|
+
const rows = this.database.prepare(`
|
|
18767
|
+
SELECT receipt FROM drive_receipts WHERE run_id = ? ORDER BY closed_at DESC, drive_id DESC LIMIT ?
|
|
18768
|
+
`).all(runId, limit);
|
|
18769
|
+
return rows.map((row) => parseDriveReceipt(row.receipt));
|
|
18770
|
+
}
|
|
18645
18771
|
insertCapability(row) {
|
|
18646
18772
|
this.database.prepare(`
|
|
18647
18773
|
INSERT INTO attempt_capabilities (attempt_id, run_id, assignment_id, step_id, step_attempt, producer_id, capability_hash, state)
|
|
@@ -18876,6 +19002,11 @@ var VnextRunEventStore = class {
|
|
|
18876
19002
|
}
|
|
18877
19003
|
}
|
|
18878
19004
|
};
|
|
19005
|
+
function parseDriveReceipt(raw) {
|
|
19006
|
+
const parsed = JSON.parse(raw);
|
|
19007
|
+
validateDriveReceipt(parsed, "drive-receipt");
|
|
19008
|
+
return parsed;
|
|
19009
|
+
}
|
|
18879
19010
|
function runFromRow(row) {
|
|
18880
19011
|
if (typeof row.memory_revision !== "string" || row.memory_revision.length === 0) {
|
|
18881
19012
|
throw runtimeError("runtime_schema_shape_invalid", row.run_id, "memory_revision is required");
|
|
@@ -19266,6 +19397,16 @@ var ATTEMPT_EDGES = {
|
|
|
19266
19397
|
executing: /* @__PURE__ */ new Set(["settling"]),
|
|
19267
19398
|
settling: /* @__PURE__ */ new Set(["terminal"])
|
|
19268
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
|
+
}
|
|
19269
19410
|
function emptyPanel() {
|
|
19270
19411
|
return { order: [], assignments: /* @__PURE__ */ Object.create(null) };
|
|
19271
19412
|
}
|
|
@@ -19497,7 +19638,10 @@ function foldVnextRunState(run, plan, events, options = {}) {
|
|
|
19497
19638
|
foldRunStatus(state, plan, event);
|
|
19498
19639
|
break;
|
|
19499
19640
|
case "run.cancel_requested":
|
|
19500
|
-
foldCancelRequested(state, event);
|
|
19641
|
+
foldCancelRequested(state, event, plan, options);
|
|
19642
|
+
break;
|
|
19643
|
+
case "run.drive_opened":
|
|
19644
|
+
foldDriveOpened(state, event);
|
|
19501
19645
|
break;
|
|
19502
19646
|
case "step.entered":
|
|
19503
19647
|
foldStepEntered(state, plan, event);
|
|
@@ -19623,6 +19767,9 @@ function foldRunStatus(state, plan, event) {
|
|
|
19623
19767
|
if ((status === "running" || status === "cancelling") && !plan) {
|
|
19624
19768
|
throw runtimeError("run_plan_missing", state.runId, `status ${status} requires a pinned run plan`);
|
|
19625
19769
|
}
|
|
19770
|
+
if (status === "running" && state.runningSince === void 0) {
|
|
19771
|
+
state.runningSince = event.occurredAt;
|
|
19772
|
+
}
|
|
19626
19773
|
if (TERMINAL_RUN.has(status)) {
|
|
19627
19774
|
assertTerminalRunStatus(state, plan, status, event);
|
|
19628
19775
|
if (typeof event.payload.reason === "string") state.terminalReason = event.payload.reason;
|
|
@@ -19710,10 +19857,47 @@ function isProvenFailure(state, plan) {
|
|
|
19710
19857
|
const used = state.stepAttempts[stepId] ?? 0;
|
|
19711
19858
|
return Boolean(step && used >= step.maxAttempts);
|
|
19712
19859
|
}
|
|
19713
|
-
function
|
|
19860
|
+
function foldDriveOpened(state, event) {
|
|
19861
|
+
if (state.status !== "running" && state.status !== "blocked_uncertain") {
|
|
19862
|
+
throw runtimeError("run_events_illegal", state.runId, "run.drive_opened is only legal while running or blocked_uncertain");
|
|
19863
|
+
}
|
|
19864
|
+
const driveId = stringPayload(event, "driveId");
|
|
19865
|
+
const mode = stringPayload(event, "mode");
|
|
19866
|
+
if (mode !== "simulated" && mode !== "live") {
|
|
19867
|
+
throw runtimeError("run_events_illegal", state.runId, "run.drive_opened mode must be simulated or live");
|
|
19868
|
+
}
|
|
19869
|
+
state.drive = { driveId, mode, openedSequence: event.sequence };
|
|
19870
|
+
}
|
|
19871
|
+
function foldCancelRequested(state, event, plan, options) {
|
|
19714
19872
|
if (state.cancelRequested || TERMINAL_RUN.has(state.status)) {
|
|
19715
19873
|
throw runtimeError("run_events_illegal", state.runId, "duplicate run.cancel_requested is not legal");
|
|
19716
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
|
+
}
|
|
19717
19901
|
state.cancelRequested = true;
|
|
19718
19902
|
if (event.commandId) state.cancelCommandId = event.commandId;
|
|
19719
19903
|
}
|
|
@@ -20358,7 +20542,9 @@ function freezeState(state) {
|
|
|
20358
20542
|
transitionsUsed: state.transitionsUsed,
|
|
20359
20543
|
cancelRequested: state.cancelRequested,
|
|
20360
20544
|
...state.terminalReason !== void 0 ? { terminalReason: state.terminalReason } : {},
|
|
20361
|
-
...state.terminalReason !== void 0 && state.status === "failed" ? { failureReason: state.terminalReason } : {}
|
|
20545
|
+
...state.terminalReason !== void 0 && state.status === "failed" ? { failureReason: state.terminalReason } : {},
|
|
20546
|
+
...state.drive !== void 0 ? { drive: Object.freeze({ ...state.drive }) } : {},
|
|
20547
|
+
...state.runningSince !== void 0 ? { runningSince: state.runningSince } : {}
|
|
20362
20548
|
};
|
|
20363
20549
|
return Object.freeze(frozen);
|
|
20364
20550
|
}
|
|
@@ -22256,7 +22442,8 @@ function foldStoredVnextRun(context, run) {
|
|
|
22256
22442
|
const step = envelope.plan.steps[stepId];
|
|
22257
22443
|
if (!step || step.kind !== "gate") return void 0;
|
|
22258
22444
|
return envelope.gates.definitions[step.gate]?.kind;
|
|
22259
|
-
}
|
|
22445
|
+
},
|
|
22446
|
+
projectLimits: envelope.projectLimits
|
|
22260
22447
|
} : {});
|
|
22261
22448
|
if (envelope) verifyVnextGateEvidence(context.eventStore, run, envelope, events, state);
|
|
22262
22449
|
assertStoredProjection(context, run);
|
|
@@ -22366,10 +22553,17 @@ function cancelVnextRun(context, runId, options = {}) {
|
|
|
22366
22553
|
};
|
|
22367
22554
|
const cancelReason = options.reason ?? "operator_cancel";
|
|
22368
22555
|
const activeAttempt = Boolean(folded.currentStep?.attemptId) || vnextFoldPanelAttemptIds(folded.currentStep).length > 0;
|
|
22369
|
-
|
|
22556
|
+
const cancelPayload = {
|
|
22370
22557
|
actor: { kind: "runtime", id: context.homeRuntimeId },
|
|
22371
22558
|
reason: cancelReason
|
|
22372
|
-
}
|
|
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);
|
|
22373
22567
|
let status = "cancelled";
|
|
22374
22568
|
if (folded.status === "running" && activeAttempt) {
|
|
22375
22569
|
status = "cancelling";
|
|
@@ -23746,7 +23940,7 @@ function recordGateNoStartInTransaction(context, run, envelope, stepId, observat
|
|
|
23746
23940
|
const cancelled = observation.stopCause === "cancel";
|
|
23747
23941
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
23748
23942
|
const seq = sequencer(context, run, now);
|
|
23749
|
-
|
|
23943
|
+
const cancelReason = cancelled ? ensureOperatorCancel(seq, ids.state, run, context) : void 0;
|
|
23750
23944
|
const observationId = newVnextObservationId();
|
|
23751
23945
|
const observed = seq.push("effect.observed", {
|
|
23752
23946
|
effect: { id: ids.effectId, policy: { class: ids.gateKind === "artifacts-exist" ? "read-only" : "unknown", sharedMutable: false } },
|
|
@@ -23778,7 +23972,7 @@ function recordGateNoStartInTransaction(context, run, envelope, stepId, observat
|
|
|
23778
23972
|
seq.push("attempt.status_changed", { attemptId: ids.attemptId, status: "terminal" });
|
|
23779
23973
|
seq.push("assignment.terminal", { assignmentId: ids.assignmentId, outcome: "cancelled", status: "terminal" });
|
|
23780
23974
|
seq.push("step.status_changed", { stepId, status: "cancelled", previousStatus: "running" });
|
|
23781
|
-
seq.push("run.status_changed", { status: "cancelled", reason: "operator_cancel", stepId });
|
|
23975
|
+
seq.push("run.status_changed", { status: "cancelled", reason: cancelReason ?? "operator_cancel", stepId });
|
|
23782
23976
|
} else {
|
|
23783
23977
|
seq.push("assignment.result_recorded", { assignmentId: ids.assignmentId, resultClass: "producer_rejected", status: "result_recorded" });
|
|
23784
23978
|
seq.push("attempt.status_changed", { attemptId: ids.attemptId, status: "terminal" });
|
|
@@ -23832,7 +24026,7 @@ function recordGateCancelObservedInTransaction(context, run, envelope, stepId, o
|
|
|
23832
24026
|
}
|
|
23833
24027
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
23834
24028
|
const seq = sequencer(context, run, now);
|
|
23835
|
-
ensureOperatorCancel(seq, ids.state, run);
|
|
24029
|
+
const cancelReason = ensureOperatorCancel(seq, ids.state, run, context);
|
|
23836
24030
|
if (ids.gateKind === "artifacts-exist") {
|
|
23837
24031
|
seq.push("assignment.executing", { assignmentId: ids.assignmentId, stepId, stepAttempt: ids.stepAttempt, attemptId: ids.attemptId, status: "executing" });
|
|
23838
24032
|
seq.push("attempt.status_changed", { attemptId: ids.attemptId, assignmentId: ids.assignmentId, stepId, stepAttempt: ids.stepAttempt, status: "executing" });
|
|
@@ -23875,7 +24069,7 @@ function recordGateCancelObservedInTransaction(context, run, envelope, stepId, o
|
|
|
23875
24069
|
seq.push("attempt.status_changed", { attemptId: ids.attemptId, status: "terminal" });
|
|
23876
24070
|
seq.push("assignment.terminal", { assignmentId: ids.assignmentId, outcome: "cancelled", status: "terminal" });
|
|
23877
24071
|
seq.push("step.status_changed", { stepId, status: "cancelled", previousStatus: "running" });
|
|
23878
|
-
seq.push("run.status_changed", { status: "cancelled", reason:
|
|
24072
|
+
seq.push("run.status_changed", { status: "cancelled", reason: cancelReason, stepId });
|
|
23879
24073
|
for (const event of seq.events) context.eventStore.appendEvent(event);
|
|
23880
24074
|
context.eventStore.insertGateObservation(observationRow);
|
|
23881
24075
|
context.eventStore.settleCapability(ids.attemptId, "settled");
|
|
@@ -23960,8 +24154,8 @@ function requireIntent(context, run, envelope, stepId) {
|
|
|
23960
24154
|
state: folded
|
|
23961
24155
|
};
|
|
23962
24156
|
}
|
|
23963
|
-
function ensureOperatorCancel(seq, state, run) {
|
|
23964
|
-
if (state.status === "cancelling") return;
|
|
24157
|
+
function ensureOperatorCancel(seq, state, run, context) {
|
|
24158
|
+
if (state.status === "cancelling") return cancelReasonFromLog(context, run.runId);
|
|
23965
24159
|
if (state.status !== "running") {
|
|
23966
24160
|
throw runtimeError("run_events_illegal", run.runId, "cancelled gate proof requires a running or cancelling run");
|
|
23967
24161
|
}
|
|
@@ -23970,6 +24164,17 @@ function ensureOperatorCancel(seq, state, run) {
|
|
|
23970
24164
|
reason: "operator_cancel"
|
|
23971
24165
|
});
|
|
23972
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";
|
|
23973
24178
|
}
|
|
23974
24179
|
function sequencer(context, run, now) {
|
|
23975
24180
|
let sequence = context.eventStore.nextSequence(run.runId);
|
|
@@ -24308,6 +24513,8 @@ async function driveAdmitted(context, runId, producer, token, options = {}) {
|
|
|
24308
24513
|
}
|
|
24309
24514
|
let latest = { state: foldStoredVnextRun(context, requireRun(context, runId)) };
|
|
24310
24515
|
while (latest.state.status === "running") {
|
|
24516
|
+
const overBudget = cancelRunDurationIfExceeded(context, runId);
|
|
24517
|
+
if (overBudget) return overBudget;
|
|
24311
24518
|
const before = context.eventStore.runState(runId)?.lastSequence;
|
|
24312
24519
|
latest = await stepLocked(context, runId, producer, token);
|
|
24313
24520
|
if (latest.handoff) return latest;
|
|
@@ -24323,6 +24530,111 @@ async function driveAdmitted(context, runId, producer, token, options = {}) {
|
|
|
24323
24530
|
function newDriveId(runId, token, runtimeId, monotonicNs) {
|
|
24324
24531
|
return `drv_${createHash11("sha256").update(`${runId}\0${token}\0${runtimeId}\0${monotonicNs}`, "utf8").digest("hex").slice(0, 24)}`;
|
|
24325
24532
|
}
|
|
24533
|
+
function driveErrorSummary(error) {
|
|
24534
|
+
const raw = error instanceof VnextConfigError ? error.issues[0]?.code : void 0;
|
|
24535
|
+
const className = raw && /^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$/.test(raw) ? raw : "drive-failure";
|
|
24536
|
+
return { class: className, component: "vnext-engine", retryable: false };
|
|
24537
|
+
}
|
|
24538
|
+
function receiptHandoff(handoff) {
|
|
24539
|
+
return {
|
|
24540
|
+
reason: handoff.reason,
|
|
24541
|
+
detail: handoff.detail,
|
|
24542
|
+
...handoff.field !== void 0 ? { field: handoff.field } : {},
|
|
24543
|
+
...handoff.stepId !== void 0 ? { stepId: handoff.stepId } : {}
|
|
24544
|
+
};
|
|
24545
|
+
}
|
|
24546
|
+
function appendDriveOpened(context, runId, driveId, mode) {
|
|
24547
|
+
return context.eventStore.transaction(() => {
|
|
24548
|
+
const run = requireRun(context, runId);
|
|
24549
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
24550
|
+
const sequence = context.eventStore.nextSequence(runId);
|
|
24551
|
+
const event = {
|
|
24552
|
+
...vnextEventBase(context, run, now, vnextMonotonicNs()),
|
|
24553
|
+
eventId: newVnextEventId(),
|
|
24554
|
+
eventType: "run.drive_opened",
|
|
24555
|
+
sequence,
|
|
24556
|
+
payload: { driveId, mode }
|
|
24557
|
+
};
|
|
24558
|
+
context.eventStore.appendEvent(event);
|
|
24559
|
+
const next = foldStoredVnextRun(context, run);
|
|
24560
|
+
persistVnextRunState(context, runId, next, sequence);
|
|
24561
|
+
return { sequence, occurredAt: now };
|
|
24562
|
+
});
|
|
24563
|
+
}
|
|
24564
|
+
function recordDriveReceipt(context, session, closeInfo) {
|
|
24565
|
+
if (isVnextRuntimeContextClosed(context)) return;
|
|
24566
|
+
try {
|
|
24567
|
+
const run = context.eventStore.run(session.runId);
|
|
24568
|
+
if (!run) return;
|
|
24569
|
+
const state = foldStoredVnextRun(context, run);
|
|
24570
|
+
const events = context.eventStore.events(session.runId, 0, 1e6);
|
|
24571
|
+
if (events.length === 0) return;
|
|
24572
|
+
const opened = state.drive;
|
|
24573
|
+
const openedEvent = opened ? events.find((event) => event.sequence === opened.openedSequence && event.eventType === "run.drive_opened") : events.find((event) => event.eventType === "run.drive_opened" && event.payload.driveId === session.driveId);
|
|
24574
|
+
const last = events[events.length - 1];
|
|
24575
|
+
const openedSequence = opened?.openedSequence ?? openedEvent?.sequence;
|
|
24576
|
+
if (openedSequence === void 0) return;
|
|
24577
|
+
const closedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
24578
|
+
const receipt = {
|
|
24579
|
+
schema: VNEXT_DRIVE_RECEIPT_SCHEMA,
|
|
24580
|
+
driveId: session.driveId,
|
|
24581
|
+
runId: session.runId,
|
|
24582
|
+
projectId: run.projectId,
|
|
24583
|
+
homeRuntimeId: session.homeRuntimeId,
|
|
24584
|
+
mode: session.mode,
|
|
24585
|
+
openedAt: openedEvent?.occurredAt ?? session.openedAt,
|
|
24586
|
+
openedSequence,
|
|
24587
|
+
closedAt,
|
|
24588
|
+
lastSequence: last.sequence,
|
|
24589
|
+
logHash: hashVnextDriveLog(events.filter((event) => event.sequence >= 1 && event.sequence <= last.sequence)),
|
|
24590
|
+
settlement: {
|
|
24591
|
+
kind: closeInfo.kind,
|
|
24592
|
+
status: state.status,
|
|
24593
|
+
reason: closeInfo.reason ?? state.terminalReason ?? "",
|
|
24594
|
+
...closeInfo.kind === "handoff" && closeInfo.handoff ? { handoff: receiptHandoff(closeInfo.handoff) } : {},
|
|
24595
|
+
...closeInfo.error !== void 0 ? { error: closeInfo.error } : {}
|
|
24596
|
+
},
|
|
24597
|
+
budget: driveReceiptBudget(context, run.runId, state, events, closedAt),
|
|
24598
|
+
producer: { id: closeInfo.producerId, closed: closeInfo.producerClosed }
|
|
24599
|
+
};
|
|
24600
|
+
context.eventStore.insertDriveReceipt(receipt);
|
|
24601
|
+
} catch {
|
|
24602
|
+
}
|
|
24603
|
+
}
|
|
24604
|
+
function vnextDrivePollProjection(context, runId, state) {
|
|
24605
|
+
if (!state.drive) return void 0;
|
|
24606
|
+
const events = context.eventStore.events(runId, 0, 1e6);
|
|
24607
|
+
const openedEvent = events.find((event) => event.sequence === state.drive.openedSequence);
|
|
24608
|
+
let receipt = null;
|
|
24609
|
+
let unreadable = false;
|
|
24610
|
+
try {
|
|
24611
|
+
receipt = context.eventStore.driveReceipt(state.drive.driveId) ?? null;
|
|
24612
|
+
} catch {
|
|
24613
|
+
unreadable = true;
|
|
24614
|
+
}
|
|
24615
|
+
const projection = {
|
|
24616
|
+
driveId: state.drive.driveId,
|
|
24617
|
+
mode: state.drive.mode,
|
|
24618
|
+
openedAt: openedEvent?.occurredAt ?? "",
|
|
24619
|
+
receipt: unreadable ? null : receipt,
|
|
24620
|
+
verified: false
|
|
24621
|
+
};
|
|
24622
|
+
if (unreadable) {
|
|
24623
|
+
projection.divergence = "receipt unreadable";
|
|
24624
|
+
return projection;
|
|
24625
|
+
}
|
|
24626
|
+
if (!receipt) {
|
|
24627
|
+
projection.divergence = "no receipt";
|
|
24628
|
+
return projection;
|
|
24629
|
+
}
|
|
24630
|
+
const checked = verifyVnextDriveReceipt(receipt, events, state.status, {
|
|
24631
|
+
runId,
|
|
24632
|
+
driveId: state.drive.driveId
|
|
24633
|
+
});
|
|
24634
|
+
projection.verified = checked.verified;
|
|
24635
|
+
if (checked.divergence !== void 0) projection.divergence = checked.divergence;
|
|
24636
|
+
return projection;
|
|
24637
|
+
}
|
|
24326
24638
|
function inflightAttemptId(state) {
|
|
24327
24639
|
const leftover = unreconciledPanelAttemptId(state);
|
|
24328
24640
|
if (leftover) return leftover;
|
|
@@ -24470,25 +24782,50 @@ var VnextRunScheduler = class _VnextRunScheduler {
|
|
|
24470
24782
|
throw error;
|
|
24471
24783
|
}
|
|
24472
24784
|
const driveId = newDriveId(runId, token, this.context.homeRuntimeId, vnextMonotonicNs());
|
|
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);
|
|
24473
24788
|
const session = {
|
|
24474
24789
|
driveId,
|
|
24475
24790
|
runId,
|
|
24476
24791
|
token,
|
|
24477
24792
|
homeRuntimeId: this.context.homeRuntimeId,
|
|
24478
24793
|
mode: options.mode,
|
|
24479
|
-
openedAt:
|
|
24794
|
+
openedAt: openedMeta.occurredAt,
|
|
24795
|
+
producerId: producer.id,
|
|
24480
24796
|
controller: new AbortController(),
|
|
24481
|
-
settled
|
|
24797
|
+
settled,
|
|
24798
|
+
...deadlineAt !== void 0 ? { deadlineAt } : {}
|
|
24482
24799
|
};
|
|
24483
24800
|
attachVnextDriveSession(this.context.eventStore.path, runId, token, session);
|
|
24484
24801
|
opened = true;
|
|
24485
24802
|
resolveOpen({ driveId, settled });
|
|
24486
24803
|
try {
|
|
24487
24804
|
const result = await driveAdmitted(this.context, runId, producer, token, driveOptions);
|
|
24805
|
+
const kind = result.handoff ? "handoff" : isTerminalRunStatus(result.state.status) ? "terminal" : "unsettled";
|
|
24806
|
+
recordDriveReceipt(this.context, session, {
|
|
24807
|
+
kind,
|
|
24808
|
+
...result.state.terminalReason !== void 0 ? { reason: result.state.terminalReason } : result.handoff ? { reason: result.handoff.reason } : {},
|
|
24809
|
+
...result.handoff ? { handoff: result.handoff } : {},
|
|
24810
|
+
producerId: producer.id,
|
|
24811
|
+
producerClosed: false
|
|
24812
|
+
});
|
|
24488
24813
|
pending.resolve(result);
|
|
24489
24814
|
return result;
|
|
24490
24815
|
} catch (error) {
|
|
24491
24816
|
recordBareDriveFailure(this.context, runId);
|
|
24817
|
+
let foldedStatus;
|
|
24818
|
+
try {
|
|
24819
|
+
foldedStatus = foldStoredVnextRun(this.context, requireRun(this.context, runId)).status;
|
|
24820
|
+
} catch {
|
|
24821
|
+
foldedStatus = void 0;
|
|
24822
|
+
}
|
|
24823
|
+
recordDriveReceipt(this.context, session, {
|
|
24824
|
+
kind: foldedStatus !== void 0 && isTerminalRunStatus(foldedStatus) ? "terminal" : "unsettled",
|
|
24825
|
+
error: driveErrorSummary(error),
|
|
24826
|
+
producerId: producer.id,
|
|
24827
|
+
producerClosed: false
|
|
24828
|
+
});
|
|
24492
24829
|
pending.reject(error);
|
|
24493
24830
|
throw error;
|
|
24494
24831
|
}
|
|
@@ -24537,11 +24874,13 @@ async function stepLocked(context, runId, producer, token) {
|
|
|
24537
24874
|
throw runtimeError("run_events_illegal", prepared.run.runId, "prepared gate is not artifacts-exist");
|
|
24538
24875
|
}
|
|
24539
24876
|
registerVnextAttemptController(context.eventStore.path, runId, { attemptId: prepared.attemptId, controller: prepared.controller });
|
|
24877
|
+
const clearBudget = armRunDurationBudgetTimer(context, runId);
|
|
24540
24878
|
try {
|
|
24541
24879
|
const observation = evaluateArtifactsGate(context, definition);
|
|
24542
24880
|
vnextGateDispatchSeams.afterEvaluate?.(prepared);
|
|
24543
24881
|
return context.eventStore.transaction(() => settlePreparedGate(context, prepared, observation));
|
|
24544
24882
|
} finally {
|
|
24883
|
+
clearBudget();
|
|
24545
24884
|
unregisterVnextAttemptController(context.eventStore.path, runId, prepared.attemptId);
|
|
24546
24885
|
}
|
|
24547
24886
|
}
|
|
@@ -24572,6 +24911,7 @@ async function runPreparedCommandGate(context, prepared, definition, token) {
|
|
|
24572
24911
|
});
|
|
24573
24912
|
armVnextGateHold(storePath, prepared.run.runId, token, prepared.attemptId, () => observer.requestStop("cancel"));
|
|
24574
24913
|
registerVnextAttemptController(storePath, prepared.run.runId, { attemptId: prepared.attemptId, controller: prepared.controller });
|
|
24914
|
+
const clearBudget = armRunDurationBudgetTimer(context, prepared.run.runId);
|
|
24575
24915
|
const unhookClose = registerVnextRuntimeCloseHook(context, () => {
|
|
24576
24916
|
try {
|
|
24577
24917
|
markVnextGateHoldUnsettled(storePath, prepared.run.runId, token, prepared.attemptId);
|
|
@@ -24612,6 +24952,7 @@ async function runPreparedCommandGate(context, prepared, definition, token) {
|
|
|
24612
24952
|
return commitCommandUncertainty(context, prepared, token, outcome.reason, outcome.observation);
|
|
24613
24953
|
} finally {
|
|
24614
24954
|
unhookClose();
|
|
24955
|
+
clearBudget();
|
|
24615
24956
|
unregisterVnextAttemptController(storePath, prepared.run.runId, prepared.attemptId);
|
|
24616
24957
|
}
|
|
24617
24958
|
}
|
|
@@ -25237,8 +25578,10 @@ async function drivePanel(context, panel, producer) {
|
|
|
25237
25578
|
const pending = /* @__PURE__ */ new Map();
|
|
25238
25579
|
let stopBirths = false;
|
|
25239
25580
|
let settlementFailed = false;
|
|
25581
|
+
const budgetClears = /* @__PURE__ */ new Map();
|
|
25240
25582
|
const register = (member) => {
|
|
25241
25583
|
registerVnextAttemptController(context.eventStore.path, runId, { attemptId: member.attemptId, controller: member.controller });
|
|
25584
|
+
budgetClears.set(member.attemptId, armRunDurationBudgetTimer(context, runId));
|
|
25242
25585
|
owned.set(member.attemptId, member);
|
|
25243
25586
|
};
|
|
25244
25587
|
const abortOwned = () => {
|
|
@@ -25421,6 +25764,7 @@ async function drivePanel(context, panel, producer) {
|
|
|
25421
25764
|
throw error;
|
|
25422
25765
|
} finally {
|
|
25423
25766
|
for (const attemptId of owned.keys()) {
|
|
25767
|
+
budgetClears.get(attemptId)?.();
|
|
25424
25768
|
unregisterVnextAttemptController(context.eventStore.path, runId, attemptId);
|
|
25425
25769
|
}
|
|
25426
25770
|
}
|
|
@@ -25600,7 +25944,7 @@ function joinPanel(context, panel) {
|
|
|
25600
25944
|
};
|
|
25601
25945
|
const cancelRun = () => {
|
|
25602
25946
|
push("step.status_changed", { stepId: panel.stepId, status: "cancelled", previousStatus: "running" });
|
|
25603
|
-
push("run.status_changed", { status: "cancelled", reason:
|
|
25947
|
+
push("run.status_changed", { status: "cancelled", reason: cancelReasonFromLog2(context, run.runId) });
|
|
25604
25948
|
for (const event of events) context.eventStore.appendEvent(event);
|
|
25605
25949
|
const next2 = foldStoredVnextRun(context, run);
|
|
25606
25950
|
persistVnextRunState(context, run.runId, next2, events[events.length - 1].sequence);
|
|
@@ -25707,16 +26051,124 @@ function transitionBudgetFailure(plan, state, fromStepId, outcome) {
|
|
|
25707
26051
|
}
|
|
25708
26052
|
return void 0;
|
|
25709
26053
|
}
|
|
25710
|
-
|
|
25711
|
-
|
|
25712
|
-
|
|
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;
|
|
25713
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) {
|
|
25714
26169
|
if (envelope.plan.limits.maxAgentTimeMs !== void 0) {
|
|
25715
26170
|
return { reason: "limit_unsupported", field: "limits.maxAgentTimeMs", detail: "agent-time budget enforcement is not available in this slice" };
|
|
25716
26171
|
}
|
|
25717
|
-
if (envelope.projectLimits.maxRunDurationMs !== void 0) {
|
|
25718
|
-
return { reason: "limit_unsupported", field: "project.limits.maxRunDurationMs", detail: "duration budget enforcement is not available in this slice" };
|
|
25719
|
-
}
|
|
25720
26172
|
if (envelope.projectLimits.maxAgentTimeMs !== void 0) {
|
|
25721
26173
|
return { reason: "limit_unsupported", field: "project.limits.maxAgentTimeMs", detail: "agent-time budget enforcement is not available in this slice" };
|
|
25722
26174
|
}
|
|
@@ -26732,6 +27184,19 @@ async function waitForDriveSessions(settled, graceMs) {
|
|
|
26732
27184
|
if (timeout !== void 0) clearTimeout(timeout);
|
|
26733
27185
|
}
|
|
26734
27186
|
}
|
|
27187
|
+
async function driveSessionStillPending(settled) {
|
|
27188
|
+
let pending = true;
|
|
27189
|
+
void settled.then(
|
|
27190
|
+
() => {
|
|
27191
|
+
pending = false;
|
|
27192
|
+
},
|
|
27193
|
+
() => {
|
|
27194
|
+
pending = false;
|
|
27195
|
+
}
|
|
27196
|
+
);
|
|
27197
|
+
await Promise.resolve();
|
|
27198
|
+
return pending;
|
|
27199
|
+
}
|
|
26735
27200
|
async function startVnextRuntimeSupervisor(options = {}) {
|
|
26736
27201
|
const now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
26737
27202
|
const paths = vnextRuntimePaths(options.stateRoot !== void 0 ? { stateRoot: options.stateRoot } : {});
|
|
@@ -26835,7 +27300,9 @@ async function startVnextRuntimeSupervisorInner(paths, requestedPortOption, now)
|
|
|
26835
27300
|
const bundle = loadVnextProject(projectRoot, {});
|
|
26836
27301
|
if (request.method === "GET" && !sub) {
|
|
26837
27302
|
const projected = rebuildVnextRunProjection(context, runId);
|
|
26838
|
-
|
|
27303
|
+
const folded = foldStoredVnextRun(context, projected);
|
|
27304
|
+
const drive = vnextDrivePollProjection(context, runId, folded);
|
|
27305
|
+
sendJson(response, 200, { ok: true, run: projected, ...drive !== void 0 ? { drive } : {} });
|
|
26839
27306
|
return;
|
|
26840
27307
|
}
|
|
26841
27308
|
if (request.method === "POST" && sub === "drive") {
|
|
@@ -26878,7 +27345,7 @@ async function startVnextRuntimeSupervisorInner(paths, requestedPortOption, now)
|
|
|
26878
27345
|
const scheduler = VnextRunScheduler.for(context, bundle);
|
|
26879
27346
|
const opening = scheduler.openDriveSession(runId, {
|
|
26880
27347
|
mode: body.mode,
|
|
26881
|
-
allowLimits:
|
|
27348
|
+
allowLimits: false,
|
|
26882
27349
|
liveMode: body.mode === "live",
|
|
26883
27350
|
createProducer
|
|
26884
27351
|
});
|
|
@@ -27052,6 +27519,15 @@ async function startVnextRuntimeSupervisorInner(paths, requestedPortOption, now)
|
|
|
27052
27519
|
}
|
|
27053
27520
|
}
|
|
27054
27521
|
await waitForDriveSessions(openSessions.map(({ session }) => session.settled), runtimeStopGraceMs());
|
|
27522
|
+
for (const { context, session } of openSessions) {
|
|
27523
|
+
if (!await driveSessionStillPending(session.settled)) continue;
|
|
27524
|
+
recordDriveReceipt(context, session, {
|
|
27525
|
+
kind: "unsettled",
|
|
27526
|
+
reason: "runtime_shutdown_grace_expired",
|
|
27527
|
+
producerId: session.producerId,
|
|
27528
|
+
producerClosed: false
|
|
27529
|
+
});
|
|
27530
|
+
}
|
|
27055
27531
|
for (const context of contexts.values()) closeVnextRuntimeContext(context);
|
|
27056
27532
|
contexts.clear();
|
|
27057
27533
|
const closed = new Promise((resolveStop) => server.close(() => resolveStop()));
|
|
@@ -29382,6 +29858,7 @@ export {
|
|
|
29382
29858
|
DEFAULT_RUNTIME_STOP_GRACE_MS,
|
|
29383
29859
|
DEFAULT_SOCKET_DIR,
|
|
29384
29860
|
DEFAULT_SUBAGENT_MODELS,
|
|
29861
|
+
DRIVE_RECEIPT_MAX_BYTES,
|
|
29385
29862
|
IMPROVEMENT_REPORT_SCHEMA,
|
|
29386
29863
|
IMPROVEMENT_REPORT_V1_SCHEMA,
|
|
29387
29864
|
LOG_LEVEL_PRIORITY,
|
|
@@ -29398,6 +29875,7 @@ export {
|
|
|
29398
29875
|
SubagentManager,
|
|
29399
29876
|
VIEWPORT_PRESETS,
|
|
29400
29877
|
VNEXT_ABSENT_MEMORY_REVISION,
|
|
29878
|
+
VNEXT_DRIVE_RECEIPT_SCHEMA,
|
|
29401
29879
|
VNEXT_EVENT_STORE_SCHEMA_VERSION,
|
|
29402
29880
|
VNEXT_REGISTRY_SCHEMA_VERSION,
|
|
29403
29881
|
VNEXT_RUN_EVENT_SCHEMA,
|
|
@@ -29452,6 +29930,7 @@ export {
|
|
|
29452
29930
|
groupRoutingRecords,
|
|
29453
29931
|
harnessCommandCandidates,
|
|
29454
29932
|
harnessSpawnUsesShell,
|
|
29933
|
+
hashVnextDriveLog,
|
|
29455
29934
|
hashVnextSupervisorToken,
|
|
29456
29935
|
hashVnextTokenProof,
|
|
29457
29936
|
isKnownHarnessId,
|
|
@@ -29511,6 +29990,7 @@ export {
|
|
|
29511
29990
|
userTables,
|
|
29512
29991
|
validateHarnessModelPair,
|
|
29513
29992
|
verifyExpectedTables,
|
|
29993
|
+
verifyVnextDriveReceipt,
|
|
29514
29994
|
vnextDeclaredExecutorIds,
|
|
29515
29995
|
vnextDeclaredRepositoryIds,
|
|
29516
29996
|
vnextEventBase,
|