@kynver-app/runtime 0.1.48 → 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;
@@ -1052,238 +1301,63 @@ function classifyExitFailure(errorText) {
1052
1301
  }
1053
1302
  }
1054
1303
  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
- }
1206
- }
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}` };
1212
- }
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();
1304
+ }
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;
1311
+ }
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;
@@ -1956,7 +2030,7 @@ function inferModelRoutingFromTask(task) {
1956
2030
  rule: "lane:landing"
1957
2031
  };
1958
2032
  }
1959
- if (ref.includes("review") || title.startsWith("review ") || roleLane.includes("review")) {
2033
+ if (ref.includes("review") || /^review[\s:]/.test(title) || roleLane.includes("review")) {
1960
2034
  if (isOpusLane(ref, title) || roleLane === "deep_reviewer") {
1961
2035
  return { model: "claude-opus-4-7", provider: "claude", rule: "lane:deep_review" };
1962
2036
  }
@@ -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) {
@@ -2419,6 +2493,11 @@ function isHarnessExpertReviewWorker(worker) {
2419
2493
 
2420
2494
  // src/pr-handoff/pr-handoff-assess.ts
2421
2495
  var REVIEW_LANE_RULE = /^(lane:)?(review|deep_review|planning|landing)(:|$)/i;
2496
+ var REVIEW_PERSONA_SLUGS = /* @__PURE__ */ new Set(["lorentz", "sentinel"]);
2497
+ var NO_PR_COMMITS_BETWEEN_RE = /no commits between/i;
2498
+ function isGhNoCommitsBetweenError(detail) {
2499
+ return Boolean(detail && NO_PR_COMMITS_BETWEEN_RE.test(detail));
2500
+ }
2422
2501
  function trimOrNull4(value) {
2423
2502
  if (typeof value !== "string") return null;
2424
2503
  const trimmed = value.trim();
@@ -2437,8 +2516,30 @@ function extractPrUrlFromText(value) {
2437
2516
  );
2438
2517
  return m ? trimOrNull4(m[0]) : null;
2439
2518
  }
2440
- function hasWorkProduct(snapshot) {
2519
+ function countCommitsAheadOfBase(worktreePath, baseRef, exec) {
2520
+ const base = baseRef.trim();
2521
+ if (!base) return null;
2522
+ const count = exec.git(worktreePath, ["rev-list", "--count", `${base}..HEAD`]);
2523
+ if (count.status !== 0) return null;
2524
+ const n = Number.parseInt(count.stdout.trim(), 10);
2525
+ return Number.isFinite(n) ? n : null;
2526
+ }
2527
+ function isReviewArtifactWorker(worker, snapshot) {
2528
+ if (snapshot.changedFiles.length > 0) return false;
2529
+ const persona = trimOrNull4(worker.personaSlug)?.toLowerCase();
2530
+ if (persona && REVIEW_PERSONA_SLUGS.has(persona)) return true;
2531
+ const rule = trimOrNull4(worker.routingRule) ?? "";
2532
+ if (rule && REVIEW_LANE_RULE.test(rule)) return true;
2533
+ return false;
2534
+ }
2535
+ function hasWorkProduct(snapshot, options) {
2441
2536
  if (snapshot.changedFiles.length > 0) return true;
2537
+ const baseRef = trimOrNull4(options?.baseRef);
2538
+ if (baseRef && options?.exec && options.worktreePath) {
2539
+ const ahead = countCommitsAheadOfBase(options.worktreePath, baseRef, options.exec);
2540
+ if (ahead === 0) return false;
2541
+ if (ahead !== null && ahead > 0) return true;
2542
+ }
2442
2543
  if (trimOrNull4(snapshot.headCommit)) return true;
2443
2544
  if (committedHead(snapshot.gitAncestry)) return true;
2444
2545
  return false;
@@ -2459,6 +2560,13 @@ function assessPrHandoffRequirement(input) {
2459
2560
  if (rule && REVIEW_LANE_RULE.test(rule)) {
2460
2561
  return { required: false, reason: "review_lane" };
2461
2562
  }
2563
+ const workerCtx = input.worker ?? {
2564
+ personaSlug: input.personaSlug,
2565
+ routingRule: input.routingRule
2566
+ };
2567
+ if (isReviewArtifactWorker(workerCtx, input.snapshot)) {
2568
+ return { required: false, reason: "review_artifact" };
2569
+ }
2462
2570
  if (trimOrNull4(input.patchPath) || trimOrNull4(input.artifactBundlePath)) {
2463
2571
  return { required: false, reason: "patch_or_bundle" };
2464
2572
  }
@@ -2466,7 +2574,12 @@ function assessPrHandoffRequirement(input) {
2466
2574
  if (prUrl) {
2467
2575
  return { required: false, reason: "already_has_pr" };
2468
2576
  }
2469
- if (!hasWorkProduct(input.snapshot)) {
2577
+ const workProductOpts = input.exec && input.baseRef ? {
2578
+ baseRef: input.baseRef,
2579
+ exec: input.exec,
2580
+ worktreePath: input.snapshot.worktreePath
2581
+ } : void 0;
2582
+ if (!hasWorkProduct(input.snapshot, workProductOpts)) {
2470
2583
  return { required: false, reason: "no_work_product" };
2471
2584
  }
2472
2585
  return { required: true, snapshot: input.snapshot };
@@ -2675,15 +2788,19 @@ function ensurePrReadyHandoff(input, exec = defaultPrHandoffExec) {
2675
2788
  prUrl: prUrlHint,
2676
2789
  headCommit: null
2677
2790
  });
2791
+ const baseRef = input.run.baseCommit?.trim() || input.run.base?.trim() || "origin/main";
2678
2792
  const requirement = assessPrHandoffRequirement({
2679
2793
  dispatched: input.worker.dispatched,
2680
2794
  routingRule: input.worker.routingRule,
2795
+ personaSlug: input.worker.personaSlug,
2681
2796
  prUrl: prUrlHint,
2682
2797
  taskTitle: input.worker.taskTitle,
2683
2798
  executorRef: input.worker.executorRef,
2684
2799
  parentTaskId: input.worker.parentTaskId,
2685
- personaSlug: input.worker.personaSlug,
2686
2800
  taskPrUrl: input.worker.taskPrUrl,
2801
+ baseRef,
2802
+ exec,
2803
+ worker: input.worker,
2687
2804
  snapshot
2688
2805
  });
2689
2806
  if (!requirement.required) {
@@ -2768,6 +2885,14 @@ function ensurePrReadyHandoff(input, exec = defaultPrHandoffExec) {
2768
2885
  exec
2769
2886
  });
2770
2887
  if (!pr.ok || !pr.prUrl) {
2888
+ if (isGhNoCommitsBetweenError(pr.detail)) {
2889
+ return {
2890
+ ok: true,
2891
+ headCommit: headCommit ?? void 0,
2892
+ committed,
2893
+ pushed
2894
+ };
2895
+ }
2771
2896
  const dirty = snapshot.changedFiles.length;
2772
2897
  const detail = dirty ? `${dirty} uncommitted change(s) and no PR URL after handoff attempt` : "no PR URL after handoff attempt";
2773
2898
  return {
@@ -2787,7 +2912,7 @@ function ensurePrReadyHandoff(input, exec = defaultPrHandoffExec) {
2787
2912
  }
2788
2913
 
2789
2914
  // src/worker-lifecycle.ts
2790
- import path10 from "node:path";
2915
+ import path11 from "node:path";
2791
2916
  var TASK_LEFT_RUNNING = /* @__PURE__ */ new Set([
2792
2917
  "awaiting_review",
2793
2918
  "blocked",
@@ -2843,7 +2968,7 @@ function syncCompletionAcknowledgedFromOperatorTick(runId, operatorTick) {
2843
2968
  const synced = [];
2844
2969
  for (const name of Object.keys(run.workers || {})) {
2845
2970
  const worker = readJson(
2846
- path10.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
2971
+ path11.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
2847
2972
  void 0
2848
2973
  );
2849
2974
  if (!worker?.taskId || isCompletionAcknowledged(worker)) continue;
@@ -3101,7 +3226,7 @@ function workerStatus(args) {
3101
3226
  const worker = loadWorker(String(args.run), String(args.name));
3102
3227
  const run = loadRun(worker.runId);
3103
3228
  const status = computeWorkerStatus(worker, workerStatusOptions(run));
3104
- writeJson(path11.join(worker.workerDir, "last-status.json"), status);
3229
+ writeJson(path12.join(worker.workerDir, "last-status.json"), status);
3105
3230
  console.log(JSON.stringify(status, null, 2));
3106
3231
  }
3107
3232
  function buildRunBoard(runId) {
@@ -3109,7 +3234,7 @@ function buildRunBoard(runId) {
3109
3234
  const names = Object.keys(run.workers || {});
3110
3235
  const workers = names.map((name) => {
3111
3236
  const worker = readJson(
3112
- path11.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
3237
+ path12.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
3113
3238
  void 0
3114
3239
  );
3115
3240
  if (!worker) {
@@ -3220,7 +3345,7 @@ function buildRunBoard(runId) {
3220
3345
  needsAttention: workers.filter((w) => w.attention && w.attention !== "ok" && w.attention !== "done").map((w) => w.worker),
3221
3346
  workers
3222
3347
  };
3223
- writeJson(path11.join(runDirectory(run.id), "last-board.json"), board);
3348
+ writeJson(path12.join(runDirectory(run.id), "last-board.json"), board);
3224
3349
  return board;
3225
3350
  }
3226
3351
  async function publishHarnessBoardSnapshot(args, source) {
@@ -3409,12 +3534,12 @@ async function autoCompleteWorkerCli(raw) {
3409
3534
  }
3410
3535
  }
3411
3536
  function resolveDefaultCliPath() {
3412
- return path12.join(fileURLToPath2(new URL(".", import.meta.url)), "cli.js");
3537
+ return path13.join(fileURLToPath3(new URL(".", import.meta.url)), "cli.js");
3413
3538
  }
3414
3539
  function spawnCompletionSidecar(opts) {
3415
3540
  const cliPath = opts.cliPath ?? resolveDefaultCliPath();
3416
- if (!existsSync10(cliPath)) return void 0;
3417
- 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");
3418
3543
  let logFd;
3419
3544
  try {
3420
3545
  logFd = openSync3(logPath, "a");
@@ -3496,16 +3621,16 @@ function spawnWorkerProcess(run, opts) {
3496
3621
  launchModel = preflight.model;
3497
3622
  }
3498
3623
  const { worktreesDir } = getPaths();
3499
- const workerDir = path13.join(runDirectory(run.id), "workers", name);
3624
+ const workerDir = path14.join(runDirectory(run.id), "workers", name);
3500
3625
  mkdirSync3(workerDir, { recursive: true });
3501
- const worktreePath = path13.join(worktreesDir, run.id, name);
3626
+ const worktreePath = path14.join(worktreesDir, run.id, name);
3502
3627
  const branch = opts.branch || `agent/${run.id}/${name}`;
3503
- if (existsSync11(worktreePath)) throw new Error(`worktree path already exists: ${worktreePath}`);
3628
+ if (existsSync12(worktreePath)) throw new Error(`worktree path already exists: ${worktreePath}`);
3504
3629
  git(run.repo, ["fetch", "origin", "--prune"], { allowFailure: true });
3505
3630
  git(run.repo, ["worktree", "add", "-b", branch, worktreePath, run.baseCommit], { throwError: true });
3506
- const stdoutPath = path13.join(workerDir, "stdout.jsonl");
3507
- const stderrPath = path13.join(workerDir, "stderr.log");
3508
- 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");
3509
3634
  const prompt = buildPrompt({
3510
3635
  task: opts.task,
3511
3636
  ownedPaths: opts.ownedPaths || [],
@@ -3570,7 +3695,7 @@ function spawnWorkerProcess(run, opts) {
3570
3695
  startedAt: (/* @__PURE__ */ new Date()).toISOString()
3571
3696
  };
3572
3697
  saveWorker(run.id, worker);
3573
- 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") } };
3574
3699
  run.status = "running";
3575
3700
  saveRun(run);
3576
3701
  if (worker.agentOsId && worker.taskId) {
@@ -3862,18 +3987,18 @@ function buildPlanPersistIdempotencyKey(input) {
3862
3987
 
3863
3988
  // src/plan-persist/paths.ts
3864
3989
  import { mkdirSync as mkdirSync4 } from "node:fs";
3865
- import { homedir as homedir4 } from "node:os";
3866
- import path14 from "node:path";
3990
+ import { homedir as homedir5 } from "node:os";
3991
+ import path15 from "node:path";
3867
3992
  function resolveKynverStateRoot() {
3868
3993
  const env = process.env.KYNVER_STATE_ROOT;
3869
- if (env) return path14.resolve(env);
3870
- return path14.join(homedir4(), ".kynver", "state");
3994
+ if (env) return path15.resolve(env);
3995
+ return path15.join(homedir5(), ".kynver", "state");
3871
3996
  }
3872
3997
  function planOutboxDir() {
3873
- return path14.join(resolveKynverStateRoot(), "plan-outbox");
3998
+ return path15.join(resolveKynverStateRoot(), "plan-outbox");
3874
3999
  }
3875
4000
  function planOutboxArchiveDir() {
3876
- return path14.join(resolveKynverStateRoot(), "plan-outbox-archive");
4001
+ return path15.join(resolveKynverStateRoot(), "plan-outbox-archive");
3877
4002
  }
3878
4003
  function ensurePlanOutboxDirs() {
3879
4004
  const outboxDir = planOutboxDir();
@@ -3884,20 +4009,20 @@ function ensurePlanOutboxDirs() {
3884
4009
  }
3885
4010
  function isTmpOnlyPath(filePath) {
3886
4011
  if (filePath.startsWith("/tmp/") || filePath.startsWith("/var/folders/")) return true;
3887
- const resolved = path14.resolve(filePath);
3888
- 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"));
3889
4014
  }
3890
4015
 
3891
4016
  // src/plan-persist/outbox-store.ts
3892
4017
  import {
3893
- existsSync as existsSync13,
3894
- readFileSync as readFileSync7,
4018
+ existsSync as existsSync14,
4019
+ readFileSync as readFileSync8,
3895
4020
  renameSync,
3896
4021
  readdirSync as readdirSync4,
3897
4022
  writeFileSync as writeFileSync3,
3898
4023
  unlinkSync
3899
4024
  } from "node:fs";
3900
- import path15 from "node:path";
4025
+ import path16 from "node:path";
3901
4026
  import { randomUUID } from "node:crypto";
3902
4027
  var DEFAULT_MAX_RETRIES = 12;
3903
4028
  function listOutboxItems() {
@@ -3905,7 +4030,7 @@ function listOutboxItems() {
3905
4030
  const files = readdirSync4(outboxDir).filter((f) => f.endsWith(".json"));
3906
4031
  const items = [];
3907
4032
  for (const file of files) {
3908
- const item = readOutboxItem(path15.join(outboxDir, file));
4033
+ const item = readOutboxItem(path16.join(outboxDir, file));
3909
4034
  if (item && item.queueStatus === "queued") items.push(item);
3910
4035
  }
3911
4036
  return items.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
@@ -3917,25 +4042,25 @@ function findOutboxByIdempotencyKey(key) {
3917
4042
  return null;
3918
4043
  }
3919
4044
  function readOutboxItem(jsonPath) {
3920
- if (!existsSync13(jsonPath)) return null;
4045
+ if (!existsSync14(jsonPath)) return null;
3921
4046
  try {
3922
- return JSON.parse(readFileSync7(jsonPath, "utf8"));
4047
+ return JSON.parse(readFileSync8(jsonPath, "utf8"));
3923
4048
  } catch {
3924
4049
  return null;
3925
4050
  }
3926
4051
  }
3927
4052
  function readOutboxBody(item) {
3928
4053
  const { outboxDir } = ensurePlanOutboxDirs();
3929
- const bodyFile = path15.join(outboxDir, item.bodyPath);
3930
- return readFileSync7(bodyFile, "utf8");
4054
+ const bodyFile = path16.join(outboxDir, item.bodyPath);
4055
+ return readFileSync8(bodyFile, "utf8");
3931
4056
  }
3932
4057
  function writeOutboxItem(input, opts) {
3933
4058
  const { outboxDir } = ensurePlanOutboxDirs();
3934
4059
  const now = (/* @__PURE__ */ new Date()).toISOString();
3935
4060
  const id = opts.existing?.id ?? randomUUID();
3936
4061
  const bodyPath = opts.existing?.bodyPath ?? `${id}.body.md`;
3937
- const jsonPath = path15.join(outboxDir, `${id}.json`);
3938
- const bodyFile = path15.join(outboxDir, bodyPath);
4062
+ const jsonPath = path16.join(outboxDir, `${id}.json`);
4063
+ const bodyFile = path16.join(outboxDir, bodyPath);
3939
4064
  if (!opts.existing) {
3940
4065
  writeFileSync3(bodyFile, input.body, "utf8");
3941
4066
  }
@@ -3971,24 +4096,24 @@ function writeOutboxItem(input, opts) {
3971
4096
  }
3972
4097
  function saveOutboxItem(item) {
3973
4098
  const { outboxDir } = ensurePlanOutboxDirs();
3974
- const jsonPath = path15.join(outboxDir, `${item.id}.json`);
4099
+ const jsonPath = path16.join(outboxDir, `${item.id}.json`);
3975
4100
  writeFileSync3(jsonPath, `${JSON.stringify(item, null, 2)}
