@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/cli.js CHANGED
@@ -2,43 +2,25 @@
2
2
 
3
3
  // src/cli.ts
4
4
  import { mkdirSync as mkdirSync7, realpathSync } from "node:fs";
5
- import { fileURLToPath as fileURLToPath4 } from "node:url";
5
+ import { fileURLToPath as fileURLToPath5 } from "node:url";
6
6
 
7
7
  // src/config.ts
8
- import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
8
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
9
+ import { homedir as homedir3 } from "node:os";
10
+ import path4 from "node:path";
11
+
12
+ // src/default-repo-discovery.ts
13
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
9
14
  import { homedir as homedir2 } from "node:os";
10
15
  import path3 from "node:path";
16
+ import { fileURLToPath } from "node:url";
11
17
 
12
- // src/path-values.ts
13
- import { homedir } from "node:os";
14
- import path from "node:path";
15
- function expandHomePath(value) {
16
- if (value === "~") return homedir();
17
- if (value.startsWith("~/") || value.startsWith("~\\")) {
18
- return path.join(homedir(), value.slice(2));
19
- }
20
- return value;
21
- }
22
- function resolveUserPath(value) {
23
- return path.resolve(expandHomePath(value));
24
- }
25
- function redactHomePath(value) {
26
- const expanded = expandHomePath(value);
27
- const resolved = path.resolve(expanded);
28
- const home = path.resolve(homedir());
29
- if (resolved === home) return "~";
30
- if (resolved.startsWith(`${home}${path.sep}`)) {
31
- return `~/${path.relative(home, resolved).split(path.sep).join("/")}`;
32
- }
33
- return resolved.replace(/^\/home\/[^/]+(?=\/|$)/, "~").replace(/^\/Users\/[^/]+(?=\/|$)/, "~");
34
- }
35
- function displayUserPath(value) {
36
- return redactHomePath(value);
37
- }
18
+ // src/git.ts
19
+ import { spawnSync } from "node:child_process";
38
20
 
39
21
  // src/util.ts
40
22
  import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
41
- import path2 from "node:path";
23
+ import path from "node:path";
42
24
  function fail(message) {
43
25
  console.error(message);
44
26
  process.exit(1);
@@ -67,7 +49,7 @@ function readJson(file, fallback) {
67
49
  }
68
50
  }
69
51
  function writeJson(file, value) {
70
- mkdirSync(path2.dirname(file), { recursive: true });
52
+ mkdirSync(path.dirname(file), { recursive: true });
71
53
  writeFileSync(file, `${JSON.stringify(value, null, 2)}
72
54
  `);
73
55
  }
@@ -103,7 +85,7 @@ function tailFile(file, lines) {
103
85
  return data.split("\n").slice(-lines).join("\n");
104
86
  }
105
87
  function readMaybeFile(file) {
106
- return file ? readFileSync(path2.resolve(file), "utf8") : "";
88
+ return file ? readFileSync(path.resolve(file), "utf8") : "";
107
89
  }
