@kontextmind/kxm 0.7.145 → 0.7.146
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/README.md +20 -2
- package/.kxm/agents/{coordinator.yaml → planner.yaml} +2 -0
- package/.kxm/agents/{critic-arch.yaml → reviewer-arch.yaml} +2 -0
- package/.kxm/agents/{critic-cli.yaml → reviewer-cli.yaml} +2 -0
- package/.kxm/agents/{implementer.yaml → writer.yaml} +2 -0
- package/.kxm/roles/planner.yaml +4 -2
- package/.kxm/roles/reviewer-arch.yaml +4 -2
- package/.kxm/roles/reviewer-cli.yaml +4 -2
- package/.kxm/roles/writer.yaml +6 -3
- package/.kxm/workflows/default.yaml +20 -12
- package/.kxm/workflows/land.yaml +1 -1
- package/.kxm/workflows/{review-arch-only.yaml → reviewer-arch-only.yaml} +7 -3
- package/.kxm/workflows/{review-cli-only.yaml → reviewer-cli-only.yaml} +7 -3
- package/.kxm/workflows/{implement-only.yaml → writer-only.yaml} +8 -4
- package/CHANGELOG.md +22 -5
- package/docs/contributing/harness-routing-internals.md +17 -11
- package/docs/reference/cli-reference.md +23 -24
- package/docs/reference/config-reference.md +69 -62
- package/docs/reference/harness-routing.md +6 -5
- package/docs/reference/workflow-catalog.md +3 -3
- package/package.json +3 -3
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/cli.js +1144 -804
- package/plugins/kxm/dist/mcp-server.js +1 -1
- package/plugins/kxm/dist/runtime-supervisor.js +636 -308
- package/plugins/kxm/dist/runtime.js +707 -379
- package/plugins/kxm/dist/server.js +49 -5
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/skills/kxm-project-setup/SKILL.md +3 -3
- package/plugins/kxm/src/cli/project.ts +2 -1
- package/plugins/kxm/src/cli/roles.ts +3 -4
- package/plugins/kxm/src/engine.ts +9 -6
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/policy-draft.mjs +11 -5
- package/plugins/kxm/src/project-config.ts +49 -11
- package/plugins/kxm/src/runtime-service.ts +2 -1
- package/plugins/kxm/src/template.ts +39 -10
- package/plugins/kxm/src/workflow-manager.ts +34 -28
- package/plugins/kxm/src/workforce-names.d.mts +38 -0
- package/plugins/kxm/src/workforce-names.mjs +317 -0
- package/schemas/agent.schema.json +7 -0
- package/schemas/model.schema.json +12 -0
- package/schemas/workflow.schema.json +14 -0
- package/scripts/harness-run.mjs +1 -1
- package/scripts/native-critic.mjs +5 -5
- package/scripts/roster-policy.mjs +9 -3
- package/scripts/workforce-lint.mjs +18 -0
|
@@ -14779,15 +14779,15 @@ var require_dist = __commonJS({
|
|
|
14779
14779
|
|
|
14780
14780
|
// plugins/kxm/src/runtime-service.ts
|
|
14781
14781
|
import { createHash as createHash9 } from "node:crypto";
|
|
14782
|
-
import { existsSync as
|
|
14783
|
-
import { join as
|
|
14782
|
+
import { existsSync as existsSync8, readdirSync as readdirSync4, readFileSync as readFileSync5 } from "node:fs";
|
|
14783
|
+
import { join as join8 } from "node:path";
|
|
14784
14784
|
|
|
14785
14785
|
// plugins/kxm/src/project-config.ts
|
|
14786
14786
|
var import__ = __toESM(require__(), 1);
|
|
14787
14787
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
14788
14788
|
import { createHash as createHash3 } from "node:crypto";
|
|
14789
|
-
import { existsSync as
|
|
14790
|
-
import { basename, dirname as dirname2, extname, isAbsolute, join as
|
|
14789
|
+
import { existsSync as existsSync4, lstatSync, readFileSync as readFileSync2, readdirSync as readdirSync2, realpathSync } from "node:fs";
|
|
14790
|
+
import { basename, dirname as dirname2, extname, isAbsolute, join as join3, relative, resolve, sep } from "node:path";
|
|
14791
14791
|
|
|
14792
14792
|
// plugins/kxm/src/restricted-yaml.mjs
|
|
14793
14793
|
var import_yaml = __toESM(require_dist(), 1);
|
|
@@ -14939,6 +14939,7 @@ var MODES = Object.freeze(["headless", "interactive", "either"]);
|
|
|
14939
14939
|
var MODEL_KEYS = Object.freeze([
|
|
14940
14940
|
"schema",
|
|
14941
14941
|
"id",
|
|
14942
|
+
"aliases",
|
|
14942
14943
|
"harness",
|
|
14943
14944
|
"model",
|
|
14944
14945
|
"vendor",
|
|
@@ -15139,6 +15140,9 @@ function validateModelShape(document, id, label, issues) {
|
|
|
15139
15140
|
if (document.id !== void 0 && document.id !== id) {
|
|
15140
15141
|
issues.push(issue2("schema", "identity_mismatch", label, `declared id ${String(document.id)} does not match ${id}`));
|
|
15141
15142
|
}
|
|
15143
|
+
if (document.aliases !== void 0 && !identifierList(document.aliases)) {
|
|
15144
|
+
issues.push(issue2("schema", "schema_pattern", label, "aliases must be unique identifiers"));
|
|
15145
|
+
}
|
|
15142
15146
|
if (document.harness !== void 0 && !identifier(document.harness)) {
|
|
15143
15147
|
issues.push(issue2("schema", "schema_pattern", label, "harness must be an identifier"));
|
|
15144
15148
|
}
|
|
@@ -15401,9 +15405,9 @@ function validateRoleSemantics(role, id, label, models, options, issues) {
|
|
|
15401
15405
|
}
|
|
15402
15406
|
if (id === role.purpose && CRITIC_PURPOSES.includes(role.purpose)) {
|
|
15403
15407
|
const admitted = role.roster.map((entry) => models.get(entry.route)).filter((model) => model && admittedRoute(model));
|
|
15404
|
-
if (admitted.length
|
|
15405
|
-
issues.push(issue2("semantic", "critic_route_count", label, "required critic purpose files must list
|
|
15406
|
-
} else if (admitted
|
|
15408
|
+
if (admitted.length < 1) {
|
|
15409
|
+
issues.push(issue2("semantic", "critic_route_count", label, "required critic purpose files must list at least one admitted route"));
|
|
15410
|
+
} else if (role.permission !== "read-only" || admitted.some((model) => model.permissions.length !== 1 || model.permissions[0] !== "read-only")) {
|
|
15407
15411
|
issues.push(issue2("semantic", "permission_escalation", label, "critic purpose files must be read-only"));
|
|
15408
15412
|
}
|
|
15409
15413
|
}
|
|
@@ -15435,7 +15439,7 @@ function validateCriticVendors(roles, models, options, issues) {
|
|
|
15435
15439
|
const role = roles.get(purpose);
|
|
15436
15440
|
if (!role || role.purpose !== purpose) continue;
|
|
15437
15441
|
const admitted = (role.roster ?? []).map((entry) => models.get(entry.route)).filter((model) => model && admittedRoute(model));
|
|
15438
|
-
if (admitted.length
|
|
15442
|
+
if (admitted.length >= 1) critics.push({ purpose, vendor: canonicalVendor(admitted[0].vendor, aliases) });
|
|
15439
15443
|
}
|
|
15440
15444
|
if (critics.length === 2 && critics[0].vendor === critics[1].vendor) {
|
|
15441
15445
|
issues.push(issue2("semantic", "critic_vendor_collision", "roles/reviewer-arch", "required critics must have independent vendors"));
|
|
@@ -15498,8 +15502,272 @@ function validatePolicyDraft(input, options) {
|
|
|
15498
15502
|
return { ok: true, data };
|
|
15499
15503
|
}
|
|
15500
15504
|
|
|
15501
|
-
// plugins/kxm/src/
|
|
15505
|
+
// plugins/kxm/src/workforce-names.mjs
|
|
15502
15506
|
var import_yaml2 = __toESM(require_dist(), 1);
|
|
15507
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
15508
|
+
import { join } from "node:path";
|
|
15509
|
+
var ROSTER_EFFORTS = /* @__PURE__ */ new Set(["off", "minimal", "low", "medium", "high", "xhigh", "max"]);
|
|
15510
|
+
var ROLE_IDS = Object.freeze([
|
|
15511
|
+
"writer",
|
|
15512
|
+
"planner",
|
|
15513
|
+
"reviewer-arch",
|
|
15514
|
+
"reviewer-cli",
|
|
15515
|
+
"experiment"
|
|
15516
|
+
]);
|
|
15517
|
+
var ROUTE_RENAMES = Object.freeze([
|
|
15518
|
+
["grok-native", "grok-grok-4-7"],
|
|
15519
|
+
["qwen-openrouter-pi", "pi-qwen3-coder-plus-openrouter"],
|
|
15520
|
+
["gemini-agy", "agy-gemini-3-8-flash-high"],
|
|
15521
|
+
["fable-claude", "claude-fable"],
|
|
15522
|
+
["sol-codex", "codex-gpt-5-6-sol"],
|
|
15523
|
+
["grok-default", "grok-grok-4-6"],
|
|
15524
|
+
["fable-default", "claude-fable"]
|
|
15525
|
+
]);
|
|
15526
|
+
var AGENT_RENAMES = Object.freeze([
|
|
15527
|
+
["coordinator", "planner"],
|
|
15528
|
+
["implementer", "writer"],
|
|
15529
|
+
["critic-arch", "reviewer-arch"],
|
|
15530
|
+
["critic-cli", "reviewer-cli"]
|
|
15531
|
+
]);
|
|
15532
|
+
var WORKFLOW_RENAMES = Object.freeze([
|
|
15533
|
+
["implement-only", "writer-only"],
|
|
15534
|
+
["review-arch-only", "reviewer-arch-only"],
|
|
15535
|
+
["review-cli-only", "reviewer-cli-only"]
|
|
15536
|
+
]);
|
|
15537
|
+
var STEP_RENAMES = Object.freeze([
|
|
15538
|
+
["implement", "writer"],
|
|
15539
|
+
["critic-arch", "reviewer-arch"],
|
|
15540
|
+
["review-arch", "reviewer-arch"],
|
|
15541
|
+
["critic-cli", "reviewer-cli"],
|
|
15542
|
+
["review-cli", "reviewer-cli"]
|
|
15543
|
+
]);
|
|
15544
|
+
var RENAMES = Object.freeze({
|
|
15545
|
+
route: ROUTE_RENAMES,
|
|
15546
|
+
agent: AGENT_RENAMES,
|
|
15547
|
+
workflow: WORKFLOW_RENAMES,
|
|
15548
|
+
step: STEP_RENAMES
|
|
15549
|
+
});
|
|
15550
|
+
var RESERVED_WORKFLOW_IDS = /* @__PURE__ */ new Set(["default", "land"]);
|
|
15551
|
+
var warned = /* @__PURE__ */ new Set();
|
|
15552
|
+
function noteDeprecatedId(kind, from, to) {
|
|
15553
|
+
const key = `${kind}\0${from}\0${to}`;
|
|
15554
|
+
if (warned.has(key)) return;
|
|
15555
|
+
warned.add(key);
|
|
15556
|
+
if (process.env.KXM_QUIET_ALIASES === "1") return;
|
|
15557
|
+
console.warn(`kxm: deprecated ${kind} id '${from}' resolves to '${to}'`);
|
|
15558
|
+
}
|
|
15559
|
+
function resolveRenamedId(requested, knownIds, pairs) {
|
|
15560
|
+
if (typeof requested !== "string" || knownIds.has(requested)) return requested;
|
|
15561
|
+
for (const [oldId, newId2] of pairs) {
|
|
15562
|
+
if (requested === oldId && knownIds.has(newId2)) return newId2;
|
|
15563
|
+
if (requested === newId2 && knownIds.has(oldId)) return oldId;
|
|
15564
|
+
}
|
|
15565
|
+
return requested;
|
|
15566
|
+
}
|
|
15567
|
+
function canonicalRouteId(harness, model) {
|
|
15568
|
+
if (typeof harness !== "string" || typeof model !== "string" || !harness || !model) return void 0;
|
|
15569
|
+
const parts = model.split("/").filter((part) => part.length > 0);
|
|
15570
|
+
if (parts.length === 0) return void 0;
|
|
15571
|
+
const slug = parts[parts.length - 1].replaceAll(".", "-").toLowerCase();
|
|
15572
|
+
const provider = parts.length > 1 ? parts[0].replaceAll(".", "-").toLowerCase() : void 0;
|
|
15573
|
+
const id = provider ? `${harness}-${slug}-${provider}` : `${harness}-${slug}`;
|
|
15574
|
+
return /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u.test(id) && id.length <= 64 ? id : void 0;
|
|
15575
|
+
}
|
|
15576
|
+
function aliasList(value) {
|
|
15577
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
15578
|
+
}
|
|
15579
|
+
function lookupById(records, requested, kind) {
|
|
15580
|
+
const list = [...records].filter((record2) => typeof record2?.id === "string" && record2.id.length > 0);
|
|
15581
|
+
const known = new Set(list.map((record2) => record2.id));
|
|
15582
|
+
const direct = list.find((record2) => record2.id === requested);
|
|
15583
|
+
if (direct) return { record: direct, viaAlias: false };
|
|
15584
|
+
for (const record2 of list) {
|
|
15585
|
+
if (aliasList(record2.aliases).includes(requested)) {
|
|
15586
|
+
noteDeprecatedId(kind, requested, record2.id);
|
|
15587
|
+
return { record: record2, viaAlias: true };
|
|
15588
|
+
}
|
|
15589
|
+
}
|
|
15590
|
+
const renamed = resolveRenamedId(requested, known, RENAMES[kind] ?? []);
|
|
15591
|
+
if (renamed !== requested) {
|
|
15592
|
+
const record2 = list.find((item) => item.id === renamed);
|
|
15593
|
+
if (record2) {
|
|
15594
|
+
noteDeprecatedId(kind, requested, renamed);
|
|
15595
|
+
return { record: record2, viaAlias: true };
|
|
15596
|
+
}
|
|
15597
|
+
}
|
|
15598
|
+
return void 0;
|
|
15599
|
+
}
|
|
15600
|
+
function findYamlBasename(directory, requested, kind) {
|
|
15601
|
+
if (typeof requested !== "string" || !requested) return void 0;
|
|
15602
|
+
if (existsSync(join(directory, `${requested}.yaml`))) return requested;
|
|
15603
|
+
if (!existsSync(directory)) return void 0;
|
|
15604
|
+
const found = [];
|
|
15605
|
+
for (const name of readdirSync(directory)) {
|
|
15606
|
+
if (!name.endsWith(".yaml") || name === "inventory.yaml") continue;
|
|
15607
|
+
const id = name.slice(0, -5);
|
|
15608
|
+
let doc;
|
|
15609
|
+
try {
|
|
15610
|
+
doc = import_yaml2.default.parse(readFileSync(join(directory, name), "utf8"));
|
|
15611
|
+
} catch {
|
|
15612
|
+
continue;
|
|
15613
|
+
}
|
|
15614
|
+
const aliases = doc && typeof doc === "object" ? aliasList(doc.aliases) : [];
|
|
15615
|
+
found.push({ id, aliases });
|
|
15616
|
+
}
|
|
15617
|
+
const hit = lookupById(found, requested, kind);
|
|
15618
|
+
return hit?.record.id;
|
|
15619
|
+
}
|
|
15620
|
+
function readYamlDir(directory, skipInventory) {
|
|
15621
|
+
if (!existsSync(directory)) return [];
|
|
15622
|
+
const docs = [];
|
|
15623
|
+
for (const name of readdirSync(directory)) {
|
|
15624
|
+
if (!name.endsWith(".yaml")) continue;
|
|
15625
|
+
const id = name.slice(0, -5);
|
|
15626
|
+
if (skipInventory && id === "inventory") continue;
|
|
15627
|
+
let doc;
|
|
15628
|
+
try {
|
|
15629
|
+
doc = import_yaml2.default.parse(readFileSync(join(directory, name), "utf8"));
|
|
15630
|
+
} catch (error) {
|
|
15631
|
+
docs.push({ id, file: `${directory}/${name}`, error: error instanceof Error ? error.message : "invalid YAML" });
|
|
15632
|
+
continue;
|
|
15633
|
+
}
|
|
15634
|
+
if (!doc || typeof doc !== "object" || Array.isArray(doc)) {
|
|
15635
|
+
docs.push({ id, file: `${directory}/${name}`, error: "root must be a mapping" });
|
|
15636
|
+
continue;
|
|
15637
|
+
}
|
|
15638
|
+
docs.push({ id, file: name, doc });
|
|
15639
|
+
}
|
|
15640
|
+
return docs;
|
|
15641
|
+
}
|
|
15642
|
+
function push(bucket, severity, code, file, message) {
|
|
15643
|
+
bucket.push({ severity, code, file, message });
|
|
15644
|
+
}
|
|
15645
|
+
function selectorsFor(doc) {
|
|
15646
|
+
const model = typeof doc.model === "string" ? doc.model : "";
|
|
15647
|
+
const vendor = typeof doc.vendor === "string" ? doc.vendor : "";
|
|
15648
|
+
const harness = typeof doc.harness === "string" ? doc.harness : "";
|
|
15649
|
+
return [model, vendor && model ? `${vendor}/${model}` : "", harness && model ? `${harness}/${model}` : ""].filter(Boolean);
|
|
15650
|
+
}
|
|
15651
|
+
function lintWorkforce(root) {
|
|
15652
|
+
const errors = [];
|
|
15653
|
+
const warnings = [];
|
|
15654
|
+
const models = readYamlDir(join(root, ".kxm", "models"), true).filter((entry) => !entry.error);
|
|
15655
|
+
for (const entry of readYamlDir(join(root, ".kxm", "models"), true)) {
|
|
15656
|
+
if (entry.error) push(errors, "error", "invalid_yaml", `.kxm/models/${entry.id}.yaml`, entry.error);
|
|
15657
|
+
}
|
|
15658
|
+
const roles = readYamlDir(join(root, ".kxm", "roles"), false).filter((entry) => !entry.error);
|
|
15659
|
+
const agents = readYamlDir(join(root, ".kxm", "agents"), false).filter((entry) => !entry.error);
|
|
15660
|
+
const workflows = readYamlDir(join(root, ".kxm", "workflows"), false).filter((entry) => !entry.error);
|
|
15661
|
+
let admitted = [];
|
|
15662
|
+
const routesFile = join(root, ".kxm", "routes.yaml");
|
|
15663
|
+
if (existsSync(routesFile)) {
|
|
15664
|
+
try {
|
|
15665
|
+
const policy = import_yaml2.default.parse(readFileSync(routesFile, "utf8"));
|
|
15666
|
+
admitted = Array.isArray(policy?.admitted) ? policy.admitted.filter((item) => typeof item === "string") : [];
|
|
15667
|
+
} catch (error) {
|
|
15668
|
+
push(errors, "error", "invalid_yaml", ".kxm/routes.yaml", error instanceof Error ? error.message : "invalid YAML");
|
|
15669
|
+
}
|
|
15670
|
+
}
|
|
15671
|
+
const modelRecords = models.map((entry) => ({ id: entry.id, aliases: aliasList(entry.doc.aliases), entry }));
|
|
15672
|
+
const used = /* @__PURE__ */ new Set();
|
|
15673
|
+
for (const role of roles) {
|
|
15674
|
+
const purpose = role.doc.purpose;
|
|
15675
|
+
if (typeof purpose === "string" && role.id !== purpose) {
|
|
15676
|
+
push(errors, "error", "role_id_convention", `.kxm/roles/${role.id}.yaml`, `role id '${role.id}' must equal purpose '${purpose}'`);
|
|
15677
|
+
}
|
|
15678
|
+
const roster = Array.isArray(role.doc.roster) ? role.doc.roster : [];
|
|
15679
|
+
for (const item of roster) {
|
|
15680
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) continue;
|
|
15681
|
+
const route = item.route;
|
|
15682
|
+
const effort = item.effort;
|
|
15683
|
+
const routeLabel = typeof route === "string" && route ? route : "(missing route)";
|
|
15684
|
+
if (typeof effort !== "string" || !ROSTER_EFFORTS.has(effort)) {
|
|
15685
|
+
const detail = typeof effort === "string" ? `effort '${effort}' is not one of off, minimal, low, medium, high, xhigh, max` : "has no effort";
|
|
15686
|
+
push(errors, "error", "roster_effort_required", `.kxm/roles/${role.id}.yaml`, `roster route '${routeLabel}' ${detail}`);
|
|
15687
|
+
}
|
|
15688
|
+
if (typeof route !== "string" || !route) continue;
|
|
15689
|
+
const found = lookupById(modelRecords, route, "route");
|
|
15690
|
+
if (!found) {
|
|
15691
|
+
push(errors, "error", "route_unknown", `.kxm/roles/${role.id}.yaml`, `roster route '${route}' does not match a model file`);
|
|
15692
|
+
continue;
|
|
15693
|
+
}
|
|
15694
|
+
used.add(found.record.id);
|
|
15695
|
+
if (found.viaAlias) {
|
|
15696
|
+
push(warnings, "warning", "deprecated_id", `.kxm/roles/${role.id}.yaml`, `roster route '${route}' is a deprecated alias of '${found.record.id}'`);
|
|
15697
|
+
}
|
|
15698
|
+
}
|
|
15699
|
+
}
|
|
15700
|
+
const covered = /* @__PURE__ */ new Set();
|
|
15701
|
+
for (const model of models) {
|
|
15702
|
+
const doc = model.doc;
|
|
15703
|
+
if (typeof doc.id === "string" && doc.id !== model.id) {
|
|
15704
|
+
push(errors, "error", "route_id_convention", `.kxm/models/${model.id}.yaml`, `declared id '${doc.id}' does not match the filename`);
|
|
15705
|
+
}
|
|
15706
|
+
const expected = canonicalRouteId(doc.harness, doc.model);
|
|
15707
|
+
if (!expected || model.id !== expected) {
|
|
15708
|
+
push(errors, "error", "route_id_convention", `.kxm/models/${model.id}.yaml`, `route id '${model.id}' must be '${expected ?? "<harness>-<model-slug>[-<provider>]"}'`);
|
|
15709
|
+
}
|
|
15710
|
+
if (!used.has(model.id)) {
|
|
15711
|
+
push(errors, "error", "route_unused", `.kxm/models/${model.id}.yaml`, `route '${model.id}' is not on any role roster`);
|
|
15712
|
+
}
|
|
15713
|
+
const selectorHit = selectorsFor(doc).find((selector) => admitted.includes(selector));
|
|
15714
|
+
if (!selectorHit) {
|
|
15715
|
+
push(errors, "error", "route_not_admitted", `.kxm/models/${model.id}.yaml`, `model '${doc.model ?? ""}' is not an admitted selector`);
|
|
15716
|
+
} else {
|
|
15717
|
+
covered.add(selectorHit);
|
|
15718
|
+
}
|
|
15719
|
+
}
|
|
15720
|
+
for (const selector of admitted) {
|
|
15721
|
+
if (!covered.has(selector)) {
|
|
15722
|
+
push(warnings, "warning", "admitted_unrouted", ".kxm/routes.yaml", `admitted model '${selector}' is not used by any route`);
|
|
15723
|
+
}
|
|
15724
|
+
}
|
|
15725
|
+
const agentRecords = agents.map((entry) => ({ id: entry.id, aliases: aliasList(entry.doc.aliases), entry }));
|
|
15726
|
+
for (const agent of agents) {
|
|
15727
|
+
const role = agent.doc.role;
|
|
15728
|
+
if (typeof role !== "string" || agent.id !== role) {
|
|
15729
|
+
push(errors, "error", "agent_id_convention", `.kxm/agents/${agent.id}.yaml`, `agent id '${agent.id}' must equal its role '${role ?? ""}'`);
|
|
15730
|
+
}
|
|
15731
|
+
}
|
|
15732
|
+
for (const workflow of workflows) {
|
|
15733
|
+
if (!RESERVED_WORKFLOW_IDS.has(workflow.id) && !ROLE_IDS.some((role) => workflow.id === `${role}-only`)) {
|
|
15734
|
+
push(errors, "error", "workflow_id_convention", `.kxm/workflows/${workflow.id}.yaml`, `workflow id '${workflow.id}' must be default, land, or <role>-only`);
|
|
15735
|
+
}
|
|
15736
|
+
const steps = Array.isArray(workflow.doc.steps) ? workflow.doc.steps : [];
|
|
15737
|
+
const realIds = new Set(steps.map((step) => step && typeof step.id === "string" ? step.id : "").filter(Boolean));
|
|
15738
|
+
for (const step of steps) {
|
|
15739
|
+
if (!step || typeof step !== "object" || typeof step.id !== "string") continue;
|
|
15740
|
+
if (step.kind === "agent" || step.kind === "moa") {
|
|
15741
|
+
const agentName = typeof step.agent === "string" ? step.agent : "";
|
|
15742
|
+
const found = agentName ? lookupById(agentRecords, agentName, "agent") : void 0;
|
|
15743
|
+
if (!found) {
|
|
15744
|
+
push(errors, "error", "agent_unknown", `.kxm/workflows/${workflow.id}.yaml`, `step '${step.id}' references unknown agent '${agentName}'`);
|
|
15745
|
+
} else if (found.viaAlias) {
|
|
15746
|
+
push(warnings, "warning", "deprecated_id", `.kxm/workflows/${workflow.id}.yaml`, `step '${step.id}' agent '${agentName}' is a deprecated alias of '${found.record.id}'`);
|
|
15747
|
+
}
|
|
15748
|
+
const role = found?.record.entry.doc.role;
|
|
15749
|
+
if (typeof role === "string" && step.id !== role) {
|
|
15750
|
+
push(errors, "error", "step_id_convention", `.kxm/workflows/${workflow.id}.yaml`, `agent step '${step.id}' must use the role id '${role}'`);
|
|
15751
|
+
}
|
|
15752
|
+
} else if (step.kind === "gate") {
|
|
15753
|
+
const gate = typeof step.gate === "string" ? step.gate : "";
|
|
15754
|
+
const allowed = step.id === "verify" || step.id === gate || step.id.startsWith(`${workflow.id}-`);
|
|
15755
|
+
if (!allowed) {
|
|
15756
|
+
push(errors, "error", "step_id_convention", `.kxm/workflows/${workflow.id}.yaml`, `gate step '${step.id}' must be verify, the gate id, or ${workflow.id}-<name>`);
|
|
15757
|
+
}
|
|
15758
|
+
}
|
|
15759
|
+
for (const alias of aliasList(step.aliases)) {
|
|
15760
|
+
if (realIds.has(alias) && alias !== step.id) {
|
|
15761
|
+
push(errors, "error", "step_id_convention", `.kxm/workflows/${workflow.id}.yaml`, `step alias '${alias}' collides with another step id`);
|
|
15762
|
+
}
|
|
15763
|
+
}
|
|
15764
|
+
}
|
|
15765
|
+
}
|
|
15766
|
+
return { errors, warnings };
|
|
15767
|
+
}
|
|
15768
|
+
|
|
15769
|
+
// plugins/kxm/src/template.ts
|
|
15770
|
+
var import_yaml3 = __toESM(require_dist(), 1);
|
|
15503
15771
|
import { createHash as createHash2 } from "node:crypto";
|
|
15504
15772
|
var KXM_TEMPLATE_ID = "builtin-minimal";
|
|
15505
15773
|
var KXM_TEMPLATE_PROVENANCE_PATH = ".kxm/template-provenance.yaml";
|
|
@@ -15636,33 +15904,60 @@ function coreTemplate(projectId, projectName, variant) {
|
|
|
15636
15904
|
]);
|
|
15637
15905
|
if (variant === "v4-registry") {
|
|
15638
15906
|
const coordinator = files.get(".kxm/agents/coordinator.yaml");
|
|
15639
|
-
if (coordinator)
|
|
15907
|
+
if (coordinator) {
|
|
15908
|
+
files.delete(".kxm/agents/coordinator.yaml");
|
|
15909
|
+
files.set(".kxm/agents/planner.yaml", { ...coordinator, role: "planner", aliases: ["coordinator"] });
|
|
15910
|
+
}
|
|
15640
15911
|
const implementer = files.get(".kxm/agents/implementer.yaml");
|
|
15641
|
-
if (implementer)
|
|
15912
|
+
if (implementer) {
|
|
15913
|
+
files.delete(".kxm/agents/implementer.yaml");
|
|
15914
|
+
files.set(".kxm/agents/writer.yaml", { ...implementer, role: "writer", aliases: ["implementer"] });
|
|
15915
|
+
}
|
|
15642
15916
|
const workflow = files.get(".kxm/workflows/default.yaml");
|
|
15643
15917
|
if (workflow) {
|
|
15644
15918
|
const limits = { ...workflow.limits };
|
|
15645
15919
|
delete limits.maxAgentTimeMs;
|
|
15646
|
-
|
|
15920
|
+
const steps = workflow.steps.map((step) => {
|
|
15921
|
+
if (step.id === "plan") {
|
|
15922
|
+
const on = { ...step.on, passed: "writer" };
|
|
15923
|
+
return { ...step, agent: "planner", on };
|
|
15924
|
+
}
|
|
15925
|
+
if (step.id === "implement") {
|
|
15926
|
+
const assignments = { ...step.assignments, allowedAgents: ["writer"] };
|
|
15927
|
+
return { ...step, id: "writer", aliases: ["implement"], agent: "writer", assignments };
|
|
15928
|
+
}
|
|
15929
|
+
if (step.id === "verify") {
|
|
15930
|
+
const on = { ...step.on };
|
|
15931
|
+
const failure = on["implementation-failure"];
|
|
15932
|
+
if (failure && typeof failure === "object" && !Array.isArray(failure)) {
|
|
15933
|
+
on["implementation-failure"] = { ...failure, target: "writer" };
|
|
15934
|
+
}
|
|
15935
|
+
return { ...step, on };
|
|
15936
|
+
}
|
|
15937
|
+
return step;
|
|
15938
|
+
});
|
|
15939
|
+
files.set(".kxm/workflows/default.yaml", { ...workflow, coordinator: "planner", limits, steps });
|
|
15647
15940
|
}
|
|
15648
15941
|
files.set(".kxm/routes.yaml", {
|
|
15649
15942
|
schema: "kxm.routes.v2",
|
|
15650
|
-
updatedAt: "2026-09-
|
|
15943
|
+
updatedAt: "2026-09-27T00:00:00.000Z",
|
|
15651
15944
|
admitted: ["anthropic/fable", "xai/grok-4.6"],
|
|
15652
15945
|
disabled: []
|
|
15653
15946
|
});
|
|
15654
|
-
files.set(".kxm/models/grok-
|
|
15947
|
+
files.set(".kxm/models/grok-grok-4-6.yaml", {
|
|
15655
15948
|
schema: "kxm.model.v2",
|
|
15656
|
-
id: "grok-
|
|
15949
|
+
id: "grok-grok-4-6",
|
|
15950
|
+
aliases: ["grok-default"],
|
|
15657
15951
|
harness: "grok",
|
|
15658
15952
|
model: "grok-4.6",
|
|
15659
15953
|
vendor: "xai",
|
|
15660
15954
|
status: "admitted",
|
|
15661
15955
|
permissions: ["edit"]
|
|
15662
15956
|
});
|
|
15663
|
-
files.set(".kxm/models/fable
|
|
15957
|
+
files.set(".kxm/models/claude-fable.yaml", {
|
|
15664
15958
|
schema: "kxm.model.v2",
|
|
15665
|
-
id: "fable
|
|
15959
|
+
id: "claude-fable",
|
|
15960
|
+
aliases: ["fable-default"],
|
|
15666
15961
|
harness: "claude",
|
|
15667
15962
|
model: "fable",
|
|
15668
15963
|
vendor: "anthropic",
|
|
@@ -15675,7 +15970,7 @@ function coreTemplate(projectId, projectName, variant) {
|
|
|
15675
15970
|
purpose: "writer",
|
|
15676
15971
|
permission: "edit",
|
|
15677
15972
|
description: "Primary implementation agent.",
|
|
15678
|
-
roster: [{ route: "grok-
|
|
15973
|
+
roster: [{ route: "grok-grok-4-6", effort: "medium" }]
|
|
15679
15974
|
});
|
|
15680
15975
|
files.set(".kxm/roles/planner.yaml", {
|
|
15681
15976
|
schema: "kxm.role.v2",
|
|
@@ -15683,7 +15978,7 @@ function coreTemplate(projectId, projectName, variant) {
|
|
|
15683
15978
|
purpose: "planner",
|
|
15684
15979
|
permission: "read-only",
|
|
15685
15980
|
description: "Plans the change before implementation.",
|
|
15686
|
-
roster: [{ route: "fable
|
|
15981
|
+
roster: [{ route: "claude-fable", effort: "medium" }]
|
|
15687
15982
|
});
|
|
15688
15983
|
files.set(".kxm/gates.yaml", {
|
|
15689
15984
|
schema: "kxm.gate-registry.v1",
|
|
@@ -15704,7 +15999,7 @@ function renderKxmTemplate(projectId, projectName, variant = CURRENT_KXM_TEMPLAT
|
|
|
15704
15999
|
const values = coreTemplate(projectId, projectName, variant);
|
|
15705
16000
|
const coreFiles = /* @__PURE__ */ new Map();
|
|
15706
16001
|
const records = [...values.entries()].map(([path, value]) => {
|
|
15707
|
-
const bytes = Buffer.from((0,
|
|
16002
|
+
const bytes = Buffer.from((0, import_yaml3.stringify)(value, { lineWidth: 0 }), "utf8");
|
|
15708
16003
|
coreFiles.set(path, bytes);
|
|
15709
16004
|
return {
|
|
15710
16005
|
path,
|
|
@@ -15724,7 +16019,7 @@ function renderKxmTemplate(projectId, projectName, variant = CURRENT_KXM_TEMPLAT
|
|
|
15724
16019
|
const fileEntries = [...coreFiles.entries()];
|
|
15725
16020
|
fileEntries.push([
|
|
15726
16021
|
KXM_TEMPLATE_PROVENANCE_PATH,
|
|
15727
|
-
Buffer.from((0,
|
|
16022
|
+
Buffer.from((0, import_yaml3.stringify)(provenance, { lineWidth: 0 }), "utf8")
|
|
15728
16023
|
]);
|
|
15729
16024
|
fileEntries.sort(([left], [right]) => compareCodeUnits3(left, right));
|
|
15730
16025
|
const files = new Map(fileEntries);
|
|
@@ -15748,15 +16043,15 @@ function resolveKxmTemplateBaseline(value) {
|
|
|
15748
16043
|
}
|
|
15749
16044
|
|
|
15750
16045
|
// plugins/kxm/src/repo-root.ts
|
|
15751
|
-
import { existsSync } from "node:fs";
|
|
15752
|
-
import { dirname, join } from "node:path";
|
|
16046
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
16047
|
+
import { dirname, join as join2 } from "node:path";
|
|
15753
16048
|
import { fileURLToPath } from "node:url";
|
|
15754
16049
|
var ROOT_MARKERS = ["scripts/kxm-hub.mjs", "scripts/kxm.mjs"];
|
|
15755
16050
|
var MAX_WALK_DEPTH = 10;
|
|
15756
16051
|
function tryFindKxmRepoRoot(fromUrl = import.meta.url) {
|
|
15757
16052
|
let dir = dirname(fileURLToPath(fromUrl));
|
|
15758
16053
|
for (let depth = 0; depth < MAX_WALK_DEPTH; depth += 1) {
|
|
15759
|
-
if (ROOT_MARKERS.some((marker) =>
|
|
16054
|
+
if (ROOT_MARKERS.some((marker) => existsSync2(join2(dir, marker)))) return dir;
|
|
15760
16055
|
const parent = dirname(dir);
|
|
15761
16056
|
if (parent === dir) break;
|
|
15762
16057
|
dir = parent;
|
|
@@ -15773,7 +16068,7 @@ function findKxmRepoRoot(fromUrl = import.meta.url) {
|
|
|
15773
16068
|
|
|
15774
16069
|
// plugins/kxm/src/harness.ts
|
|
15775
16070
|
import { spawnSync } from "node:child_process";
|
|
15776
|
-
import { existsSync as
|
|
16071
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
15777
16072
|
import { win32 as win32Path } from "node:path";
|
|
15778
16073
|
|
|
15779
16074
|
// plugins/kxm/src/oneshot-process.ts
|
|
@@ -16412,7 +16707,7 @@ function findWinNpmInnerExe(cliId, options = {}) {
|
|
|
16412
16707
|
if ((options.platform ?? process.platform) !== "win32") return void 0;
|
|
16413
16708
|
const segments = WIN_NPM_INNER_EXE[cliId];
|
|
16414
16709
|
if (!segments) return void 0;
|
|
16415
|
-
const exists = options.existsSync ??
|
|
16710
|
+
const exists = options.existsSync ?? existsSync3;
|
|
16416
16711
|
const pathEnv = options.pathEnv ?? process.env.PATH ?? "";
|
|
16417
16712
|
for (const dir of pathEnv.split(";").filter(Boolean)) {
|
|
16418
16713
|
if (!exists(win32Path.join(dir, `${cliId}.cmd`))) continue;
|
|
@@ -17113,7 +17408,7 @@ var KxmConfigError = class extends Error {
|
|
|
17113
17408
|
}
|
|
17114
17409
|
};
|
|
17115
17410
|
function defaultKxmSchemaDir() {
|
|
17116
|
-
return
|
|
17411
|
+
return join3(findKxmRepoRoot(import.meta.url), "schemas");
|
|
17117
17412
|
}
|
|
17118
17413
|
var DEFAULT_SCHEMA_DIR = defaultKxmSchemaDir();
|
|
17119
17414
|
var RESOURCE_SCHEMA = Object.freeze({
|
|
@@ -17162,7 +17457,7 @@ function parseRestrictedYaml2(input, label = "<yaml>", limits = KXM_YAML_LIMITS)
|
|
|
17162
17457
|
}
|
|
17163
17458
|
}
|
|
17164
17459
|
function readJsonObject(file) {
|
|
17165
|
-
const parsed = JSON.parse(
|
|
17460
|
+
const parsed = JSON.parse(readFileSync2(file, "utf8"));
|
|
17166
17461
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error(`${file} is not a JSON object`);
|
|
17167
17462
|
return parsed;
|
|
17168
17463
|
}
|
|
@@ -17182,10 +17477,10 @@ var KxmSchemaRegistry = class {
|
|
|
17182
17477
|
constructor(schemasDir = DEFAULT_SCHEMA_DIR) {
|
|
17183
17478
|
this.schemasDir = resolve(schemasDir);
|
|
17184
17479
|
this.ajv = new import__.Ajv2020({ allErrors: true, strict: true, strictRequired: false });
|
|
17185
|
-
const common = readJsonObject(
|
|
17480
|
+
const common = readJsonObject(join3(this.schemasDir, "common.schema.json"));
|
|
17186
17481
|
this.ajv.addSchema(common);
|
|
17187
17482
|
for (const definition of Object.values(RESOURCE_SCHEMA)) {
|
|
17188
|
-
this.ajv.addSchema(readJsonObject(
|
|
17483
|
+
this.ajv.addSchema(readJsonObject(join3(this.schemasDir, definition.file)));
|
|
17189
17484
|
}
|
|
17190
17485
|
const localBindingsFile = "local-repository-bindings.schema.json";
|
|
17191
17486
|
const templateProvenanceFile = "template-provenance.schema.json";
|
|
@@ -17196,15 +17491,15 @@ var KxmSchemaRegistry = class {
|
|
|
17196
17491
|
const coordinatorFile = "coordinator.schema.json";
|
|
17197
17492
|
const intakeMessageFile = "intake-message.schema.json";
|
|
17198
17493
|
const syncEventFile = "sync-event.schema.json";
|
|
17199
|
-
this.ajv.addSchema(readJsonObject(
|
|
17200
|
-
this.ajv.addSchema(readJsonObject(
|
|
17201
|
-
this.ajv.addSchema(readJsonObject(
|
|
17202
|
-
this.ajv.addSchema(readJsonObject(
|
|
17203
|
-
this.ajv.addSchema(readJsonObject(
|
|
17204
|
-
this.ajv.addSchema(readJsonObject(
|
|
17205
|
-
this.ajv.addSchema(readJsonObject(
|
|
17206
|
-
this.ajv.addSchema(readJsonObject(
|
|
17207
|
-
this.ajv.addSchema(readJsonObject(
|
|
17494
|
+
this.ajv.addSchema(readJsonObject(join3(this.schemasDir, localBindingsFile)));
|
|
17495
|
+
this.ajv.addSchema(readJsonObject(join3(this.schemasDir, templateProvenanceFile)));
|
|
17496
|
+
this.ajv.addSchema(readJsonObject(join3(this.schemasDir, initOperationFile)));
|
|
17497
|
+
this.ajv.addSchema(readJsonObject(join3(this.schemasDir, permissionDiffFile)));
|
|
17498
|
+
this.ajv.addSchema(readJsonObject(join3(this.schemasDir, runEventFile)));
|
|
17499
|
+
this.ajv.addSchema(readJsonObject(join3(this.schemasDir, driveReceiptFile)));
|
|
17500
|
+
this.ajv.addSchema(readJsonObject(join3(this.schemasDir, coordinatorFile)));
|
|
17501
|
+
this.ajv.addSchema(readJsonObject(join3(this.schemasDir, intakeMessageFile)));
|
|
17502
|
+
this.ajv.addSchema(readJsonObject(join3(this.schemasDir, syncEventFile)));
|
|
17208
17503
|
for (const [kind, definition] of Object.entries(RESOURCE_SCHEMA)) {
|
|
17209
17504
|
const validator = this.ajv.getSchema(`https://schemas.kxm.dev/${definition.file}`);
|
|
17210
17505
|
if (!validator) throw new Error(`schema did not compile: ${definition.file}`);
|
|
@@ -17360,15 +17655,15 @@ function containedHostPath(root, candidate) {
|
|
|
17360
17655
|
function portableBindingIssue(root, pathHint, repositoryId) {
|
|
17361
17656
|
let current = root;
|
|
17362
17657
|
for (const segment of pathHint === "." ? [] : pathHint.split("/")) {
|
|
17363
|
-
current =
|
|
17364
|
-
if (!
|
|
17658
|
+
current = join3(current, segment);
|
|
17659
|
+
if (!existsSync4(current)) break;
|
|
17365
17660
|
const stat = lstatSync(current);
|
|
17366
17661
|
if (stat.isSymbolicLink()) {
|
|
17367
17662
|
return issue3("path", "repository_binding_path_link", ".kxm/project.yaml", `portable pathHint for ${repositoryId} traverses a symbolic link or junction`);
|
|
17368
17663
|
}
|
|
17369
17664
|
}
|
|
17370
17665
|
const binding = resolve(root, ...pathHint.split("/"));
|
|
17371
|
-
if (
|
|
17666
|
+
if (existsSync4(binding)) {
|
|
17372
17667
|
const realRoot = canonicalHostPath(root);
|
|
17373
17668
|
const realBinding = canonicalHostPath(binding);
|
|
17374
17669
|
if (!containedHostPath(realRoot, realBinding)) {
|
|
@@ -17419,7 +17714,7 @@ function readResource(registry, root, file, logicalPath, kind, id, containmentRo
|
|
|
17419
17714
|
parent = dirname2(parent);
|
|
17420
17715
|
}
|
|
17421
17716
|
if (!stat.isFile()) fail2("path", "resource_not_file", label, "configuration resource must be a regular file");
|
|
17422
|
-
return { kind, ...id === void 0 ? {} : { id }, file, logicalPath, value: resourceValue(registry,
|
|
17717
|
+
return { kind, ...id === void 0 ? {} : { id }, file, logicalPath, value: resourceValue(registry, readFileSync2(file), label, kind) };
|
|
17423
17718
|
}
|
|
17424
17719
|
function resourceValue(registry, input, label, kind) {
|
|
17425
17720
|
const value = parseRestrictedYaml2(input, label);
|
|
@@ -17428,14 +17723,14 @@ function resourceValue(registry, input, label, kind) {
|
|
|
17428
17723
|
return value;
|
|
17429
17724
|
}
|
|
17430
17725
|
function readTemplateProvenance(registry, root) {
|
|
17431
|
-
const file =
|
|
17432
|
-
if (!
|
|
17726
|
+
const file = join3(root, ".kxm", "template-provenance.yaml");
|
|
17727
|
+
if (!existsSync4(file)) return void 0;
|
|
17433
17728
|
const label = ".kxm/template-provenance.yaml";
|
|
17434
17729
|
const stat = lstatSync(file);
|
|
17435
17730
|
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
17436
17731
|
fail2("path", "resource_not_file", label, "template provenance must be a regular file, not a link or directory");
|
|
17437
17732
|
}
|
|
17438
|
-
const configRoot =
|
|
17733
|
+
const configRoot = join3(root, ".kxm");
|
|
17439
17734
|
let parent = dirname2(file);
|
|
17440
17735
|
while (parent !== root) {
|
|
17441
17736
|
const parentStat = lstatSync(parent);
|
|
@@ -17445,7 +17740,7 @@ function readTemplateProvenance(registry, root) {
|
|
|
17445
17740
|
if (parent === configRoot) break;
|
|
17446
17741
|
parent = dirname2(parent);
|
|
17447
17742
|
}
|
|
17448
|
-
const value = parseRestrictedYaml2(
|
|
17743
|
+
const value = parseRestrictedYaml2(readFileSync2(file), label);
|
|
17449
17744
|
const issues = registry.validateTemplateProvenance(value, label);
|
|
17450
17745
|
if (issues.length > 0) throw new KxmConfigError(issues);
|
|
17451
17746
|
if (!resolveKxmTemplateBaseline(value)) {
|
|
@@ -17476,37 +17771,37 @@ function readTemplateProvenance(registry, root) {
|
|
|
17476
17771
|
}
|
|
17477
17772
|
function listNamedResources(registry, root, directory, logicalDirectory, kind, replacedId, collected) {
|
|
17478
17773
|
const resources = /* @__PURE__ */ new Map();
|
|
17479
|
-
if (!
|
|
17774
|
+
if (!existsSync4(directory)) return resources;
|
|
17480
17775
|
const stat = lstatSync(directory);
|
|
17481
17776
|
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
17482
17777
|
fail2("path", "resource_directory_invalid", displayPath(root, directory), "resource directory must be a regular directory");
|
|
17483
17778
|
}
|
|
17484
17779
|
const issues = [];
|
|
17485
|
-
const entries =
|
|
17780
|
+
const entries = readdirSync2(directory, { withFileTypes: true }).sort((left, right) => compareCodeUnits4(left.name, right.name));
|
|
17486
17781
|
for (const entry of entries) {
|
|
17487
17782
|
if (entry.isDirectory()) {
|
|
17488
|
-
issues.push(issue3("path", "nested_resource_directory", displayPath(root,
|
|
17783
|
+
issues.push(issue3("path", "nested_resource_directory", displayPath(root, join3(directory, entry.name)), "nested resource directories are not allowed"));
|
|
17489
17784
|
continue;
|
|
17490
17785
|
}
|
|
17491
17786
|
if (!entry.name.toLowerCase().endsWith(".yaml") && !entry.name.toLowerCase().endsWith(".yml")) continue;
|
|
17492
17787
|
if (!entry.isFile() || extname(entry.name) !== ".yaml") {
|
|
17493
|
-
issues.push(issue3("path", "resource_filename_invalid", displayPath(root,
|
|
17788
|
+
issues.push(issue3("path", "resource_filename_invalid", displayPath(root, join3(directory, entry.name)), "resource must be a regular file with the exact .yaml extension"));
|
|
17494
17789
|
continue;
|
|
17495
17790
|
}
|
|
17496
17791
|
const id = basename(entry.name, ".yaml");
|
|
17497
17792
|
if (kind === "model" && id === "inventory") continue;
|
|
17498
17793
|
if (id === replacedId) continue;
|
|
17499
17794
|
if (!resourceIdentifier(id)) {
|
|
17500
|
-
issues.push(issue3("path", "resource_id_invalid", displayPath(root,
|
|
17795
|
+
issues.push(issue3("path", "resource_id_invalid", displayPath(root, join3(directory, entry.name)), `filename-derived identity ${id} is invalid or platform-reserved`));
|
|
17501
17796
|
continue;
|
|
17502
17797
|
}
|
|
17503
17798
|
const collision = [...resources.keys()].find((candidate) => candidate.toLocaleLowerCase("en-US") === id.toLocaleLowerCase("en-US"));
|
|
17504
17799
|
if (collision) {
|
|
17505
|
-
issues.push(issue3("path", "resource_id_collision", displayPath(root,
|
|
17800
|
+
issues.push(issue3("path", "resource_id_collision", displayPath(root, join3(directory, entry.name)), `${id} case-folds to existing ${collision}`));
|
|
17506
17801
|
continue;
|
|
17507
17802
|
}
|
|
17508
17803
|
try {
|
|
17509
|
-
const resource = readResource(registry, root,
|
|
17804
|
+
const resource = readResource(registry, root, join3(directory, entry.name), `${logicalDirectory}/${id}.yaml`, kind, id);
|
|
17510
17805
|
resources.set(id, resource);
|
|
17511
17806
|
} catch (error) {
|
|
17512
17807
|
if (error instanceof KxmConfigError) issues.push(...error.issues);
|
|
@@ -17535,6 +17830,14 @@ function numberValue(value, fallback) {
|
|
|
17535
17830
|
function names(value) {
|
|
17536
17831
|
return Array.isArray(value) ? value.filter((candidate) => typeof candidate === "string") : [];
|
|
17537
17832
|
}
|
|
17833
|
+
function lookupKxmResource(resources, requested, kind) {
|
|
17834
|
+
const records = [...resources].map((resource) => ({
|
|
17835
|
+
id: resource.id,
|
|
17836
|
+
aliases: names(resource.value.aliases),
|
|
17837
|
+
resource
|
|
17838
|
+
}));
|
|
17839
|
+
return lookupById(records, requested, kind)?.record.resource;
|
|
17840
|
+
}
|
|
17538
17841
|
function mapResource(resource, map, issues) {
|
|
17539
17842
|
const id = resource.id;
|
|
17540
17843
|
if (!id) return;
|
|
@@ -17725,14 +18028,31 @@ function transition(value) {
|
|
|
17725
18028
|
function validateWorkflow(workflow, agents, models, repositories, gates, issues) {
|
|
17726
18029
|
const file = workflow.logicalPath;
|
|
17727
18030
|
const coordinator = stringValue(workflow.value.coordinator) ?? "coordinator";
|
|
17728
|
-
if (!agents.
|
|
18031
|
+
if (!lookupKxmResource(agents.values(), coordinator, "agent")) issues.push(issue3("reference", "coordinator_unknown", file, `coordinator ${coordinator} does not exist`));
|
|
17729
18032
|
const steps = valuesOf(workflow.value, "steps").map((candidate) => objectValue(candidate)).filter((candidate) => Boolean(candidate));
|
|
17730
18033
|
const stepIndex = /* @__PURE__ */ new Map();
|
|
18034
|
+
const primarySteps = /* @__PURE__ */ new Set();
|
|
17731
18035
|
for (const [index, step] of steps.entries()) {
|
|
17732
18036
|
const id = stringValue(step.id);
|
|
17733
18037
|
if (!id) continue;
|
|
17734
18038
|
if (stepIndex.has(id)) issues.push(issue3("semantic", "step_id_duplicate", file, `step ${id} is duplicated`));
|
|
17735
|
-
else
|
|
18039
|
+
else {
|
|
18040
|
+
stepIndex.set(id, index);
|
|
18041
|
+
primarySteps.add(id);
|
|
18042
|
+
}
|
|
18043
|
+
}
|
|
18044
|
+
for (const [index, step] of steps.entries()) {
|
|
18045
|
+
const id = stringValue(step.id);
|
|
18046
|
+
if (!id) continue;
|
|
18047
|
+
const aliases = new Set(names(step.aliases));
|
|
18048
|
+
for (const [oldId, newId2] of STEP_RENAMES) {
|
|
18049
|
+
if (id === newId2) aliases.add(oldId);
|
|
18050
|
+
if (id === oldId) aliases.add(newId2);
|
|
18051
|
+
}
|
|
18052
|
+
for (const alias of aliases) {
|
|
18053
|
+
if (alias === id || primarySteps.has(alias) || stepIndex.has(alias)) continue;
|
|
18054
|
+
stepIndex.set(alias, index);
|
|
18055
|
+
}
|
|
17736
18056
|
}
|
|
17737
18057
|
for (const field of ["reproOracle", "planHash"]) {
|
|
17738
18058
|
const oracle = objectValue(workflow.value[field]);
|
|
@@ -17761,7 +18081,7 @@ function validateWorkflow(workflow, agents, models, repositories, gates, issues)
|
|
|
17761
18081
|
const kind = stringValue(step.kind);
|
|
17762
18082
|
validateToolPolicy(workflow, objectValue(step.tools), `${stepId}.tools`, issues);
|
|
17763
18083
|
const primaryAgentId = stringValue(step.agent);
|
|
17764
|
-
if ((kind === "agent" || kind === "moa") && primaryAgentId && !agents.
|
|
18084
|
+
if ((kind === "agent" || kind === "moa") && primaryAgentId && !lookupKxmResource(agents.values(), primaryAgentId, "agent")) {
|
|
17765
18085
|
issues.push(issue3("reference", "agent_unknown", file, `${stepId} references unknown agent ${primaryAgentId}`));
|
|
17766
18086
|
}
|
|
17767
18087
|
const gate = stringValue(step.gate);
|
|
@@ -17788,7 +18108,7 @@ function validateWorkflow(workflow, agents, models, repositories, gates, issues)
|
|
|
17788
18108
|
const declaredAgents = assignment ? names(assignment.allowedAgents) : [];
|
|
17789
18109
|
const allowedAgents = declaredAgents.length > 0 ? declaredAgents : primaryAgentId ? [primaryAgentId] : [];
|
|
17790
18110
|
for (const agentId of allowedAgents) {
|
|
17791
|
-
const agent = agents.
|
|
18111
|
+
const agent = lookupKxmResource(agents.values(), agentId, "agent");
|
|
17792
18112
|
if (!agent) issues.push(issue3("reference", "assignment_agent_unknown", file, `${stepId} allows unknown agent ${agentId}`));
|
|
17793
18113
|
else {
|
|
17794
18114
|
validateAgentScope(agent, step, repositories, file, stepId, issues);
|
|
@@ -17819,8 +18139,8 @@ function validateWorkflow(workflow, agents, models, repositories, gates, issues)
|
|
|
17819
18139
|
const writable = Object.values(objectValue(step.repositories) ?? {}).filter((access) => access === "write").length;
|
|
17820
18140
|
if (maxWriteRepositories > writable) issues.push(issue3("semantic", "write_repository_bound_invalid", file, `${stepId} maxWriteRepositories exceeds writable repository scope`));
|
|
17821
18141
|
}
|
|
17822
|
-
const
|
|
17823
|
-
const minimumPassed =
|
|
18142
|
+
const join24 = objectValue(step.join);
|
|
18143
|
+
const minimumPassed = join24 && typeof join24.minimumPassed === "number" ? join24.minimumPassed : void 0;
|
|
17824
18144
|
if (minimumPassed !== void 0 && minimumPassed > maximum) issues.push(issue3("semantic", "join_impossible", file, `${stepId} minimumPassed exceeds assignment maximum`));
|
|
17825
18145
|
const distinctBy = names(assignment?.distinctBy);
|
|
17826
18146
|
if (distinctBy.length > 0) {
|
|
@@ -17845,7 +18165,7 @@ function validateWorkflow(workflow, agents, models, repositories, gates, issues)
|
|
|
17845
18165
|
const producerMinimum = numberValue(policy.minimumProducers, 1);
|
|
17846
18166
|
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`));
|
|
17847
18167
|
for (const agentId of eligible) {
|
|
17848
|
-
if (!agents.
|
|
18168
|
+
if (!lookupKxmResource(agents.values(), agentId, "agent")) issues.push(issue3("reference", "producer_agent_unknown", file, `${stepId}.${String(key)} references unknown producer ${agentId}`));
|
|
17849
18169
|
if (allowedAgents.length > 0 && !allowedAgents.includes(agentId)) issues.push(issue3("semantic", "producer_agent_ineligible", file, `${stepId}.${String(key)} producer ${agentId} is outside allowedAgents`));
|
|
17850
18170
|
}
|
|
17851
18171
|
const degradation = objectValue(policy.degradation);
|
|
@@ -17866,6 +18186,10 @@ function validateWorkflow(workflow, agents, models, repositories, gates, issues)
|
|
|
17866
18186
|
if (parsed.terminalStatus === "completed") completedTerminalSources.add(index);
|
|
17867
18187
|
continue;
|
|
17868
18188
|
}
|
|
18189
|
+
if (parsed.target && parsed.target !== "$terminal" && !primarySteps.has(parsed.target) && stepIndex.has(parsed.target)) {
|
|
18190
|
+
const canonical = stringValue(steps[stepIndex.get(parsed.target) ?? -1]?.id);
|
|
18191
|
+
if (canonical) noteDeprecatedId("step", parsed.target, canonical);
|
|
18192
|
+
}
|
|
17869
18193
|
const targetIndex = parsed.target === void 0 ? void 0 : stepIndex.get(parsed.target);
|
|
17870
18194
|
if (targetIndex === void 0) {
|
|
17871
18195
|
issues.push(issue3("reference", "transition_target_unknown", file, `${stepId}.${outcome} references unknown target ${String(parsed.target)}`));
|
|
@@ -18020,12 +18344,12 @@ function validateBundle(project, repositories, agents, models, workflows, enviro
|
|
|
18020
18344
|
}
|
|
18021
18345
|
validateModelReferences(models, issues);
|
|
18022
18346
|
const defaultWorkflow = stringValue(project.value.defaultWorkflow) ?? "default";
|
|
18023
|
-
if (!workflows.
|
|
18347
|
+
if (!lookupKxmResource(workflows.values(), defaultWorkflow, "workflow")) issues.push(issue3("reference", "default_workflow_unknown", project.logicalPath, `default workflow ${defaultWorkflow} does not exist`));
|
|
18024
18348
|
for (const workflow of workflows.values()) validateWorkflow(workflow, agents, models, repositoryIds, gates, issues);
|
|
18025
|
-
if (projectRoot &&
|
|
18349
|
+
if (projectRoot && existsSync4(join3(projectRoot, ".kxm", "roster.yaml"))) {
|
|
18026
18350
|
issues.push(issue3("semantic", "retired_roster_file", ".kxm/roster.yaml", "create the role and model files and delete roster.yaml"));
|
|
18027
18351
|
}
|
|
18028
|
-
if (projectRoot &&
|
|
18352
|
+
if (projectRoot && existsSync4(join3(projectRoot, ".kxm", "roles"))) {
|
|
18029
18353
|
issues.push(...developerRolePolicyIssues(projectRoot));
|
|
18030
18354
|
}
|
|
18031
18355
|
if (projectRoot) {
|
|
@@ -18040,11 +18364,12 @@ function rosterModelIssues(projectRoot, roles) {
|
|
|
18040
18364
|
for (const entry of valuesOf(role.value, "roster")) {
|
|
18041
18365
|
const route = stringValue(objectValue(entry)?.route);
|
|
18042
18366
|
if (!route) continue;
|
|
18043
|
-
const
|
|
18044
|
-
const
|
|
18367
|
+
const resolved = findYamlBasename(join3(projectRoot, ".kxm", "models"), route, "route") ?? route;
|
|
18368
|
+
const logical = `.kxm/models/${resolved}.yaml`;
|
|
18369
|
+
const file = join3(projectRoot, ".kxm", "models", `${resolved}.yaml`);
|
|
18045
18370
|
let harness;
|
|
18046
18371
|
try {
|
|
18047
|
-
if (
|
|
18372
|
+
if (existsSync4(file)) harness = stringValue(parseRestrictedYaml2(readFileSync2(file, "utf8"), logical).harness);
|
|
18048
18373
|
} catch {
|
|
18049
18374
|
harness = void 0;
|
|
18050
18375
|
}
|
|
@@ -18070,7 +18395,7 @@ function originHashIssues(projectRoot, models) {
|
|
|
18070
18395
|
}
|
|
18071
18396
|
let actual;
|
|
18072
18397
|
try {
|
|
18073
|
-
actual = createHash3("sha256").update(
|
|
18398
|
+
actual = createHash3("sha256").update(readFileSync2(file, "utf8"), "utf8").digest("hex");
|
|
18074
18399
|
} catch (error) {
|
|
18075
18400
|
const code = error.code;
|
|
18076
18401
|
if (code === "ENOENT" || code === "EACCES" || code === "EPERM" || code === "EISDIR") {
|
|
@@ -18086,7 +18411,7 @@ function originHashIssues(projectRoot, models) {
|
|
|
18086
18411
|
return issues;
|
|
18087
18412
|
}
|
|
18088
18413
|
function developerCeilings() {
|
|
18089
|
-
const script =
|
|
18414
|
+
const script = join3(findKxmRepoRoot(import.meta.url), "scripts", "harness-run.mjs");
|
|
18090
18415
|
let loaded;
|
|
18091
18416
|
try {
|
|
18092
18417
|
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}))`], {
|
|
@@ -18113,20 +18438,21 @@ function developerCeilings() {
|
|
|
18113
18438
|
}
|
|
18114
18439
|
function developerRolePolicyIssues(projectRoot) {
|
|
18115
18440
|
if (resolve(projectRoot) !== findKxmRepoRoot(import.meta.url)) return [];
|
|
18116
|
-
const
|
|
18117
|
-
const
|
|
18441
|
+
const lintIssues = lintWorkforce(projectRoot).errors.map((entry) => issue3("semantic", entry.code, entry.file, entry.message));
|
|
18442
|
+
const rolesDir = join3(projectRoot, ".kxm", "roles");
|
|
18443
|
+
const modelsDir = join3(projectRoot, ".kxm", "models");
|
|
18118
18444
|
const roles = {};
|
|
18119
18445
|
const models = {};
|
|
18120
18446
|
const evidence = {};
|
|
18121
18447
|
const parseIssues = [];
|
|
18122
18448
|
const readMap = (dir, into, skipInventory) => {
|
|
18123
|
-
if (!
|
|
18124
|
-
for (const name of
|
|
18449
|
+
if (!existsSync4(dir)) return;
|
|
18450
|
+
for (const name of readdirSync2(dir)) {
|
|
18125
18451
|
if (!name.endsWith(".yaml")) continue;
|
|
18126
18452
|
const id = basename(name, ".yaml");
|
|
18127
18453
|
if (skipInventory && id === "inventory") continue;
|
|
18128
18454
|
try {
|
|
18129
|
-
const value = parseRestrictedYaml2(
|
|
18455
|
+
const value = parseRestrictedYaml2(readFileSync2(join3(dir, name), "utf8"), `${dir}/${name}`);
|
|
18130
18456
|
into[id] = value;
|
|
18131
18457
|
} catch (error) {
|
|
18132
18458
|
if (error instanceof KxmConfigError) {
|
|
@@ -18143,12 +18469,12 @@ function developerRolePolicyIssues(projectRoot) {
|
|
|
18143
18469
|
const origin = objectValue(model.origin);
|
|
18144
18470
|
const source = origin ? stringValue(origin.source) : void 0;
|
|
18145
18471
|
if (!source || evidence[source] !== void 0) continue;
|
|
18146
|
-
const evidenceFile = isAbsolute(source) ? source :
|
|
18147
|
-
if (
|
|
18472
|
+
const evidenceFile = isAbsolute(source) ? source : join3(projectRoot, source);
|
|
18473
|
+
if (existsSync4(evidenceFile)) evidence[source] = readFileSync2(evidenceFile, "utf8");
|
|
18148
18474
|
}
|
|
18149
18475
|
const ceilings = developerCeilings();
|
|
18150
18476
|
if (!ceilings.ok) {
|
|
18151
|
-
return [...parseIssues, issue3("semantic", "developer_ceilings_unavailable", ".kxm/models", `developer ceilings could not be loaded: ${ceilings.detail}`)];
|
|
18477
|
+
return [...parseIssues, ...lintIssues, issue3("semantic", "developer_ceilings_unavailable", ".kxm/models", `developer ceilings could not be loaded: ${ceilings.detail}`)];
|
|
18152
18478
|
}
|
|
18153
18479
|
const result = validatePolicyDraft({ models, roles, evidence }, {
|
|
18154
18480
|
ceilings: ceilings.ROUTES,
|
|
@@ -18157,8 +18483,8 @@ function developerRolePolicyIssues(projectRoot) {
|
|
|
18157
18483
|
piNativeVendorProviders: ceilings.PI_NATIVE_VENDOR_PROVIDERS,
|
|
18158
18484
|
vendorAliases: { "x-ai": "xai", moonshotai: "moonshot", "google-ai": "google", qwen: "alibaba" }
|
|
18159
18485
|
});
|
|
18160
|
-
if (result.ok) return parseIssues;
|
|
18161
|
-
return [...parseIssues, ...result.issues.map((entry) => issue3(entry.phase, entry.code, entry.file, entry.message))];
|
|
18486
|
+
if (result.ok) return [...parseIssues, ...lintIssues];
|
|
18487
|
+
return [...parseIssues, ...lintIssues, ...result.issues.map((entry) => issue3(entry.phase, entry.code, entry.file, entry.message))];
|
|
18162
18488
|
}
|
|
18163
18489
|
function kxmCanonicalJson(value) {
|
|
18164
18490
|
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
@@ -18183,7 +18509,7 @@ function gitEnvironment() {
|
|
|
18183
18509
|
}
|
|
18184
18510
|
function discoverGitRoot(start = process.cwd()) {
|
|
18185
18511
|
let current = resolve(start);
|
|
18186
|
-
if (
|
|
18512
|
+
if (existsSync4(current) && !lstatSync(current).isDirectory()) current = dirname2(current);
|
|
18187
18513
|
const result = spawnSync2("git", ["-C", current, "rev-parse", "--show-toplevel"], {
|
|
18188
18514
|
encoding: "utf8",
|
|
18189
18515
|
env: gitEnvironment(),
|
|
@@ -18192,7 +18518,7 @@ function discoverGitRoot(start = process.cwd()) {
|
|
|
18192
18518
|
});
|
|
18193
18519
|
if (result.status !== 0 || result.error || !result.stdout.trim()) return void 0;
|
|
18194
18520
|
const root = resolve(result.stdout.trim());
|
|
18195
|
-
return
|
|
18521
|
+
return existsSync4(root) && lstatSync(root).isDirectory() ? root : void 0;
|
|
18196
18522
|
}
|
|
18197
18523
|
function assertNoRegisteredGates(options) {
|
|
18198
18524
|
if ("registeredGates" in options) {
|
|
@@ -18217,7 +18543,7 @@ function loadProjectBundle(projectRoot, options, candidate) {
|
|
|
18217
18543
|
)));
|
|
18218
18544
|
}
|
|
18219
18545
|
const registry = new KxmSchemaRegistry(options.schemasDir);
|
|
18220
|
-
const project = readResource(registry, root,
|
|
18546
|
+
const project = readResource(registry, root, join3(root, ".kxm", "project.yaml"), ".kxm/project.yaml", "project");
|
|
18221
18547
|
const earlyIssues = [];
|
|
18222
18548
|
validatePortablePaths(project, earlyIssues);
|
|
18223
18549
|
const templateProvenance = readTemplateProvenance(registry, root);
|
|
@@ -18236,30 +18562,30 @@ function loadProjectBundle(projectRoot, options, candidate) {
|
|
|
18236
18562
|
}
|
|
18237
18563
|
}
|
|
18238
18564
|
if (earlyIssues.length > 0) throw new KxmConfigError(earlyIssues);
|
|
18239
|
-
const agents = listNamedResources(registry, root,
|
|
18565
|
+
const agents = listNamedResources(registry, root, join3(root, ".kxm", "agents"), ".kxm/agents", "agent");
|
|
18240
18566
|
const modelIssues = [];
|
|
18241
|
-
const models = listNamedResources(registry, root,
|
|
18242
|
-
const roles = listNamedResources(registry, root,
|
|
18567
|
+
const models = listNamedResources(registry, root, join3(root, ".kxm", "models"), ".kxm/models", "model", void 0, modelIssues);
|
|
18568
|
+
const roles = listNamedResources(registry, root, join3(root, ".kxm", "roles"), ".kxm/roles", "role", writerRole === void 0 ? void 0 : "writer");
|
|
18243
18569
|
if (writerRole !== void 0) {
|
|
18244
18570
|
const logicalPath = ".kxm/roles/writer.yaml";
|
|
18245
|
-
roles.set("writer", { kind: "role", id: "writer", file:
|
|
18571
|
+
roles.set("writer", { kind: "role", id: "writer", file: join3(root, logicalPath), logicalPath, value: resourceValue(registry, writerRole, logicalPath, "role") });
|
|
18246
18572
|
}
|
|
18247
|
-
const workflows = listNamedResources(registry, root,
|
|
18573
|
+
const workflows = listNamedResources(registry, root, join3(root, ".kxm", "workflows"), ".kxm/workflows", "workflow", workflowCandidate?.id);
|
|
18248
18574
|
if (workflowCandidate) {
|
|
18249
18575
|
const { id, document } = workflowCandidate;
|
|
18250
18576
|
const logicalPath = `.kxm/workflows/${id}.yaml`;
|
|
18251
18577
|
if (!resourceIdentifier(id)) fail2("path", "resource_id_invalid", logicalPath, `filename-derived identity ${id} is invalid or platform-reserved`);
|
|
18252
18578
|
const collision = [...workflows.keys()].find((candidate2) => candidate2.toLocaleLowerCase("en-US") === id.toLocaleLowerCase("en-US"));
|
|
18253
18579
|
if (collision) fail2("path", "resource_id_collision", logicalPath, `${id} case-folds to existing ${collision}`);
|
|
18254
|
-
workflows.set(id, { kind: "workflow", id, file:
|
|
18580
|
+
workflows.set(id, { kind: "workflow", id, file: join3(root, logicalPath), logicalPath, value: resourceValue(registry, document, logicalPath, "workflow") });
|
|
18255
18581
|
}
|
|
18256
|
-
const gatePath =
|
|
18257
|
-
const gateRegistry =
|
|
18582
|
+
const gatePath = join3(root, ".kxm", "gates.yaml");
|
|
18583
|
+
const gateRegistry = existsSync4(gatePath) ? readResource(registry, root, gatePath, ".kxm/gates.yaml", "gate-registry") : void 0;
|
|
18258
18584
|
const environments = [];
|
|
18259
18585
|
const repositories = /* @__PURE__ */ new Map();
|
|
18260
18586
|
const loadIssues = [];
|
|
18261
|
-
const projectEnvironment =
|
|
18262
|
-
if (
|
|
18587
|
+
const projectEnvironment = join3(root, ".kxm", "project", "env.yaml");
|
|
18588
|
+
if (existsSync4(projectEnvironment)) {
|
|
18263
18589
|
try {
|
|
18264
18590
|
environments.push(readResource(registry, root, projectEnvironment, ".kxm/project/env.yaml", "environment"));
|
|
18265
18591
|
} catch (error) {
|
|
@@ -18301,7 +18627,7 @@ function loadProjectBundle(projectRoot, options, candidate) {
|
|
|
18301
18627
|
if (required2) loadIssues.push(issue3("discovery", "repository_binding_missing", ".kxm/project.yaml", `required repository ${repositoryId} has no portable pathHint or host-local binding`));
|
|
18302
18628
|
continue;
|
|
18303
18629
|
}
|
|
18304
|
-
if (!
|
|
18630
|
+
if (!existsSync4(binding)) {
|
|
18305
18631
|
if (required2 || hasLocalBinding) loadIssues.push(issue3("discovery", "repository_binding_unavailable", ".kxm/project.yaml", `${hasLocalBinding ? "explicit" : "required"} repository binding ${repositoryId} is unavailable`));
|
|
18306
18632
|
continue;
|
|
18307
18633
|
}
|
|
@@ -18333,9 +18659,9 @@ function loadProjectBundle(projectRoot, options, candidate) {
|
|
|
18333
18659
|
continue;
|
|
18334
18660
|
}
|
|
18335
18661
|
seenBindings.set(foldedBinding, repositoryId);
|
|
18336
|
-
const repositoryFile =
|
|
18662
|
+
const repositoryFile = join3(binding, ".kxm", "repo", "repo.yaml");
|
|
18337
18663
|
let definitionValidated = false;
|
|
18338
|
-
if (
|
|
18664
|
+
if (existsSync4(repositoryFile)) {
|
|
18339
18665
|
try {
|
|
18340
18666
|
const resource = readResource(registry, root, repositoryFile, `.kxm/repositories/${repositoryId}/repo.yaml`, "repository", repositoryId, binding);
|
|
18341
18667
|
if (resource.value.repositoryId !== repositoryId) {
|
|
@@ -18351,8 +18677,8 @@ function loadProjectBundle(projectRoot, options, candidate) {
|
|
|
18351
18677
|
} else if (required2 || hasLocalBinding) {
|
|
18352
18678
|
loadIssues.push(issue3("discovery", "repository_definition_missing", `.kxm/repositories/${repositoryId}/repo.yaml`, `${hasLocalBinding ? "explicit" : "required"} repository ${repositoryId} has no repo.yaml`));
|
|
18353
18679
|
}
|
|
18354
|
-
const environmentFile =
|
|
18355
|
-
if (
|
|
18680
|
+
const environmentFile = join3(binding, ".kxm", "repo", "env.yaml");
|
|
18681
|
+
if (existsSync4(environmentFile)) {
|
|
18356
18682
|
if (!definitionValidated) {
|
|
18357
18683
|
loadIssues.push(issue3("reference", "repository_environment_without_definition", `.kxm/repositories/${repositoryId}/env.yaml`, `repository ${repositoryId} environment requires a validated matching repo.yaml`));
|
|
18358
18684
|
} else {
|
|
@@ -18388,21 +18714,21 @@ var LEGACY_CONFIG_FILES = [".kxm/config/agents.json", ".kxm/config/gates.json"];
|
|
|
18388
18714
|
function legacyConfigFilesAt(root) {
|
|
18389
18715
|
const files = [];
|
|
18390
18716
|
for (const component of [".kxm", ".kxm/config"]) {
|
|
18391
|
-
const absolute =
|
|
18392
|
-
if (
|
|
18717
|
+
const absolute = join3(root, ...component.split("/"));
|
|
18718
|
+
if (existsSync4(absolute) && lstatSync(absolute).isSymbolicLink()) return [component];
|
|
18393
18719
|
}
|
|
18394
18720
|
for (const candidate of LEGACY_CONFIG_FILES) {
|
|
18395
|
-
if (
|
|
18721
|
+
if (existsSync4(join3(root, ...candidate.split("/")))) files.push(candidate);
|
|
18396
18722
|
}
|
|
18397
|
-
const workflowsDir =
|
|
18398
|
-
if (
|
|
18723
|
+
const workflowsDir = join3(root, ".kxm", "config", "workflows");
|
|
18724
|
+
if (existsSync4(workflowsDir)) {
|
|
18399
18725
|
const stat = lstatSync(workflowsDir);
|
|
18400
18726
|
if (stat.isSymbolicLink()) {
|
|
18401
18727
|
files.push(".kxm/config/workflows");
|
|
18402
18728
|
return files;
|
|
18403
18729
|
}
|
|
18404
18730
|
if (stat.isDirectory()) {
|
|
18405
|
-
for (const entry of
|
|
18731
|
+
for (const entry of readdirSync2(workflowsDir, { withFileTypes: true }).filter((candidate) => (candidate.isFile() || candidate.isSymbolicLink()) && candidate.name.endsWith(".json")).sort((left, right) => compareCodeUnits4(left.name, right.name))) {
|
|
18406
18732
|
files.push(`.kxm/config/workflows/${entry.name}`);
|
|
18407
18733
|
}
|
|
18408
18734
|
}
|
|
@@ -18413,11 +18739,11 @@ function legacyConfigFilesAt(root) {
|
|
|
18413
18739
|
// plugins/kxm/src/runtime-store.ts
|
|
18414
18740
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
18415
18741
|
import { createHash as createHash7, randomUUID } from "node:crypto";
|
|
18416
|
-
import { existsSync as
|
|
18742
|
+
import { existsSync as existsSync7, lstatSync as lstatSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync4, realpathSync as realpathSync3, statSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
18417
18743
|
import { dirname as dirname5, isAbsolute as isAbsolute4, resolve as resolve6 } from "node:path";
|
|
18418
18744
|
|
|
18419
18745
|
// plugins/kxm/src/sqlite.ts
|
|
18420
|
-
import { existsSync as
|
|
18746
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
18421
18747
|
import { createRequire } from "node:module";
|
|
18422
18748
|
import { resolve as resolve2 } from "node:path";
|
|
18423
18749
|
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
@@ -18458,7 +18784,7 @@ var DatabaseSync = class {
|
|
|
18458
18784
|
}
|
|
18459
18785
|
};
|
|
18460
18786
|
function openReadOnlyDatabase(path) {
|
|
18461
|
-
if (
|
|
18787
|
+
if (existsSync5(`${path}-wal`)) return new DatabaseSync(path, { readOnly: true });
|
|
18462
18788
|
const uri = pathToFileURL2(resolve2(path));
|
|
18463
18789
|
uri.searchParams.set("immutable", "1");
|
|
18464
18790
|
return new DatabaseSync(uri.href, { readOnly: true });
|
|
@@ -18467,11 +18793,11 @@ function openReadOnlyDatabase(path) {
|
|
|
18467
18793
|
// plugins/kxm/src/runtime-paths.ts
|
|
18468
18794
|
import { createHash as createHash4 } from "node:crypto";
|
|
18469
18795
|
import { realpathSync as realpathSync2 } from "node:fs";
|
|
18470
|
-
import { join as
|
|
18796
|
+
import { join as join5, resolve as resolve4 } from "node:path";
|
|
18471
18797
|
|
|
18472
18798
|
// plugins/kxm/src/bindings.ts
|
|
18473
18799
|
import { homedir } from "node:os";
|
|
18474
|
-
import { dirname as dirname3, isAbsolute as isAbsolute2, join as
|
|
18800
|
+
import { dirname as dirname3, isAbsolute as isAbsolute2, join as join4, parse, relative as relative2, resolve as resolve3, sep as sep2 } from "node:path";
|
|
18475
18801
|
var MAX_BINDING_RECORD_BYTES = 256 * 1024;
|
|
18476
18802
|
var BINDING_LABEL = "Runtime-local repository bindings";
|
|
18477
18803
|
function bindingIssue(phase, code, message) {
|
|
@@ -18491,24 +18817,24 @@ function kxmUserStateRoot(options = {}) {
|
|
|
18491
18817
|
}
|
|
18492
18818
|
if (platform === "win32") {
|
|
18493
18819
|
const localAppData = env.LOCALAPPDATA?.trim();
|
|
18494
|
-
const base2 = localAppData && isAbsolute2(localAppData) ? localAppData :
|
|
18820
|
+
const base2 = localAppData && isAbsolute2(localAppData) ? localAppData : join4(home, "AppData", "Local");
|
|
18495
18821
|
return resolve3(base2, "KXM");
|
|
18496
18822
|
}
|
|
18497
18823
|
if (platform === "darwin") return resolve3(home, "Library", "Application Support", "KXM");
|
|
18498
18824
|
const xdgState = env.XDG_STATE_HOME?.trim();
|
|
18499
|
-
const base = xdgState && isAbsolute2(xdgState) ? xdgState :
|
|
18825
|
+
const base = xdgState && isAbsolute2(xdgState) ? xdgState : join4(home, ".local", "state");
|
|
18500
18826
|
return resolve3(base, "kxm");
|
|
18501
18827
|
}
|
|
18502
18828
|
|
|
18503
18829
|
// plugins/kxm/src/runtime-paths.ts
|
|
18504
18830
|
function kxmRuntimePaths(options = {}) {
|
|
18505
18831
|
const stateRoot = options.stateRoot ? resolve4(options.stateRoot) : kxmUserStateRoot({ ...options.env ? { env: options.env } : {}, ...options.homeDir ? { homeDir: options.homeDir } : {} });
|
|
18506
|
-
const runtimeDir =
|
|
18832
|
+
const runtimeDir = join5(stateRoot, "runtime");
|
|
18507
18833
|
return {
|
|
18508
18834
|
stateRoot,
|
|
18509
18835
|
runtimeDir,
|
|
18510
|
-
registryDb:
|
|
18511
|
-
projectsDir:
|
|
18836
|
+
registryDb: join5(runtimeDir, "registry.db"),
|
|
18837
|
+
projectsDir: join5(runtimeDir, "projects")
|
|
18512
18838
|
};
|
|
18513
18839
|
}
|
|
18514
18840
|
function projectRuntimeKey(projectRoot) {
|
|
@@ -18522,7 +18848,7 @@ function projectRuntimeKey(projectRoot) {
|
|
|
18522
18848
|
return createHash4("sha256").update(folded, "utf8").digest("hex").slice(0, 24);
|
|
18523
18849
|
}
|
|
18524
18850
|
function kxmProjectRunEventsPath(projectRoot, env) {
|
|
18525
|
-
return
|
|
18851
|
+
return join5(kxmRuntimePaths({ env }).projectsDir, projectRuntimeKey(projectRoot), "run-events.db");
|
|
18526
18852
|
}
|
|
18527
18853
|
|
|
18528
18854
|
// plugins/kxm/src/sync-transform.ts
|
|
@@ -18861,22 +19187,22 @@ import { createHash as createHash6, randomBytes } from "node:crypto";
|
|
|
18861
19187
|
import {
|
|
18862
19188
|
chmodSync,
|
|
18863
19189
|
copyFileSync,
|
|
18864
|
-
existsSync as
|
|
19190
|
+
existsSync as existsSync6,
|
|
18865
19191
|
lstatSync as lstatSync2,
|
|
18866
19192
|
mkdirSync,
|
|
18867
|
-
readdirSync as
|
|
18868
|
-
readFileSync as
|
|
19193
|
+
readdirSync as readdirSync3,
|
|
19194
|
+
readFileSync as readFileSync3,
|
|
18869
19195
|
unlinkSync,
|
|
18870
19196
|
writeFileSync
|
|
18871
19197
|
} from "node:fs";
|
|
18872
|
-
import { basename as basename2, dirname as dirname4, isAbsolute as isAbsolute3, join as
|
|
19198
|
+
import { basename as basename2, dirname as dirname4, isAbsolute as isAbsolute3, join as join6, relative as relative3, resolve as resolve5, sep as sep3 } from "node:path";
|
|
18873
19199
|
function databaseError(code, file, message) {
|
|
18874
19200
|
const issue4 = { phase: "semantic", code, file, message };
|
|
18875
19201
|
return new KxmConfigError([issue4]);
|
|
18876
19202
|
}
|
|
18877
19203
|
function checkedParent(path, description) {
|
|
18878
19204
|
const parent = dirname4(path);
|
|
18879
|
-
if (!
|
|
19205
|
+
if (!existsSync6(parent)) mkdirSync(parent, { recursive: true, mode: 448 });
|
|
18880
19206
|
const stat = lstatSync2(parent, { throwIfNoEntry: false });
|
|
18881
19207
|
if (!stat || stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
18882
19208
|
throw databaseError("runtime_path_invalid", description, `${description} parent must be a regular directory, not a link`);
|
|
@@ -19097,7 +19423,7 @@ function checkIntegrity(database) {
|
|
|
19097
19423
|
return rows.length === 1 && rows[0]?.integrity_check === "ok";
|
|
19098
19424
|
}
|
|
19099
19425
|
function fileSha256(filePath) {
|
|
19100
|
-
const bytes =
|
|
19426
|
+
const bytes = readFileSync3(filePath);
|
|
19101
19427
|
return `sha256:${createHash6("sha256").update(bytes).digest("hex")}`;
|
|
19102
19428
|
}
|
|
19103
19429
|
function backupDatabaseFile(sourcePath2, targetPath, storeId) {
|
|
@@ -19108,7 +19434,7 @@ function backupDatabaseFile(sourcePath2, targetPath, storeId) {
|
|
|
19108
19434
|
throw databaseError("runtime_path_invalid", resolvedSource, `source database ${resolvedSource} must be a regular file, not a link or directory`);
|
|
19109
19435
|
}
|
|
19110
19436
|
checkedParent(resolvedTarget, "backup target");
|
|
19111
|
-
if (
|
|
19437
|
+
if (existsSync6(resolvedTarget)) {
|
|
19112
19438
|
unlinkSync(resolvedTarget);
|
|
19113
19439
|
}
|
|
19114
19440
|
const sourceDb = new DatabaseSync(resolvedSource);
|
|
@@ -19186,7 +19512,7 @@ function restoreDatabaseFile(backupPath, targetPath, storeId, expectedSchemaVers
|
|
|
19186
19512
|
}
|
|
19187
19513
|
checkedParent(resolvedTarget, "restore target");
|
|
19188
19514
|
for (const file of [resolvedTarget, `${resolvedTarget}-wal`, `${resolvedTarget}-shm`]) {
|
|
19189
|
-
if (
|
|
19515
|
+
if (existsSync6(file)) {
|
|
19190
19516
|
try {
|
|
19191
19517
|
unlinkSync(file);
|
|
19192
19518
|
} catch {
|
|
@@ -19231,14 +19557,14 @@ function pushStore(stores, storeId, sourcePath2) {
|
|
|
19231
19557
|
stores.push({ storeId, sourcePath: sourcePath2, maxSupportedVersion: kxmBackupCeiling(storeId) });
|
|
19232
19558
|
}
|
|
19233
19559
|
function pushEventStore(stores, files, paths, key) {
|
|
19234
|
-
const dbPath =
|
|
19235
|
-
if (!
|
|
19560
|
+
const dbPath = join6(paths.projectsDir, key, "run-events.db");
|
|
19561
|
+
if (!existsSync6(dbPath)) return;
|
|
19236
19562
|
const safe = key.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
19237
19563
|
let storeId = `events:${safe}`;
|
|
19238
19564
|
if (stores.some((store) => store.storeId === storeId)) storeId = `events:runtime:${safe}`;
|
|
19239
19565
|
pushStore(stores, storeId, dbPath);
|
|
19240
19566
|
const sidecar = `${dbPath}.run-prompts.json`;
|
|
19241
|
-
if (
|
|
19567
|
+
if (existsSync6(sidecar)) {
|
|
19242
19568
|
const id = `${storeId}:run-prompts`;
|
|
19243
19569
|
if (!files.some((file) => file.id === id || file.sourcePath === sidecar)) {
|
|
19244
19570
|
files.push({ id, sourcePath: sidecar });
|
|
@@ -19256,14 +19582,14 @@ function discoverUserRuntime(runtimeProjectKey, options, stores, files) {
|
|
|
19256
19582
|
pushEventStore(stores, files, paths, runtimeProjectKey);
|
|
19257
19583
|
return paths.stateRoot;
|
|
19258
19584
|
}
|
|
19259
|
-
if (
|
|
19585
|
+
if (existsSync6(paths.registryDb)) {
|
|
19260
19586
|
const storeId = stores.some((store) => store.storeId === "registry") ? "runtime-registry" : "registry";
|
|
19261
19587
|
pushStore(stores, storeId, paths.registryDb);
|
|
19262
19588
|
}
|
|
19263
|
-
if (!
|
|
19589
|
+
if (!existsSync6(paths.projectsDir)) return paths.stateRoot;
|
|
19264
19590
|
let entries;
|
|
19265
19591
|
try {
|
|
19266
|
-
entries =
|
|
19592
|
+
entries = readdirSync3(paths.projectsDir, { withFileTypes: true });
|
|
19267
19593
|
} catch {
|
|
19268
19594
|
return paths.stateRoot;
|
|
19269
19595
|
}
|
|
@@ -19276,19 +19602,19 @@ function discoverBackupSources(projectRoot, options = {}) {
|
|
|
19276
19602
|
const root = resolve5(projectRoot);
|
|
19277
19603
|
const stores = [];
|
|
19278
19604
|
const files = [];
|
|
19279
|
-
const hubPath = options.hubDataPath ? resolve5(options.hubDataPath) :
|
|
19280
|
-
if (
|
|
19281
|
-
const registryPath =
|
|
19282
|
-
if (
|
|
19283
|
-
const bindingsPath =
|
|
19284
|
-
if (
|
|
19285
|
-
const eventsDir =
|
|
19286
|
-
if (
|
|
19287
|
-
const entries =
|
|
19605
|
+
const hubPath = options.hubDataPath ? resolve5(options.hubDataPath) : join6(root, ".kxm", "state", "kxm.db");
|
|
19606
|
+
if (existsSync6(hubPath)) pushStore(stores, "hub-store", hubPath);
|
|
19607
|
+
const registryPath = join6(root, ".kxm", "runtime", "registry.db");
|
|
19608
|
+
if (existsSync6(registryPath)) pushStore(stores, "registry", registryPath);
|
|
19609
|
+
const bindingsPath = join6(root, ".kxm", "runtime", "bindings.db");
|
|
19610
|
+
if (existsSync6(bindingsPath)) pushStore(stores, "binding-store", bindingsPath);
|
|
19611
|
+
const eventsDir = join6(root, ".kxm", "runtime", "events");
|
|
19612
|
+
if (existsSync6(eventsDir)) {
|
|
19613
|
+
const entries = readdirSync3(eventsDir, { withFileTypes: true });
|
|
19288
19614
|
for (const entry of entries) {
|
|
19289
19615
|
if (entry.isFile() && entry.name.endsWith(".db")) {
|
|
19290
19616
|
const key = entry.name.replace(/\.db$/, "").replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
19291
|
-
pushStore(stores, `events:${key}`,
|
|
19617
|
+
pushStore(stores, `events:${key}`, join6(eventsDir, entry.name));
|
|
19292
19618
|
}
|
|
19293
19619
|
}
|
|
19294
19620
|
}
|
|
@@ -19303,7 +19629,7 @@ function backupPlainFile(sourcePath2, targetPath, id) {
|
|
|
19303
19629
|
throw databaseError("runtime_path_invalid", resolvedSource, `backup file ${resolvedSource} must be a regular file, not a link or directory`);
|
|
19304
19630
|
}
|
|
19305
19631
|
checkedParent(targetPath, "backup target");
|
|
19306
|
-
if (
|
|
19632
|
+
if (existsSync6(targetPath)) unlinkSync(targetPath);
|
|
19307
19633
|
copyFileSync(resolvedSource, targetPath);
|
|
19308
19634
|
try {
|
|
19309
19635
|
chmodSync(targetPath, 384);
|
|
@@ -19327,7 +19653,7 @@ function backupFilename(sourcePath2, id, used) {
|
|
|
19327
19653
|
}
|
|
19328
19654
|
function restorePlainFile(backupFilePath, targetPath) {
|
|
19329
19655
|
checkedParent(targetPath, "restore target");
|
|
19330
|
-
if (
|
|
19656
|
+
if (existsSync6(targetPath)) unlinkSync(targetPath);
|
|
19331
19657
|
copyFileSync(backupFilePath, targetPath);
|
|
19332
19658
|
try {
|
|
19333
19659
|
chmodSync(targetPath, 384);
|
|
@@ -19352,7 +19678,7 @@ function planBackup(options = {}) {
|
|
|
19352
19678
|
}
|
|
19353
19679
|
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
19354
19680
|
const timestamp = createdAt.replace(/[:.]/g, "-");
|
|
19355
|
-
const outDir = options.outDir ? resolve5(options.outDir) :
|
|
19681
|
+
const outDir = options.outDir ? resolve5(options.outDir) : join6(projectRoot, ".kxm", "backups", `backup-${timestamp}`);
|
|
19356
19682
|
const usedFilenames = /* @__PURE__ */ new Set();
|
|
19357
19683
|
const stores = discovered.stores.map((store) => ({
|
|
19358
19684
|
storeId: store.storeId,
|
|
@@ -19378,7 +19704,7 @@ function planBackup(options = {}) {
|
|
|
19378
19704
|
function createBackup(options = {}) {
|
|
19379
19705
|
const { projectRoot, outDir, createdAt, scope, stateRoot, runtimeProjectKey, stores, files } = planBackup(options);
|
|
19380
19706
|
const backupId = `bk_${randomBytes(8).toString("hex")}`;
|
|
19381
|
-
if (!
|
|
19707
|
+
if (!existsSync6(outDir)) {
|
|
19382
19708
|
mkdirSync(outDir, { recursive: true, mode: 448 });
|
|
19383
19709
|
}
|
|
19384
19710
|
const backedUpStores = [];
|
|
@@ -19386,14 +19712,14 @@ function createBackup(options = {}) {
|
|
|
19386
19712
|
const omitted = [];
|
|
19387
19713
|
for (const store of stores) {
|
|
19388
19714
|
try {
|
|
19389
|
-
backedUpStores.push(backupDatabaseFile(store.sourcePath,
|
|
19715
|
+
backedUpStores.push(backupDatabaseFile(store.sourcePath, join6(outDir, store.backupFile), store.storeId));
|
|
19390
19716
|
} catch {
|
|
19391
19717
|
omitted.push(store.storeId);
|
|
19392
19718
|
}
|
|
19393
19719
|
}
|
|
19394
19720
|
for (const file of files) {
|
|
19395
19721
|
try {
|
|
19396
|
-
backedUpFiles.push(backupPlainFile(file.sourcePath,
|
|
19722
|
+
backedUpFiles.push(backupPlainFile(file.sourcePath, join6(outDir, file.backupFile), file.id));
|
|
19397
19723
|
} catch {
|
|
19398
19724
|
omitted.push(file.id);
|
|
19399
19725
|
}
|
|
@@ -19429,7 +19755,7 @@ function createBackup(options = {}) {
|
|
|
19429
19755
|
const manifestSha256 = `sha256:${createHash6("sha256").update(manifestJson, "utf8").digest("hex")}`;
|
|
19430
19756
|
manifest.manifestSha256 = manifestSha256;
|
|
19431
19757
|
const finalJson = JSON.stringify(manifest, null, 2) + "\n";
|
|
19432
|
-
const manifestPath =
|
|
19758
|
+
const manifestPath = join6(outDir, "manifest.json");
|
|
19433
19759
|
writeFileSync(manifestPath, finalJson, "utf8");
|
|
19434
19760
|
return { manifest, outDir };
|
|
19435
19761
|
}
|
|
@@ -19443,16 +19769,16 @@ function restoreTarget(manifest, sourcePath2, id, options, currentStateRoot) {
|
|
|
19443
19769
|
const stateRel = pathUnder(manifest.stateRoot, sourcePath2);
|
|
19444
19770
|
const projectRel = pathUnder(manifest.projectRoot, sourcePath2);
|
|
19445
19771
|
if (stateRel !== void 0 && (projectRel === void 0 || stateRel.length <= projectRel.length)) {
|
|
19446
|
-
const ownEvents = manifest.runtimeProjectKey !== void 0 ?
|
|
19772
|
+
const ownEvents = manifest.runtimeProjectKey !== void 0 ? join6("runtime", "projects", manifest.runtimeProjectKey, "run-events.db") : void 0;
|
|
19447
19773
|
const own2 = ownEvents !== void 0 && (stateRel === ownEvents || stateRel === `${ownEvents}.run-prompts.json`);
|
|
19448
19774
|
if (own2 && !options.allProjects && options.projectRoot !== void 0) {
|
|
19449
19775
|
const eventsPath = kxmProjectRunEventsPath(resolve5(options.projectRoot), options.env ?? process.env);
|
|
19450
19776
|
return { targetPath: stateRel === ownEvents ? eventsPath : `${eventsPath}.run-prompts.json`, machineWide: false };
|
|
19451
19777
|
}
|
|
19452
|
-
return { targetPath:
|
|
19778
|
+
return { targetPath: join6(currentStateRoot(), stateRel), machineWide: !own2 };
|
|
19453
19779
|
}
|
|
19454
19780
|
if (projectRel !== void 0) {
|
|
19455
|
-
return { targetPath: options.projectRoot ?
|
|
19781
|
+
return { targetPath: options.projectRoot ? join6(resolve5(options.projectRoot), projectRel) : sourcePath2, machineWide: false };
|
|
19456
19782
|
}
|
|
19457
19783
|
return { targetPath: sourcePath2, machineWide: id !== "hub-store" };
|
|
19458
19784
|
}
|
|
@@ -19463,13 +19789,13 @@ function planRestore(manifestPathOrDir, options = {}) {
|
|
|
19463
19789
|
throw databaseError("runtime_path_invalid", manifestPath, `manifest path ${manifestPath} does not exist`);
|
|
19464
19790
|
}
|
|
19465
19791
|
if (stat.isDirectory()) {
|
|
19466
|
-
manifestPath =
|
|
19792
|
+
manifestPath = join6(manifestPath, "manifest.json");
|
|
19467
19793
|
}
|
|
19468
|
-
if (!
|
|
19794
|
+
if (!existsSync6(manifestPath)) {
|
|
19469
19795
|
throw databaseError("runtime_path_invalid", manifestPath, `backup manifest ${manifestPath} not found`);
|
|
19470
19796
|
}
|
|
19471
19797
|
const manifestDir = dirname4(manifestPath);
|
|
19472
|
-
const rawText =
|
|
19798
|
+
const rawText = readFileSync3(manifestPath, "utf8");
|
|
19473
19799
|
let manifest;
|
|
19474
19800
|
try {
|
|
19475
19801
|
manifest = JSON.parse(rawText);
|
|
@@ -19487,8 +19813,8 @@ function planRestore(manifestPathOrDir, options = {}) {
|
|
|
19487
19813
|
const machineWide = [];
|
|
19488
19814
|
const stores = [];
|
|
19489
19815
|
for (const store of manifest.stores) {
|
|
19490
|
-
const backupFilePath =
|
|
19491
|
-
if (!
|
|
19816
|
+
const backupFilePath = join6(manifestDir, store.backupFile);
|
|
19817
|
+
if (!existsSync6(backupFilePath)) {
|
|
19492
19818
|
throw databaseError("restore_file_missing", backupFilePath, `backup file ${store.backupFile} missing from ${manifestDir}`);
|
|
19493
19819
|
}
|
|
19494
19820
|
const actualSha256 = fileSha256(backupFilePath);
|
|
@@ -19513,8 +19839,8 @@ function planRestore(manifestPathOrDir, options = {}) {
|
|
|
19513
19839
|
}
|
|
19514
19840
|
const files = [];
|
|
19515
19841
|
for (const file of manifest.files ?? []) {
|
|
19516
|
-
const backupFilePath =
|
|
19517
|
-
if (!
|
|
19842
|
+
const backupFilePath = join6(manifestDir, file.backupFile);
|
|
19843
|
+
if (!existsSync6(backupFilePath)) {
|
|
19518
19844
|
throw databaseError("restore_file_missing", backupFilePath, `backup file ${file.backupFile} missing from ${manifestDir}`);
|
|
19519
19845
|
}
|
|
19520
19846
|
const actualSha256 = fileSha256(backupFilePath);
|
|
@@ -19574,7 +19900,7 @@ function runtimeError(code, file, message) {
|
|
|
19574
19900
|
}
|
|
19575
19901
|
function projectRuntimeOwnsRun(projectRoot, runId, env) {
|
|
19576
19902
|
const path = kxmProjectRunEventsPath(projectRoot, env);
|
|
19577
|
-
if (!
|
|
19903
|
+
if (!existsSync7(path)) return false;
|
|
19578
19904
|
const database = openReadOnlyDatabase(path);
|
|
19579
19905
|
try {
|
|
19580
19906
|
database.exec("PRAGMA busy_timeout = 5000");
|
|
@@ -20438,9 +20764,9 @@ var KxmRunEventStore = class {
|
|
|
20438
20764
|
}
|
|
20439
20765
|
readRunPrompts() {
|
|
20440
20766
|
const sidecar = this.runPromptSidecarPath();
|
|
20441
|
-
if (!
|
|
20767
|
+
if (!existsSync7(sidecar)) return {};
|
|
20442
20768
|
try {
|
|
20443
|
-
const parsed = JSON.parse(
|
|
20769
|
+
const parsed = JSON.parse(readFileSync4(sidecar, "utf8"));
|
|
20444
20770
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
|
|
20445
20771
|
return Object.fromEntries(
|
|
20446
20772
|
Object.entries(parsed).filter((entry) => typeof entry[1] === "string")
|
|
@@ -22884,7 +23210,7 @@ function compileStep(step, index, stepIndex, requirePlanHash, sink) {
|
|
|
22884
23210
|
const maxAttempts = compileCountField(step.maxAttempts, 1, `${id}.maxAttempts`, id, sink);
|
|
22885
23211
|
const timeoutMs = compileOptionalDuration(step.timeoutMs, `${id}.timeoutMs`, id, sink);
|
|
22886
23212
|
const assignments = compileAssignments(step, id, agent, sink);
|
|
22887
|
-
const
|
|
23213
|
+
const join24 = compileJoin(step, id, sink);
|
|
22888
23214
|
const requiredEvidence = compileEvidence(step, id, sink);
|
|
22889
23215
|
const transitions = compileTransitions(step, id, index, stepIndex, sink);
|
|
22890
23216
|
const outcomes = Object.keys(transitions).sort(compareCodeUnits5);
|
|
@@ -22912,7 +23238,7 @@ function compileStep(step, index, stepIndex, requirePlanHash, sink) {
|
|
|
22912
23238
|
transitions: orderedTransitions,
|
|
22913
23239
|
requiresPlanHash: requirePlanHash.includes(id),
|
|
22914
23240
|
assignments,
|
|
22915
|
-
join:
|
|
23241
|
+
join: join24
|
|
22916
23242
|
};
|
|
22917
23243
|
if (kind === "agent" || kind === "moa") {
|
|
22918
23244
|
if (!agent) return void 0;
|
|
@@ -22962,15 +23288,15 @@ function compileAssignments(step, stepId, primaryAgentId, sink) {
|
|
|
22962
23288
|
};
|
|
22963
23289
|
}
|
|
22964
23290
|
function compileJoin(step, stepId, sink) {
|
|
22965
|
-
const
|
|
22966
|
-
if (!
|
|
22967
|
-
const declared = stringValue2(
|
|
23291
|
+
const join24 = objectValue2(step.join);
|
|
23292
|
+
if (!join24) return { strategy: "all" };
|
|
23293
|
+
const declared = stringValue2(join24.strategy);
|
|
22968
23294
|
const strategy = declared && JOIN_STRATEGIES.has(declared) ? declared : "all";
|
|
22969
|
-
const minimumPassed = compileOptionalCount(
|
|
23295
|
+
const minimumPassed = compileOptionalCount(join24.minimumPassed, `${stepId}.join.minimumPassed`, stepId, sink);
|
|
22970
23296
|
const compiled = {
|
|
22971
23297
|
strategy,
|
|
22972
23298
|
...minimumPassed !== void 0 ? { minimumPassed } : {},
|
|
22973
|
-
...typeof
|
|
23299
|
+
...typeof join24.cancelRemaining === "boolean" ? { cancelRemaining: join24.cancelRemaining } : {}
|
|
22974
23300
|
};
|
|
22975
23301
|
return compiled;
|
|
22976
23302
|
}
|
|
@@ -24389,11 +24715,11 @@ function kxmToolPolicyRevision(bundle) {
|
|
|
24389
24715
|
return sha256Of(kxmCanonicalJson({ policies, gateRegistry: bundle.gateRegistry?.value ?? null }));
|
|
24390
24716
|
}
|
|
24391
24717
|
function collectMemoryFiles(dir, baseDir, ignoreSubdirs = /* @__PURE__ */ new Set()) {
|
|
24392
|
-
if (!
|
|
24393
|
-
const entries =
|
|
24718
|
+
if (!existsSync8(dir)) return [];
|
|
24719
|
+
const entries = readdirSync4(dir, { withFileTypes: true });
|
|
24394
24720
|
const results = [];
|
|
24395
24721
|
for (const entry of entries) {
|
|
24396
|
-
const fullPath =
|
|
24722
|
+
const fullPath = join8(dir, entry.name);
|
|
24397
24723
|
if (entry.isDirectory()) {
|
|
24398
24724
|
if (ignoreSubdirs.has(entry.name)) continue;
|
|
24399
24725
|
results.push(...collectMemoryFiles(fullPath, baseDir, ignoreSubdirs));
|
|
@@ -24406,19 +24732,19 @@ function collectMemoryFiles(dir, baseDir, ignoreSubdirs = /* @__PURE__ */ new Se
|
|
|
24406
24732
|
}
|
|
24407
24733
|
function computeKxmMemoryRevision(bundle, options = {}) {
|
|
24408
24734
|
const hash = createHash9("sha256");
|
|
24409
|
-
const memoryDir = options.memoryDir ??
|
|
24735
|
+
const memoryDir = options.memoryDir ?? join8(bundle.projectRoot, ".kxm", "memory");
|
|
24410
24736
|
const memoryFiles = collectMemoryFiles(memoryDir, memoryDir, /* @__PURE__ */ new Set(["candidates"])).sort((left, right) => compareCodeUnits6(left.relPath, right.relPath));
|
|
24411
24737
|
for (const file of memoryFiles) {
|
|
24412
24738
|
hash.update(`memory:${file.relPath}\0`, "utf8");
|
|
24413
|
-
hash.update(
|
|
24739
|
+
hash.update(readFileSync5(file.fullPath));
|
|
24414
24740
|
hash.update("\0", "utf8");
|
|
24415
24741
|
}
|
|
24416
|
-
const skillsDir = options.skillsDir ??
|
|
24417
|
-
const promotedSkillsDir =
|
|
24742
|
+
const skillsDir = options.skillsDir ?? join8(bundle.projectRoot, ".kxm", "skills");
|
|
24743
|
+
const promotedSkillsDir = join8(skillsDir, "promoted");
|
|
24418
24744
|
const skillFiles = collectMemoryFiles(promotedSkillsDir, promotedSkillsDir).sort((left, right) => compareCodeUnits6(left.relPath, right.relPath));
|
|
24419
24745
|
for (const file of skillFiles) {
|
|
24420
24746
|
hash.update(`skill:${file.relPath}\0`, "utf8");
|
|
24421
|
-
hash.update(
|
|
24747
|
+
hash.update(readFileSync5(file.fullPath));
|
|
24422
24748
|
hash.update("\0", "utf8");
|
|
24423
24749
|
}
|
|
24424
24750
|
if (options.promotedState && options.promotedState.length > 0) {
|
|
@@ -24504,7 +24830,7 @@ function openKxmRuntimeContext(projectRoot, options) {
|
|
|
24504
24830
|
now: options.now ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
24505
24831
|
...options.logger ? { logger: options.logger } : {}
|
|
24506
24832
|
});
|
|
24507
|
-
const eventStore = new KxmRunEventStore(
|
|
24833
|
+
const eventStore = new KxmRunEventStore(join8(paths.projectsDir, registration.projectKey, "run-events.db"));
|
|
24508
24834
|
registerKxmRuntimeHandle(eventStore.path);
|
|
24509
24835
|
return {
|
|
24510
24836
|
registry,
|
|
@@ -24565,7 +24891,7 @@ function acceptKxmRun(context, bundle, request, options = {}) {
|
|
|
24565
24891
|
const commandId = request.commandId ?? newKxmCommandId();
|
|
24566
24892
|
const now = options.now ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
24567
24893
|
const monotonicNs = options.monotonicNs ?? kxmMonotonicNs();
|
|
24568
|
-
const workflow = bundle.workflows.
|
|
24894
|
+
const workflow = lookupKxmResource(bundle.workflows.values(), request.workflowId, "workflow");
|
|
24569
24895
|
if (!workflow) {
|
|
24570
24896
|
throw runtimeError("run_workflow_unknown", ".kxm/workflows", `workflow ${request.workflowId} does not exist in this project`);
|
|
24571
24897
|
}
|
|
@@ -24763,7 +25089,7 @@ function cancelKxmRun(context, runId, options = {}) {
|
|
|
24763
25089
|
const events = [];
|
|
24764
25090
|
let sequence = context.eventStore.nextSequence(runId);
|
|
24765
25091
|
let nextMono = monotonicNs;
|
|
24766
|
-
const
|
|
25092
|
+
const push2 = (eventType, payload) => {
|
|
24767
25093
|
const event = {
|
|
24768
25094
|
...kxmEventBase(context, run, now, nextMono, commandId),
|
|
24769
25095
|
eventId: newKxmEventId(),
|
|
@@ -24787,11 +25113,11 @@ function cancelKxmRun(context, runId, options = {}) {
|
|
|
24787
25113
|
}
|
|
24788
25114
|
cancelPayload.budget = options.budget;
|
|
24789
25115
|
}
|
|
24790
|
-
|
|
25116
|
+
push2("run.cancel_requested", cancelPayload);
|
|
24791
25117
|
let status = "cancelled";
|
|
24792
25118
|
if (folded.status === "running" && activeAttempt) {
|
|
24793
25119
|
status = "cancelling";
|
|
24794
|
-
|
|
25120
|
+
push2("run.status_changed", { status: "cancelling", reason: cancelReason });
|
|
24795
25121
|
const attemptIds = new Set(kxmFoldPanelAttemptIds(folded.currentStep));
|
|
24796
25122
|
if (folded.currentStep?.attemptId) attemptIds.add(folded.currentStep.attemptId);
|
|
24797
25123
|
for (const attemptId of attemptIds) {
|
|
@@ -24804,7 +25130,7 @@ function cancelKxmRun(context, runId, options = {}) {
|
|
|
24804
25130
|
abortControllers.push(owned.controller);
|
|
24805
25131
|
}
|
|
24806
25132
|
} else if (folded.status === "blocked_uncertain") {
|
|
24807
|
-
|
|
25133
|
+
push2("run.status_changed", { status: "cancelling", reason: cancelReason });
|
|
24808
25134
|
const attemptId = folded.currentStep?.attemptId;
|
|
24809
25135
|
if (attemptId) {
|
|
24810
25136
|
const capability = context.eventStore.capabilityByAttempt(attemptId);
|
|
@@ -24816,13 +25142,13 @@ function cancelKxmRun(context, runId, options = {}) {
|
|
|
24816
25142
|
for (const owned of kxmAttemptControllers(context.eventStore.path, runId)) {
|
|
24817
25143
|
abortControllers.push(owned.controller);
|
|
24818
25144
|
}
|
|
24819
|
-
|
|
25145
|
+
push2("run.status_changed", { status: "cancelled", reason: cancelReason });
|
|
24820
25146
|
status = "cancelled";
|
|
24821
25147
|
} else if (folded.status === "running" && !activeAttempt) {
|
|
24822
|
-
|
|
24823
|
-
|
|
25148
|
+
push2("run.status_changed", { status: "cancelling", reason: cancelReason });
|
|
25149
|
+
push2("run.status_changed", { status: "cancelled", reason: cancelReason });
|
|
24824
25150
|
} else {
|
|
24825
|
-
|
|
25151
|
+
push2("run.status_changed", { status: "cancelled", reason: cancelReason });
|
|
24826
25152
|
}
|
|
24827
25153
|
for (const event of events) context.eventStore.appendEvent(event);
|
|
24828
25154
|
const nextState = foldStoredKxmRun(context, run);
|
|
@@ -24854,15 +25180,15 @@ function assertKxmConfigError(error) {
|
|
|
24854
25180
|
// plugins/kxm/src/runtime-supervisor.ts
|
|
24855
25181
|
import { spawn as spawn3 } from "node:child_process";
|
|
24856
25182
|
import { createHash as createHash16, createHmac, randomBytes as randomBytes3, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
24857
|
-
import { chmodSync as chmodSync2, existsSync as
|
|
25183
|
+
import { chmodSync as chmodSync2, existsSync as existsSync17, lstatSync as lstatSync6, mkdirSync as mkdirSync9, readFileSync as readFileSync14, renameSync as renameSync5, rmSync as rmSync4, writeFileSync as writeFileSync9 } from "node:fs";
|
|
24858
25184
|
import { createServer } from "node:http";
|
|
24859
|
-
import { dirname as dirname11, isAbsolute as isAbsolute8, join as
|
|
25185
|
+
import { dirname as dirname11, isAbsolute as isAbsolute8, join as join19 } from "node:path";
|
|
24860
25186
|
|
|
24861
25187
|
// plugins/kxm/src/oneshot-evidence.ts
|
|
24862
25188
|
import { createHash as createHash10, randomUUID as randomUUID3 } from "node:crypto";
|
|
24863
|
-
import { lstatSync as lstatSync4, readdirSync as
|
|
25189
|
+
import { lstatSync as lstatSync4, readdirSync as readdirSync5, readFileSync as readFileSync6 } from "node:fs";
|
|
24864
25190
|
import { lstat, mkdir, open, rename } from "node:fs/promises";
|
|
24865
|
-
import { join as
|
|
25191
|
+
import { join as join9 } from "node:path";
|
|
24866
25192
|
var ONESHOT_EVIDENCE_SCHEMA = "kxm.oneshot-evidence.v2";
|
|
24867
25193
|
var RETIRED_ONESHOT_EVIDENCE_SCHEMAS = /* @__PURE__ */ new Set(["kxm.oneshot-evidence.v1"]);
|
|
24868
25194
|
var TEXT_LIMIT = 4 * 1024 * 1024;
|
|
@@ -24886,12 +25212,12 @@ function parseOneShotEvidenceRecord(bytes) {
|
|
|
24886
25212
|
return parsed;
|
|
24887
25213
|
}
|
|
24888
25214
|
function oneShotEvidenceRoot() {
|
|
24889
|
-
return
|
|
25215
|
+
return join9(kxmUserStateRoot(), "runtime", "oneshot-evidence");
|
|
24890
25216
|
}
|
|
24891
25217
|
function readOneShotObservedChildExit(runId, attemptId, root = oneShotEvidenceRoot()) {
|
|
24892
25218
|
let names3;
|
|
24893
25219
|
try {
|
|
24894
|
-
names3 =
|
|
25220
|
+
names3 = readdirSync5(root);
|
|
24895
25221
|
} catch {
|
|
24896
25222
|
return void 0;
|
|
24897
25223
|
}
|
|
@@ -24900,13 +25226,13 @@ function readOneShotObservedChildExit(runId, attemptId, root = oneShotEvidenceRo
|
|
|
24900
25226
|
const capped = names3.slice(0, 4096);
|
|
24901
25227
|
for (const name of capped) {
|
|
24902
25228
|
if (!name.startsWith("evd_")) continue;
|
|
24903
|
-
const dir =
|
|
25229
|
+
const dir = join9(root, name);
|
|
24904
25230
|
try {
|
|
24905
25231
|
const info = lstatSync4(dir);
|
|
24906
25232
|
if (!info.isDirectory() || info.isSymbolicLink()) continue;
|
|
24907
|
-
const intent = parseOneShotEvidenceRecord(
|
|
25233
|
+
const intent = parseOneShotEvidenceRecord(readFileSync6(join9(dir, "intent.json"), "utf8"));
|
|
24908
25234
|
if (intent.runId !== runId || intent.attemptId !== attemptId) continue;
|
|
24909
|
-
const result = parseOneShotEvidenceRecord(
|
|
25235
|
+
const result = parseOneShotEvidenceRecord(readFileSync6(join9(dir, "result.json"), "utf8"));
|
|
24910
25236
|
if (result.observedChildExit === true) seenTrue = true;
|
|
24911
25237
|
else if (result.observedChildExit === false) seenFalse = true;
|
|
24912
25238
|
} catch {
|
|
@@ -24923,7 +25249,7 @@ async function beginOneShotEvidence(root, intent, sensitive) {
|
|
|
24923
25249
|
throw new Error("oneshot_evidence_root_not_private");
|
|
24924
25250
|
}
|
|
24925
25251
|
const id = `evd_${randomUUID3().replaceAll("-", "")}`;
|
|
24926
|
-
const dir =
|
|
25252
|
+
const dir = join9(root, id);
|
|
24927
25253
|
await mkdir(dir, { mode: 448 });
|
|
24928
25254
|
const secrets = [...new Set(sensitive.filter((value) => value.length >= 8))].sort((a, b) => b.length - a.length);
|
|
24929
25255
|
const redact = (value) => {
|
|
@@ -24943,7 +25269,7 @@ async function beginOneShotEvidence(root, intent, sensitive) {
|
|
|
24943
25269
|
const bytes = JSON.stringify(value);
|
|
24944
25270
|
if (Buffer.byteLength(bytes) > RECORD_LIMIT) throw new Error("oneshot_evidence_record_limit");
|
|
24945
25271
|
parseOneShotEvidenceRecord(bytes);
|
|
24946
|
-
const target =
|
|
25272
|
+
const target = join9(dir, name);
|
|
24947
25273
|
const partial = `${target}.partial`;
|
|
24948
25274
|
const file = await open(partial, "wx", 384);
|
|
24949
25275
|
try {
|
|
@@ -25009,10 +25335,10 @@ async function beginOneShotEvidence(root, intent, sensitive) {
|
|
|
25009
25335
|
}
|
|
25010
25336
|
|
|
25011
25337
|
// plugins/kxm/src/prices.ts
|
|
25012
|
-
var
|
|
25338
|
+
var import_yaml4 = __toESM(require_dist(), 1);
|
|
25013
25339
|
import { createHash as createHash11 } from "node:crypto";
|
|
25014
|
-
import { existsSync as
|
|
25015
|
-
import { join as
|
|
25340
|
+
import { existsSync as existsSync9, readFileSync as readFileSync7, statSync as statSync2, writeFileSync as writeFileSync3 } from "node:fs";
|
|
25341
|
+
import { join as join10 } from "node:path";
|
|
25016
25342
|
|
|
25017
25343
|
// plugins/kxm/src/price-calc.ts
|
|
25018
25344
|
var PRICES_SCHEMA = "kxm.prices.v1";
|
|
@@ -25076,7 +25402,7 @@ function hashPriceCatalog(catalog) {
|
|
|
25076
25402
|
return createHash11("sha256").update(JSON.stringify(canonical), "utf8").digest("hex");
|
|
25077
25403
|
}
|
|
25078
25404
|
function parsePriceCatalog(text) {
|
|
25079
|
-
const parsed = (0,
|
|
25405
|
+
const parsed = (0, import_yaml4.parse)(text);
|
|
25080
25406
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
25081
25407
|
throw new Error("price catalog must be an object");
|
|
25082
25408
|
}
|
|
@@ -25164,11 +25490,11 @@ function parsePriceCatalog(text) {
|
|
|
25164
25490
|
return catalog;
|
|
25165
25491
|
}
|
|
25166
25492
|
function loadPriceCatalog(rootOrPath) {
|
|
25167
|
-
const candidatePath =
|
|
25168
|
-
if (!candidatePath || !
|
|
25493
|
+
const candidatePath = existsSync9(join10(rootOrPath, ".kxm", "prices.yaml")) ? join10(rootOrPath, ".kxm", "prices.yaml") : existsSync9(join10(rootOrPath, "prices.yaml")) ? join10(rootOrPath, "prices.yaml") : existsSync9(rootOrPath) && statSync2(rootOrPath).isFile() ? rootOrPath : void 0;
|
|
25494
|
+
if (!candidatePath || !existsSync9(candidatePath)) {
|
|
25169
25495
|
return void 0;
|
|
25170
25496
|
}
|
|
25171
|
-
const content =
|
|
25497
|
+
const content = readFileSync7(candidatePath, "utf8");
|
|
25172
25498
|
return parsePriceCatalog(content);
|
|
25173
25499
|
}
|
|
25174
25500
|
function loadPriceCatalogForEstimate(options) {
|
|
@@ -25185,40 +25511,40 @@ function loadPriceCatalogForEstimate(options) {
|
|
|
25185
25511
|
}
|
|
25186
25512
|
|
|
25187
25513
|
// plugins/kxm/src/engine.ts
|
|
25188
|
-
var
|
|
25514
|
+
var import_yaml10 = __toESM(require_dist(), 1);
|
|
25189
25515
|
import { createHash as createHash15, randomBytes as randomBytes2, timingSafeEqual } from "node:crypto";
|
|
25190
|
-
import { existsSync as
|
|
25191
|
-
import { join as
|
|
25516
|
+
import { existsSync as existsSync13, readFileSync as readFileSync11 } from "node:fs";
|
|
25517
|
+
import { join as join16 } from "node:path";
|
|
25192
25518
|
|
|
25193
25519
|
// plugins/kxm/src/routes.ts
|
|
25194
|
-
var
|
|
25195
|
-
import { existsSync as
|
|
25196
|
-
import { join as
|
|
25520
|
+
var import_yaml7 = __toESM(require_dist(), 1);
|
|
25521
|
+
import { existsSync as existsSync10, readFileSync as readFileSync8, mkdirSync as mkdirSync3, writeFileSync as writeFileSync4, readdirSync as readdirSync6 } from "node:fs";
|
|
25522
|
+
import { join as join11 } from "node:path";
|
|
25197
25523
|
|
|
25198
25524
|
// plugins/kxm/src/role.ts
|
|
25199
|
-
var
|
|
25525
|
+
var import_yaml6 = __toESM(require_dist(), 1);
|
|
25200
25526
|
|
|
25201
25527
|
// plugins/kxm/src/config.ts
|
|
25202
|
-
var
|
|
25528
|
+
var import_yaml5 = __toESM(require_dist(), 1);
|
|
25203
25529
|
|
|
25204
25530
|
// plugins/kxm/src/routes.ts
|
|
25205
25531
|
var RETIRED_POLICY = ".kxm/producers.yaml";
|
|
25206
25532
|
var empty = () => ({ schema: "kxm.routes.v2", updatedAt: (/* @__PURE__ */ new Date()).toISOString(), admitted: [], disabled: [] });
|
|
25207
25533
|
function loadRoutePolicy(root) {
|
|
25208
|
-
if (
|
|
25209
|
-
const path =
|
|
25210
|
-
if (!
|
|
25211
|
-
const value = (0,
|
|
25534
|
+
if (existsSync10(join11(root, RETIRED_POLICY))) throw new Error(`retired ${RETIRED_POLICY} present; use .kxm/routes.yaml (kxm.routes.v2)`);
|
|
25535
|
+
const path = join11(root, ".kxm", "routes.yaml");
|
|
25536
|
+
if (!existsSync10(path)) return empty();
|
|
25537
|
+
const value = (0, import_yaml7.parse)(readFileSync8(path, "utf8"));
|
|
25212
25538
|
if (value?.schema !== "kxm.routes.v2" || !Array.isArray(value.admitted)) throw new Error("invalid .kxm/routes.yaml");
|
|
25213
25539
|
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") : [] };
|
|
25214
25540
|
}
|
|
25215
25541
|
function listRoleBindings(root) {
|
|
25216
|
-
const dir =
|
|
25542
|
+
const dir = join11(root, ".kxm", "roles");
|
|
25217
25543
|
const result = {};
|
|
25218
|
-
if (!
|
|
25219
|
-
for (const file of
|
|
25544
|
+
if (!existsSync10(dir)) return result;
|
|
25545
|
+
for (const file of readdirSync6(dir).filter((name) => name.endsWith(".yaml"))) {
|
|
25220
25546
|
const role = file.slice(0, -5);
|
|
25221
|
-
const value = (0,
|
|
25547
|
+
const value = (0, import_yaml7.parse)(readFileSync8(join11(dir, file), "utf8"));
|
|
25222
25548
|
result[role] = (value.roster ?? []).map((entry) => entry.route).filter((route) => typeof route === "string");
|
|
25223
25549
|
}
|
|
25224
25550
|
return result;
|
|
@@ -25550,8 +25876,8 @@ function pruneContextPacket(packet, tokenBudget) {
|
|
|
25550
25876
|
|
|
25551
25877
|
// plugins/kxm/src/dispatch-context.ts
|
|
25552
25878
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
25553
|
-
import { readdirSync as
|
|
25554
|
-
import { extname as extname3, join as
|
|
25879
|
+
import { readdirSync as readdirSync9 } from "node:fs";
|
|
25880
|
+
import { extname as extname3, join as join14 } from "node:path";
|
|
25555
25881
|
|
|
25556
25882
|
// plugins/kxm/src/protocol.ts
|
|
25557
25883
|
var DEFAULT_MESSAGE_TTL_MS = 24 * 60 * 6e4;
|
|
@@ -26182,9 +26508,9 @@ function memoryRecordToContextItem(record2, project) {
|
|
|
26182
26508
|
}
|
|
26183
26509
|
|
|
26184
26510
|
// plugins/kxm/src/memory.ts
|
|
26185
|
-
var
|
|
26186
|
-
import { existsSync as
|
|
26187
|
-
import { extname as extname2, join as
|
|
26511
|
+
var import_yaml8 = __toESM(require_dist(), 1);
|
|
26512
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync4, readdirSync as readdirSync7, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "node:fs";
|
|
26513
|
+
import { extname as extname2, join as join12, resolve as resolve7 } from "node:path";
|
|
26188
26514
|
var MEMORY_SCHEMA = "kxm.memory.v1";
|
|
26189
26515
|
var VALID_SCOPES = /* @__PURE__ */ new Set(["agent", "project", "run", "operator"]);
|
|
26190
26516
|
var VALID_AUTHORITIES = /* @__PURE__ */ new Set(["instruction", "evidence", "promoted"]);
|
|
@@ -26214,7 +26540,7 @@ function parseFrontmatter(content) {
|
|
|
26214
26540
|
}
|
|
26215
26541
|
function parseMemoryRecord(raw, filename = "memory.md") {
|
|
26216
26542
|
const { frontmatter, body } = parseFrontmatter(raw);
|
|
26217
|
-
const data = (0,
|
|
26543
|
+
const data = (0, import_yaml8.parse)(frontmatter);
|
|
26218
26544
|
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
26219
26545
|
throw new Error(`invalid YAML frontmatter in ${filename}`);
|
|
26220
26546
|
}
|
|
@@ -26283,20 +26609,20 @@ function parseMemoryRecord(raw, filename = "memory.md") {
|
|
|
26283
26609
|
}
|
|
26284
26610
|
function memoryDirectories(repoRoot) {
|
|
26285
26611
|
const root = resolve7(repoRoot);
|
|
26286
|
-
const memoryDir =
|
|
26287
|
-
const candidatesDir =
|
|
26612
|
+
const memoryDir = join12(root, ".kxm", "memory");
|
|
26613
|
+
const candidatesDir = join12(memoryDir, "candidates");
|
|
26288
26614
|
return { memoryDir, candidatesDir };
|
|
26289
26615
|
}
|
|
26290
26616
|
function loadAuthoredMemory(repoRoot) {
|
|
26291
26617
|
const { memoryDir } = memoryDirectories(repoRoot);
|
|
26292
|
-
if (!
|
|
26618
|
+
if (!existsSync11(memoryDir)) return [];
|
|
26293
26619
|
const records = [];
|
|
26294
|
-
const entries =
|
|
26620
|
+
const entries = readdirSync7(memoryDir, { withFileTypes: true });
|
|
26295
26621
|
for (const entry of entries) {
|
|
26296
26622
|
if (entry.isFile() && extname2(entry.name) === ".md") {
|
|
26297
|
-
const fullPath =
|
|
26623
|
+
const fullPath = join12(memoryDir, entry.name);
|
|
26298
26624
|
try {
|
|
26299
|
-
const text =
|
|
26625
|
+
const text = readFileSync9(fullPath, "utf8");
|
|
26300
26626
|
const record2 = parseMemoryRecord(text, entry.name);
|
|
26301
26627
|
if (record2.lifecycle === "active") {
|
|
26302
26628
|
records.push(record2);
|
|
@@ -26310,10 +26636,10 @@ function loadAuthoredMemory(repoRoot) {
|
|
|
26310
26636
|
}
|
|
26311
26637
|
|
|
26312
26638
|
// plugins/kxm/src/skills.ts
|
|
26313
|
-
var
|
|
26639
|
+
var import_yaml9 = __toESM(require_dist(), 1);
|
|
26314
26640
|
import { createHash as createHash12 } from "node:crypto";
|
|
26315
|
-
import { existsSync as
|
|
26316
|
-
import { dirname as dirname7, join as
|
|
26641
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync5, readdirSync as readdirSync8, readFileSync as readFileSync10, renameSync, rmSync, statSync as statSync3, writeFileSync as writeFileSync6 } from "node:fs";
|
|
26642
|
+
import { dirname as dirname7, join as join13 } from "node:path";
|
|
26317
26643
|
var SKILL_CANDIDATE_SCHEMA = "kxm.skill-candidate.v1";
|
|
26318
26644
|
var SKILL_EVALUATION_SCHEMA = "kxm.skill-evaluation.v1";
|
|
26319
26645
|
var SKILL_DECISION_SCHEMA = "kxm.skill-decision.v1";
|
|
@@ -26354,7 +26680,7 @@ function parseSkillFrontmatter(content) {
|
|
|
26354
26680
|
return { frontmatter: null, body: content };
|
|
26355
26681
|
}
|
|
26356
26682
|
try {
|
|
26357
|
-
const parsed = (0,
|
|
26683
|
+
const parsed = (0, import_yaml9.parse)(rawFm);
|
|
26358
26684
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
26359
26685
|
return { frontmatter: parsed, body: rawBody };
|
|
26360
26686
|
}
|
|
@@ -26419,20 +26745,20 @@ var SkillLifecycle = class {
|
|
|
26419
26745
|
writeFileSync6(file, content);
|
|
26420
26746
|
}
|
|
26421
26747
|
dir(state) {
|
|
26422
|
-
return
|
|
26748
|
+
return join13(this.root, state === "candidate" ? "candidates" : `${state}s`.replace("rejecteds", "rejected").replace("promoteds", "promoted"));
|
|
26423
26749
|
}
|
|
26424
26750
|
historyFile(id) {
|
|
26425
|
-
return
|
|
26751
|
+
return join13(this.root, "history", `${id}.jsonl`);
|
|
26426
26752
|
}
|
|
26427
26753
|
paths(state, id) {
|
|
26428
|
-
const dir =
|
|
26429
|
-
return { dir, metadata:
|
|
26754
|
+
const dir = join13(this.dir(state), id);
|
|
26755
|
+
return { dir, metadata: join13(dir, "metadata.json"), skill: join13(dir, "SKILL.md") };
|
|
26430
26756
|
}
|
|
26431
26757
|
appendHistory(id, record2) {
|
|
26432
26758
|
const line = `${JSON.stringify(record2)}
|
|
26433
26759
|
`;
|
|
26434
|
-
if (
|
|
26435
|
-
const existing =
|
|
26760
|
+
if (existsSync12(this.historyFile(id))) {
|
|
26761
|
+
const existing = readFileSync10(this.historyFile(id), "utf8");
|
|
26436
26762
|
const lines = existing.split("\n").filter((entry) => entry.trim());
|
|
26437
26763
|
this.write(this.historyFile(id), [...lines.slice(-499), line.trim()].join("\n") + "\n");
|
|
26438
26764
|
} else {
|
|
@@ -26441,20 +26767,20 @@ var SkillLifecycle = class {
|
|
|
26441
26767
|
}
|
|
26442
26768
|
history(id) {
|
|
26443
26769
|
const file = this.historyFile(id);
|
|
26444
|
-
if (!
|
|
26445
|
-
return
|
|
26770
|
+
if (!existsSync12(file)) return [];
|
|
26771
|
+
return readFileSync10(file, "utf8").split("\n").filter((line) => line.trim()).map((line) => JSON.parse(line));
|
|
26446
26772
|
}
|
|
26447
26773
|
readMetadata(state, id) {
|
|
26448
26774
|
const { metadata } = this.paths(state, id);
|
|
26449
|
-
if (!
|
|
26775
|
+
if (!existsSync12(metadata)) {
|
|
26450
26776
|
throw new SkillLifecycleError("skill_not_found", `skill ${id} not found in ${state}`);
|
|
26451
26777
|
}
|
|
26452
|
-
return JSON.parse(
|
|
26778
|
+
return JSON.parse(readFileSync10(metadata, "utf8"));
|
|
26453
26779
|
}
|
|
26454
26780
|
move(from, to, id) {
|
|
26455
|
-
const fromDir =
|
|
26456
|
-
const toDir =
|
|
26457
|
-
if (!
|
|
26781
|
+
const fromDir = join13(this.dir(from), id);
|
|
26782
|
+
const toDir = join13(this.dir(to), id);
|
|
26783
|
+
if (!existsSync12(fromDir)) {
|
|
26458
26784
|
throw new SkillLifecycleError("skill_not_found", `skill ${id} not found in ${from}`);
|
|
26459
26785
|
}
|
|
26460
26786
|
if (this.dryRun) {
|
|
@@ -26462,7 +26788,7 @@ var SkillLifecycle = class {
|
|
|
26462
26788
|
return;
|
|
26463
26789
|
}
|
|
26464
26790
|
mkdirSync5(this.dir(to), { recursive: true });
|
|
26465
|
-
if (
|
|
26791
|
+
if (existsSync12(toDir)) rmSync(toDir, { recursive: true, force: true });
|
|
26466
26792
|
renameSync(fromDir, toDir);
|
|
26467
26793
|
}
|
|
26468
26794
|
/** Submit a new skill candidate. Content is redacted of secret material at
|
|
@@ -26505,7 +26831,7 @@ var SkillLifecycle = class {
|
|
|
26505
26831
|
const contentSha256 = skillContentSha256(content);
|
|
26506
26832
|
const id = skillIdFor(name, contentSha256);
|
|
26507
26833
|
const { metadata, skill } = this.paths("candidate", id);
|
|
26508
|
-
if (
|
|
26834
|
+
if (existsSync12(metadata)) {
|
|
26509
26835
|
throw new SkillLifecycleError(
|
|
26510
26836
|
"skill_candidate_exists",
|
|
26511
26837
|
`identical candidate ${id} already exists; changed behavior requires changed content`
|
|
@@ -26614,11 +26940,11 @@ var SkillLifecycle = class {
|
|
|
26614
26940
|
};
|
|
26615
26941
|
const candidatePaths = this.paths("candidate", candidateId);
|
|
26616
26942
|
const promotedPaths = this.paths("promoted", candidateId);
|
|
26617
|
-
const skillContent =
|
|
26618
|
-
const metadataContent =
|
|
26943
|
+
const skillContent = readFileSync10(candidatePaths.skill, "utf8");
|
|
26944
|
+
const metadataContent = readFileSync10(candidatePaths.metadata, "utf8");
|
|
26619
26945
|
this.write(promotedPaths.skill, skillContent);
|
|
26620
26946
|
this.write(promotedPaths.metadata, metadataContent);
|
|
26621
|
-
const patchPath =
|
|
26947
|
+
const patchPath = join13(this.root, "patches", `${candidateId}.patch`);
|
|
26622
26948
|
const relSkillPath = `.kxm/skills/promoted/${candidateId}/SKILL.md`;
|
|
26623
26949
|
const relMetaPath = `.kxm/skills/promoted/${candidateId}/metadata.json`;
|
|
26624
26950
|
const patch = `${createUnifiedPatch(relSkillPath, skillContent)}${createUnifiedPatch(relMetaPath, metadataContent)}`;
|
|
@@ -26646,7 +26972,7 @@ var SkillLifecycle = class {
|
|
|
26646
26972
|
verify(state, id) {
|
|
26647
26973
|
const metadata = this.readMetadata(state, id);
|
|
26648
26974
|
const { skill } = this.paths(state, id);
|
|
26649
|
-
const content =
|
|
26975
|
+
const content = readFileSync10(skill, "utf8");
|
|
26650
26976
|
if (skillContentSha256(content) !== metadata.contentSha256) {
|
|
26651
26977
|
throw new SkillLifecycleError(
|
|
26652
26978
|
"skill_integrity_violation",
|
|
@@ -26664,7 +26990,7 @@ var SkillLifecycle = class {
|
|
|
26664
26990
|
}
|
|
26665
26991
|
list(state) {
|
|
26666
26992
|
const dir = this.dir(state);
|
|
26667
|
-
if (!
|
|
26993
|
+
if (!existsSync12(dir)) return [];
|
|
26668
26994
|
const ids = readdirSorted(dir);
|
|
26669
26995
|
return ids.map((id) => {
|
|
26670
26996
|
try {
|
|
@@ -26677,7 +27003,7 @@ var SkillLifecycle = class {
|
|
|
26677
27003
|
read(state, id) {
|
|
26678
27004
|
const metadata = this.readMetadata(state, id);
|
|
26679
27005
|
const { skill } = this.paths(state, id);
|
|
26680
|
-
return { metadata, content:
|
|
27006
|
+
return { metadata, content: readFileSync10(skill, "utf8") };
|
|
26681
27007
|
}
|
|
26682
27008
|
};
|
|
26683
27009
|
function boundedList(value, field) {
|
|
@@ -26692,7 +27018,7 @@ function boundedList(value, field) {
|
|
|
26692
27018
|
return [...new Set(refs)];
|
|
26693
27019
|
}
|
|
26694
27020
|
function readdirSorted(dir) {
|
|
26695
|
-
return
|
|
27021
|
+
return readdirSync8(dir).filter((entry) => statSync3(join13(dir, entry)).isDirectory()).sort();
|
|
26696
27022
|
}
|
|
26697
27023
|
|
|
26698
27024
|
// plugins/kxm/src/dispatch-context.ts
|
|
@@ -26733,18 +27059,18 @@ function contextRoleForAgent(agentId) {
|
|
|
26733
27059
|
return agentId;
|
|
26734
27060
|
}
|
|
26735
27061
|
function dispatchContextPresent(projectRoot) {
|
|
26736
|
-
return hasTopLevelMarkdown(
|
|
27062
|
+
return hasTopLevelMarkdown(join14(projectRoot, ".kxm", "memory")) || hasAnyEntry(join14(projectRoot, ".kxm", "skills", "promoted"));
|
|
26737
27063
|
}
|
|
26738
27064
|
function hasTopLevelMarkdown(dir) {
|
|
26739
27065
|
try {
|
|
26740
|
-
return
|
|
27066
|
+
return readdirSync9(dir, { withFileTypes: true }).some((entry) => entry.isFile() && extname3(entry.name) === ".md");
|
|
26741
27067
|
} catch {
|
|
26742
27068
|
return false;
|
|
26743
27069
|
}
|
|
26744
27070
|
}
|
|
26745
27071
|
function hasAnyEntry(dir) {
|
|
26746
27072
|
try {
|
|
26747
|
-
return
|
|
27073
|
+
return readdirSync9(dir).length > 0;
|
|
26748
27074
|
} catch {
|
|
26749
27075
|
return false;
|
|
26750
27076
|
}
|
|
@@ -26809,7 +27135,7 @@ function loadDispatchContextSources(input) {
|
|
|
26809
27135
|
}
|
|
26810
27136
|
const promoted = [];
|
|
26811
27137
|
try {
|
|
26812
|
-
const lifecycle = new SkillLifecycle(
|
|
27138
|
+
const lifecycle = new SkillLifecycle(join14(projectRoot, ".kxm", "skills"));
|
|
26813
27139
|
for (const metadata of lifecycle.list("promoted")) {
|
|
26814
27140
|
try {
|
|
26815
27141
|
promoted.push(lifecycle.verify("promoted", metadata.id));
|
|
@@ -26892,7 +27218,7 @@ function assembleDispatchContext(sources, input) {
|
|
|
26892
27218
|
}
|
|
26893
27219
|
|
|
26894
27220
|
// plugins/kxm/src/engine-artifacts.ts
|
|
26895
|
-
import { join as
|
|
27221
|
+
import { join as join15 } from "node:path";
|
|
26896
27222
|
|
|
26897
27223
|
// plugins/kxm/src/artifacts-exist.ts
|
|
26898
27224
|
import { lstatSync as lstatSync5, realpathSync as realpathSync4, statSync as statSync4 } from "node:fs";
|
|
@@ -26939,14 +27265,14 @@ function verifyArtifactExists(rootInput, pathInput) {
|
|
|
26939
27265
|
// plugins/kxm/src/engine-artifacts.ts
|
|
26940
27266
|
var kxmArtifactsGateSeams = {};
|
|
26941
27267
|
function evaluateArtifactsGate(context, definition) {
|
|
26942
|
-
const root =
|
|
27268
|
+
const root = join15(context.projectRoot, ".kxm", "assets");
|
|
26943
27269
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
26944
27270
|
const startTime = process.hrtime.bigint();
|
|
26945
27271
|
let checkedCount = 0;
|
|
26946
27272
|
let failedCount = 0;
|
|
26947
27273
|
for (const [index, path] of definition.paths.entries()) {
|
|
26948
27274
|
checkedCount += 1;
|
|
26949
|
-
const result = verifyArtifactExists(root,
|
|
27275
|
+
const result = verifyArtifactExists(root, join15(root, path));
|
|
26950
27276
|
if (!result.ok) failedCount += 1;
|
|
26951
27277
|
kxmArtifactsGateSeams.afterPathChecked?.({ path, index, checkedCount, failedCount });
|
|
26952
27278
|
}
|
|
@@ -28091,7 +28417,7 @@ function pinKxmCompiledPlan(context, bundle, runId, options) {
|
|
|
28091
28417
|
if (String(bundle.project.value.id) !== run.projectId) {
|
|
28092
28418
|
throw runtimeError("run_owner_mismatch", runId, "bundle project does not match the run");
|
|
28093
28419
|
}
|
|
28094
|
-
const workflow = bundle.workflows.
|
|
28420
|
+
const workflow = lookupKxmResource(bundle.workflows.values(), run.workflowId, "workflow");
|
|
28095
28421
|
if (!workflow) throw runtimeError("run_workflow_unknown", run.workflowId, `workflow ${run.workflowId} does not exist in this project`);
|
|
28096
28422
|
const revisions = kxmPolicyRevisions(bundle, options);
|
|
28097
28423
|
if (revisions.configRevision !== run.configRevision || revisions.executorPolicyRevision !== run.executorPolicyRevision || revisions.toolPolicyRevision !== run.toolPolicyRevision || revisions.memoryRevision !== run.memoryRevision) {
|
|
@@ -28824,9 +29150,9 @@ function readYamlFile(file) {
|
|
|
28824
29150
|
return readModelDocument(file);
|
|
28825
29151
|
}
|
|
28826
29152
|
function readModelDocument(file) {
|
|
28827
|
-
if (!
|
|
29153
|
+
if (!existsSync13(file)) return void 0;
|
|
28828
29154
|
try {
|
|
28829
|
-
const parsed = (0,
|
|
29155
|
+
const parsed = (0, import_yaml10.parse)(readFileSync11(file, "utf8"));
|
|
28830
29156
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return void 0;
|
|
28831
29157
|
return parsed;
|
|
28832
29158
|
} catch {
|
|
@@ -28857,7 +29183,8 @@ function resolveProducerRoute(projectRoot, step, agentId) {
|
|
|
28857
29183
|
}
|
|
28858
29184
|
};
|
|
28859
29185
|
}
|
|
28860
|
-
const
|
|
29186
|
+
const agentFile = findYamlBasename(join16(projectRoot, ".kxm", "agents"), agentId, "agent") ?? agentId;
|
|
29187
|
+
const agent = readYamlFile(join16(projectRoot, ".kxm", "agents", `${agentFile}.yaml`));
|
|
28861
29188
|
const role = typeof agent?.role === "string" ? agent.role : "";
|
|
28862
29189
|
if (!role) {
|
|
28863
29190
|
return {
|
|
@@ -28868,7 +29195,7 @@ function resolveProducerRoute(projectRoot, step, agentId) {
|
|
|
28868
29195
|
}
|
|
28869
29196
|
};
|
|
28870
29197
|
}
|
|
28871
|
-
const roleDoc = readYamlFile(
|
|
29198
|
+
const roleDoc = readYamlFile(join16(projectRoot, ".kxm", "roles", `${role}.yaml`));
|
|
28872
29199
|
const roster = Array.isArray(roleDoc?.roster) ? roleDoc.roster : void 0;
|
|
28873
29200
|
if (!roster) {
|
|
28874
29201
|
return {
|
|
@@ -28884,7 +29211,8 @@ function resolveProducerRoute(projectRoot, step, agentId) {
|
|
|
28884
29211
|
if (!entry || typeof entry !== "object" || Array.isArray(entry)) continue;
|
|
28885
29212
|
const record2 = entry;
|
|
28886
29213
|
if (typeof record2.route !== "string" || record2.route.length === 0) continue;
|
|
28887
|
-
const
|
|
29214
|
+
const routeFile = findYamlBasename(join16(projectRoot, ".kxm", "models"), record2.route, "route") ?? record2.route;
|
|
29215
|
+
const modelPath = join16(projectRoot, ".kxm", "models", `${routeFile}.yaml`);
|
|
28888
29216
|
const doc = readModelDocument(modelPath);
|
|
28889
29217
|
if (!doc) {
|
|
28890
29218
|
return {
|
|
@@ -28947,7 +29275,7 @@ function resolveProducerRoute(projectRoot, step, agentId) {
|
|
|
28947
29275
|
model: selector.slice(slash + 1),
|
|
28948
29276
|
selector,
|
|
28949
29277
|
harness,
|
|
28950
|
-
routeId:
|
|
29278
|
+
routeId: routeFile,
|
|
28951
29279
|
role,
|
|
28952
29280
|
permissions,
|
|
28953
29281
|
...typeof record2.effort === "string" ? { effort: record2.effort } : {}
|
|
@@ -28989,7 +29317,7 @@ function settleExitedExecutingAttempt(context, run, executing) {
|
|
|
28989
29317
|
let sequence = context.eventStore.nextSequence(run.runId);
|
|
28990
29318
|
let mono = kxmMonotonicNs();
|
|
28991
29319
|
const events = [];
|
|
28992
|
-
const
|
|
29320
|
+
const push2 = (eventType, payload) => {
|
|
28993
29321
|
events.push({
|
|
28994
29322
|
...kxmEventBase(context, run, now, mono),
|
|
28995
29323
|
eventId: newKxmEventId(),
|
|
@@ -28999,18 +29327,18 @@ function settleExitedExecutingAttempt(context, run, executing) {
|
|
|
28999
29327
|
});
|
|
29000
29328
|
mono = kxmIncrementMonotonicNs(mono);
|
|
29001
29329
|
};
|
|
29002
|
-
|
|
29003
|
-
|
|
29330
|
+
push2("attempt.status_changed", { attemptId: executing.attemptId, status: "settling" });
|
|
29331
|
+
push2("assignment.result_recorded", {
|
|
29004
29332
|
assignmentId: executing.assignmentId,
|
|
29005
29333
|
resultClass: "producer_rejected",
|
|
29006
29334
|
status: "result_recorded",
|
|
29007
29335
|
producerError: "executing_unrecorded",
|
|
29008
29336
|
reason: "executing_unrecorded"
|
|
29009
29337
|
});
|
|
29010
|
-
|
|
29011
|
-
|
|
29012
|
-
|
|
29013
|
-
|
|
29338
|
+
push2("attempt.status_changed", { attemptId: executing.attemptId, status: "terminal" });
|
|
29339
|
+
push2("assignment.terminal", { assignmentId: executing.assignmentId, outcome: "failed", status: "terminal" });
|
|
29340
|
+
push2("step.status_changed", { stepId: executing.stepId, status: "cancelled", previousStatus: "running" });
|
|
29341
|
+
push2("run.status_changed", { status: "cancelled", reason: cancelReasonFromLog2(context, run.runId) });
|
|
29014
29342
|
for (const event of events) context.eventStore.appendEvent(event);
|
|
29015
29343
|
context.eventStore.settleCapability(executing.attemptId, "settled");
|
|
29016
29344
|
const next = foldStoredKxmRun(context, run);
|
|
@@ -29191,7 +29519,7 @@ function prepareDispatch(context, runId, producerId, dispatchSources) {
|
|
|
29191
29519
|
let sequence = context.eventStore.nextSequence(runId);
|
|
29192
29520
|
let mono = kxmMonotonicNs();
|
|
29193
29521
|
const events = [];
|
|
29194
|
-
const
|
|
29522
|
+
const push2 = (eventType, payload) => {
|
|
29195
29523
|
events.push({
|
|
29196
29524
|
...kxmEventBase(context, run, now, mono),
|
|
29197
29525
|
eventId: newKxmEventId(),
|
|
@@ -29201,8 +29529,8 @@ function prepareDispatch(context, runId, producerId, dispatchSources) {
|
|
|
29201
29529
|
});
|
|
29202
29530
|
mono = kxmIncrementMonotonicNs(mono);
|
|
29203
29531
|
};
|
|
29204
|
-
|
|
29205
|
-
|
|
29532
|
+
push2("step.entered", { stepId, stepAttempt, status: "pending" });
|
|
29533
|
+
push2("step.status_changed", { stepId, status: "preparing", previousStatus: "pending" });
|
|
29206
29534
|
for (const event of events) context.eventStore.appendEvent(event);
|
|
29207
29535
|
const entered = foldStoredKxmRun(context, run);
|
|
29208
29536
|
persistKxmRunState(context, runId, entered, events[events.length - 1].sequence);
|
|
@@ -29303,7 +29631,7 @@ function birthMember(context, input) {
|
|
|
29303
29631
|
let sequence = context.eventStore.nextSequence(run.runId);
|
|
29304
29632
|
let mono = kxmMonotonicNs();
|
|
29305
29633
|
const events = [];
|
|
29306
|
-
const
|
|
29634
|
+
const push2 = (eventType, payload) => {
|
|
29307
29635
|
events.push({
|
|
29308
29636
|
...kxmEventBase(context, run, now, mono),
|
|
29309
29637
|
eventId: newKxmEventId(),
|
|
@@ -29313,18 +29641,18 @@ function birthMember(context, input) {
|
|
|
29313
29641
|
});
|
|
29314
29642
|
mono = kxmIncrementMonotonicNs(mono);
|
|
29315
29643
|
};
|
|
29316
|
-
|
|
29317
|
-
|
|
29318
|
-
|
|
29319
|
-
|
|
29644
|
+
push2("assignment.created", { assignmentId, stepId: input.stepId, stepAttempt: input.stepAttempt, agentId, status: "created" });
|
|
29645
|
+
push2("assignment.accepted", { assignmentId, status: "accepted" });
|
|
29646
|
+
push2("attempt.created", { attemptId, assignmentId, status: "created" });
|
|
29647
|
+
push2("assignment.dispatched", {
|
|
29320
29648
|
assignmentId,
|
|
29321
29649
|
capabilityHash: minted.hash,
|
|
29322
29650
|
status: "dispatched",
|
|
29323
29651
|
...resolvedRoute ? { model: { provider: resolvedRoute.provider, model: resolvedRoute.model } } : {}
|
|
29324
29652
|
});
|
|
29325
|
-
|
|
29653
|
+
push2("attempt.status_changed", { attemptId, status: "starting" });
|
|
29326
29654
|
if (input.enterRunning) {
|
|
29327
|
-
|
|
29655
|
+
push2("step.status_changed", { stepId: input.stepId, status: "running", previousStatus: "preparing" });
|
|
29328
29656
|
}
|
|
29329
29657
|
for (const event of events) context.eventStore.appendEvent(event);
|
|
29330
29658
|
context.eventStore.insertCapability({
|
|
@@ -29780,7 +30108,7 @@ function settleMember(context, dispatch, result, produceError, produceErrorCode)
|
|
|
29780
30108
|
let sequence = context.eventStore.nextSequence(run.runId);
|
|
29781
30109
|
let mono = kxmMonotonicNs();
|
|
29782
30110
|
const events = [];
|
|
29783
|
-
const
|
|
30111
|
+
const push2 = (eventType, payload) => {
|
|
29784
30112
|
events.push({
|
|
29785
30113
|
...kxmEventBase(context, run, now, mono),
|
|
29786
30114
|
eventId: newKxmEventId(),
|
|
@@ -29790,17 +30118,17 @@ function settleMember(context, dispatch, result, produceError, produceErrorCode)
|
|
|
29790
30118
|
});
|
|
29791
30119
|
mono = kxmIncrementMonotonicNs(mono);
|
|
29792
30120
|
};
|
|
29793
|
-
|
|
30121
|
+
push2("attempt.status_changed", { attemptId: dispatch.attemptId, status: "settling" });
|
|
29794
30122
|
if (cancelling) {
|
|
29795
|
-
|
|
30123
|
+
push2("routing.attempt.recorded", { routing: producerRoutingRecord(
|
|
29796
30124
|
context,
|
|
29797
30125
|
dispatch,
|
|
29798
30126
|
result ?? { outcome: "cancelled", costBasis: "unknown", costUsd: null },
|
|
29799
30127
|
now
|
|
29800
30128
|
) });
|
|
29801
|
-
|
|
29802
|
-
|
|
29803
|
-
|
|
30129
|
+
push2("assignment.result_recorded", { assignmentId: dispatch.assignmentId, resultClass: "cancelled", status: "result_recorded" });
|
|
30130
|
+
push2("attempt.status_changed", { attemptId: dispatch.attemptId, status: "terminal" });
|
|
30131
|
+
push2("assignment.terminal", { assignmentId: dispatch.assignmentId, outcome: "cancelled", status: "terminal" });
|
|
29804
30132
|
} else if (produceError) {
|
|
29805
30133
|
const envelope2 = loadKxmRunPlanEnvelope(context.eventStore, run);
|
|
29806
30134
|
const rawHash = result?.behavioralSha256 ?? behavioralConfigHash({
|
|
@@ -29834,22 +30162,22 @@ function settleMember(context, dispatch, result, produceError, produceErrorCode)
|
|
|
29834
30162
|
finalOutcome: "failed",
|
|
29835
30163
|
providerMetadata: engineRoutingMetadata(dispatch)
|
|
29836
30164
|
};
|
|
29837
|
-
|
|
29838
|
-
|
|
30165
|
+
push2("routing.attempt.recorded", { routing: parseRoutingRecordV2(routingRecord) });
|
|
30166
|
+
push2("assignment.result_recorded", {
|
|
29839
30167
|
assignmentId: dispatch.assignmentId,
|
|
29840
30168
|
resultClass: "producer_rejected",
|
|
29841
30169
|
status: "result_recorded",
|
|
29842
30170
|
...produceErrorCode !== void 0 ? { producerError: produceErrorCode } : {}
|
|
29843
30171
|
});
|
|
29844
|
-
|
|
29845
|
-
|
|
30172
|
+
push2("attempt.status_changed", { attemptId: dispatch.attemptId, status: "terminal" });
|
|
30173
|
+
push2("assignment.terminal", { assignmentId: dispatch.assignmentId, outcome: "failed", status: "terminal" });
|
|
29846
30174
|
} else {
|
|
29847
30175
|
if (!result) {
|
|
29848
30176
|
throw runtimeError("settle_missing_cost_basis", run.runId, `attempt settlement rejected: missing required costBasis for attempt ${dispatch.attemptId}`);
|
|
29849
30177
|
}
|
|
29850
30178
|
const outcome = typeof result.outcome === "string" ? result.outcome : void 0;
|
|
29851
30179
|
const known = outcome !== void 0 && dispatch.step.outcomes.includes(outcome);
|
|
29852
|
-
|
|
30180
|
+
push2("routing.attempt.recorded", {
|
|
29853
30181
|
routing: producerRoutingRecord(
|
|
29854
30182
|
context,
|
|
29855
30183
|
dispatch,
|
|
@@ -29859,22 +30187,22 @@ function settleMember(context, dispatch, result, produceError, produceErrorCode)
|
|
|
29859
30187
|
)
|
|
29860
30188
|
});
|
|
29861
30189
|
if (!known) {
|
|
29862
|
-
|
|
30190
|
+
push2("assignment.result_recorded", {
|
|
29863
30191
|
assignmentId: dispatch.assignmentId,
|
|
29864
30192
|
resultClass: "outcome_unknown",
|
|
29865
30193
|
status: "result_recorded"
|
|
29866
30194
|
});
|
|
29867
|
-
|
|
29868
|
-
|
|
30195
|
+
push2("attempt.status_changed", { attemptId: dispatch.attemptId, status: "terminal" });
|
|
30196
|
+
push2("assignment.terminal", { assignmentId: dispatch.assignmentId, outcome: "failed", status: "terminal" });
|
|
29869
30197
|
} else {
|
|
29870
|
-
|
|
30198
|
+
push2("assignment.result_recorded", {
|
|
29871
30199
|
assignmentId: dispatch.assignmentId,
|
|
29872
30200
|
outcome,
|
|
29873
30201
|
resultClass: "outcome",
|
|
29874
30202
|
status: "result_recorded"
|
|
29875
30203
|
});
|
|
29876
|
-
|
|
29877
|
-
|
|
30204
|
+
push2("attempt.status_changed", { attemptId: dispatch.attemptId, status: "terminal" });
|
|
30205
|
+
push2("assignment.terminal", { assignmentId: dispatch.assignmentId, outcome, status: "terminal" });
|
|
29878
30206
|
}
|
|
29879
30207
|
}
|
|
29880
30208
|
context.eventStore.settleCapability(dispatch.attemptId, "settled");
|
|
@@ -29894,7 +30222,7 @@ function joinPanel(context, panel) {
|
|
|
29894
30222
|
let sequence = context.eventStore.nextSequence(run.runId);
|
|
29895
30223
|
let mono = kxmMonotonicNs();
|
|
29896
30224
|
const events = [];
|
|
29897
|
-
const
|
|
30225
|
+
const push2 = (eventType, payload) => {
|
|
29898
30226
|
events.push({
|
|
29899
30227
|
...kxmEventBase(context, run, now, mono),
|
|
29900
30228
|
eventId: newKxmEventId(),
|
|
@@ -29905,8 +30233,8 @@ function joinPanel(context, panel) {
|
|
|
29905
30233
|
mono = kxmIncrementMonotonicNs(mono);
|
|
29906
30234
|
};
|
|
29907
30235
|
const failRun = (reason) => {
|
|
29908
|
-
|
|
29909
|
-
|
|
30236
|
+
push2("step.status_changed", { stepId: panel.stepId, status: "failed", previousStatus: "running" });
|
|
30237
|
+
push2("run.status_changed", { status: "failed", reason, stepId: panel.stepId });
|
|
29910
30238
|
for (const event of events) context.eventStore.appendEvent(event);
|
|
29911
30239
|
const next2 = foldStoredKxmRun(context, run);
|
|
29912
30240
|
persistKxmRunState(context, run.runId, next2, events[events.length - 1].sequence);
|
|
@@ -29914,8 +30242,8 @@ function joinPanel(context, panel) {
|
|
|
29914
30242
|
return { state: next2 };
|
|
29915
30243
|
};
|
|
29916
30244
|
const cancelRun = () => {
|
|
29917
|
-
|
|
29918
|
-
|
|
30245
|
+
push2("step.status_changed", { stepId: panel.stepId, status: "cancelled", previousStatus: "running" });
|
|
30246
|
+
push2("run.status_changed", { status: "cancelled", reason: cancelReasonFromLog2(context, run.runId) });
|
|
29919
30247
|
for (const event of events) context.eventStore.appendEvent(event);
|
|
29920
30248
|
const next2 = foldStoredKxmRun(context, run);
|
|
29921
30249
|
persistKxmRunState(context, run.runId, next2, events[events.length - 1].sequence);
|
|
@@ -29947,12 +30275,12 @@ function joinPanel(context, panel) {
|
|
|
29947
30275
|
const outcome = joined.outcome;
|
|
29948
30276
|
const selected = panel.step.transitions[outcome];
|
|
29949
30277
|
if (!selected) return failRun("outcome_unknown");
|
|
29950
|
-
|
|
30278
|
+
push2("step.outcome_recorded", { stepId: panel.stepId, stepAttempt: panel.stepAttempt, outcome });
|
|
29951
30279
|
const stepStatus = outcome === "passed" ? "passed" : outcome === "cancelled" ? "cancelled" : "failed";
|
|
29952
|
-
|
|
30280
|
+
push2("step.status_changed", { stepId: panel.stepId, status: stepStatus, previousStatus: "running" });
|
|
29953
30281
|
const budget = transitionBudgetFailure(panel.plan, state, panel.stepId, outcome);
|
|
29954
30282
|
if (budget) {
|
|
29955
|
-
|
|
30283
|
+
push2("run.status_changed", { status: "failed", reason: budget, stepId: panel.stepId, outcome });
|
|
29956
30284
|
for (const event of events) context.eventStore.appendEvent(event);
|
|
29957
30285
|
const next2 = foldStoredKxmRun(context, run);
|
|
29958
30286
|
persistKxmRunState(context, run.runId, next2, events[events.length - 1].sequence);
|
|
@@ -29960,10 +30288,10 @@ function joinPanel(context, panel) {
|
|
|
29960
30288
|
return { state: next2 };
|
|
29961
30289
|
}
|
|
29962
30290
|
if (selected.to === "step") {
|
|
29963
|
-
|
|
30291
|
+
push2("step.transitioned", { fromStepId: panel.stepId, toStepId: selected.target, outcome });
|
|
29964
30292
|
} else {
|
|
29965
|
-
|
|
29966
|
-
|
|
30293
|
+
push2("step.transitioned", { fromStepId: panel.stepId, status: selected.terminalStatus, outcome });
|
|
30294
|
+
push2("run.status_changed", { status: selected.terminalStatus });
|
|
29967
30295
|
}
|
|
29968
30296
|
for (const event of events) context.eventStore.appendEvent(event);
|
|
29969
30297
|
const next = foldStoredKxmRun(context, run);
|
|
@@ -30567,7 +30895,7 @@ function recoverKxmRun(context, runId, request) {
|
|
|
30567
30895
|
let sequence = context.eventStore.nextSequence(runId);
|
|
30568
30896
|
let mono = kxmMonotonicNs();
|
|
30569
30897
|
const events = [];
|
|
30570
|
-
const
|
|
30898
|
+
const push2 = (eventType, payload) => {
|
|
30571
30899
|
events.push({
|
|
30572
30900
|
...kxmEventBase(context, run, now, mono, request.commandId),
|
|
30573
30901
|
eventId: newKxmEventId(),
|
|
@@ -30578,7 +30906,7 @@ function recoverKxmRun(context, runId, request) {
|
|
|
30578
30906
|
mono = kxmIncrementMonotonicNs(mono);
|
|
30579
30907
|
};
|
|
30580
30908
|
if (state.status === "running" && state.currentStep?.effectState === "blocked_uncertain") {
|
|
30581
|
-
|
|
30909
|
+
push2("run.status_changed", {
|
|
30582
30910
|
status: "blocked_uncertain",
|
|
30583
30911
|
previousStatus: "running",
|
|
30584
30912
|
reason: request.reason ?? "uncertain gate effect"
|
|
@@ -30598,7 +30926,7 @@ function recoverKxmRun(context, runId, request) {
|
|
|
30598
30926
|
}
|
|
30599
30927
|
}
|
|
30600
30928
|
if (request.action === "retry" || request.action === "unblock") {
|
|
30601
|
-
|
|
30929
|
+
push2("run.status_changed", {
|
|
30602
30930
|
status: "running",
|
|
30603
30931
|
previousStatus: "blocked_uncertain",
|
|
30604
30932
|
reason: request.reason ?? "operator_retry"
|
|
@@ -30610,7 +30938,7 @@ function recoverKxmRun(context, runId, request) {
|
|
|
30610
30938
|
return { state: nextState, unblocked: true };
|
|
30611
30939
|
}
|
|
30612
30940
|
if (request.action === "fail") {
|
|
30613
|
-
|
|
30941
|
+
push2("run.status_changed", {
|
|
30614
30942
|
status: "failed",
|
|
30615
30943
|
previousStatus: "blocked_uncertain",
|
|
30616
30944
|
reason: request.reason ?? "operator_fail"
|
|
@@ -30622,16 +30950,16 @@ function recoverKxmRun(context, runId, request) {
|
|
|
30622
30950
|
return { state: nextState, unblocked: true };
|
|
30623
30951
|
}
|
|
30624
30952
|
if (request.action === "cancel") {
|
|
30625
|
-
|
|
30953
|
+
push2("run.cancel_requested", {
|
|
30626
30954
|
actor: { kind: "runtime", id: context.homeRuntimeId },
|
|
30627
30955
|
reason: request.reason ?? "operator_cancel"
|
|
30628
30956
|
});
|
|
30629
|
-
|
|
30957
|
+
push2("run.status_changed", {
|
|
30630
30958
|
status: "cancelling",
|
|
30631
30959
|
previousStatus: "blocked_uncertain",
|
|
30632
30960
|
reason: request.reason ?? "operator_cancel"
|
|
30633
30961
|
});
|
|
30634
|
-
|
|
30962
|
+
push2("run.status_changed", {
|
|
30635
30963
|
status: "cancelled",
|
|
30636
30964
|
previousStatus: "cancelling",
|
|
30637
30965
|
reason: request.reason ?? "operator_cancel"
|
|
@@ -31138,9 +31466,9 @@ var RuntimeHubClient = class {
|
|
|
31138
31466
|
};
|
|
31139
31467
|
|
|
31140
31468
|
// plugins/kxm/src/hub-binding.ts
|
|
31141
|
-
import { existsSync as
|
|
31469
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync6, readFileSync as readFileSync12, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync7 } from "node:fs";
|
|
31142
31470
|
import { homedir as homedir2 } from "node:os";
|
|
31143
|
-
import { dirname as dirname8, isAbsolute as isAbsolute6, join as
|
|
31471
|
+
import { dirname as dirname8, isAbsolute as isAbsolute6, join as join17, resolve as resolve9 } from "node:path";
|
|
31144
31472
|
var HUB_BINDING_SCHEMA = "kxm.hub-binding.v1";
|
|
31145
31473
|
var HubBindingError = class extends Error {
|
|
31146
31474
|
constructor(message) {
|
|
@@ -31156,16 +31484,16 @@ function resolveUserStateRoot(env) {
|
|
|
31156
31484
|
}
|
|
31157
31485
|
if (process.platform === "win32") {
|
|
31158
31486
|
const localAppData = env.LOCALAPPDATA?.trim();
|
|
31159
|
-
const base2 = localAppData && isAbsolute6(localAppData) ? localAppData :
|
|
31487
|
+
const base2 = localAppData && isAbsolute6(localAppData) ? localAppData : join17(homedir2(), "AppData", "Local");
|
|
31160
31488
|
return resolve9(base2, "KXM");
|
|
31161
31489
|
}
|
|
31162
31490
|
if (process.platform === "darwin") return resolve9(homedir2(), "Library", "Application Support", "KXM");
|
|
31163
31491
|
const xdgState = env.XDG_STATE_HOME?.trim();
|
|
31164
|
-
const base = xdgState && isAbsolute6(xdgState) ? xdgState :
|
|
31492
|
+
const base = xdgState && isAbsolute6(xdgState) ? xdgState : join17(homedir2(), ".local", "state");
|
|
31165
31493
|
return resolve9(base, "kxm");
|
|
31166
31494
|
}
|
|
31167
31495
|
function hubBindingFile(env = process.env) {
|
|
31168
|
-
return
|
|
31496
|
+
return join17(resolveUserStateRoot(env), "hub-binding.json");
|
|
31169
31497
|
}
|
|
31170
31498
|
function validateHubUrl(raw) {
|
|
31171
31499
|
let parsed;
|
|
@@ -31185,10 +31513,10 @@ function isIsoTimestamp(value) {
|
|
|
31185
31513
|
}
|
|
31186
31514
|
function readHubBinding(env = process.env) {
|
|
31187
31515
|
const file = hubBindingFile(env);
|
|
31188
|
-
if (!
|
|
31516
|
+
if (!existsSync14(file)) return void 0;
|
|
31189
31517
|
let parsed;
|
|
31190
31518
|
try {
|
|
31191
|
-
parsed = JSON.parse(
|
|
31519
|
+
parsed = JSON.parse(readFileSync12(file, "utf8"));
|
|
31192
31520
|
} catch {
|
|
31193
31521
|
throw new HubBindingError(`malformed hub binding at ${file}`);
|
|
31194
31522
|
}
|
|
@@ -31210,9 +31538,9 @@ function readHubBinding(env = process.env) {
|
|
|
31210
31538
|
}
|
|
31211
31539
|
|
|
31212
31540
|
// plugins/kxm/src/hub-env.ts
|
|
31213
|
-
import { existsSync as
|
|
31541
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync7, readFileSync as readFileSync13, renameSync as renameSync3, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "node:fs";
|
|
31214
31542
|
import { homedir as homedir3 } from "node:os";
|
|
31215
|
-
import { dirname as dirname9, isAbsolute as isAbsolute7, join as
|
|
31543
|
+
import { dirname as dirname9, isAbsolute as isAbsolute7, join as join18, resolve as resolve10 } from "node:path";
|
|
31216
31544
|
var HUB_ENV_SCHEMA = "kxm.hub-env.v1";
|
|
31217
31545
|
var HubEnvError = class extends Error {
|
|
31218
31546
|
constructor(message) {
|
|
@@ -31228,23 +31556,23 @@ function resolveUserStateRoot2(env) {
|
|
|
31228
31556
|
}
|
|
31229
31557
|
if (process.platform === "win32") {
|
|
31230
31558
|
const localAppData = env.LOCALAPPDATA?.trim();
|
|
31231
|
-
const base2 = localAppData && isAbsolute7(localAppData) ? localAppData :
|
|
31559
|
+
const base2 = localAppData && isAbsolute7(localAppData) ? localAppData : join18(homedir3(), "AppData", "Local");
|
|
31232
31560
|
return resolve10(base2, "KXM");
|
|
31233
31561
|
}
|
|
31234
31562
|
if (process.platform === "darwin") return resolve10(homedir3(), "Library", "Application Support", "KXM");
|
|
31235
31563
|
const xdgState = env.XDG_STATE_HOME?.trim();
|
|
31236
|
-
const base = xdgState && isAbsolute7(xdgState) ? xdgState :
|
|
31564
|
+
const base = xdgState && isAbsolute7(xdgState) ? xdgState : join18(homedir3(), ".local", "state");
|
|
31237
31565
|
return resolve10(base, "kxm");
|
|
31238
31566
|
}
|
|
31239
31567
|
function hubEnvFile(env = process.env) {
|
|
31240
|
-
return
|
|
31568
|
+
return join18(resolveUserStateRoot2(env), "hub-env.json");
|
|
31241
31569
|
}
|
|
31242
31570
|
function readHubEnvRecord(env = process.env) {
|
|
31243
31571
|
const file = hubEnvFile(env);
|
|
31244
|
-
if (!
|
|
31572
|
+
if (!existsSync15(file)) return void 0;
|
|
31245
31573
|
let parsed;
|
|
31246
31574
|
try {
|
|
31247
|
-
parsed = JSON.parse(
|
|
31575
|
+
parsed = JSON.parse(readFileSync13(file, "utf8"));
|
|
31248
31576
|
} catch (error) {
|
|
31249
31577
|
throw new HubEnvError(`hub env file is malformed at ${file}: ${error instanceof Error ? error.message : String(error)}`);
|
|
31250
31578
|
}
|
|
@@ -31284,7 +31612,7 @@ function resolveClientHubAuthToken(env, project) {
|
|
|
31284
31612
|
}
|
|
31285
31613
|
|
|
31286
31614
|
// plugins/kxm/src/logger.ts
|
|
31287
|
-
import { appendFileSync, existsSync as
|
|
31615
|
+
import { appendFileSync, existsSync as existsSync16, mkdirSync as mkdirSync8, renameSync as renameSync4, statSync as statSync5, unlinkSync as unlinkSync2 } from "node:fs";
|
|
31288
31616
|
import { dirname as dirname10 } from "node:path";
|
|
31289
31617
|
var LOG_LEVEL_PRIORITY = {
|
|
31290
31618
|
debug: 10,
|
|
@@ -31322,7 +31650,7 @@ function redactLogValue(val, key) {
|
|
|
31322
31650
|
function rotateLogFiles(filePath, maxFiles) {
|
|
31323
31651
|
for (let i = maxFiles; i >= 1; i--) {
|
|
31324
31652
|
const current = `${filePath}.${i}`;
|
|
31325
|
-
if (
|
|
31653
|
+
if (existsSync16(current)) {
|
|
31326
31654
|
if (i >= maxFiles) {
|
|
31327
31655
|
try {
|
|
31328
31656
|
unlinkSync2(current);
|
|
@@ -31336,7 +31664,7 @@ function rotateLogFiles(filePath, maxFiles) {
|
|
|
31336
31664
|
}
|
|
31337
31665
|
}
|
|
31338
31666
|
}
|
|
31339
|
-
if (
|
|
31667
|
+
if (existsSync16(filePath)) {
|
|
31340
31668
|
try {
|
|
31341
31669
|
renameSync4(filePath, `${filePath}.1`);
|
|
31342
31670
|
} catch {
|
|
@@ -31353,7 +31681,7 @@ function createLogger(options) {
|
|
|
31353
31681
|
const shouldStdout = options.stdout ?? !isDaemon;
|
|
31354
31682
|
const correlationDefaults = options.correlation ?? {};
|
|
31355
31683
|
let currentSize = 0;
|
|
31356
|
-
if (filePath &&
|
|
31684
|
+
if (filePath && existsSync16(filePath)) {
|
|
31357
31685
|
try {
|
|
31358
31686
|
currentSize = statSync5(filePath).size;
|
|
31359
31687
|
} catch {
|
|
@@ -31434,7 +31762,7 @@ function createLogger(options) {
|
|
|
31434
31762
|
|
|
31435
31763
|
// plugins/kxm/src/runtime-supervisor.ts
|
|
31436
31764
|
function kxmSupervisorTokenFile(paths) {
|
|
31437
|
-
return
|
|
31765
|
+
return join19(paths.runtimeDir, "supervisor.token");
|
|
31438
31766
|
}
|
|
31439
31767
|
function publishKxmSupervisorToken(paths, token) {
|
|
31440
31768
|
const file = kxmSupervisorTokenFile(paths);
|
|
@@ -31462,7 +31790,7 @@ function readKxmSupervisorToken(paths) {
|
|
|
31462
31790
|
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
31463
31791
|
throw runtimeError("runtime_path_invalid", file, "supervisor token file must be a regular file, not a link");
|
|
31464
31792
|
}
|
|
31465
|
-
const token =
|
|
31793
|
+
const token = readFileSync14(file, "utf8").trim();
|
|
31466
31794
|
return token.length >= 32 ? token : void 0;
|
|
31467
31795
|
}
|
|
31468
31796
|
function processAlive(pid) {
|
|
@@ -31476,11 +31804,11 @@ function processAlive(pid) {
|
|
|
31476
31804
|
var HEARTBEAT_STALE_MS = 15e3;
|
|
31477
31805
|
var SUPERVISOR_ERROR_MAX_AGE_MS = 3e4;
|
|
31478
31806
|
function supervisorErrorFile(paths) {
|
|
31479
|
-
return
|
|
31807
|
+
return join19(paths.runtimeDir, "supervisor.error");
|
|
31480
31808
|
}
|
|
31481
31809
|
function clearSupervisorError(paths) {
|
|
31482
31810
|
const file = supervisorErrorFile(paths);
|
|
31483
|
-
if (
|
|
31811
|
+
if (existsSync17(file)) rmSync4(file, { force: true });
|
|
31484
31812
|
}
|
|
31485
31813
|
function recordSupervisorError(paths, message) {
|
|
31486
31814
|
try {
|
|
@@ -31497,7 +31825,7 @@ function readRecentSupervisorError(paths) {
|
|
|
31497
31825
|
const ageMs = Date.now() - stat.mtimeMs;
|
|
31498
31826
|
if (ageMs > SUPERVISOR_ERROR_MAX_AGE_MS) return void 0;
|
|
31499
31827
|
try {
|
|
31500
|
-
return
|
|
31828
|
+
return readFileSync14(file, "utf8").trim();
|
|
31501
31829
|
} catch {
|
|
31502
31830
|
return void 0;
|
|
31503
31831
|
}
|
|
@@ -31536,7 +31864,7 @@ function supervisorStatusOf(record2) {
|
|
|
31536
31864
|
};
|
|
31537
31865
|
}
|
|
31538
31866
|
function kxmSupervisorStatus(paths, options = {}) {
|
|
31539
|
-
if (!
|
|
31867
|
+
if (!existsSync17(paths.registryDb)) return { running: false };
|
|
31540
31868
|
if (options.readOnly) {
|
|
31541
31869
|
const database = openReadOnlyDatabase(paths.registryDb);
|
|
31542
31870
|
try {
|
|
@@ -31602,7 +31930,7 @@ async function ensureKxmSupervisor(options = {}) {
|
|
|
31602
31930
|
throw runtimeError("runtime_supervisor_unreachable", paths.registryDb, `runtime supervisor pid ${status.pid} is registered as running but cannot be probed`);
|
|
31603
31931
|
}
|
|
31604
31932
|
clearSupervisorError(paths);
|
|
31605
|
-
const scriptPath =
|
|
31933
|
+
const scriptPath = join19(findKxmRepoRoot(import.meta.url), "scripts", "kxm-runtime-supervisor.mjs");
|
|
31606
31934
|
const spawnImpl = options.spawnImpl ?? ((script, env) => {
|
|
31607
31935
|
const child = spawn3(process.execPath, [script], {
|
|
31608
31936
|
detached: true,
|
|
@@ -31825,7 +32153,7 @@ function syncFailureText(error) {
|
|
|
31825
32153
|
return `${code ? `${code}: ` : ""}${message}`.replace(/(kxm_[A-Za-z0-9_]+|[A-Za-z0-9._-]{40,})/g, "[redacted]").slice(0, 300);
|
|
31826
32154
|
}
|
|
31827
32155
|
function runtimeSupervisorLogFile(paths) {
|
|
31828
|
-
return
|
|
32156
|
+
return join19(paths.runtimeDir, "logs", "kxm-runtime.jsonl");
|
|
31829
32157
|
}
|
|
31830
32158
|
async function startKxmRuntimeSupervisor(options = {}) {
|
|
31831
32159
|
const now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
@@ -31950,8 +32278,8 @@ async function startKxmRuntimeSupervisorInner(paths, requestedPortOption, now, e
|
|
|
31950
32278
|
openRunIds = unsettledRunIds(existing2);
|
|
31951
32279
|
} else {
|
|
31952
32280
|
const registration = registry.projectByRoot(projectRoot);
|
|
31953
|
-
const eventsPath =
|
|
31954
|
-
if (registration &&
|
|
32281
|
+
const eventsPath = join19(paths.projectsDir, registration?.projectKey ?? projectRuntimeKey(projectRoot), "run-events.db");
|
|
32282
|
+
if (registration && existsSync17(eventsPath)) {
|
|
31955
32283
|
const store = new KxmRunEventStore(eventsPath);
|
|
31956
32284
|
try {
|
|
31957
32285
|
openRunIds = store.runsForProject(registration.projectId, 1e4).filter((run) => !SETTLED_RUN_STATUS.has(run.status)).map((run) => run.runId);
|
|
@@ -32467,7 +32795,7 @@ function handoffSuffix(handoff) {
|
|
|
32467
32795
|
|
|
32468
32796
|
// plugins/kxm/src/pi-producer.ts
|
|
32469
32797
|
import { spawn as spawn4 } from "node:child_process";
|
|
32470
|
-
import { join as
|
|
32798
|
+
import { join as join20 } from "node:path";
|
|
32471
32799
|
import { StringDecoder } from "node:string_decoder";
|
|
32472
32800
|
function formatPiSessionKey(params) {
|
|
32473
32801
|
if (params.agentId === "coordinator") {
|
|
@@ -32841,7 +33169,7 @@ function createKxmPiProducer(options = {}) {
|
|
|
32841
33169
|
const args = ["--mode", "rpc", "--name", displayName];
|
|
32842
33170
|
if (options.sessionDir) {
|
|
32843
33171
|
const safeDirName = key.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
32844
|
-
args.push("--session-dir",
|
|
33172
|
+
args.push("--session-dir", join20(options.sessionDir, request.runId, safeDirName));
|
|
32845
33173
|
}
|
|
32846
33174
|
if (model) {
|
|
32847
33175
|
args.push("--model", model);
|
|
@@ -32970,8 +33298,8 @@ function createKxmPiProducer(options = {}) {
|
|
|
32970
33298
|
|
|
32971
33299
|
// plugins/kxm/src/improve.ts
|
|
32972
33300
|
import { createHash as createHash17 } from "node:crypto";
|
|
32973
|
-
import { existsSync as
|
|
32974
|
-
import { join as
|
|
33301
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync10, writeFileSync as writeFileSync10 } from "node:fs";
|
|
33302
|
+
import { join as join21, relative as relative5, resolve as resolve11 } from "node:path";
|
|
32975
33303
|
var CANDIDATE_SCHEMA = "kxm.candidate.v1";
|
|
32976
33304
|
var IMPROVEMENT_REPORT_SCHEMA = "kxm.improvement-report.v2";
|
|
32977
33305
|
var IMPROVEMENT_REPORT_V1_SCHEMA = "kxm.improvement-report.v1";
|
|
@@ -33228,7 +33556,7 @@ function groupRoutingRecords(records, options = {}) {
|
|
|
33228
33556
|
}
|
|
33229
33557
|
function buildImprovementReport(records, options = {}) {
|
|
33230
33558
|
const projectRoot = options.projectRoot ? resolve11(options.projectRoot) : process.cwd();
|
|
33231
|
-
const candidatesDir = options.candidatesDir ? resolve11(options.candidatesDir) :
|
|
33559
|
+
const candidatesDir = options.candidatesDir ? resolve11(options.candidatesDir) : join21(projectRoot, ".kxm", "candidates");
|
|
33232
33560
|
const promotionPolicy = options.promotionPolicy ?? "manual_pr";
|
|
33233
33561
|
const groups = groupRoutingRecords(records, options);
|
|
33234
33562
|
const candidates = [];
|
|
@@ -33242,7 +33570,7 @@ function buildImprovementReport(records, options = {}) {
|
|
|
33242
33570
|
group.workflowId
|
|
33243
33571
|
);
|
|
33244
33572
|
const diffFileName = `${group.candidateId}.diff`;
|
|
33245
|
-
const diffFilePath =
|
|
33573
|
+
const diffFilePath = join21(candidatesDir, diffFileName);
|
|
33246
33574
|
const relDiffPath = relative5(projectRoot, diffFilePath).replace(/\\/g, "/");
|
|
33247
33575
|
const candidate = {
|
|
33248
33576
|
schema: CANDIDATE_SCHEMA,
|
|
@@ -33264,11 +33592,11 @@ function buildImprovementReport(records, options = {}) {
|
|
|
33264
33592
|
createdAt: nowIso()
|
|
33265
33593
|
};
|
|
33266
33594
|
if (!options.dryRun) {
|
|
33267
|
-
if (!
|
|
33595
|
+
if (!existsSync18(candidatesDir)) {
|
|
33268
33596
|
mkdirSync10(candidatesDir, { recursive: true });
|
|
33269
33597
|
}
|
|
33270
33598
|
writeFileSync10(diffFilePath, diff, "utf8");
|
|
33271
|
-
const jsonFilePath =
|
|
33599
|
+
const jsonFilePath = join21(candidatesDir, `${group.candidateId}.json`);
|
|
33272
33600
|
writeFileSync10(jsonFilePath, JSON.stringify(candidate, null, 2) + "\n", "utf8");
|
|
33273
33601
|
}
|
|
33274
33602
|
candidates.push(candidate);
|
|
@@ -33297,7 +33625,7 @@ function buildImprovementReport(records, options = {}) {
|
|
|
33297
33625
|
}
|
|
33298
33626
|
function writeImprovementReport(improvementsDir, report, dryRun = false) {
|
|
33299
33627
|
const stamp = report.createdAt.replace(/[:.]/g, "-");
|
|
33300
|
-
const path =
|
|
33628
|
+
const path = join21(improvementsDir, `${stamp}.json`);
|
|
33301
33629
|
if (!dryRun) {
|
|
33302
33630
|
mkdirSync10(improvementsDir, { recursive: true });
|
|
33303
33631
|
writeFileSync10(path, `${JSON.stringify(report, null, 2)}
|
|
@@ -33958,9 +34286,9 @@ Instructions for Operator:
|
|
|
33958
34286
|
};
|
|
33959
34287
|
|
|
33960
34288
|
// plugins/kxm/src/modes.ts
|
|
33961
|
-
var
|
|
33962
|
-
import { existsSync as
|
|
33963
|
-
import { join as
|
|
34289
|
+
var import_yaml11 = __toESM(require_dist(), 1);
|
|
34290
|
+
import { existsSync as existsSync19, readFileSync as readFileSync16 } from "node:fs";
|
|
34291
|
+
import { join as join22 } from "node:path";
|
|
33964
34292
|
var DEFAULT_MODES_CONFIG = Object.freeze({
|
|
33965
34293
|
schema: "kxm.modes.v1",
|
|
33966
34294
|
majorModes: {
|
|
@@ -34023,13 +34351,13 @@ function loadModesConfig(projectRoot) {
|
|
|
34023
34351
|
if (!projectRoot) {
|
|
34024
34352
|
return DEFAULT_MODES_CONFIG;
|
|
34025
34353
|
}
|
|
34026
|
-
const modesPath =
|
|
34027
|
-
if (!
|
|
34354
|
+
const modesPath = join22(projectRoot, ".kxm", "modes.yaml");
|
|
34355
|
+
if (!existsSync19(modesPath)) {
|
|
34028
34356
|
return DEFAULT_MODES_CONFIG;
|
|
34029
34357
|
}
|
|
34030
34358
|
try {
|
|
34031
|
-
const raw =
|
|
34032
|
-
const parsed = (0,
|
|
34359
|
+
const raw = readFileSync16(modesPath, "utf8");
|
|
34360
|
+
const parsed = (0, import_yaml11.parse)(raw);
|
|
34033
34361
|
if (parsed && parsed.schema === "kxm.modes.v1" && parsed.majorModes) {
|
|
34034
34362
|
return {
|
|
34035
34363
|
schema: "kxm.modes.v1",
|
|
@@ -34114,11 +34442,11 @@ function calculatePromptFootprint(resolved, projectRoot = process.cwd(), catalog
|
|
|
34114
34442
|
estimatedTokens: estimateTokens(baseSystemPromptChars)
|
|
34115
34443
|
});
|
|
34116
34444
|
for (const relPath of resolved.contextFiles) {
|
|
34117
|
-
const fullPath =
|
|
34445
|
+
const fullPath = join22(projectRoot, relPath);
|
|
34118
34446
|
let chars = 0;
|
|
34119
|
-
if (
|
|
34447
|
+
if (existsSync19(fullPath)) {
|
|
34120
34448
|
try {
|
|
34121
|
-
chars =
|
|
34449
|
+
chars = readFileSync16(fullPath, "utf8").length;
|
|
34122
34450
|
} catch {
|
|
34123
34451
|
chars = 0;
|
|
34124
34452
|
}
|
|
@@ -34250,9 +34578,9 @@ ${divider}
|
|
|
34250
34578
|
|
|
34251
34579
|
// plugins/kxm/src/ssh-remote.ts
|
|
34252
34580
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
34253
|
-
import { existsSync as
|
|
34581
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync11, readFileSync as readFileSync17, readdirSync as readdirSync10, rmSync as rmSync5, statSync as statSync6 } from "node:fs";
|
|
34254
34582
|
import { homedir as homedir4 } from "node:os";
|
|
34255
|
-
import { join as
|
|
34583
|
+
import { join as join23, resolve as resolve12 } from "node:path";
|
|
34256
34584
|
var MAX_SSH_OUTPUT_BYTES = 50 * 1024;
|
|
34257
34585
|
var MAX_SSH_OUTPUT_LINES = 2e3;
|
|
34258
34586
|
var DEFAULT_SOCKET_DIR = ".kxm/run/ssh-sockets";
|
|
@@ -34278,12 +34606,12 @@ function truncateSshOutput(raw) {
|
|
|
34278
34606
|
return { text, truncated };
|
|
34279
34607
|
}
|
|
34280
34608
|
function parseSshConfig(configPath) {
|
|
34281
|
-
const targetPath = configPath ??
|
|
34282
|
-
if (!
|
|
34609
|
+
const targetPath = configPath ?? join23(homedir4(), ".ssh", "config");
|
|
34610
|
+
if (!existsSync20(targetPath)) {
|
|
34283
34611
|
return [];
|
|
34284
34612
|
}
|
|
34285
34613
|
try {
|
|
34286
|
-
const content =
|
|
34614
|
+
const content = readFileSync17(targetPath, "utf-8");
|
|
34287
34615
|
const lines = content.split("\n");
|
|
34288
34616
|
const hosts = [];
|
|
34289
34617
|
let currentHosts = [];
|
|
@@ -34361,14 +34689,14 @@ function resolveSshHostG(host, execFn = spawnSync6) {
|
|
|
34361
34689
|
}
|
|
34362
34690
|
function ensureSocketDir(socketDir = DEFAULT_SOCKET_DIR) {
|
|
34363
34691
|
const resolved = resolve12(socketDir);
|
|
34364
|
-
if (!
|
|
34692
|
+
if (!existsSync20(resolved)) {
|
|
34365
34693
|
mkdirSync11(resolved, { recursive: true, mode: 448 });
|
|
34366
34694
|
}
|
|
34367
34695
|
return resolved;
|
|
34368
34696
|
}
|
|
34369
34697
|
function buildSshArgs(options) {
|
|
34370
34698
|
const socketDir = ensureSocketDir(options.socketDir ?? DEFAULT_SOCKET_DIR);
|
|
34371
|
-
const controlPath =
|
|
34699
|
+
const controlPath = join23(socketDir, "%C");
|
|
34372
34700
|
const persist2 = options.controlPersist ?? DEFAULT_CONTROL_PERSIST;
|
|
34373
34701
|
const args = [
|
|
34374
34702
|
"-o",
|
|
@@ -34393,7 +34721,7 @@ function buildSshArgs(options) {
|
|
|
34393
34721
|
}
|
|
34394
34722
|
function checkControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn = spawnSync6) {
|
|
34395
34723
|
const resolvedDir = ensureSocketDir(socketDir);
|
|
34396
|
-
const controlPath =
|
|
34724
|
+
const controlPath = join23(resolvedDir, "%C");
|
|
34397
34725
|
try {
|
|
34398
34726
|
const result = execFn("ssh", ["-O", "check", "-o", `ControlPath=${controlPath}`, host], {
|
|
34399
34727
|
encoding: "utf-8"
|
|
@@ -34405,7 +34733,7 @@ function checkControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn = spawn
|
|
|
34405
34733
|
}
|
|
34406
34734
|
function closeControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn = spawnSync6) {
|
|
34407
34735
|
const resolvedDir = ensureSocketDir(socketDir);
|
|
34408
|
-
const controlPath =
|
|
34736
|
+
const controlPath = join23(resolvedDir, "%C");
|
|
34409
34737
|
try {
|
|
34410
34738
|
const result = execFn("ssh", ["-O", "stop", "-o", `ControlPath=${controlPath}`, host], {
|
|
34411
34739
|
encoding: "utf-8"
|
|
@@ -34419,9 +34747,9 @@ function pruneSocketDir(socketDir = DEFAULT_SOCKET_DIR) {
|
|
|
34419
34747
|
const resolvedDir = ensureSocketDir(socketDir);
|
|
34420
34748
|
let removed = 0;
|
|
34421
34749
|
try {
|
|
34422
|
-
const entries =
|
|
34750
|
+
const entries = readdirSync10(resolvedDir);
|
|
34423
34751
|
for (const entry of entries) {
|
|
34424
|
-
const fullPath =
|
|
34752
|
+
const fullPath = join23(resolvedDir, entry);
|
|
34425
34753
|
try {
|
|
34426
34754
|
const stat = statSync6(fullPath);
|
|
34427
34755
|
if (stat.isSocket()) {
|