@kynver-app/runtime 0.1.106 → 0.1.112

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
@@ -175,7 +175,11 @@ function scrubWorkerEnv(env) {
175
175
 
176
176
  // src/git.ts
177
177
  function git(cwd, args, options = {}) {
178
- const res = spawnSync("git", args, { cwd, encoding: "utf8" });
178
+ const res = spawnSync(
179
+ "git",
180
+ args,
181
+ hiddenSpawnOptions({ cwd, encoding: "utf8" })
182
+ );
179
183
  if (res.status !== 0 && !options.allowFailure) {
180
184
  const message = `git ${args.join(" ")} failed: ${res.stderr || res.stdout}`;
181
185
  if (options.throwError) throw new Error(message);
@@ -191,7 +195,11 @@ function gitStatusShort(worktreePath) {
191
195
  }
192
196
  function gitCapture(cwd, args) {
193
197
  try {
194
- const res = spawnSync("git", args, { cwd, encoding: "utf8" });
198
+ const res = spawnSync(
199
+ "git",
200
+ args,
201
+ hiddenSpawnOptions({ cwd, encoding: "utf8" })
202
+ );
195
203
  return {
196
204
  status: res.status,
197
205
  stdout: res.stdout || "",
@@ -5648,32 +5656,98 @@ function workerStatus(args) {
5648
5656
  writeJson(path19.join(worker.workerDir, "last-status.json"), status);
5649
5657
  console.log(JSON.stringify(status, null, 2));
5650
5658
  }
5659
+ function buildWorkerListDrilldownCommands(runId) {
5660
+ return {
5661
+ full: `kynver worker list --run ${runId} --full`,
5662
+ blocked: `kynver worker list --run ${runId} --blocked`,
5663
+ running: `kynver worker list --run ${runId} --running`,
5664
+ task: `kynver worker list --run ${runId} --task <task-id>`,
5665
+ worker: `kynver worker list --run ${runId} --worker <worker>`,
5666
+ runFull: `kynver run status --run ${runId} --full`,
5667
+ workerFull: `kynver worker status --run ${runId} --name <worker>`,
5668
+ workerTail: `kynver worker tail --run ${runId} --name <worker> --lines 80`
5669
+ };
5670
+ }
5671
+ function asOptionalArg(value) {
5672
+ if (typeof value !== "string") return void 0;
5673
+ const trimmed = value.trim();
5674
+ return trimmed.length ? trimmed : void 0;
5675
+ }
5676
+ function activeWorkerFilters(args) {
5677
+ const filters = {};
5678
+ if (args.blocked === true || args.blocked === "true") filters.blocked = true;
5679
+ if (args.running === true || args.running === "true") filters.running = true;
5680
+ const task = asOptionalArg(args.task ?? args.taskId);
5681
+ if (task) filters.task = task;
5682
+ const worker = asOptionalArg(args.worker ?? args.name);
5683
+ if (worker) filters.worker = worker;
5684
+ const status = asOptionalArg(args.status);
5685
+ if (status) filters.status = status;
5686
+ return filters;
5687
+ }
5688
+ function workerMatchesFilters(worker, filters) {
5689
+ if (filters.blocked) {
5690
+ const attention = typeof worker.attention === "string" ? worker.attention : "";
5691
+ const status = typeof worker.status === "string" ? worker.status : "";
5692
+ if (!(attention === "blocked" || attention === "needs_attention" || attention === "stale" || status === "blocked")) {
5693
+ return false;
5694
+ }
5695
+ }
5696
+ if (filters.running) {
5697
+ if (!(worker.status === "running" && worker.attention !== "blocked" && worker.attention !== "needs_attention")) {
5698
+ return false;
5699
+ }
5700
+ }
5701
+ if (typeof filters.task === "string" && worker.taskId !== filters.task) return false;
5702
+ if (typeof filters.worker === "string" && worker.worker !== filters.worker) return false;
5703
+ if (typeof filters.status === "string" && worker.status !== filters.status) return false;
5704
+ return true;
5705
+ }
5706
+ function applyWorkerFilters(board, args) {
5707
+ const filters = activeWorkerFilters(args);
5708
+ if (Object.keys(filters).length === 0) return board;
5709
+ const workers = board.workers.filter((worker) => workerMatchesFilters(worker, filters));
5710
+ const filtered = {
5711
+ ...board,
5712
+ workerCount: workers.length,
5713
+ needsAttention: workers.filter((w) => w.attention && w.attention !== "ok" && w.attention !== "done").map((w) => w.worker),
5714
+ workers,
5715
+ activeFilters: filters
5716
+ };
5717
+ if (board.summary) {
5718
+ filtered.summary = {
5719
+ statusCounts: countBy(workers, "status"),
5720
+ attentionCounts: countBy(workers, "attention"),
5721
+ lifecycleCounts: countBy(workers, "lifecycleStage")
5722
+ };
5723
+ }
5724
+ if (board.controller) filtered.controller = buildControllerSummary(workers);
5725
+ return filtered;
5726
+ }
5651
5727
  function workerList(args) {
5652
5728
  const runId = resolveRunTargetArg(args);
5653
- const explicitCompact = args.compact === true || args.compact === "true";
5654
5729
  const explicitFull = args.full === true || args.full === "true";
5655
- const run = loadRun(runId);
5656
- const workerCount = Object.keys(run.workers || {}).length;
5657
- const compact = explicitCompact || !explicitFull && workerCount > 100;
5658
- const board = compact ? buildCompactRunBoard(runId) : buildRunBoard(runId);
5659
- const autoCompact = compact && !explicitCompact;
5660
- const outputWorkers = autoCompact ? board.workers.filter((worker) => worker.attention && worker.attention !== "done" && worker.attention !== "ok") : board.workers;
5730
+ const board = applyWorkerFilters(explicitFull ? buildRunBoard(runId) : buildCompactRunBoard(runId), args);
5731
+ const outputWorkers = board.workers;
5661
5732
  console.log(
5662
5733
  JSON.stringify(
5663
5734
  {
5664
5735
  runId: board.runId,
5665
5736
  status: board.status,
5737
+ projection: explicitFull ? "full" : "compact",
5666
5738
  workerCount: board.workerCount,
5667
- ...autoCompact ? {
5668
- compact: true,
5669
- compactReason: "large-worker-board",
5670
- omittedWorkerCount: board.workers.length - outputWorkers.length,
5671
- fullCommand: `kynver worker list --run ${runId} --json --full`,
5672
- compactCommand: `kynver worker list --run ${runId} --json --compact`
5739
+ ...board.activeFilters ? { activeFilters: board.activeFilters } : {},
5740
+ ...!explicitFull ? {
5741
+ resultContract: {
5742
+ projection: "compact",
5743
+ rawWorkerPayloadsOmitted: true,
5744
+ deepDetailAvailableVia: ["--full", "worker status", "worker tail", "run status --full"]
5745
+ },
5746
+ drilldownCommands: buildWorkerListDrilldownCommands(runId)
5673
5747
  } : {},
5674
5748
  needsAttention: board.needsAttention,
5675
- ...compact && "summary" in board ? { summary: board.summary } : {},
5676
- ...compact && "controller" in board ? { controller: board.controller } : {},
5749
+ ..."summary" in board ? { summary: board.summary } : {},
5750
+ ..."controller" in board ? { controller: board.controller } : {},
5677
5751
  workers: outputWorkers
5678
5752
  },
5679
5753
  null,
@@ -5941,9 +6015,33 @@ async function publishHarnessBoardSnapshot(args, source) {
5941
6015
  authRefreshFailure: res.authRefreshFailure
5942
6016
  };
5943
6017
  }
6018
+ function buildRunStatusDrilldownCommands(runId) {
6019
+ return {
6020
+ full: `kynver run status --run ${runId} --full`,
6021
+ blocked: `kynver status --run ${runId} --blocked`,
6022
+ running: `kynver status --run ${runId} --running`,
6023
+ task: `kynver status --run ${runId} --task <task-id>`,
6024
+ worker: `kynver status --run ${runId} --worker <worker>`,
6025
+ workersFull: `kynver worker list --run ${runId} --full`,
6026
+ workerFull: `kynver worker status --run ${runId} --name <worker>`,
6027
+ workerTail: `kynver worker tail --run ${runId} --name <worker> --lines 80`,
6028
+ monitorTick: `kynver monitor status --run ${runId} --tick`
6029
+ };
6030
+ }
5944
6031
  function runStatus(args) {
5945
- const compact = args.compact === true || args.compact === "true";
5946
- const board = compact ? buildCompactRunBoard(resolveRunTargetArg(args)) : buildRunBoard(resolveRunTargetArg(args));
6032
+ const runId = resolveRunTargetArg(args);
6033
+ const explicitFull = args.full === true || args.full === "true";
6034
+ const board = applyWorkerFilters(explicitFull ? buildRunBoard(runId) : buildCompactRunBoard(runId), args);
6035
+ board.projection = explicitFull ? "full" : "compact";
6036
+ if (!explicitFull) {
6037
+ board.resultContract = {
6038
+ projection: "compact",
6039
+ rawWorkerPayloadsOmitted: true,
6040
+ omittedWorkerFields: ["finalResult", "changedFiles", "gitAncestry", "completionResponse", "stdout/stderr tails"],
6041
+ deepDetailAvailableVia: ["--full", "worker status", "worker tail", "monitor status --tick"]
6042
+ };
6043
+ board.drilldownCommands = buildRunStatusDrilldownCommands(runId);
6044
+ }
5947
6045
  console.log(JSON.stringify(board, null, 2));
5948
6046
  }
5949
6047
  function tailWorker(args) {
@@ -7038,6 +7136,344 @@ async function releaseDispatchClaimAfterSpawnFailure(input) {
7038
7136
  };
7039
7137
  }
7040
7138
 
7139
+ // src/landing/land-pr.ts
7140
+ import { spawnSync as spawnSync5 } from "node:child_process";
7141
+
7142
+ // src/landing/cli-auth.ts
7143
+ import { spawnSync as spawnSync4 } from "node:child_process";
7144
+ function ensureGitHubTokenFromCliAuth() {
7145
+ if (process.env.GITHUB_TOKEN?.trim() || process.env.GH_TOKEN?.trim()) {
7146
+ return {
7147
+ source: "env",
7148
+ configured: true,
7149
+ reason: "GitHub token already configured in environment"
7150
+ };
7151
+ }
7152
+ const result = spawnSync4("gh", ["auth", "token"], {
7153
+ encoding: "utf8",
7154
+ stdio: ["ignore", "pipe", "pipe"]
7155
+ });
7156
+ if (result.status !== 0) {
7157
+ const reason = typeof result.stderr === "string" && result.stderr.trim() ? result.stderr.trim() : "gh auth token failed; run `gh auth login` on this machine";
7158
+ return { source: "missing", configured: false, reason };
7159
+ }
7160
+ const token = typeof result.stdout === "string" ? result.stdout.trim() : "";
7161
+ if (!token) {
7162
+ return {
7163
+ source: "missing",
7164
+ configured: false,
7165
+ reason: "gh auth token returned an empty token; run `gh auth login` on this machine"
7166
+ };
7167
+ }
7168
+ process.env.GH_TOKEN = token;
7169
+ return {
7170
+ source: "gh-cli",
7171
+ configured: true,
7172
+ reason: "Using local GitHub CLI auth for daemon land_pr merge"
7173
+ };
7174
+ }
7175
+
7176
+ // src/landing/land-pr.ts
7177
+ var SUCCESSFUL_CHECK_CONCLUSIONS = /* @__PURE__ */ new Set(["SUCCESS", "SKIPPED", "NEUTRAL"]);
7178
+ var READY_MERGE_STATES = /* @__PURE__ */ new Set(["CLEAN", "HAS_HOOKS"]);
7179
+ function repoArgs(repo) {
7180
+ return repo?.trim() ? ["--repo", repo.trim()] : [];
7181
+ }
7182
+ function repoFromPrUrl(prUrl) {
7183
+ const m = /github\.com\/([^/]+)\/([^/]+)\/pull\/\d+/i.exec(prUrl.trim());
7184
+ return m ? `${m[1]}/${m[2]}` : null;
7185
+ }
7186
+ function ghJson(exec, cwd, args) {
7187
+ const res = exec.gh(cwd, args);
7188
+ if (res.status !== 0) {
7189
+ throw new Error(res.stderr || res.stdout || `gh ${args.join(" ")} failed`);
7190
+ }
7191
+ return JSON.parse(res.stdout || "{}");
7192
+ }
7193
+ function checkName(check3) {
7194
+ return typeof check3.name === "string" && check3.name || typeof check3.context === "string" && check3.context || typeof check3.workflowName === "string" && check3.workflowName || "unknown check";
7195
+ }
7196
+ function classifyChecks(statusCheckRollup) {
7197
+ const checks = Array.isArray(statusCheckRollup) ? statusCheckRollup : [];
7198
+ const pending = [];
7199
+ const failed = [];
7200
+ for (const raw of checks) {
7201
+ const check3 = raw && typeof raw === "object" ? raw : {};
7202
+ const conclusion = typeof check3.conclusion === "string" ? check3.conclusion.toUpperCase() : "";
7203
+ const status = typeof check3.status === "string" ? check3.status.toUpperCase() : "";
7204
+ const state = typeof check3.state === "string" ? check3.state.toUpperCase() : "";
7205
+ if (conclusion && SUCCESSFUL_CHECK_CONCLUSIONS.has(conclusion)) continue;
7206
+ if (conclusion) {
7207
+ failed.push(`${checkName(check3)}=${conclusion}`);
7208
+ continue;
7209
+ }
7210
+ if (state && SUCCESSFUL_CHECK_CONCLUSIONS.has(state)) continue;
7211
+ if (state && state !== "PENDING") {
7212
+ failed.push(`${checkName(check3)}=${state}`);
7213
+ continue;
7214
+ }
7215
+ if (state === "PENDING") {
7216
+ pending.push(`${checkName(check3)}=${state}`);
7217
+ continue;
7218
+ }
7219
+ if (status && status !== "COMPLETED") {
7220
+ pending.push(`${checkName(check3)}=${status}`);
7221
+ continue;
7222
+ }
7223
+ pending.push(`${checkName(check3)}=PENDING`);
7224
+ }
7225
+ return { pending, failed };
7226
+ }
7227
+ function vercelCheckSuccess(statusCheckRollup) {
7228
+ const checks = Array.isArray(statusCheckRollup) ? statusCheckRollup : [];
7229
+ return checks.some((raw) => {
7230
+ const check3 = raw && typeof raw === "object" ? raw : {};
7231
+ const name = typeof check3.name === "string" && check3.name || typeof check3.context === "string" && check3.context || "";
7232
+ const conclusion = typeof check3.conclusion === "string" ? check3.conclusion.toUpperCase() : "";
7233
+ const state = typeof check3.state === "string" ? check3.state.toUpperCase() : "";
7234
+ return /^vercel/i.test(name) && (conclusion === "SUCCESS" || state === "SUCCESS");
7235
+ });
7236
+ }
7237
+ var STRUCTURED_SECTION_RE = /^##\s*(test\s*plan|verification|test\s*evidence|verify)\b/im;
7238
+ var LOCAL_VERIFICATION_RE = /typecheck|npm run (test|build|typecheck)|vitest|tsc\b|verify-pr-local|local verify|local-verify|tests? (pass|green)|build green|node --test/i;
7239
+ var DOCS_TITLE_RE = /^docs[(:]/i;
7240
+ var DOCS_BODY_RE = /docs[- ]only|documentation only|no[- ]code change|no code changes|low[- ]risk|plan tracker only|markdown only/i;
7241
+ function runtimeVerificationEvidenceSufficient(input) {
7242
+ const title = typeof input.title === "string" ? input.title : "";
7243
+ const body = typeof input.body === "string" ? input.body : "";
7244
+ const docsLowRisk = DOCS_TITLE_RE.test(title) || DOCS_BODY_RE.test(body);
7245
+ const structuredSection = STRUCTURED_SECTION_RE.test(body);
7246
+ const localVerification = LOCAL_VERIFICATION_RE.test(body);
7247
+ return docsLowRisk || structuredSection || localVerification || input.vercelCheckSuccess === true;
7248
+ }
7249
+ function assertVerificationEvidence(pr) {
7250
+ const sufficient = runtimeVerificationEvidenceSufficient({
7251
+ title: pr.title,
7252
+ body: pr.body,
7253
+ vercelCheckSuccess: vercelCheckSuccess(pr.statusCheckRollup)
7254
+ });
7255
+ if (sufficient) return;
7256
+ throw new Error(
7257
+ `PR #${pr.number} lacks landing verification evidence \u2014 add ## Test plan / ## Verification with local commands, Vercel preview, or docs/no-code rationale`
7258
+ );
7259
+ }
7260
+ function assertLandingReady(pr) {
7261
+ if (pr.state !== "OPEN") throw new Error(`PR #${pr.number} is ${pr.state}, not OPEN`);
7262
+ if (pr.isDraft) throw new Error(`PR #${pr.number} is still a draft`);
7263
+ if (!READY_MERGE_STATES.has(pr.mergeStateStatus)) {
7264
+ throw new Error(`PR #${pr.number} mergeStateStatus is ${pr.mergeStateStatus}`);
7265
+ }
7266
+ const checks = classifyChecks(pr.statusCheckRollup);
7267
+ if (checks.failed.length > 0) {
7268
+ throw new Error(`PR #${pr.number} has failing checks: ${checks.failed.join(", ")}`);
7269
+ }
7270
+ if (checks.pending.length > 0) {
7271
+ throw new Error(`PR #${pr.number} has pending checks: ${checks.pending.join(", ")}`);
7272
+ }
7273
+ assertVerificationEvidence(pr);
7274
+ }
7275
+ function landingReadinessError(pr) {
7276
+ try {
7277
+ assertLandingReady(pr);
7278
+ return null;
7279
+ } catch (err) {
7280
+ return err instanceof Error ? err.message : String(err);
7281
+ }
7282
+ }
7283
+ function deleteRemoteBranch(exec, cwd, repo, branch) {
7284
+ if (!branch?.trim()) return;
7285
+ const refPath = encodeURI(`heads/${branch}`);
7286
+ exec.gh(cwd, ["api", "-X", "DELETE", `repos/${repo}/git/refs/${refPath}`]);
7287
+ }
7288
+ function resolveRepo(exec, cwd, repo) {
7289
+ if (repo?.trim()) return repo.trim();
7290
+ const res = exec.gh(cwd, ["repo", "view", "--json", "nameWithOwner"]);
7291
+ if (res.status !== 0) throw new Error(res.stderr || "Could not resolve GitHub repo");
7292
+ const parsed = JSON.parse(res.stdout || "{}");
7293
+ if (!parsed.nameWithOwner) throw new Error("Could not resolve GitHub repo");
7294
+ return parsed.nameWithOwner;
7295
+ }
7296
+ function removeBranchWorktrees(cwd, branch) {
7297
+ if (!branch?.trim()) return;
7298
+ const list = spawnSync5("git", ["worktree", "list", "--porcelain"], {
7299
+ cwd,
7300
+ encoding: "utf8"
7301
+ });
7302
+ if (list.status !== 0) return;
7303
+ const lines = String(list.stdout || "").split(/\r?\n/);
7304
+ let currentPath = null;
7305
+ let currentBranch = null;
7306
+ for (const line of lines) {
7307
+ if (line.startsWith("worktree ")) {
7308
+ currentPath = line.slice("worktree ".length).trim();
7309
+ currentBranch = null;
7310
+ continue;
7311
+ }
7312
+ if (line.startsWith("branch ") && currentPath) {
7313
+ currentBranch = line.slice("branch ".length).trim();
7314
+ if (currentBranch.endsWith(`/${branch}`)) {
7315
+ spawnSync5("git", ["worktree", "remove", "--force", currentPath], {
7316
+ cwd,
7317
+ encoding: "utf8"
7318
+ });
7319
+ }
7320
+ }
7321
+ }
7322
+ }
7323
+ async function executeLandPrMerge(input) {
7324
+ const cwd = input.cwd ?? process.cwd();
7325
+ const exec = input.exec ?? defaultPrHandoffExec;
7326
+ const prTarget = input.prUrl.trim();
7327
+ if (!prTarget) throw new Error("prUrl is required");
7328
+ const auth = ensureGitHubTokenFromCliAuth();
7329
+ if (!auth.configured) {
7330
+ return {
7331
+ prUrl: prTarget,
7332
+ outcome: "blocked",
7333
+ reason: auth.reason
7334
+ };
7335
+ }
7336
+ const before = ghJson(exec, cwd, [
7337
+ "pr",
7338
+ "view",
7339
+ prTarget,
7340
+ ...repoArgs(input.repo),
7341
+ "--json",
7342
+ [
7343
+ "number",
7344
+ "url",
7345
+ "title",
7346
+ "body",
7347
+ "state",
7348
+ "isDraft",
7349
+ "mergeStateStatus",
7350
+ "statusCheckRollup",
7351
+ "headRefName",
7352
+ "mergedAt",
7353
+ "mergeCommit"
7354
+ ].join(",")
7355
+ ]);
7356
+ if (before.state === "MERGED" || before.mergedAt) {
7357
+ return {
7358
+ prUrl: before.url || prTarget,
7359
+ outcome: "skipped",
7360
+ mergeCommit: before.mergeCommit?.oid ?? null,
7361
+ reason: `PR #${before.number} is already merged \u2014 land_pr no-op (redelivery)`
7362
+ };
7363
+ }
7364
+ const notReadyReason = landingReadinessError(before);
7365
+ if (notReadyReason) {
7366
+ if (input.skipNotReady) {
7367
+ return {
7368
+ prUrl: before.url || prTarget,
7369
+ outcome: "skipped",
7370
+ reason: notReadyReason
7371
+ };
7372
+ }
7373
+ return {
7374
+ prUrl: before.url || prTarget,
7375
+ outcome: "blocked",
7376
+ reason: notReadyReason
7377
+ };
7378
+ }
7379
+ if (input.dryRun) {
7380
+ return {
7381
+ prUrl: before.url || prTarget,
7382
+ outcome: "skipped",
7383
+ reason: "dry-run: PR is ready to merge"
7384
+ };
7385
+ }
7386
+ const target = String(before.number);
7387
+ const mergeRes = exec.gh(cwd, [
7388
+ "pr",
7389
+ "merge",
7390
+ target,
7391
+ ...repoArgs(input.repo),
7392
+ "--squash"
7393
+ ]);
7394
+ if (mergeRes.status !== 0) {
7395
+ return {
7396
+ prUrl: before.url || prTarget,
7397
+ outcome: "blocked",
7398
+ reason: mergeRes.stderr || mergeRes.stdout || "gh pr merge failed"
7399
+ };
7400
+ }
7401
+ const after = ghJson(exec, cwd, [
7402
+ "pr",
7403
+ "view",
7404
+ target,
7405
+ ...repoArgs(input.repo),
7406
+ "--json",
7407
+ "number,url,mergedAt,mergeCommit,state"
7408
+ ]);
7409
+ if (after.state !== "MERGED" && !after.mergedAt) {
7410
+ return {
7411
+ prUrl: after.url || before.url || prTarget,
7412
+ outcome: "blocked",
7413
+ reason: `PR #${after.number} did not verify as merged after gh pr merge`
7414
+ };
7415
+ }
7416
+ const repo = resolveRepo(exec, cwd, input.repo);
7417
+ deleteRemoteBranch(exec, cwd, repo, before.headRefName);
7418
+ removeBranchWorktrees(cwd, before.headRefName);
7419
+ const mergeCommit = after.mergeCommit?.oid ?? null;
7420
+ return {
7421
+ prUrl: after.url || before.url || prTarget,
7422
+ outcome: "merged",
7423
+ mergeCommit,
7424
+ reason: `Daemon land_pr merged PR #${after.number}`
7425
+ };
7426
+ }
7427
+
7428
+ // src/landing/land-pr-completion-post.ts
7429
+ async function postLandPrHarnessCompletion(input) {
7430
+ const secret = await resolveCallbackSecretWithMint(input.secret, input.agentOsId, {
7431
+ baseUrl: input.baseUrl
7432
+ });
7433
+ const taskId = String(input.task.id);
7434
+ const url = `${input.baseUrl}/api/agent-os/by-id/${encodeURIComponent(input.agentOsId)}/harness/completion`;
7435
+ const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
7436
+ const body = {
7437
+ source: "kynver-harness",
7438
+ agentOsId: input.agentOsId,
7439
+ runId: input.runId,
7440
+ workerName: `land-pr-${taskId}`,
7441
+ taskId,
7442
+ leaseToken: input.task.leaseToken ?? null,
7443
+ startedAt: finishedAt,
7444
+ finishedAt,
7445
+ status: {
7446
+ finalResult: input.report,
7447
+ prUrl: input.report.prUrl,
7448
+ summary: input.report.reason,
7449
+ leaseToken: input.task.leaseToken ?? null
7450
+ },
7451
+ workerInjection: null
7452
+ };
7453
+ const result = await postJsonWithCredentialRefresh(url, secret, body, {
7454
+ agentOsId: input.agentOsId,
7455
+ baseUrl: input.baseUrl
7456
+ });
7457
+ return { ok: result.ok, status: result.status };
7458
+ }
7459
+
7460
+ // src/verify-live/verify-live-prompt.ts
7461
+ function formatVerifyLiveCompletionContract() {
7462
+ return [
7463
+ "## verify_live completion contract",
7464
+ "Finish with a JSON finalResult object:",
7465
+ "{",
7466
+ ' "prUrl": "<landed pr>",',
7467
+ ' "outcome": "passed" | "failed" | "skipped" | "no_mcp_surface",',
7468
+ ' "reason": "<human summary>",',
7469
+ ' "mergeCommit": "<sha optional>",',
7470
+ ' "mcpCalls": [{ "tool": "<name>", "ok": true, "summary": "..." }]',
7471
+ "}",
7472
+ "",
7473
+ "Drive the feature MCP tools against live production. Do not mark passed without exercising the MCP surface."
7474
+ ].join("\n");
7475
+ }
7476
+
7041
7477
  // src/dispatch.ts
7042
7478
  var DEFAULT_DISPATCH_LEASE_MS = 60 * 60 * 1e3;
7043
7479
  function readAdmissionExhaustion(result) {
@@ -7095,6 +7531,9 @@ function buildDispatchTaskText(task, agentOsId) {
7095
7531
  `Board linkage: agentOsId=${agentOsId}, taskId=${task.id}, attempt=${task.attempt}, executor=${task.executor}${task.executorRef ? `, executorRef=${task.executorRef}` : ""}.`,
7096
7532
  "This worker was dispatched from the AgentOS board. The harness reports your completion back to the board when you finish."
7097
7533
  ];
7534
+ if (task.executor === "verify_live") {
7535
+ lines.push("", formatVerifyLiveCompletionContract());
7536
+ }
7098
7537
  const outboxRef = extractPlanOutboxFromTask(task);
7099
7538
  if (outboxRef?.outboxId) {
7100
7539
  const item = loadOutboxById(outboxRef.outboxId);
@@ -7128,6 +7567,12 @@ function requestedTargetTaskIds(args) {
7128
7567
  async function dispatchRun(args) {
7129
7568
  const pipeline = args.pipeline === true || args.pipeline === "true";
7130
7569
  try {
7570
+ let isLandPrDecision2 = function(decision) {
7571
+ if (decision.landPrDispatch === true) return true;
7572
+ const task = decision.task;
7573
+ return task?.executor === "land_pr";
7574
+ };
7575
+ var isLandPrDecision = isLandPrDecision2;
7131
7576
  const run = loadRun(String(required(String(args.run || ""), "--run")));
7132
7577
  const agentOsId = String(required(String(args.agentOsId || ""), "--agent-os-id"));
7133
7578
  const base = resolveBaseUrl(args.baseUrl ? String(args.baseUrl) : void 0);
@@ -7262,6 +7707,66 @@ async function dispatchRun(args) {
7262
7707
  });
7263
7708
  return false;
7264
7709
  }
7710
+ async function runLandPrClaimed(decision) {
7711
+ const task = decision.task;
7712
+ const taskId = String(task.id);
7713
+ const prUrl = task.prUrl ? String(task.prUrl) : "";
7714
+ if (!prUrl) {
7715
+ return abortClaimedSpawn(task, "land_pr task missing prUrl");
7716
+ }
7717
+ try {
7718
+ const report = await executeLandPrMerge({
7719
+ prUrl,
7720
+ repo: repoFromPrUrl(prUrl),
7721
+ cwd: run.repo
7722
+ });
7723
+ const post = await postLandPrHarnessCompletion({
7724
+ baseUrl: base,
7725
+ secret,
7726
+ agentOsId,
7727
+ runId: run.id,
7728
+ task,
7729
+ report
7730
+ });
7731
+ outcomes.push({
7732
+ taskId,
7733
+ started: true,
7734
+ landPr: true,
7735
+ outcome: report.outcome,
7736
+ completionStatus: post.status
7737
+ });
7738
+ if (!post.ok) {
7739
+ return abortClaimedSpawn(
7740
+ task,
7741
+ `land_pr completion POST failed (HTTP ${post.status})`
7742
+ );
7743
+ }
7744
+ return true;
7745
+ } catch (error) {
7746
+ return abortClaimedSpawn(task, error.message);
7747
+ }
7748
+ }
7749
+ async function releaseDuplicateLocalClaim(task) {
7750
+ const error = "duplicate_dispatch_prevented: live local worker already owns this task";
7751
+ const release = await releaseDispatchClaimAfterSpawnFailure({
7752
+ baseUrl: base,
7753
+ secret,
7754
+ agentOsId,
7755
+ taskId: String(task.id),
7756
+ leaseOwner,
7757
+ failureDetail: error
7758
+ });
7759
+ outcomes.push({
7760
+ taskId: task.id,
7761
+ started: false,
7762
+ error,
7763
+ alreadyRunning: true,
7764
+ nonFatal: true,
7765
+ released: release.released,
7766
+ releaseResponse: release.releaseResponse
7767
+ });
7768
+ return false;
7769
+ }
7265
7770
  async function spawnClaimed(decision) {
7266
7771
  const task = decision.task;
7267
7772
  const harnessContext = readHarnessWorkerContext(decision);
@@ -7283,10 +7788,7 @@ async function dispatchRun(args) {
7283
7788
  );
7284
7789
  }
7285
7790
  if (hasLiveWorkerForTask(run.id, taskId)) {
7286
- return abortClaimedSpawn(
7287
- task,
7288
- "duplicate_dispatch_prevented: live local worker already owns this task"
7289
- );
7791
+ return releaseDuplicateLocalClaim(task);
7290
7792
  }
7291
7793
  const attempt = Number(task.attempt) || 1;
7292
7794
  if (attempt > retryLimits.maxTaskAttempts) {
@@ -7379,7 +7881,8 @@ async function dispatchRun(args) {
7379
7881
  }
7380
7882
  let shouldContinueDispatch = true;
7381
7883
  for (const decision of result.started) {
7382
- shouldContinueDispatch = await spawnClaimed(decision) && shouldContinueDispatch;
7884
+ const admitted = isLandPrDecision2(decision) ? await runLandPrClaimed(decision) : await spawnClaimed(decision);
7885
+ shouldContinueDispatch = admitted && shouldContinueDispatch;
7383
7886
  }
7384
7887
  skipped.push(
7385
7888
  ...result.skipped ?? []
@@ -7417,7 +7920,8 @@ async function dispatchRun(args) {
7417
7920
  if (started.length === 0) break;
7418
7921
  for (const decision of started) {
7419
7922
  if (outcomes.length >= cappedStarts) break;
7420
- shouldContinueDispatch = await spawnClaimed(decision) && shouldContinueDispatch;
7923
+ const admitted = isLandPrDecision2(decision) ? await runLandPrClaimed(decision) : await spawnClaimed(decision);
7924
+ shouldContinueDispatch = admitted && shouldContinueDispatch;
7421
7925
  if (!shouldContinueDispatch) break;
7422
7926
  }
7423
7927
  }
@@ -7454,11 +7958,12 @@ async function dispatchRun(args) {
7454
7958
  diskGate: result.diskGate,
7455
7959
  resourceGate: result.resourceGate
7456
7960
  };
7961
+ const fatalOutcome = (outcome) => !outcome.started && outcome.nonFatal !== true;
7457
7962
  if (pipeline) {
7458
- return { ok: !outcomes.some((o) => !o.started), ...summary };
7963
+ return { ok: !outcomes.some(fatalOutcome), ...summary };
7459
7964
  }
7460
7965
  console.log(JSON.stringify(summary, null, 2));
7461
- if (outcomes.some((o) => !o.started)) process.exit(1);
7966
+ if (outcomes.some(fatalOutcome)) process.exit(1);
7462
7967
  } catch (error) {
7463
7968
  if (pipeline) return { ok: false, error: error.message };
7464
7969
  console.error(`run dispatch failed: ${error.message}`);
@@ -8692,11 +9197,29 @@ function validateDaemonInstallIdentity(config = loadUserConfig(), env = process.
8692
9197
  };
8693
9198
  }
8694
9199
 
9200
+ // src/daemon-platform-guard.ts
9201
+ function envFlag(name) {
9202
+ const raw = process.env[name]?.trim().toLowerCase();
9203
+ return raw === "1" || raw === "true" || raw === "yes" || raw === "on";
9204
+ }
9205
+ function assertNativeDaemonAllowed() {
9206
+ if (process.platform !== "win32") return;
9207
+ if (envFlag("KYNVER_DAEMON_ALLOW_NATIVE_WINDOWS")) return;
9208
+ console.error(
9209
+ JSON.stringify({
9210
+ event: "daemon_start_blocked",
9211
+ reason: "native_windows_console_flash",
9212
+ remedy: "Run the daemon inside WSL: .\\scripts\\start-tier2-wsl.ps1 \u2014 or set KYNVER_DAEMON_ALLOW_NATIVE_WINDOWS=1 to override (flashes visible consoles)."
9213
+ })
9214
+ );
9215
+ process.exit(1);
9216
+ }
9217
+
8695
9218
  // src/cron/cron-env.ts
8696
9219
  import { existsSync as existsSync27 } from "node:fs";
8697
9220
  import { homedir as homedir11 } from "node:os";
8698
9221
  import path37 from "node:path";
8699
- function envFlag(name, defaultValue) {
9222
+ function envFlag2(name, defaultValue) {
8700
9223
  const raw = process.env[name]?.trim().toLowerCase();
8701
9224
  if (!raw) return defaultValue;
8702
9225
  if (raw === "0" || raw === "false" || raw === "no" || raw === "off") return false;
@@ -8732,14 +9255,14 @@ function resolveKynverCronEnv() {
8732
9255
  const secret = resolveKynverCronSecret();
8733
9256
  const credsReady = Boolean(fireBaseUrl && secret);
8734
9257
  const storeExists = existsSync27(storePath);
8735
- const defaultEnabled = credsReady && (storeExists || envFlag("KYNVER_CRON_TICK_FORCE", false));
9258
+ const defaultEnabled = credsReady && (storeExists || envFlag2("KYNVER_CRON_TICK_FORCE", false));
8736
9259
  return {
8737
9260
  storePath,
8738
9261
  statePath,
8739
9262
  lockPath: `${statePath}.lock`,
8740
9263
  fireBaseUrl,
8741
9264
  secret,
8742
- tickEnabled: envFlag("KYNVER_CRON_TICK_ENABLED", defaultEnabled),
9265
+ tickEnabled: envFlag2("KYNVER_CRON_TICK_ENABLED", defaultEnabled),
8743
9266
  tickIntervalMs: envInt("KYNVER_CRON_TICK_INTERVAL_MS", 6e4, 5e3),
8744
9267
  missedRunPolicy: process.env.KYNVER_CRON_MISSED_RUN_POLICY?.trim().toLowerCase() === "skip" ? "skip" : "catch_up",
8745
9268
  maxCatchUpPerTick: envInt("KYNVER_CRON_MAX_CATCH_UP_PER_TICK", 3, 0),
@@ -9426,23 +9949,6 @@ function materialWorktreeChanges2(changedFiles) {
9426
9949
  });
9427
9950
  }
9428
9951
 
9429
- // src/cleanup-index-status.ts
9430
- function indexedWorktreeStatus(entry) {
9431
- if (!entry.status) {
9432
- entry.status = computeWorkerStatus(entry.worker, {
9433
- base: entry.run.base,
9434
- baseCommit: entry.run.baseCommit
9435
- });
9436
- }
9437
- return entry.status;
9438
- }
9439
- function indexedWorktreeHasMaterialChanges(entry) {
9440
- if (entry.status) {
9441
- return materialWorktreeChanges2(entry.status.changedFiles).length > 0;
9442
- }
9443
- return materialWorktreeChanges2(gitStatusShort(entry.worktreePath)).length > 0;
9444
- }
9445
-
9446
9952
  // src/cleanup-worktree-salvage.ts
9447
9953
  function prUrlFromFinalResult(finalResult) {
9448
9954
  if (typeof finalResult === "string") {
@@ -9469,6 +9975,83 @@ function isPrOrUnmergedWork(status) {
9469
9975
  return false;
9470
9976
  }
9471
9977
 
9978
+ // src/cleanup-index-status.ts
9979
+ function indexedWorktreeStatus(entry) {
9980
+ if (!entry.status) {
9981
+ entry.status = computeWorkerStatus(entry.worker, {
9982
+ base: entry.run.base,
9983
+ baseCommit: entry.run.baseCommit
9984
+ });
9985
+ }
9986
+ return entry.status;
9987
+ }
9988
+ function indexedWorktreeHasMaterialChanges(entry, gitStatusCache) {
9989
+ if (entry.status) {
9990
+ return materialWorktreeChanges2(entry.status.changedFiles).length > 0;
9991
+ }
9992
+ const porcelain = gitStatusCache ? gitStatusCache.porcelain(entry.worktreePath) : gitStatusShort(entry.worktreePath);
9993
+ return materialWorktreeChanges2(porcelain).length > 0;
9994
+ }
9995
+ function finalResultFromWorkerJson(entry) {
9996
+ const snapshot = entry.worker.completionSnapshot?.finalResult;
9997
+ if (snapshot !== void 0 && snapshot !== null) return snapshot;
9998
+ if (entry.worker.taskPrUrl) {
9999
+ return { prUrl: entry.worker.taskPrUrl };
10000
+ }
10001
+ return null;
10002
+ }
10003
+ function resolveWorktreeGuardStatus(entry, ctx) {
10004
+ if (entry.status) return entry.status;
10005
+ const worker = entry.worker;
10006
+ const completionAcknowledged = typeof worker.completionReportedAt === "string" && worker.completionReportedAt.trim().length > 0;
10007
+ const workerJsonTerminal = Boolean(worker.status && ["done", "exited", "blocked", "failed", "abandoned"].includes(worker.status)) || completionAcknowledged;
10008
+ const finalResult = finalResultFromWorkerJson(entry);
10009
+ if (workerJsonTerminal && !isPidAlive(worker.pid)) {
10010
+ const changedFiles = ctx?.gitStatusCache ? ctx.gitStatusCache.porcelain(entry.worktreePath) : gitStatusShort(entry.worktreePath);
10011
+ const baseLabel = entry.run.baseCommit?.trim() || entry.run.base?.trim() || "origin/main";
10012
+ const ahead = ctx?.gitRevCache?.countAheadOfMain(entry.worktreePath, baseLabel);
10013
+ const gitAncestry = ahead === 0 ? {
10014
+ checked: true,
10015
+ base: baseLabel,
10016
+ relation: "synced"
10017
+ } : computeGitAncestry(entry.worktreePath, {
10018
+ base: entry.run.base,
10019
+ baseCommit: entry.run.baseCommit
10020
+ });
10021
+ const status = {
10022
+ runId: entry.runId,
10023
+ worker: entry.workerName,
10024
+ pid: worker.pid,
10025
+ alive: false,
10026
+ status: worker.status ?? (completionAcknowledged ? "done" : "exited"),
10027
+ attention: { state: completionAcknowledged ? "done" : "stale" },
10028
+ branch: worker.branch,
10029
+ worktreePath: entry.worktreePath,
10030
+ ownedPaths: worker.ownedPaths,
10031
+ stdoutBytes: 0,
10032
+ stderrBytes: 0,
10033
+ heartbeatBytes: 0,
10034
+ firstEventAt: null,
10035
+ lastEventAt: null,
10036
+ lastActivityAt: worker.completionReportedAt ?? null,
10037
+ currentTool: null,
10038
+ heartbeatCount: 0,
10039
+ lastHeartbeatAt: null,
10040
+ lastHeartbeatPhase: null,
10041
+ lastHeartbeatSummary: null,
10042
+ heartbeatBlocker: null,
10043
+ changedFiles,
10044
+ gitAncestry,
10045
+ finalResult,
10046
+ completionBlocker: typeof worker.completionBlocker === "string" ? worker.completionBlocker.trim() || null : null,
10047
+ prUrl: worker.repairTargetPrUrl ?? worker.taskPrUrl ?? prUrlFromFinalResult(finalResult)
10048
+ };
10049
+ entry.status = status;
10050
+ return status;
10051
+ }
10052
+ return indexedWorktreeStatus(entry);
10053
+ }
10054
+
9472
10055
  // src/cleanup-completion-blocker.ts
9473
10056
  function completionBlockerBlocksWorktreeRemoval(indexed, status) {
9474
10057
  const blocker = typeof indexed.worker.completionBlocker === "string" ? indexed.worker.completionBlocker.trim() : "";
@@ -9489,13 +10072,28 @@ function completionBlockerBlocksWorktreeRemoval(indexed, status) {
9489
10072
  }
9490
10073
 
9491
10074
  // src/cleanup-run-liveness.ts
10075
+ var TERMINAL_WORKER_JSON_STATUSES = /* @__PURE__ */ new Set([
10076
+ "done",
10077
+ "exited",
10078
+ "blocked",
10079
+ "failed",
10080
+ "abandoned"
10081
+ ]);
9492
10082
  function deriveRunTerminal(indexed, ctx) {
9493
10083
  if (ctx) return ctx.runTerminalCache.derive(indexed.run);
9494
10084
  return deriveTerminalRunStatus(indexed.run);
9495
10085
  }
9496
10086
  function isWorkerProcessLive(indexed) {
9497
10087
  if (isPidAlive(indexed.worker.pid)) return true;
9498
- if (!indexed.worker.pid) return indexedWorktreeStatus(indexed).alive;
10088
+ if (typeof indexed.worker.completionReportedAt === "string" && indexed.worker.completionReportedAt.trim().length > 0) {
10089
+ return false;
10090
+ }
10091
+ const workerStatus2 = indexed.worker.status;
10092
+ if (workerStatus2 && TERMINAL_WORKER_JSON_STATUSES.has(workerStatus2)) return false;
10093
+ if (!indexed.worker.pid) {
10094
+ if (workerStatus2 !== "running") return false;
10095
+ return indexedWorktreeStatus(indexed).alive;
10096
+ }
9499
10097
  return false;
9500
10098
  }
9501
10099
  function isRunStaleActive(indexed, ctx) {
@@ -9504,6 +10102,10 @@ function isRunStaleActive(indexed, ctx) {
9504
10102
  }
9505
10103
  function runBlocksWorktreeRemoval(indexed, ctx) {
9506
10104
  if (isWorkerProcessLive(indexed)) return true;
10105
+ const workerStatus2 = indexed.worker.status;
10106
+ if (workerStatus2 && TERMINAL_WORKER_JSON_STATUSES.has(workerStatus2) && !indexed.worker.completionBlocker) {
10107
+ return false;
10108
+ }
9507
10109
  const status = indexedWorktreeStatus(indexed);
9508
10110
  if (completionBlockerBlocksWorktreeRemoval(indexed, status)) return true;
9509
10111
  if (isFinishedWorkerStatus(status)) return false;
@@ -9522,7 +10124,7 @@ function effectiveWorktreeAgeMs(input) {
9522
10124
  if (input.liveness && isRunStaleActive(indexed, input.liveness)) {
9523
10125
  return terminalWorktreesAgeMs;
9524
10126
  }
9525
- if (input.liveness && isFinishedWorkerStatus(indexedWorktreeStatus(indexed)) && !isWorkerProcessLive(indexed)) {
10127
+ if (input.liveness && isFinishedWorkerStatus(resolveWorktreeGuardStatus(indexed, input.liveness)) && !isWorkerProcessLive(indexed)) {
9526
10128
  return terminalWorktreesAgeMs;
9527
10129
  }
9528
10130
  return worktreesAgeMs;
@@ -9536,8 +10138,13 @@ function skipWorktreeRemoval(input) {
9536
10138
  const ageThresholdMs = effectiveWorktreeAgeMs(input);
9537
10139
  if (worktreesAgeMs <= 0 && !includeOrphans && ageThresholdMs <= 0) return "worktrees_disabled";
9538
10140
  if (ageThresholdMs > 0 && ageMs < ageThresholdMs) return "below_age_threshold";
9539
- const status = indexedWorktreeStatus(indexed);
9540
10141
  if (isWorkerProcessLive(indexed)) return "active_worker";
10142
+ if (indexedWorktreeHasMaterialChanges(indexed, input.liveness?.gitStatusCache)) {
10143
+ return "dirty_worktree";
10144
+ }
10145
+ const ahead = input.liveness?.gitRevCache?.countAheadOfMain(input.worktreePath);
10146
+ if (ahead !== null && ahead !== void 0 && ahead > 0) return "pr_or_unmerged_commits";
10147
+ const status = resolveWorktreeGuardStatus(indexed, input.liveness);
9541
10148
  if (completionBlockerBlocksWorktreeRemoval(indexed, status)) return "completion_blocked";
9542
10149
  if (runBlocksWorktreeRemoval(indexed, input.liveness)) return "run_still_active";
9543
10150
  if (!isFinishedWorkerStatus(status)) return "run_still_active";
@@ -9568,7 +10175,9 @@ function skipDependencyCacheRemoval(input) {
9568
10175
  if (!diskPressure && ageMs < nodeModulesAgeMs) return "below_age_threshold";
9569
10176
  if (activeWorktreePaths.has(path40.resolve(worktreePath))) return "active_worker";
9570
10177
  if (indexed && isWorkerProcessLive(indexed)) return "active_worker";
9571
- if (indexed && indexedWorktreeHasMaterialChanges(indexed)) return "dirty_worktree";
10178
+ if (indexed && indexedWorktreeHasMaterialChanges(indexed, input.gitStatusCache)) {
10179
+ return "dirty_worktree";
10180
+ }
9572
10181
  return null;
9573
10182
  }
9574
10183
  function skipBuildCacheRemoval(input) {
@@ -9817,7 +10426,7 @@ function pathHasForeignOwnedEntry(targetPath, maxEntries = 32) {
9817
10426
  }
9818
10427
 
9819
10428
  // src/cleanup-privileged-remove.ts
9820
- import { spawnSync as spawnSync4 } from "node:child_process";
10429
+ import { spawnSync as spawnSync6 } from "node:child_process";
9821
10430
  import path45 from "node:path";
9822
10431
 
9823
10432
  // src/cleanup-harness-path-validate.ts
@@ -9862,7 +10471,7 @@ function resolvePrivilegedCleanupMode() {
9862
10471
  return "auto";
9863
10472
  }
9864
10473
  function runSudoNonInteractive(argv) {
9865
- const res = spawnSync4("sudo", ["-n", ...argv], {
10474
+ const res = spawnSync6("sudo", ["-n", ...argv], {
9866
10475
  encoding: "utf8",
9867
10476
  stdio: ["ignore", "pipe", "pipe"]
9868
10477
  });
@@ -10410,7 +11019,7 @@ function buildWorktreeIndexAt(harnessRoot) {
10410
11019
  }
10411
11020
 
10412
11021
  // src/cleanup-retention-config.ts
10413
- function envFlag2(name) {
11022
+ function envFlag3(name) {
10414
11023
  const v = process.env[name];
10415
11024
  return v === "1" || v === "true" || v === "yes";
10416
11025
  }
@@ -10421,17 +11030,17 @@ function envMs(name, fallback) {
10421
11030
  return Number.isFinite(n) && n >= 0 ? n : fallback;
10422
11031
  }
10423
11032
  function resolveHarnessRetention(options = {}) {
10424
- const execute = options.execute === true || options.execute !== false && envFlag2("KYNVER_CLEANUP_EXECUTE");
10425
- const finalizeStaleRuns2 = options.finalizeStaleRuns !== false && !envFlag2("KYNVER_CLEANUP_SKIP_FINALIZE");
11033
+ const execute = options.execute === true || options.execute !== false && envFlag3("KYNVER_CLEANUP_EXECUTE");
11034
+ const finalizeStaleRuns2 = options.finalizeStaleRuns !== false && !envFlag3("KYNVER_CLEANUP_SKIP_FINALIZE");
10426
11035
  const nodeModulesAgeMs = options.nodeModulesAgeMs ?? envMs("KYNVER_CLEANUP_NODE_MODULES_AGE_MS", DEFAULT_NODE_MODULES_AGE_MS);
10427
11036
  const worktreesAgeMs = options.worktreesAgeMs ?? envMs("KYNVER_CLEANUP_WORKTREES_AGE_MS", 0);
10428
11037
  const terminalWorktreesAgeMs = options.terminalWorktreesAgeMs ?? envMs("KYNVER_CLEANUP_TERMINAL_WORKTREES_AGE_MS", DEFAULT_TERMINAL_WORKTREES_AGE_MS);
10429
11038
  const runDirectoriesAgeMs = options.runDirectoriesAgeMs ?? envMs("KYNVER_CLEANUP_RUN_DIRECTORIES_AGE_MS", DEFAULT_RUN_DIRECTORIES_AGE_MS);
10430
11039
  const maxActionsPerSweep = options.maxActionsPerSweep ?? envMs("KYNVER_CLEANUP_MAX_ACTIONS_PER_SWEEP", DEFAULT_MAX_ACTIONS_PER_SWEEP);
10431
- const includeOrphans = options.includeOrphans === true || envFlag2("KYNVER_CLEANUP_INCLUDE_ORPHANS");
10432
- const scopeAll = envFlag2("KYNVER_CLEANUP_SCOPE_ALL") || process.env.KYNVER_CLEANUP_SCOPE === "all";
11040
+ const includeOrphans = options.includeOrphans === true || envFlag3("KYNVER_CLEANUP_INCLUDE_ORPHANS");
11041
+ const scopeAll = envFlag3("KYNVER_CLEANUP_SCOPE_ALL") || process.env.KYNVER_CLEANUP_SCOPE === "all";
10433
11042
  const runIdFilter = scopeAll ? options.runIdFilter : options.runIdFilter ?? (process.env.KYNVER_CLEANUP_RUN_ID || void 0);
10434
- const accountBytes = options.accountBytes !== false && !envFlag2("KYNVER_CLEANUP_SKIP_BYTE_ACCOUNTING");
11043
+ const accountBytes = options.accountBytes !== false && !envFlag3("KYNVER_CLEANUP_SKIP_BYTE_ACCOUNTING");
10435
11044
  const storageCapEnv = envMs("KYNVER_CLEANUP_STORAGE_ENTRY_CAP", 2e3);
10436
11045
  const storagePerRunEntryCap = options.storagePerRunEntryCap !== void 0 ? options.storagePerRunEntryCap : accountBytes ? storageCapEnv > 0 ? storageCapEnv : null : null;
10437
11046
  const byteCapEnv = envMs("KYNVER_CLEANUP_BYTE_ENTRY_CAP", 2e3);
@@ -10628,7 +11237,7 @@ function resolveHarnessScanRoots(options = {}) {
10628
11237
  }
10629
11238
 
10630
11239
  // src/cleanup-disk-pressure.ts
10631
- function envFlag3(name) {
11240
+ function envFlag4(name) {
10632
11241
  const v = process.env[name];
10633
11242
  return v === "1" || v === "true" || v === "yes";
10634
11243
  }
@@ -10651,7 +11260,7 @@ function observeCleanupDiskPressure(input = {}) {
10651
11260
  }
10652
11261
  function applyDiskPressureToRetention(retention, pressure) {
10653
11262
  if (!pressure.pressured) return retention;
10654
- const executeOnPressure = retention.execute || !envFlag3("KYNVER_CLEANUP_DRY_RUN_ON_PRESSURE");
11263
+ const executeOnPressure = retention.execute || !envFlag4("KYNVER_CLEANUP_DRY_RUN_ON_PRESSURE");
10655
11264
  return {
10656
11265
  ...retention,
10657
11266
  execute: executeOnPressure,
@@ -10676,6 +11285,37 @@ function emitCleanupProgress(phase, detail) {
10676
11285
  console.error(`[kynver cleanup] ${phase}${suffix}`);
10677
11286
  }
10678
11287
 
11288
+ // src/cleanup-git-rev-cache.ts
11289
+ var CleanupGitRevCache = class {
11290
+ aheadOfMain = /* @__PURE__ */ new Map();
11291
+ countAheadOfMain(worktreePath, base = "origin/main") {
11292
+ const key = `${worktreePath}\0${base}`;
11293
+ if (this.aheadOfMain.has(key)) return this.aheadOfMain.get(key) ?? null;
11294
+ const result = gitCapture(worktreePath, ["rev-list", "--count", `${base}..HEAD`]);
11295
+ if (result.status !== 0) {
11296
+ this.aheadOfMain.set(key, null);
11297
+ return null;
11298
+ }
11299
+ const count = Number(result.stdout.trim());
11300
+ const parsed = Number.isFinite(count) ? count : null;
11301
+ this.aheadOfMain.set(key, parsed);
11302
+ return parsed;
11303
+ }
11304
+ };
11305
+
11306
+ // src/cleanup-git-status-cache.ts
11307
+ var CleanupGitStatusCache = class {
11308
+ cache = /* @__PURE__ */ new Map();
11309
+ porcelain(worktreePath) {
11310
+ const resolved = worktreePath;
11311
+ const cached = this.cache.get(resolved);
11312
+ if (cached !== void 0) return cached;
11313
+ const lines = gitStatusShort(resolved);
11314
+ this.cache.set(resolved, lines);
11315
+ return lines;
11316
+ }
11317
+ };
11318
+
10679
11319
  // src/cleanup-run-terminal-cache.ts
10680
11320
  var CleanupRunTerminalCache = class {
10681
11321
  cache = /* @__PURE__ */ new Map();
@@ -10793,7 +11433,11 @@ function runHarnessCleanup(options = {}) {
10793
11433
  emitCleanupProgress("index", "building worktree index");
10794
11434
  const index = mergeWorktreeIndexes(paths.scanRoots);
10795
11435
  emitCleanupProgress("index", `${index.size} indexed worktree(s)`);
10796
- const liveness = { runTerminalCache: new CleanupRunTerminalCache() };
11436
+ const liveness = {
11437
+ runTerminalCache: new CleanupRunTerminalCache(),
11438
+ gitStatusCache: new CleanupGitStatusCache(),
11439
+ gitRevCache: new CleanupGitRevCache()
11440
+ };
10797
11441
  const skips = [];
10798
11442
  const actions = [];
10799
11443
  const processedPaths = /* @__PURE__ */ new Set();
@@ -10813,8 +11457,15 @@ function runHarnessCleanup(options = {}) {
10813
11457
  index,
10814
11458
  now: paths.now
10815
11459
  };
10816
- for (const raw of scanDependencyCacheCandidates(scanOpts)) {
11460
+ const dependencyCandidates = scanDependencyCacheCandidates(scanOpts);
11461
+ emitCleanupProgress("dependency", `${dependencyCandidates.length} cache candidate(s) at ${harnessRoot}`);
11462
+ let dependencyProcessed = 0;
11463
+ for (const raw of dependencyCandidates) {
10817
11464
  if (atSweepCap()) break;
11465
+ dependencyProcessed += 1;
11466
+ if (dependencyProcessed % 50 === 0) {
11467
+ emitCleanupProgress("dependency", `${dependencyProcessed}/${dependencyCandidates.length} evaluated`);
11468
+ }
10818
11469
  const resolved = path53.resolve(raw.path);
10819
11470
  if (processedPaths.has(resolved)) continue;
10820
11471
  processedPaths.add(resolved);
@@ -10834,7 +11485,8 @@ function runHarnessCleanup(options = {}) {
10834
11485
  ageMs: candidate.ageMs,
10835
11486
  worktreePath,
10836
11487
  activeWorktreePaths: activeGuards.activeWorktreePaths,
10837
- diskPressure: retention.diskPressure
11488
+ diskPressure: retention.diskPressure,
11489
+ gitStatusCache: liveness.gitStatusCache
10838
11490
  });
10839
11491
  if (guardReason) {
10840
11492
  recordSkip(skips, candidate.path, guardReason);
@@ -10869,7 +11521,8 @@ function runHarnessCleanup(options = {}) {
10869
11521
  ageMs: candidate.ageMs,
10870
11522
  worktreePath,
10871
11523
  activeWorktreePaths: activeGuards.activeWorktreePaths,
10872
- diskPressure: retention.diskPressure
11524
+ diskPressure: retention.diskPressure,
11525
+ gitStatusCache: liveness.gitStatusCache
10873
11526
  });
10874
11527
  if (guardReason) {
10875
11528
  recordSkip(skips, candidate.path, guardReason);
@@ -10889,8 +11542,13 @@ function runHarnessCleanup(options = {}) {
10889
11542
  ];
10890
11543
  emitCleanupProgress("worktrees", `${worktreeCandidates.length} candidate(s) at ${harnessRoot}`);
10891
11544
  const worktreeSeen = /* @__PURE__ */ new Set();
11545
+ let worktreeProcessed = 0;
10892
11546
  for (const raw of worktreeCandidates) {
10893
11547
  if (atSweepCap()) break;
11548
+ worktreeProcessed += 1;
11549
+ if (worktreeProcessed % 50 === 0) {
11550
+ emitCleanupProgress("worktrees", `${worktreeProcessed}/${worktreeCandidates.length} evaluated`);
11551
+ }
10894
11552
  const resolved = path53.resolve(raw.path);
10895
11553
  if (worktreeSeen.has(resolved)) continue;
10896
11554
  worktreeSeen.add(resolved);
@@ -11486,6 +12144,7 @@ async function awaitDaemonBackoff(ms, isStopping) {
11486
12144
  }
11487
12145
  }
11488
12146
  async function runDaemon(args) {
12147
+ assertNativeDaemonAllowed();
11489
12148
  const runId = String(required(String(args.run || ""), "--run"));
11490
12149
  const agentOsId = String(required(String(args.agentOsId || loadUserConfig().agentOsId || ""), "--agent-os-id"));
11491
12150
  const execute = args.execute !== false && args.execute !== "false";
@@ -11590,7 +12249,7 @@ function formatNodeOptionsFlag(mb = resolveNodeOldSpaceSizeMb()) {
11590
12249
  }
11591
12250
 
11592
12251
  // src/bounded-build/systemd-wrap.ts
11593
- import { spawnSync as spawnSync5 } from "node:child_process";
12252
+ import { spawnSync as spawnSync7 } from "node:child_process";
11594
12253
  var systemdAvailableCache;
11595
12254
  function isSystemdRunAvailable() {
11596
12255
  if (process.env.KYNVER_BUILD_SKIP_SYSTEMD === "1" || process.env.KYNVER_BUILD_SKIP_SYSTEMD === "true") {
@@ -11601,7 +12260,7 @@ function isSystemdRunAvailable() {
11601
12260
  systemdAvailableCache = false;
11602
12261
  return false;
11603
12262
  }
11604
- const res = spawnSync5("systemd-run", ["--version"], { encoding: "utf8", stdio: ["ignore", "ignore", "pipe"] });
12263
+ const res = spawnSync7("systemd-run", ["--version"], { encoding: "utf8", stdio: ["ignore", "ignore", "pipe"] });
11605
12264
  systemdAvailableCache = res.status === 0;
11606
12265
  return systemdAvailableCache;
11607
12266
  }
@@ -11625,7 +12284,7 @@ function buildSystemdRunArgv(opts) {
11625
12284
  }
11626
12285
 
11627
12286
  // src/bounded-build/admission.ts
11628
- import { spawnSync as spawnSync6 } from "node:child_process";
12287
+ import { spawnSync as spawnSync8 } from "node:child_process";
11629
12288
  function positiveInt4(value, fallback) {
11630
12289
  const n = Number(value);
11631
12290
  if (!Number.isFinite(n) || n <= 0) return fallback;
@@ -11661,7 +12320,7 @@ function assessBuildAdmission(opts = {}) {
11661
12320
  }
11662
12321
  function sleepMs2(ms) {
11663
12322
  if (ms <= 0) return;
11664
- spawnSync6(process.execPath, ["-e", `const d=Date.now()+${Math.floor(ms)};while(Date.now()<d);`], {
12323
+ spawnSync8(process.execPath, ["-e", `const d=Date.now()+${Math.floor(ms)};while(Date.now()<d);`], {
11665
12324
  stdio: "ignore"
11666
12325
  });
11667
12326
  }
@@ -11682,7 +12341,7 @@ function waitForBuildAdmission(timeoutMs, pollMs = 2e3, opts = {}) {
11682
12341
  }
11683
12342
 
11684
12343
  // src/bounded-build/exec.ts
11685
- import { spawnSync as spawnSync8 } from "node:child_process";
12344
+ import { spawnSync as spawnSync10 } from "node:child_process";
11686
12345
 
11687
12346
  // src/heavy-verification/slot.ts
11688
12347
  import {
@@ -11888,10 +12547,10 @@ function assessHeavyVerificationGate(command, opts = {}) {
11888
12547
  }
11889
12548
 
11890
12549
  // src/heavy-verification/gate.ts
11891
- import { spawnSync as spawnSync7 } from "node:child_process";
12550
+ import { spawnSync as spawnSync9 } from "node:child_process";
11892
12551
  function sleepMs3(ms) {
11893
12552
  if (ms <= 0) return;
11894
- spawnSync7(process.execPath, ["-e", `const d=Date.now()+${Math.floor(ms)};while(Date.now()<d);`], {
12553
+ spawnSync9(process.execPath, ["-e", `const d=Date.now()+${Math.floor(ms)};while(Date.now()<d);`], {
11895
12554
  stdio: "ignore"
11896
12555
  });
11897
12556
  }
@@ -11934,7 +12593,7 @@ function envArgv(env) {
11934
12593
  return out;
11935
12594
  }
11936
12595
  function runSpawn(argv, opts) {
11937
- const res = spawnSync8(argv[0], argv.slice(1), {
12596
+ const res = spawnSync10(argv[0], argv.slice(1), {
11938
12597
  cwd: opts.cwd,
11939
12598
  env: opts.env,
11940
12599
  encoding: "utf8",
@@ -13214,10 +13873,10 @@ import path68 from "node:path";
13214
13873
  import { accessSync, constants, existsSync as existsSync45, readFileSync as readFileSync17 } from "node:fs";
13215
13874
  import { homedir as homedir15 } from "node:os";
13216
13875
  import path67 from "node:path";
13217
- import { spawnSync as spawnSync9 } from "node:child_process";
13876
+ import { spawnSync as spawnSync11 } from "node:child_process";
13218
13877
  function captureCommand(bin, args) {
13219
13878
  try {
13220
- const res = spawnSync9(bin, args, { encoding: "utf8" });
13879
+ const res = spawnSync11(bin, args, { encoding: "utf8" });
13221
13880
  const stdout = (res.stdout || "").trim();
13222
13881
  const stderr = (res.stderr || "").trim();
13223
13882
  const ok = res.status === 0;
@@ -13466,12 +14125,12 @@ function assessRuntimeTakeoverScheduler(env, ctx) {
13466
14125
  return check({
13467
14126
  id: "hotspot_openclaw_scheduler",
13468
14127
  label: "Scheduler provider (runtime daemon vs OpenClaw cron)",
13469
- status: "pass",
13470
- summary: "KYNVER_SCHEDULER_PROVIDER unset on hosted deployment; scheduler resolves to kynver-cron (Kynver-owned, not OpenClaw)",
13471
- remediation: "For production hosted AgentOS schedules, set QSTASH_TOKEN and KYNVER_SCHEDULER_PROVIDER=qstash on the Kynver server. Dev/harness hosts may rely on kynver-cron or `kynver daemon` without OpenClaw.",
14128
+ status: "warn",
14129
+ summary: "Hosted deployment with no QSTASH_TOKEN and no KYNVER_SCHEDULER_PROVIDER \u2014 kynver-cron is an in-process stub here and will NOT fire; no firing scheduler is configured",
14130
+ remediation: "Set QSTASH_TOKEN + KYNVER_SCHEDULER_PROVIDER=qstash on the Kynver server for hosted AgentOS schedules. Only use kynver-cron when a `kynver daemon` (or local cron store) actually owns firing on this box.",
13472
14131
  details: {
13473
14132
  schedulerProvider: null,
13474
- resolvedFallback: "kynver-cron",
14133
+ resolvedFallback: "none",
13475
14134
  qstashTokenPresent: false,
13476
14135
  hostedDeployment
13477
14136
  }
@@ -14117,7 +14776,7 @@ var LANDING_MAINTAINER_LANE_SPEC = {
14117
14776
  };
14118
14777
 
14119
14778
  // src/lane/landing-maintainer-local.ts
14120
- import { spawnSync as spawnSync10 } from "node:child_process";
14779
+ import { spawnSync as spawnSync12 } from "node:child_process";
14121
14780
  import path70 from "node:path";
14122
14781
  function runLandingWrapper(prNumber, repoRoot, execute) {
14123
14782
  const script = path70.join(repoRoot, LANDING_MAINTAINER_LANE_SPEC.landScript);
@@ -14131,7 +14790,7 @@ function runLandingWrapper(prNumber, repoRoot, execute) {
14131
14790
  stderr: ""
14132
14791
  };
14133
14792
  }
14134
- const result = spawnSync10("node", args, {
14793
+ const result = spawnSync12("node", args, {
14135
14794
  cwd: repoRoot,
14136
14795
  encoding: "utf8",
14137
14796
  timeout: 10 * 60 * 1e3
@@ -14273,14 +14932,15 @@ function usage(code = 0) {
14273
14932
  " kynver runner credential [--agent-os-id ID] [--base-url URL]",
14274
14933
  " kynver setup [--api-base-url URL] [--agent-os-id ID] [--agent-os-slug SLUG] [--box-kind forge|ghost] [--repo PATH] [--discover-repo] [--max-workers N] [--provider claude|cursor]",
14275
14934
  " kynver daemon --run RUN_ID --agent-os-id AOS_ID [--execute] [--interval-ms MS]",
14935
+ " kynver status --run RUN_ID [--blocked] [--running] [--task TASK_ID] [--worker WORKER] [--full] # top-level compact run status",
14276
14936
  " kynver run create [--repo /path/repo] [--name name] [--base origin/main]",
14277
14937
  " kynver run list",
14278
14938
  " kynver run resolve --name RUN_NAME",
14279
- " kynver run status --run RUN_ID [--json] [--compact]",
14939
+ " kynver run status --run RUN_ID [--full] # defaults to compact shallow map with drill-down commands",
14280
14940
  " 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 /] [--reconcile-stale-blockers]",
14281
14941
  " kynver run sweep --run RUN_ID --agent-os-id AOS_ID [--base-url URL] [--secret SECRET] [--grace-ms MS]",
14282
14942
  ' kynver worker start --run RUN_ID --name worker --task "..." [--owned path[,path]] [--model MODEL] [--provider claude|cursor] [--agent-os-id AOS_ID] [--task-id TASK_ID] [--wait]',
14283
- " kynver worker list --run RUN_ID [--json] [--compact] [--full]",
14943
+ " kynver worker list --run RUN_ID [--full] # defaults to compact shallow map with drill-down commands",
14284
14944
  " kynver worker status --run RUN_ID --name worker",
14285
14945
  " kynver worker tail --run RUN_ID --name worker [--lines 40] [--raw]",
14286
14946
  " kynver worker stop --run RUN_ID --name worker",
@@ -14334,6 +14994,9 @@ async function main(argv = process.argv.slice(2)) {
14334
14994
  const { runsDir, worktreesDir } = getPaths();
14335
14995
  mkdirSync10(runsDir, { recursive: true });
14336
14996
  mkdirSync10(worktreesDir, { recursive: true });
14997
+ if (scope === "daemon") {
14998
+ assertNativeDaemonAllowed();
14999
+ }
14337
15000
  if (shouldEnforceMemoryCostPackageGuardCli(scope, action)) {
14338
15001
  let repoRoot;
14339
15002
  const runId = args.run ? String(args.run).trim() : "";
@@ -14350,6 +15013,7 @@ async function main(argv = process.argv.slice(2)) {
14350
15013
  });
14351
15014
  }
14352
15015
  if (scope === "login") return void await runLogin(args);
15016
+ if (scope === "status") return runStatus(args);
14353
15017
  if (scope === "runner" && action === "credential") return void await mintRunnerCredential(args);
14354
15018
  if (scope === "setup") return void await runSetup(args);
14355
15019
  if (scope === "daemon") return void await runDaemon(args);