@kontextmind/kxm 0.7.104 → 0.7.105

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.
@@ -2987,7 +2987,7 @@ var require_compile = __commonJS({
2987
2987
  const schOrFunc = root.refs[ref];
2988
2988
  if (schOrFunc)
2989
2989
  return schOrFunc;
2990
- let _sch = resolve35.call(this, root, ref);
2990
+ let _sch = resolve34.call(this, root, ref);
2991
2991
  if (_sch === void 0) {
2992
2992
  const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
2993
2993
  const { schemaId } = this.opts;
@@ -3014,7 +3014,7 @@ var require_compile = __commonJS({
3014
3014
  function sameSchemaEnv(s1, s2) {
3015
3015
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
3016
3016
  }
3017
- function resolve35(root, ref) {
3017
+ function resolve34(root, ref) {
3018
3018
  let sch;
3019
3019
  while (typeof (sch = this.refs[ref]) == "string")
3020
3020
  ref = sch;
@@ -3835,11 +3835,11 @@ var require_fast_uri = __commonJS({
3835
3835
  normalizeString(uri, options);
3836
3836
  } else if (typeof uri === "object") {
3837
3837
  uri = /** @type {T} */
3838
- parse12(serialize(uri, options), options);
3838
+ parse11(serialize(uri, options), options);
3839
3839
  }
3840
3840
  return uri;
3841
3841
  }
3842
- function resolve35(baseURI, relativeURI, options) {
3842
+ function resolve34(baseURI, relativeURI, options) {
3843
3843
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
3844
3844
  const {
3845
3845
  parsed: baseParsed,
@@ -3875,8 +3875,8 @@ var require_fast_uri = __commonJS({
3875
3875
  function resolveComponent(base, relative8, options, skipNormalization) {
3876
3876
  const target = {};
3877
3877
  if (!skipNormalization) {
3878
- base = parse12(serialize(base, options), options);
3879
- relative8 = parse12(serialize(relative8, options), options);
3878
+ base = parse11(serialize(base, options), options);
3879
+ relative8 = parse11(serialize(relative8, options), options);
3880
3880
  }
3881
3881
  options = options || {};
3882
3882
  if (!options.tolerant && relative8.scheme) {
@@ -4168,7 +4168,7 @@ var require_fast_uri = __commonJS({
4168
4168
  }
4169
4169
  return { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme };
4170
4170
  }
4171
- function parse12(uri, opts) {
4171
+ function parse11(uri, opts) {
4172
4172
  return parseWithStatus(uri, opts).parsed;
4173
4173
  }
4174
4174
  function normalizeString(uri, opts) {
@@ -4201,11 +4201,11 @@ var require_fast_uri = __commonJS({
4201
4201
  var fastUri = {
4202
4202
  SCHEMES,
4203
4203
  normalize,
4204
- resolve: resolve35,
4204
+ resolve: resolve34,
4205
4205
  resolveComponent,
4206
4206
  equal,
4207
4207
  serialize,
4208
- parse: parse12
4208
+ parse: parse11
4209
4209
  };
4210
4210
  module.exports = fastUri;
4211
4211
  module.exports.default = fastUri;
@@ -14678,7 +14678,7 @@ var require_public_api = __commonJS({
14678
14678
  }
14679
14679
  return doc;
14680
14680
  }
14681
- function parse12(src, reviver, options) {
14681
+ function parse11(src, reviver, options) {
14682
14682
  let _reviver = void 0;
14683
14683
  if (typeof reviver === "function") {
14684
14684
  _reviver = reviver;
@@ -14719,7 +14719,7 @@ var require_public_api = __commonJS({
14719
14719
  return value.toString(options);
14720
14720
  return new Document.Document(value, _replacer, options).toString(options);
14721
14721
  }
14722
- exports.parse = parse12;
14722
+ exports.parse = parse11;
14723
14723
  exports.parseAllDocuments = parseAllDocuments;
14724
14724
  exports.parseDocument = parseDocument2;
14725
14725
  exports.stringify = stringify12;
@@ -14779,7 +14779,7 @@ var require_dist = __commonJS({
14779
14779
  });
14780
14780
 
14781
14781
  // plugins/kxm/src/cli.ts
14782
- import { resolve as resolve34 } from "node:path";
14782
+ import { resolve as resolve33 } from "node:path";
14783
14783
  import { fileURLToPath as fileURLToPath4 } from "node:url";
14784
14784
 
14785
14785
  // node_modules/commander/lib/error.js
@@ -19751,7 +19751,7 @@ var HubClient = class {
19751
19751
  if (signal?.aborted) throw new MeshWaitError("aborted", messageId);
19752
19752
  const message = await this.getMessage(messageId);
19753
19753
  if (["replied", "cancelled", "expired", "error"].includes(message.status)) return message;
19754
- await new Promise((resolve35, reject) => {
19754
+ await new Promise((resolve34, reject) => {
19755
19755
  const onAbort = () => {
19756
19756
  signal?.removeEventListener("abort", onAbort);
19757
19757
  clearTimeout(timer);
@@ -19759,7 +19759,7 @@ var HubClient = class {
19759
19759
  };
19760
19760
  const timer = setTimeout(() => {
19761
19761
  signal?.removeEventListener("abort", onAbort);
19762
- resolve35();
19762
+ resolve34();
19763
19763
  }, Math.min(500, Math.max(1, deadline - Date.now())));
19764
19764
  signal?.addEventListener("abort", onAbort, { once: true });
19765
19765
  if (signal?.aborted) onAbort();
@@ -19814,7 +19814,7 @@ var HubClient = class {
19814
19814
  } catch (error) {
19815
19815
  if (this.stopped || error instanceof Error && error.name === "AbortError") return;
19816
19816
  }
19817
- if (!this.stopped) await new Promise((resolve35) => setTimeout(resolve35, this.options.reconnectMs));
19817
+ if (!this.stopped) await new Promise((resolve34) => setTimeout(resolve34, this.options.reconnectMs));
19818
19818
  }
19819
19819
  }
19820
19820
  headers(includeIdentity = true) {
@@ -21350,7 +21350,7 @@ function defaultSpawn(command, args, options) {
21350
21350
  if (options.signal?.aborted) {
21351
21351
  return Promise.resolve({ stdout: "", stderr: "", code: null, started: false, observedChildExit: false, error: new Error("process_aborted") });
21352
21352
  }
21353
- return new Promise((resolve35) => {
21353
+ return new Promise((resolve34) => {
21354
21354
  const stdout = [];
21355
21355
  const stderr = [];
21356
21356
  let outputBytes = 0;
@@ -21381,7 +21381,7 @@ function defaultSpawn(command, args, options) {
21381
21381
  const started = Boolean(child?.pid);
21382
21382
  if (started && !observedChildExit) error ??= new Error("process_exit_unobserved");
21383
21383
  const unverifiedDescendants = stopping || started && !observedChildExit;
21384
- resolve35({
21384
+ resolve34({
21385
21385
  stdout: Buffer.concat(stdout).toString("utf8"),
21386
21386
  stderr: Buffer.concat(stderr).toString("utf8"),
21387
21387
  code,
@@ -21790,6 +21790,12 @@ var WRITER_ONESHOT_ARGS = Object.freeze({
21790
21790
  pi: Object.freeze(["-a", "--no-extensions", "--no-skills", "--no-prompt-templates", "--no-session"]),
21791
21791
  grok: Object.freeze(["--always-approve", "--no-subagents", "--disable-web-search"])
21792
21792
  });
21793
+ function oneShotWriterArgs(harness) {
21794
+ return Object.hasOwn(WRITER_ONESHOT_ARGS, harness) ? WRITER_ONESHOT_ARGS[harness] : void 0;
21795
+ }
21796
+ function oneShotPermissionArgs(harness, permission) {
21797
+ return permission === "edit" ? oneShotWriterArgs(harness) : oneShotReadOnlyArgs(harness);
21798
+ }
21793
21799
  var BUILTIN_HARNESSES = Object.freeze([
21794
21800
  {
21795
21801
  id: "pi",
@@ -22167,7 +22173,7 @@ function probeEntry(entry, runCommand, timeoutMs, platform, probe = {}) {
22167
22173
  return {
22168
22174
  id: entry.id,
22169
22175
  label: entry.label,
22170
- default: entry.default,
22176
+ default: entry.id === (probe.defaultHarness ?? DEFAULT_HARNESS),
22171
22177
  mode: entry.mode,
22172
22178
  detected,
22173
22179
  authenticated,
@@ -22188,7 +22194,7 @@ function probeHarnesses(options = {}) {
22188
22194
  const runCommand = options.runCommand ?? defaultRunner(options.env ?? process.env);
22189
22195
  const platform = options.platform ?? process.platform;
22190
22196
  return {
22191
- defaultHarness: DEFAULT_HARNESS,
22197
+ defaultHarness: options.defaultHarness ?? DEFAULT_HARNESS,
22192
22198
  harnesses: BUILTIN_HARNESSES.map((entry) => probeEntry(entry, runCommand, timeoutMs, platform, options))
22193
22199
  };
22194
22200
  }
@@ -22432,7 +22438,7 @@ function formatHarnessInventory(inventory) {
22432
22438
  ].join(" ");
22433
22439
  });
22434
22440
  return [
22435
- `default harness: ${inventory.defaultHarness} (omit agent harness: to use headless Pi)`,
22441
+ `default harness: ${inventory.defaultHarness} (used when an agent omits harness:)`,
22436
22442
  "enable/disable = Git YAML (.kxm/agents, .kxm/models) or the harness's own plugin CLI",
22437
22443
  "governed kxm skills are not auto-updated",
22438
22444
  header,
@@ -23094,8 +23100,8 @@ function validateWorkflow(workflow, agents, models, repositories, gates, issues)
23094
23100
  const writable = Object.values(objectValue(step.repositories) ?? {}).filter((access) => access === "write").length;
23095
23101
  if (maxWriteRepositories > writable) issues.push(issue2("semantic", "write_repository_bound_invalid", file, `${stepId} maxWriteRepositories exceeds writable repository scope`));
23096
23102
  }
23097
- const join51 = objectValue(step.join);
23098
- const minimumPassed = join51 && typeof join51.minimumPassed === "number" ? join51.minimumPassed : void 0;
23103
+ const join52 = objectValue(step.join);
23104
+ const minimumPassed = join52 && typeof join52.minimumPassed === "number" ? join52.minimumPassed : void 0;
23099
23105
  if (minimumPassed !== void 0 && minimumPassed > maximum) issues.push(issue2("semantic", "join_impossible", file, `${stepId} minimumPassed exceeds assignment maximum`));
23100
23106
  const distinctBy = names(assignment?.distinctBy);
23101
23107
  if (distinctBy.length > 0) {
@@ -23659,9 +23665,9 @@ function planKxmInitialization(start = process.cwd(), options = {}) {
23659
23665
 
23660
23666
  // plugins/kxm/src/runtime-supervisor.ts
23661
23667
  import { spawn as spawn2 } from "node:child_process";
23662
- import { createHash as createHash13, createHmac as createHmac2, randomBytes as randomBytes2, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
23663
- import { chmodSync as chmodSync4, existsSync as existsSync16, lstatSync as lstatSync6, mkdirSync as mkdirSync11, readFileSync as readFileSync14, renameSync as renameSync5, rmSync as rmSync5, writeFileSync as writeFileSync12 } from "node:fs";
23664
- import { dirname as dirname11, isAbsolute as isAbsolute7, join as join16 } from "node:path";
23668
+ import { createHash as createHash14, createHmac as createHmac2, randomBytes as randomBytes2, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
23669
+ import { chmodSync as chmodSync4, existsSync as existsSync17, lstatSync as lstatSync6, mkdirSync as mkdirSync11, readFileSync as readFileSync15, renameSync as renameSync5, rmSync as rmSync5, writeFileSync as writeFileSync12 } from "node:fs";
23670
+ import { dirname as dirname11, isAbsolute as isAbsolute7, join as join17 } from "node:path";
23665
23671
 
23666
23672
  // plugins/kxm/src/runtime-store.ts
23667
23673
  import { existsSync as existsSync10, lstatSync as lstatSync4, mkdirSync as mkdirSync6, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "node:fs";
@@ -25062,6 +25068,9 @@ var EVENT_STORE_TABLES = {
25062
25068
  };
25063
25069
  var KXM_EVENT_STORE_TABLE_NAMES = Object.keys(EVENT_STORE_TABLES).sort();
25064
25070
 
25071
+ // plugins/kxm/src/engine-plan.ts
25072
+ import { createHash as createHash11 } from "node:crypto";
25073
+
25065
25074
  // plugins/kxm/src/engine-compile.ts
25066
25075
  var KXM_COMPILED_WORKFLOW_SCHEMA = "kxm.compiled-workflow.v1";
25067
25076
  var WORKFLOW_SCHEMA = "kxm.workflow.v1";
@@ -25210,7 +25219,7 @@ function compileStep(step, index, stepIndex, requirePlanHash, sink) {
25210
25219
  const maxAttempts = compileCountField(step.maxAttempts, 1, `${id}.maxAttempts`, id, sink);
25211
25220
  const timeoutMs = compileOptionalDuration(step.timeoutMs, `${id}.timeoutMs`, id, sink);
25212
25221
  const assignments = compileAssignments(step, id, agent, sink);
25213
- const join51 = compileJoin(step, id, sink);
25222
+ const join52 = compileJoin(step, id, sink);
25214
25223
  const requiredEvidence = compileEvidence(step, id, sink);
25215
25224
  const transitions = compileTransitions(step, id, index, stepIndex, sink);
25216
25225
  const outcomes = Object.keys(transitions).sort(compareCodeUnits4);
@@ -25238,7 +25247,7 @@ function compileStep(step, index, stepIndex, requirePlanHash, sink) {
25238
25247
  transitions: orderedTransitions,
25239
25248
  requiresPlanHash: requirePlanHash.includes(id),
25240
25249
  assignments,
25241
- join: join51
25250
+ join: join52
25242
25251
  };
25243
25252
  if (kind === "agent" || kind === "moa") {
25244
25253
  if (!agent) return void 0;
@@ -25288,15 +25297,15 @@ function compileAssignments(step, stepId, primaryAgentId, sink) {
25288
25297
  };
25289
25298
  }
25290
25299
  function compileJoin(step, stepId, sink) {
25291
- const join51 = objectValue2(step.join);
25292
- if (!join51) return { strategy: "all" };
25293
- const declared = stringValue2(join51.strategy);
25300
+ const join52 = objectValue2(step.join);
25301
+ if (!join52) return { strategy: "all" };
25302
+ const declared = stringValue2(join52.strategy);
25294
25303
  const strategy = declared && JOIN_STRATEGIES.has(declared) ? declared : "all";
25295
- const minimumPassed = compileOptionalCount(join51.minimumPassed, `${stepId}.join.minimumPassed`, stepId, sink);
25304
+ const minimumPassed = compileOptionalCount(join52.minimumPassed, `${stepId}.join.minimumPassed`, stepId, sink);
25296
25305
  const compiled = {
25297
25306
  strategy,
25298
25307
  ...minimumPassed !== void 0 ? { minimumPassed } : {},
25299
- ...typeof join51.cancelRemaining === "boolean" ? { cancelRemaining: join51.cancelRemaining } : {}
25308
+ ...typeof join52.cancelRemaining === "boolean" ? { cancelRemaining: join52.cancelRemaining } : {}
25300
25309
  };
25301
25310
  return compiled;
25302
25311
  }
@@ -25565,7 +25574,110 @@ function pushIssue(sink, issue3) {
25565
25574
  sink.issues.push({ workflowId: sink.workflowId, ...issue3 });
25566
25575
  }
25567
25576
 
25577
+ // plugins/kxm/src/engine-plan.ts
25578
+ function kxmSha256(input) {
25579
+ return `sha256:${createHash11("sha256").update(input, "utf8").digest("hex")}`;
25580
+ }
25581
+ function parseGateDefinition(raw, gateId, runId) {
25582
+ const value = asObject(raw, runId, `gates.definitions.${gateId}`);
25583
+ const kind = asString(value.kind, runId, `gates.definitions.${gateId}.kind`);
25584
+ if (kind === "command") {
25585
+ assertExactKeys(value, ["kind", "argv", "timeoutMs"], ["cwd"], runId, `gates.definitions.${gateId}`);
25586
+ const argv = asStringArray(value.argv, runId, `gates.definitions.${gateId}.argv`);
25587
+ if (argv.length < 1 || argv.length > 64) {
25588
+ throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId}.argv length is invalid`);
25589
+ }
25590
+ for (const [index, entry] of argv.entries()) {
25591
+ if (entry.length === 0 || entry.length > 4096 || entry.includes("\0")) {
25592
+ throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId}.argv[${index}] is invalid`);
25593
+ }
25594
+ }
25595
+ const executable = argv[0];
25596
+ if (executable === "." || executable === ".." || executable.includes("\\") || !executable.startsWith("/") && executable.includes("/")) {
25597
+ throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId} argv[0] must be a bare executable or absolute POSIX path`);
25598
+ }
25599
+ const timeoutMs = value.timeoutMs;
25600
+ if (typeof timeoutMs !== "number" || !Number.isInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2147483647) {
25601
+ throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId}.timeoutMs is invalid`);
25602
+ }
25603
+ if (value.cwd !== void 0 && value.cwd !== "control") {
25604
+ throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId}.cwd is invalid`);
25605
+ }
25606
+ return {
25607
+ kind: "command",
25608
+ argv,
25609
+ timeoutMs,
25610
+ ...value.cwd === "control" ? { cwd: "control" } : {}
25611
+ };
25612
+ }
25613
+ if (kind === "artifacts-exist") {
25614
+ assertExactKeys(value, ["kind", "paths"], [], runId, `gates.definitions.${gateId}`);
25615
+ const paths = asStringArray(value.paths, runId, `gates.definitions.${gateId}.paths`);
25616
+ if (paths.length < 1 || paths.length > 64) {
25617
+ throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId}.paths length is invalid`);
25618
+ }
25619
+ if (new Set(paths).size !== paths.length) {
25620
+ throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId}.paths are not unique`);
25621
+ }
25622
+ for (const path4 of paths) {
25623
+ if (path4 === "." || !kxmPortablePath(path4) || path4.startsWith("/") || path4.includes("\\")) {
25624
+ throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId} path is not a portable relative path`);
25625
+ }
25626
+ }
25627
+ return { kind: "artifacts-exist", paths };
25628
+ }
25629
+ if (kind === "reserved") {
25630
+ assertExactKeys(value, ["kind"], [], runId, `gates.definitions.${gateId}`);
25631
+ return { kind: "reserved" };
25632
+ }
25633
+ throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId} kind is invalid`);
25634
+ }
25635
+ function asObject(value, runId, label) {
25636
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
25637
+ throw runtimeError("run_plan_corrupt", runId, `${label} is not an object`);
25638
+ }
25639
+ return value;
25640
+ }
25641
+ function assertExactKeys(value, required, optional, runId, label) {
25642
+ const allowed = /* @__PURE__ */ new Set([...required, ...optional]);
25643
+ for (const key of required) {
25644
+ if (!(key in value)) throw runtimeError("run_plan_corrupt", runId, `${label} is missing ${key}`);
25645
+ }
25646
+ for (const key of Object.keys(value)) {
25647
+ if (!allowed.has(key)) throw runtimeError("run_plan_corrupt", runId, `${label} has unexpected key ${key}`);
25648
+ }
25649
+ }
25650
+ function asString(value, runId, label) {
25651
+ if (typeof value !== "string" || value.length === 0) {
25652
+ throw runtimeError("run_plan_corrupt", runId, `${label} is not a non-empty string`);
25653
+ }
25654
+ return value;
25655
+ }
25656
+ function asStringArray(value, runId, label) {
25657
+ if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) {
25658
+ throw runtimeError("run_plan_corrupt", runId, `${label} is not a string array`);
25659
+ }
25660
+ return [...value];
25661
+ }
25662
+
25663
+ // plugins/kxm/src/gate-hash.ts
25664
+ function gateRegistryHash(registryValue) {
25665
+ return kxmSha256(kxmCanonicalJson(registryValue));
25666
+ }
25667
+
25568
25668
  // plugins/kxm/src/runtime-service.ts
25669
+ function objectValue3(value) {
25670
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
25671
+ }
25672
+ function kxmProjectAdmissionLimits(bundle) {
25673
+ const limits = objectValue3(bundle.project.value.limits);
25674
+ const maxConcurrentRuns = typeof limits?.maxConcurrentRuns === "number" && Number.isInteger(limits.maxConcurrentRuns) && limits.maxConcurrentRuns >= 1 ? limits.maxConcurrentRuns : 1;
25675
+ return {
25676
+ maxConcurrentRuns,
25677
+ ...typeof limits?.maxRunDurationMs === "number" ? { maxRunDurationMs: limits.maxRunDurationMs } : {},
25678
+ ...typeof limits?.maxAgentTimeMs === "number" ? { maxAgentTimeMs: limits.maxAgentTimeMs } : {}
25679
+ };
25680
+ }
25569
25681
  var RUNTIME_EPOCH_NS = process.hrtime.bigint();
25570
25682
 
25571
25683
  // plugins/kxm/src/oneshot-evidence.ts
@@ -25575,7 +25687,7 @@ var RECORD_LIMIT = 16 * 1024 * 1024;
25575
25687
 
25576
25688
  // plugins/kxm/src/prices.ts
25577
25689
  var import_yaml3 = __toESM(require_dist(), 1);
25578
- import { createHash as createHash11 } from "node:crypto";
25690
+ import { createHash as createHash12 } from "node:crypto";
25579
25691
  import { existsSync as existsSync11, readFileSync as readFileSync9, statSync, writeFileSync as writeFileSync7 } from "node:fs";
25580
25692
  import { join as join11 } from "node:path";
25581
25693
 
@@ -25638,7 +25750,7 @@ function hashPriceCatalog(catalog) {
25638
25750
  }))
25639
25751
  })).sort((a, b2) => a.id.localeCompare(b2.id))
25640
25752
  };
25641
- return createHash11("sha256").update(JSON.stringify(canonical2), "utf8").digest("hex");
25753
+ return createHash12("sha256").update(JSON.stringify(canonical2), "utf8").digest("hex");
25642
25754
  }
25643
25755
  function parsePriceCatalog(text) {
25644
25756
  const parsed = (0, import_yaml3.parse)(text);
@@ -25776,6 +25888,8 @@ function acknowledgePriceCatalog(projectRoot, now = /* @__PURE__ */ new Date())
25776
25888
 
25777
25889
  // plugins/kxm/src/engine.ts
25778
25890
  var import_yaml7 = __toESM(require_dist(), 1);
25891
+ import { existsSync as existsSync15, readFileSync as readFileSync13 } from "node:fs";
25892
+ import { join as join15 } from "node:path";
25779
25893
 
25780
25894
  // plugins/kxm/src/routes.ts
25781
25895
  var import_yaml4 = __toESM(require_dist(), 1);
@@ -25835,6 +25949,10 @@ function listRoleBindings(root) {
25835
25949
  }
25836
25950
  return result;
25837
25951
  }
25952
+ function isRouteAdmitted(root, modelId2) {
25953
+ const policy = loadRoutePolicy(root);
25954
+ return policy.admitted.includes(modelId2) && !policy.disabled.includes(modelId2);
25955
+ }
25838
25956
  function listInventoryModels(root) {
25839
25957
  const path4 = join12(root, ".kxm", "models", "inventory.yaml");
25840
25958
  if (!existsSync12(path4)) return [];
@@ -26120,7 +26238,7 @@ function syncHarnessMemory(repoRoot, options = {}) {
26120
26238
 
26121
26239
  // plugins/kxm/src/skills.ts
26122
26240
  var import_yaml6 = __toESM(require_dist(), 1);
26123
- import { createHash as createHash12 } from "node:crypto";
26241
+ import { createHash as createHash13 } from "node:crypto";
26124
26242
  import { existsSync as existsSync14, mkdirSync as mkdirSync9, readdirSync as readdirSync5, readFileSync as readFileSync12, renameSync as renameSync3, rmSync as rmSync3, statSync as statSync2, writeFileSync as writeFileSync10 } from "node:fs";
26125
26243
  import { dirname as dirname9, join as join14 } from "node:path";
26126
26244
  var SKILL_CANDIDATE_SCHEMA = "kxm.skill-candidate.v1";
@@ -26145,7 +26263,7 @@ var SkillLifecycleError = class extends Error {
26145
26263
  }
26146
26264
  };
26147
26265
  function skillContentSha256(content) {
26148
- return createHash12("sha256").update(content, "utf8").digest("hex");
26266
+ return createHash13("sha256").update(content, "utf8").digest("hex");
26149
26267
  }
26150
26268
  function skillIdFor(name, contentSha256) {
26151
26269
  const slug = name.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
@@ -27159,13 +27277,418 @@ function formatRoutingReport(report2, options = {}) {
27159
27277
  }
27160
27278
 
27161
27279
  // plugins/kxm/src/engine.ts
27280
+ function resolveProducerRoute(projectRoot, step, agentId) {
27281
+ let agentModel;
27282
+ const agentFile = join15(projectRoot, ".kxm", "agents", `${agentId}.yaml`);
27283
+ if (existsSync15(agentFile)) {
27284
+ try {
27285
+ const parsed = (0, import_yaml7.parse)(readFileSync13(agentFile, "utf8"));
27286
+ if (typeof parsed?.model === "string") {
27287
+ agentModel = parsed.model;
27288
+ } else if (parsed?.model && typeof parsed.model === "object" && !Array.isArray(parsed.model)) {
27289
+ const declared = parsed.model;
27290
+ const providerOk = typeof declared.provider === "string" && declared.provider.length > 0 && !declared.provider.includes("/");
27291
+ const modelOk = typeof declared.model === "string" && declared.model.length > 0 && !declared.model.startsWith("/") && !declared.model.endsWith("/");
27292
+ if (providerOk && modelOk) {
27293
+ agentModel = `${declared.provider}/${declared.model}`;
27294
+ } else {
27295
+ return {
27296
+ error: {
27297
+ reason: "step_unsupported",
27298
+ field: "model",
27299
+ detail: "producer_route_unsupported: invalid model declaration"
27300
+ }
27301
+ };
27302
+ }
27303
+ }
27304
+ } catch {
27305
+ }
27306
+ }
27307
+ let selector = typeof step.model === "string" ? step.model : agentModel;
27308
+ if (!selector) {
27309
+ if (agentId === "implementer") {
27310
+ selector = "xai/grok-4.6";
27311
+ }
27312
+ }
27313
+ if (!selector) {
27314
+ return {
27315
+ error: {
27316
+ reason: "step_unsupported",
27317
+ field: "model",
27318
+ detail: "producer_route_unsupported: agent or step has no model declared"
27319
+ }
27320
+ };
27321
+ }
27322
+ const slash = selector.indexOf("/");
27323
+ if (slash <= 0 || slash === selector.length - 1) {
27324
+ return {
27325
+ error: {
27326
+ reason: "step_unsupported",
27327
+ field: "model",
27328
+ detail: `producer_route_unsupported: invalid selector '${selector}'`
27329
+ }
27330
+ };
27331
+ }
27332
+ if (!isRouteAdmitted(projectRoot, selector)) {
27333
+ return {
27334
+ error: {
27335
+ reason: "step_unsupported",
27336
+ field: "model",
27337
+ detail: `producer_route_unsupported: model '${selector}' is not admitted`
27338
+ }
27339
+ };
27340
+ }
27341
+ const role = agentId === "implementer" ? "writer" : agentId;
27342
+ const roleBindings = listRoleBindings(projectRoot);
27343
+ const roster = roleBindings[role];
27344
+ if (roster && !roster.includes(selector)) {
27345
+ return {
27346
+ error: {
27347
+ reason: "step_unsupported",
27348
+ field: "model",
27349
+ detail: `producer_route_unsupported: model '${selector}' not in role '${role}' roster`
27350
+ }
27351
+ };
27352
+ }
27353
+ const provider = selector.slice(0, slash);
27354
+ const model = selector.slice(slash + 1);
27355
+ return { provider, model, selector };
27356
+ }
27162
27357
  var ENGINE_ROUTING_METADATA_KEYS = /* @__PURE__ */ new Set(["workflowId", "askSha256", "objectiveSha256", "stepWrites"]);
27163
27358
  var MAX_PRODUCER_ROUTING_METADATA_FIELDS = MAX_PROVIDER_METADATA_FIELDS - ENGINE_ROUTING_METADATA_KEYS.size;
27359
+ function kxmLiveRunPrerequisites(bundle, workflowId, projectRoot) {
27360
+ const workflow = bundle.workflows.get(workflowId);
27361
+ if (!workflow) throw runtimeError("run_workflow_unknown", workflowId, `workflow ${workflowId} does not exist in this project`);
27362
+ const plan = compileKxmWorkflow({ id: workflowId, value: workflow.value, logicalPath: workflow.logicalPath });
27363
+ const envelope = { plan, projectLimits: kxmProjectAdmissionLimits(bundle), gates: pinnedGatesForCompiledPlan(plan, bundle, projectRoot) };
27364
+ const prerequisites = [];
27365
+ const limit = unsupportedLimit(envelope);
27366
+ if (limit) prerequisites.push(limit);
27367
+ const visited = /* @__PURE__ */ new Set();
27368
+ const pending = [plan.entryStepId];
27369
+ while (pending.length > 0) {
27370
+ const stepId = pending.pop();
27371
+ if (visited.has(stepId)) continue;
27372
+ visited.add(stepId);
27373
+ const step = plan.steps[stepId];
27374
+ for (const transition2 of Object.values(step.transitions)) {
27375
+ if (transition2.to === "step") pending.push(transition2.target);
27376
+ }
27377
+ const unsupported = step.kind === "gate" ? unsupportedGateStep(plan, step, envelope, { projectRoot }) ?? starterGatePrerequisite(step, envelope.gates, projectRoot) : unsupportedStep(plan, step, "oneshot");
27378
+ if (unsupported) {
27379
+ prerequisites.push({ ...unsupported, stepId });
27380
+ continue;
27381
+ }
27382
+ if (step.kind !== "agent" && step.kind !== "moa") continue;
27383
+ const agentIds = step.assignments.allowedAgents.length > 0 ? step.assignments.allowedAgents : [step.agent];
27384
+ for (const agentId of agentIds) {
27385
+ const agent = bundle.agents.get(agentId);
27386
+ const harness = String(agent?.value.harness ?? bundle.project.value.defaultHarness ?? "pi");
27387
+ const permission = Object.values(step.repositories).includes("write") ? "edit" : "read-only";
27388
+ if (!BUILTIN_HARNESSES.some((entry) => entry.id === harness && entry.oneShot) || !oneShotPermissionArgs(harness, permission)) {
27389
+ prerequisites.push({ reason: "step_unsupported", stepId, field: "harness", detail: `${agentId}: ${harness} has no audited ${permission} one-shot profile; use a supported workflow or execute this work directly in ${harness}, without substituting another harness` });
27390
+ continue;
27391
+ }
27392
+ const route = resolveProducerRoute(projectRoot, step, agentId);
27393
+ if ("error" in route) {
27394
+ prerequisites.push({ ...route.error, stepId, detail: `${agentId}: ${route.error.detail}; configure its model in .kxm/agents/${agentId}.yaml and admit the installed model with kxm routes admit --model <provider/model>` });
27395
+ continue;
27396
+ }
27397
+ const writeRefusal = unsupportedLiveWrite(projectRoot, step, agentId, route.selector, envelope.projectLimits.maxConcurrentRuns);
27398
+ if (writeRefusal) {
27399
+ prerequisites.push({ ...writeRefusal, stepId });
27400
+ continue;
27401
+ }
27402
+ const compatible = validateHarnessModelPair(harness, route);
27403
+ if (!compatible.valid) {
27404
+ prerequisites.push({ reason: "step_unsupported", stepId, field: "model", detail: `${agentId}: ${compatible.message ?? `${harness} cannot run ${route.selector}`}; configure a model supported by ${harness} in .kxm/agents/${agentId}.yaml` });
27405
+ }
27406
+ }
27407
+ }
27408
+ return prerequisites;
27409
+ }
27410
+ function starterGatePrerequisite(step, gates, projectRoot) {
27411
+ const definition = gates.definitions[step.gate];
27412
+ if (!definition || definition.kind !== "command" || definition.argv.length !== 2 || definition.argv[0] !== "npm" || definition.argv[1] !== "test") return void 0;
27413
+ let testScript;
27414
+ try {
27415
+ const manifest = JSON.parse(readFileSync13(join15(projectRoot, "package.json"), "utf8"));
27416
+ if (manifest && typeof manifest === "object" && "scripts" in manifest) {
27417
+ const scripts = manifest.scripts;
27418
+ if (scripts && typeof scripts === "object" && "test" in scripts) testScript = scripts.test;
27419
+ }
27420
+ } catch {
27421
+ }
27422
+ if (typeof testScript !== "string" || testScript.trim().length === 0) {
27423
+ return { reason: "gate_unsupported", field: `gates.${step.gate}.argv`, detail: `gate ${step.gate} runs npm test but this repository has no readable package.json with scripts.test; configure .kxm/gates.yaml gates.${step.gate}.argv for the repository's actual test runner before driving this workflow` };
27424
+ }
27425
+ return void 0;
27426
+ }
27427
+ function unsupportedLimit(envelope) {
27428
+ if (envelope.plan.limits.maxAgentTimeMs !== void 0) {
27429
+ return { reason: "limit_unsupported", field: "limits.maxAgentTimeMs", detail: "agent-time budget enforcement is not available in this slice" };
27430
+ }
27431
+ if (envelope.projectLimits.maxAgentTimeMs !== void 0) {
27432
+ return { reason: "limit_unsupported", field: "project.limits.maxAgentTimeMs", detail: "agent-time budget enforcement is not available in this slice" };
27433
+ }
27434
+ return void 0;
27435
+ }
27436
+ function unsupportedStep(plan, step, producerId) {
27437
+ if (step.kind === "gate") throw runtimeError("run_plan_corrupt", plan.workflowId, `unsupportedStep called on gate without envelope; use unsupportedGateStep instead`);
27438
+ if (step.kind !== "agent" && step.kind !== "moa" && step.kind !== "approval" && step.kind !== "wait") {
27439
+ return { reason: "step_unsupported", field: "kind", detail: `step kind ${step.kind} is not executed in this slice` };
27440
+ }
27441
+ if (step.assignments.maxAttemptsPerAssignment > 2) {
27442
+ return { reason: "step_unsupported", field: "assignments.maxAttemptsPerAssignment", detail: "only up to two physical attempts are supported in this slice" };
27443
+ }
27444
+ if (step.join.strategy !== "all" && step.join.strategy !== "all-settled") {
27445
+ return { reason: "step_unsupported", field: "join.strategy", detail: "only join all or all-settled is supported" };
27446
+ }
27447
+ if (step.join.strategy === "all" && step.join.minimumPassed !== void 0) {
27448
+ return { reason: "step_unsupported", field: "join.minimumPassed", detail: "minimumPassed requires join strategy all-settled" };
27449
+ }
27450
+ if (step.join.cancelRemaining) {
27451
+ return { reason: "step_unsupported", field: "join.cancelRemaining", detail: "cancelRemaining is not executed in this slice" };
27452
+ }
27453
+ if (step.assignments.distinctBy.some((d2) => d2 !== "provider")) {
27454
+ return { reason: "step_unsupported", field: "assignments.distinctBy", detail: "only distinctBy provider is supported in this slice" };
27455
+ }
27456
+ if (step.assignments.maxWriteRepositories !== void 0 && step.assignments.maxWriteRepositories > 1) {
27457
+ return { reason: "step_unsupported", field: "assignments.maxWriteRepositories", detail: "at most one write repository is supported in this slice" };
27458
+ }
27459
+ for (const evidence of step.requiredEvidence) {
27460
+ if (!evidence || !["artifact", "assignment-result", "gate", "approval", "receipt"].includes(evidence.kind)) {
27461
+ return { reason: "step_unsupported", field: "requiredEvidence", detail: `evidence kind '${evidence?.kind}' is not supported in this slice` };
27462
+ }
27463
+ }
27464
+ if (step.timeoutMs !== void 0 && step.timeoutMs <= 0) {
27465
+ return { reason: "step_unsupported", field: "timeoutMs", detail: "timeoutMs must be positive" };
27466
+ }
27467
+ if (step.safeSpeculation) return { reason: "step_unsupported", field: "safeSpeculation", detail: "speculation is not executed in this slice" };
27468
+ if (step.tools) return { reason: "step_unsupported", field: "tools", detail: "tools are not executed in this slice" };
27469
+ if (step.secrets.length > 0) return { reason: "step_unsupported", field: "secrets", detail: "secrets are not executed in this slice" };
27470
+ for (const [repoId, access] of Object.entries(step.repositories)) {
27471
+ if (access !== "read" && access !== "write" && access !== "none") {
27472
+ return { reason: "step_unsupported", field: "repositories", detail: `invalid repository access '${access}' on ${repoId}` };
27473
+ }
27474
+ }
27475
+ return void 0;
27476
+ }
27477
+ function readYamlRecord(path4) {
27478
+ if (!existsSync15(path4)) return void 0;
27479
+ try {
27480
+ const parsed = (0, import_yaml7.parse)(readFileSync13(path4, "utf8"));
27481
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
27482
+ } catch {
27483
+ return void 0;
27484
+ }
27485
+ return void 0;
27486
+ }
27487
+ function agentHarness(projectRoot, agentId) {
27488
+ const harness = readYamlRecord(join15(projectRoot, ".kxm", "agents", `${agentId}.yaml`))?.harness;
27489
+ return typeof harness === "string" && harness.length > 0 ? harness : void 0;
27490
+ }
27491
+ function projectDefaultHarness(projectRoot) {
27492
+ const harness = readYamlRecord(join15(projectRoot, ".kxm", "project.yaml"))?.defaultHarness;
27493
+ return typeof harness === "string" && harness.length > 0 ? harness : "pi";
27494
+ }
27495
+ function unsupportedLiveWrite(projectRoot, step, agentId, selector, maxConcurrentRuns) {
27496
+ if (!Object.values(step.repositories).some((access) => access === "write")) return void 0;
27497
+ if (step.assignments.maximum !== 1) {
27498
+ return {
27499
+ reason: "step_unsupported",
27500
+ field: "assignments.maximum",
27501
+ detail: "live write steps run a single assignment; the checkout witness cannot attribute edits between writers"
27502
+ };
27503
+ }
27504
+ if (maxConcurrentRuns !== 1) {
27505
+ return {
27506
+ reason: "step_unsupported",
27507
+ field: "limits.maxConcurrentRuns",
27508
+ detail: "live write steps require limits.maxConcurrentRuns of 1; concurrent runs share one checkout"
27509
+ };
27510
+ }
27511
+ const harness = agentHarness(projectRoot, agentId) ?? projectDefaultHarness(projectRoot);
27512
+ if (!oneShotWriterArgs(harness)) {
27513
+ return {
27514
+ reason: "step_unsupported",
27515
+ field: "repositories",
27516
+ detail: `live write steps require an audited writer profile; ${harness} has none`
27517
+ };
27518
+ }
27519
+ const rosterPath = join15(projectRoot, ".kxm", "roster.yaml");
27520
+ if (!existsSync15(rosterPath)) return void 0;
27521
+ const roster = readYamlRecord(rosterPath);
27522
+ if (!roster || roster.schema !== "kxm.developer-roster.v1") {
27523
+ return { reason: "step_unsupported", field: "model", detail: "live write steps require a readable kxm.developer-roster.v1" };
27524
+ }
27525
+ const routes = roster.routes;
27526
+ const lineup = roster.lineup;
27527
+ const writerIds = lineup && typeof lineup === "object" && !Array.isArray(lineup) ? lineup.writer : void 0;
27528
+ if (!routes || typeof routes !== "object" || Array.isArray(routes) || !Array.isArray(writerIds)) {
27529
+ return { reason: "step_unsupported", field: "model", detail: "developer roster has no writer lineup" };
27530
+ }
27531
+ const allowed = writerIds.some((id) => {
27532
+ if (typeof id !== "string") return false;
27533
+ const route = routes[id];
27534
+ if (!route || typeof route !== "object" || Array.isArray(route)) return false;
27535
+ const record = route;
27536
+ if (record.harness !== harness || record.status !== "admitted") return false;
27537
+ if (!Array.isArray(record.permissions) || !record.permissions.includes("edit")) return false;
27538
+ const model = typeof record.model === "string" ? record.model : "";
27539
+ const vendor = typeof record.vendor === "string" ? record.vendor : "";
27540
+ return model === selector || vendor.length > 0 && `${vendor}/${model}` === selector;
27541
+ });
27542
+ if (!allowed) {
27543
+ return {
27544
+ reason: "step_unsupported",
27545
+ field: "model",
27546
+ detail: `live write route ${selector} on ${harness} is not on the developer roster writer lineup`
27547
+ };
27548
+ }
27549
+ return void 0;
27550
+ }
27551
+ function unsupportedGateStep(plan, step, envelope, context) {
27552
+ if (!step.gate) return { reason: "step_unsupported", field: "gate", detail: "gate step is missing gate id" };
27553
+ if (envelope.gates.registry === null) {
27554
+ return { reason: "step_unsupported", field: "gate", detail: `step ${step.id} refers to a gate but registry is null` };
27555
+ }
27556
+ const definition = envelope.gates.definitions[step.gate];
27557
+ if (!definition) {
27558
+ return { reason: "step_unsupported", field: "gate", detail: `gate definition ${step.gate} not found in registry` };
27559
+ }
27560
+ if (definition.kind === "reserved") {
27561
+ return { reason: "gate_unsupported", field: "gate", detail: `gate ${step.gate} is reserved and cannot be executed` };
27562
+ }
27563
+ if (step.assignments.allowedAgents && step.assignments.allowedAgents.length > 0) {
27564
+ return { reason: "step_unsupported", field: "assignments.allowedAgents", detail: "gate steps do not support allowedAgents" };
27565
+ }
27566
+ if (step.assignments.minimum !== 1 || step.assignments.target !== 1 || step.assignments.maximum !== 1) {
27567
+ return { reason: "step_unsupported", field: "assignments", detail: "gate steps require exactly 1 agent assignment" };
27568
+ }
27569
+ if (step.assignments.maxParallel !== 1) {
27570
+ return { reason: "step_unsupported", field: "assignments.maxParallel", detail: "gate steps require maxParallel of 1" };
27571
+ }
27572
+ if (step.assignments.maxAttemptsPerAssignment !== 1) {
27573
+ return { reason: "step_unsupported", field: "assignments.maxAttemptsPerAssignment", detail: "gate steps require maxAttemptsPerAssignment of 1" };
27574
+ }
27575
+ if (step.assignments.distinctBy && step.assignments.distinctBy.length > 0) {
27576
+ return { reason: "step_unsupported", field: "assignments.distinctBy", detail: "gate steps do not support distinctBy" };
27577
+ }
27578
+ if (step.assignments.maxWriteRepositories) {
27579
+ return { reason: "step_unsupported", field: "assignments.maxWriteRepositories", detail: "gate steps do not support maxWriteRepositories" };
27580
+ }
27581
+ if (step.join.strategy !== "all") {
27582
+ return { reason: "step_unsupported", field: "join", detail: `gate steps require join strategy 'all', not '${step.join.strategy}'` };
27583
+ }
27584
+ if (step.join.minimumPassed !== void 0) {
27585
+ return { reason: "step_unsupported", field: "join", detail: "gate steps do not support minimumPassed" };
27586
+ }
27587
+ if (step.join.cancelRemaining) {
27588
+ return { reason: "step_unsupported", field: "join", detail: "gate steps do not support cancelRemaining" };
27589
+ }
27590
+ for (const evidence of step.requiredEvidence) {
27591
+ if (!evidence || !["gate", "artifact", "receipt", "assignment-result", "approval"].includes(evidence.kind)) {
27592
+ return { reason: "step_unsupported", field: "requiredEvidence", detail: `gate steps do not support evidence kind '${evidence?.kind}'` };
27593
+ }
27594
+ }
27595
+ if (step.safeSpeculation) {
27596
+ return { reason: "step_unsupported", field: "safeSpeculation", detail: "gate steps do not support safeSpeculation" };
27597
+ }
27598
+ if (step.tools) {
27599
+ return { reason: "step_unsupported", field: "tools", detail: "gate steps do not support tools" };
27600
+ }
27601
+ if (step.secrets && step.secrets.length > 0) {
27602
+ return { reason: "step_unsupported", field: "secrets", detail: "gate steps do not support secrets" };
27603
+ }
27604
+ if (step.model) {
27605
+ return { reason: "step_unsupported", field: "model", detail: "gate steps do not support model" };
27606
+ }
27607
+ if (plan.reproOracle?.stageId === step.id) {
27608
+ return { reason: "step_unsupported", field: "reproOracle", detail: "gate steps cannot be repro oracle" };
27609
+ }
27610
+ if (plan.planHash?.stageId === step.id) {
27611
+ return { reason: "step_unsupported", field: "planHash", detail: "gate steps cannot be plan hash oracle" };
27612
+ }
27613
+ if (step.timeoutMs !== void 0) {
27614
+ if (definition.kind === "artifacts-exist") {
27615
+ return { reason: "step_unsupported", field: "timeoutMs", detail: "artifacts-exist gates do not support timeoutMs" };
27616
+ }
27617
+ if (definition.kind === "command" && definition.timeoutMs !== void 0 && step.timeoutMs < definition.timeoutMs) {
27618
+ return { reason: "step_unsupported", field: "timeoutMs", detail: "command step timeoutMs narrower than the registry timeout is refused" };
27619
+ }
27620
+ }
27621
+ const repositories = step.repositories;
27622
+ if (!repositories || Object.keys(repositories).length === 0) {
27623
+ return { reason: "step_unsupported", field: "repositories", detail: "gate steps require repository declarations" };
27624
+ }
27625
+ if (definition.kind === "artifacts-exist") {
27626
+ const controlAccess = repositories.control;
27627
+ if (controlAccess !== "read" && controlAccess !== "write") {
27628
+ return { reason: "step_unsupported", field: "repositories", detail: "artifacts-exist gates require control repository access as read or write" };
27629
+ }
27630
+ for (const [repoId, access] of Object.entries(repositories)) {
27631
+ if (repoId !== "control" && access !== "none") {
27632
+ return { reason: "step_unsupported", field: "repositories", detail: "artifacts-exist gates require other repositories to have access 'none'" };
27633
+ }
27634
+ }
27635
+ } else if (definition.kind === "command") {
27636
+ for (const [repoId, access] of Object.entries(repositories)) {
27637
+ if (access !== "read" && access !== "write" && access !== "none") {
27638
+ return { reason: "step_unsupported", field: "repositories", detail: `invalid repository access '${access}' on ${repoId}` };
27639
+ }
27640
+ }
27641
+ const hasWrite = Object.values(repositories).some((access) => access === "write");
27642
+ if (!hasWrite) {
27643
+ return { reason: "step_unsupported", field: "repositories", detail: "command gates require at least one repository with write access" };
27644
+ }
27645
+ }
27646
+ const expectPass = step.expect === "pass";
27647
+ if (expectPass) {
27648
+ if (!step.outcomes.includes("passed")) {
27649
+ return { reason: "gate_outcome_undeclared", field: "outcomes", detail: "step expects pass but missing required outcome passed; spell it as 'passed'" };
27650
+ }
27651
+ if (!step.outcomes.includes("implementation-failure") && !step.outcomes.includes("failed")) {
27652
+ return { reason: "gate_outcome_undeclared", field: "outcomes", detail: "step expects pass but missing required failure outcome; declare implementation-failure or failed" };
27653
+ }
27654
+ } else {
27655
+ if (!step.outcomes.includes("repro-missing")) {
27656
+ return { reason: "gate_outcome_undeclared", field: "outcomes", detail: "step expects fail but missing required outcome repro-missing; spell it as 'repro-missing'" };
27657
+ }
27658
+ if (!step.outcomes.includes("implementation-failure") && !step.outcomes.includes("failed")) {
27659
+ return { reason: "gate_outcome_undeclared", field: "outcomes", detail: "step expects fail but missing required failure outcome; declare implementation-failure or failed" };
27660
+ }
27661
+ }
27662
+ if (context && projectRuntimeKey(context.projectRoot) !== envelope.gates.controlRoot.projectKey) {
27663
+ throw runtimeError("run_owner_mismatch", plan.workflowId, `gate control root does not match project`);
27664
+ }
27665
+ if (definition.kind === "artifacts-exist" && step.expect === "fail") {
27666
+ return { reason: "step_unsupported", field: "expect", detail: "artifacts-exist gates cannot expect fail" };
27667
+ }
27668
+ return void 0;
27669
+ }
27670
+ function pinnedGatesForCompiledPlan(plan, bundle, projectRoot) {
27671
+ const referenced = /* @__PURE__ */ new Set();
27672
+ for (const step of Object.values(plan.steps)) {
27673
+ if (step.kind === "gate") referenced.add(step.gate);
27674
+ }
27675
+ const definitions = /* @__PURE__ */ Object.create(null);
27676
+ const registryValue = bundle.gateRegistry?.value;
27677
+ const gatesObject = registryValue && typeof registryValue.gates === "object" && registryValue.gates && !Array.isArray(registryValue.gates) ? registryValue.gates : {};
27678
+ for (const gateId of referenced) {
27679
+ definitions[gateId] = parseGateDefinition(gatesObject[gateId], gateId, plan.workflowId);
27680
+ }
27681
+ return {
27682
+ registry: referenced.size > 0 && bundle.gateRegistry ? { schema: "kxm.gate-registry.v1", hash: gateRegistryHash(bundle.gateRegistry.value) } : bundle.gateRegistry && referenced.size === 0 ? { schema: "kxm.gate-registry.v1", hash: gateRegistryHash(bundle.gateRegistry.value) } : null,
27683
+ definitions,
27684
+ controlRoot: { repositoryId: "control", projectKey: projectRuntimeKey(projectRoot) }
27685
+ };
27686
+ }
27164
27687
 
27165
27688
  // plugins/kxm/src/hub-binding.ts
27166
- import { existsSync as existsSync15, mkdirSync as mkdirSync10, readFileSync as readFileSync13, renameSync as renameSync4, rmSync as rmSync4, writeFileSync as writeFileSync11 } from "node:fs";
27689
+ import { existsSync as existsSync16, mkdirSync as mkdirSync10, readFileSync as readFileSync14, renameSync as renameSync4, rmSync as rmSync4, writeFileSync as writeFileSync11 } from "node:fs";
27167
27690
  import { homedir as homedir4 } from "node:os";
27168
- import { dirname as dirname10, isAbsolute as isAbsolute6, join as join15, resolve as resolve11 } from "node:path";
27691
+ import { dirname as dirname10, isAbsolute as isAbsolute6, join as join16, resolve as resolve11 } from "node:path";
27169
27692
  var HUB_BINDING_SCHEMA = "kxm.hub-binding.v1";
27170
27693
  var HUB_HEALTH_PROBE_MS = 300;
27171
27694
  var HubBindingError = class extends Error {
@@ -27182,16 +27705,16 @@ function resolveUserStateRoot2(env) {
27182
27705
  }
27183
27706
  if (process.platform === "win32") {
27184
27707
  const localAppData = env.LOCALAPPDATA?.trim();
27185
- const base2 = localAppData && isAbsolute6(localAppData) ? localAppData : join15(homedir4(), "AppData", "Local");
27708
+ const base2 = localAppData && isAbsolute6(localAppData) ? localAppData : join16(homedir4(), "AppData", "Local");
27186
27709
  return resolve11(base2, "KXM");
27187
27710
  }
27188
27711
  if (process.platform === "darwin") return resolve11(homedir4(), "Library", "Application Support", "KXM");
27189
27712
  const xdgState = env.XDG_STATE_HOME?.trim();
27190
- const base = xdgState && isAbsolute6(xdgState) ? xdgState : join15(homedir4(), ".local", "state");
27713
+ const base = xdgState && isAbsolute6(xdgState) ? xdgState : join16(homedir4(), ".local", "state");
27191
27714
  return resolve11(base, "kxm");
27192
27715
  }
27193
27716
  function hubBindingFile(env = process.env) {
27194
- return join15(resolveUserStateRoot2(env), "hub-binding.json");
27717
+ return join16(resolveUserStateRoot2(env), "hub-binding.json");
27195
27718
  }
27196
27719
  function validateHubUrl(raw) {
27197
27720
  let parsed;
@@ -27227,10 +27750,10 @@ function isAbortError(error) {
27227
27750
  }
27228
27751
  function readHubBinding(env = process.env) {
27229
27752
  const file = hubBindingFile(env);
27230
- if (!existsSync15(file)) return void 0;
27753
+ if (!existsSync16(file)) return void 0;
27231
27754
  let parsed;
27232
27755
  try {
27233
- parsed = JSON.parse(readFileSync13(file, "utf8"));
27756
+ parsed = JSON.parse(readFileSync14(file, "utf8"));
27234
27757
  } catch {
27235
27758
  throw new HubBindingError(`malformed hub binding at ${file}`);
27236
27759
  }
@@ -27253,7 +27776,7 @@ function readHubBinding(env = process.env) {
27253
27776
  function writeHubBinding(record, env = process.env) {
27254
27777
  const file = hubBindingFile(env);
27255
27778
  mkdirSync10(dirname10(file), { recursive: true, mode: 448 });
27256
- const temporary = join15(dirname10(file), `.hub-binding-${process.pid}.tmp`);
27779
+ const temporary = join16(dirname10(file), `.hub-binding-${process.pid}.tmp`);
27257
27780
  try {
27258
27781
  writeFileSync11(temporary, `${JSON.stringify(record, null, 2)}
27259
27782
  `, { encoding: "utf8", mode: 384 });
@@ -27302,7 +27825,7 @@ var DEFAULT_LOG_MAX_BYTES = 10 * 1024 * 1024;
27302
27825
 
27303
27826
  // plugins/kxm/src/runtime-supervisor.ts
27304
27827
  function kxmSupervisorTokenFile(paths) {
27305
- return join16(paths.runtimeDir, "supervisor.token");
27828
+ return join17(paths.runtimeDir, "supervisor.token");
27306
27829
  }
27307
27830
  function readKxmSupervisorToken(paths) {
27308
27831
  const file = kxmSupervisorTokenFile(paths);
@@ -27311,7 +27834,7 @@ function readKxmSupervisorToken(paths) {
27311
27834
  if (stat.isSymbolicLink() || !stat.isFile()) {
27312
27835
  throw runtimeError("runtime_path_invalid", file, "supervisor token file must be a regular file, not a link");
27313
27836
  }
27314
- const token = readFileSync14(file, "utf8").trim();
27837
+ const token = readFileSync15(file, "utf8").trim();
27315
27838
  return token.length >= 32 ? token : void 0;
27316
27839
  }
27317
27840
  function processAlive(pid) {
@@ -27325,11 +27848,11 @@ function processAlive(pid) {
27325
27848
  var HEARTBEAT_STALE_MS = 15e3;
27326
27849
  var SUPERVISOR_ERROR_MAX_AGE_MS = 3e4;
27327
27850
  function supervisorErrorFile(paths) {
27328
- return join16(paths.runtimeDir, "supervisor.error");
27851
+ return join17(paths.runtimeDir, "supervisor.error");
27329
27852
  }
27330
27853
  function clearSupervisorError(paths) {
27331
27854
  const file = supervisorErrorFile(paths);
27332
- if (existsSync16(file)) rmSync5(file, { force: true });
27855
+ if (existsSync17(file)) rmSync5(file, { force: true });
27333
27856
  }
27334
27857
  function readRecentSupervisorError(paths) {
27335
27858
  const file = supervisorErrorFile(paths);
@@ -27338,7 +27861,7 @@ function readRecentSupervisorError(paths) {
27338
27861
  const ageMs = Date.now() - stat.mtimeMs;
27339
27862
  if (ageMs > SUPERVISOR_ERROR_MAX_AGE_MS) return void 0;
27340
27863
  try {
27341
- return readFileSync14(file, "utf8").trim();
27864
+ return readFileSync15(file, "utf8").trim();
27342
27865
  } catch {
27343
27866
  return void 0;
27344
27867
  }
@@ -27359,7 +27882,7 @@ function supervisorStatusOf(record) {
27359
27882
  };
27360
27883
  }
27361
27884
  function kxmSupervisorStatus(paths, options = {}) {
27362
- if (!existsSync16(paths.registryDb)) return { running: false };
27885
+ if (!existsSync17(paths.registryDb)) return { running: false };
27363
27886
  if (options.readOnly) {
27364
27887
  const database = openReadOnlyDatabase(paths.registryDb);
27365
27888
  try {
@@ -27425,7 +27948,7 @@ async function ensureKxmSupervisor(options = {}) {
27425
27948
  throw runtimeError("runtime_supervisor_unreachable", paths.registryDb, `runtime supervisor pid ${status.pid} is registered as running but cannot be probed`);
27426
27949
  }
27427
27950
  clearSupervisorError(paths);
27428
- const scriptPath = join16(findKxmRepoRoot(import.meta.url), "scripts", "kxm-runtime-supervisor.mjs");
27951
+ const scriptPath = join17(findKxmRepoRoot(import.meta.url), "scripts", "kxm-runtime-supervisor.mjs");
27429
27952
  const spawnImpl = options.spawnImpl ?? ((script, env) => {
27430
27953
  const child = spawn2(process.execPath, [script], {
27431
27954
  detached: true,
@@ -27489,11 +28012,11 @@ function handoffSuffix(handoff) {
27489
28012
 
27490
28013
  // plugins/kxm/src/cli/types.ts
27491
28014
  import { spawn as spawn3 } from "node:child_process";
27492
- import { join as join18, resolve as resolve12 } from "node:path";
28015
+ import { join as join19, resolve as resolve12 } from "node:path";
27493
28016
 
27494
28017
  // plugins/kxm/src/telemetry.ts
27495
- import { appendFileSync, mkdirSync as mkdirSync12, readFileSync as readFileSync15 } from "node:fs";
27496
- import { dirname as dirname12, join as join17 } from "node:path";
28018
+ import { appendFileSync, mkdirSync as mkdirSync12, readFileSync as readFileSync16 } from "node:fs";
28019
+ import { dirname as dirname12, join as join18 } from "node:path";
27497
28020
  var TELEMETRY_SCHEMA = "kxm.telemetry.v1";
27498
28021
  function inferImprovementTarget(input) {
27499
28022
  const explicit = input.env?.KXM_IMPROVE_TARGET?.trim();
@@ -27508,7 +28031,7 @@ function appendTelemetry(path4, event) {
27508
28031
  `, { encoding: "utf8", mode: 384 });
27509
28032
  }
27510
28033
  function telemetryPath(logsDir) {
27511
- return join17(logsDir, "telemetry.jsonl");
28034
+ return join18(logsDir, "telemetry.jsonl");
27512
28035
  }
27513
28036
  function makeTelemetryEvent(input) {
27514
28037
  return {
@@ -27523,7 +28046,7 @@ function makeTelemetryEvent(input) {
27523
28046
  function readRoutingRecords(path4) {
27524
28047
  const records = [];
27525
28048
  try {
27526
- const raw = readFileSync15(path4, "utf8");
28049
+ const raw = readFileSync16(path4, "utf8");
27527
28050
  for (const line of raw.split(/\r?\n/)) {
27528
28051
  if (!line.trim()) continue;
27529
28052
  try {
@@ -27602,7 +28125,7 @@ function workerResult(worker, payload) {
27602
28125
  // plugins/kxm/src/cli/types.ts
27603
28126
  function spawnScript(scriptName, extraEnv = {}) {
27604
28127
  return new Promise((resolveExit) => {
27605
- const child = spawn3(process.execPath, [join18(findKxmRepoRoot(import.meta.url), "scripts", scriptName)], {
28128
+ const child = spawn3(process.execPath, [join19(findKxmRepoRoot(import.meta.url), "scripts", scriptName)], {
27606
28129
  stdio: "inherit",
27607
28130
  env: { ...process.env, ...extraEnv }
27608
28131
  });
@@ -27703,10 +28226,10 @@ function workspaceDirs(cwd, workspaceFlag, env) {
27703
28226
  return {
27704
28227
  workdir,
27705
28228
  workspace,
27706
- config: derive ? join18(workspace, "config") : resolve12(workdir, env.KXM_CONFIG_DIR?.trim() || join18(workspace, "config")),
27707
- logs: derive ? join18(workspace, "logs") : resolve12(workdir, env.KXM_LOGS_DIR?.trim() || join18(workspace, "logs")),
27708
- assets: derive ? join18(workspace, "assets") : resolve12(workdir, env.KXM_ASSETS_DIR?.trim() || join18(workspace, "assets")),
27709
- state: derive ? join18(workspace, "state") : resolve12(workdir, env.KXM_STATE_DIR?.trim() || join18(workspace, "state"))
28229
+ config: derive ? join19(workspace, "config") : resolve12(workdir, env.KXM_CONFIG_DIR?.trim() || join19(workspace, "config")),
28230
+ logs: derive ? join19(workspace, "logs") : resolve12(workdir, env.KXM_LOGS_DIR?.trim() || join19(workspace, "logs")),
28231
+ assets: derive ? join19(workspace, "assets") : resolve12(workdir, env.KXM_ASSETS_DIR?.trim() || join19(workspace, "assets")),
28232
+ state: derive ? join19(workspace, "state") : resolve12(workdir, env.KXM_STATE_DIR?.trim() || join19(workspace, "state"))
27710
28233
  };
27711
28234
  }
27712
28235
  function maskEnvName(name) {
@@ -27774,20 +28297,20 @@ function workspaceEnv(runtime) {
27774
28297
  // plugins/kxm/src/cli/roles.ts
27775
28298
  var import_yaml10 = __toESM(require_dist(), 1);
27776
28299
  import { randomUUID as randomUUID5 } from "node:crypto";
27777
- import { existsSync as existsSync19, readFileSync as readFileSync18 } from "node:fs";
27778
- import { join as join21, resolve as resolve15 } from "node:path";
28300
+ import { existsSync as existsSync20, readFileSync as readFileSync19 } from "node:fs";
28301
+ import { join as join22, resolve as resolve15 } from "node:path";
27779
28302
  import { createInterface } from "node:readline";
27780
28303
 
27781
28304
  // plugins/kxm/src/role.ts
27782
28305
  var import_yaml9 = __toESM(require_dist(), 1);
27783
- import { existsSync as existsSync18, mkdirSync as mkdirSync14, readdirSync as readdirSync6, readFileSync as readFileSync17, rmSync as rmSync6, writeFileSync as writeFileSync14 } from "node:fs";
27784
- import { join as join20 } from "node:path";
28306
+ import { existsSync as existsSync19, mkdirSync as mkdirSync14, readdirSync as readdirSync6, readFileSync as readFileSync18, rmSync as rmSync6, writeFileSync as writeFileSync14 } from "node:fs";
28307
+ import { join as join21 } from "node:path";
27785
28308
 
27786
28309
  // plugins/kxm/src/config.ts
27787
28310
  var import_yaml8 = __toESM(require_dist(), 1);
27788
- import { existsSync as existsSync17, mkdirSync as mkdirSync13, readFileSync as readFileSync16, writeFileSync as writeFileSync13 } from "node:fs";
28311
+ import { existsSync as existsSync18, mkdirSync as mkdirSync13, readFileSync as readFileSync17, writeFileSync as writeFileSync13 } from "node:fs";
27789
28312
  import { homedir as homedir5 } from "node:os";
27790
- import { dirname as dirname13, join as join19, resolve as resolve13 } from "node:path";
28313
+ import { dirname as dirname13, join as join20, resolve as resolve13 } from "node:path";
27791
28314
  var KXM_CONFIG_SCHEMA = "kxm.config.v1";
27792
28315
  var IMPROVEMENT_PROMOTION_POLICIES = ["manual_pr", "critic_quorum", "auto_threshold"];
27793
28316
  var DEFAULT_KXM_CONFIG = {
@@ -27842,7 +28365,7 @@ var DEFAULT_KXM_CONFIG = {
27842
28365
  };
27843
28366
  function userConfigDirectory(overrideDir) {
27844
28367
  if (overrideDir) return resolve13(overrideDir);
27845
- return resolve13(process.env.KXM_USER_CONFIG_DIR?.trim() || join19(homedir5(), ".config", "kxm"));
28368
+ return resolve13(process.env.KXM_USER_CONFIG_DIR?.trim() || join20(homedir5(), ".config", "kxm"));
27846
28369
  }
27847
28370
  function repoConfigDirectory(repoRoot) {
27848
28371
  return resolve13(repoRoot, ".kxm");
@@ -27889,14 +28412,14 @@ function normalizeImprovementConfig(raw) {
27889
28412
  }
27890
28413
  function loadKxmConfig(repoRoot = process.cwd(), options = {}) {
27891
28414
  const userDir = userConfigDirectory(options.userConfigDir);
27892
- const userConfigFile = join19(userDir, "config.yaml");
28415
+ const userConfigFile = join20(userDir, "config.yaml");
27893
28416
  const repoDir = repoConfigDirectory(repoRoot);
27894
- const repoConfigFile = join19(repoDir, "config.yaml");
28417
+ const repoConfigFile = join20(repoDir, "config.yaml");
27895
28418
  let userRaw = {};
27896
28419
  let userLoadedPath;
27897
- if (existsSync17(userConfigFile)) {
28420
+ if (existsSync18(userConfigFile)) {
27898
28421
  try {
27899
- const text = readFileSync16(userConfigFile, "utf8");
28422
+ const text = readFileSync17(userConfigFile, "utf8");
27900
28423
  userRaw = (0, import_yaml8.parse)(text) ?? {};
27901
28424
  userLoadedPath = userConfigFile;
27902
28425
  } catch (error) {
@@ -27905,9 +28428,9 @@ function loadKxmConfig(repoRoot = process.cwd(), options = {}) {
27905
28428
  }
27906
28429
  let repoRaw = {};
27907
28430
  let repoLoadedPath;
27908
- if (existsSync17(repoConfigFile)) {
28431
+ if (existsSync18(repoConfigFile)) {
27909
28432
  try {
27910
- const text = readFileSync16(repoConfigFile, "utf8");
28433
+ const text = readFileSync17(repoConfigFile, "utf8");
27911
28434
  repoRaw = (0, import_yaml8.parse)(text) ?? {};
27912
28435
  repoLoadedPath = repoConfigFile;
27913
28436
  } catch (error) {
@@ -27943,7 +28466,7 @@ function getKxmConfigValue(config, keyPath) {
27943
28466
  return current;
27944
28467
  }
27945
28468
  function kxmConfigFileForScope(repoRoot, scope, userConfigDir) {
27946
- return scope === "user" ? join19(userConfigDirectory(userConfigDir), "config.yaml") : join19(repoConfigDirectory(repoRoot), "config.yaml");
28469
+ return scope === "user" ? join20(userConfigDirectory(userConfigDir), "config.yaml") : join20(repoConfigDirectory(repoRoot), "config.yaml");
27947
28470
  }
27948
28471
  function configKeyParts(keyPath) {
27949
28472
  const parts = keyPath.split(".");
@@ -27954,9 +28477,9 @@ function configKeyParts(keyPath) {
27954
28477
  }
27955
28478
  function readConfigFile(targetFile2) {
27956
28479
  let existing = {};
27957
- if (existsSync17(targetFile2)) {
28480
+ if (existsSync18(targetFile2)) {
27958
28481
  try {
27959
- existing = (0, import_yaml8.parse)(readFileSync16(targetFile2, "utf8")) ?? {};
28482
+ existing = (0, import_yaml8.parse)(readFileSync17(targetFile2, "utf8")) ?? {};
27960
28483
  } catch {
27961
28484
  existing = {};
27962
28485
  }
@@ -28074,21 +28597,21 @@ var DEFAULT_ROLES = {
28074
28597
  };
28075
28598
  function rolesDirectory(scope, repoRoot = process.cwd(), userConfigDir) {
28076
28599
  if (scope === "global") {
28077
- return join20(userConfigDirectory(userConfigDir), "roles");
28600
+ return join21(userConfigDirectory(userConfigDir), "roles");
28078
28601
  }
28079
- return join20(repoConfigDirectory(repoRoot), "roles");
28602
+ return join21(repoConfigDirectory(repoRoot), "roles");
28080
28603
  }
28081
28604
  function ensureRolesDirectory(scope, repoRoot = process.cwd(), userConfigDir) {
28082
28605
  const dir = rolesDirectory(scope, repoRoot, userConfigDir);
28083
- if (!existsSync18(dir)) {
28606
+ if (!existsSync19(dir)) {
28084
28607
  mkdirSync14(dir, { recursive: true });
28085
28608
  }
28086
28609
  return dir;
28087
28610
  }
28088
28611
  function parseRoleFile(filePath) {
28089
- if (!existsSync18(filePath)) return void 0;
28612
+ if (!existsSync19(filePath)) return void 0;
28090
28613
  try {
28091
- const raw = readFileSync17(filePath, "utf8");
28614
+ const raw = readFileSync18(filePath, "utf8");
28092
28615
  const parsed = (0, import_yaml9.parse)(raw);
28093
28616
  if (!parsed || typeof parsed !== "object" || parsed.schema !== KXM_ROLE_SCHEMA) {
28094
28617
  return void 0;
@@ -28115,10 +28638,10 @@ function listRoles(options = {}) {
28115
28638
  const globalDir = rolesDirectory("global", repoRoot, options.userConfigDir);
28116
28639
  const localDir = rolesDirectory("local", repoRoot, options.userConfigDir);
28117
28640
  const localRoles = /* @__PURE__ */ new Map();
28118
- if (scopeFilter !== "global" && existsSync18(localDir)) {
28641
+ if (scopeFilter !== "global" && existsSync19(localDir)) {
28119
28642
  for (const entry of readdirSync6(localDir)) {
28120
28643
  if (entry.endsWith(".yaml") || entry.endsWith(".yml")) {
28121
- const filePath = join20(localDir, entry);
28644
+ const filePath = join21(localDir, entry);
28122
28645
  const parsed = parseRoleFile(filePath);
28123
28646
  if (parsed) {
28124
28647
  localRoles.set(parsed.id, { role: parsed, filePath });
@@ -28127,10 +28650,10 @@ function listRoles(options = {}) {
28127
28650
  }
28128
28651
  }
28129
28652
  const globalRoles = /* @__PURE__ */ new Map();
28130
- if (scopeFilter !== "local" && existsSync18(globalDir)) {
28653
+ if (scopeFilter !== "local" && existsSync19(globalDir)) {
28131
28654
  for (const entry of readdirSync6(globalDir)) {
28132
28655
  if (entry.endsWith(".yaml") || entry.endsWith(".yml")) {
28133
- const filePath = join20(globalDir, entry);
28656
+ const filePath = join21(globalDir, entry);
28134
28657
  const parsed = parseRoleFile(filePath);
28135
28658
  if (parsed) {
28136
28659
  globalRoles.set(parsed.id, { role: parsed, filePath });
@@ -28178,13 +28701,13 @@ function getRole(roleId, options = {}) {
28178
28701
  const repoRoot = options.repoRoot ?? process.cwd();
28179
28702
  if (scope !== "global") {
28180
28703
  const localDir = rolesDirectory("local", repoRoot, options.userConfigDir);
28181
- const localFile = join20(localDir, `${roleId}.yaml`);
28704
+ const localFile = join21(localDir, `${roleId}.yaml`);
28182
28705
  const parsed = parseRoleFile(localFile);
28183
28706
  if (parsed) return { role: parsed, scope: "local", filePath: localFile };
28184
28707
  }
28185
28708
  if (scope !== "local") {
28186
28709
  const globalDir = rolesDirectory("global", repoRoot, options.userConfigDir);
28187
- const globalFile = join20(globalDir, `${roleId}.yaml`);
28710
+ const globalFile = join21(globalDir, `${roleId}.yaml`);
28188
28711
  const parsed = parseRoleFile(globalFile);
28189
28712
  if (parsed) return { role: parsed, scope: "global", filePath: globalFile };
28190
28713
  }
@@ -28194,8 +28717,8 @@ function addRole(role, options = {}) {
28194
28717
  const scope = options.scope ?? "local";
28195
28718
  const repoRoot = options.repoRoot ?? process.cwd();
28196
28719
  const dir = options.dryRun ? rolesDirectory(scope, repoRoot, options.userConfigDir) : ensureRolesDirectory(scope, repoRoot, options.userConfigDir);
28197
- const filePath = join20(dir, `${role.id}.yaml`);
28198
- if (existsSync18(filePath) && !options.overwrite) {
28720
+ const filePath = join21(dir, `${role.id}.yaml`);
28721
+ if (existsSync19(filePath) && !options.overwrite) {
28199
28722
  throw new Error(`role_already_exists: role '${role.id}' already exists at ${filePath}`);
28200
28723
  }
28201
28724
  if (!options.dryRun) writeFileSync14(filePath, (0, import_yaml9.stringify)(role), "utf8");
@@ -28205,8 +28728,8 @@ function removeRole(roleId, options = {}) {
28205
28728
  const scope = options.scope ?? "local";
28206
28729
  const repoRoot = options.repoRoot ?? process.cwd();
28207
28730
  const dir = rolesDirectory(scope, repoRoot, options.userConfigDir);
28208
- const filePath = join20(dir, `${roleId}.yaml`);
28209
- if (!existsSync18(filePath)) {
28731
+ const filePath = join21(dir, `${roleId}.yaml`);
28732
+ if (!existsSync19(filePath)) {
28210
28733
  throw new Error(`role_not_found: role '${roleId}' not found in ${scope} directory (${filePath})`);
28211
28734
  }
28212
28735
  if (options.dryRun) return { id: roleId, removed: false, filePath, scope };
@@ -28228,7 +28751,7 @@ function modifyRole(roleId, updates, options = {}) {
28228
28751
  const scope = options.scope ?? target.scope;
28229
28752
  const repoRoot = options.repoRoot ?? process.cwd();
28230
28753
  const dir = options.dryRun ? rolesDirectory(scope, repoRoot, options.userConfigDir) : ensureRolesDirectory(scope, repoRoot, options.userConfigDir);
28231
- const filePath = join20(dir, `${roleId}.yaml`);
28754
+ const filePath = join21(dir, `${roleId}.yaml`);
28232
28755
  if (!options.dryRun) writeFileSync14(filePath, (0, import_yaml9.stringify)(updated), "utf8");
28233
28756
  return { id: roleId, role: updated, filePath, scope };
28234
28757
  }
@@ -28277,18 +28800,18 @@ var DEFAULT_ROLE_SEATS = {
28277
28800
  };
28278
28801
  function findExistingRoleHostsFile(scope, repoRoot = process.cwd(), userConfigDir) {
28279
28802
  const dir = scope === "global" ? userConfigDirectory(userConfigDir) : repoConfigDirectory(repoRoot);
28280
- const yamlPath = join20(dir, "role-hosts.yaml");
28281
- const ymlPath = join20(dir, "role-hosts.yml");
28282
- const jsonPath = join20(dir, "role-hosts.json");
28283
- if (existsSync18(yamlPath)) return { filePath: yamlPath, exists: true, format: "yaml" };
28284
- if (existsSync18(ymlPath)) return { filePath: ymlPath, exists: true, format: "yaml" };
28285
- if (existsSync18(jsonPath)) return { filePath: jsonPath, exists: true, format: "json" };
28803
+ const yamlPath = join21(dir, "role-hosts.yaml");
28804
+ const ymlPath = join21(dir, "role-hosts.yml");
28805
+ const jsonPath = join21(dir, "role-hosts.json");
28806
+ if (existsSync19(yamlPath)) return { filePath: yamlPath, exists: true, format: "yaml" };
28807
+ if (existsSync19(ymlPath)) return { filePath: ymlPath, exists: true, format: "yaml" };
28808
+ if (existsSync19(jsonPath)) return { filePath: jsonPath, exists: true, format: "json" };
28286
28809
  return { filePath: yamlPath, exists: false, format: "yaml" };
28287
28810
  }
28288
28811
  function parseRoleHostsFile(filePath) {
28289
- if (!existsSync18(filePath)) return void 0;
28812
+ if (!existsSync19(filePath)) return void 0;
28290
28813
  try {
28291
- const raw = readFileSync17(filePath, "utf8");
28814
+ const raw = readFileSync18(filePath, "utf8");
28292
28815
  const parsed = (0, import_yaml9.parse)(raw);
28293
28816
  if (!parsed || typeof parsed !== "object") return void 0;
28294
28817
  if (parsed.schema && parsed.schema !== KXM_ROLE_HOSTS_SCHEMA) return void 0;
@@ -28361,9 +28884,9 @@ function saveRoleHostsConfig(config, options = {}) {
28361
28884
  const repoRoot = options.repoRoot ?? process.cwd();
28362
28885
  const dir = scope === "global" ? userConfigDirectory(options.userConfigDir) : repoConfigDirectory(repoRoot);
28363
28886
  const format = options.format ?? "yaml";
28364
- const filePath = join20(dir, format === "json" ? "role-hosts.json" : "role-hosts.yaml");
28887
+ const filePath = join21(dir, format === "json" ? "role-hosts.json" : "role-hosts.yaml");
28365
28888
  if (options.dryRun) return { filePath, scope };
28366
- if (!existsSync18(dir)) {
28889
+ if (!existsSync19(dir)) {
28367
28890
  mkdirSync14(dir, { recursive: true });
28368
28891
  }
28369
28892
  const payload = {
@@ -28613,7 +29136,7 @@ async function cmdRoleAdd(runtime, roleId, options) {
28613
29136
  let roleDef;
28614
29137
  if (options.file) {
28615
29138
  const filePath = resolve15(runtime.cwd, options.file);
28616
- const content = readFileSync18(filePath, "utf8");
29139
+ const content = readFileSync19(filePath, "utf8");
28617
29140
  roleDef = (0, import_yaml10.parse)(content);
28618
29141
  roleDef.id = roleId;
28619
29142
  } else {
@@ -28942,8 +29465,8 @@ async function cmdRoleResume(runtime, runId, ruling) {
28942
29465
  return 1;
28943
29466
  }
28944
29467
  }
28945
- const dbPath = join21(runtime.cwd, ".kxm", "state", "kxm.db");
28946
- if (existsSync19(dbPath)) {
29468
+ const dbPath = join22(runtime.cwd, ".kxm", "state", "kxm.db");
29469
+ if (existsSync20(dbPath)) {
28947
29470
  try {
28948
29471
  const database = runtime.dryRun ? openReadOnlyDatabase(dbPath) : new DatabaseSync(dbPath);
28949
29472
  try {
@@ -29015,8 +29538,8 @@ async function cmdRoleResume(runtime, runId, ruling) {
29015
29538
  // plugins/kxm/src/cli/workflows.ts
29016
29539
  var import_yaml12 = __toESM(require_dist(), 1);
29017
29540
  import { randomUUID as randomUUID7 } from "node:crypto";
29018
- import { existsSync as existsSync21, readFileSync as readFileSync20 } from "node:fs";
29019
- import { join as join23, resolve as resolve18 } from "node:path";
29541
+ import { existsSync as existsSync22, readFileSync as readFileSync21 } from "node:fs";
29542
+ import { join as join24, resolve as resolve17 } from "node:path";
29020
29543
 
29021
29544
  // plugins/kxm/src/retrospective.ts
29022
29545
  import { mkdirSync as mkdirSync15, renameSync as renameSync6, writeFileSync as writeFileSync15 } from "node:fs";
@@ -29344,7 +29867,7 @@ async function watchGithubChecks(input) {
29344
29867
  }
29345
29868
  const fetchImpl = input.fetchImpl ?? fetch;
29346
29869
  const now = input.now ?? Date.now;
29347
- const sleep = input.sleep ?? ((ms) => new Promise((resolve35) => setTimeout(resolve35, ms)));
29870
+ const sleep = input.sleep ?? ((ms) => new Promise((resolve34) => setTimeout(resolve34, ms)));
29348
29871
  const deadline = now() + input.timeoutMs;
29349
29872
  const headers = {
29350
29873
  authorization: `Bearer ${token}`,
@@ -29443,8 +29966,22 @@ async function watchGithubChecks(input) {
29443
29966
 
29444
29967
  // plugins/kxm/src/workflow-manager.ts
29445
29968
  var import_yaml11 = __toESM(require_dist(), 1);
29446
- import { existsSync as existsSync20, mkdirSync as mkdirSync16, readdirSync as readdirSync7, readFileSync as readFileSync19, rmSync as rmSync7, writeFileSync as writeFileSync16 } from "node:fs";
29447
- import { join as join22 } from "node:path";
29969
+ import { existsSync as existsSync21, mkdirSync as mkdirSync16, readdirSync as readdirSync7, readFileSync as readFileSync20, rmSync as rmSync7, writeFileSync as writeFileSync16 } from "node:fs";
29970
+ import { join as join23 } from "node:path";
29971
+ function assertWorkflowId(workflowId) {
29972
+ if (!kxmResourceIdentifier(workflowId)) {
29973
+ throw new Error(`workflow_id_invalid: '${workflowId}' must be a flat lowercase slug of at most 64 characters, starting with a letter and using letters, digits, single hyphens or underscores; paths and platform-reserved names are not allowed (use 'bug-fix', not 'software-engineering/bug-fix')`);
29974
+ }
29975
+ }
29976
+ var workflowSchemaRegistry;
29977
+ function workflowPayload(workflowId, content, filePath) {
29978
+ const payload = typeof content === "string" ? content : (0, import_yaml11.stringify)(content);
29979
+ const value = parseRestrictedYaml2(payload, filePath);
29980
+ const issues = (workflowSchemaRegistry ??= new KxmSchemaRegistry()).validate("workflow", value, filePath);
29981
+ if (issues.length > 0) throw new KxmConfigError(issues);
29982
+ compileKxmWorkflow({ id: workflowId, value, logicalPath: filePath });
29983
+ return payload;
29984
+ }
29448
29985
  var completed = () => ({ target: "$terminal", terminalStatus: "completed" });
29449
29986
  var failed = () => ({ target: "$terminal", terminalStatus: "failed" });
29450
29987
  function verifyStep(retryStep) {
@@ -29581,24 +30118,22 @@ function scaffoldWorkflowDefinition(description) {
29581
30118
  var DEFAULT_WORKFLOW_TEMPLATE = WORKFLOW_TEMPLATES["implement-and-verify"];
29582
30119
  function workflowsDirectory(scope, repoRoot = process.cwd(), userConfigDir) {
29583
30120
  if (scope === "global") {
29584
- return join22(userConfigDirectory(userConfigDir), "workflows");
30121
+ return join23(userConfigDirectory(userConfigDir), "workflows");
29585
30122
  }
29586
- return join22(repoConfigDirectory(repoRoot), "workflows");
30123
+ return join23(repoConfigDirectory(repoRoot), "workflows");
29587
30124
  }
29588
30125
  function ensureWorkflowsDirectory(scope, repoRoot = process.cwd(), userConfigDir) {
29589
30126
  const dir = workflowsDirectory(scope, repoRoot, userConfigDir);
29590
- if (!existsSync20(dir)) {
30127
+ if (!existsSync21(dir)) {
29591
30128
  mkdirSync16(dir, { recursive: true });
29592
30129
  }
29593
30130
  return dir;
29594
30131
  }
29595
30132
  function parseWorkflowFile(filePath) {
29596
- if (!existsSync20(filePath)) return void 0;
30133
+ if (!existsSync21(filePath)) return void 0;
29597
30134
  try {
29598
- const raw = readFileSync19(filePath, "utf8");
29599
- const parsed = (0, import_yaml11.parse)(raw);
29600
- if (!parsed || typeof parsed !== "object") return void 0;
29601
- return parsed;
30135
+ const raw = readFileSync20(filePath, "utf8");
30136
+ return parseRestrictedYaml2(raw, filePath);
29602
30137
  } catch {
29603
30138
  return void 0;
29604
30139
  }
@@ -29609,26 +30144,26 @@ function listWorkflowDefinitions(options = {}) {
29609
30144
  const globalDir = workflowsDirectory("global", repoRoot, options.userConfigDir);
29610
30145
  const localDir = workflowsDirectory("local", repoRoot, options.userConfigDir);
29611
30146
  const localDefs = /* @__PURE__ */ new Map();
29612
- if (scopeFilter !== "global" && existsSync20(localDir)) {
30147
+ if (scopeFilter !== "global" && existsSync21(localDir)) {
29613
30148
  for (const entry of readdirSync7(localDir)) {
29614
- if (entry.endsWith(".yaml") || entry.endsWith(".yml")) {
29615
- const filePath = join22(localDir, entry);
30149
+ const id = entry.endsWith(".yaml") ? entry.slice(0, -5) : entry.endsWith(".yml") ? entry.slice(0, -4) : void 0;
30150
+ if (id && kxmResourceIdentifier(id)) {
30151
+ const filePath = join23(localDir, entry);
29616
30152
  const def = parseWorkflowFile(filePath);
29617
30153
  if (def) {
29618
- const id = def.id || entry.replace(/\.ya?ml$/i, "");
29619
30154
  localDefs.set(id, { def, filePath });
29620
30155
  }
29621
30156
  }
29622
30157
  }
29623
30158
  }
29624
30159
  const globalDefs = /* @__PURE__ */ new Map();
29625
- if (scopeFilter !== "local" && existsSync20(globalDir)) {
30160
+ if (scopeFilter !== "local" && existsSync21(globalDir)) {
29626
30161
  for (const entry of readdirSync7(globalDir)) {
29627
- if (entry.endsWith(".yaml") || entry.endsWith(".yml")) {
29628
- const filePath = join22(globalDir, entry);
30162
+ const id = entry.endsWith(".yaml") ? entry.slice(0, -5) : entry.endsWith(".yml") ? entry.slice(0, -4) : void 0;
30163
+ if (id && kxmResourceIdentifier(id)) {
30164
+ const filePath = join23(globalDir, entry);
29629
30165
  const def = parseWorkflowFile(filePath);
29630
30166
  if (def) {
29631
- const id = def.id || entry.replace(/\.ya?ml$/i, "");
29632
30167
  globalDefs.set(id, { def, filePath });
29633
30168
  }
29634
30169
  }
@@ -29639,7 +30174,7 @@ function listWorkflowDefinitions(options = {}) {
29639
30174
  const steps = Array.isArray(def.steps) ? def.steps : [];
29640
30175
  for (const step of steps) {
29641
30176
  if (step && typeof step === "object") {
29642
- const role = step.role || step.agent;
30177
+ const role = step.agent;
29643
30178
  if (typeof role === "string" && !roles.includes(role)) {
29644
30179
  roles.push(role);
29645
30180
  }
@@ -29677,40 +30212,46 @@ function listWorkflowDefinitions(options = {}) {
29677
30212
  return result;
29678
30213
  }
29679
30214
  function getWorkflowDefinition(workflowId, options = {}) {
30215
+ assertWorkflowId(workflowId);
29680
30216
  const scope = options.scope ?? "all";
29681
30217
  const repoRoot = options.repoRoot ?? process.cwd();
29682
30218
  if (scope !== "global") {
29683
30219
  const localDir = workflowsDirectory("local", repoRoot, options.userConfigDir);
29684
- const localFile = join22(localDir, `${workflowId}.yaml`);
30220
+ const localFile = join23(localDir, `${workflowId}.yaml`);
29685
30221
  const parsed = parseWorkflowFile(localFile);
29686
30222
  if (parsed) return { workflow: parsed, scope: "local", filePath: localFile };
29687
30223
  }
29688
30224
  if (scope !== "local") {
29689
30225
  const globalDir = workflowsDirectory("global", repoRoot, options.userConfigDir);
29690
- const globalFile = join22(globalDir, `${workflowId}.yaml`);
30226
+ const globalFile = join23(globalDir, `${workflowId}.yaml`);
29691
30227
  const parsed = parseWorkflowFile(globalFile);
29692
30228
  if (parsed) return { workflow: parsed, scope: "global", filePath: globalFile };
29693
30229
  }
29694
30230
  return void 0;
29695
30231
  }
29696
30232
  function addWorkflowDefinition(workflowId, content = DEFAULT_WORKFLOW_TEMPLATE, options = {}) {
30233
+ assertWorkflowId(workflowId);
29697
30234
  const scope = options.scope ?? "local";
29698
30235
  const repoRoot = options.repoRoot ?? process.cwd();
29699
- const dir = options.dryRun ? workflowsDirectory(scope, repoRoot, options.userConfigDir) : ensureWorkflowsDirectory(scope, repoRoot, options.userConfigDir);
29700
- const filePath = join22(dir, `${workflowId}.yaml`);
29701
- if (existsSync20(filePath) && !options.overwrite) {
30236
+ const dir = workflowsDirectory(scope, repoRoot, options.userConfigDir);
30237
+ const filePath = join23(dir, `${workflowId}.yaml`);
30238
+ if (existsSync21(filePath) && !options.overwrite) {
29702
30239
  throw new Error(`workflow_already_exists: workflow '${workflowId}' already exists at ${filePath}`);
29703
30240
  }
29704
- const payload = typeof content === "string" ? content : (0, import_yaml11.stringify)(content);
29705
- if (!options.dryRun) writeFileSync16(filePath, payload, "utf8");
30241
+ const payload = workflowPayload(workflowId, content, filePath);
30242
+ if (!options.dryRun) {
30243
+ ensureWorkflowsDirectory(scope, repoRoot, options.userConfigDir);
30244
+ writeFileSync16(filePath, payload, "utf8");
30245
+ }
29706
30246
  return { id: workflowId, filePath, scope };
29707
30247
  }
29708
30248
  function removeWorkflowDefinition(workflowId, options = {}) {
30249
+ assertWorkflowId(workflowId);
29709
30250
  const scope = options.scope ?? "local";
29710
30251
  const repoRoot = options.repoRoot ?? process.cwd();
29711
30252
  const dir = workflowsDirectory(scope, repoRoot, options.userConfigDir);
29712
- const filePath = join22(dir, `${workflowId}.yaml`);
29713
- if (!existsSync20(filePath)) {
30253
+ const filePath = join23(dir, `${workflowId}.yaml`);
30254
+ if (!existsSync21(filePath)) {
29714
30255
  throw new Error(`workflow_not_found: workflow '${workflowId}' not found in ${scope} directory (${filePath})`);
29715
30256
  }
29716
30257
  if (options.dryRun) return { id: workflowId, removed: false, filePath, scope };
@@ -29728,16 +30269,20 @@ function modifyWorkflowDefinition(workflowId, updates, options = {}) {
29728
30269
  };
29729
30270
  const scope = options.scope ?? target.scope;
29730
30271
  const repoRoot = options.repoRoot ?? process.cwd();
29731
- const dir = options.dryRun ? workflowsDirectory(scope, repoRoot, options.userConfigDir) : ensureWorkflowsDirectory(scope, repoRoot, options.userConfigDir);
29732
- const filePath = join22(dir, `${workflowId}.yaml`);
29733
- if (!options.dryRun) writeFileSync16(filePath, (0, import_yaml11.stringify)(updated), "utf8");
30272
+ const dir = workflowsDirectory(scope, repoRoot, options.userConfigDir);
30273
+ const filePath = join23(dir, `${workflowId}.yaml`);
30274
+ const payload = workflowPayload(workflowId, updated, filePath);
30275
+ if (!options.dryRun) {
30276
+ ensureWorkflowsDirectory(scope, repoRoot, options.userConfigDir);
30277
+ writeFileSync16(filePath, payload, "utf8");
30278
+ }
29734
30279
  return { id: workflowId, workflow: updated, filePath, scope };
29735
30280
  }
29736
30281
 
29737
30282
  // plugins/kxm/src/cli/workflows.ts
29738
30283
  var CLI_NAME = "kxm";
29739
30284
  function localWorkflowSnapshot(dataPath, runId) {
29740
- if (!existsSync21(dataPath)) throw new Error("state_database_not_found");
30285
+ if (!existsSync22(dataPath)) throw new Error("state_database_not_found");
29741
30286
  const database = openReadOnlyDatabase(dataPath);
29742
30287
  try {
29743
30288
  const rows = runId ? database.prepare("SELECT record FROM workflow_runs WHERE id = ?").all(runId) : database.prepare("SELECT record FROM workflow_runs ORDER BY rowid DESC LIMIT 200").all();
@@ -29817,7 +30362,7 @@ function activeWorkflowDefinition(runtime, definitionId) {
29817
30362
  let raw;
29818
30363
  if (file) {
29819
30364
  try {
29820
- raw = readFileSync20(resolve18(runtime.cwd, file), "utf8");
30365
+ raw = readFileSync21(resolve17(runtime.cwd, file), "utf8");
29821
30366
  } catch {
29822
30367
  throw new Error("workflow definition file is unavailable");
29823
30368
  }
@@ -29865,60 +30410,56 @@ async function cmdWorkflowDefinitions(runtime, options) {
29865
30410
  }
29866
30411
  async function cmdWorkflowAdd(runtime, workflowId, options) {
29867
30412
  const scope = options.scope ?? "local";
29868
- const refuse = (error, text) => {
29869
- print(runtime.io, runtime.json, { ok: false, command: "workflow add", error }, `workflow add failed: ${text}`);
29870
- return 2;
30413
+ const refuse = (error, message, code = 2) => {
30414
+ print(runtime.io, runtime.json, { ok: false, command: "workflow add", error, message }, `workflow add failed: ${message}`);
30415
+ return code;
29871
30416
  };
29872
- let content;
29873
- if (options.template !== void 0) {
29874
- if (options.file !== void 0 || options.pick !== void 0) {
29875
- return refuse("workflow_add_conflict", "--template cannot be combined with --file or --pick");
29876
- }
29877
- if (!workflowId) return refuse("workflow_id_required", "usage: kxm workflow add <workflowId> --template <name>");
29878
- const template = Object.hasOwn(WORKFLOW_TEMPLATES, options.template) ? WORKFLOW_TEMPLATES[options.template] : void 0;
29879
- if (!template) {
29880
- return refuse("workflow_template_unknown", `unknown template ${options.template}; choose ${Object.keys(WORKFLOW_TEMPLATES).join(", ")}`);
29881
- }
29882
- content = { ...template, ...options.description ? { description: options.description } : {} };
29883
- } else if (!workflowId || options.pick) {
29884
- const candidates = Object.entries(WORKFLOW_TEMPLATES).map(([id, tmpl]) => ({
29885
- id,
29886
- description: String(tmpl.description ?? id),
29887
- label: "template",
29888
- payload: tmpl
29889
- }));
29890
- if (scope === "local") {
29891
- const globalDefs = listWorkflowDefinitions({ scope: "global", userConfigDir: runtime.env.KXM_USER_CONFIG_DIR });
29892
- for (const gd of globalDefs) {
29893
- const definition = parseWorkflowFile(gd.filePath);
29894
- if (definition && !candidates.some((c) => c.id === gd.id)) {
29895
- candidates.push({ id: gd.id, description: gd.description, label: "global", payload: definition });
30417
+ try {
30418
+ let content;
30419
+ if (options.template !== void 0) {
30420
+ if (options.file !== void 0 || options.pick !== void 0) {
30421
+ return refuse("workflow_add_conflict", "--template cannot be combined with --file or --pick");
30422
+ }
30423
+ if (!workflowId) return refuse("workflow_id_required", "usage: kxm workflow add <workflowId> --template <name>");
30424
+ const template = Object.hasOwn(WORKFLOW_TEMPLATES, options.template) ? WORKFLOW_TEMPLATES[options.template] : void 0;
30425
+ if (!template) {
30426
+ return refuse("workflow_template_unknown", `unknown template ${options.template}; choose ${Object.keys(WORKFLOW_TEMPLATES).join(", ")}`);
30427
+ }
30428
+ content = { ...template, ...options.description !== void 0 ? { description: options.description } : {} };
30429
+ } else if (!workflowId || options.pick) {
30430
+ const candidates = Object.entries(WORKFLOW_TEMPLATES).map(([id, tmpl]) => ({
30431
+ id,
30432
+ description: String(tmpl.description ?? id),
30433
+ label: "template",
30434
+ payload: tmpl
30435
+ }));
30436
+ if (scope === "local") {
30437
+ const globalDefs = listWorkflowDefinitions({ scope: "global", userConfigDir: runtime.env.KXM_USER_CONFIG_DIR });
30438
+ for (const gd of globalDefs) {
30439
+ if (!candidates.some((c) => c.id === gd.id)) {
30440
+ candidates.push({ id: gd.id, description: gd.description, label: "global", payload: gd.filePath });
30441
+ }
29896
30442
  }
29897
30443
  }
29898
- }
29899
- const picked = await resolvePickItem(runtime.io, `Select a workflow template to add (${scope})`, candidates, options.pick, runtime.env);
29900
- if (!picked) {
29901
- if (!workflowId) {
29902
- runtime.io.stderr("workflow add failed: missing workflowId or pick selection\n");
29903
- return 1;
30444
+ const picked = await resolvePickItem(runtime.io, `Select a workflow template to add (${scope})`, candidates, options.pick, runtime.env);
30445
+ if (!picked) {
30446
+ return refuse("workflow_id_required", "provide a workflowId or select an available workflow with --pick <id>", 1);
29904
30447
  }
29905
- } else {
29906
- workflowId = picked.id;
29907
- if (!options.file && picked.payload) {
29908
- content = {
29909
- ...picked.payload,
29910
- ...options.description ? { description: options.description } : {}
29911
- };
30448
+ workflowId ??= picked.id;
30449
+ if (options.file === void 0) {
30450
+ const pickedContent = typeof picked.payload === "string" ? readFileSync21(picked.payload, "utf8") : picked.payload;
30451
+ content = options.description !== void 0 ? {
30452
+ ...typeof pickedContent === "string" ? parseRestrictedYaml2(pickedContent, String(picked.payload)) : pickedContent,
30453
+ description: options.description
30454
+ } : pickedContent;
29912
30455
  }
29913
30456
  }
29914
- }
29915
- if (options.file) {
29916
- const filePath = resolve18(runtime.cwd, options.file);
29917
- content = readFileSync20(filePath, "utf8");
29918
- } else if (!content) {
29919
- content = scaffoldWorkflowDefinition(options.description || `Workflow ${workflowId}`);
29920
- }
29921
- try {
30457
+ if (options.file !== void 0) {
30458
+ const filePath = resolve17(runtime.cwd, options.file);
30459
+ content = readFileSync21(filePath, "utf8");
30460
+ } else if (!content) {
30461
+ content = scaffoldWorkflowDefinition(options.description || `Workflow ${workflowId}`);
30462
+ }
29922
30463
  const document = typeof content === "string" ? content : (0, import_yaml12.stringify)(content);
29923
30464
  let repoRoot = runtime.cwd;
29924
30465
  if (scope === "local") {
@@ -29931,7 +30472,7 @@ async function cmdWorkflowAdd(runtime, workflowId, options) {
29931
30472
  print(
29932
30473
  runtime.io,
29933
30474
  runtime.json,
29934
- { ok: false, command: "workflow add", error: "workflow_invalid", issues },
30475
+ { ok: false, command: "workflow add", error: "workflow_invalid", message: "with this workflow the project would not load, so nothing was written", issues },
29935
30476
  `workflow add failed: with this workflow the project would not load, so nothing was written
29936
30477
  ${issues.map((entry) => ` ${entry.file}: ${entry.code}: ${entry.message}`).join("\n")}`
29937
30478
  );
@@ -29959,9 +30500,7 @@ ${issues.map((entry) => ` ${entry.file}: ${entry.code}: ${entry.message}`).join
29959
30500
  );
29960
30501
  return 0;
29961
30502
  } catch (err) {
29962
- runtime.io.stderr(`workflow add failed: ${err.message}
29963
- `);
29964
- return 1;
30503
+ return refuse("workflow_add_failed", err instanceof Error ? err.message : String(err), 1);
29965
30504
  }
29966
30505
  }
29967
30506
  async function cmdWorkflowRemove(runtime, workflowId, options) {
@@ -30093,7 +30632,7 @@ async function cmdWorkflowStart(runtime, definitionIdArg, options) {
30093
30632
  }
30094
30633
  let payload;
30095
30634
  try {
30096
- const raw = payloadFlag.startsWith("@") ? readFileSync20(resolve18(runtime.cwd, payloadFlag.slice(1)), "utf8") : payloadFlag;
30635
+ const raw = payloadFlag.startsWith("@") ? readFileSync21(resolve17(runtime.cwd, payloadFlag.slice(1)), "utf8") : payloadFlag;
30097
30636
  const value = JSON.parse(raw);
30098
30637
  if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("object required");
30099
30638
  payload = value;
@@ -30160,7 +30699,7 @@ async function cmdWorkflowDegrade(runtime, runId, stageId, options) {
30160
30699
  }
30161
30700
  }
30162
30701
  async function cmdWorkflowInspect(runtime, action, runId) {
30163
- const dataPath = resolve18(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join23(runtime.dirs.state, "kxm.db"));
30702
+ const dataPath = resolve17(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join24(runtime.dirs.state, "kxm.db"));
30164
30703
  if (action === "get" && !runId) {
30165
30704
  runtime.io.stderr(`Usage: ${CLI_NAME} workflow get <runId>
30166
30705
  `);
@@ -30329,14 +30868,14 @@ async function cmdRetrospectiveExport(runtime, runId, options) {
30329
30868
  return 2;
30330
30869
  }
30331
30870
  const snapshotFlag = String(options.input || "");
30332
- const snapshotPath = snapshotFlag ? resolve18(runtime.cwd, snapshotFlag) : "";
30871
+ const snapshotPath = snapshotFlag ? resolve17(runtime.cwd, snapshotFlag) : "";
30333
30872
  let snapshot;
30334
30873
  try {
30335
30874
  if (snapshotPath) {
30336
- if (!existsSync21(snapshotPath)) throw new Error("snapshot_missing");
30337
- snapshot = JSON.parse(readFileSync20(snapshotPath, "utf8"));
30875
+ if (!existsSync22(snapshotPath)) throw new Error("snapshot_missing");
30876
+ snapshot = JSON.parse(readFileSync21(snapshotPath, "utf8"));
30338
30877
  } else {
30339
- const dataPath = resolve18(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join23(runtime.dirs.state, "kxm.db"));
30878
+ const dataPath = resolve17(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join24(runtime.dirs.state, "kxm.db"));
30340
30879
  const local = localWorkflowSnapshot(dataPath, runId);
30341
30880
  if (!local.runs[0]) throw new Error("workflow_not_found");
30342
30881
  snapshot = { run: local.runs[0], journal: local.journal };
@@ -30348,8 +30887,8 @@ async function cmdRetrospectiveExport(runtime, runId, options) {
30348
30887
  return 1;
30349
30888
  }
30350
30889
  const doc = buildRetrospective(snapshot.run, snapshot.journal);
30351
- const outDir = resolve18(runtime.cwd, String(options.outDir || join23(runtime.dirs.assets, "retrospectives")));
30352
- const assetsRoot = resolve18(runtime.dirs.assets);
30890
+ const outDir = resolve17(runtime.cwd, String(options.outDir || join24(runtime.dirs.assets, "retrospectives")));
30891
+ const assetsRoot = resolve17(runtime.dirs.assets);
30353
30892
  const assetsPrefix = `${assetsRoot}${process.platform === "win32" ? "\\" : "/"}`;
30354
30893
  if (outDir !== assetsRoot && !outDir.startsWith(assetsPrefix)) {
30355
30894
  print(runtime.io, runtime.json, { ok: false, command: "retrospective export", error: "output_outside_workspace_assets" }, "retrospectives must stay under the workspace assets directory");
@@ -30366,133 +30905,174 @@ async function cmdRetrospectiveExport(runtime, runId, options) {
30366
30905
 
30367
30906
  // plugins/kxm/src/cli/tasks.ts
30368
30907
  var import_yaml15 = __toESM(require_dist(), 1);
30369
- import { existsSync as existsSync27, readFileSync as readFileSync26, readdirSync as readdirSync10 } from "node:fs";
30370
- import { basename as basename7, join as join31, resolve as resolve24 } from "node:path";
30908
+ import { existsSync as existsSync28, readFileSync as readFileSync27, readdirSync as readdirSync10 } from "node:fs";
30909
+ import { basename as basename7, join as join32, resolve as resolve23 } from "node:path";
30371
30910
 
30372
30911
  // plugins/kxm/src/suggest.ts
30373
30912
  var WORKFLOW_PATTERNS = [
30374
30913
  {
30375
- id: "software-engineering/bug-fix",
30914
+ id: "bug-fix",
30915
+ template: "implement-and-verify",
30376
30916
  area: "software-engineering",
30377
30917
  keywords: ["fix", "bug", "flaky", "failure", "timeout", "error", "repro", "crash", "broken", "hang"],
30378
- skills: ["kxm-workflow", "kxm-runs", "kxm-context-memory"],
30379
- defaultCommand: (p) => `kxm run software-engineering/bug-fix "${p}"`
30918
+ skills: ["kxm-workflow", "kxm-runs", "kxm-context-memory"]
30380
30919
  },
30381
30920
  {
30382
- id: "software-engineering/feature-implementation",
30921
+ id: "feature-implementation",
30922
+ template: "implement-and-verify",
30383
30923
  area: "software-engineering",
30384
30924
  keywords: ["feature", "implement", "add", "build", "create", "develop", "support", "endpoint", "ui", "tui"],
30385
- skills: ["kxm-workflow", "kxm-peer", "kxm-context-memory"],
30386
- defaultCommand: (p) => `kxm run software-engineering/feature-implementation "${p}"`
30925
+ skills: ["kxm-workflow", "kxm-peer", "kxm-context-memory"]
30387
30926
  },
30388
30927
  {
30389
- id: "software-engineering/refactoring",
30928
+ id: "refactoring",
30929
+ template: "dual-critic-review",
30390
30930
  area: "software-engineering",
30391
30931
  keywords: ["refactor", "cleanup", "reorganize", "modularize", "deduplicate", "split", "simplify", "deprecate"],
30392
- skills: ["kxm-workflow", "kxm-runs"],
30393
- defaultCommand: (p) => `kxm run software-engineering/refactoring "${p}"`
30932
+ skills: ["kxm-workflow", "kxm-runs"]
30394
30933
  },
30395
30934
  {
30396
- id: "security-reliability/vulnerability-remediation",
30935
+ id: "vulnerability-remediation",
30936
+ template: "dual-critic-review",
30397
30937
  area: "security-reliability",
30398
30938
  keywords: ["cve", "vulnerability", "security", "exploit", "sanitize", "leak", "secret", "injection", "redact", "auth"],
30399
- skills: ["kxm-workflow", "kxm-definitions"],
30400
- defaultCommand: (p) => `kxm run security-reliability/vulnerability-remediation "${p}"`
30939
+ skills: ["kxm-workflow", "kxm-definitions"]
30401
30940
  },
30402
30941
  {
30403
- id: "security-reliability/reliability-hardening",
30942
+ id: "reliability-hardening",
30943
+ template: "dual-critic-review",
30404
30944
  area: "security-reliability",
30405
30945
  keywords: ["idempotency", "retry", "circuit-breaker", "cas", "lock", "concurrency", "deadlock", "race", "crash-recovery"],
30406
- skills: ["kxm-workflow", "kxm-peer"],
30407
- defaultCommand: (p) => `kxm run security-reliability/reliability-hardening "${p}"`
30946
+ skills: ["kxm-workflow", "kxm-peer"]
30408
30947
  },
30409
30948
  {
30410
- id: "data-analytics/pipeline-migration",
30949
+ id: "pipeline-migration",
30950
+ template: "implement-and-verify",
30411
30951
  area: "data-analytics",
30412
30952
  keywords: ["database", "sqlite", "migration", "pipeline", "schema", "transform", "table", "wal", "foreign", "cascading"],
30413
- skills: ["kxm-runs", "kxm-context-memory"],
30414
- defaultCommand: (p) => `kxm run data-analytics/pipeline-migration "${p}"`
30953
+ skills: ["kxm-runs", "kxm-context-memory"]
30415
30954
  },
30416
30955
  {
30417
- id: "research-strategy/architecture-spike",
30956
+ id: "architecture-spike",
30957
+ template: "spec-and-plan",
30418
30958
  area: "research-strategy",
30419
30959
  keywords: ["spike", "investigate", "prototype", "research", "feasibility", "benchmark", "explore", "evaluate"],
30420
- skills: ["kxm-session", "kxm-context-memory", "kxm-routing-improve"],
30421
- defaultCommand: (p) => `kxm run research-strategy/architecture-spike "${p}"`
30960
+ skills: ["kxm-session", "kxm-context-memory", "kxm-routing-improve"]
30422
30961
  }
30423
30962
  ];
30424
30963
  function suggestWorkflowAndRoles(prompt, options = {}) {
30425
30964
  const normalized = prompt.toLowerCase();
30426
30965
  const words = normalized.split(/\W+/).filter(Boolean);
30427
- let bestPattern = WORKFLOW_PATTERNS[0];
30428
- let bestScore = -1;
30429
- const matchedReasons = [];
30966
+ const claudeOnly = /\bclaude(?:\s+code)?(?:[\s-]+only|\s+exclusively)\b|\b(?:only|exclusively)(?:\s+(?:use|using|with|via))?\s+claude(?:\s+code)?\b/i.test(prompt);
30967
+ let bestPattern = WORKFLOW_PATTERNS[1];
30968
+ let bestScore = 0;
30969
+ const reasons = [];
30430
30970
  for (const pattern of WORKFLOW_PATTERNS) {
30431
- let score = 0;
30432
- const matchedWords = [];
30433
- for (const keyword of pattern.keywords) {
30434
- if (words.includes(keyword) || normalized.includes(keyword)) {
30435
- score += 1;
30436
- matchedWords.push(keyword);
30437
- }
30438
- }
30439
- if (score > bestScore) {
30440
- bestScore = score;
30971
+ const matchedWords = pattern.keywords.filter((keyword) => words.includes(keyword) || normalized.includes(keyword));
30972
+ if (matchedWords.length > bestScore) {
30973
+ bestScore = matchedWords.length;
30441
30974
  bestPattern = pattern;
30442
- matchedReasons.length = 0;
30443
- matchedReasons.push(`Matched keywords: ${matchedWords.join(", ")}`);
30975
+ reasons.length = 0;
30976
+ reasons.push(`Matched keywords: ${matchedWords.join(", ")}`);
30444
30977
  }
30445
30978
  }
30446
- if (bestScore <= 0) {
30447
- bestPattern = WORKFLOW_PATTERNS[1];
30448
- matchedReasons.push("Default fallback: general feature implementation workflow");
30449
- }
30450
- const activeHarnesses = new Set(
30451
- (options.availableHarnesses ?? []).filter((h) => h.auth === "active" || h.auth === "ready" || h.auth === "configured").map((h) => h.harness.toLowerCase())
30452
- );
30453
- const hasClaude = activeHarnesses.size === 0 || activeHarnesses.has("claude");
30454
- const hasGrok = activeHarnesses.size === 0 || activeHarnesses.has("grok");
30455
- const hasCodex = activeHarnesses.size === 0 || activeHarnesses.has("codex");
30456
- const planner = hasClaude ? { harness: "claude", model: "fable", role: "planner" } : { harness: "codex", model: "gpt-5.6-sol", role: "planner" };
30457
- const writer = hasGrok ? { harness: "grok", model: "grok-4.6", role: "writer" } : activeHarnesses.has("agy") ? { harness: "agy", model: "gemini-2.5-pro", role: "writer" } : { harness: "pi", model: "qwen/qwen3-coder-plus", role: "writer" };
30458
- const critics = [
30459
- { harness: "claude", model: "fable", role: "reviewer-arch" },
30460
- { harness: "codex", model: "gpt-5.6-sol", role: "reviewer-cli" }
30461
- ];
30462
- return {
30979
+ if (bestScore === 0) reasons.push("Default fallback: general feature implementation workflow");
30980
+ if (claudeOnly) reasons.push("Explicit Claude-only constraint; other harnesses are excluded");
30981
+ const template = WORKFLOW_TEMPLATES[bestPattern.template];
30982
+ const steps = template.steps;
30983
+ const base = {
30463
30984
  workflowId: bestPattern.id,
30985
+ template: bestPattern.template,
30464
30986
  area: bestPattern.area,
30465
30987
  confidence: bestScore > 0 ? Math.min(1, 0.5 + bestScore * 0.15) : 0.5,
30466
- reasons: matchedReasons,
30988
+ reasons,
30467
30989
  suggestedSkills: bestPattern.skills,
30468
- roles: {
30469
- planner,
30470
- writer,
30471
- critics,
30472
- verifier: { kind: "witness", command: "npm run verify" }
30473
- },
30474
- suggestedCommand: bestPattern.defaultCommand(prompt.replace(/"/g, '\\"'))
30990
+ suggestedCommand: `kxm workflow add ${bestPattern.id} --template ${bestPattern.template}`
30991
+ };
30992
+ const writeStep = steps.find((step) => step.kind === "agent" && Object.values(step.repositories ?? {}).includes("write"));
30993
+ const available = (options.availableHarnesses ?? []).filter((entry) => BUILTIN_HARNESS_IDS.includes(entry.id) && entry.detected && entry.authenticated === true && entry.dispatch?.supported === true && entry.dispatch.status === "yes" && (!claudeOnly || entry.id === "claude"));
30994
+ const selected = available.find((entry) => entry.id === "claude") ?? available[0];
30995
+ if (!selected) {
30996
+ const relevant = (options.availableHarnesses ?? []).filter((entry) => !claudeOnly || entry.id === "claude");
30997
+ const detail = relevant.map((entry) => `${entry.id}: ${entry.dispatch?.reason ?? (!entry.detected ? "not_detected" : entry.authenticated !== true ? "authentication_unverified" : "dispatch_unavailable")}`).join("; ");
30998
+ return {
30999
+ ...base,
31000
+ roles: [],
31001
+ execution: {
31002
+ supported: false,
31003
+ error: "harness_unavailable",
31004
+ reason: `${claudeOnly ? "Claude-only execution requires Claude Code" : "Execution requires a supported harness"} detected, authenticated, and ready for dispatch. ${detail || "No verified harness inventory is available."}`,
31005
+ nextSteps: claudeOnly ? ["Install Claude Code if missing, authenticate with claude auth login, then confirm claude auth status and kxm harness list. Other harnesses will not be substituted."] : ["Install and authenticate a supported harness, then run kxm harness list to check detection, authentication, and dispatch readiness. Pi requires a configured model/provider auth context."]
31006
+ }
31007
+ };
31008
+ }
31009
+ if (writeStep && !oneShotWriterArgs(selected.id)) {
31010
+ return {
31011
+ ...base,
31012
+ roles: [],
31013
+ execution: {
31014
+ supported: false,
31015
+ error: "live_write_unsupported",
31016
+ reason: `${bestPattern.template} requires repository writes at step ${writeStep.id}, but the selected ${selected.id} harness has no audited live writer profile. No other harness will be substituted.`,
31017
+ nextSteps: [
31018
+ `Run the implementation directly in ${claudeOnly ? "Claude Code" : selected.id} in the target worktree, and run the repository's actual verification command.`,
31019
+ `For read-only KXM planning, try kxm suggest "Investigate an architecture spike${claudeOnly ? " using Claude only" : ""}"; the shipped spec-and-plan template does not implement changes.`,
31020
+ "Installing a definition or creating a run does not execute it. Simulation is not evidence of a bug fix or workflow completion."
31021
+ ]
31022
+ }
31023
+ };
31024
+ }
31025
+ const roles = [];
31026
+ for (const step of steps) {
31027
+ if (!step.agent) continue;
31028
+ const role = roles.find((entry) => entry.agent === step.agent);
31029
+ if (role) role.role += `, ${step.id}`;
31030
+ else roles.push({ agent: step.agent, harness: selected.id, role: step.id });
31031
+ }
31032
+ const shell = process.platform === "win32" ? "powershell" : "posix";
31033
+ const quotedPrompt = shell === "powershell" ? `'${prompt.replace(/['\u2018-\u201b]/g, "$&$&")}'` : `'${prompt.replace(/'/g, "'\\''")}'`;
31034
+ return {
31035
+ ...base,
31036
+ roles,
31037
+ execution: {
31038
+ supported: true,
31039
+ prerequisites: [
31040
+ "Run kxm init in the target repository if needed, then install the exact template with the suggested command. If that workflow ID already exists, stop and review it; the commands below apply only to a newly installed template, not an existing definition with potentially different agents or permissions.",
31041
+ ...roles.map((role) => `Set harness: ${role.harness} and model: <your authenticated compatible model selector> in .kxm/agents/${role.agent}.yaml. Admit that exact selector in .kxm/routes.yaml and ensure it is not disabled; this recommendation does not choose a model or change routing.`),
31042
+ ...writeStep ? [
31043
+ `Keep each writer step at assignments.maximum: 1 and set limits.maxConcurrentRuns: 1 in .kxm/project.yaml; the live ${selected.id} writer profile requires a lone writer in the checkout.`,
31044
+ `If .kxm/roster.yaml exists, it must be readable kxm.developer-roster.v1 with a lineup.writer route matching harness: ${selected.id} and the selected writer model (model or vendor/model), status: admitted, and permissions containing edit.`,
31045
+ "Configure the test gate in .kxm/gates.yaml with kind: command and argv for this repository's actual verification command. A scaffold/example command or simulation is not evidence that the implementation works."
31046
+ ] : [],
31047
+ `Validate the installed workflow with kxm gate validate --file .kxm/workflows/${bestPattern.id}.yaml and the project configuration with kxm init --dry-run before creating a run.`
31048
+ ],
31049
+ shell,
31050
+ createCommand: `kxm run ${bestPattern.id} -- ${quotedPrompt}`,
31051
+ driveCommand: "kxm runs drive <runId> --wait",
31052
+ statusCommand: "kxm runs status <runId>",
31053
+ receiptCommand: "kxm runs receipt <runId>"
31054
+ }
30475
31055
  };
30476
31056
  }
30477
31057
 
30478
31058
  // plugins/kxm/src/task-manager.ts
30479
31059
  var import_yaml13 = __toESM(require_dist(), 1);
30480
31060
  import { randomUUID as randomUUID8 } from "node:crypto";
30481
- import { existsSync as existsSync22, mkdirSync as mkdirSync17, readFileSync as readFileSync21, readdirSync as readdirSync8, writeFileSync as writeFileSync17 } from "node:fs";
30482
- import { join as join24, resolve as resolve19 } from "node:path";
31061
+ import { existsSync as existsSync23, mkdirSync as mkdirSync17, readFileSync as readFileSync22, readdirSync as readdirSync8, writeFileSync as writeFileSync17 } from "node:fs";
31062
+ import { join as join25, resolve as resolve18 } from "node:path";
30483
31063
  var GOAL_SCHEMA = "kxm.goal.v1";
30484
31064
  var TASK_SCHEMA = "kxm.task.v1";
30485
31065
  function goalsDirectory(repoRoot) {
30486
- return resolve19(repoRoot, ".kxm", "goals");
31066
+ return resolve18(repoRoot, ".kxm", "goals");
30487
31067
  }
30488
31068
  function tasksDirectory(repoRoot) {
30489
- return resolve19(repoRoot, ".kxm", "tasks");
31069
+ return resolve18(repoRoot, ".kxm", "tasks");
30490
31070
  }
30491
31071
  function goalFilePath(repoRoot, goalId) {
30492
- return join24(goalsDirectory(repoRoot), `${goalId}.yaml`);
31072
+ return join25(goalsDirectory(repoRoot), `${goalId}.yaml`);
30493
31073
  }
30494
31074
  function taskFilePath(repoRoot, taskId) {
30495
- return join24(tasksDirectory(repoRoot), `${taskId}.yaml`);
31075
+ return join25(tasksDirectory(repoRoot), `${taskId}.yaml`);
30496
31076
  }
30497
31077
  function createGoal(repoRoot, input, options = {}) {
30498
31078
  const id = `goal_${randomUUID8().replaceAll("-", "").slice(0, 12)}`;
@@ -30515,13 +31095,13 @@ function createGoal(repoRoot, input, options = {}) {
30515
31095
  }
30516
31096
  function listGoals(repoRoot) {
30517
31097
  const dir = goalsDirectory(repoRoot);
30518
- if (!existsSync22(dir)) return [];
31098
+ if (!existsSync23(dir)) return [];
30519
31099
  const goals = [];
30520
31100
  const entries = readdirSync8(dir, { withFileTypes: true });
30521
31101
  for (const entry of entries) {
30522
31102
  if (entry.isFile() && (entry.name.endsWith(".yaml") || entry.name.endsWith(".yml"))) {
30523
31103
  try {
30524
- const text = readFileSync21(join24(dir, entry.name), "utf8");
31104
+ const text = readFileSync22(join25(dir, entry.name), "utf8");
30525
31105
  const doc = (0, import_yaml13.parse)(text);
30526
31106
  if (doc && doc.schema === GOAL_SCHEMA) goals.push(doc);
30527
31107
  } catch {
@@ -30559,13 +31139,13 @@ function createTask(repoRoot, input, options = {}) {
30559
31139
  }
30560
31140
  function listTasks(repoRoot, options = {}) {
30561
31141
  const dir = tasksDirectory(repoRoot);
30562
- if (!existsSync22(dir)) return [];
31142
+ if (!existsSync23(dir)) return [];
30563
31143
  const tasks = [];
30564
31144
  const entries = readdirSync8(dir, { withFileTypes: true });
30565
31145
  for (const entry of entries) {
30566
31146
  if (entry.isFile() && (entry.name.endsWith(".yaml") || entry.name.endsWith(".yml"))) {
30567
31147
  try {
30568
- const text = readFileSync21(join24(dir, entry.name), "utf8");
31148
+ const text = readFileSync22(join25(dir, entry.name), "utf8");
30569
31149
  const doc = (0, import_yaml13.parse)(text);
30570
31150
  if (doc && doc.schema === TASK_SCHEMA) {
30571
31151
  if (options.goalId && doc.goalId !== options.goalId) continue;
@@ -30580,27 +31160,16 @@ function listTasks(repoRoot, options = {}) {
30580
31160
  }
30581
31161
  function getTask(repoRoot, taskId) {
30582
31162
  const dir = tasksDirectory(repoRoot);
30583
- const filePath = join24(dir, `${taskId}.yaml`);
30584
- if (!existsSync22(filePath)) return void 0;
31163
+ const filePath = join25(dir, `${taskId}.yaml`);
31164
+ if (!existsSync23(filePath)) return void 0;
30585
31165
  try {
30586
- const text = readFileSync21(filePath, "utf8");
31166
+ const text = readFileSync22(filePath, "utf8");
30587
31167
  const doc = (0, import_yaml13.parse)(text);
30588
31168
  return doc && doc.schema === TASK_SCHEMA ? doc : void 0;
30589
31169
  } catch {
30590
31170
  return void 0;
30591
31171
  }
30592
31172
  }
30593
- function updateTaskStatus(repoRoot, taskId, status, options = {}) {
30594
- const task = getTask(repoRoot, taskId);
30595
- if (!task) {
30596
- throw new Error(`Task ${taskId} not found`);
30597
- }
30598
- task.status = status;
30599
- if (options.workflowRunId) task.workflowRunId = options.workflowRunId;
30600
- task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
30601
- if (!options.dryRun) writeFileSync17(taskFilePath(repoRoot, taskId), (0, import_yaml13.stringify)(task).trim() + "\n", "utf8");
30602
- return task;
30603
- }
30604
31173
  function syncTaskWithTracker(repoRoot, taskId, options = {}) {
30605
31174
  const task = getTask(repoRoot, taskId);
30606
31175
  if (!task) {
@@ -31380,13 +31949,13 @@ function createStudioServer(options = {}) {
31380
31949
  }
31381
31950
 
31382
31951
  // plugins/kxm/src/cli/project.ts
31383
- import { existsSync as existsSync26 } from "node:fs";
31384
- import { dirname as dirname18, join as join30, resolve as resolve23 } from "node:path";
31952
+ import { existsSync as existsSync27 } from "node:fs";
31953
+ import { dirname as dirname17, join as join31, resolve as resolve22 } from "node:path";
31385
31954
  import { createInterface as createInterface2 } from "node:readline";
31386
31955
 
31387
31956
  // plugins/kxm/src/hub-autostart.ts
31388
- import { closeSync as closeSync2, mkdirSync as mkdirSync18, openSync as openSync2, readFileSync as readFileSync22 } from "node:fs";
31389
- import { join as join25, resolve as resolve20 } from "node:path";
31957
+ import { closeSync as closeSync2, mkdirSync as mkdirSync18, openSync as openSync2, readFileSync as readFileSync23 } from "node:fs";
31958
+ import { join as join26, resolve as resolve19 } from "node:path";
31390
31959
  function defaultProcessExists(pid) {
31391
31960
  try {
31392
31961
  process.kill(pid, 0);
@@ -31398,7 +31967,7 @@ function defaultProcessExists(pid) {
31398
31967
  function readLiveHubClaim(stateDir, processExists3 = defaultProcessExists) {
31399
31968
  let parsed;
31400
31969
  try {
31401
- parsed = JSON.parse(readFileSync22(join25(stateDir, "hub.pid"), "utf8"));
31970
+ parsed = JSON.parse(readFileSync23(join26(stateDir, "hub.pid"), "utf8"));
31402
31971
  } catch {
31403
31972
  return void 0;
31404
31973
  }
@@ -31416,7 +31985,7 @@ function readLiveHubClaim(stateDir, processExists3 = defaultProcessExists) {
31416
31985
  // plugins/kxm/src/model-inventory.ts
31417
31986
  var import_yaml14 = __toESM(require_dist(), 1);
31418
31987
  import { mkdirSync as mkdirSync19, writeFileSync as writeFileSync18 } from "node:fs";
31419
- import { join as join26 } from "node:path";
31988
+ import { join as join27 } from "node:path";
31420
31989
  var OR_URL = "https://openrouter.ai/api/v1/models";
31421
31990
  var NOUS_URL = "https://inference-api.nousresearch.com/v1/models";
31422
31991
  function price(value) {
@@ -31502,41 +32071,41 @@ async function refreshModelInventory(options) {
31502
32071
  sources.nous = { url: nousUrl, ok: !nous.error, ...nous.error ? { error: nous.error } : {} };
31503
32072
  for (const item of nous.data) add(item, "nous", "discount");
31504
32073
  const inventory = { schema: "kxm.model-inventory.v1", fetchedAt, currency: "USD", sources, models: [...byId.values()].sort((a, b2) => a.id.localeCompare(b2.id)) };
31505
- mkdirSync19(join26(options.outputRoot, ".kxm", "models"), { recursive: true });
31506
- writeFileSync18(join26(options.outputRoot, ".kxm", "models", "inventory.yaml"), (0, import_yaml14.stringify)(inventory), "utf8");
32074
+ mkdirSync19(join27(options.outputRoot, ".kxm", "models"), { recursive: true });
32075
+ writeFileSync18(join27(options.outputRoot, ".kxm", "models", "inventory.yaml"), (0, import_yaml14.stringify)(inventory), "utf8");
31507
32076
  return inventory;
31508
32077
  }
31509
32078
 
31510
32079
  // plugins/kxm/src/init.ts
31511
32080
  import { randomUUID as randomUUID11 } from "node:crypto";
31512
- import { existsSync as existsSync25 } from "node:fs";
31513
- import { basename as basename6, join as join29 } from "node:path";
32081
+ import { existsSync as existsSync26 } from "node:fs";
32082
+ import { basename as basename6, join as join30 } from "node:path";
31514
32083
 
31515
32084
  // plugins/kxm/src/repair.ts
31516
32085
  import { randomUUID as randomUUID10 } from "node:crypto";
31517
32086
  import {
31518
32087
  chmodSync as chmodSync5,
31519
32088
  closeSync as closeSync3,
31520
- existsSync as existsSync24,
32089
+ existsSync as existsSync25,
31521
32090
  fsyncSync as fsyncSync2,
31522
32091
  linkSync,
31523
32092
  lstatSync as lstatSync7,
31524
32093
  mkdirSync as mkdirSync21,
31525
32094
  openSync as openSync3,
31526
- readFileSync as readFileSync24,
32095
+ readFileSync as readFileSync25,
31527
32096
  readdirSync as readdirSync9,
31528
32097
  renameSync as renameSync7,
31529
32098
  rmSync as rmSync9,
31530
32099
  writeFileSync as writeFileSync20
31531
32100
  } from "node:fs";
31532
- import { dirname as dirname17, isAbsolute as isAbsolute8, join as join28, relative as relative5, resolve as resolve22 } from "node:path";
32101
+ import { dirname as dirname16, isAbsolute as isAbsolute8, join as join29, relative as relative5, resolve as resolve21 } from "node:path";
31533
32102
 
31534
32103
  // plugins/kxm/src/permission.ts
31535
32104
  import { spawnSync as spawnSync4 } from "node:child_process";
31536
- import { createHash as createHash14 } from "node:crypto";
31537
- import { existsSync as existsSync23, mkdtempSync, mkdirSync as mkdirSync20, readFileSync as readFileSync23, rmSync as rmSync8, writeFileSync as writeFileSync19 } from "node:fs";
32105
+ import { createHash as createHash15 } from "node:crypto";
32106
+ import { existsSync as existsSync24, mkdtempSync, mkdirSync as mkdirSync20, readFileSync as readFileSync24, rmSync as rmSync8, writeFileSync as writeFileSync19 } from "node:fs";
31538
32107
  import { tmpdir } from "node:os";
31539
- import { dirname as dirname16, join as join27, resolve as resolve21, sep as sep4 } from "node:path";
32108
+ import { dirname as dirname15, join as join28, resolve as resolve20, sep as sep4 } from "node:path";
31540
32109
  var PROSE_FIELDS = {
31541
32110
  project: /* @__PURE__ */ new Set(["name", "description"]),
31542
32111
  repository: /* @__PURE__ */ new Set(["description"]),
@@ -31552,7 +32121,7 @@ var UNTRACKED_RANK = { "tracked-only": 0, ask: 1, bounded: 2 };
31552
32121
  function canonical(value) {
31553
32122
  return kxmCanonicalJson(value === void 0 ? null : value);
31554
32123
  }
31555
- function asObject(value) {
32124
+ function asObject2(value) {
31556
32125
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
31557
32126
  }
31558
32127
  function valuesOf3(value, key) {
@@ -31568,12 +32137,12 @@ function pointerEscape(segment) {
31568
32137
  function secretGrantKeys(grants) {
31569
32138
  const counts = /* @__PURE__ */ new Map();
31570
32139
  for (const grant of grants) {
31571
- const ref = stringValue3(asObject(grant)?.ref);
32140
+ const ref = stringValue3(asObject2(grant)?.ref);
31572
32141
  if (ref) counts.set(ref, (counts.get(ref) ?? 0) + 1);
31573
32142
  }
31574
32143
  const keys = /* @__PURE__ */ new Map();
31575
32144
  for (const [index, grant] of grants.entries()) {
31576
- const ref = stringValue3(asObject(grant)?.ref);
32145
+ const ref = stringValue3(asObject2(grant)?.ref);
31577
32146
  keys.set(index, ref && counts.get(ref) === 1 ? `by-ref/${pointerEscape(ref)}` : String(index));
31578
32147
  }
31579
32148
  return keys;
@@ -31593,21 +32162,21 @@ function kxmAuthorityEntries(resource) {
31593
32162
  const declarations = valuesOf3(value, "repositories");
31594
32163
  const declarationIds = /* @__PURE__ */ new Map();
31595
32164
  for (const entry of declarations) {
31596
- const id = stringValue3(asObject(entry)?.id);
32165
+ const id = stringValue3(asObject2(entry)?.id);
31597
32166
  if (id) declarationIds.set(id, (declarationIds.get(id) ?? 0) + 1);
31598
32167
  }
31599
32168
  for (const [index, repository] of declarations.entries()) {
31600
- const entry = asObject(repository);
32169
+ const entry = asObject2(repository);
31601
32170
  if (!entry) continue;
31602
32171
  const id = stringValue3(entry.id);
31603
32172
  const key = id && declarationIds.get(id) === 1 ? `by-id/${pointerEscape(id)}` : String(index);
31604
32173
  push(`/repositories/${key}`, "resource-shape", entry);
31605
32174
  }
31606
- const workspace = asObject(value.workspace);
32175
+ const workspace = asObject2(value.workspace);
31607
32176
  if (workspace) push("/workspace", "snapshot-policy", workspace);
31608
- const sync = asObject(value.sync);
32177
+ const sync = asObject2(value.sync);
31609
32178
  if (sync) push("/sync", "sync-policy", sync);
31610
- const limits = asObject(value.limits);
32179
+ const limits = asObject2(value.limits);
31611
32180
  if (limits) push("/limits", "budget", limits);
31612
32181
  break;
31613
32182
  }
@@ -31625,7 +32194,7 @@ function kxmAuthorityEntries(resource) {
31625
32194
  push("/harness", "harness", value.harness ?? "pi");
31626
32195
  push("/tools", "tools", value.tools);
31627
32196
  push("/defaultRepositoryAccess", "repository-access", value.defaultRepositoryAccess);
31628
- const repositories = asObject(value.repositories);
32197
+ const repositories = asObject2(value.repositories);
31629
32198
  for (const repositoryId of Object.keys(repositories ?? {}).sort()) {
31630
32199
  push(`/repositories/${pointerEscape(repositoryId)}`, "repository-access", repositories?.[repositoryId]);
31631
32200
  }
@@ -31635,7 +32204,7 @@ function kxmAuthorityEntries(resource) {
31635
32204
  }
31636
32205
  push("/network", "network", value.network);
31637
32206
  push("/resultSchema", "delivery", value.resultSchema);
31638
- const session = asObject(value.session);
32207
+ const session = asObject2(value.session);
31639
32208
  if (session) push("/session", "resource-shape", session);
31640
32209
  break;
31641
32210
  }
@@ -31647,15 +32216,15 @@ function kxmAuthorityEntries(resource) {
31647
32216
  push("/capabilities", "model", value.capabilities);
31648
32217
  push("/priority", "model", value.priority);
31649
32218
  push("/fallbacks", "model", value.fallbacks);
31650
- const limits = asObject(value.limits);
32219
+ const limits = asObject2(value.limits);
31651
32220
  if (limits) push("/limits", "budget", limits);
31652
32221
  break;
31653
32222
  }
31654
32223
  case "environment": {
31655
32224
  push("/path", "environment", value.path);
31656
- const envValues = asObject(value.values);
32225
+ const envValues = asObject2(value.values);
31657
32226
  for (const key of Object.keys(envValues ?? {}).sort()) {
31658
- const entry = asObject(envValues?.[key]);
32227
+ const entry = asObject2(envValues?.[key]);
31659
32228
  const projected = entry ? { ...entry, value: entry.value === void 0 ? void 0 : "<redacted>" } : envValues?.[key];
31660
32229
  push(`/values/${pointerEscape(key)}`, "environment", projected);
31661
32230
  }
@@ -31667,9 +32236,9 @@ function kxmAuthorityEntries(resource) {
31667
32236
  }
31668
32237
  case "gate-registry": {
31669
32238
  push("/schema", "resource-shape", value.schema);
31670
- const gates = asObject(value.gates) ?? {};
32239
+ const gates = asObject2(value.gates) ?? {};
31671
32240
  for (const id of Object.keys(gates).sort()) {
31672
- const definition = asObject(gates[id]);
32241
+ const definition = asObject2(gates[id]);
31673
32242
  const path4 = `/gates/${pointerEscape(id)}`;
31674
32243
  if (definition?.kind === "command") {
31675
32244
  const { timeoutMs, ...authority } = definition;
@@ -31681,13 +32250,13 @@ function kxmAuthorityEntries(resource) {
31681
32250
  }
31682
32251
  case "workflow": {
31683
32252
  push("/coordinator", "resource-shape", value.coordinator);
31684
- const limits = asObject(value.limits);
32253
+ const limits = asObject2(value.limits);
31685
32254
  if (limits) push("/limits", "budget", limits);
31686
32255
  if (value.reproOracle !== void 0) push("/reproOracle", "evidence-quorum", value.reproOracle);
31687
32256
  if (value.planHash !== void 0) push("/planHash", "evidence-quorum", value.planHash);
31688
32257
  if (value.requirePlanHash !== void 0) push("/requirePlanHash", "evidence-quorum", value.requirePlanHash);
31689
32258
  for (const [index, stepEntry] of valuesOf3(value, "steps").entries()) {
31690
- const step = asObject(stepEntry);
32259
+ const step = asObject2(stepEntry);
31691
32260
  if (!step) continue;
31692
32261
  const stepPath = `/steps/${index}`;
31693
32262
  push(`${stepPath}/id`, "resource-shape", step.id);
@@ -31699,7 +32268,7 @@ function kxmAuthorityEntries(resource) {
31699
32268
  push(`${stepPath}/model`, "model", step.model);
31700
32269
  push(`${stepPath}/maxAttempts`, "budget", step.maxAttempts);
31701
32270
  push(`${stepPath}/timeoutMs`, "budget", step.timeoutMs);
31702
- const repositories = asObject(step.repositories);
32271
+ const repositories = asObject2(step.repositories);
31703
32272
  for (const repositoryId of Object.keys(repositories ?? {}).sort()) {
31704
32273
  push(`${stepPath}/repositories/${pointerEscape(repositoryId)}`, "repository-access", repositories?.[repositoryId]);
31705
32274
  }
@@ -31729,7 +32298,7 @@ function kxmProseEntries(resource) {
31729
32298
  }
31730
32299
  if (resource.kind === "workflow") {
31731
32300
  for (const [index, stepEntry] of valuesOf3(resource.value, "steps").entries()) {
31732
- const step = asObject(stepEntry);
32301
+ const step = asObject2(stepEntry);
31733
32302
  if (!step) continue;
31734
32303
  for (const field of ["description", "instructions"]) {
31735
32304
  if (step[field] !== void 0) entries.set(`/steps/${index}/${field}`, canonical(step[field]));
@@ -31739,7 +32308,7 @@ function kxmProseEntries(resource) {
31739
32308
  return entries;
31740
32309
  }
31741
32310
  function valueHash(canonicalValue) {
31742
- return `sha256:${createHash14("sha256").update(canonicalValue, "utf8").digest("hex")}`;
32311
+ return `sha256:${createHash15("sha256").update(canonicalValue, "utf8").digest("hex")}`;
31743
32312
  }
31744
32313
  function rankDirection(rank, baseValue, candidateValue) {
31745
32314
  const base = JSON.parse(baseValue);
@@ -31760,8 +32329,8 @@ function numericBoundDirection(baseValue, candidateValue) {
31760
32329
  return "neutral";
31761
32330
  }
31762
32331
  function budgetDirection(baseValue, candidateValue) {
31763
- const base = asObject(JSON.parse(baseValue));
31764
- const candidate = asObject(JSON.parse(candidateValue));
32332
+ const base = asObject2(JSON.parse(baseValue));
32333
+ const candidate = asObject2(JSON.parse(candidateValue));
31765
32334
  if (!base || !candidate) return "expansion";
31766
32335
  let sawExpansion = false;
31767
32336
  let sawNarrowing = false;
@@ -31774,8 +32343,8 @@ function budgetDirection(baseValue, candidateValue) {
31774
32343
  return sawNarrowing ? "narrowing" : "neutral";
31775
32344
  }
31776
32345
  function quorumDirection(baseValue, candidateValue) {
31777
- const base = asObject(JSON.parse(baseValue));
31778
- const candidate = asObject(JSON.parse(candidateValue));
32346
+ const base = asObject2(JSON.parse(baseValue));
32347
+ const candidate = asObject2(JSON.parse(candidateValue));
31779
32348
  if (!base || !candidate) return "expansion";
31780
32349
  if (base.strategy !== void 0 || candidate.strategy !== void 0) return "expansion";
31781
32350
  if (base.stageId !== void 0 || candidate.stageId !== void 0) return "expansion";
@@ -31789,13 +32358,13 @@ function quorumDirection(baseValue, candidateValue) {
31789
32358
  const CLASSIFIED = /* @__PURE__ */ new Set(["key", "kind", "minimum", "reusableAcrossAttempts", "strategy", "stageId", "evidenceKey"]);
31790
32359
  const CLASSIFIED_POLICY = /* @__PURE__ */ new Set(["minimumProducers", "eligibleAgents", "degradation"]);
31791
32360
  const residualOf = (value) => {
31792
- const parsed = asObject(JSON.parse(value));
32361
+ const parsed = asObject2(JSON.parse(value));
31793
32362
  if (!parsed) return null;
31794
32363
  const residual = {};
31795
32364
  for (const [key, entry] of Object.entries(parsed)) {
31796
32365
  if (CLASSIFIED.has(key)) continue;
31797
32366
  if (key === "producerPolicy") {
31798
- const policy = asObject(entry);
32367
+ const policy = asObject2(entry);
31799
32368
  if (!policy) {
31800
32369
  residual[key] = entry;
31801
32370
  continue;
@@ -31808,8 +32377,8 @@ function quorumDirection(baseValue, candidateValue) {
31808
32377
  return residual;
31809
32378
  };
31810
32379
  if (kxmCanonicalJson(residualOf(baseValue)) !== kxmCanonicalJson(residualOf(candidateValue))) return "expansion";
31811
- const basePolicy = asObject(base.producerPolicy);
31812
- const candidatePolicy = asObject(candidate.producerPolicy);
32380
+ const basePolicy = asObject2(base.producerPolicy);
32381
+ const candidatePolicy = asObject2(candidate.producerPolicy);
31813
32382
  if (!basePolicy && !candidatePolicy) return sawNarrowing ? "narrowing" : "neutral";
31814
32383
  if (!basePolicy || !candidatePolicy) return "expansion";
31815
32384
  const basePolicyMinimum = typeof basePolicy.minimumProducers === "number" ? basePolicy.minimumProducers : 1;
@@ -31818,8 +32387,8 @@ function quorumDirection(baseValue, candidateValue) {
31818
32387
  const baseEligible = new Set((Array.isArray(basePolicy.eligibleAgents) ? basePolicy.eligibleAgents : []).map(String));
31819
32388
  const candidateEligible = new Set((Array.isArray(candidatePolicy.eligibleAgents) ? candidatePolicy.eligibleAgents : []).map(String));
31820
32389
  for (const agent of baseEligible) if (!candidateEligible.has(agent)) return "expansion";
31821
- const baseDegradation = asObject(basePolicy.degradation);
31822
- const candidateDegradation = asObject(candidatePolicy.degradation);
32390
+ const baseDegradation = asObject2(basePolicy.degradation);
32391
+ const candidateDegradation = asObject2(candidatePolicy.degradation);
31823
32392
  if (!baseDegradation && candidateDegradation) return "expansion";
31824
32393
  if (baseDegradation && candidateDegradation) {
31825
32394
  const baseFloor = typeof baseDegradation.minimumProducers === "number" ? baseDegradation.minimumProducers : 1;
@@ -31833,11 +32402,11 @@ function quorumDirection(baseValue, candidateValue) {
31833
32402
  return sawNarrowing ? "narrowing" : "neutral";
31834
32403
  }
31835
32404
  function snapshotDirection(baseValue, candidateValue) {
31836
- const base = asObject(JSON.parse(baseValue));
31837
- const candidate = asObject(JSON.parse(candidateValue));
32405
+ const base = asObject2(JSON.parse(baseValue));
32406
+ const candidate = asObject2(JSON.parse(candidateValue));
31838
32407
  if (!base || !candidate) return "expansion";
31839
- const baseSnapshot = asObject(base.dirtySnapshot);
31840
- const candidateSnapshot = asObject(candidate.dirtySnapshot);
32408
+ const baseSnapshot = asObject2(base.dirtySnapshot);
32409
+ const candidateSnapshot = asObject2(candidate.dirtySnapshot);
31841
32410
  if (!baseSnapshot || !candidateSnapshot) return "expansion";
31842
32411
  const untracked = rankDirection(UNTRACKED_RANK, canonical(baseSnapshot.untracked), canonical(candidateSnapshot.untracked));
31843
32412
  if (untracked === "expansion") return "expansion";
@@ -31891,8 +32460,8 @@ function classifyChange(field, baseValue, candidateValue, context) {
31891
32460
  case "snapshot-policy":
31892
32461
  return snapshotDirection(baseValue, candidateValue);
31893
32462
  case "secrets": {
31894
- const base = asObject(JSON.parse(baseValue));
31895
- const candidate = asObject(JSON.parse(candidateValue));
32463
+ const base = asObject2(JSON.parse(baseValue));
32464
+ const candidate = asObject2(JSON.parse(candidateValue));
31896
32465
  if (base && candidate && base.ref === candidate.ref) {
31897
32466
  const { required: _b, ...baseRest } = base;
31898
32467
  const { required: _c, ...candidateRest } = candidate;
@@ -32057,9 +32626,9 @@ function loadKxmProjectAtRevision(root, revision, options = {}) {
32057
32626
  const tree = resolveTreeRevision(root, revision);
32058
32627
  const listing = gitBuffer(root, ["ls-tree", "-r", "-z", "--name-only", tree]).toString("utf8");
32059
32628
  const paths = listing.split("\0").filter((line) => line.length > 0 && (line.startsWith(".kxm/") || line.includes("/.kxm/")));
32060
- const shadow = mkdtempSync(join27(tmpdir(), "kxm-permission-base-"));
32061
- const shadowResolved = resolve21(shadow);
32062
- const shadowMembersDir = `.kxm-shadow-members-${createHash14("sha256").update(shadowResolved, "utf8").digest("hex").slice(0, 8)}`;
32629
+ const shadow = mkdtempSync(join28(tmpdir(), "kxm-permission-base-"));
32630
+ const shadowResolved = resolve20(shadow);
32631
+ const shadowMembersDir = `.kxm-shadow-members-${createHash15("sha256").update(shadowResolved, "utf8").digest("hex").slice(0, 8)}`;
32063
32632
  try {
32064
32633
  for (const path4 of paths) {
32065
32634
  const segments = path4.split("/");
@@ -32071,8 +32640,8 @@ function loadKxmProjectAtRevision(root, revision, options = {}) {
32071
32640
  message: "Git tree entry contains a traversal or non-portable segment"
32072
32641
  }]);
32073
32642
  }
32074
- const absolute = join27(shadowResolved, ...segments);
32075
- const resolved = resolve21(absolute);
32643
+ const absolute = join28(shadowResolved, ...segments);
32644
+ const resolved = resolve20(absolute);
32076
32645
  if (!resolved.startsWith(`${shadowResolved}${sep4}`)) {
32077
32646
  throw new KxmConfigError([{
32078
32647
  phase: "path",
@@ -32082,11 +32651,11 @@ function loadKxmProjectAtRevision(root, revision, options = {}) {
32082
32651
  }]);
32083
32652
  }
32084
32653
  const content = gitBuffer(root, ["cat-file", "blob", `${tree}:${path4}`]);
32085
- mkdirSync20(dirname16(absolute), { recursive: true });
32654
+ mkdirSync20(dirname15(absolute), { recursive: true });
32086
32655
  writeFileSync19(absolute, content);
32087
32656
  }
32088
32657
  initShadowGitRoot(shadowResolved);
32089
- const baseProjectFile = join27(shadowResolved, ".kxm", "project.yaml");
32658
+ const baseProjectFile = join28(shadowResolved, ".kxm", "project.yaml");
32090
32659
  let baseProject;
32091
32660
  try {
32092
32661
  baseProject = loadBaseProjectDeclarations(baseProjectFile);
@@ -32116,8 +32685,8 @@ function loadKxmProjectAtRevision(root, revision, options = {}) {
32116
32685
  }]);
32117
32686
  }
32118
32687
  const boundPath = options.repositoryBindings?.[member.repositoryId];
32119
- const memberWorktree = boundPath ? resolve21(boundPath) : member.pathHint ? resolve21(root, ...member.pathHint.split("/")) : void 0;
32120
- if (!memberWorktree || !existsSync23(memberWorktree)) {
32688
+ const memberWorktree = boundPath ? resolve20(boundPath) : member.pathHint ? resolve20(root, ...member.pathHint.split("/")) : void 0;
32689
+ if (!memberWorktree || !existsSync24(memberWorktree)) {
32121
32690
  if (member.required === false) continue;
32122
32691
  throw new KxmConfigError([{
32123
32692
  phase: "discovery",
@@ -32164,8 +32733,8 @@ function loadKxmProjectAtRevision(root, revision, options = {}) {
32164
32733
  const memberTree = git(memberWorktree, ["rev-parse", "--verify", "--quiet", `${pinnedCommit}^{tree}`]).trim();
32165
32734
  const memberListing = gitBuffer(memberWorktree, ["ls-tree", "-r", "-z", "--name-only", memberTree]).toString("utf8");
32166
32735
  const memberPaths = memberListing.split("\0").filter((line) => line.length > 0 && (line.startsWith(".kxm/") || line.includes("/.kxm/")));
32167
- const memberShadowRoot = join27(shadowResolved, shadowMembersDir, member.repositoryId);
32168
- if (!resolve21(memberShadowRoot).startsWith(`${resolve21(shadowResolved, shadowMembersDir)}${sep4}`)) {
32736
+ const memberShadowRoot = join28(shadowResolved, shadowMembersDir, member.repositoryId);
32737
+ if (!resolve20(memberShadowRoot).startsWith(`${resolve20(shadowResolved, shadowMembersDir)}${sep4}`)) {
32169
32738
  throw new KxmConfigError([{
32170
32739
  phase: "path",
32171
32740
  code: "git_tree_path_invalid",
@@ -32178,13 +32747,13 @@ function loadKxmProjectAtRevision(root, revision, options = {}) {
32178
32747
  if (segments.some((segment) => segment === "" || segment === "." || segment === ".." || /[\\:]/.test(segment))) {
32179
32748
  throw new KxmConfigError([{ phase: "path", code: "git_tree_path_invalid", file: path4, message: "member Git tree entry contains a traversal or non-portable segment" }]);
32180
32749
  }
32181
- const absolute = join27(memberShadowRoot, ...segments);
32182
- const resolved = resolve21(absolute);
32183
- if (!resolved.startsWith(`${resolve21(memberShadowRoot)}${sep4}`)) {
32750
+ const absolute = join28(memberShadowRoot, ...segments);
32751
+ const resolved = resolve20(absolute);
32752
+ if (!resolved.startsWith(`${resolve20(memberShadowRoot)}${sep4}`)) {
32184
32753
  throw new KxmConfigError([{ phase: "path", code: "git_tree_path_invalid", file: path4, message: "member Git tree entry escapes the shadow root" }]);
32185
32754
  }
32186
32755
  const content = gitBuffer(memberWorktree, ["cat-file", "blob", `${memberTree}:${path4}`]);
32187
- mkdirSync20(dirname16(absolute), { recursive: true });
32756
+ mkdirSync20(dirname15(absolute), { recursive: true });
32188
32757
  writeFileSync19(absolute, content);
32189
32758
  }
32190
32759
  initShadowGitRoot(memberShadowRoot);
@@ -32206,8 +32775,8 @@ function portableMemberPathHint(pathHint) {
32206
32775
  return pathHint !== "." && kxmPortablePath(pathHint);
32207
32776
  }
32208
32777
  function loadBaseProjectDeclarations(projectFile) {
32209
- if (!existsSync23(projectFile)) return [];
32210
- const value = parseRestrictedYaml2(readFileSync23(projectFile, "utf8"), ".kxm/project.yaml");
32778
+ if (!existsSync24(projectFile)) return [];
32779
+ const value = parseRestrictedYaml2(readFileSync24(projectFile, "utf8"), ".kxm/project.yaml");
32211
32780
  const repositories = Array.isArray(value?.repositories) ? value.repositories : [];
32212
32781
  const members = [];
32213
32782
  for (const entry of repositories) {
@@ -32310,7 +32879,7 @@ function writeDurableNew(file, bytes, mode) {
32310
32879
  } finally {
32311
32880
  if (descriptor !== void 0) closeSync3(descriptor);
32312
32881
  }
32313
- syncDirectory2(dirname17(file));
32882
+ syncDirectory2(dirname16(file));
32314
32883
  }
32315
32884
  function writeDurable(file, bytes, mode) {
32316
32885
  let descriptor;
@@ -32327,27 +32896,27 @@ function writeDurable(file, bytes, mode) {
32327
32896
  } finally {
32328
32897
  if (descriptor !== void 0) closeSync3(descriptor);
32329
32898
  }
32330
- syncDirectory2(dirname17(file));
32899
+ syncDirectory2(dirname16(file));
32331
32900
  }
32332
32901
  function sameHostPath3(left, right) {
32333
- const first = resolve22(left);
32334
- const second = resolve22(right);
32902
+ const first = resolve21(left);
32903
+ const second = resolve21(right);
32335
32904
  return process.platform === "win32" ? first.toLocaleLowerCase("en-US") === second.toLocaleLowerCase("en-US") : first === second;
32336
32905
  }
32337
32906
  function transactionRoot(projectRoot) {
32338
- return join28(resolve22(projectRoot), TRANSACTION_NAME);
32907
+ return join29(resolve21(projectRoot), TRANSACTION_NAME);
32339
32908
  }
32340
32909
  function operationFile(projectRoot) {
32341
- return join28(transactionRoot(projectRoot), OPERATION_FILE);
32910
+ return join29(transactionRoot(projectRoot), OPERATION_FILE);
32342
32911
  }
32343
32912
  function targetFile(projectRoot, portablePath2) {
32344
- return join28(transactionRoot(projectRoot), "targets", ...portablePath2.split("/"));
32913
+ return join29(transactionRoot(projectRoot), "targets", ...portablePath2.split("/"));
32345
32914
  }
32346
32915
  function shadowRoot(projectRoot) {
32347
- return join28(transactionRoot(projectRoot), "shadow");
32916
+ return join29(transactionRoot(projectRoot), "shadow");
32348
32917
  }
32349
32918
  function backupFile(projectRoot, portablePath2) {
32350
- return join28(transactionRoot(projectRoot), "backups", ...portablePath2.split("/"));
32919
+ return join29(transactionRoot(projectRoot), "backups", ...portablePath2.split("/"));
32351
32920
  }
32352
32921
  function portableManagedPath(path4) {
32353
32922
  return path4.startsWith(".kxm/") && path4 !== KXM_TEMPLATE_PROVENANCE_PATH && path4.length <= 1024 && !path4.includes("\\") && !isAbsolute8(path4) && PORTABLE_PATH.test(path4) && !/[<>:"|?*]/.test(path4);
@@ -32356,24 +32925,24 @@ function readRegularBounded(file, label) {
32356
32925
  const stat = lstatSync7(file);
32357
32926
  if (stat.isSymbolicLink() || !stat.isFile()) fail3("repair_file_invalid", label, "managed file must be a regular file, not a link or directory", "path");
32358
32927
  if (stat.size > MAX_MANAGED_FILE_BYTES) fail3("repair_file_too_large", label, `managed file exceeds ${MAX_MANAGED_FILE_BYTES} bytes`, "parse");
32359
- return readFileSync24(file);
32928
+ return readFileSync25(file);
32360
32929
  }
32361
32930
  function readOptionalManaged(projectRoot, path4) {
32362
- const file = join28(projectRoot, ...path4.split("/"));
32363
- if (!existsSync24(file)) return void 0;
32364
- let parent = dirname17(file);
32365
- const boundary = resolve22(projectRoot);
32931
+ const file = join29(projectRoot, ...path4.split("/"));
32932
+ if (!existsSync25(file)) return void 0;
32933
+ let parent = dirname16(file);
32934
+ const boundary = resolve21(projectRoot);
32366
32935
  while (parent !== boundary) {
32367
32936
  const stat = lstatSync7(parent);
32368
32937
  if (stat.isSymbolicLink() || !stat.isDirectory()) fail3("repair_parent_invalid", path4, "managed file parents must be regular directories", "path");
32369
- parent = dirname17(parent);
32938
+ parent = dirname16(parent);
32370
32939
  }
32371
32940
  return readRegularBounded(file, path4);
32372
32941
  }
32373
32942
  function readProjectAndProvenance(projectRoot, schemasDir) {
32374
- const projectFile = join28(projectRoot, ".kxm", "project.yaml");
32375
- const provenanceFile = join28(projectRoot, ...KXM_TEMPLATE_PROVENANCE_PATH.split("/"));
32376
- if (!existsSync24(projectFile) || !existsSync24(provenanceFile)) return void 0;
32943
+ const projectFile = join29(projectRoot, ".kxm", "project.yaml");
32944
+ const provenanceFile = join29(projectRoot, ...KXM_TEMPLATE_PROVENANCE_PATH.split("/"));
32945
+ if (!existsSync25(projectFile) || !existsSync25(provenanceFile)) return void 0;
32377
32946
  const projectBytes = readOptionalManaged(projectRoot, ".kxm/project.yaml");
32378
32947
  const provenanceBytes = readOptionalManaged(projectRoot, KXM_TEMPLATE_PROVENANCE_PATH);
32379
32948
  if (!projectBytes || !provenanceBytes) return void 0;
@@ -32409,7 +32978,7 @@ function readProjectAndProvenance(projectRoot, schemasDir) {
32409
32978
  }
32410
32979
  function planKxmTemplateRepair(projectRoot, options = {}) {
32411
32980
  assertNoRegisteredGates(options);
32412
- const root = resolve22(projectRoot);
32981
+ const root = resolve21(projectRoot);
32413
32982
  const source = readProjectAndProvenance(root, options.schemasDir);
32414
32983
  if (!source) return void 0;
32415
32984
  const target = renderKxmTemplate(
@@ -32537,7 +33106,7 @@ function operationPlanSha(operation) {
32537
33106
  function writeOperation(projectRoot, operation) {
32538
33107
  const root = transactionRoot(projectRoot);
32539
33108
  const file = operationFile(projectRoot);
32540
- const temporary = join28(root, `.operation-${operation.operationId}-${randomUUID10()}.tmp`);
33109
+ const temporary = join29(root, `.operation-${operation.operationId}-${randomUUID10()}.tmp`);
32541
33110
  try {
32542
33111
  writeDurableNew(temporary, Buffer.from(`${JSON.stringify(operation, null, 2)}
32543
33112
  `, "utf8"), 384);
@@ -32554,11 +33123,11 @@ function updatePhase(projectRoot, operation, phase) {
32554
33123
  }
32555
33124
  function readOperation(projectRoot, schemasDir) {
32556
33125
  const root = transactionRoot(projectRoot);
32557
- if (!existsSync24(root)) return void 0;
33126
+ if (!existsSync25(root)) return void 0;
32558
33127
  const stat = lstatSync7(root);
32559
33128
  if (stat.isSymbolicLink() || !stat.isDirectory()) fail3("init_transaction_invalid", TRANSACTION_NAME, "initialization transaction must be a regular directory", "path");
32560
33129
  const file = operationFile(projectRoot);
32561
- if (!existsSync24(file)) {
33130
+ if (!existsSync25(file)) {
32562
33131
  const entries = readdirSync9(root, { withFileTypes: true });
32563
33132
  const recoverable = entries.every((entry) => entry.isFile() && /^\.operation-op_[a-f0-9]{32}-[a-f0-9-]{36}\.tmp$/.test(entry.name));
32564
33133
  if (recoverable) return void 0;
@@ -32586,15 +33155,15 @@ function readOperation(projectRoot, schemasDir) {
32586
33155
  return operation;
32587
33156
  }
32588
33157
  function hasKxmInitTransaction(projectRoot) {
32589
- return existsSync24(transactionRoot(projectRoot));
33158
+ return existsSync25(transactionRoot(projectRoot));
32590
33159
  }
32591
33160
  function readTarget(projectRoot, path4) {
32592
- return readOptionalManaged(join28(transactionRoot(projectRoot), "targets"), path4);
33161
+ return readOptionalManaged(join29(transactionRoot(projectRoot), "targets"), path4);
32593
33162
  }
32594
33163
  function writeTarget(projectRoot, path4, bytes) {
32595
33164
  if (bytes.byteLength > MAX_MANAGED_FILE_BYTES) fail3("template_target_too_large", path4, `target exceeds ${MAX_MANAGED_FILE_BYTES} bytes`);
32596
33165
  const file = targetFile(projectRoot, path4);
32597
- mkdirSync21(dirname17(file), { recursive: true, mode: 448 });
33166
+ mkdirSync21(dirname16(file), { recursive: true, mode: 448 });
32598
33167
  writeDurableNew(file, bytes, 384);
32599
33168
  }
32600
33169
  function writeAndVerifyBackups(projectRoot, operation) {
@@ -32606,7 +33175,7 @@ function writeAndVerifyBackups(projectRoot, operation) {
32606
33175
  fail3("repair_preimage_changed", entry.path, "destination changed before transaction preparation; local bytes were preserved");
32607
33176
  }
32608
33177
  const file = backupFile(projectRoot, entry.path);
32609
- mkdirSync21(dirname17(file), { recursive: true, mode: 448 });
33178
+ mkdirSync21(dirname16(file), { recursive: true, mode: 448 });
32610
33179
  writeDurableNew(file, current, 384);
32611
33180
  }
32612
33181
  }
@@ -32621,7 +33190,7 @@ function writeKnownBackups(projectRoot, operation) {
32621
33190
  fail3("init_transaction_backup_source_invalid", entry.path, "supported source template cannot reproduce the pinned preimage");
32622
33191
  }
32623
33192
  const file = backupFile(projectRoot, entry.path);
32624
- mkdirSync21(dirname17(file), { recursive: true, mode: 448 });
33193
+ mkdirSync21(dirname16(file), { recursive: true, mode: 448 });
32625
33194
  writeDurableNew(file, bytes, 384);
32626
33195
  }
32627
33196
  }
@@ -32629,7 +33198,7 @@ function verifyBackups(projectRoot, operation) {
32629
33198
  if (operation.kind !== "repair") return;
32630
33199
  for (const entry of operation.files) {
32631
33200
  if (entry.observedSha256 === null) continue;
32632
- const bytes = readOptionalManaged(join28(transactionRoot(projectRoot), "backups"), entry.path);
33201
+ const bytes = readOptionalManaged(join29(transactionRoot(projectRoot), "backups"), entry.path);
32633
33202
  if (!bytes || kxmContentSha256(bytes) !== entry.observedSha256) {
32634
33203
  fail3("init_transaction_backup_invalid", entry.path, "pinned preimage backup is missing or corrupt");
32635
33204
  }
@@ -32663,10 +33232,10 @@ function sourceConfigFiles(projectRoot) {
32663
33232
  ".kxm/repo/repo.yaml",
32664
33233
  ".kxm/repo/env.yaml"
32665
33234
  ];
32666
- const files = fixed.filter((path4) => existsSync24(join28(projectRoot, ...path4.split("/"))));
33235
+ const files = fixed.filter((path4) => existsSync25(join29(projectRoot, ...path4.split("/"))));
32667
33236
  for (const directory of ["agents", "models", "workflows"]) {
32668
- const absolute = join28(projectRoot, ".kxm", directory);
32669
- if (!existsSync24(absolute)) continue;
33237
+ const absolute = join29(projectRoot, ".kxm", directory);
33238
+ if (!existsSync25(absolute)) continue;
32670
33239
  const stat = lstatSync7(absolute);
32671
33240
  if (stat.isSymbolicLink() || !stat.isDirectory()) fail3("repair_directory_invalid", `.kxm/${directory}`, "configuration directory must be regular", "path");
32672
33241
  for (const entry of readdirSync9(absolute, { withFileTypes: true })) {
@@ -32687,8 +33256,8 @@ function absoluteMemberBindings(projectRoot, project, explicit = {}) {
32687
33256
  const repository = candidate;
32688
33257
  if (repository.role !== "member" || typeof repository.id !== "string" || Object.hasOwn(bindings, repository.id)) continue;
32689
33258
  if (typeof repository.pathHint === "string") {
32690
- const candidate2 = resolve22(projectRoot, ...repository.pathHint.split("/"));
32691
- if (existsSync24(candidate2)) bindings[repository.id] = candidate2;
33259
+ const candidate2 = resolve21(projectRoot, ...repository.pathHint.split("/"));
33260
+ if (existsSync25(candidate2)) bindings[repository.id] = candidate2;
32692
33261
  }
32693
33262
  }
32694
33263
  return bindings;
@@ -32702,9 +33271,9 @@ function prepareShadow(projectRoot, operation, options) {
32702
33271
  const bytes = readOptionalManaged(projectRoot, path4);
32703
33272
  if (!bytes) fail3("repair_snapshot_file_missing", path4, "configuration changed during shadow preparation");
32704
33273
  total += bytes.byteLength;
32705
- const source = join28(projectRoot, ...path4.split("/"));
32706
- const destination = join28(shadow, ...path4.split("/"));
32707
- mkdirSync21(dirname17(destination), { recursive: true, mode: 448 });
33274
+ const source = join29(projectRoot, ...path4.split("/"));
33275
+ const destination = join29(shadow, ...path4.split("/"));
33276
+ mkdirSync21(dirname16(destination), { recursive: true, mode: 448 });
32708
33277
  writeDurableNew(destination, bytes, lstatSync7(source).mode & 511);
32709
33278
  if (total > MAX_MANAGED_TOTAL_BYTES) fail3("repair_snapshot_too_large", ".kxm", `configuration snapshot exceeds ${MAX_MANAGED_TOTAL_BYTES} bytes`, "parse");
32710
33279
  }
@@ -32712,12 +33281,12 @@ function prepareShadow(projectRoot, operation, options) {
32712
33281
  for (const entry of operation.files) {
32713
33282
  const bytes = readTarget(projectRoot, entry.path);
32714
33283
  if (!bytes) fail3("init_transaction_target_missing", entry.path, "pinned target artifact is missing");
32715
- const destination = join28(shadow, ...entry.path.split("/"));
32716
- mkdirSync21(dirname17(destination), { recursive: true, mode: 448 });
32717
- const mode = existsSync24(destination) ? lstatSync7(destination).mode & 511 : 420;
33284
+ const destination = join29(shadow, ...entry.path.split("/"));
33285
+ mkdirSync21(dirname16(destination), { recursive: true, mode: 448 });
33286
+ const mode = existsSync25(destination) ? lstatSync7(destination).mode & 511 : 420;
32718
33287
  writeDurable(destination, bytes, mode);
32719
33288
  }
32720
- const project = parseRestrictedYaml2(readFileSync24(join28(shadow, ".kxm", "project.yaml")), ".kxm/project.yaml");
33289
+ const project = parseRestrictedYaml2(readFileSync25(join29(shadow, ".kxm", "project.yaml")), ".kxm/project.yaml");
32721
33290
  const bound = absoluteMemberBindings(projectRoot, project, options.repositoryBindings);
32722
33291
  loadKxmProject(shadow, { ...loaderOptions(options), repositoryBindings: bound });
32723
33292
  const provenance = readProjectAndProvenance(shadow, options.schemasDir)?.provenance;
@@ -32726,7 +33295,7 @@ function prepareShadow(projectRoot, operation, options) {
32726
33295
  }
32727
33296
  }
32728
33297
  function createOperation(projectRoot, kind, rendered, files, sourceTemplateRevision) {
32729
- const root = resolve22(projectRoot);
33298
+ const root = resolve21(projectRoot);
32730
33299
  const operationWithoutHash = {
32731
33300
  schema: "kxm.init-operation.v1",
32732
33301
  operationId: `op_${randomUUID10().replaceAll("-", "")}`,
@@ -32755,7 +33324,7 @@ function prepareOperation(projectRoot, operation, rendered, options) {
32755
33324
  const transaction = transactionRoot(projectRoot);
32756
33325
  validateOperationRecord(operation, options.schemasDir);
32757
33326
  validateOperationIntent(projectRoot, operation);
32758
- if (existsSync24(transaction)) fail3("init_transaction_exists", TRANSACTION_NAME, "an initialization transaction already exists and must be resumed");
33327
+ if (existsSync25(transaction)) fail3("init_transaction_exists", TRANSACTION_NAME, "an initialization transaction already exists and must be resumed");
32759
33328
  mkdirSync21(transaction, { mode: 448 });
32760
33329
  syncDirectory2(projectRoot);
32761
33330
  writeOperation(projectRoot, operation);
@@ -32777,25 +33346,25 @@ function prepareOperation(projectRoot, operation, rendered, options) {
32777
33346
  }
32778
33347
  }
32779
33348
  function ensureDestinationParents(projectRoot, path4) {
32780
- const boundary = resolve22(projectRoot);
33349
+ const boundary = resolve21(projectRoot);
32781
33350
  const segments = path4.split("/").slice(0, -1);
32782
33351
  let current = boundary;
32783
33352
  for (const segment of segments) {
32784
- current = join28(current, segment);
32785
- if (!existsSync24(current)) mkdirSync21(current, { mode: 448 });
33353
+ current = join29(current, segment);
33354
+ if (!existsSync25(current)) mkdirSync21(current, { mode: 448 });
32786
33355
  const stat = lstatSync7(current);
32787
33356
  if (stat.isSymbolicLink() || !stat.isDirectory()) fail3("repair_destination_parent_invalid", path4, "destination parent must be a regular directory", "path");
32788
33357
  }
32789
33358
  }
32790
33359
  function destinationSha(projectRoot, path4) {
32791
- const file = join28(projectRoot, ...path4.split("/"));
32792
- if (!existsSync24(file)) return void 0;
33360
+ const file = join29(projectRoot, ...path4.split("/"));
33361
+ if (!existsSync25(file)) return void 0;
32793
33362
  return kxmContentSha256(readRegularBounded(file, path4));
32794
33363
  }
32795
33364
  function requireConditionalLinkSupport(directory, operationId, path4) {
32796
33365
  const nonce = randomUUID10();
32797
- const source = join28(directory, `.kxm-repair-${operationId}-${nonce}.link-source`);
32798
- const target = join28(directory, `.kxm-repair-${operationId}-${nonce}.link-target`);
33366
+ const source = join29(directory, `.kxm-repair-${operationId}-${nonce}.link-source`);
33367
+ const target = join29(directory, `.kxm-repair-${operationId}-${nonce}.link-target`);
32799
33368
  try {
32800
33369
  writeDurableNew(source, Buffer.from("kxm-link-probe\n", "utf8"), 384);
32801
33370
  try {
@@ -32810,19 +33379,19 @@ function requireConditionalLinkSupport(directory, operationId, path4) {
32810
33379
  }
32811
33380
  }
32812
33381
  function atomicInstallTarget(projectRoot, operation, entry) {
32813
- const destination = join28(projectRoot, ...entry.path.split("/"));
33382
+ const destination = join29(projectRoot, ...entry.path.split("/"));
32814
33383
  ensureDestinationParents(projectRoot, entry.path);
32815
- const directory = dirname17(destination);
33384
+ const directory = dirname16(destination);
32816
33385
  const temporaryPrefix = `.kxm-repair-${operation.operationId}-`;
32817
33386
  const pathKey = kxmContentSha256(entry.path).slice("sha256:".length, "sha256:".length + 16);
32818
- const displaced = join28(directory, `${temporaryPrefix}${pathKey}.preimage`);
32819
- if (existsSync24(displaced)) {
33387
+ const displaced = join29(directory, `${temporaryPrefix}${pathKey}.preimage`);
33388
+ if (existsSync25(displaced)) {
32820
33389
  const stat = lstatSync7(displaced);
32821
33390
  if (stat.isSymbolicLink() || !stat.isFile()) fail3("repair_preimage_temporary_invalid", entry.path, "stale displaced preimage is not a regular file", "path");
32822
- if (entry.observedSha256 === null || kxmContentSha256(readFileSync24(displaced)) !== entry.observedSha256) {
33391
+ if (entry.observedSha256 === null || kxmContentSha256(readFileSync25(displaced)) !== entry.observedSha256) {
32823
33392
  fail3("repair_preimage_temporary_mismatch", entry.path, "stale displaced preimage does not match the pinned operation");
32824
33393
  }
32825
- if (!existsSync24(destination)) {
33394
+ if (!existsSync25(destination)) {
32826
33395
  try {
32827
33396
  linkSync(displaced, destination);
32828
33397
  } catch {
@@ -32837,7 +33406,7 @@ function atomicInstallTarget(projectRoot, operation, entry) {
32837
33406
  const owned = candidate.name.startsWith(temporaryPrefix) && (candidate.name.endsWith(".tmp") || candidate.name.endsWith(".link-source") || candidate.name.endsWith(".link-target"));
32838
33407
  if (!owned) continue;
32839
33408
  if (!candidate.isFile()) fail3("repair_temporary_invalid", entry.path, "stale operation temporary is not a regular file", "path");
32840
- rmSync9(join28(directory, candidate.name), { force: true });
33409
+ rmSync9(join29(directory, candidate.name), { force: true });
32841
33410
  }
32842
33411
  const current = destinationSha(projectRoot, entry.path);
32843
33412
  if (current === entry.targetSha256) return;
@@ -32847,15 +33416,15 @@ function atomicInstallTarget(projectRoot, operation, entry) {
32847
33416
  requireConditionalLinkSupport(directory, operation.operationId, entry.path);
32848
33417
  const bytes = readTarget(projectRoot, entry.path);
32849
33418
  if (!bytes) fail3("init_transaction_target_missing", entry.path, "pinned target artifact is missing");
32850
- const temporary = join28(directory, `${temporaryPrefix}${randomUUID10()}.tmp`);
32851
- const mode = existsSync24(destination) ? lstatSync7(destination).mode & 511 : 420;
33419
+ const temporary = join29(directory, `${temporaryPrefix}${randomUUID10()}.tmp`);
33420
+ const mode = existsSync25(destination) ? lstatSync7(destination).mode & 511 : 420;
32852
33421
  try {
32853
33422
  writeDurableNew(temporary, bytes, mode);
32854
33423
  if (entry.observedSha256 !== null) {
32855
33424
  renameSync7(destination, displaced);
32856
33425
  syncDirectory2(directory);
32857
- if (kxmContentSha256(readFileSync24(displaced)) !== entry.observedSha256) {
32858
- if (!existsSync24(destination)) {
33426
+ if (kxmContentSha256(readFileSync25(displaced)) !== entry.observedSha256) {
33427
+ if (!existsSync25(destination)) {
32859
33428
  try {
32860
33429
  linkSync(displaced, destination);
32861
33430
  } catch {
@@ -32871,7 +33440,7 @@ function atomicInstallTarget(projectRoot, operation, entry) {
32871
33440
  try {
32872
33441
  linkSync(temporary, destination);
32873
33442
  } catch {
32874
- if (!existsSync24(destination) && existsSync24(displaced)) {
33443
+ if (!existsSync25(destination) && existsSync25(displaced)) {
32875
33444
  try {
32876
33445
  linkSync(displaced, destination);
32877
33446
  } catch {
@@ -32893,12 +33462,12 @@ function atomicInstallTarget(projectRoot, operation, entry) {
32893
33462
  if (destinationSha(projectRoot, entry.path) !== entry.targetSha256) fail3("repair_install_verification_failed", entry.path, "installed target hash does not match the pinned operation");
32894
33463
  }
32895
33464
  function installedCreateMatches(projectRoot, operation) {
32896
- const configRoot = join28(projectRoot, ".kxm");
32897
- if (!existsSync24(configRoot)) return false;
33465
+ const configRoot = join29(projectRoot, ".kxm");
33466
+ if (!existsSync25(configRoot)) return false;
32898
33467
  const actual = [];
32899
33468
  const visit2 = (directory) => {
32900
33469
  for (const entry of readdirSync9(directory, { withFileTypes: true })) {
32901
- const absolute = join28(directory, entry.name);
33470
+ const absolute = join29(directory, entry.name);
32902
33471
  if (entry.isSymbolicLink()) fail3("create_resume_link", relativeConfigPath(projectRoot, absolute), "created configuration must not contain links", "path");
32903
33472
  if (entry.isDirectory()) visit2(absolute);
32904
33473
  else if (entry.isFile()) actual.push(relativeConfigPath(projectRoot, absolute));
@@ -32911,15 +33480,15 @@ function installedCreateMatches(projectRoot, operation) {
32911
33480
  return actual.length === expected.length && actual.every((path4, index) => path4 === expected[index]) && operation.files.every((entry) => destinationSha(projectRoot, entry.path) === entry.targetSha256);
32912
33481
  }
32913
33482
  function relativeConfigPath(projectRoot, file) {
32914
- return relative5(resolve22(projectRoot), file).replaceAll("\\", "/");
33483
+ return relative5(resolve21(projectRoot), file).replaceAll("\\", "/");
32915
33484
  }
32916
33485
  function syncTreeDirectories(root) {
32917
- if (!existsSync24(root)) return;
33486
+ if (!existsSync25(root)) return;
32918
33487
  const directories = [];
32919
33488
  const visit2 = (directory) => {
32920
33489
  directories.push(directory);
32921
33490
  for (const entry of readdirSync9(directory, { withFileTypes: true })) {
32922
- if (entry.isDirectory()) visit2(join28(directory, entry.name));
33491
+ if (entry.isDirectory()) visit2(join29(directory, entry.name));
32923
33492
  }
32924
33493
  };
32925
33494
  visit2(root);
@@ -32933,7 +33502,7 @@ function rebuildCreateShadow(projectRoot, operation, options) {
32933
33502
  if (stat.isSymbolicLink() || !stat.isDirectory() || !installedCreateMatches(shadow, operation)) {
32934
33503
  fail3("create_shadow_invalid", TRANSACTION_NAME, "reconstructed create shadow does not exactly match pinned target files", "path");
32935
33504
  }
32936
- syncTreeDirectories(join28(shadow, ".kxm"));
33505
+ syncTreeDirectories(join29(shadow, ".kxm"));
32937
33506
  }
32938
33507
  function applyOperation(projectRoot, original, options, resumed) {
32939
33508
  assertNoRegisteredGates(options);
@@ -32943,10 +33512,10 @@ function applyOperation(projectRoot, original, options, resumed) {
32943
33512
  verifyBackups(projectRoot, operation);
32944
33513
  operation = updatePhase(projectRoot, operation, "applying");
32945
33514
  if (operation.kind === "create") {
32946
- const destination = join28(projectRoot, ".kxm");
32947
- if (!existsSync24(destination)) {
33515
+ const destination = join29(projectRoot, ".kxm");
33516
+ if (!existsSync25(destination)) {
32948
33517
  rebuildCreateShadow(projectRoot, operation, effectiveOptions);
32949
- renameSync7(join28(shadowRoot(projectRoot), ".kxm"), destination);
33518
+ renameSync7(join29(shadowRoot(projectRoot), ".kxm"), destination);
32950
33519
  syncDirectory2(projectRoot);
32951
33520
  } else if (!installedCreateMatches(projectRoot, operation)) {
32952
33521
  fail3("create_resume_conflict", ".kxm", "existing project configuration does not match the pinned create operation");
@@ -32973,7 +33542,7 @@ function applyOperation(projectRoot, original, options, resumed) {
32973
33542
  operation = updatePhase(projectRoot, operation, "verified");
32974
33543
  if (options.testFaultAt === "verified") throw new Error("injected init fault after verification");
32975
33544
  const files = operation.files.map((entry) => entry.path);
32976
- syncTreeDirectories(join28(projectRoot, ".kxm"));
33545
+ syncTreeDirectories(join29(projectRoot, ".kxm"));
32977
33546
  return { kind: operation.kind, resumed, bundle, files };
32978
33547
  }
32979
33548
  function prepareAndApplyKxmCreate(projectRoot, rendered, options = {}) {
@@ -33088,7 +33657,7 @@ function finishPreparingOperation(projectRoot, operation, options) {
33088
33657
  const rendered = rendererForOperation(operation);
33089
33658
  if (!rendered) fail3("init_transaction_renderer_unavailable", TRANSACTION_NAME, "this binary cannot reproduce the pinned pre-application target");
33090
33659
  for (const directory of ["targets", "backups", "shadow"]) {
33091
- rmSync9(join28(transactionRoot(projectRoot), directory), { recursive: true, force: true });
33660
+ rmSync9(join29(transactionRoot(projectRoot), directory), { recursive: true, force: true });
33092
33661
  }
33093
33662
  for (const entry of operation.files) {
33094
33663
  const bytes = rendered.files.get(entry.path);
@@ -33107,7 +33676,7 @@ function resumeKxmInitTransaction(projectRoot, options = {}) {
33107
33676
  let operation = readOperation(projectRoot, options.schemasDir);
33108
33677
  if (!operation) {
33109
33678
  const root = transactionRoot(projectRoot);
33110
- if (existsSync24(root)) {
33679
+ if (existsSync25(root)) {
33111
33680
  rmSync9(root, { recursive: true, force: true });
33112
33681
  syncDirectory2(projectRoot);
33113
33682
  }
@@ -33123,7 +33692,7 @@ function commitKxmInitTransaction(projectRoot, schemasDir) {
33123
33692
  fail3("init_transaction_not_verified", TRANSACTION_NAME, "initialization transaction cannot commit before installed state is verified");
33124
33693
  }
33125
33694
  const transaction = transactionRoot(projectRoot);
33126
- const retired = join28(projectRoot, `${TRANSACTION_NAME}-cleanup-${operation.operationId}-${randomUUID10()}`);
33695
+ const retired = join29(projectRoot, `${TRANSACTION_NAME}-cleanup-${operation.operationId}-${randomUUID10()}`);
33127
33696
  renameSync7(transaction, retired);
33128
33697
  syncDirectory2(projectRoot);
33129
33698
  rmSync9(retired, { recursive: true, force: true });
@@ -33232,7 +33801,7 @@ function validateBindingIdentities(bundle, persisted, explicit) {
33232
33801
  }
33233
33802
  function initializeKxmProjectAtGitRoot(start, gitRoot, options, mutationLock) {
33234
33803
  const storeOptions = bindingStoreOptions(options);
33235
- const persisted = existsSync25(join29(gitRoot, ".kxm", "project.yaml")) ? readKxmLocalBindings(gitRoot, storeOptions) : void 0;
33804
+ const persisted = existsSync26(join30(gitRoot, ".kxm", "project.yaml")) ? readKxmLocalBindings(gitRoot, storeOptions) : void 0;
33236
33805
  const repositoryBindings = Object.fromEntries([
33237
33806
  ...Object.entries(persisted?.repositories ?? {}),
33238
33807
  ...Object.entries(options.repositoryBindings ?? {})
@@ -33420,7 +33989,7 @@ function initializeKxmProjectAtGitRoot(start, gitRoot, options, mutationLock) {
33420
33989
  const files = [...rendered.files.keys()];
33421
33990
  if (options.dryRun) return { action: "planned", plan, projectRoot: gitRoot, files };
33422
33991
  if (!mutationLock) throw new Error("project mutation lock is required to create configuration");
33423
- if (existsSync25(join29(gitRoot, ".kxm"))) {
33992
+ if (existsSync26(join30(gitRoot, ".kxm"))) {
33424
33993
  throw new KxmConfigError([initIssue("workspace_changed", ".kxm", "workspace changed after planning; existing .kxm state was not overwritten")]);
33425
33994
  }
33426
33995
  const created = prepareAndApplyKxmCreate(gitRoot, rendered, transactionOptions);
@@ -33457,7 +34026,7 @@ function hubSourceReason(status) {
33457
34026
  if (status === 404) return "hub_not_found";
33458
34027
  return `hub_http_${status}`;
33459
34028
  }
33460
- function asString(value) {
34029
+ function asString2(value) {
33461
34030
  return typeof value === "string" && value.length > 0 ? value : void 0;
33462
34031
  }
33463
34032
  function isObject(value) {
@@ -33505,37 +34074,37 @@ async function assembleTenantStatus(input) {
33505
34074
  project: body.project,
33506
34075
  fetchedAt: body.fetchedAt,
33507
34076
  agents: body.agents.filter(isObject).map((agent) => ({
33508
- id: asString(agent.id) ?? "",
33509
- name: asString(agent.name) ?? "",
34077
+ id: asString2(agent.id) ?? "",
34078
+ name: asString2(agent.name) ?? "",
33510
34079
  online: agent.online === true
33511
34080
  })),
33512
34081
  openMessageTotal: body.openMessageTotal,
33513
34082
  runTotal: body.runTotal,
33514
34083
  runs: runRows.map((run) => {
33515
34084
  const stages = Array.isArray(run.stages) ? run.stages.filter(isObject).map((stage) => ({
33516
- id: asString(stage.id) ?? "",
33517
- status: asString(stage.status) ?? "unknown",
34085
+ id: asString2(stage.id) ?? "",
34086
+ status: asString2(stage.status) ?? "unknown",
33518
34087
  ...typeof stage.attempts === "number" ? { attempts: stage.attempts } : {}
33519
34088
  })) : void 0;
33520
34089
  const done = stages?.filter((stage) => stage.status === "passed" || stage.status === "failed" || stage.status === "warning").length;
33521
34090
  return {
33522
- id: asString(run.id) ?? "",
33523
- status: asString(run.status) ?? "unknown",
33524
- definitionId: asString(run.definitionId) ?? "",
33525
- ...asString(run.currentStage) ? { currentStage: asString(run.currentStage) } : {},
33526
- ...asString(run.targetAgentName) ? { targetAgentName: asString(run.targetAgentName) } : {},
33527
- ...asString(run.updatedAt) ? { updatedAt: asString(run.updatedAt) } : {},
34091
+ id: asString2(run.id) ?? "",
34092
+ status: asString2(run.status) ?? "unknown",
34093
+ definitionId: asString2(run.definitionId) ?? "",
34094
+ ...asString2(run.currentStage) ? { currentStage: asString2(run.currentStage) } : {},
34095
+ ...asString2(run.targetAgentName) ? { targetAgentName: asString2(run.targetAgentName) } : {},
34096
+ ...asString2(run.updatedAt) ? { updatedAt: asString2(run.updatedAt) } : {},
33528
34097
  ...stages !== void 0 ? { progress: { done: done ?? 0, total: stages.length }, stages } : {},
33529
34098
  source: "hub-projection"
33530
34099
  };
33531
34100
  }),
33532
34101
  plans: (Array.isArray(body.plans) ? body.plans : []).filter(isObject).map((plan) => ({
33533
- id: asString(plan.id) ?? "",
33534
- runId: asString(plan.runId) ?? "",
33535
- summary: asString(plan.summary) ?? "",
33536
- createdAt: asString(plan.createdAt) ?? "",
33537
- ...asString(plan.stageId) ? { stageId: asString(plan.stageId) } : {},
33538
- ...asString(plan.severity) ? { severity: asString(plan.severity) } : {}
34102
+ id: asString2(plan.id) ?? "",
34103
+ runId: asString2(plan.runId) ?? "",
34104
+ summary: asString2(plan.summary) ?? "",
34105
+ createdAt: asString2(plan.createdAt) ?? "",
34106
+ ...asString2(plan.stageId) ? { stageId: asString2(plan.stageId) } : {},
34107
+ ...asString2(plan.severity) ? { severity: asString2(plan.severity) } : {}
33539
34108
  }))
33540
34109
  };
33541
34110
  return { state: "ok", observedAt: attemptedAt, value };
@@ -33676,6 +34245,11 @@ async function cmdKxmInit(runtime, options, postHooks) {
33676
34245
  localStateRoot: kxmUserStateRoot({ env: runtime.env }),
33677
34246
  dryRun: runtime.dryRun
33678
34247
  });
34248
+ const starterGuidance = initialized.action === "created" || initialized.action === "planned" && initialized.plan.mode === "create" ? [
34249
+ "defaultHarness: pi and the npm test gate are generic starter settings, not repository detection.",
34250
+ "For Claude, set defaultHarness: claude in .kxm/project.yaml, update any explicit harness overrides in .kxm/agents/*.yaml, and configure compatible agent models.",
34251
+ "For .NET or other non-npm repositories, set gates.test.argv in .kxm/gates.yaml to the repository's actual test runner before driving a workflow."
34252
+ ] : [];
33679
34253
  const payload = {
33680
34254
  ok: initialized.action !== "planned" || runtime.dryRun,
33681
34255
  command: "init",
@@ -33688,10 +34262,11 @@ async function cmdKxmInit(runtime, options, postHooks) {
33688
34262
  ...initialized.repairPlan === void 0 ? {} : { repairPlan: initialized.repairPlan },
33689
34263
  ...initialized.resumePending === void 0 ? {} : { resumePending: initialized.resumePending },
33690
34264
  ...initialized.transactionKind === void 0 ? {} : { transactionKind: initialized.transactionKind },
33691
- plannedOnly: initialized.action === "planned"
34265
+ plannedOnly: initialized.action === "planned",
34266
+ ...starterGuidance.length > 0 ? { guidance: starterGuidance } : {}
33692
34267
  };
33693
34268
  const finishInit = (code, text) => {
33694
- print(runtime.io, runtime.json, payload, text);
34269
+ print(runtime.io, runtime.json, payload, [text, ...starterGuidance].join("\n"));
33695
34270
  return code;
33696
34271
  };
33697
34272
  if (initialized.action === "created") {
@@ -33745,7 +34320,7 @@ async function cmdBackup(runtime, options) {
33745
34320
  projectRoot: backupProjectRoot(runtime),
33746
34321
  env: runtime.env,
33747
34322
  ...options.allProjects === true ? { allProjects: true } : {},
33748
- ...options.out ? { outDir: resolve23(runtime.cwd, options.out) } : {}
34323
+ ...options.out ? { outDir: resolve22(runtime.cwd, options.out) } : {}
33749
34324
  };
33750
34325
  if (runtime.dryRun) {
33751
34326
  const plan = planBackup(backupOptions);
@@ -33753,8 +34328,8 @@ async function cmdBackup(runtime, options) {
33753
34328
  runtime,
33754
34329
  { command: "backup", scope: plan.scope, outDir: plan.outDir, stores: plan.stores, files: plan.files },
33755
34330
  [
33756
- ...[...plan.stores, ...plan.files].map((entry) => ({ action: "write", target: join30(plan.outDir, entry.backupFile) })),
33757
- { action: "write", target: join30(plan.outDir, "manifest.json") }
34331
+ ...[...plan.stores, ...plan.files].map((entry) => ({ action: "write", target: join31(plan.outDir, entry.backupFile) })),
34332
+ { action: "write", target: join31(plan.outDir, "manifest.json") }
33758
34333
  ],
33759
34334
  `back up ${plan.stores.length} store(s) and ${plan.files.length} file(s) (${plan.scope} scope) to ${plan.outDir} (sources are not opened, so their WAL is not checkpointed)`
33760
34335
  );
@@ -33773,7 +34348,7 @@ async function cmdBackup(runtime, options) {
33773
34348
  complete ? `Created SQLite backup with ${manifest.stores.length} store(s) (${manifest.scope ?? "project"} scope):` : `Backup is incomplete (${manifest.omitted?.length ?? 0} omitted); not ok:`,
33774
34349
  ...manifest.stores.map((s) => ` - ${s.storeId}: ${s.sourcePath} -> ${s.backupFile} (schema v${s.schemaVersion}, ${s.bytes} bytes, sha256 ${s.sha256.slice(0, 12)}...)`),
33775
34350
  ...(manifest.omitted ?? []).map((id) => ` - omitted ${id}`),
33776
- `Manifest: ${join30(outDir, "manifest.json")}`
34351
+ `Manifest: ${join31(outDir, "manifest.json")}`
33777
34352
  ].join("\n");
33778
34353
  print(runtime.io, runtime.json, payload, summary);
33779
34354
  return complete ? 0 : 1;
@@ -33807,7 +34382,7 @@ function assertRestoreTargetsStopped(runtime, plan) {
33807
34382
  }
33808
34383
  for (const store of plan.stores) {
33809
34384
  if (store.storeId !== "hub-store") continue;
33810
- const claim = readLiveHubClaim(dirname18(store.targetPath));
34385
+ const claim = readLiveHubClaim(dirname17(store.targetPath));
33811
34386
  if (claim) {
33812
34387
  throw databaseError(
33813
34388
  "restore_hub_running",
@@ -33819,7 +34394,7 @@ function assertRestoreTargetsStopped(runtime, plan) {
33819
34394
  }
33820
34395
  async function cmdRestore(runtime, manifestArg, options = {}) {
33821
34396
  try {
33822
- const plan = planRestore(resolve23(runtime.cwd, manifestArg), {
34397
+ const plan = planRestore(resolve22(runtime.cwd, manifestArg), {
33823
34398
  projectRoot: backupProjectRoot(runtime),
33824
34399
  env: runtime.env,
33825
34400
  ...options.allProjects === true ? { allProjects: true } : {}
@@ -33839,7 +34414,7 @@ async function cmdRestore(runtime, manifestArg, options = {}) {
33839
34414
  [
33840
34415
  ...plan.stores.flatMap((store) => [
33841
34416
  { action: "write", target: store.targetPath },
33842
- ...[`${store.targetPath}-wal`, `${store.targetPath}-shm`].filter((file) => existsSync26(file)).map((file) => ({ action: "delete", target: file }))
34417
+ ...[`${store.targetPath}-wal`, `${store.targetPath}-shm`].filter((file) => existsSync27(file)).map((file) => ({ action: "delete", target: file }))
33843
34418
  ]),
33844
34419
  ...plan.files.map((file) => ({ action: "write", target: file.targetPath }))
33845
34420
  ],
@@ -33920,11 +34495,17 @@ async function readSupervisor(runtime, command) {
33920
34495
  const attached = await attachKxmSupervisor({ env: runtime.env });
33921
34496
  return attached ?? refuseDryRun(runtime.io, runtime.json, command, "the Runtime supervisor is not running and --dry-run will not start it");
33922
34497
  }
33923
- var RUN_ENGINE_PHASE = "pre-3a";
33924
- function runEngineNotice(runId) {
33925
- return `drive it model-free: kxm runs drive ${runId} --simulated --wait (or cancel: kxm runs cancel ${runId})`;
34498
+ function runEngineNotice(runId, defaultHarness, prerequisites) {
34499
+ return [
34500
+ `No steps executed. Project default harness: ${defaultHarness}; per-agent harness settings take precedence.`,
34501
+ ...prerequisites.map((item) => `Live prerequisite${item.stepId ? ` (${item.stepId})` : ""}: ${item.detail}`),
34502
+ "Live execution uses one-shot harness calls; no hub or Pi worker is required. Check installation/authentication with kxm harness list.",
34503
+ `${prerequisites.length > 0 ? "Resolve the prerequisites above, then execute" : "Execute"}: kxm runs drive ${runId} --wait`,
34504
+ `Inspect: kxm runs status ${runId} --json; receipt: kxm runs receipt ${runId} --json; cancel: kxm runs cancel ${runId}`,
34505
+ "These are local Runtime runs, not webhook workflows; use kxm runs, not kxm workflow get."
34506
+ ].join("\n");
33926
34507
  }
33927
- function resolveKxmRunTarget(runtime, workflow) {
34508
+ function resolveKxmRunTarget(runtime, workflow, forTask = false) {
33928
34509
  if (runtime.workspaceFlag !== void 0) {
33929
34510
  print(runtime.io, runtime.json, {
33930
34511
  ok: false,
@@ -33933,7 +34514,7 @@ function resolveKxmRunTarget(runtime, workflow) {
33933
34514
  }, "kxm run discovers the authoritative project from the current directory; --workspace is not supported");
33934
34515
  return 2;
33935
34516
  }
33936
- if (!workflow) {
34517
+ if (!workflow && !forTask) {
33937
34518
  print(runtime.io, runtime.json, { ok: false, command: "run", error: "workflow_required" }, "usage: kxm run <workflow> [prompt]");
33938
34519
  return 2;
33939
34520
  }
@@ -33943,15 +34524,30 @@ function resolveKxmRunTarget(runtime, workflow) {
33943
34524
  return 1;
33944
34525
  }
33945
34526
  const bundle = loadKxmProject(projectRoot, {});
33946
- if (!bundle.workflows.has(workflow)) {
33947
- print(runtime.io, runtime.json, { ok: false, command: "run", error: "run_workflow_unknown", workflow }, `workflow ${workflow} does not exist in this project`);
34527
+ const workflowId = workflow ?? String(bundle.project.value.defaultWorkflow ?? "default");
34528
+ if (!bundle.workflows.has(workflowId)) {
34529
+ print(runtime.io, runtime.json, { ok: false, command: "run", error: "run_workflow_unknown", workflow: workflowId }, `workflow ${workflowId} does not exist in this project`);
34530
+ return 1;
34531
+ }
34532
+ const defaultHarness = String(bundle.project.value.defaultHarness ?? "pi");
34533
+ const prerequisites = kxmLiveRunPrerequisites(bundle, workflowId, projectRoot);
34534
+ if (forTask && prerequisites.length > 0) {
34535
+ print(runtime.io, runtime.json, {
34536
+ ok: false,
34537
+ command: "task run",
34538
+ error: "run_execution_unavailable",
34539
+ workflowId,
34540
+ defaultHarness,
34541
+ execution: { status: "not_started", mode: "live", prerequisites }
34542
+ }, `task run refused; no run created or task changed (default harness: ${defaultHarness}).
34543
+ ${prerequisites.map((item) => `${item.stepId ?? workflowId}: ${item.detail}`).join("\n")}`);
33948
34544
  return 1;
33949
34545
  }
33950
- return { projectRoot, workflowId: workflow, configRevision: bundle.configRevision };
34546
+ return { projectRoot, workflowId, configRevision: bundle.configRevision, defaultHarness, prerequisites };
33951
34547
  }
33952
- async function cmdKxmRun(runtime, workflow, promptParts) {
34548
+ async function cmdKxmRun(runtime, workflow, promptParts, forTask = false) {
33953
34549
  try {
33954
- const target = resolveKxmRunTarget(runtime, workflow);
34550
+ const target = resolveKxmRunTarget(runtime, workflow, forTask);
33955
34551
  if (typeof target === "number") return target;
33956
34552
  const { projectRoot } = target;
33957
34553
  if (runtime.dryRun) {
@@ -33974,12 +34570,24 @@ async function cmdKxmRun(runtime, workflow, promptParts) {
33974
34570
  print(runtime.io, runtime.json, {
33975
34571
  ok: true,
33976
34572
  command: "run",
33977
- phase: RUN_ENGINE_PHASE,
34573
+ execution: {
34574
+ status: "not_started",
34575
+ mode: "live",
34576
+ defaultHarness: target.defaultHarness,
34577
+ authentication: "not_checked",
34578
+ prerequisites: target.prerequisites,
34579
+ nextSteps: {
34580
+ harnesses: "kxm harness list",
34581
+ drive: `kxm runs drive ${run.runId} --wait`,
34582
+ status: `kxm runs status ${run.runId} --json`,
34583
+ receipt: `kxm runs receipt ${run.runId} --json`
34584
+ }
34585
+ },
33978
34586
  idempotent: acceptance.idempotent === true,
33979
34587
  run,
33980
34588
  supervisor: { runtimeId: supervisor.runtimeId, port: supervisor.port, started: supervisor.started }
33981
34589
  }, `run ${run.status}: ${run.runId} (home ${run.homeRuntimeId.slice(0, 12)}\u2026, config ${run.configRevision.slice(0, 19)}\u2026)
33982
- ${runEngineNotice(run.runId)}`);
34590
+ ${runEngineNotice(run.runId, target.defaultHarness, target.prerequisites)}`);
33983
34591
  return 0;
33984
34592
  } catch (error) {
33985
34593
  if (error instanceof KxmConfigError) {
@@ -34063,7 +34671,7 @@ function parseDriveWaitTimeoutMs(raw) {
34063
34671
  return value;
34064
34672
  }
34065
34673
  function sleepMs(ms) {
34066
- return new Promise((resolve35) => setTimeout(resolve35, ms));
34674
+ return new Promise((resolve34) => setTimeout(resolve34, ms));
34067
34675
  }
34068
34676
  function driveWaitCompleted(drive) {
34069
34677
  return drive?.verified === true && drive.receipt?.settlement?.kind === "terminal" && drive.receipt.settlement.status === "completed";
@@ -34260,9 +34868,20 @@ async function cmdTenantStatus(runtime) {
34260
34868
  }
34261
34869
  }
34262
34870
  async function cmdHarnessList(runtime) {
34263
- const inventory = await probeHarnessesAsync({ env: runtime.env });
34264
- print(runtime.io, runtime.json, { ok: true, command: "harness list", ...inventory }, formatHarnessInventory(inventory));
34265
- return 0;
34871
+ try {
34872
+ const projectRoot = discoverKxmProjectRoot(runtime.cwd);
34873
+ const defaultHarness = projectRoot ? String(loadKxmProject(projectRoot).project.value.defaultHarness ?? "pi") : void 0;
34874
+ const inventory = await probeHarnessesAsync({ env: runtime.env, defaultHarness });
34875
+ print(runtime.io, runtime.json, { ok: true, command: "harness list", ...inventory }, formatHarnessInventory(inventory));
34876
+ return 0;
34877
+ } catch (error) {
34878
+ if (error instanceof KxmConfigError) {
34879
+ print(runtime.io, runtime.json, { ok: false, command: "harness list", error: "harness_list_failed", issues: error.issues }, `harness list failed: ${error.message}`);
34880
+ return 1;
34881
+ }
34882
+ print(runtime.io, runtime.json, { ok: false, command: "harness list", error: "harness_list_io_failed" }, "harness list failed because a local operation did not complete");
34883
+ return 1;
34884
+ }
34266
34885
  }
34267
34886
  async function selectInventoryModel(runtime, requested) {
34268
34887
  const models = listInventoryModels(runtime.dirs.workdir);
@@ -34270,7 +34889,7 @@ async function selectInventoryModel(runtime, requested) {
34270
34889
  if (runtime.json || !process.stdin.isTTY || models.length === 0) return void 0;
34271
34890
  runtime.io.stdout(models.slice(0, 100).map((model, index2) => `${index2 + 1}. ${model}`).join("\n") + "\nSelect model number: ");
34272
34891
  const rl = createInterface2({ input: process.stdin, output: process.stderr });
34273
- const answer = await new Promise((resolve35) => rl.once("line", resolve35));
34892
+ const answer = await new Promise((resolve34) => rl.once("line", resolve34));
34274
34893
  rl.close();
34275
34894
  const index = Number.parseInt(answer.trim(), 10) - 1;
34276
34895
  return Number.isInteger(index) && index >= 0 && index < models.length ? models[index] : void 0;
@@ -34296,7 +34915,7 @@ async function cmdModelsScreen(runtime) {
34296
34915
  return 2;
34297
34916
  }
34298
34917
  const rl = createInterface2({ input: process.stdin, output: process.stderr });
34299
- const ask = (q2) => new Promise((resolve35) => rl.question(q2, resolve35));
34918
+ const ask = (q2) => new Promise((resolve34) => rl.question(q2, resolve34));
34300
34919
  try {
34301
34920
  while (true) {
34302
34921
  const policy = loadRoutePolicy(runtime.dirs.workdir);
@@ -34473,39 +35092,64 @@ async function cmdKxmRunReceipt(runtime, runId, options = {}) {
34473
35092
  }
34474
35093
 
34475
35094
  // plugins/kxm/src/cli/tasks.ts
34476
- async function cmdSuggest(runtime, promptParts) {
35095
+ async function cmdSuggest(runtime, promptParts, probeHarnesses2 = probeHarnessesAsync) {
34477
35096
  try {
34478
35097
  const prompt = promptParts.join(" ").trim();
34479
35098
  if (!prompt) {
34480
- runtime.io.stderr("prompt must be non-empty\n");
35099
+ print(runtime.io, runtime.json, { ok: false, command: "suggest", error: "prompt_required" }, "prompt must be non-empty");
34481
35100
  return 2;
34482
35101
  }
34483
- const inventory = await probeHarnessesAsync({ env: runtime.env });
34484
- const availableHarnesses = inventory.harnesses.map((h) => ({
34485
- harness: h.id,
34486
- auth: h.authenticated === true ? "authenticated" : "unauthenticated"
34487
- }));
34488
- const suggestion = suggestWorkflowAndRoles(prompt, { availableHarnesses });
35102
+ const inventory = runtime.dryRun ? void 0 : await probeHarnesses2({ env: runtime.env });
35103
+ const suggestion = suggestWorkflowAndRoles(prompt, { availableHarnesses: inventory?.harnesses });
35104
+ if (suggestion.execution.supported && [".yaml", ".yml"].some((extension) => existsSync28(join32(runtime.cwd, ".kxm", "workflows", `${suggestion.workflowId}${extension}`)))) {
35105
+ suggestion.roles = [];
35106
+ suggestion.execution = {
35107
+ supported: false,
35108
+ error: "workflow_already_exists",
35109
+ reason: `Workflow ${suggestion.workflowId} already exists; its agents and permissions may differ from the recommended template.`,
35110
+ nextSteps: ["Review the existing definition and every agent's harness/model before running it, or install the recommended template under a fresh flat ID. No existing workflow is overwritten or recommended for execution."]
35111
+ };
35112
+ }
35113
+ const execution = suggestion.execution;
34489
35114
  const text = [
34490
35115
  `Suggested Workflow: ${suggestion.workflowId} (${suggestion.area})`,
35116
+ `Template: ${suggestion.template}`,
34491
35117
  `Confidence: ${(suggestion.confidence * 100).toFixed(0)}%`,
34492
35118
  `Reasons: ${suggestion.reasons.join("; ")}`,
34493
35119
  `Suggested Skills: ${suggestion.suggestedSkills.join(", ") || "none"}`,
34494
- `Roles:`,
34495
- ` Planner: ${suggestion.roles.planner.harness} (${suggestion.roles.planner.model})`,
34496
- ` Writer: ${suggestion.roles.writer.harness} (${suggestion.roles.writer.model})`,
34497
- ` Critics: ${suggestion.roles.critics.map((c) => `${c.harness}:${c.model}`).join(", ")}`,
34498
- ` Verifier: ${suggestion.roles.verifier.command}`,
34499
- ``,
34500
- `Execute with:`,
34501
- ` ${suggestion.suggestedCommand}`
35120
+ "",
35121
+ "Install definition only (requires kxm init; does not execute):",
35122
+ ` ${suggestion.suggestedCommand}`,
35123
+ "",
35124
+ ...execution.supported ? [
35125
+ "Suggested agent routing (not applied):",
35126
+ ...suggestion.roles.map((role) => ` ${role.agent}: ${role.harness} (${role.role}; use a configured compatible model)`),
35127
+ "Prerequisites:",
35128
+ ...execution.prerequisites.map((step) => ` ${step}`),
35129
+ `Create a run only (${execution.shell}; does not execute steps):`,
35130
+ ` ${execution.createCommand}`,
35131
+ "Then drive the returned run ID with live calls and inspect its result:",
35132
+ ` ${execution.driveCommand}`,
35133
+ ` ${execution.statusCommand}`,
35134
+ ` ${execution.receiptCommand}`
35135
+ ] : [
35136
+ `Execution unavailable: ${execution.reason}`,
35137
+ ...execution.nextSteps.map((step) => ` ${step}`),
35138
+ ...runtime.dryRun ? ["Harness authentication was not probed during --dry-run."] : []
35139
+ ]
34502
35140
  ].join("\n");
34503
- print(runtime.io, runtime.json, { ok: true, command: "suggest", prompt, ...suggestion }, text);
34504
- return 0;
35141
+ print(runtime.io, runtime.json, {
35142
+ ok: execution.supported,
35143
+ command: "suggest",
35144
+ prompt,
35145
+ ...suggestion,
35146
+ ...runtime.dryRun ? { dryRun: true } : {},
35147
+ ...!execution.supported ? { error: execution.error } : {}
35148
+ }, text);
35149
+ return execution.supported ? 0 : 1;
34505
35150
  } catch (error) {
34506
35151
  const message = error instanceof Error ? error.message : String(error);
34507
- runtime.io.stderr(`suggest failed: ${message}
34508
- `);
35152
+ print(runtime.io, runtime.json, { ok: false, command: "suggest", error: "suggest_failed", detail: message }, `suggest failed: ${message}`);
34509
35153
  return 1;
34510
35154
  }
34511
35155
  }
@@ -34625,27 +35269,21 @@ async function cmdTaskRun(runtime, taskId) {
34625
35269
  `);
34626
35270
  return 1;
34627
35271
  }
34628
- const workflow = task.assignedWorkflow ?? "default";
35272
+ const workflow = task.assignedWorkflow;
34629
35273
  if (runtime.dryRun) {
34630
- const target = resolveKxmRunTarget(runtime, workflow);
35274
+ const target = resolveKxmRunTarget(runtime, workflow, true);
34631
35275
  if (typeof target === "number") return target;
34632
- const started = updateTaskStatus(runtime.cwd, taskId, "in_progress", { dryRun: true });
34633
35276
  printPlan(
34634
35277
  runtime,
34635
- { command: "task run", taskId, ...target, status: started.status },
35278
+ { command: "task run", taskId, ...target, status: task.status, execution: { status: "not_started", mode: "live" } },
34636
35279
  [
34637
- { action: "request", target: "POST kxm-runtime /v1/runs (starts the Runtime supervisor if it is not running)" },
34638
- { action: "write", target: taskFilePath(runtime.cwd, taskId) }
35280
+ { action: "request", target: "POST kxm-runtime /v1/runs (starts the Runtime supervisor if it is not running)" }
34639
35281
  ],
34640
- `run workflow ${workflow} for task ${taskId}, then mark it ${started.status}`
35282
+ `create workflow ${target.workflowId} for task ${taskId}; task status stays ${task.status} until work actually starts`
34641
35283
  );
34642
35284
  return 0;
34643
35285
  }
34644
- const exitCode = await cmdKxmRun(runtime, workflow, [task.objective]);
34645
- if (exitCode === 0) {
34646
- updateTaskStatus(runtime.cwd, taskId, "in_progress");
34647
- }
34648
- return exitCode;
35286
+ return await cmdKxmRun(runtime, workflow, [task.objective], true);
34649
35287
  } catch (error) {
34650
35288
  const message = error instanceof Error ? error.message : String(error);
34651
35289
  runtime.io.stderr(`task run failed: ${message}
@@ -34679,12 +35317,12 @@ async function cmdStudioLayout(runtime, workflowPath) {
34679
35317
  const projectRoot = discoverKxmProjectRoot(runtime.cwd) ?? runtime.cwd;
34680
35318
  let filePath = workflowPath;
34681
35319
  if (!filePath) {
34682
- filePath = resolve24(projectRoot, ".kxm", "workflows", "default.yaml");
35320
+ filePath = resolve23(projectRoot, ".kxm", "workflows", "default.yaml");
34683
35321
  }
34684
35322
  let yamlContent;
34685
35323
  let workflowId = "default";
34686
- if (existsSync27(filePath)) {
34687
- yamlContent = readFileSync26(filePath, "utf8");
35324
+ if (existsSync28(filePath)) {
35325
+ yamlContent = readFileSync27(filePath, "utf8");
34688
35326
  } else {
34689
35327
  yamlContent = `schema: kxm.workflow.v1
34690
35328
  description: Feature implementation workflow
@@ -34757,15 +35395,15 @@ async function cmdStudioServe(runtime, options) {
34757
35395
  sessionToken,
34758
35396
  planProvider: () => {
34759
35397
  try {
34760
- const wfDir = join31(runtime.cwd, ".kxm", "workflows");
34761
- const defaultPath = join31(wfDir, "default.yaml");
34762
- let targetPath = existsSync27(defaultPath) ? defaultPath : void 0;
34763
- if (!targetPath && existsSync27(wfDir)) {
35398
+ const wfDir = join32(runtime.cwd, ".kxm", "workflows");
35399
+ const defaultPath = join32(wfDir, "default.yaml");
35400
+ let targetPath = existsSync28(defaultPath) ? defaultPath : void 0;
35401
+ if (!targetPath && existsSync28(wfDir)) {
34764
35402
  const yml = readdirSync10(wfDir).find((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
34765
- if (yml) targetPath = join31(wfDir, yml);
35403
+ if (yml) targetPath = join32(wfDir, yml);
34766
35404
  }
34767
- if (targetPath && existsSync27(targetPath)) {
34768
- const raw = readFileSync26(targetPath, "utf8");
35405
+ if (targetPath && existsSync28(targetPath)) {
35406
+ const raw = readFileSync27(targetPath, "utf8");
34769
35407
  const parsed = (0, import_yaml15.parse)(raw);
34770
35408
  const wfId = basename7(targetPath).replace(/\.(yaml|yml)$/, "");
34771
35409
  return compileKxmWorkflow({ id: wfId, value: parsed });
@@ -34815,16 +35453,16 @@ Press Ctrl+C to stop.
34815
35453
  }
34816
35454
 
34817
35455
  // plugins/kxm/src/cli/context-skills.ts
34818
- import { readFileSync as readFileSync27 } from "node:fs";
34819
- import { join as join33, relative as relative6, resolve as resolve25 } from "node:path";
35456
+ import { readFileSync as readFileSync28 } from "node:fs";
35457
+ import { join as join34, relative as relative6, resolve as resolve24 } from "node:path";
34820
35458
 
34821
35459
  // plugins/kxm/src/wiki.ts
34822
35460
  import { mkdirSync as mkdirSync23, writeFileSync as writeFileSync21 } from "node:fs";
34823
- import { join as join32 } from "node:path";
35461
+ import { join as join33 } from "node:path";
34824
35462
  function writeCompiledWiki(root, wiki) {
34825
35463
  const written = [];
34826
35464
  for (const [relativePath, content] of [...wiki.pages].sort(([left], [right]) => left.localeCompare(right))) {
34827
- const absolute = join32(root, relativePath);
35465
+ const absolute = join33(root, relativePath);
34828
35466
  mkdirSync23(absolute.slice(0, absolute.lastIndexOf("/")), { recursive: true });
34829
35467
  writeFileSync21(absolute, content);
34830
35468
  written.push(relativePath);
@@ -34976,7 +35614,7 @@ async function cmdContextWikiCompile(runtime, project, options) {
34976
35614
  printPlan(
34977
35615
  runtime,
34978
35616
  { command: "context wiki-compile", project, pages: compiled.audit.pages, openContradictions: compiled.audit.contradictions, outDir },
34979
- compiled.pages.map((page) => ({ action: "write", target: resolve25(outDir, page.path) })),
35617
+ compiled.pages.map((page) => ({ action: "write", target: resolve24(outDir, page.path) })),
34980
35618
  `write ${compiled.pages.length} compiled wiki page(s) to ${outDir}`
34981
35619
  );
34982
35620
  return 0;
@@ -35011,7 +35649,7 @@ function skillStateFromFlag(value) {
35011
35649
  throw new Error(`invalid skill state ${value}`);
35012
35650
  }
35013
35651
  function skillsRoot(runtime) {
35014
- return join33(runtime.dirs.workdir, ".kxm", "skills");
35652
+ return join34(runtime.dirs.workdir, ".kxm", "skills");
35015
35653
  }
35016
35654
  function csv(value) {
35017
35655
  if (!value) return void 0;
@@ -35028,7 +35666,7 @@ async function cmdSkillsCreate(runtime, options) {
35028
35666
  const metadata = lifecycle.create({
35029
35667
  name: options.name,
35030
35668
  description: options.description ?? "",
35031
- content: readFileSync27(options.file, "utf8"),
35669
+ content: readFileSync28(options.file, "utf8"),
35032
35670
  createdBy: options.createdBy,
35033
35671
  sources: {
35034
35672
  runIds: csv(options.run) ?? [],
@@ -35190,7 +35828,7 @@ async function cmdMemorySync(runtime) {
35190
35828
  printPlan(
35191
35829
  runtime,
35192
35830
  { command: "memory sync", ...result },
35193
- result.updated.map((file) => ({ action: "write", target: join33(runtime.cwd, file) })),
35831
+ result.updated.map((file) => ({ action: "write", target: join34(runtime.cwd, file) })),
35194
35832
  "regenerate the project memory block in the instruction files this project already has"
35195
35833
  );
35196
35834
  return 0;
@@ -35218,8 +35856,8 @@ ${lines.join("\n")}`
35218
35856
 
35219
35857
  // plugins/kxm/src/cli/hub.ts
35220
35858
  import { randomUUID as randomUUID13 } from "node:crypto";
35221
- import { existsSync as existsSync33, mkdirSync as mkdirSync27, readFileSync as readFileSync33, readdirSync as readdirSync12, rmSync as rmSync11, writeFileSync as writeFileSync24 } from "node:fs";
35222
- import { join as join43, resolve as resolve28 } from "node:path";
35859
+ import { existsSync as existsSync34, mkdirSync as mkdirSync27, readFileSync as readFileSync34, readdirSync as readdirSync12, rmSync as rmSync11, writeFileSync as writeFileSync24 } from "node:fs";
35860
+ import { join as join44, resolve as resolve27 } from "node:path";
35223
35861
 
35224
35862
  // node_modules/marked/lib/marked.esm.js
35225
35863
  function M() {
@@ -39381,10 +40019,10 @@ var TuiBase = class _TuiBase extends Container {
39381
40019
  * @returns Promise containing the parsed RGB color, or undefined if it times out or fails to parse.
39382
40020
  */
39383
40021
  queryTerminalBackgroundColor({ timeoutMs }) {
39384
- return new Promise((resolve35) => {
40022
+ return new Promise((resolve34) => {
39385
40023
  const query = {
39386
40024
  settled: false,
39387
- resolve: resolve35,
40025
+ resolve: resolve34,
39388
40026
  timer: void 0
39389
40027
  };
39390
40028
  query.timer = setTimeout(() => {
@@ -39407,7 +40045,7 @@ var TuiBase = class _TuiBase extends Container {
39407
40045
  * `CSI ? 997 ; 1 n` for dark or `CSI ? 997 ; 2 n` for light.
39408
40046
  */
39409
40047
  queryTerminalColorScheme({ timeoutMs }) {
39410
- return new Promise((resolve35) => {
40048
+ return new Promise((resolve34) => {
39411
40049
  let settled = false;
39412
40050
  let timer;
39413
40051
  let unsubscribe = () => {
@@ -39421,7 +40059,7 @@ var TuiBase = class _TuiBase extends Container {
39421
40059
  timer = void 0;
39422
40060
  }
39423
40061
  unsubscribe();
39424
- resolve35(scheme);
40062
+ resolve34(scheme);
39425
40063
  };
39426
40064
  unsubscribe = this.onTerminalColorSchemeChange(settle);
39427
40065
  timer = setTimeout(() => settle(void 0), timeoutMs);
@@ -41152,19 +41790,19 @@ import * as path2 from "node:path";
41152
41790
 
41153
41791
  // node_modules/@earendil-works/pi-tui/dist/native-module-path.js
41154
41792
  import { createRequire as createRequire2 } from "node:module";
41155
- import { dirname as dirname19, join as join34 } from "node:path";
41793
+ import { dirname as dirname18, join as join35 } from "node:path";
41156
41794
  import { fileURLToPath as fileURLToPath2 } from "node:url";
41157
41795
  var moduleRequire = createRequire2(import.meta.url);
41158
41796
  var TUI_PACKAGE_NAME = "@earendil-works/pi-tui";
41159
41797
  function getNativeModuleCandidates(nativePath, options = {}) {
41160
- const moduleDir = dirname19(fileURLToPath2(options.moduleUrl ?? import.meta.url));
41798
+ const moduleDir = dirname18(fileURLToPath2(options.moduleUrl ?? import.meta.url));
41161
41799
  const candidates = [];
41162
41800
  try {
41163
41801
  const packageEntry = (options.resolvePackage ?? moduleRequire.resolve)(TUI_PACKAGE_NAME);
41164
- candidates.push(join34(dirname19(packageEntry), "..", nativePath));
41802
+ candidates.push(join35(dirname18(packageEntry), "..", nativePath));
41165
41803
  } catch {
41166
41804
  }
41167
- candidates.push(join34(moduleDir, "..", nativePath), join34(moduleDir, nativePath), join34(dirname19(options.execPath ?? process.execPath), nativePath));
41805
+ candidates.push(join35(moduleDir, "..", nativePath), join35(moduleDir, nativePath), join35(dirname18(options.execPath ?? process.execPath), nativePath));
41168
41806
  return Array.from(new Set(candidates));
41169
41807
  }
41170
41808
 
@@ -41505,7 +42143,7 @@ var ProcessTerminal = class {
41505
42143
  break;
41506
42144
  if (now - lastDataTime >= idleMs)
41507
42145
  break;
41508
- await new Promise((resolve35) => setTimeout(resolve35, Math.min(idleMs, timeLeft)));
42146
+ await new Promise((resolve34) => setTimeout(resolve34, Math.min(idleMs, timeLeft)));
41509
42147
  }
41510
42148
  } finally {
41511
42149
  process.stdin.removeListener("data", onData);
@@ -43621,7 +44259,7 @@ var MAX_RENDER_WRITE_CHARS = 1024 * 1024;
43621
44259
 
43622
44260
  // plugins/kxm/src/tui.ts
43623
44261
  import { mkdirSync as mkdirSync24 } from "node:fs";
43624
- import { join as join38, resolve as resolve27, dirname as dirname20 } from "node:path";
44262
+ import { join as join39, resolve as resolve26, dirname as dirname19 } from "node:path";
43625
44263
  import { spawnSync as spawnSync5 } from "node:child_process";
43626
44264
 
43627
44265
  // packages/core/tui/src/types/surface.ts
@@ -43762,9 +44400,9 @@ var SHARED_EFFECT_KINDS = Object.freeze([
43762
44400
  ]);
43763
44401
 
43764
44402
  // plugins/kxm/src/local-snapshot.ts
43765
- import { existsSync as existsSync28, readdirSync as readdirSync11, readFileSync as readFileSync28 } from "node:fs";
44403
+ import { existsSync as existsSync29, readdirSync as readdirSync11, readFileSync as readFileSync29 } from "node:fs";
43766
44404
  import { homedir as homedir6 } from "node:os";
43767
- import { isAbsolute as isAbsolute9, join as join37, resolve as resolve26 } from "node:path";
44405
+ import { isAbsolute as isAbsolute9, join as join38, resolve as resolve25 } from "node:path";
43768
44406
  var DEFAULT_BUSY_TIMEOUT_MS = 5e3;
43769
44407
  var RUNTIME_PROJECT_KEY = /^[A-Za-z0-9_-][A-Za-z0-9._-]*$/;
43770
44408
  function processExists2(pid) {
@@ -43889,36 +44527,36 @@ function readOpenMessageMetadata(database, where = OPEN_MESSAGE_WHERE, params =
43889
44527
  return messages;
43890
44528
  }
43891
44529
  function resolveKxmSnapshotPaths(cwd, env = process.env) {
43892
- const stateDir = env.KXM_STATE_DIR?.trim() || join37(cwd, ".kxm", "state");
44530
+ const stateDir = env.KXM_STATE_DIR?.trim() || join38(cwd, ".kxm", "state");
43893
44531
  const configured = env.KXM_DATA_PATH?.trim();
43894
- const dataPath = configured ? resolve26(cwd, configured) : join37(stateDir, "kxm.db");
44532
+ const dataPath = configured ? resolve25(cwd, configured) : join38(stateDir, "kxm.db");
43895
44533
  return { dataPath, stateDir };
43896
44534
  }
43897
44535
  function resolveKxmStateRoot(stateDir, options) {
43898
- if (options?.kxmStateRoot && existsSync28(options.kxmStateRoot)) {
43899
- return resolve26(options.kxmStateRoot);
44536
+ if (options?.kxmStateRoot && existsSync29(options.kxmStateRoot)) {
44537
+ return resolve25(options.kxmStateRoot);
43900
44538
  }
43901
- if (existsSync28(join37(stateDir, "runtime", "registry.db")) || existsSync28(join37(stateDir, "runtime", "projects"))) {
44539
+ if (existsSync29(join38(stateDir, "runtime", "registry.db")) || existsSync29(join38(stateDir, "runtime", "projects"))) {
43902
44540
  return stateDir;
43903
44541
  }
43904
44542
  const env = options?.env ?? process.env;
43905
44543
  const explicit = env.KXM_STATE_HOME?.trim() || env.KXM_USER_STATE_DIR?.trim() || env.KXM_STATE_ROOT?.trim();
43906
- if (explicit && isAbsolute9(explicit) && existsSync28(resolve26(explicit))) {
43907
- return resolve26(explicit);
44544
+ if (explicit && isAbsolute9(explicit) && existsSync29(resolve25(explicit))) {
44545
+ return resolve25(explicit);
43908
44546
  }
43909
44547
  let base;
43910
44548
  if (process.platform === "win32") {
43911
44549
  const localAppData = env.LOCALAPPDATA?.trim();
43912
- base = localAppData && isAbsolute9(localAppData) ? localAppData : join37(homedir6(), "AppData", "Local");
43913
- base = resolve26(base, "KXM");
44550
+ base = localAppData && isAbsolute9(localAppData) ? localAppData : join38(homedir6(), "AppData", "Local");
44551
+ base = resolve25(base, "KXM");
43914
44552
  } else if (process.platform === "darwin") {
43915
- base = resolve26(homedir6(), "Library", "Application Support", "KXM");
44553
+ base = resolve25(homedir6(), "Library", "Application Support", "KXM");
43916
44554
  } else {
43917
44555
  const xdgState = env.XDG_STATE_HOME?.trim();
43918
- base = xdgState && isAbsolute9(xdgState) ? xdgState : join37(homedir6(), ".local", "state");
43919
- base = resolve26(base, "kxm");
44556
+ base = xdgState && isAbsolute9(xdgState) ? xdgState : join38(homedir6(), ".local", "state");
44557
+ base = resolve25(base, "kxm");
43920
44558
  }
43921
- if (existsSync28(base)) return base;
44559
+ if (existsSync29(base)) return base;
43922
44560
  return void 0;
43923
44561
  }
43924
44562
  function readRuntimeRuns(eventDb, projectId) {
@@ -43951,7 +44589,7 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
43951
44589
  let legacyRuns = [];
43952
44590
  let legacyRunTotal = 0;
43953
44591
  let plans = [];
43954
- if (existsSync28(dataPath)) {
44592
+ if (existsSync29(dataPath)) {
43955
44593
  hasLegacy = true;
43956
44594
  const database = openReadOnlyDatabase(dataPath);
43957
44595
  try {
@@ -43982,11 +44620,11 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
43982
44620
  let kxmRunTotal = 0;
43983
44621
  const kxmStateRoot = scope && !scope.runtimeProjectId ? void 0 : resolveKxmStateRoot(stateDir, options);
43984
44622
  if (kxmStateRoot) {
43985
- const runtimeDir = join37(kxmStateRoot, "runtime");
43986
- const registryDbPath = join37(runtimeDir, "registry.db");
43987
- const projectsDir = join37(runtimeDir, "projects");
44623
+ const runtimeDir = join38(kxmStateRoot, "runtime");
44624
+ const registryDbPath = join38(runtimeDir, "registry.db");
44625
+ const projectsDir = join38(runtimeDir, "projects");
43988
44626
  const projectKeys = /* @__PURE__ */ new Set();
43989
- if (existsSync28(registryDbPath)) {
44627
+ if (existsSync29(registryDbPath)) {
43990
44628
  hasKxm = true;
43991
44629
  try {
43992
44630
  const regDb = openReadOnlyDatabase(registryDbPath);
@@ -44002,7 +44640,7 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
44002
44640
  } catch {
44003
44641
  }
44004
44642
  }
44005
- if (!scope && existsSync28(projectsDir)) {
44643
+ if (!scope && existsSync29(projectsDir)) {
44006
44644
  try {
44007
44645
  for (const entry of readdirSync11(projectsDir, { withFileTypes: true })) {
44008
44646
  if (entry.isDirectory()) {
@@ -44014,8 +44652,8 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
44014
44652
  }
44015
44653
  for (const key of projectKeys) {
44016
44654
  if (scope && !RUNTIME_PROJECT_KEY.test(key)) continue;
44017
- const eventDbPath = join37(projectsDir, key, "run-events.db");
44018
- if (existsSync28(eventDbPath)) {
44655
+ const eventDbPath = join38(projectsDir, key, "run-events.db");
44656
+ if (existsSync29(eventDbPath)) {
44019
44657
  hasKxm = true;
44020
44658
  try {
44021
44659
  const eventDb = openReadOnlyDatabase(eventDbPath);
@@ -44033,10 +44671,10 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
44033
44671
  }
44034
44672
  }
44035
44673
  const pids = [];
44036
- if (existsSync28(stateDir)) {
44674
+ if (existsSync29(stateDir)) {
44037
44675
  for (const file of readdirSync11(stateDir).filter((name) => name.endsWith(".pid"))) {
44038
44676
  try {
44039
- const record = JSON.parse(readFileSync28(join37(stateDir, file), "utf8"));
44677
+ const record = JSON.parse(readFileSync29(join38(stateDir, file), "utf8"));
44040
44678
  pids.push({
44041
44679
  file,
44042
44680
  ...record.role ? { role: record.role } : {},
@@ -44075,8 +44713,8 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
44075
44713
  } else {
44076
44714
  source = "legacy";
44077
44715
  }
44078
- const telemetryFile = join37(stateDir, "telemetry.jsonl");
44079
- const spend = existsSync28(telemetryFile) ? readRoutingRecords(telemetryFile) : [];
44716
+ const telemetryFile = join38(stateDir, "telemetry.jsonl");
44717
+ const spend = existsSync29(telemetryFile) ? readRoutingRecords(telemetryFile) : [];
44080
44718
  return {
44081
44719
  source,
44082
44720
  agents: agents.sort((a, b2) => Number(b2.online) - Number(a.online) || a.name.localeCompare(b2.name)),
@@ -44210,11 +44848,11 @@ function spawnDegradeWorktree(repoRoot, runId, options) {
44210
44848
  const cleanRunId = runId.replace(/^run_/, "");
44211
44849
  const desc = options?.description ? slugifyBranchPart(options.description, 30) : "degrade";
44212
44850
  const branchName = `kxm/run-${cleanRunId}-${desc}`;
44213
- const relativeWtPath = join38(".kxm", "worktrees", `run-${cleanRunId}-${desc}`);
44214
- const absoluteWtPath = resolve27(repoRoot, relativeWtPath);
44851
+ const relativeWtPath = join39(".kxm", "worktrees", `run-${cleanRunId}-${desc}`);
44852
+ const absoluteWtPath = resolve26(repoRoot, relativeWtPath);
44215
44853
  const jumpCommand = `cd "${absoluteWtPath}"`;
44216
44854
  try {
44217
- mkdirSync24(dirname20(absoluteWtPath), { recursive: true });
44855
+ mkdirSync24(dirname19(absoluteWtPath), { recursive: true });
44218
44856
  const branchCheck = runner("git", ["rev-parse", "--verify", `refs/heads/${branchName}`]);
44219
44857
  let wtRes;
44220
44858
  if (branchCheck.status === 0) {
@@ -44612,11 +45250,11 @@ async function readJson(response) {
44612
45250
  }
44613
45251
  async function waitForReconnect(signal, milliseconds = 1e3) {
44614
45252
  if (signal.aborted) return;
44615
- await new Promise((resolve35) => {
45253
+ await new Promise((resolve34) => {
44616
45254
  const done = () => {
44617
45255
  clearTimeout(timer);
44618
45256
  signal.removeEventListener("abort", done);
44619
- resolve35();
45257
+ resolve34();
44620
45258
  };
44621
45259
  const timer = setTimeout(done, milliseconds);
44622
45260
  timer.unref();
@@ -44979,8 +45617,8 @@ async function runMeshTui(input) {
44979
45617
  // plugins/kxm/src/session-work.ts
44980
45618
  import { spawnSync as spawnSync6 } from "node:child_process";
44981
45619
  import { randomUUID as randomUUID12 } from "node:crypto";
44982
- import { existsSync as existsSync29, mkdirSync as mkdirSync25, readFileSync as readFileSync29, renameSync as renameSync8, writeFileSync as writeFileSync22 } from "node:fs";
44983
- import { join as join39 } from "node:path";
45620
+ import { existsSync as existsSync30, mkdirSync as mkdirSync25, readFileSync as readFileSync30, renameSync as renameSync8, writeFileSync as writeFileSync22 } from "node:fs";
45621
+ import { join as join40 } from "node:path";
44984
45622
  var MAX_SESSION_BRIEF_TASKS = 5;
44985
45623
  var MAX_SESSION_BRIEF_PLANS = 5;
44986
45624
  var SESSION_BRIEF_SCHEMA = "kxm.session-brief.v1";
@@ -45137,10 +45775,10 @@ function buildSessionBrief(snapshot, current, hub, ship, updateLatest, cost, ses
45137
45775
  };
45138
45776
  }
45139
45777
  function readCachedSessionBrief(stateDir) {
45140
- const file = join39(stateDir, "session-brief.json");
45778
+ const file = join40(stateDir, "session-brief.json");
45141
45779
  try {
45142
- if (!existsSync29(file)) return void 0;
45143
- const raw = readFileSync29(file, "utf8");
45780
+ if (!existsSync30(file)) return void 0;
45781
+ const raw = readFileSync30(file, "utf8");
45144
45782
  const parsed = JSON.parse(raw);
45145
45783
  if (parsed && parsed.schema === "kxm.session-brief.v1" && typeof parsed.generatedAt === "string") {
45146
45784
  const ageMs = Date.now() - Date.parse(parsed.generatedAt);
@@ -45156,7 +45794,7 @@ function readCachedSessionBrief(stateDir) {
45156
45794
  function writeCachedSessionBrief(stateDir, brief) {
45157
45795
  try {
45158
45796
  mkdirSync25(stateDir, { recursive: true, mode: 448 });
45159
- const file = join39(stateDir, "session-brief.json");
45797
+ const file = join40(stateDir, "session-brief.json");
45160
45798
  const tmp = `${file}.tmp.${randomUUID12().slice(0, 8)}`;
45161
45799
  writeFileSync22(tmp, JSON.stringify(brief, null, 2), { encoding: "utf8", mode: 384 });
45162
45800
  renameSync8(tmp, file);
@@ -45165,8 +45803,8 @@ function writeCachedSessionBrief(stateDir, brief) {
45165
45803
  }
45166
45804
  function estimateSessionCost(stateDir) {
45167
45805
  try {
45168
- const telemetryFile = join39(stateDir, "telemetry.jsonl");
45169
- if (!existsSync29(telemetryFile)) return void 0;
45806
+ const telemetryFile = join40(stateDir, "telemetry.jsonl");
45807
+ if (!existsSync30(telemetryFile)) return void 0;
45170
45808
  const records = readRoutingRecords(telemetryFile);
45171
45809
  if (records.length === 0) return void 0;
45172
45810
  let totalCost = 0;
@@ -45334,11 +45972,11 @@ function formatSessionBriefText(brief) {
45334
45972
  }
45335
45973
 
45336
45974
  // plugins/kxm/src/kxm-install-kind.ts
45337
- import { existsSync as existsSync30, readFileSync as readFileSync30 } from "node:fs";
45338
- import { dirname as dirname21, join as join40 } from "node:path";
45975
+ import { existsSync as existsSync31, readFileSync as readFileSync31 } from "node:fs";
45976
+ import { dirname as dirname20, join as join41 } from "node:path";
45339
45977
  function packageIsKxm(root) {
45340
45978
  try {
45341
- const pkg = JSON.parse(readFileSync30(join40(root, "package.json"), "utf8"));
45979
+ const pkg = JSON.parse(readFileSync31(join41(root, "package.json"), "utf8"));
45342
45980
  return pkg.name === "@kontextmind/kxm";
45343
45981
  } catch {
45344
45982
  return false;
@@ -45387,12 +46025,12 @@ function report(kind, root) {
45387
46025
  return { kind, root, instruction: installKindInstruction(kind, root) };
45388
46026
  }
45389
46027
  function classifyInstallRoot(probe) {
45390
- const piGitRoot = join40(probe.homeDir, ".pi", "agent", "git");
46028
+ const piGitRoot = join41(probe.homeDir, ".pi", "agent", "git");
45391
46029
  if (isUnder(probe.repoRoot, piGitRoot, probe.platform)) return report("pi-git", probe.repoRoot);
45392
- const claudeHome = probe.env.CLAUDE_CONFIG_DIR?.trim() || join40(probe.homeDir, ".claude");
45393
- const pluginsRoot = join40(claudeHome, "plugins");
46030
+ const claudeHome = probe.env.CLAUDE_CONFIG_DIR?.trim() || join41(probe.homeDir, ".claude");
46031
+ const pluginsRoot = join41(claudeHome, "plugins");
45394
46032
  if (isUnder(probe.moduleDir, pluginsRoot, probe.platform)) return report("claude-marketplace", probe.moduleDir);
45395
- const hasGit = existsSync30(join40(probe.repoRoot, ".git"));
46033
+ const hasGit = existsSync31(join41(probe.repoRoot, ".git"));
45396
46034
  if (hasGit && packageIsKxm(probe.repoRoot)) return report("source", probe.repoRoot);
45397
46035
  if (isNpmPackageLayout(probe.repoRoot, probe.platform) && packageIsKxm(probe.repoRoot) && !hasGit) {
45398
46036
  return report("npm-package", probe.repoRoot);
@@ -45406,21 +46044,21 @@ function resolveInstallKind(probe, npmGlobalRoot) {
45406
46044
  if (typeof globalRoot !== "string" || globalRoot.trim() === "") {
45407
46045
  return report("unknown", classified.root);
45408
46046
  }
45409
- const expected = dirname21(dirname21(classified.root));
46047
+ const expected = dirname20(dirname20(classified.root));
45410
46048
  if (sameHostPath2(globalRoot.trim(), expected, probe.platform)) return report("npm-global", classified.root);
45411
46049
  return report("npm-local", classified.root);
45412
46050
  }
45413
46051
 
45414
46052
  // plugins/kxm/src/kxm-update-config.ts
45415
46053
  var import_yaml16 = __toESM(require_dist(), 1);
45416
- import { existsSync as existsSync31, readFileSync as readFileSync31 } from "node:fs";
45417
- import { join as join41 } from "node:path";
46054
+ import { existsSync as existsSync32, readFileSync as readFileSync32 } from "node:fs";
46055
+ import { join as join42 } from "node:path";
45418
46056
  function loadKxmUpdateConfig(env = process.env) {
45419
- const path4 = join41(kxmUserStateRoot({ env }), "update.yaml");
45420
- if (!existsSync31(path4)) return { schema: KXM_UPDATE_SCHEMA, auto: false, source: "github" };
46057
+ const path4 = join42(kxmUserStateRoot({ env }), "update.yaml");
46058
+ if (!existsSync32(path4)) return { schema: KXM_UPDATE_SCHEMA, auto: false, source: "github" };
45421
46059
  let parsed;
45422
46060
  try {
45423
- parsed = (0, import_yaml16.parse)(readFileSync31(path4, "utf8"));
46061
+ parsed = (0, import_yaml16.parse)(readFileSync32(path4, "utf8"));
45424
46062
  } catch {
45425
46063
  throw new KxmUpdateConfigError("update.yaml is not valid YAML");
45426
46064
  }
@@ -45446,8 +46084,8 @@ function loadKxmUpdateConfig(env = process.env) {
45446
46084
  }
45447
46085
 
45448
46086
  // plugins/kxm/src/session.ts
45449
- import { existsSync as existsSync32, mkdirSync as mkdirSync26, readFileSync as readFileSync32, writeFileSync as writeFileSync23 } from "node:fs";
45450
- import { join as join42 } from "node:path";
46087
+ import { existsSync as existsSync33, mkdirSync as mkdirSync26, readFileSync as readFileSync33, writeFileSync as writeFileSync23 } from "node:fs";
46088
+ import { join as join43 } from "node:path";
45451
46089
  var SESSION_SCHEMA = "kxm.session.v1";
45452
46090
  var SessionConfigError = class extends Error {
45453
46091
  code = "session_config_invalid";
@@ -45457,12 +46095,12 @@ var SessionConfigError = class extends Error {
45457
46095
  }
45458
46096
  };
45459
46097
  function workflowAssetDirs(assetsDir, workflowId) {
45460
- const root = join42(assetsDir, "workflows", workflowId);
45461
- return [root, join42(root, "inputs"), join42(root, "outputs"), join42(root, "generated")];
46098
+ const root = join43(assetsDir, "workflows", workflowId);
46099
+ return [root, join43(root, "inputs"), join43(root, "outputs"), join43(root, "generated")];
45462
46100
  }
45463
46101
  function sessionAssetDirs(assetsDir, sessionId) {
45464
- const root = join42(assetsDir, "sessions", sessionId);
45465
- return [root, join42(root, "inputs"), join42(root, "outputs")];
46102
+ const root = join43(assetsDir, "sessions", sessionId);
46103
+ return [root, join43(root, "inputs"), join43(root, "outputs")];
45466
46104
  }
45467
46105
  function rosterNames(configDir) {
45468
46106
  return [...loadRosterMap(configDir).values()].map((row) => String(row.name));
@@ -45512,7 +46150,7 @@ function workerFromRosterRow(name, row, project) {
45512
46150
  function loadRosterMap(configDir) {
45513
46151
  const byName = /* @__PURE__ */ new Map();
45514
46152
  for (const [file, key] of [["agents.json", "agents"], ["gates.json", "gates"]]) {
45515
- const path4 = join42(configDir, file);
46153
+ const path4 = join43(configDir, file);
45516
46154
  for (const row of loadRoster(path4, key)) {
45517
46155
  const name = String(row.name).trim();
45518
46156
  const lowered = name.toLowerCase();
@@ -45526,10 +46164,10 @@ function loadRosterMap(configDir) {
45526
46164
  return byName;
45527
46165
  }
45528
46166
  function loadRoster(path4, key) {
45529
- if (!existsSync32(path4)) return [];
46167
+ if (!existsSync33(path4)) return [];
45530
46168
  let parsed;
45531
46169
  try {
45532
- parsed = JSON.parse(readFileSync32(path4, "utf8"));
46170
+ parsed = JSON.parse(readFileSync33(path4, "utf8"));
45533
46171
  } catch (error) {
45534
46172
  throw new SessionConfigError(
45535
46173
  `${key} roster at ${path4} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`
@@ -45554,7 +46192,7 @@ function loadRoster(path4, key) {
45554
46192
  });
45555
46193
  }
45556
46194
  function createSession(input) {
45557
- const assetDir = input.mode === "workflow" && input.workflowId ? join42("assets", "workflows", input.workflowId) : join42("assets", "sessions", input.id);
46195
+ const assetDir = input.mode === "workflow" && input.workflowId ? join43("assets", "workflows", input.workflowId) : join43("assets", "sessions", input.id);
45558
46196
  return {
45559
46197
  schema: SESSION_SCHEMA,
45560
46198
  id: input.id,
@@ -45567,8 +46205,8 @@ function createSession(input) {
45567
46205
  };
45568
46206
  }
45569
46207
  function writeSession(assetsDir, session, dryRun = false) {
45570
- const dir = join42(assetsDir, "sessions", session.id);
45571
- const path4 = join42(dir, "session.json");
46208
+ const dir = join43(assetsDir, "sessions", session.id);
46209
+ const path4 = join43(dir, "session.json");
45572
46210
  if (!dryRun) {
45573
46211
  mkdirSync26(dir, { recursive: true });
45574
46212
  writeFileSync23(path4, `${JSON.stringify(session, null, 2)}
@@ -45579,7 +46217,7 @@ function writeSession(assetsDir, session, dryRun = false) {
45579
46217
 
45580
46218
  // plugins/kxm/src/cli/hub.ts
45581
46219
  import { fileURLToPath as fileURLToPath3 } from "node:url";
45582
- import { dirname as dirname22 } from "node:path";
46220
+ import { dirname as dirname21 } from "node:path";
45583
46221
  import { homedir as homedir7 } from "node:os";
45584
46222
  async function hubGet(url, fetchImpl) {
45585
46223
  try {
@@ -45597,7 +46235,7 @@ async function hubGet(url, fetchImpl) {
45597
46235
  }
45598
46236
  function installProbeFrom(runtime) {
45599
46237
  const partial = runtime.io.installProbe ?? {};
45600
- const moduleDir = partial.moduleDir ?? dirname22(fileURLToPath3(import.meta.url));
46238
+ const moduleDir = partial.moduleDir ?? dirname21(fileURLToPath3(import.meta.url));
45601
46239
  return {
45602
46240
  moduleDir,
45603
46241
  // Classify the install that is **running**, never the directory the caller
@@ -45618,13 +46256,13 @@ function moduleInstallRoot() {
45618
46256
  try {
45619
46257
  return findKxmRepoRoot(import.meta.url);
45620
46258
  } catch {
45621
- return dirname22(fileURLToPath3(import.meta.url));
46259
+ return dirname21(fileURLToPath3(import.meta.url));
45622
46260
  }
45623
46261
  }
45624
46262
  function warnIgnoredProjectUpdateYaml(runtime) {
45625
- const projectFile = join43(runtime.dirs.workspace, "update.yaml");
45626
- if (!existsSync33(projectFile)) return;
45627
- const userFile = join43(kxmUserStateRoot({ env: runtime.env }), "update.yaml");
46263
+ const projectFile = join44(runtime.dirs.workspace, "update.yaml");
46264
+ if (!existsSync34(projectFile)) return;
46265
+ const userFile = join44(kxmUserStateRoot({ env: runtime.env }), "update.yaml");
45628
46266
  runtime.io.stderr(`kxm: ignoring .kxm/update.yaml in ${runtime.dirs.workdir}; update settings are read only from ${userFile}
45629
46267
  `);
45630
46268
  }
@@ -45672,7 +46310,7 @@ async function cmdDash(runtime, options = {}) {
45672
46310
  return 2;
45673
46311
  }
45674
46312
  const screen = requested;
45675
- const dataPath = resolve28(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join43(runtime.dirs.state, "kxm.db"));
46313
+ const dataPath = resolve27(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join44(runtime.dirs.state, "kxm.db"));
45676
46314
  if (runtime.dryRun) {
45677
46315
  print(runtime.io, runtime.json, {
45678
46316
  ok: true,
@@ -45718,7 +46356,7 @@ async function cmdHub(runtime) {
45718
46356
  config = loadKxmUpdateConfig(runtime.env);
45719
46357
  } catch (error) {
45720
46358
  if (error instanceof KxmUpdateConfigError) {
45721
- const yamlPath = join43(kxmUserStateRoot({ env: runtime.env }), "update.yaml");
46359
+ const yamlPath = join44(kxmUserStateRoot({ env: runtime.env }), "update.yaml");
45722
46360
  runtime.io.stderr(`kxm: ${error.message}; update check skipped; fix or remove ${yamlPath}
45723
46361
  `);
45724
46362
  } else {
@@ -45905,7 +46543,7 @@ async function cmdWorker(runtime, options) {
45905
46543
  return await (runtime.io.spawnWorker ?? ((launchEnv) => spawnScript("kxm-worker.mjs", launchEnv)))(extraEnv);
45906
46544
  }
45907
46545
  async function cmdStop(runtime, waitMsFlag) {
45908
- const pids = existsSync33(runtime.dirs.state) ? readdirSync12(runtime.dirs.state).filter((name) => name.endsWith(".pid")) : [];
46546
+ const pids = existsSync34(runtime.dirs.state) ? readdirSync12(runtime.dirs.state).filter((name) => name.endsWith(".pid")) : [];
45909
46547
  if (runtime.dryRun) {
45910
46548
  print(runtime.io, runtime.json, { ok: true, command: "stop", dryRun: true, pidFiles: pids }, "would signal pid files");
45911
46549
  return 0;
@@ -45920,7 +46558,7 @@ async function cmdStop(runtime, waitMsFlag) {
45920
46558
  const orphans = [];
45921
46559
  for (const file of pids) {
45922
46560
  try {
45923
- const record = JSON.parse(readFileSync33(join43(runtime.dirs.state, file), "utf8"));
46561
+ const record = JSON.parse(readFileSync34(join44(runtime.dirs.state, file), "utf8"));
45924
46562
  const expectedControl = file === "hub.pid" ? "hub.stop" : file.startsWith("worker-") ? `${file.slice(0, -4)}.stop` : void 0;
45925
46563
  const expectedRole = file === "hub.pid" ? "hub" : file.startsWith("worker-") ? "worker" : void 0;
45926
46564
  if (record.version !== 1 || !Number.isInteger(record.pid) || record.pid <= 0 || !record.startedAt || !expectedControl || record.controlFile !== expectedControl || record.role !== expectedRole) {
@@ -45940,7 +46578,7 @@ async function cmdStop(runtime, waitMsFlag) {
45940
46578
  ignored.push(file);
45941
46579
  continue;
45942
46580
  }
45943
- writeFileSync24(join43(runtime.dirs.state, record.controlFile), `${JSON.stringify({ startedAt: record.startedAt, ...record.generation ? { generation: record.generation } : {}, requestedAt: (/* @__PURE__ */ new Date()).toISOString() })}
46581
+ writeFileSync24(join44(runtime.dirs.state, record.controlFile), `${JSON.stringify({ startedAt: record.startedAt, ...record.generation ? { generation: record.generation } : {}, requestedAt: (/* @__PURE__ */ new Date()).toISOString() })}
45944
46582
  `, { encoding: "utf8", mode: 384 });
45945
46583
  requested.push(file);
45946
46584
  records.set(file, { pid: record.pid, startedAt: record.startedAt, ...record.generation ? { generation: record.generation } : {} });
@@ -45958,7 +46596,7 @@ async function cmdStop(runtime, waitMsFlag) {
45958
46596
  while (Date.now() <= deadline && stopped.size < requested.length) {
45959
46597
  for (const [file, record] of records) {
45960
46598
  try {
45961
- const current = JSON.parse(readFileSync33(join43(runtime.dirs.state, file), "utf8"));
46599
+ const current = JSON.parse(readFileSync34(join44(runtime.dirs.state, file), "utf8"));
45962
46600
  if (current.pid !== record.pid || current.startedAt !== record.startedAt || current.generation !== record.generation || !processExists(record.pid)) stopped.add(file);
45963
46601
  } catch {
45964
46602
  stopped.add(file);
@@ -45971,7 +46609,7 @@ async function cmdStop(runtime, waitMsFlag) {
45971
46609
  for (const file of orphans) {
45972
46610
  while (Date.now() <= deadlineOrphans) {
45973
46611
  try {
45974
- const current = JSON.parse(readFileSync33(join43(runtime.dirs.state, file), "utf8"));
46612
+ const current = JSON.parse(readFileSync34(join44(runtime.dirs.state, file), "utf8"));
45975
46613
  if (!Number.isInteger(current.serverPid) || !processExists(current.serverPid)) break;
45976
46614
  } catch {
45977
46615
  break;
@@ -45979,14 +46617,14 @@ async function cmdStop(runtime, waitMsFlag) {
45979
46617
  await (runtime.io.sleep ?? ((ms) => new Promise((resolveSleep) => setTimeout(resolveSleep, ms))))(100);
45980
46618
  }
45981
46619
  try {
45982
- const current = JSON.parse(readFileSync33(join43(runtime.dirs.state, file), "utf8"));
46620
+ const current = JSON.parse(readFileSync34(join44(runtime.dirs.state, file), "utf8"));
45983
46621
  if (Number.isInteger(current.serverPid) && processExists(current.serverPid)) {
45984
46622
  try {
45985
46623
  process.kill(current.serverPid, "SIGKILL");
45986
46624
  } catch {
45987
46625
  }
45988
46626
  }
45989
- rmSync11(join43(runtime.dirs.state, file), { force: true });
46627
+ rmSync11(join44(runtime.dirs.state, file), { force: true });
45990
46628
  } catch {
45991
46629
  }
45992
46630
  stopped.add(file);
@@ -45997,11 +46635,11 @@ async function cmdStop(runtime, waitMsFlag) {
45997
46635
  }
45998
46636
  async function cmdSessionStatus(runtime) {
45999
46637
  const stateDir = runtime.dirs.state;
46000
- const names3 = existsSync33(stateDir) ? readdirSync12(stateDir) : [];
46638
+ const names3 = existsSync34(stateDir) ? readdirSync12(stateDir) : [];
46001
46639
  const claims = [];
46002
46640
  for (const file of names3.filter((name) => name.endsWith(".pid"))) {
46003
46641
  try {
46004
- const record = JSON.parse(readFileSync33(join43(stateDir, file), "utf8"));
46642
+ const record = JSON.parse(readFileSync34(join44(stateDir, file), "utf8"));
46005
46643
  claims.push({
46006
46644
  file,
46007
46645
  role: record.role,
@@ -46016,7 +46654,7 @@ async function cmdSessionStatus(runtime) {
46016
46654
  const recoveries = [];
46017
46655
  for (const file of names3.filter((name) => name.startsWith("worker-recovery-") && name.endsWith(".json"))) {
46018
46656
  try {
46019
- const envelope = JSON.parse(readFileSync33(join43(stateDir, file), "utf8"));
46657
+ const envelope = JSON.parse(readFileSync34(join44(stateDir, file), "utf8"));
46020
46658
  recoveries.push({
46021
46659
  file,
46022
46660
  reason: envelope.reason,
@@ -46042,7 +46680,7 @@ async function cmdSessionStatus(runtime) {
46042
46680
  async function cmdAuthToken(runtime, options = {}) {
46043
46681
  const tokenFile = sessionTokenPath(runtime.env.KXM_USER_CONFIG_DIR);
46044
46682
  if (options.clear && runtime.dryRun) {
46045
- const present = existsSync33(tokenFile);
46683
+ const present = existsSync34(tokenFile);
46046
46684
  printPlan(runtime, { command: "auth token", cleared: present }, present ? [{ action: "delete", target: tokenFile }] : [], present ? "clear the session token from disk" : "no session token file to clear");
46047
46685
  return 0;
46048
46686
  }
@@ -46131,7 +46769,7 @@ async function cmdSessionBrief(runtime, options = {}) {
46131
46769
  print(runtime.io, runtime.json, { ok: true, command: "session brief", sessionToken }, sessionToken);
46132
46770
  return 0;
46133
46771
  }
46134
- const dataPath = resolve28(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join43(runtime.dirs.state, "kxm.db"));
46772
+ const dataPath = resolve27(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join44(runtime.dirs.state, "kxm.db"));
46135
46773
  const env = {
46136
46774
  ...runtime.env,
46137
46775
  KXM_STATE_DIR: runtime.dirs.state,
@@ -46157,7 +46795,7 @@ async function cmdSessionBrief(runtime, options = {}) {
46157
46795
  writeCache: !runtime.dryRun
46158
46796
  });
46159
46797
  if (runtime.dryRun) {
46160
- if (!readCachedSessionBrief(runtime.dirs.state)) planned.push({ action: "write", target: join43(runtime.dirs.state, "session-brief.json") });
46798
+ if (!readCachedSessionBrief(runtime.dirs.state)) planned.push({ action: "write", target: join44(runtime.dirs.state, "session-brief.json") });
46161
46799
  print(runtime.io, runtime.json, { ...brief, dryRun: true, planned }, [
46162
46800
  options.status ? brief.statusLine : formatSessionBriefText(brief),
46163
46801
  ...planned.map((change) => `dry run: would ${change.action} ${change.target}`)
@@ -46232,16 +46870,16 @@ async function cmdSessionStart(runtime, options) {
46232
46870
 
46233
46871
  // plugins/kxm/src/cli/system.ts
46234
46872
  import { spawnSync as spawnSync8 } from "node:child_process";
46235
- import { createHash as createHash16 } from "node:crypto";
46236
- import { existsSync as existsSync40, mkdtempSync as mkdtempSync3, readFileSync as readFileSync38, rmSync as rmSync13 } from "node:fs";
46873
+ import { createHash as createHash17 } from "node:crypto";
46874
+ import { existsSync as existsSync41, mkdtempSync as mkdtempSync3, readFileSync as readFileSync39, rmSync as rmSync13 } from "node:fs";
46237
46875
  import { tmpdir as tmpdir2 } from "node:os";
46238
- import { join as join49, resolve as resolve33 } from "node:path";
46876
+ import { basename as basename10, extname as extname3, join as join50, resolve as resolve32 } from "node:path";
46239
46877
  import { createInterface as createInterface3 } from "node:readline";
46240
46878
 
46241
46879
  // plugins/kxm/src/improve.ts
46242
- import { createHash as createHash15 } from "node:crypto";
46243
- import { existsSync as existsSync34, mkdirSync as mkdirSync28, writeFileSync as writeFileSync25 } from "node:fs";
46244
- import { join as join44, relative as relative7, resolve as resolve29 } from "node:path";
46880
+ import { createHash as createHash16 } from "node:crypto";
46881
+ import { existsSync as existsSync35, mkdirSync as mkdirSync28, writeFileSync as writeFileSync25 } from "node:fs";
46882
+ import { join as join45, relative as relative7, resolve as resolve28 } from "node:path";
46245
46883
  var CANDIDATE_SCHEMA = "kxm.candidate.v1";
46246
46884
  var IMPROVEMENT_REPORT_SCHEMA = "kxm.improvement-report.v2";
46247
46885
  function classifyCandidateKind(stepId, agentRole) {
@@ -46456,7 +47094,7 @@ function groupRoutingRecords(records, options = {}) {
46456
47094
  }
46457
47095
  const candidateKind = isCandidate ? classifyCandidateKind(group.stepId, group.agentRole) : void 0;
46458
47096
  const safeSlug = group.stepId.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 16) || "step";
46459
- const keyHash = createHash15("sha256").update(`${group.workflowHash}:${group.stepId}:${group.agentRole}:${group.promptHash}`).digest("hex").slice(0, 10);
47097
+ const keyHash = createHash16("sha256").update(`${group.workflowHash}:${group.stepId}:${group.agentRole}:${group.promptHash}`).digest("hex").slice(0, 10);
46460
47098
  const candidateId = isCandidate && candidateKind ? `cand_${candidateKind.replace(/-/g, "_")}_${safeSlug}_${keyHash}` : void 0;
46461
47099
  keyed.push({
46462
47100
  key: group.key,
@@ -46496,8 +47134,8 @@ function groupRoutingRecords(records, options = {}) {
46496
47134
  }).map((entry) => entry.row);
46497
47135
  }
46498
47136
  function buildImprovementReport(records, options = {}) {
46499
- const projectRoot = options.projectRoot ? resolve29(options.projectRoot) : process.cwd();
46500
- const candidatesDir = options.candidatesDir ? resolve29(options.candidatesDir) : join44(projectRoot, ".kxm", "candidates");
47137
+ const projectRoot = options.projectRoot ? resolve28(options.projectRoot) : process.cwd();
47138
+ const candidatesDir = options.candidatesDir ? resolve28(options.candidatesDir) : join45(projectRoot, ".kxm", "candidates");
46501
47139
  const promotionPolicy = options.promotionPolicy ?? "manual_pr";
46502
47140
  const groups = groupRoutingRecords(records, options);
46503
47141
  const candidates = [];
@@ -46511,7 +47149,7 @@ function buildImprovementReport(records, options = {}) {
46511
47149
  group.workflowId
46512
47150
  );
46513
47151
  const diffFileName = `${group.candidateId}.diff`;
46514
- const diffFilePath = join44(candidatesDir, diffFileName);
47152
+ const diffFilePath = join45(candidatesDir, diffFileName);
46515
47153
  const relDiffPath = relative7(projectRoot, diffFilePath).replace(/\\/g, "/");
46516
47154
  const candidate = {
46517
47155
  schema: CANDIDATE_SCHEMA,
@@ -46533,11 +47171,11 @@ function buildImprovementReport(records, options = {}) {
46533
47171
  createdAt: nowIso()
46534
47172
  };
46535
47173
  if (!options.dryRun) {
46536
- if (!existsSync34(candidatesDir)) {
47174
+ if (!existsSync35(candidatesDir)) {
46537
47175
  mkdirSync28(candidatesDir, { recursive: true });
46538
47176
  }
46539
47177
  writeFileSync25(diffFilePath, diff, "utf8");
46540
- const jsonFilePath = join44(candidatesDir, `${group.candidateId}.json`);
47178
+ const jsonFilePath = join45(candidatesDir, `${group.candidateId}.json`);
46541
47179
  writeFileSync25(jsonFilePath, JSON.stringify(candidate, null, 2) + "\n", "utf8");
46542
47180
  }
46543
47181
  candidates.push(candidate);
@@ -46566,7 +47204,7 @@ function buildImprovementReport(records, options = {}) {
46566
47204
  }
46567
47205
  function writeImprovementReport(improvementsDir, report2, dryRun = false) {
46568
47206
  const stamp = report2.createdAt.replace(/[:.]/g, "-");
46569
- const path4 = join44(improvementsDir, `${stamp}.json`);
47207
+ const path4 = join45(improvementsDir, `${stamp}.json`);
46570
47208
  if (!dryRun) {
46571
47209
  mkdirSync28(improvementsDir, { recursive: true });
46572
47210
  writeFileSync25(path4, `${JSON.stringify(report2, null, 2)}
@@ -46645,8 +47283,8 @@ function evaluatePromotionPolicy(candidate, policy = "manual_pr", options = {})
46645
47283
  }
46646
47284
 
46647
47285
  // plugins/kxm/src/improve-sources.ts
46648
- import { existsSync as existsSync35 } from "node:fs";
46649
- import { resolve as resolve30 } from "node:path";
47286
+ import { existsSync as existsSync36 } from "node:fs";
47287
+ import { resolve as resolve29 } from "node:path";
46650
47288
  var ENGINE_EVENTS_SQL = "SELECT run_id, sequence, event_type, payload FROM events WHERE event_type IN ('routing.attempt.recorded','step.entered','run.status_changed') ORDER BY run_id, sequence";
46651
47289
  var SIMULATED_HARNESS = "driver-simulated";
46652
47290
  function parsePayload(text) {
@@ -46668,7 +47306,7 @@ function readEngineRoutingRecords(path4) {
46668
47306
  undecided: 0,
46669
47307
  duplicatesDropped: 0
46670
47308
  };
46671
- if (!existsSync35(path4)) return { records: [], source };
47309
+ if (!existsSync36(path4)) return { records: [], source };
46672
47310
  source.exists = true;
46673
47311
  let rows;
46674
47312
  let database;
@@ -46744,7 +47382,7 @@ function readEngineRoutingRecords(path4) {
46744
47382
  };
46745
47383
  }
46746
47384
  function readJsonlSource(kind, path4) {
46747
- const exists = existsSync35(path4);
47385
+ const exists = existsSync36(path4);
46748
47386
  const records = exists ? readRoutingRecords(path4).map((entry) => entry.routing) : [];
46749
47387
  return { records, source: { kind, path: path4, exists, records: records.length, duplicatesDropped: 0 } };
46750
47388
  }
@@ -46757,7 +47395,7 @@ function loadRoutingSources(options) {
46757
47395
  const projectRoot = discoverKxmProjectRoot(options.cwd);
46758
47396
  const reads = [];
46759
47397
  if (options.file !== void 0) {
46760
- reads.push(readJsonlSource("file", resolve30(options.cwd, options.file)));
47398
+ reads.push(readJsonlSource("file", resolve29(options.cwd, options.file)));
46761
47399
  } else {
46762
47400
  if (projectRoot !== void 0) reads.push(readEngineRoutingRecords(kxmProjectRunEventsPath(projectRoot, options.env)));
46763
47401
  reads.push(readJsonlSource("telemetry", telemetryPath(options.logsDir)));
@@ -46788,8 +47426,8 @@ function loadRoutingSources(options) {
46788
47426
 
46789
47427
  // plugins/kxm/src/modes.ts
46790
47428
  var import_yaml17 = __toESM(require_dist(), 1);
46791
- import { existsSync as existsSync36, readFileSync as readFileSync35 } from "node:fs";
46792
- import { join as join45 } from "node:path";
47429
+ import { existsSync as existsSync37, readFileSync as readFileSync36 } from "node:fs";
47430
+ import { join as join46 } from "node:path";
46793
47431
  var DEFAULT_MODES_CONFIG = Object.freeze({
46794
47432
  schema: "kxm.modes.v1",
46795
47433
  majorModes: {
@@ -46852,12 +47490,12 @@ function loadModesConfig(projectRoot) {
46852
47490
  if (!projectRoot) {
46853
47491
  return DEFAULT_MODES_CONFIG;
46854
47492
  }
46855
- const modesPath = join45(projectRoot, ".kxm", "modes.yaml");
46856
- if (!existsSync36(modesPath)) {
47493
+ const modesPath = join46(projectRoot, ".kxm", "modes.yaml");
47494
+ if (!existsSync37(modesPath)) {
46857
47495
  return DEFAULT_MODES_CONFIG;
46858
47496
  }
46859
47497
  try {
46860
- const raw = readFileSync35(modesPath, "utf8");
47498
+ const raw = readFileSync36(modesPath, "utf8");
46861
47499
  const parsed = (0, import_yaml17.parse)(raw);
46862
47500
  if (parsed && parsed.schema === "kxm.modes.v1" && parsed.majorModes) {
46863
47501
  return {
@@ -46943,11 +47581,11 @@ function calculatePromptFootprint(resolved, projectRoot = process.cwd(), catalog
46943
47581
  estimatedTokens: estimateTokens(baseSystemPromptChars)
46944
47582
  });
46945
47583
  for (const relPath of resolved.contextFiles) {
46946
- const fullPath = join45(projectRoot, relPath);
47584
+ const fullPath = join46(projectRoot, relPath);
46947
47585
  let chars = 0;
46948
- if (existsSync36(fullPath)) {
47586
+ if (existsSync37(fullPath)) {
46949
47587
  try {
46950
- chars = readFileSync35(fullPath, "utf8").length;
47588
+ chars = readFileSync36(fullPath, "utf8").length;
46951
47589
  } catch {
46952
47590
  chars = 0;
46953
47591
  }
@@ -47079,9 +47717,9 @@ ${divider}
47079
47717
 
47080
47718
  // plugins/kxm/src/ssh-remote.ts
47081
47719
  import { spawnSync as spawnSync7 } from "node:child_process";
47082
- import { existsSync as existsSync37, mkdirSync as mkdirSync29, readFileSync as readFileSync36, readdirSync as readdirSync13, rmSync as rmSync12, statSync as statSync5 } from "node:fs";
47720
+ import { existsSync as existsSync38, mkdirSync as mkdirSync29, readFileSync as readFileSync37, readdirSync as readdirSync13, rmSync as rmSync12, statSync as statSync5 } from "node:fs";
47083
47721
  import { homedir as homedir8 } from "node:os";
47084
- import { join as join46, resolve as resolve31 } from "node:path";
47722
+ import { join as join47, resolve as resolve30 } from "node:path";
47085
47723
  var MAX_SSH_OUTPUT_BYTES = 50 * 1024;
47086
47724
  var MAX_SSH_OUTPUT_LINES = 2e3;
47087
47725
  var DEFAULT_SOCKET_DIR = ".kxm/run/ssh-sockets";
@@ -47107,12 +47745,12 @@ function truncateSshOutput(raw) {
47107
47745
  return { text, truncated };
47108
47746
  }
47109
47747
  function parseSshConfig(configPath) {
47110
- const targetPath = configPath ?? join46(homedir8(), ".ssh", "config");
47111
- if (!existsSync37(targetPath)) {
47748
+ const targetPath = configPath ?? join47(homedir8(), ".ssh", "config");
47749
+ if (!existsSync38(targetPath)) {
47112
47750
  return [];
47113
47751
  }
47114
47752
  try {
47115
- const content = readFileSync36(targetPath, "utf-8");
47753
+ const content = readFileSync37(targetPath, "utf-8");
47116
47754
  const lines = content.split("\n");
47117
47755
  const hosts = [];
47118
47756
  let currentHosts = [];
@@ -47189,15 +47827,15 @@ function resolveSshHostG(host, execFn = spawnSync7) {
47189
47827
  }
47190
47828
  }
47191
47829
  function ensureSocketDir(socketDir = DEFAULT_SOCKET_DIR) {
47192
- const resolved = resolve31(socketDir);
47193
- if (!existsSync37(resolved)) {
47830
+ const resolved = resolve30(socketDir);
47831
+ if (!existsSync38(resolved)) {
47194
47832
  mkdirSync29(resolved, { recursive: true, mode: 448 });
47195
47833
  }
47196
47834
  return resolved;
47197
47835
  }
47198
47836
  function buildSshArgs(options) {
47199
47837
  const socketDir = ensureSocketDir(options.socketDir ?? DEFAULT_SOCKET_DIR);
47200
- const controlPath = join46(socketDir, "%C");
47838
+ const controlPath = join47(socketDir, "%C");
47201
47839
  const persist = options.controlPersist ?? DEFAULT_CONTROL_PERSIST;
47202
47840
  const args = [
47203
47841
  "-o",
@@ -47222,7 +47860,7 @@ function buildSshArgs(options) {
47222
47860
  }
47223
47861
  function checkControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn = spawnSync7) {
47224
47862
  const resolvedDir = ensureSocketDir(socketDir);
47225
- const controlPath = join46(resolvedDir, "%C");
47863
+ const controlPath = join47(resolvedDir, "%C");
47226
47864
  try {
47227
47865
  const result = execFn("ssh", ["-O", "check", "-o", `ControlPath=${controlPath}`, host], {
47228
47866
  encoding: "utf-8"
@@ -47234,7 +47872,7 @@ function checkControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn = spawn
47234
47872
  }
47235
47873
  function closeControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn = spawnSync7) {
47236
47874
  const resolvedDir = ensureSocketDir(socketDir);
47237
- const controlPath = join46(resolvedDir, "%C");
47875
+ const controlPath = join47(resolvedDir, "%C");
47238
47876
  try {
47239
47877
  const result = execFn("ssh", ["-O", "stop", "-o", `ControlPath=${controlPath}`, host], {
47240
47878
  encoding: "utf-8"
@@ -47679,9 +48317,9 @@ complete -c kxm -n "__fish_seen_subcommand_from goal" -a "create list get"
47679
48317
  }
47680
48318
 
47681
48319
  // plugins/kxm/src/completion-install.ts
47682
- import { existsSync as existsSync38, mkdirSync as mkdirSync30, readFileSync as readFileSync37, writeFileSync as writeFileSync26 } from "node:fs";
48320
+ import { existsSync as existsSync39, mkdirSync as mkdirSync30, readFileSync as readFileSync38, writeFileSync as writeFileSync26 } from "node:fs";
47683
48321
  import { homedir as homedir9 } from "node:os";
47684
- import { basename as basename9, delimiter, dirname as dirname24, isAbsolute as isAbsolute10, join as join47, resolve as resolve32 } from "node:path";
48322
+ import { basename as basename9, delimiter, dirname as dirname23, isAbsolute as isAbsolute10, join as join48, resolve as resolve31 } from "node:path";
47685
48323
  var COMPLETION_MARKER = "# kxm completion";
47686
48324
  var PATH_MARKER = "# kxm path";
47687
48325
  function detectShell(env = process.env, platform = process.platform) {
@@ -47697,31 +48335,31 @@ function effectiveHome(options) {
47697
48335
  const env = options.env ?? process.env;
47698
48336
  if (options.homeDir) return options.homeDir;
47699
48337
  const envHome = env.HOME?.trim() ?? env.USERPROFILE?.trim();
47700
- return envHome && envHome.length > 0 ? resolve32(envHome) : homedir9();
48338
+ return envHome && envHome.length > 0 ? resolve31(envHome) : homedir9();
47701
48339
  }
47702
48340
  function completionScriptPath(shell, options = {}) {
47703
48341
  const env = options.env ?? process.env;
47704
48342
  const explicit = options.configDir ?? env.KXM_USER_CONFIG_DIR?.trim();
47705
- const base = explicit && explicit.length > 0 ? resolve32(explicit) : resolve32(effectiveHome(options), ".config", "kxm");
47706
- return join47(base, "completions", `kxm.${shell}`);
48343
+ const base = explicit && explicit.length > 0 ? resolve31(explicit) : resolve31(effectiveHome(options), ".config", "kxm");
48344
+ return join48(base, "completions", `kxm.${shell}`);
47707
48345
  }
47708
48346
  function bashRcCandidate(options) {
47709
48347
  const home = effectiveHome(options);
47710
- const candidates = [join47(home, ".bashrc"), join47(home, ".bash_profile")];
47711
- const existing = candidates.find((candidate) => existsSync38(candidate));
48348
+ const candidates = [join48(home, ".bashrc"), join48(home, ".bash_profile")];
48349
+ const existing = candidates.find((candidate) => existsSync39(candidate));
47712
48350
  return existing ?? candidates[0];
47713
48351
  }
47714
48352
  function zshRcCandidate(options) {
47715
48353
  const env = options.env ?? process.env;
47716
48354
  const home = effectiveHome(options);
47717
- if (env.ZDOTDIR?.trim()) return join47(resolve32(env.ZDOTDIR.trim()), ".zshrc");
47718
- return join47(home, ".zshrc");
48355
+ if (env.ZDOTDIR?.trim()) return join48(resolve31(env.ZDOTDIR.trim()), ".zshrc");
48356
+ return join48(home, ".zshrc");
47719
48357
  }
47720
48358
  function fishCompletionTarget(shell, options) {
47721
48359
  const env = options.env ?? process.env;
47722
48360
  const home = effectiveHome(options);
47723
- if (env.XDG_CONFIG_HOME?.trim()) return join47(resolve32(env.XDG_CONFIG_HOME.trim()), "fish", "completions", "kxm.fish");
47724
- return join47(home, ".config", "fish", "completions", "kxm.fish");
48361
+ if (env.XDG_CONFIG_HOME?.trim()) return join48(resolve31(env.XDG_CONFIG_HOME.trim()), "fish", "completions", "kxm.fish");
48362
+ return join48(home, ".config", "fish", "completions", "kxm.fish");
47725
48363
  }
47726
48364
  function completionRcTarget(shell, options = {}) {
47727
48365
  if (shell === "fish") {
@@ -47753,16 +48391,16 @@ function installShellCompletion(shellInput, options = {}) {
47753
48391
  const scriptPath = completionScriptPath(shell, options);
47754
48392
  const { rcFile } = completionRcTarget(shell, options);
47755
48393
  const script = generateShellCompletion(shell);
47756
- const existingScript = existsSync38(scriptPath);
47757
- const existingScriptMatches = existingScript && readFileSync37(scriptPath, "utf8") === script;
48394
+ const existingScript = existsSync39(scriptPath);
48395
+ const existingScriptMatches = existingScript && readFileSync38(scriptPath, "utf8") === script;
47758
48396
  let rcModified = false;
47759
48397
  let alreadyInstalled = false;
47760
48398
  if (shell === "fish") {
47761
48399
  const fishTarget = fishCompletionTarget(shell, options);
47762
- const fishInstalled = existsSync38(fishTarget) && readFileSync37(fishTarget, "utf8") === script;
48400
+ const fishInstalled = existsSync39(fishTarget) && readFileSync38(fishTarget, "utf8") === script;
47763
48401
  alreadyInstalled = fishInstalled;
47764
48402
  if (!options.dryRun && (!fishInstalled || options.overwrite)) {
47765
- mkdirSync30(resolve32(fishTarget, ".."), { recursive: true });
48403
+ mkdirSync30(resolve31(fishTarget, ".."), { recursive: true });
47766
48404
  writeFileSync26(fishTarget, script, { encoding: "utf8" });
47767
48405
  }
47768
48406
  return {
@@ -47776,12 +48414,12 @@ function installShellCompletion(shellInput, options = {}) {
47776
48414
  }
47777
48415
  if (rcFile) {
47778
48416
  const line = sourceLine(shell, scriptPath);
47779
- const rcContent = existsSync38(rcFile) ? readFileSync37(rcFile, "utf8") : "";
48417
+ const rcContent = existsSync39(rcFile) ? readFileSync38(rcFile, "utf8") : "";
47780
48418
  alreadyInstalled = rcContent.includes(line) || rcContent.includes(`${COMPLETION_MARKER}`);
47781
48419
  }
47782
48420
  if (!options.dryRun) {
47783
48421
  if (!existingScriptMatches || options.overwrite) {
47784
- mkdirSync30(resolve32(scriptPath, ".."), { recursive: true });
48422
+ mkdirSync30(resolve31(scriptPath, ".."), { recursive: true });
47785
48423
  writeFileSync26(scriptPath, script, { encoding: "utf8" });
47786
48424
  }
47787
48425
  if (rcFile && !alreadyInstalled) {
@@ -47791,8 +48429,8 @@ function installShellCompletion(shellInput, options = {}) {
47791
48429
  ${marker}
47792
48430
  ${line}
47793
48431
  `;
47794
- mkdirSync30(resolve32(rcFile, ".."), { recursive: true });
47795
- writeFileSync26(rcFile, (existsSync38(rcFile) ? readFileSync37(rcFile, "utf8") : "") + stanza, { encoding: "utf8" });
48432
+ mkdirSync30(resolve31(rcFile, ".."), { recursive: true });
48433
+ writeFileSync26(rcFile, (existsSync39(rcFile) ? readFileSync38(rcFile, "utf8") : "") + stanza, { encoding: "utf8" });
47796
48434
  rcModified = true;
47797
48435
  }
47798
48436
  }
@@ -47808,14 +48446,14 @@ ${line}
47808
48446
  function kxmBinDir(env = process.env) {
47809
48447
  const argv1 = env.KXM_ENTRY ?? process.argv[1];
47810
48448
  if (argv1 && isAbsolute10(argv1)) {
47811
- const dir = dirname24(argv1);
47812
- if (existsSync38(join47(dir, process.platform === "win32" ? "kxm.cmd" : "kxm"))) return dir;
48449
+ const dir = dirname23(argv1);
48450
+ if (existsSync39(join48(dir, process.platform === "win32" ? "kxm.cmd" : "kxm"))) return dir;
47813
48451
  }
47814
48452
  const pathEnv = env.PATH ?? "";
47815
48453
  for (const part of pathEnv.split(delimiter)) {
47816
48454
  if (!part) continue;
47817
- const candidate = resolve32(part, process.platform === "win32" ? "kxm.cmd" : "kxm");
47818
- if (existsSync38(candidate)) return resolve32(part);
48455
+ const candidate = resolve31(part, process.platform === "win32" ? "kxm.cmd" : "kxm");
48456
+ if (existsSync39(candidate)) return resolve31(part);
47819
48457
  }
47820
48458
  return void 0;
47821
48459
  }
@@ -47833,10 +48471,10 @@ function installPathEntry(shellInput, options = {}) {
47833
48471
  }
47834
48472
  const { rcFile } = completionRcTarget(shell, options);
47835
48473
  const line = pathLine(binDir);
47836
- const rcContent = existsSync38(rcFile ?? "") ? readFileSync37(rcFile, "utf8") : "";
48474
+ const rcContent = existsSync39(rcFile ?? "") ? readFileSync38(rcFile, "utf8") : "";
47837
48475
  const alreadyInstalled = rcContent.includes(binDir) || (options.env?.PATH ?? process.env.PATH ?? "").split(delimiter).includes(binDir);
47838
48476
  if (!options.dryRun && rcFile && !alreadyInstalled) {
47839
- mkdirSync30(dirname24(rcFile), { recursive: true });
48477
+ mkdirSync30(dirname23(rcFile), { recursive: true });
47840
48478
  writeFileSync26(rcFile, rcContent + `
47841
48479
  ${PATH_MARKER}
47842
48480
  ${line}
@@ -47847,8 +48485,8 @@ ${line}
47847
48485
 
47848
48486
  // plugins/kxm/src/init-guide-setup.ts
47849
48487
  var import_yaml18 = __toESM(require_dist(), 1);
47850
- import { existsSync as existsSync39, mkdirSync as mkdirSync31, writeFileSync as writeFileSync27 } from "node:fs";
47851
- import { dirname as dirname25, join as join48 } from "node:path";
48488
+ import { existsSync as existsSync40, mkdirSync as mkdirSync31, writeFileSync as writeFileSync27 } from "node:fs";
48489
+ import { dirname as dirname24, join as join49 } from "node:path";
47852
48490
  var ADMITTED_GUIDE_BINDINGS = Object.freeze({
47853
48491
  "anthropic/claude-fable-5.1": { harness: "claude", provider: "anthropic", model: "fable" },
47854
48492
  "anthropic/fable": { harness: "claude", provider: "anthropic", model: "fable" },
@@ -48239,13 +48877,13 @@ function renderGuideSetupFiles(projectRoot, plan) {
48239
48877
  const stage = roleStages.get(role);
48240
48878
  if (!stage) continue;
48241
48879
  files.push({
48242
- path: join48(projectRoot, ".kxm", "agents", `${role}.yaml`),
48880
+ path: join49(projectRoot, ".kxm", "agents", `${role}.yaml`),
48243
48881
  content: (0, import_yaml18.stringify)(agentDocument(role, stage, binding))
48244
48882
  });
48245
48883
  }
48246
48884
  for (const workflow of plan.workflows) {
48247
48885
  files.push({
48248
- path: join48(projectRoot, ".kxm", "workflows", `${workflow.slug}.yaml`),
48886
+ path: join49(projectRoot, ".kxm", "workflows", `${workflow.slug}.yaml`),
48249
48887
  content: (0, import_yaml18.stringify)(workflowDocument(workflow))
48250
48888
  });
48251
48889
  }
@@ -48262,8 +48900,8 @@ function mergeGuideRouteAdmission(projectRoot, plan) {
48262
48900
  }
48263
48901
  if (added.length === 0) return added;
48264
48902
  policy.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
48265
- const path4 = join48(projectRoot, ".kxm", "routes.yaml");
48266
- mkdirSync31(dirname25(path4), { recursive: true });
48903
+ const path4 = join49(projectRoot, ".kxm", "routes.yaml");
48904
+ mkdirSync31(dirname24(path4), { recursive: true });
48267
48905
  writeFileSync27(path4, (0, import_yaml18.stringify)(policy), "utf8");
48268
48906
  return added;
48269
48907
  }
@@ -48271,11 +48909,11 @@ function writeGuideSetupFiles(files) {
48271
48909
  const written = [];
48272
48910
  const existed = [];
48273
48911
  for (const file of files) {
48274
- if (existsSync39(file.path)) {
48912
+ if (existsSync40(file.path)) {
48275
48913
  existed.push(file.path);
48276
48914
  continue;
48277
48915
  }
48278
- mkdirSync31(dirname25(file.path), { recursive: true });
48916
+ mkdirSync31(dirname24(file.path), { recursive: true });
48279
48917
  writeFileSync27(file.path, file.content, "utf8");
48280
48918
  written.push(file.path);
48281
48919
  }
@@ -48339,7 +48977,7 @@ function applyKxmPackageUpdate(runtime, notice) {
48339
48977
  detail: `release v${notice.latest} has no sha256 digest for ${name}; refusing to install`
48340
48978
  };
48341
48979
  }
48342
- const releaseDir = mkdtempSync3(join49(tmpdir2(), "kxm-pkg-update-"));
48980
+ const releaseDir = mkdtempSync3(join50(tmpdir2(), "kxm-pkg-update-"));
48343
48981
  try {
48344
48982
  const planned = planKxmPackageUpdate(notice.source, notice.latest, releaseDir, notice.asset);
48345
48983
  if (runtime.dryRun) {
@@ -48348,7 +48986,7 @@ function applyKxmPackageUpdate(runtime, notice) {
48348
48986
  for (const step of planned) {
48349
48987
  if (step.kind === "verify") {
48350
48988
  if (!verifyReleaseAssetDigest(step.path, step.sha256)) {
48351
- const actual = existsSync40(step.path) ? createHash16("sha256").update(readFileSync38(step.path)).digest("hex") : "missing";
48989
+ const actual = existsSync41(step.path) ? createHash17("sha256").update(readFileSync39(step.path)).digest("hex") : "missing";
48352
48990
  return {
48353
48991
  ok: false,
48354
48992
  error: "release_digest_mismatch",
@@ -48625,13 +49263,27 @@ async function cmdValidate(runtime, fileFlag) {
48625
49263
  return 2;
48626
49264
  }
48627
49265
  const selectedFile = explicitFile || configuredFile;
48628
- const file = selectedFile ? resolve33(runtime.cwd, selectedFile) : void 0;
48629
- if (file && !existsSync40(file)) {
49266
+ const file = selectedFile ? resolve32(runtime.cwd, selectedFile) : void 0;
49267
+ if (file && !existsSync41(file)) {
48630
49268
  printWorker(runtime, worker, { ok: false, command: "validate", error: "file_not_found", file }, `workflow file not found: ${file}`);
48631
49269
  return 1;
48632
49270
  }
48633
49271
  try {
48634
- const raw = file ? readFileSync38(file, "utf8") : inline;
49272
+ const raw = file ? readFileSync39(file, "utf8") : inline;
49273
+ if (explicitFile && file && !raw.trimStart().startsWith("[")) {
49274
+ const value = parseRestrictedYaml2(raw, file);
49275
+ const issues = new KxmSchemaRegistry().validate("workflow", value, file);
49276
+ if (issues.length > 0) throw new KxmConfigError(issues);
49277
+ const id = basename10(file, extname3(file));
49278
+ compileKxmWorkflow({ id, value, logicalPath: file });
49279
+ printWorker(
49280
+ runtime,
49281
+ worker,
49282
+ { ok: true, command: "validate", source: "file", file, workflows: [{ id, schema: value.schema }], warnings: [] },
49283
+ `validated local workflow ${id} (schema and transitions); kxm init validates project references`
49284
+ );
49285
+ return 0;
49286
+ }
48635
49287
  const warnings = [];
48636
49288
  const definitions = parseWorkflowDefinitions(raw, runtime.env, (message) => warnings.push(message));
48637
49289
  const secretEnvs = definitions.map((definition) => ({
@@ -48656,7 +49308,7 @@ async function cmdValidate(runtime, fileFlag) {
48656
49308
  }
48657
49309
  async function cmdArtifactsExist(runtime, pathFlag) {
48658
49310
  const worker = gateOf(runtime, "artifacts-exist");
48659
- const checked = verifyArtifactExists(runtime.dirs.assets, resolve33(runtime.cwd, pathFlag));
49311
+ const checked = verifyArtifactExists(runtime.dirs.assets, resolve32(runtime.cwd, pathFlag));
48660
49312
  if (!checked.ok) {
48661
49313
  printWorker(
48662
49314
  runtime,
@@ -48712,7 +49364,7 @@ async function cmdImprove(runtime, options = {}) {
48712
49364
  print(runtime.io, runtime.json, { ok: false, command: "improve", error: "config_invalid", detail: message }, `config_invalid: ${message}`);
48713
49365
  return 1;
48714
49366
  }
48715
- const candidatesDir = options.outDir ? resolve33(runtime.cwd, options.outDir) : join49(root, ".kxm", "candidates");
49367
+ const candidatesDir = options.outDir ? resolve32(runtime.cwd, options.outDir) : join50(root, ".kxm", "candidates");
48716
49368
  const report2 = buildImprovementReport(loaded.records, {
48717
49369
  candidatesDir,
48718
49370
  projectRoot: root,
@@ -48721,7 +49373,7 @@ async function cmdImprove(runtime, options = {}) {
48721
49373
  autoThreshold: config.improvement.autoThreshold,
48722
49374
  halfLifeDays: config.improvement.telemetryHalfLifeDays
48723
49375
  });
48724
- const reportDir = join49(runtime.dirs.assets, "improvements");
49376
+ const reportDir = join50(runtime.dirs.assets, "improvements");
48725
49377
  const reportPath = writeImprovementReport(reportDir, report2, runtime.dryRun);
48726
49378
  const text = [
48727
49379
  "Sources:",
@@ -48865,9 +49517,9 @@ async function maybeOfferCompletionInstall(runtime) {
48865
49517
  if (!pathNeeded) {
48866
49518
  const scriptPath = completionScriptPath(shell, { env: runtime.env, configDir: runtime.env.KXM_USER_CONFIG_DIR });
48867
49519
  const { rcFile } = completionRcTarget(shell, { env: runtime.env });
48868
- if (rcFile && existsSync40(rcFile)) {
49520
+ if (rcFile && existsSync41(rcFile)) {
48869
49521
  try {
48870
- if (readFileSync38(rcFile, "utf8").includes(scriptPath)) return;
49522
+ if (readFileSync39(rcFile, "utf8").includes(scriptPath)) return;
48871
49523
  } catch {
48872
49524
  }
48873
49525
  }
@@ -49030,7 +49682,7 @@ async function cmdRoutingReport(runtime, options) {
49030
49682
  let catalog;
49031
49683
  if (includeEquivalentListCost) {
49032
49684
  try {
49033
- const pricesPath = options.prices ? resolve33(runtime.cwd, options.prices) : join49(runtime.dirs.workspace, "prices.yaml");
49685
+ const pricesPath = options.prices ? resolve32(runtime.cwd, options.prices) : join50(runtime.dirs.workspace, "prices.yaml");
49034
49686
  catalog = loadPriceCatalog(pricesPath);
49035
49687
  } catch {
49036
49688
  }
@@ -49414,7 +50066,7 @@ function createProgram(ctx, result) {
49414
50066
  addGlobalOptions(program2.command("restore <manifest>").description("Restore this project's SQLite stores from a verified backup manifest; refuses while the Runtime or hub is running")).option("--all-projects", "Also restore the shared Runtime registry and other projects' event stores the backup holds").action(async function restoreAction(manifest, options) {
49415
50067
  result.code = await cmdRestore(runtimeFrom(ctx, this), manifest, options);
49416
50068
  });
49417
- addGlobalOptions(program2.command("run").description("Create a KXM run (offline-first; kxm runs drive <runId> --simulated executes it model-free)").argument("[workflow]", "Workflow id to run").argument("[prompt...]", "Run prompt (events keep its hash; the full text is kept in a local 0600 sidecar file)").action(async function runAction(workflow2, promptParts) {
50069
+ addGlobalOptions(program2.command("run").description("Create a KXM run without executing steps; follow its prerequisites, then kxm runs drive <runId> --wait").argument("[workflow]", "Workflow id to run").argument("[prompt...]", "Run prompt (events keep its hash; the full text is kept in a local 0600 sidecar file)").action(async function runAction(workflow2, promptParts) {
49418
50070
  result.code = await cmdKxmRun(runtimeFrom(ctx, this), workflow2, promptParts);
49419
50071
  }));
49420
50072
  const runCmd = addGlobalOptions(program2.command("runs").description("Inspect KXM runs"));
@@ -49879,7 +50531,7 @@ async function runCli(argv, env = process.env, io = { stdout: (text) => process.
49879
50531
  throw error;
49880
50532
  }
49881
50533
  }
49882
- if (process.argv[1] && resolve34(process.argv[1]) === fileURLToPath4(import.meta.url)) {
50534
+ if (process.argv[1] && resolve33(process.argv[1]) === fileURLToPath4(import.meta.url)) {
49883
50535
  const code = await runCli(process.argv.slice(2));
49884
50536
  process.exitCode = code;
49885
50537
  }