@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
@@ -15663,21 +15663,9 @@ function coreTemplate(projectId, projectName, variant) {
15663
15663
  ]);
15664
15664
  if (variant === "v4-registry") {
15665
15665
  const coordinator = files.get(".kxm/agents/coordinator.yaml");
15666
- if (coordinator) {
15667
- files.set(".kxm/agents/coordinator.yaml", {
15668
- ...coordinator,
15669
- harness: "claude",
15670
- model: { provider: "anthropic", model: "fable" }
15671
- });
15672
- }
15666
+ if (coordinator) files.set(".kxm/agents/coordinator.yaml", { ...coordinator, role: "planner" });
15673
15667
  const implementer = files.get(".kxm/agents/implementer.yaml");
15674
- if (implementer) {
15675
- files.set(".kxm/agents/implementer.yaml", {
15676
- ...implementer,
15677
- harness: "grok",
15678
- model: { provider: "xai", model: "grok-4.6" }
15679
- });
15680
- }
15668
+ if (implementer) files.set(".kxm/agents/implementer.yaml", { ...implementer, role: "writer" });
15681
15669
  const workflow = files.get(".kxm/workflows/default.yaml");
15682
15670
  if (workflow) {
15683
15671
  const limits = { ...workflow.limits };
@@ -17316,7 +17304,7 @@ function readTemplateProvenance(registry, root) {
17316
17304
  }
17317
17305
  return value;
17318
17306
  }
17319
- function listNamedResources(registry, root, directory, logicalDirectory, kind, replacedId) {
17307
+ function listNamedResources(registry, root, directory, logicalDirectory, kind, replacedId, collected) {
17320
17308
  const resources = /* @__PURE__ */ new Map();
17321
17309
  if (!existsSync3(directory)) return resources;
17322
17310
  const stat = lstatSync(directory);
@@ -17355,7 +17343,10 @@ function listNamedResources(registry, root, directory, logicalDirectory, kind, r
17355
17343
  else throw error;
17356
17344
  }
17357
17345
  }
17358
- if (issues.length > 0) throw new KxmConfigError(issues);
17346
+ if (issues.length > 0) {
17347
+ if (collected) collected.push(...issues);
17348
+ else throw new KxmConfigError(issues);
17349
+ }
17359
17350
  return resources;
17360
17351
  }
17361
17352
  function valuesOf(object, field) {
@@ -17619,6 +17610,9 @@ function validateWorkflow(workflow, agents, models, repositories, gates, issues)
17619
17610
  for (const repositoryId of Object.keys(objectValue(step.repositories) ?? {})) {
17620
17611
  if (!repositories.has(repositoryId)) issues.push(issue3("reference", "repository_unknown", file, `${stepId} references unknown repository ${repositoryId}`));
17621
17612
  }
17613
+ if ((kind === "agent" || kind === "moa") && step.model !== void 0) {
17614
+ issues.push(issue3("semantic", "producer_route_unsupported", file, `${stepId} model is not honored; remove model from the step`));
17615
+ }
17622
17616
  selectorCandidates(step.model, models, file, `${stepId}.model`, issues);
17623
17617
  const assignment = objectValue(step.assignments);
17624
17618
  const declaredAgents = assignment ? names(assignment.allowedAgents) : [];
@@ -17801,7 +17795,7 @@ function validateModelReferences(models, issues) {
17801
17795
  };
17802
17796
  for (const id of models.keys()) walk(id, []);
17803
17797
  }
17804
- function validateBundle(project, repositories, agents, models, workflows, environments, options, gateRegistry, projectRoot, writerRole) {
17798
+ function validateBundle(project, repositories, agents, models, workflows, environments, options, gateRegistry, projectRoot, roles = /* @__PURE__ */ new Map()) {
17805
17799
  const issues = [];
17806
17800
  const entries = valuesOf(project.value, "repositories").map((candidate) => objectValue(candidate)).filter((candidate) => Boolean(candidate));
17807
17801
  const repositoryIds = /* @__PURE__ */ new Set();
@@ -17842,84 +17836,84 @@ function validateBundle(project, repositories, agents, models, workflows, enviro
17842
17836
  const defaultHarness = stringValue(project.value.defaultHarness) ?? DEFAULT_HARNESS;
17843
17837
  if (!harnesses.has(defaultHarness)) issues.push(issue3("reference", "harness_unknown", project.logicalPath, `defaultHarness ${defaultHarness} is not registered`));
17844
17838
  for (const agent of agents.values()) {
17839
+ if (agent.value.model !== void 0 || agent.value.harness !== void 0) {
17840
+ issues.push(issue3("semantic", "retired_agent_routing_fields", agent.logicalPath, "routing resolves from role; remove model and harness"));
17841
+ }
17845
17842
  validateToolPolicy(agent, objectValue(agent.value.tools), "tools", issues);
17846
17843
  const executor = stringValue(agent.value.executor);
17847
17844
  if (executor && !executors.has(executor)) issues.push(issue3("reference", "executor_unknown", agent.logicalPath, `executor ${executor} is not registered`));
17848
- const harness = stringValue(agent.value.harness) ?? defaultHarness;
17849
- if (!harnesses.has(harness)) issues.push(issue3("reference", "harness_unknown", agent.logicalPath, `harness ${harness} is not registered`));
17850
17845
  const preset = stringValue(objectValue(agent.value.tools)?.preset);
17851
17846
  if (preset && !presets.has(preset)) issues.push(issue3("reference", "tool_preset_unknown", agent.logicalPath, `tool preset ${preset} is not registered`));
17852
17847
  for (const repositoryId of Object.keys(objectValue(agent.value.repositories) ?? {})) {
17853
17848
  if (!repositoryIds.has(repositoryId)) issues.push(issue3("reference", "repository_unknown", agent.logicalPath, `references unknown repository ${repositoryId}`));
17854
17849
  }
17855
- const candidates = selectorCandidates(agent.value.model, models, agent.logicalPath, "model", issues);
17856
- const declaredHarness = stringValue(agent.value.harness);
17857
- if (declaredHarness && harnesses.has(declaredHarness)) {
17858
- for (const candidate of candidates) {
17859
- const candidateProvider = stringValue(candidate.value.provider);
17860
- const candidateModel = stringValue(candidate.value.model);
17861
- const validation = validateHarnessModelPair(declaredHarness, { provider: candidateProvider, model: candidateModel });
17862
- if (!validation.valid) {
17863
- issues.push(issue3("semantic", validation.issue ?? "harness_unhosted_model", agent.logicalPath, validation.message ?? `harness ${declaredHarness} cannot host model ${candidateModel ?? candidate.logicalPath}`));
17864
- }
17865
- }
17866
- }
17867
17850
  }
17868
17851
  validateModelReferences(models, issues);
17869
17852
  const defaultWorkflow = stringValue(project.value.defaultWorkflow) ?? "default";
17870
17853
  if (!workflows.has(defaultWorkflow)) issues.push(issue3("reference", "default_workflow_unknown", project.logicalPath, `default workflow ${defaultWorkflow} does not exist`));
17871
17854
  for (const workflow of workflows.values()) validateWorkflow(workflow, agents, models, repositoryIds, gates, issues);
17855
+ if (projectRoot && existsSync3(join2(projectRoot, ".kxm", "roster.yaml"))) {
17856
+ issues.push(issue3("semantic", "retired_roster_file", ".kxm/roster.yaml", "create the role and model files and delete roster.yaml"));
17857
+ }
17858
+ if (projectRoot && existsSync3(join2(projectRoot, ".kxm", "roles"))) {
17859
+ issues.push(...developerRolePolicyIssues(projectRoot));
17860
+ }
17872
17861
  if (projectRoot) {
17873
- const writerRolePath = join2(projectRoot, ".kxm", "roles", "writer.yaml");
17874
- const implementerAgent = agents.get("implementer") ?? agents.get("writer");
17875
- if ((writerRole !== void 0 || existsSync3(writerRolePath)) && implementerAgent) {
17862
+ issues.push(...rosterModelIssues(projectRoot, roles));
17863
+ issues.push(...originHashIssues(projectRoot, models));
17864
+ }
17865
+ return sortIssues3(issues);
17866
+ }
17867
+ function rosterModelIssues(projectRoot, roles) {
17868
+ const issues = [];
17869
+ for (const role of roles.values()) {
17870
+ for (const entry of valuesOf(role.value, "roster")) {
17871
+ const route = stringValue(objectValue(entry)?.route);
17872
+ if (!route) continue;
17873
+ const logical = `.kxm/models/${route}.yaml`;
17874
+ const file = join2(projectRoot, ".kxm", "models", `${route}.yaml`);
17875
+ let harness;
17876
17876
  try {
17877
- const rawRole = parseRestrictedYaml2(writerRole ?? readFileSync(writerRolePath, "utf8"));
17878
- const roleObj = objectValue(rawRole);
17879
- const rosterEntries = valuesOf(roleObj ?? {}, "roster").map((candidate) => objectValue(candidate)).filter((entry) => Boolean(entry));
17880
- const enabledRosterModels = rosterEntries.flatMap((entry) => {
17881
- const direct = stringValue(entry.model);
17882
- const route = stringValue(entry.route);
17883
- const named = direct ? [direct] : [];
17884
- if (route) {
17885
- const modelFile = join2(projectRoot, ".kxm", "models", `${route}.yaml`);
17886
- if (existsSync3(modelFile)) {
17887
- try {
17888
- const modelDoc = objectValue(parseRestrictedYaml2(readFileSync(modelFile, "utf8"), modelFile));
17889
- const model = modelDoc ? stringValue(modelDoc.model) : void 0;
17890
- const vendor = modelDoc ? stringValue(modelDoc.vendor) : void 0;
17891
- const harness = modelDoc ? stringValue(modelDoc.harness) : void 0;
17892
- if (model) named.push(model);
17893
- if (vendor && model) named.push(`${vendor}/${model}`);
17894
- if (harness && model) named.push(`${harness}/${model}`);
17895
- } catch {
17896
- }
17897
- }
17898
- }
17899
- return named;
17900
- });
17901
- const agentModelObj = objectValue(implementerAgent.value.model);
17902
- const agentModelStr = stringValue(implementerAgent.value.model);
17903
- const agentProvider = agentModelObj ? stringValue(agentModelObj.provider) : void 0;
17904
- const agentModel = agentModelObj ? stringValue(agentModelObj.model) : agentModelStr;
17905
- const canonicalAgentModel = agentProvider && agentModel ? `${agentProvider}/${agentModel}` : agentModel;
17906
- if (enabledRosterModels.length > 0 && canonicalAgentModel) {
17907
- const matches = enabledRosterModels.some((rm) => rm === canonicalAgentModel || rm === agentModel || rm.endsWith(`/${agentModel}`));
17908
- if (!matches) {
17909
- issues.push(issue3("semantic", "role_roster_conflicts_with_agent", ".kxm/roles/writer.yaml", `role roster in .kxm/roles/writer.yaml does not include agent model ${canonicalAgentModel} from ${implementerAgent.logicalPath}`));
17910
- }
17911
- }
17912
- } catch (error) {
17913
- if (error instanceof KxmConfigError) {
17914
- issues.push(...error.issues);
17915
- }
17877
+ if (existsSync3(file)) harness = stringValue(parseRestrictedYaml2(readFileSync(file, "utf8"), logical).harness);
17878
+ } catch {
17879
+ harness = void 0;
17880
+ }
17881
+ if (!harness) {
17882
+ issues.push(issue3("semantic", "roster_model_unreadable", role.logicalPath, `roster route ${route} does not resolve to a readable ${logical} carrying harness`));
17916
17883
  }
17917
17884
  }
17918
17885
  }
17919
- if (projectRoot && existsSync3(join2(projectRoot, ".kxm", "roster.yaml"))) {
17920
- issues.push(...developerRolePolicyIssues(projectRoot));
17886
+ return issues;
17887
+ }
17888
+ function originHashIssues(projectRoot, models) {
17889
+ const issues = [];
17890
+ for (const model of models.values()) {
17891
+ const origin = objectValue(model.value.origin);
17892
+ const source = origin ? stringValue(origin.source) : void 0;
17893
+ const digest2 = origin ? stringValue(origin.sha256) : void 0;
17894
+ if (!source || !digest2) continue;
17895
+ const file = resolve(projectRoot, source);
17896
+ const escaped = relative(projectRoot, file);
17897
+ if (isAbsolute(source) || escaped.startsWith("..") || isAbsolute(escaped)) {
17898
+ issues.push(issue3("semantic", "origin_evidence_missing", model.logicalPath, `missing evidence bytes for ${source}`));
17899
+ continue;
17900
+ }
17901
+ let actual;
17902
+ try {
17903
+ actual = createHash3("sha256").update(readFileSync(file, "utf8"), "utf8").digest("hex");
17904
+ } catch (error) {
17905
+ const code = error.code;
17906
+ if (code === "ENOENT" || code === "EACCES" || code === "EPERM" || code === "EISDIR") {
17907
+ issues.push(issue3("semantic", "origin_evidence_missing", model.logicalPath, `missing evidence bytes for ${source}`));
17908
+ continue;
17909
+ }
17910
+ throw error;
17911
+ }
17912
+ if (actual !== digest2) {
17913
+ issues.push(issue3("semantic", "origin_hash_mismatch", model.logicalPath, "origin evidence hash does not match supplied bytes"));
17914
+ }
17921
17915
  }
17922
- return sortIssues3(issues);
17916
+ return issues;
17923
17917
  }
17924
17918
  function developerCeilings() {
17925
17919
  const script = join2(findKxmRepoRoot(import.meta.url), "scripts", "harness-run.mjs");
@@ -17948,6 +17942,7 @@ function developerCeilings() {
17948
17942
  }
17949
17943
  }
17950
17944
  function developerRolePolicyIssues(projectRoot) {
17945
+ if (resolve(projectRoot) !== findKxmRepoRoot(import.meta.url)) return [];
17951
17946
  const rolesDir = join2(projectRoot, ".kxm", "roles");
17952
17947
  const modelsDir = join2(projectRoot, ".kxm", "models");
17953
17948
  const roles = {};
@@ -17968,6 +17963,7 @@ function developerRolePolicyIssues(projectRoot) {
17968
17963
  parseIssues.push(...error.issues);
17969
17964
  continue;
17970
17965
  }
17966
+ throw error;
17971
17967
  }
17972
17968
  }
17973
17969
  };
@@ -17982,7 +17978,7 @@ function developerRolePolicyIssues(projectRoot) {
17982
17978
  }
17983
17979
  const ceilings = developerCeilings();
17984
17980
  if (!ceilings.ok) {
17985
- return [...parseIssues, issue3("semantic", "developer_ceilings_unavailable", ".kxm/roster.yaml", `developer ceilings could not be loaded: ${ceilings.detail}`)];
17981
+ return [...parseIssues, issue3("semantic", "developer_ceilings_unavailable", ".kxm/models", `developer ceilings could not be loaded: ${ceilings.detail}`)];
17986
17982
  }
17987
17983
  const result = validatePolicyDraft({ models, roles, evidence }, {
17988
17984
  ceilings: ceilings.ROUTES,
@@ -18071,7 +18067,8 @@ function loadProjectBundle(projectRoot, options, candidate) {
18071
18067
  }
18072
18068
  if (earlyIssues.length > 0) throw new KxmConfigError(earlyIssues);
18073
18069
  const agents = listNamedResources(registry, root, join2(root, ".kxm", "agents"), ".kxm/agents", "agent");
18074
- const models = listNamedResources(registry, root, join2(root, ".kxm", "models"), ".kxm/models", "model");
18070
+ const modelIssues = [];
18071
+ const models = listNamedResources(registry, root, join2(root, ".kxm", "models"), ".kxm/models", "model", void 0, modelIssues);
18075
18072
  const roles = listNamedResources(registry, root, join2(root, ".kxm", "roles"), ".kxm/roles", "role", writerRole === void 0 ? void 0 : "writer");
18076
18073
  if (writerRole !== void 0) {
18077
18074
  const logicalPath = ".kxm/roles/writer.yaml";
@@ -18198,8 +18195,8 @@ function loadProjectBundle(projectRoot, options, candidate) {
18198
18195
  }
18199
18196
  }
18200
18197
  }
18201
- if (loadIssues.length > 0) throw new KxmConfigError(loadIssues);
18202
- const issues = validateBundle(project, repositories, agents, models, workflows, environments, options, gateRegistry, root, writerRole);
18198
+ if (loadIssues.length > 0) throw new KxmConfigError([...loadIssues, ...modelIssues]);
18199
+ const issues = [...modelIssues, ...validateBundle(project, repositories, agents, models, workflows, environments, options, gateRegistry, root, roles)];
18203
18200
  if (issues.length > 0) throw new KxmConfigError(issues);
18204
18201
  const resources = [project, ...repositories.values(), ...agents.values(), ...models.values(), ...roles.values(), ...workflows.values(), ...environments, ...gateRegistry ? [gateRegistry] : []].sort((left, right) => compareCodeUnits4(left.logicalPath, right.logicalPath));
18205
18202
  return {
@@ -24479,14 +24476,14 @@ var import_yaml4 = __toESM(require_dist(), 1);
24479
24476
 
24480
24477
  // plugins/kxm/src/routes.ts
24481
24478
  var RETIRED_POLICY = ".kxm/producers.yaml";
24482
- var empty = () => ({ schema: "kxm.routes.v2", updatedAt: (/* @__PURE__ */ new Date()).toISOString(), admitted: [], disabled: [], roles: {} });
24479
+ var empty = () => ({ schema: "kxm.routes.v2", updatedAt: (/* @__PURE__ */ new Date()).toISOString(), admitted: [], disabled: [] });
24483
24480
  function loadRoutePolicy(root) {
24484
24481
  if (existsSync9(join10(root, RETIRED_POLICY))) throw new Error(`retired ${RETIRED_POLICY} present; use .kxm/routes.yaml (kxm.routes.v2)`);
24485
24482
  const path = join10(root, ".kxm", "routes.yaml");
24486
24483
  if (!existsSync9(path)) return empty();
24487
24484
  const value = (0, import_yaml6.parse)(readFileSync7(path, "utf8"));
24488
24485
  if (value?.schema !== "kxm.routes.v2" || !Array.isArray(value.admitted)) throw new Error("invalid .kxm/routes.yaml");
24489
- 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")])) : {} };
24486
+ 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") : [] };
24490
24487
  }
24491
24488
  function listRoleBindings(root) {
24492
24489
  const dir = join10(root, ".kxm", "roles");
@@ -24499,10 +24496,6 @@ function listRoleBindings(root) {
24499
24496
  }
24500
24497
  return result;
24501
24498
  }
24502
- function isRouteAdmitted(root, modelId) {
24503
- const policy = loadRoutePolicy(root);
24504
- return policy.admitted.includes(modelId) && !policy.disabled.includes(modelId);
24505
- }
24506
24499
 
24507
24500
  // plugins/kxm/src/worktree-witness.ts
24508
24501
  import { spawnSync as spawnSync4 } from "node:child_process";
@@ -28070,25 +28063,18 @@ function commitCommandUncertainty(context, prepared, token, reason, observation,
28070
28063
  }
28071
28064
  }
28072
28065
  var kxmPanelDispatchSeams = {};
28073
- function selectorsNamedByRoutes(projectRoot, routeIds) {
28074
- const selectors = /* @__PURE__ */ new Set();
28075
- for (const routeId2 of routeIds) {
28076
- const file = join15(projectRoot, ".kxm", "models", `${routeId2}.yaml`);
28077
- if (!existsSync12(file)) continue;
28078
- let parsed;
28079
- try {
28080
- parsed = (0, import_yaml9.parse)(readFileSync10(file, "utf8"));
28081
- } catch {
28082
- continue;
28083
- }
28084
- const model = typeof parsed.model === "string" ? parsed.model : "";
28085
- const vendor = typeof parsed.vendor === "string" ? parsed.vendor : "";
28086
- const harness = typeof parsed.harness === "string" ? parsed.harness : "";
28087
- if (model) selectors.add(model);
28088
- if (vendor && model) selectors.add(`${vendor}/${model}`);
28089
- if (harness && model) selectors.add(`${harness}/${model}`);
28066
+ function readYamlFile(file) {
28067
+ return readModelDocument(file);
28068
+ }
28069
+ function readModelDocument(file) {
28070
+ if (!existsSync12(file)) return void 0;
28071
+ try {
28072
+ const parsed = (0, import_yaml9.parse)(readFileSync10(file, "utf8"));
28073
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return void 0;
28074
+ return parsed;
28075
+ } catch {
28076
+ return void 0;
28090
28077
  }
28091
- return [...selectors];
28092
28078
  }
28093
28079
  function unreconciledPanelAttemptId(state) {
28094
28080
  const current = state.currentStep;
@@ -28105,82 +28091,118 @@ function unreconciledPanelAttemptId(state) {
28105
28091
  return void 0;
28106
28092
  }
28107
28093
  function resolveProducerRoute(projectRoot, step, agentId) {
28108
- let agentModel;
28109
- const agentFile = join15(projectRoot, ".kxm", "agents", `${agentId}.yaml`);
28110
- if (existsSync12(agentFile)) {
28111
- try {
28112
- const parsed = (0, import_yaml9.parse)(readFileSync10(agentFile, "utf8"));
28113
- if (typeof parsed?.model === "string") {
28114
- agentModel = parsed.model;
28115
- } else if (parsed?.model && typeof parsed.model === "object" && !Array.isArray(parsed.model)) {
28116
- const declared = parsed.model;
28117
- const providerOk = typeof declared.provider === "string" && declared.provider.length > 0 && !declared.provider.includes("/");
28118
- const modelOk = typeof declared.model === "string" && declared.model.length > 0 && !declared.model.startsWith("/") && !declared.model.endsWith("/");
28119
- if (providerOk && modelOk) {
28120
- agentModel = `${declared.provider}/${declared.model}`;
28121
- } else {
28122
- return {
28123
- error: {
28124
- reason: "step_unsupported",
28125
- field: "model",
28126
- detail: "producer_route_unsupported: invalid model declaration"
28127
- }
28128
- };
28129
- }
28130
- }
28131
- } catch {
28132
- }
28133
- }
28134
- let selector = typeof step.model === "string" ? step.model : agentModel;
28135
- if (!selector) {
28136
- if (agentId === "implementer") {
28137
- selector = "xai/grok-4.6";
28138
- }
28139
- }
28140
- if (!selector) {
28094
+ if ((step.kind === "agent" || step.kind === "moa") && step.model !== void 0) {
28141
28095
  return {
28142
28096
  error: {
28143
28097
  reason: "step_unsupported",
28144
28098
  field: "model",
28145
- detail: "producer_route_unsupported: agent or step has no model declared"
28099
+ detail: "producer_route_unsupported: agent step model is not honored"
28146
28100
  }
28147
28101
  };
28148
28102
  }
28149
- const slash = selector.indexOf("/");
28150
- if (slash <= 0 || slash === selector.length - 1) {
28103
+ const agent = readYamlFile(join15(projectRoot, ".kxm", "agents", `${agentId}.yaml`));
28104
+ const role = typeof agent?.role === "string" ? agent.role : "";
28105
+ if (!role) {
28151
28106
  return {
28152
28107
  error: {
28153
28108
  reason: "step_unsupported",
28154
28109
  field: "model",
28155
- detail: `producer_route_unsupported: invalid selector '${selector}'`
28110
+ detail: "producer_route_unsupported: agent has no role"
28156
28111
  }
28157
28112
  };
28158
28113
  }
28159
- if (!isRouteAdmitted(projectRoot, selector)) {
28114
+ const roleDoc = readYamlFile(join15(projectRoot, ".kxm", "roles", `${role}.yaml`));
28115
+ const roster = Array.isArray(roleDoc?.roster) ? roleDoc.roster : void 0;
28116
+ if (!roster) {
28160
28117
  return {
28161
28118
  error: {
28162
28119
  reason: "step_unsupported",
28163
28120
  field: "model",
28164
- detail: `producer_route_unsupported: model '${selector}' is not admitted`
28121
+ detail: `producer_route_unsupported: role '${role}' has no roster`
28165
28122
  }
28166
28123
  };
28167
28124
  }
28168
- const role = agentId === "implementer" ? "writer" : agentId;
28169
- const roleBindings = listRoleBindings(projectRoot);
28170
- const roster = roleBindings[role];
28171
- const rosterSelectors = roster ? selectorsNamedByRoutes(projectRoot, roster) : void 0;
28172
- if (rosterSelectors && !rosterSelectors.includes(selector)) {
28125
+ const policy = loadRoutePolicy(projectRoot);
28126
+ for (const entry of roster) {
28127
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) continue;
28128
+ const record2 = entry;
28129
+ if (typeof record2.route !== "string" || record2.route.length === 0) continue;
28130
+ const modelPath = join15(projectRoot, ".kxm", "models", `${record2.route}.yaml`);
28131
+ const doc = readModelDocument(modelPath);
28132
+ if (!doc) {
28133
+ return {
28134
+ error: {
28135
+ reason: "step_unsupported",
28136
+ field: "model",
28137
+ detail: `producer_route_unsupported: model file '.kxm/models/${record2.route}.yaml' is missing or unreadable`
28138
+ }
28139
+ };
28140
+ }
28141
+ if (doc.status !== "admitted") continue;
28142
+ const modelName = typeof doc.model === "string" ? doc.model : "";
28143
+ const vendor = typeof doc.vendor === "string" ? doc.vendor : "";
28144
+ const harness = typeof doc.harness === "string" ? doc.harness : "";
28145
+ if (!modelName || !vendor || !harness) continue;
28146
+ const candidates = [modelName, `${vendor}/${modelName}`, `${harness}/${modelName}`];
28147
+ const admittedSelector = candidates.find((candidate) => policy.admitted.includes(candidate));
28148
+ const disabledSelector = candidates.find((candidate) => policy.disabled.includes(candidate));
28149
+ if (!admittedSelector && disabledSelector) {
28150
+ return {
28151
+ error: {
28152
+ reason: "step_unsupported",
28153
+ field: "model",
28154
+ detail: `producer_route_unsupported: first admitted route '${record2.route}' for role '${role}' is disabled`
28155
+ }
28156
+ };
28157
+ }
28158
+ const selector = admittedSelector ?? (modelName.includes("/") ? modelName : `${vendor}/${modelName}`);
28159
+ if (policy.disabled.includes(selector)) {
28160
+ return {
28161
+ error: {
28162
+ reason: "step_unsupported",
28163
+ field: "model",
28164
+ detail: `producer_route_unsupported: first admitted route '${record2.route}' for role '${role}' is disabled`
28165
+ }
28166
+ };
28167
+ }
28168
+ if (!policy.admitted.includes(selector)) {
28169
+ return {
28170
+ error: {
28171
+ reason: "step_unsupported",
28172
+ field: "model",
28173
+ detail: `producer_route_unsupported: model '${selector}' is not admitted`
28174
+ }
28175
+ };
28176
+ }
28177
+ const slash = selector.indexOf("/");
28178
+ if (slash <= 0 || slash === selector.length - 1) {
28179
+ return {
28180
+ error: {
28181
+ reason: "step_unsupported",
28182
+ field: "model",
28183
+ detail: `producer_route_unsupported: invalid selector '${selector}'`
28184
+ }
28185
+ };
28186
+ }
28187
+ const permissions = Array.isArray(doc.permissions) ? doc.permissions.filter((item) => typeof item === "string") : [];
28173
28188
  return {
28174
- error: {
28175
- reason: "step_unsupported",
28176
- field: "model",
28177
- detail: `producer_route_unsupported: model '${selector}' not in role '${role}' roster`
28178
- }
28189
+ provider: selector.slice(0, slash),
28190
+ model: selector.slice(slash + 1),
28191
+ selector,
28192
+ harness,
28193
+ routeId: record2.route,
28194
+ role,
28195
+ permissions,
28196
+ ...typeof record2.effort === "string" ? { effort: record2.effort } : {}
28179
28197
  };
28180
28198
  }
28181
- const provider = selector.slice(0, slash);
28182
- const model = selector.slice(slash + 1);
28183
- return { provider, model, selector };
28199
+ return {
28200
+ error: {
28201
+ reason: "step_unsupported",
28202
+ field: "model",
28203
+ detail: `producer_route_unsupported: role '${role}' has no admitted route`
28204
+ }
28205
+ };
28184
28206
  }
28185
28207
  function projectAgentStepTimeoutMs(context, run) {
28186
28208
  return loadKxmRunPlanEnvelope(context.eventStore, run).projectLimits.agentStepTimeoutMs ?? KXM_DEFAULT_AGENT_STEP_TIMEOUT_MS;
@@ -28398,8 +28420,7 @@ function prepareDispatch(context, runId, producerId, dispatchSources) {
28398
28420
  const writeRefusal = unsupportedLiveWrite(
28399
28421
  context.projectRoot,
28400
28422
  step,
28401
- agentId,
28402
- resolvedRoute.selector,
28423
+ resolvedRoute,
28403
28424
  loadKxmRunPlanEnvelope(context.eventStore, run).projectLimits.maxConcurrentRuns
28404
28425
  );
28405
28426
  if (writeRefusal) return { kind: "return", state, handoff: { ...writeRefusal, stepId } };
@@ -28428,7 +28449,7 @@ function prepareDispatch(context, runId, producerId, dispatchSources) {
28428
28449
  for (const event of events) context.eventStore.appendEvent(event);
28429
28450
  const entered = foldStoredKxmRun(context, run);
28430
28451
  persistKxmRunState(context, runId, entered, events[events.length - 1].sequence);
28431
- const first = birthMember(context, {
28452
+ const firstBirth = birthMember(context, {
28432
28453
  run,
28433
28454
  plan,
28434
28455
  step,
@@ -28439,6 +28460,10 @@ function prepareDispatch(context, runId, producerId, dispatchSources) {
28439
28460
  resolvedRoute,
28440
28461
  dispatchSources
28441
28462
  });
28463
+ if ("handoff" in firstBirth) {
28464
+ return { kind: "return", state: entered, handoff: firstBirth.handoff };
28465
+ }
28466
+ const first = firstBirth;
28442
28467
  return {
28443
28468
  kind: "panel",
28444
28469
  panel: {
@@ -28501,9 +28526,10 @@ function birthMember(context, input) {
28501
28526
  let resolvedRoute = input.resolvedRoute;
28502
28527
  if (!resolvedRoute && input.producerId && input.producerId !== "driver-simulated") {
28503
28528
  const routeResult = resolveProducerRoute(context.projectRoot, input.step, agentId);
28504
- if (!("error" in routeResult)) {
28505
- resolvedRoute = routeResult;
28529
+ if ("error" in routeResult) {
28530
+ return { handoff: { ...routeResult.error, stepId: input.stepId } };
28506
28531
  }
28532
+ resolvedRoute = routeResult;
28507
28533
  }
28508
28534
  const dispatchContext = assembleDispatchContext(input.dispatchSources, {
28509
28535
  projectId: run.projectId,
@@ -28610,11 +28636,11 @@ function birthMember(context, input) {
28610
28636
  prompt: input.step.instructions ? `${input.step.instructions}
28611
28637
 
28612
28638
  ${generatedPrompt}` : generatedPrompt,
28613
- thinking: input.stepAttempt <= 1 ? "low" : "medium",
28614
28639
  permission: Object.values(input.step.repositories).some((access) => access === "write") ? "edit" : "read-only",
28615
28640
  ...(input.step.kind === "agent" || input.step.kind === "moa") && input.step.timeoutMs !== void 0 ? { timeoutMs: input.step.timeoutMs } : {},
28616
28641
  contextPacket,
28617
- ...resolvedRoute ? { provider: resolvedRoute.provider, model: resolvedRoute.model } : {}
28642
+ ...resolvedRoute?.effort !== void 0 ? { thinking: resolvedRoute.effort } : {},
28643
+ ...resolvedRoute ? { harness: resolvedRoute.harness, provider: resolvedRoute.provider, model: resolvedRoute.model } : {}
28618
28644
  },
28619
28645
  controller,
28620
28646
  state: next
@@ -28759,13 +28785,14 @@ async function drivePanel(context, panel, producer) {
28759
28785
  })();
28760
28786
  pending.set(member.attemptId, work);
28761
28787
  };
28788
+ let routeHandoff;
28762
28789
  const tryBirth = () => {
28763
- if (stopBirths) return void 0;
28790
+ if (stopBirths || routeHandoff) return void 0;
28764
28791
  return context.eventStore.transaction(() => {
28765
28792
  const run = requireRun(context, runId);
28766
28793
  const state = foldStoredKxmRun(context, run);
28767
28794
  if (!birthAllowed(state, panel.step)) return void 0;
28768
- return birthMember(context, {
28795
+ const born = birthMember(context, {
28769
28796
  run,
28770
28797
  plan: panel.plan,
28771
28798
  step: panel.step,
@@ -28774,6 +28801,11 @@ async function drivePanel(context, panel, producer) {
28774
28801
  producerId: producer.id,
28775
28802
  dispatchSources: panel.dispatchSources
28776
28803
  });
28804
+ if ("handoff" in born) {
28805
+ routeHandoff = born.handoff;
28806
+ return void 0;
28807
+ }
28808
+ return born;
28777
28809
  });
28778
28810
  };
28779
28811
  const settleInvoked = (member, produced) => {
@@ -28817,6 +28849,10 @@ async function drivePanel(context, panel, producer) {
28817
28849
  while (!stopBirths && !settlementFailed) {
28818
28850
  try {
28819
28851
  const next = tryBirth();
28852
+ if (routeHandoff) {
28853
+ stopBirths = true;
28854
+ break;
28855
+ }
28820
28856
  if (!next) break;
28821
28857
  launch(next);
28822
28858
  } catch {
@@ -28827,6 +28863,13 @@ async function drivePanel(context, panel, producer) {
28827
28863
  return unreconciledHandoff(state, panel.stepId);
28828
28864
  }
28829
28865
  }
28866
+ if (routeHandoff) {
28867
+ const handoff = routeHandoff;
28868
+ abortOwned();
28869
+ await drainPendingInvoked();
28870
+ const state = foldStoredKxmRun(context, requireRun(context, runId));
28871
+ return { state, handoff };
28872
+ }
28830
28873
  if (pending.size === 0) break;
28831
28874
  const finished = await Promise.race(pending.values());
28832
28875
  pending.delete(finished.attemptId);
@@ -29390,25 +29433,7 @@ function unsupportedStep(plan, step, producerId) {
29390
29433
  }
29391
29434
  return void 0;
29392
29435
  }
29393
- function readYamlRecord(path) {
29394
- if (!existsSync12(path)) return void 0;
29395
- try {
29396
- const parsed = (0, import_yaml9.parse)(readFileSync10(path, "utf8"));
29397
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
29398
- } catch {
29399
- return void 0;
29400
- }
29401
- return void 0;
29402
- }
29403
- function agentHarness(projectRoot, agentId) {
29404
- const harness = readYamlRecord(join15(projectRoot, ".kxm", "agents", `${agentId}.yaml`))?.harness;
29405
- return typeof harness === "string" && harness.length > 0 ? harness : void 0;
29406
- }
29407
- function projectDefaultHarness(projectRoot) {
29408
- const harness = readYamlRecord(join15(projectRoot, ".kxm", "project.yaml"))?.defaultHarness;
29409
- return typeof harness === "string" && harness.length > 0 ? harness : "pi";
29410
- }
29411
- function unsupportedLiveWrite(projectRoot, step, agentId, selector, maxConcurrentRuns) {
29436
+ function unsupportedLiveWrite(projectRoot, step, route, maxConcurrentRuns) {
29412
29437
  if (!Object.values(step.repositories).some((access) => access === "write")) return void 0;
29413
29438
  if (step.assignments.maximum !== 1) {
29414
29439
  return {
@@ -29424,42 +29449,23 @@ function unsupportedLiveWrite(projectRoot, step, agentId, selector, maxConcurren
29424
29449
  detail: "live write steps require limits.maxConcurrentRuns of 1; concurrent runs share one checkout"
29425
29450
  };
29426
29451
  }
29427
- const harness = agentHarness(projectRoot, agentId) ?? projectDefaultHarness(projectRoot);
29428
- if (!oneShotWriterArgs(harness)) {
29452
+ if (!oneShotWriterArgs(route.harness)) {
29429
29453
  return {
29430
29454
  reason: "step_unsupported",
29431
29455
  field: "repositories",
29432
- detail: `live write steps require an audited writer profile; ${harness} has none`
29456
+ detail: `live write steps require an audited writer profile; ${route.harness} has none`
29433
29457
  };
29434
29458
  }
29435
- const rosterPath = join15(projectRoot, ".kxm", "roster.yaml");
29436
- if (!existsSync12(rosterPath)) return void 0;
29437
- const roster = readYamlRecord(rosterPath);
29438
- if (!roster || roster.schema !== "kxm.developer-roster.v1") {
29439
- return { reason: "step_unsupported", field: "model", detail: "live write steps require a readable kxm.developer-roster.v1" };
29440
- }
29441
- const routes = roster.routes;
29442
- const lineup = roster.lineup;
29443
- const writerIds = lineup && typeof lineup === "object" && !Array.isArray(lineup) ? lineup.writer : void 0;
29444
- if (!routes || typeof routes !== "object" || Array.isArray(routes) || !Array.isArray(writerIds)) {
29445
- return { reason: "step_unsupported", field: "model", detail: "developer roster has no writer lineup" };
29446
- }
29447
- const allowed = writerIds.some((id) => {
29448
- if (typeof id !== "string") return false;
29449
- const route = routes[id];
29450
- if (!route || typeof route !== "object" || Array.isArray(route)) return false;
29451
- const record2 = route;
29452
- if (record2.harness !== harness || record2.status !== "admitted") return false;
29453
- if (!Array.isArray(record2.permissions) || !record2.permissions.includes("edit")) return false;
29454
- const model = typeof record2.model === "string" ? record2.model : "";
29455
- const vendor = typeof record2.vendor === "string" ? record2.vendor : "";
29456
- return model === selector || vendor.length > 0 && `${vendor}/${model}` === selector;
29457
- });
29458
- if (!allowed) {
29459
+ const writerIds = listRoleBindings(projectRoot).writer;
29460
+ if (!Array.isArray(writerIds) || writerIds.length === 0) {
29461
+ return { reason: "step_unsupported", field: "model", detail: "writer roster has no admitted edit route" };
29462
+ }
29463
+ const named = writerIds.includes(route.routeId);
29464
+ if (!named || !route.permissions.includes("edit")) {
29459
29465
  return {
29460
29466
  reason: "step_unsupported",
29461
29467
  field: "model",
29462
- detail: `live write route ${selector} on ${harness} is not on the developer roster writer lineup`
29468
+ detail: `live write route ${route.selector} on ${route.harness} is not an admitted edit route on the writer roster`
29463
29469
  };
29464
29470
  }
29465
29471
  return void 0;
@@ -29896,20 +29902,11 @@ function determineOutcome(text, allowedOutcomes) {
29896
29902
  return "failed";
29897
29903
  }
29898
29904
  function createKxmOneShotProducer(options = {}) {
29899
- const defaultHarness = options.defaultHarness ?? "claude";
29900
29905
  const running = /* @__PURE__ */ new Map();
29901
29906
  let closed = false;
29902
- function resolveHarnessForRequest(request) {
29907
+ function requireHarness(request) {
29903
29908
  if (request.harness) return request.harness;
29904
- if (options.resolveHarness) {
29905
- const resolved = options.resolveHarness(request.agentId, request.runId);
29906
- if (resolved) return resolved;
29907
- }
29908
- if (options.resolveModel) {
29909
- const resolved = options.resolveModel(request.agentId, request.runId);
29910
- if (resolved?.harness) return resolved.harness;
29911
- }
29912
- return defaultHarness;
29909
+ throw new Error("producer_harness_required");
29913
29910
  }
29914
29911
  function parseModelString(spec, harness) {
29915
29912
  const trimmed = spec.trim();
@@ -29971,7 +29968,7 @@ function createKxmOneShotProducer(options = {}) {
29971
29968
  }
29972
29969
  async function executeRequest(request) {
29973
29970
  const startTime = Date.now();
29974
- const harness = resolveHarnessForRequest(request);
29971
+ const harness = requireHarness(request);
29975
29972
  const resolved = resolveModelForRequest(request, harness);
29976
29973
  if (closed) throw new Error("oneshot_producer_closed");
29977
29974
  const cancelled = () => ({
@@ -31221,24 +31218,7 @@ async function startKxmRuntimeSupervisorInner(paths, requestedPortOption, now, e
31221
31218
  const delayMs = typeof body.delayMs === "number" && body.delayMs > 0 ? body.delayMs : 0;
31222
31219
  const createProducer = () => body.mode === "live" ? createKxmOneShotProducer({
31223
31220
  projectRoot,
31224
- timeoutMs: kxmProjectAdmissionLimits(bundle).agentStepTimeoutMs,
31225
- defaultHarness: String(bundle.project.value.defaultHarness ?? "pi"),
31226
- resolveHarness: (agentId) => {
31227
- const agent = bundle.agents.get(agentId);
31228
- return typeof agent?.value.harness === "string" ? agent.value.harness : void 0;
31229
- },
31230
- resolveModel: (agentId) => {
31231
- const agent = bundle.agents.get(agentId);
31232
- const model = agent?.value.model;
31233
- if (!model || typeof model !== "object" || Array.isArray(model)) return void 0;
31234
- const value = model;
31235
- const provider = typeof value.provider === "string" ? value.provider : void 0;
31236
- const modelName = typeof value.model === "string" ? value.model : void 0;
31237
- if (!provider || !modelName || !isRouteAdmitted(projectRoot, `${provider}/${modelName}`)) {
31238
- throw new Error("producer_route_not_admitted");
31239
- }
31240
- return { provider, model: modelName };
31241
- }
31221
+ timeoutMs: kxmProjectAdmissionLimits(bundle).agentStepTimeoutMs
31242
31222
  }) : createKxmSimulatedProducer(async () => {
31243
31223
  if (delayMs > 0) {
31244
31224
  await new Promise((r) => setTimeout(r, delayMs));