Do not collapse consecutive underscores in uniqueName so mcp__server__tool is sent intact to Kiro, attach reverse map on request translation, and restore client tool names in responses.
Replace the literal 'continue' placeholder on tool-result-only user turns with 'Tool results provided.' to prevent models from treating it as a new user instruction.
Forward images inside tool_result to OpenAI and Kiro upstreams via following user messages, restore original client tool names on Kiro responses via _toolNameMap, and preserve thinking display settings across translations.
Command Code dropped vision and ignored client effort through the router:
image blocks became "[image omitted]", HTTP image URLs were never inlined,
and reasoning_effort landed on the envelope wrapper instead of params (so the
DeepSeek family mapping remapped low -> high). The catalog also treated
deepseek/deepseek-v4.1-flash as text-only, so the vision adapter stole those
requests to another provider.
- Map OpenAI image_url / Claude image blocks (base64 or data-URI) to the
native {type:"image", image:"data:...;base64,...", mimeType} generate block.
- Add FORMATS.COMMANDCODE to TARGETS_NEED_BASE64 so remote http(s) images are
inlined by the existing SSRF-safe fetcher before translation.
- Write reasoning_effort inside params for targetFormat commandcode and pass
low|medium|high|xhigh|max through unmapped; allow it in thinkingLevels.
- Provider-scoped capabilities for commandcode/cmc: vision except the CLI
text-only denylist, thinkingFormat commandcode, so family patterns
(deepseek-v4 -> thinkingFormat deepseek, vision false) no longer win.
- Quota Tracker: whoami + billing credits/subscriptions (credits vs plan cap,
5h and weekly windows), labels from AI_PROVIDERS[].name.
Adds the Desktop-exclusive Preview models and the Xiaomi account-session
route to the existing xiaomi-mimo provider instead of a separate
xiaomi-desktop provider, so the dashboard shows one MiMo entry rather than
three overlapping ones.
Dual auth, same pattern as kimi — API key (sk-) covers the cloud API,
Desktop/OAuth adds the account session used by the Preview models:
- registry: category oauth, authModes [oauth, apikey], oauth block, the two
mimo-x-*-preview models, and the invite signupUrl
- executor: routes Preview models to the account-service route with a Cookie
session, everything else keeps the sourceFormat-matched transport
- oauth: custom ECDH encrypted-callback flow (X25519 -> SHA256 -> AES-256-GCM)
with a loopback callback proxy, plus one-click import of the local Desktop
auth.json
- usage: weekly quota from the account session
Fixes found while merging:
- the OAuth browser flow was dead: poll-status cleared the session before the
client could POST /exchange, so every exchange returned 400
- a Claude-format client was sent to /v1/chat/completions instead of the
declared /anthropic/v1/messages transport, because buildUrl ignored
runtimeTransport
- stopXiaomiMimoProxy leaked every pending session (each holding an X25519
private key) for the process lifetime
- the OAuth exchange did not persist the Desktop passToken, so the Preview
models could never work after a browser sign-in
Removes dead code: the local engine token minting (mimoEngine, never called
on the request path), the model-catalog and usage routes, engineToken/
engineUrl plumbing, and an unread top-level usage block.
Adds tests/unit/xiaomi-mimo-{executor,oauth-session,oauth-proxy}.test.js —
the provider previously had none.
The merged Qoder work also rewrote shared translator/handler code so that
/v1/responses clients got token usage on response.completed. That changed
behaviour for every provider, not just Qoder: proxies saw input tokens
rise by the 2000-token context buffer, and the plain token mapping was
replaced by one that always adds input_tokens_details.
A probe confirms the Qoder benefit does not depend on those edits: the
executor's coalescer already emits one include_usage-style finish chunk, so
a Claude client receives input_tokens and cache_read_input_tokens with
every shared file at its original state. Only the Responses path relies on
the shared translator, and that path has no Qoder-owned seam to put it in.
Reverts the shared files to their pre-PR state and drops the Responses
usage test. The Cline envelope unwrap in nonStreamingHandler.js, which
landed after the PR in the same file, is kept.
defaultClaudeToolType() stamped tools[].type = "custom" onto every
Claude-format request carrying tools since e08ac6da. That satisfied
MiniMax (error 2013) but broke Anthropic-compatible endpoints that only
accept the legacy typeless tool shape. DeepSeek's endpoint
(api.deepseek.com/anthropic/v1/messages) whitelists its tool `type` enum
to the web_search_* variants and answers HTTP 400 "unknown variant
`custom`", so every Claude Code request routed to a DeepSeek connection
failed and surfaced as a persistent 503.
Run the defaulting only when the target provider declares the new
requireClaudeToolType quirk (MiniMax, MiniMax-CN). Add
shouldDefaultClaudeToolType(provider, finalFormat, tools, PROVIDERS) in
translator/concerns/toolCall.js so the gate is unit-testable, and cover
MiniMax keeping the explicit type, DeepSeek/Anthropic staying typeless,
non-Claude formats and tool-less requests never defaulting.
Effectively a no-op for MiniMax and a restore of the pre-e08ac6da
behaviour everywhere else. Another strict gateway now only needs the same
one-line quirk instead of a global behavioural change.
Anthropic accepts at most 4 blocks carrying cache_control per request. When the
client had already spent that budget, the re-anchor added a 5th marker and the
request was rejected with a non-retryable 400 that the failure path treated as
an account problem, retrying the same malformed body across the whole pool until
every account locked.
anchorClaudeCache now normalizes bare-object content, strips the invalid
cache_control carried by defer_loading tools, pins the 1h head anchors on the
last system block and last cacheable tool, then trims an over-budget body to 4
markers. The trim holds those head anchors and fills the remaining slots with the
tail-most message markers: a plain "keep the last four in document order" rule
drops the anchors first even though they lead document order, and skipping the
re-anchor at a spent budget left system/tools on the 5m default instead of 1h.
Some clients send content as a single block object rather than a one-element
array. Such a turn was dropped or zeroed on every leg that reads messages,
silently losing conversation history. normalizeMessageContent wraps it as a
one-block array on all four paths, and hasValidContent keeps it.
DeepSeek's Anthropic-compatible endpoint accepts only the built-in
web_search_20250305 / web_search_20260209 tools and rejects client-defined
`custom` tools (MCP / Read / Bash) with HTTP 400 "unknown variant `custom`".
The generic non-Claude filter in prepareClaudeRequest dropped the offending
tools but also dropped the web_search_* ones DeepSeek does accept.
- Add an opt-in per-provider transport quirk `claudeSupportedToolTypes`; when
declared it becomes a strict allow-list for Anthropic tool `type` values
- Stop stripping the `type` discriminator from surviving tools under that
quirk, since DeepSeek needs it to route built-ins
- Declare the quirk on the deepseek transport with the two web_search_* types
- Providers without the quirk keep the previous filter and normalisation
behaviour byte-for-byte; openai-format targets never reach this path
kiro.dev rejects any body carrying a top-level systemPrompt with
400 REQUEST_BODY_INVALID. The translators stopped emitting the field in
v0.5.59 (the prompt travels in the first user turn via contentPrefix),
but two paths kept writing it back downstream of the translator:
- rtk/systemInject.js::injectKiroSystem() appended the RTK prompt to
body.systemPrompt, so every kr/ model failed whenever an RTK injector
(caveman, ponytail) was active. It now appends to the first history
user turn's content (else currentMessage), reusing
dedupStringAppend/hasPrompt so retries stay idempotent.
- executors/kiro.js::appendRepairInstruction() wrote the tool-call repair
instruction to systemPrompt on the retry, turning every repair into a
hard failure. It now appends to currentMessage.userInputMessage.content.
isKiroBody() no longer requires a string body.systemPrompt — that marker
is gone from the wire shape — and sniffs the conversation turn shape
instead, keeping the stray-conversationState guard intact. Stale comments
in both kiro translators corrected: the systemPrompt local is only a
session-replay cache key, not a wire field.
Also drops the mirror/rollback repair heuristic the injector no longer
needs: net -52 lines.
Fixes#3641, #3845, #2890, #2901, #2939, #3109, #3459, #3749
- Coalesce Qoder's empty finish-in-delta frame with the later choices:[] usage
frame so OpenAI and Claude clients receive prompt_tokens, completion_tokens
and cache-hit tokens (the dashboard already saw them)
- Upload inlined images through /api/v2/image/upload like qodercli, and stub
oversized non-image files instead of stuffing 30MB+ data URIs into
agent_chat_generation
- Emit response.completed -> response.usage for chat-native upstreams so
/v1/responses clients (Codex CLI, sub2api) no longer log 0/0/0
- Keep Claude message_delta.usage working when usage arrives without choices[0]
- Escalate to the smallest advertised Qoder context tier (200K/400K/1M) when
the estimated prompt no longer fits max_input_tokens
- Pass apiKey for PAT connections and list hidden enable:false catalog keys
from /v1/models
- Add muse-spark-1.2-contributor as responses-only model on OpenCode Go
- Normalize object tool schemas without properties in OpenCode Go executor
- Make fallback Responses call_ids unique across same-millisecond calls
- Make Responses output coercion fail-soft for circular and non-stringifiable values
- Add muse-spark-1.3-contributor as responses-only model on OpenCode Go with dedicated executor
- Key Responses→chat streaming tool calls by item_id to prevent parallel tool calls merging into index 0
- Standardize tool coercions and call_id clamping in Responses API translation
Claude adaptive requests without an explicit effort are normalized to
output_config.effort: "high" instead of forwarding the unsupported
literal value "auto" which Anthropic rejects with HTTP 400.
- add claude-fable-5-1 to the Claude Code model catalog (1M context,
permanent adaptive thinking)
- centralize the spoofed Claude Code version and update both request
and billing identities to 2.1.257 (Fable 5.1 rejects < 2.1.251)
- send output_config.effort without the redundant thinking switch for
permanently adaptive models
- add regression coverage for capabilities, headers, billing identity
and adaptive-effort payload
# Conflicts:
# open-sse/providers/registry/claude.js
# open-sse/providers/shared.js
# open-sse/utils/claudeCloaking.js
# tests/__baseline__/providers-baseline.json
Anthropic validates server_tool_use.id against ^srvtoolu_[a-zA-Z0-9_]+$
and 400s the whole request when one does not match. A combo that falls
back to a provider with its own built-in tools (z.ai/glm emits
OpenAI-style call_ ids for analyze_image) leaves such blocks in the
history, so every later Claude turn fails.
Extend normalizeClaudePassthrough to drop those blocks (reusing the
existing loop), drop the paired tool_result / web_search_tool_result
referencing a dropped id, and drop empty text blocks plus messages left
with no content. Well-formed srvtoolu_ blocks and regular tool_use ids
are untouched.
translateResponse() short-circuited untouched on claude->claude streaming,
so OAuth-cloaked tool names (CLAUDE_TOOL_SUFFIX) leaked to the client and
every tool call was rejected as unknown. Add decloakStreamChunk(), the
streaming counterpart of decloakToolNames(), and call it on the same-format
path using the already-plumbed state.toolNameMap.
Z.ai / GLM-5.2+ require a top-level reasoning_effort (low/high/max)
alongside thinking:{type:"enabled"} to control reasoning depth; the zai
branch previously only set thinking and dropped reasoning_effort, so every
GLM-5.x request ran at the model default (max). Gate the field behind
GLM-5.2+ (thinkingEffortSupported in capabilities.js) since older GLM
(4.x, 5.0, 5.1, 5-turbo, 5v-turbo) do not read it, and map client levels
to the exact low/high/max values z.ai accepts.
extractThinking now checks reasoning_effort/reasoning.effort before the
thinking object so a client-supplied effort is not overwritten by
thinking:{type:"enabled"} mapping to mode:auto.
Fixes#2721
muse-spark-1.2-contributor-free returned HTTP 500 on /zen/v1/chat/completions.
The model is only served by /zen/v1/responses, so route it there via a per-model
targetFormat and normalize the Chat fields the Responses API rejects
(max_tokens -> max_output_tokens, reasoning_effort -> reasoning{effort,summary}),
clamping max/ultra down to the highest effort the model accepts (xhigh).
Routing stays per-model: the other free models (big-pickle, hy3-free, mimo,
nemotron, laguna) are not served by /responses and keep /chat/completions.
Strict Anthropic-compatible gateways (e.g. MiniMax) reject Claude-format
requests with HTTP 400 when tools[].type is missing. Normalize each
missing/falsy tools[].type to "custom" before dispatch when the final
request format is Claude. Built-in tool types (computer_use, bash,
web_search_*) are passed through untouched.
Passthrough kept the client's own cache_control markers, which point at
pre-normalization offsets. Once normalize/dedupe reshaped system and tools,
the breakpoints landed mid-array and the tail was re-cached every request.
- Pin the last system block and last tool at ttl 1h (was the client's 5m)
- Anchor the last assistant turn at 5m, falling back to the final message
so a first turn still gets a breakpoint
- Fold mid-conversation system messages into the neighbouring user turn
instead of hoisting them into body.system, where the volatile token
counters invalidated the prefix on every request
- Run the anchoring after every token saver, at the final body
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
Some providers (e.g. codebuddy/cbcn) attach an empty tool_calls array to every streaming chunk. An empty array is truthy in JS, so the guard 'if (delta.tool_calls)' closed the message on the first content token and emitted response.output_text.done early, dropping the remaining deltas. Guard on a non-empty array; finish_reason still closes the message and real tool calls still close it before emitting function_call items.
fixes#3234
Adds exact per-model rates for 110 TokenRouter models (pulled from
TokenRouter's own pricing API) plus a dedicated thinkingFormat case
(reasoning_effort enum low/medium/high/xhigh/max) and the provider
logo. Provider registration itself already landed in a prior commit;
this fills in what PR #3043 added on top.
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.
hasValidContent() only treated text/tool_use/tool_result blocks as valid
content, so a user message containing only an image block was filtered
out as empty. When it was the only non-system message, this left an
empty messages array and Anthropic rejected the request.
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.
Tool schemas carrying uniqueItems, contains, multipleOf,
unevaluatedProperties, unevaluatedItems, or contentSchema get rejected
by the Gemini API with "Unknown name ...: Cannot find field", failing
the whole request. Add them to UNSUPPORTED_SCHEMA_CONSTRAINTS alongside
the existing stripped keywords (minItems, maxItems, format, ...).
Vertex rejects orphan {} left when $ref/$defs are removed from function declarations. Promote empty nodes to object+reason placeholder in addPlaceholders.
Strip the generic dashboard model(level) suffix before resolving Kiro
synthetic -thinking/-agentic variants so the upstream request no longer
carries an invalid parenthesized model id. Map explicit levels to native
Kiro effort fields only for supported Claude/GPT model families, and stop
advertising native levels for unsupported legacy Kiro models. Applies to
both OpenAI→Kiro and direct Claude→Kiro routes.
Forward the service_tier field from OpenAI requests into the Responses API payload so clients can select priority/default/flex tiers instead of the field being silently dropped.
Route GPT-5.6 reasoning effort through Kiro's native reasoning.effort field instead of the legacy Claude output_config.effort path. GPT-5.6 models now emit reasoning.effort for low/medium/high/xhigh, with max mapped to the xhigh wire value.
Preserve the Responses API reasoning.effort through the OpenAI intermediate by copying it to reasoning_effort before the field is dropped. Skip legacy thinking_mode prompt tags when a supported native GPT effort is emitted, while keeping the legacy fallback for unsupported values (auto/minimal/ultra) and explicit disable semantics (none/off/disabled). Claude adaptive effort continues to use thinking plus output_config.effort.
Reshape Kiro direct requests so resumed client sessions reuse Kiro's
cache-affinity fields instead of starting unrelated CodeWhisperer
conversations.
- keep conversationState.conversationId stable when the client sends an
explicit session id (x-session-id, session_id, conversation_id, Claude
Code session metadata)
- add a stable conversationState.agentContinuationId per Kiro session
- send conversationState.agentTaskType: "vibe" and agentMode: "vibe",
matching the normal Kiro CLI/KAS chat path
- move Kiro thinking instructions into Kiro-compatible systemPrompt /
additionalModelRequestFields instead of generic top-level thinking
- keep volatile timestamp context out of the top-level systemPrompt; it
remains only in user content fallback
- suppress additionalModelRequestFields for legacy 4.5-era Claude/Kiro
models that reject it, while defaulting future Claude/Kiro model ids
to supported
- preserve Kiro meteringEvent credit usage internally for accounting
without leaking provider-specific fields into OpenAI-compatible usage
- prevent unrelated headerless Kiro requests from sharing one
connection-wide continuation
- cap/evict continuation sessions so long-running processes do not grow
the continuation map unbounded
- treat generated headerless Kiro sessions as one-shot so they do not
evict real explicit-session continuations
- keep credit-only Kiro metering valid for internal persistence when
token metrics are unavailable
client_metadata is an OpenAI Responses API-specific field. When translating
openai-responses requests to openai (Chat Completions), it leaked through
to providers like NVIDIA, which rejected it with a 400 "Unsupported
parameter". Strip it in the Responses-specific cleanup block alongside
input, instructions, store, and reasoning.
Broaden strip rule from /claude-opus-4/i to /claude/i so temperature is
removed for every Claude model, not just opus-4. Fixes Anthropic 400 on
OpenAI-compatible routes. #1748
Gemini CLI requests with small max_tokens spend the whole output budget on
thoughts after reasoning_effort maps to thinkingConfig, returning blank
content or finish=length. Raise maxOutputTokens floors per thinking level/
budget (clamped to caps.maxOutput). Also emit toolConfig
functionCallingConfig.mode=VALIDATED for Gemini CLI tool requests to avoid
MALFORMED_FUNCTION_CALL.
Co-authored-by: Cursor <cursoragent@cursor.com>