@pushpalsdev/cli 1.1.38 → 1.1.40
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/pushpals-cli.js
CHANGED
|
@@ -58,11 +58,13 @@ function shouldRestartService(attempts, maxAttempts = DEFAULT_SERVICE_MANAGER_MA
|
|
|
58
58
|
return normalizedAttempts < normalizedMax;
|
|
59
59
|
}
|
|
60
60
|
function pipeProcessStreamToLines(stream, onLine) {
|
|
61
|
-
if (!stream || typeof stream === "number" || typeof stream.getReader !== "function")
|
|
62
|
-
return;
|
|
61
|
+
if (!stream || typeof stream === "number" || typeof stream.getReader !== "function") {
|
|
62
|
+
return { cancel: () => {} };
|
|
63
|
+
}
|
|
63
64
|
const reader = stream.getReader();
|
|
64
65
|
const decoder = new TextDecoder;
|
|
65
66
|
let pending = "";
|
|
67
|
+
let cancelled = false;
|
|
66
68
|
(async () => {
|
|
67
69
|
try {
|
|
68
70
|
while (true) {
|
|
@@ -86,9 +88,21 @@ function pipeProcessStreamToLines(stream, onLine) {
|
|
|
86
88
|
if (tail)
|
|
87
89
|
onLine?.(tail);
|
|
88
90
|
} catch {} finally {
|
|
89
|
-
|
|
91
|
+
try {
|
|
92
|
+
reader.releaseLock();
|
|
93
|
+
} catch {}
|
|
90
94
|
}
|
|
91
95
|
})();
|
|
96
|
+
return {
|
|
97
|
+
cancel: () => {
|
|
98
|
+
if (cancelled)
|
|
99
|
+
return;
|
|
100
|
+
cancelled = true;
|
|
101
|
+
try {
|
|
102
|
+
reader.cancel().catch(() => {});
|
|
103
|
+
} catch {}
|
|
104
|
+
}
|
|
105
|
+
};
|
|
92
106
|
}
|
|
93
107
|
function spawnManagedService(spec) {
|
|
94
108
|
const env = { ...spec.env ?? {} };
|
|
@@ -98,8 +112,8 @@ function spawnManagedService(spec) {
|
|
|
98
112
|
stdout: "pipe",
|
|
99
113
|
stderr: "pipe"
|
|
100
114
|
});
|
|
101
|
-
pipeProcessStreamToLines(proc.stdout, spec.onStdoutLine);
|
|
102
|
-
pipeProcessStreamToLines(proc.stderr, spec.onStderrLine);
|
|
115
|
+
const stdoutPipe = pipeProcessStreamToLines(proc.stdout, spec.onStdoutLine);
|
|
116
|
+
const stderrPipe = pipeProcessStreamToLines(proc.stderr, spec.onStderrLine);
|
|
103
117
|
const service = {
|
|
104
118
|
name: spec.name,
|
|
105
119
|
proc,
|
|
@@ -109,7 +123,11 @@ function spawnManagedService(spec) {
|
|
|
109
123
|
exited: false,
|
|
110
124
|
exitCode: null,
|
|
111
125
|
launchedAtMs: Date.now(),
|
|
112
|
-
logPath: spec.logPath
|
|
126
|
+
logPath: spec.logPath,
|
|
127
|
+
stopOutputPipes: () => {
|
|
128
|
+
stdoutPipe.cancel();
|
|
129
|
+
stderrPipe.cancel();
|
|
130
|
+
}
|
|
113
131
|
};
|
|
114
132
|
proc.exited.then((code) => {
|
|
115
133
|
service.exited = true;
|
|
@@ -161,6 +179,7 @@ class ServiceManager {
|
|
|
161
179
|
const existing = this.services.get(spec.name);
|
|
162
180
|
if (existing && !existing.exited) {
|
|
163
181
|
try {
|
|
182
|
+
existing.stopOutputPipes?.();
|
|
164
183
|
const pid = existing.proc.pid;
|
|
165
184
|
if (process.platform === "win32" && typeof pid === "number" && pid > 0) {
|
|
166
185
|
Bun.spawnSync(["taskkill", "/PID", String(pid), "/T", "/F"], {
|
|
@@ -224,6 +243,7 @@ class ServiceManager {
|
|
|
224
243
|
this.stopped = true;
|
|
225
244
|
for (const service of this.services.values()) {
|
|
226
245
|
try {
|
|
246
|
+
service.stopOutputPipes?.();
|
|
227
247
|
const pid = service.proc.pid;
|
|
228
248
|
if (process.platform === "win32" && typeof pid === "number" && pid > 0) {
|
|
229
249
|
Bun.spawnSync(["taskkill", "/PID", String(pid), "/T", "/F"], {
|
|
@@ -2169,6 +2189,97 @@ function withWindowsGitSchannelEnv(env, platform = process.platform) {
|
|
|
2169
2189
|
return env;
|
|
2170
2190
|
return appendGitConfigEnv(env, "http.sslBackend", "schannel");
|
|
2171
2191
|
}
|
|
2192
|
+
var WINDOWS_NODE_EXTRA_CA_CERTS_DISABLE_ENV = "PUSHPALS_DISABLE_WINDOWS_NODE_EXTRA_CA_CERTS";
|
|
2193
|
+
var WINDOWS_NODE_EXTRA_CA_CERTS_BUNDLE_RELATIVE_PATH = ["certs", "windows-root-ca.pem"];
|
|
2194
|
+
function resolveWindowsNodeExtraCaCertsBundlePath(runtimeRoot) {
|
|
2195
|
+
return join2(runtimeRoot, ...WINDOWS_NODE_EXTRA_CA_CERTS_BUNDLE_RELATIVE_PATH);
|
|
2196
|
+
}
|
|
2197
|
+
function hasUsablePemCertificate(pathValue) {
|
|
2198
|
+
try {
|
|
2199
|
+
return /-----BEGIN CERTIFICATE-----/.test(readFileSync4(pathValue, "utf8"));
|
|
2200
|
+
} catch {
|
|
2201
|
+
return false;
|
|
2202
|
+
}
|
|
2203
|
+
}
|
|
2204
|
+
function ensureWindowsNodeExtraCaCertsBundle(outPath, env) {
|
|
2205
|
+
if (hasUsablePemCertificate(outPath))
|
|
2206
|
+
return outPath;
|
|
2207
|
+
const outDir = dirname(outPath);
|
|
2208
|
+
try {
|
|
2209
|
+
mkdirSync(outDir, { recursive: true });
|
|
2210
|
+
} catch {
|
|
2211
|
+
return "";
|
|
2212
|
+
}
|
|
2213
|
+
const script = String.raw`
|
|
2214
|
+
$ErrorActionPreference = "Stop"
|
|
2215
|
+
$outPath = $env:PUSHPALS_WINDOWS_NODE_EXTRA_CA_CERTS_OUT
|
|
2216
|
+
if (-not $outPath) { throw "PUSHPALS_WINDOWS_NODE_EXTRA_CA_CERTS_OUT is required" }
|
|
2217
|
+
$outDir = Split-Path -Parent $outPath
|
|
2218
|
+
if ($outDir) { [System.IO.Directory]::CreateDirectory($outDir) | Out-Null }
|
|
2219
|
+
$stores = @("Cert:\CurrentUser\Root", "Cert:\LocalMachine\Root")
|
|
2220
|
+
$seen = @{}
|
|
2221
|
+
$lines = New-Object System.Collections.Generic.List[string]
|
|
2222
|
+
foreach ($store in $stores) {
|
|
2223
|
+
if (-not (Test-Path $store)) { continue }
|
|
2224
|
+
foreach ($cert in Get-ChildItem $store) {
|
|
2225
|
+
if (-not $cert.RawData) { continue }
|
|
2226
|
+
if ($cert.NotAfter -lt (Get-Date)) { continue }
|
|
2227
|
+
$thumbprint = [string]$cert.Thumbprint
|
|
2228
|
+
if ($seen.ContainsKey($thumbprint)) { continue }
|
|
2229
|
+
$seen[$thumbprint] = $true
|
|
2230
|
+
$lines.Add("-----BEGIN CERTIFICATE-----")
|
|
2231
|
+
$encoded = [Convert]::ToBase64String($cert.RawData, [Base64FormattingOptions]::InsertLineBreaks)
|
|
2232
|
+
foreach ($line in [regex]::Split($encoded, '\r?\n')) {
|
|
2233
|
+
if ($line) { $lines.Add($line) }
|
|
2234
|
+
}
|
|
2235
|
+
$lines.Add("-----END CERTIFICATE-----")
|
|
2236
|
+
}
|
|
2237
|
+
}
|
|
2238
|
+
if ($lines.Count -eq 0) { throw "No Windows root certificates found" }
|
|
2239
|
+
[System.IO.File]::WriteAllLines($outPath, $lines, [System.Text.Encoding]::ASCII)
|
|
2240
|
+
`;
|
|
2241
|
+
const encodedScript = Buffer.from(script, "utf16le").toString("base64");
|
|
2242
|
+
const childEnv = normalizeChildProcessEnv({
|
|
2243
|
+
...env,
|
|
2244
|
+
PUSHPALS_WINDOWS_NODE_EXTRA_CA_CERTS_OUT: outPath
|
|
2245
|
+
});
|
|
2246
|
+
const result = Bun.spawnSync([
|
|
2247
|
+
"powershell.exe",
|
|
2248
|
+
"-NoProfile",
|
|
2249
|
+
"-NonInteractive",
|
|
2250
|
+
"-ExecutionPolicy",
|
|
2251
|
+
"Bypass",
|
|
2252
|
+
"-EncodedCommand",
|
|
2253
|
+
encodedScript
|
|
2254
|
+
], {
|
|
2255
|
+
cwd: process.cwd(),
|
|
2256
|
+
env: childEnv,
|
|
2257
|
+
stdout: "pipe",
|
|
2258
|
+
stderr: "pipe"
|
|
2259
|
+
});
|
|
2260
|
+
if (result.exitCode !== 0)
|
|
2261
|
+
return "";
|
|
2262
|
+
return hasUsablePemCertificate(outPath) ? outPath : "";
|
|
2263
|
+
}
|
|
2264
|
+
function withWindowsNodeExtraCaCertsEnv(env, opts) {
|
|
2265
|
+
const platform = opts.platform ?? process.platform;
|
|
2266
|
+
if (platform !== "win32")
|
|
2267
|
+
return env;
|
|
2268
|
+
if (parseBooleanFlag(env[WINDOWS_NODE_EXTRA_CA_CERTS_DISABLE_ENV]) === true)
|
|
2269
|
+
return env;
|
|
2270
|
+
if (typeof env.NODE_EXTRA_CA_CERTS === "string" && env.NODE_EXTRA_CA_CERTS.trim())
|
|
2271
|
+
return env;
|
|
2272
|
+
const runtimeRoot = String(opts.runtimeRoot ?? "").trim();
|
|
2273
|
+
if (!runtimeRoot || !existsSync5(runtimeRoot))
|
|
2274
|
+
return env;
|
|
2275
|
+
const bundlePath = ensureWindowsNodeExtraCaCertsBundle(resolveWindowsNodeExtraCaCertsBundlePath(runtimeRoot), env);
|
|
2276
|
+
if (!bundlePath)
|
|
2277
|
+
return env;
|
|
2278
|
+
return {
|
|
2279
|
+
...env,
|
|
2280
|
+
NODE_EXTRA_CA_CERTS: bundlePath
|
|
2281
|
+
};
|
|
2282
|
+
}
|
|
2172
2283
|
async function runGitWithEnv(args, cwd, env, timeoutMs) {
|
|
2173
2284
|
return await runCommandWithEnv(["git", ...args], cwd, withWindowsGitSchannelEnv(env), timeoutMs);
|
|
2174
2285
|
}
|
|
@@ -2844,7 +2955,11 @@ function buildEmbeddedRuntimeEnv(baseEnv, opts) {
|
|
|
2844
2955
|
...typeof env.PUSHPALS_DOCKER_BIN === "string" && env.PUSHPALS_DOCKER_BIN.trim() ? { PUSHPALS_DOCKER_BIN: env.PUSHPALS_DOCKER_BIN.trim() } : {},
|
|
2845
2956
|
...typeof env.PUSHPALS_DOCKER_BIN_ABSOLUTE === "string" && env.PUSHPALS_DOCKER_BIN_ABSOLUTE.trim() ? { PUSHPALS_DOCKER_BIN_ABSOLUTE: env.PUSHPALS_DOCKER_BIN_ABSOLUTE.trim() } : {}
|
|
2846
2957
|
};
|
|
2847
|
-
|
|
2958
|
+
const runtimeEnvWithWindowsCa = withWindowsNodeExtraCaCertsEnv(runtimeEnv, {
|
|
2959
|
+
platform,
|
|
2960
|
+
runtimeRoot: opts.runtimeRoot
|
|
2961
|
+
});
|
|
2962
|
+
return withWindowsGitSchannelEnv(runtimeEnvWithWindowsCa, platform);
|
|
2848
2963
|
}
|
|
2849
2964
|
function parseBooleanFlag(raw) {
|
|
2850
2965
|
const normalized = String(raw ?? "").trim().toLowerCase();
|
|
@@ -3126,6 +3241,7 @@ function buildServiceStopCommand(pid, platform = process.platform) {
|
|
|
3126
3241
|
function stopRuntimeServices(services) {
|
|
3127
3242
|
for (const service of services) {
|
|
3128
3243
|
try {
|
|
3244
|
+
service.stopOutputPipes?.();
|
|
3129
3245
|
const stopCommand = buildServiceStopCommand(service.proc.pid, process.platform);
|
|
3130
3246
|
if (stopCommand) {
|
|
3131
3247
|
Bun.spawnSync(stopCommand, {
|
|
@@ -3141,6 +3257,52 @@ function stopRuntimeServices(services) {
|
|
|
3141
3257
|
} catch {}
|
|
3142
3258
|
}
|
|
3143
3259
|
}
|
|
3260
|
+
async function runWindowsServiceStopCommand(command, timeoutMs) {
|
|
3261
|
+
const proc = Bun.spawn(command, {
|
|
3262
|
+
stdin: "ignore",
|
|
3263
|
+
stdout: "ignore",
|
|
3264
|
+
stderr: "ignore"
|
|
3265
|
+
});
|
|
3266
|
+
let timeout = null;
|
|
3267
|
+
let timedOut = false;
|
|
3268
|
+
const exitCode = await Promise.race([
|
|
3269
|
+
proc.exited,
|
|
3270
|
+
new Promise((resolveTimeout) => {
|
|
3271
|
+
timeout = setTimeout(() => {
|
|
3272
|
+
timedOut = true;
|
|
3273
|
+
try {
|
|
3274
|
+
proc.kill("SIGKILL");
|
|
3275
|
+
} catch {}
|
|
3276
|
+
resolveTimeout(-1);
|
|
3277
|
+
}, Math.max(250, timeoutMs));
|
|
3278
|
+
})
|
|
3279
|
+
]);
|
|
3280
|
+
if (timeout) {
|
|
3281
|
+
clearTimeout(timeout);
|
|
3282
|
+
timeout = null;
|
|
3283
|
+
}
|
|
3284
|
+
if (timedOut) {
|
|
3285
|
+
await Promise.race([proc.exited.catch(() => -1), Bun.sleep(250)]);
|
|
3286
|
+
return false;
|
|
3287
|
+
}
|
|
3288
|
+
return exitCode === 0;
|
|
3289
|
+
}
|
|
3290
|
+
async function stopRuntimeServicesOnWindows(services, timeoutMs) {
|
|
3291
|
+
const deadline = Date.now() + Math.max(1000, timeoutMs);
|
|
3292
|
+
for (const service of services) {
|
|
3293
|
+
service.stopOutputPipes?.();
|
|
3294
|
+
const stopCommand = buildServiceStopCommand(service.proc.pid, "win32");
|
|
3295
|
+
if (stopCommand) {
|
|
3296
|
+
const remainingMs = Math.max(250, deadline - Date.now());
|
|
3297
|
+
const stopped = await runWindowsServiceStopCommand(stopCommand, Math.min(WINDOWS_TASKKILL_TIMEOUT_MS2, remainingMs));
|
|
3298
|
+
if (stopped)
|
|
3299
|
+
continue;
|
|
3300
|
+
}
|
|
3301
|
+
try {
|
|
3302
|
+
service.proc.kill("SIGKILL");
|
|
3303
|
+
} catch {}
|
|
3304
|
+
}
|
|
3305
|
+
}
|
|
3144
3306
|
function resolveGracefulShutdownPriority(name) {
|
|
3145
3307
|
if (name === "source_control_manager")
|
|
3146
3308
|
return 0;
|
|
@@ -3169,7 +3331,7 @@ async function stopRuntimeServicesGracefully(services, timeoutMs = 1e4) {
|
|
|
3169
3331
|
return;
|
|
3170
3332
|
const ordered = [...running].sort((a, b) => resolveGracefulShutdownPriority(a.name) - resolveGracefulShutdownPriority(b.name));
|
|
3171
3333
|
if (process.platform === "win32") {
|
|
3172
|
-
|
|
3334
|
+
await stopRuntimeServicesOnWindows(ordered, timeoutMs);
|
|
3173
3335
|
await waitForRuntimeServicesExit(ordered, Math.min(1000, timeoutMs));
|
|
3174
3336
|
return;
|
|
3175
3337
|
}
|
|
@@ -3200,6 +3362,7 @@ async function shutdownEmbeddedServiceManagerGracefully(options) {
|
|
|
3200
3362
|
reason,
|
|
3201
3363
|
requestShutdown = requestLocalRuntimeShutdown,
|
|
3202
3364
|
shutdownAcceptedDelayMs = 1500,
|
|
3365
|
+
serviceStopTimeoutMs = 1e4,
|
|
3203
3366
|
onLog = (line) => console.log(line),
|
|
3204
3367
|
onWarn = (line) => console.warn(line),
|
|
3205
3368
|
cleanupTasks = []
|
|
@@ -3215,7 +3378,7 @@ async function shutdownEmbeddedServiceManagerGracefully(options) {
|
|
|
3215
3378
|
} else if (shutdown.detail) {
|
|
3216
3379
|
onWarn(`[pushpals] ${shutdown.detail}`);
|
|
3217
3380
|
}
|
|
3218
|
-
await stopRuntimeServicesGracefully(services);
|
|
3381
|
+
await stopRuntimeServicesGracefully(services, serviceStopTimeoutMs);
|
|
3219
3382
|
for (const task of cleanupTasks) {
|
|
3220
3383
|
await task();
|
|
3221
3384
|
}
|
|
@@ -6034,10 +6197,13 @@ ${line}
|
|
|
6034
6197
|
console.log("[pushpals] Runtime-only mode is active. Send `exit` on stdin or terminate the process to stop.");
|
|
6035
6198
|
await new Promise((resolveStop) => {
|
|
6036
6199
|
let resolved = false;
|
|
6200
|
+
let exitRequestedFromInput = false;
|
|
6201
|
+
const keepAlive = setInterval(() => {}, 60000);
|
|
6037
6202
|
const finish = () => {
|
|
6038
6203
|
if (resolved)
|
|
6039
6204
|
return;
|
|
6040
6205
|
resolved = true;
|
|
6206
|
+
clearInterval(keepAlive);
|
|
6041
6207
|
resolveStop();
|
|
6042
6208
|
};
|
|
6043
6209
|
process.once("SIGINT", finish);
|
|
@@ -6050,13 +6216,17 @@ ${line}
|
|
|
6050
6216
|
runtimeOnlyInput.on("line", (line) => {
|
|
6051
6217
|
if (!isCliExitCommand(line))
|
|
6052
6218
|
return;
|
|
6219
|
+
exitRequestedFromInput = true;
|
|
6053
6220
|
requestStop();
|
|
6054
6221
|
runtimeOnlyInput.close();
|
|
6055
6222
|
finish();
|
|
6056
6223
|
});
|
|
6057
6224
|
runtimeOnlyInput.on("close", () => {
|
|
6058
|
-
|
|
6059
|
-
|
|
6225
|
+
if (exitRequestedFromInput || resolved) {
|
|
6226
|
+
finish();
|
|
6227
|
+
return;
|
|
6228
|
+
}
|
|
6229
|
+
console.log("[pushpals] Runtime-only stdin closed; continuing until terminated.");
|
|
6060
6230
|
});
|
|
6061
6231
|
});
|
|
6062
6232
|
await requestStop();
|
|
@@ -6138,6 +6308,7 @@ if (import.meta.main) {
|
|
|
6138
6308
|
});
|
|
6139
6309
|
}
|
|
6140
6310
|
export {
|
|
6311
|
+
withWindowsNodeExtraCaCertsEnv,
|
|
6141
6312
|
waitForWorkerpalCapacity,
|
|
6142
6313
|
waitForRemoteBuddySessionConsumer,
|
|
6143
6314
|
startEmbeddedMonitoringHub,
|
|
@@ -6154,6 +6325,7 @@ export {
|
|
|
6154
6325
|
resolveWorkerExecutionReadiness,
|
|
6155
6326
|
resolveWindowsWhereExecutableCandidatesForEnv,
|
|
6156
6327
|
resolveWindowsShellExecutableCandidatesForEnv,
|
|
6328
|
+
resolveWindowsNodeExtraCaCertsBundlePath,
|
|
6157
6329
|
resolveWindowsFreshRuntimeWorkerpalPrewarmDelayMs,
|
|
6158
6330
|
resolveRuntimeGitExecutableCandidates,
|
|
6159
6331
|
resolveRuntimeDockerExecutableCandidates,
|
package/package.json
CHANGED
|
@@ -127,6 +127,7 @@ _SMALL_TASK_ROLLOUT_WATCHDOG_S = 240
|
|
|
127
127
|
_NARROW_TEST_TASK_ROLLOUT_WATCHDOG_S = 150
|
|
128
128
|
_WEB_REVIEW_ROLLOUT_WATCHDOG_S = 180
|
|
129
129
|
_BACKGROUND_ROLLOUT_WATCHDOG_S = 90
|
|
130
|
+
_MIN_AUTO_WATCHDOG_TIMEOUT_S = 180
|
|
130
131
|
_MIN_CODEX_RECOVERY_ATTEMPT_S = 120
|
|
131
132
|
_NO_PUBLISHABLE_FAILURE_COOLDOWN_MS = 10 * 60 * 1000
|
|
132
133
|
_CODEX_STARTUP_ONLY_EVENT_TYPES = {"thread.started", "turn.started"}
|
|
@@ -664,6 +665,9 @@ def _looks_like_narrow_test_task_prompt(prompt: str) -> bool:
|
|
|
664
665
|
"ranking contract",
|
|
665
666
|
"regression coverage",
|
|
666
667
|
"focused coverage",
|
|
668
|
+
"focused test",
|
|
669
|
+
"focused tests",
|
|
670
|
+
"focused testing",
|
|
667
671
|
"focused regression",
|
|
668
672
|
"test-only",
|
|
669
673
|
"test only",
|
|
@@ -742,7 +746,7 @@ def _resolve_no_edit_watchdog_seconds(
|
|
|
742
746
|
else:
|
|
743
747
|
return max(1, min(parsed, max(1, communicate_timeout_s - 1)))
|
|
744
748
|
|
|
745
|
-
if communicate_timeout_s <
|
|
749
|
+
if communicate_timeout_s < _MIN_AUTO_WATCHDOG_TIMEOUT_S:
|
|
746
750
|
return None
|
|
747
751
|
|
|
748
752
|
prompt_text = str(prompt or "").lower()
|
|
@@ -887,7 +891,7 @@ def _resolve_rollout_watchdog_seconds(
|
|
|
887
891
|
communicate_timeout_s: Optional[int],
|
|
888
892
|
no_edit_watchdog_s: Optional[int],
|
|
889
893
|
) -> Optional[int]:
|
|
890
|
-
if not communicate_timeout_s or communicate_timeout_s <
|
|
894
|
+
if not communicate_timeout_s or communicate_timeout_s < _MIN_AUTO_WATCHDOG_TIMEOUT_S:
|
|
891
895
|
return None
|
|
892
896
|
|
|
893
897
|
raw = os.environ.get("WORKERPALS_OPENAI_CODEX_ROLLOUT_WATCHDOG_S", "").strip()
|
|
@@ -2156,6 +2156,24 @@ class OpenAICodexRuntimeConfigTests(unittest.TestCase):
|
|
|
2156
2156
|
|
|
2157
2157
|
self.assertEqual(watchdog_s, 180)
|
|
2158
2158
|
|
|
2159
|
+
def test_review_fix_child_budget_below_ten_minutes_still_uses_watchdogs(self) -> None:
|
|
2160
|
+
prompt = (
|
|
2161
|
+
"Rejected PR revision brief: Previous ReviewAgent score: 8.0 / 10. "
|
|
2162
|
+
"Add focused tests for createCleanupHarness.runTask covering successful execution, "
|
|
2163
|
+
"execute failure, cleanup failure, invalid task input, and cleanup execution after "
|
|
2164
|
+
"successful task completion."
|
|
2165
|
+
)
|
|
2166
|
+
env = {
|
|
2167
|
+
"WORKERPALS_OPENAI_CODEX_NO_EDIT_WATCHDOG_S": "",
|
|
2168
|
+
"WORKERPALS_OPENAI_CODEX_ROLLOUT_WATCHDOG_S": "",
|
|
2169
|
+
}
|
|
2170
|
+
with mock.patch.dict(os.environ, env, clear=False):
|
|
2171
|
+
no_edit_s = _resolve_no_edit_watchdog_seconds(prompt, 570)
|
|
2172
|
+
rollout_s = _resolve_rollout_watchdog_seconds(prompt, 570, no_edit_s)
|
|
2173
|
+
|
|
2174
|
+
self.assertEqual(no_edit_s, 180)
|
|
2175
|
+
self.assertEqual(rollout_s, 120)
|
|
2176
|
+
|
|
2159
2177
|
def test_no_edit_recovery_guidance_warns_against_artifact_only_progress(self) -> None:
|
|
2160
2178
|
guidance = _build_no_edit_recovery_guidance(
|
|
2161
2179
|
"item.completed | still inspecting",
|