Commit Graph
77 Commits
Author SHA1 Message Date
Wesley Liddick bc4bd118d8 Merge pull request #4547 from heathermhuang/codex/fix-model-scoped-temp-cooldown-4527
fix(routing): isolate temporary cooldowns by model
2026-07-18 20:41:12 +08:00
Heatherm Huang ef2a22be70 fix(routing): isolate temporary cooldowns by model 2026-07-18 15:35:30 +08:00
haruka 1f7b7b9132 fix: honor temp unschedulable rules in pool mode 2026-07-17 18:12:33 +08:00
Bestony 5aeb03018c fix(scheduler): cool down Codex plan-gated models per account
OpenAI OAuth (ChatGPT) accounts deterministically reject plan-gated
models with 400 "The 'X' model is not supported when using Codex with
a ChatGPT account". Account selection had no capability filtering for
this, so the scheduler kept picking the same account for the same model
forever; every attempt burned an upstream call and surfaced to clients
as a retryable 502, sustaining client retry storms.

Treat this 400 like upstream model-not-found: mark the (account, model)
pair via SetModelRateLimit (30min cooldown) so
IsSchedulableForModelWithContext skips the account for that model
during selection, and return true so the in-flight request fails over
to another account through the existing UpstreamFailoverError path.
2026-07-13 16:29:18 +08:00
zhengshanandClaude Opus 4.8 3866da508f fix(ratelimit): Anthropic 无 reset 头的 429 也进入兜底冷却
此前 Anthropic 429 若响应头无窗口重置时间(如 Extra usage required),
被视为"非真实限流"直接跳过标记。后果:账号永不冷却,调度器让每个
请求反复撞同一批持续 429 的账号——failover 预算烧尽后客户端稳定
收到 429(生产实测:一次请求 1.3s 内连换 4 账号全 429,而 DB 中
这些账号的限流状态毫无更新)。

改为与其他平台一致走 apply429FallbackRateLimit 秒级兜底回避:
- 默认 5s,管理端 RateLimit429CooldownSettings 可调 1~7200s
- 管理端关闭该设置即恢复旧行为(不标记)
- 日志 reason 标记为 anthropic_no_reset_time 便于运营区分

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 23:19:23 +08:00
Wesley Liddick 759332aa92 Merge pull request #3692 from linshuboy/codex/openai-advanced-scheduler-controls-squashed
[codex] add OpenAI advanced scheduler controls
2026-07-06 10:59:23 +08:00
linshuboy f26ca5661e feat: add OpenAI advanced scheduler controls
Related: #1089, #408, #123
2026-07-05 17:24:38 +08:00
wucm667 b23475ac0c fix(antigravity): refresh server-invalidated tokens 2026-07-05 08:34:34 +08:00
shaw b3f796972e feat(anthropic): treat 7d_oi (Fable) window 429 as model-level rate limit
Anthropic OAuth/setup-token accounts now expose a Fable-only 7d window in
429 responses (anthropic-ratelimit-unified-7d_oi-*). Previously, a 429
triggered solely by this window fell through to the legacy path and marked
the whole account rate limited (picking the sooner 5h/7d reset) even though
5h/7d were still "allowed".

Backend:
- HandleUpstreamError: when only 7d_oi is rejected/exceeded, set a
  model-level rate limit (scope "claude-fable-5") and skip account-level
  SetRateLimited / session-window rewrite / temp-unsched entirely.
  Real 5h/7d exhaustion keeps the exact legacy account-level behavior.
- modelRateLimitKeysForRequest: append the Fable family scope for any
  mapped Anthropic model containing "fable", so claude-fable-5[1m] and
  other variants are excluded from scheduling together.
- Passive sampling: collect 7d_oi utilization/reset into Extra
  (passive_usage_7d_oi_*), including from the 429 response itself so the
  usage bar doesn't freeze at a stale value during the limit window;
  cleared on 5h window reset like the existing 7d keys.
- Usage API: new seven_day_fable window in UsageInfo. GetPassiveUsage
  builds it from passive samples; active queries parse the upstream
  seven_day_overage_included field (claim name from the
  representative-claim header) and fall back to passive data when the
  upstream usage API doesn't return it, so the active-query button keeps
  the bar populated. seven_day_sonnet handling is preserved unchanged.

