@kontextmind/kxm 0.7.180 → 0.7.182

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.
@@ -20760,8 +20760,8 @@ function validateWorkflow(workflow, agents, models, repositories, gates, issues)
20760
20760
  const writable = Object.values(objectValue(step.repositories) ?? {}).filter((access) => access === "write").length;
20761
20761
  if (maxWriteRepositories > writable) issues.push(issue3("semantic", "write_repository_bound_invalid", file, `${stepId} maxWriteRepositories exceeds writable repository scope`));
20762
20762
  }
20763
- const join60 = objectValue(step.join);
20764
- const minimumPassed = join60 && typeof join60.minimumPassed === "number" ? join60.minimumPassed : void 0;
20763
+ const join61 = objectValue(step.join);
20764
+ const minimumPassed = join61 && typeof join61.minimumPassed === "number" ? join61.minimumPassed : void 0;
20765
20765
  if (minimumPassed !== void 0 && minimumPassed > maximum) issues.push(issue3("semantic", "join_impossible", file, `${stepId} minimumPassed exceeds assignment maximum`));
20766
20766
  const distinctBy = names(assignment?.distinctBy);
20767
20767
  if (distinctBy.length > 0) {
@@ -25101,7 +25101,7 @@ function compileStep(step, index, stepIndex, requirePlanHash, sink) {
25101
25101
  const maxAttempts = compileCountField(step.maxAttempts, 1, `${id}.maxAttempts`, id, sink);
25102
25102
  const timeoutMs = compileOptionalDuration(step.timeoutMs, `${id}.timeoutMs`, id, sink);
25103
25103
  const assignments = compileAssignments(step, id, agent, sink);
25104
- const join60 = compileJoin(step, id, sink);
25104
+ const join61 = compileJoin(step, id, sink);
25105
25105
  const requiredEvidence = compileEvidence(step, id, sink);
25106
25106
  const transitions = compileTransitions(step, id, index, stepIndex, sink);
25107
25107
  const outcomes = Object.keys(transitions).sort(compareCodeUnits5);
@@ -25129,7 +25129,7 @@ function compileStep(step, index, stepIndex, requirePlanHash, sink) {
25129
25129
  transitions: orderedTransitions,
25130
25130
  requiresPlanHash: requirePlanHash.includes(id),
25131
25131
  assignments,
25132
- join: join60
25132
+ join: join61
25133
25133
  };
25134
25134
  if (kind === "agent" || kind === "moa") {
25135
25135
  if (!agent) return void 0;
@@ -25179,15 +25179,15 @@ function compileAssignments(step, stepId, primaryAgentId, sink) {
25179
25179
  };
25180
25180
  }
25181
25181
  function compileJoin(step, stepId, sink) {
25182
- const join60 = objectValue2(step.join);
25183
- if (!join60) return { strategy: "all" };
25184
- const declared = stringValue2(join60.strategy);
25182
+ const join61 = objectValue2(step.join);
25183
+ if (!join61) return { strategy: "all" };
25184
+ const declared = stringValue2(join61.strategy);
25185
25185
  const strategy = declared && JOIN_STRATEGIES.has(declared) ? declared : "all";
25186
- const minimumPassed = compileOptionalCount(join60.minimumPassed, `${stepId}.join.minimumPassed`, stepId, sink);
25186
+ const minimumPassed = compileOptionalCount(join61.minimumPassed, `${stepId}.join.minimumPassed`, stepId, sink);
25187
25187
  const compiled = {
25188
25188
  strategy,
25189
25189
  ...minimumPassed !== void 0 ? { minimumPassed } : {},
25190
- ...typeof join60.cancelRemaining === "boolean" ? { cancelRemaining: join60.cancelRemaining } : {}
25190
+ ...typeof join61.cancelRemaining === "boolean" ? { cancelRemaining: join61.cancelRemaining } : {}
25191
25191
  };
25192
25192
  return compiled;
25193
25193
  }
@@ -38995,6 +38995,11 @@ function generateStudioLayout(plan, state) {
38995
38995
  };
38996
38996
  }
38997
38997
  var DEFAULT_STUDIO_PORT = 4242;
38998
+ function isKxmdAccountView(value) {
38999
+ if (typeof value !== "object" || value === null) return false;
39000
+ const account = value;
39001
+ return account.account === "kxmd" && account.database === "kxmd" && account.namespace === "kxmd" && account.role === "kxmd";
39002
+ }
38998
39003
  function createStudioServer(options = {}) {
38999
39004
  const targetPort = options.port ?? DEFAULT_STUDIO_PORT;
39000
39005
  const host = options.host ?? "127.0.0.1";
@@ -39013,6 +39018,46 @@ function createStudioServer(options = {}) {
39013
39018
  res.end(JSON.stringify({ ok: true, studio: true, port: targetPort }));
39014
39019
  return;
39015
39020
  }
39021
+ if (url.pathname === "/api/control/account" && req.method === "GET") {
39022
+ const base = options.controlUrl ?? process.env.KXM_CONTROL_URL;
39023
+ if (!base) {
39024
+ res.writeHead(503, { "Content-Type": "application/json" });
39025
+ res.end(JSON.stringify({ ok: false, error: "control_unconfigured" }));
39026
+ return;
39027
+ }
39028
+ let upstream;
39029
+ try {
39030
+ upstream = await fetch(new URL("/v1/account", base), {
39031
+ signal: AbortSignal.timeout(3e3)
39032
+ });
39033
+ } catch {
39034
+ res.writeHead(502, { "Content-Type": "application/json" });
39035
+ res.end(JSON.stringify({ ok: false, error: "control_unreachable" }));
39036
+ return;
39037
+ }
39038
+ const contentType = upstream.headers.get("content-type");
39039
+ if (!contentType?.toLowerCase().startsWith("application/json")) {
39040
+ res.writeHead(502, { "Content-Type": "application/json" });
39041
+ res.end(JSON.stringify({ ok: false, error: "control_response_invalid" }));
39042
+ return;
39043
+ }
39044
+ let body;
39045
+ try {
39046
+ body = await upstream.json();
39047
+ } catch {
39048
+ res.writeHead(502, { "Content-Type": "application/json" });
39049
+ res.end(JSON.stringify({ ok: false, error: "control_response_invalid" }));
39050
+ return;
39051
+ }
39052
+ if (upstream.ok && !isKxmdAccountView(body)) {
39053
+ res.writeHead(502, { "Content-Type": "application/json" });
39054
+ res.end(JSON.stringify({ ok: false, error: "control_response_invalid" }));
39055
+ return;
39056
+ }
39057
+ res.writeHead(upstream.status, { "Content-Type": "application/json" });
39058
+ res.end(JSON.stringify(body));
39059
+ return;
39060
+ }
39016
39061
  if (url.pathname === "/api/layout" && req.method === "GET") {
39017
39062
  const plan = options.planProvider?.();
39018
39063
  if (!plan) {
@@ -54567,7 +54612,7 @@ import { spawnSync as spawnSync15 } from "node:child_process";
54567
54612
  import { createHash as createHash19 } from "node:crypto";
54568
54613
  import { existsSync as existsSync50, mkdtempSync as mkdtempSync3, readFileSync as readFileSync44, rmSync as rmSync13 } from "node:fs";
54569
54614
  import { tmpdir as tmpdir3 } from "node:os";
54570
- import { basename as basename11, extname as extname3, join as join58, resolve as resolve33 } from "node:path";
54615
+ import { basename as basename11, extname as extname3, join as join59, resolve as resolve33 } from "node:path";
54571
54616
  import { createInterface as createInterface3 } from "node:readline";
54572
54617
 
54573
54618
  // plugins/kxm/src/improve.ts
@@ -54992,13 +55037,15 @@ function evaluatePromotionPolicy(candidate, policy = "manual_pr", options = {})
54992
55037
  // plugins/kxm/src/improve-sources.ts
54993
55038
  init_sqlite();
54994
55039
  init_project_config();
54995
- init_runtime_store();
55040
+ init_runtime_paths();
54996
55041
  init_route_switch();
54997
55042
  init_routing();
54998
55043
  init_telemetry();
54999
- import { existsSync as existsSync44 } from "node:fs";
55000
- import { resolve as resolve30 } from "node:path";
55044
+ import { existsSync as existsSync44, lstatSync as lstatSync8 } from "node:fs";
55045
+ import { join as join54, resolve as resolve30 } from "node:path";
55001
55046
  var ENGINE_EVENTS_SQL = "SELECT run_id, sequence, event_type, payload FROM events WHERE event_type IN ('routing.attempt.recorded','routing.route_switched','step.entered','run.status_changed') ORDER BY run_id, sequence";
55047
+ var REGISTRY_SIBLING_SQL = "SELECT sibling.project_key AS project_key, sibling.lane_of AS lane_of FROM projects AS current JOIN projects AS sibling ON sibling.project_id = current.project_id WHERE current.project_key = ? ORDER BY CASE WHEN sibling.lane_of IS NULL THEN 0 ELSE 1 END, sibling.registered_at, sibling.project_key";
55048
+ var PROJECT_KEY = /^[0-9a-f]{24}$/;
55002
55049
  var SIMULATED_HARNESS = "driver-simulated";
55003
55050
  function parsePayload(text) {
55004
55051
  try {
@@ -55104,6 +55151,61 @@ function readEngineRoutingRecords(path4) {
55104
55151
  source: { ...source, records: records.length, skippedInvalid, excludedSimulated, undecided }
55105
55152
  };
55106
55153
  }
55154
+ function registryUnreadable(registryDb, detail) {
55155
+ return new Error(`improve_source_unreadable: ${registryDb}: ${detail}`);
55156
+ }
55157
+ function discoverProjectEventStores(projectRoot, env) {
55158
+ const paths = kxmRuntimePaths({ env });
55159
+ const ownStore = join54(paths.projectsDir, projectRuntimeKey(projectRoot), "run-events.db");
55160
+ const registryDb = paths.registryDb;
55161
+ const info = lstatSync8(registryDb, { throwIfNoEntry: false });
55162
+ if (!info) return [ownStore];
55163
+ if (!info.isFile()) throw registryUnreadable(registryDb, "runtime registry must be a regular database file");
55164
+ for (const suffix of ["-wal", "-shm"]) {
55165
+ const sidecar = lstatSync8(`${registryDb}${suffix}`, { throwIfNoEntry: false });
55166
+ if (sidecar?.isSymbolicLink()) throw registryUnreadable(registryDb, "runtime registry sidecar must not be a link");
55167
+ }
55168
+ let database;
55169
+ try {
55170
+ database = openReadOnlyDatabase(registryDb);
55171
+ database.exec("PRAGMA busy_timeout = 5000");
55172
+ const versionRow = database.prepare("PRAGMA user_version").get();
55173
+ const version = versionRow?.user_version ?? 0;
55174
+ if (version !== 2) throw registryUnreadable(registryDb, `registry schema version ${version}; this reader requires 2`);
55175
+ const rows = database.prepare(REGISTRY_SIBLING_SQL).all(projectRuntimeKey(projectRoot));
55176
+ if (rows.length === 0) return [ownStore];
55177
+ return eventStoresFromRegistryRows(registryDb, paths.projectsDir, rows);
55178
+ } catch (error) {
55179
+ const message = error instanceof Error ? error.message : String(error);
55180
+ if (message.startsWith("improve_source_unreadable:")) throw error;
55181
+ throw new Error(`improve_source_unreadable: ${registryDb}: ${message}`, { cause: error });
55182
+ } finally {
55183
+ try {
55184
+ database?.close();
55185
+ } catch {
55186
+ }
55187
+ }
55188
+ }
55189
+ function eventStoresFromRegistryRows(registryDb, projectsDir, rows) {
55190
+ const seen = /* @__PURE__ */ new Set();
55191
+ const parsed = [];
55192
+ for (const row of rows) {
55193
+ const projectKey = typeof row.project_key === "string" ? row.project_key : "";
55194
+ const laneRaw = row.lane_of;
55195
+ const laneOf = laneRaw === null || laneRaw === void 0 ? null : typeof laneRaw === "string" ? laneRaw : "";
55196
+ if (!PROJECT_KEY.test(projectKey) || seen.has(projectKey) || laneOf !== null && !PROJECT_KEY.test(laneOf)) {
55197
+ throw registryUnreadable(registryDb, "registry identity is corrupt");
55198
+ }
55199
+ seen.add(projectKey);
55200
+ parsed.push({ projectKey, laneOf });
55201
+ }
55202
+ const homes = parsed.filter((row) => row.laneOf === null);
55203
+ const homeKey = homes[0]?.projectKey;
55204
+ if (homes.length !== 1 || homeKey === void 0 || parsed.some((row) => row.laneOf !== null && row.laneOf !== homeKey)) {
55205
+ throw registryUnreadable(registryDb, "registry identity is corrupt");
55206
+ }
55207
+ return parsed.map((row) => join54(projectsDir, row.projectKey, "run-events.db"));
55208
+ }
55107
55209
  function readJsonlSource(kind, path4) {
55108
55210
  const exists = existsSync44(path4);
55109
55211
  const records = exists ? readRoutingRecords(path4).map((entry) => entry.routing) : [];
@@ -55120,7 +55222,11 @@ function loadRoutingSources(options) {
55120
55222
  if (options.file !== void 0) {
55121
55223
  reads.push(readJsonlSource("file", resolve30(options.cwd, options.file)));
55122
55224
  } else {
55123
- if (projectRoot !== void 0) reads.push(readEngineRoutingRecords(kxmProjectRunEventsPath(projectRoot, options.env)));
55225
+ if (projectRoot !== void 0) {
55226
+ for (const path4 of discoverProjectEventStores(projectRoot, options.env)) {
55227
+ reads.push(readEngineRoutingRecords(path4));
55228
+ }
55229
+ }
55124
55230
  reads.push(readJsonlSource("telemetry", telemetryPath(options.logsDir)));
55125
55231
  }
55126
55232
  const seen = /* @__PURE__ */ new Set();
@@ -55152,7 +55258,7 @@ function loadRoutingSources(options) {
55152
55258
  var import_yaml21 = __toESM(require_dist(), 1);
55153
55259
  init_prices();
55154
55260
  import { existsSync as existsSync45, readFileSync as readFileSync40 } from "node:fs";
55155
- import { join as join54 } from "node:path";
55261
+ import { join as join55 } from "node:path";
55156
55262
  var DEFAULT_MODES_CONFIG = Object.freeze({
55157
55263
  schema: "kxm.modes.v1",
55158
55264
  majorModes: {
@@ -55215,7 +55321,7 @@ function loadModesConfig(projectRoot) {
55215
55321
  if (!projectRoot) {
55216
55322
  return DEFAULT_MODES_CONFIG;
55217
55323
  }
55218
- const modesPath = join54(projectRoot, ".kxm", "modes.yaml");
55324
+ const modesPath = join55(projectRoot, ".kxm", "modes.yaml");
55219
55325
  if (!existsSync45(modesPath)) {
55220
55326
  return DEFAULT_MODES_CONFIG;
55221
55327
  }
@@ -55306,7 +55412,7 @@ function calculatePromptFootprint(resolved, projectRoot = process.cwd(), catalog
55306
55412
  estimatedTokens: estimateTokens(baseSystemPromptChars)
55307
55413
  });
55308
55414
  for (const relPath of resolved.contextFiles) {
55309
- const fullPath = join54(projectRoot, relPath);
55415
+ const fullPath = join55(projectRoot, relPath);
55310
55416
  let chars = 0;
55311
55417
  if (existsSync45(fullPath)) {
55312
55418
  try {
@@ -55445,7 +55551,7 @@ init_safety_integrity();
55445
55551
  import { spawnSync as spawnSync14 } from "node:child_process";
55446
55552
  import { existsSync as existsSync46, mkdirSync as mkdirSync32, readFileSync as readFileSync41, readdirSync as readdirSync14, rmSync as rmSync12, statSync as statSync7 } from "node:fs";
55447
55553
  import { homedir as homedir8 } from "node:os";
55448
- import { join as join55, resolve as resolve31 } from "node:path";
55554
+ import { join as join56, resolve as resolve31 } from "node:path";
55449
55555
  var MAX_SSH_OUTPUT_BYTES = 50 * 1024;
55450
55556
  var MAX_SSH_OUTPUT_LINES = 2e3;
55451
55557
  var DEFAULT_SOCKET_DIR = ".kxm/run/ssh-sockets";
@@ -55471,7 +55577,7 @@ function truncateSshOutput(raw) {
55471
55577
  return { text, truncated };
55472
55578
  }
55473
55579
  function parseSshConfig(configPath) {
55474
- const targetPath = configPath ?? join55(homedir8(), ".ssh", "config");
55580
+ const targetPath = configPath ?? join56(homedir8(), ".ssh", "config");
55475
55581
  if (!existsSync46(targetPath)) {
55476
55582
  return [];
55477
55583
  }
@@ -55561,7 +55667,7 @@ function ensureSocketDir(socketDir = DEFAULT_SOCKET_DIR) {
55561
55667
  }
55562
55668
  function buildSshArgs(options) {
55563
55669
  const socketDir = ensureSocketDir(options.socketDir ?? DEFAULT_SOCKET_DIR);
55564
- const controlPath = join55(socketDir, "%C");
55670
+ const controlPath = join56(socketDir, "%C");
55565
55671
  const persist = options.controlPersist ?? DEFAULT_CONTROL_PERSIST;
55566
55672
  const args = [
55567
55673
  "-o",
@@ -55586,7 +55692,7 @@ function buildSshArgs(options) {
55586
55692
  }
55587
55693
  function checkControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn = spawnSync14) {
55588
55694
  const resolvedDir = ensureSocketDir(socketDir);
55589
- const controlPath = join55(resolvedDir, "%C");
55695
+ const controlPath = join56(resolvedDir, "%C");
55590
55696
  try {
55591
55697
  const result = execFn("ssh", ["-O", "check", "-o", `ControlPath=${controlPath}`, host], {
55592
55698
  encoding: "utf-8"
@@ -55598,7 +55704,7 @@ function checkControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn = spawn
55598
55704
  }
55599
55705
  function closeControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn = spawnSync14) {
55600
55706
  const resolvedDir = ensureSocketDir(socketDir);
55601
- const controlPath = join55(resolvedDir, "%C");
55707
+ const controlPath = join56(resolvedDir, "%C");
55602
55708
  try {
55603
55709
  const result = execFn("ssh", ["-O", "stop", "-o", `ControlPath=${controlPath}`, host], {
55604
55710
  encoding: "utf-8"
@@ -56349,7 +56455,7 @@ complete -c kxm -n "__fish_seen_subcommand_from goal" -a "create list get"
56349
56455
  // plugins/kxm/src/completion-install.ts
56350
56456
  import { existsSync as existsSync48, mkdirSync as mkdirSync33, readFileSync as readFileSync43, writeFileSync as writeFileSync28 } from "node:fs";
56351
56457
  import { homedir as homedir9 } from "node:os";
56352
- import { basename as basename10, delimiter, dirname as dirname24, isAbsolute as isAbsolute12, join as join56, resolve as resolve32 } from "node:path";
56458
+ import { basename as basename10, delimiter, dirname as dirname24, isAbsolute as isAbsolute12, join as join57, resolve as resolve32 } from "node:path";
56353
56459
  var COMPLETION_MARKER = "# kxm completion";
56354
56460
  var PATH_MARKER = "# kxm path";
56355
56461
  function detectShell(env = process.env, platform = process.platform) {
@@ -56371,25 +56477,25 @@ function completionScriptPath(shell, options = {}) {
56371
56477
  const env = options.env ?? process.env;
56372
56478
  const explicit = options.configDir ?? env.KXM_USER_CONFIG_DIR?.trim();
56373
56479
  const base = explicit && explicit.length > 0 ? resolve32(explicit) : resolve32(effectiveHome(options), ".config", "kxm");
56374
- return join56(base, "completions", `kxm.${shell}`);
56480
+ return join57(base, "completions", `kxm.${shell}`);
56375
56481
  }
56376
56482
  function bashRcCandidate(options) {
56377
56483
  const home = effectiveHome(options);
56378
- const candidates = [join56(home, ".bashrc"), join56(home, ".bash_profile")];
56484
+ const candidates = [join57(home, ".bashrc"), join57(home, ".bash_profile")];
56379
56485
  const existing = candidates.find((candidate) => existsSync48(candidate));
56380
56486
  return existing ?? candidates[0];
56381
56487
  }
56382
56488
  function zshRcCandidate(options) {
56383
56489
  const env = options.env ?? process.env;
56384
56490
  const home = effectiveHome(options);
56385
- if (env.ZDOTDIR?.trim()) return join56(resolve32(env.ZDOTDIR.trim()), ".zshrc");
56386
- return join56(home, ".zshrc");
56491
+ if (env.ZDOTDIR?.trim()) return join57(resolve32(env.ZDOTDIR.trim()), ".zshrc");
56492
+ return join57(home, ".zshrc");
56387
56493
  }
56388
56494
  function fishCompletionTarget(shell, options) {
56389
56495
  const env = options.env ?? process.env;
56390
56496
  const home = effectiveHome(options);
56391
- if (env.XDG_CONFIG_HOME?.trim()) return join56(resolve32(env.XDG_CONFIG_HOME.trim()), "fish", "completions", "kxm.fish");
56392
- return join56(home, ".config", "fish", "completions", "kxm.fish");
56497
+ if (env.XDG_CONFIG_HOME?.trim()) return join57(resolve32(env.XDG_CONFIG_HOME.trim()), "fish", "completions", "kxm.fish");
56498
+ return join57(home, ".config", "fish", "completions", "kxm.fish");
56393
56499
  }
56394
56500
  function completionRcTarget(shell, options = {}) {
56395
56501
  if (shell === "fish") {
@@ -56477,7 +56583,7 @@ function kxmBinDir(env = process.env) {
56477
56583
  const argv1 = env.KXM_ENTRY ?? process.argv[1];
56478
56584
  if (argv1 && isAbsolute12(argv1)) {
56479
56585
  const dir = dirname24(argv1);
56480
- if (existsSync48(join56(dir, process.platform === "win32" ? "kxm.cmd" : "kxm"))) return dir;
56586
+ if (existsSync48(join57(dir, process.platform === "win32" ? "kxm.cmd" : "kxm"))) return dir;
56481
56587
  }
56482
56588
  const pathEnv = env.PATH ?? "";
56483
56589
  for (const part of pathEnv.split(delimiter)) {
@@ -56517,7 +56623,7 @@ ${line}
56517
56623
  var import_yaml23 = __toESM(require_dist(), 1);
56518
56624
  init_routes();
56519
56625
  import { existsSync as existsSync49, mkdirSync as mkdirSync34, writeFileSync as writeFileSync29 } from "node:fs";
56520
- import { dirname as dirname25, join as join57 } from "node:path";
56626
+ import { dirname as dirname25, join as join58 } from "node:path";
56521
56627
  var ADMITTED_GUIDE_BINDINGS = Object.freeze({
56522
56628
  "anthropic/claude-fable-5.1": { harness: "claude", provider: "anthropic", model: "fable" },
56523
56629
  "anthropic/fable": { harness: "claude", provider: "anthropic", model: "fable" },
@@ -56948,7 +57054,7 @@ function renderGuideSetupFiles(projectRoot, plan) {
56948
57054
  const piMatch = binding.harness === "pi" && guidePiNoteMatches(binding);
56949
57055
  if (binding.harness === "pi" && !piMatch) {
56950
57056
  files.push({
56951
- path: join57(projectRoot, ".kxm", "agents", `${role}.yaml`),
57057
+ path: join58(projectRoot, ".kxm", "agents", `${role}.yaml`),
56952
57058
  content: (0, import_yaml23.stringify)(agentDocument(role, stage))
56953
57059
  });
56954
57060
  continue;
@@ -56966,22 +57072,22 @@ function renderGuideSetupFiles(projectRoot, plan) {
56966
57072
  modelDocument.origin = { source: GUIDE_PI_EVIDENCE_SOURCE, sha256: GUIDE_PI_EVIDENCE_SHA256 };
56967
57073
  if (!evidenceQueued) {
56968
57074
  files.push({
56969
- path: join57(projectRoot, GUIDE_PI_EVIDENCE_SOURCE),
57075
+ path: join58(projectRoot, GUIDE_PI_EVIDENCE_SOURCE),
56970
57076
  content: GUIDE_PI_EVIDENCE_NOTE
56971
57077
  });
56972
57078
  evidenceQueued = true;
56973
57079
  }
56974
57080
  }
56975
57081
  files.push({
56976
- path: join57(projectRoot, ".kxm", "agents", `${role}.yaml`),
57082
+ path: join58(projectRoot, ".kxm", "agents", `${role}.yaml`),
56977
57083
  content: (0, import_yaml23.stringify)(agentDocument(role, stage))
56978
57084
  });
56979
57085
  files.push({
56980
- path: join57(projectRoot, ".kxm", "models", `${routeId2}.yaml`),
57086
+ path: join58(projectRoot, ".kxm", "models", `${routeId2}.yaml`),
56981
57087
  content: (0, import_yaml23.stringify)(modelDocument)
56982
57088
  });
56983
57089
  files.push({
56984
- path: join57(projectRoot, ".kxm", "roles", `${role}.yaml`),
57090
+ path: join58(projectRoot, ".kxm", "roles", `${role}.yaml`),
56985
57091
  content: (0, import_yaml23.stringify)({
56986
57092
  schema: "kxm.role.v2",
56987
57093
  id: role,
@@ -56994,7 +57100,7 @@ function renderGuideSetupFiles(projectRoot, plan) {
56994
57100
  }
56995
57101
  for (const workflow of plan.workflows) {
56996
57102
  files.push({
56997
- path: join57(projectRoot, ".kxm", "workflows", `${workflow.slug}.yaml`),
57103
+ path: join58(projectRoot, ".kxm", "workflows", `${workflow.slug}.yaml`),
56998
57104
  content: (0, import_yaml23.stringify)(workflowDocument(workflow))
56999
57105
  });
57000
57106
  }
@@ -57011,7 +57117,7 @@ function mergeGuideRouteAdmission(projectRoot, plan) {
57011
57117
  }
57012
57118
  if (added.length === 0) return added;
57013
57119
  policy.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
57014
- const path4 = join57(projectRoot, ".kxm", "routes.yaml");
57120
+ const path4 = join58(projectRoot, ".kxm", "routes.yaml");
57015
57121
  mkdirSync34(dirname25(path4), { recursive: true });
57016
57122
  writeFileSync29(path4, (0, import_yaml23.stringify)(policy), "utf8");
57017
57123
  return added;
@@ -57091,7 +57197,7 @@ function applyKxmPackageUpdate(runtime, notice) {
57091
57197
  detail: `release v${notice.latest} has no sha256 digest for ${name}; refusing to install`
57092
57198
  };
57093
57199
  }
57094
- const releaseDir = mkdtempSync3(join58(tmpdir3(), "kxm-pkg-update-"));
57200
+ const releaseDir = mkdtempSync3(join59(tmpdir3(), "kxm-pkg-update-"));
57095
57201
  try {
57096
57202
  const downloader = notice.source === "github" && !ghReleaseDownloadAvailable(runtime.env) ? "curl" : "gh";
57097
57203
  const planned = planKxmPackageUpdate(notice.source, notice.latest, releaseDir, notice.asset, downloader, notice.localRoot);
@@ -57529,7 +57635,7 @@ async function cmdImprove(runtime, options = {}) {
57529
57635
  print(runtime.io, runtime.json, { ok: false, command: "improve", error: "config_invalid", detail: message }, `config_invalid: ${message}`);
57530
57636
  return 1;
57531
57637
  }
57532
- const candidatesDir = options.outDir ? resolve33(runtime.cwd, options.outDir) : join58(root, ".kxm", "candidates");
57638
+ const candidatesDir = options.outDir ? resolve33(runtime.cwd, options.outDir) : join59(root, ".kxm", "candidates");
57533
57639
  const report2 = buildImprovementReport(loaded2.records, {
57534
57640
  candidatesDir,
57535
57641
  projectRoot: root,
@@ -57539,7 +57645,7 @@ async function cmdImprove(runtime, options = {}) {
57539
57645
  halfLifeDays: config.improvement.telemetryHalfLifeDays,
57540
57646
  routeSwitches: loaded2.routeSwitches
57541
57647
  });
57542
- const reportDir = join58(runtime.dirs.assets, "improvements");
57648
+ const reportDir = join59(runtime.dirs.assets, "improvements");
57543
57649
  const reportPath = writeImprovementReport(reportDir, report2, runtime.dryRun);
57544
57650
  const text = [
57545
57651
  "Sources:",
@@ -57881,7 +57987,7 @@ async function cmdRoutingReport(runtime, options) {
57881
57987
  let catalog;
57882
57988
  if (includeEquivalentListCost) {
57883
57989
  try {
57884
- const pricesPath = options.prices ? resolve33(runtime.cwd, options.prices) : join58(runtime.dirs.workspace, "prices.yaml");
57990
+ const pricesPath = options.prices ? resolve33(runtime.cwd, options.prices) : join59(runtime.dirs.workspace, "prices.yaml");
57885
57991
  catalog = loadPriceCatalog(pricesPath);
57886
57992
  } catch {
57887
57993
  }
@@ -25462,7 +25462,7 @@ function sessionTokenFixHint(policy) {
25462
25462
  }
25463
25463
 
25464
25464
  // plugins/kxm/src/mcp-server.ts
25465
- var VERSION = "0.7.180";
25465
+ var VERSION = "0.7.182";
25466
25466
  var CONFIGURE_PLUGIN = "/plugin configure kxm@kxm";
25467
25467
  var inbox = /* @__PURE__ */ new Map();
25468
25468
  var notifiedInbox = /* @__PURE__ */ new Set();
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-plugin",
3
- "version": "0.7.180",
3
+ "version": "0.7.182",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "engines": {
@@ -1,8 +1,8 @@
1
- import { existsSync } from "node:fs";
2
- import { resolve } from "node:path";
3
- import { DatabaseSync } from "./sqlite.ts";
1
+ import { existsSync, lstatSync } from "node:fs";
2
+ import { join, resolve } from "node:path";
3
+ import { DatabaseSync, openReadOnlyDatabase } from "./sqlite.ts";
4
4
  import { discoverKxmProjectRoot } from "./project-config.ts";
5
- import { kxmProjectRunEventsPath } from "./runtime-store.ts";
5
+ import { kxmRuntimePaths, projectRuntimeKey } from "./runtime-paths.ts";
6
6
  import { parseRouteSwitchRecord, type RouteSwitchRecord } from "./route-switch.ts";
7
7
  import { parseRoutingRecordV2, ROUTING_RECORD_V2_SCHEMA, type RoutingRecord, type RoutingRecordV2 } from "./routing.ts";
8
8
  import { readRoutingRecords, telemetryPath } from "./telemetry.ts";
@@ -10,10 +10,14 @@ import { readRoutingRecords, telemetryPath } from "./telemetry.ts";
10
10
  /**
11
11
  * Routing-record sources for `kxm improve` and `kxm routing report`.
12
12
  *
13
- * The project's Runtime event store is read read-only: one SELECT over the
14
- * events table, never the runs table, the run plans, or the prompts sidecar,
15
- * and never a write or a migration. Each settled attempt's outcome is resolved
16
- * in memory from the event log; nothing resolved here is stored.
13
+ * Each Runtime event store is read read-only: one SELECT over the events
14
+ * table, never the runs table, the run plans, or the prompts sidecar, and
15
+ * never a write or a migration. When the user-state registry is present and
16
+ * this checkout is registered, the home store and every lane store for that
17
+ * project id are read. The registry itself is opened read-only at schema
18
+ * version 2: no constructor, migration, write lock, directory, or sidecar.
19
+ * Each settled attempt's outcome is resolved in memory from the event log;
20
+ * nothing resolved here is stored.
17
21
  */
18
22
 
19
23
  export interface RoutingSourceSummary {
@@ -45,6 +49,15 @@ const ENGINE_EVENTS_SQL = "SELECT run_id, sequence, event_type, payload FROM eve
45
49
  + " WHERE event_type IN ('routing.attempt.recorded','routing.route_switched','step.entered','run.status_changed')"
46
50
  + " ORDER BY run_id, sequence";
47
51
 
52
+ /** Home row first, then lanes by registration time and project key. One query. */
53
+ const REGISTRY_SIBLING_SQL = "SELECT sibling.project_key AS project_key, sibling.lane_of AS lane_of"
54
+ + " FROM projects AS current"
55
+ + " JOIN projects AS sibling ON sibling.project_id = current.project_id"
56
+ + " WHERE current.project_key = ?"
57
+ + " ORDER BY CASE WHEN sibling.lane_of IS NULL THEN 0 ELSE 1 END, sibling.registered_at, sibling.project_key";
58
+
59
+ const PROJECT_KEY = /^[0-9a-f]{24}$/;
60
+
48
61
  /** The simulated producer's harness label; its attempts measure nothing. */
49
62
  const SIMULATED_HARNESS = "driver-simulated";
50
63
 
@@ -175,6 +188,75 @@ export function readEngineRoutingRecords(path: string): EngineRoutingRead {
175
188
  };
176
189
  }
177
190
 
191
+ function registryUnreadable(registryDb: string, detail: string): Error {
192
+ return new Error(`improve_source_unreadable: ${registryDb}: ${detail}`);
193
+ }
194
+
195
+ /**
196
+ * Event stores for this checkout.
197
+ *
198
+ * An absent registry, or a registry that has no row for this checkout's
199
+ * project key, keeps the single store derived from the checkout path. A
200
+ * present registry must be a regular schema-v2 database. Paths then come
201
+ * from the stored keys and the Runtime projects directory, so a stale
202
+ * checkout path in the registry cannot choose the store.
203
+ */
204
+ function discoverProjectEventStores(projectRoot: string, env: NodeJS.ProcessEnv): string[] {
205
+ const paths = kxmRuntimePaths({ env });
206
+ const ownStore = join(paths.projectsDir, projectRuntimeKey(projectRoot), "run-events.db");
207
+ const registryDb = paths.registryDb;
208
+ const info = lstatSync(registryDb, { throwIfNoEntry: false });
209
+ if (!info) return [ownStore];
210
+ if (!info.isFile()) throw registryUnreadable(registryDb, "runtime registry must be a regular database file");
211
+ for (const suffix of ["-wal", "-shm"]) {
212
+ const sidecar = lstatSync(`${registryDb}${suffix}`, { throwIfNoEntry: false });
213
+ if (sidecar?.isSymbolicLink()) throw registryUnreadable(registryDb, "runtime registry sidecar must not be a link");
214
+ }
215
+
216
+ let database: DatabaseSync | undefined;
217
+ try {
218
+ database = openReadOnlyDatabase(registryDb);
219
+ database.exec("PRAGMA busy_timeout = 5000");
220
+ const versionRow = database.prepare("PRAGMA user_version").get() as { user_version?: number } | undefined;
221
+ const version = versionRow?.user_version ?? 0;
222
+ if (version !== 2) throw registryUnreadable(registryDb, `registry schema version ${version}; this reader requires 2`);
223
+ const rows = database.prepare(REGISTRY_SIBLING_SQL).all(projectRuntimeKey(projectRoot));
224
+ if (rows.length === 0) return [ownStore];
225
+ return eventStoresFromRegistryRows(registryDb, paths.projectsDir, rows);
226
+ } catch (error) {
227
+ const message = error instanceof Error ? error.message : String(error);
228
+ if (message.startsWith("improve_source_unreadable:")) throw error;
229
+ throw new Error(`improve_source_unreadable: ${registryDb}: ${message}`, { cause: error });
230
+ } finally {
231
+ try {
232
+ database?.close();
233
+ } catch {
234
+ // Closing a read-only handle has nothing to flush.
235
+ }
236
+ }
237
+ }
238
+
239
+ function eventStoresFromRegistryRows(registryDb: string, projectsDir: string, rows: Array<Record<string, unknown>>): string[] {
240
+ const seen = new Set<string>();
241
+ const parsed: Array<{ projectKey: string; laneOf: string | null }> = [];
242
+ for (const row of rows) {
243
+ const projectKey = typeof row.project_key === "string" ? row.project_key : "";
244
+ const laneRaw = row.lane_of;
245
+ const laneOf = laneRaw === null || laneRaw === undefined ? null : typeof laneRaw === "string" ? laneRaw : "";
246
+ if (!PROJECT_KEY.test(projectKey) || seen.has(projectKey) || (laneOf !== null && !PROJECT_KEY.test(laneOf))) {
247
+ throw registryUnreadable(registryDb, "registry identity is corrupt");
248
+ }
249
+ seen.add(projectKey);
250
+ parsed.push({ projectKey, laneOf });
251
+ }
252
+ const homes = parsed.filter((row) => row.laneOf === null);
253
+ const homeKey = homes[0]?.projectKey;
254
+ if (homes.length !== 1 || homeKey === undefined || parsed.some((row) => row.laneOf !== null && row.laneOf !== homeKey)) {
255
+ throw registryUnreadable(registryDb, "registry identity is corrupt");
256
+ }
257
+ return parsed.map((row) => join(projectsDir, row.projectKey, "run-events.db"));
258
+ }
259
+
178
260
  function readJsonlSource(kind: "telemetry" | "file", path: string): { records: Array<RoutingRecord | RoutingRecordV2>; source: RoutingSourceSummary } {
179
261
  const exists = existsSync(path);
180
262
  const records = exists ? readRoutingRecords(path).map((entry) => entry.routing) : [];
@@ -190,10 +272,10 @@ function attemptKey(record: RoutingRecord | RoutingRecordV2): string | undefined
190
272
  /**
191
273
  * Load routing records for improvement and routing reports.
192
274
  *
193
- * With `file`, only that file is read. Otherwise the project's Runtime event
194
- * store (when cwd is inside a KXM project) comes first, then telemetry.jsonl.
195
- * v2 records are de-duplicated by attemptId: the first occurrence wins and the
196
- * later source counts the drop.
275
+ * With `file`, only that file is read. Otherwise every discovered Runtime
276
+ * event store (when cwd is inside a KXM project) comes first, then
277
+ * telemetry.jsonl. v2 records are de-duplicated by attemptId: the first
278
+ * occurrence wins and the later source counts the drop.
197
279
  */
198
280
  export function loadRoutingSources(options: {
199
281
  cwd: string;
@@ -206,7 +288,11 @@ export function loadRoutingSources(options: {
206
288
  if (options.file !== undefined) {
207
289
  reads.push(readJsonlSource("file", resolve(options.cwd, options.file)));
208
290
  } else {
209
- if (projectRoot !== undefined) reads.push(readEngineRoutingRecords(kxmProjectRunEventsPath(projectRoot, options.env)));
291
+ if (projectRoot !== undefined) {
292
+ for (const path of discoverProjectEventStores(projectRoot, options.env)) {
293
+ reads.push(readEngineRoutingRecords(path));
294
+ }
295
+ }
210
296
  reads.push(readJsonlSource("telemetry", telemetryPath(options.logsDir)));
211
297
  }
212
298
 
@@ -11,7 +11,7 @@ import { deliverInboxNotification } from "./inbox.ts";
11
11
  import type { HubEvent, MessageRecord } from "./protocol.ts";
12
12
  import { sessionTokenFixHint } from "./session-token-hint.ts";
13
13
 
14
- const VERSION = "0.7.180";
14
+ const VERSION = "0.7.182";
15
15
  const CONFIGURE_PLUGIN = "/plugin configure kxm@kxm";
16
16
  const inbox = new Map<string, MessageRecord>();
17
17
  const notifiedInbox = new Set<string>();