@kynver-app/runtime 0.1.95 → 0.1.102
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/bounded-build/admission.d.ts +2 -2
- package/dist/bounded-build/exec.d.ts +6 -2
- package/dist/cli.js +970 -262
- package/dist/cli.js.map +4 -4
- package/dist/harness-verify.d.ts +3 -2
- package/dist/harness-worker-active.d.ts +12 -0
- package/dist/heavy-verification/gate.d.ts +10 -0
- package/dist/heavy-verification/index.d.ts +3 -0
- package/dist/heavy-verification/paths.d.ts +3 -0
- package/dist/heavy-verification/slot.d.ts +49 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +970 -248
- package/dist/index.js.map +4 -4
- package/dist/lane/landing-maintainer-local.d.ts +15 -0
- package/dist/lane/landing-maintainer-tick.d.ts +16 -0
- package/dist/lane/lane-spec.d.ts +8 -0
- package/dist/lane/lane-tick-cli.d.ts +1 -0
- package/dist/local-pr-attention-reconcile.d.ts +25 -0
- package/dist/resource-gate.d.ts +2 -0
- package/dist/run-resolve.d.ts +4 -0
- package/dist/stale-reconcile.d.ts +2 -0
- package/dist/status.d.ts +1 -0
- package/dist/util.d.ts +2 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -119,6 +119,9 @@ function readMaybeFile(file) {
|
|
|
119
119
|
function sleepMs(ms) {
|
|
120
120
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
121
121
|
}
|
|
122
|
+
function sleepMsAsync(ms) {
|
|
123
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
124
|
+
}
|
|
122
125
|
function isPidAlive(pid) {
|
|
123
126
|
if (!pid) return false;
|
|
124
127
|
try {
|
|
@@ -725,7 +728,7 @@ function shouldEnforceMemoryCostPackageGuardCli(scope, action) {
|
|
|
725
728
|
}
|
|
726
729
|
|
|
727
730
|
// src/config.ts
|
|
728
|
-
import { existsSync as existsSync11, mkdirSync as mkdirSync2, readFileSync as
|
|
731
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync2, readFileSync as readFileSync10, writeFileSync as writeFileSync2 } from "node:fs";
|
|
729
732
|
import { homedir as homedir5, totalmem } from "node:os";
|
|
730
733
|
import path10 from "node:path";
|
|
731
734
|
|
|
@@ -827,23 +830,23 @@ function isWslHost() {
|
|
|
827
830
|
function observeWslHostDisk(options = {}) {
|
|
828
831
|
const wsl = options.forceWsl === void 0 ? isWslHost() : options.forceWsl;
|
|
829
832
|
if (!wsl) return null;
|
|
830
|
-
const
|
|
833
|
+
const path73 = options.wslHostMount?.trim() || process.env.KYNVER_WSL_HOST_MOUNT?.trim() || DEFAULT_WSL_HOST_MOUNT;
|
|
831
834
|
const warnBelowBytes = options.wslHostFreeWarnBytes ?? DEFAULT_WSL_HOST_WARN_FREE_BYTES;
|
|
832
835
|
const criticalBelowBytes = options.wslHostFreeCriticalBytes ?? DEFAULT_WSL_HOST_CRITICAL_FREE_BYTES;
|
|
833
836
|
const statfs = options.statfs ?? statfsSync;
|
|
834
837
|
let stats;
|
|
835
838
|
try {
|
|
836
|
-
stats = statfs(
|
|
839
|
+
stats = statfs(path73);
|
|
837
840
|
} catch (error) {
|
|
838
841
|
return {
|
|
839
842
|
ok: false,
|
|
840
|
-
path:
|
|
843
|
+
path: path73,
|
|
841
844
|
freeBytes: 0,
|
|
842
845
|
totalBytes: 0,
|
|
843
846
|
usedPercent: 100,
|
|
844
847
|
warnBelowBytes,
|
|
845
848
|
criticalBelowBytes,
|
|
846
|
-
reason: `Windows host disk probe failed at ${
|
|
849
|
+
reason: `Windows host disk probe failed at ${path73}: ${error.message}`,
|
|
847
850
|
probeError: error.message
|
|
848
851
|
};
|
|
849
852
|
}
|
|
@@ -857,11 +860,11 @@ function observeWslHostDisk(options = {}) {
|
|
|
857
860
|
let reason = null;
|
|
858
861
|
if (!ok) {
|
|
859
862
|
const tag = criticalFree ? "critical" : "warning";
|
|
860
|
-
reason = `Windows host disk ${
|
|
863
|
+
reason = `Windows host disk ${path73} at ${tag}: ${freeGiB} GiB free (<${(criticalFree ? criticalBelowBytes : warnBelowBytes) / 1024 / 1024 / 1024} GiB); WSL VHDX cannot grow safely. ${summarizeWslRecoverySteps()}`;
|
|
861
864
|
}
|
|
862
865
|
return {
|
|
863
866
|
ok,
|
|
864
|
-
path:
|
|
867
|
+
path: path73,
|
|
865
868
|
freeBytes,
|
|
866
869
|
totalBytes,
|
|
867
870
|
usedPercent,
|
|
@@ -881,12 +884,12 @@ var DEFAULT_CRITICAL_FREE_BYTES = 15 * 1024 * 1024 * 1024;
|
|
|
881
884
|
var DEFAULT_MAX_USED_PERCENT = 80;
|
|
882
885
|
var DEFAULT_HARD_MAX_USED_PERCENT = 90;
|
|
883
886
|
function observeRunnerDiskGate(input = {}) {
|
|
884
|
-
const
|
|
887
|
+
const path73 = input.diskPath?.trim() || "/";
|
|
885
888
|
const warnBelowBytes = input.diskFreeWarnBytes ?? DEFAULT_WARN_FREE_BYTES;
|
|
886
889
|
const criticalBelowBytes = input.diskFreeCriticalBytes ?? DEFAULT_CRITICAL_FREE_BYTES;
|
|
887
890
|
const maxUsedPercent = input.diskMaxUsedPercent ?? DEFAULT_MAX_USED_PERCENT;
|
|
888
891
|
const hardMaxUsedPercent = input.diskHardMaxUsedPercent ?? DEFAULT_HARD_MAX_USED_PERCENT;
|
|
889
|
-
const stats = statfsSync2(
|
|
892
|
+
const stats = statfsSync2(path73);
|
|
890
893
|
const freeBytes = Number(stats.bavail) * Number(stats.bsize);
|
|
891
894
|
const totalBytes = Number(stats.blocks) * Number(stats.bsize);
|
|
892
895
|
const usedPercent = totalBytes > 0 ? (totalBytes - freeBytes) / totalBytes * 100 : 100;
|
|
@@ -909,7 +912,7 @@ function observeRunnerDiskGate(input = {}) {
|
|
|
909
912
|
}
|
|
910
913
|
return {
|
|
911
914
|
ok,
|
|
912
|
-
path:
|
|
915
|
+
path: path73,
|
|
913
916
|
freeBytes,
|
|
914
917
|
totalBytes,
|
|
915
918
|
usedPercent,
|
|
@@ -1043,6 +1046,9 @@ function listRunWorkerNames(run) {
|
|
|
1043
1046
|
return [...names];
|
|
1044
1047
|
}
|
|
1045
1048
|
|
|
1049
|
+
// src/harness-worker-active.ts
|
|
1050
|
+
import { readFileSync as readFileSync9 } from "node:fs";
|
|
1051
|
+
|
|
1046
1052
|
// src/heartbeat.ts
|
|
1047
1053
|
import { existsSync as existsSync9, readFileSync as readFileSync7 } from "node:fs";
|
|
1048
1054
|
|
|
@@ -2126,6 +2132,9 @@ function computeAttention(input) {
|
|
|
2126
2132
|
return { state: "blocked", reason: input.completionBlocker };
|
|
2127
2133
|
}
|
|
2128
2134
|
if (input.finalResult) {
|
|
2135
|
+
if (input.localOnly && hasMergedTargetPrReconciliation(input.finalResult)) {
|
|
2136
|
+
return { state: "done", reason: "local-only worker superseded by merged PR" };
|
|
2137
|
+
}
|
|
2129
2138
|
const landingSnapshot = {
|
|
2130
2139
|
finalResult: input.finalResult,
|
|
2131
2140
|
changedFiles: input.changedFiles ?? [],
|
|
@@ -2191,9 +2200,24 @@ function computeAttention(input) {
|
|
|
2191
2200
|
}
|
|
2192
2201
|
return { state: "ok", reason: "recent activity" };
|
|
2193
2202
|
}
|
|
2203
|
+
function hasMergedTargetPrReconciliation(value) {
|
|
2204
|
+
let record3 = null;
|
|
2205
|
+
if (typeof value === "string") record3 = extractEmbeddedWorkerFinalResultRecord(value);
|
|
2206
|
+
else if (value && typeof value === "object" && !Array.isArray(value)) record3 = value;
|
|
2207
|
+
if (!record3) return false;
|
|
2208
|
+
const raw = record3.targetPrReconciliation ?? record3.target_pr_reconciliation;
|
|
2209
|
+
if (!Array.isArray(raw)) return false;
|
|
2210
|
+
return raw.some((item) => {
|
|
2211
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) return false;
|
|
2212
|
+
return String(item.outcome ?? "").trim() === "merged";
|
|
2213
|
+
});
|
|
2214
|
+
}
|
|
2194
2215
|
function resolveFinalResult(worker, parsedFinalResult, heartbeat) {
|
|
2195
|
-
if (parsedFinalResult) return parsedFinalResult;
|
|
2196
2216
|
const ackSnapshot = worker.completionSnapshot?.finalResult;
|
|
2217
|
+
if (worker.completionAckSource === "local-pr-merged-reconcile" && ackSnapshot !== void 0 && ackSnapshot !== null) {
|
|
2218
|
+
return ackSnapshot;
|
|
2219
|
+
}
|
|
2220
|
+
if (parsedFinalResult) return parsedFinalResult;
|
|
2197
2221
|
if (ackSnapshot !== void 0 && ackSnapshot !== null) return ackSnapshot;
|
|
2198
2222
|
return terminalFinalResultFromHeartbeat(heartbeat);
|
|
2199
2223
|
}
|
|
@@ -2240,7 +2264,8 @@ function computeWorkerStatus(worker, options = {}) {
|
|
|
2240
2264
|
gitAncestry,
|
|
2241
2265
|
completionBlocker,
|
|
2242
2266
|
landingContract,
|
|
2243
|
-
prUrl: worker.repairTargetPrUrl ?? worker.taskPrUrl ?? null
|
|
2267
|
+
prUrl: worker.repairTargetPrUrl ?? worker.taskPrUrl ?? null,
|
|
2268
|
+
localOnly: worker.localOnly === true
|
|
2244
2269
|
});
|
|
2245
2270
|
const workerStatusLabel = completionBlocker || attention.state === "blocked" ? "blocked" : completionAcknowledged || attention.state === "done" ? "done" : finalResult ? "exited" : alive ? "running" : "exited";
|
|
2246
2271
|
return {
|
|
@@ -2294,6 +2319,33 @@ function deriveRunStatus(fallback, workers) {
|
|
|
2294
2319
|
return fallback;
|
|
2295
2320
|
}
|
|
2296
2321
|
|
|
2322
|
+
// src/harness-worker-active.ts
|
|
2323
|
+
function pidCommandLine(pid) {
|
|
2324
|
+
if (!pid || process.platform !== "linux") return null;
|
|
2325
|
+
try {
|
|
2326
|
+
return readFileSync9(`/proc/${pid}/cmdline`, "utf8").replace(/\0/g, " ");
|
|
2327
|
+
} catch {
|
|
2328
|
+
return null;
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
function workerProcessMatchesRecord(worker) {
|
|
2332
|
+
if (!worker.pid || process.platform !== "linux") return true;
|
|
2333
|
+
const cmdline = pidCommandLine(worker.pid);
|
|
2334
|
+
if (!cmdline) return false;
|
|
2335
|
+
const probes = [worker.worktreePath, worker.workerDir, worker.heartbeatPath].filter(
|
|
2336
|
+
(value) => typeof value === "string" && value.trim().length > 0
|
|
2337
|
+
);
|
|
2338
|
+
return probes.some((probe) => cmdline.includes(probe));
|
|
2339
|
+
}
|
|
2340
|
+
function isActiveHarnessWorker(worker) {
|
|
2341
|
+
if (typeof worker.completionBlocker === "string" && worker.completionBlocker.trim()) {
|
|
2342
|
+
return false;
|
|
2343
|
+
}
|
|
2344
|
+
const status = computeWorkerStatus(worker);
|
|
2345
|
+
if (status.alive && !workerProcessMatchesRecord(worker)) return false;
|
|
2346
|
+
return status.alive && !status.finalResult && status.attention.state !== "done";
|
|
2347
|
+
}
|
|
2348
|
+
|
|
2297
2349
|
// src/resource-gate.ts
|
|
2298
2350
|
var DEFAULT_PER_WORKER_MEM_BYTES = 500 * 1024 * 1024;
|
|
2299
2351
|
var DEFAULT_MEM_RESERVE_BYTES = 4 * 1024 * 1024 * 1024;
|
|
@@ -2336,13 +2388,6 @@ function computeAutoMaxWorkers(totalMemBytes, opts = {}) {
|
|
|
2336
2388
|
function readAvailableMemBytes() {
|
|
2337
2389
|
return readMemAvailableBytes();
|
|
2338
2390
|
}
|
|
2339
|
-
function isActiveHarnessWorker(worker) {
|
|
2340
|
-
if (typeof worker.completionBlocker === "string" && worker.completionBlocker.trim()) {
|
|
2341
|
-
return false;
|
|
2342
|
-
}
|
|
2343
|
-
const status = computeWorkerStatus(worker);
|
|
2344
|
-
return status.alive && !status.finalResult && status.attention.state !== "done";
|
|
2345
|
-
}
|
|
2346
2391
|
function countActiveWorkersForRun(run) {
|
|
2347
2392
|
let active = 0;
|
|
2348
2393
|
for (const name of listRunWorkerNames(run)) {
|
|
@@ -2491,7 +2536,7 @@ var CREDENTIALS_FILE = path10.join(CONFIG_DIR, "credentials");
|
|
|
2491
2536
|
function loadUserConfig() {
|
|
2492
2537
|
if (!existsSync11(CONFIG_FILE)) return {};
|
|
2493
2538
|
try {
|
|
2494
|
-
return JSON.parse(
|
|
2539
|
+
return JSON.parse(readFileSync10(CONFIG_FILE, "utf8"));
|
|
2495
2540
|
} catch {
|
|
2496
2541
|
return {};
|
|
2497
2542
|
}
|
|
@@ -2568,7 +2613,7 @@ function resolveSetupWorkerConfig(existing, args, totalMemBytes = totalmem()) {
|
|
|
2568
2613
|
function loadCredentialsFile() {
|
|
2569
2614
|
if (!existsSync11(CREDENTIALS_FILE)) return {};
|
|
2570
2615
|
try {
|
|
2571
|
-
return JSON.parse(
|
|
2616
|
+
return JSON.parse(readFileSync10(CREDENTIALS_FILE, "utf8"));
|
|
2572
2617
|
} catch {
|
|
2573
2618
|
return {};
|
|
2574
2619
|
}
|
|
@@ -2845,15 +2890,30 @@ async function withTimeout(fn) {
|
|
|
2845
2890
|
clearTimeout(timeout);
|
|
2846
2891
|
}
|
|
2847
2892
|
}
|
|
2893
|
+
function callbackFetchError(error) {
|
|
2894
|
+
return {
|
|
2895
|
+
ok: false,
|
|
2896
|
+
status: 0,
|
|
2897
|
+
response: {
|
|
2898
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2899
|
+
timeoutMs: callbackTimeoutMs()
|
|
2900
|
+
}
|
|
2901
|
+
};
|
|
2902
|
+
}
|
|
2848
2903
|
async function postJson(url, secret, body) {
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2904
|
+
let res;
|
|
2905
|
+
try {
|
|
2906
|
+
res = await withTimeout(
|
|
2907
|
+
(signal) => fetch(url, {
|
|
2908
|
+
method: "POST",
|
|
2909
|
+
headers: buildHarnessCallbackHeaders(secret),
|
|
2910
|
+
body: JSON.stringify(body),
|
|
2911
|
+
signal
|
|
2912
|
+
})
|
|
2913
|
+
);
|
|
2914
|
+
} catch (error) {
|
|
2915
|
+
return callbackFetchError(error);
|
|
2916
|
+
}
|
|
2857
2917
|
let response = null;
|
|
2858
2918
|
try {
|
|
2859
2919
|
response = await res.json();
|
|
@@ -2871,13 +2931,18 @@ async function postJsonWithCredentialRefresh(url, secret, body, opts) {
|
|
|
2871
2931
|
return { ...retry, refreshedAuth: true };
|
|
2872
2932
|
}
|
|
2873
2933
|
async function getJson(url, secret) {
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2934
|
+
let res;
|
|
2935
|
+
try {
|
|
2936
|
+
res = await withTimeout(
|
|
2937
|
+
(signal) => fetch(url, {
|
|
2938
|
+
method: "GET",
|
|
2939
|
+
headers: buildHarnessCallbackHeaders(secret),
|
|
2940
|
+
signal
|
|
2941
|
+
})
|
|
2942
|
+
);
|
|
2943
|
+
} catch (error) {
|
|
2944
|
+
return callbackFetchError(error);
|
|
2945
|
+
}
|
|
2881
2946
|
let response = null;
|
|
2882
2947
|
try {
|
|
2883
2948
|
response = await res.json();
|
|
@@ -2887,41 +2952,6 @@ async function getJson(url, secret) {
|
|
|
2887
2952
|
return { ok: res.ok, status: res.status, response };
|
|
2888
2953
|
}
|
|
2889
2954
|
|
|
2890
|
-
// src/dispatch-lane-normalization.ts
|
|
2891
|
-
function trimLower(value) {
|
|
2892
|
-
return (value ?? "").trim().toLowerCase();
|
|
2893
|
-
}
|
|
2894
|
-
function roleLaneToDispatchLane(roleLane) {
|
|
2895
|
-
switch (trimLower(roleLane)) {
|
|
2896
|
-
case "implementer":
|
|
2897
|
-
case "repair_implementer":
|
|
2898
|
-
case "plan_author":
|
|
2899
|
-
case "runtime_verifier":
|
|
2900
|
-
return "implementation";
|
|
2901
|
-
case "plan_reviewer":
|
|
2902
|
-
case "report_reviewer":
|
|
2903
|
-
case "deep_reviewer":
|
|
2904
|
-
return "review";
|
|
2905
|
-
default:
|
|
2906
|
-
return null;
|
|
2907
|
-
}
|
|
2908
|
-
}
|
|
2909
|
-
function normalizeDispatchNextLaneFilter(raw) {
|
|
2910
|
-
const key = trimLower(raw);
|
|
2911
|
-
if (!key) return void 0;
|
|
2912
|
-
if (key === "implementation" || key === "review" || key === "landing" || key === "any") {
|
|
2913
|
-
return key;
|
|
2914
|
-
}
|
|
2915
|
-
const mapped = roleLaneToDispatchLane(key);
|
|
2916
|
-
if (mapped) return mapped;
|
|
2917
|
-
if (key === "implement" || key === "repair" || key === "coding") return "implementation";
|
|
2918
|
-
if (key === "land" || key === "merge") return "landing";
|
|
2919
|
-
return void 0;
|
|
2920
|
-
}
|
|
2921
|
-
function resolveDispatchNextLaneFilter(raw) {
|
|
2922
|
-
return normalizeDispatchNextLaneFilter(raw) ?? "any";
|
|
2923
|
-
}
|
|
2924
|
-
|
|
2925
2955
|
// src/worker-persona-catalog.ts
|
|
2926
2956
|
var WORKER_PERSONA_CATALOG = [
|
|
2927
2957
|
{
|
|
@@ -3036,6 +3066,41 @@ function workerPersonaLandingSlugs() {
|
|
|
3036
3066
|
);
|
|
3037
3067
|
}
|
|
3038
3068
|
|
|
3069
|
+
// src/dispatch-lane-normalization.ts
|
|
3070
|
+
function trimLower(value) {
|
|
3071
|
+
return (value ?? "").trim().toLowerCase();
|
|
3072
|
+
}
|
|
3073
|
+
function roleLaneToDispatchLane(roleLane) {
|
|
3074
|
+
switch (trimLower(roleLane)) {
|
|
3075
|
+
case "implementer":
|
|
3076
|
+
case "repair_implementer":
|
|
3077
|
+
case "plan_author":
|
|
3078
|
+
case "runtime_verifier":
|
|
3079
|
+
return "implementation";
|
|
3080
|
+
case "plan_reviewer":
|
|
3081
|
+
case "report_reviewer":
|
|
3082
|
+
case "deep_reviewer":
|
|
3083
|
+
return "review";
|
|
3084
|
+
default:
|
|
3085
|
+
return null;
|
|
3086
|
+
}
|
|
3087
|
+
}
|
|
3088
|
+
function normalizeDispatchNextLaneFilter(raw) {
|
|
3089
|
+
const key = trimLower(raw);
|
|
3090
|
+
if (!key) return void 0;
|
|
3091
|
+
if (key === "implementation" || key === "review" || key === "landing" || key === "any") {
|
|
3092
|
+
return key;
|
|
3093
|
+
}
|
|
3094
|
+
const mapped = roleLaneToDispatchLane(key);
|
|
3095
|
+
if (mapped) return mapped;
|
|
3096
|
+
if (key === "implement" || key === "repair" || key === "coding") return "implementation";
|
|
3097
|
+
if (key === "land" || key === "merge") return "landing";
|
|
3098
|
+
return void 0;
|
|
3099
|
+
}
|
|
3100
|
+
function resolveDispatchNextLaneFilter(raw) {
|
|
3101
|
+
return normalizeDispatchNextLaneFilter(raw) ?? "any";
|
|
3102
|
+
}
|
|
3103
|
+
|
|
3039
3104
|
// src/model-routing-task-enrich.ts
|
|
3040
3105
|
function taskString(task, key) {
|
|
3041
3106
|
const v = task[key];
|
|
@@ -3503,7 +3568,7 @@ function probeCursorOAuthBinding(nowIso = (/* @__PURE__ */ new Date()).toISOStri
|
|
|
3503
3568
|
}
|
|
3504
3569
|
|
|
3505
3570
|
// src/orchestration-providers/hermes-cli-adapter.ts
|
|
3506
|
-
import { existsSync as existsSync16, readFileSync as
|
|
3571
|
+
import { existsSync as existsSync16, readFileSync as readFileSync11 } from "node:fs";
|
|
3507
3572
|
import { homedir as homedir10 } from "node:os";
|
|
3508
3573
|
import path15 from "node:path";
|
|
3509
3574
|
var PROFILE_ENV_KEYS = [
|
|
@@ -3519,7 +3584,7 @@ function hermesProfileEnvPath() {
|
|
|
3519
3584
|
}
|
|
3520
3585
|
function profileEnvKeyPresence(envPath) {
|
|
3521
3586
|
try {
|
|
3522
|
-
const text =
|
|
3587
|
+
const text = readFileSync11(envPath, "utf8");
|
|
3523
3588
|
const present = [];
|
|
3524
3589
|
for (const key of PROFILE_ENV_KEYS) {
|
|
3525
3590
|
const re = new RegExp(`^${key}=`, "m");
|
|
@@ -4493,7 +4558,7 @@ function buildPrompt(input) {
|
|
|
4493
4558
|
"Completion handoff (required): before you stop, ensure the harness records a final result \u2014 summarize outcome in your last message and append a heartbeat line with phase `complete`. If you leave uncommitted changes or committed work without a PR, the orchestrator blocks completion until a GitHub PR exists (or you discard/commit cleanly). One-off helper scripts must be removed (`kynver worker discard-disposable --path <file>`) or committed before completion \u2014 maintenance/board-drain workers are not exempt. Exiting with only dirty files and no PR routes to salvage review, not production review.",
|
|
4494
4559
|
"PR-ready handoff: for substantial implementation work, commit, push, and open a GitHub PR (draft OK) on your branch before finishing \u2014 or rely on the harness to run `gh pr create` at completion when `gh` is authenticated.",
|
|
4495
4560
|
"Expert review / production-review workers (Dalton/Lorentz, plan-review-task, scheduledJob reviewer children): do NOT open new implementation PRs \u2014 review the parent task's existing PR and record reviewVerdict in finalResult; landing-contract targetPrReconciliation does not apply.",
|
|
4496
|
-
"Worker resource guard: do not run full monorepo verification (`npm run typecheck`, `npm run build`, or equivalent) from this worker lane unless an operator explicitly requests it. Use targeted checks for touched paths and rely on CI/operator lanes for heavy gates.",
|
|
4561
|
+
"Worker resource guard: do not run full monorepo verification (`npm run typecheck`, `npm run build`, or equivalent) from this worker lane unless an operator explicitly requests it. Use targeted checks for touched paths and rely on CI/operator lanes for heavy gates. When heavy verification is required, route through `node scripts/verify-pr-local.mjs` or `kynver harness verify` \u2014 they acquire the global heavy-verification lease so parallel workers do not launch simultaneous typechecks.",
|
|
4497
4562
|
"npm publish boundary: do not run `npm publish`, do not republish `@kynver-app/*` packages, and do not block on an operator to publish. When you need newer runtime code than npm, use this repo checkout (`npm run kynver:build`, `npm run kynver`) and record evidence: packages/kynver-runtime/package.json version + git ref in your completion report.",
|
|
4498
4563
|
"If verification fails (including OOM), append a heartbeat line immediately with the last command, failure reason, dirty-file status, commit/PR handoff state, and next action so recovery does not require log spelunking.",
|
|
4499
4564
|
"Landing-wrapper cleanup on a git ref: use `node scripts/agent-os-land-pr-cleanup-verify.mjs --ref origin/main` (JSON, exit 0). Do not use `git show <ref>:scripts/agent-os-land-pr.mjs | rg \u2026` \u2014 ripgrep exit 1 when markers are absent is reported as a failed command in Telegram/status tooling.",
|
|
@@ -5369,8 +5434,8 @@ function dirtyPathsCoveredByDisposableRemoval(changedFiles, removed) {
|
|
|
5369
5434
|
if (removed.length === 0) return false;
|
|
5370
5435
|
const removedSet = new Set(removed.map((p) => normalizeRelativePath(p)));
|
|
5371
5436
|
return material.every((line) => {
|
|
5372
|
-
const
|
|
5373
|
-
return removedSet.has(
|
|
5437
|
+
const path73 = normalizeRelativePath(pathFromGitStatusLine(line));
|
|
5438
|
+
return removedSet.has(path73);
|
|
5374
5439
|
});
|
|
5375
5440
|
}
|
|
5376
5441
|
|
|
@@ -6678,7 +6743,7 @@ function collectRunActiveHarnessWorkers(runId) {
|
|
|
6678
6743
|
path24.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
6679
6744
|
void 0
|
|
6680
6745
|
);
|
|
6681
|
-
if (!worker?.taskId || !
|
|
6746
|
+
if (!worker?.taskId || !isActiveHarnessWorker(worker)) continue;
|
|
6682
6747
|
out.push({
|
|
6683
6748
|
runId: run.id,
|
|
6684
6749
|
workerName: name,
|
|
@@ -6917,7 +6982,7 @@ function isTmpOnlyPath(filePath) {
|
|
|
6917
6982
|
// src/plan-persist/outbox-store.ts
|
|
6918
6983
|
import {
|
|
6919
6984
|
existsSync as existsSync23,
|
|
6920
|
-
readFileSync as
|
|
6985
|
+
readFileSync as readFileSync12,
|
|
6921
6986
|
renameSync,
|
|
6922
6987
|
readdirSync as readdirSync5,
|
|
6923
6988
|
writeFileSync as writeFileSync3,
|
|
@@ -6945,7 +7010,7 @@ function findOutboxByIdempotencyKey(key) {
|
|
|
6945
7010
|
function readOutboxItem(jsonPath) {
|
|
6946
7011
|
if (!existsSync23(jsonPath)) return null;
|
|
6947
7012
|
try {
|
|
6948
|
-
return JSON.parse(
|
|
7013
|
+
return JSON.parse(readFileSync12(jsonPath, "utf8"));
|
|
6949
7014
|
} catch {
|
|
6950
7015
|
return null;
|
|
6951
7016
|
}
|
|
@@ -6953,7 +7018,7 @@ function readOutboxItem(jsonPath) {
|
|
|
6953
7018
|
function readOutboxBody(item) {
|
|
6954
7019
|
const { outboxDir } = ensurePlanOutboxDirs();
|
|
6955
7020
|
const bodyFile = path26.join(outboxDir, item.bodyPath);
|
|
6956
|
-
return
|
|
7021
|
+
return readFileSync12(bodyFile, "utf8");
|
|
6957
7022
|
}
|
|
6958
7023
|
function writeOutboxItem(input, opts) {
|
|
6959
7024
|
const { outboxDir } = ensurePlanOutboxDirs();
|
|
@@ -7539,7 +7604,7 @@ async function dispatchRun(args) {
|
|
|
7539
7604
|
);
|
|
7540
7605
|
}
|
|
7541
7606
|
const attempt = Number(task.attempt) || 1;
|
|
7542
|
-
if (attempt
|
|
7607
|
+
if (attempt > retryLimits.maxTaskAttempts) {
|
|
7543
7608
|
return abortClaimedSpawn(
|
|
7544
7609
|
task,
|
|
7545
7610
|
`task attempt ${attempt} exceeds KYNVER_MAX_TASK_ATTEMPTS (${retryLimits.maxTaskAttempts})`
|
|
@@ -7675,7 +7740,12 @@ async function dispatchRun(args) {
|
|
|
7675
7740
|
const admissionExhaustion = readAdmissionExhaustion(result);
|
|
7676
7741
|
const capacityIdle = admissionExhaustion?.capacityIdle === true || startedCount === 0 && Number(result.resourceGate?.slotsAvailable) > 0;
|
|
7677
7742
|
if (capacityIdle && admissionExhaustion?.summary) {
|
|
7678
|
-
|
|
7743
|
+
const retryCeiling = admissionExhaustion.skipReasonCounts?.retry_ceiling_exceeded ?? 0;
|
|
7744
|
+
const recovery = admissionExhaustion.overAttemptIdleRecovery;
|
|
7745
|
+
const recoveryNote = recovery?.attempted === true ? `; over_attempt_recovery minted=${recovery.minted ?? 0} started=${recovery.started ?? 0}` : retryCeiling > 0 ? "; over_attempt_recovery not attempted" : "";
|
|
7746
|
+
console.error(
|
|
7747
|
+
`[dispatch] ${admissionExhaustion.summary}${retryCeiling > 0 ? `; retry_ceiling_exceeded=${retryCeiling}` : ""}${recoveryNote}`
|
|
7748
|
+
);
|
|
7679
7749
|
}
|
|
7680
7750
|
const summary = {
|
|
7681
7751
|
runId: run.id,
|
|
@@ -8260,14 +8330,14 @@ function applyProductionDatabaseToProcess(options = {}) {
|
|
|
8260
8330
|
|
|
8261
8331
|
// src/worktree.ts
|
|
8262
8332
|
import { existsSync as existsSync29, mkdirSync as mkdirSync6 } from "node:fs";
|
|
8263
|
-
import
|
|
8333
|
+
import path38 from "node:path";
|
|
8264
8334
|
|
|
8265
8335
|
// src/run-list.ts
|
|
8266
|
-
import { existsSync as existsSync28, readFileSync as
|
|
8267
|
-
import
|
|
8336
|
+
import { existsSync as existsSync28, readFileSync as readFileSync15 } from "node:fs";
|
|
8337
|
+
import path37 from "node:path";
|
|
8268
8338
|
|
|
8269
8339
|
// src/stale-reconcile.ts
|
|
8270
|
-
import
|
|
8340
|
+
import path36 from "node:path";
|
|
8271
8341
|
|
|
8272
8342
|
// src/finalize.ts
|
|
8273
8343
|
import path30 from "node:path";
|
|
@@ -8902,6 +8972,193 @@ function reconcileWorkerMetadataCli() {
|
|
|
8902
8972
|
);
|
|
8903
8973
|
}
|
|
8904
8974
|
|
|
8975
|
+
// src/local-pr-attention-reconcile.ts
|
|
8976
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
8977
|
+
import path35 from "node:path";
|
|
8978
|
+
function normalizePrUrl3(url) {
|
|
8979
|
+
const m = url.trim().match(/github\.com\/([^/]+\/[^/]+)\/(?:pull|pulls)\/(\d+)/i);
|
|
8980
|
+
if (!m) return null;
|
|
8981
|
+
return `https://github.com/${m[1]}/pull/${m[2]}`;
|
|
8982
|
+
}
|
|
8983
|
+
function prNumberFromUrl(url) {
|
|
8984
|
+
const m = normalizePrUrl3(url)?.match(/\/pull\/(\d+)$/);
|
|
8985
|
+
if (!m) return null;
|
|
8986
|
+
const n = Number.parseInt(m[1], 10);
|
|
8987
|
+
return Number.isFinite(n) ? n : null;
|
|
8988
|
+
}
|
|
8989
|
+
function extractText(value) {
|
|
8990
|
+
if (value == null) return "";
|
|
8991
|
+
if (typeof value === "string") return value;
|
|
8992
|
+
try {
|
|
8993
|
+
return JSON.stringify(value);
|
|
8994
|
+
} catch {
|
|
8995
|
+
return "";
|
|
8996
|
+
}
|
|
8997
|
+
}
|
|
8998
|
+
function extractPrNumbersFromText(text) {
|
|
8999
|
+
const out = /* @__PURE__ */ new Set();
|
|
9000
|
+
for (const match of text.matchAll(/github\.com\/[^/\s)>"']+\/[^/\s)>"']+\/(?:pull|pulls)\/(\d+)/gi)) {
|
|
9001
|
+
const n = Number.parseInt(match[1], 10);
|
|
9002
|
+
if (Number.isFinite(n)) out.add(n);
|
|
9003
|
+
}
|
|
9004
|
+
for (const match of text.matchAll(/\bPR\s*#?\s*(\d{2,})\b/gi)) {
|
|
9005
|
+
const n = Number.parseInt(match[1], 10);
|
|
9006
|
+
if (Number.isFinite(n)) out.add(n);
|
|
9007
|
+
}
|
|
9008
|
+
for (const match of text.matchAll(/\bpr[-_]?(\d{2,})\b/gi)) {
|
|
9009
|
+
const n = Number.parseInt(match[1], 10);
|
|
9010
|
+
if (Number.isFinite(n)) out.add(n);
|
|
9011
|
+
}
|
|
9012
|
+
return [...out];
|
|
9013
|
+
}
|
|
9014
|
+
function extractTargetPrReconciliation(value) {
|
|
9015
|
+
let record3 = null;
|
|
9016
|
+
if (typeof value === "string") record3 = extractEmbeddedWorkerFinalResultRecord(value);
|
|
9017
|
+
else if (value && typeof value === "object" && !Array.isArray(value)) record3 = value;
|
|
9018
|
+
if (!record3) return [];
|
|
9019
|
+
const raw = record3.targetPrReconciliation ?? record3.target_pr_reconciliation;
|
|
9020
|
+
if (!Array.isArray(raw)) return [];
|
|
9021
|
+
const out = [];
|
|
9022
|
+
for (const item of raw) {
|
|
9023
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) continue;
|
|
9024
|
+
const row = item;
|
|
9025
|
+
const prUrl = normalizePrUrl3(String(row.prUrl ?? row.pr_url ?? ""));
|
|
9026
|
+
const outcome = String(row.outcome ?? "").trim();
|
|
9027
|
+
if (!prUrl || outcome !== "merged") continue;
|
|
9028
|
+
out.push({
|
|
9029
|
+
prUrl,
|
|
9030
|
+
mergeCommit: typeof row.mergeCommit === "string" ? row.mergeCommit : typeof row.merge_commit === "string" ? row.merge_commit : null,
|
|
9031
|
+
reason: typeof row.reason === "string" ? row.reason : null
|
|
9032
|
+
});
|
|
9033
|
+
}
|
|
9034
|
+
return out;
|
|
9035
|
+
}
|
|
9036
|
+
function defaultLookupPr(input) {
|
|
9037
|
+
try {
|
|
9038
|
+
const repo = execFileSync2("git", ["config", "--get", "remote.origin.url"], {
|
|
9039
|
+
cwd: input.repoDir,
|
|
9040
|
+
encoding: "utf8",
|
|
9041
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
9042
|
+
}).trim();
|
|
9043
|
+
const repoMatch = repo.match(/github\.com[:/]([^/]+\/[^/.]+)(?:\.git)?$/i);
|
|
9044
|
+
const repoSlug = repoMatch?.[1];
|
|
9045
|
+
if (!repoSlug) return null;
|
|
9046
|
+
const raw = execFileSync2(
|
|
9047
|
+
"gh",
|
|
9048
|
+
["pr", "view", String(input.prNumber), "--repo", repoSlug, "--json", "state,mergedAt,mergeCommit,url"],
|
|
9049
|
+
{ cwd: input.repoDir, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }
|
|
9050
|
+
);
|
|
9051
|
+
const parsed = JSON.parse(raw);
|
|
9052
|
+
return {
|
|
9053
|
+
prUrl: normalizePrUrl3(parsed.url ?? `https://github.com/${repoSlug}/pull/${input.prNumber}`) ?? `https://github.com/${repoSlug}/pull/${input.prNumber}`,
|
|
9054
|
+
state: parsed.state ?? "",
|
|
9055
|
+
mergedAt: parsed.mergedAt ?? null,
|
|
9056
|
+
mergeCommit: parsed.mergeCommit?.oid ?? null
|
|
9057
|
+
};
|
|
9058
|
+
} catch {
|
|
9059
|
+
return null;
|
|
9060
|
+
}
|
|
9061
|
+
}
|
|
9062
|
+
function finalResultForWorker(worker) {
|
|
9063
|
+
const heartbeat = parseHeartbeat(worker.heartbeatPath);
|
|
9064
|
+
return worker.completionSnapshot?.finalResult ?? terminalFinalResultFromHeartbeat(heartbeat);
|
|
9065
|
+
}
|
|
9066
|
+
function collectMergedEvidenceByPr(run) {
|
|
9067
|
+
const byPr = /* @__PURE__ */ new Map();
|
|
9068
|
+
const runDir2 = runDirectory(run.id);
|
|
9069
|
+
for (const name of listRunWorkerNames(run)) {
|
|
9070
|
+
const worker = readJson(
|
|
9071
|
+
path35.join(runDir2, "workers", safeSlug(name), "worker.json"),
|
|
9072
|
+
void 0
|
|
9073
|
+
);
|
|
9074
|
+
if (!worker) continue;
|
|
9075
|
+
for (const evidence of extractTargetPrReconciliation(finalResultForWorker(worker))) {
|
|
9076
|
+
const n = prNumberFromUrl(evidence.prUrl);
|
|
9077
|
+
if (n != null && !byPr.has(n)) byPr.set(n, evidence);
|
|
9078
|
+
}
|
|
9079
|
+
}
|
|
9080
|
+
return byPr;
|
|
9081
|
+
}
|
|
9082
|
+
function candidatePrNumbers(worker, statusFinalResult) {
|
|
9083
|
+
const heartbeat = parseHeartbeat(worker.heartbeatPath);
|
|
9084
|
+
const text = [
|
|
9085
|
+
worker.name,
|
|
9086
|
+
worker.repairTargetPrUrl,
|
|
9087
|
+
worker.taskPrUrl,
|
|
9088
|
+
worker.branch,
|
|
9089
|
+
heartbeat.lastHeartbeatSummary,
|
|
9090
|
+
extractText(statusFinalResult)
|
|
9091
|
+
].filter(Boolean).join("\n");
|
|
9092
|
+
return extractPrNumbersFromText(text);
|
|
9093
|
+
}
|
|
9094
|
+
function isLocalOnlyAttentionNoise(worker) {
|
|
9095
|
+
return worker.localOnly === true && !worker.taskId && !worker.agentOsId;
|
|
9096
|
+
}
|
|
9097
|
+
function reconcileLocalOnlyMergedPrAttention(options = {}) {
|
|
9098
|
+
const lookupPr = options.lookupPr ?? defaultLookupPr;
|
|
9099
|
+
const outcomes = [];
|
|
9100
|
+
const liveLookupCache = /* @__PURE__ */ new Map();
|
|
9101
|
+
for (const run of listRunRecords()) {
|
|
9102
|
+
const mergedByPr = collectMergedEvidenceByPr(run);
|
|
9103
|
+
const runDir2 = runDirectory(run.id);
|
|
9104
|
+
for (const name of listRunWorkerNames(run)) {
|
|
9105
|
+
const workerPath = path35.join(runDir2, "workers", safeSlug(name), "worker.json");
|
|
9106
|
+
const worker = readJson(workerPath, void 0);
|
|
9107
|
+
if (!worker || !isLocalOnlyAttentionNoise(worker)) continue;
|
|
9108
|
+
const status = computeWorkerStatus(worker, { base: run.base, baseCommit: run.baseCommit });
|
|
9109
|
+
if (status.attention.state !== "needs_attention") continue;
|
|
9110
|
+
let merged = null;
|
|
9111
|
+
for (const prNumber of candidatePrNumbers(worker, status.finalResult)) {
|
|
9112
|
+
merged = mergedByPr.get(prNumber) ?? null;
|
|
9113
|
+
if (!merged) {
|
|
9114
|
+
const repoDir = run.repo || worker.worktreePath;
|
|
9115
|
+
const cacheKey = `${repoDir}#${prNumber}`;
|
|
9116
|
+
const live = liveLookupCache.has(cacheKey) ? liveLookupCache.get(cacheKey) ?? null : lookupPr({ repoDir, prNumber });
|
|
9117
|
+
if (!liveLookupCache.has(cacheKey)) liveLookupCache.set(cacheKey, live);
|
|
9118
|
+
if (live && (live.state === "MERGED" || live.mergedAt)) {
|
|
9119
|
+
merged = { prUrl: live.prUrl, mergeCommit: live.mergeCommit ?? null, reason: "GitHub reports PR merged" };
|
|
9120
|
+
}
|
|
9121
|
+
}
|
|
9122
|
+
if (merged) break;
|
|
9123
|
+
}
|
|
9124
|
+
if (!merged) {
|
|
9125
|
+
outcomes.push({ runId: run.id, worker: name, action: "skipped", reason: "no merged PR evidence" });
|
|
9126
|
+
continue;
|
|
9127
|
+
}
|
|
9128
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9129
|
+
worker.status = "done";
|
|
9130
|
+
worker.completionSnapshot = {
|
|
9131
|
+
prUrl: merged.prUrl,
|
|
9132
|
+
summary: `Local-only worker superseded by merged PR ${merged.prUrl}`,
|
|
9133
|
+
finalResult: {
|
|
9134
|
+
summary: `Local-only repair/salvage worker superseded by merged PR ${merged.prUrl}`,
|
|
9135
|
+
targetPrReconciliation: [
|
|
9136
|
+
{
|
|
9137
|
+
prUrl: merged.prUrl,
|
|
9138
|
+
outcome: "merged",
|
|
9139
|
+
mergeCommit: merged.mergeCommit ?? null,
|
|
9140
|
+
reason: merged.reason ?? "PR already merged; local-only worker no longer requires attention"
|
|
9141
|
+
}
|
|
9142
|
+
]
|
|
9143
|
+
}
|
|
9144
|
+
};
|
|
9145
|
+
worker.completionAckSource = "local-pr-merged-reconcile";
|
|
9146
|
+
worker.reconciledAt = now;
|
|
9147
|
+
worker.reconcileReason = "local-only needs_attention superseded by merged PR";
|
|
9148
|
+
saveWorker(run.id, worker);
|
|
9149
|
+
outcomes.push({
|
|
9150
|
+
runId: run.id,
|
|
9151
|
+
worker: name,
|
|
9152
|
+
action: "marked_done",
|
|
9153
|
+
reason: worker.reconcileReason,
|
|
9154
|
+
prUrl: merged.prUrl,
|
|
9155
|
+
mergeCommit: merged.mergeCommit ?? null
|
|
9156
|
+
});
|
|
9157
|
+
}
|
|
9158
|
+
}
|
|
9159
|
+
return { workers: outcomes };
|
|
9160
|
+
}
|
|
9161
|
+
|
|
8905
9162
|
// src/stale-reconcile.ts
|
|
8906
9163
|
var STALE_RECONCILE_HEARTBEAT_MS = 15 * 60 * 1e3;
|
|
8907
9164
|
function staleReconcileDisabled() {
|
|
@@ -8910,13 +9167,14 @@ function staleReconcileDisabled() {
|
|
|
8910
9167
|
function reconcileStaleWorkers() {
|
|
8911
9168
|
const metadataReconcile = reconcileWorkerMetadata();
|
|
8912
9169
|
if (staleReconcileDisabled()) {
|
|
8913
|
-
|
|
9170
|
+
const localPrAttentionReconcile2 = reconcileLocalOnlyMergedPrAttention();
|
|
9171
|
+
return { workers: [], finalizedRuns: finalizeStaleRuns(), metadataReconcile, localPrAttentionReconcile: localPrAttentionReconcile2 };
|
|
8914
9172
|
}
|
|
8915
9173
|
const outcomes = [];
|
|
8916
9174
|
const now = Date.now();
|
|
8917
9175
|
for (const run of listRunRecords()) {
|
|
8918
9176
|
for (const name of listRunWorkerNames(run)) {
|
|
8919
|
-
const workerPath =
|
|
9177
|
+
const workerPath = path36.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
|
|
8920
9178
|
const worker = readJson(workerPath, void 0);
|
|
8921
9179
|
if (!worker || worker.status !== "running") {
|
|
8922
9180
|
outcomes.push({
|
|
@@ -8988,7 +9246,8 @@ function reconcileStaleWorkers() {
|
|
|
8988
9246
|
});
|
|
8989
9247
|
}
|
|
8990
9248
|
}
|
|
8991
|
-
|
|
9249
|
+
const localPrAttentionReconcile = reconcileLocalOnlyMergedPrAttention();
|
|
9250
|
+
return { workers: outcomes, finalizedRuns: finalizeStaleRuns(), metadataReconcile, localPrAttentionReconcile };
|
|
8992
9251
|
}
|
|
8993
9252
|
function reconcileRunsCli() {
|
|
8994
9253
|
const result = reconcileStaleWorkers();
|
|
@@ -8999,6 +9258,10 @@ function reconcileRunsCli() {
|
|
|
8999
9258
|
acc[row.action] = (acc[row.action] ?? 0) + 1;
|
|
9000
9259
|
return acc;
|
|
9001
9260
|
}, {});
|
|
9261
|
+
const localPrAttentionTotals = result.localPrAttentionReconcile.workers.reduce((acc, row) => {
|
|
9262
|
+
acc[row.action] = (acc[row.action] ?? 0) + 1;
|
|
9263
|
+
return acc;
|
|
9264
|
+
}, {});
|
|
9002
9265
|
const runRetentionTotals = result.metadataReconcile.runMetadataRetention.runs.reduce((acc, row) => {
|
|
9003
9266
|
acc[row.action] = (acc[row.action] ?? 0) + 1;
|
|
9004
9267
|
return acc;
|
|
@@ -9016,10 +9279,15 @@ function reconcileRunsCli() {
|
|
|
9016
9279
|
total: result.metadataReconcile.runMetadataRetention.runs.length
|
|
9017
9280
|
}
|
|
9018
9281
|
},
|
|
9282
|
+
localPrAttentionReconcile: {
|
|
9283
|
+
totals: localPrAttentionTotals,
|
|
9284
|
+
total: result.localPrAttentionReconcile.workers.length
|
|
9285
|
+
},
|
|
9019
9286
|
finalizedRuns: result.finalizedRuns.length,
|
|
9020
9287
|
details: {
|
|
9021
9288
|
workers: result.workers,
|
|
9022
9289
|
metadataReconcile: result.metadataReconcile.workers,
|
|
9290
|
+
localPrAttentionReconcile: result.localPrAttentionReconcile.workers,
|
|
9023
9291
|
runMetadataRetention: result.metadataReconcile.runMetadataRetention.runs,
|
|
9024
9292
|
finalizedRuns: result.finalizedRuns
|
|
9025
9293
|
}
|
|
@@ -9034,13 +9302,13 @@ function reconcileRunsCli() {
|
|
|
9034
9302
|
function heartbeatByteLength(heartbeatPath) {
|
|
9035
9303
|
if (!heartbeatPath || !existsSync28(heartbeatPath)) return 0;
|
|
9036
9304
|
try {
|
|
9037
|
-
return
|
|
9305
|
+
return readFileSync15(heartbeatPath, "utf8").trim().length;
|
|
9038
9306
|
} catch {
|
|
9039
9307
|
return 0;
|
|
9040
9308
|
}
|
|
9041
9309
|
}
|
|
9042
9310
|
function workerEvidence(run, workerName) {
|
|
9043
|
-
const workerPath =
|
|
9311
|
+
const workerPath = path37.join(runDirectory(run.id), "workers", safeSlug(workerName), "worker.json");
|
|
9044
9312
|
const worker = readJson(workerPath, void 0);
|
|
9045
9313
|
if (!worker) {
|
|
9046
9314
|
return {
|
|
@@ -9097,7 +9365,7 @@ function aggregateRunAttention(workers) {
|
|
|
9097
9365
|
function countOpenWorkers(run) {
|
|
9098
9366
|
let open = 0;
|
|
9099
9367
|
for (const name of listRunWorkerNames(run)) {
|
|
9100
|
-
const workerPath =
|
|
9368
|
+
const workerPath = path37.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
|
|
9101
9369
|
const worker = readJson(workerPath, void 0);
|
|
9102
9370
|
if (!worker) continue;
|
|
9103
9371
|
const status = computeWorkerStatus(worker, { base: run.base, baseCommit: run.baseCommit });
|
|
@@ -9173,7 +9441,7 @@ function createRun(args) {
|
|
|
9173
9441
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9174
9442
|
workers: {}
|
|
9175
9443
|
};
|
|
9176
|
-
writeJson(
|
|
9444
|
+
writeJson(path38.join(dir, "run.json"), run);
|
|
9177
9445
|
console.log(JSON.stringify({ runId: id, runDir: dir, repo, base, baseCommit }, null, 2));
|
|
9178
9446
|
}
|
|
9179
9447
|
function listRuns() {
|
|
@@ -9184,8 +9452,32 @@ function failExists(message) {
|
|
|
9184
9452
|
process.exit(1);
|
|
9185
9453
|
}
|
|
9186
9454
|
|
|
9455
|
+
// src/run-resolve.ts
|
|
9456
|
+
function resolveHarnessRunByName(runName) {
|
|
9457
|
+
const name = runName.trim();
|
|
9458
|
+
if (!name) return null;
|
|
9459
|
+
const rows = buildRunListRows();
|
|
9460
|
+
return rows.find((row) => row.name === name && row.status !== "completed") ?? null;
|
|
9461
|
+
}
|
|
9462
|
+
function resolveHarnessRunCli(args) {
|
|
9463
|
+
const name = String(required(String(args.name || ""), "--name"));
|
|
9464
|
+
const hit = resolveHarnessRunByName(name);
|
|
9465
|
+
console.log(
|
|
9466
|
+
JSON.stringify(
|
|
9467
|
+
{
|
|
9468
|
+
runId: hit?.id ?? null,
|
|
9469
|
+
name,
|
|
9470
|
+
status: hit?.status ?? null,
|
|
9471
|
+
effectiveStatus: hit?.effectiveStatus ?? null
|
|
9472
|
+
},
|
|
9473
|
+
null,
|
|
9474
|
+
2
|
|
9475
|
+
)
|
|
9476
|
+
);
|
|
9477
|
+
}
|
|
9478
|
+
|
|
9187
9479
|
// src/sweep.ts
|
|
9188
|
-
import
|
|
9480
|
+
import path39 from "node:path";
|
|
9189
9481
|
async function sweepRun(args) {
|
|
9190
9482
|
const pipeline = args.pipeline === true || args.pipeline === "true";
|
|
9191
9483
|
try {
|
|
@@ -9198,7 +9490,7 @@ async function sweepRun(args) {
|
|
|
9198
9490
|
const releasedLocalOrphans = [];
|
|
9199
9491
|
for (const name of Object.keys(run.workers || {})) {
|
|
9200
9492
|
const worker = readJson(
|
|
9201
|
-
|
|
9493
|
+
path39.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
9202
9494
|
void 0
|
|
9203
9495
|
);
|
|
9204
9496
|
if (!worker || !worker.dispatched || !worker.taskId) continue;
|
|
@@ -9247,17 +9539,17 @@ async function sweepRun(args) {
|
|
|
9247
9539
|
|
|
9248
9540
|
// src/harness-storage-snapshot.ts
|
|
9249
9541
|
import { existsSync as existsSync31, readdirSync as readdirSync9, statSync as statSync7 } from "node:fs";
|
|
9250
|
-
import
|
|
9542
|
+
import path41 from "node:path";
|
|
9251
9543
|
|
|
9252
9544
|
// src/cleanup-dir-size.ts
|
|
9253
|
-
import { execFileSync as
|
|
9545
|
+
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
9254
9546
|
import { existsSync as existsSync30, readdirSync as readdirSync8, statSync as statSync6 } from "node:fs";
|
|
9255
|
-
import
|
|
9547
|
+
import path40 from "node:path";
|
|
9256
9548
|
var DEFAULT_DU_TIMEOUT_MS = 2500;
|
|
9257
9549
|
function directorySizeBytesDu(root, timeoutMs = DEFAULT_DU_TIMEOUT_MS) {
|
|
9258
9550
|
if (!existsSync30(root)) return 0;
|
|
9259
9551
|
try {
|
|
9260
|
-
const out =
|
|
9552
|
+
const out = execFileSync3("du", ["-sb", root], {
|
|
9261
9553
|
encoding: "utf8",
|
|
9262
9554
|
timeout: timeoutMs,
|
|
9263
9555
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -9286,7 +9578,7 @@ function directorySizeBytes(root, maxEntries = 5e4) {
|
|
|
9286
9578
|
}
|
|
9287
9579
|
for (const name of entries) {
|
|
9288
9580
|
if (seen++ > maxEntries) return null;
|
|
9289
|
-
const full =
|
|
9581
|
+
const full = path40.join(current, name);
|
|
9290
9582
|
let st;
|
|
9291
9583
|
try {
|
|
9292
9584
|
st = statSync6(full);
|
|
@@ -9338,7 +9630,7 @@ function harnessStorageSnapshot(opts = {}) {
|
|
|
9338
9630
|
for (const runEntry of entries) {
|
|
9339
9631
|
if (!runEntry.isDirectory()) continue;
|
|
9340
9632
|
runCount += 1;
|
|
9341
|
-
const runPath =
|
|
9633
|
+
const runPath = path41.join(worktreesDir, runEntry.name);
|
|
9342
9634
|
try {
|
|
9343
9635
|
const st = statSync7(runPath);
|
|
9344
9636
|
oldestMs = oldestMs === null ? st.mtimeMs : Math.min(oldestMs, st.mtimeMs);
|
|
@@ -9373,10 +9665,10 @@ function harnessStorageSnapshot(opts = {}) {
|
|
|
9373
9665
|
}
|
|
9374
9666
|
|
|
9375
9667
|
// src/cleanup.ts
|
|
9376
|
-
import
|
|
9668
|
+
import path53 from "node:path";
|
|
9377
9669
|
|
|
9378
9670
|
// src/cleanup-guards.ts
|
|
9379
|
-
import
|
|
9671
|
+
import path42 from "node:path";
|
|
9380
9672
|
|
|
9381
9673
|
// src/cleanup-build-cache-paths.ts
|
|
9382
9674
|
var HARNESS_BUILD_CACHE_RELATIVE_PATHS = [
|
|
@@ -9517,7 +9809,7 @@ function skipWorktreeRemoval(input) {
|
|
|
9517
9809
|
function skipDependencyCacheRemoval(input) {
|
|
9518
9810
|
const { indexed, nodeModulesAgeMs, ageMs, worktreePath, activeWorktreePaths, diskPressure } = input;
|
|
9519
9811
|
if (!diskPressure && ageMs < nodeModulesAgeMs) return "below_age_threshold";
|
|
9520
|
-
if (activeWorktreePaths.has(
|
|
9812
|
+
if (activeWorktreePaths.has(path42.resolve(worktreePath))) return "active_worker";
|
|
9521
9813
|
if (indexed && isWorkerProcessLive(indexed)) return "active_worker";
|
|
9522
9814
|
if (indexed && indexedWorktreeHasMaterialChanges(indexed)) return "dirty_worktree";
|
|
9523
9815
|
return null;
|
|
@@ -9544,11 +9836,11 @@ var LIVE_SKIP_REASONS = /* @__PURE__ */ new Set([
|
|
|
9544
9836
|
function collectPreservedLivePaths(actions, skips) {
|
|
9545
9837
|
const out = [];
|
|
9546
9838
|
const seen = /* @__PURE__ */ new Set();
|
|
9547
|
-
const push = (
|
|
9548
|
-
const key = `${
|
|
9839
|
+
const push = (path73, reason, detail) => {
|
|
9840
|
+
const key = `${path73}\0${reason}`;
|
|
9549
9841
|
if (seen.has(key) || out.length >= MAX_PRESERVED_LIVE_PATH_SAMPLES) return;
|
|
9550
9842
|
seen.add(key);
|
|
9551
|
-
out.push({ path:
|
|
9843
|
+
out.push({ path: path73, reason, ...detail ? { detail } : {} });
|
|
9552
9844
|
};
|
|
9553
9845
|
for (const skip2 of skips) {
|
|
9554
9846
|
if (!LIVE_SKIP_REASONS.has(skip2.reason)) continue;
|
|
@@ -9564,11 +9856,11 @@ function collectPreservedLivePaths(actions, skips) {
|
|
|
9564
9856
|
|
|
9565
9857
|
// src/cleanup-run-directory.ts
|
|
9566
9858
|
import { existsSync as existsSync33, readdirSync as readdirSync11, statSync as statSync9 } from "node:fs";
|
|
9567
|
-
import
|
|
9859
|
+
import path44 from "node:path";
|
|
9568
9860
|
|
|
9569
9861
|
// src/cleanup-active-worktrees.ts
|
|
9570
9862
|
import { existsSync as existsSync32, readdirSync as readdirSync10, statSync as statSync8 } from "node:fs";
|
|
9571
|
-
import
|
|
9863
|
+
import path43 from "node:path";
|
|
9572
9864
|
function workerHasRecentHarnessActivity(worker, now) {
|
|
9573
9865
|
const paths = [worker.heartbeatPath, worker.stdoutPath, worker.stderrPath];
|
|
9574
9866
|
for (const target of paths) {
|
|
@@ -9594,11 +9886,11 @@ function collectActiveWorktreeGuards(harnessRoots, now = Date.now()) {
|
|
|
9594
9886
|
let runHasLive = false;
|
|
9595
9887
|
for (const name of Object.keys(run.workers || {})) {
|
|
9596
9888
|
const worker = readJson(
|
|
9597
|
-
|
|
9889
|
+
path43.join(runDirectoryAt(harnessRoot, run.id), "workers", safeSlug(name), "worker.json"),
|
|
9598
9890
|
void 0
|
|
9599
9891
|
);
|
|
9600
9892
|
if (!worker?.worktreePath) continue;
|
|
9601
|
-
const worktreePath =
|
|
9893
|
+
const worktreePath = path43.resolve(worker.worktreePath);
|
|
9602
9894
|
if (!isActiveHarnessWorker2(worker, now)) continue;
|
|
9603
9895
|
runHasLive = true;
|
|
9604
9896
|
activeWorktreePaths.add(worktreePath);
|
|
@@ -9626,7 +9918,7 @@ function pathAgeMs(target, now) {
|
|
|
9626
9918
|
}
|
|
9627
9919
|
}
|
|
9628
9920
|
function loadRunStatus(harnessRoot, runId) {
|
|
9629
|
-
const runPath =
|
|
9921
|
+
const runPath = path44.join(harnessRoot, "runs", runId, "run.json");
|
|
9630
9922
|
if (!existsSync33(runPath)) return null;
|
|
9631
9923
|
return readJson(runPath, null);
|
|
9632
9924
|
}
|
|
@@ -9662,7 +9954,7 @@ function scanStaleRunDirectoryCandidates(opts) {
|
|
|
9662
9954
|
if (!runEntry.isDirectory()) continue;
|
|
9663
9955
|
const runId = runEntry.name;
|
|
9664
9956
|
if (opts.runIdFilter && runId !== opts.runIdFilter) continue;
|
|
9665
|
-
const runPath =
|
|
9957
|
+
const runPath = path44.join(opts.worktreesDir, runId);
|
|
9666
9958
|
if (!runDirectoryIsEmpty(runPath)) continue;
|
|
9667
9959
|
candidates.push({
|
|
9668
9960
|
kind: "remove_run_directory",
|
|
@@ -9716,20 +10008,20 @@ function pathHasForeignOwnedEntry(targetPath, maxEntries = 32) {
|
|
|
9716
10008
|
|
|
9717
10009
|
// src/cleanup-privileged-remove.ts
|
|
9718
10010
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
9719
|
-
import
|
|
10011
|
+
import path46 from "node:path";
|
|
9720
10012
|
|
|
9721
10013
|
// src/cleanup-harness-path-validate.ts
|
|
9722
|
-
import
|
|
10014
|
+
import path45 from "node:path";
|
|
9723
10015
|
function isHarnessDependencyCachePath(targetPath, harnessRoot, worktreesDir, cacheDirName) {
|
|
9724
|
-
const resolved =
|
|
9725
|
-
const suffix = `${
|
|
10016
|
+
const resolved = path45.resolve(targetPath);
|
|
10017
|
+
const suffix = `${path45.sep}${cacheDirName}`;
|
|
9726
10018
|
const cachePath = resolved.endsWith(suffix) ? resolved : null;
|
|
9727
10019
|
if (!cachePath) return "path_outside_harness";
|
|
9728
|
-
const rel =
|
|
9729
|
-
if (rel.startsWith("..") ||
|
|
9730
|
-
const parts = rel.split(
|
|
10020
|
+
const rel = path45.relative(worktreesDir, cachePath);
|
|
10021
|
+
if (rel.startsWith("..") || path45.isAbsolute(rel)) return "path_outside_harness";
|
|
10022
|
+
const parts = rel.split(path45.sep);
|
|
9731
10023
|
if (parts.length < 3 || parts[parts.length - 1] !== cacheDirName) return "path_outside_harness";
|
|
9732
|
-
if (!resolved.startsWith(
|
|
10024
|
+
if (!resolved.startsWith(path45.resolve(harnessRoot))) return "path_outside_harness";
|
|
9733
10025
|
return null;
|
|
9734
10026
|
}
|
|
9735
10027
|
function isHarnessNodeModulesPath(targetPath, harnessRoot, worktreesDir) {
|
|
@@ -9739,16 +10031,16 @@ function isHarnessNextCachePath(targetPath, harnessRoot, worktreesDir) {
|
|
|
9739
10031
|
return isHarnessDependencyCachePath(targetPath, harnessRoot, worktreesDir, ".next");
|
|
9740
10032
|
}
|
|
9741
10033
|
function isHarnessBuildCachePath(targetPath, harnessRoot, worktreesDir) {
|
|
9742
|
-
const resolved =
|
|
9743
|
-
const relToWt =
|
|
9744
|
-
if (relToWt.startsWith("..") ||
|
|
9745
|
-
const parts = relToWt.split(
|
|
10034
|
+
const resolved = path45.resolve(targetPath);
|
|
10035
|
+
const relToWt = path45.relative(worktreesDir, resolved);
|
|
10036
|
+
if (relToWt.startsWith("..") || path45.isAbsolute(relToWt)) return "path_outside_harness";
|
|
10037
|
+
const parts = relToWt.split(path45.sep);
|
|
9746
10038
|
if (parts.length < 3) return "path_outside_harness";
|
|
9747
|
-
if (!resolved.startsWith(
|
|
10039
|
+
if (!resolved.startsWith(path45.resolve(harnessRoot))) return "path_outside_harness";
|
|
9748
10040
|
return null;
|
|
9749
10041
|
}
|
|
9750
10042
|
function isHarnessGeneratedCachePath(targetPath, harnessRoot, worktreesDir) {
|
|
9751
|
-
const resolved =
|
|
10043
|
+
const resolved = path45.resolve(targetPath);
|
|
9752
10044
|
return isHarnessNodeModulesPath(resolved, harnessRoot, worktreesDir) === null || isHarnessNextCachePath(resolved, harnessRoot, worktreesDir) === null || isHarnessBuildCachePath(resolved, harnessRoot, worktreesDir) === null;
|
|
9753
10045
|
}
|
|
9754
10046
|
|
|
@@ -9782,12 +10074,12 @@ function tryPrivilegedReclaimHarnessCache(targetPath, harnessRoot, worktreesDir)
|
|
|
9782
10074
|
"chown",
|
|
9783
10075
|
"-R",
|
|
9784
10076
|
`${effectiveUid}:${effectiveGid}`,
|
|
9785
|
-
|
|
10077
|
+
path46.resolve(targetPath)
|
|
9786
10078
|
]);
|
|
9787
10079
|
if (chown.ok) {
|
|
9788
10080
|
return { ok: true, method: "chown_then_rm" };
|
|
9789
10081
|
}
|
|
9790
|
-
const rm = runSudoNonInteractive(["rm", "-rf",
|
|
10082
|
+
const rm = runSudoNonInteractive(["rm", "-rf", path46.resolve(targetPath)]);
|
|
9791
10083
|
if (rm.ok) {
|
|
9792
10084
|
return { ok: true, method: "sudo_rm" };
|
|
9793
10085
|
}
|
|
@@ -9989,7 +10281,7 @@ function removeWorktree(candidate, execute) {
|
|
|
9989
10281
|
|
|
9990
10282
|
// src/cleanup-scan.ts
|
|
9991
10283
|
import { existsSync as existsSync36, readdirSync as readdirSync13, statSync as statSync10 } from "node:fs";
|
|
9992
|
-
import
|
|
10284
|
+
import path47 from "node:path";
|
|
9993
10285
|
function pathAgeMs2(target, now) {
|
|
9994
10286
|
try {
|
|
9995
10287
|
const mtime = statSync10(target).mtimeMs;
|
|
@@ -9999,16 +10291,16 @@ function pathAgeMs2(target, now) {
|
|
|
9999
10291
|
}
|
|
10000
10292
|
}
|
|
10001
10293
|
function isPathInside(child, parent) {
|
|
10002
|
-
const rel =
|
|
10003
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
10294
|
+
const rel = path47.relative(parent, child);
|
|
10295
|
+
return rel === "" || !rel.startsWith("..") && !path47.isAbsolute(rel);
|
|
10004
10296
|
}
|
|
10005
10297
|
function collectBuildCacheForWorktree(worktreePath, opts, seen, meta) {
|
|
10006
10298
|
const out = [];
|
|
10007
10299
|
for (const rel of HARNESS_BUILD_CACHE_RELATIVE_PATHS) {
|
|
10008
10300
|
if (rel === ".next") continue;
|
|
10009
|
-
const target =
|
|
10301
|
+
const target = path47.join(worktreePath, rel);
|
|
10010
10302
|
if (!existsSync36(target)) continue;
|
|
10011
|
-
const resolved =
|
|
10303
|
+
const resolved = path47.resolve(target);
|
|
10012
10304
|
if (seen.has(resolved)) continue;
|
|
10013
10305
|
if (!isPathInside(resolved, opts.harnessRoot)) continue;
|
|
10014
10306
|
seen.add(resolved);
|
|
@@ -10040,10 +10332,10 @@ function scanBuildCacheCandidates(opts) {
|
|
|
10040
10332
|
if (!opts.includeOrphans || !existsSync36(opts.worktreesDir)) return candidates;
|
|
10041
10333
|
for (const runEntry of readdirSync13(opts.worktreesDir, { withFileTypes: true })) {
|
|
10042
10334
|
if (!runEntry.isDirectory()) continue;
|
|
10043
|
-
const runPath =
|
|
10335
|
+
const runPath = path47.join(opts.worktreesDir, runEntry.name);
|
|
10044
10336
|
for (const workerEntry of readdirSync13(runPath, { withFileTypes: true })) {
|
|
10045
10337
|
if (!workerEntry.isDirectory()) continue;
|
|
10046
|
-
const worktreePath =
|
|
10338
|
+
const worktreePath = path47.join(runPath, workerEntry.name);
|
|
10047
10339
|
candidates.push(
|
|
10048
10340
|
...collectBuildCacheForWorktree(worktreePath, opts, seen, {
|
|
10049
10341
|
runId: runEntry.name,
|
|
@@ -10081,12 +10373,12 @@ function scanWorktreeCandidates(opts) {
|
|
|
10081
10373
|
if (!orphanEnabled || !existsSync36(opts.worktreesDir)) return candidates;
|
|
10082
10374
|
const indexedPaths = /* @__PURE__ */ new Set();
|
|
10083
10375
|
for (const entry of opts.index.values()) {
|
|
10084
|
-
indexedPaths.add(
|
|
10376
|
+
indexedPaths.add(path47.resolve(entry.worktreePath));
|
|
10085
10377
|
}
|
|
10086
10378
|
for (const runEntry of readdirSync13(opts.worktreesDir, { withFileTypes: true })) {
|
|
10087
10379
|
if (!runEntry.isDirectory()) continue;
|
|
10088
10380
|
if (opts.runIdFilter && runEntry.name !== opts.runIdFilter) continue;
|
|
10089
|
-
const runPath =
|
|
10381
|
+
const runPath = path47.join(opts.worktreesDir, runEntry.name);
|
|
10090
10382
|
let workerEntries;
|
|
10091
10383
|
try {
|
|
10092
10384
|
workerEntries = readdirSync13(runPath, { withFileTypes: true });
|
|
@@ -10095,7 +10387,7 @@ function scanWorktreeCandidates(opts) {
|
|
|
10095
10387
|
}
|
|
10096
10388
|
for (const workerEntry of workerEntries) {
|
|
10097
10389
|
if (!workerEntry.isDirectory()) continue;
|
|
10098
|
-
const worktreePath =
|
|
10390
|
+
const worktreePath = path47.resolve(path47.join(runPath, workerEntry.name));
|
|
10099
10391
|
if (seen.has(worktreePath)) continue;
|
|
10100
10392
|
if (indexedPaths.has(worktreePath)) continue;
|
|
10101
10393
|
if (!isPathInside(worktreePath, opts.harnessRoot)) continue;
|
|
@@ -10115,7 +10407,7 @@ function scanWorktreeCandidates(opts) {
|
|
|
10115
10407
|
|
|
10116
10408
|
// src/cleanup-dependency-scan.ts
|
|
10117
10409
|
import { existsSync as existsSync37, readdirSync as readdirSync14, statSync as statSync11 } from "node:fs";
|
|
10118
|
-
import
|
|
10410
|
+
import path48 from "node:path";
|
|
10119
10411
|
var DEPENDENCY_CACHE_DIRS = [
|
|
10120
10412
|
{ dirName: "node_modules", kind: "remove_node_modules" },
|
|
10121
10413
|
{ dirName: ".next", kind: "remove_next_cache" }
|
|
@@ -10129,12 +10421,12 @@ function pathAgeMs3(target, now) {
|
|
|
10129
10421
|
}
|
|
10130
10422
|
}
|
|
10131
10423
|
function isPathInside2(child, parent) {
|
|
10132
|
-
const rel =
|
|
10133
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
10424
|
+
const rel = path48.relative(parent, child);
|
|
10425
|
+
return rel === "" || !rel.startsWith("..") && !path48.isAbsolute(rel);
|
|
10134
10426
|
}
|
|
10135
10427
|
function pushCandidate2(candidates, seen, opts, targetPath, kind, meta) {
|
|
10136
10428
|
if (!existsSync37(targetPath)) return;
|
|
10137
|
-
const resolved =
|
|
10429
|
+
const resolved = path48.resolve(targetPath);
|
|
10138
10430
|
if (seen.has(resolved)) return;
|
|
10139
10431
|
if (!isPathInside2(resolved, opts.harnessRoot)) return;
|
|
10140
10432
|
seen.add(resolved);
|
|
@@ -10151,7 +10443,7 @@ function pushCandidate2(candidates, seen, opts, targetPath, kind, meta) {
|
|
|
10151
10443
|
}
|
|
10152
10444
|
function scanWorktreeDependencyCaches(candidates, seen, opts, worktreePath, meta) {
|
|
10153
10445
|
for (const entry of DEPENDENCY_CACHE_DIRS) {
|
|
10154
|
-
pushCandidate2(candidates, seen, opts,
|
|
10446
|
+
pushCandidate2(candidates, seen, opts, path48.join(worktreePath, entry.dirName), entry.kind, meta);
|
|
10155
10447
|
}
|
|
10156
10448
|
}
|
|
10157
10449
|
function scanDependencyCacheCandidates(opts) {
|
|
@@ -10169,7 +10461,7 @@ function scanDependencyCacheCandidates(opts) {
|
|
|
10169
10461
|
for (const runEntry of readdirSync14(opts.worktreesDir, { withFileTypes: true })) {
|
|
10170
10462
|
if (!runEntry.isDirectory()) continue;
|
|
10171
10463
|
if (opts.runIdFilter && runEntry.name !== opts.runIdFilter) continue;
|
|
10172
|
-
const runPath =
|
|
10464
|
+
const runPath = path48.join(opts.worktreesDir, runEntry.name);
|
|
10173
10465
|
let workerEntries;
|
|
10174
10466
|
try {
|
|
10175
10467
|
workerEntries = readdirSync14(runPath, { withFileTypes: true });
|
|
@@ -10178,7 +10470,7 @@ function scanDependencyCacheCandidates(opts) {
|
|
|
10178
10470
|
}
|
|
10179
10471
|
for (const workerEntry of workerEntries) {
|
|
10180
10472
|
if (!workerEntry.isDirectory()) continue;
|
|
10181
|
-
const worktreePath =
|
|
10473
|
+
const worktreePath = path48.join(runPath, workerEntry.name);
|
|
10182
10474
|
scanWorktreeDependencyCaches(candidates, seen, opts, worktreePath, {
|
|
10183
10475
|
runId: runEntry.name,
|
|
10184
10476
|
worker: workerEntry.name
|
|
@@ -10190,7 +10482,7 @@ function scanDependencyCacheCandidates(opts) {
|
|
|
10190
10482
|
|
|
10191
10483
|
// src/cleanup-duplicate-worktrees.ts
|
|
10192
10484
|
import { existsSync as existsSync38, statSync as statSync12 } from "node:fs";
|
|
10193
|
-
import
|
|
10485
|
+
import path49 from "node:path";
|
|
10194
10486
|
function pathAgeMs4(target, now) {
|
|
10195
10487
|
try {
|
|
10196
10488
|
const mtime = statSync12(target).mtimeMs;
|
|
@@ -10220,8 +10512,8 @@ function parseWorktreePorcelain(output) {
|
|
|
10220
10512
|
return records;
|
|
10221
10513
|
}
|
|
10222
10514
|
function isUnderWorktreesDir(worktreePath, worktreesDir) {
|
|
10223
|
-
const rel =
|
|
10224
|
-
return rel !== "" && !rel.startsWith("..") && !
|
|
10515
|
+
const rel = path49.relative(path49.resolve(worktreesDir), path49.resolve(worktreePath));
|
|
10516
|
+
return rel !== "" && !rel.startsWith("..") && !path49.isAbsolute(rel);
|
|
10225
10517
|
}
|
|
10226
10518
|
function isCleanWorktree(worktreePath, repoRoot) {
|
|
10227
10519
|
try {
|
|
@@ -10237,11 +10529,11 @@ function scanDuplicateWorktreeCandidates(opts) {
|
|
|
10237
10529
|
if (!opts.includeOrphans || !existsSync38(opts.worktreesDir)) return [];
|
|
10238
10530
|
const repos = /* @__PURE__ */ new Set();
|
|
10239
10531
|
for (const entry of opts.index.values()) {
|
|
10240
|
-
if (entry.run.repo) repos.add(
|
|
10532
|
+
if (entry.run.repo) repos.add(path49.resolve(entry.run.repo));
|
|
10241
10533
|
}
|
|
10242
10534
|
const indexedPaths = /* @__PURE__ */ new Set();
|
|
10243
10535
|
for (const entry of opts.index.values()) {
|
|
10244
|
-
indexedPaths.add(
|
|
10536
|
+
indexedPaths.add(path49.resolve(entry.worktreePath));
|
|
10245
10537
|
}
|
|
10246
10538
|
const candidates = [];
|
|
10247
10539
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -10254,15 +10546,15 @@ function scanDuplicateWorktreeCandidates(opts) {
|
|
|
10254
10546
|
}
|
|
10255
10547
|
const worktrees = parseWorktreePorcelain(porcelain);
|
|
10256
10548
|
for (const wt of worktrees) {
|
|
10257
|
-
const resolved =
|
|
10258
|
-
if (resolved ===
|
|
10549
|
+
const resolved = path49.resolve(wt.path);
|
|
10550
|
+
if (resolved === path49.resolve(repoRoot)) continue;
|
|
10259
10551
|
if (!isUnderWorktreesDir(resolved, opts.worktreesDir)) continue;
|
|
10260
10552
|
if (indexedPaths.has(resolved)) continue;
|
|
10261
10553
|
if (seen.has(resolved)) continue;
|
|
10262
10554
|
if (!existsSync38(resolved)) continue;
|
|
10263
10555
|
if (!isCleanWorktree(resolved, repoRoot)) continue;
|
|
10264
|
-
const rel =
|
|
10265
|
-
const parts = rel.split(
|
|
10556
|
+
const rel = path49.relative(opts.worktreesDir, resolved);
|
|
10557
|
+
const parts = rel.split(path49.sep);
|
|
10266
10558
|
const runId = parts[0];
|
|
10267
10559
|
const worker = parts[1] ?? "unknown";
|
|
10268
10560
|
seen.add(resolved);
|
|
@@ -10281,12 +10573,12 @@ function scanDuplicateWorktreeCandidates(opts) {
|
|
|
10281
10573
|
}
|
|
10282
10574
|
|
|
10283
10575
|
// src/cleanup-worktree-index.ts
|
|
10284
|
-
import
|
|
10576
|
+
import path50 from "node:path";
|
|
10285
10577
|
function buildWorktreeIndexAt(harnessRoot) {
|
|
10286
10578
|
const index = /* @__PURE__ */ new Map();
|
|
10287
10579
|
for (const run of listRunRecordsForHarnessRoot(harnessRoot)) {
|
|
10288
10580
|
for (const name of Object.keys(run.workers || {})) {
|
|
10289
|
-
const workerPath =
|
|
10581
|
+
const workerPath = path50.join(
|
|
10290
10582
|
runDirectoryAt(harnessRoot, run.id),
|
|
10291
10583
|
"workers",
|
|
10292
10584
|
safeSlug(name),
|
|
@@ -10294,9 +10586,9 @@ function buildWorktreeIndexAt(harnessRoot) {
|
|
|
10294
10586
|
);
|
|
10295
10587
|
const worker = readJson(workerPath, void 0);
|
|
10296
10588
|
if (!worker?.worktreePath) continue;
|
|
10297
|
-
index.set(
|
|
10589
|
+
index.set(path50.resolve(worker.worktreePath), {
|
|
10298
10590
|
harnessRoot,
|
|
10299
|
-
worktreePath:
|
|
10591
|
+
worktreePath: path50.resolve(worker.worktreePath),
|
|
10300
10592
|
runId: run.id,
|
|
10301
10593
|
workerName: name,
|
|
10302
10594
|
run,
|
|
@@ -10366,14 +10658,14 @@ function resolvePipelineHarnessRetention(runId) {
|
|
|
10366
10658
|
|
|
10367
10659
|
// src/cleanup-orphan-safety.ts
|
|
10368
10660
|
import { existsSync as existsSync39, statSync as statSync13 } from "node:fs";
|
|
10369
|
-
import
|
|
10661
|
+
import path51 from "node:path";
|
|
10370
10662
|
var DEFAULT_HEARTBEAT_FRESH_MS = 30 * 60 * 1e3;
|
|
10371
10663
|
function assessOrphanWorktreeSafety(input) {
|
|
10372
10664
|
const now = input.now ?? Date.now();
|
|
10373
10665
|
const heartbeatFreshMs = input.heartbeatFreshMs ?? DEFAULT_HEARTBEAT_FRESH_MS;
|
|
10374
10666
|
if (!existsSync39(input.worktreePath)) return null;
|
|
10375
10667
|
if (input.runId && input.workerName) {
|
|
10376
|
-
const heartbeatPath =
|
|
10668
|
+
const heartbeatPath = path51.join(
|
|
10377
10669
|
input.harnessRoot,
|
|
10378
10670
|
"runs",
|
|
10379
10671
|
input.runId,
|
|
@@ -10387,7 +10679,7 @@ function assessOrphanWorktreeSafety(input) {
|
|
|
10387
10679
|
} catch {
|
|
10388
10680
|
}
|
|
10389
10681
|
}
|
|
10390
|
-
const gitDir =
|
|
10682
|
+
const gitDir = path51.join(input.worktreePath, ".git");
|
|
10391
10683
|
if (!existsSync39(gitDir)) return null;
|
|
10392
10684
|
const porcelain = gitCapture(input.worktreePath, ["status", "--porcelain"]);
|
|
10393
10685
|
if (porcelain.status !== 0) return "pr_or_unmerged_commits";
|
|
@@ -10419,10 +10711,10 @@ function assessOrphanWorktreeSafety(input) {
|
|
|
10419
10711
|
// src/cleanup-harness-roots.ts
|
|
10420
10712
|
import { existsSync as existsSync40 } from "node:fs";
|
|
10421
10713
|
import { homedir as homedir13 } from "node:os";
|
|
10422
|
-
import
|
|
10714
|
+
import path52 from "node:path";
|
|
10423
10715
|
var WELL_KNOWN_HARNESS_SCAN_ROOTS = [
|
|
10424
10716
|
"/var/tmp/kynver-harness",
|
|
10425
|
-
|
|
10717
|
+
path52.join(homedir13(), ".openclaw", "harness")
|
|
10426
10718
|
];
|
|
10427
10719
|
function addRoot(seen, roots, candidate) {
|
|
10428
10720
|
if (!candidate?.trim()) return;
|
|
@@ -10444,7 +10736,7 @@ function resolveHarnessScanRoots(options = {}) {
|
|
|
10444
10736
|
for (const candidate of extra ?? []) addRoot(seen, roots, candidate);
|
|
10445
10737
|
if (shouldScanWellKnownRoots(options)) {
|
|
10446
10738
|
for (const candidate of WELL_KNOWN_HARNESS_SCAN_ROOTS) {
|
|
10447
|
-
const resolved =
|
|
10739
|
+
const resolved = path52.resolve(candidate);
|
|
10448
10740
|
if (!seen.has(resolved) && existsSync40(resolved)) addRoot(seen, roots, resolved);
|
|
10449
10741
|
}
|
|
10450
10742
|
}
|
|
@@ -10599,9 +10891,9 @@ function mergeWorktreeIndexes(scanRoots) {
|
|
|
10599
10891
|
}
|
|
10600
10892
|
function worktreePathForCandidate(candidate, worktreesDir) {
|
|
10601
10893
|
if (candidate.runId && candidate.worker) {
|
|
10602
|
-
return
|
|
10894
|
+
return path53.join(worktreesDir, candidate.runId, candidate.worker);
|
|
10603
10895
|
}
|
|
10604
|
-
return
|
|
10896
|
+
return path53.resolve(candidate.path, "..");
|
|
10605
10897
|
}
|
|
10606
10898
|
function runHarnessCleanup(options = {}) {
|
|
10607
10899
|
let retention = resolveHarnessRetention(options);
|
|
@@ -10626,7 +10918,7 @@ function runHarnessCleanup(options = {}) {
|
|
|
10626
10918
|
for (const harnessRoot of paths.scanRoots) {
|
|
10627
10919
|
if (atSweepCap()) break;
|
|
10628
10920
|
emitCleanupProgress("root", harnessRoot);
|
|
10629
|
-
const worktreesDir =
|
|
10921
|
+
const worktreesDir = path53.join(harnessRoot, "worktrees");
|
|
10630
10922
|
const scanOpts = {
|
|
10631
10923
|
harnessRoot,
|
|
10632
10924
|
worktreesDir,
|
|
@@ -10639,7 +10931,7 @@ function runHarnessCleanup(options = {}) {
|
|
|
10639
10931
|
};
|
|
10640
10932
|
for (const raw of scanDependencyCacheCandidates(scanOpts)) {
|
|
10641
10933
|
if (atSweepCap()) break;
|
|
10642
|
-
const resolved =
|
|
10934
|
+
const resolved = path53.resolve(raw.path);
|
|
10643
10935
|
if (processedPaths.has(resolved)) continue;
|
|
10644
10936
|
processedPaths.add(resolved);
|
|
10645
10937
|
const candidate = { ...raw, path: resolved };
|
|
@@ -10650,7 +10942,7 @@ function runHarnessCleanup(options = {}) {
|
|
|
10650
10942
|
continue;
|
|
10651
10943
|
}
|
|
10652
10944
|
const worktreePath = worktreePathForCandidate(candidate, worktreesDir);
|
|
10653
|
-
const indexed = index.get(
|
|
10945
|
+
const indexed = index.get(path53.resolve(worktreePath)) ?? null;
|
|
10654
10946
|
const guardReason = skipDependencyCacheRemoval({
|
|
10655
10947
|
indexed,
|
|
10656
10948
|
includeOrphans: true,
|
|
@@ -10674,7 +10966,7 @@ function runHarnessCleanup(options = {}) {
|
|
|
10674
10966
|
}
|
|
10675
10967
|
for (const raw of scanBuildCacheCandidates(scanOpts)) {
|
|
10676
10968
|
if (atSweepCap()) break;
|
|
10677
|
-
const resolved =
|
|
10969
|
+
const resolved = path53.resolve(raw.path);
|
|
10678
10970
|
if (processedPaths.has(resolved)) continue;
|
|
10679
10971
|
processedPaths.add(resolved);
|
|
10680
10972
|
const candidate = { ...raw, path: resolved };
|
|
@@ -10685,7 +10977,7 @@ function runHarnessCleanup(options = {}) {
|
|
|
10685
10977
|
continue;
|
|
10686
10978
|
}
|
|
10687
10979
|
const worktreePath = worktreePathForCandidate(candidate, worktreesDir);
|
|
10688
|
-
const indexed = index.get(
|
|
10980
|
+
const indexed = index.get(path53.resolve(worktreePath)) ?? null;
|
|
10689
10981
|
const guardReason = skipBuildCacheRemoval({
|
|
10690
10982
|
indexed,
|
|
10691
10983
|
includeOrphans: true,
|
|
@@ -10715,11 +11007,11 @@ function runHarnessCleanup(options = {}) {
|
|
|
10715
11007
|
const worktreeSeen = /* @__PURE__ */ new Set();
|
|
10716
11008
|
for (const raw of worktreeCandidates) {
|
|
10717
11009
|
if (atSweepCap()) break;
|
|
10718
|
-
const resolved =
|
|
11010
|
+
const resolved = path53.resolve(raw.path);
|
|
10719
11011
|
if (worktreeSeen.has(resolved)) continue;
|
|
10720
11012
|
worktreeSeen.add(resolved);
|
|
10721
11013
|
const candidate = { ...raw, path: resolved };
|
|
10722
|
-
const indexed = index.get(
|
|
11014
|
+
const indexed = index.get(path53.resolve(candidate.path)) ?? null;
|
|
10723
11015
|
const orphanSafety = indexed ? null : assessOrphanWorktreeSafety({
|
|
10724
11016
|
worktreePath: candidate.path,
|
|
10725
11017
|
harnessRoot,
|
|
@@ -10729,7 +11021,7 @@ function runHarnessCleanup(options = {}) {
|
|
|
10729
11021
|
});
|
|
10730
11022
|
const guardSkip = skipWorktreeRemoval({
|
|
10731
11023
|
indexed,
|
|
10732
|
-
worktreePath:
|
|
11024
|
+
worktreePath: path53.resolve(candidate.path),
|
|
10733
11025
|
includeOrphans: retention.includeOrphans,
|
|
10734
11026
|
worktreesAgeMs: retention.worktreesAgeMs,
|
|
10735
11027
|
terminalWorktreesAgeMs: retention.terminalWorktreesAgeMs,
|
|
@@ -10761,11 +11053,11 @@ function runHarnessCleanup(options = {}) {
|
|
|
10761
11053
|
now: paths.now
|
|
10762
11054
|
})) {
|
|
10763
11055
|
if (atSweepCap()) break;
|
|
10764
|
-
const resolved =
|
|
11056
|
+
const resolved = path53.resolve(raw.path);
|
|
10765
11057
|
if (processedPaths.has(resolved)) continue;
|
|
10766
11058
|
processedPaths.add(resolved);
|
|
10767
11059
|
const candidate = { ...raw, path: resolved };
|
|
10768
|
-
const runId = candidate.runId ??
|
|
11060
|
+
const runId = candidate.runId ?? path53.basename(resolved);
|
|
10769
11061
|
const dirSkip = skipRunDirectoryRemoval({
|
|
10770
11062
|
harnessRoot,
|
|
10771
11063
|
runId,
|
|
@@ -10899,12 +11191,12 @@ function isPipelineCleanupEnabled() {
|
|
|
10899
11191
|
}
|
|
10900
11192
|
|
|
10901
11193
|
// src/cli.ts
|
|
10902
|
-
import { mkdirSync as
|
|
11194
|
+
import { mkdirSync as mkdirSync10, realpathSync } from "node:fs";
|
|
10903
11195
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
10904
11196
|
|
|
10905
11197
|
// src/discard-disposable.ts
|
|
10906
11198
|
import { existsSync as existsSync41, rmSync as rmSync4 } from "node:fs";
|
|
10907
|
-
import
|
|
11199
|
+
import path54 from "node:path";
|
|
10908
11200
|
function normalizeRelativePath2(value) {
|
|
10909
11201
|
const normalized = value.replace(/\\/g, "/").replace(/^\.\//, "").trim();
|
|
10910
11202
|
if (!normalized || normalized.startsWith("/") || normalized.includes("..")) {
|
|
@@ -10926,12 +11218,12 @@ function discardDisposableArtifacts(args) {
|
|
|
10926
11218
|
if (paths.length === 0) {
|
|
10927
11219
|
return { ok: false, removed: [], reason: "requires at least one --path" };
|
|
10928
11220
|
}
|
|
10929
|
-
const worktreeRoot =
|
|
11221
|
+
const worktreeRoot = path54.resolve(worker.worktreePath);
|
|
10930
11222
|
const removed = [];
|
|
10931
11223
|
for (const raw of paths) {
|
|
10932
11224
|
const rel = normalizeRelativePath2(raw);
|
|
10933
|
-
const abs =
|
|
10934
|
-
if (!abs.startsWith(worktreeRoot +
|
|
11225
|
+
const abs = path54.resolve(worktreeRoot, rel);
|
|
11226
|
+
if (!abs.startsWith(worktreeRoot + path54.sep) && abs !== worktreeRoot) {
|
|
10935
11227
|
return { ok: false, removed, reason: `path escapes worktree: ${raw}` };
|
|
10936
11228
|
}
|
|
10937
11229
|
if (!existsSync41(abs)) {
|
|
@@ -10998,7 +11290,7 @@ function validateDaemonInstallIdentity(config = loadUserConfig(), env = process.
|
|
|
10998
11290
|
// src/cron/cron-env.ts
|
|
10999
11291
|
import { existsSync as existsSync42 } from "node:fs";
|
|
11000
11292
|
import { homedir as homedir14 } from "node:os";
|
|
11001
|
-
import
|
|
11293
|
+
import path55 from "node:path";
|
|
11002
11294
|
function envFlag3(name, defaultValue) {
|
|
11003
11295
|
const raw = process.env[name]?.trim().toLowerCase();
|
|
11004
11296
|
if (!raw) return defaultValue;
|
|
@@ -11014,7 +11306,7 @@ function envInt(name, fallback, min = 1) {
|
|
|
11014
11306
|
function defaultKynverCronStorePath() {
|
|
11015
11307
|
const explicit = process.env.KYNVER_CRON_STORE_PATH?.trim() || process.env.OPENCLAW_CRON_STORE_PATH?.trim();
|
|
11016
11308
|
if (explicit) return explicit;
|
|
11017
|
-
return
|
|
11309
|
+
return path55.join(homedir14(), ".kynver", "agent-os-cron.json");
|
|
11018
11310
|
}
|
|
11019
11311
|
function defaultKynverCronStatePath(storePath = defaultKynverCronStorePath()) {
|
|
11020
11312
|
const explicit = process.env.KYNVER_CRON_TICK_STATE_PATH?.trim();
|
|
@@ -11087,12 +11379,12 @@ async function fireKynverCronJob(input) {
|
|
|
11087
11379
|
}
|
|
11088
11380
|
|
|
11089
11381
|
// src/cron/cron-lock.ts
|
|
11090
|
-
import { closeSync as closeSync6, existsSync as existsSync43, openSync as openSync6, readFileSync as
|
|
11382
|
+
import { closeSync as closeSync6, existsSync as existsSync43, openSync as openSync6, readFileSync as readFileSync16, unlinkSync as unlinkSync3, writeFileSync as writeFileSync4 } from "node:fs";
|
|
11091
11383
|
var STALE_LOCK_MS = 10 * 6e4;
|
|
11092
11384
|
function readLockInfo(lockPath) {
|
|
11093
11385
|
if (!existsSync43(lockPath)) return null;
|
|
11094
11386
|
try {
|
|
11095
|
-
const parsed = JSON.parse(
|
|
11387
|
+
const parsed = JSON.parse(readFileSync16(lockPath, "utf8"));
|
|
11096
11388
|
if (typeof parsed.pid === "number" && typeof parsed.at === "string") return parsed;
|
|
11097
11389
|
} catch {
|
|
11098
11390
|
return null;
|
|
@@ -11256,7 +11548,7 @@ async function loadCronJobs(storePath = defaultKynverCronStorePath()) {
|
|
|
11256
11548
|
// src/cron/cron-tick-state.ts
|
|
11257
11549
|
import { randomBytes } from "node:crypto";
|
|
11258
11550
|
import { promises as fs4 } from "node:fs";
|
|
11259
|
-
import
|
|
11551
|
+
import path56 from "node:path";
|
|
11260
11552
|
var EMPTY = { version: 1, jobs: {} };
|
|
11261
11553
|
async function readFileIfExists2(filePath) {
|
|
11262
11554
|
try {
|
|
@@ -11283,7 +11575,7 @@ async function loadCronTickState(statePath) {
|
|
|
11283
11575
|
return parseCronTickState(raw);
|
|
11284
11576
|
}
|
|
11285
11577
|
async function writeStateAtomic(statePath, state) {
|
|
11286
|
-
await fs4.mkdir(
|
|
11578
|
+
await fs4.mkdir(path56.dirname(statePath), { recursive: true });
|
|
11287
11579
|
const suffix = randomBytes(6).toString("hex");
|
|
11288
11580
|
const tmp = `${statePath}.tmp-${process.pid}-${Date.now()}-${suffix}`;
|
|
11289
11581
|
await fs4.writeFile(tmp, `${JSON.stringify(state, null, 2)}
|
|
@@ -11460,7 +11752,7 @@ async function runKynverCronTick(opts = {}) {
|
|
|
11460
11752
|
}
|
|
11461
11753
|
|
|
11462
11754
|
// src/pipeline-tick.ts
|
|
11463
|
-
import
|
|
11755
|
+
import path58 from "node:path";
|
|
11464
11756
|
|
|
11465
11757
|
// src/pipeline-dispatch.ts
|
|
11466
11758
|
var RESERVED_REVIEW_STARTS = 1;
|
|
@@ -11591,7 +11883,7 @@ function resolvePipelineMaxStarts(resourceGate, operatorTick) {
|
|
|
11591
11883
|
}
|
|
11592
11884
|
|
|
11593
11885
|
// src/plan-progress-daemon-sync.ts
|
|
11594
|
-
import
|
|
11886
|
+
import path57 from "node:path";
|
|
11595
11887
|
|
|
11596
11888
|
// src/plan-progress-sync.ts
|
|
11597
11889
|
async function syncPlanProgress(args) {
|
|
@@ -11615,7 +11907,7 @@ async function syncActiveWorkerPlanProgress(runId, args) {
|
|
|
11615
11907
|
const outcomes = [];
|
|
11616
11908
|
for (const name of Object.keys(run.workers || {})) {
|
|
11617
11909
|
const worker = readJson(
|
|
11618
|
-
|
|
11910
|
+
path57.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
11619
11911
|
void 0
|
|
11620
11912
|
);
|
|
11621
11913
|
if (!worker?.dispatched || !worker.taskId) continue;
|
|
@@ -11677,7 +11969,7 @@ async function completeFinishedWorkers(runId, args) {
|
|
|
11677
11969
|
const outcomes = [];
|
|
11678
11970
|
for (const name of Object.keys(run.workers || {})) {
|
|
11679
11971
|
const worker = readJson(
|
|
11680
|
-
|
|
11972
|
+
path58.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json"),
|
|
11681
11973
|
void 0
|
|
11682
11974
|
);
|
|
11683
11975
|
if (!worker?.taskId || worker.localOnly) continue;
|
|
@@ -11824,6 +12116,15 @@ async function runPipelineTick(args) {
|
|
|
11824
12116
|
var DEFAULT_INTERVAL_MS = 6e4;
|
|
11825
12117
|
var IDLE_INTERVAL_MS = 5 * 6e4;
|
|
11826
12118
|
var MAX_IDLE_STREAK = 10;
|
|
12119
|
+
var SLEEP_POLL_MS = 250;
|
|
12120
|
+
async function awaitDaemonBackoff(ms, isStopping) {
|
|
12121
|
+
let remaining = ms;
|
|
12122
|
+
while (remaining > 0 && !isStopping()) {
|
|
12123
|
+
const step = Math.min(SLEEP_POLL_MS, remaining);
|
|
12124
|
+
await sleepMsAsync(step);
|
|
12125
|
+
remaining -= step;
|
|
12126
|
+
}
|
|
12127
|
+
}
|
|
11827
12128
|
async function runDaemon(args) {
|
|
11828
12129
|
const runId = String(required(String(args.run || ""), "--run"));
|
|
11829
12130
|
const agentOsId = String(required(String(args.agentOsId || loadUserConfig().agentOsId || ""), "--agent-os-id"));
|
|
@@ -11881,17 +12182,17 @@ async function runDaemon(args) {
|
|
|
11881
12182
|
idleStreak = 0;
|
|
11882
12183
|
}
|
|
11883
12184
|
const backoff = idleStreak >= MAX_IDLE_STREAK ? IDLE_INTERVAL_MS : intervalMs;
|
|
11884
|
-
|
|
12185
|
+
await awaitDaemonBackoff(backoff, () => stopping);
|
|
11885
12186
|
} catch (error) {
|
|
11886
12187
|
console.error(JSON.stringify({ event: "daemon_tick_error", error: error.message }));
|
|
11887
|
-
|
|
12188
|
+
await awaitDaemonBackoff(intervalMs, () => stopping);
|
|
11888
12189
|
}
|
|
11889
12190
|
}
|
|
11890
12191
|
console.error(JSON.stringify({ event: "daemon_stop", runId, agentOsId }));
|
|
11891
12192
|
}
|
|
11892
12193
|
|
|
11893
12194
|
// src/plan-progress.ts
|
|
11894
|
-
import
|
|
12195
|
+
import path62 from "node:path";
|
|
11895
12196
|
|
|
11896
12197
|
// src/bounded-build/constants.ts
|
|
11897
12198
|
var DEFAULT_BUILD_MEM_BUDGET_BYTES = 1536 * 1024 * 1024;
|
|
@@ -12021,14 +12322,235 @@ function waitForBuildAdmission(timeoutMs, pollMs = 2e3, opts = {}) {
|
|
|
12021
12322
|
}
|
|
12022
12323
|
|
|
12023
12324
|
// src/bounded-build/exec.ts
|
|
12325
|
+
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
12326
|
+
|
|
12327
|
+
// src/heavy-verification/slot.ts
|
|
12328
|
+
import {
|
|
12329
|
+
closeSync as closeSync7,
|
|
12330
|
+
existsSync as existsSync44,
|
|
12331
|
+
mkdirSync as mkdirSync8,
|
|
12332
|
+
openSync as openSync7,
|
|
12333
|
+
readdirSync as readdirSync15,
|
|
12334
|
+
readFileSync as readFileSync17,
|
|
12335
|
+
unlinkSync as unlinkSync4,
|
|
12336
|
+
writeFileSync as writeFileSync5
|
|
12337
|
+
} from "node:fs";
|
|
12338
|
+
import path60 from "node:path";
|
|
12339
|
+
|
|
12340
|
+
// src/heavy-verification/paths.ts
|
|
12341
|
+
import { mkdirSync as mkdirSync7 } from "node:fs";
|
|
12342
|
+
import path59 from "node:path";
|
|
12343
|
+
function resolveHeavyVerificationRoot() {
|
|
12344
|
+
return path59.join(resolveKynverStateRoot(), "heavy-verification");
|
|
12345
|
+
}
|
|
12346
|
+
function heavyVerificationSlotsDir() {
|
|
12347
|
+
return path59.join(resolveHeavyVerificationRoot(), "slots");
|
|
12348
|
+
}
|
|
12349
|
+
function ensureHeavyVerificationDirs() {
|
|
12350
|
+
const dir = heavyVerificationSlotsDir();
|
|
12351
|
+
mkdirSync7(dir, { recursive: true });
|
|
12352
|
+
return dir;
|
|
12353
|
+
}
|
|
12354
|
+
|
|
12355
|
+
// src/heavy-verification/slot.ts
|
|
12356
|
+
var DEFAULT_HEAVY_VERIFICATION_STALE_MS = 2 * 60 * 6e4;
|
|
12357
|
+
var DEFAULT_HEAVY_VERIFICATION_MAX_CONCURRENT = 1;
|
|
12358
|
+
function positiveInt5(value, fallback) {
|
|
12359
|
+
const n = Number(value);
|
|
12360
|
+
if (!Number.isFinite(n) || n <= 0) return fallback;
|
|
12361
|
+
return Math.floor(n);
|
|
12362
|
+
}
|
|
12363
|
+
function isHeavyVerificationGateSkipped() {
|
|
12364
|
+
const v = process.env.KYNVER_HEAVY_VERIFICATION_SKIP?.trim().toLowerCase();
|
|
12365
|
+
return v === "1" || v === "true" || v === "yes";
|
|
12366
|
+
}
|
|
12367
|
+
function resolveHeavyVerificationMaxConcurrent() {
|
|
12368
|
+
const env = process.env.KYNVER_HEAVY_VERIFICATION_MAX_CONCURRENT;
|
|
12369
|
+
if (env) return positiveInt5(env, DEFAULT_HEAVY_VERIFICATION_MAX_CONCURRENT);
|
|
12370
|
+
return DEFAULT_HEAVY_VERIFICATION_MAX_CONCURRENT;
|
|
12371
|
+
}
|
|
12372
|
+
function indexedSlotId(index) {
|
|
12373
|
+
return `slot-${index}`;
|
|
12374
|
+
}
|
|
12375
|
+
function slotFilePath(slotId, slotsDir = heavyVerificationSlotsDir()) {
|
|
12376
|
+
return path60.join(slotsDir, `${slotId}.json`);
|
|
12377
|
+
}
|
|
12378
|
+
function readSlotRecord(filePath) {
|
|
12379
|
+
if (!existsSync44(filePath)) return null;
|
|
12380
|
+
try {
|
|
12381
|
+
const parsed = JSON.parse(readFileSync17(filePath, "utf8"));
|
|
12382
|
+
if (typeof parsed.slotId === "string" && typeof parsed.pid === "number" && typeof parsed.acquiredAt === "string" && typeof parsed.command === "string") {
|
|
12383
|
+
return parsed;
|
|
12384
|
+
}
|
|
12385
|
+
} catch {
|
|
12386
|
+
return null;
|
|
12387
|
+
}
|
|
12388
|
+
return null;
|
|
12389
|
+
}
|
|
12390
|
+
function slotIsStale(record3, staleMs = DEFAULT_HEAVY_VERIFICATION_STALE_MS) {
|
|
12391
|
+
if (!record3) return true;
|
|
12392
|
+
if (!isPidAlive(record3.pid)) return true;
|
|
12393
|
+
const atMs = Date.parse(record3.acquiredAt);
|
|
12394
|
+
if (Number.isNaN(atMs)) return true;
|
|
12395
|
+
return Date.now() - atMs > staleMs;
|
|
12396
|
+
}
|
|
12397
|
+
function reclaimStaleSlot(filePath, staleMs) {
|
|
12398
|
+
const record3 = readSlotRecord(filePath);
|
|
12399
|
+
if (!slotIsStale(record3, staleMs)) return;
|
|
12400
|
+
try {
|
|
12401
|
+
unlinkSync4(filePath);
|
|
12402
|
+
} catch {
|
|
12403
|
+
}
|
|
12404
|
+
}
|
|
12405
|
+
function ensureSlotsDir(slotsDir) {
|
|
12406
|
+
mkdirSync8(slotsDir, { recursive: true });
|
|
12407
|
+
return slotsDir;
|
|
12408
|
+
}
|
|
12409
|
+
function reclaimStaleHeavyVerificationSlots(opts = {}) {
|
|
12410
|
+
const slotsDir = ensureSlotsDir(opts.slotsDir ?? ensureHeavyVerificationDirs());
|
|
12411
|
+
const staleMs = opts.staleMs ?? DEFAULT_HEAVY_VERIFICATION_STALE_MS;
|
|
12412
|
+
let reclaimed = 0;
|
|
12413
|
+
for (const name of readdirSync15(slotsDir)) {
|
|
12414
|
+
if (!name.endsWith(".json")) continue;
|
|
12415
|
+
const filePath = path60.join(slotsDir, name);
|
|
12416
|
+
const before = existsSync44(filePath);
|
|
12417
|
+
reclaimStaleSlot(filePath, staleMs);
|
|
12418
|
+
if (before && !existsSync44(filePath)) reclaimed += 1;
|
|
12419
|
+
}
|
|
12420
|
+
return reclaimed;
|
|
12421
|
+
}
|
|
12422
|
+
function listActiveHeavyVerificationSlots(opts = {}) {
|
|
12423
|
+
const slotsDir = opts.slotsDir ?? ensureHeavyVerificationDirs();
|
|
12424
|
+
const staleMs = opts.staleMs ?? DEFAULT_HEAVY_VERIFICATION_STALE_MS;
|
|
12425
|
+
reclaimStaleHeavyVerificationSlots({ slotsDir, staleMs });
|
|
12426
|
+
const active = [];
|
|
12427
|
+
for (const name of readdirSync15(slotsDir)) {
|
|
12428
|
+
if (!name.endsWith(".json")) continue;
|
|
12429
|
+
const record3 = readSlotRecord(path60.join(slotsDir, name));
|
|
12430
|
+
if (record3 && !slotIsStale(record3, staleMs)) active.push(record3);
|
|
12431
|
+
}
|
|
12432
|
+
return active;
|
|
12433
|
+
}
|
|
12434
|
+
function countActiveHeavyVerificationSlots(opts = {}) {
|
|
12435
|
+
return listActiveHeavyVerificationSlots(opts).length;
|
|
12436
|
+
}
|
|
12437
|
+
function tryAcquireHeavyVerificationSlot(command, opts = {}) {
|
|
12438
|
+
if (isHeavyVerificationGateSkipped()) {
|
|
12439
|
+
return {
|
|
12440
|
+
admitted: true,
|
|
12441
|
+
slotId: null,
|
|
12442
|
+
activeSlots: 0,
|
|
12443
|
+
maxSlots: resolveHeavyVerificationMaxConcurrent(),
|
|
12444
|
+
reason: null
|
|
12445
|
+
};
|
|
12446
|
+
}
|
|
12447
|
+
const slotsDir = opts.slotsDir ?? ensureHeavyVerificationDirs();
|
|
12448
|
+
const staleMs = opts.staleMs ?? DEFAULT_HEAVY_VERIFICATION_STALE_MS;
|
|
12449
|
+
const maxSlots = opts.maxSlots ?? resolveHeavyVerificationMaxConcurrent();
|
|
12450
|
+
reclaimStaleHeavyVerificationSlots({ slotsDir, staleMs });
|
|
12451
|
+
for (let index = 0; index < maxSlots; index += 1) {
|
|
12452
|
+
const slotId = indexedSlotId(index);
|
|
12453
|
+
const filePath = slotFilePath(slotId, slotsDir);
|
|
12454
|
+
const existing = readSlotRecord(filePath);
|
|
12455
|
+
if (existing && slotIsStale(existing, staleMs)) {
|
|
12456
|
+
try {
|
|
12457
|
+
unlinkSync4(filePath);
|
|
12458
|
+
} catch {
|
|
12459
|
+
}
|
|
12460
|
+
} else if (existing && !slotIsStale(existing, staleMs)) {
|
|
12461
|
+
continue;
|
|
12462
|
+
}
|
|
12463
|
+
const record3 = {
|
|
12464
|
+
slotId,
|
|
12465
|
+
pid: process.pid,
|
|
12466
|
+
acquiredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12467
|
+
command
|
|
12468
|
+
};
|
|
12469
|
+
try {
|
|
12470
|
+
const fd = openSync7(filePath, "wx");
|
|
12471
|
+
writeFileSync5(fd, JSON.stringify(record3, null, 2), "utf8");
|
|
12472
|
+
closeSync7(fd);
|
|
12473
|
+
const activeSlots2 = countActiveHeavyVerificationSlots({ slotsDir, staleMs });
|
|
12474
|
+
return {
|
|
12475
|
+
admitted: true,
|
|
12476
|
+
slotId,
|
|
12477
|
+
activeSlots: activeSlots2,
|
|
12478
|
+
maxSlots,
|
|
12479
|
+
reason: null
|
|
12480
|
+
};
|
|
12481
|
+
} catch (err) {
|
|
12482
|
+
if (err.code === "EEXIST") {
|
|
12483
|
+
continue;
|
|
12484
|
+
}
|
|
12485
|
+
throw err;
|
|
12486
|
+
}
|
|
12487
|
+
}
|
|
12488
|
+
const activeSlots = countActiveHeavyVerificationSlots({ slotsDir, staleMs });
|
|
12489
|
+
return {
|
|
12490
|
+
admitted: false,
|
|
12491
|
+
slotId: null,
|
|
12492
|
+
activeSlots,
|
|
12493
|
+
maxSlots,
|
|
12494
|
+
reason: `heavy verification at capacity (${activeSlots}/${maxSlots} slots)`
|
|
12495
|
+
};
|
|
12496
|
+
}
|
|
12497
|
+
function releaseHeavyVerificationSlot(slotId, opts = {}) {
|
|
12498
|
+
if (!slotId) return;
|
|
12499
|
+
const filePath = slotFilePath(slotId, opts.slotsDir ?? heavyVerificationSlotsDir());
|
|
12500
|
+
try {
|
|
12501
|
+
unlinkSync4(filePath);
|
|
12502
|
+
} catch {
|
|
12503
|
+
}
|
|
12504
|
+
}
|
|
12505
|
+
function assessHeavyVerificationGate(command, opts = {}) {
|
|
12506
|
+
if (isHeavyVerificationGateSkipped()) {
|
|
12507
|
+
return {
|
|
12508
|
+
admitted: true,
|
|
12509
|
+
slotId: null,
|
|
12510
|
+
activeSlots: 0,
|
|
12511
|
+
maxSlots: resolveHeavyVerificationMaxConcurrent(),
|
|
12512
|
+
reason: null
|
|
12513
|
+
};
|
|
12514
|
+
}
|
|
12515
|
+
const slotsDir = opts.slotsDir ?? ensureHeavyVerificationDirs();
|
|
12516
|
+
const staleMs = opts.staleMs ?? DEFAULT_HEAVY_VERIFICATION_STALE_MS;
|
|
12517
|
+
const maxSlots = opts.maxSlots ?? resolveHeavyVerificationMaxConcurrent();
|
|
12518
|
+
reclaimStaleHeavyVerificationSlots({ slotsDir, staleMs });
|
|
12519
|
+
const activeSlots = countActiveHeavyVerificationSlots({ slotsDir, staleMs });
|
|
12520
|
+
const admitted = activeSlots < maxSlots;
|
|
12521
|
+
return {
|
|
12522
|
+
admitted,
|
|
12523
|
+
slotId: null,
|
|
12524
|
+
activeSlots,
|
|
12525
|
+
maxSlots,
|
|
12526
|
+
reason: admitted ? null : `heavy verification at capacity (${activeSlots}/${maxSlots} slots); waiting for ${command}`
|
|
12527
|
+
};
|
|
12528
|
+
}
|
|
12529
|
+
|
|
12530
|
+
// src/heavy-verification/gate.ts
|
|
12024
12531
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
12532
|
+
function sleepMs3(ms) {
|
|
12533
|
+
if (ms <= 0) return;
|
|
12534
|
+
spawnSync8(process.execPath, ["-e", `const d=Date.now()+${Math.floor(ms)};while(Date.now()<d);`], {
|
|
12535
|
+
stdio: "ignore"
|
|
12536
|
+
});
|
|
12537
|
+
}
|
|
12538
|
+
function waitForHeavyVerificationSlot(command, timeoutMs, pollMs = 2e3, opts = {}) {
|
|
12539
|
+
const deadline = Date.now() + Math.max(0, timeoutMs);
|
|
12540
|
+
let verdict = tryAcquireHeavyVerificationSlot(command, opts);
|
|
12541
|
+
while (!verdict.admitted && Date.now() < deadline) {
|
|
12542
|
+
sleepMs3(Math.min(pollMs, deadline - Date.now()));
|
|
12543
|
+
verdict = tryAcquireHeavyVerificationSlot(command, opts);
|
|
12544
|
+
}
|
|
12545
|
+
return verdict;
|
|
12546
|
+
}
|
|
12025
12547
|
|
|
12026
12548
|
// src/harness-worktree-build-guard.ts
|
|
12027
|
-
import
|
|
12549
|
+
import path61 from "node:path";
|
|
12028
12550
|
function isPathUnderHarnessWorktree(cwd) {
|
|
12029
12551
|
const worktreesDir = harnessWorktreesDir(resolveHarnessRoot());
|
|
12030
|
-
const rel =
|
|
12031
|
-
return rel.length > 0 && !rel.startsWith("..") && !
|
|
12552
|
+
const rel = path61.relative(worktreesDir, path61.resolve(cwd));
|
|
12553
|
+
return rel.length > 0 && !rel.startsWith("..") && !path61.isAbsolute(rel);
|
|
12032
12554
|
}
|
|
12033
12555
|
function assessHarnessWorktreeBuildGuard(cwd) {
|
|
12034
12556
|
if (!isPathUnderHarnessWorktree(cwd)) return { ok: true };
|
|
@@ -12052,7 +12574,7 @@ function envArgv(env) {
|
|
|
12052
12574
|
return out;
|
|
12053
12575
|
}
|
|
12054
12576
|
function runSpawn(argv, opts) {
|
|
12055
|
-
const res =
|
|
12577
|
+
const res = spawnSync9(argv[0], argv.slice(1), {
|
|
12056
12578
|
cwd: opts.cwd,
|
|
12057
12579
|
env: opts.env,
|
|
12058
12580
|
encoding: "utf8",
|
|
@@ -12068,8 +12590,25 @@ function runSpawn(argv, opts) {
|
|
|
12068
12590
|
}
|
|
12069
12591
|
function runBoundedBuildCheck(input) {
|
|
12070
12592
|
const waitMs = input.waitForAdmissionMs ?? 6e5;
|
|
12593
|
+
const verificationGate = waitMs > 0 ? waitForHeavyVerificationSlot(input.command, waitMs) : tryAcquireOrAssessVerificationGate(input.command);
|
|
12594
|
+
if (!verificationGate.admitted) {
|
|
12595
|
+
return {
|
|
12596
|
+
ok: false,
|
|
12597
|
+
exitCode: 1,
|
|
12598
|
+
stdout: "",
|
|
12599
|
+
stderr: verificationGate.reason ?? "heavy verification gate denied",
|
|
12600
|
+
admitted: false,
|
|
12601
|
+
wrappedWithSystemd: false,
|
|
12602
|
+
nodeOptionsFlag: formatNodeOptionsFlag(),
|
|
12603
|
+
admission: assessBuildAdmission(),
|
|
12604
|
+
verificationGate,
|
|
12605
|
+
command: input.command
|
|
12606
|
+
};
|
|
12607
|
+
}
|
|
12608
|
+
const slotId = verificationGate.slotId;
|
|
12071
12609
|
const admission = waitMs > 0 ? waitForBuildAdmission(waitMs) : assessBuildAdmission();
|
|
12072
12610
|
if (!admission.admitted) {
|
|
12611
|
+
releaseHeavyVerificationSlot(slotId);
|
|
12073
12612
|
return {
|
|
12074
12613
|
ok: false,
|
|
12075
12614
|
exitCode: 1,
|
|
@@ -12079,11 +12618,13 @@ function runBoundedBuildCheck(input) {
|
|
|
12079
12618
|
wrappedWithSystemd: false,
|
|
12080
12619
|
nodeOptionsFlag: formatNodeOptionsFlag(),
|
|
12081
12620
|
admission,
|
|
12621
|
+
verificationGate,
|
|
12082
12622
|
command: input.command
|
|
12083
12623
|
};
|
|
12084
12624
|
}
|
|
12085
12625
|
const worktreeGuard = assessHarnessWorktreeBuildGuard(input.cwd);
|
|
12086
12626
|
if (!worktreeGuard.ok) {
|
|
12627
|
+
releaseHeavyVerificationSlot(slotId);
|
|
12087
12628
|
return {
|
|
12088
12629
|
ok: false,
|
|
12089
12630
|
exitCode: 1,
|
|
@@ -12093,6 +12634,7 @@ function runBoundedBuildCheck(input) {
|
|
|
12093
12634
|
wrappedWithSystemd: false,
|
|
12094
12635
|
nodeOptionsFlag: formatNodeOptionsFlag(),
|
|
12095
12636
|
admission,
|
|
12637
|
+
verificationGate,
|
|
12096
12638
|
command: input.command
|
|
12097
12639
|
};
|
|
12098
12640
|
}
|
|
@@ -12125,12 +12667,19 @@ function runBoundedBuildCheck(input) {
|
|
|
12125
12667
|
wrappedWithSystemd: useSystemd,
|
|
12126
12668
|
nodeOptionsFlag,
|
|
12127
12669
|
admission,
|
|
12670
|
+
verificationGate,
|
|
12128
12671
|
command: input.command
|
|
12129
12672
|
};
|
|
12130
12673
|
} finally {
|
|
12131
12674
|
registerBuildEnd();
|
|
12675
|
+
releaseHeavyVerificationSlot(slotId);
|
|
12132
12676
|
}
|
|
12133
12677
|
}
|
|
12678
|
+
function tryAcquireOrAssessVerificationGate(command) {
|
|
12679
|
+
const acquired = waitForHeavyVerificationSlot(command, 0);
|
|
12680
|
+
if (acquired.admitted) return acquired;
|
|
12681
|
+
return { ...assessHeavyVerificationGate(command), slotId: null };
|
|
12682
|
+
}
|
|
12134
12683
|
|
|
12135
12684
|
// src/harness-verify.ts
|
|
12136
12685
|
var DEFAULT_HARNESS_VERIFY_COMMANDS = ["npm run typecheck", "npm run test"];
|
|
@@ -12213,7 +12762,7 @@ async function emitPlanProgress(args) {
|
|
|
12213
12762
|
}
|
|
12214
12763
|
function verifyPlanLocal(args) {
|
|
12215
12764
|
const worktree = required(args.worktree ? String(args.worktree) : void 0, "worktree");
|
|
12216
|
-
const cwd =
|
|
12765
|
+
const cwd = path62.resolve(worktree);
|
|
12217
12766
|
const summary = runHarnessVerifyCommands(cwd);
|
|
12218
12767
|
const emitJson = args.json === true || args.json === "true";
|
|
12219
12768
|
const payload = { passed: summary.passed, worktree: cwd, steps: summary.steps };
|
|
@@ -12262,9 +12811,9 @@ async function verifyPlan(args) {
|
|
|
12262
12811
|
}
|
|
12263
12812
|
|
|
12264
12813
|
// src/harness-verify-cli.ts
|
|
12265
|
-
import
|
|
12814
|
+
import path63 from "node:path";
|
|
12266
12815
|
function runHarnessVerifyCli(args) {
|
|
12267
|
-
const cwd =
|
|
12816
|
+
const cwd = path63.resolve(required(args.worktree ? String(args.worktree) : void 0, "worktree"));
|
|
12268
12817
|
const emitJson = args.json === true || args.json === "true" || args.emitJson === true || args.emitJson === "true";
|
|
12269
12818
|
const commands = [];
|
|
12270
12819
|
const rawCmd = args.command;
|
|
@@ -12292,6 +12841,7 @@ function runHarnessVerifyCli(args) {
|
|
|
12292
12841
|
wrappedWithSystemd: s.result.wrappedWithSystemd,
|
|
12293
12842
|
nodeOptionsFlag: s.result.nodeOptionsFlag,
|
|
12294
12843
|
admission: s.result.admission,
|
|
12844
|
+
verificationGate: s.result.verificationGate,
|
|
12295
12845
|
stderr: s.result.stderr.slice(0, 4e3)
|
|
12296
12846
|
}))
|
|
12297
12847
|
};
|
|
@@ -12308,7 +12858,7 @@ function runHarnessVerifyCli(args) {
|
|
|
12308
12858
|
}
|
|
12309
12859
|
|
|
12310
12860
|
// src/plan-persist-cli.ts
|
|
12311
|
-
import { readFileSync as
|
|
12861
|
+
import { readFileSync as readFileSync18 } from "node:fs";
|
|
12312
12862
|
var OPERATIONS = ["create", "add_version", "update_metadata"];
|
|
12313
12863
|
var FAILURE_KINDS = [
|
|
12314
12864
|
"approval_guard",
|
|
@@ -12320,7 +12870,7 @@ var FAILURE_KINDS = [
|
|
|
12320
12870
|
function readBodyArg(args) {
|
|
12321
12871
|
const bodyFile = args.bodyFile ? String(args.bodyFile) : void 0;
|
|
12322
12872
|
if (bodyFile) {
|
|
12323
|
-
return { body:
|
|
12873
|
+
return { body: readFileSync18(bodyFile, "utf8"), bodyPathHint: bodyFile };
|
|
12324
12874
|
}
|
|
12325
12875
|
const inline = args.body ? String(args.body) : void 0;
|
|
12326
12876
|
if (inline) return { body: inline };
|
|
@@ -12700,7 +13250,7 @@ ${text.slice(0, 800)}`,
|
|
|
12700
13250
|
}
|
|
12701
13251
|
|
|
12702
13252
|
// src/monitor/monitor.service.ts
|
|
12703
|
-
import
|
|
13253
|
+
import path65 from "node:path";
|
|
12704
13254
|
|
|
12705
13255
|
// src/monitor/monitor.classify.ts
|
|
12706
13256
|
function classifyWorkerHealth(input) {
|
|
@@ -12752,19 +13302,19 @@ function classifyWorkerHealth(input) {
|
|
|
12752
13302
|
}
|
|
12753
13303
|
|
|
12754
13304
|
// src/monitor/monitor.store.ts
|
|
12755
|
-
import { existsSync as
|
|
12756
|
-
import
|
|
13305
|
+
import { existsSync as existsSync45, mkdirSync as mkdirSync9, readdirSync as readdirSync16, unlinkSync as unlinkSync5 } from "node:fs";
|
|
13306
|
+
import path64 from "node:path";
|
|
12757
13307
|
function monitorsDir() {
|
|
12758
13308
|
const { harnessRoot } = getHarnessPaths();
|
|
12759
|
-
const dir =
|
|
12760
|
-
|
|
13309
|
+
const dir = path64.join(harnessRoot, "monitors");
|
|
13310
|
+
mkdirSync9(dir, { recursive: true });
|
|
12761
13311
|
return dir;
|
|
12762
13312
|
}
|
|
12763
13313
|
function monitorIdFor(runId, workerName) {
|
|
12764
13314
|
return workerName ? `${safeSlug(runId)}--${safeSlug(workerName)}` : safeSlug(runId);
|
|
12765
13315
|
}
|
|
12766
13316
|
function monitorPath(monitorId) {
|
|
12767
|
-
return
|
|
13317
|
+
return path64.join(monitorsDir(), `${monitorId}.json`);
|
|
12768
13318
|
}
|
|
12769
13319
|
function loadMonitorSession(monitorId) {
|
|
12770
13320
|
return readJson(monitorPath(monitorId), void 0);
|
|
@@ -12774,18 +13324,18 @@ function saveMonitorSession(session) {
|
|
|
12774
13324
|
}
|
|
12775
13325
|
function deleteMonitorSession(monitorId) {
|
|
12776
13326
|
const file = monitorPath(monitorId);
|
|
12777
|
-
if (!
|
|
12778
|
-
|
|
13327
|
+
if (!existsSync45(file)) return false;
|
|
13328
|
+
unlinkSync5(file);
|
|
12779
13329
|
return true;
|
|
12780
13330
|
}
|
|
12781
13331
|
function listMonitorSessions() {
|
|
12782
13332
|
const dir = monitorsDir();
|
|
12783
|
-
if (!
|
|
13333
|
+
if (!existsSync45(dir)) return [];
|
|
12784
13334
|
const entries = [];
|
|
12785
|
-
for (const name of
|
|
13335
|
+
for (const name of readdirSync16(dir)) {
|
|
12786
13336
|
if (!name.endsWith(".json")) continue;
|
|
12787
13337
|
const session = readJson(
|
|
12788
|
-
|
|
13338
|
+
path64.join(dir, name),
|
|
12789
13339
|
void 0
|
|
12790
13340
|
);
|
|
12791
13341
|
if (!session?.monitorId) continue;
|
|
@@ -12876,7 +13426,7 @@ async function fetchTaskLeasesForWorkers(input) {
|
|
|
12876
13426
|
// src/monitor/monitor.service.ts
|
|
12877
13427
|
function workerRecord2(runId, name) {
|
|
12878
13428
|
return readJson(
|
|
12879
|
-
|
|
13429
|
+
path65.join(runDirectory(runId), "workers", safeSlug(name), "worker.json"),
|
|
12880
13430
|
void 0
|
|
12881
13431
|
);
|
|
12882
13432
|
}
|
|
@@ -13082,21 +13632,21 @@ async function runMonitorLoop(args) {
|
|
|
13082
13632
|
|
|
13083
13633
|
// src/monitor/monitor-spawn.ts
|
|
13084
13634
|
import { spawn as spawn6 } from "node:child_process";
|
|
13085
|
-
import { closeSync as
|
|
13086
|
-
import
|
|
13635
|
+
import { closeSync as closeSync8, existsSync as existsSync46, openSync as openSync8 } from "node:fs";
|
|
13636
|
+
import path66 from "node:path";
|
|
13087
13637
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
13088
13638
|
function resolveDefaultCliPath2() {
|
|
13089
|
-
return
|
|
13639
|
+
return path66.join(fileURLToPath4(new URL(".", import.meta.url)), "cli.js");
|
|
13090
13640
|
}
|
|
13091
13641
|
function spawnMonitorSidecar(opts) {
|
|
13092
13642
|
const cliPath = opts.cliPath ?? resolveDefaultCliPath2();
|
|
13093
|
-
if (!
|
|
13643
|
+
if (!existsSync46(cliPath)) return void 0;
|
|
13094
13644
|
const monitorId = monitorIdFor(opts.runId, opts.workerName);
|
|
13095
13645
|
const { harnessRoot } = getHarnessPaths();
|
|
13096
|
-
const logPath =
|
|
13646
|
+
const logPath = path66.join(harnessRoot, "monitors", `${monitorId}.log`);
|
|
13097
13647
|
let logFd;
|
|
13098
13648
|
try {
|
|
13099
|
-
logFd =
|
|
13649
|
+
logFd = openSync8(logPath, "a");
|
|
13100
13650
|
} catch {
|
|
13101
13651
|
logFd = void 0;
|
|
13102
13652
|
}
|
|
@@ -13136,7 +13686,7 @@ function spawnMonitorSidecar(opts) {
|
|
|
13136
13686
|
env: process.env
|
|
13137
13687
|
})
|
|
13138
13688
|
);
|
|
13139
|
-
if (logFd !== void 0)
|
|
13689
|
+
if (logFd !== void 0) closeSync8(logFd);
|
|
13140
13690
|
child.unref();
|
|
13141
13691
|
const session = {
|
|
13142
13692
|
monitorId,
|
|
@@ -13153,7 +13703,7 @@ function spawnMonitorSidecar(opts) {
|
|
|
13153
13703
|
} catch {
|
|
13154
13704
|
if (logFd !== void 0) {
|
|
13155
13705
|
try {
|
|
13156
|
-
|
|
13706
|
+
closeSync8(logFd);
|
|
13157
13707
|
} catch {
|
|
13158
13708
|
}
|
|
13159
13709
|
}
|
|
@@ -13213,7 +13763,7 @@ async function monitorTickCli(args) {
|
|
|
13213
13763
|
}
|
|
13214
13764
|
|
|
13215
13765
|
// src/post-restart-unblock.ts
|
|
13216
|
-
import
|
|
13766
|
+
import path67 from "node:path";
|
|
13217
13767
|
function skip(runId, worker, taskId, agentOsId, leaseOwner, reason) {
|
|
13218
13768
|
return { runId, worker, taskId, agentOsId, leaseOwner, action: "skipped", reason };
|
|
13219
13769
|
}
|
|
@@ -13226,7 +13776,7 @@ async function postRestartUnblock(args) {
|
|
|
13226
13776
|
const errors = [];
|
|
13227
13777
|
for (const run of listRunRecords()) {
|
|
13228
13778
|
for (const name of Object.keys(run.workers ?? {})) {
|
|
13229
|
-
const workerPath =
|
|
13779
|
+
const workerPath = path67.join(runDirectory(run.id), "workers", safeSlug(name), "worker.json");
|
|
13230
13780
|
const worker = readJson(workerPath, void 0);
|
|
13231
13781
|
if (!worker) {
|
|
13232
13782
|
skipped.push(skip(run.id, name, "", "", "", "worker.json missing"));
|
|
@@ -13338,9 +13888,9 @@ async function postRestartUnblockCli(args) {
|
|
|
13338
13888
|
}
|
|
13339
13889
|
|
|
13340
13890
|
// src/default-repo-cli.ts
|
|
13341
|
-
import
|
|
13891
|
+
import path68 from "node:path";
|
|
13342
13892
|
import { homedir as homedir15 } from "node:os";
|
|
13343
|
-
var CONFIG_FILE2 =
|
|
13893
|
+
var CONFIG_FILE2 = path68.join(homedir15(), ".kynver", "config.json");
|
|
13344
13894
|
function ensureDefaultRepo(opts) {
|
|
13345
13895
|
const existing = loadUserConfig();
|
|
13346
13896
|
const resolved = resolveDefaultRepo({ ...opts, config: existing });
|
|
@@ -13421,16 +13971,16 @@ function summarizeResolvedDefaultRepo(resolved) {
|
|
|
13421
13971
|
}
|
|
13422
13972
|
|
|
13423
13973
|
// src/doctor/runtime-takeover.ts
|
|
13424
|
-
import
|
|
13974
|
+
import path70 from "node:path";
|
|
13425
13975
|
|
|
13426
13976
|
// src/doctor/runtime-takeover.probes.ts
|
|
13427
|
-
import { accessSync, constants, existsSync as
|
|
13977
|
+
import { accessSync, constants, existsSync as existsSync47, readFileSync as readFileSync19 } from "node:fs";
|
|
13428
13978
|
import { homedir as homedir16 } from "node:os";
|
|
13429
|
-
import
|
|
13430
|
-
import { spawnSync as
|
|
13979
|
+
import path69 from "node:path";
|
|
13980
|
+
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
13431
13981
|
function captureCommand(bin, args) {
|
|
13432
13982
|
try {
|
|
13433
|
-
const res =
|
|
13983
|
+
const res = spawnSync10(bin, args, { encoding: "utf8" });
|
|
13434
13984
|
const stdout = (res.stdout || "").trim();
|
|
13435
13985
|
const stderr = (res.stderr || "").trim();
|
|
13436
13986
|
const ok = res.status === 0;
|
|
@@ -13455,7 +14005,7 @@ function tokenPrefix(token) {
|
|
|
13455
14005
|
return trimmed.length <= 12 ? `${trimmed}\u2026` : `${trimmed.slice(0, 12)}\u2026`;
|
|
13456
14006
|
}
|
|
13457
14007
|
function isWritable(target) {
|
|
13458
|
-
if (!
|
|
14008
|
+
if (!existsSync47(target)) return false;
|
|
13459
14009
|
try {
|
|
13460
14010
|
accessSync(target, constants.W_OK);
|
|
13461
14011
|
return true;
|
|
@@ -13468,15 +14018,15 @@ var defaultRuntimeTakeoverProbes = {
|
|
|
13468
14018
|
commandOnPath: (bin) => captureCommand(process.platform === "win32" ? "where" : "which", [bin]),
|
|
13469
14019
|
kynverVersion: (bin) => captureCommand(bin, ["--version"]),
|
|
13470
14020
|
loadConfig: () => loadUserConfig(),
|
|
13471
|
-
configFilePath: () =>
|
|
13472
|
-
credentialsFilePath: () =>
|
|
14021
|
+
configFilePath: () => path69.join(homedir16(), ".kynver", "config.json"),
|
|
14022
|
+
credentialsFilePath: () => path69.join(homedir16(), ".kynver", "credentials"),
|
|
13473
14023
|
readCredentials: () => {
|
|
13474
|
-
const credPath =
|
|
13475
|
-
if (!
|
|
14024
|
+
const credPath = path69.join(homedir16(), ".kynver", "credentials");
|
|
14025
|
+
if (!existsSync47(credPath)) {
|
|
13476
14026
|
return { hasApiKey: false };
|
|
13477
14027
|
}
|
|
13478
14028
|
try {
|
|
13479
|
-
const parsed = JSON.parse(
|
|
14029
|
+
const parsed = JSON.parse(readFileSync19(credPath, "utf8"));
|
|
13480
14030
|
return {
|
|
13481
14031
|
hasApiKey: Boolean(parsed.apiKey?.trim()),
|
|
13482
14032
|
runnerTokenPrefix: tokenPrefix(parsed.runnerToken),
|
|
@@ -13506,8 +14056,8 @@ var defaultRuntimeTakeoverProbes = {
|
|
|
13506
14056
|
})()
|
|
13507
14057
|
}),
|
|
13508
14058
|
harnessRoot: () => resolveHarnessRoot(),
|
|
13509
|
-
legacyOpenclawHarnessRoot: () =>
|
|
13510
|
-
pathExists: (target) =>
|
|
14059
|
+
legacyOpenclawHarnessRoot: () => path69.join(homedir16(), ".openclaw", "harness"),
|
|
14060
|
+
pathExists: (target) => existsSync47(target),
|
|
13511
14061
|
pathWritable: (target) => isWritable(target)
|
|
13512
14062
|
};
|
|
13513
14063
|
|
|
@@ -13913,8 +14463,8 @@ function assessVercelDeployEvidence(probes) {
|
|
|
13913
14463
|
}
|
|
13914
14464
|
function assessHarnessDirs(probes) {
|
|
13915
14465
|
const harnessRoot = probes.harnessRoot();
|
|
13916
|
-
const runsDir =
|
|
13917
|
-
const worktreesDir =
|
|
14466
|
+
const runsDir = path70.join(harnessRoot, "runs");
|
|
14467
|
+
const worktreesDir = path70.join(harnessRoot, "worktrees");
|
|
13918
14468
|
const displayHarnessRoot = redactHomePath(harnessRoot);
|
|
13919
14469
|
const displayRunsDir = redactHomePath(runsDir);
|
|
13920
14470
|
const displayWorktreesDir = redactHomePath(worktreesDir);
|
|
@@ -14178,9 +14728,9 @@ function applySchedulerCutoverAttestation(config) {
|
|
|
14178
14728
|
}
|
|
14179
14729
|
|
|
14180
14730
|
// src/scheduler-cutover-cli.ts
|
|
14181
|
-
import
|
|
14731
|
+
import path71 from "node:path";
|
|
14182
14732
|
import { homedir as homedir17 } from "node:os";
|
|
14183
|
-
var CONFIG_FILE3 =
|
|
14733
|
+
var CONFIG_FILE3 = path71.join(homedir17(), ".kynver", "config.json");
|
|
14184
14734
|
function runSchedulerCutoverCheckCli(json = false) {
|
|
14185
14735
|
const config = loadUserConfig();
|
|
14186
14736
|
const report = assessSchedulerCutover(config);
|
|
@@ -14317,6 +14867,157 @@ async function runCronTickCli(args) {
|
|
|
14317
14867
|
);
|
|
14318
14868
|
}
|
|
14319
14869
|
|
|
14870
|
+
// src/lane/landing-maintainer-tick.ts
|
|
14871
|
+
import os9 from "node:os";
|
|
14872
|
+
|
|
14873
|
+
// src/lane/lane-spec.ts
|
|
14874
|
+
var LANDING_MAINTAINER_LANE_SPEC = {
|
|
14875
|
+
slug: "landing-maintainer",
|
|
14876
|
+
originCron: "maintain-8-blocker-and-pr-landing-workers",
|
|
14877
|
+
defaultRepo: "Totalsolutionsync/Kynver",
|
|
14878
|
+
landScript: "scripts/agent-os-land-pr.mjs",
|
|
14879
|
+
landScriptArgs: ["--skip-not-ready"]
|
|
14880
|
+
};
|
|
14881
|
+
|
|
14882
|
+
// src/lane/landing-maintainer-local.ts
|
|
14883
|
+
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
14884
|
+
import path72 from "node:path";
|
|
14885
|
+
function runLandingWrapper(prNumber, repoRoot, execute) {
|
|
14886
|
+
const script = path72.join(repoRoot, LANDING_MAINTAINER_LANE_SPEC.landScript);
|
|
14887
|
+
const args = [script, String(prNumber), ...LANDING_MAINTAINER_LANE_SPEC.landScriptArgs];
|
|
14888
|
+
if (!execute) {
|
|
14889
|
+
return {
|
|
14890
|
+
action: { kind: "land_pr", prNumber, reason: "dry-run" },
|
|
14891
|
+
executed: false,
|
|
14892
|
+
exitCode: 0,
|
|
14893
|
+
stdout: `dry-run: node ${args.join(" ")}`,
|
|
14894
|
+
stderr: ""
|
|
14895
|
+
};
|
|
14896
|
+
}
|
|
14897
|
+
const result = spawnSync11("node", args, {
|
|
14898
|
+
cwd: repoRoot,
|
|
14899
|
+
encoding: "utf8",
|
|
14900
|
+
timeout: 10 * 60 * 1e3
|
|
14901
|
+
});
|
|
14902
|
+
return {
|
|
14903
|
+
action: { kind: "land_pr", prNumber, reason: "landing wrapper invoked" },
|
|
14904
|
+
executed: true,
|
|
14905
|
+
exitCode: result.status,
|
|
14906
|
+
stdout: result.stdout ?? "",
|
|
14907
|
+
stderr: result.stderr ?? ""
|
|
14908
|
+
};
|
|
14909
|
+
}
|
|
14910
|
+
function resolveLandingMaintainerRepoRoot(args) {
|
|
14911
|
+
const explicit = args.repoPath ? String(args.repoPath).trim() : "";
|
|
14912
|
+
if (explicit) return path72.resolve(explicit);
|
|
14913
|
+
const resolved = resolveDefaultRepo();
|
|
14914
|
+
return resolved?.repo ?? process.cwd();
|
|
14915
|
+
}
|
|
14916
|
+
|
|
14917
|
+
// src/lane/landing-maintainer-tick.ts
|
|
14918
|
+
async function runLandingMaintainerLaneTick(args) {
|
|
14919
|
+
const agentOsId = String(required(String(args.agentOsId || ""), "--agent-os-id"));
|
|
14920
|
+
const repoSlug = String(args.repo || LANDING_MAINTAINER_LANE_SPEC.defaultRepo).trim();
|
|
14921
|
+
const fleet = args.fleet === true || args.fleet === "true";
|
|
14922
|
+
const execute = args.execute !== false && args.execute !== "false";
|
|
14923
|
+
const runId = args.run ? String(args.run) : void 0;
|
|
14924
|
+
const resourceGate = observeRunnerResourceGate({
|
|
14925
|
+
runId: runId ?? "fleet-lane-tick"
|
|
14926
|
+
});
|
|
14927
|
+
const boxCapacity = {
|
|
14928
|
+
...buildBoxResourceSnapshotFromGate(resourceGate, {
|
|
14929
|
+
harnessRunId: runId,
|
|
14930
|
+
boxKind: resolveBoxKindFromConfig(loadUserConfig()),
|
|
14931
|
+
hostLabel: os9.hostname()
|
|
14932
|
+
}),
|
|
14933
|
+
providerHealthy: resourceGate.ok,
|
|
14934
|
+
authorizedForRepair: resourceGate.ok,
|
|
14935
|
+
authorizedForLanding: resourceGate.ok,
|
|
14936
|
+
systemHealthBlockers: resourceGate.ok ? [] : [resourceGate.reason ?? "resource_gate_blocked"],
|
|
14937
|
+
actionableWorkers: resourceGate.activeWorkers
|
|
14938
|
+
};
|
|
14939
|
+
const base = resolveBaseUrl(args.baseUrl ? String(args.baseUrl) : void 0);
|
|
14940
|
+
const secret = await resolveCallbackSecretWithMint(
|
|
14941
|
+
args.secret ? String(args.secret) : void 0,
|
|
14942
|
+
agentOsId,
|
|
14943
|
+
{ baseUrl: base }
|
|
14944
|
+
);
|
|
14945
|
+
const url = `${base}/api/agent-os/by-id/${encodeURIComponent(agentOsId)}/fleet-landing-maintainer/tick`;
|
|
14946
|
+
const res = await postJson(url, secret, {
|
|
14947
|
+
repo: repoSlug,
|
|
14948
|
+
fleet,
|
|
14949
|
+
execute,
|
|
14950
|
+
runId,
|
|
14951
|
+
boxCapacity
|
|
14952
|
+
});
|
|
14953
|
+
const coordinator = res.response;
|
|
14954
|
+
const localOutcomes = [];
|
|
14955
|
+
const repoRoot = resolveLandingMaintainerRepoRoot(args);
|
|
14956
|
+
const actions = Array.isArray(coordinator?.localActions) ? coordinator.localActions : [];
|
|
14957
|
+
const holderBoxId = boxCapacity.boxId;
|
|
14958
|
+
for (const action of actions) {
|
|
14959
|
+
if (action.kind === "land_pr" && typeof action.prNumber === "number") {
|
|
14960
|
+
const outcome = runLandingWrapper(action.prNumber, repoRoot, execute);
|
|
14961
|
+
localOutcomes.push({
|
|
14962
|
+
action: outcome.action,
|
|
14963
|
+
executed: outcome.executed,
|
|
14964
|
+
exitCode: outcome.exitCode
|
|
14965
|
+
});
|
|
14966
|
+
if (execute && outcome.executed) {
|
|
14967
|
+
const merged = outcome.exitCode === 0;
|
|
14968
|
+
const prUrl = typeof action.prUrl === "string" && action.prUrl.trim() ? action.prUrl.trim() : `https://github.com/${repoSlug}/pull/${action.prNumber}`;
|
|
14969
|
+
try {
|
|
14970
|
+
await postJson(
|
|
14971
|
+
`${base}/api/agent-os/by-id/${encodeURIComponent(agentOsId)}/fleet-landing-maintainer/outcome`,
|
|
14972
|
+
secret,
|
|
14973
|
+
{ repo: repoSlug, prUrl, holderBoxId, merged }
|
|
14974
|
+
);
|
|
14975
|
+
} catch {
|
|
14976
|
+
}
|
|
14977
|
+
}
|
|
14978
|
+
continue;
|
|
14979
|
+
}
|
|
14980
|
+
localOutcomes.push({
|
|
14981
|
+
action,
|
|
14982
|
+
executed: false,
|
|
14983
|
+
exitCode: null
|
|
14984
|
+
});
|
|
14985
|
+
}
|
|
14986
|
+
return {
|
|
14987
|
+
repo: repoSlug,
|
|
14988
|
+
fleet,
|
|
14989
|
+
execute,
|
|
14990
|
+
coordinator,
|
|
14991
|
+
localOutcomes
|
|
14992
|
+
};
|
|
14993
|
+
}
|
|
14994
|
+
|
|
14995
|
+
// src/lane/lane-tick-cli.ts
|
|
14996
|
+
async function runLaneTickCli(args, laneFromArgv) {
|
|
14997
|
+
const lane = String(laneFromArgv ?? args.lane ?? "").trim();
|
|
14998
|
+
if (lane !== "landing-maintainer") {
|
|
14999
|
+
console.error(`unknown lane: ${lane || "(none)"}`);
|
|
15000
|
+
process.exit(1);
|
|
15001
|
+
}
|
|
15002
|
+
const result = await runLandingMaintainerLaneTick(args);
|
|
15003
|
+
if (args.json === true || args.json === "true") {
|
|
15004
|
+
console.log(JSON.stringify(result, null, 2));
|
|
15005
|
+
return;
|
|
15006
|
+
}
|
|
15007
|
+
console.log(
|
|
15008
|
+
[
|
|
15009
|
+
`fleet landing-maintainer tick`,
|
|
15010
|
+
`repo=${result.repo}`,
|
|
15011
|
+
`fleet=${result.fleet}`,
|
|
15012
|
+
`execute=${result.execute}`,
|
|
15013
|
+
`localOutcomes=${result.localOutcomes.length}`
|
|
15014
|
+
].join(" ")
|
|
15015
|
+
);
|
|
15016
|
+
for (const row of result.localOutcomes) {
|
|
15017
|
+
console.log(` ${row.action.kind} pr=${row.action.prNumber ?? "-"} executed=${row.executed}`);
|
|
15018
|
+
}
|
|
15019
|
+
}
|
|
15020
|
+
|
|
14320
15021
|
// src/cli.ts
|
|
14321
15022
|
function isHelpFlag(arg) {
|
|
14322
15023
|
return arg === "help" || arg === "--help" || arg === "-h";
|
|
@@ -14337,6 +15038,7 @@ function usage(code = 0) {
|
|
|
14337
15038
|
" kynver daemon --run RUN_ID --agent-os-id AOS_ID [--execute] [--interval-ms MS]",
|
|
14338
15039
|
" kynver run create [--repo /path/repo] [--name name] [--base origin/main]",
|
|
14339
15040
|
" kynver run list",
|
|
15041
|
+
" kynver run resolve --name RUN_NAME",
|
|
14340
15042
|
" kynver run status --run RUN_ID [--json] [--compact]",
|
|
14341
15043
|
" 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]",
|
|
14342
15044
|
" kynver run sweep --run RUN_ID --agent-os-id AOS_ID [--base-url URL] [--secret SECRET] [--grace-ms MS]",
|
|
@@ -14372,6 +15074,7 @@ function usage(code = 0) {
|
|
|
14372
15074
|
" kynver scheduler attest-cutover [--json]",
|
|
14373
15075
|
" kynver cron status [--json]",
|
|
14374
15076
|
" kynver cron tick [--agent-os-id AOS_ID] [--json]",
|
|
15077
|
+
" kynver lane tick landing-maintainer [--fleet] [--repo OWNER/NAME] [--agent-os-id AOS_ID] [--execute] [--json]",
|
|
14375
15078
|
" kynver board contract [--agent-os-id ID] [--base-url URL] [--since ISO] [--limit N]"
|
|
14376
15079
|
].join("\n")
|
|
14377
15080
|
);
|
|
@@ -14383,7 +15086,7 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
14383
15086
|
const scope = argv.shift();
|
|
14384
15087
|
let action;
|
|
14385
15088
|
let rest;
|
|
14386
|
-
if (scope === "run" || scope === "worker" || scope === "plan" || scope === "runner" || scope === "harness" || scope === "monitor" || scope === "doctor" || scope === "config" || scope === "scheduler" || scope === "cron" || scope === "board") {
|
|
15089
|
+
if (scope === "run" || scope === "worker" || scope === "plan" || scope === "runner" || scope === "harness" || scope === "monitor" || scope === "doctor" || scope === "config" || scope === "scheduler" || scope === "cron" || scope === "lane" || scope === "board") {
|
|
14387
15090
|
action = argv.shift();
|
|
14388
15091
|
rest = argv;
|
|
14389
15092
|
} else {
|
|
@@ -14392,8 +15095,8 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
14392
15095
|
if (action && isHelpFlag(action) || rest.some(isHelpFlag)) return usage(0);
|
|
14393
15096
|
const args = parseArgs(rest);
|
|
14394
15097
|
const { runsDir, worktreesDir } = getPaths();
|
|
14395
|
-
|
|
14396
|
-
|
|
15098
|
+
mkdirSync10(runsDir, { recursive: true });
|
|
15099
|
+
mkdirSync10(worktreesDir, { recursive: true });
|
|
14397
15100
|
if (shouldEnforceMemoryCostPackageGuardCli(scope, action)) {
|
|
14398
15101
|
let repoRoot;
|
|
14399
15102
|
const runId = args.run ? String(args.run).trim() : "";
|
|
@@ -14440,11 +15143,16 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
14440
15143
|
if (scope === "cron" && action === "tick") {
|
|
14441
15144
|
return void await runCronTickCli(args);
|
|
14442
15145
|
}
|
|
15146
|
+
if (scope === "lane" && action === "tick") {
|
|
15147
|
+
const laneName = rest.shift();
|
|
15148
|
+
return void await runLaneTickCli(parseArgs(rest), laneName);
|
|
15149
|
+
}
|
|
14443
15150
|
if (scope === "board" && action === "contract") {
|
|
14444
15151
|
return void await runCommandCenterContractCli(args);
|
|
14445
15152
|
}
|
|
14446
15153
|
if (scope === "run" && action === "create") return createRun(args);
|
|
14447
15154
|
if (scope === "run" && action === "list") return listRuns();
|
|
15155
|
+
if (scope === "run" && action === "resolve") return resolveHarnessRunCli(args);
|
|
14448
15156
|
if (scope === "run" && action === "status") return runStatus(args);
|
|
14449
15157
|
if (scope === "run" && action === "dispatch") return void await dispatchRun(args);
|
|
14450
15158
|
if (scope === "run" && action === "sweep") return void await sweepRun(args);
|
|
@@ -14828,6 +15536,7 @@ export {
|
|
|
14828
15536
|
CODEX_DEFAULT_MODEL,
|
|
14829
15537
|
DEFAULT_DISPATCH_LEASE_MS,
|
|
14830
15538
|
DEFAULT_HARNESS_VERIFY_COMMANDS,
|
|
15539
|
+
DEFAULT_HEAVY_VERIFICATION_MAX_CONCURRENT,
|
|
14831
15540
|
DEFAULT_MAX_ACTIONS_PER_SWEEP,
|
|
14832
15541
|
DEFAULT_NODE_MODULES_AGE_MS,
|
|
14833
15542
|
DEFAULT_RUN_DIRECTORIES_AGE_MS,
|
|
@@ -14856,6 +15565,7 @@ export {
|
|
|
14856
15565
|
assessAutoCompleteEligibility,
|
|
14857
15566
|
assessBuildAdmission,
|
|
14858
15567
|
assessExitedWorkerSalvage,
|
|
15568
|
+
assessHeavyVerificationGate,
|
|
14859
15569
|
assessOrphanWorktreeSafety,
|
|
14860
15570
|
assessPrHandoffRequirement,
|
|
14861
15571
|
assessWorkerLanding,
|
|
@@ -14889,6 +15599,7 @@ export {
|
|
|
14889
15599
|
completeWorker,
|
|
14890
15600
|
computeAttention,
|
|
14891
15601
|
computeWorkerStatus,
|
|
15602
|
+
countActiveHeavyVerificationSlots,
|
|
14892
15603
|
createRun,
|
|
14893
15604
|
defaultBoxId,
|
|
14894
15605
|
deriveRunStatus,
|
|
@@ -14935,6 +15646,7 @@ export {
|
|
|
14935
15646
|
hasNestedRunsSegment,
|
|
14936
15647
|
hashPlanBody,
|
|
14937
15648
|
hermesCodexProvider,
|
|
15649
|
+
isActiveHarnessWorker,
|
|
14938
15650
|
isClaudeFamilyProvider,
|
|
14939
15651
|
isDashboardVercelUrl,
|
|
14940
15652
|
isEngagementRequiredSkip,
|
|
@@ -14942,6 +15654,7 @@ export {
|
|
|
14942
15654
|
isForbiddenWorkerEnvKey,
|
|
14943
15655
|
isGeneratedHarnessPath,
|
|
14944
15656
|
isHarnessRunMetadataPath,
|
|
15657
|
+
isHeavyVerificationGateSkipped,
|
|
14945
15658
|
isInspectableVercelTarget,
|
|
14946
15659
|
isKnownWorkerPersonaSlug,
|
|
14947
15660
|
isKynverMonorepoRoot,
|
|
@@ -14956,6 +15669,7 @@ export {
|
|
|
14956
15669
|
isWslHost,
|
|
14957
15670
|
joinHarnessNotice,
|
|
14958
15671
|
landingContractAttentionReason,
|
|
15672
|
+
listActiveHeavyVerificationSlots,
|
|
14959
15673
|
listForbiddenWorkerEnvKeys,
|
|
14960
15674
|
listMonitors,
|
|
14961
15675
|
listOrchestrationProviderCapabilities,
|
|
@@ -14995,12 +15709,15 @@ export {
|
|
|
14995
15709
|
providerCapableForRisk,
|
|
14996
15710
|
readMemAvailableBytes,
|
|
14997
15711
|
readProductionDbKeysFromEnvFile,
|
|
15712
|
+
reclaimStaleHeavyVerificationSlots,
|
|
15713
|
+
reconcileLocalOnlyMergedPrAttention,
|
|
14998
15714
|
reconcileRunsCli,
|
|
14999
15715
|
reconcileStaleWorkers,
|
|
15000
15716
|
reconcileWorkerMetadata,
|
|
15001
15717
|
reconcileWorkerMetadataCli,
|
|
15002
15718
|
redactHarness,
|
|
15003
15719
|
redactProviderErrorText,
|
|
15720
|
+
releaseHeavyVerificationSlot,
|
|
15004
15721
|
remediateDefaultRepo,
|
|
15005
15722
|
repairMissingRunMetadata,
|
|
15006
15723
|
repairNestedRunsPath,
|
|
@@ -15012,6 +15729,9 @@ export {
|
|
|
15012
15729
|
resolveConfiguredWorkerProvider,
|
|
15013
15730
|
resolveDefaultRepo,
|
|
15014
15731
|
resolveHarnessRoot,
|
|
15732
|
+
resolveHarnessRunByName,
|
|
15733
|
+
resolveHarnessRunCli,
|
|
15734
|
+
resolveHeavyVerificationMaxConcurrent,
|
|
15015
15735
|
resolveOpenAiCodexRetryBudget,
|
|
15016
15736
|
resolveOrchestrationPolicyMode,
|
|
15017
15737
|
resolveOrchestrationRouting,
|
|
@@ -15049,12 +15769,14 @@ export {
|
|
|
15049
15769
|
tailWorker,
|
|
15050
15770
|
taskAllowsClaudeWorker,
|
|
15051
15771
|
terminalFinalResultFromHeartbeat,
|
|
15772
|
+
tryAcquireHeavyVerificationSlot,
|
|
15052
15773
|
usage,
|
|
15053
15774
|
validateOwnedPaths,
|
|
15054
15775
|
validateRepo,
|
|
15055
15776
|
validateRunId,
|
|
15056
15777
|
validateTailLines,
|
|
15057
15778
|
validateWorkerName,
|
|
15779
|
+
waitForHeavyVerificationSlot,
|
|
15058
15780
|
workerDirHasActiveRetentionSignals,
|
|
15059
15781
|
workerPersonaLandingSlugs,
|
|
15060
15782
|
workerPersonaReviewSlugs,
|