108
90
  function listRunIds(runsDir) {
109
91
  if (!existsSync(runsDir)) return [];
@@ -145,14 +127,270 @@ function secsAgo(ms) {
145
127
  return Math.max(0, Math.round((Date.now() - ms) / 1e3));
146
128
  }
147
129
 
130
+ // src/worker-env.ts
131
+ var FORBIDDEN_WORKER_ENV_KEYS = [
132
+ "ANTHROPIC_API_KEY",
133
+ "ANALYST_API_KEY",
134
+ "RECRUITER_API_KEY",
135
+ "AUTH_SECRET",
136
+ "NEXTAUTH_SECRET",
137
+ "DATABASE_URL",
138
+ "PRODUCTION_DATABASE_URL",
139
+ "REDIS_URL",
140
+ "GOOGLE_CLIENT_SECRET",
141
+ "GITHUB_CLIENT_SECRET",
142
+ "KYNVER_API_KEY",
143
+ "KYNVER_SERVICE_SECRET",
144
+ "KYNVER_RUNTIME_SECRET",
145
+ "OPENCLAW_CRON_SECRET",
146
+ "QSTASH_TOKEN",
147
+ "QSTASH_CURRENT_SIGNING_KEY",
148
+ "QSTASH_NEXT_SIGNING_KEY",
149
+ "TOOL_SECRETS_KEK",
150
+ "TOOL_EXECUTOR_DISPATCH_SECRET",
151
+ "CLOUDFLARE_API_TOKEN",
152
+ "STRIPE_SECRET_KEY",
153
+ "STRIPE_WEBHOOK_SECRET",
154
+ "STRIPE_IDENTITY_WEBHOOK_SECRET",
155
+ "VOYAGE_API_KEY",
156
+ "PERPLEXITY_API_KEY",
157
+ "FRED_API_KEY",
158
+ "FMP_API_KEY",
159
+ "CURSOR_API_KEY"
160
+ ];
161
+ var FORBIDDEN_KEY_SET = new Set(FORBIDDEN_WORKER_ENV_KEYS);
162
+ var FORBIDDEN_SUFFIXES = ["_SECRET", "_API_KEY"];
163
+ function isForbiddenWorkerEnvKey(key) {
164
+ if (FORBIDDEN_KEY_SET.has(key)) return true;
165
+ return FORBIDDEN_SUFFIXES.some((suffix) => key.endsWith(suffix));
166
+ }
167
+ function scrubWorkerEnv(env) {
168
+ const next = { ...env };
169
+ for (const key of Object.keys(next)) {
170
+ if (isForbiddenWorkerEnvKey(key)) delete next[key];
171
+ }
172
+ return next;
173
+ }
174
+
175
+ // src/git.ts
176
+ function git(cwd, args, options = {}) {
177
+ const res = spawnSync("git", args, { cwd, encoding: "utf8" });
178
+ if (res.status !== 0 && !options.allowFailure) {
179
+ const message = `git ${args.join(" ")} failed: ${res.stderr || res.stdout}`;
180
+ if (options.throwError) throw new Error(message);
181
+ fail(message);
182
+ }
183
+ return res.stdout || "";
184
+ }
185
+ function ensureGitRepo(repo) {
186
+ git(repo, ["rev-parse", "--show-toplevel"]);
187
+ }
188
+ function gitStatusShort(worktreePath) {
189
+ return git(worktreePath, ["status", "--short"], { allowFailure: true }).split("\n").map((line) => line.trim()).filter(Boolean);
190
+ }
191
+ function gitCapture(cwd, args) {
192
+ try {
193
+ const res = spawnSync("git", args, { cwd, encoding: "utf8" });
194
+ return {
195
+ status: res.status,
196
+ stdout: res.stdout || "",
197
+ stderr: res.stderr || "",
198
+ error: res.error ? res.error.message : null
199
+ };
200
+ } catch (error) {
201
+ return {
202
+ status: null,
203
+ stdout: "",
204
+ stderr: "",
205
+ error: error.message
206
+ };
207
+ }
208
+ }
209
+ function gitIsAncestor(cwd, ancestor, descendant) {
210
+ const res = gitCapture(cwd, ["merge-base", "--is-ancestor", ancestor, descendant]);
211
+ if (res.status === 0) return { isAncestor: true, error: null };
212
+ if (res.status === 1) return { isAncestor: false, error: null };
213
+ return { isAncestor: null, error: res.error || res.stderr || res.stdout || `git exited ${res.status}` };
214
+ }
215
+ function computeGitAncestry(worktreePath, baseOrOptions = "origin/main") {
216
+ const options = typeof baseOrOptions === "string" ? { base: baseOrOptions } : baseOrOptions;
217
+ const baseLabel = options.baseCommit?.trim() || options.base?.trim() || "origin/main";
218
+ const pinnedBaseCommit = options.baseCommit?.trim() || null;
219
+ if (!worktreePath) {
220
+ return unknownAncestry(baseLabel, "missing worktree path");
221
+ }
222
+ const head = gitCapture(worktreePath, ["rev-parse", "HEAD"]);
223
+ if (head.status !== 0) {
224
+ return unknownAncestry(baseLabel, head.error || head.stderr || head.stdout || "failed to resolve HEAD");
225
+ }
226
+ let baseSha;
227
+ if (pinnedBaseCommit) {
228
+ baseSha = pinnedBaseCommit;
229
+ } else {
230
+ const baseHead = gitCapture(worktreePath, ["rev-parse", baseLabel]);
231
+ if (baseHead.status !== 0) {
232
+ return unknownAncestry(
233
+ baseLabel,
234
+ baseHead.error || baseHead.stderr || baseHead.stdout || `failed to resolve ${baseLabel}`,
235
+ head.stdout.trim()
236
+ );
237
+ }
238
+ baseSha = baseHead.stdout.trim();
239
+ }
240
+ const headSha = head.stdout.trim();
241
+ if (headSha === baseSha) {
242
+ return {
243
+ checked: true,
244
+ base: baseLabel,
245
+ head: headSha,
246
+ baseHead: baseSha,
247
+ baseIsAncestorOfHead: true,
248
+ headIsAncestorOfBase: true,
249
+ relation: "synced"
250
+ };
251
+ }
252
+ const baseIsAncestorOfHead = gitIsAncestor(worktreePath, baseSha, headSha);
253
+ const headIsAncestorOfBase = gitIsAncestor(worktreePath, headSha, baseSha);
254
+ const error = baseIsAncestorOfHead.error || headIsAncestorOfBase.error || void 0;
255
+ if (baseIsAncestorOfHead.isAncestor == null || headIsAncestorOfBase.isAncestor == null) {
256
+ return {
257
+ checked: false,
258
+ base: baseLabel,
259
+ head: headSha,
260
+ baseHead: baseSha,
261
+ baseIsAncestorOfHead: baseIsAncestorOfHead.isAncestor,
262
+ headIsAncestorOfBase: headIsAncestorOfBase.isAncestor,
263
+ relation: "unknown",
264
+ ...error ? { error } : {}
265
+ };
266
+ }
267
+ const relation = baseIsAncestorOfHead.isAncestor ? "ahead" : headIsAncestorOfBase.isAncestor ? "merged" : "diverged";
268
+ return {
269
+ checked: true,
270
+ base: baseLabel,
271
+ head: headSha,
272
+ baseHead: baseSha,
273
+ baseIsAncestorOfHead: baseIsAncestorOfHead.isAncestor,
274
+ headIsAncestorOfBase: headIsAncestorOfBase.isAncestor,
275
+ relation,
276
+ ...error ? { error } : {}
277
+ };
278
+ }
279
+ function unknownAncestry(base, error, head = null) {
280
+ return {
281
+ checked: false,
282
+ base,
283
+ head,
284
+ baseHead: null,
285
+ baseIsAncestorOfHead: null,
286
+ headIsAncestorOfBase: null,
287
+ relation: "unknown",
288
+ error
289
+ };
290
+ }
291
+
292
+ // src/path-values.ts
293
+ import { homedir } from "node:os";
294
+ import path2 from "node:path";
295
+ function expandHomePath(value) {
296
+ if (value === "~") return homedir();
297
+ if (value.startsWith("~/") || value.startsWith("~\\")) {
298
+ return path2.join(homedir(), value.slice(2));
299
+ }
300
+ return value;
301
+ }
302
+ function resolveUserPath(value) {
303
+ return path2.resolve(expandHomePath(value));
304
+ }
305
+ function redactHomePath(value) {
306
+ const expanded = expandHomePath(value);
307
+ const resolved = path2.resolve(expanded);
308
+ const home = path2.resolve(homedir());
309
+ if (resolved === home) return "~";
310
+ if (resolved.startsWith(`${home}${path2.sep}`)) {
311
+ return `~/${path2.relative(home, resolved).split(path2.sep).join("/")}`;
312
+ }
313
+ return resolved.replace(/^\/home\/[^/]+(?=\/|$)/, "~").replace(/^\/Users\/[^/]+(?=\/|$)/, "~");
314
+ }
315
+ function displayUserPath(value) {
316
+ return redactHomePath(value);
317
+ }
318
+
319
+ // src/default-repo-discovery.ts
320
+ var WELL_KNOWN_REPO_DIRS = ["Kynver", "repos/Kynver", "code/Kynver", "projects/Kynver"];
321
+ function readPackageName(repoRoot) {
322
+ const pkgPath = path3.join(repoRoot, "package.json");
323
+ if (!existsSync2(pkgPath)) return null;
324
+ try {
325
+ const pkg = JSON.parse(readFileSync2(pkgPath, "utf8"));
326
+ return typeof pkg.name === "string" ? pkg.name.trim() : null;
327
+ } catch {
328
+ return null;
329
+ }
330
+ }
331
+ function isKynverMonorepoRoot(repoRoot) {
332
+ return readPackageName(repoRoot) === "kynver";
333
+ }
334
+ function gitRepoRoot(startDir) {
335
+ const resolvedStart = path3.resolve(startDir);
336
+ if (!existsSync2(resolvedStart)) return null;
337
+ const probe = gitCapture(resolvedStart, ["rev-parse", "--show-toplevel"]);
338
+ if (probe.status !== 0) return null;
339
+ const root = probe.stdout.trim();
340
+ return root.length ? path3.resolve(root) : null;
341
+ }
342
+ function resolveRuntimePackageRoot(moduleUrl = import.meta.url) {
343
+ let dir = path3.dirname(fileURLToPath(moduleUrl));
344
+ for (let depth = 0; depth < 8; depth += 1) {
345
+ const pkgPath = path3.join(dir, "package.json");
346
+ if (existsSync2(pkgPath)) {
347
+ try {
348
+ const pkg = JSON.parse(readFileSync2(pkgPath, "utf8"));
349
+ if (pkg.name === "@kynver-app/runtime") return dir;
350
+ } catch {
351
+ }
352
+ }
353
+ const parent = path3.dirname(dir);
354
+ if (parent === dir) break;
355
+ dir = parent;
356
+ }
357
+ return null;
358
+ }
359
+ function pushCandidate(seen, out, repo, source) {
360
+ if (!repo) return;
361
+ const resolved = path3.resolve(repo);
362
+ if (seen.has(resolved)) return;
363
+ if (!isKynverMonorepoRoot(resolved)) return;
364
+ seen.add(resolved);
365
+ out.push({ repo: resolved, source });
366
+ }
367
+ function discoverDefaultRepoCandidates(opts) {
368
+ const cwd = opts?.cwd ?? process.cwd();
369
+ const seen = /* @__PURE__ */ new Set();
370
+ const candidates = [];
371
+ pushCandidate(seen, candidates, gitRepoRoot(cwd), "cwd_git");
372
+ const runtimePkgRoot = resolveRuntimePackageRoot(opts?.runtimeModuleUrl ?? import.meta.url);
373
+ if (runtimePkgRoot) {
374
+ pushCandidate(seen, candidates, gitRepoRoot(runtimePkgRoot), "runtime_checkout");
375
+ }
376
+ const home = homedir2();
377
+ for (const rel of WELL_KNOWN_REPO_DIRS) {
378
+ pushCandidate(seen, candidates, resolveUserPath(path3.join(home, rel)), "well_known_path");
379
+ }
380
+ return candidates;
381
+ }
382
+ function discoverDefaultRepo(opts) {
383
+ return discoverDefaultRepoCandidates(opts)[0] ?? null;
384
+ }
385
+
148
386
  // src/config.ts
149
- var CONFIG_DIR = path3.join(homedir2(), ".kynver");
150
- var CONFIG_FILE = path3.join(CONFIG_DIR, "config.json");
151
- var CREDENTIALS_FILE = path3.join(CONFIG_DIR, "credentials");
387
+ var CONFIG_DIR = path4.join(homedir3(), ".kynver");
388
+ var CONFIG_FILE = path4.join(CONFIG_DIR, "config.json");
389
+ var CREDENTIALS_FILE = path4.join(CONFIG_DIR, "credentials");
152
390
  function loadUserConfig() {
153
- if (!existsSync2(CONFIG_FILE)) return {};
391
+ if (!existsSync3(CONFIG_FILE)) return {};
154
392
  try {
155
- return JSON.parse(readFileSync2(CONFIG_FILE, "utf8"));
393
+ return JSON.parse(readFileSync3(CONFIG_FILE, "utf8"));
156
394
  } catch {
157
395
  return {};
158
396
  }
@@ -176,7 +414,8 @@ function inferSetupFields(existing, args) {
176
414
  const creds = loadCredentialsFile();
177
415
  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();
178
416
  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);
179
- 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();
417
+ const explicitRepo = typeof args.repo === "string" ? args.repo : args.discoverRepo === true || args.discoverRepo === "true" ? discoverDefaultRepo()?.repo : void 0;
418
+ const defaultRepo = explicitRepo || existing.defaultRepo?.trim() || process.env.KYNVER_DEFAULT_REPO?.trim() || process.env.KYNVER_HARNESS_REPO?.trim() || discoverDefaultRepo()?.repo;
180
419
  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();
181
420
  return {
182
421
  ...apiBaseUrl ? { apiBaseUrl: trimTrailingSlash(apiBaseUrl) } : {},
@@ -187,9 +426,9 @@ function inferSetupFields(existing, args) {
187
426
  };
188
427
  }
189
428
  function loadCredentialsFile() {
190
- if (!existsSync2(CREDENTIALS_FILE)) return {};
429
+ if (!existsSync3(CREDENTIALS_FILE)) return {};
191
430
  try {
192
- return JSON.parse(readFileSync2(CREDENTIALS_FILE, "utf8"));
431
+ return JSON.parse(readFileSync3(CREDENTIALS_FILE, "utf8"));
193
432
  } catch {
194
433
  return {};
195
434
  }
@@ -409,7 +648,7 @@ async function runLogin(args) {
409
648
  }
410
649
 
411
650
  // src/dispatch.ts
412
- import path17 from "node:path";
651
+ import path18 from "node:path";
413
652
 
414
653
  // src/callback-headers.ts
415
654
  function buildHarnessCallbackHeaders(secret) {
@@ -471,12 +710,12 @@ var DEFAULT_CRITICAL_FREE_BYTES = 15 * 1024 * 1024 * 1024;
471
710
  var DEFAULT_MAX_USED_PERCENT = 80;
472
711
  var DEFAULT_HARD_MAX_USED_PERCENT = 90;
473
712
  function observeRunnerDiskGate(input = {}) {
474
- const path38 = input.diskPath?.trim() || "/";
713
+ const path40 = input.diskPath?.trim() || "/";
475
714
  const warnBelowBytes = input.diskFreeWarnBytes ?? DEFAULT_WARN_FREE_BYTES;
476
715
  const criticalBelowBytes = input.diskFreeCriticalBytes ?? DEFAULT_CRITICAL_FREE_BYTES;
477
716
  const maxUsedPercent = input.diskMaxUsedPercent ?? DEFAULT_MAX_USED_PERCENT;
478
717
  const hardMaxUsedPercent = input.diskHardMaxUsedPercent ?? DEFAULT_HARD_MAX_USED_PERCENT;
479
- const stats = statfsSync(path38);
718
+ const stats = statfsSync(path40);
480
719
  const freeBytes = Number(stats.bavail) * Number(stats.bsize);
481
720
  const totalBytes = Number(stats.blocks) * Number(stats.bsize);
482
721
  const usedPercent = totalBytes > 0 ? (totalBytes - freeBytes) / totalBytes * 100 : 100;
@@ -496,7 +735,7 @@ function observeRunnerDiskGate(input = {}) {
496
735
  }
497
736
  return {
498
737
  ok,
499
- path: path38,
738
+ path: path40,
500
739
  freeBytes,
501
740
  totalBytes,
502
741
  usedPercent,
@@ -512,7 +751,7 @@ function observeRunnerDiskGate(input = {}) {
512
751
  import os2 from "node:os";
513
752
 
514
753
  // src/bounded-build/meminfo.ts
515
- import { readFileSync as readFileSync3 } from "node:fs";
754
+ import { readFileSync as readFileSync4 } from "node:fs";
516
755
  import os from "node:os";
517
756
  function readMemAvailableBytes(meminfoText) {
518
757
  if (meminfoText !== void 0) {
@@ -522,7 +761,7 @@ function readMemAvailableBytes(meminfoText) {
522
761
  }
523
762
  if (process.platform === "linux") {
524
763
  try {
525
- const meminfo = readFileSync3("/proc/meminfo", "utf8");
764
+ const meminfo = readFileSync4("/proc/meminfo", "utf8");
526
765
  const match = meminfo.match(/^MemAvailable:\s+(\d+)\s*kB/m);
527
766
  if (match) return Number(match[1]) * 1024;
528
767
  } catch {
@@ -532,37 +771,37 @@ function readMemAvailableBytes(meminfoText) {
532
771
  }
533
772
 
534
773
  // src/resource-gate.ts
535
- import path6 from "node:path";
774
+ import path7 from "node:path";
536
775
 
537
776
  // src/run-store.ts
538
- import { existsSync as existsSync4, readdirSync as readdirSync2 } from "node:fs";
539
- import path5 from "node:path";
777
+ import { existsSync as existsSync5, readdirSync as readdirSync2 } from "node:fs";
778
+ import path6 from "node:path";
540
779
 
541
780
  // src/paths.ts
542
- import { existsSync as existsSync3 } from "node:fs";
543
- import { homedir as homedir3 } from "node:os";
544
- import path4 from "node:path";
545
- var LEGACY_ROOT = path4.join(homedir3(), ".openclaw", "harness");
781
+ import { existsSync as existsSync4 } from "node:fs";
782
+ import { homedir as homedir4 } from "node:os";
783
+ import path5 from "node:path";
784
+ var LEGACY_ROOT = path5.join(homedir4(), ".openclaw", "harness");
546
785
  function resolveHarnessRoot() {
547
786
  const env = process.env.KYNVER_HARNESS_ROOT || process.env.OPUS_HARNESS_ROOT;
548
787
  if (env) return resolveUserPath(env);
549
788
  const configured = loadUserConfig().harnessRoot?.trim();
550
789
  if (configured) return resolveUserPath(configured);
551
- const kynverRoot = path4.join(homedir3(), ".kynver", "harness");
552
- if (existsSync3(kynverRoot)) return kynverRoot;
553
- if (existsSync3(LEGACY_ROOT)) return LEGACY_ROOT;
790
+ const kynverRoot = path5.join(homedir4(), ".kynver", "harness");
791
+ if (existsSync4(kynverRoot)) return kynverRoot;
792
+ if (existsSync4(LEGACY_ROOT)) return LEGACY_ROOT;
554
793
  return kynverRoot;
555
794
  }
556
795
  function getHarnessPaths() {
557
796
  const harnessRoot = resolveHarnessRoot();
558
797
  return {
559
798
  harnessRoot,
560
- runsDir: path4.join(harnessRoot, "runs"),
561
- worktreesDir: path4.join(harnessRoot, "worktrees")
799
+ runsDir: path5.join(harnessRoot, "runs"),
800
+ worktreesDir: path5.join(harnessRoot, "worktrees")
562
801
  };
563
802
  }
564
803
  function runDir(runsDir, id) {
565
- return path4.join(runsDir, safeSlug(id));
804
+ return path5.join(runsDir, safeSlug(id));
566
805
  }
567
806
 
568
807
  // src/run-store.ts
@@ -571,16 +810,16 @@ function getPaths() {
571
810
  }
572
811
  function loadRun(id) {
573
812
  const { runsDir } = getPaths();
574
- return readJson(path5.join(runDir(runsDir, safeSlug(id)), "run.json"));
813
+ return readJson(path6.join(runDir(runsDir, safeSlug(id)), "run.json"));
575
814
  }
576
815
  function listRunRecords() {
577
816
  const { runsDir } = getPaths();
578
- if (!existsSync4(runsDir)) return [];
817
+ if (!existsSync5(runsDir)) return [];
579
818
  const runs = [];
580
819
  for (const entry of readdirSync2(runsDir, { withFileTypes: true })) {
581
820
  if (!entry.isDirectory()) continue;
582
821
  const run = readJson(
583
- path5.join(runsDir, entry.name, "run.json"),
822
+ path6.join(runsDir, entry.name, "run.json"),
584
823
  void 0
585
824
  );
586
825
  if (run?.id) runs.push(run);
@@ -590,16 +829,16 @@ function listRunRecords() {
590
829
  function loadWorker(runId, name) {
591
830
  const { runsDir } = getPaths();
592
831
  return readJson(
593
- path5.join(runDir(runsDir, safeSlug(runId)), "workers", safeSlug(name), "worker.json")
832
+ path6.join(runDir(runsDir, safeSlug(runId)), "workers", safeSlug(name), "worker.json")
594
833
  );
595
834
  }
596
835
  function saveRun(run) {
597
836
  const { runsDir } = getPaths();
598
- writeJson(path5.join(runDir(runsDir, run.id), "run.json"), run);
837
+ writeJson(path6.join(runDir(runsDir, run.id), "run.json"), run);
599
838
  }
600
839
  function saveWorker(runId, worker) {
601
840
  const { runsDir } = getPaths();
602
- writeJson(path5.join(runDir(runsDir, runId), "workers", worker.name, "worker.json"), worker);
841
+ writeJson(path6.join(runDir(runsDir, runId), "workers", worker.name, "worker.json"), worker);
603
842
  }
604
843
  function runDirectory(id) {
605
844
  const { runsDir } = getPaths();
@@ -607,7 +846,7 @@ function runDirectory(id) {
607
846
  }
608
847
 
609
848
  // src/heartbeat.ts
610
- import { existsSync as existsSync5, readFileSync as readFileSync4 } from "node:fs";
849
+ import { existsSync as existsSync6, readFileSync as readFileSync5 } from "node:fs";
611
850
  var HEARTBEAT_FUTURE_SKEW_MS = 6e4;
612
851
  function isTerminalHeartbeatPhase(phase) {
613
852
  return phase === "complete";
@@ -626,10 +865,10 @@ function parseHeartbeat(file) {
626
865
  heartbeatBlocker: null,
627
866
  timestampAnomalies: []
628
867
  };
629
- if (!existsSync5(file)) return result;
868
+ if (!existsSync6(file)) return result;
630
869
  const maxFutureMs = Date.now() + HEARTBEAT_FUTURE_SKEW_MS;
631
870
  const clampedTo = new Date(maxFutureMs).toISOString();
632
- const lines = readFileSync4(file, "utf8").split("\n").filter(Boolean);
871
+ const lines = readFileSync5(file, "utf8").split("\n").filter(Boolean);
633
872
  for (const line of lines) {
634
873
  const entry = safeJson(line);
635
874
  if (!entry || typeof entry !== "object" || Array.isArray(entry)) continue;
@@ -656,7 +895,7 @@ function parseHeartbeat(file) {
656
895
  }
657
896
 
658
897
  // src/stream.ts
659
- import { existsSync as existsSync6, readFileSync as readFileSync5 } from "node:fs";
898
+ import { existsSync as existsSync7, readFileSync as readFileSync6 } from "node:fs";
660
899
 
661
900
  // src/shell-command-outcome.ts
662
901
  var NPM_AUDIT_RE = /\bnpm\s+audit\b/i;
@@ -860,8 +1099,8 @@ function parseHarnessStream(file) {
860
1099
  error: null,
861
1100
  lastShellOutcome: null
862
1101
  };
863
- if (!existsSync6(file)) return result;
864
- const lines = readFileSync5(file, "utf8").split("\n").filter(Boolean);
1102
+ if (!existsSync7(file)) return result;
1103
+ const lines = readFileSync6(file, "utf8").split("\n").filter(Boolean);
865
1104
  for (const line of lines) {
866
1105
  const event = safeJson(line);
867
1106
  if (!event) continue;
@@ -1026,214 +1265,49 @@ function hasFinalResult(value) {
1026
1265
  if (typeof value === "boolean") return value;
1027
1266
  if (Array.isArray(value)) return value.length > 0;
1028
1267
  if (typeof value === "object") return Object.keys(value).length > 0;
1029
- return true;
1030
- }
1031
- function committedHeadFromAncestry(ancestry) {
1032
- if (!ancestry?.checked) return null;
1033
- if (ancestry.headIsAncestorOfBase !== false) return null;
1034
- return trimOrNull(ancestry.head);
1035
- }
1036
- function buildAttentionReason(kind, uncommittedCount, headCommit) {
1037
- const parts = ["exited_with_changes_salvage"];
1038
- if (kind === "uncommitted" || kind === "both") {
1039
- parts.push(
1040
- `${uncommittedCount} uncommitted change${uncommittedCount === 1 ? "" : "s"} with no final result`
1041
- );
1042
- }
1043
- if ((kind === "committed_ahead" || kind === "both") && headCommit) {
1044
- const sha = headCommit.length > 12 ? headCommit.slice(0, 12) : headCommit;
1045
- parts.push(`commit ${sha} ahead of base with no final result`);
1046
- }
1047
- parts.push("review worktree \u2014 commit, open a PR, or run a salvage worker before discarding");
1048
- return parts.join(": ");
1049
- }
1050
- function assessExitedWorkerSalvage(input) {
1051
- if (input.alive || hasFinalResult(input.finalResult)) return null;
1052
- const uncommittedCount = (input.changedFiles ?? []).filter((line) => line.trim()).length;
1053
- const headCommit = trimOrNull(input.headCommit) ?? committedHeadFromAncestry(input.gitAncestry);
1054
- const hasUncommitted = uncommittedCount > 0;
1055
- const hasCommittedAhead = Boolean(headCommit);
1056
- if (!hasUncommitted && !hasCommittedAhead) {
1057
- return {
1058
- kind: "none",
1059
- salvageable: false,
1060
- uncommittedCount: 0,
1061
- headCommit: null,
1062
- attentionReason: "process exited without a final result"
1063
- };
1064
- }
1065
- const kind = hasUncommitted && hasCommittedAhead ? "both" : hasUncommitted ? "uncommitted" : "committed_ahead";
1066
- return {
1067
- kind,
1068
- salvageable: true,
1069
- uncommittedCount,
1070
- headCommit,
1071
- attentionReason: buildAttentionReason(kind, uncommittedCount, headCommit)
1072
- };
1073
- }
1074
-
1075
- // src/git.ts
1076
- import { spawnSync } from "node:child_process";
1077
-
1078
- // src/worker-env.ts
1079
- var FORBIDDEN_WORKER_ENV_KEYS = [
1080
- "ANTHROPIC_API_KEY",
1081
- "ANALYST_API_KEY",
1082
- "RECRUITER_API_KEY",
1083
- "AUTH_SECRET",
1084
- "NEXTAUTH_SECRET",
1085
- "DATABASE_URL",
1086
- "PRODUCTION_DATABASE_URL",
1087
- "REDIS_URL",
1088
- "GOOGLE_CLIENT_SECRET",
1089
- "GITHUB_CLIENT_SECRET",
1090
- "KYNVER_API_KEY",
1091
- "KYNVER_SERVICE_SECRET",
1092
- "KYNVER_RUNTIME_SECRET",
1093
- "OPENCLAW_CRON_SECRET",
1094
- "QSTASH_TOKEN",
1095
- "QSTASH_CURRENT_SIGNING_KEY",
1096
- "QSTASH_NEXT_SIGNING_KEY",
1097
- "TOOL_SECRETS_KEK",
1098
- "TOOL_EXECUTOR_DISPATCH_SECRET",
1099
- "CLOUDFLARE_API_TOKEN",
1100
- "STRIPE_SECRET_KEY",
1101
- "STRIPE_WEBHOOK_SECRET",
1102
- "STRIPE_IDENTITY_WEBHOOK_SECRET",
1103
- "VOYAGE_API_KEY",
1104
- "PERPLEXITY_API_KEY",
1105
- "FRED_API_KEY",
1106
- "FMP_API_KEY",
1107
- "CURSOR_API_KEY"
1108
- ];
1109
- var FORBIDDEN_KEY_SET = new Set(FORBIDDEN_WORKER_ENV_KEYS);
1110
- var FORBIDDEN_SUFFIXES = ["_SECRET", "_API_KEY"];
1111
- function isForbiddenWorkerEnvKey(key) {
1112
- if (FORBIDDEN_KEY_SET.has(key)) return true;
1113
- return FORBIDDEN_SUFFIXES.some((suffix) => key.endsWith(suffix));
1114
- }
1115
- function scrubWorkerEnv(env) {
1116
- const next = { ...env };
1117
- for (const key of Object.keys(next)) {
1118
- if (isForbiddenWorkerEnvKey(key)) delete next[key];
1119
- }
1120
- return next;
1121
- }
1122
-
1123
- // src/git.ts
1124
- function git(cwd, args, options = {}) {
1125
- const res = spawnSync("git", args, { cwd, encoding: "utf8" });
1126
- if (res.status !== 0 && !options.allowFailure) {
1127
- const message = `git ${args.join(" ")} failed: ${res.stderr || res.stdout}`;
1128
- if (options.throwError) throw new Error(message);
1129
- fail(message);
1130
- }
1131
- return res.stdout || "";
1132
- }
1133
- function ensureGitRepo(repo) {
1134
- git(repo, ["rev-parse", "--show-toplevel"]);
1135
- }
1136
- function gitStatusShort(worktreePath) {
1137
- return git(worktreePath, ["status", "--short"], { allowFailure: true }).split("\n").map((line) => line.trim()).filter(Boolean);
1138
- }
1139
- function gitCapture(cwd, args) {
1140
- try {
1141
- const res = spawnSync("git", args, { cwd, encoding: "utf8" });
1142
- return {
1143
- status: res.status,
1144
- stdout: res.stdout || "",
1145
- stderr: res.stderr || "",
1146
- error: res.error ? res.error.message : null
1147
- };
1148
- } catch (error) {
1149
- return {
1150
- status: null,
1151
- stdout: "",
1152
- stderr: "",
1153
- error: error.message
1154
- };
1155
- }
1268
+ return true;
1156
1269
  }
1157
- function gitIsAncestor(cwd, ancestor, descendant) {
1158
- const res = gitCapture(cwd, ["merge-base", "--is-ancestor", ancestor, descendant]);
1159
- if (res.status === 0) return { isAncestor: true, error: null };
1160
- if (res.status === 1) return { isAncestor: false, error: null };
1161
- return { isAncestor: null, error: res.error || res.stderr || res.stdout || `git exited ${res.status}` };
1270
+ function committedHeadFromAncestry(ancestry) {
1271
+ if (!ancestry?.checked) return null;
1272
+ if (ancestry.headIsAncestorOfBase !== false) return null;
1273
+ return trimOrNull(ancestry.head);
1162
1274
  }
1163
- function computeGitAncestry(worktreePath, baseOrOptions = "origin/main") {
1164
- const options = typeof baseOrOptions === "string" ? { base: baseOrOptions } : baseOrOptions;
1165
- const baseLabel = options.baseCommit?.trim() || options.base?.trim() || "origin/main";
1166
- const pinnedBaseCommit = options.baseCommit?.trim() || null;
1167
- if (!worktreePath) {
1168
- return unknownAncestry(baseLabel, "missing worktree path");
1169
- }
1170
- const head = gitCapture(worktreePath, ["rev-parse", "HEAD"]);
1171
- if (head.status !== 0) {
1172
- return unknownAncestry(baseLabel, head.error || head.stderr || head.stdout || "failed to resolve HEAD");
1173
- }
1174
- let baseSha;
1175
- if (pinnedBaseCommit) {
1176
- baseSha = pinnedBaseCommit;
1177
- } else {
1178
- const baseHead = gitCapture(worktreePath, ["rev-parse", baseLabel]);
1179
- if (baseHead.status !== 0) {
1180
- return unknownAncestry(
1181
- baseLabel,
1182
- baseHead.error || baseHead.stderr || baseHead.stdout || `failed to resolve ${baseLabel}`,
1183
- head.stdout.trim()
1184
- );
1185
- }
1186
- baseSha = baseHead.stdout.trim();
1275
+ function buildAttentionReason(kind, uncommittedCount, headCommit) {
1276
+ const parts = ["exited_with_changes_salvage"];
1277
+ if (kind === "uncommitted" || kind === "both") {
1278
+ parts.push(
1279
+ `${uncommittedCount} uncommitted change${uncommittedCount === 1 ? "" : "s"} with no final result`
1280
+ );
1187
1281
  }
1188
- const headSha = head.stdout.trim();
1189
- if (headSha === baseSha) {
1190
- return {
1191
- checked: true,
1192
- base: baseLabel,
1193
- head: headSha,
1194
- baseHead: baseSha,
1195
- baseIsAncestorOfHead: true,
1196
- headIsAncestorOfBase: true,
1197
- relation: "synced"
1198
- };
1282
+ if ((kind === "committed_ahead" || kind === "both") && headCommit) {
1283
+ const sha = headCommit.length > 12 ? headCommit.slice(0, 12) : headCommit;
1284
+ parts.push(`commit ${sha} ahead of base with no final result`);
1199
1285
  }
1200
- const baseIsAncestorOfHead = gitIsAncestor(worktreePath, baseSha, headSha);
1201
- const headIsAncestorOfBase = gitIsAncestor(worktreePath, headSha, baseSha);
1202
- const error = baseIsAncestorOfHead.error || headIsAncestorOfBase.error || void 0;
1203
- if (baseIsAncestorOfHead.isAncestor == null || headIsAncestorOfBase.isAncestor == null) {
1286
+ parts.push("review worktree \u2014 commit, open a PR, or run a salvage worker before discarding");
1287
+ return parts.join(": ");
1288
+ }
1289
+ function assessExitedWorkerSalvage(input) {
1290
+ if (input.alive || hasFinalResult(input.finalResult)) return null;
1291
+ const uncommittedCount = (input.changedFiles ?? []).filter((line) => line.trim()).length;
1292
+ const headCommit = trimOrNull(input.headCommit) ?? committedHeadFromAncestry(input.gitAncestry);
1293
+ const hasUncommitted = uncommittedCount > 0;
1294
+ const hasCommittedAhead = Boolean(headCommit);
1295
+ if (!hasUncommitted && !hasCommittedAhead) {
1204
1296
  return {
1205
- checked: false,
1206
- base: baseLabel,
1207
- head: headSha,
1208
- baseHead: baseSha,
1209
- baseIsAncestorOfHead: baseIsAncestorOfHead.isAncestor,
1210
- headIsAncestorOfBase: headIsAncestorOfBase.isAncestor,
1211
- relation: "unknown",
1212
- ...error ? { error } : {}
1297
+ kind: "none",
1298
+ salvageable: false,
1299
+ uncommittedCount: 0,
1300
+ headCommit: null,
1301
+ attentionReason: "process exited without a final result"
1213
1302
  };
1214
1303
  }
1215
- const relation = baseIsAncestorOfHead.isAncestor ? "ahead" : headIsAncestorOfBase.isAncestor ? "merged" : "diverged";
1216
- return {
1217
- checked: true,
1218
- base: baseLabel,
1219
- head: headSha,
1220
- baseHead: baseSha,
1221
- baseIsAncestorOfHead: baseIsAncestorOfHead.isAncestor,
1222
- headIsAncestorOfBase: headIsAncestorOfBase.isAncestor,
1223
- relation,
1224
- ...error ? { error } : {}
1225
- };
1226
- }
1227
- function unknownAncestry(base, error, head = null) {
1304
+ const kind = hasUncommitted && hasCommittedAhead ? "both" : hasUncommitted ? "uncommitted" : "committed_ahead";
1228
1305
  return {
1229
- checked: false,
1230
- base,
1231
- head,
1232
- baseHead: null,
1233
- baseIsAncestorOfHead: null,
1234
- headIsAncestorOfBase: null,
1235
- relation: "unknown",
1236
- error
1306
+ kind,
1307
+ salvageable: true,
1308
+ uncommittedCount,
1309
+ headCommit,
1310
+ attentionReason: buildAttentionReason(kind, uncommittedCount, headCommit)
1237
1311
  };
1238
1312
  }
1239
1313
 
@@ -1595,7 +1669,7 @@ function countActiveWorkersForRun(run) {
1595
1669
  let active = 0;
1596
1670
  for (const name of Object.keys(run.workers || {})) {
1597
1671
  const worker = readJson(
1598
- path6.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
1672
+ path7.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
1599
1673
  void 0
1600
1674
  );
1601
1675
  if (!worker || !isActiveHarnessWorker(worker)) continue;
@@ -1906,7 +1980,7 @@ function inferModelRoutingFromTask(task) {
1906
1980
  rule: "lane:landing"
1907
1981
  };
1908
1982
  }
1909
- if (ref.includes("review") || title.startsWith("review ") || roleLane.includes("review")) {
1983
+ if (ref.includes("review") || /^review[\s:]/.test(title) || roleLane.includes("review")) {
1910
1984
  if (isOpusLane(ref, title) || roleLane === "deep_reviewer") {
1911
1985
  return { model: "claude-opus-4-7", provider: "claude", rule: "lane:deep_review" };
1912
1986
  }
@@ -1980,10 +2054,10 @@ function readHarnessRetryLimits() {
1980
2054
  }
1981
2055
 
1982
2056
  // src/lease-renewal.ts
1983
- import path7 from "node:path";
2057
+ import path8 from "node:path";
1984
2058
  function workerRecord(runId, name) {
1985
2059
  return readJson(
1986
- path7.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
2060
+ path8.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
1987
2061
  void 0
1988
2062
  );
1989
2063
  }
@@ -2049,8 +2123,8 @@ function hasLiveWorkerForTask(runId, taskId) {
2049
2123
  }
2050
2124
 
2051
2125
  // src/supervisor.ts
2052
- import { existsSync as existsSync10, mkdirSync as mkdirSync3 } from "node:fs";
2053
- import path13 from "node:path";
2126
+ import { existsSync as existsSync11, mkdirSync as mkdirSync3 } from "node:fs";
2127
+ import path14 from "node:path";
2054
2128
 
2055
2129
  // src/prompt.ts
2056
2130
  function buildPrompt(input) {
@@ -2119,13 +2193,13 @@ function buildPrompt(input) {
2119
2193
  }
2120
2194
 
2121
2195
  // src/providers/cursor.ts
2122
- import { closeSync as closeSync2, existsSync as existsSync8, openSync as openSync2 } from "node:fs";
2196
+ import { closeSync as closeSync2, existsSync as existsSync9, openSync as openSync2 } from "node:fs";
2123
2197
  import { spawn as spawn2 } from "node:child_process";
2124
- import path9 from "node:path";
2198
+ import path10 from "node:path";
2125
2199
 
2126
2200
  // src/providers/cursor-windows.ts
2127
- import { existsSync as existsSync7, readdirSync as readdirSync3 } from "node:fs";
2128
- import path8 from "node:path";
2201
+ import { existsSync as existsSync8, readdirSync as readdirSync3 } from "node:fs";
2202
+ import path9 from "node:path";
2129
2203
  var CURSOR_VERSION_DIR = /^\d{4}\.\d{1,2}\.\d{1,2}-[a-f0-9]+$/i;
2130
2204
  function parseCursorVersionSortKey(versionName) {
2131
2205
  const datePart = versionName.split("-")[0];
@@ -2136,8 +2210,8 @@ function parseCursorVersionSortKey(versionName) {
2136
2210
  return Number(`${year}${month.padStart(2, "0")}${day.padStart(2, "0")}`);
2137
2211
  }
2138
2212
  function pickLatestCursorVersionDir(agentRoot) {
2139
- const versionsRoot = path8.join(agentRoot, "versions");
2140
- if (!existsSync7(versionsRoot)) return null;
2213
+ const versionsRoot = path9.join(agentRoot, "versions");
2214
+ if (!existsSync8(versionsRoot)) return null;
2141
2215
  let bestDir = null;
2142
2216
  let bestKey = -1;
2143
2217
  for (const entry of readdirSync3(versionsRoot, { withFileTypes: true })) {
@@ -2145,22 +2219,22 @@ function pickLatestCursorVersionDir(agentRoot) {
2145
2219
  const key = parseCursorVersionSortKey(entry.name);
2146
2220
  if (key == null || key <= bestKey) continue;
2147
2221
  bestKey = key;
2148
- bestDir = path8.join(versionsRoot, entry.name);
2222
+ bestDir = path9.join(versionsRoot, entry.name);
2149
2223
  }
2150
2224
  return bestDir;
2151
2225
  }
2152
2226
  function resolveWindowsCursorBundled(agentRoot) {
2153
- const root = agentRoot?.trim() || path8.join(process.env.LOCALAPPDATA || "", "cursor-agent");
2154
- const directNode = path8.join(root, "node.exe");
2155
- const directIndex = path8.join(root, "index.js");
2156
- if (existsSync7(directNode) && existsSync7(directIndex)) {
2227
+ const root = agentRoot?.trim() || path9.join(process.env.LOCALAPPDATA || "", "cursor-agent");
2228
+ const directNode = path9.join(root, "node.exe");
2229
+ const directIndex = path9.join(root, "index.js");
2230
+ if (existsSync8(directNode) && existsSync8(directIndex)) {
2157
2231
  return { nodeExe: directNode, indexJs: directIndex, versionDir: root };
2158
2232
  }
2159
2233
  const versionDir = pickLatestCursorVersionDir(root);
2160
2234
  if (!versionDir) return null;
2161
- const nodeExe = path8.join(versionDir, "node.exe");
2162
- const indexJs = path8.join(versionDir, "index.js");
2163
- if (!existsSync7(nodeExe) || !existsSync7(indexJs)) return null;
2235
+ const nodeExe = path9.join(versionDir, "node.exe");
2236
+ const indexJs = path9.join(versionDir, "index.js");
2237
+ if (!existsSync8(nodeExe) || !existsSync8(indexJs)) return null;
2164
2238
  return { nodeExe, indexJs, versionDir };
2165
2239
  }
2166
2240
 
@@ -2178,13 +2252,13 @@ function bundledSpawnTarget(nodeExe, indexJs, versionDir) {
2178
2252
  function resolveCursorSpawn(agentBin) {
2179
2253
  if (process.platform === "win32") {
2180
2254
  const isCursorWrapper = /\.(cmd|bat)$/i.test(agentBin);
2181
- const isBundledNode = /node\.exe$/i.test(agentBin) && existsSync8(path9.join(path9.dirname(agentBin), "index.js"));
2255
+ const isBundledNode = /node\.exe$/i.test(agentBin) && existsSync9(path10.join(path10.dirname(agentBin), "index.js"));
2182
2256
  const isDefaultShim = agentBin === "agent";
2183
2257
  if (isCursorWrapper || isBundledNode || isDefaultShim) {
2184
- const bundled = isCursorWrapper ? resolveWindowsCursorBundled(path9.dirname(agentBin)) : isBundledNode ? {
2258
+ const bundled = isCursorWrapper ? resolveWindowsCursorBundled(path10.dirname(agentBin)) : isBundledNode ? {
2185
2259
  nodeExe: agentBin,
2186
- indexJs: path9.join(path9.dirname(agentBin), "index.js"),
2187
- versionDir: path9.dirname(agentBin)
2260
+ indexJs: path10.join(path10.dirname(agentBin), "index.js"),
2261
+ versionDir: path10.dirname(agentBin)
2188
2262
  } : resolveWindowsCursorBundled();
2189
2263
  if (bundled) {
2190
2264
  return bundledSpawnTarget(bundled.nodeExe, bundled.indexJs, bundled.versionDir);
@@ -2204,8 +2278,8 @@ function resolveAgentBin() {
2204
2278
  process.env.KYNVER_CURSOR_AGENT_ROOT?.trim() || void 0
2205
2279
  );
2206
2280
  if (bundled) return bundled.nodeExe;
2207
- const localAgent = path9.join(process.env.LOCALAPPDATA || "", "cursor-agent", "agent.cmd");
2208
- if (existsSync8(localAgent)) return localAgent;
2281
+ const localAgent = path10.join(process.env.LOCALAPPDATA || "", "cursor-agent", "agent.cmd");
2282
+ if (existsSync9(localAgent)) return localAgent;
2209
2283
  }
2210
2284
  return "agent";
2211
2285
  }
@@ -2214,7 +2288,7 @@ function cursorWorkerEnv(agentBin, spawnTarget) {
2214
2288
  ...process.env,
2215
2289
  CI: "1",
2216
2290
  NO_COLOR: "1",
2217
- ...spawnTarget.bundledVersionDir ? { CURSOR_INVOKED_AS: path9.basename(agentBin) || "agent.cmd" } : {}
2291
+ ...spawnTarget.bundledVersionDir ? { CURSOR_INVOKED_AS: path10.basename(agentBin) || "agent.cmd" } : {}
2218
2292
  });
2219
2293
  }
2220
2294
  var cursorProvider = {
@@ -2288,9 +2362,9 @@ function resolveWorkerProvider(name) {
2288
2362
 
2289
2363
  // src/auto-complete.ts
2290
2364
  import { spawn as spawn3 } from "node:child_process";
2291
- import { existsSync as existsSync9, openSync as openSync3, closeSync as closeSync3 } from "node:fs";
2292
- import path12 from "node:path";
2293
- import { fileURLToPath } from "node:url";
2365
+ import { existsSync as existsSync10, openSync as openSync3, closeSync as closeSync3 } from "node:fs";
2366
+ import path13 from "node:path";
2367
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
2294
2368
 
2295
2369
  // src/completion-ack.ts
2296
2370
  function hasCompletionAck(worker) {
@@ -2306,7 +2380,7 @@ function persistCompletionAck(worker, runId, fields) {
2306
2380
  }
2307
2381
 
2308
2382
  // src/worker-ops.ts
2309
- import path11 from "node:path";
2383
+ import path12 from "node:path";
2310
2384
 
2311
2385
  // src/completion-response.ts
2312
2386
  function asRecord(value) {
@@ -2369,6 +2443,11 @@ function isHarnessExpertReviewWorker(worker) {
2369
2443
 
2370
2444
  // src/pr-handoff/pr-handoff-assess.ts
2371
2445
  var REVIEW_LANE_RULE = /^(lane:)?(review|deep_review|planning|landing)(:|$)/i;
2446
+ var REVIEW_PERSONA_SLUGS = /* @__PURE__ */ new Set(["lorentz", "sentinel"]);
2447
+ var NO_PR_COMMITS_BETWEEN_RE = /no commits between/i;
2448
+ function isGhNoCommitsBetweenError(detail) {
2449
+ return Boolean(detail && NO_PR_COMMITS_BETWEEN_RE.test(detail));
2450
+ }
2372
2451
  function trimOrNull4(value) {
2373
2452
  if (typeof value !== "string") return null;
2374
2453
  const trimmed = value.trim();
@@ -2387,8 +2466,30 @@ function extractPrUrlFromText(value) {
2387
2466
  );
2388
2467
  return m ? trimOrNull4(m[0]) : null;
2389
2468
  }
2390
- function hasWorkProduct(snapshot) {
2469
+ function countCommitsAheadOfBase(worktreePath, baseRef, exec) {
2470
+ const base = baseRef.trim();
2471
+ if (!base) return null;
2472
+ const count = exec.git(worktreePath, ["rev-list", "--count", `${base}..HEAD`]);
2473
+ if (count.status !== 0) return null;
2474
+ const n = Number.parseInt(count.stdout.trim(), 10);
2475
+ return Number.isFinite(n) ? n : null;
2476
+ }
2477
+ function isReviewArtifactWorker(worker, snapshot) {
2478
+ if (snapshot.changedFiles.length > 0) return false;
2479
+ const persona = trimOrNull4(worker.personaSlug)?.toLowerCase();
2480
+ if (persona && REVIEW_PERSONA_SLUGS.has(persona)) return true;
2481
+ const rule = trimOrNull4(worker.routingRule) ?? "";
2482
+ if (rule && REVIEW_LANE_RULE.test(rule)) return true;
2483
+ return false;
2484
+ }
2485
+ function hasWorkProduct(snapshot, options) {
2391
2486
  if (snapshot.changedFiles.length > 0) return true;
2487
+ const baseRef = trimOrNull4(options?.baseRef);
2488
+ if (baseRef && options?.exec && options.worktreePath) {
2489
+ const ahead = countCommitsAheadOfBase(options.worktreePath, baseRef, options.exec);
2490
+ if (ahead === 0) return false;
2491
+ if (ahead !== null && ahead > 0) return true;
2492
+ }
2392
2493
  if (trimOrNull4(snapshot.headCommit)) return true;
2393
2494
  if (committedHead(snapshot.gitAncestry)) return true;
2394
2495
  return false;
@@ -2409,6 +2510,13 @@ function assessPrHandoffRequirement(input) {
2409
2510
  if (rule && REVIEW_LANE_RULE.test(rule)) {
2410
2511
  return { required: false, reason: "review_lane" };
2411
2512
  }
2513
+ const workerCtx = input.worker ?? {
2514
+ personaSlug: input.personaSlug,
2515
+ routingRule: input.routingRule
2516
+ };
2517
+ if (isReviewArtifactWorker(workerCtx, input.snapshot)) {
2518
+ return { required: false, reason: "review_artifact" };
2519
+ }
2412
2520
  if (trimOrNull4(input.patchPath) || trimOrNull4(input.artifactBundlePath)) {
2413
2521
  return { required: false, reason: "patch_or_bundle" };
2414
2522
  }
@@ -2416,7 +2524,12 @@ function assessPrHandoffRequirement(input) {
2416
2524
  if (prUrl) {
2417
2525
  return { required: false, reason: "already_has_pr" };
2418
2526
  }
2419
- if (!hasWorkProduct(input.snapshot)) {
2527
+ const workProductOpts = input.exec && input.baseRef ? {
2528
+ baseRef: input.baseRef,
2529
+ exec: input.exec,
2530
+ worktreePath: input.snapshot.worktreePath
2531
+ } : void 0;
2532
+ if (!hasWorkProduct(input.snapshot, workProductOpts)) {
2420
2533
  return { required: false, reason: "no_work_product" };
2421
2534
  }
2422
2535
  return { required: true, snapshot: input.snapshot };
@@ -2625,15 +2738,19 @@ function ensurePrReadyHandoff(input, exec = defaultPrHandoffExec) {
2625
2738
  prUrl: prUrlHint,
2626
2739
  headCommit: null
2627
2740
  });
2741
+ const baseRef = input.run.baseCommit?.trim() || input.run.base?.trim() || "origin/main";
2628
2742
  const requirement = assessPrHandoffRequirement({
2629
2743
  dispatched: input.worker.dispatched,
2630
2744
  routingRule: input.worker.routingRule,
2745
+ personaSlug: input.worker.personaSlug,
2631
2746
  prUrl: prUrlHint,
2632
2747
  taskTitle: input.worker.taskTitle,
2633
2748
  executorRef: input.worker.executorRef,
2634
2749
  parentTaskId: input.worker.parentTaskId,
2635
- personaSlug: input.worker.personaSlug,
2636
2750
  taskPrUrl: input.worker.taskPrUrl,
2751
+ baseRef,
2752
+ exec,
2753
+ worker: input.worker,
2637
2754
  snapshot
2638
2755
  });
2639
2756
  if (!requirement.required) {
@@ -2718,6 +2835,14 @@ function ensurePrReadyHandoff(input, exec = defaultPrHandoffExec) {
2718
2835
  exec
2719
2836
  });
2720
2837
  if (!pr.ok || !pr.prUrl) {
2838
+ if (isGhNoCommitsBetweenError(pr.detail)) {
2839
+ return {
2840
+ ok: true,
2841
+ headCommit: headCommit ?? void 0,
2842
+ committed,
2843
+ pushed
2844
+ };
2845
+ }
2721
2846
  const dirty = snapshot.changedFiles.length;
2722
2847
  const detail = dirty ? `${dirty} uncommitted change(s) and no PR URL after handoff attempt` : "no PR URL after handoff attempt";
2723
2848
  return {
@@ -2737,7 +2862,7 @@ function ensurePrReadyHandoff(input, exec = defaultPrHandoffExec) {
2737
2862
  }
2738
2863
 
2739
2864
  // src/worker-lifecycle.ts
2740
- import path10 from "node:path";
2865
+ import path11 from "node:path";
2741
2866
  var TASK_LEFT_RUNNING = /* @__PURE__ */ new Set([
2742
2867
  "awaiting_review",
2743
2868
  "blocked",
@@ -2793,7 +2918,7 @@ function syncCompletionAcknowledgedFromOperatorTick(runId, operatorTick) {
2793
2918
  const synced = [];
2794
2919
  for (const name of Object.keys(run.workers || {})) {
2795
2920
  const worker = readJson(
2796
- path10.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
2921
+ path11.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
2797
2922
  void 0
2798
2923
  );
2799
2924
  if (!worker?.taskId || isCompletionAcknowledged(worker)) continue;
@@ -3051,7 +3176,7 @@ function workerStatus(args) {
3051
3176
  const worker = loadWorker(String(args.run), String(args.name));
3052
3177
  const run = loadRun(worker.runId);
3053
3178
  const status = computeWorkerStatus(worker, workerStatusOptions(run));
3054
- writeJson(path11.join(worker.workerDir, "last-status.json"), status);
3179
+ writeJson(path12.join(worker.workerDir, "last-status.json"), status);
3055
3180
  console.log(JSON.stringify(status, null, 2));
3056
3181
  }
3057
3182
  function buildRunBoard(runId) {
@@ -3059,7 +3184,7 @@ function buildRunBoard(runId) {
3059
3184
  const names = Object.keys(run.workers || {});
3060
3185
  const workers = names.map((name) => {
3061
3186
  const worker = readJson(
3062
- path11.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
3187
+ path12.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
3063
3188
  void 0
3064
3189
  );
3065
3190
  if (!worker) {
@@ -3170,7 +3295,7 @@ function buildRunBoard(runId) {
3170
3295
  needsAttention: workers.filter((w) => w.attention && w.attention !== "ok" && w.attention !== "done").map((w) => w.worker),
3171
3296
  workers
3172
3297
  };
3173
- writeJson(path11.join(runDirectory(run.id), "last-board.json"), board);
3298
+ writeJson(path12.join(runDirectory(run.id), "last-board.json"), board);
3174
3299
  return board;
3175
3300
  }
3176
3301
  async function publishHarnessBoardSnapshot(args, source) {
@@ -3359,12 +3484,12 @@ async function autoCompleteWorkerCli(raw) {
3359
3484
  }
3360
3485
  }
3361
3486
  function resolveDefaultCliPath() {
3362
- return path12.join(fileURLToPath(new URL(".", import.meta.url)), "cli.js");
3487
+ return path13.join(fileURLToPath2(new URL(".", import.meta.url)), "cli.js");
3363
3488
  }
3364
3489
  function spawnCompletionSidecar(opts) {
3365
3490
  const cliPath = opts.cliPath ?? resolveDefaultCliPath();
3366
- if (!existsSync9(cliPath)) return void 0;
3367
- const logPath = path12.join(opts.workerDir, "auto-complete.log");
3491
+ if (!existsSync10(cliPath)) return void 0;
3492
+ const logPath = path13.join(opts.workerDir, "auto-complete.log");
3368
3493
  let logFd;
3369
3494
  try {
3370
3495
  logFd = openSync3(logPath, "a");
@@ -3446,16 +3571,16 @@ function spawnWorkerProcess(run, opts) {
3446
3571
  launchModel = preflight.model;
3447
3572
  }
3448
3573
  const { worktreesDir } = getPaths();
3449
- const workerDir = path13.join(runDirectory(run.id), "workers", name);
3574
+ const workerDir = path14.join(runDirectory(run.id), "workers", name);
3450
3575
  mkdirSync3(workerDir, { recursive: true });
3451
- const worktreePath = path13.join(worktreesDir, run.id, name);
3576
+ const worktreePath = path14.join(worktreesDir, run.id, name);
3452
3577
  const branch = opts.branch || `agent/${run.id}/${name}`;
3453
- if (existsSync10(worktreePath)) throw new Error(`worktree path already exists: ${worktreePath}`);
3578
+ if (existsSync11(worktreePath)) throw new Error(`worktree path already exists: ${worktreePath}`);
3454
3579
  git(run.repo, ["fetch", "origin", "--prune"], { allowFailure: true });
3455
3580
  git(run.repo, ["worktree", "add", "-b", branch, worktreePath, run.baseCommit], { throwError: true });
3456
- const stdoutPath = path13.join(workerDir, "stdout.jsonl");
3457
- const stderrPath = path13.join(workerDir, "stderr.log");
3458
- const heartbeatPath = path13.join(workerDir, "heartbeat.jsonl");
3581
+ const stdoutPath = path14.join(workerDir, "stdout.jsonl");
3582
+ const stderrPath = path14.join(workerDir, "stderr.log");
3583
+ const heartbeatPath = path14.join(workerDir, "heartbeat.jsonl");
3459
3584
  const prompt = buildPrompt({
3460
3585
  task: opts.task,
3461
3586
  ownedPaths: opts.ownedPaths || [],
@@ -3520,7 +3645,7 @@ function spawnWorkerProcess(run, opts) {
3520
3645
  startedAt: (/* @__PURE__ */ new Date()).toISOString()
3521
3646
  };
3522
3647
  saveWorker(run.id, worker);
3523
- run.workers = { ...run.workers || {}, [name]: { workerDir, statusPath: path13.join(workerDir, "worker.json") } };
3648
+ run.workers = { ...run.workers || {}, [name]: { workerDir, statusPath: path14.join(workerDir, "worker.json") } };
3524
3649
  run.status = "running";
3525
3650
  saveRun(run);
3526
3651
  if (worker.agentOsId && worker.taskId) {
@@ -3812,18 +3937,18 @@ function buildPlanPersistIdempotencyKey(input) {
3812
3937
 
3813
3938
  // src/plan-persist/paths.ts
3814
3939
  import { mkdirSync as mkdirSync4 } from "node:fs";
3815
- import { homedir as homedir4 } from "node:os";
3816
- import path14 from "node:path";
3940
+ import { homedir as homedir5 } from "node:os";
3941
+ import path15 from "node:path";
3817
3942
  function resolveKynverStateRoot() {
3818
3943
  const env = process.env.KYNVER_STATE_ROOT;
3819
- if (env) return path14.resolve(env);
3820
- return path14.join(homedir4(), ".kynver", "state");
3944
+ if (env) return path15.resolve(env);
3945
+ return path15.join(homedir5(), ".kynver", "state");
3821
3946
  }
3822
3947
  function planOutboxDir() {
3823
- return path14.join(resolveKynverStateRoot(), "plan-outbox");
3948
+ return path15.join(resolveKynverStateRoot(), "plan-outbox");
3824
3949
  }
3825
3950
  function planOutboxArchiveDir() {
3826
- return path14.join(resolveKynverStateRoot(), "plan-outbox-archive");
3951
+ return path15.join(resolveKynverStateRoot(), "plan-outbox-archive");
3827
3952
  }
3828
3953
  function ensurePlanOutboxDirs() {
3829
3954
  const outboxDir = planOutboxDir();
@@ -3834,20 +3959,20 @@ function ensurePlanOutboxDirs() {
3834
3959
  }
3835
3960
  function isTmpOnlyPath(filePath) {
3836
3961
  if (filePath.startsWith("/tmp/") || filePath.startsWith("/var/folders/")) return true;
3837
- const resolved = path14.resolve(filePath);
3838
- return resolved.startsWith("/tmp/") || resolved.startsWith(path14.join("/var", "folders"));
3962
+ const resolved = path15.resolve(filePath);
3963
+ return resolved.startsWith("/tmp/") || resolved.startsWith(path15.join("/var", "folders"));
3839
3964
  }
3840
3965
 
3841
3966
  // src/plan-persist/outbox-store.ts
3842
3967
  import {
3843
- existsSync as existsSync12,
3844
- readFileSync as readFileSync6,
3968
+ existsSync as existsSync13,
3969
+ readFileSync as readFileSync7,
3845
3970
  renameSync,
3846
3971
  readdirSync as readdirSync4,
3847
3972
  writeFileSync as writeFileSync3,
3848
3973
  unlinkSync
3849
3974
  } from "node:fs";
3850
- import path15 from "node:path";
3975
+ import path16 from "node:path";
3851
3976
  import { randomUUID } from "node:crypto";
3852
3977
  var DEFAULT_MAX_RETRIES = 12;
3853
3978
  function listOutboxItems() {
@@ -3855,7 +3980,7 @@ function listOutboxItems() {
3855
3980
  const files = readdirSync4(outboxDir).filter((f) => f.endsWith(".json"));
3856
3981
  const items = [];
3857
3982
  for (const file of files) {
3858
- const item = readOutboxItem(path15.join(outboxDir, file));
3983
+ const item = readOutboxItem(path16.join(outboxDir, file));
3859
3984
  if (item && item.queueStatus === "queued") items.push(item);
3860
3985
  }
3861
3986
  return items.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
@@ -3867,25 +3992,25 @@ function findOutboxByIdempotencyKey(key) {
3867
3992
  return null;
3868
3993
  }
3869
3994
  function readOutboxItem(jsonPath) {
3870
- if (!existsSync12(jsonPath)) return null;
3995
+ if (!existsSync13(jsonPath)) return null;
3871
3996
  try {
3872
- return JSON.parse(readFileSync6(jsonPath, "utf8"));
3997
+ return JSON.parse(readFileSync7(jsonPath, "utf8"));
3873
3998
  } catch {
3874
3999
  return null;
3875
4000
  }
3876
4001
  }
3877
4002
  function readOutboxBody(item) {
3878
4003
  const { outboxDir } = ensurePlanOutboxDirs();
3879
- const bodyFile = path15.join(outboxDir, item.bodyPath);
3880
- return readFileSync6(bodyFile, "utf8");
4004
+ const bodyFile = path16.join(outboxDir, item.bodyPath);
4005
+ return readFileSync7(bodyFile, "utf8");
3881
4006
  }
3882
4007
  function writeOutboxItem(input, opts) {
3883
4008
  const { outboxDir } = ensurePlanOutboxDirs();
3884
4009
  const now = (/* @__PURE__ */ new Date()).toISOString();
3885
4010
  const id = opts.existing?.id ?? randomUUID();
3886
4011
  const bodyPath = opts.existing?.bodyPath ?? `${id}.body.md`;
3887
- const jsonPath = path15.join(outboxDir, `${id}.json`);
3888
- const bodyFile = path15.join(outboxDir, bodyPath);
4012
+ const jsonPath = path16.join(outboxDir, `${id}.json`);
4013
+ const bodyFile = path16.join(outboxDir, bodyPath);
3889
4014
  if (!opts.existing) {
3890
4015
  writeFileSync3(bodyFile, input.body, "utf8");
3891
4016
  }
@@ -3921,24 +4046,24 @@ function writeOutboxItem(input, opts) {
3921
4046
  }
3922
4047
  function saveOutboxItem(item) {
3923
4048
  const { outboxDir } = ensurePlanOutboxDirs();
3924
- const jsonPath = path15.join(outboxDir, `${item.id}.json`);
4049
+ const jsonPath = path16.join(outboxDir, `${item.id}.json`);
3925
4050
  writeFileSync3(jsonPath, `${JSON.stringify(item, null, 2)}
3926
4051
  `, { mode: 384 });
3927
4052
  }
3928
4053
  function archiveOutboxItem(item) {
3929
4054
  const { outboxDir, archiveDir } = ensurePlanOutboxDirs();
3930
- const jsonSrc = path15.join(outboxDir, `${item.id}.json`);
3931
- const bodySrc = path15.join(outboxDir, item.bodyPath);
3932
- const jsonDst = path15.join(archiveDir, `${item.id}.json`);
3933
- const bodyDst = path15.join(archiveDir, item.bodyPath);
3934
- if (existsSync12(jsonSrc)) renameSync(jsonSrc, jsonDst);
3935
- if (existsSync12(bodySrc)) renameSync(bodySrc, bodyDst);
4055
+ const jsonSrc = path16.join(outboxDir, `${item.id}.json`);
4056
+ const bodySrc = path16.join(outboxDir, item.bodyPath);
4057
+ const jsonDst = path16.join(archiveDir, `${item.id}.json`);
4058
+ const bodyDst = path16.join(archiveDir, item.bodyPath);
4059
+ if (existsSync13(jsonSrc)) renameSync(jsonSrc, jsonDst);
4060
+ if (existsSync13(bodySrc)) renameSync(bodySrc, bodyDst);
3936
4061
  }
3937
4062
  function outboxItemPaths(item) {
3938
4063
  const { outboxDir } = ensurePlanOutboxDirs();
3939
4064
  return {
3940
- jsonPath: path15.join(outboxDir, `${item.id}.json`),
3941
- bodyPath: path15.join(outboxDir, item.bodyPath)
4065
+ jsonPath: path16.join(outboxDir, `${item.id}.json`),
4066
+ bodyPath: path16.join(outboxDir, item.bodyPath)
3942
4067
  };
3943
4068
  }
3944
4069
  function outboxInputFromItem(item, body) {
@@ -4124,7 +4249,7 @@ function markOutboxFailed(item, message) {
4124
4249
  }
4125
4250
 
4126
4251
  // src/plan-persist/drain.ts
4127
- import path16 from "node:path";
4252
+ import path17 from "node:path";
4128
4253
  async function drainPlanOutbox(opts = {}, deps = {}) {
4129
4254
  const items = listOutboxItems().filter(
4130
4255
  (item) => opts.outboxId ? item.id === opts.outboxId : true
@@ -4158,7 +4283,7 @@ async function drainPlanOutbox(opts = {}, deps = {}) {
4158
4283
  return result;
4159
4284
  }
4160
4285
  function loadOutboxById(outboxId) {
4161
- const jsonPath = path16.join(planOutboxDir(), `${outboxId}.json`);
4286
+ const jsonPath = path17.join(planOutboxDir(), `${outboxId}.json`);
4162
4287
  return readOutboxItem(jsonPath);
4163
4288
  }
4164
4289
 
@@ -4258,7 +4383,7 @@ async function dispatchRun(args) {
4258
4383
  const activeHarnessWorkers = [];
4259
4384
  for (const name of Object.keys(run.workers || {})) {
4260
4385
  const worker = readJson(
4261
- path17.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
4386
+ path18.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
4262
4387
  void 0
4263
4388
  );
4264
4389
  if (!worker?.taskId || !isPidAlive(worker.pid)) continue;
@@ -4467,7 +4592,7 @@ async function dispatchRun(args) {
4467
4592
  }
4468
4593
 
4469
4594
  // src/sweep.ts
4470
- import path18 from "node:path";
4595
+ import path19 from "node:path";
4471
4596
  async function sweepRun(args) {
4472
4597
  const pipeline = args.pipeline === true || args.pipeline === "true";
4473
4598
  try {
@@ -4480,7 +4605,7 @@ async function sweepRun(args) {
4480
4605
  const releasedLocalOrphans = [];
4481
4606
  for (const name of Object.keys(run.workers || {})) {
4482
4607
  const worker = readJson(
4483
- path18.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
4608
+ path19.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
4484
4609
  void 0
4485
4610
  );
4486
4611
  if (!worker || !worker.dispatched || !worker.taskId) continue;
@@ -4523,11 +4648,53 @@ async function sweepRun(args) {
4523
4648
  }
4524
4649
 
4525
4650
  // src/worktree.ts
4526
- import { existsSync as existsSync13, mkdirSync as mkdirSync5 } from "node:fs";
4651
+ import { existsSync as existsSync14, mkdirSync as mkdirSync5 } from "node:fs";
4652
+ import path22 from "node:path";
4653
+
4654
+ // src/default-repo.ts
4527
4655
  import path20 from "node:path";
4656
+ function expandConfiguredRepo(value) {
4657
+ return path20.resolve(resolveUserPath(value.trim()));
4658
+ }
4659
+ function fromConfigured(value, source, persistedInConfig) {
4660
+ const trimmed = value?.trim();
4661
+ if (!trimmed) return null;
4662
+ return {
4663
+ repo: expandConfiguredRepo(trimmed),
4664
+ source,
4665
+ persistedInConfig
4666
+ };
4667
+ }
4668
+ function resolveDefaultRepo(opts = {}) {
4669
+ const env = opts.env ?? process.env;
4670
+ const config = opts.config ?? loadUserConfig();
4671
+ const fromConfig = fromConfigured(config.defaultRepo, "config", true);
4672
+ if (fromConfig) return fromConfig;
4673
+ const fromDefaultEnv = fromConfigured(env.KYNVER_DEFAULT_REPO, "env_default_repo", false);
4674
+ if (fromDefaultEnv) return fromDefaultEnv;
4675
+ const fromHarnessEnv = fromConfigured(env.KYNVER_HARNESS_REPO, "env_harness_repo", false);
4676
+ if (fromHarnessEnv) return fromHarnessEnv;
4677
+ const discovered = discoverDefaultRepo({
4678
+ cwd: opts.cwd,
4679
+ runtimeModuleUrl: opts.runtimeModuleUrl
4680
+ });
4681
+ if (!discovered) return null;
4682
+ return {
4683
+ repo: discovered.repo,
4684
+ source: discovered.source,
4685
+ persistedInConfig: false
4686
+ };
4687
+ }
4688
+ function formatResolvedDefaultRepo(resolved) {
4689
+ return {
4690
+ defaultRepo: displayUserPath(resolved.repo),
4691
+ source: resolved.source,
4692
+ persistedInConfig: resolved.persistedInConfig
4693
+ };
4694
+ }
4528
4695
 
4529
4696
  // src/validate.ts
4530
- import path19 from "node:path";
4697
+ import path21 from "node:path";
4531
4698
  var RUN_ID_RE = /^[a-z0-9][a-z0-9._-]{0,127}$/i;
4532
4699
  function validateRunId(runId) {
4533
4700
  const trimmed = runId.trim();
@@ -4535,18 +4702,26 @@ function validateRunId(runId) {
4535
4702
  return trimmed;
4536
4703
  }
4537
4704
  function validateRepo(repo) {
4538
- const resolved = path19.resolve(repo);
4705
+ const resolved = path21.resolve(repo);
4539
4706
  if (resolved.includes("..")) throw new Error("repo path must not contain .. segments");
4540
4707
  return resolved;
4541
4708
  }
4542
4709
 
4543
4710
  // src/worktree.ts
4711
+ function resolveCreateRunRepo(args) {
4712
+ const explicit = typeof args.repo === "string" ? args.repo.trim() : "";
4713
+ if (explicit) return explicit;
4714
+ const resolved = resolveDefaultRepo();
4715
+ if (resolved) return resolved.repo;
4716
+ required("", "--repo (or set defaultRepo via `kynver setup` / KYNVER_DEFAULT_REPO)");
4717
+ return "";
4718
+ }
4544
4719
  function createRun(args) {
4545
- const repo = validateRepo(required(String(args.repo || ""), "--repo"));
4720
+ const repo = validateRepo(resolveCreateRunRepo(args));
4546
4721
  ensureGitRepo(repo);
4547
4722
  const id = args.id ? validateRunId(String(args.id)) : timestampSlug(String(args.name || "run"));
4548
4723
  const dir = runDirectory(id);
4549
- if (existsSync13(dir)) failExists(`run already exists: ${id}`);
4724
+ if (existsSync14(dir)) failExists(`run already exists: ${id}`);
4550
4725
  mkdirSync5(dir, { recursive: true });
4551
4726
  const base = String(args.base || "origin/main");
4552
4727
  const baseCommit = git(repo, ["rev-parse", base]).trim();
@@ -4560,12 +4735,12 @@ function createRun(args) {
4560
4735
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
4561
4736
  workers: {}
4562
4737
  };
4563
- writeJson(path20.join(dir, "run.json"), run);
4738
+ writeJson(path22.join(dir, "run.json"), run);
4564
4739
  console.log(JSON.stringify({ runId: id, runDir: dir, repo, base, baseCommit }, null, 2));
4565
4740
  }
4566
4741
  function listRuns() {
4567
4742
  const { runsDir } = getPaths();
4568
- const rows = listRunIds(runsDir).map((id) => readJson(path20.join(runDirectory(id), "run.json"), void 0)).filter(Boolean).map((run) => ({
4743
+ const rows = listRunIds(runsDir).map((id) => readJson(path22.join(runDirectory(id), "run.json"), void 0)).filter(Boolean).map((run) => ({
4569
4744
  id: run.id,
4570
4745
  name: run.name,
4571
4746
  status: run.status,
@@ -4580,7 +4755,7 @@ function failExists(message) {
4580
4755
  }
4581
4756
 
4582
4757
  // src/pipeline-tick.ts
4583
- import path30 from "node:path";
4758
+ import path32 from "node:path";
4584
4759
 
4585
4760
  // src/pipeline-dispatch.ts
4586
4761
  var RESERVED_REVIEW_STARTS = 1;
@@ -4634,10 +4809,10 @@ async function runPipelineDispatch(args, slots) {
4634
4809
  }
4635
4810
 
4636
4811
  // src/stale-reconcile.ts
4637
- import path22 from "node:path";
4812
+ import path24 from "node:path";
4638
4813
 
4639
4814
  // src/finalize.ts
4640
- import path21 from "node:path";
4815
+ import path23 from "node:path";
4641
4816
  var ACTIVE_RUN_STATUSES = /* @__PURE__ */ new Set(["running", "dispatching", "pending", "queued"]);
4642
4817
  function terminalStatusFor(run) {
4643
4818
  const names = Object.keys(run.workers || {});
@@ -4648,7 +4823,7 @@ function terminalStatusFor(run) {
4648
4823
  let anyLandingBlocked = false;
4649
4824
  for (const name of names) {
4650
4825
  const worker = readJson(
4651
- path21.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
4826
+ path23.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
4652
4827
  void 0
4653
4828
  );
4654
4829
  if (!worker) continue;
@@ -4700,7 +4875,7 @@ function reconcileStaleWorkers() {
4700
4875
  const now = Date.now();
4701
4876
  for (const run of listRunRecords()) {
4702
4877
  for (const name of Object.keys(run.workers || {})) {
4703
- const workerPath = path22.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
4878
+ const workerPath = path24.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
4704
4879
  const worker = readJson(workerPath, void 0);
4705
4880
  if (!worker || worker.status !== "running") {
4706
4881
  outcomes.push({
@@ -4794,7 +4969,7 @@ function reconcileRunsCli() {
4794
4969
  }
4795
4970
 
4796
4971
  // src/plan-progress-daemon-sync.ts
4797
- import path23 from "node:path";
4972
+ import path25 from "node:path";
4798
4973
 
4799
4974
  // src/plan-progress-sync.ts
4800
4975
  async function syncPlanProgress(args) {
@@ -4818,7 +4993,7 @@ async function syncActiveWorkerPlanProgress(runId, args) {
4818
4993
  const outcomes = [];
4819
4994
  for (const name of Object.keys(run.workers || {})) {
4820
4995
  const worker = readJson(
4821
- path23.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
4996
+ path25.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
4822
4997
  void 0
4823
4998
  );
4824
4999
  if (!worker?.dispatched || !worker.taskId) continue;
@@ -4867,7 +5042,7 @@ async function fetchWorkspaceRuntimePreferences(agentOsId, args) {
4867
5042
  }
4868
5043
 
4869
5044
  // src/cleanup.ts
4870
- import path28 from "node:path";
5045
+ import path30 from "node:path";
4871
5046
 
4872
5047
  // src/cleanup-types.ts
4873
5048
  var DEFAULT_NODE_MODULES_AGE_MS = 6 * 60 * 60 * 1e3;
@@ -4938,14 +5113,14 @@ function skipNodeModulesRemoval(input) {
4938
5113
  }
4939
5114
 
4940
5115
  // src/cleanup-execute.ts
4941
- import { existsSync as existsSync15, rmSync } from "node:fs";
4942
- import path25 from "node:path";
5116
+ import { existsSync as existsSync16, rmSync } from "node:fs";
5117
+ import path27 from "node:path";
4943
5118
 
4944
5119
  // src/cleanup-dir-size.ts
4945
- import { existsSync as existsSync14, readdirSync as readdirSync5, statSync as statSync2 } from "node:fs";
4946
- import path24 from "node:path";
5120
+ import { existsSync as existsSync15, readdirSync as readdirSync5, statSync as statSync2 } from "node:fs";
5121
+ import path26 from "node:path";
4947
5122
  function directorySizeBytes(root, maxEntries = 5e4) {
4948
- if (!existsSync14(root)) return 0;
5123
+ if (!existsSync15(root)) return 0;
4949
5124
  let total = 0;
4950
5125
  let seen = 0;
4951
5126
  const stack = [root];
@@ -4959,7 +5134,7 @@ function directorySizeBytes(root, maxEntries = 5e4) {
4959
5134
  }
4960
5135
  for (const name of entries) {
4961
5136
  if (seen++ > maxEntries) return null;
4962
- const full = path24.join(current, name);
5137
+ const full = path26.join(current, name);
4963
5138
  let st;
4964
5139
  try {
4965
5140
  st = statSync2(full);
@@ -4975,7 +5150,7 @@ function directorySizeBytes(root, maxEntries = 5e4) {
4975
5150
 
4976
5151
  // src/cleanup-execute.ts
4977
5152
  function removeNodeModules(candidate, execute) {
4978
- if (!existsSync15(candidate.path)) {
5153
+ if (!existsSync16(candidate.path)) {
4979
5154
  return {
4980
5155
  ...candidate,
4981
5156
  executed: false,
@@ -5006,7 +5181,7 @@ function removeNodeModules(candidate, execute) {
5006
5181
  }
5007
5182
  }
5008
5183
  function removeWorktree(candidate, execute) {
5009
- if (!existsSync15(candidate.path)) {
5184
+ if (!existsSync16(candidate.path)) {
5010
5185
  return {
5011
5186
  ...candidate,
5012
5187
  executed: false,
@@ -5023,7 +5198,7 @@ function removeWorktree(candidate, execute) {
5023
5198
  if (repo) {
5024
5199
  git(repo, ["worktree", "remove", "--force", candidate.path], { allowFailure: true });
5025
5200
  }
5026
- if (existsSync15(candidate.path)) {
5201
+ if (existsSync16(candidate.path)) {
5027
5202
  rmSync(candidate.path, { recursive: true, force: true });
5028
5203
  }
5029
5204
  return {
@@ -5043,20 +5218,20 @@ function removeWorktree(candidate, execute) {
5043
5218
  }
5044
5219
  }
5045
5220
  function isHarnessNodeModulesPath(targetPath, harnessRoot, worktreesDir) {
5046
- const resolved = path25.resolve(targetPath);
5047
- const nm = resolved.endsWith(`${path25.sep}node_modules`) ? resolved : null;
5221
+ const resolved = path27.resolve(targetPath);
5222
+ const nm = resolved.endsWith(`${path27.sep}node_modules`) ? resolved : null;
5048
5223
  if (!nm) return "path_outside_harness";
5049
- const rel = path25.relative(worktreesDir, nm);
5050
- if (rel.startsWith("..") || path25.isAbsolute(rel)) return "path_outside_harness";
5051
- const parts = rel.split(path25.sep);
5224
+ const rel = path27.relative(worktreesDir, nm);
5225
+ if (rel.startsWith("..") || path27.isAbsolute(rel)) return "path_outside_harness";
5226
+ const parts = rel.split(path27.sep);
5052
5227
  if (parts.length < 3 || parts[parts.length - 1] !== "node_modules") return "path_outside_harness";
5053
- if (!resolved.startsWith(path25.resolve(harnessRoot))) return "path_outside_harness";
5228
+ if (!resolved.startsWith(path27.resolve(harnessRoot))) return "path_outside_harness";
5054
5229
  return null;
5055
5230
  }
5056
5231
 
5057
5232
  // src/cleanup-scan.ts
5058
- import { existsSync as existsSync16, readdirSync as readdirSync6, statSync as statSync3 } from "node:fs";
5059
- import path26 from "node:path";
5233
+ import { existsSync as existsSync17, readdirSync as readdirSync6, statSync as statSync3 } from "node:fs";
5234
+ import path28 from "node:path";
5060
5235
  function pathAgeMs(target, now) {
5061
5236
  try {
5062
5237
  const mtime = statSync3(target).mtimeMs;
@@ -5066,17 +5241,17 @@ function pathAgeMs(target, now) {
5066
5241
  }
5067
5242
  }
5068
5243
  function isPathInside(child, parent) {
5069
- const rel = path26.relative(parent, child);
5070
- return rel === "" || !rel.startsWith("..") && !path26.isAbsolute(rel);
5244
+ const rel = path28.relative(parent, child);
5245
+ return rel === "" || !rel.startsWith("..") && !path28.isAbsolute(rel);
5071
5246
  }
5072
5247
  function scanNodeModulesCandidates(opts) {
5073
5248
  const candidates = [];
5074
5249
  const seen = /* @__PURE__ */ new Set();
5075
5250
  for (const entry of opts.index.values()) {
5076
5251
  if (opts.runIdFilter && entry.runId !== opts.runIdFilter) continue;
5077
- const nm = path26.join(entry.worktreePath, "node_modules");
5078
- if (!existsSync16(nm)) continue;
5079
- const resolved = path26.resolve(nm);
5252
+ const nm = path28.join(entry.worktreePath, "node_modules");
5253
+ if (!existsSync17(nm)) continue;
5254
+ const resolved = path28.resolve(nm);
5080
5255
  if (seen.has(resolved)) continue;
5081
5256
  seen.add(resolved);
5082
5257
  candidates.push({
@@ -5089,16 +5264,16 @@ function scanNodeModulesCandidates(opts) {
5089
5264
  ageMs: pathAgeMs(resolved, opts.now)
5090
5265
  });
5091
5266
  }
5092
- if (!opts.includeOrphans || !existsSync16(opts.worktreesDir)) return candidates;
5267
+ if (!opts.includeOrphans || !existsSync17(opts.worktreesDir)) return candidates;
5093
5268
  for (const runEntry of readdirSync6(opts.worktreesDir, { withFileTypes: true })) {
5094
5269
  if (!runEntry.isDirectory()) continue;
5095
- const runPath = path26.join(opts.worktreesDir, runEntry.name);
5270
+ const runPath = path28.join(opts.worktreesDir, runEntry.name);
5096
5271
  for (const workerEntry of readdirSync6(runPath, { withFileTypes: true })) {
5097
5272
  if (!workerEntry.isDirectory()) continue;
5098
- const worktreePath = path26.join(runPath, workerEntry.name);
5099
- const nm = path26.join(worktreePath, "node_modules");
5100
- if (!existsSync16(nm)) continue;
5101
- const resolved = path26.resolve(nm);
5273
+ const worktreePath = path28.join(runPath, workerEntry.name);
5274
+ const nm = path28.join(worktreePath, "node_modules");
5275
+ if (!existsSync17(nm)) continue;
5276
+ const resolved = path28.resolve(nm);
5102
5277
  if (seen.has(resolved)) continue;
5103
5278
  if (!isPathInside(resolved, opts.harnessRoot)) continue;
5104
5279
  seen.add(resolved);
@@ -5121,7 +5296,7 @@ function scanWorktreeCandidates(opts) {
5121
5296
  for (const entry of opts.index.values()) {
5122
5297
  if (opts.runIdFilter && entry.runId !== opts.runIdFilter) continue;
5123
5298
  const resolved = entry.worktreePath;
5124
- if (!existsSync16(resolved)) continue;
5299
+ if (!existsSync17(resolved)) continue;
5125
5300
  if (seen.has(resolved)) continue;
5126
5301
  seen.add(resolved);
5127
5302
  candidates.push({
@@ -5138,17 +5313,17 @@ function scanWorktreeCandidates(opts) {
5138
5313
  }
5139
5314
 
5140
5315
  // src/cleanup-worktree-index.ts
5141
- import path27 from "node:path";
5316
+ import path29 from "node:path";
5142
5317
  function buildWorktreeIndex() {
5143
5318
  const index = /* @__PURE__ */ new Map();
5144
5319
  for (const run of listRunRecords()) {
5145
5320
  for (const name of Object.keys(run.workers || {})) {
5146
- const workerPath = path27.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
5321
+ const workerPath = path29.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
5147
5322
  const worker = readJson(workerPath, void 0);
5148
5323
  if (!worker?.worktreePath) continue;
5149
5324
  const status = computeWorkerStatus(worker, { base: run.base, baseCommit: run.baseCommit });
5150
- index.set(path27.resolve(worker.worktreePath), {
5151
- worktreePath: path27.resolve(worker.worktreePath),
5325
+ index.set(path29.resolve(worker.worktreePath), {
5326
+ worktreePath: path29.resolve(worker.worktreePath),
5152
5327
  runId: run.id,
5153
5328
  workerName: name,
5154
5329
  run,
@@ -5163,7 +5338,7 @@ function buildWorktreeIndex() {
5163
5338
  // src/cleanup.ts
5164
5339
  function resolveOptions(options = {}) {
5165
5340
  const harnessRoot = options.harnessRoot ? resolveUserPath(options.harnessRoot) : resolveHarnessRoot();
5166
- const { worktreesDir } = options.harnessRoot ? { worktreesDir: path28.join(harnessRoot, "worktrees") } : getHarnessPaths();
5341
+ const { worktreesDir } = options.harnessRoot ? { worktreesDir: path30.join(harnessRoot, "worktrees") } : getHarnessPaths();
5167
5342
  const execute = options.execute === true;
5168
5343
  const nodeModulesAgeMs = options.nodeModulesAgeMs ?? DEFAULT_NODE_MODULES_AGE_MS;
5169
5344
  const worktreesAgeMs = options.worktreesAgeMs ?? 0;
@@ -5207,7 +5382,7 @@ function runHarnessCleanup(options = {}) {
5207
5382
  actions.push({ ...candidate, executed: false, skipped: true, skipReason: pathSkip });
5208
5383
  continue;
5209
5384
  }
5210
- const worktreePath = path28.resolve(candidate.path, "..");
5385
+ const worktreePath = path30.resolve(candidate.path, "..");
5211
5386
  const indexed = index.get(worktreePath) ?? null;
5212
5387
  const guardReason = skipNodeModulesRemoval({
5213
5388
  indexed,
@@ -5223,7 +5398,7 @@ function runHarnessCleanup(options = {}) {
5223
5398
  actions.push(removeNodeModules(candidate, resolved.execute));
5224
5399
  }
5225
5400
  for (const candidate of scanWorktreeCandidates(scanOpts)) {
5226
- const indexed = index.get(path28.resolve(candidate.path)) ?? null;
5401
+ const indexed = index.get(path30.resolve(candidate.path)) ?? null;
5227
5402
  const guardReason = skipWorktreeRemoval({
5228
5403
  indexed,
5229
5404
  includeOrphans: resolved.includeOrphans,
@@ -5288,8 +5463,8 @@ function isPipelineCleanupEnabled() {
5288
5463
 
5289
5464
  // src/installed-package-versions.ts
5290
5465
  import { readFile } from "node:fs/promises";
5291
- import { homedir as homedir5 } from "node:os";
5292
- import path29 from "node:path";
5466
+ import { homedir as homedir6 } from "node:os";
5467
+ import path31 from "node:path";
5293
5468
  var MANAGED_PACKAGES = [
5294
5469
  "@kynver-app/runtime",
5295
5470
  "@kynver-app/openclaw-agent-os",
@@ -5303,13 +5478,13 @@ function unique(values) {
5303
5478
  return [...new Set(values.filter((value) => Boolean(value)))];
5304
5479
  }
5305
5480
  function moduleRoots() {
5306
- const home = homedir5();
5307
- const openClawPrefix = trim(process.env.KYNVER_OPENCLAW_NPM_ROOT) ?? trim(process.env.OPENCLAW_NPM_ROOT) ?? path29.join(home, ".openclaw", "npm");
5308
- 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"));
5481
+ const home = homedir6();
5482
+ const openClawPrefix = trim(process.env.KYNVER_OPENCLAW_NPM_ROOT) ?? trim(process.env.OPENCLAW_NPM_ROOT) ?? path31.join(home, ".openclaw", "npm");
5483
+ 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"));
5309
5484
  return unique([
5310
- path29.join(openClawPrefix, "lib", "node_modules"),
5311
- path29.join(openClawPrefix, "node_modules"),
5312
- npmGlobalRoot.endsWith("node_modules") ? npmGlobalRoot : path29.join(npmGlobalRoot, "lib", "node_modules")
5485
+ path31.join(openClawPrefix, "lib", "node_modules"),
5486
+ path31.join(openClawPrefix, "node_modules"),
5487
+ npmGlobalRoot.endsWith("node_modules") ? npmGlobalRoot : path31.join(npmGlobalRoot, "lib", "node_modules")
5313
5488
  ]);
5314
5489
  }
5315
5490
  async function readVersion(packageJsonPath) {
@@ -5325,7 +5500,7 @@ async function collectInstalledPackageVersions(observedAt = (/* @__PURE__ */ new
5325
5500
  const out = {};
5326
5501
  for (const packageName of MANAGED_PACKAGES) {
5327
5502
  for (const root of roots) {
5328
- const packageJsonPath = path29.join(root, packageName, "package.json");
5503
+ const packageJsonPath = path31.join(root, packageName, "package.json");
5329
5504
  const version = await readVersion(packageJsonPath);
5330
5505
  if (!version) continue;
5331
5506
  out[packageName] = { version, observedAt, path: packageJsonPath };
@@ -5341,7 +5516,7 @@ async function completeFinishedWorkers(runId, args) {
5341
5516
  const outcomes = [];
5342
5517
  for (const name of Object.keys(run.workers || {})) {
5343
5518
  const worker = readJson(
5344
- path30.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
5519
+ path32.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
5345
5520
  void 0
5346
5521
  );
5347
5522
  if (!worker?.taskId || worker.localOnly) continue;
@@ -5483,7 +5658,7 @@ async function runDaemon(args) {
5483
5658
  }
5484
5659
 
5485
5660
  // src/plan-progress.ts
5486
- import path31 from "node:path";
5661
+ import path33 from "node:path";
5487
5662
 
5488
5663
  // src/bounded-build/constants.ts
5489
5664
  var DEFAULT_BUILD_MEM_BUDGET_BYTES = 1536 * 1024 * 1024;
@@ -5769,7 +5944,7 @@ async function emitPlanProgress(args) {
5769
5944
  }
5770
5945
  function verifyPlanLocal(args) {
5771
5946
  const worktree = required(args.worktree ? String(args.worktree) : void 0, "worktree");
5772
- const cwd = path31.resolve(worktree);
5947
+ const cwd = path33.resolve(worktree);
5773
5948
  const summary = runHarnessVerifyCommands(cwd);
5774
5949
  const emitJson = args.json === true || args.json === "true";
5775
5950
  const payload = { passed: summary.passed, worktree: cwd, steps: summary.steps };
@@ -5818,9 +5993,9 @@ async function verifyPlan(args) {
5818
5993
  }
5819
5994
 
5820
5995
  // src/harness-verify-cli.ts
5821
- import path32 from "node:path";
5996
+ import path34 from "node:path";
5822
5997
  function runHarnessVerifyCli(args) {
5823
- const cwd = path32.resolve(required(args.worktree ? String(args.worktree) : void 0, "worktree"));
5998
+ const cwd = path34.resolve(required(args.worktree ? String(args.worktree) : void 0, "worktree"));
5824
5999
  const emitJson = args.json === true || args.json === "true" || args.emitJson === true || args.emitJson === "true";
5825
6000
  const commands = [];
5826
6001
  const rawCmd = args.command;
@@ -5864,7 +6039,7 @@ function runHarnessVerifyCli(args) {
5864
6039
  }
5865
6040
 
5866
6041
  // src/plan-persist-cli.ts
5867
- import { readFileSync as readFileSync7 } from "node:fs";
6042
+ import { readFileSync as readFileSync8 } from "node:fs";
5868
6043
  var OPERATIONS = ["create", "add_version", "update_metadata"];
5869
6044
  var FAILURE_KINDS = [
5870
6045
  "approval_guard",
@@ -5876,7 +6051,7 @@ var FAILURE_KINDS = [
5876
6051
  function readBodyArg(args) {
5877
6052
  const bodyFile = args.bodyFile ? String(args.bodyFile) : void 0;
5878
6053
  if (bodyFile) {
5879
- return { body: readFileSync7(bodyFile, "utf8"), bodyPathHint: bodyFile };
6054
+ return { body: readFileSync8(bodyFile, "utf8"), bodyPathHint: bodyFile };
5880
6055
  }
5881
6056
  const inline = args.body ? String(args.body) : void 0;
5882
6057
  if (inline) return { body: inline };
@@ -5964,7 +6139,7 @@ function runCleanupCli(args) {
5964
6139
  }
5965
6140
 
5966
6141
  // src/monitor/monitor.service.ts
5967
- import path34 from "node:path";
6142
+ import path36 from "node:path";
5968
6143
 
5969
6144
  // src/monitor/monitor.classify.ts
5970
6145
  function expectedLeaseOwner(runId) {
@@ -6020,11 +6195,11 @@ function classifyWorkerHealth(input) {
6020
6195
  }
6021
6196
 
6022
6197
  // src/monitor/monitor.store.ts
6023
- import { existsSync as existsSync17, mkdirSync as mkdirSync6, readdirSync as readdirSync7, unlinkSync as unlinkSync2 } from "node:fs";
6024
- import path33 from "node:path";
6198
+ import { existsSync as existsSync18, mkdirSync as mkdirSync6, readdirSync as readdirSync7, unlinkSync as unlinkSync2 } from "node:fs";
6199
+ import path35 from "node:path";
6025
6200
  function monitorsDir() {
6026
6201
  const { harnessRoot } = getHarnessPaths();
6027
- const dir = path33.join(harnessRoot, "monitors");
6202
+ const dir = path35.join(harnessRoot, "monitors");
6028
6203
  mkdirSync6(dir, { recursive: true });
6029
6204
  return dir;
6030
6205
  }
@@ -6032,7 +6207,7 @@ function monitorIdFor(runId, workerName) {
6032
6207
  return workerName ? `${safeSlug(runId)}--${safeSlug(workerName)}` : safeSlug(runId);
6033
6208
  }
6034
6209
  function monitorPath(monitorId) {
6035
- return path33.join(monitorsDir(), `${monitorId}.json`);
6210
+ return path35.join(monitorsDir(), `${monitorId}.json`);
6036
6211
  }
6037
6212
  function loadMonitorSession(monitorId) {
6038
6213
  return readJson(monitorPath(monitorId), void 0);
@@ -6042,18 +6217,18 @@ function saveMonitorSession(session) {
6042
6217
  }
6043
6218
  function deleteMonitorSession(monitorId) {
6044
6219
  const file = monitorPath(monitorId);
6045
- if (!existsSync17(file)) return false;
6220
+ if (!existsSync18(file)) return false;
6046
6221
  unlinkSync2(file);
6047
6222
  return true;
6048
6223
  }
6049
6224
  function listMonitorSessions() {
6050
6225
  const dir = monitorsDir();
6051
- if (!existsSync17(dir)) return [];
6226
+ if (!existsSync18(dir)) return [];
6052
6227
  const entries = [];
6053
6228
  for (const name of readdirSync7(dir)) {
6054
6229
  if (!name.endsWith(".json")) continue;
6055
6230
  const session = readJson(
6056
- path33.join(dir, name),
6231
+ path35.join(dir, name),
6057
6232
  void 0
6058
6233
  );
6059
6234
  if (!session?.monitorId) continue;
@@ -6144,7 +6319,7 @@ async function fetchTaskLeasesForWorkers(input) {
6144
6319
  // src/monitor/monitor.service.ts
6145
6320
  function workerRecord2(runId, name) {
6146
6321
  return readJson(
6147
- path34.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
6322
+ path36.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
6148
6323
  void 0
6149
6324
  );
6150
6325
  }
@@ -6346,18 +6521,18 @@ async function runMonitorLoop(args) {
6346
6521
 
6347
6522
  // src/monitor/monitor-spawn.ts
6348
6523
  import { spawn as spawn4 } from "node:child_process";
6349
- import { closeSync as closeSync4, existsSync as existsSync18, openSync as openSync4 } from "node:fs";
6350
- import path35 from "node:path";
6351
- import { fileURLToPath as fileURLToPath2 } from "node:url";
6524
+ import { closeSync as closeSync4, existsSync as existsSync19, openSync as openSync4 } from "node:fs";
6525
+ import path37 from "node:path";
6526
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
6352
6527
  function resolveDefaultCliPath2() {
6353
- return path35.join(fileURLToPath2(new URL(".", import.meta.url)), "..", "cli.js");
6528
+ return path37.join(fileURLToPath3(new URL(".", import.meta.url)), "..", "cli.js");
6354
6529
  }
6355
6530
  function spawnMonitorSidecar(opts) {
6356
6531
  const cliPath = opts.cliPath ?? resolveDefaultCliPath2();
6357
- if (!existsSync18(cliPath)) return void 0;
6532
+ if (!existsSync19(cliPath)) return void 0;
6358
6533
  const monitorId = monitorIdFor(opts.runId, opts.workerName);
6359
6534
  const { harnessRoot } = getHarnessPaths();
6360
- const logPath = path35.join(harnessRoot, "monitors", `${monitorId}.log`);
6535
+ const logPath = path37.join(harnessRoot, "monitors", `${monitorId}.log`);
6361
6536
  let logFd;
6362
6537
  try {
6363
6538
  logFd = openSync4(logPath, "a");
@@ -6477,22 +6652,22 @@ async function monitorTickCli(args) {
6477
6652
  }
6478
6653
 
6479
6654
  // src/package-version.ts
6480
- import { existsSync as existsSync19, readFileSync as readFileSync8 } from "node:fs";
6655
+ import { existsSync as existsSync20, readFileSync as readFileSync9 } from "node:fs";
6481
6656
  import { dirname, join } from "node:path";
6482
- import { fileURLToPath as fileURLToPath3 } from "node:url";
6657
+ import { fileURLToPath as fileURLToPath4 } from "node:url";
6483
6658
  function resolvePackageRoot(moduleUrl) {
6484
- let dir = dirname(fileURLToPath3(moduleUrl));
6659
+ let dir = dirname(fileURLToPath4(moduleUrl));
6485
6660
  for (let depth = 0; depth < 6; depth += 1) {
6486
- if (existsSync19(join(dir, "package.json"))) return dir;
6661
+ if (existsSync20(join(dir, "package.json"))) return dir;
6487
6662
  const parent = dirname(dir);
6488
6663
  if (parent === dir) break;
6489
6664
  dir = parent;
6490
6665
  }
6491
- throw new Error(`package.json not found above ${dirname(fileURLToPath3(moduleUrl))}`);
6666
+ throw new Error(`package.json not found above ${dirname(fileURLToPath4(moduleUrl))}`);
6492
6667
  }
6493
6668
  function readOwnPackageVersion(moduleUrl = import.meta.url) {
6494
6669
  const pkgPath = join(resolvePackageRoot(moduleUrl), "package.json");
6495
- const pkg = JSON.parse(readFileSync8(pkgPath, "utf8"));
6670
+ const pkg = JSON.parse(readFileSync9(pkgPath, "utf8"));
6496
6671
  if (typeof pkg.version !== "string" || !pkg.version.trim()) {
6497
6672
  throw new Error(`Missing package.json version at ${pkgPath}`);
6498
6673
  }
@@ -6513,12 +6688,12 @@ function handleCliVersionFlag(argv, moduleUrl = import.meta.url, binName) {
6513
6688
  }
6514
6689
 
6515
6690
  // src/doctor/runtime-takeover.ts
6516
- import path37 from "node:path";
6691
+ import path39 from "node:path";
6517
6692
 
6518
6693
  // src/doctor/runtime-takeover.probes.ts
6519
- import { accessSync, constants, existsSync as existsSync20, readFileSync as readFileSync9 } from "node:fs";
6520
- import { homedir as homedir6 } from "node:os";
6521
- import path36 from "node:path";
6694
+ import { accessSync, constants, existsSync as existsSync21, readFileSync as readFileSync10 } from "node:fs";
6695
+ import { homedir as homedir7 } from "node:os";
6696
+ import path38 from "node:path";
6522
6697
  import { spawnSync as spawnSync6 } from "node:child_process";
6523
6698
  function captureCommand(bin, args) {
6524
6699
  try {
@@ -6547,7 +6722,7 @@ function tokenPrefix(token) {
6547
6722
  return trimmed.length <= 12 ? `${trimmed}\u2026` : `${trimmed.slice(0, 12)}\u2026`;
6548
6723
  }
6549
6724
  function isWritable(target) {
6550
- if (!existsSync20(target)) return false;
6725
+ if (!existsSync21(target)) return false;
6551
6726
  try {
6552
6727
  accessSync(target, constants.W_OK);
6553
6728
  return true;
@@ -6560,15 +6735,15 @@ var defaultRuntimeTakeoverProbes = {
6560
6735
  commandOnPath: (bin) => captureCommand(process.platform === "win32" ? "where" : "which", [bin]),
6561
6736
  kynverVersion: (bin) => captureCommand(bin, ["--version"]),
6562
6737
  loadConfig: () => loadUserConfig(),
6563
- configFilePath: () => path36.join(homedir6(), ".kynver", "config.json"),
6564
- credentialsFilePath: () => path36.join(homedir6(), ".kynver", "credentials"),
6738
+ configFilePath: () => path38.join(homedir7(), ".kynver", "config.json"),
6739
+ credentialsFilePath: () => path38.join(homedir7(), ".kynver", "credentials"),
6565
6740
  readCredentials: () => {
6566
- const credPath = path36.join(homedir6(), ".kynver", "credentials");
6567
- if (!existsSync20(credPath)) {
6741
+ const credPath = path38.join(homedir7(), ".kynver", "credentials");
6742
+ if (!existsSync21(credPath)) {
6568
6743
  return { hasApiKey: false };
6569
6744
  }
6570
6745
  try {
6571
- const parsed = JSON.parse(readFileSync9(credPath, "utf8"));
6746
+ const parsed = JSON.parse(readFileSync10(credPath, "utf8"));
6572
6747
  return {
6573
6748
  hasApiKey: Boolean(parsed.apiKey?.trim()),
6574
6749
  runnerTokenPrefix: tokenPrefix(parsed.runnerToken),
@@ -6586,20 +6761,119 @@ var defaultRuntimeTakeoverProbes = {
6586
6761
  openclawCronSecret: Boolean(process.env.OPENCLAW_CRON_SECRET?.trim()),
6587
6762
  kynverHarnessRoot: process.env.KYNVER_HARNESS_ROOT?.trim() || void 0,
6588
6763
  opusHarnessRoot: process.env.OPUS_HARNESS_ROOT?.trim() || void 0,
6589
- kynverSchedulerProvider: process.env.KYNVER_SCHEDULER_PROVIDER?.trim() || void 0
6764
+ kynverSchedulerProvider: process.env.KYNVER_SCHEDULER_PROVIDER?.trim() || void 0,
6765
+ qstashTokenPresent: Boolean(process.env.QSTASH_TOKEN?.trim()),
6766
+ kynverHostedDeployment: (() => {
6767
+ const v = process.env.KYNVER_HOSTED_DEPLOYMENT?.trim().toLowerCase();
6768
+ return v === "1" || v === "true" || v === "yes";
6769
+ })()
6590
6770
  }),
6591
6771
  harnessRoot: () => resolveHarnessRoot(),
6592
- legacyOpenclawHarnessRoot: () => path36.join(homedir6(), ".openclaw", "harness"),
6593
- pathExists: (target) => existsSync20(target),
6772
+ legacyOpenclawHarnessRoot: () => path38.join(homedir7(), ".openclaw", "harness"),
6773
+ pathExists: (target) => existsSync21(target),
6594
6774
  pathWritable: (target) => isWritable(target),
6595
6775
  vercelVersion: () => captureCommand("vercel", ["--version"]),
6596
6776
  vercelWhoami: () => captureCommand("vercel", ["whoami"])
6597
6777
  };
6598
6778
 
6599
- // src/doctor/runtime-takeover.ts
6779
+ // src/doctor/runtime-takeover-scheduler.ts
6600
6780
  function check(partial) {
6601
6781
  return partial;
6602
6782
  }
6783
+ function assessRuntimeTakeoverScheduler(env, ctx) {
6784
+ const runnerOpenclaw = env.kynverSchedulerProvider === "openclaw-cron";
6785
+ const runnerQstash = env.kynverSchedulerProvider === "qstash";
6786
+ const hostedDeployment = Boolean(env.qstashTokenPresent) || Boolean(env.kynverHostedDeployment);
6787
+ const daemonDispatchReady = Boolean(ctx.agentOsId?.trim()) && Boolean(ctx.apiBaseUrl?.trim()) && ctx.hasScopedRunnerToken;
6788
+ const hostedSchedulerProcess = hostedDeployment && !daemonDispatchReady;
6789
+ const deploymentNeedsQstash = hostedSchedulerProcess && !env.qstashTokenPresent && env.kynverSchedulerProvider !== "qstash";
6790
+ const deploymentOpenclaw = hostedSchedulerProcess && env.kynverSchedulerProvider === "openclaw-cron";
6791
+ if (daemonDispatchReady && runnerOpenclaw) {
6792
+ return check({
6793
+ id: "hotspot_openclaw_scheduler",
6794
+ label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
6795
+ status: "warn",
6796
+ summary: "KYNVER_SCHEDULER_PROVIDER=openclaw-cron on this runner \u2014 dispatch is owned by kynver daemon; unset the OpenClaw override",
6797
+ 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.",
6798
+ details: {
6799
+ schedulerProvider: env.kynverSchedulerProvider ?? null,
6800
+ dispatchPath: "kynver-daemon-pipeline-tick",
6801
+ hostedDeployment
6802
+ }
6803
+ });
6804
+ }
6805
+ if (daemonDispatchReady) {
6806
+ return check({
6807
+ id: "hotspot_openclaw_scheduler",
6808
+ label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
6809
+ status: "pass",
6810
+ 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",
6811
+ details: {
6812
+ schedulerProvider: env.kynverSchedulerProvider ?? null,
6813
+ dispatchPath: "kynver-daemon-pipeline-tick",
6814
+ hostedDeployment
6815
+ }
6816
+ });
6817
+ }
6818
+ if (runnerOpenclaw) {
6819
+ return check({
6820
+ id: "hotspot_openclaw_scheduler",
6821
+ label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
6822
+ status: "warn",
6823
+ summary: "KYNVER_SCHEDULER_PROVIDER=openclaw-cron on this runner \u2014 hosted dispatch still depends on the OpenClaw local-cron adapter",
6824
+ 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.",
6825
+ details: { schedulerProvider: env.kynverSchedulerProvider ?? null, hostedDeployment }
6826
+ });
6827
+ }
6828
+ if (deploymentOpenclaw || deploymentNeedsQstash) {
6829
+ return check({
6830
+ id: "hotspot_openclaw_scheduler",
6831
+ label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
6832
+ status: "warn",
6833
+ 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",
6834
+ 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.",
6835
+ details: {
6836
+ schedulerProvider: env.kynverSchedulerProvider ?? null,
6837
+ qstashTokenPresent: env.qstashTokenPresent ?? false,
6838
+ hostedDeployment
6839
+ }
6840
+ });
6841
+ }
6842
+ if (hostedDeployment && env.qstashTokenPresent && !env.kynverSchedulerProvider) {
6843
+ return check({
6844
+ id: "hotspot_openclaw_scheduler",
6845
+ label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
6846
+ status: "pass",
6847
+ summary: "QSTASH_TOKEN present; hosted scheduler auto-selects qstash (explicit KYNVER_SCHEDULER_PROVIDER=qstash optional)",
6848
+ details: {
6849
+ schedulerProvider: null,
6850
+ qstashTokenPresent: true,
6851
+ hostedDeployment
6852
+ }
6853
+ });
6854
+ }
6855
+ if (!env.kynverSchedulerProvider) {
6856
+ return check({
6857
+ id: "hotspot_openclaw_scheduler",
6858
+ label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
6859
+ status: "pass",
6860
+ summary: "No KYNVER_SCHEDULER_PROVIDER on runner (expected) \u2014 finish runner setup so daemon pipeline-tick owns dispatch",
6861
+ details: { schedulerProvider: null, dispatchPath: "kynver-daemon-pipeline-tick-pending" }
6862
+ });
6863
+ }
6864
+ return check({
6865
+ id: "hotspot_openclaw_scheduler",
6866
+ label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
6867
+ status: "pass",
6868
+ summary: `KYNVER_SCHEDULER_PROVIDER=${env.kynverSchedulerProvider}`,
6869
+ details: { schedulerProvider: env.kynverSchedulerProvider ?? null }
6870
+ });
6871
+ }
6872
+
6873
+ // src/doctor/runtime-takeover.ts
6874
+ function check2(partial) {
6875
+ return partial;
6876
+ }
6603
6877
  function summarizeCounts(sections) {
6604
6878
  const counts = { pass: 0, warn: 0, fail: 0 };
6605
6879
  for (const section of sections) {
@@ -6616,14 +6890,14 @@ function assessCliPackage(probes) {
6616
6890
  const firstPath = onPath ? which.stdout.split(/\r?\n/)[0]?.trim() : void 0;
6617
6891
  const displayCliPath = firstPath ? displayUserPath(firstPath) : void 0;
6618
6892
  const checks = [
6619
- check({
6893
+ check2({
6620
6894
  id: "cli_running_version",
6621
6895
  label: "Running @kynver-app/runtime version",
6622
6896
  status: "pass",
6623
6897
  summary: `@kynver-app/runtime ${runningVersion}`,
6624
6898
  details: { version: runningVersion }
6625
6899
  }),
6626
- check({
6900
+ check2({
6627
6901
  id: "cli_on_path",
6628
6902
  label: "kynver executable on PATH",
6629
6903
  status: onPath ? "pass" : "warn",
@@ -6637,7 +6911,7 @@ function assessCliPackage(probes) {
6637
6911
  const installedVersion = versionProbe.stdout.replace(/^kynver\s+/i, "").trim() || void 0;
6638
6912
  const versionMatch = versionProbe.ok && (!installedVersion || installedVersion === runningVersion);
6639
6913
  checks.push(
6640
- check({
6914
+ check2({
6641
6915
  id: "cli_installed_version",
6642
6916
  label: "Installed kynver CLI version matches running package",
6643
6917
  status: versionMatch ? "pass" : "warn",
@@ -6655,7 +6929,7 @@ function assessUserConfig(probes) {
6655
6929
  const exists = probes.pathExists(configPath);
6656
6930
  const config = probes.loadConfig();
6657
6931
  const checks = [
6658
- check({
6932
+ check2({
6659
6933
  id: "config_file",
6660
6934
  label: "~/.kynver/config.json present",
6661
6935
  status: exists ? "pass" : "fail",
@@ -6667,10 +6941,16 @@ function assessUserConfig(probes) {
6667
6941
  if (exists) {
6668
6942
  const apiBaseUrl = config.apiBaseUrl?.trim();
6669
6943
  const agentOsId = config.agentOsId?.trim();
6670
- const defaultRepo = config.defaultRepo?.trim();
6671
- const displayDefaultRepo = defaultRepo ? displayUserPath(defaultRepo) : null;
6944
+ const resolvedDefaultRepo = resolveDefaultRepo({ config });
6945
+ const formatted = resolvedDefaultRepo ? formatResolvedDefaultRepo(resolvedDefaultRepo) : null;
6946
+ let defaultRepoRemediation;
6947
+ if (!resolvedDefaultRepo) {
6948
+ defaultRepoRemediation = "Run `kynver setup` from a Kynver checkout (auto-discovers repo) or `kynver setup --repo /path/to/Kynver`.";
6949
+ } else if (!resolvedDefaultRepo.persistedInConfig) {
6950
+ defaultRepoRemediation = "Run `kynver setup` (no --repo) to persist discovered defaultRepo in ~/.kynver/config.json.";
6951
+ }
6672
6952
  checks.push(
6673
- check({
6953
+ check2({
6674
6954
  id: "config_api_base_url",
6675
6955
  label: "Default API base URL",
6676
6956
  status: apiBaseUrl ? "pass" : "warn",
@@ -6678,7 +6958,7 @@ function assessUserConfig(probes) {
6678
6958
  remediation: apiBaseUrl ? void 0 : "Set `apiBaseUrl` via `kynver setup --api-base-url https://\u2026`.",
6679
6959
  details: { apiBaseUrl: apiBaseUrl ?? null }
6680
6960
  }),
6681
- check({
6961
+ check2({
6682
6962
  id: "config_agent_os_id",
6683
6963
  label: "Default AgentOS id",
6684
6964
  status: agentOsId ? "pass" : "warn",
@@ -6686,14 +6966,16 @@ function assessUserConfig(probes) {
6686
6966
  remediation: agentOsId ? void 0 : "Set `agentOsId` via `kynver setup --agent-os-id <uuid>`.",
6687
6967
  details: { agentOsId: agentOsId ?? null, agentOsSlug: config.agentOsSlug ?? null }
6688
6968
  }),
6689
- check({
6969
+ check2({
6690
6970
  id: "config_default_repo",
6691
6971
  label: "Default repo path",
6692
- status: defaultRepo ? "pass" : "warn",
6693
- summary: displayDefaultRepo ?? "Not set (pass --repo on `kynver run create`)",
6694
- remediation: defaultRepo ? void 0 : "Set `defaultRepo` via `kynver setup --repo /path/to/repo`.",
6972
+ status: resolvedDefaultRepo ? "pass" : "warn",
6973
+ 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)",
6974
+ remediation: defaultRepoRemediation,
6695
6975
  details: {
6696
- defaultRepo: displayDefaultRepo,
6976
+ defaultRepo: formatted?.defaultRepo ?? null,
6977
+ source: formatted?.source ?? null,
6978
+ persistedInConfig: formatted?.persistedInConfig ?? false,
6697
6979
  harnessRoot: config.harnessRoot ? redactHomePath(config.harnessRoot) : null
6698
6980
  }
6699
6981
  })
@@ -6714,7 +6996,7 @@ function assessRunnerToken(probes) {
6714
6996
  const scopedSaved = Boolean(savedToken) && (!targetAgentOsId || !savedAgentOsId || savedAgentOsId === targetAgentOsId);
6715
6997
  const hasScoped = Boolean(envToken?.startsWith("krc1.")) || scopedSaved && savedToken?.startsWith("krc1.");
6716
6998
  const checks = [
6717
- check({
6999
+ check2({
6718
7000
  id: "runner_token_scoped",
6719
7001
  label: "Scoped runner token (krc1.*) ready",
6720
7002
  status: hasScoped ? "pass" : "fail",
@@ -6727,7 +7009,7 @@ function assessRunnerToken(probes) {
6727
7009
  credentialsPath: displayCredPath
6728
7010
  }
6729
7011
  }),
6730
- check({
7012
+ check2({
6731
7013
  id: "runner_token_agent_os_match",
6732
7014
  label: "Saved runner token matches configured agentOsId",
6733
7015
  status: !savedToken || !targetAgentOsId || !savedAgentOsId || savedAgentOsId === targetAgentOsId ? "pass" : "warn",
@@ -6735,7 +7017,7 @@ function assessRunnerToken(probes) {
6735
7017
  remediation: savedToken && targetAgentOsId && savedAgentOsId && savedAgentOsId !== targetAgentOsId ? "`kynver runner credential --agent-os-id <configured-id>` to mint a workspace-bound token." : void 0,
6736
7018
  details: { configuredAgentOsId: targetAgentOsId ?? null, savedAgentOsId: savedAgentOsId ?? null }
6737
7019
  }),
6738
- check({
7020
+ check2({
6739
7021
  id: "runner_api_key_for_refresh",
6740
7022
  label: "API key available for token refresh",
6741
7023
  status: creds.hasApiKey || Boolean(process.env.KYNVER_API_KEY?.trim()) ? "pass" : "warn",
@@ -6754,7 +7036,7 @@ function assessVercelCli(probes) {
6754
7036
  id: "vercel_cli",
6755
7037
  label: "Vercel CLI",
6756
7038
  checks: [
6757
- check({
7039
+ check2({
6758
7040
  id: "vercel_installed",
6759
7041
  label: "Vercel CLI installed",
6760
7042
  status: installed ? "pass" : "warn",
@@ -6762,7 +7044,7 @@ function assessVercelCli(probes) {
6762
7044
  remediation: installed ? void 0 : "Install Vercel CLI (`npm i -g vercel`) for deploy evidence and env pulls.",
6763
7045
  details: { stderr: version.stderr || null }
6764
7046
  }),
6765
- check({
7047
+ check2({
6766
7048
  id: "vercel_authenticated",
6767
7049
  label: "Vercel CLI authenticated",
6768
7050
  status: !installed ? "warn" : whoami.ok ? "pass" : "warn",
@@ -6775,8 +7057,8 @@ function assessVercelCli(probes) {
6775
7057
  }
6776
7058
  function assessHarnessDirs(probes) {
6777
7059
  const harnessRoot = probes.harnessRoot();
6778
- const runsDir = path37.join(harnessRoot, "runs");
6779
- const worktreesDir = path37.join(harnessRoot, "worktrees");
7060
+ const runsDir = path39.join(harnessRoot, "runs");
7061
+ const worktreesDir = path39.join(harnessRoot, "worktrees");
6780
7062
  const displayHarnessRoot = redactHomePath(harnessRoot);
6781
7063
  const displayRunsDir = redactHomePath(runsDir);
6782
7064
  const displayWorktreesDir = redactHomePath(worktreesDir);
@@ -6786,14 +7068,14 @@ function assessHarnessDirs(probes) {
6786
7068
  id: "harness_dirs",
6787
7069
  label: "Harness / daemon directories",
6788
7070
  checks: [
6789
- check({
7071
+ check2({
6790
7072
  id: "harness_root",
6791
7073
  label: "Harness root resolved",
6792
7074
  status: "pass",
6793
7075
  summary: displayHarnessRoot,
6794
7076
  details: { harnessRoot: displayHarnessRoot }
6795
7077
  }),
6796
- check({
7078
+ check2({
6797
7079
  id: "harness_runs_dir",
6798
7080
  label: "Runs directory ready",
6799
7081
  status: runsExists && probes.pathWritable(runsDir) ? "pass" : runsExists ? "warn" : "warn",
@@ -6801,7 +7083,7 @@ function assessHarnessDirs(probes) {
6801
7083
  remediation: runsExists && !probes.pathWritable(runsDir) ? `Fix permissions on ${displayRunsDir} or set KYNVER_HARNESS_ROOT to a writable path.` : void 0,
6802
7084
  details: { runsDir: displayRunsDir, exists: runsExists, writable: probes.pathWritable(runsDir) }
6803
7085
  }),
6804
- check({
7086
+ check2({
6805
7087
  id: "harness_worktrees_dir",
6806
7088
  label: "Worktrees directory ready",
6807
7089
  status: worktreesExists && probes.pathWritable(worktreesDir) ? "pass" : "warn",
@@ -6822,7 +7104,7 @@ function assessCallbackAuth(probes) {
6822
7104
  const creds = probes.readCredentials();
6823
7105
  const savedScoped = creds.runnerTokenPrefix?.startsWith("krc1.");
6824
7106
  const checks = [
6825
- check({
7107
+ check2({
6826
7108
  id: "callback_base_url",
6827
7109
  label: "Callback base URL configured",
6828
7110
  status: baseUrl ? usingLegacyBase ? "warn" : "pass" : "fail",
@@ -6833,7 +7115,7 @@ function assessCallbackAuth(probes) {
6833
7115
  baseUrl: baseUrl ?? null
6834
7116
  }
6835
7117
  }),
6836
- check({
7118
+ check2({
6837
7119
  id: "callback_auth_mode",
6838
7120
  label: "Callback auth uses scoped runner token",
6839
7121
  status: envScoped || savedScoped ? "pass" : legacySecret ? "warn" : "fail",
@@ -6849,15 +7131,22 @@ function assessCallbackAuth(probes) {
6849
7131
  }
6850
7132
  function assessOpenclawHotspots(probes) {
6851
7133
  const env = probes.envSnapshot();
7134
+ const config = probes.loadConfig();
7135
+ const creds = probes.readCredentials();
6852
7136
  const harnessRoot = probes.harnessRoot();
6853
7137
  const legacyRoot = probes.legacyOpenclawHarnessRoot();
6854
7138
  const displayHarnessRoot = redactHomePath(harnessRoot);
6855
7139
  const displayLegacyRoot = redactHomePath(legacyRoot);
6856
7140
  const displayOpusHarnessRoot = env.opusHarnessRoot ? redactHomePath(env.opusHarnessRoot) : null;
6857
7141
  const legacyHarnessActive = harnessRoot === legacyRoot && probes.pathExists(legacyRoot);
6858
- const schedulerOpenclaw = !env.kynverSchedulerProvider || env.kynverSchedulerProvider === "openclaw-cron";
7142
+ const targetAgentOsId = config.agentOsId?.trim();
7143
+ const envToken = env.kynverRunnerTokenPrefix;
7144
+ const savedToken = creds.runnerTokenPrefix;
7145
+ const savedAgentOsId = creds.runnerTokenAgentOsId;
7146
+ const scopedSaved = Boolean(savedToken) && (!targetAgentOsId || !savedAgentOsId || savedAgentOsId === targetAgentOsId);
7147
+ const hasScopedRunnerToken = Boolean(envToken?.startsWith("krc1.")) || scopedSaved && Boolean(savedToken?.startsWith("krc1."));
6859
7148
  const checks = [
6860
- check({
7149
+ check2({
6861
7150
  id: "hotspot_legacy_harness_root",
6862
7151
  label: "Legacy ~/.openclaw/harness still active",
6863
7152
  status: legacyHarnessActive ? "warn" : "pass",
@@ -6865,7 +7154,7 @@ function assessOpenclawHotspots(probes) {
6865
7154
  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,
6866
7155
  details: { harnessRoot: displayHarnessRoot, legacyRoot: displayLegacyRoot, opusHarnessRoot: displayOpusHarnessRoot }
6867
7156
  }),
6868
- check({
7157
+ check2({
6869
7158
  id: "hotspot_openclaw_env_secrets",
6870
7159
  label: "OpenClaw deployment secrets in runner env",
6871
7160
  status: env.openclawCronSecret || env.openclawCronFireBaseUrl ? "warn" : "pass",
@@ -6875,15 +7164,12 @@ function assessOpenclawHotspots(probes) {
6875
7164
  ].filter(Boolean).join("; ") : "No OpenClaw cron env overrides on this runner",
6876
7165
  remediation: env.openclawCronSecret || env.openclawCronFireBaseUrl ? "Move to KYNVER_API_URL + scoped runner tokens; unset OpenClaw cron env on user-hosted runners." : void 0
6877
7166
  }),
6878
- check({
6879
- id: "hotspot_openclaw_scheduler",
6880
- label: "openclaw-cron scheduler dependency (deployment)",
6881
- status: schedulerOpenclaw ? "warn" : "pass",
6882
- 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}`,
6883
- 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,
6884
- details: { schedulerProvider: env.kynverSchedulerProvider ?? null }
7167
+ assessRuntimeTakeoverScheduler(env, {
7168
+ agentOsId: targetAgentOsId ?? null,
7169
+ apiBaseUrl: config.apiBaseUrl?.trim() ?? env.kynverApiUrl ?? null,
7170
+ hasScopedRunnerToken
6885
7171
  }),
6886
- check({
7172
+ check2({
6887
7173
  id: "hotspot_lease_source_names",
6888
7174
  label: "Harness lease/completion source names",
6889
7175
  status: "pass",
@@ -6975,9 +7261,9 @@ function usage(code = 0) {
6975
7261
  "Usage:",
6976
7262
  " kynver login --api-key KEY",
6977
7263
  " kynver runner credential [--agent-os-id ID] [--base-url URL]",
6978
- " kynver setup [--api-base-url URL] [--agent-os-id ID] [--agent-os-slug SLUG] [--repo PATH] [--max-workers N] [--provider claude|cursor]",
7264
+ " kynver setup [--api-base-url URL] [--agent-os-id ID] [--agent-os-slug SLUG] [--repo PATH] [--discover-repo] [--max-workers N] [--provider claude|cursor]",
6979
7265
  " kynver daemon --run RUN_ID --agent-os-id AOS_ID [--execute] [--interval-ms MS]",
6980
- " kynver run create --repo /path/repo [--name name] [--base origin/main]",
7266
+ " kynver run create [--repo /path/repo] [--name name] [--base origin/main]",
6981
7267
  " kynver run list",
6982
7268
  " kynver run status --run RUN_ID",
6983
7269
  " 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 /]",
@@ -7070,7 +7356,7 @@ async function main(argv = process.argv.slice(2)) {
7070
7356
  if (scope === "monitor" && action === "run-loop") return void await monitorRunLoopCli(args);
7071
7357
  unknownCommand(scope, action);
7072
7358
  }
7073
- var isCliEntry = process.argv[1] && realpathSync.native(process.argv[1]) === realpathSync.native(fileURLToPath4(import.meta.url));
7359
+ var isCliEntry = process.argv[1] && realpathSync.native(process.argv[1]) === realpathSync.native(fileURLToPath5(import.meta.url));
7074
7360
  if (isCliEntry) {
7075
7361
  void main().catch((error) => {
7076
7362
  console.error(error);