CommandCode upstream only speaks NDJSON streaming; the executor always
wraps it as OpenAI SSE. Force body.stream=true so non-stream client
requests still produce a parseable upstream stream (router aggregates
SSE→JSON downstream).
Co-authored-by: Cursor <cursoragent@cursor.com>
Add shared OAuth credential lifecycle manager with provider-aware refresh
decisions. Implement CodexExecutor.refreshCredentials so 401/403 retry
refresh works for Codex, track lastRefreshAt and refresh before the
upstream stale-token window, preserve omitted idToken, and add
per-connection single-flight refresh to avoid refresh-token rotation races.
Merged from PR #1664.
Co-authored-by: Cursor <cursoragent@cursor.com>
- Add qmodel_latest to QODER_MODEL_MAP
- Expose qmodel_latest in static Qoder provider catalog (qd)
- Generalize executor comment so model set does not go stale
- Add unit coverage for the new model key + catalog
Closes#1638
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>
## Features
- Add Qoder provider: device-flow OAuth, COSY signing, WAF-bypass body encoding, live model catalog, dashboard quota tracker, 11 models (#1372)
- Add new models: Claude Opus 4.8 (Claude Code), GPT 5.4 Mini (Codex)
## Fixes
- DeepSeek thinking mode: echo `reasoning_content` back on follow-up/tool-call turns so OpenCode-free and custom providers no longer 400 with "reasoning_content must be passed back" (#1543)
- Reasoning injector: match deepseek/kimi model ids case-insensitively (covers custom providers using capitalized model names)
- OpenCode suggested-models: include free models without the `-free` suffix, e.g. `big-pickle` (#1535)
## Improvements
- Codex: trim sunset models, keep gpt-5.5 / gpt-5.4 / gpt-5.3-codex family, add gpt-5.4-mini
- volcengine-ark: refresh model list (add DeepSeek-V4-Flash/Pro, drop EOL entries)
- Lower stream stall timeout 35s → 30s for faster hang detection
Adds 18 new tests covering the bugs fixed in the previous commit so they
can't silently regress:
- parseExpiry (7 tests): numeric ms-epoch input, numeric strings handled
before Date.parse so "1700000000" doesn't get year-interpreted, RFC3339
strings, expires_in:0 honored as already-expired, 30-day fallback only
when both inputs are missing/invalid
- normalizeMessages (4 tests): system hoisting, multipart text flatten,
multiple system joining, empty input
- wrapQoderSSE (6 tests): the fixed cases — trailing partial line drained
in flush(), no chunks forwarded after [DONE], embedded newlines stripped
from inner body, error envelope produces error chunk + [DONE], non-ok
responses returned unchanged
- expose parseExpiry from auth.js, expose normalizeMessages/wrapQoderSSE
via __test__ from the executor (internals only — not part of the public
API). Marked with comment so the surface is intentional.
42 tests total (24 original + 18 new). Build still clean.
Correctness:
- testUtils: drop checkExpiry so the userinfo URL probe actually runs (revoked
tokens used to look "active" until local 30-day expiry passed)
- auth.parseExpiry: handle numeric expiresAt, swap parseInt before Date.parse
so "2026" doesn't get interpreted as year-2026, treat expires_in:0 as
already-expired instead of fabricating a 30-day default
- providers.mapTokens: synthesize email from userId when fetchUserInfo fails
so OAuth dedup works (re-logins no longer accumulate "Account N" rows)
SSE wrapper:
- wrapQoderSSE: add !doneEmitted guard on success branch (chunks could leak
past [DONE] when an error envelope shared a TCP packet with a valid one)
- flush(): finalize TextDecoder + drain trailing buffer so the chunk carrying
finish_reason is delivered when upstream closes without a final \n
- sanitize literal \n inside inner OpenAI body so SSE framing stays intact
Robustness:
- executor: wrap buildCosyHeaders in try/catch so a missing accessToken
returns 401 (re-auth) instead of bubbling as 500
- executor: short-circuit on missing accessToken before signing
- executor: plumb proxyOptions/signal through buildQoderRequestBody so
proxy-only networks can fetch the model_config catalog
- qoderModels: dedupe concurrent first-time misses with an in-flight Promise
map (parallel chat windows now do 1 upstream fetch instead of N)
- qoderModels: check signal.aborted before addEventListener so a pre-aborted
parent signal cancels the inner fetch immediately
- auth: AbortController + 15s timeout on pollDeviceToken / fetchUserInfo to
prevent hung sockets when openapi.qoder.sh stalls mid-response
UX:
- OAuthModal: derive polling deadline from device-code expires_in (qoder
publishes 300s; the previous fixed 120s caused timeouts when users took
more than 2 minutes on the consent page)
Cleanup:
- delete src/lib/oauth/services/qoder.js — referenced removed config fields
(clientId/clientSecret/tokenUrl/authorizeUrl) and was re-exported from
services/index.js, so any future caller would TypeError on first use
GitHub Copilot's /responses endpoint only serves OpenAI (gpt/codex)
models. gemini-3.1-pro-preview was failing on /chat/completions with a
"not supported" error, getting cached as a codex model, then escalated
to /responses where it 400s with "does not support Responses API".
Add GithubExecutor.supportsResponsesEndpoint() and gate both the cached
/responses route and the 400-fallback on it, so Gemini/Claude always
stay on /chat/completions and the real upstream error surfaces.
Adds tests/unit/github-responses-routing.test.js (5 tests).
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
When json_schema response_format is sent to models that do not natively
support Structured Output (e.g. DeepSeek/Ollama/local LLMs via
openai-compatible-* providers), they often return empty or malformed
content.
Detect response_format.type === "json_schema", inject the schema into the
system prompt, and downgrade response_format to json_object so Structured
Output works transparently across openai-compatible providers.
Gated to provider.startsWith("openai-compatible-") so providers with
native Structured Output support are not downgraded.
Co-authored-by: Cursor <cursoragent@cursor.com>
## Features
- Xiaomi MiMo Token Plan: region selector (Singapore / China / Europe) — keys are cluster-specific
- Antigravity: risk confirmation dialog before first connection
- Gemini CLI: surface upstream retry delay on 429 errors
## Fixes
- MITM: cannot kill process on macOS under sudo (lsof not found in PATH)
- Stream: false-positive stall timeout on Claude reasoning / Kiro responses
- Tunnel: cannot re-enable after disable (stuck state)
- Tunnel: cloudflared error messages now include log tail for easier debugging
- Language switcher: applies selected locale immediately on close (#1234)
- Antigravity OAuth: metadata now matches the official client
## Improvements
- Gemini CLI: bump engine to 0.34.0
- Re-hide `qwen` (OAuth EOL) and `iflow` (not ready) providers
- Introduced OllamaLocalExecutor to handle requests for the "ollama-local" provider.
- Removed the direct URL construction for "ollama-local" from BaseExecutor.
- Updated index.js to include the new OllamaLocalExecutor in the executors mapping.
- Enhanced the ProvidersPage component to support dynamic addition of OpenAI/Anthropic compatible providers.
- Refactored handleChatCore to include Caveman functionality, allowing for terse-style system prompts to reduce output token usage.
- Updated APIPageClient to manage Caveman settings, including enabling/disabling and selecting compression levels.
- Adjusted AntigravityExecutor to consolidate function declarations for compatibility with Gemini.
- Removed unnecessary console logs during translator initialization across multiple routes.
- Updated refreshCredentials methods in various executors (Antigravity, Base, Default, Github, Kiro) to accept optional proxyOptions for improved proxy handling.
- Modified token refresh logic to utilize proxy-aware fetch for better network management.
- Enhanced usage retrieval functions to support proxy options, ensuring seamless integration with proxy configurations.
- Updated ModelSelectModal and ProviderInfoCard components to incorporate kind filtering for improved user experience in model selection.
- Added validation for API keys in the provider validation route, including support for webSearch/webFetch providers.
- Introduced Cloudflare AI as a new provider with specific configurations in providerModels.js and providers.js.
- Updated DefaultExecutor to handle account ID resolution for Cloudflare AI connections.
- Enhanced AddApiKeyModal and EditConnectionModal to include account ID input for Cloudflare AI.
- Implemented validation for Cloudflare AI API key connections in testUtils.js and route.js.
- Updated UI components to reflect changes in provider management and connection handling.
- github.js: split thinking vs reasoning_effort stripping
- thinking (Claude-native format) still stripped for all Claude on Copilot
- reasoning_effort now passed through for Opus 4.6 and Sonnet 4.6
- still stripped for Haiku 4.5 and Opus 4.7 (rejected upstream)
- reasoning_effort "none" stripped for all models (not all support it)
- openai-to-claude.js: map reasoning_effort → thinking.budget_tokens
for direct Anthropic backend (none→skip, low→4096, medium→8192,
high→16384, xhigh→32768)
Previously reasoning_effort was stripped for ALL Claude models,
meaning Opus 4.6 via Copilot never received thinking configuration.
AI-generated commit by Claude Opus 4.6 (Anthropic)
- Introduced OpenCode Go provider with relevant configurations.
- Enhanced model management by allowing users to add and delete custom models.
- Updated UI components to support model selection for image types.
- Adjusted sidebar visibility to include image media kinds.
Add a shared resolveOllamaLocalHost() helper and wire it through the
executor, models/validate/test routes, so users can point ollama-local
at a remote Ollama instance instead of being locked to localhost:11434.
Also expose the host as an "Ollama Host URL" field in AddApiKeyModal
(empty = default localhost:11434), making the option reachable from the
dashboard without hand-editing db.json.
Co-authored-by: anuragg-saxenaa <anuragg.saxenaa@gmail.com>
Made-with: Cursor
The previous blanket strip in GithubExecutor.transformRequest removed
`thinking` AND `reasoning_effort` for every GitHub-routed model to avoid
Claude-on-Copilot 400s from OpenClaw. That regressed GPT-5 family support
(gh/gpt-5-mini honors reasoning_effort: low/medium/high).
Make supportsThinking(model) model-aware — return false only for Claude
models, so the strip fires only where the upstream actually rejects these
fields.
Benchmarks on /v1/chat/completions via GitHub Copilot:
effort=(none) → 64 reasoning_tokens, ~2.0s
effort=low → 0 reasoning_tokens, ~1.55s
effort=medium → 64 reasoning_tokens, ~1.9s
effort=high → 128 reasoning_tokens, ~2.2s
Made-with: Cursor
GitHub Copilot /chat/completions endpoint does not support the thinking
or reasoning_effort fields. OpenClaw sends thinking: { type: "enabled" }
for Claude models which causes a 400 Bad Request.
Added supportsThinking() and strip both fields in transformRequest before
sending to the upstream endpoint.
Co-authored-by: anuragg-saxenaa <anuragg.saxenaa@gmail.com>
Made-with: Cursor
Remote HTTP(S) image URLs are fetched and inlined as base64 data URIs
in a new prefetchImages() step run before super.execute(), so the body
sent to Codex contains resolved image bytes instead of URLs the backend
cannot access.
Scope is limited to the Codex executor — base executor and other
providers are untouched.
Co-authored-by: anuragg-saxenaa <anuragg.saxenaa@gmail.com>
Made-with: Cursor