Frontend:
- /admin/accounts usage cell renders a "7d F" (amber) progress bar next to
  the existing 5h / 7d / 7d S bars for Anthropic OAuth/setup-token accounts.
2026-07-04 08:28:30 +08:00
wucm667 d0a1443a41 fix(antigravity): allow oauth 401 auto recovery 2026-07-03 15:21:53 +08:00
DaydreamCodingandClaude Sonnet 5 bdf7ead157 feat(spark-shadow): OpenAI Spark 链接型影子账号
背景:gpt-5.3-codex-spark 使用独立于 codex 全局(5h/7d)的配额窗口(数据源是
/wham/usage 响应体的 codex_bengalfox,而非 codex 全局用的 x-codex-* 响应头),且
只能挂在已完成 OAuth 授权的 OpenAI 账号下复用其登录态,不能作为独立账号单独接入。
为此新增“链接型影子账号”(spark shadow account):影子账号本身不持有任何凭据,
通过 parent_account_id 指向母账号,凭据/token/代理透传自母账号并共享母账号的刷新
周期,仅在配额维度(quota_dimension=spark)和用量窗口上与母账号完全独立调度、互不
连坐。

实现:
- 数据模型:migration 154(+154a)给 accounts 表加 parent_account_id /
  quota_dimension 列 + 4 条约束(维度合法 / parent⟺非 global 维度一致 / 禁自指 /
  FK)+ 2 个 CONCURRENTLY 索引(母账号索引 + 每母账号至多一个影子的唯一索引)。
- 创建:POST /api/v1/admin/accounts/:id/shadow(CreateShadow)—— 一母一影(唯一
  索引兜底并发竞态),继承母账号 proxy/分组/并发/优先级(显式传参可覆盖),默认
  model_mapping 恒等映射到 spark(拒绝非 spark 模型),母账号必须是真实的 OpenAI
  OAuth 账号(非影子)。
- 凭据透传:resolveCredentialAccount 把影子解析回母账号,GetAccessToken / 请求头
  / WS 三条路径统一走此函数;影子自身 Credentials 恒为空(仅允许写 model_mapping),
  凭据写入的汇聚点 persistAccountCredentials 对影子早返 no-op,防止误写。
- 调度:parentHealthyForShadow 只看母账号是否仍是 OpenAI OAuth + 凭据/传输是否
  可用(active、token 未过期、未处于 401/刷新失败/传输故障导致的临时不可调度冷却),
  刻意不看母账号的 global 限流窗口——两条 429 道互不连坐。
- 用量:影子的 codex_5h/7d 走 OpenAIQuotaService.QueryUsage(/wham/usage 的
  codex_bengalfox),与母账号走的 WSv2 探测(/responses 头)完全独立的数据源、
  刷新节流与 staleness 判定。
- 备份:ExportData 显式排除影子账号(影子不持凭据,通用凭据型导入强制
  credentials 非空、无法表达父子链接),按 skipped_shadows 计数提示前端。
- 前端:账号操作菜单新增“创建 Spark 影子”入口,影子行展示回填的母账号信息
  (邮箱 / plan / 隐私模式 / 订阅到期 / chatgpt_account_id),批量操作自动跳过
  影子账号。

说明:migrations 目录用完整文件名(而非纯数字前缀)标识迁移,故本次新增的
154_account_spark_shadow.sql / 154a_..._notx.sql 与已有的
154_add_ops_system_logs_api_key_id.sql 按序号共存,与目录里 145/151 已有的
先例一致。

测试:新增约 20 个测试文件,覆盖 handler(CreateShadow 校验 / 母账号信息回填)、
repository(影子 round-trip / 一母一影唯一索引 / 迁移 schema)、service(凭据
透传三路径 / 调度母健康门 / 用量窗口来源与刷新节流 / CRS 母账号不变量 / 各类
早返与 fail-closed 场景)及前端组件(账号列表 / 操作菜单 / 用量重置)。

验证(镜像 CI;golangci-lint 首次全量分析耗时过长被跳过,其余全部实测):
- gofmt -l:干净
- go build ./... / go vet ./...:通过
- go test ./... -count=1:全绿(全部包 ok,含 internal/service、
  internal/repository、migrations)
