@kontextmind/kxm 0.7.128 → 0.7.130

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.
@@ -14783,7 +14783,7 @@ import { spawn as spawn3 } from "node:child_process";
14783
14783
  import { createHash as createHash15, createHmac, randomBytes as randomBytes2, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
14784
14784
  import { chmodSync as chmodSync2, existsSync as existsSync16, lstatSync as lstatSync6, mkdirSync as mkdirSync9, readFileSync as readFileSync13, renameSync as renameSync5, rmSync as rmSync4, writeFileSync as writeFileSync9 } from "node:fs";
14785
14785
  import { createServer } from "node:http";
14786
- import { dirname as dirname11, isAbsolute as isAbsolute7, join as join18 } from "node:path";
14786
+ import { dirname as dirname11, isAbsolute as isAbsolute8, join as join18 } from "node:path";
14787
14787
 
14788
14788
  // plugins/kxm/src/repo-root.ts
14789
14789
  import { existsSync } from "node:fs";
@@ -18244,9 +18244,10 @@ function legacyConfigFilesAt(root) {
18244
18244
  }
18245
18245
 
18246
18246
  // plugins/kxm/src/runtime-store.ts
18247
+ import { spawnSync as spawnSync3 } from "node:child_process";
18247
18248
  import { createHash as createHash6, randomUUID } from "node:crypto";
18248
- import { existsSync as existsSync6, lstatSync as lstatSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
18249
- import { dirname as dirname5, resolve as resolve6 } from "node:path";
18249
+ import { existsSync as existsSync6, lstatSync as lstatSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, realpathSync as realpathSync3, statSync, writeFileSync as writeFileSync2 } from "node:fs";
18250
+ import { dirname as dirname5, isAbsolute as isAbsolute4, resolve as resolve6 } from "node:path";
18250
18251
 
18251
18252
  // plugins/kxm/src/sqlite.ts
18252
18253
  import { existsSync as existsSync4 } from "node:fs";
@@ -18760,22 +18761,25 @@ function ensureWalJournalMode(database, file, description, timeoutMs = 5e3) {
18760
18761
  Atomics.wait(sleeper, 0, 0, 10);
18761
18762
  }
18762
18763
  }
18764
+ function assertDatabaseFile(file, description) {
18765
+ checkedParent(file, description);
18766
+ const stat = lstatSync2(file, { throwIfNoEntry: false });
18767
+ if (stat) {
18768
+ if (stat.isSymbolicLink() || !stat.isFile()) {
18769
+ throw databaseError("runtime_path_invalid", description, `${description} must be a regular file, not a link or directory`);
18770
+ }
18771
+ }
18772
+ for (const sidecar of [`${file}-wal`, `${file}-shm`]) {
18773
+ const info = lstatSync2(sidecar, { throwIfNoEntry: false });
18774
+ if (info?.isSymbolicLink()) {
18775
+ throw databaseError("runtime_path_invalid", description, `${description} sidecar must not be a link`);
18776
+ }
18777
+ }
18778
+ }
18763
18779
  function openDatabase(file, description, spec) {
18764
18780
  const isMemory = file === ":memory:";
18765
18781
  if (!isMemory) {
18766
- checkedParent(file, description);
18767
- const stat = lstatSync2(file, { throwIfNoEntry: false });
18768
- if (stat) {
18769
- if (stat.isSymbolicLink() || !stat.isFile()) {
18770
- throw databaseError("runtime_path_invalid", description, `${description} must be a regular file, not a link or directory`);
18771
- }
18772
- }
18773
- for (const sidecar of [`${file}-wal`, `${file}-shm`]) {
18774
- const info = lstatSync2(sidecar, { throwIfNoEntry: false });
18775
- if (info?.isSymbolicLink()) {
18776
- throw databaseError("runtime_path_invalid", description, `${description} sidecar must not be a link`);
18777
- }
18778
- }
18782
+ assertDatabaseFile(file, description);
18779
18783
  }
18780
18784
  const database = new DatabaseSync(file);
18781
18785
  let transaction = false;
@@ -18804,7 +18808,7 @@ function openDatabase(file, description, spec) {
18804
18808
  throw databaseError(
18805
18809
  "runtime_schema_outdated",
18806
18810
  file,
18807
- `${description} is schema version ${version}; this build requires ${spec.version}. Delete the state file to start fresh and let its owning process recreate it (\`kxm hub start\` for hub state, the Runtime for registry/event stores); \`kxm init\` is project-only and rebuilds no database \u2014 upgrading old state in place is deliberately unsupported`
18811
+ `${description} is schema version ${version}; this build requires ${spec.version}. Delete the state file to start fresh and let its owning process recreate it (\`kxm hub start\` for hub state, the Runtime for event stores). The Runtime registry copy from schema 1 to 2 is the one exception. \`kxm init\` is project-only and rebuilds no database. Upgrading any other old state in place is deliberately unsupported`
18808
18812
  );
18809
18813
  }
18810
18814
  if (spec.tables) {
@@ -18917,10 +18921,10 @@ function runtimeIssue(phase, code, file, message) {
18917
18921
  function runtimeError(code, file, message) {
18918
18922
  return new KxmConfigError([runtimeIssue("semantic", code, file, message)]);
18919
18923
  }
18920
- var KXM_REGISTRY_SCHEMA_VERSION = 1;
18924
+ var KXM_REGISTRY_SCHEMA_VERSION = 2;
18921
18925
  var REGISTRY_TABLES = {
18922
18926
  supervisor: ["singleton_id", "runtime_id", "pid", "port", "token_hash", "started_at", "heartbeat_at", "state"],
18923
- projects: ["project_id", "project_root", "project_key", "home_runtime_id", "config_revision", "registered_at"]
18927
+ projects: ["project_id", "project_root", "project_key", "home_runtime_id", "config_revision", "registered_at", "lane_of"]
18924
18928
  };
18925
18929
  var REGISTRY_SCHEMA = `
18926
18930
  CREATE TABLE supervisor (
@@ -18934,14 +18938,117 @@ CREATE TABLE supervisor (
18934
18938
  state TEXT NOT NULL
18935
18939
  ) STRICT;
18936
18940
  CREATE TABLE projects (
18937
- project_id TEXT PRIMARY KEY,
18941
+ project_key TEXT PRIMARY KEY,
18942
+ project_id TEXT NOT NULL,
18938
18943
  project_root TEXT NOT NULL,
18939
- project_key TEXT NOT NULL UNIQUE,
18940
18944
  home_runtime_id TEXT NOT NULL,
18941
18945
  config_revision TEXT,
18942
- registered_at TEXT NOT NULL
18946
+ registered_at TEXT NOT NULL,
18947
+ lane_of TEXT
18943
18948
  ) STRICT;
18949
+ CREATE INDEX projects_by_project_id ON projects (project_id);
18944
18950
  `;
18951
+ var PROJECT_COLUMNS = "project_id, project_root, project_key, home_runtime_id, config_revision, registered_at, lane_of";
18952
+ function registrationFromRow(row) {
18953
+ return {
18954
+ projectId: row.project_id,
18955
+ projectRoot: row.project_root,
18956
+ projectKey: row.project_key,
18957
+ homeRuntimeId: row.home_runtime_id,
18958
+ ...row.config_revision !== null ? { configRevision: row.config_revision } : {},
18959
+ ...row.lane_of !== null ? { laneOf: row.lane_of } : {},
18960
+ registeredAt: row.registered_at
18961
+ };
18962
+ }
18963
+ function controlRootDirectoryExists(projectRoot) {
18964
+ const stat = statSync(projectRoot, { throwIfNoEntry: false });
18965
+ return Boolean(stat?.isDirectory());
18966
+ }
18967
+ function gitEnv() {
18968
+ return Object.fromEntries(Object.entries(process.env).filter(([name]) => !name.toUpperCase().startsWith("GIT_")));
18969
+ }
18970
+ function gitCommonDirectory(projectRoot) {
18971
+ const result = spawnSync3("git", ["-C", projectRoot, "rev-parse", "--git-common-dir"], {
18972
+ encoding: "utf8",
18973
+ env: gitEnv(),
18974
+ timeout: 3e4,
18975
+ windowsHide: true
18976
+ });
18977
+ if (result.status !== 0) return void 0;
18978
+ const text = (result.stdout ?? "").trim();
18979
+ if (!text) return void 0;
18980
+ const absolute = isAbsolute4(text) ? text : resolve6(projectRoot, text);
18981
+ let canonical;
18982
+ try {
18983
+ canonical = realpathSync3.native(absolute);
18984
+ } catch {
18985
+ canonical = resolve6(absolute);
18986
+ }
18987
+ return process.platform === "win32" ? canonical.toLocaleLowerCase("en-US") : canonical;
18988
+ }
18989
+ function isPrimaryWorktree(projectRoot, commonDir) {
18990
+ if (commonDir === void 0) return false;
18991
+ const dotGit = resolve6(projectRoot, ".git");
18992
+ let canonical;
18993
+ try {
18994
+ canonical = realpathSync3.native(dotGit);
18995
+ } catch {
18996
+ canonical = resolve6(dotGit);
18997
+ }
18998
+ if (process.platform === "win32") canonical = canonical.toLocaleLowerCase("en-US");
18999
+ return canonical === commonDir;
19000
+ }
19001
+ function migrateRegistryLanes(file) {
19002
+ const stat = lstatSync3(file, { throwIfNoEntry: false });
19003
+ if (!stat) return;
19004
+ assertDatabaseFile(file, "runtime registry");
19005
+ const database = new DatabaseSync(file);
19006
+ let transaction = false;
19007
+ try {
19008
+ database.exec("PRAGMA busy_timeout = 5000");
19009
+ const current = database.prepare("PRAGMA user_version").get();
19010
+ if ((current?.user_version ?? 0) !== 1) return;
19011
+ database.exec("BEGIN IMMEDIATE");
19012
+ transaction = true;
19013
+ const locked = database.prepare("PRAGMA user_version").get();
19014
+ if ((locked?.user_version ?? 0) !== 1) {
19015
+ database.exec("ROLLBACK");
19016
+ transaction = false;
19017
+ return;
19018
+ }
19019
+ if (!tableColumns(database, "projects").includes("lane_of")) {
19020
+ database.exec(`
19021
+ CREATE TABLE projects_v2 (
19022
+ project_key TEXT PRIMARY KEY,
19023
+ project_id TEXT NOT NULL,
19024
+ project_root TEXT NOT NULL,
19025
+ home_runtime_id TEXT NOT NULL,
19026
+ config_revision TEXT,
19027
+ registered_at TEXT NOT NULL,
19028
+ lane_of TEXT
19029
+ ) STRICT;
19030
+ INSERT INTO projects_v2 (project_key, project_id, project_root, home_runtime_id, config_revision, registered_at, lane_of)
19031
+ SELECT project_key, project_id, project_root, home_runtime_id, config_revision, registered_at, NULL FROM projects;
19032
+ DROP TABLE projects;
19033
+ ALTER TABLE projects_v2 RENAME TO projects;
19034
+ CREATE INDEX projects_by_project_id ON projects (project_id);
19035
+ `);
19036
+ }
19037
+ database.exec("PRAGMA user_version = 2");
19038
+ database.exec("COMMIT");
19039
+ transaction = false;
19040
+ } catch (error) {
19041
+ if (transaction) {
19042
+ try {
19043
+ database.exec("ROLLBACK");
19044
+ } catch {
19045
+ }
19046
+ }
19047
+ throw error;
19048
+ } finally {
19049
+ database.close();
19050
+ }
19051
+ }
18945
19052
  function readKxmSupervisorRecord(database) {
18946
19053
  const row = database.prepare("SELECT runtime_id, pid, port, token_hash, started_at, heartbeat_at, state FROM supervisor WHERE singleton_id = 1").get();
18947
19054
  return row ? {
@@ -18959,6 +19066,7 @@ var KxmRuntimeRegistry = class {
18959
19066
  database;
18960
19067
  constructor(path) {
18961
19068
  this.path = resolve6(path);
19069
+ migrateRegistryLanes(this.path);
18962
19070
  this.database = openDatabase(this.path, "runtime registry", {
18963
19071
  schema: REGISTRY_SCHEMA,
18964
19072
  version: KXM_REGISTRY_SCHEMA_VERSION,
@@ -19037,57 +19145,188 @@ var KxmRuntimeRegistry = class {
19037
19145
  supervisor() {
19038
19146
  return this.readSupervisorRow();
19039
19147
  }
19040
- /** Register or revalidate a project's home binding. Home Runtime is immutable. */
19041
- /** All projects registered to this Runtime, for restart recovery: the
19042
- * supervisor needs to reopen their contexts so pending outbox rows resume
19043
- * syncing and presence keeps beating. */
19148
+ /** All projects registered to this Runtime, including lane roots, for restart
19149
+ * recovery: the supervisor reopens their contexts so pending outbox rows
19150
+ * resume syncing and presence keeps beating. */
19044
19151
  projectsForRuntime(homeRuntimeId) {
19045
19152
  const rows = this.database.prepare(
19046
19153
  "SELECT project_root, project_id FROM projects WHERE home_runtime_id = ? ORDER BY registered_at"
19047
19154
  ).all(homeRuntimeId);
19048
19155
  return rows.map((row) => ({ projectRoot: row.project_root, projectId: row.project_id }));
19049
19156
  }
19157
+ /**
19158
+ * Register or revalidate a control root. The home runtime of a live row is
19159
+ * immutable. A second root whose git common directory matches a live row
19160
+ * for the same project is a lane, unless the registering root is the primary
19161
+ * worktree and the live home row is not: then the primary becomes the home
19162
+ * row (same home runtime id) and every other live row of that repository
19163
+ * becomes its lane. A primary never becomes a lane. A root whose directory
19164
+ * is gone is replaced. A live root that is a different repository is
19165
+ * `project_home_conflict`.
19166
+ */
19050
19167
  registerProject(registration) {
19051
19168
  const projectRoot = resolve6(registration.projectRoot);
19052
19169
  const projectKey = projectRuntimeKey(projectRoot);
19170
+ const candidateCommon = gitCommonDirectory(projectRoot);
19171
+ const commonByRoot = /* @__PURE__ */ new Map();
19172
+ for (let attempt = 0; attempt < 8; attempt += 1) {
19173
+ this.database.exec("BEGIN IMMEDIATE");
19174
+ let rootsToMeasure;
19175
+ try {
19176
+ const byKey = this.projectRow("SELECT " + PROJECT_COLUMNS + " FROM projects WHERE project_key = ?", projectKey);
19177
+ if (byKey) {
19178
+ const problems = [];
19179
+ if (byKey.project_id !== registration.projectId) problems.push(`control root is already bound to a different project id ${byKey.project_id}`);
19180
+ if (byKey.home_runtime_id !== registration.homeRuntimeId) problems.push(`project ${registration.projectId} home runtime is immutable and cannot be rebound`);
19181
+ if (problems.length > 0) {
19182
+ this.database.exec("ROLLBACK");
19183
+ throw runtimeError("project_home_conflict", ".kxm/project.yaml", problems.join("; "));
19184
+ }
19185
+ const result = registrationFromRow(byKey);
19186
+ this.database.exec("COMMIT");
19187
+ return result;
19188
+ }
19189
+ const rows = this.database.prepare(
19190
+ `SELECT ${PROJECT_COLUMNS} FROM projects WHERE project_id = ? ORDER BY registered_at`
19191
+ ).all(registration.projectId);
19192
+ const live = rows.filter((row) => controlRootDirectoryExists(row.project_root));
19193
+ const dead = rows.filter((row) => !controlRootDirectoryExists(row.project_root));
19194
+ const unknown = live.filter((row) => !commonByRoot.has(row.project_root));
19195
+ if (unknown.length > 0) {
19196
+ rootsToMeasure = unknown.map((row) => row.project_root);
19197
+ this.database.exec("ROLLBACK");
19198
+ } else if (live.length > 0) {
19199
+ const anchor = live.find((row) => {
19200
+ const dir = commonByRoot.get(row.project_root);
19201
+ return candidateCommon !== void 0 && dir === candidateCommon;
19202
+ });
19203
+ if (!anchor) {
19204
+ this.database.exec("ROLLBACK");
19205
+ throw runtimeError(
19206
+ "project_home_conflict",
19207
+ ".kxm/project.yaml",
19208
+ `project ${registration.projectId} is already bound to a different control root`
19209
+ );
19210
+ }
19211
+ if (live.some((row) => row.home_runtime_id !== registration.homeRuntimeId)) {
19212
+ this.database.exec("ROLLBACK");
19213
+ throw runtimeError(
19214
+ "project_home_conflict",
19215
+ ".kxm/project.yaml",
19216
+ `project ${registration.projectId} home runtime is immutable and cannot be rebound`
19217
+ );
19218
+ }
19219
+ const sameRepo = live.filter((row) => commonByRoot.get(row.project_root) === candidateCommon);
19220
+ const home = sameRepo.find((row) => row.lane_of === null);
19221
+ const registeringIsPrimary = isPrimaryWorktree(projectRoot, candidateCommon);
19222
+ const homeIsPrimary = home !== void 0 && isPrimaryWorktree(home.project_root, commonByRoot.get(home.project_root));
19223
+ if (registeringIsPrimary && !homeIsPrimary) {
19224
+ this.insertHome(registration, projectRoot, projectKey);
19225
+ for (const row of sameRepo) {
19226
+ this.database.prepare("UPDATE projects SET lane_of = ? WHERE project_key = ?").run(projectKey, row.project_key);
19227
+ }
19228
+ this.database.exec("COMMIT");
19229
+ registration.logger?.({
19230
+ event: "project_home_promoted",
19231
+ projectId: registration.projectId,
19232
+ projectRoot,
19233
+ homeRuntimeId: registration.homeRuntimeId,
19234
+ previousHomeRoot: home?.project_root ?? null,
19235
+ previousHomeKey: home?.project_key ?? null
19236
+ });
19237
+ return this.insertedRegistration(registration, projectRoot, projectKey);
19238
+ }
19239
+ if (registeringIsPrimary) {
19240
+ this.database.exec("ROLLBACK");
19241
+ throw runtimeError(
19242
+ "project_home_conflict",
19243
+ ".kxm/project.yaml",
19244
+ `project ${registration.projectId} primary checkout is already the home row`
19245
+ );
19246
+ }
19247
+ const laneOf = anchor.lane_of ?? anchor.project_key;
19248
+ this.database.prepare(`
19249
+ INSERT INTO projects (project_key, project_id, project_root, home_runtime_id, config_revision, registered_at, lane_of)
19250
+ VALUES (?, ?, ?, ?, ?, ?, ?)
19251
+ `).run(projectKey, registration.projectId, projectRoot, registration.homeRuntimeId, registration.configRevision ?? null, registration.now, laneOf);
19252
+ const result = this.insertedRegistration(registration, projectRoot, projectKey, laneOf);
19253
+ this.database.exec("COMMIT");
19254
+ return result;
19255
+ } else if (dead.length > 0) {
19256
+ const replaced = dead[0];
19257
+ if (dead.some((row) => row.home_runtime_id !== registration.homeRuntimeId)) {
19258
+ this.database.exec("ROLLBACK");
19259
+ throw runtimeError(
19260
+ "project_home_conflict",
19261
+ ".kxm/project.yaml",
19262
+ `project ${registration.projectId} home runtime is immutable and cannot be rebound`
19263
+ );
19264
+ }
19265
+ this.database.prepare("DELETE FROM projects WHERE project_id = ?").run(registration.projectId);
19266
+ this.insertHome(registration, projectRoot, projectKey);
19267
+ this.database.exec("COMMIT");
19268
+ registration.logger?.({
19269
+ event: "project_registration_replaced",
19270
+ projectId: registration.projectId,
19271
+ projectRoot,
19272
+ replacedRoot: replaced.project_root,
19273
+ replacedCount: dead.length,
19274
+ replacedHomeRuntimeId: replaced.home_runtime_id,
19275
+ replacedProjectKey: replaced.project_key
19276
+ });
19277
+ return this.insertedRegistration(registration, projectRoot, projectKey);
19278
+ } else {
19279
+ this.insertHome(registration, projectRoot, projectKey);
19280
+ this.database.exec("COMMIT");
19281
+ return this.insertedRegistration(registration, projectRoot, projectKey);
19282
+ }
19283
+ } catch (error) {
19284
+ try {
19285
+ this.database.exec("ROLLBACK");
19286
+ } catch {
19287
+ }
19288
+ throw error;
19289
+ }
19290
+ for (const root of rootsToMeasure ?? []) {
19291
+ commonByRoot.set(root, gitCommonDirectory(root));
19292
+ }
19293
+ }
19294
+ throw runtimeError(
19295
+ "runtime_registry_busy",
19296
+ projectRoot,
19297
+ `project ${registration.projectId} registration could not be rechecked`
19298
+ );
19299
+ }
19300
+ /** Remove one control root. Returns false when that root was not registered. */
19301
+ unregisterProject(projectRoot) {
19053
19302
  this.database.exec("BEGIN IMMEDIATE");
19054
19303
  try {
19055
- const byId = this.database.prepare("SELECT project_id, project_root, project_key, home_runtime_id, config_revision, registered_at FROM projects WHERE project_id = ?").get(registration.projectId);
19056
- const byKey = this.database.prepare("SELECT project_id, project_root, project_key, home_runtime_id, config_revision, registered_at FROM projects WHERE project_key = ?").get(projectKey);
19057
- const existing = byId ?? byKey;
19058
- if (existing) {
19059
- const problems = [];
19060
- if (byId && byId.project_key !== projectKey) problems.push(`project ${registration.projectId} is already bound to a different control root`);
19061
- if (byKey && byKey.project_id !== registration.projectId) problems.push(`control root is already bound to a different project id ${byKey.project_id}`);
19062
- if (existing.home_runtime_id !== registration.homeRuntimeId) problems.push(`project ${registration.projectId} home runtime is immutable and cannot be rebound`);
19063
- if (problems.length > 0) {
19064
- this.database.exec("ROLLBACK");
19065
- throw runtimeError("project_home_conflict", ".kxm/project.yaml", problems.join("; "));
19066
- }
19067
- const result = {
19068
- projectId: existing.project_id,
19069
- projectRoot: existing.project_root,
19070
- projectKey: existing.project_key,
19071
- homeRuntimeId: existing.home_runtime_id,
19072
- ...existing.config_revision !== null ? { configRevision: existing.config_revision } : {},
19073
- registeredAt: existing.registered_at
19074
- };
19075
- this.database.exec("COMMIT");
19076
- return result;
19304
+ const removed = this.deleteRegisteredRoot(projectRoot);
19305
+ this.database.exec("COMMIT");
19306
+ return removed;
19307
+ } catch (error) {
19308
+ try {
19309
+ this.database.exec("ROLLBACK");
19310
+ } catch {
19311
+ }
19312
+ throw error;
19313
+ }
19314
+ }
19315
+ /**
19316
+ * Delete the row only when `supervisorIsLive` is false for the supervisor
19317
+ * row read inside this write transaction. A live supervisor keeps the row;
19318
+ * the caller uses the supervisor route instead of editing the file.
19319
+ */
19320
+ unregisterProjectIfIdle(projectRoot, supervisorIsLive) {
19321
+ this.database.exec("BEGIN IMMEDIATE");
19322
+ try {
19323
+ if (supervisorIsLive(this.readSupervisorRow())) {
19324
+ this.database.exec("ROLLBACK");
19325
+ return "supervisor_live";
19077
19326
  }
19078
- this.database.prepare(`
19079
- INSERT INTO projects (project_id, project_root, project_key, home_runtime_id, config_revision, registered_at)
19080
- VALUES (?, ?, ?, ?, ?, ?)
19081
- `).run(registration.projectId, projectRoot, projectKey, registration.homeRuntimeId, registration.configRevision ?? null, registration.now);
19327
+ const removed = this.deleteRegisteredRoot(projectRoot);
19082
19328
  this.database.exec("COMMIT");
19083
- return {
19084
- projectId: registration.projectId,
19085
- projectRoot,
19086
- projectKey,
19087
- homeRuntimeId: registration.homeRuntimeId,
19088
- ...registration.configRevision !== void 0 ? { configRevision: registration.configRevision } : {},
19089
- registeredAt: registration.now
19090
- };
19329
+ return removed ? "removed" : "absent";
19091
19330
  } catch (error) {
19092
19331
  try {
19093
19332
  this.database.exec("ROLLBACK");
@@ -19096,28 +19335,65 @@ var KxmRuntimeRegistry = class {
19096
19335
  throw error;
19097
19336
  }
19098
19337
  }
19338
+ /** How many other rows share this home root's project. Zero when this root is a lane or missing. */
19339
+ homeLaneCount(projectRoot) {
19340
+ const row = this.projectRow(`SELECT ${PROJECT_COLUMNS} FROM projects WHERE project_key = ?`, projectRuntimeKey(resolve6(projectRoot)));
19341
+ if (!row || row.lane_of !== null) return 0;
19342
+ const count = this.database.prepare(
19343
+ "SELECT COUNT(*) AS total FROM projects WHERE project_id = ? AND project_key != ?"
19344
+ ).get(row.project_id, row.project_key);
19345
+ return Number(count?.total ?? 0);
19346
+ }
19347
+ /** Caller holds the write transaction. Does not commit. */
19348
+ deleteRegisteredRoot(projectRoot) {
19349
+ const projectKey = projectRuntimeKey(resolve6(projectRoot));
19350
+ const row = this.projectRow(`SELECT ${PROJECT_COLUMNS} FROM projects WHERE project_key = ?`, projectKey);
19351
+ if (!row) return false;
19352
+ this.database.prepare("DELETE FROM projects WHERE project_key = ?").run(projectKey);
19353
+ if (row.lane_of === null) {
19354
+ const successor = this.database.prepare(
19355
+ "SELECT project_key FROM projects WHERE project_id = ? ORDER BY registered_at LIMIT 1"
19356
+ ).get(row.project_id);
19357
+ if (successor) {
19358
+ this.database.prepare("UPDATE projects SET lane_of = NULL WHERE project_key = ?").run(successor.project_key);
19359
+ this.database.prepare(
19360
+ "UPDATE projects SET lane_of = ? WHERE project_id = ? AND project_key != ? AND (lane_of IS NULL OR lane_of = ?)"
19361
+ ).run(successor.project_key, row.project_id, successor.project_key, projectKey);
19362
+ }
19363
+ }
19364
+ return true;
19365
+ }
19366
+ /** The home row for a project id, or the earliest row when every row is a lane. */
19099
19367
  project(projectId) {
19100
- const row = this.database.prepare("SELECT project_id, project_root, project_key, home_runtime_id, config_revision, registered_at FROM projects WHERE project_id = ?").get(projectId);
19101
- return row ? {
19102
- projectId: row.project_id,
19103
- projectRoot: row.project_root,
19104
- projectKey: row.project_key,
19105
- homeRuntimeId: row.home_runtime_id,
19106
- ...row.config_revision !== null ? { configRevision: row.config_revision } : {},
19107
- registeredAt: row.registered_at
19108
- } : void 0;
19368
+ const row = this.projectRow(
19369
+ `SELECT ${PROJECT_COLUMNS} FROM projects WHERE project_id = ? ORDER BY CASE WHEN lane_of IS NULL THEN 0 ELSE 1 END, registered_at LIMIT 1`,
19370
+ projectId
19371
+ );
19372
+ return row ? registrationFromRow(row) : void 0;
19109
19373
  }
19110
19374
  projectByRoot(projectRoot) {
19111
- const key = projectRuntimeKey(projectRoot);
19112
- const row = this.database.prepare("SELECT project_id, project_root, project_key, home_runtime_id, config_revision, registered_at FROM projects WHERE project_key = ?").get(key);
19113
- return row ? {
19114
- projectId: row.project_id,
19115
- projectRoot: row.project_root,
19116
- projectKey: row.project_key,
19117
- homeRuntimeId: row.home_runtime_id,
19118
- ...row.config_revision !== null ? { configRevision: row.config_revision } : {},
19119
- registeredAt: row.registered_at
19120
- } : void 0;
19375
+ const row = this.projectRow(`SELECT ${PROJECT_COLUMNS} FROM projects WHERE project_key = ?`, projectRuntimeKey(projectRoot));
19376
+ return row ? registrationFromRow(row) : void 0;
19377
+ }
19378
+ projectRow(sql, parameter) {
19379
+ return this.database.prepare(sql).get(parameter);
19380
+ }
19381
+ insertHome(registration, projectRoot, projectKey) {
19382
+ this.database.prepare(`
19383
+ INSERT INTO projects (project_key, project_id, project_root, home_runtime_id, config_revision, registered_at, lane_of)
19384
+ VALUES (?, ?, ?, ?, ?, ?, NULL)
19385
+ `).run(projectKey, registration.projectId, projectRoot, registration.homeRuntimeId, registration.configRevision ?? null, registration.now);
19386
+ }
19387
+ insertedRegistration(registration, projectRoot, projectKey, laneOf) {
19388
+ return {
19389
+ projectId: registration.projectId,
19390
+ projectRoot,
19391
+ projectKey,
19392
+ homeRuntimeId: registration.homeRuntimeId,
19393
+ ...registration.configRevision !== void 0 ? { configRevision: registration.configRevision } : {},
19394
+ ...laneOf !== void 0 ? { laneOf } : {},
19395
+ registeredAt: registration.now
19396
+ };
19121
19397
  }
19122
19398
  };
19123
19399
  var KXM_RUN_EVENT_SCHEMA = "kxm.run-event.v1";
@@ -23559,7 +23835,8 @@ function openKxmRuntimeContext(projectRoot, options) {
23559
23835
  projectRoot: bundle.projectRoot,
23560
23836
  homeRuntimeId: options.homeRuntimeId,
23561
23837
  configRevision: bundle.configRevision,
23562
- now: options.now ?? (/* @__PURE__ */ new Date()).toISOString()
23838
+ now: options.now ?? (/* @__PURE__ */ new Date()).toISOString(),
23839
+ ...options.logger ? { logger: options.logger } : {}
23563
23840
  });
23564
23841
  const eventStore = new KxmRunEventStore(join7(paths.projectsDir, registration.projectKey, "run-events.db"));
23565
23842
  registerKxmRuntimeHandle(eventStore.path);
@@ -24022,7 +24299,7 @@ async function beginOneShotEvidence(root, intent, sensitive) {
24022
24299
  // plugins/kxm/src/prices.ts
24023
24300
  var import_yaml3 = __toESM(require_dist(), 1);
24024
24301
  import { createHash as createHash10 } from "node:crypto";
24025
- import { existsSync as existsSync8, readFileSync as readFileSync6, statSync, writeFileSync as writeFileSync3 } from "node:fs";
24302
+ import { existsSync as existsSync8, readFileSync as readFileSync6, statSync as statSync2, writeFileSync as writeFileSync3 } from "node:fs";
24026
24303
  import { join as join9 } from "node:path";
24027
24304
 
24028
24305
  // plugins/kxm/src/price-calc.ts
@@ -24175,7 +24452,7 @@ function parsePriceCatalog(text) {
24175
24452
  return catalog;
24176
24453
  }
24177
24454
  function loadPriceCatalog(rootOrPath) {
24178
- const candidatePath = existsSync8(join9(rootOrPath, ".kxm", "prices.yaml")) ? join9(rootOrPath, ".kxm", "prices.yaml") : existsSync8(join9(rootOrPath, "prices.yaml")) ? join9(rootOrPath, "prices.yaml") : existsSync8(rootOrPath) && statSync(rootOrPath).isFile() ? rootOrPath : void 0;
24455
+ const candidatePath = existsSync8(join9(rootOrPath, ".kxm", "prices.yaml")) ? join9(rootOrPath, ".kxm", "prices.yaml") : existsSync8(join9(rootOrPath, "prices.yaml")) ? join9(rootOrPath, "prices.yaml") : existsSync8(rootOrPath) && statSync2(rootOrPath).isFile() ? rootOrPath : void 0;
24179
24456
  if (!candidatePath || !existsSync8(candidatePath)) {
24180
24457
  return void 0;
24181
24458
  }
@@ -24228,9 +24505,9 @@ function isRouteAdmitted(root, modelId) {
24228
24505
  }
24229
24506
 
24230
24507
  // plugins/kxm/src/worktree-witness.ts
24231
- import { spawnSync as spawnSync3 } from "node:child_process";
24508
+ import { spawnSync as spawnSync4 } from "node:child_process";
24232
24509
  function gitText(cwd, args) {
24233
- const result = spawnSync3("git", ["-C", cwd, ...args], {
24510
+ const result = spawnSync4("git", ["-C", cwd, ...args], {
24234
24511
  encoding: "utf8",
24235
24512
  windowsHide: true,
24236
24513
  timeout: 15e3
@@ -24543,7 +24820,7 @@ function pruneContextPacket(packet, tokenBudget) {
24543
24820
  }
24544
24821
 
24545
24822
  // plugins/kxm/src/dispatch-context.ts
24546
- import { spawnSync as spawnSync4 } from "node:child_process";
24823
+ import { spawnSync as spawnSync5 } from "node:child_process";
24547
24824
  import { readdirSync as readdirSync8 } from "node:fs";
24548
24825
  import { extname as extname3, join as join13 } from "node:path";
24549
24826
 
@@ -25303,7 +25580,7 @@ function loadAuthoredMemory(repoRoot) {
25303
25580
  // plugins/kxm/src/skills.ts
25304
25581
  var import_yaml8 = __toESM(require_dist(), 1);
25305
25582
  import { createHash as createHash11 } from "node:crypto";
25306
- import { existsSync as existsSync11, mkdirSync as mkdirSync5, readdirSync as readdirSync7, readFileSync as readFileSync9, renameSync, rmSync, statSync as statSync2, writeFileSync as writeFileSync6 } from "node:fs";
25583
+ import { existsSync as existsSync11, mkdirSync as mkdirSync5, readdirSync as readdirSync7, readFileSync as readFileSync9, renameSync, rmSync, statSync as statSync3, writeFileSync as writeFileSync6 } from "node:fs";
25307
25584
  import { dirname as dirname7, join as join12 } from "node:path";
25308
25585
  var SKILL_CANDIDATE_SCHEMA = "kxm.skill-candidate.v1";
25309
25586
  var SKILL_EVALUATION_SCHEMA = "kxm.skill-evaluation.v1";
@@ -25683,7 +25960,7 @@ function boundedList(value, field) {
25683
25960
  return [...new Set(refs)];
25684
25961
  }
25685
25962
  function readdirSorted(dir) {
25686
- return readdirSync7(dir).filter((entry) => statSync2(join12(dir, entry)).isDirectory()).sort();
25963
+ return readdirSync7(dir).filter((entry) => statSync3(join12(dir, entry)).isDirectory()).sort();
25687
25964
  }
25688
25965
 
25689
25966
  // plugins/kxm/src/dispatch-context.ts
@@ -25744,7 +26021,7 @@ function gitEnvironment2() {
25744
26021
  return Object.fromEntries(Object.entries(process.env).filter(([name]) => !name.toUpperCase().startsWith("GIT_")));
25745
26022
  }
25746
26023
  function committedGateGap(projectRoot) {
25747
- const result = spawnSync4("git", [
26024
+ const result = spawnSync5("git", [
25748
26025
  "--no-optional-locks",
25749
26026
  "-C",
25750
26027
  projectRoot,
@@ -25886,11 +26163,11 @@ function assembleDispatchContext(sources, input) {
25886
26163
  import { join as join14 } from "node:path";
25887
26164
 
25888
26165
  // plugins/kxm/src/artifacts-exist.ts
25889
- import { lstatSync as lstatSync5, realpathSync as realpathSync3, statSync as statSync3 } from "node:fs";
25890
- import { isAbsolute as isAbsolute4, relative as relative4, resolve as resolve8 } from "node:path";
26166
+ import { lstatSync as lstatSync5, realpathSync as realpathSync4, statSync as statSync4 } from "node:fs";
26167
+ import { isAbsolute as isAbsolute5, relative as relative4, resolve as resolve8 } from "node:path";
25891
26168
  function staysUnder(root, candidate) {
25892
26169
  const child = relative4(root, candidate);
25893
- return child === "" || !isAbsolute4(child) && child !== ".." && !child.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`);
26170
+ return child === "" || !isAbsolute5(child) && child !== ".." && !child.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`);
25894
26171
  }
25895
26172
  function verifyArtifactExists(rootInput, pathInput) {
25896
26173
  const lexicalRoot = resolve8(rootInput);
@@ -25901,12 +26178,12 @@ function verifyArtifactExists(rootInput, pathInput) {
25901
26178
  let realRoot;
25902
26179
  let realPath;
25903
26180
  try {
25904
- realRoot = realpathSync3(lexicalRoot);
26181
+ realRoot = realpathSync4(lexicalRoot);
25905
26182
  } catch {
25906
26183
  return { ok: false, error: "artifact_unreadable", path: lexicalPath };
25907
26184
  }
25908
26185
  try {
25909
- realPath = realpathSync3(lexicalPath);
26186
+ realPath = realpathSync4(lexicalPath);
25910
26187
  } catch {
25911
26188
  return { ok: false, error: "artifact_missing", path: lexicalPath };
25912
26189
  }
@@ -25918,7 +26195,7 @@ function verifyArtifactExists(rootInput, pathInput) {
25918
26195
  if (link.isSymbolicLink()) {
25919
26196
  return { ok: false, error: "artifact_outside_workspace_assets", path: lexicalPath };
25920
26197
  }
25921
- const artifact = statSync3(realPath);
26198
+ const artifact = statSync4(realPath);
25922
26199
  if (!artifact.isFile()) return { ok: false, error: "artifact_not_file", path: lexicalPath };
25923
26200
  if (artifact.size < 1) return { ok: false, error: "artifact_empty", path: lexicalPath };
25924
26201
  return { ok: true, path: realPath, bytes: artifact.size };
@@ -30041,7 +30318,7 @@ var RuntimeHubClient = class {
30041
30318
  // plugins/kxm/src/hub-binding.ts
30042
30319
  import { existsSync as existsSync13, mkdirSync as mkdirSync6, readFileSync as readFileSync11, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync7 } from "node:fs";
30043
30320
  import { homedir as homedir2 } from "node:os";
30044
- import { dirname as dirname8, isAbsolute as isAbsolute5, join as join16, resolve as resolve9 } from "node:path";
30321
+ import { dirname as dirname8, isAbsolute as isAbsolute6, join as join16, resolve as resolve9 } from "node:path";
30045
30322
  var HUB_BINDING_SCHEMA = "kxm.hub-binding.v1";
30046
30323
  var HubBindingError = class extends Error {
30047
30324
  constructor(message) {
@@ -30052,17 +30329,17 @@ var HubBindingError = class extends Error {
30052
30329
  function resolveUserStateRoot(env) {
30053
30330
  const explicit = env.KXM_STATE_HOME?.trim();
30054
30331
  if (explicit) {
30055
- if (!isAbsolute5(explicit)) throw new HubBindingError("local_state_root_not_absolute");
30332
+ if (!isAbsolute6(explicit)) throw new HubBindingError("local_state_root_not_absolute");
30056
30333
  return resolve9(explicit);
30057
30334
  }
30058
30335
  if (process.platform === "win32") {
30059
30336
  const localAppData = env.LOCALAPPDATA?.trim();
30060
- const base2 = localAppData && isAbsolute5(localAppData) ? localAppData : join16(homedir2(), "AppData", "Local");
30337
+ const base2 = localAppData && isAbsolute6(localAppData) ? localAppData : join16(homedir2(), "AppData", "Local");
30061
30338
  return resolve9(base2, "KXM");
30062
30339
  }
30063
30340
  if (process.platform === "darwin") return resolve9(homedir2(), "Library", "Application Support", "KXM");
30064
30341
  const xdgState = env.XDG_STATE_HOME?.trim();
30065
- const base = xdgState && isAbsolute5(xdgState) ? xdgState : join16(homedir2(), ".local", "state");
30342
+ const base = xdgState && isAbsolute6(xdgState) ? xdgState : join16(homedir2(), ".local", "state");
30066
30343
  return resolve9(base, "kxm");
30067
30344
  }
30068
30345
  function hubBindingFile(env = process.env) {
@@ -30113,7 +30390,7 @@ function readHubBinding(env = process.env) {
30113
30390
  // plugins/kxm/src/hub-env.ts
30114
30391
  import { existsSync as existsSync14, mkdirSync as mkdirSync7, readFileSync as readFileSync12, renameSync as renameSync3, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "node:fs";
30115
30392
  import { homedir as homedir3 } from "node:os";
30116
- import { dirname as dirname9, isAbsolute as isAbsolute6, join as join17, resolve as resolve10 } from "node:path";
30393
+ import { dirname as dirname9, isAbsolute as isAbsolute7, join as join17, resolve as resolve10 } from "node:path";
30117
30394
  var HUB_ENV_SCHEMA = "kxm.hub-env.v1";
30118
30395
  var HubEnvError = class extends Error {
30119
30396
  constructor(message) {
@@ -30124,17 +30401,17 @@ var HubEnvError = class extends Error {
30124
30401
  function resolveUserStateRoot2(env) {
30125
30402
  const explicit = env.KXM_STATE_HOME?.trim();
30126
30403
  if (explicit) {
30127
- if (!isAbsolute6(explicit)) throw new HubEnvError("local_state_root_not_absolute");
30404
+ if (!isAbsolute7(explicit)) throw new HubEnvError("local_state_root_not_absolute");
30128
30405
  return resolve10(explicit);
30129
30406
  }
30130
30407
  if (process.platform === "win32") {
30131
30408
  const localAppData = env.LOCALAPPDATA?.trim();
30132
- const base2 = localAppData && isAbsolute6(localAppData) ? localAppData : join17(homedir3(), "AppData", "Local");
30409
+ const base2 = localAppData && isAbsolute7(localAppData) ? localAppData : join17(homedir3(), "AppData", "Local");
30133
30410
  return resolve10(base2, "KXM");
30134
30411
  }
30135
30412
  if (process.platform === "darwin") return resolve10(homedir3(), "Library", "Application Support", "KXM");
30136
30413
  const xdgState = env.XDG_STATE_HOME?.trim();
30137
- const base = xdgState && isAbsolute6(xdgState) ? xdgState : join17(homedir3(), ".local", "state");
30414
+ const base = xdgState && isAbsolute7(xdgState) ? xdgState : join17(homedir3(), ".local", "state");
30138
30415
  return resolve10(base, "kxm");
30139
30416
  }
30140
30417
  function hubEnvFile(env = process.env) {
@@ -30185,7 +30462,7 @@ function resolveClientHubAuthToken(env, project) {
30185
30462
  }
30186
30463
 
30187
30464
  // plugins/kxm/src/logger.ts
30188
- import { appendFileSync, existsSync as existsSync15, mkdirSync as mkdirSync8, renameSync as renameSync4, statSync as statSync4, unlinkSync as unlinkSync2 } from "node:fs";
30465
+ import { appendFileSync, existsSync as existsSync15, mkdirSync as mkdirSync8, renameSync as renameSync4, statSync as statSync5, unlinkSync as unlinkSync2 } from "node:fs";
30189
30466
  import { dirname as dirname10 } from "node:path";
30190
30467
  var LOG_LEVEL_PRIORITY = {
30191
30468
  debug: 10,
@@ -30256,7 +30533,7 @@ function createLogger(options) {
30256
30533
  let currentSize = 0;
30257
30534
  if (filePath && existsSync15(filePath)) {
30258
30535
  try {
30259
- currentSize = statSync4(filePath).size;
30536
+ currentSize = statSync5(filePath).size;
30260
30537
  } catch {
30261
30538
  currentSize = 0;
30262
30539
  }
@@ -30403,6 +30680,24 @@ function readRecentSupervisorError(paths) {
30403
30680
  return void 0;
30404
30681
  }
30405
30682
  }
30683
+ var SETTLED_RUN_STATUS = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
30684
+ function kxmSupervisorRecordIsLive(record2) {
30685
+ return supervisorStatusOf(record2).running;
30686
+ }
30687
+ function unsettledRunIds(context) {
30688
+ const ids = [];
30689
+ for (const stored of context.eventStore.runsForProject(context.projectId, 1e4)) {
30690
+ let status = stored.status;
30691
+ try {
30692
+ status = projectKxmRunReadOnly(context, stored.runId).status;
30693
+ } catch {
30694
+ ids.push(stored.runId);
30695
+ continue;
30696
+ }
30697
+ if (!SETTLED_RUN_STATUS.has(status)) ids.push(stored.runId);
30698
+ }
30699
+ return ids;
30700
+ }
30406
30701
  function supervisorStatusOf(record2) {
30407
30702
  if (!record2) return { running: false };
30408
30703
  const heartbeatAgeMs = Date.now() - Date.parse(record2.heartbeatAt);
@@ -30751,7 +31046,7 @@ async function startKxmRuntimeSupervisorInner(paths, requestedPortOption, now, e
30751
31046
  }
30752
31047
  };
30753
31048
  const contextFor = (projectRoot) => {
30754
- if (!isAbsolute7(projectRoot)) {
31049
+ if (!isAbsolute8(projectRoot)) {
30755
31050
  throw runtimeError("runtime_request_invalid", "projectRoot", "projectRoot must be an absolute path");
30756
31051
  }
30757
31052
  const key = projectRuntimeKey(projectRoot);
@@ -30817,6 +31112,62 @@ async function startKxmRuntimeSupervisorInner(paths, requestedPortOption, now, e
30817
31112
  sendJson(response, 200, { ok: true, projectId: context.projectId, retried, outbox: context.eventStore.outboxStatus() });
30818
31113
  return;
30819
31114
  }
31115
+ if (request.method === "POST" && url.pathname === "/v1/projects/unregister") {
31116
+ const body = await readJsonBody(request);
31117
+ const projectRoot = typeof body.projectRoot === "string" ? body.projectRoot : "";
31118
+ const force = body.force === true;
31119
+ if (!projectRoot || !isAbsolute8(projectRoot)) {
31120
+ sendJson(response, 400, { ok: false, error: "runtime_request_invalid", message: "projectRoot must be an absolute path" });
31121
+ return;
31122
+ }
31123
+ if (!force) {
31124
+ const key2 = projectRuntimeKey(projectRoot);
31125
+ const existing2 = contexts.get(key2);
31126
+ let openRunIds = [];
31127
+ if (existing2) {
31128
+ openRunIds = unsettledRunIds(existing2);
31129
+ } else {
31130
+ const registration = registry.projectByRoot(projectRoot);
31131
+ const eventsPath = join18(paths.projectsDir, registration?.projectKey ?? projectRuntimeKey(projectRoot), "run-events.db");
31132
+ if (registration && existsSync16(eventsPath)) {
31133
+ const store = new KxmRunEventStore(eventsPath);
31134
+ try {
31135
+ openRunIds = store.runsForProject(registration.projectId, 1e4).filter((run) => !SETTLED_RUN_STATUS.has(run.status)).map((run) => run.runId);
31136
+ } finally {
31137
+ store.close();
31138
+ }
31139
+ }
31140
+ }
31141
+ if (openRunIds.length > 0) {
31142
+ sendJson(response, 409, {
31143
+ ok: false,
31144
+ error: "runtime_project_busy",
31145
+ message: `unsettled run ${openRunIds.join(", ")}`,
31146
+ runIds: openRunIds
31147
+ });
31148
+ return;
31149
+ }
31150
+ const laneCount = registry.homeLaneCount(projectRoot);
31151
+ if (laneCount > 0) {
31152
+ sendJson(response, 409, {
31153
+ ok: false,
31154
+ error: "runtime_project_has_lanes",
31155
+ message: `home root still has ${laneCount} lane(s)`
31156
+ });
31157
+ return;
31158
+ }
31159
+ }
31160
+ const key = projectRuntimeKey(projectRoot);
31161
+ const existing = contexts.get(key);
31162
+ const unregistered = registry.unregisterProject(projectRoot);
31163
+ if (existing) {
31164
+ contexts.delete(key);
31165
+ syncStatuses.delete(key);
31166
+ closeKxmRuntimeContext(existing);
31167
+ }
31168
+ sendJson(response, 200, { ok: true, unregistered });
31169
+ return;
31170
+ }
30820
31171
  if (request.method === "POST" && url.pathname === "/v1/runs") {
30821
31172
  const body = await readJsonBody(request);
30822
31173
  const projectRoot = typeof body.projectRoot === "string" ? body.projectRoot : "";
@@ -31040,7 +31391,10 @@ async function startKxmRuntimeSupervisorInner(paths, requestedPortOption, now, e
31040
31391
  sendJson(response, 400, { ok: false, error: "runtime_request_invalid", message: `project ${requestedProjectId} is not the bound project ${context.projectId}` });
31041
31392
  return;
31042
31393
  }
31043
- const runs = context.eventStore.runsForProject(requestedProjectId, 50).map((stored) => {
31394
+ const limitText = url.searchParams.get("limit");
31395
+ const parsedLimit = limitText === null ? 50 : Number(limitText);
31396
+ const limit = Number.isInteger(parsedLimit) && parsedLimit > 0 ? Math.min(parsedLimit, 1e4) : 50;
31397
+ const runs = context.eventStore.runsForProject(requestedProjectId, limit).map((stored) => {
31044
31398
  try {
31045
31399
  return projectKxmRunReadOnly(context, stored.runId);
31046
31400
  } catch (error) {
@@ -31314,6 +31668,7 @@ export {
31314
31668
  hashKxmSupervisorToken,
31315
31669
  hashKxmTokenProof,
31316
31670
  kxmRuntimeRequest,
31671
+ kxmSupervisorRecordIsLive,
31317
31672
  kxmSupervisorStatus,
31318
31673
  kxmSupervisorTokenFile,
31319
31674
  readKxmSupervisorToken,