@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.
- package/dist/cli.js +210 -36
- package/dist/cli.js.map +4 -4
- package/dist/doctor/runtime-takeover-scheduler.d.ts +20 -0
- package/dist/doctor/runtime-takeover.probes.d.ts +2 -0
- package/dist/harness-expert-review.d.ts +8 -0
- package/dist/index.js +210 -36
- package/dist/index.js.map +4 -4
- package/dist/pr-handoff/pr-handoff-assess.d.ts +16 -0
- package/dist/pr-handoff/pr-handoff.types.d.ts +1 -1
- package/dist/status.d.ts +5 -0
- package/dist/supervisor.d.ts +4 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1906,7 +1906,7 @@ function inferModelRoutingFromTask(task) {
|
|
|
1906
1906
|
rule: "lane:landing"
|
|
1907
1907
|
};
|
|
1908
1908
|
}
|
|
1909
|
-
if (ref.includes("review") || title
|
|
1909
|
+
if (ref.includes("review") || /^review[\s:]/.test(title) || roleLane.includes("review")) {
|
|
1910
1910
|
if (isOpusLane(ref, title) || roleLane === "deep_reviewer") {
|
|
1911
1911
|
return { model: "claude-opus-4-7", provider: "claude", rule: "lane:deep_review" };
|
|
1912
1912
|
}
|
|
@@ -2099,6 +2099,7 @@ function buildPrompt(input) {
|
|
|
2099
2099
|
"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.",
|
|
2100
2100
|
"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.",
|
|
2101
2101
|
"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.",
|
|
2102
|
+
"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.",
|
|
2102
2103
|
"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.",
|
|
2103
2104
|
"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.",
|
|
2104
2105
|
"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.",
|
|
@@ -2340,8 +2341,39 @@ function completionPostSucceeded(summary) {
|
|
|
2340
2341
|
return summary.taskAdvanced;
|
|
2341
2342
|
}
|
|
2342
2343
|
|
|
2344
|
+
// src/harness-expert-review.ts
|
|
2345
|
+
var EXPERT_LANE_REVIEW_REF = "expert-lane-pr-review:";
|
|
2346
|
+
var PLAN_REVIEW_EXECUTOR_REF = "plan-review-task";
|
|
2347
|
+
var SCHEDULED_JOB_EXECUTOR_REF = "scheduledjob:";
|
|
2348
|
+
function normalizePersonaSlug(value) {
|
|
2349
|
+
if (!value) return null;
|
|
2350
|
+
const t = value.trim().toLowerCase();
|
|
2351
|
+
return t.length ? t : null;
|
|
2352
|
+
}
|
|
2353
|
+
function isHarnessExpertReviewWorker(worker) {
|
|
2354
|
+
const ref = (worker.executorRef ?? "").toLowerCase();
|
|
2355
|
+
if (ref.startsWith(EXPERT_LANE_REVIEW_REF)) return true;
|
|
2356
|
+
if (ref === PLAN_REVIEW_EXECUTOR_REF || ref.startsWith("daemon-review:")) return true;
|
|
2357
|
+
if (ref.startsWith(SCHEDULED_JOB_EXECUTOR_REF) && worker.parentTaskId) {
|
|
2358
|
+
const persona = normalizePersonaSlug(worker.personaSlug);
|
|
2359
|
+
if (persona === "lorentz" || persona === "dalton") return true;
|
|
2360
|
+
}
|
|
2361
|
+
const title = (worker.title ?? "").toLowerCase();
|
|
2362
|
+
if (title.includes("expert pr review")) return true;
|
|
2363
|
+
if (worker.parentTaskId && (title.startsWith("review:") || title.includes("review required") || title.includes("runtime review"))) {
|
|
2364
|
+
const persona = normalizePersonaSlug(worker.personaSlug);
|
|
2365
|
+
if (persona === "lorentz" || persona === "dalton") return true;
|
|
2366
|
+
}
|
|
2367
|
+
return false;
|
|
2368
|
+
}
|
|
2369
|
+
|
|
2343
2370
|
// src/pr-handoff/pr-handoff-assess.ts
|
|
2344
2371
|
var REVIEW_LANE_RULE = /^(lane:)?(review|deep_review|planning|landing)(:|$)/i;
|
|
2372
|
+
var REVIEW_PERSONA_SLUGS = /* @__PURE__ */ new Set(["lorentz", "sentinel"]);
|
|
2373
|
+
var NO_PR_COMMITS_BETWEEN_RE = /no commits between/i;
|
|
2374
|
+
function isGhNoCommitsBetweenError(detail) {
|
|
2375
|
+
return Boolean(detail && NO_PR_COMMITS_BETWEEN_RE.test(detail));
|
|
2376
|
+
}
|
|
2345
2377
|
function trimOrNull4(value) {
|
|
2346
2378
|
if (typeof value !== "string") return null;
|
|
2347
2379
|
const trimmed = value.trim();
|
|
@@ -2360,8 +2392,30 @@ function extractPrUrlFromText(value) {
|
|
|
2360
2392
|
);
|
|
2361
2393
|
return m ? trimOrNull4(m[0]) : null;
|
|
2362
2394
|
}
|
|
2363
|
-
function
|
|
2395
|
+
function countCommitsAheadOfBase(worktreePath, baseRef, exec) {
|
|
2396
|
+
const base = baseRef.trim();
|
|
2397
|
+
if (!base) return null;
|
|
2398
|
+
const count = exec.git(worktreePath, ["rev-list", "--count", `${base}..HEAD`]);
|
|
2399
|
+
if (count.status !== 0) return null;
|
|
2400
|
+
const n = Number.parseInt(count.stdout.trim(), 10);
|
|
2401
|
+
return Number.isFinite(n) ? n : null;
|
|
2402
|
+
}
|
|
2403
|
+
function isReviewArtifactWorker(worker, snapshot) {
|
|
2404
|
+
if (snapshot.changedFiles.length > 0) return false;
|
|
2405
|
+
const persona = trimOrNull4(worker.personaSlug)?.toLowerCase();
|
|
2406
|
+
if (persona && REVIEW_PERSONA_SLUGS.has(persona)) return true;
|
|
2407
|
+
const rule = trimOrNull4(worker.routingRule) ?? "";
|
|
2408
|
+
if (rule && REVIEW_LANE_RULE.test(rule)) return true;
|
|
2409
|
+
return false;
|
|
2410
|
+
}
|
|
2411
|
+
function hasWorkProduct(snapshot, options) {
|
|
2364
2412
|
if (snapshot.changedFiles.length > 0) return true;
|
|
2413
|
+
const baseRef = trimOrNull4(options?.baseRef);
|
|
2414
|
+
if (baseRef && options?.exec && options.worktreePath) {
|
|
2415
|
+
const ahead = countCommitsAheadOfBase(options.worktreePath, baseRef, options.exec);
|
|
2416
|
+
if (ahead === 0) return false;
|
|
2417
|
+
if (ahead !== null && ahead > 0) return true;
|
|
2418
|
+
}
|
|
2365
2419
|
if (trimOrNull4(snapshot.headCommit)) return true;
|
|
2366
2420
|
if (committedHead(snapshot.gitAncestry)) return true;
|
|
2367
2421
|
return false;
|
|
@@ -2370,18 +2424,38 @@ function assessPrHandoffRequirement(input) {
|
|
|
2370
2424
|
if (!input.dispatched) {
|
|
2371
2425
|
return { required: false, reason: "not_dispatched" };
|
|
2372
2426
|
}
|
|
2427
|
+
if (isHarnessExpertReviewWorker({
|
|
2428
|
+
title: input.taskTitle ?? void 0,
|
|
2429
|
+
personaSlug: input.personaSlug,
|
|
2430
|
+
parentTaskId: input.parentTaskId,
|
|
2431
|
+
executorRef: input.executorRef
|
|
2432
|
+
})) {
|
|
2433
|
+
return { required: false, reason: "expert_review_task" };
|
|
2434
|
+
}
|
|
2373
2435
|
const rule = trimOrNull4(input.routingRule) ?? "";
|
|
2374
2436
|
if (rule && REVIEW_LANE_RULE.test(rule)) {
|
|
2375
2437
|
return { required: false, reason: "review_lane" };
|
|
2376
2438
|
}
|
|
2439
|
+
const workerCtx = input.worker ?? {
|
|
2440
|
+
personaSlug: input.personaSlug,
|
|
2441
|
+
routingRule: input.routingRule
|
|
2442
|
+
};
|
|
2443
|
+
if (isReviewArtifactWorker(workerCtx, input.snapshot)) {
|
|
2444
|
+
return { required: false, reason: "review_artifact" };
|
|
2445
|
+
}
|
|
2377
2446
|
if (trimOrNull4(input.patchPath) || trimOrNull4(input.artifactBundlePath)) {
|
|
2378
2447
|
return { required: false, reason: "patch_or_bundle" };
|
|
2379
2448
|
}
|
|
2380
|
-
const prUrl = trimOrNull4(input.prUrl) ?? trimOrNull4(input.snapshot.prUrl);
|
|
2449
|
+
const prUrl = trimOrNull4(input.prUrl) ?? trimOrNull4(input.taskPrUrl) ?? trimOrNull4(input.snapshot.prUrl);
|
|
2381
2450
|
if (prUrl) {
|
|
2382
2451
|
return { required: false, reason: "already_has_pr" };
|
|
2383
2452
|
}
|
|
2384
|
-
|
|
2453
|
+
const workProductOpts = input.exec && input.baseRef ? {
|
|
2454
|
+
baseRef: input.baseRef,
|
|
2455
|
+
exec: input.exec,
|
|
2456
|
+
worktreePath: input.snapshot.worktreePath
|
|
2457
|
+
} : void 0;
|
|
2458
|
+
if (!hasWorkProduct(input.snapshot, workProductOpts)) {
|
|
2385
2459
|
return { required: false, reason: "no_work_product" };
|
|
2386
2460
|
}
|
|
2387
2461
|
return { required: true, snapshot: input.snapshot };
|
|
@@ -2590,10 +2664,19 @@ function ensurePrReadyHandoff(input, exec = defaultPrHandoffExec) {
|
|
|
2590
2664
|
prUrl: prUrlHint,
|
|
2591
2665
|
headCommit: null
|
|
2592
2666
|
});
|
|
2667
|
+
const baseRef = input.run.baseCommit?.trim() || input.run.base?.trim() || "origin/main";
|
|
2593
2668
|
const requirement = assessPrHandoffRequirement({
|
|
2594
2669
|
dispatched: input.worker.dispatched,
|
|
2595
2670
|
routingRule: input.worker.routingRule,
|
|
2671
|
+
personaSlug: input.worker.personaSlug,
|
|
2596
2672
|
prUrl: prUrlHint,
|
|
2673
|
+
taskTitle: input.worker.taskTitle,
|
|
2674
|
+
executorRef: input.worker.executorRef,
|
|
2675
|
+
parentTaskId: input.worker.parentTaskId,
|
|
2676
|
+
taskPrUrl: input.worker.taskPrUrl,
|
|
2677
|
+
baseRef,
|
|
2678
|
+
exec,
|
|
2679
|
+
worker: input.worker,
|
|
2597
2680
|
snapshot
|
|
2598
2681
|
});
|
|
2599
2682
|
if (!requirement.required) {
|
|
@@ -2678,6 +2761,14 @@ function ensurePrReadyHandoff(input, exec = defaultPrHandoffExec) {
|
|
|
2678
2761
|
exec
|
|
2679
2762
|
});
|
|
2680
2763
|
if (!pr.ok || !pr.prUrl) {
|
|
2764
|
+
if (isGhNoCommitsBetweenError(pr.detail)) {
|
|
2765
|
+
return {
|
|
2766
|
+
ok: true,
|
|
2767
|
+
headCommit: headCommit ?? void 0,
|
|
2768
|
+
committed,
|
|
2769
|
+
pushed
|
|
2770
|
+
};
|
|
2771
|
+
}
|
|
2681
2772
|
const dirty = snapshot.changedFiles.length;
|
|
2682
2773
|
const detail = dirty ? `${dirty} uncommitted change(s) and no PR URL after handoff attempt` : "no PR URL after handoff attempt";
|
|
2683
2774
|
return {
|
|
@@ -3473,6 +3564,10 @@ function spawnWorkerProcess(run, opts) {
|
|
|
3473
3564
|
...!opts.agentOsId || !opts.taskId ? { localOnly: true } : {},
|
|
3474
3565
|
routingRule: routing.rule,
|
|
3475
3566
|
...routing.requestedModel ? { requestedModel: routing.requestedModel } : {},
|
|
3567
|
+
...opts.executorRef ? { executorRef: String(opts.executorRef) } : {},
|
|
3568
|
+
...opts.parentTaskId ? { parentTaskId: String(opts.parentTaskId) } : {},
|
|
3569
|
+
...opts.taskTitle ? { taskTitle: String(opts.taskTitle) } : {},
|
|
3570
|
+
...opts.taskPrUrl ? { taskPrUrl: String(opts.taskPrUrl) } : {},
|
|
3476
3571
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3477
3572
|
};
|
|
3478
3573
|
saveWorker(run.id, worker);
|
|
@@ -4167,7 +4262,7 @@ function readHarnessWorkerContext(decision) {
|
|
|
4167
4262
|
personaInjectionReady
|
|
4168
4263
|
};
|
|
4169
4264
|
}
|
|
4170
|
-
function
|
|
4265
|
+
function normalizePersonaSlug2(value) {
|
|
4171
4266
|
if (typeof value !== "string") return null;
|
|
4172
4267
|
const trimmed = value.trim().toLowerCase();
|
|
4173
4268
|
return trimmed.length ? trimmed : null;
|
|
@@ -4298,7 +4393,7 @@ async function dispatchRun(args) {
|
|
|
4298
4393
|
const task = decision.task;
|
|
4299
4394
|
const harnessContext = readHarnessWorkerContext(decision);
|
|
4300
4395
|
const taskId = String(task.id);
|
|
4301
|
-
const expectedPersona =
|
|
4396
|
+
const expectedPersona = normalizePersonaSlug2(task.personaSlug);
|
|
4302
4397
|
if (expectedPersona && (!harnessContext?.personaInjectionReady || !harnessContext.personaMarkdown)) {
|
|
4303
4398
|
outcomes.push({
|
|
4304
4399
|
taskId,
|
|
@@ -4342,6 +4437,10 @@ async function dispatchRun(args) {
|
|
|
4342
4437
|
agentOsId,
|
|
4343
4438
|
taskId: String(task.id),
|
|
4344
4439
|
planId,
|
|
4440
|
+
executorRef: task.executorRef ? String(task.executorRef) : void 0,
|
|
4441
|
+
parentTaskId: task.parentTaskId ? String(task.parentTaskId) : void 0,
|
|
4442
|
+
taskTitle: task.title ? String(task.title) : void 0,
|
|
4443
|
+
taskPrUrl: task.prUrl ? String(task.prUrl) : void 0,
|
|
4345
4444
|
instructionPolicyMarkdown: harnessContext?.instructionPolicyMarkdown ?? null,
|
|
4346
4445
|
instructionPolicyFingerprint: harnessContext?.instructionPolicyFingerprint ?? null,
|
|
4347
4446
|
instructionPolicyEvidence: harnessContext?.instructionPolicyEvidence ?? null,
|
|
@@ -6538,7 +6637,12 @@ var defaultRuntimeTakeoverProbes = {
|
|
|
6538
6637
|
openclawCronSecret: Boolean(process.env.OPENCLAW_CRON_SECRET?.trim()),
|
|
6539
6638
|
kynverHarnessRoot: process.env.KYNVER_HARNESS_ROOT?.trim() || void 0,
|
|
6540
6639
|
opusHarnessRoot: process.env.OPUS_HARNESS_ROOT?.trim() || void 0,
|
|
6541
|
-
kynverSchedulerProvider: process.env.KYNVER_SCHEDULER_PROVIDER?.trim() || void 0
|
|
6640
|
+
kynverSchedulerProvider: process.env.KYNVER_SCHEDULER_PROVIDER?.trim() || void 0,
|
|
6641
|
+
qstashTokenPresent: Boolean(process.env.QSTASH_TOKEN?.trim()),
|
|
6642
|
+
kynverHostedDeployment: (() => {
|
|
6643
|
+
const v = process.env.KYNVER_HOSTED_DEPLOYMENT?.trim().toLowerCase();
|
|
6644
|
+
return v === "1" || v === "true" || v === "yes";
|
|
6645
|
+
})()
|
|
6542
6646
|
}),
|
|
6543
6647
|
harnessRoot: () => resolveHarnessRoot(),
|
|
6544
6648
|
legacyOpenclawHarnessRoot: () => path36.join(homedir6(), ".openclaw", "harness"),
|
|
@@ -6548,10 +6652,76 @@ var defaultRuntimeTakeoverProbes = {
|
|
|
6548
6652
|
vercelWhoami: () => captureCommand("vercel", ["whoami"])
|
|
6549
6653
|
};
|
|
6550
6654
|
|
|
6551
|
-
// src/doctor/runtime-takeover.ts
|
|
6655
|
+
// src/doctor/runtime-takeover-scheduler.ts
|
|
6552
6656
|
function check(partial) {
|
|
6553
6657
|
return partial;
|
|
6554
6658
|
}
|
|
6659
|
+
function assessRuntimeTakeoverScheduler(env, ctx) {
|
|
6660
|
+
const runnerOpenclaw = env.kynverSchedulerProvider === "openclaw-cron";
|
|
6661
|
+
const runnerQstash = env.kynverSchedulerProvider === "qstash";
|
|
6662
|
+
const hostedDeployment = Boolean(env.qstashTokenPresent) || Boolean(env.kynverHostedDeployment);
|
|
6663
|
+
const daemonDispatchReady = Boolean(ctx.agentOsId?.trim()) && Boolean(ctx.apiBaseUrl?.trim()) && ctx.hasScopedRunnerToken;
|
|
6664
|
+
const deploymentNeedsQstash = hostedDeployment && !env.qstashTokenPresent && env.kynverSchedulerProvider !== "qstash";
|
|
6665
|
+
const deploymentOpenclaw = hostedDeployment && env.kynverSchedulerProvider === "openclaw-cron";
|
|
6666
|
+
if (runnerOpenclaw) {
|
|
6667
|
+
return check({
|
|
6668
|
+
id: "hotspot_openclaw_scheduler",
|
|
6669
|
+
label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
|
|
6670
|
+
status: "warn",
|
|
6671
|
+
summary: "KYNVER_SCHEDULER_PROVIDER=openclaw-cron on this runner \u2014 hosted dispatch still depends on the OpenClaw local-cron adapter",
|
|
6672
|
+
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.",
|
|
6673
|
+
details: { schedulerProvider: env.kynverSchedulerProvider ?? null, hostedDeployment }
|
|
6674
|
+
});
|
|
6675
|
+
}
|
|
6676
|
+
if (deploymentOpenclaw || deploymentNeedsQstash) {
|
|
6677
|
+
return check({
|
|
6678
|
+
id: "hotspot_openclaw_scheduler",
|
|
6679
|
+
label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
|
|
6680
|
+
status: "warn",
|
|
6681
|
+
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",
|
|
6682
|
+
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.",
|
|
6683
|
+
details: {
|
|
6684
|
+
schedulerProvider: env.kynverSchedulerProvider ?? null,
|
|
6685
|
+
qstashTokenPresent: env.qstashTokenPresent ?? false,
|
|
6686
|
+
hostedDeployment
|
|
6687
|
+
}
|
|
6688
|
+
});
|
|
6689
|
+
}
|
|
6690
|
+
if (daemonDispatchReady) {
|
|
6691
|
+
return check({
|
|
6692
|
+
id: "hotspot_openclaw_scheduler",
|
|
6693
|
+
label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
|
|
6694
|
+
status: "pass",
|
|
6695
|
+
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",
|
|
6696
|
+
details: {
|
|
6697
|
+
schedulerProvider: env.kynverSchedulerProvider ?? null,
|
|
6698
|
+
dispatchPath: "kynver-daemon-pipeline-tick",
|
|
6699
|
+
hostedDeployment
|
|
6700
|
+
}
|
|
6701
|
+
});
|
|
6702
|
+
}
|
|
6703
|
+
if (!env.kynverSchedulerProvider) {
|
|
6704
|
+
return check({
|
|
6705
|
+
id: "hotspot_openclaw_scheduler",
|
|
6706
|
+
label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
|
|
6707
|
+
status: "pass",
|
|
6708
|
+
summary: "No KYNVER_SCHEDULER_PROVIDER on runner (expected) \u2014 finish runner setup so daemon pipeline-tick owns dispatch",
|
|
6709
|
+
details: { schedulerProvider: null, dispatchPath: "kynver-daemon-pipeline-tick-pending" }
|
|
6710
|
+
});
|
|
6711
|
+
}
|
|
6712
|
+
return check({
|
|
6713
|
+
id: "hotspot_openclaw_scheduler",
|
|
6714
|
+
label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
|
|
6715
|
+
status: "pass",
|
|
6716
|
+
summary: `KYNVER_SCHEDULER_PROVIDER=${env.kynverSchedulerProvider}`,
|
|
6717
|
+
details: { schedulerProvider: env.kynverSchedulerProvider ?? null }
|
|
6718
|
+
});
|
|
6719
|
+
}
|
|
6720
|
+
|
|
6721
|
+
// src/doctor/runtime-takeover.ts
|
|
6722
|
+
function check2(partial) {
|
|
6723
|
+
return partial;
|
|
6724
|
+
}
|
|
6555
6725
|
function summarizeCounts(sections) {
|
|
6556
6726
|
const counts = { pass: 0, warn: 0, fail: 0 };
|
|
6557
6727
|
for (const section of sections) {
|
|
@@ -6568,14 +6738,14 @@ function assessCliPackage(probes) {
|
|
|
6568
6738
|
const firstPath = onPath ? which.stdout.split(/\r?\n/)[0]?.trim() : void 0;
|
|
6569
6739
|
const displayCliPath = firstPath ? displayUserPath(firstPath) : void 0;
|
|
6570
6740
|
const checks = [
|
|
6571
|
-
|
|
6741
|
+
check2({
|
|
6572
6742
|
id: "cli_running_version",
|
|
6573
6743
|
label: "Running @kynver-app/runtime version",
|
|
6574
6744
|
status: "pass",
|
|
6575
6745
|
summary: `@kynver-app/runtime ${runningVersion}`,
|
|
6576
6746
|
details: { version: runningVersion }
|
|
6577
6747
|
}),
|
|
6578
|
-
|
|
6748
|
+
check2({
|
|
6579
6749
|
id: "cli_on_path",
|
|
6580
6750
|
label: "kynver executable on PATH",
|
|
6581
6751
|
status: onPath ? "pass" : "warn",
|
|
@@ -6589,7 +6759,7 @@ function assessCliPackage(probes) {
|
|
|
6589
6759
|
const installedVersion = versionProbe.stdout.replace(/^kynver\s+/i, "").trim() || void 0;
|
|
6590
6760
|
const versionMatch = versionProbe.ok && (!installedVersion || installedVersion === runningVersion);
|
|
6591
6761
|
checks.push(
|
|
6592
|
-
|
|
6762
|
+
check2({
|
|
6593
6763
|
id: "cli_installed_version",
|
|
6594
6764
|
label: "Installed kynver CLI version matches running package",
|
|
6595
6765
|
status: versionMatch ? "pass" : "warn",
|
|
@@ -6607,7 +6777,7 @@ function assessUserConfig(probes) {
|
|
|
6607
6777
|
const exists = probes.pathExists(configPath);
|
|
6608
6778
|
const config = probes.loadConfig();
|
|
6609
6779
|
const checks = [
|
|
6610
|
-
|
|
6780
|
+
check2({
|
|
6611
6781
|
id: "config_file",
|
|
6612
6782
|
label: "~/.kynver/config.json present",
|
|
6613
6783
|
status: exists ? "pass" : "fail",
|
|
@@ -6622,7 +6792,7 @@ function assessUserConfig(probes) {
|
|
|
6622
6792
|
const defaultRepo = config.defaultRepo?.trim();
|
|
6623
6793
|
const displayDefaultRepo = defaultRepo ? displayUserPath(defaultRepo) : null;
|
|
6624
6794
|
checks.push(
|
|
6625
|
-
|
|
6795
|
+
check2({
|
|
6626
6796
|
id: "config_api_base_url",
|
|
6627
6797
|
label: "Default API base URL",
|
|
6628
6798
|
status: apiBaseUrl ? "pass" : "warn",
|
|
@@ -6630,7 +6800,7 @@ function assessUserConfig(probes) {
|
|
|
6630
6800
|
remediation: apiBaseUrl ? void 0 : "Set `apiBaseUrl` via `kynver setup --api-base-url https://\u2026`.",
|
|
6631
6801
|
details: { apiBaseUrl: apiBaseUrl ?? null }
|
|
6632
6802
|
}),
|
|
6633
|
-
|
|
6803
|
+
check2({
|
|
6634
6804
|
id: "config_agent_os_id",
|
|
6635
6805
|
label: "Default AgentOS id",
|
|
6636
6806
|
status: agentOsId ? "pass" : "warn",
|
|
@@ -6638,7 +6808,7 @@ function assessUserConfig(probes) {
|
|
|
6638
6808
|
remediation: agentOsId ? void 0 : "Set `agentOsId` via `kynver setup --agent-os-id <uuid>`.",
|
|
6639
6809
|
details: { agentOsId: agentOsId ?? null, agentOsSlug: config.agentOsSlug ?? null }
|
|
6640
6810
|
}),
|
|
6641
|
-
|
|
6811
|
+
check2({
|
|
6642
6812
|
id: "config_default_repo",
|
|
6643
6813
|
label: "Default repo path",
|
|
6644
6814
|
status: defaultRepo ? "pass" : "warn",
|
|
@@ -6666,7 +6836,7 @@ function assessRunnerToken(probes) {
|
|
|
6666
6836
|
const scopedSaved = Boolean(savedToken) && (!targetAgentOsId || !savedAgentOsId || savedAgentOsId === targetAgentOsId);
|
|
6667
6837
|
const hasScoped = Boolean(envToken?.startsWith("krc1.")) || scopedSaved && savedToken?.startsWith("krc1.");
|
|
6668
6838
|
const checks = [
|
|
6669
|
-
|
|
6839
|
+
check2({
|
|
6670
6840
|
id: "runner_token_scoped",
|
|
6671
6841
|
label: "Scoped runner token (krc1.*) ready",
|
|
6672
6842
|
status: hasScoped ? "pass" : "fail",
|
|
@@ -6679,7 +6849,7 @@ function assessRunnerToken(probes) {
|
|
|
6679
6849
|
credentialsPath: displayCredPath
|
|
6680
6850
|
}
|
|
6681
6851
|
}),
|
|
6682
|
-
|
|
6852
|
+
check2({
|
|
6683
6853
|
id: "runner_token_agent_os_match",
|
|
6684
6854
|
label: "Saved runner token matches configured agentOsId",
|
|
6685
6855
|
status: !savedToken || !targetAgentOsId || !savedAgentOsId || savedAgentOsId === targetAgentOsId ? "pass" : "warn",
|
|
@@ -6687,7 +6857,7 @@ function assessRunnerToken(probes) {
|
|
|
6687
6857
|
remediation: savedToken && targetAgentOsId && savedAgentOsId && savedAgentOsId !== targetAgentOsId ? "`kynver runner credential --agent-os-id <configured-id>` to mint a workspace-bound token." : void 0,
|
|
6688
6858
|
details: { configuredAgentOsId: targetAgentOsId ?? null, savedAgentOsId: savedAgentOsId ?? null }
|
|
6689
6859
|
}),
|
|
6690
|
-
|
|
6860
|
+
check2({
|
|
6691
6861
|
id: "runner_api_key_for_refresh",
|
|
6692
6862
|
label: "API key available for token refresh",
|
|
6693
6863
|
status: creds.hasApiKey || Boolean(process.env.KYNVER_API_KEY?.trim()) ? "pass" : "warn",
|
|
@@ -6706,7 +6876,7 @@ function assessVercelCli(probes) {
|
|
|
6706
6876
|
id: "vercel_cli",
|
|
6707
6877
|
label: "Vercel CLI",
|
|
6708
6878
|
checks: [
|
|
6709
|
-
|
|
6879
|
+
check2({
|
|
6710
6880
|
id: "vercel_installed",
|
|
6711
6881
|
label: "Vercel CLI installed",
|
|
6712
6882
|
status: installed ? "pass" : "warn",
|
|
@@ -6714,7 +6884,7 @@ function assessVercelCli(probes) {
|
|
|
6714
6884
|
remediation: installed ? void 0 : "Install Vercel CLI (`npm i -g vercel`) for deploy evidence and env pulls.",
|
|
6715
6885
|
details: { stderr: version.stderr || null }
|
|
6716
6886
|
}),
|
|
6717
|
-
|
|
6887
|
+
check2({
|
|
6718
6888
|
id: "vercel_authenticated",
|
|
6719
6889
|
label: "Vercel CLI authenticated",
|
|
6720
6890
|
status: !installed ? "warn" : whoami.ok ? "pass" : "warn",
|
|
@@ -6738,14 +6908,14 @@ function assessHarnessDirs(probes) {
|
|
|
6738
6908
|
id: "harness_dirs",
|
|
6739
6909
|
label: "Harness / daemon directories",
|
|
6740
6910
|
checks: [
|
|
6741
|
-
|
|
6911
|
+
check2({
|
|
6742
6912
|
id: "harness_root",
|
|
6743
6913
|
label: "Harness root resolved",
|
|
6744
6914
|
status: "pass",
|
|
6745
6915
|
summary: displayHarnessRoot,
|
|
6746
6916
|
details: { harnessRoot: displayHarnessRoot }
|
|
6747
6917
|
}),
|
|
6748
|
-
|
|
6918
|
+
check2({
|
|
6749
6919
|
id: "harness_runs_dir",
|
|
6750
6920
|
label: "Runs directory ready",
|
|
6751
6921
|
status: runsExists && probes.pathWritable(runsDir) ? "pass" : runsExists ? "warn" : "warn",
|
|
@@ -6753,7 +6923,7 @@ function assessHarnessDirs(probes) {
|
|
|
6753
6923
|
remediation: runsExists && !probes.pathWritable(runsDir) ? `Fix permissions on ${displayRunsDir} or set KYNVER_HARNESS_ROOT to a writable path.` : void 0,
|
|
6754
6924
|
details: { runsDir: displayRunsDir, exists: runsExists, writable: probes.pathWritable(runsDir) }
|
|
6755
6925
|
}),
|
|
6756
|
-
|
|
6926
|
+
check2({
|
|
6757
6927
|
id: "harness_worktrees_dir",
|
|
6758
6928
|
label: "Worktrees directory ready",
|
|
6759
6929
|
status: worktreesExists && probes.pathWritable(worktreesDir) ? "pass" : "warn",
|
|
@@ -6774,7 +6944,7 @@ function assessCallbackAuth(probes) {
|
|
|
6774
6944
|
const creds = probes.readCredentials();
|
|
6775
6945
|
const savedScoped = creds.runnerTokenPrefix?.startsWith("krc1.");
|
|
6776
6946
|
const checks = [
|
|
6777
|
-
|
|
6947
|
+
check2({
|
|
6778
6948
|
id: "callback_base_url",
|
|
6779
6949
|
label: "Callback base URL configured",
|
|
6780
6950
|
status: baseUrl ? usingLegacyBase ? "warn" : "pass" : "fail",
|
|
@@ -6785,7 +6955,7 @@ function assessCallbackAuth(probes) {
|
|
|
6785
6955
|
baseUrl: baseUrl ?? null
|
|
6786
6956
|
}
|
|
6787
6957
|
}),
|
|
6788
|
-
|
|
6958
|
+
check2({
|
|
6789
6959
|
id: "callback_auth_mode",
|
|
6790
6960
|
label: "Callback auth uses scoped runner token",
|
|
6791
6961
|
status: envScoped || savedScoped ? "pass" : legacySecret ? "warn" : "fail",
|
|
@@ -6801,15 +6971,22 @@ function assessCallbackAuth(probes) {
|
|
|
6801
6971
|
}
|
|
6802
6972
|
function assessOpenclawHotspots(probes) {
|
|
6803
6973
|
const env = probes.envSnapshot();
|
|
6974
|
+
const config = probes.loadConfig();
|
|
6975
|
+
const creds = probes.readCredentials();
|
|
6804
6976
|
const harnessRoot = probes.harnessRoot();
|
|
6805
6977
|
const legacyRoot = probes.legacyOpenclawHarnessRoot();
|
|
6806
6978
|
const displayHarnessRoot = redactHomePath(harnessRoot);
|
|
6807
6979
|
const displayLegacyRoot = redactHomePath(legacyRoot);
|
|
6808
6980
|
const displayOpusHarnessRoot = env.opusHarnessRoot ? redactHomePath(env.opusHarnessRoot) : null;
|
|
6809
6981
|
const legacyHarnessActive = harnessRoot === legacyRoot && probes.pathExists(legacyRoot);
|
|
6810
|
-
const
|
|
6982
|
+
const targetAgentOsId = config.agentOsId?.trim();
|
|
6983
|
+
const envToken = env.kynverRunnerTokenPrefix;
|
|
6984
|
+
const savedToken = creds.runnerTokenPrefix;
|
|
6985
|
+
const savedAgentOsId = creds.runnerTokenAgentOsId;
|
|
6986
|
+
const scopedSaved = Boolean(savedToken) && (!targetAgentOsId || !savedAgentOsId || savedAgentOsId === targetAgentOsId);
|
|
6987
|
+
const hasScopedRunnerToken = Boolean(envToken?.startsWith("krc1.")) || scopedSaved && Boolean(savedToken?.startsWith("krc1."));
|
|
6811
6988
|
const checks = [
|
|
6812
|
-
|
|
6989
|
+
check2({
|
|
6813
6990
|
id: "hotspot_legacy_harness_root",
|
|
6814
6991
|
label: "Legacy ~/.openclaw/harness still active",
|
|
6815
6992
|
status: legacyHarnessActive ? "warn" : "pass",
|
|
@@ -6817,7 +6994,7 @@ function assessOpenclawHotspots(probes) {
|
|
|
6817
6994
|
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,
|
|
6818
6995
|
details: { harnessRoot: displayHarnessRoot, legacyRoot: displayLegacyRoot, opusHarnessRoot: displayOpusHarnessRoot }
|
|
6819
6996
|
}),
|
|
6820
|
-
|
|
6997
|
+
check2({
|
|
6821
6998
|
id: "hotspot_openclaw_env_secrets",
|
|
6822
6999
|
label: "OpenClaw deployment secrets in runner env",
|
|
6823
7000
|
status: env.openclawCronSecret || env.openclawCronFireBaseUrl ? "warn" : "pass",
|
|
@@ -6827,15 +7004,12 @@ function assessOpenclawHotspots(probes) {
|
|
|
6827
7004
|
].filter(Boolean).join("; ") : "No OpenClaw cron env overrides on this runner",
|
|
6828
7005
|
remediation: env.openclawCronSecret || env.openclawCronFireBaseUrl ? "Move to KYNVER_API_URL + scoped runner tokens; unset OpenClaw cron env on user-hosted runners." : void 0
|
|
6829
7006
|
}),
|
|
6830
|
-
|
|
6831
|
-
|
|
6832
|
-
|
|
6833
|
-
|
|
6834
|
-
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}`,
|
|
6835
|
-
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,
|
|
6836
|
-
details: { schedulerProvider: env.kynverSchedulerProvider ?? null }
|
|
7007
|
+
assessRuntimeTakeoverScheduler(env, {
|
|
7008
|
+
agentOsId: targetAgentOsId ?? null,
|
|
7009
|
+
apiBaseUrl: config.apiBaseUrl?.trim() ?? env.kynverApiUrl ?? null,
|
|
7010
|
+
hasScopedRunnerToken
|
|
6837
7011
|
}),
|
|
6838
|
-
|
|
7012
|
+
check2({
|
|
6839
7013
|
id: "hotspot_lease_source_names",
|
|
6840
7014
|
label: "Harness lease/completion source names",
|
|
6841
7015
|
status: "pass",
|