- go test -tags integration ./internal/repository/... ./internal/service/...
  (真实 Postgres,testcontainers):全绿,含迁移幂等性
  (TestMigrationsRunner_IsIdempotent_AndSchemaIsUpToDate)与影子相关全部用例
- pnpm lint:check / pnpm typecheck / pnpm build(真实 vite 构建)/
  pnpm vitest run:全绿(124 文件 760 用例)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-01 12:21:45 +08:00
wangziwenunique f6e0ebc6bf fix: preserve Anthropic window cooldowns 2026-06-12 13:35:03 +08:00
wucm667 36721d35a8 feat(openai): cool down image rate limits by capability 2026-06-05 18:12:33 +08:00
fofoj 6aec505016 fix(oauth): don't overwrite credentials JSONB in 401 handler
The 401 handler in RateLimitService.HandleUpstreamError set
account.Credentials["expires_at"] = time.Now() and then persisted the
full credentials map via persistAccountCredentials, which routes through
accountRepository.UpdateCredentials -> ent SetCredentials and replaces
the entire JSONB column.

The account passed to the handler is the request-start snapshot taken
by the gateway at SelectAccount time. When another worker has just
rotated refresh_token via oauth_refresh_api.RefreshIfNeeded, the
snapshot still holds the old refresh_token; writing the full snapshot
back rolls refresh_token in the DB back to the stale value.

The next refresh cycle then calls the upstream with the stale token,
receives invalid_grant, and tryRecoverFromRefreshRace re-reads the DB
only to find currentRT == usedRT (because the 401 handler just poisoned
the DB), returns false, and the account is incorrectly disabled.

Drop the credentials write. InvalidateToken + SetTempUnschedulable is
sufficient: the account is held out of scheduling during the cooldown,
and after the cooldown the next request goes through token_provider's
NeedsRefresh check, which routes through the locked, DB-re-reading
RefreshIfNeeded path.

The "force background refresh by setting expires_at = now" semantic is
intentionally dropped. token_refresh_service will naturally pick the
account up when the real expires_at enters the refresh window, and if
the real expires_at has already passed by the time the account becomes
schedulable again, token_provider's NeedsRefresh returns true and
RefreshIfNeeded fires synchronously on the next request.
2026-05-28 20:05:38 +08:00
wucm667 a31b507484 fix(scheduler): 模型404仅冷却账号模型组合 2026-05-26 20:29:48 +08:00
shaw 1e406fed52 fix: optimize OpenAI account cooldown scheduling 2026-05-23 10:18:43 +08:00
name bec1e2b697 fix(openai): 永久禁用缺失 refresh_token 的 OAuth 账号
token_provider 在 expires_at 已过且 refresh_token 缺失时,仅返回 error,未做任何降级。
HandleUpstreamError 的 OAuth 401 分支也只走 10min 冷却,不区分账号是否具备刷新能力。
两条路径相加导致缺 refresh_token 的账号被反复选中、每次都在 token 阶段失败,对用户呈现持续 502。

token_provider.GetAccessToken: 命中"过期且无 refresh_token"时调用 SetError 永久禁用并清缓存,
依赖 background context 避免请求 ctx 提前结束影响落库。
ratelimit_service 401 OAuth 分支:refresh_token 为空时直接 SetError,不再写 expires_at、
不再 SetTempUnschedulable,缓存失效保留。RT 账号路径完全不动。

新增/调整测试覆盖两条路径,旧测试为 RT 路径补足 refresh_token 字段以保留原意图。
2026-05-16 19:40:23 +08:00
XiaoYu994 c3a1471775 fix: sync OpenAI plan type from usage limit errors 2026-05-11 16:22:40 +08:00
shaw 11ae6f2105 fix(rate-limit): remove 429 cooldown config option 2026-05-05 20:11:12 +08:00
gaoren002 4b904c887c fix(rate-limit): make 429 fallback cooldown configurable 2026-04-30 03:01:39 +00:00
KnowSky404 f68909a68b fix: reconcile openai admin test rate-limit state 2026-04-24 11:32:41 +08:00
wx-11 11cf23da7d 修改403逻辑: 先临时冷却,再根据连续次数决定是否判坏号 2026-04-23 12:58:13 +08:00
shaw 5d586a9f3a fix: 上游返回 KYC 身份验证要求时停止账号调度 2026-04-17 10:17:50 +08:00
bot cb016ad861 fix: handle Anthropic credit balance exhausted (400) as account error
When an Anthropic API key's credit balance is depleted, the upstream
returns HTTP 400 with message containing "credit balance". Previously,
the 400 handler only checked for "organization has been disabled",
so credit-exhausted accounts kept being scheduled — every request
returned the same error.

