Release v3.8.2#2503
Conversation
…2390) The omniroute_web_search fallback tool was always built in Chat Completions nested shape ({type, function:{name}}). On the Responses->Responses passthrough path nothing flattens it, so Codex/relay upstreams rejected it with 'Missing required parameter: tools[0].name'. buildFallbackTool and the tool_choice injection now emit the flat Responses-API shape ({type, name}) when the target provider speaks the Responses API.
…2446) An OpenAI-style role:"tool" message carrying structured/array content was collapsing to content:[{ text: "" }], which CodeWhisperer rejects with 400 'Improperly formed request'. Reuse serializeToolResultContent (already used by the Anthropic tool_result path) so structured output is never empty.
#2454) selectBetaFlags now gates the heavy-agent betas (context-1m, effort, advanced-tool-use) on Opus/Sonnet only; Haiku with OAuth was rejecting context-1m with 400 'incompatible with the long context beta header'. base.ts stops deleting Haiku's thinking config (real Claude Desktop keeps it). chatCore passthrough converts historical thinking/redacted_thinking blocks to redacted_thinking with a synthetic signature, fixing 400 'Invalid signature in thinking block' on mid-session model switches. Co-authored analysis by havockdev.
…2459) New perplexityTlsClient.ts (Firefox-148 TLS profile, mirrors chatgptTlsClient) routes perplexity-web requests so Cloudflare stops 403-challenging datacenter IPs. Executor and connection validator now distinguish a Cloudflare block from an invalid session cookie. Adds OMNIROUTE_PPLX_TLS_TIMEOUT_MS / OMNIROUTE_PPLX_TLS_GRACE_MS. Co-authored analysis by havockdev.
…ries for OpenCode context window detection OpenCode determines model context windows by reading limit.context from opencode.json model entries. The provider was not emitting this field, so all OmniRoute models appeared with an unknown (0) context window in OpenCode, preventing proper compaction and overflow detection. - Add limit.context to OpenCodeModelEntry interface - Add OMNIROUTE_DEFAULT_MODEL_CONTEXT_LENGTHS map (200K Claude / 1M Gemini) - Include limit.context when generating model entries - Extend fetchLiveModels to capture context_length from /v1/models - 5 new tests covering context length coverage, JSON serialisation, unknown model fallback, and live model fetch Closes #2481
…#2463) A corrupted or mis-typed credential (non-string apiKey, or a non-string modelsUrl from providerSpecificData/registry) could throw 'TypeError: ... is not a function' when validation called .startsWith()/.trim() during a provider connection test. Adds typeof guards in validateOpenAILikeProvider, validateGeminiLikeProvider and validateSnowflakeProvider so validation returns a clean { valid } result instead of crashing. Does not pinpoint the NVIDIA NIM e.startsWith report (needs a stack trace), but hardens the whole class.
…window fix(@omniroute/opencode-provider): include limit.context in model entries for OpenCode context window detection
…navailable credentials The combo loop log messages misleadingly said '(all accounts in cooldown)' when the actual reason could be model exclusion, rate-limiting, or other credential unavailability. Updated to accurately describe the real reason.
…Manager Extract paid/current/restricted tiers from loadCodeAssist (shared module), fix invalid LINUX metadata on Docker, refresh tier on quota update without re-auth, and persist tier fields back to connections. Co-authored-by: Cursor <cursoragent@cursor.com>
… cache Simplify onboard tier ID fallback and reuse subscription lookup in error path. Co-authored-by: Cursor <cursoragent@cursor.com>
fix(combo): clarify log message when combo target is skipped due to unavailable credentials
There was a problem hiding this comment.
Code Review
This pull request implements a browser-impersonating TLS client for Perplexity to bypass Cloudflare challenges and introduces several critical bug fixes across the translator, Claude identity, and provider validation modules. Key improvements include gating heavy-agent beta headers for Claude Haiku, sanitizing thinking-block signatures during model switches, and ensuring correct tool shapes for the Responses API. Additionally, the opencode-provider now supports model context window limits. The review feedback identifies a potential UTF-8 safety issue in the streaming logic of the new TLS client and recommends using non-deprecated, cross-platform file system APIs for directory management.
| const text = chunk.toString("utf8"); | ||
| if (text.includes(eofSymbol)) { | ||
| const cutAt = text.indexOf(eofSymbol) + eofSymbol.length; | ||
| controller.enqueue(new Uint8Array(chunk.subarray(0, cutAt))); |
There was a problem hiding this comment.
Calculating cutAt using string indices from chunk.toString() and applying it to chunk.subarray() is unsafe for UTF-8 content, as character indices do not necessarily match byte offsets (e.g., for multi-byte characters). Use chunk.indexOf(eofSymbol) to find the correct byte position directly.
const cutAt = chunk.indexOf(eofSymbol);
if (cutAt !== -1) {
controller.enqueue(new Uint8Array(chunk.subarray(0, cutAt + eofSymbol.length)));
break;
}| import { join } from "node:path"; | ||
| import { mkdtemp, open, unlink, rmdir, stat } from "node:fs/promises"; |
There was a problem hiding this comment.
Add dirname and rm to the imports to support cross-platform path handling and use non-deprecated file system APIs.
| import { join } from "node:path"; | |
| import { mkdtemp, open, unlink, rmdir, stat } from "node:fs/promises"; | |
| import { join, dirname } from "node:path"; | |
| import { mkdtemp, open, unlink, rm, stat } from "node:fs/promises"; |
| const dir = path.substring(0, path.lastIndexOf("/")); | ||
| await rmdir(dir).catch(() => {}); |
There was a problem hiding this comment.
Using path.lastIndexOf("/") is fragile as it fails on platforms using different path separators (like Windows). Use dirname() for portability. Also, rmdir() is deprecated; rm() is preferred for directory removal.
| const dir = path.substring(0, path.lastIndexOf("/")); | |
| await rmdir(dir).catch(() => {}); | |
| const dir = dirname(path); | |
| await rm(dir, { recursive: true, force: true }).catch(() => {}); |
| const dir = path.substring(0, path.lastIndexOf("/")); | ||
| await rmdir(dir).catch(() => {}); |
There was a problem hiding this comment.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3e84b81f6f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const ready = await waitForContent(path, 5_000, requestPromise); | ||
| if (!ready) { | ||
| const r = await requestPromise.catch( |
There was a problem hiding this comment.
Remove fixed 5s first-byte cutoff for SSE startup
The streaming path hard-fails when the first SSE bytes take more than 5 seconds: waitForContent(path, 5_000, requestPromise) returns false, then this function falls back to a buffered response (body: null), and PerplexityWebExecutor later converts that into a 502 "empty response body". Any valid Perplexity request with slow time-to-first-token (>5s due to upstream latency/load) will be reported as an upstream failure even though the request eventually succeeds.
Useful? React with 👍 / 👎.
| const dir = path.substring(0, path.lastIndexOf("/")); | ||
| await rmdir(dir).catch(() => {}); |
There was a problem hiding this comment.
Use platform-safe directory extraction for temp cleanup
Temp cleanup derives the parent directory with path.substring(0, path.lastIndexOf("/")), which is POSIX-specific. On Windows, temp paths use \, so lastIndexOf("/") is -1 and cleanup targets an empty/non-parent path; this leaks temp files/directories for each streamed request. Please use a path-aware helper (e.g., dirname) instead of manual slash parsing.
Useful? React with 👍 / 👎.
| signal: AbortSignal | null = null | ||
| ): ReadableStream<Uint8Array> { | ||
| return new ReadableStream<Uint8Array>({ | ||
| async start(controller) { |
There was a problem hiding this comment.
SUGGESTION: Missing cancel handler on the ReadableStream. If the consumer cancels the stream (e.g., reader.cancel() or the downstream ReadableStream from buildStreamingResponse is cancelled before the signal aborts), the start async function continues polling until aborted or finished becomes true — holding the file descriptor and temp directory open longer than necessary.
Add a cancel method that sets aborted = true so cleanup runs immediately on consumer cancellation:
| async start(controller) { | |
| async cancel() { aborted = true; }, | |
| async start(controller) { |
Code Review SummaryStatus: 1 Issue Found | Recommendation: Address before merge Overview
Issue Details (click to expand)SUGGESTION
Other Observations (not in diff)Issues found in unchanged code that cannot receive inline comments:
Files Reviewed (13 files)
Fix these issues in Kilo Cloud Reviewed by qwen3.6-plus · 1,780,372 tokens |
Prefer persisted tier when live subscription maps to an unknown label, and only return mapped tier IDs from extractCodeAssistTierId. Add regression test for fallback from providerSpecificData. Co-authored-by: Cursor <cursoragent@cursor.com>
CI Coverage Report
Coverage artifact was not available for this run. |
…with live API OpenCode's Zen provider changed its slug from 'opencode-zen' to 'opencode', breaking OmniRoute's provider resolution when users reference models with the new prefix (e.g. 'opencode/deepseek-v4-flash-free'). Changes: 1. open-sse/services/model.ts: Add manual ALIAS_TO_PROVIDER_ID entry mapping 'opencode' → 'opencode-zen' so parseModel() resolves correctly for model strings using the new slug. 2. open-sse/executors/index.ts: Register 'opencode' as an OpencodeExecutor alias for 'opencode-zen' so getExecutor() returns the correct executor. 3. open-sse/config/providerRegistry.ts: Update opencode-zen model list to match the live API at https://opencode.ai/zen/v1/models: - Add deepseek-v4-flash-free (the model users reported as broken) - Add all 30+ models from the API (Claude, GPT, Gemini, Grok, GLM, MiniMax, Kimi, Qwen series) - Apply targetFormat: 'claude' to qwen3.5-plus (same SSE bug as qwen3.6) - Remove ling-2.6-1t-free and trinity-large-preview-free (no longer in API) - Enable passthroughModels so new models work without code deploys 4. @omniroute/opencode-provider/src/index.ts: Remove broken reference to undefined OMNIROUTE_DEFAULT_MODEL_CONTEXT_LENGTHS constant. 5. tests/unit/opencode-executor.test.ts: Add tests for opencode alias, deepseek-v4-flash-free routing, and model registry presence.
…ct (#2513) Integrated into release/v3.8.2
…nrecognized models (#2492) Integrated into release/v3.8.2
…esponse (#2477) Integrated into release/v3.8.2
…rough paths (#2497) Integrated into release/v3.8.2
Integrated into release/v3.8.2
Integrated into release/v3.8.2
…d logging (#2514) Integrated into release/v3.8.2
…2476) Integrated into release/v3.8.2
Integrated into release/v3.8.2
…mniRoute self-loop (#2487) Integrated into release/v3.8.2
Integrated into release/v3.8.2
|
Kilo Code Review could not run — your account is out of credits. Add credits or switch to a free model to enable reviews on this change. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2fc8f9e9b9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const selfLoopApiKey = resolvedApiKey || "sk_omniroute"; | ||
| const headers: Record<string, string> = { | ||
| "Content-Type": "application/json", | ||
| Authorization: `Bearer ${selfLoopApiKey}`, | ||
| }; |
There was a problem hiding this comment.
Use router credential on vision self-loop requests
When resolveVisionBridgeBaseUrl(config.model) selects the OmniRoute self-loop (http://localhost:...) for non-OpenAI providers, this code still reuses resolvedApiKey (typically OPENAI_API_KEY/provider key) as the Authorization bearer. In deployments with REQUIRE_API_KEY=true, that key is not a valid OmniRoute management/client key, so the internal /v1/chat/completions call is rejected with 401 and vision-bridge fails for the new self-loop path. The self-loop branch should send an OmniRoute-valid key (or explicitly bypass local auth), not an upstream provider key.
Useful? React with 👍 / 👎.
| export const ANTIGRAVITY_MODEL_ALIASES = Object.freeze({ | ||
| "gemini-3-pro-preview": "gemini-3.1-pro-high", | ||
| "gemini-3.5-flash-preview": "gemini-3-flash-agent", | ||
| "gemini-3.5-flash-preview": "gemini-3.5-flash-high", |
There was a problem hiding this comment.
Keep preview alias on a declared Antigravity model id
This remaps gemini-3.5-flash-preview to gemini-3.5-flash-high, but that target id is not declared in ANTIGRAVITY_PUBLIC_MODELS and is only referenced by this alias pair. Since resolveAntigravityModelId() applies this mapping on request routing, preview calls are rewritten to an otherwise unadvertised id and can fail upstream (e.g., model-not-found) instead of using the known working gemini-3-flash-agent path that the rest of this module still references.
Useful? React with 👍 / 👎.
Release v3.8.2
✨ New Features
claude-webprovider — cookie-based Claude Web chat access without OAuth. (#2476)🔧 Bug Fixes
role:"system"/role:"developer"messages from themessages[]array to the top-levelsystemparameter before sending to Anthropic. Fixes memory injection context being silently dropped. (#2497)nrequests in parallel — significantly reducing total latency for multi-image generation. (#2499)Content-Encodingheaders from upstream response — prevents silent data corruption. (#2477)opencodeprovider alias and sync model list with live API. (#2508)Math.randomwithcrypto.randomUUIDingenerateTaskId/ActivityId. (#2489)limit.contextin model entries for OpenCode context window detection.gitlawb/gitlawb-gmimodel entry optional. (#2476)omniroute_web_searchin the Responses-API flat tool shape. (#2390)role:"tool"message content before sending to CodeWhisperer. (#2446)apiKey/modelsUrlagainst non-string values. (#2463)📝 Maintenance
.claude/worktreesfrom git tracking.📊 Stats
🏆 Contributors Hall of Fame
A huge thank you to all the amazing contributors who made v3.8.2 possible! 🎉