* provider, codex: a ChatGPT account at 100% with credits left keeps Codex signed in — magpie becomes its provider only while the Codex app itself holds the account magpie made itself Codex's provider (model_provider = "magpie", #540) whenever any of the active ChatGPT account's windows read 100%. A Pro account at 100% on its week with credits left keeps working in the Codex app: the backend answers every turn (about 490 200s in one day's usage log at 100%), yet magpie moved Codex off it, and as a provider without requires_openai_auth the app lost its ChatGPT state ("Sign in to ChatGPT to start a durable thread", no remote control). CodexUsedUp now reads codexHeld, which mirrors the Codex app's own composer block (ChatGPT.app webview bundle, hP and its helpers): the backend says the account isn't allowed (rate_limit.allowed = false), it has no credits (has_credits / unlimited), and it isn't a workspace still within its overage (overage_limit_reached = false, no spend cap, an ordinary rate_limit_reached). Window percent never decides it. Where the app goes by what /wham/usage can't show (a statsig gate, the gpt-reserve fallback, Team's reserve experiment that ignores overage), it counts as held: a wrong move costs ChatGPT extras, a missed one every turn. A reading kept through a failed read (keepLast) stops saying held once its used-up window has started again, as its usage does. NextLogin's account rotation and its thresholds are unchanged. A user stuck on the old move (codex.out = 1) is put back beside the sign-in by the next Sync, as #540 already does once the account has room. Verification: - TestCodexUsedUpIsWhatTheAppHolds (real /wham/usage replies, ids replaced) fails on the old trigger: 7 subcases, "CodexUsedUp = true, want false". Each workspace guard (spend cap, reason, overage, plan list, Team reserve) was mutated and fails its own subcase. - TestCodexHeldKeptOnlyTillTheReset fails without the quota_last change. - The real Pro account (7-day window 100%, ~28k credits, allowed false): CodexUsedUp true before, false after. - go vet ./...; go build (darwin), GOOS=linux -tags nogui, GOOS=windows; go test -tags nogui ./... under a sandbox HOME; -race -count=20 on the new and related Codex tests. - Not run: a live Codex app turn on an account truly held (none at hand), Windows/WSL runtime, GUI suites (no GUI change). * provider: a Codex account at a spend cap or past its overage is held, credits or not; limit_reached counts as not allowed codexHeld let credits through before the spend cap, so a Team seat at its cap (spend_control.reached, allowed false) with has_credits read not held and magpie left Codex on an account the backend refuses. The app's readiness check (26.930.61225, app-primary) returns limited on spend_control.reached or credits.overage_limit_reached before it looks at credits, and treats rate_limit.limit_reached like allowed false; held now reads the same. A spend cap with the rate limit still allowed (a workspace's included usage) stays not held.
77 lines
1.9 KiB
JSON
77 lines
1.9 KiB
JSON
{
|
|
"user_id": "user-1",
|
|
"account_id": "acct-1",
|
|
"email": "me@example.com",
|
|
"plan_type": "team",
|
|
"rate_limit": {
|
|
"allowed": false,
|
|
"limit_reached": true,
|
|
"primary_window": {
|
|
"used_percent": 100,
|
|
"limit_window_seconds": 18000,
|
|
"reset_after_seconds": 9452,
|
|
"reset_at": 1791264745
|
|
},
|
|
"secondary_window": {
|
|
"used_percent": 63,
|
|
"limit_window_seconds": 604800,
|
|
"reset_after_seconds": 360157,
|
|
"reset_at": 1791615450
|
|
}
|
|
},
|
|
"code_review_rate_limit": null,
|
|
"additional_rate_limits": null,
|
|
"model_usage": {
|
|
"gpt-6-astra": {
|
|
"available": false,
|
|
"available_at": "2026-10-06T05:32:26.047406Z",
|
|
"credits_would_enable": false
|
|
}
|
|
},
|
|
"credits": {
|
|
"has_credits": true,
|
|
"unlimited": false,
|
|
"overage_limit_reached": false,
|
|
"balance": null,
|
|
"approx_local_messages": null,
|
|
"approx_cloud_messages": null
|
|
},
|
|
"spend_control": {
|
|
"reached": true,
|
|
"individual_limit": {
|
|
"source": "account_user_spend_controls",
|
|
"unit": "credit",
|
|
"limit": "0",
|
|
"used": "0.0",
|
|
"remaining": "0.0",
|
|
"used_percent": 100,
|
|
"remaining_percent": 0,
|
|
"reset_after_seconds": 2235907,
|
|
"reset_at": 1793491201
|
|
}
|
|
},
|
|
"rate_limit_reached_type": {
|
|
"type": "workspace_owner_usage_limit_reached",
|
|
"details": null
|
|
},
|
|
"rate_limit_upsell": {
|
|
"banner_type": "workspace_owner_usage_limit_reached",
|
|
"title": "Usage limit reached",
|
|
"description": "You've reached your usage limit. Your limit will reset {time}, or you can increase your limits",
|
|
"ctas": [
|
|
{
|
|
"action": "increase_spend_cap",
|
|
"label": "Increase Usage Limit"
|
|
}
|
|
],
|
|
"reset_at": 1793491201,
|
|
"referral": null,
|
|
"request_url": null
|
|
},
|
|
"promo": null,
|
|
"rate_limit_reset_credits": {
|
|
"available_count": 2,
|
|
"applicable_available_count": 2
|
|
}
|
|
}
|