diff --git a/packages/ai/test/openai-responses-history-payload.test.ts b/packages/ai/test/openai-responses-history-payload.test.ts index 7fe03fdcc..6cc50a18c 100644 --- a/packages/ai/test/openai-responses-history-payload.test.ts +++ b/packages/ai/test/openai-responses-history-payload.test.ts @@ -1360,13 +1360,6 @@ describe("OpenAI responses history payload", () => { const model = getOpenAIReasoningModel("openai", "gpt-5-mini"); const payload = (await captureResponsesPayload(model, context)) as { input?: unknown[] }; - const nonStrictDefaultModel: Model<"openai-responses"> = { - ...model, - compat: { ...model.compat, strictResponsesPairing: false }, - }; - const overriddenPayload = (await captureResponsesPayload(nonStrictDefaultModel, context, undefined, { - strictResponsesPairing: true, - })) as { input?: unknown[] }; const orphanSurvivors = (payload.input ?? []).filter(item => { if (!item || typeof item !== "object") return false; @@ -1374,12 +1367,6 @@ describe("OpenAI responses history payload", () => { return candidate.type === "function_call_output" && candidate.call_id === orphanCallId; }); expect(orphanSurvivors).toEqual([]); - const overriddenOrphanSurvivors = (overriddenPayload.input ?? []).filter(item => { - if (!item || typeof item !== "object") return false; - const candidate = item as { type?: unknown; call_id?: unknown }; - return candidate.type === "function_call_output" && candidate.call_id === orphanCallId; - }); - expect(overriddenOrphanSurvivors).toEqual([]); const pairedOutputs = (payload.input ?? []).filter(item => { if (!item || typeof item !== "object") return false; @@ -1400,4 +1387,63 @@ describe("OpenAI responses history payload", () => { }) as { content?: string } | undefined; expect(note?.content).toContain(orphanOutput); }); + + it("honors strict pairing overrides against opposite catalog defaults", async () => { + const orphanCallId = "call_override_orphan"; + const orphanOutput = "orphan result"; + const context: Context = { + messages: [ + { + role: "assistant", + content: [{ type: "toolCall", id: orphanCallId, name: "read", arguments: { path: "README.md" } }], + api: "openai-responses", + provider: "openai", + model: "gpt-5-mini", + usage: issue5002ZeroUsage, + stopReason: "toolUse", + providerPayload: createOpenAIResponsesHistoryPayload( + "openai", + [{ type: "message", role: "assistant", content: [{ type: "output_text", text: "snapshot" }] }], + false, + ), + timestamp: Date.now(), + }, + { + role: "toolResult", + toolCallId: orphanCallId, + toolName: "read", + content: [{ type: "text", text: orphanOutput }], + isError: false, + timestamp: Date.now(), + }, + { role: "user", content: "Continue", timestamp: Date.now() }, + ], + }; + const catalogNonStrictModel = getOpenAIReasoningModel("openai", "gpt-5-mini"); + const catalogStrictModel: Model<"openai-responses"> = { + ...catalogNonStrictModel, + compat: { ...catalogNonStrictModel.compat, strictResponsesPairing: true }, + }; + const strictOverridePayload = (await captureResponsesPayload(catalogNonStrictModel, context, undefined, { + strictResponsesPairing: true, + })) as { input?: unknown[] }; + const nonStrictOverridePayload = (await captureResponsesPayload(catalogStrictModel, context, undefined, { + strictResponsesPairing: false, + })) as { input?: unknown[] }; + const orphanNote = (input: unknown[] | undefined): { content?: string } | undefined => + input?.find(item => { + if (!item || typeof item !== "object") return false; + const candidate = item as { type?: unknown; role?: unknown; content?: unknown }; + return ( + candidate.type === "message" && + candidate.role === "assistant" && + typeof candidate.content === "string" && + candidate.content.includes(orphanCallId) && + candidate.content.includes(orphanOutput) + ); + }) as { content?: string } | undefined; + + expect(orphanNote(strictOverridePayload.input)?.content).toContain("[Orphan read result;"); + expect(orphanNote(nonStrictOverridePayload.input)?.content).toContain("[Orphan tool result;"); + }); });