@kontextmind/kxm 0.7.92 → 0.7.93
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.kxm/workflows/default.yaml +1 -1
- package/CHANGELOG.md +204 -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 +29 -3
- 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 +3068 -2446
- 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 +61 -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/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
|
@@ -163,10 +163,10 @@ var require_code = __commonJS({
|
|
|
163
163
|
function interpolate(x) {
|
|
164
164
|
return typeof x == "number" || typeof x == "boolean" || x === null ? x : safeStringify(Array.isArray(x) ? x.join(",") : x);
|
|
165
165
|
}
|
|
166
|
-
function
|
|
166
|
+
function stringify4(x) {
|
|
167
167
|
return new _Code(safeStringify(x));
|
|
168
168
|
}
|
|
169
|
-
exports.stringify =
|
|
169
|
+
exports.stringify = stringify4;
|
|
170
170
|
function safeStringify(x) {
|
|
171
171
|
return JSON.stringify(x).replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
172
172
|
}
|
|
@@ -2986,7 +2986,7 @@ var require_compile = __commonJS({
|
|
|
2986
2986
|
const schOrFunc = root.refs[ref];
|
|
2987
2987
|
if (schOrFunc)
|
|
2988
2988
|
return schOrFunc;
|
|
2989
|
-
let _sch =
|
|
2989
|
+
let _sch = resolve11.call(this, root, ref);
|
|
2990
2990
|
if (_sch === void 0) {
|
|
2991
2991
|
const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
|
|
2992
2992
|
const { schemaId } = this.opts;
|
|
@@ -3013,7 +3013,7 @@ var require_compile = __commonJS({
|
|
|
3013
3013
|
function sameSchemaEnv(s1, s2) {
|
|
3014
3014
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
3015
3015
|
}
|
|
3016
|
-
function
|
|
3016
|
+
function resolve11(root, ref) {
|
|
3017
3017
|
let sch;
|
|
3018
3018
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
3019
3019
|
ref = sch;
|
|
@@ -3834,11 +3834,11 @@ var require_fast_uri = __commonJS({
|
|
|
3834
3834
|
normalizeString(uri, options);
|
|
3835
3835
|
} else if (typeof uri === "object") {
|
|
3836
3836
|
uri = /** @type {T} */
|
|
3837
|
-
|
|
3837
|
+
parse8(serialize(uri, options), options);
|
|
3838
3838
|
}
|
|
3839
3839
|
return uri;
|
|
3840
3840
|
}
|
|
3841
|
-
function
|
|
3841
|
+
function resolve11(baseURI, relativeURI, options) {
|
|
3842
3842
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
3843
3843
|
const {
|
|
3844
3844
|
parsed: baseParsed,
|
|
@@ -3874,8 +3874,8 @@ var require_fast_uri = __commonJS({
|
|
|
3874
3874
|
function resolveComponent(base, relative5, options, skipNormalization) {
|
|
3875
3875
|
const target = {};
|
|
3876
3876
|
if (!skipNormalization) {
|
|
3877
|
-
base =
|
|
3878
|
-
relative5 =
|
|
3877
|
+
base = parse8(serialize(base, options), options);
|
|
3878
|
+
relative5 = parse8(serialize(relative5, options), options);
|
|
3879
3879
|
}
|
|
3880
3880
|
options = options || {};
|
|
3881
3881
|
if (!options.tolerant && relative5.scheme) {
|
|
@@ -4167,7 +4167,7 @@ var require_fast_uri = __commonJS({
|
|
|
4167
4167
|
}
|
|
4168
4168
|
return { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme };
|
|
4169
4169
|
}
|
|
4170
|
-
function
|
|
4170
|
+
function parse8(uri, opts) {
|
|
4171
4171
|
return parseWithStatus(uri, opts).parsed;
|
|
4172
4172
|
}
|
|
4173
4173
|
function normalizeString(uri, opts) {
|
|
@@ -4200,11 +4200,11 @@ var require_fast_uri = __commonJS({
|
|
|
4200
4200
|
var fastUri = {
|
|
4201
4201
|
SCHEMES,
|
|
4202
4202
|
normalize,
|
|
4203
|
-
resolve:
|
|
4203
|
+
resolve: resolve11,
|
|
4204
4204
|
resolveComponent,
|
|
4205
4205
|
equal,
|
|
4206
4206
|
serialize,
|
|
4207
|
-
parse:
|
|
4207
|
+
parse: parse8
|
|
4208
4208
|
};
|
|
4209
4209
|
module.exports = fastUri;
|
|
4210
4210
|
module.exports.default = fastUri;
|
|
@@ -6886,7 +6886,7 @@ var require_discriminator = __commonJS({
|
|
|
6886
6886
|
error,
|
|
6887
6887
|
code(cxt) {
|
|
6888
6888
|
const { gen, data, schema, parentSchema, it } = cxt;
|
|
6889
|
-
const { oneOf } = parentSchema;
|
|
6889
|
+
const { oneOf: oneOf2 } = parentSchema;
|
|
6890
6890
|
if (!it.opts.discriminator) {
|
|
6891
6891
|
throw new Error("discriminator: requires discriminator option");
|
|
6892
6892
|
}
|
|
@@ -6895,7 +6895,7 @@ var require_discriminator = __commonJS({
|
|
|
6895
6895
|
throw new Error("discriminator: requires propertyName");
|
|
6896
6896
|
if (schema.mapping)
|
|
6897
6897
|
throw new Error("discriminator: mapping is not supported");
|
|
6898
|
-
if (!
|
|
6898
|
+
if (!oneOf2)
|
|
6899
6899
|
throw new Error("discriminator: requires oneOf keyword");
|
|
6900
6900
|
const valid = gen.let("valid", false);
|
|
6901
6901
|
const tag = gen.const("tag", (0, codegen_1._)`${data}${(0, codegen_1.getProperty)(tagName)}`);
|
|
@@ -6923,8 +6923,8 @@ var require_discriminator = __commonJS({
|
|
|
6923
6923
|
const oneOfMapping = {};
|
|
6924
6924
|
const topRequired = hasRequired(parentSchema);
|
|
6925
6925
|
let tagRequired = true;
|
|
6926
|
-
for (let i = 0; i <
|
|
6927
|
-
let sch =
|
|
6926
|
+
for (let i = 0; i < oneOf2.length; i++) {
|
|
6927
|
+
let sch = oneOf2[i];
|
|
6928
6928
|
if ((sch === null || sch === void 0 ? void 0 : sch.$ref) && !(0, util_1.schemaHasRulesButRef)(sch, it.self.RULES)) {
|
|
6929
6929
|
const ref = sch.$ref;
|
|
6930
6930
|
sch = compile_1.resolveRef.call(it.self, it.schemaEnv.root, it.baseId, ref);
|
|
@@ -8917,7 +8917,7 @@ var require_stringify = __commonJS({
|
|
|
8917
8917
|
props.push(doc.directives.tagString(tag));
|
|
8918
8918
|
return props.join(" ");
|
|
8919
8919
|
}
|
|
8920
|
-
function
|
|
8920
|
+
function stringify4(item, ctx, onComment, onChompKeep) {
|
|
8921
8921
|
if (identity.isPair(item))
|
|
8922
8922
|
return item.toString(ctx, onComment, onChompKeep);
|
|
8923
8923
|
if (identity.isAlias(item)) {
|
|
@@ -8946,7 +8946,7 @@ var require_stringify = __commonJS({
|
|
|
8946
8946
|
${ctx.indent}${str}`;
|
|
8947
8947
|
}
|
|
8948
8948
|
exports.createStringifyContext = createStringifyContext;
|
|
8949
|
-
exports.stringify =
|
|
8949
|
+
exports.stringify = stringify4;
|
|
8950
8950
|
}
|
|
8951
8951
|
});
|
|
8952
8952
|
|
|
@@ -8956,7 +8956,7 @@ var require_stringifyPair = __commonJS({
|
|
|
8956
8956
|
"use strict";
|
|
8957
8957
|
var identity = require_identity();
|
|
8958
8958
|
var Scalar = require_Scalar();
|
|
8959
|
-
var
|
|
8959
|
+
var stringify4 = require_stringify();
|
|
8960
8960
|
var stringifyComment = require_stringifyComment();
|
|
8961
8961
|
function stringifyPair({ key, value }, ctx, onComment, onChompKeep) {
|
|
8962
8962
|
const { allNullValues, doc, indent, indentStep, options: { commentString, indentSeq, simpleKeys } } = ctx;
|
|
@@ -8978,7 +8978,7 @@ var require_stringifyPair = __commonJS({
|
|
|
8978
8978
|
});
|
|
8979
8979
|
let keyCommentDone = false;
|
|
8980
8980
|
let chompKeep = false;
|
|
8981
|
-
let str =
|
|
8981
|
+
let str = stringify4.stringify(key, ctx, () => keyCommentDone = true, () => chompKeep = true);
|
|
8982
8982
|
if (!explicitKey && !ctx.inFlow && str.length > 1024) {
|
|
8983
8983
|
if (simpleKeys)
|
|
8984
8984
|
throw new Error("With simple keys, single line scalar must not span more than 1024 characters");
|
|
@@ -9030,7 +9030,7 @@ ${indent}:`;
|
|
|
9030
9030
|
ctx.indent = ctx.indent.substring(2);
|
|
9031
9031
|
}
|
|
9032
9032
|
let valueCommentDone = false;
|
|
9033
|
-
const valueStr =
|
|
9033
|
+
const valueStr = stringify4.stringify(value, ctx, () => valueCommentDone = true, () => chompKeep = true);
|
|
9034
9034
|
let ws = " ";
|
|
9035
9035
|
if (keyComment || vsb || vcb) {
|
|
9036
9036
|
ws = vsb ? "\n" : "";
|
|
@@ -9171,7 +9171,7 @@ var require_addPairToJSMap = __commonJS({
|
|
|
9171
9171
|
"use strict";
|
|
9172
9172
|
var log = require_log();
|
|
9173
9173
|
var merge = require_merge();
|
|
9174
|
-
var
|
|
9174
|
+
var stringify4 = require_stringify();
|
|
9175
9175
|
var identity = require_identity();
|
|
9176
9176
|
var toJS = require_toJS();
|
|
9177
9177
|
function addPairToJSMap(ctx, map, { key, value }) {
|
|
@@ -9207,7 +9207,7 @@ var require_addPairToJSMap = __commonJS({
|
|
|
9207
9207
|
if (typeof jsKey !== "object")
|
|
9208
9208
|
return String(jsKey);
|
|
9209
9209
|
if (identity.isNode(key) && ctx?.doc) {
|
|
9210
|
-
const strCtx =
|
|
9210
|
+
const strCtx = stringify4.createStringifyContext(ctx.doc, {});
|
|
9211
9211
|
strCtx.anchors = /* @__PURE__ */ new Set();
|
|
9212
9212
|
for (const node of ctx.anchors.keys())
|
|
9213
9213
|
strCtx.anchors.add(node.anchor);
|
|
@@ -9274,12 +9274,12 @@ var require_stringifyCollection = __commonJS({
|
|
|
9274
9274
|
"node_modules/yaml/dist/stringify/stringifyCollection.js"(exports) {
|
|
9275
9275
|
"use strict";
|
|
9276
9276
|
var identity = require_identity();
|
|
9277
|
-
var
|
|
9277
|
+
var stringify4 = require_stringify();
|
|
9278
9278
|
var stringifyComment = require_stringifyComment();
|
|
9279
9279
|
function stringifyCollection(collection, ctx, options) {
|
|
9280
9280
|
const flow = ctx.inFlow ?? collection.flow;
|
|
9281
|
-
const
|
|
9282
|
-
return
|
|
9281
|
+
const stringify5 = flow ? stringifyFlowCollection : stringifyBlockCollection;
|
|
9282
|
+
return stringify5(collection, ctx, options);
|
|
9283
9283
|
}
|
|
9284
9284
|
function stringifyBlockCollection({ comment, items }, ctx, { blockItemPrefix, flowChars, itemIndent, onChompKeep, onComment }) {
|
|
9285
9285
|
const { indent, options: { commentString } } = ctx;
|
|
@@ -9304,7 +9304,7 @@ var require_stringifyCollection = __commonJS({
|
|
|
9304
9304
|
}
|
|
9305
9305
|
}
|
|
9306
9306
|
chompKeep = false;
|
|
9307
|
-
let str2 =
|
|
9307
|
+
let str2 = stringify4.stringify(item, itemCtx, () => comment2 = null, () => chompKeep = true);
|
|
9308
9308
|
if (comment2)
|
|
9309
9309
|
str2 += stringifyComment.lineComment(str2, itemIndent, commentString(comment2));
|
|
9310
9310
|
if (chompKeep && comment2)
|
|
@@ -9371,7 +9371,7 @@ ${indent}${line}` : "\n";
|
|
|
9371
9371
|
}
|
|
9372
9372
|
if (comment)
|
|
9373
9373
|
reqNewline = true;
|
|
9374
|
-
let str =
|
|
9374
|
+
let str = stringify4.stringify(item, itemCtx, () => comment = null);
|
|
9375
9375
|
reqNewline || (reqNewline = lines.length > linesAtValue || str.includes("\n"));
|
|
9376
9376
|
if (i < items.length - 1) {
|
|
9377
9377
|
str += ",";
|
|
@@ -10732,7 +10732,7 @@ var require_stringifyDocument = __commonJS({
|
|
|
10732
10732
|
"node_modules/yaml/dist/stringify/stringifyDocument.js"(exports) {
|
|
10733
10733
|
"use strict";
|
|
10734
10734
|
var identity = require_identity();
|
|
10735
|
-
var
|
|
10735
|
+
var stringify4 = require_stringify();
|
|
10736
10736
|
var stringifyComment = require_stringifyComment();
|
|
10737
10737
|
function stringifyDocument(doc, options) {
|
|
10738
10738
|
const lines = [];
|
|
@@ -10747,7 +10747,7 @@ var require_stringifyDocument = __commonJS({
|
|
|
10747
10747
|
}
|
|
10748
10748
|
if (hasDirectives)
|
|
10749
10749
|
lines.push("---");
|
|
10750
|
-
const ctx =
|
|
10750
|
+
const ctx = stringify4.createStringifyContext(doc, options);
|
|
10751
10751
|
const { commentString } = ctx.options;
|
|
10752
10752
|
if (doc.commentBefore) {
|
|
10753
10753
|
if (lines.length !== 1)
|
|
@@ -10769,7 +10769,7 @@ var require_stringifyDocument = __commonJS({
|
|
|
10769
10769
|
contentComment = doc.contents.comment;
|
|
10770
10770
|
}
|
|
10771
10771
|
const onChompKeep = contentComment ? void 0 : () => chompKeep = true;
|
|
10772
|
-
let body =
|
|
10772
|
+
let body = stringify4.stringify(doc.contents, ctx, () => contentComment = null, onChompKeep);
|
|
10773
10773
|
if (contentComment)
|
|
10774
10774
|
body += stringifyComment.lineComment(body, "", commentString(contentComment));
|
|
10775
10775
|
if ((body[0] === "|" || body[0] === ">") && lines[lines.length - 1] === "---") {
|
|
@@ -10777,7 +10777,7 @@ var require_stringifyDocument = __commonJS({
|
|
|
10777
10777
|
} else
|
|
10778
10778
|
lines.push(body);
|
|
10779
10779
|
} else {
|
|
10780
|
-
lines.push(
|
|
10780
|
+
lines.push(stringify4.stringify(doc.contents, ctx));
|
|
10781
10781
|
}
|
|
10782
10782
|
if (doc.directives?.docEnd) {
|
|
10783
10783
|
if (doc.comment) {
|
|
@@ -12913,7 +12913,7 @@ var require_cst_scalar = __commonJS({
|
|
|
12913
12913
|
var require_cst_stringify = __commonJS({
|
|
12914
12914
|
"node_modules/yaml/dist/parse/cst-stringify.js"(exports) {
|
|
12915
12915
|
"use strict";
|
|
12916
|
-
var
|
|
12916
|
+
var stringify4 = (cst) => "type" in cst ? stringifyToken(cst) : stringifyItem(cst);
|
|
12917
12917
|
function stringifyToken(token) {
|
|
12918
12918
|
switch (token.type) {
|
|
12919
12919
|
case "block-scalar": {
|
|
@@ -12966,7 +12966,7 @@ var require_cst_stringify = __commonJS({
|
|
|
12966
12966
|
res += stringifyToken(value);
|
|
12967
12967
|
return res;
|
|
12968
12968
|
}
|
|
12969
|
-
exports.stringify =
|
|
12969
|
+
exports.stringify = stringify4;
|
|
12970
12970
|
}
|
|
12971
12971
|
});
|
|
12972
12972
|
|
|
@@ -14677,7 +14677,7 @@ var require_public_api = __commonJS({
|
|
|
14677
14677
|
}
|
|
14678
14678
|
return doc;
|
|
14679
14679
|
}
|
|
14680
|
-
function
|
|
14680
|
+
function parse8(src, reviver, options) {
|
|
14681
14681
|
let _reviver = void 0;
|
|
14682
14682
|
if (typeof reviver === "function") {
|
|
14683
14683
|
_reviver = reviver;
|
|
@@ -14696,7 +14696,7 @@ var require_public_api = __commonJS({
|
|
|
14696
14696
|
}
|
|
14697
14697
|
return doc.toJS(Object.assign({ reviver: _reviver }, options));
|
|
14698
14698
|
}
|
|
14699
|
-
function
|
|
14699
|
+
function stringify4(value, replacer, options) {
|
|
14700
14700
|
let _replacer = null;
|
|
14701
14701
|
if (typeof replacer === "function" || Array.isArray(replacer)) {
|
|
14702
14702
|
_replacer = replacer;
|
|
@@ -14718,10 +14718,10 @@ var require_public_api = __commonJS({
|
|
|
14718
14718
|
return value.toString(options);
|
|
14719
14719
|
return new Document.Document(value, _replacer, options).toString(options);
|
|
14720
14720
|
}
|
|
14721
|
-
exports.parse =
|
|
14721
|
+
exports.parse = parse8;
|
|
14722
14722
|
exports.parseAllDocuments = parseAllDocuments;
|
|
14723
14723
|
exports.parseDocument = parseDocument2;
|
|
14724
|
-
exports.stringify =
|
|
14724
|
+
exports.stringify = stringify4;
|
|
14725
14725
|
}
|
|
14726
14726
|
});
|
|
14727
14727
|
|
|
@@ -15165,7 +15165,7 @@ function defaultSpawn(command, args, options) {
|
|
|
15165
15165
|
if (options.signal?.aborted) {
|
|
15166
15166
|
return Promise.resolve({ stdout: "", stderr: "", code: null, started: false, observedChildExit: false, error: new Error("process_aborted") });
|
|
15167
15167
|
}
|
|
15168
|
-
return new Promise((
|
|
15168
|
+
return new Promise((resolve11) => {
|
|
15169
15169
|
const stdout = [];
|
|
15170
15170
|
const stderr = [];
|
|
15171
15171
|
let outputBytes = 0;
|
|
@@ -15196,7 +15196,7 @@ function defaultSpawn(command, args, options) {
|
|
|
15196
15196
|
const started = Boolean(child?.pid);
|
|
15197
15197
|
if (started && !observedChildExit) error ??= new Error("process_exit_unobserved");
|
|
15198
15198
|
const unverifiedDescendants = stopping || started && !observedChildExit;
|
|
15199
|
-
|
|
15199
|
+
resolve11({
|
|
15200
15200
|
stdout: Buffer.concat(stdout).toString("utf8"),
|
|
15201
15201
|
stderr: Buffer.concat(stderr).toString("utf8"),
|
|
15202
15202
|
code,
|
|
@@ -16991,6 +16991,19 @@ function validateAgentScope(agent, step, repositories, file, stepId, issues) {
|
|
|
16991
16991
|
}
|
|
16992
16992
|
}
|
|
16993
16993
|
}
|
|
16994
|
+
var GATE_STEP_OUTCOMES = {
|
|
16995
|
+
pass: ["passed", "implementation-failure"],
|
|
16996
|
+
fail: ["passed", "repro-missing"]
|
|
16997
|
+
};
|
|
16998
|
+
function gateOutcomeImpossible(step, stepId, file) {
|
|
16999
|
+
const expect = step.expect === "fail" ? "fail" : "pass";
|
|
17000
|
+
const produced = GATE_STEP_OUTCOMES[expect];
|
|
17001
|
+
const declared = Object.keys(objectValue(step.on) ?? {});
|
|
17002
|
+
const impossible = declared.filter((outcome) => !produced.includes(outcome) && outcome !== "implementation_failure" && outcome !== "repro_missing");
|
|
17003
|
+
const missing = produced.filter((outcome) => !declared.includes(outcome));
|
|
17004
|
+
if (impossible.length === 0 || missing.length === 0) return void 0;
|
|
17005
|
+
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 ")}`);
|
|
17006
|
+
}
|
|
16994
17007
|
function transition(value) {
|
|
16995
17008
|
if (typeof value === "string") return { target: value };
|
|
16996
17009
|
const object = objectValue(value);
|
|
@@ -17055,6 +17068,8 @@ function validateWorkflow(workflow, agents, models, repositories, gates, issues)
|
|
|
17055
17068
|
if (kind === "gate" && Object.keys(objectValue(step.on) ?? {}).some((outcome) => outcome === "implementation_failure" || outcome === "repro_missing")) {
|
|
17056
17069
|
issues.push(issue2("semantic", "gate_outcome_renamed", file, `${stepId} must use implementation-failure and repro-missing`));
|
|
17057
17070
|
}
|
|
17071
|
+
const impossibleOutcome = kind === "gate" ? gateOutcomeImpossible(step, stepId, file) : void 0;
|
|
17072
|
+
if (impossibleOutcome) issues.push(impossibleOutcome);
|
|
17058
17073
|
for (const repositoryId of Object.keys(objectValue(step.repositories) ?? {})) {
|
|
17059
17074
|
if (!repositories.has(repositoryId)) issues.push(issue2("reference", "repository_unknown", file, `${stepId} references unknown repository ${repositoryId}`));
|
|
17060
17075
|
}
|
|
@@ -17094,8 +17109,8 @@ function validateWorkflow(workflow, agents, models, repositories, gates, issues)
|
|
|
17094
17109
|
const writable = Object.values(objectValue(step.repositories) ?? {}).filter((access) => access === "write").length;
|
|
17095
17110
|
if (maxWriteRepositories > writable) issues.push(issue2("semantic", "write_repository_bound_invalid", file, `${stepId} maxWriteRepositories exceeds writable repository scope`));
|
|
17096
17111
|
}
|
|
17097
|
-
const
|
|
17098
|
-
const minimumPassed =
|
|
17112
|
+
const join23 = objectValue(step.join);
|
|
17113
|
+
const minimumPassed = join23 && typeof join23.minimumPassed === "number" ? join23.minimumPassed : void 0;
|
|
17099
17114
|
if (minimumPassed !== void 0 && minimumPassed > maximum) issues.push(issue2("semantic", "join_impossible", file, `${stepId} minimumPassed exceeds assignment maximum`));
|
|
17100
17115
|
const distinctBy = names(assignment?.distinctBy);
|
|
17101
17116
|
if (distinctBy.length > 0) {
|
|
@@ -21536,7 +21551,7 @@ function compileStep(step, index, stepIndex, requirePlanHash, sink) {
|
|
|
21536
21551
|
const maxAttempts = compileCountField(step.maxAttempts, 1, `${id}.maxAttempts`, id, sink);
|
|
21537
21552
|
const timeoutMs = compileOptionalDuration(step.timeoutMs, `${id}.timeoutMs`, id, sink);
|
|
21538
21553
|
const assignments = compileAssignments(step, id, agent, sink);
|
|
21539
|
-
const
|
|
21554
|
+
const join23 = compileJoin(step, id, sink);
|
|
21540
21555
|
const requiredEvidence = compileEvidence(step, id, sink);
|
|
21541
21556
|
const transitions = compileTransitions(step, id, index, stepIndex, sink);
|
|
21542
21557
|
const outcomes = Object.keys(transitions).sort(compareCodeUnits4);
|
|
@@ -21564,7 +21579,7 @@ function compileStep(step, index, stepIndex, requirePlanHash, sink) {
|
|
|
21564
21579
|
transitions: orderedTransitions,
|
|
21565
21580
|
requiresPlanHash: requirePlanHash.includes(id),
|
|
21566
21581
|
assignments,
|
|
21567
|
-
join:
|
|
21582
|
+
join: join23
|
|
21568
21583
|
};
|
|
21569
21584
|
if (kind === "agent" || kind === "moa") {
|
|
21570
21585
|
if (!agent) return void 0;
|
|
@@ -21614,15 +21629,15 @@ function compileAssignments(step, stepId, primaryAgentId, sink) {
|
|
|
21614
21629
|
};
|
|
21615
21630
|
}
|
|
21616
21631
|
function compileJoin(step, stepId, sink) {
|
|
21617
|
-
const
|
|
21618
|
-
if (!
|
|
21619
|
-
const declared = stringValue2(
|
|
21632
|
+
const join23 = objectValue2(step.join);
|
|
21633
|
+
if (!join23) return { strategy: "all" };
|
|
21634
|
+
const declared = stringValue2(join23.strategy);
|
|
21620
21635
|
const strategy = declared && JOIN_STRATEGIES.has(declared) ? declared : "all";
|
|
21621
|
-
const minimumPassed = compileOptionalCount(
|
|
21636
|
+
const minimumPassed = compileOptionalCount(join23.minimumPassed, `${stepId}.join.minimumPassed`, stepId, sink);
|
|
21622
21637
|
const compiled = {
|
|
21623
21638
|
strategy,
|
|
21624
21639
|
...minimumPassed !== void 0 ? { minimumPassed } : {},
|
|
21625
|
-
...typeof
|
|
21640
|
+
...typeof join23.cancelRemaining === "boolean" ? { cancelRemaining: join23.cancelRemaining } : {}
|
|
21626
21641
|
};
|
|
21627
21642
|
return compiled;
|
|
21628
21643
|
}
|
|
@@ -21935,6 +21950,28 @@ var EVIDENCE_OPTIONAL = ["producerPolicy"];
|
|
|
21935
21950
|
function kxmSha256(input) {
|
|
21936
21951
|
return `sha256:${createHash6("sha256").update(input, "utf8").digest("hex")}`;
|
|
21937
21952
|
}
|
|
21953
|
+
function kxmStepAskSha256(plan, stepId, agentId) {
|
|
21954
|
+
const step = Object.hasOwn(plan.steps, stepId) ? plan.steps[stepId] : void 0;
|
|
21955
|
+
if (!step) throw runtimeError("run_plan_corrupt", plan.workflowId, `compiled plan is missing step ${stepId}`);
|
|
21956
|
+
return kxmSha256(kxmCanonicalJson({
|
|
21957
|
+
v: 1,
|
|
21958
|
+
workflowId: plan.workflowId,
|
|
21959
|
+
stepId,
|
|
21960
|
+
kind: step.kind,
|
|
21961
|
+
agentId,
|
|
21962
|
+
instructions: step.instructions ?? null,
|
|
21963
|
+
outcomes: [...step.outcomes],
|
|
21964
|
+
requiredEvidence: step.requiredEvidence.map((entry) => entry.key)
|
|
21965
|
+
}));
|
|
21966
|
+
}
|
|
21967
|
+
function kxmAttemptFinalOutcome(step, settled) {
|
|
21968
|
+
if (settled.resultClass !== "outcome") return "failed";
|
|
21969
|
+
const outcome = settled.outcome;
|
|
21970
|
+
const transition2 = outcome !== void 0 && Object.hasOwn(step.transitions, outcome) ? step.transitions[outcome] : void 0;
|
|
21971
|
+
if (!transition2) return "failed";
|
|
21972
|
+
if (transition2.to === "terminal") return transition2.terminalStatus === "completed" ? void 0 : "failed";
|
|
21973
|
+
return transition2.edge === "back" ? "blocked" : void 0;
|
|
21974
|
+
}
|
|
21938
21975
|
function hashKxmRunPlanEnvelope(envelope2) {
|
|
21939
21976
|
return kxmSha256(kxmCanonicalJson(envelope2));
|
|
21940
21977
|
}
|
|
@@ -22946,12 +22983,12 @@ function enqueueKxmScheduledRun(storePath, runId, configRevision, bound, start)
|
|
|
22946
22983
|
if (owner.admitted.has(runId) || owner.queue.some((item) => item.runId === runId)) {
|
|
22947
22984
|
return Promise.reject(runtimeError("run_busy", runId, `run ${runId} is already admitted or queued`));
|
|
22948
22985
|
}
|
|
22949
|
-
return new Promise((
|
|
22986
|
+
return new Promise((resolve11, reject) => {
|
|
22950
22987
|
owner.queue.push({
|
|
22951
22988
|
runId,
|
|
22952
22989
|
configRevision,
|
|
22953
22990
|
bound,
|
|
22954
|
-
start: (token) => start(token).then(
|
|
22991
|
+
start: (token) => start(token).then(resolve11, reject),
|
|
22955
22992
|
fail: (error) => reject(error)
|
|
22956
22993
|
});
|
|
22957
22994
|
pump(storePath, owner);
|
|
@@ -23130,7 +23167,8 @@ function openKxmRuntimeContext(projectRoot, options) {
|
|
|
23130
23167
|
homeRuntimeId: registration.homeRuntimeId,
|
|
23131
23168
|
eventStore,
|
|
23132
23169
|
configRevision: bundle.configRevision,
|
|
23133
|
-
...options.budgetClock ? { budgetClock: options.budgetClock } : {}
|
|
23170
|
+
...options.budgetClock ? { budgetClock: options.budgetClock } : {},
|
|
23171
|
+
...options.logger ? { logger: options.logger } : {}
|
|
23134
23172
|
};
|
|
23135
23173
|
} catch (error) {
|
|
23136
23174
|
registry.close();
|
|
@@ -23469,13 +23507,13 @@ function assertKxmConfigError(error) {
|
|
|
23469
23507
|
|
|
23470
23508
|
// plugins/kxm/src/runtime-supervisor.ts
|
|
23471
23509
|
import { spawn as spawn3 } from "node:child_process";
|
|
23472
|
-
import { createHash as
|
|
23473
|
-
import { chmodSync as chmodSync2, existsSync as
|
|
23510
|
+
import { createHash as createHash14, createHmac, randomBytes as randomBytes3, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
23511
|
+
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";
|
|
23474
23512
|
import { createServer } from "node:http";
|
|
23475
|
-
import { dirname as
|
|
23513
|
+
import { dirname as dirname11, isAbsolute as isAbsolute6, join as join18 } from "node:path";
|
|
23476
23514
|
|
|
23477
23515
|
// plugins/kxm/src/oneshot-producer.ts
|
|
23478
|
-
import { join as
|
|
23516
|
+
import { join as join15 } from "node:path";
|
|
23479
23517
|
|
|
23480
23518
|
// plugins/kxm/src/oneshot-evidence.ts
|
|
23481
23519
|
import { createHash as createHash8, randomUUID as randomUUID3 } from "node:crypto";
|
|
@@ -23771,10 +23809,10 @@ function loadPriceCatalogForEstimate(options) {
|
|
|
23771
23809
|
}
|
|
23772
23810
|
|
|
23773
23811
|
// plugins/kxm/src/engine.ts
|
|
23774
|
-
var
|
|
23775
|
-
import { createHash as
|
|
23776
|
-
import { existsSync as
|
|
23777
|
-
import { join as
|
|
23812
|
+
var import_yaml7 = __toESM(require_dist(), 1);
|
|
23813
|
+
import { createHash as createHash13, randomBytes as randomBytes2, timingSafeEqual } from "node:crypto";
|
|
23814
|
+
import { existsSync as existsSync11, readFileSync as readFileSync9 } from "node:fs";
|
|
23815
|
+
import { join as join14 } from "node:path";
|
|
23778
23816
|
|
|
23779
23817
|
// plugins/kxm/src/routes.ts
|
|
23780
23818
|
var import_yaml4 = __toESM(require_dist(), 1);
|
|
@@ -23864,7 +23902,7 @@ function buildFormalContextPacket(input) {
|
|
|
23864
23902
|
budget: {
|
|
23865
23903
|
allocatedTokens,
|
|
23866
23904
|
estimatedTokens,
|
|
23867
|
-
unresolvedGaps: [],
|
|
23905
|
+
unresolvedGaps: [...input.unresolvedGaps ?? []],
|
|
23868
23906
|
provenanceSummary
|
|
23869
23907
|
}
|
|
23870
23908
|
};
|
|
@@ -23913,7 +23951,7 @@ function formatContextPacketForPrompt(packet) {
|
|
|
23913
23951
|
}
|
|
23914
23952
|
sections.push("");
|
|
23915
23953
|
}
|
|
23916
|
-
if (packet.environment.projectKnowledge.length > 0 || packet.environment.sharedDefaults.length > 0) {
|
|
23954
|
+
if (packet.environment.projectKnowledge.length > 0 || packet.environment.sharedDefaults.length > 0 || packet.environment.activeSkills.length > 0 || packet.environment.contradictions.length > 0) {
|
|
23917
23955
|
sections.push(`## 5. Environment & Memory`);
|
|
23918
23956
|
if (packet.environment.sharedDefaults.length > 0) {
|
|
23919
23957
|
sections.push(`### Shared Defaults`);
|
|
@@ -23927,6 +23965,12 @@ function formatContextPacketForPrompt(packet) {
|
|
|
23927
23965
|
sections.push(`- [${k.authority}] ${k.summary}`);
|
|
23928
23966
|
}
|
|
23929
23967
|
}
|
|
23968
|
+
if (packet.environment.activeSkills.length > 0) {
|
|
23969
|
+
sections.push(`### Active Skills`);
|
|
23970
|
+
for (const skill of packet.environment.activeSkills) {
|
|
23971
|
+
sections.push(`- ${skill.summary} (${skill.provenance.sourceRef ?? skill.id})`);
|
|
23972
|
+
}
|
|
23973
|
+
}
|
|
23930
23974
|
if (packet.environment.contradictions.length > 0) {
|
|
23931
23975
|
sections.push(`### Active Contradictions`);
|
|
23932
23976
|
for (const c of packet.environment.contradictions) {
|
|
@@ -24066,19 +24110,1362 @@ function pruneContextPacket(packet, tokenBudget) {
|
|
|
24066
24110
|
};
|
|
24067
24111
|
}
|
|
24068
24112
|
|
|
24113
|
+
// plugins/kxm/src/dispatch-context.ts
|
|
24114
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
24115
|
+
import { readdirSync as readdirSync7 } from "node:fs";
|
|
24116
|
+
import { extname as extname3, join as join12 } from "node:path";
|
|
24117
|
+
|
|
24118
|
+
// plugins/kxm/src/protocol.ts
|
|
24119
|
+
var DEFAULT_MESSAGE_TTL_MS = 24 * 60 * 6e4;
|
|
24120
|
+
var MAX_MESSAGE_TTL_MS = 7 * 24 * 60 * 6e4;
|
|
24121
|
+
var DEFAULT_MESSAGE_RETENTION_MS = 7 * 24 * 60 * 6e4;
|
|
24122
|
+
var MAX_BODY_BYTES = 256 * 1024;
|
|
24123
|
+
var MAX_AGENT_HOST_CHARS = 64;
|
|
24124
|
+
var MAX_LEASE_TTL_MS = 10 * 6e4;
|
|
24125
|
+
var DEFAULT_LEASE_TTL_MS = 5 * 6e4;
|
|
24126
|
+
var ProtocolError = class extends Error {
|
|
24127
|
+
statusCode;
|
|
24128
|
+
code;
|
|
24129
|
+
extras;
|
|
24130
|
+
constructor(statusCode, message, code = "protocol_error", extras) {
|
|
24131
|
+
super(message);
|
|
24132
|
+
this.name = "ProtocolError";
|
|
24133
|
+
this.statusCode = statusCode;
|
|
24134
|
+
this.code = code;
|
|
24135
|
+
if (extras) this.extras = extras;
|
|
24136
|
+
}
|
|
24137
|
+
};
|
|
24138
|
+
function nowIso() {
|
|
24139
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
24140
|
+
}
|
|
24141
|
+
function requireString(value, field, options = {}) {
|
|
24142
|
+
if (typeof value !== "string") {
|
|
24143
|
+
throw new ProtocolError(400, `${field} must be a string`);
|
|
24144
|
+
}
|
|
24145
|
+
const result = value.trim();
|
|
24146
|
+
if (!options.allowEmpty && result.length === 0) {
|
|
24147
|
+
throw new ProtocolError(400, `${field} cannot be empty`);
|
|
24148
|
+
}
|
|
24149
|
+
if (options.max !== void 0 && result.length > options.max) {
|
|
24150
|
+
throw new ProtocolError(400, `${field} exceeds ${options.max} characters`);
|
|
24151
|
+
}
|
|
24152
|
+
return result;
|
|
24153
|
+
}
|
|
24154
|
+
|
|
24155
|
+
// plugins/kxm/src/context.ts
|
|
24156
|
+
var MAX_CONTEXT_SUMMARY_CHARS = 4e3;
|
|
24157
|
+
var MAX_CONTEXT_ID_REFS = 64;
|
|
24158
|
+
var MAX_CONTEXT_ITEMS = 256;
|
|
24159
|
+
var MIN_CONTEXT_BUDGET_TOKENS = 512;
|
|
24160
|
+
var MAX_CONTEXT_BUDGET_TOKENS = 2e5;
|
|
24161
|
+
var DEFAULT_CONTEXT_BUDGET_TOKENS = 32e3;
|
|
24162
|
+
var MAX_CONTEXT_ROLE_CHARS = 64;
|
|
24163
|
+
var MAX_CONTEXT_TASK_CHARS = 2e3;
|
|
24164
|
+
var AUTHORITY_GRANT_FLOOR = {
|
|
24165
|
+
human: "policy",
|
|
24166
|
+
workflow: "policy",
|
|
24167
|
+
git: "instruction",
|
|
24168
|
+
peer: "evidence",
|
|
24169
|
+
tool: "evidence",
|
|
24170
|
+
external: "evidence",
|
|
24171
|
+
derived: "evidence"
|
|
24172
|
+
};
|
|
24173
|
+
function authorityGrantFloor(sourceType) {
|
|
24174
|
+
return AUTHORITY_GRANT_FLOOR[sourceType];
|
|
24175
|
+
}
|
|
24176
|
+
var RESERVED_CONTROL_PLANE_FIELDS = /* @__PURE__ */ new Set([
|
|
24177
|
+
"permissions",
|
|
24178
|
+
"tools",
|
|
24179
|
+
"allow",
|
|
24180
|
+
"deny",
|
|
24181
|
+
"grants",
|
|
24182
|
+
"approval",
|
|
24183
|
+
"policy",
|
|
24184
|
+
"scopes",
|
|
24185
|
+
"credentials",
|
|
24186
|
+
"secrets",
|
|
24187
|
+
"token",
|
|
24188
|
+
"apiKey",
|
|
24189
|
+
"password"
|
|
24190
|
+
]);
|
|
24191
|
+
var CONTEXT_ITEM_KINDS = ["evidence", "state", "episode", "knowledge", "skill"];
|
|
24192
|
+
var CONTEXT_SOURCE_TYPES = [
|
|
24193
|
+
"human",
|
|
24194
|
+
"git",
|
|
24195
|
+
"workflow",
|
|
24196
|
+
"tool",
|
|
24197
|
+
"peer",
|
|
24198
|
+
"external",
|
|
24199
|
+
"derived"
|
|
24200
|
+
];
|
|
24201
|
+
var CONTEXT_AUTHORITIES = ["policy", "instruction", "evidence", "hypothesis"];
|
|
24202
|
+
var CONTEXT_CONFIDENCES = ["verified", "probable", "uncertain"];
|
|
24203
|
+
var CONTEXT_SCOPES = ["agent", "project", "run", "operator"];
|
|
24204
|
+
var CONTEXT_ROLES = ["repro", "planner", "critic", "implementer", "verifier"];
|
|
24205
|
+
function oneOf(value, field, allowed) {
|
|
24206
|
+
if (typeof value !== "string" || !allowed.includes(value)) {
|
|
24207
|
+
throw new ProtocolError(400, `${field} must be one of ${allowed.join(", ")}`, "invalid_context_field");
|
|
24208
|
+
}
|
|
24209
|
+
return value;
|
|
24210
|
+
}
|
|
24211
|
+
function idRefs(value, field, required = false) {
|
|
24212
|
+
if (value === void 0 || value === null) return required ? [] : void 0;
|
|
24213
|
+
if (!Array.isArray(value)) {
|
|
24214
|
+
throw new ProtocolError(400, `${field} must be an array of identifiers`, "invalid_context_field");
|
|
24215
|
+
}
|
|
24216
|
+
if (value.length > MAX_CONTEXT_ID_REFS) {
|
|
24217
|
+
throw new ProtocolError(
|
|
24218
|
+
400,
|
|
24219
|
+
`${field} exceeds ${MAX_CONTEXT_ID_REFS} references`,
|
|
24220
|
+
"context_limits_exceeded"
|
|
24221
|
+
);
|
|
24222
|
+
}
|
|
24223
|
+
const seen = /* @__PURE__ */ new Set();
|
|
24224
|
+
const refs = [];
|
|
24225
|
+
for (const candidate of value) {
|
|
24226
|
+
if (typeof candidate !== "string" || !candidate.trim()) {
|
|
24227
|
+
throw new ProtocolError(400, `${field} must contain non-empty identifiers`, "invalid_context_field");
|
|
24228
|
+
}
|
|
24229
|
+
const ref = candidate.trim();
|
|
24230
|
+
if (seen.has(ref)) {
|
|
24231
|
+
throw new ProtocolError(400, `${field} contains duplicate reference ${ref}`, "invalid_context_field");
|
|
24232
|
+
}
|
|
24233
|
+
seen.add(ref);
|
|
24234
|
+
refs.push(ref);
|
|
24235
|
+
}
|
|
24236
|
+
return refs;
|
|
24237
|
+
}
|
|
24238
|
+
function optionalIsoTimestamp(value, field) {
|
|
24239
|
+
if (value === void 0 || value === null) return void 0;
|
|
24240
|
+
if (typeof value !== "string" || !Number.isFinite(Date.parse(value))) {
|
|
24241
|
+
throw new ProtocolError(400, `${field} must be an ISO-8601 timestamp`, "invalid_context_field");
|
|
24242
|
+
}
|
|
24243
|
+
return value;
|
|
24244
|
+
}
|
|
24245
|
+
function parseContextItem(value) {
|
|
24246
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
24247
|
+
throw new ProtocolError(400, "context item must be an object", "invalid_context_item");
|
|
24248
|
+
}
|
|
24249
|
+
const input = value;
|
|
24250
|
+
for (const field of Object.keys(input)) {
|
|
24251
|
+
if (RESERVED_CONTROL_PLANE_FIELDS.has(field)) {
|
|
24252
|
+
throw new ProtocolError(
|
|
24253
|
+
403,
|
|
24254
|
+
`context items may not carry control-plane field "${field}"`,
|
|
24255
|
+
"context_authority_violation"
|
|
24256
|
+
);
|
|
24257
|
+
}
|
|
24258
|
+
}
|
|
24259
|
+
const item = {
|
|
24260
|
+
id: requireString(input.id, "context item id", { max: 128 }),
|
|
24261
|
+
kind: oneOf(input.kind, "context item kind", CONTEXT_ITEM_KINDS),
|
|
24262
|
+
project: requireString(input.project, "context item project", { max: 200 }),
|
|
24263
|
+
summary: redactSecrets(requireString(input.summary, "context item summary", { max: MAX_CONTEXT_SUMMARY_CHARS })),
|
|
24264
|
+
provenance: parseContextProvenance(input.provenance),
|
|
24265
|
+
authority: oneOf(input.authority, "context item authority", CONTEXT_AUTHORITIES),
|
|
24266
|
+
confidence: oneOf(input.confidence, "context item confidence", CONTEXT_CONFIDENCES)
|
|
24267
|
+
};
|
|
24268
|
+
if (input.scope !== void 0 && input.scope !== null) {
|
|
24269
|
+
item.scope = oneOf(input.scope, "context item scope", CONTEXT_SCOPES);
|
|
24270
|
+
}
|
|
24271
|
+
const observedAt = optionalIsoTimestamp(input.observedAt, "context item observedAt");
|
|
24272
|
+
const validFrom = optionalIsoTimestamp(input.validFrom, "context item validFrom");
|
|
24273
|
+
const validUntil = optionalIsoTimestamp(input.validUntil, "context item validUntil");
|
|
24274
|
+
if (observedAt !== void 0) item.observedAt = observedAt;
|
|
24275
|
+
if (validFrom !== void 0) item.validFrom = validFrom;
|
|
24276
|
+
if (validUntil !== void 0) item.validUntil = validUntil;
|
|
24277
|
+
if (input.status !== void 0 && input.status !== null) {
|
|
24278
|
+
item.status = oneOf(input.status, "context item status", ["current", "superseded", "proposed", "rejected"]);
|
|
24279
|
+
}
|
|
24280
|
+
const supersedes = idRefs(input.supersedes, "context item supersedes");
|
|
24281
|
+
if (supersedes !== void 0) {
|
|
24282
|
+
if (supersedes.includes(item.id)) {
|
|
24283
|
+
throw new ProtocolError(400, "context item cannot supersede itself", "invalid_context_item");
|
|
24284
|
+
}
|
|
24285
|
+
item.supersedes = supersedes;
|
|
24286
|
+
}
|
|
24287
|
+
const evidenceRefs = idRefs(input.evidenceRefs, "context item evidenceRefs");
|
|
24288
|
+
if (evidenceRefs !== void 0) item.evidenceRefs = evidenceRefs;
|
|
24289
|
+
const stateKey = input.stateKey === void 0 || input.stateKey === null ? void 0 : requireString(input.stateKey, "context item stateKey", { max: 200 });
|
|
24290
|
+
if (stateKey !== void 0) item.stateKey = stateKey;
|
|
24291
|
+
if (item.kind === "state" && item.stateKey === void 0) {
|
|
24292
|
+
throw new ProtocolError(400, "state items require a stateKey", "invalid_context_item");
|
|
24293
|
+
}
|
|
24294
|
+
if (item.kind === "state" && item.status === void 0) {
|
|
24295
|
+
throw new ProtocolError(400, "state items require an explicit lifecycle status", "invalid_context_item");
|
|
24296
|
+
}
|
|
24297
|
+
const grantFloor = authorityRank(authorityGrantFloor(item.provenance.sourceType));
|
|
24298
|
+
if (authorityRank(item.authority) > grantFloor) {
|
|
24299
|
+
throw new ProtocolError(
|
|
24300
|
+
403,
|
|
24301
|
+
`content of origin ${item.provenance.sourceType} cannot claim ${item.authority} authority`,
|
|
24302
|
+
"context_authority_violation"
|
|
24303
|
+
);
|
|
24304
|
+
}
|
|
24305
|
+
return item;
|
|
24306
|
+
}
|
|
24307
|
+
function parseContextProvenance(value) {
|
|
24308
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
24309
|
+
throw new ProtocolError(400, "context provenance must be an object", "invalid_context_item");
|
|
24310
|
+
}
|
|
24311
|
+
const input = value;
|
|
24312
|
+
const provenance = {
|
|
24313
|
+
sourceType: oneOf(input.sourceType, "context provenance sourceType", CONTEXT_SOURCE_TYPES)
|
|
24314
|
+
};
|
|
24315
|
+
const sourceRef = input.sourceRef === void 0 || input.sourceRef === null ? void 0 : requireString(input.sourceRef, "context provenance sourceRef", { max: 512 });
|
|
24316
|
+
if (sourceRef !== void 0) provenance.sourceRef = redactSecrets(sourceRef);
|
|
24317
|
+
const derivedFrom = idRefs(input.derivedFrom, "context provenance derivedFrom");
|
|
24318
|
+
if (derivedFrom !== void 0) provenance.derivedFrom = derivedFrom;
|
|
24319
|
+
return provenance;
|
|
24320
|
+
}
|
|
24321
|
+
function parseContextRequest(value) {
|
|
24322
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
24323
|
+
throw new ProtocolError(400, "context request must be an object", "invalid_context_request");
|
|
24324
|
+
}
|
|
24325
|
+
const input = value;
|
|
24326
|
+
const request = {
|
|
24327
|
+
project: requireString(input.project, "context request project", { max: 200 }),
|
|
24328
|
+
role: requireString(input.role, "context request role", { max: MAX_CONTEXT_ROLE_CHARS }),
|
|
24329
|
+
task: requireString(input.task, "context request task", { max: MAX_CONTEXT_TASK_CHARS })
|
|
24330
|
+
};
|
|
24331
|
+
const workflowRunId = input.workflowRunId === void 0 || input.workflowRunId === null ? void 0 : requireString(input.workflowRunId, "context request workflowRunId", { max: 128 });
|
|
24332
|
+
if (workflowRunId !== void 0) request.workflowRunId = workflowRunId;
|
|
24333
|
+
const stageId = input.stageId === void 0 || input.stageId === null ? void 0 : requireString(input.stageId, "context request stageId", { max: 128 });
|
|
24334
|
+
if (stageId !== void 0) request.stageId = stageId;
|
|
24335
|
+
if (input.budgetTokens !== void 0 && input.budgetTokens !== null) {
|
|
24336
|
+
const budget = input.budgetTokens;
|
|
24337
|
+
if (!Number.isInteger(budget) || budget < MIN_CONTEXT_BUDGET_TOKENS || budget > MAX_CONTEXT_BUDGET_TOKENS) {
|
|
24338
|
+
throw new ProtocolError(
|
|
24339
|
+
400,
|
|
24340
|
+
`context request budgetTokens must be an integer between ${MIN_CONTEXT_BUDGET_TOKENS} and ${MAX_CONTEXT_BUDGET_TOKENS}`,
|
|
24341
|
+
"invalid_context_request"
|
|
24342
|
+
);
|
|
24343
|
+
}
|
|
24344
|
+
request.budgetTokens = budget;
|
|
24345
|
+
}
|
|
24346
|
+
if (input.includeKinds !== void 0 && input.includeKinds !== null) {
|
|
24347
|
+
if (!Array.isArray(input.includeKinds) || input.includeKinds.length < 1 || input.includeKinds.length > CONTEXT_ITEM_KINDS.length) {
|
|
24348
|
+
throw new ProtocolError(
|
|
24349
|
+
400,
|
|
24350
|
+
"context request includeKinds must be a non-empty array of item kinds",
|
|
24351
|
+
"invalid_context_request"
|
|
24352
|
+
);
|
|
24353
|
+
}
|
|
24354
|
+
request.includeKinds = input.includeKinds.map((kind) => oneOf(kind, "context request includeKinds", CONTEXT_ITEM_KINDS));
|
|
24355
|
+
}
|
|
24356
|
+
return request;
|
|
24357
|
+
}
|
|
24358
|
+
function validateContextPacketContents(request, packet) {
|
|
24359
|
+
const items = [
|
|
24360
|
+
...packet.currentState,
|
|
24361
|
+
...packet.knowledge,
|
|
24362
|
+
...packet.evidence,
|
|
24363
|
+
...packet.episodes,
|
|
24364
|
+
...packet.skills,
|
|
24365
|
+
...packet.contradictions
|
|
24366
|
+
];
|
|
24367
|
+
if (items.length > MAX_CONTEXT_ITEMS) {
|
|
24368
|
+
throw new ProtocolError(400, `context packet exceeds ${MAX_CONTEXT_ITEMS} items`, "context_limits_exceeded");
|
|
24369
|
+
}
|
|
24370
|
+
const allowed = request.includeKinds ? new Set(request.includeKinds) : void 0;
|
|
24371
|
+
for (const item of items) {
|
|
24372
|
+
if (item.project !== request.project && item.project !== "_shared") {
|
|
24373
|
+
throw new ProtocolError(
|
|
24374
|
+
400,
|
|
24375
|
+
`context packet contains cross-project item ${item.id}`,
|
|
24376
|
+
"context_isolation_violation"
|
|
24377
|
+
);
|
|
24378
|
+
}
|
|
24379
|
+
if (allowed && !allowed.has(item.kind)) {
|
|
24380
|
+
throw new ProtocolError(
|
|
24381
|
+
400,
|
|
24382
|
+
`context packet contains item ${item.id} of unrequested kind ${item.kind}`,
|
|
24383
|
+
"context_isolation_violation"
|
|
24384
|
+
);
|
|
24385
|
+
}
|
|
24386
|
+
}
|
|
24387
|
+
}
|
|
24388
|
+
function contextItemCharacters(item) {
|
|
24389
|
+
return item.summary.length + item.id.length + item.kind.length + (item.provenance.sourceRef?.length ?? 0);
|
|
24390
|
+
}
|
|
24391
|
+
function estimateContextTokens(items) {
|
|
24392
|
+
let characters = 0;
|
|
24393
|
+
for (const item of items) characters += contextItemCharacters(item);
|
|
24394
|
+
return Math.ceil(characters / 4);
|
|
24395
|
+
}
|
|
24396
|
+
function authorityRank(authority) {
|
|
24397
|
+
switch (authority) {
|
|
24398
|
+
case "policy":
|
|
24399
|
+
return 3;
|
|
24400
|
+
case "instruction":
|
|
24401
|
+
return 2;
|
|
24402
|
+
case "evidence":
|
|
24403
|
+
return 1;
|
|
24404
|
+
case "hypothesis":
|
|
24405
|
+
return 0;
|
|
24406
|
+
}
|
|
24407
|
+
}
|
|
24408
|
+
function provenanceSummaryOf(items) {
|
|
24409
|
+
const summary = {};
|
|
24410
|
+
for (const item of items) {
|
|
24411
|
+
summary[item.provenance.sourceType] = (summary[item.provenance.sourceType] ?? 0) + 1;
|
|
24412
|
+
}
|
|
24413
|
+
for (const key of Object.keys(summary).sort()) {
|
|
24414
|
+
if (summary[key] === 0) delete summary[key];
|
|
24415
|
+
}
|
|
24416
|
+
return summary;
|
|
24417
|
+
}
|
|
24418
|
+
|
|
24419
|
+
// plugins/kxm/src/relevance.ts
|
|
24420
|
+
var RELEVANCE_STOPWORDS = Object.freeze(/* @__PURE__ */ new Set([
|
|
24421
|
+
"a",
|
|
24422
|
+
"an",
|
|
24423
|
+
"and",
|
|
24424
|
+
"are",
|
|
24425
|
+
"as",
|
|
24426
|
+
"at",
|
|
24427
|
+
"be",
|
|
24428
|
+
"been",
|
|
24429
|
+
"but",
|
|
24430
|
+
"by",
|
|
24431
|
+
"can",
|
|
24432
|
+
"could",
|
|
24433
|
+
"did",
|
|
24434
|
+
"do",
|
|
24435
|
+
"does",
|
|
24436
|
+
"for",
|
|
24437
|
+
"from",
|
|
24438
|
+
"had",
|
|
24439
|
+
"has",
|
|
24440
|
+
"have",
|
|
24441
|
+
"how",
|
|
24442
|
+
"if",
|
|
24443
|
+
"in",
|
|
24444
|
+
"into",
|
|
24445
|
+
"is",
|
|
24446
|
+
"it",
|
|
24447
|
+
"its",
|
|
24448
|
+
"of",
|
|
24449
|
+
"on",
|
|
24450
|
+
"or",
|
|
24451
|
+
"our",
|
|
24452
|
+
"should",
|
|
24453
|
+
"so",
|
|
24454
|
+
"than",
|
|
24455
|
+
"that",
|
|
24456
|
+
"the",
|
|
24457
|
+
"their",
|
|
24458
|
+
"them",
|
|
24459
|
+
"then",
|
|
24460
|
+
"there",
|
|
24461
|
+
"these",
|
|
24462
|
+
"they",
|
|
24463
|
+
"this",
|
|
24464
|
+
"those",
|
|
24465
|
+
"to",
|
|
24466
|
+
"was",
|
|
24467
|
+
"we",
|
|
24468
|
+
"were",
|
|
24469
|
+
"what",
|
|
24470
|
+
"when",
|
|
24471
|
+
"where",
|
|
24472
|
+
"which",
|
|
24473
|
+
"while",
|
|
24474
|
+
"who",
|
|
24475
|
+
"why",
|
|
24476
|
+
"will",
|
|
24477
|
+
"with",
|
|
24478
|
+
"would",
|
|
24479
|
+
"you",
|
|
24480
|
+
"your"
|
|
24481
|
+
]));
|
|
24482
|
+
var RELEVANCE_K1 = 1.2;
|
|
24483
|
+
var RELEVANCE_B = 0.75;
|
|
24484
|
+
var MIN_TOKEN_CHARS = 2;
|
|
24485
|
+
var MAX_TOKEN_CHARS = 64;
|
|
24486
|
+
function foldPlural(token) {
|
|
24487
|
+
if (new RegExp("^\\p{N}+$", "u").test(token)) return token;
|
|
24488
|
+
if (token.length > 4 && token.endsWith("ies")) return `${token.slice(0, -3)}y`;
|
|
24489
|
+
if (token.length > 4 && token.endsWith("sses")) return token.slice(0, -2);
|
|
24490
|
+
if (token.length > 3 && token.endsWith("s") && !token.endsWith("ss") && !token.endsWith("us") && !token.endsWith("is")) {
|
|
24491
|
+
return token.slice(0, -1);
|
|
24492
|
+
}
|
|
24493
|
+
return token;
|
|
24494
|
+
}
|
|
24495
|
+
function relevanceTokens(text) {
|
|
24496
|
+
const tokens = [];
|
|
24497
|
+
for (const raw of text.normalize("NFKC").toLowerCase().split(/[^\p{L}\p{N}]+/u)) {
|
|
24498
|
+
if (raw.length < MIN_TOKEN_CHARS || raw.length > MAX_TOKEN_CHARS) continue;
|
|
24499
|
+
if (RELEVANCE_STOPWORDS.has(raw)) continue;
|
|
24500
|
+
tokens.push(foldPlural(raw));
|
|
24501
|
+
}
|
|
24502
|
+
return tokens;
|
|
24503
|
+
}
|
|
24504
|
+
function scoreRelevance(query, documents) {
|
|
24505
|
+
const scores = documents.map(() => 0);
|
|
24506
|
+
const terms = [...new Set(relevanceTokens(query))];
|
|
24507
|
+
if (terms.length === 0 || documents.length === 0) return scores;
|
|
24508
|
+
const indexed = documents.map((document) => {
|
|
24509
|
+
const tokens = relevanceTokens(document);
|
|
24510
|
+
const frequencies = /* @__PURE__ */ new Map();
|
|
24511
|
+
for (const token of tokens) frequencies.set(token, (frequencies.get(token) ?? 0) + 1);
|
|
24512
|
+
return { length: tokens.length, frequencies };
|
|
24513
|
+
});
|
|
24514
|
+
const count = indexed.length;
|
|
24515
|
+
let totalLength = 0;
|
|
24516
|
+
for (const document of indexed) totalLength += document.length;
|
|
24517
|
+
const averageLength = totalLength > 0 ? totalLength / count : 1;
|
|
24518
|
+
const inverseFrequency = /* @__PURE__ */ new Map();
|
|
24519
|
+
for (const term of terms) {
|
|
24520
|
+
let documentFrequency = 0;
|
|
24521
|
+
for (const document of indexed) {
|
|
24522
|
+
if (document.frequencies.has(term)) documentFrequency += 1;
|
|
24523
|
+
}
|
|
24524
|
+
inverseFrequency.set(term, Math.log(1 + (count - documentFrequency + 0.5) / (documentFrequency + 0.5)));
|
|
24525
|
+
}
|
|
24526
|
+
indexed.forEach((document, index) => {
|
|
24527
|
+
let score = 0;
|
|
24528
|
+
for (const term of terms) {
|
|
24529
|
+
const frequency = document.frequencies.get(term) ?? 0;
|
|
24530
|
+
if (frequency === 0) continue;
|
|
24531
|
+
const lengthNorm = 1 - RELEVANCE_B + RELEVANCE_B * (document.length / averageLength);
|
|
24532
|
+
score += inverseFrequency.get(term) * (frequency * (RELEVANCE_K1 + 1) / (frequency + RELEVANCE_K1 * lengthNorm));
|
|
24533
|
+
}
|
|
24534
|
+
scores[index] = score;
|
|
24535
|
+
});
|
|
24536
|
+
return scores;
|
|
24537
|
+
}
|
|
24538
|
+
function roundRelevance(score) {
|
|
24539
|
+
return Math.round(score * 1e3) / 1e3;
|
|
24540
|
+
}
|
|
24541
|
+
function contextItemRelevanceText(item) {
|
|
24542
|
+
return item.stateKey !== void 0 ? `${item.summary} ${item.stateKey}` : item.summary;
|
|
24543
|
+
}
|
|
24544
|
+
function compareCodeUnitIds(left, right) {
|
|
24545
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
24546
|
+
}
|
|
24547
|
+
|
|
24548
|
+
// plugins/kxm/src/arbiter.ts
|
|
24549
|
+
var ROLE_POLICIES = [
|
|
24550
|
+
{
|
|
24551
|
+
role: "repro",
|
|
24552
|
+
label: "Reproduction specialist",
|
|
24553
|
+
kinds: ["episode", "knowledge", "evidence"],
|
|
24554
|
+
journalCategories: ["error", "lesson", "observation", "contradiction"],
|
|
24555
|
+
budgetTokens: 8e3
|
|
24556
|
+
},
|
|
24557
|
+
{
|
|
24558
|
+
role: "planner",
|
|
24559
|
+
label: "Planner",
|
|
24560
|
+
kinds: ["state", "knowledge", "evidence"],
|
|
24561
|
+
journalCategories: ["plan", "decision", "contradiction", "observation", "hypothesis", "experiment"],
|
|
24562
|
+
budgetTokens: 16e3
|
|
24563
|
+
},
|
|
24564
|
+
{
|
|
24565
|
+
role: "critic",
|
|
24566
|
+
label: "Independent critic",
|
|
24567
|
+
kinds: ["knowledge", "evidence", "episode"],
|
|
24568
|
+
journalCategories: ["contradiction", "error", "lesson", "experiment"],
|
|
24569
|
+
budgetTokens: 12e3
|
|
24570
|
+
},
|
|
24571
|
+
{
|
|
24572
|
+
role: "implementer",
|
|
24573
|
+
label: "Implementer",
|
|
24574
|
+
kinds: ["knowledge", "state", "skill", "episode", "evidence"],
|
|
24575
|
+
journalCategories: ["plan", "decision", "lesson", "state-change"],
|
|
24576
|
+
budgetTokens: 16e3
|
|
24577
|
+
},
|
|
24578
|
+
{
|
|
24579
|
+
role: "verifier",
|
|
24580
|
+
label: "Verifier",
|
|
24581
|
+
kinds: ["evidence", "knowledge", "episode"],
|
|
24582
|
+
journalCategories: ["plan", "error", "lesson", "contradiction"],
|
|
24583
|
+
budgetTokens: 8e3
|
|
24584
|
+
}
|
|
24585
|
+
];
|
|
24586
|
+
function rolePolicy(role) {
|
|
24587
|
+
return ROLE_POLICIES.find((policy) => policy.role === role) ?? {
|
|
24588
|
+
role,
|
|
24589
|
+
label: role,
|
|
24590
|
+
kinds: ["knowledge", "evidence"],
|
|
24591
|
+
journalCategories: ["lesson", "observation"],
|
|
24592
|
+
budgetTokens: DEFAULT_CONTEXT_BUDGET_TOKENS
|
|
24593
|
+
};
|
|
24594
|
+
}
|
|
24595
|
+
var CONFIDENCE_RANK = { verified: 3, probable: 2, uncertain: 1 };
|
|
24596
|
+
var AUTHORITY_WEIGHT = { policy: 3, instruction: 2, evidence: 1, hypothesis: 0 };
|
|
24597
|
+
function arbitrate(requestInput, pool, options = {}) {
|
|
24598
|
+
const request = parseContextRequest(requestInput);
|
|
24599
|
+
const policy = rolePolicy(request.role);
|
|
24600
|
+
const budget = request.budgetTokens ?? policy.budgetTokens;
|
|
24601
|
+
const contradictions = new Set(options.contradictionIds ?? []);
|
|
24602
|
+
const candidates = [];
|
|
24603
|
+
let excludedSuperseded = 0;
|
|
24604
|
+
for (const candidate of pool) {
|
|
24605
|
+
if (candidate.project !== request.project && candidate.project !== "_shared") {
|
|
24606
|
+
throw new ProtocolError(
|
|
24607
|
+
403,
|
|
24608
|
+
`context pool contains cross-project item ${candidate.id}`,
|
|
24609
|
+
"context_isolation_violation"
|
|
24610
|
+
);
|
|
24611
|
+
}
|
|
24612
|
+
if (candidate.status === "superseded" || candidate.status === "rejected") {
|
|
24613
|
+
excludedSuperseded += 1;
|
|
24614
|
+
continue;
|
|
24615
|
+
}
|
|
24616
|
+
candidates.push(candidate);
|
|
24617
|
+
}
|
|
24618
|
+
if (options.skillLifecycle) {
|
|
24619
|
+
for (const metadata of options.skillLifecycle.list("promoted")) {
|
|
24620
|
+
try {
|
|
24621
|
+
options.skillLifecycle.verify("promoted", metadata.id);
|
|
24622
|
+
candidates.push(parseContextItem({
|
|
24623
|
+
id: `skill_${metadata.id}`,
|
|
24624
|
+
kind: "skill",
|
|
24625
|
+
project: request.project,
|
|
24626
|
+
summary: metadata.description ? `${metadata.name}: ${metadata.description}` : metadata.name,
|
|
24627
|
+
provenance: {
|
|
24628
|
+
sourceType: "git",
|
|
24629
|
+
sourceRef: `skill:${metadata.id}@${metadata.contentSha256}`
|
|
24630
|
+
},
|
|
24631
|
+
authority: "instruction",
|
|
24632
|
+
confidence: "verified",
|
|
24633
|
+
status: "current"
|
|
24634
|
+
}));
|
|
24635
|
+
} catch {
|
|
24636
|
+
}
|
|
24637
|
+
}
|
|
24638
|
+
}
|
|
24639
|
+
const kindRank = /* @__PURE__ */ new Map();
|
|
24640
|
+
const requestedKinds = request.includeKinds ?? policy.kinds;
|
|
24641
|
+
requestedKinds.forEach((kind, index) => kindRank.set(kind, index));
|
|
24642
|
+
const kindPreference = (item) => {
|
|
24643
|
+
const rank = kindRank.get(item.kind);
|
|
24644
|
+
return rank === void 0 ? requestedKinds.length : rank;
|
|
24645
|
+
};
|
|
24646
|
+
const allowedKinds = new Set(requestedKinds);
|
|
24647
|
+
const inertProposal = (item) => item.kind === "state" && item.status !== void 0 && item.status !== "current" || item.kind === "skill" && item.status === "proposed";
|
|
24648
|
+
const eligible = candidates.filter((item) => contradictions.has(item.id) || allowedKinds.has(item.kind) && !inertProposal(item));
|
|
24649
|
+
const scoreList = scoreRelevance(request.task, eligible.map(contextItemRelevanceText));
|
|
24650
|
+
const scores = /* @__PURE__ */ new Map();
|
|
24651
|
+
eligible.forEach((item, index) => scores.set(item, scoreList[index] ?? 0));
|
|
24652
|
+
const scoreOf = (item) => scores.get(item) ?? 0;
|
|
24653
|
+
const when = (item) => {
|
|
24654
|
+
const parsed = Date.parse(item.observedAt ?? item.validFrom ?? "");
|
|
24655
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
24656
|
+
};
|
|
24657
|
+
const ordered = [...eligible].sort(
|
|
24658
|
+
(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)
|
|
24659
|
+
);
|
|
24660
|
+
const selected = [];
|
|
24661
|
+
const unresolvedGaps = [];
|
|
24662
|
+
let characters = 0;
|
|
24663
|
+
let deferredForBudget = 0;
|
|
24664
|
+
for (const item of ordered) {
|
|
24665
|
+
if (selected.length >= MAX_CONTEXT_ITEMS) {
|
|
24666
|
+
unresolvedGaps.push("context item limit reached; refine the task or kinds");
|
|
24667
|
+
break;
|
|
24668
|
+
}
|
|
24669
|
+
const itemCharacters = contextItemCharacters(item);
|
|
24670
|
+
if (Math.ceil((characters + itemCharacters) / 4) > budget) {
|
|
24671
|
+
deferredForBudget += 1;
|
|
24672
|
+
continue;
|
|
24673
|
+
}
|
|
24674
|
+
characters += itemCharacters;
|
|
24675
|
+
selected.push(item);
|
|
24676
|
+
}
|
|
24677
|
+
if (deferredForBudget > 0) {
|
|
24678
|
+
unresolvedGaps.push(selected.length === 0 ? `budget of ${budget} tokens cannot fit any selected context` : `budget of ${budget} tokens reached; ${deferredForBudget} candidates deferred`);
|
|
24679
|
+
}
|
|
24680
|
+
if (candidates.length === 0) {
|
|
24681
|
+
unresolvedGaps.push("no context records exist for this project yet");
|
|
24682
|
+
}
|
|
24683
|
+
const bySection = (kind) => selected.filter((item) => item.kind === kind && !contradictions.has(item.id));
|
|
24684
|
+
const packet = {
|
|
24685
|
+
workingState: options.workingState ?? {},
|
|
24686
|
+
currentState: bySection("state").filter((item) => item.status === "current" || item.status === void 0),
|
|
24687
|
+
knowledge: bySection("knowledge"),
|
|
24688
|
+
evidence: bySection("evidence"),
|
|
24689
|
+
episodes: bySection("episode"),
|
|
24690
|
+
skills: bySection("skill").filter((item) => item.status !== "proposed"),
|
|
24691
|
+
contradictions: selected.filter((item) => contradictions.has(item.id)),
|
|
24692
|
+
unresolvedGaps,
|
|
24693
|
+
provenanceSummary: provenanceSummaryOf(selected),
|
|
24694
|
+
estimatedTokens: estimateContextTokens(selected)
|
|
24695
|
+
};
|
|
24696
|
+
validateContextPacketContents(request, packet);
|
|
24697
|
+
return {
|
|
24698
|
+
packet,
|
|
24699
|
+
audit: {
|
|
24700
|
+
request: {
|
|
24701
|
+
project: request.project,
|
|
24702
|
+
role: request.role,
|
|
24703
|
+
task: request.task,
|
|
24704
|
+
...request.workflowRunId !== void 0 ? { workflowRunId: request.workflowRunId } : {},
|
|
24705
|
+
...request.stageId !== void 0 ? { stageId: request.stageId } : {}
|
|
24706
|
+
},
|
|
24707
|
+
selectedIds: selected.map((item) => item.id),
|
|
24708
|
+
provenanceSummary: packet.provenanceSummary,
|
|
24709
|
+
estimatedTokens: packet.estimatedTokens,
|
|
24710
|
+
budgetTokens: budget,
|
|
24711
|
+
candidateCount: candidates.length,
|
|
24712
|
+
excludedSuperseded,
|
|
24713
|
+
unresolvedGaps,
|
|
24714
|
+
relevance: {
|
|
24715
|
+
taskTokens: new Set(relevanceTokens(request.task)).size,
|
|
24716
|
+
matchedCandidates: scoreList.filter((score) => score > 0).length,
|
|
24717
|
+
selected: selected.map((item) => roundRelevance(scoreOf(item)))
|
|
24718
|
+
}
|
|
24719
|
+
}
|
|
24720
|
+
};
|
|
24721
|
+
}
|
|
24722
|
+
function memoryRecordToContextItem(record2, project) {
|
|
24723
|
+
let authority = "instruction";
|
|
24724
|
+
if (record2.authority === "evidence") {
|
|
24725
|
+
authority = "evidence";
|
|
24726
|
+
}
|
|
24727
|
+
const sourceType = CONTEXT_SOURCE_TYPES.includes(record2.provenance?.sourceType) ? record2.provenance.sourceType : "git";
|
|
24728
|
+
const targetProject = record2.scope === "operator" ? "_shared" : project;
|
|
24729
|
+
return parseContextItem({
|
|
24730
|
+
id: `mem_${record2.id}`,
|
|
24731
|
+
kind: "knowledge",
|
|
24732
|
+
scope: record2.scope,
|
|
24733
|
+
project: targetProject,
|
|
24734
|
+
summary: record2.summary,
|
|
24735
|
+
provenance: {
|
|
24736
|
+
sourceType,
|
|
24737
|
+
sourceRef: record2.provenance?.sourceRef ?? `memory:${record2.id}.md`
|
|
24738
|
+
},
|
|
24739
|
+
authority,
|
|
24740
|
+
confidence: record2.confidence,
|
|
24741
|
+
status: record2.lifecycle === "active" ? "current" : "superseded",
|
|
24742
|
+
evidenceRefs: record2.evidenceRefs && record2.evidenceRefs.length > 0 ? record2.evidenceRefs : void 0
|
|
24743
|
+
});
|
|
24744
|
+
}
|
|
24745
|
+
|
|
24746
|
+
// plugins/kxm/src/memory.ts
|
|
24747
|
+
var import_yaml5 = __toESM(require_dist(), 1);
|
|
24748
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync4, readdirSync as readdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "node:fs";
|
|
24749
|
+
import { extname as extname2, join as join10, resolve as resolve5 } from "node:path";
|
|
24750
|
+
var MEMORY_SCHEMA = "kxm.memory.v1";
|
|
24751
|
+
var VALID_SCOPES = /* @__PURE__ */ new Set(["agent", "project", "run", "operator"]);
|
|
24752
|
+
var VALID_AUTHORITIES = /* @__PURE__ */ new Set(["instruction", "evidence", "promoted"]);
|
|
24753
|
+
var VALID_CONFIDENCES = /* @__PURE__ */ new Set(["verified", "probable", "uncertain"]);
|
|
24754
|
+
var VALID_LIFECYCLES = /* @__PURE__ */ new Set(["active", "deprecated", "superseded"]);
|
|
24755
|
+
var BANNED_CONTROL_PLANE_FIELDS = /* @__PURE__ */ new Set([
|
|
24756
|
+
"permissions",
|
|
24757
|
+
"tools",
|
|
24758
|
+
"allow",
|
|
24759
|
+
"deny",
|
|
24760
|
+
"grants",
|
|
24761
|
+
"approval",
|
|
24762
|
+
"policy",
|
|
24763
|
+
"scopes",
|
|
24764
|
+
"credentials",
|
|
24765
|
+
"secrets",
|
|
24766
|
+
"token",
|
|
24767
|
+
"apiKey",
|
|
24768
|
+
"password"
|
|
24769
|
+
]);
|
|
24770
|
+
function parseFrontmatter(content) {
|
|
24771
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
|
|
24772
|
+
if (!match) {
|
|
24773
|
+
throw new Error("memory file must contain YAML frontmatter enclosed in '---'");
|
|
24774
|
+
}
|
|
24775
|
+
return { frontmatter: match[1] ?? "", body: match[2]?.trim() ?? "" };
|
|
24776
|
+
}
|
|
24777
|
+
function parseMemoryRecord(raw, filename = "memory.md") {
|
|
24778
|
+
const { frontmatter, body } = parseFrontmatter(raw);
|
|
24779
|
+
const data = (0, import_yaml5.parse)(frontmatter);
|
|
24780
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
24781
|
+
throw new Error(`invalid YAML frontmatter in ${filename}`);
|
|
24782
|
+
}
|
|
24783
|
+
for (const field of BANNED_CONTROL_PLANE_FIELDS) {
|
|
24784
|
+
if (field in data) {
|
|
24785
|
+
throw new Error(`memory record ${filename} may not carry control-plane field '${field}'`);
|
|
24786
|
+
}
|
|
24787
|
+
}
|
|
24788
|
+
if (data.schema !== MEMORY_SCHEMA) {
|
|
24789
|
+
throw new Error(`memory record ${filename} has unsupported schema: expected ${MEMORY_SCHEMA}, got ${String(data.schema)}`);
|
|
24790
|
+
}
|
|
24791
|
+
const id = typeof data.id === "string" ? data.id.trim() : "";
|
|
24792
|
+
if (!id || !/^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$/i.test(id)) {
|
|
24793
|
+
throw new Error(`memory record ${filename} has invalid id: ${String(data.id)}`);
|
|
24794
|
+
}
|
|
24795
|
+
const scope = data.scope;
|
|
24796
|
+
if (!VALID_SCOPES.has(scope)) {
|
|
24797
|
+
throw new Error(`memory record ${filename} has invalid scope: must be one of agent, project, run, operator`);
|
|
24798
|
+
}
|
|
24799
|
+
const kind = typeof data.kind === "string" ? data.kind.trim() : "";
|
|
24800
|
+
if (!kind) {
|
|
24801
|
+
throw new Error(`memory record ${filename} is missing kind`);
|
|
24802
|
+
}
|
|
24803
|
+
const rawSummary = typeof data.summary === "string" ? data.summary.trim() : "";
|
|
24804
|
+
if (!rawSummary) {
|
|
24805
|
+
throw new Error(`memory record ${filename} is missing summary`);
|
|
24806
|
+
}
|
|
24807
|
+
const summary = redactSecrets(rawSummary);
|
|
24808
|
+
const rawProv = data.provenance;
|
|
24809
|
+
if (!rawProv || typeof rawProv !== "object" || typeof rawProv.sourceType !== "string" || !rawProv.sourceType.trim()) {
|
|
24810
|
+
throw new Error(`memory record ${filename} is missing provenance.sourceType`);
|
|
24811
|
+
}
|
|
24812
|
+
const provenance = {
|
|
24813
|
+
sourceType: rawProv.sourceType.trim(),
|
|
24814
|
+
...typeof rawProv.sourceRef === "string" ? { sourceRef: redactSecrets(rawProv.sourceRef.trim()) } : {},
|
|
24815
|
+
...typeof rawProv.runId === "string" ? { runId: rawProv.runId.trim() } : {},
|
|
24816
|
+
...typeof rawProv.timestamp === "string" ? { timestamp: rawProv.timestamp.trim() } : {}
|
|
24817
|
+
};
|
|
24818
|
+
const authority = data.authority;
|
|
24819
|
+
if (!VALID_AUTHORITIES.has(authority)) {
|
|
24820
|
+
throw new Error(`memory record ${filename} has invalid authority: ${String(data.authority)}`);
|
|
24821
|
+
}
|
|
24822
|
+
const confidence = data.confidence;
|
|
24823
|
+
if (!VALID_CONFIDENCES.has(confidence)) {
|
|
24824
|
+
throw new Error(`memory record ${filename} has invalid confidence: ${String(data.confidence)}`);
|
|
24825
|
+
}
|
|
24826
|
+
const lifecycle = data.lifecycle;
|
|
24827
|
+
if (!VALID_LIFECYCLES.has(lifecycle)) {
|
|
24828
|
+
throw new Error(`memory record ${filename} has invalid lifecycle: ${String(data.lifecycle)}`);
|
|
24829
|
+
}
|
|
24830
|
+
const rawRefs = Array.isArray(data.evidenceRefs) ? data.evidenceRefs : [];
|
|
24831
|
+
const evidenceRefs = rawRefs.map(String);
|
|
24832
|
+
return {
|
|
24833
|
+
schema: MEMORY_SCHEMA,
|
|
24834
|
+
id,
|
|
24835
|
+
scope,
|
|
24836
|
+
kind,
|
|
24837
|
+
summary,
|
|
24838
|
+
provenance,
|
|
24839
|
+
authority,
|
|
24840
|
+
confidence,
|
|
24841
|
+
lifecycle,
|
|
24842
|
+
evidenceRefs,
|
|
24843
|
+
...body ? { body } : {}
|
|
24844
|
+
};
|
|
24845
|
+
}
|
|
24846
|
+
function memoryDirectories(repoRoot) {
|
|
24847
|
+
const root = resolve5(repoRoot);
|
|
24848
|
+
const memoryDir = join10(root, ".kxm", "memory");
|
|
24849
|
+
const candidatesDir = join10(memoryDir, "candidates");
|
|
24850
|
+
return { memoryDir, candidatesDir };
|
|
24851
|
+
}
|
|
24852
|
+
function loadAuthoredMemory(repoRoot) {
|
|
24853
|
+
const { memoryDir } = memoryDirectories(repoRoot);
|
|
24854
|
+
if (!existsSync9(memoryDir)) return [];
|
|
24855
|
+
const records = [];
|
|
24856
|
+
const entries = readdirSync5(memoryDir, { withFileTypes: true });
|
|
24857
|
+
for (const entry of entries) {
|
|
24858
|
+
if (entry.isFile() && extname2(entry.name) === ".md") {
|
|
24859
|
+
const fullPath = join10(memoryDir, entry.name);
|
|
24860
|
+
try {
|
|
24861
|
+
const text = readFileSync7(fullPath, "utf8");
|
|
24862
|
+
const record2 = parseMemoryRecord(text, entry.name);
|
|
24863
|
+
if (record2.lifecycle === "active") {
|
|
24864
|
+
records.push(record2);
|
|
24865
|
+
}
|
|
24866
|
+
} catch (error) {
|
|
24867
|
+
throw new Error(`failed to parse authored memory file ${entry.name}: ${error instanceof Error ? error.message : String(error)}`);
|
|
24868
|
+
}
|
|
24869
|
+
}
|
|
24870
|
+
}
|
|
24871
|
+
return records.sort((left, right) => left.id.localeCompare(right.id));
|
|
24872
|
+
}
|
|
24873
|
+
|
|
24874
|
+
// plugins/kxm/src/skills.ts
|
|
24875
|
+
var import_yaml6 = __toESM(require_dist(), 1);
|
|
24876
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
24877
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync5, readdirSync as readdirSync6, readFileSync as readFileSync8, renameSync, rmSync, statSync as statSync2, writeFileSync as writeFileSync5 } from "node:fs";
|
|
24878
|
+
import { dirname as dirname7, join as join11 } from "node:path";
|
|
24879
|
+
var SKILL_CANDIDATE_SCHEMA = "kxm.skill-candidate.v1";
|
|
24880
|
+
var SKILL_EVALUATION_SCHEMA = "kxm.skill-evaluation.v1";
|
|
24881
|
+
var SKILL_DECISION_SCHEMA = "kxm.skill-decision.v1";
|
|
24882
|
+
var MAX_SKILL_NAME_CHARS = 64;
|
|
24883
|
+
var MAX_SKILL_CONTENT_CHARS = 32e3;
|
|
24884
|
+
var MAX_SKILL_EVIDENCE_REFS = 32;
|
|
24885
|
+
var MAX_SKILL_MODELS = 16;
|
|
24886
|
+
var PROMOTION_REQUIRED_EVALUATIONS = [
|
|
24887
|
+
"static-review",
|
|
24888
|
+
"sandbox",
|
|
24889
|
+
"functional",
|
|
24890
|
+
"safety"
|
|
24891
|
+
];
|
|
24892
|
+
var SkillLifecycleError = class extends Error {
|
|
24893
|
+
code;
|
|
24894
|
+
constructor(code, message) {
|
|
24895
|
+
super(message);
|
|
24896
|
+
this.name = "SkillLifecycleError";
|
|
24897
|
+
this.code = code;
|
|
24898
|
+
}
|
|
24899
|
+
};
|
|
24900
|
+
function skillContentSha256(content) {
|
|
24901
|
+
return createHash10("sha256").update(content, "utf8").digest("hex");
|
|
24902
|
+
}
|
|
24903
|
+
function skillIdFor(name, contentSha256) {
|
|
24904
|
+
const slug = name.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
|
|
24905
|
+
if (!slug) throw new SkillLifecycleError("invalid_skill_name", "skill name must contain alphanumeric characters");
|
|
24906
|
+
return `${slug}.${contentSha256.slice(0, 12)}`;
|
|
24907
|
+
}
|
|
24908
|
+
function parseSkillFrontmatter(content) {
|
|
24909
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
|
|
24910
|
+
if (!match) {
|
|
24911
|
+
return { frontmatter: null, body: content };
|
|
24912
|
+
}
|
|
24913
|
+
const rawFm = match[1];
|
|
24914
|
+
const rawBody = match[2];
|
|
24915
|
+
if (rawFm === void 0 || rawBody === void 0) {
|
|
24916
|
+
return { frontmatter: null, body: content };
|
|
24917
|
+
}
|
|
24918
|
+
try {
|
|
24919
|
+
const parsed = (0, import_yaml6.parse)(rawFm);
|
|
24920
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
24921
|
+
return { frontmatter: parsed, body: rawBody };
|
|
24922
|
+
}
|
|
24923
|
+
} catch {
|
|
24924
|
+
}
|
|
24925
|
+
return { frontmatter: null, body: content };
|
|
24926
|
+
}
|
|
24927
|
+
function ensureSkillFrontmatter(content, name, description) {
|
|
24928
|
+
const parsed = parseSkillFrontmatter(content);
|
|
24929
|
+
if (parsed.frontmatter) {
|
|
24930
|
+
const fmName = typeof parsed.frontmatter.name === "string" && parsed.frontmatter.name.trim() ? parsed.frontmatter.name.trim() : name;
|
|
24931
|
+
const fmDesc = typeof parsed.frontmatter.description === "string" && parsed.frontmatter.description.trim() ? parsed.frontmatter.description.trim() : description || `Governed skill for ${fmName}`;
|
|
24932
|
+
const rest2 = parsed.body.replace(/^(\r?\n)+/, "");
|
|
24933
|
+
return `---
|
|
24934
|
+
name: ${fmName}
|
|
24935
|
+
description: ${fmDesc}
|
|
24936
|
+
---
|
|
24937
|
+
|
|
24938
|
+
${rest2}`;
|
|
24939
|
+
}
|
|
24940
|
+
const desc = description || `Governed skill for ${name}`;
|
|
24941
|
+
const rest = content.replace(/^(\r?\n)+/, "");
|
|
24942
|
+
return `---
|
|
24943
|
+
name: ${name}
|
|
24944
|
+
description: ${desc}
|
|
24945
|
+
---
|
|
24946
|
+
|
|
24947
|
+
${rest}`;
|
|
24948
|
+
}
|
|
24949
|
+
function createUnifiedPatch(relativePath, content) {
|
|
24950
|
+
const lines = content.split("\n");
|
|
24951
|
+
const count = lines.length;
|
|
24952
|
+
const header = [
|
|
24953
|
+
`diff --git a/${relativePath} b/${relativePath}`,
|
|
24954
|
+
"new file mode 100644",
|
|
24955
|
+
"--- /dev/null",
|
|
24956
|
+
`+++ b/${relativePath}`,
|
|
24957
|
+
`@@ -0,0 +1,${count} @@`
|
|
24958
|
+
];
|
|
24959
|
+
const body = lines.map((l) => `+${l}`);
|
|
24960
|
+
return [...header, ...body, ""].join("\n");
|
|
24961
|
+
}
|
|
24962
|
+
var SkillLifecycle = class {
|
|
24963
|
+
root;
|
|
24964
|
+
now;
|
|
24965
|
+
allowOptimizationEvals;
|
|
24966
|
+
dryRun;
|
|
24967
|
+
/** What a `dryRun` lifecycle would have written or moved, in order. */
|
|
24968
|
+
planned = [];
|
|
24969
|
+
constructor(root, options = {}) {
|
|
24970
|
+
this.root = root;
|
|
24971
|
+
this.now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
24972
|
+
this.allowOptimizationEvals = options.allowOptimizationEvals === true;
|
|
24973
|
+
this.dryRun = options.dryRun === true;
|
|
24974
|
+
}
|
|
24975
|
+
write(file, content) {
|
|
24976
|
+
if (this.dryRun) {
|
|
24977
|
+
this.planned.push({ action: "write", target: file });
|
|
24978
|
+
return;
|
|
24979
|
+
}
|
|
24980
|
+
mkdirSync5(dirname7(file), { recursive: true });
|
|
24981
|
+
writeFileSync5(file, content);
|
|
24982
|
+
}
|
|
24983
|
+
dir(state) {
|
|
24984
|
+
return join11(this.root, state === "candidate" ? "candidates" : `${state}s`.replace("rejecteds", "rejected").replace("promoteds", "promoted"));
|
|
24985
|
+
}
|
|
24986
|
+
historyFile(id) {
|
|
24987
|
+
return join11(this.root, "history", `${id}.jsonl`);
|
|
24988
|
+
}
|
|
24989
|
+
paths(state, id) {
|
|
24990
|
+
const dir = join11(this.dir(state), id);
|
|
24991
|
+
return { dir, metadata: join11(dir, "metadata.json"), skill: join11(dir, "SKILL.md") };
|
|
24992
|
+
}
|
|
24993
|
+
appendHistory(id, record2) {
|
|
24994
|
+
const line = `${JSON.stringify(record2)}
|
|
24995
|
+
`;
|
|
24996
|
+
if (existsSync10(this.historyFile(id))) {
|
|
24997
|
+
const existing = readFileSync8(this.historyFile(id), "utf8");
|
|
24998
|
+
const lines = existing.split("\n").filter((entry) => entry.trim());
|
|
24999
|
+
this.write(this.historyFile(id), [...lines.slice(-499), line.trim()].join("\n") + "\n");
|
|
25000
|
+
} else {
|
|
25001
|
+
this.write(this.historyFile(id), line);
|
|
25002
|
+
}
|
|
25003
|
+
}
|
|
25004
|
+
history(id) {
|
|
25005
|
+
const file = this.historyFile(id);
|
|
25006
|
+
if (!existsSync10(file)) return [];
|
|
25007
|
+
return readFileSync8(file, "utf8").split("\n").filter((line) => line.trim()).map((line) => JSON.parse(line));
|
|
25008
|
+
}
|
|
25009
|
+
readMetadata(state, id) {
|
|
25010
|
+
const { metadata } = this.paths(state, id);
|
|
25011
|
+
if (!existsSync10(metadata)) {
|
|
25012
|
+
throw new SkillLifecycleError("skill_not_found", `skill ${id} not found in ${state}`);
|
|
25013
|
+
}
|
|
25014
|
+
return JSON.parse(readFileSync8(metadata, "utf8"));
|
|
25015
|
+
}
|
|
25016
|
+
move(from, to, id) {
|
|
25017
|
+
const fromDir = join11(this.dir(from), id);
|
|
25018
|
+
const toDir = join11(this.dir(to), id);
|
|
25019
|
+
if (!existsSync10(fromDir)) {
|
|
25020
|
+
throw new SkillLifecycleError("skill_not_found", `skill ${id} not found in ${from}`);
|
|
25021
|
+
}
|
|
25022
|
+
if (this.dryRun) {
|
|
25023
|
+
this.planned.push({ action: "move", target: `${fromDir} -> ${toDir}` });
|
|
25024
|
+
return;
|
|
25025
|
+
}
|
|
25026
|
+
mkdirSync5(this.dir(to), { recursive: true });
|
|
25027
|
+
if (existsSync10(toDir)) rmSync(toDir, { recursive: true, force: true });
|
|
25028
|
+
renameSync(fromDir, toDir);
|
|
25029
|
+
}
|
|
25030
|
+
/** Submit a new skill candidate. Content is redacted of secret material at
|
|
25031
|
+
* creation; the ID is derived from name + content hash. */
|
|
25032
|
+
create(input) {
|
|
25033
|
+
const name = input.name?.trim();
|
|
25034
|
+
if (!name || name.length > MAX_SKILL_NAME_CHARS) {
|
|
25035
|
+
throw new SkillLifecycleError("invalid_skill_name", `skill name must be 1-${MAX_SKILL_NAME_CHARS} characters`);
|
|
25036
|
+
}
|
|
25037
|
+
const rawContent = redactSecrets(input.content ?? "");
|
|
25038
|
+
if (!rawContent.trim() || rawContent.length > MAX_SKILL_CONTENT_CHARS) {
|
|
25039
|
+
throw new SkillLifecycleError("invalid_skill_content", `skill content must be 1-${MAX_SKILL_CONTENT_CHARS} characters`);
|
|
25040
|
+
}
|
|
25041
|
+
const rawDesc = redactSecrets(input.description?.trim() ?? "");
|
|
25042
|
+
const content = ensureSkillFrontmatter(rawContent, name, rawDesc);
|
|
25043
|
+
if (content.length > MAX_SKILL_CONTENT_CHARS) {
|
|
25044
|
+
throw new SkillLifecycleError("invalid_skill_content", `skill content must be 1-${MAX_SKILL_CONTENT_CHARS} characters`);
|
|
25045
|
+
}
|
|
25046
|
+
const { frontmatter } = parseSkillFrontmatter(content);
|
|
25047
|
+
const description = typeof frontmatter?.description === "string" && frontmatter.description.trim() ? frontmatter.description.trim() : rawDesc || `Governed skill for ${name}`;
|
|
25048
|
+
const sources = {
|
|
25049
|
+
runIds: boundedList(input.sources?.runIds, "runIds"),
|
|
25050
|
+
journalEntryIds: boundedList(input.sources?.journalEntryIds, "journalEntryIds"),
|
|
25051
|
+
evidenceReceipts: boundedList(input.sources?.evidenceReceipts, "evidenceReceipts")
|
|
25052
|
+
};
|
|
25053
|
+
if (sources.runIds.length === 0 && sources.journalEntryIds.length === 0 && sources.evidenceReceipts.length === 0) {
|
|
25054
|
+
throw new SkillLifecycleError(
|
|
25055
|
+
"skill_sources_required",
|
|
25056
|
+
"a skill candidate must reference at least one source run, journal entry, or evidence receipt"
|
|
25057
|
+
);
|
|
25058
|
+
}
|
|
25059
|
+
const createdBy = input.createdBy?.trim();
|
|
25060
|
+
if (!createdBy) throw new SkillLifecycleError("invalid_skill_author", "createdBy is required");
|
|
25061
|
+
const models = boundedList(input.compatibility?.models, "compatibility.models");
|
|
25062
|
+
if (models.length === 0 || models.length > MAX_SKILL_MODELS) {
|
|
25063
|
+
throw new SkillLifecycleError("invalid_skill_compatibility", `compatibility.models must list 1-${MAX_SKILL_MODELS} models`);
|
|
25064
|
+
}
|
|
25065
|
+
const harness = input.compatibility?.harness?.trim();
|
|
25066
|
+
if (!harness) throw new SkillLifecycleError("invalid_skill_compatibility", "compatibility.harness is required");
|
|
25067
|
+
const contentSha256 = skillContentSha256(content);
|
|
25068
|
+
const id = skillIdFor(name, contentSha256);
|
|
25069
|
+
const { metadata, skill } = this.paths("candidate", id);
|
|
25070
|
+
if (existsSync10(metadata)) {
|
|
25071
|
+
throw new SkillLifecycleError(
|
|
25072
|
+
"skill_candidate_exists",
|
|
25073
|
+
`identical candidate ${id} already exists; changed behavior requires changed content`
|
|
25074
|
+
);
|
|
25075
|
+
}
|
|
25076
|
+
const record2 = {
|
|
25077
|
+
schema: SKILL_CANDIDATE_SCHEMA,
|
|
25078
|
+
id,
|
|
25079
|
+
name,
|
|
25080
|
+
description,
|
|
25081
|
+
contentSha256,
|
|
25082
|
+
version: input.version ?? 1,
|
|
25083
|
+
sources,
|
|
25084
|
+
compatibility: { harness, models },
|
|
25085
|
+
createdBy,
|
|
25086
|
+
createdAt: this.now(),
|
|
25087
|
+
...input.supersedes ? { supersedes: input.supersedes } : {}
|
|
25088
|
+
};
|
|
25089
|
+
this.write(skill, content);
|
|
25090
|
+
this.write(metadata, `${JSON.stringify(record2, null, 2)}
|
|
25091
|
+
`);
|
|
25092
|
+
this.appendHistory(id, { schema: "kxm.skill-history-event.v1", event: "candidate_created", by: createdBy, supersedes: input.supersedes, at: record2.createdAt });
|
|
25093
|
+
return record2;
|
|
25094
|
+
}
|
|
25095
|
+
/** Record a protected evaluation. A failed functional or safety evaluation
|
|
25096
|
+
* deterministically quarantines the candidate. */
|
|
25097
|
+
evaluate(candidateId, input) {
|
|
25098
|
+
if (input.kind === "optimization" && !this.allowOptimizationEvals) {
|
|
25099
|
+
throw new SkillLifecycleError(
|
|
25100
|
+
"skill_optimization_disabled",
|
|
25101
|
+
"optimization evaluations are disabled; enable them explicitly behind protected evals"
|
|
25102
|
+
);
|
|
25103
|
+
}
|
|
25104
|
+
const metadata = this.readMetadata("candidate", candidateId);
|
|
25105
|
+
const evaluatorVersion = input.evaluatorVersion?.trim();
|
|
25106
|
+
if (!evaluatorVersion) throw new SkillLifecycleError("invalid_skill_evaluation", "evaluatorVersion is required");
|
|
25107
|
+
const evaluation = {
|
|
25108
|
+
schema: SKILL_EVALUATION_SCHEMA,
|
|
25109
|
+
candidateId,
|
|
25110
|
+
kind: input.kind,
|
|
25111
|
+
evaluatorVersion,
|
|
25112
|
+
passed: input.passed === true,
|
|
25113
|
+
...input.score !== void 0 ? { score: input.score } : {},
|
|
25114
|
+
...input.details ? { details: redactSecrets(input.details.slice(0, 2e3)) } : {},
|
|
25115
|
+
evaluatedAt: this.now()
|
|
25116
|
+
};
|
|
25117
|
+
this.appendHistory(candidateId, evaluation);
|
|
25118
|
+
let quarantined = false;
|
|
25119
|
+
if (!evaluation.passed && (input.kind === "functional" || input.kind === "safety")) {
|
|
25120
|
+
const decision = {
|
|
25121
|
+
schema: SKILL_DECISION_SCHEMA,
|
|
25122
|
+
candidateId,
|
|
25123
|
+
decision: "quarantined",
|
|
25124
|
+
decidedBy: input.evaluatedBy?.trim() || `evaluator:${evaluatorVersion}`,
|
|
25125
|
+
reason: `automatic quarantine: ${input.kind} evaluation failed (${evaluatorVersion})`,
|
|
25126
|
+
evidenceRefs: [`evaluation:${input.kind}:${evaluatorVersion}`],
|
|
25127
|
+
decidedAt: this.now()
|
|
25128
|
+
};
|
|
25129
|
+
this.move("candidate", "quarantined", candidateId);
|
|
25130
|
+
this.appendHistory(candidateId, decision);
|
|
25131
|
+
quarantined = true;
|
|
25132
|
+
}
|
|
25133
|
+
return { evaluation, quarantined };
|
|
25134
|
+
}
|
|
25135
|
+
evaluationsFor(candidateId) {
|
|
25136
|
+
return this.history(candidateId).filter(
|
|
25137
|
+
(record2) => record2.schema === SKILL_EVALUATION_SCHEMA
|
|
25138
|
+
);
|
|
25139
|
+
}
|
|
25140
|
+
/** Promote a candidate that passed every protected evaluation. The
|
|
25141
|
+
* promoter must differ from the author, cite durable evidence, and the
|
|
25142
|
+
* promoted content is hash-pinned and immutable. Emits a unified diff patch
|
|
25143
|
+
* instead of moving the candidate directory. */
|
|
25144
|
+
promote(candidateId, decision) {
|
|
25145
|
+
const metadata = this.readMetadata("candidate", candidateId);
|
|
25146
|
+
const decidedBy = decision.decidedBy?.trim();
|
|
25147
|
+
if (!decidedBy) throw new SkillLifecycleError("invalid_skill_decision", "decidedBy is required");
|
|
25148
|
+
if (decidedBy === metadata.createdBy) {
|
|
25149
|
+
throw new SkillLifecycleError("skill_promotion_invalid", "the author of a skill candidate cannot promote it");
|
|
25150
|
+
}
|
|
25151
|
+
const evidenceRefs = boundedList(decision.evidenceRefs, "evidenceRefs");
|
|
25152
|
+
if (evidenceRefs.length === 0) {
|
|
25153
|
+
throw new SkillLifecycleError("skill_promotion_invalid", "promotion requires durable evidence references");
|
|
25154
|
+
}
|
|
25155
|
+
const evaluations = this.evaluationsFor(candidateId);
|
|
25156
|
+
const missing = [];
|
|
25157
|
+
for (const kind of PROMOTION_REQUIRED_EVALUATIONS) {
|
|
25158
|
+
const latest = [...evaluations].reverse().find((record3) => record3.kind === kind);
|
|
25159
|
+
if (!latest || !latest.passed) missing.push(kind);
|
|
25160
|
+
}
|
|
25161
|
+
if (missing.length > 0) {
|
|
25162
|
+
throw new SkillLifecycleError(
|
|
25163
|
+
"skill_evaluations_incomplete",
|
|
25164
|
+
`promotion requires passing ${missing.join(", ")} evaluations`
|
|
25165
|
+
);
|
|
25166
|
+
}
|
|
25167
|
+
this.verify("candidate", candidateId);
|
|
25168
|
+
const record2 = {
|
|
25169
|
+
schema: SKILL_DECISION_SCHEMA,
|
|
25170
|
+
candidateId,
|
|
25171
|
+
decision: "promoted",
|
|
25172
|
+
decidedBy,
|
|
25173
|
+
reason: decision.reason?.trim() || "passed protected evaluation",
|
|
25174
|
+
evidenceRefs,
|
|
25175
|
+
decidedAt: this.now()
|
|
25176
|
+
};
|
|
25177
|
+
const candidatePaths = this.paths("candidate", candidateId);
|
|
25178
|
+
const promotedPaths = this.paths("promoted", candidateId);
|
|
25179
|
+
const skillContent = readFileSync8(candidatePaths.skill, "utf8");
|
|
25180
|
+
const metadataContent = readFileSync8(candidatePaths.metadata, "utf8");
|
|
25181
|
+
this.write(promotedPaths.skill, skillContent);
|
|
25182
|
+
this.write(promotedPaths.metadata, metadataContent);
|
|
25183
|
+
const patchPath = join11(this.root, "patches", `${candidateId}.patch`);
|
|
25184
|
+
const relSkillPath = `.kxm/skills/promoted/${candidateId}/SKILL.md`;
|
|
25185
|
+
const relMetaPath = `.kxm/skills/promoted/${candidateId}/metadata.json`;
|
|
25186
|
+
const patch = `${createUnifiedPatch(relSkillPath, skillContent)}${createUnifiedPatch(relMetaPath, metadataContent)}`;
|
|
25187
|
+
this.write(patchPath, patch);
|
|
25188
|
+
this.appendHistory(candidateId, record2);
|
|
25189
|
+
return { ...metadata, patch, patchPath };
|
|
25190
|
+
}
|
|
25191
|
+
reject(candidateId, decision) {
|
|
25192
|
+
const metadata = this.readMetadata("candidate", candidateId);
|
|
25193
|
+
const record2 = {
|
|
25194
|
+
schema: SKILL_DECISION_SCHEMA,
|
|
25195
|
+
candidateId,
|
|
25196
|
+
decision: "rejected",
|
|
25197
|
+
decidedBy: decision.decidedBy?.trim() || "kxm-admin",
|
|
25198
|
+
reason: decision.reason?.trim() || "rejected",
|
|
25199
|
+
evidenceRefs: [],
|
|
25200
|
+
decidedAt: this.now()
|
|
25201
|
+
};
|
|
25202
|
+
this.move("candidate", "rejected", candidateId);
|
|
25203
|
+
this.appendHistory(candidateId, record2);
|
|
25204
|
+
return metadata;
|
|
25205
|
+
}
|
|
25206
|
+
/** Verify content integrity of a stored skill (any state). Detects
|
|
25207
|
+
* out-of-band edits to promoted skills and verifies standard YAML frontmatter. */
|
|
25208
|
+
verify(state, id) {
|
|
25209
|
+
const metadata = this.readMetadata(state, id);
|
|
25210
|
+
const { skill } = this.paths(state, id);
|
|
25211
|
+
const content = readFileSync8(skill, "utf8");
|
|
25212
|
+
if (skillContentSha256(content) !== metadata.contentSha256) {
|
|
25213
|
+
throw new SkillLifecycleError(
|
|
25214
|
+
"skill_integrity_violation",
|
|
25215
|
+
`skill ${id} content does not match its pinned hash; promoted skills are immutable and require a new candidate/eval cycle`
|
|
25216
|
+
);
|
|
25217
|
+
}
|
|
25218
|
+
const { frontmatter } = parseSkillFrontmatter(content);
|
|
25219
|
+
if (!frontmatter || typeof frontmatter.name !== "string" || !frontmatter.name.trim() || typeof frontmatter.description !== "string" || !frontmatter.description.trim()) {
|
|
25220
|
+
throw new SkillLifecycleError(
|
|
25221
|
+
"invalid_skill_frontmatter",
|
|
25222
|
+
`skill ${id} must contain valid YAML frontmatter with 'name' and 'description'`
|
|
25223
|
+
);
|
|
25224
|
+
}
|
|
25225
|
+
return metadata;
|
|
25226
|
+
}
|
|
25227
|
+
list(state) {
|
|
25228
|
+
const dir = this.dir(state);
|
|
25229
|
+
if (!existsSync10(dir)) return [];
|
|
25230
|
+
const ids = readdirSorted(dir);
|
|
25231
|
+
return ids.map((id) => {
|
|
25232
|
+
try {
|
|
25233
|
+
return this.readMetadata(state, id);
|
|
25234
|
+
} catch {
|
|
25235
|
+
return void 0;
|
|
25236
|
+
}
|
|
25237
|
+
}).filter((metadata) => metadata !== void 0);
|
|
25238
|
+
}
|
|
25239
|
+
read(state, id) {
|
|
25240
|
+
const metadata = this.readMetadata(state, id);
|
|
25241
|
+
const { skill } = this.paths(state, id);
|
|
25242
|
+
return { metadata, content: readFileSync8(skill, "utf8") };
|
|
25243
|
+
}
|
|
25244
|
+
};
|
|
25245
|
+
function boundedList(value, field) {
|
|
25246
|
+
if (value === void 0) return [];
|
|
25247
|
+
if (!Array.isArray(value)) {
|
|
25248
|
+
throw new SkillLifecycleError("invalid_skill_input", `${field} must be an array of strings`);
|
|
25249
|
+
}
|
|
25250
|
+
const refs = value.map((ref) => String(ref).trim()).filter((ref) => ref.length > 0);
|
|
25251
|
+
if (refs.length > MAX_SKILL_EVIDENCE_REFS) {
|
|
25252
|
+
throw new SkillLifecycleError("invalid_skill_input", `${field} exceeds ${MAX_SKILL_EVIDENCE_REFS} references`);
|
|
25253
|
+
}
|
|
25254
|
+
return [...new Set(refs)];
|
|
25255
|
+
}
|
|
25256
|
+
function readdirSorted(dir) {
|
|
25257
|
+
return readdirSync6(dir).filter((entry) => statSync2(join11(dir, entry)).isDirectory()).sort();
|
|
25258
|
+
}
|
|
25259
|
+
|
|
25260
|
+
// plugins/kxm/src/dispatch-context.ts
|
|
25261
|
+
var DISPATCH_CONTEXT_BUDGET_TOKENS = 4e3;
|
|
25262
|
+
var DISPATCH_CONTEXT_MAX_PROJECT_ITEMS = 5;
|
|
25263
|
+
var GAP_NOT_LOADED = "dispatch_context_not_loaded";
|
|
25264
|
+
var GAP_FAILED = "dispatch_context_failed";
|
|
25265
|
+
var GAP_GIT_UNAVAILABLE = "dispatch_context_withheld:git_unavailable";
|
|
25266
|
+
var GAP_UNCOMMITTED = "dispatch_context_withheld:uncommitted";
|
|
25267
|
+
var GAP_DRIFT = "dispatch_context_withheld:memory_revision_drift";
|
|
25268
|
+
var GAP_MEMORY_UNREADABLE = "dispatch_context_memory_unreadable";
|
|
25269
|
+
var GAP_SKILLS_UNREADABLE = "dispatch_context_skills_unreadable";
|
|
25270
|
+
var DISPATCH_CONTEXT_PATHSPEC = [
|
|
25271
|
+
":(glob).kxm/memory/*.md",
|
|
25272
|
+
":(glob).kxm/skills/promoted/*/SKILL.md",
|
|
25273
|
+
":(glob).kxm/skills/promoted/*/metadata.json"
|
|
25274
|
+
];
|
|
25275
|
+
var GIT_STATUS_TIMEOUT_MS = 5e3;
|
|
25276
|
+
var EMPTY_DISPATCH_SOURCES = Object.freeze({
|
|
25277
|
+
present: false,
|
|
25278
|
+
pool: Object.freeze([]),
|
|
25279
|
+
promoted: Object.freeze([]),
|
|
25280
|
+
gaps: Object.freeze([]),
|
|
25281
|
+
skippedUnboundScopes: 0
|
|
25282
|
+
});
|
|
25283
|
+
var NOT_LOADED_DISPATCH_SOURCES = Object.freeze({
|
|
25284
|
+
present: true,
|
|
25285
|
+
pool: Object.freeze([]),
|
|
25286
|
+
promoted: Object.freeze([]),
|
|
25287
|
+
gaps: Object.freeze([GAP_NOT_LOADED]),
|
|
25288
|
+
skippedUnboundScopes: 0
|
|
25289
|
+
});
|
|
25290
|
+
function contextRoleForAgent(agentId) {
|
|
25291
|
+
if (CONTEXT_ROLES.includes(agentId)) return agentId;
|
|
25292
|
+
for (const role of CONTEXT_ROLES) {
|
|
25293
|
+
if (agentId.startsWith(`${role}-`)) return role;
|
|
25294
|
+
}
|
|
25295
|
+
return agentId;
|
|
25296
|
+
}
|
|
25297
|
+
function dispatchContextPresent(projectRoot) {
|
|
25298
|
+
return hasTopLevelMarkdown(join12(projectRoot, ".kxm", "memory")) || hasAnyEntry(join12(projectRoot, ".kxm", "skills", "promoted"));
|
|
25299
|
+
}
|
|
25300
|
+
function hasTopLevelMarkdown(dir) {
|
|
25301
|
+
try {
|
|
25302
|
+
return readdirSync7(dir, { withFileTypes: true }).some((entry) => entry.isFile() && extname3(entry.name) === ".md");
|
|
25303
|
+
} catch {
|
|
25304
|
+
return false;
|
|
25305
|
+
}
|
|
25306
|
+
}
|
|
25307
|
+
function hasAnyEntry(dir) {
|
|
25308
|
+
try {
|
|
25309
|
+
return readdirSync7(dir).length > 0;
|
|
25310
|
+
} catch {
|
|
25311
|
+
return false;
|
|
25312
|
+
}
|
|
25313
|
+
}
|
|
25314
|
+
function gitEnvironment2() {
|
|
25315
|
+
return Object.fromEntries(Object.entries(process.env).filter(([name]) => !name.toUpperCase().startsWith("GIT_")));
|
|
25316
|
+
}
|
|
25317
|
+
function committedGateGap(projectRoot) {
|
|
25318
|
+
const result = spawnSync3("git", [
|
|
25319
|
+
"--no-optional-locks",
|
|
25320
|
+
"-C",
|
|
25321
|
+
projectRoot,
|
|
25322
|
+
"status",
|
|
25323
|
+
"--porcelain=v1",
|
|
25324
|
+
"--untracked-files=all",
|
|
25325
|
+
"--ignored=matching",
|
|
25326
|
+
"--",
|
|
25327
|
+
...DISPATCH_CONTEXT_PATHSPEC
|
|
25328
|
+
], {
|
|
25329
|
+
encoding: "utf8",
|
|
25330
|
+
env: gitEnvironment2(),
|
|
25331
|
+
timeout: GIT_STATUS_TIMEOUT_MS,
|
|
25332
|
+
windowsHide: true
|
|
25333
|
+
});
|
|
25334
|
+
if (result.error || result.status !== 0) return GAP_GIT_UNAVAILABLE;
|
|
25335
|
+
if (typeof result.stdout !== "string" || result.stdout.length > 0) return GAP_UNCOMMITTED;
|
|
25336
|
+
return void 0;
|
|
25337
|
+
}
|
|
25338
|
+
function gapId(value) {
|
|
25339
|
+
return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(value) ? value : "invalid";
|
|
25340
|
+
}
|
|
25341
|
+
function withheld(gaps) {
|
|
25342
|
+
return { present: true, pool: [], promoted: [], gaps: [...gaps], skippedUnboundScopes: 0 };
|
|
25343
|
+
}
|
|
25344
|
+
function loadDispatchContextSources(input) {
|
|
25345
|
+
try {
|
|
25346
|
+
const { projectRoot, projectId, pinnedMemoryRevision } = input;
|
|
25347
|
+
if (!dispatchContextPresent(projectRoot)) return EMPTY_DISPATCH_SOURCES;
|
|
25348
|
+
const gateGap = committedGateGap(projectRoot);
|
|
25349
|
+
if (gateGap !== void 0) return withheld([gateGap]);
|
|
25350
|
+
if (computeKxmMemoryRevision({ projectRoot }) !== pinnedMemoryRevision) return withheld([GAP_DRIFT]);
|
|
25351
|
+
const gaps = [];
|
|
25352
|
+
let records;
|
|
25353
|
+
try {
|
|
25354
|
+
records = loadAuthoredMemory(projectRoot);
|
|
25355
|
+
} catch {
|
|
25356
|
+
gaps.push(GAP_MEMORY_UNREADABLE);
|
|
25357
|
+
records = [];
|
|
25358
|
+
}
|
|
25359
|
+
const pool = [];
|
|
25360
|
+
let skippedUnboundScopes = 0;
|
|
25361
|
+
for (const record2 of records) {
|
|
25362
|
+
if (record2.scope !== "project" && record2.scope !== "operator") {
|
|
25363
|
+
skippedUnboundScopes += 1;
|
|
25364
|
+
continue;
|
|
25365
|
+
}
|
|
25366
|
+
try {
|
|
25367
|
+
pool.push(memoryRecordToContextItem(record2, projectId));
|
|
25368
|
+
} catch {
|
|
25369
|
+
gaps.push(`dispatch_context_memory_rejected:${gapId(record2.id)}`);
|
|
25370
|
+
}
|
|
25371
|
+
}
|
|
25372
|
+
const promoted = [];
|
|
25373
|
+
try {
|
|
25374
|
+
const lifecycle = new SkillLifecycle(join12(projectRoot, ".kxm", "skills"));
|
|
25375
|
+
for (const metadata of lifecycle.list("promoted")) {
|
|
25376
|
+
try {
|
|
25377
|
+
promoted.push(lifecycle.verify("promoted", metadata.id));
|
|
25378
|
+
} catch {
|
|
25379
|
+
gaps.push(`dispatch_context_skill_unverified:${gapId(metadata.id)}`);
|
|
25380
|
+
}
|
|
25381
|
+
}
|
|
25382
|
+
} catch {
|
|
25383
|
+
gaps.push(GAP_SKILLS_UNREADABLE);
|
|
25384
|
+
}
|
|
25385
|
+
if (computeKxmMemoryRevision({ projectRoot }) !== pinnedMemoryRevision) return withheld([...gaps, GAP_DRIFT]);
|
|
25386
|
+
return { present: true, pool, promoted, gaps, skippedUnboundScopes };
|
|
25387
|
+
} catch {
|
|
25388
|
+
return withheld([GAP_FAILED]);
|
|
25389
|
+
}
|
|
25390
|
+
}
|
|
25391
|
+
function assembleDispatchContext(sources, input) {
|
|
25392
|
+
const role = contextRoleForAgent(input.agentId);
|
|
25393
|
+
try {
|
|
25394
|
+
if (sources.pool.length === 0 && sources.promoted.length === 0) {
|
|
25395
|
+
return { items: [], deliveredIds: [], renderDeferred: 0, role, unresolvedGaps: [...sources.gaps] };
|
|
25396
|
+
}
|
|
25397
|
+
const task = (input.task.trim() || input.stepId).slice(0, MAX_CONTEXT_TASK_CHARS);
|
|
25398
|
+
const promoted = sources.promoted;
|
|
25399
|
+
const snapshot = {
|
|
25400
|
+
list: (state) => state === "promoted" ? [...promoted] : [],
|
|
25401
|
+
verify: (state, id) => {
|
|
25402
|
+
const found = state === "promoted" ? promoted.find((metadata) => metadata.id === id) : void 0;
|
|
25403
|
+
if (!found) throw new Error("skill is not in the verified dispatch snapshot");
|
|
25404
|
+
return found;
|
|
25405
|
+
}
|
|
25406
|
+
};
|
|
25407
|
+
const { packet, audit } = arbitrate(
|
|
25408
|
+
{
|
|
25409
|
+
project: input.projectId,
|
|
25410
|
+
role,
|
|
25411
|
+
task,
|
|
25412
|
+
workflowRunId: input.runId,
|
|
25413
|
+
stageId: input.stepId,
|
|
25414
|
+
budgetTokens: Math.min(rolePolicy(role).budgetTokens, DISPATCH_CONTEXT_BUDGET_TOKENS)
|
|
25415
|
+
},
|
|
25416
|
+
[...sources.pool],
|
|
25417
|
+
promoted.length > 0 ? { skillLifecycle: snapshot } : {}
|
|
25418
|
+
);
|
|
25419
|
+
const byId = /* @__PURE__ */ new Map();
|
|
25420
|
+
for (const section of [packet.currentState, packet.knowledge, packet.evidence, packet.episodes, packet.skills, packet.contradictions]) {
|
|
25421
|
+
for (const item of section) byId.set(item.id, item);
|
|
25422
|
+
}
|
|
25423
|
+
const items = [];
|
|
25424
|
+
let projectItems = 0;
|
|
25425
|
+
let renderDeferred = 0;
|
|
25426
|
+
for (const id of audit.selectedIds) {
|
|
25427
|
+
const item = byId.get(id);
|
|
25428
|
+
if (!item) continue;
|
|
25429
|
+
const projectRouted = item.project !== "_shared" && item.kind !== "state" && item.kind !== "episode" && item.kind !== "skill";
|
|
25430
|
+
if (projectRouted) {
|
|
25431
|
+
if (projectItems >= DISPATCH_CONTEXT_MAX_PROJECT_ITEMS) {
|
|
25432
|
+
renderDeferred += 1;
|
|
25433
|
+
continue;
|
|
25434
|
+
}
|
|
25435
|
+
projectItems += 1;
|
|
25436
|
+
}
|
|
25437
|
+
items.push(item);
|
|
25438
|
+
}
|
|
25439
|
+
return {
|
|
25440
|
+
items,
|
|
25441
|
+
deliveredIds: items.map((item) => item.id),
|
|
25442
|
+
renderDeferred,
|
|
25443
|
+
role,
|
|
25444
|
+
unresolvedGaps: [
|
|
25445
|
+
...sources.gaps,
|
|
25446
|
+
...packet.unresolvedGaps,
|
|
25447
|
+
...renderDeferred > 0 ? [`dispatch_context_render_deferred:${renderDeferred}`] : []
|
|
25448
|
+
],
|
|
25449
|
+
audit
|
|
25450
|
+
};
|
|
25451
|
+
} catch {
|
|
25452
|
+
return { items: [], deliveredIds: [], renderDeferred: 0, role, unresolvedGaps: [...sources.gaps, GAP_FAILED] };
|
|
25453
|
+
}
|
|
25454
|
+
}
|
|
25455
|
+
|
|
24069
25456
|
// plugins/kxm/src/engine-artifacts.ts
|
|
24070
|
-
import { join as
|
|
25457
|
+
import { join as join13 } from "node:path";
|
|
24071
25458
|
|
|
24072
25459
|
// plugins/kxm/src/artifacts-exist.ts
|
|
24073
|
-
import { lstatSync as lstatSync4, realpathSync as realpathSync3, statSync as
|
|
24074
|
-
import { isAbsolute as isAbsolute3, relative as relative3, resolve as
|
|
25460
|
+
import { lstatSync as lstatSync4, realpathSync as realpathSync3, statSync as statSync3 } from "node:fs";
|
|
25461
|
+
import { isAbsolute as isAbsolute3, relative as relative3, resolve as resolve6 } from "node:path";
|
|
24075
25462
|
function staysUnder(root, candidate) {
|
|
24076
25463
|
const child = relative3(root, candidate);
|
|
24077
25464
|
return child === "" || !isAbsolute3(child) && child !== ".." && !child.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`);
|
|
24078
25465
|
}
|
|
24079
25466
|
function verifyArtifactExists(rootInput, pathInput) {
|
|
24080
|
-
const lexicalRoot =
|
|
24081
|
-
const lexicalPath =
|
|
25467
|
+
const lexicalRoot = resolve6(rootInput);
|
|
25468
|
+
const lexicalPath = resolve6(pathInput);
|
|
24082
25469
|
if (!staysUnder(lexicalRoot, lexicalPath)) {
|
|
24083
25470
|
return { ok: false, error: "artifact_outside_workspace_assets", path: lexicalPath };
|
|
24084
25471
|
}
|
|
@@ -24102,7 +25489,7 @@ function verifyArtifactExists(rootInput, pathInput) {
|
|
|
24102
25489
|
if (link.isSymbolicLink()) {
|
|
24103
25490
|
return { ok: false, error: "artifact_outside_workspace_assets", path: lexicalPath };
|
|
24104
25491
|
}
|
|
24105
|
-
const artifact =
|
|
25492
|
+
const artifact = statSync3(realPath);
|
|
24106
25493
|
if (!artifact.isFile()) return { ok: false, error: "artifact_not_file", path: lexicalPath };
|
|
24107
25494
|
if (artifact.size < 1) return { ok: false, error: "artifact_empty", path: lexicalPath };
|
|
24108
25495
|
return { ok: true, path: realPath, bytes: artifact.size };
|
|
@@ -24114,14 +25501,14 @@ function verifyArtifactExists(rootInput, pathInput) {
|
|
|
24114
25501
|
// plugins/kxm/src/engine-artifacts.ts
|
|
24115
25502
|
var kxmArtifactsGateSeams = {};
|
|
24116
25503
|
function evaluateArtifactsGate(context, definition) {
|
|
24117
|
-
const root =
|
|
25504
|
+
const root = join13(context.projectRoot, ".kxm", "assets");
|
|
24118
25505
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
24119
25506
|
const startTime = process.hrtime.bigint();
|
|
24120
25507
|
let checkedCount = 0;
|
|
24121
25508
|
let failedCount = 0;
|
|
24122
25509
|
for (const [index, path] of definition.paths.entries()) {
|
|
24123
25510
|
checkedCount += 1;
|
|
24124
|
-
const result = verifyArtifactExists(root,
|
|
25511
|
+
const result = verifyArtifactExists(root, join13(root, path));
|
|
24125
25512
|
if (!result.ok) failedCount += 1;
|
|
24126
25513
|
kxmArtifactsGateSeams.afterPathChecked?.({ path, index, checkedCount, failedCount });
|
|
24127
25514
|
}
|
|
@@ -24154,7 +25541,7 @@ function evaluateArtifactsGate(context, definition) {
|
|
|
24154
25541
|
|
|
24155
25542
|
// plugins/kxm/src/engine-command.ts
|
|
24156
25543
|
import { spawn as spawn2 } from "node:child_process";
|
|
24157
|
-
import { createHash as
|
|
25544
|
+
import { createHash as createHash11 } from "node:crypto";
|
|
24158
25545
|
|
|
24159
25546
|
// plugins/kxm/src/safety-integrity.ts
|
|
24160
25547
|
var DESTRUCTIVE_COMMAND_PATTERNS = Object.freeze([
|
|
@@ -24202,7 +25589,7 @@ function createCommandObserver(input) {
|
|
|
24202
25589
|
return new CommandObserver(input);
|
|
24203
25590
|
}
|
|
24204
25591
|
var StreamDigest = class {
|
|
24205
|
-
hash =
|
|
25592
|
+
hash = createHash11("sha256");
|
|
24206
25593
|
bytes = 0;
|
|
24207
25594
|
complete = 0;
|
|
24208
25595
|
digest() {
|
|
@@ -24277,8 +25664,8 @@ var CommandObserver = class {
|
|
|
24277
25664
|
this.requestStop("error", "lost-close");
|
|
24278
25665
|
}
|
|
24279
25666
|
run() {
|
|
24280
|
-
return new Promise((
|
|
24281
|
-
this.resolveOutcome =
|
|
25667
|
+
return new Promise((resolve11) => {
|
|
25668
|
+
this.resolveOutcome = resolve11;
|
|
24282
25669
|
try {
|
|
24283
25670
|
this.start();
|
|
24284
25671
|
} catch (error) {
|
|
@@ -25049,7 +26436,7 @@ function hashFromStore(context, runId) {
|
|
|
25049
26436
|
}
|
|
25050
26437
|
|
|
25051
26438
|
// plugins/kxm/src/routing.ts
|
|
25052
|
-
import { createHash as
|
|
26439
|
+
import { createHash as createHash12 } from "node:crypto";
|
|
25053
26440
|
var ROUTING_RECORD_V2_SCHEMA = "kxm.routing-record.v2";
|
|
25054
26441
|
var BEHAVIORAL_HASH_VERSION = 1;
|
|
25055
26442
|
var MAX_PROVIDER_METADATA_FIELDS = 32;
|
|
@@ -25072,7 +26459,7 @@ function behavioralConfigHash(input) {
|
|
|
25072
26459
|
workflowDefinitionSha256: input.workflowDefinitionSha256?.trim() ?? null,
|
|
25073
26460
|
verifierConfigSha256: input.verifierConfigSha256?.trim() ?? null
|
|
25074
26461
|
};
|
|
25075
|
-
return
|
|
26462
|
+
return createHash12("sha256").update(JSON.stringify(canonical), "utf8").digest("hex");
|
|
25076
26463
|
}
|
|
25077
26464
|
function boundedInt(value, field) {
|
|
25078
26465
|
if (value === void 0 || value === null) return void 0;
|
|
@@ -25203,6 +26590,13 @@ function parseRoutingRecordV2(value) {
|
|
|
25203
26590
|
}
|
|
25204
26591
|
return record2;
|
|
25205
26592
|
}
|
|
26593
|
+
function computeDecayedWeight(recordedAt, halfLifeDays = 14, now = Date.now()) {
|
|
26594
|
+
const ts = typeof recordedAt === "number" ? recordedAt : Date.parse(recordedAt);
|
|
26595
|
+
if (!Number.isFinite(ts)) return 1;
|
|
26596
|
+
const deltaMs = Math.max(0, now - ts);
|
|
26597
|
+
const halfLifeMs = halfLifeDays * 24 * 60 * 60 * 1e3;
|
|
26598
|
+
return Math.pow(2, -deltaMs / halfLifeMs);
|
|
26599
|
+
}
|
|
25206
26600
|
|
|
25207
26601
|
// plugins/kxm/src/engine.ts
|
|
25208
26602
|
var trustedProducers = /* @__PURE__ */ new WeakSet();
|
|
@@ -25241,7 +26635,7 @@ function requireTrustedProducer(producer) {
|
|
|
25241
26635
|
}
|
|
25242
26636
|
}
|
|
25243
26637
|
function hashCapabilitySecret(secret) {
|
|
25244
|
-
return `sha256:${
|
|
26638
|
+
return `sha256:${createHash13("sha256").update(`${CAPABILITY_PREFIX}${secret}`, "utf8").digest("hex")}`;
|
|
25245
26639
|
}
|
|
25246
26640
|
function mintKxmCapabilitySecret() {
|
|
25247
26641
|
const secret = `kxmcap_${randomBytes2(32).toString("base64url")}`;
|
|
@@ -25370,7 +26764,7 @@ async function driveAdmitted(context, runId, producer, token, options = {}) {
|
|
|
25370
26764
|
return latest;
|
|
25371
26765
|
}
|
|
25372
26766
|
function newDriveId(runId, token, runtimeId, monotonicNs) {
|
|
25373
|
-
return `drv_${
|
|
26767
|
+
return `drv_${createHash13("sha256").update(`${runId}\0${token}\0${runtimeId}\0${monotonicNs}`, "utf8").digest("hex").slice(0, 24)}`;
|
|
25374
26768
|
}
|
|
25375
26769
|
function driveErrorSummary(error) {
|
|
25376
26770
|
const raw = error instanceof KxmConfigError ? error.issues[0]?.code : void 0;
|
|
@@ -25597,8 +26991,8 @@ var KxmRunScheduler = class _KxmRunScheduler {
|
|
|
25597
26991
|
resolve: (_value) => void 0,
|
|
25598
26992
|
reject: (_error) => void 0
|
|
25599
26993
|
};
|
|
25600
|
-
const settled = new Promise((
|
|
25601
|
-
pending.resolve =
|
|
26994
|
+
const settled = new Promise((resolve11, reject) => {
|
|
26995
|
+
pending.resolve = resolve11;
|
|
25602
26996
|
pending.reject = reject;
|
|
25603
26997
|
});
|
|
25604
26998
|
void settled.then(void 0, () => void 0);
|
|
@@ -25712,8 +27106,28 @@ function invokeProducer(producer, request) {
|
|
|
25712
27106
|
);
|
|
25713
27107
|
}
|
|
25714
27108
|
var kxmGateDispatchSeams = {};
|
|
27109
|
+
function peekDispatchContextSources(context, runId) {
|
|
27110
|
+
if (!dispatchContextPresent(context.projectRoot)) return EMPTY_DISPATCH_SOURCES;
|
|
27111
|
+
let run;
|
|
27112
|
+
try {
|
|
27113
|
+
run = requireRun(context, runId);
|
|
27114
|
+
const state = foldStoredKxmRun(context, run);
|
|
27115
|
+
const plan = rehydrateKxmCompiledPlanFromStore(context.eventStore, run);
|
|
27116
|
+
if (state.status !== "running" || state.currentStep) return NOT_LOADED_DISPATCH_SOURCES;
|
|
27117
|
+
const next = plan.steps[state.pendingStepId ?? plan.entryStepId];
|
|
27118
|
+
if (next?.kind !== "agent" && next?.kind !== "moa") return NOT_LOADED_DISPATCH_SOURCES;
|
|
27119
|
+
} catch {
|
|
27120
|
+
return NOT_LOADED_DISPATCH_SOURCES;
|
|
27121
|
+
}
|
|
27122
|
+
return loadDispatchContextSources({
|
|
27123
|
+
projectRoot: context.projectRoot,
|
|
27124
|
+
projectId: run.projectId,
|
|
27125
|
+
pinnedMemoryRevision: run.memoryRevision
|
|
27126
|
+
});
|
|
27127
|
+
}
|
|
25715
27128
|
async function stepLocked(context, runId, producer, token) {
|
|
25716
|
-
const
|
|
27129
|
+
const dispatchSources = peekDispatchContextSources(context, runId);
|
|
27130
|
+
const prepared = context.eventStore.transaction(() => prepareDispatch(context, runId, producer.id, dispatchSources));
|
|
25717
27131
|
if (prepared.kind === "return") {
|
|
25718
27132
|
return prepared.handoff ? { state: prepared.state, handoff: prepared.handoff } : { state: prepared.state };
|
|
25719
27133
|
}
|
|
@@ -25976,10 +27390,10 @@ function unreconciledPanelAttemptId(state) {
|
|
|
25976
27390
|
}
|
|
25977
27391
|
function resolveProducerRoute(projectRoot, step, agentId) {
|
|
25978
27392
|
let agentModel;
|
|
25979
|
-
const agentFile =
|
|
25980
|
-
if (
|
|
27393
|
+
const agentFile = join14(projectRoot, ".kxm", "agents", `${agentId}.yaml`);
|
|
27394
|
+
if (existsSync11(agentFile)) {
|
|
25981
27395
|
try {
|
|
25982
|
-
const parsed = (0,
|
|
27396
|
+
const parsed = (0, import_yaml7.parse)(readFileSync9(agentFile, "utf8"));
|
|
25983
27397
|
if (typeof parsed?.model === "string") {
|
|
25984
27398
|
agentModel = parsed.model;
|
|
25985
27399
|
} else if (parsed?.model && typeof parsed.model === "object" && !Array.isArray(parsed.model)) {
|
|
@@ -26051,7 +27465,7 @@ function resolveProducerRoute(projectRoot, step, agentId) {
|
|
|
26051
27465
|
const model = selector.slice(slash + 1);
|
|
26052
27466
|
return { provider, model, selector };
|
|
26053
27467
|
}
|
|
26054
|
-
function prepareDispatch(context, runId, producerId) {
|
|
27468
|
+
function prepareDispatch(context, runId, producerId, dispatchSources) {
|
|
26055
27469
|
const run = requireRun(context, runId);
|
|
26056
27470
|
const plan = rehydrateKxmCompiledPlanFromStore(context.eventStore, run);
|
|
26057
27471
|
const state = foldStoredKxmRun(context, run);
|
|
@@ -26221,7 +27635,8 @@ function prepareDispatch(context, runId, producerId) {
|
|
|
26221
27635
|
stepAttempt,
|
|
26222
27636
|
enterRunning: true,
|
|
26223
27637
|
producerId,
|
|
26224
|
-
resolvedRoute
|
|
27638
|
+
resolvedRoute,
|
|
27639
|
+
dispatchSources
|
|
26225
27640
|
});
|
|
26226
27641
|
return {
|
|
26227
27642
|
kind: "panel",
|
|
@@ -26234,7 +27649,8 @@ function prepareDispatch(context, runId, producerId) {
|
|
|
26234
27649
|
target: step.assignments.target,
|
|
26235
27650
|
maxParallel: step.assignments.maxParallel,
|
|
26236
27651
|
first,
|
|
26237
|
-
state: first.state
|
|
27652
|
+
state: first.state,
|
|
27653
|
+
dispatchSources
|
|
26238
27654
|
}
|
|
26239
27655
|
};
|
|
26240
27656
|
}
|
|
@@ -26273,7 +27689,7 @@ function birthMember(context, input) {
|
|
|
26273
27689
|
if (promptText === void 0) {
|
|
26274
27690
|
throw runtimeError("run_prompt_mismatch", run.runId, "prompt text missing from accepted run prompt store");
|
|
26275
27691
|
}
|
|
26276
|
-
const promptHash =
|
|
27692
|
+
const promptHash = createHash13("sha256").update(promptText, "utf8").digest("hex");
|
|
26277
27693
|
const expectedHash = run.promptSha256.replace(/^sha256:/, "");
|
|
26278
27694
|
if (promptHash !== expectedHash) {
|
|
26279
27695
|
throw runtimeError("run_prompt_mismatch", run.runId, "prompt text does not match accepted promptSha256");
|
|
@@ -26288,6 +27704,13 @@ function birthMember(context, input) {
|
|
|
26288
27704
|
resolvedRoute = routeResult;
|
|
26289
27705
|
}
|
|
26290
27706
|
}
|
|
27707
|
+
const dispatchContext = assembleDispatchContext(input.dispatchSources, {
|
|
27708
|
+
projectId: run.projectId,
|
|
27709
|
+
runId: run.runId,
|
|
27710
|
+
stepId: input.stepId,
|
|
27711
|
+
agentId,
|
|
27712
|
+
task: [input.step.instructions, promptText].filter((part) => typeof part === "string" && part.trim().length > 0).join("\n\n")
|
|
27713
|
+
});
|
|
26291
27714
|
const assignmentId = newKxmAssignmentId();
|
|
26292
27715
|
const attemptId = newKxmAttemptId();
|
|
26293
27716
|
const minted = mintCapabilitySecret();
|
|
@@ -26354,7 +27777,9 @@ function birthMember(context, input) {
|
|
|
26354
27777
|
activeStepIndex: input.step.index,
|
|
26355
27778
|
totalSteps: input.plan.order.length,
|
|
26356
27779
|
settledDecisions: []
|
|
26357
|
-
}
|
|
27780
|
+
},
|
|
27781
|
+
...dispatchContext.items.length > 0 ? { arbitratedItems: dispatchContext.items } : {},
|
|
27782
|
+
...dispatchContext.unresolvedGaps.length > 0 ? { unresolvedGaps: dispatchContext.unresolvedGaps } : {}
|
|
26358
27783
|
});
|
|
26359
27784
|
const { packet: contextPacket } = pruneContextPacket(rawContextPacket);
|
|
26360
27785
|
const generatedPrompt = formatContextPacketForPrompt(contextPacket);
|
|
@@ -26391,6 +27816,26 @@ ${generatedPrompt}` : generatedPrompt,
|
|
|
26391
27816
|
controller,
|
|
26392
27817
|
state: next
|
|
26393
27818
|
};
|
|
27819
|
+
if (input.dispatchSources.present && context.logger) {
|
|
27820
|
+
try {
|
|
27821
|
+
context.logger({
|
|
27822
|
+
event: "dispatch_context_assembled",
|
|
27823
|
+
runId: run.runId,
|
|
27824
|
+
stepId: input.stepId,
|
|
27825
|
+
stepAttempt: input.stepAttempt,
|
|
27826
|
+
agentId,
|
|
27827
|
+
role: dispatchContext.role,
|
|
27828
|
+
deliveredIds: dispatchContext.deliveredIds,
|
|
27829
|
+
renderDeferred: dispatchContext.renderDeferred,
|
|
27830
|
+
provenanceSummary: dispatchContext.audit?.provenanceSummary ?? {},
|
|
27831
|
+
estimatedTokens: dispatchContext.audit?.estimatedTokens ?? 0,
|
|
27832
|
+
budgetTokens: dispatchContext.audit?.budgetTokens ?? null,
|
|
27833
|
+
skippedUnboundScopes: input.dispatchSources.skippedUnboundScopes,
|
|
27834
|
+
unresolvedGaps: dispatchContext.unresolvedGaps
|
|
27835
|
+
});
|
|
27836
|
+
} catch {
|
|
27837
|
+
}
|
|
27838
|
+
}
|
|
26394
27839
|
kxmPanelDispatchSeams.afterBirth?.(member);
|
|
26395
27840
|
return member;
|
|
26396
27841
|
}
|
|
@@ -26512,7 +27957,8 @@ async function drivePanel(context, panel, producer) {
|
|
|
26512
27957
|
step: panel.step,
|
|
26513
27958
|
stepId: panel.stepId,
|
|
26514
27959
|
stepAttempt: panel.stepAttempt,
|
|
26515
|
-
producerId: producer.id
|
|
27960
|
+
producerId: producer.id,
|
|
27961
|
+
dispatchSources: panel.dispatchSources
|
|
26516
27962
|
});
|
|
26517
27963
|
});
|
|
26518
27964
|
};
|
|
@@ -26640,7 +28086,29 @@ async function drivePanel(context, panel, producer) {
|
|
|
26640
28086
|
}
|
|
26641
28087
|
}
|
|
26642
28088
|
}
|
|
26643
|
-
|
|
28089
|
+
var ENGINE_ROUTING_METADATA_KEYS = /* @__PURE__ */ new Set(["workflowId", "askSha256", "objectiveSha256", "stepWrites"]);
|
|
28090
|
+
var MAX_PRODUCER_ROUTING_METADATA_FIELDS = MAX_PROVIDER_METADATA_FIELDS - ENGINE_ROUTING_METADATA_KEYS.size;
|
|
28091
|
+
function engineRoutingMetadata(dispatch, producer) {
|
|
28092
|
+
const metadata = {};
|
|
28093
|
+
if (producer !== void 0 && producer !== null) {
|
|
28094
|
+
if (typeof producer !== "object" || Array.isArray(producer)) {
|
|
28095
|
+
throw new Error("routing providerMetadata must be an object");
|
|
28096
|
+
}
|
|
28097
|
+
let kept = 0;
|
|
28098
|
+
for (const [key, value] of Object.entries(producer)) {
|
|
28099
|
+
if (kept >= MAX_PRODUCER_ROUTING_METADATA_FIELDS) break;
|
|
28100
|
+
if (ENGINE_ROUTING_METADATA_KEYS.has(key)) continue;
|
|
28101
|
+
metadata[key] = value;
|
|
28102
|
+
kept += 1;
|
|
28103
|
+
}
|
|
28104
|
+
}
|
|
28105
|
+
metadata.workflowId = dispatch.plan.workflowId;
|
|
28106
|
+
metadata.askSha256 = kxmStepAskSha256(dispatch.plan, dispatch.stepId, dispatch.agentId);
|
|
28107
|
+
metadata.objectiveSha256 = dispatch.run.promptSha256;
|
|
28108
|
+
metadata.stepWrites = Object.values(dispatch.step.repositories).some((access) => access === "write");
|
|
28109
|
+
return metadata;
|
|
28110
|
+
}
|
|
28111
|
+
function producerRoutingRecord(context, dispatch, result, now, finalOutcome) {
|
|
26644
28112
|
const run = requireRun(context, dispatch.run.runId);
|
|
26645
28113
|
if (result.costBasis === void 0 || result.costBasis === null) {
|
|
26646
28114
|
throw runtimeError("settle_missing_cost_basis", run.runId, `attempt settlement rejected: missing required costBasis for attempt ${dispatch.attemptId}`);
|
|
@@ -26677,9 +28145,12 @@ function producerRoutingRecord(context, dispatch, result, now) {
|
|
|
26677
28145
|
retries: Math.max(0, dispatch.stepAttempt - 1),
|
|
26678
28146
|
thinking: result.thinking ?? dispatch.request.thinking
|
|
26679
28147
|
};
|
|
26680
|
-
for (const field of ["
|
|
28148
|
+
for (const field of ["contextTokens", "tokensIn", "tokensOut", "cacheReadTokens", "cacheWriteTokens", "priceRef"]) {
|
|
26681
28149
|
if (result[field] !== void 0) record2[field] = result[field];
|
|
26682
28150
|
}
|
|
28151
|
+
record2.agentRole = result.agentRole ?? dispatch.agentId;
|
|
28152
|
+
record2.providerMetadata = engineRoutingMetadata(dispatch, result.providerMetadata);
|
|
28153
|
+
if (finalOutcome !== void 0) record2.finalOutcome = finalOutcome;
|
|
26683
28154
|
return parseRoutingRecordV2(record2);
|
|
26684
28155
|
}
|
|
26685
28156
|
function settleMember(context, dispatch, result, produceError, produceErrorCode) {
|
|
@@ -26744,7 +28215,10 @@ function settleMember(context, dispatch, result, produceError, produceErrorCode)
|
|
|
26744
28215
|
latencyMs: typeof result?.latencyMs === "number" ? result.latencyMs : 0,
|
|
26745
28216
|
costBasis: "unknown",
|
|
26746
28217
|
costUsd: null,
|
|
26747
|
-
retries: Math.max(0, dispatch.stepAttempt - 1)
|
|
28218
|
+
retries: Math.max(0, dispatch.stepAttempt - 1),
|
|
28219
|
+
agentRole: dispatch.agentId,
|
|
28220
|
+
finalOutcome: "failed",
|
|
28221
|
+
providerMetadata: engineRoutingMetadata(dispatch)
|
|
26748
28222
|
};
|
|
26749
28223
|
push("routing.attempt.recorded", { routing: parseRoutingRecordV2(routingRecord) });
|
|
26750
28224
|
push("assignment.result_recorded", {
|
|
@@ -26759,9 +28233,17 @@ function settleMember(context, dispatch, result, produceError, produceErrorCode)
|
|
|
26759
28233
|
if (!result) {
|
|
26760
28234
|
throw runtimeError("settle_missing_cost_basis", run.runId, `attempt settlement rejected: missing required costBasis for attempt ${dispatch.attemptId}`);
|
|
26761
28235
|
}
|
|
26762
|
-
push("routing.attempt.recorded", { routing: producerRoutingRecord(context, dispatch, result, now) });
|
|
26763
28236
|
const outcome = typeof result.outcome === "string" ? result.outcome : void 0;
|
|
26764
28237
|
const known = outcome !== void 0 && dispatch.step.outcomes.includes(outcome);
|
|
28238
|
+
push("routing.attempt.recorded", {
|
|
28239
|
+
routing: producerRoutingRecord(
|
|
28240
|
+
context,
|
|
28241
|
+
dispatch,
|
|
28242
|
+
result,
|
|
28243
|
+
now,
|
|
28244
|
+
known ? kxmAttemptFinalOutcome(dispatch.step, { resultClass: "outcome", outcome }) : "failed"
|
|
28245
|
+
)
|
|
28246
|
+
});
|
|
26765
28247
|
if (!known) {
|
|
26766
28248
|
push("assignment.result_recorded", {
|
|
26767
28249
|
assignmentId: dispatch.assignmentId,
|
|
@@ -27708,7 +29190,7 @@ Return a final JSON object with an "outcome" field chosen from ${JSON.stringify(
|
|
|
27708
29190
|
}
|
|
27709
29191
|
const spawnFn = options.spawnProcess ?? defaultSpawn;
|
|
27710
29192
|
const cwd = options.projectRoot ?? process.cwd();
|
|
27711
|
-
const evidence = await beginOneShotEvidence(options.evidenceRoot ??
|
|
29193
|
+
const evidence = await beginOneShotEvidence(options.evidenceRoot ?? join15(kxmUserStateRoot(), "runtime", "oneshot-evidence"), {
|
|
27712
29194
|
runId: request.runId,
|
|
27713
29195
|
stepId: request.stepId,
|
|
27714
29196
|
attemptId: request.attemptId,
|
|
@@ -27853,20 +29335,6 @@ Return a final JSON object with an "outcome" field chosen from ${JSON.stringify(
|
|
|
27853
29335
|
|
|
27854
29336
|
// plugins/kxm/src/client.ts
|
|
27855
29337
|
import { hostname } from "node:os";
|
|
27856
|
-
|
|
27857
|
-
// plugins/kxm/src/protocol.ts
|
|
27858
|
-
var DEFAULT_MESSAGE_TTL_MS = 24 * 60 * 6e4;
|
|
27859
|
-
var MAX_MESSAGE_TTL_MS = 7 * 24 * 60 * 6e4;
|
|
27860
|
-
var DEFAULT_MESSAGE_RETENTION_MS = 7 * 24 * 60 * 6e4;
|
|
27861
|
-
var MAX_BODY_BYTES = 256 * 1024;
|
|
27862
|
-
var MAX_AGENT_HOST_CHARS = 64;
|
|
27863
|
-
var MAX_LEASE_TTL_MS = 10 * 6e4;
|
|
27864
|
-
var DEFAULT_LEASE_TTL_MS = 5 * 6e4;
|
|
27865
|
-
function nowIso() {
|
|
27866
|
-
return (/* @__PURE__ */ new Date()).toISOString();
|
|
27867
|
-
}
|
|
27868
|
-
|
|
27869
|
-
// plugins/kxm/src/client.ts
|
|
27870
29338
|
function defaultHostLabel() {
|
|
27871
29339
|
try {
|
|
27872
29340
|
return hostname().trim().slice(0, MAX_AGENT_HOST_CHARS) || void 0;
|
|
@@ -27957,9 +29425,9 @@ var RuntimeHubClient = class {
|
|
|
27957
29425
|
};
|
|
27958
29426
|
|
|
27959
29427
|
// plugins/kxm/src/hub-binding.ts
|
|
27960
|
-
import { existsSync as
|
|
29428
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync6, readFileSync as readFileSync10, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "node:fs";
|
|
27961
29429
|
import { homedir as homedir2 } from "node:os";
|
|
27962
|
-
import { dirname as
|
|
29430
|
+
import { dirname as dirname8, isAbsolute as isAbsolute4, join as join16, resolve as resolve7 } from "node:path";
|
|
27963
29431
|
var HUB_BINDING_SCHEMA = "kxm.hub-binding.v1";
|
|
27964
29432
|
var HubBindingError = class extends Error {
|
|
27965
29433
|
constructor(message) {
|
|
@@ -27971,20 +29439,20 @@ function resolveUserStateRoot(env) {
|
|
|
27971
29439
|
const explicit = env.KXM_STATE_HOME?.trim();
|
|
27972
29440
|
if (explicit) {
|
|
27973
29441
|
if (!isAbsolute4(explicit)) throw new HubBindingError("local_state_root_not_absolute");
|
|
27974
|
-
return
|
|
29442
|
+
return resolve7(explicit);
|
|
27975
29443
|
}
|
|
27976
29444
|
if (process.platform === "win32") {
|
|
27977
29445
|
const localAppData = env.LOCALAPPDATA?.trim();
|
|
27978
|
-
const base2 = localAppData && isAbsolute4(localAppData) ? localAppData :
|
|
27979
|
-
return
|
|
29446
|
+
const base2 = localAppData && isAbsolute4(localAppData) ? localAppData : join16(homedir2(), "AppData", "Local");
|
|
29447
|
+
return resolve7(base2, "KXM");
|
|
27980
29448
|
}
|
|
27981
|
-
if (process.platform === "darwin") return
|
|
29449
|
+
if (process.platform === "darwin") return resolve7(homedir2(), "Library", "Application Support", "KXM");
|
|
27982
29450
|
const xdgState = env.XDG_STATE_HOME?.trim();
|
|
27983
|
-
const base = xdgState && isAbsolute4(xdgState) ? xdgState :
|
|
27984
|
-
return
|
|
29451
|
+
const base = xdgState && isAbsolute4(xdgState) ? xdgState : join16(homedir2(), ".local", "state");
|
|
29452
|
+
return resolve7(base, "kxm");
|
|
27985
29453
|
}
|
|
27986
29454
|
function hubBindingFile(env = process.env) {
|
|
27987
|
-
return
|
|
29455
|
+
return join16(resolveUserStateRoot(env), "hub-binding.json");
|
|
27988
29456
|
}
|
|
27989
29457
|
function validateHubUrl(raw) {
|
|
27990
29458
|
let parsed;
|
|
@@ -28004,10 +29472,10 @@ function isIsoTimestamp(value) {
|
|
|
28004
29472
|
}
|
|
28005
29473
|
function readHubBinding(env = process.env) {
|
|
28006
29474
|
const file = hubBindingFile(env);
|
|
28007
|
-
if (!
|
|
29475
|
+
if (!existsSync12(file)) return void 0;
|
|
28008
29476
|
let parsed;
|
|
28009
29477
|
try {
|
|
28010
|
-
parsed = JSON.parse(
|
|
29478
|
+
parsed = JSON.parse(readFileSync10(file, "utf8"));
|
|
28011
29479
|
} catch {
|
|
28012
29480
|
throw new HubBindingError(`malformed hub binding at ${file}`);
|
|
28013
29481
|
}
|
|
@@ -28029,9 +29497,9 @@ function readHubBinding(env = process.env) {
|
|
|
28029
29497
|
}
|
|
28030
29498
|
|
|
28031
29499
|
// plugins/kxm/src/hub-env.ts
|
|
28032
|
-
import { existsSync as
|
|
29500
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync7, readFileSync as readFileSync11, renameSync as renameSync3, rmSync as rmSync3, writeFileSync as writeFileSync7 } from "node:fs";
|
|
28033
29501
|
import { homedir as homedir3 } from "node:os";
|
|
28034
|
-
import { dirname as
|
|
29502
|
+
import { dirname as dirname9, isAbsolute as isAbsolute5, join as join17, resolve as resolve8 } from "node:path";
|
|
28035
29503
|
var HUB_ENV_SCHEMA = "kxm.hub-env.v1";
|
|
28036
29504
|
var HubEnvError = class extends Error {
|
|
28037
29505
|
constructor(message) {
|
|
@@ -28043,27 +29511,27 @@ function resolveUserStateRoot2(env) {
|
|
|
28043
29511
|
const explicit = env.KXM_STATE_HOME?.trim();
|
|
28044
29512
|
if (explicit) {
|
|
28045
29513
|
if (!isAbsolute5(explicit)) throw new HubEnvError("local_state_root_not_absolute");
|
|
28046
|
-
return
|
|
29514
|
+
return resolve8(explicit);
|
|
28047
29515
|
}
|
|
28048
29516
|
if (process.platform === "win32") {
|
|
28049
29517
|
const localAppData = env.LOCALAPPDATA?.trim();
|
|
28050
|
-
const base2 = localAppData && isAbsolute5(localAppData) ? localAppData :
|
|
28051
|
-
return
|
|
29518
|
+
const base2 = localAppData && isAbsolute5(localAppData) ? localAppData : join17(homedir3(), "AppData", "Local");
|
|
29519
|
+
return resolve8(base2, "KXM");
|
|
28052
29520
|
}
|
|
28053
|
-
if (process.platform === "darwin") return
|
|
29521
|
+
if (process.platform === "darwin") return resolve8(homedir3(), "Library", "Application Support", "KXM");
|
|
28054
29522
|
const xdgState = env.XDG_STATE_HOME?.trim();
|
|
28055
|
-
const base = xdgState && isAbsolute5(xdgState) ? xdgState :
|
|
28056
|
-
return
|
|
29523
|
+
const base = xdgState && isAbsolute5(xdgState) ? xdgState : join17(homedir3(), ".local", "state");
|
|
29524
|
+
return resolve8(base, "kxm");
|
|
28057
29525
|
}
|
|
28058
29526
|
function hubEnvFile(env = process.env) {
|
|
28059
|
-
return
|
|
29527
|
+
return join17(resolveUserStateRoot2(env), "hub-env.json");
|
|
28060
29528
|
}
|
|
28061
29529
|
function readHubEnvRecord(env = process.env) {
|
|
28062
29530
|
const file = hubEnvFile(env);
|
|
28063
|
-
if (!
|
|
29531
|
+
if (!existsSync13(file)) return void 0;
|
|
28064
29532
|
let parsed;
|
|
28065
29533
|
try {
|
|
28066
|
-
parsed = JSON.parse(
|
|
29534
|
+
parsed = JSON.parse(readFileSync11(file, "utf8"));
|
|
28067
29535
|
} catch (error) {
|
|
28068
29536
|
throw new HubEnvError(`hub env file is malformed at ${file}: ${error instanceof Error ? error.message : String(error)}`);
|
|
28069
29537
|
}
|
|
@@ -28103,8 +29571,8 @@ function resolveClientHubAuthToken(env, project) {
|
|
|
28103
29571
|
}
|
|
28104
29572
|
|
|
28105
29573
|
// plugins/kxm/src/logger.ts
|
|
28106
|
-
import { appendFileSync, existsSync as
|
|
28107
|
-
import { dirname as
|
|
29574
|
+
import { appendFileSync, existsSync as existsSync14, mkdirSync as mkdirSync8, renameSync as renameSync4, statSync as statSync4, unlinkSync as unlinkSync2 } from "node:fs";
|
|
29575
|
+
import { dirname as dirname10 } from "node:path";
|
|
28108
29576
|
var LOG_LEVEL_PRIORITY = {
|
|
28109
29577
|
debug: 10,
|
|
28110
29578
|
info: 20,
|
|
@@ -28141,7 +29609,7 @@ function redactLogValue(val, key) {
|
|
|
28141
29609
|
function rotateLogFiles(filePath, maxFiles) {
|
|
28142
29610
|
for (let i = maxFiles; i >= 1; i--) {
|
|
28143
29611
|
const current = `${filePath}.${i}`;
|
|
28144
|
-
if (
|
|
29612
|
+
if (existsSync14(current)) {
|
|
28145
29613
|
if (i >= maxFiles) {
|
|
28146
29614
|
try {
|
|
28147
29615
|
unlinkSync2(current);
|
|
@@ -28149,15 +29617,15 @@ function rotateLogFiles(filePath, maxFiles) {
|
|
|
28149
29617
|
}
|
|
28150
29618
|
} else {
|
|
28151
29619
|
try {
|
|
28152
|
-
|
|
29620
|
+
renameSync4(current, `${filePath}.${i + 1}`);
|
|
28153
29621
|
} catch {
|
|
28154
29622
|
}
|
|
28155
29623
|
}
|
|
28156
29624
|
}
|
|
28157
29625
|
}
|
|
28158
|
-
if (
|
|
29626
|
+
if (existsSync14(filePath)) {
|
|
28159
29627
|
try {
|
|
28160
|
-
|
|
29628
|
+
renameSync4(filePath, `${filePath}.1`);
|
|
28161
29629
|
} catch {
|
|
28162
29630
|
}
|
|
28163
29631
|
}
|
|
@@ -28172,9 +29640,9 @@ function createLogger(options) {
|
|
|
28172
29640
|
const shouldStdout = options.stdout ?? !isDaemon;
|
|
28173
29641
|
const correlationDefaults = options.correlation ?? {};
|
|
28174
29642
|
let currentSize = 0;
|
|
28175
|
-
if (filePath &&
|
|
29643
|
+
if (filePath && existsSync14(filePath)) {
|
|
28176
29644
|
try {
|
|
28177
|
-
currentSize =
|
|
29645
|
+
currentSize = statSync4(filePath).size;
|
|
28178
29646
|
} catch {
|
|
28179
29647
|
currentSize = 0;
|
|
28180
29648
|
}
|
|
@@ -28210,7 +29678,7 @@ function createLogger(options) {
|
|
|
28210
29678
|
currentSize = 0;
|
|
28211
29679
|
}
|
|
28212
29680
|
try {
|
|
28213
|
-
|
|
29681
|
+
mkdirSync8(dirname10(filePath), { recursive: true });
|
|
28214
29682
|
appendFileSync(filePath, line, { encoding: "utf8", mode: 384 });
|
|
28215
29683
|
currentSize += lineBytes;
|
|
28216
29684
|
} catch {
|
|
@@ -28253,26 +29721,26 @@ function createLogger(options) {
|
|
|
28253
29721
|
|
|
28254
29722
|
// plugins/kxm/src/runtime-supervisor.ts
|
|
28255
29723
|
function kxmSupervisorTokenFile(paths) {
|
|
28256
|
-
return
|
|
29724
|
+
return join18(paths.runtimeDir, "supervisor.token");
|
|
28257
29725
|
}
|
|
28258
29726
|
function publishKxmSupervisorToken(paths, token) {
|
|
28259
29727
|
const file = kxmSupervisorTokenFile(paths);
|
|
28260
|
-
|
|
29728
|
+
mkdirSync9(dirname11(file), { recursive: true, mode: 448 });
|
|
28261
29729
|
const temp = `${file}.${process.pid}.tmp`;
|
|
28262
|
-
|
|
29730
|
+
writeFileSync8(temp, `${token}
|
|
28263
29731
|
`, { encoding: "utf8", mode: 384 });
|
|
28264
29732
|
try {
|
|
28265
29733
|
chmodSync2(temp, 384);
|
|
28266
29734
|
} catch {
|
|
28267
29735
|
}
|
|
28268
|
-
|
|
29736
|
+
renameSync5(temp, file);
|
|
28269
29737
|
try {
|
|
28270
29738
|
chmodSync2(file, 384);
|
|
28271
29739
|
} catch {
|
|
28272
29740
|
}
|
|
28273
29741
|
}
|
|
28274
29742
|
function hashKxmSupervisorToken(token) {
|
|
28275
|
-
return `sha256:${
|
|
29743
|
+
return `sha256:${createHash14("sha256").update(`kxm-runtime-supervisor\0${token}`, "utf8").digest("hex")}`;
|
|
28276
29744
|
}
|
|
28277
29745
|
function readKxmSupervisorToken(paths) {
|
|
28278
29746
|
const file = kxmSupervisorTokenFile(paths);
|
|
@@ -28281,7 +29749,7 @@ function readKxmSupervisorToken(paths) {
|
|
|
28281
29749
|
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
28282
29750
|
throw runtimeError("runtime_path_invalid", file, "supervisor token file must be a regular file, not a link");
|
|
28283
29751
|
}
|
|
28284
|
-
const token =
|
|
29752
|
+
const token = readFileSync12(file, "utf8").trim();
|
|
28285
29753
|
return token.length >= 32 ? token : void 0;
|
|
28286
29754
|
}
|
|
28287
29755
|
function processAlive(pid) {
|
|
@@ -28295,16 +29763,16 @@ function processAlive(pid) {
|
|
|
28295
29763
|
var HEARTBEAT_STALE_MS = 15e3;
|
|
28296
29764
|
var SUPERVISOR_ERROR_MAX_AGE_MS = 3e4;
|
|
28297
29765
|
function supervisorErrorFile(paths) {
|
|
28298
|
-
return
|
|
29766
|
+
return join18(paths.runtimeDir, "supervisor.error");
|
|
28299
29767
|
}
|
|
28300
29768
|
function clearSupervisorError(paths) {
|
|
28301
29769
|
const file = supervisorErrorFile(paths);
|
|
28302
|
-
if (
|
|
29770
|
+
if (existsSync15(file)) rmSync4(file, { force: true });
|
|
28303
29771
|
}
|
|
28304
29772
|
function recordSupervisorError(paths, message) {
|
|
28305
29773
|
try {
|
|
28306
|
-
|
|
28307
|
-
|
|
29774
|
+
mkdirSync9(paths.runtimeDir, { recursive: true, mode: 448 });
|
|
29775
|
+
writeFileSync8(supervisorErrorFile(paths), `${message}
|
|
28308
29776
|
`, { encoding: "utf8", mode: 384 });
|
|
28309
29777
|
} catch {
|
|
28310
29778
|
}
|
|
@@ -28316,13 +29784,13 @@ function readRecentSupervisorError(paths) {
|
|
|
28316
29784
|
const ageMs = Date.now() - stat.mtimeMs;
|
|
28317
29785
|
if (ageMs > SUPERVISOR_ERROR_MAX_AGE_MS) return void 0;
|
|
28318
29786
|
try {
|
|
28319
|
-
return
|
|
29787
|
+
return readFileSync12(file, "utf8").trim();
|
|
28320
29788
|
} catch {
|
|
28321
29789
|
return void 0;
|
|
28322
29790
|
}
|
|
28323
29791
|
}
|
|
28324
29792
|
function kxmSupervisorStatus(paths) {
|
|
28325
|
-
if (!
|
|
29793
|
+
if (!existsSync15(paths.registryDb)) return { running: false };
|
|
28326
29794
|
const registry = new KxmRuntimeRegistry(paths.registryDb);
|
|
28327
29795
|
try {
|
|
28328
29796
|
const record2 = registry.supervisor();
|
|
@@ -28392,7 +29860,7 @@ async function ensureKxmSupervisor(options = {}) {
|
|
|
28392
29860
|
throw runtimeError("runtime_supervisor_unreachable", paths.registryDb, `runtime supervisor pid ${status.pid} is registered as running but cannot be probed`);
|
|
28393
29861
|
}
|
|
28394
29862
|
clearSupervisorError(paths);
|
|
28395
|
-
const scriptPath =
|
|
29863
|
+
const scriptPath = join18(findKxmRepoRoot(import.meta.url), "scripts", "kxm-runtime-supervisor.mjs");
|
|
28396
29864
|
const spawnImpl = options.spawnImpl ?? ((script, env) => {
|
|
28397
29865
|
const child = spawn3(process.execPath, [script], {
|
|
28398
29866
|
detached: true,
|
|
@@ -28477,8 +29945,8 @@ async function waitForDriveSessions(settled, graceMs) {
|
|
|
28477
29945
|
try {
|
|
28478
29946
|
await Promise.race([
|
|
28479
29947
|
Promise.allSettled(settled),
|
|
28480
|
-
new Promise((
|
|
28481
|
-
timeout = setTimeout(
|
|
29948
|
+
new Promise((resolve11) => {
|
|
29949
|
+
timeout = setTimeout(resolve11, graceMs);
|
|
28482
29950
|
timeout.unref?.();
|
|
28483
29951
|
})
|
|
28484
29952
|
]);
|
|
@@ -28615,7 +30083,7 @@ function syncFailureText(error) {
|
|
|
28615
30083
|
return `${code ? `${code}: ` : ""}${message}`.replace(/(kxm_[A-Za-z0-9_]+|[A-Za-z0-9._-]{40,})/g, "[redacted]").slice(0, 300);
|
|
28616
30084
|
}
|
|
28617
30085
|
function runtimeSupervisorLogFile(paths) {
|
|
28618
|
-
return
|
|
30086
|
+
return join18(paths.runtimeDir, "logs", "kxm-runtime.jsonl");
|
|
28619
30087
|
}
|
|
28620
30088
|
async function startKxmRuntimeSupervisor(options = {}) {
|
|
28621
30089
|
const now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
@@ -28631,8 +30099,8 @@ async function startKxmRuntimeSupervisor(options = {}) {
|
|
|
28631
30099
|
}
|
|
28632
30100
|
}
|
|
28633
30101
|
async function startKxmRuntimeSupervisorInner(paths, requestedPortOption, now, env) {
|
|
28634
|
-
|
|
28635
|
-
|
|
30102
|
+
mkdirSync9(paths.runtimeDir, { recursive: true, mode: 448 });
|
|
30103
|
+
mkdirSync9(paths.projectsDir, { recursive: true, mode: 448 });
|
|
28636
30104
|
const logger = createLogger({
|
|
28637
30105
|
component: "runtime",
|
|
28638
30106
|
path: runtimeSupervisorLogFile(paths),
|
|
@@ -28642,7 +30110,7 @@ async function startKxmRuntimeSupervisorInner(paths, requestedPortOption, now, e
|
|
|
28642
30110
|
const token = randomBytes3(32).toString("hex");
|
|
28643
30111
|
const tokenHash = hashKxmSupervisorToken(token);
|
|
28644
30112
|
const registry = new KxmRuntimeRegistry(paths.registryDb);
|
|
28645
|
-
const runtimeId = `rtm_${
|
|
30113
|
+
const runtimeId = `rtm_${createHash14("sha256").update(`${paths.stateRoot}\0${process.pid}\0${now()}\0${randomBytes3(16).toString("hex")}`, "utf8").digest("hex").slice(0, 24)}`;
|
|
28646
30114
|
const requestedPort = requestedPortOption ?? 0;
|
|
28647
30115
|
let activeRuntimeId = runtimeId;
|
|
28648
30116
|
const contexts = /* @__PURE__ */ new Map();
|
|
@@ -28664,7 +30132,7 @@ async function startKxmRuntimeSupervisorInner(paths, requestedPortOption, now, e
|
|
|
28664
30132
|
const key = projectRuntimeKey(projectRoot);
|
|
28665
30133
|
const existing = contexts.get(key);
|
|
28666
30134
|
if (existing) return existing;
|
|
28667
|
-
const context = openKxmRuntimeContext(projectRoot, { homeRuntimeId: activeRuntimeId, stateRoot: paths.stateRoot });
|
|
30135
|
+
const context = openKxmRuntimeContext(projectRoot, { homeRuntimeId: activeRuntimeId, stateRoot: paths.stateRoot, logger });
|
|
28668
30136
|
registerSyncCredentials(context);
|
|
28669
30137
|
contexts.set(key, context);
|
|
28670
30138
|
return context;
|
|
@@ -29197,14 +30665,24 @@ async function kxmRuntimeRequest(handle, method, path, body) {
|
|
|
29197
30665
|
if (!response.ok) {
|
|
29198
30666
|
const code = typeof payload.error === "string" ? payload.error : "runtime_request_failed";
|
|
29199
30667
|
const message = typeof payload.message === "string" ? payload.message : `runtime request failed with HTTP ${response.status}`;
|
|
29200
|
-
throw runtimeError(code, path, message);
|
|
30668
|
+
throw runtimeError(code, path, `${message}${handoffSuffix(payload.handoff)}`);
|
|
29201
30669
|
}
|
|
29202
30670
|
return payload;
|
|
29203
30671
|
}
|
|
30672
|
+
var HANDOFF_TEXT_MAX = 200;
|
|
30673
|
+
function handoffSuffix(handoff) {
|
|
30674
|
+
if (!handoff || typeof handoff !== "object" || Array.isArray(handoff)) return "";
|
|
30675
|
+
const parts = [];
|
|
30676
|
+
for (const key of ["reason", "field", "detail"]) {
|
|
30677
|
+
const value = handoff[key];
|
|
30678
|
+
if (typeof value === "string" && value.length > 0) parts.push(`${key} ${value.slice(0, HANDOFF_TEXT_MAX)}`);
|
|
30679
|
+
}
|
|
30680
|
+
return parts.length > 0 ? ` (handoff ${parts.join("; ")})` : "";
|
|
30681
|
+
}
|
|
29204
30682
|
|
|
29205
30683
|
// plugins/kxm/src/pi-producer.ts
|
|
29206
30684
|
import { spawn as spawn4 } from "node:child_process";
|
|
29207
|
-
import { join as
|
|
30685
|
+
import { join as join19 } from "node:path";
|
|
29208
30686
|
import { StringDecoder } from "node:string_decoder";
|
|
29209
30687
|
function formatPiSessionKey(params) {
|
|
29210
30688
|
if (params.agentId === "coordinator") {
|
|
@@ -29420,8 +30898,8 @@ var PiSession = class {
|
|
|
29420
30898
|
}
|
|
29421
30899
|
const id = `cmd_${++this.commandCounter}`;
|
|
29422
30900
|
const payload = { ...command, id };
|
|
29423
|
-
return new Promise((
|
|
29424
|
-
this.pendingCommands.set(id, { resolve:
|
|
30901
|
+
return new Promise((resolve11, reject) => {
|
|
30902
|
+
this.pendingCommands.set(id, { resolve: resolve11, reject });
|
|
29425
30903
|
try {
|
|
29426
30904
|
this.process.stdin.write(JSON.stringify(payload) + "\n");
|
|
29427
30905
|
} catch (err) {
|
|
@@ -29441,7 +30919,7 @@ var PiSession = class {
|
|
|
29441
30919
|
return { outcome: "cancelled", text: "aborted", usage: {} };
|
|
29442
30920
|
}
|
|
29443
30921
|
this.status = "busy";
|
|
29444
|
-
return new Promise((
|
|
30922
|
+
return new Promise((resolve11, reject) => {
|
|
29445
30923
|
let signalCleanup;
|
|
29446
30924
|
if (signal) {
|
|
29447
30925
|
const onAbort = () => {
|
|
@@ -29455,7 +30933,7 @@ var PiSession = class {
|
|
|
29455
30933
|
signalCleanup = () => signal.removeEventListener("abort", onAbort);
|
|
29456
30934
|
}
|
|
29457
30935
|
this.activePrompt = {
|
|
29458
|
-
resolve:
|
|
30936
|
+
resolve: resolve11,
|
|
29459
30937
|
reject,
|
|
29460
30938
|
allowedOutcomes,
|
|
29461
30939
|
text: "",
|
|
@@ -29578,7 +31056,7 @@ function createKxmPiProducer(options = {}) {
|
|
|
29578
31056
|
const args = ["--mode", "rpc", "--name", displayName];
|
|
29579
31057
|
if (options.sessionDir) {
|
|
29580
31058
|
const safeDirName = key.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
29581
|
-
args.push("--session-dir",
|
|
31059
|
+
args.push("--session-dir", join19(options.sessionDir, request.runId, safeDirName));
|
|
29582
31060
|
}
|
|
29583
31061
|
if (model) {
|
|
29584
31062
|
args.push("--model", model);
|
|
@@ -29706,9 +31184,9 @@ function createKxmPiProducer(options = {}) {
|
|
|
29706
31184
|
}
|
|
29707
31185
|
|
|
29708
31186
|
// plugins/kxm/src/improve.ts
|
|
29709
|
-
import { createHash as
|
|
29710
|
-
import { existsSync as
|
|
29711
|
-
import { join as
|
|
31187
|
+
import { createHash as createHash15 } from "node:crypto";
|
|
31188
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync10, writeFileSync as writeFileSync9 } from "node:fs";
|
|
31189
|
+
import { join as join20, relative as relative4, resolve as resolve9 } from "node:path";
|
|
29712
31190
|
var CANDIDATE_SCHEMA = "kxm.candidate.v1";
|
|
29713
31191
|
var IMPROVEMENT_REPORT_SCHEMA = "kxm.improvement-report.v2";
|
|
29714
31192
|
var IMPROVEMENT_REPORT_V1_SCHEMA = "kxm.improvement-report.v1";
|
|
@@ -29723,8 +31201,11 @@ function classifyCandidateKind(stepId, agentRole) {
|
|
|
29723
31201
|
}
|
|
29724
31202
|
return "workflow-step";
|
|
29725
31203
|
}
|
|
29726
|
-
function
|
|
29727
|
-
|
|
31204
|
+
function candidateSlug(value) {
|
|
31205
|
+
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "step";
|
|
31206
|
+
}
|
|
31207
|
+
function generateCandidateDiff(kind, stepId, agentRole, workflowId) {
|
|
31208
|
+
const safeSlug = candidateSlug(stepId);
|
|
29728
31209
|
if (kind === "gate") {
|
|
29729
31210
|
const diff2 = [
|
|
29730
31211
|
"diff --git a/.kxm/gates.yaml b/.kxm/gates.yaml",
|
|
@@ -29772,56 +31253,96 @@ function generateCandidateDiff(kind, stepId, agentRole, workflowHash) {
|
|
|
29772
31253
|
diff: diff2,
|
|
29773
31254
|
declaredOutcome: `Governed reusable skill for ${stepId}`,
|
|
29774
31255
|
measure: `Reduced prompt drift and consistent model guidance for ${stepId}`,
|
|
29775
|
-
summary: `
|
|
31256
|
+
summary: `Consolidate repeated instructions for '${stepId}' (${agentRole}) into a governed skill (consolidation, not a coded step)`
|
|
29776
31257
|
};
|
|
29777
31258
|
}
|
|
29778
|
-
const rel = `.kxm/workflows/${safeSlug}.yaml`;
|
|
31259
|
+
const rel = `.kxm/workflows/${workflowId !== void 0 ? candidateSlug(workflowId) : safeSlug}.yaml`;
|
|
29779
31260
|
const diff = [
|
|
29780
31261
|
`diff --git a/${rel} b/${rel}`,
|
|
29781
31262
|
`--- a/${rel}`,
|
|
29782
31263
|
`+++ b/${rel}`,
|
|
29783
|
-
"@@ -1,3 +1,
|
|
29784
|
-
|
|
29785
|
-
|
|
29786
|
-
|
|
29787
|
-
|
|
31264
|
+
"@@ -1,3 +1,3 @@",
|
|
31265
|
+
` - id: ${stepId}`,
|
|
31266
|
+
"- kind: agent",
|
|
31267
|
+
`- agent: ${agentRole}`,
|
|
31268
|
+
"+ kind: gate",
|
|
31269
|
+
`+ gate: ${safeSlug}`,
|
|
31270
|
+
"diff --git a/.kxm/gates.yaml b/.kxm/gates.yaml",
|
|
31271
|
+
"--- a/.kxm/gates.yaml",
|
|
31272
|
+
"+++ b/.kxm/gates.yaml",
|
|
31273
|
+
"@@ -1,2 +1,6 @@",
|
|
31274
|
+
" schema: kxm.gate-registry.v1",
|
|
31275
|
+
" gates:",
|
|
31276
|
+
`+ ${safeSlug}:`,
|
|
31277
|
+
"+ kind: command",
|
|
31278
|
+
`+ argv: [node, scripts/${safeSlug}.mjs]`,
|
|
31279
|
+
"+ timeoutMs: 600000",
|
|
29788
31280
|
""
|
|
29789
31281
|
].join("\n");
|
|
29790
31282
|
return {
|
|
29791
31283
|
diff,
|
|
29792
|
-
declaredOutcome: `
|
|
29793
|
-
measure: `
|
|
29794
|
-
summary: `
|
|
31284
|
+
declaredOutcome: `Coded gate replaces the model turn for ${stepId}; the operator writes scripts/${safeSlug}.mjs`,
|
|
31285
|
+
measure: `Model cost and latency for ${stepId} drop to zero while its accepted rate holds`,
|
|
31286
|
+
summary: `Replace the model turn for '${stepId}' (${agentRole}) with a coded gate step`
|
|
29795
31287
|
};
|
|
29796
31288
|
}
|
|
31289
|
+
function nonEmptyText(value) {
|
|
31290
|
+
if (typeof value !== "string") return void 0;
|
|
31291
|
+
const trimmed = value.trim();
|
|
31292
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
31293
|
+
}
|
|
31294
|
+
function roundThree(value) {
|
|
31295
|
+
return Number(value.toFixed(3));
|
|
31296
|
+
}
|
|
29797
31297
|
function groupRoutingRecords(records, options = {}) {
|
|
29798
31298
|
const minRecurrence = options.minRecurrence ?? 2;
|
|
29799
31299
|
const minPassRate = options.minPassRate ?? 0.75;
|
|
31300
|
+
const halfLifeDays = options.halfLifeDays ?? 14;
|
|
31301
|
+
const now = options.now ?? Date.now();
|
|
31302
|
+
const raws = records.map((record2) => record2);
|
|
31303
|
+
const runKeys = raws.map((raw, index) => nonEmptyText(raw.runId) ?? nonEmptyText(raw.workflowRunId) ?? `record:${index}`);
|
|
31304
|
+
const stepIds = raws.map((raw) => nonEmptyText(raw.stepId) ?? nonEmptyText(raw.stageId) ?? "unknown");
|
|
31305
|
+
const retriesOf = (raw) => typeof raw.retries === "number" && Number.isFinite(raw.retries) ? raw.retries : 0;
|
|
31306
|
+
const maxRetries = /* @__PURE__ */ new Map();
|
|
31307
|
+
raws.forEach((raw, index) => {
|
|
31308
|
+
const attemptKey = `${runKeys[index]}\0${stepIds[index]}`;
|
|
31309
|
+
maxRetries.set(attemptKey, Math.max(maxRetries.get(attemptKey) ?? 0, retriesOf(raw)));
|
|
31310
|
+
});
|
|
29800
31311
|
const map = /* @__PURE__ */ new Map();
|
|
29801
|
-
|
|
29802
|
-
const
|
|
29803
|
-
const
|
|
29804
|
-
const workflowHash = raw.workflowDefinitionSha256
|
|
29805
|
-
const stepId =
|
|
29806
|
-
const agentRole = raw.agentRole
|
|
29807
|
-
const promptHash =
|
|
31312
|
+
raws.forEach((raw, index) => {
|
|
31313
|
+
const providerMeta = raw.providerMetadata && typeof raw.providerMetadata === "object" && !Array.isArray(raw.providerMetadata) ? raw.providerMetadata : {};
|
|
31314
|
+
const workflowId = nonEmptyText(providerMeta.workflowId);
|
|
31315
|
+
const workflowHash = workflowId ?? nonEmptyText(raw.workflowDefinitionSha256) ?? nonEmptyText(providerMeta.workflowDefinitionSha256) ?? nonEmptyText(raw.workflowRunId) ?? nonEmptyText(raw.runId) ?? "standalone";
|
|
31316
|
+
const stepId = stepIds[index];
|
|
31317
|
+
const agentRole = nonEmptyText(raw.agentRole) ?? "agent";
|
|
31318
|
+
const promptHash = nonEmptyText(providerMeta.askSha256) ?? nonEmptyText(raw.rolePromptSha256) ?? "none";
|
|
31319
|
+
const runKey = runKeys[index];
|
|
29808
31320
|
const key = `${workflowHash}:${stepId}:${agentRole}:${promptHash}`;
|
|
29809
31321
|
let group = map.get(key);
|
|
29810
31322
|
if (!group) {
|
|
29811
31323
|
group = {
|
|
31324
|
+
key,
|
|
29812
31325
|
workflowHash,
|
|
29813
31326
|
stepId,
|
|
29814
31327
|
agentRole,
|
|
29815
31328
|
promptHash,
|
|
29816
31329
|
costs: [],
|
|
29817
31330
|
latencies: [],
|
|
29818
|
-
|
|
31331
|
+
total: 0,
|
|
31332
|
+
decided: 0,
|
|
31333
|
+
passed: 0,
|
|
31334
|
+
undecided: 0,
|
|
29819
31335
|
reworkCount: 0,
|
|
29820
|
-
|
|
29821
|
-
|
|
31336
|
+
weight: 0,
|
|
31337
|
+
undated: 0,
|
|
31338
|
+
writes: false,
|
|
31339
|
+
decidedRuns: /* @__PURE__ */ new Set(),
|
|
31340
|
+
askRuns: /* @__PURE__ */ new Map(),
|
|
31341
|
+
evidenceRefs: /* @__PURE__ */ new Set()
|
|
29822
31342
|
};
|
|
29823
31343
|
map.set(key, group);
|
|
29824
31344
|
}
|
|
31345
|
+
if (group.workflowId === void 0 && workflowId !== void 0) group.workflowId = workflowId;
|
|
29825
31346
|
group.total += 1;
|
|
29826
31347
|
const cost = raw.costUsd;
|
|
29827
31348
|
if (typeof cost === "number" && !Number.isNaN(cost) && cost >= 0) {
|
|
@@ -29833,67 +31354,110 @@ function groupRoutingRecords(records, options = {}) {
|
|
|
29833
31354
|
}
|
|
29834
31355
|
const verifierOutcome = raw.verifierOutcome;
|
|
29835
31356
|
const finalOutcome = raw.finalOutcome;
|
|
29836
|
-
|
|
29837
|
-
|
|
31357
|
+
const decided = verifierOutcome !== void 0 && verifierOutcome !== null || finalOutcome !== void 0 && finalOutcome !== null && finalOutcome !== "pending";
|
|
31358
|
+
if (decided) {
|
|
31359
|
+
group.decided += 1;
|
|
31360
|
+
group.decidedRuns.add(runKey);
|
|
31361
|
+
const ask = nonEmptyText(providerMeta.objectiveSha256) ?? "(unkeyed)";
|
|
31362
|
+
let askRuns = group.askRuns.get(ask);
|
|
31363
|
+
if (!askRuns) {
|
|
31364
|
+
askRuns = /* @__PURE__ */ new Set();
|
|
31365
|
+
group.askRuns.set(ask, askRuns);
|
|
31366
|
+
}
|
|
31367
|
+
askRuns.add(runKey);
|
|
31368
|
+
const superseded = (maxRetries.get(`${runKey}\0${stepId}`) ?? 0) > retriesOf(raw);
|
|
31369
|
+
if (!superseded && (verifierOutcome === "passed" || finalOutcome === "accepted" || finalOutcome === "completed")) {
|
|
31370
|
+
group.passed += 1;
|
|
31371
|
+
}
|
|
31372
|
+
} else {
|
|
31373
|
+
group.undecided += 1;
|
|
29838
31374
|
}
|
|
29839
31375
|
const retries = raw.retries;
|
|
29840
31376
|
if (typeof retries === "number") {
|
|
29841
31377
|
group.reworkCount += retries;
|
|
29842
31378
|
}
|
|
31379
|
+
const recordedAt = nonEmptyText(raw.recordedAt);
|
|
31380
|
+
if (recordedAt === void 0 || !Number.isFinite(Date.parse(recordedAt))) group.undated += 1;
|
|
31381
|
+
group.weight += computeDecayedWeight(recordedAt ?? "", halfLifeDays, now);
|
|
31382
|
+
if (providerMeta.stepWrites === true) group.writes = true;
|
|
29843
31383
|
const ref = raw.attemptId || raw.runId || raw.workflowRunId || raw.behavioralSha256;
|
|
29844
31384
|
if (ref && group.evidenceRefs.size < 16) {
|
|
29845
31385
|
group.evidenceRefs.add(ref);
|
|
29846
31386
|
}
|
|
29847
|
-
}
|
|
29848
|
-
const
|
|
31387
|
+
});
|
|
31388
|
+
const keyed = [];
|
|
29849
31389
|
for (const group of map.values()) {
|
|
29850
31390
|
const recurrence = group.total;
|
|
29851
31391
|
const meanCost = group.costs.length > 0 ? Number((group.costs.reduce((a, b) => a + b, 0) / group.costs.length).toFixed(4)) : 0;
|
|
29852
31392
|
const meanLatency = group.latencies.length > 0 ? Math.round(group.latencies.reduce((a, b) => a + b, 0) / group.latencies.length) : 0;
|
|
29853
|
-
const verifyPassRate =
|
|
29854
|
-
const
|
|
29855
|
-
const
|
|
31393
|
+
const verifyPassRate = group.decided > 0 ? roundThree(group.passed / group.decided) : 0;
|
|
31394
|
+
const distinctRuns = group.decidedRuns.size;
|
|
31395
|
+
const askRecurrence = Math.max(0, ...[...group.askRuns.values()].map((runs) => runs.size));
|
|
31396
|
+
const passes = verifyPassRate >= minPassRate;
|
|
31397
|
+
const isCandidate = askRecurrence >= minRecurrence && passes && !group.writes;
|
|
31398
|
+
let excludedReason;
|
|
31399
|
+
if (!isCandidate && passes) {
|
|
31400
|
+
if (askRecurrence >= minRecurrence && group.writes) excludedReason = "writes-repository";
|
|
31401
|
+
else if (distinctRuns >= minRecurrence && askRecurrence < minRecurrence) excludedReason = "ask-not-repeated";
|
|
31402
|
+
}
|
|
29856
31403
|
const candidateKind = isCandidate ? classifyCandidateKind(group.stepId, group.agentRole) : void 0;
|
|
29857
31404
|
const safeSlug = group.stepId.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 16) || "step";
|
|
29858
|
-
const keyHash =
|
|
31405
|
+
const keyHash = createHash15("sha256").update(`${group.workflowHash}:${group.stepId}:${group.agentRole}:${group.promptHash}`).digest("hex").slice(0, 10);
|
|
29859
31406
|
const candidateId = isCandidate && candidateKind ? `cand_${candidateKind.replace(/-/g, "_")}_${safeSlug}_${keyHash}` : void 0;
|
|
29860
|
-
|
|
29861
|
-
|
|
29862
|
-
|
|
29863
|
-
|
|
29864
|
-
|
|
29865
|
-
|
|
29866
|
-
|
|
29867
|
-
|
|
29868
|
-
|
|
29869
|
-
|
|
29870
|
-
|
|
29871
|
-
|
|
29872
|
-
|
|
29873
|
-
|
|
31407
|
+
keyed.push({
|
|
31408
|
+
key: group.key,
|
|
31409
|
+
row: {
|
|
31410
|
+
workflowHash: group.workflowHash,
|
|
31411
|
+
...group.workflowId !== void 0 ? { workflowId: group.workflowId } : {},
|
|
31412
|
+
stepId: group.stepId,
|
|
31413
|
+
agentRole: group.agentRole,
|
|
31414
|
+
promptHash: group.promptHash,
|
|
31415
|
+
recurrence,
|
|
31416
|
+
distinctRuns,
|
|
31417
|
+
askRecurrence,
|
|
31418
|
+
undecidedRecords: group.undecided,
|
|
31419
|
+
meanCost,
|
|
31420
|
+
meanLatency,
|
|
31421
|
+
verifyPassRate,
|
|
31422
|
+
rework: group.reworkCount,
|
|
31423
|
+
weightedRecurrence: roundThree(group.weight),
|
|
31424
|
+
undatedRecords: group.undated,
|
|
31425
|
+
costSamples: group.costs.length,
|
|
31426
|
+
writesRepository: group.writes,
|
|
31427
|
+
evidenceRefs: [...group.evidenceRefs],
|
|
31428
|
+
isCandidate,
|
|
31429
|
+
...excludedReason !== void 0 ? { excludedReason } : {},
|
|
31430
|
+
...candidateKind !== void 0 ? { candidateKind } : {},
|
|
31431
|
+
...candidateId !== void 0 ? { candidateId } : {}
|
|
31432
|
+
}
|
|
29874
31433
|
});
|
|
29875
31434
|
}
|
|
29876
|
-
return
|
|
31435
|
+
return keyed.sort((left, right) => {
|
|
31436
|
+
const a = left.row;
|
|
31437
|
+
const b = right.row;
|
|
29877
31438
|
if (a.isCandidate !== b.isCandidate) return a.isCandidate ? -1 : 1;
|
|
31439
|
+
if (b.weightedRecurrence !== a.weightedRecurrence) return b.weightedRecurrence - a.weightedRecurrence;
|
|
29878
31440
|
if (b.recurrence !== a.recurrence) return b.recurrence - a.recurrence;
|
|
29879
|
-
return a.stepId
|
|
29880
|
-
});
|
|
31441
|
+
return compareCodeUnitIds(a.stepId, b.stepId) || compareCodeUnitIds(left.key, right.key);
|
|
31442
|
+
}).map((entry) => entry.row);
|
|
29881
31443
|
}
|
|
29882
31444
|
function buildImprovementReport(records, options = {}) {
|
|
29883
|
-
const projectRoot = options.projectRoot ?
|
|
29884
|
-
const candidatesDir = options.candidatesDir ?
|
|
31445
|
+
const projectRoot = options.projectRoot ? resolve9(options.projectRoot) : process.cwd();
|
|
31446
|
+
const candidatesDir = options.candidatesDir ? resolve9(options.candidatesDir) : join20(projectRoot, ".kxm", "candidates");
|
|
31447
|
+
const promotionPolicy = options.promotionPolicy ?? "manual_pr";
|
|
29885
31448
|
const groups = groupRoutingRecords(records, options);
|
|
29886
31449
|
const candidates = [];
|
|
31450
|
+
const promotion = [];
|
|
29887
31451
|
for (const group of groups) {
|
|
29888
31452
|
if (!group.isCandidate || !group.candidateKind || !group.candidateId) continue;
|
|
29889
31453
|
const { diff, declaredOutcome, measure, summary } = generateCandidateDiff(
|
|
29890
31454
|
group.candidateKind,
|
|
29891
31455
|
group.stepId,
|
|
29892
31456
|
group.agentRole,
|
|
29893
|
-
group.
|
|
31457
|
+
group.workflowId
|
|
29894
31458
|
);
|
|
29895
31459
|
const diffFileName = `${group.candidateId}.diff`;
|
|
29896
|
-
const diffFilePath =
|
|
31460
|
+
const diffFilePath = join20(candidatesDir, diffFileName);
|
|
29897
31461
|
const relDiffPath = relative4(projectRoot, diffFilePath).replace(/\\/g, "/");
|
|
29898
31462
|
const candidate = {
|
|
29899
31463
|
schema: CANDIDATE_SCHEMA,
|
|
@@ -29915,55 +31479,72 @@ function buildImprovementReport(records, options = {}) {
|
|
|
29915
31479
|
createdAt: nowIso()
|
|
29916
31480
|
};
|
|
29917
31481
|
if (!options.dryRun) {
|
|
29918
|
-
if (!
|
|
29919
|
-
|
|
31482
|
+
if (!existsSync16(candidatesDir)) {
|
|
31483
|
+
mkdirSync10(candidatesDir, { recursive: true });
|
|
29920
31484
|
}
|
|
29921
|
-
|
|
29922
|
-
const jsonFilePath =
|
|
29923
|
-
|
|
31485
|
+
writeFileSync9(diffFilePath, diff, "utf8");
|
|
31486
|
+
const jsonFilePath = join20(candidatesDir, `${group.candidateId}.json`);
|
|
31487
|
+
writeFileSync9(jsonFilePath, JSON.stringify(candidate, null, 2) + "\n", "utf8");
|
|
29924
31488
|
}
|
|
29925
31489
|
candidates.push(candidate);
|
|
29926
31490
|
}
|
|
31491
|
+
const rowsByCandidate = new Map(groups.flatMap((row) => row.candidateId !== void 0 ? [[row.candidateId, row]] : []));
|
|
31492
|
+
for (const candidate of candidates) {
|
|
31493
|
+
const row = rowsByCandidate.get(candidate.id);
|
|
31494
|
+
promotion.push({
|
|
31495
|
+
candidateId: candidate.id,
|
|
31496
|
+
...evaluatePromotionPolicy(candidate, promotionPolicy, {
|
|
31497
|
+
autoThreshold: options.autoThreshold,
|
|
31498
|
+
...row ? { costSamples: row.costSamples, distinctRuns: row.distinctRuns } : {}
|
|
31499
|
+
})
|
|
31500
|
+
});
|
|
31501
|
+
}
|
|
29927
31502
|
return {
|
|
29928
31503
|
schema: IMPROVEMENT_REPORT_SCHEMA,
|
|
29929
31504
|
createdAt: nowIso(),
|
|
29930
31505
|
reviewDecision: "proposed",
|
|
29931
31506
|
recordsCount: records.length,
|
|
29932
31507
|
groups,
|
|
29933
|
-
candidates
|
|
31508
|
+
candidates,
|
|
31509
|
+
promotionPolicy,
|
|
31510
|
+
promotion
|
|
29934
31511
|
};
|
|
29935
31512
|
}
|
|
29936
31513
|
function writeImprovementReport(improvementsDir, report, dryRun = false) {
|
|
29937
31514
|
const stamp = report.createdAt.replace(/[:.]/g, "-");
|
|
29938
|
-
const path =
|
|
31515
|
+
const path = join20(improvementsDir, `${stamp}.json`);
|
|
29939
31516
|
if (!dryRun) {
|
|
29940
|
-
|
|
29941
|
-
|
|
31517
|
+
mkdirSync10(improvementsDir, { recursive: true });
|
|
31518
|
+
writeFileSync9(path, `${JSON.stringify(report, null, 2)}
|
|
29942
31519
|
`, { encoding: "utf8" });
|
|
29943
31520
|
}
|
|
29944
31521
|
return path;
|
|
29945
31522
|
}
|
|
29946
31523
|
function formatImprovementReport(report) {
|
|
29947
31524
|
const lines = [
|
|
29948
|
-
`Improvement Report (${report.recordsCount} record(s), ${report.groups.length} group(s), ${report.candidates.length} candidate(s))`,
|
|
31525
|
+
`Improvement Report (${report.recordsCount} record(s), ${report.groups.length} group(s), ${report.candidates.length} candidate(s); promotion policy ${report.promotionPolicy})`,
|
|
29949
31526
|
"",
|
|
29950
|
-
"Workflow Step Role Prompt
|
|
29951
|
-
"
|
|
31527
|
+
"Workflow Step Role Prompt Records Runs Asks Weighted Cost ($) Latency (ms) Accepted Rework Candidate",
|
|
31528
|
+
"------------------------------------------------------------------------------------------------------------------------------------------"
|
|
29952
31529
|
];
|
|
29953
31530
|
for (const g of report.groups) {
|
|
29954
31531
|
const wf = g.workflowHash.slice(0, 12).padEnd(14);
|
|
29955
31532
|
const step = g.stepId.slice(0, 11).padEnd(12);
|
|
29956
31533
|
const role = g.agentRole.slice(0, 11).padEnd(12);
|
|
29957
|
-
const prompt = g.promptHash.slice(0, 10).padEnd(12);
|
|
29958
|
-
const rec = String(g.recurrence).padStart(
|
|
31534
|
+
const prompt = g.promptHash.replace(/^sha256:/, "").slice(0, 10).padEnd(12);
|
|
31535
|
+
const rec = String(g.recurrence).padStart(7);
|
|
31536
|
+
const runs = String(g.distinctRuns).padStart(5);
|
|
31537
|
+
const asks = String(g.askRecurrence).padStart(5);
|
|
31538
|
+
const weighted = g.weightedRecurrence.toFixed(3).padStart(9);
|
|
29959
31539
|
const cost = g.meanCost.toFixed(3).padStart(9);
|
|
29960
31540
|
const lat = String(g.meanLatency).padStart(13);
|
|
29961
|
-
const
|
|
31541
|
+
const accepted = `${(g.verifyPassRate * 100).toFixed(0)}%`.padStart(9);
|
|
29962
31542
|
const rework = String(g.rework).padStart(7);
|
|
29963
|
-
const cand = g.isCandidate ? `yes (${g.candidateKind})` : "no";
|
|
29964
|
-
lines.push(`${wf} ${step} ${role} ${prompt} ${rec} ${cost} ${lat} ${
|
|
31543
|
+
const cand = g.isCandidate ? `yes (${g.candidateKind})` : g.excludedReason !== void 0 ? `no (${g.excludedReason})` : "no";
|
|
31544
|
+
lines.push(`${wf} ${step} ${role} ${prompt} ${rec} ${runs} ${asks} ${weighted} ${cost} ${lat} ${accepted} ${rework} ${cand}`);
|
|
29965
31545
|
}
|
|
29966
31546
|
if (report.candidates.length > 0) {
|
|
31547
|
+
const readiness = new Map(report.promotion.map((entry) => [entry.candidateId, entry]));
|
|
29967
31548
|
lines.push("");
|
|
29968
31549
|
lines.push("Emitted Coded-Repeat Candidates:");
|
|
29969
31550
|
for (const c of report.candidates) {
|
|
@@ -29971,47 +31552,42 @@ function formatImprovementReport(report) {
|
|
|
29971
31552
|
lines.push(` Outcome: ${c.declaredOutcome}`);
|
|
29972
31553
|
lines.push(` Measure: ${c.measure}`);
|
|
29973
31554
|
lines.push(` Proposed diff: ${c.proposedDiffPath}`);
|
|
31555
|
+
const entry = readiness.get(c.id);
|
|
31556
|
+
if (entry) {
|
|
31557
|
+
lines.push(` Promotion (${entry.policy}): ${entry.readyForReview ? "ready for review" : "not ready for review"}; ${entry.reason}`);
|
|
31558
|
+
}
|
|
29974
31559
|
}
|
|
31560
|
+
lines.push("");
|
|
31561
|
+
lines.push("Candidates are proposals: readiness never authorizes, and activation is a reviewed Git change.");
|
|
29975
31562
|
}
|
|
29976
31563
|
return lines.join("\n");
|
|
29977
31564
|
}
|
|
29978
|
-
function evaluatePromotionPolicy(candidate, policy = "manual_pr", options) {
|
|
31565
|
+
function evaluatePromotionPolicy(candidate, policy = "manual_pr", options = {}) {
|
|
29979
31566
|
if (policy === "manual_pr") {
|
|
29980
|
-
return {
|
|
29981
|
-
eligible: true,
|
|
29982
|
-
policy,
|
|
29983
|
-
authorized: false,
|
|
29984
|
-
reason: "Manual PR review and signoff required by policy (fail-closed anti-privilege-escalation)"
|
|
29985
|
-
};
|
|
31567
|
+
return { policy, readyForReview: true, reason: `operator PR applying ${candidate.proposedDiffPath} required` };
|
|
29986
31568
|
}
|
|
29987
31569
|
if (policy === "critic_quorum") {
|
|
29988
|
-
const
|
|
29989
|
-
|
|
29990
|
-
return {
|
|
29991
|
-
eligible: true,
|
|
29992
|
-
policy,
|
|
29993
|
-
authorized: hasQuorum,
|
|
29994
|
-
reason: hasQuorum ? `Authorized by critic quorum (${approvals.join(", ")})` : `Requires dual critic quorum; current approvals: ${approvals.length}/2`
|
|
29995
|
-
};
|
|
31570
|
+
const receipts = new Set((options.criticReceipts ?? []).map((receipt) => receipt.trim()).filter((receipt) => receipt.length > 0));
|
|
31571
|
+
return receipts.size >= 2 ? { policy, readyForReview: true, reason: "two critic receipts cited; operator PR still required" } : { policy, readyForReview: false, reason: `awaiting 2 distinct critic receipts (have ${receipts.size})` };
|
|
29996
31572
|
}
|
|
29997
31573
|
if (policy === "auto_threshold") {
|
|
29998
|
-
const
|
|
29999
|
-
const
|
|
31574
|
+
const minRuns = options.autoThreshold?.minRuns ?? 10;
|
|
31575
|
+
const minPassRate = options.autoThreshold?.minPassRate ?? 0.95;
|
|
31576
|
+
const minCostSavings = options.autoThreshold?.minCostSavings;
|
|
31577
|
+
const runs = options.distinctRuns ?? candidate.baselineMetrics.recurrence;
|
|
30000
31578
|
const passRate = candidate.baselineMetrics.verifyPassRate;
|
|
30001
|
-
const
|
|
30002
|
-
|
|
30003
|
-
|
|
30004
|
-
|
|
30005
|
-
|
|
30006
|
-
|
|
30007
|
-
|
|
30008
|
-
|
|
30009
|
-
|
|
30010
|
-
|
|
30011
|
-
|
|
30012
|
-
|
|
30013
|
-
reason: `Unknown promotion policy: ${String(policy)}`
|
|
30014
|
-
};
|
|
31579
|
+
const costSamples = options.costSamples ?? 0;
|
|
31580
|
+
const meanCost = candidate.baselineMetrics.meanCost;
|
|
31581
|
+
const costMet = minCostSavings === void 0 || costSamples > 0 && meanCost >= minCostSavings;
|
|
31582
|
+
const ready = runs >= minRuns && passRate >= minPassRate && costMet;
|
|
31583
|
+
const detail = [
|
|
31584
|
+
`runs=${runs}/${minRuns}`,
|
|
31585
|
+
`passRate=${passRate}/${minPassRate}`,
|
|
31586
|
+
...minCostSavings === void 0 ? [] : [`meanCost=${meanCost}/${minCostSavings} over ${costSamples} cost sample(s)`]
|
|
31587
|
+
].join(", ");
|
|
31588
|
+
return ready ? { policy, readyForReview: true, reason: `auto-threshold met (${detail}); operator PR still required` } : { policy, readyForReview: false, reason: `auto-threshold not met (${detail})` };
|
|
31589
|
+
}
|
|
31590
|
+
return { policy, readyForReview: false, reason: "unknown promotion policy" };
|
|
30015
31591
|
}
|
|
30016
31592
|
|
|
30017
31593
|
// plugins/kxm/src/browser.ts
|
|
@@ -30438,9 +32014,9 @@ Instructions for Operator:
|
|
|
30438
32014
|
};
|
|
30439
32015
|
|
|
30440
32016
|
// plugins/kxm/src/modes.ts
|
|
30441
|
-
var
|
|
30442
|
-
import { existsSync as
|
|
30443
|
-
import { join as
|
|
32017
|
+
var import_yaml8 = __toESM(require_dist(), 1);
|
|
32018
|
+
import { existsSync as existsSync17, readFileSync as readFileSync14 } from "node:fs";
|
|
32019
|
+
import { join as join21 } from "node:path";
|
|
30444
32020
|
var DEFAULT_MODES_CONFIG = Object.freeze({
|
|
30445
32021
|
schema: "kxm.modes.v1",
|
|
30446
32022
|
majorModes: {
|
|
@@ -30503,13 +32079,13 @@ function loadModesConfig(projectRoot) {
|
|
|
30503
32079
|
if (!projectRoot) {
|
|
30504
32080
|
return DEFAULT_MODES_CONFIG;
|
|
30505
32081
|
}
|
|
30506
|
-
const modesPath =
|
|
30507
|
-
if (!
|
|
32082
|
+
const modesPath = join21(projectRoot, ".kxm", "modes.yaml");
|
|
32083
|
+
if (!existsSync17(modesPath)) {
|
|
30508
32084
|
return DEFAULT_MODES_CONFIG;
|
|
30509
32085
|
}
|
|
30510
32086
|
try {
|
|
30511
|
-
const raw =
|
|
30512
|
-
const parsed = (0,
|
|
32087
|
+
const raw = readFileSync14(modesPath, "utf8");
|
|
32088
|
+
const parsed = (0, import_yaml8.parse)(raw);
|
|
30513
32089
|
if (parsed && parsed.schema === "kxm.modes.v1" && parsed.majorModes) {
|
|
30514
32090
|
return {
|
|
30515
32091
|
schema: "kxm.modes.v1",
|
|
@@ -30594,11 +32170,11 @@ function calculatePromptFootprint(resolved, projectRoot = process.cwd(), catalog
|
|
|
30594
32170
|
estimatedTokens: estimateTokens(baseSystemPromptChars)
|
|
30595
32171
|
});
|
|
30596
32172
|
for (const relPath of resolved.contextFiles) {
|
|
30597
|
-
const fullPath =
|
|
32173
|
+
const fullPath = join21(projectRoot, relPath);
|
|
30598
32174
|
let chars = 0;
|
|
30599
|
-
if (
|
|
32175
|
+
if (existsSync17(fullPath)) {
|
|
30600
32176
|
try {
|
|
30601
|
-
chars =
|
|
32177
|
+
chars = readFileSync14(fullPath, "utf8").length;
|
|
30602
32178
|
} catch {
|
|
30603
32179
|
chars = 0;
|
|
30604
32180
|
}
|
|
@@ -30729,10 +32305,10 @@ ${divider}
|
|
|
30729
32305
|
}
|
|
30730
32306
|
|
|
30731
32307
|
// plugins/kxm/src/ssh-remote.ts
|
|
30732
|
-
import { spawnSync as
|
|
30733
|
-
import { existsSync as
|
|
32308
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
32309
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync11, readFileSync as readFileSync15, readdirSync as readdirSync8, rmSync as rmSync5, statSync as statSync5 } from "node:fs";
|
|
30734
32310
|
import { homedir as homedir4 } from "node:os";
|
|
30735
|
-
import { join as
|
|
32311
|
+
import { join as join22, resolve as resolve10 } from "node:path";
|
|
30736
32312
|
var MAX_SSH_OUTPUT_BYTES = 50 * 1024;
|
|
30737
32313
|
var MAX_SSH_OUTPUT_LINES = 2e3;
|
|
30738
32314
|
var DEFAULT_SOCKET_DIR = ".kxm/run/ssh-sockets";
|
|
@@ -30758,12 +32334,12 @@ function truncateSshOutput(raw) {
|
|
|
30758
32334
|
return { text, truncated };
|
|
30759
32335
|
}
|
|
30760
32336
|
function parseSshConfig(configPath) {
|
|
30761
|
-
const targetPath = configPath ??
|
|
30762
|
-
if (!
|
|
32337
|
+
const targetPath = configPath ?? join22(homedir4(), ".ssh", "config");
|
|
32338
|
+
if (!existsSync18(targetPath)) {
|
|
30763
32339
|
return [];
|
|
30764
32340
|
}
|
|
30765
32341
|
try {
|
|
30766
|
-
const content =
|
|
32342
|
+
const content = readFileSync15(targetPath, "utf-8");
|
|
30767
32343
|
const lines = content.split("\n");
|
|
30768
32344
|
const hosts = [];
|
|
30769
32345
|
let currentHosts = [];
|
|
@@ -30805,7 +32381,7 @@ function parseSshConfig(configPath) {
|
|
|
30805
32381
|
return [];
|
|
30806
32382
|
}
|
|
30807
32383
|
}
|
|
30808
|
-
function resolveSshHostG(host, execFn =
|
|
32384
|
+
function resolveSshHostG(host, execFn = spawnSync4) {
|
|
30809
32385
|
try {
|
|
30810
32386
|
const result = execFn("ssh", ["-G", host], { encoding: "utf-8" });
|
|
30811
32387
|
if (result.status !== 0 || !result.stdout) {
|
|
@@ -30840,15 +32416,15 @@ function resolveSshHostG(host, execFn = spawnSync3) {
|
|
|
30840
32416
|
}
|
|
30841
32417
|
}
|
|
30842
32418
|
function ensureSocketDir(socketDir = DEFAULT_SOCKET_DIR) {
|
|
30843
|
-
const resolved =
|
|
30844
|
-
if (!
|
|
30845
|
-
|
|
32419
|
+
const resolved = resolve10(socketDir);
|
|
32420
|
+
if (!existsSync18(resolved)) {
|
|
32421
|
+
mkdirSync11(resolved, { recursive: true, mode: 448 });
|
|
30846
32422
|
}
|
|
30847
32423
|
return resolved;
|
|
30848
32424
|
}
|
|
30849
32425
|
function buildSshArgs(options) {
|
|
30850
32426
|
const socketDir = ensureSocketDir(options.socketDir ?? DEFAULT_SOCKET_DIR);
|
|
30851
|
-
const controlPath =
|
|
32427
|
+
const controlPath = join22(socketDir, "%C");
|
|
30852
32428
|
const persist2 = options.controlPersist ?? DEFAULT_CONTROL_PERSIST;
|
|
30853
32429
|
const args = [
|
|
30854
32430
|
"-o",
|
|
@@ -30871,9 +32447,9 @@ function buildSshArgs(options) {
|
|
|
30871
32447
|
args.push(options.host);
|
|
30872
32448
|
return args;
|
|
30873
32449
|
}
|
|
30874
|
-
function checkControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn =
|
|
32450
|
+
function checkControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn = spawnSync4) {
|
|
30875
32451
|
const resolvedDir = ensureSocketDir(socketDir);
|
|
30876
|
-
const controlPath =
|
|
32452
|
+
const controlPath = join22(resolvedDir, "%C");
|
|
30877
32453
|
try {
|
|
30878
32454
|
const result = execFn("ssh", ["-O", "check", "-o", `ControlPath=${controlPath}`, host], {
|
|
30879
32455
|
encoding: "utf-8"
|
|
@@ -30883,9 +32459,9 @@ function checkControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn = spawn
|
|
|
30883
32459
|
return false;
|
|
30884
32460
|
}
|
|
30885
32461
|
}
|
|
30886
|
-
function closeControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn =
|
|
32462
|
+
function closeControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn = spawnSync4) {
|
|
30887
32463
|
const resolvedDir = ensureSocketDir(socketDir);
|
|
30888
|
-
const controlPath =
|
|
32464
|
+
const controlPath = join22(resolvedDir, "%C");
|
|
30889
32465
|
try {
|
|
30890
32466
|
const result = execFn("ssh", ["-O", "stop", "-o", `ControlPath=${controlPath}`, host], {
|
|
30891
32467
|
encoding: "utf-8"
|
|
@@ -30899,13 +32475,13 @@ function pruneSocketDir(socketDir = DEFAULT_SOCKET_DIR) {
|
|
|
30899
32475
|
const resolvedDir = ensureSocketDir(socketDir);
|
|
30900
32476
|
let removed = 0;
|
|
30901
32477
|
try {
|
|
30902
|
-
const entries =
|
|
32478
|
+
const entries = readdirSync8(resolvedDir);
|
|
30903
32479
|
for (const entry of entries) {
|
|
30904
|
-
const fullPath =
|
|
32480
|
+
const fullPath = join22(resolvedDir, entry);
|
|
30905
32481
|
try {
|
|
30906
|
-
const stat =
|
|
32482
|
+
const stat = statSync5(fullPath);
|
|
30907
32483
|
if (stat.isSocket()) {
|
|
30908
|
-
|
|
32484
|
+
rmSync5(fullPath, { force: true });
|
|
30909
32485
|
removed++;
|
|
30910
32486
|
}
|
|
30911
32487
|
} catch {
|
|
@@ -30917,7 +32493,7 @@ function pruneSocketDir(socketDir = DEFAULT_SOCKET_DIR) {
|
|
|
30917
32493
|
}
|
|
30918
32494
|
function executeSshRun(params) {
|
|
30919
32495
|
const startTime = Date.now();
|
|
30920
|
-
const execSyncFn = params.execFn ??
|
|
32496
|
+
const execSyncFn = params.execFn ?? spawnSync4;
|
|
30921
32497
|
if (params.action === "info") {
|
|
30922
32498
|
if (params.host) {
|
|
30923
32499
|
const hostInfo = resolveSshHostG(params.host, execSyncFn);
|