@kontextmind/kxm 0.7.91 → 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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.kxm/workflows/default.yaml +1 -1
- package/CHANGELOG.md +212 -0
- package/README.md +3 -0
- package/docs/README.md +3 -0
- package/docs/agent-skills.md +123 -60
- package/docs/architecture.md +5 -2
- package/docs/cli-reference.md +3527 -0
- package/docs/config-reference.md +1943 -0
- package/docs/configuration.md +30 -4
- package/docs/continuous-improvement.md +122 -10
- package/docs/contracts/routing.md +95 -11
- package/docs/harness-routing.md +616 -0
- package/docs/kxm-handbook.md +106 -19
- package/docs/templates/README.md +1 -1
- package/docs/test-matrix.md +12 -6
- package/docs/troubleshooting.md +2 -2
- package/examples/project/.kxm/workflows/fix.yaml +1 -1
- package/examples/project/.kxm/workflows/improve.yaml +1 -1
- package/package.json +1 -1
- package/plugins/kxm/.claude-plugin/plugin.json +9 -10
- package/plugins/kxm/README.md +238 -56
- package/plugins/kxm/dist/claude-hook.js +10083 -0
- package/plugins/kxm/dist/cli.js +2487 -1848
- package/plugins/kxm/dist/client.js +64 -0
- package/plugins/kxm/dist/core.js +102 -9
- package/plugins/kxm/dist/extension.js +210 -68
- package/plugins/kxm/dist/mcp-server.js +217 -40
- package/plugins/kxm/dist/runtime-supervisor.js +1628 -157
- package/plugins/kxm/dist/runtime.js +1874 -298
- package/plugins/kxm/dist/server.js +416 -82
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/skills/hints.json +1 -1
- package/plugins/kxm/skills/kxm/SKILL.md +48 -24
- package/plugins/kxm/skills/kxm/references/protocol.md +3 -3
- package/plugins/kxm/skills/kxm-context-memory/SKILL.md +67 -21
- package/plugins/kxm/skills/kxm-definitions/SKILL.md +9 -0
- package/plugins/kxm/skills/kxm-harness-auth/SKILL.md +82 -16
- package/plugins/kxm/skills/kxm-harvest/SKILL.md +1 -1
- package/plugins/kxm/skills/kxm-hub-ops/SKILL.md +55 -27
- package/plugins/kxm/skills/kxm-insights/SKILL.md +1 -1
- package/plugins/kxm/skills/kxm-mind/SKILL.md +2 -2
- package/plugins/kxm/skills/{kxm-setup → kxm-mind-setup}/SKILL.md +4 -4
- package/plugins/kxm/skills/kxm-peer/SKILL.md +68 -93
- package/plugins/kxm/skills/kxm-project-setup/SKILL.md +156 -23
- package/plugins/kxm/skills/kxm-projects/SKILL.md +1 -1
- package/plugins/kxm/skills/kxm-protocol/SKILL.md +1 -1
- package/plugins/kxm/skills/kxm-query/SKILL.md +1 -1
- package/plugins/kxm/skills/kxm-routing-improve/SKILL.md +74 -15
- package/plugins/kxm/skills/kxm-runs/SKILL.md +46 -17
- package/plugins/kxm/skills/kxm-session/SKILL.md +64 -36
- package/plugins/kxm/skills/kxm-skill-lifecycle/SKILL.md +44 -15
- package/plugins/kxm/skills/kxm-tasks/SKILL.md +16 -4
- package/plugins/kxm/skills/kxm-triage/SKILL.md +1 -1
- package/plugins/kxm/skills/kxm-work/SKILL.md +1 -1
- package/plugins/kxm/skills/kxm-workflow/SKILL.md +60 -19
- package/plugins/kxm/src/arbiter.ts +67 -22
- package/plugins/kxm/src/autocomplete.ts +1 -1
- package/plugins/kxm/src/claude-hook.ts +192 -0
- package/plugins/kxm/src/cli/project.ts +11 -5
- package/plugins/kxm/src/cli/system.ts +85 -13
- package/plugins/kxm/src/cli/types.ts +4 -1
- package/plugins/kxm/src/cli/workflows.ts +18 -16
- package/plugins/kxm/src/cli.ts +23 -13
- package/plugins/kxm/src/client.ts +15 -4
- package/plugins/kxm/src/commands.ts +19 -9
- package/plugins/kxm/src/config.ts +42 -7
- package/plugins/kxm/src/context-packet.ts +14 -2
- package/plugins/kxm/src/context.ts +16 -5
- package/plugins/kxm/src/dispatch-context.ts +286 -0
- package/plugins/kxm/src/engine-plan.ts +40 -0
- package/plugins/kxm/src/engine.ts +138 -6
- package/plugins/kxm/src/hub-env.ts +17 -1
- package/plugins/kxm/src/hub.ts +92 -29
- package/plugins/kxm/src/improve-sources.ts +228 -0
- package/plugins/kxm/src/improve.ts +325 -140
- package/plugins/kxm/src/local-snapshot.ts +101 -42
- package/plugins/kxm/src/mcp-server.ts +129 -30
- package/plugins/kxm/src/memory.ts +43 -20
- package/plugins/kxm/src/project-config.ts +25 -0
- package/plugins/kxm/src/protocol.ts +11 -0
- package/plugins/kxm/src/relevance.ts +138 -0
- package/plugins/kxm/src/retrospective.ts +16 -10
- package/plugins/kxm/src/runtime-service.ts +8 -1
- package/plugins/kxm/src/runtime-supervisor.ts +16 -2
- package/plugins/kxm/src/session-token-hint.ts +17 -0
- package/plugins/kxm/src/suggest.ts +7 -7
- package/plugins/kxm/src/workflow-manager.ts +80 -78
- package/plugins/kxm/src/workflow.ts +202 -12
- package/scripts/build-runtime.mjs +7 -1
- package/scripts/check-generated.mjs +1 -0
- 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
|
|
167
|
+
function stringify4(x) {
|
|
168
168
|
return new _Code(safeStringify(x));
|
|
169
169
|
}
|
|
170
|
-
exports.stringify =
|
|
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 =
|
|
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
|
|
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
|
-
|
|
3838
|
+
parse7(serialize(uri, options), options);
|
|
3839
3839
|
}
|
|
3840
3840
|
return uri;
|
|
3841
3841
|
}
|
|
3842
|
-
function
|
|
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 =
|
|
3879
|
-
relative4 =
|
|
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
|
|
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:
|
|
4204
|
+
resolve: resolve9,
|
|
4205
4205
|
resolveComponent,
|
|
4206
4206
|
equal,
|
|
4207
4207
|
serialize,
|
|
4208
|
-
parse:
|
|
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 (!
|
|
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 <
|
|
6928
|
-
let sch =
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
9283
|
-
return
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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(
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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 =
|
|
14722
|
+
exports.parse = parse7;
|
|
14723
14723
|
exports.parseAllDocuments = parseAllDocuments;
|
|
14724
14724
|
exports.parseDocument = parseDocument2;
|
|
14725
|
-
exports.stringify =
|
|
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
|
|
14784
|
-
import { chmodSync as chmodSync2, existsSync as
|
|
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
|
|
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((
|
|
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
|
-
|
|
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
|
|
16928
|
-
const minimumPassed =
|
|
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
|
|
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:
|
|
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
|
|
21162
|
-
if (!
|
|
21163
|
-
const declared = stringValue2(
|
|
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(
|
|
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
|
|
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((
|
|
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(
|
|
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
|
|
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
|
|
23253
|
-
import { createHash as
|
|
23254
|
-
import { existsSync as
|
|
23255
|
-
import { join as
|
|
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
|
|
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
|
|
23552
|
-
import { isAbsolute as isAbsolute3, relative as relative3, resolve as
|
|
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 =
|
|
23559
|
-
const lexicalPath =
|
|
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 =
|
|
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 =
|
|
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,
|
|
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
|
|
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 =
|
|
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((
|
|
23748
|
-
this.resolveOutcome =
|
|
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
|
|
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
|
|
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:${
|
|
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_${
|
|
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((
|
|
25068
|
-
pending.resolve =
|
|
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
|
|
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 =
|
|
25447
|
-
if (
|
|
26850
|
+
const agentFile = join14(projectRoot, ".kxm", "agents", `${agentId}.yaml`);
|
|
26851
|
+
if (existsSync11(agentFile)) {
|
|
25448
26852
|
try {
|
|
25449
|
-
const parsed = (0,
|
|
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 =
|
|
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
|
-
|
|
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 ["
|
|
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 ??
|
|
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
|
|
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
|
|
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
|
|
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 :
|
|
27443
|
-
return
|
|
28903
|
+
const base2 = localAppData && isAbsolute4(localAppData) ? localAppData : join16(homedir2(), "AppData", "Local");
|
|
28904
|
+
return resolve7(base2, "KXM");
|
|
27444
28905
|
}
|
|
27445
|
-
if (process.platform === "darwin") return
|
|
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 :
|
|
27448
|
-
return
|
|
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
|
|
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 (!
|
|
28932
|
+
if (!existsSync12(file)) return void 0;
|
|
27472
28933
|
let parsed;
|
|
27473
28934
|
try {
|
|
27474
|
-
parsed = JSON.parse(
|
|
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
|
|
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
|
|
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
|
|
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 :
|
|
27515
|
-
return
|
|
28975
|
+
const base2 = localAppData && isAbsolute5(localAppData) ? localAppData : join17(homedir3(), "AppData", "Local");
|
|
28976
|
+
return resolve8(base2, "KXM");
|
|
27516
28977
|
}
|
|
27517
|
-
if (process.platform === "darwin") return
|
|
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 :
|
|
27520
|
-
return
|
|
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
|
|
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 (!
|
|
28988
|
+
if (!existsSync13(file)) return void 0;
|
|
27528
28989
|
let parsed;
|
|
27529
28990
|
try {
|
|
27530
|
-
parsed = JSON.parse(
|
|
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
|
|
27571
|
-
import { dirname as
|
|
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 (
|
|
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
|
-
|
|
29077
|
+
renameSync4(current, `${filePath}.${i + 1}`);
|
|
27617
29078
|
} catch {
|
|
27618
29079
|
}
|
|
27619
29080
|
}
|
|
27620
29081
|
}
|
|
27621
29082
|
}
|
|
27622
|
-
if (
|
|
29083
|
+
if (existsSync14(filePath)) {
|
|
27623
29084
|
try {
|
|
27624
|
-
|
|
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 &&
|
|
29100
|
+
if (filePath && existsSync14(filePath)) {
|
|
27640
29101
|
try {
|
|
27641
|
-
currentSize =
|
|
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
|
-
|
|
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
|
|
29181
|
+
return join18(paths.runtimeDir, "supervisor.token");
|
|
27721
29182
|
}
|
|
27722
29183
|
function publishKxmSupervisorToken(paths, token) {
|
|
27723
29184
|
const file = kxmSupervisorTokenFile(paths);
|
|
27724
|
-
|
|
29185
|
+
mkdirSync9(dirname11(file), { recursive: true, mode: 448 });
|
|
27725
29186
|
const temp = `${file}.${process.pid}.tmp`;
|
|
27726
|
-
|
|
29187
|
+
writeFileSync8(temp, `${token}
|
|
27727
29188
|
`, { encoding: "utf8", mode: 384 });
|
|
27728
29189
|
try {
|
|
27729
29190
|
chmodSync2(temp, 384);
|
|
27730
29191
|
} catch {
|
|
27731
29192
|
}
|
|
27732
|
-
|
|
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:${
|
|
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 =
|
|
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
|
|
29223
|
+
return join18(paths.runtimeDir, "supervisor.error");
|
|
27763
29224
|
}
|
|
27764
29225
|
function clearSupervisorError(paths) {
|
|
27765
29226
|
const file = supervisorErrorFile(paths);
|
|
27766
|
-
if (
|
|
29227
|
+
if (existsSync15(file)) rmSync4(file, { force: true });
|
|
27767
29228
|
}
|
|
27768
29229
|
function recordSupervisorError(paths, message) {
|
|
27769
29230
|
try {
|
|
27770
|
-
|
|
27771
|
-
|
|
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
|
|
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 (!
|
|
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 =
|
|
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((
|
|
27945
|
-
timeout = setTimeout(
|
|
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
|
|
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
|
-
|
|
28099
|
-
|
|
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_${
|
|
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,
|