diff --git a/autoplan/SKILL.md b/autoplan/SKILL.md index 4bba21fe2..8fb5117ca 100644 --- a/autoplan/SKILL.md +++ b/autoplan/SKILL.md @@ -773,6 +773,8 @@ else _CODEX_MODE="sandbox_unavailable" elif [ "$_CODEX_MP" -eq 2 ]; then _CODEX_MODE="broken_install" + elif [ "$_CODEX_MP" -eq 4 ]; then + _CODEX_MODE="quota_exhausted" elif [ "$_CODEX_MP" -ne 0 ]; then _CODEX_MODE="model_unusable" elif [ "${_GSTACK_CODEX_PROBE_STATE:-}" = inconclusive ]; then @@ -791,6 +793,7 @@ Branch on the echoed `CODEX_MODE`: - **`not_authed`** — installed but no credentials. Print: "Codex not authenticated; outside coverage unavailable. Run `codex login` or set `$CODEX_API_KEY`." Keep the required Claude adversarial pass; do not dispatch a duplicate. - **`broken_install`** — the CLI is on PATH but cannot execute (spawn ENOENT, non-executable binary, missing vendor payload). Print: "Codex is installed but its binary cannot run — Codex passes skipped. Reinstall: `npm install -g @openai/codex`." Relay the probe's HINT lines. Keep the required Claude adversarial pass; do not dispatch a duplicate. - **`model_unusable`** — the selected model (see `CODEX_MODEL:`) is invalid or unavailable to the account (HTTP 400 on every call). Relay the probe's HINT lines and the fix (`GSTACK_CODEX_MODEL=` or config.toml `model`); never substitute a model. Keep the required Claude adversarial pass; do not dispatch a duplicate. The ~10s round trip is cached for 1h. +- **`quota_exhausted`** — Codex usage/rate limit: relay its line verbatim (reset time), no more Codex calls. Keep the required Claude adversarial pass; do not dispatch a duplicate. - **`sandbox_unavailable`** — Codex's sandbox cannot start here (containers without user namespaces); the probe printed the reason and fix. No paid call ran; outside coverage is unavailable. Keep the required Claude adversarial pass; do not dispatch a duplicate. - **`ready`** or **`unverified`** — run the Codex pass below. `unverified` means the model check timed out; say so, and let the pass's own verdict decide. diff --git a/bin/gstack-codex-probe b/bin/gstack-codex-probe index 151415f2c..772d2487e 100755 --- a/bin/gstack-codex-probe +++ b/bin/gstack-codex-probe @@ -8,7 +8,8 @@ # _gstack_codex_sandbox_mode — read-only, or full access only for GSTACK_CODEX_NO_SANDBOX=1 # _gstack_codex_sandbox_preflight — free `codex sandbox` check before paid calls (Linux) # _gstack_codex_first_use_notice — once per machine: provider, account, how to turn off -# _gstack_codex_model_probe — round-trip probe of the selected model (#2477) +# _gstack_codex_model_probe — round-trip probe of the selected model (#2477); +# exit 4 = the account's Codex usage limit (#3051) # _gstack_codex_version_check — warn on known-bad Codex CLI versions # _gstack_codex_timeout_wrapper — gtimeout -> timeout -> bash-native watchdog # _gstack_codex_log_event — telemetry emission to ~/.gstack/analytics/ @@ -256,6 +257,12 @@ _gstack_codex_model_probe() { echo "MODEL_OK (cached)" return 0 fi + if [ "$_c_status" = "MODEL_QUOTA_EXHAUSTED" ] && [ "$_c_sig" = "$_sig" ] && [ $((_now - _c_ts)) -lt 900 ]; then + echo "MODEL_QUOTA_EXHAUSTED (cached)" + sed -n '2,4p' "$_cache" 2>/dev/null + echo "HINT: Codex refused this account's calls for its usage limit within the last 15 minutes; outside coverage is unavailable until the reset time above. No Codex call was made." + return 4 + fi if [ "$_c_status" = "MODEL_UNUSABLE" ] && [ "$_c_sig" = "$_sig" ] && [ $((_now - _c_ts)) -lt 900 ]; then echo "MODEL_UNUSABLE (cached)" echo "HINT: gstack requested model '$_model' (source: $_GSTACK_CODEX_SEL_SRC)." @@ -271,6 +278,23 @@ _gstack_codex_model_probe() { echo "MODEL_OK" return 0 fi + # A usage limit or rate limit is the account, not the model or the network + # (#3051): Codex says so plainly ("You've hit your usage limit ... try again + # at