From 7ae788860df31630472413f0768b49789f912e6c Mon Sep 17 00:00:00 2001 From: Hazem Ali Date: Thu, 30 Apr 2026 16:34:54 +0300 Subject: [PATCH 1/2] Implement normalization for response input items Added normalization functions to handle response input items, ensuring no empty or missing type fields. --- .../platform/endpoint/node/responsesApi.ts | 45 ++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/extensions/copilot/src/platform/endpoint/node/responsesApi.ts b/extensions/copilot/src/platform/endpoint/node/responsesApi.ts index 1602620080fa9..bcb51ff3a672d 100644 --- a/extensions/copilot/src/platform/endpoint/node/responsesApi.ts +++ b/extensions/copilot/src/platform/endpoint/node/responsesApi.ts @@ -302,6 +302,48 @@ interface RawMessagesToResponseAPIOptions { readonly modeChanged?: boolean; } + +function hasMessageShape(item: unknown): item is { role: string; content: unknown } { + return !!item + && typeof item === "object" + && typeof (item as { role?: unknown }).role === "string" + && "content" in item; +} + +function hasEmptyType(item: unknown): boolean { + if (!item || typeof item !== "object") { + return true; + } + const type = (item as { type?: unknown }).type; + return typeof type !== "string" || type.trim().length === 0; +} + +function normalizeResponsesInputItem( + item: T +): T | (T & { type: "message" }) | undefined { + if (!item || typeof item !== "object") { + return undefined; + } + if (!hasEmptyType(item)) { + return item; + } + if (hasMessageShape(item)) { + return { + ...item, + type: "message" + }; + } + return undefined; +} + +function normalizeResponsesInput( + input: readonly T[] +): Array { + return input + .map(normalizeResponsesInputItem) + .filter((item): item is T | (T & { type: "message" }) => !!item); +} + function rawMessagesToResponseAPI(modelId: string, messages: readonly Raw.ChatMessage[], ignoreStatefulMarker: boolean, webSocketStatefulMarker: string | undefined, options: RawMessagesToResponseAPIOptions = {}): { input: OpenAI.Responses.ResponseInputItem[]; previous_response_id?: string } { const { toolsMap, shouldLoadToolFromToolSearch, modeChanged = false } = options; const latestCompactionMessageIndex = getLatestCompactionMessageIndex(messages); @@ -442,7 +484,8 @@ function rawMessagesToResponseAPI(modelId: string, messages: readonly Raw.ChatMe } } - return { input, previous_response_id: previousResponseId }; + // Defensive normalization: ensure no empty or missing type fields + return { input: normalizeResponsesInput(input), previous_response_id: previousResponseId }; } /** From 0e577be0609db81e4549841940e1c4d25a634f80 Mon Sep 17 00:00:00 2001 From: Hazem Ali Date: Thu, 30 Apr 2026 16:35:44 +0300 Subject: [PATCH 2/2] Add tests for normalizeResponsesInput function --- .../endpoint/node/test/responsesApi.spec.ts | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/extensions/copilot/src/platform/endpoint/node/test/responsesApi.spec.ts b/extensions/copilot/src/platform/endpoint/node/test/responsesApi.spec.ts index d323216447ba3..94039e62898c8 100644 --- a/extensions/copilot/src/platform/endpoint/node/test/responsesApi.spec.ts +++ b/extensions/copilot/src/platform/endpoint/node/test/responsesApi.spec.ts @@ -1,3 +1,27 @@ +import { normalizeResponsesInput } from "../responsesApi"; + +describe("normalizeResponsesInput (regression)", () => { + it("normalizes empty type to message and drops empty items", () => { + const input = [ + { type: "message", role: "user", content: "hello" }, + { type: "", role: "assistant", content: "hi" }, + { type: "" } + ]; + const expected = [ + { type: "message", role: "user", content: "hello" }, + { type: "message", role: "assistant", content: "hi" } + ]; + expect(normalizeResponsesInput(input)).toEqual(expected); + }); + it("preserves valid tool and reasoning items", () => { + const input = [ + { type: "tool_search_call", foo: 1 }, + { type: "function_call_output", bar: 2 }, + { type: "reasoning", baz: 3 } + ]; + expect(normalizeResponsesInput(input)).toEqual(input); + }); +}); /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information.