Extract Spark rate limit windows from the Codex usage response and expose
them as spark_session/spark_weekly quotas, reusing the existing prefix
mechanism. Map codex quota types to readable dashboard labels.
Fixes#3431
Add gemini-3.7-flash and its tiered high/medium/low variants to the
Antigravity and Gemini registries, with matching capabilities, pricing
and Antigravity quota tracking.
extractModel now recognises gemini-3.7-flash-tiered alongside 3.6 and
derives the version from the request, so thinkingLevel still maps to the
right tiered alias.
Closes#3286Closes#3281
Inspect images[], experimental_attachments/attachments, message-level
image/image_url/audio_url, and inline data:image|audio|pdf URIs on trailing
user turns so Vision Adapter auto-switch fires for Hermes/Ollama/Vercel AI
SDK shapes. stripOpenAI now also drops msg.images and image attachments when
the active model lacks vision support.
Multiple tabs/accounts/auto-refresh funneled straight to Anthropic and tripped 429. Add a 120s TTL cache keyed by access token with in-flight promise dedup, serve the last good read on soft failure, and thread a force flag through getUsageForProvider for manual refresh. Also lower the dashboard poll cadence (180s to 600s) and stable group-by-provider so connection order stops jumping.
Co-Authored-By: Claude <noreply@anthropic.com>
Google fingerprints User-Agent/Client-Metadata on loadCodeAssist and
onboardUser, silently refusing to provision a cloudaicompanionProject
when they don't match the real IDE. Split antigravity's headers out of
the shared gemini-cli constants instead of overwriting them, so the fix
doesn't touch gemini-cli or any other provider.
Inspired by #3000 (thanks @stoXmod for flagging the resource-exhausted
issue), rewritten to keep gemini-cli untouched.
Qwen OAuth flow (portal.qwen.ai) stopped working reliably; drop the
executor, registry entry, OAuth provider/service, token refresh
profile, usage handler, and related test coverage and baselines.
- detectRequiredCapabilities: infer audioInput/videoInput from block
type and embedded mime, not just vision/pdf
- handleChat / handleSingleModelChat: augment combo and single-model
routing with capacity-adapter models when the target lacks a
required capability, wrapped with history stripping for the
adapter model's context window
- Enable vision + audioInput capacity-adapter pools by default for new
and existing users (mergeWithDefaults backward-compat)
- Fall back to oc/mimo-v2.5-free when an enabled pool has no models
configured, both in the backend resolver and the combos UI (auto
refill on removing the last model from a pool)
- Hide PDF/Video from the Vision Adapter UI (PDF never implemented,
Video lacks translator support) while keeping the settings shape
- Exclude combos from the model picker when opened from the Vision
Adapter section
- mimo-v2.5 registry entry now declares audioInput/videoInput
- Simplify combo strategy and Vision Adapter descriptions
Codex Responses Lite clients routed to a chat-native OpenAI-compatible
provider lost tool use in three places: non-streaming Chat responses
leaked the raw chat.completion envelope instead of Responses output
items, internal reasoning continuity fields leaked into the outbound
Chat body causing some upstreams to reject the request, and the
Responses to Chat request translator ignored additional_tools,
custom_tool_call, and custom_tool_call_output items entirely.
Also fixes apiType (chat vs responses) for openai-compatible nodes
being resolved from the immutable provider ID instead of the stored
node config, so editing a node's API Type had no runtime effect.
getAntigravityUsage filtered the fetchAvailableModels response through a
hardcoded importantModels list that only contained 3.5 Flash, silently
dropping the 3.6 Flash quota buckets so no usage bar rendered.
Map the Grok OAuth access-token tier claim to the public plan label
and prefer it over internal /user entitlement names. Fails open to
existing plan detection for opaque or malformed tokens; upstream
remains authoritative for access and quota enforcement.
PAT-to-job-token exchange was duplicated between the executor and the model service, each with its own cache. Consolidate into qoderModels.js and have the executor import it.
Also add a qoder case to the API-key validate route - the generic OpenAI-compat probe cannot validate a PAT (needs job-token exchange + COSY signing first), so bulk-add always reported unknown for qoder keys.
Adds pt-... token auth as an alternative to OAuth device flow. A PAT can't
sign COSY requests directly, so it's exchanged for a short-lived job token
(jt-...) plus userId via openapi.qoder.sh, then used for signing.
Also fixes job-token traffic (jt-...) being rejected by api3.qoder.sh with
403 "Login expired" — the official qodercli serves jt- traffic from
api2.qoder.sh instead, so buildUrl/model-list routing now branches on it.
Quota usage and the dashboard add-key modal are updated to resolve PAT
credentials and label the field correctly, and bulk-add now validates
each key so it gets a real testStatus instead of a hardcoded "unknown".
Neutralize CLI-agent system prompts that trigger CodeBuddy CN's content filter, add usage/quota tracking for codebuddy-intl sharing CN's logic, and normalize codebuddy-intl request messages to the shape it expects.
Ollama: replace informational stub with real quota tracker hitting ollama.com/api/usage (session 5h + weekly 7d, 0..1 ratio) and /api/me plan label; bind handler to apiKey + add features.usageApikey so apikey connections work.
Token refresh: add backgroundTokenRefresh scheduler that refreshes OAuth connections within max(provider lead, 30min) of expiry, independent of inbound traffic (10s after boot, then every 5min, unref'd timers, DISABLE_BACKGROUND_TOKEN_REFRESH kill-switch, fail-open per tick/connection). Registered from custom-server.js (listening) and initializeApp.js. checkAndRefreshToken gains opt-in {force} for the scheduler; request path unchanged.
Remove "Ported from OmniRoute" and cockpit-tools attribution comments.
User-Agent strings and README/landing credits are left intact.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Decode GetGrokCreditsConfig frames when REST billing returns empty
caps, so SuperGrok weekly quota shows in the usage dashboard.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Wire /v1/usages for Kimi (OAuth + API key) and balance API for DeepSeek,
flag both providers with usage/usageApikey, and normalize their quotas
in the dashboard ProviderLimits parser.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add gemini-3.6-flash tiered (high/medium/low) for Antigravity routing
via upstreamModelId "gemini-3.6-flash-tiered(level)" + thinkingLevel,
plus gemini-3.6-flash and gemini-3.5-flash-lite direct API models.
- getModelUpstreamId: split (level) suffix before lookup, re-append after
- Antigravity executor: preserve transformed body.model
- MITM extractModel: parse thinkingLevel for tiered model (default medium)
- Isolate Cloud Code endpoints: discovery (loadCodeAssist/onboardUser/
quota) on PROD cloudcode-pa, chat transport on daily-cloudcode-pa
to bypass prod 429
Real Cursor IDE now uses AgentService at agent.api5.cursor.sh (HTTP/2-only)
while 9router still spoke the retired ChatService at api2.cursor.sh with
outdated headers, producing HTTP 429 "Update Required". Add an executeAgent
path that builds an agent.v1.RunRequest Connect RPC over a raw http2 stream
and fetches the account-specific usable model catalog via GetUsableModels.
Also implement MCP tool calling over AgentService: encode OpenAI tools as
AgentRunRequest.mcp_tools (McpToolDefinition with google.protobuf.Value
input_schema), decode McpArgs tool calls, and forward them to the client as
OpenAI tool_calls so the client runs the tool and resumes in the next turn.
Reply to request_context_args with a non-empty RequestContext, to server
heartbeats with client_heartbeat, and to KV blob get/set with empty results,
so action queries no longer stall the stream. Fold the client system prompt
into the user message (custom_system_prompt makes the server return an empty
turn). Bump clientVersion to 3.12.17 and add the x-cursor-client-commit
header so the gateway identifies as a current Cursor IDE release.
Match captured official Antigravity IDE traffic: cloudcode-pa host,
antigravity/ide/2.1.1 User-Agent, IDE-shaped agent requestId, and drop
router-only stream/usage headers plus the legacy double system prompt.
Co-authored-by: Cursor <cursoragent@cursor.com>
Add read-only GET to inspect per-credit reset inventory (status, granted,
expiry, remaining) with a Quota Tracker modal. DRY the route via shared
connection/refresh helpers; keep existing consume POST unchanged.
Co-authored-by: Cursor <cursoragent@cursor.com>
Add Kimchi as a browser-token OAuth provider routed through its
OpenAI-compatible gateway. Discover live models for /v1/models and
provider models, normalize Claude-compatible requests, and wire up
provider connection tests.
Co-authored-by: Cursor <cursoragent@cursor.com>
CodeBuddy CN bonus packs ("Bonus Pack N") are one-shot credits whose
CycleEndTime equals DeductionEndTime — they expire for good and never
replenish. The dashboard rendered their resetAt as "Reset in Xd",
implying a monthly refill.
Tag bonus packs recurring:false (refill packs recurring:true) in the
usage handler, forward the flag through parseQuotaData, and word the
quota table / progress bar as "Expires in" / "Expires at" for
one-shot packs.
Carry Claude reasoning_effort/reasoning into OpenAI Chat, map into
OpenAI Responses reasoning.effort, and keep request-level effort
(incl. xhigh) across tool-result turns instead of collapsing to high.
Co-authored-by: Cursor <cursoragent@cursor.com>
Fetch the live model list from the Copilot /models endpoint and surface
it through /v1/models, replacing the hardcoded github entry so newly
shipped models appear without a code change. Catalog is cached per
credential and the Copilot token is refreshed on 401/403 before retry.
Also raise the connectivity-test budget to max_tokens:16, since Claude on
Copilot emits no choices at max_tokens:1 and produced a false negative.
Co-authored-by: Cursor <cursoragent@cursor.com>
Add image generation for Antigravity provider via gemini-3.1-flash-image
and gemini-3-pro-image, exposed through Text to Image UI and
/v1/images/generations.
- registry: serviceKinds ['llm','image'] + image model entries
- executor: image model detection + image_gen request envelope
- chatCore: force stream=false for image models (generateContent)
- nonStreamingHandler: parse inlineData -> markdown image
- imageGenerationCore: useExecutor fast-path for executor delegation
- imageProviders/antigravity: image adapter with image input support
- usage/google: image models in quota whitelist
Co-authored-by: Cursor <cursoragent@cursor.com>
Two additions on top of the merged CodeBuddy CN OAuth provider:
1. API key auth — connect with a direct API key (sent as
Authorization: Bearer), not only via OAuth device-code flow.
- registry: add authModes ["oauth","apikey"] + hasOAuth; combined Bearer
auth already forwards the key, token-refresh skips key connections.
- providers POST: accept dual-auth providers (authModes includes
"apikey") that live under category "oauth" — previously rejected as
"Invalid provider". Also fixes the same latent gap for xai.
2. Quota tracker — surface CodeBuddy CN credit balance on the usage
dashboard for both OAuth and API-key connections.
- registry: add transport.usage.url (Tencent billing endpoint) +
features.usage/usageApikey so the connection is quota-eligible.
- new CN-scoped handler services/usage/codebuddy-cn.js: POST the billing
meter endpoint, unwrap data.Response.Data.Accounts[]. The payload mixes
two credit types that must not be merged:
* refill/base ("基础体验包") — recurring allowance; cycle resets well
before the resource expires (CycleEndTime << DeductionEndTime).
Reads the *Cycle* balance, resetAt = next refresh. Cadence-labelled.
* bonus ("活动赠送包") — one-shot credits that expire at CycleEndTime.
Reads the plain Capacity balance. Labelled "Bonus Pack N".
One quota row per package, soonest-expiring first.
- register handler under "codebuddy-cn" in USAGE_HANDLERS.
Frontend needs no change — USAGE_SUPPORTED_PROVIDERS/USAGE_APIKEY_PROVIDERS
and the generic parseQuotaData branch already cover this shape.
Co-authored-by: Cursor <cursoragent@cursor.com>
flattenToolHistory only recognized OpenAI-style tool calls. Anthropic-compatible clients (Claude Code, /v1/messages) send tool invocations and results as tool_use/tool_result blocks inside the message content array. Since panel calls strip the tools definitions (#1859), the panel expert models received structured tool history without schemas, causing them to fail or misbehave (leading to empty responses and 503 errors).
Extend flattenToolHistory to recognize and flatten tool_use and tool_result blocks in the content array into prose text, keeping panel expert execution robust and independent of the client API format.
PR decolua/9router#1910
Co-authored-by: Cursor <cursoragent@cursor.com>
Build on the optional Headroom Token Saver from Carmelo Campos
(PR: feat: add optional Headroom token saver). Add managed start/stop
of the local headroom proxy from the dashboard, install detection,
status probing, and a simplified Token Saver UI.
- detect headroom CLI + python>=3.10, probe proxy /health
- spawn/stop proxy as a detached, pid-tracked process
- /api/headroom/{status,start,stop} routes, gated local-only in dashboardGuard
- one-click Start/Stop Headroom modal, no manual config needed
- claude<->openai shape conversion for /v1/compress via 9router translators
Thanks to Carmelo Campos (@carmelogunsroses) for the original Headroom integration.
Co-authored-by: Cursor <cursoragent@cursor.com>
Panel models in the fusion strategy must answer in prose. When the request
carried tools or prior tool_calls/tool messages, agentic panel models kept
emitting tool_calls instead of prose, so extractPanelText() returned empty
and the engine fell into the 503 "All fusion panel models failed" branch.
Panel fan-out now strips tools/tool_choice and flattens tool turns into
assistant prose (instead of dropping them), so panels keep the context but
cannot loop on tools. The judge still receives the unmodified history.
Co-authored-by: warelik <warelik@WARELIK-MB.local>
Co-authored-by: Cursor <cursoragent@cursor.com>
Adds Fusion as a third combo strategy alongside fallback/round-robin. A
fusion combo fans the prompt out to all member models in parallel, then a
configurable judge model synthesizes one final answer from the panel.
- handleFusionChat in open-sse/services/combo.js: quorum-grace collection
caps the straggler penalty, anonymized sources prevent judge brand-bias,
degrades to a direct answer on single survivor and 503 on total failure.
- chat.js dispatches strategy==="fusion" at both combo entry points.
- Combos dashboard: per-combo strategy Select replaces the round-robin
toggle, fusion reveals a judge picker, plus a strategy/capacity explainer.
- tests/unit/combo-fusion.test.js covers fan-out, judge routing/default,
quorum-grace straggler drop, single-survivor and total-failure degradation.
Co-authored-by: Cursor <cursoragent@cursor.com>
Adds long-lived API-key (ksk_) authentication for Kiro/AWS CodeWhisperer
and a direct claude:kiro / kiro:claude translation route that avoids the
lossy OpenAI two-hop pivot.
- translator: claude-to-kiro request + kiro-to-claude response translators,
registered on the exact source:target pair (direct route ahead of the
OpenAI pivot in index.js). claude-to-kiro uses shared schema constants
(ROLE/CLAUDE_BLOCK/DEFAULT_IMAGE_MIME) per app convention.
- auth: POST /api/oauth/kiro/api-key imports + validates a key via
ListAvailableProfiles, persists authMethod="api_key" (no refresh token).
- executor: send tokentype: API_KEY header and try *.amazonaws.com hosts
first for api-key creds; OAuth keeps kiro.dev first.
- fix: never inject the default placeholder profileArn for api-key auth
(CodeWhisperer 403s an ARN not owned by the key's account).
- ui: API Key method in the Kiro connect modal; surface api-key accounts
on the Quota Tracker and provider count.
- stream: env-overridable TTFT vs stall timeouts + Kiro keepalive frame.
- tests: claude-kiro-direct + kiro-profile-arn (11 tests).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>