chore(release): v2.9.4 — bug fixes (#491, #515, #517)

- fix(translator): preserve prompt_cache_key in Responses API translation (#517)
- fix(combo): escape \n in tagContent for valid JSON injection (#515)
- fix(usage): sync expired token status back to DB on live auth failure (#491)
- chore: bump version to 2.9.4 in package.json + docs/openapi.yaml
- docs: update CHANGELOG.md with v2.9.4 release notes
This commit is contained in:
diegosouzapw
2026-03-21 17:37:51 -03:00
parent d68c884649
commit 2f2d6b8535
5 changed files with 62 additions and 7 deletions

View File

@@ -4,6 +4,27 @@
---
## [2.9.4] — 2026-03-21
> Sprint: Bug fixes — preserve Codex prompt cache key, fix tagContent JSON escaping, sync expired token status to DB.
### 🐛 Bug Fixes
- **fix(translator)**: Preserve `prompt_cache_key` in Responses API → Chat Completions translation (#517)
— The field is a cache-affinity signal used by Codex; stripping it was preventing prompt cache hits.
Fixed in `openai-responses.ts` and `responsesApiHelper.ts`.
- **fix(combo)**: Escape `\n` in `tagContent` so injected JSON string is valid (#515)
— Template literal newlines (U+000A) are not allowed unescaped inside JSON string values.
Replaced with `\\n` literal sequences in `open-sse/services/combo.ts`.
- **fix(usage)**: Sync expired token status back to DB on live auth failure (#491)
— When the Limits & Quotas live check returns 401/403, the connection `testStatus` is now updated
to `"expired"` in the database so the Providers page reflects the same degraded state.
Fixed in `src/app/api/usage/[connectionId]/route.ts`.
---
## [2.9.3] — 2026-03-21
> Sprint: Add 5 new free AI providers — LongCat, Pollinations, Cloudflare AI, Scaleway, AI/ML API.

View File

@@ -1,7 +1,7 @@
openapi: 3.1.0
info:
title: OmniRoute API
version: 2.9.3
version: 2.9.4
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,

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "omniroute",
"version": "2.9.3",
"version": "2.9.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "omniroute",
"version": "2.9.3",
"version": "2.9.4",
"hasInstallScript": true,
"license": "MIT",
"workspaces": [

View File

@@ -1,6 +1,6 @@
{
"name": "omniroute",
"version": "2.9.3",
"version": "2.9.4",
"description": "Smart AI Router with auto fallback — route to FREE & cheap models, zero downtime. Works with Cursor, Cline, Claude Desktop, Codex, and any OpenAI-compatible tool.",
"type": "module",
"bin": {

View File

@@ -1,4 +1,8 @@
import { getProviderConnectionById, updateProviderConnection, resolveProxyForConnection } from "@/lib/localDb";
import {
getProviderConnectionById,
updateProviderConnection,
resolveProxyForConnection,
} from "@/lib/localDb";
import { getMachineId } from "@/shared/utils/machine";
import { getUsageForProvider } from "@omniroute/open-sse/services/usage.ts";
import { getExecutor } from "@omniroute/open-sse/executors/index.ts";
@@ -109,7 +113,10 @@ async function refreshAndUpdateCredentials(connection: any) {
/**
* GET /api/usage/[connectionId] - Get usage data for a specific connection
*/
export async function GET(request: Request, { params }: { params: Promise<{ connectionId: string }> }) {
export async function GET(
request: Request,
{ params }: { params: Promise<{ connectionId: string }> }
) {
try {
const { connectionId } = await params;
@@ -155,7 +162,34 @@ export async function GET(request: Request, { params }: { params: Promise<{ conn
// Populate quota cache for quota-aware account selection
if (isRecord(usage?.quotas)) {
setQuotaCache(connectionId, connection.provider, usage.quotas);
setQuotaCache(
connectionId,
connection.provider as string,
usage.quotas as Record<string, unknown>
);
}
// (#491) If the live usage check returned an auth error, sync the expired status
// back to the DB so the Providers page reflects the same degraded state as
// Limits & Quotas (which performs the live check).
const errorMessage = typeof usage?.message === "string" ? usage.message.toLowerCase() : "";
const isAuthError =
errorMessage.includes("token expired") ||
errorMessage.includes("access denied") ||
errorMessage.includes("re-authenticate") ||
errorMessage.includes("unauthorized");
if (isAuthError && connection.testStatus !== "expired") {
try {
await updateProviderConnection(connection.id as string, {
testStatus: "expired",
lastErrorType: "token_expired",
lastErrorAt: new Date().toISOString(),
});
} catch (dbErr) {
// Non-critical: log but don't block the response
console.error("[Usage API] Failed to sync expired status to DB:", dbErr);
}
}
return Response.json(usage);