@kontextmind/kxm 0.7.97 → 0.7.98

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.
Files changed (43) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.kxm/workflows/default.yaml +2 -0
  3. package/CHANGELOG.md +39 -2
  4. package/docs/concepts/architecture.md +1 -1
  5. package/docs/concepts/data-and-storage.md +1 -1
  6. package/docs/contracts/routing.md +1 -1
  7. package/docs/contributing/test-matrix.md +6 -5
  8. package/docs/operations/backup-and-restore.md +43 -24
  9. package/docs/operations/deploy.md +1 -1
  10. package/docs/reference/cli-reference.md +59 -24
  11. package/docs/reference/config-reference.md +24 -13
  12. package/docs/reference/harness-routing.md +3 -3
  13. package/docs/reference/http-api.md +1 -1
  14. package/docs/start/first-workflow.md +4 -4
  15. package/docs/start/quickstart-claude-code.md +2 -2
  16. package/package.json +1 -1
  17. package/plugins/kxm/.claude-plugin/plugin.json +1 -1
  18. package/plugins/kxm/dist/cli.js +393 -154
  19. package/plugins/kxm/dist/core.js +5 -2
  20. package/plugins/kxm/dist/mcp-server.js +1 -1
  21. package/plugins/kxm/dist/runtime-supervisor.js +231 -48
  22. package/plugins/kxm/dist/runtime.js +415 -92
  23. package/plugins/kxm/dist/server.js +7 -0
  24. package/plugins/kxm/package.json +1 -1
  25. package/plugins/kxm/skills/kxm-hub-ops/SKILL.md +4 -3
  26. package/plugins/kxm/skills/kxm-project-setup/SKILL.md +3 -2
  27. package/plugins/kxm/skills/kxm-routing-improve/SKILL.md +3 -0
  28. package/plugins/kxm/skills/kxm-runs/SKILL.md +8 -7
  29. package/plugins/kxm/src/cli/project.ts +22 -13
  30. package/plugins/kxm/src/cli/system.ts +22 -1
  31. package/plugins/kxm/src/cli.ts +11 -3
  32. package/plugins/kxm/src/database.ts +210 -36
  33. package/plugins/kxm/src/engine.ts +117 -2
  34. package/plugins/kxm/src/harness.ts +29 -0
  35. package/plugins/kxm/src/init-guide-setup.ts +43 -28
  36. package/plugins/kxm/src/mcp-server.ts +1 -1
  37. package/plugins/kxm/src/oneshot-producer.ts +16 -8
  38. package/plugins/kxm/src/prices.ts +33 -2
  39. package/plugins/kxm/src/routing.ts +13 -7
  40. package/plugins/kxm/src/studio-layout.ts +5 -4
  41. package/plugins/kxm/src/template.ts +31 -0
  42. package/plugins/kxm/src/worktree-witness.ts +71 -0
  43. package/schemas/backup-manifest.schema.json +33 -0
