@kynver-app/runtime 0.1.103 → 0.1.106
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 +171 -62
- package/dist/cli.js.map +4 -4
- package/dist/index.js +172 -63
- 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/dist/worker-persona-catalog.js +2 -2
- package/dist/worker-persona-catalog.js.map +2 -2
- package/package.json +63 -3
package/dist/index.js
CHANGED
|
@@ -350,7 +350,9 @@ function redactHomePath(value) {
|
|
|
350
350
|
if (resolved.startsWith(`${home}${path2.sep}`)) {
|
|
351
351
|
return `~/${path2.relative(home, resolved).split(path2.sep).join("/")}`;
|
|
352
352
|
}
|
|
353
|
-
|
|
353
|
+
const posix = resolved.replace(/\\/g, "/");
|
|
354
|
+
const redacted = posix.replace(/^\/home\/[^/]+(?=\/|$)/, "~").replace(/^\/Users\/[^/]+(?=\/|$)/, "~").replace(/^[A-Za-z]:\/home\/[^/]+(?=\/|$)/i, "~").replace(/^[A-Za-z]:\/Users\/[^/]+(?=\/|$)/i, "~");
|
|
355
|
+
return redacted;
|
|
354
356
|
}
|
|
355
357
|
function displayUserPath(value) {
|
|
356
358
|
return redactHomePath(value);
|
|
@@ -2128,7 +2130,7 @@ var NO_START_MS = 18e4;
|
|
|
2128
2130
|
var STALE_MS = 6e5;
|
|
2129
2131
|
function computeAttention(input) {
|
|
2130
2132
|
const now = Date.now();
|
|
2131
|
-
if (input.completionBlocker) {
|
|
2133
|
+
if (input.completionBlocker && !isSkippedTerminalCompletionBlocker(input.completionBlocker)) {
|
|
2132
2134
|
return { state: "blocked", reason: input.completionBlocker };
|
|
2133
2135
|
}
|
|
2134
2136
|
if (input.finalResult) {
|
|
@@ -2166,6 +2168,9 @@ function computeAttention(input) {
|
|
|
2166
2168
|
return { state: "done", reason: "final result recorded" };
|
|
2167
2169
|
}
|
|
2168
2170
|
if (!input.alive) {
|
|
2171
|
+
if (isAbandonedEmptyWorker(input)) {
|
|
2172
|
+
return { state: "done", reason: "empty abandoned worker record" };
|
|
2173
|
+
}
|
|
2169
2174
|
const classified = classifyExitFailure(input.error);
|
|
2170
2175
|
if (classified) return { state: "blocked", reason: classified.reason };
|
|
2171
2176
|
const salvage = assessExitedWorkerSalvage({
|
|
@@ -2200,6 +2205,19 @@ function computeAttention(input) {
|
|
|
2200
2205
|
}
|
|
2201
2206
|
return { state: "ok", reason: "recent activity" };
|
|
2202
2207
|
}
|
|
2208
|
+
function isSkippedTerminalCompletionBlocker(reason) {
|
|
2209
|
+
const text = reason?.trim();
|
|
2210
|
+
if (!text) return false;
|
|
2211
|
+
return /completion acknowledged but board not advanced/i.test(text) && /task already terminal/i.test(text);
|
|
2212
|
+
}
|
|
2213
|
+
function isAbandonedEmptyWorker(input) {
|
|
2214
|
+
if (input.finalResult) return false;
|
|
2215
|
+
if (input.taskId || input.agentOsId) return false;
|
|
2216
|
+
if (input.stdoutBytes > 0 || (input.stderrBytes ?? 0) > 0 || input.heartbeatBytes > 0) return false;
|
|
2217
|
+
if (input.error?.trim()) return false;
|
|
2218
|
+
if ((input.changedFiles ?? []).some((line) => line.trim())) return false;
|
|
2219
|
+
return /empty worker dir|marked abandoned/i.test(input.reconcileReason ?? "");
|
|
2220
|
+
}
|
|
2203
2221
|
function hasMergedTargetPrReconciliation(value) {
|
|
2204
2222
|
let record3 = null;
|
|
2205
2223
|
if (typeof value === "string") record3 = extractEmbeddedWorkerFinalResultRecord(value);
|
|
@@ -2244,6 +2262,7 @@ function computeWorkerStatus(worker, options = {}) {
|
|
|
2244
2262
|
]);
|
|
2245
2263
|
const error = parsed.error || (!alive && !finalResult ? tailFile(worker.stderrPath, 10).trim() || void 0 : void 0);
|
|
2246
2264
|
const completionBlocker = typeof worker.completionBlocker === "string" && worker.completionBlocker.trim() ? worker.completionBlocker.trim() : null;
|
|
2265
|
+
const effectiveCompletionBlocker = isSkippedTerminalCompletionBlocker(completionBlocker) ? null : completionBlocker;
|
|
2247
2266
|
const landingContract = worker.repairTargetPrUrl ? {
|
|
2248
2267
|
landingOnly: false,
|
|
2249
2268
|
targetPrUrls: [worker.repairTargetPrUrl],
|
|
@@ -2255,6 +2274,7 @@ function computeWorkerStatus(worker, options = {}) {
|
|
|
2255
2274
|
finalResult,
|
|
2256
2275
|
firstEventAt: parsed.firstEventAt,
|
|
2257
2276
|
stdoutBytes,
|
|
2277
|
+
stderrBytes,
|
|
2258
2278
|
heartbeatBytes,
|
|
2259
2279
|
lastActivityAt,
|
|
2260
2280
|
heartbeatBlocker: heartbeat.heartbeatBlocker,
|
|
@@ -2262,12 +2282,15 @@ function computeWorkerStatus(worker, options = {}) {
|
|
|
2262
2282
|
error,
|
|
2263
2283
|
changedFiles,
|
|
2264
2284
|
gitAncestry,
|
|
2265
|
-
completionBlocker,
|
|
2285
|
+
completionBlocker: effectiveCompletionBlocker,
|
|
2266
2286
|
landingContract,
|
|
2267
2287
|
prUrl: worker.repairTargetPrUrl ?? worker.taskPrUrl ?? null,
|
|
2268
|
-
localOnly: worker.localOnly === true
|
|
2288
|
+
localOnly: worker.localOnly === true,
|
|
2289
|
+
taskId: worker.taskId ?? null,
|
|
2290
|
+
agentOsId: worker.agentOsId ?? null,
|
|
2291
|
+
reconcileReason: worker.reconcileReason ?? null
|
|
2269
2292
|
});
|
|
2270
|
-
const workerStatusLabel =
|
|
2293
|
+
const workerStatusLabel = effectiveCompletionBlocker || attention.state === "blocked" ? "blocked" : completionAcknowledged || attention.state === "done" ? "done" : finalResult ? "exited" : alive ? "running" : "exited";
|
|
2271
2294
|
return {
|
|
2272
2295
|
runId: worker.runId,
|
|
2273
2296
|
worker: worker.name,
|
|
@@ -3027,14 +3050,14 @@ var WORKER_PERSONA_CATALOG = [
|
|
|
3027
3050
|
{
|
|
3028
3051
|
slug: "lorentz",
|
|
3029
3052
|
displayName: "Lorentz",
|
|
3030
|
-
description: "
|
|
3053
|
+
description: "Deep/adversarial review lane expert for risk, correctness, and safety gates. Run adversarial review and validation gating.",
|
|
3031
3054
|
dispatchLane: "review",
|
|
3032
3055
|
defaultRoleLane: "report_reviewer"
|
|
3033
3056
|
},
|
|
3034
3057
|
{
|
|
3035
3058
|
slug: "dalton",
|
|
3036
3059
|
displayName: "Dalton",
|
|
3037
|
-
description: "Landing
|
|
3060
|
+
description: "Landing-only \u2014 merge-ready handoff and final verification evidence; no implementation ownership.",
|
|
3038
3061
|
dispatchLane: "landing",
|
|
3039
3062
|
defaultRoleLane: "implementer"
|
|
3040
3063
|
}
|
|
@@ -3203,6 +3226,9 @@ function enforceCursorWorkerProvider(input) {
|
|
|
3203
3226
|
if (taskAllowsClaudeWorker(task)) {
|
|
3204
3227
|
return routing;
|
|
3205
3228
|
}
|
|
3229
|
+
if (routing.rule === "explicit:cli" && isClaudeFamilyProvider(routing.provider)) {
|
|
3230
|
+
return routing;
|
|
3231
|
+
}
|
|
3206
3232
|
if (!isClaudeFamilyProvider(routing.provider)) {
|
|
3207
3233
|
return routing;
|
|
3208
3234
|
}
|
|
@@ -4256,7 +4282,7 @@ function resolveWorkerLaunch(input) {
|
|
|
4256
4282
|
if (!input.task || Object.keys(input.task).length === 0) {
|
|
4257
4283
|
return afterCursorPolicy;
|
|
4258
4284
|
}
|
|
4259
|
-
if (afterCursorPolicy.rule === "explicit:model_provider_alias" || afterCursorPolicy.rule === "explicit:model_provider_alias_overrode_provider") {
|
|
4285
|
+
if (afterCursorPolicy.rule === "explicit:model_provider_alias" || afterCursorPolicy.rule === "explicit:model_provider_alias_overrode_provider" || afterCursorPolicy.rule === "explicit:cli") {
|
|
4260
4286
|
return afterCursorPolicy;
|
|
4261
4287
|
}
|
|
4262
4288
|
if (isClaudeFamilyProvider(afterCursorPolicy.provider) && (input.explicitProviderIsOperatorOverride || taskAllowsClaudeWorker(input.task))) {
|
|
@@ -4873,6 +4899,7 @@ function asString(value) {
|
|
|
4873
4899
|
var ADVANCED_OUTCOMES = /* @__PURE__ */ new Set([
|
|
4874
4900
|
"review_scheduled",
|
|
4875
4901
|
"review_already_scheduled",
|
|
4902
|
+
"skipped_terminal_task",
|
|
4876
4903
|
"closed",
|
|
4877
4904
|
"dispatched",
|
|
4878
4905
|
"dispatch_already_done"
|
|
@@ -5220,6 +5247,36 @@ function defaultPrBody(taskId, workerName, runId) {
|
|
|
5220
5247
|
"Opened by orchestrator completion enforcement so production review receives a reviewable artifact."
|
|
5221
5248
|
].filter(Boolean).join("\n");
|
|
5222
5249
|
}
|
|
5250
|
+
function commitDirtyToExistingPr(input) {
|
|
5251
|
+
if (input.snapshot.changedFiles.length === 0) {
|
|
5252
|
+
return {
|
|
5253
|
+
ok: true,
|
|
5254
|
+
prUrl: input.prUrl,
|
|
5255
|
+
headCommit: input.snapshot.headCommit ?? resolveHeadCommit(input.snapshot.worktreePath, input.exec) ?? void 0
|
|
5256
|
+
};
|
|
5257
|
+
}
|
|
5258
|
+
const pushResult = commitAndPushBranch({
|
|
5259
|
+
worktreePath: input.snapshot.worktreePath,
|
|
5260
|
+
branch: input.snapshot.branch,
|
|
5261
|
+
commitMessage: input.commitMessage,
|
|
5262
|
+
hasDirtyFiles: true,
|
|
5263
|
+
exec: input.exec
|
|
5264
|
+
});
|
|
5265
|
+
if (!pushResult.ok) {
|
|
5266
|
+
return {
|
|
5267
|
+
ok: false,
|
|
5268
|
+
reason: `PR-ready handoff blocked: ${pushResult.detail ?? "git commit/push failed"}`,
|
|
5269
|
+
nextAction: "Commit and push the dirty worker changes to the existing PR branch, then rerun `kynver worker complete`."
|
|
5270
|
+
};
|
|
5271
|
+
}
|
|
5272
|
+
return {
|
|
5273
|
+
ok: true,
|
|
5274
|
+
prUrl: input.prUrl,
|
|
5275
|
+
headCommit: pushResult.headCommit ?? input.snapshot.headCommit ?? void 0,
|
|
5276
|
+
committed: pushResult.committed,
|
|
5277
|
+
pushed: pushResult.pushed
|
|
5278
|
+
};
|
|
5279
|
+
}
|
|
5223
5280
|
function ensurePrReadyHandoff(input, exec = defaultPrHandoffExec) {
|
|
5224
5281
|
const prUrlHint = input.prUrlHint ?? extractPrUrlFromText(input.status.finalResult) ?? null;
|
|
5225
5282
|
const snapshot = buildPrHandoffSnapshotFromStatus(input.status, {
|
|
@@ -5243,10 +5300,23 @@ function ensurePrReadyHandoff(input, exec = defaultPrHandoffExec) {
|
|
|
5243
5300
|
snapshot
|
|
5244
5301
|
});
|
|
5245
5302
|
if (!requirement.required) {
|
|
5246
|
-
|
|
5303
|
+
if (prUrlHint) {
|
|
5304
|
+
return commitDirtyToExistingPr({
|
|
5305
|
+
snapshot,
|
|
5306
|
+
prUrl: prUrlHint,
|
|
5307
|
+
commitMessage: `chore(harness): update PR handoff for ${input.worker.name}`,
|
|
5308
|
+
exec
|
|
5309
|
+
});
|
|
5310
|
+
}
|
|
5311
|
+
return { ok: true };
|
|
5247
5312
|
}
|
|
5248
5313
|
if (prUrlHint) {
|
|
5249
|
-
return {
|
|
5314
|
+
return commitDirtyToExistingPr({
|
|
5315
|
+
snapshot,
|
|
5316
|
+
prUrl: prUrlHint,
|
|
5317
|
+
commitMessage: `chore(harness): update PR handoff for ${input.worker.name}`,
|
|
5318
|
+
exec
|
|
5319
|
+
});
|
|
5250
5320
|
}
|
|
5251
5321
|
if (!ghAvailable(exec)) {
|
|
5252
5322
|
const dirty = snapshot.changedFiles.length;
|
|
@@ -5309,11 +5379,12 @@ function ensurePrReadyHandoff(input, exec = defaultPrHandoffExec) {
|
|
|
5309
5379
|
}
|
|
5310
5380
|
const existing = findOpenPrUrl(snapshot.worktreePath, repo, snapshot.branch, exec);
|
|
5311
5381
|
if (existing) {
|
|
5312
|
-
return {
|
|
5313
|
-
|
|
5382
|
+
return commitDirtyToExistingPr({
|
|
5383
|
+
snapshot,
|
|
5314
5384
|
prUrl: existing,
|
|
5315
|
-
|
|
5316
|
-
|
|
5385
|
+
commitMessage: `chore(harness): update PR handoff for ${input.worker.name}`,
|
|
5386
|
+
exec
|
|
5387
|
+
});
|
|
5317
5388
|
}
|
|
5318
5389
|
const hasDirty = snapshot.changedFiles.length > 0;
|
|
5319
5390
|
let committed = false;
|
|
@@ -5723,6 +5794,11 @@ function deriveNextAction(input) {
|
|
|
5723
5794
|
}
|
|
5724
5795
|
return null;
|
|
5725
5796
|
}
|
|
5797
|
+
function isSkippedTerminalCompletionBlocker2(reason) {
|
|
5798
|
+
const text = reason?.trim();
|
|
5799
|
+
if (!text) return false;
|
|
5800
|
+
return /completion acknowledged but board not advanced/i.test(text) && /task already terminal/i.test(text);
|
|
5801
|
+
}
|
|
5726
5802
|
function deriveHandoffState(input) {
|
|
5727
5803
|
if (input.prUrl) return "pr_handoff";
|
|
5728
5804
|
if (input.headCommit) return "commit_handoff";
|
|
@@ -5779,6 +5855,23 @@ async function tryCompleteWorker(args) {
|
|
|
5779
5855
|
if (!forceReplay && shouldReplayHarnessCompletion(worker)) {
|
|
5780
5856
|
clearCompletionBlockerForReplay(worker);
|
|
5781
5857
|
}
|
|
5858
|
+
const skipPrHandoff = args.skipPrHandoff === true || args.skipPrHandoff === "true";
|
|
5859
|
+
if (!skipPrHandoff && worker.dispatched && taskId) {
|
|
5860
|
+
const handoff = ensurePrReadyHandoff({ worker, run, status });
|
|
5861
|
+
if (!handoff.ok) {
|
|
5862
|
+
persistCompletionBlocker(worker, handoff.reason);
|
|
5863
|
+
return {
|
|
5864
|
+
ok: false,
|
|
5865
|
+
reason: handoff.reason,
|
|
5866
|
+
nextAction: handoff.nextAction,
|
|
5867
|
+
completionBlocked: true
|
|
5868
|
+
};
|
|
5869
|
+
}
|
|
5870
|
+
if (handoff.prUrl || handoff.headCommit) {
|
|
5871
|
+
status = computeWorkerStatus(worker, workerStatusOptions(run));
|
|
5872
|
+
status = applyPrHandoffToStatus(status, handoff);
|
|
5873
|
+
}
|
|
5874
|
+
}
|
|
5782
5875
|
const headCommit = status.gitAncestry.headIsAncestorOfBase === false && status.gitAncestry.head ? status.gitAncestry.head : status.headCommit;
|
|
5783
5876
|
if (worker.dispatched) {
|
|
5784
5877
|
const handoff = assessWorktreeCompletionHandoff({
|
|
@@ -5799,22 +5892,6 @@ async function tryCompleteWorker(args) {
|
|
|
5799
5892
|
};
|
|
5800
5893
|
}
|
|
5801
5894
|
}
|
|
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
5895
|
const base = resolveBaseUrl(args.baseUrl ? String(args.baseUrl) : void 0);
|
|
5819
5896
|
const explicitSecret = args.secret ? String(args.secret) : void 0;
|
|
5820
5897
|
let secret = await resolveCallbackSecretWithMint(explicitSecret, agentOsId, { baseUrl: base });
|
|
@@ -6020,7 +6097,8 @@ function buildWorkerBoardEntry(input) {
|
|
|
6020
6097
|
headCommit
|
|
6021
6098
|
});
|
|
6022
6099
|
const rawBlocker = worker.completionBlocker;
|
|
6023
|
-
const
|
|
6100
|
+
const rawCompletionBlocker = typeof rawBlocker === "string" && rawBlocker ? rawBlocker : void 0;
|
|
6101
|
+
const completionBlocker = isSkippedTerminalCompletionBlocker2(rawCompletionBlocker) ? void 0 : rawCompletionBlocker;
|
|
6024
6102
|
const boardStatus = completionBlocker ? "blocked" : status.status;
|
|
6025
6103
|
const boardAttention = completionBlocker ? "blocked" : status.attention.state;
|
|
6026
6104
|
const completionResponse = asRecord3(worker.completionResponse);
|
|
@@ -6105,7 +6183,8 @@ function buildWorkerBoardEntry(input) {
|
|
|
6105
6183
|
}
|
|
6106
6184
|
function isMetadataTerminalDone(worker) {
|
|
6107
6185
|
const status = typeof worker.status === "string" ? worker.status : "";
|
|
6108
|
-
const
|
|
6186
|
+
const rawCompletionBlocker = typeof worker.completionBlocker === "string" && worker.completionBlocker.trim().length > 0 ? worker.completionBlocker : void 0;
|
|
6187
|
+
const completionBlocker = rawCompletionBlocker && !isSkippedTerminalCompletionBlocker2(rawCompletionBlocker);
|
|
6109
6188
|
if (completionBlocker) return false;
|
|
6110
6189
|
if (typeof worker.completionReportedAt === "string" && worker.completionReportedAt.trim()) return true;
|
|
6111
6190
|
if (worker.completionOutcome === "acknowledged") return true;
|
|
@@ -8135,7 +8214,7 @@ function expandHomePath2(filePath) {
|
|
|
8135
8214
|
function discoverProductionDbEnvFilePaths(options = {}) {
|
|
8136
8215
|
const env = options.env ?? process.env;
|
|
8137
8216
|
const cwd = options.cwd ?? process.cwd();
|
|
8138
|
-
const home = options.homeDir ?? homedir12();
|
|
8217
|
+
const home = options.homeDir ?? (env.HOME?.trim() || env.USERPROFILE?.trim() || homedir12());
|
|
8139
8218
|
const seen = /* @__PURE__ */ new Set();
|
|
8140
8219
|
const out = [];
|
|
8141
8220
|
const push = (candidate) => {
|
|
@@ -9724,30 +9803,7 @@ function indexedWorktreeHasMaterialChanges(entry) {
|
|
|
9724
9803
|
return materialWorktreeChanges2(gitStatusShort(entry.worktreePath)).length > 0;
|
|
9725
9804
|
}
|
|
9726
9805
|
|
|
9727
|
-
// src/cleanup-
|
|
9728
|
-
function deriveRunTerminal(indexed, ctx) {
|
|
9729
|
-
if (ctx) return ctx.runTerminalCache.derive(indexed.run);
|
|
9730
|
-
return deriveTerminalRunStatus(indexed.run);
|
|
9731
|
-
}
|
|
9732
|
-
function isWorkerProcessLive(indexed) {
|
|
9733
|
-
if (isPidAlive(indexed.worker.pid)) return true;
|
|
9734
|
-
if (!indexed.worker.pid) return indexedWorktreeStatus(indexed).alive;
|
|
9735
|
-
return false;
|
|
9736
|
-
}
|
|
9737
|
-
function isRunStaleActive(indexed, ctx) {
|
|
9738
|
-
if (TERMINAL_RUN_STATUSES.has(indexed.run.status)) return false;
|
|
9739
|
-
return deriveRunTerminal(indexed, ctx) !== null;
|
|
9740
|
-
}
|
|
9741
|
-
function runBlocksWorktreeRemoval(indexed, ctx) {
|
|
9742
|
-
if (isWorkerProcessLive(indexed)) return true;
|
|
9743
|
-
if (indexed.worker.completionBlocker) return true;
|
|
9744
|
-
if (TERMINAL_RUN_STATUSES.has(indexed.run.status)) return false;
|
|
9745
|
-
if (isRunStaleActive(indexed, ctx)) return false;
|
|
9746
|
-
if (!isFinishedWorkerStatus(indexedWorktreeStatus(indexed))) return true;
|
|
9747
|
-
return deriveRunTerminal(indexed, ctx) === null;
|
|
9748
|
-
}
|
|
9749
|
-
|
|
9750
|
-
// src/cleanup-guards.ts
|
|
9806
|
+
// src/cleanup-worktree-salvage.ts
|
|
9751
9807
|
function prUrlFromFinalResult(finalResult) {
|
|
9752
9808
|
if (typeof finalResult === "string") {
|
|
9753
9809
|
const match = finalResult.match(/https:\/\/github\.com\/[^\s]+\/pull\/\d+/i);
|
|
@@ -9772,12 +9828,63 @@ function isPrOrUnmergedWork(status) {
|
|
|
9772
9828
|
if (status.changedFiles.length > 0 && status.finalResult) return true;
|
|
9773
9829
|
return false;
|
|
9774
9830
|
}
|
|
9831
|
+
|
|
9832
|
+
// src/cleanup-completion-blocker.ts
|
|
9833
|
+
function completionBlockerBlocksWorktreeRemoval(indexed, status) {
|
|
9834
|
+
const blocker = typeof indexed.worker.completionBlocker === "string" ? indexed.worker.completionBlocker.trim() : "";
|
|
9835
|
+
if (!blocker) return false;
|
|
9836
|
+
if (isWorkerProcessLive(indexed)) return true;
|
|
9837
|
+
const resolved = status ?? indexedWorktreeStatus(indexed);
|
|
9838
|
+
if (!isFinishedWorkerStatus(resolved)) return true;
|
|
9839
|
+
if (isPrOrUnmergedWork(resolved)) return true;
|
|
9840
|
+
if (materialWorktreeChanges2(resolved.changedFiles).length > 0) return true;
|
|
9841
|
+
const landing = assessWorkerLanding({
|
|
9842
|
+
finalResult: resolved.finalResult,
|
|
9843
|
+
changedFiles: resolved.changedFiles,
|
|
9844
|
+
gitAncestry: resolved.gitAncestry,
|
|
9845
|
+
prUrl: prUrlFromFinalResult(resolved.finalResult)
|
|
9846
|
+
});
|
|
9847
|
+
if (landing.blocked) return true;
|
|
9848
|
+
return false;
|
|
9849
|
+
}
|
|
9850
|
+
|
|
9851
|
+
// src/cleanup-run-liveness.ts
|
|
9852
|
+
function deriveRunTerminal(indexed, ctx) {
|
|
9853
|
+
if (ctx) return ctx.runTerminalCache.derive(indexed.run);
|
|
9854
|
+
return deriveTerminalRunStatus(indexed.run);
|
|
9855
|
+
}
|
|
9856
|
+
function isWorkerProcessLive(indexed) {
|
|
9857
|
+
if (isPidAlive(indexed.worker.pid)) return true;
|
|
9858
|
+
if (!indexed.worker.pid) return indexedWorktreeStatus(indexed).alive;
|
|
9859
|
+
return false;
|
|
9860
|
+
}
|
|
9861
|
+
function isRunStaleActive(indexed, ctx) {
|
|
9862
|
+
if (TERMINAL_RUN_STATUSES.has(indexed.run.status)) return false;
|
|
9863
|
+
return deriveRunTerminal(indexed, ctx) !== null;
|
|
9864
|
+
}
|
|
9865
|
+
function runBlocksWorktreeRemoval(indexed, ctx) {
|
|
9866
|
+
if (isWorkerProcessLive(indexed)) return true;
|
|
9867
|
+
const status = indexedWorktreeStatus(indexed);
|
|
9868
|
+
if (completionBlockerBlocksWorktreeRemoval(indexed, status)) return true;
|
|
9869
|
+
if (isFinishedWorkerStatus(status)) return false;
|
|
9870
|
+
if (TERMINAL_RUN_STATUSES.has(indexed.run.status)) return false;
|
|
9871
|
+
if (isRunStaleActive(indexed, ctx)) return false;
|
|
9872
|
+
return deriveRunTerminal(indexed, ctx) === null;
|
|
9873
|
+
}
|
|
9874
|
+
|
|
9875
|
+
// src/cleanup-guards.ts
|
|
9775
9876
|
function effectiveWorktreeAgeMs(input) {
|
|
9776
9877
|
const { indexed, includeOrphans, worktreesAgeMs, terminalWorktreesAgeMs } = input;
|
|
9777
9878
|
if (!indexed) return includeOrphans ? terminalWorktreesAgeMs : worktreesAgeMs;
|
|
9778
9879
|
if (TERMINAL_RUN_STATUSES.has(indexed.run.status)) {
|
|
9779
9880
|
return terminalWorktreesAgeMs;
|
|
9780
9881
|
}
|
|
9882
|
+
if (input.liveness && isRunStaleActive(indexed, input.liveness)) {
|
|
9883
|
+
return terminalWorktreesAgeMs;
|
|
9884
|
+
}
|
|
9885
|
+
if (input.liveness && isFinishedWorkerStatus(indexedWorktreeStatus(indexed)) && !isWorkerProcessLive(indexed)) {
|
|
9886
|
+
return terminalWorktreesAgeMs;
|
|
9887
|
+
}
|
|
9781
9888
|
return worktreesAgeMs;
|
|
9782
9889
|
}
|
|
9783
9890
|
function skipWorktreeRemoval(input) {
|
|
@@ -9791,7 +9898,7 @@ function skipWorktreeRemoval(input) {
|
|
|
9791
9898
|
if (ageThresholdMs > 0 && ageMs < ageThresholdMs) return "below_age_threshold";
|
|
9792
9899
|
const status = indexedWorktreeStatus(indexed);
|
|
9793
9900
|
if (isWorkerProcessLive(indexed)) return "active_worker";
|
|
9794
|
-
if (indexed
|
|
9901
|
+
if (completionBlockerBlocksWorktreeRemoval(indexed, status)) return "completion_blocked";
|
|
9795
9902
|
if (runBlocksWorktreeRemoval(indexed, input.liveness)) return "run_still_active";
|
|
9796
9903
|
if (!isFinishedWorkerStatus(status)) return "run_still_active";
|
|
9797
9904
|
if (isPrOrUnmergedWork(status)) return "pr_or_unmerged_commits";
|
|
@@ -9947,7 +10054,9 @@ function skipRunDirectoryRemoval(input) {
|
|
|
9947
10054
|
}
|
|
9948
10055
|
if (!runDirectoryIsEmpty(runPath)) return "run_still_active";
|
|
9949
10056
|
const run = loadRunStatus(harnessRoot, runId);
|
|
9950
|
-
if (run && !TERMINAL_RUN_STATUSES.has(run.status))
|
|
10057
|
+
if (run && !TERMINAL_RUN_STATUSES.has(run.status)) {
|
|
10058
|
+
if (!deriveTerminalRunStatus(run)) return "run_still_active";
|
|
10059
|
+
}
|
|
9951
10060
|
if (runDirectoriesAgeMs > 0 && ageMs < runDirectoriesAgeMs) return "below_age_threshold";
|
|
9952
10061
|
return null;
|
|
9953
10062
|
}
|
|
@@ -10912,14 +11021,14 @@ function runHarnessCleanup(options = {}) {
|
|
|
10912
11021
|
const paths = resolvePaths(options);
|
|
10913
11022
|
emitCleanupProgress("scan", `${paths.scanRoots.length} harness root(s)`);
|
|
10914
11023
|
const activeGuards = collectActiveWorktreeGuards(paths.scanRoots, paths.now);
|
|
10915
|
-
emitCleanupProgress("index", "building worktree index");
|
|
10916
|
-
const index = mergeWorktreeIndexes(paths.scanRoots);
|
|
10917
|
-
emitCleanupProgress("index", `${index.size} indexed worktree(s)`);
|
|
10918
|
-
const liveness = { runTerminalCache: new CleanupRunTerminalCache() };
|
|
10919
11024
|
const finalizedRuns = retention.finalizeStaleRuns ? finalizeStaleRuns().map((f) => ({ runId: f.runId, from: f.from, to: f.to })) : [];
|
|
10920
11025
|
if (finalizedRuns.length > 0) {
|
|
10921
11026
|
emitCleanupProgress("finalize", `${finalizedRuns.length} stale run(s) marked terminal`);
|
|
10922
11027
|
}
|
|
11028
|
+
emitCleanupProgress("index", "building worktree index");
|
|
11029
|
+
const index = mergeWorktreeIndexes(paths.scanRoots);
|
|
11030
|
+
emitCleanupProgress("index", `${index.size} indexed worktree(s)`);
|
|
11031
|
+
const liveness = { runTerminalCache: new CleanupRunTerminalCache() };
|
|
10923
11032
|
const skips = [];
|
|
10924
11033
|
const actions = [];
|
|
10925
11034
|
const processedPaths = /* @__PURE__ */ new Set();
|