3976
4101
  `, { mode: 384 });
3977
4102
  }
3978
4103
  function archiveOutboxItem(item) {
3979
4104
  const { outboxDir, archiveDir } = ensurePlanOutboxDirs();
3980
- const jsonSrc = path15.join(outboxDir, `${item.id}.json`);
3981
- const bodySrc = path15.join(outboxDir, item.bodyPath);
3982
- const jsonDst = path15.join(archiveDir, `${item.id}.json`);
3983
- const bodyDst = path15.join(archiveDir, item.bodyPath);
3984
- if (existsSync13(jsonSrc)) renameSync(jsonSrc, jsonDst);
3985
- 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);
3986
4111
  }
3987
4112
  function outboxItemPaths(item) {
3988
4113
  const { outboxDir } = ensurePlanOutboxDirs();
3989
4114
  return {
3990
- jsonPath: path15.join(outboxDir, `${item.id}.json`),
3991
- bodyPath: path15.join(outboxDir, item.bodyPath)
4115
+ jsonPath: path16.join(outboxDir, `${item.id}.json`),
4116
+ bodyPath: path16.join(outboxDir, item.bodyPath)
3992
4117
  };
3993
4118
  }
3994
4119
  function outboxInputFromItem(item, body) {
@@ -4174,7 +4299,7 @@ function markOutboxFailed(item, message) {
4174
4299
  }
4175
4300
 
4176
4301
  // src/plan-persist/drain.ts
4177
- import path16 from "node:path";
4302
+ import path17 from "node:path";
4178
4303
  async function drainPlanOutbox(opts = {}, deps = {}) {
4179
4304
  const items = listOutboxItems().filter(
4180
4305
  (item) => opts.outboxId ? item.id === opts.outboxId : true
@@ -4208,7 +4333,7 @@ async function drainPlanOutbox(opts = {}, deps = {}) {
4208
4333
  return result;
4209
4334
  }
4210
4335
  function loadOutboxById(outboxId) {
4211
- const jsonPath = path16.join(planOutboxDir(), `${outboxId}.json`);
4336
+ const jsonPath = path17.join(planOutboxDir(), `${outboxId}.json`);
4212
4337
  return readOutboxItem(jsonPath);
4213
4338
  }
4214
4339
 
@@ -4308,7 +4433,7 @@ async function dispatchRun(args) {
4308
4433
  const activeHarnessWorkers = [];
4309
4434
  for (const name of Object.keys(run.workers || {})) {
4310
4435
  const worker = readJson(
4311
- path17.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
4436
+ path18.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
4312
4437
  void 0
4313
4438
  );
4314
4439
  if (!worker?.taskId || !isPidAlive(worker.pid)) continue;
@@ -4533,7 +4658,7 @@ function redactHarness(text, secret) {
4533
4658
  }
4534
4659
 
4535
4660
  // src/validate.ts
4536
- import path18 from "node:path";
4661
+ import path19 from "node:path";
4537
4662
  var RUN_ID_RE = /^[a-z0-9][a-z0-9._-]{0,127}$/i;
4538
4663
  var WORKER_NAME_RE = /^[a-z0-9][a-z0-9._-]{0,63}$/i;
4539
4664
  function validateRunId(runId) {
@@ -4547,15 +4672,15 @@ function validateWorkerName(name) {
4547
4672
  return trimmed;
4548
4673
  }
4549
4674
  function validateRepo(repo) {
4550
- const resolved = path18.resolve(repo);
4675
+ const resolved = path19.resolve(repo);
4551
4676
  if (resolved.includes("..")) throw new Error("repo path must not contain .. segments");
4552
4677
  return resolved;
4553
4678
  }
4554
4679
  function validateOwnedPaths(repoRoot, ownedPaths) {
4555
4680
  return ownedPaths.map((owned) => {
4556
- const resolved = path18.resolve(repoRoot, owned);
4557
- const rel = path18.relative(repoRoot, resolved);
4558
- 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)) {
4559
4684
  throw new Error(`owned path escapes repo: ${owned}`);
4560
4685
  }
4561
4686
  return resolved;
@@ -4567,14 +4692,93 @@ function validateTailLines(lines) {
4567
4692
  }
4568
4693
 
4569
4694
  // src/worktree.ts
4570
- import { existsSync as existsSync14, mkdirSync as mkdirSync5 } from "node:fs";
4571
- 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
+ }
4572
4776
  function createRun(args) {
4573
- const repo = validateRepo(required(String(args.repo || ""), "--repo"));
4777
+ const repo = validateRepo(resolveCreateRunRepo(args));
4574
4778
  ensureGitRepo(repo);
4575
4779
  const id = args.id ? validateRunId(String(args.id)) : timestampSlug(String(args.name || "run"));
4576
4780
  const dir = runDirectory(id);
4577
- if (existsSync14(dir)) failExists(`run already exists: ${id}`);
4781
+ if (existsSync15(dir)) failExists(`run already exists: ${id}`);
4578
4782
  mkdirSync5(dir, { recursive: true });
4579
4783
  const base = String(args.base || "origin/main");
4580
4784
  const baseCommit = git(repo, ["rev-parse", base]).trim();
@@ -4588,12 +4792,12 @@ function createRun(args) {
4588
4792
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
4589
4793
  workers: {}
4590
4794
  };
4591
- writeJson(path19.join(dir, "run.json"), run);
4795
+ writeJson(path21.join(dir, "run.json"), run);
4592
4796
  console.log(JSON.stringify({ runId: id, runDir: dir, repo, base, baseCommit }, null, 2));
4593
4797
  }
4594
4798
  function listRuns() {
4595
4799
  const { runsDir } = getPaths();
4596
- 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) => ({
4597
4801
  id: run.id,
4598
4802
  name: run.name,
4599
4803
  status: run.status,
@@ -4608,7 +4812,7 @@ function failExists(message) {
4608
4812
  }
4609
4813
 
4610
4814
  // src/sweep.ts
4611
- import path20 from "node:path";
4815
+ import path22 from "node:path";
4612
4816
  async function sweepRun(args) {
4613
4817
  const pipeline = args.pipeline === true || args.pipeline === "true";
4614
4818
  try {
@@ -4621,7 +4825,7 @@ async function sweepRun(args) {
4621
4825
  const releasedLocalOrphans = [];
4622
4826
  for (const name of Object.keys(run.workers || {})) {
4623
4827
  const worker = readJson(
4624
- path20.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
4828
+ path22.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
4625
4829
  void 0
4626
4830
  );
4627
4831
  if (!worker || !worker.dispatched || !worker.taskId) continue;
@@ -4665,10 +4869,10 @@ async function sweepRun(args) {
4665
4869
 
4666
4870
  // src/cli.ts
4667
4871
  import { mkdirSync as mkdirSync7, realpathSync } from "node:fs";
4668
- import { fileURLToPath as fileURLToPath4 } from "node:url";
4872
+ import { fileURLToPath as fileURLToPath5 } from "node:url";
4669
4873
 
4670
4874
  // src/pipeline-tick.ts
4671
- import path30 from "node:path";
4875
+ import path32 from "node:path";
4672
4876
 
4673
4877
  // src/pipeline-dispatch.ts
4674
4878
  var RESERVED_REVIEW_STARTS = 1;
@@ -4722,10 +4926,10 @@ async function runPipelineDispatch(args, slots) {
4722
4926
  }
4723
4927
 
4724
4928
  // src/stale-reconcile.ts
4725
- import path22 from "node:path";
4929
+ import path24 from "node:path";
4726
4930
 
4727
4931
  // src/finalize.ts
4728
- import path21 from "node:path";
4932
+ import path23 from "node:path";
4729
4933
  var ACTIVE_RUN_STATUSES = /* @__PURE__ */ new Set(["running", "dispatching", "pending", "queued"]);
4730
4934
  function terminalStatusFor(run) {
4731
4935
  const names = Object.keys(run.workers || {});
@@ -4736,7 +4940,7 @@ function terminalStatusFor(run) {
4736
4940
  let anyLandingBlocked = false;
4737
4941
  for (const name of names) {
4738
4942
  const worker = readJson(
4739
- path21.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
4943
+ path23.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
4740
4944
  void 0
4741
4945
  );
4742
4946
  if (!worker) continue;
@@ -4788,7 +4992,7 @@ function reconcileStaleWorkers() {
4788
4992
  const now = Date.now();
4789
4993
  for (const run of listRunRecords()) {
4790
4994
  for (const name of Object.keys(run.workers || {})) {
4791
- 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");
4792
4996
  const worker = readJson(workerPath, void 0);
4793
4997
  if (!worker || worker.status !== "running") {
4794
4998
  outcomes.push({
@@ -4882,7 +5086,7 @@ function reconcileRunsCli() {
4882
5086
  }
4883
5087
 
4884
5088
  // src/plan-progress-daemon-sync.ts
4885
- import path23 from "node:path";
5089
+ import path25 from "node:path";
4886
5090
 
4887
5091
  // src/plan-progress-sync.ts
4888
5092
  async function syncPlanProgress(args) {
@@ -4906,7 +5110,7 @@ async function syncActiveWorkerPlanProgress(runId, args) {
4906
5110
  const outcomes = [];
4907
5111
  for (const name of Object.keys(run.workers || {})) {
4908
5112
  const worker = readJson(
4909
- path23.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
5113
+ path25.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
4910
5114
  void 0
4911
5115
  );
4912
5116
  if (!worker?.dispatched || !worker.taskId) continue;
@@ -4955,7 +5159,7 @@ async function fetchWorkspaceRuntimePreferences(agentOsId, args) {
4955
5159
  }
4956
5160
 
4957
5161
  // src/cleanup.ts
4958
- import path28 from "node:path";
5162
+ import path30 from "node:path";
4959
5163
 
4960
5164
  // src/cleanup-types.ts
4961
5165
  var DEFAULT_NODE_MODULES_AGE_MS = 6 * 60 * 60 * 1e3;
@@ -5026,14 +5230,14 @@ function skipNodeModulesRemoval(input) {
5026
5230
  }
5027
5231
 
5028
5232
  // src/cleanup-execute.ts
5029
- import { existsSync as existsSync16, rmSync } from "node:fs";
5030
- import path25 from "node:path";
5233
+ import { existsSync as existsSync17, rmSync } from "node:fs";
5234
+ import path27 from "node:path";
5031
5235
 
5032
5236
  // src/cleanup-dir-size.ts
5033
- import { existsSync as existsSync15, readdirSync as readdirSync5, statSync as statSync2 } from "node:fs";
5034
- 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";
5035
5239
  function directorySizeBytes(root, maxEntries = 5e4) {
5036
- if (!existsSync15(root)) return 0;
5240
+ if (!existsSync16(root)) return 0;
5037
5241
  let total = 0;
5038
5242
  let seen = 0;
5039
5243
  const stack = [root];
@@ -5047,7 +5251,7 @@ function directorySizeBytes(root, maxEntries = 5e4) {
5047
5251
  }
5048
5252
  for (const name of entries) {
5049
5253
  if (seen++ > maxEntries) return null;
5050
- const full = path24.join(current, name);
5254
+ const full = path26.join(current, name);
5051
5255
  let st;
5052
5256
  try {
5053
5257
  st = statSync2(full);
@@ -5063,7 +5267,7 @@ function directorySizeBytes(root, maxEntries = 5e4) {
5063
5267
 
5064
5268
  // src/cleanup-execute.ts
5065
5269
  function removeNodeModules(candidate, execute) {
5066
- if (!existsSync16(candidate.path)) {
5270
+ if (!existsSync17(candidate.path)) {
5067
5271
  return {
5068
5272
  ...candidate,
5069
5273
  executed: false,
@@ -5094,7 +5298,7 @@ function removeNodeModules(candidate, execute) {
5094
5298
  }
5095
5299
  }
5096
5300
  function removeWorktree(candidate, execute) {
5097
- if (!existsSync16(candidate.path)) {
5301
+ if (!existsSync17(candidate.path)) {
5098
5302
  return {
5099
5303
  ...candidate,
5100
5304
  executed: false,
@@ -5111,7 +5315,7 @@ function removeWorktree(candidate, execute) {
5111
5315
  if (repo) {
5112
5316
  git(repo, ["worktree", "remove", "--force", candidate.path], { allowFailure: true });
5113
5317
  }
5114
- if (existsSync16(candidate.path)) {
5318
+ if (existsSync17(candidate.path)) {
5115
5319
  rmSync(candidate.path, { recursive: true, force: true });
5116
5320
  }
5117
5321
  return {
@@ -5131,20 +5335,20 @@ function removeWorktree(candidate, execute) {
5131
5335
  }
5132
5336
  }
5133
5337
  function isHarnessNodeModulesPath(targetPath, harnessRoot, worktreesDir) {
5134
- const resolved = path25.resolve(targetPath);
5135
- 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;
5136
5340
  if (!nm) return "path_outside_harness";
5137
- const rel = path25.relative(worktreesDir, nm);
5138
- if (rel.startsWith("..") || path25.isAbsolute(rel)) return "path_outside_harness";
5139
- 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);
5140
5344
  if (parts.length < 3 || parts[parts.length - 1] !== "node_modules") return "path_outside_harness";
5141
- if (!resolved.startsWith(path25.resolve(harnessRoot))) return "path_outside_harness";
5345
+ if (!resolved.startsWith(path27.resolve(harnessRoot))) return "path_outside_harness";
5142
5346
  return null;
5143
5347
  }
5144
5348
 
5145
5349
  // src/cleanup-scan.ts
5146
- import { existsSync as existsSync17, readdirSync as readdirSync6, statSync as statSync3 } from "node:fs";
5147
- 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";
5148
5352
  function pathAgeMs(target, now) {
5149
5353
  try {
5150
5354
  const mtime = statSync3(target).mtimeMs;
@@ -5154,17 +5358,17 @@ function pathAgeMs(target, now) {
5154
5358
  }
5155
5359
  }
5156
5360
  function isPathInside(child, parent) {
5157
- const rel = path26.relative(parent, child);
5158
- return rel === "" || !rel.startsWith("..") && !path26.isAbsolute(rel);
5361
+ const rel = path28.relative(parent, child);
5362
+ return rel === "" || !rel.startsWith("..") && !path28.isAbsolute(rel);
5159
5363
  }
5160
5364
  function scanNodeModulesCandidates(opts) {
5161
5365
  const candidates = [];
5162
5366
  const seen = /* @__PURE__ */ new Set();
5163
5367
  for (const entry of opts.index.values()) {
5164
5368
  if (opts.runIdFilter && entry.runId !== opts.runIdFilter) continue;
5165
- const nm = path26.join(entry.worktreePath, "node_modules");
5166
- if (!existsSync17(nm)) continue;
5167
- 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);
5168
5372
  if (seen.has(resolved)) continue;
5169
5373
  seen.add(resolved);
5170
5374
  candidates.push({
@@ -5177,16 +5381,16 @@ function scanNodeModulesCandidates(opts) {
5177
5381
  ageMs: pathAgeMs(resolved, opts.now)
5178
5382
  });
5179
5383
  }
5180
- if (!opts.includeOrphans || !existsSync17(opts.worktreesDir)) return candidates;
5384
+ if (!opts.includeOrphans || !existsSync18(opts.worktreesDir)) return candidates;
5181
5385
  for (const runEntry of readdirSync6(opts.worktreesDir, { withFileTypes: true })) {
5182
5386
  if (!runEntry.isDirectory()) continue;
5183
- const runPath = path26.join(opts.worktreesDir, runEntry.name);
5387
+ const runPath = path28.join(opts.worktreesDir, runEntry.name);
5184
5388
  for (const workerEntry of readdirSync6(runPath, { withFileTypes: true })) {
5185
5389
  if (!workerEntry.isDirectory()) continue;
5186
- const worktreePath = path26.join(runPath, workerEntry.name);
5187
- const nm = path26.join(worktreePath, "node_modules");
5188
- if (!existsSync17(nm)) continue;
5189
- 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);
5190
5394
  if (seen.has(resolved)) continue;
5191
5395
  if (!isPathInside(resolved, opts.harnessRoot)) continue;
5192
5396
  seen.add(resolved);
@@ -5209,7 +5413,7 @@ function scanWorktreeCandidates(opts) {
5209
5413
  for (const entry of opts.index.values()) {
5210
5414
  if (opts.runIdFilter && entry.runId !== opts.runIdFilter) continue;
5211
5415
  const resolved = entry.worktreePath;
5212
- if (!existsSync17(resolved)) continue;
5416
+ if (!existsSync18(resolved)) continue;
5213
5417
  if (seen.has(resolved)) continue;
5214
5418
  seen.add(resolved);
5215
5419
  candidates.push({
@@ -5226,17 +5430,17 @@ function scanWorktreeCandidates(opts) {
5226
5430
  }
5227
5431
 
5228
5432
  // src/cleanup-worktree-index.ts
5229
- import path27 from "node:path";
5433
+ import path29 from "node:path";
5230
5434
  function buildWorktreeIndex() {
5231
5435
  const index = /* @__PURE__ */ new Map();
5232
5436
  for (const run of listRunRecords()) {
5233
5437
  for (const name of Object.keys(run.workers || {})) {
5234
- 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");
5235
5439
  const worker = readJson(workerPath, void 0);
5236
5440
  if (!worker?.worktreePath) continue;
5237
5441
  const status = computeWorkerStatus(worker, { base: run.base, baseCommit: run.baseCommit });
5238
- index.set(path27.resolve(worker.worktreePath), {
5239
- worktreePath: path27.resolve(worker.worktreePath),
5442
+ index.set(path29.resolve(worker.worktreePath), {
5443
+ worktreePath: path29.resolve(worker.worktreePath),
5240
5444
  runId: run.id,
5241
5445
  workerName: name,
5242
5446
  run,
@@ -5251,7 +5455,7 @@ function buildWorktreeIndex() {
5251
5455
  // src/cleanup.ts
5252
5456
  function resolveOptions(options = {}) {
5253
5457
  const harnessRoot = options.harnessRoot ? resolveUserPath(options.harnessRoot) : resolveHarnessRoot();
5254
- const { worktreesDir } = options.harnessRoot ? { worktreesDir: path28.join(harnessRoot, "worktrees") } : getHarnessPaths();
5458
+ const { worktreesDir } = options.harnessRoot ? { worktreesDir: path30.join(harnessRoot, "worktrees") } : getHarnessPaths();
5255
5459
  const execute = options.execute === true;
5256
5460
  const nodeModulesAgeMs = options.nodeModulesAgeMs ?? DEFAULT_NODE_MODULES_AGE_MS;
5257
5461
  const worktreesAgeMs = options.worktreesAgeMs ?? 0;
@@ -5295,7 +5499,7 @@ function runHarnessCleanup(options = {}) {
5295
5499
  actions.push({ ...candidate, executed: false, skipped: true, skipReason: pathSkip });
5296
5500
  continue;
5297
5501
  }
5298
- const worktreePath = path28.resolve(candidate.path, "..");
5502
+ const worktreePath = path30.resolve(candidate.path, "..");
5299
5503
  const indexed = index.get(worktreePath) ?? null;
5300
5504
  const guardReason = skipNodeModulesRemoval({
5301
5505
  indexed,
@@ -5311,7 +5515,7 @@ function runHarnessCleanup(options = {}) {
5311
5515
  actions.push(removeNodeModules(candidate, resolved.execute));
5312
5516
  }
5313
5517
  for (const candidate of scanWorktreeCandidates(scanOpts)) {
5314
- const indexed = index.get(path28.resolve(candidate.path)) ?? null;
5518
+ const indexed = index.get(path30.resolve(candidate.path)) ?? null;
5315
5519
  const guardReason = skipWorktreeRemoval({
5316
5520
  indexed,
5317
5521
  includeOrphans: resolved.includeOrphans,
@@ -5376,8 +5580,8 @@ function isPipelineCleanupEnabled() {
5376
5580
 
5377
5581
  // src/installed-package-versions.ts
5378
5582
  import { readFile } from "node:fs/promises";
5379
- import { homedir as homedir5 } from "node:os";
5380
- import path29 from "node:path";
5583
+ import { homedir as homedir6 } from "node:os";
5584
+ import path31 from "node:path";
5381
5585
  var MANAGED_PACKAGES = [
5382
5586
  "@kynver-app/runtime",
5383
5587
  "@kynver-app/openclaw-agent-os",
@@ -5391,13 +5595,13 @@ function unique(values) {
5391
5595
  return [...new Set(values.filter((value) => Boolean(value)))];
5392
5596
  }
5393
5597
  function moduleRoots() {
5394
- const home = homedir5();
5395
- const openClawPrefix = trim(process.env.KYNVER_OPENCLAW_NPM_ROOT) ?? trim(process.env.OPENCLAW_NPM_ROOT) ?? path29.join(home, ".openclaw", "npm");
5396
- 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"));
5397
5601
  return unique([
5398
- path29.join(openClawPrefix, "lib", "node_modules"),
5399
- path29.join(openClawPrefix, "node_modules"),
5400
- 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")
5401
5605
  ]);
5402
5606
  }
5403
5607
  async function readVersion(packageJsonPath) {
@@ -5413,7 +5617,7 @@ async function collectInstalledPackageVersions(observedAt = (/* @__PURE__ */ new
5413
5617
  const out = {};
5414
5618
  for (const packageName of MANAGED_PACKAGES) {
5415
5619
  for (const root of roots) {
5416
- const packageJsonPath = path29.join(root, packageName, "package.json");
5620
+ const packageJsonPath = path31.join(root, packageName, "package.json");
5417
5621
  const version = await readVersion(packageJsonPath);
5418
5622
  if (!version) continue;
5419
5623
  out[packageName] = { version, observedAt, path: packageJsonPath };
@@ -5429,7 +5633,7 @@ async function completeFinishedWorkers(runId, args) {
5429
5633
  const outcomes = [];
5430
5634
  for (const name of Object.keys(run.workers || {})) {
5431
5635
  const worker = readJson(
5432
- path30.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
5636
+ path32.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
5433
5637
  void 0
5434
5638
  );
5435
5639
  if (!worker?.taskId || worker.localOnly) continue;
@@ -5571,7 +5775,7 @@ async function runDaemon(args) {
5571
5775
  }
5572
5776
 
5573
5777
  // src/plan-progress.ts
5574
- import path31 from "node:path";
5778
+ import path33 from "node:path";
5575
5779
 
5576
5780
  // src/bounded-build/constants.ts
5577
5781
  var DEFAULT_BUILD_MEM_BUDGET_BYTES = 1536 * 1024 * 1024;
@@ -5857,7 +6061,7 @@ async function emitPlanProgress(args) {
5857
6061
  }
5858
6062
  function verifyPlanLocal(args) {
5859
6063
  const worktree = required(args.worktree ? String(args.worktree) : void 0, "worktree");
5860
- const cwd = path31.resolve(worktree);
6064
+ const cwd = path33.resolve(worktree);
5861
6065
  const summary = runHarnessVerifyCommands(cwd);
5862
6066
  const emitJson = args.json === true || args.json === "true";
5863
6067
  const payload = { passed: summary.passed, worktree: cwd, steps: summary.steps };
@@ -5906,9 +6110,9 @@ async function verifyPlan(args) {
5906
6110
  }
5907
6111
 
5908
6112
  // src/harness-verify-cli.ts
5909
- import path32 from "node:path";
6113
+ import path34 from "node:path";
5910
6114
  function runHarnessVerifyCli(args) {
5911
- 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"));
5912
6116
  const emitJson = args.json === true || args.json === "true" || args.emitJson === true || args.emitJson === "true";
5913
6117
  const commands = [];
5914
6118
  const rawCmd = args.command;
@@ -5952,7 +6156,7 @@ function runHarnessVerifyCli(args) {
5952
6156
  }
5953
6157
 
5954
6158
  // src/plan-persist-cli.ts
5955
- import { readFileSync as readFileSync8 } from "node:fs";
6159
+ import { readFileSync as readFileSync9 } from "node:fs";
5956
6160
  var OPERATIONS = ["create", "add_version", "update_metadata"];
5957
6161
  var FAILURE_KINDS = [
5958
6162
  "approval_guard",
@@ -5964,7 +6168,7 @@ var FAILURE_KINDS = [
5964
6168
  function readBodyArg(args) {
5965
6169
  const bodyFile = args.bodyFile ? String(args.bodyFile) : void 0;
5966
6170
  if (bodyFile) {
5967
- return { body: readFileSync8(bodyFile, "utf8"), bodyPathHint: bodyFile };
6171
+ return { body: readFileSync9(bodyFile, "utf8"), bodyPathHint: bodyFile };
5968
6172
  }
5969
6173
  const inline = args.body ? String(args.body) : void 0;
5970
6174
  if (inline) return { body: inline };
@@ -6052,7 +6256,7 @@ function runCleanupCli(args) {
6052
6256
  }
6053
6257
 
6054
6258
  // src/monitor/monitor.service.ts
6055
- import path34 from "node:path";
6259
+ import path36 from "node:path";
6056
6260
 
6057
6261
  // src/monitor/monitor.classify.ts
6058
6262
  function expectedLeaseOwner(runId) {
@@ -6108,11 +6312,11 @@ function classifyWorkerHealth(input) {
6108
6312
  }
6109
6313
 
6110
6314
  // src/monitor/monitor.store.ts
6111
- import { existsSync as existsSync18, mkdirSync as mkdirSync6, readdirSync as readdirSync7, unlinkSync as unlinkSync2 } from "node:fs";
6112
- 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";
6113
6317
  function monitorsDir() {
6114
6318
  const { harnessRoot } = getHarnessPaths();
6115
- const dir = path33.join(harnessRoot, "monitors");
6319
+ const dir = path35.join(harnessRoot, "monitors");
6116
6320
  mkdirSync6(dir, { recursive: true });
6117
6321
  return dir;
6118
6322
  }
@@ -6120,7 +6324,7 @@ function monitorIdFor(runId, workerName) {
6120
6324
  return workerName ? `${safeSlug(runId)}--${safeSlug(workerName)}` : safeSlug(runId);
6121
6325
  }
6122
6326
  function monitorPath(monitorId) {
6123
- return path33.join(monitorsDir(), `${monitorId}.json`);
6327
+ return path35.join(monitorsDir(), `${monitorId}.json`);
6124
6328
  }
6125
6329
  function loadMonitorSession(monitorId) {
6126
6330
  return readJson(monitorPath(monitorId), void 0);
@@ -6130,18 +6334,18 @@ function saveMonitorSession(session) {
6130
6334
  }
6131
6335
  function deleteMonitorSession(monitorId) {
6132
6336
  const file = monitorPath(monitorId);
6133
- if (!existsSync18(file)) return false;
6337
+ if (!existsSync19(file)) return false;
6134
6338
  unlinkSync2(file);
6135
6339
  return true;
6136
6340
  }
6137
6341
  function listMonitorSessions() {
6138
6342
  const dir = monitorsDir();
6139
- if (!existsSync18(dir)) return [];
6343
+ if (!existsSync19(dir)) return [];
6140
6344
  const entries = [];
6141
6345
  for (const name of readdirSync7(dir)) {
6142
6346
  if (!name.endsWith(".json")) continue;
6143
6347
  const session = readJson(
6144
- path33.join(dir, name),
6348
+ path35.join(dir, name),
6145
6349
  void 0
6146
6350
  );
6147
6351
  if (!session?.monitorId) continue;
@@ -6232,7 +6436,7 @@ async function fetchTaskLeasesForWorkers(input) {
6232
6436
  // src/monitor/monitor.service.ts
6233
6437
  function workerRecord2(runId, name) {
6234
6438
  return readJson(
6235
- path34.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
6439
+ path36.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
6236
6440
  void 0
6237
6441
  );
6238
6442
  }
@@ -6434,18 +6638,18 @@ async function runMonitorLoop(args) {
6434
6638
 
6435
6639
  // src/monitor/monitor-spawn.ts
6436
6640
  import { spawn as spawn4 } from "node:child_process";
6437
- import { closeSync as closeSync4, existsSync as existsSync19, openSync as openSync4 } from "node:fs";
6438
- import path35 from "node:path";
6439
- 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";
6440
6644
  function resolveDefaultCliPath2() {
6441
- return path35.join(fileURLToPath3(new URL(".", import.meta.url)), "..", "cli.js");
6645
+ return path37.join(fileURLToPath4(new URL(".", import.meta.url)), "..", "cli.js");
6442
6646
  }
6443
6647
  function spawnMonitorSidecar(opts) {
6444
6648
  const cliPath = opts.cliPath ?? resolveDefaultCliPath2();
6445
- if (!existsSync19(cliPath)) return void 0;
6649
+ if (!existsSync20(cliPath)) return void 0;
6446
6650
  const monitorId = monitorIdFor(opts.runId, opts.workerName);
6447
6651
  const { harnessRoot } = getHarnessPaths();
6448
- const logPath = path35.join(harnessRoot, "monitors", `${monitorId}.log`);
6652
+ const logPath = path37.join(harnessRoot, "monitors", `${monitorId}.log`);
6449
6653
  let logFd;
6450
6654
  try {
6451
6655
  logFd = openSync4(logPath, "a");
@@ -6565,12 +6769,12 @@ async function monitorTickCli(args) {
6565
6769
  }
6566
6770
 
6567
6771
  // src/doctor/runtime-takeover.ts
6568
- import path37 from "node:path";
6772
+ import path39 from "node:path";
6569
6773
 
6570
6774
  // src/doctor/runtime-takeover.probes.ts
6571
- import { accessSync, constants, existsSync as existsSync20, readFileSync as readFileSync9 } from "node:fs";
6572
- import { homedir as homedir6 } from "node:os";
6573
- 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";
6574
6778
  import { spawnSync as spawnSync6 } from "node:child_process";
6575
6779
  function captureCommand(bin, args) {
6576
6780
  try {
@@ -6599,7 +6803,7 @@ function tokenPrefix(token) {
6599
6803
  return trimmed.length <= 12 ? `${trimmed}\u2026` : `${trimmed.slice(0, 12)}\u2026`;
6600
6804
  }
6601
6805
  function isWritable(target) {
6602
- if (!existsSync20(target)) return false;
6806
+ if (!existsSync21(target)) return false;
6603
6807
  try {
6604
6808
  accessSync(target, constants.W_OK);
6605
6809
  return true;
@@ -6612,15 +6816,15 @@ var defaultRuntimeTakeoverProbes = {
6612
6816
  commandOnPath: (bin) => captureCommand(process.platform === "win32" ? "where" : "which", [bin]),
6613
6817
  kynverVersion: (bin) => captureCommand(bin, ["--version"]),
6614
6818
  loadConfig: () => loadUserConfig(),
6615
- configFilePath: () => path36.join(homedir6(), ".kynver", "config.json"),
6616
- credentialsFilePath: () => path36.join(homedir6(), ".kynver", "credentials"),
6819
+ configFilePath: () => path38.join(homedir7(), ".kynver", "config.json"),
6820
+ credentialsFilePath: () => path38.join(homedir7(), ".kynver", "credentials"),
6617
6821
  readCredentials: () => {
6618
- const credPath = path36.join(homedir6(), ".kynver", "credentials");
6619
- if (!existsSync20(credPath)) {
6822
+ const credPath = path38.join(homedir7(), ".kynver", "credentials");
6823
+ if (!existsSync21(credPath)) {
6620
6824
  return { hasApiKey: false };
6621
6825
  }
6622
6826
  try {
6623
- const parsed = JSON.parse(readFileSync9(credPath, "utf8"));
6827
+ const parsed = JSON.parse(readFileSync10(credPath, "utf8"));
6624
6828
  return {
6625
6829
  hasApiKey: Boolean(parsed.apiKey?.trim()),
6626
6830
  runnerTokenPrefix: tokenPrefix(parsed.runnerToken),
@@ -6638,20 +6842,119 @@ var defaultRuntimeTakeoverProbes = {
6638
6842
  openclawCronSecret: Boolean(process.env.OPENCLAW_CRON_SECRET?.trim()),
6639
6843
  kynverHarnessRoot: process.env.KYNVER_HARNESS_ROOT?.trim() || void 0,
6640
6844
  opusHarnessRoot: process.env.OPUS_HARNESS_ROOT?.trim() || void 0,
6641
- kynverSchedulerProvider: process.env.KYNVER_SCHEDULER_PROVIDER?.trim() || void 0
6845
+ kynverSchedulerProvider: process.env.KYNVER_SCHEDULER_PROVIDER?.trim() || void 0,
6846
+ qstashTokenPresent: Boolean(process.env.QSTASH_TOKEN?.trim()),
6847
+ kynverHostedDeployment: (() => {
6848
+ const v = process.env.KYNVER_HOSTED_DEPLOYMENT?.trim().toLowerCase();
6849
+ return v === "1" || v === "true" || v === "yes";
6850
+ })()
6642
6851
  }),
6643
6852
  harnessRoot: () => resolveHarnessRoot(),
6644
- legacyOpenclawHarnessRoot: () => path36.join(homedir6(), ".openclaw", "harness"),
6645
- pathExists: (target) => existsSync20(target),
6853
+ legacyOpenclawHarnessRoot: () => path38.join(homedir7(), ".openclaw", "harness"),
6854
+ pathExists: (target) => existsSync21(target),
6646
6855
  pathWritable: (target) => isWritable(target),
6647
6856
  vercelVersion: () => captureCommand("vercel", ["--version"]),
6648
6857
  vercelWhoami: () => captureCommand("vercel", ["whoami"])
6649
6858
  };
6650
6859
 
6651
- // src/doctor/runtime-takeover.ts
6860
+ // src/doctor/runtime-takeover-scheduler.ts
6652
6861
  function check(partial) {
6653
6862
  return partial;
6654
6863
  }
6864
+ function assessRuntimeTakeoverScheduler(env, ctx) {
6865
+ const runnerOpenclaw = env.kynverSchedulerProvider === "openclaw-cron";
6866
+ const runnerQstash = env.kynverSchedulerProvider === "qstash";
6867
+ const hostedDeployment = Boolean(env.qstashTokenPresent) || Boolean(env.kynverHostedDeployment);
6868
+ const daemonDispatchReady = Boolean(ctx.agentOsId?.trim()) && Boolean(ctx.apiBaseUrl?.trim()) && ctx.hasScopedRunnerToken;
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
+ }
6899
+ if (runnerOpenclaw) {
6900
+ return check({
6901
+ id: "hotspot_openclaw_scheduler",
6902
+ label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
6903
+ status: "warn",
6904
+ summary: "KYNVER_SCHEDULER_PROVIDER=openclaw-cron on this runner \u2014 hosted dispatch still depends on the OpenClaw local-cron adapter",
6905
+ 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.",
6906
+ details: { schedulerProvider: env.kynverSchedulerProvider ?? null, hostedDeployment }
6907
+ });
6908
+ }
6909
+ if (deploymentOpenclaw || deploymentNeedsQstash) {
6910
+ return check({
6911
+ id: "hotspot_openclaw_scheduler",
6912
+ label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
6913
+ status: "warn",
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",
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.",
6916
+ details: {
6917
+ schedulerProvider: env.kynverSchedulerProvider ?? null,
6918
+ qstashTokenPresent: env.qstashTokenPresent ?? false,
6919
+ hostedDeployment
6920
+ }
6921
+ });
6922
+ }
6923
+ if (hostedDeployment && env.qstashTokenPresent && !env.kynverSchedulerProvider) {
6924
+ return check({
6925
+ id: "hotspot_openclaw_scheduler",
6926
+ label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
6927
+ status: "pass",
6928
+ summary: "QSTASH_TOKEN present; hosted scheduler auto-selects qstash (explicit KYNVER_SCHEDULER_PROVIDER=qstash optional)",
6929
+ details: {
6930
+ schedulerProvider: null,
6931
+ qstashTokenPresent: true,
6932
+ hostedDeployment
6933
+ }
6934
+ });
6935
+ }
6936
+ if (!env.kynverSchedulerProvider) {
6937
+ return check({
6938
+ id: "hotspot_openclaw_scheduler",
6939
+ label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
6940
+ status: "pass",
6941
+ summary: "No KYNVER_SCHEDULER_PROVIDER on runner (expected) \u2014 finish runner setup so daemon pipeline-tick owns dispatch",
6942
+ details: { schedulerProvider: null, dispatchPath: "kynver-daemon-pipeline-tick-pending" }
6943
+ });
6944
+ }
6945
+ return check({
6946
+ id: "hotspot_openclaw_scheduler",
6947
+ label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
6948
+ status: "pass",
6949
+ summary: `KYNVER_SCHEDULER_PROVIDER=${env.kynverSchedulerProvider}`,
6950
+ details: { schedulerProvider: env.kynverSchedulerProvider ?? null }
6951
+ });
6952
+ }
6953
+
6954
+ // src/doctor/runtime-takeover.ts
6955
+ function check2(partial) {
6956
+ return partial;
6957
+ }
6655
6958
  function summarizeCounts(sections) {
6656
6959
  const counts = { pass: 0, warn: 0, fail: 0 };
6657
6960
  for (const section of sections) {
@@ -6668,14 +6971,14 @@ function assessCliPackage(probes) {
6668
6971
  const firstPath = onPath ? which.stdout.split(/\r?\n/)[0]?.trim() : void 0;
6669
6972
  const displayCliPath = firstPath ? displayUserPath(firstPath) : void 0;
6670
6973
  const checks = [
6671
- check({
6974
+ check2({
6672
6975
  id: "cli_running_version",
6673
6976
  label: "Running @kynver-app/runtime version",
6674
6977
  status: "pass",
6675
6978
  summary: `@kynver-app/runtime ${runningVersion}`,
6676
6979
  details: { version: runningVersion }
6677
6980
  }),
6678
- check({
6981
+ check2({
6679
6982
  id: "cli_on_path",
6680
6983
  label: "kynver executable on PATH",
6681
6984
  status: onPath ? "pass" : "warn",
@@ -6689,7 +6992,7 @@ function assessCliPackage(probes) {
6689
6992
  const installedVersion = versionProbe.stdout.replace(/^kynver\s+/i, "").trim() || void 0;
6690
6993
  const versionMatch = versionProbe.ok && (!installedVersion || installedVersion === runningVersion);
6691
6994
  checks.push(
6692
- check({
6995
+ check2({
6693
6996
  id: "cli_installed_version",
6694
6997
  label: "Installed kynver CLI version matches running package",
6695
6998
  status: versionMatch ? "pass" : "warn",
@@ -6707,7 +7010,7 @@ function assessUserConfig(probes) {
6707
7010
  const exists = probes.pathExists(configPath);
6708
7011
  const config = probes.loadConfig();
6709
7012
  const checks = [
6710
- check({
7013
+ check2({
6711
7014
  id: "config_file",
6712
7015
  label: "~/.kynver/config.json present",
6713
7016
  status: exists ? "pass" : "fail",
@@ -6719,10 +7022,16 @@ function assessUserConfig(probes) {
6719
7022
  if (exists) {
6720
7023
  const apiBaseUrl = config.apiBaseUrl?.trim();
6721
7024
  const agentOsId = config.agentOsId?.trim();
6722
- const defaultRepo = config.defaultRepo?.trim();
6723
- 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
+ }
6724
7033
  checks.push(
6725
- check({
7034
+ check2({
6726
7035
  id: "config_api_base_url",
6727
7036
  label: "Default API base URL",
6728
7037
  status: apiBaseUrl ? "pass" : "warn",
@@ -6730,7 +7039,7 @@ function assessUserConfig(probes) {
6730
7039
  remediation: apiBaseUrl ? void 0 : "Set `apiBaseUrl` via `kynver setup --api-base-url https://\u2026`.",
