diff --git a/CHANGELOG.md b/CHANGELOG.md index 1819716964..66197d84f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,25 @@ --- +## [3.6.9] — 2026-04-18 + +### ✨ New Features + +- **feat(core):** Implement persistent API key, backup pruning, and GPU optimization (#1350, #1367, #1369) + +### 🐛 Bug Fixes + +- **fix(providers):** Resolve Claude passthrough corruption (#1359), Kimi-k2 reasoning header rejections (#1360), thinking parameter leaks (#1361), and Ollama proxy redirect drops (#1381) +- **fix(core):** Proxy lookup in key validation effectively respects the new ProxyRegistry environments (#1384) +- **fix(electron):** Resolve type error in Header electronAPI properties +- **fix(security):** Resolve CodeQL security alerts including safe prototype bindings (#151, #152, #154, #155-159) + +### 🛠️ Maintenance + +- **chore:** Sync infrastructure with docker postinstall components and secondary CodeQL analysis rules + +--- + ## [3.6.8] — 2026-04-17 ### ✨ New Features diff --git a/docs/openapi.yaml b/docs/openapi.yaml index 941e8a4eba..ac43b5671a 100644 --- a/docs/openapi.yaml +++ b/docs/openapi.yaml @@ -1,7 +1,7 @@ openapi: 3.1.0 info: title: OmniRoute API - version: 3.6.8 + version: 3.6.9 description: | OmniRoute is a local-first AI API proxy router. It provides an OpenAI-compatible endpoint that routes requests to multiple AI providers with load balancing, diff --git a/electron/package.json b/electron/package.json index f37885dae6..22a1a2b76c 100644 --- a/electron/package.json +++ b/electron/package.json @@ -1,6 +1,6 @@ { "name": "omniroute-desktop", - "version": "3.6.8", + "version": "3.6.9", "description": "OmniRoute Desktop Application", "main": "main.js", "author": { diff --git a/llm.txt b/llm.txt index b14bae387e..b0fd1e1f66 100644 --- a/llm.txt +++ b/llm.txt @@ -8,7 +8,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo **Key value:** One endpoint (`http://localhost:20128/v1`), unlimited models, zero downtime, minimal cost. -**Current version:** 3.6.8 +**Current version:** 3.6.9 ## Tech Stack @@ -279,7 +279,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo └── .env.example # Environment variable template ``` -## Key Features (v3.6.8) +## Key Features (v3.6.9) ### Core Proxy - **60+ AI providers** with automatic format translation diff --git a/open-sse/package.json b/open-sse/package.json index 9fbfe86a8f..fae8803126 100644 --- a/open-sse/package.json +++ b/open-sse/package.json @@ -1,6 +1,6 @@ { "name": "@omniroute/open-sse", - "version": "3.6.8", + "version": "3.6.9", "description": "Express SSE sidecar for OmniRoute — handles streaming, protocol translation, and provider orchestration", "type": "module", "main": "index.js", diff --git a/package-lock.json b/package-lock.json index 11d32a09f0..bb28534564 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20962,7 +20962,7 @@ }, "open-sse": { "name": "@omniroute/open-sse", - "version": "3.6.8" + "version": "3.6.9" } } } diff --git a/src/app/api/providers/validate/route.ts b/src/app/api/providers/validate/route.ts index 41e12e7f7c..41f198d992 100644 --- a/src/app/api/providers/validate/route.ts +++ b/src/app/api/providers/validate/route.ts @@ -7,7 +7,7 @@ import { isAnthropicCompatibleProvider, } from "@/shared/constants/providers"; import { validateProviderApiKey } from "@/lib/providers/validation"; -import { getProxyForLevel } from "@/lib/localDb"; +import { getProxyForLevel, resolveProxyForProvider } from "@/lib/localDb"; import { validateProviderApiKeySchema } from "@/shared/validation/schemas"; import { isValidationFailure, validateBody } from "@/shared/validation/helpers"; import { runWithProxyContext } from "@omniroute/open-sse/utils/proxyFetch.ts"; @@ -87,10 +87,16 @@ export async function POST(request) { }; } - const providerProxy = await getProxyForLevel("provider", provider); - const globalProxy = providerProxy ? null : await getProxyForLevel("global"); + const registryProxy = await resolveProxyForProvider(provider); + let proxyToUse = registryProxy; - const result = await runWithProxyContext(providerProxy || globalProxy || null, () => + if (!proxyToUse) { + const providerProxy = await getProxyForLevel("provider", provider); + const globalProxy = providerProxy ? null : await getProxyForLevel("global"); + proxyToUse = providerProxy || globalProxy || null; + } + + const result = await runWithProxyContext(proxyToUse || null, () => validateProviderApiKey({ provider, apiKey, diff --git a/tests/unit/chatcore-translation-paths.test.ts b/tests/unit/chatcore-translation-paths.test.ts index 5f60ff4acb..40b6a4e5b1 100644 --- a/tests/unit/chatcore-translation-paths.test.ts +++ b/tests/unit/chatcore-translation-paths.test.ts @@ -581,12 +581,12 @@ test("chatCore auto cache policy becomes false for nondeterministic combos", asy responseFormat: "claude", }); - assert.equal(call.body.system[0].text.includes("You are Claude Code"), true); + assert.equal(call.body.system[0].text, "system"); + // Cache markers are removed by removeCacheControlFromClaudePayload for nondeterministic combos assert.equal( - call.body.system.some((block) => block.cache_control?.ttl === "5m"), + call.body.system.some((block) => !!block.cache_control), false ); - assert.equal(call.body.system.at(-1).cache_control?.ttl, "1h"); }); test("chatCore always-preserve mode keeps cache_control even without Claude Code user-agent", async () => { @@ -635,9 +635,10 @@ test("chatCore disables raw Claude passthrough when cache preservation is off an responseFormat: "claude", }); - assert.equal(call.body.system[0].text.includes("You are Claude Code"), true); - assert.equal(call.body.system.at(-1).cache_control?.ttl, "1h"); + assert.equal(call.body.system[0].text, "system"); + // Cache preservation is off, so cache markers are stripped assert.equal(call.body.messages[0].content[0].cache_control, undefined); + // Tools disable flag is applied assert.equal("_disableToolPrefix" in call.body, false); });