diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 027efc0974b0..e86b70bdc067 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -22,6 +22,17 @@ export const OUTPUT_TOKEN_MAX = 32_000 // branch that requests it stays in lockstep. const INCLUDE_ENCRYPTED_REASONING = ["reasoning.encrypted_content"] as const +function responsesItemIDOptionKeys(model: Provider.Model, sdkOptionKey: string | undefined) { + if (model.api.npm === "@ai-sdk/github-copilot") return ["openai", "copilot"] + if ( + sdkOptionKey && + ["@ai-sdk/openai", "@ai-sdk/azure", "@ai-sdk/amazon-bedrock/mantle"].includes(model.api.npm) + ) { + return [sdkOptionKey] + } + return [] +} + export function sanitizeSurrogates(content: string) { return content.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(? 0) { msgs = mapProviderOptions(msgs, (options) => { - if (!options?.[key] || !("itemId" in options[key])) return options - const metadata = { ...options[key] } - delete metadata.itemId - return { ...options, [key]: metadata } + if (!options) return options + let result = options + for (const itemIDKey of itemIDKeys) { + if (!result[itemIDKey] || !("itemId" in result[itemIDKey])) continue + const metadata = { ...result[itemIDKey] } + delete metadata.itemId + result = { ...result, [itemIDKey]: metadata } + } + return result }) } diff --git a/packages/opencode/test/provider/transform.test.ts b/packages/opencode/test/provider/transform.test.ts index c23a2aa9995c..128cbe67ff79 100644 --- a/packages/opencode/test/provider/transform.test.ts +++ b/packages/opencode/test/provider/transform.test.ts @@ -1928,6 +1928,58 @@ describe("ProviderTransform.message - strip openai metadata when store=false", ( expect(result[0].content[1].providerOptions?.openai?.itemId).toBeUndefined() }) + test("strips Copilot Responses itemId while preserving encrypted reasoning", () => { + const copilotModel = { + ...openaiModel, + id: "github-copilot/gpt-5.5", + providerID: "github-copilot", + api: { + id: "gpt-5.5", + url: "https://api.githubcopilot.com", + npm: "@ai-sdk/github-copilot", + }, + } + const msgs = [ + { + role: "assistant", + content: [ + { + type: "reasoning", + text: "thinking...", + providerOptions: { + openai: { + itemId: "rs_123", + reasoningEncryptedContent: "encrypted", + }, + copilot: { + itemId: "rs_123", + reasoningEncryptedContent: "encrypted", + }, + }, + }, + { + type: "text", + text: "Hello", + providerOptions: { + openai: { + itemId: "msg_456", + }, + }, + }, + ], + }, + ] as any[] + + const result = ProviderTransform.message(msgs, copilotModel, { store: false }) as any[] + + expect(result).toHaveLength(1) + expect(result[0].content[0].providerOptions?.openai?.itemId).toBeUndefined() + expect(result[0].content[0].providerOptions?.openai?.reasoningEncryptedContent).toBe("encrypted") + expect(result[0].content[0].providerOptions?.copilot?.itemId).toBeUndefined() + expect(result[0].content[0].providerOptions?.copilot?.reasoningEncryptedContent).toBe("encrypted") + expect(result[0].content[1].providerOptions?.openai?.itemId).toBeUndefined() + }) + test("uses the SDK package namespace rather than provider ID", () => { const zenModel = { ...openaiModel,