Treat this case identically to 402 (Payment Required): call
handleAuthError → SetError to stop scheduling the account until
an admin manually recovers it after topping up credits.

Closes #1586
2026-04-12 13:30:15 +08:00
QTom 1f6a73f0db fix(openai): treat 401 {"detail":"Unauthorized"} as permanent auth failure
- ratelimit_service: detect non-standard OpenAI 401 format and permanently disable account
- account_test_service: mark account error on 401 during connection test

Made-with: Cursor
2026-04-02 20:44:05 +08:00
QTomandClaude Opus 4.6 5875571215 fix(ratelimit): OpenAI 401 token_invalidated/token_revoked 及 402 deactivated_workspace 标记账号异常
- 401 token_invalidated / token_revoked: OAuth token 被永久作废,跳过临时不可调度逻辑,直接 SetError
- 402 deactivated_workspace: 解析 detail.code 字段,标记工作区已停用

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 19:46:17 +08:00
Wang Lvyuan ad7c10727a fix(account): preserve runtime state during credentials-only updates 2026-03-23 03:49:28 +08:00
shaw 525cdb8830 feat: Anthropic 账号被动用量采样,页面默认展示被动数据
从上游 /v1/messages 响应头被动采集 5h/7d utilization 并存储到
Account.Extra,页面加载时直接读取本地数据而非调用外部 Usage API。
用户可点击"查询"按钮主动拉取最新数据,主动查询结果自动回写被动缓存。

后端:
- UpdateSessionWindow 合并采集 5h + 7d headers 为单次 DB 写入
- 新增 GetPassiveUsage 从 Extra 构建 UsageInfo (复用 estimateSetupTokenUsage)
- GetUsage 主动查询后 syncActiveToPassive 回写被动缓存
- passive_usage_ 前缀注册为 scheduler-neutral

前端:
- Anthropic 账号 mount/refresh 默认 source=passive
- 新增"被动采样"标签和"查询"按钮 (带 loading 动画)
2026-03-19 17:42:59 +08:00
shaw bf3d6c0e6e feat: add 529 overload cooldown toggle and duration settings in admin gateway page
Move 529 overload cooldown configuration from config file to admin
settings UI. Adds an enable/disable toggle and configurable cooldown
duration (1-120 min) under /admin/settings gateway tab, stored as
JSON in the settings table.

When disabled, 529 errors are logged but accounts are no longer
paused from scheduling. Falls back to config file value when DB
is unreachable or settingService is nil.
2026-03-18 16:22:19 +08:00
harukaandClaude Sonnet 4.6 869952d113 fix(review): address Copilot PR feedback
- Add compile-time interface assertion for sessionWindowMockRepo
- Fix flaky fallback test by capturing time.Now() before calling UpdateSessionWindow
- Replace stale hardcoded timestamps with dynamic future values
- Add millisecond detection and bounds validation for reset header timestamp
- Use pause/resume pattern for interval in UsageProgressBar to avoid idle timers on large lists
- Fix gofmt comment alignment

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 10:19:20 +08:00
ElysiaandClaude Opus 4.6 668e164793 fix(usage): use real reset header for session window instead of prediction
The 5h window reset time displayed for Setup Token accounts was inaccurate
because UpdateSessionWindow predicted the window end as "current hour + 5h"
instead of reading the actual `anthropic-ratelimit-unified-5h-reset` response
header. This caused the countdown to differ from the official Claude page.

Backend: parse the reset header (Unix timestamp) and use it as the real
window end, falling back to the hour-truncated prediction only when the
header is absent. Also correct stale predictions when a subsequent request
provides the real reset time.

Frontend: add a reactive 60s timer so the reset countdown in
UsageProgressBar ticks down in real-time instead of freezing at the
initial value.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 00:13:45 +08:00
erio 8a260defc2 refactor: replace sync.Map credits state with AICredits rate limit key
Replace process-memory sync.Map + per-model runtime state with a single
"AICredits" key in model_rate_limits, making credits exhaustion fully
isomorphic with model-level rate limiting.

