Repository navigation
Codex account-status parity with Hermes Accounts tooltip: quota, sign-in health and actionable checks #40
Description
Summary
Codex account status in Resetwatch can disagree with Hermes Desktop's model/account tooltip. Some differences are expected because the views refresh independently, but Resetwatch currently drops invalid/expired logins before producing a status row, and cached quota percentages can be mistaken for current availability after a window reset.
Please distinguish live quota, cached/expired quota observations, and sign-in health, so users can tell which account has a quota cap versus which credential needs reauthentication.
Environment and scope
- Linux, Hermes Desktop with local gateway, default profile, multiple Codex OAuth pool entries.
- Updated installation: catalog Resetwatch v0.2.21, commit
d460334db633fbaa91f0f8b96679e8463bad2a04. Verified both the installed catalog package and the Desktop-loadedplugin.jsreport 0.2.21. - Post-update source inspection confirms that the installed 0.2.21 probe still skips
dead/expiring Codex entries and returns cached snapshot lists without their originalfetched_atenvelope. Account-ID deduplication is present. After updating, user-provided screenshots confirm that the quota windows agree between the two views, but the Desktop tooltip includes a sign-in-required account that Resetwatch omits. - Also inspected current main
f0dc5704e20945b6b53618920462c56142dc6d29before filing: account-ID deduplication and the newer popup's cache/staleness labels already exist. This report does not request those features as if they were missing upstream. - No credential refresh, auth-store mutation, or inference request is required for the usage probe.
Confirmed behavior in v0.2.21 and requested account-status parity
In v0.2.21, screenshots of Resetwatch and Hermes Desktop’s Accounts tooltip show matching quota values when compared by the same window. The tooltip summarizes the tightest relevant window; Resetwatch shows Session and Weekly separately. The remaining functional gap is the account roster and actionable authentication status: the tooltip shows Sign in again for an account absent from Resetwatch.
A targeted hermes auth refresh openai-codex <entry-id> for that account also failed with the saved session is no longer valid; sign in again. This was an explicit diagnostic action outside Resetwatch, not a proposal to make the background usage probe refresh tokens. Earlier direct usage requests returned HTTP 401 token_revoked. The revocation cause is not established.
Desired UX: the same useful answers as the Accounts tooltip and a targeted diagnostic check
For every configured account, show a compact summary alongside its detailed windows:
- Available: latest usage API succeeded and reports allowance available; label this as quota/auth verification, not proof of successful inference for every model.
- Usage limit reached — resets at …: authenticated but a relevant quota window blocks usage; no re-login recommendation.
- Sign in again: known revoked/invalid grant, or failed renewal confirming the saved session cannot be renewed.
- Expired token / not checked: access token cannot be queried read-only; do not assume its refresh grant is invalid.
- Unknown / check failed: network/server error, stale data, or unsupported response; no fabricated quota or false sign-in instruction.
Also show the account label, a privacy-preserving identity (masked email if available), active-account marker where Hermes exposes it, last actual vendor check time, the tightest relevant window summary, and expandable Session/Weekly/additional windows. Keep account counts consistent with identity-based deduplication. If one account has valid and revoked credential entries, show the account as available via the healthy entry and expose the bad duplicate separately if helpful.
Provide a Check status / Refresh usage action using the read-only vendor usage endpoint and existing backoff. For an authentication problem, show actionable guidance or a supported Hermes sign-in action. If an explicit user-triggered Renew sign-in action is feasible, delegate it to Hermes’s supported credential manager for the exact entry rather than directly rotating tokens in the probe; distinguish successful renewal from continued quota exhaustion. Never renew tokens on a Resetwatch polling timer. A permanently invalid grant requires interactive sign-in; do not promise automatic reauthentication or bypass login/2FA.
Prefer a supported shared Hermes account-status source when available, so Resetwatch and the Accounts tooltip agree on identities and auth status. Preserve read-only probing rather than importing private helpers with auth-store write side effects.
Current code findings
In main probe.py::_codex_pool_accounts:
last_status == "dead"is skipped;- missing tokens and tokens considered expiring are skipped;
- only the remaining entries become fetchers/cards.
Consequently a known configured account can disappear instead of showing a sign-in/unknown status. Expiring access tokens must not automatically be described as revoked grants: Resetwatch deliberately does not refresh OAuth, and Hermes may still be able to renew them.
Current main correctly deduplicates account IDs. Please retain that improvement, but ensure mixed healthy/revoked entries for the same account yield one usable account row rather than a misleading account-wide sign-in failure.
The probe cache stores a fetched_at timestamp, while _read_probe_result_cache() returns the snapshot list. The UI's fetchLiveCards() reports a new checkedAt: Date.now(). A UI refresh that reuses the probe cache is therefore not necessarily a new vendor observation. The popup now has stale/offline labels; preserving the actual observation time through this boundary would make those labels more reliable.
Reproduction scenarios
A. Invalid credential silently disappears
- Have a configured Codex pool account whose only entry is
dead(or whose access token has expired). - Open/refresh Resetwatch and compare its account roster with Hermes Desktop's account tooltip.
- Actual: the entry is skipped before fetching, so no actionable status card is emitted.
- Expected: show a nonnumeric status row: reauthentication required for a known dead grant; expired/not checked for an access token that cannot be queried read-only.
B. Cached exhausted window crosses its reset
- Cache a session window at 100% used with reset time shortly in the future.
- Keep/show the cached snapshot after that timestamp, before a successful new vendor fetch.
- Expected: retain the historical percentage only with an explicit observation time and reset elapsed / awaiting refresh state; do not imply either continued exhaustion or guaranteed replenishment until checked.
C. Mixed credential health for one account
- Have two entries for the same OpenAI account, one valid and one revoked.
- Expected: one account row, using the valid entry's quota; optionally disclose a stale credential separately. A bad duplicate should not make the entire account unavailable.
Requested behavior / acceptance criteria
- Keep configured Codex accounts visible when quota cannot be fetched, with explicit unknown/expired/sign-in-required status and no invented percentage.
- Separate account availability (
allowed,limit_reached, relevant windows) from local credential cooldown and authentication state. - Display both session and weekly windows (and any named additional limits already supported); make clear that one exhausted relevant window can block usage despite remaining weekly allowance.
- Carry the actual vendor observation timestamp through cached probe results and UI caching; distinguish it from the time the UI last requested data.
- Mark an exhausted cached window whose reset has elapsed as pending recheck, not a definitive current limit and not automatically refilled.
- Preserve account-ID deduplication and read-only credential handling. Prefer a usable credential when the same account has mixed credential health.
- Preserve current polling/backoff limits; no need for aggressive polling or token refresh in the plugin.
Suggested regression tests: post-update tooltip/Resetwatch account-roster parity; available versus quota-limited versus revoked/expired/unknown status; mixed-credential active-account identity; targeted user-triggered check and renewal outcomes; dead-only account; expiring-token-only account; healthy+revoked duplicate; cached exhausted window crossing reset; UI refresh served from probe cache preserving observation time; session exhausted with weekly allowance remaining.
No email addresses, account IDs, tokens, or full auth files are included.
Suggested implementation plan (proposal, not a tested patch)
Implement the read-only status/accuracy changes first; account selection and interactive renewal can be a separate follow-up.
- Separate account inventory from fetch eligibility in
probe.py. Retain configured rows even whendead, missing an access token, or expiring. Group by provider + normalized route host + decoded OpenAI account identity; use a credential-scoped opaque fallback when identity is unavailable. Keep secrets internal. Do not deduplicate by label or email. An identity decoded from a JWT is grouping metadata, not proof that the credential is accepted by OpenAI. - Evaluate credentials within each account group. Prefer a non-dead, non-expiring candidate. If it receives an auth failure, try another eligible candidate for that same account within the existing time budget. Emit one account row: one successful candidate wins over failed duplicates; only a group with no usable candidate gets an auth/unknown row. No token refresh and no auth-file writes in background polling.
- Add optional, backward-compatible status metadata to snapshots. Suggested fields (names are illustrative):
auth_state,quota_state,observed_at,last_attempt_at,source, and a sanitizedreason_code. Use empty windows/null percentages for status-only cards, not zero. Preserve the last successful observation separately from the latest failed attempt. Extend the snapshot keep/filter logic so status-only configured-account cards survive through collection, serialization, UI normalization and both renderers. - Classify results without conflating failures. Known local dead/revoked grant or vendor
token_revoked-> sign-in required for that credential. Expiring access token -> expired/not checked, not necessarily a dead refresh grant. Generic 401 -> authentication problem, renewal may help; do not assert permanent revocation without evidence. Timeout/5xx/unrecognized body -> unknown/check failed. Usage HTTP 200 means auth/usage lookup succeeded, not model inference tested; useallowed,limit_reachedand relevant quota windows to describe quota. Preserve model-specific scope and paid-credit semantics rather than treating every extra model limit as an account-wide block. - Propagate observation age through both caches. Stamp successful per-account fetches at the vendor response, preserve that timestamp on probe cache reads, and carry it through
fetchLiveCards()and persistent UI cards. KeepcheckedAt/last-attempt time separate. Older cache entries lacking observation metadata should show age unknown or be re-fetched, not receive a new vendor timestamp. Reconcile cached roster with current configured accounts so removed/replaced credentials do not linger or hide newly configured accounts; a non-secret inventory fingerprint can invalidate roster cache while honoring the manual-refresh floor. - Render status consistently in the full page and popup. Reuse one derived-status helper for badges, compact tightest-window summary and detail windows. Retain expired/invalid accounts with actionable text. After an exhausted cached window passes its reset timestamp, display reset elapsed / awaiting check; do not fabricate replenishment or retain a definitive exhausted-state claim. Preserve existing stale/offline indicators and locale support.
- Actions and integration. Reuse the existing manual fresh-usage path and polling/backoff limits for Check status. Initially provide safe sign-in guidance. Add active-account identity or explicit Renew sign-in only through a supported Hermes API, with exact credential targeting and explicit user consent; provider-level sign-in can replace a singleton rather than repair the intended pooled entry. Do not invent an RPC or shell-login automation, and do not modify grants from the read-only probe. Share the supported account-status source with the Accounts tooltip if practical.
- Targeted tests and packaging. Use mocked usage responses and a frozen clock: dead-only and expired-only groups remain visible; valid+revoked duplicate yields one available row; all-invalid duplicates yield one sign-in row; same identity on different hosts is not merged; 200 available/limited and 401/5xx are distinguished; cached exhausted window crosses reset without invented quota; cached reads preserve original observation time; roster changes invalidate old cards; full-page/popup status-only rendering agrees. Run the relevant probe/UI tests, then regenerate the catalog artifacts using the repository's normal build process so standalone and catalog distributions stay aligned.
This is deliberately an incremental plan: it does not require automatic re-login, additional inference requests, aggressive polling, or private Hermes helpers that mutate credential stores.
Activity
[-]Codex status accuracy: show invalid logins and preserve quota observation time across caches[/-][+]Codex account-status parity with Hermes Accounts tooltip: quota, sign-in health and actionable checks[/+]Adolanium commentedon Oct 13, 2026
Thanks for the detailed report. In #41 a dead or expired Codex login now shows as a card that says why, instead of disappearing. I'll handle keeping the real check time through the cache separately.
fix(codex): keep dead and expired logins visible as status cards
Adolanium commentedon Oct 17, 2026
#43 carries the real check time through the cache. Each probe snapshot keeps
observed_atfrom the vendor's answer, and the page and popup show that time and base the stale marker on it, not on when the UI asked. It's in catalog-v0.2.23.Still open from this report: grouping mixed-health credentials per account, and the user-triggered check and renew actions. I'm keeping this issue open for those.
fix(probe): fall back to the base credential pool per provider and ke…