@kynver-app/runtime 0.1.48 → 0.1.49

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.
@@ -0,0 +1,20 @@
1
+ import type { DoctorCheck } from "./doctor.types.js";
2
+ export interface RuntimeTakeoverSchedulerEnv {
3
+ kynverSchedulerProvider?: string;
4
+ /** True when this process has QStash credentials (typical Kynver server deploy). */
5
+ qstashTokenPresent?: boolean;
6
+ /** Explicit hosted-deploy marker (`KYNVER_HOSTED_DEPLOYMENT=1|true|yes`). */
7
+ kynverHostedDeployment?: boolean;
8
+ }
9
+ export interface RuntimeTakeoverSchedulerContext {
10
+ agentOsId?: string | null;
11
+ apiBaseUrl?: string | null;
12
+ hasScopedRunnerToken: boolean;
13
+ }
14
+ /**
15
+ * Scheduler readiness for runtime takeover.
16
+ *
17
+ * Runner hosts drive dispatch via `kynver daemon` → `pipeline-tick` → `operator/tick`
18
+ * and must not depend on OpenClaw cron. Server/hosted deploys should resolve to QStash.
19
+ */
20
+ export declare function assessRuntimeTakeoverScheduler(env: RuntimeTakeoverSchedulerEnv, ctx: RuntimeTakeoverSchedulerContext): DoctorCheck;
@@ -26,6 +26,8 @@ export interface RuntimeTakeoverProbes {
26
26
  kynverHarnessRoot?: string;
27
27
  opusHarnessRoot?: string;
28
28
  kynverSchedulerProvider?: string;
29
+ qstashTokenPresent?: boolean;
30
+ kynverHostedDeployment?: boolean;
29
31
  };
30
32
  harnessRoot(): string;
31
33
  legacyOpenclawHarnessRoot(): string;
package/dist/index.js CHANGED
@@ -1956,7 +1956,7 @@ function inferModelRoutingFromTask(task) {
1956
1956
  rule: "lane:landing"
1957
1957
  };
1958
1958
  }
