Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b62e6c5a69 | |||
| 92e29a6ad7 |
@@ -4,6 +4,30 @@
|
||||
|
||||
---
|
||||
|
||||
## [2.8.5] — 2026-03-19
|
||||
|
||||
> Sprint: Fix zombie SSE streams, context cache first-turn, KIRO MITM, and triage 5 external issues.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **Zombie SSE Streams** (#473): Reduce `STREAM_IDLE_TIMEOUT_MS` from 300s → 120s for faster combo fallback when providers hang mid-stream. Configurable via env var.
|
||||
- **Context Cache Tag** (#474): Fix `injectModelTag()` to handle first-turn requests (no assistant messages) — context cache protection now works from the very first response.
|
||||
- **KIRO MITM** (#481): Change KIRO `configType` from `guide` → `mitm` so the dashboard renders MITM Start/Stop controls.
|
||||
- **E2E Test** (CI): Fix `providers-bailian-coding-plan.spec.ts` — dismiss pre-existing modal overlay before clicking Add API Key button.
|
||||
|
||||
### Closed Issues
|
||||
|
||||
- #473 — Zombie SSE streams bypass combo fallback
|
||||
- #474 — Context cache `<omniModel>` tag missing on first turn
|
||||
- #481 — MITM for KIRO not activatable from dashboard
|
||||
- #468 — Gemini CLI remote server (superseded by #462 deprecation)
|
||||
- #438 — Claude unable to write files (external CLI issue)
|
||||
- #439 — AppImage doesn't work (documented libfuse2 workaround)
|
||||
- #402 — ARM64 DMG "damaged" (documented xattr -cr workaround)
|
||||
- #460 — CLI not runnable on Windows (documented PATH fix)
|
||||
|
||||
---
|
||||
|
||||
## [2.8.4] — 2026-03-19
|
||||
|
||||
> Sprint: Gemini CLI deprecation, VM guide i18n fix, dependabot security fix, provider schema expansion.
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
openapi: 3.1.0
|
||||
info:
|
||||
title: OmniRoute API
|
||||
version: 2.8.4
|
||||
version: 2.8.5
|
||||
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,9 +4,9 @@ import { loadProviderCredentials } from "./credentialLoader.ts";
|
||||
export const FETCH_TIMEOUT_MS = parseInt(process.env.FETCH_TIMEOUT_MS || "120000", 10);
|
||||
|
||||
// Idle timeout for SSE streams (ms). Closes stream if no data for this duration.
|
||||
// Default: 300s to support extended-thinking models (claude-opus-4-6, o3, etc.)
|
||||
// that may pause for >60s during deep reasoning phases. Override with STREAM_IDLE_TIMEOUT_MS env var.
|
||||
export const STREAM_IDLE_TIMEOUT_MS = parseInt(process.env.STREAM_IDLE_TIMEOUT_MS || "300000", 10);
|
||||
// Default: 120s balances deep-reasoning pauses with fast zombie stream detection (#473).
|
||||
// Extended-thinking models rarely pause >90s between chunks. Override with STREAM_IDLE_TIMEOUT_MS env var.
|
||||
export const STREAM_IDLE_TIMEOUT_MS = parseInt(process.env.STREAM_IDLE_TIMEOUT_MS || "120000", 10);
|
||||
|
||||
// Provider configurations
|
||||
// OAuth credentials read from env vars with hardcoded fallbacks for backward compatibility.
|
||||
|
||||
@@ -52,7 +52,15 @@ export function injectModelTag(messages: Message[], providerModel: string): Mess
|
||||
|
||||
// Find last assistant message with string content
|
||||
const lastAssistantIdx = cleaned.map((m) => m.role).lastIndexOf("assistant");
|
||||
if (lastAssistantIdx === -1) return cleaned;
|
||||
|
||||
// #474: If no assistant message exists yet (first turn), append a synthetic one
|
||||
// so the tag is present when the client sends the next request with the response.
|
||||
if (lastAssistantIdx === -1) {
|
||||
return [
|
||||
...cleaned,
|
||||
{ role: "assistant", content: `\n<omniModel>${providerModel}</omniModel>` },
|
||||
];
|
||||
}
|
||||
|
||||
const msg = cleaned[lastAssistantIdx];
|
||||
if (typeof msg.content !== "string") return cleaned;
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "omniroute",
|
||||
"version": "2.8.4",
|
||||
"version": "2.8.5",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "omniroute",
|
||||
"version": "2.8.4",
|
||||
"version": "2.8.5",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "omniroute",
|
||||
"version": "2.8.4",
|
||||
"version": "2.8.5",
|
||||
"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": {
|
||||
|
||||
@@ -193,8 +193,8 @@ export const CLI_TOOLS = {
|
||||
image: "/providers/kiro.png",
|
||||
icon: "psychology_alt",
|
||||
color: "#FF6B35",
|
||||
description: "Amazon Kiro — AI-powered IDE",
|
||||
configType: "guide",
|
||||
description: "Amazon Kiro — AI-powered IDE with MITM",
|
||||
configType: "mitm",
|
||||
guideSteps: [
|
||||
{ step: 1, title: "Open Kiro Settings", desc: "Go to Settings → AI Provider" },
|
||||
{ step: 2, title: "Base URL", value: "{{baseUrl}}", copyable: true },
|
||||
|
||||
@@ -63,6 +63,13 @@ test.describe("Bailian Coding Plan Provider", () => {
|
||||
const redirectedToLogin = page.url().includes("/login");
|
||||
test.skip(redirectedToLogin, "Authentication enabled without a login fixture.");
|
||||
|
||||
// Dismiss any pre-existing dialog/overlay that may appear on page load
|
||||
const preExistingDialog = page.getByRole("dialog").first();
|
||||
if (await preExistingDialog.isVisible({ timeout: 2000 }).catch(() => false)) {
|
||||
await page.keyboard.press("Escape");
|
||||
await preExistingDialog.waitFor({ state: "hidden", timeout: 3000 }).catch(() => {});
|
||||
}
|
||||
|
||||
const addKeyButton = page.getByRole("button", {
|
||||
name: /add.*api.*key|add.*key|add.*connection|connect/i,
|
||||
});
|
||||
@@ -175,6 +182,13 @@ test.describe("Bailian Coding Plan Provider", () => {
|
||||
const redirectedToLogin = page.url().includes("/login");
|
||||
test.skip(redirectedToLogin, "Authentication enabled without a login fixture.");
|
||||
|
||||
// Dismiss any pre-existing dialog/overlay that may appear on page load
|
||||
const preExistingDialog = page.getByRole("dialog").first();
|
||||
if (await preExistingDialog.isVisible({ timeout: 2000 }).catch(() => false)) {
|
||||
await page.keyboard.press("Escape");
|
||||
await preExistingDialog.waitFor({ state: "hidden", timeout: 3000 }).catch(() => {});
|
||||
}
|
||||
|
||||
const addKeyButton = page.getByRole("button", {
|
||||
name: /add.*api.*key|add.*key|add.*connection|connect/i,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user