Skip OpenAI messages-dispatch gpt-5.x defaults for OpenCode groups and
composite OpenCode targets. Filter unpriced claude-* billing candidates,
estimate count_tokens locally, sanitize body session IDs before setting
X-OpenCode-Session, and treat Zen 402 as recoverable payg cooldown.
Stop rewriting the inbound body before protocol routing so account
model_mapping applies once and usage_logs.model stays the client name.
Apply account model_mapping to the request body before OpenCode
protocol routing so /v1/responses, /v1/chat/completions, and
/v1/messages all send the mapped ID as the real upstream model.
Keep the internal platform ID opencode_go. Product surface is OpenCode
with account_mode zen|go, model-aware routing across Chat Completions,
Responses, and Anthropic Messages, and X-OpenCode-Session derived from
prompt_cache_key / metadata.user_id so prompt cache can hit.
Unmatched models fall back to /v1/chat/completions. Related to #6742.
- Resolve conflict in openai_codex_models_service_test.go: keep both
GPT-6 Astra instructions template assertion (from this PR) and ultra
reasoning level + multi_agent fields (from #6718 on main)
- Fix compilation error on main: add missing `group *Group` parameter
to buildCodexModelsManifestForAccounts calls in
openai_codex_model_metadata_test.go (lines 24, 32) — caused by #6688
changing the function signature while #6718 added new callers with
the old signature
Follow-up to the event-time proxy snapshot: every upstream attempt now
carries attribution, the direct/unknown contract is enforced everywhere,
and the queued upstream_errors array has hard bounds again.
Attribution coverage
- compact fallback retry helper and the streaming compact failover event
now stamp proxy_id/proxy_name; the non-streaming SSE compact retry
previously dropped its first attempt on `continue` and now records it
- raw chat-completions stream truncation events (new on main) attributed
- Anthropic/Bedrock transport errors get attribution inside the shared
handleUpstreamTransportError helper, so all five callers inherit it
- OpenAI first-output timeout takes attribution from the caller: HTTP
callers pass the HTTP rule, the WS passthrough relay passes the WS rule
(no managed proxy => unknown, never direct, because http.DefaultClient
may honor environment proxies)
Contract hardening
- single opsUpstreamProxyAttribution returns (id, name) from one decision;
a hydrated Proxy without a durable ID is unknown, not id=null+name
- both normalizers (struct at append time, JSON on detail reads) enforce
proxy_id==null <=> sentinel name; blank managed name => "proxy"
- detail-read normalization edits only the attribution keys via sjson, so
keys from older struct versions and stored key order are preserved
Queue bounds
- keep at most 256 attempts and ~512 KiB serialized per entry, newest
first; the oldest retained event records dropped_earlier_attempts
- attempts outside the 16-event body window trim url/message to 512 bytes
- restore the empty-event drop rule, judged before the body window clears
detail so older real attempts survive
Tests cover each site above (managed proxy, direct, WS unknown), the
normalizer invariants, key/order preservation, and the count/byte bounds.
Moonshot pay-as-you-go and Kimi For Coding both expose native
/v1/responses. Stop converting Kimi Responses traffic to Chat
Completions, and treat Kimi like DeepSeek for protocol selection while
keeping /v1/responses URL construction.
Merge origin/main into contrib/routed-codex-model-catalog.
Single conflict in openai_gateway_forward.go: main added
ClearActualOpenAIUpstreamEndpoint + SetActualOpenAIUpstreamEndpoint
(commit 4795650d2) at the same insertion point where the PR added
filterOpenAIResponsesNoneReasoningEffortForAccount. Both changes
are independent — endpoint observability first, then body filtering.
Keep catalog membership on schedulable accounts and intersect advertised
capabilities across all active group members that map an alias. Preserve
upstream plugin, service-tier, and models-list body-limit changes.
Co-authored-by: Cursor <cursoragent@cursor.com>
- Accept fast|priority (canonical priority), flex|auto|default|scale on
/v1/responses and /v1/chat/completions; reject unknown/empty/non-string
with HTTP 400; omitted and null stay compatible.
- Propagate service_tier through JSON/SSE, Responses<->Chat conversions,
fallback paths and HTTP->upstream WebSocket bridge.
- Billing prefers the upstream terminal tier; the outbound (policy-
transformed) tier is used only when upstream omits the field.
Explicit upstream default bills Standard even when Fast was requested.
- Pricing: Fast premium 2x Standard for gpt-5.6-sol/terra/luna and
gpt-5.4; 2.5x for gpt-5.5; channel FastMultiplier stays authoritative.
- Live verification (official Codex 0.149.0 + gateway, HTTP & WS):
upstream ChatGPT backend may return terminal default even when the
account catalog advertises priority; billing follows the actual tier.
Account model mappings decide availability, but generated Codex catalogs previously inferred capabilities from local model-name tables. Compatible upstreams can expose unknown models such as OpenCode x-preview-f-free, so reasoning levels, image input, and context limits were missing or wrong.
Sync capability metadata from the account model endpoint, enrich incomplete lists from the Models.dev provider matching the account base URL, and persist only complete snapshots. Mixed groups consume the safe intersection across schedulable accounts. When IDs sync without complete metadata, return an explicit warning and preserve the previous snapshot.
Third-party Responses providers often omit /models. If that endpoint returns 404 or 405 and the account already has concrete model mappings, use those configured upstream IDs for capability enrichment. Authentication, rate-limit, server, and network failures remain hard errors, and metadata is never guessed across providers by model name alone.
Advertise a single none choice for non-reasoning models so current Codex can select them, then omit that catalog placeholder when forwarding to compatible Responses upstreams while preserving official OpenAI request semantics.