@kontextmind/kxm 0.7.130 → 0.7.132

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.
Files changed (54) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.kxm/README.md +4 -1
  3. package/.kxm/agents/coordinator.yaml +1 -0
  4. package/.kxm/agents/critic-arch.yaml +1 -4
  5. package/.kxm/agents/critic-cli.yaml +1 -4
  6. package/.kxm/agents/implementer.yaml +1 -4
  7. package/.kxm/workflows/implement-only.yaml +1 -1
  8. package/.kxm/workflows/review-arch-only.yaml +1 -1
  9. package/.kxm/workflows/review-cli-only.yaml +1 -1
  10. package/CHANGELOG.md +22 -1
  11. package/docs/concepts/architecture.md +1 -1
  12. package/docs/contracts/routing.md +3 -3
  13. package/docs/contributing/assignment-runner.md +14 -15
  14. package/docs/contributing/development.md +19 -5
  15. package/docs/contributing/harness-routing-internals.md +8 -8
  16. package/docs/glossary.md +1 -1
  17. package/docs/guides/agent-skills.md +1 -1
  18. package/docs/operations/backup-and-restore.md +4 -4
  19. package/docs/reference/cli-reference.md +26 -8
  20. package/docs/reference/config-reference.md +76 -153
  21. package/docs/reference/harness-routing.md +29 -21
  22. package/docs/reference/workflow-catalog.md +4 -4
  23. package/docs/start/first-workflow.md +1 -1
  24. package/examples/project/.kxm/agents/coordinator.yaml +1 -2
  25. package/examples/project/.kxm/agents/critic-1.yaml +1 -3
  26. package/examples/project/.kxm/agents/critic-2.yaml +1 -2
  27. package/examples/project/.kxm/agents/critic-3.yaml +2 -3
  28. package/examples/project/.kxm/agents/implementer.yaml +1 -2
  29. package/examples/project/.kxm/agents/planner.yaml +1 -2
  30. package/examples/project/.kxm/agents/reproducer.yaml +1 -2
  31. package/examples/project/.kxm/agents/reviewer.yaml +1 -2
  32. package/examples/project/.kxm/workflows/fix.yaml +0 -12
  33. package/package.json +1 -1
  34. package/plugins/kxm/.claude-plugin/plugin.json +1 -1
  35. package/plugins/kxm/dist/cli.js +287 -249
  36. package/plugins/kxm/dist/mcp-server.js +1 -1
  37. package/plugins/kxm/dist/runtime-supervisor.js +222 -242
  38. package/plugins/kxm/dist/runtime.js +224 -244
  39. package/plugins/kxm/package.json +1 -1
  40. package/plugins/kxm/skills/kxm/SKILL.md +1 -1
  41. package/plugins/kxm/skills/kxm-definitions/SKILL.md +8 -0
  42. package/plugins/kxm/src/cli/project.ts +4 -4
  43. package/plugins/kxm/src/engine.ts +168 -163
  44. package/plugins/kxm/src/init-guide-setup.ts +75 -15
  45. package/plugins/kxm/src/mcp-server.ts +1 -1
  46. package/plugins/kxm/src/oneshot-producer.ts +3 -15
  47. package/plugins/kxm/src/project-config.ts +83 -70
  48. package/plugins/kxm/src/routes.ts +26 -18
  49. package/plugins/kxm/src/runtime-supervisor.ts +0 -18
  50. package/plugins/kxm/src/suggest.ts +2 -2
  51. package/plugins/kxm/src/template.ts +2 -14
  52. package/schemas/agent.schema.json +8 -0
  53. package/scripts/roster-policy.d.mts +2 -1
  54. package/scripts/roster-policy.mjs +119 -22
@@ -15636,21 +15636,9 @@ function coreTemplate(projectId, projectName, variant) {
15636
15636
  ]);
