Skip to content

fix(rate-limits): stop driving a hidden Codex TUI to read usage - #23806

Merged
brennanb2025 merged 6 commits into
mainfrom
brennanb2025/bug-background-rate-limit-pty-interrupts-codex-s
Sep 30, 2026
Merged

brennanb2025 merged 6 commits into
mainfrom
brennanb2025/bug-background-rate-limit-pty-interrupts-codex-s

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 12 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​86 $\color{#cf222e}{\Huge{\mathbf{−}}}$​621 $\color{#cf222e}{\Huge{\mathbf{−}}}$​535
Prod 10 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​38 $\color{#cf222e}{\Huge{\mathbf{−}}}$​391 $\color{#cf222e}{\Huge{\mathbf{−}}}$​353

What this fixes

Orca's background Codex usage check could break the user's Codex install. When a newer Codex was out, the check selected "Update now" in Codex's update prompt and then killed the installer 15 seconds later, leaving codex missing or failing with Missing optional dependency @openai/codex-<platform>. Nothing in Orca reported it; the status bar only said "Refresh failed".

Who hits it: anyone signed in to Codex on macOS or Linux whose installed Codex is older than the latest release, whenever Orca's headless usage call fails for a reason other than sign-in (a slow or dropped network, a timeout, a Codex process that exits early). It fires on window focus, show and restore, and on the 15-minute timer while Orca is focused. It repeats on every check, because the install never finishes so Codex shows the prompt again. It breaks npm installs (npm install -g @openai/codex) and would equally interrupt the standalone installer (curl … install.sh | sh) or brew upgrade.

Not affected: Windows, including WSL homes, which never ran this fallback. Terminal and chat panes, where the user answers Codex's prompts themselves. Claude, Gemini and other providers' usage checks.

This is not a regression from a recent PR: the hidden-terminal fallback has existed since the usage status bar was first added (#483).

ELI5

To show how much Codex quota you have left, Orca asks Codex quietly in the background. If that quiet question failed, Orca used to open an invisible Codex window, type /status, and press Enter without looking at the screen. When Codex was showing "Update available — 1. Update now", that blind Enter started the update, and Orca then closed the invisible window mid-install. Now Orca never opens that invisible window; if the quiet question fails, it asks the same usage server directly over the web instead.

What Changed

Before: usage check → headless codex app-server call → on a non-sign-in failure, a hidden interactive Codex in a hidden terminal → /status, Enter at +350 ms and again at +3 s → terminal killed at 15 s. With an update pending, Codex exits its screen and runs its installer inside that hidden terminal, and the 15 s kill interrupts it.

After: usage check → headless codex app-server call → on a non-sign-in failure, a read of the HTTP usage endpoint (chatgpt.com/backend-api/wham/usage) with the access token Codex keeps in auth.json → if there is no token or the request fails, the headless call's error is shown. No Codex screen is ever opened by a background check, so no Codex prompt can be answered by it.

For the user: Codex is never updated, trusted, or otherwise changed by Orca's usage check. A failed check no longer spends up to 15 s in a hidden terminal. When the headless call fails but the web read succeeds, usage still shows, as it did when the hidden terminal succeeded.

Mechanism:

  • codex-fetcher.ts: removed the hidden-terminal path. A non-sign-in headless failure now falls back to fetchBackendUsage, the existing HTTP reader that WSL homes already try first (it was fetchWslBackend). WSL homes skip the second HTTP attempt, since they already made it.
  • Deleted codex-pty-rate-limit-probe.ts and codex-pty-status-parser.ts (the screen driver and the /status text parser) and their tests.
  • Removed the Codex-only allowPtyFallback option and shouldAllowCodexPtyFallback(). Claude's option of the same name is untouched.

Why

Nothing needs the hidden terminal. Codex's own /status screen gets its numbers by sending the same account/rateLimits/read request Orca's headless call already makes, so the hidden terminal was a slow second try of the same request, not a different source. That request has been in Codex since 0.48 (October 2025), older than Orca's usage bar. Sign-in failures never reached the fallback, and Windows never had it. After a failed check the status bar keeps the last good numbers for 30 minutes while it retries, so a brief failure does not blank it.

The HTTP read keeps the one case the fallback did cover. That case is Orca's headless Codex process failing while a fresh Codex would have succeeded. Orca already has this reader: WSL homes use it first, and checks already call it when the headless call returns no 5-hour window. It reads the same numbers without starting Codex.

Alternatives considered:

  • Launch the hidden terminal with -c check_for_update_on_startup=false (Codex's own switch; the workaround in the issue). It removes only the update prompt. The same blind Enter still answers Codex's other startup screens: its "trust this folder?" screen defaults to Trust, which writes to the user's Codex config. An admin-managed Codex config can also force the update check back on.
  • Read the screen and only type once the normal prompt is showing. This adds screen-matching rules against a UI that changes every Codex release, and it still has a background check driving an interactive app.
  • Detect a broken install and offer a one-click reinstall. This is worth doing separately for people already hit, but it does not prevent the bug.

Deliberate widening, disclosed: Windows and the switcher's background previews of inactive accounts previously got no fallback at all (the hidden terminal could crash on Windows, and previews shouldn't start processes). Neither reason applies to an HTTP request, so both now get the HTTP retry too. It uses the same token and endpoint Orca already uses for WSL homes and for a missing 5-hour window.

Linked Issue

Fixes #17415

Visual Proof

Reproduced live in an isolated dev Orca. It used the real Codex 0.157.0 showing its update prompt, a fake npm that only logs and sleeps, a separate HOME, CODEX_HOME and profile, and the headless call forced to fail. Nothing on the real machine was touched: the real ~/.codex was byte-identical afterwards.

The status bar reads "Refresh failed" in both builds, because the test forces the headless call to fail and has no HTTP token. The difference is what happened behind it:

Before (unmodified main) After (this PR)
Codex launches per usage refresh 2 (headless call, then a hidden interactive Codex) 1 (headless call only)
npm install -g @openai/codex started 2 of 2 refreshes, each killed by SIGHUP 11.0 s and 11.5 s in, before finishing 0
Time for a failed refresh ~15 s, ending in PTY timeout ~0.1 s, ending in the headless call's own error

Before, from the fake npm's log (the parent chain is the hidden Codex started by Orca's main process, in Orca's rate-limit-pty-cwd folder):

[22:41:15.751] npm-stub START ppid=<hidden codex> cwd=<profile>/rate-limit-pty-cwd argv=[install -g @openai/codex]
[22:41:26.787] npm-stub RECEIVED SIGHUP after 11.031s (before completion)

After: the fake npm's log is empty, and the Codex log shows only the headless call for each refresh.

Before:

Usage panel before the fix: Codex · Refresh failed after a 15 s hidden-terminal run that started and killed npm

After:

Usage panel after the fix: Codex · Refresh failed from the headless call alone, with no hidden terminal and no npm

Testing

  • I manually tested these changes locally (macOS arm64, isolated dev app, before and after as above)

  • Automated tests added/updated

  • Rate-limit suites: 54 files, 649 tests pass. The hidden-terminal cases were replaced with HTTP-fallback cases, and each asserts that no hidden terminal is spawned. Coverage: the web read serves usage when the headless call fails; the headless error stays when there is no token; aborting skips the web read; WSL homes don't repeat the web read; sign-in errors don't fall back.

  • Run against the previous fetcher, the 4 new or changed fallback tests fail.

  • Tests where the headless call fails now use a temp Codex home or a mocked fetch, so none can read the developer's real auth.json or reach the network.

  • tsc for the main-process project passes. oxlint is clean on the changed files.

  • Not tested live: Windows, Linux, WSL and SSH. Also not tested live: the success path where the web read returns usage after a headless failure (that needs a real account token); it is covered by the unit test above.

Review

Agent skill upstream boundary

  • Not applicable, or this change follows docs/reference/agent-skill-sharing-upstream-boundary.md and copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

  • Security: no new credential use. The web read uses the same auth.json token and endpoint Orca already uses for WSL homes and for a missing 5-hour window. One process-spawning path is removed.
  • Cross-platform: macOS and Linux lose the hidden terminal. Windows and inactive-account previews gain the web retry (see Why).
  • SSH: unaffected; these checks run on the local host.
  • Backwards compatibility: no wire or persisted-state change.
  • Performance: a failed check no longer holds a hidden terminal for up to 15 s (about 0.1 s instead of 15 s in the test above, where the headless call failed instantly). There is no hidden terminal process and no screen parsing.
  • Stale comment left in place: config/relay-assets/node-pty-1.1.0-windows-pty-teardown-patch.cjs still mentions the deleted Codex probe in a comment. It is a hashed relay asset, so editing it would make every SSH host reinstall its relay, and the mention was already inaccurate because that probe never ran on Windows. The matching test comment is updated.
  • Users already hit by this: reinstall with npm install -g @openai/codex@latest. Detecting and repairing a broken install is a possible follow-up, not part of this PR.

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred). Scoped lint, main-process typecheck and rate-limit tests pass locally; the full runs are left to CI.

When the headless Codex usage call failed, Orca opened a hidden
interactive Codex, typed /status and pressed Enter without reading the
screen, then killed it after 15 s. If Codex showed its "Update available"
prompt, that Enter picked "Update now", Codex started its installer, and
the 15 s kill interrupted it, leaving the global install broken.

Drop the hidden-terminal fallback. When the headless call fails with a
non-sign-in error, read the same usage from the HTTP endpoint Orca
already calls on WSL and for the 5-hour window, so a usage check can no
longer answer any Codex startup screen.

Fixes #17415
Also drop comments that still described the removed hidden-terminal fallback.
The changed-code gate rejects the new type assertions this PR added.
@brennanb2025
brennanb2025 marked this pull request as ready for review September 30, 2026 00:09
@greptile-apps

greptile-apps Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 3/5

[Medium risk] Removes the hidden PTY fallback from rate-limit fetching.

The PR does not appear safe to merge until empty HTTP usage is rejected and review-note reservations cover delivery on older hosts.

Findings

  1. P1 Empty usage replaces valid data ▶

Summary

The PR replaces the hidden interactive Codex usage fallback with an HTTP read. Changes since the previous review also add mobile agent-launch and review-note handling, Codex trust-config repair, a Vitest home-write guard, and updates to desktop tab and agent presentation.

  • The mobile review-note reservation does not cover the asynchronous send on older hosts.
  • The prior empty-usage finding remains outstanding.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  Check[Codex usage refresh] --> RPC[Headless app-server read]
  RPC -->|Success| Usage[Show usage]
  RPC -->|Non-auth failure| HTTP[HTTP usage read]
  HTTP -->|Usable response| Usage
  HTTP -->|Failure| Error[Keep RPC error]
Loading

Reviews (4) · Last reviewed commit: "Merge remote-tracking branch 'origin/mai..."

if (options?.signal?.aborted) {
return abortedCodexRateLimitResult()
}
return backendResult ?? rpcFailure

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Empty usage replaces valid data

If the RPC check fails and the HTTP endpoint returns a plan type but no rate-limit windows, the backend reader marks that response as successful. This line then returns empty usage instead of the RPC error, so the service replaces the last good numbers rather than retaining them during a failed refresh.

@coderabbitai

coderabbitai Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Warning

Review limit reached

Next included review available in 11 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 10 included reviews currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: a82b1e87-974f-46ba-86d5-ab7c33c68795

📥 Commits

Reviewing files that changed from the base of the PR and between bafe52a and 1f3024a.

📒 Files selected for processing (22)
  • config/scripts/node-pty-windows-pty-teardown-patch.test.mjs
  • src/main/codex-accounts/codex-account-registration.ts
  • src/main/codex-accounts/service-quota-refresh-decoupling.test.ts
  • src/main/rate-limits/codex-fetcher-auth-errors.test.ts
  • src/main/rate-limits/codex-fetcher-probe-shutdown.test.ts
  • src/main/rate-limits/codex-fetcher-process-contract.test.ts
  • src/main/rate-limits/codex-fetcher-pty-settle.test.ts
  • src/main/rate-limits/codex-fetcher-rpc-exit-diagnostics.test.ts
  • src/main/rate-limits/codex-fetcher-runtime-pairing.test.ts
  • src/main/rate-limits/codex-fetcher.test.ts
  • src/main/rate-limits/codex-fetcher.ts
  • src/main/rate-limits/codex-pty-rate-limit-probe.test.ts
  • src/main/rate-limits/codex-pty-rate-limit-probe.ts
  • src/main/rate-limits/codex-pty-status-parser.test.ts
  • src/main/rate-limits/codex-pty-status-parser.ts
  • src/main/rate-limits/codex-rate-limit-fetch-options.ts
  • src/main/rate-limits/service-inactive-account-previews.test.ts
  • src/main/rate-limits/service/service-fetch-targets.ts
  • src/main/rate-limits/service/service-full-cycle-preparation.ts
  • src/main/rate-limits/service/service-inactive-accounts.ts
  • src/main/rate-limits/service/service-provider-cycles.ts
  • src/shared/codex-auth-errors.ts

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: de5513b1-2f5d-4a14-94e1-b73b1e2c8feb

📥 Commits

Reviewing files that changed from the base of the PR and between 3ba125d and bafe52a.

📒 Files selected for processing (5)
  • src/main/rate-limits/codex-fetcher-process-contract.test.ts
  • src/main/rate-limits/codex-fetcher-rpc-exit-diagnostics.test.ts
  • src/main/rate-limits/codex-fetcher-runtime-pairing.test.ts
  • src/main/rate-limits/codex-fetcher.test.ts
  • src/main/rate-limits/codex-fetcher.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 4 remain after this review.


📝 Walkthrough

Walkthrough

The Codex rate-limit fetcher removes the PTY probe and parser. For WSL homes, it tries HTTP usage retrieval before RPC. For non-WSL homes, it tries RPC first and then HTTP after an RPC failure. The fetch options and service callers no longer include allowPtyFallback. Tests cover HTTP fallback paths and remove PTY-specific coverage.

Priority: ⬆️ High

Severity of issue fixed: High

Merge Risk: ⚪ Minimal · up to bafe5

The change removes the hidden interactive Codex probe and replaces its fallback with HTTP usage retrieval. No concrete merge-blocking issue is established; merge after normal checks pass.

Security Architecture Review

Security architecture risk: 🔵 Low · up to bafe5

This removes automatic interaction with Codex update prompts. Background usage requests now run in more account and platform contexts, but selected-account and cancellation safeguards remain. No material security regression was established in the examined paths.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The changed exposure is additional background HTTP usage reads for non-WSL callers, including inactive accounts and Windows contexts that previously disabled fallback. Examined inactive-account requests use each account's managed home rather than copying credentials into the shared runtime location.

Trust Boundaries and Controls

  • observed — The backend helper reads auth.json from the selected Codex home and sends its access token and optional account ID to the fixed usage endpoint. Inactive-account publication checks cancellation, account generation, and current account membership; active publication checks cancellation, generation, and home provenance.

Resilience and Maintainability Implications

  • observed — RPC settlement is guarded against repetition and removes its timer and registered data, error, close, and abort listeners. Abort, timeout, and process-error paths request termination; process close produces an error result. HTTP requests combine caller cancellation with a timeout, and reset-result publication separately validates target and home provenance.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 12 functions across 12 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: removing the hidden Codex TUI used to read rate-limit usage.
Description check ✅ Passed The description follows the required template and provides a clear ELI5 summary, user-facing changes, rationale, linked issue, visual proof, testing details, review notes, cross-platform consideration…
Linked Issues check ✅ Passed The PR meets the coding requirements in [#17415]. src/main/rate-limits/codex-fetcher.ts removes the interactive PTY fallback and uses the HTTP usage endpoint with the token from auth.json after a …
Out of Scope Changes check ✅ Passed The changes stay within [#17415]. Production changes remove the destructive hidden Codex TUI path and update callers and comments for the HTTP flow. Test updates, deleted PTY parser tests, and no-PTY …
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes

  • Deletes the hidden Codex TUI probe — removes codex-pty-rate-limit-probe.ts, codex-pty-status-parser.ts, and their tests, plus the Codex-only allowPtyFallback option and shouldAllowCodexPtyFallback(). Claude's identically-named option is untouched.
  • Replaces the fallback with the existing HTTP usage reader — on a non-sign-in headless failure, fetchCodexRateLimits now calls fetchBackendUsage (the chatgpt.com/backend-api/wham/usage reader, renamed from fetchWslBackend) instead of driving codex. WSL homes keep their HTTP-first ordering and skip the second read; non-WSL homes return backendResult ?? rpcFailure.
  • Widens the retry to Windows and inactive-account previews, which previously had no fallback. Disclosed in the PR body.
  • Fallout cleanups — buildWslCodexCommand drops its now-single-valued isolateRpcStdio parameter, the auth-error comment in codex-auth-errors.ts is updated, and callers in the rate-limit service shed the removed option.

The core flow checks out: auth errors short-circuit before the fallback, abort paths return abortedCodexRateLimitResult() before any HTTP read, and a failed HTTP read falls back to the RPC failure. pnpm tc:node and the seven codex/rate-limit suites (57 tests) pass locally; tests that exercise a failing headless call use a temp home or stubbed fetch, so none touch a real login or the network.

ℹ️ The HTTP fallback cannot refresh an expired token

The removed interactive codex would refresh the ChatGPT token in auth.json before reading usage; getCodexBackendAuthHeaders reads the stored access_token verbatim. This only diverges when the headless app-server fails before it can refresh (for example an older codex rejecting the probe flags) and the stored token is stale — then the web read returns 401 and the RPC error surfaces instead of usage. The blast radius is bounded (the status bar keeps the last good numbers for 30 minutes and retries), and this is a defensible trade against the install-corruption bug, so I am not asking for a change — just flagging it since the PR body discusses the flag-incompatibility case but not token freshness.

Technical details
# Token freshness in the Codex HTTP fallback

## Affected sites
- `src/main/rate-limits/codex-fetcher.ts` — non-WSL branch reaches `fetchBackendUsage` after the RPC failure.
- `src/main/rate-limits/codex-backend-auth.ts:74` — `getCodexBackendAuthHeaders` only reads `auth.json`; it never refreshes.
- `codex-rpc-rate-limit-probe.ts:161` — the headless app-server refreshes tokens itself, so the gap is only the "app-server never started" failure mode.

## Why it is only a note
- `fetchViaRpc` failures that happen after startup carry a refreshed token, so HTTP succeeds.
- When the app-server cannot start, the fallback may read a stale token; the result is a failed refresh, not a wrong number.
- The PR already bounds the user impact (30-minute last-good retention) and the alternative (running the TUI) is exactly what this PR removes.

## Open question for the human (optional)
- Is a stale-token 401 worth a distinct, non-auth error message so the user knows a usage refresh failed vs. their install being broken?

Pullfrog  | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes

  • 649f07aa merges origin/main into the branch. The net PR diff against the new merge base (fed1eca4) is byte-identical to the version reviewed at 3ba125da — no files, behavior, or tests changed.

No new concerns. The prior review's observations (the HTTP fallback's inability to refresh an expired auth.json token, and the disclosed stale comment in the content-hashed relay asset) still stand and were not re-raised.

Pullfrog  | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@brennanb2025

Copy link
Copy Markdown
Contributor Author

Review status: ready for review

The problem (#17415): when Orca's headless Codex usage call failed for a reason other than sign-in, Orca opened a hidden interactive Codex, typed /status and pressed Enter without looking at the screen, then killed it after 15 s. When a Codex update was pending, that Enter picked "Update now", and the 15 s kill interrupted the installer. That left the user's global Codex install broken (codex missing, or Missing optional dependency @openai/codex-<platform>), and it happened again on every usage check.

What changes for the user: Orca's background usage check never opens an interactive Codex, so it can never answer a Codex prompt or start an update. If the headless call fails, Orca reads the same usage over the web with the token Codex already keeps in auth.json. If that works, usage still shows. If it fails too, the status bar shows the headless call's own error ("Refresh failed", with the reason in the usage menu), and the last good numbers stay for up to 30 minutes. A failed check now takes well under a second instead of about 15 s.

Fixed during review (commits befeca1 and 3ba125d, no production code change):

  • Added a test for the most common new case: the headless call fails and the web read is rejected. It pins that the user sees the headless call's real error, not a generic one. Breaking the fallback on purpose makes this test fail.
  • Rewrote four code comments that still described the deleted hidden-terminal fallback.
  • CI's changed-code check rejected three type casts (as Response) in the updated test file, which also stopped CI from running the tests. The tests now build real Response objects.

Deferred (minor, none user-visible except the first):

  • On Macs where Tailscale MagicDNS is the only DNS resolver, a timed-out headless call no longer adds the Tailscale DNS hint to its error. Error and early-exit cases still add it. It is unverified that a DNS failure shows up as a timeout rather than an error.
  • config/scripts/ci-shard-timings.json still lists the deleted test files. They are only used as timing weights, so this is harmless.
  • CODEX_DISABLE_PLUGINS_ARGS no longer needs to be exported.
  • Two redundant cancellation checks in the web-read helper (older than this PR).

Found, but not caused by this PR: Claude's usage check has the same kind of hidden-terminal fallback (src/main/rate-limits/claude-pty.ts). It presses Enter blind every 0.8 s and answers y to any "do you trust" prompt. This should be its own issue.

What was verified:

  • Code review of the whole PR by independent reviewers (correctness, scope and design), plus a final readiness pass on the final diff. None found a problem above minor.
  • Confirmed from the Codex CLI source that the headless codex app-server Orca still uses never reaches Codex's self-updater, which only runs after the interactive screen exits.
  • Checked that the web read uses the same Codex home, and so the same account, as the headless call, including managed accounts and inactive-account previews. A failed check can't take longer than before, and WSL homes still try the web read only once.
  • All 97 rate-limit and Codex-account test files pass locally (942 tests), as do the main-process typecheck, lint and the changed-code check.
  • CI kept testing this branch against an older main that had its own type error (fixed on main by fix(native-chat): main's draft-queue tests follow #23524's /clear (fixes main) #23940), so current main was merged in (649f07a). CI is green on 649f07a. The one retry was an unrelated native-chat test that fails intermittently on main.
  • Live on a second Mac (M4 MacBook Air, real Codex 0.158.0). The test setup used its own home and profile, a fake npm, curl and brew, an API-key-only auth.json, a pending Codex update, and a codex wrapper that refuses the headless call so the fallback always runs. Each refresh was triggered from the app:
Before (current main) After (this PR)
Hidden interactive Codex launched 2 (one per refresh) 0
npm install -g @openai/codex started 2, each killed mid-install after about 11.5 s 0
Time per refresh 15.4 s / 15.1 s 0.12 s / 0.06 s
Error in the usage menu "PTY timeout" "Codex RPC process exited (exit code 1): …" (the headless call's real reason)
Status bar "⚠ Refresh failed" "⚠ Refresh failed"

Before: the usage menu shows "PTY timeout". Behind it, each refresh started a Codex update that was killed partway through.

Before: Codex usage menu reads Refresh failed / PTY timeout

After: the same failure shows the headless call's own error, and nothing else was launched.

After: Codex usage menu reads Refresh failed / Codex RPC process exited (exit code 1)

After, with the headless call still refused but a ChatGPT token present: the web read fills in usage (Plus, 5h 37%, weekly 12%) in 87 ms. The app's two web requests (usage and reset credits) were answered inside the test app, so nothing reached the network.

After: usage shows Codex Plus, 5h 37%, weekly 12% from the web read

What was not verified: Windows, Linux, WSL and SSH were not tested live. The web read was never tested against the real chatgpt.com with a real account token. The success case above answered the app's web request locally, and no fake token was ever sent to chatgpt.com.

…ground-rate-limit-pty-interrupts-codex-s

# Conflicts:
#	src/main/rate-limits/codex-fetcher.test.ts
#	src/main/rate-limits/codex-fetcher.ts

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes

  • Adopted the shared spawnProcess chokepoint for the Codex RPC probe — fetchViaRpc now hands the resolved CLI to resolveSpawn instead of pre-wrapping an npm .cmd shim in cmd.exe via getSpawnArgsForWindows; the explicit windowsHide is dropped because the chokepoint pins it. WSL still spawns wsl.exe through buildWslExecArgs.
  • Updated the probe-termination comment to say the Windows launcher may now be node (resolved shim) or cmd.exe (unresolved shim), not always cmd.exe.
  • Retargeted the Codex fetcher tests to the spawnProcess seam — codex-fetcher*.test.ts now mock ../../shared/child-process/run-process (the budget/process-contract tests run the node stub through the real chokepoint) and drop the win32-utils/getCmdExePath stubs, with the win32 spawn assertion now expecting the bare CLI rather than cmd.exe.

This delta comes from merging origin/main, which had already moved fetchViaRpc to the spawn chokepoint while retaining the PTY fallback; the merge combines that with this PR's removal of the PTY path. Behavior for the RPC probe is unchanged: POSIX spawns the resolved CLI exactly as before, and Windows now lets spawnProcess resolve an npm/pnpm .cmd shim to its real target (falling back to a properly quoted cmd.exe line for unrecognised shims). The ['pipe','pipe','pipe'] stdio the RPC reader depends on is preserved. No new concerns.

Pullfrog  | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@brennanb2025
brennanb2025 merged commit 59c05d3 into main Sep 30, 2026
35 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Background rate-limit PTY interrupts Codex self-update, leaving the global npm install corrupted

1 participant