@kontextmind/kxm 0.7.72 → 0.7.74
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/external-effects.ts +30 -5
- 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
|
@@ -2986,7 +2986,7 @@ var require_compile = __commonJS({
|
|
|
2986
2986
|
const schOrFunc = root.refs[ref];
|
|
2987
2987
|
if (schOrFunc)
|
|
2988
2988
|
return schOrFunc;
|
|
2989
|
-
let _sch =
|
|
2989
|
+
let _sch = resolve10.call(this, root, ref);
|
|
2990
2990
|
if (_sch === void 0) {
|
|
2991
2991
|
const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
|
|
2992
2992
|
const { schemaId } = this.opts;
|
|
@@ -3013,7 +3013,7 @@ var require_compile = __commonJS({
|
|
|
3013
3013
|
function sameSchemaEnv(s1, s2) {
|
|
3014
3014
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
3015
3015
|
}
|
|
3016
|
-
function
|
|
3016
|
+
function resolve10(root, ref) {
|
|
3017
3017
|
let sch;
|
|
3018
3018
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
3019
3019
|
ref = sch;
|
|
@@ -3838,7 +3838,7 @@ var require_fast_uri = __commonJS({
|
|
|
3838
3838
|
}
|
|
3839
3839
|
return uri;
|
|
3840
3840
|
}
|
|
3841
|
-
function
|
|
3841
|
+
function resolve10(baseURI, relativeURI, options) {
|
|
3842
3842
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
3843
3843
|
const {
|
|
3844
3844
|
parsed: baseParsed,
|
|
@@ -4200,7 +4200,7 @@ var require_fast_uri = __commonJS({
|
|
|
4200
4200
|
var fastUri = {
|
|
4201
4201
|
SCHEMES,
|
|
4202
4202
|
normalize,
|
|
4203
|
-
resolve:
|
|
4203
|
+
resolve: resolve10,
|
|
4204
4204
|
resolveComponent,
|
|
4205
4205
|
equal,
|
|
4206
4206
|
serialize,
|
|
@@ -14778,7 +14778,7 @@ var require_dist = __commonJS({
|
|
|
14778
14778
|
});
|
|
14779
14779
|
|
|
14780
14780
|
// plugins/kxm/src/runtime-service.ts
|
|
14781
|
-
import { createHash as
|
|
14781
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
14782
14782
|
import { existsSync as existsSync6, readdirSync as readdirSync3, readFileSync as readFileSync4 } from "node:fs";
|
|
14783
14783
|
import { join as join6 } from "node:path";
|
|
14784
14784
|
|
|
@@ -15160,7 +15160,7 @@ function defaultSpawn(command, args, options) {
|
|
|
15160
15160
|
if (options.signal?.aborted) {
|
|
15161
15161
|
return Promise.resolve({ stdout: "", stderr: "", code: null, started: false, observedChildExit: false, error: new Error("process_aborted") });
|
|
15162
15162
|
}
|
|
15163
|
-
return new Promise((
|
|
15163
|
+
return new Promise((resolve10) => {
|
|
15164
15164
|
const stdout = [];
|
|
15165
15165
|
const stderr = [];
|
|
15166
15166
|
let outputBytes = 0;
|
|
@@ -15191,7 +15191,7 @@ function defaultSpawn(command, args, options) {
|
|
|
15191
15191
|
const started = Boolean(child?.pid);
|
|
15192
15192
|
if (started && !observedChildExit) error ??= new Error("process_exit_unobserved");
|
|
15193
15193
|
const unverifiedDescendants = stopping || started && !observedChildExit;
|
|
15194
|
-
|
|
15194
|
+
resolve10({
|
|
15195
15195
|
stdout: Buffer.concat(stdout).toString("utf8"),
|
|
15196
15196
|
stderr: Buffer.concat(stderr).toString("utf8"),
|
|
15197
15197
|
code,
|
|
@@ -16473,6 +16473,7 @@ var KxmSchemaRegistry = class {
|
|
|
16473
16473
|
driveReceiptValidator;
|
|
16474
16474
|
coordinatorValidator;
|
|
16475
16475
|
intakeMessageValidator;
|
|
16476
|
+
syncEventValidator;
|
|
16476
16477
|
constructor(schemasDir = DEFAULT_SCHEMA_DIR) {
|
|
16477
16478
|
this.schemasDir = resolve(schemasDir);
|
|
16478
16479
|
this.ajv = new import__.Ajv2020({ allErrors: true, strict: true, strictRequired: false });
|
|
@@ -16489,6 +16490,7 @@ var KxmSchemaRegistry = class {
|
|
|
16489
16490
|
const driveReceiptFile = "drive-receipt.schema.json";
|
|
16490
16491
|
const coordinatorFile = "coordinator.schema.json";
|
|
16491
16492
|
const intakeMessageFile = "intake-message.schema.json";
|
|
16493
|
+
const syncEventFile = "sync-event.schema.json";
|
|
16492
16494
|
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, localBindingsFile)));
|
|
16493
16495
|
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, templateProvenanceFile)));
|
|
16494
16496
|
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, initOperationFile)));
|
|
@@ -16497,6 +16499,7 @@ var KxmSchemaRegistry = class {
|
|
|
16497
16499
|
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, driveReceiptFile)));
|
|
16498
16500
|
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, coordinatorFile)));
|
|
16499
16501
|
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, intakeMessageFile)));
|
|
16502
|
+
this.ajv.addSchema(readJsonObject(join2(this.schemasDir, syncEventFile)));
|
|
16500
16503
|
for (const [kind, definition] of Object.entries(RESOURCE_SCHEMA)) {
|
|
16501
16504
|
const validator = this.ajv.getSchema(`https://schemas.kxm.dev/${definition.file}`);
|
|
16502
16505
|
if (!validator) throw new Error(`schema did not compile: ${definition.file}`);
|
|
@@ -16510,6 +16513,7 @@ var KxmSchemaRegistry = class {
|
|
|
16510
16513
|
const driveReceiptValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${driveReceiptFile}`);
|
|
16511
16514
|
const coordinatorValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${coordinatorFile}`);
|
|
16512
16515
|
const intakeMessageValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${intakeMessageFile}`);
|
|
16516
|
+
const syncEventValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${syncEventFile}`);
|
|
16513
16517
|
if (!localBindingsValidator) throw new Error(`schema did not compile: ${localBindingsFile}`);
|
|
16514
16518
|
if (!templateProvenanceValidator) throw new Error(`schema did not compile: ${templateProvenanceFile}`);
|
|
16515
16519
|
if (!initOperationValidator) throw new Error(`schema did not compile: ${initOperationFile}`);
|
|
@@ -16518,6 +16522,7 @@ var KxmSchemaRegistry = class {
|
|
|
16518
16522
|
if (!driveReceiptValidator) throw new Error(`schema did not compile: ${driveReceiptFile}`);
|
|
16519
16523
|
if (!coordinatorValidator) throw new Error(`schema did not compile: ${coordinatorFile}`);
|
|
16520
16524
|
if (!intakeMessageValidator) throw new Error(`schema did not compile: ${intakeMessageFile}`);
|
|
16525
|
+
if (!syncEventValidator) throw new Error(`schema did not compile: ${syncEventFile}`);
|
|
16521
16526
|
this.localBindingsValidator = localBindingsValidator;
|
|
16522
16527
|
this.templateProvenanceValidator = templateProvenanceValidator;
|
|
16523
16528
|
this.initOperationValidator = initOperationValidator;
|
|
@@ -16526,6 +16531,7 @@ var KxmSchemaRegistry = class {
|
|
|
16526
16531
|
this.driveReceiptValidator = driveReceiptValidator;
|
|
16527
16532
|
this.coordinatorValidator = coordinatorValidator;
|
|
16528
16533
|
this.intakeMessageValidator = intakeMessageValidator;
|
|
16534
|
+
this.syncEventValidator = syncEventValidator;
|
|
16529
16535
|
}
|
|
16530
16536
|
validate(kind, value, file) {
|
|
16531
16537
|
const definition = RESOURCE_SCHEMA[kind];
|
|
@@ -16572,6 +16578,11 @@ function validateRunEvent(value, file) {
|
|
|
16572
16578
|
)]);
|
|
16573
16579
|
}
|
|
16574
16580
|
}
|
|
16581
|
+
function syncEventSchemaErrors(value) {
|
|
16582
|
+
const registry = cachedRunEventRegistry ??= new KxmSchemaRegistry();
|
|
16583
|
+
if (registry.syncEventValidator(value)) return void 0;
|
|
16584
|
+
return registry.ajv.errorsText(registry.syncEventValidator.errors, { separator: "; " });
|
|
16585
|
+
}
|
|
16575
16586
|
function validateDriveReceipt(value, file) {
|
|
16576
16587
|
const registry = cachedRunEventRegistry ??= new KxmSchemaRegistry();
|
|
16577
16588
|
if (!value || typeof value !== "object" || Array.isArray(value) || value.schema !== "kxm.drive-receipt.v1") {
|
|
@@ -17078,8 +17089,8 @@ function validateWorkflow(workflow, agents, models, repositories, gates, issues)
|
|
|
17078
17089
|
const writable = Object.values(objectValue(step.repositories) ?? {}).filter((access) => access === "write").length;
|
|
17079
17090
|
if (maxWriteRepositories > writable) issues.push(issue2("semantic", "write_repository_bound_invalid", file, `${stepId} maxWriteRepositories exceeds writable repository scope`));
|
|
17080
17091
|
}
|
|
17081
|
-
const
|
|
17082
|
-
const minimumPassed =
|
|
17092
|
+
const join21 = objectValue(step.join);
|
|
17093
|
+
const minimumPassed = join21 && typeof join21.minimumPassed === "number" ? join21.minimumPassed : void 0;
|
|
17083
17094
|
if (minimumPassed !== void 0 && minimumPassed > maximum) issues.push(issue2("semantic", "join_impossible", file, `${stepId} minimumPassed exceeds assignment maximum`));
|
|
17084
17095
|
const distinctBy = names(assignment?.distinctBy);
|
|
17085
17096
|
if (distinctBy.length > 0) {
|
|
@@ -17552,7 +17563,7 @@ function legacyConfigFilesAt(root) {
|
|
|
17552
17563
|
}
|
|
17553
17564
|
|
|
17554
17565
|
// plugins/kxm/src/runtime-store.ts
|
|
17555
|
-
import { createHash as
|
|
17566
|
+
import { createHash as createHash5, randomUUID } from "node:crypto";
|
|
17556
17567
|
import { existsSync as existsSync5, lstatSync as lstatSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, realpathSync as realpathSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
17557
17568
|
import { dirname as dirname5, join as join5, resolve as resolve4 } from "node:path";
|
|
17558
17569
|
|
|
@@ -17628,8 +17639,339 @@ function kxmUserStateRoot(options = {}) {
|
|
|
17628
17639
|
return resolve2(base, "kxm");
|
|
17629
17640
|
}
|
|
17630
17641
|
|
|
17642
|
+
// plugins/kxm/src/sync-transform.ts
|
|
17643
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
17644
|
+
|
|
17645
|
+
// plugins/kxm/src/redact.ts
|
|
17646
|
+
var SECRET_PATTERNS = [
|
|
17647
|
+
/\bsk-[A-Za-z0-9_-]{8,}\b/g,
|
|
17648
|
+
/\bsk-ant-[A-Za-z0-9_-]{8,}\b/g,
|
|
17649
|
+
/\bghp_[A-Za-z0-9_]{20,}\b/g,
|
|
17650
|
+
/\bgithub_pat_[A-Za-z0-9_]{20,}\b/g,
|
|
17651
|
+
/\bxox[baprs]-[A-Za-z0-9-]{10,}\b/gi,
|
|
17652
|
+
/\bBearer\s+[A-Za-z0-9._~+/=-]+\b/gi,
|
|
17653
|
+
/\bya29\.[A-Za-z0-9._~+/-]+=*/g,
|
|
17654
|
+
/\b1\/\/[A-Za-z0-9_-]+/g,
|
|
17655
|
+
/\b1\/[A-Za-z0-9_-]{20,}/g,
|
|
17656
|
+
/("?(?:access_token|refresh_token|id_token|sessionKey|session_key|claude_oauth_token|anthropicApiKey)"?\s*[:=]\s*")[^"]*(")/gi,
|
|
17657
|
+
/\bKXM_[A-Z0-9_]*(TOKEN|SECRET|KEY)[A-Z0-9_]*=\S+/gi,
|
|
17658
|
+
/\b(GITHUB_TOKEN|GH_TOKEN|KXM_AUTH_TOKEN|KXM_WORKFLOW_SIGNAL_SECRET|ANTHROPIC_API_KEY|ANTHROPIC_AUTH_TOKEN|CLAUDE_API_KEY)=\S+/gi,
|
|
17659
|
+
/\b[A-Fa-f0-9]{64}\b/g
|
|
17660
|
+
];
|
|
17661
|
+
function redactSecrets(value) {
|
|
17662
|
+
let result = value;
|
|
17663
|
+
for (const pattern of SECRET_PATTERNS) {
|
|
17664
|
+
result = result.replace(pattern, "[redacted]");
|
|
17665
|
+
}
|
|
17666
|
+
return result;
|
|
17667
|
+
}
|
|
17668
|
+
|
|
17669
|
+
// plugins/kxm/src/sync-transform.ts
|
|
17670
|
+
var KXM_SYNC_EVENT_SCHEMA = "kxm.sync-event.v1";
|
|
17671
|
+
var KXM_SYNC_REDACTOR_VERSION = "redactor-v1";
|
|
17672
|
+
var KXM_DEFAULT_SYNC_POLICY = Object.freeze({
|
|
17673
|
+
prompts: "title-only",
|
|
17674
|
+
results: "bounded-summary",
|
|
17675
|
+
evidence: "references",
|
|
17676
|
+
artifacts: "metadata",
|
|
17677
|
+
fileChanges: "paths-only",
|
|
17678
|
+
rawLogs: false,
|
|
17679
|
+
diffs: false,
|
|
17680
|
+
environmentValues: false
|
|
17681
|
+
});
|
|
17682
|
+
var KXM_DEFAULT_SYNC_POLICY_REVISION = `sha256:${createHash3("sha256").update(kxmCanonicalJson(KXM_DEFAULT_SYNC_POLICY), "utf8").digest("hex")}`;
|
|
17683
|
+
var MIN_REGISTERED_SECRET_CHARS = 4;
|
|
17684
|
+
var REDACTED = "[redacted]";
|
|
17685
|
+
var PATH_REDACTED = "[path]";
|
|
17686
|
+
var CREDENTIAL_SHAPES = [
|
|
17687
|
+
/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?(?:-----END [A-Z ]*PRIVATE KEY-----|$)/g,
|
|
17688
|
+
/\bssh-(?:rsa|ed25519|dss|ecdsa-[a-z0-9-]+) AAAA[0-9A-Za-z+/=]+/g,
|
|
17689
|
+
/\bAKIA[0-9A-Z]{16}\b/g,
|
|
17690
|
+
/\bgh[opsu]_[A-Za-z0-9]{20,}\b/g,
|
|
17691
|
+
/\b(?:api[_-]?key|token|secret|password|passwd)\s*[:=]\s*\S+/gi
|
|
17692
|
+
];
|
|
17693
|
+
var URL_USERINFO = /\b([a-z][a-z0-9+.-]*:\/\/)[^\s/@]+@/gi;
|
|
17694
|
+
var ABSOLUTE_PATHS = [
|
|
17695
|
+
/\\\\[^\s\\"'`]+\\[^\s"'`]*/g,
|
|
17696
|
+
// UNC
|
|
17697
|
+
/\b[A-Za-z]:[\\/][^\s"'`]*/g,
|
|
17698
|
+
// Windows drive
|
|
17699
|
+
/(?<![\w.~:/-])~[\\/][^\s"'`]*/g,
|
|
17700
|
+
// home
|
|
17701
|
+
/(?<![\w.~:/-])\/(?:[\w.@%+-]+\/)*[\w.@%+-]+\/?/g
|
|
17702
|
+
// POSIX
|
|
17703
|
+
];
|
|
17704
|
+
var CONTROL_CHARACTERS = /[\u0000-\u001F\u007F]/g;
|
|
17705
|
+
var KxmSyncRedactor = class {
|
|
17706
|
+
values = /* @__PURE__ */ new Set();
|
|
17707
|
+
register(value) {
|
|
17708
|
+
const trimmed = value.trim();
|
|
17709
|
+
if (trimmed.length >= MIN_REGISTERED_SECRET_CHARS) this.values.add(trimmed);
|
|
17710
|
+
}
|
|
17711
|
+
get size() {
|
|
17712
|
+
return this.values.size;
|
|
17713
|
+
}
|
|
17714
|
+
/** Scrub one free-text value; `replaced` counts every substitution. */
|
|
17715
|
+
scrub(input) {
|
|
17716
|
+
let replaced = 0;
|
|
17717
|
+
let text = input;
|
|
17718
|
+
for (const secret of [...this.values].sort((left, right) => right.length - left.length)) {
|
|
17719
|
+
const parts = text.split(secret);
|
|
17720
|
+
if (parts.length > 1) {
|
|
17721
|
+
replaced += parts.length - 1;
|
|
17722
|
+
text = parts.join(REDACTED);
|
|
17723
|
+
}
|
|
17724
|
+
}
|
|
17725
|
+
const substitute = (pattern, replacement) => {
|
|
17726
|
+
text = text.replace(pattern, (...args) => {
|
|
17727
|
+
replaced += 1;
|
|
17728
|
+
return typeof replacement === "string" ? replacement : replacement(...args);
|
|
17729
|
+
});
|
|
17730
|
+
};
|
|
17731
|
+
for (const pattern of CREDENTIAL_SHAPES) substitute(pattern, REDACTED);
|
|
17732
|
+
substitute(URL_USERINFO, (_match, scheme) => `${scheme}${REDACTED}@`);
|
|
17733
|
+
const shaped = redactSecrets(text);
|
|
17734
|
+
if (shaped !== text) {
|
|
17735
|
+
replaced += shaped.split(REDACTED).length - text.split(REDACTED).length;
|
|
17736
|
+
text = shaped;
|
|
17737
|
+
}
|
|
17738
|
+
for (const pattern of ABSOLUTE_PATHS) substitute(pattern, PATH_REDACTED);
|
|
17739
|
+
text = text.replace(CONTROL_CHARACTERS, " ").replace(/\s+/g, " ").trim();
|
|
17740
|
+
return { text, replaced };
|
|
17741
|
+
}
|
|
17742
|
+
};
|
|
17743
|
+
function isRecord(value) {
|
|
17744
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
17745
|
+
}
|
|
17746
|
+
function pickScalars(source, keys, redactor) {
|
|
17747
|
+
if (!isRecord(source)) return void 0;
|
|
17748
|
+
const result = {};
|
|
17749
|
+
for (const key of keys) {
|
|
17750
|
+
const value = source[key];
|
|
17751
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
17752
|
+
result[key] = value;
|
|
17753
|
+
} else if (typeof value === "string") {
|
|
17754
|
+
if (redactor) {
|
|
17755
|
+
const scrubbed = redactor.scrub(value);
|
|
17756
|
+
if (scrubbed.text.length > 0) result[key] = scrubbed.text.slice(0, 200);
|
|
17757
|
+
} else {
|
|
17758
|
+
result[key] = value;
|
|
17759
|
+
}
|
|
17760
|
+
}
|
|
17761
|
+
}
|
|
17762
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
17763
|
+
}
|
|
17764
|
+
var SyncPayloadBuilder = class {
|
|
17765
|
+
payload = {};
|
|
17766
|
+
omitted = /* @__PURE__ */ new Set();
|
|
17767
|
+
replaced = 0;
|
|
17768
|
+
redactor;
|
|
17769
|
+
constructor(redactor) {
|
|
17770
|
+
this.redactor = redactor;
|
|
17771
|
+
}
|
|
17772
|
+
/** Bounded, scrubbed free text; empty after scrubbing means absent. */
|
|
17773
|
+
text(value, max) {
|
|
17774
|
+
if (typeof value !== "string") return void 0;
|
|
17775
|
+
const { text, replaced } = this.redactor.scrub(value);
|
|
17776
|
+
this.replaced += replaced;
|
|
17777
|
+
if (text.length === 0) return void 0;
|
|
17778
|
+
return text.length > max ? `${text.slice(0, max - 1)}\u2026` : text;
|
|
17779
|
+
}
|
|
17780
|
+
/** A nested record rebuilt key by key, with the named keys scrubbed as text. */
|
|
17781
|
+
record(source, keys, textKeys = {}) {
|
|
17782
|
+
const scalars = pickScalars(source, keys.filter((key) => !(key in textKeys)));
|
|
17783
|
+
const result = { ...scalars ?? {} };
|
|
17784
|
+
if (isRecord(source)) {
|
|
17785
|
+
for (const [key, max] of Object.entries(textKeys)) {
|
|
17786
|
+
const value = this.text(source[key], max);
|
|
17787
|
+
if (value !== void 0) result[key] = value;
|
|
17788
|
+
}
|
|
17789
|
+
}
|
|
17790
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
17791
|
+
}
|
|
17792
|
+
set(key, value) {
|
|
17793
|
+
if (value !== void 0) this.payload[key] = value;
|
|
17794
|
+
}
|
|
17795
|
+
};
|
|
17796
|
+
var SCALAR_FIELDS = [
|
|
17797
|
+
"workflowId",
|
|
17798
|
+
"promptHash",
|
|
17799
|
+
"status",
|
|
17800
|
+
"previousStatus",
|
|
17801
|
+
"outcome",
|
|
17802
|
+
"stepId",
|
|
17803
|
+
"stepAttempt",
|
|
17804
|
+
"fromStepId",
|
|
17805
|
+
"toStepId",
|
|
17806
|
+
"assignmentId",
|
|
17807
|
+
"attemptId",
|
|
17808
|
+
"agentId",
|
|
17809
|
+
"instanceNo",
|
|
17810
|
+
"scopeEpoch",
|
|
17811
|
+
"resolutionAction"
|
|
17812
|
+
];
|
|
17813
|
+
var REF_KEYS = ["id", "kind", "hash", "status", "producerId"];
|
|
17814
|
+
var ARTIFACT_KEYS = ["id", "kind", "hash", "size", "classification", "availability"];
|
|
17815
|
+
var LEASE_KEYS = ["id", "fencingToken", "acquiredAt", "expiresAt", "operation"];
|
|
17816
|
+
var MAX_REFS = 64;
|
|
17817
|
+
var CARRIED_FIELDS = /* @__PURE__ */ new Set([
|
|
17818
|
+
...SCALAR_FIELDS,
|
|
17819
|
+
"displayTitle",
|
|
17820
|
+
"summary",
|
|
17821
|
+
"reason",
|
|
17822
|
+
"executor",
|
|
17823
|
+
"model",
|
|
17824
|
+
"timing",
|
|
17825
|
+
"usage",
|
|
17826
|
+
"evidenceRefs",
|
|
17827
|
+
"artifacts",
|
|
17828
|
+
"receipt",
|
|
17829
|
+
"error",
|
|
17830
|
+
"effect",
|
|
17831
|
+
"lease",
|
|
17832
|
+
"actor",
|
|
17833
|
+
"resolvedBy",
|
|
17834
|
+
"suppliedRefs",
|
|
17835
|
+
"counts"
|
|
17836
|
+
]);
|
|
17837
|
+
var CONTROL_FIELDS = ["effect", "lease", "actor", "resolvedBy", "resolutionAction", "reason", "suppliedRefs", "outcome"];
|
|
17838
|
+
function buildPayload(source, builder) {
|
|
17839
|
+
for (const key of SCALAR_FIELDS) {
|
|
17840
|
+
const value = source[key];
|
|
17841
|
+
if (typeof value === "string" || typeof value === "number") builder.set(key, value);
|
|
17842
|
+
}
|
|
17843
|
+
builder.set("displayTitle", builder.text(source.displayTitle, 200));
|
|
17844
|
+
builder.set("summary", builder.text(source.summary, 1e3));
|
|
17845
|
+
if (source.reason !== void 0) {
|
|
17846
|
+
builder.set("reason", builder.text(source.reason, 1e3) ?? REDACTED);
|
|
17847
|
+
}
|
|
17848
|
+
builder.set("executor", builder.record(source.executor, ["id", "kind", "harness"], {
|
|
17849
|
+
executorVersion: 128,
|
|
17850
|
+
harnessVersion: 128,
|
|
17851
|
+
helperGeneration: 256
|
|
17852
|
+
}));
|
|
17853
|
+
const model = builder.record(source.model, ["profile", "provider"], { model: 200, thinking: 64 });
|
|
17854
|
+
if (model && isRecord(source.model) && Array.isArray(source.model.tags)) {
|
|
17855
|
+
model.tags = source.model.tags.filter((tag) => typeof tag === "string").slice(0, 32);
|
|
17856
|
+
}
|
|
17857
|
+
builder.set("model", model);
|
|
17858
|
+
builder.set("timing", builder.record(source.timing, ["startedAt", "finishedAt", "durationMs", "queueMs", "agentMs"]));
|
|
17859
|
+
builder.set("usage", builder.record(source.usage, [
|
|
17860
|
+
"inputTokens",
|
|
17861
|
+
"outputTokens",
|
|
17862
|
+
"cacheReadTokens",
|
|
17863
|
+
"cacheWriteTokens",
|
|
17864
|
+
"cost",
|
|
17865
|
+
"currency",
|
|
17866
|
+
"costKind"
|
|
17867
|
+
]));
|
|
17868
|
+
const refs = (value, keys) => Array.isArray(value) ? value.slice(0, MAX_REFS).map((entry) => pickScalars(entry, keys)).filter((entry) => entry !== void 0) : void 0;
|
|
17869
|
+
builder.set("evidenceRefs", refs(source.evidenceRefs, REF_KEYS));
|
|
17870
|
+
builder.set("suppliedRefs", refs(source.suppliedRefs, REF_KEYS));
|
|
17871
|
+
builder.set("artifacts", refs(source.artifacts, ARTIFACT_KEYS));
|
|
17872
|
+
if (isRecord(source.receipt)) {
|
|
17873
|
+
const receipt = builder.record(source.receipt, ["provider", "kind", "hash", "observedAt"], { id: 512 });
|
|
17874
|
+
if (receipt) builder.set("receipts", [receipt]);
|
|
17875
|
+
if (source.receipt.url !== void 0) builder.omitted.add("receipt.url");
|
|
17876
|
+
}
|
|
17877
|
+
builder.set("error", builder.record(source.error, ["class", "retryable"], { component: 128 }));
|
|
17878
|
+
if (isRecord(source.effect)) {
|
|
17879
|
+
const effect = pickScalars(source.effect, ["id", "idempotencyKeyHash"], builder.redactor) ?? {};
|
|
17880
|
+
const policy = pickScalars(source.effect.policy, ["class", "sharedMutable", "idempotencyKey", "receiptQuery"], builder.redactor);
|
|
17881
|
+
if (policy) effect.policy = policy;
|
|
17882
|
+
builder.set("effect", effect);
|
|
17883
|
+
}
|
|
17884
|
+
builder.set("lease", pickScalars(source.lease, LEASE_KEYS, builder.redactor));
|
|
17885
|
+
builder.set("actor", builder.record(source.actor, ["kind"], { id: 200 }));
|
|
17886
|
+
builder.set("resolvedBy", builder.record(source.resolvedBy, ["kind"], { id: 200 }));
|
|
17887
|
+
if (isRecord(source.counts)) {
|
|
17888
|
+
const counts = Object.fromEntries(Object.entries(source.counts).filter((entry) => Number.isInteger(entry[1]) && entry[1] >= 0).slice(0, 32));
|
|
17889
|
+
builder.set("counts", counts);
|
|
17890
|
+
}
|
|
17891
|
+
}
|
|
17892
|
+
function envelope(event, policyRevision, payload, fieldsOmitted, valuesReplaced) {
|
|
17893
|
+
return {
|
|
17894
|
+
schema: KXM_SYNC_EVENT_SCHEMA,
|
|
17895
|
+
sourceEventId: event.eventId,
|
|
17896
|
+
eventType: event.eventType,
|
|
17897
|
+
projectId: event.projectId,
|
|
17898
|
+
runId: event.runId,
|
|
17899
|
+
homeRuntimeId: event.homeRuntimeId,
|
|
17900
|
+
sequence: event.sequence,
|
|
17901
|
+
occurredAt: event.occurredAt,
|
|
17902
|
+
configRevision: event.configRevision,
|
|
17903
|
+
memoryRevision: event.memoryRevision,
|
|
17904
|
+
executorPolicyRevision: event.executorPolicyRevision,
|
|
17905
|
+
toolPolicyRevision: event.toolPolicyRevision,
|
|
17906
|
+
syncPolicyRevision: policyRevision,
|
|
17907
|
+
redaction: {
|
|
17908
|
+
version: KXM_SYNC_REDACTOR_VERSION,
|
|
17909
|
+
fieldsOmitted: [...new Set(fieldsOmitted)].sort().slice(0, 128),
|
|
17910
|
+
valuesReplaced: Math.min(valuesReplaced, 1e5)
|
|
17911
|
+
},
|
|
17912
|
+
payload
|
|
17913
|
+
};
|
|
17914
|
+
}
|
|
17915
|
+
function deriveKxmSyncEvent(event, options = {}) {
|
|
17916
|
+
const redactor = options.redactor ?? new KxmSyncRedactor();
|
|
17917
|
+
const policyRevision = options.policyRevision ?? KXM_DEFAULT_SYNC_POLICY_REVISION;
|
|
17918
|
+
const source = isRecord(event.payload) ? event.payload : {};
|
|
17919
|
+
const builder = new SyncPayloadBuilder(redactor);
|
|
17920
|
+
buildPayload(source, builder);
|
|
17921
|
+
const omitted = new Set(builder.omitted);
|
|
17922
|
+
for (const key of Object.keys(source)) if (!CARRIED_FIELDS.has(key)) omitted.add(key);
|
|
17923
|
+
if (event.protectedPayload !== void 0) omitted.add("protectedPayload");
|
|
17924
|
+
const payload = Object.keys(builder.payload).length > 0 ? builder.payload : { counts: {} };
|
|
17925
|
+
const full = envelope(event, policyRevision, payload, omitted, builder.replaced);
|
|
17926
|
+
if (syncEventSchemaErrors(full) === void 0) return full;
|
|
17927
|
+
const degradedPayload = { degraded: true };
|
|
17928
|
+
for (const key of CONTROL_FIELDS) {
|
|
17929
|
+
const value = builder.payload[key];
|
|
17930
|
+
const restoreNested = (built, orig) => {
|
|
17931
|
+
if (!isRecord(orig)) return built !== void 0 ? built : "[redacted]";
|
|
17932
|
+
const result = {};
|
|
17933
|
+
const builtRecord = isRecord(built) ? built : {};
|
|
17934
|
+
for (const [k, v] of Object.entries(orig)) {
|
|
17935
|
+
const builtValue = builtRecord[k];
|
|
17936
|
+
if (builtValue === void 0 || typeof builtValue === "string" && builtValue.trim().length === 0) {
|
|
17937
|
+
result[k] = isRecord(v) ? restoreNested(void 0, v) : "[redacted]";
|
|
17938
|
+
} else {
|
|
17939
|
+
result[k] = builtValue;
|
|
17940
|
+
}
|
|
17941
|
+
}
|
|
17942
|
+
return result;
|
|
17943
|
+
};
|
|
17944
|
+
if (value === void 0) {
|
|
17945
|
+
const sourceValue = source[key];
|
|
17946
|
+
if (sourceValue !== void 0) {
|
|
17947
|
+
degradedPayload[key] = restoreNested(void 0, sourceValue);
|
|
17948
|
+
}
|
|
17949
|
+
} else if (typeof value === "string" && value.trim().length === 0) {
|
|
17950
|
+
degradedPayload[key] = "[redacted]";
|
|
17951
|
+
} else if (isRecord(value)) {
|
|
17952
|
+
degradedPayload[key] = restoreNested(value, source[key]);
|
|
17953
|
+
} else {
|
|
17954
|
+
degradedPayload[key] = value;
|
|
17955
|
+
}
|
|
17956
|
+
}
|
|
17957
|
+
const degradedOmitted = new Set(omitted);
|
|
17958
|
+
for (const key of Object.keys(builder.payload)) if (!(key in degradedPayload)) degradedOmitted.add(key);
|
|
17959
|
+
const degraded = envelope(event, policyRevision, degradedPayload, degradedOmitted, builder.replaced);
|
|
17960
|
+
const errors = syncEventSchemaErrors(degraded);
|
|
17961
|
+
if (errors === void 0) return degraded;
|
|
17962
|
+
throw new KxmConfigError([{
|
|
17963
|
+
phase: "schema",
|
|
17964
|
+
code: "sync_event_invalid",
|
|
17965
|
+
file: `${event.runId}#${event.sequence}`,
|
|
17966
|
+
message: `event cannot be derived into kxm.sync-event.v1: ${errors}`
|
|
17967
|
+
}]);
|
|
17968
|
+
}
|
|
17969
|
+
function kxmSyncEventBytes(event) {
|
|
17970
|
+
return kxmCanonicalJson(event);
|
|
17971
|
+
}
|
|
17972
|
+
|
|
17631
17973
|
// plugins/kxm/src/database.ts
|
|
17632
|
-
import { createHash as
|
|
17974
|
+
import { createHash as createHash4, randomBytes } from "node:crypto";
|
|
17633
17975
|
import {
|
|
17634
17976
|
chmodSync,
|
|
17635
17977
|
copyFileSync,
|
|
@@ -17867,7 +18209,7 @@ function checkIntegrity(database) {
|
|
|
17867
18209
|
}
|
|
17868
18210
|
function fileSha256(filePath) {
|
|
17869
18211
|
const bytes = readFileSync2(filePath);
|
|
17870
|
-
return `sha256:${
|
|
18212
|
+
return `sha256:${createHash4("sha256").update(bytes).digest("hex")}`;
|
|
17871
18213
|
}
|
|
17872
18214
|
function backupDatabaseFile(sourcePath, targetPath, storeId) {
|
|
17873
18215
|
const resolvedSource = resolve3(sourcePath);
|
|
@@ -17989,7 +18331,7 @@ function discoverProjectStores(projectRoot, options = {}) {
|
|
|
17989
18331
|
const stores = [];
|
|
17990
18332
|
const hubPath = options.hubDataPath ? resolve3(options.hubDataPath) : join4(root, ".kxm", "state", "kxm.db");
|
|
17991
18333
|
if (existsSync4(hubPath)) {
|
|
17992
|
-
stores.push({ storeId: "hub-store", sourcePath: hubPath, maxSupportedVersion:
|
|
18334
|
+
stores.push({ storeId: "hub-store", sourcePath: hubPath, maxSupportedVersion: 5 });
|
|
17993
18335
|
}
|
|
17994
18336
|
const registryPath = join4(root, ".kxm", "runtime", "registry.db");
|
|
17995
18337
|
if (existsSync4(registryPath)) {
|
|
@@ -18008,7 +18350,7 @@ function discoverProjectStores(projectRoot, options = {}) {
|
|
|
18008
18350
|
stores.push({
|
|
18009
18351
|
storeId: `events:${key}`,
|
|
18010
18352
|
sourcePath: join4(eventsDir, entry.name),
|
|
18011
|
-
maxSupportedVersion:
|
|
18353
|
+
maxSupportedVersion: 6
|
|
18012
18354
|
});
|
|
18013
18355
|
}
|
|
18014
18356
|
}
|
|
@@ -18051,7 +18393,7 @@ function createBackup(options = {}) {
|
|
|
18051
18393
|
stores: backedUpStores
|
|
18052
18394
|
};
|
|
18053
18395
|
const manifestJson = JSON.stringify(manifest, null, 2) + "\n";
|
|
18054
|
-
const manifestSha256 = `sha256:${
|
|
18396
|
+
const manifestSha256 = `sha256:${createHash4("sha256").update(manifestJson, "utf8").digest("hex")}`;
|
|
18055
18397
|
manifest.manifestSha256 = manifestSha256;
|
|
18056
18398
|
const finalJson = JSON.stringify(manifest, null, 2) + "\n";
|
|
18057
18399
|
const manifestPath = join4(outDir, "manifest.json");
|
|
@@ -18095,11 +18437,11 @@ function restoreBackup(manifestPathOrDir, options = {}) {
|
|
|
18095
18437
|
`backup file ${store.backupFile} sha256 ${actualSha256} does not match manifest hash ${store.sha256}`
|
|
18096
18438
|
);
|
|
18097
18439
|
}
|
|
18098
|
-
let maxSupported =
|
|
18440
|
+
let maxSupported = 5;
|
|
18099
18441
|
if (store.storeId === "registry" || store.storeId === "binding-store") {
|
|
18100
18442
|
maxSupported = 1;
|
|
18101
18443
|
} else if (store.storeId.startsWith("events:")) {
|
|
18102
|
-
maxSupported =
|
|
18444
|
+
maxSupported = 6;
|
|
18103
18445
|
}
|
|
18104
18446
|
let targetPath = store.sourcePath;
|
|
18105
18447
|
if (options.projectRoot && manifest.projectRoot && targetPath.startsWith(manifest.projectRoot)) {
|
|
@@ -18147,7 +18489,7 @@ function projectRuntimeKey(projectRoot) {
|
|
|
18147
18489
|
canonical = resolve4(projectRoot);
|
|
18148
18490
|
}
|
|
18149
18491
|
const folded = process.platform === "win32" ? canonical.toLocaleLowerCase("en-US") : canonical;
|
|
18150
|
-
return
|
|
18492
|
+
return createHash5("sha256").update(folded, "utf8").digest("hex").slice(0, 24);
|
|
18151
18493
|
}
|
|
18152
18494
|
var KXM_REGISTRY_SCHEMA_VERSION = 1;
|
|
18153
18495
|
var REGISTRY_TABLES = {
|
|
@@ -18267,6 +18609,15 @@ var KxmRuntimeRegistry = class {
|
|
|
18267
18609
|
return this.readSupervisorRow();
|
|
18268
18610
|
}
|
|
18269
18611
|
/** Register or revalidate a project's home binding. Home Runtime is immutable. */
|
|
18612
|
+
/** All projects registered to this Runtime, for restart recovery: the
|
|
18613
|
+
* supervisor needs to reopen their contexts so pending outbox rows resume
|
|
18614
|
+
* syncing and presence keeps beating. */
|
|
18615
|
+
projectsForRuntime(homeRuntimeId) {
|
|
18616
|
+
const rows = this.database.prepare(
|
|
18617
|
+
"SELECT project_root, project_id FROM projects WHERE home_runtime_id = ? ORDER BY registered_at"
|
|
18618
|
+
).all(homeRuntimeId);
|
|
18619
|
+
return rows.map((row) => ({ projectRoot: row.project_root, projectId: row.project_id }));
|
|
18620
|
+
}
|
|
18270
18621
|
registerProject(registration) {
|
|
18271
18622
|
const projectRoot = resolve4(registration.projectRoot);
|
|
18272
18623
|
const projectKey = projectRuntimeKey(projectRoot);
|
|
@@ -18344,7 +18695,7 @@ var KXM_RUN_EVENT_SCHEMA = "kxm.run-event.v1";
|
|
|
18344
18695
|
var KXM_ABSENT_MEMORY_REVISION = "ctxrev_absent";
|
|
18345
18696
|
function hashKxmDriveLog(events) {
|
|
18346
18697
|
const body = events.map((event) => `${event.eventId}:${event.sequence}`).join("\n");
|
|
18347
|
-
return `sha256:${
|
|
18698
|
+
return `sha256:${createHash5("sha256").update(body, "utf8").digest("hex")}`;
|
|
18348
18699
|
}
|
|
18349
18700
|
function verifyKxmDriveReceipt(receipt, events, foldedStatus, binding) {
|
|
18350
18701
|
const reasons = [];
|
|
@@ -18369,7 +18720,7 @@ function verifyKxmDriveReceipt(receipt, events, foldedStatus, binding) {
|
|
|
18369
18720
|
if (reasons.length === 0) return { verified: true };
|
|
18370
18721
|
return { verified: false, divergence: reasons.join("; ") };
|
|
18371
18722
|
}
|
|
18372
|
-
var KXM_EVENT_STORE_SCHEMA_VERSION =
|
|
18723
|
+
var KXM_EVENT_STORE_SCHEMA_VERSION = 6;
|
|
18373
18724
|
var KXM_DRIVE_RECEIPT_SCHEMA = "kxm.drive-receipt.v1";
|
|
18374
18725
|
var DRIVE_RECEIPT_MAX_BYTES = 8 * 1024;
|
|
18375
18726
|
var EVENT_STORE_TABLES = {
|
|
@@ -18474,7 +18825,8 @@ var EVENT_STORE_TABLES = {
|
|
|
18474
18825
|
"schema",
|
|
18475
18826
|
"record"
|
|
18476
18827
|
],
|
|
18477
|
-
project_controls: ["project_id", "paused", "reason", "updated_at", "actor", "schema", "record"]
|
|
18828
|
+
project_controls: ["project_id", "paused", "reason", "updated_at", "actor", "schema", "record"],
|
|
18829
|
+
outbox: ["seq", "run_id", "sequence", "sync_event", "attempted_at", "acked_at"]
|
|
18478
18830
|
};
|
|
18479
18831
|
var KXM_EVENT_STORE_TABLE_NAMES = Object.keys(EVENT_STORE_TABLES).sort();
|
|
18480
18832
|
var EVENT_STORE_SCHEMA = `
|
|
@@ -18662,9 +19014,22 @@ CREATE TABLE project_controls (
|
|
|
18662
19014
|
schema TEXT NOT NULL,
|
|
18663
19015
|
record TEXT NOT NULL
|
|
18664
19016
|
) STRICT;
|
|
19017
|
+
CREATE TABLE IF NOT EXISTS outbox (
|
|
19018
|
+
seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
19019
|
+
run_id TEXT NOT NULL,
|
|
19020
|
+
sequence INTEGER NOT NULL,
|
|
19021
|
+
sync_event TEXT NOT NULL,
|
|
19022
|
+
attempted_at TEXT,
|
|
19023
|
+
acked_at TEXT,
|
|
19024
|
+
UNIQUE (run_id, sequence)
|
|
19025
|
+
) STRICT;
|
|
19026
|
+
CREATE INDEX outbox_pending ON outbox(seq) WHERE acked_at IS NULL;
|
|
18665
19027
|
`;
|
|
18666
19028
|
var KxmRunEventStore = class {
|
|
18667
19029
|
path;
|
|
19030
|
+
/** Secret values registered here are replaced in every sync object this
|
|
19031
|
+
* store derives. In memory only: they are never written anywhere. */
|
|
19032
|
+
syncRedactor = new KxmSyncRedactor();
|
|
18668
19033
|
database;
|
|
18669
19034
|
constructor(path) {
|
|
18670
19035
|
this.path = resolve4(path);
|
|
@@ -18747,6 +19112,15 @@ var KxmRunEventStore = class {
|
|
|
18747
19112
|
`).get(runId);
|
|
18748
19113
|
return row ? runFromRow(row) : void 0;
|
|
18749
19114
|
}
|
|
19115
|
+
/** All projects registered to this Runtime, for restart recovery: the
|
|
19116
|
+
* supervisor needs to reopen their contexts so pending outbox rows resume
|
|
19117
|
+
* syncing and presence keeps beating. */
|
|
19118
|
+
projectsForRuntime(homeRuntimeId) {
|
|
19119
|
+
const rows = this.database.prepare(
|
|
19120
|
+
"SELECT project_root, project_id FROM projects WHERE home_runtime_id = ? ORDER BY registered_at"
|
|
19121
|
+
).all(homeRuntimeId);
|
|
19122
|
+
return rows.map((row) => ({ projectRoot: row.project_root, projectId: row.project_id }));
|
|
19123
|
+
}
|
|
18750
19124
|
runsForProject(projectId, limit = 50) {
|
|
18751
19125
|
const rows = this.database.prepare(`
|
|
18752
19126
|
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
|
|
@@ -18786,6 +19160,39 @@ var KxmRunEventStore = class {
|
|
|
18786
19160
|
event.schema,
|
|
18787
19161
|
event.homeRuntimeId
|
|
18788
19162
|
);
|
|
19163
|
+
const syncEvent = deriveKxmSyncEvent(event, { redactor: this.syncRedactor });
|
|
19164
|
+
this.database.prepare("INSERT INTO outbox (run_id, sequence, sync_event) VALUES (?, ?, ?)").run(event.runId, event.sequence, kxmSyncEventBytes(syncEvent));
|
|
19165
|
+
}
|
|
19166
|
+
/** Unacknowledged outbox rows after `afterSeq`, in outbox order, oldest first. */
|
|
19167
|
+
pendingOutbox(limit = 100, afterSeq = 0) {
|
|
19168
|
+
const rows = this.database.prepare(`
|
|
19169
|
+
SELECT seq, run_id, sequence, sync_event, attempted_at, acked_at
|
|
19170
|
+
FROM outbox WHERE acked_at IS NULL AND seq > ? ORDER BY seq ASC LIMIT ?
|
|
19171
|
+
`).all(afterSeq, limit);
|
|
19172
|
+
return rows.map(outboxFromRow);
|
|
19173
|
+
}
|
|
19174
|
+
/** Every outbox row of one run, acknowledged or not, in sequence order. */
|
|
19175
|
+
outboxForRun(runId) {
|
|
19176
|
+
const rows = this.database.prepare(`
|
|
19177
|
+
SELECT seq, run_id, sequence, sync_event, attempted_at, acked_at
|
|
19178
|
+
FROM outbox WHERE run_id = ? ORDER BY sequence ASC
|
|
19179
|
+
`).all(runId);
|
|
19180
|
+
return rows.map(outboxFromRow);
|
|
19181
|
+
}
|
|
19182
|
+
markOutboxAttempted(seqs, at) {
|
|
19183
|
+
const statement = this.database.prepare("UPDATE outbox SET attempted_at = ? WHERE seq = ? AND acked_at IS NULL");
|
|
19184
|
+
this.transaction(() => {
|
|
19185
|
+
for (const seq of seqs) statement.run(at, seq);
|
|
19186
|
+
});
|
|
19187
|
+
}
|
|
19188
|
+
/** Advance the cursor: the hub holds these rows now. Returns rows newly acked. */
|
|
19189
|
+
ackOutbox(seqs, at) {
|
|
19190
|
+
const statement = this.database.prepare("UPDATE outbox SET acked_at = ? WHERE seq = ? AND acked_at IS NULL");
|
|
19191
|
+
return this.transaction(() => {
|
|
19192
|
+
let changed = 0;
|
|
19193
|
+
for (const seq of seqs) changed += Number(statement.run(at, seq).changes);
|
|
19194
|
+
return changed;
|
|
19195
|
+
});
|
|
18789
19196
|
}
|
|
18790
19197
|
events(runId, afterSequence = 0, limit = 200) {
|
|
18791
19198
|
const rows = this.database.prepare(`
|
|
@@ -19302,6 +19709,16 @@ function parseDriveReceipt(raw) {
|
|
|
19302
19709
|
validateDriveReceipt(parsed, "drive-receipt");
|
|
19303
19710
|
return parsed;
|
|
19304
19711
|
}
|
|
19712
|
+
function outboxFromRow(row) {
|
|
19713
|
+
return {
|
|
19714
|
+
seq: row.seq,
|
|
19715
|
+
runId: row.run_id,
|
|
19716
|
+
sequence: row.sequence,
|
|
19717
|
+
syncEvent: row.sync_event,
|
|
19718
|
+
...row.attempted_at !== null ? { attemptedAt: row.attempted_at } : {},
|
|
19719
|
+
...row.acked_at !== null ? { ackedAt: row.acked_at } : {}
|
|
19720
|
+
};
|
|
19721
|
+
}
|
|
19305
19722
|
function runFromRow(row) {
|
|
19306
19723
|
if (typeof row.memory_revision !== "string" || row.memory_revision.length === 0) {
|
|
19307
19724
|
throw runtimeError("runtime_schema_shape_invalid", row.run_id, "memory_revision is required");
|
|
@@ -19424,7 +19841,7 @@ function gateRowContentHash(table, row) {
|
|
|
19424
19841
|
const copy = { ...row };
|
|
19425
19842
|
delete copy.contentHash;
|
|
19426
19843
|
void table;
|
|
19427
|
-
return `sha256:${
|
|
19844
|
+
return `sha256:${createHash5("sha256").update(kxmCanonicalJson(copy), "utf8").digest("hex")}`;
|
|
19428
19845
|
}
|
|
19429
19846
|
function assertClosedGateObservation(kind, row, id = "observation") {
|
|
19430
19847
|
if (row.completeness === "incomplete") return;
|
|
@@ -20855,7 +21272,7 @@ function isTerminalRunStatus(status) {
|
|
|
20855
21272
|
}
|
|
20856
21273
|
|
|
20857
21274
|
// plugins/kxm/src/engine-plan.ts
|
|
20858
|
-
import { createHash as
|
|
21275
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
20859
21276
|
|
|
20860
21277
|
// plugins/kxm/src/engine-compile.ts
|
|
20861
21278
|
var KXM_COMPILED_WORKFLOW_SCHEMA = "kxm.compiled-workflow.v1";
|
|
@@ -21005,7 +21422,7 @@ function compileStep(step, index, stepIndex, requirePlanHash, sink) {
|
|
|
21005
21422
|
const maxAttempts = compileCountField(step.maxAttempts, 1, `${id}.maxAttempts`, id, sink);
|
|
21006
21423
|
const timeoutMs = compileOptionalDuration(step.timeoutMs, `${id}.timeoutMs`, id, sink);
|
|
21007
21424
|
const assignments = compileAssignments(step, id, agent, sink);
|
|
21008
|
-
const
|
|
21425
|
+
const join21 = compileJoin(step, id, sink);
|
|
21009
21426
|
const requiredEvidence = compileEvidence(step, id, sink);
|
|
21010
21427
|
const transitions = compileTransitions(step, id, index, stepIndex, sink);
|
|
21011
21428
|
const outcomes = Object.keys(transitions).sort(compareCodeUnits4);
|
|
@@ -21033,7 +21450,7 @@ function compileStep(step, index, stepIndex, requirePlanHash, sink) {
|
|
|
21033
21450
|
transitions: orderedTransitions,
|
|
21034
21451
|
requiresPlanHash: requirePlanHash.includes(id),
|
|
21035
21452
|
assignments,
|
|
21036
|
-
join:
|
|
21453
|
+
join: join21
|
|
21037
21454
|
};
|
|
21038
21455
|
if (kind === "agent" || kind === "moa") {
|
|
21039
21456
|
if (!agent) return void 0;
|
|
@@ -21083,15 +21500,15 @@ function compileAssignments(step, stepId, primaryAgentId, sink) {
|
|
|
21083
21500
|
};
|
|
21084
21501
|
}
|
|
21085
21502
|
function compileJoin(step, stepId, sink) {
|
|
21086
|
-
const
|
|
21087
|
-
if (!
|
|
21088
|
-
const declared = stringValue2(
|
|
21503
|
+
const join21 = objectValue2(step.join);
|
|
21504
|
+
if (!join21) return { strategy: "all" };
|
|
21505
|
+
const declared = stringValue2(join21.strategy);
|
|
21089
21506
|
const strategy = declared && JOIN_STRATEGIES.has(declared) ? declared : "all";
|
|
21090
|
-
const minimumPassed = compileOptionalCount(
|
|
21507
|
+
const minimumPassed = compileOptionalCount(join21.minimumPassed, `${stepId}.join.minimumPassed`, stepId, sink);
|
|
21091
21508
|
const compiled = {
|
|
21092
21509
|
strategy,
|
|
21093
21510
|
...minimumPassed !== void 0 ? { minimumPassed } : {},
|
|
21094
|
-
...typeof
|
|
21511
|
+
...typeof join21.cancelRemaining === "boolean" ? { cancelRemaining: join21.cancelRemaining } : {}
|
|
21095
21512
|
};
|
|
21096
21513
|
return compiled;
|
|
21097
21514
|
}
|
|
@@ -21402,10 +21819,10 @@ var JOIN_OPTIONAL = ["minimumPassed", "cancelRemaining"];
|
|
|
21402
21819
|
var EVIDENCE_REQUIRED = ["key", "kind", "minimum", "reusableAcrossAttempts"];
|
|
21403
21820
|
var EVIDENCE_OPTIONAL = ["producerPolicy"];
|
|
21404
21821
|
function kxmSha256(input) {
|
|
21405
|
-
return `sha256:${
|
|
21822
|
+
return `sha256:${createHash6("sha256").update(input, "utf8").digest("hex")}`;
|
|
21406
21823
|
}
|
|
21407
|
-
function hashKxmRunPlanEnvelope(
|
|
21408
|
-
return kxmSha256(kxmCanonicalJson(
|
|
21824
|
+
function hashKxmRunPlanEnvelope(envelope2) {
|
|
21825
|
+
return kxmSha256(kxmCanonicalJson(envelope2));
|
|
21409
21826
|
}
|
|
21410
21827
|
function freezeKxmCompiledPlan(plan) {
|
|
21411
21828
|
const steps = /* @__PURE__ */ Object.create(null);
|
|
@@ -21452,12 +21869,12 @@ function parseKxmRunPlanEnvelope(raw, run, expectedHash) {
|
|
|
21452
21869
|
} catch {
|
|
21453
21870
|
throw runtimeError("run_plan_corrupt", run.runId, "run plan envelope is not JSON");
|
|
21454
21871
|
}
|
|
21455
|
-
const
|
|
21456
|
-
const hash = hashKxmRunPlanEnvelope(
|
|
21872
|
+
const envelope2 = parseEnvelope(parsed, run);
|
|
21873
|
+
const hash = hashKxmRunPlanEnvelope(envelope2);
|
|
21457
21874
|
if (hash !== expectedHash) {
|
|
21458
21875
|
throw runtimeError("run_plan_corrupt", run.runId, "run plan envelope hash does not match");
|
|
21459
21876
|
}
|
|
21460
|
-
return
|
|
21877
|
+
return envelope2;
|
|
21461
21878
|
}
|
|
21462
21879
|
function rehydrateKxmCompiledPlanFromStore(store, run) {
|
|
21463
21880
|
return loadPinnedEnvelope(store, run).plan;
|
|
@@ -21967,7 +22384,7 @@ function gateRegistryHash(registryValue) {
|
|
|
21967
22384
|
}
|
|
21968
22385
|
|
|
21969
22386
|
// plugins/kxm/src/engine-evidence.ts
|
|
21970
|
-
function verifyKxmGateEvidence(store, run,
|
|
22387
|
+
function verifyKxmGateEvidence(store, run, envelope2, events, state) {
|
|
21971
22388
|
const attempts = store.gateAttemptsForRun(run.runId);
|
|
21972
22389
|
const observations = store.gateObservationsForRun(run.runId);
|
|
21973
22390
|
const evidence = store.gateEvidenceForRun(run.runId);
|
|
@@ -21985,7 +22402,7 @@ function verifyKxmGateEvidence(store, run, envelope, events, state) {
|
|
|
21985
22402
|
const attemptId = stringField(event, "attemptId");
|
|
21986
22403
|
const row = attemptById.get(attemptId);
|
|
21987
22404
|
if (!row) throw runtimeError("gate_attempt_pin_mismatch", run.runId, `effect.intent_recorded ${event.eventId} has no gate_attempts row`);
|
|
21988
|
-
assertAttemptMatchesEvent(run,
|
|
22405
|
+
assertAttemptMatchesEvent(run, envelope2, state, event, row);
|
|
21989
22406
|
}
|
|
21990
22407
|
for (const event of observedEvents) {
|
|
21991
22408
|
const attemptId = stringField(event, "attemptId");
|
|
@@ -22048,7 +22465,7 @@ function verifyKxmGateEvidence(store, run, envelope, events, state) {
|
|
|
22048
22465
|
if (row.outcome !== recomputed || event.payload.outcome !== recomputed) {
|
|
22049
22466
|
throw runtimeError("gate_evidence_hash_mismatch", run.runId, "evidence outcome does not match the observation and expect");
|
|
22050
22467
|
}
|
|
22051
|
-
const step =
|
|
22468
|
+
const step = envelope2.plan.steps[row.stepId];
|
|
22052
22469
|
const required = step?.requiredEvidence.filter((item) => item.kind === "gate") ?? [];
|
|
22053
22470
|
const expectedKey = required.length === 1 ? required[0].key : null;
|
|
22054
22471
|
if (row.evidenceKey !== expectedKey) {
|
|
@@ -22144,7 +22561,7 @@ function lastEffectByAttempt(events) {
|
|
|
22144
22561
|
}
|
|
22145
22562
|
return last;
|
|
22146
22563
|
}
|
|
22147
|
-
function assertAttemptMatchesEvent(run,
|
|
22564
|
+
function assertAttemptMatchesEvent(run, envelope2, state, event, row) {
|
|
22148
22565
|
const effectId = event.payload.effect?.id;
|
|
22149
22566
|
if (row.effectId !== effectId || row.intentEventId !== event.eventId) {
|
|
22150
22567
|
throw runtimeError("gate_attempt_pin_mismatch", run.runId, "intent event does not match the gate attempt");
|
|
@@ -22155,14 +22572,14 @@ function assertAttemptMatchesEvent(run, envelope, state, event, row) {
|
|
|
22155
22572
|
if (row.stepId !== event.payload.stepId || row.stepAttempt !== event.payload.stepAttempt || row.assignmentId !== event.payload.assignmentId || row.attemptId !== event.payload.attemptId) {
|
|
22156
22573
|
throw runtimeError("gate_attempt_pin_mismatch", run.runId, "gate attempt identity does not match the intent payload");
|
|
22157
22574
|
}
|
|
22158
|
-
const step =
|
|
22575
|
+
const step = envelope2.plan.steps[row.stepId];
|
|
22159
22576
|
if (!step || step.kind !== "gate") {
|
|
22160
22577
|
throw runtimeError("gate_attempt_pin_mismatch", run.runId, "gate attempt step is not a gate");
|
|
22161
22578
|
}
|
|
22162
22579
|
if (row.gateId !== step.gate || row.expect !== step.expect) {
|
|
22163
22580
|
throw runtimeError("gate_attempt_pin_mismatch", run.runId, "gate attempt gate/expect do not match the plan");
|
|
22164
22581
|
}
|
|
22165
|
-
const definition =
|
|
22582
|
+
const definition = envelope2.gates.definitions[step.gate];
|
|
22166
22583
|
if (!definition || definition.kind !== row.gateKind) {
|
|
22167
22584
|
throw runtimeError("gate_attempt_pin_mismatch", run.runId, "gate attempt kind does not match the pinned definition");
|
|
22168
22585
|
}
|
|
@@ -22170,14 +22587,14 @@ function assertAttemptMatchesEvent(run, envelope, state, event, row) {
|
|
|
22170
22587
|
if (row.gateDefinitionHash !== expectedDefinitionHash) {
|
|
22171
22588
|
throw runtimeError("gate_attempt_pin_mismatch", run.runId, "gate_definition_hash does not match the pinned definition");
|
|
22172
22589
|
}
|
|
22173
|
-
if (!
|
|
22590
|
+
if (!envelope2.gates.registry || row.registryHash !== envelope2.gates.registry.hash) {
|
|
22174
22591
|
throw runtimeError("gate_attempt_pin_mismatch", run.runId, "registry_hash does not match the pinned registry");
|
|
22175
22592
|
}
|
|
22176
|
-
const planRowHash =
|
|
22593
|
+
const planRowHash = envelope2 && state.runPlanHash;
|
|
22177
22594
|
if (row.runPlanHash !== planRowHash) {
|
|
22178
22595
|
throw runtimeError("gate_attempt_pin_mismatch", run.runId, "run_plan_hash does not match the pinned plan");
|
|
22179
22596
|
}
|
|
22180
|
-
if (row.controlProjectKey !==
|
|
22597
|
+
if (row.controlProjectKey !== envelope2.gates.controlRoot.projectKey) {
|
|
22181
22598
|
throw runtimeError("gate_attempt_pin_mismatch", run.runId, "control_project_key does not match the envelope");
|
|
22182
22599
|
}
|
|
22183
22600
|
}
|
|
@@ -22415,12 +22832,12 @@ function enqueueKxmScheduledRun(storePath, runId, configRevision, bound, start)
|
|
|
22415
22832
|
if (owner.admitted.has(runId) || owner.queue.some((item) => item.runId === runId)) {
|
|
22416
22833
|
return Promise.reject(runtimeError("run_busy", runId, `run ${runId} is already admitted or queued`));
|
|
22417
22834
|
}
|
|
22418
|
-
return new Promise((
|
|
22835
|
+
return new Promise((resolve10, reject) => {
|
|
22419
22836
|
owner.queue.push({
|
|
22420
22837
|
runId,
|
|
22421
22838
|
configRevision,
|
|
22422
22839
|
bound,
|
|
22423
|
-
start: (token) => start(token).then(
|
|
22840
|
+
start: (token) => start(token).then(resolve10, reject),
|
|
22424
22841
|
fail: (error) => reject(error)
|
|
22425
22842
|
});
|
|
22426
22843
|
pump(storePath, owner);
|
|
@@ -22449,7 +22866,7 @@ function compareCodeUnits5(left, right) {
|
|
|
22449
22866
|
return left < right ? -1 : left > right ? 1 : 0;
|
|
22450
22867
|
}
|
|
22451
22868
|
function sha256Of(input) {
|
|
22452
|
-
return `sha256:${
|
|
22869
|
+
return `sha256:${createHash7("sha256").update(input, "utf8").digest("hex")}`;
|
|
22453
22870
|
}
|
|
22454
22871
|
function objectValue3(value) {
|
|
22455
22872
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
@@ -22497,7 +22914,7 @@ function collectMemoryFiles(dir, baseDir, ignoreSubdirs = /* @__PURE__ */ new Se
|
|
|
22497
22914
|
return results;
|
|
22498
22915
|
}
|
|
22499
22916
|
function computeKxmMemoryRevision(bundle, options = {}) {
|
|
22500
|
-
const hash =
|
|
22917
|
+
const hash = createHash7("sha256");
|
|
22501
22918
|
const memoryDir = options.memoryDir ?? join6(bundle.projectRoot, ".kxm", "memory");
|
|
22502
22919
|
const memoryFiles = collectMemoryFiles(memoryDir, memoryDir, /* @__PURE__ */ new Set(["candidates"])).sort((left, right) => compareCodeUnits5(left.relPath, right.relPath));
|
|
22503
22920
|
for (const file of memoryFiles) {
|
|
@@ -22656,7 +23073,7 @@ function acceptKxmRun(context, bundle, request, options = {}) {
|
|
|
22656
23073
|
}
|
|
22657
23074
|
const revisions = kxmPolicyRevisions(bundle, options);
|
|
22658
23075
|
const memoryRevision = options.memoryRevision ?? revisions.memoryRevision;
|
|
22659
|
-
const promptSha256 = `sha256:${
|
|
23076
|
+
const promptSha256 = `sha256:${createHash7("sha256").update(request.prompt, "utf8").digest("hex")}`;
|
|
22660
23077
|
return context.eventStore.transaction(() => {
|
|
22661
23078
|
const prior = context.eventStore.command(commandId);
|
|
22662
23079
|
if (prior) {
|
|
@@ -22731,21 +23148,21 @@ function foldStoredKxmRun(context, run) {
|
|
|
22731
23148
|
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")) {
|
|
22732
23149
|
throw runtimeError("run_plan_missing", run.runId, "run reached preparing or later without a pinned plan");
|
|
22733
23150
|
}
|
|
22734
|
-
const
|
|
22735
|
-
const state = foldKxmRunState(run,
|
|
23151
|
+
const envelope2 = planRow ? loadKxmRunPlanEnvelope(context.eventStore, run) : void 0;
|
|
23152
|
+
const state = foldKxmRunState(run, envelope2?.plan, events, envelope2 ? {
|
|
22736
23153
|
observationLookup: (attemptId, observationId) => {
|
|
22737
23154
|
const row = context.eventStore.gateObservationForAttempt(attemptId);
|
|
22738
23155
|
if (!row || row.observationId !== observationId) return void 0;
|
|
22739
23156
|
return { completeness: row.completeness };
|
|
22740
23157
|
},
|
|
22741
23158
|
gateKind: (stepId) => {
|
|
22742
|
-
const step =
|
|
23159
|
+
const step = envelope2.plan.steps[stepId];
|
|
22743
23160
|
if (!step || step.kind !== "gate") return void 0;
|
|
22744
|
-
return
|
|
23161
|
+
return envelope2.gates.definitions[step.gate]?.kind;
|
|
22745
23162
|
},
|
|
22746
|
-
projectLimits:
|
|
23163
|
+
projectLimits: envelope2.projectLimits
|
|
22747
23164
|
} : {});
|
|
22748
|
-
if (
|
|
23165
|
+
if (envelope2) verifyKxmGateEvidence(context.eventStore, run, envelope2, events, state);
|
|
22749
23166
|
assertStoredProjection(context, run);
|
|
22750
23167
|
return state;
|
|
22751
23168
|
}
|
|
@@ -22938,16 +23355,16 @@ function assertKxmConfigError(error) {
|
|
|
22938
23355
|
|
|
22939
23356
|
// plugins/kxm/src/runtime-supervisor.ts
|
|
22940
23357
|
import { spawn as spawn3 } from "node:child_process";
|
|
22941
|
-
import { createHash as
|
|
22942
|
-
import { chmodSync as chmodSync2, existsSync as
|
|
23358
|
+
import { createHash as createHash13, createHmac, randomBytes as randomBytes3, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
23359
|
+
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";
|
|
22943
23360
|
import { createServer } from "node:http";
|
|
22944
|
-
import { dirname as
|
|
23361
|
+
import { dirname as dirname8, isAbsolute as isAbsolute6, join as join16 } from "node:path";
|
|
22945
23362
|
|
|
22946
23363
|
// plugins/kxm/src/oneshot-producer.ts
|
|
22947
23364
|
import { join as join12 } from "node:path";
|
|
22948
23365
|
|
|
22949
23366
|
// plugins/kxm/src/oneshot-evidence.ts
|
|
22950
|
-
import { createHash as
|
|
23367
|
+
import { createHash as createHash8, randomUUID as randomUUID3 } from "node:crypto";
|
|
22951
23368
|
import { lstat, mkdir, open, rename } from "node:fs/promises";
|
|
22952
23369
|
import { join as join7 } from "node:path";
|
|
22953
23370
|
var ONESHOT_EVIDENCE_SCHEMA = "kxm.oneshot-evidence.v2";
|
|
@@ -22955,7 +23372,7 @@ var RETIRED_ONESHOT_EVIDENCE_SCHEMAS = /* @__PURE__ */ new Set(["kxm.oneshot-evi
|
|
|
22955
23372
|
var TEXT_LIMIT = 4 * 1024 * 1024;
|
|
22956
23373
|
var ARGV_LIMIT = 64 * 1024;
|
|
22957
23374
|
var RECORD_LIMIT = 16 * 1024 * 1024;
|
|
22958
|
-
var digest = (value) => `sha256:${
|
|
23375
|
+
var digest = (value) => `sha256:${createHash8("sha256").update(value).digest("hex")}`;
|
|
22959
23376
|
function parseOneShotEvidenceRecord(bytes) {
|
|
22960
23377
|
let parsed;
|
|
22961
23378
|
try {
|
|
@@ -23065,7 +23482,7 @@ async function beginOneShotEvidence(root, intent, sensitive) {
|
|
|
23065
23482
|
|
|
23066
23483
|
// plugins/kxm/src/prices.ts
|
|
23067
23484
|
var import_yaml3 = __toESM(require_dist(), 1);
|
|
23068
|
-
import { createHash as
|
|
23485
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
23069
23486
|
import { existsSync as existsSync7, readFileSync as readFileSync5, statSync } from "node:fs";
|
|
23070
23487
|
import { join as join8 } from "node:path";
|
|
23071
23488
|
|
|
@@ -23128,7 +23545,7 @@ function hashPriceCatalog(catalog) {
|
|
|
23128
23545
|
}))
|
|
23129
23546
|
})).sort((a, b) => a.id.localeCompare(b.id))
|
|
23130
23547
|
};
|
|
23131
|
-
return
|
|
23548
|
+
return createHash9("sha256").update(JSON.stringify(canonical), "utf8").digest("hex");
|
|
23132
23549
|
}
|
|
23133
23550
|
function parsePriceCatalog(text) {
|
|
23134
23551
|
const parsed = (0, import_yaml3.parse)(text);
|
|
@@ -23241,7 +23658,7 @@ function loadPriceCatalogForEstimate(options) {
|
|
|
23241
23658
|
|
|
23242
23659
|
// plugins/kxm/src/engine.ts
|
|
23243
23660
|
var import_yaml5 = __toESM(require_dist(), 1);
|
|
23244
|
-
import { createHash as
|
|
23661
|
+
import { createHash as createHash12, randomBytes as randomBytes2, timingSafeEqual } from "node:crypto";
|
|
23245
23662
|
import { existsSync as existsSync9, readFileSync as readFileSync7 } from "node:fs";
|
|
23246
23663
|
import { join as join11 } from "node:path";
|
|
23247
23664
|
|
|
@@ -23623,7 +24040,7 @@ function evaluateArtifactsGate(context, definition) {
|
|
|
23623
24040
|
|
|
23624
24041
|
// plugins/kxm/src/engine-command.ts
|
|
23625
24042
|
import { spawn as spawn2 } from "node:child_process";
|
|
23626
|
-
import { createHash as
|
|
24043
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
23627
24044
|
|
|
23628
24045
|
// plugins/kxm/src/safety-integrity.ts
|
|
23629
24046
|
var DESTRUCTIVE_COMMAND_PATTERNS = Object.freeze([
|
|
@@ -23671,7 +24088,7 @@ function createCommandObserver(input) {
|
|
|
23671
24088
|
return new CommandObserver(input);
|
|
23672
24089
|
}
|
|
23673
24090
|
var StreamDigest = class {
|
|
23674
|
-
hash =
|
|
24091
|
+
hash = createHash10("sha256");
|
|
23675
24092
|
bytes = 0;
|
|
23676
24093
|
complete = 0;
|
|
23677
24094
|
digest() {
|
|
@@ -23746,8 +24163,8 @@ var CommandObserver = class {
|
|
|
23746
24163
|
this.requestStop("error", "lost-close");
|
|
23747
24164
|
}
|
|
23748
24165
|
run() {
|
|
23749
|
-
return new Promise((
|
|
23750
|
-
this.resolveOutcome =
|
|
24166
|
+
return new Promise((resolve10) => {
|
|
24167
|
+
this.resolveOutcome = resolve10;
|
|
23751
24168
|
try {
|
|
23752
24169
|
this.start();
|
|
23753
24170
|
} catch (error) {
|
|
@@ -24091,9 +24508,9 @@ function schedule(ms, fn) {
|
|
|
24091
24508
|
}
|
|
24092
24509
|
|
|
24093
24510
|
// plugins/kxm/src/engine-gate-records.ts
|
|
24094
|
-
function recordGateIntentInTransaction(context, run,
|
|
24095
|
-
const step = requireGateStep(
|
|
24096
|
-
const definition =
|
|
24511
|
+
function recordGateIntentInTransaction(context, run, envelope2, stepId) {
|
|
24512
|
+
const step = requireGateStep(envelope2, stepId, run.runId);
|
|
24513
|
+
const definition = envelope2.gates.definitions[step.gate];
|
|
24097
24514
|
if (!definition || definition.kind === "reserved") {
|
|
24098
24515
|
throw runtimeError("gate_row_invalid", run.runId, "reserved gates cannot record intent in this slice");
|
|
24099
24516
|
}
|
|
@@ -24146,9 +24563,9 @@ function recordGateIntentInTransaction(context, run, envelope, stepId) {
|
|
|
24146
24563
|
gateKind: definition.kind,
|
|
24147
24564
|
expect: step.expect,
|
|
24148
24565
|
gateDefinitionHash: gateDefinitionHash(step.gate, definition),
|
|
24149
|
-
registryHash:
|
|
24150
|
-
runPlanHash:
|
|
24151
|
-
controlProjectKey:
|
|
24566
|
+
registryHash: envelope2.gates.registry.hash,
|
|
24567
|
+
runPlanHash: envelope2 && folded.runPlanHash ? folded.runPlanHash : hashFromStore(context, run.runId),
|
|
24568
|
+
controlProjectKey: envelope2.gates.controlRoot.projectKey,
|
|
24152
24569
|
producerId: "kxm-gate",
|
|
24153
24570
|
intentEventId: intent.eventId,
|
|
24154
24571
|
contentHash: ""
|
|
@@ -24158,8 +24575,8 @@ function recordGateIntentInTransaction(context, run, envelope, stepId) {
|
|
|
24158
24575
|
context.eventStore.insertGateAttempt(withHash);
|
|
24159
24576
|
return persist(context, run, now, seq.events.at(-1).sequence);
|
|
24160
24577
|
}
|
|
24161
|
-
function recordGateSpawnedInTransaction(context, run,
|
|
24162
|
-
const ids = requireIntent(context, run,
|
|
24578
|
+
function recordGateSpawnedInTransaction(context, run, envelope2, stepId) {
|
|
24579
|
+
const ids = requireIntent(context, run, envelope2, stepId);
|
|
24163
24580
|
if (ids.gateKind !== "command") throw runtimeError("gate_row_invalid", run.runId, "effect.dispatched is command-only");
|
|
24164
24581
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
24165
24582
|
const seq = sequencer(context, run, now);
|
|
@@ -24175,8 +24592,8 @@ function recordGateSpawnedInTransaction(context, run, envelope, stepId) {
|
|
|
24175
24592
|
for (const event of seq.events) context.eventStore.appendEvent(event);
|
|
24176
24593
|
return persist(context, run, now, seq.events.at(-1).sequence);
|
|
24177
24594
|
}
|
|
24178
|
-
function recordGateSettlementInTransaction(context, run,
|
|
24179
|
-
const ids = requireIntent(context, run,
|
|
24595
|
+
function recordGateSettlementInTransaction(context, run, envelope2, stepId, observation) {
|
|
24596
|
+
const ids = requireIntent(context, run, envelope2, stepId);
|
|
24180
24597
|
assertClosedGateObservation(ids.gateKind, observation);
|
|
24181
24598
|
const outcome = computeGateEvidenceOutcome(ids.gateKind, ids.expect, observation);
|
|
24182
24599
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -24216,7 +24633,7 @@ function recordGateSettlementInTransaction(context, run, envelope, stepId, obser
|
|
|
24216
24633
|
stepAttempt: ids.stepAttempt,
|
|
24217
24634
|
assignmentId: ids.assignmentId,
|
|
24218
24635
|
effectId: ids.effectId,
|
|
24219
|
-
evidenceKey: evidenceKeyFor(
|
|
24636
|
+
evidenceKey: evidenceKeyFor(envelope2, stepId),
|
|
24220
24637
|
kind: "gate",
|
|
24221
24638
|
expect: ids.expect,
|
|
24222
24639
|
outcome,
|
|
@@ -24235,15 +24652,15 @@ function recordGateSettlementInTransaction(context, run, envelope, stepId, obser
|
|
|
24235
24652
|
evidenceDraft.settledEventId = settled.eventId;
|
|
24236
24653
|
evidenceDraft.contentHash = gateRowContentHash("gate_evidence", { ...evidenceDraft, contentHash: "" });
|
|
24237
24654
|
settled.payload.evidenceRefs[0].hash = evidenceDraft.contentHash;
|
|
24238
|
-
appendEvaluatedTail(seq,
|
|
24655
|
+
appendEvaluatedTail(seq, envelope2, ids, stepId, outcome);
|
|
24239
24656
|
for (const event of seq.events) context.eventStore.appendEvent(event);
|
|
24240
24657
|
context.eventStore.insertGateObservation(observationRow);
|
|
24241
24658
|
context.eventStore.insertGateEvidence(evidenceDraft);
|
|
24242
24659
|
context.eventStore.settleCapability(ids.attemptId, "settled");
|
|
24243
24660
|
return persist(context, run, now, seq.events.at(-1).sequence);
|
|
24244
24661
|
}
|
|
24245
|
-
function recordGateNoStartInTransaction(context, run,
|
|
24246
|
-
const ids = requireIntent(context, run,
|
|
24662
|
+
function recordGateNoStartInTransaction(context, run, envelope2, stepId, observation) {
|
|
24663
|
+
const ids = requireIntent(context, run, envelope2, stepId);
|
|
24247
24664
|
assertClosedGateObservation(ids.gateKind, observation);
|
|
24248
24665
|
if (observation.completeness !== "no-start") {
|
|
24249
24666
|
throw runtimeError("gate_row_invalid", run.runId, "no-start proof requires spawned 0 and completeness no-start");
|
|
@@ -24296,8 +24713,8 @@ function recordGateNoStartInTransaction(context, run, envelope, stepId, observat
|
|
|
24296
24713
|
context.eventStore.settleCapability(ids.attemptId, "settled");
|
|
24297
24714
|
return persist(context, run, now, seq.events.at(-1).sequence);
|
|
24298
24715
|
}
|
|
24299
|
-
function recordGateUncertainInTransaction(context, run,
|
|
24300
|
-
const ids = requireIntent(context, run,
|
|
24716
|
+
function recordGateUncertainInTransaction(context, run, envelope2, stepId, reason, observation) {
|
|
24717
|
+
const ids = requireIntent(context, run, envelope2, stepId);
|
|
24301
24718
|
if (observation.completeness !== "incomplete") {
|
|
24302
24719
|
throw runtimeError("gate_row_invalid", run.runId, "uncertainty observation must be incomplete");
|
|
24303
24720
|
}
|
|
@@ -24326,8 +24743,8 @@ function recordGateUncertainInTransaction(context, run, envelope, stepId, reason
|
|
|
24326
24743
|
context.eventStore.insertGateObservation(observationRow);
|
|
24327
24744
|
return persist(context, run, now, seq.events.at(-1).sequence);
|
|
24328
24745
|
}
|
|
24329
|
-
function recordGateCancelObservedInTransaction(context, run,
|
|
24330
|
-
const ids = requireIntent(context, run,
|
|
24746
|
+
function recordGateCancelObservedInTransaction(context, run, envelope2, stepId, observation) {
|
|
24747
|
+
const ids = requireIntent(context, run, envelope2, stepId);
|
|
24331
24748
|
assertClosedGateObservation(ids.gateKind, observation);
|
|
24332
24749
|
if (observation.completeness !== "complete") {
|
|
24333
24750
|
throw runtimeError("gate_row_invalid", run.runId, "cancel-after-observation requires a complete observation");
|
|
@@ -24386,19 +24803,19 @@ function recordGateCancelObservedInTransaction(context, run, envelope, stepId, o
|
|
|
24386
24803
|
context.eventStore.settleCapability(ids.attemptId, "settled");
|
|
24387
24804
|
return persist(context, run, now, seq.events.at(-1).sequence);
|
|
24388
24805
|
}
|
|
24389
|
-
function appendEvaluatedTail(seq,
|
|
24806
|
+
function appendEvaluatedTail(seq, envelope2, ids, stepId, outcome) {
|
|
24390
24807
|
const mapped = outcome === "passed" ? "passed" : outcome === "repro-missing" ? "failed" : "failed";
|
|
24391
24808
|
seq.push("assignment.result_recorded", { assignmentId: ids.assignmentId, resultClass: "outcome", outcome, status: "result_recorded" });
|
|
24392
24809
|
seq.push("attempt.status_changed", { attemptId: ids.attemptId, status: "terminal" });
|
|
24393
24810
|
seq.push("assignment.terminal", { assignmentId: ids.assignmentId, outcome, status: "terminal" });
|
|
24394
24811
|
seq.push("step.outcome_recorded", { stepId, stepAttempt: ids.stepAttempt, outcome });
|
|
24395
24812
|
seq.push("step.status_changed", { stepId, status: mapped, previousStatus: "running" });
|
|
24396
|
-
const selected =
|
|
24813
|
+
const selected = envelope2.plan.steps[stepId].transitions[outcome];
|
|
24397
24814
|
if (!selected) {
|
|
24398
24815
|
seq.push("run.status_changed", { status: "failed", reason: "outcome_unknown", stepId, outcome });
|
|
24399
24816
|
return;
|
|
24400
24817
|
}
|
|
24401
|
-
const budget = kxmTransitionBudgetFailure(
|
|
24818
|
+
const budget = kxmTransitionBudgetFailure(envelope2.plan, ids.state, stepId, outcome);
|
|
24402
24819
|
if (budget) {
|
|
24403
24820
|
seq.push("run.status_changed", { status: "failed", reason: budget, stepId, outcome });
|
|
24404
24821
|
return;
|
|
@@ -24410,8 +24827,8 @@ function appendEvaluatedTail(seq, envelope, ids, stepId, outcome) {
|
|
|
24410
24827
|
seq.push("run.status_changed", { status: selected.terminalStatus });
|
|
24411
24828
|
}
|
|
24412
24829
|
}
|
|
24413
|
-
function evidenceKeyFor(
|
|
24414
|
-
const required =
|
|
24830
|
+
function evidenceKeyFor(envelope2, stepId) {
|
|
24831
|
+
const required = envelope2.plan.steps[stepId]?.requiredEvidence.filter((item) => item.kind === "gate") ?? [];
|
|
24415
24832
|
return required.length === 1 ? required[0].key : null;
|
|
24416
24833
|
}
|
|
24417
24834
|
function placeholderReceipt(observationId, kind, now) {
|
|
@@ -24444,13 +24861,13 @@ function buildObservation(ids, observation, observationId, recordedEventId) {
|
|
|
24444
24861
|
};
|
|
24445
24862
|
return { ...row, contentHash: gateRowContentHash("gate_observations", { ...row, contentHash: "" }) };
|
|
24446
24863
|
}
|
|
24447
|
-
function requireGateStep(
|
|
24448
|
-
const step =
|
|
24864
|
+
function requireGateStep(envelope2, stepId, runId) {
|
|
24865
|
+
const step = envelope2.plan.steps[stepId];
|
|
24449
24866
|
if (!step || step.kind !== "gate") throw runtimeError("run_events_illegal", runId, `step ${stepId} is not a gate`);
|
|
24450
24867
|
return step;
|
|
24451
24868
|
}
|
|
24452
|
-
function requireIntent(context, run,
|
|
24453
|
-
const step = requireGateStep(
|
|
24869
|
+
function requireIntent(context, run, envelope2, stepId) {
|
|
24870
|
+
const step = requireGateStep(envelope2, stepId, run.runId);
|
|
24454
24871
|
const folded = foldStoredKxmRun(context, run);
|
|
24455
24872
|
const attemptId = folded.currentStep?.attemptId;
|
|
24456
24873
|
if (!attemptId || folded.currentStep?.stepId !== stepId) {
|
|
@@ -24518,7 +24935,7 @@ function hashFromStore(context, runId) {
|
|
|
24518
24935
|
}
|
|
24519
24936
|
|
|
24520
24937
|
// plugins/kxm/src/routing.ts
|
|
24521
|
-
import { createHash as
|
|
24938
|
+
import { createHash as createHash11 } from "node:crypto";
|
|
24522
24939
|
var ROUTING_RECORD_V2_SCHEMA = "kxm.routing-record.v2";
|
|
24523
24940
|
var BEHAVIORAL_HASH_VERSION = 1;
|
|
24524
24941
|
var MAX_PROVIDER_METADATA_FIELDS = 32;
|
|
@@ -24541,7 +24958,7 @@ function behavioralConfigHash(input) {
|
|
|
24541
24958
|
workflowDefinitionSha256: input.workflowDefinitionSha256?.trim() ?? null,
|
|
24542
24959
|
verifierConfigSha256: input.verifierConfigSha256?.trim() ?? null
|
|
24543
24960
|
};
|
|
24544
|
-
return
|
|
24961
|
+
return createHash11("sha256").update(JSON.stringify(canonical), "utf8").digest("hex");
|
|
24545
24962
|
}
|
|
24546
24963
|
function boundedInt(value, field) {
|
|
24547
24964
|
if (value === void 0 || value === null) return void 0;
|
|
@@ -24710,7 +25127,7 @@ function requireTrustedProducer(producer) {
|
|
|
24710
25127
|
}
|
|
24711
25128
|
}
|
|
24712
25129
|
function hashCapabilitySecret(secret) {
|
|
24713
|
-
return `sha256:${
|
|
25130
|
+
return `sha256:${createHash12("sha256").update(`${CAPABILITY_PREFIX}${secret}`, "utf8").digest("hex")}`;
|
|
24714
25131
|
}
|
|
24715
25132
|
function mintKxmCapabilitySecret() {
|
|
24716
25133
|
const secret = `kxmcap_${randomBytes2(32).toString("base64url")}`;
|
|
@@ -24739,7 +25156,7 @@ function pinKxmCompiledPlan(context, bundle, runId, options) {
|
|
|
24739
25156
|
value: workflow.value,
|
|
24740
25157
|
...workflow.logicalPath !== void 0 ? { logicalPath: workflow.logicalPath } : {}
|
|
24741
25158
|
}));
|
|
24742
|
-
const
|
|
25159
|
+
const envelope2 = {
|
|
24743
25160
|
schema: KXM_RUN_PLAN_SCHEMA,
|
|
24744
25161
|
runId: run.runId,
|
|
24745
25162
|
projectId: run.projectId,
|
|
@@ -24755,7 +25172,7 @@ function pinKxmCompiledPlan(context, bundle, runId, options) {
|
|
|
24755
25172
|
plan: compiled,
|
|
24756
25173
|
gates: pinnedGatesForCompiledPlan(compiled, bundle, context.projectRoot)
|
|
24757
25174
|
};
|
|
24758
|
-
const runPlanHash = hashKxmRunPlanEnvelope(
|
|
25175
|
+
const runPlanHash = hashKxmRunPlanEnvelope(envelope2);
|
|
24759
25176
|
const existing = context.eventStore.runPlan(runId);
|
|
24760
25177
|
if (existing) {
|
|
24761
25178
|
if (existing.runPlanHash !== runPlanHash) {
|
|
@@ -24778,7 +25195,7 @@ function pinKxmCompiledPlan(context, bundle, runId, options) {
|
|
|
24778
25195
|
context.eventStore.insertRunPlan({
|
|
24779
25196
|
runId,
|
|
24780
25197
|
runPlanHash,
|
|
24781
|
-
envelope: kxmCanonicalJson(
|
|
25198
|
+
envelope: kxmCanonicalJson(envelope2),
|
|
24782
25199
|
pinnedSequence: sequence
|
|
24783
25200
|
});
|
|
24784
25201
|
context.eventStore.appendEvent(event);
|
|
@@ -24791,8 +25208,8 @@ function pinKxmCompiledPlan(context, bundle, runId, options) {
|
|
|
24791
25208
|
function startKxmRun(context, runId, options = {}) {
|
|
24792
25209
|
return context.eventStore.transaction(() => {
|
|
24793
25210
|
const run = requireRun(context, runId);
|
|
24794
|
-
const
|
|
24795
|
-
const unsupported = options.allowLimits ? void 0 : unsupportedLimit(
|
|
25211
|
+
const envelope2 = loadKxmRunPlanEnvelope(context.eventStore, run);
|
|
25212
|
+
const unsupported = options.allowLimits ? void 0 : unsupportedLimit(envelope2);
|
|
24796
25213
|
const state = foldStoredKxmRun(context, run);
|
|
24797
25214
|
if (unsupported) {
|
|
24798
25215
|
return { state, handoff: unsupported };
|
|
@@ -24839,7 +25256,7 @@ async function driveAdmitted(context, runId, producer, token, options = {}) {
|
|
|
24839
25256
|
return latest;
|
|
24840
25257
|
}
|
|
24841
25258
|
function newDriveId(runId, token, runtimeId, monotonicNs) {
|
|
24842
|
-
return `drv_${
|
|
25259
|
+
return `drv_${createHash12("sha256").update(`${runId}\0${token}\0${runtimeId}\0${monotonicNs}`, "utf8").digest("hex").slice(0, 24)}`;
|
|
24843
25260
|
}
|
|
24844
25261
|
function driveErrorSummary(error) {
|
|
24845
25262
|
const raw = error instanceof KxmConfigError ? error.issues[0]?.code : void 0;
|
|
@@ -25031,12 +25448,12 @@ var KxmRunScheduler = class _KxmRunScheduler {
|
|
|
25031
25448
|
}
|
|
25032
25449
|
try {
|
|
25033
25450
|
const run = requireRun(this.context, runId);
|
|
25034
|
-
const
|
|
25451
|
+
const envelope2 = loadKxmRunPlanEnvelope(this.context.eventStore, run);
|
|
25035
25452
|
return enqueueKxmScheduledRun(
|
|
25036
25453
|
this.context.eventStore.path,
|
|
25037
25454
|
runId,
|
|
25038
|
-
|
|
25039
|
-
|
|
25455
|
+
envelope2.revisions.config,
|
|
25456
|
+
envelope2.projectLimits.maxConcurrentRuns,
|
|
25040
25457
|
(token) => driveAdmitted(this.context, runId, producer, token, options)
|
|
25041
25458
|
);
|
|
25042
25459
|
} catch (error) {
|
|
@@ -25066,8 +25483,8 @@ var KxmRunScheduler = class _KxmRunScheduler {
|
|
|
25066
25483
|
resolve: (_value) => void 0,
|
|
25067
25484
|
reject: (_error) => void 0
|
|
25068
25485
|
};
|
|
25069
|
-
const settled = new Promise((
|
|
25070
|
-
pending.resolve =
|
|
25486
|
+
const settled = new Promise((resolve10, reject) => {
|
|
25487
|
+
pending.resolve = resolve10;
|
|
25071
25488
|
pending.reject = reject;
|
|
25072
25489
|
});
|
|
25073
25490
|
void settled.then(void 0, () => void 0);
|
|
@@ -25606,10 +26023,10 @@ function prepareDispatch(context, runId, producerId) {
|
|
|
25606
26023
|
return { kind: "return", ...failBudget(context, run, plan, state, "budget_unmetered_attempts", stepId) };
|
|
25607
26024
|
}
|
|
25608
26025
|
if (step.kind === "gate") {
|
|
25609
|
-
const
|
|
25610
|
-
const unsupported2 = unsupportedGateStep(plan, step,
|
|
26026
|
+
const envelope2 = loadKxmRunPlanEnvelope(context.eventStore, run);
|
|
26027
|
+
const unsupported2 = unsupportedGateStep(plan, step, envelope2, context);
|
|
25611
26028
|
if (unsupported2) return { kind: "return", state, handoff: { ...unsupported2, stepId } };
|
|
25612
|
-
const definition =
|
|
26029
|
+
const definition = envelope2.gates.definitions[step.gate];
|
|
25613
26030
|
if (definition?.kind === "command") {
|
|
25614
26031
|
const preflightResult = gateRecoveryPreflight(context);
|
|
25615
26032
|
const firstBlocking = preflightResult.blocking[0];
|
|
@@ -25625,7 +26042,7 @@ function prepareDispatch(context, runId, producerId) {
|
|
|
25625
26042
|
};
|
|
25626
26043
|
}
|
|
25627
26044
|
}
|
|
25628
|
-
const result = recordGateIntentInTransaction(context, run,
|
|
26045
|
+
const result = recordGateIntentInTransaction(context, run, envelope2, stepId);
|
|
25629
26046
|
const current = result.state.currentStep;
|
|
25630
26047
|
const attemptId = current?.attemptId;
|
|
25631
26048
|
const assignmentId = current?.assignmentId;
|
|
@@ -25636,7 +26053,7 @@ function prepareDispatch(context, runId, producerId) {
|
|
|
25636
26053
|
return {
|
|
25637
26054
|
kind: "gate",
|
|
25638
26055
|
run,
|
|
25639
|
-
envelope,
|
|
26056
|
+
envelope: envelope2,
|
|
25640
26057
|
stepId,
|
|
25641
26058
|
stepAttempt: current.stepAttempt,
|
|
25642
26059
|
assignmentId,
|
|
@@ -25742,7 +26159,7 @@ function birthMember(context, input) {
|
|
|
25742
26159
|
if (promptText === void 0) {
|
|
25743
26160
|
throw runtimeError("run_prompt_mismatch", run.runId, "prompt text missing from accepted run prompt store");
|
|
25744
26161
|
}
|
|
25745
|
-
const promptHash =
|
|
26162
|
+
const promptHash = createHash12("sha256").update(promptText, "utf8").digest("hex");
|
|
25746
26163
|
const expectedHash = run.promptSha256.replace(/^sha256:/, "");
|
|
25747
26164
|
if (promptHash !== expectedHash) {
|
|
25748
26165
|
throw runtimeError("run_prompt_mismatch", run.runId, "prompt text does not match accepted promptSha256");
|
|
@@ -26120,12 +26537,12 @@ function producerRoutingRecord(context, dispatch, result, now) {
|
|
|
26120
26537
|
if (result.costBasis === "metered" && (typeof result.costUsd !== "number" || !Number.isFinite(result.costUsd) || result.costUsd < 0)) {
|
|
26121
26538
|
throw runtimeError("settle_invalid_cost", run.runId, "attempt settlement rejected: metered costBasis requires non-negative finite costUsd");
|
|
26122
26539
|
}
|
|
26123
|
-
const
|
|
26540
|
+
const envelope2 = loadKxmRunPlanEnvelope(context.eventStore, run);
|
|
26124
26541
|
const rawHash = result.behavioralSha256 ?? behavioralConfigHash({
|
|
26125
26542
|
requestedModel: result.requestedModel ?? "simulated",
|
|
26126
26543
|
effectiveModel: result.effectiveModel ?? result.requestedModel ?? "simulated",
|
|
26127
26544
|
agentRole: dispatch.step.kind === "agent" || dispatch.step.kind === "moa" ? dispatch.step.agent : void 0,
|
|
26128
|
-
toolPolicyVersion:
|
|
26545
|
+
toolPolicyVersion: envelope2.revisions.toolPolicy
|
|
26129
26546
|
});
|
|
26130
26547
|
const record2 = {
|
|
26131
26548
|
schema: ROUTING_RECORD_V2_SCHEMA,
|
|
@@ -26186,12 +26603,12 @@ function settleMember(context, dispatch, result, produceError, produceErrorCode)
|
|
|
26186
26603
|
push("attempt.status_changed", { attemptId: dispatch.attemptId, status: "terminal" });
|
|
26187
26604
|
push("assignment.terminal", { assignmentId: dispatch.assignmentId, outcome: "cancelled", status: "terminal" });
|
|
26188
26605
|
} else if (produceError) {
|
|
26189
|
-
const
|
|
26606
|
+
const envelope2 = loadKxmRunPlanEnvelope(context.eventStore, run);
|
|
26190
26607
|
const rawHash = result?.behavioralSha256 ?? behavioralConfigHash({
|
|
26191
26608
|
requestedModel: result?.requestedModel ?? "simulated",
|
|
26192
26609
|
effectiveModel: result?.effectiveModel ?? result?.requestedModel ?? "simulated",
|
|
26193
26610
|
agentRole: dispatch.step.kind === "agent" || dispatch.step.kind === "moa" ? dispatch.step.agent : void 0,
|
|
26194
|
-
toolPolicyVersion:
|
|
26611
|
+
toolPolicyVersion: envelope2.revisions.toolPolicy
|
|
26195
26612
|
});
|
|
26196
26613
|
const behavioralSha256 = rawHash.startsWith("sha256:") ? rawHash : `sha256:${rawHash}`;
|
|
26197
26614
|
const routingRecord = {
|
|
@@ -26406,8 +26823,8 @@ function declaredRunDurationBudget(context, runId) {
|
|
|
26406
26823
|
const run = context.eventStore.run(runId);
|
|
26407
26824
|
if (!run) return void 0;
|
|
26408
26825
|
try {
|
|
26409
|
-
const
|
|
26410
|
-
return effectiveRunDurationBudget(
|
|
26826
|
+
const envelope2 = loadKxmRunPlanEnvelope(context.eventStore, run);
|
|
26827
|
+
return effectiveRunDurationBudget(envelope2.plan.limits, envelope2.projectLimits);
|
|
26411
26828
|
} catch {
|
|
26412
26829
|
return void 0;
|
|
26413
26830
|
}
|
|
@@ -26513,11 +26930,11 @@ function armRunDurationBudgetTimer(context, runId) {
|
|
|
26513
26930
|
unhook();
|
|
26514
26931
|
};
|
|
26515
26932
|
}
|
|
26516
|
-
function unsupportedLimit(
|
|
26517
|
-
if (
|
|
26933
|
+
function unsupportedLimit(envelope2) {
|
|
26934
|
+
if (envelope2.plan.limits.maxAgentTimeMs !== void 0) {
|
|
26518
26935
|
return { reason: "limit_unsupported", field: "limits.maxAgentTimeMs", detail: "agent-time budget enforcement is not available in this slice" };
|
|
26519
26936
|
}
|
|
26520
|
-
if (
|
|
26937
|
+
if (envelope2.projectLimits.maxAgentTimeMs !== void 0) {
|
|
26521
26938
|
return { reason: "limit_unsupported", field: "project.limits.maxAgentTimeMs", detail: "agent-time budget enforcement is not available in this slice" };
|
|
26522
26939
|
}
|
|
26523
26940
|
return void 0;
|
|
@@ -26570,12 +26987,12 @@ function unsupportedStep(plan, step, producerId) {
|
|
|
26570
26987
|
}
|
|
26571
26988
|
return void 0;
|
|
26572
26989
|
}
|
|
26573
|
-
function unsupportedGateStep(plan, step,
|
|
26990
|
+
function unsupportedGateStep(plan, step, envelope2, context) {
|
|
26574
26991
|
if (!step.gate) return { reason: "step_unsupported", field: "gate", detail: "gate step is missing gate id" };
|
|
26575
|
-
if (
|
|
26992
|
+
if (envelope2.gates.registry === null) {
|
|
26576
26993
|
return { reason: "step_unsupported", field: "gate", detail: `step ${step.id} refers to a gate but registry is null` };
|
|
26577
26994
|
}
|
|
26578
|
-
const definition =
|
|
26995
|
+
const definition = envelope2.gates.definitions[step.gate];
|
|
26579
26996
|
if (!definition) {
|
|
26580
26997
|
return { reason: "step_unsupported", field: "gate", detail: `gate definition ${step.gate} not found in registry` };
|
|
26581
26998
|
}
|
|
@@ -26681,7 +27098,7 @@ function unsupportedGateStep(plan, step, envelope, context) {
|
|
|
26681
27098
|
return { reason: "gate_outcome_undeclared", field: "outcomes", detail: "step expects fail but missing required failure outcome; declare implementation-failure or failed" };
|
|
26682
27099
|
}
|
|
26683
27100
|
}
|
|
26684
|
-
if (context && projectRuntimeKey(context.projectRoot) !==
|
|
27101
|
+
if (context && projectRuntimeKey(context.projectRoot) !== envelope2.gates.controlRoot.projectKey) {
|
|
26685
27102
|
throw runtimeError("run_owner_mismatch", plan.workflowId, `gate control root does not match project`);
|
|
26686
27103
|
}
|
|
26687
27104
|
if (definition.kind === "artifacts-exist" && step.expect === "fail") {
|
|
@@ -27320,28 +27737,293 @@ Return a final JSON object with an "outcome" field chosen from ${JSON.stringify(
|
|
|
27320
27737
|
return producer;
|
|
27321
27738
|
}
|
|
27322
27739
|
|
|
27740
|
+
// plugins/kxm/src/client.ts
|
|
27741
|
+
import { hostname } from "node:os";
|
|
27742
|
+
|
|
27743
|
+
// plugins/kxm/src/protocol.ts
|
|
27744
|
+
var DEFAULT_MESSAGE_TTL_MS = 24 * 60 * 6e4;
|
|
27745
|
+
var MAX_MESSAGE_TTL_MS = 7 * 24 * 60 * 6e4;
|
|
27746
|
+
var DEFAULT_MESSAGE_RETENTION_MS = 7 * 24 * 60 * 6e4;
|
|
27747
|
+
var MAX_BODY_BYTES = 256 * 1024;
|
|
27748
|
+
var MAX_AGENT_HOST_CHARS = 64;
|
|
27749
|
+
var MAX_LEASE_TTL_MS = 10 * 6e4;
|
|
27750
|
+
var DEFAULT_LEASE_TTL_MS = 5 * 6e4;
|
|
27751
|
+
function nowIso() {
|
|
27752
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
27753
|
+
}
|
|
27754
|
+
|
|
27755
|
+
// plugins/kxm/src/client.ts
|
|
27756
|
+
function defaultHostLabel() {
|
|
27757
|
+
try {
|
|
27758
|
+
return hostname().trim().slice(0, MAX_AGENT_HOST_CHARS) || void 0;
|
|
27759
|
+
} catch {
|
|
27760
|
+
return void 0;
|
|
27761
|
+
}
|
|
27762
|
+
}
|
|
27763
|
+
var HubHttpError = class extends Error {
|
|
27764
|
+
statusCode;
|
|
27765
|
+
code;
|
|
27766
|
+
requestId;
|
|
27767
|
+
extras;
|
|
27768
|
+
constructor(statusCode, message, code, requestId, extras) {
|
|
27769
|
+
super(message);
|
|
27770
|
+
this.name = "HubHttpError";
|
|
27771
|
+
this.statusCode = statusCode;
|
|
27772
|
+
if (code) this.code = code;
|
|
27773
|
+
if (requestId) this.requestId = requestId;
|
|
27774
|
+
if (extras) this.extras = extras;
|
|
27775
|
+
}
|
|
27776
|
+
};
|
|
27777
|
+
async function hubJsonRequest(options, path, init, headers) {
|
|
27778
|
+
const requestTimeoutMs = options.requestTimeoutMs ?? 15e3;
|
|
27779
|
+
const timeoutSignal = AbortSignal.timeout(requestTimeoutMs);
|
|
27780
|
+
const signal = init.signal ? AbortSignal.any([init.signal, timeoutSignal]) : timeoutSignal;
|
|
27781
|
+
let response;
|
|
27782
|
+
try {
|
|
27783
|
+
response = await (options.fetchImpl ?? fetch)(`${options.serverUrl.replace(/\/$/, "")}${path}`, {
|
|
27784
|
+
...init,
|
|
27785
|
+
signal,
|
|
27786
|
+
headers: { ...headers, ...init.headers ?? {} }
|
|
27787
|
+
});
|
|
27788
|
+
} catch (error) {
|
|
27789
|
+
if (timeoutSignal.aborted) throw new Error(`request timed out after ${requestTimeoutMs}ms`);
|
|
27790
|
+
throw error;
|
|
27791
|
+
}
|
|
27792
|
+
const text = await response.text();
|
|
27793
|
+
let body = {};
|
|
27794
|
+
if (text) {
|
|
27795
|
+
try {
|
|
27796
|
+
body = JSON.parse(text);
|
|
27797
|
+
} catch {
|
|
27798
|
+
throw new Error(`hub returned invalid JSON with HTTP ${response.status}`);
|
|
27799
|
+
}
|
|
27800
|
+
}
|
|
27801
|
+
if (!response.ok) {
|
|
27802
|
+
const extras = {};
|
|
27803
|
+
for (const key of ["operation", "nextAction", "assignedCoordinatorName"]) {
|
|
27804
|
+
if (typeof body[key] === "string") extras[key] = body[key];
|
|
27805
|
+
}
|
|
27806
|
+
if (body.lease && typeof body.lease === "object") extras.lease = body.lease;
|
|
27807
|
+
throw new HubHttpError(
|
|
27808
|
+
response.status,
|
|
27809
|
+
String(body.error ?? `HTTP ${response.status}`),
|
|
27810
|
+
typeof body.code === "string" ? body.code : void 0,
|
|
27811
|
+
response.headers.get("x-request-id") ?? void 0,
|
|
27812
|
+
Object.keys(extras).length > 0 ? extras : void 0
|
|
27813
|
+
);
|
|
27814
|
+
}
|
|
27815
|
+
return body;
|
|
27816
|
+
}
|
|
27817
|
+
var RuntimeHubClient = class {
|
|
27818
|
+
options;
|
|
27819
|
+
constructor(options) {
|
|
27820
|
+
this.options = options;
|
|
27821
|
+
}
|
|
27822
|
+
async heartbeat() {
|
|
27823
|
+
const result = await this.request("/v1/runtime/presence", {
|
|
27824
|
+
project: this.options.project,
|
|
27825
|
+
runtimeId: this.options.runtimeId,
|
|
27826
|
+
host: this.options.host ?? defaultHostLabel()
|
|
27827
|
+
});
|
|
27828
|
+
return result.presence;
|
|
27829
|
+
}
|
|
27830
|
+
/** Push already-derived `kxm.sync-event.v1` objects, in outbox order. */
|
|
27831
|
+
async pushSyncEvents(events) {
|
|
27832
|
+
return await this.request("/v1/sync/events", {
|
|
27833
|
+
project: this.options.project,
|
|
27834
|
+
runtimeId: this.options.runtimeId,
|
|
27835
|
+
events
|
|
27836
|
+
});
|
|
27837
|
+
}
|
|
27838
|
+
async request(path, body) {
|
|
27839
|
+
const headers = { "content-type": "application/json" };
|
|
27840
|
+
if (this.options.authToken) headers.authorization = `Bearer ${this.options.authToken}`;
|
|
27841
|
+
return await hubJsonRequest(this.options, path, { method: "POST", body: JSON.stringify(body) }, headers);
|
|
27842
|
+
}
|
|
27843
|
+
};
|
|
27844
|
+
|
|
27845
|
+
// plugins/kxm/src/hub-binding.ts
|
|
27846
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync4, readFileSync as readFileSync8, renameSync, rmSync, writeFileSync as writeFileSync4 } from "node:fs";
|
|
27847
|
+
import { homedir as homedir2 } from "node:os";
|
|
27848
|
+
import { dirname as dirname6, isAbsolute as isAbsolute4, join as join13, resolve as resolve6 } from "node:path";
|
|
27849
|
+
var HUB_BINDING_SCHEMA = "kxm.hub-binding.v1";
|
|
27850
|
+
var HubBindingError = class extends Error {
|
|
27851
|
+
constructor(message) {
|
|
27852
|
+
super(message);
|
|
27853
|
+
this.name = "HubBindingError";
|
|
27854
|
+
}
|
|
27855
|
+
};
|
|
27856
|
+
function resolveUserStateRoot(env) {
|
|
27857
|
+
const explicit = env.KXM_STATE_HOME?.trim();
|
|
27858
|
+
if (explicit) {
|
|
27859
|
+
if (!isAbsolute4(explicit)) throw new HubBindingError("local_state_root_not_absolute");
|
|
27860
|
+
return resolve6(explicit);
|
|
27861
|
+
}
|
|
27862
|
+
if (process.platform === "win32") {
|
|
27863
|
+
const localAppData = env.LOCALAPPDATA?.trim();
|
|
27864
|
+
const base2 = localAppData && isAbsolute4(localAppData) ? localAppData : join13(homedir2(), "AppData", "Local");
|
|
27865
|
+
return resolve6(base2, "KXM");
|
|
27866
|
+
}
|
|
27867
|
+
if (process.platform === "darwin") return resolve6(homedir2(), "Library", "Application Support", "KXM");
|
|
27868
|
+
const xdgState = env.XDG_STATE_HOME?.trim();
|
|
27869
|
+
const base = xdgState && isAbsolute4(xdgState) ? xdgState : join13(homedir2(), ".local", "state");
|
|
27870
|
+
return resolve6(base, "kxm");
|
|
27871
|
+
}
|
|
27872
|
+
function hubBindingFile(env = process.env) {
|
|
27873
|
+
return join13(resolveUserStateRoot(env), "hub-binding.json");
|
|
27874
|
+
}
|
|
27875
|
+
function validateHubUrl(raw) {
|
|
27876
|
+
let parsed;
|
|
27877
|
+
try {
|
|
27878
|
+
parsed = new URL(raw);
|
|
27879
|
+
} catch {
|
|
27880
|
+
throw new HubBindingError("hub_url_invalid");
|
|
27881
|
+
}
|
|
27882
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:" || parsed.username !== "" || parsed.password !== "" || parsed.search !== "" || parsed.hash !== "" || raw.includes("?") || raw.includes("#")) {
|
|
27883
|
+
throw new HubBindingError("hub_url_invalid");
|
|
27884
|
+
}
|
|
27885
|
+
return parsed.href.replace(/\/$/, "");
|
|
27886
|
+
}
|
|
27887
|
+
function isIsoTimestamp(value) {
|
|
27888
|
+
if (Number.isNaN(Date.parse(value))) return false;
|
|
27889
|
+
return value === new Date(value).toISOString();
|
|
27890
|
+
}
|
|
27891
|
+
function readHubBinding(env = process.env) {
|
|
27892
|
+
const file = hubBindingFile(env);
|
|
27893
|
+
if (!existsSync10(file)) return void 0;
|
|
27894
|
+
let parsed;
|
|
27895
|
+
try {
|
|
27896
|
+
parsed = JSON.parse(readFileSync8(file, "utf8"));
|
|
27897
|
+
} catch {
|
|
27898
|
+
throw new HubBindingError(`malformed hub binding at ${file}`);
|
|
27899
|
+
}
|
|
27900
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
27901
|
+
throw new HubBindingError(`malformed hub binding at ${file}`);
|
|
27902
|
+
}
|
|
27903
|
+
const row = parsed;
|
|
27904
|
+
const keys = Object.keys(row);
|
|
27905
|
+
if (keys.length !== 3 || row.schema !== HUB_BINDING_SCHEMA || typeof row.url !== "string" || typeof row.boundAt !== "string" || !isIsoTimestamp(row.boundAt)) {
|
|
27906
|
+
throw new HubBindingError(`malformed hub binding at ${file}`);
|
|
27907
|
+
}
|
|
27908
|
+
let url;
|
|
27909
|
+
try {
|
|
27910
|
+
url = validateHubUrl(row.url);
|
|
27911
|
+
} catch {
|
|
27912
|
+
throw new HubBindingError(`malformed hub binding at ${file}`);
|
|
27913
|
+
}
|
|
27914
|
+
return { schema: HUB_BINDING_SCHEMA, url, boundAt: row.boundAt };
|
|
27915
|
+
}
|
|
27916
|
+
|
|
27917
|
+
// plugins/kxm/src/hub-env.ts
|
|
27918
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync5, readFileSync as readFileSync9, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "node:fs";
|
|
27919
|
+
import { homedir as homedir3 } from "node:os";
|
|
27920
|
+
import { dirname as dirname7, isAbsolute as isAbsolute5, join as join14, resolve as resolve7 } from "node:path";
|
|
27921
|
+
var HUB_ENV_SCHEMA = "kxm.hub-env.v1";
|
|
27922
|
+
var HubEnvError = class extends Error {
|
|
27923
|
+
constructor(message) {
|
|
27924
|
+
super(message);
|
|
27925
|
+
this.name = "HubEnvError";
|
|
27926
|
+
}
|
|
27927
|
+
};
|
|
27928
|
+
function resolveUserStateRoot2(env) {
|
|
27929
|
+
const explicit = env.KXM_STATE_HOME?.trim();
|
|
27930
|
+
if (explicit) {
|
|
27931
|
+
if (!isAbsolute5(explicit)) throw new HubEnvError("local_state_root_not_absolute");
|
|
27932
|
+
return resolve7(explicit);
|
|
27933
|
+
}
|
|
27934
|
+
if (process.platform === "win32") {
|
|
27935
|
+
const localAppData = env.LOCALAPPDATA?.trim();
|
|
27936
|
+
const base2 = localAppData && isAbsolute5(localAppData) ? localAppData : join14(homedir3(), "AppData", "Local");
|
|
27937
|
+
return resolve7(base2, "KXM");
|
|
27938
|
+
}
|
|
27939
|
+
if (process.platform === "darwin") return resolve7(homedir3(), "Library", "Application Support", "KXM");
|
|
27940
|
+
const xdgState = env.XDG_STATE_HOME?.trim();
|
|
27941
|
+
const base = xdgState && isAbsolute5(xdgState) ? xdgState : join14(homedir3(), ".local", "state");
|
|
27942
|
+
return resolve7(base, "kxm");
|
|
27943
|
+
}
|
|
27944
|
+
function hubEnvFile(env = process.env) {
|
|
27945
|
+
return join14(resolveUserStateRoot2(env), "hub-env.json");
|
|
27946
|
+
}
|
|
27947
|
+
function readHubEnvRecord(env = process.env) {
|
|
27948
|
+
const file = hubEnvFile(env);
|
|
27949
|
+
if (!existsSync11(file)) return void 0;
|
|
27950
|
+
let parsed;
|
|
27951
|
+
try {
|
|
27952
|
+
parsed = JSON.parse(readFileSync9(file, "utf8"));
|
|
27953
|
+
} catch (error) {
|
|
27954
|
+
throw new HubEnvError(`hub env file is malformed at ${file}: ${error instanceof Error ? error.message : String(error)}`);
|
|
27955
|
+
}
|
|
27956
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
27957
|
+
throw new HubEnvError(`hub env file is malformed at ${file}: expected a JSON object`);
|
|
27958
|
+
}
|
|
27959
|
+
const record2 = parsed;
|
|
27960
|
+
if (record2.schema !== HUB_ENV_SCHEMA) {
|
|
27961
|
+
throw new HubEnvError(`hub env file at ${file} has unsupported schema ${String(record2.schema)}`);
|
|
27962
|
+
}
|
|
27963
|
+
if (record2.authToken !== void 0 && (typeof record2.authToken !== "string" || record2.authToken.trim().length === 0)) {
|
|
27964
|
+
throw new HubEnvError(`hub env file at ${file} has an invalid authToken`);
|
|
27965
|
+
}
|
|
27966
|
+
if (record2.projectTokens !== void 0) {
|
|
27967
|
+
const tokens = record2.projectTokens;
|
|
27968
|
+
if (!tokens || typeof tokens !== "object" || Array.isArray(tokens)) {
|
|
27969
|
+
throw new HubEnvError(`hub env file at ${file} has invalid projectTokens`);
|
|
27970
|
+
}
|
|
27971
|
+
for (const [project, token] of Object.entries(tokens)) {
|
|
27972
|
+
if (!project.trim() || typeof token !== "string" || !token.trim()) {
|
|
27973
|
+
throw new HubEnvError(`hub env file at ${file} has an empty project name or token`);
|
|
27974
|
+
}
|
|
27975
|
+
}
|
|
27976
|
+
}
|
|
27977
|
+
return {
|
|
27978
|
+
schema: HUB_ENV_SCHEMA,
|
|
27979
|
+
createdAt: typeof record2.createdAt === "string" ? record2.createdAt : "",
|
|
27980
|
+
...record2.authToken !== void 0 ? { authToken: record2.authToken } : {},
|
|
27981
|
+
...record2.projectTokens !== void 0 ? { projectTokens: record2.projectTokens } : {}
|
|
27982
|
+
};
|
|
27983
|
+
}
|
|
27984
|
+
function resolveClientHubAuthToken(env, project) {
|
|
27985
|
+
const envToken = env.KXM_AUTH_TOKEN?.trim();
|
|
27986
|
+
if (envToken) return envToken;
|
|
27987
|
+
const record2 = readHubEnvRecord(env);
|
|
27988
|
+
return record2?.projectTokens?.[project]?.trim() || record2?.authToken?.trim() || void 0;
|
|
27989
|
+
}
|
|
27990
|
+
|
|
27991
|
+
// plugins/kxm/src/project-name.ts
|
|
27992
|
+
import { readFileSync as readFileSync10 } from "node:fs";
|
|
27993
|
+
import { basename as basename3, join as join15 } from "node:path";
|
|
27994
|
+
function defaultProjectName(cwd, env = process.env) {
|
|
27995
|
+
const fromEnv = env.KXM_PROJECT?.trim();
|
|
27996
|
+
if (fromEnv) return fromEnv;
|
|
27997
|
+
try {
|
|
27998
|
+
const pkg = JSON.parse(readFileSync10(join15(cwd, "package.json"), "utf8"));
|
|
27999
|
+
if (typeof pkg.name === "string" && pkg.name.trim().length > 0) return pkg.name.trim();
|
|
28000
|
+
} catch {
|
|
28001
|
+
}
|
|
28002
|
+
return basename3(cwd);
|
|
28003
|
+
}
|
|
28004
|
+
|
|
27323
28005
|
// plugins/kxm/src/runtime-supervisor.ts
|
|
27324
28006
|
function kxmSupervisorTokenFile(paths) {
|
|
27325
|
-
return
|
|
28007
|
+
return join16(paths.runtimeDir, "supervisor.token");
|
|
27326
28008
|
}
|
|
27327
28009
|
function publishKxmSupervisorToken(paths, token) {
|
|
27328
28010
|
const file = kxmSupervisorTokenFile(paths);
|
|
27329
|
-
|
|
28011
|
+
mkdirSync6(dirname8(file), { recursive: true, mode: 448 });
|
|
27330
28012
|
const temp = `${file}.${process.pid}.tmp`;
|
|
27331
|
-
|
|
28013
|
+
writeFileSync6(temp, `${token}
|
|
27332
28014
|
`, { encoding: "utf8", mode: 384 });
|
|
27333
28015
|
try {
|
|
27334
28016
|
chmodSync2(temp, 384);
|
|
27335
28017
|
} catch {
|
|
27336
28018
|
}
|
|
27337
|
-
|
|
28019
|
+
renameSync3(temp, file);
|
|
27338
28020
|
try {
|
|
27339
28021
|
chmodSync2(file, 384);
|
|
27340
28022
|
} catch {
|
|
27341
28023
|
}
|
|
27342
28024
|
}
|
|
27343
28025
|
function hashKxmSupervisorToken(token) {
|
|
27344
|
-
return `sha256:${
|
|
28026
|
+
return `sha256:${createHash13("sha256").update(`kxm-runtime-supervisor\0${token}`, "utf8").digest("hex")}`;
|
|
27345
28027
|
}
|
|
27346
28028
|
function readKxmSupervisorToken(paths) {
|
|
27347
28029
|
const file = kxmSupervisorTokenFile(paths);
|
|
@@ -27350,7 +28032,7 @@ function readKxmSupervisorToken(paths) {
|
|
|
27350
28032
|
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
27351
28033
|
throw runtimeError("runtime_path_invalid", file, "supervisor token file must be a regular file, not a link");
|
|
27352
28034
|
}
|
|
27353
|
-
const token =
|
|
28035
|
+
const token = readFileSync11(file, "utf8").trim();
|
|
27354
28036
|
return token.length >= 32 ? token : void 0;
|
|
27355
28037
|
}
|
|
27356
28038
|
function processAlive(pid) {
|
|
@@ -27364,16 +28046,16 @@ function processAlive(pid) {
|
|
|
27364
28046
|
var HEARTBEAT_STALE_MS = 15e3;
|
|
27365
28047
|
var SUPERVISOR_ERROR_MAX_AGE_MS = 3e4;
|
|
27366
28048
|
function supervisorErrorFile(paths) {
|
|
27367
|
-
return
|
|
28049
|
+
return join16(paths.runtimeDir, "supervisor.error");
|
|
27368
28050
|
}
|
|
27369
28051
|
function clearSupervisorError(paths) {
|
|
27370
28052
|
const file = supervisorErrorFile(paths);
|
|
27371
|
-
if (
|
|
28053
|
+
if (existsSync12(file)) rmSync3(file, { force: true });
|
|
27372
28054
|
}
|
|
27373
28055
|
function recordSupervisorError(paths, message) {
|
|
27374
28056
|
try {
|
|
27375
|
-
|
|
27376
|
-
|
|
28057
|
+
mkdirSync6(paths.runtimeDir, { recursive: true, mode: 448 });
|
|
28058
|
+
writeFileSync6(supervisorErrorFile(paths), `${message}
|
|
27377
28059
|
`, { encoding: "utf8", mode: 384 });
|
|
27378
28060
|
} catch {
|
|
27379
28061
|
}
|
|
@@ -27385,13 +28067,13 @@ function readRecentSupervisorError(paths) {
|
|
|
27385
28067
|
const ageMs = Date.now() - stat.mtimeMs;
|
|
27386
28068
|
if (ageMs > SUPERVISOR_ERROR_MAX_AGE_MS) return void 0;
|
|
27387
28069
|
try {
|
|
27388
|
-
return
|
|
28070
|
+
return readFileSync11(file, "utf8").trim();
|
|
27389
28071
|
} catch {
|
|
27390
28072
|
return void 0;
|
|
27391
28073
|
}
|
|
27392
28074
|
}
|
|
27393
28075
|
function kxmSupervisorStatus(paths) {
|
|
27394
|
-
if (!
|
|
28076
|
+
if (!existsSync12(paths.registryDb)) return { running: false };
|
|
27395
28077
|
const registry = new KxmRuntimeRegistry(paths.registryDb);
|
|
27396
28078
|
try {
|
|
27397
28079
|
const record2 = registry.supervisor();
|
|
@@ -27461,7 +28143,7 @@ async function ensureKxmSupervisor(options = {}) {
|
|
|
27461
28143
|
throw runtimeError("runtime_supervisor_unreachable", paths.registryDb, `runtime supervisor pid ${status.pid} is registered as running but cannot be probed`);
|
|
27462
28144
|
}
|
|
27463
28145
|
clearSupervisorError(paths);
|
|
27464
|
-
const scriptPath =
|
|
28146
|
+
const scriptPath = join16(findKxmRepoRoot(import.meta.url), "scripts", "kxm-runtime-supervisor.mjs");
|
|
27465
28147
|
const spawnImpl = options.spawnImpl ?? ((script, env) => {
|
|
27466
28148
|
const child = spawn3(process.execPath, [script], {
|
|
27467
28149
|
detached: true,
|
|
@@ -27546,8 +28228,8 @@ async function waitForDriveSessions(settled, graceMs) {
|
|
|
27546
28228
|
try {
|
|
27547
28229
|
await Promise.race([
|
|
27548
28230
|
Promise.allSettled(settled),
|
|
27549
|
-
new Promise((
|
|
27550
|
-
timeout = setTimeout(
|
|
28231
|
+
new Promise((resolve10) => {
|
|
28232
|
+
timeout = setTimeout(resolve10, graceMs);
|
|
27551
28233
|
timeout.unref?.();
|
|
27552
28234
|
})
|
|
27553
28235
|
]);
|
|
@@ -27568,6 +28250,63 @@ async function driveSessionStillPending(settled) {
|
|
|
27568
28250
|
await Promise.resolve();
|
|
27569
28251
|
return pending;
|
|
27570
28252
|
}
|
|
28253
|
+
var DEFAULT_RUNTIME_SYNC_INTERVAL_MS = 1e4;
|
|
28254
|
+
var MIN_RUNTIME_SYNC_INTERVAL_MS = 250;
|
|
28255
|
+
var MAX_RUNTIME_SYNC_INTERVAL_MS = 6e4;
|
|
28256
|
+
var OUTBOX_PUSH_BATCH = 32;
|
|
28257
|
+
function runtimeSyncIntervalMs(env = process.env) {
|
|
28258
|
+
const raw = env.KXM_RUNTIME_SYNC_INTERVAL_MS?.trim();
|
|
28259
|
+
if (!raw) return DEFAULT_RUNTIME_SYNC_INTERVAL_MS;
|
|
28260
|
+
const parsed = Number(raw);
|
|
28261
|
+
if (!Number.isInteger(parsed)) return DEFAULT_RUNTIME_SYNC_INTERVAL_MS;
|
|
28262
|
+
return Math.min(MAX_RUNTIME_SYNC_INTERVAL_MS, Math.max(MIN_RUNTIME_SYNC_INTERVAL_MS, parsed));
|
|
28263
|
+
}
|
|
28264
|
+
async function syncKxmOutbox(eventStore, client, options = {}) {
|
|
28265
|
+
const now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
28266
|
+
const batchSize = options.batchSize ?? OUTBOX_PUSH_BATCH;
|
|
28267
|
+
const result = { pushed: 0, acked: 0, conflicts: 0, rejected: 0 };
|
|
28268
|
+
let afterSeq = 0;
|
|
28269
|
+
for (; ; ) {
|
|
28270
|
+
const rows = eventStore.pendingOutbox(batchSize, afterSeq);
|
|
28271
|
+
if (rows.length === 0) return result;
|
|
28272
|
+
const MAX_BATCH_BYTES = 2e5;
|
|
28273
|
+
let byteBudget = MAX_BATCH_BYTES;
|
|
28274
|
+
let sendCount = 0;
|
|
28275
|
+
for (const row of rows) {
|
|
28276
|
+
const rowBytes = Buffer.byteLength(row.syncEvent, "utf8") + 64;
|
|
28277
|
+
if (sendCount > 0 && byteBudget - rowBytes < 0) break;
|
|
28278
|
+
byteBudget -= rowBytes;
|
|
28279
|
+
sendCount += 1;
|
|
28280
|
+
}
|
|
28281
|
+
const batch = rows.slice(0, sendCount);
|
|
28282
|
+
if (batch.length === 0) batch.push(rows[0]);
|
|
28283
|
+
afterSeq = batch[batch.length - 1].seq;
|
|
28284
|
+
eventStore.markOutboxAttempted(batch.map((row) => row.seq), now());
|
|
28285
|
+
const response = await client.pushSyncEvents(batch.map((row) => JSON.parse(row.syncEvent)));
|
|
28286
|
+
result.pushed += batch.length;
|
|
28287
|
+
const outcomes = new Map(response.results.map((entry) => [`${entry.runId}\0${entry.sequence}`, entry.outcome]));
|
|
28288
|
+
const acked = [];
|
|
28289
|
+
for (const row of batch) {
|
|
28290
|
+
const outcome = outcomes.get(`${row.runId}\0${row.sequence}`);
|
|
28291
|
+
if (outcome === "accepted" || outcome === "duplicate") acked.push(row.seq);
|
|
28292
|
+
else if (outcome === "conflict") result.conflicts += 1;
|
|
28293
|
+
else result.rejected += 1;
|
|
28294
|
+
}
|
|
28295
|
+
result.acked += eventStore.ackOutbox(acked, now());
|
|
28296
|
+
}
|
|
28297
|
+
}
|
|
28298
|
+
function runtimeHubClientFor(context, env) {
|
|
28299
|
+
const serverUrl = env.KXM_SERVER_URL?.trim() || readHubBinding(env)?.url;
|
|
28300
|
+
if (!serverUrl) return void 0;
|
|
28301
|
+
const project = defaultProjectName(context.projectRoot, env);
|
|
28302
|
+
const authToken = resolveClientHubAuthToken(env, project);
|
|
28303
|
+
return new RuntimeHubClient({
|
|
28304
|
+
serverUrl,
|
|
28305
|
+
project,
|
|
28306
|
+
runtimeId: context.homeRuntimeId,
|
|
28307
|
+
...authToken ? { authToken } : {}
|
|
28308
|
+
});
|
|
28309
|
+
}
|
|
27571
28310
|
async function startKxmRuntimeSupervisor(options = {}) {
|
|
27572
28311
|
const now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
27573
28312
|
const paths = kxmRuntimePaths(options.stateRoot !== void 0 ? { stateRoot: options.stateRoot } : {});
|
|
@@ -27581,23 +28320,34 @@ async function startKxmRuntimeSupervisor(options = {}) {
|
|
|
27581
28320
|
}
|
|
27582
28321
|
}
|
|
27583
28322
|
async function startKxmRuntimeSupervisorInner(paths, requestedPortOption, now) {
|
|
27584
|
-
|
|
27585
|
-
|
|
28323
|
+
mkdirSync6(paths.runtimeDir, { recursive: true, mode: 448 });
|
|
28324
|
+
mkdirSync6(paths.projectsDir, { recursive: true, mode: 448 });
|
|
27586
28325
|
const token = randomBytes3(32).toString("hex");
|
|
27587
28326
|
const tokenHash = hashKxmSupervisorToken(token);
|
|
27588
28327
|
const registry = new KxmRuntimeRegistry(paths.registryDb);
|
|
27589
|
-
const runtimeId = `rtm_${
|
|
28328
|
+
const runtimeId = `rtm_${createHash13("sha256").update(`${paths.stateRoot}\0${process.pid}\0${now()}\0${randomBytes3(16).toString("hex")}`, "utf8").digest("hex").slice(0, 24)}`;
|
|
27590
28329
|
const requestedPort = requestedPortOption ?? 0;
|
|
27591
28330
|
let activeRuntimeId = runtimeId;
|
|
27592
28331
|
const contexts = /* @__PURE__ */ new Map();
|
|
28332
|
+
const registerSyncCredentials = (context) => {
|
|
28333
|
+
const hubToken = resolveClientHubAuthToken(process.env, defaultProjectName(context.projectRoot, process.env));
|
|
28334
|
+
if (hubToken) context.eventStore.syncRedactor.register(hubToken);
|
|
28335
|
+
for (const key of Object.keys(process.env)) {
|
|
28336
|
+
if (key.startsWith("KXM_") && (key.endsWith("_TOKEN") || key.endsWith("_KEY")) || key.endsWith("_API_KEY") || key.endsWith("_SECRET")) {
|
|
28337
|
+
const value = process.env[key]?.trim();
|
|
28338
|
+
if (value) context.eventStore.syncRedactor.register(value);
|
|
28339
|
+
}
|
|
28340
|
+
}
|
|
28341
|
+
};
|
|
27593
28342
|
const contextFor = (projectRoot) => {
|
|
27594
|
-
if (!
|
|
28343
|
+
if (!isAbsolute6(projectRoot)) {
|
|
27595
28344
|
throw runtimeError("runtime_request_invalid", "projectRoot", "projectRoot must be an absolute path");
|
|
27596
28345
|
}
|
|
27597
28346
|
const key = projectRuntimeKey(projectRoot);
|
|
27598
28347
|
const existing = contexts.get(key);
|
|
27599
28348
|
if (existing) return existing;
|
|
27600
28349
|
const context = openKxmRuntimeContext(projectRoot, { homeRuntimeId: activeRuntimeId, stateRoot: paths.stateRoot });
|
|
28350
|
+
registerSyncCredentials(context);
|
|
27601
28351
|
contexts.set(key, context);
|
|
27602
28352
|
return context;
|
|
27603
28353
|
};
|
|
@@ -27929,11 +28679,37 @@ async function startKxmRuntimeSupervisorInner(paths, requestedPortOption, now) {
|
|
|
27929
28679
|
}
|
|
27930
28680
|
}, 1e3);
|
|
27931
28681
|
heartbeat.unref();
|
|
28682
|
+
for (const reg of registry.projectsForRuntime(activeRuntimeId)) {
|
|
28683
|
+
try {
|
|
28684
|
+
contextFor(reg.projectRoot);
|
|
28685
|
+
} catch {
|
|
28686
|
+
}
|
|
28687
|
+
}
|
|
28688
|
+
let syncing = false;
|
|
28689
|
+
const syncTimer = setInterval(() => {
|
|
28690
|
+
if (syncing || stopping) return;
|
|
28691
|
+
syncing = true;
|
|
28692
|
+
void (async () => {
|
|
28693
|
+
for (const context of [...contexts.values()]) {
|
|
28694
|
+
try {
|
|
28695
|
+
const client = runtimeHubClientFor(context, process.env);
|
|
28696
|
+
if (!client) continue;
|
|
28697
|
+
await client.heartbeat();
|
|
28698
|
+
await syncKxmOutbox(context.eventStore, client, { now });
|
|
28699
|
+
} catch {
|
|
28700
|
+
}
|
|
28701
|
+
}
|
|
28702
|
+
})().finally(() => {
|
|
28703
|
+
syncing = false;
|
|
28704
|
+
});
|
|
28705
|
+
}, runtimeSyncIntervalMs());
|
|
28706
|
+
syncTimer.unref();
|
|
27932
28707
|
let stopping = false;
|
|
27933
28708
|
const stop = async () => {
|
|
27934
28709
|
if (stopping) return;
|
|
27935
28710
|
stopping = true;
|
|
27936
28711
|
clearInterval(heartbeat);
|
|
28712
|
+
clearInterval(syncTimer);
|
|
27937
28713
|
try {
|
|
27938
28714
|
registry.markStopping(process.pid, now());
|
|
27939
28715
|
} catch {
|
|
@@ -27998,7 +28774,7 @@ async function kxmRuntimeRequest(handle, method, path, body) {
|
|
|
27998
28774
|
|
|
27999
28775
|
// plugins/kxm/src/pi-producer.ts
|
|
28000
28776
|
import { spawn as spawn4 } from "node:child_process";
|
|
28001
|
-
import { join as
|
|
28777
|
+
import { join as join17 } from "node:path";
|
|
28002
28778
|
import { StringDecoder } from "node:string_decoder";
|
|
28003
28779
|
function formatPiSessionKey(params) {
|
|
28004
28780
|
if (params.agentId === "coordinator") {
|
|
@@ -28214,8 +28990,8 @@ var PiSession = class {
|
|
|
28214
28990
|
}
|
|
28215
28991
|
const id = `cmd_${++this.commandCounter}`;
|
|
28216
28992
|
const payload = { ...command, id };
|
|
28217
|
-
return new Promise((
|
|
28218
|
-
this.pendingCommands.set(id, { resolve:
|
|
28993
|
+
return new Promise((resolve10, reject) => {
|
|
28994
|
+
this.pendingCommands.set(id, { resolve: resolve10, reject });
|
|
28219
28995
|
try {
|
|
28220
28996
|
this.process.stdin.write(JSON.stringify(payload) + "\n");
|
|
28221
28997
|
} catch (err) {
|
|
@@ -28235,7 +29011,7 @@ var PiSession = class {
|
|
|
28235
29011
|
return { outcome: "cancelled", text: "aborted", usage: {} };
|
|
28236
29012
|
}
|
|
28237
29013
|
this.status = "busy";
|
|
28238
|
-
return new Promise((
|
|
29014
|
+
return new Promise((resolve10, reject) => {
|
|
28239
29015
|
let signalCleanup;
|
|
28240
29016
|
if (signal) {
|
|
28241
29017
|
const onAbort = () => {
|
|
@@ -28249,7 +29025,7 @@ var PiSession = class {
|
|
|
28249
29025
|
signalCleanup = () => signal.removeEventListener("abort", onAbort);
|
|
28250
29026
|
}
|
|
28251
29027
|
this.activePrompt = {
|
|
28252
|
-
resolve:
|
|
29028
|
+
resolve: resolve10,
|
|
28253
29029
|
reject,
|
|
28254
29030
|
allowedOutcomes,
|
|
28255
29031
|
text: "",
|
|
@@ -28372,7 +29148,7 @@ function createKxmPiProducer(options = {}) {
|
|
|
28372
29148
|
const args = ["--mode", "rpc", "--name", displayName];
|
|
28373
29149
|
if (options.sessionDir) {
|
|
28374
29150
|
const safeDirName = key.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
28375
|
-
args.push("--session-dir",
|
|
29151
|
+
args.push("--session-dir", join17(options.sessionDir, request.runId, safeDirName));
|
|
28376
29152
|
}
|
|
28377
29153
|
if (model) {
|
|
28378
29154
|
args.push("--model", model);
|
|
@@ -28500,34 +29276,8 @@ function createKxmPiProducer(options = {}) {
|
|
|
28500
29276
|
}
|
|
28501
29277
|
|
|
28502
29278
|
// plugins/kxm/src/logger.ts
|
|
28503
|
-
import { appendFileSync, existsSync as
|
|
28504
|
-
import { dirname as
|
|
28505
|
-
|
|
28506
|
-
// plugins/kxm/src/redact.ts
|
|
28507
|
-
var SECRET_PATTERNS = [
|
|
28508
|
-
/\bsk-[A-Za-z0-9_-]{8,}\b/g,
|
|
28509
|
-
/\bsk-ant-[A-Za-z0-9_-]{8,}\b/g,
|
|
28510
|
-
/\bghp_[A-Za-z0-9_]{20,}\b/g,
|
|
28511
|
-
/\bgithub_pat_[A-Za-z0-9_]{20,}\b/g,
|
|
28512
|
-
/\bxox[baprs]-[A-Za-z0-9-]{10,}\b/gi,
|
|
28513
|
-
/\bBearer\s+[A-Za-z0-9._~+/=-]+\b/gi,
|
|
28514
|
-
/\bya29\.[A-Za-z0-9._~+/-]+=*/g,
|
|
28515
|
-
/\b1\/\/[A-Za-z0-9_-]+/g,
|
|
28516
|
-
/\b1\/[A-Za-z0-9_-]{20,}/g,
|
|
28517
|
-
/("?(?:access_token|refresh_token|id_token|sessionKey|session_key|claude_oauth_token|anthropicApiKey)"?\s*[:=]\s*")[^"]*(")/gi,
|
|
28518
|
-
/\bKXM_[A-Z0-9_]*(TOKEN|SECRET|KEY)[A-Z0-9_]*=\S+/gi,
|
|
28519
|
-
/\b(GITHUB_TOKEN|GH_TOKEN|KXM_AUTH_TOKEN|KXM_WORKFLOW_SIGNAL_SECRET|ANTHROPIC_API_KEY|ANTHROPIC_AUTH_TOKEN|CLAUDE_API_KEY)=\S+/gi,
|
|
28520
|
-
/\b[A-Fa-f0-9]{64}\b/g
|
|
28521
|
-
];
|
|
28522
|
-
function redactSecrets(value) {
|
|
28523
|
-
let result = value;
|
|
28524
|
-
for (const pattern of SECRET_PATTERNS) {
|
|
28525
|
-
result = result.replace(pattern, "[redacted]");
|
|
28526
|
-
}
|
|
28527
|
-
return result;
|
|
28528
|
-
}
|
|
28529
|
-
|
|
28530
|
-
// plugins/kxm/src/logger.ts
|
|
29279
|
+
import { appendFileSync, existsSync as existsSync13, mkdirSync as mkdirSync7, renameSync as renameSync4, statSync as statSync3, unlinkSync as unlinkSync2 } from "node:fs";
|
|
29280
|
+
import { dirname as dirname9 } from "node:path";
|
|
28531
29281
|
var LOG_LEVEL_PRIORITY = {
|
|
28532
29282
|
debug: 10,
|
|
28533
29283
|
info: 20,
|
|
@@ -28564,7 +29314,7 @@ function redactLogValue(val, key) {
|
|
|
28564
29314
|
function rotateLogFiles(filePath, maxFiles) {
|
|
28565
29315
|
for (let i = maxFiles; i >= 1; i--) {
|
|
28566
29316
|
const current = `${filePath}.${i}`;
|
|
28567
|
-
if (
|
|
29317
|
+
if (existsSync13(current)) {
|
|
28568
29318
|
if (i >= maxFiles) {
|
|
28569
29319
|
try {
|
|
28570
29320
|
unlinkSync2(current);
|
|
@@ -28572,15 +29322,15 @@ function rotateLogFiles(filePath, maxFiles) {
|
|
|
28572
29322
|
}
|
|
28573
29323
|
} else {
|
|
28574
29324
|
try {
|
|
28575
|
-
|
|
29325
|
+
renameSync4(current, `${filePath}.${i + 1}`);
|
|
28576
29326
|
} catch {
|
|
28577
29327
|
}
|
|
28578
29328
|
}
|
|
28579
29329
|
}
|
|
28580
29330
|
}
|
|
28581
|
-
if (
|
|
29331
|
+
if (existsSync13(filePath)) {
|
|
28582
29332
|
try {
|
|
28583
|
-
|
|
29333
|
+
renameSync4(filePath, `${filePath}.1`);
|
|
28584
29334
|
} catch {
|
|
28585
29335
|
}
|
|
28586
29336
|
}
|
|
@@ -28595,7 +29345,7 @@ function createLogger(options) {
|
|
|
28595
29345
|
const shouldStdout = options.stdout ?? !isDaemon;
|
|
28596
29346
|
const correlationDefaults = options.correlation ?? {};
|
|
28597
29347
|
let currentSize = 0;
|
|
28598
|
-
if (filePath &&
|
|
29348
|
+
if (filePath && existsSync13(filePath)) {
|
|
28599
29349
|
try {
|
|
28600
29350
|
currentSize = statSync3(filePath).size;
|
|
28601
29351
|
} catch {
|
|
@@ -28633,7 +29383,7 @@ function createLogger(options) {
|
|
|
28633
29383
|
currentSize = 0;
|
|
28634
29384
|
}
|
|
28635
29385
|
try {
|
|
28636
|
-
|
|
29386
|
+
mkdirSync7(dirname9(filePath), { recursive: true });
|
|
28637
29387
|
appendFileSync(filePath, line, { encoding: "utf8", mode: 384 });
|
|
28638
29388
|
currentSize += lineBytes;
|
|
28639
29389
|
} catch {
|
|
@@ -28675,22 +29425,9 @@ function createLogger(options) {
|
|
|
28675
29425
|
}
|
|
28676
29426
|
|
|
28677
29427
|
// plugins/kxm/src/improve.ts
|
|
28678
|
-
import { createHash as
|
|
28679
|
-
import { existsSync as
|
|
28680
|
-
import { join as
|
|
28681
|
-
|
|
28682
|
-
// plugins/kxm/src/protocol.ts
|
|
28683
|
-
var DEFAULT_MESSAGE_TTL_MS = 24 * 60 * 6e4;
|
|
28684
|
-
var MAX_MESSAGE_TTL_MS = 7 * 24 * 60 * 6e4;
|
|
28685
|
-
var DEFAULT_MESSAGE_RETENTION_MS = 7 * 24 * 60 * 6e4;
|
|
28686
|
-
var MAX_BODY_BYTES = 256 * 1024;
|
|
28687
|
-
var MAX_LEASE_TTL_MS = 10 * 6e4;
|
|
28688
|
-
var DEFAULT_LEASE_TTL_MS = 5 * 6e4;
|
|
28689
|
-
function nowIso() {
|
|
28690
|
-
return (/* @__PURE__ */ new Date()).toISOString();
|
|
28691
|
-
}
|
|
28692
|
-
|
|
28693
|
-
// plugins/kxm/src/improve.ts
|
|
29428
|
+
import { createHash as createHash14 } from "node:crypto";
|
|
29429
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync8, writeFileSync as writeFileSync7 } from "node:fs";
|
|
29430
|
+
import { join as join18, relative as relative4, resolve as resolve8 } from "node:path";
|
|
28694
29431
|
var CANDIDATE_SCHEMA = "kxm.candidate.v1";
|
|
28695
29432
|
var IMPROVEMENT_REPORT_SCHEMA = "kxm.improvement-report.v2";
|
|
28696
29433
|
var IMPROVEMENT_REPORT_V1_SCHEMA = "kxm.improvement-report.v1";
|
|
@@ -28837,7 +29574,7 @@ function groupRoutingRecords(records, options = {}) {
|
|
|
28837
29574
|
const isCandidate = recurrence >= minRecurrence && verifyPassRate >= minPassRate;
|
|
28838
29575
|
const candidateKind = isCandidate ? classifyCandidateKind(group.stepId, group.agentRole) : void 0;
|
|
28839
29576
|
const safeSlug = group.stepId.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 16) || "step";
|
|
28840
|
-
const keyHash =
|
|
29577
|
+
const keyHash = createHash14("sha256").update(`${group.workflowHash}:${group.stepId}:${group.agentRole}:${group.promptHash}`).digest("hex").slice(0, 10);
|
|
28841
29578
|
const candidateId = isCandidate && candidateKind ? `cand_${candidateKind.replace(/-/g, "_")}_${safeSlug}_${keyHash}` : void 0;
|
|
28842
29579
|
rows.push({
|
|
28843
29580
|
workflowHash: group.workflowHash,
|
|
@@ -28862,8 +29599,8 @@ function groupRoutingRecords(records, options = {}) {
|
|
|
28862
29599
|
});
|
|
28863
29600
|
}
|
|
28864
29601
|
function buildImprovementReport(records, options = {}) {
|
|
28865
|
-
const projectRoot = options.projectRoot ?
|
|
28866
|
-
const candidatesDir = options.candidatesDir ?
|
|
29602
|
+
const projectRoot = options.projectRoot ? resolve8(options.projectRoot) : process.cwd();
|
|
29603
|
+
const candidatesDir = options.candidatesDir ? resolve8(options.candidatesDir) : join18(projectRoot, ".kxm", "candidates");
|
|
28867
29604
|
const groups = groupRoutingRecords(records, options);
|
|
28868
29605
|
const candidates = [];
|
|
28869
29606
|
for (const group of groups) {
|
|
@@ -28875,7 +29612,7 @@ function buildImprovementReport(records, options = {}) {
|
|
|
28875
29612
|
group.workflowHash
|
|
28876
29613
|
);
|
|
28877
29614
|
const diffFileName = `${group.candidateId}.diff`;
|
|
28878
|
-
const diffFilePath =
|
|
29615
|
+
const diffFilePath = join18(candidatesDir, diffFileName);
|
|
28879
29616
|
const relDiffPath = relative4(projectRoot, diffFilePath).replace(/\\/g, "/");
|
|
28880
29617
|
const candidate = {
|
|
28881
29618
|
schema: CANDIDATE_SCHEMA,
|
|
@@ -28897,12 +29634,12 @@ function buildImprovementReport(records, options = {}) {
|
|
|
28897
29634
|
createdAt: nowIso()
|
|
28898
29635
|
};
|
|
28899
29636
|
if (!options.dryRun) {
|
|
28900
|
-
if (!
|
|
28901
|
-
|
|
29637
|
+
if (!existsSync14(candidatesDir)) {
|
|
29638
|
+
mkdirSync8(candidatesDir, { recursive: true });
|
|
28902
29639
|
}
|
|
28903
|
-
|
|
28904
|
-
const jsonFilePath =
|
|
28905
|
-
|
|
29640
|
+
writeFileSync7(diffFilePath, diff, "utf8");
|
|
29641
|
+
const jsonFilePath = join18(candidatesDir, `${group.candidateId}.json`);
|
|
29642
|
+
writeFileSync7(jsonFilePath, JSON.stringify(candidate, null, 2) + "\n", "utf8");
|
|
28906
29643
|
}
|
|
28907
29644
|
candidates.push(candidate);
|
|
28908
29645
|
}
|
|
@@ -28917,10 +29654,10 @@ function buildImprovementReport(records, options = {}) {
|
|
|
28917
29654
|
}
|
|
28918
29655
|
function writeImprovementReport(improvementsDir, report, dryRun = false) {
|
|
28919
29656
|
const stamp = report.createdAt.replace(/[:.]/g, "-");
|
|
28920
|
-
const path =
|
|
29657
|
+
const path = join18(improvementsDir, `${stamp}.json`);
|
|
28921
29658
|
if (!dryRun) {
|
|
28922
|
-
|
|
28923
|
-
|
|
29659
|
+
mkdirSync8(improvementsDir, { recursive: true });
|
|
29660
|
+
writeFileSync7(path, `${JSON.stringify(report, null, 2)}
|
|
28924
29661
|
`, { encoding: "utf8" });
|
|
28925
29662
|
}
|
|
28926
29663
|
return path;
|
|
@@ -29421,8 +30158,8 @@ Instructions for Operator:
|
|
|
29421
30158
|
|
|
29422
30159
|
// plugins/kxm/src/modes.ts
|
|
29423
30160
|
var import_yaml6 = __toESM(require_dist(), 1);
|
|
29424
|
-
import { existsSync as
|
|
29425
|
-
import { join as
|
|
30161
|
+
import { existsSync as existsSync15, readFileSync as readFileSync13 } from "node:fs";
|
|
30162
|
+
import { join as join19 } from "node:path";
|
|
29426
30163
|
var DEFAULT_MODES_CONFIG = Object.freeze({
|
|
29427
30164
|
schema: "kxm.modes.v1",
|
|
29428
30165
|
majorModes: {
|
|
@@ -29485,12 +30222,12 @@ function loadModesConfig(projectRoot) {
|
|
|
29485
30222
|
if (!projectRoot) {
|
|
29486
30223
|
return DEFAULT_MODES_CONFIG;
|
|
29487
30224
|
}
|
|
29488
|
-
const modesPath =
|
|
29489
|
-
if (!
|
|
30225
|
+
const modesPath = join19(projectRoot, ".kxm", "modes.yaml");
|
|
30226
|
+
if (!existsSync15(modesPath)) {
|
|
29490
30227
|
return DEFAULT_MODES_CONFIG;
|
|
29491
30228
|
}
|
|
29492
30229
|
try {
|
|
29493
|
-
const raw =
|
|
30230
|
+
const raw = readFileSync13(modesPath, "utf8");
|
|
29494
30231
|
const parsed = (0, import_yaml6.parse)(raw);
|
|
29495
30232
|
if (parsed && parsed.schema === "kxm.modes.v1" && parsed.majorModes) {
|
|
29496
30233
|
return {
|
|
@@ -29576,11 +30313,11 @@ function calculatePromptFootprint(resolved, projectRoot = process.cwd(), catalog
|
|
|
29576
30313
|
estimatedTokens: estimateTokens(baseSystemPromptChars)
|
|
29577
30314
|
});
|
|
29578
30315
|
for (const relPath of resolved.contextFiles) {
|
|
29579
|
-
const fullPath =
|
|
30316
|
+
const fullPath = join19(projectRoot, relPath);
|
|
29580
30317
|
let chars = 0;
|
|
29581
|
-
if (
|
|
30318
|
+
if (existsSync15(fullPath)) {
|
|
29582
30319
|
try {
|
|
29583
|
-
chars =
|
|
30320
|
+
chars = readFileSync13(fullPath, "utf8").length;
|
|
29584
30321
|
} catch {
|
|
29585
30322
|
chars = 0;
|
|
29586
30323
|
}
|
|
@@ -29712,9 +30449,9 @@ ${divider}
|
|
|
29712
30449
|
|
|
29713
30450
|
// plugins/kxm/src/ssh-remote.ts
|
|
29714
30451
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
29715
|
-
import { existsSync as
|
|
29716
|
-
import { homedir as
|
|
29717
|
-
import { join as
|
|
30452
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync9, readFileSync as readFileSync14, readdirSync as readdirSync5, rmSync as rmSync4, statSync as statSync4 } from "node:fs";
|
|
30453
|
+
import { homedir as homedir4 } from "node:os";
|
|
30454
|
+
import { join as join20, resolve as resolve9 } from "node:path";
|
|
29718
30455
|
var MAX_SSH_OUTPUT_BYTES = 50 * 1024;
|
|
29719
30456
|
var MAX_SSH_OUTPUT_LINES = 2e3;
|
|
29720
30457
|
var DEFAULT_SOCKET_DIR = ".kxm/run/ssh-sockets";
|
|
@@ -29740,12 +30477,12 @@ function truncateSshOutput(raw) {
|
|
|
29740
30477
|
return { text, truncated };
|
|
29741
30478
|
}
|
|
29742
30479
|
function parseSshConfig(configPath) {
|
|
29743
|
-
const targetPath = configPath ??
|
|
29744
|
-
if (!
|
|
30480
|
+
const targetPath = configPath ?? join20(homedir4(), ".ssh", "config");
|
|
30481
|
+
if (!existsSync16(targetPath)) {
|
|
29745
30482
|
return [];
|
|
29746
30483
|
}
|
|
29747
30484
|
try {
|
|
29748
|
-
const content =
|
|
30485
|
+
const content = readFileSync14(targetPath, "utf-8");
|
|
29749
30486
|
const lines = content.split("\n");
|
|
29750
30487
|
const hosts = [];
|
|
29751
30488
|
let currentHosts = [];
|
|
@@ -29822,15 +30559,15 @@ function resolveSshHostG(host, execFn = spawnSync3) {
|
|
|
29822
30559
|
}
|
|
29823
30560
|
}
|
|
29824
30561
|
function ensureSocketDir(socketDir = DEFAULT_SOCKET_DIR) {
|
|
29825
|
-
const resolved =
|
|
29826
|
-
if (!
|
|
29827
|
-
|
|
30562
|
+
const resolved = resolve9(socketDir);
|
|
30563
|
+
if (!existsSync16(resolved)) {
|
|
30564
|
+
mkdirSync9(resolved, { recursive: true, mode: 448 });
|
|
29828
30565
|
}
|
|
29829
30566
|
return resolved;
|
|
29830
30567
|
}
|
|
29831
30568
|
function buildSshArgs(options) {
|
|
29832
30569
|
const socketDir = ensureSocketDir(options.socketDir ?? DEFAULT_SOCKET_DIR);
|
|
29833
|
-
const controlPath =
|
|
30570
|
+
const controlPath = join20(socketDir, "%C");
|
|
29834
30571
|
const persist2 = options.controlPersist ?? DEFAULT_CONTROL_PERSIST;
|
|
29835
30572
|
const args = [
|
|
29836
30573
|
"-o",
|
|
@@ -29855,7 +30592,7 @@ function buildSshArgs(options) {
|
|
|
29855
30592
|
}
|
|
29856
30593
|
function checkControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn = spawnSync3) {
|
|
29857
30594
|
const resolvedDir = ensureSocketDir(socketDir);
|
|
29858
|
-
const controlPath =
|
|
30595
|
+
const controlPath = join20(resolvedDir, "%C");
|
|
29859
30596
|
try {
|
|
29860
30597
|
const result = execFn("ssh", ["-O", "check", "-o", `ControlPath=${controlPath}`, host], {
|
|
29861
30598
|
encoding: "utf-8"
|
|
@@ -29867,7 +30604,7 @@ function checkControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn = spawn
|
|
|
29867
30604
|
}
|
|
29868
30605
|
function closeControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn = spawnSync3) {
|
|
29869
30606
|
const resolvedDir = ensureSocketDir(socketDir);
|
|
29870
|
-
const controlPath =
|
|
30607
|
+
const controlPath = join20(resolvedDir, "%C");
|
|
29871
30608
|
try {
|
|
29872
30609
|
const result = execFn("ssh", ["-O", "stop", "-o", `ControlPath=${controlPath}`, host], {
|
|
29873
30610
|
encoding: "utf-8"
|
|
@@ -29883,11 +30620,11 @@ function pruneSocketDir(socketDir = DEFAULT_SOCKET_DIR) {
|
|
|
29883
30620
|
try {
|
|
29884
30621
|
const entries = readdirSync5(resolvedDir);
|
|
29885
30622
|
for (const entry of entries) {
|
|
29886
|
-
const fullPath =
|
|
30623
|
+
const fullPath = join20(resolvedDir, entry);
|
|
29887
30624
|
try {
|
|
29888
30625
|
const stat = statSync4(fullPath);
|
|
29889
30626
|
if (stat.isSocket()) {
|
|
29890
|
-
|
|
30627
|
+
rmSync4(fullPath, { force: true });
|
|
29891
30628
|
removed++;
|
|
29892
30629
|
}
|
|
29893
30630
|
} catch {
|
|
@@ -30311,6 +31048,7 @@ export {
|
|
|
30311
31048
|
DEFAULT_LOG_MAX_FILES,
|
|
30312
31049
|
DEFAULT_MODES_CONFIG,
|
|
30313
31050
|
DEFAULT_RUNTIME_STOP_GRACE_MS,
|
|
31051
|
+
DEFAULT_RUNTIME_SYNC_INTERVAL_MS,
|
|
30314
31052
|
DEFAULT_SOCKET_DIR,
|
|
30315
31053
|
DEFAULT_SUBAGENT_MODELS,
|
|
30316
31054
|
DRIVE_RECEIPT_MAX_BYTES,
|
|
@@ -30458,8 +31196,10 @@ export {
|
|
|
30458
31196
|
runHarnessUpdate,
|
|
30459
31197
|
runtimeError,
|
|
30460
31198
|
runtimeStopGraceMs,
|
|
31199
|
+
runtimeSyncIntervalMs,
|
|
30461
31200
|
sanitizeLogOutput,
|
|
30462
31201
|
startKxmRuntimeSupervisor,
|
|
31202
|
+
syncKxmOutbox,
|
|
30463
31203
|
tableColumns,
|
|
30464
31204
|
truncateSshOutput,
|
|
30465
31205
|
userTables,
|