1959
- if (ref.includes("review") || title.startsWith("review ") || roleLane.includes("review")) {
1959
+ if (ref.includes("review") || /^review[\s:]/.test(title) || roleLane.includes("review")) {
1960
1960
  if (isOpusLane(ref, title) || roleLane === "deep_reviewer") {
1961
1961
  return { model: "claude-opus-4-7", provider: "claude", rule: "lane:deep_review" };
1962
1962
  }
@@ -2419,6 +2419,11 @@ function isHarnessExpertReviewWorker(worker) {
2419
2419
 
2420
2420
  // src/pr-handoff/pr-handoff-assess.ts
2421
2421
  var REVIEW_LANE_RULE = /^(lane:)?(review|deep_review|planning|landing)(:|$)/i;
2422
+ var REVIEW_PERSONA_SLUGS = /* @__PURE__ */ new Set(["lorentz", "sentinel"]);
2423
+ var NO_PR_COMMITS_BETWEEN_RE = /no commits between/i;
2424
+ function isGhNoCommitsBetweenError(detail) {
2425
+ return Boolean(detail && NO_PR_COMMITS_BETWEEN_RE.test(detail));
2426
+ }
2422
2427
  function trimOrNull4(value) {
2423
2428
  if (typeof value !== "string") return null;
2424
2429
  const trimmed = value.trim();
@@ -2437,8 +2442,30 @@ function extractPrUrlFromText(value) {
2437
2442
  );
2438
2443
  return m ? trimOrNull4(m[0]) : null;
2439
2444
  }
2440
- function hasWorkProduct(snapshot) {
2445
+ function countCommitsAheadOfBase(worktreePath, baseRef, exec) {
2446
+ const base = baseRef.trim();
2447
+ if (!base) return null;
2448
+ const count = exec.git(worktreePath, ["rev-list", "--count", `${base}..HEAD`]);
2449
+ if (count.status !== 0) return null;
2450
+ const n = Number.parseInt(count.stdout.trim(), 10);
2451
+ return Number.isFinite(n) ? n : null;
2452
+ }
2453
+ function isReviewArtifactWorker(worker, snapshot) {
2454
+ if (snapshot.changedFiles.length > 0) return false;
2455
+ const persona = trimOrNull4(worker.personaSlug)?.toLowerCase();
2456
+ if (persona && REVIEW_PERSONA_SLUGS.has(persona)) return true;
2457
+ const rule = trimOrNull4(worker.routingRule) ?? "";
2458
+ if (rule && REVIEW_LANE_RULE.test(rule)) return true;
2459
+ return false;
2460
+ }
2461
+ function hasWorkProduct(snapshot, options) {
2441
2462
  if (snapshot.changedFiles.length > 0) return true;
2463
+ const baseRef = trimOrNull4(options?.baseRef);
2464
+ if (baseRef && options?.exec && options.worktreePath) {
2465
+ const ahead = countCommitsAheadOfBase(options.worktreePath, baseRef, options.exec);
2466
+ if (ahead === 0) return false;
2467
+ if (ahead !== null && ahead > 0) return true;
2468
+ }
2442
2469
  if (trimOrNull4(snapshot.headCommit)) return true;
2443
2470
  if (committedHead(snapshot.gitAncestry)) return true;
2444
2471
  return false;
@@ -2459,6 +2486,13 @@ function assessPrHandoffRequirement(input) {
2459
2486
  if (rule && REVIEW_LANE_RULE.test(rule)) {
2460
2487
  return { required: false, reason: "review_lane" };
2461
2488
  }
2489
+ const workerCtx = input.worker ?? {
2490
+ personaSlug: input.personaSlug,
2491
+ routingRule: input.routingRule
2492
+ };
2493
+ if (isReviewArtifactWorker(workerCtx, input.snapshot)) {
2494
+ return { required: false, reason: "review_artifact" };
2495
+ }
2462
2496
  if (trimOrNull4(input.patchPath) || trimOrNull4(input.artifactBundlePath)) {
2463
2497
  return { required: false, reason: "patch_or_bundle" };
2464
2498
  }
@@ -2466,7 +2500,12 @@ function assessPrHandoffRequirement(input) {
2466
2500
  if (prUrl) {
2467
2501
  return { required: false, reason: "already_has_pr" };
2468
2502
  }
2469
- if (!hasWorkProduct(input.snapshot)) {
2503
+ const workProductOpts = input.exec && input.baseRef ? {
2504
+ baseRef: input.baseRef,
2505
+ exec: input.exec,
2506
+ worktreePath: input.snapshot.worktreePath
2507
+ } : void 0;
2508
+ if (!hasWorkProduct(input.snapshot, workProductOpts)) {
2470
2509
  return { required: false, reason: "no_work_product" };
2471
2510
  }
2472
2511
  return { required: true, snapshot: input.snapshot };
@@ -2675,15 +2714,19 @@ function ensurePrReadyHandoff(input, exec = defaultPrHandoffExec) {
2675
2714
  prUrl: prUrlHint,
2676
2715
  headCommit: null
2677
2716
  });
2717
+ const baseRef = input.run.baseCommit?.trim() || input.run.base?.trim() || "origin/main";
2678
2718
  const requirement = assessPrHandoffRequirement({
2679
2719
  dispatched: input.worker.dispatched,
2680
2720
  routingRule: input.worker.routingRule,
2721
+ personaSlug: input.worker.personaSlug,
2681
2722
  prUrl: prUrlHint,
2682
2723
  taskTitle: input.worker.taskTitle,
2683
2724
  executorRef: input.worker.executorRef,
2684
2725
  parentTaskId: input.worker.parentTaskId,
2685
- personaSlug: input.worker.personaSlug,
2686
2726
  taskPrUrl: input.worker.taskPrUrl,
2727
+ baseRef,
2728
+ exec,
2729
+ worker: input.worker,
2687
2730
  snapshot
2688
2731
  });
2689
2732
  if (!requirement.required) {
@@ -2768,6 +2811,14 @@ function ensurePrReadyHandoff(input, exec = defaultPrHandoffExec) {
2768
2811
  exec
2769
2812
  });
2770
2813
  if (!pr.ok || !pr.prUrl) {
2814
+ if (isGhNoCommitsBetweenError(pr.detail)) {
2815
+ return {
2816
+ ok: true,
2817
+ headCommit: headCommit ?? void 0,
2818
+ committed,
2819
+ pushed
2820
+ };
2821
+ }
2771
2822
  const dirty = snapshot.changedFiles.length;
2772
2823
  const detail = dirty ? `${dirty} uncommitted change(s) and no PR URL after handoff attempt` : "no PR URL after handoff attempt";
2773
2824
  return {
@@ -6638,7 +6689,12 @@ var defaultRuntimeTakeoverProbes = {
6638
6689
  openclawCronSecret: Boolean(process.env.OPENCLAW_CRON_SECRET?.trim()),
6639
6690
  kynverHarnessRoot: process.env.KYNVER_HARNESS_ROOT?.trim() || void 0,
6640
6691
  opusHarnessRoot: process.env.OPUS_HARNESS_ROOT?.trim() || void 0,
6641
- kynverSchedulerProvider: process.env.KYNVER_SCHEDULER_PROVIDER?.trim() || void 0
6692
+ kynverSchedulerProvider: process.env.KYNVER_SCHEDULER_PROVIDER?.trim() || void 0,
6693
+ qstashTokenPresent: Boolean(process.env.QSTASH_TOKEN?.trim()),
6694
+ kynverHostedDeployment: (() => {
6695
+ const v = process.env.KYNVER_HOSTED_DEPLOYMENT?.trim().toLowerCase();
6696
+ return v === "1" || v === "true" || v === "yes";
6697
+ })()
6642
6698
  }),
6643
6699
  harnessRoot: () => resolveHarnessRoot(),
6644
6700
  legacyOpenclawHarnessRoot: () => path36.join(homedir6(), ".openclaw", "harness"),
@@ -6648,10 +6704,76 @@ var defaultRuntimeTakeoverProbes = {
6648
6704
  vercelWhoami: () => captureCommand("vercel", ["whoami"])
6649
6705
  };
6650
6706
 
6651
- // src/doctor/runtime-takeover.ts
6707
+ // src/doctor/runtime-takeover-scheduler.ts
6652
6708
  function check(partial) {
6653
6709
  return partial;
6654
6710
  }
6711
+ function assessRuntimeTakeoverScheduler(env, ctx) {
6712
+ const runnerOpenclaw = env.kynverSchedulerProvider === "openclaw-cron";
6713
+ const runnerQstash = env.kynverSchedulerProvider === "qstash";
6714
+ const hostedDeployment = Boolean(env.qstashTokenPresent) || Boolean(env.kynverHostedDeployment);
6715
+ const daemonDispatchReady = Boolean(ctx.agentOsId?.trim()) && Boolean(ctx.apiBaseUrl?.trim()) && ctx.hasScopedRunnerToken;
6716
+ const deploymentNeedsQstash = hostedDeployment && !env.qstashTokenPresent && env.kynverSchedulerProvider !== "qstash";
6717
+ const deploymentOpenclaw = hostedDeployment && env.kynverSchedulerProvider === "openclaw-cron";
6718
+ if (runnerOpenclaw) {
6719
+ return check({
6720
+ id: "hotspot_openclaw_scheduler",
6721
+ label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
6722
+ status: "warn",
6723
+ summary: "KYNVER_SCHEDULER_PROVIDER=openclaw-cron on this runner \u2014 hosted dispatch still depends on the OpenClaw local-cron adapter",
6724
+ 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.",
6725
+ details: { schedulerProvider: env.kynverSchedulerProvider ?? null, hostedDeployment }
6726
+ });
6727
+ }
6728
+ if (deploymentOpenclaw || deploymentNeedsQstash) {
6729
+ return check({
6730
+ id: "hotspot_openclaw_scheduler",
6731
+ label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
6732
+ status: "warn",
6733
+ summary: deploymentOpenclaw ? "Hosted deployment has KYNVER_SCHEDULER_PROVIDER=openclaw-cron \u2014 AgentOS scheduled ticks should use QStash" : "Hosted deployment without QSTASH_TOKEN \u2014 scheduler may fall back to openclaw-cron",
6734
+ remediation: "Set QSTASH_TOKEN and KYNVER_SCHEDULER_PROVIDER=qstash on the Kynver server. User runners use `kynver daemon` for dispatch and should not set a scheduler provider.",
6735
+ details: {
6736
+ schedulerProvider: env.kynverSchedulerProvider ?? null,
6737
+ qstashTokenPresent: env.qstashTokenPresent ?? false,
6738
+ hostedDeployment
6739
+ }
6740
+ });
6741
+ }
6742
+ if (daemonDispatchReady) {
6743
+ return check({
6744
+ id: "hotspot_openclaw_scheduler",
6745
+ label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
6746
+ status: "pass",
6747
+ summary: runnerQstash ? "Runner override qstash present; hosted dispatch still owned by kynver daemon pipeline-tick" : "Hosted dispatch owned by kynver daemon (pipeline-tick \u2192 operator/tick); no OpenClaw cron on runner",
6748
+ details: {
6749
+ schedulerProvider: env.kynverSchedulerProvider ?? null,
6750
+ dispatchPath: "kynver-daemon-pipeline-tick",
6751
+ hostedDeployment
6752
+ }
6753
+ });
6754
+ }
6755
+ if (!env.kynverSchedulerProvider) {
6756
+ return check({
6757
+ id: "hotspot_openclaw_scheduler",
6758
+ label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
6759
+ status: "pass",
6760
+ summary: "No KYNVER_SCHEDULER_PROVIDER on runner (expected) \u2014 finish runner setup so daemon pipeline-tick owns dispatch",
6761
+ details: { schedulerProvider: null, dispatchPath: "kynver-daemon-pipeline-tick-pending" }
6762
+ });
6763
+ }
6764
+ return check({
6765
+ id: "hotspot_openclaw_scheduler",
6766
+ label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
6767
+ status: "pass",
6768
+ summary: `KYNVER_SCHEDULER_PROVIDER=${env.kynverSchedulerProvider}`,
6769
+ details: { schedulerProvider: env.kynverSchedulerProvider ?? null }
6770
+ });
6771
+ }
6772
+
6773
+ // src/doctor/runtime-takeover.ts
6774
+ function check2(partial) {
6775
+ return partial;
6776
+ }
6655
6777
  function summarizeCounts(sections) {
6656
6778
  const counts = { pass: 0, warn: 0, fail: 0 };
6657
6779
  for (const section of sections) {
@@ -6668,14 +6790,14 @@ function assessCliPackage(probes) {
6668
6790
  const firstPath = onPath ? which.stdout.split(/\r?\n/)[0]?.trim() : void 0;
6669
6791
  const displayCliPath = firstPath ? displayUserPath(firstPath) : void 0;
6670
6792
  const checks = [
6671
- check({
6793
+ check2({
6672
6794
  id: "cli_running_version",
6673
6795
  label: "Running @kynver-app/runtime version",
6674
6796
  status: "pass",
6675
6797
  summary: `@kynver-app/runtime ${runningVersion}`,
6676
6798
  details: { version: runningVersion }
6677
6799
  }),
6678
- check({
6800
+ check2({
6679
6801
  id: "cli_on_path",
6680
6802
  label: "kynver executable on PATH",
6681
6803
  status: onPath ? "pass" : "warn",
@@ -6689,7 +6811,7 @@ function assessCliPackage(probes) {
6689
6811
  const installedVersion = versionProbe.stdout.replace(/^kynver\s+/i, "").trim() || void 0;
6690
6812
  const versionMatch = versionProbe.ok && (!installedVersion || installedVersion === runningVersion);
6691
6813
  checks.push(
6692
- check({
6814
+ check2({
6693
6815
  id: "cli_installed_version",
6694
6816
  label: "Installed kynver CLI version matches running package",
6695
6817
  status: versionMatch ? "pass" : "warn",
@@ -6707,7 +6829,7 @@ function assessUserConfig(probes) {
6707
6829
  const exists = probes.pathExists(configPath);
6708
6830
  const config = probes.loadConfig();
6709
6831
  const checks = [
6710
- check({
6832
+ check2({
6711
6833
  id: "config_file",
6712
6834
  label: "~/.kynver/config.json present",
6713
6835
  status: exists ? "pass" : "fail",
@@ -6722,7 +6844,7 @@ function assessUserConfig(probes) {
6722
6844
  const defaultRepo = config.defaultRepo?.trim();
6723
6845
  const displayDefaultRepo = defaultRepo ? displayUserPath(defaultRepo) : null;
6724
6846
  checks.push(
6725
- check({
6847
+ check2({
6726
6848
  id: "config_api_base_url",
6727
6849
  label: "Default API base URL",
6728
6850
  status: apiBaseUrl ? "pass" : "warn",
@@ -6730,7 +6852,7 @@ function assessUserConfig(probes) {
6730
6852
  remediation: apiBaseUrl ? void 0 : "Set `apiBaseUrl` via `kynver setup --api-base-url https://\u2026`.",
6731
6853
  details: { apiBaseUrl: apiBaseUrl ?? null }
6732
6854
  }),
6733
- check({
6855
+ check2({
6734
6856
  id: "config_agent_os_id",
6735
6857
  label: "Default AgentOS id",
6736
6858
  status: agentOsId ? "pass" : "warn",
@@ -6738,7 +6860,7 @@ function assessUserConfig(probes) {
6738
6860
  remediation: agentOsId ? void 0 : "Set `agentOsId` via `kynver setup --agent-os-id <uuid>`.",
6739
6861
  details: { agentOsId: agentOsId ?? null, agentOsSlug: config.agentOsSlug ?? null }
6740
6862
  }),
6741
- check({
6863
+ check2({
6742
6864
  id: "config_default_repo",
6743
6865
  label: "Default repo path",
6744
6866
  status: defaultRepo ? "pass" : "warn",
@@ -6766,7 +6888,7 @@ function assessRunnerToken(probes) {
6766
6888
  const scopedSaved = Boolean(savedToken) && (!targetAgentOsId || !savedAgentOsId || savedAgentOsId === targetAgentOsId);
6767
6889
  const hasScoped = Boolean(envToken?.startsWith("krc1.")) || scopedSaved && savedToken?.startsWith("krc1.");
6768
6890
  const checks = [
6769
- check({
6891
+ check2({
6770
6892
  id: "runner_token_scoped",
6771
6893
  label: "Scoped runner token (krc1.*) ready",
6772
6894
  status: hasScoped ? "pass" : "fail",
@@ -6779,7 +6901,7 @@ function assessRunnerToken(probes) {
6779
6901
  credentialsPath: displayCredPath
6780
6902
  }
6781
6903
  }),
6782
- check({
6904
+ check2({
6783
6905
  id: "runner_token_agent_os_match",
6784
6906
  label: "Saved runner token matches configured agentOsId",
6785
6907
  status: !savedToken || !targetAgentOsId || !savedAgentOsId || savedAgentOsId === targetAgentOsId ? "pass" : "warn",
@@ -6787,7 +6909,7 @@ function assessRunnerToken(probes) {
6787
6909
  remediation: savedToken && targetAgentOsId && savedAgentOsId && savedAgentOsId !== targetAgentOsId ? "`kynver runner credential --agent-os-id <configured-id>` to mint a workspace-bound token." : void 0,
6788
6910
  details: { configuredAgentOsId: targetAgentOsId ?? null, savedAgentOsId: savedAgentOsId ?? null }
6789
6911
  }),
6790
- check({
6912
+ check2({
6791
6913
  id: "runner_api_key_for_refresh",
6792
6914
  label: "API key available for token refresh",
6793
6915
  status: creds.hasApiKey || Boolean(process.env.KYNVER_API_KEY?.trim()) ? "pass" : "warn",
@@ -6806,7 +6928,7 @@ function assessVercelCli(probes) {
6806
6928
  id: "vercel_cli",
6807
6929
  label: "Vercel CLI",
6808
6930
  checks: [
6809
- check({
6931
+ check2({
6810
6932
  id: "vercel_installed",
6811
6933
  label: "Vercel CLI installed",
6812
6934
  status: installed ? "pass" : "warn",
@@ -6814,7 +6936,7 @@ function assessVercelCli(probes) {
6814
6936
  remediation: installed ? void 0 : "Install Vercel CLI (`npm i -g vercel`) for deploy evidence and env pulls.",
6815
6937
  details: { stderr: version.stderr || null }
6816
6938
  }),
6817
- check({
6939
+ check2({
6818
6940
  id: "vercel_authenticated",
6819
6941
  label: "Vercel CLI authenticated",
6820
6942
  status: !installed ? "warn" : whoami.ok ? "pass" : "warn",
@@ -6838,14 +6960,14 @@ function assessHarnessDirs(probes) {
6838
6960
  id: "harness_dirs",
6839
6961
  label: "Harness / daemon directories",
6840
6962
  checks: [
6841
- check({
6963
+ check2({
6842
6964
  id: "harness_root",
6843
6965
  label: "Harness root resolved",
6844
6966
  status: "pass",
6845
6967
  summary: displayHarnessRoot,
6846
6968
  details: { harnessRoot: displayHarnessRoot }
6847
6969
  }),
6848
- check({
6970
+ check2({
6849
6971
  id: "harness_runs_dir",
6850
6972
  label: "Runs directory ready",
6851
6973
  status: runsExists && probes.pathWritable(runsDir) ? "pass" : runsExists ? "warn" : "warn",
@@ -6853,7 +6975,7 @@ function assessHarnessDirs(probes) {
6853
6975
  remediation: runsExists && !probes.pathWritable(runsDir) ? `Fix permissions on ${displayRunsDir} or set KYNVER_HARNESS_ROOT to a writable path.` : void 0,
6854
6976
  details: { runsDir: displayRunsDir, exists: runsExists, writable: probes.pathWritable(runsDir) }
6855
6977
  }),
6856
- check({
6978
+ check2({
6857
6979
  id: "harness_worktrees_dir",
6858
6980
  label: "Worktrees directory ready",
6859
6981
  status: worktreesExists && probes.pathWritable(worktreesDir) ? "pass" : "warn",
@@ -6874,7 +6996,7 @@ function assessCallbackAuth(probes) {
6874
6996
  const creds = probes.readCredentials();
6875
6997
  const savedScoped = creds.runnerTokenPrefix?.startsWith("krc1.");
6876
6998
  const checks = [
6877
- check({
6999
+ check2({
6878
7000
  id: "callback_base_url",
6879
7001
  label: "Callback base URL configured",
6880
7002
  status: baseUrl ? usingLegacyBase ? "warn" : "pass" : "fail",
@@ -6885,7 +7007,7 @@ function assessCallbackAuth(probes) {
6885
7007
  baseUrl: baseUrl ?? null
6886
7008
  }
6887
7009
  }),
6888
- check({
7010
+ check2({
6889
7011
  id: "callback_auth_mode",
6890
7012
  label: "Callback auth uses scoped runner token",
6891
7013
  status: envScoped || savedScoped ? "pass" : legacySecret ? "warn" : "fail",
@@ -6901,15 +7023,22 @@ function assessCallbackAuth(probes) {
6901
7023
  }
6902
7024
  function assessOpenclawHotspots(probes) {
6903
7025
  const env = probes.envSnapshot();
7026
+ const config = probes.loadConfig();
7027
+ const creds = probes.readCredentials();
6904
7028
  const harnessRoot = probes.harnessRoot();
6905
7029
  const legacyRoot = probes.legacyOpenclawHarnessRoot();
6906
7030
  const displayHarnessRoot = redactHomePath(harnessRoot);
6907
7031
  const displayLegacyRoot = redactHomePath(legacyRoot);
6908
7032
  const displayOpusHarnessRoot = env.opusHarnessRoot ? redactHomePath(env.opusHarnessRoot) : null;
6909
7033
  const legacyHarnessActive = harnessRoot === legacyRoot && probes.pathExists(legacyRoot);
6910
- const schedulerOpenclaw = !env.kynverSchedulerProvider || env.kynverSchedulerProvider === "openclaw-cron";
7034
+ const targetAgentOsId = config.agentOsId?.trim();
7035
+ const envToken = env.kynverRunnerTokenPrefix;
7036
+ const savedToken = creds.runnerTokenPrefix;
7037
+ const savedAgentOsId = creds.runnerTokenAgentOsId;
7038
+ const scopedSaved = Boolean(savedToken) && (!targetAgentOsId || !savedAgentOsId || savedAgentOsId === targetAgentOsId);
7039
+ const hasScopedRunnerToken = Boolean(envToken?.startsWith("krc1.")) || scopedSaved && Boolean(savedToken?.startsWith("krc1."));
6911
7040
  const checks = [
6912
- check({
7041
+ check2({
6913
7042
  id: "hotspot_legacy_harness_root",
6914
7043
  label: "Legacy ~/.openclaw/harness still active",
6915
7044
  status: legacyHarnessActive ? "warn" : "pass",
@@ -6917,7 +7046,7 @@ function assessOpenclawHotspots(probes) {
6917
7046
  remediation: legacyHarnessActive ? "Set KYNVER_HARNESS_ROOT=~/.kynver/harness (or run setup), migrate artifacts, retire OPUS_HARNESS_ROOT." : env.opusHarnessRoot ? "Prefer KYNVER_HARNESS_ROOT over OPUS_HARNESS_ROOT." : void 0,
6918
7047
  details: { harnessRoot: displayHarnessRoot, legacyRoot: displayLegacyRoot, opusHarnessRoot: displayOpusHarnessRoot }
6919
7048
  }),
6920
- check({
7049
+ check2({
6921
7050
  id: "hotspot_openclaw_env_secrets",
6922
7051
  label: "OpenClaw deployment secrets in runner env",
6923
7052
  status: env.openclawCronSecret || env.openclawCronFireBaseUrl ? "warn" : "pass",
@@ -6927,15 +7056,12 @@ function assessOpenclawHotspots(probes) {
6927
7056
  ].filter(Boolean).join("; ") : "No OpenClaw cron env overrides on this runner",
6928
7057
  remediation: env.openclawCronSecret || env.openclawCronFireBaseUrl ? "Move to KYNVER_API_URL + scoped runner tokens; unset OpenClaw cron env on user-hosted runners." : void 0
6929
7058
  }),
6930
- check({
6931
- id: "hotspot_openclaw_scheduler",
6932
- label: "openclaw-cron scheduler dependency (deployment)",
6933
- status: schedulerOpenclaw ? "warn" : "pass",
6934
- summary: schedulerOpenclaw ? env.kynverSchedulerProvider === "openclaw-cron" ? "KYNVER_SCHEDULER_PROVIDER=openclaw-cron \u2014 AgentOS ticks still routed via OpenClaw local cron adapter" : "KYNVER_SCHEDULER_PROVIDER unset \u2014 server may fall back to openclaw-cron when QStash is absent" : `KYNVER_SCHEDULER_PROVIDER=${env.kynverSchedulerProvider}`,
6935
- remediation: schedulerOpenclaw ? "On Kynver-hosted scheduler: set KYNVER_SCHEDULER_PROVIDER=qstash where QStash is configured; retire openclaw-cron stub when runtime daemon owns dispatch." : void 0,
6936
- details: { schedulerProvider: env.kynverSchedulerProvider ?? null }
7059
+ assessRuntimeTakeoverScheduler(env, {
7060
+ agentOsId: targetAgentOsId ?? null,
7061
+ apiBaseUrl: config.apiBaseUrl?.trim() ?? env.kynverApiUrl ?? null,
7062
+ hasScopedRunnerToken
6937
7063
  }),
6938
- check({
7064
+ check2({
6939
7065
  id: "hotspot_lease_source_names",
6940
7066
  label: "Harness lease/completion source names",
6941
7067
  status: "pass",