@@ -164,10 +164,10 @@ var require_code = __commonJS({
164
164
  function interpolate(x2) {
165
165
  return typeof x2 == "number" || typeof x2 == "boolean" || x2 === null ? x2 : safeStringify(Array.isArray(x2) ? x2.join(",") : x2);
166
166
  }
167
- function stringify10(x2) {
167
+ function stringify11(x2) {
168
168
  return new _Code(safeStringify(x2));
169
169
  }
170
- exports.stringify = stringify10;
170
+ exports.stringify = stringify11;
171
171
  function safeStringify(x2) {
172
172
  return JSON.stringify(x2).replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
173
173
  }
@@ -8918,7 +8918,7 @@ var require_stringify = __commonJS({
8918
8918
  props.push(doc.directives.tagString(tag));
8919
8919
  return props.join(" ");
8920
8920
  }
8921
- function stringify10(item, ctx, onComment, onChompKeep) {
8921
+ function stringify11(item, ctx, onComment, onChompKeep) {
8922
8922
  if (identity.isPair(item))
8923
8923
  return item.toString(ctx, onComment, onChompKeep);
8924
8924
  if (identity.isAlias(item)) {
@@ -8947,7 +8947,7 @@ var require_stringify = __commonJS({
8947
8947
  ${ctx.indent}${str}`;
8948
8948
  }
8949
8949
  exports.createStringifyContext = createStringifyContext;
8950
- exports.stringify = stringify10;
8950
+ exports.stringify = stringify11;
8951
8951
  }
8952
8952
  });
8953
8953
 
@@ -8957,7 +8957,7 @@ var require_stringifyPair = __commonJS({
8957
8957
  "use strict";
8958
8958
  var identity = require_identity();
8959
8959
  var Scalar = require_Scalar();
8960
- var stringify10 = require_stringify();
8960
+ var stringify11 = require_stringify();
8961
8961
  var stringifyComment = require_stringifyComment();
8962
8962
  function stringifyPair({ key, value }, ctx, onComment, onChompKeep) {
8963
8963
  const { allNullValues, doc, indent, indentStep, options: { commentString, indentSeq, simpleKeys } } = ctx;
@@ -8979,7 +8979,7 @@ var require_stringifyPair = __commonJS({
8979
8979
  });
8980
8980
  let keyCommentDone = false;
8981
8981
  let chompKeep = false;
8982
- let str = stringify10.stringify(key, ctx, () => keyCommentDone = true, () => chompKeep = true);
8982
+ let str = stringify11.stringify(key, ctx, () => keyCommentDone = true, () => chompKeep = true);
8983
8983
  if (!explicitKey && !ctx.inFlow && str.length > 1024) {
8984
8984
  if (simpleKeys)
8985
8985
  throw new Error("With simple keys, single line scalar must not span more than 1024 characters");
@@ -9031,7 +9031,7 @@ ${indent}:`;
9031
9031
  ctx.indent = ctx.indent.substring(2);
9032
9032
  }
9033
9033
  let valueCommentDone = false;
9034
- const valueStr = stringify10.stringify(value, ctx, () => valueCommentDone = true, () => chompKeep = true);
9034
+ const valueStr = stringify11.stringify(value, ctx, () => valueCommentDone = true, () => chompKeep = true);
9035
9035
  let ws = " ";
9036
9036
  if (keyComment || vsb || vcb) {
9037
9037
  ws = vsb ? "\n" : "";
@@ -9172,7 +9172,7 @@ var require_addPairToJSMap = __commonJS({
9172
9172
  "use strict";
9173
9173
  var log = require_log();
9174
9174
  var merge = require_merge();
9175
- var stringify10 = require_stringify();
9175
+ var stringify11 = require_stringify();
9176
9176
  var identity = require_identity();
9177
9177
  var toJS = require_toJS();
9178
9178
  function addPairToJSMap(ctx, map, { key, value }) {
@@ -9208,7 +9208,7 @@ var require_addPairToJSMap = __commonJS({
9208
9208
  if (typeof jsKey !== "object")
9209
9209
  return String(jsKey);
9210
9210
  if (identity.isNode(key) && ctx?.doc) {
9211
- const strCtx = stringify10.createStringifyContext(ctx.doc, {});
9211
+ const strCtx = stringify11.createStringifyContext(ctx.doc, {});
9212
9212
  strCtx.anchors = /* @__PURE__ */ new Set();
9213
9213
  for (const node of ctx.anchors.keys())
9214
9214
  strCtx.anchors.add(node.anchor);
@@ -9275,12 +9275,12 @@ var require_stringifyCollection = __commonJS({
9275
9275
  "node_modules/yaml/dist/stringify/stringifyCollection.js"(exports) {
9276
9276
  "use strict";
9277
9277
  var identity = require_identity();
9278
- var stringify10 = require_stringify();
9278
+ var stringify11 = require_stringify();
9279
9279
  var stringifyComment = require_stringifyComment();
9280
9280
  function stringifyCollection(collection, ctx, options) {
9281
9281
  const flow = ctx.inFlow ?? collection.flow;
9282
- const stringify11 = flow ? stringifyFlowCollection : stringifyBlockCollection;
9283
- return stringify11(collection, ctx, options);
9282
+ const stringify12 = flow ? stringifyFlowCollection : stringifyBlockCollection;
9283
+ return stringify12(collection, ctx, options);
9284
9284
  }
9285
9285
  function stringifyBlockCollection({ comment, items }, ctx, { blockItemPrefix, flowChars, itemIndent, onChompKeep, onComment }) {
9286
9286
  const { indent, options: { commentString } } = ctx;
@@ -9305,7 +9305,7 @@ var require_stringifyCollection = __commonJS({
9305
9305
  }
9306
9306
  }
9307
9307
  chompKeep = false;
9308
- let str2 = stringify10.stringify(item, itemCtx, () => comment2 = null, () => chompKeep = true);
9308
+ let str2 = stringify11.stringify(item, itemCtx, () => comment2 = null, () => chompKeep = true);
9309
9309
  if (comment2)
9310
9310
  str2 += stringifyComment.lineComment(str2, itemIndent, commentString(comment2));
9311
9311
  if (chompKeep && comment2)
@@ -9372,7 +9372,7 @@ ${indent}${line}` : "\n";
9372
9372
  }
9373
9373
  if (comment)
9374
9374
  reqNewline = true;
9375
- let str = stringify10.stringify(item, itemCtx, () => comment = null);
9375
+ let str = stringify11.stringify(item, itemCtx, () => comment = null);
9376
9376
  reqNewline || (reqNewline = lines.length > linesAtValue || str.includes("\n"));
9377
9377
  if (i < items.length - 1) {
9378
9378
  str += ",";
@@ -10733,7 +10733,7 @@ var require_stringifyDocument = __commonJS({
10733
10733
  "node_modules/yaml/dist/stringify/stringifyDocument.js"(exports) {
10734
10734
  "use strict";
10735
10735
  var identity = require_identity();
10736
- var stringify10 = require_stringify();
10736
+ var stringify11 = require_stringify();
10737
10737
  var stringifyComment = require_stringifyComment();
10738
10738
  function stringifyDocument(doc, options) {
10739
10739
  const lines = [];
@@ -10748,7 +10748,7 @@ var require_stringifyDocument = __commonJS({
10748
10748
  }
10749
10749
  if (hasDirectives)
10750
10750
  lines.push("---");
10751
- const ctx = stringify10.createStringifyContext(doc, options);
10751
+ const ctx = stringify11.createStringifyContext(doc, options);
10752
10752
  const { commentString } = ctx.options;
10753
10753
  if (doc.commentBefore) {
10754
10754
  if (lines.length !== 1)
@@ -10770,7 +10770,7 @@ var require_stringifyDocument = __commonJS({
10770
10770
  contentComment = doc.contents.comment;
10771
10771
  }
10772
10772
  const onChompKeep = contentComment ? void 0 : () => chompKeep = true;
10773
- let body = stringify10.stringify(doc.contents, ctx, () => contentComment = null, onChompKeep);
10773
+ let body = stringify11.stringify(doc.contents, ctx, () => contentComment = null, onChompKeep);
10774
10774
  if (contentComment)
10775
10775
  body += stringifyComment.lineComment(body, "", commentString(contentComment));
10776
10776
  if ((body[0] === "|" || body[0] === ">") && lines[lines.length - 1] === "---") {
@@ -10778,7 +10778,7 @@ var require_stringifyDocument = __commonJS({
10778
10778
  } else
10779
10779
  lines.push(body);
10780
10780
  } else {
10781
- lines.push(stringify10.stringify(doc.contents, ctx));
10781
+ lines.push(stringify11.stringify(doc.contents, ctx));
10782
10782
  }
10783
10783
  if (doc.directives?.docEnd) {
10784
10784
  if (doc.comment) {
@@ -12914,7 +12914,7 @@ var require_cst_scalar = __commonJS({
12914
12914
  var require_cst_stringify = __commonJS({
12915
12915
  "node_modules/yaml/dist/parse/cst-stringify.js"(exports) {
12916
12916
  "use strict";
12917
- var stringify10 = (cst) => "type" in cst ? stringifyToken(cst) : stringifyItem(cst);
12917
+ var stringify11 = (cst) => "type" in cst ? stringifyToken(cst) : stringifyItem(cst);
12918
12918
  function stringifyToken(token) {
12919
12919
  switch (token.type) {
12920
12920
  case "block-scalar": {
@@ -12967,7 +12967,7 @@ var require_cst_stringify = __commonJS({
12967
12967
  res += stringifyToken(value);
12968
12968
  return res;
12969
12969
  }
12970
- exports.stringify = stringify10;
12970
+ exports.stringify = stringify11;
12971
12971
  }
12972
12972
  });
12973
12973
 
@@ -14697,7 +14697,7 @@ var require_public_api = __commonJS({
14697
14697
  }
14698
14698
  return doc.toJS(Object.assign({ reviver: _reviver }, options));
14699
14699
  }
14700
- function stringify10(value, replacer, options) {
14700
+ function stringify11(value, replacer, options) {
14701
14701
  let _replacer = null;
14702
14702
  if (typeof replacer === "function" || Array.isArray(replacer)) {
14703
14703
  _replacer = replacer;
@@ -14722,7 +14722,7 @@ var require_public_api = __commonJS({
14722
14722
  exports.parse = parse12;
14723
14723
  exports.parseAllDocuments = parseAllDocuments;
14724
14724
  exports.parseDocument = parseDocument2;
14725
- exports.stringify = stringify10;
14725
+ exports.stringify = stringify11;
14726
14726
  }
14727
14727
  });
14728
14728
 
@@ -21219,6 +21219,35 @@ function coreTemplate(projectId, projectName, variant) {
21219
21219
  }]
21220
21220
  ]);
21221
21221
  if (variant === "v4-registry") {
21222
+ const coordinator = files.get(".kxm/agents/coordinator.yaml");
21223
+ if (coordinator) {
21224
+ files.set(".kxm/agents/coordinator.yaml", {
21225
+ ...coordinator,
21226
+ harness: "claude",
21227
+ model: { provider: "anthropic", model: "fable" }
21228
+ });
21229
+ }
21230
+ const implementer = files.get(".kxm/agents/implementer.yaml");
21231
+ if (implementer) {
21232
+ files.set(".kxm/agents/implementer.yaml", {
21233
+ ...implementer,
21234
+ harness: "grok",
21235
+ model: { provider: "xai", model: "grok-4.6" }
21236
+ });
21237
+ }
21238
+ const workflow = files.get(".kxm/workflows/default.yaml");
21239
+ if (workflow) {
21240
+ const limits = { ...workflow.limits };
21241
+ delete limits.maxAgentTimeMs;
21242
+ files.set(".kxm/workflows/default.yaml", { ...workflow, limits });
21243
+ }
21244
+ files.set(".kxm/routes.yaml", {
21245
+ schema: "kxm.routes.v2",
21246
+ updatedAt: "2026-09-23T00:00:00.000Z",
21247
+ admitted: ["anthropic/fable", "xai/grok-4.6"],
21248
+ disabled: [],
21249
+ roles: { implementer: ["xai/grok-4.6"] }
21250
+ });
21222
21251
  files.set(".kxm/gates.yaml", {
21223
21252
  schema: "kxm.gate-registry.v1",
21224
21253
  gates: { test: { kind: "command", argv: ["npm", "test"], timeoutMs: 36e5 } }
@@ -21745,6 +21774,10 @@ var READ_ONLY_ONESHOT_ARGS = Object.freeze({
21745
21774
  function oneShotReadOnlyArgs(harness) {
21746
21775
  return Object.hasOwn(READ_ONLY_ONESHOT_ARGS, harness) ? READ_ONLY_ONESHOT_ARGS[harness] : void 0;
21747
21776
  }
21777
+ var WRITER_ONESHOT_ARGS = Object.freeze({
21778
+ pi: Object.freeze(["-a", "--no-extensions", "--no-skills", "--no-prompt-templates", "--no-session"]),
21779
+ grok: Object.freeze(["--always-approve", "--no-subagents", "--disable-web-search"])
21780
+ });
21748
21781
  var BUILTIN_HARNESSES = Object.freeze([
21749
21782
  {
21750
21783
  id: "pi",
@@ -23591,7 +23624,7 @@ function planKxmInitialization(start = process.cwd(), options = {}) {
23591
23624
  // plugins/kxm/src/runtime-supervisor.ts
23592
23625
  import { spawn as spawn2 } from "node:child_process";
23593
23626
  import { createHash as createHash13, createHmac as createHmac2, randomBytes as randomBytes2, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
23594
- import { chmodSync as chmodSync4, existsSync as existsSync16, lstatSync as lstatSync6, mkdirSync as mkdirSync11, readFileSync as readFileSync14, renameSync as renameSync5, rmSync as rmSync5, writeFileSync as writeFileSync11 } from "node:fs";
23627
+ 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";
23595
23628
  import { dirname as dirname11, isAbsolute as isAbsolute6, join as join15 } from "node:path";
23596
23629
 
23597
23630
  // plugins/kxm/src/runtime-store.ts
@@ -24288,79 +24321,187 @@ var KXM_BACKUP_CEILINGS = {
24288
24321
  events: 7
24289
24322
  };
24290
24323
  function kxmBackupCeiling(storeId) {
24324
+ if (storeId === "runtime-registry") return KXM_BACKUP_CEILINGS.registry;
24291
24325
  if (storeId.startsWith("events:")) return KXM_BACKUP_CEILINGS.events;
24292
24326
  return KXM_BACKUP_CEILINGS[storeId] ?? KXM_BACKUP_CEILINGS["hub-store"];
24293
24327
  }
24294
- function discoverProjectStores(projectRoot, options = {}) {
24328
+ function pushStore(stores, storeId, sourcePath) {
24329
+ if (stores.some((store) => store.storeId === storeId || store.sourcePath === sourcePath)) return;
24330
+ stores.push({ storeId, sourcePath, maxSupportedVersion: kxmBackupCeiling(storeId) });
24331
+ }
24332
+ function discoverUserRuntime(env, stores, files) {
24333
+ let stateRoot;
24334
+ try {
24335
+ stateRoot = kxmUserStateRoot(env ? { env } : {});
24336
+ } catch {
24337
+ return;
24338
+ }
24339
+ const runtimeDir = join8(stateRoot, "runtime");
24340
+ const registryPath = join8(runtimeDir, "registry.db");
24341
+ if (existsSync9(registryPath)) {
24342
+ const storeId = stores.some((store) => store.storeId === "registry") ? "runtime-registry" : "registry";
24343
+ pushStore(stores, storeId, registryPath);
24344
+ }
24345
+ const projectsDir = join8(runtimeDir, "projects");
24346
+ if (!existsSync9(projectsDir)) return;
24347
+ let entries;
24348
+ try {
24349
+ entries = readdirSync2(projectsDir, { withFileTypes: true });
24350
+ } catch {
24351
+ return;
24352
+ }
24353
+ for (const entry of entries) {
24354
+ if (!entry.isDirectory()) continue;
24355
+ const dbPath = join8(projectsDir, entry.name, "run-events.db");
24356
+ if (!existsSync9(dbPath)) continue;
24357
+ const safe = entry.name.replace(/[^a-zA-Z0-9_.-]/g, "_");
24358
+ let storeId = `events:${safe}`;
24359
+ if (stores.some((store) => store.storeId === storeId)) storeId = `events:runtime:${safe}`;
24360
+ pushStore(stores, storeId, dbPath);
24361
+ const sidecar = `${dbPath}.run-prompts.json`;
24362
+ if (existsSync9(sidecar)) {
24363
+ const id = `${storeId}:run-prompts`;
24364
+ if (!files.some((file) => file.id === id || file.sourcePath === sidecar)) {
24365
+ files.push({ id, sourcePath: sidecar });
24366
+ }
24367
+ }
24368
+ }
24369
+ }
24370
+ function discoverBackupSources(projectRoot, options = {}) {
24295
24371
  const root = resolve6(projectRoot);
24296
24372
  const stores = [];
24373
+ const files = [];
24297
24374
  const hubPath = options.hubDataPath ? resolve6(options.hubDataPath) : join8(root, ".kxm", "state", "kxm.db");
24298
- if (existsSync9(hubPath)) {
24299
- stores.push({ storeId: "hub-store", sourcePath: hubPath, maxSupportedVersion: kxmBackupCeiling("hub-store") });
24300
- }
24375
+ if (existsSync9(hubPath)) pushStore(stores, "hub-store", hubPath);
24301
24376
  const registryPath = join8(root, ".kxm", "runtime", "registry.db");
24302
- if (existsSync9(registryPath)) {
24303
- stores.push({ storeId: "registry", sourcePath: registryPath, maxSupportedVersion: kxmBackupCeiling("registry") });
24304
- }
24377
+ if (existsSync9(registryPath)) pushStore(stores, "registry", registryPath);
24305
24378
  const bindingsPath = join8(root, ".kxm", "runtime", "bindings.db");
24306
- if (existsSync9(bindingsPath)) {
24307
- stores.push({ storeId: "binding-store", sourcePath: bindingsPath, maxSupportedVersion: kxmBackupCeiling("binding-store") });
24308
- }
24379
+ if (existsSync9(bindingsPath)) pushStore(stores, "binding-store", bindingsPath);
24309
24380
  const eventsDir = join8(root, ".kxm", "runtime", "events");
24310
24381
  if (existsSync9(eventsDir)) {
24311
24382
  const entries = readdirSync2(eventsDir, { withFileTypes: true });
24312
24383
  for (const entry of entries) {
24313
24384
  if (entry.isFile() && entry.name.endsWith(".db")) {
24314
- const key = entry.name.replace(/\.db$/, "");
24315
- stores.push({
24316
- storeId: `events:${key}`,
24317
- sourcePath: join8(eventsDir, entry.name),
24318
- maxSupportedVersion: kxmBackupCeiling(`events:${key}`)
24319
- });
24385
+ const key = entry.name.replace(/\.db$/, "").replace(/[^a-zA-Z0-9_.-]/g, "_");
24386
+ pushStore(stores, `events:${key}`, join8(eventsDir, entry.name));
24320
24387
  }
24321
24388
  }
24322
24389
  }
24323
- return stores;
24390
+ discoverUserRuntime(options.env, stores, files);
24391
+ return { stores, files };
24392
+ }
24393
+ function backupPlainFile(sourcePath, targetPath, id) {
24394
+ const resolvedSource = resolve6(sourcePath);
24395
+ const sourceStat = lstatSync3(resolvedSource, { throwIfNoEntry: false });
24396
+ if (!sourceStat || !sourceStat.isFile() || sourceStat.isSymbolicLink()) {
24397
+ throw databaseError("runtime_path_invalid", resolvedSource, `backup file ${resolvedSource} must be a regular file, not a link or directory`);
24398
+ }
24399
+ checkedParent(targetPath, "backup target");
24400
+ if (existsSync9(targetPath)) unlinkSync2(targetPath);
24401
+ copyFileSync(resolvedSource, targetPath);
24402
+ try {
24403
+ chmodSync3(targetPath, 384);
24404
+ } catch {
24405
+ }
24406
+ return {
24407
+ id,
24408
+ sourcePath: resolvedSource,
24409
+ backupFile: basename3(targetPath),
24410
+ sha256: fileSha256(targetPath),
24411
+ bytes: sourceStat.size
24412
+ };
24413
+ }
24414
+ function backupFilename(sourcePath, id, used) {
24415
+ let filename = basename3(sourcePath);
24416
+ if (used.has(filename)) {
24417
+ filename = `${id.replace(/[^a-zA-Z0-9_.-]/g, "_")}-${filename}`;
24418
+ }
24419
+ used.add(filename);
24420
+ return filename;
24421
+ }
24422
+ function restorePlainFile(backupFilePath, targetPath) {
24423
+ checkedParent(targetPath, "restore target");
24424
+ if (existsSync9(targetPath)) unlinkSync2(targetPath);
24425
+ copyFileSync(backupFilePath, targetPath);
24426
+ try {
24427
+ chmodSync3(targetPath, 384);
24428
+ } catch {
24429
+ }
24430
+ }
24431
+ function backupDiscoverOptions(options) {
24432
+ return {
24433
+ ...options.hubDataPath !== void 0 ? { hubDataPath: options.hubDataPath } : {},
24434
+ ...options.env !== void 0 ? { env: options.env } : {}
24435
+ };
24324
24436
  }
24325
24437
  function planBackup(options = {}) {
24326
24438
  const projectRoot = options.projectRoot ? resolve6(options.projectRoot) : process.cwd();
24327
- const stores = discoverProjectStores(projectRoot, {
24328
- ...options.hubDataPath !== void 0 ? { hubDataPath: options.hubDataPath } : {}
24329
- });
24330
- if (stores.length === 0) {
24439
+ const discovered = discoverBackupSources(projectRoot, backupDiscoverOptions(options));
24440
+ if (discovered.stores.length === 0) {
24331
24441
  throw databaseError("backup_no_stores", projectRoot, "no existing SQLite stores found to backup");
24332
24442
  }
24333
24443
  const createdAt = (/* @__PURE__ */ new Date()).toISOString();
24334
24444
  const timestamp = createdAt.replace(/[:.]/g, "-");
24335
24445
  const outDir = options.outDir ? resolve6(options.outDir) : join8(projectRoot, ".kxm", "backups", `backup-${timestamp}`);
24336
24446
  const usedFilenames = /* @__PURE__ */ new Set();
24337
- const planned = stores.map((store) => {
24338
- let filename = basename3(store.sourcePath);
24339
- if (usedFilenames.has(filename)) {
24340
- const sanitizedId = store.storeId.replace(/[^a-zA-Z0-9_.-]/g, "_");
24341
- filename = `${sanitizedId}-${filename}`;
24342
- }
24343
- usedFilenames.add(filename);
24344
- return { storeId: store.storeId, sourcePath: store.sourcePath, backupFile: filename };
24345
- });
24346
- return { projectRoot, outDir, createdAt, stores: planned };
24447
+ const stores = discovered.stores.map((store) => ({
24448
+ storeId: store.storeId,
24449
+ sourcePath: store.sourcePath,
24450
+ backupFile: backupFilename(store.sourcePath, store.storeId, usedFilenames)
24451
+ }));
24452
+ const files = discovered.files.map((file) => ({
24453
+ id: file.id,
24454
+ sourcePath: file.sourcePath,
24455
+ backupFile: backupFilename(file.sourcePath, file.id, usedFilenames)
24456
+ }));
24457
+ return { projectRoot, outDir, createdAt, stores, files };
24347
24458
  }
24348
24459
  function createBackup(options = {}) {
24349
- const { projectRoot, outDir, createdAt, stores } = planBackup(options);
24460
+ const { projectRoot, outDir, createdAt, stores, files } = planBackup(options);
24350
24461
  const backupId = `bk_${randomBytes(8).toString("hex")}`;
24351
24462
  if (!existsSync9(outDir)) {
24352
24463
  mkdirSync5(outDir, { recursive: true, mode: 448 });
24353
24464
  }
24354
24465
  const backedUpStores = [];
24466
+ const backedUpFiles = [];
24467
+ const omitted = [];
24355
24468
  for (const store of stores) {
24356
- backedUpStores.push(backupDatabaseFile(store.sourcePath, join8(outDir, store.backupFile), store.storeId));
24469
+ try {
24470
+ backedUpStores.push(backupDatabaseFile(store.sourcePath, join8(outDir, store.backupFile), store.storeId));
24471
+ } catch {
24472
+ omitted.push(store.storeId);
24473
+ }
24474
+ }
24475
+ for (const file of files) {
24476
+ try {
24477
+ backedUpFiles.push(backupPlainFile(file.sourcePath, join8(outDir, file.backupFile), file.id));
24478
+ } catch {
24479
+ omitted.push(file.id);
24480
+ }
24481
+ }
24482
+ if (backedUpStores.length === 0) {
24483
+ throw databaseError("backup_no_stores", projectRoot, "no SQLite store could be copied");
24484
+ }
24485
+ const again = discoverBackupSources(projectRoot, backupDiscoverOptions(options));
24486
+ for (const store of again.stores) {
24487
+ if (!backedUpStores.some((copied) => copied.storeId === store.storeId) && !omitted.includes(store.storeId)) {
24488
+ omitted.push(store.storeId);
24489
+ }
24490
+ }
24491
+ for (const file of again.files) {
24492
+ if (!backedUpFiles.some((copied) => copied.id === file.id) && !omitted.includes(file.id)) {
24493
+ omitted.push(file.id);
24494
+ }
24357
24495
  }
24358
24496
  const manifest = {
24359
24497
  schema: "kxm.backup-manifest.v1",
24360
24498
  backupId,
24361
24499
  createdAt,
24362
24500
  projectRoot,
24363
- stores: backedUpStores
24501
+ stores: backedUpStores,
24502
+ ...backedUpFiles.length > 0 ? { files: backedUpFiles } : {},
24503
+ complete: omitted.length === 0,
24504
+ ...omitted.length > 0 ? { omitted } : {}
24364
24505
  };
24365
24506
  const manifestJson = JSON.stringify(manifest, null, 2) + "\n";
24366
24507
  const manifestSha256 = `sha256:${createHash9("sha256").update(manifestJson, "utf8").digest("hex")}`;
@@ -24393,6 +24534,9 @@ function planRestore(manifestPathOrDir, options = {}) {
24393
24534
  if (manifest.schema !== "kxm.backup-manifest.v1" || !Array.isArray(manifest.stores) || manifest.stores.length === 0) {
24394
24535
  throw databaseError("restore_manifest_invalid", manifestPath, "manifest is not a valid kxm.backup-manifest.v1 document");
24395
24536
  }
24537
+ if (manifest.complete === false) {
24538
+ throw databaseError("restore_incomplete", manifestPath, "backup manifest is incomplete; refusing to restore a partial copy");
24539
+ }
24396
24540
  const stores = [];
24397
24541
  for (const store of manifest.stores) {
24398
24542
  const backupFilePath = join8(manifestDir, store.backupFile);
@@ -24422,7 +24566,28 @@ function planRestore(manifestPathOrDir, options = {}) {
24422
24566
  }
24423
24567
  stores.push({ storeId: store.storeId, backupFilePath, targetPath, schemaVersion: store.schemaVersion, maxSupportedVersion });
24424
24568
  }
24425
- return { manifestPath, backupId: manifest.backupId, stores };
24569
+ const files = [];
24570
+ for (const file of manifest.files ?? []) {
24571
+ const backupFilePath = join8(manifestDir, file.backupFile);
24572
+ if (!existsSync9(backupFilePath)) {
24573
+ throw databaseError("restore_file_missing", backupFilePath, `backup file ${file.backupFile} missing from ${manifestDir}`);
24574
+ }
24575
+ const actualSha256 = fileSha256(backupFilePath);
24576
+ if (actualSha256 !== file.sha256) {
24577
+ throw databaseError(
24578
+ "restore_manifest_digest_mismatch",
24579
+ backupFilePath,
24580
+ `backup file ${file.backupFile} sha256 ${actualSha256} does not match manifest hash ${file.sha256}`
24581
+ );
24582
+ }
24583
+ let targetPath = file.sourcePath;
24584
+ if (options.projectRoot && manifest.projectRoot && targetPath.startsWith(manifest.projectRoot)) {
24585
+ const rel = targetPath.slice(manifest.projectRoot.length).replace(/^[\\/]+/, "");
24586
+ targetPath = join8(resolve6(options.projectRoot), rel);
24587
+ }
24588
+ files.push({ id: file.id, backupFilePath, targetPath });
24589
+ }
24590
+ return { manifestPath, backupId: manifest.backupId, stores, files };
24426
24591
  }
24427
24592
  function restoreBackup(manifestPathOrDir, options = {}) {
24428
24593
  const plan = planRestore(manifestPathOrDir, options);
@@ -24433,6 +24598,9 @@ function restoreBackup(manifestPathOrDir, options = {}) {
24433
24598
  store.schemaVersion,
24434
24599
  store.maxSupportedVersion
24435
24600
  ));
24601
+ for (const file of plan.files) {
24602
+ restorePlainFile(file.backupFilePath, file.targetPath);
24603
+ }
24436
24604
  return {
24437
24605
  manifestPath: plan.manifestPath,
24438
24606
  backupId: plan.backupId,
@@ -25313,7 +25481,7 @@ var RECORD_LIMIT = 16 * 1024 * 1024;
25313
25481
  // plugins/kxm/src/prices.ts
25314
25482
  var import_yaml3 = __toESM(require_dist(), 1);
25315
25483
  import { createHash as createHash11 } from "node:crypto";
25316
- import { existsSync as existsSync11, readFileSync as readFileSync9, statSync } from "node:fs";
25484
+ import { existsSync as existsSync11, readFileSync as readFileSync9, statSync, writeFileSync as writeFileSync7 } from "node:fs";
25317
25485
  import { join as join10 } from "node:path";
25318
25486
 
25319
25487
  // plugins/kxm/src/price-calc.ts
@@ -25485,13 +25653,38 @@ function loadPriceCatalogForEstimate(options) {
25485
25653
  }
25486
25654
  return { catalog, unavailable: false, stale: false };
25487
25655
  }
25656
+ function acknowledgePriceCatalog(projectRoot, now = /* @__PURE__ */ new Date()) {
25657
+ const path4 = join10(projectRoot, ".kxm", "prices.yaml");
25658
+ const loaded = loadPriceCatalog(projectRoot);
25659
+ if (!loaded) throw new Error("price catalog missing");
25660
+ const date = now.toISOString().slice(0, 10);
25661
+ const body = {
25662
+ schema: loaded.schema,
25663
+ date,
25664
+ currency: loaded.currency ?? "USD",
25665
+ models: loaded.models
25666
+ };
25667
+ const stamped = { ...body, sha256: hashPriceCatalog(body) };
25668
+ writeFileSync7(path4, (0, import_yaml3.stringify)({
25669
+ schema: stamped.schema,
25670
+ date: stamped.date,
25671
+ sha256: stamped.sha256,
25672
+ currency: stamped.currency ?? "USD",
25673
+ models: stamped.models
25674
+ }), "utf8");
25675
+ const again = loadPriceCatalog(projectRoot);
25676
+ if (!again || again.date !== date || again.sha256 !== stamped.sha256) {
25677
+ throw new Error("price catalog acknowledge failed verification");
25678
+ }
25679
+ return again;
25680
+ }
25488
25681
 
25489
25682
  // plugins/kxm/src/engine.ts
25490
25683
  var import_yaml7 = __toESM(require_dist(), 1);
25491
25684
 
25492
25685
  // plugins/kxm/src/routes.ts
25493
25686
  var import_yaml4 = __toESM(require_dist(), 1);
25494
- import { existsSync as existsSync12, readFileSync as readFileSync10, mkdirSync as mkdirSync7, writeFileSync as writeFileSync7, readdirSync as readdirSync3 } from "node:fs";
25687
+ import { existsSync as existsSync12, readFileSync as readFileSync10, mkdirSync as mkdirSync7, writeFileSync as writeFileSync8, readdirSync as readdirSync3 } from "node:fs";
25495
25688
  import { join as join11 } from "node:path";
25496
25689
  var RETIRED_POLICY = ".kxm/producers.yaml";
25497
25690
  var empty = () => ({ schema: "kxm.routes.v2", updatedAt: (/* @__PURE__ */ new Date()).toISOString(), admitted: [], disabled: [], roles: {} });
@@ -25529,11 +25722,11 @@ function setRouteState(root, model, state, role, removeRole2 = false) {
25529
25722
  roleFile.id ??= role;
25530
25723
  roleFile.roster = roster;
25531
25724
  mkdirSync7(join11(root, ".kxm", "roles"), { recursive: true });
25532
- writeFileSync7(rolePath, (0, import_yaml4.stringify)(roleFile), "utf8");
25725
+ writeFileSync8(rolePath, (0, import_yaml4.stringify)(roleFile), "utf8");
25533
25726
  }
25534
25727
  policy.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
25535
25728
  mkdirSync7(join11(root, ".kxm"), { recursive: true });
25536
- writeFileSync7(join11(root, ".kxm", "routes.yaml"), (0, import_yaml4.stringify)(policy), "utf8");
25729
+ writeFileSync8(join11(root, ".kxm", "routes.yaml"), (0, import_yaml4.stringify)(policy), "utf8");
25537
25730
  return policy;
25538
25731
  }
25539
25732
  function listRoleBindings(root) {
@@ -25557,7 +25750,7 @@ function listInventoryModels(root) {
25557
25750
  // plugins/kxm/src/memory.ts
25558
25751
  var import_yaml5 = __toESM(require_dist(), 1);
25559
25752
  import { randomUUID as randomUUID5 } from "node:crypto";
25560
- import { existsSync as existsSync13, mkdirSync as mkdirSync8, readdirSync as readdirSync4, readFileSync as readFileSync11, writeFileSync as writeFileSync8 } from "node:fs";
25753
+ import { existsSync as existsSync13, mkdirSync as mkdirSync8, readdirSync as readdirSync4, readFileSync as readFileSync11, writeFileSync as writeFileSync9 } from "node:fs";
25561
25754
  import { extname as extname2, join as join12, resolve as resolve8 } from "node:path";
25562
25755
  var MEMORY_SCHEMA = "kxm.memory.v1";
25563
25756
  var MEMORY_BRIEF_SCHEMA = "kxm.memory-brief.v1";
@@ -25739,7 +25932,7 @@ function createMemoryNote(repoRoot, summary, options = {}) {
25739
25932
  const targetPath = join12(candidatesDir, `${id}.md`);
25740
25933
  if (options.dryRun) return { record, path: targetPath };
25741
25934
  mkdirSync8(candidatesDir, { recursive: true });
25742
- writeFileSync8(targetPath, formatMemoryRecord(record), "utf8");
25935
+ writeFileSync9(targetPath, formatMemoryRecord(record), "utf8");
25743
25936
  return { record, path: targetPath };
25744
25937
  }
25745
25938
  function generateMemoryBrief(repoRoot) {
@@ -25823,7 +26016,7 @@ function syncHarnessMemory(repoRoot, options = {}) {
25823
26016
  if (next === original) {
25824
26017
  unchanged.push(name);
25825
26018
  } else {
25826
- if (!options.dryRun) writeFileSync8(join12(root, name), next, "utf8");
26019
+ if (!options.dryRun) writeFileSync9(join12(root, name), next, "utf8");
25827
26020
  updated.push(name);
25828
26021
  }
25829
26022
  }
@@ -25833,7 +26026,7 @@ function syncHarnessMemory(repoRoot, options = {}) {
25833
26026
  // plugins/kxm/src/skills.ts
25834
26027
  var import_yaml6 = __toESM(require_dist(), 1);
25835
26028
  import { createHash as createHash12 } from "node:crypto";
25836
- import { existsSync as existsSync14, mkdirSync as mkdirSync9, readdirSync as readdirSync5, readFileSync as readFileSync12, renameSync as renameSync3, rmSync as rmSync3, statSync as statSync2, writeFileSync as writeFileSync9 } from "node:fs";
26029
+ 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";
25837
26030
  import { dirname as dirname9, join as join13 } from "node:path";
25838
26031
  var SKILL_CANDIDATE_SCHEMA = "kxm.skill-candidate.v1";
25839
26032
  var SKILL_EVALUATION_SCHEMA = "kxm.skill-evaluation.v1";
@@ -25937,7 +26130,7 @@ var SkillLifecycle = class {
25937
26130
  return;
25938
26131
  }
25939
26132
  mkdirSync9(dirname9(file), { recursive: true });
25940
- writeFileSync9(file, content);
26133
+ writeFileSync10(file, content);
25941
26134
  }
25942
26135
  dir(state) {
25943
26136
  return join13(this.root, state === "candidate" ? "candidates" : `${state}s`.replace("rejecteds", "rejected").replace("promoteds", "promoted"));
@@ -26574,6 +26767,8 @@ function compareRoutingRecords(records) {
26574
26767
  const blocked = settled.filter((record) => record.finalOutcome === "blocked").length;
26575
26768
  const failed2 = settled.filter((record) => record.finalOutcome === "failed").length;
26576
26769
  const reworked = records.filter((record) => record.retries > 0 || record.transitions > 0).length;
26770
+ const missingCostRuns = records.filter((record) => typeof record.costUsd !== "number" || !Number.isFinite(record.costUsd)).length;
26771
+ const summedCost = records.reduce((sum, record) => sum + (typeof record.costUsd === "number" && Number.isFinite(record.costUsd) ? record.costUsd : 0), 0);
26577
26772
  return {
26578
26773
  behavioralSha256,
26579
26774
  runs: records.length,
@@ -26581,7 +26776,8 @@ function compareRoutingRecords(records) {
26581
26776
  blocked,
26582
26777
  failed: failed2,
26583
26778
  reworkRate: records.length === 0 ? 0 : Math.round(reworked / records.length * 100) / 100,
26584
- totalCostUsd: Math.round(records.reduce((sum, record) => sum + (record.costUsd ?? 0), 0) * 1e4) / 1e4,
26779
+ totalCostUsd: missingCostRuns > 0 ? null : Math.round(summedCost * 1e4) / 1e4,
26780
+ missingCostRuns,
26585
26781
  totalTokensIn: records.reduce((sum, record) => sum + (record.tokensIn ?? 0), 0),
26586
26782
  totalTokensOut: records.reduce((sum, record) => sum + (record.tokensOut ?? 0), 0),
26587
26783
  totalHumanInterventions: records.reduce((sum, record) => sum + record.humanInterventions, 0)
@@ -26715,7 +26911,7 @@ function generateRoutingReport(records, options = {}) {
26715
26911
  medianContextTokens = contextVals.length % 2 !== 0 ? contextVals[mid] : Math.round((contextVals[mid - 1] + contextVals[mid]) / 2);
26716
26912
  }
26717
26913
  const meteredCostUsd = Math.round(meteredCostTotal * 1e4) / 1e4;
26718
- const costPerAcceptedUsd = acceptedCount > 0 ? meteredCostUsd > 0 || unmeteredAttempts > 0 ? Math.round(meteredCostUsd / acceptedCount * 1e4) / 1e4 : unknownCostAttempts === attempts ? null : 0 : null;
26914
+ const costPerAcceptedUsd = acceptedCount > 0 ? unknownCostAttempts > 0 ? null : meteredCostUsd > 0 || unmeteredAttempts > 0 ? Math.round(meteredCostUsd / acceptedCount * 1e4) / 1e4 : 0 : null;
26719
26915
  const flagged = unknownCostAttempts > 0;
26720
26916
  let equivalentListCostUsd = void 0;
26721
26917
  if (options.includeEquivalentListCost && options.catalog) {
@@ -26872,7 +27068,7 @@ var ENGINE_ROUTING_METADATA_KEYS = /* @__PURE__ */ new Set(["workflowId", "askSh
26872
27068
  var MAX_PRODUCER_ROUTING_METADATA_FIELDS = MAX_PROVIDER_METADATA_FIELDS - ENGINE_ROUTING_METADATA_KEYS.size;
26873
27069
 
26874
27070
  // plugins/kxm/src/hub-binding.ts
26875
- import { existsSync as existsSync15, mkdirSync as mkdirSync10, readFileSync as readFileSync13, renameSync as renameSync4, rmSync as rmSync4, writeFileSync as writeFileSync10 } from "node:fs";
27071
+ import { existsSync as existsSync15, mkdirSync as mkdirSync10, readFileSync as readFileSync13, renameSync as renameSync4, rmSync as rmSync4, writeFileSync as writeFileSync11 } from "node:fs";
26876
27072
  import { homedir as homedir4 } from "node:os";
26877
27073
  import { dirname as dirname10, isAbsolute as isAbsolute5, join as join14, resolve as resolve10 } from "node:path";
26878
27074
  var HUB_BINDING_SCHEMA = "kxm.hub-binding.v1";
@@ -26964,7 +27160,7 @@ function writeHubBinding(record, env = process.env) {
26964
27160
  mkdirSync10(dirname10(file), { recursive: true, mode: 448 });
26965
27161
  const temporary = join14(dirname10(file), `.hub-binding-${process.pid}.tmp`);
26966
27162
  try {
26967
- writeFileSync10(temporary, `${JSON.stringify(record, null, 2)}
27163
+ writeFileSync11(temporary, `${JSON.stringify(record, null, 2)}
26968
27164
  `, { encoding: "utf8", mode: 384 });
26969
27165
  renameSync4(temporary, file);
26970
27166
  } finally {
@@ -27478,12 +27674,12 @@ import { createInterface } from "node:readline";
27478
27674
 
27479
27675
  // plugins/kxm/src/role.ts
27480
27676
  var import_yaml9 = __toESM(require_dist(), 1);
27481
- import { existsSync as existsSync18, mkdirSync as mkdirSync14, readdirSync as readdirSync6, readFileSync as readFileSync17, rmSync as rmSync6, writeFileSync as writeFileSync13 } from "node:fs";
27677
+ import { existsSync as existsSync18, mkdirSync as mkdirSync14, readdirSync as readdirSync6, readFileSync as readFileSync17, rmSync as rmSync6, writeFileSync as writeFileSync14 } from "node:fs";
27482
27678
  import { join as join19 } from "node:path";
27483
27679
 
27484
27680
  // plugins/kxm/src/config.ts
27485
27681
  var import_yaml8 = __toESM(require_dist(), 1);
27486
- import { existsSync as existsSync17, mkdirSync as mkdirSync13, readFileSync as readFileSync16, writeFileSync as writeFileSync12 } from "node:fs";
27682
+ import { existsSync as existsSync17, mkdirSync as mkdirSync13, readFileSync as readFileSync16, writeFileSync as writeFileSync13 } from "node:fs";
27487
27683
  import { homedir as homedir5 } from "node:os";
27488
27684
  import { dirname as dirname13, join as join18, resolve as resolve12 } from "node:path";
27489
27685
  var KXM_CONFIG_SCHEMA = "kxm.config.v1";
@@ -27663,7 +27859,7 @@ function readConfigFile(targetFile2) {
27663
27859
  }
27664
27860
  function writeConfigFile(targetFile2, value) {
27665
27861
  mkdirSync13(dirname13(targetFile2), { recursive: true });
27666
- writeFileSync12(targetFile2, (0, import_yaml8.stringify)(value).trim() + "\n", "utf8");
27862
+ writeFileSync13(targetFile2, (0, import_yaml8.stringify)(value).trim() + "\n", "utf8");
27667
27863
  }
27668
27864
  function setKxmConfigValue(repoRoot, keyPath, value, options = {}) {
27669
27865
  const targetFile2 = kxmConfigFileForScope(repoRoot, options.scope ?? "project", options.userConfigDir);
@@ -27896,7 +28092,7 @@ function addRole(role, options = {}) {
27896
28092
  if (existsSync18(filePath) && !options.overwrite) {
27897
28093
  throw new Error(`role_already_exists: role '${role.id}' already exists at ${filePath}`);
27898
28094
  }
27899
- if (!options.dryRun) writeFileSync13(filePath, (0, import_yaml9.stringify)(role), "utf8");
28095
+ if (!options.dryRun) writeFileSync14(filePath, (0, import_yaml9.stringify)(role), "utf8");
27900
28096
  return { id: role.id, filePath, scope };
27901
28097
  }
27902
28098
  function removeRole(roleId, options = {}) {
@@ -27927,7 +28123,7 @@ function modifyRole(roleId, updates, options = {}) {
27927
28123
  const repoRoot = options.repoRoot ?? process.cwd();
27928
28124
  const dir = options.dryRun ? rolesDirectory(scope, repoRoot, options.userConfigDir) : ensureRolesDirectory(scope, repoRoot, options.userConfigDir);
27929
28125
  const filePath = join19(dir, `${roleId}.yaml`);
27930
- if (!options.dryRun) writeFileSync13(filePath, (0, import_yaml9.stringify)(updated), "utf8");
28126
+ if (!options.dryRun) writeFileSync14(filePath, (0, import_yaml9.stringify)(updated), "utf8");
27931
28127
  return { id: roleId, role: updated, filePath, scope };
27932
28128
  }
27933
28129
  var KXM_ROLE_HOSTS_SCHEMA = "kxm.role-hosts.v1";
@@ -28070,7 +28266,7 @@ function saveRoleHostsConfig(config, options = {}) {
28070
28266
  ...config.hostProviders && Object.keys(config.hostProviders).length > 0 ? { hostProviders: config.hostProviders } : {}
28071
28267
  };
28072
28268
  const content = format === "json" ? JSON.stringify(payload, null, 2) + "\n" : (0, import_yaml9.stringify)(payload);
28073
- writeFileSync13(filePath, content, "utf8");
28269
+ writeFileSync14(filePath, content, "utf8");
28074
28270
  return { filePath, scope };
28075
28271
  }
28076
28272
  function setRoleSeatHost(seatId, host, options = {}) {
@@ -28716,7 +28912,7 @@ import { existsSync as existsSync21, readFileSync as readFileSync20 } from "node
28716
28912
  import { join as join22, resolve as resolve17 } from "node:path";
28717
28913
 
28718
28914
  // plugins/kxm/src/retrospective.ts
28719
- import { mkdirSync as mkdirSync15, renameSync as renameSync6, writeFileSync as writeFileSync14 } from "node:fs";
28915
+ import { mkdirSync as mkdirSync15, renameSync as renameSync6, writeFileSync as writeFileSync15 } from "node:fs";
28720
28916
  import { resolve as resolve15 } from "node:path";
28721
28917
  var MAX_RETROSPECTIVE_ENTRIES = 500;
28722
28918
  var SAFE_RUN_ID = /^run_[A-Za-z0-9_-]{1,120}$/;
@@ -28943,9 +29139,9 @@ function writeRetrospective(outDir, doc) {
28943
29139
  if (!jsonPath.startsWith(prefix) || !mdPath.startsWith(prefix)) throw new Error("retrospective path escaped output directory");
28944
29140
  const jsonTmp = `${jsonPath}.tmp`;
28945
29141
  const mdTmp = `${mdPath}.tmp`;
28946
- writeFileSync14(jsonTmp, `${JSON.stringify(doc, null, 2)}
29142
+ writeFileSync15(jsonTmp, `${JSON.stringify(doc, null, 2)}
28947
29143
  `, { encoding: "utf8", mode: 384 });
28948
- writeFileSync14(mdTmp, renderRetrospectiveMarkdown(doc), { encoding: "utf8", mode: 384 });
29144
+ writeFileSync15(mdTmp, renderRetrospectiveMarkdown(doc), { encoding: "utf8", mode: 384 });
28949
29145
  renameSync6(jsonTmp, jsonPath);
28950
29146
  renameSync6(mdTmp, mdPath);
28951
29147
  return { jsonPath, mdPath };
@@ -29140,7 +29336,7 @@ async function watchGithubChecks(input) {
29140
29336
 
29141
29337
  // plugins/kxm/src/workflow-manager.ts
29142
29338
  var import_yaml11 = __toESM(require_dist(), 1);
29143
- import { existsSync as existsSync20, mkdirSync as mkdirSync16, readdirSync as readdirSync7, readFileSync as readFileSync19, rmSync as rmSync7, writeFileSync as writeFileSync15 } from "node:fs";
29339
+ import { existsSync as existsSync20, mkdirSync as mkdirSync16, readdirSync as readdirSync7, readFileSync as readFileSync19, rmSync as rmSync7, writeFileSync as writeFileSync16 } from "node:fs";
29144
29340
  import { join as join21 } from "node:path";
29145
29341
  var completed = () => ({ target: "$terminal", terminalStatus: "completed" });
29146
29342
  var failed = () => ({ target: "$terminal", terminalStatus: "failed" });
@@ -29399,7 +29595,7 @@ function addWorkflowDefinition(workflowId, content = DEFAULT_WORKFLOW_TEMPLATE,
29399
29595
  throw new Error(`workflow_already_exists: workflow '${workflowId}' already exists at ${filePath}`);
29400
29596
  }
29401
29597
  const payload = typeof content === "string" ? content : (0, import_yaml11.stringify)(content);
29402
- if (!options.dryRun) writeFileSync15(filePath, payload, "utf8");
29598
+ if (!options.dryRun) writeFileSync16(filePath, payload, "utf8");
29403
29599
  return { id: workflowId, filePath, scope };
29404
29600
  }
29405
29601
  function removeWorkflowDefinition(workflowId, options = {}) {
@@ -29427,7 +29623,7 @@ function modifyWorkflowDefinition(workflowId, updates, options = {}) {
29427
29623
  const repoRoot = options.repoRoot ?? process.cwd();
29428
29624
  const dir = options.dryRun ? workflowsDirectory(scope, repoRoot, options.userConfigDir) : ensureWorkflowsDirectory(scope, repoRoot, options.userConfigDir);
29429
29625
  const filePath = join21(dir, `${workflowId}.yaml`);
29430
- if (!options.dryRun) writeFileSync15(filePath, (0, import_yaml11.stringify)(updated), "utf8");
29626
+ if (!options.dryRun) writeFileSync16(filePath, (0, import_yaml11.stringify)(updated), "utf8");
29431
29627
  return { id: workflowId, workflow: updated, filePath, scope };
29432
29628
  }
29433
29629
 
@@ -30154,7 +30350,7 @@ function suggestWorkflowAndRoles(prompt, options = {}) {
30154
30350
  // plugins/kxm/src/task-manager.ts
30155
30351
  var import_yaml12 = __toESM(require_dist(), 1);
30156
30352
  import { randomUUID as randomUUID9 } from "node:crypto";
30157
- import { existsSync as existsSync22, mkdirSync as mkdirSync17, readFileSync as readFileSync21, readdirSync as readdirSync8, writeFileSync as writeFileSync16 } from "node:fs";
30353
+ import { existsSync as existsSync22, mkdirSync as mkdirSync17, readFileSync as readFileSync21, readdirSync as readdirSync8, writeFileSync as writeFileSync17 } from "node:fs";
30158
30354
  import { join as join23, resolve as resolve18 } from "node:path";
30159
30355
  var GOAL_SCHEMA = "kxm.goal.v1";
30160
30356
  var TASK_SCHEMA = "kxm.task.v1";
@@ -30186,7 +30382,7 @@ function createGoal(repoRoot, input, options = {}) {
30186
30382
  };
30187
30383
  if (options.dryRun) return record;
30188
30384
  mkdirSync17(goalsDirectory(repoRoot), { recursive: true });
30189
- writeFileSync16(goalFilePath(repoRoot, id), (0, import_yaml12.stringify)(record).trim() + "\n", "utf8");
30385
+ writeFileSync17(goalFilePath(repoRoot, id), (0, import_yaml12.stringify)(record).trim() + "\n", "utf8");
30190
30386
  return record;
30191
30387
  }
30192
30388
  function listGoals(repoRoot) {
@@ -30230,7 +30426,7 @@ function createTask(repoRoot, input, options = {}) {
30230
30426
  };
30231
30427
  if (options.dryRun) return record;
30232
30428
  mkdirSync17(tasksDirectory(repoRoot), { recursive: true });
30233
- writeFileSync16(taskFilePath(repoRoot, id), (0, import_yaml12.stringify)(record).trim() + "\n", "utf8");
30429
+ writeFileSync17(taskFilePath(repoRoot, id), (0, import_yaml12.stringify)(record).trim() + "\n", "utf8");
30234
30430
  return record;
30235
30431
  }
30236
30432
  function listTasks(repoRoot, options = {}) {
@@ -30274,7 +30470,7 @@ function updateTaskStatus(repoRoot, taskId, status, options = {}) {
30274
30470
  task.status = status;
30275
30471
  if (options.workflowRunId) task.workflowRunId = options.workflowRunId;
30276
30472
  task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
30277
- if (!options.dryRun) writeFileSync16(taskFilePath(repoRoot, taskId), (0, import_yaml12.stringify)(task).trim() + "\n", "utf8");
30473
+ if (!options.dryRun) writeFileSync17(taskFilePath(repoRoot, taskId), (0, import_yaml12.stringify)(task).trim() + "\n", "utf8");
30278
30474
  return task;
30279
30475
  }
30280
30476
  function syncTaskWithTracker(repoRoot, taskId, options = {}) {
@@ -30289,7 +30485,7 @@ function syncTaskWithTracker(repoRoot, taskId, options = {}) {
30289
30485
  task.trackerSync.syncStatus = "synced";
30290
30486
  task.trackerSync.lastSyncedAt = now;
30291
30487
  task.updatedAt = now;
30292
- if (!options.dryRun) writeFileSync16(taskFilePath(repoRoot, taskId), (0, import_yaml12.stringify)(task).trim() + "\n", "utf8");
30488
+ if (!options.dryRun) writeFileSync17(taskFilePath(repoRoot, taskId), (0, import_yaml12.stringify)(task).trim() + "\n", "utf8");
30293
30489
  return task;
30294
30490
  }
30295
30491
 
@@ -30570,13 +30766,14 @@ function createStudioServer(options = {}) {
30570
30766
  }
30571
30767
  return;
30572
30768
  }
30573
- res.writeHead(200, { "Content-Type": "application/json" });
30769
+ res.writeHead(501, { "Content-Type": "application/json" });
30574
30770
  res.end(JSON.stringify({
30575
- ok: true,
30771
+ ok: false,
30772
+ executed: false,
30576
30773
  mutationId,
30577
30774
  command,
30578
- mappedToCli: true,
30579
- executedAt: (/* @__PURE__ */ new Date()).toISOString()
30775
+ mappedToCli: false,
30776
+ error: "mutation_handler_missing"
30580
30777
  }));
30581
30778
  return;
30582
30779
  }
@@ -31061,7 +31258,7 @@ import { createInterface as createInterface2 } from "node:readline";
31061
31258
 
31062
31259
  // plugins/kxm/src/model-inventory.ts
31063
31260
  var import_yaml13 = __toESM(require_dist(), 1);
31064
- import { mkdirSync as mkdirSync18, writeFileSync as writeFileSync17 } from "node:fs";
31261
+ import { mkdirSync as mkdirSync18, writeFileSync as writeFileSync18 } from "node:fs";
31065
31262
  import { join as join24 } from "node:path";
31066
31263
  var OR_URL = "https://openrouter.ai/api/v1/models";
31067
31264
  var NOUS_URL = "https://inference-api.nousresearch.com/v1/models";
@@ -31149,7 +31346,7 @@ async function refreshModelInventory(options) {
31149
31346
  for (const item of nous.data) add(item, "nous", "discount");
31150
31347
  const inventory = { schema: "kxm.model-inventory.v1", fetchedAt, currency: "USD", sources, models: [...byId.values()].sort((a, b2) => a.id.localeCompare(b2.id)) };
31151
31348
  mkdirSync18(join24(options.outputRoot, ".kxm", "models"), { recursive: true });
31152
- writeFileSync17(join24(options.outputRoot, ".kxm", "models", "inventory.yaml"), (0, import_yaml13.stringify)(inventory), "utf8");
31349
+ writeFileSync18(join24(options.outputRoot, ".kxm", "models", "inventory.yaml"), (0, import_yaml13.stringify)(inventory), "utf8");
31153
31350
  return inventory;
31154
31351
  }
31155
31352
 
@@ -31173,14 +31370,14 @@ import {
31173
31370
  readdirSync as readdirSync9,
31174
31371
  renameSync as renameSync7,
31175
31372
  rmSync as rmSync9,
31176
- writeFileSync as writeFileSync19
31373
+ writeFileSync as writeFileSync20
31177
31374
  } from "node:fs";
31178
31375
  import { dirname as dirname17, isAbsolute as isAbsolute7, join as join26, relative as relative4, resolve as resolve20 } from "node:path";
31179
31376
 
31180
31377
  // plugins/kxm/src/permission.ts
31181
31378
  import { spawnSync as spawnSync4 } from "node:child_process";
31182
31379
  import { createHash as createHash14 } from "node:crypto";
31183
- import { existsSync as existsSync23, mkdtempSync, mkdirSync as mkdirSync19, readFileSync as readFileSync22, rmSync as rmSync8, writeFileSync as writeFileSync18 } from "node:fs";
31380
+ import { existsSync as existsSync23, mkdtempSync, mkdirSync as mkdirSync19, readFileSync as readFileSync22, rmSync as rmSync8, writeFileSync as writeFileSync19 } from "node:fs";
31184
31381
  import { tmpdir } from "node:os";
31185
31382
  import { dirname as dirname16, join as join25, resolve as resolve19, sep as sep3 } from "node:path";
31186
31383
  var PROSE_FIELDS = {
@@ -31729,7 +31926,7 @@ function loadKxmProjectAtRevision(root, revision, options = {}) {
31729
31926
  }
31730
31927
  const content = gitBuffer(root, ["cat-file", "blob", `${tree}:${path4}`]);
31731
31928
  mkdirSync19(dirname16(absolute), { recursive: true });
31732
- writeFileSync18(absolute, content);
31929
+ writeFileSync19(absolute, content);
31733
31930
  }
31734
31931
  initShadowGitRoot(shadowResolved);
31735
31932
  const baseProjectFile = join25(shadowResolved, ".kxm", "project.yaml");
@@ -31831,7 +32028,7 @@ function loadKxmProjectAtRevision(root, revision, options = {}) {
31831
32028
  }
31832
32029
  const content = gitBuffer(memberWorktree, ["cat-file", "blob", `${memberTree}:${path4}`]);
31833
32030
  mkdirSync19(dirname16(absolute), { recursive: true });
31834
- writeFileSync18(absolute, content);
32031
+ writeFileSync19(absolute, content);
31835
32032
  }
31836
32033
  initShadowGitRoot(memberShadowRoot);
31837
32034
  shadowBindings[member.repositoryId] = memberShadowRoot;
@@ -31945,7 +32142,7 @@ function writeDurableNew(file, bytes, mode) {
31945
32142
  let descriptor;
31946
32143
  try {
31947
32144
  descriptor = openSync2(file, "wx", mode);
31948
- writeFileSync19(descriptor, bytes);
32145
+ writeFileSync20(descriptor, bytes);
31949
32146
  fsyncSync2(descriptor);
31950
32147
  closeSync2(descriptor);
31951
32148
  descriptor = void 0;
@@ -31962,7 +32159,7 @@ function writeDurable(file, bytes, mode) {
31962
32159
  let descriptor;
31963
32160
  try {
31964
32161
  descriptor = openSync2(file, "w", mode);
31965
- writeFileSync19(descriptor, bytes);
32162
+ writeFileSync20(descriptor, bytes);
31966
32163
  fsyncSync2(descriptor);
31967
32164
  closeSync2(descriptor);
31968
32165
  descriptor = void 0;
@@ -33386,36 +33583,39 @@ async function cmdBackup(runtime, options) {
33386
33583
  try {
33387
33584
  const backupOptions = {
33388
33585
  projectRoot: runtime.cwd,
33586
+ env: runtime.env,
33389
33587
  ...options.out ? { outDir: resolve21(runtime.cwd, options.out) } : {}
33390
33588
  };
33391
33589
  if (runtime.dryRun) {
33392
33590
  const plan = planBackup(backupOptions);
33393
33591
  printPlan(
33394
33592
  runtime,
33395
- { command: "backup", outDir: plan.outDir, stores: plan.stores },
33593
+ { command: "backup", outDir: plan.outDir, stores: plan.stores, files: plan.files },
33396
33594
  [
33397
- ...plan.stores.map((store) => ({ action: "write", target: join28(plan.outDir, store.backupFile) })),
33595
+ ...[...plan.stores, ...plan.files].map((entry) => ({ action: "write", target: join28(plan.outDir, entry.backupFile) })),
33398
33596
  { action: "write", target: join28(plan.outDir, "manifest.json") }
33399
33597
  ],
33400
- `back up ${plan.stores.length} store(s) to ${plan.outDir} (sources are not opened, so their WAL is not checkpointed)`
33598
+ `back up ${plan.stores.length} store(s) and ${plan.files.length} file(s) to ${plan.outDir} (sources are not opened, so their WAL is not checkpointed)`
33401
33599
  );
33402
33600
  return 0;
33403
33601
  }
33404
33602
  const { manifest, outDir } = createBackup(backupOptions);
33603
+ const complete = manifest.complete === true;
33405
33604
  const payload = {
33406
- ok: true,
33605
+ ok: complete,
33407
33606
  command: "backup",
33408
33607
  backupId: manifest.backupId,
33409
33608
  outDir,
33410
33609
  manifest
33411
33610
  };
33412
33611
  const summary = [
33413
- `Created SQLite backup with ${manifest.stores.length} store(s):`,
33612
+ complete ? `Created SQLite backup with ${manifest.stores.length} store(s):` : `Backup is incomplete (${manifest.omitted?.length ?? 0} omitted); not ok:`,
33414
33613
  ...manifest.stores.map((s) => ` - ${s.storeId}: ${s.sourcePath} -> ${s.backupFile} (schema v${s.schemaVersion}, ${s.bytes} bytes, sha256 ${s.sha256.slice(0, 12)}...)`),
33614
+ ...(manifest.omitted ?? []).map((id) => ` - omitted ${id}`),
33415
33615
  `Manifest: ${join28(outDir, "manifest.json")}`
33416
33616
  ].join("\n");
33417
33617
  print(runtime.io, runtime.json, payload, summary);
33418
- return 0;
33618
+ return complete ? 0 : 1;
33419
33619
  } catch (error) {
33420
33620
  if (error instanceof KxmConfigError) {
33421
33621
  print(runtime.io, runtime.json, { ok: false, command: "backup", error: "backup_failed", issues: error.issues }, `backup failed: ${error.message}`);
@@ -33435,13 +33635,17 @@ async function cmdRestore(runtime, manifestArg) {
33435
33635
  command: "restore",
33436
33636
  backupId: plan.backupId,
33437
33637
  manifestPath: plan.manifestPath,
33438
- stores: plan.stores.map(({ storeId, targetPath, schemaVersion }) => ({ storeId, targetPath, schemaVersion }))
33638
+ stores: plan.stores.map(({ storeId, targetPath, schemaVersion }) => ({ storeId, targetPath, schemaVersion })),
33639
+ files: plan.files.map(({ id, targetPath }) => ({ id, targetPath }))
33439
33640
  },
33440
- plan.stores.flatMap((store) => [
33441
- { action: "write", target: store.targetPath },
33442
- ...[`${store.targetPath}-wal`, `${store.targetPath}-shm`].filter((file) => existsSync26(file)).map((file) => ({ action: "delete", target: file }))
33443
- ]),
33444
- `restore ${plan.stores.length} store(s) from ${plan.manifestPath}; digests verified against the manifest`
33641
+ [
33642
+ ...plan.stores.flatMap((store) => [
33643
+ { action: "write", target: store.targetPath },
33644
+ ...[`${store.targetPath}-wal`, `${store.targetPath}-shm`].filter((file) => existsSync26(file)).map((file) => ({ action: "delete", target: file }))
33645
+ ]),
33646
+ ...plan.files.map((file) => ({ action: "write", target: file.targetPath }))
33647
+ ],
33648
+ `restore ${plan.stores.length} store(s) and ${plan.files.length} file(s) from ${plan.manifestPath}; digests verified against the manifest`
33445
33649
  );
33446
33650
  return 0;
33447
33651
  }
@@ -34418,14 +34622,14 @@ import { readFileSync as readFileSync26 } from "node:fs";
34418
34622
  import { join as join31, relative as relative5, resolve as resolve23 } from "node:path";
34419
34623
 
34420
34624
  // plugins/kxm/src/wiki.ts
34421
- import { mkdirSync as mkdirSync22, writeFileSync as writeFileSync20 } from "node:fs";
34625
+ import { mkdirSync as mkdirSync22, writeFileSync as writeFileSync21 } from "node:fs";
34422
34626
  import { join as join30 } from "node:path";
34423
34627
  function writeCompiledWiki(root, wiki) {
34424
34628
  const written = [];
34425
34629
  for (const [relativePath, content] of [...wiki.pages].sort(([left], [right]) => left.localeCompare(right))) {
34426
34630
  const absolute = join30(root, relativePath);
34427
34631
  mkdirSync22(absolute.slice(0, absolute.lastIndexOf("/")), { recursive: true });
34428
- writeFileSync20(absolute, content);
34632
+ writeFileSync21(absolute, content);
34429
34633
  written.push(relativePath);
34430
34634
  }
34431
34635
  return written;
@@ -34817,7 +35021,7 @@ ${lines.join("\n")}`
34817
35021
 
34818
35022
  // plugins/kxm/src/cli/hub.ts
34819
35023
  import { randomUUID as randomUUID14 } from "node:crypto";
34820
- import { existsSync as existsSync33, mkdirSync as mkdirSync26, readFileSync as readFileSync32, readdirSync as readdirSync12, rmSync as rmSync11, writeFileSync as writeFileSync23 } from "node:fs";
35024
+ import { existsSync as existsSync33, mkdirSync as mkdirSync26, readFileSync as readFileSync32, readdirSync as readdirSync12, rmSync as rmSync11, writeFileSync as writeFileSync24 } from "node:fs";
34821
35025
  import { join as join41, resolve as resolve26 } from "node:path";
34822
35026
 
34823
35027
  // node_modules/marked/lib/marked.esm.js
@@ -44578,7 +44782,7 @@ async function runMeshTui(input) {
44578
44782
  // plugins/kxm/src/session-work.ts
44579
44783
  import { spawnSync as spawnSync6 } from "node:child_process";
44580
44784
  import { randomUUID as randomUUID13 } from "node:crypto";
44581
- import { existsSync as existsSync29, mkdirSync as mkdirSync24, readFileSync as readFileSync28, renameSync as renameSync8, writeFileSync as writeFileSync21 } from "node:fs";
44785
+ import { existsSync as existsSync29, mkdirSync as mkdirSync24, readFileSync as readFileSync28, renameSync as renameSync8, writeFileSync as writeFileSync22 } from "node:fs";
44582
44786
  import { join as join37 } from "node:path";
44583
44787
  var MAX_SESSION_BRIEF_TASKS = 5;
44584
44788
  var MAX_SESSION_BRIEF_PLANS = 5;
@@ -44757,7 +44961,7 @@ function writeCachedSessionBrief(stateDir, brief) {
44757
44961
  mkdirSync24(stateDir, { recursive: true, mode: 448 });
44758
44962
  const file = join37(stateDir, "session-brief.json");
44759
44963
  const tmp = `${file}.tmp.${randomUUID13().slice(0, 8)}`;
44760
- writeFileSync21(tmp, JSON.stringify(brief, null, 2), { encoding: "utf8", mode: 384 });
44964
+ writeFileSync22(tmp, JSON.stringify(brief, null, 2), { encoding: "utf8", mode: 384 });
44761
44965
  renameSync8(tmp, file);
44762
44966
  } catch {
44763
44967
  }
@@ -45045,7 +45249,7 @@ function loadKxmUpdateConfig(env = process.env) {
45045
45249
  }
45046
45250
 
45047
45251
  // plugins/kxm/src/session.ts
45048
- import { existsSync as existsSync32, mkdirSync as mkdirSync25, readFileSync as readFileSync31, writeFileSync as writeFileSync22 } from "node:fs";
45252
+ import { existsSync as existsSync32, mkdirSync as mkdirSync25, readFileSync as readFileSync31, writeFileSync as writeFileSync23 } from "node:fs";
45049
45253
  import { join as join40 } from "node:path";
45050
45254
  var SESSION_SCHEMA = "kxm.session.v1";
45051
45255
  var SessionConfigError = class extends Error {
@@ -45170,7 +45374,7 @@ function writeSession(assetsDir, session, dryRun = false) {
45170
45374
  const path4 = join40(dir, "session.json");
45171
45375
  if (!dryRun) {
45172
45376
  mkdirSync25(dir, { recursive: true });
45173
- writeFileSync22(path4, `${JSON.stringify(session, null, 2)}
45377
+ writeFileSync23(path4, `${JSON.stringify(session, null, 2)}
45174
45378
  `, { encoding: "utf8" });
45175
45379
  }
45176
45380
  return path4;
@@ -45539,7 +45743,7 @@ async function cmdStop(runtime, waitMsFlag) {
45539
45743
  ignored.push(file);
45540
45744
  continue;
45541
45745
  }
45542
- writeFileSync23(join41(runtime.dirs.state, record.controlFile), `${JSON.stringify({ startedAt: record.startedAt, ...record.generation ? { generation: record.generation } : {}, requestedAt: (/* @__PURE__ */ new Date()).toISOString() })}
45746
+ writeFileSync24(join41(runtime.dirs.state, record.controlFile), `${JSON.stringify({ startedAt: record.startedAt, ...record.generation ? { generation: record.generation } : {}, requestedAt: (/* @__PURE__ */ new Date()).toISOString() })}
45543
45747
  `, { encoding: "utf8", mode: 384 });
45544
45748
  requested.push(file);
45545
45749
  records.set(file, { pid: record.pid, startedAt: record.startedAt, ...record.generation ? { generation: record.generation } : {} });
@@ -45839,7 +46043,7 @@ import { createInterface as createInterface3 } from "node:readline";
45839
46043
 
45840
46044
  // plugins/kxm/src/improve.ts
45841
46045
  import { createHash as createHash15 } from "node:crypto";
45842
- import { existsSync as existsSync34, mkdirSync as mkdirSync27, writeFileSync as writeFileSync24 } from "node:fs";
46046
+ import { existsSync as existsSync34, mkdirSync as mkdirSync27, writeFileSync as writeFileSync25 } from "node:fs";
45843
46047
  import { join as join42, relative as relative6, resolve as resolve27 } from "node:path";
45844
46048
  var CANDIDATE_SCHEMA = "kxm.candidate.v1";
45845
46049
  var IMPROVEMENT_REPORT_SCHEMA = "kxm.improvement-report.v2";
@@ -46135,9 +46339,9 @@ function buildImprovementReport(records, options = {}) {
46135
46339
  if (!existsSync34(candidatesDir)) {
46136
46340
  mkdirSync27(candidatesDir, { recursive: true });
46137
46341
  }
46138
- writeFileSync24(diffFilePath, diff, "utf8");
46342
+ writeFileSync25(diffFilePath, diff, "utf8");
46139
46343
  const jsonFilePath = join42(candidatesDir, `${group.candidateId}.json`);
46140
- writeFileSync24(jsonFilePath, JSON.stringify(candidate, null, 2) + "\n", "utf8");
46344
+ writeFileSync25(jsonFilePath, JSON.stringify(candidate, null, 2) + "\n", "utf8");
46141
46345
  }
46142
46346
  candidates.push(candidate);
46143
46347
  }
@@ -46168,7 +46372,7 @@ function writeImprovementReport(improvementsDir, report2, dryRun = false) {
46168
46372
  const path4 = join42(improvementsDir, `${stamp}.json`);
46169
46373
  if (!dryRun) {
46170
46374
  mkdirSync27(improvementsDir, { recursive: true });
46171
- writeFileSync24(path4, `${JSON.stringify(report2, null, 2)}
46375
+ writeFileSync25(path4, `${JSON.stringify(report2, null, 2)}
46172
46376
  `, { encoding: "utf8" });
46173
46377
  }
46174
46378
  return path4;
@@ -47278,7 +47482,7 @@ complete -c kxm -n "__fish_seen_subcommand_from goal" -a "create list get"
47278
47482
  }
47279
47483
 
47280
47484
  // plugins/kxm/src/completion-install.ts
47281
- import { existsSync as existsSync38, mkdirSync as mkdirSync29, readFileSync as readFileSync36, writeFileSync as writeFileSync25 } from "node:fs";
47485
+ import { existsSync as existsSync38, mkdirSync as mkdirSync29, readFileSync as readFileSync36, writeFileSync as writeFileSync26 } from "node:fs";
47282
47486
  import { homedir as homedir9 } from "node:os";
47283
47487
  import { basename as basename9, delimiter, dirname as dirname23, isAbsolute as isAbsolute9, join as join45, resolve as resolve30 } from "node:path";
47284
47488
  var COMPLETION_MARKER = "# kxm completion";
@@ -47362,7 +47566,7 @@ function installShellCompletion(shellInput, options = {}) {
47362
47566
  alreadyInstalled = fishInstalled;
47363
47567
  if (!options.dryRun && (!fishInstalled || options.overwrite)) {
47364
47568
  mkdirSync29(resolve30(fishTarget, ".."), { recursive: true });
47365
- writeFileSync25(fishTarget, script, { encoding: "utf8" });
47569
+ writeFileSync26(fishTarget, script, { encoding: "utf8" });
47366
47570
  }
47367
47571
  return {
47368
47572
  ok: true,
@@ -47381,7 +47585,7 @@ function installShellCompletion(shellInput, options = {}) {
47381
47585
  if (!options.dryRun) {
47382
47586
  if (!existingScriptMatches || options.overwrite) {
47383
47587
  mkdirSync29(resolve30(scriptPath, ".."), { recursive: true });
47384
- writeFileSync25(scriptPath, script, { encoding: "utf8" });
47588
+ writeFileSync26(scriptPath, script, { encoding: "utf8" });
47385
47589
  }
47386
47590
  if (rcFile && !alreadyInstalled) {
47387
47591
  const line = sourceLine(shell, scriptPath);
@@ -47391,7 +47595,7 @@ ${marker}
47391
47595
  ${line}
47392
47596
  `;
47393
47597
  mkdirSync29(resolve30(rcFile, ".."), { recursive: true });
47394
- writeFileSync25(rcFile, (existsSync38(rcFile) ? readFileSync36(rcFile, "utf8") : "") + stanza, { encoding: "utf8" });
47598
+ writeFileSync26(rcFile, (existsSync38(rcFile) ? readFileSync36(rcFile, "utf8") : "") + stanza, { encoding: "utf8" });
47395
47599
  rcModified = true;
47396
47600
  }
47397
47601
  }
@@ -47436,7 +47640,7 @@ function installPathEntry(shellInput, options = {}) {
47436
47640
  const alreadyInstalled = rcContent.includes(binDir) || (options.env?.PATH ?? process.env.PATH ?? "").split(delimiter).includes(binDir);
47437
47641
  if (!options.dryRun && rcFile && !alreadyInstalled) {
47438
47642
  mkdirSync29(dirname23(rcFile), { recursive: true });
47439
- writeFileSync25(rcFile, rcContent + `
47643
+ writeFileSync26(rcFile, rcContent + `
47440
47644
  ${PATH_MARKER}
47441
47645
  ${line}
47442
47646
  `, { encoding: "utf8" });
@@ -47446,17 +47650,17 @@ ${line}
47446
47650
 
47447
47651
  // plugins/kxm/src/init-guide-setup.ts
47448
47652
  var import_yaml17 = __toESM(require_dist(), 1);
47449
- import { existsSync as existsSync39, mkdirSync as mkdirSync30, writeFileSync as writeFileSync26 } from "node:fs";
47653
+ import { existsSync as existsSync39, mkdirSync as mkdirSync30, writeFileSync as writeFileSync27 } from "node:fs";
47450
47654
  import { dirname as dirname24, join as join46 } from "node:path";
47451
- var NATIVE_VENDOR_HARNESS = Object.freeze({
47452
- anthropic: "claude",
47453
- openai: "codex",
47454
- "x-ai": "grok",
47455
- xai: "grok",
47456
- google: "agy",
47457
- moonshotai: "kimi",
47458
- moonshot: "kimi",
47459
- deepseek: "deepseek"
47655
+ var ADMITTED_GUIDE_BINDINGS = Object.freeze({
47656
+ "anthropic/claude-fable-5.1": { harness: "claude", provider: "anthropic", model: "fable" },
47657
+ "anthropic/fable": { harness: "claude", provider: "anthropic", model: "fable" },
47658
+ "openai/gpt-5.6-sol": { harness: "codex", provider: "openai", model: "gpt-5.6-sol" },
47659
+ "x-ai/grok-4.6": { harness: "grok", provider: "xai", model: "grok-4.6" },
47660
+ "xai/grok-4.6": { harness: "grok", provider: "xai", model: "grok-4.6" },
47661
+ "google/gemini-3.8-flash": { harness: "pi", provider: "antigravity", model: "gemini-3.8-flash-high" },
47662
+ "google/gemini-3.8-flash-high": { harness: "pi", provider: "antigravity", model: "gemini-3.8-flash-high" },
47663
+ "qwen/qwen3-coder-plus": { harness: "pi", provider: "openrouter", model: "qwen/qwen3-coder-plus" }
47460
47664
  });
47461
47665
  var GUIDE_WORKFLOWS = [
47462
47666
  {
@@ -47754,16 +47958,9 @@ function authenticatedHarnesses(inventory) {
47754
47958
  }
47755
47959
  function resolveCandidate(candidates, eligible) {
47756
47960
  for (const candidate of candidates) {
47757
- const nativeHarness = NATIVE_VENDOR_HARNESS[candidate.vendor];
47758
- if (nativeHarness) {
47759
- if (eligible.has(nativeHarness)) {
47760
- return { harness: nativeHarness, provider: NATIVE_HARNESS_PROVIDERS[nativeHarness], model: candidate.model };
47761
- }
47762
- continue;
47763
- }
47764
- if (eligible.has("pi")) {
47765
- return { harness: "pi", provider: "openrouter", model: `${candidate.vendor}/${candidate.model}` };
47766
- }
47961
+ const mapped = ADMITTED_GUIDE_BINDINGS[`${candidate.vendor}/${candidate.model}`];
47962
+ if (!mapped || !eligible.has(mapped.harness)) continue;
47963
+ return mapped;
47767
47964
  }
47768
47965
  return void 0;
47769
47966
  }
@@ -47784,7 +47981,7 @@ function planGuideSetup(options) {
47784
47981
  workflow: workflow.slug,
47785
47982
  stage: stage.slug,
47786
47983
  role: stage.role,
47787
- reason: "no guide candidate has an authenticated harness (see `kxm harness list`)"
47984
+ reason: "no admitted guide candidate has an authenticated harness (see `kxm harness list`)"
47788
47985
  });
47789
47986
  covered = false;
47790
47987
  continue;
@@ -47821,6 +48018,7 @@ function workflowDocument(workflow) {
47821
48018
  id: stage.slug,
47822
48019
  kind: "agent",
47823
48020
  agent: stage.role,
48021
+ repositories: { control: isWriterRole(stage.role) ? "write" : "read" },
47824
48022
  maxAttempts: 2,
47825
48023
  on: {
47826
48024
  passed: last ? { target: "$terminal", terminalStatus: "completed" } : workflow.stages[index + 1].slug,
@@ -47858,6 +48056,22 @@ function renderGuideSetupFiles(projectRoot, plan) {
47858
48056
  }
47859
48057
  return files;
47860
48058
  }
48059
+ function mergeGuideRouteAdmission(projectRoot, plan) {
48060
+ const policy = loadRoutePolicy(projectRoot);
48061
+ const added = [];
48062
+ for (const binding of plan.agents.values()) {
48063
+ const selector = `${binding.provider}/${binding.model}`;
48064
+ if (policy.disabled.includes(selector) || policy.admitted.includes(selector)) continue;
48065
+ policy.admitted.push(selector);
48066
+ added.push(selector);
48067
+ }
48068
+ if (added.length === 0) return added;
48069
+ policy.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
48070
+ const path4 = join46(projectRoot, ".kxm", "routes.yaml");
48071
+ mkdirSync30(dirname24(path4), { recursive: true });
48072
+ writeFileSync27(path4, (0, import_yaml17.stringify)(policy), "utf8");
48073
+ return added;
48074
+ }
47861
48075
  function writeGuideSetupFiles(files) {
47862
48076
  const written = [];
47863
48077
  const existed = [];
@@ -47867,7 +48081,7 @@ function writeGuideSetupFiles(files) {
47867
48081
  continue;
47868
48082
  }
47869
48083
  mkdirSync30(dirname24(file.path), { recursive: true });
47870
- writeFileSync26(file.path, file.content, "utf8");
48084
+ writeFileSync27(file.path, file.content, "utf8");
47871
48085
  written.push(file.path);
47872
48086
  }
47873
48087
  return { written, existed };
@@ -48571,9 +48785,12 @@ Set up workflow-guide agents and workflows for authenticated harnesses (${harnes
48571
48785
  const plan = planGuideSetup({ inventory, selected });
48572
48786
  const files = renderGuideSetupFiles(runtime.cwd, plan);
48573
48787
  const report2 = writeGuideSetupFiles(files);
48788
+ const admitted = mergeGuideRouteAdmission(runtime.cwd, plan);
48574
48789
  for (const file of report2.written) runtime.io.stdout(`wrote ${file}
48575
48790
  `);
48576
48791
  for (const file of report2.existed) runtime.io.stdout(`kept existing ${file} (not overwritten)
48792
+ `);
48793
+ for (const selector of admitted) runtime.io.stdout(`admitted route ${selector}
48577
48794
  `);
48578
48795
  for (const skip of plan.skipped) {
48579
48796
  runtime.io.stdout(`skipped ${skip.workflow}/${skip.role}: ${skip.reason}
@@ -48583,6 +48800,23 @@ Set up workflow-guide agents and workflows for authenticated harnesses (${harnes
48583
48800
  runtime.io.stdout("inspect with `kxm workflow definitions`; guide candidates are dated research \u2014 verify before dispatch\n");
48584
48801
  }
48585
48802
  }
48803
+ async function cmdPricesAcknowledge(runtime) {
48804
+ try {
48805
+ const catalog = acknowledgePriceCatalog(runtime.cwd);
48806
+ print(runtime.io, runtime.json, {
48807
+ ok: true,
48808
+ command: "prices acknowledge",
48809
+ date: catalog.date,
48810
+ sha256: catalog.sha256,
48811
+ note: "stamped the existing list as today's estimate; vendor rates were not fetched"
48812
+ }, `price catalog stamped ${catalog.date} (list estimate only; vendor rates were not fetched)`);
48813
+ return 0;
48814
+ } catch (error) {
48815
+ const message = error instanceof Error ? error.message : String(error);
48816
+ print(runtime.io, runtime.json, { ok: false, command: "prices acknowledge", error: "prices_acknowledge_failed", message }, `prices acknowledge failed: ${message}`);
48817
+ return 1;
48818
+ }
48819
+ }
48586
48820
  async function cmdRoutingReport(runtime, options) {
48587
48821
  let file;
48588
48822
  let records;
@@ -48979,7 +49213,7 @@ function createProgram(ctx, result) {
48979
49213
  maybeOfferGuideSetup
48980
49214
  });
48981
49215
  });
48982
- addGlobalOptions(program2.command("backup").description("Create a verified SQLite backup of the project hub store with a hashed manifest (Runtime stores under the user state root are not included)")).option("--out <dir>", "Directory to write backup and manifest").action(async function backupAction(options) {
49216
+ addGlobalOptions(program2.command("backup").description("Create a verified SQLite backup of the project hub store and the Runtime stores under the user state root, with a hashed manifest")).option("--out <dir>", "Directory to write backup and manifest").action(async function backupAction(options) {
48983
49217
  result.code = await cmdBackup(runtimeFrom(ctx, this), options);
48984
49218
  });
48985
49219
  addGlobalOptions(program2.command("restore <manifest>").description("Restore SQLite stores from a verified backup manifest")).action(async function restoreAction(manifest) {
@@ -48993,7 +49227,7 @@ function createProgram(ctx, result) {
48993
49227
  addGlobalOptions(runCmd.command("status").description("Show the projected status of a run, including durable drive receipt state (open / receipt verified / unsettled / orphaned)")).argument("<runId>", "Run id").action(async function runStatusAction(runId) {
48994
49228
  result.code = await cmdKxmRunStatus(runtimeFrom(ctx, this), runId);
48995
49229
  });
48996
- addGlobalOptions(runCmd.command("drive").description("Drive a run with live harness calls, or with the model-free simulation when --simulated is passed")).argument("<runId>", "Run id").option("--simulated", "Use the model-free simulation producer").option("--wait", "Wait until a drive receipt is recorded; exits 0 only for a VERIFIED COMPLETED settlement").option("--timeout-ms <n>", "Wait timeout in milliseconds (default 60000, max 600000)").action(async function runDriveAction(runId, options) {
49230
+ addGlobalOptions(runCmd.command("drive").description("Drive a run with live harness calls, or with the model-free simulation when --simulated is passed")).argument("<runId>", "Run id").option("--simulated", "Use the model-free simulation producer instead of a live model").option("--wait", "Wait until a drive receipt is recorded; exits 0 only for a VERIFIED COMPLETED settlement").option("--timeout-ms <n>", "Wait timeout in milliseconds (default 60000, max 600000)").action(async function runDriveAction(runId, options) {
48997
49231
  result.code = await cmdKxmRunDrive(runtimeFrom(ctx, this), runId, options.simulated === true, {
48998
49232
  wait: options.wait === true,
48999
49233
  ...options.timeoutMs !== void 0 ? { timeoutMs: options.timeoutMs } : {}
@@ -49013,6 +49247,11 @@ function createProgram(ctx, result) {
49013
49247
  addGlobalOptions(tenantCmd.command("status").description("Read hub metadata and authoritative Runtime run state as one labeled view")).action(async function tenantStatusAction() {
49014
49248
  result.code = await cmdTenantStatus(runtimeFrom(ctx, this));
49015
49249
  });
49250
+ const pricesCmd = addGlobalOptions(program2.command("prices").description("Stamp the local list-price catalog. Estimates stay unknown until today's stamp"));
49251
+ pricesCmd.helpCommand("help", "Show prices help");
49252
+ addGlobalOptions(pricesCmd.command("acknowledge").description("Stamp the existing .kxm/prices.yaml list as today's estimate without fetching vendor rates")).action(async function pricesAcknowledgeAction() {
49253
+ result.code = await cmdPricesAcknowledge(runtimeFrom(ctx, this));
49254
+ });
49016
49255
  const modelsCmd = addGlobalOptions(program2.command("models").description("Manage model catalogs, roles, and route state"));
49017
49256
  modelsCmd.action(async function modelsScreenAction() {
49018
49257
  result.code = await cmdModelsScreen(runtimeFrom(ctx, this));
@@ -49223,7 +49462,7 @@ function createProgram(ctx, result) {
49223
49462
  addGlobalOptions(github.command("watch").description("Poll required checks and post the signed signal")).option("--run-id <id>", "Workflow run ID").option("--stage-id <id>", "Waiting stage ID").option("--signal-key <key>", "Wait signal key").option("--repo <owner/name>", "GitHub repository").option("--pr <number>", "Pull request number").option("--required <names>", "Comma-separated required check names").option("--timeout-ms <ms>", "Watch timeout").option("--interval-ms <ms>", "Poll interval").option("--delivery-id <id>", "Stable callback delivery ID").action(async function watchAction(options) {
49224
49463
  result.code = await cmdGithubWatch(runtimeFrom(ctx, this), options);
49225
49464
  });
49226
- const improve = addGlobalOptions(program2.command("improve").description("Propose coded-repeat candidates from this project's Runtime routing records and telemetry"));
49465
+ const improve = addGlobalOptions(program2.command("improve").description("Propose coded-repeat candidates from this project's Runtime routing records and telemetry. Proposals are not applied to the next run"));
49227
49466
  improve.helpCommand("help", "Show improve help");
49228
49467
  addGlobalOptions(improve.command("report", { isDefault: true }).description("Generate improvement report and candidates from routing records")).option("--file <path>", "Read only this routing-record JSONL instead of the project's Runtime store and telemetry").option("--out-dir <path>", "Directory for candidates (default .kxm/candidates)").action(async function improveReportAction(options) {
49229
49468
  result.code = await cmdImprove(runtimeFrom(ctx, this), options);