15637
15637
  if (variant === "v4-registry") {
15638
15638
  const coordinator = files.get(".kxm/agents/coordinator.yaml");
15639
- if (coordinator) {
15640
- files.set(".kxm/agents/coordinator.yaml", {
15641
- ...coordinator,
15642
- harness: "claude",
15643
- model: { provider: "anthropic", model: "fable" }
15644
- });
15645
- }
15639
+ if (coordinator) files.set(".kxm/agents/coordinator.yaml", { ...coordinator, role: "planner" });
15646
15640
  const implementer = files.get(".kxm/agents/implementer.yaml");
15647
- if (implementer) {
15648
- files.set(".kxm/agents/implementer.yaml", {
15649
- ...implementer,
15650
- harness: "grok",
15651
- model: { provider: "xai", model: "grok-4.6" }
15652
- });
15653
- }
15641
+ if (implementer) files.set(".kxm/agents/implementer.yaml", { ...implementer, role: "writer" });
15654
15642
  const workflow = files.get(".kxm/workflows/default.yaml");
15655
15643
  if (workflow) {
15656
15644
  const limits = { ...workflow.limits };
@@ -17486,7 +17474,7 @@ function readTemplateProvenance(registry, root) {
17486
17474
  }
17487
17475
  return value;
17488
17476
  }
17489
- function listNamedResources(registry, root, directory, logicalDirectory, kind, replacedId) {
17477
+ function listNamedResources(registry, root, directory, logicalDirectory, kind, replacedId, collected) {
17490
17478
  const resources = /* @__PURE__ */ new Map();
17491
17479
  if (!existsSync3(directory)) return resources;
17492
17480
  const stat = lstatSync(directory);
@@ -17525,7 +17513,10 @@ function listNamedResources(registry, root, directory, logicalDirectory, kind, r
17525
17513
  else throw error;
17526
17514
  }
17527
17515
  }
17528
- if (issues.length > 0) throw new KxmConfigError(issues);
17516
+ if (issues.length > 0) {
17517
+ if (collected) collected.push(...issues);
17518
+ else throw new KxmConfigError(issues);
17519
+ }
17529
17520
  return resources;
17530
17521
  }
17531
17522
  function valuesOf(object, field) {
@@ -17789,6 +17780,9 @@ function validateWorkflow(workflow, agents, models, repositories, gates, issues)
17789
17780
  for (const repositoryId of Object.keys(objectValue(step.repositories) ?? {})) {
17790
17781
  if (!repositories.has(repositoryId)) issues.push(issue3("reference", "repository_unknown", file, `${stepId} references unknown repository ${repositoryId}`));
17791
17782
  }
17783
+ if ((kind === "agent" || kind === "moa") && step.model !== void 0) {
17784
+ issues.push(issue3("semantic", "producer_route_unsupported", file, `${stepId} model is not honored; remove model from the step`));
17785
+ }
17792
17786
  selectorCandidates(step.model, models, file, `${stepId}.model`, issues);
17793
17787
  const assignment = objectValue(step.assignments);
17794
17788
  const declaredAgents = assignment ? names(assignment.allowedAgents) : [];
@@ -17971,7 +17965,7 @@ function validateModelReferences(models, issues) {
17971
17965
  };
17972
17966
  for (const id of models.keys()) walk(id, []);
17973
17967
  }
17974
- function validateBundle(project, repositories, agents, models, workflows, environments, options, gateRegistry, projectRoot, writerRole) {
17968
+ function validateBundle(project, repositories, agents, models, workflows, environments, options, gateRegistry, projectRoot, roles = /* @__PURE__ */ new Map()) {
17975
17969
  const issues = [];
17976
17970
  const entries = valuesOf(project.value, "repositories").map((candidate) => objectValue(candidate)).filter((candidate) => Boolean(candidate));
17977
17971
  const repositoryIds = /* @__PURE__ */ new Set();
@@ -18012,84 +18006,84 @@ function validateBundle(project, repositories, agents, models, workflows, enviro
18012
18006
  const defaultHarness = stringValue(project.value.defaultHarness) ?? DEFAULT_HARNESS;
18013
18007
  if (!harnesses.has(defaultHarness)) issues.push(issue3("reference", "harness_unknown", project.logicalPath, `defaultHarness ${defaultHarness} is not registered`));
18014
18008
  for (const agent of agents.values()) {
18009
+ if (agent.value.model !== void 0 || agent.value.harness !== void 0) {
18010
+ issues.push(issue3("semantic", "retired_agent_routing_fields", agent.logicalPath, "routing resolves from role; remove model and harness"));
18011
+ }
18015
18012
  validateToolPolicy(agent, objectValue(agent.value.tools), "tools", issues);
18016
18013
  const executor = stringValue(agent.value.executor);
18017
18014
  if (executor && !executors.has(executor)) issues.push(issue3("reference", "executor_unknown", agent.logicalPath, `executor ${executor} is not registered`));
18018
- const harness = stringValue(agent.value.harness) ?? defaultHarness;
18019
- if (!harnesses.has(harness)) issues.push(issue3("reference", "harness_unknown", agent.logicalPath, `harness ${harness} is not registered`));
18020
18015
  const preset = stringValue(objectValue(agent.value.tools)?.preset);
18021
18016
  if (preset && !presets.has(preset)) issues.push(issue3("reference", "tool_preset_unknown", agent.logicalPath, `tool preset ${preset} is not registered`));
18022
18017
  for (const repositoryId of Object.keys(objectValue(agent.value.repositories) ?? {})) {
18023
18018
  if (!repositoryIds.has(repositoryId)) issues.push(issue3("reference", "repository_unknown", agent.logicalPath, `references unknown repository ${repositoryId}`));
18024
18019
  }
18025
- const candidates = selectorCandidates(agent.value.model, models, agent.logicalPath, "model", issues);
18026
- const declaredHarness = stringValue(agent.value.harness);
18027
- if (declaredHarness && harnesses.has(declaredHarness)) {
18028
- for (const candidate of candidates) {
18029
- const candidateProvider = stringValue(candidate.value.provider);
18030
- const candidateModel = stringValue(candidate.value.model);
18031
- const validation = validateHarnessModelPair(declaredHarness, { provider: candidateProvider, model: candidateModel });
18032
- if (!validation.valid) {
18033
- issues.push(issue3("semantic", validation.issue ?? "harness_unhosted_model", agent.logicalPath, validation.message ?? `harness ${declaredHarness} cannot host model ${candidateModel ?? candidate.logicalPath}`));
18034
- }
18035
- }
18036
- }
18037
18020
  }
18038
18021
  validateModelReferences(models, issues);
18039
18022
  const defaultWorkflow = stringValue(project.value.defaultWorkflow) ?? "default";
18040
18023
  if (!workflows.has(defaultWorkflow)) issues.push(issue3("reference", "default_workflow_unknown", project.logicalPath, `default workflow ${defaultWorkflow} does not exist`));
18041
18024
  for (const workflow of workflows.values()) validateWorkflow(workflow, agents, models, repositoryIds, gates, issues);
18025
+ if (projectRoot && existsSync3(join2(projectRoot, ".kxm", "roster.yaml"))) {
18026
+ issues.push(issue3("semantic", "retired_roster_file", ".kxm/roster.yaml", "create the role and model files and delete roster.yaml"));
18027
+ }
18028
+ if (projectRoot && existsSync3(join2(projectRoot, ".kxm", "roles"))) {
18029
+ issues.push(...developerRolePolicyIssues(projectRoot));
18030
+ }
18042
18031
  if (projectRoot) {
18043
- const writerRolePath = join2(projectRoot, ".kxm", "roles", "writer.yaml");
18044
- const implementerAgent = agents.get("implementer") ?? agents.get("writer");
18045
- if ((writerRole !== void 0 || existsSync3(writerRolePath)) && implementerAgent) {
18032
+ issues.push(...rosterModelIssues(projectRoot, roles));
18033
+ issues.push(...originHashIssues(projectRoot, models));
18034
+ }
18035
+ return sortIssues3(issues);
18036
+ }
18037
+ function rosterModelIssues(projectRoot, roles) {
18038
+ const issues = [];
18039
+ for (const role of roles.values()) {
18040
+ for (const entry of valuesOf(role.value, "roster")) {
18041
+ const route = stringValue(objectValue(entry)?.route);
18042
+ if (!route) continue;
18043
+ const logical = `.kxm/models/${route}.yaml`;
18044
+ const file = join2(projectRoot, ".kxm", "models", `${route}.yaml`);
18045
+ let harness;
18046
18046
  try {
18047
- const rawRole = parseRestrictedYaml2(writerRole ?? readFileSync(writerRolePath, "utf8"));
18048
- const roleObj = objectValue(rawRole);
18049
- const rosterEntries = valuesOf(roleObj ?? {}, "roster").map((candidate) => objectValue(candidate)).filter((entry) => Boolean(entry));
18050
- const enabledRosterModels = rosterEntries.flatMap((entry) => {
18051
- const direct = stringValue(entry.model);
18052
- const route = stringValue(entry.route);
18053
- const named = direct ? [direct] : [];
18054
- if (route) {
18055
- const modelFile = join2(projectRoot, ".kxm", "models", `${route}.yaml`);
18056
- if (existsSync3(modelFile)) {
18057
- try {
18058
- const modelDoc = objectValue(parseRestrictedYaml2(readFileSync(modelFile, "utf8"), modelFile));
18059
- const model = modelDoc ? stringValue(modelDoc.model) : void 0;
18060
- const vendor = modelDoc ? stringValue(modelDoc.vendor) : void 0;
18061
- const harness = modelDoc ? stringValue(modelDoc.harness) : void 0;
18062
- if (model) named.push(model);
18063
- if (vendor && model) named.push(`${vendor}/${model}`);
18064
- if (harness && model) named.push(`${harness}/${model}`);
18065
- } catch {
18066
- }
18067
- }
18068
- }
18069
- return named;
18070
- });
18071
- const agentModelObj = objectValue(implementerAgent.value.model);
18072
- const agentModelStr = stringValue(implementerAgent.value.model);
18073
- const agentProvider = agentModelObj ? stringValue(agentModelObj.provider) : void 0;
18074
- const agentModel = agentModelObj ? stringValue(agentModelObj.model) : agentModelStr;
18075
- const canonicalAgentModel = agentProvider && agentModel ? `${agentProvider}/${agentModel}` : agentModel;
18076
- if (enabledRosterModels.length > 0 && canonicalAgentModel) {
18077
- const matches = enabledRosterModels.some((rm) => rm === canonicalAgentModel || rm === agentModel || rm.endsWith(`/${agentModel}`));
18078
- if (!matches) {
18079
- issues.push(issue3("semantic", "role_roster_conflicts_with_agent", ".kxm/roles/writer.yaml", `role roster in .kxm/roles/writer.yaml does not include agent model ${canonicalAgentModel} from ${implementerAgent.logicalPath}`));
18080
- }
18081
- }
18082
- } catch (error) {
18083
- if (error instanceof KxmConfigError) {
18084
- issues.push(...error.issues);
18085
- }
18047
+ if (existsSync3(file)) harness = stringValue(parseRestrictedYaml2(readFileSync(file, "utf8"), logical).harness);
18048
+ } catch {
18049
+ harness = void 0;
18050
+ }
18051
+ if (!harness) {
18052
+ issues.push(issue3("semantic", "roster_model_unreadable", role.logicalPath, `roster route ${route} does not resolve to a readable ${logical} carrying harness`));
18086
18053
  }
18087
18054
  }
18088
18055
  }
18089
- if (projectRoot && existsSync3(join2(projectRoot, ".kxm", "roster.yaml"))) {
18090
- issues.push(...developerRolePolicyIssues(projectRoot));
18056
+ return issues;
18057
+ }
18058
+ function originHashIssues(projectRoot, models) {
18059
+ const issues = [];
18060
+ for (const model of models.values()) {
18061
+ const origin = objectValue(model.value.origin);
18062
+ const source = origin ? stringValue(origin.source) : void 0;
18063
+ const digest2 = origin ? stringValue(origin.sha256) : void 0;
18064
+ if (!source || !digest2) continue;
18065
+ const file = resolve(projectRoot, source);
18066
+ const escaped = relative(projectRoot, file);
18067
+ if (isAbsolute(source) || escaped.startsWith("..") || isAbsolute(escaped)) {
18068
+ issues.push(issue3("semantic", "origin_evidence_missing", model.logicalPath, `missing evidence bytes for ${source}`));
18069
+ continue;
18070
+ }
18071
+ let actual;
18072
+ try {
18073
+ actual = createHash3("sha256").update(readFileSync(file, "utf8"), "utf8").digest("hex");
18074
+ } catch (error) {
18075
+ const code = error.code;
18076
+ if (code === "ENOENT" || code === "EACCES" || code === "EPERM" || code === "EISDIR") {
18077
+ issues.push(issue3("semantic", "origin_evidence_missing", model.logicalPath, `missing evidence bytes for ${source}`));
18078
+ continue;
18079
+ }
18080
+ throw error;
18081
+ }
18082
+ if (actual !== digest2) {
18083
+ issues.push(issue3("semantic", "origin_hash_mismatch", model.logicalPath, "origin evidence hash does not match supplied bytes"));
18084
+ }
18091
18085
  }
18092
- return sortIssues3(issues);
18086
+ return issues;
18093
18087
  }
18094
18088
  function developerCeilings() {
18095
18089
  const script = join2(findKxmRepoRoot(import.meta.url), "scripts", "harness-run.mjs");
@@ -18118,6 +18112,7 @@ function developerCeilings() {
18118
18112
  }
18119
18113
  }
18120
18114
  function developerRolePolicyIssues(projectRoot) {
18115
+ if (resolve(projectRoot) !== findKxmRepoRoot(import.meta.url)) return [];
18121
18116
  const rolesDir = join2(projectRoot, ".kxm", "roles");
18122
18117
  const modelsDir = join2(projectRoot, ".kxm", "models");
18123
18118
  const roles = {};
@@ -18138,6 +18133,7 @@ function developerRolePolicyIssues(projectRoot) {
18138
18133
  parseIssues.push(...error.issues);
18139
18134
  continue;
18140
18135
  }
18136
+ throw error;
18141
18137
  }
18142
18138
  }
18143
18139
  };
@@ -18152,7 +18148,7 @@ function developerRolePolicyIssues(projectRoot) {
18152
18148
  }
18153
18149
  const ceilings = developerCeilings();
18154
18150
  if (!ceilings.ok) {
18155
- return [...parseIssues, issue3("semantic", "developer_ceilings_unavailable", ".kxm/roster.yaml", `developer ceilings could not be loaded: ${ceilings.detail}`)];
18151
+ return [...parseIssues, issue3("semantic", "developer_ceilings_unavailable", ".kxm/models", `developer ceilings could not be loaded: ${ceilings.detail}`)];
18156
18152
  }
18157
18153
  const result = validatePolicyDraft({ models, roles, evidence }, {
18158
18154
  ceilings: ceilings.ROUTES,
@@ -18241,7 +18237,8 @@ function loadProjectBundle(projectRoot, options, candidate) {
18241
18237
  }
18242
18238
  if (earlyIssues.length > 0) throw new KxmConfigError(earlyIssues);
18243
18239
  const agents = listNamedResources(registry, root, join2(root, ".kxm", "agents"), ".kxm/agents", "agent");
18244
- const models = listNamedResources(registry, root, join2(root, ".kxm", "models"), ".kxm/models", "model");
18240
+ const modelIssues = [];
18241
+ const models = listNamedResources(registry, root, join2(root, ".kxm", "models"), ".kxm/models", "model", void 0, modelIssues);
18245
18242
  const roles = listNamedResources(registry, root, join2(root, ".kxm", "roles"), ".kxm/roles", "role", writerRole === void 0 ? void 0 : "writer");
18246
18243
  if (writerRole !== void 0) {
18247
18244
  const logicalPath = ".kxm/roles/writer.yaml";
@@ -18368,8 +18365,8 @@ function loadProjectBundle(projectRoot, options, candidate) {
18368
18365
  }
18369
18366
  }
18370
18367
  }
18371
- if (loadIssues.length > 0) throw new KxmConfigError(loadIssues);
18372
- const issues = validateBundle(project, repositories, agents, models, workflows, environments, options, gateRegistry, root, writerRole);
18368
+ if (loadIssues.length > 0) throw new KxmConfigError([...loadIssues, ...modelIssues]);
18369
+ const issues = [...modelIssues, ...validateBundle(project, repositories, agents, models, workflows, environments, options, gateRegistry, root, roles)];
18373
18370
  if (issues.length > 0) throw new KxmConfigError(issues);
18374
18371
  const resources = [project, ...repositories.values(), ...agents.values(), ...models.values(), ...roles.values(), ...workflows.values(), ...environments, ...gateRegistry ? [gateRegistry] : []].sort((left, right) => compareCodeUnits4(left.logicalPath, right.logicalPath));
18375
18372
  return {
@@ -25206,14 +25203,14 @@ var import_yaml4 = __toESM(require_dist(), 1);
25206
25203
 
25207
25204
  // plugins/kxm/src/routes.ts
25208
25205
  var RETIRED_POLICY = ".kxm/producers.yaml";
25209
- var empty = () => ({ schema: "kxm.routes.v2", updatedAt: (/* @__PURE__ */ new Date()).toISOString(), admitted: [], disabled: [], roles: {} });
25206
+ var empty = () => ({ schema: "kxm.routes.v2", updatedAt: (/* @__PURE__ */ new Date()).toISOString(), admitted: [], disabled: [] });
25210
25207
  function loadRoutePolicy(root) {
25211
25208
  if (existsSync9(join10(root, RETIRED_POLICY))) throw new Error(`retired ${RETIRED_POLICY} present; use .kxm/routes.yaml (kxm.routes.v2)`);
25212
25209
  const path = join10(root, ".kxm", "routes.yaml");
25213
25210
  if (!existsSync9(path)) return empty();
25214
25211
  const value = (0, import_yaml6.parse)(readFileSync7(path, "utf8"));
25215
25212
  if (value?.schema !== "kxm.routes.v2" || !Array.isArray(value.admitted)) throw new Error("invalid .kxm/routes.yaml");
25216
- return { schema: "kxm.routes.v2", updatedAt: typeof value.updatedAt === "string" ? value.updatedAt : (/* @__PURE__ */ new Date()).toISOString(), admitted: value.admitted.filter((x) => typeof x === "string"), disabled: Array.isArray(value.disabled) ? value.disabled.filter((x) => typeof x === "string") : [], roles: value.roles && typeof value.roles === "object" ? Object.fromEntries(Object.entries(value.roles).filter(([, v]) => Array.isArray(v)).map(([k, v]) => [k, v.filter((x) => typeof x === "string")])) : {} };
25213
+ 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") : [] };
25217
25214
  }
25218
25215
  function listRoleBindings(root) {
25219
25216
  const dir = join10(root, ".kxm", "roles");
@@ -25226,10 +25223,6 @@ function listRoleBindings(root) {
25226
25223
  }
25227
25224
  return result;
25228
25225
  }
25229
- function isRouteAdmitted(root, modelId) {
25230
- const policy = loadRoutePolicy(root);
25231
- return policy.admitted.includes(modelId) && !policy.disabled.includes(modelId);
25232
- }
25233
25226
 
25234
25227
  // plugins/kxm/src/worktree-witness.ts
25235
25228
  import { spawnSync as spawnSync4 } from "node:child_process";
@@ -28818,25 +28811,18 @@ function commitCommandUncertainty(context, prepared, token, reason, observation,
28818
28811
  }
28819
28812
  }
28820
28813
  var kxmPanelDispatchSeams = {};
28821
- function selectorsNamedByRoutes(projectRoot, routeIds) {
28822
- const selectors = /* @__PURE__ */ new Set();
28823
- for (const routeId2 of routeIds) {
28824
- const file = join15(projectRoot, ".kxm", "models", `${routeId2}.yaml`);
28825
- if (!existsSync12(file)) continue;
28826
- let parsed;
28827
- try {
28828
- parsed = (0, import_yaml9.parse)(readFileSync10(file, "utf8"));
28829
- } catch {
28830
- continue;
28831
- }
28832
- const model = typeof parsed.model === "string" ? parsed.model : "";
28833
- const vendor = typeof parsed.vendor === "string" ? parsed.vendor : "";
28834
- const harness = typeof parsed.harness === "string" ? parsed.harness : "";
28835
- if (model) selectors.add(model);
28836
- if (vendor && model) selectors.add(`${vendor}/${model}`);
28837
- if (harness && model) selectors.add(`${harness}/${model}`);
28814
+ function readYamlFile(file) {
28815
+ return readModelDocument(file);
28816
+ }
28817
+ function readModelDocument(file) {
28818
+ if (!existsSync12(file)) return void 0;
28819
+ try {
28820
+ const parsed = (0, import_yaml9.parse)(readFileSync10(file, "utf8"));
28821
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return void 0;
28822
+ return parsed;
28823
+ } catch {
28824
+ return void 0;
28838
28825
  }
28839
- return [...selectors];
28840
28826
  }
28841
28827
  function unreconciledPanelAttemptId(state) {
28842
28828
  const current = state.currentStep;
@@ -28853,82 +28839,118 @@ function unreconciledPanelAttemptId(state) {
28853
28839
  return void 0;
28854
28840
  }
28855
28841
  function resolveProducerRoute(projectRoot, step, agentId) {
28856
- let agentModel;
28857
- const agentFile = join15(projectRoot, ".kxm", "agents", `${agentId}.yaml`);
28858
- if (existsSync12(agentFile)) {
28859
- try {
28860
- const parsed = (0, import_yaml9.parse)(readFileSync10(agentFile, "utf8"));
28861
- if (typeof parsed?.model === "string") {
28862
- agentModel = parsed.model;
28863
- } else if (parsed?.model && typeof parsed.model === "object" && !Array.isArray(parsed.model)) {
28864
- const declared = parsed.model;
28865
- const providerOk = typeof declared.provider === "string" && declared.provider.length > 0 && !declared.provider.includes("/");
28866
- const modelOk = typeof declared.model === "string" && declared.model.length > 0 && !declared.model.startsWith("/") && !declared.model.endsWith("/");
28867
- if (providerOk && modelOk) {
28868
- agentModel = `${declared.provider}/${declared.model}`;
28869
- } else {
28870
- return {
28871
- error: {
28872
- reason: "step_unsupported",
28873
- field: "model",
28874
- detail: "producer_route_unsupported: invalid model declaration"
28875
- }
28876
- };
28877
- }
28878
- }
28879
- } catch {
28880
- }
28881
- }
28882
- let selector = typeof step.model === "string" ? step.model : agentModel;
28883
- if (!selector) {
28884
- if (agentId === "implementer") {
28885
- selector = "xai/grok-4.6";
28886
- }
28887
- }
28888
- if (!selector) {
28842
+ if ((step.kind === "agent" || step.kind === "moa") && step.model !== void 0) {
28889
28843
  return {
28890
28844
  error: {
28891
28845
  reason: "step_unsupported",
28892
28846
  field: "model",
28893
- detail: "producer_route_unsupported: agent or step has no model declared"
28847
+ detail: "producer_route_unsupported: agent step model is not honored"
28894
28848
  }
28895
28849
  };
28896
28850
  }
28897
- const slash = selector.indexOf("/");
28898
- if (slash <= 0 || slash === selector.length - 1) {
28851
+ const agent = readYamlFile(join15(projectRoot, ".kxm", "agents", `${agentId}.yaml`));
28852
+ const role = typeof agent?.role === "string" ? agent.role : "";
28853
+ if (!role) {
28899
28854
  return {
28900
28855
  error: {
28901
28856
  reason: "step_unsupported",
28902
28857
  field: "model",
28903
- detail: `producer_route_unsupported: invalid selector '${selector}'`
28858
+ detail: "producer_route_unsupported: agent has no role"
28904
28859
  }
28905
28860
  };
28906
28861
  }
28907
- if (!isRouteAdmitted(projectRoot, selector)) {
28862
+ const roleDoc = readYamlFile(join15(projectRoot, ".kxm", "roles", `${role}.yaml`));
28863
+ const roster = Array.isArray(roleDoc?.roster) ? roleDoc.roster : void 0;
28864
+ if (!roster) {
28908
28865
  return {
28909
28866
  error: {
28910
28867
  reason: "step_unsupported",
28911
28868
  field: "model",
28912
- detail: `producer_route_unsupported: model '${selector}' is not admitted`
28869
+ detail: `producer_route_unsupported: role '${role}' has no roster`
28913
28870
  }
28914
28871
  };
28915
28872
  }
28916
- const role = agentId === "implementer" ? "writer" : agentId;
28917
- const roleBindings = listRoleBindings(projectRoot);
28918
- const roster = roleBindings[role];
28919
- const rosterSelectors = roster ? selectorsNamedByRoutes(projectRoot, roster) : void 0;
28920
- if (rosterSelectors && !rosterSelectors.includes(selector)) {
28873
+ const policy = loadRoutePolicy(projectRoot);
28874
+ for (const entry of roster) {
28875
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) continue;
28876
+ const record2 = entry;
28877
+ if (typeof record2.route !== "string" || record2.route.length === 0) continue;
28878
+ const modelPath = join15(projectRoot, ".kxm", "models", `${record2.route}.yaml`);
28879
+ const doc = readModelDocument(modelPath);
28880
+ if (!doc) {
28881
+ return {
28882
+ error: {
28883
+ reason: "step_unsupported",
28884
+ field: "model",
28885
+ detail: `producer_route_unsupported: model file '.kxm/models/${record2.route}.yaml' is missing or unreadable`
28886
+ }
28887
+ };
28888
+ }
28889
+ if (doc.status !== "admitted") continue;
28890
+ const modelName = typeof doc.model === "string" ? doc.model : "";
28891
+ const vendor = typeof doc.vendor === "string" ? doc.vendor : "";
28892
+ const harness = typeof doc.harness === "string" ? doc.harness : "";
28893
+ if (!modelName || !vendor || !harness) continue;
28894
+ const candidates = [modelName, `${vendor}/${modelName}`, `${harness}/${modelName}`];
28895
+ const admittedSelector = candidates.find((candidate) => policy.admitted.includes(candidate));
28896
+ const disabledSelector = candidates.find((candidate) => policy.disabled.includes(candidate));
28897
+ if (!admittedSelector && disabledSelector) {
28898
+ return {
28899
+ error: {
28900
+ reason: "step_unsupported",
28901
+ field: "model",
28902
+ detail: `producer_route_unsupported: first admitted route '${record2.route}' for role '${role}' is disabled`
28903
+ }
28904
+ };
28905
+ }
28906
+ const selector = admittedSelector ?? (modelName.includes("/") ? modelName : `${vendor}/${modelName}`);
28907
+ if (policy.disabled.includes(selector)) {
28908
+ return {
28909
+ error: {
28910
+ reason: "step_unsupported",
28911
+ field: "model",
28912
+ detail: `producer_route_unsupported: first admitted route '${record2.route}' for role '${role}' is disabled`
28913
+ }
28914
+ };
28915
+ }
28916
+ if (!policy.admitted.includes(selector)) {
28917
+ return {
28918
+ error: {
28919
+ reason: "step_unsupported",
28920
+ field: "model",
28921
+ detail: `producer_route_unsupported: model '${selector}' is not admitted`
28922
+ }
28923
+ };
28924
+ }
28925
+ const slash = selector.indexOf("/");
28926
+ if (slash <= 0 || slash === selector.length - 1) {
28927
+ return {
28928
+ error: {
28929
+ reason: "step_unsupported",
28930
+ field: "model",
28931
+ detail: `producer_route_unsupported: invalid selector '${selector}'`
28932
+ }
28933
+ };
28934
+ }
28935
+ const permissions = Array.isArray(doc.permissions) ? doc.permissions.filter((item) => typeof item === "string") : [];
28921
28936
  return {
28922
- error: {
28923
- reason: "step_unsupported",
28924
- field: "model",
28925
- detail: `producer_route_unsupported: model '${selector}' not in role '${role}' roster`
28926
- }
28937
+ provider: selector.slice(0, slash),
28938
+ model: selector.slice(slash + 1),
28939
+ selector,
28940
+ harness,
28941
+ routeId: record2.route,
28942
+ role,
28943
+ permissions,
28944
+ ...typeof record2.effort === "string" ? { effort: record2.effort } : {}
28927
28945
  };
28928
28946
  }
28929
- const provider = selector.slice(0, slash);
28930
- const model = selector.slice(slash + 1);
28931
- return { provider, model, selector };
28947
+ return {
28948
+ error: {
28949
+ reason: "step_unsupported",
28950
+ field: "model",
28951
+ detail: `producer_route_unsupported: role '${role}' has no admitted route`
28952
+ }
28953
+ };
28932
28954
  }
28933
28955
  function projectAgentStepTimeoutMs(context, run) {
28934
28956
  return loadKxmRunPlanEnvelope(context.eventStore, run).projectLimits.agentStepTimeoutMs ?? KXM_DEFAULT_AGENT_STEP_TIMEOUT_MS;
@@ -29146,8 +29168,7 @@ function prepareDispatch(context, runId, producerId, dispatchSources) {
29146
29168
  const writeRefusal = unsupportedLiveWrite(
29147
29169
  context.projectRoot,
29148
29170
  step,
29149
- agentId,
29150
- resolvedRoute.selector,
29171
+ resolvedRoute,
29151
29172
  loadKxmRunPlanEnvelope(context.eventStore, run).projectLimits.maxConcurrentRuns
29152
29173
  );
29153
29174
  if (writeRefusal) return { kind: "return", state, handoff: { ...writeRefusal, stepId } };
@@ -29176,7 +29197,7 @@ function prepareDispatch(context, runId, producerId, dispatchSources) {
29176
29197
  for (const event of events) context.eventStore.appendEvent(event);
29177
29198
  const entered = foldStoredKxmRun(context, run);
29178
29199
  persistKxmRunState(context, runId, entered, events[events.length - 1].sequence);
29179
- const first = birthMember(context, {
29200
+ const firstBirth = birthMember(context, {
29180
29201
  run,
29181
29202
  plan,
29182
29203
  step,
@@ -29187,6 +29208,10 @@ function prepareDispatch(context, runId, producerId, dispatchSources) {
29187
29208
  resolvedRoute,
29188
29209
  dispatchSources
29189
29210
  });
29211
+ if ("handoff" in firstBirth) {
29212
+ return { kind: "return", state: entered, handoff: firstBirth.handoff };
29213
+ }
29214
+ const first = firstBirth;
29190
29215
  return {
29191
29216
  kind: "panel",
29192
29217
  panel: {
@@ -29249,9 +29274,10 @@ function birthMember(context, input) {
29249
29274
  let resolvedRoute = input.resolvedRoute;
29250
29275
  if (!resolvedRoute && input.producerId && input.producerId !== "driver-simulated") {
29251
29276
  const routeResult = resolveProducerRoute(context.projectRoot, input.step, agentId);
29252
- if (!("error" in routeResult)) {
29253
- resolvedRoute = routeResult;
29277
+ if ("error" in routeResult) {
29278
+ return { handoff: { ...routeResult.error, stepId: input.stepId } };
29254
29279
  }
29280
+ resolvedRoute = routeResult;
29255
29281
  }
29256
29282
  const dispatchContext = assembleDispatchContext(input.dispatchSources, {
29257
29283
  projectId: run.projectId,
@@ -29358,11 +29384,11 @@ function birthMember(context, input) {
29358
29384
  prompt: input.step.instructions ? `${input.step.instructions}
29359
29385
 
29360
29386
  ${generatedPrompt}` : generatedPrompt,
29361
- thinking: input.stepAttempt <= 1 ? "low" : "medium",
29362
29387
  permission: Object.values(input.step.repositories).some((access) => access === "write") ? "edit" : "read-only",
29363
29388
  ...(input.step.kind === "agent" || input.step.kind === "moa") && input.step.timeoutMs !== void 0 ? { timeoutMs: input.step.timeoutMs } : {},
29364
29389
  contextPacket,
29365
- ...resolvedRoute ? { provider: resolvedRoute.provider, model: resolvedRoute.model } : {}
29390
+ ...resolvedRoute?.effort !== void 0 ? { thinking: resolvedRoute.effort } : {},
29391
+ ...resolvedRoute ? { harness: resolvedRoute.harness, provider: resolvedRoute.provider, model: resolvedRoute.model } : {}
29366
29392
  },
29367
29393
  controller,
29368
29394
  state: next
@@ -29507,13 +29533,14 @@ async function drivePanel(context, panel, producer) {
29507
29533
  })();
29508
29534
  pending.set(member.attemptId, work);
29509
29535
  };
29536
+ let routeHandoff;
29510
29537
  const tryBirth = () => {
29511
- if (stopBirths) return void 0;
29538
+ if (stopBirths || routeHandoff) return void 0;
29512
29539
  return context.eventStore.transaction(() => {
29513
29540
  const run = requireRun(context, runId);
29514
29541
  const state = foldStoredKxmRun(context, run);
29515
29542
  if (!birthAllowed(state, panel.step)) return void 0;
29516
- return birthMember(context, {
29543
+ const born = birthMember(context, {
29517
29544
  run,
29518
29545
  plan: panel.plan,
29519
29546
  step: panel.step,
@@ -29522,6 +29549,11 @@ async function drivePanel(context, panel, producer) {
29522
29549
  producerId: producer.id,
29523
29550
  dispatchSources: panel.dispatchSources
29524
29551
  });
29552
+ if ("handoff" in born) {
29553
+ routeHandoff = born.handoff;
29554
+ return void 0;
29555
+ }
29556
+ return born;
29525
29557
  });
29526
29558
  };
29527
29559
  const settleInvoked = (member, produced) => {
@@ -29565,6 +29597,10 @@ async function drivePanel(context, panel, producer) {
29565
29597
  while (!stopBirths && !settlementFailed) {
29566
29598
  try {
29567
29599
  const next = tryBirth();
29600
+ if (routeHandoff) {
29601
+ stopBirths = true;
29602
+ break;
29603
+ }
29568
29604
  if (!next) break;
29569
29605
  launch(next);
29570
29606
  } catch {
@@ -29575,6 +29611,13 @@ async function drivePanel(context, panel, producer) {
29575
29611
  return unreconciledHandoff(state, panel.stepId);
29576
29612
  }
29577
29613
  }
29614
+ if (routeHandoff) {
29615
+ const handoff = routeHandoff;
29616
+ abortOwned();
29617
+ await drainPendingInvoked();
29618
+ const state = foldStoredKxmRun(context, requireRun(context, runId));
29619
+ return { state, handoff };
29620
+ }
29578
29621
  if (pending.size === 0) break;
29579
29622
  const finished = await Promise.race(pending.values());
29580
29623
  pending.delete(finished.attemptId);
@@ -30138,25 +30181,7 @@ function unsupportedStep(plan, step, producerId) {
30138
30181
  }
30139
30182
  return void 0;
30140
30183
  }
30141
- function readYamlRecord(path) {
30142
- if (!existsSync12(path)) return void 0;
30143
- try {
30144
- const parsed = (0, import_yaml9.parse)(readFileSync10(path, "utf8"));
30145
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
30146
- } catch {
30147
- return void 0;
30148
- }
30149
- return void 0;
30150
- }
30151
- function agentHarness(projectRoot, agentId) {
30152
- const harness = readYamlRecord(join15(projectRoot, ".kxm", "agents", `${agentId}.yaml`))?.harness;
30153
- return typeof harness === "string" && harness.length > 0 ? harness : void 0;
30154
- }
30155
- function projectDefaultHarness(projectRoot) {
30156
- const harness = readYamlRecord(join15(projectRoot, ".kxm", "project.yaml"))?.defaultHarness;
30157
- return typeof harness === "string" && harness.length > 0 ? harness : "pi";
30158
- }
30159
- function unsupportedLiveWrite(projectRoot, step, agentId, selector, maxConcurrentRuns) {
30184
+ function unsupportedLiveWrite(projectRoot, step, route, maxConcurrentRuns) {
30160
30185
  if (!Object.values(step.repositories).some((access) => access === "write")) return void 0;
30161
30186
  if (step.assignments.maximum !== 1) {
30162
30187
  return {
@@ -30172,42 +30197,23 @@ function unsupportedLiveWrite(projectRoot, step, agentId, selector, maxConcurren
30172
30197
  detail: "live write steps require limits.maxConcurrentRuns of 1; concurrent runs share one checkout"
30173
30198
  };
30174
30199
  }
30175
- const harness = agentHarness(projectRoot, agentId) ?? projectDefaultHarness(projectRoot);
30176
- if (!oneShotWriterArgs(harness)) {
30200
+ if (!oneShotWriterArgs(route.harness)) {
30177
30201
  return {
30178
30202
  reason: "step_unsupported",
30179
30203
  field: "repositories",
30180
- detail: `live write steps require an audited writer profile; ${harness} has none`
30181
- };
30182
- }
30183
- const rosterPath = join15(projectRoot, ".kxm", "roster.yaml");
30184
- if (!existsSync12(rosterPath)) return void 0;
30185
- const roster = readYamlRecord(rosterPath);
30186
- if (!roster || roster.schema !== "kxm.developer-roster.v1") {
30187
- return { reason: "step_unsupported", field: "model", detail: "live write steps require a readable kxm.developer-roster.v1" };
30188
- }
30189
- const routes = roster.routes;
30190
- const lineup = roster.lineup;
30191
- const writerIds = lineup && typeof lineup === "object" && !Array.isArray(lineup) ? lineup.writer : void 0;
30192
- if (!routes || typeof routes !== "object" || Array.isArray(routes) || !Array.isArray(writerIds)) {
30193
- return { reason: "step_unsupported", field: "model", detail: "developer roster has no writer lineup" };
30194
- }
30195
- const allowed = writerIds.some((id) => {
30196
- if (typeof id !== "string") return false;
30197
- const route = routes[id];
30198
- if (!route || typeof route !== "object" || Array.isArray(route)) return false;
30199
- const record2 = route;
30200
- if (record2.harness !== harness || record2.status !== "admitted") return false;
30201
- if (!Array.isArray(record2.permissions) || !record2.permissions.includes("edit")) return false;
30202
- const model = typeof record2.model === "string" ? record2.model : "";
30203
- const vendor = typeof record2.vendor === "string" ? record2.vendor : "";
30204
- return model === selector || vendor.length > 0 && `${vendor}/${model}` === selector;
30205
- });
30206
- if (!allowed) {
30204
+ detail: `live write steps require an audited writer profile; ${route.harness} has none`
30205
+ };
30206
+ }
30207
+ const writerIds = listRoleBindings(projectRoot).writer;
30208
+ if (!Array.isArray(writerIds) || writerIds.length === 0) {
30209
+ return { reason: "step_unsupported", field: "model", detail: "writer roster has no admitted edit route" };
30210
+ }
30211
+ const named = writerIds.includes(route.routeId);
30212
+ if (!named || !route.permissions.includes("edit")) {
30207
30213
  return {
30208
30214
  reason: "step_unsupported",
30209
30215
  field: "model",
30210
- detail: `live write route ${selector} on ${harness} is not on the developer roster writer lineup`
30216
+ detail: `live write route ${route.selector} on ${route.harness} is not an admitted edit route on the writer roster`
30211
30217
  };
30212
30218
  }
30213
30219
  return void 0;
@@ -30644,20 +30650,11 @@ function determineOutcome(text, allowedOutcomes) {
30644
30650
  return "failed";
30645
30651
  }
30646
30652
  function createKxmOneShotProducer(options = {}) {
30647
- const defaultHarness = options.defaultHarness ?? "claude";
30648
30653
  const running = /* @__PURE__ */ new Map();
30649
30654
  let closed = false;
30650
- function resolveHarnessForRequest(request) {
30655
+ function requireHarness(request) {
30651
30656
  if (request.harness) return request.harness;
30652
- if (options.resolveHarness) {
30653
- const resolved = options.resolveHarness(request.agentId, request.runId);
30654
- if (resolved) return resolved;
30655
- }
30656
- if (options.resolveModel) {
30657
- const resolved = options.resolveModel(request.agentId, request.runId);
30658
- if (resolved?.harness) return resolved.harness;
30659
- }
30660
- return defaultHarness;
30657
+ throw new Error("producer_harness_required");
30661
30658
  }
30662
30659
  function parseModelString(spec, harness) {
30663
30660
  const trimmed = spec.trim();
@@ -30719,7 +30716,7 @@ function createKxmOneShotProducer(options = {}) {
30719
30716
  }
30720
30717
  async function executeRequest(request) {
30721
30718
  const startTime = Date.now();
30722
- const harness = resolveHarnessForRequest(request);
30719
+ const harness = requireHarness(request);
30723
30720
  const resolved = resolveModelForRequest(request, harness);
30724
30721
  if (closed) throw new Error("oneshot_producer_closed");
30725
30722
  const cancelled = () => ({
@@ -31969,24 +31966,7 @@ async function startKxmRuntimeSupervisorInner(paths, requestedPortOption, now, e
31969
31966
  const delayMs = typeof body.delayMs === "number" && body.delayMs > 0 ? body.delayMs : 0;
31970
31967
  const createProducer = () => body.mode === "live" ? createKxmOneShotProducer({
31971
31968
  projectRoot,
31972
- timeoutMs: kxmProjectAdmissionLimits(bundle).agentStepTimeoutMs,
31973
- defaultHarness: String(bundle.project.value.defaultHarness ?? "pi"),
31974
- resolveHarness: (agentId) => {
31975
- const agent = bundle.agents.get(agentId);
31976
- return typeof agent?.value.harness === "string" ? agent.value.harness : void 0;
31977
- },
31978
- resolveModel: (agentId) => {
31979
- const agent = bundle.agents.get(agentId);
31980
- const model = agent?.value.model;
31981
- if (!model || typeof model !== "object" || Array.isArray(model)) return void 0;
31982
- const value = model;
31983
- const provider = typeof value.provider === "string" ? value.provider : void 0;
31984
- const modelName = typeof value.model === "string" ? value.model : void 0;
31985
- if (!provider || !modelName || !isRouteAdmitted(projectRoot, `${provider}/${modelName}`)) {
31986
- throw new Error("producer_route_not_admitted");
31987
- }
31988
- return { provider, model: modelName };
31989
- }
31969
+ timeoutMs: kxmProjectAdmissionLimits(bundle).agentStepTimeoutMs
31990
31970
  }) : createKxmSimulatedProducer(async () => {
31991
31971
  if (delayMs > 0) {
31992
31972
  await new Promise((r) => setTimeout(r, delayMs));