From 5a8c6440f0f5456465cae9746a236df57415de19 Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Tue, 24 Mar 2026 15:49:26 -0300 Subject: [PATCH] fix(combo): strip omniModel tags from outbound streaming responses (#585) The tag was leaking into user-visible content when context_cache_protection was enabled on a combo. The tag is an internal marker for model pinning across conversation turns. Fix: Add a second TransformStream pass (sanitize) that strips the tag from SSE chunk content before delivery to the client. The tag is still injected for round-trip context pinning but cleaned from visible output. Also adds X-OmniRoute-Model response header as a cleaner metadata channel. Closes #585 --- open-sse/services/combo.ts | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/open-sse/services/combo.ts b/open-sse/services/combo.ts index 7742ab492f..5bc36d6bc0 100644 --- a/open-sse/services/combo.ts +++ b/open-sse/services/combo.ts @@ -522,10 +522,33 @@ export async function handleComboChat({ }, }); - const transformedStream = res.body.pipeThrough(transform); + // FIX #585: Sanitize outbound stream — strip tags from + // visible content so they don't leak to the user. The tag is still + // present in the full response for round-trip context pinning, but + // we clean it from each SSE chunk's content field before delivery. + const sanitize = new TransformStream({ + transform(chunk, controller) { + const text = decoder.decode(chunk, { stream: true }); + // Only run replacement if the chunk actually contains the tag + if (text.includes("")) { + const cleaned = text.replace( + /(?:\\\\n|\\n)?[^<]+<\/omniModel>(?:\\\\n|\\n)?/g, + "" + ); + controller.enqueue(encoder.encode(cleaned)); + } else { + controller.enqueue(chunk); + } + }, + }); + + const transformedStream = res.body.pipeThrough(transform).pipeThrough(sanitize); + // Add model info as response header for clients that support it + const headers = new Headers(res.headers); + headers.set("X-OmniRoute-Model", modelStr); return new Response(transformedStream, { status: res.status, - headers: res.headers, + headers, }); } : handleSingleModel;