@kontextmind/kxm 0.7.72 → 0.7.73
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/docs/contracts/synchronization.md +6 -3
- package/docs/operations.md +35 -4
- package/package.json +1 -1
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/cli.js +307 -235
- package/plugins/kxm/dist/client.js +67 -36
- package/plugins/kxm/dist/core.js +2 -0
- package/plugins/kxm/dist/extension.js +40 -37
- package/plugins/kxm/dist/mcp-server.js +40 -37
- package/plugins/kxm/dist/runtime-supervisor.js +917 -141
- package/plugins/kxm/dist/runtime.js +977 -237
- package/plugins/kxm/dist/server.js +16206 -8256
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/src/client.ts +85 -3
- package/plugins/kxm/src/database.ts +4 -4
- package/plugins/kxm/src/hub.ts +221 -0
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/project-config.ts +15 -0
- package/plugins/kxm/src/protocol.ts +27 -0
- package/plugins/kxm/src/runtime-store.ts +109 -1
- package/plugins/kxm/src/runtime-supervisor.ts +150 -0
- package/plugins/kxm/src/store.ts +207 -4
- package/plugins/kxm/src/sync-transform.ts +380 -0
|
@@ -2987,7 +2987,7 @@ var require_compile = __commonJS({
|
|
|
2987
2987
|
const schOrFunc = root.refs[ref];
|
|
2988
2988
|
if (schOrFunc)
|
|
2989
2989
|
return schOrFunc;
|
|
2990
|
-
let _sch =
|
|
2990
|
+
let _sch = resolve8.call(this, root, ref);
|
|
2991
2991
|
if (_sch === void 0) {
|
|
2992
2992
|
const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
|
|
2993
2993
|
const { schemaId } = this.opts;
|
|
@@ -3014,7 +3014,7 @@ var require_compile = __commonJS({
|
|
|
3014
3014
|
function sameSchemaEnv(s1, s2) {
|
|
3015
3015
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
3016
3016
|
}
|
|
3017
|
-
function
|
|
3017
|
+
function resolve8(root, ref) {
|
|
3018
3018
|
let sch;
|
|
3019
3019
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
3020
3020
|
ref = sch;
|
|
@@ -3839,7 +3839,7 @@ var require_fast_uri = __commonJS({
|
|
|
3839
3839
|
}
|
|
3840
3840
|
return uri;
|
|
3841
3841
|
}
|
|
3842
|
-
function
|
|
3842
|
+
function resolve8(baseURI, relativeURI, options) {
|
|
3843
3843
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
3844
3844
|
const {
|
|
3845
3845
|
parsed: baseParsed,
|
|
@@ -4201,7 +4201,7 @@ var require_fast_uri = __commonJS({
|
|
|
4201
4201
|
var fastUri = {
|
|
4202
4202
|
SCHEMES,
|
|
4203
4203
|
normalize,
|
|
4204
|
-
resolve:
|
|
4204
|
+
resolve: resolve8,
|
|
4205
4205
|
resolveComponent,
|
|
4206
4206
|
equal,
|
|
4207
4207
|
serialize,
|
|
@@ -14780,10 +14780,10 @@ var require_dist = __commonJS({
|
|
|
14780
14780
|
|
|
14781
14781
|
// plugins/kxm/src/runtime-supervisor.ts
|
|
14782
14782
|
import { spawn as spawn3 } from "node:child_process";
|
|
14783
|
-
import { createHash as
|
|
14784
|
-
import { chmodSync as chmodSync2, existsSync as
|
|
14783
|
+
import { createHash as createHash12, createHmac, randomBytes as randomBytes2, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
14784
|
+
import { chmodSync as chmodSync2, existsSync as existsSync12, lstatSync as lstatSync5, mkdirSync as mkdirSync6, readFileSync as readFileSync11, renameSync as renameSync3, rmSync as rmSync3, writeFileSync as writeFileSync6 } from "node:fs";
|
|
14785
14785
|
import { createServer } from "node:http";
|
|
14786
|
-
import { dirname as
|
|
14786
|
+
import { dirname as dirname8, isAbsolute as isAbsolute6, join as join16 } from "node:path";
|
|
14787
14787
|
|
|
14788
14788
|
// plugins/kxm/src/repo-root.ts
|
|
14789
14789
|
import { existsSync } from "node:fs";
|
|
@@ -15163,7 +15163,7 @@ function defaultSpawn(command, args, options) {
|
|
|
15163
15163
|
if (options.signal?.aborted) {
|
|
15164
15164
|
return Promise.resolve({ stdout: "", stderr: "", code: null, started: false, observedChildExit: false, error: new Error("process_aborted") });
|
|
15165
15165
|
}
|
|
15166
|
-
return new Promise((
|
|
15166
|
+
return new Promise((resolve8) => {
|
|
15167
15167
|
const stdout = [];
|
|
15168
15168
|
const stderr = [];
|
|
15169
15169
|
let outputBytes = 0;
|
|
@@ -15194,7 +15194,7 @@ function defaultSpawn(command, args, options) {
|
|
|
15194
15194
|
const started = Boolean(child?.pid);
|
|
15195
15195
|
if (started && !observedChildExit) error ??= new Error("process_exit_unobserved");
|
|
15196
15196
|
const unverifiedDescendants = stopping || started && !observedChildExit;
|
|
15197
|
-
|
|
15197
|
+
resolve8({
|
|
15198
15198
|
stdout: Buffer.concat(stdout).toString("utf8"),
|
|
15199
15199
|
stderr: Buffer.concat(stderr).toString("utf8"),
|
|
15200
15200
|
code,
|
|
@@ -16303,6 +16303,7 @@ var KxmSchemaRegistry = class {
|
|
|
16303
16303
|
driveReceiptValidator;
|
|
16304
16304
|
coordinatorValidator;
|
|
16305
16305
|
intakeMessageValidator;
|
|
16306
|
+
syncEventValidator;
|
|
16306
16307
|
constructor(schemasDir = DEFAULT_SCHEMA_DIR) {
|
|
16307
16308
|
this.schemasDir = resolve(schemasDir);
|
|
16308
16309
|
this.ajv = new import__.Ajv2020({ allErrors: true, strict: true, strictRequired: false });
|
|
@@ -16319,6 +16320,7 @@ var KxmSchemaRegistry = class {
|
|
|
16319
16320
|
const driveReceiptFile = "drive-receipt.schema.json";
|
|
16320
16321
|
const coordinatorFile = "coordinator.schema.json";
|
|
16321
16322
|
const intakeMessageFile = "intake-message.schema.json";
|
|
16323
|
+
const syncEventFile = "sync-event.schema.json";
|
|
16322
16324
|
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, localBindingsFile)));
|
|
16323
16325
|
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, templateProvenanceFile)));
|
|
16324
16326
|
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, initOperationFile)));
|
|
@@ -16327,6 +16329,7 @@ var KxmSchemaRegistry = class {
|
|
|
16327
16329
|
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, driveReceiptFile)));
|
|
16328
16330
|
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, coordinatorFile)));
|
|
16329
16331
|
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, intakeMessageFile)));
|
|
16332
|
+
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, syncEventFile)));
|
|
16330
16333
|
for (const [kind, definition] of Object.entries(RESOURCE_SCHEMA)) {
|
|
16331
16334
|
const validator = this.ajv.getSchema(`https://schemas.kxm.dev/${definition.file}`);
|
|
16332
16335
|
if (!validator) throw new Error(`schema did not compile: ${definition.file}`);
|
|
@@ -16340,6 +16343,7 @@ var KxmSchemaRegistry = class {
|
|
|
16340
16343
|
const driveReceiptValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${driveReceiptFile}`);
|
|
16341
16344
|
const coordinatorValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${coordinatorFile}`);
|
|
16342
16345
|
const intakeMessageValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${intakeMessageFile}`);
|
|
16346
|
+
const syncEventValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${syncEventFile}`);
|
|
16343
16347
|
if (!localBindingsValidator) throw new Error(`schema did not compile: ${localBindingsFile}`);
|
|
16344
16348
|
if (!templateProvenanceValidator) throw new Error(`schema did not compile: ${templateProvenanceFile}`);
|
|
16345
16349
|
if (!initOperationValidator) throw new Error(`schema did not compile: ${initOperationFile}`);
|
|
@@ -16348,6 +16352,7 @@ var KxmSchemaRegistry = class {
|
|
|
16348
16352
|
if (!driveReceiptValidator) throw new Error(`schema did not compile: ${driveReceiptFile}`);
|
|
16349
16353
|
if (!coordinatorValidator) throw new Error(`schema did not compile: ${coordinatorFile}`);
|
|
16350
16354
|
if (!intakeMessageValidator) throw new Error(`schema did not compile: ${intakeMessageFile}`);
|
|
16355
|
+
if (!syncEventValidator) throw new Error(`schema did not compile: ${syncEventFile}`);
|
|
16351
16356
|
this.localBindingsValidator = localBindingsValidator;
|
|
16352
16357
|
this.templateProvenanceValidator = templateProvenanceValidator;
|
|
16353
16358
|
this.initOperationValidator = initOperationValidator;
|
|
@@ -16356,6 +16361,7 @@ var KxmSchemaRegistry = class {
|
|
|
16356
16361
|
this.driveReceiptValidator = driveReceiptValidator;
|
|
16357
16362
|
this.coordinatorValidator = coordinatorValidator;
|
|
16358
16363
|
this.intakeMessageValidator = intakeMessageValidator;
|
|
16364
|
+
this.syncEventValidator = syncEventValidator;
|
|
16359
16365
|
}
|
|
16360
16366
|
validate(kind, value, file) {
|
|
16361
16367
|
const definition = RESOURCE_SCHEMA[kind];
|
|
@@ -16402,6 +16408,11 @@ function validateRunEvent(value, file) {
|
|
|
16402
16408
|
)]);
|
|
16403
16409
|
}
|
|
16404
16410
|
}
|
|
16411
|
+
function syncEventSchemaErrors(value) {
|
|
16412
|
+
const registry = cachedRunEventRegistry ??= new KxmSchemaRegistry();
|
|
16413
|
+
if (registry.syncEventValidator(value)) return void 0;
|
|
16414
|
+
return registry.ajv.errorsText(registry.syncEventValidator.errors, { separator: "; " });
|
|
16415
|
+
}
|
|
16405
16416
|
function validateDriveReceipt(value, file) {
|
|
16406
16417
|
const registry = cachedRunEventRegistry ??= new KxmSchemaRegistry();
|
|
16407
16418
|
if (!value || typeof value !== "object" || Array.isArray(value) || value.schema !== "kxm.drive-receipt.v1") {
|
|
@@ -16908,8 +16919,8 @@ function validateWorkflow(workflow, agents, models, repositories, gates, issues)
|
|
|
16908
16919
|
const writable = Object.values(objectValue(step.repositories) ?? {}).filter((access) => access === "write").length;
|
|
16909
16920
|
if (maxWriteRepositories > writable) issues.push(issue2("semantic", "write_repository_bound_invalid", file, `${stepId} maxWriteRepositories exceeds writable repository scope`));
|
|
16910
16921
|
}
|
|
16911
|
-
const
|
|
16912
|
-
const minimumPassed =
|
|
16922
|
+
const join17 = objectValue(step.join);
|
|
16923
|
+
const minimumPassed = join17 && typeof join17.minimumPassed === "number" ? join17.minimumPassed : void 0;
|
|
16913
16924
|
if (minimumPassed !== void 0 && minimumPassed > maximum) issues.push(issue2("semantic", "join_impossible", file, `${stepId} minimumPassed exceeds assignment maximum`));
|
|
16914
16925
|
const distinctBy = names(assignment?.distinctBy);
|
|
16915
16926
|
if (distinctBy.length > 0) {
|
|
@@ -17382,7 +17393,7 @@ function legacyConfigFilesAt(root) {
|
|
|
17382
17393
|
}
|
|
17383
17394
|
|
|
17384
17395
|
// plugins/kxm/src/runtime-store.ts
|
|
17385
|
-
import { createHash as
|
|
17396
|
+
import { createHash as createHash4, randomUUID } from "node:crypto";
|
|
17386
17397
|
import { existsSync as existsSync5, lstatSync as lstatSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, realpathSync as realpathSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
17387
17398
|
import { dirname as dirname5, join as join5, resolve as resolve4 } from "node:path";
|
|
17388
17399
|
|
|
@@ -17458,6 +17469,337 @@ function kxmUserStateRoot(options = {}) {
|
|
|
17458
17469
|
return resolve2(base, "kxm");
|
|
17459
17470
|
}
|
|
17460
17471
|
|
|
17472
|
+
// plugins/kxm/src/sync-transform.ts
|
|
17473
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
17474
|
+
|
|
17475
|
+
// plugins/kxm/src/redact.ts
|
|
17476
|
+
var SECRET_PATTERNS = [
|
|
17477
|
+
/\bsk-[A-Za-z0-9_-]{8,}\b/g,
|
|
17478
|
+
/\bsk-ant-[A-Za-z0-9_-]{8,}\b/g,
|
|
17479
|
+
/\bghp_[A-Za-z0-9_]{20,}\b/g,
|
|
17480
|
+
/\bgithub_pat_[A-Za-z0-9_]{20,}\b/g,
|
|
17481
|
+
/\bxox[baprs]-[A-Za-z0-9-]{10,}\b/gi,
|
|
17482
|
+
/\bBearer\s+[A-Za-z0-9._~+/=-]+\b/gi,
|
|
17483
|
+
/\bya29\.[A-Za-z0-9._~+/-]+=*/g,
|
|
17484
|
+
/\b1\/\/[A-Za-z0-9_-]+/g,
|
|
17485
|
+
/\b1\/[A-Za-z0-9_-]{20,}/g,
|
|
17486
|
+
/("?(?:access_token|refresh_token|id_token|sessionKey|session_key|claude_oauth_token|anthropicApiKey)"?\s*[:=]\s*")[^"]*(")/gi,
|
|
17487
|
+
/\bKXM_[A-Z0-9_]*(TOKEN|SECRET|KEY)[A-Z0-9_]*=\S+/gi,
|
|
17488
|
+
/\b(GITHUB_TOKEN|GH_TOKEN|KXM_AUTH_TOKEN|KXM_WORKFLOW_SIGNAL_SECRET|ANTHROPIC_API_KEY|ANTHROPIC_AUTH_TOKEN|CLAUDE_API_KEY)=\S+/gi,
|
|
17489
|
+
/\b[A-Fa-f0-9]{64}\b/g
|
|
17490
|
+
];
|
|
17491
|
+
function redactSecrets(value) {
|
|
17492
|
+
let result = value;
|
|
17493
|
+
for (const pattern of SECRET_PATTERNS) {
|
|
17494
|
+
result = result.replace(pattern, "[redacted]");
|
|
17495
|
+
}
|
|
17496
|
+
return result;
|
|
17497
|
+
}
|
|
17498
|
+
|
|
17499
|
+
// plugins/kxm/src/sync-transform.ts
|
|
17500
|
+
var KXM_SYNC_EVENT_SCHEMA = "kxm.sync-event.v1";
|
|
17501
|
+
var KXM_SYNC_REDACTOR_VERSION = "redactor-v1";
|
|
17502
|
+
var KXM_DEFAULT_SYNC_POLICY = Object.freeze({
|
|
17503
|
+
prompts: "title-only",
|
|
17504
|
+
results: "bounded-summary",
|
|
17505
|
+
evidence: "references",
|
|
17506
|
+
artifacts: "metadata",
|
|
17507
|
+
fileChanges: "paths-only",
|
|
17508
|
+
rawLogs: false,
|
|
17509
|
+
diffs: false,
|
|
17510
|
+
environmentValues: false
|
|
17511
|
+
});
|
|
17512
|
+
var KXM_DEFAULT_SYNC_POLICY_REVISION = `sha256:${createHash3("sha256").update(kxmCanonicalJson(KXM_DEFAULT_SYNC_POLICY), "utf8").digest("hex")}`;
|
|
17513
|
+
var MIN_REGISTERED_SECRET_CHARS = 4;
|
|
17514
|
+
var REDACTED = "[redacted]";
|
|
17515
|
+
var PATH_REDACTED = "[path]";
|
|
17516
|
+
var CREDENTIAL_SHAPES = [
|
|
17517
|
+
/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?(?:-----END [A-Z ]*PRIVATE KEY-----|$)/g,
|
|
17518
|
+
/\bssh-(?:rsa|ed25519|dss|ecdsa-[a-z0-9-]+) AAAA[0-9A-Za-z+/=]+/g,
|
|
17519
|
+
/\bAKIA[0-9A-Z]{16}\b/g,
|
|
17520
|
+
/\bgh[opsu]_[A-Za-z0-9]{20,}\b/g,
|
|
17521
|
+
/\b(?:api[_-]?key|token|secret|password|passwd)\s*[:=]\s*\S+/gi
|
|
17522
|
+
];
|
|
17523
|
+
var URL_USERINFO = /\b([a-z][a-z0-9+.-]*:\/\/)[^\s/@]+@/gi;
|
|
17524
|
+
var ABSOLUTE_PATHS = [
|
|
17525
|
+
/\\\\[^\s\\"'`]+\\[^\s"'`]*/g,
|
|
17526
|
+
// UNC
|
|
17527
|
+
/\b[A-Za-z]:[\\/][^\s"'`]*/g,
|
|
17528
|
+
// Windows drive
|
|
17529
|
+
/(?<![\w.~:/-])~[\\/][^\s"'`]*/g,
|
|
17530
|
+
// home
|
|
17531
|
+
/(?<![\w.~:/-])\/(?:[\w.@%+-]+\/)*[\w.@%+-]+\/?/g
|
|
17532
|
+
// POSIX
|
|
17533
|
+
];
|
|
17534
|
+
var CONTROL_CHARACTERS = /[\u0000-\u001F\u007F]/g;
|
|
17535
|
+
var KxmSyncRedactor = class {
|
|
17536
|
+
values = /* @__PURE__ */ new Set();
|
|
17537
|
+
register(value) {
|
|
17538
|
+
const trimmed = value.trim();
|
|
17539
|
+
if (trimmed.length >= MIN_REGISTERED_SECRET_CHARS) this.values.add(trimmed);
|
|
17540
|
+
}
|
|
17541
|
+
get size() {
|
|
17542
|
+
return this.values.size;
|
|
17543
|
+
}
|
|
17544
|
+
/** Scrub one free-text value; `replaced` counts every substitution. */
|
|
17545
|
+
scrub(input) {
|
|
17546
|
+
let replaced = 0;
|
|
17547
|
+
let text = input;
|
|
17548
|
+
for (const secret of [...this.values].sort((left, right) => right.length - left.length)) {
|
|
17549
|
+
const parts = text.split(secret);
|
|
17550
|
+
if (parts.length > 1) {
|
|
17551
|
+
replaced += parts.length - 1;
|
|
17552
|
+
text = parts.join(REDACTED);
|
|
17553
|
+
}
|
|
17554
|
+
}
|
|
17555
|
+
const substitute = (pattern, replacement) => {
|
|
17556
|
+
text = text.replace(pattern, (...args) => {
|
|
17557
|
+
replaced += 1;
|
|
17558
|
+
return typeof replacement === "string" ? replacement : replacement(...args);
|
|
17559
|
+
});
|
|
17560
|
+
};
|
|
17561
|
+
for (const pattern of CREDENTIAL_SHAPES) substitute(pattern, REDACTED);
|
|
17562
|
+
substitute(URL_USERINFO, (_match, scheme) => `${scheme}${REDACTED}@`);
|
|
17563
|
+
const shaped = redactSecrets(text);
|
|
17564
|
+
if (shaped !== text) {
|
|
17565
|
+
replaced += shaped.split(REDACTED).length - text.split(REDACTED).length;
|
|
17566
|
+
text = shaped;
|
|
17567
|
+
}
|
|
17568
|
+
for (const pattern of ABSOLUTE_PATHS) substitute(pattern, PATH_REDACTED);
|
|
17569
|
+
text = text.replace(CONTROL_CHARACTERS, " ").replace(/\s+/g, " ").trim();
|
|
17570
|
+
return { text, replaced };
|
|
17571
|
+
}
|
|
17572
|
+
};
|
|
17573
|
+
function isRecord(value) {
|
|
17574
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
17575
|
+
}
|
|
17576
|
+
function pickScalars(source, keys, redactor) {
|
|
17577
|
+
if (!isRecord(source)) return void 0;
|
|
17578
|
+
const result = {};
|
|
17579
|
+
for (const key of keys) {
|
|
17580
|
+
const value = source[key];
|
|
17581
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
17582
|
+
result[key] = value;
|
|
17583
|
+
} else if (typeof value === "string") {
|
|
17584
|
+
if (redactor) {
|
|
17585
|
+
const scrubbed = redactor.scrub(value);
|
|
17586
|
+
if (scrubbed.text.length > 0) result[key] = scrubbed.text.slice(0, 200);
|
|
17587
|
+
} else {
|
|
17588
|
+
result[key] = value;
|
|
17589
|
+
}
|
|
17590
|
+
}
|
|
17591
|
+
}
|
|
17592
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
17593
|
+
}
|
|
17594
|
+
var SyncPayloadBuilder = class {
|
|
17595
|
+
payload = {};
|
|
17596
|
+
omitted = /* @__PURE__ */ new Set();
|
|
17597
|
+
replaced = 0;
|
|
17598
|
+
redactor;
|
|
17599
|
+
constructor(redactor) {
|
|
17600
|
+
this.redactor = redactor;
|
|
17601
|
+
}
|
|
17602
|
+
/** Bounded, scrubbed free text; empty after scrubbing means absent. */
|
|
17603
|
+
text(value, max) {
|
|
17604
|
+
if (typeof value !== "string") return void 0;
|
|
17605
|
+
const { text, replaced } = this.redactor.scrub(value);
|
|
17606
|
+
this.replaced += replaced;
|
|
17607
|
+
if (text.length === 0) return void 0;
|
|
17608
|
+
return text.length > max ? `${text.slice(0, max - 1)}\u2026` : text;
|
|
17609
|
+
}
|
|
17610
|
+
/** A nested record rebuilt key by key, with the named keys scrubbed as text. */
|
|
17611
|
+
record(source, keys, textKeys = {}) {
|
|
17612
|
+
const scalars = pickScalars(source, keys.filter((key) => !(key in textKeys)));
|
|
17613
|
+
const result = { ...scalars ?? {} };
|
|
17614
|
+
if (isRecord(source)) {
|
|
17615
|
+
for (const [key, max] of Object.entries(textKeys)) {
|
|
17616
|
+
const value = this.text(source[key], max);
|
|
17617
|
+
if (value !== void 0) result[key] = value;
|
|
17618
|
+
}
|
|
17619
|
+
}
|
|
17620
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
17621
|
+
}
|
|
17622
|
+
set(key, value) {
|
|
17623
|
+
if (value !== void 0) this.payload[key] = value;
|
|
17624
|
+
}
|
|
17625
|
+
};
|
|
17626
|
+
var SCALAR_FIELDS = [
|
|
17627
|
+
"workflowId",
|
|
17628
|
+
"promptHash",
|
|
17629
|
+
"status",
|
|
17630
|
+
"previousStatus",
|
|
17631
|
+
"outcome",
|
|
17632
|
+
"stepId",
|
|
17633
|
+
"stepAttempt",
|
|
17634
|
+
"fromStepId",
|
|
17635
|
+
"toStepId",
|
|
17636
|
+
"assignmentId",
|
|
17637
|
+
"attemptId",
|
|
17638
|
+
"agentId",
|
|
17639
|
+
"instanceNo",
|
|
17640
|
+
"scopeEpoch",
|
|
17641
|
+
"resolutionAction"
|
|
17642
|
+
];
|
|
17643
|
+
var REF_KEYS = ["id", "kind", "hash", "status", "producerId"];
|
|
17644
|
+
var ARTIFACT_KEYS = ["id", "kind", "hash", "size", "classification", "availability"];
|
|
17645
|
+
var LEASE_KEYS = ["id", "fencingToken", "acquiredAt", "expiresAt", "operation"];
|
|
17646
|
+
var MAX_REFS = 64;
|
|
17647
|
+
var CARRIED_FIELDS = /* @__PURE__ */ new Set([
|
|
17648
|
+
...SCALAR_FIELDS,
|
|
17649
|
+
"displayTitle",
|
|
17650
|
+
"summary",
|
|
17651
|
+
"reason",
|
|
17652
|
+
"executor",
|
|
17653
|
+
"model",
|
|
17654
|
+
"timing",
|
|
17655
|
+
"usage",
|
|
17656
|
+
"evidenceRefs",
|
|
17657
|
+
"artifacts",
|
|
17658
|
+
"receipt",
|
|
17659
|
+
"error",
|
|
17660
|
+
"effect",
|
|
17661
|
+
"lease",
|
|
17662
|
+
"actor",
|
|
17663
|
+
"resolvedBy",
|
|
17664
|
+
"suppliedRefs",
|
|
17665
|
+
"counts"
|
|
17666
|
+
]);
|
|
17667
|
+
var CONTROL_FIELDS = ["effect", "lease", "actor", "resolvedBy", "resolutionAction", "reason", "suppliedRefs", "outcome"];
|
|
17668
|
+
function buildPayload(source, builder) {
|
|
17669
|
+
for (const key of SCALAR_FIELDS) {
|
|
17670
|
+
const value = source[key];
|
|
17671
|
+
if (typeof value === "string" || typeof value === "number") builder.set(key, value);
|
|
17672
|
+
}
|
|
17673
|
+
builder.set("displayTitle", builder.text(source.displayTitle, 200));
|
|
17674
|
+
builder.set("summary", builder.text(source.summary, 1e3));
|
|
17675
|
+
if (source.reason !== void 0) {
|
|
17676
|
+
builder.set("reason", builder.text(source.reason, 1e3) ?? REDACTED);
|
|
17677
|
+
}
|
|
17678
|
+
builder.set("executor", builder.record(source.executor, ["id", "kind", "harness"], {
|
|
17679
|
+
executorVersion: 128,
|
|
17680
|
+
harnessVersion: 128,
|
|
17681
|
+
helperGeneration: 256
|
|
17682
|
+
}));
|
|
17683
|
+
const model = builder.record(source.model, ["profile", "provider"], { model: 200, thinking: 64 });
|
|
17684
|
+
if (model && isRecord(source.model) && Array.isArray(source.model.tags)) {
|
|
17685
|
+
model.tags = source.model.tags.filter((tag) => typeof tag === "string").slice(0, 32);
|
|
17686
|
+
}
|
|
17687
|
+
builder.set("model", model);
|
|
17688
|
+
builder.set("timing", builder.record(source.timing, ["startedAt", "finishedAt", "durationMs", "queueMs", "agentMs"]));
|
|
17689
|
+
builder.set("usage", builder.record(source.usage, [
|
|
17690
|
+
"inputTokens",
|
|
17691
|
+
"outputTokens",
|
|
17692
|
+
"cacheReadTokens",
|
|
17693
|
+
"cacheWriteTokens",
|
|
17694
|
+
"cost",
|
|
17695
|
+
"currency",
|
|
17696
|
+
"costKind"
|
|
17697
|
+
]));
|
|
17698
|
+
const refs = (value, keys) => Array.isArray(value) ? value.slice(0, MAX_REFS).map((entry) => pickScalars(entry, keys)).filter((entry) => entry !== void 0) : void 0;
|
|
17699
|
+
builder.set("evidenceRefs", refs(source.evidenceRefs, REF_KEYS));
|
|
17700
|
+
builder.set("suppliedRefs", refs(source.suppliedRefs, REF_KEYS));
|
|
17701
|
+
builder.set("artifacts", refs(source.artifacts, ARTIFACT_KEYS));
|
|
17702
|
+
if (isRecord(source.receipt)) {
|
|
17703
|
+
const receipt = builder.record(source.receipt, ["provider", "kind", "hash", "observedAt"], { id: 512 });
|
|
17704
|
+
if (receipt) builder.set("receipts", [receipt]);
|
|
17705
|
+
if (source.receipt.url !== void 0) builder.omitted.add("receipt.url");
|
|
17706
|
+
}
|
|
17707
|
+
builder.set("error", builder.record(source.error, ["class", "retryable"], { component: 128 }));
|
|
17708
|
+
if (isRecord(source.effect)) {
|
|
17709
|
+
const effect = pickScalars(source.effect, ["id", "idempotencyKeyHash"], builder.redactor) ?? {};
|
|
17710
|
+
const policy = pickScalars(source.effect.policy, ["class", "sharedMutable", "idempotencyKey", "receiptQuery"], builder.redactor);
|
|
17711
|
+
if (policy) effect.policy = policy;
|
|
17712
|
+
builder.set("effect", effect);
|
|
17713
|
+
}
|
|
17714
|
+
builder.set("lease", pickScalars(source.lease, LEASE_KEYS, builder.redactor));
|
|
17715
|
+
builder.set("actor", builder.record(source.actor, ["kind"], { id: 200 }));
|
|
17716
|
+
builder.set("resolvedBy", builder.record(source.resolvedBy, ["kind"], { id: 200 }));
|
|
17717
|
+
if (isRecord(source.counts)) {
|
|
17718
|
+
const counts = Object.fromEntries(Object.entries(source.counts).filter((entry) => Number.isInteger(entry[1]) && entry[1] >= 0).slice(0, 32));
|
|
17719
|
+
builder.set("counts", counts);
|
|
17720
|
+
}
|
|
17721
|
+
}
|
|
17722
|
+
function envelope(event, policyRevision, payload, fieldsOmitted, valuesReplaced) {
|
|
17723
|
+
return {
|
|
17724
|
+
schema: KXM_SYNC_EVENT_SCHEMA,
|
|
17725
|
+
sourceEventId: event.eventId,
|
|
17726
|
+
eventType: event.eventType,
|
|
17727
|
+
projectId: event.projectId,
|
|
17728
|
+
runId: event.runId,
|
|
17729
|
+
homeRuntimeId: event.homeRuntimeId,
|
|
17730
|
+
sequence: event.sequence,
|
|
17731
|
+
occurredAt: event.occurredAt,
|
|
17732
|
+
configRevision: event.configRevision,
|
|
17733
|
+
memoryRevision: event.memoryRevision,
|
|
17734
|
+
executorPolicyRevision: event.executorPolicyRevision,
|
|
17735
|
+
toolPolicyRevision: event.toolPolicyRevision,
|
|
17736
|
+
syncPolicyRevision: policyRevision,
|
|
17737
|
+
redaction: {
|
|
17738
|
+
version: KXM_SYNC_REDACTOR_VERSION,
|
|
17739
|
+
fieldsOmitted: [...new Set(fieldsOmitted)].sort().slice(0, 128),
|
|
17740
|
+
valuesReplaced: Math.min(valuesReplaced, 1e5)
|
|
17741
|
+
},
|
|
17742
|
+
payload
|
|
17743
|
+
};
|
|
17744
|
+
}
|
|
17745
|
+
function deriveKxmSyncEvent(event, options = {}) {
|
|
17746
|
+
const redactor = options.redactor ?? new KxmSyncRedactor();
|
|
17747
|
+
const policyRevision = options.policyRevision ?? KXM_DEFAULT_SYNC_POLICY_REVISION;
|
|
17748
|
+
const source = isRecord(event.payload) ? event.payload : {};
|
|
17749
|
+
const builder = new SyncPayloadBuilder(redactor);
|
|
17750
|
+
buildPayload(source, builder);
|
|
17751
|
+
const omitted = new Set(builder.omitted);
|
|
17752
|
+
for (const key of Object.keys(source)) if (!CARRIED_FIELDS.has(key)) omitted.add(key);
|
|
17753
|
+
if (event.protectedPayload !== void 0) omitted.add("protectedPayload");
|
|
17754
|
+
const payload = Object.keys(builder.payload).length > 0 ? builder.payload : { counts: {} };
|
|
17755
|
+
const full = envelope(event, policyRevision, payload, omitted, builder.replaced);
|
|
17756
|
+
if (syncEventSchemaErrors(full) === void 0) return full;
|
|
17757
|
+
const degradedPayload = { degraded: true };
|
|
17758
|
+
for (const key of CONTROL_FIELDS) {
|
|
17759
|
+
const value = builder.payload[key];
|
|
17760
|
+
const restoreNested = (built, orig) => {
|
|
17761
|
+
if (!isRecord(orig)) return built !== void 0 ? built : "[redacted]";
|
|
17762
|
+
const result = {};
|
|
17763
|
+
const builtRecord = isRecord(built) ? built : {};
|
|
17764
|
+
for (const [k, v] of Object.entries(orig)) {
|
|
17765
|
+
const builtValue = builtRecord[k];
|
|
17766
|
+
if (builtValue === void 0 || typeof builtValue === "string" && builtValue.trim().length === 0) {
|
|
17767
|
+
result[k] = isRecord(v) ? restoreNested(void 0, v) : "[redacted]";
|
|
17768
|
+
} else {
|
|
17769
|
+
result[k] = builtValue;
|
|
17770
|
+
}
|
|
17771
|
+
}
|
|
17772
|
+
return result;
|
|
17773
|
+
};
|
|
17774
|
+
if (value === void 0) {
|
|
17775
|
+
const sourceValue = source[key];
|
|
17776
|
+
if (sourceValue !== void 0) {
|
|
17777
|
+
degradedPayload[key] = restoreNested(void 0, sourceValue);
|
|
17778
|
+
}
|
|
17779
|
+
} else if (typeof value === "string" && value.trim().length === 0) {
|
|
17780
|
+
degradedPayload[key] = "[redacted]";
|
|
17781
|
+
} else if (isRecord(value)) {
|
|
17782
|
+
degradedPayload[key] = restoreNested(value, source[key]);
|
|
17783
|
+
} else {
|
|
17784
|
+
degradedPayload[key] = value;
|
|
17785
|
+
}
|
|
17786
|
+
}
|
|
17787
|
+
const degradedOmitted = new Set(omitted);
|
|
17788
|
+
for (const key of Object.keys(builder.payload)) if (!(key in degradedPayload)) degradedOmitted.add(key);
|
|
17789
|
+
const degraded = envelope(event, policyRevision, degradedPayload, degradedOmitted, builder.replaced);
|
|
17790
|
+
const errors = syncEventSchemaErrors(degraded);
|
|
17791
|
+
if (errors === void 0) return degraded;
|
|
17792
|
+
throw new KxmConfigError([{
|
|
17793
|
+
phase: "schema",
|
|
17794
|
+
code: "sync_event_invalid",
|
|
17795
|
+
file: `${event.runId}#${event.sequence}`,
|
|
17796
|
+
message: `event cannot be derived into kxm.sync-event.v1: ${errors}`
|
|
17797
|
+
}]);
|
|
17798
|
+
}
|
|
17799
|
+
function kxmSyncEventBytes(event) {
|
|
17800
|
+
return kxmCanonicalJson(event);
|
|
17801
|
+
}
|
|
17802
|
+
|
|
17461
17803
|
// plugins/kxm/src/database.ts
|
|
17462
17804
|
import {
|
|
17463
17805
|
chmodSync,
|
|
@@ -17708,7 +18050,7 @@ function projectRuntimeKey(projectRoot) {
|
|
|
17708
18050
|
canonical = resolve4(projectRoot);
|
|
17709
18051
|
}
|
|
17710
18052
|
const folded = process.platform === "win32" ? canonical.toLocaleLowerCase("en-US") : canonical;
|
|
17711
|
-
return
|
|
18053
|
+
return createHash4("sha256").update(folded, "utf8").digest("hex").slice(0, 24);
|
|
17712
18054
|
}
|
|
17713
18055
|
var KXM_REGISTRY_SCHEMA_VERSION = 1;
|
|
17714
18056
|
var REGISTRY_TABLES = {
|
|
@@ -17828,6 +18170,15 @@ var KxmRuntimeRegistry = class {
|
|
|
17828
18170
|
return this.readSupervisorRow();
|
|
17829
18171
|
}
|
|
17830
18172
|
/** Register or revalidate a project's home binding. Home Runtime is immutable. */
|
|
18173
|
+
/** All projects registered to this Runtime, for restart recovery: the
|
|
18174
|
+
* supervisor needs to reopen their contexts so pending outbox rows resume
|
|
18175
|
+
* syncing and presence keeps beating. */
|
|
18176
|
+
projectsForRuntime(homeRuntimeId) {
|
|
18177
|
+
const rows = this.database.prepare(
|
|
18178
|
+
"SELECT project_root, project_id FROM projects WHERE home_runtime_id = ? ORDER BY registered_at"
|
|
18179
|
+
).all(homeRuntimeId);
|
|
18180
|
+
return rows.map((row) => ({ projectRoot: row.project_root, projectId: row.project_id }));
|
|
18181
|
+
}
|
|
17831
18182
|
registerProject(registration) {
|
|
17832
18183
|
const projectRoot = resolve4(registration.projectRoot);
|
|
17833
18184
|
const projectKey = projectRuntimeKey(projectRoot);
|
|
@@ -17904,7 +18255,7 @@ var KxmRuntimeRegistry = class {
|
|
|
17904
18255
|
var KXM_RUN_EVENT_SCHEMA = "kxm.run-event.v1";
|
|
17905
18256
|
function hashKxmDriveLog(events) {
|
|
17906
18257
|
const body = events.map((event) => `${event.eventId}:${event.sequence}`).join("\n");
|
|
17907
|
-
return `sha256:${
|
|
18258
|
+
return `sha256:${createHash4("sha256").update(body, "utf8").digest("hex")}`;
|
|
17908
18259
|
}
|
|
17909
18260
|
function verifyKxmDriveReceipt(receipt, events, foldedStatus, binding) {
|
|
17910
18261
|
const reasons = [];
|
|
@@ -17929,7 +18280,7 @@ function verifyKxmDriveReceipt(receipt, events, foldedStatus, binding) {
|
|
|
17929
18280
|
if (reasons.length === 0) return { verified: true };
|
|
17930
18281
|
return { verified: false, divergence: reasons.join("; ") };
|
|
17931
18282
|
}
|
|
17932
|
-
var KXM_EVENT_STORE_SCHEMA_VERSION =
|
|
18283
|
+
var KXM_EVENT_STORE_SCHEMA_VERSION = 6;
|
|
17933
18284
|
var KXM_DRIVE_RECEIPT_SCHEMA = "kxm.drive-receipt.v1";
|
|
17934
18285
|
var DRIVE_RECEIPT_MAX_BYTES = 8 * 1024;
|
|
17935
18286
|
var EVENT_STORE_TABLES = {
|
|
@@ -18034,7 +18385,8 @@ var EVENT_STORE_TABLES = {
|
|
|
18034
18385
|
"schema",
|
|
18035
18386
|
"record"
|
|
18036
18387
|
],
|
|
18037
|
-
project_controls: ["project_id", "paused", "reason", "updated_at", "actor", "schema", "record"]
|
|
18388
|
+
project_controls: ["project_id", "paused", "reason", "updated_at", "actor", "schema", "record"],
|
|
18389
|
+
outbox: ["seq", "run_id", "sequence", "sync_event", "attempted_at", "acked_at"]
|
|
18038
18390
|
};
|
|
18039
18391
|
var KXM_EVENT_STORE_TABLE_NAMES = Object.keys(EVENT_STORE_TABLES).sort();
|
|
18040
18392
|
var EVENT_STORE_SCHEMA = `
|
|
@@ -18222,9 +18574,22 @@ CREATE TABLE project_controls (
|
|
|
18222
18574
|
schema TEXT NOT NULL,
|
|
18223
18575
|
record TEXT NOT NULL
|
|
18224
18576
|
) STRICT;
|
|
18577
|
+
CREATE TABLE IF NOT EXISTS outbox (
|
|
18578
|
+
seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
18579
|
+
run_id TEXT NOT NULL,
|
|
18580
|
+
sequence INTEGER NOT NULL,
|
|
18581
|
+
sync_event TEXT NOT NULL,
|
|
18582
|
+
attempted_at TEXT,
|
|
18583
|
+
acked_at TEXT,
|
|
18584
|
+
UNIQUE (run_id, sequence)
|
|
18585
|
+
) STRICT;
|
|
18586
|
+
CREATE INDEX outbox_pending ON outbox(seq) WHERE acked_at IS NULL;
|
|
18225
18587
|
`;
|
|
18226
18588
|
var KxmRunEventStore = class {
|
|
18227
18589
|
path;
|
|
18590
|
+
/** Secret values registered here are replaced in every sync object this
|
|
18591
|
+
* store derives. In memory only: they are never written anywhere. */
|
|
18592
|
+
syncRedactor = new KxmSyncRedactor();
|
|
18228
18593
|
database;
|
|
18229
18594
|
constructor(path) {
|
|
18230
18595
|
this.path = resolve4(path);
|
|
@@ -18307,6 +18672,15 @@ var KxmRunEventStore = class {
|
|
|
18307
18672
|
`).get(runId);
|
|
18308
18673
|
return row ? runFromRow(row) : void 0;
|
|
18309
18674
|
}
|
|
18675
|
+
/** All projects registered to this Runtime, for restart recovery: the
|
|
18676
|
+
* supervisor needs to reopen their contexts so pending outbox rows resume
|
|
18677
|
+
* syncing and presence keeps beating. */
|
|
18678
|
+
projectsForRuntime(homeRuntimeId) {
|
|
18679
|
+
const rows = this.database.prepare(
|
|
18680
|
+
"SELECT project_root, project_id FROM projects WHERE home_runtime_id = ? ORDER BY registered_at"
|
|
18681
|
+
).all(homeRuntimeId);
|
|
18682
|
+
return rows.map((row) => ({ projectRoot: row.project_root, projectId: row.project_id }));
|
|
18683
|
+
}
|
|
18310
18684
|
runsForProject(projectId, limit = 50) {
|
|
18311
18685
|
const rows = this.database.prepare(`
|
|
18312
18686
|
SELECT 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
|
|
@@ -18346,6 +18720,39 @@ var KxmRunEventStore = class {
|
|
|
18346
18720
|
event.schema,
|
|
18347
18721
|
event.homeRuntimeId
|
|
18348
18722
|
);
|
|
18723
|
+
const syncEvent = deriveKxmSyncEvent(event, { redactor: this.syncRedactor });
|
|
18724
|
+
this.database.prepare("INSERT INTO outbox (run_id, sequence, sync_event) VALUES (?, ?, ?)").run(event.runId, event.sequence, kxmSyncEventBytes(syncEvent));
|
|
18725
|
+
}
|
|
18726
|
+
/** Unacknowledged outbox rows after `afterSeq`, in outbox order, oldest first. */
|
|
18727
|
+
pendingOutbox(limit = 100, afterSeq = 0) {
|
|
18728
|
+
const rows = this.database.prepare(`
|
|
18729
|
+
SELECT seq, run_id, sequence, sync_event, attempted_at, acked_at
|
|
18730
|
+
FROM outbox WHERE acked_at IS NULL AND seq > ? ORDER BY seq ASC LIMIT ?
|
|
18731
|
+
`).all(afterSeq, limit);
|
|
18732
|
+
return rows.map(outboxFromRow);
|
|
18733
|
+
}
|
|
18734
|
+
/** Every outbox row of one run, acknowledged or not, in sequence order. */
|
|
18735
|
+
outboxForRun(runId) {
|
|
18736
|
+
const rows = this.database.prepare(`
|
|
18737
|
+
SELECT seq, run_id, sequence, sync_event, attempted_at, acked_at
|
|
18738
|
+
FROM outbox WHERE run_id = ? ORDER BY sequence ASC
|
|
18739
|
+
`).all(runId);
|
|
18740
|
+
return rows.map(outboxFromRow);
|
|
18741
|
+
}
|
|
18742
|
+
markOutboxAttempted(seqs, at) {
|
|
18743
|
+
const statement = this.database.prepare("UPDATE outbox SET attempted_at = ? WHERE seq = ? AND acked_at IS NULL");
|
|
18744
|
+
this.transaction(() => {
|
|
18745
|
+
for (const seq of seqs) statement.run(at, seq);
|
|
18746
|
+
});
|
|
18747
|
+
}
|
|
18748
|
+
/** Advance the cursor: the hub holds these rows now. Returns rows newly acked. */
|
|
18749
|
+
ackOutbox(seqs, at) {
|
|
18750
|
+
const statement = this.database.prepare("UPDATE outbox SET acked_at = ? WHERE seq = ? AND acked_at IS NULL");
|
|
18751
|
+
return this.transaction(() => {
|
|
18752
|
+
let changed = 0;
|
|
18753
|
+
for (const seq of seqs) changed += Number(statement.run(at, seq).changes);
|
|
18754
|
+
return changed;
|
|
18755
|
+
});
|
|
18349
18756
|
}
|
|
18350
18757
|
events(runId, afterSequence = 0, limit = 200) {
|
|
18351
18758
|
const rows = this.database.prepare(`
|
|
@@ -18862,6 +19269,16 @@ function parseDriveReceipt(raw) {
|
|
|
18862
19269
|
validateDriveReceipt(parsed, "drive-receipt");
|
|
18863
19270
|
return parsed;
|
|
18864
19271
|
}
|
|
19272
|
+
function outboxFromRow(row) {
|
|
19273
|
+
return {
|
|
19274
|
+
seq: row.seq,
|
|
19275
|
+
runId: row.run_id,
|
|
19276
|
+
sequence: row.sequence,
|
|
19277
|
+
syncEvent: row.sync_event,
|
|
19278
|
+
...row.attempted_at !== null ? { attemptedAt: row.attempted_at } : {},
|
|
19279
|
+
...row.acked_at !== null ? { ackedAt: row.acked_at } : {}
|
|
19280
|
+
};
|
|
19281
|
+
}
|
|
18865
19282
|
function runFromRow(row) {
|
|
18866
19283
|
if (typeof row.memory_revision !== "string" || row.memory_revision.length === 0) {
|
|
18867
19284
|
throw runtimeError("runtime_schema_shape_invalid", row.run_id, "memory_revision is required");
|
|
@@ -18984,7 +19401,7 @@ function gateRowContentHash(table, row) {
|
|
|
18984
19401
|
const copy = { ...row };
|
|
18985
19402
|
delete copy.contentHash;
|
|
18986
19403
|
void table;
|
|
18987
|
-
return `sha256:${
|
|
19404
|
+
return `sha256:${createHash4("sha256").update(kxmCanonicalJson(copy), "utf8").digest("hex")}`;
|
|
18988
19405
|
}
|
|
18989
19406
|
function assertClosedGateObservation(kind, row, id = "observation") {
|
|
18990
19407
|
if (row.completeness === "incomplete") return;
|
|
@@ -19214,7 +19631,7 @@ function capabilityFromRow(row) {
|
|
|
19214
19631
|
}
|
|
19215
19632
|
|
|
19216
19633
|
// plugins/kxm/src/runtime-service.ts
|
|
19217
|
-
import { createHash as
|
|
19634
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
19218
19635
|
import { existsSync as existsSync6, readdirSync as readdirSync3, readFileSync as readFileSync4 } from "node:fs";
|
|
19219
19636
|
import { join as join6 } from "node:path";
|
|
19220
19637
|
|
|
@@ -20420,7 +20837,7 @@ function isTerminalRunStatus(status) {
|
|
|
20420
20837
|
}
|
|
20421
20838
|
|
|
20422
20839
|
// plugins/kxm/src/engine-plan.ts
|
|
20423
|
-
import { createHash as
|
|
20840
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
20424
20841
|
|
|
20425
20842
|
// plugins/kxm/src/engine-compile.ts
|
|
20426
20843
|
var KXM_COMPILED_WORKFLOW_SCHEMA = "kxm.compiled-workflow.v1";
|
|
@@ -20570,7 +20987,7 @@ function compileStep(step, index, stepIndex, requirePlanHash, sink) {
|
|
|
20570
20987
|
const maxAttempts = compileCountField(step.maxAttempts, 1, `${id}.maxAttempts`, id, sink);
|
|
20571
20988
|
const timeoutMs = compileOptionalDuration(step.timeoutMs, `${id}.timeoutMs`, id, sink);
|
|
20572
20989
|
const assignments = compileAssignments(step, id, agent, sink);
|
|
20573
|
-
const
|
|
20990
|
+
const join17 = compileJoin(step, id, sink);
|
|
20574
20991
|
const requiredEvidence = compileEvidence(step, id, sink);
|
|
20575
20992
|
const transitions = compileTransitions(step, id, index, stepIndex, sink);
|
|
20576
20993
|
const outcomes = Object.keys(transitions).sort(compareCodeUnits4);
|
|
@@ -20598,7 +21015,7 @@ function compileStep(step, index, stepIndex, requirePlanHash, sink) {
|
|
|
20598
21015
|
transitions: orderedTransitions,
|
|
20599
21016
|
requiresPlanHash: requirePlanHash.includes(id),
|
|
20600
21017
|
assignments,
|
|
20601
|
-
join:
|
|
21018
|
+
join: join17
|
|
20602
21019
|
};
|
|
20603
21020
|
if (kind === "agent" || kind === "moa") {
|
|
20604
21021
|
if (!agent) return void 0;
|
|
@@ -20648,15 +21065,15 @@ function compileAssignments(step, stepId, primaryAgentId, sink) {
|
|
|
20648
21065
|
};
|
|
20649
21066
|
}
|
|
20650
21067
|
function compileJoin(step, stepId, sink) {
|
|
20651
|
-
const
|
|
20652
|
-
if (!
|
|
20653
|
-
const declared = stringValue2(
|
|
21068
|
+
const join17 = objectValue2(step.join);
|
|
21069
|
+
if (!join17) return { strategy: "all" };
|
|
21070
|
+
const declared = stringValue2(join17.strategy);
|
|
20654
21071
|
const strategy = declared && JOIN_STRATEGIES.has(declared) ? declared : "all";
|
|
20655
|
-
const minimumPassed = compileOptionalCount(
|
|
21072
|
+
const minimumPassed = compileOptionalCount(join17.minimumPassed, `${stepId}.join.minimumPassed`, stepId, sink);
|
|
20656
21073
|
const compiled = {
|
|
20657
21074
|
strategy,
|
|
20658
21075
|
...minimumPassed !== void 0 ? { minimumPassed } : {},
|
|
20659
|
-
...typeof
|
|
21076
|
+
...typeof join17.cancelRemaining === "boolean" ? { cancelRemaining: join17.cancelRemaining } : {}
|
|
20660
21077
|
};
|
|
20661
21078
|
return compiled;
|
|
20662
21079
|
}
|
|
@@ -20967,10 +21384,10 @@ var JOIN_OPTIONAL = ["minimumPassed", "cancelRemaining"];
|
|
|
20967
21384
|
var EVIDENCE_REQUIRED = ["key", "kind", "minimum", "reusableAcrossAttempts"];
|
|
20968
21385
|
var EVIDENCE_OPTIONAL = ["producerPolicy"];
|
|
20969
21386
|
function kxmSha256(input) {
|
|
20970
|
-
return `sha256:${
|
|
21387
|
+
return `sha256:${createHash5("sha256").update(input, "utf8").digest("hex")}`;
|
|
20971
21388
|
}
|
|
20972
|
-
function hashKxmRunPlanEnvelope(
|
|
20973
|
-
return kxmSha256(kxmCanonicalJson(
|
|
21389
|
+
function hashKxmRunPlanEnvelope(envelope2) {
|
|
21390
|
+
return kxmSha256(kxmCanonicalJson(envelope2));
|
|
20974
21391
|
}
|
|
20975
21392
|
function freezeKxmCompiledPlan(plan) {
|
|
20976
21393
|
const steps = /* @__PURE__ */ Object.create(null);
|
|
@@ -21017,12 +21434,12 @@ function parseKxmRunPlanEnvelope(raw, run, expectedHash) {
|
|
|
21017
21434
|
} catch {
|
|
21018
21435
|
throw runtimeError("run_plan_corrupt", run.runId, "run plan envelope is not JSON");
|
|
21019
21436
|
}
|
|
21020
|
-
const
|
|
21021
|
-
const hash = hashKxmRunPlanEnvelope(
|
|
21437
|
+
const envelope2 = parseEnvelope(parsed, run);
|
|
21438
|
+
const hash = hashKxmRunPlanEnvelope(envelope2);
|
|
21022
21439
|
if (hash !== expectedHash) {
|
|
21023
21440
|
throw runtimeError("run_plan_corrupt", run.runId, "run plan envelope hash does not match");
|
|
21024
21441
|
}
|
|
21025
|
-
return
|
|
21442
|
+
return envelope2;
|
|
21026
21443
|
}
|
|
21027
21444
|
function rehydrateKxmCompiledPlanFromStore(store, run) {
|
|
21028
21445
|
return loadPinnedEnvelope(store, run).plan;
|
|
@@ -21532,7 +21949,7 @@ function gateRegistryHash(registryValue) {
|
|
|
21532
21949
|
}
|
|
21533
21950
|
|
|
21534
21951
|
// plugins/kxm/src/engine-evidence.ts
|
|
21535
|
-
function verifyKxmGateEvidence(store, run,
|
|
21952
|
+
function verifyKxmGateEvidence(store, run, envelope2, events, state) {
|
|
21536
21953
|
const attempts = store.gateAttemptsForRun(run.runId);
|
|
21537
21954
|
const observations = store.gateObservationsForRun(run.runId);
|
|
21538
21955
|
const evidence = store.gateEvidenceForRun(run.runId);
|
|
@@ -21550,7 +21967,7 @@ function verifyKxmGateEvidence(store, run, envelope, events, state) {
|
|
|
21550
21967
|
const attemptId = stringField(event, "attemptId");
|
|
21551
21968
|
const row = attemptById.get(attemptId);
|
|
21552
21969
|
if (!row) throw runtimeError("gate_attempt_pin_mismatch", run.runId, `effect.intent_recorded ${event.eventId} has no gate_attempts row`);
|
|
21553
|
-
assertAttemptMatchesEvent(run,
|
|
21970
|
+
assertAttemptMatchesEvent(run, envelope2, state, event, row);
|
|
21554
21971
|
}
|
|
21555
21972
|
for (const event of observedEvents) {
|
|
21556
21973
|
const attemptId = stringField(event, "attemptId");
|
|
@@ -21613,7 +22030,7 @@ function verifyKxmGateEvidence(store, run, envelope, events, state) {
|
|
|
21613
22030
|
if (row.outcome !== recomputed || event.payload.outcome !== recomputed) {
|
|
21614
22031
|
throw runtimeError("gate_evidence_hash_mismatch", run.runId, "evidence outcome does not match the observation and expect");
|
|
21615
22032
|
}
|
|
21616
|
-
const step =
|
|
22033
|
+
const step = envelope2.plan.steps[row.stepId];
|
|
21617
22034
|
const required = step?.requiredEvidence.filter((item) => item.kind === "gate") ?? [];
|
|
21618
22035
|
const expectedKey = required.length === 1 ? required[0].key : null;
|
|
21619
22036
|
if (row.evidenceKey !== expectedKey) {
|
|
@@ -21709,7 +22126,7 @@ function lastEffectByAttempt(events) {
|
|
|
21709
22126
|
}
|
|
21710
22127
|
return last;
|
|
21711
22128
|
}
|
|
21712
|
-
function assertAttemptMatchesEvent(run,
|
|
22129
|
+
function assertAttemptMatchesEvent(run, envelope2, state, event, row) {
|
|
21713
22130
|
const effectId = event.payload.effect?.id;
|
|
21714
22131
|
if (row.effectId !== effectId || row.intentEventId !== event.eventId) {
|
|
21715
22132
|
throw runtimeError("gate_attempt_pin_mismatch", run.runId, "intent event does not match the gate attempt");
|
|
@@ -21720,14 +22137,14 @@ function assertAttemptMatchesEvent(run, envelope, state, event, row) {
|
|
|
21720
22137
|
if (row.stepId !== event.payload.stepId || row.stepAttempt !== event.payload.stepAttempt || row.assignmentId !== event.payload.assignmentId || row.attemptId !== event.payload.attemptId) {
|
|
21721
22138
|
throw runtimeError("gate_attempt_pin_mismatch", run.runId, "gate attempt identity does not match the intent payload");
|
|
21722
22139
|
}
|
|
21723
|
-
const step =
|
|
22140
|
+
const step = envelope2.plan.steps[row.stepId];
|
|
21724
22141
|
if (!step || step.kind !== "gate") {
|
|
21725
22142
|
throw runtimeError("gate_attempt_pin_mismatch", run.runId, "gate attempt step is not a gate");
|
|
21726
22143
|
}
|
|
21727
22144
|
if (row.gateId !== step.gate || row.expect !== step.expect) {
|
|
21728
22145
|
throw runtimeError("gate_attempt_pin_mismatch", run.runId, "gate attempt gate/expect do not match the plan");
|
|
21729
22146
|
}
|
|
21730
|
-
const definition =
|
|
22147
|
+
const definition = envelope2.gates.definitions[step.gate];
|
|
21731
22148
|
if (!definition || definition.kind !== row.gateKind) {
|
|
21732
22149
|
throw runtimeError("gate_attempt_pin_mismatch", run.runId, "gate attempt kind does not match the pinned definition");
|
|
21733
22150
|
}
|
|
@@ -21735,14 +22152,14 @@ function assertAttemptMatchesEvent(run, envelope, state, event, row) {
|
|
|
21735
22152
|
if (row.gateDefinitionHash !== expectedDefinitionHash) {
|
|
21736
22153
|
throw runtimeError("gate_attempt_pin_mismatch", run.runId, "gate_definition_hash does not match the pinned definition");
|
|
21737
22154
|
}
|
|
21738
|
-
if (!
|
|
22155
|
+
if (!envelope2.gates.registry || row.registryHash !== envelope2.gates.registry.hash) {
|
|
21739
22156
|
throw runtimeError("gate_attempt_pin_mismatch", run.runId, "registry_hash does not match the pinned registry");
|
|
21740
22157
|
}
|
|
21741
|
-
const planRowHash =
|
|
22158
|
+
const planRowHash = envelope2 && state.runPlanHash;
|
|
21742
22159
|
if (row.runPlanHash !== planRowHash) {
|
|
21743
22160
|
throw runtimeError("gate_attempt_pin_mismatch", run.runId, "run_plan_hash does not match the pinned plan");
|
|
21744
22161
|
}
|
|
21745
|
-
if (row.controlProjectKey !==
|
|
22162
|
+
if (row.controlProjectKey !== envelope2.gates.controlRoot.projectKey) {
|
|
21746
22163
|
throw runtimeError("gate_attempt_pin_mismatch", run.runId, "control_project_key does not match the envelope");
|
|
21747
22164
|
}
|
|
21748
22165
|
}
|
|
@@ -21980,12 +22397,12 @@ function enqueueKxmScheduledRun(storePath, runId, configRevision, bound, start)
|
|
|
21980
22397
|
if (owner.admitted.has(runId) || owner.queue.some((item) => item.runId === runId)) {
|
|
21981
22398
|
return Promise.reject(runtimeError("run_busy", runId, `run ${runId} is already admitted or queued`));
|
|
21982
22399
|
}
|
|
21983
|
-
return new Promise((
|
|
22400
|
+
return new Promise((resolve8, reject) => {
|
|
21984
22401
|
owner.queue.push({
|
|
21985
22402
|
runId,
|
|
21986
22403
|
configRevision,
|
|
21987
22404
|
bound,
|
|
21988
|
-
start: (token) => start(token).then(
|
|
22405
|
+
start: (token) => start(token).then(resolve8, reject),
|
|
21989
22406
|
fail: (error) => reject(error)
|
|
21990
22407
|
});
|
|
21991
22408
|
pump(storePath, owner);
|
|
@@ -22014,7 +22431,7 @@ function compareCodeUnits5(left, right) {
|
|
|
22014
22431
|
return left < right ? -1 : left > right ? 1 : 0;
|
|
22015
22432
|
}
|
|
22016
22433
|
function sha256Of(input) {
|
|
22017
|
-
return `sha256:${
|
|
22434
|
+
return `sha256:${createHash6("sha256").update(input, "utf8").digest("hex")}`;
|
|
22018
22435
|
}
|
|
22019
22436
|
function objectValue3(value) {
|
|
22020
22437
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
@@ -22062,7 +22479,7 @@ function collectMemoryFiles(dir, baseDir, ignoreSubdirs = /* @__PURE__ */ new Se
|
|
|
22062
22479
|
return results;
|
|
22063
22480
|
}
|
|
22064
22481
|
function computeKxmMemoryRevision(bundle, options = {}) {
|
|
22065
|
-
const hash =
|
|
22482
|
+
const hash = createHash6("sha256");
|
|
22066
22483
|
const memoryDir = options.memoryDir ?? join6(bundle.projectRoot, ".kxm", "memory");
|
|
22067
22484
|
const memoryFiles = collectMemoryFiles(memoryDir, memoryDir, /* @__PURE__ */ new Set(["candidates"])).sort((left, right) => compareCodeUnits5(left.relPath, right.relPath));
|
|
22068
22485
|
for (const file of memoryFiles) {
|
|
@@ -22213,7 +22630,7 @@ function acceptKxmRun(context, bundle, request, options = {}) {
|
|
|
22213
22630
|
}
|
|
22214
22631
|
const revisions = kxmPolicyRevisions(bundle, options);
|
|
22215
22632
|
const memoryRevision = options.memoryRevision ?? revisions.memoryRevision;
|
|
22216
|
-
const promptSha256 = `sha256:${
|
|
22633
|
+
const promptSha256 = `sha256:${createHash6("sha256").update(request.prompt, "utf8").digest("hex")}`;
|
|
22217
22634
|
return context.eventStore.transaction(() => {
|
|
22218
22635
|
const prior = context.eventStore.command(commandId);
|
|
22219
22636
|
if (prior) {
|
|
@@ -22288,21 +22705,21 @@ function foldStoredKxmRun(context, run) {
|
|
|
22288
22705
|
if (!planRow && events.some((event) => event.eventType.startsWith("step.") || event.eventType.startsWith("effect.") || event.payload.status === "preparing" || event.payload.status === "running" || event.payload.status === "cancelling")) {
|
|
22289
22706
|
throw runtimeError("run_plan_missing", run.runId, "run reached preparing or later without a pinned plan");
|
|
22290
22707
|
}
|
|
22291
|
-
const
|
|
22292
|
-
const state = foldKxmRunState(run,
|
|
22708
|
+
const envelope2 = planRow ? loadKxmRunPlanEnvelope(context.eventStore, run) : void 0;
|
|
22709
|
+
const state = foldKxmRunState(run, envelope2?.plan, events, envelope2 ? {
|
|
22293
22710
|
observationLookup: (attemptId, observationId) => {
|
|
22294
22711
|
const row = context.eventStore.gateObservationForAttempt(attemptId);
|
|
22295
22712
|
if (!row || row.observationId !== observationId) return void 0;
|
|
22296
22713
|
return { completeness: row.completeness };
|
|
22297
22714
|
},
|
|
22298
22715
|
gateKind: (stepId) => {
|
|
22299
|
-
const step =
|
|
22716
|
+
const step = envelope2.plan.steps[stepId];
|
|
22300
22717
|
if (!step || step.kind !== "gate") return void 0;
|
|
22301
|
-
return
|
|
22718
|
+
return envelope2.gates.definitions[step.gate]?.kind;
|
|
22302
22719
|
},
|
|
22303
|
-
projectLimits:
|
|
22720
|
+
projectLimits: envelope2.projectLimits
|
|
22304
22721
|
} : {});
|
|
22305
|
-
if (
|
|
22722
|
+
if (envelope2) verifyKxmGateEvidence(context.eventStore, run, envelope2, events, state);
|
|
22306
22723
|
assertStoredProjection(context, run);
|
|
22307
22724
|
return state;
|
|
22308
22725
|
}
|
|
@@ -22458,7 +22875,7 @@ function cancelKxmRun(context, runId, options = {}) {
|
|
|
22458
22875
|
import { join as join12 } from "node:path";
|
|
22459
22876
|
|
|
22460
22877
|
// plugins/kxm/src/oneshot-evidence.ts
|
|
22461
|
-
import { createHash as
|
|
22878
|
+
import { createHash as createHash7, randomUUID as randomUUID3 } from "node:crypto";
|
|
22462
22879
|
import { lstat, mkdir, open, rename } from "node:fs/promises";
|
|
22463
22880
|
import { join as join7 } from "node:path";
|
|
22464
22881
|
var ONESHOT_EVIDENCE_SCHEMA = "kxm.oneshot-evidence.v2";
|
|
@@ -22466,7 +22883,7 @@ var RETIRED_ONESHOT_EVIDENCE_SCHEMAS = /* @__PURE__ */ new Set(["kxm.oneshot-evi
|
|
|
22466
22883
|
var TEXT_LIMIT = 4 * 1024 * 1024;
|
|
22467
22884
|
var ARGV_LIMIT = 64 * 1024;
|
|
22468
22885
|
var RECORD_LIMIT = 16 * 1024 * 1024;
|
|
22469
|
-
var digest = (value) => `sha256:${
|
|
22886
|
+
var digest = (value) => `sha256:${createHash7("sha256").update(value).digest("hex")}`;
|
|
22470
22887
|
function parseOneShotEvidenceRecord(bytes) {
|
|
22471
22888
|
let parsed;
|
|
22472
22889
|
try {
|
|
@@ -22576,7 +22993,7 @@ async function beginOneShotEvidence(root, intent, sensitive) {
|
|
|
22576
22993
|
|
|
22577
22994
|
// plugins/kxm/src/prices.ts
|
|
22578
22995
|
var import_yaml3 = __toESM(require_dist(), 1);
|
|
22579
|
-
import { createHash as
|
|
22996
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
22580
22997
|
import { existsSync as existsSync7, readFileSync as readFileSync5, statSync } from "node:fs";
|
|
22581
22998
|
import { join as join8 } from "node:path";
|
|
22582
22999
|
|
|
@@ -22639,7 +23056,7 @@ function hashPriceCatalog(catalog) {
|
|
|
22639
23056
|
}))
|
|
22640
23057
|
})).sort((a, b) => a.id.localeCompare(b.id))
|
|
22641
23058
|
};
|
|
22642
|
-
return
|
|
23059
|
+
return createHash8("sha256").update(JSON.stringify(canonical), "utf8").digest("hex");
|
|
22643
23060
|
}
|
|
22644
23061
|
function parsePriceCatalog(text) {
|
|
22645
23062
|
const parsed = (0, import_yaml3.parse)(text);
|
|
@@ -22740,7 +23157,7 @@ function loadPriceCatalog(rootOrPath) {
|
|
|
22740
23157
|
|
|
22741
23158
|
// plugins/kxm/src/engine.ts
|
|
22742
23159
|
var import_yaml5 = __toESM(require_dist(), 1);
|
|
22743
|
-
import { createHash as
|
|
23160
|
+
import { createHash as createHash11, randomBytes, timingSafeEqual } from "node:crypto";
|
|
22744
23161
|
import { existsSync as existsSync9, readFileSync as readFileSync7 } from "node:fs";
|
|
22745
23162
|
import { join as join11 } from "node:path";
|
|
22746
23163
|
|
|
@@ -23122,7 +23539,7 @@ function evaluateArtifactsGate(context, definition) {
|
|
|
23122
23539
|
|
|
23123
23540
|
// plugins/kxm/src/engine-command.ts
|
|
23124
23541
|
import { spawn as spawn2 } from "node:child_process";
|
|
23125
|
-
import { createHash as
|
|
23542
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
23126
23543
|
|
|
23127
23544
|
// plugins/kxm/src/safety-integrity.ts
|
|
23128
23545
|
var DESTRUCTIVE_COMMAND_PATTERNS = Object.freeze([
|
|
@@ -23159,7 +23576,7 @@ function createCommandObserver(input) {
|
|
|
23159
23576
|
return new CommandObserver(input);
|
|
23160
23577
|
}
|
|
23161
23578
|
var StreamDigest = class {
|
|
23162
|
-
hash =
|
|
23579
|
+
hash = createHash9("sha256");
|
|
23163
23580
|
bytes = 0;
|
|
23164
23581
|
complete = 0;
|
|
23165
23582
|
digest() {
|
|
@@ -23234,8 +23651,8 @@ var CommandObserver = class {
|
|
|
23234
23651
|
this.requestStop("error", "lost-close");
|
|
23235
23652
|
}
|
|
23236
23653
|
run() {
|
|
23237
|
-
return new Promise((
|
|
23238
|
-
this.resolveOutcome =
|
|
23654
|
+
return new Promise((resolve8) => {
|
|
23655
|
+
this.resolveOutcome = resolve8;
|
|
23239
23656
|
try {
|
|
23240
23657
|
this.start();
|
|
23241
23658
|
} catch (error) {
|
|
@@ -23579,9 +23996,9 @@ function schedule(ms, fn) {
|
|
|
23579
23996
|
}
|
|
23580
23997
|
|
|
23581
23998
|
// plugins/kxm/src/engine-gate-records.ts
|
|
23582
|
-
function recordGateIntentInTransaction(context, run,
|
|
23583
|
-
const step = requireGateStep(
|
|
23584
|
-
const definition =
|
|
23999
|
+
function recordGateIntentInTransaction(context, run, envelope2, stepId) {
|
|
24000
|
+
const step = requireGateStep(envelope2, stepId, run.runId);
|
|
24001
|
+
const definition = envelope2.gates.definitions[step.gate];
|
|
23585
24002
|
if (!definition || definition.kind === "reserved") {
|
|
23586
24003
|
throw runtimeError("gate_row_invalid", run.runId, "reserved gates cannot record intent in this slice");
|
|
23587
24004
|
}
|
|
@@ -23634,9 +24051,9 @@ function recordGateIntentInTransaction(context, run, envelope, stepId) {
|
|
|
23634
24051
|
gateKind: definition.kind,
|
|
23635
24052
|
expect: step.expect,
|
|
23636
24053
|
gateDefinitionHash: gateDefinitionHash(step.gate, definition),
|
|
23637
|
-
registryHash:
|
|
23638
|
-
runPlanHash:
|
|
23639
|
-
controlProjectKey:
|
|
24054
|
+
registryHash: envelope2.gates.registry.hash,
|
|
24055
|
+
runPlanHash: envelope2 && folded.runPlanHash ? folded.runPlanHash : hashFromStore(context, run.runId),
|
|
24056
|
+
controlProjectKey: envelope2.gates.controlRoot.projectKey,
|
|
23640
24057
|
producerId: "kxm-gate",
|
|
23641
24058
|
intentEventId: intent.eventId,
|
|
23642
24059
|
contentHash: ""
|
|
@@ -23646,8 +24063,8 @@ function recordGateIntentInTransaction(context, run, envelope, stepId) {
|
|
|
23646
24063
|
context.eventStore.insertGateAttempt(withHash);
|
|
23647
24064
|
return persist(context, run, now, seq.events.at(-1).sequence);
|
|
23648
24065
|
}
|
|
23649
|
-
function recordGateSpawnedInTransaction(context, run,
|
|
23650
|
-
const ids = requireIntent(context, run,
|
|
24066
|
+
function recordGateSpawnedInTransaction(context, run, envelope2, stepId) {
|
|
24067
|
+
const ids = requireIntent(context, run, envelope2, stepId);
|
|
23651
24068
|
if (ids.gateKind !== "command") throw runtimeError("gate_row_invalid", run.runId, "effect.dispatched is command-only");
|
|
23652
24069
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
23653
24070
|
const seq = sequencer(context, run, now);
|
|
@@ -23663,8 +24080,8 @@ function recordGateSpawnedInTransaction(context, run, envelope, stepId) {
|
|
|
23663
24080
|
for (const event of seq.events) context.eventStore.appendEvent(event);
|
|
23664
24081
|
return persist(context, run, now, seq.events.at(-1).sequence);
|
|
23665
24082
|
}
|
|
23666
|
-
function recordGateSettlementInTransaction(context, run,
|
|
23667
|
-
const ids = requireIntent(context, run,
|
|
24083
|
+
function recordGateSettlementInTransaction(context, run, envelope2, stepId, observation) {
|
|
24084
|
+
const ids = requireIntent(context, run, envelope2, stepId);
|
|
23668
24085
|
assertClosedGateObservation(ids.gateKind, observation);
|
|
23669
24086
|
const outcome = computeGateEvidenceOutcome(ids.gateKind, ids.expect, observation);
|
|
23670
24087
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -23704,7 +24121,7 @@ function recordGateSettlementInTransaction(context, run, envelope, stepId, obser
|
|
|
23704
24121
|
stepAttempt: ids.stepAttempt,
|
|
23705
24122
|
assignmentId: ids.assignmentId,
|
|
23706
24123
|
effectId: ids.effectId,
|
|
23707
|
-
evidenceKey: evidenceKeyFor(
|
|
24124
|
+
evidenceKey: evidenceKeyFor(envelope2, stepId),
|
|
23708
24125
|
kind: "gate",
|
|
23709
24126
|
expect: ids.expect,
|
|
23710
24127
|
outcome,
|
|
@@ -23723,15 +24140,15 @@ function recordGateSettlementInTransaction(context, run, envelope, stepId, obser
|
|
|
23723
24140
|
evidenceDraft.settledEventId = settled.eventId;
|
|
23724
24141
|
evidenceDraft.contentHash = gateRowContentHash("gate_evidence", { ...evidenceDraft, contentHash: "" });
|
|
23725
24142
|
settled.payload.evidenceRefs[0].hash = evidenceDraft.contentHash;
|
|
23726
|
-
appendEvaluatedTail(seq,
|
|
24143
|
+
appendEvaluatedTail(seq, envelope2, ids, stepId, outcome);
|
|
23727
24144
|
for (const event of seq.events) context.eventStore.appendEvent(event);
|
|
23728
24145
|
context.eventStore.insertGateObservation(observationRow);
|
|
23729
24146
|
context.eventStore.insertGateEvidence(evidenceDraft);
|
|
23730
24147
|
context.eventStore.settleCapability(ids.attemptId, "settled");
|
|
23731
24148
|
return persist(context, run, now, seq.events.at(-1).sequence);
|
|
23732
24149
|
}
|
|
23733
|
-
function recordGateNoStartInTransaction(context, run,
|
|
23734
|
-
const ids = requireIntent(context, run,
|
|
24150
|
+
function recordGateNoStartInTransaction(context, run, envelope2, stepId, observation) {
|
|
24151
|
+
const ids = requireIntent(context, run, envelope2, stepId);
|
|
23735
24152
|
assertClosedGateObservation(ids.gateKind, observation);
|
|
23736
24153
|
if (observation.completeness !== "no-start") {
|
|
23737
24154
|
throw runtimeError("gate_row_invalid", run.runId, "no-start proof requires spawned 0 and completeness no-start");
|
|
@@ -23784,8 +24201,8 @@ function recordGateNoStartInTransaction(context, run, envelope, stepId, observat
|
|
|
23784
24201
|
context.eventStore.settleCapability(ids.attemptId, "settled");
|
|
23785
24202
|
return persist(context, run, now, seq.events.at(-1).sequence);
|
|
23786
24203
|
}
|
|
23787
|
-
function recordGateUncertainInTransaction(context, run,
|
|
23788
|
-
const ids = requireIntent(context, run,
|
|
24204
|
+
function recordGateUncertainInTransaction(context, run, envelope2, stepId, reason, observation) {
|
|
24205
|
+
const ids = requireIntent(context, run, envelope2, stepId);
|
|
23789
24206
|
if (observation.completeness !== "incomplete") {
|
|
23790
24207
|
throw runtimeError("gate_row_invalid", run.runId, "uncertainty observation must be incomplete");
|
|
23791
24208
|
}
|
|
@@ -23814,8 +24231,8 @@ function recordGateUncertainInTransaction(context, run, envelope, stepId, reason
|
|
|
23814
24231
|
context.eventStore.insertGateObservation(observationRow);
|
|
23815
24232
|
return persist(context, run, now, seq.events.at(-1).sequence);
|
|
23816
24233
|
}
|
|
23817
|
-
function recordGateCancelObservedInTransaction(context, run,
|
|
23818
|
-
const ids = requireIntent(context, run,
|
|
24234
|
+
function recordGateCancelObservedInTransaction(context, run, envelope2, stepId, observation) {
|
|
24235
|
+
const ids = requireIntent(context, run, envelope2, stepId);
|
|
23819
24236
|
assertClosedGateObservation(ids.gateKind, observation);
|
|
23820
24237
|
if (observation.completeness !== "complete") {
|
|
23821
24238
|
throw runtimeError("gate_row_invalid", run.runId, "cancel-after-observation requires a complete observation");
|
|
@@ -23874,19 +24291,19 @@ function recordGateCancelObservedInTransaction(context, run, envelope, stepId, o
|
|
|
23874
24291
|
context.eventStore.settleCapability(ids.attemptId, "settled");
|
|
23875
24292
|
return persist(context, run, now, seq.events.at(-1).sequence);
|
|
23876
24293
|
}
|
|
23877
|
-
function appendEvaluatedTail(seq,
|
|
24294
|
+
function appendEvaluatedTail(seq, envelope2, ids, stepId, outcome) {
|
|
23878
24295
|
const mapped = outcome === "passed" ? "passed" : outcome === "repro-missing" ? "failed" : "failed";
|
|
23879
24296
|
seq.push("assignment.result_recorded", { assignmentId: ids.assignmentId, resultClass: "outcome", outcome, status: "result_recorded" });
|
|
23880
24297
|
seq.push("attempt.status_changed", { attemptId: ids.attemptId, status: "terminal" });
|
|
23881
24298
|
seq.push("assignment.terminal", { assignmentId: ids.assignmentId, outcome, status: "terminal" });
|
|
23882
24299
|
seq.push("step.outcome_recorded", { stepId, stepAttempt: ids.stepAttempt, outcome });
|
|
23883
24300
|
seq.push("step.status_changed", { stepId, status: mapped, previousStatus: "running" });
|
|
23884
|
-
const selected =
|
|
24301
|
+
const selected = envelope2.plan.steps[stepId].transitions[outcome];
|
|
23885
24302
|
if (!selected) {
|
|
23886
24303
|
seq.push("run.status_changed", { status: "failed", reason: "outcome_unknown", stepId, outcome });
|
|
23887
24304
|
return;
|
|
23888
24305
|
}
|
|
23889
|
-
const budget = kxmTransitionBudgetFailure(
|
|
24306
|
+
const budget = kxmTransitionBudgetFailure(envelope2.plan, ids.state, stepId, outcome);
|
|
23890
24307
|
if (budget) {
|
|
23891
24308
|
seq.push("run.status_changed", { status: "failed", reason: budget, stepId, outcome });
|
|
23892
24309
|
return;
|
|
@@ -23898,8 +24315,8 @@ function appendEvaluatedTail(seq, envelope, ids, stepId, outcome) {
|
|
|
23898
24315
|
seq.push("run.status_changed", { status: selected.terminalStatus });
|
|
23899
24316
|
}
|
|
23900
24317
|
}
|
|
23901
|
-
function evidenceKeyFor(
|
|
23902
|
-
const required =
|
|
24318
|
+
function evidenceKeyFor(envelope2, stepId) {
|
|
24319
|
+
const required = envelope2.plan.steps[stepId]?.requiredEvidence.filter((item) => item.kind === "gate") ?? [];
|
|
23903
24320
|
return required.length === 1 ? required[0].key : null;
|
|
23904
24321
|
}
|
|
23905
24322
|
function placeholderReceipt(observationId, kind, now) {
|
|
@@ -23932,13 +24349,13 @@ function buildObservation(ids, observation, observationId, recordedEventId) {
|
|
|
23932
24349
|
};
|
|
23933
24350
|
return { ...row, contentHash: gateRowContentHash("gate_observations", { ...row, contentHash: "" }) };
|
|
23934
24351
|
}
|
|
23935
|
-
function requireGateStep(
|
|
23936
|
-
const step =
|
|
24352
|
+
function requireGateStep(envelope2, stepId, runId) {
|
|
24353
|
+
const step = envelope2.plan.steps[stepId];
|
|
23937
24354
|
if (!step || step.kind !== "gate") throw runtimeError("run_events_illegal", runId, `step ${stepId} is not a gate`);
|
|
23938
24355
|
return step;
|
|
23939
24356
|
}
|
|
23940
|
-
function requireIntent(context, run,
|
|
23941
|
-
const step = requireGateStep(
|
|
24357
|
+
function requireIntent(context, run, envelope2, stepId) {
|
|
24358
|
+
const step = requireGateStep(envelope2, stepId, run.runId);
|
|
23942
24359
|
const folded = foldStoredKxmRun(context, run);
|
|
23943
24360
|
const attemptId = folded.currentStep?.attemptId;
|
|
23944
24361
|
if (!attemptId || folded.currentStep?.stepId !== stepId) {
|
|
@@ -24006,7 +24423,7 @@ function hashFromStore(context, runId) {
|
|
|
24006
24423
|
}
|
|
24007
24424
|
|
|
24008
24425
|
// plugins/kxm/src/routing.ts
|
|
24009
|
-
import { createHash as
|
|
24426
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
24010
24427
|
var ROUTING_RECORD_V2_SCHEMA = "kxm.routing-record.v2";
|
|
24011
24428
|
var BEHAVIORAL_HASH_VERSION = 1;
|
|
24012
24429
|
var MAX_PROVIDER_METADATA_FIELDS = 32;
|
|
@@ -24029,7 +24446,7 @@ function behavioralConfigHash(input) {
|
|
|
24029
24446
|
workflowDefinitionSha256: input.workflowDefinitionSha256?.trim() ?? null,
|
|
24030
24447
|
verifierConfigSha256: input.verifierConfigSha256?.trim() ?? null
|
|
24031
24448
|
};
|
|
24032
|
-
return
|
|
24449
|
+
return createHash10("sha256").update(JSON.stringify(canonical), "utf8").digest("hex");
|
|
24033
24450
|
}
|
|
24034
24451
|
function boundedInt(value, field) {
|
|
24035
24452
|
if (value === void 0 || value === null) return void 0;
|
|
@@ -24198,7 +24615,7 @@ function requireTrustedProducer(producer) {
|
|
|
24198
24615
|
}
|
|
24199
24616
|
}
|
|
24200
24617
|
function hashCapabilitySecret(secret) {
|
|
24201
|
-
return `sha256:${
|
|
24618
|
+
return `sha256:${createHash11("sha256").update(`${CAPABILITY_PREFIX}${secret}`, "utf8").digest("hex")}`;
|
|
24202
24619
|
}
|
|
24203
24620
|
function mintKxmCapabilitySecret() {
|
|
24204
24621
|
const secret = `kxmcap_${randomBytes(32).toString("base64url")}`;
|
|
@@ -24227,7 +24644,7 @@ function pinKxmCompiledPlan(context, bundle, runId, options) {
|
|
|
24227
24644
|
value: workflow.value,
|
|
24228
24645
|
...workflow.logicalPath !== void 0 ? { logicalPath: workflow.logicalPath } : {}
|
|
24229
24646
|
}));
|
|
24230
|
-
const
|
|
24647
|
+
const envelope2 = {
|
|
24231
24648
|
schema: KXM_RUN_PLAN_SCHEMA,
|
|
24232
24649
|
runId: run.runId,
|
|
24233
24650
|
projectId: run.projectId,
|
|
@@ -24243,7 +24660,7 @@ function pinKxmCompiledPlan(context, bundle, runId, options) {
|
|
|
24243
24660
|
plan: compiled,
|
|
24244
24661
|
gates: pinnedGatesForCompiledPlan(compiled, bundle, context.projectRoot)
|
|
24245
24662
|
};
|
|
24246
|
-
const runPlanHash = hashKxmRunPlanEnvelope(
|
|
24663
|
+
const runPlanHash = hashKxmRunPlanEnvelope(envelope2);
|
|
24247
24664
|
const existing = context.eventStore.runPlan(runId);
|
|
24248
24665
|
if (existing) {
|
|
24249
24666
|
if (existing.runPlanHash !== runPlanHash) {
|
|
@@ -24266,7 +24683,7 @@ function pinKxmCompiledPlan(context, bundle, runId, options) {
|
|
|
24266
24683
|
context.eventStore.insertRunPlan({
|
|
24267
24684
|
runId,
|
|
24268
24685
|
runPlanHash,
|
|
24269
|
-
envelope: kxmCanonicalJson(
|
|
24686
|
+
envelope: kxmCanonicalJson(envelope2),
|
|
24270
24687
|
pinnedSequence: sequence
|
|
24271
24688
|
});
|
|
24272
24689
|
context.eventStore.appendEvent(event);
|
|
@@ -24279,8 +24696,8 @@ function pinKxmCompiledPlan(context, bundle, runId, options) {
|
|
|
24279
24696
|
function startKxmRun(context, runId, options = {}) {
|
|
24280
24697
|
return context.eventStore.transaction(() => {
|
|
24281
24698
|
const run = requireRun(context, runId);
|
|
24282
|
-
const
|
|
24283
|
-
const unsupported = options.allowLimits ? void 0 : unsupportedLimit(
|
|
24699
|
+
const envelope2 = loadKxmRunPlanEnvelope(context.eventStore, run);
|
|
24700
|
+
const unsupported = options.allowLimits ? void 0 : unsupportedLimit(envelope2);
|
|
24284
24701
|
const state = foldStoredKxmRun(context, run);
|
|
24285
24702
|
if (unsupported) {
|
|
24286
24703
|
return { state, handoff: unsupported };
|
|
@@ -24327,7 +24744,7 @@ async function driveAdmitted(context, runId, producer, token, options = {}) {
|
|
|
24327
24744
|
return latest;
|
|
24328
24745
|
}
|
|
24329
24746
|
function newDriveId(runId, token, runtimeId, monotonicNs) {
|
|
24330
|
-
return `drv_${
|
|
24747
|
+
return `drv_${createHash11("sha256").update(`${runId}\0${token}\0${runtimeId}\0${monotonicNs}`, "utf8").digest("hex").slice(0, 24)}`;
|
|
24331
24748
|
}
|
|
24332
24749
|
function driveErrorSummary(error) {
|
|
24333
24750
|
const raw = error instanceof KxmConfigError ? error.issues[0]?.code : void 0;
|
|
@@ -24519,12 +24936,12 @@ var KxmRunScheduler = class _KxmRunScheduler {
|
|
|
24519
24936
|
}
|
|
24520
24937
|
try {
|
|
24521
24938
|
const run = requireRun(this.context, runId);
|
|
24522
|
-
const
|
|
24939
|
+
const envelope2 = loadKxmRunPlanEnvelope(this.context.eventStore, run);
|
|
24523
24940
|
return enqueueKxmScheduledRun(
|
|
24524
24941
|
this.context.eventStore.path,
|
|
24525
24942
|
runId,
|
|
24526
|
-
|
|
24527
|
-
|
|
24943
|
+
envelope2.revisions.config,
|
|
24944
|
+
envelope2.projectLimits.maxConcurrentRuns,
|
|
24528
24945
|
(token) => driveAdmitted(this.context, runId, producer, token, options)
|
|
24529
24946
|
);
|
|
24530
24947
|
} catch (error) {
|
|
@@ -24554,8 +24971,8 @@ var KxmRunScheduler = class _KxmRunScheduler {
|
|
|
24554
24971
|
resolve: (_value) => void 0,
|
|
24555
24972
|
reject: (_error) => void 0
|
|
24556
24973
|
};
|
|
24557
|
-
const settled = new Promise((
|
|
24558
|
-
pending.resolve =
|
|
24974
|
+
const settled = new Promise((resolve8, reject) => {
|
|
24975
|
+
pending.resolve = resolve8;
|
|
24559
24976
|
pending.reject = reject;
|
|
24560
24977
|
});
|
|
24561
24978
|
void settled.then(void 0, () => void 0);
|
|
@@ -25094,10 +25511,10 @@ function prepareDispatch(context, runId, producerId) {
|
|
|
25094
25511
|
return { kind: "return", ...failBudget(context, run, plan, state, "budget_unmetered_attempts", stepId) };
|
|
25095
25512
|
}
|
|
25096
25513
|
if (step.kind === "gate") {
|
|
25097
|
-
const
|
|
25098
|
-
const unsupported2 = unsupportedGateStep(plan, step,
|
|
25514
|
+
const envelope2 = loadKxmRunPlanEnvelope(context.eventStore, run);
|
|
25515
|
+
const unsupported2 = unsupportedGateStep(plan, step, envelope2, context);
|
|
25099
25516
|
if (unsupported2) return { kind: "return", state, handoff: { ...unsupported2, stepId } };
|
|
25100
|
-
const definition =
|
|
25517
|
+
const definition = envelope2.gates.definitions[step.gate];
|
|
25101
25518
|
if (definition?.kind === "command") {
|
|
25102
25519
|
const preflightResult = gateRecoveryPreflight(context);
|
|
25103
25520
|
const firstBlocking = preflightResult.blocking[0];
|
|
@@ -25113,7 +25530,7 @@ function prepareDispatch(context, runId, producerId) {
|
|
|
25113
25530
|
};
|
|
25114
25531
|
}
|
|
25115
25532
|
}
|
|
25116
|
-
const result = recordGateIntentInTransaction(context, run,
|
|
25533
|
+
const result = recordGateIntentInTransaction(context, run, envelope2, stepId);
|
|
25117
25534
|
const current = result.state.currentStep;
|
|
25118
25535
|
const attemptId = current?.attemptId;
|
|
25119
25536
|
const assignmentId = current?.assignmentId;
|
|
@@ -25124,7 +25541,7 @@ function prepareDispatch(context, runId, producerId) {
|
|
|
25124
25541
|
return {
|
|
25125
25542
|
kind: "gate",
|
|
25126
25543
|
run,
|
|
25127
|
-
envelope,
|
|
25544
|
+
envelope: envelope2,
|
|
25128
25545
|
stepId,
|
|
25129
25546
|
stepAttempt: current.stepAttempt,
|
|
25130
25547
|
assignmentId,
|
|
@@ -25230,7 +25647,7 @@ function birthMember(context, input) {
|
|
|
25230
25647
|
if (promptText === void 0) {
|
|
25231
25648
|
throw runtimeError("run_prompt_mismatch", run.runId, "prompt text missing from accepted run prompt store");
|
|
25232
25649
|
}
|
|
25233
|
-
const promptHash =
|
|
25650
|
+
const promptHash = createHash11("sha256").update(promptText, "utf8").digest("hex");
|
|
25234
25651
|
const expectedHash = run.promptSha256.replace(/^sha256:/, "");
|
|
25235
25652
|
if (promptHash !== expectedHash) {
|
|
25236
25653
|
throw runtimeError("run_prompt_mismatch", run.runId, "prompt text does not match accepted promptSha256");
|
|
@@ -25608,12 +26025,12 @@ function producerRoutingRecord(context, dispatch, result, now) {
|
|
|
25608
26025
|
if (result.costBasis === "metered" && (typeof result.costUsd !== "number" || !Number.isFinite(result.costUsd) || result.costUsd < 0)) {
|
|
25609
26026
|
throw runtimeError("settle_invalid_cost", run.runId, "attempt settlement rejected: metered costBasis requires non-negative finite costUsd");
|
|
25610
26027
|
}
|
|
25611
|
-
const
|
|
26028
|
+
const envelope2 = loadKxmRunPlanEnvelope(context.eventStore, run);
|
|
25612
26029
|
const rawHash = result.behavioralSha256 ?? behavioralConfigHash({
|
|
25613
26030
|
requestedModel: result.requestedModel ?? "simulated",
|
|
25614
26031
|
effectiveModel: result.effectiveModel ?? result.requestedModel ?? "simulated",
|
|
25615
26032
|
agentRole: dispatch.step.kind === "agent" || dispatch.step.kind === "moa" ? dispatch.step.agent : void 0,
|
|
25616
|
-
toolPolicyVersion:
|
|
26033
|
+
toolPolicyVersion: envelope2.revisions.toolPolicy
|
|
25617
26034
|
});
|
|
25618
26035
|
const record2 = {
|
|
25619
26036
|
schema: ROUTING_RECORD_V2_SCHEMA,
|
|
@@ -25674,12 +26091,12 @@ function settleMember(context, dispatch, result, produceError, produceErrorCode)
|
|
|
25674
26091
|
push("attempt.status_changed", { attemptId: dispatch.attemptId, status: "terminal" });
|
|
25675
26092
|
push("assignment.terminal", { assignmentId: dispatch.assignmentId, outcome: "cancelled", status: "terminal" });
|
|
25676
26093
|
} else if (produceError) {
|
|
25677
|
-
const
|
|
26094
|
+
const envelope2 = loadKxmRunPlanEnvelope(context.eventStore, run);
|
|
25678
26095
|
const rawHash = result?.behavioralSha256 ?? behavioralConfigHash({
|
|
25679
26096
|
requestedModel: result?.requestedModel ?? "simulated",
|
|
25680
26097
|
effectiveModel: result?.effectiveModel ?? result?.requestedModel ?? "simulated",
|
|
25681
26098
|
agentRole: dispatch.step.kind === "agent" || dispatch.step.kind === "moa" ? dispatch.step.agent : void 0,
|
|
25682
|
-
toolPolicyVersion:
|
|
26099
|
+
toolPolicyVersion: envelope2.revisions.toolPolicy
|
|
25683
26100
|
});
|
|
25684
26101
|
const behavioralSha256 = rawHash.startsWith("sha256:") ? rawHash : `sha256:${rawHash}`;
|
|
25685
26102
|
const routingRecord = {
|
|
@@ -25894,8 +26311,8 @@ function declaredRunDurationBudget(context, runId) {
|
|
|
25894
26311
|
const run = context.eventStore.run(runId);
|
|
25895
26312
|
if (!run) return void 0;
|
|
25896
26313
|
try {
|
|
25897
|
-
const
|
|
25898
|
-
return effectiveRunDurationBudget(
|
|
26314
|
+
const envelope2 = loadKxmRunPlanEnvelope(context.eventStore, run);
|
|
26315
|
+
return effectiveRunDurationBudget(envelope2.plan.limits, envelope2.projectLimits);
|
|
25899
26316
|
} catch {
|
|
25900
26317
|
return void 0;
|
|
25901
26318
|
}
|
|
@@ -26001,11 +26418,11 @@ function armRunDurationBudgetTimer(context, runId) {
|
|
|
26001
26418
|
unhook();
|
|
26002
26419
|
};
|
|
26003
26420
|
}
|
|
26004
|
-
function unsupportedLimit(
|
|
26005
|
-
if (
|
|
26421
|
+
function unsupportedLimit(envelope2) {
|
|
26422
|
+
if (envelope2.plan.limits.maxAgentTimeMs !== void 0) {
|
|
26006
26423
|
return { reason: "limit_unsupported", field: "limits.maxAgentTimeMs", detail: "agent-time budget enforcement is not available in this slice" };
|
|
26007
26424
|
}
|
|
26008
|
-
if (
|
|
26425
|
+
if (envelope2.projectLimits.maxAgentTimeMs !== void 0) {
|
|
26009
26426
|
return { reason: "limit_unsupported", field: "project.limits.maxAgentTimeMs", detail: "agent-time budget enforcement is not available in this slice" };
|
|
26010
26427
|
}
|
|
26011
26428
|
return void 0;
|
|
@@ -26058,12 +26475,12 @@ function unsupportedStep(plan, step, producerId) {
|
|
|
26058
26475
|
}
|
|
26059
26476
|
return void 0;
|
|
26060
26477
|
}
|
|
26061
|
-
function unsupportedGateStep(plan, step,
|
|
26478
|
+
function unsupportedGateStep(plan, step, envelope2, context) {
|
|
26062
26479
|
if (!step.gate) return { reason: "step_unsupported", field: "gate", detail: "gate step is missing gate id" };
|
|
26063
|
-
if (
|
|
26480
|
+
if (envelope2.gates.registry === null) {
|
|
26064
26481
|
return { reason: "step_unsupported", field: "gate", detail: `step ${step.id} refers to a gate but registry is null` };
|
|
26065
26482
|
}
|
|
26066
|
-
const definition =
|
|
26483
|
+
const definition = envelope2.gates.definitions[step.gate];
|
|
26067
26484
|
if (!definition) {
|
|
26068
26485
|
return { reason: "step_unsupported", field: "gate", detail: `gate definition ${step.gate} not found in registry` };
|
|
26069
26486
|
}
|
|
@@ -26169,7 +26586,7 @@ function unsupportedGateStep(plan, step, envelope, context) {
|
|
|
26169
26586
|
return { reason: "gate_outcome_undeclared", field: "outcomes", detail: "step expects fail but missing required failure outcome; declare implementation-failure or failed" };
|
|
26170
26587
|
}
|
|
26171
26588
|
}
|
|
26172
|
-
if (context && projectRuntimeKey(context.projectRoot) !==
|
|
26589
|
+
if (context && projectRuntimeKey(context.projectRoot) !== envelope2.gates.controlRoot.projectKey) {
|
|
26173
26590
|
throw runtimeError("run_owner_mismatch", plan.workflowId, `gate control root does not match project`);
|
|
26174
26591
|
}
|
|
26175
26592
|
if (definition.kind === "artifacts-exist" && step.expect === "fail") {
|
|
@@ -26808,28 +27225,290 @@ Return a final JSON object with an "outcome" field chosen from ${JSON.stringify(
|
|
|
26808
27225
|
return producer;
|
|
26809
27226
|
}
|
|
26810
27227
|
|
|
27228
|
+
// plugins/kxm/src/client.ts
|
|
27229
|
+
import { hostname } from "node:os";
|
|
27230
|
+
|
|
27231
|
+
// plugins/kxm/src/protocol.ts
|
|
27232
|
+
var DEFAULT_MESSAGE_TTL_MS = 24 * 60 * 6e4;
|
|
27233
|
+
var MAX_MESSAGE_TTL_MS = 7 * 24 * 60 * 6e4;
|
|
27234
|
+
var DEFAULT_MESSAGE_RETENTION_MS = 7 * 24 * 60 * 6e4;
|
|
27235
|
+
var MAX_BODY_BYTES = 256 * 1024;
|
|
27236
|
+
var MAX_AGENT_HOST_CHARS = 64;
|
|
27237
|
+
var MAX_LEASE_TTL_MS = 10 * 6e4;
|
|
27238
|
+
var DEFAULT_LEASE_TTL_MS = 5 * 6e4;
|
|
27239
|
+
|
|
27240
|
+
// plugins/kxm/src/client.ts
|
|
27241
|
+
function defaultHostLabel() {
|
|
27242
|
+
try {
|
|
27243
|
+
return hostname().trim().slice(0, MAX_AGENT_HOST_CHARS) || void 0;
|
|
27244
|
+
} catch {
|
|
27245
|
+
return void 0;
|
|
27246
|
+
}
|
|
27247
|
+
}
|
|
27248
|
+
var HubHttpError = class extends Error {
|
|
27249
|
+
statusCode;
|
|
27250
|
+
code;
|
|
27251
|
+
requestId;
|
|
27252
|
+
extras;
|
|
27253
|
+
constructor(statusCode, message, code, requestId, extras) {
|
|
27254
|
+
super(message);
|
|
27255
|
+
this.name = "HubHttpError";
|
|
27256
|
+
this.statusCode = statusCode;
|
|
27257
|
+
if (code) this.code = code;
|
|
27258
|
+
if (requestId) this.requestId = requestId;
|
|
27259
|
+
if (extras) this.extras = extras;
|
|
27260
|
+
}
|
|
27261
|
+
};
|
|
27262
|
+
async function hubJsonRequest(options, path, init, headers) {
|
|
27263
|
+
const requestTimeoutMs = options.requestTimeoutMs ?? 15e3;
|
|
27264
|
+
const timeoutSignal = AbortSignal.timeout(requestTimeoutMs);
|
|
27265
|
+
const signal = init.signal ? AbortSignal.any([init.signal, timeoutSignal]) : timeoutSignal;
|
|
27266
|
+
let response;
|
|
27267
|
+
try {
|
|
27268
|
+
response = await (options.fetchImpl ?? fetch)(`${options.serverUrl.replace(/\/$/, "")}${path}`, {
|
|
27269
|
+
...init,
|
|
27270
|
+
signal,
|
|
27271
|
+
headers: { ...headers, ...init.headers ?? {} }
|
|
27272
|
+
});
|
|
27273
|
+
} catch (error) {
|
|
27274
|
+
if (timeoutSignal.aborted) throw new Error(`request timed out after ${requestTimeoutMs}ms`);
|
|
27275
|
+
throw error;
|
|
27276
|
+
}
|
|
27277
|
+
const text = await response.text();
|
|
27278
|
+
let body = {};
|
|
27279
|
+
if (text) {
|
|
27280
|
+
try {
|
|
27281
|
+
body = JSON.parse(text);
|
|
27282
|
+
} catch {
|
|
27283
|
+
throw new Error(`hub returned invalid JSON with HTTP ${response.status}`);
|
|
27284
|
+
}
|
|
27285
|
+
}
|
|
27286
|
+
if (!response.ok) {
|
|
27287
|
+
const extras = {};
|
|
27288
|
+
for (const key of ["operation", "nextAction", "assignedCoordinatorName"]) {
|
|
27289
|
+
if (typeof body[key] === "string") extras[key] = body[key];
|
|
27290
|
+
}
|
|
27291
|
+
if (body.lease && typeof body.lease === "object") extras.lease = body.lease;
|
|
27292
|
+
throw new HubHttpError(
|
|
27293
|
+
response.status,
|
|
27294
|
+
String(body.error ?? `HTTP ${response.status}`),
|
|
27295
|
+
typeof body.code === "string" ? body.code : void 0,
|
|
27296
|
+
response.headers.get("x-request-id") ?? void 0,
|
|
27297
|
+
Object.keys(extras).length > 0 ? extras : void 0
|
|
27298
|
+
);
|
|
27299
|
+
}
|
|
27300
|
+
return body;
|
|
27301
|
+
}
|
|
27302
|
+
var RuntimeHubClient = class {
|
|
27303
|
+
options;
|
|
27304
|
+
constructor(options) {
|
|
27305
|
+
this.options = options;
|
|
27306
|
+
}
|
|
27307
|
+
async heartbeat() {
|
|
27308
|
+
const result = await this.request("/v1/runtime/presence", {
|
|
27309
|
+
project: this.options.project,
|
|
27310
|
+
runtimeId: this.options.runtimeId,
|
|
27311
|
+
host: this.options.host ?? defaultHostLabel()
|
|
27312
|
+
});
|
|
27313
|
+
return result.presence;
|
|
27314
|
+
}
|
|
27315
|
+
/** Push already-derived `kxm.sync-event.v1` objects, in outbox order. */
|
|
27316
|
+
async pushSyncEvents(events) {
|
|
27317
|
+
return await this.request("/v1/sync/events", {
|
|
27318
|
+
project: this.options.project,
|
|
27319
|
+
runtimeId: this.options.runtimeId,
|
|
27320
|
+
events
|
|
27321
|
+
});
|
|
27322
|
+
}
|
|
27323
|
+
async request(path, body) {
|
|
27324
|
+
const headers = { "content-type": "application/json" };
|
|
27325
|
+
if (this.options.authToken) headers.authorization = `Bearer ${this.options.authToken}`;
|
|
27326
|
+
return await hubJsonRequest(this.options, path, { method: "POST", body: JSON.stringify(body) }, headers);
|
|
27327
|
+
}
|
|
27328
|
+
};
|
|
27329
|
+
|
|
27330
|
+
// plugins/kxm/src/hub-binding.ts
|
|
27331
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync4, readFileSync as readFileSync8, renameSync, rmSync, writeFileSync as writeFileSync4 } from "node:fs";
|
|
27332
|
+
import { homedir as homedir2 } from "node:os";
|
|
27333
|
+
import { dirname as dirname6, isAbsolute as isAbsolute4, join as join13, resolve as resolve6 } from "node:path";
|
|
27334
|
+
var HUB_BINDING_SCHEMA = "kxm.hub-binding.v1";
|
|
27335
|
+
var HubBindingError = class extends Error {
|
|
27336
|
+
constructor(message) {
|
|
27337
|
+
super(message);
|
|
27338
|
+
this.name = "HubBindingError";
|
|
27339
|
+
}
|
|
27340
|
+
};
|
|
27341
|
+
function resolveUserStateRoot(env) {
|
|
27342
|
+
const explicit = env.KXM_STATE_HOME?.trim();
|
|
27343
|
+
if (explicit) {
|
|
27344
|
+
if (!isAbsolute4(explicit)) throw new HubBindingError("local_state_root_not_absolute");
|
|
27345
|
+
return resolve6(explicit);
|
|
27346
|
+
}
|
|
27347
|
+
if (process.platform === "win32") {
|
|
27348
|
+
const localAppData = env.LOCALAPPDATA?.trim();
|
|
27349
|
+
const base2 = localAppData && isAbsolute4(localAppData) ? localAppData : join13(homedir2(), "AppData", "Local");
|
|
27350
|
+
return resolve6(base2, "KXM");
|
|
27351
|
+
}
|
|
27352
|
+
if (process.platform === "darwin") return resolve6(homedir2(), "Library", "Application Support", "KXM");
|
|
27353
|
+
const xdgState = env.XDG_STATE_HOME?.trim();
|
|
27354
|
+
const base = xdgState && isAbsolute4(xdgState) ? xdgState : join13(homedir2(), ".local", "state");
|
|
27355
|
+
return resolve6(base, "kxm");
|
|
27356
|
+
}
|
|
27357
|
+
function hubBindingFile(env = process.env) {
|
|
27358
|
+
return join13(resolveUserStateRoot(env), "hub-binding.json");
|
|
27359
|
+
}
|
|
27360
|
+
function validateHubUrl(raw) {
|
|
27361
|
+
let parsed;
|
|
27362
|
+
try {
|
|
27363
|
+
parsed = new URL(raw);
|
|
27364
|
+
} catch {
|
|
27365
|
+
throw new HubBindingError("hub_url_invalid");
|
|
27366
|
+
}
|
|
27367
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:" || parsed.username !== "" || parsed.password !== "" || parsed.search !== "" || parsed.hash !== "" || raw.includes("?") || raw.includes("#")) {
|
|
27368
|
+
throw new HubBindingError("hub_url_invalid");
|
|
27369
|
+
}
|
|
27370
|
+
return parsed.href.replace(/\/$/, "");
|
|
27371
|
+
}
|
|
27372
|
+
function isIsoTimestamp(value) {
|
|
27373
|
+
if (Number.isNaN(Date.parse(value))) return false;
|
|
27374
|
+
return value === new Date(value).toISOString();
|
|
27375
|
+
}
|
|
27376
|
+
function readHubBinding(env = process.env) {
|
|
27377
|
+
const file = hubBindingFile(env);
|
|
27378
|
+
if (!existsSync10(file)) return void 0;
|
|
27379
|
+
let parsed;
|
|
27380
|
+
try {
|
|
27381
|
+
parsed = JSON.parse(readFileSync8(file, "utf8"));
|
|
27382
|
+
} catch {
|
|
27383
|
+
throw new HubBindingError(`malformed hub binding at ${file}`);
|
|
27384
|
+
}
|
|
27385
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
27386
|
+
throw new HubBindingError(`malformed hub binding at ${file}`);
|
|
27387
|
+
}
|
|
27388
|
+
const row = parsed;
|
|
27389
|
+
const keys = Object.keys(row);
|
|
27390
|
+
if (keys.length !== 3 || row.schema !== HUB_BINDING_SCHEMA || typeof row.url !== "string" || typeof row.boundAt !== "string" || !isIsoTimestamp(row.boundAt)) {
|
|
27391
|
+
throw new HubBindingError(`malformed hub binding at ${file}`);
|
|
27392
|
+
}
|
|
27393
|
+
let url;
|
|
27394
|
+
try {
|
|
27395
|
+
url = validateHubUrl(row.url);
|
|
27396
|
+
} catch {
|
|
27397
|
+
throw new HubBindingError(`malformed hub binding at ${file}`);
|
|
27398
|
+
}
|
|
27399
|
+
return { schema: HUB_BINDING_SCHEMA, url, boundAt: row.boundAt };
|
|
27400
|
+
}
|
|
27401
|
+
|
|
27402
|
+
// plugins/kxm/src/hub-env.ts
|
|
27403
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync5, readFileSync as readFileSync9, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "node:fs";
|
|
27404
|
+
import { homedir as homedir3 } from "node:os";
|
|
27405
|
+
import { dirname as dirname7, isAbsolute as isAbsolute5, join as join14, resolve as resolve7 } from "node:path";
|
|
27406
|
+
var HUB_ENV_SCHEMA = "kxm.hub-env.v1";
|
|
27407
|
+
var HubEnvError = class extends Error {
|
|
27408
|
+
constructor(message) {
|
|
27409
|
+
super(message);
|
|
27410
|
+
this.name = "HubEnvError";
|
|
27411
|
+
}
|
|
27412
|
+
};
|
|
27413
|
+
function resolveUserStateRoot2(env) {
|
|
27414
|
+
const explicit = env.KXM_STATE_HOME?.trim();
|
|
27415
|
+
if (explicit) {
|
|
27416
|
+
if (!isAbsolute5(explicit)) throw new HubEnvError("local_state_root_not_absolute");
|
|
27417
|
+
return resolve7(explicit);
|
|
27418
|
+
}
|
|
27419
|
+
if (process.platform === "win32") {
|
|
27420
|
+
const localAppData = env.LOCALAPPDATA?.trim();
|
|
27421
|
+
const base2 = localAppData && isAbsolute5(localAppData) ? localAppData : join14(homedir3(), "AppData", "Local");
|
|
27422
|
+
return resolve7(base2, "KXM");
|
|
27423
|
+
}
|
|
27424
|
+
if (process.platform === "darwin") return resolve7(homedir3(), "Library", "Application Support", "KXM");
|
|
27425
|
+
const xdgState = env.XDG_STATE_HOME?.trim();
|
|
27426
|
+
const base = xdgState && isAbsolute5(xdgState) ? xdgState : join14(homedir3(), ".local", "state");
|
|
27427
|
+
return resolve7(base, "kxm");
|
|
27428
|
+
}
|
|
27429
|
+
function hubEnvFile(env = process.env) {
|
|
27430
|
+
return join14(resolveUserStateRoot2(env), "hub-env.json");
|
|
27431
|
+
}
|
|
27432
|
+
function readHubEnvRecord(env = process.env) {
|
|
27433
|
+
const file = hubEnvFile(env);
|
|
27434
|
+
if (!existsSync11(file)) return void 0;
|
|
27435
|
+
let parsed;
|
|
27436
|
+
try {
|
|
27437
|
+
parsed = JSON.parse(readFileSync9(file, "utf8"));
|
|
27438
|
+
} catch (error) {
|
|
27439
|
+
throw new HubEnvError(`hub env file is malformed at ${file}: ${error instanceof Error ? error.message : String(error)}`);
|
|
27440
|
+
}
|
|
27441
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
27442
|
+
throw new HubEnvError(`hub env file is malformed at ${file}: expected a JSON object`);
|
|
27443
|
+
}
|
|
27444
|
+
const record2 = parsed;
|
|
27445
|
+
if (record2.schema !== HUB_ENV_SCHEMA) {
|
|
27446
|
+
throw new HubEnvError(`hub env file at ${file} has unsupported schema ${String(record2.schema)}`);
|
|
27447
|
+
}
|
|
27448
|
+
if (record2.authToken !== void 0 && (typeof record2.authToken !== "string" || record2.authToken.trim().length === 0)) {
|
|
27449
|
+
throw new HubEnvError(`hub env file at ${file} has an invalid authToken`);
|
|
27450
|
+
}
|
|
27451
|
+
if (record2.projectTokens !== void 0) {
|
|
27452
|
+
const tokens = record2.projectTokens;
|
|
27453
|
+
if (!tokens || typeof tokens !== "object" || Array.isArray(tokens)) {
|
|
27454
|
+
throw new HubEnvError(`hub env file at ${file} has invalid projectTokens`);
|
|
27455
|
+
}
|
|
27456
|
+
for (const [project, token] of Object.entries(tokens)) {
|
|
27457
|
+
if (!project.trim() || typeof token !== "string" || !token.trim()) {
|
|
27458
|
+
throw new HubEnvError(`hub env file at ${file} has an empty project name or token`);
|
|
27459
|
+
}
|
|
27460
|
+
}
|
|
27461
|
+
}
|
|
27462
|
+
return {
|
|
27463
|
+
schema: HUB_ENV_SCHEMA,
|
|
27464
|
+
createdAt: typeof record2.createdAt === "string" ? record2.createdAt : "",
|
|
27465
|
+
...record2.authToken !== void 0 ? { authToken: record2.authToken } : {},
|
|
27466
|
+
...record2.projectTokens !== void 0 ? { projectTokens: record2.projectTokens } : {}
|
|
27467
|
+
};
|
|
27468
|
+
}
|
|
27469
|
+
function resolveClientHubAuthToken(env, project) {
|
|
27470
|
+
const envToken = env.KXM_AUTH_TOKEN?.trim();
|
|
27471
|
+
if (envToken) return envToken;
|
|
27472
|
+
const record2 = readHubEnvRecord(env);
|
|
27473
|
+
return record2?.projectTokens?.[project]?.trim() || record2?.authToken?.trim() || void 0;
|
|
27474
|
+
}
|
|
27475
|
+
|
|
27476
|
+
// plugins/kxm/src/project-name.ts
|
|
27477
|
+
import { readFileSync as readFileSync10 } from "node:fs";
|
|
27478
|
+
import { basename as basename3, join as join15 } from "node:path";
|
|
27479
|
+
function defaultProjectName(cwd, env = process.env) {
|
|
27480
|
+
const fromEnv = env.KXM_PROJECT?.trim();
|
|
27481
|
+
if (fromEnv) return fromEnv;
|
|
27482
|
+
try {
|
|
27483
|
+
const pkg = JSON.parse(readFileSync10(join15(cwd, "package.json"), "utf8"));
|
|
27484
|
+
if (typeof pkg.name === "string" && pkg.name.trim().length > 0) return pkg.name.trim();
|
|
27485
|
+
} catch {
|
|
27486
|
+
}
|
|
27487
|
+
return basename3(cwd);
|
|
27488
|
+
}
|
|
27489
|
+
|
|
26811
27490
|
// plugins/kxm/src/runtime-supervisor.ts
|
|
26812
27491
|
function kxmSupervisorTokenFile(paths) {
|
|
26813
|
-
return
|
|
27492
|
+
return join16(paths.runtimeDir, "supervisor.token");
|
|
26814
27493
|
}
|
|
26815
27494
|
function publishKxmSupervisorToken(paths, token) {
|
|
26816
27495
|
const file = kxmSupervisorTokenFile(paths);
|
|
26817
|
-
|
|
27496
|
+
mkdirSync6(dirname8(file), { recursive: true, mode: 448 });
|
|
26818
27497
|
const temp = `${file}.${process.pid}.tmp`;
|
|
26819
|
-
|
|
27498
|
+
writeFileSync6(temp, `${token}
|
|
26820
27499
|
`, { encoding: "utf8", mode: 384 });
|
|
26821
27500
|
try {
|
|
26822
27501
|
chmodSync2(temp, 384);
|
|
26823
27502
|
} catch {
|
|
26824
27503
|
}
|
|
26825
|
-
|
|
27504
|
+
renameSync3(temp, file);
|
|
26826
27505
|
try {
|
|
26827
27506
|
chmodSync2(file, 384);
|
|
26828
27507
|
} catch {
|
|
26829
27508
|
}
|
|
26830
27509
|
}
|
|
26831
27510
|
function hashKxmSupervisorToken(token) {
|
|
26832
|
-
return `sha256:${
|
|
27511
|
+
return `sha256:${createHash12("sha256").update(`kxm-runtime-supervisor\0${token}`, "utf8").digest("hex")}`;
|
|
26833
27512
|
}
|
|
26834
27513
|
function readKxmSupervisorToken(paths) {
|
|
26835
27514
|
const file = kxmSupervisorTokenFile(paths);
|
|
@@ -26838,7 +27517,7 @@ function readKxmSupervisorToken(paths) {
|
|
|
26838
27517
|
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
26839
27518
|
throw runtimeError("runtime_path_invalid", file, "supervisor token file must be a regular file, not a link");
|
|
26840
27519
|
}
|
|
26841
|
-
const token =
|
|
27520
|
+
const token = readFileSync11(file, "utf8").trim();
|
|
26842
27521
|
return token.length >= 32 ? token : void 0;
|
|
26843
27522
|
}
|
|
26844
27523
|
function processAlive(pid) {
|
|
@@ -26852,16 +27531,16 @@ function processAlive(pid) {
|
|
|
26852
27531
|
var HEARTBEAT_STALE_MS = 15e3;
|
|
26853
27532
|
var SUPERVISOR_ERROR_MAX_AGE_MS = 3e4;
|
|
26854
27533
|
function supervisorErrorFile(paths) {
|
|
26855
|
-
return
|
|
27534
|
+
return join16(paths.runtimeDir, "supervisor.error");
|
|
26856
27535
|
}
|
|
26857
27536
|
function clearSupervisorError(paths) {
|
|
26858
27537
|
const file = supervisorErrorFile(paths);
|
|
26859
|
-
if (
|
|
27538
|
+
if (existsSync12(file)) rmSync3(file, { force: true });
|
|
26860
27539
|
}
|
|
26861
27540
|
function recordSupervisorError(paths, message) {
|
|
26862
27541
|
try {
|
|
26863
|
-
|
|
26864
|
-
|
|
27542
|
+
mkdirSync6(paths.runtimeDir, { recursive: true, mode: 448 });
|
|
27543
|
+
writeFileSync6(supervisorErrorFile(paths), `${message}
|
|
26865
27544
|
`, { encoding: "utf8", mode: 384 });
|
|
26866
27545
|
} catch {
|
|
26867
27546
|
}
|
|
@@ -26873,13 +27552,13 @@ function readRecentSupervisorError(paths) {
|
|
|
26873
27552
|
const ageMs = Date.now() - stat.mtimeMs;
|
|
26874
27553
|
if (ageMs > SUPERVISOR_ERROR_MAX_AGE_MS) return void 0;
|
|
26875
27554
|
try {
|
|
26876
|
-
return
|
|
27555
|
+
return readFileSync11(file, "utf8").trim();
|
|
26877
27556
|
} catch {
|
|
26878
27557
|
return void 0;
|
|
26879
27558
|
}
|
|
26880
27559
|
}
|
|
26881
27560
|
function kxmSupervisorStatus(paths) {
|
|
26882
|
-
if (!
|
|
27561
|
+
if (!existsSync12(paths.registryDb)) return { running: false };
|
|
26883
27562
|
const registry = new KxmRuntimeRegistry(paths.registryDb);
|
|
26884
27563
|
try {
|
|
26885
27564
|
const record2 = registry.supervisor();
|
|
@@ -26949,7 +27628,7 @@ async function ensureKxmSupervisor(options = {}) {
|
|
|
26949
27628
|
throw runtimeError("runtime_supervisor_unreachable", paths.registryDb, `runtime supervisor pid ${status.pid} is registered as running but cannot be probed`);
|
|
26950
27629
|
}
|
|
26951
27630
|
clearSupervisorError(paths);
|
|
26952
|
-
const scriptPath =
|
|
27631
|
+
const scriptPath = join16(findKxmRepoRoot(import.meta.url), "scripts", "kxm-runtime-supervisor.mjs");
|
|
26953
27632
|
const spawnImpl = options.spawnImpl ?? ((script, env) => {
|
|
26954
27633
|
const child = spawn3(process.execPath, [script], {
|
|
26955
27634
|
detached: true,
|
|
@@ -27034,8 +27713,8 @@ async function waitForDriveSessions(settled, graceMs) {
|
|
|
27034
27713
|
try {
|
|
27035
27714
|
await Promise.race([
|
|
27036
27715
|
Promise.allSettled(settled),
|
|
27037
|
-
new Promise((
|
|
27038
|
-
timeout = setTimeout(
|
|
27716
|
+
new Promise((resolve8) => {
|
|
27717
|
+
timeout = setTimeout(resolve8, graceMs);
|
|
27039
27718
|
timeout.unref?.();
|
|
27040
27719
|
})
|
|
27041
27720
|
]);
|
|
@@ -27056,6 +27735,63 @@ async function driveSessionStillPending(settled) {
|
|
|
27056
27735
|
await Promise.resolve();
|
|
27057
27736
|
return pending;
|
|
27058
27737
|
}
|
|
27738
|
+
var DEFAULT_RUNTIME_SYNC_INTERVAL_MS = 1e4;
|
|
27739
|
+
var MIN_RUNTIME_SYNC_INTERVAL_MS = 250;
|
|
27740
|
+
var MAX_RUNTIME_SYNC_INTERVAL_MS = 6e4;
|
|
27741
|
+
var OUTBOX_PUSH_BATCH = 32;
|
|
27742
|
+
function runtimeSyncIntervalMs(env = process.env) {
|
|
27743
|
+
const raw = env.KXM_RUNTIME_SYNC_INTERVAL_MS?.trim();
|
|
27744
|
+
if (!raw) return DEFAULT_RUNTIME_SYNC_INTERVAL_MS;
|
|
27745
|
+
const parsed = Number(raw);
|
|
27746
|
+
if (!Number.isInteger(parsed)) return DEFAULT_RUNTIME_SYNC_INTERVAL_MS;
|
|
27747
|
+
return Math.min(MAX_RUNTIME_SYNC_INTERVAL_MS, Math.max(MIN_RUNTIME_SYNC_INTERVAL_MS, parsed));
|
|
27748
|
+
}
|
|
27749
|
+
async function syncKxmOutbox(eventStore, client, options = {}) {
|
|
27750
|
+
const now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
27751
|
+
const batchSize = options.batchSize ?? OUTBOX_PUSH_BATCH;
|
|
27752
|
+
const result = { pushed: 0, acked: 0, conflicts: 0, rejected: 0 };
|
|
27753
|
+
let afterSeq = 0;
|
|
27754
|
+
for (; ; ) {
|
|
27755
|
+
const rows = eventStore.pendingOutbox(batchSize, afterSeq);
|
|
27756
|
+
if (rows.length === 0) return result;
|
|
27757
|
+
const MAX_BATCH_BYTES = 2e5;
|
|
27758
|
+
let byteBudget = MAX_BATCH_BYTES;
|
|
27759
|
+
let sendCount = 0;
|
|
27760
|
+
for (const row of rows) {
|
|
27761
|
+
const rowBytes = Buffer.byteLength(row.syncEvent, "utf8") + 64;
|
|
27762
|
+
if (sendCount > 0 && byteBudget - rowBytes < 0) break;
|
|
27763
|
+
byteBudget -= rowBytes;
|
|
27764
|
+
sendCount += 1;
|
|
27765
|
+
}
|
|
27766
|
+
const batch = rows.slice(0, sendCount);
|
|
27767
|
+
if (batch.length === 0) batch.push(rows[0]);
|
|
27768
|
+
afterSeq = batch[batch.length - 1].seq;
|
|
27769
|
+
eventStore.markOutboxAttempted(batch.map((row) => row.seq), now());
|
|
27770
|
+
const response = await client.pushSyncEvents(batch.map((row) => JSON.parse(row.syncEvent)));
|
|
27771
|
+
result.pushed += batch.length;
|
|
27772
|
+
const outcomes = new Map(response.results.map((entry) => [`${entry.runId}\0${entry.sequence}`, entry.outcome]));
|
|
27773
|
+
const acked = [];
|
|
27774
|
+
for (const row of batch) {
|
|
27775
|
+
const outcome = outcomes.get(`${row.runId}\0${row.sequence}`);
|
|
27776
|
+
if (outcome === "accepted" || outcome === "duplicate") acked.push(row.seq);
|
|
27777
|
+
else if (outcome === "conflict") result.conflicts += 1;
|
|
27778
|
+
else result.rejected += 1;
|
|
27779
|
+
}
|
|
27780
|
+
result.acked += eventStore.ackOutbox(acked, now());
|
|
27781
|
+
}
|
|
27782
|
+
}
|
|
27783
|
+
function runtimeHubClientFor(context, env) {
|
|
27784
|
+
const serverUrl = env.KXM_SERVER_URL?.trim() || readHubBinding(env)?.url;
|
|
27785
|
+
if (!serverUrl) return void 0;
|
|
27786
|
+
const project = defaultProjectName(context.projectRoot, env);
|
|
27787
|
+
const authToken = resolveClientHubAuthToken(env, project);
|
|
27788
|
+
return new RuntimeHubClient({
|
|
27789
|
+
serverUrl,
|
|
27790
|
+
project,
|
|
27791
|
+
runtimeId: context.homeRuntimeId,
|
|
27792
|
+
...authToken ? { authToken } : {}
|
|
27793
|
+
});
|
|
27794
|
+
}
|
|
27059
27795
|
async function startKxmRuntimeSupervisor(options = {}) {
|
|
27060
27796
|
const now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
27061
27797
|
const paths = kxmRuntimePaths(options.stateRoot !== void 0 ? { stateRoot: options.stateRoot } : {});
|
|
@@ -27069,23 +27805,34 @@ async function startKxmRuntimeSupervisor(options = {}) {
|
|
|
27069
27805
|
}
|
|
27070
27806
|
}
|
|
27071
27807
|
async function startKxmRuntimeSupervisorInner(paths, requestedPortOption, now) {
|
|
27072
|
-
|
|
27073
|
-
|
|
27808
|
+
mkdirSync6(paths.runtimeDir, { recursive: true, mode: 448 });
|
|
27809
|
+
mkdirSync6(paths.projectsDir, { recursive: true, mode: 448 });
|
|
27074
27810
|
const token = randomBytes2(32).toString("hex");
|
|
27075
27811
|
const tokenHash = hashKxmSupervisorToken(token);
|
|
27076
27812
|
const registry = new KxmRuntimeRegistry(paths.registryDb);
|
|
27077
|
-
const runtimeId = `rtm_${
|
|
27813
|
+
const runtimeId = `rtm_${createHash12("sha256").update(`${paths.stateRoot}\0${process.pid}\0${now()}\0${randomBytes2(16).toString("hex")}`, "utf8").digest("hex").slice(0, 24)}`;
|
|
27078
27814
|
const requestedPort = requestedPortOption ?? 0;
|
|
27079
27815
|
let activeRuntimeId = runtimeId;
|
|
27080
27816
|
const contexts = /* @__PURE__ */ new Map();
|
|
27817
|
+
const registerSyncCredentials = (context) => {
|
|
27818
|
+
const hubToken = resolveClientHubAuthToken(process.env, defaultProjectName(context.projectRoot, process.env));
|
|
27819
|
+
if (hubToken) context.eventStore.syncRedactor.register(hubToken);
|
|
27820
|
+
for (const key of Object.keys(process.env)) {
|
|
27821
|
+
if (key.startsWith("KXM_") && (key.endsWith("_TOKEN") || key.endsWith("_KEY")) || key.endsWith("_API_KEY") || key.endsWith("_SECRET")) {
|
|
27822
|
+
const value = process.env[key]?.trim();
|
|
27823
|
+
if (value) context.eventStore.syncRedactor.register(value);
|
|
27824
|
+
}
|
|
27825
|
+
}
|
|
27826
|
+
};
|
|
27081
27827
|
const contextFor = (projectRoot) => {
|
|
27082
|
-
if (!
|
|
27828
|
+
if (!isAbsolute6(projectRoot)) {
|
|
27083
27829
|
throw runtimeError("runtime_request_invalid", "projectRoot", "projectRoot must be an absolute path");
|
|
27084
27830
|
}
|
|
27085
27831
|
const key = projectRuntimeKey(projectRoot);
|
|
27086
27832
|
const existing = contexts.get(key);
|
|
27087
27833
|
if (existing) return existing;
|
|
27088
27834
|
const context = openKxmRuntimeContext(projectRoot, { homeRuntimeId: activeRuntimeId, stateRoot: paths.stateRoot });
|
|
27835
|
+
registerSyncCredentials(context);
|
|
27089
27836
|
contexts.set(key, context);
|
|
27090
27837
|
return context;
|
|
27091
27838
|
};
|
|
@@ -27417,11 +28164,37 @@ async function startKxmRuntimeSupervisorInner(paths, requestedPortOption, now) {
|
|
|
27417
28164
|
}
|
|
27418
28165
|
}, 1e3);
|
|
27419
28166
|
heartbeat.unref();
|
|
28167
|
+
for (const reg of registry.projectsForRuntime(activeRuntimeId)) {
|
|
28168
|
+
try {
|
|
28169
|
+
contextFor(reg.projectRoot);
|
|
28170
|
+
} catch {
|
|
28171
|
+
}
|
|
28172
|
+
}
|
|
28173
|
+
let syncing = false;
|
|
28174
|
+
const syncTimer = setInterval(() => {
|
|
28175
|
+
if (syncing || stopping) return;
|
|
28176
|
+
syncing = true;
|
|
28177
|
+
void (async () => {
|
|
28178
|
+
for (const context of [...contexts.values()]) {
|
|
28179
|
+
try {
|
|
28180
|
+
const client = runtimeHubClientFor(context, process.env);
|
|
28181
|
+
if (!client) continue;
|
|
28182
|
+
await client.heartbeat();
|
|
28183
|
+
await syncKxmOutbox(context.eventStore, client, { now });
|
|
28184
|
+
} catch {
|
|
28185
|
+
}
|
|
28186
|
+
}
|
|
28187
|
+
})().finally(() => {
|
|
28188
|
+
syncing = false;
|
|
28189
|
+
});
|
|
28190
|
+
}, runtimeSyncIntervalMs());
|
|
28191
|
+
syncTimer.unref();
|
|
27420
28192
|
let stopping = false;
|
|
27421
28193
|
const stop = async () => {
|
|
27422
28194
|
if (stopping) return;
|
|
27423
28195
|
stopping = true;
|
|
27424
28196
|
clearInterval(heartbeat);
|
|
28197
|
+
clearInterval(syncTimer);
|
|
27425
28198
|
try {
|
|
27426
28199
|
registry.markStopping(process.pid, now());
|
|
27427
28200
|
} catch {
|
|
@@ -27485,6 +28258,7 @@ async function kxmRuntimeRequest(handle, method, path, body) {
|
|
|
27485
28258
|
}
|
|
27486
28259
|
export {
|
|
27487
28260
|
DEFAULT_RUNTIME_STOP_GRACE_MS,
|
|
28261
|
+
DEFAULT_RUNTIME_SYNC_INTERVAL_MS,
|
|
27488
28262
|
MAX_RUNTIME_STOP_GRACE_MS,
|
|
27489
28263
|
attachKxmSupervisor,
|
|
27490
28264
|
ensureKxmSupervisor,
|
|
@@ -27495,5 +28269,7 @@ export {
|
|
|
27495
28269
|
kxmSupervisorTokenFile,
|
|
27496
28270
|
readKxmSupervisorToken,
|
|
27497
28271
|
runtimeStopGraceMs,
|
|
27498
|
-
|
|
28272
|
+
runtimeSyncIntervalMs,
|
|
28273
|
+
startKxmRuntimeSupervisor,
|
|
28274
|
+
syncKxmOutbox
|
|
27499
28275
|
};
|