@kontextmind/kxm 0.7.92 → 0.7.93

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 (91) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.kxm/workflows/default.yaml +1 -1
  3. package/CHANGELOG.md +204 -0
  4. package/README.md +3 -0
  5. package/docs/README.md +3 -0
  6. package/docs/agent-skills.md +123 -60
  7. package/docs/architecture.md +5 -2
  8. package/docs/cli-reference.md +3527 -0
  9. package/docs/config-reference.md +1943 -0
  10. package/docs/configuration.md +29 -3
  11. package/docs/continuous-improvement.md +122 -10
  12. package/docs/contracts/routing.md +95 -11
  13. package/docs/harness-routing.md +616 -0
  14. package/docs/kxm-handbook.md +106 -19
  15. package/docs/templates/README.md +1 -1
  16. package/docs/test-matrix.md +12 -6
  17. package/docs/troubleshooting.md +2 -2
  18. package/examples/project/.kxm/workflows/fix.yaml +1 -1
  19. package/examples/project/.kxm/workflows/improve.yaml +1 -1
  20. package/package.json +1 -1
  21. package/plugins/kxm/.claude-plugin/plugin.json +9 -10
  22. package/plugins/kxm/README.md +238 -56
  23. package/plugins/kxm/dist/claude-hook.js +10083 -0
  24. package/plugins/kxm/dist/cli.js +3068 -2446
  25. package/plugins/kxm/dist/client.js +64 -0
  26. package/plugins/kxm/dist/core.js +102 -9
  27. package/plugins/kxm/dist/extension.js +210 -68
  28. package/plugins/kxm/dist/mcp-server.js +217 -40
  29. package/plugins/kxm/dist/runtime-supervisor.js +1628 -157
  30. package/plugins/kxm/dist/runtime.js +1874 -298
  31. package/plugins/kxm/dist/server.js +416 -82
  32. package/plugins/kxm/package.json +1 -1
  33. package/plugins/kxm/skills/hints.json +1 -1
  34. package/plugins/kxm/skills/kxm/SKILL.md +48 -24
  35. package/plugins/kxm/skills/kxm/references/protocol.md +3 -3
  36. package/plugins/kxm/skills/kxm-context-memory/SKILL.md +61 -21
  37. package/plugins/kxm/skills/kxm-definitions/SKILL.md +9 -0
  38. package/plugins/kxm/skills/kxm-harness-auth/SKILL.md +82 -16
  39. package/plugins/kxm/skills/kxm-harvest/SKILL.md +1 -1
  40. package/plugins/kxm/skills/kxm-hub-ops/SKILL.md +55 -27
  41. package/plugins/kxm/skills/kxm-insights/SKILL.md +1 -1
  42. package/plugins/kxm/skills/kxm-mind/SKILL.md +2 -2
  43. package/plugins/kxm/skills/{kxm-setup → kxm-mind-setup}/SKILL.md +4 -4
  44. package/plugins/kxm/skills/kxm-peer/SKILL.md +68 -93
  45. package/plugins/kxm/skills/kxm-project-setup/SKILL.md +156 -23
  46. package/plugins/kxm/skills/kxm-projects/SKILL.md +1 -1
  47. package/plugins/kxm/skills/kxm-protocol/SKILL.md +1 -1
  48. package/plugins/kxm/skills/kxm-query/SKILL.md +1 -1
  49. package/plugins/kxm/skills/kxm-routing-improve/SKILL.md +74 -15
  50. package/plugins/kxm/skills/kxm-runs/SKILL.md +46 -17
  51. package/plugins/kxm/skills/kxm-session/SKILL.md +64 -36
  52. package/plugins/kxm/skills/kxm-skill-lifecycle/SKILL.md +44 -15
  53. package/plugins/kxm/skills/kxm-tasks/SKILL.md +16 -4
  54. package/plugins/kxm/skills/kxm-triage/SKILL.md +1 -1
  55. package/plugins/kxm/skills/kxm-work/SKILL.md +1 -1
  56. package/plugins/kxm/skills/kxm-workflow/SKILL.md +60 -19
  57. package/plugins/kxm/src/arbiter.ts +67 -22
  58. package/plugins/kxm/src/autocomplete.ts +1 -1
  59. package/plugins/kxm/src/claude-hook.ts +192 -0
  60. package/plugins/kxm/src/cli/project.ts +11 -5
  61. package/plugins/kxm/src/cli/system.ts +85 -13
  62. package/plugins/kxm/src/cli/types.ts +4 -1
  63. package/plugins/kxm/src/cli/workflows.ts +18 -16
  64. package/plugins/kxm/src/cli.ts +23 -13
  65. package/plugins/kxm/src/client.ts +15 -4
  66. package/plugins/kxm/src/commands.ts +19 -9
  67. package/plugins/kxm/src/config.ts +42 -7
  68. package/plugins/kxm/src/context-packet.ts +14 -2
  69. package/plugins/kxm/src/context.ts +16 -5
  70. package/plugins/kxm/src/dispatch-context.ts +286 -0
  71. package/plugins/kxm/src/engine-plan.ts +40 -0
  72. package/plugins/kxm/src/engine.ts +138 -6
  73. package/plugins/kxm/src/hub-env.ts +17 -1
  74. package/plugins/kxm/src/hub.ts +92 -29
  75. package/plugins/kxm/src/improve-sources.ts +228 -0
  76. package/plugins/kxm/src/improve.ts +325 -140
  77. package/plugins/kxm/src/local-snapshot.ts +101 -42
  78. package/plugins/kxm/src/mcp-server.ts +129 -30
  79. package/plugins/kxm/src/project-config.ts +25 -0
  80. package/plugins/kxm/src/protocol.ts +11 -0
  81. package/plugins/kxm/src/relevance.ts +138 -0
  82. package/plugins/kxm/src/retrospective.ts +16 -10
  83. package/plugins/kxm/src/runtime-service.ts +8 -1
  84. package/plugins/kxm/src/runtime-supervisor.ts +16 -2
  85. package/plugins/kxm/src/session-token-hint.ts +17 -0
  86. package/plugins/kxm/src/suggest.ts +7 -7
  87. package/plugins/kxm/src/workflow-manager.ts +80 -78
  88. package/plugins/kxm/src/workflow.ts +202 -12
  89. package/scripts/build-runtime.mjs +7 -1
  90. package/scripts/check-generated.mjs +1 -0
  91. package/scripts/emit-codex-artifacts.mjs +1 -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 stringify3(x) {
167
+ function stringify4(x) {
168
168
  return new _Code(safeStringify(x));
169
169
  }
170
- exports.stringify = stringify3;
170
+ exports.stringify = stringify4;
171
171
  function safeStringify(x) {
172
172
  return JSON.stringify(x).replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
173
173
  }
@@ -2987,7 +2987,7 @@ var require_compile = __commonJS({
2987
2987
  const schOrFunc = root.refs[ref];
2988
2988
  if (schOrFunc)
2989
2989
  return schOrFunc;
2990
- let _sch = resolve8.call(this, root, ref);
2990
+ let _sch = resolve9.call(this, root, ref);
2991
2991
  if (_sch === void 0) {
2992
2992
  const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
2993
2993
  const { schemaId } = this.opts;
@@ -3014,7 +3014,7 @@ var require_compile = __commonJS({
3014
3014
  function sameSchemaEnv(s1, s2) {
3015
3015
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
3016
3016
  }
3017
- function resolve8(root, ref) {
3017
+ function resolve9(root, ref) {
3018
3018
  let sch;
3019
3019
  while (typeof (sch = this.refs[ref]) == "string")
3020
3020
  ref = sch;
@@ -3835,11 +3835,11 @@ var require_fast_uri = __commonJS({
3835
3835
  normalizeString(uri, options);
3836
3836
  } else if (typeof uri === "object") {
3837
3837
  uri = /** @type {T} */
3838
- parse5(serialize(uri, options), options);
3838
+ parse7(serialize(uri, options), options);
3839
3839
  }
3840
3840
  return uri;
3841
3841
  }
3842
- function resolve8(baseURI, relativeURI, options) {
3842
+ function resolve9(baseURI, relativeURI, options) {
3843
3843
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
3844
3844
  const {
3845
3845
  parsed: baseParsed,
@@ -3875,8 +3875,8 @@ var require_fast_uri = __commonJS({
3875
3875
  function resolveComponent(base, relative4, options, skipNormalization) {
3876
3876
  const target = {};
3877
3877
  if (!skipNormalization) {
3878
- base = parse5(serialize(base, options), options);
3879
- relative4 = parse5(serialize(relative4, options), options);
3878
+ base = parse7(serialize(base, options), options);
3879
+ relative4 = parse7(serialize(relative4, options), options);
3880
3880
  }
3881
3881
  options = options || {};
3882
3882
  if (!options.tolerant && relative4.scheme) {
@@ -4168,7 +4168,7 @@ var require_fast_uri = __commonJS({
4168
4168
  }
4169
4169
  return { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme };
4170
4170
  }
4171
- function parse5(uri, opts) {
4171
+ function parse7(uri, opts) {
4172
4172
  return parseWithStatus(uri, opts).parsed;
4173
4173
  }
4174
4174
  function normalizeString(uri, opts) {
@@ -4201,11 +4201,11 @@ var require_fast_uri = __commonJS({
4201
4201
  var fastUri = {
4202
4202
  SCHEMES,
4203
4203
  normalize,
4204
- resolve: resolve8,
4204
+ resolve: resolve9,
4205
4205
  resolveComponent,
4206
4206
  equal,
4207
4207
  serialize,
4208
- parse: parse5
4208
+ parse: parse7
4209
4209
  };
4210
4210
  module.exports = fastUri;
4211
4211
  module.exports.default = fastUri;
@@ -6887,7 +6887,7 @@ var require_discriminator = __commonJS({
6887
6887
  error,
6888
6888
  code(cxt) {
6889
6889
  const { gen, data, schema, parentSchema, it } = cxt;
6890
- const { oneOf } = parentSchema;
6890
+ const { oneOf: oneOf2 } = parentSchema;
6891
6891
  if (!it.opts.discriminator) {
6892
6892
  throw new Error("discriminator: requires discriminator option");
6893
6893
  }
@@ -6896,7 +6896,7 @@ var require_discriminator = __commonJS({
6896
6896
  throw new Error("discriminator: requires propertyName");
6897
6897
  if (schema.mapping)
6898
6898
  throw new Error("discriminator: mapping is not supported");
6899
- if (!oneOf)
6899
+ if (!oneOf2)
6900
6900
  throw new Error("discriminator: requires oneOf keyword");
6901
6901
  const valid = gen.let("valid", false);
6902
6902
  const tag = gen.const("tag", (0, codegen_1._)`${data}${(0, codegen_1.getProperty)(tagName)}`);
@@ -6924,8 +6924,8 @@ var require_discriminator = __commonJS({
6924
6924
  const oneOfMapping = {};
6925
6925
  const topRequired = hasRequired(parentSchema);
6926
6926
  let tagRequired = true;
6927
- for (let i = 0; i < oneOf.length; i++) {
6928
- let sch = oneOf[i];
6927
+ for (let i = 0; i < oneOf2.length; i++) {
6928
+ let sch = oneOf2[i];
6929
6929
  if ((sch === null || sch === void 0 ? void 0 : sch.$ref) && !(0, util_1.schemaHasRulesButRef)(sch, it.self.RULES)) {
6930
6930
  const ref = sch.$ref;
6931
6931
  sch = compile_1.resolveRef.call(it.self, it.schemaEnv.root, it.baseId, ref);
@@ -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 stringify3(item, ctx, onComment, onChompKeep) {
8921
+ function stringify4(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 = stringify3;
8950
+ exports.stringify = stringify4;
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 stringify3 = require_stringify();
8960
+ var stringify4 = 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 = stringify3.stringify(key, ctx, () => keyCommentDone = true, () => chompKeep = true);
8982
+ let str = stringify4.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 = stringify3.stringify(value, ctx, () => valueCommentDone = true, () => chompKeep = true);
9034
+ const valueStr = stringify4.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 stringify3 = require_stringify();
9175
+ var stringify4 = 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 = stringify3.createStringifyContext(ctx.doc, {});
9211
+ const strCtx = stringify4.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 stringify3 = require_stringify();
9278
+ var stringify4 = 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 stringify4 = flow ? stringifyFlowCollection : stringifyBlockCollection;
9283
- return stringify4(collection, ctx, options);
9282
+ const stringify5 = flow ? stringifyFlowCollection : stringifyBlockCollection;
9283
+ return stringify5(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 = stringify3.stringify(item, itemCtx, () => comment2 = null, () => chompKeep = true);
9308
+ let str2 = stringify4.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 = stringify3.stringify(item, itemCtx, () => comment = null);
9375
+ let str = stringify4.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 stringify3 = require_stringify();
10736
+ var stringify4 = 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 = stringify3.createStringifyContext(doc, options);
10751
+ const ctx = stringify4.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 = stringify3.stringify(doc.contents, ctx, () => contentComment = null, onChompKeep);
10773
+ let body = stringify4.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(stringify3.stringify(doc.contents, ctx));
10781
+ lines.push(stringify4.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 stringify3 = (cst) => "type" in cst ? stringifyToken(cst) : stringifyItem(cst);
12917
+ var stringify4 = (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 = stringify3;
12970
+ exports.stringify = stringify4;
12971
12971
  }
12972
12972
  });
12973
12973
 
@@ -14678,7 +14678,7 @@ var require_public_api = __commonJS({
14678
14678
  }
14679
14679
  return doc;
14680
14680
  }
14681
- function parse5(src, reviver, options) {
14681
+ function parse7(src, reviver, options) {
14682
14682
  let _reviver = void 0;
14683
14683
  if (typeof reviver === "function") {
14684
14684
  _reviver = reviver;
@@ -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 stringify3(value, replacer, options) {
14700
+ function stringify4(value, replacer, options) {
14701
14701
  let _replacer = null;
14702
14702
  if (typeof replacer === "function" || Array.isArray(replacer)) {
14703
14703
  _replacer = replacer;
@@ -14719,10 +14719,10 @@ var require_public_api = __commonJS({
14719
14719
  return value.toString(options);
14720
14720
  return new Document.Document(value, _replacer, options).toString(options);
14721
14721
  }
14722
- exports.parse = parse5;
14722
+ exports.parse = parse7;
14723
14723
  exports.parseAllDocuments = parseAllDocuments;
14724
14724
  exports.parseDocument = parseDocument2;
14725
- exports.stringify = stringify3;
14725
+ exports.stringify = stringify4;
14726
14726
  }
14727
14727
  });
14728
14728
 
@@ -14780,10 +14780,10 @@ var require_dist = __commonJS({
14780
14780
 
14781
14781
  // plugins/kxm/src/runtime-supervisor.ts
14782
14782
  import { spawn as spawn3 } from "node:child_process";
14783
- import { createHash as createHash12, createHmac, randomBytes as randomBytes2, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
14784
- import { chmodSync as chmodSync2, existsSync as existsSync13, lstatSync as lstatSync5, mkdirSync as mkdirSync7, readFileSync as readFileSync10, renameSync as renameSync4, rmSync as rmSync3, writeFileSync as writeFileSync6 } from "node:fs";
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";
14785
14785
  import { createServer } from "node:http";
14786
- import { dirname as dirname9, isAbsolute as isAbsolute6, join as join15 } from "node:path";
14786
+ import { dirname as dirname11, isAbsolute as isAbsolute6, join as join18 } from "node:path";
14787
14787
 
14788
14788
  // plugins/kxm/src/repo-root.ts
14789
14789
  import { existsSync } from "node:fs";
@@ -15168,7 +15168,7 @@ function defaultSpawn(command, args, options) {
15168
15168
  if (options.signal?.aborted) {
15169
15169
  return Promise.resolve({ stdout: "", stderr: "", code: null, started: false, observedChildExit: false, error: new Error("process_aborted") });
15170
15170
  }
15171
- return new Promise((resolve8) => {
15171
+ return new Promise((resolve9) => {
15172
15172
  const stdout = [];
15173
15173
  const stderr = [];
15174
15174
  let outputBytes = 0;
@@ -15199,7 +15199,7 @@ function defaultSpawn(command, args, options) {
15199
15199
  const started = Boolean(child?.pid);
15200
15200
  if (started && !observedChildExit) error ??= new Error("process_exit_unobserved");
15201
15201
  const unverifiedDescendants = stopping || started && !observedChildExit;
15202
- resolve8({
15202
+ resolve9({
15203
15203
  stdout: Buffer.concat(stdout).toString("utf8"),
15204
15204
  stderr: Buffer.concat(stderr).toString("utf8"),
15205
15205
  code,
@@ -16821,6 +16821,19 @@ function validateAgentScope(agent, step, repositories, file, stepId, issues) {
16821
16821
  }
16822
16822
  }
16823
16823
  }
16824
+ var GATE_STEP_OUTCOMES = {
16825
+ pass: ["passed", "implementation-failure"],
16826
+ fail: ["passed", "repro-missing"]
16827
+ };
16828
+ function gateOutcomeImpossible(step, stepId, file) {
16829
+ const expect = step.expect === "fail" ? "fail" : "pass";
16830
+ const produced = GATE_STEP_OUTCOMES[expect];
16831
+ const declared = Object.keys(objectValue(step.on) ?? {});
16832
+ const impossible = declared.filter((outcome) => !produced.includes(outcome) && outcome !== "implementation_failure" && outcome !== "repro_missing");
16833
+ const missing = produced.filter((outcome) => !declared.includes(outcome));
16834
+ if (impossible.length === 0 || missing.length === 0) return void 0;
16835
+ return issue2("semantic", "gate_outcome_impossible", file, `${stepId} declares ${impossible.join(", ")}, which a gate step with expect ${expect} never produces; it settles on ${produced.join(" or ")}, so declare ${missing.join(" and ")}`);
16836
+ }
16824
16837
  function transition(value) {
16825
16838
  if (typeof value === "string") return { target: value };
16826
16839
  const object = objectValue(value);
@@ -16885,6 +16898,8 @@ function validateWorkflow(workflow, agents, models, repositories, gates, issues)
16885
16898
  if (kind === "gate" && Object.keys(objectValue(step.on) ?? {}).some((outcome) => outcome === "implementation_failure" || outcome === "repro_missing")) {
16886
16899
  issues.push(issue2("semantic", "gate_outcome_renamed", file, `${stepId} must use implementation-failure and repro-missing`));
16887
16900
  }
16901
+ const impossibleOutcome = kind === "gate" ? gateOutcomeImpossible(step, stepId, file) : void 0;
16902
+ if (impossibleOutcome) issues.push(impossibleOutcome);
16888
16903
  for (const repositoryId of Object.keys(objectValue(step.repositories) ?? {})) {
16889
16904
  if (!repositories.has(repositoryId)) issues.push(issue2("reference", "repository_unknown", file, `${stepId} references unknown repository ${repositoryId}`));
16890
16905
  }
@@ -16924,8 +16939,8 @@ function validateWorkflow(workflow, agents, models, repositories, gates, issues)
16924
16939
  const writable = Object.values(objectValue(step.repositories) ?? {}).filter((access) => access === "write").length;
16925
16940
  if (maxWriteRepositories > writable) issues.push(issue2("semantic", "write_repository_bound_invalid", file, `${stepId} maxWriteRepositories exceeds writable repository scope`));
16926
16941
  }
16927
- const join16 = objectValue(step.join);
16928
- const minimumPassed = join16 && typeof join16.minimumPassed === "number" ? join16.minimumPassed : void 0;
16942
+ const join19 = objectValue(step.join);
16943
+ const minimumPassed = join19 && typeof join19.minimumPassed === "number" ? join19.minimumPassed : void 0;
16929
16944
  if (minimumPassed !== void 0 && minimumPassed > maximum) issues.push(issue2("semantic", "join_impossible", file, `${stepId} minimumPassed exceeds assignment maximum`));
16930
16945
  const distinctBy = names(assignment?.distinctBy);
16931
16946
  if (distinctBy.length > 0) {
@@ -21080,7 +21095,7 @@ function compileStep(step, index, stepIndex, requirePlanHash, sink) {
21080
21095
  const maxAttempts = compileCountField(step.maxAttempts, 1, `${id}.maxAttempts`, id, sink);
21081
21096
  const timeoutMs = compileOptionalDuration(step.timeoutMs, `${id}.timeoutMs`, id, sink);
21082
21097
  const assignments = compileAssignments(step, id, agent, sink);
21083
- const join16 = compileJoin(step, id, sink);
21098
+ const join19 = compileJoin(step, id, sink);
21084
21099
  const requiredEvidence = compileEvidence(step, id, sink);
21085
21100
  const transitions = compileTransitions(step, id, index, stepIndex, sink);
21086
21101
  const outcomes = Object.keys(transitions).sort(compareCodeUnits4);
@@ -21108,7 +21123,7 @@ function compileStep(step, index, stepIndex, requirePlanHash, sink) {
21108
21123
  transitions: orderedTransitions,
21109
21124
  requiresPlanHash: requirePlanHash.includes(id),
21110
21125
  assignments,
21111
- join: join16
21126
+ join: join19
21112
21127
  };
21113
21128
  if (kind === "agent" || kind === "moa") {
21114
21129
  if (!agent) return void 0;
@@ -21158,15 +21173,15 @@ function compileAssignments(step, stepId, primaryAgentId, sink) {
21158
21173
  };
21159
21174
  }
21160
21175
  function compileJoin(step, stepId, sink) {
21161
- const join16 = objectValue2(step.join);
21162
- if (!join16) return { strategy: "all" };
21163
- const declared = stringValue2(join16.strategy);
21176
+ const join19 = objectValue2(step.join);
21177
+ if (!join19) return { strategy: "all" };
21178
+ const declared = stringValue2(join19.strategy);
21164
21179
  const strategy = declared && JOIN_STRATEGIES.has(declared) ? declared : "all";
21165
- const minimumPassed = compileOptionalCount(join16.minimumPassed, `${stepId}.join.minimumPassed`, stepId, sink);
21180
+ const minimumPassed = compileOptionalCount(join19.minimumPassed, `${stepId}.join.minimumPassed`, stepId, sink);
21166
21181
  const compiled = {
21167
21182
  strategy,
21168
21183
  ...minimumPassed !== void 0 ? { minimumPassed } : {},
21169
- ...typeof join16.cancelRemaining === "boolean" ? { cancelRemaining: join16.cancelRemaining } : {}
21184
+ ...typeof join19.cancelRemaining === "boolean" ? { cancelRemaining: join19.cancelRemaining } : {}
21170
21185
  };
21171
21186
  return compiled;
21172
21187
  }
@@ -21479,6 +21494,28 @@ var EVIDENCE_OPTIONAL = ["producerPolicy"];
21479
21494
  function kxmSha256(input) {
21480
21495
  return `sha256:${createHash5("sha256").update(input, "utf8").digest("hex")}`;
21481
21496
  }
21497
+ function kxmStepAskSha256(plan, stepId, agentId) {
21498
+ const step = Object.hasOwn(plan.steps, stepId) ? plan.steps[stepId] : void 0;
21499
+ if (!step) throw runtimeError("run_plan_corrupt", plan.workflowId, `compiled plan is missing step ${stepId}`);
21500
+ return kxmSha256(kxmCanonicalJson({
21501
+ v: 1,
21502
+ workflowId: plan.workflowId,
21503
+ stepId,
21504
+ kind: step.kind,
21505
+ agentId,
21506
+ instructions: step.instructions ?? null,
21507
+ outcomes: [...step.outcomes],
21508
+ requiredEvidence: step.requiredEvidence.map((entry) => entry.key)
21509
+ }));
21510
+ }
21511
+ function kxmAttemptFinalOutcome(step, settled) {
21512
+ if (settled.resultClass !== "outcome") return "failed";
21513
+ const outcome = settled.outcome;
21514
+ const transition2 = outcome !== void 0 && Object.hasOwn(step.transitions, outcome) ? step.transitions[outcome] : void 0;
21515
+ if (!transition2) return "failed";
21516
+ if (transition2.to === "terminal") return transition2.terminalStatus === "completed" ? void 0 : "failed";
21517
+ return transition2.edge === "back" ? "blocked" : void 0;
21518
+ }
21482
21519
  function hashKxmRunPlanEnvelope(envelope2) {
21483
21520
  return kxmSha256(kxmCanonicalJson(envelope2));
21484
21521
  }
@@ -22490,12 +22527,12 @@ function enqueueKxmScheduledRun(storePath, runId, configRevision, bound, start)
22490
22527
  if (owner.admitted.has(runId) || owner.queue.some((item) => item.runId === runId)) {
22491
22528
  return Promise.reject(runtimeError("run_busy", runId, `run ${runId} is already admitted or queued`));
22492
22529
  }
22493
- return new Promise((resolve8, reject) => {
22530
+ return new Promise((resolve9, reject) => {
22494
22531
  owner.queue.push({
22495
22532
  runId,
22496
22533
  configRevision,
22497
22534
  bound,
22498
- start: (token) => start(token).then(resolve8, reject),
22535
+ start: (token) => start(token).then(resolve9, reject),
22499
22536
  fail: (error) => reject(error)
22500
22537
  });
22501
22538
  pump(storePath, owner);
@@ -22666,7 +22703,8 @@ function openKxmRuntimeContext(projectRoot, options) {
22666
22703
  homeRuntimeId: registration.homeRuntimeId,
22667
22704
  eventStore,
22668
22705
  configRevision: bundle.configRevision,
22669
- ...options.budgetClock ? { budgetClock: options.budgetClock } : {}
22706
+ ...options.budgetClock ? { budgetClock: options.budgetClock } : {},
22707
+ ...options.logger ? { logger: options.logger } : {}
22670
22708
  };
22671
22709
  } catch (error) {
22672
22710
  registry.close();
@@ -22965,7 +23003,7 @@ function cancelKxmRun(context, runId, options = {}) {
22965
23003
  }
22966
23004
 
22967
23005
  // plugins/kxm/src/oneshot-producer.ts
22968
- import { join as join12 } from "node:path";
23006
+ import { join as join15 } from "node:path";
22969
23007
 
22970
23008
  // plugins/kxm/src/oneshot-evidence.ts
22971
23009
  import { createHash as createHash7, randomUUID as randomUUID3 } from "node:crypto";
@@ -23249,10 +23287,10 @@ function loadPriceCatalog(rootOrPath) {
23249
23287
  }
23250
23288
 
23251
23289
  // plugins/kxm/src/engine.ts
23252
- var import_yaml5 = __toESM(require_dist(), 1);
23253
- import { createHash as createHash11, randomBytes, timingSafeEqual } from "node:crypto";
23254
- import { existsSync as existsSync9, readFileSync as readFileSync7 } from "node:fs";
23255
- import { join as join11 } from "node:path";
23290
+ var import_yaml7 = __toESM(require_dist(), 1);
23291
+ import { createHash as createHash12, randomBytes, timingSafeEqual } from "node:crypto";
23292
+ import { existsSync as existsSync11, readFileSync as readFileSync9 } from "node:fs";
23293
+ import { join as join14 } from "node:path";
23256
23294
 
23257
23295
  // plugins/kxm/src/routes.ts
23258
23296
  var import_yaml4 = __toESM(require_dist(), 1);
@@ -23342,7 +23380,7 @@ function buildFormalContextPacket(input) {
23342
23380
  budget: {
23343
23381
  allocatedTokens,
23344
23382
  estimatedTokens,
23345
- unresolvedGaps: [],
23383
+ unresolvedGaps: [...input.unresolvedGaps ?? []],
23346
23384
  provenanceSummary
23347
23385
  }
23348
23386
  };
@@ -23391,7 +23429,7 @@ function formatContextPacketForPrompt(packet) {
23391
23429
  }
23392
23430
  sections.push("");
23393
23431
  }
23394
- if (packet.environment.projectKnowledge.length > 0 || packet.environment.sharedDefaults.length > 0) {
23432
+ if (packet.environment.projectKnowledge.length > 0 || packet.environment.sharedDefaults.length > 0 || packet.environment.activeSkills.length > 0 || packet.environment.contradictions.length > 0) {
23395
23433
  sections.push(`## 5. Environment & Memory`);
23396
23434
  if (packet.environment.sharedDefaults.length > 0) {
23397
23435
  sections.push(`### Shared Defaults`);
@@ -23405,6 +23443,12 @@ function formatContextPacketForPrompt(packet) {
23405
23443
  sections.push(`- [${k.authority}] ${k.summary}`);
23406
23444
  }
23407
23445
  }
23446
+ if (packet.environment.activeSkills.length > 0) {
23447
+ sections.push(`### Active Skills`);
23448
+ for (const skill of packet.environment.activeSkills) {
23449
+ sections.push(`- ${skill.summary} (${skill.provenance.sourceRef ?? skill.id})`);
23450
+ }
23451
+ }
23408
23452
  if (packet.environment.contradictions.length > 0) {
23409
23453
  sections.push(`### Active Contradictions`);
23410
23454
  for (const c of packet.environment.contradictions) {
@@ -23544,19 +23588,1359 @@ function pruneContextPacket(packet, tokenBudget) {
23544
23588
  };
23545
23589
  }
23546
23590
 
23591
+ // plugins/kxm/src/dispatch-context.ts
23592
+ import { spawnSync as spawnSync3 } from "node:child_process";
23593
+ import { readdirSync as readdirSync7 } from "node:fs";
23594
+ import { extname as extname3, join as join12 } from "node:path";
23595
+
23596
+ // plugins/kxm/src/protocol.ts
23597
+ var DEFAULT_MESSAGE_TTL_MS = 24 * 60 * 6e4;
23598
+ var MAX_MESSAGE_TTL_MS = 7 * 24 * 60 * 6e4;
23599
+ var DEFAULT_MESSAGE_RETENTION_MS = 7 * 24 * 60 * 6e4;
23600
+ var MAX_BODY_BYTES = 256 * 1024;
23601
+ var MAX_AGENT_HOST_CHARS = 64;
23602
+ var MAX_LEASE_TTL_MS = 10 * 6e4;
23603
+ var DEFAULT_LEASE_TTL_MS = 5 * 6e4;
23604
+ var ProtocolError = class extends Error {
23605
+ statusCode;
23606
+ code;
23607
+ extras;
23608
+ constructor(statusCode, message, code = "protocol_error", extras) {
23609
+ super(message);
23610
+ this.name = "ProtocolError";
23611
+ this.statusCode = statusCode;
23612
+ this.code = code;
23613
+ if (extras) this.extras = extras;
23614
+ }
23615
+ };
23616
+ function requireString(value, field, options = {}) {
23617
+ if (typeof value !== "string") {
23618
+ throw new ProtocolError(400, `${field} must be a string`);
23619
+ }
23620
+ const result = value.trim();
23621
+ if (!options.allowEmpty && result.length === 0) {
23622
+ throw new ProtocolError(400, `${field} cannot be empty`);
23623
+ }
23624
+ if (options.max !== void 0 && result.length > options.max) {
23625
+ throw new ProtocolError(400, `${field} exceeds ${options.max} characters`);
23626
+ }
23627
+ return result;
23628
+ }
23629
+
23630
+ // plugins/kxm/src/context.ts
23631
+ var MAX_CONTEXT_SUMMARY_CHARS = 4e3;
23632
+ var MAX_CONTEXT_ID_REFS = 64;
23633
+ var MAX_CONTEXT_ITEMS = 256;
23634
+ var MIN_CONTEXT_BUDGET_TOKENS = 512;
23635
+ var MAX_CONTEXT_BUDGET_TOKENS = 2e5;
23636
+ var DEFAULT_CONTEXT_BUDGET_TOKENS = 32e3;
23637
+ var MAX_CONTEXT_ROLE_CHARS = 64;
23638
+ var MAX_CONTEXT_TASK_CHARS = 2e3;
23639
+ var AUTHORITY_GRANT_FLOOR = {
23640
+ human: "policy",
23641
+ workflow: "policy",
23642
+ git: "instruction",
23643
+ peer: "evidence",
23644
+ tool: "evidence",
23645
+ external: "evidence",
23646
+ derived: "evidence"
23647
+ };
23648
+ function authorityGrantFloor(sourceType) {
23649
+ return AUTHORITY_GRANT_FLOOR[sourceType];
23650
+ }
23651
+ var RESERVED_CONTROL_PLANE_FIELDS = /* @__PURE__ */ new Set([
23652
+ "permissions",
23653
+ "tools",
23654
+ "allow",
23655
+ "deny",
23656
+ "grants",
23657
+ "approval",
23658
+ "policy",
23659
+ "scopes",
23660
+ "credentials",
23661
+ "secrets",
23662
+ "token",
23663
+ "apiKey",
23664
+ "password"
23665
+ ]);
23666
+ var CONTEXT_ITEM_KINDS = ["evidence", "state", "episode", "knowledge", "skill"];
23667
+ var CONTEXT_SOURCE_TYPES = [
23668
+ "human",
23669
+ "git",
23670
+ "workflow",
23671
+ "tool",
23672
+ "peer",
23673
+ "external",
23674
+ "derived"
23675
+ ];
23676
+ var CONTEXT_AUTHORITIES = ["policy", "instruction", "evidence", "hypothesis"];
23677
+ var CONTEXT_CONFIDENCES = ["verified", "probable", "uncertain"];
23678
+ var CONTEXT_SCOPES = ["agent", "project", "run", "operator"];
23679
+ var CONTEXT_ROLES = ["repro", "planner", "critic", "implementer", "verifier"];
23680
+ function oneOf(value, field, allowed) {
23681
+ if (typeof value !== "string" || !allowed.includes(value)) {
23682
+ throw new ProtocolError(400, `${field} must be one of ${allowed.join(", ")}`, "invalid_context_field");
23683
+ }
23684
+ return value;
23685
+ }
23686
+ function idRefs(value, field, required = false) {
23687
+ if (value === void 0 || value === null) return required ? [] : void 0;
23688
+ if (!Array.isArray(value)) {
23689
+ throw new ProtocolError(400, `${field} must be an array of identifiers`, "invalid_context_field");
23690
+ }
23691
+ if (value.length > MAX_CONTEXT_ID_REFS) {
23692
+ throw new ProtocolError(
23693
+ 400,
23694
+ `${field} exceeds ${MAX_CONTEXT_ID_REFS} references`,
23695
+ "context_limits_exceeded"
23696
+ );
23697
+ }
23698
+ const seen = /* @__PURE__ */ new Set();
23699
+ const refs = [];
23700
+ for (const candidate of value) {
23701
+ if (typeof candidate !== "string" || !candidate.trim()) {
23702
+ throw new ProtocolError(400, `${field} must contain non-empty identifiers`, "invalid_context_field");
23703
+ }
23704
+ const ref = candidate.trim();
23705
+ if (seen.has(ref)) {
23706
+ throw new ProtocolError(400, `${field} contains duplicate reference ${ref}`, "invalid_context_field");
23707
+ }
23708
+ seen.add(ref);
23709
+ refs.push(ref);
23710
+ }
23711
+ return refs;
23712
+ }
23713
+ function optionalIsoTimestamp(value, field) {
23714
+ if (value === void 0 || value === null) return void 0;
23715
+ if (typeof value !== "string" || !Number.isFinite(Date.parse(value))) {
23716
+ throw new ProtocolError(400, `${field} must be an ISO-8601 timestamp`, "invalid_context_field");
23717
+ }
23718
+ return value;
23719
+ }
23720
+ function parseContextItem(value) {
23721
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
23722
+ throw new ProtocolError(400, "context item must be an object", "invalid_context_item");
23723
+ }
23724
+ const input = value;
23725
+ for (const field of Object.keys(input)) {
23726
+ if (RESERVED_CONTROL_PLANE_FIELDS.has(field)) {
23727
+ throw new ProtocolError(
23728
+ 403,
23729
+ `context items may not carry control-plane field "${field}"`,
23730
+ "context_authority_violation"
23731
+ );
23732
+ }
23733
+ }
23734
+ const item = {
23735
+ id: requireString(input.id, "context item id", { max: 128 }),
23736
+ kind: oneOf(input.kind, "context item kind", CONTEXT_ITEM_KINDS),
23737
+ project: requireString(input.project, "context item project", { max: 200 }),
23738
+ summary: redactSecrets(requireString(input.summary, "context item summary", { max: MAX_CONTEXT_SUMMARY_CHARS })),
23739
+ provenance: parseContextProvenance(input.provenance),
23740
+ authority: oneOf(input.authority, "context item authority", CONTEXT_AUTHORITIES),
23741
+ confidence: oneOf(input.confidence, "context item confidence", CONTEXT_CONFIDENCES)
23742
+ };
23743
+ if (input.scope !== void 0 && input.scope !== null) {
23744
+ item.scope = oneOf(input.scope, "context item scope", CONTEXT_SCOPES);
23745
+ }
23746
+ const observedAt = optionalIsoTimestamp(input.observedAt, "context item observedAt");
23747
+ const validFrom = optionalIsoTimestamp(input.validFrom, "context item validFrom");
23748
+ const validUntil = optionalIsoTimestamp(input.validUntil, "context item validUntil");
23749
+ if (observedAt !== void 0) item.observedAt = observedAt;
23750
+ if (validFrom !== void 0) item.validFrom = validFrom;
23751
+ if (validUntil !== void 0) item.validUntil = validUntil;
23752
+ if (input.status !== void 0 && input.status !== null) {
23753
+ item.status = oneOf(input.status, "context item status", ["current", "superseded", "proposed", "rejected"]);
23754
+ }
23755
+ const supersedes = idRefs(input.supersedes, "context item supersedes");
23756
+ if (supersedes !== void 0) {
23757
+ if (supersedes.includes(item.id)) {
23758
+ throw new ProtocolError(400, "context item cannot supersede itself", "invalid_context_item");
23759
+ }
23760
+ item.supersedes = supersedes;
23761
+ }
23762
+ const evidenceRefs = idRefs(input.evidenceRefs, "context item evidenceRefs");
23763
+ if (evidenceRefs !== void 0) item.evidenceRefs = evidenceRefs;
23764
+ const stateKey = input.stateKey === void 0 || input.stateKey === null ? void 0 : requireString(input.stateKey, "context item stateKey", { max: 200 });
23765
+ if (stateKey !== void 0) item.stateKey = stateKey;
23766
+ if (item.kind === "state" && item.stateKey === void 0) {
23767
+ throw new ProtocolError(400, "state items require a stateKey", "invalid_context_item");
23768
+ }
23769
+ if (item.kind === "state" && item.status === void 0) {
23770
+ throw new ProtocolError(400, "state items require an explicit lifecycle status", "invalid_context_item");
23771
+ }
23772
+ const grantFloor = authorityRank(authorityGrantFloor(item.provenance.sourceType));
23773
+ if (authorityRank(item.authority) > grantFloor) {
23774
+ throw new ProtocolError(
23775
+ 403,
23776
+ `content of origin ${item.provenance.sourceType} cannot claim ${item.authority} authority`,
23777
+ "context_authority_violation"
23778
+ );
23779
+ }
23780
+ return item;
23781
+ }
23782
+ function parseContextProvenance(value) {
23783
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
23784
+ throw new ProtocolError(400, "context provenance must be an object", "invalid_context_item");
23785
+ }
23786
+ const input = value;
23787
+ const provenance = {
23788
+ sourceType: oneOf(input.sourceType, "context provenance sourceType", CONTEXT_SOURCE_TYPES)
23789
+ };
23790
+ const sourceRef = input.sourceRef === void 0 || input.sourceRef === null ? void 0 : requireString(input.sourceRef, "context provenance sourceRef", { max: 512 });
23791
+ if (sourceRef !== void 0) provenance.sourceRef = redactSecrets(sourceRef);
23792
+ const derivedFrom = idRefs(input.derivedFrom, "context provenance derivedFrom");
23793
+ if (derivedFrom !== void 0) provenance.derivedFrom = derivedFrom;
23794
+ return provenance;
23795
+ }
23796
+ function parseContextRequest(value) {
23797
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
23798
+ throw new ProtocolError(400, "context request must be an object", "invalid_context_request");
23799
+ }
23800
+ const input = value;
23801
+ const request = {
23802
+ project: requireString(input.project, "context request project", { max: 200 }),
23803
+ role: requireString(input.role, "context request role", { max: MAX_CONTEXT_ROLE_CHARS }),
23804
+ task: requireString(input.task, "context request task", { max: MAX_CONTEXT_TASK_CHARS })
23805
+ };
23806
+ const workflowRunId = input.workflowRunId === void 0 || input.workflowRunId === null ? void 0 : requireString(input.workflowRunId, "context request workflowRunId", { max: 128 });
23807
+ if (workflowRunId !== void 0) request.workflowRunId = workflowRunId;
23808
+ const stageId = input.stageId === void 0 || input.stageId === null ? void 0 : requireString(input.stageId, "context request stageId", { max: 128 });
23809
+ if (stageId !== void 0) request.stageId = stageId;
23810
+ if (input.budgetTokens !== void 0 && input.budgetTokens !== null) {
23811
+ const budget = input.budgetTokens;
23812
+ if (!Number.isInteger(budget) || budget < MIN_CONTEXT_BUDGET_TOKENS || budget > MAX_CONTEXT_BUDGET_TOKENS) {
23813
+ throw new ProtocolError(
23814
+ 400,
23815
+ `context request budgetTokens must be an integer between ${MIN_CONTEXT_BUDGET_TOKENS} and ${MAX_CONTEXT_BUDGET_TOKENS}`,
23816
+ "invalid_context_request"
23817
+ );
23818
+ }
23819
+ request.budgetTokens = budget;
23820
+ }
23821
+ if (input.includeKinds !== void 0 && input.includeKinds !== null) {
23822
+ if (!Array.isArray(input.includeKinds) || input.includeKinds.length < 1 || input.includeKinds.length > CONTEXT_ITEM_KINDS.length) {
23823
+ throw new ProtocolError(
23824
+ 400,
23825
+ "context request includeKinds must be a non-empty array of item kinds",
23826
+ "invalid_context_request"
23827
+ );
23828
+ }
23829
+ request.includeKinds = input.includeKinds.map((kind) => oneOf(kind, "context request includeKinds", CONTEXT_ITEM_KINDS));
23830
+ }
23831
+ return request;
23832
+ }
23833
+ function validateContextPacketContents(request, packet) {
23834
+ const items = [
23835
+ ...packet.currentState,
23836
+ ...packet.knowledge,
23837
+ ...packet.evidence,
23838
+ ...packet.episodes,
23839
+ ...packet.skills,
23840
+ ...packet.contradictions
23841
+ ];
23842
+ if (items.length > MAX_CONTEXT_ITEMS) {
23843
+ throw new ProtocolError(400, `context packet exceeds ${MAX_CONTEXT_ITEMS} items`, "context_limits_exceeded");
23844
+ }
23845
+ const allowed = request.includeKinds ? new Set(request.includeKinds) : void 0;
23846
+ for (const item of items) {
23847
+ if (item.project !== request.project && item.project !== "_shared") {
23848
+ throw new ProtocolError(
23849
+ 400,
23850
+ `context packet contains cross-project item ${item.id}`,
23851
+ "context_isolation_violation"
23852
+ );
23853
+ }
23854
+ if (allowed && !allowed.has(item.kind)) {
23855
+ throw new ProtocolError(
23856
+ 400,
23857
+ `context packet contains item ${item.id} of unrequested kind ${item.kind}`,
23858
+ "context_isolation_violation"
23859
+ );
23860
+ }
23861
+ }
23862
+ }
23863
+ function contextItemCharacters(item) {
23864
+ return item.summary.length + item.id.length + item.kind.length + (item.provenance.sourceRef?.length ?? 0);
23865
+ }
23866
+ function estimateContextTokens(items) {
23867
+ let characters = 0;
23868
+ for (const item of items) characters += contextItemCharacters(item);
23869
+ return Math.ceil(characters / 4);
23870
+ }
23871
+ function authorityRank(authority) {
23872
+ switch (authority) {
23873
+ case "policy":
23874
+ return 3;
23875
+ case "instruction":
23876
+ return 2;
23877
+ case "evidence":
23878
+ return 1;
23879
+ case "hypothesis":
23880
+ return 0;
23881
+ }
23882
+ }
23883
+ function provenanceSummaryOf(items) {
23884
+ const summary = {};
23885
+ for (const item of items) {
23886
+ summary[item.provenance.sourceType] = (summary[item.provenance.sourceType] ?? 0) + 1;
23887
+ }
23888
+ for (const key of Object.keys(summary).sort()) {
23889
+ if (summary[key] === 0) delete summary[key];
23890
+ }
23891
+ return summary;
23892
+ }
23893
+
23894
+ // plugins/kxm/src/relevance.ts
23895
+ var RELEVANCE_STOPWORDS = Object.freeze(/* @__PURE__ */ new Set([
23896
+ "a",
23897
+ "an",
23898
+ "and",
23899
+ "are",
23900
+ "as",
23901
+ "at",
23902
+ "be",
23903
+ "been",
23904
+ "but",
23905
+ "by",
23906
+ "can",
23907
+ "could",
23908
+ "did",
23909
+ "do",
23910
+ "does",
23911
+ "for",
23912
+ "from",
23913
+ "had",
23914
+ "has",
23915
+ "have",
23916
+ "how",
23917
+ "if",
23918
+ "in",
23919
+ "into",
23920
+ "is",
23921
+ "it",
23922
+ "its",
23923
+ "of",
23924
+ "on",
23925
+ "or",
23926
+ "our",
23927
+ "should",
23928
+ "so",
23929
+ "than",
23930
+ "that",
23931
+ "the",
23932
+ "their",
23933
+ "them",
23934
+ "then",
23935
+ "there",
23936
+ "these",
23937
+ "they",
23938
+ "this",
23939
+ "those",
23940
+ "to",
23941
+ "was",
23942
+ "we",
23943
+ "were",
23944
+ "what",
23945
+ "when",
23946
+ "where",
23947
+ "which",
23948
+ "while",
23949
+ "who",
23950
+ "why",
23951
+ "will",
23952
+ "with",
23953
+ "would",
23954
+ "you",
23955
+ "your"
23956
+ ]));
23957
+ var RELEVANCE_K1 = 1.2;
23958
+ var RELEVANCE_B = 0.75;
23959
+ var MIN_TOKEN_CHARS = 2;
23960
+ var MAX_TOKEN_CHARS = 64;
23961
+ function foldPlural(token) {
23962
+ if (new RegExp("^\\p{N}+$", "u").test(token)) return token;
23963
+ if (token.length > 4 && token.endsWith("ies")) return `${token.slice(0, -3)}y`;
23964
+ if (token.length > 4 && token.endsWith("sses")) return token.slice(0, -2);
23965
+ if (token.length > 3 && token.endsWith("s") && !token.endsWith("ss") && !token.endsWith("us") && !token.endsWith("is")) {
23966
+ return token.slice(0, -1);
23967
+ }
23968
+ return token;
23969
+ }
23970
+ function relevanceTokens(text) {
23971
+ const tokens = [];
23972
+ for (const raw of text.normalize("NFKC").toLowerCase().split(/[^\p{L}\p{N}]+/u)) {
23973
+ if (raw.length < MIN_TOKEN_CHARS || raw.length > MAX_TOKEN_CHARS) continue;
23974
+ if (RELEVANCE_STOPWORDS.has(raw)) continue;
23975
+ tokens.push(foldPlural(raw));
23976
+ }
23977
+ return tokens;
23978
+ }
23979
+ function scoreRelevance(query, documents) {
23980
+ const scores = documents.map(() => 0);
23981
+ const terms = [...new Set(relevanceTokens(query))];
23982
+ if (terms.length === 0 || documents.length === 0) return scores;
23983
+ const indexed = documents.map((document) => {
23984
+ const tokens = relevanceTokens(document);
23985
+ const frequencies = /* @__PURE__ */ new Map();
23986
+ for (const token of tokens) frequencies.set(token, (frequencies.get(token) ?? 0) + 1);
23987
+ return { length: tokens.length, frequencies };
23988
+ });
23989
+ const count = indexed.length;
23990
+ let totalLength = 0;
23991
+ for (const document of indexed) totalLength += document.length;
23992
+ const averageLength = totalLength > 0 ? totalLength / count : 1;
23993
+ const inverseFrequency = /* @__PURE__ */ new Map();
23994
+ for (const term of terms) {
23995
+ let documentFrequency = 0;
23996
+ for (const document of indexed) {
23997
+ if (document.frequencies.has(term)) documentFrequency += 1;
23998
+ }
23999
+ inverseFrequency.set(term, Math.log(1 + (count - documentFrequency + 0.5) / (documentFrequency + 0.5)));
24000
+ }
24001
+ indexed.forEach((document, index) => {
24002
+ let score = 0;
24003
+ for (const term of terms) {
24004
+ const frequency = document.frequencies.get(term) ?? 0;
24005
+ if (frequency === 0) continue;
24006
+ const lengthNorm = 1 - RELEVANCE_B + RELEVANCE_B * (document.length / averageLength);
24007
+ score += inverseFrequency.get(term) * (frequency * (RELEVANCE_K1 + 1) / (frequency + RELEVANCE_K1 * lengthNorm));
24008
+ }
24009
+ scores[index] = score;
24010
+ });
24011
+ return scores;
24012
+ }
24013
+ function roundRelevance(score) {
24014
+ return Math.round(score * 1e3) / 1e3;
24015
+ }
24016
+ function contextItemRelevanceText(item) {
24017
+ return item.stateKey !== void 0 ? `${item.summary} ${item.stateKey}` : item.summary;
24018
+ }
24019
+ function compareCodeUnitIds(left, right) {
24020
+ return left < right ? -1 : left > right ? 1 : 0;
24021
+ }
24022
+
24023
+ // plugins/kxm/src/arbiter.ts
24024
+ var ROLE_POLICIES = [
24025
+ {
24026
+ role: "repro",
24027
+ label: "Reproduction specialist",
24028
+ kinds: ["episode", "knowledge", "evidence"],
24029
+ journalCategories: ["error", "lesson", "observation", "contradiction"],
24030
+ budgetTokens: 8e3
24031
+ },
24032
+ {
24033
+ role: "planner",
24034
+ label: "Planner",
24035
+ kinds: ["state", "knowledge", "evidence"],
24036
+ journalCategories: ["plan", "decision", "contradiction", "observation", "hypothesis", "experiment"],
24037
+ budgetTokens: 16e3
24038
+ },
24039
+ {
24040
+ role: "critic",
24041
+ label: "Independent critic",
24042
+ kinds: ["knowledge", "evidence", "episode"],
24043
+ journalCategories: ["contradiction", "error", "lesson", "experiment"],
24044
+ budgetTokens: 12e3
24045
+ },
24046
+ {
24047
+ role: "implementer",
24048
+ label: "Implementer",
24049
+ kinds: ["knowledge", "state", "skill", "episode", "evidence"],
24050
+ journalCategories: ["plan", "decision", "lesson", "state-change"],
24051
+ budgetTokens: 16e3
24052
+ },
24053
+ {
24054
+ role: "verifier",
24055
+ label: "Verifier",
24056
+ kinds: ["evidence", "knowledge", "episode"],
24057
+ journalCategories: ["plan", "error", "lesson", "contradiction"],
24058
+ budgetTokens: 8e3
24059
+ }
24060
+ ];
24061
+ function rolePolicy(role) {
24062
+ return ROLE_POLICIES.find((policy) => policy.role === role) ?? {
24063
+ role,
24064
+ label: role,
24065
+ kinds: ["knowledge", "evidence"],
24066
+ journalCategories: ["lesson", "observation"],
24067
+ budgetTokens: DEFAULT_CONTEXT_BUDGET_TOKENS
24068
+ };
24069
+ }
24070
+ var CONFIDENCE_RANK = { verified: 3, probable: 2, uncertain: 1 };
24071
+ var AUTHORITY_WEIGHT = { policy: 3, instruction: 2, evidence: 1, hypothesis: 0 };
24072
+ function arbitrate(requestInput, pool, options = {}) {
24073
+ const request = parseContextRequest(requestInput);
24074
+ const policy = rolePolicy(request.role);
24075
+ const budget = request.budgetTokens ?? policy.budgetTokens;
24076
+ const contradictions = new Set(options.contradictionIds ?? []);
24077
+ const candidates = [];
24078
+ let excludedSuperseded = 0;
24079
+ for (const candidate of pool) {
24080
+ if (candidate.project !== request.project && candidate.project !== "_shared") {
24081
+ throw new ProtocolError(
24082
+ 403,
24083
+ `context pool contains cross-project item ${candidate.id}`,
24084
+ "context_isolation_violation"
24085
+ );
24086
+ }
24087
+ if (candidate.status === "superseded" || candidate.status === "rejected") {
24088
+ excludedSuperseded += 1;
24089
+ continue;
24090
+ }
24091
+ candidates.push(candidate);
24092
+ }
24093
+ if (options.skillLifecycle) {
24094
+ for (const metadata of options.skillLifecycle.list("promoted")) {
24095
+ try {
24096
+ options.skillLifecycle.verify("promoted", metadata.id);
24097
+ candidates.push(parseContextItem({
24098
+ id: `skill_${metadata.id}`,
24099
+ kind: "skill",
24100
+ project: request.project,
24101
+ summary: metadata.description ? `${metadata.name}: ${metadata.description}` : metadata.name,
24102
+ provenance: {
24103
+ sourceType: "git",
24104
+ sourceRef: `skill:${metadata.id}@${metadata.contentSha256}`
24105
+ },
24106
+ authority: "instruction",
24107
+ confidence: "verified",
24108
+ status: "current"
24109
+ }));
24110
+ } catch {
24111
+ }
24112
+ }
24113
+ }
24114
+ const kindRank = /* @__PURE__ */ new Map();
24115
+ const requestedKinds = request.includeKinds ?? policy.kinds;
24116
+ requestedKinds.forEach((kind, index) => kindRank.set(kind, index));
24117
+ const kindPreference = (item) => {
24118
+ const rank = kindRank.get(item.kind);
24119
+ return rank === void 0 ? requestedKinds.length : rank;
24120
+ };
24121
+ const allowedKinds = new Set(requestedKinds);
24122
+ const inertProposal = (item) => item.kind === "state" && item.status !== void 0 && item.status !== "current" || item.kind === "skill" && item.status === "proposed";
24123
+ const eligible = candidates.filter((item) => contradictions.has(item.id) || allowedKinds.has(item.kind) && !inertProposal(item));
24124
+ const scoreList = scoreRelevance(request.task, eligible.map(contextItemRelevanceText));
24125
+ const scores = /* @__PURE__ */ new Map();
24126
+ eligible.forEach((item, index) => scores.set(item, scoreList[index] ?? 0));
24127
+ const scoreOf = (item) => scores.get(item) ?? 0;
24128
+ const when = (item) => {
24129
+ const parsed = Date.parse(item.observedAt ?? item.validFrom ?? "");
24130
+ return Number.isFinite(parsed) ? parsed : 0;
24131
+ };
24132
+ const ordered = [...eligible].sort(
24133
+ (left, right) => (contradictions.has(right.id) ? 1 : 0) - (contradictions.has(left.id) ? 1 : 0) || (left.project === request.project ? 0 : 1) - (right.project === request.project ? 0 : 1) || (scoreOf(right) > 0 ? 1 : 0) - (scoreOf(left) > 0 ? 1 : 0) || kindPreference(left) - kindPreference(right) || scoreOf(right) - scoreOf(left) || CONFIDENCE_RANK[right.confidence] - CONFIDENCE_RANK[left.confidence] || AUTHORITY_WEIGHT[right.authority] - AUTHORITY_WEIGHT[left.authority] || when(right) - when(left) || compareCodeUnitIds(left.id, right.id)
24134
+ );
24135
+ const selected = [];
24136
+ const unresolvedGaps = [];
24137
+ let characters = 0;
24138
+ let deferredForBudget = 0;
24139
+ for (const item of ordered) {
24140
+ if (selected.length >= MAX_CONTEXT_ITEMS) {
24141
+ unresolvedGaps.push("context item limit reached; refine the task or kinds");
24142
+ break;
24143
+ }
24144
+ const itemCharacters = contextItemCharacters(item);
24145
+ if (Math.ceil((characters + itemCharacters) / 4) > budget) {
24146
+ deferredForBudget += 1;
24147
+ continue;
24148
+ }
24149
+ characters += itemCharacters;
24150
+ selected.push(item);
24151
+ }
24152
+ if (deferredForBudget > 0) {
24153
+ unresolvedGaps.push(selected.length === 0 ? `budget of ${budget} tokens cannot fit any selected context` : `budget of ${budget} tokens reached; ${deferredForBudget} candidates deferred`);
24154
+ }
24155
+ if (candidates.length === 0) {
24156
+ unresolvedGaps.push("no context records exist for this project yet");
24157
+ }
24158
+ const bySection = (kind) => selected.filter((item) => item.kind === kind && !contradictions.has(item.id));
24159
+ const packet = {
24160
+ workingState: options.workingState ?? {},
24161
+ currentState: bySection("state").filter((item) => item.status === "current" || item.status === void 0),
24162
+ knowledge: bySection("knowledge"),
24163
+ evidence: bySection("evidence"),
24164
+ episodes: bySection("episode"),
24165
+ skills: bySection("skill").filter((item) => item.status !== "proposed"),
24166
+ contradictions: selected.filter((item) => contradictions.has(item.id)),
24167
+ unresolvedGaps,
24168
+ provenanceSummary: provenanceSummaryOf(selected),
24169
+ estimatedTokens: estimateContextTokens(selected)
24170
+ };
24171
+ validateContextPacketContents(request, packet);
24172
+ return {
24173
+ packet,
24174
+ audit: {
24175
+ request: {
24176
+ project: request.project,
24177
+ role: request.role,
24178
+ task: request.task,
24179
+ ...request.workflowRunId !== void 0 ? { workflowRunId: request.workflowRunId } : {},
24180
+ ...request.stageId !== void 0 ? { stageId: request.stageId } : {}
24181
+ },
24182
+ selectedIds: selected.map((item) => item.id),
24183
+ provenanceSummary: packet.provenanceSummary,
24184
+ estimatedTokens: packet.estimatedTokens,
24185
+ budgetTokens: budget,
24186
+ candidateCount: candidates.length,
24187
+ excludedSuperseded,
24188
+ unresolvedGaps,
24189
+ relevance: {
24190
+ taskTokens: new Set(relevanceTokens(request.task)).size,
24191
+ matchedCandidates: scoreList.filter((score) => score > 0).length,
24192
+ selected: selected.map((item) => roundRelevance(scoreOf(item)))
24193
+ }
24194
+ }
24195
+ };
24196
+ }
24197
+ function memoryRecordToContextItem(record2, project) {
24198
+ let authority = "instruction";
24199
+ if (record2.authority === "evidence") {
24200
+ authority = "evidence";
24201
+ }
24202
+ const sourceType = CONTEXT_SOURCE_TYPES.includes(record2.provenance?.sourceType) ? record2.provenance.sourceType : "git";
24203
+ const targetProject = record2.scope === "operator" ? "_shared" : project;
24204
+ return parseContextItem({
24205
+ id: `mem_${record2.id}`,
24206
+ kind: "knowledge",
24207
+ scope: record2.scope,
24208
+ project: targetProject,
24209
+ summary: record2.summary,
24210
+ provenance: {
24211
+ sourceType,
24212
+ sourceRef: record2.provenance?.sourceRef ?? `memory:${record2.id}.md`
24213
+ },
24214
+ authority,
24215
+ confidence: record2.confidence,
24216
+ status: record2.lifecycle === "active" ? "current" : "superseded",
24217
+ evidenceRefs: record2.evidenceRefs && record2.evidenceRefs.length > 0 ? record2.evidenceRefs : void 0
24218
+ });
24219
+ }
24220
+
24221
+ // plugins/kxm/src/memory.ts
24222
+ var import_yaml5 = __toESM(require_dist(), 1);
24223
+ import { existsSync as existsSync9, mkdirSync as mkdirSync4, readdirSync as readdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "node:fs";
24224
+ import { extname as extname2, join as join10, resolve as resolve5 } from "node:path";
24225
+ var MEMORY_SCHEMA = "kxm.memory.v1";
24226
+ var VALID_SCOPES = /* @__PURE__ */ new Set(["agent", "project", "run", "operator"]);
24227
+ var VALID_AUTHORITIES = /* @__PURE__ */ new Set(["instruction", "evidence", "promoted"]);
24228
+ var VALID_CONFIDENCES = /* @__PURE__ */ new Set(["verified", "probable", "uncertain"]);
24229
+ var VALID_LIFECYCLES = /* @__PURE__ */ new Set(["active", "deprecated", "superseded"]);
24230
+ var BANNED_CONTROL_PLANE_FIELDS = /* @__PURE__ */ new Set([
24231
+ "permissions",
24232
+ "tools",
24233
+ "allow",
24234
+ "deny",
24235
+ "grants",
24236
+ "approval",
24237
+ "policy",
24238
+ "scopes",
24239
+ "credentials",
24240
+ "secrets",
24241
+ "token",
24242
+ "apiKey",
24243
+ "password"
24244
+ ]);
24245
+ function parseFrontmatter(content) {
24246
+ const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
24247
+ if (!match) {
24248
+ throw new Error("memory file must contain YAML frontmatter enclosed in '---'");
24249
+ }
24250
+ return { frontmatter: match[1] ?? "", body: match[2]?.trim() ?? "" };
24251
+ }
24252
+ function parseMemoryRecord(raw, filename = "memory.md") {
24253
+ const { frontmatter, body } = parseFrontmatter(raw);
24254
+ const data = (0, import_yaml5.parse)(frontmatter);
24255
+ if (!data || typeof data !== "object" || Array.isArray(data)) {
24256
+ throw new Error(`invalid YAML frontmatter in ${filename}`);
24257
+ }
24258
+ for (const field of BANNED_CONTROL_PLANE_FIELDS) {
24259
+ if (field in data) {
24260
+ throw new Error(`memory record ${filename} may not carry control-plane field '${field}'`);
24261
+ }
24262
+ }
24263
+ if (data.schema !== MEMORY_SCHEMA) {
24264
+ throw new Error(`memory record ${filename} has unsupported schema: expected ${MEMORY_SCHEMA}, got ${String(data.schema)}`);
24265
+ }
24266
+ const id = typeof data.id === "string" ? data.id.trim() : "";
24267
+ if (!id || !/^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$/i.test(id)) {
24268
+ throw new Error(`memory record ${filename} has invalid id: ${String(data.id)}`);
24269
+ }
24270
+ const scope = data.scope;
24271
+ if (!VALID_SCOPES.has(scope)) {
24272
+ throw new Error(`memory record ${filename} has invalid scope: must be one of agent, project, run, operator`);
24273
+ }
24274
+ const kind = typeof data.kind === "string" ? data.kind.trim() : "";
24275
+ if (!kind) {
24276
+ throw new Error(`memory record ${filename} is missing kind`);
24277
+ }
24278
+ const rawSummary = typeof data.summary === "string" ? data.summary.trim() : "";
24279
+ if (!rawSummary) {
24280
+ throw new Error(`memory record ${filename} is missing summary`);
24281
+ }
24282
+ const summary = redactSecrets(rawSummary);
24283
+ const rawProv = data.provenance;
24284
+ if (!rawProv || typeof rawProv !== "object" || typeof rawProv.sourceType !== "string" || !rawProv.sourceType.trim()) {
24285
+ throw new Error(`memory record ${filename} is missing provenance.sourceType`);
24286
+ }
24287
+ const provenance = {
24288
+ sourceType: rawProv.sourceType.trim(),
24289
+ ...typeof rawProv.sourceRef === "string" ? { sourceRef: redactSecrets(rawProv.sourceRef.trim()) } : {},
24290
+ ...typeof rawProv.runId === "string" ? { runId: rawProv.runId.trim() } : {},
24291
+ ...typeof rawProv.timestamp === "string" ? { timestamp: rawProv.timestamp.trim() } : {}
24292
+ };
24293
+ const authority = data.authority;
24294
+ if (!VALID_AUTHORITIES.has(authority)) {
24295
+ throw new Error(`memory record ${filename} has invalid authority: ${String(data.authority)}`);
24296
+ }
24297
+ const confidence = data.confidence;
24298
+ if (!VALID_CONFIDENCES.has(confidence)) {
24299
+ throw new Error(`memory record ${filename} has invalid confidence: ${String(data.confidence)}`);
24300
+ }
24301
+ const lifecycle = data.lifecycle;
24302
+ if (!VALID_LIFECYCLES.has(lifecycle)) {
24303
+ throw new Error(`memory record ${filename} has invalid lifecycle: ${String(data.lifecycle)}`);
24304
+ }
24305
+ const rawRefs = Array.isArray(data.evidenceRefs) ? data.evidenceRefs : [];
24306
+ const evidenceRefs = rawRefs.map(String);
24307
+ return {
24308
+ schema: MEMORY_SCHEMA,
24309
+ id,
24310
+ scope,
24311
+ kind,
24312
+ summary,
24313
+ provenance,
24314
+ authority,
24315
+ confidence,
24316
+ lifecycle,
24317
+ evidenceRefs,
24318
+ ...body ? { body } : {}
24319
+ };
24320
+ }
24321
+ function memoryDirectories(repoRoot) {
24322
+ const root = resolve5(repoRoot);
24323
+ const memoryDir = join10(root, ".kxm", "memory");
24324
+ const candidatesDir = join10(memoryDir, "candidates");
24325
+ return { memoryDir, candidatesDir };
24326
+ }
24327
+ function loadAuthoredMemory(repoRoot) {
24328
+ const { memoryDir } = memoryDirectories(repoRoot);
24329
+ if (!existsSync9(memoryDir)) return [];
24330
+ const records = [];
24331
+ const entries = readdirSync5(memoryDir, { withFileTypes: true });
24332
+ for (const entry of entries) {
24333
+ if (entry.isFile() && extname2(entry.name) === ".md") {
24334
+ const fullPath = join10(memoryDir, entry.name);
24335
+ try {
24336
+ const text = readFileSync7(fullPath, "utf8");
24337
+ const record2 = parseMemoryRecord(text, entry.name);
24338
+ if (record2.lifecycle === "active") {
24339
+ records.push(record2);
24340
+ }
24341
+ } catch (error) {
24342
+ throw new Error(`failed to parse authored memory file ${entry.name}: ${error instanceof Error ? error.message : String(error)}`);
24343
+ }
24344
+ }
24345
+ }
24346
+ return records.sort((left, right) => left.id.localeCompare(right.id));
24347
+ }
24348
+
24349
+ // plugins/kxm/src/skills.ts
24350
+ var import_yaml6 = __toESM(require_dist(), 1);
24351
+ import { createHash as createHash9 } from "node:crypto";
24352
+ import { existsSync as existsSync10, mkdirSync as mkdirSync5, readdirSync as readdirSync6, readFileSync as readFileSync8, renameSync, rmSync, statSync as statSync2, writeFileSync as writeFileSync5 } from "node:fs";
24353
+ import { dirname as dirname7, join as join11 } from "node:path";
24354
+ var SKILL_CANDIDATE_SCHEMA = "kxm.skill-candidate.v1";
24355
+ var SKILL_EVALUATION_SCHEMA = "kxm.skill-evaluation.v1";
24356
+ var SKILL_DECISION_SCHEMA = "kxm.skill-decision.v1";
24357
+ var MAX_SKILL_NAME_CHARS = 64;
24358
+ var MAX_SKILL_CONTENT_CHARS = 32e3;
24359
+ var MAX_SKILL_EVIDENCE_REFS = 32;
24360
+ var MAX_SKILL_MODELS = 16;
24361
+ var PROMOTION_REQUIRED_EVALUATIONS = [
24362
+ "static-review",
24363
+ "sandbox",
24364
+ "functional",
24365
+ "safety"
24366
+ ];
24367
+ var SkillLifecycleError = class extends Error {
24368
+ code;
24369
+ constructor(code, message) {
24370
+ super(message);
24371
+ this.name = "SkillLifecycleError";
24372
+ this.code = code;
24373
+ }
24374
+ };
24375
+ function skillContentSha256(content) {
24376
+ return createHash9("sha256").update(content, "utf8").digest("hex");
24377
+ }
24378
+ function skillIdFor(name, contentSha256) {
24379
+ const slug = name.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
24380
+ if (!slug) throw new SkillLifecycleError("invalid_skill_name", "skill name must contain alphanumeric characters");
24381
+ return `${slug}.${contentSha256.slice(0, 12)}`;
24382
+ }
24383
+ function parseSkillFrontmatter(content) {
24384
+ const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
24385
+ if (!match) {
24386
+ return { frontmatter: null, body: content };
24387
+ }
24388
+ const rawFm = match[1];
24389
+ const rawBody = match[2];
24390
+ if (rawFm === void 0 || rawBody === void 0) {
24391
+ return { frontmatter: null, body: content };
24392
+ }
24393
+ try {
24394
+ const parsed = (0, import_yaml6.parse)(rawFm);
24395
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
24396
+ return { frontmatter: parsed, body: rawBody };
24397
+ }
24398
+ } catch {
24399
+ }
24400
+ return { frontmatter: null, body: content };
24401
+ }
24402
+ function ensureSkillFrontmatter(content, name, description) {
24403
+ const parsed = parseSkillFrontmatter(content);
24404
+ if (parsed.frontmatter) {
24405
+ const fmName = typeof parsed.frontmatter.name === "string" && parsed.frontmatter.name.trim() ? parsed.frontmatter.name.trim() : name;
24406
+ const fmDesc = typeof parsed.frontmatter.description === "string" && parsed.frontmatter.description.trim() ? parsed.frontmatter.description.trim() : description || `Governed skill for ${fmName}`;
24407
+ const rest2 = parsed.body.replace(/^(\r?\n)+/, "");
24408
+ return `---
24409
+ name: ${fmName}
24410
+ description: ${fmDesc}
24411
+ ---
24412
+
24413
+ ${rest2}`;
24414
+ }
24415
+ const desc = description || `Governed skill for ${name}`;
24416
+ const rest = content.replace(/^(\r?\n)+/, "");
24417
+ return `---
24418
+ name: ${name}
24419
+ description: ${desc}
24420
+ ---
24421
+
24422
+ ${rest}`;
24423
+ }
24424
+ function createUnifiedPatch(relativePath, content) {
24425
+ const lines = content.split("\n");
24426
+ const count = lines.length;
24427
+ const header = [
24428
+ `diff --git a/${relativePath} b/${relativePath}`,
24429
+ "new file mode 100644",
24430
+ "--- /dev/null",
24431
+ `+++ b/${relativePath}`,
24432
+ `@@ -0,0 +1,${count} @@`
24433
+ ];
24434
+ const body = lines.map((l) => `+${l}`);
24435
+ return [...header, ...body, ""].join("\n");
24436
+ }
24437
+ var SkillLifecycle = class {
24438
+ root;
24439
+ now;
24440
+ allowOptimizationEvals;
24441
+ dryRun;
24442
+ /** What a `dryRun` lifecycle would have written or moved, in order. */
24443
+ planned = [];
24444
+ constructor(root, options = {}) {
24445
+ this.root = root;
24446
+ this.now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
24447
+ this.allowOptimizationEvals = options.allowOptimizationEvals === true;
24448
+ this.dryRun = options.dryRun === true;
24449
+ }
24450
+ write(file, content) {
24451
+ if (this.dryRun) {
24452
+ this.planned.push({ action: "write", target: file });
24453
+ return;
24454
+ }
24455
+ mkdirSync5(dirname7(file), { recursive: true });
24456
+ writeFileSync5(file, content);
24457
+ }
24458
+ dir(state) {
24459
+ return join11(this.root, state === "candidate" ? "candidates" : `${state}s`.replace("rejecteds", "rejected").replace("promoteds", "promoted"));
24460
+ }
24461
+ historyFile(id) {
24462
+ return join11(this.root, "history", `${id}.jsonl`);
24463
+ }
24464
+ paths(state, id) {
24465
+ const dir = join11(this.dir(state), id);
24466
+ return { dir, metadata: join11(dir, "metadata.json"), skill: join11(dir, "SKILL.md") };
24467
+ }
24468
+ appendHistory(id, record2) {
24469
+ const line = `${JSON.stringify(record2)}
24470
+ `;
24471
+ if (existsSync10(this.historyFile(id))) {
24472
+ const existing = readFileSync8(this.historyFile(id), "utf8");
24473
+ const lines = existing.split("\n").filter((entry) => entry.trim());
24474
+ this.write(this.historyFile(id), [...lines.slice(-499), line.trim()].join("\n") + "\n");
24475
+ } else {
24476
+ this.write(this.historyFile(id), line);
24477
+ }
24478
+ }
24479
+ history(id) {
24480
+ const file = this.historyFile(id);
24481
+ if (!existsSync10(file)) return [];
24482
+ return readFileSync8(file, "utf8").split("\n").filter((line) => line.trim()).map((line) => JSON.parse(line));
24483
+ }
24484
+ readMetadata(state, id) {
24485
+ const { metadata } = this.paths(state, id);
24486
+ if (!existsSync10(metadata)) {
24487
+ throw new SkillLifecycleError("skill_not_found", `skill ${id} not found in ${state}`);
24488
+ }
24489
+ return JSON.parse(readFileSync8(metadata, "utf8"));
24490
+ }
24491
+ move(from, to, id) {
24492
+ const fromDir = join11(this.dir(from), id);
24493
+ const toDir = join11(this.dir(to), id);
24494
+ if (!existsSync10(fromDir)) {
24495
+ throw new SkillLifecycleError("skill_not_found", `skill ${id} not found in ${from}`);
24496
+ }
24497
+ if (this.dryRun) {
24498
+ this.planned.push({ action: "move", target: `${fromDir} -> ${toDir}` });
24499
+ return;
24500
+ }
24501
+ mkdirSync5(this.dir(to), { recursive: true });
24502
+ if (existsSync10(toDir)) rmSync(toDir, { recursive: true, force: true });
24503
+ renameSync(fromDir, toDir);
24504
+ }
24505
+ /** Submit a new skill candidate. Content is redacted of secret material at
24506
+ * creation; the ID is derived from name + content hash. */
24507
+ create(input) {
24508
+ const name = input.name?.trim();
24509
+ if (!name || name.length > MAX_SKILL_NAME_CHARS) {
24510
+ throw new SkillLifecycleError("invalid_skill_name", `skill name must be 1-${MAX_SKILL_NAME_CHARS} characters`);
24511
+ }
24512
+ const rawContent = redactSecrets(input.content ?? "");
24513
+ if (!rawContent.trim() || rawContent.length > MAX_SKILL_CONTENT_CHARS) {
24514
+ throw new SkillLifecycleError("invalid_skill_content", `skill content must be 1-${MAX_SKILL_CONTENT_CHARS} characters`);
24515
+ }
24516
+ const rawDesc = redactSecrets(input.description?.trim() ?? "");
24517
+ const content = ensureSkillFrontmatter(rawContent, name, rawDesc);
24518
+ if (content.length > MAX_SKILL_CONTENT_CHARS) {
24519
+ throw new SkillLifecycleError("invalid_skill_content", `skill content must be 1-${MAX_SKILL_CONTENT_CHARS} characters`);
24520
+ }
24521
+ const { frontmatter } = parseSkillFrontmatter(content);
24522
+ const description = typeof frontmatter?.description === "string" && frontmatter.description.trim() ? frontmatter.description.trim() : rawDesc || `Governed skill for ${name}`;
24523
+ const sources = {
24524
+ runIds: boundedList(input.sources?.runIds, "runIds"),
24525
+ journalEntryIds: boundedList(input.sources?.journalEntryIds, "journalEntryIds"),
24526
+ evidenceReceipts: boundedList(input.sources?.evidenceReceipts, "evidenceReceipts")
24527
+ };
24528
+ if (sources.runIds.length === 0 && sources.journalEntryIds.length === 0 && sources.evidenceReceipts.length === 0) {
24529
+ throw new SkillLifecycleError(
24530
+ "skill_sources_required",
24531
+ "a skill candidate must reference at least one source run, journal entry, or evidence receipt"
24532
+ );
24533
+ }
24534
+ const createdBy = input.createdBy?.trim();
24535
+ if (!createdBy) throw new SkillLifecycleError("invalid_skill_author", "createdBy is required");
24536
+ const models = boundedList(input.compatibility?.models, "compatibility.models");
24537
+ if (models.length === 0 || models.length > MAX_SKILL_MODELS) {
24538
+ throw new SkillLifecycleError("invalid_skill_compatibility", `compatibility.models must list 1-${MAX_SKILL_MODELS} models`);
24539
+ }
24540
+ const harness = input.compatibility?.harness?.trim();
24541
+ if (!harness) throw new SkillLifecycleError("invalid_skill_compatibility", "compatibility.harness is required");
24542
+ const contentSha256 = skillContentSha256(content);
24543
+ const id = skillIdFor(name, contentSha256);
24544
+ const { metadata, skill } = this.paths("candidate", id);
24545
+ if (existsSync10(metadata)) {
24546
+ throw new SkillLifecycleError(
24547
+ "skill_candidate_exists",
24548
+ `identical candidate ${id} already exists; changed behavior requires changed content`
24549
+ );
24550
+ }
24551
+ const record2 = {
24552
+ schema: SKILL_CANDIDATE_SCHEMA,
24553
+ id,
24554
+ name,
24555
+ description,
24556
+ contentSha256,
24557
+ version: input.version ?? 1,
24558
+ sources,
24559
+ compatibility: { harness, models },
24560
+ createdBy,
24561
+ createdAt: this.now(),
24562
+ ...input.supersedes ? { supersedes: input.supersedes } : {}
24563
+ };
24564
+ this.write(skill, content);
24565
+ this.write(metadata, `${JSON.stringify(record2, null, 2)}
24566
+ `);
24567
+ this.appendHistory(id, { schema: "kxm.skill-history-event.v1", event: "candidate_created", by: createdBy, supersedes: input.supersedes, at: record2.createdAt });
24568
+ return record2;
24569
+ }
24570
+ /** Record a protected evaluation. A failed functional or safety evaluation
24571
+ * deterministically quarantines the candidate. */
24572
+ evaluate(candidateId, input) {
24573
+ if (input.kind === "optimization" && !this.allowOptimizationEvals) {
24574
+ throw new SkillLifecycleError(
24575
+ "skill_optimization_disabled",
24576
+ "optimization evaluations are disabled; enable them explicitly behind protected evals"
24577
+ );
24578
+ }
24579
+ const metadata = this.readMetadata("candidate", candidateId);
24580
+ const evaluatorVersion = input.evaluatorVersion?.trim();
24581
+ if (!evaluatorVersion) throw new SkillLifecycleError("invalid_skill_evaluation", "evaluatorVersion is required");
24582
+ const evaluation = {
24583
+ schema: SKILL_EVALUATION_SCHEMA,
24584
+ candidateId,
24585
+ kind: input.kind,
24586
+ evaluatorVersion,
24587
+ passed: input.passed === true,
24588
+ ...input.score !== void 0 ? { score: input.score } : {},
24589
+ ...input.details ? { details: redactSecrets(input.details.slice(0, 2e3)) } : {},
24590
+ evaluatedAt: this.now()
24591
+ };
24592
+ this.appendHistory(candidateId, evaluation);
24593
+ let quarantined = false;
24594
+ if (!evaluation.passed && (input.kind === "functional" || input.kind === "safety")) {
24595
+ const decision = {
24596
+ schema: SKILL_DECISION_SCHEMA,
24597
+ candidateId,
24598
+ decision: "quarantined",
24599
+ decidedBy: input.evaluatedBy?.trim() || `evaluator:${evaluatorVersion}`,
24600
+ reason: `automatic quarantine: ${input.kind} evaluation failed (${evaluatorVersion})`,
24601
+ evidenceRefs: [`evaluation:${input.kind}:${evaluatorVersion}`],
24602
+ decidedAt: this.now()
24603
+ };
24604
+ this.move("candidate", "quarantined", candidateId);
24605
+ this.appendHistory(candidateId, decision);
24606
+ quarantined = true;
24607
+ }
24608
+ return { evaluation, quarantined };
24609
+ }
24610
+ evaluationsFor(candidateId) {
24611
+ return this.history(candidateId).filter(
24612
+ (record2) => record2.schema === SKILL_EVALUATION_SCHEMA
24613
+ );
24614
+ }
24615
+ /** Promote a candidate that passed every protected evaluation. The
24616
+ * promoter must differ from the author, cite durable evidence, and the
24617
+ * promoted content is hash-pinned and immutable. Emits a unified diff patch
24618
+ * instead of moving the candidate directory. */
24619
+ promote(candidateId, decision) {
24620
+ const metadata = this.readMetadata("candidate", candidateId);
24621
+ const decidedBy = decision.decidedBy?.trim();
24622
+ if (!decidedBy) throw new SkillLifecycleError("invalid_skill_decision", "decidedBy is required");
24623
+ if (decidedBy === metadata.createdBy) {
24624
+ throw new SkillLifecycleError("skill_promotion_invalid", "the author of a skill candidate cannot promote it");
24625
+ }
24626
+ const evidenceRefs = boundedList(decision.evidenceRefs, "evidenceRefs");
24627
+ if (evidenceRefs.length === 0) {
24628
+ throw new SkillLifecycleError("skill_promotion_invalid", "promotion requires durable evidence references");
24629
+ }
24630
+ const evaluations = this.evaluationsFor(candidateId);
24631
+ const missing = [];
24632
+ for (const kind of PROMOTION_REQUIRED_EVALUATIONS) {
24633
+ const latest = [...evaluations].reverse().find((record3) => record3.kind === kind);
24634
+ if (!latest || !latest.passed) missing.push(kind);
24635
+ }
24636
+ if (missing.length > 0) {
24637
+ throw new SkillLifecycleError(
24638
+ "skill_evaluations_incomplete",
24639
+ `promotion requires passing ${missing.join(", ")} evaluations`
24640
+ );
24641
+ }
24642
+ this.verify("candidate", candidateId);
24643
+ const record2 = {
24644
+ schema: SKILL_DECISION_SCHEMA,
24645
+ candidateId,
24646
+ decision: "promoted",
24647
+ decidedBy,
24648
+ reason: decision.reason?.trim() || "passed protected evaluation",
24649
+ evidenceRefs,
24650
+ decidedAt: this.now()
24651
+ };
24652
+ const candidatePaths = this.paths("candidate", candidateId);
24653
+ const promotedPaths = this.paths("promoted", candidateId);
24654
+ const skillContent = readFileSync8(candidatePaths.skill, "utf8");
24655
+ const metadataContent = readFileSync8(candidatePaths.metadata, "utf8");
24656
+ this.write(promotedPaths.skill, skillContent);
24657
+ this.write(promotedPaths.metadata, metadataContent);
24658
+ const patchPath = join11(this.root, "patches", `${candidateId}.patch`);
24659
+ const relSkillPath = `.kxm/skills/promoted/${candidateId}/SKILL.md`;
24660
+ const relMetaPath = `.kxm/skills/promoted/${candidateId}/metadata.json`;
24661
+ const patch = `${createUnifiedPatch(relSkillPath, skillContent)}${createUnifiedPatch(relMetaPath, metadataContent)}`;
24662
+ this.write(patchPath, patch);
24663
+ this.appendHistory(candidateId, record2);
24664
+ return { ...metadata, patch, patchPath };
24665
+ }
24666
+ reject(candidateId, decision) {
24667
+ const metadata = this.readMetadata("candidate", candidateId);
24668
+ const record2 = {
24669
+ schema: SKILL_DECISION_SCHEMA,
24670
+ candidateId,
24671
+ decision: "rejected",
24672
+ decidedBy: decision.decidedBy?.trim() || "kxm-admin",
24673
+ reason: decision.reason?.trim() || "rejected",
24674
+ evidenceRefs: [],
24675
+ decidedAt: this.now()
24676
+ };
24677
+ this.move("candidate", "rejected", candidateId);
24678
+ this.appendHistory(candidateId, record2);
24679
+ return metadata;
24680
+ }
24681
+ /** Verify content integrity of a stored skill (any state). Detects
24682
+ * out-of-band edits to promoted skills and verifies standard YAML frontmatter. */
24683
+ verify(state, id) {
24684
+ const metadata = this.readMetadata(state, id);
24685
+ const { skill } = this.paths(state, id);
24686
+ const content = readFileSync8(skill, "utf8");
24687
+ if (skillContentSha256(content) !== metadata.contentSha256) {
24688
+ throw new SkillLifecycleError(
24689
+ "skill_integrity_violation",
24690
+ `skill ${id} content does not match its pinned hash; promoted skills are immutable and require a new candidate/eval cycle`
24691
+ );
24692
+ }
24693
+ const { frontmatter } = parseSkillFrontmatter(content);
24694
+ if (!frontmatter || typeof frontmatter.name !== "string" || !frontmatter.name.trim() || typeof frontmatter.description !== "string" || !frontmatter.description.trim()) {
24695
+ throw new SkillLifecycleError(
24696
+ "invalid_skill_frontmatter",
24697
+ `skill ${id} must contain valid YAML frontmatter with 'name' and 'description'`
24698
+ );
24699
+ }
24700
+ return metadata;
24701
+ }
24702
+ list(state) {
24703
+ const dir = this.dir(state);
24704
+ if (!existsSync10(dir)) return [];
24705
+ const ids = readdirSorted(dir);
24706
+ return ids.map((id) => {
24707
+ try {
24708
+ return this.readMetadata(state, id);
24709
+ } catch {
24710
+ return void 0;
24711
+ }
24712
+ }).filter((metadata) => metadata !== void 0);
24713
+ }
24714
+ read(state, id) {
24715
+ const metadata = this.readMetadata(state, id);
24716
+ const { skill } = this.paths(state, id);
24717
+ return { metadata, content: readFileSync8(skill, "utf8") };
24718
+ }
24719
+ };
24720
+ function boundedList(value, field) {
24721
+ if (value === void 0) return [];
24722
+ if (!Array.isArray(value)) {
24723
+ throw new SkillLifecycleError("invalid_skill_input", `${field} must be an array of strings`);
24724
+ }
24725
+ const refs = value.map((ref) => String(ref).trim()).filter((ref) => ref.length > 0);
24726
+ if (refs.length > MAX_SKILL_EVIDENCE_REFS) {
24727
+ throw new SkillLifecycleError("invalid_skill_input", `${field} exceeds ${MAX_SKILL_EVIDENCE_REFS} references`);
24728
+ }
24729
+ return [...new Set(refs)];
24730
+ }
24731
+ function readdirSorted(dir) {
24732
+ return readdirSync6(dir).filter((entry) => statSync2(join11(dir, entry)).isDirectory()).sort();
24733
+ }
24734
+
24735
+ // plugins/kxm/src/dispatch-context.ts
24736
+ var DISPATCH_CONTEXT_BUDGET_TOKENS = 4e3;
24737
+ var DISPATCH_CONTEXT_MAX_PROJECT_ITEMS = 5;
24738
+ var GAP_NOT_LOADED = "dispatch_context_not_loaded";
24739
+ var GAP_FAILED = "dispatch_context_failed";
24740
+ var GAP_GIT_UNAVAILABLE = "dispatch_context_withheld:git_unavailable";
24741
+ var GAP_UNCOMMITTED = "dispatch_context_withheld:uncommitted";
24742
+ var GAP_DRIFT = "dispatch_context_withheld:memory_revision_drift";
24743
+ var GAP_MEMORY_UNREADABLE = "dispatch_context_memory_unreadable";
24744
+ var GAP_SKILLS_UNREADABLE = "dispatch_context_skills_unreadable";
24745
+ var DISPATCH_CONTEXT_PATHSPEC = [
24746
+ ":(glob).kxm/memory/*.md",
24747
+ ":(glob).kxm/skills/promoted/*/SKILL.md",
24748
+ ":(glob).kxm/skills/promoted/*/metadata.json"
24749
+ ];
24750
+ var GIT_STATUS_TIMEOUT_MS = 5e3;
24751
+ var EMPTY_DISPATCH_SOURCES = Object.freeze({
24752
+ present: false,
24753
+ pool: Object.freeze([]),
24754
+ promoted: Object.freeze([]),
24755
+ gaps: Object.freeze([]),
24756
+ skippedUnboundScopes: 0
24757
+ });
24758
+ var NOT_LOADED_DISPATCH_SOURCES = Object.freeze({
24759
+ present: true,
24760
+ pool: Object.freeze([]),
24761
+ promoted: Object.freeze([]),
24762
+ gaps: Object.freeze([GAP_NOT_LOADED]),
24763
+ skippedUnboundScopes: 0
24764
+ });
24765
+ function contextRoleForAgent(agentId) {
24766
+ if (CONTEXT_ROLES.includes(agentId)) return agentId;
24767
+ for (const role of CONTEXT_ROLES) {
24768
+ if (agentId.startsWith(`${role}-`)) return role;
24769
+ }
24770
+ return agentId;
24771
+ }
24772
+ function dispatchContextPresent(projectRoot) {
24773
+ return hasTopLevelMarkdown(join12(projectRoot, ".kxm", "memory")) || hasAnyEntry(join12(projectRoot, ".kxm", "skills", "promoted"));
24774
+ }
24775
+ function hasTopLevelMarkdown(dir) {
24776
+ try {
24777
+ return readdirSync7(dir, { withFileTypes: true }).some((entry) => entry.isFile() && extname3(entry.name) === ".md");
24778
+ } catch {
24779
+ return false;
24780
+ }
24781
+ }
24782
+ function hasAnyEntry(dir) {
24783
+ try {
24784
+ return readdirSync7(dir).length > 0;
24785
+ } catch {
24786
+ return false;
24787
+ }
24788
+ }
24789
+ function gitEnvironment2() {
24790
+ return Object.fromEntries(Object.entries(process.env).filter(([name]) => !name.toUpperCase().startsWith("GIT_")));
24791
+ }
24792
+ function committedGateGap(projectRoot) {
24793
+ const result = spawnSync3("git", [
24794
+ "--no-optional-locks",
24795
+ "-C",
24796
+ projectRoot,
24797
+ "status",
24798
+ "--porcelain=v1",
24799
+ "--untracked-files=all",
24800
+ "--ignored=matching",
24801
+ "--",
24802
+ ...DISPATCH_CONTEXT_PATHSPEC
24803
+ ], {
24804
+ encoding: "utf8",
24805
+ env: gitEnvironment2(),
24806
+ timeout: GIT_STATUS_TIMEOUT_MS,
24807
+ windowsHide: true
24808
+ });
24809
+ if (result.error || result.status !== 0) return GAP_GIT_UNAVAILABLE;
24810
+ if (typeof result.stdout !== "string" || result.stdout.length > 0) return GAP_UNCOMMITTED;
24811
+ return void 0;
24812
+ }
24813
+ function gapId(value) {
24814
+ return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(value) ? value : "invalid";
24815
+ }
24816
+ function withheld(gaps) {
24817
+ return { present: true, pool: [], promoted: [], gaps: [...gaps], skippedUnboundScopes: 0 };
24818
+ }
24819
+ function loadDispatchContextSources(input) {
24820
+ try {
24821
+ const { projectRoot, projectId, pinnedMemoryRevision } = input;
24822
+ if (!dispatchContextPresent(projectRoot)) return EMPTY_DISPATCH_SOURCES;
24823
+ const gateGap = committedGateGap(projectRoot);
24824
+ if (gateGap !== void 0) return withheld([gateGap]);
24825
+ if (computeKxmMemoryRevision({ projectRoot }) !== pinnedMemoryRevision) return withheld([GAP_DRIFT]);
24826
+ const gaps = [];
24827
+ let records;
24828
+ try {
24829
+ records = loadAuthoredMemory(projectRoot);
24830
+ } catch {
24831
+ gaps.push(GAP_MEMORY_UNREADABLE);
24832
+ records = [];
24833
+ }
24834
+ const pool = [];
24835
+ let skippedUnboundScopes = 0;
24836
+ for (const record2 of records) {
24837
+ if (record2.scope !== "project" && record2.scope !== "operator") {
24838
+ skippedUnboundScopes += 1;
24839
+ continue;
24840
+ }
24841
+ try {
24842
+ pool.push(memoryRecordToContextItem(record2, projectId));
24843
+ } catch {
24844
+ gaps.push(`dispatch_context_memory_rejected:${gapId(record2.id)}`);
24845
+ }
24846
+ }
24847
+ const promoted = [];
24848
+ try {
24849
+ const lifecycle = new SkillLifecycle(join12(projectRoot, ".kxm", "skills"));
24850
+ for (const metadata of lifecycle.list("promoted")) {
24851
+ try {
24852
+ promoted.push(lifecycle.verify("promoted", metadata.id));
24853
+ } catch {
24854
+ gaps.push(`dispatch_context_skill_unverified:${gapId(metadata.id)}`);
24855
+ }
24856
+ }
24857
+ } catch {
24858
+ gaps.push(GAP_SKILLS_UNREADABLE);
24859
+ }
24860
+ if (computeKxmMemoryRevision({ projectRoot }) !== pinnedMemoryRevision) return withheld([...gaps, GAP_DRIFT]);
24861
+ return { present: true, pool, promoted, gaps, skippedUnboundScopes };
24862
+ } catch {
24863
+ return withheld([GAP_FAILED]);
24864
+ }
24865
+ }
24866
+ function assembleDispatchContext(sources, input) {
24867
+ const role = contextRoleForAgent(input.agentId);
24868
+ try {
24869
+ if (sources.pool.length === 0 && sources.promoted.length === 0) {
24870
+ return { items: [], deliveredIds: [], renderDeferred: 0, role, unresolvedGaps: [...sources.gaps] };
24871
+ }
24872
+ const task = (input.task.trim() || input.stepId).slice(0, MAX_CONTEXT_TASK_CHARS);
24873
+ const promoted = sources.promoted;
24874
+ const snapshot = {
24875
+ list: (state) => state === "promoted" ? [...promoted] : [],
24876
+ verify: (state, id) => {
24877
+ const found = state === "promoted" ? promoted.find((metadata) => metadata.id === id) : void 0;
24878
+ if (!found) throw new Error("skill is not in the verified dispatch snapshot");
24879
+ return found;
24880
+ }
24881
+ };
24882
+ const { packet, audit } = arbitrate(
24883
+ {
24884
+ project: input.projectId,
24885
+ role,
24886
+ task,
24887
+ workflowRunId: input.runId,
24888
+ stageId: input.stepId,
24889
+ budgetTokens: Math.min(rolePolicy(role).budgetTokens, DISPATCH_CONTEXT_BUDGET_TOKENS)
24890
+ },
24891
+ [...sources.pool],
24892
+ promoted.length > 0 ? { skillLifecycle: snapshot } : {}
24893
+ );
24894
+ const byId = /* @__PURE__ */ new Map();
24895
+ for (const section of [packet.currentState, packet.knowledge, packet.evidence, packet.episodes, packet.skills, packet.contradictions]) {
24896
+ for (const item of section) byId.set(item.id, item);
24897
+ }
24898
+ const items = [];
24899
+ let projectItems = 0;
24900
+ let renderDeferred = 0;
24901
+ for (const id of audit.selectedIds) {
24902
+ const item = byId.get(id);
24903
+ if (!item) continue;
24904
+ const projectRouted = item.project !== "_shared" && item.kind !== "state" && item.kind !== "episode" && item.kind !== "skill";
24905
+ if (projectRouted) {
24906
+ if (projectItems >= DISPATCH_CONTEXT_MAX_PROJECT_ITEMS) {
24907
+ renderDeferred += 1;
24908
+ continue;
24909
+ }
24910
+ projectItems += 1;
24911
+ }
24912
+ items.push(item);
24913
+ }
24914
+ return {
24915
+ items,
24916
+ deliveredIds: items.map((item) => item.id),
24917
+ renderDeferred,
24918
+ role,
24919
+ unresolvedGaps: [
24920
+ ...sources.gaps,
24921
+ ...packet.unresolvedGaps,
24922
+ ...renderDeferred > 0 ? [`dispatch_context_render_deferred:${renderDeferred}`] : []
24923
+ ],
24924
+ audit
24925
+ };
24926
+ } catch {
24927
+ return { items: [], deliveredIds: [], renderDeferred: 0, role, unresolvedGaps: [...sources.gaps, GAP_FAILED] };
24928
+ }
24929
+ }
24930
+
23547
24931
  // plugins/kxm/src/engine-artifacts.ts
23548
- import { join as join10 } from "node:path";
24932
+ import { join as join13 } from "node:path";
23549
24933
 
23550
24934
  // plugins/kxm/src/artifacts-exist.ts
23551
- import { lstatSync as lstatSync4, realpathSync as realpathSync3, statSync as statSync2 } from "node:fs";
23552
- import { isAbsolute as isAbsolute3, relative as relative3, resolve as resolve5 } from "node:path";
24935
+ import { lstatSync as lstatSync4, realpathSync as realpathSync3, statSync as statSync3 } from "node:fs";
24936
+ import { isAbsolute as isAbsolute3, relative as relative3, resolve as resolve6 } from "node:path";
23553
24937
  function staysUnder(root, candidate) {
23554
24938
  const child = relative3(root, candidate);
23555
24939
  return child === "" || !isAbsolute3(child) && child !== ".." && !child.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`);
23556
24940
  }
23557
24941
  function verifyArtifactExists(rootInput, pathInput) {
23558
- const lexicalRoot = resolve5(rootInput);
23559
- const lexicalPath = resolve5(pathInput);
24942
+ const lexicalRoot = resolve6(rootInput);
24943
+ const lexicalPath = resolve6(pathInput);
23560
24944
  if (!staysUnder(lexicalRoot, lexicalPath)) {
23561
24945
  return { ok: false, error: "artifact_outside_workspace_assets", path: lexicalPath };
23562
24946
  }
@@ -23580,7 +24964,7 @@ function verifyArtifactExists(rootInput, pathInput) {
23580
24964
  if (link.isSymbolicLink()) {
23581
24965
  return { ok: false, error: "artifact_outside_workspace_assets", path: lexicalPath };
23582
24966
  }
23583
- const artifact = statSync2(realPath);
24967
+ const artifact = statSync3(realPath);
23584
24968
  if (!artifact.isFile()) return { ok: false, error: "artifact_not_file", path: lexicalPath };
23585
24969
  if (artifact.size < 1) return { ok: false, error: "artifact_empty", path: lexicalPath };
23586
24970
  return { ok: true, path: realPath, bytes: artifact.size };
@@ -23592,14 +24976,14 @@ function verifyArtifactExists(rootInput, pathInput) {
23592
24976
  // plugins/kxm/src/engine-artifacts.ts
23593
24977
  var kxmArtifactsGateSeams = {};
23594
24978
  function evaluateArtifactsGate(context, definition) {
23595
- const root = join10(context.projectRoot, ".kxm", "assets");
24979
+ const root = join13(context.projectRoot, ".kxm", "assets");
23596
24980
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
23597
24981
  const startTime = process.hrtime.bigint();
23598
24982
  let checkedCount = 0;
23599
24983
  let failedCount = 0;
23600
24984
  for (const [index, path] of definition.paths.entries()) {
23601
24985
  checkedCount += 1;
23602
- const result = verifyArtifactExists(root, join10(root, path));
24986
+ const result = verifyArtifactExists(root, join13(root, path));
23603
24987
  if (!result.ok) failedCount += 1;
23604
24988
  kxmArtifactsGateSeams.afterPathChecked?.({ path, index, checkedCount, failedCount });
23605
24989
  }
@@ -23632,7 +25016,7 @@ function evaluateArtifactsGate(context, definition) {
23632
25016
 
23633
25017
  // plugins/kxm/src/engine-command.ts
23634
25018
  import { spawn as spawn2 } from "node:child_process";
23635
- import { createHash as createHash9 } from "node:crypto";
25019
+ import { createHash as createHash10 } from "node:crypto";
23636
25020
 
23637
25021
  // plugins/kxm/src/safety-integrity.ts
23638
25022
  var DESTRUCTIVE_COMMAND_PATTERNS = Object.freeze([
@@ -23669,7 +25053,7 @@ function createCommandObserver(input) {
23669
25053
  return new CommandObserver(input);
23670
25054
  }
23671
25055
  var StreamDigest = class {
23672
- hash = createHash9("sha256");
25056
+ hash = createHash10("sha256");
23673
25057
  bytes = 0;
23674
25058
  complete = 0;
23675
25059
  digest() {
@@ -23744,8 +25128,8 @@ var CommandObserver = class {
23744
25128
  this.requestStop("error", "lost-close");
23745
25129
  }
23746
25130
  run() {
23747
- return new Promise((resolve8) => {
23748
- this.resolveOutcome = resolve8;
25131
+ return new Promise((resolve9) => {
25132
+ this.resolveOutcome = resolve9;
23749
25133
  try {
23750
25134
  this.start();
23751
25135
  } catch (error) {
@@ -24516,7 +25900,7 @@ function hashFromStore(context, runId) {
24516
25900
  }
24517
25901
 
24518
25902
  // plugins/kxm/src/routing.ts
24519
- import { createHash as createHash10 } from "node:crypto";
25903
+ import { createHash as createHash11 } from "node:crypto";
24520
25904
  var ROUTING_RECORD_V2_SCHEMA = "kxm.routing-record.v2";
24521
25905
  var BEHAVIORAL_HASH_VERSION = 1;
24522
25906
  var MAX_PROVIDER_METADATA_FIELDS = 32;
@@ -24539,7 +25923,7 @@ function behavioralConfigHash(input) {
24539
25923
  workflowDefinitionSha256: input.workflowDefinitionSha256?.trim() ?? null,
24540
25924
  verifierConfigSha256: input.verifierConfigSha256?.trim() ?? null
24541
25925
  };
24542
- return createHash10("sha256").update(JSON.stringify(canonical), "utf8").digest("hex");
25926
+ return createHash11("sha256").update(JSON.stringify(canonical), "utf8").digest("hex");
24543
25927
  }
24544
25928
  function boundedInt(value, field) {
24545
25929
  if (value === void 0 || value === null) return void 0;
@@ -24708,7 +26092,7 @@ function requireTrustedProducer(producer) {
24708
26092
  }
24709
26093
  }
24710
26094
  function hashCapabilitySecret(secret) {
24711
- return `sha256:${createHash11("sha256").update(`${CAPABILITY_PREFIX}${secret}`, "utf8").digest("hex")}`;
26095
+ return `sha256:${createHash12("sha256").update(`${CAPABILITY_PREFIX}${secret}`, "utf8").digest("hex")}`;
24712
26096
  }
24713
26097
  function mintKxmCapabilitySecret() {
24714
26098
  const secret = `kxmcap_${randomBytes(32).toString("base64url")}`;
@@ -24837,7 +26221,7 @@ async function driveAdmitted(context, runId, producer, token, options = {}) {
24837
26221
  return latest;
24838
26222
  }
24839
26223
  function newDriveId(runId, token, runtimeId, monotonicNs) {
24840
- return `drv_${createHash11("sha256").update(`${runId}\0${token}\0${runtimeId}\0${monotonicNs}`, "utf8").digest("hex").slice(0, 24)}`;
26224
+ return `drv_${createHash12("sha256").update(`${runId}\0${token}\0${runtimeId}\0${monotonicNs}`, "utf8").digest("hex").slice(0, 24)}`;
24841
26225
  }
24842
26226
  function driveErrorSummary(error) {
24843
26227
  const raw = error instanceof KxmConfigError ? error.issues[0]?.code : void 0;
@@ -25064,8 +26448,8 @@ var KxmRunScheduler = class _KxmRunScheduler {
25064
26448
  resolve: (_value) => void 0,
25065
26449
  reject: (_error) => void 0
25066
26450
  };
25067
- const settled = new Promise((resolve8, reject) => {
25068
- pending.resolve = resolve8;
26451
+ const settled = new Promise((resolve9, reject) => {
26452
+ pending.resolve = resolve9;
25069
26453
  pending.reject = reject;
25070
26454
  });
25071
26455
  void settled.then(void 0, () => void 0);
@@ -25179,8 +26563,28 @@ function invokeProducer(producer, request) {
25179
26563
  );
25180
26564
  }
25181
26565
  var kxmGateDispatchSeams = {};
26566
+ function peekDispatchContextSources(context, runId) {
26567
+ if (!dispatchContextPresent(context.projectRoot)) return EMPTY_DISPATCH_SOURCES;
26568
+ let run;
26569
+ try {
26570
+ run = requireRun(context, runId);
26571
+ const state = foldStoredKxmRun(context, run);
26572
+ const plan = rehydrateKxmCompiledPlanFromStore(context.eventStore, run);
26573
+ if (state.status !== "running" || state.currentStep) return NOT_LOADED_DISPATCH_SOURCES;
26574
+ const next = plan.steps[state.pendingStepId ?? plan.entryStepId];
26575
+ if (next?.kind !== "agent" && next?.kind !== "moa") return NOT_LOADED_DISPATCH_SOURCES;
26576
+ } catch {
26577
+ return NOT_LOADED_DISPATCH_SOURCES;
26578
+ }
26579
+ return loadDispatchContextSources({
26580
+ projectRoot: context.projectRoot,
26581
+ projectId: run.projectId,
26582
+ pinnedMemoryRevision: run.memoryRevision
26583
+ });
26584
+ }
25182
26585
  async function stepLocked(context, runId, producer, token) {
25183
- const prepared = context.eventStore.transaction(() => prepareDispatch(context, runId, producer.id));
26586
+ const dispatchSources = peekDispatchContextSources(context, runId);
26587
+ const prepared = context.eventStore.transaction(() => prepareDispatch(context, runId, producer.id, dispatchSources));
25184
26588
  if (prepared.kind === "return") {
25185
26589
  return prepared.handoff ? { state: prepared.state, handoff: prepared.handoff } : { state: prepared.state };
25186
26590
  }
@@ -25443,10 +26847,10 @@ function unreconciledPanelAttemptId(state) {
25443
26847
  }
25444
26848
  function resolveProducerRoute(projectRoot, step, agentId) {
25445
26849
  let agentModel;
25446
- const agentFile = join11(projectRoot, ".kxm", "agents", `${agentId}.yaml`);
25447
- if (existsSync9(agentFile)) {
26850
+ const agentFile = join14(projectRoot, ".kxm", "agents", `${agentId}.yaml`);
26851
+ if (existsSync11(agentFile)) {
25448
26852
  try {
25449
- const parsed = (0, import_yaml5.parse)(readFileSync7(agentFile, "utf8"));
26853
+ const parsed = (0, import_yaml7.parse)(readFileSync9(agentFile, "utf8"));
25450
26854
  if (typeof parsed?.model === "string") {
25451
26855
  agentModel = parsed.model;
25452
26856
  } else if (parsed?.model && typeof parsed.model === "object" && !Array.isArray(parsed.model)) {
@@ -25518,7 +26922,7 @@ function resolveProducerRoute(projectRoot, step, agentId) {
25518
26922
  const model = selector.slice(slash + 1);
25519
26923
  return { provider, model, selector };
25520
26924
  }
25521
- function prepareDispatch(context, runId, producerId) {
26925
+ function prepareDispatch(context, runId, producerId, dispatchSources) {
25522
26926
  const run = requireRun(context, runId);
25523
26927
  const plan = rehydrateKxmCompiledPlanFromStore(context.eventStore, run);
25524
26928
  const state = foldStoredKxmRun(context, run);
@@ -25688,7 +27092,8 @@ function prepareDispatch(context, runId, producerId) {
25688
27092
  stepAttempt,
25689
27093
  enterRunning: true,
25690
27094
  producerId,
25691
- resolvedRoute
27095
+ resolvedRoute,
27096
+ dispatchSources
25692
27097
  });
25693
27098
  return {
25694
27099
  kind: "panel",
@@ -25701,7 +27106,8 @@ function prepareDispatch(context, runId, producerId) {
25701
27106
  target: step.assignments.target,
25702
27107
  maxParallel: step.assignments.maxParallel,
25703
27108
  first,
25704
- state: first.state
27109
+ state: first.state,
27110
+ dispatchSources
25705
27111
  }
25706
27112
  };
25707
27113
  }
@@ -25740,7 +27146,7 @@ function birthMember(context, input) {
25740
27146
  if (promptText === void 0) {
25741
27147
  throw runtimeError("run_prompt_mismatch", run.runId, "prompt text missing from accepted run prompt store");
25742
27148
  }
25743
- const promptHash = createHash11("sha256").update(promptText, "utf8").digest("hex");
27149
+ const promptHash = createHash12("sha256").update(promptText, "utf8").digest("hex");
25744
27150
  const expectedHash = run.promptSha256.replace(/^sha256:/, "");
25745
27151
  if (promptHash !== expectedHash) {
25746
27152
  throw runtimeError("run_prompt_mismatch", run.runId, "prompt text does not match accepted promptSha256");
@@ -25755,6 +27161,13 @@ function birthMember(context, input) {
25755
27161
  resolvedRoute = routeResult;
25756
27162
  }
25757
27163
  }
27164
+ const dispatchContext = assembleDispatchContext(input.dispatchSources, {
27165
+ projectId: run.projectId,
27166
+ runId: run.runId,
27167
+ stepId: input.stepId,
27168
+ agentId,
27169
+ task: [input.step.instructions, promptText].filter((part) => typeof part === "string" && part.trim().length > 0).join("\n\n")
27170
+ });
25758
27171
  const assignmentId = newKxmAssignmentId();
25759
27172
  const attemptId = newKxmAttemptId();
25760
27173
  const minted = mintCapabilitySecret();
@@ -25821,7 +27234,9 @@ function birthMember(context, input) {
25821
27234
  activeStepIndex: input.step.index,
25822
27235
  totalSteps: input.plan.order.length,
25823
27236
  settledDecisions: []
25824
- }
27237
+ },
27238
+ ...dispatchContext.items.length > 0 ? { arbitratedItems: dispatchContext.items } : {},
27239
+ ...dispatchContext.unresolvedGaps.length > 0 ? { unresolvedGaps: dispatchContext.unresolvedGaps } : {}
25825
27240
  });
25826
27241
  const { packet: contextPacket } = pruneContextPacket(rawContextPacket);
25827
27242
  const generatedPrompt = formatContextPacketForPrompt(contextPacket);
@@ -25858,6 +27273,26 @@ ${generatedPrompt}` : generatedPrompt,
25858
27273
  controller,
25859
27274
  state: next
25860
27275
  };
27276
+ if (input.dispatchSources.present && context.logger) {
27277
+ try {
27278
+ context.logger({
27279
+ event: "dispatch_context_assembled",
27280
+ runId: run.runId,
27281
+ stepId: input.stepId,
27282
+ stepAttempt: input.stepAttempt,
27283
+ agentId,
27284
+ role: dispatchContext.role,
27285
+ deliveredIds: dispatchContext.deliveredIds,
27286
+ renderDeferred: dispatchContext.renderDeferred,
27287
+ provenanceSummary: dispatchContext.audit?.provenanceSummary ?? {},
27288
+ estimatedTokens: dispatchContext.audit?.estimatedTokens ?? 0,
27289
+ budgetTokens: dispatchContext.audit?.budgetTokens ?? null,
27290
+ skippedUnboundScopes: input.dispatchSources.skippedUnboundScopes,
27291
+ unresolvedGaps: dispatchContext.unresolvedGaps
27292
+ });
27293
+ } catch {
27294
+ }
27295
+ }
25861
27296
  kxmPanelDispatchSeams.afterBirth?.(member);
25862
27297
  return member;
25863
27298
  }
@@ -25979,7 +27414,8 @@ async function drivePanel(context, panel, producer) {
25979
27414
  step: panel.step,
25980
27415
  stepId: panel.stepId,
25981
27416
  stepAttempt: panel.stepAttempt,
25982
- producerId: producer.id
27417
+ producerId: producer.id,
27418
+ dispatchSources: panel.dispatchSources
25983
27419
  });
25984
27420
  });
25985
27421
  };
@@ -26107,7 +27543,29 @@ async function drivePanel(context, panel, producer) {
26107
27543
  }
26108
27544
  }
26109
27545
  }
26110
- function producerRoutingRecord(context, dispatch, result, now) {
27546
+ var ENGINE_ROUTING_METADATA_KEYS = /* @__PURE__ */ new Set(["workflowId", "askSha256", "objectiveSha256", "stepWrites"]);
27547
+ var MAX_PRODUCER_ROUTING_METADATA_FIELDS = MAX_PROVIDER_METADATA_FIELDS - ENGINE_ROUTING_METADATA_KEYS.size;
27548
+ function engineRoutingMetadata(dispatch, producer) {
27549
+ const metadata = {};
27550
+ if (producer !== void 0 && producer !== null) {
27551
+ if (typeof producer !== "object" || Array.isArray(producer)) {
27552
+ throw new Error("routing providerMetadata must be an object");
27553
+ }
27554
+ let kept = 0;
27555
+ for (const [key, value] of Object.entries(producer)) {
27556
+ if (kept >= MAX_PRODUCER_ROUTING_METADATA_FIELDS) break;
27557
+ if (ENGINE_ROUTING_METADATA_KEYS.has(key)) continue;
27558
+ metadata[key] = value;
27559
+ kept += 1;
27560
+ }
27561
+ }
27562
+ metadata.workflowId = dispatch.plan.workflowId;
27563
+ metadata.askSha256 = kxmStepAskSha256(dispatch.plan, dispatch.stepId, dispatch.agentId);
27564
+ metadata.objectiveSha256 = dispatch.run.promptSha256;
27565
+ metadata.stepWrites = Object.values(dispatch.step.repositories).some((access) => access === "write");
27566
+ return metadata;
27567
+ }
27568
+ function producerRoutingRecord(context, dispatch, result, now, finalOutcome) {
26111
27569
  const run = requireRun(context, dispatch.run.runId);
26112
27570
  if (result.costBasis === void 0 || result.costBasis === null) {
26113
27571
  throw runtimeError("settle_missing_cost_basis", run.runId, `attempt settlement rejected: missing required costBasis for attempt ${dispatch.attemptId}`);
@@ -26144,9 +27602,12 @@ function producerRoutingRecord(context, dispatch, result, now) {
26144
27602
  retries: Math.max(0, dispatch.stepAttempt - 1),
26145
27603
  thinking: result.thinking ?? dispatch.request.thinking
26146
27604
  };
26147
- for (const field of ["agentRole", "contextTokens", "tokensIn", "tokensOut", "cacheReadTokens", "cacheWriteTokens", "priceRef", "providerMetadata"]) {
27605
+ for (const field of ["contextTokens", "tokensIn", "tokensOut", "cacheReadTokens", "cacheWriteTokens", "priceRef"]) {
26148
27606
  if (result[field] !== void 0) record2[field] = result[field];
26149
27607
  }
27608
+ record2.agentRole = result.agentRole ?? dispatch.agentId;
27609
+ record2.providerMetadata = engineRoutingMetadata(dispatch, result.providerMetadata);
27610
+ if (finalOutcome !== void 0) record2.finalOutcome = finalOutcome;
26150
27611
  return parseRoutingRecordV2(record2);
26151
27612
  }
26152
27613
  function settleMember(context, dispatch, result, produceError, produceErrorCode) {
@@ -26211,7 +27672,10 @@ function settleMember(context, dispatch, result, produceError, produceErrorCode)
26211
27672
  latencyMs: typeof result?.latencyMs === "number" ? result.latencyMs : 0,
26212
27673
  costBasis: "unknown",
26213
27674
  costUsd: null,
26214
- retries: Math.max(0, dispatch.stepAttempt - 1)
27675
+ retries: Math.max(0, dispatch.stepAttempt - 1),
27676
+ agentRole: dispatch.agentId,
27677
+ finalOutcome: "failed",
27678
+ providerMetadata: engineRoutingMetadata(dispatch)
26215
27679
  };
26216
27680
  push("routing.attempt.recorded", { routing: parseRoutingRecordV2(routingRecord) });
26217
27681
  push("assignment.result_recorded", {
@@ -26226,9 +27690,17 @@ function settleMember(context, dispatch, result, produceError, produceErrorCode)
26226
27690
  if (!result) {
26227
27691
  throw runtimeError("settle_missing_cost_basis", run.runId, `attempt settlement rejected: missing required costBasis for attempt ${dispatch.attemptId}`);
26228
27692
  }
26229
- push("routing.attempt.recorded", { routing: producerRoutingRecord(context, dispatch, result, now) });
26230
27693
  const outcome = typeof result.outcome === "string" ? result.outcome : void 0;
26231
27694
  const known = outcome !== void 0 && dispatch.step.outcomes.includes(outcome);
27695
+ push("routing.attempt.recorded", {
27696
+ routing: producerRoutingRecord(
27697
+ context,
27698
+ dispatch,
27699
+ result,
27700
+ now,
27701
+ known ? kxmAttemptFinalOutcome(dispatch.step, { resultClass: "outcome", outcome }) : "failed"
27702
+ )
27703
+ });
26232
27704
  if (!known) {
26233
27705
  push("assignment.result_recorded", {
26234
27706
  assignmentId: dispatch.assignmentId,
@@ -27175,7 +28647,7 @@ Return a final JSON object with an "outcome" field chosen from ${JSON.stringify(
27175
28647
  }
27176
28648
  const spawnFn = options.spawnProcess ?? defaultSpawn;
27177
28649
  const cwd = options.projectRoot ?? process.cwd();
27178
- const evidence = await beginOneShotEvidence(options.evidenceRoot ?? join12(kxmUserStateRoot(), "runtime", "oneshot-evidence"), {
28650
+ const evidence = await beginOneShotEvidence(options.evidenceRoot ?? join15(kxmUserStateRoot(), "runtime", "oneshot-evidence"), {
27179
28651
  runId: request.runId,
27180
28652
  stepId: request.stepId,
27181
28653
  attemptId: request.attemptId,
@@ -27320,17 +28792,6 @@ Return a final JSON object with an "outcome" field chosen from ${JSON.stringify(
27320
28792
 
27321
28793
  // plugins/kxm/src/client.ts
27322
28794
  import { hostname } from "node:os";
27323
-
27324
- // plugins/kxm/src/protocol.ts
27325
- var DEFAULT_MESSAGE_TTL_MS = 24 * 60 * 6e4;
27326
- var MAX_MESSAGE_TTL_MS = 7 * 24 * 60 * 6e4;
27327
- var DEFAULT_MESSAGE_RETENTION_MS = 7 * 24 * 60 * 6e4;
27328
- var MAX_BODY_BYTES = 256 * 1024;
27329
- var MAX_AGENT_HOST_CHARS = 64;
27330
- var MAX_LEASE_TTL_MS = 10 * 6e4;
27331
- var DEFAULT_LEASE_TTL_MS = 5 * 6e4;
27332
-
27333
- // plugins/kxm/src/client.ts
27334
28795
  function defaultHostLabel() {
27335
28796
  try {
27336
28797
  return hostname().trim().slice(0, MAX_AGENT_HOST_CHARS) || void 0;
@@ -27421,9 +28882,9 @@ var RuntimeHubClient = class {
27421
28882
  };
27422
28883
 
27423
28884
  // plugins/kxm/src/hub-binding.ts
27424
- import { existsSync as existsSync10, mkdirSync as mkdirSync4, readFileSync as readFileSync8, renameSync, rmSync, writeFileSync as writeFileSync4 } from "node:fs";
28885
+ import { existsSync as existsSync12, mkdirSync as mkdirSync6, readFileSync as readFileSync10, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "node:fs";
27425
28886
  import { homedir as homedir2 } from "node:os";
27426
- import { dirname as dirname6, isAbsolute as isAbsolute4, join as join13, resolve as resolve6 } from "node:path";
28887
+ import { dirname as dirname8, isAbsolute as isAbsolute4, join as join16, resolve as resolve7 } from "node:path";
27427
28888
  var HUB_BINDING_SCHEMA = "kxm.hub-binding.v1";
27428
28889
  var HubBindingError = class extends Error {
27429
28890
  constructor(message) {
@@ -27435,20 +28896,20 @@ function resolveUserStateRoot(env) {
27435
28896
  const explicit = env.KXM_STATE_HOME?.trim();
27436
28897
  if (explicit) {
27437
28898
  if (!isAbsolute4(explicit)) throw new HubBindingError("local_state_root_not_absolute");
27438
- return resolve6(explicit);
28899
+ return resolve7(explicit);
27439
28900
  }
27440
28901
  if (process.platform === "win32") {
27441
28902
  const localAppData = env.LOCALAPPDATA?.trim();
27442
- const base2 = localAppData && isAbsolute4(localAppData) ? localAppData : join13(homedir2(), "AppData", "Local");
27443
- return resolve6(base2, "KXM");
28903
+ const base2 = localAppData && isAbsolute4(localAppData) ? localAppData : join16(homedir2(), "AppData", "Local");
28904
+ return resolve7(base2, "KXM");
27444
28905
  }
27445
- if (process.platform === "darwin") return resolve6(homedir2(), "Library", "Application Support", "KXM");
28906
+ if (process.platform === "darwin") return resolve7(homedir2(), "Library", "Application Support", "KXM");
27446
28907
  const xdgState = env.XDG_STATE_HOME?.trim();
27447
- const base = xdgState && isAbsolute4(xdgState) ? xdgState : join13(homedir2(), ".local", "state");
27448
- return resolve6(base, "kxm");
28908
+ const base = xdgState && isAbsolute4(xdgState) ? xdgState : join16(homedir2(), ".local", "state");
28909
+ return resolve7(base, "kxm");
27449
28910
  }
27450
28911
  function hubBindingFile(env = process.env) {
27451
- return join13(resolveUserStateRoot(env), "hub-binding.json");
28912
+ return join16(resolveUserStateRoot(env), "hub-binding.json");
27452
28913
  }
27453
28914
  function validateHubUrl(raw) {
27454
28915
  let parsed;
@@ -27468,10 +28929,10 @@ function isIsoTimestamp(value) {
27468
28929
  }
27469
28930
  function readHubBinding(env = process.env) {
27470
28931
  const file = hubBindingFile(env);
27471
- if (!existsSync10(file)) return void 0;
28932
+ if (!existsSync12(file)) return void 0;
27472
28933
  let parsed;
27473
28934
  try {
27474
- parsed = JSON.parse(readFileSync8(file, "utf8"));
28935
+ parsed = JSON.parse(readFileSync10(file, "utf8"));
27475
28936
  } catch {
27476
28937
  throw new HubBindingError(`malformed hub binding at ${file}`);
27477
28938
  }
@@ -27493,9 +28954,9 @@ function readHubBinding(env = process.env) {
27493
28954
  }
27494
28955
 
27495
28956
  // plugins/kxm/src/hub-env.ts
27496
- import { existsSync as existsSync11, mkdirSync as mkdirSync5, readFileSync as readFileSync9, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "node:fs";
28957
+ import { existsSync as existsSync13, mkdirSync as mkdirSync7, readFileSync as readFileSync11, renameSync as renameSync3, rmSync as rmSync3, writeFileSync as writeFileSync7 } from "node:fs";
27497
28958
  import { homedir as homedir3 } from "node:os";
27498
- import { dirname as dirname7, isAbsolute as isAbsolute5, join as join14, resolve as resolve7 } from "node:path";
28959
+ import { dirname as dirname9, isAbsolute as isAbsolute5, join as join17, resolve as resolve8 } from "node:path";
27499
28960
  var HUB_ENV_SCHEMA = "kxm.hub-env.v1";
27500
28961
  var HubEnvError = class extends Error {
27501
28962
  constructor(message) {
@@ -27507,27 +28968,27 @@ function resolveUserStateRoot2(env) {
27507
28968
  const explicit = env.KXM_STATE_HOME?.trim();
27508
28969
  if (explicit) {
27509
28970
  if (!isAbsolute5(explicit)) throw new HubEnvError("local_state_root_not_absolute");
27510
- return resolve7(explicit);
28971
+ return resolve8(explicit);
27511
28972
  }
27512
28973
  if (process.platform === "win32") {
27513
28974
  const localAppData = env.LOCALAPPDATA?.trim();
27514
- const base2 = localAppData && isAbsolute5(localAppData) ? localAppData : join14(homedir3(), "AppData", "Local");
27515
- return resolve7(base2, "KXM");
28975
+ const base2 = localAppData && isAbsolute5(localAppData) ? localAppData : join17(homedir3(), "AppData", "Local");
28976
+ return resolve8(base2, "KXM");
27516
28977
  }
27517
- if (process.platform === "darwin") return resolve7(homedir3(), "Library", "Application Support", "KXM");
28978
+ if (process.platform === "darwin") return resolve8(homedir3(), "Library", "Application Support", "KXM");
27518
28979
  const xdgState = env.XDG_STATE_HOME?.trim();
27519
- const base = xdgState && isAbsolute5(xdgState) ? xdgState : join14(homedir3(), ".local", "state");
27520
- return resolve7(base, "kxm");
28980
+ const base = xdgState && isAbsolute5(xdgState) ? xdgState : join17(homedir3(), ".local", "state");
28981
+ return resolve8(base, "kxm");
27521
28982
  }
27522
28983
  function hubEnvFile(env = process.env) {
27523
- return join14(resolveUserStateRoot2(env), "hub-env.json");
28984
+ return join17(resolveUserStateRoot2(env), "hub-env.json");
27524
28985
  }
27525
28986
  function readHubEnvRecord(env = process.env) {
27526
28987
  const file = hubEnvFile(env);
27527
- if (!existsSync11(file)) return void 0;
28988
+ if (!existsSync13(file)) return void 0;
27528
28989
  let parsed;
27529
28990
  try {
27530
- parsed = JSON.parse(readFileSync9(file, "utf8"));
28991
+ parsed = JSON.parse(readFileSync11(file, "utf8"));
27531
28992
  } catch (error) {
27532
28993
  throw new HubEnvError(`hub env file is malformed at ${file}: ${error instanceof Error ? error.message : String(error)}`);
27533
28994
  }
@@ -27567,8 +29028,8 @@ function resolveClientHubAuthToken(env, project) {
27567
29028
  }
27568
29029
 
27569
29030
  // plugins/kxm/src/logger.ts
27570
- import { appendFileSync, existsSync as existsSync12, mkdirSync as mkdirSync6, renameSync as renameSync3, statSync as statSync3, unlinkSync as unlinkSync2 } from "node:fs";
27571
- import { dirname as dirname8 } from "node:path";
29031
+ import { appendFileSync, existsSync as existsSync14, mkdirSync as mkdirSync8, renameSync as renameSync4, statSync as statSync4, unlinkSync as unlinkSync2 } from "node:fs";
29032
+ import { dirname as dirname10 } from "node:path";
27572
29033
  var LOG_LEVEL_PRIORITY = {
27573
29034
  debug: 10,
27574
29035
  info: 20,
@@ -27605,7 +29066,7 @@ function redactLogValue(val, key) {
27605
29066
  function rotateLogFiles(filePath, maxFiles) {
27606
29067
  for (let i = maxFiles; i >= 1; i--) {
27607
29068
  const current = `${filePath}.${i}`;
27608
- if (existsSync12(current)) {
29069
+ if (existsSync14(current)) {
27609
29070
  if (i >= maxFiles) {
27610
29071
  try {
27611
29072
  unlinkSync2(current);
@@ -27613,15 +29074,15 @@ function rotateLogFiles(filePath, maxFiles) {
27613
29074
  }
27614
29075
  } else {
27615
29076
  try {
27616
- renameSync3(current, `${filePath}.${i + 1}`);
29077
+ renameSync4(current, `${filePath}.${i + 1}`);
27617
29078
  } catch {
27618
29079
  }
27619
29080
  }
27620
29081
  }
27621
29082
  }
27622
- if (existsSync12(filePath)) {
29083
+ if (existsSync14(filePath)) {
27623
29084
  try {
27624
- renameSync3(filePath, `${filePath}.1`);
29085
+ renameSync4(filePath, `${filePath}.1`);
27625
29086
  } catch {
27626
29087
  }
27627
29088
  }
@@ -27636,9 +29097,9 @@ function createLogger(options) {
27636
29097
  const shouldStdout = options.stdout ?? !isDaemon;
27637
29098
  const correlationDefaults = options.correlation ?? {};
27638
29099
  let currentSize = 0;
27639
- if (filePath && existsSync12(filePath)) {
29100
+ if (filePath && existsSync14(filePath)) {
27640
29101
  try {
27641
- currentSize = statSync3(filePath).size;
29102
+ currentSize = statSync4(filePath).size;
27642
29103
  } catch {
27643
29104
  currentSize = 0;
27644
29105
  }
@@ -27674,7 +29135,7 @@ function createLogger(options) {
27674
29135
  currentSize = 0;
27675
29136
  }
27676
29137
  try {
27677
- mkdirSync6(dirname8(filePath), { recursive: true });
29138
+ mkdirSync8(dirname10(filePath), { recursive: true });
27678
29139
  appendFileSync(filePath, line, { encoding: "utf8", mode: 384 });
27679
29140
  currentSize += lineBytes;
27680
29141
  } catch {
@@ -27717,26 +29178,26 @@ function createLogger(options) {
27717
29178
 
27718
29179
  // plugins/kxm/src/runtime-supervisor.ts
27719
29180
  function kxmSupervisorTokenFile(paths) {
27720
- return join15(paths.runtimeDir, "supervisor.token");
29181
+ return join18(paths.runtimeDir, "supervisor.token");
27721
29182
  }
27722
29183
  function publishKxmSupervisorToken(paths, token) {
27723
29184
  const file = kxmSupervisorTokenFile(paths);
27724
- mkdirSync7(dirname9(file), { recursive: true, mode: 448 });
29185
+ mkdirSync9(dirname11(file), { recursive: true, mode: 448 });
27725
29186
  const temp = `${file}.${process.pid}.tmp`;
27726
- writeFileSync6(temp, `${token}
29187
+ writeFileSync8(temp, `${token}
27727
29188
  `, { encoding: "utf8", mode: 384 });
27728
29189
  try {
27729
29190
  chmodSync2(temp, 384);
27730
29191
  } catch {
27731
29192
  }
27732
- renameSync4(temp, file);
29193
+ renameSync5(temp, file);
27733
29194
  try {
27734
29195
  chmodSync2(file, 384);
27735
29196
  } catch {
27736
29197
  }
27737
29198
  }
27738
29199
  function hashKxmSupervisorToken(token) {
27739
- return `sha256:${createHash12("sha256").update(`kxm-runtime-supervisor\0${token}`, "utf8").digest("hex")}`;
29200
+ return `sha256:${createHash13("sha256").update(`kxm-runtime-supervisor\0${token}`, "utf8").digest("hex")}`;
27740
29201
  }
27741
29202
  function readKxmSupervisorToken(paths) {
27742
29203
  const file = kxmSupervisorTokenFile(paths);
@@ -27745,7 +29206,7 @@ function readKxmSupervisorToken(paths) {
27745
29206
  if (stat.isSymbolicLink() || !stat.isFile()) {
27746
29207
  throw runtimeError("runtime_path_invalid", file, "supervisor token file must be a regular file, not a link");
27747
29208
  }
27748
- const token = readFileSync10(file, "utf8").trim();
29209
+ const token = readFileSync12(file, "utf8").trim();
27749
29210
  return token.length >= 32 ? token : void 0;
27750
29211
  }
27751
29212
  function processAlive(pid) {
@@ -27759,16 +29220,16 @@ function processAlive(pid) {
27759
29220
  var HEARTBEAT_STALE_MS = 15e3;
27760
29221
  var SUPERVISOR_ERROR_MAX_AGE_MS = 3e4;
27761
29222
  function supervisorErrorFile(paths) {
27762
- return join15(paths.runtimeDir, "supervisor.error");
29223
+ return join18(paths.runtimeDir, "supervisor.error");
27763
29224
  }
27764
29225
  function clearSupervisorError(paths) {
27765
29226
  const file = supervisorErrorFile(paths);
27766
- if (existsSync13(file)) rmSync3(file, { force: true });
29227
+ if (existsSync15(file)) rmSync4(file, { force: true });
27767
29228
  }
27768
29229
  function recordSupervisorError(paths, message) {
27769
29230
  try {
27770
- mkdirSync7(paths.runtimeDir, { recursive: true, mode: 448 });
27771
- writeFileSync6(supervisorErrorFile(paths), `${message}
29231
+ mkdirSync9(paths.runtimeDir, { recursive: true, mode: 448 });
29232
+ writeFileSync8(supervisorErrorFile(paths), `${message}
27772
29233
  `, { encoding: "utf8", mode: 384 });
27773
29234
  } catch {
27774
29235
  }
@@ -27780,13 +29241,13 @@ function readRecentSupervisorError(paths) {
27780
29241
  const ageMs = Date.now() - stat.mtimeMs;
27781
29242
  if (ageMs > SUPERVISOR_ERROR_MAX_AGE_MS) return void 0;
27782
29243
  try {
27783
- return readFileSync10(file, "utf8").trim();
29244
+ return readFileSync12(file, "utf8").trim();
27784
29245
  } catch {
27785
29246
  return void 0;
27786
29247
  }
27787
29248
  }
27788
29249
  function kxmSupervisorStatus(paths) {
27789
- if (!existsSync13(paths.registryDb)) return { running: false };
29250
+ if (!existsSync15(paths.registryDb)) return { running: false };
27790
29251
  const registry = new KxmRuntimeRegistry(paths.registryDb);
27791
29252
  try {
27792
29253
  const record2 = registry.supervisor();
@@ -27856,7 +29317,7 @@ async function ensureKxmSupervisor(options = {}) {
27856
29317
  throw runtimeError("runtime_supervisor_unreachable", paths.registryDb, `runtime supervisor pid ${status.pid} is registered as running but cannot be probed`);
27857
29318
  }
27858
29319
  clearSupervisorError(paths);
27859
- const scriptPath = join15(findKxmRepoRoot(import.meta.url), "scripts", "kxm-runtime-supervisor.mjs");
29320
+ const scriptPath = join18(findKxmRepoRoot(import.meta.url), "scripts", "kxm-runtime-supervisor.mjs");
27860
29321
  const spawnImpl = options.spawnImpl ?? ((script, env) => {
27861
29322
  const child = spawn3(process.execPath, [script], {
27862
29323
  detached: true,
@@ -27941,8 +29402,8 @@ async function waitForDriveSessions(settled, graceMs) {
27941
29402
  try {
27942
29403
  await Promise.race([
27943
29404
  Promise.allSettled(settled),
27944
- new Promise((resolve8) => {
27945
- timeout = setTimeout(resolve8, graceMs);
29405
+ new Promise((resolve9) => {
29406
+ timeout = setTimeout(resolve9, graceMs);
27946
29407
  timeout.unref?.();
27947
29408
  })
27948
29409
  ]);
@@ -28079,7 +29540,7 @@ function syncFailureText(error) {
28079
29540
  return `${code ? `${code}: ` : ""}${message}`.replace(/(kxm_[A-Za-z0-9_]+|[A-Za-z0-9._-]{40,})/g, "[redacted]").slice(0, 300);
28080
29541
  }
28081
29542
  function runtimeSupervisorLogFile(paths) {
28082
- return join15(paths.runtimeDir, "logs", "kxm-runtime.jsonl");
29543
+ return join18(paths.runtimeDir, "logs", "kxm-runtime.jsonl");
28083
29544
  }
28084
29545
  async function startKxmRuntimeSupervisor(options = {}) {
28085
29546
  const now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
@@ -28095,8 +29556,8 @@ async function startKxmRuntimeSupervisor(options = {}) {
28095
29556
  }
28096
29557
  }
28097
29558
  async function startKxmRuntimeSupervisorInner(paths, requestedPortOption, now, env) {
28098
- mkdirSync7(paths.runtimeDir, { recursive: true, mode: 448 });
28099
- mkdirSync7(paths.projectsDir, { recursive: true, mode: 448 });
29559
+ mkdirSync9(paths.runtimeDir, { recursive: true, mode: 448 });
29560
+ mkdirSync9(paths.projectsDir, { recursive: true, mode: 448 });
28100
29561
  const logger = createLogger({
28101
29562
  component: "runtime",
28102
29563
  path: runtimeSupervisorLogFile(paths),
@@ -28106,7 +29567,7 @@ async function startKxmRuntimeSupervisorInner(paths, requestedPortOption, now, e
28106
29567
  const token = randomBytes2(32).toString("hex");
28107
29568
  const tokenHash = hashKxmSupervisorToken(token);
28108
29569
  const registry = new KxmRuntimeRegistry(paths.registryDb);
28109
- const runtimeId = `rtm_${createHash12("sha256").update(`${paths.stateRoot}\0${process.pid}\0${now()}\0${randomBytes2(16).toString("hex")}`, "utf8").digest("hex").slice(0, 24)}`;
29570
+ const runtimeId = `rtm_${createHash13("sha256").update(`${paths.stateRoot}\0${process.pid}\0${now()}\0${randomBytes2(16).toString("hex")}`, "utf8").digest("hex").slice(0, 24)}`;
28110
29571
  const requestedPort = requestedPortOption ?? 0;
28111
29572
  let activeRuntimeId = runtimeId;
28112
29573
  const contexts = /* @__PURE__ */ new Map();
@@ -28128,7 +29589,7 @@ async function startKxmRuntimeSupervisorInner(paths, requestedPortOption, now, e
28128
29589
  const key = projectRuntimeKey(projectRoot);
28129
29590
  const existing = contexts.get(key);
28130
29591
  if (existing) return existing;
28131
- const context = openKxmRuntimeContext(projectRoot, { homeRuntimeId: activeRuntimeId, stateRoot: paths.stateRoot });
29592
+ const context = openKxmRuntimeContext(projectRoot, { homeRuntimeId: activeRuntimeId, stateRoot: paths.stateRoot, logger });
28132
29593
  registerSyncCredentials(context);
28133
29594
  contexts.set(key, context);
28134
29595
  return context;
@@ -28661,10 +30122,20 @@ async function kxmRuntimeRequest(handle, method, path, body) {
28661
30122
  if (!response.ok) {
28662
30123
  const code = typeof payload.error === "string" ? payload.error : "runtime_request_failed";
28663
30124
  const message = typeof payload.message === "string" ? payload.message : `runtime request failed with HTTP ${response.status}`;
28664
- throw runtimeError(code, path, message);
30125
+ throw runtimeError(code, path, `${message}${handoffSuffix(payload.handoff)}`);
28665
30126
  }
28666
30127
  return payload;
28667
30128
  }
30129
+ var HANDOFF_TEXT_MAX = 200;
30130
+ function handoffSuffix(handoff) {
30131
+ if (!handoff || typeof handoff !== "object" || Array.isArray(handoff)) return "";
30132
+ const parts = [];
30133
+ for (const key of ["reason", "field", "detail"]) {
30134
+ const value = handoff[key];
30135
+ if (typeof value === "string" && value.length > 0) parts.push(`${key} ${value.slice(0, HANDOFF_TEXT_MAX)}`);
30136
+ }
30137
+ return parts.length > 0 ? ` (handoff ${parts.join("; ")})` : "";
30138
+ }
28668
30139
  export {
28669
30140
  DEFAULT_RUNTIME_STOP_GRACE_MS,
28670
30141
  DEFAULT_RUNTIME_SYNC_INTERVAL_MS,