- ChannelsView platformOrder now includes the three CN provider
platforms so channel pricing can be configured for them; composite
group expansion/attachment stays limited to the five main platforms
(matches backend isConcreteRequestPlatform and composite-routes
target_platform validation)
- SyncPricingModels maps kimi->moonshot, zhipu->zhipu,
deepseek->deepseek; also fixes gemini mapping to "google" which
matched zero catalog entries (provider key is "gemini")
- Add CN platform colors to channel pricing model tag classes
- Replace ApplyCodexCanonicalIdentity with CodexCanonicalAuthIdentity /
ApplyCodexCanonicalAuthIdentity: the credential face (auth.openai.com
token exchange / refresh / PAT whoami) now sends the originator +
canonical User-Agent pair and no version header, matching codex-rs
default_headers(); the version gate (#3901) only exists on the
/backend-api/codex inference face. whoami keeps its original header
shape (originator + UA) with the canonical UA source.
- Token exchange and refresh send the full pair instead of a bare UA,
eliminating the half-identity (UA without originator) combination no
real client ever emits.
- Codex models manifest: the Version header now follows the client's
own client_version when it is valid and >= the upstream floor (same
source as the query param, restoring the pre-refactor consistency),
falling back to the canonical version otherwise; the query param
keeps its verbatim passthrough contract.
- Drop the now-unreferenced openAICodexProbeVersion constant and its
vacuous consistency assertions; probes resolve their version through
resolveCodexOutboundIdentity at runtime.
The struct field alone never reached the wire: the raw passthrough
pipeline is covered by enableMixedGeminiToolInvocations (#5711), but
TransformClaudeToGeminiWithOptions builds GeminiToolConfig from scratch
and never set the flag, so gemini-* models entering through the Claude
format gateway could still hit the upstream 400 from issue #5709.
- Set IncludeServerSideToolInvocations=true when the built tool
declarations mix functionDeclarations with googleSearch, matching the
raw-path injection semantics.
- Replace the marshal-roundtrip-only test with behavior tests that
drive TransformClaudeToGeminiWithOptions: mixed tools set the flag,
function-only and web-search-only requests leave it unset.
- user_repo.create(): keep the TxFromContext fast path, but restore
tolerance for dbent.ErrTxStarted in the self-owned-transaction branch.
ent's Client.Tx only inspects the driver type, so a repository built
from a tx-bound client (client-injected transactions, e.g. the
integration fixture testEntTx + tx.Client()) hits ErrTxStarted; reuse
that client instead of failing. Fixes the two red integration tests in
allowed_groups_contract_integration_test.go.
- createUserAndClaimInvitation: roll back via defer (matching the OAuth
registration precedent) so a panic inside the transaction cannot leak
the connection.
- settingRepoStub: guard call counters and state with a mutex; the new
concurrency regression test exercises it from multiple goroutines and
the unsynchronized counters were flagged by -race.
Token exchange, PAT whoami, models, probes, and pre-writes now follow
the same UA/version chain as Codex inference instead of hardcoded
codex-cli/0.91.0 or compile-time constants.
- Add IncludeServerSideToolInvocations field to GeminiToolConfig to prevent dropping client tool settings.
- Fix HTTP 400 error when mixing built-in tools (e.g. Google Search) with function calling on Gemini 3.6/3.7 models.
- Add serialization/deserialization unit test TestGeminiToolConfig_IncludeServerSideToolInvocations.
Fixes#5709
RegisterWithVerification checked CanUse() and then marked the code used in
two separate, non-transactional steps; the second step's failure was
swallowed ("invitation code mark failure does not affect registration").
Concurrent registrations with the same invitation code could all pass the
check and each create an account, turning a one-time invitation code into
an unlimited account factory (TOCTOU race).
Fix:
- AuthService: create user and claim the invitation code inside one DB
transaction (createUserAndClaimInvitation). The claim reuses
redeemRepo.Use's conditional UPDATE (WHERE status='unused'); losers are
rejected with INVITATION_CODE_INVALID and their transaction (including
the user insert) is rolled back. No-code registration path unchanged.
- userRepository.create: explicitly join an outer ent transaction via
TxFromContext instead of relying on Client.Tx returning ErrTxStarted
(ent's Tx never inspects the context, so the old reuse branch was dead
code and user inserts always committed in their own transaction,
leaving orphan users behind when the outer transaction rolled back).
Regression tests:
- unit: concurrent register with one invitation code must succeed exactly
once (8 goroutines -> 1 success, 7 x INVITATION_CODE_INVALID)
- integration: outer-tx rollback removes user and releases the claim;
commit persists both atomically