@kynver-app/runtime 0.1.47 → 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;
@@ -0,0 +1,8 @@
1
+ export interface HarnessExpertReviewTaskShape {
2
+ title?: string;
3
+ personaSlug?: string | null;
4
+ parentTaskId?: string | null;
5
+ executorRef?: string | null;
6
+ }
7
+ /** Expert review workers must not inherit landing-only PR gates or open new PRs. */
8
+ export declare function isHarnessExpertReviewWorker(worker: HarnessExpertReviewTaskShape): boolean;
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
  }
@@ -2149,6 +2149,7 @@ function buildPrompt(input) {
2149
2149
  "Structured final result (recommended): record completion as JSON with summary, laneExpertise { whatChanged, why, files, prUrls, verification, risks, blockers, lessonsLearned, laneGuidance }, and targetPrReconciliation [{ prUrl, outcome: merged|skipped|blocked, mergeCommit?, reason? }] for every target PR on landing-only tasks.",
2150
2150
  "Completion handoff (required): before you stop, ensure the harness records a final result \u2014 summarize outcome in your last message and append a heartbeat line with phase `complete`. If you leave uncommitted changes or committed work without a PR, the orchestrator blocks completion until a GitHub PR exists (or you discard/commit cleanly). Exiting with only dirty files and no PR routes to salvage review, not production review.",
2151
2151
  "PR-ready handoff: for substantial implementation work, commit, push, and open a GitHub PR (draft OK) on your branch before finishing \u2014 or rely on the harness to run `gh pr create` at completion when `gh` is authenticated.",
2152
+ "Expert review / production-review workers (Dalton/Lorentz, plan-review-task, scheduledJob reviewer children): do NOT open new implementation PRs \u2014 review the parent task's existing PR and record reviewVerdict in finalResult; landing-contract targetPrReconciliation does not apply.",
2152
2153
  "Worker resource guard: do not run full monorepo verification (`npm run typecheck`, `npm run build`, or equivalent) from this worker lane unless an operator explicitly requests it. Use targeted checks for touched paths and rely on CI/operator lanes for heavy gates.",
2153
2154
  "npm publish boundary: do not run `npm publish`, do not republish `@kynver-app/*` packages, and do not block on an operator to publish. When you need newer runtime code than npm, use this repo checkout (`npm run kynver:build`, `npm run kynver`) and record evidence: packages/kynver-runtime/package.json version + git ref in your completion report.",
2154
2155
  "If verification fails (including OOM), append a heartbeat line immediately with the last command, failure reason, dirty-file status, commit/PR handoff state, and next action so recovery does not require log spelunking.",
@@ -2390,8 +2391,39 @@ function completionPostSucceeded(summary) {
2390
2391
  return summary.taskAdvanced;
2391
2392
  }
2392
2393
 
2394
+ // src/harness-expert-review.ts
2395
+ var EXPERT_LANE_REVIEW_REF = "expert-lane-pr-review:";
2396
+ var PLAN_REVIEW_EXECUTOR_REF = "plan-review-task";
2397
+ var SCHEDULED_JOB_EXECUTOR_REF = "scheduledjob:";
2398
+ function normalizePersonaSlug(value) {
2399
+ if (!value) return null;
2400
+ const t = value.trim().toLowerCase();
2401
+ return t.length ? t : null;
2402
+ }
2403
+ function isHarnessExpertReviewWorker(worker) {
2404
+ const ref = (worker.executorRef ?? "").toLowerCase();
2405
+ if (ref.startsWith(EXPERT_LANE_REVIEW_REF)) return true;
2406
+ if (ref === PLAN_REVIEW_EXECUTOR_REF || ref.startsWith("daemon-review:")) return true;
2407
+ if (ref.startsWith(SCHEDULED_JOB_EXECUTOR_REF) && worker.parentTaskId) {
2408
+ const persona = normalizePersonaSlug(worker.personaSlug);
2409
+ if (persona === "lorentz" || persona === "dalton") return true;
2410
+ }
2411
+ const title = (worker.title ?? "").toLowerCase();
2412
+ if (title.includes("expert pr review")) return true;
2413
+ if (worker.parentTaskId && (title.startsWith("review:") || title.includes("review required") || title.includes("runtime review"))) {
2414
+ const persona = normalizePersonaSlug(worker.personaSlug);
2415
+ if (persona === "lorentz" || persona === "dalton") return true;
2416
+ }
2417
+ return false;
2418
+ }
2419
+
2393
2420
  // src/pr-handoff/pr-handoff-assess.ts
2394
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
+ }
2395
2427
  function trimOrNull4(value) {
2396
2428
  if (typeof value !== "string") return null;
2397
2429
  const trimmed = value.trim();
@@ -2410,8 +2442,30 @@ function extractPrUrlFromText(value) {
2410
2442
  );
2411
2443
  return m ? trimOrNull4(m[0]) : null;
2412
2444
  }
