Skip to content

chore: merge upstream v0.2.13 preserving downstream contracts - #232

Merged
HTExplicit merged 110 commits into
mainfrom
sync/upstream-0.2.13
Oct 4, 2026
Merged

HTExplicit merged 110 commits into
mainfrom
sync/upstream-0.2.13

Conversation

@HTExplicit

Copy link
Copy Markdown
Owner

Integrates official v0.2.13 (3040209f2, plus the VERSION sync b8dece900) on top of v0.2.11-codexrip.8. Covers the two official releases since the last integration: 26 non-merge commits, 157 paths.

Do not merge yet. The owner decided on 2026-10-03 to ship the upgrade together with the follow-up changes (profit control back to upstream, reasoning recovery as one global setting, removal of the deprecated-settings block) as a single v0.2.13-codexrip.1. This PR is open so that the required checks run early.

What is adopted

Every official change, with official behaviour:

  • TypeSafe / Jev platform with POST /v1/systemone
  • Recharge bonus and discount tiers
  • Account priority quick-adjust in the accounts table
  • Order-verify rate limit, atomic verify-code attempts, hashed single-use reset tokens
  • Antigravity client-facing error scrubbing
  • Usage settlement for API keys deleted mid-request
  • Grok CLI identity update, API key sort by group name

Downstream contracts kept

Details and per-file resolutions are in .downstream/upstream-review-v0.2.13.md.

  • TypeSafe goes into the fork's generic mechanisms (platform lists derived from CONCRETE_PLATFORM_VALUES, defaultModelIDsForPlatform, accent() colour tokens, composite capacity pool) instead of upstream's literal lists.
  • Connection tests keep the fork's rules for TypeSafe: raw upstream error text, and success only on a valid System One answer to the question asked.
  • A failed System One request keeps the bounded upstream body in Ops, like every other forward path.
  • Capacity stays manifest-derived; jev-latest gets no static window.
  • New official UI is styled for the console theme with static hooks only.

Migrations

  • Upstream's 241_add_payment_order_bonus_amount.sql is byte-identical.
  • 241_add_typesafe_platform.sql carries cindy in the quota list, as the downstream copies of 237 and 238 do, so a database that has not reached 260 still upgrades in one start.
  • New tail 265_user_platform_quota_typesafe_union.sql states the final eleven-platform quota CHECK. Without it a new database ends without typesafe, because upstream's 241 sorts before the downstream files that re-create that CHECK.
  • 264 is left for a separate downstream change.

Verification

Run locally on the final tree: backend build (windows and linux), go vet with every build tag, golangci-lint on the touched packages, the Go tests that live in conflicted files or pin the adapted behaviour, vue-tsc, eslint on changed files, and vitest for the conflicted, new and modified-component specs.

Not run locally: full and race suites, database-backed integration tests, any migration rehearsal against PostgreSQL (the migration analysis is by reading), real TypeSafe or model calls. The noul answer shape required by the TypeSafe connection test comes from upstream's own client and was not checked against the live API.

Closes #225, closes #228.

🤖 Generated with Claude Code

aofee and others added 30 commits September 20, 2026 20:20
分组开启 claude_code_only 并配置降级分组时,/v1/messages 会在选号入口的
checkClaudeCodeRestriction 中把非 Claude Code 请求解析到降级分组;但
GatewayHandler.ChatCompletions / Responses 在 handler 层只要分组开了
claude_code_only 就直接返回 403,不看降级分组,请求到不了调度层:只会发
Chat Completions / Responses 的第三方客户端拿这类分组的 Key 一律 403。

- handler 层提前拒绝只在未配置降级分组时生效,拒绝的状态码与文案不变
- 配置了降级分组时请求继续,由 SelectAccountWithLoadAwareness 解析到降级分组
  选号,再经已有的 Chat Completions / Responses ⇄ Anthropic Messages 转换转发;
  service 层无改动
- 与 /v1/messages 的降级语义一致:选号与渠道限制预检按降级分组,计费与用量
  记录仍按 API Key 所属分组

新增 handler 单测覆盖两个入口在有/无降级分组时的行为。
Replace the static priority number in the accounts table with a compact
stepper: hover reveals -/+ buttons, clicking the value allows typing
(Enter to save, Esc to cancel, arrow keys to nudge). Rapid clicks are
debounced into a single priority-only PUT, failures revert the value and
surface a toast, and the row is patched in place without a full reload.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
disabled:opacity-30 overrode opacity-0, so the decrement button stayed
faintly visible on every row already at the minimum. Style disabled
buttons via text colour instead, and raise idle icon contrast.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
POST /api/v1/payment/public/orders/verify is unauthenticated and still used
by PaymentResultView as a fallback, so keep it but cap it at 20 req/min per
client IP (fail-open on Redis errors) to make out_trade_no enumeration
impractical without affecting users mid-payment.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ient

