@kontextmind/kxm 0.7.100 → 0.7.101

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.
@@ -11,7 +11,7 @@
11
11
  "name": "kxm",
12
12
  "source": "./plugins/kxm",
13
13
  "description": "Durable workflows, peer agents, and kxm tui",
14
- "version": "0.7.100",
14
+ "version": "0.7.101",
15
15
  "category": "development",
16
16
  "tags": ["kxm", "multi-agent", "workflows", "mcp"]
17
17
  }
package/CHANGELOG.md CHANGED
@@ -375,8 +375,9 @@ All notable user-facing changes are documented here. The project follows [Semant
375
375
  or the Claude MCP server send one hop past the inbound request being handled, so a
376
376
  chain of agents forwarding to each other stops at `hop_limit_reached`.
377
377
  - **Workflow prompts no longer point agents at `.kxm/config`**, a path KXM refuses.
378
- - **`kxm gate signal` and `kxm workflow wait` inside a KXM project reach the hub for hub
379
- runs.** They go to the local Runtime only for a run its store holds.
378
+ - **`kxm gate signal`, `kxm workflow wait` and `kxm role resume` inside a KXM project reach
379
+ the hub for hub runs.** They go to the local Runtime only for a run its store holds, and
380
+ the lookup leaves no files behind, so `--dry-run` changes nothing.
380
381
  - **`kxm peer inbox` lists the requests waiting for a named CLI agent.** It returned
381
382
  `{"messages":[]}` every time. The hub now serves `GET /v1/agents/:id/inbox`
382
383
  (agent-authenticated, project-scoped): the caller's queued and delivered requests,
@@ -1351,11 +1351,13 @@ kxm role resume <runId> [ruling]
1351
1351
  Resumes an audit-escalated run with an operator directive. The default ruling is `operator_ruling: waived and resumed`.
1352
1352
 
1353
1353
  - Arguments: `<runId>`; `[ruling]`, free text recorded with the decision.
1354
- - For a KXM run ID (`run_` followed by 32 hex digits) inside a project, posts an `audit_escalation` signal with action `unblock` to the Runtime, starting the supervisor if needed. `--dry-run` plans the request without starting the supervisor. JSON keys: `runId`, `ruling`, `unblocked`.
1355
- - For any other ID, updates the hub store at `.kxm/state/kxm.db` in the current directory directly (ignoring `--workspace` and `KXM_DATA_PATH`) and adds a `decision` journal entry. `--dry-run` reads the store read-only, reports the stage it would resume and the resulting `status`, and plans the write. JSON keys: `runId`, `stageId`, `ruling`, `status`.
1354
+ - Inside a project, a run that this project's Runtime store holds gets an `audit_escalation` signal with action `unblock` in the Runtime, starting the supervisor if needed. Hub workflow runs share the `run_` + 32-hex shape, so the store, not the ID, decides. `--dry-run` plans the request without starting the supervisor. JSON keys: `runId`, `ruling`, `unblocked`.
1355
+ - Any other run ID, including a hub workflow run of the same shape, updates the hub store at `.kxm/state/kxm.db` in the current directory directly (ignoring `--workspace` and `KXM_DATA_PATH`) and adds a `decision` journal entry. `--dry-run` reads the store read-only, reports the stage it would resume and the resulting `status`, and plans the write. JSON keys: `runId`, `stageId`, `ruling`, `status`.
1356
1356
  - The hub-run path bypasses the hub even while one is running: it writes SQLite directly, without authentication, in two statements outside one transaction. A running hub keeps runs in memory, so it does not see the change until it restarts, and its next write to that run overwrites it; it also pushes no event and sends the coordinator no resume message. Stop the hub first, or resume a live hub's run with a signed `audit_escalation` signal (see [Waits, signals and escalation](workflow-definitions.md#waits-signals-and-escalation)).
1357
1357
  - Errors: `resume_failed` (exit 1), or a plain `not found` line (exit 1).
1358
1358
 
1359
+ A run the project's Runtime holds:
1360
+
1359
1361
  ```bash
1360
1362
  kxm role resume run_0123456789abcdef0123456789abcdef "waive the audit" --dry-run --json
1361
1363
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kontextmind/kxm",
3
- "version": "0.7.100",
3
+ "version": "0.7.101",
4
4
  "description": "KXM local-first multi-agent orchestration and operator dashboard",
5
5
  "type": "module",
6
6
  "author": "KontextMind",
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
3
3
  "name": "kxm",
4
4
  "displayName": "KXM",
5
- "version": "0.7.100",
5
+ "version": "0.7.101",
6
6
  "description": "Headless multi-agent orchestration, durable workflows, and a live operator dashboard for Pi and Claude Code",
7
7
  "author": {
8
8
  "name": "KontextMind",
@@ -24677,7 +24677,7 @@ function kxmProjectRunEventsPath(projectRoot, env) {
24677
24677
  function projectRuntimeOwnsRun(projectRoot, runId, env) {
24678
24678
  const path4 = kxmProjectRunEventsPath(projectRoot, env);
24679
24679
  if (!existsSync10(path4)) return false;
24680
- const database = new DatabaseSync(path4, { readOnly: true });
24680
+ const database = openReadOnlyDatabase(path4);
24681
24681
  try {
24682
24682
  database.exec("PRAGMA busy_timeout = 5000");
24683
24683
  return database.prepare("SELECT 1 FROM runs WHERE run_id = ?").get(runId) !== void 0;
@@ -28834,7 +28834,7 @@ async function cmdRoleResume(runtime, runId, ruling) {
28834
28834
  }
28835
28835
  const effectiveRuling = ruling?.trim() || "operator_ruling: waived and resumed";
28836
28836
  const projectRoot = discoverKxmProjectRoot(runtime.cwd);
28837
- if (projectRoot && /^run_[a-f0-9]{32}$/i.test(runId)) {
28837
+ if (projectRoot && projectRuntimeOwnsRun(projectRoot, runId, runtime.env)) {
28838
28838
  if (runtime.dryRun) {
28839
28839
  printPlan(
28840
28840
  runtime,
@@ -17308,7 +17308,7 @@ function sessionTokenFixHint(policy) {
17308
17308
  }
17309
17309
 
17310
17310
  // plugins/kxm/src/mcp-server.ts
17311
- var VERSION = "0.7.100";
17311
+ var VERSION = "0.7.101";
17312
17312
  var CONFIGURE_PLUGIN = "/plugin configure kxm@kxm";
17313
17313
  var inbox = /* @__PURE__ */ new Map();
17314
17314
  var notifiedInbox = /* @__PURE__ */ new Set();
@@ -2986,7 +2986,7 @@ var require_compile = __commonJS({
2986
2986
  const schOrFunc = root.refs[ref];
2987
2987
  if (schOrFunc)
2988
2988
  return schOrFunc;
2989
- let _sch = resolve11.call(this, root, ref);
2989
+ let _sch = resolve12.call(this, root, ref);
2990
2990
  if (_sch === void 0) {
2991
2991
  const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
2992
2992
  const { schemaId } = this.opts;
@@ -3013,7 +3013,7 @@ var require_compile = __commonJS({
3013
3013
  function sameSchemaEnv(s1, s2) {
3014
3014
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
3015
3015
  }
3016
- function resolve11(root, ref) {
3016
+ function resolve12(root, ref) {
3017
3017
  let sch;
3018
3018
  while (typeof (sch = this.refs[ref]) == "string")
3019
3019
  ref = sch;
@@ -3838,7 +3838,7 @@ var require_fast_uri = __commonJS({
3838
3838
  }
3839
3839
  return uri;
3840
3840
  }
3841
- function resolve11(baseURI, relativeURI, options) {
3841
+ function resolve12(baseURI, relativeURI, options) {
3842
3842
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
3843
3843
  const {
3844
3844
  parsed: baseParsed,
@@ -4200,7 +4200,7 @@ var require_fast_uri = __commonJS({
4200
4200
  var fastUri = {
4201
4201
  SCHEMES,
4202
4202
  normalize,
4203
- resolve: resolve11,
4203
+ resolve: resolve12,
4204
4204
  resolveComponent,
4205
4205
  equal,
4206
4206
  serialize,
@@ -14779,7 +14779,7 @@ var require_dist = __commonJS({
14779
14779
 
14780
14780
  // plugins/kxm/src/runtime-service.ts
14781
14781
  import { createHash as createHash7 } from "node:crypto";
14782
- import { existsSync as existsSync6, readdirSync as readdirSync3, readFileSync as readFileSync4 } from "node:fs";
14782
+ import { existsSync as existsSync7, readdirSync as readdirSync3, readFileSync as readFileSync4 } from "node:fs";
14783
14783
  import { join as join6 } from "node:path";
14784
14784
 
14785
14785
  // plugins/kxm/src/project-config.ts
@@ -15194,7 +15194,7 @@ function defaultSpawn(command, args, options) {
15194
15194
  if (options.signal?.aborted) {
15195
15195
  return Promise.resolve({ stdout: "", stderr: "", code: null, started: false, observedChildExit: false, error: new Error("process_aborted") });
15196
15196
  }
15197
- return new Promise((resolve11) => {
15197
+ return new Promise((resolve12) => {
15198
15198
  const stdout = [];
15199
15199
  const stderr = [];
15200
15200
  let outputBytes = 0;
@@ -15225,7 +15225,7 @@ function defaultSpawn(command, args, options) {
15225
15225
  const started = Boolean(child?.pid);
15226
15226
  if (started && !observedChildExit) error ??= new Error("process_exit_unobserved");
15227
15227
  const unverifiedDescendants = stopping || started && !observedChildExit;
15228
- resolve11({
15228
+ resolve12({
15229
15229
  stdout: Buffer.concat(stdout).toString("utf8"),
15230
15230
  stderr: Buffer.concat(stderr).toString("utf8"),
15231
15231
  code,
@@ -17661,11 +17661,14 @@ function legacyConfigFilesAt(root) {
17661
17661
 
17662
17662
  // plugins/kxm/src/runtime-store.ts
17663
17663
  import { createHash as createHash5, randomUUID } from "node:crypto";
17664
- import { existsSync as existsSync5, lstatSync as lstatSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, realpathSync as realpathSync2, writeFileSync as writeFileSync2 } from "node:fs";
17665
- import { dirname as dirname5, join as join5, resolve as resolve4 } from "node:path";
17664
+ import { existsSync as existsSync6, lstatSync as lstatSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, realpathSync as realpathSync2, writeFileSync as writeFileSync2 } from "node:fs";
17665
+ import { dirname as dirname5, join as join5, resolve as resolve5 } from "node:path";
17666
17666
 
17667
17667
  // plugins/kxm/src/sqlite.ts
17668
+ import { existsSync as existsSync4 } from "node:fs";
17668
17669
  import { createRequire } from "node:module";
17670
+ import { resolve as resolve2 } from "node:path";
17671
+ import { pathToFileURL } from "node:url";
17669
17672
  var requireFromHere = createRequire(import.meta.url);
17670
17673
  function loadNative() {
17671
17674
  try {
@@ -17702,10 +17705,16 @@ var DatabaseSync = class {
17702
17705
  this.inner.close();
17703
17706
  }
17704
17707
  };
17708
+ function openReadOnlyDatabase(path) {
17709
+ if (existsSync4(`${path}-wal`)) return new DatabaseSync(path, { readOnly: true });
17710
+ const uri = pathToFileURL(resolve2(path));
17711
+ uri.searchParams.set("immutable", "1");
17712
+ return new DatabaseSync(uri.href, { readOnly: true });
17713
+ }
17705
17714
 
17706
17715
  // plugins/kxm/src/bindings.ts
17707
17716
  import { homedir } from "node:os";
17708
- import { dirname as dirname3, isAbsolute as isAbsolute2, join as join3, parse, relative as relative2, resolve as resolve2, sep as sep2 } from "node:path";
17717
+ import { dirname as dirname3, isAbsolute as isAbsolute2, join as join3, parse, relative as relative2, resolve as resolve3, sep as sep2 } from "node:path";
17709
17718
  var MAX_BINDING_RECORD_BYTES = 256 * 1024;
17710
17719
  var BINDING_LABEL = "Runtime-local repository bindings";
17711
17720
  function bindingIssue(phase, code, message) {
@@ -17721,17 +17730,17 @@ function kxmUserStateRoot(options = {}) {
17721
17730
  const explicit = options.stateRoot ?? env.KXM_STATE_HOME?.trim();
17722
17731
  if (explicit) {
17723
17732
  if (!isAbsolute2(explicit)) bindingError("path", "local_state_root_not_absolute", "KXM_STATE_HOME must be an absolute host-local path");
17724
- return resolve2(explicit);
17733
+ return resolve3(explicit);
17725
17734
  }
17726
17735
  if (platform === "win32") {
17727
17736
  const localAppData = env.LOCALAPPDATA?.trim();
17728
17737
  const base2 = localAppData && isAbsolute2(localAppData) ? localAppData : join3(home, "AppData", "Local");
17729
- return resolve2(base2, "KXM");
17738
+ return resolve3(base2, "KXM");
17730
17739
  }
17731
- if (platform === "darwin") return resolve2(home, "Library", "Application Support", "KXM");
17740
+ if (platform === "darwin") return resolve3(home, "Library", "Application Support", "KXM");
17732
17741
  const xdgState = env.XDG_STATE_HOME?.trim();
17733
17742
  const base = xdgState && isAbsolute2(xdgState) ? xdgState : join3(home, ".local", "state");
17734
- return resolve2(base, "kxm");
17743
+ return resolve3(base, "kxm");
17735
17744
  }
17736
17745
 
17737
17746
  // plugins/kxm/src/sync-transform.ts
@@ -18070,7 +18079,7 @@ import { createHash as createHash4, randomBytes } from "node:crypto";
18070
18079
  import {
18071
18080
  chmodSync,
18072
18081
  copyFileSync,
18073
- existsSync as existsSync4,
18082
+ existsSync as existsSync5,
18074
18083
  lstatSync as lstatSync2,
18075
18084
  mkdirSync,
18076
18085
  readdirSync as readdirSync2,
@@ -18078,14 +18087,14 @@ import {
18078
18087
  unlinkSync,
18079
18088
  writeFileSync
18080
18089
  } from "node:fs";
18081
- import { basename as basename2, dirname as dirname4, join as join4, resolve as resolve3 } from "node:path";
18090
+ import { basename as basename2, dirname as dirname4, join as join4, resolve as resolve4 } from "node:path";
18082
18091
  function databaseError(code, file, message) {
18083
18092
  const issue3 = { phase: "semantic", code, file, message };
18084
18093
  return new KxmConfigError([issue3]);
18085
18094
  }
18086
18095
  function checkedParent(path, description) {
18087
18096
  const parent = dirname4(path);
18088
- if (!existsSync4(parent)) mkdirSync(parent, { recursive: true, mode: 448 });
18097
+ if (!existsSync5(parent)) mkdirSync(parent, { recursive: true, mode: 448 });
18089
18098
  const stat = lstatSync2(parent, { throwIfNoEntry: false });
18090
18099
  if (!stat || stat.isSymbolicLink() || !stat.isDirectory()) {
18091
18100
  throw databaseError("runtime_path_invalid", description, `${description} parent must be a regular directory, not a link`);
@@ -18307,14 +18316,14 @@ function fileSha256(filePath) {
18307
18316
  return `sha256:${createHash4("sha256").update(bytes).digest("hex")}`;
18308
18317
  }
18309
18318
  function backupDatabaseFile(sourcePath, targetPath, storeId) {
18310
- const resolvedSource = resolve3(sourcePath);
18311
- const resolvedTarget = resolve3(targetPath);
18319
+ const resolvedSource = resolve4(sourcePath);
18320
+ const resolvedTarget = resolve4(targetPath);
18312
18321
  const sourceStat = lstatSync2(resolvedSource, { throwIfNoEntry: false });
18313
18322
  if (!sourceStat || !sourceStat.isFile() || sourceStat.isSymbolicLink()) {
18314
18323
  throw databaseError("runtime_path_invalid", resolvedSource, `source database ${resolvedSource} must be a regular file, not a link or directory`);
18315
18324
  }
18316
18325
  checkedParent(resolvedTarget, "backup target");
18317
- if (existsSync4(resolvedTarget)) {
18326
+ if (existsSync5(resolvedTarget)) {
18318
18327
  unlinkSync(resolvedTarget);
18319
18328
  }
18320
18329
  const sourceDb = new DatabaseSync(resolvedSource);
@@ -18358,8 +18367,8 @@ function backupDatabaseFile(sourcePath, targetPath, storeId) {
18358
18367
  };
18359
18368
  }
18360
18369
  function restoreDatabaseFile(backupPath, targetPath, storeId, expectedSchemaVersion, maxSupportedVersion) {
18361
- const resolvedBackup = resolve3(backupPath);
18362
- const resolvedTarget = resolve3(targetPath);
18370
+ const resolvedBackup = resolve4(backupPath);
18371
+ const resolvedTarget = resolve4(targetPath);
18363
18372
  const backupStat = lstatSync2(resolvedBackup, { throwIfNoEntry: false });
18364
18373
  if (!backupStat || !backupStat.isFile() || backupStat.isSymbolicLink()) {
18365
18374
  throw databaseError("runtime_path_invalid", resolvedBackup, `backup database ${resolvedBackup} must be a regular file, not a link or directory`);
@@ -18392,7 +18401,7 @@ function restoreDatabaseFile(backupPath, targetPath, storeId, expectedSchemaVers
18392
18401
  }
18393
18402
  checkedParent(resolvedTarget, "restore target");
18394
18403
  for (const file of [resolvedTarget, `${resolvedTarget}-wal`, `${resolvedTarget}-shm`]) {
18395
- if (existsSync4(file)) {
18404
+ if (existsSync5(file)) {
18396
18405
  try {
18397
18406
  unlinkSync(file);
18398
18407
  } catch {
@@ -18445,12 +18454,12 @@ function discoverUserRuntime(env, stores, files) {
18445
18454
  }
18446
18455
  const runtimeDir = join4(stateRoot, "runtime");
18447
18456
  const registryPath = join4(runtimeDir, "registry.db");
18448
- if (existsSync4(registryPath)) {
18457
+ if (existsSync5(registryPath)) {
18449
18458
  const storeId = stores.some((store) => store.storeId === "registry") ? "runtime-registry" : "registry";
18450
18459
  pushStore(stores, storeId, registryPath);
18451
18460
  }
18452
18461
  const projectsDir = join4(runtimeDir, "projects");
18453
- if (!existsSync4(projectsDir)) return;
18462
+ if (!existsSync5(projectsDir)) return;
18454
18463
  let entries;
18455
18464
  try {
18456
18465
  entries = readdirSync2(projectsDir, { withFileTypes: true });
@@ -18460,13 +18469,13 @@ function discoverUserRuntime(env, stores, files) {
18460
18469
  for (const entry of entries) {
18461
18470
  if (!entry.isDirectory()) continue;
18462
18471
  const dbPath = join4(projectsDir, entry.name, "run-events.db");
18463
- if (!existsSync4(dbPath)) continue;
18472
+ if (!existsSync5(dbPath)) continue;
18464
18473
  const safe = entry.name.replace(/[^a-zA-Z0-9_.-]/g, "_");
18465
18474
  let storeId = `events:${safe}`;
18466
18475
  if (stores.some((store) => store.storeId === storeId)) storeId = `events:runtime:${safe}`;
18467
18476
  pushStore(stores, storeId, dbPath);
18468
18477
  const sidecar = `${dbPath}.run-prompts.json`;
18469
- if (existsSync4(sidecar)) {
18478
+ if (existsSync5(sidecar)) {
18470
18479
  const id = `${storeId}:run-prompts`;
18471
18480
  if (!files.some((file) => file.id === id || file.sourcePath === sidecar)) {
18472
18481
  files.push({ id, sourcePath: sidecar });
@@ -18475,17 +18484,17 @@ function discoverUserRuntime(env, stores, files) {
18475
18484
  }
18476
18485
  }
18477
18486
  function discoverBackupSources(projectRoot, options = {}) {
18478
- const root = resolve3(projectRoot);
18487
+ const root = resolve4(projectRoot);
18479
18488
  const stores = [];
18480
18489
  const files = [];
18481
- const hubPath = options.hubDataPath ? resolve3(options.hubDataPath) : join4(root, ".kxm", "state", "kxm.db");
18482
- if (existsSync4(hubPath)) pushStore(stores, "hub-store", hubPath);
18490
+ const hubPath = options.hubDataPath ? resolve4(options.hubDataPath) : join4(root, ".kxm", "state", "kxm.db");
18491
+ if (existsSync5(hubPath)) pushStore(stores, "hub-store", hubPath);
18483
18492
  const registryPath = join4(root, ".kxm", "runtime", "registry.db");
18484
- if (existsSync4(registryPath)) pushStore(stores, "registry", registryPath);
18493
+ if (existsSync5(registryPath)) pushStore(stores, "registry", registryPath);
18485
18494
  const bindingsPath = join4(root, ".kxm", "runtime", "bindings.db");
18486
- if (existsSync4(bindingsPath)) pushStore(stores, "binding-store", bindingsPath);
18495
+ if (existsSync5(bindingsPath)) pushStore(stores, "binding-store", bindingsPath);
18487
18496
  const eventsDir = join4(root, ".kxm", "runtime", "events");
18488
- if (existsSync4(eventsDir)) {
18497
+ if (existsSync5(eventsDir)) {
18489
18498
  const entries = readdirSync2(eventsDir, { withFileTypes: true });
18490
18499
  for (const entry of entries) {
18491
18500
  if (entry.isFile() && entry.name.endsWith(".db")) {
@@ -18498,13 +18507,13 @@ function discoverBackupSources(projectRoot, options = {}) {
18498
18507
  return { stores, files };
18499
18508
  }
18500
18509
  function backupPlainFile(sourcePath, targetPath, id) {
18501
- const resolvedSource = resolve3(sourcePath);
18510
+ const resolvedSource = resolve4(sourcePath);
18502
18511
  const sourceStat = lstatSync2(resolvedSource, { throwIfNoEntry: false });
18503
18512
  if (!sourceStat || !sourceStat.isFile() || sourceStat.isSymbolicLink()) {
18504
18513
  throw databaseError("runtime_path_invalid", resolvedSource, `backup file ${resolvedSource} must be a regular file, not a link or directory`);
18505
18514
  }
18506
18515
  checkedParent(targetPath, "backup target");
18507
- if (existsSync4(targetPath)) unlinkSync(targetPath);
18516
+ if (existsSync5(targetPath)) unlinkSync(targetPath);
18508
18517
  copyFileSync(resolvedSource, targetPath);
18509
18518
  try {
18510
18519
  chmodSync(targetPath, 384);
@@ -18528,7 +18537,7 @@ function backupFilename(sourcePath, id, used) {
18528
18537
  }
18529
18538
  function restorePlainFile(backupFilePath, targetPath) {
18530
18539
  checkedParent(targetPath, "restore target");
18531
- if (existsSync4(targetPath)) unlinkSync(targetPath);
18540
+ if (existsSync5(targetPath)) unlinkSync(targetPath);
18532
18541
  copyFileSync(backupFilePath, targetPath);
18533
18542
  try {
18534
18543
  chmodSync(targetPath, 384);
@@ -18545,14 +18554,14 @@ function backupDiscoverOptions(options) {
18545
18554
  };
18546
18555
  }
18547
18556
  function planBackup(options = {}) {
18548
- const projectRoot = options.projectRoot ? resolve3(options.projectRoot) : process.cwd();
18557
+ const projectRoot = options.projectRoot ? resolve4(options.projectRoot) : process.cwd();
18549
18558
  const discovered = discoverBackupSources(projectRoot, backupDiscoverOptions(options));
18550
18559
  if (discovered.stores.length === 0) {
18551
18560
  throw databaseError("backup_no_stores", projectRoot, "no existing SQLite stores found to backup");
18552
18561
  }
18553
18562
  const createdAt = (/* @__PURE__ */ new Date()).toISOString();
18554
18563
  const timestamp = createdAt.replace(/[:.]/g, "-");
18555
- const outDir = options.outDir ? resolve3(options.outDir) : join4(projectRoot, ".kxm", "backups", `backup-${timestamp}`);
18564
+ const outDir = options.outDir ? resolve4(options.outDir) : join4(projectRoot, ".kxm", "backups", `backup-${timestamp}`);
18556
18565
  const usedFilenames = /* @__PURE__ */ new Set();
18557
18566
  const stores = discovered.stores.map((store) => ({
18558
18567
  storeId: store.storeId,
@@ -18569,7 +18578,7 @@ function planBackup(options = {}) {
18569
18578
  function createBackup(options = {}) {
18570
18579
  const { projectRoot, outDir, createdAt, stores, files } = planBackup(options);
18571
18580
  const backupId = `bk_${randomBytes(8).toString("hex")}`;
18572
- if (!existsSync4(outDir)) {
18581
+ if (!existsSync5(outDir)) {
18573
18582
  mkdirSync(outDir, { recursive: true, mode: 448 });
18574
18583
  }
18575
18584
  const backedUpStores = [];
@@ -18622,7 +18631,7 @@ function createBackup(options = {}) {
18622
18631
  return { manifest, outDir };
18623
18632
  }
18624
18633
  function planRestore(manifestPathOrDir, options = {}) {
18625
- let manifestPath = resolve3(manifestPathOrDir);
18634
+ let manifestPath = resolve4(manifestPathOrDir);
18626
18635
  const stat = lstatSync2(manifestPath, { throwIfNoEntry: false });
18627
18636
  if (!stat) {
18628
18637
  throw databaseError("runtime_path_invalid", manifestPath, `manifest path ${manifestPath} does not exist`);
@@ -18630,7 +18639,7 @@ function planRestore(manifestPathOrDir, options = {}) {
18630
18639
  if (stat.isDirectory()) {
18631
18640
  manifestPath = join4(manifestPath, "manifest.json");
18632
18641
  }
18633
- if (!existsSync4(manifestPath)) {
18642
+ if (!existsSync5(manifestPath)) {
18634
18643
  throw databaseError("runtime_path_invalid", manifestPath, `backup manifest ${manifestPath} not found`);
18635
18644
  }
18636
18645
  const manifestDir = dirname4(manifestPath);
@@ -18650,7 +18659,7 @@ function planRestore(manifestPathOrDir, options = {}) {
18650
18659
  const stores = [];
18651
18660
  for (const store of manifest.stores) {
18652
18661
  const backupFilePath = join4(manifestDir, store.backupFile);
18653
- if (!existsSync4(backupFilePath)) {
18662
+ if (!existsSync5(backupFilePath)) {
18654
18663
  throw databaseError("restore_file_missing", backupFilePath, `backup file ${store.backupFile} missing from ${manifestDir}`);
18655
18664
  }
18656
18665
  const actualSha256 = fileSha256(backupFilePath);
@@ -18672,14 +18681,14 @@ function planRestore(manifestPathOrDir, options = {}) {
18672
18681
  let targetPath = store.sourcePath;
18673
18682
  if (options.projectRoot && manifest.projectRoot && targetPath.startsWith(manifest.projectRoot)) {
18674
18683
  const rel = targetPath.slice(manifest.projectRoot.length).replace(/^[\\/]+/, "");
18675
- targetPath = join4(resolve3(options.projectRoot), rel);
18684
+ targetPath = join4(resolve4(options.projectRoot), rel);
18676
18685
  }
18677
18686
  stores.push({ storeId: store.storeId, backupFilePath, targetPath, schemaVersion: store.schemaVersion, maxSupportedVersion });
18678
18687
  }
18679
18688
  const files = [];
18680
18689
  for (const file of manifest.files ?? []) {
18681
18690
  const backupFilePath = join4(manifestDir, file.backupFile);
18682
- if (!existsSync4(backupFilePath)) {
18691
+ if (!existsSync5(backupFilePath)) {
18683
18692
  throw databaseError("restore_file_missing", backupFilePath, `backup file ${file.backupFile} missing from ${manifestDir}`);
18684
18693
  }
18685
18694
  const actualSha256 = fileSha256(backupFilePath);
@@ -18693,7 +18702,7 @@ function planRestore(manifestPathOrDir, options = {}) {
18693
18702
  let targetPath = file.sourcePath;
18694
18703
  if (options.projectRoot && manifest.projectRoot && targetPath.startsWith(manifest.projectRoot)) {
18695
18704
  const rel = targetPath.slice(manifest.projectRoot.length).replace(/^[\\/]+/, "");
18696
- targetPath = join4(resolve3(options.projectRoot), rel);
18705
+ targetPath = join4(resolve4(options.projectRoot), rel);
18697
18706
  }
18698
18707
  files.push({ id: file.id, backupFilePath, targetPath });
18699
18708
  }
@@ -18720,7 +18729,7 @@ function restoreBackup(manifestPathOrDir, options = {}) {
18720
18729
 
18721
18730
  // plugins/kxm/src/runtime-store.ts
18722
18731
  function kxmRuntimePaths(options = {}) {
18723
- const stateRoot = options.stateRoot ? resolve4(options.stateRoot) : kxmUserStateRoot({ ...options.env ? { env: options.env } : {}, ...options.homeDir ? { homeDir: options.homeDir } : {} });
18732
+ const stateRoot = options.stateRoot ? resolve5(options.stateRoot) : kxmUserStateRoot({ ...options.env ? { env: options.env } : {}, ...options.homeDir ? { homeDir: options.homeDir } : {} });
18724
18733
  const runtimeDir = join5(stateRoot, "runtime");
18725
18734
  return {
18726
18735
  stateRoot,
@@ -18738,9 +18747,9 @@ function runtimeError(code, file, message) {
18738
18747
  function projectRuntimeKey(projectRoot) {
18739
18748
  let canonical;
18740
18749
  try {
18741
- canonical = realpathSync2.native(resolve4(projectRoot));
18750
+ canonical = realpathSync2.native(resolve5(projectRoot));
18742
18751
  } catch {
18743
- canonical = resolve4(projectRoot);
18752
+ canonical = resolve5(projectRoot);
18744
18753
  }
18745
18754
  const folded = process.platform === "win32" ? canonical.toLocaleLowerCase("en-US") : canonical;
18746
18755
  return createHash5("sha256").update(folded, "utf8").digest("hex").slice(0, 24);
@@ -18750,8 +18759,8 @@ function kxmProjectRunEventsPath(projectRoot, env) {
18750
18759
  }
18751
18760
  function projectRuntimeOwnsRun(projectRoot, runId, env) {
18752
18761
  const path = kxmProjectRunEventsPath(projectRoot, env);
18753
- if (!existsSync5(path)) return false;
18754
- const database = new DatabaseSync(path, { readOnly: true });
18762
+ if (!existsSync6(path)) return false;
18763
+ const database = openReadOnlyDatabase(path);
18755
18764
  try {
18756
18765
  database.exec("PRAGMA busy_timeout = 5000");
18757
18766
  return database.prepare("SELECT 1 FROM runs WHERE run_id = ?").get(runId) !== void 0;
@@ -18788,7 +18797,7 @@ var KxmRuntimeRegistry = class {
18788
18797
  path;
18789
18798
  database;
18790
18799
  constructor(path) {
18791
- this.path = resolve4(path);
18800
+ this.path = resolve5(path);
18792
18801
  this.database = openDatabase(this.path, "runtime registry", {
18793
18802
  schema: REGISTRY_SCHEMA,
18794
18803
  version: KXM_REGISTRY_SCHEMA_VERSION,
@@ -18887,7 +18896,7 @@ var KxmRuntimeRegistry = class {
18887
18896
  return rows.map((row) => ({ projectRoot: row.project_root, projectId: row.project_id }));
18888
18897
  }
18889
18898
  registerProject(registration) {
18890
- const projectRoot = resolve4(registration.projectRoot);
18899
+ const projectRoot = resolve5(registration.projectRoot);
18891
18900
  const projectKey = projectRuntimeKey(projectRoot);
18892
18901
  this.database.exec("BEGIN IMMEDIATE");
18893
18902
  try {
@@ -19314,7 +19323,7 @@ var KxmRunEventStore = class {
19314
19323
  syncRedactor = new KxmSyncRedactor();
19315
19324
  database;
19316
19325
  constructor(path) {
19317
- this.path = resolve4(path);
19326
+ this.path = resolve5(path);
19318
19327
  this.database = openDatabase(this.path, "run event store", {
19319
19328
  schema: EVENT_STORE_SCHEMA,
19320
19329
  version: KXM_EVENT_STORE_SCHEMA_VERSION,
@@ -19339,7 +19348,7 @@ var KxmRunEventStore = class {
19339
19348
  }
19340
19349
  readRunPrompts() {
19341
19350
  const sidecar = this.runPromptSidecarPath();
19342
- if (!existsSync5(sidecar)) return {};
19351
+ if (!existsSync6(sidecar)) return {};
19343
19352
  try {
19344
19353
  const parsed = JSON.parse(readFileSync3(sidecar, "utf8"));
19345
19354
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
@@ -23210,12 +23219,12 @@ function enqueueKxmScheduledRun(storePath, runId, configRevision, bound, start)
23210
23219
  if (owner.admitted.has(runId) || owner.queue.some((item) => item.runId === runId)) {
23211
23220
  return Promise.reject(runtimeError("run_busy", runId, `run ${runId} is already admitted or queued`));
23212
23221
  }
23213
- return new Promise((resolve11, reject) => {
23222
+ return new Promise((resolve12, reject) => {
23214
23223
  owner.queue.push({
23215
23224
  runId,
23216
23225
  configRevision,
23217
23226
  bound,
23218
- start: (token) => start(token).then(resolve11, reject),
23227
+ start: (token) => start(token).then(resolve12, reject),
23219
23228
  fail: (error) => reject(error)
23220
23229
  });
23221
23230
  pump(storePath, owner);
@@ -23276,7 +23285,7 @@ function kxmToolPolicyRevision(bundle) {
23276
23285
  return sha256Of(kxmCanonicalJson({ policies, gateRegistry: bundle.gateRegistry?.value ?? null }));
23277
23286
  }
23278
23287
  function collectMemoryFiles(dir, baseDir, ignoreSubdirs = /* @__PURE__ */ new Set()) {
23279
- if (!existsSync6(dir)) return [];
23288
+ if (!existsSync7(dir)) return [];
23280
23289
  const entries = readdirSync3(dir, { withFileTypes: true });
23281
23290
  const results = [];
23282
23291
  for (const entry of entries) {
@@ -23735,7 +23744,7 @@ function assertKxmConfigError(error) {
23735
23744
  // plugins/kxm/src/runtime-supervisor.ts
23736
23745
  import { spawn as spawn3 } from "node:child_process";
23737
23746
  import { createHash as createHash14, createHmac, randomBytes as randomBytes3, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
23738
- import { chmodSync as chmodSync2, existsSync as existsSync15, lstatSync as lstatSync5, mkdirSync as mkdirSync9, readFileSync as readFileSync12, renameSync as renameSync5, rmSync as rmSync4, writeFileSync as writeFileSync9 } from "node:fs";
23747
+ import { chmodSync as chmodSync2, existsSync as existsSync16, lstatSync as lstatSync5, mkdirSync as mkdirSync9, readFileSync as readFileSync12, renameSync as renameSync5, rmSync as rmSync4, writeFileSync as writeFileSync9 } from "node:fs";
23739
23748
  import { createServer } from "node:http";
23740
23749
  import { dirname as dirname11, isAbsolute as isAbsolute6, join as join18 } from "node:path";
23741
23750
 
@@ -23862,7 +23871,7 @@ async function beginOneShotEvidence(root, intent, sensitive) {
23862
23871
  // plugins/kxm/src/prices.ts
23863
23872
  var import_yaml3 = __toESM(require_dist(), 1);
23864
23873
  import { createHash as createHash9 } from "node:crypto";
23865
- import { existsSync as existsSync7, readFileSync as readFileSync5, statSync, writeFileSync as writeFileSync3 } from "node:fs";
23874
+ import { existsSync as existsSync8, readFileSync as readFileSync5, statSync, writeFileSync as writeFileSync3 } from "node:fs";
23866
23875
  import { join as join8 } from "node:path";
23867
23876
 
23868
23877
  // plugins/kxm/src/price-calc.ts
@@ -24015,8 +24024,8 @@ function parsePriceCatalog(text) {
24015
24024
  return catalog;
24016
24025
  }
24017
24026
  function loadPriceCatalog(rootOrPath) {
24018
- const candidatePath = existsSync7(join8(rootOrPath, ".kxm", "prices.yaml")) ? join8(rootOrPath, ".kxm", "prices.yaml") : existsSync7(join8(rootOrPath, "prices.yaml")) ? join8(rootOrPath, "prices.yaml") : existsSync7(rootOrPath) && statSync(rootOrPath).isFile() ? rootOrPath : void 0;
24019
- if (!candidatePath || !existsSync7(candidatePath)) {
24027
+ const candidatePath = existsSync8(join8(rootOrPath, ".kxm", "prices.yaml")) ? join8(rootOrPath, ".kxm", "prices.yaml") : existsSync8(join8(rootOrPath, "prices.yaml")) ? join8(rootOrPath, "prices.yaml") : existsSync8(rootOrPath) && statSync(rootOrPath).isFile() ? rootOrPath : void 0;
24028
+ if (!candidatePath || !existsSync8(candidatePath)) {
24020
24029
  return void 0;
24021
24030
  }
24022
24031
  const content = readFileSync5(candidatePath, "utf8");
@@ -24038,19 +24047,19 @@ function loadPriceCatalogForEstimate(options) {
24038
24047
  // plugins/kxm/src/engine.ts
24039
24048
  var import_yaml7 = __toESM(require_dist(), 1);
24040
24049
  import { createHash as createHash13, randomBytes as randomBytes2, timingSafeEqual } from "node:crypto";
24041
- import { existsSync as existsSync11, readFileSync as readFileSync9 } from "node:fs";
24050
+ import { existsSync as existsSync12, readFileSync as readFileSync9 } from "node:fs";
24042
24051
  import { join as join14 } from "node:path";
24043
24052
 
24044
24053
  // plugins/kxm/src/routes.ts
24045
24054
  var import_yaml4 = __toESM(require_dist(), 1);
24046
- import { existsSync as existsSync8, readFileSync as readFileSync6, mkdirSync as mkdirSync3, writeFileSync as writeFileSync4, readdirSync as readdirSync4 } from "node:fs";
24055
+ import { existsSync as existsSync9, readFileSync as readFileSync6, mkdirSync as mkdirSync3, writeFileSync as writeFileSync4, readdirSync as readdirSync4 } from "node:fs";
24047
24056
  import { join as join9 } from "node:path";
24048
24057
  var RETIRED_POLICY = ".kxm/producers.yaml";
24049
24058
  var empty = () => ({ schema: "kxm.routes.v2", updatedAt: (/* @__PURE__ */ new Date()).toISOString(), admitted: [], disabled: [], roles: {} });
24050
24059
  function loadRoutePolicy(root) {
24051
- if (existsSync8(join9(root, RETIRED_POLICY))) throw new Error(`retired ${RETIRED_POLICY} present; use .kxm/routes.yaml (kxm.routes.v2)`);
24060
+ if (existsSync9(join9(root, RETIRED_POLICY))) throw new Error(`retired ${RETIRED_POLICY} present; use .kxm/routes.yaml (kxm.routes.v2)`);
24052
24061
  const path = join9(root, ".kxm", "routes.yaml");
24053
- if (!existsSync8(path)) return empty();
24062
+ if (!existsSync9(path)) return empty();
24054
24063
  const value = (0, import_yaml4.parse)(readFileSync6(path, "utf8"));
24055
24064
  if (value?.schema !== "kxm.routes.v2" || !Array.isArray(value.admitted)) throw new Error("invalid .kxm/routes.yaml");
24056
24065
  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")])) : {} };
@@ -24058,7 +24067,7 @@ function loadRoutePolicy(root) {
24058
24067
  function listRoleBindings(root) {
24059
24068
  const dir = join9(root, ".kxm", "roles");
24060
24069
  const result = {};
24061
- if (!existsSync8(dir)) return result;
24070
+ if (!existsSync9(dir)) return result;
24062
24071
  for (const file of readdirSync4(dir).filter((name) => name.endsWith(".yaml"))) {
24063
24072
  const role = file.slice(0, -5);
24064
24073
  const value = (0, import_yaml4.parse)(readFileSync6(join9(dir, file), "utf8"));
@@ -25021,8 +25030,8 @@ function memoryRecordToContextItem(record2, project) {
25021
25030
 
25022
25031
  // plugins/kxm/src/memory.ts
25023
25032
  var import_yaml5 = __toESM(require_dist(), 1);
25024
- import { existsSync as existsSync9, mkdirSync as mkdirSync4, readdirSync as readdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "node:fs";
25025
- import { extname as extname2, join as join10, resolve as resolve5 } from "node:path";
25033
+ import { existsSync as existsSync10, mkdirSync as mkdirSync4, readdirSync as readdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "node:fs";
25034
+ import { extname as extname2, join as join10, resolve as resolve6 } from "node:path";
25026
25035
  var MEMORY_SCHEMA = "kxm.memory.v1";
25027
25036
  var VALID_SCOPES = /* @__PURE__ */ new Set(["agent", "project", "run", "operator"]);
25028
25037
  var VALID_AUTHORITIES = /* @__PURE__ */ new Set(["instruction", "evidence", "promoted"]);
@@ -25120,14 +25129,14 @@ function parseMemoryRecord(raw, filename = "memory.md") {
25120
25129
  };
25121
25130
  }
25122
25131
  function memoryDirectories(repoRoot) {
25123
- const root = resolve5(repoRoot);
25132
+ const root = resolve6(repoRoot);
25124
25133
  const memoryDir = join10(root, ".kxm", "memory");
25125
25134
  const candidatesDir = join10(memoryDir, "candidates");
25126
25135
  return { memoryDir, candidatesDir };
25127
25136
  }
25128
25137
  function loadAuthoredMemory(repoRoot) {
25129
25138
  const { memoryDir } = memoryDirectories(repoRoot);
25130
- if (!existsSync9(memoryDir)) return [];
25139
+ if (!existsSync10(memoryDir)) return [];
25131
25140
  const records = [];
25132
25141
  const entries = readdirSync5(memoryDir, { withFileTypes: true });
25133
25142
  for (const entry of entries) {
@@ -25150,7 +25159,7 @@ function loadAuthoredMemory(repoRoot) {
25150
25159
  // plugins/kxm/src/skills.ts
25151
25160
  var import_yaml6 = __toESM(require_dist(), 1);
25152
25161
  import { createHash as createHash10 } from "node:crypto";
25153
- import { existsSync as existsSync10, mkdirSync as mkdirSync5, readdirSync as readdirSync6, readFileSync as readFileSync8, renameSync, rmSync, statSync as statSync2, writeFileSync as writeFileSync6 } from "node:fs";
25162
+ import { existsSync as existsSync11, mkdirSync as mkdirSync5, readdirSync as readdirSync6, readFileSync as readFileSync8, renameSync, rmSync, statSync as statSync2, writeFileSync as writeFileSync6 } from "node:fs";
25154
25163
  import { dirname as dirname7, join as join11 } from "node:path";
25155
25164
  var SKILL_CANDIDATE_SCHEMA = "kxm.skill-candidate.v1";
25156
25165
  var SKILL_EVALUATION_SCHEMA = "kxm.skill-evaluation.v1";
@@ -25269,7 +25278,7 @@ var SkillLifecycle = class {
25269
25278
  appendHistory(id, record2) {
25270
25279
  const line = `${JSON.stringify(record2)}
25271
25280
  `;
25272
- if (existsSync10(this.historyFile(id))) {
25281
+ if (existsSync11(this.historyFile(id))) {
25273
25282
  const existing = readFileSync8(this.historyFile(id), "utf8");
25274
25283
  const lines = existing.split("\n").filter((entry) => entry.trim());
25275
25284
  this.write(this.historyFile(id), [...lines.slice(-499), line.trim()].join("\n") + "\n");
@@ -25279,12 +25288,12 @@ var SkillLifecycle = class {
25279
25288
  }
25280
25289
  history(id) {
25281
25290
  const file = this.historyFile(id);
25282
- if (!existsSync10(file)) return [];
25291
+ if (!existsSync11(file)) return [];
25283
25292
  return readFileSync8(file, "utf8").split("\n").filter((line) => line.trim()).map((line) => JSON.parse(line));
25284
25293
  }
25285
25294
  readMetadata(state, id) {
25286
25295
  const { metadata } = this.paths(state, id);
25287
- if (!existsSync10(metadata)) {
25296
+ if (!existsSync11(metadata)) {
25288
25297
  throw new SkillLifecycleError("skill_not_found", `skill ${id} not found in ${state}`);
25289
25298
  }
25290
25299
  return JSON.parse(readFileSync8(metadata, "utf8"));
@@ -25292,7 +25301,7 @@ var SkillLifecycle = class {
25292
25301
  move(from, to, id) {
25293
25302
  const fromDir = join11(this.dir(from), id);
25294
25303
  const toDir = join11(this.dir(to), id);
25295
- if (!existsSync10(fromDir)) {
25304
+ if (!existsSync11(fromDir)) {
25296
25305
  throw new SkillLifecycleError("skill_not_found", `skill ${id} not found in ${from}`);
25297
25306
  }
25298
25307
  if (this.dryRun) {
@@ -25300,7 +25309,7 @@ var SkillLifecycle = class {
25300
25309
  return;
25301
25310
  }
25302
25311
  mkdirSync5(this.dir(to), { recursive: true });
25303
- if (existsSync10(toDir)) rmSync(toDir, { recursive: true, force: true });
25312
+ if (existsSync11(toDir)) rmSync(toDir, { recursive: true, force: true });
25304
25313
  renameSync(fromDir, toDir);
25305
25314
  }
25306
25315
  /** Submit a new skill candidate. Content is redacted of secret material at
@@ -25343,7 +25352,7 @@ var SkillLifecycle = class {
25343
25352
  const contentSha256 = skillContentSha256(content);
25344
25353
  const id = skillIdFor(name, contentSha256);
25345
25354
  const { metadata, skill } = this.paths("candidate", id);
25346
- if (existsSync10(metadata)) {
25355
+ if (existsSync11(metadata)) {
25347
25356
  throw new SkillLifecycleError(
25348
25357
  "skill_candidate_exists",
25349
25358
  `identical candidate ${id} already exists; changed behavior requires changed content`
@@ -25502,7 +25511,7 @@ var SkillLifecycle = class {
25502
25511
  }
25503
25512
  list(state) {
25504
25513
  const dir = this.dir(state);
25505
- if (!existsSync10(dir)) return [];
25514
+ if (!existsSync11(dir)) return [];
25506
25515
  const ids = readdirSorted(dir);
25507
25516
  return ids.map((id) => {
25508
25517
  try {
@@ -25734,14 +25743,14 @@ import { join as join13 } from "node:path";
25734
25743
 
25735
25744
  // plugins/kxm/src/artifacts-exist.ts
25736
25745
  import { lstatSync as lstatSync4, realpathSync as realpathSync3, statSync as statSync3 } from "node:fs";
25737
- import { isAbsolute as isAbsolute3, relative as relative3, resolve as resolve6 } from "node:path";
25746
+ import { isAbsolute as isAbsolute3, relative as relative3, resolve as resolve7 } from "node:path";
25738
25747
  function staysUnder(root, candidate) {
25739
25748
  const child = relative3(root, candidate);
25740
25749
  return child === "" || !isAbsolute3(child) && child !== ".." && !child.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`);
