@kontextmind/kxm 0.7.96 → 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 (68) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.kxm/workflows/default.yaml +2 -0
  3. package/CHANGELOG.md +61 -2
  4. package/docs/concepts/architecture.md +1 -1
  5. package/docs/concepts/data-and-storage.md +1 -1
  6. package/docs/concepts/trust-model.md +3 -2
  7. package/docs/contracts/routing.md +1 -1
  8. package/docs/contributing/test-matrix.md +6 -5
  9. package/docs/glossary.md +1 -1
  10. package/docs/guides/peer-messaging.md +2 -2
  11. package/docs/guides/pi-workers.md +1 -1
  12. package/docs/guides/webhook-workflows.md +53 -18
  13. package/docs/operations/backup-and-restore.md +43 -24
  14. package/docs/operations/deploy.md +2 -2
  15. package/docs/operations/troubleshooting.md +3 -2
  16. package/docs/reference/cli-reference.md +65 -30
  17. package/docs/reference/config-reference.md +24 -13
  18. package/docs/reference/configuration.md +2 -2
  19. package/docs/reference/harness-routing.md +3 -3
  20. package/docs/reference/http-api.md +7 -7
  21. package/docs/reference/tools.md +1 -1
  22. package/docs/reference/workflow-definitions.md +2 -2
  23. package/docs/start/first-workflow.md +4 -4
  24. package/docs/start/quickstart-claude-code.md +2 -2
  25. package/docs/start/quickstart-pi.md +1 -1
  26. package/examples/workflow-signal.ts +4 -5
  27. package/package.json +1 -1
  28. package/plugins/kxm/.claude-plugin/plugin.json +1 -1
  29. package/plugins/kxm/dist/claude-hook.js +11 -1
  30. package/plugins/kxm/dist/cli.js +552 -228
  31. package/plugins/kxm/dist/client.js +3 -1
  32. package/plugins/kxm/dist/core.js +16 -3
  33. package/plugins/kxm/dist/extension.js +45 -13
  34. package/plugins/kxm/dist/mcp-server.js +20 -4
  35. package/plugins/kxm/dist/runtime-supervisor.js +232 -51
  36. package/plugins/kxm/dist/runtime.js +432 -95
  37. package/plugins/kxm/dist/server.js +122 -20
  38. package/plugins/kxm/package.json +1 -1
  39. package/plugins/kxm/skills/kxm-hub-ops/SKILL.md +4 -3
  40. package/plugins/kxm/skills/kxm-project-setup/SKILL.md +3 -2
  41. package/plugins/kxm/skills/kxm-routing-improve/SKILL.md +3 -0
  42. package/plugins/kxm/skills/kxm-runs/SKILL.md +8 -7
  43. package/plugins/kxm/src/cli/project.ts +22 -13
  44. package/plugins/kxm/src/cli/system.ts +22 -1
  45. package/plugins/kxm/src/cli/workflows.ts +12 -7
  46. package/plugins/kxm/src/cli.ts +30 -8
  47. package/plugins/kxm/src/client.ts +4 -0
  48. package/plugins/kxm/src/commands.ts +23 -1
  49. package/plugins/kxm/src/database.ts +210 -36
  50. package/plugins/kxm/src/engine.ts +117 -2
  51. package/plugins/kxm/src/extension.ts +20 -14
  52. package/plugins/kxm/src/github-watch.ts +8 -5
  53. package/plugins/kxm/src/harness.ts +29 -0
  54. package/plugins/kxm/src/hub-env.ts +19 -1
  55. package/plugins/kxm/src/hub.ts +105 -21
  56. package/plugins/kxm/src/improve-sources.ts +2 -7
  57. package/plugins/kxm/src/init-guide-setup.ts +43 -28
  58. package/plugins/kxm/src/mcp-server.ts +9 -2
  59. package/plugins/kxm/src/oneshot-producer.ts +16 -8
  60. package/plugins/kxm/src/prices.ts +33 -2
  61. package/plugins/kxm/src/routing.ts +13 -7
  62. package/plugins/kxm/src/runtime-store.ts +23 -0
  63. package/plugins/kxm/src/studio-layout.ts +5 -4
  64. package/plugins/kxm/src/template.ts +31 -0
  65. package/plugins/kxm/src/workflow.ts +70 -1
  66. package/plugins/kxm/src/worktree-witness.ts +71 -0
  67. package/schemas/backup-manifest.schema.json +33 -0
  68. package/scripts/smoke-multi-pi.mjs +5 -1