The antigravity Gemini forward path returned the raw upstream Google error
body to end users, which can contain consumer project numbers, GCP project
IDs and service account emails. Return a Gemini-style error body with only
code/status and a scrubbed message; raw body stays in ops logging.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…okens

- Verification code attempts (register + notify email) are now reserved via
  an atomic Redis INCR (Lua) before comparing, so concurrent wrong guesses
  cannot exceed the 5-attempt cap.
- Password reset tokens are stored as SHA-256 hashes and consumed with an
  atomic Lua compare-and-delete, so a token can only be used once even under
  concurrent requests.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Balance-mode admission only checked cached balance > threshold, so N
concurrent expensive requests could all pass and overdraw the user.
Add a Redis in-flight reservation: at admission estimate a conservative
per-request cost (input estimate + output price x max_tokens, times rate
multiplier) and atomically require cached_balance - inflight >= estimate
(first in-flight request always admitted). Reservations are released via
defer on every path and expire by TTL. Fails open on estimation / Redis
errors. Configurable via billing.inflight_reservation (enabled by default).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…timate like billing; wire remaining endpoints

Address gaps found in adversarial verification of the in-flight balance
reservation:

- Early release: the reservation is now a ref-counted handle carried on the
  request context. Billing tasks submitted via submit*UsageRecordTask take a
  ref and return it only after RecordUsage has run; with reservation enabled
  the balance cache is deducted synchronously inside the billing task, so the
  reservation is released only after admission can see the deduction. Dropped
  tasks return their ref immediately. The reservation is renewed every ttl/3
  while the handler runs; after the handler returns it is no longer renewed,
  so the post-handler hold is bounded by one TTL.
- Estimate: use the same billing model (channel mapping + BillingModelSource,
  max over requested/mapped when ambiguous) and ModelPricingResolver (group
  ModelPricing incl. wildcards, channel per_request/image/video/token
  overrides), default/group/user rate + peak multiplier, plus image, video,
  audio and search surcharge kinds. Unpriced requests fail open with a
  throttled log; new billing.inflight_reservation.fail_closed_on_unpriced
  (default false) rejects them instead.
- Wire images, embeddings, web/x search, alpha search, grok media generation,
  grok realtime/voice, OpenAI-platform /v1/messages and ResponsesWebSocket
  (session-level reservation with renewal).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…don't renew expired reservations

- Inflight estimate now mirrors billableModelWithFallback: when the primary
  billing model has no price, fall back to the channel-mapped model (also for
  billing_model_source=requested) and to account-level mapped models (max over
  schedulable accounts in the group, cached 30s).
- Composite groups also consider concrete account-mapped models (max) so the
  alias' family fuzzy price cannot underestimate.
- Renew Lua refuses (and purges) members whose score <= now.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…y when unpriced

