5519 Commits
Author SHA1 Message Date
Wesley Liddick ce328fb37a Merge pull request #6670 from ssharkkky/fix/payment-fulfillment-isolation
fix(payment): isolate fulfillment from redeem limits
2026-09-07 17:30:43 +08:00
ccemoji be4ab92b2b fix: cancel streaming upstream before closing body 2026-09-07 16:47:51 +08:00
alfadb 3efeba33a4 test(claude): 版本覆盖用例字面量随内置基线升至 2.1.259
这三个用例的字面量 2.1.251 写于内置基线为 2.1.220 的时期,本意是取一个
高于基线的补丁位验证"覆盖不得低于 pin"规则。基线升至 2.1.258 后,2.1.251
落在 pin 之下:IsSupportedCLIVersion 应判 false、resolveCLIVersion 应回落
基线,与用例原意相悖,CI 随之失败。

改为真正高于当前 pin 的最小补丁位 2.1.259,用例语义不变;实现未放宽,
constants_cli_version_test.go 中独立的 Fable 5.1 闸门下限 2.1.251 保持不变。
2026-09-07 16:34:35 +08:00
alfadb ce157b32ed fix(claude): 指纹自愈路径同样执行 claude-cli 版本下限
GetOrCreateFingerprint 的 heal 分支(畸形缓存自愈)此前在版本下限逻辑之外:
畸形缓存 + 合法但过旧的客户端 UA 会在首次读取时把过旧 UA 原样落库并返回,
下一次读取才被抬到下限——该窗口内账号仍以过旧版本撞上游客户端版本闸门
(PR #6481 review 指出的边界 case)。

修复:把 floorClaudeCLIUserAgentVersion 后置块从 healthy else 内移到
heal/healthy if-else 之后、TTL 续期检查之前,作为两条缓存命中路径的共同
后置条件;heal 分支 needWrite 本已为 true,单次落库语义不变。

测试:TestGetOrCreateFingerprintHealsPoisonedCacheUsingValidClientUA
期望改为首次返回/落库即 claude-cli/CLICurrentVersion(保留 ClientID 不变、
恰好一次落库断言);floor/UA 全部既有用例保持绿。
2026-09-07 16:34:35 +08:00
alfadb 3cb2381bdc fix(claude): 让伪装的 Claude Code 版本真正生效,修复 Fable 5.1 仍然 400
claude-fable-5-1 已登记进 DefaultModels,但 OAuth 账号实际调用仍然全部失败:

  400 claude_code_version_too_old
  "Claude Code 2.1.220 does not support this model;
   version 2.1.251 or newer is required."

两个独立的原因,缺一不可:

1. CLICurrentVersion 停留在 2.1.220,低于上游对该模型的客户端版本闸门。
   升到 2.1.258(当前官方 CLI 最新版,npm @anthropic-ai/claude-code)。

2. 只升常量对所有已有账号无效。上游请求实际发送的 User-Agent 来自账号级
   持久指纹(IdentityService,Redis fingerprint:<accountID>),不是
   DefaultHeaders;defaultFingerprint 只在首次创建指纹时使用。
   GetOrCreateFingerprint 的缓存命中路径只有两条改写分支:UA 畸形时自愈,
   或客户端送来更新版本时升级。指纹 7 天 TTL 但活跃账号每 24h 续期,实际
   近乎永不过期,系统内也没有重置入口——客户端版本比缓存旧时(例如缓存
   2.1.220、客户端 2.1.75),存量账号会永远对上游声称 2.1.220。

   生产实测:升级 CLICurrentVersion 并部署后仍报同样的 400,读 Redis 确认
   出问题账号的缓存值为 "claude-cli/2.1.220 (external, cli)"。

修复:新增 floorClaudeCLIUserAgentVersion,把 CLICurrentVersion 作为缓存
指纹里 claude-cli 版本的下限,缓存命中与首次创建两条路径共用。

  - 只对 claude-cli 产品生效,其它客户端一律不动
  - 只升不降:版本等于或高于下限(含客户端上报的更新版本)不做任何改动
  - 只替换版本号段,UA 其余部分原样保留,例如
    "(external, claude-desktop-3p, agent-sdk/0.3.100)" 不受影响
  - 与既有的"客户端更新版本"升级分支相互独立、二者取更新者
  - X-Stainless-* 维持既有 merge 语义,不被下限逻辑覆盖

cc_version 无需单独处理:billing attribution block 的版本已由
syncBillingHeaderVersion 跟随实际发送的 User-Agent 同步。

测试:identity_service_version_floor_test.go 覆盖 helper 的 8 种 UA 形态与
5 个 GetOrCreateFingerprint 场景,其中一个直接复现线上故障(缓存 2.1.220 +
客户端 2.1.75 → 写回 2.1.258,Stainless 字段保持不变)。
constants_cli_version_test.go 锁定常量与 DefaultHeaders 的 UA 同源,并断言
不低于 Fable 5.1 的闸门下限。回退实现后对应用例确认转红。

identity_service_user_agent_validation_test.go 中 3 个把版本钉死在旧字面量
的既有用例改为钉 CLICurrentVersion 或更高版本——它们断言的正是本次有意改变
的行为,各自的原始意图(哨兵拒绝、合法升级生效、健康缓存不重写)保持不变。
2026-09-07 16:34:35 +08:00
Wesley Liddick a7d2c782df Merge pull request #5717 from wucm667/feat/issue-2629-current-main
feat(simple-mode): allow basic account grouping
2026-09-07 15:43:22 +08:00
Robert Guo b939fa9d4a fix(openai): select GPT-6 Astra instructions correctly 2026-09-07 14:12:21 +08:00
qiu121 6aabbdf547 fix(openai): 记录流式失败的上游诊断信息
- 在 Responses 流已经输出语义内容后记录 response.failed 的上游事件,使运维可以查询原始错误、账号和上游 request ID,定位 OpenAI 临时处理失败而不丢失诊断上下文。
- 保留已提交语义内容后禁止切号重放的安全边界,避免客户端收到重复或混合响应;补充原生与透传路径的回归测试,确保该错误可观测且行为一致。

Refs: #5165, #6714
2026-09-07 11:27:08 +08:00
lyy0709 ad4b2f30f5 test(codex): assert alias workflow effort before dereferencing 2026-09-07 11:23:11 +08:00
milesians ff758f37d2 fix: preserve plaza 1h pricing and align interval fallbacks 2026-09-07 11:07:35 +08:00
zhuanzhi 2e31d8b702 fix(codex): preserve allowed_tools selection constraints
Keep allowed_tools policies intact for upstream validation and rewrite their
function references alongside reserved tool-name aliases.
2026-09-07 10:47:51 +08:00
刘佳林 f62ec2e4a0 feat(groups): 推理强度映射转发值支持拒绝
分组推理强度映射的转发值新增 deny。命中对应请求档位时直接拒绝请求,
返回 403 permission_error,而不是改写后转发。
2026-09-07 10:12:42 +08:00
milesians 415a0dc9d0 fix: display multiplier-only interval prices 2026-09-07 10:11:07 +08:00
wucm667 b59f3bf46c fix(simple-mode): enforce basic group boundaries 2026-09-07 09:20:48 +08:00
lyy0709 2db78bd3d6 fix(codex): preserve Astra Ultra workflow metadata 2026-09-07 01:12:55 +08:00
Zero Clover f88d62ad28 fix(openai): use live account models in connection tests 2026-09-07 00:23:49 +08:00
wucm667 8363d537e0 fix(gateway): fail over model-not-found responses 2026-09-06 20:38:17 +08:00
r266-tech b8ed245085 fix(openai-ws): recover later turns from quota exhaustion 2026-09-06 18:42:06 +08:00
aofee 96884dd1ae fix(openai-ws): isolate HTTP bridge connection state 2026-09-06 17:05:06 +08:00
a1gaoshanlaiyige 0b061c5fa1 fix(gateway): use JSON escaping for message cache promotion
Use encoding/json instead of Go string quoting when promoting message content to cached text blocks. Add regression coverage for control characters, Unicode, literal backslashes, text preservation, and idempotence.
2026-09-06 16:42:57 +08:00
qiu121 958a21ad6e fix(billing): 修复 DeepSeek 高峰账号成本少计
- 将两个用量记录入口的请求级定价时刻传递给账号成本统计,使跨峰谷请求的成本与客户计费采用同一时间基准。
- 默认目录成本改用统一计费入口并隔离分组与渠道售价,使 DeepSeek 高峰成本正确应用两倍价格,同时保留服务层级、长上下文和单次推理等级倍率。
- 增加 Flash 与 Pro 的峰谷边界、缓存用量、规则优先级及真实网关回归测试,确保账号成本按最终上游模型计算且客户扣款金额与次数保持一致。

验证:Go 1.27.0 下全量 unit 与 integration 测试命令通过;golangci-lint 2.13.0 报告 0 个问题。定向回归无失败或跳过,全量套件保留原有条件跳过项。
2026-09-06 15:15:21 +08:00
tunsuyandCursor 6c2d2ed043 fix(antigravity): drop mixed web_search when client tools present
Antigravity v1internal rejects googleSearch/codeExecution mixed with
functionDeclarations even with includeServerSideToolInvocations (#6464).
Codex enables web_search by default alongside shell tools, which made
Sub2API return opaque upstream_error. Prefer client function tools so
agent sessions keep working; keep web_search-only fallback intact.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-06 13:23:53 +08:00
suixincrazy 28167bbf85 fix(openai): honor model_routing for Codex manifests and scheduling
An OpenAI group that keeps a failover account for a shared public alias
produced a Codex model descriptor with no reasoning levels and text-only
input, which leaves the client's reasoning-effort slider with nothing to
select.

Two accounts explicitly mapping one alias to different upstream models
makes codexExplicitModelTargetsConflictForPlatform true. With no
upstream_model_metadata snapshot to intersect, groupCodexModelMetadata
short-circuits to {reasoningConflict, inputModalitiesConflict}, so
DefaultReasoningLevel is nil (the key vanishes under omitempty) and
SupportedReasoningLevels is an empty array. mergeMissingCodexModelFields
fills only missing-or-null fields, so an empty array is never repaired.

Failing closed is right when the alias target is genuinely ambiguous, but
a group that declares model_routing for the alias has already resolved
that ambiguity: the operator stated which accounts serve it and which is
primary. Treating that as a conflict rejects a deliberate failover setup.

Manifest side: when a routing rule names the alias, restrict capability
declaration to the routed accounts and skip the conflict override.
Capabilities then fall back to the same by-name derivation used for a
single account without a metadata snapshot. Groups with no rule, a
disabled rule, or a rule for another alias keep failing closed.

Scheduling side: model_routing was stored and read platform-agnostically
(admin_group.go, Group.GetRoutingAccountIDs) but four gates in
gateway_scheduling.go only consulted it for Anthropic, so an OpenAI
group's rules were accepted and displayed yet never honored. Without
this, the manifest would honor a rule that scheduling ignores. Both
gates now share one predicate, with the allowed platform set defined in
a single place.

Selection inside a routing list is unchanged: the list is a membership
set, and the winner is still decided by Priority, then load, then
LastUsedAt.

Tests: alias-failover manifest recovery plus the three negative cases,
the platform predicate table, and an OpenAI routed selection that must
beat a higher-priority unrouted account.
2026-09-06 10:12:20 +08:00
Chenning-Tao 985428d9d2 fix(openai): preserve named standalone Responses inputs 2026-09-06 09:33:53 +08:00
wucm667 4a4fd35e0c fix(simple-mode): guard non-empty group deletion 2026-09-06 08:11:23 +08:00
wucm667 f46038820b fix(simple-mode): enforce group capability allowlist 2026-09-06 08:11:23 +08:00
wucm667 05ad6b49a1 fix(simple-mode): enforce basic group capabilities 2026-09-06 08:10:41 +08:00
wucm667 a3675552bd feat(simple-mode): allow basic account grouping 2026-09-06 08:10:41 +08:00
cyh f49a393566 fix(openai): retain default models in mixed account catalogs 2026-09-06 02:02:49 +08:00
DAlaoli 8a46947865 Fix stale official Astra input modalities 2026-09-06 00:50:01 +08:00
LynricsyandWine Fox 7ae0312091 fix(claude): 🐛 补齐 thinking block binding beta
Co-authored-by: Wine Fox <fox@ling.plus>
2026-09-06 00:34:05 +08:00
WUJI-Labs 58e35a4f32 fix(antigravity): always set toolConfig for reasoning models without tools
Upstream returns 400 INVALID_ARGUMENT when toolConfig is absent. The guard
`if !isReasoning || len(tools) > 0` skipped it for reasoning models called
without tools, contradicting the intent stated in the comment above it.
Set toolConfig unconditionally and cover the three cases with a unit test.
2026-09-06 00:25:21 +08:00
TT 95023e7d49 fix(backup): serialize pg_dump/psql with the migrations advisory lock 2026-09-06 00:11:56 +08:00
TT 7a70de401c fix(payment): isolate fulfillment from redeem limits
Payment and admin fulfillment shared the public Redeem entry point, so
fulfillment retries were blocked by, and contributed to, the user's
public redeem failure counter, and lookup errors were silently treated as
"code does not exist" on a partial re-run.

- resolveRedeemAction now distinguishes a genuine not-found lookup from
  other lookup errors (which now fail the fulfillment instead of being
  swallowed and re-created).
- Add validatePaymentRedeemCode: before reusing an existing code during
  fulfillment, verify it belongs to the order (code id, balance type,
  amount, user, status), so a stale or foreign code can never be
  credited against the wrong order/user.
- Split Redeem into a policy-based core: public redemption enforces the
  failure counter; payment fulfillment (redeemForPaymentFulfillment) and
  admin fulfillment (RedeemForAdminFulfillment) bypass it, while all code
  validation and transactional updates remain identical. Admin
  fulfillment keeps the normal redeem-level affiliate rebate.
- The admin CreateAndRedeem endpoint now uses RedeemForAdminFulfillment.

Tests: updated resolveRedeemAction cases, new TestValidatePaymentRedeemCode,
code-used-by-another-user rejection, payment/admin rate-limit bypass
coverage, and public-path regression coverage.
2026-09-05 23:56:59 +08:00
TT 666a5c6f87 fix(redeem): use a fixed 10-minute failure window
The redeem failure counter used INCR followed by Expire(24h) on every
attempt, so each failed attempt refreshed the key TTL and the window
never actually closed for a user who kept retrying within 24h.

- Count attempts in a fixed 10-minute window via a small Lua script
  (INCR, then PEXPIRE only when the key is created or has no TTL), which
  is atomic and also repairs keys that lost their TTL.
- Move the counter to the redeem:ratelimit:v2: key space so pre-existing
  24h counters do not seed the new window.
- Rename the service threshold to redeemMaxFailedAttempts (30) to reflect
  that it bounds attempts per window, not per hour.

Public redeem behavior is unchanged apart from the window semantics:
the check fails open when the counter read fails, and a failed
increment never masks the underlying domain error.
2026-09-05 23:38:50 +08:00
TT c7343d2aa2 fix(monitors): send explicit user role in Gemini monitor probes
The Antigravity native Gemini route (generateContent) expects each
contents entry to carry an explicit "user" role, matching the request
shape used by the established Antigravity account-test flow. Without it
the probe body is rejected with 400 INVALID_ARGUMENT, so Gemini channel
monitors report false failures.

Add the explicit role to the Gemini provider adapter body and cover it
with a regression test that parses the generated request body.
2026-09-05 23:30:45 +08:00
TT a6430bfe20 fix(antigravity): refresh default user agent version
The Antigravity service fingerprints clients by the version embedded in the User-Agent header. The built-in default (1.23.2) had drifted far behind current real client builds, so requests using the default UA started failing OAuth and probe flows.

Bump DefaultUserAgentVersion from 1.23.2 to 2.9.1 (current client build) and update the constant test accordingly. The default remains overridable via ANTIGRAVITY_USER_AGENT_VERSION and the admin setting.
2026-09-05 23:28:33 +08:00
TT 32bf3d0f33 fix(settings): describe Grok cross-client mapping default as enabled
The Grok cross-client model mapping is enabled by default: InitializeDefaultSettings writes "true", and the parser treats a missing/empty value as enabled ("Default true (missing/empty -> enabled)"). However the comment above the defaults and the admin settings UI hint (EN and ZH) both claimed "disabled by default", contradicting the actual behavior.

Align the comment and both UI hints with the real default. No behavior change.
2026-09-05 23:17:34 +08:00
whhphd b97a798ebc fix(grok): release abandoned media slots and preserve video ownership
Claim scheduler-acquired slots before media eligibility checks and task-owner
validation. Release abandoned attempts immediately and retain once-only
cleanup on cancellation and forwarding completion.

Select only the resolved video owner, reuse bounded account waiting without
sticky escape, and preserve the task binding and TTL. Keep generation
failover and owner/billing isolation intact.

Regression tests reproduce unreleased account slots on upstream ab99d56e9
and pass with this change. Validation: go test ./..., unit and integration
suites, focused race tests, backend build, and golangci-lint (0 issues).
2026-09-05 20:25:08 +08:00
Zero Clover cb31033972 feat(openai): reuse pinned accounts for standard model discovery 2026-09-05 19:58:47 +08:00
Wesley Liddick 578785ee7f Merge pull request #6640 from cyhhao/fix/claude-billing-fingerprint
fix(claude): align billing fingerprint with outbound User-Agent
2026-09-05 16:59:33 +08:00
Wesley Liddick ed7c8f2208 Merge pull request #6572 from lyy0709/codex/gpt-6-astra
feat(openai): complete GPT-6 Astra support
2026-09-05 16:50:40 +08:00
cyh 2e1c7c00a6 test(claude): check request body close errors 2026-09-05 16:43:54 +08:00
cyh 994ca26e92 fix(claude): align billing fingerprint with outbound user agent 2026-09-05 16:34:00 +08:00
Wesley Liddick d2f87c612e Merge pull request #6638 from feeeei/feat/images-url-to-b64json-backfill
feat(images): 账户开关将生图结果 url 下载回填为 b64_json
2026-09-05 16:24:40 +08:00
lyy0709 62cd63bfda fix(openai): retain capabilities when model listing is unavailable 2026-09-05 16:24:15 +08:00
lyy0709 dee35a841d merge: reconcile Astra fixes with upstream capability sync 2026-09-05 16:19:57 +08:00
Wesley Liddick fb44e2dd09 Merge pull request #6514 from StarryKira/codex/anthropic-reasoning-pricing
feat: add Anthropic reasoning effort and Fable 5.1 max pricing
2026-09-05 16:19:18 +08:00
Wesley Liddick c42d78e26c Merge pull request #6571 from DuskLin/feat/codex-ultrafast-tier
feat(codex): support ultrafast service tier
2026-09-05 16:18:16 +08:00
Wesley Liddick 2dc287f2e0 Merge pull request #6636 from specialpointcentral/cyber-ws-recording-fix
fix(openai): record WS cyber policy failures across paths
2026-09-05 16:17:33 +08:00