@kontextmind/kxm 0.7.150 → 0.7.151
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/CHANGELOG.md +14 -0
- package/docs/architecture/agent-path.md +5 -0
- package/docs/operations/troubleshooting.md +1 -0
- package/docs/reference/cli-reference.md +4 -4
- package/docs/reference/config-reference.md +17 -4
- package/docs/reference/harness-routing.md +7 -1
- package/package.json +1 -1
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/cli.js +153 -60
- package/plugins/kxm/dist/core.js +22 -3
- package/plugins/kxm/dist/mcp-server.js +1 -1
- package/plugins/kxm/dist/runtime-supervisor.js +676 -130
- package/plugins/kxm/dist/runtime.js +720 -164
- package/plugins/kxm/dist/server.js +1 -1
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/src/cli/system.ts +4 -1
- package/plugins/kxm/src/engine-fold.ts +1 -0
- package/plugins/kxm/src/engine.ts +264 -22
- package/plugins/kxm/src/improve-sources.ts +17 -3
- package/plugins/kxm/src/improve.ts +14 -0
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/policy-draft.mjs +2 -2
- package/plugins/kxm/src/role.ts +1 -1
- package/plugins/kxm/src/route-fallback.ts +504 -0
- package/plugins/kxm/src/route-switch.ts +49 -0
- package/plugins/kxm/src/routing.ts +24 -1
- package/schemas/common.schema.json +2 -1
- package/schemas/role.schema.json +1 -1
- package/schemas/run-event.schema.json +20 -1
|
@@ -3835,7 +3835,7 @@ var require_fast_uri = __commonJS({
|
|
|
3835
3835
|
normalizeString(uri, options);
|
|
3836
3836
|
} else if (typeof uri === "object") {
|
|
3837
3837
|
uri = /** @type {T} */
|
|
3838
|
-
|
|
3838
|
+
parse10(serialize(uri, options), options);
|
|
3839
3839
|
}
|
|
3840
3840
|
return uri;
|
|
3841
3841
|
}
|
|
@@ -3875,8 +3875,8 @@ var require_fast_uri = __commonJS({
|
|
|
3875
3875
|
function resolveComponent(base, relative5, options, skipNormalization) {
|
|
3876
3876
|
const target = {};
|
|
3877
3877
|
if (!skipNormalization) {
|
|
3878
|
-
base =
|
|
3879
|
-
relative5 =
|
|
3878
|
+
base = parse10(serialize(base, options), options);
|
|
3879
|
+
relative5 = parse10(serialize(relative5, options), options);
|
|
3880
3880
|
}
|
|
3881
3881
|
options = options || {};
|
|
3882
3882
|
if (!options.tolerant && relative5.scheme) {
|
|
@@ -4168,7 +4168,7 @@ var require_fast_uri = __commonJS({
|
|
|
4168
4168
|
}
|
|
4169
4169
|
return { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme };
|
|
4170
4170
|
}
|
|
4171
|
-
function
|
|
4171
|
+
function parse10(uri, opts) {
|
|
4172
4172
|
return parseWithStatus(uri, opts).parsed;
|
|
4173
4173
|
}
|
|
4174
4174
|
function normalizeString(uri, opts) {
|
|
@@ -4205,7 +4205,7 @@ var require_fast_uri = __commonJS({
|
|
|
4205
4205
|
resolveComponent,
|
|
4206
4206
|
equal,
|
|
4207
4207
|
serialize,
|
|
4208
|
-
parse:
|
|
4208
|
+
parse: parse10
|
|
4209
4209
|
};
|
|
4210
4210
|
module.exports = fastUri;
|
|
4211
4211
|
module.exports.default = fastUri;
|
|
@@ -14678,7 +14678,7 @@ var require_public_api = __commonJS({
|
|
|
14678
14678
|
}
|
|
14679
14679
|
return doc;
|
|
14680
14680
|
}
|
|
14681
|
-
function
|
|
14681
|
+
function parse10(src, reviver, options) {
|
|
14682
14682
|
let _reviver = void 0;
|
|
14683
14683
|
if (typeof reviver === "function") {
|
|
14684
14684
|
_reviver = reviver;
|
|
@@ -14719,7 +14719,7 @@ var require_public_api = __commonJS({
|
|
|
14719
14719
|
return value.toString(options);
|
|
14720
14720
|
return new Document.Document(value, _replacer, options).toString(options);
|
|
14721
14721
|
}
|
|
14722
|
-
exports.parse =
|
|
14722
|
+
exports.parse = parse10;
|
|
14723
14723
|
exports.parseAllDocuments = parseAllDocuments;
|
|
14724
14724
|
exports.parseDocument = parseDocument3;
|
|
14725
14725
|
exports.stringify = stringify7;
|
|
@@ -14781,9 +14781,9 @@ var require_dist = __commonJS({
|
|
|
14781
14781
|
// plugins/kxm/src/runtime-supervisor.ts
|
|
14782
14782
|
import { spawn as spawn3 } from "node:child_process";
|
|
14783
14783
|
import { createHash as createHash15, createHmac, randomBytes as randomBytes2, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
14784
|
-
import { chmodSync as chmodSync2, existsSync as
|
|
14784
|
+
import { chmodSync as chmodSync2, existsSync as existsSync18, lstatSync as lstatSync6, mkdirSync as mkdirSync9, readFileSync as readFileSync15, renameSync as renameSync5, rmSync as rmSync4, writeFileSync as writeFileSync9 } from "node:fs";
|
|
14785
14785
|
import { createServer } from "node:http";
|
|
14786
|
-
import { dirname as dirname11, isAbsolute as isAbsolute8, join as
|
|
14786
|
+
import { dirname as dirname11, isAbsolute as isAbsolute8, join as join20 } from "node:path";
|
|
14787
14787
|
|
|
14788
14788
|
// plugins/kxm/src/repo-root.ts
|
|
14789
14789
|
import { existsSync } from "node:fs";
|
|
@@ -15000,7 +15000,7 @@ var TOOL_KEYS = Object.freeze(["preset", "allow", "deny"]);
|
|
|
15000
15000
|
var TEMPLATE_KEYS = Object.freeze(["template", "schema"]);
|
|
15001
15001
|
var POLICY_KEYS = Object.freeze(["vendorIndependenceRequired", "maxTransitions", "requiresGateVerification", "fallback"]);
|
|
15002
15002
|
var FALLBACK_KEYS = Object.freeze(["onError", "maxSwitches", "revert"]);
|
|
15003
|
-
var FALLBACK_ERRORS = Object.freeze(["rate_limit", "transport", "provider_unavailable"]);
|
|
15003
|
+
var FALLBACK_ERRORS = Object.freeze(["rate_limit", "transport", "provider_unavailable", "context_overflow"]);
|
|
15004
15004
|
var FALLBACK_REVERT = Object.freeze(["next_run", "never"]);
|
|
15005
15005
|
var ROSTER_ENTRY_KEYS = Object.freeze(["route", "effort", "mode"]);
|
|
15006
15006
|
var CRITIC_PURPOSES = Object.freeze(["reviewer-arch", "reviewer-cli"]);
|
|
@@ -15329,7 +15329,7 @@ function validateRoleShape(document, id, label, issues) {
|
|
|
15329
15329
|
if (!closedObject(fallback, FALLBACK_KEYS, path, issues)) return;
|
|
15330
15330
|
if (fallback.onError !== void 0) {
|
|
15331
15331
|
if (!Array.isArray(fallback.onError) || new Set(fallback.onError).size !== fallback.onError.length || fallback.onError.some((item) => !FALLBACK_ERRORS.includes(item))) {
|
|
15332
|
-
issues.push(issue2("schema", "schema_enum", path, "onError must be unique rate_limit, transport, or
|
|
15332
|
+
issues.push(issue2("schema", "schema_enum", path, "onError must be unique rate_limit, transport, provider_unavailable, or context_overflow values"));
|
|
15333
15333
|
}
|
|
15334
15334
|
}
|
|
15335
15335
|
if (fallback.maxSwitches !== void 0 && (!Number.isInteger(fallback.maxSwitches) || fallback.maxSwitches < 0)) {
|
|
@@ -17969,8 +17969,8 @@ function validateWorkflow(workflow, agents, models, repositories, gates, issues)
|
|
|
17969
17969
|
const writable = Object.values(objectValue(step.repositories) ?? {}).filter((access) => access === "write").length;
|
|
17970
17970
|
if (maxWriteRepositories > writable) issues.push(issue3("semantic", "write_repository_bound_invalid", file, `${stepId} maxWriteRepositories exceeds writable repository scope`));
|
|
17971
17971
|
}
|
|
17972
|
-
const
|
|
17973
|
-
const minimumPassed =
|
|
17972
|
+
const join21 = objectValue(step.join);
|
|
17973
|
+
const minimumPassed = join21 && typeof join21.minimumPassed === "number" ? join21.minimumPassed : void 0;
|
|
17974
17974
|
if (minimumPassed !== void 0 && minimumPassed > maximum) issues.push(issue3("semantic", "join_impossible", file, `${stepId} minimumPassed exceeds assignment maximum`));
|
|
17975
17975
|
const distinctBy = names(assignment?.distinctBy);
|
|
17976
17976
|
if (distinctBy.length > 0) {
|
|
@@ -17991,10 +17991,10 @@ function validateWorkflow(workflow, agents, models, repositories, gates, issues)
|
|
|
17991
17991
|
}
|
|
17992
17992
|
const policy = objectValue(requirement.producerPolicy);
|
|
17993
17993
|
if (!policy) continue;
|
|
17994
|
-
const
|
|
17994
|
+
const eligible2 = names(policy.eligibleAgents);
|
|
17995
17995
|
const producerMinimum = numberValue(policy.minimumProducers, 1);
|
|
17996
|
-
if (producerMinimum > target || producerMinimum >
|
|
17997
|
-
for (const agentId of
|
|
17996
|
+
if (producerMinimum > target || producerMinimum > eligible2.length) issues.push(issue3("semantic", "producer_minimum_impossible", file, `${stepId}.${String(key)} producer minimum exceeds its eligible or target pool`));
|
|
17997
|
+
for (const agentId of eligible2) {
|
|
17998
17998
|
if (!lookupKxmResource(agents.values(), agentId, "agent")) issues.push(issue3("reference", "producer_agent_unknown", file, `${stepId}.${String(key)} references unknown producer ${agentId}`));
|
|
17999
17999
|
if (allowedAgents.length > 0 && !allowedAgents.includes(agentId)) issues.push(issue3("semantic", "producer_agent_ineligible", file, `${stepId}.${String(key)} producer ${agentId} is outside allowedAgents`));
|
|
18000
18000
|
}
|
|
@@ -21530,6 +21530,7 @@ function foldKxmRunState(run, plan, events, options = {}) {
|
|
|
21530
21530
|
foldEffectUncertain(state, plan, event);
|
|
21531
21531
|
break;
|
|
21532
21532
|
case "routing.attempt.recorded":
|
|
21533
|
+
case "routing.route_switched":
|
|
21533
21534
|
break;
|
|
21534
21535
|
default:
|
|
21535
21536
|
throw runtimeError("run_events_illegal", run.runId, `event type ${event.eventType} is not legal in this engine slice`);
|
|
@@ -22549,7 +22550,7 @@ function compileStep(step, index, stepIndex, requirePlanHash, sink) {
|
|
|
22549
22550
|
const maxAttempts = compileCountField(step.maxAttempts, 1, `${id}.maxAttempts`, id, sink);
|
|
22550
22551
|
const timeoutMs = compileOptionalDuration(step.timeoutMs, `${id}.timeoutMs`, id, sink);
|
|
22551
22552
|
const assignments = compileAssignments(step, id, agent, sink);
|
|
22552
|
-
const
|
|
22553
|
+
const join21 = compileJoin(step, id, sink);
|
|
22553
22554
|
const requiredEvidence = compileEvidence(step, id, sink);
|
|
22554
22555
|
const transitions = compileTransitions(step, id, index, stepIndex, sink);
|
|
22555
22556
|
const outcomes = Object.keys(transitions).sort(compareCodeUnits5);
|
|
@@ -22577,7 +22578,7 @@ function compileStep(step, index, stepIndex, requirePlanHash, sink) {
|
|
|
22577
22578
|
transitions: orderedTransitions,
|
|
22578
22579
|
requiresPlanHash: requirePlanHash.includes(id),
|
|
22579
22580
|
assignments,
|
|
22580
|
-
join:
|
|
22581
|
+
join: join21
|
|
22581
22582
|
};
|
|
22582
22583
|
if (kind === "agent" || kind === "moa") {
|
|
22583
22584
|
if (!agent) return void 0;
|
|
@@ -22627,15 +22628,15 @@ function compileAssignments(step, stepId, primaryAgentId, sink) {
|
|
|
22627
22628
|
};
|
|
22628
22629
|
}
|
|
22629
22630
|
function compileJoin(step, stepId, sink) {
|
|
22630
|
-
const
|
|
22631
|
-
if (!
|
|
22632
|
-
const declared = stringValue2(
|
|
22631
|
+
const join21 = objectValue2(step.join);
|
|
22632
|
+
if (!join21) return { strategy: "all" };
|
|
22633
|
+
const declared = stringValue2(join21.strategy);
|
|
22633
22634
|
const strategy = declared && JOIN_STRATEGIES.has(declared) ? declared : "all";
|
|
22634
|
-
const minimumPassed = compileOptionalCount(
|
|
22635
|
+
const minimumPassed = compileOptionalCount(join21.minimumPassed, `${stepId}.join.minimumPassed`, stepId, sink);
|
|
22635
22636
|
const compiled = {
|
|
22636
22637
|
strategy,
|
|
22637
22638
|
...minimumPassed !== void 0 ? { minimumPassed } : {},
|
|
22638
|
-
...typeof
|
|
22639
|
+
...typeof join21.cancelRemaining === "boolean" ? { cancelRemaining: join21.cancelRemaining } : {}
|
|
22639
22640
|
};
|
|
22640
22641
|
return compiled;
|
|
22641
22642
|
}
|
|
@@ -24784,10 +24785,10 @@ function loadPriceCatalog(rootOrPath) {
|
|
|
24784
24785
|
}
|
|
24785
24786
|
|
|
24786
24787
|
// plugins/kxm/src/engine.ts
|
|
24787
|
-
var
|
|
24788
|
+
var import_yaml11 = __toESM(require_dist(), 1);
|
|
24788
24789
|
import { createHash as createHash14, randomBytes, timingSafeEqual } from "node:crypto";
|
|
24789
|
-
import { existsSync as
|
|
24790
|
-
import { join as
|
|
24790
|
+
import { existsSync as existsSync14, readFileSync as readFileSync12 } from "node:fs";
|
|
24791
|
+
import { join as join17 } from "node:path";
|
|
24791
24792
|
|
|
24792
24793
|
// plugins/kxm/src/routes.ts
|
|
24793
24794
|
var import_yaml7 = __toESM(require_dist(), 1);
|
|
@@ -24823,6 +24824,363 @@ function listRoleBindings(root) {
|
|
|
24823
24824
|
return result;
|
|
24824
24825
|
}
|
|
24825
24826
|
|
|
24827
|
+
// plugins/kxm/src/route-fallback.ts
|
|
24828
|
+
var import_yaml8 = __toESM(require_dist(), 1);
|
|
24829
|
+
import { existsSync as existsSync11, readdirSync as readdirSync7, readFileSync as readFileSync9 } from "node:fs";
|
|
24830
|
+
import { join as join12 } from "node:path";
|
|
24831
|
+
|
|
24832
|
+
// plugins/kxm/src/route-switch.ts
|
|
24833
|
+
var WALKABLE_CLASSES = ["rate_limit", "transport", "provider_unavailable", "context_overflow"];
|
|
24834
|
+
var EFFORTS2 = /* @__PURE__ */ new Set(["off", "minimal", "low", "medium", "high", "xhigh", "max"]);
|
|
24835
|
+
|
|
24836
|
+
// plugins/kxm/src/route-fallback.ts
|
|
24837
|
+
var CARRY_ROLES = /* @__PURE__ */ new Set(["user", "assistant", "tool"]);
|
|
24838
|
+
var MAX_CARRY_MESSAGES = 8;
|
|
24839
|
+
var MAX_CARRY_TEXT = 2e3;
|
|
24840
|
+
var MAX_CARRY_TOTAL = 8e3;
|
|
24841
|
+
var DEFAULT_MAX_SWITCHES = 1;
|
|
24842
|
+
function classifyRouteFailure(input) {
|
|
24843
|
+
const code = (input.code ?? "").toLowerCase();
|
|
24844
|
+
const text = `${code} ${input.text ?? ""}`.toLowerCase();
|
|
24845
|
+
const name = (input.name ?? "").toLowerCase();
|
|
24846
|
+
const status = input.httpStatus;
|
|
24847
|
+
const result = (failureClass) => ({
|
|
24848
|
+
class: failureClass,
|
|
24849
|
+
walkable: WALKABLE_CLASSES.includes(failureClass)
|
|
24850
|
+
});
|
|
24851
|
+
if (name === "aborterror" || /\b(?:user_cancelled|cancelled|cancellation)\b/.test(text) || code === "process_aborted" || text.includes("abort")) {
|
|
24852
|
+
return result("cancelled");
|
|
24853
|
+
}
|
|
24854
|
+
if (/policy_refusal|content_policy|tool_policy|usage policy|safety/.test(text) || text.includes("policy refusal")) {
|
|
24855
|
+
return result("policy_refusal");
|
|
24856
|
+
}
|
|
24857
|
+
if (code.includes("not_authenticated") || /unauthori[sz]ed|authentication|invalid api key|auth_failed|\bauth\b/.test(text) || status === 401 || status === 403) {
|
|
24858
|
+
return result("auth");
|
|
24859
|
+
}
|
|
24860
|
+
if (/permission_profile_unaudited|writer_profile_unaudited|tool_policy/.test(text)) {
|
|
24861
|
+
return result("tool_policy");
|
|
24862
|
+
}
|
|
24863
|
+
if (/harness_unhosted_model|unhosted|does not host/.test(text)) {
|
|
24864
|
+
return result("unhosted_model");
|
|
24865
|
+
}
|
|
24866
|
+
if (code.startsWith("gate_") || text.includes("gate_unsupported") || text.includes("gate_failed")) {
|
|
24867
|
+
return result("gate");
|
|
24868
|
+
}
|
|
24869
|
+
if (code === "producer_route_not_admitted" || text.includes("not_admitted") || text.includes("not admitted")) {
|
|
24870
|
+
return result("admission");
|
|
24871
|
+
}
|
|
24872
|
+
if (status === 413 || /context_overflow|context length|context window|maximum context|token limit|prompt too long|context overflow/.test(text)) {
|
|
24873
|
+
return result("context_overflow");
|
|
24874
|
+
}
|
|
24875
|
+
if (status === 429 || /rate_limit|rate limit|ratelimit|too many requests/.test(text)) {
|
|
24876
|
+
return result("rate_limit");
|
|
24877
|
+
}
|
|
24878
|
+
if (status === 408 || status === 502 || status === 504 || code === "process_timeout" || /etimedout|econnreset|econnrefused|enotfound|eai_again|socket hang up|\btimeout\b|timed out|\btransport\b/.test(text)) {
|
|
24879
|
+
return result("transport");
|
|
24880
|
+
}
|
|
24881
|
+
if (status === 500 || status === 503 || /provider_unavailable|model unavailable|service unavailable|overloaded|model_not_found|no available model/.test(text)) {
|
|
24882
|
+
return result("provider_unavailable");
|
|
24883
|
+
}
|
|
24884
|
+
return result("unknown");
|
|
24885
|
+
}
|
|
24886
|
+
function parseFallbackPolicy(value) {
|
|
24887
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
24888
|
+
const record2 = value;
|
|
24889
|
+
const onErrorRaw = record2.onError;
|
|
24890
|
+
if (!Array.isArray(onErrorRaw) || onErrorRaw.length === 0) return void 0;
|
|
24891
|
+
if (new Set(onErrorRaw).size !== onErrorRaw.length) return void 0;
|
|
24892
|
+
const onError = [];
|
|
24893
|
+
for (const item of onErrorRaw) {
|
|
24894
|
+
if (typeof item !== "string" || !WALKABLE_CLASSES.includes(item)) return void 0;
|
|
24895
|
+
onError.push(item);
|
|
24896
|
+
}
|
|
24897
|
+
let maxSwitches = DEFAULT_MAX_SWITCHES;
|
|
24898
|
+
if (record2.maxSwitches !== void 0) {
|
|
24899
|
+
if (!Number.isInteger(record2.maxSwitches) || record2.maxSwitches < 0) return void 0;
|
|
24900
|
+
maxSwitches = record2.maxSwitches;
|
|
24901
|
+
}
|
|
24902
|
+
let revert = "next_run";
|
|
24903
|
+
if (record2.revert !== void 0) {
|
|
24904
|
+
if (record2.revert !== "next_run" && record2.revert !== "never") return void 0;
|
|
24905
|
+
revert = record2.revert;
|
|
24906
|
+
}
|
|
24907
|
+
return { onError, maxSwitches, revert };
|
|
24908
|
+
}
|
|
24909
|
+
function admittedSelector(model, admitted, disabled) {
|
|
24910
|
+
const candidates = [model.model, `${model.vendor}/${model.model}`, `${model.harness}/${model.model}`];
|
|
24911
|
+
const selector = candidates.find((candidate) => admitted.has(candidate));
|
|
24912
|
+
if (!selector || disabled.has(selector)) return void 0;
|
|
24913
|
+
return selector;
|
|
24914
|
+
}
|
|
24915
|
+
function splitSelector(selector) {
|
|
24916
|
+
const slash = selector.indexOf("/");
|
|
24917
|
+
if (slash <= 0 || slash === selector.length - 1) return void 0;
|
|
24918
|
+
return { provider: selector.slice(0, slash), model: selector.slice(slash + 1) };
|
|
24919
|
+
}
|
|
24920
|
+
function eligible(model, admitted, disabled, liveWrite) {
|
|
24921
|
+
if (model.status !== "admitted" || !model.hosted) return void 0;
|
|
24922
|
+
if (liveWrite && (!model.permissions.includes("edit") || !model.writerReady || !model.onWriterRoster)) return void 0;
|
|
24923
|
+
const selector = admittedSelector(model, admitted, disabled);
|
|
24924
|
+
if (!selector) return void 0;
|
|
24925
|
+
const parts = splitSelector(selector);
|
|
24926
|
+
if (!parts) return void 0;
|
|
24927
|
+
return {
|
|
24928
|
+
routeId: model.routeId,
|
|
24929
|
+
harness: model.harness,
|
|
24930
|
+
provider: parts.provider,
|
|
24931
|
+
model: parts.model,
|
|
24932
|
+
selector,
|
|
24933
|
+
vendor: model.vendor,
|
|
24934
|
+
permissions: model.permissions,
|
|
24935
|
+
priority: model.priority
|
|
24936
|
+
};
|
|
24937
|
+
}
|
|
24938
|
+
function selectorsFor2(fallback, models) {
|
|
24939
|
+
if (fallback.profile) {
|
|
24940
|
+
const model = models.find((item) => item.routeId === fallback.profile);
|
|
24941
|
+
return model ? [model] : [];
|
|
24942
|
+
}
|
|
24943
|
+
if (fallback.tag) {
|
|
24944
|
+
const required2 = new Set(fallback.capabilities ?? []);
|
|
24945
|
+
return models.filter((item) => item.tags.includes(fallback.tag) && [...required2].every((capability) => item.capabilities.includes(capability))).slice().sort((left, right) => right.priority - left.priority || left.routeId.localeCompare(right.routeId));
|
|
24946
|
+
}
|
|
24947
|
+
if (fallback.provider && fallback.model) {
|
|
24948
|
+
return models.filter((item) => item.vendor === fallback.provider && item.model === fallback.model);
|
|
24949
|
+
}
|
|
24950
|
+
return [];
|
|
24951
|
+
}
|
|
24952
|
+
function buildFallbackChain(input) {
|
|
24953
|
+
const admitted = new Set(input.admitted);
|
|
24954
|
+
const disabled = new Set(input.disabled);
|
|
24955
|
+
const critics = new Set(input.criticVendors ?? []);
|
|
24956
|
+
const byId = new Map(input.models.map((model) => [model.routeId, model]));
|
|
24957
|
+
const chain = [];
|
|
24958
|
+
const seen = /* @__PURE__ */ new Set();
|
|
24959
|
+
const entries = [...input.roster, ...input.extendsRoster ?? []];
|
|
24960
|
+
const push2 = (model, effort) => {
|
|
24961
|
+
if (!model || seen.has(model.routeId)) return;
|
|
24962
|
+
const candidate = eligible(model, admitted, disabled, input.liveWrite);
|
|
24963
|
+
if (!candidate) return;
|
|
24964
|
+
if (input.vendorIndependenceRequired && chain.length > 0 && critics.has(candidate.vendor)) return;
|
|
24965
|
+
seen.add(model.routeId);
|
|
24966
|
+
const resolvedEffort = effort && EFFORTS2.has(effort) ? effort : void 0;
|
|
24967
|
+
chain.push(resolvedEffort ? { ...candidate, effort: resolvedEffort } : candidate);
|
|
24968
|
+
};
|
|
24969
|
+
for (const entry of entries) {
|
|
24970
|
+
const model = byId.get(entry.route);
|
|
24971
|
+
push2(model, entry.effort);
|
|
24972
|
+
if (!model) continue;
|
|
24973
|
+
for (const fallback of model.fallbacks) {
|
|
24974
|
+
for (const resolved of selectorsFor2(fallback, input.models)) push2(resolved, entry.effort);
|
|
24975
|
+
}
|
|
24976
|
+
}
|
|
24977
|
+
return chain;
|
|
24978
|
+
}
|
|
24979
|
+
function decideRouteSwitch(input) {
|
|
24980
|
+
if (!input.policy || input.policy.onError.length === 0) return { action: "continue", cause: "disabled" };
|
|
24981
|
+
if (!input.failure.walkable) return { action: "continue", cause: "not_walkable" };
|
|
24982
|
+
if (!input.policy.onError.includes(input.failure.class)) {
|
|
24983
|
+
return { action: "continue", cause: "class_not_opted_in" };
|
|
24984
|
+
}
|
|
24985
|
+
if (input.switchesUsed >= input.policy.maxSwitches) return { action: "continue", cause: "budget" };
|
|
24986
|
+
const index = input.chain.findIndex((candidate) => candidate.routeId === input.currentRouteId);
|
|
24987
|
+
const next = index >= 0 ? input.chain[index + 1] : void 0;
|
|
24988
|
+
if (!next) return { action: "continue", cause: "no_candidate" };
|
|
24989
|
+
const effort = next.effort ?? (input.currentEffort && EFFORTS2.has(input.currentEffort) ? input.currentEffort : void 0);
|
|
24990
|
+
return {
|
|
24991
|
+
action: "switch",
|
|
24992
|
+
to: next,
|
|
24993
|
+
reason: input.failure.class,
|
|
24994
|
+
...effort ? { effort } : {}
|
|
24995
|
+
};
|
|
24996
|
+
}
|
|
24997
|
+
function parseSessionCarry(value) {
|
|
24998
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
24999
|
+
const transcript = value.transcript;
|
|
25000
|
+
if (!Array.isArray(transcript) || transcript.length === 0) return void 0;
|
|
25001
|
+
const messages = [];
|
|
25002
|
+
let total = 0;
|
|
25003
|
+
for (const item of transcript) {
|
|
25004
|
+
if (messages.length >= MAX_CARRY_MESSAGES) break;
|
|
25005
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) continue;
|
|
25006
|
+
const role = item.role;
|
|
25007
|
+
const text = item.text;
|
|
25008
|
+
if (typeof role !== "string" || !CARRY_ROLES.has(role) || typeof text !== "string" || text.length === 0) continue;
|
|
25009
|
+
const clipped = text.slice(0, MAX_CARRY_TEXT);
|
|
25010
|
+
if (total + clipped.length > MAX_CARRY_TOTAL) break;
|
|
25011
|
+
total += clipped.length;
|
|
25012
|
+
messages.push({ role, text: clipped });
|
|
25013
|
+
}
|
|
25014
|
+
return messages.length > 0 ? { transcript: messages } : void 0;
|
|
25015
|
+
}
|
|
25016
|
+
function mergeSessionCarry(left, right) {
|
|
25017
|
+
return parseSessionCarry({ transcript: [...left?.transcript ?? [], ...right?.transcript ?? []] });
|
|
25018
|
+
}
|
|
25019
|
+
function carryForwardPrompt(prompt, carry) {
|
|
25020
|
+
if (!carry || carry.transcript.length === 0) return prompt;
|
|
25021
|
+
const lines = carry.transcript.map((message) => `${message.role}: ${message.text}`);
|
|
25022
|
+
const block = `Prior attempt transcript:
|
|
25023
|
+
${lines.join("\n")}`;
|
|
25024
|
+
if (!prompt || prompt.length === 0) return block;
|
|
25025
|
+
return `${prompt}
|
|
25026
|
+
|
|
25027
|
+
${block}`;
|
|
25028
|
+
}
|
|
25029
|
+
function readYaml(file) {
|
|
25030
|
+
if (!existsSync11(file)) return void 0;
|
|
25031
|
+
try {
|
|
25032
|
+
const parsed = (0, import_yaml8.parse)(readFileSync9(file, "utf8"));
|
|
25033
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return void 0;
|
|
25034
|
+
return parsed;
|
|
25035
|
+
} catch {
|
|
25036
|
+
return void 0;
|
|
25037
|
+
}
|
|
25038
|
+
}
|
|
25039
|
+
function stringList(value) {
|
|
25040
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
25041
|
+
}
|
|
25042
|
+
function readSelectors(projectRoot, value) {
|
|
25043
|
+
if (!Array.isArray(value)) return [];
|
|
25044
|
+
const modelsDir = join12(projectRoot, ".kxm", "models");
|
|
25045
|
+
const selectors = [];
|
|
25046
|
+
for (const item of value) {
|
|
25047
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) continue;
|
|
25048
|
+
const record2 = item;
|
|
25049
|
+
if (typeof record2.profile === "string") {
|
|
25050
|
+
selectors.push({ profile: findYamlBasename(modelsDir, record2.profile, "route") ?? record2.profile });
|
|
25051
|
+
} else if (typeof record2.tag === "string") {
|
|
25052
|
+
selectors.push({
|
|
25053
|
+
tag: record2.tag,
|
|
25054
|
+
...Array.isArray(record2.capabilities) ? { capabilities: stringList(record2.capabilities) } : {}
|
|
25055
|
+
});
|
|
25056
|
+
} else if (typeof record2.provider === "string" && typeof record2.model === "string") {
|
|
25057
|
+
selectors.push({ provider: record2.provider, model: record2.model });
|
|
25058
|
+
}
|
|
25059
|
+
}
|
|
25060
|
+
return selectors;
|
|
25061
|
+
}
|
|
25062
|
+
function loadChainModel(projectRoot, routeId2, writerIds) {
|
|
25063
|
+
const modelsDir = join12(projectRoot, ".kxm", "models");
|
|
25064
|
+
const fileId = findYamlBasename(modelsDir, routeId2, "route") ?? routeId2;
|
|
25065
|
+
const doc = readYaml(join12(modelsDir, `${fileId}.yaml`));
|
|
25066
|
+
if (!doc || doc.schema !== "kxm.model.v2") return void 0;
|
|
25067
|
+
const harness = typeof doc.harness === "string" ? doc.harness : "";
|
|
25068
|
+
const model = typeof doc.model === "string" ? doc.model : "";
|
|
25069
|
+
const vendor = typeof doc.vendor === "string" ? doc.vendor : "";
|
|
25070
|
+
if (!harness || !model || !vendor) return void 0;
|
|
25071
|
+
const permissions = stringList(doc.permissions);
|
|
25072
|
+
const selector = [model, `${vendor}/${model}`, `${harness}/${model}`].find((candidate) => candidate.includes("/")) ?? `${vendor}/${model}`;
|
|
25073
|
+
const parts = splitSelector(selector);
|
|
25074
|
+
const hosted = parts ? validateHarnessModelPair(harness, { provider: parts.provider, model: parts.model }).valid : false;
|
|
25075
|
+
return {
|
|
25076
|
+
routeId: fileId,
|
|
25077
|
+
harness,
|
|
25078
|
+
model,
|
|
25079
|
+
vendor,
|
|
25080
|
+
status: typeof doc.status === "string" ? doc.status : "",
|
|
25081
|
+
permissions,
|
|
25082
|
+
tags: stringList(doc.tags),
|
|
25083
|
+
capabilities: stringList(doc.capabilities),
|
|
25084
|
+
priority: typeof doc.priority === "number" ? doc.priority : 0,
|
|
25085
|
+
fallbacks: readSelectors(projectRoot, doc.fallbacks),
|
|
25086
|
+
hosted,
|
|
25087
|
+
writerReady: oneShotWriterArgs(harness) !== void 0,
|
|
25088
|
+
onWriterRoster: writerIds.has(fileId) || writerIds.has(routeId2)
|
|
25089
|
+
};
|
|
25090
|
+
}
|
|
25091
|
+
function canonicalRoster(projectRoot, entries) {
|
|
25092
|
+
const modelsDir = join12(projectRoot, ".kxm", "models");
|
|
25093
|
+
return entries.map((entry) => {
|
|
25094
|
+
const route = findYamlBasename(modelsDir, entry.route, "route") ?? entry.route;
|
|
25095
|
+
return entry.effort ? { route, effort: entry.effort } : { route };
|
|
25096
|
+
});
|
|
25097
|
+
}
|
|
25098
|
+
function rosterEntries(doc) {
|
|
25099
|
+
if (!Array.isArray(doc?.roster)) return [];
|
|
25100
|
+
const entries = [];
|
|
25101
|
+
for (const item of doc.roster) {
|
|
25102
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) continue;
|
|
25103
|
+
const route = item.route;
|
|
25104
|
+
const effort = item.effort;
|
|
25105
|
+
if (typeof route !== "string" || route.length === 0) continue;
|
|
25106
|
+
entries.push(typeof effort === "string" ? { route, effort } : { route });
|
|
25107
|
+
}
|
|
25108
|
+
return entries;
|
|
25109
|
+
}
|
|
25110
|
+
function loadModelsFor(projectRoot, roster, writerIds) {
|
|
25111
|
+
const modelsDir = join12(projectRoot, ".kxm", "models");
|
|
25112
|
+
const wanted = /* @__PURE__ */ new Set();
|
|
25113
|
+
for (const entry of roster) wanted.add(entry.route);
|
|
25114
|
+
if (existsSync11(modelsDir)) {
|
|
25115
|
+
for (const name of readdirSync7(modelsDir)) {
|
|
25116
|
+
if (!name.endsWith(".yaml") || name === "inventory.yaml") continue;
|
|
25117
|
+
wanted.add(name.slice(0, -5));
|
|
25118
|
+
}
|
|
25119
|
+
}
|
|
25120
|
+
const models = [];
|
|
25121
|
+
const seen = /* @__PURE__ */ new Set();
|
|
25122
|
+
for (const routeId2 of wanted) {
|
|
25123
|
+
const model = loadChainModel(projectRoot, routeId2, writerIds);
|
|
25124
|
+
if (!model || seen.has(model.routeId)) continue;
|
|
25125
|
+
seen.add(model.routeId);
|
|
25126
|
+
models.push(model);
|
|
25127
|
+
}
|
|
25128
|
+
return models;
|
|
25129
|
+
}
|
|
25130
|
+
function criticVendors(projectRoot, writerIds) {
|
|
25131
|
+
const dir = join12(projectRoot, ".kxm", "roles");
|
|
25132
|
+
if (!existsSync11(dir)) return [];
|
|
25133
|
+
const vendors = [];
|
|
25134
|
+
for (const name of readdirSync7(dir)) {
|
|
25135
|
+
if (!name.endsWith(".yaml")) continue;
|
|
25136
|
+
const doc = readYaml(join12(dir, name));
|
|
25137
|
+
const purpose = doc?.purpose;
|
|
25138
|
+
if (purpose !== "reviewer-arch" && purpose !== "reviewer-cli") continue;
|
|
25139
|
+
const first = rosterEntries(doc)[0];
|
|
25140
|
+
if (!first) continue;
|
|
25141
|
+
const model = loadChainModel(projectRoot, first.route, writerIds);
|
|
25142
|
+
if (model?.status === "admitted") vendors.push(model.vendor);
|
|
25143
|
+
}
|
|
25144
|
+
return vendors;
|
|
25145
|
+
}
|
|
25146
|
+
function loadAgentFallback(projectRoot, agentId, options) {
|
|
25147
|
+
const agentsDir = join12(projectRoot, ".kxm", "agents");
|
|
25148
|
+
const agentFile = findYamlBasename(agentsDir, agentId, "agent") ?? agentId;
|
|
25149
|
+
const agent = readYaml(join12(agentsDir, `${agentFile}.yaml`));
|
|
25150
|
+
const roleId = typeof agent?.role === "string" ? agent.role : "";
|
|
25151
|
+
if (!roleId) return void 0;
|
|
25152
|
+
const role = readYaml(join12(projectRoot, ".kxm", "roles", `${roleId}.yaml`));
|
|
25153
|
+
if (!role) return void 0;
|
|
25154
|
+
const policyRecord = role.policy && typeof role.policy === "object" ? role.policy.fallback : void 0;
|
|
25155
|
+
const policy = parseFallbackPolicy(policyRecord);
|
|
25156
|
+
if (!policy) return void 0;
|
|
25157
|
+
const own2 = canonicalRoster(projectRoot, rosterEntries(role));
|
|
25158
|
+
const seenRoles = /* @__PURE__ */ new Set([roleId]);
|
|
25159
|
+
let extendsRoster = [];
|
|
25160
|
+
let cursor = typeof role.extends === "string" ? role.extends : "";
|
|
25161
|
+
while (cursor && !seenRoles.has(cursor)) {
|
|
25162
|
+
seenRoles.add(cursor);
|
|
25163
|
+
const parent = readYaml(join12(projectRoot, ".kxm", "roles", `${cursor}.yaml`));
|
|
25164
|
+
extendsRoster = extendsRoster.concat(canonicalRoster(projectRoot, rosterEntries(parent)));
|
|
25165
|
+
cursor = typeof parent?.extends === "string" ? parent.extends : "";
|
|
25166
|
+
}
|
|
25167
|
+
const writerIds = new Set(listRoleBindings(projectRoot).writer ?? []);
|
|
25168
|
+
const models = loadModelsFor(projectRoot, [...own2, ...extendsRoster], writerIds);
|
|
25169
|
+
const routePolicy = loadRoutePolicy(projectRoot);
|
|
25170
|
+
const independence = Boolean(role.policy && typeof role.policy === "object" && role.policy.vendorIndependenceRequired);
|
|
25171
|
+
const chain = buildFallbackChain({
|
|
25172
|
+
roster: own2,
|
|
25173
|
+
extendsRoster,
|
|
25174
|
+
models,
|
|
25175
|
+
admitted: routePolicy.admitted,
|
|
25176
|
+
disabled: routePolicy.disabled,
|
|
25177
|
+
liveWrite: options.liveWrite,
|
|
25178
|
+
criticVendors: independence ? criticVendors(projectRoot, writerIds) : [],
|
|
25179
|
+
vendorIndependenceRequired: independence
|
|
25180
|
+
});
|
|
25181
|
+
return { policy, chain, roleId };
|
|
25182
|
+
}
|
|
25183
|
+
|
|
24826
25184
|
// plugins/kxm/src/worktree-witness.ts
|
|
24827
25185
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
24828
25186
|
function gitText(cwd, args) {
|
|
@@ -25149,8 +25507,8 @@ function pruneContextPacket(packet, tokenBudget) {
|
|
|
25149
25507
|
|
|
25150
25508
|
// plugins/kxm/src/dispatch-context.ts
|
|
25151
25509
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
25152
|
-
import { readdirSync as
|
|
25153
|
-
import { extname as extname3, join as
|
|
25510
|
+
import { readdirSync as readdirSync10 } from "node:fs";
|
|
25511
|
+
import { extname as extname3, join as join15 } from "node:path";
|
|
25154
25512
|
|
|
25155
25513
|
// plugins/kxm/src/protocol.ts
|
|
25156
25514
|
var DEFAULT_MESSAGE_TTL_MS = 24 * 60 * 6e4;
|
|
@@ -25679,16 +26037,16 @@ function arbitrate(requestInput, pool, options = {}) {
|
|
|
25679
26037
|
};
|
|
25680
26038
|
const allowedKinds = new Set(requestedKinds);
|
|
25681
26039
|
const inertProposal = (item) => item.kind === "state" && item.status !== void 0 && item.status !== "current" || item.kind === "skill" && item.status === "proposed";
|
|
25682
|
-
const
|
|
25683
|
-
const scoreList = scoreRelevance(request.task,
|
|
26040
|
+
const eligible2 = candidates.filter((item) => contradictions.has(item.id) || allowedKinds.has(item.kind) && !inertProposal(item));
|
|
26041
|
+
const scoreList = scoreRelevance(request.task, eligible2.map(contextItemRelevanceText));
|
|
25684
26042
|
const scores = /* @__PURE__ */ new Map();
|
|
25685
|
-
|
|
26043
|
+
eligible2.forEach((item, index) => scores.set(item, scoreList[index] ?? 0));
|
|
25686
26044
|
const scoreOf = (item) => scores.get(item) ?? 0;
|
|
25687
26045
|
const when = (item) => {
|
|
25688
26046
|
const parsed = Date.parse(item.observedAt ?? item.validFrom ?? "");
|
|
25689
26047
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
25690
26048
|
};
|
|
25691
|
-
const ordered = [...
|
|
26049
|
+
const ordered = [...eligible2].sort(
|
|
25692
26050
|
(left, right) => (contradictions.has(right.id) ? 1 : 0) - (contradictions.has(left.id) ? 1 : 0) || (left.project === request.project ? 0 : 1) - (right.project === request.project ? 0 : 1) || (scoreOf(right) > 0 ? 1 : 0) - (scoreOf(left) > 0 ? 1 : 0) || kindPreference(left) - kindPreference(right) || scoreOf(right) - scoreOf(left) || CONFIDENCE_RANK[right.confidence] - CONFIDENCE_RANK[left.confidence] || AUTHORITY_WEIGHT[right.authority] - AUTHORITY_WEIGHT[left.authority] || when(right) - when(left) || compareCodeUnitIds(left.id, right.id)
|
|
25693
26051
|
);
|
|
25694
26052
|
const selected = [];
|
|
@@ -25778,9 +26136,9 @@ function memoryRecordToContextItem(record2, project) {
|
|
|
25778
26136
|
}
|
|
25779
26137
|
|
|
25780
26138
|
// plugins/kxm/src/memory.ts
|
|
25781
|
-
var
|
|
25782
|
-
import { existsSync as
|
|
25783
|
-
import { extname as extname2, join as
|
|
26139
|
+
var import_yaml9 = __toESM(require_dist(), 1);
|
|
26140
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync4, readdirSync as readdirSync8, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "node:fs";
|
|
26141
|
+
import { extname as extname2, join as join13, resolve as resolve7 } from "node:path";
|
|
25784
26142
|
var MEMORY_SCHEMA = "kxm.memory.v1";
|
|
25785
26143
|
var VALID_SCOPES = /* @__PURE__ */ new Set(["agent", "project", "run", "operator"]);
|
|
25786
26144
|
var VALID_AUTHORITIES = /* @__PURE__ */ new Set(["instruction", "evidence", "promoted"]);
|
|
@@ -25810,7 +26168,7 @@ function parseFrontmatter(content) {
|
|
|
25810
26168
|
}
|
|
25811
26169
|
function parseMemoryRecord(raw, filename = "memory.md") {
|
|
25812
26170
|
const { frontmatter, body } = parseFrontmatter(raw);
|
|
25813
|
-
const data = (0,
|
|
26171
|
+
const data = (0, import_yaml9.parse)(frontmatter);
|
|
25814
26172
|
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
25815
26173
|
throw new Error(`invalid YAML frontmatter in ${filename}`);
|
|
25816
26174
|
}
|
|
@@ -25879,20 +26237,20 @@ function parseMemoryRecord(raw, filename = "memory.md") {
|
|
|
25879
26237
|
}
|
|
25880
26238
|
function memoryDirectories(repoRoot) {
|
|
25881
26239
|
const root = resolve7(repoRoot);
|
|
25882
|
-
const memoryDir =
|
|
25883
|
-
const candidatesDir =
|
|
26240
|
+
const memoryDir = join13(root, ".kxm", "memory");
|
|
26241
|
+
const candidatesDir = join13(memoryDir, "candidates");
|
|
25884
26242
|
return { memoryDir, candidatesDir };
|
|
25885
26243
|
}
|
|
25886
26244
|
function loadAuthoredMemory(repoRoot) {
|
|
25887
26245
|
const { memoryDir } = memoryDirectories(repoRoot);
|
|
25888
|
-
if (!
|
|
26246
|
+
if (!existsSync12(memoryDir)) return [];
|
|
25889
26247
|
const records = [];
|
|
25890
|
-
const entries =
|
|
26248
|
+
const entries = readdirSync8(memoryDir, { withFileTypes: true });
|
|
25891
26249
|
for (const entry of entries) {
|
|
25892
26250
|
if (entry.isFile() && extname2(entry.name) === ".md") {
|
|
25893
|
-
const fullPath =
|
|
26251
|
+
const fullPath = join13(memoryDir, entry.name);
|
|
25894
26252
|
try {
|
|
25895
|
-
const text =
|
|
26253
|
+
const text = readFileSync10(fullPath, "utf8");
|
|
25896
26254
|
const record2 = parseMemoryRecord(text, entry.name);
|
|
25897
26255
|
if (record2.lifecycle === "active") {
|
|
25898
26256
|
records.push(record2);
|
|
@@ -25906,10 +26264,10 @@ function loadAuthoredMemory(repoRoot) {
|
|
|
25906
26264
|
}
|
|
25907
26265
|
|
|
25908
26266
|
// plugins/kxm/src/skills.ts
|
|
25909
|
-
var
|
|
26267
|
+
var import_yaml10 = __toESM(require_dist(), 1);
|
|
25910
26268
|
import { createHash as createHash11 } from "node:crypto";
|
|
25911
|
-
import { existsSync as
|
|
25912
|
-
import { dirname as dirname7, join as
|
|
26269
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync5, readdirSync as readdirSync9, readFileSync as readFileSync11, renameSync, rmSync, statSync as statSync3, writeFileSync as writeFileSync6 } from "node:fs";
|
|
26270
|
+
import { dirname as dirname7, join as join14 } from "node:path";
|
|
25913
26271
|
var SKILL_CANDIDATE_SCHEMA = "kxm.skill-candidate.v1";
|
|
25914
26272
|
var SKILL_EVALUATION_SCHEMA = "kxm.skill-evaluation.v1";
|
|
25915
26273
|
var SKILL_DECISION_SCHEMA = "kxm.skill-decision.v1";
|
|
@@ -25950,7 +26308,7 @@ function parseSkillFrontmatter(content) {
|
|
|
25950
26308
|
return { frontmatter: null, body: content };
|
|
25951
26309
|
}
|
|
25952
26310
|
try {
|
|
25953
|
-
const parsed = (0,
|
|
26311
|
+
const parsed = (0, import_yaml10.parse)(rawFm);
|
|
25954
26312
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
25955
26313
|
return { frontmatter: parsed, body: rawBody };
|
|
25956
26314
|
}
|
|
@@ -26015,20 +26373,20 @@ var SkillLifecycle = class {
|
|
|
26015
26373
|
writeFileSync6(file, content);
|
|
26016
26374
|
}
|
|
26017
26375
|
dir(state) {
|
|
26018
|
-
return
|
|
26376
|
+
return join14(this.root, state === "candidate" ? "candidates" : `${state}s`.replace("rejecteds", "rejected").replace("promoteds", "promoted"));
|
|
26019
26377
|
}
|
|
26020
26378
|
historyFile(id) {
|
|
26021
|
-
return
|
|
26379
|
+
return join14(this.root, "history", `${id}.jsonl`);
|
|
26022
26380
|
}
|
|
26023
26381
|
paths(state, id) {
|
|
26024
|
-
const dir =
|
|
26025
|
-
return { dir, metadata:
|
|
26382
|
+
const dir = join14(this.dir(state), id);
|
|
26383
|
+
return { dir, metadata: join14(dir, "metadata.json"), skill: join14(dir, "SKILL.md") };
|
|
26026
26384
|
}
|
|
26027
26385
|
appendHistory(id, record2) {
|
|
26028
26386
|
const line = `${JSON.stringify(record2)}
|
|
26029
26387
|
`;
|
|
26030
|
-
if (
|
|
26031
|
-
const existing =
|
|
26388
|
+
if (existsSync13(this.historyFile(id))) {
|
|
26389
|
+
const existing = readFileSync11(this.historyFile(id), "utf8");
|
|
26032
26390
|
const lines = existing.split("\n").filter((entry) => entry.trim());
|
|
26033
26391
|
this.write(this.historyFile(id), [...lines.slice(-499), line.trim()].join("\n") + "\n");
|
|
26034
26392
|
} else {
|
|
@@ -26037,20 +26395,20 @@ var SkillLifecycle = class {
|
|
|
26037
26395
|
}
|
|
26038
26396
|
history(id) {
|
|
26039
26397
|
const file = this.historyFile(id);
|
|
26040
|
-
if (!
|
|
26041
|
-
return
|
|
26398
|
+
if (!existsSync13(file)) return [];
|
|
26399
|
+
return readFileSync11(file, "utf8").split("\n").filter((line) => line.trim()).map((line) => JSON.parse(line));
|
|
26042
26400
|
}
|
|
26043
26401
|
readMetadata(state, id) {
|
|
26044
26402
|
const { metadata } = this.paths(state, id);
|
|
26045
|
-
if (!
|
|
26403
|
+
if (!existsSync13(metadata)) {
|
|
26046
26404
|
throw new SkillLifecycleError("skill_not_found", `skill ${id} not found in ${state}`);
|
|
26047
26405
|
}
|
|
26048
|
-
return JSON.parse(
|
|
26406
|
+
return JSON.parse(readFileSync11(metadata, "utf8"));
|
|
26049
26407
|
}
|
|
26050
26408
|
move(from, to, id) {
|
|
26051
|
-
const fromDir =
|
|
26052
|
-
const toDir =
|
|
26053
|
-
if (!
|
|
26409
|
+
const fromDir = join14(this.dir(from), id);
|
|
26410
|
+
const toDir = join14(this.dir(to), id);
|
|
26411
|
+
if (!existsSync13(fromDir)) {
|
|
26054
26412
|
throw new SkillLifecycleError("skill_not_found", `skill ${id} not found in ${from}`);
|
|
26055
26413
|
}
|
|
26056
26414
|
if (this.dryRun) {
|
|
@@ -26058,7 +26416,7 @@ var SkillLifecycle = class {
|
|
|
26058
26416
|
return;
|
|
26059
26417
|
}
|
|
26060
26418
|
mkdirSync5(this.dir(to), { recursive: true });
|
|
26061
|
-
if (
|
|
26419
|
+
if (existsSync13(toDir)) rmSync(toDir, { recursive: true, force: true });
|
|
26062
26420
|
renameSync(fromDir, toDir);
|
|
26063
26421
|
}
|
|
26064
26422
|
/** Submit a new skill candidate. Content is redacted of secret material at
|
|
@@ -26101,7 +26459,7 @@ var SkillLifecycle = class {
|
|
|
26101
26459
|
const contentSha256 = skillContentSha256(content);
|
|
26102
26460
|
const id = skillIdFor(name, contentSha256);
|
|
26103
26461
|
const { metadata, skill } = this.paths("candidate", id);
|
|
26104
|
-
if (
|
|
26462
|
+
if (existsSync13(metadata)) {
|
|
26105
26463
|
throw new SkillLifecycleError(
|
|
26106
26464
|
"skill_candidate_exists",
|
|
26107
26465
|
`identical candidate ${id} already exists; changed behavior requires changed content`
|
|
@@ -26210,11 +26568,11 @@ var SkillLifecycle = class {
|
|
|
26210
26568
|
};
|
|
26211
26569
|
const candidatePaths = this.paths("candidate", candidateId);
|
|
26212
26570
|
const promotedPaths = this.paths("promoted", candidateId);
|
|
26213
|
-
const skillContent =
|
|
26214
|
-
const metadataContent =
|
|
26571
|
+
const skillContent = readFileSync11(candidatePaths.skill, "utf8");
|
|
26572
|
+
const metadataContent = readFileSync11(candidatePaths.metadata, "utf8");
|
|
26215
26573
|
this.write(promotedPaths.skill, skillContent);
|
|
26216
26574
|
this.write(promotedPaths.metadata, metadataContent);
|
|
26217
|
-
const patchPath =
|
|
26575
|
+
const patchPath = join14(this.root, "patches", `${candidateId}.patch`);
|
|
26218
26576
|
const relSkillPath = `.kxm/skills/promoted/${candidateId}/SKILL.md`;
|
|
26219
26577
|
const relMetaPath = `.kxm/skills/promoted/${candidateId}/metadata.json`;
|
|
26220
26578
|
const patch = `${createUnifiedPatch(relSkillPath, skillContent)}${createUnifiedPatch(relMetaPath, metadataContent)}`;
|
|
@@ -26242,7 +26600,7 @@ var SkillLifecycle = class {
|
|
|
26242
26600
|
verify(state, id) {
|
|
26243
26601
|
const metadata = this.readMetadata(state, id);
|
|
26244
26602
|
const { skill } = this.paths(state, id);
|
|
26245
|
-
const content =
|
|
26603
|
+
const content = readFileSync11(skill, "utf8");
|
|
26246
26604
|
if (skillContentSha256(content) !== metadata.contentSha256) {
|
|
26247
26605
|
throw new SkillLifecycleError(
|
|
26248
26606
|
"skill_integrity_violation",
|
|
@@ -26260,7 +26618,7 @@ var SkillLifecycle = class {
|
|
|
26260
26618
|
}
|
|
26261
26619
|
list(state) {
|
|
26262
26620
|
const dir = this.dir(state);
|
|
26263
|
-
if (!
|
|
26621
|
+
if (!existsSync13(dir)) return [];
|
|
26264
26622
|
const ids = readdirSorted(dir);
|
|
26265
26623
|
return ids.map((id) => {
|
|
26266
26624
|
try {
|
|
@@ -26273,7 +26631,7 @@ var SkillLifecycle = class {
|
|
|
26273
26631
|
read(state, id) {
|
|
26274
26632
|
const metadata = this.readMetadata(state, id);
|
|
26275
26633
|
const { skill } = this.paths(state, id);
|
|
26276
|
-
return { metadata, content:
|
|
26634
|
+
return { metadata, content: readFileSync11(skill, "utf8") };
|
|
26277
26635
|
}
|
|
26278
26636
|
};
|
|
26279
26637
|
function boundedList(value, field) {
|
|
@@ -26288,7 +26646,7 @@ function boundedList(value, field) {
|
|
|
26288
26646
|
return [...new Set(refs)];
|
|
26289
26647
|
}
|
|
26290
26648
|
function readdirSorted(dir) {
|
|
26291
|
-
return
|
|
26649
|
+
return readdirSync9(dir).filter((entry) => statSync3(join14(dir, entry)).isDirectory()).sort();
|
|
26292
26650
|
}
|
|
26293
26651
|
|
|
26294
26652
|
// plugins/kxm/src/dispatch-context.ts
|
|
@@ -26329,18 +26687,18 @@ function contextRoleForAgent(agentId) {
|
|
|
26329
26687
|
return agentId;
|
|
26330
26688
|
}
|
|
26331
26689
|
function dispatchContextPresent(projectRoot) {
|
|
26332
|
-
return hasTopLevelMarkdown(
|
|
26690
|
+
return hasTopLevelMarkdown(join15(projectRoot, ".kxm", "memory")) || hasAnyEntry(join15(projectRoot, ".kxm", "skills", "promoted"));
|
|
26333
26691
|
}
|
|
26334
26692
|
function hasTopLevelMarkdown(dir) {
|
|
26335
26693
|
try {
|
|
26336
|
-
return
|
|
26694
|
+
return readdirSync10(dir, { withFileTypes: true }).some((entry) => entry.isFile() && extname3(entry.name) === ".md");
|
|
26337
26695
|
} catch {
|
|
26338
26696
|
return false;
|
|
26339
26697
|
}
|
|
26340
26698
|
}
|
|
26341
26699
|
function hasAnyEntry(dir) {
|
|
26342
26700
|
try {
|
|
26343
|
-
return
|
|
26701
|
+
return readdirSync10(dir).length > 0;
|
|
26344
26702
|
} catch {
|
|
26345
26703
|
return false;
|
|
26346
26704
|
}
|
|
@@ -26405,7 +26763,7 @@ function loadDispatchContextSources(input) {
|
|
|
26405
26763
|
}
|
|
26406
26764
|
const promoted = [];
|
|
26407
26765
|
try {
|
|
26408
|
-
const lifecycle = new SkillLifecycle(
|
|
26766
|
+
const lifecycle = new SkillLifecycle(join15(projectRoot, ".kxm", "skills"));
|
|
26409
26767
|
for (const metadata of lifecycle.list("promoted")) {
|
|
26410
26768
|
try {
|
|
26411
26769
|
promoted.push(lifecycle.verify("promoted", metadata.id));
|
|
@@ -26488,7 +26846,7 @@ function assembleDispatchContext(sources, input) {
|
|
|
26488
26846
|
}
|
|
26489
26847
|
|
|
26490
26848
|
// plugins/kxm/src/engine-artifacts.ts
|
|
26491
|
-
import { join as
|
|
26849
|
+
import { join as join16 } from "node:path";
|
|
26492
26850
|
|
|
26493
26851
|
// plugins/kxm/src/artifacts-exist.ts
|
|
26494
26852
|
import { lstatSync as lstatSync5, realpathSync as realpathSync4, statSync as statSync4 } from "node:fs";
|
|
@@ -26535,14 +26893,14 @@ function verifyArtifactExists(rootInput, pathInput) {
|
|
|
26535
26893
|
// plugins/kxm/src/engine-artifacts.ts
|
|
26536
26894
|
var kxmArtifactsGateSeams = {};
|
|
26537
26895
|
function evaluateArtifactsGate(context, definition) {
|
|
26538
|
-
const root =
|
|
26896
|
+
const root = join16(context.projectRoot, ".kxm", "assets");
|
|
26539
26897
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
26540
26898
|
const startTime = process.hrtime.bigint();
|
|
26541
26899
|
let checkedCount = 0;
|
|
26542
26900
|
let failedCount = 0;
|
|
26543
26901
|
for (const [index, path] of definition.paths.entries()) {
|
|
26544
26902
|
checkedCount += 1;
|
|
26545
|
-
const result = verifyArtifactExists(root,
|
|
26903
|
+
const result = verifyArtifactExists(root, join16(root, path));
|
|
26546
26904
|
if (!result.ok) failedCount += 1;
|
|
26547
26905
|
kxmArtifactsGateSeams.afterPathChecked?.({ path, index, checkedCount, failedCount });
|
|
26548
26906
|
}
|
|
@@ -28103,32 +28461,185 @@ var KxmRunScheduler = class _KxmRunScheduler {
|
|
|
28103
28461
|
});
|
|
28104
28462
|
}
|
|
28105
28463
|
};
|
|
28464
|
+
var attemptFallback = /* @__PURE__ */ new Map();
|
|
28465
|
+
var runStickyRoute = /* @__PURE__ */ new Map();
|
|
28466
|
+
function observeProducerFailure(error, capability) {
|
|
28467
|
+
try {
|
|
28468
|
+
const raw = error instanceof Error ? error.message : error;
|
|
28469
|
+
const message = typeof raw === "string" ? raw : "";
|
|
28470
|
+
let text = message;
|
|
28471
|
+
if (capability) text = text.split(capability).join("[redacted]");
|
|
28472
|
+
text = text.replace(/kxmcap_[A-Za-z0-9_-]+/g, "[redacted]");
|
|
28473
|
+
const match = /^[a-z][a-z0-9]*(?:_[a-z0-9]+){1,3}/.exec(text);
|
|
28474
|
+
const extracted = match?.[0] ?? "";
|
|
28475
|
+
const code = extracted.length > 0 && extracted.length <= 64 ? extracted : "producer_error";
|
|
28476
|
+
const record2 = error && typeof error === "object" ? error : void 0;
|
|
28477
|
+
const statusValue = record2?.statusCode ?? record2?.status ?? record2?.httpStatus;
|
|
28478
|
+
const httpStatus = typeof statusValue === "number" && Number.isInteger(statusValue) ? statusValue : void 0;
|
|
28479
|
+
const name = error instanceof Error ? error.name : void 0;
|
|
28480
|
+
const sessionCarry = parseSessionCarry(record2?.sessionCarry);
|
|
28481
|
+
return {
|
|
28482
|
+
code,
|
|
28483
|
+
failure: {
|
|
28484
|
+
classified: classifyRouteFailure({ code, text: text.slice(0, 500), httpStatus, name }),
|
|
28485
|
+
...sessionCarry ? { sessionCarry } : {}
|
|
28486
|
+
}
|
|
28487
|
+
};
|
|
28488
|
+
} catch {
|
|
28489
|
+
return { code: "producer_error", failure: { classified: classifyRouteFailure({ code: "producer_error" }) } };
|
|
28490
|
+
}
|
|
28491
|
+
}
|
|
28106
28492
|
function invokeProducer(producer, request) {
|
|
28107
|
-
const reason = (error) => {
|
|
28108
|
-
try {
|
|
28109
|
-
const raw = error instanceof Error ? error.message : error;
|
|
28110
|
-
const message = typeof raw === "string" ? raw : "";
|
|
28111
|
-
let text = message;
|
|
28112
|
-
if (request.capability) text = text.split(request.capability).join("[redacted]");
|
|
28113
|
-
text = text.replace(/kxmcap_[A-Za-z0-9_-]+/g, "[redacted]");
|
|
28114
|
-
const match = /^[a-z][a-z0-9]*(?:_[a-z0-9]+){1,3}/.exec(text);
|
|
28115
|
-
const code = match?.[0] ?? "";
|
|
28116
|
-
return code.length > 0 && code.length <= 64 ? code : "producer_error";
|
|
28117
|
-
} catch {
|
|
28118
|
-
return "producer_error";
|
|
28119
|
-
}
|
|
28120
|
-
};
|
|
28121
28493
|
let pending;
|
|
28122
28494
|
try {
|
|
28123
28495
|
pending = Promise.resolve(producer.produce(request));
|
|
28124
28496
|
} catch (error) {
|
|
28125
|
-
|
|
28497
|
+
const observed = observeProducerFailure(error, request.capability);
|
|
28498
|
+
return Promise.resolve({ error: true, errorCode: observed.code, failure: observed.failure });
|
|
28126
28499
|
}
|
|
28127
28500
|
return pending.then(
|
|
28128
28501
|
(result) => ({ result, error: false }),
|
|
28129
|
-
(error) =>
|
|
28502
|
+
(error) => {
|
|
28503
|
+
const observed = observeProducerFailure(error, request.capability);
|
|
28504
|
+
return { error: true, errorCode: observed.code, failure: observed.failure };
|
|
28505
|
+
}
|
|
28130
28506
|
);
|
|
28131
28507
|
}
|
|
28508
|
+
function stickyRouteFromEvents(context, runId) {
|
|
28509
|
+
const remembered = runStickyRoute.get(runId);
|
|
28510
|
+
if (remembered) return remembered;
|
|
28511
|
+
let found;
|
|
28512
|
+
for (const event of context.eventStore.events(runId, 0, 1e5)) {
|
|
28513
|
+
if (event.eventType !== "routing.attempt.recorded") continue;
|
|
28514
|
+
const routing = event.payload.routing;
|
|
28515
|
+
const route = routing?.providerMetadata?.fallbackRoute;
|
|
28516
|
+
if (typeof route === "string" && route.length > 0) found = route;
|
|
28517
|
+
}
|
|
28518
|
+
return found;
|
|
28519
|
+
}
|
|
28520
|
+
function stickyWalkRoute(context, step, agentId, runId, liveWrite) {
|
|
28521
|
+
const loaded = loadAgentFallback(context.projectRoot, agentId, { liveWrite });
|
|
28522
|
+
if (!loaded || loaded.policy.revert !== "never") return void 0;
|
|
28523
|
+
const sticky = stickyRouteFromEvents(context, runId);
|
|
28524
|
+
if (!sticky) return void 0;
|
|
28525
|
+
const candidate = loaded.chain.find((item) => item.routeId === sticky);
|
|
28526
|
+
if (!candidate) return void 0;
|
|
28527
|
+
return candidateToRoute(candidate, loaded.roleId);
|
|
28528
|
+
}
|
|
28529
|
+
function candidateToRoute(candidate, role) {
|
|
28530
|
+
return {
|
|
28531
|
+
provider: candidate.provider,
|
|
28532
|
+
model: candidate.model,
|
|
28533
|
+
selector: candidate.selector,
|
|
28534
|
+
harness: candidate.harness,
|
|
28535
|
+
routeId: candidate.routeId,
|
|
28536
|
+
role,
|
|
28537
|
+
permissions: [...candidate.permissions],
|
|
28538
|
+
...candidate.effort ? { effort: candidate.effort } : {}
|
|
28539
|
+
};
|
|
28540
|
+
}
|
|
28541
|
+
function rememberAttemptFallback(attemptId, loaded, index, basePrompt) {
|
|
28542
|
+
if (loaded.chain.length < 2 || loaded.policy.onError.length === 0 || loaded.policy.maxSwitches <= 0) return;
|
|
28543
|
+
if (index < 0 || index >= loaded.chain.length) return;
|
|
28544
|
+
attemptFallback.set(attemptId, {
|
|
28545
|
+
policy: loaded.policy,
|
|
28546
|
+
chain: loaded.chain,
|
|
28547
|
+
index,
|
|
28548
|
+
switches: 0,
|
|
28549
|
+
basePrompt
|
|
28550
|
+
});
|
|
28551
|
+
}
|
|
28552
|
+
function appendRouteSwitch(context, member, routeSwitch) {
|
|
28553
|
+
const run = requireRun(context, member.run.runId);
|
|
28554
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
28555
|
+
const sequence = context.eventStore.nextSequence(run.runId);
|
|
28556
|
+
const event = {
|
|
28557
|
+
...kxmEventBase(context, run, now, kxmMonotonicNs()),
|
|
28558
|
+
eventId: newKxmEventId(),
|
|
28559
|
+
eventType: "routing.route_switched",
|
|
28560
|
+
sequence,
|
|
28561
|
+
payload: {
|
|
28562
|
+
attemptId: member.attemptId,
|
|
28563
|
+
stepId: member.stepId,
|
|
28564
|
+
routeSwitch: {
|
|
28565
|
+
from: routeSwitch.from,
|
|
28566
|
+
to: routeSwitch.to,
|
|
28567
|
+
reason: routeSwitch.reason,
|
|
28568
|
+
...routeSwitch.effort ? { effort: routeSwitch.effort } : {}
|
|
28569
|
+
}
|
|
28570
|
+
}
|
|
28571
|
+
};
|
|
28572
|
+
context.eventStore.appendEvent(event);
|
|
28573
|
+
const next = foldStoredKxmRun(context, run);
|
|
28574
|
+
persistKxmRunState(context, run.runId, next, sequence);
|
|
28575
|
+
try {
|
|
28576
|
+
context.logger?.({
|
|
28577
|
+
event: "route_switch",
|
|
28578
|
+
runId: run.runId,
|
|
28579
|
+
attemptId: member.attemptId,
|
|
28580
|
+
stepId: member.stepId,
|
|
28581
|
+
from: routeSwitch.from,
|
|
28582
|
+
to: routeSwitch.to,
|
|
28583
|
+
reason: routeSwitch.reason,
|
|
28584
|
+
...routeSwitch.effort ? { effort: routeSwitch.effort } : {}
|
|
28585
|
+
});
|
|
28586
|
+
} catch {
|
|
28587
|
+
}
|
|
28588
|
+
}
|
|
28589
|
+
async function produceWithRouteFallback(context, member, producer) {
|
|
28590
|
+
const session = attemptFallback.get(member.attemptId);
|
|
28591
|
+
let request = member.request;
|
|
28592
|
+
let produced = await invokeProducer(producer, request);
|
|
28593
|
+
if (!session) return producerOutcome(produced);
|
|
28594
|
+
let carry;
|
|
28595
|
+
while (produced.error) {
|
|
28596
|
+
const current = session.chain[session.index];
|
|
28597
|
+
if (!current) break;
|
|
28598
|
+
const failure = produced.failure?.classified ?? classifyRouteFailure({ code: produced.errorCode, text: produced.errorCode });
|
|
28599
|
+
const decision = decideRouteSwitch({
|
|
28600
|
+
policy: session.policy,
|
|
28601
|
+
chain: session.chain,
|
|
28602
|
+
currentRouteId: current.routeId,
|
|
28603
|
+
switchesUsed: session.switches,
|
|
28604
|
+
failure,
|
|
28605
|
+
...current.effort || member.request.thinking ? { currentEffort: current.effort ?? member.request.thinking } : {}
|
|
28606
|
+
});
|
|
28607
|
+
if (decision.action !== "switch") break;
|
|
28608
|
+
appendRouteSwitch(context, member, {
|
|
28609
|
+
from: current.routeId,
|
|
28610
|
+
to: decision.to.routeId,
|
|
28611
|
+
reason: decision.reason,
|
|
28612
|
+
...decision.effort ? { effort: decision.effort } : {}
|
|
28613
|
+
});
|
|
28614
|
+
session.switches += 1;
|
|
28615
|
+
session.index = session.chain.findIndex((candidate) => candidate.routeId === decision.to.routeId);
|
|
28616
|
+
carry = mergeSessionCarry(carry, produced.failure?.sessionCarry);
|
|
28617
|
+
request = {
|
|
28618
|
+
...member.request,
|
|
28619
|
+
prompt: carryForwardPrompt(session.basePrompt, carry),
|
|
28620
|
+
model: decision.to.model,
|
|
28621
|
+
provider: decision.to.provider,
|
|
28622
|
+
harness: decision.to.harness,
|
|
28623
|
+
...decision.effort ? { thinking: decision.effort } : {}
|
|
28624
|
+
};
|
|
28625
|
+
produced = await invokeProducer(producer, request);
|
|
28626
|
+
}
|
|
28627
|
+
if (!produced.error && session.switches > 0 && session.policy.revert === "never") {
|
|
28628
|
+
const route = session.chain[session.index];
|
|
28629
|
+
if (route) {
|
|
28630
|
+
session.stickyRouteId = route.routeId;
|
|
28631
|
+
runStickyRoute.set(member.run.runId, route.routeId);
|
|
28632
|
+
}
|
|
28633
|
+
}
|
|
28634
|
+
return producerOutcome(produced);
|
|
28635
|
+
}
|
|
28636
|
+
function producerOutcome(produced) {
|
|
28637
|
+
return {
|
|
28638
|
+
error: produced.error,
|
|
28639
|
+
...produced.result !== void 0 ? { result: produced.result } : {},
|
|
28640
|
+
...produced.errorCode !== void 0 ? { errorCode: produced.errorCode } : {}
|
|
28641
|
+
};
|
|
28642
|
+
}
|
|
28132
28643
|
var kxmGateDispatchSeams = {};
|
|
28133
28644
|
function peekDispatchContextSources(context, runId) {
|
|
28134
28645
|
if (!dispatchContextPresent(context.projectRoot)) return EMPTY_DISPATCH_SOURCES;
|
|
@@ -28402,9 +28913,9 @@ function readYamlFile(file) {
|
|
|
28402
28913
|
return readModelDocument(file);
|
|
28403
28914
|
}
|
|
28404
28915
|
function readModelDocument(file) {
|
|
28405
|
-
if (!
|
|
28916
|
+
if (!existsSync14(file)) return void 0;
|
|
28406
28917
|
try {
|
|
28407
|
-
const parsed = (0,
|
|
28918
|
+
const parsed = (0, import_yaml11.parse)(readFileSync12(file, "utf8"));
|
|
28408
28919
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return void 0;
|
|
28409
28920
|
return parsed;
|
|
28410
28921
|
} catch {
|
|
@@ -28435,8 +28946,8 @@ function resolveProducerRoute(projectRoot, step, agentId) {
|
|
|
28435
28946
|
}
|
|
28436
28947
|
};
|
|
28437
28948
|
}
|
|
28438
|
-
const agentFile = findYamlBasename(
|
|
28439
|
-
const agent = readYamlFile(
|
|
28949
|
+
const agentFile = findYamlBasename(join17(projectRoot, ".kxm", "agents"), agentId, "agent") ?? agentId;
|
|
28950
|
+
const agent = readYamlFile(join17(projectRoot, ".kxm", "agents", `${agentFile}.yaml`));
|
|
28440
28951
|
const role = typeof agent?.role === "string" ? agent.role : "";
|
|
28441
28952
|
if (!role) {
|
|
28442
28953
|
return {
|
|
@@ -28447,7 +28958,7 @@ function resolveProducerRoute(projectRoot, step, agentId) {
|
|
|
28447
28958
|
}
|
|
28448
28959
|
};
|
|
28449
28960
|
}
|
|
28450
|
-
const roleDoc = readYamlFile(
|
|
28961
|
+
const roleDoc = readYamlFile(join17(projectRoot, ".kxm", "roles", `${role}.yaml`));
|
|
28451
28962
|
const roster = Array.isArray(roleDoc?.roster) ? roleDoc.roster : void 0;
|
|
28452
28963
|
if (!roster) {
|
|
28453
28964
|
return {
|
|
@@ -28463,8 +28974,8 @@ function resolveProducerRoute(projectRoot, step, agentId) {
|
|
|
28463
28974
|
if (!entry || typeof entry !== "object" || Array.isArray(entry)) continue;
|
|
28464
28975
|
const record2 = entry;
|
|
28465
28976
|
if (typeof record2.route !== "string" || record2.route.length === 0) continue;
|
|
28466
|
-
const routeFile = findYamlBasename(
|
|
28467
|
-
const modelPath =
|
|
28977
|
+
const routeFile = findYamlBasename(join17(projectRoot, ".kxm", "models"), record2.route, "route") ?? record2.route;
|
|
28978
|
+
const modelPath = join17(projectRoot, ".kxm", "models", `${routeFile}.yaml`);
|
|
28468
28979
|
const doc = readModelDocument(modelPath);
|
|
28469
28980
|
if (!doc) {
|
|
28470
28981
|
return {
|
|
@@ -28481,9 +28992,9 @@ function resolveProducerRoute(projectRoot, step, agentId) {
|
|
|
28481
28992
|
const harness = typeof doc.harness === "string" ? doc.harness : "";
|
|
28482
28993
|
if (!modelName || !vendor || !harness) continue;
|
|
28483
28994
|
const candidates = [modelName, `${vendor}/${modelName}`, `${harness}/${modelName}`];
|
|
28484
|
-
const
|
|
28995
|
+
const admittedSelector2 = candidates.find((candidate) => policy.admitted.includes(candidate));
|
|
28485
28996
|
const disabledSelector = candidates.find((candidate) => policy.disabled.includes(candidate));
|
|
28486
|
-
if (!
|
|
28997
|
+
if (!admittedSelector2 && disabledSelector) {
|
|
28487
28998
|
return {
|
|
28488
28999
|
error: {
|
|
28489
29000
|
reason: "step_unsupported",
|
|
@@ -28492,7 +29003,7 @@ function resolveProducerRoute(projectRoot, step, agentId) {
|
|
|
28492
29003
|
}
|
|
28493
29004
|
};
|
|
28494
29005
|
}
|
|
28495
|
-
const selector =
|
|
29006
|
+
const selector = admittedSelector2 ?? (modelName.includes("/") ? modelName : `${vendor}/${modelName}`);
|
|
28496
29007
|
if (policy.disabled.includes(selector)) {
|
|
28497
29008
|
return {
|
|
28498
29009
|
error: {
|
|
@@ -28754,6 +29265,14 @@ function prepareDispatch(context, runId, producerId, dispatchSources) {
|
|
|
28754
29265
|
return { kind: "return", state, handoff: { ...routeResult.error, stepId } };
|
|
28755
29266
|
}
|
|
28756
29267
|
resolvedRoute = routeResult;
|
|
29268
|
+
const stickyRoute = stickyWalkRoute(
|
|
29269
|
+
context,
|
|
29270
|
+
step,
|
|
29271
|
+
agentId,
|
|
29272
|
+
run.runId,
|
|
29273
|
+
Object.values(step.repositories).some((access) => access === "write")
|
|
29274
|
+
);
|
|
29275
|
+
if (stickyRoute && stickyRoute.routeId !== resolvedRoute.routeId) resolvedRoute = stickyRoute;
|
|
28757
29276
|
const writeRefusal = unsupportedLiveWrite(
|
|
28758
29277
|
context.projectRoot,
|
|
28759
29278
|
step,
|
|
@@ -28868,6 +29387,27 @@ function birthMember(context, input) {
|
|
|
28868
29387
|
}
|
|
28869
29388
|
resolvedRoute = routeResult;
|
|
28870
29389
|
}
|
|
29390
|
+
const liveProducer = Boolean(input.producerId && input.producerId !== "driver-simulated");
|
|
29391
|
+
const liveWrite = liveProducer && Object.values(input.step.repositories).some((access) => access === "write");
|
|
29392
|
+
const loadedFallback = loadAgentFallback(context.projectRoot, agentId, { liveWrite });
|
|
29393
|
+
let fallbackIndex = -1;
|
|
29394
|
+
if (loadedFallback && loadedFallback.chain.length > 1 && loadedFallback.policy.maxSwitches > 0) {
|
|
29395
|
+
const sticky = loadedFallback.policy.revert === "never" ? stickyRouteFromEvents(context, run.runId) : void 0;
|
|
29396
|
+
const stickyIndex = sticky ? loadedFallback.chain.findIndex((candidate) => candidate.routeId === sticky) : -1;
|
|
29397
|
+
if (!resolvedRoute) {
|
|
29398
|
+
fallbackIndex = stickyIndex > 0 ? stickyIndex : 0;
|
|
29399
|
+
const start = loadedFallback.chain[fallbackIndex];
|
|
29400
|
+
if (start) resolvedRoute = candidateToRoute(start, loadedFallback.roleId);
|
|
29401
|
+
} else if (stickyIndex > 0 && resolvedRoute.routeId !== loadedFallback.chain[stickyIndex]?.routeId) {
|
|
29402
|
+
const start = loadedFallback.chain[stickyIndex];
|
|
29403
|
+
if (start) {
|
|
29404
|
+
resolvedRoute = candidateToRoute(start, loadedFallback.roleId);
|
|
29405
|
+
fallbackIndex = stickyIndex;
|
|
29406
|
+
}
|
|
29407
|
+
} else {
|
|
29408
|
+
fallbackIndex = loadedFallback.chain.findIndex((candidate) => candidate.routeId === resolvedRoute?.routeId);
|
|
29409
|
+
}
|
|
29410
|
+
}
|
|
28871
29411
|
const dispatchContext = assembleDispatchContext(input.dispatchSources, {
|
|
28872
29412
|
projectId: run.projectId,
|
|
28873
29413
|
runId: run.runId,
|
|
@@ -29002,6 +29542,9 @@ ${generatedPrompt}` : generatedPrompt,
|
|
|
29002
29542
|
} catch {
|
|
29003
29543
|
}
|
|
29004
29544
|
}
|
|
29545
|
+
if (loadedFallback && fallbackIndex >= 0) {
|
|
29546
|
+
rememberAttemptFallback(attemptId, loadedFallback, fallbackIndex, member.request.prompt ?? "");
|
|
29547
|
+
}
|
|
29005
29548
|
kxmPanelDispatchSeams.afterBirth?.(member);
|
|
29006
29549
|
return member;
|
|
29007
29550
|
}
|
|
@@ -29105,7 +29648,7 @@ async function drivePanel(context, panel, producer) {
|
|
|
29105
29648
|
const live = member.producerId !== "driver-simulated";
|
|
29106
29649
|
const writes = Object.values(member.step.repositories).some((access) => access === "write");
|
|
29107
29650
|
const before = live ? captureWorktreeWitness(context.projectRoot) : void 0;
|
|
29108
|
-
const produced = await
|
|
29651
|
+
const produced = await produceWithRouteFallback(context, member, producer);
|
|
29109
29652
|
if (live && produced.result && before) {
|
|
29110
29653
|
const after = captureWorktreeWitness(context.projectRoot);
|
|
29111
29654
|
return {
|
|
@@ -29300,6 +29843,8 @@ function engineRoutingMetadata(dispatch, producer) {
|
|
|
29300
29843
|
metadata.askSha256 = kxmStepAskSha256(dispatch.plan, dispatch.stepId, dispatch.agentId);
|
|
29301
29844
|
metadata.objectiveSha256 = dispatch.run.promptSha256;
|
|
29302
29845
|
metadata.stepWrites = Object.values(dispatch.step.repositories).some((access) => access === "write");
|
|
29846
|
+
const stickyRouteId = attemptFallback.get(dispatch.attemptId)?.stickyRouteId;
|
|
29847
|
+
if (stickyRouteId && Object.keys(metadata).length < MAX_PROVIDER_METADATA_FIELDS) metadata.fallbackRoute = stickyRouteId;
|
|
29303
29848
|
return metadata;
|
|
29304
29849
|
}
|
|
29305
29850
|
function producerRoutingRecord(context, dispatch, result, now, finalOutcome) {
|
|
@@ -29458,6 +30003,7 @@ function settleMember(context, dispatch, result, produceError, produceErrorCode)
|
|
|
29458
30003
|
}
|
|
29459
30004
|
}
|
|
29460
30005
|
context.eventStore.settleCapability(dispatch.attemptId, "settled");
|
|
30006
|
+
attemptFallback.delete(dispatch.attemptId);
|
|
29461
30007
|
for (const event of events) context.eventStore.appendEvent(event);
|
|
29462
30008
|
const next = foldStoredKxmRun(context, run);
|
|
29463
30009
|
persistKxmRunState(context, run.runId, next, events[events.length - 1].sequence);
|
|
@@ -30718,9 +31264,9 @@ var RuntimeHubClient = class {
|
|
|
30718
31264
|
};
|
|
30719
31265
|
|
|
30720
31266
|
// plugins/kxm/src/hub-binding.ts
|
|
30721
|
-
import { existsSync as
|
|
31267
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync6, readFileSync as readFileSync13, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync7 } from "node:fs";
|
|
30722
31268
|
import { homedir as homedir2 } from "node:os";
|
|
30723
|
-
import { dirname as dirname8, isAbsolute as isAbsolute6, join as
|
|
31269
|
+
import { dirname as dirname8, isAbsolute as isAbsolute6, join as join18, resolve as resolve9 } from "node:path";
|
|
30724
31270
|
var HUB_BINDING_SCHEMA = "kxm.hub-binding.v1";
|
|
30725
31271
|
var HubBindingError = class extends Error {
|
|
30726
31272
|
constructor(message) {
|
|
@@ -30736,16 +31282,16 @@ function resolveUserStateRoot(env) {
|
|
|
30736
31282
|
}
|
|
30737
31283
|
if (process.platform === "win32") {
|
|
30738
31284
|
const localAppData = env.LOCALAPPDATA?.trim();
|
|
30739
|
-
const base2 = localAppData && isAbsolute6(localAppData) ? localAppData :
|
|
31285
|
+
const base2 = localAppData && isAbsolute6(localAppData) ? localAppData : join18(homedir2(), "AppData", "Local");
|
|
30740
31286
|
return resolve9(base2, "KXM");
|
|
30741
31287
|
}
|
|
30742
31288
|
if (process.platform === "darwin") return resolve9(homedir2(), "Library", "Application Support", "KXM");
|
|
30743
31289
|
const xdgState = env.XDG_STATE_HOME?.trim();
|
|
30744
|
-
const base = xdgState && isAbsolute6(xdgState) ? xdgState :
|
|
31290
|
+
const base = xdgState && isAbsolute6(xdgState) ? xdgState : join18(homedir2(), ".local", "state");
|
|
30745
31291
|
return resolve9(base, "kxm");
|
|
30746
31292
|
}
|
|
30747
31293
|
function hubBindingFile(env = process.env) {
|
|
30748
|
-
return
|
|
31294
|
+
return join18(resolveUserStateRoot(env), "hub-binding.json");
|
|
30749
31295
|
}
|
|
30750
31296
|
function validateHubUrl(raw) {
|
|
30751
31297
|
let parsed;
|
|
@@ -30765,10 +31311,10 @@ function isIsoTimestamp(value) {
|
|
|
30765
31311
|
}
|
|
30766
31312
|
function readHubBinding(env = process.env) {
|
|
30767
31313
|
const file = hubBindingFile(env);
|
|
30768
|
-
if (!
|
|
31314
|
+
if (!existsSync15(file)) return void 0;
|
|
30769
31315
|
let parsed;
|
|
30770
31316
|
try {
|
|
30771
|
-
parsed = JSON.parse(
|
|
31317
|
+
parsed = JSON.parse(readFileSync13(file, "utf8"));
|
|
30772
31318
|
} catch {
|
|
30773
31319
|
throw new HubBindingError(`malformed hub binding at ${file}`);
|
|
30774
31320
|
}
|
|
@@ -30790,9 +31336,9 @@ function readHubBinding(env = process.env) {
|
|
|
30790
31336
|
}
|
|
30791
31337
|
|
|
30792
31338
|
// plugins/kxm/src/hub-env.ts
|
|
30793
|
-
import { existsSync as
|
|
31339
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync7, readFileSync as readFileSync14, renameSync as renameSync3, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "node:fs";
|
|
30794
31340
|
import { homedir as homedir3 } from "node:os";
|
|
30795
|
-
import { dirname as dirname9, isAbsolute as isAbsolute7, join as
|
|
31341
|
+
import { dirname as dirname9, isAbsolute as isAbsolute7, join as join19, resolve as resolve10 } from "node:path";
|
|
30796
31342
|
var HUB_ENV_SCHEMA = "kxm.hub-env.v1";
|
|
30797
31343
|
var HubEnvError = class extends Error {
|
|
30798
31344
|
constructor(message) {
|
|
@@ -30808,23 +31354,23 @@ function resolveUserStateRoot2(env) {
|
|
|
30808
31354
|
}
|
|
30809
31355
|
if (process.platform === "win32") {
|
|
30810
31356
|
const localAppData = env.LOCALAPPDATA?.trim();
|
|
30811
|
-
const base2 = localAppData && isAbsolute7(localAppData) ? localAppData :
|
|
31357
|
+
const base2 = localAppData && isAbsolute7(localAppData) ? localAppData : join19(homedir3(), "AppData", "Local");
|
|
30812
31358
|
return resolve10(base2, "KXM");
|
|
30813
31359
|
}
|
|
30814
31360
|
if (process.platform === "darwin") return resolve10(homedir3(), "Library", "Application Support", "KXM");
|
|
30815
31361
|
const xdgState = env.XDG_STATE_HOME?.trim();
|
|
30816
|
-
const base = xdgState && isAbsolute7(xdgState) ? xdgState :
|
|
31362
|
+
const base = xdgState && isAbsolute7(xdgState) ? xdgState : join19(homedir3(), ".local", "state");
|
|
30817
31363
|
return resolve10(base, "kxm");
|
|
30818
31364
|
}
|
|
30819
31365
|
function hubEnvFile(env = process.env) {
|
|
30820
|
-
return
|
|
31366
|
+
return join19(resolveUserStateRoot2(env), "hub-env.json");
|
|
30821
31367
|
}
|
|
30822
31368
|
function readHubEnvRecord(env = process.env) {
|
|
30823
31369
|
const file = hubEnvFile(env);
|
|
30824
|
-
if (!
|
|
31370
|
+
if (!existsSync16(file)) return void 0;
|
|
30825
31371
|
let parsed;
|
|
30826
31372
|
try {
|
|
30827
|
-
parsed = JSON.parse(
|
|
31373
|
+
parsed = JSON.parse(readFileSync14(file, "utf8"));
|
|
30828
31374
|
} catch (error) {
|
|
30829
31375
|
throw new HubEnvError(`hub env file is malformed at ${file}: ${error instanceof Error ? error.message : String(error)}`);
|
|
30830
31376
|
}
|
|
@@ -30864,7 +31410,7 @@ function resolveClientHubAuthToken(env, project) {
|
|
|
30864
31410
|
}
|
|
30865
31411
|
|
|
30866
31412
|
// plugins/kxm/src/logger.ts
|
|
30867
|
-
import { appendFileSync, existsSync as
|
|
31413
|
+
import { appendFileSync, existsSync as existsSync17, mkdirSync as mkdirSync8, renameSync as renameSync4, statSync as statSync5, unlinkSync as unlinkSync2 } from "node:fs";
|
|
30868
31414
|
import { dirname as dirname10 } from "node:path";
|
|
30869
31415
|
var LOG_LEVEL_PRIORITY = {
|
|
30870
31416
|
debug: 10,
|
|
@@ -30902,7 +31448,7 @@ function redactLogValue(val, key) {
|
|
|
30902
31448
|
function rotateLogFiles(filePath, maxFiles) {
|
|
30903
31449
|
for (let i = maxFiles; i >= 1; i--) {
|
|
30904
31450
|
const current = `${filePath}.${i}`;
|
|
30905
|
-
if (
|
|
31451
|
+
if (existsSync17(current)) {
|
|
30906
31452
|
if (i >= maxFiles) {
|
|
30907
31453
|
try {
|
|
30908
31454
|
unlinkSync2(current);
|
|
@@ -30916,7 +31462,7 @@ function rotateLogFiles(filePath, maxFiles) {
|
|
|
30916
31462
|
}
|
|
30917
31463
|
}
|
|
30918
31464
|
}
|
|
30919
|
-
if (
|
|
31465
|
+
if (existsSync17(filePath)) {
|
|
30920
31466
|
try {
|
|
30921
31467
|
renameSync4(filePath, `${filePath}.1`);
|
|
30922
31468
|
} catch {
|
|
@@ -30933,7 +31479,7 @@ function createLogger(options) {
|
|
|
30933
31479
|
const shouldStdout = options.stdout ?? !isDaemon;
|
|
30934
31480
|
const correlationDefaults = options.correlation ?? {};
|
|
30935
31481
|
let currentSize = 0;
|
|
30936
|
-
if (filePath &&
|
|
31482
|
+
if (filePath && existsSync17(filePath)) {
|
|
30937
31483
|
try {
|
|
30938
31484
|
currentSize = statSync5(filePath).size;
|
|
30939
31485
|
} catch {
|
|
@@ -31014,7 +31560,7 @@ function createLogger(options) {
|
|
|
31014
31560
|
|
|
31015
31561
|
// plugins/kxm/src/runtime-supervisor.ts
|
|
31016
31562
|
function kxmSupervisorTokenFile(paths) {
|
|
31017
|
-
return
|
|
31563
|
+
return join20(paths.runtimeDir, "supervisor.token");
|
|
31018
31564
|
}
|
|
31019
31565
|
function publishKxmSupervisorToken(paths, token) {
|
|
31020
31566
|
const file = kxmSupervisorTokenFile(paths);
|
|
@@ -31042,7 +31588,7 @@ function readKxmSupervisorToken(paths) {
|
|
|
31042
31588
|
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
31043
31589
|
throw runtimeError("runtime_path_invalid", file, "supervisor token file must be a regular file, not a link");
|
|
31044
31590
|
}
|
|
31045
|
-
const token =
|
|
31591
|
+
const token = readFileSync15(file, "utf8").trim();
|
|
31046
31592
|
return token.length >= 32 ? token : void 0;
|
|
31047
31593
|
}
|
|
31048
31594
|
function processAlive(pid) {
|
|
@@ -31056,11 +31602,11 @@ function processAlive(pid) {
|
|
|
31056
31602
|
var HEARTBEAT_STALE_MS = 15e3;
|
|
31057
31603
|
var SUPERVISOR_ERROR_MAX_AGE_MS = 3e4;
|
|
31058
31604
|
function supervisorErrorFile(paths) {
|
|
31059
|
-
return
|
|
31605
|
+
return join20(paths.runtimeDir, "supervisor.error");
|
|
31060
31606
|
}
|
|
31061
31607
|
function clearSupervisorError(paths) {
|
|
31062
31608
|
const file = supervisorErrorFile(paths);
|
|
31063
|
-
if (
|
|
31609
|
+
if (existsSync18(file)) rmSync4(file, { force: true });
|
|
31064
31610
|
}
|
|
31065
31611
|
function recordSupervisorError(paths, message) {
|
|
31066
31612
|
try {
|
|
@@ -31077,7 +31623,7 @@ function readRecentSupervisorError(paths) {
|
|
|
31077
31623
|
const ageMs = Date.now() - stat.mtimeMs;
|
|
31078
31624
|
if (ageMs > SUPERVISOR_ERROR_MAX_AGE_MS) return void 0;
|
|
31079
31625
|
try {
|
|
31080
|
-
return
|
|
31626
|
+
return readFileSync15(file, "utf8").trim();
|
|
31081
31627
|
} catch {
|
|
31082
31628
|
return void 0;
|
|
31083
31629
|
}
|
|
@@ -31116,7 +31662,7 @@ function supervisorStatusOf(record2) {
|
|
|
31116
31662
|
};
|
|
31117
31663
|
}
|
|
31118
31664
|
function kxmSupervisorStatus(paths, options = {}) {
|
|
31119
|
-
if (!
|
|
31665
|
+
if (!existsSync18(paths.registryDb)) return { running: false };
|
|
31120
31666
|
if (options.readOnly) {
|
|
31121
31667
|
const database = openReadOnlyDatabase(paths.registryDb);
|
|
31122
31668
|
try {
|
|
@@ -31182,7 +31728,7 @@ async function ensureKxmSupervisor(options = {}) {
|
|
|
31182
31728
|
throw runtimeError("runtime_supervisor_unreachable", paths.registryDb, `runtime supervisor pid ${status.pid} is registered as running but cannot be probed`);
|
|
31183
31729
|
}
|
|
31184
31730
|
clearSupervisorError(paths);
|
|
31185
|
-
const scriptPath =
|
|
31731
|
+
const scriptPath = join20(findKxmRepoRoot(import.meta.url), "scripts", "kxm-runtime-supervisor.mjs");
|
|
31186
31732
|
const spawnImpl = options.spawnImpl ?? ((script, env) => {
|
|
31187
31733
|
const child = spawn3(process.execPath, [script], {
|
|
31188
31734
|
detached: true,
|
|
@@ -31405,7 +31951,7 @@ function syncFailureText(error) {
|
|
|
31405
31951
|
return `${code ? `${code}: ` : ""}${message}`.replace(/(kxm_[A-Za-z0-9_]+|[A-Za-z0-9._-]{40,})/g, "[redacted]").slice(0, 300);
|
|
31406
31952
|
}
|
|
31407
31953
|
function runtimeSupervisorLogFile(paths) {
|
|
31408
|
-
return
|
|
31954
|
+
return join20(paths.runtimeDir, "logs", "kxm-runtime.jsonl");
|
|
31409
31955
|
}
|
|
31410
31956
|
async function startKxmRuntimeSupervisor(options = {}) {
|
|
31411
31957
|
const now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
@@ -31530,8 +32076,8 @@ async function startKxmRuntimeSupervisorInner(paths, requestedPortOption, now, e
|
|
|
31530
32076
|
openRunIds = unsettledRunIds(existing2);
|
|
31531
32077
|
} else {
|
|
31532
32078
|
const registration = registry.projectByRoot(projectRoot);
|
|
31533
|
-
const eventsPath =
|
|
31534
|
-
if (registration &&
|
|
32079
|
+
const eventsPath = join20(paths.projectsDir, registration?.projectKey ?? projectRuntimeKey(projectRoot), "run-events.db");
|
|
32080
|
+
if (registration && existsSync18(eventsPath)) {
|
|
31535
32081
|
const store = new KxmRunEventStore(eventsPath);
|
|
31536
32082
|
try {
|
|
31537
32083
|
openRunIds = store.runsForProject(registration.projectId, 1e4).filter((run) => !SETTLED_RUN_STATUS.has(run.status)).map((run) => run.runId);
|