@kynver-app/runtime 0.1.49 → 0.1.50

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.
package/dist/index.js CHANGED
@@ -35,43 +35,25 @@ function handleCliVersionFlag(argv, moduleUrl = import.meta.url, binName) {
35
35
  }
36
36
 
37
37
  // src/dispatch.ts
38
- import path17 from "node:path";
38
+ import path18 from "node:path";
39
39
 
40
40
  // src/config.ts
41
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
41
+ import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "node:fs";
42
+ import { homedir as homedir3 } from "node:os";
43
+ import path4 from "node:path";
44
+
45
+ // src/default-repo-discovery.ts
46
+ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
42
47
  import { homedir as homedir2 } from "node:os";
43
48
  import path3 from "node:path";
49
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
44
50
 
45
- // src/path-values.ts
46
- import { homedir } from "node:os";
47
- import path from "node:path";
48
- function expandHomePath(value) {
49
- if (value === "~") return homedir();
50
- if (value.startsWith("~/") || value.startsWith("~\\")) {
51
- return path.join(homedir(), value.slice(2));
52
- }
53
- return value;
54
- }
55
- function resolveUserPath(value) {
56
- return path.resolve(expandHomePath(value));
57
- }
58
- function redactHomePath(value) {
59
- const expanded = expandHomePath(value);
60
- const resolved = path.resolve(expanded);
61
- const home = path.resolve(homedir());
62
- if (resolved === home) return "~";
63
- if (resolved.startsWith(`${home}${path.sep}`)) {
64
- return `~/${path.relative(home, resolved).split(path.sep).join("/")}`;
65
- }
66
- return resolved.replace(/^\/home\/[^/]+(?=\/|$)/, "~").replace(/^\/Users\/[^/]+(?=\/|$)/, "~");
67
- }
68
- function displayUserPath(value) {
69
- return redactHomePath(value);
70
- }
51
+ // src/git.ts
52
+ import { spawnSync } from "node:child_process";
71
53
 
72
54
  // src/util.ts
73
55
  import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, readdirSync, statSync, writeFileSync } from "node:fs";
74
- import path2 from "node:path";
56
+ import path from "node:path";
75
57
  function fail(message) {
76
58
  console.error(message);
77
59
  process.exit(1);
@@ -100,7 +82,7 @@ function readJson(file, fallback) {
100
82
  }
101
83
  }
102
84
  function writeJson(file, value) {
103
- mkdirSync(path2.dirname(file), { recursive: true });
85
+ mkdirSync(path.dirname(file), { recursive: true });
104
86
  writeFileSync(file, `${JSON.stringify(value, null, 2)}
105
87
  `);
106
88
  }
@@ -136,7 +118,7 @@ function tailFile(file, lines) {
136
118
  return data.split("\n").slice(-lines).join("\n");
137
119
  }
138
120
  function readMaybeFile(file) {
139
- return file ? readFileSync2(path2.resolve(file), "utf8") : "";
121
+ return file ? readFileSync2(path.resolve(file), "utf8") : "";
140
122
  }
