@kontextmind/kxm 0.7.45 → 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.
@@ -11,7 +11,7 @@
11
11
  "name": "kxm",
12
12
  "source": "./plugins/kxm",
13
13
  "description": "Durable workflows, peer agents, and kxm tui",
14
- "version": "0.7.45",
14
+ "version": "0.7.46",
15
15
  "category": "development",
16
16
  "tags": ["kxm", "multi-agent", "workflows", "mcp"]
17
17
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kontextmind/kxm",
3
- "version": "0.7.45",
3
+ "version": "0.7.46",
4
4
  "description": "KXM local-first multi-agent orchestration and operator dashboard",
5
5
  "type": "module",
6
6
  "author": "KontextMind",
@@ -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.45",
5
+ "version": "0.7.46",
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",
@@ -22051,6 +22051,8 @@ var KxmSchemaRegistry = class {
22051
22051
  permissionDiffValidator;
22052
22052
  runEventValidator;
22053
22053
  driveReceiptValidator;
22054
+ coordinatorValidator;
22055
+ intakeMessageValidator;
22054
22056
  constructor(schemasDir = DEFAULT_SCHEMA_DIR) {
22055
22057
  this.schemasDir = resolve3(schemasDir);
22056
22058
  this.ajv = new import__.Ajv2020({ allErrors: true, strict: true, strictRequired: false });
@@ -22068,6 +22070,8 @@ var KxmSchemaRegistry = class {
22068
22070
  const permissionDiffFile = "permission-diff.schema.json";
22069
22071
  const runEventFile = "run-event.schema.json";
22070
22072
  const driveReceiptFile = "drive-receipt.schema.json";
22073
+ const coordinatorFile = "coordinator.schema.json";
22074
+ const intakeMessageFile = "intake-message.schema.json";
22071
22075
  this.ajv.addSchema(readJsonObject(join6(this.schemasDir, localBindingsFile)));
22072
22076
  this.ajv.addSchema(readJsonObject(join6(this.schemasDir, templateProvenanceFile)));
22073
22077
  this.ajv.addSchema(readJsonObject(join6(this.schemasDir, initOperationFile)));
@@ -22077,6 +22081,8 @@ var KxmSchemaRegistry = class {
22077
22081
  this.ajv.addSchema(readJsonObject(join6(this.schemasDir, permissionDiffFile)));
22078
22082
  this.ajv.addSchema(readJsonObject(join6(this.schemasDir, runEventFile)));
22079
22083
  this.ajv.addSchema(readJsonObject(join6(this.schemasDir, driveReceiptFile)));
22084
+ this.ajv.addSchema(readJsonObject(join6(this.schemasDir, coordinatorFile)));
22085
+ this.ajv.addSchema(readJsonObject(join6(this.schemasDir, intakeMessageFile)));
22080
22086
  for (const [kind, definition] of Object.entries(RESOURCE_SCHEMA)) {
22081
22087
  const validator = this.ajv.getSchema(`https://schemas.kxm.dev/${definition.file}`);
22082
22088
  if (!validator) throw new Error(`schema did not compile: ${definition.file}`);
@@ -22091,6 +22097,8 @@ var KxmSchemaRegistry = class {
22091
22097
  const permissionDiffValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${permissionDiffFile}`);
22092
22098
  const runEventValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${runEventFile}`);
22093
22099
  const driveReceiptValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${driveReceiptFile}`);
22100
+ const coordinatorValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${coordinatorFile}`);
22101
+ const intakeMessageValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${intakeMessageFile}`);
22094
22102
  if (!localBindingsValidator) throw new Error(`schema did not compile: ${localBindingsFile}`);
22095
22103
  if (!templateProvenanceValidator) throw new Error(`schema did not compile: ${templateProvenanceFile}`);
22096
22104
  if (!initOperationValidator) throw new Error(`schema did not compile: ${initOperationFile}`);
@@ -22100,6 +22108,8 @@ var KxmSchemaRegistry = class {
22100
22108
  if (!permissionDiffValidator) throw new Error(`schema did not compile: ${permissionDiffFile}`);
22101
22109
  if (!runEventValidator) throw new Error(`schema did not compile: ${runEventFile}`);
22102
22110
  if (!driveReceiptValidator) throw new Error(`schema did not compile: ${driveReceiptFile}`);
22111
+ if (!coordinatorValidator) throw new Error(`schema did not compile: ${coordinatorFile}`);
22112
+ if (!intakeMessageValidator) throw new Error(`schema did not compile: ${intakeMessageFile}`);
22103
22113
  this.localBindingsValidator = localBindingsValidator;
22104
22114
  this.templateProvenanceValidator = templateProvenanceValidator;
22105
22115
  this.initOperationValidator = initOperationValidator;
@@ -22109,6 +22119,8 @@ var KxmSchemaRegistry = class {
22109
22119
  this.permissionDiffValidator = permissionDiffValidator;
22110
22120
  this.runEventValidator = runEventValidator;
22111
22121
  this.driveReceiptValidator = driveReceiptValidator;
22122
+ this.coordinatorValidator = coordinatorValidator;
22123
+ this.intakeMessageValidator = intakeMessageValidator;
22112
22124
  }
22113
22125
  validate(kind, value, file) {
22114
22126
  const definition = RESOURCE_SCHEMA[kind];
@@ -24018,7 +24030,7 @@ function restoreBackup(manifestPathOrDir, options = {}) {
24018
24030
  if (store.storeId === "registry" || store.storeId === "binding-store") {
24019
24031
  maxSupported = 1;
24020
24032
  } else if (store.storeId.startsWith("events:")) {
24021
- maxSupported = 4;
24033
+ maxSupported = 5;
24022
24034
  }
24023
24035
  let targetPath = store.sourcePath;
24024
24036
  if (options.projectRoot && manifest.projectRoot && targetPath.startsWith(manifest.projectRoot)) {
@@ -24260,6 +24272,111 @@ var KxmRuntimeRegistry = class {
24260
24272
  }
24261
24273
  };
24262
24274
  var DRIVE_RECEIPT_MAX_BYTES = 8 * 1024;
24275
+ var EVENT_STORE_TABLES = {
24276
+ 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"],
24277
+ 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"],
24278
+ commands: ["command_id", "run_id", "kind", "result", "recorded_at"],
24279
+ run_plans: ["run_id", "run_plan_hash", "envelope", "pinned_sequence"],
24280
+ run_state: ["run_id", "last_sequence", "state"],
24281
+ attempt_capabilities: ["attempt_id", "run_id", "assignment_id", "step_id", "step_attempt", "producer_id", "capability_hash", "state"],
24282
+ gate_attempts: [
24283
+ "attempt_id",
24284
+ "run_id",
24285
+ "project_id",
24286
+ "home_runtime_id",
24287
+ "step_id",
24288
+ "step_attempt",
24289
+ "assignment_id",
24290
+ "effect_id",
24291
+ "gate_id",
24292
+ "gate_kind",
24293
+ "expect",
24294
+ "gate_definition_hash",
24295
+ "registry_hash",
24296
+ "run_plan_hash",
24297
+ "control_project_key",
24298
+ "producer_id",
24299
+ "intent_event_id",
24300
+ "content_hash"
24301
+ ],
24302
+ gate_observations: [
24303
+ "observation_id",
24304
+ "attempt_id",
24305
+ "run_id",
24306
+ "project_id",
24307
+ "home_runtime_id",
24308
+ "step_id",
24309
+ "step_attempt",
24310
+ "assignment_id",
24311
+ "effect_id",
24312
+ "completeness",
24313
+ "spawned",
24314
+ "pid",
24315
+ "exit_code",
24316
+ "signal",
24317
+ "exit_observed",
24318
+ "close_observed",
24319
+ "stop_cause",
24320
+ "signals_attempted",
24321
+ "error_class",
24322
+ "stdout_sha256",
24323
+ "stdout_bytes",
24324
+ "stdout_complete",
24325
+ "stderr_sha256",
24326
+ "stderr_bytes",
24327
+ "stderr_complete",
24328
+ "checked_count",
24329
+ "failed_count",
24330
+ "elapsed_ms",
24331
+ "started_at",
24332
+ "finished_at",
24333
+ "recorded_event_id",
24334
+ "content_hash"
24335
+ ],
24336
+ gate_evidence: [
24337
+ "evidence_id",
24338
+ "attempt_id",
24339
+ "observation_id",
24340
+ "run_id",
24341
+ "project_id",
24342
+ "home_runtime_id",
24343
+ "step_id",
24344
+ "step_attempt",
24345
+ "assignment_id",
24346
+ "effect_id",
24347
+ "evidence_key",
24348
+ "kind",
24349
+ "expect",
24350
+ "outcome",
24351
+ "settled_event_id",
24352
+ "content_hash"
24353
+ ],
24354
+ drive_receipts: ["drive_id", "run_id", "project_id", "opened_sequence", "last_sequence", "closed_at", "schema", "receipt"],
24355
+ coordinators: [
24356
+ "coordinator_id",
24357
+ "project_id",
24358
+ "role",
24359
+ "channel",
24360
+ "ceiling_hash",
24361
+ "config_revision",
24362
+ "bound_at",
24363
+ "schema",
24364
+ "record"
24365
+ ],
24366
+ intake_messages: [
24367
+ "message_id",
24368
+ "project_id",
24369
+ "coordinator_id",
24370
+ "idempotency_key",
24371
+ "content_hash",
24372
+ "received_at",
24373
+ "dispatch_state",
24374
+ "schema",
24375
+ "record"
24376
+ ],
24377
+ project_controls: ["project_id", "paused", "reason", "updated_at", "actor", "schema", "record"]
24378
+ };
24379
+ var KXM_EVENT_STORE_TABLE_NAMES = Object.keys(EVENT_STORE_TABLES).sort();
24263
24380
 
24264
24381
  // plugins/kxm/src/engine-compile.ts
24265
24382
  var KXM_COMPILED_WORKFLOW_SCHEMA = "kxm.compiled-workflow.v1";
@@ -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.45";
17124
+ var VERSION = "0.7.46";
17125
17125
  var inbox = /* @__PURE__ */ new Map();
17126
17126
  var notifiedInbox = /* @__PURE__ */ new Set();
17127
17127
  var meshClient;
@@ -16262,6 +16262,8 @@ var KxmSchemaRegistry = class {
16262
16262
  permissionDiffValidator;
16263
16263
  runEventValidator;
16264
16264
  driveReceiptValidator;
16265
+ coordinatorValidator;
16266
+ intakeMessageValidator;
16265
16267
  constructor(schemasDir = DEFAULT_SCHEMA_DIR) {
16266
16268
  this.schemasDir = resolve(schemasDir);
16267
16269
  this.ajv = new import__.Ajv2020({ allErrors: true, strict: true, strictRequired: false });
@@ -16279,6 +16281,8 @@ var KxmSchemaRegistry = class {
16279
16281
  const permissionDiffFile = "permission-diff.schema.json";
16280
16282
  const runEventFile = "run-event.schema.json";
16281
16283
  const driveReceiptFile = "drive-receipt.schema.json";
16284
+ const coordinatorFile = "coordinator.schema.json";
16285
+ const intakeMessageFile = "intake-message.schema.json";
16282
16286
  this.ajv.addSchema(readJsonObject(join2(this.schemasDir, localBindingsFile)));
16283
16287
  this.ajv.addSchema(readJsonObject(join2(this.schemasDir, templateProvenanceFile)));
16284
16288
  this.ajv.addSchema(readJsonObject(join2(this.schemasDir, initOperationFile)));
@@ -16288,6 +16292,8 @@ var KxmSchemaRegistry = class {
16288
16292
  this.ajv.addSchema(readJsonObject(join2(this.schemasDir, permissionDiffFile)));
16289
16293
  this.ajv.addSchema(readJsonObject(join2(this.schemasDir, runEventFile)));
16290
16294
  this.ajv.addSchema(readJsonObject(join2(this.schemasDir, driveReceiptFile)));
16295
+ this.ajv.addSchema(readJsonObject(join2(this.schemasDir, coordinatorFile)));
16296
+ this.ajv.addSchema(readJsonObject(join2(this.schemasDir, intakeMessageFile)));
16291
16297
  for (const [kind, definition] of Object.entries(RESOURCE_SCHEMA)) {
16292
16298
  const validator = this.ajv.getSchema(`https://schemas.kxm.dev/${definition.file}`);
16293
16299
  if (!validator) throw new Error(`schema did not compile: ${definition.file}`);
@@ -16302,6 +16308,8 @@ var KxmSchemaRegistry = class {
16302
16308
  const permissionDiffValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${permissionDiffFile}`);
16303
16309
  const runEventValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${runEventFile}`);
16304
16310
  const driveReceiptValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${driveReceiptFile}`);
16311
+ const coordinatorValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${coordinatorFile}`);
16312
+ const intakeMessageValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${intakeMessageFile}`);
16305
16313
  if (!localBindingsValidator) throw new Error(`schema did not compile: ${localBindingsFile}`);
16306
16314
  if (!templateProvenanceValidator) throw new Error(`schema did not compile: ${templateProvenanceFile}`);
16307
16315
  if (!initOperationValidator) throw new Error(`schema did not compile: ${initOperationFile}`);
@@ -16311,6 +16319,8 @@ var KxmSchemaRegistry = class {
16311
16319
  if (!permissionDiffValidator) throw new Error(`schema did not compile: ${permissionDiffFile}`);
16312
16320
  if (!runEventValidator) throw new Error(`schema did not compile: ${runEventFile}`);
16313
16321
  if (!driveReceiptValidator) throw new Error(`schema did not compile: ${driveReceiptFile}`);
16322
+ if (!coordinatorValidator) throw new Error(`schema did not compile: ${coordinatorFile}`);
16323
+ if (!intakeMessageValidator) throw new Error(`schema did not compile: ${intakeMessageFile}`);
16314
16324
  this.localBindingsValidator = localBindingsValidator;
16315
16325
  this.templateProvenanceValidator = templateProvenanceValidator;
16316
16326
  this.initOperationValidator = initOperationValidator;
@@ -16320,6 +16330,8 @@ var KxmSchemaRegistry = class {
16320
16330
  this.permissionDiffValidator = permissionDiffValidator;
16321
16331
  this.runEventValidator = runEventValidator;
16322
16332
  this.driveReceiptValidator = driveReceiptValidator;
16333
+ this.coordinatorValidator = coordinatorValidator;
16334
+ this.intakeMessageValidator = intakeMessageValidator;
16323
16335
  }
16324
16336
  validate(kind, value, file) {
16325
16337
  const definition = RESOURCE_SCHEMA[kind];
@@ -16394,6 +16406,34 @@ function validateDriveReceipt(value, file) {
16394
16406
  )]);
16395
16407
  }
16396
16408
  }
16409
+ function validateCoordinator(value, file) {
16410
+ const registry = cachedRunEventRegistry ??= new KxmSchemaRegistry();
16411
+ if (!value || typeof value !== "object" || Array.isArray(value) || value.schema !== "kxm.coordinator.v1") {
16412
+ throw new KxmConfigError([issue2("schema", "coordinator_invalid", file, "expected kxm.coordinator.v1")]);
16413
+ }
16414
+ if (!registry.coordinatorValidator(value)) {
16415
+ throw new KxmConfigError([issue2(
16416
+ "schema",
16417
+ "coordinator_invalid",
16418
+ file,
16419
+ registry.ajv.errorsText(registry.coordinatorValidator.errors, { separator: "; " })
16420
+ )]);
16421
+ }
16422
+ }
16423
+ function validateIntakeMessage(value, file) {
16424
+ const registry = cachedRunEventRegistry ??= new KxmSchemaRegistry();
16425
+ if (!value || typeof value !== "object" || Array.isArray(value) || value.schema !== "kxm.intake-message.v1") {
16426
+ throw new KxmConfigError([issue2("schema", "intake_message_invalid", file, "expected kxm.intake-message.v1")]);
16427
+ }
16428
+ if (!registry.intakeMessageValidator(value)) {
16429
+ throw new KxmConfigError([issue2(
16430
+ "schema",
16431
+ "intake_message_invalid",
16432
+ file,
16433
+ registry.ajv.errorsText(registry.intakeMessageValidator.errors, { separator: "; " })
16434
+ )]);
16435
+ }
16436
+ }
16397
16437
  function schemaIssue(file, error) {
16398
16438
  const location = error.instancePath || "/";
16399
16439
  const suffix = error.params && "additionalProperty" in error.params ? ` (${String(error.params.additionalProperty)})` : "";
@@ -17872,7 +17912,7 @@ function verifyKxmDriveReceipt(receipt, events, foldedStatus, binding) {
17872
17912
  if (reasons.length === 0) return { verified: true };
17873
17913
  return { verified: false, divergence: reasons.join("; ") };
17874
17914
  }
17875
- var KXM_EVENT_STORE_SCHEMA_VERSION = 4;
17915
+ var KXM_EVENT_STORE_SCHEMA_VERSION = 5;
17876
17916
  var KXM_DRIVE_RECEIPT_SCHEMA = "kxm.drive-receipt.v1";
17877
17917
  var DRIVE_RECEIPT_MAX_BYTES = 8 * 1024;
17878
17918
  var EVENT_STORE_TABLES = {
@@ -17954,8 +17994,32 @@ var EVENT_STORE_TABLES = {
17954
17994
  "settled_event_id",
17955
17995
  "content_hash"
17956
17996
  ],
17957
- drive_receipts: ["drive_id", "run_id", "project_id", "opened_sequence", "last_sequence", "closed_at", "schema", "receipt"]
17997
+ drive_receipts: ["drive_id", "run_id", "project_id", "opened_sequence", "last_sequence", "closed_at", "schema", "receipt"],
17998
+ coordinators: [
17999
+ "coordinator_id",
18000
+ "project_id",
18001
+ "role",
18002
+ "channel",
18003
+ "ceiling_hash",
18004
+ "config_revision",
18005
+ "bound_at",
18006
+ "schema",
18007
+ "record"
18008
+ ],
18009
+ intake_messages: [
18010
+ "message_id",
18011
+ "project_id",
18012
+ "coordinator_id",
18013
+ "idempotency_key",
18014
+ "content_hash",
18015
+ "received_at",
18016
+ "dispatch_state",
18017
+ "schema",
18018
+ "record"
18019
+ ],
18020
+ project_controls: ["project_id", "paused", "reason", "updated_at", "actor", "schema", "record"]
17958
18021
  };
18022
+ var KXM_EVENT_STORE_TABLE_NAMES = Object.keys(EVENT_STORE_TABLES).sort();
17959
18023
  var EVENT_STORE_SCHEMA = `
17960
18024
  CREATE TABLE runs (
17961
18025
  run_id TEXT PRIMARY KEY,
@@ -18107,6 +18171,76 @@ CREATE TABLE drive_receipts (
18107
18171
  receipt TEXT NOT NULL
18108
18172
  ) STRICT;
18109
18173
  CREATE INDEX drive_receipts_run ON drive_receipts(run_id);
18174
+ CREATE TABLE coordinators (
18175
+ coordinator_id TEXT PRIMARY KEY,
18176
+ project_id TEXT NOT NULL,
18177
+ role TEXT NOT NULL,
18178
+ channel TEXT NOT NULL,
18179
+ ceiling_hash TEXT NOT NULL,
18180
+ config_revision TEXT NOT NULL,
18181
+ bound_at TEXT NOT NULL,
18182
+ schema TEXT NOT NULL,
18183
+ record TEXT NOT NULL
18184
+ ) STRICT;
18185
+ CREATE UNIQUE INDEX coordinators_slot ON coordinators(project_id, role, channel);
18186
+ CREATE TABLE intake_messages (
18187
+ message_id TEXT PRIMARY KEY,
18188
+ project_id TEXT NOT NULL,
18189
+ coordinator_id TEXT NOT NULL,
18190
+ idempotency_key TEXT NOT NULL,
18191
+ content_hash TEXT NOT NULL,
18192
+ received_at TEXT NOT NULL,
18193
+ dispatch_state TEXT NOT NULL CHECK (dispatch_state IN ('ready','held_paused','admitted','refused')),
18194
+ schema TEXT NOT NULL,
18195
+ record TEXT NOT NULL
18196
+ ) STRICT;
18197
+ CREATE UNIQUE INDEX intake_idempotency ON intake_messages(project_id, coordinator_id, idempotency_key);
18198
+ CREATE INDEX intake_dispatch ON intake_messages(project_id, dispatch_state, received_at, message_id);
18199
+ CREATE TABLE project_controls (
18200
+ project_id TEXT PRIMARY KEY,
18201
+ paused INTEGER NOT NULL CHECK (paused IN (0, 1)),
18202
+ reason TEXT,
18203
+ updated_at TEXT NOT NULL,
18204
+ actor TEXT NOT NULL,
18205
+ schema TEXT NOT NULL,
18206
+ record TEXT NOT NULL
18207
+ ) STRICT;
18208
+ `;
18209
+ var COORDINATOR_INTAKE_DDL = `
18210
+ CREATE TABLE IF NOT EXISTS coordinators (
18211
+ coordinator_id TEXT PRIMARY KEY,
18212
+ project_id TEXT NOT NULL,
18213
+ role TEXT NOT NULL,
18214
+ channel TEXT NOT NULL,
18215
+ ceiling_hash TEXT NOT NULL,
18216
+ config_revision TEXT NOT NULL,
18217
+ bound_at TEXT NOT NULL,
18218
+ schema TEXT NOT NULL,
18219
+ record TEXT NOT NULL
18220
+ ) STRICT;
18221
+ CREATE UNIQUE INDEX IF NOT EXISTS coordinators_slot ON coordinators(project_id, role, channel);
18222
+ CREATE TABLE IF NOT EXISTS intake_messages (
18223
+ message_id TEXT PRIMARY KEY,
18224
+ project_id TEXT NOT NULL,
18225
+ coordinator_id TEXT NOT NULL,
18226
+ idempotency_key TEXT NOT NULL,
18227
+ content_hash TEXT NOT NULL,
18228
+ received_at TEXT NOT NULL,
18229
+ dispatch_state TEXT NOT NULL CHECK (dispatch_state IN ('ready','held_paused','admitted','refused')),
18230
+ schema TEXT NOT NULL,
18231
+ record TEXT NOT NULL
18232
+ ) STRICT;
18233
+ CREATE UNIQUE INDEX IF NOT EXISTS intake_idempotency ON intake_messages(project_id, coordinator_id, idempotency_key);
18234
+ CREATE INDEX IF NOT EXISTS intake_dispatch ON intake_messages(project_id, dispatch_state, received_at, message_id);
18235
+ CREATE TABLE IF NOT EXISTS project_controls (
18236
+ project_id TEXT PRIMARY KEY,
18237
+ paused INTEGER NOT NULL CHECK (paused IN (0, 1)),
18238
+ reason TEXT,
18239
+ updated_at TEXT NOT NULL,
18240
+ actor TEXT NOT NULL,
18241
+ schema TEXT NOT NULL,
18242
+ record TEXT NOT NULL
18243
+ ) STRICT;
18110
18244
  `;
18111
18245
  var DRIVE_RECEIPTS_DDL = `
18112
18246
  CREATE TABLE IF NOT EXISTS drive_receipts (
@@ -18128,6 +18262,13 @@ var EVENT_STORE_MIGRATIONS = [
18128
18262
  migrate(database) {
18129
18263
  database.exec(DRIVE_RECEIPTS_DDL);
18130
18264
  }
18265
+ },
18266
+ {
18267
+ fromVersion: 4,
18268
+ toVersion: 5,
18269
+ migrate(database) {
18270
+ database.exec(COORDINATOR_INTAKE_DDL);
18271
+ }
18131
18272
  }
18132
18273
  ];
18133
18274
  var KxmRunEventStore = class {
@@ -18334,6 +18475,134 @@ var KxmRunEventStore = class {
18334
18475
  }
18335
18476
  });
18336
18477
  }
18478
+ /** Insert a coordinator unless the (project, role, channel) slot is taken. */
18479
+ insertCoordinatorIfAbsent(row) {
18480
+ const result = this.database.prepare(`
18481
+ INSERT OR IGNORE INTO coordinators
18482
+ (coordinator_id, project_id, role, channel, ceiling_hash, config_revision, bound_at, schema, record)
18483
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
18484
+ `).run(
18485
+ row.coordinatorId,
18486
+ row.projectId,
18487
+ row.role,
18488
+ row.channel,
18489
+ row.ceilingHash,
18490
+ row.configRevision,
18491
+ row.boundAt,
18492
+ "kxm.coordinator.v1",
18493
+ row.record
18494
+ );
18495
+ return Number(result.changes) === 1;
18496
+ }
18497
+ coordinatorById(coordinatorId) {
18498
+ const row = this.database.prepare("SELECT * FROM coordinators WHERE coordinator_id = ?").get(coordinatorId);
18499
+ return row ? coordinatorFromRow(row) : void 0;
18500
+ }
18501
+ coordinatorInSlot(projectId, role, channel) {
18502
+ const row = this.database.prepare(`
18503
+ SELECT * FROM coordinators WHERE project_id = ? AND role = ? AND channel = ?
18504
+ `).get(projectId, role, channel);
18505
+ return row ? coordinatorFromRow(row) : void 0;
18506
+ }
18507
+ /**
18508
+ * Replace the coordinator holding a (project, role, channel) slot, guarded by
18509
+ * the identity that is currently there. A rebind therefore cannot clobber a
18510
+ * record that changed underneath it, and cannot leave two live identities for
18511
+ * one slot.
18512
+ */
18513
+ replaceCoordinatorInSlot(expectedCoordinatorId, row) {
18514
+ const result = this.database.prepare(`
18515
+ UPDATE coordinators
18516
+ SET coordinator_id = ?, ceiling_hash = ?, config_revision = ?, bound_at = ?, schema = ?, record = ?
18517
+ WHERE project_id = ? AND role = ? AND channel = ? AND coordinator_id = ?
18518
+ `).run(
18519
+ row.coordinatorId,
18520
+ row.ceilingHash,
18521
+ row.configRevision,
18522
+ row.boundAt,
18523
+ "kxm.coordinator.v1",
18524
+ row.record,
18525
+ row.projectId,
18526
+ row.role,
18527
+ row.channel,
18528
+ expectedCoordinatorId
18529
+ );
18530
+ return Number(result.changes) === 1;
18531
+ }
18532
+ /** Insert an intake message unless its idempotency slot is taken. */
18533
+ insertIntakeMessageIfAbsent(row) {
18534
+ const result = this.database.prepare(`
18535
+ INSERT OR IGNORE INTO intake_messages
18536
+ (message_id, project_id, coordinator_id, idempotency_key, content_hash, received_at, dispatch_state, schema, record)
18537
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
18538
+ `).run(
18539
+ row.messageId,
18540
+ row.projectId,
18541
+ row.coordinatorId,
18542
+ row.idempotencyKey,
18543
+ row.contentHash,
18544
+ row.receivedAt,
18545
+ row.dispatchState,
18546
+ "kxm.intake-message.v1",
18547
+ row.record
18548
+ );
18549
+ return Number(result.changes) === 1;
18550
+ }
18551
+ intakeMessage(messageId) {
18552
+ const row = this.database.prepare("SELECT * FROM intake_messages WHERE message_id = ?").get(messageId);
18553
+ return row ? intakeFromRow(row) : void 0;
18554
+ }
18555
+ intakeBySlot(projectId, coordinatorId, idempotencyKey) {
18556
+ const row = this.database.prepare(`
18557
+ SELECT * FROM intake_messages WHERE project_id = ? AND coordinator_id = ? AND idempotency_key = ?
18558
+ `).get(projectId, coordinatorId, idempotencyKey);
18559
+ return row ? intakeFromRow(row) : void 0;
18560
+ }
18561
+ /** Intake rows in the given dispatch states, oldest first (stable, replay-safe order). */
18562
+ intakeInStates(projectId, states, limit = 100) {
18563
+ if (states.length === 0) return [];
18564
+ const placeholders = states.map(() => "?").join(", ");
18565
+ const rows = this.database.prepare(`
18566
+ SELECT * FROM intake_messages
18567
+ WHERE project_id = ? AND dispatch_state IN (${placeholders})
18568
+ ORDER BY received_at ASC, message_id ASC
18569
+ LIMIT ?
18570
+ `).all(projectId, ...states, limit);
18571
+ return rows.map(intakeFromRow);
18572
+ }
18573
+ /** Rewrite one intake row's dispatch state and record. Returns false when it raced away. */
18574
+ updateIntakeDispatch(messageId, expectState, next) {
18575
+ const result = this.database.prepare(`
18576
+ UPDATE intake_messages SET dispatch_state = ?, record = ?, schema = ?
18577
+ WHERE message_id = ? AND dispatch_state = ?
18578
+ `).run(next.state, next.record, "kxm.intake-message.v1", messageId, expectState);
18579
+ return Number(result.changes) === 1;
18580
+ }
18581
+ projectControl(projectId) {
18582
+ const row = this.database.prepare("SELECT * FROM project_controls WHERE project_id = ?").get(projectId);
18583
+ return row ? controlFromRow(row) : void 0;
18584
+ }
18585
+ putProjectControl(row) {
18586
+ this.database.prepare(`
18587
+ INSERT INTO project_controls (project_id, paused, reason, updated_at, actor, schema, record)
18588
+ VALUES (?, ?, ?, ?, ?, ?, ?)
18589
+ ON CONFLICT(project_id) DO UPDATE SET
18590
+ paused = excluded.paused,
18591
+ reason = excluded.reason,
18592
+ updated_at = excluded.updated_at,
18593
+ actor = excluded.actor,
18594
+ schema = excluded.schema,
18595
+ record = excluded.record
18596
+ `).run(
18597
+ row.projectId,
18598
+ row.paused ? 1 : 0,
18599
+ row.reason ?? null,
18600
+ row.updatedAt,
18601
+ row.actor,
18602
+ "kxm.project-control.v1",
18603
+ row.record
18604
+ );
18605
+ }
18337
18606
  insertCapability(row) {
18338
18607
  this.database.prepare(`
18339
18608
  INSERT INTO attempt_capabilities (attempt_id, run_id, assignment_id, step_id, step_attempt, producer_id, capability_hash, state)
@@ -18568,6 +18837,62 @@ var KxmRunEventStore = class {
18568
18837
  }
18569
18838
  }
18570
18839
  };
18840
+ function coordinatorFromRow(row) {
18841
+ const parsed = JSON.parse(row.record);
18842
+ validateCoordinator(parsed, row.coordinator_id);
18843
+ if (parsed.coordinatorId !== row.coordinator_id || parsed.ceilingHash !== row.ceiling_hash || parsed.configRevision !== row.config_revision || parsed.boundAt !== row.bound_at) {
18844
+ throw runtimeError("coordinator_record_divergent", row.coordinator_id, "coordinator columns do not match the persisted record");
18845
+ }
18846
+ return {
18847
+ coordinatorId: row.coordinator_id,
18848
+ projectId: row.project_id,
18849
+ role: row.role,
18850
+ channel: row.channel,
18851
+ ceilingHash: row.ceiling_hash,
18852
+ configRevision: row.config_revision,
18853
+ boundAt: row.bound_at,
18854
+ record: row.record
18855
+ };
18856
+ }
18857
+ function intakeFromRow(row) {
18858
+ const parsed = JSON.parse(row.record);
18859
+ validateIntakeMessage(parsed, row.message_id);
18860
+ if (parsed.messageId !== row.message_id || parsed.contentHash !== row.content_hash || parsed.receivedAt !== row.received_at || parsed.dispatch?.state !== row.dispatch_state) {
18861
+ throw runtimeError("intake_record_divergent", row.message_id, "intake columns do not match the persisted record");
18862
+ }
18863
+ return {
18864
+ messageId: row.message_id,
18865
+ projectId: row.project_id,
18866
+ coordinatorId: row.coordinator_id,
18867
+ idempotencyKey: row.idempotency_key,
18868
+ contentHash: row.content_hash,
18869
+ receivedAt: row.received_at,
18870
+ dispatchState: row.dispatch_state,
18871
+ record: row.record
18872
+ };
18873
+ }
18874
+ function controlFromRow(row) {
18875
+ const paused = row.paused === 1;
18876
+ const expected = kxmCanonicalJson({
18877
+ schema: "kxm.project-control.v1",
18878
+ projectId: row.project_id,
18879
+ paused,
18880
+ ...row.reason !== null ? { reason: row.reason } : {},
18881
+ updatedAt: row.updated_at,
18882
+ actor: row.actor
18883
+ });
18884
+ if (row.record !== expected) {
18885
+ throw runtimeError("project_control_divergent", row.project_id, "project control record does not match its columns");
18886
+ }
18887
+ return {
18888
+ projectId: row.project_id,
18889
+ paused,
18890
+ ...row.reason !== null ? { reason: row.reason } : {},
18891
+ updatedAt: row.updated_at,
18892
+ actor: row.actor,
18893
+ record: row.record
18894
+ };
18895
+ }
18571
18896
  function parseDriveReceipt(raw) {
18572
18897
  const parsed = JSON.parse(raw);
18573
18898
  validateDriveReceipt(parsed, "drive-receipt");