Skip to content

Release v3.8.2#2503

Open
diegosouzapw wants to merge 31 commits into
mainfrom
release/v3.8.2
Open

Release v3.8.2#2503
diegosouzapw wants to merge 31 commits into
mainfrom
release/v3.8.2

Conversation

@diegosouzapw
Copy link
Copy Markdown
Owner

@diegosouzapw diegosouzapw commented May 21, 2026

Release v3.8.2

✨ New Features

  • feat(providers): add 7 free-tier providers (Wave 1) — Arcee AI, InclusionAI, Krutrim, Liquid AI, MonsterAPI, Nomic, and Poolside now available as new API-key providers with provider icons, model specs, and full routing support. (#2479)
  • feat(providers): add Astraflow provider support with global + China endpoints — new provider with dual-region base URLs for global and mainland China access. (#2486)
  • feat(providers): add claude-web provider — cookie-based Claude Web chat access without OAuth. (#2476)

🔧 Bug Fixes

  • fix(claude): extract system/developer role messages in Claude Code semantic passthrough paths — moves role:"system" / role:"developer" messages from the messages[] array to the top-level system parameter before sending to Anthropic. Fixes memory injection context being silently dropped. (#2497)
  • fix(vision-bridge): auto-route non-standard provider models through OmniRoute self-loop — vision-bridge now detects when a model doesn't natively support vision and automatically re-routes the image. (#2487)
  • fix(mitm): add IPv6 DNS redirect, modular antigravity target, improved logging. (#2514)
  • fix(usage): improve Claude and MiniMax plan label detection — better tier name resolution for Claude OAuth usage and new MiniMax plan label inference. (#2498)
  • fix(codex): fan out image n requests in parallel — significantly reducing total latency for multi-image generation. (#2499)
  • fix(embeddings): strip stale Content-Encoding headers from upstream response — prevents silent data corruption. (#2477)
  • fix(model): return clear error instead of silent OpenAI default for unrecognized models — now returns a 404 with a descriptive message. (#2492)
  • fix(dark-mode): correct background token on Compression Override select. (#2513)
  • fix(antigravity): align subscription tier detection with Antigravity Manager — correct nested field parsing + onboarding fallback. (#2496)
  • fix(opencode-zen): add opencode provider alias and sync model list with live API. (#2508)
  • fix(combo): clarify log message when combo target is skipped due to unavailable credentials. (#2494)
  • fix(security): replace Math.random with crypto.randomUUID in generateTaskId/ActivityId. (#2489)
  • fix(electron): downgrade to Electron 41.x for better-sqlite3 V8 compatibility.
  • fix(@omniroute/opencode-provider): include limit.context in model entries for OpenCode context window detection.
  • fix(providers): make gitlawb/gitlawb-gmi model entry optional. (#2476)
  • fix(translator): inject omniroute_web_search in the Responses-API flat tool shape. (#2390)
  • fix(kiro): serialize non-string role:"tool" message content before sending to CodeWhisperer. (#2446)
  • fix(claude): gate heavy-agent beta headers on Opus/Sonnet only + sanitize thinking block signatures. (#2454)
  • fix(perplexity-web): TLS impersonation to bypass Cloudflare on VPS. (#2459)
  • fix(validation): guard apiKey/modelsUrl against non-string values. (#2463)

📝 Maintenance

  • chore: remove Akamai VPS deploy from release workflow and skills.
  • chore: ignore .claude/worktrees from git tracking.

📊 Stats

Metric Count
Total commits 30
PRs merged 15
New providers 10
Bug fixes 20
Community contributors 10

🏆 Contributors Hall of Fame

A huge thank you to all the amazing contributors who made v3.8.2 possible! 🎉

Contributor Contributions
@herjarsa 5 PRs — vision-bridge self-loop (#2487), MITM IPv6 + antigravity target (#2514), unrecognized model error (#2492), combo skip log (#2494), opencode-zen alias (#2508)
@oyi77 2 PRs — 7 free-tier providers Wave 1 (#2479), claude-web provider + gitlawb optional (#2476)
@Gi99lin 2 PRs — Claude/MiniMax plan label detection (#2498), Antigravity subscription tier (#2496)
@unitythemaker 1 PR — system/developer role extraction in Claude passthrough (#2497)
@nmime 1 PR — parallel image generation for Codex (#2499)
@lordavadon2 1 PR — stale Content-Encoding header stripping (#2477)
@apoapostolov 1 PR — dark-mode Compression Override select fix (#2513)
@ucloudnb666 1 PR — Astraflow provider support (#2486)
@havockdev 2 fixes — Claude beta gating (#2454), Perplexity TLS bypass (#2459)
@ivan_yakimkin Antigravity tier detection refinements (#2496)

Total community PRs merged in v3.8.2: 15 — Thank you for making OmniRoute better! 🚀

diegosouzapw and others added 14 commits May 21, 2026 03:59
…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
…TaskId/ActivityId and fix URL hostname check in test (#2461) (#2489)

Co-authored-by: diegosouzapw <diego.souza.pw@gmail.com>
…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
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +560 to +563
const text = chunk.toString("utf8");
if (text.includes(eofSymbol)) {
const cutAt = text.indexOf(eofSymbol) + eofSymbol.length;
controller.enqueue(new Uint8Array(chunk.subarray(0, cutAt)));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

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;
            }

Comment on lines +19 to +20
import { join } from "node:path";
import { mkdtemp, open, unlink, rmdir, stat } from "node:fs/promises";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Add dirname and rm to the imports to support cross-platform path handling and use non-deprecated file system APIs.

Suggested change
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";

Comment on lines +463 to +464
const dir = path.substring(0, path.lastIndexOf("/"));
await rmdir(dir).catch(() => {});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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.

Suggested change
const dir = path.substring(0, path.lastIndexOf("/"));
await rmdir(dir).catch(() => {});
const dir = dirname(path);
await rm(dir, { recursive: true, force: true }).catch(() => {});

Comment on lines +587 to +588
const dir = path.substring(0, path.lastIndexOf("/"));
await rmdir(dir).catch(() => {});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Use dirname() for cross-platform compatibility and rm() instead of the deprecated rmdir().

Suggested change
const dir = path.substring(0, path.lastIndexOf("/"));
await rmdir(dir).catch(() => {});
const dir = dirname(path);
await rm(dir, { recursive: true, force: true }).catch(() => {});

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment on lines +402 to +404
const ready = await waitForContent(path, 5_000, requestPromise);
if (!ready) {
const r = await requestPromise.catch(
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Comment on lines +463 to +464
const dir = path.substring(0, path.lastIndexOf("/"));
await rmdir(dir).catch(() => {});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

Suggested change
async start(controller) {
async cancel() { aborted = true; },
async start(controller) {

@kilo-code-bot
Copy link
Copy Markdown

kilo-code-bot Bot commented May 21, 2026

Code Review Summary

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 1
Issue Details (click to expand)

SUGGESTION

File Line Issue
open-sse/services/perplexityTlsClient.ts 522 Missing cancel handler on ReadableStream — consumer cancellation (e.g., reader.cancel()) doesn't trigger immediate cleanup of file descriptor and temp directory. The start async function continues polling until the request completes or signal aborts, holding resources open longer than necessary.
Other Observations (not in diff)

Issues found in unchanged code that cannot receive inline comments:

File Line Issue
src/lib/providers/validation.ts 3029 Behavioral change: success condition narrowed from response.ok (any 2xx) to response.status === 200 (exactly 200). If the Perplexity validation endpoint ever returns 201/204, validation would incorrectly fail. Likely intentional for this specific endpoint, but worth verifying.
src/lib/providers/validation.ts 2991 UA changed to Firefox 148 (rv:148.0), but Firefox 148 does not exist yet (current is ~138). If this is a future-proof placeholder, add a comment explaining. If intended to match firefox_148 TLS profile, verify that tls-client-node's firefox_148 profile actually corresponds to Firefox 148 UA.
open-sse/services/perplexityTlsClient.ts 205 import { resolveProxyForRequest } placed mid-file (after interface definitions). ES module hoisting makes this technically correct, but unusual placement may confuse readers. Consider moving to top of file with other imports.
open-sse/services/perplexityTlsClient.ts 362 mkdtemp creates temp directory but no try/catch wraps the full streaming path. If client.request() throws synchronously (before returning a promise), the temp directory leaks. Low risk if client.request() always returns a Promise.
open-sse/executors/base.ts 718-722 For Haiku, context_management is deleted but the re-pair logic at line 735 only fires when tb.thinking is truthy. If tb.thinking was never set (undefined), context_management is NOT re-added — which may be correct but is subtle. The comment says "re-paired" which could be misleading.
Files Reviewed (13 files)
  • open-sse/services/perplexityTlsClient.ts - 1 SUGGESTION + 3 observations
  • open-sse/executors/perplexity-web.ts - 1 observation (UA version)
  • src/lib/providers/validation.ts - 1 observation (success condition)
  • open-sse/handlers/chatCore.ts - reviewed, no issues
  • open-sse/executors/claudeIdentity.ts - reviewed, no issues
  • open-sse/executors/base.ts - 1 observation (context_management re-pair)
  • open-sse/services/webSearchFallback.ts - reviewed, no issues
  • open-sse/translator/request/openai-to-kiro.ts - reviewed, no issues
  • src/lib/cloudAgent/baseAgent.ts - reviewed, no issues
  • open-sse/services/combo.ts - reviewed, no issues
  • .env.example - reviewed, no issues
  • tests/unit/perplexity-web.test.ts - reviewed, no issues
  • tests/unit/claude-beta-flags-2454.test.ts - reviewed, no issues

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>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 21, 2026

CI Coverage Report

  • Coverage job: cancelled
  • PR test policy: success

Coverage artifact was not available for this run.

Automation and others added 10 commits May 21, 2026 20:03
…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.
…nrecognized models (#2492)

Integrated into release/v3.8.2
herjarsa and others added 2 commits May 21, 2026 15:53
@kilo-code-bot
Copy link
Copy Markdown

kilo-code-bot Bot commented May 21, 2026

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.

@diegosouzapw diegosouzapw changed the title Release/v3.8.2 Release v3.8.2 May 21, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment on lines +299 to +303
const selfLoopApiKey = resolvedApiKey || "sk_omniroute";
const headers: Record<string, string> = {
"Content-Type": "application/json",
Authorization: `Bearer ${selfLoopApiKey}`,
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants