@pushpalsdev/cli 1.1.43 → 1.1.44
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/package.json
CHANGED
|
@@ -107,6 +107,7 @@ _MAX_WRAPPER_BOOTSTRAP_TOTAL_CHARS = 5_000
|
|
|
107
107
|
_MAX_CREDIBLE_WRAPPER_LOOP_CHANGED_PATHS = 8
|
|
108
108
|
_MAX_CREDIBLE_WRAPPER_LOOP_TOP_LEVELS = 4
|
|
109
109
|
_MAX_STARTUP_STALL_RECOVERY_ATTEMPTS = 1
|
|
110
|
+
_MAX_STARTUP_STALL_DURING_NO_EDIT_RECOVERY_ATTEMPTS = 2
|
|
110
111
|
_MAX_NO_EDIT_RECOVERY_ATTEMPTS = 1
|
|
111
112
|
_MAX_ROLLOUT_RECOVERY_ATTEMPTS = 1
|
|
112
113
|
_DEFAULT_NO_EDIT_WATCHDOG_S = 480
|
|
@@ -3137,21 +3138,36 @@ def _run_codex_task(
|
|
|
3137
3138
|
|
|
3138
3139
|
if no_edit_watchdog_fired:
|
|
3139
3140
|
startup_stall = _codex_trace_is_startup_stall(stdout_trace)
|
|
3140
|
-
|
|
3141
|
+
startup_stall_recovery_limit = _MAX_STARTUP_STALL_RECOVERY_ATTEMPTS
|
|
3142
|
+
if no_edit_recovery_attempt > 0:
|
|
3143
|
+
startup_stall_recovery_limit = max(
|
|
3144
|
+
startup_stall_recovery_limit,
|
|
3145
|
+
_MAX_STARTUP_STALL_DURING_NO_EDIT_RECOVERY_ATTEMPTS,
|
|
3146
|
+
)
|
|
3147
|
+
if startup_stall and startup_stall_recovery_attempt < startup_stall_recovery_limit:
|
|
3141
3148
|
retry_guidance = [
|
|
3142
3149
|
*supplemental_guidance,
|
|
3143
3150
|
_build_startup_stall_recovery_guidance(trace_excerpt),
|
|
3144
3151
|
]
|
|
3145
|
-
|
|
3152
|
+
prefer_same_model = (
|
|
3153
|
+
no_edit_recovery_attempt > 0
|
|
3154
|
+
and startup_stall_recovery_attempt < _MAX_STARTUP_STALL_RECOVERY_ATTEMPTS
|
|
3155
|
+
)
|
|
3156
|
+
recovery_model = model if prefer_same_model else _startup_stall_recovery_model(model)
|
|
3146
3157
|
recovery_detail = (
|
|
3147
|
-
f" using
|
|
3148
|
-
if
|
|
3149
|
-
else
|
|
3158
|
+
f" using same model {recovery_model!r} because an earlier attempt made tool progress"
|
|
3159
|
+
if prefer_same_model
|
|
3160
|
+
else (
|
|
3161
|
+
f" using fallback model {recovery_model!r}"
|
|
3162
|
+
if recovery_model and recovery_model != model
|
|
3163
|
+
else ""
|
|
3164
|
+
)
|
|
3150
3165
|
)
|
|
3151
3166
|
log.warning(
|
|
3152
3167
|
"Codex emitted only startup events before the no-edit watchdog; "
|
|
3153
|
-
f"restarting Codex
|
|
3168
|
+
f"restarting Codex{recovery_detail} before classifying the job terminally."
|
|
3154
3169
|
)
|
|
3170
|
+
retry_model_override = model_override if prefer_same_model else recovery_model or model_override
|
|
3155
3171
|
retry_result = _run_codex_task(
|
|
3156
3172
|
repo,
|
|
3157
3173
|
instruction,
|
|
@@ -3161,7 +3177,7 @@ def _run_codex_task(
|
|
|
3161
3177
|
startup_stall_recovery_attempt=startup_stall_recovery_attempt + 1,
|
|
3162
3178
|
no_edit_recovery_attempt=no_edit_recovery_attempt,
|
|
3163
3179
|
rollout_recovery_attempt=rollout_recovery_attempt,
|
|
3164
|
-
model_override=
|
|
3180
|
+
model_override=retry_model_override,
|
|
3165
3181
|
baseline_changes=baseline_snapshot,
|
|
3166
3182
|
execution_deadline_monotonic=overall_deadline,
|
|
3167
3183
|
)
|
|
@@ -1307,6 +1307,101 @@ class OpenAICodexRuntimeConfigTests(unittest.TestCase):
|
|
|
1307
1307
|
self.assertNotIn("no publishable", str(result.get("summary") or "").lower())
|
|
1308
1308
|
self.assertEqual(result.get("cooldownMs"), 600000)
|
|
1309
1309
|
|
|
1310
|
+
def test_run_codex_task_no_edit_recovery_retries_same_model_after_startup_stall(self) -> None:
|
|
1311
|
+
with tempfile.TemporaryDirectory(prefix="pushpals-codex-no-edit-startup-stall-") as temp_dir:
|
|
1312
|
+
repo = Path(temp_dir) / "repo"
|
|
1313
|
+
repo.mkdir(parents=True, exist_ok=True)
|
|
1314
|
+
(repo / "README.md").write_text("# no edit startup stall repo\n", encoding="utf-8")
|
|
1315
|
+
subprocess.run(["git", "init"], cwd=repo, check=True, capture_output=True, text=True)
|
|
1316
|
+
subprocess.run(
|
|
1317
|
+
["git", "config", "user.name", "PushPals Test"],
|
|
1318
|
+
cwd=repo,
|
|
1319
|
+
check=True,
|
|
1320
|
+
capture_output=True,
|
|
1321
|
+
text=True,
|
|
1322
|
+
)
|
|
1323
|
+
subprocess.run(
|
|
1324
|
+
["git", "config", "user.email", "pushpals-tests@example.com"],
|
|
1325
|
+
cwd=repo,
|
|
1326
|
+
check=True,
|
|
1327
|
+
capture_output=True,
|
|
1328
|
+
text=True,
|
|
1329
|
+
)
|
|
1330
|
+
subprocess.run(["git", "add", "README.md"], cwd=repo, check=True, capture_output=True, text=True)
|
|
1331
|
+
subprocess.run(
|
|
1332
|
+
["git", "commit", "-m", "chore: seed no-edit startup stall repo"],
|
|
1333
|
+
cwd=repo,
|
|
1334
|
+
check=True,
|
|
1335
|
+
capture_output=True,
|
|
1336
|
+
text=True,
|
|
1337
|
+
)
|
|
1338
|
+
|
|
1339
|
+
stub_path = Path(temp_dir) / "fake_codex_no_edit_startup_stall.py"
|
|
1340
|
+
stub_path.write_text(
|
|
1341
|
+
"\n".join(
|
|
1342
|
+
[
|
|
1343
|
+
"from pathlib import Path",
|
|
1344
|
+
"import json",
|
|
1345
|
+
"import sys",
|
|
1346
|
+
"import time",
|
|
1347
|
+
"",
|
|
1348
|
+
"argv = sys.argv[1:]",
|
|
1349
|
+
"last_message_path = None",
|
|
1350
|
+
"model = ''",
|
|
1351
|
+
"for index, arg in enumerate(argv):",
|
|
1352
|
+
" if arg == '--output-last-message' and index + 1 < len(argv):",
|
|
1353
|
+
" last_message_path = argv[index + 1]",
|
|
1354
|
+
" if arg == '-m' and index + 1 < len(argv):",
|
|
1355
|
+
" model = argv[index + 1]",
|
|
1356
|
+
"",
|
|
1357
|
+
"prompt = sys.stdin.read()",
|
|
1358
|
+
"has_no_edit_recovery = 'No-edit watchdog recovery' in prompt",
|
|
1359
|
+
"has_startup_recovery = 'Codex startup-stall recovery' in prompt",
|
|
1360
|
+
"if has_no_edit_recovery and has_startup_recovery and model != 'gpt-5.4':",
|
|
1361
|
+
" Path('src').mkdir(exist_ok=True)",
|
|
1362
|
+
" Path('src/no-edit-startup-stall-recovered.txt').write_text('patched after same-model restart\\n', encoding='utf-8')",
|
|
1363
|
+
" if last_message_path:",
|
|
1364
|
+
" Path(last_message_path).write_text('Patched after same-model startup-stall recovery.', encoding='utf-8')",
|
|
1365
|
+
" print(json.dumps({'type': 'item.completed', 'item': {'type': 'message', 'text': 'Patched after same-model startup-stall recovery.'}}), flush=True)",
|
|
1366
|
+
" raise SystemExit(0)",
|
|
1367
|
+
"",
|
|
1368
|
+
"print(json.dumps({'type': 'thread.started'}), flush=True)",
|
|
1369
|
+
"print(json.dumps({'type': 'turn.started'}), flush=True)",
|
|
1370
|
+
"if not has_no_edit_recovery:",
|
|
1371
|
+
" print(json.dumps({'type': 'item.started', 'item': {'id': 'cmd-read', 'type': 'command_execution', 'command': 'cat README.md', 'status': 'in_progress'}}), flush=True)",
|
|
1372
|
+
" time.sleep(0.2)",
|
|
1373
|
+
" print(json.dumps({'type': 'item.completed', 'item': {'id': 'cmd-read', 'type': 'command_execution', 'command': 'cat README.md', 'status': 'completed', 'exit_code': 0}}), flush=True)",
|
|
1374
|
+
"time.sleep(10)",
|
|
1375
|
+
]
|
|
1376
|
+
),
|
|
1377
|
+
encoding="utf-8",
|
|
1378
|
+
)
|
|
1379
|
+
|
|
1380
|
+
env_overrides = {
|
|
1381
|
+
"PUSHPALS_OPENAI_CODEX_BIN_JSON": json.dumps([sys.executable, str(stub_path)]),
|
|
1382
|
+
"PUSHPALS_OPENAI_CODEX_AUTH_MODE": "api_key",
|
|
1383
|
+
"OPENAI_API_KEY": "pushpals-no-edit-startup-stall-test-key",
|
|
1384
|
+
"WORKERPALS_OPENAI_CODEX_JSON": "true",
|
|
1385
|
+
"WORKERPALS_OPENAI_CODEX_TIMEOUT_S": "30",
|
|
1386
|
+
"WORKERPALS_OPENAI_CODEX_NO_EDIT_WATCHDOG_S": "1",
|
|
1387
|
+
"WORKERPALS_OPENAI_CODEX_NO_EDIT_COMMAND_GRACE_S": "1",
|
|
1388
|
+
"WORKERPALS_OPENAI_CODEX_NO_EDIT_COMMAND_PROGRESS_CAP_S": "1",
|
|
1389
|
+
"WORKERPALS_OPENAI_CODEX_STARTUP_STALL_WATCHDOG_S": "1",
|
|
1390
|
+
"WORKERPALS_OPENAI_CODEX_PROGRESS_LOG_INTERVAL_S": "1",
|
|
1391
|
+
}
|
|
1392
|
+
with mock.patch.dict(os.environ, env_overrides, clear=False):
|
|
1393
|
+
result = _run_codex_task(
|
|
1394
|
+
str(repo),
|
|
1395
|
+
"Add one focused regression assertion after reading the hinted test.",
|
|
1396
|
+
[],
|
|
1397
|
+
)
|
|
1398
|
+
|
|
1399
|
+
self.assertTrue(result.get("ok"), result)
|
|
1400
|
+
self.assertEqual(result.get("exitCode"), 0)
|
|
1401
|
+
stdout = str(result.get("stdout") or "")
|
|
1402
|
+
self.assertIn("same-model startup-stall recovery", stdout)
|
|
1403
|
+
self.assertIn("src/", stdout)
|
|
1404
|
+
|
|
1310
1405
|
def test_run_codex_task_retries_once_when_no_edit_watchdog_fires(self) -> None:
|
|
1311
1406
|
with tempfile.TemporaryDirectory(prefix="pushpals-codex-no-edit-watchdog-") as temp_dir:
|
|
1312
1407
|
repo = Path(temp_dir) / "repo"
|