@kontextmind/kxm 0.7.126 → 0.7.127
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/roles/planner.yaml +6 -7
- package/.kxm/roles/reviewer-arch.yaml +6 -7
- package/.kxm/roles/reviewer-cli.yaml +6 -7
- package/.kxm/roles/writer.yaml +8 -17
- package/CHANGELOG.md +17 -0
- package/docs/architecture/access.md +2 -4
- package/docs/contracts/validation.md +1 -1
- package/docs/contributing/harness-routing-internals.md +1 -1
- package/docs/contributing/test-matrix.md +1 -1
- package/docs/glossary.md +1 -1
- package/docs/guides/agent-skills.md +1 -1
- package/docs/operations/backup-and-restore.md +2 -2
- package/docs/reference/cli-reference.md +31 -74
- package/docs/reference/config-reference.md +83 -118
- package/docs/reference/harness-routing.md +15 -8
- package/examples/project/.kxm/models/critic-claude.yaml +6 -2
- package/examples/project/.kxm/models/critic-gemini.yaml +6 -2
- package/examples/project/.kxm/models/critic-grok.yaml +6 -2
- package/examples/project/.kxm/models/implementation.yaml +6 -2
- package/examples/project/.kxm/models/primary.yaml +6 -2
- package/examples/project/.kxm/roles/planner.yaml +8 -0
- package/examples/project/.kxm/roles/reviewer-arch.yaml +8 -0
- package/examples/project/.kxm/roles/reviewer-cli.yaml +8 -0
- package/examples/project/.kxm/roles/writer.yaml +8 -0
- package/package.json +1 -1
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/cli.js +1759 -1306
- package/plugins/kxm/dist/mcp-server.js +1 -1
- package/plugins/kxm/dist/runtime-supervisor.js +994 -245
- package/plugins/kxm/dist/runtime.js +1018 -269
- package/plugins/kxm/dist/server.js +55 -1
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/skills/kxm/SKILL.md +1 -1
- package/plugins/kxm/skills/kxm-definitions/SKILL.md +3 -7
- package/plugins/kxm/src/autocomplete.ts +1 -1
- package/plugins/kxm/src/cli/project.ts +7 -1
- package/plugins/kxm/src/cli/roles.ts +46 -150
- package/plugins/kxm/src/cli.ts +8 -22
- package/plugins/kxm/src/engine.ts +24 -1
- package/plugins/kxm/src/init-guide-setup.ts +38 -4
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/permission.ts +11 -0
- package/plugins/kxm/src/policy-draft.mjs +56 -16
- package/plugins/kxm/src/project-config.ts +121 -10
- package/plugins/kxm/src/repair.ts +1 -0
- package/plugins/kxm/src/role.ts +40 -440
- package/plugins/kxm/src/routes.ts +28 -5
- package/plugins/kxm/src/template.ts +34 -1
- package/schemas/README.md +2 -1
- package/schemas/model.schema.json +111 -13
- package/schemas/role.schema.json +61 -31
- package/schemas/policy-draft/README.md +0 -17
- package/schemas/policy-draft/model.v2.schema.json +0 -140
- package/schemas/policy-draft/role.v2.schema.json +0 -91
|
@@ -164,10 +164,10 @@ var require_code = __commonJS({
|
|
|
164
164
|
function interpolate(x) {
|
|
165
165
|
return typeof x == "number" || typeof x == "boolean" || x === null ? x : safeStringify(Array.isArray(x) ? x.join(",") : x);
|
|
166
166
|
}
|
|
167
|
-
function
|
|
167
|
+
function stringify7(x) {
|
|
168
168
|
return new _Code(safeStringify(x));
|
|
169
169
|
}
|
|
170
|
-
exports.stringify =
|
|
170
|
+
exports.stringify = stringify7;
|
|
171
171
|
function safeStringify(x) {
|
|
172
172
|
return JSON.stringify(x).replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
173
173
|
}
|
|
@@ -3835,7 +3835,7 @@ var require_fast_uri = __commonJS({
|
|
|
3835
3835
|
normalizeString(uri, options);
|
|
3836
3836
|
} else if (typeof uri === "object") {
|
|
3837
3837
|
uri = /** @type {T} */
|
|
3838
|
-
|
|
3838
|
+
parse9(serialize(uri, options), options);
|
|
3839
3839
|
}
|
|
3840
3840
|
return uri;
|
|
3841
3841
|
}
|
|
@@ -3875,8 +3875,8 @@ var require_fast_uri = __commonJS({
|
|
|
3875
3875
|
function resolveComponent(base, relative5, options, skipNormalization) {
|
|
3876
3876
|
const target = {};
|
|
3877
3877
|
if (!skipNormalization) {
|
|
3878
|
-
base =
|
|
3879
|
-
relative5 =
|
|
3878
|
+
base = parse9(serialize(base, options), options);
|
|
3879
|
+
relative5 = parse9(serialize(relative5, options), options);
|
|
3880
3880
|
}
|
|
3881
3881
|
options = options || {};
|
|
3882
3882
|
if (!options.tolerant && relative5.scheme) {
|
|
@@ -4168,7 +4168,7 @@ var require_fast_uri = __commonJS({
|
|
|
4168
4168
|
}
|
|
4169
4169
|
return { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme };
|
|
4170
4170
|
}
|
|
4171
|
-
function
|
|
4171
|
+
function parse9(uri, opts) {
|
|
4172
4172
|
return parseWithStatus(uri, opts).parsed;
|
|
4173
4173
|
}
|
|
4174
4174
|
function normalizeString(uri, opts) {
|
|
@@ -4205,7 +4205,7 @@ var require_fast_uri = __commonJS({
|
|
|
4205
4205
|
resolveComponent,
|
|
4206
4206
|
equal,
|
|
4207
4207
|
serialize,
|
|
4208
|
-
parse:
|
|
4208
|
+
parse: parse9
|
|
4209
4209
|
};
|
|
4210
4210
|
module.exports = fastUri;
|
|
4211
4211
|
module.exports.default = fastUri;
|
|
@@ -6944,8 +6944,8 @@ var require_discriminator = __commonJS({
|
|
|
6944
6944
|
if (!tagRequired)
|
|
6945
6945
|
throw new Error(`discriminator: "${tagName}" must be required`);
|
|
6946
6946
|
return oneOfMapping;
|
|
6947
|
-
function hasRequired({ required }) {
|
|
6948
|
-
return Array.isArray(
|
|
6947
|
+
function hasRequired({ required: required2 }) {
|
|
6948
|
+
return Array.isArray(required2) && required2.includes(tagName);
|
|
6949
6949
|
}
|
|
6950
6950
|
function addMappings(sch, i) {
|
|
6951
6951
|
if (sch.const) {
|
|
@@ -8918,7 +8918,7 @@ var require_stringify = __commonJS({
|
|
|
8918
8918
|
props.push(doc.directives.tagString(tag));
|
|
8919
8919
|
return props.join(" ");
|
|
8920
8920
|
}
|
|
8921
|
-
function
|
|
8921
|
+
function stringify7(item, ctx, onComment, onChompKeep) {
|
|
8922
8922
|
if (identity.isPair(item))
|
|
8923
8923
|
return item.toString(ctx, onComment, onChompKeep);
|
|
8924
8924
|
if (identity.isAlias(item)) {
|
|
@@ -8947,7 +8947,7 @@ var require_stringify = __commonJS({
|
|
|
8947
8947
|
${ctx.indent}${str}`;
|
|
8948
8948
|
}
|
|
8949
8949
|
exports.createStringifyContext = createStringifyContext;
|
|
8950
|
-
exports.stringify =
|
|
8950
|
+
exports.stringify = stringify7;
|
|
8951
8951
|
}
|
|
8952
8952
|
});
|
|
8953
8953
|
|
|
@@ -8957,7 +8957,7 @@ var require_stringifyPair = __commonJS({
|
|
|
8957
8957
|
"use strict";
|
|
8958
8958
|
var identity = require_identity();
|
|
8959
8959
|
var Scalar = require_Scalar();
|
|
8960
|
-
var
|
|
8960
|
+
var stringify7 = require_stringify();
|
|
8961
8961
|
var stringifyComment = require_stringifyComment();
|
|
8962
8962
|
function stringifyPair({ key, value }, ctx, onComment, onChompKeep) {
|
|
8963
8963
|
const { allNullValues, doc, indent, indentStep, options: { commentString, indentSeq, simpleKeys } } = ctx;
|
|
@@ -8979,7 +8979,7 @@ var require_stringifyPair = __commonJS({
|
|
|
8979
8979
|
});
|
|
8980
8980
|
let keyCommentDone = false;
|
|
8981
8981
|
let chompKeep = false;
|
|
8982
|
-
let str =
|
|
8982
|
+
let str = stringify7.stringify(key, ctx, () => keyCommentDone = true, () => chompKeep = true);
|
|
8983
8983
|
if (!explicitKey && !ctx.inFlow && str.length > 1024) {
|
|
8984
8984
|
if (simpleKeys)
|
|
8985
8985
|
throw new Error("With simple keys, single line scalar must not span more than 1024 characters");
|
|
@@ -9031,7 +9031,7 @@ ${indent}:`;
|
|
|
9031
9031
|
ctx.indent = ctx.indent.substring(2);
|
|
9032
9032
|
}
|
|
9033
9033
|
let valueCommentDone = false;
|
|
9034
|
-
const valueStr =
|
|
9034
|
+
const valueStr = stringify7.stringify(value, ctx, () => valueCommentDone = true, () => chompKeep = true);
|
|
9035
9035
|
let ws = " ";
|
|
9036
9036
|
if (keyComment || vsb || vcb) {
|
|
9037
9037
|
ws = vsb ? "\n" : "";
|
|
@@ -9172,7 +9172,7 @@ var require_addPairToJSMap = __commonJS({
|
|
|
9172
9172
|
"use strict";
|
|
9173
9173
|
var log = require_log();
|
|
9174
9174
|
var merge = require_merge();
|
|
9175
|
-
var
|
|
9175
|
+
var stringify7 = require_stringify();
|
|
9176
9176
|
var identity = require_identity();
|
|
9177
9177
|
var toJS = require_toJS();
|
|
9178
9178
|
function addPairToJSMap(ctx, map, { key, value }) {
|
|
@@ -9208,7 +9208,7 @@ var require_addPairToJSMap = __commonJS({
|
|
|
9208
9208
|
if (typeof jsKey !== "object")
|
|
9209
9209
|
return String(jsKey);
|
|
9210
9210
|
if (identity.isNode(key) && ctx?.doc) {
|
|
9211
|
-
const strCtx =
|
|
9211
|
+
const strCtx = stringify7.createStringifyContext(ctx.doc, {});
|
|
9212
9212
|
strCtx.anchors = /* @__PURE__ */ new Set();
|
|
9213
9213
|
for (const node of ctx.anchors.keys())
|
|
9214
9214
|
strCtx.anchors.add(node.anchor);
|
|
@@ -9275,12 +9275,12 @@ var require_stringifyCollection = __commonJS({
|
|
|
9275
9275
|
"node_modules/yaml/dist/stringify/stringifyCollection.js"(exports) {
|
|
9276
9276
|
"use strict";
|
|
9277
9277
|
var identity = require_identity();
|
|
9278
|
-
var
|
|
9278
|
+
var stringify7 = require_stringify();
|
|
9279
9279
|
var stringifyComment = require_stringifyComment();
|
|
9280
9280
|
function stringifyCollection(collection, ctx, options) {
|
|
9281
9281
|
const flow = ctx.inFlow ?? collection.flow;
|
|
9282
|
-
const
|
|
9283
|
-
return
|
|
9282
|
+
const stringify8 = flow ? stringifyFlowCollection : stringifyBlockCollection;
|
|
9283
|
+
return stringify8(collection, ctx, options);
|
|
9284
9284
|
}
|
|
9285
9285
|
function stringifyBlockCollection({ comment, items }, ctx, { blockItemPrefix, flowChars, itemIndent, onChompKeep, onComment }) {
|
|
9286
9286
|
const { indent, options: { commentString } } = ctx;
|
|
@@ -9305,7 +9305,7 @@ var require_stringifyCollection = __commonJS({
|
|
|
9305
9305
|
}
|
|
9306
9306
|
}
|
|
9307
9307
|
chompKeep = false;
|
|
9308
|
-
let str2 =
|
|
9308
|
+
let str2 = stringify7.stringify(item, itemCtx, () => comment2 = null, () => chompKeep = true);
|
|
9309
9309
|
if (comment2)
|
|
9310
9310
|
str2 += stringifyComment.lineComment(str2, itemIndent, commentString(comment2));
|
|
9311
9311
|
if (chompKeep && comment2)
|
|
@@ -9372,7 +9372,7 @@ ${indent}${line}` : "\n";
|
|
|
9372
9372
|
}
|
|
9373
9373
|
if (comment)
|
|
9374
9374
|
reqNewline = true;
|
|
9375
|
-
let str =
|
|
9375
|
+
let str = stringify7.stringify(item, itemCtx, () => comment = null);
|
|
9376
9376
|
reqNewline || (reqNewline = lines.length > linesAtValue || str.includes("\n"));
|
|
9377
9377
|
if (i < items.length - 1) {
|
|
9378
9378
|
str += ",";
|
|
@@ -10733,7 +10733,7 @@ var require_stringifyDocument = __commonJS({
|
|
|
10733
10733
|
"node_modules/yaml/dist/stringify/stringifyDocument.js"(exports) {
|
|
10734
10734
|
"use strict";
|
|
10735
10735
|
var identity = require_identity();
|
|
10736
|
-
var
|
|
10736
|
+
var stringify7 = require_stringify();
|
|
10737
10737
|
var stringifyComment = require_stringifyComment();
|
|
10738
10738
|
function stringifyDocument(doc, options) {
|
|
10739
10739
|
const lines = [];
|
|
@@ -10748,7 +10748,7 @@ var require_stringifyDocument = __commonJS({
|
|
|
10748
10748
|
}
|
|
10749
10749
|
if (hasDirectives)
|
|
10750
10750
|
lines.push("---");
|
|
10751
|
-
const ctx =
|
|
10751
|
+
const ctx = stringify7.createStringifyContext(doc, options);
|
|
10752
10752
|
const { commentString } = ctx.options;
|
|
10753
10753
|
if (doc.commentBefore) {
|
|
10754
10754
|
if (lines.length !== 1)
|
|
@@ -10770,7 +10770,7 @@ var require_stringifyDocument = __commonJS({
|
|
|
10770
10770
|
contentComment = doc.contents.comment;
|
|
10771
10771
|
}
|
|
10772
10772
|
const onChompKeep = contentComment ? void 0 : () => chompKeep = true;
|
|
10773
|
-
let body =
|
|
10773
|
+
let body = stringify7.stringify(doc.contents, ctx, () => contentComment = null, onChompKeep);
|
|
10774
10774
|
if (contentComment)
|
|
10775
10775
|
body += stringifyComment.lineComment(body, "", commentString(contentComment));
|
|
10776
10776
|
if ((body[0] === "|" || body[0] === ">") && lines[lines.length - 1] === "---") {
|
|
@@ -10778,7 +10778,7 @@ var require_stringifyDocument = __commonJS({
|
|
|
10778
10778
|
} else
|
|
10779
10779
|
lines.push(body);
|
|
10780
10780
|
} else {
|
|
10781
|
-
lines.push(
|
|
10781
|
+
lines.push(stringify7.stringify(doc.contents, ctx));
|
|
10782
10782
|
}
|
|
10783
10783
|
if (doc.directives?.docEnd) {
|
|
10784
10784
|
if (doc.comment) {
|
|
@@ -12914,7 +12914,7 @@ var require_cst_scalar = __commonJS({
|
|
|
12914
12914
|
var require_cst_stringify = __commonJS({
|
|
12915
12915
|
"node_modules/yaml/dist/parse/cst-stringify.js"(exports) {
|
|
12916
12916
|
"use strict";
|
|
12917
|
-
var
|
|
12917
|
+
var stringify7 = (cst) => "type" in cst ? stringifyToken(cst) : stringifyItem(cst);
|
|
12918
12918
|
function stringifyToken(token) {
|
|
12919
12919
|
switch (token.type) {
|
|
12920
12920
|
case "block-scalar": {
|
|
@@ -12967,7 +12967,7 @@ var require_cst_stringify = __commonJS({
|
|
|
12967
12967
|
res += stringifyToken(value);
|
|
12968
12968
|
return res;
|
|
12969
12969
|
}
|
|
12970
|
-
exports.stringify =
|
|
12970
|
+
exports.stringify = stringify7;
|
|
12971
12971
|
}
|
|
12972
12972
|
});
|
|
12973
12973
|
|
|
@@ -14659,7 +14659,7 @@ var require_public_api = __commonJS({
|
|
|
14659
14659
|
return docs;
|
|
14660
14660
|
return Object.assign([], { empty: true }, composer$1.streamInfo());
|
|
14661
14661
|
}
|
|
14662
|
-
function
|
|
14662
|
+
function parseDocument3(source, options = {}) {
|
|
14663
14663
|
const { lineCounter: lineCounter2, prettyErrors } = parseOptions(options);
|
|
14664
14664
|
const parser$1 = new parser.Parser(lineCounter2?.addNewLine);
|
|
14665
14665
|
const composer$1 = new composer.Composer(options);
|
|
@@ -14678,14 +14678,14 @@ var require_public_api = __commonJS({
|
|
|
14678
14678
|
}
|
|
14679
14679
|
return doc;
|
|
14680
14680
|
}
|
|
14681
|
-
function
|
|
14681
|
+
function parse9(src, reviver, options) {
|
|
14682
14682
|
let _reviver = void 0;
|
|
14683
14683
|
if (typeof reviver === "function") {
|
|
14684
14684
|
_reviver = reviver;
|
|
14685
14685
|
} else if (options === void 0 && reviver && typeof reviver === "object") {
|
|
14686
14686
|
options = reviver;
|
|
14687
14687
|
}
|
|
14688
|
-
const doc =
|
|
14688
|
+
const doc = parseDocument3(src, options);
|
|
14689
14689
|
if (!doc)
|
|
14690
14690
|
return null;
|
|
14691
14691
|
doc.warnings.forEach((warning) => log.warn(doc.options.logLevel, warning));
|
|
@@ -14697,7 +14697,7 @@ var require_public_api = __commonJS({
|
|
|
14697
14697
|
}
|
|
14698
14698
|
return doc.toJS(Object.assign({ reviver: _reviver }, options));
|
|
14699
14699
|
}
|
|
14700
|
-
function
|
|
14700
|
+
function stringify7(value, replacer, options) {
|
|
14701
14701
|
let _replacer = null;
|
|
14702
14702
|
if (typeof replacer === "function" || Array.isArray(replacer)) {
|
|
14703
14703
|
_replacer = replacer;
|
|
@@ -14719,10 +14719,10 @@ var require_public_api = __commonJS({
|
|
|
14719
14719
|
return value.toString(options);
|
|
14720
14720
|
return new Document.Document(value, _replacer, options).toString(options);
|
|
14721
14721
|
}
|
|
14722
|
-
exports.parse =
|
|
14722
|
+
exports.parse = parse9;
|
|
14723
14723
|
exports.parseAllDocuments = parseAllDocuments;
|
|
14724
|
-
exports.parseDocument =
|
|
14725
|
-
exports.stringify =
|
|
14724
|
+
exports.parseDocument = parseDocument3;
|
|
14725
|
+
exports.stringify = stringify7;
|
|
14726
14726
|
}
|
|
14727
14727
|
});
|
|
14728
14728
|
|
|
@@ -14780,7 +14780,7 @@ var require_dist = __commonJS({
|
|
|
14780
14780
|
|
|
14781
14781
|
// plugins/kxm/src/runtime-supervisor.ts
|
|
14782
14782
|
import { spawn as spawn3 } from "node:child_process";
|
|
14783
|
-
import { createHash as
|
|
14783
|
+
import { createHash as createHash15, createHmac, randomBytes as randomBytes2, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
14784
14784
|
import { chmodSync as chmodSync2, existsSync as existsSync16, lstatSync as lstatSync6, mkdirSync as mkdirSync9, readFileSync as readFileSync13, renameSync as renameSync5, rmSync as rmSync4, writeFileSync as writeFileSync9 } from "node:fs";
|
|
14785
14785
|
import { createServer } from "node:http";
|
|
14786
14786
|
import { dirname as dirname11, isAbsolute as isAbsolute7, join as join18 } from "node:path";
|
|
@@ -14812,7 +14812,7 @@ function findKxmRepoRoot(fromUrl = import.meta.url) {
|
|
|
14812
14812
|
// plugins/kxm/src/project-config.ts
|
|
14813
14813
|
var import__ = __toESM(require__(), 1);
|
|
14814
14814
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
14815
|
-
import { createHash as
|
|
14815
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
14816
14816
|
import { existsSync as existsSync3, lstatSync, readFileSync, readdirSync, realpathSync } from "node:fs";
|
|
14817
14817
|
import { basename, dirname as dirname2, extname, isAbsolute, join as join2, relative, resolve, sep } from "node:path";
|
|
14818
14818
|
|
|
@@ -14941,14 +14941,598 @@ function parseRestrictedYaml(input, label = "<yaml>", limits = KXM_YAML_LIMITS)
|
|
|
14941
14941
|
return value;
|
|
14942
14942
|
}
|
|
14943
14943
|
|
|
14944
|
+
// plugins/kxm/src/project-config.ts
|
|
14945
|
+
import { pathToFileURL } from "node:url";
|
|
14946
|
+
|
|
14947
|
+
// plugins/kxm/src/policy-draft.mjs
|
|
14948
|
+
import { createHash } from "node:crypto";
|
|
14949
|
+
var POLICY_DRAFT_MODEL_SCHEMA = "kxm.model.v2";
|
|
14950
|
+
var POLICY_DRAFT_ROLE_SCHEMA = "kxm.role.v2";
|
|
14951
|
+
var POLICY_DRAFT_PURPOSES = Object.freeze([
|
|
14952
|
+
"writer",
|
|
14953
|
+
"planner",
|
|
14954
|
+
"reviewer-arch",
|
|
14955
|
+
"reviewer-cli",
|
|
14956
|
+
"experiment"
|
|
14957
|
+
]);
|
|
14958
|
+
var POLICY_DRAFT_PERMISSIONS = Object.freeze(["edit", "read-only"]);
|
|
14959
|
+
var POLICY_DRAFT_STATUSES = Object.freeze(["admitted", "candidate", "retired"]);
|
|
14960
|
+
var IDENTIFIER = /^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$/;
|
|
14961
|
+
var ROUTE_ID = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
|
|
14962
|
+
var SHA256 = /^[a-f0-9]{64}$/;
|
|
14963
|
+
var TOKEN = /[\s\x00-\x1f]/u;
|
|
14964
|
+
var EFFORTS = Object.freeze(["off", "minimal", "low", "medium", "high", "xhigh", "max"]);
|
|
14965
|
+
var MODES = Object.freeze(["headless", "interactive", "either"]);
|
|
14966
|
+
var MODEL_KEYS = Object.freeze([
|
|
14967
|
+
"schema",
|
|
14968
|
+
"id",
|
|
14969
|
+
"harness",
|
|
14970
|
+
"model",
|
|
14971
|
+
"vendor",
|
|
14972
|
+
"status",
|
|
14973
|
+
"permissions",
|
|
14974
|
+
"origin",
|
|
14975
|
+
"thinking",
|
|
14976
|
+
"tags",
|
|
14977
|
+
"capabilities",
|
|
14978
|
+
"priority",
|
|
14979
|
+
"fallbacks",
|
|
14980
|
+
"limits"
|
|
14981
|
+
]);
|
|
14982
|
+
var ROLE_KEYS = Object.freeze([
|
|
14983
|
+
"schema",
|
|
14984
|
+
"id",
|
|
14985
|
+
"purpose",
|
|
14986
|
+
"permission",
|
|
14987
|
+
"description",
|
|
14988
|
+
"extends",
|
|
14989
|
+
"roster",
|
|
14990
|
+
"skills",
|
|
14991
|
+
"tools",
|
|
14992
|
+
"produces",
|
|
14993
|
+
"consumes",
|
|
14994
|
+
"policy"
|
|
14995
|
+
]);
|
|
14996
|
+
var ORIGIN_KEYS = Object.freeze(["source", "sha256"]);
|
|
14997
|
+
var LIMIT_KEYS = Object.freeze(["contextTokens", "outputTokens", "timeoutMs"]);
|
|
14998
|
+
var TOOL_KEYS = Object.freeze(["preset", "allow", "deny"]);
|
|
14999
|
+
var TEMPLATE_KEYS = Object.freeze(["template", "schema"]);
|
|
15000
|
+
var POLICY_KEYS = Object.freeze(["vendorIndependenceRequired", "maxTransitions", "requiresGateVerification", "fallback"]);
|
|
15001
|
+
var FALLBACK_KEYS = Object.freeze(["onError", "maxSwitches", "revert"]);
|
|
15002
|
+
var FALLBACK_ERRORS = Object.freeze(["rate_limit", "transport", "provider_unavailable"]);
|
|
15003
|
+
var FALLBACK_REVERT = Object.freeze(["next_run", "never"]);
|
|
15004
|
+
var ROSTER_ENTRY_KEYS = Object.freeze(["route", "effort", "mode"]);
|
|
15005
|
+
var CRITIC_PURPOSES = Object.freeze(["reviewer-arch", "reviewer-cli"]);
|
|
15006
|
+
function compareCodeUnits2(left, right) {
|
|
15007
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
15008
|
+
}
|
|
15009
|
+
function sortIssues2(issues) {
|
|
15010
|
+
return [...issues].sort((left, right) => compareCodeUnits2(left.file, right.file) || compareCodeUnits2(left.phase, right.phase) || compareCodeUnits2(left.code, right.code) || compareCodeUnits2(left.message, right.message));
|
|
15011
|
+
}
|
|
15012
|
+
function issue2(phase, code, file, message) {
|
|
15013
|
+
return { phase, code, file, message };
|
|
15014
|
+
}
|
|
15015
|
+
function isObject(value) {
|
|
15016
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
15017
|
+
}
|
|
15018
|
+
function own(object, key) {
|
|
15019
|
+
return Object.hasOwn(object, key);
|
|
15020
|
+
}
|
|
15021
|
+
function extraKeys(value, allowed) {
|
|
15022
|
+
return Object.keys(value).filter((key) => !allowed.includes(key));
|
|
15023
|
+
}
|
|
15024
|
+
function freezeDeep(value) {
|
|
15025
|
+
if (value && typeof value === "object") {
|
|
15026
|
+
for (const child of Object.values(value)) freezeDeep(child);
|
|
15027
|
+
Object.freeze(value);
|
|
15028
|
+
}
|
|
15029
|
+
return value;
|
|
15030
|
+
}
|
|
15031
|
+
function cloneJson(value) {
|
|
15032
|
+
return JSON.parse(JSON.stringify(value));
|
|
15033
|
+
}
|
|
15034
|
+
function sha256Bytes(input) {
|
|
15035
|
+
const bytes = typeof input === "string" ? Buffer.from(input, "utf8") : Buffer.from(input);
|
|
15036
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
15037
|
+
}
|
|
15038
|
+
function canonicalVendor(value, aliases) {
|
|
15039
|
+
if (typeof value !== "string") return "";
|
|
15040
|
+
const lower = value.toLowerCase();
|
|
15041
|
+
return own(aliases, lower) ? aliases[lower] : lower;
|
|
15042
|
+
}
|
|
15043
|
+
function identifier(value, max = 64) {
|
|
15044
|
+
return typeof value === "string" && value.length >= 1 && value.length <= max && IDENTIFIER.test(value);
|
|
15045
|
+
}
|
|
15046
|
+
function routeId(value) {
|
|
15047
|
+
return typeof value === "string" && value.length >= 1 && value.length <= 64 && ROUTE_ID.test(value);
|
|
15048
|
+
}
|
|
15049
|
+
function nonemptyToken(value, max = 512) {
|
|
15050
|
+
return typeof value === "string" && value.length >= 1 && value.length <= max && !TOKEN.test(value);
|
|
15051
|
+
}
|
|
15052
|
+
function codePointCount(value) {
|
|
15053
|
+
return [...value].length;
|
|
15054
|
+
}
|
|
15055
|
+
function sourcePath(value) {
|
|
15056
|
+
if (!nonemptyToken(value)) return false;
|
|
15057
|
+
if (value.startsWith("/") || value.includes("\\")) return false;
|
|
15058
|
+
const parts = value.split("/");
|
|
15059
|
+
return parts.length > 0 && parts.every((part) => part && part !== "." && part !== "..");
|
|
15060
|
+
}
|
|
15061
|
+
function uniqueStringList(value, allowed, { min = 1, max = 64 } = {}) {
|
|
15062
|
+
if (!Array.isArray(value) || value.length < min || value.length > max) return false;
|
|
15063
|
+
if (new Set(value).size !== value.length) return false;
|
|
15064
|
+
return value.every((item) => allowed.includes(item));
|
|
15065
|
+
}
|
|
15066
|
+
function identifierList(value, { maxItems = 32, itemMax = 64 } = {}) {
|
|
15067
|
+
if (!Array.isArray(value) || value.length > maxItems) return false;
|
|
15068
|
+
if (new Set(value).size !== value.length) return false;
|
|
15069
|
+
return value.every((item) => identifier(item, itemMax));
|
|
15070
|
+
}
|
|
15071
|
+
function parseDocument2(value, label, issues) {
|
|
15072
|
+
if (typeof value === "string" || value instanceof Uint8Array) {
|
|
15073
|
+
try {
|
|
15074
|
+
return parseRestrictedYaml(value, label);
|
|
15075
|
+
} catch (error) {
|
|
15076
|
+
if (error instanceof RestrictedYamlError) issues.push(...error.issues);
|
|
15077
|
+
else issues.push(issue2("parse", "invalid_yaml", label, error instanceof Error ? error.message : "invalid YAML"));
|
|
15078
|
+
return void 0;
|
|
15079
|
+
}
|
|
15080
|
+
}
|
|
15081
|
+
if (isObject(value)) return value;
|
|
15082
|
+
issues.push(issue2("schema", "root_not_object", label, "resource root must be a mapping"));
|
|
15083
|
+
return void 0;
|
|
15084
|
+
}
|
|
15085
|
+
function collectDocuments(raw, kind, issues) {
|
|
15086
|
+
const collected = [];
|
|
15087
|
+
const seen = /* @__PURE__ */ new Set();
|
|
15088
|
+
if (raw === void 0) return collected;
|
|
15089
|
+
if (Array.isArray(raw)) {
|
|
15090
|
+
for (const [index, value] of raw.entries()) {
|
|
15091
|
+
const label = `${kind}[${index}]`;
|
|
15092
|
+
const document = parseDocument2(value, label, issues);
|
|
15093
|
+
if (!document) continue;
|
|
15094
|
+
const declared = document.id;
|
|
15095
|
+
if (!routeId(declared) && !(kind === "roles" && identifier(declared))) {
|
|
15096
|
+
issues.push(issue2("schema", "missing_id", label, `${kind.slice(0, -1)} identity is missing or invalid`));
|
|
15097
|
+
continue;
|
|
15098
|
+
}
|
|
15099
|
+
if (seen.has(declared)) {
|
|
15100
|
+
issues.push(issue2("schema", kind === "models" ? "duplicate_route_id" : "duplicate_role_id", label, `duplicate ${kind.slice(0, -1)} identity ${declared}`));
|
|
15101
|
+
continue;
|
|
15102
|
+
}
|
|
15103
|
+
seen.add(declared);
|
|
15104
|
+
collected.push({ id: declared, label: `${kind}/${declared}`, document });
|
|
15105
|
+
}
|
|
15106
|
+
return collected;
|
|
15107
|
+
}
|
|
15108
|
+
if (!isObject(raw)) {
|
|
15109
|
+
issues.push(issue2("schema", "root_not_object", kind, `${kind} must be a mapping or array`));
|
|
15110
|
+
return collected;
|
|
15111
|
+
}
|
|
15112
|
+
for (const id of Object.keys(raw).sort(compareCodeUnits2)) {
|
|
15113
|
+
const label = `${kind}/${id}`;
|
|
15114
|
+
if (kind === "models" ? !routeId(id) : !identifier(id)) {
|
|
15115
|
+
issues.push(issue2("schema", "invalid_id", label, `invalid ${kind.slice(0, -1)} identity`));
|
|
15116
|
+
continue;
|
|
15117
|
+
}
|
|
15118
|
+
if (seen.has(id)) {
|
|
15119
|
+
issues.push(issue2("schema", kind === "models" ? "duplicate_route_id" : "duplicate_role_id", label, `duplicate ${kind.slice(0, -1)} identity ${id}`));
|
|
15120
|
+
continue;
|
|
15121
|
+
}
|
|
15122
|
+
seen.add(id);
|
|
15123
|
+
const document = parseDocument2(raw[id], label, issues);
|
|
15124
|
+
if (!document) continue;
|
|
15125
|
+
if (document.id !== void 0 && document.id !== id) {
|
|
15126
|
+
issues.push(issue2("schema", "identity_mismatch", label, `declared id ${String(document.id)} does not match ${id}`));
|
|
15127
|
+
continue;
|
|
15128
|
+
}
|
|
15129
|
+
collected.push({ id, label, document });
|
|
15130
|
+
}
|
|
15131
|
+
return collected;
|
|
15132
|
+
}
|
|
15133
|
+
function closedObject(value, allowed, label, issues, code = "schema_additionalProperties") {
|
|
15134
|
+
if (!isObject(value)) {
|
|
15135
|
+
issues.push(issue2("schema", "root_not_object", label, "value must be a mapping"));
|
|
15136
|
+
return false;
|
|
15137
|
+
}
|
|
15138
|
+
const unexpected = extraKeys(value, allowed);
|
|
15139
|
+
if (unexpected.length > 0) {
|
|
15140
|
+
issues.push(issue2("schema", code, label, `unexpected properties: ${unexpected.join(", ")}`));
|
|
15141
|
+
return false;
|
|
15142
|
+
}
|
|
15143
|
+
return true;
|
|
15144
|
+
}
|
|
15145
|
+
function required(value, keys, label, issues) {
|
|
15146
|
+
for (const key of keys) {
|
|
15147
|
+
if (!own(value, key)) issues.push(issue2("schema", "schema_required", label, `missing ${key}`));
|
|
15148
|
+
}
|
|
15149
|
+
}
|
|
15150
|
+
function validateOrigin(origin, label, issues) {
|
|
15151
|
+
if (!closedObject(origin, ORIGIN_KEYS, `${label}.origin`, issues)) return;
|
|
15152
|
+
required(origin, ORIGIN_KEYS, `${label}.origin`, issues);
|
|
15153
|
+
if (origin.source !== void 0 && !sourcePath(origin.source)) {
|
|
15154
|
+
issues.push(issue2("schema", "origin_source_invalid", `${label}.origin`, "origin source must be a contained relative path"));
|
|
15155
|
+
}
|
|
15156
|
+
if (origin.sha256 !== void 0 && (typeof origin.sha256 !== "string" || !SHA256.test(origin.sha256))) {
|
|
15157
|
+
issues.push(issue2("schema", "origin_hash_invalid", `${label}.origin`, "origin sha256 must be a 64-character hex digest"));
|
|
15158
|
+
}
|
|
15159
|
+
}
|
|
15160
|
+
function validateModelShape(document, id, label, issues) {
|
|
15161
|
+
if (!closedObject(document, MODEL_KEYS, label, issues)) return;
|
|
15162
|
+
required(document, ["schema", "harness", "model", "vendor", "status", "permissions"], label, issues);
|
|
15163
|
+
if (document.schema !== void 0 && document.schema !== POLICY_DRAFT_MODEL_SCHEMA) {
|
|
15164
|
+
issues.push(issue2("schema", "schema_identity_mismatch", label, `expected ${POLICY_DRAFT_MODEL_SCHEMA}, received ${String(document.schema)}`));
|
|
15165
|
+
}
|
|
15166
|
+
if (document.id !== void 0 && document.id !== id) {
|
|
15167
|
+
issues.push(issue2("schema", "identity_mismatch", label, `declared id ${String(document.id)} does not match ${id}`));
|
|
15168
|
+
}
|
|
15169
|
+
if (document.harness !== void 0 && !identifier(document.harness)) {
|
|
15170
|
+
issues.push(issue2("schema", "schema_pattern", label, "harness must be an identifier"));
|
|
15171
|
+
}
|
|
15172
|
+
if (document.model !== void 0 && (typeof document.model !== "string" || codePointCount(document.model) < 1 || codePointCount(document.model) > 200)) {
|
|
15173
|
+
issues.push(issue2("schema", "schema_pattern", label, "model must be a nonempty string"));
|
|
15174
|
+
}
|
|
15175
|
+
if (document.vendor !== void 0 && !nonemptyToken(document.vendor, 64)) {
|
|
15176
|
+
issues.push(issue2("schema", "schema_pattern", label, "vendor must be a nonempty token"));
|
|
15177
|
+
}
|
|
15178
|
+
if (document.status !== void 0 && !POLICY_DRAFT_STATUSES.includes(document.status)) {
|
|
15179
|
+
issues.push(issue2("schema", "unsupported_status", label, "status must be admitted, candidate, or retired"));
|
|
15180
|
+
}
|
|
15181
|
+
if (document.permissions !== void 0 && !uniqueStringList(document.permissions, POLICY_DRAFT_PERMISSIONS, { min: 1, max: 2 })) {
|
|
15182
|
+
issues.push(issue2("schema", "schema_enum", label, "permissions must be unique edit and/or read-only"));
|
|
15183
|
+
}
|
|
15184
|
+
if (document.harness === "pi" && !own(document, "origin")) {
|
|
15185
|
+
issues.push(issue2("schema", "schema_required", label, "Pi routes require origin source and sha256"));
|
|
15186
|
+
}
|
|
15187
|
+
if (own(document, "origin")) validateOrigin(document.origin, label, issues);
|
|
15188
|
+
if (document.thinking !== void 0 && (typeof document.thinking !== "string" || document.thinking.length < 1 || document.thinking.length > 64)) {
|
|
15189
|
+
issues.push(issue2("schema", "schema_pattern", label, "thinking must be a short string"));
|
|
15190
|
+
}
|
|
15191
|
+
if (document.tags !== void 0 && !identifierList(document.tags)) {
|
|
15192
|
+
issues.push(issue2("schema", "schema_pattern", label, "tags must be unique identifiers"));
|
|
15193
|
+
}
|
|
15194
|
+
if (document.capabilities !== void 0 && !identifierList(document.capabilities)) {
|
|
15195
|
+
issues.push(issue2("schema", "schema_pattern", label, "capabilities must be unique identifiers"));
|
|
15196
|
+
}
|
|
15197
|
+
if (document.priority !== void 0 && (!Number.isInteger(document.priority) || document.priority < -1e4 || document.priority > 1e4)) {
|
|
15198
|
+
issues.push(issue2("schema", "schema_type", label, "priority must be an integer in range"));
|
|
15199
|
+
}
|
|
15200
|
+
if (document.fallbacks !== void 0) {
|
|
15201
|
+
if (!Array.isArray(document.fallbacks) || document.fallbacks.length > 8) {
|
|
15202
|
+
issues.push(issue2("schema", "schema_type", label, "fallbacks must be a bounded array"));
|
|
15203
|
+
} else {
|
|
15204
|
+
for (const [index, fallback] of document.fallbacks.entries()) {
|
|
15205
|
+
if (!isObject(fallback)) {
|
|
15206
|
+
issues.push(issue2("schema", "root_not_object", `${label}.fallbacks[${index}]`, "fallback must be a mapping"));
|
|
15207
|
+
continue;
|
|
15208
|
+
}
|
|
15209
|
+
const keys = Object.keys(fallback);
|
|
15210
|
+
const profile = keys.length === 1 && own(fallback, "profile") && identifier(fallback.profile);
|
|
15211
|
+
const tag = own(fallback, "tag") && identifier(fallback.tag) && extraKeys(fallback, ["tag", "capabilities"]).length === 0 && (fallback.capabilities === void 0 || identifierList(fallback.capabilities));
|
|
15212
|
+
const provider = own(fallback, "provider") && own(fallback, "model") && extraKeys(fallback, ["provider", "model"]).length === 0 && identifier(fallback.provider) && typeof fallback.model === "string" && codePointCount(fallback.model) >= 1 && codePointCount(fallback.model) <= 200;
|
|
15213
|
+
if (!profile && !tag && !provider) {
|
|
15214
|
+
issues.push(issue2("schema", "schema_oneOf", `${label}.fallbacks[${index}]`, "fallback does not match a retained v1 selector"));
|
|
15215
|
+
}
|
|
15216
|
+
}
|
|
15217
|
+
}
|
|
15218
|
+
}
|
|
15219
|
+
if (document.limits !== void 0) {
|
|
15220
|
+
if (!closedObject(document.limits, LIMIT_KEYS, `${label}.limits`, issues)) return;
|
|
15221
|
+
for (const key of ["contextTokens", "outputTokens"]) {
|
|
15222
|
+
if (document.limits[key] !== void 0 && (!Number.isInteger(document.limits[key]) || document.limits[key] < 1)) {
|
|
15223
|
+
issues.push(issue2("schema", "schema_type", `${label}.limits`, `${key} must be a positive integer`));
|
|
15224
|
+
}
|
|
15225
|
+
}
|
|
15226
|
+
if (document.limits.timeoutMs !== void 0 && (!Number.isInteger(document.limits.timeoutMs) || document.limits.timeoutMs < 0 || document.limits.timeoutMs > 31536e6)) {
|
|
15227
|
+
issues.push(issue2("schema", "schema_type", `${label}.limits`, "timeoutMs must be a non-negative integer between 0 and 31536000000"));
|
|
15228
|
+
}
|
|
15229
|
+
}
|
|
15230
|
+
}
|
|
15231
|
+
function validateTemplateList(value, label, issues) {
|
|
15232
|
+
if (!Array.isArray(value)) {
|
|
15233
|
+
issues.push(issue2("schema", "schema_type", label, "must be an array"));
|
|
15234
|
+
return;
|
|
15235
|
+
}
|
|
15236
|
+
for (const [index, entry] of value.entries()) {
|
|
15237
|
+
const path = `${label}[${index}]`;
|
|
15238
|
+
if (!closedObject(entry, TEMPLATE_KEYS, path, issues)) continue;
|
|
15239
|
+
if (typeof entry.template !== "string" || !entry.template) {
|
|
15240
|
+
issues.push(issue2("schema", "schema_required", path, "template is required"));
|
|
15241
|
+
}
|
|
15242
|
+
if (entry.schema !== void 0 && typeof entry.schema !== "string") {
|
|
15243
|
+
issues.push(issue2("schema", "schema_type", path, "schema must be a string"));
|
|
15244
|
+
}
|
|
15245
|
+
}
|
|
15246
|
+
}
|
|
15247
|
+
function validateRoleShape(document, id, label, issues) {
|
|
15248
|
+
if (!closedObject(document, ROLE_KEYS, label, issues)) return;
|
|
15249
|
+
required(document, ["schema", "purpose", "permission", "description", "roster"], label, issues);
|
|
15250
|
+
if (document.schema !== void 0 && document.schema !== POLICY_DRAFT_ROLE_SCHEMA) {
|
|
15251
|
+
issues.push(issue2("schema", "schema_identity_mismatch", label, `expected ${POLICY_DRAFT_ROLE_SCHEMA}, received ${String(document.schema)}`));
|
|
15252
|
+
}
|
|
15253
|
+
if (document.id !== void 0 && document.id !== id) {
|
|
15254
|
+
issues.push(issue2("schema", "identity_mismatch", label, `declared id ${String(document.id)} does not match ${id}`));
|
|
15255
|
+
}
|
|
15256
|
+
if (document.extends !== void 0 && !identifier(document.extends)) {
|
|
15257
|
+
issues.push(issue2("schema", "schema_pattern", label, "extends must be a role identifier"));
|
|
15258
|
+
}
|
|
15259
|
+
if (document.purpose !== void 0 && !POLICY_DRAFT_PURPOSES.includes(document.purpose)) {
|
|
15260
|
+
issues.push(issue2("schema", "unsupported_purpose", label, "purpose must be a runner role label"));
|
|
15261
|
+
}
|
|
15262
|
+
if (document.permission !== void 0 && !POLICY_DRAFT_PERMISSIONS.includes(document.permission)) {
|
|
15263
|
+
issues.push(issue2("schema", "schema_enum", label, "permission must be edit or read-only"));
|
|
15264
|
+
}
|
|
15265
|
+
if (document.description !== void 0 && (typeof document.description !== "string" || document.description.length < 1 || document.description.length > 2e3)) {
|
|
15266
|
+
issues.push(issue2("schema", "schema_type", label, "description must be a bounded string"));
|
|
15267
|
+
}
|
|
15268
|
+
if (document.roster !== void 0) {
|
|
15269
|
+
if (!Array.isArray(document.roster) || document.roster.length > 32) {
|
|
15270
|
+
issues.push(issue2("schema", "schema_type", label, "roster must be a bounded array"));
|
|
15271
|
+
} else {
|
|
15272
|
+
const seenRoutes = /* @__PURE__ */ new Set();
|
|
15273
|
+
for (const [index, entry] of document.roster.entries()) {
|
|
15274
|
+
const path = `${label}.roster[${index}]`;
|
|
15275
|
+
if (!closedObject(entry, ROSTER_ENTRY_KEYS, path, issues)) continue;
|
|
15276
|
+
if (!routeId(entry.route)) {
|
|
15277
|
+
issues.push(issue2("schema", "schema_pattern", path, "roster route must be a route identity"));
|
|
15278
|
+
continue;
|
|
15279
|
+
}
|
|
15280
|
+
if (seenRoutes.has(entry.route)) {
|
|
15281
|
+
issues.push(issue2("schema", "duplicate_route_id", path, `duplicate roster route ${entry.route}`));
|
|
15282
|
+
}
|
|
15283
|
+
seenRoutes.add(entry.route);
|
|
15284
|
+
if (entry.effort !== void 0 && !EFFORTS.includes(entry.effort)) {
|
|
15285
|
+
issues.push(issue2("schema", "schema_enum", path, "effort is not a known helper effort"));
|
|
15286
|
+
}
|
|
15287
|
+
if (entry.mode !== void 0 && !MODES.includes(entry.mode)) {
|
|
15288
|
+
issues.push(issue2("schema", "schema_enum", path, "mode must be headless, interactive, or either"));
|
|
15289
|
+
}
|
|
15290
|
+
}
|
|
15291
|
+
}
|
|
15292
|
+
}
|
|
15293
|
+
if (document.skills !== void 0) {
|
|
15294
|
+
if (!Array.isArray(document.skills) || document.skills.length > 64 || document.skills.some((item) => typeof item !== "string" || item.length < 1 || item.length > 128)) {
|
|
15295
|
+
issues.push(issue2("schema", "schema_type", label, "skills must be bounded strings"));
|
|
15296
|
+
}
|
|
15297
|
+
}
|
|
15298
|
+
if (document.tools !== void 0) {
|
|
15299
|
+
if (!closedObject(document.tools, TOOL_KEYS, `${label}.tools`, issues)) return;
|
|
15300
|
+
if (document.tools.preset !== void 0 && typeof document.tools.preset !== "string") {
|
|
15301
|
+
issues.push(issue2("schema", "schema_type", `${label}.tools`, "preset must be a string"));
|
|
15302
|
+
}
|
|
15303
|
+
for (const key of ["allow", "deny"]) {
|
|
15304
|
+
if (document.tools[key] !== void 0 && (!Array.isArray(document.tools[key]) || document.tools[key].some((item) => typeof item !== "string"))) {
|
|
15305
|
+
issues.push(issue2("schema", "schema_type", `${label}.tools`, `${key} must be a string array`));
|
|
15306
|
+
}
|
|
15307
|
+
}
|
|
15308
|
+
}
|
|
15309
|
+
if (document.produces !== void 0) validateTemplateList(document.produces, `${label}.produces`, issues);
|
|
15310
|
+
if (document.consumes !== void 0) validateTemplateList(document.consumes, `${label}.consumes`, issues);
|
|
15311
|
+
if (document.policy !== void 0) {
|
|
15312
|
+
if (!closedObject(document.policy, POLICY_KEYS, `${label}.policy`, issues)) return;
|
|
15313
|
+
if (document.policy.vendorIndependenceRequired !== void 0 && typeof document.policy.vendorIndependenceRequired !== "boolean") {
|
|
15314
|
+
issues.push(issue2("schema", "schema_type", `${label}.policy`, "vendorIndependenceRequired must be boolean"));
|
|
15315
|
+
}
|
|
15316
|
+
if (document.policy.requiresGateVerification !== void 0 && typeof document.policy.requiresGateVerification !== "boolean") {
|
|
15317
|
+
issues.push(issue2("schema", "schema_type", `${label}.policy`, "requiresGateVerification must be boolean"));
|
|
15318
|
+
}
|
|
15319
|
+
if (document.policy.maxTransitions !== void 0 && (!Number.isInteger(document.policy.maxTransitions) || document.policy.maxTransitions < 1)) {
|
|
15320
|
+
issues.push(issue2("schema", "schema_type", `${label}.policy`, "maxTransitions must be a positive integer"));
|
|
15321
|
+
}
|
|
15322
|
+
if (document.policy.fallback !== void 0) {
|
|
15323
|
+
const fallback = document.policy.fallback;
|
|
15324
|
+
const path = `${label}.policy.fallback`;
|
|
15325
|
+
if (!closedObject(fallback, FALLBACK_KEYS, path, issues)) return;
|
|
15326
|
+
if (fallback.onError !== void 0) {
|
|
15327
|
+
if (!Array.isArray(fallback.onError) || new Set(fallback.onError).size !== fallback.onError.length || fallback.onError.some((item) => !FALLBACK_ERRORS.includes(item))) {
|
|
15328
|
+
issues.push(issue2("schema", "schema_enum", path, "onError must be unique rate_limit, transport, or provider_unavailable values"));
|
|
15329
|
+
}
|
|
15330
|
+
}
|
|
15331
|
+
if (fallback.maxSwitches !== void 0 && (!Number.isInteger(fallback.maxSwitches) || fallback.maxSwitches < 0)) {
|
|
15332
|
+
issues.push(issue2("schema", "schema_type", path, "maxSwitches must be an integer >= 0"));
|
|
15333
|
+
}
|
|
15334
|
+
if (fallback.revert !== void 0 && !FALLBACK_REVERT.includes(fallback.revert)) {
|
|
15335
|
+
issues.push(issue2("schema", "schema_enum", path, "revert must be next_run or never"));
|
|
15336
|
+
}
|
|
15337
|
+
}
|
|
15338
|
+
}
|
|
15339
|
+
}
|
|
15340
|
+
function subset(values, allowed) {
|
|
15341
|
+
return values.every((value) => allowed.includes(value));
|
|
15342
|
+
}
|
|
15343
|
+
function validateModelSemantics(model, label, options, evidence, issues) {
|
|
15344
|
+
const aliases = options.vendorAliases ?? {};
|
|
15345
|
+
const ceiling = options.ceilings[model.harness];
|
|
15346
|
+
if (!ceiling) {
|
|
15347
|
+
issues.push(issue2("semantic", "unsupported_harness", label, `no code-owned ceiling for harness ${String(model.harness)}`));
|
|
15348
|
+
return;
|
|
15349
|
+
}
|
|
15350
|
+
const vendor = canonicalVendor(model.vendor, aliases);
|
|
15351
|
+
if (options.piAllowedProviders.includes(vendor)) {
|
|
15352
|
+
issues.push(issue2("semantic", "vendor_aggregator", label, "vendor must name the model vendor, not a billing aggregator"));
|
|
15353
|
+
}
|
|
15354
|
+
if (!subset(model.permissions, ceiling.permissions)) {
|
|
15355
|
+
issues.push(issue2("semantic", "permission_escalation", label, "route permissions exceed the harness ceiling"));
|
|
15356
|
+
}
|
|
15357
|
+
if (Array.isArray(ceiling.models) && ceiling.models.length > 0 && !ceiling.models.includes(model.model)) {
|
|
15358
|
+
issues.push(issue2("semantic", "model_not_in_ceiling", label, "exact model is not listed on the harness ceiling"));
|
|
15359
|
+
}
|
|
15360
|
+
if (model.harness === "pi") {
|
|
15361
|
+
const parts = typeof model.model === "string" ? model.model.split("/") : [];
|
|
15362
|
+
const nativeVendors = isObject(options.piNativeVendorProviders) ? options.piNativeVendorProviders : {};
|
|
15363
|
+
const provider = parts[0];
|
|
15364
|
+
const owned = own(nativeVendors, provider) ? canonicalVendor(nativeVendors[provider], aliases) : "";
|
|
15365
|
+
const minParts = owned ? 2 : 3;
|
|
15366
|
+
const maxParts = owned ? 2 : Number.POSITIVE_INFINITY;
|
|
15367
|
+
if (parts.length < minParts || parts.length > maxParts || parts.some((part) => !part) || !options.piAllowedProviders.includes(provider) || owned && !/^gemini-[a-z0-9.-]+$/.test(parts[1])) {
|
|
15368
|
+
issues.push(issue2("semantic", "unsupported_pi_model", label, "Pi model must use an allowlisted aggregator prefix"));
|
|
15369
|
+
} else if (owned) {
|
|
15370
|
+
if (owned !== vendor) {
|
|
15371
|
+
issues.push(issue2("semantic", "pi_native_vendor_forbidden", label, "native vendor cannot use Pi"));
|
|
15372
|
+
}
|
|
15373
|
+
} else {
|
|
15374
|
+
const prefix = canonicalVendor(parts[1], aliases);
|
|
15375
|
+
if (options.nativePiBrakeProviders.includes(prefix) || options.nativePiBrakeProviders.includes(vendor)) {
|
|
15376
|
+
issues.push(issue2("semantic", "pi_native_vendor_forbidden", label, "native vendor cannot use Pi"));
|
|
15377
|
+
}
|
|
15378
|
+
}
|
|
15379
|
+
} else if (vendor !== ceiling.provider || typeof model.model === "string" && model.model.includes("/")) {
|
|
15380
|
+
issues.push(issue2("semantic", "native_vendor_mismatch", label, "native route vendor or model does not match the harness ceiling"));
|
|
15381
|
+
}
|
|
15382
|
+
const origin = model.origin;
|
|
15383
|
+
if (isObject(origin) && typeof origin.source === "string" && typeof origin.sha256 === "string") {
|
|
15384
|
+
if (!own(evidence, origin.source)) {
|
|
15385
|
+
issues.push(issue2("semantic", "origin_evidence_missing", label, `missing evidence bytes for ${origin.source}`));
|
|
15386
|
+
} else if (sha256Bytes(evidence[origin.source]) !== origin.sha256) {
|
|
15387
|
+
issues.push(issue2("semantic", "origin_hash_mismatch", label, "origin evidence hash does not match supplied bytes"));
|
|
15388
|
+
}
|
|
15389
|
+
}
|
|
15390
|
+
}
|
|
15391
|
+
function admittedRoute(model) {
|
|
15392
|
+
return model.status === "admitted";
|
|
15393
|
+
}
|
|
15394
|
+
function validateRoleSemantics(role, id, label, models, options, issues) {
|
|
15395
|
+
if (!Array.isArray(role.roster)) return;
|
|
15396
|
+
for (const [index, entry] of role.roster.entries()) {
|
|
15397
|
+
const path = `${label}.roster[${index}]`;
|
|
15398
|
+
const model = models.get(entry.route);
|
|
15399
|
+
if (!model) {
|
|
15400
|
+
issues.push(issue2("reference", "route_unknown", path, `roster references unknown route ${entry.route}`));
|
|
15401
|
+
continue;
|
|
15402
|
+
}
|
|
15403
|
+
const ceiling = options.ceilings[model.harness];
|
|
15404
|
+
if (!ceiling) continue;
|
|
15405
|
+
if (Array.isArray(ceiling.roles) && ceiling.roles.length > 0 && !ceiling.roles.includes(role.purpose)) {
|
|
15406
|
+
issues.push(issue2("semantic", "purpose_ceiling_mismatch", path, `purpose ${role.purpose} is not allowed on harness ${model.harness}`));
|
|
15407
|
+
}
|
|
15408
|
+
if (!model.permissions.includes(role.permission)) {
|
|
15409
|
+
issues.push(issue2("semantic", "permission_escalation", path, "role permission is not granted by the route"));
|
|
15410
|
+
}
|
|
15411
|
+
if (!ceiling.permissions.includes(role.permission)) {
|
|
15412
|
+
issues.push(issue2("semantic", "permission_escalation", path, "role permission exceeds the harness ceiling"));
|
|
15413
|
+
}
|
|
15414
|
+
if (Array.isArray(ceiling.efforts) && ceiling.efforts.length > 0 && entry.effort !== void 0 && !ceiling.efforts.includes(entry.effort)) {
|
|
15415
|
+
issues.push(issue2("semantic", "effort_ceiling_mismatch", path, "effort is not allowed on the harness ceiling"));
|
|
15416
|
+
}
|
|
15417
|
+
if (!admittedRoute(model)) {
|
|
15418
|
+
issues.push(issue2("semantic", "route_not_admitted", path, `route ${entry.route} is ${model.status} and is not treated as admitted`));
|
|
15419
|
+
}
|
|
15420
|
+
if (model.harness === "pi") {
|
|
15421
|
+
if (role.permission === "edit" && !["writer", "experiment"].includes(role.purpose)) {
|
|
15422
|
+
issues.push(issue2("semantic", "permission_escalation", path, "Pi critic/planner cannot edit"));
|
|
15423
|
+
}
|
|
15424
|
+
if (role.purpose === "writer" && (model.permissions.length !== 1 || model.permissions[0] !== "edit")) {
|
|
15425
|
+
issues.push(issue2("semantic", "permission_escalation", path, "Pi writer requires edit permission only"));
|
|
15426
|
+
}
|
|
15427
|
+
}
|
|
15428
|
+
}
|
|
15429
|
+
if (id === role.purpose && CRITIC_PURPOSES.includes(role.purpose)) {
|
|
15430
|
+
const admitted = role.roster.map((entry) => models.get(entry.route)).filter((model) => model && admittedRoute(model));
|
|
15431
|
+
if (admitted.length !== 1) {
|
|
15432
|
+
issues.push(issue2("semantic", "critic_route_count", label, "required critic purpose files must list exactly one admitted route"));
|
|
15433
|
+
} else if (admitted[0].permissions.length !== 1 || admitted[0].permissions[0] !== "read-only" || role.permission !== "read-only") {
|
|
15434
|
+
issues.push(issue2("semantic", "permission_escalation", label, "critic purpose files must be read-only"));
|
|
15435
|
+
}
|
|
15436
|
+
}
|
|
15437
|
+
}
|
|
15438
|
+
function validateExtends(roles, issues) {
|
|
15439
|
+
for (const [id, role] of roles) {
|
|
15440
|
+
if (role.extends === void 0) continue;
|
|
15441
|
+
const label = `roles/${id}`;
|
|
15442
|
+
if (!roles.has(role.extends)) {
|
|
15443
|
+
issues.push(issue2("reference", "role_extends_unknown", label, `extends names unknown role ${role.extends}`));
|
|
15444
|
+
continue;
|
|
15445
|
+
}
|
|
15446
|
+
const seen = /* @__PURE__ */ new Set([id]);
|
|
15447
|
+
let current = role.extends;
|
|
15448
|
+
while (typeof current === "string" && current.length > 0) {
|
|
15449
|
+
if (seen.has(current)) {
|
|
15450
|
+
issues.push(issue2("semantic", "role_extends_cycle", label, `extends cycle: ${[...seen, current].join(" -> ")}`));
|
|
15451
|
+
break;
|
|
15452
|
+
}
|
|
15453
|
+
seen.add(current);
|
|
15454
|
+
current = roles.get(current)?.extends;
|
|
15455
|
+
}
|
|
15456
|
+
}
|
|
15457
|
+
}
|
|
15458
|
+
function validateCriticVendors(roles, models, options, issues) {
|
|
15459
|
+
const aliases = options.vendorAliases ?? {};
|
|
15460
|
+
const critics = [];
|
|
15461
|
+
for (const purpose of CRITIC_PURPOSES) {
|
|
15462
|
+
const role = roles.get(purpose);
|
|
15463
|
+
if (!role || role.purpose !== purpose) continue;
|
|
15464
|
+
const admitted = (role.roster ?? []).map((entry) => models.get(entry.route)).filter((model) => model && admittedRoute(model));
|
|
15465
|
+
if (admitted.length === 1) critics.push({ purpose, vendor: canonicalVendor(admitted[0].vendor, aliases) });
|
|
15466
|
+
}
|
|
15467
|
+
if (critics.length === 2 && critics[0].vendor === critics[1].vendor) {
|
|
15468
|
+
issues.push(issue2("semantic", "critic_vendor_collision", "roles/reviewer-arch", "required critics must have independent vendors"));
|
|
15469
|
+
}
|
|
15470
|
+
const writer = roles.get("writer");
|
|
15471
|
+
if (writer && critics.length > 0) {
|
|
15472
|
+
for (const entry of writer.roster ?? []) {
|
|
15473
|
+
const model = models.get(entry.route);
|
|
15474
|
+
if (!model || !admittedRoute(model)) continue;
|
|
15475
|
+
const vendor = canonicalVendor(model.vendor, aliases);
|
|
15476
|
+
if (critics.some((critic) => critic.vendor === vendor)) {
|
|
15477
|
+
issues.push(issue2("semantic", "writer_critic_vendor_collision", "roles/writer", "writer and critics must have independent vendors"));
|
|
15478
|
+
}
|
|
15479
|
+
}
|
|
15480
|
+
}
|
|
15481
|
+
}
|
|
15482
|
+
function validatePolicyDraft(input, options) {
|
|
15483
|
+
const issues = [];
|
|
15484
|
+
if (!isObject(input)) {
|
|
15485
|
+
return { ok: false, issues: [issue2("schema", "root_not_object", "<draft>", "draft input must be a mapping")] };
|
|
15486
|
+
}
|
|
15487
|
+
const unexpected = extraKeys(input, ["models", "roles", "evidence"]);
|
|
15488
|
+
if (unexpected.length > 0) {
|
|
15489
|
+
issues.push(issue2("schema", "schema_additionalProperties", "<draft>", `unexpected properties: ${unexpected.join(", ")}`));
|
|
15490
|
+
}
|
|
15491
|
+
if (!isObject(options) || !isObject(options.ceilings) || !Array.isArray(options.nativePiBrakeProviders) || !Array.isArray(options.piAllowedProviders)) {
|
|
15492
|
+
issues.push(issue2("semantic", "ceilings_required", "<options>", "code-owned ceilings and vendor brakes must be supplied explicitly"));
|
|
15493
|
+
return { ok: false, issues: sortIssues2(issues) };
|
|
15494
|
+
}
|
|
15495
|
+
const evidence = isObject(input.evidence) ? input.evidence : {};
|
|
15496
|
+
if (input.evidence !== void 0 && !isObject(input.evidence)) {
|
|
15497
|
+
issues.push(issue2("schema", "root_not_object", "evidence", "evidence must be a mapping of source path to bytes"));
|
|
15498
|
+
}
|
|
15499
|
+
const modelDocs = collectDocuments(input.models, "models", issues);
|
|
15500
|
+
const roleDocs = collectDocuments(input.roles, "roles", issues);
|
|
15501
|
+
for (const entry of modelDocs) validateModelShape(entry.document, entry.id, entry.label, issues);
|
|
15502
|
+
for (const entry of roleDocs) validateRoleShape(entry.document, entry.id, entry.label, issues);
|
|
15503
|
+
if (issues.length > 0) return { ok: false, issues: sortIssues2(issues) };
|
|
15504
|
+
const models = /* @__PURE__ */ new Map();
|
|
15505
|
+
for (const entry of modelDocs) {
|
|
15506
|
+
validateModelSemantics(entry.document, entry.label, options, evidence, issues);
|
|
15507
|
+
const normalized = cloneJson(entry.document);
|
|
15508
|
+
normalized.id = entry.id;
|
|
15509
|
+
models.set(entry.id, normalized);
|
|
15510
|
+
}
|
|
15511
|
+
const roles = /* @__PURE__ */ new Map();
|
|
15512
|
+
for (const entry of roleDocs) {
|
|
15513
|
+
validateRoleSemantics(entry.document, entry.id, entry.label, models, options, issues);
|
|
15514
|
+
const normalized = cloneJson(entry.document);
|
|
15515
|
+
normalized.id = entry.id;
|
|
15516
|
+
roles.set(entry.id, normalized);
|
|
15517
|
+
}
|
|
15518
|
+
validateExtends(roles, issues);
|
|
15519
|
+
validateCriticVendors(roles, models, options, issues);
|
|
15520
|
+
if (issues.length > 0) return { ok: false, issues: sortIssues2(issues) };
|
|
15521
|
+
const data = freezeDeep({
|
|
15522
|
+
models: Object.fromEntries([...models.entries()].sort(([left], [right]) => compareCodeUnits2(left, right))),
|
|
15523
|
+
roles: Object.fromEntries([...roles.entries()].sort(([left], [right]) => compareCodeUnits2(left, right)))
|
|
15524
|
+
});
|
|
15525
|
+
return { ok: true, data };
|
|
15526
|
+
}
|
|
15527
|
+
|
|
14944
15528
|
// plugins/kxm/src/template.ts
|
|
14945
15529
|
var import_yaml2 = __toESM(require_dist(), 1);
|
|
14946
|
-
import { createHash } from "node:crypto";
|
|
15530
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
14947
15531
|
var KXM_TEMPLATE_ID = "builtin-minimal";
|
|
14948
15532
|
var KXM_TEMPLATE_PROVENANCE_PATH = ".kxm/template-provenance.yaml";
|
|
14949
15533
|
var CURRENT_KXM_TEMPLATE_VARIANT = "v4-registry";
|
|
14950
15534
|
var SUPPORTED_KXM_TEMPLATE_VARIANTS = ["v1", "v2", "v3-policy", "v4-registry"];
|
|
14951
|
-
function
|
|
15535
|
+
function compareCodeUnits3(left, right) {
|
|
14952
15536
|
return left < right ? -1 : left > right ? 1 : 0;
|
|
14953
15537
|
}
|
|
14954
15538
|
function canonicalJson(value) {
|
|
@@ -14956,10 +15540,10 @@ function canonicalJson(value) {
|
|
|
14956
15540
|
return JSON.stringify(value);
|
|
14957
15541
|
}
|
|
14958
15542
|
if (Array.isArray(value)) return `[${value.map((candidate) => canonicalJson(candidate)).join(",")}]`;
|
|
14959
|
-
return `{${Object.keys(value).sort(
|
|
15543
|
+
return `{${Object.keys(value).sort(compareCodeUnits3).map((key) => `${JSON.stringify(key)}:${canonicalJson(value[key])}`).join(",")}}`;
|
|
14960
15544
|
}
|
|
14961
15545
|
function kxmContentSha256(input) {
|
|
14962
|
-
return `sha256:${
|
|
15546
|
+
return `sha256:${createHash2("sha256").update(input).digest("hex")}`;
|
|
14963
15547
|
}
|
|
14964
15548
|
function authorityProjection(value) {
|
|
14965
15549
|
const projected = structuredClone(value);
|
|
@@ -15104,8 +15688,41 @@ function coreTemplate(projectId, projectName, variant) {
|
|
|
15104
15688
|
schema: "kxm.routes.v2",
|
|
15105
15689
|
updatedAt: "2026-09-23T00:00:00.000Z",
|
|
15106
15690
|
admitted: ["anthropic/fable", "xai/grok-4.6"],
|
|
15107
|
-
disabled: []
|
|
15108
|
-
|
|
15691
|
+
disabled: []
|
|
15692
|
+
});
|
|
15693
|
+
files.set(".kxm/models/grok-default.yaml", {
|
|
15694
|
+
schema: "kxm.model.v2",
|
|
15695
|
+
id: "grok-default",
|
|
15696
|
+
harness: "grok",
|
|
15697
|
+
model: "grok-4.6",
|
|
15698
|
+
vendor: "xai",
|
|
15699
|
+
status: "admitted",
|
|
15700
|
+
permissions: ["edit"]
|
|
15701
|
+
});
|
|
15702
|
+
files.set(".kxm/models/fable-default.yaml", {
|
|
15703
|
+
schema: "kxm.model.v2",
|
|
15704
|
+
id: "fable-default",
|
|
15705
|
+
harness: "claude",
|
|
15706
|
+
model: "fable",
|
|
15707
|
+
vendor: "anthropic",
|
|
15708
|
+
status: "admitted",
|
|
15709
|
+
permissions: ["read-only"]
|
|
15710
|
+
});
|
|
15711
|
+
files.set(".kxm/roles/writer.yaml", {
|
|
15712
|
+
schema: "kxm.role.v2",
|
|
15713
|
+
id: "writer",
|
|
15714
|
+
purpose: "writer",
|
|
15715
|
+
permission: "edit",
|
|
15716
|
+
description: "Primary implementation agent.",
|
|
15717
|
+
roster: [{ route: "grok-default" }]
|
|
15718
|
+
});
|
|
15719
|
+
files.set(".kxm/roles/planner.yaml", {
|
|
15720
|
+
schema: "kxm.role.v2",
|
|
15721
|
+
id: "planner",
|
|
15722
|
+
purpose: "planner",
|
|
15723
|
+
permission: "read-only",
|
|
15724
|
+
description: "Plans the change before implementation.",
|
|
15725
|
+
roster: [{ route: "fable-default" }]
|
|
15109
15726
|
});
|
|
15110
15727
|
files.set(".kxm/gates.yaml", {
|
|
15111
15728
|
schema: "kxm.gate-registry.v1",
|
|
@@ -15134,7 +15751,7 @@ function renderKxmTemplate(projectId, projectName, variant = CURRENT_KXM_TEMPLAT
|
|
|
15134
15751
|
authoritySha256: kxmAuthoritySha256(value),
|
|
15135
15752
|
bytes: bytes.byteLength
|
|
15136
15753
|
};
|
|
15137
|
-
}).sort((left, right) =>
|
|
15754
|
+
}).sort((left, right) => compareCodeUnits3(left.path, right.path));
|
|
15138
15755
|
const templateRevision = provenanceRevision(records);
|
|
15139
15756
|
const provenance = {
|
|
15140
15757
|
schema: "kxm.template-provenance.v1",
|
|
@@ -15148,7 +15765,7 @@ function renderKxmTemplate(projectId, projectName, variant = CURRENT_KXM_TEMPLAT
|
|
|
15148
15765
|
KXM_TEMPLATE_PROVENANCE_PATH,
|
|
15149
15766
|
Buffer.from((0, import_yaml2.stringify)(provenance, { lineWidth: 0 }), "utf8")
|
|
15150
15767
|
]);
|
|
15151
|
-
fileEntries.sort(([left], [right]) =>
|
|
15768
|
+
fileEntries.sort(([left], [right]) => compareCodeUnits3(left, right));
|
|
15152
15769
|
const files = new Map(fileEntries);
|
|
15153
15770
|
return { projectId, projectName, templateRevision, provenance, files, values };
|
|
15154
15771
|
}
|
|
@@ -15984,7 +16601,7 @@ function resolveDispatchStatus(entry, detected, authenticated, issues) {
|
|
|
15984
16601
|
return { status: "no", supported: false, reason: "not_authenticated" };
|
|
15985
16602
|
}
|
|
15986
16603
|
if (authenticated === null) {
|
|
15987
|
-
const authIssue = issues.find((
|
|
16604
|
+
const authIssue = issues.find((issue4) => issue4 === "auth_context_required" || issue4 === "auth_unknown" || issue4 === "auth_unparsed" || issue4.startsWith("auth_"));
|
|
15988
16605
|
return { status: "no", supported: false, reason: authIssue ?? issues[0] ?? "auth_unknown" };
|
|
15989
16606
|
}
|
|
15990
16607
|
return { status: "yes", supported: true };
|
|
@@ -16331,8 +16948,8 @@ async function probeHarnessAssignmentAsync(options) {
|
|
|
16331
16948
|
var KxmConfigError = class extends Error {
|
|
16332
16949
|
issues;
|
|
16333
16950
|
constructor(issues) {
|
|
16334
|
-
const sorted =
|
|
16335
|
-
super(sorted.map((
|
|
16951
|
+
const sorted = sortIssues3(issues);
|
|
16952
|
+
super(sorted.map((issue4) => `${issue4.file}: ${issue4.code}: ${issue4.message}`).join("\n"));
|
|
16336
16953
|
this.name = "KxmConfigError";
|
|
16337
16954
|
this.issues = sorted;
|
|
16338
16955
|
}
|
|
@@ -16345,12 +16962,13 @@ var RESOURCE_SCHEMA = Object.freeze({
|
|
|
16345
16962
|
project: { identity: "kxm.project.v1", file: "project.schema.json" },
|
|
16346
16963
|
repository: { identity: "kxm.repository.v1", file: "repository.schema.json" },
|
|
16347
16964
|
agent: { identity: "kxm.agent.v1", file: "agent.schema.json" },
|
|
16348
|
-
model: { identity: "kxm.model.
|
|
16965
|
+
model: { identity: "kxm.model.v2", file: "model.schema.json" },
|
|
16966
|
+
role: { identity: "kxm.role.v2", file: "role.schema.json" },
|
|
16349
16967
|
environment: { identity: "kxm.environment.v1", file: "environment.schema.json" },
|
|
16350
16968
|
workflow: { identity: "kxm.workflow.v1", file: "workflow.schema.json" },
|
|
16351
16969
|
"gate-registry": { identity: "kxm.gate-registry.v1", file: "gate-registry.schema.json" }
|
|
16352
16970
|
});
|
|
16353
|
-
var
|
|
16971
|
+
var IDENTIFIER2 = /^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$/;
|
|
16354
16972
|
var WINDOWS_RESERVED = /^(?:con|prn|aux|nul|com[1-9¹²³]|lpt[1-9¹²³]|conin\$|conout\$|clock\$)$/i;
|
|
16355
16973
|
var BUILTIN_EXECUTORS = ["local", "ssh", "exe-dev"];
|
|
16356
16974
|
var BUILTIN_TOOL_PRESETS = ["coordinator", "read-only", "workspace-writer", "tests-writer"];
|
|
@@ -16362,17 +16980,17 @@ var SECRET_VALUE_PATTERNS = [
|
|
|
16362
16980
|
/\bAKIA[A-Z0-9]{16}\b/,
|
|
16363
16981
|
/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/
|
|
16364
16982
|
];
|
|
16365
|
-
function
|
|
16983
|
+
function compareCodeUnits4(left, right) {
|
|
16366
16984
|
return left < right ? -1 : left > right ? 1 : 0;
|
|
16367
16985
|
}
|
|
16368
|
-
function
|
|
16369
|
-
return [...issues].sort((left, right) =>
|
|
16986
|
+
function sortIssues3(issues) {
|
|
16987
|
+
return [...issues].sort((left, right) => compareCodeUnits4(left.file, right.file) || compareCodeUnits4(left.phase, right.phase) || compareCodeUnits4(left.code, right.code) || compareCodeUnits4(left.message, right.message));
|
|
16370
16988
|
}
|
|
16371
|
-
function
|
|
16989
|
+
function issue3(phase, code, file, message) {
|
|
16372
16990
|
return { phase, code, file, message };
|
|
16373
16991
|
}
|
|
16374
16992
|
function fail2(phase, code, file, message) {
|
|
16375
|
-
throw new KxmConfigError([
|
|
16993
|
+
throw new KxmConfigError([issue3(phase, code, file, message)]);
|
|
16376
16994
|
}
|
|
16377
16995
|
function isJsonObject2(value) {
|
|
16378
16996
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -16465,7 +17083,7 @@ var KxmSchemaRegistry = class {
|
|
|
16465
17083
|
validate(kind, value, file) {
|
|
16466
17084
|
const definition = RESOURCE_SCHEMA[kind];
|
|
16467
17085
|
if (value.schema !== definition.identity) {
|
|
16468
|
-
return [
|
|
17086
|
+
return [issue3("schema", "schema_identity_mismatch", file, `expected ${definition.identity}, received ${String(value.schema)}`)];
|
|
16469
17087
|
}
|
|
16470
17088
|
const validator = this.validators.get(kind);
|
|
16471
17089
|
if (!validator) throw new Error(`missing KXM validator for ${kind}`);
|
|
@@ -16489,7 +17107,7 @@ var KxmSchemaRegistry = class {
|
|
|
16489
17107
|
}
|
|
16490
17108
|
validateAuxiliary(value, file, identity, validator) {
|
|
16491
17109
|
if (value.schema !== identity) {
|
|
16492
|
-
return [
|
|
17110
|
+
return [issue3("schema", "schema_identity_mismatch", file, `expected ${identity}, received ${String(value.schema)}`)];
|
|
16493
17111
|
}
|
|
16494
17112
|
if (validator(value)) return [];
|
|
16495
17113
|
return (validator.errors ?? []).map((error) => schemaIssue(file, error));
|
|
@@ -16499,7 +17117,7 @@ var cachedRunEventRegistry;
|
|
|
16499
17117
|
function validateRunEvent(value, file) {
|
|
16500
17118
|
const registry = cachedRunEventRegistry ??= new KxmSchemaRegistry();
|
|
16501
17119
|
if (!registry.runEventValidator(value)) {
|
|
16502
|
-
throw new KxmConfigError([
|
|
17120
|
+
throw new KxmConfigError([issue3(
|
|
16503
17121
|
"schema",
|
|
16504
17122
|
"run_event_invalid",
|
|
16505
17123
|
file,
|
|
@@ -16515,7 +17133,7 @@ function syncEventSchemaErrors(value) {
|
|
|
16515
17133
|
function validateDriveReceipt(value, file) {
|
|
16516
17134
|
const registry = cachedRunEventRegistry ??= new KxmSchemaRegistry();
|
|
16517
17135
|
if (!value || typeof value !== "object" || Array.isArray(value) || value.schema !== "kxm.drive-receipt.v1") {
|
|
16518
|
-
throw new KxmConfigError([
|
|
17136
|
+
throw new KxmConfigError([issue3(
|
|
16519
17137
|
"schema",
|
|
16520
17138
|
"drive_receipt_invalid",
|
|
16521
17139
|
file,
|
|
@@ -16523,7 +17141,7 @@ function validateDriveReceipt(value, file) {
|
|
|
16523
17141
|
)]);
|
|
16524
17142
|
}
|
|
16525
17143
|
if (!registry.driveReceiptValidator(value)) {
|
|
16526
|
-
throw new KxmConfigError([
|
|
17144
|
+
throw new KxmConfigError([issue3(
|
|
16527
17145
|
"schema",
|
|
16528
17146
|
"drive_receipt_invalid",
|
|
16529
17147
|
file,
|
|
@@ -16534,10 +17152,10 @@ function validateDriveReceipt(value, file) {
|
|
|
16534
17152
|
function validateCoordinator(value, file) {
|
|
16535
17153
|
const registry = cachedRunEventRegistry ??= new KxmSchemaRegistry();
|
|
16536
17154
|
if (!value || typeof value !== "object" || Array.isArray(value) || value.schema !== "kxm.coordinator.v1") {
|
|
16537
|
-
throw new KxmConfigError([
|
|
17155
|
+
throw new KxmConfigError([issue3("schema", "coordinator_invalid", file, "expected kxm.coordinator.v1")]);
|
|
16538
17156
|
}
|
|
16539
17157
|
if (!registry.coordinatorValidator(value)) {
|
|
16540
|
-
throw new KxmConfigError([
|
|
17158
|
+
throw new KxmConfigError([issue3(
|
|
16541
17159
|
"schema",
|
|
16542
17160
|
"coordinator_invalid",
|
|
16543
17161
|
file,
|
|
@@ -16548,10 +17166,10 @@ function validateCoordinator(value, file) {
|
|
|
16548
17166
|
function validateIntakeMessage(value, file) {
|
|
16549
17167
|
const registry = cachedRunEventRegistry ??= new KxmSchemaRegistry();
|
|
16550
17168
|
if (!value || typeof value !== "object" || Array.isArray(value) || value.schema !== "kxm.intake-message.v1") {
|
|
16551
|
-
throw new KxmConfigError([
|
|
17169
|
+
throw new KxmConfigError([issue3("schema", "intake_message_invalid", file, "expected kxm.intake-message.v1")]);
|
|
16552
17170
|
}
|
|
16553
17171
|
if (!registry.intakeMessageValidator(value)) {
|
|
16554
|
-
throw new KxmConfigError([
|
|
17172
|
+
throw new KxmConfigError([issue3(
|
|
16555
17173
|
"schema",
|
|
16556
17174
|
"intake_message_invalid",
|
|
16557
17175
|
file,
|
|
@@ -16562,7 +17180,7 @@ function validateIntakeMessage(value, file) {
|
|
|
16562
17180
|
function schemaIssue(file, error) {
|
|
16563
17181
|
const location = error.instancePath || "/";
|
|
16564
17182
|
const suffix = error.params && "additionalProperty" in error.params ? ` (${String(error.params.additionalProperty)})` : "";
|
|
16565
|
-
return
|
|
17183
|
+
return issue3("schema", `schema_${error.keyword}`, file, `${location} ${error.message ?? "is invalid"}${suffix}`);
|
|
16566
17184
|
}
|
|
16567
17185
|
function canonicalHostPath(path) {
|
|
16568
17186
|
const resolved = resolve(path);
|
|
@@ -16588,7 +17206,7 @@ function portableBindingIssue(root, pathHint, repositoryId) {
|
|
|
16588
17206
|
if (!existsSync3(current)) break;
|
|
16589
17207
|
const stat = lstatSync(current);
|
|
16590
17208
|
if (stat.isSymbolicLink()) {
|
|
16591
|
-
return
|
|
17209
|
+
return issue3("path", "repository_binding_path_link", ".kxm/project.yaml", `portable pathHint for ${repositoryId} traverses a symbolic link or junction`);
|
|
16592
17210
|
}
|
|
16593
17211
|
}
|
|
16594
17212
|
const binding = resolve(root, ...pathHint.split("/"));
|
|
@@ -16596,7 +17214,7 @@ function portableBindingIssue(root, pathHint, repositoryId) {
|
|
|
16596
17214
|
const realRoot = canonicalHostPath(root);
|
|
16597
17215
|
const realBinding = canonicalHostPath(binding);
|
|
16598
17216
|
if (!containedHostPath(realRoot, realBinding)) {
|
|
16599
|
-
return
|
|
17217
|
+
return issue3("path", "repository_binding_outside_project", ".kxm/project.yaml", `portable pathHint for ${repositoryId} resolves outside the authoritative project root`);
|
|
16600
17218
|
}
|
|
16601
17219
|
}
|
|
16602
17220
|
return void 0;
|
|
@@ -16611,7 +17229,7 @@ function portablePath(path) {
|
|
|
16611
17229
|
return segments.every((segment) => segment.length > 0 && segment !== "." && segment !== ".." && !WINDOWS_RESERVED.test((segment.split(".")[0] ?? segment).replace(/[ .]+$/g, "")) && !segment.endsWith(".") && !segment.endsWith(" ") && !segment.includes(":"));
|
|
16612
17230
|
}
|
|
16613
17231
|
function kxmResourceIdentifier(id) {
|
|
16614
|
-
return id.length <= 64 &&
|
|
17232
|
+
return id.length <= 64 && IDENTIFIER2.test(id) && !WINDOWS_RESERVED.test(id);
|
|
16615
17233
|
}
|
|
16616
17234
|
function resourceIdentifier(id) {
|
|
16617
17235
|
return kxmResourceIdentifier(id);
|
|
@@ -16689,7 +17307,7 @@ function readTemplateProvenance(registry, root) {
|
|
|
16689
17307
|
fail2("path", "template_provenance_path_invalid", label, `managed path ${path} is not a portable project-configuration path`);
|
|
16690
17308
|
}
|
|
16691
17309
|
if (seen.has(folded)) fail2("path", "template_provenance_path_collision", label, `managed path ${path} collides after case folding`);
|
|
16692
|
-
if (prior &&
|
|
17310
|
+
if (prior && compareCodeUnits4(prior, path) >= 0) fail2("semantic", "template_provenance_order_invalid", label, "managed file entries must use strict code-unit order");
|
|
16693
17311
|
seen.add(folded);
|
|
16694
17312
|
prior = path;
|
|
16695
17313
|
}
|
|
@@ -16706,27 +17324,27 @@ function listNamedResources(registry, root, directory, logicalDirectory, kind, r
|
|
|
16706
17324
|
fail2("path", "resource_directory_invalid", displayPath(root, directory), "resource directory must be a regular directory");
|
|
16707
17325
|
}
|
|
16708
17326
|
const issues = [];
|
|
16709
|
-
const entries = readdirSync(directory, { withFileTypes: true }).sort((left, right) =>
|
|
17327
|
+
const entries = readdirSync(directory, { withFileTypes: true }).sort((left, right) => compareCodeUnits4(left.name, right.name));
|
|
16710
17328
|
for (const entry of entries) {
|
|
16711
17329
|
if (entry.isDirectory()) {
|
|
16712
|
-
issues.push(
|
|
17330
|
+
issues.push(issue3("path", "nested_resource_directory", displayPath(root, join2(directory, entry.name)), "nested resource directories are not allowed"));
|
|
16713
17331
|
continue;
|
|
16714
17332
|
}
|
|
16715
17333
|
if (!entry.name.toLowerCase().endsWith(".yaml") && !entry.name.toLowerCase().endsWith(".yml")) continue;
|
|
16716
17334
|
if (!entry.isFile() || extname(entry.name) !== ".yaml") {
|
|
16717
|
-
issues.push(
|
|
17335
|
+
issues.push(issue3("path", "resource_filename_invalid", displayPath(root, join2(directory, entry.name)), "resource must be a regular file with the exact .yaml extension"));
|
|
16718
17336
|
continue;
|
|
16719
17337
|
}
|
|
16720
17338
|
const id = basename(entry.name, ".yaml");
|
|
16721
17339
|
if (kind === "model" && id === "inventory") continue;
|
|
16722
17340
|
if (id === replacedId) continue;
|
|
16723
17341
|
if (!resourceIdentifier(id)) {
|
|
16724
|
-
issues.push(
|
|
17342
|
+
issues.push(issue3("path", "resource_id_invalid", displayPath(root, join2(directory, entry.name)), `filename-derived identity ${id} is invalid or platform-reserved`));
|
|
16725
17343
|
continue;
|
|
16726
17344
|
}
|
|
16727
17345
|
const collision = [...resources.keys()].find((candidate) => candidate.toLocaleLowerCase("en-US") === id.toLocaleLowerCase("en-US"));
|
|
16728
17346
|
if (collision) {
|
|
16729
|
-
issues.push(
|
|
17347
|
+
issues.push(issue3("path", "resource_id_collision", displayPath(root, join2(directory, entry.name)), `${id} case-folds to existing ${collision}`));
|
|
16730
17348
|
continue;
|
|
16731
17349
|
}
|
|
16732
17350
|
try {
|
|
@@ -16761,7 +17379,7 @@ function mapResource(resource, map, issues) {
|
|
|
16761
17379
|
if (!id) return;
|
|
16762
17380
|
const collision = [...map.keys()].find((candidate) => candidate.toLocaleLowerCase("en-US") === id.toLocaleLowerCase("en-US"));
|
|
16763
17381
|
if (collision) {
|
|
16764
|
-
issues.push(
|
|
17382
|
+
issues.push(issue3("path", "resource_id_collision", resource.logicalPath, `${id} case-folds to existing ${collision}`));
|
|
16765
17383
|
return;
|
|
16766
17384
|
}
|
|
16767
17385
|
map.set(id, resource);
|
|
@@ -16785,7 +17403,7 @@ function validatePortablePaths(resource, issues) {
|
|
|
16785
17403
|
}
|
|
16786
17404
|
for (const candidate of candidates) {
|
|
16787
17405
|
if (!portablePath(candidate.path)) {
|
|
16788
|
-
issues.push(
|
|
17406
|
+
issues.push(issue3("path", "portable_path_invalid", resource.logicalPath, `${candidate.label} is not a normalized forward-slash relative path: ${candidate.path}`));
|
|
16789
17407
|
}
|
|
16790
17408
|
}
|
|
16791
17409
|
}
|
|
@@ -16795,14 +17413,14 @@ function selectorCandidates(selectorValue, models, file, label, issues) {
|
|
|
16795
17413
|
const profile = stringValue(selector.profile);
|
|
16796
17414
|
if (profile) {
|
|
16797
17415
|
const model = models.get(profile);
|
|
16798
|
-
if (!model) issues.push(
|
|
17416
|
+
if (!model) issues.push(issue3("reference", "model_profile_unknown", file, `${label} references unknown model profile ${profile}`));
|
|
16799
17417
|
return model ? [model] : [];
|
|
16800
17418
|
}
|
|
16801
17419
|
const tag = stringValue(selector.tag);
|
|
16802
17420
|
if (!tag) return [];
|
|
16803
17421
|
const capabilities = new Set(names(selector.capabilities));
|
|
16804
17422
|
const candidates = [...models.values()].filter((model) => names(model.value.tags).includes(tag) && [...capabilities].every((capability) => names(model.value.capabilities).includes(capability)));
|
|
16805
|
-
if (candidates.length === 0) issues.push(
|
|
17423
|
+
if (candidates.length === 0) issues.push(issue3("reference", "model_tag_unresolved", file, `${label} selector tag ${tag} has no matching profile`));
|
|
16806
17424
|
return candidates;
|
|
16807
17425
|
}
|
|
16808
17426
|
function modelCandidates(selectorValue, models) {
|
|
@@ -16815,7 +17433,7 @@ function modelCandidates(selectorValue, models) {
|
|
|
16815
17433
|
return [{
|
|
16816
17434
|
key: `profile:${profile}`,
|
|
16817
17435
|
profile,
|
|
16818
|
-
...stringValue(resource.value.provider) ? { provider: stringValue(resource.value.provider) } : {},
|
|
17436
|
+
...stringValue(resource.value.provider) || stringValue(resource.value.vendor) ? { provider: stringValue(resource.value.provider) ?? stringValue(resource.value.vendor) } : {},
|
|
16819
17437
|
...stringValue(resource.value.model) ? { model: stringValue(resource.value.model) } : {}
|
|
16820
17438
|
}];
|
|
16821
17439
|
}
|
|
@@ -16825,7 +17443,7 @@ function modelCandidates(selectorValue, models) {
|
|
|
16825
17443
|
return [...models.values()].filter((resource) => names(resource.value.tags).includes(tag) && [...capabilities].every((capability) => names(resource.value.capabilities).includes(capability))).map((resource) => ({
|
|
16826
17444
|
key: `profile:${resource.id ?? "unknown"}`,
|
|
16827
17445
|
...resource.id ? { profile: resource.id } : {},
|
|
16828
|
-
...stringValue(resource.value.provider) ? { provider: stringValue(resource.value.provider) } : {},
|
|
17446
|
+
...stringValue(resource.value.provider) || stringValue(resource.value.vendor) ? { provider: stringValue(resource.value.provider) ?? stringValue(resource.value.vendor) } : {},
|
|
16829
17447
|
...stringValue(resource.value.model) ? { model: stringValue(resource.value.model) } : {}
|
|
16830
17448
|
}));
|
|
16831
17449
|
}
|
|
@@ -16857,13 +17475,13 @@ function validateEnvironment(resource, issues) {
|
|
|
16857
17475
|
const secret = objectValue(candidate);
|
|
16858
17476
|
const name = secret && stringValue(secret.name);
|
|
16859
17477
|
if (!name) continue;
|
|
16860
|
-
if (secretNames.has(name)) issues.push(
|
|
17478
|
+
if (secretNames.has(name)) issues.push(issue3("semantic", "secret_name_duplicate", resource.logicalPath, `secret environment name ${name} is duplicated`));
|
|
16861
17479
|
secretNames.add(name);
|
|
16862
|
-
if (Object.hasOwn(values, name)) issues.push(
|
|
17480
|
+
if (Object.hasOwn(values, name)) issues.push(issue3("semantic", "environment_name_conflict", resource.logicalPath, `${name} appears in both values and secrets`));
|
|
16863
17481
|
}
|
|
16864
17482
|
for (const [name, value] of Object.entries(values)) {
|
|
16865
17483
|
if (typeof value === "string" && SECRET_VALUE_PATTERNS.some((pattern) => pattern.test(value))) {
|
|
16866
|
-
issues.push(
|
|
17484
|
+
issues.push(issue3("semantic", "probable_secret_value", resource.logicalPath, `${name} resembles a credential and must be represented by secrets[].ref`));
|
|
16867
17485
|
}
|
|
16868
17486
|
}
|
|
16869
17487
|
}
|
|
@@ -16875,29 +17493,29 @@ function validateToolScope(agent, step, file, stepId, issues) {
|
|
|
16875
17493
|
if (!requested) return;
|
|
16876
17494
|
const ceiling = objectValue(agent.value.tools);
|
|
16877
17495
|
if (!ceiling) {
|
|
16878
|
-
issues.push(
|
|
17496
|
+
issues.push(issue3("semantic", "tool_scope_expansion", file, `${stepId} requests tools but agent ${agent.id ?? "unknown"} declares no tool ceiling`));
|
|
16879
17497
|
return;
|
|
16880
17498
|
}
|
|
16881
17499
|
const requestedPreset = stringValue(requested.preset);
|
|
16882
17500
|
const ceilingPreset = stringValue(ceiling.preset);
|
|
16883
17501
|
if (requestedPreset !== ceilingPreset) {
|
|
16884
|
-
issues.push(
|
|
17502
|
+
issues.push(issue3("semantic", "tool_scope_expansion", file, `${stepId} tool preset ${String(requestedPreset)} does not preserve agent ${agent.id ?? "unknown"} preset ${String(ceilingPreset)}`));
|
|
16885
17503
|
}
|
|
16886
17504
|
const allowed = new Set(names(ceiling.allow));
|
|
16887
17505
|
const denied = new Set(names(ceiling.deny));
|
|
16888
17506
|
for (const tool of names(requested.allow)) {
|
|
16889
|
-
if (!allowed.has(tool) || denied.has(tool)) issues.push(
|
|
17507
|
+
if (!allowed.has(tool) || denied.has(tool)) issues.push(issue3("semantic", "tool_scope_expansion", file, `${stepId} requests tool ${tool} beyond agent ${agent.id ?? "unknown"} explicit allowlist`));
|
|
16890
17508
|
}
|
|
16891
17509
|
const requestedDenied = new Set(names(requested.deny));
|
|
16892
17510
|
for (const tool of denied) {
|
|
16893
|
-
if (!requestedDenied.has(tool)) issues.push(
|
|
17511
|
+
if (!requestedDenied.has(tool)) issues.push(issue3("semantic", "tool_scope_expansion", file, `${stepId} drops agent ${agent.id ?? "unknown"} denial for ${tool}`));
|
|
16894
17512
|
}
|
|
16895
17513
|
}
|
|
16896
17514
|
function validateToolPolicy(resource, policy, label, issues) {
|
|
16897
17515
|
if (!policy) return;
|
|
16898
17516
|
const allowed = new Set(names(policy.allow));
|
|
16899
17517
|
for (const tool of names(policy.deny)) {
|
|
16900
|
-
if (allowed.has(tool)) issues.push(
|
|
17518
|
+
if (allowed.has(tool)) issues.push(issue3("semantic", "tool_policy_contradiction", resource.logicalPath, `${label} both allows and denies ${tool}`));
|
|
16901
17519
|
}
|
|
16902
17520
|
}
|
|
16903
17521
|
function validateAgentScope(agent, step, repositories, file, stepId, issues) {
|
|
@@ -16908,14 +17526,14 @@ function validateAgentScope(agent, step, repositories, file, stepId, issues) {
|
|
|
16908
17526
|
if (!repositories.has(repositoryId)) continue;
|
|
16909
17527
|
const ceiling = agentRepositories[repositoryId] ?? defaultAccess ?? "none";
|
|
16910
17528
|
if (accessRank(requested) > accessRank(ceiling)) {
|
|
16911
|
-
issues.push(
|
|
17529
|
+
issues.push(issue3("semantic", "repository_scope_expansion", file, `${stepId} requests ${String(requested)} access to ${repositoryId} beyond agent ${agent.id ?? "unknown"} ceiling ${String(ceiling)}`));
|
|
16912
17530
|
}
|
|
16913
17531
|
}
|
|
16914
17532
|
const grants = new Set(valuesOf(agent.value, "secrets").map((candidate) => objectValue(candidate)).filter((candidate) => Boolean(candidate)).map((candidate) => stringValue(candidate.ref)).filter((candidate) => Boolean(candidate)));
|
|
16915
17533
|
for (const candidate of valuesOf(step, "secrets")) {
|
|
16916
17534
|
const reference = stringValue(objectValue(candidate)?.ref);
|
|
16917
17535
|
if (reference && !grants.has(reference)) {
|
|
16918
|
-
issues.push(
|
|
17536
|
+
issues.push(issue3("semantic", "secret_scope_expansion", file, `${stepId} requests secret ${reference} beyond agent ${agent.id ?? "unknown"} ceiling`));
|
|
16919
17537
|
}
|
|
16920
17538
|
}
|
|
16921
17539
|
}
|
|
@@ -16930,7 +17548,7 @@ function gateOutcomeImpossible(step, stepId, file) {
|
|
|
16930
17548
|
const impossible = declared.filter((outcome) => !produced.includes(outcome) && outcome !== "implementation_failure" && outcome !== "repro_missing");
|
|
16931
17549
|
const missing = produced.filter((outcome) => !declared.includes(outcome));
|
|
16932
17550
|
if (impossible.length === 0 || missing.length === 0) return void 0;
|
|
16933
|
-
return
|
|
17551
|
+
return issue3("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 ")}`);
|
|
16934
17552
|
}
|
|
16935
17553
|
function transition(value) {
|
|
16936
17554
|
if (typeof value === "string") return { target: value };
|
|
@@ -16946,13 +17564,13 @@ function transition(value) {
|
|
|
16946
17564
|
function validateWorkflow(workflow, agents, models, repositories, gates, issues) {
|
|
16947
17565
|
const file = workflow.logicalPath;
|
|
16948
17566
|
const coordinator = stringValue(workflow.value.coordinator) ?? "coordinator";
|
|
16949
|
-
if (!agents.has(coordinator)) issues.push(
|
|
17567
|
+
if (!agents.has(coordinator)) issues.push(issue3("reference", "coordinator_unknown", file, `coordinator ${coordinator} does not exist`));
|
|
16950
17568
|
const steps = valuesOf(workflow.value, "steps").map((candidate) => objectValue(candidate)).filter((candidate) => Boolean(candidate));
|
|
16951
17569
|
const stepIndex = /* @__PURE__ */ new Map();
|
|
16952
17570
|
for (const [index, step] of steps.entries()) {
|
|
16953
17571
|
const id = stringValue(step.id);
|
|
16954
17572
|
if (!id) continue;
|
|
16955
|
-
if (stepIndex.has(id)) issues.push(
|
|
17573
|
+
if (stepIndex.has(id)) issues.push(issue3("semantic", "step_id_duplicate", file, `step ${id} is duplicated`));
|
|
16956
17574
|
else stepIndex.set(id, index);
|
|
16957
17575
|
}
|
|
16958
17576
|
for (const field of ["reproOracle", "planHash"]) {
|
|
@@ -16962,16 +17580,16 @@ function validateWorkflow(workflow, agents, models, repositories, gates, issues)
|
|
|
16962
17580
|
const evidenceKey = stringValue(oracle.evidenceKey);
|
|
16963
17581
|
const index = stageId === void 0 ? void 0 : stepIndex.get(stageId);
|
|
16964
17582
|
if (index === void 0) {
|
|
16965
|
-
issues.push(
|
|
17583
|
+
issues.push(issue3("reference", "oracle_stage_unknown", file, `${field} references unknown stage ${String(stageId)}`));
|
|
16966
17584
|
continue;
|
|
16967
17585
|
}
|
|
16968
17586
|
const evidence = new Set(valuesOf(steps[index] ?? {}, "requiredEvidence").map((candidate) => stringValue(objectValue(candidate)?.key)).filter((candidate) => Boolean(candidate)));
|
|
16969
|
-
if (!evidenceKey || !evidence.has(evidenceKey)) issues.push(
|
|
17587
|
+
if (!evidenceKey || !evidence.has(evidenceKey)) issues.push(issue3("reference", "oracle_evidence_unknown", file, `${field} references undeclared evidence ${String(evidenceKey)} in ${stageId}`));
|
|
16970
17588
|
}
|
|
16971
17589
|
const planStages = new Set(names(workflow.value.requirePlanHash));
|
|
16972
|
-
if (planStages.size > 0 && !objectValue(workflow.value.planHash)) issues.push(
|
|
17590
|
+
if (planStages.size > 0 && !objectValue(workflow.value.planHash)) issues.push(issue3("semantic", "plan_hash_missing", file, "requirePlanHash requires planHash"));
|
|
16973
17591
|
for (const stageId of planStages) {
|
|
16974
|
-
if (!stepIndex.has(stageId)) issues.push(
|
|
17592
|
+
if (!stepIndex.has(stageId)) issues.push(issue3("reference", "plan_hash_stage_unknown", file, `requirePlanHash references unknown stage ${stageId}`));
|
|
16975
17593
|
}
|
|
16976
17594
|
const adjacency = steps.map(() => /* @__PURE__ */ new Set());
|
|
16977
17595
|
const terminalReachable = /* @__PURE__ */ new Set();
|
|
@@ -16983,23 +17601,23 @@ function validateWorkflow(workflow, agents, models, repositories, gates, issues)
|
|
|
16983
17601
|
validateToolPolicy(workflow, objectValue(step.tools), `${stepId}.tools`, issues);
|
|
16984
17602
|
const primaryAgentId = stringValue(step.agent);
|
|
16985
17603
|
if ((kind === "agent" || kind === "moa") && primaryAgentId && !agents.has(primaryAgentId)) {
|
|
16986
|
-
issues.push(
|
|
17604
|
+
issues.push(issue3("reference", "agent_unknown", file, `${stepId} references unknown agent ${primaryAgentId}`));
|
|
16987
17605
|
}
|
|
16988
17606
|
const gate = stringValue(step.gate);
|
|
16989
17607
|
if (kind === "gate" && gate) {
|
|
16990
|
-
if (!gates) issues.push(
|
|
16991
|
-
else if (!gates.has(gate)) issues.push(
|
|
17608
|
+
if (!gates) issues.push(issue3("reference", "gate_registry_missing", file, `${stepId} requires .kxm/gates.yaml`));
|
|
17609
|
+
else if (!gates.has(gate)) issues.push(issue3("reference", "gate_unknown", file, `${stepId} references unregistered gate ${gate}`));
|
|
16992
17610
|
}
|
|
16993
17611
|
if (step.expect !== void 0 && (kind !== "gate" || !["pass", "fail"].includes(String(step.expect)))) {
|
|
16994
|
-
issues.push(
|
|
17612
|
+
issues.push(issue3("semantic", "gate_expect_invalid", file, `${stepId} expect is gate-only pass or fail`));
|
|
16995
17613
|
}
|
|
16996
17614
|
if (kind === "gate" && Object.keys(objectValue(step.on) ?? {}).some((outcome) => outcome === "implementation_failure" || outcome === "repro_missing")) {
|
|
16997
|
-
issues.push(
|
|
17615
|
+
issues.push(issue3("semantic", "gate_outcome_renamed", file, `${stepId} must use implementation-failure and repro-missing`));
|
|
16998
17616
|
}
|
|
16999
17617
|
const impossibleOutcome = kind === "gate" ? gateOutcomeImpossible(step, stepId, file) : void 0;
|
|
17000
17618
|
if (impossibleOutcome) issues.push(impossibleOutcome);
|
|
17001
17619
|
for (const repositoryId of Object.keys(objectValue(step.repositories) ?? {})) {
|
|
17002
|
-
if (!repositories.has(repositoryId)) issues.push(
|
|
17620
|
+
if (!repositories.has(repositoryId)) issues.push(issue3("reference", "repository_unknown", file, `${stepId} references unknown repository ${repositoryId}`));
|
|
17003
17621
|
}
|
|
17004
17622
|
selectorCandidates(step.model, models, file, `${stepId}.model`, issues);
|
|
17005
17623
|
const assignment = objectValue(step.assignments);
|
|
@@ -17007,7 +17625,7 @@ function validateWorkflow(workflow, agents, models, repositories, gates, issues)
|
|
|
17007
17625
|
const allowedAgents = declaredAgents.length > 0 ? declaredAgents : primaryAgentId ? [primaryAgentId] : [];
|
|
17008
17626
|
for (const agentId of allowedAgents) {
|
|
17009
17627
|
const agent = agents.get(agentId);
|
|
17010
|
-
if (!agent) issues.push(
|
|
17628
|
+
if (!agent) issues.push(issue3("reference", "assignment_agent_unknown", file, `${stepId} allows unknown agent ${agentId}`));
|
|
17011
17629
|
else {
|
|
17012
17630
|
validateAgentScope(agent, step, repositories, file, stepId, issues);
|
|
17013
17631
|
if (step.model !== void 0 && agent.value.model !== void 0) {
|
|
@@ -17015,37 +17633,37 @@ function validateWorkflow(workflow, agents, models, repositories, gates, issues)
|
|
|
17015
17633
|
const ceilingModels = modelCandidates(agent.value.model, models);
|
|
17016
17634
|
const effectiveModels = requestedModels.filter((requested) => ceilingModels.some((ceiling) => compatibleModel(requested, ceiling)));
|
|
17017
17635
|
if (requestedModels.length > 0 && effectiveModels.length === 0) {
|
|
17018
|
-
issues.push(
|
|
17636
|
+
issues.push(issue3("semantic", "model_selector_incompatible", file, `${stepId} model selector has no candidate within agent ${agentId} ceiling`));
|
|
17019
17637
|
}
|
|
17020
17638
|
}
|
|
17021
17639
|
}
|
|
17022
17640
|
}
|
|
17023
17641
|
if (primaryAgentId && allowedAgents.length > 0 && !allowedAgents.includes(primaryAgentId)) {
|
|
17024
|
-
issues.push(
|
|
17642
|
+
issues.push(issue3("semantic", "primary_agent_ineligible", file, `${stepId} primary agent ${primaryAgentId} is not in allowedAgents`));
|
|
17025
17643
|
}
|
|
17026
17644
|
const minimum = numberValue(assignment?.minimum, 1);
|
|
17027
17645
|
const target = numberValue(assignment?.target, minimum);
|
|
17028
17646
|
const maximum = numberValue(assignment?.maximum, target);
|
|
17029
17647
|
const maxParallel = numberValue(assignment?.maxParallel, maximum);
|
|
17030
|
-
if (!(minimum <= target && target <= maximum)) issues.push(
|
|
17031
|
-
if (maxParallel > maximum) issues.push(
|
|
17648
|
+
if (!(minimum <= target && target <= maximum)) issues.push(issue3("semantic", "assignment_bounds_invalid", file, `${stepId} must satisfy minimum <= target <= maximum`));
|
|
17649
|
+
if (maxParallel > maximum) issues.push(issue3("semantic", "assignment_parallelism_invalid", file, `${stepId} maxParallel exceeds maximum`));
|
|
17032
17650
|
if (assignment && allowedAgents.length > 0 && maximum > allowedAgents.length && kind === "moa") {
|
|
17033
|
-
issues.push(
|
|
17651
|
+
issues.push(issue3("semantic", "assignment_pool_too_small", file, `${stepId} maximum ${maximum} exceeds ${allowedAgents.length} eligible agents`));
|
|
17034
17652
|
}
|
|
17035
17653
|
const maxWriteRepositories = assignment && typeof assignment.maxWriteRepositories === "number" ? assignment.maxWriteRepositories : void 0;
|
|
17036
17654
|
if (maxWriteRepositories !== void 0) {
|
|
17037
17655
|
const writable = Object.values(objectValue(step.repositories) ?? {}).filter((access) => access === "write").length;
|
|
17038
|
-
if (maxWriteRepositories > writable) issues.push(
|
|
17656
|
+
if (maxWriteRepositories > writable) issues.push(issue3("semantic", "write_repository_bound_invalid", file, `${stepId} maxWriteRepositories exceeds writable repository scope`));
|
|
17039
17657
|
}
|
|
17040
17658
|
const join19 = objectValue(step.join);
|
|
17041
17659
|
const minimumPassed = join19 && typeof join19.minimumPassed === "number" ? join19.minimumPassed : void 0;
|
|
17042
|
-
if (minimumPassed !== void 0 && minimumPassed > maximum) issues.push(
|
|
17660
|
+
if (minimumPassed !== void 0 && minimumPassed > maximum) issues.push(issue3("semantic", "join_impossible", file, `${stepId} minimumPassed exceeds assignment maximum`));
|
|
17043
17661
|
const distinctBy = names(assignment?.distinctBy);
|
|
17044
17662
|
if (distinctBy.length > 0) {
|
|
17045
17663
|
const candidates = diversityCandidates(step.model, allowedAgents, agents, models);
|
|
17046
17664
|
for (const dimension of distinctBy) {
|
|
17047
17665
|
const distinct = new Set(candidates.map((candidate) => candidate[dimension]).filter((value) => Boolean(value)));
|
|
17048
|
-
if (distinct.size < target) issues.push(
|
|
17666
|
+
if (distinct.size < target) issues.push(issue3("semantic", "model_diversity_impossible", file, `${stepId} requires ${target} distinct ${dimension} values but resolves ${distinct.size}`));
|
|
17049
17667
|
}
|
|
17050
17668
|
}
|
|
17051
17669
|
const evidenceKeys = /* @__PURE__ */ new Set();
|
|
@@ -17054,29 +17672,29 @@ function validateWorkflow(workflow, agents, models, repositories, gates, issues)
|
|
|
17054
17672
|
if (!requirement) continue;
|
|
17055
17673
|
const key = stringValue(requirement.key);
|
|
17056
17674
|
if (key) {
|
|
17057
|
-
if (evidenceKeys.has(key)) issues.push(
|
|
17675
|
+
if (evidenceKeys.has(key)) issues.push(issue3("semantic", "evidence_key_duplicate", file, `${stepId} evidence key ${key} is duplicated`));
|
|
17058
17676
|
evidenceKeys.add(key);
|
|
17059
17677
|
}
|
|
17060
17678
|
const policy = objectValue(requirement.producerPolicy);
|
|
17061
17679
|
if (!policy) continue;
|
|
17062
17680
|
const eligible = names(policy.eligibleAgents);
|
|
17063
17681
|
const producerMinimum = numberValue(policy.minimumProducers, 1);
|
|
17064
|
-
if (producerMinimum > target || producerMinimum > eligible.length) issues.push(
|
|
17682
|
+
if (producerMinimum > target || producerMinimum > eligible.length) issues.push(issue3("semantic", "producer_minimum_impossible", file, `${stepId}.${String(key)} producer minimum exceeds its eligible or target pool`));
|
|
17065
17683
|
for (const agentId of eligible) {
|
|
17066
|
-
if (!agents.has(agentId)) issues.push(
|
|
17067
|
-
if (allowedAgents.length > 0 && !allowedAgents.includes(agentId)) issues.push(
|
|
17684
|
+
if (!agents.has(agentId)) issues.push(issue3("reference", "producer_agent_unknown", file, `${stepId}.${String(key)} references unknown producer ${agentId}`));
|
|
17685
|
+
if (allowedAgents.length > 0 && !allowedAgents.includes(agentId)) issues.push(issue3("semantic", "producer_agent_ineligible", file, `${stepId}.${String(key)} producer ${agentId} is outside allowedAgents`));
|
|
17068
17686
|
}
|
|
17069
17687
|
const degradation = objectValue(policy.degradation);
|
|
17070
|
-
if (degradation && numberValue(degradation.minimumProducers, 1) >= producerMinimum) issues.push(
|
|
17688
|
+
if (degradation && numberValue(degradation.minimumProducers, 1) >= producerMinimum) issues.push(issue3("semantic", "producer_degradation_invalid", file, `${stepId}.${String(key)} degradation must lower minimumProducers`));
|
|
17071
17689
|
}
|
|
17072
17690
|
const writes = Object.values(objectValue(step.repositories) ?? {}).some((access) => access === "write");
|
|
17073
17691
|
const planHashStage = stringValue(objectValue(workflow.value.planHash)?.stageId);
|
|
17074
17692
|
const planHashIndex = planHashStage === void 0 ? void 0 : stepIndex.get(planHashStage);
|
|
17075
17693
|
if (writes && planHashIndex !== void 0 && index > planHashIndex && !planStages.has(stepId)) {
|
|
17076
|
-
issues.push(
|
|
17694
|
+
issues.push(issue3("semantic", "mutation_missing_plan_hash", file, `${stepId} mutates after the approved-plan stage but is absent from requirePlanHash`));
|
|
17077
17695
|
}
|
|
17078
17696
|
const outcomes = objectValue(step.on) ?? {};
|
|
17079
|
-
if (Object.keys(outcomes).length === 0) issues.push(
|
|
17697
|
+
if (Object.keys(outcomes).length === 0) issues.push(issue3("semantic", "step_transitions_missing", file, `${stepId} has no declared transitions`));
|
|
17080
17698
|
for (const [outcome, rawTransition] of Object.entries(outcomes)) {
|
|
17081
17699
|
const parsed = transition(rawTransition);
|
|
17082
17700
|
if (parsed.target === "$terminal") {
|
|
@@ -17086,29 +17704,29 @@ function validateWorkflow(workflow, agents, models, repositories, gates, issues)
|
|
|
17086
17704
|
}
|
|
17087
17705
|
const targetIndex = parsed.target === void 0 ? void 0 : stepIndex.get(parsed.target);
|
|
17088
17706
|
if (targetIndex === void 0) {
|
|
17089
|
-
issues.push(
|
|
17707
|
+
issues.push(issue3("reference", "transition_target_unknown", file, `${stepId}.${outcome} references unknown target ${String(parsed.target)}`));
|
|
17090
17708
|
continue;
|
|
17091
17709
|
}
|
|
17092
17710
|
adjacency[index]?.add(targetIndex);
|
|
17093
17711
|
if (targetIndex <= index) {
|
|
17094
17712
|
hasBackEdge = true;
|
|
17095
|
-
if (parsed.maxTransitions === void 0) issues.push(
|
|
17713
|
+
if (parsed.maxTransitions === void 0) issues.push(issue3("semantic", "back_edge_unbounded", file, `${stepId}.${outcome} back-edge requires maxTransitions`));
|
|
17096
17714
|
}
|
|
17097
17715
|
}
|
|
17098
17716
|
}
|
|
17099
17717
|
if (hasBackEdge && typeof objectValue(workflow.value.limits)?.maxTransitions !== "number") {
|
|
17100
|
-
issues.push(
|
|
17718
|
+
issues.push(issue3("semantic", "workflow_cycle_unbounded", file, "workflow with back-edges requires limits.maxTransitions"));
|
|
17101
17719
|
}
|
|
17102
17720
|
const readyIndex = stepIndex.get("ready");
|
|
17103
17721
|
const verifyIndex = stepIndex.get("verify");
|
|
17104
17722
|
if (readyIndex !== void 0 && verifyIndex !== void 0) {
|
|
17105
17723
|
const verifyTargets = adjacency[verifyIndex] ?? /* @__PURE__ */ new Set();
|
|
17106
17724
|
if (!verifyTargets.has(readyIndex)) {
|
|
17107
|
-
issues.push(
|
|
17725
|
+
issues.push(issue3("semantic", "verify_must_precede_ready", file, "verify must transition to ready"));
|
|
17108
17726
|
}
|
|
17109
17727
|
for (const [srcIndex, targets] of adjacency.entries()) {
|
|
17110
17728
|
if (targets.has(readyIndex) && srcIndex !== verifyIndex) {
|
|
17111
|
-
issues.push(
|
|
17729
|
+
issues.push(issue3("semantic", "verify_must_precede_ready", file, `step ${String(steps[srcIndex]?.id)} transitions to ready bypassing verify`));
|
|
17112
17730
|
}
|
|
17113
17731
|
}
|
|
17114
17732
|
}
|
|
@@ -17123,7 +17741,7 @@ function validateWorkflow(workflow, agents, models, repositories, gates, issues)
|
|
|
17123
17741
|
queue.push(target);
|
|
17124
17742
|
}
|
|
17125
17743
|
}
|
|
17126
|
-
for (const [index, step] of steps.entries()) if (!reachable.has(index)) issues.push(
|
|
17744
|
+
for (const [index, step] of steps.entries()) if (!reachable.has(index)) issues.push(issue3("semantic", "step_unreachable", file, `step ${String(step.id)} is unreachable from the first step`));
|
|
17127
17745
|
for (const [requiredIndex, requiredStep] of steps.entries()) {
|
|
17128
17746
|
if (requiredStep.kind !== "gate" && requiredStep.kind !== "approval") continue;
|
|
17129
17747
|
const reachableWithoutRequired = /* @__PURE__ */ new Set();
|
|
@@ -17139,7 +17757,7 @@ function validateWorkflow(workflow, agents, models, repositories, gates, issues)
|
|
|
17139
17757
|
}
|
|
17140
17758
|
}
|
|
17141
17759
|
if ([...completedTerminalSources].some((source) => source !== requiredIndex && reachableWithoutRequired.has(source))) {
|
|
17142
|
-
issues.push(
|
|
17760
|
+
issues.push(issue3("semantic", "required_step_bypass", file, `a completed path can bypass required ${String(requiredStep.id)}`));
|
|
17143
17761
|
}
|
|
17144
17762
|
}
|
|
17145
17763
|
const canTerminate = new Set(terminalReachable);
|
|
@@ -17153,7 +17771,7 @@ function validateWorkflow(workflow, agents, models, repositories, gates, issues)
|
|
|
17153
17771
|
}
|
|
17154
17772
|
}
|
|
17155
17773
|
}
|
|
17156
|
-
for (const index of reachable) if (!canTerminate.has(index)) issues.push(
|
|
17774
|
+
for (const index of reachable) if (!canTerminate.has(index)) issues.push(issue3("semantic", "step_cannot_terminate", file, `step ${String(steps[index]?.id)} has no path to a terminal transition`));
|
|
17157
17775
|
}
|
|
17158
17776
|
}
|
|
17159
17777
|
function validateModelReferences(models, issues) {
|
|
@@ -17166,7 +17784,7 @@ function validateModelReferences(models, issues) {
|
|
|
17166
17784
|
const visited = /* @__PURE__ */ new Set();
|
|
17167
17785
|
const walk = (id, path) => {
|
|
17168
17786
|
if (visiting.has(id)) {
|
|
17169
|
-
issues.push(
|
|
17787
|
+
issues.push(issue3("semantic", "model_fallback_cycle", models.get(id)?.logicalPath ?? ".kxm/models", `fallback profile cycle: ${[...path, id].join(" -> ")}`));
|
|
17170
17788
|
return;
|
|
17171
17789
|
}
|
|
17172
17790
|
if (visited.has(id)) return;
|
|
@@ -17194,17 +17812,17 @@ function validateBundle(project, repositories, agents, models, workflows, enviro
|
|
|
17194
17812
|
if (!id) continue;
|
|
17195
17813
|
const folded = id.toLocaleLowerCase("en-US");
|
|
17196
17814
|
const previous = foldedRepositories.get(folded);
|
|
17197
|
-
if (previous) issues.push(
|
|
17815
|
+
if (previous) issues.push(issue3("path", "repository_id_collision", project.logicalPath, `${id} case-folds to existing ${previous}`));
|
|
17198
17816
|
else foldedRepositories.set(folded, id);
|
|
17199
17817
|
repositoryIds.add(id);
|
|
17200
17818
|
if (entry.role === "control") controls += 1;
|
|
17201
17819
|
}
|
|
17202
|
-
if (controls !== 1) issues.push(
|
|
17820
|
+
if (controls !== 1) issues.push(issue3("semantic", "control_repository_count", project.logicalPath, `expected exactly one control repository, received ${controls}`));
|
|
17203
17821
|
for (const repository of repositories.values()) {
|
|
17204
|
-
if (repository.value.projectId !== project.value.id) issues.push(
|
|
17822
|
+
if (repository.value.projectId !== project.value.id) issues.push(issue3("reference", "repository_project_mismatch", repository.logicalPath, "projectId does not match the authoritative project"));
|
|
17205
17823
|
const id = stringValue(repository.value.repositoryId);
|
|
17206
|
-
if (!id || !repositoryIds.has(id)) issues.push(
|
|
17207
|
-
if (repository.id !== id) issues.push(
|
|
17824
|
+
if (!id || !repositoryIds.has(id)) issues.push(issue3("reference", "repository_definition_unknown", repository.logicalPath, `repositoryId ${String(id)} is not bound by project.yaml`));
|
|
17825
|
+
if (repository.id !== id) issues.push(issue3("reference", "repository_binding_identity_mismatch", repository.logicalPath, `binding ${String(repository.id)} declares repositoryId ${String(id)}`));
|
|
17208
17826
|
}
|
|
17209
17827
|
for (const resource of [project, ...environments]) validatePortablePaths(resource, issues);
|
|
17210
17828
|
for (const environment of environments) validateEnvironment(environment, issues);
|
|
@@ -17214,25 +17832,25 @@ function validateBundle(project, repositories, agents, models, workflows, enviro
|
|
|
17214
17832
|
const definition = objectValue(value);
|
|
17215
17833
|
const executable = Array.isArray(definition?.argv) ? definition.argv[0] : void 0;
|
|
17216
17834
|
if (definition?.kind === "command" && typeof executable === "string" && (executable === "." || executable === ".." || executable.includes("\\") || !executable.startsWith("/") && executable.includes("/"))) {
|
|
17217
|
-
issues.push(
|
|
17835
|
+
issues.push(issue3("semantic", "gate_executable_invalid", ".kxm/gates.yaml", `${id} argv[0] must be a bare executable or absolute POSIX path`));
|
|
17218
17836
|
}
|
|
17219
17837
|
}
|
|
17220
17838
|
const presets = new Set(options.registeredToolPresets ?? BUILTIN_TOOL_PRESETS);
|
|
17221
17839
|
const harnesses = new Set(options.registeredHarnesses ?? BUILTIN_HARNESS_IDS);
|
|
17222
17840
|
const defaultExecutor = stringValue(project.value.defaultExecutor);
|
|
17223
|
-
if (defaultExecutor && !executors.has(defaultExecutor)) issues.push(
|
|
17841
|
+
if (defaultExecutor && !executors.has(defaultExecutor)) issues.push(issue3("reference", "executor_unknown", project.logicalPath, `defaultExecutor ${defaultExecutor} is not registered`));
|
|
17224
17842
|
const defaultHarness = stringValue(project.value.defaultHarness) ?? DEFAULT_HARNESS;
|
|
17225
|
-
if (!harnesses.has(defaultHarness)) issues.push(
|
|
17843
|
+
if (!harnesses.has(defaultHarness)) issues.push(issue3("reference", "harness_unknown", project.logicalPath, `defaultHarness ${defaultHarness} is not registered`));
|
|
17226
17844
|
for (const agent of agents.values()) {
|
|
17227
17845
|
validateToolPolicy(agent, objectValue(agent.value.tools), "tools", issues);
|
|
17228
17846
|
const executor = stringValue(agent.value.executor);
|
|
17229
|
-
if (executor && !executors.has(executor)) issues.push(
|
|
17847
|
+
if (executor && !executors.has(executor)) issues.push(issue3("reference", "executor_unknown", agent.logicalPath, `executor ${executor} is not registered`));
|
|
17230
17848
|
const harness = stringValue(agent.value.harness) ?? defaultHarness;
|
|
17231
|
-
if (!harnesses.has(harness)) issues.push(
|
|
17849
|
+
if (!harnesses.has(harness)) issues.push(issue3("reference", "harness_unknown", agent.logicalPath, `harness ${harness} is not registered`));
|
|
17232
17850
|
const preset = stringValue(objectValue(agent.value.tools)?.preset);
|
|
17233
|
-
if (preset && !presets.has(preset)) issues.push(
|
|
17851
|
+
if (preset && !presets.has(preset)) issues.push(issue3("reference", "tool_preset_unknown", agent.logicalPath, `tool preset ${preset} is not registered`));
|
|
17234
17852
|
for (const repositoryId of Object.keys(objectValue(agent.value.repositories) ?? {})) {
|
|
17235
|
-
if (!repositoryIds.has(repositoryId)) issues.push(
|
|
17853
|
+
if (!repositoryIds.has(repositoryId)) issues.push(issue3("reference", "repository_unknown", agent.logicalPath, `references unknown repository ${repositoryId}`));
|
|
17236
17854
|
}
|
|
17237
17855
|
const candidates = selectorCandidates(agent.value.model, models, agent.logicalPath, "model", issues);
|
|
17238
17856
|
const declaredHarness = stringValue(agent.value.harness);
|
|
@@ -17242,14 +17860,14 @@ function validateBundle(project, repositories, agents, models, workflows, enviro
|
|
|
17242
17860
|
const candidateModel = stringValue(candidate.value.model);
|
|
17243
17861
|
const validation = validateHarnessModelPair(declaredHarness, { provider: candidateProvider, model: candidateModel });
|
|
17244
17862
|
if (!validation.valid) {
|
|
17245
|
-
issues.push(
|
|
17863
|
+
issues.push(issue3("semantic", validation.issue ?? "harness_unhosted_model", agent.logicalPath, validation.message ?? `harness ${declaredHarness} cannot host model ${candidateModel ?? candidate.logicalPath}`));
|
|
17246
17864
|
}
|
|
17247
17865
|
}
|
|
17248
17866
|
}
|
|
17249
17867
|
}
|
|
17250
17868
|
validateModelReferences(models, issues);
|
|
17251
17869
|
const defaultWorkflow = stringValue(project.value.defaultWorkflow) ?? "default";
|
|
17252
|
-
if (!workflows.has(defaultWorkflow)) issues.push(
|
|
17870
|
+
if (!workflows.has(defaultWorkflow)) issues.push(issue3("reference", "default_workflow_unknown", project.logicalPath, `default workflow ${defaultWorkflow} does not exist`));
|
|
17253
17871
|
for (const workflow of workflows.values()) validateWorkflow(workflow, agents, models, repositoryIds, gates, issues);
|
|
17254
17872
|
if (projectRoot) {
|
|
17255
17873
|
const writerRolePath = join2(projectRoot, ".kxm", "roles", "writer.yaml");
|
|
@@ -17259,7 +17877,27 @@ function validateBundle(project, repositories, agents, models, workflows, enviro
|
|
|
17259
17877
|
const rawRole = parseRestrictedYaml2(writerRole ?? readFileSync(writerRolePath, "utf8"));
|
|
17260
17878
|
const roleObj = objectValue(rawRole);
|
|
17261
17879
|
const rosterEntries = valuesOf(roleObj ?? {}, "roster").map((candidate) => objectValue(candidate)).filter((entry) => Boolean(entry));
|
|
17262
|
-
const enabledRosterModels = rosterEntries.
|
|
17880
|
+
const enabledRosterModels = rosterEntries.flatMap((entry) => {
|
|
17881
|
+
const direct = stringValue(entry.model);
|
|
17882
|
+
const route = stringValue(entry.route);
|
|
17883
|
+
const named = direct ? [direct] : [];
|
|
17884
|
+
if (route) {
|
|
17885
|
+
const modelFile = join2(projectRoot, ".kxm", "models", `${route}.yaml`);
|
|
17886
|
+
if (existsSync3(modelFile)) {
|
|
17887
|
+
try {
|
|
17888
|
+
const modelDoc = objectValue(parseRestrictedYaml2(readFileSync(modelFile, "utf8"), modelFile));
|
|
17889
|
+
const model = modelDoc ? stringValue(modelDoc.model) : void 0;
|
|
17890
|
+
const vendor = modelDoc ? stringValue(modelDoc.vendor) : void 0;
|
|
17891
|
+
const harness = modelDoc ? stringValue(modelDoc.harness) : void 0;
|
|
17892
|
+
if (model) named.push(model);
|
|
17893
|
+
if (vendor && model) named.push(`${vendor}/${model}`);
|
|
17894
|
+
if (harness && model) named.push(`${harness}/${model}`);
|
|
17895
|
+
} catch {
|
|
17896
|
+
}
|
|
17897
|
+
}
|
|
17898
|
+
}
|
|
17899
|
+
return named;
|
|
17900
|
+
});
|
|
17263
17901
|
const agentModelObj = objectValue(implementerAgent.value.model);
|
|
17264
17902
|
const agentModelStr = stringValue(implementerAgent.value.model);
|
|
17265
17903
|
const agentProvider = agentModelObj ? stringValue(agentModelObj.provider) : void 0;
|
|
@@ -17268,7 +17906,7 @@ function validateBundle(project, repositories, agents, models, workflows, enviro
|
|
|
17268
17906
|
if (enabledRosterModels.length > 0 && canonicalAgentModel) {
|
|
17269
17907
|
const matches = enabledRosterModels.some((rm) => rm === canonicalAgentModel || rm === agentModel || rm.endsWith(`/${agentModel}`));
|
|
17270
17908
|
if (!matches) {
|
|
17271
|
-
issues.push(
|
|
17909
|
+
issues.push(issue3("semantic", "role_roster_conflicts_with_agent", ".kxm/roles/writer.yaml", `role roster in .kxm/roles/writer.yaml does not include agent model ${canonicalAgentModel} from ${implementerAgent.logicalPath}`));
|
|
17272
17910
|
}
|
|
17273
17911
|
}
|
|
17274
17912
|
} catch (error) {
|
|
@@ -17278,7 +17916,83 @@ function validateBundle(project, repositories, agents, models, workflows, enviro
|
|
|
17278
17916
|
}
|
|
17279
17917
|
}
|
|
17280
17918
|
}
|
|
17281
|
-
|
|
17919
|
+
if (projectRoot && existsSync3(join2(projectRoot, ".kxm", "roster.yaml"))) {
|
|
17920
|
+
issues.push(...developerRolePolicyIssues(projectRoot));
|
|
17921
|
+
}
|
|
17922
|
+
return sortIssues3(issues);
|
|
17923
|
+
}
|
|
17924
|
+
function developerCeilings() {
|
|
17925
|
+
const script = join2(findKxmRepoRoot(import.meta.url), "scripts", "harness-run.mjs");
|
|
17926
|
+
let loaded;
|
|
17927
|
+
try {
|
|
17928
|
+
loaded = spawnSync2(process.execPath, ["--input-type=module", "-e", `const m = await import(${JSON.stringify(pathToFileURL(script).href)}); process.stdout.write(JSON.stringify({ROUTES:m.ROUTES,NATIVE_PI_BRAKE_PROVIDERS:m.NATIVE_PI_BRAKE_PROVIDERS,PI_ALLOWED_PROVIDERS:m.PI_ALLOWED_PROVIDERS,PI_NATIVE_VENDOR_PROVIDERS:m.PI_NATIVE_VENDOR_PROVIDERS}))`], {
|
|
17929
|
+
encoding: "utf8",
|
|
17930
|
+
timeout: 15e3
|
|
17931
|
+
});
|
|
17932
|
+
} catch (error) {
|
|
17933
|
+
return { ok: false, detail: error instanceof Error ? error.message : String(error) };
|
|
17934
|
+
}
|
|
17935
|
+
if (loaded.error) {
|
|
17936
|
+
const code = loaded.error.code;
|
|
17937
|
+
const detail = code === "ETIMEDOUT" || loaded.signal ? `timed out after 15s (${loaded.error.message})` : loaded.error.message;
|
|
17938
|
+
return { ok: false, detail };
|
|
17939
|
+
}
|
|
17940
|
+
if (loaded.status !== 0 || !loaded.stdout) {
|
|
17941
|
+
const stderr = (loaded.stderr ?? "").trim();
|
|
17942
|
+
return { ok: false, detail: stderr || `scripts/harness-run.mjs exited ${loaded.status}` };
|
|
17943
|
+
}
|
|
17944
|
+
try {
|
|
17945
|
+
return { ok: true, ...JSON.parse(loaded.stdout) };
|
|
17946
|
+
} catch (error) {
|
|
17947
|
+
return { ok: false, detail: `ceilings JSON parse failed: ${error instanceof Error ? error.message : String(error)}` };
|
|
17948
|
+
}
|
|
17949
|
+
}
|
|
17950
|
+
function developerRolePolicyIssues(projectRoot) {
|
|
17951
|
+
const rolesDir = join2(projectRoot, ".kxm", "roles");
|
|
17952
|
+
const modelsDir = join2(projectRoot, ".kxm", "models");
|
|
17953
|
+
const roles = {};
|
|
17954
|
+
const models = {};
|
|
17955
|
+
const evidence = {};
|
|
17956
|
+
const parseIssues = [];
|
|
17957
|
+
const readMap = (dir, into, skipInventory) => {
|
|
17958
|
+
if (!existsSync3(dir)) return;
|
|
17959
|
+
for (const name of readdirSync(dir)) {
|
|
17960
|
+
if (!name.endsWith(".yaml")) continue;
|
|
17961
|
+
const id = basename(name, ".yaml");
|
|
17962
|
+
if (skipInventory && id === "inventory") continue;
|
|
17963
|
+
try {
|
|
17964
|
+
const value = parseRestrictedYaml2(readFileSync(join2(dir, name), "utf8"), `${dir}/${name}`);
|
|
17965
|
+
into[id] = value;
|
|
17966
|
+
} catch (error) {
|
|
17967
|
+
if (error instanceof KxmConfigError) {
|
|
17968
|
+
parseIssues.push(...error.issues);
|
|
17969
|
+
continue;
|
|
17970
|
+
}
|
|
17971
|
+
}
|
|
17972
|
+
}
|
|
17973
|
+
};
|
|
17974
|
+
readMap(rolesDir, roles, false);
|
|
17975
|
+
readMap(modelsDir, models, true);
|
|
17976
|
+
for (const model of Object.values(models)) {
|
|
17977
|
+
const origin = objectValue(model.origin);
|
|
17978
|
+
const source = origin ? stringValue(origin.source) : void 0;
|
|
17979
|
+
if (!source || evidence[source] !== void 0) continue;
|
|
17980
|
+
const evidenceFile = isAbsolute(source) ? source : join2(projectRoot, source);
|
|
17981
|
+
if (existsSync3(evidenceFile)) evidence[source] = readFileSync(evidenceFile, "utf8");
|
|
17982
|
+
}
|
|
17983
|
+
const ceilings = developerCeilings();
|
|
17984
|
+
if (!ceilings.ok) {
|
|
17985
|
+
return [...parseIssues, issue3("semantic", "developer_ceilings_unavailable", ".kxm/roster.yaml", `developer ceilings could not be loaded: ${ceilings.detail}`)];
|
|
17986
|
+
}
|
|
17987
|
+
const result = validatePolicyDraft({ models, roles, evidence }, {
|
|
17988
|
+
ceilings: ceilings.ROUTES,
|
|
17989
|
+
nativePiBrakeProviders: ceilings.NATIVE_PI_BRAKE_PROVIDERS,
|
|
17990
|
+
piAllowedProviders: ceilings.PI_ALLOWED_PROVIDERS,
|
|
17991
|
+
piNativeVendorProviders: ceilings.PI_NATIVE_VENDOR_PROVIDERS,
|
|
17992
|
+
vendorAliases: { "x-ai": "xai", moonshotai: "moonshot", "google-ai": "google", qwen: "alibaba" }
|
|
17993
|
+
});
|
|
17994
|
+
if (result.ok) return parseIssues;
|
|
17995
|
+
return [...parseIssues, ...result.issues.map((entry) => issue3(entry.phase, entry.code, entry.file, entry.message))];
|
|
17282
17996
|
}
|
|
17283
17997
|
function kxmCanonicalJson(value) {
|
|
17284
17998
|
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
@@ -17289,8 +18003,8 @@ function canonicalize(value) {
|
|
|
17289
18003
|
return kxmCanonicalJson(value);
|
|
17290
18004
|
}
|
|
17291
18005
|
function bundleRevision(resources) {
|
|
17292
|
-
const hash =
|
|
17293
|
-
for (const resource of [...resources].sort((left, right) =>
|
|
18006
|
+
const hash = createHash3("sha256");
|
|
18007
|
+
for (const resource of [...resources].sort((left, right) => compareCodeUnits4(left.logicalPath, right.logicalPath))) {
|
|
17294
18008
|
hash.update(resource.logicalPath, "utf8");
|
|
17295
18009
|
hash.update("\0", "utf8");
|
|
17296
18010
|
hash.update(canonicalize(resource.value), "utf8");
|
|
@@ -17316,7 +18030,7 @@ function discoverGitRoot(start = process.cwd()) {
|
|
|
17316
18030
|
}
|
|
17317
18031
|
function assertNoRegisteredGates(options) {
|
|
17318
18032
|
if ("registeredGates" in options) {
|
|
17319
|
-
throw new KxmConfigError([
|
|
18033
|
+
throw new KxmConfigError([issue3("semantic", "registered_gates_removed", ".kxm/gates.yaml", "registeredGates was removed; declare gates in .kxm/gates.yaml")]);
|
|
17320
18034
|
}
|
|
17321
18035
|
}
|
|
17322
18036
|
function loadKxmProject(projectRoot, options = {}) {
|
|
@@ -17329,7 +18043,7 @@ function loadProjectBundle(projectRoot, options, candidate) {
|
|
|
17329
18043
|
const root = resolve(projectRoot);
|
|
17330
18044
|
const legacyPresent = legacyConfigFilesAt(root);
|
|
17331
18045
|
if (legacyPresent.length > 0) {
|
|
17332
|
-
throw new KxmConfigError(legacyPresent.map((file) =>
|
|
18046
|
+
throw new KxmConfigError(legacyPresent.map((file) => issue3(
|
|
17333
18047
|
"semantic",
|
|
17334
18048
|
"legacy_state_unsupported",
|
|
17335
18049
|
file,
|
|
@@ -17344,20 +18058,25 @@ function loadProjectBundle(projectRoot, options, candidate) {
|
|
|
17344
18058
|
if (templateProvenance && templateProvenance.inputs && typeof templateProvenance.inputs === "object" && !Array.isArray(templateProvenance.inputs)) {
|
|
17345
18059
|
const provenanceProjectId = stringValue(templateProvenance.inputs.projectId);
|
|
17346
18060
|
if (provenanceProjectId !== stringValue(project.value.id)) {
|
|
17347
|
-
earlyIssues.push(
|
|
18061
|
+
earlyIssues.push(issue3("semantic", "template_provenance_project_mismatch", ".kxm/template-provenance.yaml", "template provenance belongs to a different project identity"));
|
|
17348
18062
|
}
|
|
17349
18063
|
}
|
|
17350
18064
|
const declaredRepositoryIds = new Set(valuesOf(project.value, "repositories").map((candidate2) => stringValue(objectValue(candidate2)?.id)).filter((candidate2) => candidate2 !== void 0));
|
|
17351
|
-
for (const repositoryId of Object.keys(options.repositoryBindings ?? {}).sort(
|
|
18065
|
+
for (const repositoryId of Object.keys(options.repositoryBindings ?? {}).sort(compareCodeUnits4)) {
|
|
17352
18066
|
if (!resourceIdentifier(repositoryId)) {
|
|
17353
|
-
earlyIssues.push(
|
|
18067
|
+
earlyIssues.push(issue3("path", "repository_binding_id_invalid", ".kxm/project.yaml", `host-local binding identity ${repositoryId} is invalid`));
|
|
17354
18068
|
} else if (!declaredRepositoryIds.has(repositoryId)) {
|
|
17355
|
-
earlyIssues.push(
|
|
18069
|
+
earlyIssues.push(issue3("reference", "repository_binding_unknown", ".kxm/project.yaml", `host-local binding references unknown repository ${repositoryId}`));
|
|
17356
18070
|
}
|
|
17357
18071
|
}
|
|
17358
18072
|
if (earlyIssues.length > 0) throw new KxmConfigError(earlyIssues);
|
|
17359
18073
|
const agents = listNamedResources(registry, root, join2(root, ".kxm", "agents"), ".kxm/agents", "agent");
|
|
17360
18074
|
const models = listNamedResources(registry, root, join2(root, ".kxm", "models"), ".kxm/models", "model");
|
|
18075
|
+
const roles = listNamedResources(registry, root, join2(root, ".kxm", "roles"), ".kxm/roles", "role", writerRole === void 0 ? void 0 : "writer");
|
|
18076
|
+
if (writerRole !== void 0) {
|
|
18077
|
+
const logicalPath = ".kxm/roles/writer.yaml";
|
|
18078
|
+
roles.set("writer", { kind: "role", id: "writer", file: join2(root, logicalPath), logicalPath, value: resourceValue(registry, writerRole, logicalPath, "role") });
|
|
18079
|
+
}
|
|
17361
18080
|
const workflows = listNamedResources(registry, root, join2(root, ".kxm", "workflows"), ".kxm/workflows", "workflow", workflowCandidate?.id);
|
|
17362
18081
|
if (workflowCandidate) {
|
|
17363
18082
|
const { id, document } = workflowCandidate;
|
|
@@ -17387,20 +18106,20 @@ function loadProjectBundle(projectRoot, options, candidate) {
|
|
|
17387
18106
|
const repositoryId = entry && stringValue(entry.id);
|
|
17388
18107
|
const pathHint = entry && stringValue(entry.pathHint);
|
|
17389
18108
|
const role = entry && stringValue(entry.role);
|
|
17390
|
-
const
|
|
18109
|
+
const required2 = entry?.required !== false;
|
|
17391
18110
|
if (!repositoryId) continue;
|
|
17392
18111
|
const hasLocalBinding = Object.hasOwn(options.repositoryBindings ?? {}, repositoryId);
|
|
17393
18112
|
const localBinding = options.repositoryBindings?.[repositoryId];
|
|
17394
18113
|
if (hasLocalBinding && (!localBinding || !isAbsolute(localBinding))) {
|
|
17395
|
-
loadIssues.push(
|
|
18114
|
+
loadIssues.push(issue3("path", "repository_binding_not_absolute", ".kxm/project.yaml", `host-local binding for ${repositoryId} must be absolute`));
|
|
17396
18115
|
continue;
|
|
17397
18116
|
}
|
|
17398
18117
|
if (role === "control" && localBinding && !sameHostPath(localBinding, root)) {
|
|
17399
|
-
loadIssues.push(
|
|
18118
|
+
loadIssues.push(issue3("path", "control_repository_binding_invalid", ".kxm/project.yaml", `control repository ${repositoryId} must bind to the authoritative project root`));
|
|
17400
18119
|
continue;
|
|
17401
18120
|
}
|
|
17402
18121
|
if (role === "control" && pathHint !== void 0 && pathHint !== ".") {
|
|
17403
|
-
loadIssues.push(
|
|
18122
|
+
loadIssues.push(issue3("path", "control_repository_path_invalid", ".kxm/project.yaml", `control repository ${repositoryId} must use pathHint .`));
|
|
17404
18123
|
continue;
|
|
17405
18124
|
}
|
|
17406
18125
|
if (!hasLocalBinding && role !== "control" && pathHint && portablePath(pathHint)) {
|
|
@@ -17412,28 +18131,28 @@ function loadProjectBundle(projectRoot, options, candidate) {
|
|
|
17412
18131
|
}
|
|
17413
18132
|
const binding = hasLocalBinding && localBinding ? resolve(localBinding) : role === "control" ? root : pathHint && portablePath(pathHint) ? resolve(root, ...pathHint.split("/")) : void 0;
|
|
17414
18133
|
if (!binding) {
|
|
17415
|
-
if (
|
|
18134
|
+
if (required2) loadIssues.push(issue3("discovery", "repository_binding_missing", ".kxm/project.yaml", `required repository ${repositoryId} has no portable pathHint or host-local binding`));
|
|
17416
18135
|
continue;
|
|
17417
18136
|
}
|
|
17418
18137
|
if (!existsSync3(binding)) {
|
|
17419
|
-
if (
|
|
18138
|
+
if (required2 || hasLocalBinding) loadIssues.push(issue3("discovery", "repository_binding_unavailable", ".kxm/project.yaml", `${hasLocalBinding ? "explicit" : "required"} repository binding ${repositoryId} is unavailable`));
|
|
17420
18139
|
continue;
|
|
17421
18140
|
}
|
|
17422
18141
|
const bindingStat = lstatSync(binding);
|
|
17423
18142
|
if (bindingStat.isSymbolicLink() || !bindingStat.isDirectory()) {
|
|
17424
|
-
loadIssues.push(
|
|
18143
|
+
loadIssues.push(issue3("path", "repository_binding_invalid", ".kxm/project.yaml", `repository binding ${repositoryId} must be a regular directory`));
|
|
17425
18144
|
continue;
|
|
17426
18145
|
}
|
|
17427
18146
|
if (role === "member") {
|
|
17428
18147
|
const repositoryGitRoot = discoverGitRoot(binding);
|
|
17429
18148
|
if (!repositoryGitRoot || !sameHostPath(repositoryGitRoot, binding)) {
|
|
17430
|
-
loadIssues.push(
|
|
18149
|
+
loadIssues.push(issue3("discovery", "repository_git_root_invalid", ".kxm/project.yaml", `member repository ${repositoryId} must bind to its authoritative Git worktree root`));
|
|
17431
18150
|
continue;
|
|
17432
18151
|
}
|
|
17433
18152
|
}
|
|
17434
18153
|
const memberLegacy = binding === root ? [] : legacyConfigFilesAt(binding);
|
|
17435
18154
|
if (memberLegacy.length > 0) {
|
|
17436
|
-
throw new KxmConfigError(memberLegacy.map((file) =>
|
|
18155
|
+
throw new KxmConfigError(memberLegacy.map((file) => issue3(
|
|
17437
18156
|
"semantic",
|
|
17438
18157
|
"legacy_state_unsupported",
|
|
17439
18158
|
`${repositoryId}/${file}`,
|
|
@@ -17443,7 +18162,7 @@ function loadProjectBundle(projectRoot, options, candidate) {
|
|
|
17443
18162
|
const foldedBinding = canonicalHostPath(binding).toLocaleLowerCase("en-US");
|
|
17444
18163
|
const priorBinding = seenBindings.get(foldedBinding);
|
|
17445
18164
|
if (priorBinding && priorBinding !== repositoryId) {
|
|
17446
|
-
loadIssues.push(
|
|
18165
|
+
loadIssues.push(issue3("path", "repository_binding_collision", ".kxm/project.yaml", `${repositoryId} and ${priorBinding} use the same repository binding`));
|
|
17447
18166
|
continue;
|
|
17448
18167
|
}
|
|
17449
18168
|
seenBindings.set(foldedBinding, repositoryId);
|
|
@@ -17453,7 +18172,7 @@ function loadProjectBundle(projectRoot, options, candidate) {
|
|
|
17453
18172
|
try {
|
|
17454
18173
|
const resource = readResource(registry, root, repositoryFile, `.kxm/repositories/${repositoryId}/repo.yaml`, "repository", repositoryId, binding);
|
|
17455
18174
|
if (resource.value.repositoryId !== repositoryId) {
|
|
17456
|
-
loadIssues.push(
|
|
18175
|
+
loadIssues.push(issue3("reference", "repository_binding_identity_mismatch", resource.logicalPath, `binding ${repositoryId} declares repositoryId ${String(resource.value.repositoryId)}`));
|
|
17457
18176
|
} else {
|
|
17458
18177
|
mapResource(resource, repositories, loadIssues);
|
|
17459
18178
|
definitionValidated = repositories.get(repositoryId) === resource;
|
|
@@ -17462,13 +18181,13 @@ function loadProjectBundle(projectRoot, options, candidate) {
|
|
|
17462
18181
|
if (error instanceof KxmConfigError) loadIssues.push(...error.issues);
|
|
17463
18182
|
else throw error;
|
|
17464
18183
|
}
|
|
17465
|
-
} else if (
|
|
17466
|
-
loadIssues.push(
|
|
18184
|
+
} else if (required2 || hasLocalBinding) {
|
|
18185
|
+
loadIssues.push(issue3("discovery", "repository_definition_missing", `.kxm/repositories/${repositoryId}/repo.yaml`, `${hasLocalBinding ? "explicit" : "required"} repository ${repositoryId} has no repo.yaml`));
|
|
17467
18186
|
}
|
|
17468
18187
|
const environmentFile = join2(binding, ".kxm", "repo", "env.yaml");
|
|
17469
18188
|
if (existsSync3(environmentFile)) {
|
|
17470
18189
|
if (!definitionValidated) {
|
|
17471
|
-
loadIssues.push(
|
|
18190
|
+
loadIssues.push(issue3("reference", "repository_environment_without_definition", `.kxm/repositories/${repositoryId}/env.yaml`, `repository ${repositoryId} environment requires a validated matching repo.yaml`));
|
|
17472
18191
|
} else {
|
|
17473
18192
|
try {
|
|
17474
18193
|
environments.push(readResource(registry, root, environmentFile, `.kxm/repositories/${repositoryId}/env.yaml`, "environment", void 0, binding));
|
|
@@ -17482,13 +18201,14 @@ function loadProjectBundle(projectRoot, options, candidate) {
|
|
|
17482
18201
|
if (loadIssues.length > 0) throw new KxmConfigError(loadIssues);
|
|
17483
18202
|
const issues = validateBundle(project, repositories, agents, models, workflows, environments, options, gateRegistry, root, writerRole);
|
|
17484
18203
|
if (issues.length > 0) throw new KxmConfigError(issues);
|
|
17485
|
-
const resources = [project, ...repositories.values(), ...agents.values(), ...models.values(), ...workflows.values(), ...environments, ...gateRegistry ? [gateRegistry] : []].sort((left, right) =>
|
|
18204
|
+
const resources = [project, ...repositories.values(), ...agents.values(), ...models.values(), ...roles.values(), ...workflows.values(), ...environments, ...gateRegistry ? [gateRegistry] : []].sort((left, right) => compareCodeUnits4(left.logicalPath, right.logicalPath));
|
|
17486
18205
|
return {
|
|
17487
18206
|
projectRoot: root,
|
|
17488
18207
|
project,
|
|
17489
18208
|
repositories,
|
|
17490
18209
|
agents,
|
|
17491
18210
|
models,
|
|
18211
|
+
roles,
|
|
17492
18212
|
workflows,
|
|
17493
18213
|
environments,
|
|
17494
18214
|
...gateRegistry ? { gateRegistry } : {},
|
|
@@ -17515,7 +18235,7 @@ function legacyConfigFilesAt(root) {
|
|
|
17515
18235
|
return files;
|
|
17516
18236
|
}
|
|
17517
18237
|
if (stat.isDirectory()) {
|
|
17518
|
-
for (const entry of readdirSync(workflowsDir, { withFileTypes: true }).filter((candidate) => (candidate.isFile() || candidate.isSymbolicLink()) && candidate.name.endsWith(".json")).sort((left, right) =>
|
|
18238
|
+
for (const entry of readdirSync(workflowsDir, { withFileTypes: true }).filter((candidate) => (candidate.isFile() || candidate.isSymbolicLink()) && candidate.name.endsWith(".json")).sort((left, right) => compareCodeUnits4(left.name, right.name))) {
|
|
17519
18239
|
files.push(`.kxm/config/workflows/${entry.name}`);
|
|
17520
18240
|
}
|
|
17521
18241
|
}
|
|
@@ -17524,7 +18244,7 @@ function legacyConfigFilesAt(root) {
|
|
|
17524
18244
|
}
|
|
17525
18245
|
|
|
17526
18246
|
// plugins/kxm/src/runtime-store.ts
|
|
17527
|
-
import { createHash as
|
|
18247
|
+
import { createHash as createHash6, randomUUID } from "node:crypto";
|
|
17528
18248
|
import { existsSync as existsSync6, lstatSync as lstatSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
17529
18249
|
import { dirname as dirname5, resolve as resolve6 } from "node:path";
|
|
17530
18250
|
|
|
@@ -17532,7 +18252,7 @@ import { dirname as dirname5, resolve as resolve6 } from "node:path";
|
|
|
17532
18252
|
import { existsSync as existsSync4 } from "node:fs";
|
|
17533
18253
|
import { createRequire } from "node:module";
|
|
17534
18254
|
import { resolve as resolve2 } from "node:path";
|
|
17535
|
-
import { pathToFileURL } from "node:url";
|
|
18255
|
+
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
17536
18256
|
var requireFromHere = createRequire(import.meta.url);
|
|
17537
18257
|
function loadNative() {
|
|
17538
18258
|
try {
|
|
@@ -17571,13 +18291,13 @@ var DatabaseSync = class {
|
|
|
17571
18291
|
};
|
|
17572
18292
|
function openReadOnlyDatabase(path) {
|
|
17573
18293
|
if (existsSync4(`${path}-wal`)) return new DatabaseSync(path, { readOnly: true });
|
|
17574
|
-
const uri =
|
|
18294
|
+
const uri = pathToFileURL2(resolve2(path));
|
|
17575
18295
|
uri.searchParams.set("immutable", "1");
|
|
17576
18296
|
return new DatabaseSync(uri.href, { readOnly: true });
|
|
17577
18297
|
}
|
|
17578
18298
|
|
|
17579
18299
|
// plugins/kxm/src/runtime-paths.ts
|
|
17580
|
-
import { createHash as
|
|
18300
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
17581
18301
|
import { realpathSync as realpathSync2 } from "node:fs";
|
|
17582
18302
|
import { join as join4, resolve as resolve4 } from "node:path";
|
|
17583
18303
|
|
|
@@ -17631,11 +18351,11 @@ function projectRuntimeKey(projectRoot) {
|
|
|
17631
18351
|
canonical = resolve4(projectRoot);
|
|
17632
18352
|
}
|
|
17633
18353
|
const folded = process.platform === "win32" ? canonical.toLocaleLowerCase("en-US") : canonical;
|
|
17634
|
-
return
|
|
18354
|
+
return createHash4("sha256").update(folded, "utf8").digest("hex").slice(0, 24);
|
|
17635
18355
|
}
|
|
17636
18356
|
|
|
17637
18357
|
// plugins/kxm/src/sync-transform.ts
|
|
17638
|
-
import { createHash as
|
|
18358
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
17639
18359
|
|
|
17640
18360
|
// plugins/kxm/src/redact.ts
|
|
17641
18361
|
var SECRET_PATTERNS = [
|
|
@@ -17674,7 +18394,7 @@ var KXM_DEFAULT_SYNC_POLICY = Object.freeze({
|
|
|
17674
18394
|
diffs: false,
|
|
17675
18395
|
environmentValues: false
|
|
17676
18396
|
});
|
|
17677
|
-
var KXM_DEFAULT_SYNC_POLICY_REVISION = `sha256:${
|
|
18397
|
+
var KXM_DEFAULT_SYNC_POLICY_REVISION = `sha256:${createHash5("sha256").update(kxmCanonicalJson(KXM_DEFAULT_SYNC_POLICY), "utf8").digest("hex")}`;
|
|
17678
18398
|
var MIN_REGISTERED_SECRET_CHARS = 4;
|
|
17679
18399
|
var REDACTED = "[redacted]";
|
|
17680
18400
|
var PATH_REDACTED = "[path]";
|
|
@@ -17979,8 +18699,8 @@ import {
|
|
|
17979
18699
|
} from "node:fs";
|
|
17980
18700
|
import { basename as basename2, dirname as dirname4, isAbsolute as isAbsolute3, join as join5, relative as relative3, resolve as resolve5, sep as sep3 } from "node:path";
|
|
17981
18701
|
function databaseError(code, file, message) {
|
|
17982
|
-
const
|
|
17983
|
-
return new KxmConfigError([
|
|
18702
|
+
const issue4 = { phase: "semantic", code, file, message };
|
|
18703
|
+
return new KxmConfigError([issue4]);
|
|
17984
18704
|
}
|
|
17985
18705
|
function checkedParent(path, description) {
|
|
17986
18706
|
const parent = dirname4(path);
|
|
@@ -18403,7 +19123,7 @@ var KxmRuntimeRegistry = class {
|
|
|
18403
19123
|
var KXM_RUN_EVENT_SCHEMA = "kxm.run-event.v1";
|
|
18404
19124
|
function hashKxmDriveLog(events) {
|
|
18405
19125
|
const body = events.map((event) => `${event.eventId}:${event.sequence}`).join("\n");
|
|
18406
|
-
return `sha256:${
|
|
19126
|
+
return `sha256:${createHash6("sha256").update(body, "utf8").digest("hex")}`;
|
|
18407
19127
|
}
|
|
18408
19128
|
function verifyKxmDriveReceipt(receipt, events, foldedStatus, binding) {
|
|
18409
19129
|
const reasons = [];
|
|
@@ -19558,7 +20278,7 @@ function newKxmObservationId() {
|
|
|
19558
20278
|
function newKxmEvidenceId() {
|
|
19559
20279
|
return `gev_${randomUUID().replaceAll("-", "")}`;
|
|
19560
20280
|
}
|
|
19561
|
-
var
|
|
20281
|
+
var SHA2562 = /^sha256:[a-f0-9]{64}$/;
|
|
19562
20282
|
var OPAQUE = /^[a-z][a-z0-9]{1,15}_[A-Za-z0-9][A-Za-z0-9_-]{5,127}$/;
|
|
19563
20283
|
var FORBIDDEN_TEXT_KEYS = /* @__PURE__ */ new Set(["stdout", "stderr", "error", "errorText", "stdoutText", "stderrText", "errorMessage", "message"]);
|
|
19564
20284
|
var GATE_ATTEMPT_KEYS = [
|
|
@@ -19637,7 +20357,7 @@ function gateRowContentHash(table, row) {
|
|
|
19637
20357
|
const copy = { ...row };
|
|
19638
20358
|
delete copy.contentHash;
|
|
19639
20359
|
void table;
|
|
19640
|
-
return `sha256:${
|
|
20360
|
+
return `sha256:${createHash6("sha256").update(kxmCanonicalJson(copy), "utf8").digest("hex")}`;
|
|
19641
20361
|
}
|
|
19642
20362
|
function assertClosedGateObservation(kind, row, id = "observation") {
|
|
19643
20363
|
if (row.completeness === "incomplete") return;
|
|
@@ -19689,7 +20409,7 @@ function assertId(value, prefix, id, label) {
|
|
|
19689
20409
|
}
|
|
19690
20410
|
}
|
|
19691
20411
|
function assertSha(value, id, label) {
|
|
19692
|
-
if (typeof value !== "string" || !
|
|
20412
|
+
if (typeof value !== "string" || !SHA2562.test(value)) throw gateRowInvalid(id, `${label} is not a sha256 hash`);
|
|
19693
20413
|
}
|
|
19694
20414
|
function assertFlag(value, id, label) {
|
|
19695
20415
|
if (value !== 0 && value !== 1) throw gateRowInvalid(id, `${label} must be 0 or 1`);
|
|
@@ -19867,7 +20587,7 @@ function capabilityFromRow(row) {
|
|
|
19867
20587
|
}
|
|
19868
20588
|
|
|
19869
20589
|
// plugins/kxm/src/runtime-service.ts
|
|
19870
|
-
import { createHash as
|
|
20590
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
19871
20591
|
import { existsSync as existsSync7, readdirSync as readdirSync3, readFileSync as readFileSync4 } from "node:fs";
|
|
19872
20592
|
import { join as join7 } from "node:path";
|
|
19873
20593
|
|
|
@@ -21080,7 +21800,7 @@ function isTerminalRunStatus(status) {
|
|
|
21080
21800
|
}
|
|
21081
21801
|
|
|
21082
21802
|
// plugins/kxm/src/engine-plan.ts
|
|
21083
|
-
import { createHash as
|
|
21803
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
21084
21804
|
|
|
21085
21805
|
// plugins/kxm/src/engine-compile.ts
|
|
21086
21806
|
var KXM_COMPILED_WORKFLOW_SCHEMA = "kxm.compiled-workflow.v1";
|
|
@@ -21096,8 +21816,8 @@ var BANNED_RUNTIME_HINT = "not yet supported";
|
|
|
21096
21816
|
var KxmEngineCompileError = class extends Error {
|
|
21097
21817
|
issues;
|
|
21098
21818
|
constructor(issues) {
|
|
21099
|
-
const sorted =
|
|
21100
|
-
super(sorted.map((
|
|
21819
|
+
const sorted = sortIssues4(issues);
|
|
21820
|
+
super(sorted.map((issue4) => `${issue4.workflowId}: ${issue4.code}: ${issue4.message}`).join("\n"));
|
|
21101
21821
|
this.name = "KxmEngineCompileError";
|
|
21102
21822
|
this.issues = sorted;
|
|
21103
21823
|
}
|
|
@@ -21233,7 +21953,7 @@ function compileStep(step, index, stepIndex, requirePlanHash, sink) {
|
|
|
21233
21953
|
const join19 = compileJoin(step, id, sink);
|
|
21234
21954
|
const requiredEvidence = compileEvidence(step, id, sink);
|
|
21235
21955
|
const transitions = compileTransitions(step, id, index, stepIndex, sink);
|
|
21236
|
-
const outcomes = Object.keys(transitions).sort(
|
|
21956
|
+
const outcomes = Object.keys(transitions).sort(compareCodeUnits5);
|
|
21237
21957
|
const orderedTransitions = /* @__PURE__ */ Object.create(null);
|
|
21238
21958
|
for (const outcome of outcomes) {
|
|
21239
21959
|
const transition2 = transitions[outcome];
|
|
@@ -21563,26 +22283,26 @@ function deepFreeze(value) {
|
|
|
21563
22283
|
}
|
|
21564
22284
|
return Object.freeze(value);
|
|
21565
22285
|
}
|
|
21566
|
-
function
|
|
22286
|
+
function compareCodeUnits5(left, right) {
|
|
21567
22287
|
return left < right ? -1 : left > right ? 1 : 0;
|
|
21568
22288
|
}
|
|
21569
22289
|
function compareOptional(left, right) {
|
|
21570
22290
|
if (left === right) return 0;
|
|
21571
22291
|
if (left === void 0) return -1;
|
|
21572
22292
|
if (right === void 0) return 1;
|
|
21573
|
-
return
|
|
22293
|
+
return compareCodeUnits5(left, right);
|
|
21574
22294
|
}
|
|
21575
|
-
function
|
|
22295
|
+
function sortIssues4(issues) {
|
|
21576
22296
|
return [...issues].sort((left, right) => {
|
|
21577
22297
|
const byStep = compareOptional(left.stepId, right.stepId);
|
|
21578
22298
|
if (byStep !== 0) return byStep;
|
|
21579
22299
|
const byOutcome = compareOptional(left.outcome, right.outcome);
|
|
21580
22300
|
if (byOutcome !== 0) return byOutcome;
|
|
21581
|
-
return
|
|
22301
|
+
return compareCodeUnits5(left.code, right.code);
|
|
21582
22302
|
});
|
|
21583
22303
|
}
|
|
21584
|
-
function pushIssue(sink,
|
|
21585
|
-
sink.issues.push({ workflowId: sink.workflowId, ...
|
|
22304
|
+
function pushIssue(sink, issue4) {
|
|
22305
|
+
sink.issues.push({ workflowId: sink.workflowId, ...issue4 });
|
|
21586
22306
|
}
|
|
21587
22307
|
|
|
21588
22308
|
// plugins/kxm/src/engine-plan.ts
|
|
@@ -21627,7 +22347,7 @@ var JOIN_OPTIONAL = ["minimumPassed", "cancelRemaining"];
|
|
|
21627
22347
|
var EVIDENCE_REQUIRED = ["key", "kind", "minimum", "reusableAcrossAttempts"];
|
|
21628
22348
|
var EVIDENCE_OPTIONAL = ["producerPolicy"];
|
|
21629
22349
|
function kxmSha256(input) {
|
|
21630
|
-
return `sha256:${
|
|
22350
|
+
return `sha256:${createHash7("sha256").update(input, "utf8").digest("hex")}`;
|
|
21631
22351
|
}
|
|
21632
22352
|
function kxmStepAskSha256(plan, stepId, agentId) {
|
|
21633
22353
|
const step = Object.hasOwn(plan.steps, stepId) ? plan.steps[stepId] : void 0;
|
|
@@ -21798,9 +22518,9 @@ function parseGates(raw, plan, runId) {
|
|
|
21798
22518
|
}
|
|
21799
22519
|
return { registry, definitions, controlRoot };
|
|
21800
22520
|
}
|
|
21801
|
-
function parseRegistryPin(raw, runId,
|
|
22521
|
+
function parseRegistryPin(raw, runId, required2) {
|
|
21802
22522
|
if (raw === null) {
|
|
21803
|
-
if (
|
|
22523
|
+
if (required2) throw runtimeError("run_plan_corrupt", runId, "gate steps require a non-null registry pin");
|
|
21804
22524
|
return null;
|
|
21805
22525
|
}
|
|
21806
22526
|
const value = asObject(raw, runId, "gates.registry");
|
|
@@ -21983,7 +22703,7 @@ function parseStep(raw, stepId, runId) {
|
|
|
21983
22703
|
repositories,
|
|
21984
22704
|
...value.tools !== void 0 ? { tools: asJsonObject(value.tools, runId, `${stepId}.tools`) } : {},
|
|
21985
22705
|
secrets: asObjectArray(value.secrets, runId, `${stepId}.secrets`).map((entry) => ({ ...entry })),
|
|
21986
|
-
...value.model !== void 0 ? { model:
|
|
22706
|
+
...value.model !== void 0 ? { model: cloneJson2(value.model) } : {},
|
|
21987
22707
|
requiredEvidence: asUnknownArray(value.requiredEvidence, runId, `${stepId}.requiredEvidence`).map((entry, index) => parseEvidence(entry, stepId, index, runId)),
|
|
21988
22708
|
outcomes,
|
|
21989
22709
|
transitions,
|
|
@@ -22121,9 +22841,9 @@ function asObject(value, runId, label) {
|
|
|
22121
22841
|
}
|
|
22122
22842
|
return value;
|
|
22123
22843
|
}
|
|
22124
|
-
function assertExactKeys(value,
|
|
22125
|
-
const allowed = /* @__PURE__ */ new Set([...
|
|
22126
|
-
for (const key of
|
|
22844
|
+
function assertExactKeys(value, required2, optional, runId, label) {
|
|
22845
|
+
const allowed = /* @__PURE__ */ new Set([...required2, ...optional]);
|
|
22846
|
+
for (const key of required2) {
|
|
22127
22847
|
if (!(key in value)) throw runtimeError("run_plan_corrupt", runId, `${label} is missing ${key}`);
|
|
22128
22848
|
}
|
|
22129
22849
|
for (const key of Object.keys(value)) {
|
|
@@ -22192,9 +22912,9 @@ function asJsonObject(value, runId, label) {
|
|
|
22192
22912
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
22193
22913
|
throw runtimeError("run_plan_corrupt", runId, `${label} is not an object`);
|
|
22194
22914
|
}
|
|
22195
|
-
return
|
|
22915
|
+
return cloneJson2(value);
|
|
22196
22916
|
}
|
|
22197
|
-
function
|
|
22917
|
+
function cloneJson2(value) {
|
|
22198
22918
|
return JSON.parse(JSON.stringify(value));
|
|
22199
22919
|
}
|
|
22200
22920
|
function sameSet(left, right) {
|
|
@@ -22303,8 +23023,8 @@ function verifyKxmGateEvidence(store, run, envelope2, events, state) {
|
|
|
22303
23023
|
throw runtimeError("gate_evidence_hash_mismatch", run.runId, "evidence outcome does not match the observation and expect");
|
|
22304
23024
|
}
|
|
22305
23025
|
const step = envelope2.plan.steps[row.stepId];
|
|
22306
|
-
const
|
|
22307
|
-
const expectedKey =
|
|
23026
|
+
const required2 = step?.requiredEvidence.filter((item) => item.kind === "gate") ?? [];
|
|
23027
|
+
const expectedKey = required2.length === 1 ? required2[0].key : null;
|
|
22308
23028
|
if (row.evidenceKey !== expectedKey) {
|
|
22309
23029
|
throw runtimeError("gate_evidence_unexpected", run.runId, "evidence_key does not match the declared requiredEvidence key");
|
|
22310
23030
|
}
|
|
@@ -22699,11 +23419,11 @@ function pump(storePath, owner) {
|
|
|
22699
23419
|
}
|
|
22700
23420
|
|
|
22701
23421
|
// plugins/kxm/src/runtime-service.ts
|
|
22702
|
-
function
|
|
23422
|
+
function compareCodeUnits6(left, right) {
|
|
22703
23423
|
return left < right ? -1 : left > right ? 1 : 0;
|
|
22704
23424
|
}
|
|
22705
23425
|
function sha256Of(input) {
|
|
22706
|
-
return `sha256:${
|
|
23426
|
+
return `sha256:${createHash8("sha256").update(input, "utf8").digest("hex")}`;
|
|
22707
23427
|
}
|
|
22708
23428
|
function objectValue3(value) {
|
|
22709
23429
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
@@ -22751,9 +23471,9 @@ function collectMemoryFiles(dir, baseDir, ignoreSubdirs = /* @__PURE__ */ new Se
|
|
|
22751
23471
|
return results;
|
|
22752
23472
|
}
|
|
22753
23473
|
function computeKxmMemoryRevision(bundle, options = {}) {
|
|
22754
|
-
const hash =
|
|
23474
|
+
const hash = createHash8("sha256");
|
|
22755
23475
|
const memoryDir = options.memoryDir ?? join7(bundle.projectRoot, ".kxm", "memory");
|
|
22756
|
-
const memoryFiles = collectMemoryFiles(memoryDir, memoryDir, /* @__PURE__ */ new Set(["candidates"])).sort((left, right) =>
|
|
23476
|
+
const memoryFiles = collectMemoryFiles(memoryDir, memoryDir, /* @__PURE__ */ new Set(["candidates"])).sort((left, right) => compareCodeUnits6(left.relPath, right.relPath));
|
|
22757
23477
|
for (const file of memoryFiles) {
|
|
22758
23478
|
hash.update(`memory:${file.relPath}\0`, "utf8");
|
|
22759
23479
|
hash.update(readFileSync4(file.fullPath));
|
|
@@ -22761,14 +23481,14 @@ function computeKxmMemoryRevision(bundle, options = {}) {
|
|
|
22761
23481
|
}
|
|
22762
23482
|
const skillsDir = options.skillsDir ?? join7(bundle.projectRoot, ".kxm", "skills");
|
|
22763
23483
|
const promotedSkillsDir = join7(skillsDir, "promoted");
|
|
22764
|
-
const skillFiles = collectMemoryFiles(promotedSkillsDir, promotedSkillsDir).sort((left, right) =>
|
|
23484
|
+
const skillFiles = collectMemoryFiles(promotedSkillsDir, promotedSkillsDir).sort((left, right) => compareCodeUnits6(left.relPath, right.relPath));
|
|
22765
23485
|
for (const file of skillFiles) {
|
|
22766
23486
|
hash.update(`skill:${file.relPath}\0`, "utf8");
|
|
22767
23487
|
hash.update(readFileSync4(file.fullPath));
|
|
22768
23488
|
hash.update("\0", "utf8");
|
|
22769
23489
|
}
|
|
22770
23490
|
if (options.promotedState && options.promotedState.length > 0) {
|
|
22771
|
-
const currentItems = options.promotedState.filter((item) => !item.status || item.status === "current").slice().sort((left, right) =>
|
|
23491
|
+
const currentItems = options.promotedState.filter((item) => !item.status || item.status === "current").slice().sort((left, right) => compareCodeUnits6(left.id, right.id));
|
|
22772
23492
|
for (const item of currentItems) {
|
|
22773
23493
|
hash.update(`state:${item.id}\0`, "utf8");
|
|
22774
23494
|
hash.update(kxmCanonicalJson(item), "utf8");
|
|
@@ -22908,7 +23628,7 @@ function acceptKxmRun(context, bundle, request, options = {}) {
|
|
|
22908
23628
|
}
|
|
22909
23629
|
const revisions = kxmPolicyRevisions(bundle, options);
|
|
22910
23630
|
const memoryRevision = options.memoryRevision ?? revisions.memoryRevision;
|
|
22911
|
-
const promptSha256 = `sha256:${
|
|
23631
|
+
const promptSha256 = `sha256:${createHash8("sha256").update(request.prompt, "utf8").digest("hex")}`;
|
|
22912
23632
|
return context.eventStore.transaction(() => {
|
|
22913
23633
|
const prior = context.eventStore.command(commandId);
|
|
22914
23634
|
if (prior) {
|
|
@@ -23150,7 +23870,7 @@ function cancelKxmRun(context, runId, options = {}) {
|
|
|
23150
23870
|
}
|
|
23151
23871
|
|
|
23152
23872
|
// plugins/kxm/src/oneshot-evidence.ts
|
|
23153
|
-
import { createHash as
|
|
23873
|
+
import { createHash as createHash9, randomUUID as randomUUID3 } from "node:crypto";
|
|
23154
23874
|
import { lstatSync as lstatSync4, readdirSync as readdirSync4, readFileSync as readFileSync5 } from "node:fs";
|
|
23155
23875
|
import { lstat, mkdir, open, rename } from "node:fs/promises";
|
|
23156
23876
|
import { join as join8 } from "node:path";
|
|
@@ -23159,7 +23879,7 @@ var RETIRED_ONESHOT_EVIDENCE_SCHEMAS = /* @__PURE__ */ new Set(["kxm.oneshot-evi
|
|
|
23159
23879
|
var TEXT_LIMIT = 4 * 1024 * 1024;
|
|
23160
23880
|
var ARGV_LIMIT = 64 * 1024;
|
|
23161
23881
|
var RECORD_LIMIT = 16 * 1024 * 1024;
|
|
23162
|
-
var digest = (value) => `sha256:${
|
|
23882
|
+
var digest = (value) => `sha256:${createHash9("sha256").update(value).digest("hex")}`;
|
|
23163
23883
|
function parseOneShotEvidenceRecord(bytes) {
|
|
23164
23884
|
let parsed;
|
|
23165
23885
|
try {
|
|
@@ -23301,7 +24021,7 @@ async function beginOneShotEvidence(root, intent, sensitive) {
|
|
|
23301
24021
|
|
|
23302
24022
|
// plugins/kxm/src/prices.ts
|
|
23303
24023
|
var import_yaml3 = __toESM(require_dist(), 1);
|
|
23304
|
-
import { createHash as
|
|
24024
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
23305
24025
|
import { existsSync as existsSync8, readFileSync as readFileSync6, statSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
23306
24026
|
import { join as join9 } from "node:path";
|
|
23307
24027
|
|
|
@@ -23364,7 +24084,7 @@ function hashPriceCatalog(catalog) {
|
|
|
23364
24084
|
}))
|
|
23365
24085
|
})).sort((a, b) => a.id.localeCompare(b.id))
|
|
23366
24086
|
};
|
|
23367
|
-
return
|
|
24087
|
+
return createHash10("sha256").update(JSON.stringify(canonical), "utf8").digest("hex");
|
|
23368
24088
|
}
|
|
23369
24089
|
function parsePriceCatalog(text) {
|
|
23370
24090
|
const parsed = (0, import_yaml3.parse)(text);
|
|
@@ -23412,9 +24132,9 @@ function parsePriceCatalog(text) {
|
|
|
23412
24132
|
throw new Error(`tier at index ${tIndex} for model ${m.id} must be an object`);
|
|
23413
24133
|
}
|
|
23414
24134
|
const t = tItem;
|
|
23415
|
-
const rate = (field, short,
|
|
24135
|
+
const rate = (field, short, required2 = false) => {
|
|
23416
24136
|
const value = t[field] !== void 0 ? t[field] : t[short];
|
|
23417
|
-
if (value == null && !
|
|
24137
|
+
if (value == null && !required2) return null;
|
|
23418
24138
|
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
23419
24139
|
throw new Error(`tier ${tIndex} for model ${m.id} requires finite non-negative ${field}`);
|
|
23420
24140
|
}
|
|
@@ -23464,22 +24184,30 @@ function loadPriceCatalog(rootOrPath) {
|
|
|
23464
24184
|
}
|
|
23465
24185
|
|
|
23466
24186
|
// plugins/kxm/src/engine.ts
|
|
23467
|
-
var
|
|
23468
|
-
import { createHash as
|
|
24187
|
+
var import_yaml9 = __toESM(require_dist(), 1);
|
|
24188
|
+
import { createHash as createHash14, randomBytes, timingSafeEqual } from "node:crypto";
|
|
23469
24189
|
import { existsSync as existsSync12, readFileSync as readFileSync10 } from "node:fs";
|
|
23470
24190
|
import { join as join15 } from "node:path";
|
|
23471
24191
|
|
|
23472
24192
|
// plugins/kxm/src/routes.ts
|
|
23473
|
-
var
|
|
24193
|
+
var import_yaml6 = __toESM(require_dist(), 1);
|
|
23474
24194
|
import { existsSync as existsSync9, readFileSync as readFileSync7, mkdirSync as mkdirSync3, writeFileSync as writeFileSync4, readdirSync as readdirSync5 } from "node:fs";
|
|
23475
24195
|
import { join as join10 } from "node:path";
|
|
24196
|
+
|
|
24197
|
+
// plugins/kxm/src/role.ts
|
|
24198
|
+
var import_yaml5 = __toESM(require_dist(), 1);
|
|
24199
|
+
|
|
24200
|
+
// plugins/kxm/src/config.ts
|
|
24201
|
+
var import_yaml4 = __toESM(require_dist(), 1);
|
|
24202
|
+
|
|
24203
|
+
// plugins/kxm/src/routes.ts
|
|
23476
24204
|
var RETIRED_POLICY = ".kxm/producers.yaml";
|
|
23477
24205
|
var empty = () => ({ schema: "kxm.routes.v2", updatedAt: (/* @__PURE__ */ new Date()).toISOString(), admitted: [], disabled: [], roles: {} });
|
|
23478
24206
|
function loadRoutePolicy(root) {
|
|
23479
24207
|
if (existsSync9(join10(root, RETIRED_POLICY))) throw new Error(`retired ${RETIRED_POLICY} present; use .kxm/routes.yaml (kxm.routes.v2)`);
|
|
23480
24208
|
const path = join10(root, ".kxm", "routes.yaml");
|
|
23481
24209
|
if (!existsSync9(path)) return empty();
|
|
23482
|
-
const value = (0,
|
|
24210
|
+
const value = (0, import_yaml6.parse)(readFileSync7(path, "utf8"));
|
|
23483
24211
|
if (value?.schema !== "kxm.routes.v2" || !Array.isArray(value.admitted)) throw new Error("invalid .kxm/routes.yaml");
|
|
23484
24212
|
return { schema: "kxm.routes.v2", updatedAt: typeof value.updatedAt === "string" ? value.updatedAt : (/* @__PURE__ */ new Date()).toISOString(), admitted: value.admitted.filter((x) => typeof x === "string"), disabled: Array.isArray(value.disabled) ? value.disabled.filter((x) => typeof x === "string") : [], roles: value.roles && typeof value.roles === "object" ? Object.fromEntries(Object.entries(value.roles).filter(([, v]) => Array.isArray(v)).map(([k, v]) => [k, v.filter((x) => typeof x === "string")])) : {} };
|
|
23485
24213
|
}
|
|
@@ -23489,8 +24217,8 @@ function listRoleBindings(root) {
|
|
|
23489
24217
|
if (!existsSync9(dir)) return result;
|
|
23490
24218
|
for (const file of readdirSync5(dir).filter((name) => name.endsWith(".yaml"))) {
|
|
23491
24219
|
const role = file.slice(0, -5);
|
|
23492
|
-
const value = (0,
|
|
23493
|
-
result[role] = (value.roster ?? []).map((entry) => entry.
|
|
24220
|
+
const value = (0, import_yaml6.parse)(readFileSync7(join10(dir, file), "utf8"));
|
|
24221
|
+
result[role] = (value.roster ?? []).map((entry) => entry.route).filter((route) => typeof route === "string");
|
|
23494
24222
|
}
|
|
23495
24223
|
return result;
|
|
23496
24224
|
}
|
|
@@ -23909,8 +24637,8 @@ function oneOf(value, field, allowed) {
|
|
|
23909
24637
|
}
|
|
23910
24638
|
return value;
|
|
23911
24639
|
}
|
|
23912
|
-
function idRefs(value, field,
|
|
23913
|
-
if (value === void 0 || value === null) return
|
|
24640
|
+
function idRefs(value, field, required2 = false) {
|
|
24641
|
+
if (value === void 0 || value === null) return required2 ? [] : void 0;
|
|
23914
24642
|
if (!Array.isArray(value)) {
|
|
23915
24643
|
throw new ProtocolError(400, `${field} must be an array of identifiers`, "invalid_context_field");
|
|
23916
24644
|
}
|
|
@@ -24445,7 +25173,7 @@ function memoryRecordToContextItem(record2, project) {
|
|
|
24445
25173
|
}
|
|
24446
25174
|
|
|
24447
25175
|
// plugins/kxm/src/memory.ts
|
|
24448
|
-
var
|
|
25176
|
+
var import_yaml7 = __toESM(require_dist(), 1);
|
|
24449
25177
|
import { existsSync as existsSync10, mkdirSync as mkdirSync4, readdirSync as readdirSync6, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
|
|
24450
25178
|
import { extname as extname2, join as join11, resolve as resolve7 } from "node:path";
|
|
24451
25179
|
var MEMORY_SCHEMA = "kxm.memory.v1";
|
|
@@ -24477,7 +25205,7 @@ function parseFrontmatter(content) {
|
|
|
24477
25205
|
}
|
|
24478
25206
|
function parseMemoryRecord(raw, filename = "memory.md") {
|
|
24479
25207
|
const { frontmatter, body } = parseFrontmatter(raw);
|
|
24480
|
-
const data = (0,
|
|
25208
|
+
const data = (0, import_yaml7.parse)(frontmatter);
|
|
24481
25209
|
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
24482
25210
|
throw new Error(`invalid YAML frontmatter in ${filename}`);
|
|
24483
25211
|
}
|
|
@@ -24573,8 +25301,8 @@ function loadAuthoredMemory(repoRoot) {
|
|
|
24573
25301
|
}
|
|
24574
25302
|
|
|
24575
25303
|
// plugins/kxm/src/skills.ts
|
|
24576
|
-
var
|
|
24577
|
-
import { createHash as
|
|
25304
|
+
var import_yaml8 = __toESM(require_dist(), 1);
|
|
25305
|
+
import { createHash as createHash11 } from "node:crypto";
|
|
24578
25306
|
import { existsSync as existsSync11, mkdirSync as mkdirSync5, readdirSync as readdirSync7, readFileSync as readFileSync9, renameSync, rmSync, statSync as statSync2, writeFileSync as writeFileSync6 } from "node:fs";
|
|
24579
25307
|
import { dirname as dirname7, join as join12 } from "node:path";
|
|
24580
25308
|
var SKILL_CANDIDATE_SCHEMA = "kxm.skill-candidate.v1";
|
|
@@ -24599,7 +25327,7 @@ var SkillLifecycleError = class extends Error {
|
|
|
24599
25327
|
}
|
|
24600
25328
|
};
|
|
24601
25329
|
function skillContentSha256(content) {
|
|
24602
|
-
return
|
|
25330
|
+
return createHash11("sha256").update(content, "utf8").digest("hex");
|
|
24603
25331
|
}
|
|
24604
25332
|
function skillIdFor(name, contentSha256) {
|
|
24605
25333
|
const slug = name.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
|
|
@@ -24617,7 +25345,7 @@ function parseSkillFrontmatter(content) {
|
|
|
24617
25345
|
return { frontmatter: null, body: content };
|
|
24618
25346
|
}
|
|
24619
25347
|
try {
|
|
24620
|
-
const parsed = (0,
|
|
25348
|
+
const parsed = (0, import_yaml8.parse)(rawFm);
|
|
24621
25349
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
24622
25350
|
return { frontmatter: parsed, body: rawBody };
|
|
24623
25351
|
}
|
|
@@ -25242,7 +25970,7 @@ function evaluateArtifactsGate(context, definition) {
|
|
|
25242
25970
|
|
|
25243
25971
|
// plugins/kxm/src/engine-command.ts
|
|
25244
25972
|
import { spawn as spawn2 } from "node:child_process";
|
|
25245
|
-
import { createHash as
|
|
25973
|
+
import { createHash as createHash12 } from "node:crypto";
|
|
25246
25974
|
|
|
25247
25975
|
// plugins/kxm/src/safety-integrity.ts
|
|
25248
25976
|
var DESTRUCTIVE_COMMAND_PATTERNS = Object.freeze([
|
|
@@ -25279,7 +26007,7 @@ function createCommandObserver(input) {
|
|
|
25279
26007
|
return new CommandObserver(input);
|
|
25280
26008
|
}
|
|
25281
26009
|
var StreamDigest = class {
|
|
25282
|
-
hash =
|
|
26010
|
+
hash = createHash12("sha256");
|
|
25283
26011
|
bytes = 0;
|
|
25284
26012
|
complete = 0;
|
|
25285
26013
|
digest() {
|
|
@@ -26019,8 +26747,8 @@ function appendEvaluatedTail(seq, envelope2, ids, stepId, outcome) {
|
|
|
26019
26747
|
}
|
|
26020
26748
|
}
|
|
26021
26749
|
function evidenceKeyFor(envelope2, stepId) {
|
|
26022
|
-
const
|
|
26023
|
-
return
|
|
26750
|
+
const required2 = envelope2.plan.steps[stepId]?.requiredEvidence.filter((item) => item.kind === "gate") ?? [];
|
|
26751
|
+
return required2.length === 1 ? required2[0].key : null;
|
|
26024
26752
|
}
|
|
26025
26753
|
function placeholderReceipt(observationId, kind, now) {
|
|
26026
26754
|
return {
|
|
@@ -26126,7 +26854,7 @@ function hashFromStore(context, runId) {
|
|
|
26126
26854
|
}
|
|
26127
26855
|
|
|
26128
26856
|
// plugins/kxm/src/routing.ts
|
|
26129
|
-
import { createHash as
|
|
26857
|
+
import { createHash as createHash13 } from "node:crypto";
|
|
26130
26858
|
var ROUTING_RECORD_V2_SCHEMA = "kxm.routing-record.v2";
|
|
26131
26859
|
var BEHAVIORAL_HASH_VERSION = 1;
|
|
26132
26860
|
var MAX_PROVIDER_METADATA_FIELDS = 32;
|
|
@@ -26149,7 +26877,7 @@ function behavioralConfigHash(input) {
|
|
|
26149
26877
|
workflowDefinitionSha256: input.workflowDefinitionSha256?.trim() ?? null,
|
|
26150
26878
|
verifierConfigSha256: input.verifierConfigSha256?.trim() ?? null
|
|
26151
26879
|
};
|
|
26152
|
-
return
|
|
26880
|
+
return createHash13("sha256").update(JSON.stringify(canonical), "utf8").digest("hex");
|
|
26153
26881
|
}
|
|
26154
26882
|
function boundedInt(value, field) {
|
|
26155
26883
|
if (value === void 0 || value === null) return void 0;
|
|
@@ -26318,7 +27046,7 @@ function requireTrustedProducer(producer) {
|
|
|
26318
27046
|
}
|
|
26319
27047
|
}
|
|
26320
27048
|
function hashCapabilitySecret(secret) {
|
|
26321
|
-
return `sha256:${
|
|
27049
|
+
return `sha256:${createHash14("sha256").update(`${CAPABILITY_PREFIX}${secret}`, "utf8").digest("hex")}`;
|
|
26322
27050
|
}
|
|
26323
27051
|
function mintKxmCapabilitySecret() {
|
|
26324
27052
|
const secret = `kxmcap_${randomBytes(32).toString("base64url")}`;
|
|
@@ -26451,7 +27179,7 @@ async function driveAdmitted(context, runId, producer, token, options = {}) {
|
|
|
26451
27179
|
return latest;
|
|
26452
27180
|
}
|
|
26453
27181
|
function newDriveId(runId, token, runtimeId, monotonicNs) {
|
|
26454
|
-
return `drv_${
|
|
27182
|
+
return `drv_${createHash14("sha256").update(`${runId}\0${token}\0${runtimeId}\0${monotonicNs}`, "utf8").digest("hex").slice(0, 24)}`;
|
|
26455
27183
|
}
|
|
26456
27184
|
function driveErrorSummary(error) {
|
|
26457
27185
|
const raw = error instanceof KxmConfigError ? error.issues[0]?.code : void 0;
|
|
@@ -27065,6 +27793,26 @@ function commitCommandUncertainty(context, prepared, token, reason, observation,
|
|
|
27065
27793
|
}
|
|
27066
27794
|
}
|
|
27067
27795
|
var kxmPanelDispatchSeams = {};
|
|
27796
|
+
function selectorsNamedByRoutes(projectRoot, routeIds) {
|
|
27797
|
+
const selectors = /* @__PURE__ */ new Set();
|
|
27798
|
+
for (const routeId2 of routeIds) {
|
|
27799
|
+
const file = join15(projectRoot, ".kxm", "models", `${routeId2}.yaml`);
|
|
27800
|
+
if (!existsSync12(file)) continue;
|
|
27801
|
+
let parsed;
|
|
27802
|
+
try {
|
|
27803
|
+
parsed = (0, import_yaml9.parse)(readFileSync10(file, "utf8"));
|
|
27804
|
+
} catch {
|
|
27805
|
+
continue;
|
|
27806
|
+
}
|
|
27807
|
+
const model = typeof parsed.model === "string" ? parsed.model : "";
|
|
27808
|
+
const vendor = typeof parsed.vendor === "string" ? parsed.vendor : "";
|
|
27809
|
+
const harness = typeof parsed.harness === "string" ? parsed.harness : "";
|
|
27810
|
+
if (model) selectors.add(model);
|
|
27811
|
+
if (vendor && model) selectors.add(`${vendor}/${model}`);
|
|
27812
|
+
if (harness && model) selectors.add(`${harness}/${model}`);
|
|
27813
|
+
}
|
|
27814
|
+
return [...selectors];
|
|
27815
|
+
}
|
|
27068
27816
|
function unreconciledPanelAttemptId(state) {
|
|
27069
27817
|
const current = state.currentStep;
|
|
27070
27818
|
if (!current) return void 0;
|
|
@@ -27084,7 +27832,7 @@ function resolveProducerRoute(projectRoot, step, agentId) {
|
|
|
27084
27832
|
const agentFile = join15(projectRoot, ".kxm", "agents", `${agentId}.yaml`);
|
|
27085
27833
|
if (existsSync12(agentFile)) {
|
|
27086
27834
|
try {
|
|
27087
|
-
const parsed = (0,
|
|
27835
|
+
const parsed = (0, import_yaml9.parse)(readFileSync10(agentFile, "utf8"));
|
|
27088
27836
|
if (typeof parsed?.model === "string") {
|
|
27089
27837
|
agentModel = parsed.model;
|
|
27090
27838
|
} else if (parsed?.model && typeof parsed.model === "object" && !Array.isArray(parsed.model)) {
|
|
@@ -27143,7 +27891,8 @@ function resolveProducerRoute(projectRoot, step, agentId) {
|
|
|
27143
27891
|
const role = agentId === "implementer" ? "writer" : agentId;
|
|
27144
27892
|
const roleBindings = listRoleBindings(projectRoot);
|
|
27145
27893
|
const roster = roleBindings[role];
|
|
27146
|
-
|
|
27894
|
+
const rosterSelectors = roster ? selectorsNamedByRoutes(projectRoot, roster) : void 0;
|
|
27895
|
+
if (rosterSelectors && !rosterSelectors.includes(selector)) {
|
|
27147
27896
|
return {
|
|
27148
27897
|
error: {
|
|
27149
27898
|
reason: "step_unsupported",
|
|
@@ -27464,7 +28213,7 @@ function birthMember(context, input) {
|
|
|
27464
28213
|
if (promptText === void 0) {
|
|
27465
28214
|
throw runtimeError("run_prompt_mismatch", run.runId, "prompt text missing from accepted run prompt store");
|
|
27466
28215
|
}
|
|
27467
|
-
const promptHash =
|
|
28216
|
+
const promptHash = createHash14("sha256").update(promptText, "utf8").digest("hex");
|
|
27468
28217
|
const expectedHash = run.promptSha256.replace(/^sha256:/, "");
|
|
27469
28218
|
if (promptHash !== expectedHash) {
|
|
27470
28219
|
throw runtimeError("run_prompt_mismatch", run.runId, "prompt text does not match accepted promptSha256");
|
|
@@ -28367,7 +29116,7 @@ function unsupportedStep(plan, step, producerId) {
|
|
|
28367
29116
|
function readYamlRecord(path) {
|
|
28368
29117
|
if (!existsSync12(path)) return void 0;
|
|
28369
29118
|
try {
|
|
28370
|
-
const parsed = (0,
|
|
29119
|
+
const parsed = (0, import_yaml9.parse)(readFileSync10(path, "utf8"));
|
|
28371
29120
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
|
|
28372
29121
|
} catch {
|
|
28373
29122
|
return void 0;
|
|
@@ -29605,7 +30354,7 @@ function publishKxmSupervisorToken(paths, token) {
|
|
|
29605
30354
|
}
|
|
29606
30355
|
}
|
|
29607
30356
|
function hashKxmSupervisorToken(token) {
|
|
29608
|
-
return `sha256:${
|
|
30357
|
+
return `sha256:${createHash15("sha256").update(`kxm-runtime-supervisor\0${token}`, "utf8").digest("hex")}`;
|
|
29609
30358
|
}
|
|
29610
30359
|
function readKxmSupervisorToken(paths) {
|
|
29611
30360
|
const file = kxmSupervisorTokenFile(paths);
|
|
@@ -29968,7 +30717,7 @@ async function startKxmRuntimeSupervisor(options = {}) {
|
|
|
29968
30717
|
try {
|
|
29969
30718
|
return await startKxmRuntimeSupervisorInner(paths, options.port, now, env);
|
|
29970
30719
|
} catch (error) {
|
|
29971
|
-
if (!(error instanceof KxmConfigError && error.issues.some((
|
|
30720
|
+
if (!(error instanceof KxmConfigError && error.issues.some((issue4) => issue4.code === "runtime_supervisor_conflict"))) {
|
|
29972
30721
|
recordSupervisorError(paths, error.message);
|
|
29973
30722
|
}
|
|
29974
30723
|
throw error;
|
|
@@ -29986,7 +30735,7 @@ async function startKxmRuntimeSupervisorInner(paths, requestedPortOption, now, e
|
|
|
29986
30735
|
const token = randomBytes2(32).toString("hex");
|
|
29987
30736
|
const tokenHash = hashKxmSupervisorToken(token);
|
|
29988
30737
|
const registry = new KxmRuntimeRegistry(paths.registryDb);
|
|
29989
|
-
const runtimeId = `rtm_${
|
|
30738
|
+
const runtimeId = `rtm_${createHash15("sha256").update(`${paths.stateRoot}\0${process.pid}\0${now()}\0${randomBytes2(16).toString("hex")}`, "utf8").digest("hex").slice(0, 24)}`;
|
|
29990
30739
|
const requestedPort = requestedPortOption ?? 0;
|
|
29991
30740
|
let activeRuntimeId = runtimeId;
|
|
29992
30741
|
const contexts = /* @__PURE__ */ new Map();
|