- only consult account mappings after primary/channel candidates fail to price (or composite)
- source mappings from the scheduler snapshot (same pool as account selection), no direct DB query and no per-model cache
- ungrouped API keys use the scheduler's ungrouped pool
- no negative caching: new mappings take effect on the next request

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
github-actions Bot and others added 24 commits October 2, 2026 06:45
The create form shows the upstream billing auto-probe toggle for every
API-key platform and enables it by default. TypeSafe was added as an
API-key platform but not to IsUpstreamBillingProbeIdentity, so creating a
TypeSafe account with the default form state fails with
400 UPSTREAM_BILLING_PROBE_ACCOUNT_INVALID ("account is not an API key
account").

TypeSafe accounts store credentials.api_key/base_url like every other
API-key platform, which is all the probe reads. Add TypeSafe to the probe
identity set, and add typesafe.ai to the official API domains so accounts
on the default https://api.typesafe.ai base URL record "unsupported"
without sending the key to a path that cannot exist, matching the other
official providers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs: add security policy with private vulnerability reporting
… completes

When an API key with quota or rate limits was deleted while a request was
still in flight, the key counter update matched no rows and returned
ErrAPIKeyNotFound, rolling back the whole billing transaction including the
balance/subscription charge. Skip the key-scoped counters in that case and
keep settling the user and account side as usual.
…ted-key

fix(billing): settle usage when the API key is deleted before billing completes
…am-billing-probe

fix(billing): allow upstream billing probe for TypeSafe API-key accounts
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Upstream v0.2.13 (3040209) merged at upstream/main b8dece9, which adds
only the VERSION sync. v0.2.12 lies inside the range. 26 upstream commits,
157 paths; 16 conflicted.

Adopted as upstream: the TypeSafe / Jev System One platform
(POST /v1/systemone, API-key accounts, jev-latest), recharge bonus tiers,
the account priority quick adjust, the public order verify rate limit, the
Antigravity client error scrubbing, atomic verification codes with hashed
single-use reset tokens, the Grok CLI identity update, usage settlement for
deleted API keys and API key sorting by group.

Downstream contracts kept at the conflicts:
- GatewayHandler.Models keeps the generic non-Anthropic fallback;
  defaultModelIDsForPlatform gains the TypeSafe case.
- Quota tables, default quota settings and dashboard cards keep the lists
  derived from CONCRETE_PLATFORM_VALUES; platformColors keeps accent().
- The TypeSafe API-key-only check runs before the downstream credential
  normalization and capacity override validation.
- The TypeSafe connection test joins the downstream test dispatcher.
- The jev-latest fallback price is added; the DeepSeek fallback block stays
  removed.

Adaptations of auto-merged code: accountTestCatalog returns the TypeSafe
entry instead of writing the response, and parseTestSSEOutput returns three
values. Fixtures that pinned ten concrete platforms pin eleven; the name
filter of the downstream-verify quota step follows the two renamed tests.

frontend/package.json, pnpm-lock.yaml, go.mod and go.sum are unchanged:
axios 1.20.0 was already downstream. Generated ent code matches the schema.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Connection and batch tests resolve jev-latest for a TypeSafe account
  instead of a Claude model name; the reasoning resolver offers no effort.
- The TypeSafe connection test reports the transport error and the whole
  upstream body, like every downstream connection test. Upstream sanitized
  the error and cut the body to 2000 bytes.
- TypeSafe accounts are not eligible for the API-key model catalog refresh:
  System One has no model list and the sync request rejects the platform,
  so a refresh could only fail and back off.
- The composite capacity pool uses matchingPlatforms(PlatformComposite)
  instead of a second platform list, so TypeSafe accounts of a composite
  group contribute their capacity rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…abases

Upstream's 241_add_typesafe_platform.sql sorts before the downstream files
that re-create user_platform_quotas_platform_check (241 compat, 251, 260).
A new database therefore ended with ten platforms and no typesafe, while a
database already past 260 applies upstream's file last and keeps eleven.

264 states the eleven-platform CHECK once more at the tail: SET LOCAL lock
and statement timeouts, DROP IF EXISTS plus ADD, no row written, a no-op
when repeated. The composite route CHECK needs nothing: no downstream file
re-creates it after upstream's 241.

TestTypeSafeStaysInTheFinalPlatformChecks pins the last file that defines
each constraint and the shape of the tail file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Accounts table: a click inside the priority control no longer reaches
  the row, which opens the details drawer downstream. The control is the
  value of the stacked priority line and its steppers take the label tone.
- Create account dialog: the eleventh platform segment (TypeSafe) takes the
  sky identity glyph.
- Recharge tier editor: the mode toggle carries the static seg-box hook.

Behaviour stays upstream's. No :has() and no runtime-bound hook is added.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…fest

upstream-risk.json is regenerated with .github/scripts/upstream_risk.py for
v0.2.11..v0.2.13 against 06e865f: review_required, 157 upstream paths, 65
overlapping, 31 critical, no remaining conflict. The 16 initially conflicted
paths are recorded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n Ops

Upstream's System One forwarder recorded only the extracted error message
(empty unless the body has error.message, detail or message) and, for a
2xx response it cannot relay, only "typesafe invalid response". The client
gets a fixed sentence and no log line carried the body, so an administrator
had no way to read what the upstream answered. Every other forward path
keeps the body in the Ops record.

- handleSystemOneErrorResponse records the body bounded by
  gateway.log_upstream_error_body[_max_bytes], as handleErrorResponse does:
  in the top-level Ops detail, in the event's Detail and (the event is
  passthrough) UpstreamResponseBody, and in the service body log line.
- DecodeSystemOneResponse returns the body it read but did not accept in a
  SystemOneResponseError, so the response_error event carries it too. The
  error text and errors.Is(err, ErrSystemOneResponseTooLarge) are unchanged.
- The client response is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…swer

Upstream's TypeSafe connection test passes on any HTTP 200 JSON object, so
a relay or a wrong base URL that answers {} or an error envelope with 200
counted as a healthy account, and a passed test recovers the account's
error and rate-limit state (single, batch and scheduled tests).