141
123
  function listRunIds(runsDir) {
142
124
  if (!existsSync2(runsDir)) return [];
@@ -178,14 +160,280 @@ function secsAgo(ms) {
178
160
  return Math.max(0, Math.round((Date.now() - ms) / 1e3));
179
161
  }
180
162
 
163
+ // src/worker-env.ts
164
+ var FORBIDDEN_WORKER_ENV_KEYS = [
165
+ "ANTHROPIC_API_KEY",
166
+ "ANALYST_API_KEY",
167
+ "RECRUITER_API_KEY",
168
+ "AUTH_SECRET",
169
+ "NEXTAUTH_SECRET",
170
+ "DATABASE_URL",
171
+ "PRODUCTION_DATABASE_URL",
172
+ "REDIS_URL",
173
+ "GOOGLE_CLIENT_SECRET",
174
+ "GITHUB_CLIENT_SECRET",
175
+ "KYNVER_API_KEY",
176
+ "KYNVER_SERVICE_SECRET",
177
+ "KYNVER_RUNTIME_SECRET",
178
+ "OPENCLAW_CRON_SECRET",
179
+ "QSTASH_TOKEN",
180
+ "QSTASH_CURRENT_SIGNING_KEY",
181
+ "QSTASH_NEXT_SIGNING_KEY",
182
+ "TOOL_SECRETS_KEK",
183
+ "TOOL_EXECUTOR_DISPATCH_SECRET",
184
+ "CLOUDFLARE_API_TOKEN",
185
+ "STRIPE_SECRET_KEY",
186
+ "STRIPE_WEBHOOK_SECRET",
187
+ "STRIPE_IDENTITY_WEBHOOK_SECRET",
188
+ "VOYAGE_API_KEY",
189
+ "PERPLEXITY_API_KEY",
190
+ "FRED_API_KEY",
191
+ "FMP_API_KEY",
192
+ "CURSOR_API_KEY"
193
+ ];
194
+ var FORBIDDEN_KEY_SET = new Set(FORBIDDEN_WORKER_ENV_KEYS);
195
+ var FORBIDDEN_SUFFIXES = ["_SECRET", "_API_KEY"];
196
+ function isForbiddenWorkerEnvKey(key) {
197
+ if (FORBIDDEN_KEY_SET.has(key)) return true;
198
+ return FORBIDDEN_SUFFIXES.some((suffix) => key.endsWith(suffix));
199
+ }
200
+ function listForbiddenWorkerEnvKeys(env) {
201
+ return Object.keys(env).filter(isForbiddenWorkerEnvKey).sort();
202
+ }
203
+ function scrubWorkerEnv(env) {
204
+ const next = { ...env };
205
+ for (const key of Object.keys(next)) {
206
+ if (isForbiddenWorkerEnvKey(key)) delete next[key];
207
+ }
208
+ return next;
209
+ }
210
+ function auditWorkerEnv(env) {
211
+ const forbiddenPresent = listForbiddenWorkerEnvKeys(env);
212
+ return { forbiddenPresent, safe: forbiddenPresent.length === 0 };
213
+ }
214
+ function scrubClaudeEnv(env) {
215
+ return scrubWorkerEnv(env);
216
+ }
217
+
218
+ // src/git.ts
219
+ function git(cwd, args, options = {}) {
220
+ const res = spawnSync("git", args, { cwd, encoding: "utf8" });
221
+ if (res.status !== 0 && !options.allowFailure) {
222
+ const message = `git ${args.join(" ")} failed: ${res.stderr || res.stdout}`;
223
+ if (options.throwError) throw new Error(message);
224
+ fail(message);
225
+ }
226
+ return res.stdout || "";
227
+ }
228
+ function ensureGitRepo(repo) {
229
+ git(repo, ["rev-parse", "--show-toplevel"]);
230
+ }
231
+ function gitStatusShort(worktreePath) {
232
+ return git(worktreePath, ["status", "--short"], { allowFailure: true }).split("\n").map((line) => line.trim()).filter(Boolean);
233
+ }
234
+ function gitCapture(cwd, args) {
235
+ try {
236
+ const res = spawnSync("git", args, { cwd, encoding: "utf8" });
237
+ return {
238
+ status: res.status,
239
+ stdout: res.stdout || "",
240
+ stderr: res.stderr || "",
241
+ error: res.error ? res.error.message : null
242
+ };
243
+ } catch (error) {
244
+ return {
245
+ status: null,
246
+ stdout: "",
247
+ stderr: "",
248
+ error: error.message
249
+ };
250
+ }
251
+ }
252
+ function gitIsAncestor(cwd, ancestor, descendant) {
253
+ const res = gitCapture(cwd, ["merge-base", "--is-ancestor", ancestor, descendant]);
254
+ if (res.status === 0) return { isAncestor: true, error: null };
255
+ if (res.status === 1) return { isAncestor: false, error: null };
256
+ return { isAncestor: null, error: res.error || res.stderr || res.stdout || `git exited ${res.status}` };
257
+ }
258
+ function computeGitAncestry(worktreePath, baseOrOptions = "origin/main") {
259
+ const options = typeof baseOrOptions === "string" ? { base: baseOrOptions } : baseOrOptions;
260
+ const baseLabel = options.baseCommit?.trim() || options.base?.trim() || "origin/main";
261
+ const pinnedBaseCommit = options.baseCommit?.trim() || null;
262
+ if (!worktreePath) {
263
+ return unknownAncestry(baseLabel, "missing worktree path");
264
+ }
265
+ const head = gitCapture(worktreePath, ["rev-parse", "HEAD"]);
266
+ if (head.status !== 0) {
267
+ return unknownAncestry(baseLabel, head.error || head.stderr || head.stdout || "failed to resolve HEAD");
268
+ }
269
+ let baseSha;
270
+ if (pinnedBaseCommit) {
271
+ baseSha = pinnedBaseCommit;
272
+ } else {
273
+ const baseHead = gitCapture(worktreePath, ["rev-parse", baseLabel]);
274
+ if (baseHead.status !== 0) {
275
+ return unknownAncestry(
276
+ baseLabel,
277
+ baseHead.error || baseHead.stderr || baseHead.stdout || `failed to resolve ${baseLabel}`,
278
+ head.stdout.trim()
279
+ );
280
+ }
281
+ baseSha = baseHead.stdout.trim();
282
+ }
283
+ const headSha = head.stdout.trim();
284
+ if (headSha === baseSha) {
285
+ return {
286
+ checked: true,
287
+ base: baseLabel,
288
+ head: headSha,
289
+ baseHead: baseSha,
290
+ baseIsAncestorOfHead: true,
291
+ headIsAncestorOfBase: true,
292
+ relation: "synced"
293
+ };
294
+ }
295
+ const baseIsAncestorOfHead = gitIsAncestor(worktreePath, baseSha, headSha);
296
+ const headIsAncestorOfBase = gitIsAncestor(worktreePath, headSha, baseSha);
297
+ const error = baseIsAncestorOfHead.error || headIsAncestorOfBase.error || void 0;
298
+ if (baseIsAncestorOfHead.isAncestor == null || headIsAncestorOfBase.isAncestor == null) {
299
+ return {
300
+ checked: false,
301
+ base: baseLabel,
302
+ head: headSha,
303
+ baseHead: baseSha,
304
+ baseIsAncestorOfHead: baseIsAncestorOfHead.isAncestor,
305
+ headIsAncestorOfBase: headIsAncestorOfBase.isAncestor,
306
+ relation: "unknown",
307
+ ...error ? { error } : {}
308
+ };
309
+ }
310
+ const relation = baseIsAncestorOfHead.isAncestor ? "ahead" : headIsAncestorOfBase.isAncestor ? "merged" : "diverged";
311
+ return {
312
+ checked: true,
313
+ base: baseLabel,
314
+ head: headSha,
315
+ baseHead: baseSha,
316
+ baseIsAncestorOfHead: baseIsAncestorOfHead.isAncestor,
317
+ headIsAncestorOfBase: headIsAncestorOfBase.isAncestor,
318
+ relation,
319
+ ...error ? { error } : {}
320
+ };
321
+ }
322
+ function unknownAncestry(base, error, head = null) {
323
+ return {
324
+ checked: false,
325
+ base,
326
+ head,
327
+ baseHead: null,
328
+ baseIsAncestorOfHead: null,
329
+ headIsAncestorOfBase: null,
330
+ relation: "unknown",
331
+ error
332
+ };
333
+ }
334
+
335
+ // src/path-values.ts
336
+ import { homedir } from "node:os";
337
+ import path2 from "node:path";
338
+ function expandHomePath(value) {
339
+ if (value === "~") return homedir();
340
+ if (value.startsWith("~/") || value.startsWith("~\\")) {
341
+ return path2.join(homedir(), value.slice(2));
342
+ }
343
+ return value;
344
+ }
345
+ function resolveUserPath(value) {
346
+ return path2.resolve(expandHomePath(value));
347
+ }
348
+ function redactHomePath(value) {
349
+ const expanded = expandHomePath(value);
350
+ const resolved = path2.resolve(expanded);
351
+ const home = path2.resolve(homedir());
352
+ if (resolved === home) return "~";
353
+ if (resolved.startsWith(`${home}${path2.sep}`)) {
354
+ return `~/${path2.relative(home, resolved).split(path2.sep).join("/")}`;
355
+ }
356
+ return resolved.replace(/^\/home\/[^/]+(?=\/|$)/, "~").replace(/^\/Users\/[^/]+(?=\/|$)/, "~");
357
+ }
358
+ function displayUserPath(value) {
359
+ return redactHomePath(value);
360
+ }
361
+
362
+ // src/default-repo-discovery.ts
363
+ var WELL_KNOWN_REPO_DIRS = ["Kynver", "repos/Kynver", "code/Kynver", "projects/Kynver"];
364
+ function readPackageName(repoRoot) {
365
+ const pkgPath = path3.join(repoRoot, "package.json");
366
+ if (!existsSync3(pkgPath)) return null;
367
+ try {
368
+ const pkg = JSON.parse(readFileSync3(pkgPath, "utf8"));
369
+ return typeof pkg.name === "string" ? pkg.name.trim() : null;
370
+ } catch {
371
+ return null;
372
+ }
373
+ }
374
+ function isKynverMonorepoRoot(repoRoot) {
375
+ return readPackageName(repoRoot) === "kynver";
376
+ }
377
+ function gitRepoRoot(startDir) {
378
+ const resolvedStart = path3.resolve(startDir);
379
+ if (!existsSync3(resolvedStart)) return null;
380
+ const probe = gitCapture(resolvedStart, ["rev-parse", "--show-toplevel"]);
381
+ if (probe.status !== 0) return null;
382
+ const root = probe.stdout.trim();
383
+ return root.length ? path3.resolve(root) : null;
384
+ }
385
+ function resolveRuntimePackageRoot(moduleUrl = import.meta.url) {
386
+ let dir = path3.dirname(fileURLToPath2(moduleUrl));
387
+ for (let depth = 0; depth < 8; depth += 1) {
388
+ const pkgPath = path3.join(dir, "package.json");
389
+ if (existsSync3(pkgPath)) {
390
+ try {
391
+ const pkg = JSON.parse(readFileSync3(pkgPath, "utf8"));
392
+ if (pkg.name === "@kynver-app/runtime") return dir;
393
+ } catch {
394
+ }
395
+ }
396
+ const parent = path3.dirname(dir);
397
+ if (parent === dir) break;
398
+ dir = parent;
399
+ }
400
+ return null;
401
+ }
402
+ function pushCandidate(seen, out, repo, source) {
403
+ if (!repo) return;
404
+ const resolved = path3.resolve(repo);
405
+ if (seen.has(resolved)) return;
406
+ if (!isKynverMonorepoRoot(resolved)) return;
407
+ seen.add(resolved);
408
+ out.push({ repo: resolved, source });
409
+ }
410
+ function discoverDefaultRepoCandidates(opts) {
411
+ const cwd = opts?.cwd ?? process.cwd();
412
+ const seen = /* @__PURE__ */ new Set();
413
+ const candidates = [];
414
+ pushCandidate(seen, candidates, gitRepoRoot(cwd), "cwd_git");
415
+ const runtimePkgRoot = resolveRuntimePackageRoot(opts?.runtimeModuleUrl ?? import.meta.url);
416
+ if (runtimePkgRoot) {
417
+ pushCandidate(seen, candidates, gitRepoRoot(runtimePkgRoot), "runtime_checkout");
418
+ }
419
+ const home = homedir2();
420
+ for (const rel of WELL_KNOWN_REPO_DIRS) {
421
+ pushCandidate(seen, candidates, resolveUserPath(path3.join(home, rel)), "well_known_path");
422
+ }
423
+ return candidates;
424
+ }
425
+ function discoverDefaultRepo(opts) {
426
+ return discoverDefaultRepoCandidates(opts)[0] ?? null;
427
+ }
428
+
181
429
  // src/config.ts
182
- var CONFIG_DIR = path3.join(homedir2(), ".kynver");
183
- var CONFIG_FILE = path3.join(CONFIG_DIR, "config.json");
184
- var CREDENTIALS_FILE = path3.join(CONFIG_DIR, "credentials");
430
+ var CONFIG_DIR = path4.join(homedir3(), ".kynver");
431
+ var CONFIG_FILE = path4.join(CONFIG_DIR, "config.json");
432
+ var CREDENTIALS_FILE = path4.join(CONFIG_DIR, "credentials");
185
433
  function loadUserConfig() {
186
- if (!existsSync3(CONFIG_FILE)) return {};
434
+ if (!existsSync4(CONFIG_FILE)) return {};
187
435
  try {
188
- return JSON.parse(readFileSync3(CONFIG_FILE, "utf8"));
436
+ return JSON.parse(readFileSync4(CONFIG_FILE, "utf8"));
189
437
  } catch {
190
438
  return {};
191
439
  }
@@ -209,7 +457,8 @@ function inferSetupFields(existing, args) {
209
457
  const creds = loadCredentialsFile();
210
458
  const apiBaseUrl = (typeof args.apiBaseUrl === "string" ? args.apiBaseUrl : void 0) || existing.apiBaseUrl?.trim() || process.env.KYNVER_API_URL?.trim() || process.env.OPENCLAW_CRON_FIRE_BASE_URL?.trim();
211
459
  const agentOsId = (typeof args.agentOsId === "string" ? args.agentOsId : void 0) || existing.agentOsId?.trim() || process.env.KYNVER_AGENT_OS_ID?.trim() || (creds.runnerToken?.trim().startsWith("krc1.") ? creds.runnerTokenAgentOsId?.trim() : void 0);
212
- const defaultRepo = (typeof args.repo === "string" ? args.repo : void 0) || existing.defaultRepo?.trim() || process.env.KYNVER_DEFAULT_REPO?.trim() || process.env.KYNVER_HARNESS_REPO?.trim();
460
+ const explicitRepo = typeof args.repo === "string" ? args.repo : args.discoverRepo === true || args.discoverRepo === "true" ? discoverDefaultRepo()?.repo : void 0;
461
+ const defaultRepo = explicitRepo || existing.defaultRepo?.trim() || process.env.KYNVER_DEFAULT_REPO?.trim() || process.env.KYNVER_HARNESS_REPO?.trim() || discoverDefaultRepo()?.repo;
213
462
  const harnessRoot = (typeof args.harnessRoot === "string" ? args.harnessRoot : void 0) || existing.harnessRoot?.trim() || process.env.KYNVER_HARNESS_ROOT?.trim() || process.env.OPUS_HARNESS_ROOT?.trim();
214
463
  return {
215
464
  ...apiBaseUrl ? { apiBaseUrl: trimTrailingSlash(apiBaseUrl) } : {},
@@ -220,9 +469,9 @@ function inferSetupFields(existing, args) {
220
469
  };
221
470
  }
222
471
  function loadCredentialsFile() {
223
- if (!existsSync3(CREDENTIALS_FILE)) return {};
472
+ if (!existsSync4(CREDENTIALS_FILE)) return {};
224
473
  try {
225
- return JSON.parse(readFileSync3(CREDENTIALS_FILE, "utf8"));
474
+ return JSON.parse(readFileSync4(CREDENTIALS_FILE, "utf8"));
226
475
  } catch {
227
476
  return {};
228
477
  }
@@ -508,12 +757,12 @@ var DEFAULT_CRITICAL_FREE_BYTES = 15 * 1024 * 1024 * 1024;
508
757
  var DEFAULT_MAX_USED_PERCENT = 80;
509
758
  var DEFAULT_HARD_MAX_USED_PERCENT = 90;
510
759
  function observeRunnerDiskGate(input = {}) {
511
- const path38 = input.diskPath?.trim() || "/";
760
+ const path40 = input.diskPath?.trim() || "/";
512
761
  const warnBelowBytes = input.diskFreeWarnBytes ?? DEFAULT_WARN_FREE_BYTES;
513
762
  const criticalBelowBytes = input.diskFreeCriticalBytes ?? DEFAULT_CRITICAL_FREE_BYTES;
514
763
  const maxUsedPercent = input.diskMaxUsedPercent ?? DEFAULT_MAX_USED_PERCENT;
515
764
  const hardMaxUsedPercent = input.diskHardMaxUsedPercent ?? DEFAULT_HARD_MAX_USED_PERCENT;
516
- const stats = statfsSync(path38);
765
+ const stats = statfsSync(path40);
517
766
  const freeBytes = Number(stats.bavail) * Number(stats.bsize);
518
767
  const totalBytes = Number(stats.blocks) * Number(stats.bsize);
519
768
  const usedPercent = totalBytes > 0 ? (totalBytes - freeBytes) / totalBytes * 100 : 100;
@@ -533,7 +782,7 @@ function observeRunnerDiskGate(input = {}) {
533
782
  }
534
783
  return {
535
784
  ok,
536
- path: path38,
785
+ path: path40,
537
786
  freeBytes,
538
787
  totalBytes,
539
788
  usedPercent,
@@ -549,7 +798,7 @@ function observeRunnerDiskGate(input = {}) {
549
798
  import os2 from "node:os";
550
799
 
551
800
  // src/bounded-build/meminfo.ts
552
- import { readFileSync as readFileSync4 } from "node:fs";
801
+ import { readFileSync as readFileSync5 } from "node:fs";
553
802
  import os from "node:os";
554
803
  function readMemAvailableBytes(meminfoText) {
555
804
  if (meminfoText !== void 0) {
@@ -559,7 +808,7 @@ function readMemAvailableBytes(meminfoText) {
559
808
  }
560
809
  if (process.platform === "linux") {
561
810
  try {
562
- const meminfo = readFileSync4("/proc/meminfo", "utf8");
811
+ const meminfo = readFileSync5("/proc/meminfo", "utf8");
563
812
  const match = meminfo.match(/^MemAvailable:\s+(\d+)\s*kB/m);
564
813
  if (match) return Number(match[1]) * 1024;
565
814
  } catch {
@@ -569,37 +818,37 @@ function readMemAvailableBytes(meminfoText) {
569
818
  }
570
819
 
571
820
  // src/resource-gate.ts
572
- import path6 from "node:path";
821
+ import path7 from "node:path";
573
822
 
574
823
  // src/run-store.ts
575
- import { existsSync as existsSync5, readdirSync as readdirSync2 } from "node:fs";
576
- import path5 from "node:path";
824
+ import { existsSync as existsSync6, readdirSync as readdirSync2 } from "node:fs";
825
+ import path6 from "node:path";
577
826
 
578
827
  // src/paths.ts
579
- import { existsSync as existsSync4 } from "node:fs";
580
- import { homedir as homedir3 } from "node:os";
581
- import path4 from "node:path";
582
- var LEGACY_ROOT = path4.join(homedir3(), ".openclaw", "harness");
828
+ import { existsSync as existsSync5 } from "node:fs";
829
+ import { homedir as homedir4 } from "node:os";
830
+ import path5 from "node:path";
831
+ var LEGACY_ROOT = path5.join(homedir4(), ".openclaw", "harness");
583
832
  function resolveHarnessRoot() {
584
833
  const env = process.env.KYNVER_HARNESS_ROOT || process.env.OPUS_HARNESS_ROOT;
585
834
  if (env) return resolveUserPath(env);
586
835
  const configured = loadUserConfig().harnessRoot?.trim();
587
836
  if (configured) return resolveUserPath(configured);
588
- const kynverRoot = path4.join(homedir3(), ".kynver", "harness");
589
- if (existsSync4(kynverRoot)) return kynverRoot;
590
- if (existsSync4(LEGACY_ROOT)) return LEGACY_ROOT;
837
+ const kynverRoot = path5.join(homedir4(), ".kynver", "harness");
838
+ if (existsSync5(kynverRoot)) return kynverRoot;
839
+ if (existsSync5(LEGACY_ROOT)) return LEGACY_ROOT;
591
840
  return kynverRoot;
592
841
  }
593
842
  function getHarnessPaths() {
594
843
  const harnessRoot = resolveHarnessRoot();
595
844
  return {
596
845
  harnessRoot,
597
- runsDir: path4.join(harnessRoot, "runs"),
598
- worktreesDir: path4.join(harnessRoot, "worktrees")
846
+ runsDir: path5.join(harnessRoot, "runs"),
847
+ worktreesDir: path5.join(harnessRoot, "worktrees")
599
848
  };
600
849
  }
601
850
  function runDir(runsDir, id) {
602
- return path4.join(runsDir, safeSlug(id));
851
+ return path5.join(runsDir, safeSlug(id));
603
852
  }
604
853
 
605
854
  // src/run-store.ts
@@ -608,16 +857,16 @@ function getPaths() {
608
857
  }
609
858
  function loadRun(id) {
610
859
  const { runsDir } = getPaths();
611
- return readJson(path5.join(runDir(runsDir, safeSlug(id)), "run.json"));
860
+ return readJson(path6.join(runDir(runsDir, safeSlug(id)), "run.json"));
612
861
  }
613
862
  function listRunRecords() {
614
863
  const { runsDir } = getPaths();
615
- if (!existsSync5(runsDir)) return [];
864
+ if (!existsSync6(runsDir)) return [];
616
865
  const runs = [];
617
866
  for (const entry of readdirSync2(runsDir, { withFileTypes: true })) {
618
867
  if (!entry.isDirectory()) continue;
619
868
  const run = readJson(
620
- path5.join(runsDir, entry.name, "run.json"),
869
+ path6.join(runsDir, entry.name, "run.json"),
621
870
  void 0
622
871
  );
623
872
  if (run?.id) runs.push(run);
@@ -627,16 +876,16 @@ function listRunRecords() {
627
876
  function loadWorker(runId, name) {
628
877
  const { runsDir } = getPaths();
629
878
  return readJson(
630
- path5.join(runDir(runsDir, safeSlug(runId)), "workers", safeSlug(name), "worker.json")
879
+ path6.join(runDir(runsDir, safeSlug(runId)), "workers", safeSlug(name), "worker.json")
631
880
  );
632
881
  }
633
882
  function saveRun(run) {
634
883
  const { runsDir } = getPaths();
635
- writeJson(path5.join(runDir(runsDir, run.id), "run.json"), run);
884
+ writeJson(path6.join(runDir(runsDir, run.id), "run.json"), run);
636
885
  }
637
886
  function saveWorker(runId, worker) {
638
887
  const { runsDir } = getPaths();
639
- writeJson(path5.join(runDir(runsDir, runId), "workers", worker.name, "worker.json"), worker);
888
+ writeJson(path6.join(runDir(runsDir, runId), "workers", worker.name, "worker.json"), worker);
640
889
  }
641
890
  function runDirectory(id) {
642
891
  const { runsDir } = getPaths();
@@ -644,7 +893,7 @@ function runDirectory(id) {
644
893
  }
645
894
 
646
895
  // src/heartbeat.ts
647
- import { existsSync as existsSync6, readFileSync as readFileSync5 } from "node:fs";
896
+ import { existsSync as existsSync7, readFileSync as readFileSync6 } from "node:fs";
648
897
  var HEARTBEAT_FUTURE_SKEW_MS = 6e4;
649
898
  function isTerminalHeartbeatPhase(phase) {
650
899
  return phase === "complete";
@@ -663,10 +912,10 @@ function parseHeartbeat(file) {
663
912
  heartbeatBlocker: null,
664
913
  timestampAnomalies: []
665
914
  };
666
- if (!existsSync6(file)) return result;
915
+ if (!existsSync7(file)) return result;
667
916
  const maxFutureMs = Date.now() + HEARTBEAT_FUTURE_SKEW_MS;
668
917
  const clampedTo = new Date(maxFutureMs).toISOString();
669
- const lines = readFileSync5(file, "utf8").split("\n").filter(Boolean);
918
+ const lines = readFileSync6(file, "utf8").split("\n").filter(Boolean);
670
919
  for (const line of lines) {
671
920
  const entry = safeJson(line);
672
921
  if (!entry || typeof entry !== "object" || Array.isArray(entry)) continue;
@@ -693,7 +942,7 @@ function parseHeartbeat(file) {
693
942
  }
694
943
 
695
944
  // src/stream.ts
696
- import { existsSync as existsSync7, readFileSync as readFileSync6 } from "node:fs";
945
+ import { existsSync as existsSync8, readFileSync as readFileSync7 } from "node:fs";
697
946
 
698
947
  // src/shell-command-outcome.ts
699
948
  var NPM_AUDIT_RE = /\bnpm\s+audit\b/i;
@@ -897,8 +1146,8 @@ function parseHarnessStream(file) {
897
1146
  error: null,
898
1147
  lastShellOutcome: null
899
1148
  };
900
- if (!existsSync7(file)) return result;
901
- const lines = readFileSync6(file, "utf8").split("\n").filter(Boolean);
1149
+ if (!existsSync8(file)) return result;
1150
+ const lines = readFileSync7(file, "utf8").split("\n").filter(Boolean);
902
1151
  for (const line of lines) {
903
1152
  const event = safeJson(line);
904
1153
  if (!event) continue;
@@ -1051,239 +1300,64 @@ function classifyExitFailure(errorText) {
1051
1300
  return { blocked: true, reason: `${pattern.label}: ${tidy2(text)}` };
1052
1301
  }
1053
1302
  }
1054
- return null;
1055
- }
1056
-
1057
- // src/exited-salvage.ts
1058
- function trimOrNull(value) {
1059
- if (typeof value !== "string") return null;
1060
- const trimmed = value.trim();
1061
- return trimmed.length ? trimmed : null;
1062
- }
1063
- function hasFinalResult(value) {
1064
- if (value === void 0 || value === null) return false;
1065
- if (typeof value === "string") return value.trim().length > 0;
1066
- if (typeof value === "boolean") return value;
1067
- if (Array.isArray(value)) return value.length > 0;
1068
- if (typeof value === "object") return Object.keys(value).length > 0;
1069
- return true;
1070
- }
1071
- function committedHeadFromAncestry(ancestry) {
1072
- if (!ancestry?.checked) return null;
1073
- if (ancestry.headIsAncestorOfBase !== false) return null;
1074
- return trimOrNull(ancestry.head);
1075
- }
1076
- function buildAttentionReason(kind, uncommittedCount, headCommit) {
1077
- const parts = ["exited_with_changes_salvage"];
1078
- if (kind === "uncommitted" || kind === "both") {
1079
- parts.push(
1080
- `${uncommittedCount} uncommitted change${uncommittedCount === 1 ? "" : "s"} with no final result`
1081
- );
1082
- }
1083
- if ((kind === "committed_ahead" || kind === "both") && headCommit) {
1084
- const sha = headCommit.length > 12 ? headCommit.slice(0, 12) : headCommit;
1085
- parts.push(`commit ${sha} ahead of base with no final result`);
1086
- }
1087
- parts.push("review worktree \u2014 commit, open a PR, or run a salvage worker before discarding");
1088
- return parts.join(": ");
1089
- }
1090
- function assessExitedWorkerSalvage(input) {
1091
- if (input.alive || hasFinalResult(input.finalResult)) return null;
1092
- const uncommittedCount = (input.changedFiles ?? []).filter((line) => line.trim()).length;
1093
- const headCommit = trimOrNull(input.headCommit) ?? committedHeadFromAncestry(input.gitAncestry);
1094
- const hasUncommitted = uncommittedCount > 0;
1095
- const hasCommittedAhead = Boolean(headCommit);
1096
- if (!hasUncommitted && !hasCommittedAhead) {
1097
- return {
1098
- kind: "none",
1099
- salvageable: false,
1100
- uncommittedCount: 0,
1101
- headCommit: null,
1102
- attentionReason: "process exited without a final result"
1103
- };
1104
- }
1105
- const kind = hasUncommitted && hasCommittedAhead ? "both" : hasUncommitted ? "uncommitted" : "committed_ahead";
1106
- return {
1107
- kind,
1108
- salvageable: true,
1109
- uncommittedCount,
1110
- headCommit,
1111
- attentionReason: buildAttentionReason(kind, uncommittedCount, headCommit)
1112
- };
1113
- }
1114
-
1115
- // src/git.ts
1116
- import { spawnSync } from "node:child_process";
1117
-
1118
- // src/worker-env.ts
1119
- var FORBIDDEN_WORKER_ENV_KEYS = [
1120
- "ANTHROPIC_API_KEY",
1121
- "ANALYST_API_KEY",
1122
- "RECRUITER_API_KEY",
1123
- "AUTH_SECRET",
1124
- "NEXTAUTH_SECRET",
1125
- "DATABASE_URL",
1126
- "PRODUCTION_DATABASE_URL",
1127
- "REDIS_URL",
1128
- "GOOGLE_CLIENT_SECRET",
1129
- "GITHUB_CLIENT_SECRET",
1130
- "KYNVER_API_KEY",
1131
- "KYNVER_SERVICE_SECRET",
1132
- "KYNVER_RUNTIME_SECRET",
1133
- "OPENCLAW_CRON_SECRET",
1134
- "QSTASH_TOKEN",
1135
- "QSTASH_CURRENT_SIGNING_KEY",
1136
- "QSTASH_NEXT_SIGNING_KEY",
1137
- "TOOL_SECRETS_KEK",
1138
- "TOOL_EXECUTOR_DISPATCH_SECRET",
1139
- "CLOUDFLARE_API_TOKEN",
1140
- "STRIPE_SECRET_KEY",
1141
- "STRIPE_WEBHOOK_SECRET",
1142
- "STRIPE_IDENTITY_WEBHOOK_SECRET",
1143
- "VOYAGE_API_KEY",
1144
- "PERPLEXITY_API_KEY",
1145
- "FRED_API_KEY",
1146
- "FMP_API_KEY",
1147
- "CURSOR_API_KEY"
1148
- ];
1149
- var FORBIDDEN_KEY_SET = new Set(FORBIDDEN_WORKER_ENV_KEYS);
1150
- var FORBIDDEN_SUFFIXES = ["_SECRET", "_API_KEY"];
1151
- function isForbiddenWorkerEnvKey(key) {
1152
- if (FORBIDDEN_KEY_SET.has(key)) return true;
1153
- return FORBIDDEN_SUFFIXES.some((suffix) => key.endsWith(suffix));
1154
- }
1155
- function listForbiddenWorkerEnvKeys(env) {
1156
- return Object.keys(env).filter(isForbiddenWorkerEnvKey).sort();
1157
- }
1158
- function scrubWorkerEnv(env) {
1159
- const next = { ...env };
1160
- for (const key of Object.keys(next)) {
1161
- if (isForbiddenWorkerEnvKey(key)) delete next[key];
1162
- }
1163
- return next;
1164
- }
1165
- function auditWorkerEnv(env) {
1166
- const forbiddenPresent = listForbiddenWorkerEnvKeys(env);
1167
- return { forbiddenPresent, safe: forbiddenPresent.length === 0 };
1168
- }
1169
- function scrubClaudeEnv(env) {
1170
- return scrubWorkerEnv(env);
1171
- }
1172
-
1173
- // src/git.ts
1174
- function git(cwd, args, options = {}) {
1175
- const res = spawnSync("git", args, { cwd, encoding: "utf8" });
1176
- if (res.status !== 0 && !options.allowFailure) {
1177
- const message = `git ${args.join(" ")} failed: ${res.stderr || res.stdout}`;
1178
- if (options.throwError) throw new Error(message);
1179
- fail(message);
1180
- }
1181
- return res.stdout || "";
1182
- }
1183
- function ensureGitRepo(repo) {
1184
- git(repo, ["rev-parse", "--show-toplevel"]);
1185
- }
1186
- function gitStatusShort(worktreePath) {
1187
- return git(worktreePath, ["status", "--short"], { allowFailure: true }).split("\n").map((line) => line.trim()).filter(Boolean);
1188
- }
1189
- function gitCapture(cwd, args) {
1190
- try {
1191
- const res = spawnSync("git", args, { cwd, encoding: "utf8" });
1192
- return {
1193
- status: res.status,
1194
- stdout: res.stdout || "",
1195
- stderr: res.stderr || "",
1196
- error: res.error ? res.error.message : null
1197
- };
1198
- } catch (error) {
1199
- return {
1200
- status: null,
1201
- stdout: "",
1202
- stderr: "",
1203
- error: error.message
1204
- };
1205
- }
1303
+ return null;
1206
1304
  }
1207
- function gitIsAncestor(cwd, ancestor, descendant) {
1208
- const res = gitCapture(cwd, ["merge-base", "--is-ancestor", ancestor, descendant]);
1209
- if (res.status === 0) return { isAncestor: true, error: null };
1210
- if (res.status === 1) return { isAncestor: false, error: null };
1211
- return { isAncestor: null, error: res.error || res.stderr || res.stdout || `git exited ${res.status}` };
1305
+
1306
+ // src/exited-salvage.ts
1307
+ function trimOrNull(value) {
1308
+ if (typeof value !== "string") return null;
1309
+ const trimmed = value.trim();
1310
+ return trimmed.length ? trimmed : null;
1212
1311
  }
1213
- function computeGitAncestry(worktreePath, baseOrOptions = "origin/main") {
1214
- const options = typeof baseOrOptions === "string" ? { base: baseOrOptions } : baseOrOptions;
1215
- const baseLabel = options.baseCommit?.trim() || options.base?.trim() || "origin/main";
1216
- const pinnedBaseCommit = options.baseCommit?.trim() || null;
1217
- if (!worktreePath) {
1218
- return unknownAncestry(baseLabel, "missing worktree path");
1219
- }
1220
- const head = gitCapture(worktreePath, ["rev-parse", "HEAD"]);
1221
- if (head.status !== 0) {
1222
- return unknownAncestry(baseLabel, head.error || head.stderr || head.stdout || "failed to resolve HEAD");
1223
- }
1224
- let baseSha;
1225
- if (pinnedBaseCommit) {
1226
- baseSha = pinnedBaseCommit;
1227
- } else {
1228
- const baseHead = gitCapture(worktreePath, ["rev-parse", baseLabel]);
1229
- if (baseHead.status !== 0) {
1230
- return unknownAncestry(
1231
- baseLabel,
1232
- baseHead.error || baseHead.stderr || baseHead.stdout || `failed to resolve ${baseLabel}`,
1233
- head.stdout.trim()
1234
- );
1235
- }
1236
- baseSha = baseHead.stdout.trim();
1312
+ function hasFinalResult(value) {
1313
+ if (value === void 0 || value === null) return false;
1314
+ if (typeof value === "string") return value.trim().length > 0;
1315
+ if (typeof value === "boolean") return value;
1316
+ if (Array.isArray(value)) return value.length > 0;
1317
+ if (typeof value === "object") return Object.keys(value).length > 0;
1318
+ return true;
1319
+ }
1320
+ function committedHeadFromAncestry(ancestry) {
1321
+ if (!ancestry?.checked) return null;
1322
+ if (ancestry.headIsAncestorOfBase !== false) return null;
1323
+ return trimOrNull(ancestry.head);
1324
+ }
1325
+ function buildAttentionReason(kind, uncommittedCount, headCommit) {
1326
+ const parts = ["exited_with_changes_salvage"];
1327
+ if (kind === "uncommitted" || kind === "both") {
1328
+ parts.push(
1329
+ `${uncommittedCount} uncommitted change${uncommittedCount === 1 ? "" : "s"} with no final result`
1330
+ );
1237
1331
  }
1238
- const headSha = head.stdout.trim();
1239
- if (headSha === baseSha) {
1240
- return {
1241
- checked: true,
1242
- base: baseLabel,
1243
- head: headSha,
1244
- baseHead: baseSha,
1245
- baseIsAncestorOfHead: true,
1246
- headIsAncestorOfBase: true,
1247
- relation: "synced"
1248
- };
1332
+ if ((kind === "committed_ahead" || kind === "both") && headCommit) {
1333
+ const sha = headCommit.length > 12 ? headCommit.slice(0, 12) : headCommit;
1334
+ parts.push(`commit ${sha} ahead of base with no final result`);
1249
1335
  }
1250
- const baseIsAncestorOfHead = gitIsAncestor(worktreePath, baseSha, headSha);
1251
- const headIsAncestorOfBase = gitIsAncestor(worktreePath, headSha, baseSha);
1252
- const error = baseIsAncestorOfHead.error || headIsAncestorOfBase.error || void 0;
1253
- if (baseIsAncestorOfHead.isAncestor == null || headIsAncestorOfBase.isAncestor == null) {
1336
+ parts.push("review worktree \u2014 commit, open a PR, or run a salvage worker before discarding");
1337
+ return parts.join(": ");
1338
+ }
1339
+ function assessExitedWorkerSalvage(input) {
1340
+ if (input.alive || hasFinalResult(input.finalResult)) return null;
1341
+ const uncommittedCount = (input.changedFiles ?? []).filter((line) => line.trim()).length;
1342
+ const headCommit = trimOrNull(input.headCommit) ?? committedHeadFromAncestry(input.gitAncestry);
1343
+ const hasUncommitted = uncommittedCount > 0;
1344
+ const hasCommittedAhead = Boolean(headCommit);
1345
+ if (!hasUncommitted && !hasCommittedAhead) {
1254
1346
  return {
1255
- checked: false,
1256
- base: baseLabel,
1257
- head: headSha,
1258
- baseHead: baseSha,
1259
- baseIsAncestorOfHead: baseIsAncestorOfHead.isAncestor,
1260
- headIsAncestorOfBase: headIsAncestorOfBase.isAncestor,
1261
- relation: "unknown",
1262
- ...error ? { error } : {}
1347
+ kind: "none",
1348
+ salvageable: false,
1349
+ uncommittedCount: 0,
1350
+ headCommit: null,
1351
+ attentionReason: "process exited without a final result"
1263
1352
  };
1264
1353
  }
1265
- const relation = baseIsAncestorOfHead.isAncestor ? "ahead" : headIsAncestorOfBase.isAncestor ? "merged" : "diverged";
1266
- return {
1267
- checked: true,
1268
- base: baseLabel,
1269
- head: headSha,
1270
- baseHead: baseSha,
1271
- baseIsAncestorOfHead: baseIsAncestorOfHead.isAncestor,
1272
- headIsAncestorOfBase: headIsAncestorOfBase.isAncestor,
1273
- relation,
1274
- ...error ? { error } : {}
1275
- };
1276
- }
1277
- function unknownAncestry(base, error, head = null) {
1354
+ const kind = hasUncommitted && hasCommittedAhead ? "both" : hasUncommitted ? "uncommitted" : "committed_ahead";
1278
1355
  return {
1279
- checked: false,
1280
- base,
1281
- head,
1282
- baseHead: null,
1283
- baseIsAncestorOfHead: null,
1284
- headIsAncestorOfBase: null,
1285
- relation: "unknown",
1286
- error
1356
+ kind,
1357
+ salvageable: true,
1358
+ uncommittedCount,
1359
+ headCommit,
1360
+ attentionReason: buildAttentionReason(kind, uncommittedCount, headCommit)
1287
1361
  };
1288
1362
  }
1289
1363
 
@@ -1645,7 +1719,7 @@ function countActiveWorkersForRun(run) {
1645
1719
  let active = 0;
1646
1720
  for (const name of Object.keys(run.workers || {})) {
1647
1721
  const worker = readJson(
1648
- path6.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
1722
+ path7.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
1649
1723
  void 0
1650
1724
  );
1651
1725
  if (!worker || !isActiveHarnessWorker(worker)) continue;
@@ -2030,10 +2104,10 @@ function readHarnessRetryLimits() {
2030
2104
  }
2031
2105
 
2032
2106
  // src/lease-renewal.ts
2033
- import path7 from "node:path";
2107
+ import path8 from "node:path";
2034
2108
  function workerRecord(runId, name) {
2035
2109
  return readJson(
2036
- path7.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
2110
+ path8.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
2037
2111
  void 0
2038
2112
  );
2039
2113
  }
@@ -2099,8 +2173,8 @@ function hasLiveWorkerForTask(runId, taskId) {
2099
2173
  }
2100
2174
 
2101
2175
  // src/supervisor.ts
2102
- import { existsSync as existsSync11, mkdirSync as mkdirSync3 } from "node:fs";
2103
- import path13 from "node:path";
2176
+ import { existsSync as existsSync12, mkdirSync as mkdirSync3 } from "node:fs";
2177
+ import path14 from "node:path";
2104
2178
 
2105
2179
  // src/prompt.ts
2106
2180
  function buildPrompt(input) {
@@ -2169,13 +2243,13 @@ function buildPrompt(input) {
2169
2243
  }
2170
2244
 
2171
2245
  // src/providers/cursor.ts
2172
- import { closeSync as closeSync2, existsSync as existsSync9, openSync as openSync2 } from "node:fs";
2246
+ import { closeSync as closeSync2, existsSync as existsSync10, openSync as openSync2 } from "node:fs";
2173
2247
  import { spawn as spawn2 } from "node:child_process";
2174
- import path9 from "node:path";
2248
+ import path10 from "node:path";
2175
2249
 
2176
2250
  // src/providers/cursor-windows.ts
2177
- import { existsSync as existsSync8, readdirSync as readdirSync3 } from "node:fs";
2178
- import path8 from "node:path";
2251
+ import { existsSync as existsSync9, readdirSync as readdirSync3 } from "node:fs";
2252
+ import path9 from "node:path";
2179
2253
  var CURSOR_VERSION_DIR = /^\d{4}\.\d{1,2}\.\d{1,2}-[a-f0-9]+$/i;
2180
2254
  function parseCursorVersionSortKey(versionName) {
2181
2255
  const datePart = versionName.split("-")[0];
@@ -2186,8 +2260,8 @@ function parseCursorVersionSortKey(versionName) {
2186
2260
  return Number(`${year}${month.padStart(2, "0")}${day.padStart(2, "0")}`);
2187
2261
  }
2188
2262
  function pickLatestCursorVersionDir(agentRoot) {
2189
- const versionsRoot = path8.join(agentRoot, "versions");
2190
- if (!existsSync8(versionsRoot)) return null;
2263
+ const versionsRoot = path9.join(agentRoot, "versions");
2264
+ if (!existsSync9(versionsRoot)) return null;
2191
2265
  let bestDir = null;
2192
2266
  let bestKey = -1;
2193
2267
  for (const entry of readdirSync3(versionsRoot, { withFileTypes: true })) {
@@ -2195,22 +2269,22 @@ function pickLatestCursorVersionDir(agentRoot) {
2195
2269
  const key = parseCursorVersionSortKey(entry.name);
2196
2270
  if (key == null || key <= bestKey) continue;
2197
2271
  bestKey = key;
2198
- bestDir = path8.join(versionsRoot, entry.name);
2272
+ bestDir = path9.join(versionsRoot, entry.name);
2199
2273
  }
2200
2274
  return bestDir;
2201
2275
  }
2202
2276
  function resolveWindowsCursorBundled(agentRoot) {
2203
- const root = agentRoot?.trim() || path8.join(process.env.LOCALAPPDATA || "", "cursor-agent");
2204
- const directNode = path8.join(root, "node.exe");
2205
- const directIndex = path8.join(root, "index.js");
2206
- if (existsSync8(directNode) && existsSync8(directIndex)) {
2277
+ const root = agentRoot?.trim() || path9.join(process.env.LOCALAPPDATA || "", "cursor-agent");
2278
+ const directNode = path9.join(root, "node.exe");
2279
+ const directIndex = path9.join(root, "index.js");
2280
+ if (existsSync9(directNode) && existsSync9(directIndex)) {
2207
2281
  return { nodeExe: directNode, indexJs: directIndex, versionDir: root };
2208
2282
  }
2209
2283
  const versionDir = pickLatestCursorVersionDir(root);
2210
2284
  if (!versionDir) return null;
2211
- const nodeExe = path8.join(versionDir, "node.exe");
2212
- const indexJs = path8.join(versionDir, "index.js");
2213
- if (!existsSync8(nodeExe) || !existsSync8(indexJs)) return null;
2285
+ const nodeExe = path9.join(versionDir, "node.exe");
2286
+ const indexJs = path9.join(versionDir, "index.js");
2287
+ if (!existsSync9(nodeExe) || !existsSync9(indexJs)) return null;
2214
2288
  return { nodeExe, indexJs, versionDir };
2215
2289
  }
2216
2290
 
@@ -2228,13 +2302,13 @@ function bundledSpawnTarget(nodeExe, indexJs, versionDir) {
2228
2302
  function resolveCursorSpawn(agentBin) {
2229
2303
  if (process.platform === "win32") {
2230
2304
  const isCursorWrapper = /\.(cmd|bat)$/i.test(agentBin);
2231
- const isBundledNode = /node\.exe$/i.test(agentBin) && existsSync9(path9.join(path9.dirname(agentBin), "index.js"));
2305
+ const isBundledNode = /node\.exe$/i.test(agentBin) && existsSync10(path10.join(path10.dirname(agentBin), "index.js"));
2232
2306
  const isDefaultShim = agentBin === "agent";
2233
2307
  if (isCursorWrapper || isBundledNode || isDefaultShim) {
2234
- const bundled = isCursorWrapper ? resolveWindowsCursorBundled(path9.dirname(agentBin)) : isBundledNode ? {
2308
+ const bundled = isCursorWrapper ? resolveWindowsCursorBundled(path10.dirname(agentBin)) : isBundledNode ? {
2235
2309
  nodeExe: agentBin,
2236
- indexJs: path9.join(path9.dirname(agentBin), "index.js"),
2237
- versionDir: path9.dirname(agentBin)
2310
+ indexJs: path10.join(path10.dirname(agentBin), "index.js"),
2311
+ versionDir: path10.dirname(agentBin)
2238
2312
  } : resolveWindowsCursorBundled();
2239
2313
  if (bundled) {
2240
2314
  return bundledSpawnTarget(bundled.nodeExe, bundled.indexJs, bundled.versionDir);
@@ -2254,8 +2328,8 @@ function resolveAgentBin() {
2254
2328
  process.env.KYNVER_CURSOR_AGENT_ROOT?.trim() || void 0
2255
2329
  );
2256
2330
  if (bundled) return bundled.nodeExe;
2257
- const localAgent = path9.join(process.env.LOCALAPPDATA || "", "cursor-agent", "agent.cmd");
2258
- if (existsSync9(localAgent)) return localAgent;
2331
+ const localAgent = path10.join(process.env.LOCALAPPDATA || "", "cursor-agent", "agent.cmd");
2332
+ if (existsSync10(localAgent)) return localAgent;
2259
2333
  }
2260
2334
  return "agent";
2261
2335
  }
@@ -2264,7 +2338,7 @@ function cursorWorkerEnv(agentBin, spawnTarget) {
2264
2338
  ...process.env,
2265
2339
  CI: "1",
2266
2340
  NO_COLOR: "1",
2267
- ...spawnTarget.bundledVersionDir ? { CURSOR_INVOKED_AS: path9.basename(agentBin) || "agent.cmd" } : {}
2341
+ ...spawnTarget.bundledVersionDir ? { CURSOR_INVOKED_AS: path10.basename(agentBin) || "agent.cmd" } : {}
2268
2342
  });
2269
2343
  }
2270
2344
  var cursorProvider = {
@@ -2338,9 +2412,9 @@ function resolveWorkerProvider(name) {
2338
2412
 
2339
2413
  // src/auto-complete.ts
2340
2414
  import { spawn as spawn3 } from "node:child_process";
2341
- import { existsSync as existsSync10, openSync as openSync3, closeSync as closeSync3 } from "node:fs";
2342
- import path12 from "node:path";
2343
- import { fileURLToPath as fileURLToPath2 } from "node:url";
2415
+ import { existsSync as existsSync11, openSync as openSync3, closeSync as closeSync3 } from "node:fs";
2416
+ import path13 from "node:path";
2417
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
2344
2418
 
2345
2419
  // src/completion-ack.ts
2346
2420
  function hasCompletionAck(worker) {
@@ -2356,7 +2430,7 @@ function persistCompletionAck(worker, runId, fields) {
2356
2430
  }
2357
2431
 
2358
2432
  // src/worker-ops.ts
2359
- import path11 from "node:path";
2433
+ import path12 from "node:path";
2360
2434
 
2361
2435
  // src/completion-response.ts
2362
2436
  function asRecord(value) {
@@ -2838,7 +2912,7 @@ function ensurePrReadyHandoff(input, exec = defaultPrHandoffExec) {
2838
2912
  }
2839
2913
 
2840
2914
  // src/worker-lifecycle.ts
2841
- import path10 from "node:path";
2915
+ import path11 from "node:path";
2842
2916
  var TASK_LEFT_RUNNING = /* @__PURE__ */ new Set([
2843
2917
  "awaiting_review",
2844
2918
  "blocked",
@@ -2894,7 +2968,7 @@ function syncCompletionAcknowledgedFromOperatorTick(runId, operatorTick) {
2894
2968
  const synced = [];
2895
2969
  for (const name of Object.keys(run.workers || {})) {
2896
2970
  const worker = readJson(
2897
- path10.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
2971
+ path11.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
2898
2972
  void 0
2899
2973
  );
2900
2974
  if (!worker?.taskId || isCompletionAcknowledged(worker)) continue;
@@ -3152,7 +3226,7 @@ function workerStatus(args) {
3152
3226
  const worker = loadWorker(String(args.run), String(args.name));
3153
3227
  const run = loadRun(worker.runId);
3154
3228
  const status = computeWorkerStatus(worker, workerStatusOptions(run));
3155
- writeJson(path11.join(worker.workerDir, "last-status.json"), status);
3229
+ writeJson(path12.join(worker.workerDir, "last-status.json"), status);
3156
3230
  console.log(JSON.stringify(status, null, 2));
3157
3231
  }
3158
3232
  function buildRunBoard(runId) {
@@ -3160,7 +3234,7 @@ function buildRunBoard(runId) {
3160
3234
  const names = Object.keys(run.workers || {});
3161
3235
  const workers = names.map((name) => {
3162
3236
  const worker = readJson(
3163
- path11.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
3237
+ path12.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
3164
3238
  void 0
3165
3239
  );
3166
3240
  if (!worker) {
@@ -3271,7 +3345,7 @@ function buildRunBoard(runId) {
3271
3345
  needsAttention: workers.filter((w) => w.attention && w.attention !== "ok" && w.attention !== "done").map((w) => w.worker),
3272
3346
  workers
3273
3347
  };
3274
- writeJson(path11.join(runDirectory(run.id), "last-board.json"), board);
3348
+ writeJson(path12.join(runDirectory(run.id), "last-board.json"), board);
3275
3349
  return board;
3276
3350
  }
3277
3351
  async function publishHarnessBoardSnapshot(args, source) {
@@ -3460,12 +3534,12 @@ async function autoCompleteWorkerCli(raw) {
3460
3534
  }
3461
3535
  }
3462
3536
  function resolveDefaultCliPath() {
3463
- return path12.join(fileURLToPath2(new URL(".", import.meta.url)), "cli.js");
3537
+ return path13.join(fileURLToPath3(new URL(".", import.meta.url)), "cli.js");
3464
3538
  }
3465
3539
  function spawnCompletionSidecar(opts) {
3466
3540
  const cliPath = opts.cliPath ?? resolveDefaultCliPath();
3467
- if (!existsSync10(cliPath)) return void 0;
3468
- const logPath = path12.join(opts.workerDir, "auto-complete.log");
3541
+ if (!existsSync11(cliPath)) return void 0;
3542
+ const logPath = path13.join(opts.workerDir, "auto-complete.log");
3469
3543
  let logFd;
3470
3544
  try {
3471
3545
  logFd = openSync3(logPath, "a");
@@ -3547,16 +3621,16 @@ function spawnWorkerProcess(run, opts) {
3547
3621
  launchModel = preflight.model;
3548
3622
  }
3549
3623
  const { worktreesDir } = getPaths();
3550
- const workerDir = path13.join(runDirectory(run.id), "workers", name);
3624
+ const workerDir = path14.join(runDirectory(run.id), "workers", name);
3551
3625
  mkdirSync3(workerDir, { recursive: true });
3552
- const worktreePath = path13.join(worktreesDir, run.id, name);
3626
+ const worktreePath = path14.join(worktreesDir, run.id, name);
3553
3627
  const branch = opts.branch || `agent/${run.id}/${name}`;
3554
- if (existsSync11(worktreePath)) throw new Error(`worktree path already exists: ${worktreePath}`);
3628
+ if (existsSync12(worktreePath)) throw new Error(`worktree path already exists: ${worktreePath}`);
3555
3629
  git(run.repo, ["fetch", "origin", "--prune"], { allowFailure: true });
3556
3630
  git(run.repo, ["worktree", "add", "-b", branch, worktreePath, run.baseCommit], { throwError: true });
3557
- const stdoutPath = path13.join(workerDir, "stdout.jsonl");
3558
- const stderrPath = path13.join(workerDir, "stderr.log");
3559
- const heartbeatPath = path13.join(workerDir, "heartbeat.jsonl");
3631
+ const stdoutPath = path14.join(workerDir, "stdout.jsonl");
3632
+ const stderrPath = path14.join(workerDir, "stderr.log");
3633
+ const heartbeatPath = path14.join(workerDir, "heartbeat.jsonl");
3560
3634
  const prompt = buildPrompt({
3561
3635
  task: opts.task,
3562
3636
  ownedPaths: opts.ownedPaths || [],
@@ -3621,7 +3695,7 @@ function spawnWorkerProcess(run, opts) {
3621
3695
  startedAt: (/* @__PURE__ */ new Date()).toISOString()
3622
3696
  };
3623
3697
  saveWorker(run.id, worker);
3624
- run.workers = { ...run.workers || {}, [name]: { workerDir, statusPath: path13.join(workerDir, "worker.json") } };
3698
+ run.workers = { ...run.workers || {}, [name]: { workerDir, statusPath: path14.join(workerDir, "worker.json") } };
3625
3699
  run.status = "running";
3626
3700
  saveRun(run);
3627
3701
  if (worker.agentOsId && worker.taskId) {
@@ -3913,18 +3987,18 @@ function buildPlanPersistIdempotencyKey(input) {
3913
3987
 
3914
3988
  // src/plan-persist/paths.ts
3915
3989
  import { mkdirSync as mkdirSync4 } from "node:fs";
3916
- import { homedir as homedir4 } from "node:os";
3917
- import path14 from "node:path";
3990
+ import { homedir as homedir5 } from "node:os";
3991
+ import path15 from "node:path";
3918
3992
  function resolveKynverStateRoot() {
3919
3993
  const env = process.env.KYNVER_STATE_ROOT;
3920
- if (env) return path14.resolve(env);
3921
- return path14.join(homedir4(), ".kynver", "state");
3994
+ if (env) return path15.resolve(env);
3995
+ return path15.join(homedir5(), ".kynver", "state");
3922
3996
  }
3923
3997
  function planOutboxDir() {
3924
- return path14.join(resolveKynverStateRoot(), "plan-outbox");
3998
+ return path15.join(resolveKynverStateRoot(), "plan-outbox");
3925
3999
  }
3926
4000
  function planOutboxArchiveDir() {
3927
- return path14.join(resolveKynverStateRoot(), "plan-outbox-archive");
4001
+ return path15.join(resolveKynverStateRoot(), "plan-outbox-archive");
3928
4002
  }
3929
4003
  function ensurePlanOutboxDirs() {
3930
4004
  const outboxDir = planOutboxDir();
@@ -3935,20 +4009,20 @@ function ensurePlanOutboxDirs() {
3935
4009
  }
3936
4010
  function isTmpOnlyPath(filePath) {
3937
4011
  if (filePath.startsWith("/tmp/") || filePath.startsWith("/var/folders/")) return true;
3938
- const resolved = path14.resolve(filePath);
3939
- return resolved.startsWith("/tmp/") || resolved.startsWith(path14.join("/var", "folders"));
4012
+ const resolved = path15.resolve(filePath);
4013
+ return resolved.startsWith("/tmp/") || resolved.startsWith(path15.join("/var", "folders"));
3940
4014
  }
3941
4015
 
3942
4016
  // src/plan-persist/outbox-store.ts
3943
4017
  import {
3944
- existsSync as existsSync13,
3945
- readFileSync as readFileSync7,
4018
+ existsSync as existsSync14,
4019
+ readFileSync as readFileSync8,
3946
4020
  renameSync,
3947
4021
  readdirSync as readdirSync4,
3948
4022
  writeFileSync as writeFileSync3,
3949
4023
  unlinkSync
3950
4024
  } from "node:fs";
3951
- import path15 from "node:path";
4025
+ import path16 from "node:path";
3952
4026
  import { randomUUID } from "node:crypto";
3953
4027
  var DEFAULT_MAX_RETRIES = 12;
3954
4028
  function listOutboxItems() {
@@ -3956,7 +4030,7 @@ function listOutboxItems() {
3956
4030
  const files = readdirSync4(outboxDir).filter((f) => f.endsWith(".json"));
3957
4031
  const items = [];
3958
4032
  for (const file of files) {
3959
- const item = readOutboxItem(path15.join(outboxDir, file));
4033
+ const item = readOutboxItem(path16.join(outboxDir, file));
3960
4034
  if (item && item.queueStatus === "queued") items.push(item);
3961
4035
  }
3962
4036
  return items.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
@@ -3968,25 +4042,25 @@ function findOutboxByIdempotencyKey(key) {
3968
4042
  return null;
3969
4043
  }
3970
4044
  function readOutboxItem(jsonPath) {
3971
- if (!existsSync13(jsonPath)) return null;
4045
+ if (!existsSync14(jsonPath)) return null;
3972
4046
  try {
3973
- return JSON.parse(readFileSync7(jsonPath, "utf8"));
4047
+ return JSON.parse(readFileSync8(jsonPath, "utf8"));
3974
4048
  } catch {
3975
4049
  return null;
3976
4050
  }
3977
4051
  }
3978
4052
  function readOutboxBody(item) {
3979
4053
  const { outboxDir } = ensurePlanOutboxDirs();
3980
- const bodyFile = path15.join(outboxDir, item.bodyPath);
3981
- return readFileSync7(bodyFile, "utf8");
4054
+ const bodyFile = path16.join(outboxDir, item.bodyPath);
4055
+ return readFileSync8(bodyFile, "utf8");
3982
4056
  }
3983
4057
  function writeOutboxItem(input, opts) {
3984
4058
  const { outboxDir } = ensurePlanOutboxDirs();
3985
4059
  const now = (/* @__PURE__ */ new Date()).toISOString();
3986
4060
  const id = opts.existing?.id ?? randomUUID();
3987
4061
  const bodyPath = opts.existing?.bodyPath ?? `${id}.body.md`;
3988
- const jsonPath = path15.join(outboxDir, `${id}.json`);
3989
- const bodyFile = path15.join(outboxDir, bodyPath);
4062
+ const jsonPath = path16.join(outboxDir, `${id}.json`);
4063
+ const bodyFile = path16.join(outboxDir, bodyPath);
3990
4064
  if (!opts.existing) {
3991
4065
  writeFileSync3(bodyFile, input.body, "utf8");
3992
4066
  }
@@ -4022,24 +4096,24 @@ function writeOutboxItem(input, opts) {
4022
4096
  }
4023
4097
  function saveOutboxItem(item) {
4024
4098
  const { outboxDir } = ensurePlanOutboxDirs();
4025
- const jsonPath = path15.join(outboxDir, `${item.id}.json`);
4099
+ const jsonPath = path16.join(outboxDir, `${item.id}.json`);
4026
4100
  writeFileSync3(jsonPath, `${JSON.stringify(item, null, 2)}
4027
4101
  `, { mode: 384 });
4028
4102
  }
4029
4103
  function archiveOutboxItem(item) {
4030
4104
  const { outboxDir, archiveDir } = ensurePlanOutboxDirs();
4031
- const jsonSrc = path15.join(outboxDir, `${item.id}.json`);
4032
- const bodySrc = path15.join(outboxDir, item.bodyPath);
4033
- const jsonDst = path15.join(archiveDir, `${item.id}.json`);
4034
- const bodyDst = path15.join(archiveDir, item.bodyPath);
4035
- if (existsSync13(jsonSrc)) renameSync(jsonSrc, jsonDst);
4036
- if (existsSync13(bodySrc)) renameSync(bodySrc, bodyDst);
4105
+ const jsonSrc = path16.join(outboxDir, `${item.id}.json`);
4106
+ const bodySrc = path16.join(outboxDir, item.bodyPath);
4107
+ const jsonDst = path16.join(archiveDir, `${item.id}.json`);
4108
+ const bodyDst = path16.join(archiveDir, item.bodyPath);
4109
+ if (existsSync14(jsonSrc)) renameSync(jsonSrc, jsonDst);
4110
+ if (existsSync14(bodySrc)) renameSync(bodySrc, bodyDst);
4037
4111
  }
4038
4112
  function outboxItemPaths(item) {
4039
4113
  const { outboxDir } = ensurePlanOutboxDirs();
4040
4114
  return {
4041
- jsonPath: path15.join(outboxDir, `${item.id}.json`),
4042
- bodyPath: path15.join(outboxDir, item.bodyPath)
4115
+ jsonPath: path16.join(outboxDir, `${item.id}.json`),
4116
+ bodyPath: path16.join(outboxDir, item.bodyPath)
4043
4117
  };
4044
4118
  }
4045
4119
  function outboxInputFromItem(item, body) {
@@ -4225,7 +4299,7 @@ function markOutboxFailed(item, message) {
4225
4299
  }
4226
4300
 
4227
4301
  // src/plan-persist/drain.ts
4228
- import path16 from "node:path";
4302
+ import path17 from "node:path";
4229
4303
  async function drainPlanOutbox(opts = {}, deps = {}) {
4230
4304
  const items = listOutboxItems().filter(
4231
4305
  (item) => opts.outboxId ? item.id === opts.outboxId : true
@@ -4259,7 +4333,7 @@ async function drainPlanOutbox(opts = {}, deps = {}) {
4259
4333
  return result;
4260
4334
  }
4261
4335
  function loadOutboxById(outboxId) {
4262
- const jsonPath = path16.join(planOutboxDir(), `${outboxId}.json`);
4336
+ const jsonPath = path17.join(planOutboxDir(), `${outboxId}.json`);
4263
4337
  return readOutboxItem(jsonPath);
4264
4338
  }
4265
4339
 
@@ -4359,7 +4433,7 @@ async function dispatchRun(args) {
4359
4433
  const activeHarnessWorkers = [];
4360
4434
  for (const name of Object.keys(run.workers || {})) {
4361
4435
  const worker = readJson(
4362
- path17.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
4436
+ path18.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
4363
4437
  void 0
4364
4438
  );
4365
4439
  if (!worker?.taskId || !isPidAlive(worker.pid)) continue;
@@ -4584,7 +4658,7 @@ function redactHarness(text, secret) {
4584
4658
  }
4585
4659
 
4586
4660
  // src/validate.ts
4587
- import path18 from "node:path";
4661
+ import path19 from "node:path";
4588
4662
  var RUN_ID_RE = /^[a-z0-9][a-z0-9._-]{0,127}$/i;
4589
4663
  var WORKER_NAME_RE = /^[a-z0-9][a-z0-9._-]{0,63}$/i;
4590
4664
  function validateRunId(runId) {
@@ -4598,15 +4672,15 @@ function validateWorkerName(name) {
4598
4672
  return trimmed;
4599
4673
  }
4600
4674
  function validateRepo(repo) {
4601
- const resolved = path18.resolve(repo);
4675
+ const resolved = path19.resolve(repo);
4602
4676
  if (resolved.includes("..")) throw new Error("repo path must not contain .. segments");
4603
4677
  return resolved;
4604
4678
  }
4605
4679
  function validateOwnedPaths(repoRoot, ownedPaths) {
4606
4680
  return ownedPaths.map((owned) => {
4607
- const resolved = path18.resolve(repoRoot, owned);
4608
- const rel = path18.relative(repoRoot, resolved);
4609
- if (rel.startsWith("..") || path18.isAbsolute(rel)) {
4681
+ const resolved = path19.resolve(repoRoot, owned);
4682
+ const rel = path19.relative(repoRoot, resolved);
4683
+ if (rel.startsWith("..") || path19.isAbsolute(rel)) {
4610
4684
  throw new Error(`owned path escapes repo: ${owned}`);
4611
4685
  }
4612
4686
  return resolved;
@@ -4618,14 +4692,93 @@ function validateTailLines(lines) {
4618
4692
  }
4619
4693
 
4620
4694
  // src/worktree.ts
4621
- import { existsSync as existsSync14, mkdirSync as mkdirSync5 } from "node:fs";
4622
- import path19 from "node:path";
4695
+ import { existsSync as existsSync15, mkdirSync as mkdirSync5 } from "node:fs";
4696
+ import path21 from "node:path";
4697
+
4698
+ // src/default-repo.ts
4699
+ import path20 from "node:path";
4700
+ function expandConfiguredRepo(value) {
4701
+ return path20.resolve(resolveUserPath(value.trim()));
4702
+ }
4703
+ function fromConfigured(value, source, persistedInConfig) {
4704
+ const trimmed = value?.trim();
4705
+ if (!trimmed) return null;
4706
+ return {
4707
+ repo: expandConfiguredRepo(trimmed),
4708
+ source,
4709
+ persistedInConfig
4710
+ };
4711
+ }
4712
+ function resolveDefaultRepo(opts = {}) {
4713
+ const env = opts.env ?? process.env;
4714
+ const config = opts.config ?? loadUserConfig();
4715
+ const fromConfig = fromConfigured(config.defaultRepo, "config", true);
4716
+ if (fromConfig) return fromConfig;
4717
+ const fromDefaultEnv = fromConfigured(env.KYNVER_DEFAULT_REPO, "env_default_repo", false);
4718
+ if (fromDefaultEnv) return fromDefaultEnv;
4719
+ const fromHarnessEnv = fromConfigured(env.KYNVER_HARNESS_REPO, "env_harness_repo", false);
4720
+ if (fromHarnessEnv) return fromHarnessEnv;
4721
+ const discovered = discoverDefaultRepo({
4722
+ cwd: opts.cwd,
4723
+ runtimeModuleUrl: opts.runtimeModuleUrl
4724
+ });
4725
+ if (!discovered) return null;
4726
+ return {
4727
+ repo: discovered.repo,
4728
+ source: discovered.source,
4729
+ persistedInConfig: false
4730
+ };
4731
+ }
4732
+ function persistDefaultRepo(repo, existing) {
4733
+ const config = {
4734
+ ...existing ?? loadUserConfig(),
4735
+ defaultRepo: redactHomePath(path20.resolve(repo))
4736
+ };
4737
+ saveUserConfig(config);
4738
+ return config;
4739
+ }
4740
+ function remediateDefaultRepo(opts) {
4741
+ const existing = opts?.config ?? loadUserConfig();
4742
+ const resolved = resolveDefaultRepo({ ...opts, config: existing });
4743
+ if (!resolved) {
4744
+ return {
4745
+ ok: false,
4746
+ reason: "No Kynver git checkout found. Clone the repo, cd into it, then run `kynver setup --repo /path/to/Kynver` (or export KYNVER_DEFAULT_REPO)."
4747
+ };
4748
+ }
4749
+ if (resolved.persistedInConfig) {
4750
+ return { ok: true, resolved, config: existing };
4751
+ }
4752
+ const config = persistDefaultRepo(resolved.repo, existing);
4753
+ return {
4754
+ ok: true,
4755
+ resolved: { ...resolved, persistedInConfig: true, source: "config" },
4756
+ config
4757
+ };
4758
+ }
4759
+ function formatResolvedDefaultRepo(resolved) {
4760
+ return {
4761
+ defaultRepo: displayUserPath(resolved.repo),
4762
+ source: resolved.source,
4763
+ persistedInConfig: resolved.persistedInConfig
4764
+ };
4765
+ }
4766
+
4767
+ // src/worktree.ts
4768
+ function resolveCreateRunRepo(args) {
4769
+ const explicit = typeof args.repo === "string" ? args.repo.trim() : "";
4770
+ if (explicit) return explicit;
4771
+ const resolved = resolveDefaultRepo();
4772
+ if (resolved) return resolved.repo;
4773
+ required("", "--repo (or set defaultRepo via `kynver setup` / KYNVER_DEFAULT_REPO)");
4774
+ return "";
4775
+ }
4623
4776
  function createRun(args) {
4624
- const repo = validateRepo(required(String(args.repo || ""), "--repo"));
4777
+ const repo = validateRepo(resolveCreateRunRepo(args));
4625
4778
  ensureGitRepo(repo);
4626
4779
  const id = args.id ? validateRunId(String(args.id)) : timestampSlug(String(args.name || "run"));
4627
4780
  const dir = runDirectory(id);
4628
- if (existsSync14(dir)) failExists(`run already exists: ${id}`);
4781
+ if (existsSync15(dir)) failExists(`run already exists: ${id}`);
4629
4782
  mkdirSync5(dir, { recursive: true });
4630
4783
  const base = String(args.base || "origin/main");
4631
4784
  const baseCommit = git(repo, ["rev-parse", base]).trim();
@@ -4639,12 +4792,12 @@ function createRun(args) {
4639
4792
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
4640
4793
  workers: {}
4641
4794
  };
4642
- writeJson(path19.join(dir, "run.json"), run);
4795
+ writeJson(path21.join(dir, "run.json"), run);
4643
4796
  console.log(JSON.stringify({ runId: id, runDir: dir, repo, base, baseCommit }, null, 2));
4644
4797
  }
4645
4798
  function listRuns() {
4646
4799
  const { runsDir } = getPaths();
4647
- const rows = listRunIds(runsDir).map((id) => readJson(path19.join(runDirectory(id), "run.json"), void 0)).filter(Boolean).map((run) => ({
4800
+ const rows = listRunIds(runsDir).map((id) => readJson(path21.join(runDirectory(id), "run.json"), void 0)).filter(Boolean).map((run) => ({
4648
4801
  id: run.id,
4649
4802
  name: run.name,
4650
4803
  status: run.status,
@@ -4659,7 +4812,7 @@ function failExists(message) {
4659
4812
  }
4660
4813
 
4661
4814
  // src/sweep.ts
4662
- import path20 from "node:path";
4815
+ import path22 from "node:path";
4663
4816
  async function sweepRun(args) {
4664
4817
  const pipeline = args.pipeline === true || args.pipeline === "true";
4665
4818
  try {
@@ -4672,7 +4825,7 @@ async function sweepRun(args) {
4672
4825
  const releasedLocalOrphans = [];
4673
4826
  for (const name of Object.keys(run.workers || {})) {
4674
4827
  const worker = readJson(
4675
- path20.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
4828
+ path22.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
4676
4829
  void 0
4677
4830
  );
4678
4831
  if (!worker || !worker.dispatched || !worker.taskId) continue;
@@ -4716,10 +4869,10 @@ async function sweepRun(args) {
4716
4869
 
4717
4870
  // src/cli.ts
4718
4871
  import { mkdirSync as mkdirSync7, realpathSync } from "node:fs";
4719
- import { fileURLToPath as fileURLToPath4 } from "node:url";
4872
+ import { fileURLToPath as fileURLToPath5 } from "node:url";
4720
4873
 
4721
4874
  // src/pipeline-tick.ts
4722
- import path30 from "node:path";
4875
+ import path32 from "node:path";
4723
4876
 
4724
4877
  // src/pipeline-dispatch.ts
4725
4878
  var RESERVED_REVIEW_STARTS = 1;
@@ -4773,10 +4926,10 @@ async function runPipelineDispatch(args, slots) {
4773
4926
  }
4774
4927
 
4775
4928
  // src/stale-reconcile.ts
4776
- import path22 from "node:path";
4929
+ import path24 from "node:path";
4777
4930
 
4778
4931
  // src/finalize.ts
4779
- import path21 from "node:path";
4932
+ import path23 from "node:path";
4780
4933
  var ACTIVE_RUN_STATUSES = /* @__PURE__ */ new Set(["running", "dispatching", "pending", "queued"]);
4781
4934
  function terminalStatusFor(run) {
4782
4935
  const names = Object.keys(run.workers || {});
@@ -4787,7 +4940,7 @@ function terminalStatusFor(run) {
4787
4940
  let anyLandingBlocked = false;
4788
4941
  for (const name of names) {
4789
4942
  const worker = readJson(
4790
- path21.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
4943
+ path23.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
4791
4944
  void 0
4792
4945
  );
4793
4946
  if (!worker) continue;
@@ -4839,7 +4992,7 @@ function reconcileStaleWorkers() {
4839
4992
  const now = Date.now();
4840
4993
  for (const run of listRunRecords()) {
4841
4994
  for (const name of Object.keys(run.workers || {})) {
4842
- const workerPath = path22.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
4995
+ const workerPath = path24.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
4843
4996
  const worker = readJson(workerPath, void 0);
4844
4997
  if (!worker || worker.status !== "running") {
4845
4998
  outcomes.push({
@@ -4933,7 +5086,7 @@ function reconcileRunsCli() {
4933
5086
  }
4934
5087
 
4935
5088
  // src/plan-progress-daemon-sync.ts
4936
- import path23 from "node:path";
5089
+ import path25 from "node:path";
4937
5090
 
4938
5091
  // src/plan-progress-sync.ts
4939
5092
  async function syncPlanProgress(args) {
@@ -4957,7 +5110,7 @@ async function syncActiveWorkerPlanProgress(runId, args) {
4957
5110
  const outcomes = [];
4958
5111
  for (const name of Object.keys(run.workers || {})) {
4959
5112
  const worker = readJson(
4960
- path23.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
5113
+ path25.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
4961
5114
  void 0
4962
5115
  );
4963
5116
  if (!worker?.dispatched || !worker.taskId) continue;
@@ -5006,7 +5159,7 @@ async function fetchWorkspaceRuntimePreferences(agentOsId, args) {
5006
5159
  }
5007
5160
 
5008
5161
  // src/cleanup.ts
5009
- import path28 from "node:path";
5162
+ import path30 from "node:path";
5010
5163
 
5011
5164
  // src/cleanup-types.ts
5012
5165
  var DEFAULT_NODE_MODULES_AGE_MS = 6 * 60 * 60 * 1e3;
@@ -5077,14 +5230,14 @@ function skipNodeModulesRemoval(input) {
5077
5230
  }
5078
5231
 
5079
5232
  // src/cleanup-execute.ts
5080
- import { existsSync as existsSync16, rmSync } from "node:fs";
5081
- import path25 from "node:path";
5233
+ import { existsSync as existsSync17, rmSync } from "node:fs";
5234
+ import path27 from "node:path";
5082
5235
 
5083
5236
  // src/cleanup-dir-size.ts
5084
- import { existsSync as existsSync15, readdirSync as readdirSync5, statSync as statSync2 } from "node:fs";
5085
- import path24 from "node:path";
5237
+ import { existsSync as existsSync16, readdirSync as readdirSync5, statSync as statSync2 } from "node:fs";
5238
+ import path26 from "node:path";
5086
5239
  function directorySizeBytes(root, maxEntries = 5e4) {
5087
- if (!existsSync15(root)) return 0;
5240
+ if (!existsSync16(root)) return 0;
5088
5241
  let total = 0;
5089
5242
  let seen = 0;
5090
5243
  const stack = [root];
@@ -5098,7 +5251,7 @@ function directorySizeBytes(root, maxEntries = 5e4) {
5098
5251
  }
5099
5252
  for (const name of entries) {
5100
5253
  if (seen++ > maxEntries) return null;
5101
- const full = path24.join(current, name);
5254
+ const full = path26.join(current, name);
5102
5255
  let st;
5103
5256
  try {
5104
5257
  st = statSync2(full);
@@ -5114,7 +5267,7 @@ function directorySizeBytes(root, maxEntries = 5e4) {
5114
5267
 
5115
5268
  // src/cleanup-execute.ts
5116
5269
  function removeNodeModules(candidate, execute) {
5117
- if (!existsSync16(candidate.path)) {
5270
+ if (!existsSync17(candidate.path)) {
5118
5271
  return {
5119
5272
  ...candidate,
5120
5273
  executed: false,
@@ -5145,7 +5298,7 @@ function removeNodeModules(candidate, execute) {
5145
5298
  }
5146
5299
  }
5147
5300
  function removeWorktree(candidate, execute) {
5148
- if (!existsSync16(candidate.path)) {
5301
+ if (!existsSync17(candidate.path)) {
5149
5302
  return {
5150
5303
  ...candidate,
5151
5304
  executed: false,
@@ -5162,7 +5315,7 @@ function removeWorktree(candidate, execute) {
5162
5315
  if (repo) {
5163
5316
  git(repo, ["worktree", "remove", "--force", candidate.path], { allowFailure: true });
5164
5317
  }
5165
- if (existsSync16(candidate.path)) {
5318
+ if (existsSync17(candidate.path)) {
5166
5319
  rmSync(candidate.path, { recursive: true, force: true });
5167
5320
  }
5168
5321
  return {
@@ -5182,20 +5335,20 @@ function removeWorktree(candidate, execute) {
5182
5335
  }
5183
5336
  }
5184
5337
  function isHarnessNodeModulesPath(targetPath, harnessRoot, worktreesDir) {
5185
- const resolved = path25.resolve(targetPath);
5186
- const nm = resolved.endsWith(`${path25.sep}node_modules`) ? resolved : null;
5338
+ const resolved = path27.resolve(targetPath);
5339
+ const nm = resolved.endsWith(`${path27.sep}node_modules`) ? resolved : null;
5187
5340
  if (!nm) return "path_outside_harness";
5188
- const rel = path25.relative(worktreesDir, nm);
5189
- if (rel.startsWith("..") || path25.isAbsolute(rel)) return "path_outside_harness";
5190
- const parts = rel.split(path25.sep);
5341
+ const rel = path27.relative(worktreesDir, nm);
5342
+ if (rel.startsWith("..") || path27.isAbsolute(rel)) return "path_outside_harness";
5343
+ const parts = rel.split(path27.sep);
5191
5344
  if (parts.length < 3 || parts[parts.length - 1] !== "node_modules") return "path_outside_harness";
5192
- if (!resolved.startsWith(path25.resolve(harnessRoot))) return "path_outside_harness";
5345
+ if (!resolved.startsWith(path27.resolve(harnessRoot))) return "path_outside_harness";
5193
5346
  return null;
5194
5347
  }
5195
5348
 
5196
5349
  // src/cleanup-scan.ts
5197
- import { existsSync as existsSync17, readdirSync as readdirSync6, statSync as statSync3 } from "node:fs";
5198
- import path26 from "node:path";
5350
+ import { existsSync as existsSync18, readdirSync as readdirSync6, statSync as statSync3 } from "node:fs";
5351
+ import path28 from "node:path";
5199
5352
  function pathAgeMs(target, now) {
5200
5353
  try {
5201
5354
  const mtime = statSync3(target).mtimeMs;
@@ -5205,17 +5358,17 @@ function pathAgeMs(target, now) {
5205
5358
  }
5206
5359
  }
5207
5360
  function isPathInside(child, parent) {
5208
- const rel = path26.relative(parent, child);
5209
- return rel === "" || !rel.startsWith("..") && !path26.isAbsolute(rel);
5361
+ const rel = path28.relative(parent, child);
5362
+ return rel === "" || !rel.startsWith("..") && !path28.isAbsolute(rel);
5210
5363
  }
5211
5364
  function scanNodeModulesCandidates(opts) {
5212
5365
  const candidates = [];
5213
5366
  const seen = /* @__PURE__ */ new Set();
5214
5367
  for (const entry of opts.index.values()) {
5215
5368
  if (opts.runIdFilter && entry.runId !== opts.runIdFilter) continue;
5216
- const nm = path26.join(entry.worktreePath, "node_modules");
5217
- if (!existsSync17(nm)) continue;
5218
- const resolved = path26.resolve(nm);
5369
+ const nm = path28.join(entry.worktreePath, "node_modules");
5370
+ if (!existsSync18(nm)) continue;
5371
+ const resolved = path28.resolve(nm);
5219
5372
  if (seen.has(resolved)) continue;
5220
5373
  seen.add(resolved);
5221
5374
  candidates.push({
@@ -5228,16 +5381,16 @@ function scanNodeModulesCandidates(opts) {
5228
5381
  ageMs: pathAgeMs(resolved, opts.now)
5229
5382
  });
5230
5383
  }
5231
- if (!opts.includeOrphans || !existsSync17(opts.worktreesDir)) return candidates;
5384
+ if (!opts.includeOrphans || !existsSync18(opts.worktreesDir)) return candidates;
5232
5385
  for (const runEntry of readdirSync6(opts.worktreesDir, { withFileTypes: true })) {
5233
5386
  if (!runEntry.isDirectory()) continue;
5234
- const runPath = path26.join(opts.worktreesDir, runEntry.name);
5387
+ const runPath = path28.join(opts.worktreesDir, runEntry.name);
5235
5388
  for (const workerEntry of readdirSync6(runPath, { withFileTypes: true })) {
5236
5389
  if (!workerEntry.isDirectory()) continue;
5237
- const worktreePath = path26.join(runPath, workerEntry.name);
5238
- const nm = path26.join(worktreePath, "node_modules");
5239
- if (!existsSync17(nm)) continue;
5240
- const resolved = path26.resolve(nm);
5390
+ const worktreePath = path28.join(runPath, workerEntry.name);
5391
+ const nm = path28.join(worktreePath, "node_modules");
5392
+ if (!existsSync18(nm)) continue;
5393
+ const resolved = path28.resolve(nm);
5241
5394
  if (seen.has(resolved)) continue;
5242
5395
  if (!isPathInside(resolved, opts.harnessRoot)) continue;
5243
5396
  seen.add(resolved);
@@ -5260,7 +5413,7 @@ function scanWorktreeCandidates(opts) {
5260
5413
  for (const entry of opts.index.values()) {
5261
5414
  if (opts.runIdFilter && entry.runId !== opts.runIdFilter) continue;
5262
5415
  const resolved = entry.worktreePath;
5263
- if (!existsSync17(resolved)) continue;
5416
+ if (!existsSync18(resolved)) continue;
5264
5417
  if (seen.has(resolved)) continue;
5265
5418
  seen.add(resolved);
5266
5419
  candidates.push({
@@ -5277,17 +5430,17 @@ function scanWorktreeCandidates(opts) {
5277
5430
  }
5278
5431
 
5279
5432
  // src/cleanup-worktree-index.ts
5280
- import path27 from "node:path";
5433
+ import path29 from "node:path";
5281
5434
  function buildWorktreeIndex() {
5282
5435
  const index = /* @__PURE__ */ new Map();
5283
5436
  for (const run of listRunRecords()) {
5284
5437
  for (const name of Object.keys(run.workers || {})) {
5285
- const workerPath = path27.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
5438
+ const workerPath = path29.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
5286
5439
  const worker = readJson(workerPath, void 0);
5287
5440
  if (!worker?.worktreePath) continue;
5288
5441
  const status = computeWorkerStatus(worker, { base: run.base, baseCommit: run.baseCommit });
5289
- index.set(path27.resolve(worker.worktreePath), {
5290
- worktreePath: path27.resolve(worker.worktreePath),
5442
+ index.set(path29.resolve(worker.worktreePath), {
5443
+ worktreePath: path29.resolve(worker.worktreePath),
5291
5444
  runId: run.id,
5292
5445
  workerName: name,
5293
5446
  run,
@@ -5302,7 +5455,7 @@ function buildWorktreeIndex() {
5302
5455
  // src/cleanup.ts
5303
5456
  function resolveOptions(options = {}) {
5304
5457
  const harnessRoot = options.harnessRoot ? resolveUserPath(options.harnessRoot) : resolveHarnessRoot();
5305
- const { worktreesDir } = options.harnessRoot ? { worktreesDir: path28.join(harnessRoot, "worktrees") } : getHarnessPaths();
5458
+ const { worktreesDir } = options.harnessRoot ? { worktreesDir: path30.join(harnessRoot, "worktrees") } : getHarnessPaths();
5306
5459
  const execute = options.execute === true;
5307
5460
  const nodeModulesAgeMs = options.nodeModulesAgeMs ?? DEFAULT_NODE_MODULES_AGE_MS;
5308
5461
  const worktreesAgeMs = options.worktreesAgeMs ?? 0;
@@ -5346,7 +5499,7 @@ function runHarnessCleanup(options = {}) {
5346
5499
  actions.push({ ...candidate, executed: false, skipped: true, skipReason: pathSkip });
5347
5500
  continue;
5348
5501
  }
5349
- const worktreePath = path28.resolve(candidate.path, "..");
5502
+ const worktreePath = path30.resolve(candidate.path, "..");
5350
5503
  const indexed = index.get(worktreePath) ?? null;
5351
5504
  const guardReason = skipNodeModulesRemoval({
5352
5505
  indexed,
@@ -5362,7 +5515,7 @@ function runHarnessCleanup(options = {}) {
5362
5515
  actions.push(removeNodeModules(candidate, resolved.execute));
5363
5516
  }
5364
5517
  for (const candidate of scanWorktreeCandidates(scanOpts)) {
5365
- const indexed = index.get(path28.resolve(candidate.path)) ?? null;
5518
+ const indexed = index.get(path30.resolve(candidate.path)) ?? null;
5366
5519
  const guardReason = skipWorktreeRemoval({
5367
5520
  indexed,
5368
5521
  includeOrphans: resolved.includeOrphans,
@@ -5427,8 +5580,8 @@ function isPipelineCleanupEnabled() {
5427
5580
 
5428
5581
  // src/installed-package-versions.ts
5429
5582
  import { readFile } from "node:fs/promises";
5430
- import { homedir as homedir5 } from "node:os";
5431
- import path29 from "node:path";
5583
+ import { homedir as homedir6 } from "node:os";
5584
+ import path31 from "node:path";
5432
5585
  var MANAGED_PACKAGES = [
5433
5586
  "@kynver-app/runtime",
5434
5587
  "@kynver-app/openclaw-agent-os",
@@ -5442,13 +5595,13 @@ function unique(values) {
5442
5595
  return [...new Set(values.filter((value) => Boolean(value)))];
5443
5596
  }
5444
5597
  function moduleRoots() {
5445
- const home = homedir5();
5446
- const openClawPrefix = trim(process.env.KYNVER_OPENCLAW_NPM_ROOT) ?? trim(process.env.OPENCLAW_NPM_ROOT) ?? path29.join(home, ".openclaw", "npm");
5447
- const npmGlobalRoot = trim(process.env.KYNVER_NPM_GLOBAL_ROOT) ?? trim(process.env.KYNVER_NPM_GLOBAL_MODULES_ROOT) ?? (trim(process.env.NPM_CONFIG_PREFIX) ? path29.join(trim(process.env.NPM_CONFIG_PREFIX), "lib", "node_modules") : path29.join(home, ".npm-global", "lib", "node_modules"));
5598
+ const home = homedir6();
5599
+ const openClawPrefix = trim(process.env.KYNVER_OPENCLAW_NPM_ROOT) ?? trim(process.env.OPENCLAW_NPM_ROOT) ?? path31.join(home, ".openclaw", "npm");
5600
+ const npmGlobalRoot = trim(process.env.KYNVER_NPM_GLOBAL_ROOT) ?? trim(process.env.KYNVER_NPM_GLOBAL_MODULES_ROOT) ?? (trim(process.env.NPM_CONFIG_PREFIX) ? path31.join(trim(process.env.NPM_CONFIG_PREFIX), "lib", "node_modules") : path31.join(home, ".npm-global", "lib", "node_modules"));
5448
5601
  return unique([
5449
- path29.join(openClawPrefix, "lib", "node_modules"),
5450
- path29.join(openClawPrefix, "node_modules"),
5451
- npmGlobalRoot.endsWith("node_modules") ? npmGlobalRoot : path29.join(npmGlobalRoot, "lib", "node_modules")
5602
+ path31.join(openClawPrefix, "lib", "node_modules"),
5603
+ path31.join(openClawPrefix, "node_modules"),
5604
+ npmGlobalRoot.endsWith("node_modules") ? npmGlobalRoot : path31.join(npmGlobalRoot, "lib", "node_modules")
5452
5605
  ]);
5453
5606
  }
5454
5607
  async function readVersion(packageJsonPath) {
@@ -5464,7 +5617,7 @@ async function collectInstalledPackageVersions(observedAt = (/* @__PURE__ */ new
5464
5617
  const out = {};
5465
5618
  for (const packageName of MANAGED_PACKAGES) {
5466
5619
  for (const root of roots) {
5467
- const packageJsonPath = path29.join(root, packageName, "package.json");
5620
+ const packageJsonPath = path31.join(root, packageName, "package.json");
5468
5621
  const version = await readVersion(packageJsonPath);
5469
5622
  if (!version) continue;
5470
5623
  out[packageName] = { version, observedAt, path: packageJsonPath };
@@ -5480,7 +5633,7 @@ async function completeFinishedWorkers(runId, args) {
5480
5633
  const outcomes = [];
5481
5634
  for (const name of Object.keys(run.workers || {})) {
5482
5635
  const worker = readJson(
5483
- path30.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
5636
+ path32.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
5484
5637
  void 0
5485
5638
  );
5486
5639
  if (!worker?.taskId || worker.localOnly) continue;
@@ -5622,7 +5775,7 @@ async function runDaemon(args) {
5622
5775
  }
5623
5776
 
5624
5777
  // src/plan-progress.ts
5625
- import path31 from "node:path";
5778
+ import path33 from "node:path";
5626
5779
 
5627
5780
  // src/bounded-build/constants.ts
5628
5781
  var DEFAULT_BUILD_MEM_BUDGET_BYTES = 1536 * 1024 * 1024;
@@ -5908,7 +6061,7 @@ async function emitPlanProgress(args) {
5908
6061
  }
5909
6062
  function verifyPlanLocal(args) {
5910
6063
  const worktree = required(args.worktree ? String(args.worktree) : void 0, "worktree");
5911
- const cwd = path31.resolve(worktree);
6064
+ const cwd = path33.resolve(worktree);
5912
6065
  const summary = runHarnessVerifyCommands(cwd);
5913
6066
  const emitJson = args.json === true || args.json === "true";
5914
6067
  const payload = { passed: summary.passed, worktree: cwd, steps: summary.steps };
@@ -5957,9 +6110,9 @@ async function verifyPlan(args) {
5957
6110
  }
5958
6111
 
5959
6112
  // src/harness-verify-cli.ts
5960
- import path32 from "node:path";
6113
+ import path34 from "node:path";
5961
6114
  function runHarnessVerifyCli(args) {
5962
- const cwd = path32.resolve(required(args.worktree ? String(args.worktree) : void 0, "worktree"));
6115
+ const cwd = path34.resolve(required(args.worktree ? String(args.worktree) : void 0, "worktree"));
5963
6116
  const emitJson = args.json === true || args.json === "true" || args.emitJson === true || args.emitJson === "true";
5964
6117
  const commands = [];
5965
6118
  const rawCmd = args.command;
@@ -6003,7 +6156,7 @@ function runHarnessVerifyCli(args) {
6003
6156
  }
6004
6157
 
6005
6158
  // src/plan-persist-cli.ts
6006
- import { readFileSync as readFileSync8 } from "node:fs";
6159
+ import { readFileSync as readFileSync9 } from "node:fs";
6007
6160
  var OPERATIONS = ["create", "add_version", "update_metadata"];
6008
6161
  var FAILURE_KINDS = [
6009
6162
  "approval_guard",
@@ -6015,7 +6168,7 @@ var FAILURE_KINDS = [
6015
6168
  function readBodyArg(args) {
6016
6169
  const bodyFile = args.bodyFile ? String(args.bodyFile) : void 0;
6017
6170
  if (bodyFile) {
6018
- return { body: readFileSync8(bodyFile, "utf8"), bodyPathHint: bodyFile };
6171
+ return { body: readFileSync9(bodyFile, "utf8"), bodyPathHint: bodyFile };
6019
6172
  }
6020
6173
  const inline = args.body ? String(args.body) : void 0;
6021
6174
  if (inline) return { body: inline };
@@ -6103,7 +6256,7 @@ function runCleanupCli(args) {
6103
6256
  }
6104
6257
 
6105
6258
  // src/monitor/monitor.service.ts
6106
- import path34 from "node:path";
6259
+ import path36 from "node:path";
6107
6260
 
6108
6261
  // src/monitor/monitor.classify.ts
6109
6262
  function expectedLeaseOwner(runId) {
@@ -6159,11 +6312,11 @@ function classifyWorkerHealth(input) {
6159
6312
  }
6160
6313
 
6161
6314
  // src/monitor/monitor.store.ts
6162
- import { existsSync as existsSync18, mkdirSync as mkdirSync6, readdirSync as readdirSync7, unlinkSync as unlinkSync2 } from "node:fs";
6163
- import path33 from "node:path";
6315
+ import { existsSync as existsSync19, mkdirSync as mkdirSync6, readdirSync as readdirSync7, unlinkSync as unlinkSync2 } from "node:fs";
6316
+ import path35 from "node:path";
6164
6317
  function monitorsDir() {
6165
6318
  const { harnessRoot } = getHarnessPaths();
6166
- const dir = path33.join(harnessRoot, "monitors");
6319
+ const dir = path35.join(harnessRoot, "monitors");
6167
6320
  mkdirSync6(dir, { recursive: true });
6168
6321
  return dir;
6169
6322
  }
@@ -6171,7 +6324,7 @@ function monitorIdFor(runId, workerName) {
6171
6324
  return workerName ? `${safeSlug(runId)}--${safeSlug(workerName)}` : safeSlug(runId);
6172
6325
  }
6173
6326
  function monitorPath(monitorId) {
6174
- return path33.join(monitorsDir(), `${monitorId}.json`);
6327
+ return path35.join(monitorsDir(), `${monitorId}.json`);
6175
6328
  }
6176
6329
  function loadMonitorSession(monitorId) {
6177
6330
  return readJson(monitorPath(monitorId), void 0);
@@ -6181,18 +6334,18 @@ function saveMonitorSession(session) {
6181
6334
  }
6182
6335
  function deleteMonitorSession(monitorId) {
6183
6336
  const file = monitorPath(monitorId);
6184
- if (!existsSync18(file)) return false;
6337
+ if (!existsSync19(file)) return false;
6185
6338
  unlinkSync2(file);
6186
6339
  return true;
6187
6340
  }
6188
6341
  function listMonitorSessions() {
6189
6342
  const dir = monitorsDir();
6190
- if (!existsSync18(dir)) return [];
6343
+ if (!existsSync19(dir)) return [];
6191
6344
  const entries = [];
6192
6345
  for (const name of readdirSync7(dir)) {
6193
6346
  if (!name.endsWith(".json")) continue;
6194
6347
  const session = readJson(
6195
- path33.join(dir, name),
6348
+ path35.join(dir, name),
6196
6349
  void 0
6197
6350
  );
6198
6351
  if (!session?.monitorId) continue;
@@ -6283,7 +6436,7 @@ async function fetchTaskLeasesForWorkers(input) {
6283
6436
  // src/monitor/monitor.service.ts
6284
6437
  function workerRecord2(runId, name) {
6285
6438
  return readJson(
6286
- path34.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
6439
+ path36.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
6287
6440
  void 0
6288
6441
  );
6289
6442
  }
@@ -6485,18 +6638,18 @@ async function runMonitorLoop(args) {
6485
6638
 
6486
6639
  // src/monitor/monitor-spawn.ts
6487
6640
  import { spawn as spawn4 } from "node:child_process";
6488
- import { closeSync as closeSync4, existsSync as existsSync19, openSync as openSync4 } from "node:fs";
6489
- import path35 from "node:path";
6490
- import { fileURLToPath as fileURLToPath3 } from "node:url";
6641
+ import { closeSync as closeSync4, existsSync as existsSync20, openSync as openSync4 } from "node:fs";
6642
+ import path37 from "node:path";
6643
+ import { fileURLToPath as fileURLToPath4 } from "node:url";
6491
6644
  function resolveDefaultCliPath2() {
6492
- return path35.join(fileURLToPath3(new URL(".", import.meta.url)), "..", "cli.js");
6645
+ return path37.join(fileURLToPath4(new URL(".", import.meta.url)), "..", "cli.js");
6493
6646
  }
6494
6647
  function spawnMonitorSidecar(opts) {
6495
6648
  const cliPath = opts.cliPath ?? resolveDefaultCliPath2();
6496
- if (!existsSync19(cliPath)) return void 0;
6649
+ if (!existsSync20(cliPath)) return void 0;
6497
6650
  const monitorId = monitorIdFor(opts.runId, opts.workerName);
6498
6651
  const { harnessRoot } = getHarnessPaths();
6499
- const logPath = path35.join(harnessRoot, "monitors", `${monitorId}.log`);
6652
+ const logPath = path37.join(harnessRoot, "monitors", `${monitorId}.log`);
6500
6653
  let logFd;
6501
6654
  try {
6502
6655
  logFd = openSync4(logPath, "a");
@@ -6616,12 +6769,12 @@ async function monitorTickCli(args) {
6616
6769
  }
6617
6770
 
6618
6771
  // src/doctor/runtime-takeover.ts
6619
- import path37 from "node:path";
6772
+ import path39 from "node:path";
6620
6773
 
6621
6774
  // src/doctor/runtime-takeover.probes.ts
6622
- import { accessSync, constants, existsSync as existsSync20, readFileSync as readFileSync9 } from "node:fs";
6623
- import { homedir as homedir6 } from "node:os";
6624
- import path36 from "node:path";
6775
+ import { accessSync, constants, existsSync as existsSync21, readFileSync as readFileSync10 } from "node:fs";
6776
+ import { homedir as homedir7 } from "node:os";
6777
+ import path38 from "node:path";
6625
6778
  import { spawnSync as spawnSync6 } from "node:child_process";
6626
6779
  function captureCommand(bin, args) {
6627
6780
  try {
@@ -6650,7 +6803,7 @@ function tokenPrefix(token) {
6650
6803
  return trimmed.length <= 12 ? `${trimmed}\u2026` : `${trimmed.slice(0, 12)}\u2026`;
6651
6804
  }
6652
6805
  function isWritable(target) {
6653
- if (!existsSync20(target)) return false;
6806
+ if (!existsSync21(target)) return false;
6654
6807
  try {
6655
6808
  accessSync(target, constants.W_OK);
6656
6809
  return true;
@@ -6663,15 +6816,15 @@ var defaultRuntimeTakeoverProbes = {
6663
6816
  commandOnPath: (bin) => captureCommand(process.platform === "win32" ? "where" : "which", [bin]),
6664
6817
  kynverVersion: (bin) => captureCommand(bin, ["--version"]),
6665
6818
  loadConfig: () => loadUserConfig(),
6666
- configFilePath: () => path36.join(homedir6(), ".kynver", "config.json"),
6667
- credentialsFilePath: () => path36.join(homedir6(), ".kynver", "credentials"),
6819
+ configFilePath: () => path38.join(homedir7(), ".kynver", "config.json"),
6820
+ credentialsFilePath: () => path38.join(homedir7(), ".kynver", "credentials"),
6668
6821
  readCredentials: () => {
6669
- const credPath = path36.join(homedir6(), ".kynver", "credentials");
6670
- if (!existsSync20(credPath)) {
6822
+ const credPath = path38.join(homedir7(), ".kynver", "credentials");
6823
+ if (!existsSync21(credPath)) {
6671
6824
  return { hasApiKey: false };
6672
6825
  }
6673
6826
  try {
6674
- const parsed = JSON.parse(readFileSync9(credPath, "utf8"));
6827
+ const parsed = JSON.parse(readFileSync10(credPath, "utf8"));
6675
6828
  return {
6676
6829
  hasApiKey: Boolean(parsed.apiKey?.trim()),
6677
6830
  runnerTokenPrefix: tokenPrefix(parsed.runnerToken),
@@ -6697,8 +6850,8 @@ var defaultRuntimeTakeoverProbes = {
6697
6850
  })()
6698
6851
  }),
6699
6852
  harnessRoot: () => resolveHarnessRoot(),
6700
- legacyOpenclawHarnessRoot: () => path36.join(homedir6(), ".openclaw", "harness"),
6701
- pathExists: (target) => existsSync20(target),
6853
+ legacyOpenclawHarnessRoot: () => path38.join(homedir7(), ".openclaw", "harness"),
6854
+ pathExists: (target) => existsSync21(target),
6702
6855
  pathWritable: (target) => isWritable(target),
6703
6856
  vercelVersion: () => captureCommand("vercel", ["--version"]),
6704
6857
  vercelWhoami: () => captureCommand("vercel", ["whoami"])
@@ -6713,8 +6866,36 @@ function assessRuntimeTakeoverScheduler(env, ctx) {
6713
6866
  const runnerQstash = env.kynverSchedulerProvider === "qstash";
6714
6867
  const hostedDeployment = Boolean(env.qstashTokenPresent) || Boolean(env.kynverHostedDeployment);
6715
6868
  const daemonDispatchReady = Boolean(ctx.agentOsId?.trim()) && Boolean(ctx.apiBaseUrl?.trim()) && ctx.hasScopedRunnerToken;
6716
- const deploymentNeedsQstash = hostedDeployment && !env.qstashTokenPresent && env.kynverSchedulerProvider !== "qstash";
6717
- const deploymentOpenclaw = hostedDeployment && env.kynverSchedulerProvider === "openclaw-cron";
6869
+ const hostedSchedulerProcess = hostedDeployment && !daemonDispatchReady;
6870
+ const deploymentNeedsQstash = hostedSchedulerProcess && !env.qstashTokenPresent && env.kynverSchedulerProvider !== "qstash";
6871
+ const deploymentOpenclaw = hostedSchedulerProcess && env.kynverSchedulerProvider === "openclaw-cron";
6872
+ if (daemonDispatchReady && runnerOpenclaw) {
6873
+ return check({
6874
+ id: "hotspot_openclaw_scheduler",
6875
+ label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
6876
+ status: "warn",
6877
+ summary: "KYNVER_SCHEDULER_PROVIDER=openclaw-cron on this runner \u2014 dispatch is owned by kynver daemon; unset the OpenClaw override",
6878
+ remediation: "Unset KYNVER_SCHEDULER_PROVIDER on user runners. Use `kynver daemon` (pipeline-tick \u2192 operator/tick). On the Kynver server set KYNVER_SCHEDULER_PROVIDER=qstash when QStash is configured.",
6879
+ details: {
6880
+ schedulerProvider: env.kynverSchedulerProvider ?? null,
6881
+ dispatchPath: "kynver-daemon-pipeline-tick",
6882
+ hostedDeployment
6883
+ }
6884
+ });
6885
+ }
6886
+ if (daemonDispatchReady) {
6887
+ return check({
6888
+ id: "hotspot_openclaw_scheduler",
6889
+ label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
6890
+ status: "pass",
6891
+ summary: runnerQstash ? "Runner override qstash present; hosted dispatch still owned by kynver daemon pipeline-tick" : "Hosted dispatch owned by kynver daemon (pipeline-tick \u2192 operator/tick); no OpenClaw cron on runner",
6892
+ details: {
6893
+ schedulerProvider: env.kynverSchedulerProvider ?? null,
6894
+ dispatchPath: "kynver-daemon-pipeline-tick",
6895
+ hostedDeployment
6896
+ }
6897
+ });
6898
+ }
6718
6899
  if (runnerOpenclaw) {
6719
6900
  return check({
6720
6901
  id: "hotspot_openclaw_scheduler",
@@ -6730,7 +6911,7 @@ function assessRuntimeTakeoverScheduler(env, ctx) {
6730
6911
  id: "hotspot_openclaw_scheduler",
6731
6912
  label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
6732
6913
  status: "warn",
6733
- summary: deploymentOpenclaw ? "Hosted deployment has KYNVER_SCHEDULER_PROVIDER=openclaw-cron \u2014 AgentOS scheduled ticks should use QStash" : "Hosted deployment without QSTASH_TOKEN \u2014 scheduler may fall back to openclaw-cron",
6914
+ summary: deploymentOpenclaw ? "Hosted deployment has KYNVER_SCHEDULER_PROVIDER=openclaw-cron \u2014 AgentOS scheduled ticks should use QStash" : env.kynverSchedulerProvider ? `Hosted deployment without QSTASH_TOKEN (KYNVER_SCHEDULER_PROVIDER=${env.kynverSchedulerProvider}) \u2014 scheduler may fall back to openclaw-cron` : "KYNVER_SCHEDULER_PROVIDER unset on hosted deployment; without QSTASH_TOKEN the server may fall back to openclaw-cron",
6734
6915
  remediation: "Set QSTASH_TOKEN and KYNVER_SCHEDULER_PROVIDER=qstash on the Kynver server. User runners use `kynver daemon` for dispatch and should not set a scheduler provider.",
6735
6916
  details: {
6736
6917
  schedulerProvider: env.kynverSchedulerProvider ?? null,
@@ -6739,15 +6920,15 @@ function assessRuntimeTakeoverScheduler(env, ctx) {
6739
6920
  }
6740
6921
  });
6741
6922
  }
6742
- if (daemonDispatchReady) {
6923
+ if (hostedDeployment && env.qstashTokenPresent && !env.kynverSchedulerProvider) {
6743
6924
  return check({
6744
6925
  id: "hotspot_openclaw_scheduler",
6745
6926
  label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
6746
6927
  status: "pass",
6747
- summary: runnerQstash ? "Runner override qstash present; hosted dispatch still owned by kynver daemon pipeline-tick" : "Hosted dispatch owned by kynver daemon (pipeline-tick \u2192 operator/tick); no OpenClaw cron on runner",
6928
+ summary: "QSTASH_TOKEN present; hosted scheduler auto-selects qstash (explicit KYNVER_SCHEDULER_PROVIDER=qstash optional)",
6748
6929
  details: {
6749
- schedulerProvider: env.kynverSchedulerProvider ?? null,
6750
- dispatchPath: "kynver-daemon-pipeline-tick",
6930
+ schedulerProvider: null,
6931
+ qstashTokenPresent: true,
6751
6932
  hostedDeployment
6752
6933
  }
6753
6934
  });
@@ -6841,8 +7022,14 @@ function assessUserConfig(probes) {
6841
7022
  if (exists) {
6842
7023
  const apiBaseUrl = config.apiBaseUrl?.trim();
6843
7024
  const agentOsId = config.agentOsId?.trim();
6844
- const defaultRepo = config.defaultRepo?.trim();
6845
- const displayDefaultRepo = defaultRepo ? displayUserPath(defaultRepo) : null;
7025
+ const resolvedDefaultRepo = resolveDefaultRepo({ config });
7026
+ const formatted = resolvedDefaultRepo ? formatResolvedDefaultRepo(resolvedDefaultRepo) : null;
7027
+ let defaultRepoRemediation;
7028
+ if (!resolvedDefaultRepo) {
7029
+ defaultRepoRemediation = "Run `kynver setup` from a Kynver checkout (auto-discovers repo) or `kynver setup --repo /path/to/Kynver`.";
7030
+ } else if (!resolvedDefaultRepo.persistedInConfig) {
7031
+ defaultRepoRemediation = "Run `kynver setup` (no --repo) to persist discovered defaultRepo in ~/.kynver/config.json.";
7032
+ }
6846
7033
  checks.push(
6847
7034
  check2({
6848
7035
  id: "config_api_base_url",
@@ -6863,11 +7050,13 @@ function assessUserConfig(probes) {
6863
7050
  check2({
6864
7051
  id: "config_default_repo",
6865
7052
  label: "Default repo path",
6866
- status: defaultRepo ? "pass" : "warn",
6867
- summary: displayDefaultRepo ?? "Not set (pass --repo on `kynver run create`)",
6868
- remediation: defaultRepo ? void 0 : "Set `defaultRepo` via `kynver setup --repo /path/to/repo`.",
7053
+ status: resolvedDefaultRepo ? "pass" : "warn",
7054
+ summary: resolvedDefaultRepo ? `${formatted.defaultRepo} (${resolvedDefaultRepo.source}${resolvedDefaultRepo.persistedInConfig ? "" : ", not persisted"})` : "Not set (pass --repo on `kynver run create` or run `kynver setup` to auto-discover)",
7055
+ remediation: defaultRepoRemediation,
6869
7056
  details: {
6870
- defaultRepo: displayDefaultRepo,
7057
+ defaultRepo: formatted?.defaultRepo ?? null,
7058
+ source: formatted?.source ?? null,
7059
+ persistedInConfig: formatted?.persistedInConfig ?? false,
6871
7060
  harnessRoot: config.harnessRoot ? redactHomePath(config.harnessRoot) : null
6872
7061
  }
6873
7062
  })
@@ -6949,8 +7138,8 @@ function assessVercelCli(probes) {
6949
7138
  }
6950
7139
  function assessHarnessDirs(probes) {
6951
7140
  const harnessRoot = probes.harnessRoot();
6952
- const runsDir = path37.join(harnessRoot, "runs");
6953
- const worktreesDir = path37.join(harnessRoot, "worktrees");
7141
+ const runsDir = path39.join(harnessRoot, "runs");
7142
+ const worktreesDir = path39.join(harnessRoot, "worktrees");
6954
7143
  const displayHarnessRoot = redactHomePath(harnessRoot);
6955
7144
  const displayRunsDir = redactHomePath(runsDir);
6956
7145
  const displayWorktreesDir = redactHomePath(worktreesDir);
@@ -7153,9 +7342,9 @@ function usage(code = 0) {
7153
7342
  "Usage:",
7154
7343
  " kynver login --api-key KEY",
7155
7344
  " kynver runner credential [--agent-os-id ID] [--base-url URL]",
7156
- " kynver setup [--api-base-url URL] [--agent-os-id ID] [--agent-os-slug SLUG] [--repo PATH] [--max-workers N] [--provider claude|cursor]",
7345
+ " kynver setup [--api-base-url URL] [--agent-os-id ID] [--agent-os-slug SLUG] [--repo PATH] [--discover-repo] [--max-workers N] [--provider claude|cursor]",
7157
7346
  " kynver daemon --run RUN_ID --agent-os-id AOS_ID [--execute] [--interval-ms MS]",
7158
- " kynver run create --repo /path/repo [--name name] [--base origin/main]",
7347
+ " kynver run create [--repo /path/repo] [--name name] [--base origin/main]",
7159
7348
  " kynver run list",
7160
7349
  " kynver run status --run RUN_ID",
7161
7350
  " kynver run dispatch --run RUN_ID --agent-os-id AOS_ID [--base-url URL] [--secret SECRET] [--execute] [--lane any|implementation|review|landing] [--target-task-id TASK_ID] [--executor harness] [--max-starts 1] [--lease-ms MS] [--owned path[,path]] [--model claude-opus-4-8] [--disk-path /]",
@@ -7248,7 +7437,7 @@ async function main(argv = process.argv.slice(2)) {
7248
7437
  if (scope === "monitor" && action === "run-loop") return void await monitorRunLoopCli(args);
7249
7438
  unknownCommand(scope, action);
7250
7439
  }
7251
- var isCliEntry = process.argv[1] && realpathSync.native(process.argv[1]) === realpathSync.native(fileURLToPath4(import.meta.url));
7440
+ var isCliEntry = process.argv[1] && realpathSync.native(process.argv[1]) === realpathSync.native(fileURLToPath5(import.meta.url));
7252
7441
  if (isCliEntry) {
7253
7442
  void main().catch((error) => {
7254
7443
  console.error(error);
@@ -7571,6 +7760,8 @@ export {
7571
7760
  computeWorkerStatus,
7572
7761
  createRun,
7573
7762
  deriveRunStatus,
7763
+ discoverDefaultRepo,
7764
+ discoverDefaultRepoCandidates,
7574
7765
  dispatchRun,
7575
7766
  drainPlanOutbox,
7576
7767
  ensurePrReadyHandoff,
@@ -7578,13 +7769,16 @@ export {
7578
7769
  extractPlanOutboxFromTask,
7579
7770
  extractPrUrlFromText,
7580
7771
  formatPlanOutboxHandoffBlock,
7772
+ formatResolvedDefaultRepo,
7581
7773
  getHarnessPaths,
7582
7774
  getMonitorStatus,
7775
+ gitRepoRoot,
7583
7776
  hashPlanBody,
7584
7777
  isDashboardVercelUrl,
7585
7778
  isEngagementRequiredSkip,
7586
7779
  isFinishedWorkerStatus,
7587
7780
  isForbiddenWorkerEnvKey,
7781
+ isKynverMonorepoRoot,
7588
7782
  isLandingBlockedWorkerStatus,
7589
7783
  isSystemdRunAvailable,
7590
7784
  isTerminalHeartbeatPhase,
@@ -7603,6 +7797,7 @@ export {
7603
7797
  parseClaudeStream,
7604
7798
  parseHarnessStream,
7605
7799
  parseHeartbeat,
7800
+ persistDefaultRepo,
7606
7801
  persistPlan,
7607
7802
  pickVercelStatusContext,
7608
7803
  postJson,
@@ -7611,9 +7806,11 @@ export {
7611
7806
  reconcileRunsCli,
7612
7807
  reconcileStaleWorkers,
7613
7808
  redactHarness,
7809
+ remediateDefaultRepo,
7614
7810
  resolveBaseUrl,
7615
7811
  resolveCallbackSecret,
7616
7812
  resolveCallbackSecretWithMint,
7813
+ resolveDefaultRepo,
7617
7814
  resolveHarnessRoot,
7618
7815
  resolveVercelInspectTarget,
7619
7816
  runBoundedBuildCheck,