@@ -164,10 +164,10 @@ var require_code = __commonJS({
164
164
  function interpolate(x) {
165
165
  return typeof x == "number" || typeof x == "boolean" || x === null ? x : safeStringify(Array.isArray(x) ? x.join(",") : x);
166
166
  }
167
- function stringify4(x) {
167
+ function stringify5(x) {
168
168
  return new _Code(safeStringify(x));
169
169
  }
170
- exports.stringify = stringify4;
170
+ exports.stringify = stringify5;
171
171
  function safeStringify(x) {
172
172
  return JSON.stringify(x).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 stringify4(item, ctx, onComment, onChompKeep) {
8921
+ function stringify5(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 = stringify4;
8950
+ exports.stringify = stringify5;
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 stringify4 = require_stringify();
8960
+ var stringify5 = 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 = stringify4.stringify(key, ctx, () => keyCommentDone = true, () => chompKeep = true);
8982
+ let str = stringify5.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 = stringify4.stringify(value, ctx, () => valueCommentDone = true, () => chompKeep = true);
9034
+ const valueStr = stringify5.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 stringify4 = require_stringify();
9175
+ var stringify5 = 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 = stringify4.createStringifyContext(ctx.doc, {});
9211
+ const strCtx = stringify5.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 stringify4 = require_stringify();
9278
+ var stringify5 = 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 stringify5 = flow ? stringifyFlowCollection : stringifyBlockCollection;
9283
- return stringify5(collection, ctx, options);
9282
+ const stringify6 = flow ? stringifyFlowCollection : stringifyBlockCollection;
9283
+ return stringify6(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 = stringify4.stringify(item, itemCtx, () => comment2 = null, () => chompKeep = true);
9308
+ let str2 = stringify5.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 = stringify4.stringify(item, itemCtx, () => comment = null);
9375
+ let str = stringify5.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 stringify4 = require_stringify();
10736
+ var stringify5 = 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 = stringify4.createStringifyContext(doc, options);
10751
+ const ctx = stringify5.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 = stringify4.stringify(doc.contents, ctx, () => contentComment = null, onChompKeep);
10773
+ let body = stringify5.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(stringify4.stringify(doc.contents, ctx));
10781
+ lines.push(stringify5.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 stringify4 = (cst) => "type" in cst ? stringifyToken(cst) : stringifyItem(cst);
12917
+ var stringify5 = (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 = stringify4;
12970
+ exports.stringify = stringify5;
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 stringify4(value, replacer, options) {
14700
+ function stringify5(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 = parse7;
14723
14723
  exports.parseAllDocuments = parseAllDocuments;
14724
14724
  exports.parseDocument = parseDocument2;
14725
- exports.stringify = stringify4;
14725
+ exports.stringify = stringify5;
14726
14726
  }
14727
14727
  });
14728
14728
 
@@ -14781,7 +14781,7 @@ var require_dist = __commonJS({
14781
14781
  // plugins/kxm/src/runtime-supervisor.ts
14782
14782
  import { spawn as spawn3 } from "node:child_process";
14783
14783
  import { createHash as createHash13, createHmac, randomBytes as randomBytes2, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
14784
- import { chmodSync as chmodSync2, existsSync as existsSync15, lstatSync as lstatSync5, mkdirSync as mkdirSync9, readFileSync as readFileSync12, renameSync as renameSync5, rmSync as rmSync4, writeFileSync as writeFileSync8 } from "node:fs";
14784
+ import { chmodSync as chmodSync2, existsSync as existsSync15, lstatSync as lstatSync5, mkdirSync as mkdirSync9, readFileSync as readFileSync12, renameSync as renameSync5, rmSync as rmSync4, writeFileSync as writeFileSync9 } from "node:fs";
14785
14785
  import { createServer } from "node:http";
14786
14786
  import { dirname as dirname11, isAbsolute as isAbsolute6, join as join18 } from "node:path";
14787
14787
 
@@ -15078,6 +15078,35 @@ function coreTemplate(projectId, projectName, variant) {
15078
15078
  }]
15079
15079
  ]);
15080
15080
  if (variant === "v4-registry") {
15081
+ const coordinator = files.get(".kxm/agents/coordinator.yaml");
15082
+ if (coordinator) {
15083
+ files.set(".kxm/agents/coordinator.yaml", {
15084
+ ...coordinator,
15085
+ harness: "claude",
15086
+ model: { provider: "anthropic", model: "fable" }
15087
+ });
15088
+ }
15089
+ const implementer = files.get(".kxm/agents/implementer.yaml");
15090
+ if (implementer) {
15091
+ files.set(".kxm/agents/implementer.yaml", {
15092
+ ...implementer,
15093
+ harness: "grok",
15094
+ model: { provider: "xai", model: "grok-4.6" }
15095
+ });
15096
+ }
15097
+ const workflow = files.get(".kxm/workflows/default.yaml");
15098
+ if (workflow) {
15099
+ const limits = { ...workflow.limits };
15100
+ delete limits.maxAgentTimeMs;
15101
+ files.set(".kxm/workflows/default.yaml", { ...workflow, limits });
15102
+ }
15103
+ files.set(".kxm/routes.yaml", {
15104
+ schema: "kxm.routes.v2",
15105
+ updatedAt: "2026-09-23T00:00:00.000Z",
15106
+ admitted: ["anthropic/fable", "xai/grok-4.6"],
15107
+ disabled: [],
15108
+ roles: { implementer: ["xai/grok-4.6"] }
15109
+ });
15081
15110
  files.set(".kxm/gates.yaml", {
15082
15111
  schema: "kxm.gate-registry.v1",
15083
15112
  gates: { test: { kind: "command", argv: ["npm", "test"], timeoutMs: 36e5 } }
@@ -15604,6 +15633,16 @@ var READ_ONLY_ONESHOT_ARGS = Object.freeze({
15604
15633
  function oneShotReadOnlyArgs(harness) {
15605
15634
  return Object.hasOwn(READ_ONLY_ONESHOT_ARGS, harness) ? READ_ONLY_ONESHOT_ARGS[harness] : void 0;
15606
15635
  }
15636
+ var WRITER_ONESHOT_ARGS = Object.freeze({
15637
+ pi: Object.freeze(["-a", "--no-extensions", "--no-skills", "--no-prompt-templates", "--no-session"]),
15638
+ grok: Object.freeze(["--always-approve", "--no-subagents", "--disable-web-search"])
15639
+ });
15640
+ function oneShotWriterArgs(harness) {
15641
+ return Object.hasOwn(WRITER_ONESHOT_ARGS, harness) ? WRITER_ONESHOT_ARGS[harness] : void 0;
15642
+ }
15643
+ function oneShotPermissionArgs(harness, permission) {
15644
+ return permission === "edit" ? oneShotWriterArgs(harness) : oneShotReadOnlyArgs(harness);
15645
+ }
15607
15646
  var BUILTIN_HARNESSES = Object.freeze([
15608
15647
  {
15609
15648
  id: "pi",
@@ -17440,9 +17479,6 @@ import { createHash as createHash4, randomUUID } from "node:crypto";
17440
17479
  import { existsSync as existsSync5, lstatSync as lstatSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, realpathSync as realpathSync2, writeFileSync as writeFileSync2 } from "node:fs";
17441
17480
  import { dirname as dirname5, join as join5, resolve as resolve4 } from "node:path";
17442
17481
 
17443
- // plugins/kxm/src/bindings.ts
17444
- import { homedir } from "node:os";
17445
-
17446
17482
  // plugins/kxm/src/sqlite.ts
17447
17483
  import { createRequire } from "node:module";
17448
17484
  var requireFromHere = createRequire(import.meta.url);
@@ -17483,6 +17519,7 @@ var DatabaseSync = class {
17483
17519
  };
17484
17520
 
17485
17521
  // plugins/kxm/src/bindings.ts
17522
+ import { homedir } from "node:os";
17486
17523
  import { dirname as dirname3, isAbsolute as isAbsolute2, join as join3, parse, relative as relative2, resolve as resolve2, sep as sep2 } from "node:path";
17487
17524
  var MAX_BINDING_RECORD_BYTES = 256 * 1024;
17488
17525
  var BINDING_LABEL = "Runtime-local repository bindings";
@@ -23148,7 +23185,7 @@ async function beginOneShotEvidence(root, intent, sensitive) {
23148
23185
  // plugins/kxm/src/prices.ts
23149
23186
  var import_yaml3 = __toESM(require_dist(), 1);
23150
23187
  import { createHash as createHash8 } from "node:crypto";
23151
- import { existsSync as existsSync7, readFileSync as readFileSync5, statSync } from "node:fs";
23188
+ import { existsSync as existsSync7, readFileSync as readFileSync5, statSync, writeFileSync as writeFileSync3 } from "node:fs";
23152
23189
  import { join as join8 } from "node:path";
23153
23190
 
23154
23191
  // plugins/kxm/src/price-calc.ts
@@ -23317,7 +23354,7 @@ import { join as join14 } from "node:path";
23317
23354
 
23318
23355
  // plugins/kxm/src/routes.ts
23319
23356
  var import_yaml4 = __toESM(require_dist(), 1);
23320
- import { existsSync as existsSync8, readFileSync as readFileSync6, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3, readdirSync as readdirSync4 } from "node:fs";
23357
+ import { existsSync as existsSync8, readFileSync as readFileSync6, mkdirSync as mkdirSync3, writeFileSync as writeFileSync4, readdirSync as readdirSync4 } from "node:fs";
23321
23358
  import { join as join9 } from "node:path";
23322
23359
  var RETIRED_POLICY = ".kxm/producers.yaml";
23323
23360
  var empty = () => ({ schema: "kxm.routes.v2", updatedAt: (/* @__PURE__ */ new Date()).toISOString(), admitted: [], disabled: [], roles: {} });
@@ -23345,6 +23382,55 @@ function isRouteAdmitted(root, modelId) {
23345
23382
  return policy.admitted.includes(modelId) && !policy.disabled.includes(modelId);
23346
23383
  }
23347
23384
 
23385
+ // plugins/kxm/src/worktree-witness.ts
23386
+ import { spawnSync as spawnSync3 } from "node:child_process";
23387
+ function gitText(cwd, args) {
23388
+ const result = spawnSync3("git", ["-C", cwd, ...args], {
23389
+ encoding: "utf8",
23390
+ windowsHide: true,
23391
+ timeout: 15e3
23392
+ });
23393
+ if (result.error || result.status !== 0) return void 0;
23394
+ return result.stdout ?? "";
23395
+ }
23396
+ function captureWorktreeWitness(cwd) {
23397
+ const porcelain = gitText(cwd, ["status", "--porcelain=v1", "-uall"]);
23398
+ const diff = gitText(cwd, ["diff", "--no-ext-diff"]);
23399
+ const staged = gitText(cwd, ["diff", "--cached", "--no-ext-diff"]);
23400
+ if (porcelain === void 0 || diff === void 0 || staged === void 0) {
23401
+ return { unwitnessed: true, fingerprint: "" };
23402
+ }
23403
+ return { unwitnessed: false, fingerprint: `${porcelain}\0${diff}\0${staged}` };
23404
+ }
23405
+ function worktreeChanged(before, after) {
23406
+ if (before.unwitnessed || after.unwitnessed) return false;
23407
+ return before.fingerprint !== after.fingerprint;
23408
+ }
23409
+ function applyAuthoringWitness(result, input) {
23410
+ const changed = worktreeChanged(input.before, input.after);
23411
+ const unwitnessed = input.before.unwitnessed || input.after.unwitnessed;
23412
+ const providerMetadata = { ...result.providerMetadata ?? {} };
23413
+ if (input.writes) {
23414
+ if (unwitnessed || !changed) {
23415
+ providerMetadata.authored = false;
23416
+ providerMetadata.authoringWitness = unwitnessed ? "unwitnessed" : "unchanged";
23417
+ if (result.outcome === "passed") return { ...result, outcome: "failed", providerMetadata };
23418
+ } else {
23419
+ providerMetadata.authored = true;
23420
+ providerMetadata.authoringWitness = "changed";
23421
+ }
23422
+ } else {
23423
+ providerMetadata.authored = false;
23424
+ if (!unwitnessed && changed) {
23425
+ providerMetadata.authoringWitness = "readonly_mutated";
23426
+ if (result.outcome === "passed") return { ...result, outcome: "failed", providerMetadata };
23427
+ } else {
23428
+ providerMetadata.authoringWitness = unwitnessed ? "unwitnessed" : "read-only";
23429
+ }
23430
+ }
23431
+ return { ...result, providerMetadata };
23432
+ }
23433
+
23348
23434
  // plugins/kxm/src/context-packet.ts
23349
23435
  import { randomUUID as randomUUID4 } from "node:crypto";
23350
23436
  var CONTEXT_PACKET_SCHEMA = "kxm.context-packet.v2";
@@ -23612,7 +23698,7 @@ function pruneContextPacket(packet, tokenBudget) {
23612
23698
  }
23613
23699
 
23614
23700
  // plugins/kxm/src/dispatch-context.ts
23615
- import { spawnSync as spawnSync3 } from "node:child_process";
23701
+ import { spawnSync as spawnSync4 } from "node:child_process";
23616
23702
  import { readdirSync as readdirSync7 } from "node:fs";
23617
23703
  import { extname as extname3, join as join12 } from "node:path";
23618
23704
 
@@ -24243,7 +24329,7 @@ function memoryRecordToContextItem(record2, project) {
24243
24329
 
24244
24330
  // plugins/kxm/src/memory.ts
24245
24331
  var import_yaml5 = __toESM(require_dist(), 1);
24246
- import { existsSync as existsSync9, mkdirSync as mkdirSync4, readdirSync as readdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "node:fs";
24332
+ import { existsSync as existsSync9, mkdirSync as mkdirSync4, readdirSync as readdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "node:fs";
24247
24333
  import { extname as extname2, join as join10, resolve as resolve5 } from "node:path";
24248
24334
  var MEMORY_SCHEMA = "kxm.memory.v1";
24249
24335
  var VALID_SCOPES = /* @__PURE__ */ new Set(["agent", "project", "run", "operator"]);
@@ -24372,7 +24458,7 @@ function loadAuthoredMemory(repoRoot) {
24372
24458
  // plugins/kxm/src/skills.ts
24373
24459
  var import_yaml6 = __toESM(require_dist(), 1);
24374
24460
  import { createHash as createHash9 } from "node:crypto";
24375
- import { existsSync as existsSync10, mkdirSync as mkdirSync5, readdirSync as readdirSync6, readFileSync as readFileSync8, renameSync, rmSync, statSync as statSync2, writeFileSync as writeFileSync5 } from "node:fs";
24461
+ import { existsSync as existsSync10, mkdirSync as mkdirSync5, readdirSync as readdirSync6, readFileSync as readFileSync8, renameSync, rmSync, statSync as statSync2, writeFileSync as writeFileSync6 } from "node:fs";
24376
24462
  import { dirname as dirname7, join as join11 } from "node:path";
24377
24463
  var SKILL_CANDIDATE_SCHEMA = "kxm.skill-candidate.v1";
24378
24464
  var SKILL_EVALUATION_SCHEMA = "kxm.skill-evaluation.v1";
@@ -24476,7 +24562,7 @@ var SkillLifecycle = class {
24476
24562
  return;
24477
24563
  }
24478
24564
  mkdirSync5(dirname7(file), { recursive: true });
24479
- writeFileSync5(file, content);
24565
+ writeFileSync6(file, content);
24480
24566
  }
24481
24567
  dir(state) {
24482
24568
  return join11(this.root, state === "candidate" ? "candidates" : `${state}s`.replace("rejecteds", "rejected").replace("promoteds", "promoted"));
@@ -24813,7 +24899,7 @@ function gitEnvironment2() {
24813
24899
  return Object.fromEntries(Object.entries(process.env).filter(([name]) => !name.toUpperCase().startsWith("GIT_")));
24814
24900
  }
24815
24901
  function committedGateGap(projectRoot) {
24816
- const result = spawnSync3("git", [
24902
+ const result = spawnSync4("git", [
24817
24903
  "--no-optional-locks",
24818
24904
  "-C",
24819
24905
  projectRoot,
@@ -27082,6 +27168,14 @@ function prepareDispatch(context, runId, producerId, dispatchSources) {
27082
27168
  return { kind: "return", state, handoff: { ...routeResult.error, stepId } };
27083
27169
  }
27084
27170
  resolvedRoute = routeResult;
27171
+ const writeRefusal = unsupportedLiveWrite(
27172
+ context.projectRoot,
27173
+ step,
27174
+ agentId,
27175
+ resolvedRoute.selector,
27176
+ loadKxmRunPlanEnvelope(context.eventStore, run).projectLimits.maxConcurrentRuns
27177
+ );
27178
+ if (writeRefusal) return { kind: "return", state, handoff: { ...writeRefusal, stepId } };
27085
27179
  }
27086
27180
  const used = state.stepAttempts[stepId] ?? 0;
27087
27181
  if (used >= step.maxAttempts) {
@@ -27290,6 +27384,7 @@ function birthMember(context, input) {
27290
27384
 
27291
27385
  ${generatedPrompt}` : generatedPrompt,
27292
27386
  thinking: input.stepAttempt <= 1 ? "low" : "medium",
27387
+ permission: Object.values(input.step.repositories).some((access) => access === "write") ? "edit" : "read-only",
27293
27388
  contextPacket,
27294
27389
  ...resolvedRoute ? { provider: resolvedRoute.provider, model: resolvedRoute.model } : {}
27295
27390
  },
@@ -27416,7 +27511,18 @@ async function drivePanel(context, panel, producer) {
27416
27511
  if (!executingBound()) return { attemptId: member.attemptId, invoked: false, skipped: true };
27417
27512
  kxmPanelDispatchSeams.beforeInvoke?.(member);
27418
27513
  if (!executingBound()) return { attemptId: member.attemptId, invoked: false, skipped: true };
27514
+ const live = member.producerId !== "driver-simulated";
27515
+ const writes = Object.values(member.step.repositories).some((access) => access === "write");
27516
+ const before = live ? captureWorktreeWitness(context.projectRoot) : void 0;
27419
27517
  const produced = await invokeProducer(producer, member.request);
27518
+ if (live && produced.result && before) {
27519
+ const after = captureWorktreeWitness(context.projectRoot);
27520
+ return {
27521
+ attemptId: member.attemptId,
27522
+ invoked: true,
27523
+ produced: { ...produced, result: applyAuthoringWitness(produced.result, { writes, before, after }) }
27524
+ };
27525
+ }
27420
27526
  return { attemptId: member.attemptId, invoked: true, produced };
27421
27527
  } catch (error) {
27422
27528
  member.controller.abort();
@@ -28054,11 +28160,78 @@ function unsupportedStep(plan, step, producerId) {
28054
28160
  return { reason: "step_unsupported", field: "repositories", detail: `invalid repository access '${access}' on ${repoId}` };
28055
28161
  }
28056
28162
  }
28057
- if (producerId !== "driver-simulated" && Object.values(step.repositories).some((access) => access === "write")) {
28163
+ return void 0;
28164
+ }
28165
+ function readYamlRecord(path) {
28166
+ if (!existsSync11(path)) return void 0;
28167
+ try {
28168
+ const parsed = (0, import_yaml7.parse)(readFileSync9(path, "utf8"));
28169
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
28170
+ } catch {
28171
+ return void 0;
28172
+ }
28173
+ return void 0;
28174
+ }
28175
+ function agentHarness(projectRoot, agentId) {
28176
+ const harness = readYamlRecord(join14(projectRoot, ".kxm", "agents", `${agentId}.yaml`))?.harness;
28177
+ return typeof harness === "string" && harness.length > 0 ? harness : void 0;
28178
+ }
28179
+ function projectDefaultHarness(projectRoot) {
28180
+ const harness = readYamlRecord(join14(projectRoot, ".kxm", "project.yaml"))?.defaultHarness;
28181
+ return typeof harness === "string" && harness.length > 0 ? harness : "pi";
28182
+ }
28183
+ function unsupportedLiveWrite(projectRoot, step, agentId, selector, maxConcurrentRuns) {
28184
+ if (!Object.values(step.repositories).some((access) => access === "write")) return void 0;
28185
+ if (step.assignments.maximum !== 1) {
28186
+ return {
28187
+ reason: "step_unsupported",
28188
+ field: "assignments.maximum",
28189
+ detail: "live write steps run a single assignment; the checkout witness cannot attribute edits between writers"
28190
+ };
28191
+ }
28192
+ if (maxConcurrentRuns !== 1) {
28193
+ return {
28194
+ reason: "step_unsupported",
28195
+ field: "limits.maxConcurrentRuns",
28196
+ detail: "live write steps require limits.maxConcurrentRuns of 1; concurrent runs share one checkout"
28197
+ };
28198
+ }
28199
+ const harness = agentHarness(projectRoot, agentId) ?? projectDefaultHarness(projectRoot);
28200
+ if (!oneShotWriterArgs(harness)) {
28058
28201
  return {
28059
28202
  reason: "step_unsupported",
28060
28203
  field: "repositories",
28061
- detail: "live write steps are unsupported until writer sandboxing witness passes"
28204
+ detail: `live write steps require an audited writer profile; ${harness} has none`
28205
+ };
28206
+ }
28207
+ const rosterPath = join14(projectRoot, ".kxm", "roster.yaml");
28208
+ if (!existsSync11(rosterPath)) return void 0;
28209
+ const roster = readYamlRecord(rosterPath);
28210
+ if (!roster || roster.schema !== "kxm.developer-roster.v1") {
28211
+ return { reason: "step_unsupported", field: "model", detail: "live write steps require a readable kxm.developer-roster.v1" };
28212
+ }
28213
+ const routes = roster.routes;
28214
+ const lineup = roster.lineup;
28215
+ const writerIds = lineup && typeof lineup === "object" && !Array.isArray(lineup) ? lineup.writer : void 0;
28216
+ if (!routes || typeof routes !== "object" || Array.isArray(routes) || !Array.isArray(writerIds)) {
28217
+ return { reason: "step_unsupported", field: "model", detail: "developer roster has no writer lineup" };
28218
+ }
28219
+ const allowed = writerIds.some((id) => {
28220
+ if (typeof id !== "string") return false;
28221
+ const route = routes[id];
28222
+ if (!route || typeof route !== "object" || Array.isArray(route)) return false;
28223
+ const record2 = route;
28224
+ if (record2.harness !== harness || record2.status !== "admitted") return false;
28225
+ if (!Array.isArray(record2.permissions) || !record2.permissions.includes("edit")) return false;
28226
+ const model = typeof record2.model === "string" ? record2.model : "";
28227
+ const vendor = typeof record2.vendor === "string" ? record2.vendor : "";
28228
+ return model === selector || vendor.length > 0 && `${vendor}/${model}` === selector;
28229
+ });
28230
+ if (!allowed) {
28231
+ return {
28232
+ reason: "step_unsupported",
28233
+ field: "model",
28234
+ detail: `live write route ${selector} on ${harness} is not on the developer roster writer lineup`
28062
28235
  };
28063
28236
  }
28064
28237
  return void 0;
@@ -28524,10 +28697,10 @@ function createKxmOneShotProducer(options = {}) {
28524
28697
  if (request.provider) {
28525
28698
  return { provider: request.provider.toLowerCase(), model: request.model, thinking: request.thinking };
28526
28699
  }
28527
- const parsed2 = parseModelString(request.model, harness);
28700
+ const parsed = parseModelString(request.model, harness);
28528
28701
  return {
28529
- provider: request.provider?.toLowerCase() ?? parsed2.provider,
28530
- model: parsed2.model,
28702
+ provider: request.provider?.toLowerCase() ?? parsed.provider,
28703
+ model: parsed.model,
28531
28704
  thinking: request.thinking
28532
28705
  };
28533
28706
  }
@@ -28537,17 +28710,19 @@ function createKxmOneShotProducer(options = {}) {
28537
28710
  if (resolved.provider) {
28538
28711
  return { provider: resolved.provider.toLowerCase(), model: resolved.model, thinking: resolved.thinking };
28539
28712
  }
28540
- const parsed2 = parseModelString(resolved.model, harness);
28713
+ const parsed = parseModelString(resolved.model, harness);
28541
28714
  return {
28542
- provider: resolved.provider?.toLowerCase() ?? parsed2.provider,
28543
- model: parsed2.model,
28715
+ provider: resolved.provider?.toLowerCase() ?? parsed.provider,
28716
+ model: parsed.model,
28544
28717
  thinking: resolved.thinking
28545
28718
  };
28546
28719
  }
28547
28720
  }
28548
- const defaultModel = options.defaultModel ?? (harness === "codex" ? "gpt-5.6-sol" : harness === "kimi" ? "kimi-for-coding" : harness === "agy" ? "gemini-3.8-flash-high" : "claude-3-7-sonnet");
28549
- const parsed = parseModelString(defaultModel, harness);
28550
- return { provider: parsed.provider, model: parsed.model, thinking: request.thinking };
28721
+ if (options.defaultModel) {
28722
+ const parsed = parseModelString(options.defaultModel, harness);
28723
+ return { provider: parsed.provider, model: parsed.model, thinking: request.thinking };
28724
+ }
28725
+ throw new Error("producer_route_not_admitted");
28551
28726
  }
28552
28727
  async function checkAuth(harness, provider, model, env, signal) {
28553
28728
  if (options.inventory) {
@@ -28585,8 +28760,11 @@ function createKxmOneShotProducer(options = {}) {
28585
28760
  if (request.signal.aborted) return cancelled();
28586
28761
  const catalogEntry = (options.catalog ?? BUILTIN_HARNESSES).find((h) => h.id === harness);
28587
28762
  if (!catalogEntry?.oneShot) throw new Error(`oneshot_harness_unsupported: ${harness}`);
28588
- const permissionArgs = oneShotReadOnlyArgs(harness);
28589
- if (!permissionArgs) throw new Error(`oneshot_harness_unsupported: ${harness} permission_profile_unaudited`);
28763
+ const permission = request.permission === "edit" || request.contextPacket?.task.permissionCeiling === "edit" ? "edit" : "read-only";
28764
+ const permissionArgs = oneShotPermissionArgs(harness, permission);
28765
+ if (!permissionArgs) {
28766
+ throw new Error(`oneshot_harness_unsupported: ${harness} ${permission === "edit" ? "writer_profile_unaudited" : "permission_profile_unaudited"}`);
28767
+ }
28590
28768
  const env = { ...options.env ?? process.env };
28591
28769
  let auth;
28592
28770
  try {
@@ -28702,7 +28880,10 @@ Return a final JSON object with an "outcome" field chosen from ${JSON.stringify(
28702
28880
  const outcome = aborted ? "cancelled" : transportFailed || parsed.isError ? "failed" : determineOutcome(parsed.text, request.allowedOutcomes);
28703
28881
  const providerMetadata = {
28704
28882
  processStatus: aborted ? "aborted" : transportFailed ? "failed" : "completed",
28705
- executionEvidenceId: evidence.id
28883
+ executionEvidenceId: evidence.id,
28884
+ permission,
28885
+ permissionProfile: permission === "edit" ? "writer" : "read-only",
28886
+ authored: false
28706
28887
  };
28707
28888
  if (procResult.code !== null && Number.isFinite(procResult.code)) providerMetadata.processExitCode = procResult.code;
28708
28889
  if (procResult.signal) providerMetadata.processSignal = procResult.signal;
@@ -28905,7 +29086,7 @@ var RuntimeHubClient = class {
28905
29086
  };
28906
29087
 
28907
29088
  // plugins/kxm/src/hub-binding.ts
28908
- import { existsSync as existsSync12, mkdirSync as mkdirSync6, readFileSync as readFileSync10, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "node:fs";
29089
+ import { existsSync as existsSync12, mkdirSync as mkdirSync6, readFileSync as readFileSync10, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync7 } from "node:fs";
28909
29090
  import { homedir as homedir2 } from "node:os";
28910
29091
  import { dirname as dirname8, isAbsolute as isAbsolute4, join as join16, resolve as resolve7 } from "node:path";
28911
29092
  var HUB_BINDING_SCHEMA = "kxm.hub-binding.v1";
@@ -28977,7 +29158,7 @@ function readHubBinding(env = process.env) {
28977
29158
  }
28978
29159
 
28979
29160
  // plugins/kxm/src/hub-env.ts
28980
- import { existsSync as existsSync13, mkdirSync as mkdirSync7, readFileSync as readFileSync11, renameSync as renameSync3, rmSync as rmSync3, writeFileSync as writeFileSync7 } from "node:fs";
29161
+ import { existsSync as existsSync13, mkdirSync as mkdirSync7, readFileSync as readFileSync11, renameSync as renameSync3, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "node:fs";
28981
29162
  import { homedir as homedir3 } from "node:os";
28982
29163
  import { dirname as dirname9, isAbsolute as isAbsolute5, join as join17, resolve as resolve8 } from "node:path";
28983
29164
  var HUB_ENV_SCHEMA = "kxm.hub-env.v1";
@@ -29207,7 +29388,7 @@ function publishKxmSupervisorToken(paths, token) {
29207
29388
  const file = kxmSupervisorTokenFile(paths);
29208
29389
  mkdirSync9(dirname11(file), { recursive: true, mode: 448 });
29209
29390
  const temp = `${file}.${process.pid}.tmp`;
29210
- writeFileSync8(temp, `${token}
29391
+ writeFileSync9(temp, `${token}
29211
29392
  `, { encoding: "utf8", mode: 384 });
29212
29393
  try {
29213
29394
  chmodSync2(temp, 384);
@@ -29252,7 +29433,7 @@ function clearSupervisorError(paths) {
29252
29433
  function recordSupervisorError(paths, message) {
29253
29434
  try {
29254
29435
  mkdirSync9(paths.runtimeDir, { recursive: true, mode: 448 });
29255
- writeFileSync8(supervisorErrorFile(paths), `${message}
29436
+ writeFileSync9(supervisorErrorFile(paths), `${message}
29256
29437
  `, { encoding: "utf8", mode: 384 });
29257
29438
  } catch {
29258
29439
  }