From bd7049b7d1b02678db53cbeb8a3e6d970ddf5521 Mon Sep 17 00:00:00 2001 From: Markus Ylisiurunen Date: Thu, 22 Jan 2026 18:55:30 +0200 Subject: [PATCH] fix(ai): port openai responses handoff guard --- .../ai/src/providers/openai-codex-responses.ts | 2 +- .../ai/src/providers/openai-responses-shared.ts | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/packages/ai/src/providers/openai-codex-responses.ts b/packages/ai/src/providers/openai-codex-responses.ts index aa911c86..f168f657 100644 --- a/packages/ai/src/providers/openai-codex-responses.ts +++ b/packages/ai/src/providers/openai-codex-responses.ts @@ -6,7 +6,7 @@ if (typeof process !== "undefined" && (process.versions?.node || process.version }); } -import type { ResponseInput, ResponseStreamEvent, Tool as OpenAITool } from "openai/resources/responses/responses.js"; +import type { Tool as OpenAITool, ResponseInput, ResponseStreamEvent } from "openai/resources/responses/responses.js"; import { getEnvApiKey } from "../stream.js"; import type { Api, AssistantMessage, Context, Model, StreamFunction, StreamOptions } from "../types.js"; import { AssistantMessageEventStream } from "../utils/event-stream.js"; diff --git a/packages/ai/src/providers/openai-responses-shared.ts b/packages/ai/src/providers/openai-responses-shared.ts index 9ee16b10..7ee67850 100644 --- a/packages/ai/src/providers/openai-responses-shared.ts +++ b/packages/ai/src/providers/openai-responses-shared.ts @@ -135,6 +135,11 @@ export function convertResponsesMessages( } } else if (msg.role === "assistant") { const output: ResponseInput = []; + const assistantMsg = msg as AssistantMessage; + const isDifferentModel = + assistantMsg.model !== model.id && + assistantMsg.provider === model.provider && + assistantMsg.api === model.api; for (const block of msg.content) { if (block.type === "thinking") { @@ -160,7 +165,16 @@ export function convertResponsesMessages( } satisfies ResponseOutputMessage); } else if (block.type === "toolCall") { const toolCall = block as ToolCall; - const [callId, itemId] = toolCall.id.split("|"); + const [callId, itemIdRaw] = toolCall.id.split("|"); + let itemId: string | undefined = itemIdRaw; + + // For different-model messages, set id to undefined to avoid pairing validation. + // OpenAI tracks which fc_xxx IDs were paired with rs_xxx reasoning items. + // By omitting the id, we avoid triggering that validation (like cross-provider does). + if (isDifferentModel && itemId?.startsWith("fc_")) { + itemId = undefined; + } + output.push({ type: "function_call", id: itemId,