@kontextmind/kxm 0.7.72 → 0.7.73

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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 = resolve8.call(this, root, ref);
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 resolve8(root, ref) {
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 resolve8(baseURI, relativeURI, options) {
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: resolve8,
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 createHash6 } from "node:crypto";
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((resolve8) => {
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
- resolve8({
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 join18 = objectValue(step.join);
17082
- const minimumPassed = join18 && typeof join18.minimumPassed === "number" ? join18.minimumPassed : void 0;
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 createHash4, randomUUID } from "node:crypto";
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 createHash3, randomBytes } from "node:crypto";
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:${createHash3("sha256").update(bytes).digest("hex")}`;
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: 4 });
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: 4
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:${createHash3("sha256").update(manifestJson, "utf8").digest("hex")}`;
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 = 4;
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 = 5;
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 createHash4("sha256").update(folded, "utf8").digest("hex").slice(0, 24);
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:${createHash4("sha256").update(body, "utf8").digest("hex")}`;
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 = 5;
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:${createHash4("sha256").update(kxmCanonicalJson(copy), "utf8").digest("hex")}`;
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 createHash5 } from "node:crypto";
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 join18 = compileJoin(step, id, sink);
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: join18
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 join18 = objectValue2(step.join);
21087
- if (!join18) return { strategy: "all" };
21088
- const declared = stringValue2(join18.strategy);
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(join18.minimumPassed, `${stepId}.join.minimumPassed`, stepId, sink);
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 join18.cancelRemaining === "boolean" ? { cancelRemaining: join18.cancelRemaining } : {}
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:${createHash5("sha256").update(input, "utf8").digest("hex")}`;
21822
+ return `sha256:${createHash6("sha256").update(input, "utf8").digest("hex")}`;
21406
21823
  }
21407
- function hashKxmRunPlanEnvelope(envelope) {
21408
- return kxmSha256(kxmCanonicalJson(envelope));
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 envelope = parseEnvelope(parsed, run);
21456
- const hash = hashKxmRunPlanEnvelope(envelope);
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 envelope;
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, envelope, events, state) {
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, envelope, state, event, row);
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 = envelope.plan.steps[row.stepId];
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, envelope, state, event, row) {
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 = envelope.plan.steps[row.stepId];
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 = envelope.gates.definitions[step.gate];
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 (!envelope.gates.registry || row.registryHash !== envelope.gates.registry.hash) {
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 = envelope && state.runPlanHash;
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 !== envelope.gates.controlRoot.projectKey) {
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((resolve8, reject) => {
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(resolve8, reject),
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:${createHash6("sha256").update(input, "utf8").digest("hex")}`;
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 = createHash6("sha256");
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:${createHash6("sha256").update(request.prompt, "utf8").digest("hex")}`;
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 envelope = planRow ? loadKxmRunPlanEnvelope(context.eventStore, run) : void 0;
22735
- const state = foldKxmRunState(run, envelope?.plan, events, envelope ? {
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 = envelope.plan.steps[stepId];
23159
+ const step = envelope2.plan.steps[stepId];
22743
23160
  if (!step || step.kind !== "gate") return void 0;
22744
- return envelope.gates.definitions[step.gate]?.kind;
23161
+ return envelope2.gates.definitions[step.gate]?.kind;
22745
23162
  },
22746
- projectLimits: envelope.projectLimits
23163
+ projectLimits: envelope2.projectLimits
22747
23164
  } : {});
22748
- if (envelope) verifyKxmGateEvidence(context.eventStore, run, envelope, events, state);
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 createHash12, createHmac, randomBytes as randomBytes3, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
22942
- import { chmodSync as chmodSync2, existsSync as existsSync10, lstatSync as lstatSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync8, renameSync, rmSync, writeFileSync as writeFileSync4 } from "node:fs";
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 dirname6, isAbsolute as isAbsolute4, join as join13 } from "node:path";
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 createHash7, randomUUID as randomUUID3 } from "node:crypto";
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:${createHash7("sha256").update(value).digest("hex")}`;
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 createHash8 } from "node:crypto";
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 createHash8("sha256").update(JSON.stringify(canonical), "utf8").digest("hex");
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 createHash11, randomBytes as randomBytes2, timingSafeEqual } from "node:crypto";
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 createHash9 } from "node:crypto";
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 = createHash9("sha256");
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((resolve8) => {
23750
- this.resolveOutcome = resolve8;
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, envelope, stepId) {
24095
- const step = requireGateStep(envelope, stepId, run.runId);
24096
- const definition = envelope.gates.definitions[step.gate];
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: envelope.gates.registry.hash,
24150
- runPlanHash: envelope && folded.runPlanHash ? folded.runPlanHash : hashFromStore(context, run.runId),
24151
- controlProjectKey: envelope.gates.controlRoot.projectKey,
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, envelope, stepId) {
24162
- const ids = requireIntent(context, run, envelope, stepId);
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, envelope, stepId, observation) {
24179
- const ids = requireIntent(context, run, envelope, stepId);
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(envelope, stepId),
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, envelope, ids, stepId, outcome);
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, envelope, stepId, observation) {
24246
- const ids = requireIntent(context, run, envelope, stepId);
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, envelope, stepId, reason, observation) {
24300
- const ids = requireIntent(context, run, envelope, stepId);
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, envelope, stepId, observation) {
24330
- const ids = requireIntent(context, run, envelope, stepId);
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, envelope, ids, stepId, outcome) {
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 = envelope.plan.steps[stepId].transitions[outcome];
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(envelope.plan, ids.state, stepId, outcome);
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(envelope, stepId) {
24414
- const required = envelope.plan.steps[stepId]?.requiredEvidence.filter((item) => item.kind === "gate") ?? [];
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(envelope, stepId, runId) {
24448
- const step = envelope.plan.steps[stepId];
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, envelope, stepId) {
24453
- const step = requireGateStep(envelope, stepId, run.runId);
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 createHash10 } from "node:crypto";
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 createHash10("sha256").update(JSON.stringify(canonical), "utf8").digest("hex");
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:${createHash11("sha256").update(`${CAPABILITY_PREFIX}${secret}`, "utf8").digest("hex")}`;
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 envelope = {
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(envelope);
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(envelope),
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 envelope = loadKxmRunPlanEnvelope(context.eventStore, run);
24795
- const unsupported = options.allowLimits ? void 0 : unsupportedLimit(envelope);
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_${createHash11("sha256").update(`${runId}\0${token}\0${runtimeId}\0${monotonicNs}`, "utf8").digest("hex").slice(0, 24)}`;
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 envelope = loadKxmRunPlanEnvelope(this.context.eventStore, run);
25451
+ const envelope2 = loadKxmRunPlanEnvelope(this.context.eventStore, run);
25035
25452
  return enqueueKxmScheduledRun(
25036
25453
  this.context.eventStore.path,
25037
25454
  runId,
25038
- envelope.revisions.config,
25039
- envelope.projectLimits.maxConcurrentRuns,
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((resolve8, reject) => {
25070
- pending.resolve = resolve8;
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 envelope = loadKxmRunPlanEnvelope(context.eventStore, run);
25610
- const unsupported2 = unsupportedGateStep(plan, step, envelope, context);
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 = envelope.gates.definitions[step.gate];
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, envelope, stepId);
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 = createHash11("sha256").update(promptText, "utf8").digest("hex");
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 envelope = loadKxmRunPlanEnvelope(context.eventStore, run);
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: envelope.revisions.toolPolicy
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 envelope = loadKxmRunPlanEnvelope(context.eventStore, run);
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: envelope.revisions.toolPolicy
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 envelope = loadKxmRunPlanEnvelope(context.eventStore, run);
26410
- return effectiveRunDurationBudget(envelope.plan.limits, envelope.projectLimits);
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(envelope) {
26517
- if (envelope.plan.limits.maxAgentTimeMs !== void 0) {
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 (envelope.projectLimits.maxAgentTimeMs !== void 0) {
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, envelope, context) {
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 (envelope.gates.registry === null) {
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 = envelope.gates.definitions[step.gate];
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) !== envelope.gates.controlRoot.projectKey) {
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 join13(paths.runtimeDir, "supervisor.token");
28007
+ return join16(paths.runtimeDir, "supervisor.token");
27326
28008
  }
27327
28009
  function publishKxmSupervisorToken(paths, token) {
27328
28010
  const file = kxmSupervisorTokenFile(paths);
27329
- mkdirSync4(dirname6(file), { recursive: true, mode: 448 });
28011
+ mkdirSync6(dirname8(file), { recursive: true, mode: 448 });
27330
28012
  const temp = `${file}.${process.pid}.tmp`;
27331
- writeFileSync4(temp, `${token}
28013
+ writeFileSync6(temp, `${token}
27332
28014
  `, { encoding: "utf8", mode: 384 });
27333
28015
  try {
27334
28016
  chmodSync2(temp, 384);
27335
28017
  } catch {
27336
28018
  }
27337
- renameSync(temp, file);
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:${createHash12("sha256").update(`kxm-runtime-supervisor\0${token}`, "utf8").digest("hex")}`;
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 = readFileSync8(file, "utf8").trim();
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 join13(paths.runtimeDir, "supervisor.error");
28049
+ return join16(paths.runtimeDir, "supervisor.error");
27368
28050
  }
27369
28051
  function clearSupervisorError(paths) {
27370
28052
  const file = supervisorErrorFile(paths);
27371
- if (existsSync10(file)) rmSync(file, { force: true });
28053
+ if (existsSync12(file)) rmSync3(file, { force: true });
27372
28054
  }
27373
28055
  function recordSupervisorError(paths, message) {
27374
28056
  try {
27375
- mkdirSync4(paths.runtimeDir, { recursive: true, mode: 448 });
27376
- writeFileSync4(supervisorErrorFile(paths), `${message}
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 readFileSync8(file, "utf8").trim();
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 (!existsSync10(paths.registryDb)) return { running: false };
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 = join13(findKxmRepoRoot(import.meta.url), "scripts", "kxm-runtime-supervisor.mjs");
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((resolve8) => {
27550
- timeout = setTimeout(resolve8, graceMs);
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
- mkdirSync4(paths.runtimeDir, { recursive: true, mode: 448 });
27585
- mkdirSync4(paths.projectsDir, { recursive: true, mode: 448 });
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_${createHash12("sha256").update(`${paths.stateRoot}\0${process.pid}\0${now()}\0${randomBytes3(16).toString("hex")}`, "utf8").digest("hex").slice(0, 24)}`;
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 (!isAbsolute4(projectRoot)) {
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 join14 } from "node:path";
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((resolve8, reject) => {
28218
- this.pendingCommands.set(id, { resolve: resolve8, reject });
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((resolve8, reject) => {
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: resolve8,
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", join14(options.sessionDir, request.runId, safeDirName));
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 existsSync11, mkdirSync as mkdirSync5, renameSync as renameSync2, statSync as statSync3, unlinkSync as unlinkSync2 } from "node:fs";
28504
- import { dirname as dirname7 } from "node:path";
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 (existsSync11(current)) {
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
- renameSync2(current, `${filePath}.${i + 1}`);
29325
+ renameSync4(current, `${filePath}.${i + 1}`);
28576
29326
  } catch {
28577
29327
  }
28578
29328
  }
28579
29329
  }
28580
29330
  }
28581
- if (existsSync11(filePath)) {
29331
+ if (existsSync13(filePath)) {
28582
29332
  try {
28583
- renameSync2(filePath, `${filePath}.1`);
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 && existsSync11(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
- mkdirSync5(dirname7(filePath), { recursive: true });
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 createHash13 } from "node:crypto";
28679
- import { existsSync as existsSync12, mkdirSync as mkdirSync6, writeFileSync as writeFileSync5 } from "node:fs";
28680
- import { join as join15, relative as relative4, resolve as resolve6 } from "node:path";
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 = createHash13("sha256").update(`${group.workflowHash}:${group.stepId}:${group.agentRole}:${group.promptHash}`).digest("hex").slice(0, 10);
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 ? resolve6(options.projectRoot) : process.cwd();
28866
- const candidatesDir = options.candidatesDir ? resolve6(options.candidatesDir) : join15(projectRoot, ".kxm", "candidates");
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 = join15(candidatesDir, diffFileName);
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 (!existsSync12(candidatesDir)) {
28901
- mkdirSync6(candidatesDir, { recursive: true });
29637
+ if (!existsSync14(candidatesDir)) {
29638
+ mkdirSync8(candidatesDir, { recursive: true });
28902
29639
  }
28903
- writeFileSync5(diffFilePath, diff, "utf8");
28904
- const jsonFilePath = join15(candidatesDir, `${group.candidateId}.json`);
28905
- writeFileSync5(jsonFilePath, JSON.stringify(candidate, null, 2) + "\n", "utf8");
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 = join15(improvementsDir, `${stamp}.json`);
29657
+ const path = join18(improvementsDir, `${stamp}.json`);
28921
29658
  if (!dryRun) {
28922
- mkdirSync6(improvementsDir, { recursive: true });
28923
- writeFileSync5(path, `${JSON.stringify(report, null, 2)}
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 existsSync13, readFileSync as readFileSync10 } from "node:fs";
29425
- import { join as join16 } from "node:path";
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 = join16(projectRoot, ".kxm", "modes.yaml");
29489
- if (!existsSync13(modesPath)) {
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 = readFileSync10(modesPath, "utf8");
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 = join16(projectRoot, relPath);
30316
+ const fullPath = join19(projectRoot, relPath);
29580
30317
  let chars = 0;
29581
- if (existsSync13(fullPath)) {
30318
+ if (existsSync15(fullPath)) {
29582
30319
  try {
29583
- chars = readFileSync10(fullPath, "utf8").length;
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 existsSync14, mkdirSync as mkdirSync7, readFileSync as readFileSync11, readdirSync as readdirSync5, rmSync as rmSync2, statSync as statSync4 } from "node:fs";
29716
- import { homedir as homedir2 } from "node:os";
29717
- import { join as join17, resolve as resolve7 } from "node:path";
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 ?? join17(homedir2(), ".ssh", "config");
29744
- if (!existsSync14(targetPath)) {
30480
+ const targetPath = configPath ?? join20(homedir4(), ".ssh", "config");
30481
+ if (!existsSync16(targetPath)) {
29745
30482
  return [];
29746
30483
  }
29747
30484
  try {
29748
- const content = readFileSync11(targetPath, "utf-8");
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 = resolve7(socketDir);
29826
- if (!existsSync14(resolved)) {
29827
- mkdirSync7(resolved, { recursive: true, mode: 448 });
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 = join17(socketDir, "%C");
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 = join17(resolvedDir, "%C");
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 = join17(resolvedDir, "%C");
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 = join17(resolvedDir, entry);
30623
+ const fullPath = join20(resolvedDir, entry);
29887
30624
  try {
29888
30625
  const stat = statSync4(fullPath);
29889
30626
  if (stat.isSocket()) {
29890
- rmSync2(fullPath, { force: true });
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,