Add Self-hosted STT/TTS/Embedding providers that read baseUrl per connection
instead of a fixed registry endpoint, so 9Router can point at whisper.cpp,
faster-whisper, Kokoro-FastAPI, llama-server, vLLM, Infinity, and similar
OpenAI-compatible local servers.
Self-hosted Embedding refuses to run without a baseUrl rather than falling
back to api.openai.com like openaiCompatNode does, since that fallback would
silently send input text and the API key to OpenAI under a provider named
"Self-hosted". Also fixes embeddingsCore to catch adapter build errors as a
400 instead of letting them escape uncaught, and bounds the upstream fetch
with FETCH_CONNECT_TIMEOUT_MS to avoid hanging forever on a dead endpoint.
Self-hosted TTS treats a bare model value as the model rather than the voice,
since the generic OpenAI TTS convention (bare = voice) is backwards for a
provider where the model is the variable part.
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.
Adds mimo-v2.5-tts as a Media Provider TTS through the existing
OpenAI-compatible chat-completions endpoint. Voice is selected via the
top-level audio.voice field, and an optional style/language hint is
threaded through tts.js -> ttsCore.js -> the new adapter.
Add "ultra" reasoning level for Codex GPT-5.6 Sol and Terra, and expose
Max for Luna (Luna falls back Ultra to Max since it is not supported
upstream). Scoped to cx/ routes only; Kiro and generic OpenAI routing
unchanged.
handleForcedSSEToJson dropped cached prompt tokens in two ways: the
Responses branch summed only input_tokens, which excludes cache_read
and cache_creation on cache-capable upstreams (measured 2012 reported
vs ~5344 actual, 5332 from cache); and the Chat Completions branch
computed usage correctly but it didn't always reach the client (an
Anthropic response with cache_read_input_tokens: 11022 arrived with no
usage field at all). Now folds cache counters into prompt_tokens,
surfaces them via prompt_tokens_details, and re-attaches usage before
serialisation.
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>
Rotating-RT providers (xAI/grok-cli) issue a new refresh_token on every
refresh; mutate credentials in-place so refreshWithRetry reuses the fresh
RT instead of the already-consumed one.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Clear stale provider error code and account lock after a successful web
fetch (the core fetch handler never consumed the onRequestSuccess
callback), switch Jina Reader to its documented JSON POST request, and
parse the Title: metadata line before falling back to a Markdown heading.
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.
Validate AWS EventStream framing, header bounds, CRCs, error frames,
and terminal stop metadata before exposing Kiro output. Classify stop
reasons into dispositions (complete / retryable / terminal_incomplete /
refusal) and retry once when the stream ends with a malformed tool call,
ellipsis-only output, or a short future-action sentence.
Fail closed: propagate streaming failures as error SSE (502) instead of
collapsing them into a successful stop, so incomplete responses no longer
leak as final answers.
Detect the observed evidence-prefixed trailing progress final without
broadening the Chinese heuristic to completed findings.
Add pxpipe as an experimental fifth Token Saver: Claude-format request
bodies above a configurable size threshold are rendered as dense PNGs
via the pxpipe-proxy library API (transformAnthropicMessages) before
dispatch, cutting estimated input tokens by ~35-60% on token-dense
contexts. Integration follows the Headroom pattern: applied to the final
body in chatCore just before dispatch, fail-open on any error/timeout.
Managed npm install into DATA_DIR/pxpipe, dynamic loader with per-version
cache-bust, JSONL event log with rotation, /api/pxpipe/* endpoints, Token
Saver card (marked experimental) + /dashboard/pxpipe page, and per-request
Activated/Skipped annotation in Request Details. Disabled by default.
Normalize every provider to one cache-inclusive convention via
canonicalizeUsage() before persist, and price cached + cache_creation as
subsets of prompt_tokens in calculateCostFromTokens() to stop
double-counting. usageRepo now delegates cost math to a single source.
Surface Cached tokens/cost across dashboard (overview, tokens, cost,
details). Merge Claude message_start cache with message_delta output so
cache counts survive. Compatible LLM nodes now allow multiple API-key
connections (key pool).
Co-authored-by: Cursor <cursoragent@cursor.com>
handleStreamingResponse and buildOnStreamComplete each generated their
own streamDetailId for what should be one logical record — the
placeholder row (0 tokens) and the final row (real usage) never shared
an id, so the DB's ON CONFLICT(id) upsert never merged them, leaving a
permanent 0-token stub for every streaming request.
Share the id from buildOnStreamComplete with handleStreamingResponse
so both writes hit the same row.
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>
Keep provider-required streaming when client prefers JSON. The
Accept: application/json branch no longer flips stream back to false
for forceStream providers, fixing 400 errors on stream-only providers
(e.g. Command Code) for Hermes / Claude Code / other JSON clients.
Fixes#2031
Co-authored-by: Cursor <cursoragent@cursor.com>
Resolve Gemini TTS models from shared TTS catalog and provider registry
with a safe fallback, fixing requests resolving to models/undefined when
ttsConfig.models is empty. Add gemini-3.1-flash-tts-preview to catalogs.
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>
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>
Some providers (e.g. xiaomi-tokenplan -claude models) return OpenAI-format
responses even when request was translated to Claude. Early-return now detects
choices[]. Also strip reasoning_content only when content is non-empty so
thinking models keep their only output.
Closes#1836
Co-authored-by: Cursor <cursoragent@cursor.com>
Gom block SSE headers + CORS lặp ở streamingHandler + responsesHandler
vào sseConstants.SSE_HEADERS_CORS. Codex format-routing giữ nguyên
(logic-driven theo kim chỉ nam DATA/LOGIC docs 07).
Co-authored-by: Cursor <cursoragent@cursor.com>
Add mimo-v2.5-pro-claude alias routing to the Xiaomi TokenPlan Anthropic-compatible
/anthropic/v1/messages endpoint. Logic lives in a dedicated XiaomiTokenplanExecutor
(config-driven via targetFormat) instead of the shared DefaultExecutor.
Co-authored-by: Cursor <cursoragent@cursor.com>