2413
- 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) {
2414
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
+ }
2415
2469
  if (trimOrNull4(snapshot.headCommit)) return true;
2416
2470
  if (committedHead(snapshot.gitAncestry)) return true;
2417
2471
  return false;
@@ -2420,18 +2474,38 @@ function assessPrHandoffRequirement(input) {
2420
2474
  if (!input.dispatched) {
2421
2475
  return { required: false, reason: "not_dispatched" };
2422
2476
  }
2477
+ if (isHarnessExpertReviewWorker({
2478
+ title: input.taskTitle ?? void 0,
2479
+ personaSlug: input.personaSlug,
2480
+ parentTaskId: input.parentTaskId,
2481
+ executorRef: input.executorRef
2482
+ })) {
2483
+ return { required: false, reason: "expert_review_task" };
2484
+ }
2423
2485
  const rule = trimOrNull4(input.routingRule) ?? "";
2424
2486
  if (rule && REVIEW_LANE_RULE.test(rule)) {
2425
2487
  return { required: false, reason: "review_lane" };
2426
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
+ }
2427
2496
  if (trimOrNull4(input.patchPath) || trimOrNull4(input.artifactBundlePath)) {
2428
2497
  return { required: false, reason: "patch_or_bundle" };
2429
2498
  }
2430
- const prUrl = trimOrNull4(input.prUrl) ?? trimOrNull4(input.snapshot.prUrl);
2499
+ const prUrl = trimOrNull4(input.prUrl) ?? trimOrNull4(input.taskPrUrl) ?? trimOrNull4(input.snapshot.prUrl);
2431
2500
  if (prUrl) {
2432
2501
  return { required: false, reason: "already_has_pr" };
2433
2502
  }
2434
- 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)) {
2435
2509
  return { required: false, reason: "no_work_product" };
2436
2510
  }
2437
2511
  return { required: true, snapshot: input.snapshot };
@@ -2640,10 +2714,19 @@ function ensurePrReadyHandoff(input, exec = defaultPrHandoffExec) {
2640
2714
  prUrl: prUrlHint,
2641
2715
  headCommit: null
2642
2716
  });
2717
+ const baseRef = input.run.baseCommit?.trim() || input.run.base?.trim() || "origin/main";
2643
2718
  const requirement = assessPrHandoffRequirement({
2644
2719
  dispatched: input.worker.dispatched,
2645
2720
  routingRule: input.worker.routingRule,
2721
+ personaSlug: input.worker.personaSlug,
2646
2722
  prUrl: prUrlHint,
2723
+ taskTitle: input.worker.taskTitle,
2724
+ executorRef: input.worker.executorRef,
2725
+ parentTaskId: input.worker.parentTaskId,
2726
+ taskPrUrl: input.worker.taskPrUrl,
2727
+ baseRef,
2728
+ exec,
2729
+ worker: input.worker,
2647
2730
  snapshot
2648
2731
  });
2649
2732
  if (!requirement.required) {
@@ -2728,6 +2811,14 @@ function ensurePrReadyHandoff(input, exec = defaultPrHandoffExec) {
2728
2811
  exec
2729
2812
  });
2730
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
+ }
2731
2822
  const dirty = snapshot.changedFiles.length;
