diff --git a/CHANGELOG.md b/CHANGELOG.md index ac154c6157e1..c081a84c1aa7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_Cycle open — entries are added here as PRs merge into `release/v3.8.27`._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/ar/CHANGELOG.md b/docs/i18n/ar/CHANGELOG.md index 5b8b543c624a..b8f80c7b87bd 100644 --- a/docs/i18n/ar/CHANGELOG.md +++ b/docs/i18n/ar/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/az/CHANGELOG.md b/docs/i18n/az/CHANGELOG.md index 5ba821284458..d6be17945052 100644 --- a/docs/i18n/az/CHANGELOG.md +++ b/docs/i18n/az/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/bg/CHANGELOG.md b/docs/i18n/bg/CHANGELOG.md index 5ba821284458..d6be17945052 100644 --- a/docs/i18n/bg/CHANGELOG.md +++ b/docs/i18n/bg/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/bn/CHANGELOG.md b/docs/i18n/bn/CHANGELOG.md index 0db73346c0a2..56cdbfeeb1c7 100644 --- a/docs/i18n/bn/CHANGELOG.md +++ b/docs/i18n/bn/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/cs/CHANGELOG.md b/docs/i18n/cs/CHANGELOG.md index 6e6ecbf61fd8..3339eed465f0 100644 --- a/docs/i18n/cs/CHANGELOG.md +++ b/docs/i18n/cs/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/da/CHANGELOG.md b/docs/i18n/da/CHANGELOG.md index 5aae04c2b382..1992c27ae690 100644 --- a/docs/i18n/da/CHANGELOG.md +++ b/docs/i18n/da/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/de/CHANGELOG.md b/docs/i18n/de/CHANGELOG.md index 60453894769d..6d80745373ec 100644 --- a/docs/i18n/de/CHANGELOG.md +++ b/docs/i18n/de/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/es/CHANGELOG.md b/docs/i18n/es/CHANGELOG.md index e4b50140a346..2fe24aea91fc 100644 --- a/docs/i18n/es/CHANGELOG.md +++ b/docs/i18n/es/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/fa/CHANGELOG.md b/docs/i18n/fa/CHANGELOG.md index c9b7f1c1eb6b..2201c0fcff8c 100644 --- a/docs/i18n/fa/CHANGELOG.md +++ b/docs/i18n/fa/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/fi/CHANGELOG.md b/docs/i18n/fi/CHANGELOG.md index e67c3b82d7eb..a90e48f993aa 100644 --- a/docs/i18n/fi/CHANGELOG.md +++ b/docs/i18n/fi/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/fr/CHANGELOG.md b/docs/i18n/fr/CHANGELOG.md index a11461a7dddd..04622e09c3a2 100644 --- a/docs/i18n/fr/CHANGELOG.md +++ b/docs/i18n/fr/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/gu/CHANGELOG.md b/docs/i18n/gu/CHANGELOG.md index b7e03639099d..3e9105e1b12b 100644 --- a/docs/i18n/gu/CHANGELOG.md +++ b/docs/i18n/gu/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/he/CHANGELOG.md b/docs/i18n/he/CHANGELOG.md index df506e0a21e7..27ae953f9e25 100644 --- a/docs/i18n/he/CHANGELOG.md +++ b/docs/i18n/he/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/hi/CHANGELOG.md b/docs/i18n/hi/CHANGELOG.md index 3b9c5a200167..845eaad63563 100644 --- a/docs/i18n/hi/CHANGELOG.md +++ b/docs/i18n/hi/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/hu/CHANGELOG.md b/docs/i18n/hu/CHANGELOG.md index b24157d80f42..20edec49b94f 100644 --- a/docs/i18n/hu/CHANGELOG.md +++ b/docs/i18n/hu/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/id/CHANGELOG.md b/docs/i18n/id/CHANGELOG.md index 7420d2f9943b..f9563b3291c1 100644 --- a/docs/i18n/id/CHANGELOG.md +++ b/docs/i18n/id/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/in/CHANGELOG.md b/docs/i18n/in/CHANGELOG.md index 99e1a40dd693..43a8bc58a306 100644 --- a/docs/i18n/in/CHANGELOG.md +++ b/docs/i18n/in/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/it/CHANGELOG.md b/docs/i18n/it/CHANGELOG.md index d052bb3d0425..4c2951387c84 100644 --- a/docs/i18n/it/CHANGELOG.md +++ b/docs/i18n/it/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/ja/CHANGELOG.md b/docs/i18n/ja/CHANGELOG.md index aec4d34bc2c3..9db0991052bc 100644 --- a/docs/i18n/ja/CHANGELOG.md +++ b/docs/i18n/ja/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/ko/CHANGELOG.md b/docs/i18n/ko/CHANGELOG.md index 07081ef476b1..6306a6e9069f 100644 --- a/docs/i18n/ko/CHANGELOG.md +++ b/docs/i18n/ko/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/mr/CHANGELOG.md b/docs/i18n/mr/CHANGELOG.md index 375a9004c9d7..92fd0993dfac 100644 --- a/docs/i18n/mr/CHANGELOG.md +++ b/docs/i18n/mr/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/ms/CHANGELOG.md b/docs/i18n/ms/CHANGELOG.md index 764739a7a1a7..4c58d57105da 100644 --- a/docs/i18n/ms/CHANGELOG.md +++ b/docs/i18n/ms/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/nl/CHANGELOG.md b/docs/i18n/nl/CHANGELOG.md index cdad222e1b75..39cfce681d21 100644 --- a/docs/i18n/nl/CHANGELOG.md +++ b/docs/i18n/nl/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/no/CHANGELOG.md b/docs/i18n/no/CHANGELOG.md index 540758225ab7..defa53e1c1f9 100644 --- a/docs/i18n/no/CHANGELOG.md +++ b/docs/i18n/no/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/phi/CHANGELOG.md b/docs/i18n/phi/CHANGELOG.md index 934b4ab5aefd..1ae7e4d50057 100644 --- a/docs/i18n/phi/CHANGELOG.md +++ b/docs/i18n/phi/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/pl/CHANGELOG.md b/docs/i18n/pl/CHANGELOG.md index 842fdf33eea6..91f1405df8ea 100644 --- a/docs/i18n/pl/CHANGELOG.md +++ b/docs/i18n/pl/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/pt-BR/CHANGELOG.md b/docs/i18n/pt-BR/CHANGELOG.md index c11b13850cf9..1116092d952a 100644 --- a/docs/i18n/pt-BR/CHANGELOG.md +++ b/docs/i18n/pt-BR/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/pt/CHANGELOG.md b/docs/i18n/pt/CHANGELOG.md index 5c16ce49b4e0..c7798402d380 100644 --- a/docs/i18n/pt/CHANGELOG.md +++ b/docs/i18n/pt/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/ro/CHANGELOG.md b/docs/i18n/ro/CHANGELOG.md index 543e96200293..fac0658ed6ef 100644 --- a/docs/i18n/ro/CHANGELOG.md +++ b/docs/i18n/ro/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/ru/CHANGELOG.md b/docs/i18n/ru/CHANGELOG.md index bfbf32e71837..ee01642c283a 100644 --- a/docs/i18n/ru/CHANGELOG.md +++ b/docs/i18n/ru/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/sk/CHANGELOG.md b/docs/i18n/sk/CHANGELOG.md index d5993cd760bd..f04e81154f62 100644 --- a/docs/i18n/sk/CHANGELOG.md +++ b/docs/i18n/sk/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/sv/CHANGELOG.md b/docs/i18n/sv/CHANGELOG.md index a91421234ad2..5c56f70ac53b 100644 --- a/docs/i18n/sv/CHANGELOG.md +++ b/docs/i18n/sv/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/sw/CHANGELOG.md b/docs/i18n/sw/CHANGELOG.md index 9df3b002c3b4..7608d3452d8d 100644 --- a/docs/i18n/sw/CHANGELOG.md +++ b/docs/i18n/sw/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/ta/CHANGELOG.md b/docs/i18n/ta/CHANGELOG.md index cfb4e9273a1c..96964f36efee 100644 --- a/docs/i18n/ta/CHANGELOG.md +++ b/docs/i18n/ta/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/te/CHANGELOG.md b/docs/i18n/te/CHANGELOG.md index 99d60804ef16..585db1d9cd0f 100644 --- a/docs/i18n/te/CHANGELOG.md +++ b/docs/i18n/te/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/th/CHANGELOG.md b/docs/i18n/th/CHANGELOG.md index e9d4d7ad2fd3..4df1694ec86e 100644 --- a/docs/i18n/th/CHANGELOG.md +++ b/docs/i18n/th/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/tr/CHANGELOG.md b/docs/i18n/tr/CHANGELOG.md index 573c4e94de6d..4cf98b63ddbd 100644 --- a/docs/i18n/tr/CHANGELOG.md +++ b/docs/i18n/tr/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/uk-UA/CHANGELOG.md b/docs/i18n/uk-UA/CHANGELOG.md index 687e84c30ad3..c06d2634d2fe 100644 --- a/docs/i18n/uk-UA/CHANGELOG.md +++ b/docs/i18n/uk-UA/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/ur/CHANGELOG.md b/docs/i18n/ur/CHANGELOG.md index 81878e2c0b78..cd075238609d 100644 --- a/docs/i18n/ur/CHANGELOG.md +++ b/docs/i18n/ur/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/vi/CHANGELOG.md b/docs/i18n/vi/CHANGELOG.md index 7845c11a6f07..2ebe300a2ccc 100644 --- a/docs/i18n/vi/CHANGELOG.md +++ b/docs/i18n/vi/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/i18n/zh-CN/CHANGELOG.md b/docs/i18n/zh-CN/CHANGELOG.md index 9da0b2dda0e1..cc15d7b0ea82 100644 --- a/docs/i18n/zh-CN/CHANGELOG.md +++ b/docs/i18n/zh-CN/CHANGELOG.md @@ -4,6 +4,12 @@ --- +## [3.8.27] — TBD + +_See English CHANGELOG for v3.8.27 details._ + +--- + ## [3.8.26] — 2026-06-15 ### ✨ New Features diff --git a/docs/reference/openapi.yaml b/docs/reference/openapi.yaml index 1ae4e9ab05f1..06161d5e5a62 100644 --- a/docs/reference/openapi.yaml +++ b/docs/reference/openapi.yaml @@ -1,7 +1,7 @@ openapi: 3.1.0 info: title: OmniRoute API - version: 3.8.26 + version: 3.8.27 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-lock.json b/electron/package-lock.json index a42c1dbfc888..641e835c4d74 100644 --- a/electron/package-lock.json +++ b/electron/package-lock.json @@ -1,12 +1,12 @@ { "name": "omniroute-desktop", - "version": "3.8.26", + "version": "3.8.27", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "omniroute-desktop", - "version": "3.8.26", + "version": "3.8.27", "license": "MIT", "dependencies": { "electron-updater": "^6.8.9" diff --git a/electron/package.json b/electron/package.json index 2f07ac06cde5..52919d9ab191 100644 --- a/electron/package.json +++ b/electron/package.json @@ -1,6 +1,6 @@ { "name": "omniroute-desktop", - "version": "3.8.26", + "version": "3.8.27", "description": "OmniRoute Desktop Application", "main": "main.js", "author": { diff --git a/open-sse/package.json b/open-sse/package.json index 22fc9005deef..3a3ecc763adb 100644 --- a/open-sse/package.json +++ b/open-sse/package.json @@ -1,6 +1,6 @@ { "name": "@omniroute/open-sse", - "version": "3.8.26", + "version": "3.8.27", "description": "Express SSE sidecar for OmniRoute — handles streaming, protocol translation, and provider orchestration", "type": "module", "main": "index.js", diff --git a/open-sse/utils/stream/claudeLifecycle.ts b/open-sse/utils/stream/claudeLifecycle.ts new file mode 100644 index 000000000000..277977017b85 --- /dev/null +++ b/open-sse/utils/stream/claudeLifecycle.ts @@ -0,0 +1,182 @@ +import { JsonRecord } from "./types.ts"; + +export type ClaudeEmptyResponseLifecycle = { + hasMessageStart: boolean; + hasContentBlock: boolean; + hasMessageDelta: boolean; + hasMessageStop: boolean; + hasError: boolean; + syntheticContentInjected: boolean; + warningLogged: boolean; +}; + +export const SYNTHETIC_CLAUDE_EMPTY_RESPONSE_TEXT = ""; + +export function createClaudeEmptyResponseLifecycle(): ClaudeEmptyResponseLifecycle { + return { + hasMessageStart: false, + hasContentBlock: false, + hasMessageDelta: false, + hasMessageStop: false, + hasError: false, + syntheticContentInjected: false, + warningLogged: false, + }; +} + +export function getClaudeEventType(payload: unknown): string | null { + if (!payload || typeof payload !== "object") return null; + const type = (payload as JsonRecord).type; + return typeof type === "string" ? type : null; +} + +export function isClaudeEventPayload(payload: unknown): payload is JsonRecord { + return getClaudeEventType(payload) !== null; +} + +export function updateClaudeEmptyResponseLifecycle( + lifecycle: ClaudeEmptyResponseLifecycle, + payload: unknown +) { + const type = getClaudeEventType(payload); + if (!type) return; + + switch (type) { + case "message_start": + lifecycle.hasMessageStart = true; + break; + case "content_block_start": + case "content_block_delta": + case "content_block_stop": + lifecycle.hasContentBlock = true; + break; + case "message_delta": + lifecycle.hasMessageDelta = true; + break; + case "message_stop": + lifecycle.hasMessageStop = true; + break; + case "error": + lifecycle.hasError = true; + break; + default: + break; + } +} + +export function hasClaudeAssistantLifecycle(lifecycle: ClaudeEmptyResponseLifecycle): boolean { + return lifecycle.hasMessageStart || lifecycle.hasMessageDelta || lifecycle.hasMessageStop; +} + +export function shouldInjectClaudeEmptyResponseBeforeCurrentEvent( + lifecycle: ClaudeEmptyResponseLifecycle, + payload: unknown +): boolean { + const type = getClaudeEventType(payload); + if (!type || lifecycle.hasError || lifecycle.hasContentBlock) return false; + if (!hasClaudeAssistantLifecycle(lifecycle)) return false; + return type === "message_delta" || type === "message_stop"; +} + +export function shouldInjectClaudeEmptyResponseOnFlush( + lifecycle: ClaudeEmptyResponseLifecycle +): boolean { + if (lifecycle.hasError || lifecycle.hasContentBlock) return false; + return hasClaudeAssistantLifecycle(lifecycle); +} + +export function shouldInjectClaudeMissingFinalizersOnFlush( + lifecycle: ClaudeEmptyResponseLifecycle +): boolean { + if (lifecycle.hasError || !lifecycle.syntheticContentInjected) return false; + return !lifecycle.hasMessageDelta || !lifecycle.hasMessageStop; +} + +export function buildSyntheticClaudeEmptyResponseEvents( + lifecycle: ClaudeEmptyResponseLifecycle, + model: string | null, + options: { + includeContentBlock?: boolean; + includeMessageDelta?: boolean; + includeMessageStop?: boolean; + } = {} +): JsonRecord[] { + const { + includeContentBlock = true, + includeMessageDelta = false, + includeMessageStop = false, + } = options; + const events: JsonRecord[] = []; + const resolvedModel = typeof model === "string" && model ? model : "unknown"; + + if (includeContentBlock) { + if (!lifecycle.hasMessageStart) { + events.push({ + type: "message_start", + message: { + id: `msg_synthetic_${Date.now()}`, + type: "message", + role: "assistant", + model: resolvedModel, + content: [], + stop_reason: null, + stop_sequence: null, + usage: { input_tokens: 0, output_tokens: 0 }, + }, + }); + } + + events.push( + { + type: "content_block_start", + index: 0, + content_block: { type: "text", text: "" }, + }, + { + type: "content_block_delta", + index: 0, + delta: { + type: "text_delta", + text: SYNTHETIC_CLAUDE_EMPTY_RESPONSE_TEXT, + }, + }, + { + type: "content_block_stop", + index: 0, + } + ); + } + + if (includeMessageDelta) { + events.push({ + type: "message_delta", + delta: { stop_reason: "end_turn", stop_sequence: null }, + usage: { input_tokens: 0, output_tokens: 0 }, + }); + } + + if (includeMessageStop) { + events.push({ type: "message_stop" }); + } + + return events; +} + +export function restoreClaudePassthroughToolUseName( + parsed: JsonRecord, + toolNameMap: unknown +): boolean { + if (!(toolNameMap instanceof Map)) return false; + if (!parsed || typeof parsed !== "object") return false; + + const block = + parsed.content_block && typeof parsed.content_block === "object" + ? (parsed.content_block as JsonRecord) + : null; + if (!block || block.type !== "tool_use" || typeof block.name !== "string") return false; + + const restoredName = toolNameMap.get(block.name) ?? block.name; + if (restoredName === block.name) return false; + block.name = restoredName; + return true; +} diff --git a/package-lock.json b/package-lock.json index 6be7d680157b..b76d121f6009 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "omniroute", - "version": "3.8.26", + "version": "3.8.27", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "omniroute", - "version": "3.8.26", + "version": "3.8.27", "hasInstallScript": true, "license": "MIT", "workspaces": [ @@ -27928,7 +27928,7 @@ }, "open-sse": { "name": "@omniroute/open-sse", - "version": "3.8.26" + "version": "3.8.27" } } } diff --git a/package.json b/package.json index ea8323a251d7..e83e69fb65c6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "omniroute", - "version": "3.8.26", + "version": "3.8.27", "description": "Unified AI router with 160+ providers, RTK+Caveman compression, auto fallback, MCP/A2A, desktop, PWA, and OpenAI-compatible APIs.", "type": "module", "bin": {