Repository navigation
Offer Refresh when Codex loses its sign-in - #137
Conversation
Papilot's Codex reads its sign-in once, when it starts. When another Codex on the host signs in again or rotates the shared refresh token, its turns fail with "Your access token could not be refreshed ... Please sign in again.", yet Papilot still showed Codex as ready, and Reconnect left the running process alone, so only restarting Papilot recovered. A Codex turn or account read that fails for lack of a sign-in now shows Codex signed out, which holds queued tasks. In that state the connection notice and the agent's settings offer Refresh instead of Reconnect: it starts Codex again, so it uses the host's current sign-in. Turns in progress are interrupted and can be resumed.
|
@codex review |
Codex Review Summary: ✅ CompletedThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e8bc2cc313
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| agent.close(); | ||
| this.disconnected(id, new Error('Codex restarted to load the current sign-in.')); |
There was a problem hiding this comment.
Notify helper turns when restarting Codex
If Refresh is selected while a Codex helper turn is in flight, agent.close() kills that turn without emitting the agent's disconnected event, and calling Workspace.disconnected() directly does not notify its listeners. In helperTurn(), helpers whose turn/start request already completed are waiting for either a notification or that event, so they remain pending until their timeout instead of failing promptly. This can leave commit-message generation stuck for two minutes or import repair stuck for ten minutes after Refresh has successfully reconnected. Notify the helper listeners as part of this restart so their promises and UI state settle immediately.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in eeec67e: the restart now emits the agent's own disconnected event, so waiting helper turns reject immediately (and tasks are interrupted as before). Covered in the sign-in test.
Codex's close() does not emit 'disconnected', so a commit message or import repair waiting on a helper turn hung until its timeout after Refresh. Announce the restart through the agent's own event, which also interrupts the tasks as before.
|
@codex review |
|
Codex Review: Didn't find any major issues. Nice work! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Problem
Papilot's Codex app-server reads its sign-in once, at startup. When another Codex on the host (CLI or desktop app) signs in again or rotates the shared refresh token, Papilot's in-memory token is revoked and every turn fails with:
Papilot still showed Codex as Ready (its
account/readreads the cached account), and Reconnect leaves a running Codex alone, so the only way out was restarting Papilot.Change
turn/completed, a non-retryingerror, or a failedthread/start/turn/start) whose error isunauthorized, an HTTP 401, or says "could not be refreshed" / "sign in again" marks Codex signed out. Codex 0.164 reports the revoked-token case ascodexErrorInfo: "other", so the message is matched too. Anaccount/readrefused with 401 at connect (seen with a revoked token:workspace routing discovery unauthorized (401)) now also shows Codex signed out instead of unavailable. Signed out holds queued tasks./api/reconnect, which now restarts a running Codex that is signed out, so it loads the host's currentauth.json. Turns in progress are marked interrupted (resumable), as on a disconnect. It never restarts on its own:connect()runs only at startup and from this button.papilot codex loginthere first.Testing
tests/workspace.test.ts: two new tests (revoked-token turn → signed out → Refresh restarts and signs back in, without restarting a signed-in Codex; unrelated failures keep the sign-in; a 401 account read shows signed out). Both fail without the server change. Whole file: 190/190.scripts/check-task-model.mjs: asserts Refresh (and no Reconnect) for a signed-out agent and the "Codex sign-in expired" notice.npm run typecheck,npm run build,npm run i18n -- check,npm run i18n:check-app. New strings are translated for all locales (zh-CN curated, the rest via the i18n pipeline); the pipeline also re-sorted the error dictionaries' keys.