2732
2823
  const detail = dirty ? `${dirty} uncommitted change(s) and no PR URL after handoff attempt` : "no PR URL after handoff attempt";
2733
2824
  return {
@@ -3523,6 +3614,10 @@ function spawnWorkerProcess(run, opts) {
3523
3614
  ...!opts.agentOsId || !opts.taskId ? { localOnly: true } : {},
3524
3615
  routingRule: routing.rule,
3525
3616
  ...routing.requestedModel ? { requestedModel: routing.requestedModel } : {},
3617
+ ...opts.executorRef ? { executorRef: String(opts.executorRef) } : {},
3618
+ ...opts.parentTaskId ? { parentTaskId: String(opts.parentTaskId) } : {},
3619
+ ...opts.taskTitle ? { taskTitle: String(opts.taskTitle) } : {},
3620
+ ...opts.taskPrUrl ? { taskPrUrl: String(opts.taskPrUrl) } : {},
3526
3621
  startedAt: (/* @__PURE__ */ new Date()).toISOString()
3527
3622
  };
3528
3623
  saveWorker(run.id, worker);
@@ -4217,7 +4312,7 @@ function readHarnessWorkerContext(decision) {
4217
4312
  personaInjectionReady
4218
4313
  };
4219
4314
  }
4220
- function normalizePersonaSlug(value) {
4315
+ function normalizePersonaSlug2(value) {
4221
4316
  if (typeof value !== "string") return null;
4222
4317
  const trimmed = value.trim().toLowerCase();
4223
4318
  return trimmed.length ? trimmed : null;
@@ -4348,7 +4443,7 @@ async function dispatchRun(args) {
4348
4443
  const task = decision.task;
4349
4444
  const harnessContext = readHarnessWorkerContext(decision);
4350
4445
  const taskId = String(task.id);
4351
- const expectedPersona = normalizePersonaSlug(task.personaSlug);
4446
+ const expectedPersona = normalizePersonaSlug2(task.personaSlug);
4352
4447
  if (expectedPersona && (!harnessContext?.personaInjectionReady || !harnessContext.personaMarkdown)) {
4353
4448
  outcomes.push({
4354
4449
  taskId,
@@ -4392,6 +4487,10 @@ async function dispatchRun(args) {
4392
4487
  agentOsId,
4393
4488
  taskId: String(task.id),
4394
4489
  planId,
4490
+ executorRef: task.executorRef ? String(task.executorRef) : void 0,
4491
+ parentTaskId: task.parentTaskId ? String(task.parentTaskId) : void 0,
4492
+ taskTitle: task.title ? String(task.title) : void 0,
4493
+ taskPrUrl: task.prUrl ? String(task.prUrl) : void 0,
4395
4494
  instructionPolicyMarkdown: harnessContext?.instructionPolicyMarkdown ?? null,
4396
4495
  instructionPolicyFingerprint: harnessContext?.instructionPolicyFingerprint ?? null,
4397
4496
  instructionPolicyEvidence: harnessContext?.instructionPolicyEvidence ?? null,
@@ -6590,7 +6689,12 @@ var defaultRuntimeTakeoverProbes = {
6590
6689
  openclawCronSecret: Boolean(process.env.OPENCLAW_CRON_SECRET?.trim()),
6591
6690
  kynverHarnessRoot: process.env.KYNVER_HARNESS_ROOT?.trim() || void 0,
6592
6691
  opusHarnessRoot: process.env.OPUS_HARNESS_ROOT?.trim() || void 0,
6593
- 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
+ })()
6594
6698
  }),
6595
6699
  harnessRoot: () => resolveHarnessRoot(),
6596
6700
  legacyOpenclawHarnessRoot: () => path36.join(homedir6(), ".openclaw", "harness"),
@@ -6600,10 +6704,76 @@ var defaultRuntimeTakeoverProbes = {
6600
6704
  vercelWhoami: () => captureCommand("vercel", ["whoami"])
6601
6705
  };
6602
6706
 
6603
- // src/doctor/runtime-takeover.ts
6707
+ // src/doctor/runtime-takeover-scheduler.ts
6604
6708
  function check(partial) {
6605
6709
  return partial;
6606
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
+ }
6607
6777
  function summarizeCounts(sections) {
6608
6778
  const counts = { pass: 0, warn: 0, fail: 0 };
6609
6779
  for (const section of sections) {
@@ -6620,14 +6790,14 @@ function assessCliPackage(probes) {
6620
6790
  const firstPath = onPath ? which.stdout.split(/\r?\n/)[0]?.trim() : void 0;
6621
6791
  const displayCliPath = firstPath ? displayUserPath(firstPath) : void 0;
6622
6792
  const checks = [
6623
- check({
6793
+ check2({
6624
6794
  id: "cli_running_version",
6625
6795
  label: "Running @kynver-app/runtime version",
6626
6796
  status: "pass",
6627
6797
  summary: `@kynver-app/runtime ${runningVersion}`,
6628
6798
  details: { version: runningVersion }
6629
6799
  }),
6630
- check({
6800
+ check2({
6631
6801
  id: "cli_on_path",
6632
6802
  label: "kynver executable on PATH",
6633
6803
  status: onPath ? "pass" : "warn",
@@ -6641,7 +6811,7 @@ function assessCliPackage(probes) {
6641
6811
  const installedVersion = versionProbe.stdout.replace(/^kynver\s+/i, "").trim() || void 0;
6642
6812
  const versionMatch = versionProbe.ok && (!installedVersion || installedVersion === runningVersion);
6643
6813
  checks.push(
6644
- check({
6814
+ check2({
6645
6815
  id: "cli_installed_version",
6646
6816
  label: "Installed kynver CLI version matches running package",
6647
6817
  status: versionMatch ? "pass" : "warn",
@@ -6659,7 +6829,7 @@ function assessUserConfig(probes) {
6659
6829
  const exists = probes.pathExists(configPath);
6660
6830
  const config = probes.loadConfig();
6661
6831
  const checks = [
6662
- check({
6832
+ check2({
6663
6833
  id: "config_file",
6664
6834
  label: "~/.kynver/config.json present",
6665
6835
  status: exists ? "pass" : "fail",
@@ -6674,7 +6844,7 @@ function assessUserConfig(probes) {
6674
6844
  const defaultRepo = config.defaultRepo?.trim();
6675
6845
  const displayDefaultRepo = defaultRepo ? displayUserPath(defaultRepo) : null;
6676
6846
  checks.push(
6677
- check({
6847
+ check2({
6678
6848
  id: "config_api_base_url",
6679
6849
  label: "Default API base URL",
6680
6850
  status: apiBaseUrl ? "pass" : "warn",
@@ -6682,7 +6852,7 @@ function assessUserConfig(probes) {
6682
6852
  remediation: apiBaseUrl ? void 0 : "Set `apiBaseUrl` via `kynver setup --api-base-url https://\u2026`.",
6683
6853
  details: { apiBaseUrl: apiBaseUrl ?? null }
6684
6854
  }),
6685
- check({
6855
+ check2({
6686
6856
  id: "config_agent_os_id",
6687
6857
  label: "Default AgentOS id",
6688
6858
  status: agentOsId ? "pass" : "warn",
@@ -6690,7 +6860,7 @@ function assessUserConfig(probes) {
6690
6860
  remediation: agentOsId ? void 0 : "Set `agentOsId` via `kynver setup --agent-os-id <uuid>`.",
6691
6861
  details: { agentOsId: agentOsId ?? null, agentOsSlug: config.agentOsSlug ?? null }
6692
6862
  }),
6693
- check({
6863
+ check2({
6694
6864
  id: "config_default_repo",
6695
6865
  label: "Default repo path",
6696
6866
  status: defaultRepo ? "pass" : "warn",
@@ -6718,7 +6888,7 @@ function assessRunnerToken(probes) {
6718
6888
  const scopedSaved = Boolean(savedToken) && (!targetAgentOsId || !savedAgentOsId || savedAgentOsId === targetAgentOsId);
6719
6889
  const hasScoped = Boolean(envToken?.startsWith("krc1.")) || scopedSaved && savedToken?.startsWith("krc1.");
6720
6890
  const checks = [
6721
- check({
6891
+ check2({
6722
6892
  id: "runner_token_scoped",
6723
6893
  label: "Scoped runner token (krc1.*) ready",
6724
6894
  status: hasScoped ? "pass" : "fail",
@@ -6731,7 +6901,7 @@ function assessRunnerToken(probes) {
6731
6901
  credentialsPath: displayCredPath
6732
6902
  }
6733
6903
  }),
6734
- check({
6904
+ check2({
6735
6905
  id: "runner_token_agent_os_match",
6736
6906
  label: "Saved runner token matches configured agentOsId",
6737
6907
  status: !savedToken || !targetAgentOsId || !savedAgentOsId || savedAgentOsId === targetAgentOsId ? "pass" : "warn",
@@ -6739,7 +6909,7 @@ function assessRunnerToken(probes) {
6739
6909
  remediation: savedToken && targetAgentOsId && savedAgentOsId && savedAgentOsId !== targetAgentOsId ? "`kynver runner credential --agent-os-id <configured-id>` to mint a workspace-bound token." : void 0,
6740
6910
  details: { configuredAgentOsId: targetAgentOsId ?? null, savedAgentOsId: savedAgentOsId ?? null }
6741
6911
  }),
6742
- check({
6912
+ check2({
6743
6913
  id: "runner_api_key_for_refresh",
6744
6914
  label: "API key available for token refresh",
6745
6915
  status: creds.hasApiKey || Boolean(process.env.KYNVER_API_KEY?.trim()) ? "pass" : "warn",
@@ -6758,7 +6928,7 @@ function assessVercelCli(probes) {
6758
6928
  id: "vercel_cli",
6759
6929
  label: "Vercel CLI",
6760
6930
  checks: [
6761
- check({
6931
+ check2({
6762
6932
  id: "vercel_installed",
6763
6933
  label: "Vercel CLI installed",
6764
6934
  status: installed ? "pass" : "warn",
@@ -6766,7 +6936,7 @@ function assessVercelCli(probes) {
6766
6936
  remediation: installed ? void 0 : "Install Vercel CLI (`npm i -g vercel`) for deploy evidence and env pulls.",
6767
6937
  details: { stderr: version.stderr || null }
6768
6938
  }),
6769
- check({
6939
+ check2({
6770
6940
  id: "vercel_authenticated",
6771
6941
  label: "Vercel CLI authenticated",
6772
6942
  status: !installed ? "warn" : whoami.ok ? "pass" : "warn",
@@ -6790,14 +6960,14 @@ function assessHarnessDirs(probes) {
6790
6960
  id: "harness_dirs",
6791
6961
  label: "Harness / daemon directories",
6792
6962
  checks: [
6793
- check({
6963
+ check2({
6794
6964
  id: "harness_root",
6795
6965
  label: "Harness root resolved",
6796
6966
  status: "pass",
6797
6967
  summary: displayHarnessRoot,
6798
6968
  details: { harnessRoot: displayHarnessRoot }
6799
6969
  }),
6800
- check({
6970
+ check2({
6801
6971
  id: "harness_runs_dir",
6802
6972
  label: "Runs directory ready",
6803
6973
  status: runsExists && probes.pathWritable(runsDir) ? "pass" : runsExists ? "warn" : "warn",
@@ -6805,7 +6975,7 @@ function assessHarnessDirs(probes) {
6805
6975
  remediation: runsExists && !probes.pathWritable(runsDir) ? `Fix permissions on ${displayRunsDir} or set KYNVER_HARNESS_ROOT to a writable path.` : void 0,
6806
6976
  details: { runsDir: displayRunsDir, exists: runsExists, writable: probes.pathWritable(runsDir) }
6807
6977
  }),
6808
- check({
6978
+ check2({
6809
6979
  id: "harness_worktrees_dir",
6810
6980
  label: "Worktrees directory ready",
6811
6981
  status: worktreesExists && probes.pathWritable(worktreesDir) ? "pass" : "warn",
@@ -6826,7 +6996,7 @@ function assessCallbackAuth(probes) {
6826
6996
  const creds = probes.readCredentials();
6827
6997
  const savedScoped = creds.runnerTokenPrefix?.startsWith("krc1.");
6828
6998
  const checks = [
6829
- check({
6999
+ check2({
6830
7000
  id: "callback_base_url",
6831
7001
  label: "Callback base URL configured",
6832
7002
  status: baseUrl ? usingLegacyBase ? "warn" : "pass" : "fail",
@@ -6837,7 +7007,7 @@ function assessCallbackAuth(probes) {
6837
7007
  baseUrl: baseUrl ?? null
6838
7008
  }
6839
7009
  }),
6840
- check({
7010
+ check2({
6841
7011
  id: "callback_auth_mode",
6842
7012
  label: "Callback auth uses scoped runner token",
6843
7013
  status: envScoped || savedScoped ? "pass" : legacySecret ? "warn" : "fail",
@@ -6853,15 +7023,22 @@ function assessCallbackAuth(probes) {
6853
7023
  }
6854
7024
  function assessOpenclawHotspots(probes) {
6855
7025
  const env = probes.envSnapshot();
7026
+ const config = probes.loadConfig();
7027
+ const creds = probes.readCredentials();
6856
7028
  const harnessRoot = probes.harnessRoot();
6857
7029
  const legacyRoot = probes.legacyOpenclawHarnessRoot();
6858
7030
  const displayHarnessRoot = redactHomePath(harnessRoot);
6859
7031
  const displayLegacyRoot = redactHomePath(legacyRoot);
6860
7032
  const displayOpusHarnessRoot = env.opusHarnessRoot ? redactHomePath(env.opusHarnessRoot) : null;
6861
7033
  const legacyHarnessActive = harnessRoot === legacyRoot && probes.pathExists(legacyRoot);
6862
- 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."));
6863
7040
  const checks = [
6864
- check({
7041
+ check2({
6865
7042
  id: "hotspot_legacy_harness_root",
6866
7043
  label: "Legacy ~/.openclaw/harness still active",
6867
7044
  status: legacyHarnessActive ? "warn" : "pass",
@@ -6869,7 +7046,7 @@ function assessOpenclawHotspots(probes) {
6869
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,
6870
7047
  details: { harnessRoot: displayHarnessRoot, legacyRoot: displayLegacyRoot, opusHarnessRoot: displayOpusHarnessRoot }
6871
7048
  }),
6872
- check({
7049
+ check2({
6873
7050
  id: "hotspot_openclaw_env_secrets",
6874
7051
  label: "OpenClaw deployment secrets in runner env",
6875
7052
  status: env.openclawCronSecret || env.openclawCronFireBaseUrl ? "warn" : "pass",
@@ -6879,15 +7056,12 @@ function assessOpenclawHotspots(probes) {
6879
7056
  ].filter(Boolean).join("; ") : "No OpenClaw cron env overrides on this runner",
6880
7057
  remediation: env.openclawCronSecret || env.openclawCronFireBaseUrl ? "Move to KYNVER_API_URL + scoped runner tokens; unset OpenClaw cron env on user-hosted runners." : void 0
6881
7058
  }),
6882
- check({
6883
- id: "hotspot_openclaw_scheduler",
6884
- label: "openclaw-cron scheduler dependency (deployment)",
6885
- status: schedulerOpenclaw ? "warn" : "pass",
6886
- 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}`,
6887
- 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,
6888
- details: { schedulerProvider: env.kynverSchedulerProvider ?? null }
7059
+ assessRuntimeTakeoverScheduler(env, {
7060
+ agentOsId: targetAgentOsId ?? null,
7061
+ apiBaseUrl: config.apiBaseUrl?.trim() ?? env.kynverApiUrl ?? null,
7062
+ hasScopedRunnerToken
6889
7063
  }),
6890
- check({
7064
+ check2({
6891
7065
  id: "hotspot_lease_source_names",
6892
7066
  label: "Harness lease/completion source names",
6893
7067
  status: "pass",