@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.
- 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 +118 -1
- package/plugins/kxm/dist/mcp-server.js +1 -1
- package/plugins/kxm/dist/runtime-supervisor.js +327 -2
- package/plugins/kxm/dist/runtime.js +329 -3
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/src/database.ts +4 -1
- package/plugins/kxm/src/intake.ts +536 -0
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/project-config.ts +44 -0
- package/plugins/kxm/src/runtime-store.ts +390 -2
- package/schemas/README.md +2 -0
- package/schemas/coordinator.schema.json +54 -0
- package/schemas/intake-message.schema.json +72 -0
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
3
|
"name": "kxm",
|
|
4
4
|
"displayName": "KXM",
|
|
5
|
-
"version": "0.7.
|
|
5
|
+
"version": "0.7.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",
|
package/plugins/kxm/dist/cli.js
CHANGED
|
@@ -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 =
|
|
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.
|
|
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 =
|
|
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");
|