@kynver-app/runtime 0.1.102 → 0.1.105
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/cleanup-completion-blocker.d.ts +10 -0
- package/dist/cleanup-guards.d.ts +1 -6
- package/dist/cleanup-worktree-salvage.d.ts +7 -0
- package/dist/cli.js +181 -64
- package/dist/cli.js.map +4 -4
- package/dist/index.js +181 -64
- package/dist/index.js.map +4 -4
- package/dist/server/cleanup.d.ts +3 -0
- package/dist/server/cleanup.js +3511 -0
- package/dist/server/cleanup.js.map +7 -0
- package/dist/server/default-repo.d.ts +1 -0
- package/dist/server/default-repo.js +228 -0
- package/dist/server/default-repo.js.map +7 -0
- package/dist/server/harness-notice.d.ts +2 -0
- package/dist/server/harness-notice.js +287 -0
- package/dist/server/harness-notice.js.map +7 -0
- package/dist/server/heavy-verification.d.ts +2 -0
- package/dist/server/heavy-verification.js +223 -0
- package/dist/server/heavy-verification.js.map +7 -0
- package/dist/server/landing.d.ts +1 -0
- package/dist/server/landing.js +44 -0
- package/dist/server/landing.js.map +7 -0
- package/dist/server/memory-cost-enforce.d.ts +1 -0
- package/dist/server/memory-cost-enforce.js +470 -0
- package/dist/server/memory-cost-enforce.js.map +7 -0
- package/dist/server/memory-cost.d.ts +1 -0
- package/dist/server/memory-cost.js +184 -0
- package/dist/server/memory-cost.js.map +7 -0
- package/dist/server/monitor.d.ts +3 -0
- package/dist/server/monitor.js +1577 -0
- package/dist/server/monitor.js.map +7 -0
- package/dist/server/orchestration.d.ts +10 -0
- package/dist/server/orchestration.js +444 -0
- package/dist/server/orchestration.js.map +7 -0
- package/dist/server/pr-evidence.d.ts +2 -0
- package/dist/server/pr-evidence.js +163 -0
- package/dist/server/pr-evidence.js.map +7 -0
- package/dist/server/repo-search.d.ts +1 -0
- package/dist/server/repo-search.js +224 -0
- package/dist/server/repo-search.js.map +7 -0
- package/dist/server/worker-policy.d.ts +2 -0
- package/dist/server/worker-policy.js +177 -0
- package/dist/server/worker-policy.js.map +7 -0
- package/dist/status.d.ts +4 -0
- package/package.json +63 -3
package/dist/index.js
CHANGED
|
@@ -2128,7 +2128,7 @@ var NO_START_MS = 18e4;
|
|
|
2128
2128
|
var STALE_MS = 6e5;
|
|
2129
2129
|
function computeAttention(input) {
|
|
2130
2130
|
const now = Date.now();
|
|
2131
|
-
if (input.completionBlocker) {
|
|
2131
|
+
if (input.completionBlocker && !isSkippedTerminalCompletionBlocker(input.completionBlocker)) {
|
|
2132
2132
|
return { state: "blocked", reason: input.completionBlocker };
|
|
2133
2133
|
}
|
|
2134
2134
|
if (input.finalResult) {
|
|
@@ -2166,6 +2166,9 @@ function computeAttention(input) {
|
|
|
2166
2166
|
return { state: "done", reason: "final result recorded" };
|
|
2167
2167
|
}
|
|
2168
2168
|
if (!input.alive) {
|
|
2169
|
+
if (isAbandonedEmptyWorker(input)) {
|
|
2170
|
+
return { state: "done", reason: "empty abandoned worker record" };
|
|
2171
|
+
}
|
|
2169
2172
|
const classified = classifyExitFailure(input.error);
|
|
2170
2173
|
if (classified) return { state: "blocked", reason: classified.reason };
|
|
2171
2174
|
const salvage = assessExitedWorkerSalvage({
|
|
@@ -2200,6 +2203,19 @@ function computeAttention(input) {
|
|
|
2200
2203
|
}
|
|
2201
2204
|
return { state: "ok", reason: "recent activity" };
|
|
2202
2205
|
}
|
|
2206
|
+
function isSkippedTerminalCompletionBlocker(reason) {
|
|
2207
|
+
const text = reason?.trim();
|
|
2208
|
+
if (!text) return false;
|
|
2209
|
+
return /completion acknowledged but board not advanced/i.test(text) && /task already terminal/i.test(text);
|
|
2210
|
+
}
|
|
2211
|
+
function isAbandonedEmptyWorker(input) {
|
|
2212
|
+
if (input.finalResult) return false;
|
|
2213
|
+
if (input.taskId || input.agentOsId) return false;
|
|
2214
|
+
if (input.stdoutBytes > 0 || (input.stderrBytes ?? 0) > 0 || input.heartbeatBytes > 0) return false;
|
|
2215
|
+
if (input.error?.trim()) return false;
|
|
2216
|
+
if ((input.changedFiles ?? []).some((line) => line.trim())) return false;
|
|
2217
|
+
return /empty worker dir|marked abandoned/i.test(input.reconcileReason ?? "");
|
|
2218
|
+
}
|
|
2203
2219
|
function hasMergedTargetPrReconciliation(value) {
|
|
2204
2220
|
let record3 = null;
|
|
2205
2221
|
if (typeof value === "string") record3 = extractEmbeddedWorkerFinalResultRecord(value);
|
|
@@ -2244,6 +2260,7 @@ function computeWorkerStatus(worker, options = {}) {
|
|
|
2244
2260
|
]);
|
|
2245
2261
|
const error = parsed.error || (!alive && !finalResult ? tailFile(worker.stderrPath, 10).trim() || void 0 : void 0);
|
|
2246
2262
|
const completionBlocker = typeof worker.completionBlocker === "string" && worker.completionBlocker.trim() ? worker.completionBlocker.trim() : null;
|
|
2263
|
+
const effectiveCompletionBlocker = isSkippedTerminalCompletionBlocker(completionBlocker) ? null : completionBlocker;
|
|
2247
2264
|
const landingContract = worker.repairTargetPrUrl ? {
|
|
2248
2265
|
landingOnly: false,
|
|
2249
2266
|
targetPrUrls: [worker.repairTargetPrUrl],
|
|
@@ -2255,6 +2272,7 @@ function computeWorkerStatus(worker, options = {}) {
|
|
|
2255
2272
|
finalResult,
|
|
2256
2273
|
firstEventAt: parsed.firstEventAt,
|
|
2257
2274
|
stdoutBytes,
|
|
2275
|
+
stderrBytes,
|
|
2258
2276
|
heartbeatBytes,
|
|
2259
2277
|
lastActivityAt,
|
|
2260
2278
|
heartbeatBlocker: heartbeat.heartbeatBlocker,
|
|
@@ -2262,12 +2280,15 @@ function computeWorkerStatus(worker, options = {}) {
|
|
|
2262
2280
|
error,
|
|
2263
2281
|
changedFiles,
|
|
2264
2282
|
gitAncestry,
|
|
2265
|
-
completionBlocker,
|
|
2283
|
+
completionBlocker: effectiveCompletionBlocker,
|
|
2266
2284
|
landingContract,
|
|
2267
2285
|
prUrl: worker.repairTargetPrUrl ?? worker.taskPrUrl ?? null,
|
|
2268
|
-
localOnly: worker.localOnly === true
|
|
2286
|
+
localOnly: worker.localOnly === true,
|
|
2287
|
+
taskId: worker.taskId ?? null,
|
|
2288
|
+
agentOsId: worker.agentOsId ?? null,
|
|
2289
|
+
reconcileReason: worker.reconcileReason ?? null
|
|
2269
2290
|
});
|
|
2270
|
-
const workerStatusLabel =
|
|
2291
|
+
const workerStatusLabel = effectiveCompletionBlocker || attention.state === "blocked" ? "blocked" : completionAcknowledged || attention.state === "done" ? "done" : finalResult ? "exited" : alive ? "running" : "exited";
|
|
2271
2292
|
return {
|
|
2272
2293
|
runId: worker.runId,
|
|
2273
2294
|
worker: worker.name,
|
|
@@ -3203,6 +3224,9 @@ function enforceCursorWorkerProvider(input) {
|
|
|
3203
3224
|
if (taskAllowsClaudeWorker(task)) {
|
|
3204
3225
|
return routing;
|
|
3205
3226
|
}
|
|
3227
|
+
if (routing.rule === "explicit:cli" && isClaudeFamilyProvider(routing.provider)) {
|
|
3228
|
+
return routing;
|
|
3229
|
+
}
|
|
3206
3230
|
if (!isClaudeFamilyProvider(routing.provider)) {
|
|
3207
3231
|
return routing;
|
|
3208
3232
|
}
|
|
@@ -4256,7 +4280,7 @@ function resolveWorkerLaunch(input) {
|
|
|
4256
4280
|
if (!input.task || Object.keys(input.task).length === 0) {
|
|
4257
4281
|
return afterCursorPolicy;
|
|
4258
4282
|
}
|
|
4259
|
-
if (afterCursorPolicy.rule === "explicit:model_provider_alias" || afterCursorPolicy.rule === "explicit:model_provider_alias_overrode_provider") {
|
|
4283
|
+
if (afterCursorPolicy.rule === "explicit:model_provider_alias" || afterCursorPolicy.rule === "explicit:model_provider_alias_overrode_provider" || afterCursorPolicy.rule === "explicit:cli") {
|
|
4260
4284
|
return afterCursorPolicy;
|
|
4261
4285
|
}
|
|
4262
4286
|
if (isClaudeFamilyProvider(afterCursorPolicy.provider) && (input.explicitProviderIsOperatorOverride || taskAllowsClaudeWorker(input.task))) {
|
|
@@ -4873,6 +4897,7 @@ function asString(value) {
|
|
|
4873
4897
|
var ADVANCED_OUTCOMES = /* @__PURE__ */ new Set([
|
|
4874
4898
|
"review_scheduled",
|
|
4875
4899
|
"review_already_scheduled",
|
|
4900
|
+
"skipped_terminal_task",
|
|
4876
4901
|
"closed",
|
|
4877
4902
|
"dispatched",
|
|
4878
4903
|
"dispatch_already_done"
|
|
@@ -5220,6 +5245,36 @@ function defaultPrBody(taskId, workerName, runId) {
|
|
|
5220
5245
|
"Opened by orchestrator completion enforcement so production review receives a reviewable artifact."
|
|
5221
5246
|
].filter(Boolean).join("\n");
|
|
5222
5247
|
}
|
|
5248
|
+
function commitDirtyToExistingPr(input) {
|
|
5249
|
+
if (input.snapshot.changedFiles.length === 0) {
|
|
5250
|
+
return {
|
|
5251
|
+
ok: true,
|
|
5252
|
+
prUrl: input.prUrl,
|
|
5253
|
+
headCommit: input.snapshot.headCommit ?? resolveHeadCommit(input.snapshot.worktreePath, input.exec) ?? void 0
|
|
5254
|
+
};
|
|
5255
|
+
}
|
|
5256
|
+
const pushResult = commitAndPushBranch({
|
|
5257
|
+
worktreePath: input.snapshot.worktreePath,
|
|
5258
|
+
branch: input.snapshot.branch,
|
|
5259
|
+
commitMessage: input.commitMessage,
|
|
5260
|
+
hasDirtyFiles: true,
|
|
5261
|
+
exec: input.exec
|
|
5262
|
+
});
|
|
5263
|
+
if (!pushResult.ok) {
|
|
5264
|
+
return {
|
|
5265
|
+
ok: false,
|
|
5266
|
+
reason: `PR-ready handoff blocked: ${pushResult.detail ?? "git commit/push failed"}`,
|
|
5267
|
+
nextAction: "Commit and push the dirty worker changes to the existing PR branch, then rerun `kynver worker complete`."
|
|
5268
|
+
};
|
|
5269
|
+
}
|
|
5270
|
+
return {
|
|
5271
|
+
ok: true,
|
|
5272
|
+
prUrl: input.prUrl,
|
|
5273
|
+
headCommit: pushResult.headCommit ?? input.snapshot.headCommit ?? void 0,
|
|
5274
|
+
committed: pushResult.committed,
|
|
5275
|
+
pushed: pushResult.pushed
|
|
5276
|
+
};
|
|
5277
|
+
}
|
|
5223
5278
|
function ensurePrReadyHandoff(input, exec = defaultPrHandoffExec) {
|
|
5224
5279
|
const prUrlHint = input.prUrlHint ?? extractPrUrlFromText(input.status.finalResult) ?? null;
|
|
5225
5280
|
const snapshot = buildPrHandoffSnapshotFromStatus(input.status, {
|
|
@@ -5243,10 +5298,23 @@ function ensurePrReadyHandoff(input, exec = defaultPrHandoffExec) {
|
|
|
5243
5298
|
snapshot
|
|
5244
5299
|
});
|
|
5245
5300
|
if (!requirement.required) {
|
|
5246
|
-
|
|
5301
|
+
if (prUrlHint) {
|
|
5302
|
+
return commitDirtyToExistingPr({
|
|
5303
|
+
snapshot,
|
|
5304
|
+
prUrl: prUrlHint,
|
|
5305
|
+
commitMessage: `chore(harness): update PR handoff for ${input.worker.name}`,
|
|
5306
|
+
exec
|
|
5307
|
+
});
|
|
5308
|
+
}
|
|
5309
|
+
return { ok: true };
|
|
5247
5310
|
}
|
|
5248
5311
|
if (prUrlHint) {
|
|
5249
|
-
return {
|
|
5312
|
+
return commitDirtyToExistingPr({
|
|
5313
|
+
snapshot,
|
|
5314
|
+
prUrl: prUrlHint,
|
|
5315
|
+
commitMessage: `chore(harness): update PR handoff for ${input.worker.name}`,
|
|
5316
|
+
exec
|
|
5317
|
+
});
|
|
5250
5318
|
}
|
|
5251
5319
|
if (!ghAvailable(exec)) {
|
|
5252
5320
|
const dirty = snapshot.changedFiles.length;
|
|
@@ -5309,11 +5377,12 @@ function ensurePrReadyHandoff(input, exec = defaultPrHandoffExec) {
|
|
|
5309
5377
|
}
|
|
5310
5378
|
const existing = findOpenPrUrl(snapshot.worktreePath, repo, snapshot.branch, exec);
|
|
5311
5379
|
if (existing) {
|
|
5312
|
-
return {
|
|
5313
|
-
|
|
5380
|
+
return commitDirtyToExistingPr({
|
|
5381
|
+
snapshot,
|
|
5314
5382
|
prUrl: existing,
|
|
5315
|
-
|
|
5316
|
-
|
|
5383
|
+
commitMessage: `chore(harness): update PR handoff for ${input.worker.name}`,
|
|
5384
|
+
exec
|
|
5385
|
+
});
|
|
5317
5386
|
}
|
|
5318
5387
|
const hasDirty = snapshot.changedFiles.length > 0;
|
|
5319
5388
|
let committed = false;
|
|
@@ -5723,6 +5792,11 @@ function deriveNextAction(input) {
|
|
|
5723
5792
|
}
|
|
5724
5793
|
return null;
|
|
5725
5794
|
}
|
|
5795
|
+
function isSkippedTerminalCompletionBlocker2(reason) {
|
|
5796
|
+
const text = reason?.trim();
|
|
5797
|
+
if (!text) return false;
|
|
5798
|
+
return /completion acknowledged but board not advanced/i.test(text) && /task already terminal/i.test(text);
|
|
5799
|
+
}
|
|
5726
5800
|
function deriveHandoffState(input) {
|
|
5727
5801
|
if (input.prUrl) return "pr_handoff";
|
|
5728
5802
|
if (input.headCommit) return "commit_handoff";
|
|
@@ -5779,6 +5853,23 @@ async function tryCompleteWorker(args) {
|
|
|
5779
5853
|
if (!forceReplay && shouldReplayHarnessCompletion(worker)) {
|
|
5780
5854
|
clearCompletionBlockerForReplay(worker);
|
|
5781
5855
|
}
|
|
5856
|
+
const skipPrHandoff = args.skipPrHandoff === true || args.skipPrHandoff === "true";
|
|
5857
|
+
if (!skipPrHandoff && worker.dispatched && taskId) {
|
|
5858
|
+
const handoff = ensurePrReadyHandoff({ worker, run, status });
|
|
5859
|
+
if (!handoff.ok) {
|
|
5860
|
+
persistCompletionBlocker(worker, handoff.reason);
|
|
5861
|
+
return {
|
|
5862
|
+
ok: false,
|
|
5863
|
+
reason: handoff.reason,
|
|
5864
|
+
nextAction: handoff.nextAction,
|
|
5865
|
+
completionBlocked: true
|
|
5866
|
+
};
|
|
5867
|
+
}
|
|
5868
|
+
if (handoff.prUrl || handoff.headCommit) {
|
|
5869
|
+
status = computeWorkerStatus(worker, workerStatusOptions(run));
|
|
5870
|
+
status = applyPrHandoffToStatus(status, handoff);
|
|
5871
|
+
}
|
|
5872
|
+
}
|
|
5782
5873
|
const headCommit = status.gitAncestry.headIsAncestorOfBase === false && status.gitAncestry.head ? status.gitAncestry.head : status.headCommit;
|
|
5783
5874
|
if (worker.dispatched) {
|
|
5784
5875
|
const handoff = assessWorktreeCompletionHandoff({
|
|
@@ -5799,22 +5890,6 @@ async function tryCompleteWorker(args) {
|
|
|
5799
5890
|
};
|
|
5800
5891
|
}
|
|
5801
5892
|
}
|
|
5802
|
-
const skipPrHandoff = args.skipPrHandoff === true || args.skipPrHandoff === "true";
|
|
5803
|
-
if (!skipPrHandoff && worker.dispatched && taskId) {
|
|
5804
|
-
const handoff = ensurePrReadyHandoff({ worker, run, status });
|
|
5805
|
-
if (!handoff.ok) {
|
|
5806
|
-
persistCompletionBlocker(worker, handoff.reason);
|
|
5807
|
-
return {
|
|
5808
|
-
ok: false,
|
|
5809
|
-
reason: handoff.reason,
|
|
5810
|
-
nextAction: handoff.nextAction,
|
|
5811
|
-
completionBlocked: true
|
|
5812
|
-
};
|
|
5813
|
-
}
|
|
5814
|
-
if (handoff.prUrl || handoff.headCommit) {
|
|
5815
|
-
status = applyPrHandoffToStatus(status, handoff);
|
|
5816
|
-
}
|
|
5817
|
-
}
|
|
5818
5893
|
const base = resolveBaseUrl(args.baseUrl ? String(args.baseUrl) : void 0);
|
|
5819
5894
|
const explicitSecret = args.secret ? String(args.secret) : void 0;
|
|
5820
5895
|
let secret = await resolveCallbackSecretWithMint(explicitSecret, agentOsId, { baseUrl: base });
|
|
@@ -6020,7 +6095,8 @@ function buildWorkerBoardEntry(input) {
|
|
|
6020
6095
|
headCommit
|
|
6021
6096
|
});
|
|
6022
6097
|
const rawBlocker = worker.completionBlocker;
|
|
6023
|
-
const
|
|
6098
|
+
const rawCompletionBlocker = typeof rawBlocker === "string" && rawBlocker ? rawBlocker : void 0;
|
|
6099
|
+
const completionBlocker = isSkippedTerminalCompletionBlocker2(rawCompletionBlocker) ? void 0 : rawCompletionBlocker;
|
|
6024
6100
|
const boardStatus = completionBlocker ? "blocked" : status.status;
|
|
6025
6101
|
const boardAttention = completionBlocker ? "blocked" : status.attention.state;
|
|
6026
6102
|
const completionResponse = asRecord3(worker.completionResponse);
|
|
@@ -6105,7 +6181,8 @@ function buildWorkerBoardEntry(input) {
|
|
|
6105
6181
|
}
|
|
6106
6182
|
function isMetadataTerminalDone(worker) {
|
|
6107
6183
|
const status = typeof worker.status === "string" ? worker.status : "";
|
|
6108
|
-
const
|
|
6184
|
+
const rawCompletionBlocker = typeof worker.completionBlocker === "string" && worker.completionBlocker.trim().length > 0 ? worker.completionBlocker : void 0;
|
|
6185
|
+
const completionBlocker = rawCompletionBlocker && !isSkippedTerminalCompletionBlocker2(rawCompletionBlocker);
|
|
6109
6186
|
if (completionBlocker) return false;
|
|
6110
6187
|
if (typeof worker.completionReportedAt === "string" && worker.completionReportedAt.trim()) return true;
|
|
6111
6188
|
if (worker.completionOutcome === "acknowledged") return true;
|
|
@@ -7367,6 +7444,11 @@ function readAdmissionExhaustion(result) {
|
|
|
7367
7444
|
if (!raw || typeof raw !== "object") return null;
|
|
7368
7445
|
return raw;
|
|
7369
7446
|
}
|
|
7447
|
+
function readDispatchSkipDrain(result) {
|
|
7448
|
+
const raw = result.dispatchSkipDrain;
|
|
7449
|
+
if (!raw || typeof raw !== "object") return null;
|
|
7450
|
+
return raw;
|
|
7451
|
+
}
|
|
7370
7452
|
function readHarnessWorkerContext(decision) {
|
|
7371
7453
|
const raw = decision.harnessWorkerContext;
|
|
7372
7454
|
if (!raw || typeof raw !== "object") return null;
|
|
@@ -7530,6 +7612,7 @@ async function dispatchRun(args) {
|
|
|
7530
7612
|
const result = first.result;
|
|
7531
7613
|
if (dryRun) {
|
|
7532
7614
|
const admissionExhaustion2 = readAdmissionExhaustion(result);
|
|
7615
|
+
const dispatchSkipDrain2 = readDispatchSkipDrain(result);
|
|
7533
7616
|
const summary2 = {
|
|
7534
7617
|
runId: run.id,
|
|
7535
7618
|
agentOsId,
|
|
@@ -7548,7 +7631,8 @@ async function dispatchRun(args) {
|
|
|
7548
7631
|
pagesScanned: result.pagesScanned ?? null,
|
|
7549
7632
|
candidatesExhausted: result.candidatesExhausted ?? null,
|
|
7550
7633
|
capacityIdle: admissionExhaustion2?.capacityIdle === true,
|
|
7551
|
-
admissionExhaustion: admissionExhaustion2
|
|
7634
|
+
admissionExhaustion: admissionExhaustion2,
|
|
7635
|
+
dispatchSkipDrain: dispatchSkipDrain2
|
|
7552
7636
|
};
|
|
7553
7637
|
if (pipeline) return { ok: true, ...summary2 };
|
|
7554
7638
|
console.log(JSON.stringify(summary2, null, 2));
|
|
@@ -7738,13 +7822,15 @@ async function dispatchRun(args) {
|
|
|
7738
7822
|
}
|
|
7739
7823
|
const startedCount = outcomes.filter((o) => o.started).length;
|
|
7740
7824
|
const admissionExhaustion = readAdmissionExhaustion(result);
|
|
7741
|
-
const
|
|
7825
|
+
const dispatchSkipDrain = readDispatchSkipDrain(result);
|
|
7826
|
+
const capacityIdle = startedCount === 0 && (admissionExhaustion?.capacityIdle === true || Number(result.resourceGate?.slotsAvailable) > 0);
|
|
7742
7827
|
if (capacityIdle && admissionExhaustion?.summary) {
|
|
7743
7828
|
const retryCeiling = admissionExhaustion.skipReasonCounts?.retry_ceiling_exceeded ?? 0;
|
|
7744
|
-
const recovery = admissionExhaustion.overAttemptIdleRecovery;
|
|
7829
|
+
const recovery = result.overAttemptIdleRecovery ?? admissionExhaustion.overAttemptIdleRecovery;
|
|
7745
7830
|
const recoveryNote = recovery?.attempted === true ? `; over_attempt_recovery minted=${recovery.minted ?? 0} started=${recovery.started ?? 0}` : retryCeiling > 0 ? "; over_attempt_recovery not attempted" : "";
|
|
7831
|
+
const drainNote = dispatchSkipDrain ? `; dispatch_skip_drain scanned=${dispatchSkipDrain.scanned ?? 0} advanced=${dispatchSkipDrain.advanced ?? 0}` : "";
|
|
7746
7832
|
console.error(
|
|
7747
|
-
`[dispatch] ${admissionExhaustion.summary}${retryCeiling > 0 ? `; retry_ceiling_exceeded=${retryCeiling}` : ""}${recoveryNote}`
|
|
7833
|
+
`[dispatch] ${admissionExhaustion.summary}${retryCeiling > 0 ? `; retry_ceiling_exceeded=${retryCeiling}` : ""}${recoveryNote}${drainNote}`
|
|
7748
7834
|
);
|
|
7749
7835
|
}
|
|
7750
7836
|
const summary = {
|
|
@@ -7755,6 +7841,7 @@ async function dispatchRun(args) {
|
|
|
7755
7841
|
startedCount,
|
|
7756
7842
|
capacityIdle,
|
|
7757
7843
|
admissionExhaustion,
|
|
7844
|
+
dispatchSkipDrain,
|
|
7758
7845
|
outcomes,
|
|
7759
7846
|
skipped: skipped.map((d) => ({
|
|
7760
7847
|
taskId: d.task.id,
|
|
@@ -9714,30 +9801,7 @@ function indexedWorktreeHasMaterialChanges(entry) {
|
|
|
9714
9801
|
return materialWorktreeChanges2(gitStatusShort(entry.worktreePath)).length > 0;
|
|
9715
9802
|
}
|
|
9716
9803
|
|
|
9717
|
-
// src/cleanup-
|
|
9718
|
-
function deriveRunTerminal(indexed, ctx) {
|
|
9719
|
-
if (ctx) return ctx.runTerminalCache.derive(indexed.run);
|
|
9720
|
-
return deriveTerminalRunStatus(indexed.run);
|
|
9721
|
-
}
|
|
9722
|
-
function isWorkerProcessLive(indexed) {
|
|
9723
|
-
if (isPidAlive(indexed.worker.pid)) return true;
|
|
9724
|
-
if (!indexed.worker.pid) return indexedWorktreeStatus(indexed).alive;
|
|
9725
|
-
return false;
|
|
9726
|
-
}
|
|
9727
|
-
function isRunStaleActive(indexed, ctx) {
|
|
9728
|
-
if (TERMINAL_RUN_STATUSES.has(indexed.run.status)) return false;
|
|
9729
|
-
return deriveRunTerminal(indexed, ctx) !== null;
|
|
9730
|
-
}
|
|
9731
|
-
function runBlocksWorktreeRemoval(indexed, ctx) {
|
|
9732
|
-
if (isWorkerProcessLive(indexed)) return true;
|
|
9733
|
-
if (indexed.worker.completionBlocker) return true;
|
|
9734
|
-
if (TERMINAL_RUN_STATUSES.has(indexed.run.status)) return false;
|
|
9735
|
-
if (isRunStaleActive(indexed, ctx)) return false;
|
|
9736
|
-
if (!isFinishedWorkerStatus(indexedWorktreeStatus(indexed))) return true;
|
|
9737
|
-
return deriveRunTerminal(indexed, ctx) === null;
|
|
9738
|
-
}
|
|
9739
|
-
|
|
9740
|
-
// src/cleanup-guards.ts
|
|
9804
|
+
// src/cleanup-worktree-salvage.ts
|
|
9741
9805
|
function prUrlFromFinalResult(finalResult) {
|
|
9742
9806
|
if (typeof finalResult === "string") {
|
|
9743
9807
|
const match = finalResult.match(/https:\/\/github\.com\/[^\s]+\/pull\/\d+/i);
|
|
@@ -9762,12 +9826,63 @@ function isPrOrUnmergedWork(status) {
|
|
|
9762
9826
|
if (status.changedFiles.length > 0 && status.finalResult) return true;
|
|
9763
9827
|
return false;
|
|
9764
9828
|
}
|
|
9829
|
+
|
|
9830
|
+
// src/cleanup-completion-blocker.ts
|
|
9831
|
+
function completionBlockerBlocksWorktreeRemoval(indexed, status) {
|
|
9832
|
+
const blocker = typeof indexed.worker.completionBlocker === "string" ? indexed.worker.completionBlocker.trim() : "";
|
|
9833
|
+
if (!blocker) return false;
|
|
9834
|
+
if (isWorkerProcessLive(indexed)) return true;
|
|
9835
|
+
const resolved = status ?? indexedWorktreeStatus(indexed);
|
|
9836
|
+
if (!isFinishedWorkerStatus(resolved)) return true;
|
|
9837
|
+
if (isPrOrUnmergedWork(resolved)) return true;
|
|
9838
|
+
if (materialWorktreeChanges2(resolved.changedFiles).length > 0) return true;
|
|
9839
|
+
const landing = assessWorkerLanding({
|
|
9840
|
+
finalResult: resolved.finalResult,
|
|
9841
|
+
changedFiles: resolved.changedFiles,
|
|
9842
|
+
gitAncestry: resolved.gitAncestry,
|
|
9843
|
+
prUrl: prUrlFromFinalResult(resolved.finalResult)
|
|
9844
|
+
});
|
|
9845
|
+
if (landing.blocked) return true;
|
|
9846
|
+
return false;
|
|
9847
|
+
}
|
|
9848
|
+
|
|
9849
|
+
// src/cleanup-run-liveness.ts
|
|
9850
|
+
function deriveRunTerminal(indexed, ctx) {
|
|
9851
|
+
if (ctx) return ctx.runTerminalCache.derive(indexed.run);
|
|
9852
|
+
return deriveTerminalRunStatus(indexed.run);
|
|
9853
|
+
}
|
|
9854
|
+
function isWorkerProcessLive(indexed) {
|
|
9855
|
+
if (isPidAlive(indexed.worker.pid)) return true;
|
|
9856
|
+
if (!indexed.worker.pid) return indexedWorktreeStatus(indexed).alive;
|
|
9857
|
+
return false;
|
|
9858
|
+
}
|
|
9859
|
+
function isRunStaleActive(indexed, ctx) {
|
|
9860
|
+
if (TERMINAL_RUN_STATUSES.has(indexed.run.status)) return false;
|
|
9861
|
+
return deriveRunTerminal(indexed, ctx) !== null;
|
|
9862
|
+
}
|
|
9863
|
+
function runBlocksWorktreeRemoval(indexed, ctx) {
|
|
9864
|
+
if (isWorkerProcessLive(indexed)) return true;
|
|
9865
|
+
const status = indexedWorktreeStatus(indexed);
|
|
9866
|
+
if (completionBlockerBlocksWorktreeRemoval(indexed, status)) return true;
|
|
9867
|
+
if (isFinishedWorkerStatus(status)) return false;
|
|
9868
|
+
if (TERMINAL_RUN_STATUSES.has(indexed.run.status)) return false;
|
|
9869
|
+
if (isRunStaleActive(indexed, ctx)) return false;
|
|
9870
|
+
return deriveRunTerminal(indexed, ctx) === null;
|
|
9871
|
+
}
|
|
9872
|
+
|
|
9873
|
+
// src/cleanup-guards.ts
|
|
9765
9874
|
function effectiveWorktreeAgeMs(input) {
|
|
9766
9875
|
const { indexed, includeOrphans, worktreesAgeMs, terminalWorktreesAgeMs } = input;
|
|
9767
9876
|
if (!indexed) return includeOrphans ? terminalWorktreesAgeMs : worktreesAgeMs;
|
|
9768
9877
|
if (TERMINAL_RUN_STATUSES.has(indexed.run.status)) {
|
|
9769
9878
|
return terminalWorktreesAgeMs;
|
|
9770
9879
|
}
|
|
9880
|
+
if (input.liveness && isRunStaleActive(indexed, input.liveness)) {
|
|
9881
|
+
return terminalWorktreesAgeMs;
|
|
9882
|
+
}
|
|
9883
|
+
if (input.liveness && isFinishedWorkerStatus(indexedWorktreeStatus(indexed)) && !isWorkerProcessLive(indexed)) {
|
|
9884
|
+
return terminalWorktreesAgeMs;
|
|
9885
|
+
}
|
|
9771
9886
|
return worktreesAgeMs;
|
|
9772
9887
|
}
|
|
9773
9888
|
function skipWorktreeRemoval(input) {
|
|
@@ -9781,7 +9896,7 @@ function skipWorktreeRemoval(input) {
|
|
|
9781
9896
|
if (ageThresholdMs > 0 && ageMs < ageThresholdMs) return "below_age_threshold";
|
|
9782
9897
|
const status = indexedWorktreeStatus(indexed);
|
|
9783
9898
|
if (isWorkerProcessLive(indexed)) return "active_worker";
|
|
9784
|
-
if (indexed
|
|
9899
|
+
if (completionBlockerBlocksWorktreeRemoval(indexed, status)) return "completion_blocked";
|
|
9785
9900
|
if (runBlocksWorktreeRemoval(indexed, input.liveness)) return "run_still_active";
|
|
9786
9901
|
if (!isFinishedWorkerStatus(status)) return "run_still_active";
|
|
9787
9902
|
if (isPrOrUnmergedWork(status)) return "pr_or_unmerged_commits";
|
|
@@ -9937,7 +10052,9 @@ function skipRunDirectoryRemoval(input) {
|
|
|
9937
10052
|
}
|
|
9938
10053
|
if (!runDirectoryIsEmpty(runPath)) return "run_still_active";
|
|
9939
10054
|
const run = loadRunStatus(harnessRoot, runId);
|
|
9940
|
-
if (run && !TERMINAL_RUN_STATUSES.has(run.status))
|
|
10055
|
+
if (run && !TERMINAL_RUN_STATUSES.has(run.status)) {
|
|
10056
|
+
if (!deriveTerminalRunStatus(run)) return "run_still_active";
|
|
10057
|
+
}
|
|
9941
10058
|
if (runDirectoriesAgeMs > 0 && ageMs < runDirectoriesAgeMs) return "below_age_threshold";
|
|
9942
10059
|
return null;
|
|
9943
10060
|
}
|
|
@@ -10902,14 +11019,14 @@ function runHarnessCleanup(options = {}) {
|
|
|
10902
11019
|
const paths = resolvePaths(options);
|
|
10903
11020
|
emitCleanupProgress("scan", `${paths.scanRoots.length} harness root(s)`);
|
|
10904
11021
|
const activeGuards = collectActiveWorktreeGuards(paths.scanRoots, paths.now);
|
|
10905
|
-
emitCleanupProgress("index", "building worktree index");
|
|
10906
|
-
const index = mergeWorktreeIndexes(paths.scanRoots);
|
|
10907
|
-
emitCleanupProgress("index", `${index.size} indexed worktree(s)`);
|
|
10908
|
-
const liveness = { runTerminalCache: new CleanupRunTerminalCache() };
|
|
10909
11022
|
const finalizedRuns = retention.finalizeStaleRuns ? finalizeStaleRuns().map((f) => ({ runId: f.runId, from: f.from, to: f.to })) : [];
|
|
10910
11023
|
if (finalizedRuns.length > 0) {
|
|
10911
11024
|
emitCleanupProgress("finalize", `${finalizedRuns.length} stale run(s) marked terminal`);
|
|
10912
11025
|
}
|
|
11026
|
+
emitCleanupProgress("index", "building worktree index");
|
|
11027
|
+
const index = mergeWorktreeIndexes(paths.scanRoots);
|
|
11028
|
+
emitCleanupProgress("index", `${index.size} indexed worktree(s)`);
|
|
11029
|
+
const liveness = { runTerminalCache: new CleanupRunTerminalCache() };
|
|
10913
11030
|
const skips = [];
|
|
10914
11031
|
const actions = [];
|
|
10915
11032
|
const processedPaths = /* @__PURE__ */ new Set();
|
|
@@ -14919,7 +15036,7 @@ async function runLandingMaintainerLaneTick(args) {
|
|
|
14919
15036
|
const agentOsId = String(required(String(args.agentOsId || ""), "--agent-os-id"));
|
|
14920
15037
|
const repoSlug = String(args.repo || LANDING_MAINTAINER_LANE_SPEC.defaultRepo).trim();
|
|
14921
15038
|
const fleet = args.fleet === true || args.fleet === "true";
|
|
14922
|
-
const execute = args.execute
|
|
15039
|
+
const execute = args.execute === true || args.execute === "true";
|
|
14923
15040
|
const runId = args.run ? String(args.run) : void 0;
|
|
14924
15041
|
const resourceGate = observeRunnerResourceGate({
|
|
14925
15042
|
runId: runId ?? "fleet-lane-tick"
|