Scheduler: rate-limited accounts with overages enabled + credits available
are now scheduled instead of excluded.

Forwarding: when model is rate-limited + credits available, inject credits
proactively without waiting for a 429 round trip.

Storage: credits exhaustion stored as model_rate_limits["AICredits"] with
5h duration, reusing SetModelRateLimit/isRateLimitActiveForKey.

Frontend: show credits_active (yellow ⚡) when model rate-limited but
credits available, credits_exhausted (red) when AICredits key active.

Tests: add unit tests for shouldMarkCreditsExhausted, injectEnabledCreditTypes,
clearCreditsExhausted, and update existing overages tests.
2026-03-16 04:58:58 +08:00
SilentFlower 17e4033340 feat: implement resolveCreditsOveragesModelKey function to stabilize model key resolution for credit overages 2026-03-16 04:58:12 +08:00
erio 45456fa24c fix: restore OAuth 401 temp-unschedulable for Gemini, update Antigravity tests
The 403 detection PR changed the 401 handler condition from
`account.Type == AccountTypeOAuth` to
`account.Type == AccountTypeOAuth && account.Platform == PlatformOpenAI`,
which accidentally excluded Gemini OAuth from the temp-unschedulable path.

Fix: use `!= PlatformAntigravity` instead, preserving Gemini behavior
while correctly excluding Antigravity (whose 401 is handled by
applyErrorPolicy's temp_unschedulable_rules).

Update tests to reflect Antigravity's new 401 semantics:
- HandleUpstreamError: Antigravity OAuth 401 now uses SetError
- CheckErrorPolicy: Antigravity 401 second hit stays TempUnscheduled
- DB fallback: split into Gemini (escalates) and Antigravity (stays temp)
2026-03-14 02:21:22 +08:00
erio 6344fa2a86 feat(antigravity): add 403 forbidden status detection, classification and display
Backend:
- Detect and classify 403 responses into three types:
  validation (account needs Google verification),
  violation (terms of service / banned),
  forbidden (generic 403)
- Extract verification/appeal URLs from 403 response body
  (structured JSON parsing with regex fallback)
- Add needs_verify, is_banned, needs_reauth, error_code fields
  to UsageInfo (omitempty for zero impact on other platforms)
- Handle 403 in request path: classify and permanently set account error
- Save validation_url in error_message for degraded path recovery
- Enrich usage with account error on both success and degraded paths
- Add singleflight dedup for usage requests with independent context
- Differentiate cache TTL: success/403 → 3min, errors → 1min
- Return degraded UsageInfo instead of HTTP 500 on quota fetch errors

Frontend:
- Display forbidden status badges with color coding (red for banned,
  amber for needs verification, gray for generic)
- Show clickable verification/appeal URL links
- Display needs_reauth and degraded error states in usage cell
- Add Antigravity tier label badge next to platform type

Tests:
- Comprehensive unit tests for classifyForbiddenType (7 cases)
- Unit tests for extractValidationURL (8 cases including unicode escapes)
- Integration test for FetchQuota forbidden path
2026-03-13 18:22:45 +08:00
Wesley Liddick 97aaa24733 Merge pull request #858 from james-6-23/fix/pool-mode-03bf3485
支持 API Key 上游池模式的同账号重试次数配置与自定义错误策略
2026-03-09 08:48:53 +08:00
Wesley Liddick faf6441633 Merge pull request #854 from james-6-23/main
feat(admin): 支持定时测试自动恢复并统一账号恢复入口
2026-03-09 08:48:36 +08:00
kyx236 e643fc382c feat: 支持 API Key 上游池模式同账号重试次数配置与自定义错误策略 2026-03-08 14:12:17 +08:00
kyx236 0c29468f90 feat(admin): 支持定时测试自动恢复并统一账号恢复入口
- 为定时测试计划增加 auto_recover 配置,补齐前后端类型、接口、仓储与数据库迁移
- 在定时测试成功后自动恢复账号 error、rate-limit 等可恢复运行时状态
- 新增 /admin/accounts/:id/recover-state 接口,合并原有重置状态与清限流操作
- 更新账号管理菜单与定时测试面板,补充自动恢复开关、说明提示和状态展示
- 补充账号恢复、限流清理与仓储同步相关测试
2026-03-08 06:59:53 +08:00
神乐 0debe0a80c fix: 修复 OpenAI WS 用量窗口刷新与限额纠偏 2026-03-07 20:02:58 +08:00
FizzlyCodeandClaude Opus 4.6 49d0301dde fix: Setup Token 账号使用真实 utilization 值替代状态估算
从响应头 anthropic-ratelimit-unified-5h-utilization 获取并存储真实
utilization 值,解决进度条始终显示 0% 的问题。窗口重置时清除旧值,
避免残留上个窗口的数据。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 21:04:44 +08:00
kyx236 3d79773ba2 Merge branch 'main' of https://github.com/james-6-23/sub2api 2026-03-04 20:25:39 +08:00
kyx236 6aa8cbbf20 feat: 二次 401 直接升级为错误状态,添加 DB 回退确保生效
账号首次 401 仅临时不可调度,给予 token 刷新窗口;若恢复后再次 401
说明凭证确实失效,直接升级为错误状态以避免反复无效调度。

- 缓存中 reason 为空时从 DB 回退读取,防止升级判断失效
- ClearError 同时清除临时不可调度状态,管理员恢复后重新给予一次机会
- 管理后台账号列表添加"临时不可调度"状态筛选
- 补充 DB 回退场景单元测试
2026-03-04 20:25:15 +08:00
shaw 72961c5858 fix: Anthropic 平台无限流重置时间的 429 不再误标记账号限流 2026-03-04 09:36:24 +08:00
zqq61 ec6bcfeb83 fix: OAuth 401 不再永久锁死账号,改用临时不可调度实现自动恢复
OAuth 账号收到 401 时,原逻辑同时设置 expires_at=now() 和 SetError(),
但刷新服务只查询 status=active 的账号,导致 error 状态的账号永远无法
被刷新服务拾取,expires_at=now() 实际上是死代码。

修复:
- OAuth 401 使用 SetTempUnschedulable 替代 SetError,保持 status=active
- 新增 oauth_401_cooldown_minutes 配置项(默认 10 分钟)
- 刷新成功后同步清除 DB 和 Redis 中的临时不可调度状态
- 不可重试错误检查(invalid_grant 等)从 Antigravity 推广到所有平台
- 可重试错误耗尽后不再标记 error,下个刷新周期继续重试

恢复流程:
OAuth 401 → temp_unschedulable + expires_at=now → 刷新服务拾取
  → 成功: 清除 temp_unschedulable → 自动恢复
  → invalid_grant: SetError → 永久禁用
  → 网络错误: 仅记日志 → 下周期重试
2026-03-02 22:54:38 +08:00
yangjianbo bb664d9bbf feat(sync): full code sync from release 2026-02-28 15:01:20 +08:00
yangjianbo 0b32f61062 fix(ratelimit): 清除限流时同步清理临时不可调度状态
- ClearRateLimit 增加清理 temp_unschedulable 与缓存\n- 新增 ClearRateLimit 相关单元测试覆盖成功与失败分支
2026-02-22 17:00:29 +08:00
shaw e681431454 fix: Anthropic 429 限流使用精确的窗口重置时间而非聚合最大值
当账号仅触发 5h 窗口限流时,旧逻辑从聚合头
anthropic-ratelimit-unified-reset 读取重置时间,该值为所有窗口的
最大值(即 7d 重置时间),导致账号被标记为不可调度约 6 天。

新增 calculateAnthropic429ResetTime 函数,解析 Anthropic 的
per-window 头(5h-utilization/reset、7d-utilization/reset、
surpassed-threshold),判断实际触发的窗口并使用对应的重置时间:
- 仅 5h 超标 → 使用 5h-reset(约 5 小时)
- 仅 7d 超标 → 使用 7d-reset
- 两者均超标 → 使用 7d-reset(较长冷却)
- per-window 头不存在 → 回退到聚合头(向后兼容)
2026-02-14 00:21:56 +08:00
erio 4a84ca9a02 fix: support clearing model-level rate limits from action menu and temp-unsched reset 2026-02-09 20:37:30 +08:00
erio 2f1182e8a9 feat: unified error policy for Antigravity + enable custom error codes for Gemini accounts 2026-02-09 06:54:42 +08:00