Downstream connection tests need a valid protocol terminal. The test now
passes only when the response answers the question it asked:
answers.connection_test of type noul with a probability between 0 and 1.
typesafe.NoulScore checks that with the answer validator Evaluate applies,
now one shared function. Any other 200 body fails with the body as it was
received and changes no account state.

The two upstream fixtures that passed without a valid answer get one, and
TestTypeSafeAccountTestRequiresAnswerToItsQuestion pins the failures.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…m file

Upstream's 241_add_typesafe_platform.sql relies on its quota list being a
superset of 238's. The downstream 238 (and 237) list cindy, so with
upstream's bytes a downstream database that has not reached 260 and still
holds cindy quota rows fails on this file and startup stops, although the
same database upgrades in one start on the current release, where 260
deletes those rows before it re-creates the CHECK.

Carry cindy in the quota list of the still unrecorded file, as 237 and 238
do. 260 then removes the rows and the value, and 264 states the final
eleven platforms on every path. Production (past 263, no cindy row) is
unaffected: it gets the wider list from 241 and the final one from 264 in
the same start.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Coverage: 84 of the 93 upstream-only paths are byte-identical to
  upstream/main and the nine adapted ones are named; 44 of the 48
  auto-merged overlapping paths carry exactly upstream's line changes, and
  platforms.spec.ts is listed as adapted next to account_handler.go.
- SettingsView.spec.ts and UserPlatformQuotaModal.spec.ts had four conflict
  hunks each, not five.
- The Codex catalog exclusion of TypeSafe holds for composite groups; a
  TypeSafe group's own manifest lists jev-latest, as upstream.
- Record the System One Ops body, the connection test success rule and
  cindy in the 241 quota list; the known restriction for databases older
  than 260 is gone. List the checks run again on the final tree.
- settings-ops-public.css 2.2: the positional p-4 body rule also reaches
  upstream's RechargeBonusTierEditor card; the comment names both cards
  (rule unchanged).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The connection test's success rule relies on the noul answer shape that
upstream's own client requires of every content moderation answer. Record
that it was not checked against the live API here.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The upstream event for a 2xx response the gateway cannot relay carried the
  decoder's error text, which is either the invalid response or the size
  limit error.
- A TypeSafe connection test whose 200 body cannot be read reports the read
  error.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
264 is taken by a separate downstream change (Codex runtime plugin state
removal) that merges on its own schedule. Neither file has been applied
anywhere, so only the name and the references to it change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@HTExplicit HTExplicit added the upstream-reviewed Human upstream overlap review completed label Oct 3, 2026
One conflict, backend/internal/service/admin_account.go: upstream's TypeSafe
API-key-only check stays, and the maps.Clone(accountExtra) line that #233
removed stays removed. The risk manifest is regenerated against the new
downstream commit and the review document names it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@HTExplicit
HTExplicit merged commit 27e1c00 into main Oct 4, 2026
21 checks passed
@HTExplicit
HTExplicit deleted the sync/upstream-0.2.13 branch October 4, 2026 00:13
HTExplicit added a commit that referenced this pull request Oct 4, 2026
…ired settings and options deleted (#235)

Ships with the upstream v0.2.13 integration (#232) as v0.2.13-codexrip.1.
Owner decisions of 2026-10-03 and 2026-10-04.

Group profit control is upstream's again: the kill switch, the 400 on
profit_* fields, the snapshot constants and the read-only "retired" block
are gone; the code and its tests equal upstream v0.2.13, and the Codex
models manifest stays outside the gate. Auth snapshot version 28.

Signature recovery keeps its logic and follows one global switch, default
on: settings row reasoning_recovery_config, read from memory on the
forwarding path, GET/PUT /api/v1/admin/reasoning-recovery, page 推理恢复
under 扩展功能. Chat reasoning replay and the two per-account reasoning
options are deleted. The rejected-cipher memory runs on one long-lived
Redis client with the budget as the absolute deadline.

Deleted: the two retired OpenAI API-key compatibility settings with their
read-only block, the stop reason policy_unavailable, the dated live
reasoning diagnostic.

TypeSafe account dialogs show their own hints, and an upstream model sync
is offered and started only where the backend can list models. A system
prompt binding is offered and stored only for platforms with an insertion
point.

Migrations: 266 deletes the two retired settings rows, 267 removes the two
reasoning option keys from accounts.extra, 268 removes system prompt
bindings from accounts on a platform without an insertion point.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

upstream-reviewed Human upstream overlap review completed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Upstream v0.2.13 merge conflicts Upstream v0.2.12 merge conflicts