From da87422d9e78168652fa2173bb5394c84a1f0ca0 Mon Sep 17 00:00:00 2001 From: backryun Date: Thu, 6 Aug 2026 10:29:49 +0900 Subject: [PATCH] fix(types): preserve array-buffer response bodies (#9092) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Validated in local merge-train (devbox-vm-06-dev002) @ combined-tip (FAST gates: typecheck/complexity/cognitive/changelog/vitest — only pre-existing audit.test.ts flake). Evidence: /home/diegosouzapw/dev/proxys/OmniRoute/.claude/worktrees/merge-train-20260805-222248-suite.log --- open-sse/executors/duckduckgo-web.ts | 3 ++- open-sse/executors/edgeTts.ts | 6 ++---- open-sse/executors/windsurf.ts | 5 +++-- src/lib/skills/builtins.ts | 11 ++--------- 4 files changed, 9 insertions(+), 16 deletions(-) diff --git a/open-sse/executors/duckduckgo-web.ts b/open-sse/executors/duckduckgo-web.ts index f028dee7cd..4a761373b1 100644 --- a/open-sse/executors/duckduckgo-web.ts +++ b/open-sse/executors/duckduckgo-web.ts @@ -1,3 +1,4 @@ +import { Buffer } from "node:buffer"; import { generateKeyPairSync, randomUUID } from "node:crypto"; import vm from "node:vm"; import { solveDuckDuckGoChallenge, makeDuckDuckGoFeSignals } from "./duckduckgo-web/challenge.ts"; @@ -499,7 +500,7 @@ export class DuckDuckGoWebExecutor extends BaseExecutor { // Wrap the captured body as a Response so processResponse // (already a streaming/non-streaming transformer) can be // reused unchanged. - const upstreamResp = new Response(result.body, { + const upstreamResp = new Response(Buffer.from(result.body), { status: result.status, headers: { "Content-Type": result.contentType || "text/event-stream", diff --git a/open-sse/executors/edgeTts.ts b/open-sse/executors/edgeTts.ts index 74a9c7f333..18a9f144e9 100644 --- a/open-sse/executors/edgeTts.ts +++ b/open-sse/executors/edgeTts.ts @@ -58,7 +58,7 @@ export interface EdgeTtsSynthInput { } export interface EdgeTtsSynthResult { - audio: Buffer; + audio: Buffer; contentType: string; } @@ -189,9 +189,7 @@ export function isTurnEndMessage(message: string): boolean { * ASCII headers, then the remaining bytes are audio data. Returns `null` * for a frame too short to contain a valid header-length prefix. */ -export function demuxAudioChunk( - frame: Buffer -): { headers: string; audio: Buffer } | null { +export function demuxAudioChunk(frame: Buffer): { headers: string; audio: Buffer } | null { if (!Buffer.isBuffer(frame) || frame.length < 2) return null; const headerLength = frame.readUInt16BE(0); if (2 + headerLength > frame.length) return null; diff --git a/open-sse/executors/windsurf.ts b/open-sse/executors/windsurf.ts index d87e245149..782e48e4b7 100644 --- a/open-sse/executors/windsurf.ts +++ b/open-sse/executors/windsurf.ts @@ -168,7 +168,7 @@ function encodeVarint(value: number): Uint8Array { return new Uint8Array(bytes); } -function concatBytes(arrays: Uint8Array[]): Uint8Array { +function concatBytes(arrays: Uint8Array[]): Uint8Array { const total = arrays.reduce((n, a) => n + a.length, 0); const out = new Uint8Array(total); let off = 0; @@ -626,7 +626,8 @@ export class WindsurfExecutor extends BaseExecutor { const { done, value } = await reader.read(); if (done) break; if (!value) continue; - pending = pending.length === 0 ? value : concatBytes([pending, value]); + pending = + pending.length === 0 ? Uint8Array.from(value) : concatBytes([pending, value]); drainFrames(); } } finally { diff --git a/src/lib/skills/builtins.ts b/src/lib/skills/builtins.ts index ed367382cf..c488049070 100644 --- a/src/lib/skills/builtins.ts +++ b/src/lib/skills/builtins.ts @@ -148,7 +148,7 @@ async function readResponseText(response: Response, maxBytes: number) { function normalizeBody(body: unknown, headers?: Record) { if (body === undefined || body === null) return undefined; if (typeof body === "string") return body; - if (body instanceof Uint8Array) return body; + if (body instanceof Uint8Array) return new Uint8Array(body); if (typeof body === "object") { if (headers && !Object.keys(headers).some((key) => key.toLowerCase() === "content-type")) { headers["Content-Type"] = "application/json"; @@ -388,14 +388,7 @@ export const builtinSkills: Record = { }, web_fetch: async (input, context) => { - const { - url, - format, - depth, - wait_for_selector, - include_metadata, - provider, - } = input as { + const { url, format, depth, wait_for_selector, include_metadata, provider } = input as { url: string; format?: "markdown" | "html" | "links" | "screenshot"; depth?: 0 | 1 | 2;