@kontextmind/kxm 0.7.44 → 0.7.46

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.
@@ -16432,6 +16432,8 @@ var KxmSchemaRegistry = class {
16432
16432
  permissionDiffValidator;
16433
16433
  runEventValidator;
16434
16434
  driveReceiptValidator;
16435
+ coordinatorValidator;
16436
+ intakeMessageValidator;
16435
16437
  constructor(schemasDir = DEFAULT_SCHEMA_DIR) {
16436
16438
  this.schemasDir = resolve(schemasDir);
16437
16439
  this.ajv = new import__.Ajv2020({ allErrors: true, strict: true, strictRequired: false });
@@ -16449,6 +16451,8 @@ var KxmSchemaRegistry = class {
16449
16451
  const permissionDiffFile = "permission-diff.schema.json";
16450
16452
  const runEventFile = "run-event.schema.json";
16451
16453
  const driveReceiptFile = "drive-receipt.schema.json";
16454
+ const coordinatorFile = "coordinator.schema.json";
16455
+ const intakeMessageFile = "intake-message.schema.json";
16452
16456
  this.ajv.addSchema(readJsonObject(join2(this.schemasDir, localBindingsFile)));
16453
16457
  this.ajv.addSchema(readJsonObject(join2(this.schemasDir, templateProvenanceFile)));
16454
16458
  this.ajv.addSchema(readJsonObject(join2(this.schemasDir, initOperationFile)));
@@ -16458,6 +16462,8 @@ var KxmSchemaRegistry = class {
16458
16462
  this.ajv.addSchema(readJsonObject(join2(this.schemasDir, permissionDiffFile)));
16459
16463
  this.ajv.addSchema(readJsonObject(join2(this.schemasDir, runEventFile)));
16460
16464
  this.ajv.addSchema(readJsonObject(join2(this.schemasDir, driveReceiptFile)));
16465
+ this.ajv.addSchema(readJsonObject(join2(this.schemasDir, coordinatorFile)));
16466
+ this.ajv.addSchema(readJsonObject(join2(this.schemasDir, intakeMessageFile)));
16461
16467
  for (const [kind, definition] of Object.entries(RESOURCE_SCHEMA)) {
16462
16468
  const validator = this.ajv.getSchema(`https://schemas.kxm.dev/${definition.file}`);
16463
16469
  if (!validator) throw new Error(`schema did not compile: ${definition.file}`);
@@ -16472,6 +16478,8 @@ var KxmSchemaRegistry = class {
16472
16478
  const permissionDiffValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${permissionDiffFile}`);
16473
16479
  const runEventValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${runEventFile}`);
16474
16480
  const driveReceiptValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${driveReceiptFile}`);
16481
+ const coordinatorValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${coordinatorFile}`);
16482
+ const intakeMessageValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${intakeMessageFile}`);
16475
16483
  if (!localBindingsValidator) throw new Error(`schema did not compile: ${localBindingsFile}`);
16476
16484
  if (!templateProvenanceValidator) throw new Error(`schema did not compile: ${templateProvenanceFile}`);
16477
16485
  if (!initOperationValidator) throw new Error(`schema did not compile: ${initOperationFile}`);
@@ -16481,6 +16489,8 @@ var KxmSchemaRegistry = class {
16481
16489
  if (!permissionDiffValidator) throw new Error(`schema did not compile: ${permissionDiffFile}`);
16482
16490
  if (!runEventValidator) throw new Error(`schema did not compile: ${runEventFile}`);
16483
16491
  if (!driveReceiptValidator) throw new Error(`schema did not compile: ${driveReceiptFile}`);
16492
+ if (!coordinatorValidator) throw new Error(`schema did not compile: ${coordinatorFile}`);
16493
+ if (!intakeMessageValidator) throw new Error(`schema did not compile: ${intakeMessageFile}`);
16484
16494
  this.localBindingsValidator = localBindingsValidator;
16485
16495
  this.templateProvenanceValidator = templateProvenanceValidator;
16486
16496
  this.initOperationValidator = initOperationValidator;
@@ -16490,6 +16500,8 @@ var KxmSchemaRegistry = class {
16490
16500
  this.permissionDiffValidator = permissionDiffValidator;
16491
16501
  this.runEventValidator = runEventValidator;
16492
16502
  this.driveReceiptValidator = driveReceiptValidator;
16503
+ this.coordinatorValidator = coordinatorValidator;
16504
+ this.intakeMessageValidator = intakeMessageValidator;
16493
16505
  }
16494
16506
  validate(kind, value, file) {
16495
16507
  const definition = RESOURCE_SCHEMA[kind];
@@ -16564,6 +16576,34 @@ function validateDriveReceipt(value, file) {
16564
16576
  )]);
16565
16577
  }
16566
16578
  }
16579
+ function validateCoordinator(value, file) {
16580
+ const registry = cachedRunEventRegistry ??= new KxmSchemaRegistry();
16581
+ if (!value || typeof value !== "object" || Array.isArray(value) || value.schema !== "kxm.coordinator.v1") {
16582
+ throw new KxmConfigError([issue2("schema", "coordinator_invalid", file, "expected kxm.coordinator.v1")]);
16583
+ }
16584
+ if (!registry.coordinatorValidator(value)) {
16585
+ throw new KxmConfigError([issue2(
16586
+ "schema",
16587
+ "coordinator_invalid",
16588
+ file,
16589
+ registry.ajv.errorsText(registry.coordinatorValidator.errors, { separator: "; " })
16590
+ )]);
16591
+ }
16592
+ }
16593
+ function validateIntakeMessage(value, file) {
16594
+ const registry = cachedRunEventRegistry ??= new KxmSchemaRegistry();
16595
+ if (!value || typeof value !== "object" || Array.isArray(value) || value.schema !== "kxm.intake-message.v1") {
16596
+ throw new KxmConfigError([issue2("schema", "intake_message_invalid", file, "expected kxm.intake-message.v1")]);
16597
+ }
16598
+ if (!registry.intakeMessageValidator(value)) {
16599
+ throw new KxmConfigError([issue2(
16600
+ "schema",
16601
+ "intake_message_invalid",
16602
+ file,
16603
+ registry.ajv.errorsText(registry.intakeMessageValidator.errors, { separator: "; " })
16604
+ )]);
16605
+ }
16606
+ }
16567
16607
  function schemaIssue(file, error) {
16568
16608
  const location = error.instancePath || "/";
16569
16609
  const suffix = error.params && "additionalProperty" in error.params ? ` (${String(error.params.additionalProperty)})` : "";
@@ -18042,7 +18082,7 @@ function restoreBackup(manifestPathOrDir, options = {}) {
18042
18082
  if (store.storeId === "registry" || store.storeId === "binding-store") {
18043
18083
  maxSupported = 1;
18044
18084
  } else if (store.storeId.startsWith("events:")) {
18045
- maxSupported = 4;
18085
+ maxSupported = 5;
18046
18086
  }
18047
18087
  let targetPath = store.sourcePath;
18048
18088
  if (options.projectRoot && manifest.projectRoot && targetPath.startsWith(manifest.projectRoot)) {
@@ -18312,7 +18352,7 @@ function verifyKxmDriveReceipt(receipt, events, foldedStatus, binding) {
18312
18352
  if (reasons.length === 0) return { verified: true };
18313
18353
  return { verified: false, divergence: reasons.join("; ") };
18314
18354
  }
18315
- var KXM_EVENT_STORE_SCHEMA_VERSION = 4;
18355
+ var KXM_EVENT_STORE_SCHEMA_VERSION = 5;
18316
18356
  var KXM_DRIVE_RECEIPT_SCHEMA = "kxm.drive-receipt.v1";
18317
18357
  var DRIVE_RECEIPT_MAX_BYTES = 8 * 1024;
18318
18358
  var EVENT_STORE_TABLES = {
@@ -18394,8 +18434,32 @@ var EVENT_STORE_TABLES = {
18394
18434
  "settled_event_id",
18395
18435
  "content_hash"
18396
18436
  ],
18397
- drive_receipts: ["drive_id", "run_id", "project_id", "opened_sequence", "last_sequence", "closed_at", "schema", "receipt"]
18437
+ drive_receipts: ["drive_id", "run_id", "project_id", "opened_sequence", "last_sequence", "closed_at", "schema", "receipt"],
18438
+ coordinators: [
18439
+ "coordinator_id",
18440
+ "project_id",
18441
+ "role",
18442
+ "channel",
18443
+ "ceiling_hash",
18444
+ "config_revision",
18445
+ "bound_at",
18446
+ "schema",
18447
+ "record"
18448
+ ],
18449
+ intake_messages: [
18450
+ "message_id",
18451
+ "project_id",
18452
+ "coordinator_id",
18453
+ "idempotency_key",
18454
+ "content_hash",
18455
+ "received_at",
18456
+ "dispatch_state",
18457
+ "schema",
18458
+ "record"
18459
+ ],
18460
+ project_controls: ["project_id", "paused", "reason", "updated_at", "actor", "schema", "record"]
18398
18461
  };
18462
+ var KXM_EVENT_STORE_TABLE_NAMES = Object.keys(EVENT_STORE_TABLES).sort();
18399
18463
  var EVENT_STORE_SCHEMA = `
18400
18464
  CREATE TABLE runs (
18401
18465
  run_id TEXT PRIMARY KEY,
@@ -18547,6 +18611,76 @@ CREATE TABLE drive_receipts (
18547
18611
  receipt TEXT NOT NULL
18548
18612
  ) STRICT;
18549
18613
  CREATE INDEX drive_receipts_run ON drive_receipts(run_id);
18614
+ CREATE TABLE coordinators (
18615
+ coordinator_id TEXT PRIMARY KEY,
18616
+ project_id TEXT NOT NULL,
18617
+ role TEXT NOT NULL,
18618
+ channel TEXT NOT NULL,
18619
+ ceiling_hash TEXT NOT NULL,
18620
+ config_revision TEXT NOT NULL,
18621
+ bound_at TEXT NOT NULL,
18622
+ schema TEXT NOT NULL,
18623
+ record TEXT NOT NULL
18624
+ ) STRICT;
18625
+ CREATE UNIQUE INDEX coordinators_slot ON coordinators(project_id, role, channel);
18626
+ CREATE TABLE intake_messages (
18627
+ message_id TEXT PRIMARY KEY,
18628
+ project_id TEXT NOT NULL,
18629
+ coordinator_id TEXT NOT NULL,
18630
+ idempotency_key TEXT NOT NULL,
18631
+ content_hash TEXT NOT NULL,
18632
+ received_at TEXT NOT NULL,
18633
+ dispatch_state TEXT NOT NULL CHECK (dispatch_state IN ('ready','held_paused','admitted','refused')),
18634
+ schema TEXT NOT NULL,
18635
+ record TEXT NOT NULL
18636
+ ) STRICT;
18637
+ CREATE UNIQUE INDEX intake_idempotency ON intake_messages(project_id, coordinator_id, idempotency_key);
18638
+ CREATE INDEX intake_dispatch ON intake_messages(project_id, dispatch_state, received_at, message_id);
18639
+ CREATE TABLE project_controls (
18640
+ project_id TEXT PRIMARY KEY,
18641
+ paused INTEGER NOT NULL CHECK (paused IN (0, 1)),
18642
+ reason TEXT,
18643
+ updated_at TEXT NOT NULL,
18644
+ actor TEXT NOT NULL,
18645
+ schema TEXT NOT NULL,
18646
+ record TEXT NOT NULL
18647
+ ) STRICT;
18648
+ `;
18649
+ var COORDINATOR_INTAKE_DDL = `
18650
+ CREATE TABLE IF NOT EXISTS coordinators (
18651
+ coordinator_id TEXT PRIMARY KEY,
18652
+ project_id TEXT NOT NULL,
18653
+ role TEXT NOT NULL,
18654
+ channel TEXT NOT NULL,
18655
+ ceiling_hash TEXT NOT NULL,
18656
+ config_revision TEXT NOT NULL,
18657
+ bound_at TEXT NOT NULL,
18658
+ schema TEXT NOT NULL,
18659
+ record TEXT NOT NULL
18660
+ ) STRICT;
18661
+ CREATE UNIQUE INDEX IF NOT EXISTS coordinators_slot ON coordinators(project_id, role, channel);
18662
+ CREATE TABLE IF NOT EXISTS intake_messages (
18663
+ message_id TEXT PRIMARY KEY,
18664
+ project_id TEXT NOT NULL,
18665
+ coordinator_id TEXT NOT NULL,
18666
+ idempotency_key TEXT NOT NULL,
18667
+ content_hash TEXT NOT NULL,
18668
+ received_at TEXT NOT NULL,
18669
+ dispatch_state TEXT NOT NULL CHECK (dispatch_state IN ('ready','held_paused','admitted','refused')),
18670
+ schema TEXT NOT NULL,
18671
+ record TEXT NOT NULL
18672
+ ) STRICT;
18673
+ CREATE UNIQUE INDEX IF NOT EXISTS intake_idempotency ON intake_messages(project_id, coordinator_id, idempotency_key);
18674
+ CREATE INDEX IF NOT EXISTS intake_dispatch ON intake_messages(project_id, dispatch_state, received_at, message_id);
18675
+ CREATE TABLE IF NOT EXISTS project_controls (
18676
+ project_id TEXT PRIMARY KEY,
18677
+ paused INTEGER NOT NULL CHECK (paused IN (0, 1)),
18678
+ reason TEXT,
18679
+ updated_at TEXT NOT NULL,
18680
+ actor TEXT NOT NULL,
18681
+ schema TEXT NOT NULL,
18682
+ record TEXT NOT NULL
18683
+ ) STRICT;
18550
18684
  `;
18551
18685
  var DRIVE_RECEIPTS_DDL = `
18552
18686
  CREATE TABLE IF NOT EXISTS drive_receipts (
@@ -18568,6 +18702,13 @@ var EVENT_STORE_MIGRATIONS = [
18568
18702
  migrate(database) {
18569
18703
  database.exec(DRIVE_RECEIPTS_DDL);
18570
18704
  }
18705
+ },
18706
+ {
18707
+ fromVersion: 4,
18708
+ toVersion: 5,
18709
+ migrate(database) {
18710
+ database.exec(COORDINATOR_INTAKE_DDL);
18711
+ }
18571
18712
  }
18572
18713
  ];
18573
18714
  var KxmRunEventStore = class {
@@ -18774,6 +18915,134 @@ var KxmRunEventStore = class {
18774
18915
  }
18775
18916
  });
18776
18917
  }
18918
+ /** Insert a coordinator unless the (project, role, channel) slot is taken. */
18919
+ insertCoordinatorIfAbsent(row) {
18920
+ const result = this.database.prepare(`
18921
+ INSERT OR IGNORE INTO coordinators
18922
+ (coordinator_id, project_id, role, channel, ceiling_hash, config_revision, bound_at, schema, record)
18923
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
18924
+ `).run(
18925
+ row.coordinatorId,
18926
+ row.projectId,
18927
+ row.role,
18928
+ row.channel,
18929
+ row.ceilingHash,
18930
+ row.configRevision,
18931
+ row.boundAt,
18932
+ "kxm.coordinator.v1",
18933
+ row.record
18934
+ );
18935
+ return Number(result.changes) === 1;
18936
+ }
18937
+ coordinatorById(coordinatorId) {
18938
+ const row = this.database.prepare("SELECT * FROM coordinators WHERE coordinator_id = ?").get(coordinatorId);
18939
+ return row ? coordinatorFromRow(row) : void 0;
18940
+ }
18941
+ coordinatorInSlot(projectId, role, channel) {
18942
+ const row = this.database.prepare(`
18943
+ SELECT * FROM coordinators WHERE project_id = ? AND role = ? AND channel = ?
18944
+ `).get(projectId, role, channel);
18945
+ return row ? coordinatorFromRow(row) : void 0;
18946
+ }
18947
+ /**
18948
+ * Replace the coordinator holding a (project, role, channel) slot, guarded by
18949
+ * the identity that is currently there. A rebind therefore cannot clobber a
18950
+ * record that changed underneath it, and cannot leave two live identities for
18951
+ * one slot.
18952
+ */
18953
+ replaceCoordinatorInSlot(expectedCoordinatorId, row) {
18954
+ const result = this.database.prepare(`
18955
+ UPDATE coordinators
18956
+ SET coordinator_id = ?, ceiling_hash = ?, config_revision = ?, bound_at = ?, schema = ?, record = ?
18957
+ WHERE project_id = ? AND role = ? AND channel = ? AND coordinator_id = ?
18958
+ `).run(
18959
+ row.coordinatorId,
18960
+ row.ceilingHash,
18961
+ row.configRevision,
18962
+ row.boundAt,
18963
+ "kxm.coordinator.v1",
18964
+ row.record,
18965
+ row.projectId,
18966
+ row.role,
18967
+ row.channel,
18968
+ expectedCoordinatorId
18969
+ );
18970
+ return Number(result.changes) === 1;
18971
+ }
18972
+ /** Insert an intake message unless its idempotency slot is taken. */
18973
+ insertIntakeMessageIfAbsent(row) {
18974
+ const result = this.database.prepare(`
18975
+ INSERT OR IGNORE INTO intake_messages
18976
+ (message_id, project_id, coordinator_id, idempotency_key, content_hash, received_at, dispatch_state, schema, record)
18977
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
18978
+ `).run(
18979
+ row.messageId,
18980
+ row.projectId,
18981
+ row.coordinatorId,
18982
+ row.idempotencyKey,
18983
+ row.contentHash,
18984
+ row.receivedAt,
18985
+ row.dispatchState,
18986
+ "kxm.intake-message.v1",
18987
+ row.record
18988
+ );
18989
+ return Number(result.changes) === 1;
18990
+ }
18991
+ intakeMessage(messageId) {
18992
+ const row = this.database.prepare("SELECT * FROM intake_messages WHERE message_id = ?").get(messageId);
18993
+ return row ? intakeFromRow(row) : void 0;
18994
+ }
18995
+ intakeBySlot(projectId, coordinatorId, idempotencyKey) {
18996
+ const row = this.database.prepare(`
18997
+ SELECT * FROM intake_messages WHERE project_id = ? AND coordinator_id = ? AND idempotency_key = ?
18998
+ `).get(projectId, coordinatorId, idempotencyKey);
18999
+ return row ? intakeFromRow(row) : void 0;
19000
+ }
19001
+ /** Intake rows in the given dispatch states, oldest first (stable, replay-safe order). */
19002
+ intakeInStates(projectId, states, limit = 100) {
19003
+ if (states.length === 0) return [];
19004
+ const placeholders = states.map(() => "?").join(", ");
19005
+ const rows = this.database.prepare(`
19006
+ SELECT * FROM intake_messages
19007
+ WHERE project_id = ? AND dispatch_state IN (${placeholders})
19008
+ ORDER BY received_at ASC, message_id ASC
19009
+ LIMIT ?
19010
+ `).all(projectId, ...states, limit);
19011
+ return rows.map(intakeFromRow);
19012
+ }
19013
+ /** Rewrite one intake row's dispatch state and record. Returns false when it raced away. */
19014
+ updateIntakeDispatch(messageId, expectState, next) {
19015
+ const result = this.database.prepare(`
19016
+ UPDATE intake_messages SET dispatch_state = ?, record = ?, schema = ?
19017
+ WHERE message_id = ? AND dispatch_state = ?
19018
+ `).run(next.state, next.record, "kxm.intake-message.v1", messageId, expectState);
19019
+ return Number(result.changes) === 1;
19020
+ }
19021
+ projectControl(projectId) {
19022
+ const row = this.database.prepare("SELECT * FROM project_controls WHERE project_id = ?").get(projectId);
19023
+ return row ? controlFromRow(row) : void 0;
19024
+ }
19025
+ putProjectControl(row) {
19026
+ this.database.prepare(`
19027
+ INSERT INTO project_controls (project_id, paused, reason, updated_at, actor, schema, record)
19028
+ VALUES (?, ?, ?, ?, ?, ?, ?)
19029
+ ON CONFLICT(project_id) DO UPDATE SET
19030
+ paused = excluded.paused,
19031
+ reason = excluded.reason,
19032
+ updated_at = excluded.updated_at,
19033
+ actor = excluded.actor,
19034
+ schema = excluded.schema,
19035
+ record = excluded.record
19036
+ `).run(
19037
+ row.projectId,
19038
+ row.paused ? 1 : 0,
19039
+ row.reason ?? null,
19040
+ row.updatedAt,
19041
+ row.actor,
19042
+ "kxm.project-control.v1",
19043
+ row.record
19044
+ );
19045
+ }
18777
19046
  insertCapability(row) {
18778
19047
  this.database.prepare(`
18779
19048
  INSERT INTO attempt_capabilities (attempt_id, run_id, assignment_id, step_id, step_attempt, producer_id, capability_hash, state)
@@ -19008,6 +19277,62 @@ var KxmRunEventStore = class {
19008
19277
  }
19009
19278
  }
19010
19279
  };
19280
+ function coordinatorFromRow(row) {
19281
+ const parsed = JSON.parse(row.record);
19282
+ validateCoordinator(parsed, row.coordinator_id);
19283
+ if (parsed.coordinatorId !== row.coordinator_id || parsed.ceilingHash !== row.ceiling_hash || parsed.configRevision !== row.config_revision || parsed.boundAt !== row.bound_at) {
19284
+ throw runtimeError("coordinator_record_divergent", row.coordinator_id, "coordinator columns do not match the persisted record");
19285
+ }
19286
+ return {
19287
+ coordinatorId: row.coordinator_id,
19288
+ projectId: row.project_id,
19289
+ role: row.role,
19290
+ channel: row.channel,
19291
+ ceilingHash: row.ceiling_hash,
19292
+ configRevision: row.config_revision,
19293
+ boundAt: row.bound_at,
19294
+ record: row.record
19295
+ };
19296
+ }
19297
+ function intakeFromRow(row) {
19298
+ const parsed = JSON.parse(row.record);
19299
+ validateIntakeMessage(parsed, row.message_id);
19300
+ if (parsed.messageId !== row.message_id || parsed.contentHash !== row.content_hash || parsed.receivedAt !== row.received_at || parsed.dispatch?.state !== row.dispatch_state) {
19301
+ throw runtimeError("intake_record_divergent", row.message_id, "intake columns do not match the persisted record");
19302
+ }
19303
+ return {
19304
+ messageId: row.message_id,
19305
+ projectId: row.project_id,
19306
+ coordinatorId: row.coordinator_id,
19307
+ idempotencyKey: row.idempotency_key,
19308
+ contentHash: row.content_hash,
19309
+ receivedAt: row.received_at,
19310
+ dispatchState: row.dispatch_state,
19311
+ record: row.record
19312
+ };
19313
+ }
19314
+ function controlFromRow(row) {
19315
+ const paused = row.paused === 1;
19316
+ const expected = kxmCanonicalJson({
19317
+ schema: "kxm.project-control.v1",
19318
+ projectId: row.project_id,
19319
+ paused,
19320
+ ...row.reason !== null ? { reason: row.reason } : {},
19321
+ updatedAt: row.updated_at,
19322
+ actor: row.actor
19323
+ });
19324
+ if (row.record !== expected) {
19325
+ throw runtimeError("project_control_divergent", row.project_id, "project control record does not match its columns");
19326
+ }
19327
+ return {
19328
+ projectId: row.project_id,
19329
+ paused,
19330
+ ...row.reason !== null ? { reason: row.reason } : {},
19331
+ updatedAt: row.updated_at,
19332
+ actor: row.actor,
19333
+ record: row.record
19334
+ };
19335
+ }
19011
19336
  function parseDriveReceipt(raw) {
19012
19337
  const parsed = JSON.parse(raw);
19013
19338
  validateDriveReceipt(parsed, "drive-receipt");
@@ -22308,7 +22633,8 @@ function openKxmRuntimeContext(projectRoot, options) {
22308
22633
  projectRoot: bundle.projectRoot,
22309
22634
  projectId: registration.projectId,
22310
22635
  homeRuntimeId: registration.homeRuntimeId,
22311
- eventStore
22636
+ eventStore,
22637
+ ...options.budgetClock ? { budgetClock: options.budgetClock } : {}
22312
22638
  };
22313
22639
  } catch (error) {
22314
22640
  registry.close();
@@ -26088,7 +26414,11 @@ function declaredRunDurationBudget(context, runId) {
26088
26414
  return void 0;
26089
26415
  }
26090
26416
  }
26091
- function runDurationOverrunPayload(context, runId, nowIso2 = (/* @__PURE__ */ new Date()).toISOString()) {
26417
+ function budgetNowIso(context) {
26418
+ if (process.env.KXM_DETERMINISTIC_TEST_CLOCK === "1" && context.budgetClock) return context.budgetClock();
26419
+ return (/* @__PURE__ */ new Date()).toISOString();
26420
+ }
26421
+ function runDurationOverrunPayload(context, runId, nowIso2 = budgetNowIso(context)) {
26092
26422
  const declared = declaredRunDurationBudget(context, runId);
26093
26423
  if (!declared) return void 0;
26094
26424
  const run = context.eventStore.run(runId);
@@ -26151,7 +26481,7 @@ function armRunDurationBudgetTimer(context, runId) {
26151
26481
  const fire = () => {
26152
26482
  timer = void 0;
26153
26483
  if (cleared || isKxmRuntimeContextClosed(context)) return;
26154
- const nowIso2 = (/* @__PURE__ */ new Date()).toISOString();
26484
+ const nowIso2 = budgetNowIso(context);
26155
26485
  const elapsedMs = elapsedMsBetween(state.runningSince, nowIso2);
26156
26486
  if (elapsedMs < declared.budgetMs) {
26157
26487
  arm(declared.budgetMs - elapsedMs);
@@ -29935,6 +30265,7 @@ export {
29935
30265
  KXM_ABSENT_MEMORY_REVISION,
29936
30266
  KXM_DRIVE_RECEIPT_SCHEMA,
29937
30267
  KXM_EVENT_STORE_SCHEMA_VERSION,
30268
+ KXM_EVENT_STORE_TABLE_NAMES,
29938
30269
  KXM_REGISTRY_SCHEMA_VERSION,
29939
30270
  KXM_RUN_EVENT_SCHEMA,
29940
30271
  KxmRunEventStore,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-plugin",
3
- "version": "0.7.44",
3
+ "version": "0.7.46",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "engines": {
@@ -538,7 +538,10 @@ export function restoreBackup(
538
538
  if (store.storeId === "registry" || store.storeId === "binding-store") {
539
539
  maxSupported = 1;
540
540
  } else if (store.storeId.startsWith("events:")) {
541
- maxSupported = 4;
541
+ // Must track KXM_EVENT_STORE_SCHEMA_VERSION in runtime-store.ts. The pin is
542
+ // the e6 backup/restore round-trip test: bump one without the other and it
543
+ // refuses its own fresh backup.
544
+ maxSupported = 5;
542
545
  }
543
546
 
544
547
  let targetPath = store.sourcePath;
@@ -2352,10 +2352,25 @@ function declaredRunDurationBudget(context: KxmRuntimeContext, runId: string): K
2352
2352
  }
2353
2353
  }
2354
2354
 
2355
+ /**
2356
+ * "Now" for run-duration budget accounting.
2357
+ *
2358
+ * Budgets are always measured from the log-derived `runningSince`; this only
2359
+ * supplies the other side of that subtraction. An injected `budgetClock` is
2360
+ * honoured only under `KXM_DETERMINISTIC_TEST_CLOCK=1`, so deterministic engine
2361
+ * tests can pin a budget boundary while no production path — including the
2362
+ * supervisor, which never passes a clock — can postpone a cancellation by
2363
+ * feeding the engine a stale time.
2364
+ */
2365
+ function budgetNowIso(context: KxmRuntimeContext): string {
2366
+ if (process.env.KXM_DETERMINISTIC_TEST_CLOCK === "1" && context.budgetClock) return context.budgetClock();
2367
+ return new Date().toISOString();
2368
+ }
2369
+
2355
2370
  function runDurationOverrunPayload(
2356
2371
  context: KxmRuntimeContext,
2357
2372
  runId: string,
2358
- nowIso = new Date().toISOString(),
2373
+ nowIso = budgetNowIso(context),
2359
2374
  ): { budgetMs: number; elapsedMs: number; source: KxmRunDurationBudget["source"] } | undefined {
2360
2375
  const declared = declaredRunDurationBudget(context, runId);
2361
2376
  if (!declared) return undefined;
@@ -2432,7 +2447,7 @@ function armRunDurationBudgetTimer(context: KxmRuntimeContext, runId: string): (
2432
2447
  const fire = (): void => {
2433
2448
  timer = undefined;
2434
2449
  if (cleared || isKxmRuntimeContextClosed(context)) return;
2435
- const nowIso = new Date().toISOString();
2450
+ const nowIso = budgetNowIso(context);
2436
2451
  const elapsedMs = elapsedMsBetween(state.runningSince!, nowIso);
2437
2452
  if (elapsedMs < declared.budgetMs) {
2438
2453
  arm(declared.budgetMs - elapsedMs);