25741
25750
  }
25742
25751
  function verifyArtifactExists(rootInput, pathInput) {
25743
- const lexicalRoot = resolve6(rootInput);
25744
- const lexicalPath = resolve6(pathInput);
25752
+ const lexicalRoot = resolve7(rootInput);
25753
+ const lexicalPath = resolve7(pathInput);
25745
25754
  if (!staysUnder(lexicalRoot, lexicalPath)) {
25746
25755
  return { ok: false, error: "artifact_outside_workspace_assets", path: lexicalPath };
25747
25756
  }
@@ -25940,8 +25949,8 @@ var CommandObserver = class {
25940
25949
  this.requestStop("error", "lost-close");
25941
25950
  }
25942
25951
  run() {
25943
- return new Promise((resolve11) => {
25944
- this.resolveOutcome = resolve11;
25952
+ return new Promise((resolve12) => {
25953
+ this.resolveOutcome = resolve12;
25945
25954
  try {
25946
25955
  this.start();
25947
25956
  } catch (error) {
@@ -27267,8 +27276,8 @@ var KxmRunScheduler = class _KxmRunScheduler {
27267
27276
  resolve: (_value) => void 0,
27268
27277
  reject: (_error) => void 0
27269
27278
  };
27270
- const settled = new Promise((resolve11, reject) => {
27271
- pending.resolve = resolve11;
27279
+ const settled = new Promise((resolve12, reject) => {
27280
+ pending.resolve = resolve12;
27272
27281
  pending.reject = reject;
27273
27282
  });
27274
27283
  void settled.then(void 0, () => void 0);
@@ -27667,7 +27676,7 @@ function unreconciledPanelAttemptId(state) {
27667
27676
  function resolveProducerRoute(projectRoot, step, agentId) {
27668
27677
  let agentModel;
27669
27678
  const agentFile = join14(projectRoot, ".kxm", "agents", `${agentId}.yaml`);
27670
- if (existsSync11(agentFile)) {
27679
+ if (existsSync12(agentFile)) {
27671
27680
  try {
27672
27681
  const parsed = (0, import_yaml7.parse)(readFileSync9(agentFile, "utf8"));
27673
27682
  if (typeof parsed?.model === "string") {
@@ -28873,7 +28882,7 @@ function unsupportedStep(plan, step, producerId) {
28873
28882
  return void 0;
28874
28883
  }
28875
28884
  function readYamlRecord(path) {
28876
- if (!existsSync11(path)) return void 0;
28885
+ if (!existsSync12(path)) return void 0;
28877
28886
  try {
28878
28887
  const parsed = (0, import_yaml7.parse)(readFileSync9(path, "utf8"));
28879
28888
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
@@ -28915,7 +28924,7 @@ function unsupportedLiveWrite(projectRoot, step, agentId, selector, maxConcurren
28915
28924
  };
28916
28925
  }
28917
28926
  const rosterPath = join14(projectRoot, ".kxm", "roster.yaml");
28918
- if (!existsSync11(rosterPath)) return void 0;
28927
+ if (!existsSync12(rosterPath)) return void 0;
28919
28928
  const roster = readYamlRecord(rosterPath);
28920
28929
  if (!roster || roster.schema !== "kxm.developer-roster.v1") {
28921
28930
  return { reason: "step_unsupported", field: "model", detail: "live write steps require a readable kxm.developer-roster.v1" };
@@ -29796,9 +29805,9 @@ var RuntimeHubClient = class {
29796
29805
  };
29797
29806
 
29798
29807
  // plugins/kxm/src/hub-binding.ts
29799
- import { existsSync as existsSync12, mkdirSync as mkdirSync6, readFileSync as readFileSync10, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync7 } from "node:fs";
29808
+ import { existsSync as existsSync13, mkdirSync as mkdirSync6, readFileSync as readFileSync10, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync7 } from "node:fs";
29800
29809
  import { homedir as homedir2 } from "node:os";
29801
- import { dirname as dirname8, isAbsolute as isAbsolute4, join as join16, resolve as resolve7 } from "node:path";
29810
+ import { dirname as dirname8, isAbsolute as isAbsolute4, join as join16, resolve as resolve8 } from "node:path";
29802
29811
  var HUB_BINDING_SCHEMA = "kxm.hub-binding.v1";
29803
29812
  var HubBindingError = class extends Error {
29804
29813
  constructor(message) {
@@ -29810,17 +29819,17 @@ function resolveUserStateRoot(env) {
29810
29819
  const explicit = env.KXM_STATE_HOME?.trim();
29811
29820
  if (explicit) {
29812
29821
  if (!isAbsolute4(explicit)) throw new HubBindingError("local_state_root_not_absolute");
29813
- return resolve7(explicit);
29822
+ return resolve8(explicit);
29814
29823
  }
29815
29824
  if (process.platform === "win32") {
29816
29825
  const localAppData = env.LOCALAPPDATA?.trim();
29817
29826
  const base2 = localAppData && isAbsolute4(localAppData) ? localAppData : join16(homedir2(), "AppData", "Local");
29818
- return resolve7(base2, "KXM");
29827
+ return resolve8(base2, "KXM");
29819
29828
  }
29820
- if (process.platform === "darwin") return resolve7(homedir2(), "Library", "Application Support", "KXM");
29829
+ if (process.platform === "darwin") return resolve8(homedir2(), "Library", "Application Support", "KXM");
29821
29830
  const xdgState = env.XDG_STATE_HOME?.trim();
29822
29831
  const base = xdgState && isAbsolute4(xdgState) ? xdgState : join16(homedir2(), ".local", "state");
29823
- return resolve7(base, "kxm");
29832
+ return resolve8(base, "kxm");
29824
29833
  }
29825
29834
  function hubBindingFile(env = process.env) {
29826
29835
  return join16(resolveUserStateRoot(env), "hub-binding.json");
@@ -29843,7 +29852,7 @@ function isIsoTimestamp(value) {
29843
29852
  }
29844
29853
  function readHubBinding(env = process.env) {
29845
29854
  const file = hubBindingFile(env);
29846
- if (!existsSync12(file)) return void 0;
29855
+ if (!existsSync13(file)) return void 0;
29847
29856
  let parsed;
29848
29857
  try {
29849
29858
  parsed = JSON.parse(readFileSync10(file, "utf8"));
@@ -29868,9 +29877,9 @@ function readHubBinding(env = process.env) {
29868
29877
  }
29869
29878
 
29870
29879
  // plugins/kxm/src/hub-env.ts
29871
- import { existsSync as existsSync13, mkdirSync as mkdirSync7, readFileSync as readFileSync11, renameSync as renameSync3, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "node:fs";
29880
+ import { existsSync as existsSync14, mkdirSync as mkdirSync7, readFileSync as readFileSync11, renameSync as renameSync3, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "node:fs";
29872
29881
  import { homedir as homedir3 } from "node:os";
29873
- import { dirname as dirname9, isAbsolute as isAbsolute5, join as join17, resolve as resolve8 } from "node:path";
29882
+ import { dirname as dirname9, isAbsolute as isAbsolute5, join as join17, resolve as resolve9 } from "node:path";
29874
29883
  var HUB_ENV_SCHEMA = "kxm.hub-env.v1";
29875
29884
  var HubEnvError = class extends Error {
29876
29885
  constructor(message) {
@@ -29882,24 +29891,24 @@ function resolveUserStateRoot2(env) {
29882
29891
  const explicit = env.KXM_STATE_HOME?.trim();
29883
29892
  if (explicit) {
29884
29893
  if (!isAbsolute5(explicit)) throw new HubEnvError("local_state_root_not_absolute");
29885
- return resolve8(explicit);
29894
+ return resolve9(explicit);
29886
29895
  }
29887
29896
  if (process.platform === "win32") {
29888
29897
  const localAppData = env.LOCALAPPDATA?.trim();
29889
29898
  const base2 = localAppData && isAbsolute5(localAppData) ? localAppData : join17(homedir3(), "AppData", "Local");
29890
- return resolve8(base2, "KXM");
29899
+ return resolve9(base2, "KXM");
29891
29900
  }
29892
- if (process.platform === "darwin") return resolve8(homedir3(), "Library", "Application Support", "KXM");
29901
+ if (process.platform === "darwin") return resolve9(homedir3(), "Library", "Application Support", "KXM");
29893
29902
  const xdgState = env.XDG_STATE_HOME?.trim();
29894
29903
  const base = xdgState && isAbsolute5(xdgState) ? xdgState : join17(homedir3(), ".local", "state");
29895
- return resolve8(base, "kxm");
29904
+ return resolve9(base, "kxm");
29896
29905
  }
29897
29906
  function hubEnvFile(env = process.env) {
29898
29907
  return join17(resolveUserStateRoot2(env), "hub-env.json");
29899
29908
  }
29900
29909
  function readHubEnvRecord(env = process.env) {
29901
29910
  const file = hubEnvFile(env);
29902
- if (!existsSync13(file)) return void 0;
29911
+ if (!existsSync14(file)) return void 0;
29903
29912
  let parsed;
29904
29913
  try {
29905
29914
  parsed = JSON.parse(readFileSync11(file, "utf8"));
@@ -29942,7 +29951,7 @@ function resolveClientHubAuthToken(env, project) {
29942
29951
  }
29943
29952
 
29944
29953
  // plugins/kxm/src/logger.ts
29945
- import { appendFileSync, existsSync as existsSync14, mkdirSync as mkdirSync8, renameSync as renameSync4, statSync as statSync4, unlinkSync as unlinkSync2 } from "node:fs";
29954
+ import { appendFileSync, existsSync as existsSync15, mkdirSync as mkdirSync8, renameSync as renameSync4, statSync as statSync4, unlinkSync as unlinkSync2 } from "node:fs";
29946
29955
  import { dirname as dirname10 } from "node:path";
29947
29956
  var LOG_LEVEL_PRIORITY = {
29948
29957
  debug: 10,
@@ -29980,7 +29989,7 @@ function redactLogValue(val, key) {
29980
29989
  function rotateLogFiles(filePath, maxFiles) {
29981
29990
  for (let i = maxFiles; i >= 1; i--) {
29982
29991
  const current = `${filePath}.${i}`;
29983
- if (existsSync14(current)) {
29992
+ if (existsSync15(current)) {
29984
29993
  if (i >= maxFiles) {
29985
29994
  try {
29986
29995
  unlinkSync2(current);
@@ -29994,7 +30003,7 @@ function rotateLogFiles(filePath, maxFiles) {
29994
30003
  }
29995
30004
  }
29996
30005
  }
29997
- if (existsSync14(filePath)) {
30006
+ if (existsSync15(filePath)) {
29998
30007
  try {
29999
30008
  renameSync4(filePath, `${filePath}.1`);
30000
30009
  } catch {
@@ -30011,7 +30020,7 @@ function createLogger(options) {
30011
30020
  const shouldStdout = options.stdout ?? !isDaemon;
30012
30021
  const correlationDefaults = options.correlation ?? {};
30013
30022
  let currentSize = 0;
30014
- if (filePath && existsSync14(filePath)) {
30023
+ if (filePath && existsSync15(filePath)) {
30015
30024
  try {
30016
30025
  currentSize = statSync4(filePath).size;
30017
30026
  } catch {
@@ -30138,7 +30147,7 @@ function supervisorErrorFile(paths) {
30138
30147
  }
30139
30148
  function clearSupervisorError(paths) {
30140
30149
  const file = supervisorErrorFile(paths);
30141
- if (existsSync15(file)) rmSync4(file, { force: true });
30150
+ if (existsSync16(file)) rmSync4(file, { force: true });
30142
30151
  }
30143
30152
  function recordSupervisorError(paths, message) {
30144
30153
  try {
@@ -30161,7 +30170,7 @@ function readRecentSupervisorError(paths) {
30161
30170
  }
30162
30171
  }
30163
30172
  function kxmSupervisorStatus(paths) {
30164
- if (!existsSync15(paths.registryDb)) return { running: false };
30173
+ if (!existsSync16(paths.registryDb)) return { running: false };
30165
30174
  const registry = new KxmRuntimeRegistry(paths.registryDb);
30166
30175
  try {
30167
30176
  const record2 = registry.supervisor();
@@ -30316,8 +30325,8 @@ async function waitForDriveSessions(settled, graceMs) {
30316
30325
  try {
30317
30326
  await Promise.race([
30318
30327
  Promise.allSettled(settled),
30319
- new Promise((resolve11) => {
30320
- timeout = setTimeout(resolve11, graceMs);
30328
+ new Promise((resolve12) => {
30329
+ timeout = setTimeout(resolve12, graceMs);
30321
30330
  timeout.unref?.();
30322
30331
  })
30323
30332
  ]);
@@ -31269,8 +31278,8 @@ var PiSession = class {
31269
31278
  }
31270
31279
  const id = `cmd_${++this.commandCounter}`;
31271
31280
  const payload = { ...command, id };
31272
- return new Promise((resolve11, reject) => {
31273
- this.pendingCommands.set(id, { resolve: resolve11, reject });
31281
+ return new Promise((resolve12, reject) => {
31282
+ this.pendingCommands.set(id, { resolve: resolve12, reject });
31274
31283
  try {
31275
31284
  this.process.stdin.write(JSON.stringify(payload) + "\n");
31276
31285
  } catch (err) {
@@ -31290,7 +31299,7 @@ var PiSession = class {
31290
31299
  return { outcome: "cancelled", text: "aborted", usage: {} };
31291
31300
  }
31292
31301
  this.status = "busy";
31293
- return new Promise((resolve11, reject) => {
31302
+ return new Promise((resolve12, reject) => {
31294
31303
  let signalCleanup;
31295
31304
  if (signal) {
31296
31305
  const onAbort = () => {
@@ -31304,7 +31313,7 @@ var PiSession = class {
31304
31313
  signalCleanup = () => signal.removeEventListener("abort", onAbort);
31305
31314
  }
31306
31315
  this.activePrompt = {
31307
- resolve: resolve11,
31316
+ resolve: resolve12,
31308
31317
  reject,
31309
31318
  allowedOutcomes,
31310
31319
  text: "",
@@ -31556,8 +31565,8 @@ function createKxmPiProducer(options = {}) {
31556
31565
 
31557
31566
  // plugins/kxm/src/improve.ts
31558
31567
  import { createHash as createHash15 } from "node:crypto";
31559
- import { existsSync as existsSync16, mkdirSync as mkdirSync10, writeFileSync as writeFileSync10 } from "node:fs";
31560
- import { join as join20, relative as relative4, resolve as resolve9 } from "node:path";
31568
+ import { existsSync as existsSync17, mkdirSync as mkdirSync10, writeFileSync as writeFileSync10 } from "node:fs";
31569
+ import { join as join20, relative as relative4, resolve as resolve10 } from "node:path";
31561
31570
  var CANDIDATE_SCHEMA = "kxm.candidate.v1";
31562
31571
  var IMPROVEMENT_REPORT_SCHEMA = "kxm.improvement-report.v2";
31563
31572
  var IMPROVEMENT_REPORT_V1_SCHEMA = "kxm.improvement-report.v1";
@@ -31813,8 +31822,8 @@ function groupRoutingRecords(records, options = {}) {
31813
31822
  }).map((entry) => entry.row);
31814
31823
  }
31815
31824
  function buildImprovementReport(records, options = {}) {
31816
- const projectRoot = options.projectRoot ? resolve9(options.projectRoot) : process.cwd();
31817
- const candidatesDir = options.candidatesDir ? resolve9(options.candidatesDir) : join20(projectRoot, ".kxm", "candidates");
31825
+ const projectRoot = options.projectRoot ? resolve10(options.projectRoot) : process.cwd();
31826
+ const candidatesDir = options.candidatesDir ? resolve10(options.candidatesDir) : join20(projectRoot, ".kxm", "candidates");
31818
31827
  const promotionPolicy = options.promotionPolicy ?? "manual_pr";
31819
31828
  const groups = groupRoutingRecords(records, options);
31820
31829
  const candidates = [];
@@ -31850,7 +31859,7 @@ function buildImprovementReport(records, options = {}) {
31850
31859
  createdAt: nowIso()
31851
31860
  };
31852
31861
  if (!options.dryRun) {
31853
- if (!existsSync16(candidatesDir)) {
31862
+ if (!existsSync17(candidatesDir)) {
31854
31863
  mkdirSync10(candidatesDir, { recursive: true });
31855
31864
  }
31856
31865
  writeFileSync10(diffFilePath, diff, "utf8");
@@ -32386,7 +32395,7 @@ Instructions for Operator:
32386
32395
 
32387
32396
  // plugins/kxm/src/modes.ts
32388
32397
  var import_yaml8 = __toESM(require_dist(), 1);
32389
- import { existsSync as existsSync17, readFileSync as readFileSync14 } from "node:fs";
32398
+ import { existsSync as existsSync18, readFileSync as readFileSync14 } from "node:fs";
32390
32399
  import { join as join21 } from "node:path";
32391
32400
  var DEFAULT_MODES_CONFIG = Object.freeze({
32392
32401
  schema: "kxm.modes.v1",
@@ -32451,7 +32460,7 @@ function loadModesConfig(projectRoot) {
32451
32460
  return DEFAULT_MODES_CONFIG;
32452
32461
  }
32453
32462
  const modesPath = join21(projectRoot, ".kxm", "modes.yaml");
32454
- if (!existsSync17(modesPath)) {
32463
+ if (!existsSync18(modesPath)) {
32455
32464
  return DEFAULT_MODES_CONFIG;
32456
32465
  }
32457
32466
  try {
@@ -32543,7 +32552,7 @@ function calculatePromptFootprint(resolved, projectRoot = process.cwd(), catalog
32543
32552
  for (const relPath of resolved.contextFiles) {
32544
32553
  const fullPath = join21(projectRoot, relPath);
32545
32554
  let chars = 0;
32546
- if (existsSync17(fullPath)) {
32555
+ if (existsSync18(fullPath)) {
32547
32556
  try {
32548
32557
  chars = readFileSync14(fullPath, "utf8").length;
32549
32558
  } catch {
@@ -32677,9 +32686,9 @@ ${divider}
32677
32686
 
32678
32687
  // plugins/kxm/src/ssh-remote.ts
32679
32688
  import { spawnSync as spawnSync5 } from "node:child_process";
32680
- import { existsSync as existsSync18, mkdirSync as mkdirSync11, readFileSync as readFileSync15, readdirSync as readdirSync8, rmSync as rmSync5, statSync as statSync5 } from "node:fs";
32689
+ import { existsSync as existsSync19, mkdirSync as mkdirSync11, readFileSync as readFileSync15, readdirSync as readdirSync8, rmSync as rmSync5, statSync as statSync5 } from "node:fs";
32681
32690
  import { homedir as homedir4 } from "node:os";
32682
- import { join as join22, resolve as resolve10 } from "node:path";
32691
+ import { join as join22, resolve as resolve11 } from "node:path";
32683
32692
  var MAX_SSH_OUTPUT_BYTES = 50 * 1024;
32684
32693
  var MAX_SSH_OUTPUT_LINES = 2e3;
32685
32694
  var DEFAULT_SOCKET_DIR = ".kxm/run/ssh-sockets";
@@ -32706,7 +32715,7 @@ function truncateSshOutput(raw) {
32706
32715
  }
32707
32716
  function parseSshConfig(configPath) {
32708
32717
  const targetPath = configPath ?? join22(homedir4(), ".ssh", "config");
32709
- if (!existsSync18(targetPath)) {
32718
+ if (!existsSync19(targetPath)) {
32710
32719
  return [];
32711
32720
  }
32712
32721
  try {
@@ -32787,8 +32796,8 @@ function resolveSshHostG(host, execFn = spawnSync5) {
32787
32796
  }
32788
32797
  }
32789
32798
  function ensureSocketDir(socketDir = DEFAULT_SOCKET_DIR) {
32790
- const resolved = resolve10(socketDir);
32791
- if (!existsSync18(resolved)) {
32799
+ const resolved = resolve11(socketDir);
32800
+ if (!existsSync19(resolved)) {
32792
32801
  mkdirSync11(resolved, { recursive: true, mode: 448 });
32793
32802
  }
32794
32803
  return resolved;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-plugin",
3
- "version": "0.7.100",
3
+ "version": "0.7.101",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "engines": {
@@ -81,10 +81,10 @@ kxm workflow record run_12345 decision "Serialize the fixture setup" --stage-id
81
81
  does not read project `.kxm/workflows/*.yaml`; validate those with
82
82
  `kxm init --dry-run --json`.
83
83
 
84
- `kxm gate signal` on a KXM run ID (`run_` plus 32 hex digits) goes to the
85
- Runtime and passes `--recovery-action` through; on a hub webhook run it posts a
86
- signed callback that needs `KXM_WORKFLOW_ID` and the definition's signal
87
- secret. Reuse `--delivery-id` to retry one unchanged callback.
84
+ `kxm gate signal` on a run this project's Runtime store holds goes to the
85
+ Runtime and passes `--recovery-action` through; on a hub webhook run, including
86
+ one with the same `run_` + 32-hex shape, it posts a signed callback that needs
87
+ `KXM_WORKFLOW_ID` and the definition's signal secret. Reuse `--delivery-id` to retry one unchanged callback.
88
88
 
89
89
  ```bash
90
90
  kxm gate validate --file workflows.json --json
@@ -19,6 +19,7 @@ import {
19
19
  } from "../role.ts";
20
20
  import { discoverKxmProjectRoot } from "../project-config.ts";
21
21
  import { ensureKxmSupervisor, kxmRuntimeRequest } from "../runtime-supervisor.ts";
22
+ import { projectRuntimeOwnsRun } from "../runtime-store.ts";
22
23
  import { resumeWorkflowFromRuling, type WorkflowRun } from "../workflow.ts";
23
24
  import { print, printPlan, type CliIo, type Runtime } from "./types.ts";
24
25
 
@@ -547,9 +548,9 @@ export async function cmdRoleResume(
547
548
 
548
549
  const effectiveRuling = ruling?.trim() || "operator_ruling: waived and resumed";
549
550
 
550
- // Check if it's a KXM run
551
+ // A run this project's Runtime owns; a hub workflow run of the same id shape falls through.
551
552
  const projectRoot = discoverKxmProjectRoot(runtime.cwd);
552
- if (projectRoot && /^run_[a-f0-9]{32}$/i.test(runId)) {
553
+ if (projectRoot && projectRuntimeOwnsRun(projectRoot, runId, runtime.env)) {
553
554
  if (runtime.dryRun) {
554
555
  printPlan(
555
556
  runtime,
@@ -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.100";
14
+ const VERSION = "0.7.101";
15
15
  const CONFIGURE_PLUGIN = "/plugin configure kxm@kxm";
16
16
  const inbox = new Map<string, MessageRecord>();
17
17
  const notifiedInbox = new Set<string>();
@@ -1,7 +1,7 @@
1
1
  import { createHash, randomUUID } from "node:crypto";
2
2
  import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, writeFileSync } from "node:fs";
3
3
  import { dirname, join, resolve } from "node:path";
4
- import { DatabaseSync } from "./sqlite.ts";
4
+ import { DatabaseSync, openReadOnlyDatabase } from "./sqlite.ts";
5
5
  import { KxmConfigError, validateCoordinator, validateDriveReceipt, validateIntakeMessage, validateRunEvent, kxmCanonicalJson, type JsonValue, type KxmConfigIssue, type KxmConfigOptions } from "./project-config.ts";
6
6
  import { kxmUserStateRoot } from "./bindings.ts";
7
7
  import { deriveKxmSyncEvent, kxmSyncEventBytes, KxmSyncRedactor } from "./sync-transform.ts";
@@ -59,13 +59,13 @@ export function kxmProjectRunEventsPath(projectRoot: string, env: NodeJS.Process
59
59
  /**
60
60
  * Whether this project's Runtime event store holds `runId`. Hub workflow runs and Runtime
61
61
  * runs share the `run_<32 hex>` shape, so a command addressed by run id asks the owning
62
- * store instead of guessing from the id. Read-only; a project whose Runtime never ran
63
- * owns no runs.
62
+ * store instead of guessing from the id. Read-only and leaves no sidecars, so a dry run
63
+ * can ask; a project whose Runtime never ran owns no runs.
64
64
  */
65
65
  export function projectRuntimeOwnsRun(projectRoot: string, runId: string, env: NodeJS.ProcessEnv): boolean {
66
66
  const path = kxmProjectRunEventsPath(projectRoot, env);
67
67
  if (!existsSync(path)) return false;
68
- const database = new DatabaseSync(path, { readOnly: true });
68
+ const database = openReadOnlyDatabase(path);
69
69
  try {
70
70
  database.exec("PRAGMA busy_timeout = 5000");
71
71
  return database.prepare("SELECT 1 FROM runs WHERE run_id = ?").get(runId) !== undefined;