6731
7040
  details: { apiBaseUrl: apiBaseUrl ?? null }
6732
7041
  }),
6733
- check({
7042
+ check2({
6734
7043
  id: "config_agent_os_id",
6735
7044
  label: "Default AgentOS id",
6736
7045
  status: agentOsId ? "pass" : "warn",
@@ -6738,14 +7047,16 @@ function assessUserConfig(probes) {
6738
7047
  remediation: agentOsId ? void 0 : "Set `agentOsId` via `kynver setup --agent-os-id <uuid>`.",
6739
7048
  details: { agentOsId: agentOsId ?? null, agentOsSlug: config.agentOsSlug ?? null }
6740
7049
  }),
6741
- check({
7050
+ check2({
6742
7051
  id: "config_default_repo",
6743
7052
  label: "Default repo path",
6744
- status: defaultRepo ? "pass" : "warn",
6745
- summary: displayDefaultRepo ?? "Not set (pass --repo on `kynver run create`)",
6746
- 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,
6747
7056
  details: {
6748
- defaultRepo: displayDefaultRepo,
7057
+ defaultRepo: formatted?.defaultRepo ?? null,
7058
+ source: formatted?.source ?? null,
7059
+ persistedInConfig: formatted?.persistedInConfig ?? false,
6749
7060
  harnessRoot: config.harnessRoot ? redactHomePath(config.harnessRoot) : null
6750
7061
  }
6751
7062
  })
@@ -6766,7 +7077,7 @@ function assessRunnerToken(probes) {
6766
7077
  const scopedSaved = Boolean(savedToken) && (!targetAgentOsId || !savedAgentOsId || savedAgentOsId === targetAgentOsId);
6767
7078
  const hasScoped = Boolean(envToken?.startsWith("krc1.")) || scopedSaved && savedToken?.startsWith("krc1.");
6768
7079
  const checks = [
6769
- check({
7080
+ check2({
6770
7081
  id: "runner_token_scoped",
6771
7082
  label: "Scoped runner token (krc1.*) ready",
6772
7083
  status: hasScoped ? "pass" : "fail",
@@ -6779,7 +7090,7 @@ function assessRunnerToken(probes) {
6779
7090
  credentialsPath: displayCredPath
6780
7091
  }
6781
7092
  }),
6782
- check({
7093
+ check2({
6783
7094
  id: "runner_token_agent_os_match",
6784
7095
  label: "Saved runner token matches configured agentOsId",
6785
7096
  status: !savedToken || !targetAgentOsId || !savedAgentOsId || savedAgentOsId === targetAgentOsId ? "pass" : "warn",
@@ -6787,7 +7098,7 @@ function assessRunnerToken(probes) {
6787
7098
  remediation: savedToken && targetAgentOsId && savedAgentOsId && savedAgentOsId !== targetAgentOsId ? "`kynver runner credential --agent-os-id <configured-id>` to mint a workspace-bound token." : void 0,
6788
7099
  details: { configuredAgentOsId: targetAgentOsId ?? null, savedAgentOsId: savedAgentOsId ?? null }
6789
7100
  }),
6790
- check({
7101
+ check2({
6791
7102
  id: "runner_api_key_for_refresh",
6792
7103
  label: "API key available for token refresh",
6793
7104
  status: creds.hasApiKey || Boolean(process.env.KYNVER_API_KEY?.trim()) ? "pass" : "warn",
@@ -6806,7 +7117,7 @@ function assessVercelCli(probes) {
6806
7117
  id: "vercel_cli",
6807
7118
  label: "Vercel CLI",
6808
7119
  checks: [
6809
- check({
7120
+ check2({
6810
7121
  id: "vercel_installed",
6811
7122
  label: "Vercel CLI installed",
6812
7123
  status: installed ? "pass" : "warn",
@@ -6814,7 +7125,7 @@ function assessVercelCli(probes) {
6814
7125
  remediation: installed ? void 0 : "Install Vercel CLI (`npm i -g vercel`) for deploy evidence and env pulls.",
6815
7126
  details: { stderr: version.stderr || null }
6816
7127
  }),
6817
- check({
7128
+ check2({
6818
7129
  id: "vercel_authenticated",
6819
7130
  label: "Vercel CLI authenticated",
6820
7131
  status: !installed ? "warn" : whoami.ok ? "pass" : "warn",
@@ -6827,8 +7138,8 @@ function assessVercelCli(probes) {
6827
7138
  }
6828
7139
  function assessHarnessDirs(probes) {
6829
7140
  const harnessRoot = probes.harnessRoot();
6830
- const runsDir = path37.join(harnessRoot, "runs");
6831
- const worktreesDir = path37.join(harnessRoot, "worktrees");
7141
+ const runsDir = path39.join(harnessRoot, "runs");
7142
+ const worktreesDir = path39.join(harnessRoot, "worktrees");
6832
7143
  const displayHarnessRoot = redactHomePath(harnessRoot);
6833
7144
  const displayRunsDir = redactHomePath(runsDir);
6834
7145
  const displayWorktreesDir = redactHomePath(worktreesDir);
@@ -6838,14 +7149,14 @@ function assessHarnessDirs(probes) {
6838
7149
  id: "harness_dirs",
6839
7150
  label: "Harness / daemon directories",
6840
7151
  checks: [
6841
- check({
7152
+ check2({
6842
7153
  id: "harness_root",
6843
7154
  label: "Harness root resolved",
6844
7155
  status: "pass",
6845
7156
  summary: displayHarnessRoot,
6846
7157
  details: { harnessRoot: displayHarnessRoot }
6847
7158
  }),
6848
- check({
7159
+ check2({
6849
7160
  id: "harness_runs_dir",
6850
7161
  label: "Runs directory ready",
6851
7162
  status: runsExists && probes.pathWritable(runsDir) ? "pass" : runsExists ? "warn" : "warn",
@@ -6853,7 +7164,7 @@ function assessHarnessDirs(probes) {
6853
7164
  remediation: runsExists && !probes.pathWritable(runsDir) ? `Fix permissions on ${displayRunsDir} or set KYNVER_HARNESS_ROOT to a writable path.` : void 0,
6854
7165
  details: { runsDir: displayRunsDir, exists: runsExists, writable: probes.pathWritable(runsDir) }
6855
7166
  }),
6856
- check({
7167
+ check2({
6857
7168
  id: "harness_worktrees_dir",
6858
7169
  label: "Worktrees directory ready",
6859
7170
  status: worktreesExists && probes.pathWritable(worktreesDir) ? "pass" : "warn",
@@ -6874,7 +7185,7 @@ function assessCallbackAuth(probes) {
6874
7185
  const creds = probes.readCredentials();
6875
7186
  const savedScoped = creds.runnerTokenPrefix?.startsWith("krc1.");
6876
7187
  const checks = [
6877
- check({
7188
+ check2({
6878
7189
  id: "callback_base_url",
6879
7190
  label: "Callback base URL configured",
6880
7191
  status: baseUrl ? usingLegacyBase ? "warn" : "pass" : "fail",
@@ -6885,7 +7196,7 @@ function assessCallbackAuth(probes) {
6885
7196
  baseUrl: baseUrl ?? null
6886
7197
  }
6887
7198
  }),
6888
- check({
7199
+ check2({
6889
7200
  id: "callback_auth_mode",
6890
7201
  label: "Callback auth uses scoped runner token",
6891
7202
  status: envScoped || savedScoped ? "pass" : legacySecret ? "warn" : "fail",
@@ -6901,15 +7212,22 @@ function assessCallbackAuth(probes) {
6901
7212
  }
6902
7213
  function assessOpenclawHotspots(probes) {
6903
7214
  const env = probes.envSnapshot();
7215
+ const config = probes.loadConfig();
7216
+ const creds = probes.readCredentials();
6904
7217
  const harnessRoot = probes.harnessRoot();
6905
7218
  const legacyRoot = probes.legacyOpenclawHarnessRoot();
6906
7219
  const displayHarnessRoot = redactHomePath(harnessRoot);
6907
7220
  const displayLegacyRoot = redactHomePath(legacyRoot);
6908
7221
  const displayOpusHarnessRoot = env.opusHarnessRoot ? redactHomePath(env.opusHarnessRoot) : null;
6909
7222
  const legacyHarnessActive = harnessRoot === legacyRoot && probes.pathExists(legacyRoot);
6910
- const schedulerOpenclaw = !env.kynverSchedulerProvider || env.kynverSchedulerProvider === "openclaw-cron";
7223
+ const targetAgentOsId = config.agentOsId?.trim();
7224
+ const envToken = env.kynverRunnerTokenPrefix;
7225
+ const savedToken = creds.runnerTokenPrefix;
7226
+ const savedAgentOsId = creds.runnerTokenAgentOsId;
7227
+ const scopedSaved = Boolean(savedToken) && (!targetAgentOsId || !savedAgentOsId || savedAgentOsId === targetAgentOsId);
7228
+ const hasScopedRunnerToken = Boolean(envToken?.startsWith("krc1.")) || scopedSaved && Boolean(savedToken?.startsWith("krc1."));
6911
7229
  const checks = [
6912
- check({
7230
+ check2({
6913
7231
  id: "hotspot_legacy_harness_root",
6914
7232
  label: "Legacy ~/.openclaw/harness still active",
6915
7233
  status: legacyHarnessActive ? "warn" : "pass",
@@ -6917,7 +7235,7 @@ function assessOpenclawHotspots(probes) {
6917
7235
  remediation: legacyHarnessActive ? "Set KYNVER_HARNESS_ROOT=~/.kynver/harness (or run setup), migrate artifacts, retire OPUS_HARNESS_ROOT." : env.opusHarnessRoot ? "Prefer KYNVER_HARNESS_ROOT over OPUS_HARNESS_ROOT." : void 0,
6918
7236
  details: { harnessRoot: displayHarnessRoot, legacyRoot: displayLegacyRoot, opusHarnessRoot: displayOpusHarnessRoot }
6919
7237
  }),
6920
- check({
7238
+ check2({
6921
7239
  id: "hotspot_openclaw_env_secrets",
6922
7240
  label: "OpenClaw deployment secrets in runner env",
6923
7241
  status: env.openclawCronSecret || env.openclawCronFireBaseUrl ? "warn" : "pass",
@@ -6927,15 +7245,12 @@ function assessOpenclawHotspots(probes) {
6927
7245
  ].filter(Boolean).join("; ") : "No OpenClaw cron env overrides on this runner",
6928
7246
  remediation: env.openclawCronSecret || env.openclawCronFireBaseUrl ? "Move to KYNVER_API_URL + scoped runner tokens; unset OpenClaw cron env on user-hosted runners." : void 0
6929
7247
  }),
6930
- check({
6931
- id: "hotspot_openclaw_scheduler",
6932
- label: "openclaw-cron scheduler dependency (deployment)",
6933
- status: schedulerOpenclaw ? "warn" : "pass",
6934
- summary: schedulerOpenclaw ? env.kynverSchedulerProvider === "openclaw-cron" ? "KYNVER_SCHEDULER_PROVIDER=openclaw-cron \u2014 AgentOS ticks still routed via OpenClaw local cron adapter" : "KYNVER_SCHEDULER_PROVIDER unset \u2014 server may fall back to openclaw-cron when QStash is absent" : `KYNVER_SCHEDULER_PROVIDER=${env.kynverSchedulerProvider}`,
6935
- remediation: schedulerOpenclaw ? "On Kynver-hosted scheduler: set KYNVER_SCHEDULER_PROVIDER=qstash where QStash is configured; retire openclaw-cron stub when runtime daemon owns dispatch." : void 0,
6936
- details: { schedulerProvider: env.kynverSchedulerProvider ?? null }
7248
+ assessRuntimeTakeoverScheduler(env, {
7249
+ agentOsId: targetAgentOsId ?? null,
7250
+ apiBaseUrl: config.apiBaseUrl?.trim() ?? env.kynverApiUrl ?? null,
7251
+ hasScopedRunnerToken
6937
7252
  }),
6938
- check({
7253
+ check2({
6939
7254
  id: "hotspot_lease_source_names",
6940
7255
  label: "Harness lease/completion source names",
6941
7256
  status: "pass",
@@ -7027,9 +7342,9 @@ function usage(code = 0) {
7027
7342
  "Usage:",
7028
7343
  " kynver login --api-key KEY",
7029
7344
  " kynver runner credential [--agent-os-id ID] [--base-url URL]",
7030
- " 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]",
7031
7346
  " kynver daemon --run RUN_ID --agent-os-id AOS_ID [--execute] [--interval-ms MS]",
7032
- " kynver run create --repo /path/repo [--name name] [--base origin/main]",
7347
+ " kynver run create [--repo /path/repo] [--name name] [--base origin/main]",
7033
7348
  " kynver run list",
7034
7349
  " kynver run status --run RUN_ID",
7035
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 /]",
@@ -7122,7 +7437,7 @@ async function main(argv = process.argv.slice(2)) {
7122
7437
  if (scope === "monitor" && action === "run-loop") return void await monitorRunLoopCli(args);
7123
7438
  unknownCommand(scope, action);
7124
7439
  }
7125
- 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));
7126
7441
  if (isCliEntry) {
7127
7442
  void main().catch((error) => {
7128
7443
  console.error(error);
@@ -7445,6 +7760,8 @@ export {
7445
7760
  computeWorkerStatus,
7446
7761
  createRun,
7447
7762
  deriveRunStatus,
7763
+ discoverDefaultRepo,
7764
+ discoverDefaultRepoCandidates,
7448
7765
  dispatchRun,
7449
7766
  drainPlanOutbox,
7450
7767
  ensurePrReadyHandoff,
@@ -7452,13 +7769,16 @@ export {
7452
7769
  extractPlanOutboxFromTask,
7453
7770
  extractPrUrlFromText,
7454
7771
  formatPlanOutboxHandoffBlock,
7772
+ formatResolvedDefaultRepo,
7455
7773
  getHarnessPaths,
7456
7774
  getMonitorStatus,
7775
+ gitRepoRoot,
7457
7776
  hashPlanBody,
7458
7777
  isDashboardVercelUrl,
7459
7778
  isEngagementRequiredSkip,
7460
7779
  isFinishedWorkerStatus,
7461
7780
  isForbiddenWorkerEnvKey,
7781
+ isKynverMonorepoRoot,
7462
7782
  isLandingBlockedWorkerStatus,
7463
7783
  isSystemdRunAvailable,
7464
7784
  isTerminalHeartbeatPhase,
@@ -7477,6 +7797,7 @@ export {
7477
7797
  parseClaudeStream,
7478
7798
  parseHarnessStream,
7479
7799
  parseHeartbeat,
7800
+ persistDefaultRepo,
7480
7801
  persistPlan,
7481
7802
  pickVercelStatusContext,
7482
7803
  postJson,
@@ -7485,9 +7806,11 @@ export {
7485
7806
  reconcileRunsCli,
7486
7807
  reconcileStaleWorkers,
7487
7808
  redactHarness,
7809
+ remediateDefaultRepo,
7488
7810
  resolveBaseUrl,
7489
7811
  resolveCallbackSecret,
7490
7812
  resolveCallbackSecretWithMint,
7813
+ resolveDefaultRepo,
7491
7814
  resolveHarnessRoot,
7492
7815
  resolveVercelInspectTarget,
7493
7816
  runBoundedBuildCheck,