import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; vi.mock("../plugins/provider-runtime.js", () => ({ resolveProviderRuntimePlugin: vi.fn(({ provider }: { provider?: string }) => { if ( !provider || ![ "amazon-bedrock", "anthropic", "google", "kilocode", "kimi", "kimi-code", "minimax", "minimax-portal", "mistral", "moonshot", "openai", "openai-codex", "opencode", "opencode-go", "ollama", "openrouter", "sglang", "vllm", "xai", "zai", ].includes(provider) ) { return undefined; } if (provider === "sglang" || provider === "vllm") { return {}; } return { buildReplayPolicy: (context?: { modelId?: string; modelApi?: string }) => { const modelId = context?.modelId?.toLowerCase() ?? ""; switch (provider) { case "amazon-bedrock": case "anthropic": return { sanitizeMode: "full", sanitizeToolCallIds: true, toolCallIdMode: "strict", preserveSignatures: true, repairToolUseResultPairing: true, validateAnthropicTurns: true, allowSyntheticToolResults: true, ...(modelId.includes("claude") ? { dropThinkingBlocks: true } : {}), }; case "minimax": case "minimax-portal": return context?.modelApi === "openai-completions" ? { sanitizeToolCallIds: true, toolCallIdMode: "strict", applyAssistantFirstOrderingFix: true, validateGeminiTurns: true, validateAnthropicTurns: true, } : { sanitizeMode: "full", sanitizeToolCallIds: true, toolCallIdMode: "strict", preserveSignatures: true, repairToolUseResultPairing: true, validateAnthropicTurns: true, allowSyntheticToolResults: true, ...(modelId.includes("claude") ? { dropThinkingBlocks: true } : {}), }; case "moonshot": case "ollama": case "zai": return context?.modelApi === "openai-completions" ? { sanitizeToolCallIds: true, toolCallIdMode: "strict", applyAssistantFirstOrderingFix: true, validateGeminiTurns: true, validateAnthropicTurns: true, } : undefined; case "google": return { sanitizeMode: "full", sanitizeToolCallIds: true, toolCallIdMode: "strict", sanitizeThoughtSignatures: { allowBase64Only: true, includeCamelCase: true, }, repairToolUseResultPairing: true, applyAssistantFirstOrderingFix: true, validateGeminiTurns: true, validateAnthropicTurns: false, allowSyntheticToolResults: true, }; case "mistral": return { sanitizeToolCallIds: true, toolCallIdMode: "strict9", }; case "openai": case "openai-codex": return { sanitizeMode: "images-only", sanitizeToolCallIds: context?.modelApi === "openai-completions", ...(context?.modelApi === "openai-completions" ? { toolCallIdMode: "strict" } : {}), applyAssistantFirstOrderingFix: false, validateGeminiTurns: false, validateAnthropicTurns: false, }; case "kimi": case "kimi-code": return { preserveSignatures: false, }; case "openrouter": case "opencode": case "opencode-go": return { applyAssistantFirstOrderingFix: false, validateGeminiTurns: false, validateAnthropicTurns: false, ...(modelId.includes("gemini") ? { sanitizeThoughtSignatures: { allowBase64Only: true, includeCamelCase: true, }, } : {}), }; case "xai": if ( context?.modelApi === "openai-completions" || context?.modelApi === "openai-responses" ) { return { sanitizeToolCallIds: true, toolCallIdMode: "strict", ...(context.modelApi === "openai-completions" ? { applyAssistantFirstOrderingFix: true, validateGeminiTurns: true, validateAnthropicTurns: true, } : { applyAssistantFirstOrderingFix: false, validateGeminiTurns: false, validateAnthropicTurns: false, }), }; } return undefined; case "kilocode": return modelId.includes("gemini") ? { sanitizeThoughtSignatures: { allowBase64Only: true, includeCamelCase: true, }, } : undefined; default: return undefined; } }, }; }), resetProviderRuntimeHookCacheForTest: vi.fn(), })); let resolveTranscriptPolicy: typeof import("./transcript-policy.js").resolveTranscriptPolicy; describe("resolveTranscriptPolicy", () => { beforeAll(async () => { ({ resolveTranscriptPolicy } = await import("./transcript-policy.js")); }); beforeEach(() => { vi.clearAllMocks(); }); it("enables sanitizeToolCallIds for Anthropic provider", () => { const policy = resolveTranscriptPolicy({ provider: "anthropic", modelId: "claude-opus-4-6", modelApi: "anthropic-messages", }); expect(policy.sanitizeToolCallIds).toBe(true); expect(policy.toolCallIdMode).toBe("strict"); }); it("enables sanitizeToolCallIds for Google provider", () => { const policy = resolveTranscriptPolicy({ provider: "google", modelId: "gemini-2.0-flash", modelApi: "google-generative-ai", }); expect(policy.sanitizeToolCallIds).toBe(true); expect(policy.sanitizeThoughtSignatures).toEqual({ allowBase64Only: true, includeCamelCase: true, }); }); it("enables sanitizeToolCallIds for Mistral provider", () => { const policy = resolveTranscriptPolicy({ provider: "mistral", modelId: "mistral-large-latest", }); expect(policy.sanitizeToolCallIds).toBe(true); expect(policy.toolCallIdMode).toBe("strict9"); }); it("disables sanitizeToolCallIds for OpenAI provider", () => { const policy = resolveTranscriptPolicy({ provider: "openai", modelId: "gpt-4o", modelApi: "openai", }); expect(policy.sanitizeToolCallIds).toBe(false); expect(policy.toolCallIdMode).toBeUndefined(); expect(policy.applyGoogleTurnOrdering).toBe(false); expect(policy.validateGeminiTurns).toBe(false); expect(policy.validateAnthropicTurns).toBe(false); }); it("enables strict tool call id sanitization for openai-completions APIs", () => { const policy = resolveTranscriptPolicy({ provider: "openai", modelId: "gpt-5.4", modelApi: "openai-completions", }); expect(policy.sanitizeToolCallIds).toBe(true); expect(policy.toolCallIdMode).toBe("strict"); }); it("enables user-turn merge for strict OpenAI-compatible providers", () => { const policy = resolveTranscriptPolicy({ provider: "moonshot", modelId: "kimi-k2.5", modelApi: "openai-completions", }); expect(policy.applyGoogleTurnOrdering).toBe(true); expect(policy.validateGeminiTurns).toBe(true); expect(policy.validateAnthropicTurns).toBe(true); }); it("falls back to unowned transport defaults when no owning plugin exists", () => { const policy = resolveTranscriptPolicy({ provider: "custom-openai-proxy", modelId: "demo-model", modelApi: "openai-completions", }); expect(policy.sanitizeToolCallIds).toBe(true); expect(policy.toolCallIdMode).toBe("strict"); expect(policy.applyGoogleTurnOrdering).toBe(true); expect(policy.validateGeminiTurns).toBe(true); expect(policy.validateAnthropicTurns).toBe(true); }); it("preserves transport defaults when a runtime plugin has not adopted replay hooks", () => { const policy = resolveTranscriptPolicy({ provider: "vllm", modelId: "demo-model", modelApi: "openai-completions", }); expect(policy.sanitizeToolCallIds).toBe(true); expect(policy.toolCallIdMode).toBe("strict"); expect(policy.applyGoogleTurnOrdering).toBe(true); expect(policy.validateGeminiTurns).toBe(true); expect(policy.validateAnthropicTurns).toBe(true); }); it("uses provider-owned Anthropic replay policy for MiniMax transports", () => { const policy = resolveTranscriptPolicy({ provider: "minimax", modelId: "MiniMax-M2.7", modelApi: "anthropic-messages", }); expect(policy.sanitizeMode).toBe("full"); expect(policy.sanitizeToolCallIds).toBe(true); expect(policy.preserveSignatures).toBe(true); expect(policy.validateAnthropicTurns).toBe(true); }); it("uses provider-owned OpenAI-compatible replay policy for MiniMax portal completions", () => { const policy = resolveTranscriptPolicy({ provider: "minimax-portal", modelId: "MiniMax-M2.7", modelApi: "openai-completions", }); expect(policy.sanitizeMode).toBe("images-only"); expect(policy.sanitizeToolCallIds).toBe(true); expect(policy.toolCallIdMode).toBe("strict"); expect(policy.preserveSignatures).toBe(false); expect(policy.applyGoogleTurnOrdering).toBe(true); expect(policy.validateGeminiTurns).toBe(true); expect(policy.validateAnthropicTurns).toBe(true); }); it("enables Anthropic-compatible policies for Bedrock provider", () => { const policy = resolveTranscriptPolicy({ provider: "amazon-bedrock", modelId: "us.anthropic.claude-opus-4-6-v1", modelApi: "bedrock-converse-stream", }); expect(policy.repairToolUseResultPairing).toBe(true); expect(policy.validateAnthropicTurns).toBe(true); expect(policy.allowSyntheticToolResults).toBe(true); expect(policy.sanitizeToolCallIds).toBe(true); expect(policy.sanitizeMode).toBe("full"); }); it.each([ { title: "Anthropic provider", provider: "anthropic", modelId: "claude-opus-4-6", modelApi: "anthropic-messages" as const, preserveSignatures: true, }, { title: "Bedrock Anthropic", provider: "amazon-bedrock", modelId: "us.anthropic.claude-opus-4-6-v1", modelApi: "bedrock-converse-stream" as const, preserveSignatures: true, }, { title: "Google provider", provider: "google", modelId: "gemini-2.0-flash", modelApi: "google-generative-ai" as const, preserveSignatures: false, }, { title: "OpenAI provider", provider: "openai", modelId: "gpt-4o", modelApi: "openai" as const, preserveSignatures: false, }, { title: "Mistral provider", provider: "mistral", modelId: "mistral-large-latest", preserveSignatures: false, }, { title: "Kimi provider", provider: "kimi", modelId: "kimi-code", modelApi: "anthropic-messages" as const, preserveSignatures: false, }, { title: "kimi-code alias", provider: "kimi-code", modelId: "kimi-code", modelApi: "anthropic-messages" as const, preserveSignatures: false, }, ])("sets preserveSignatures for $title (#32526, #39798)", ({ preserveSignatures, ...input }) => { const policy = resolveTranscriptPolicy(input); expect(policy.preserveSignatures).toBe(preserveSignatures); }); it("enables turn-ordering and assistant-merge for strict OpenAI-compatible providers (#38962)", () => { const policy = resolveTranscriptPolicy({ provider: "vllm", modelId: "gemma-3-27b", modelApi: "openai-completions", }); expect(policy.applyGoogleTurnOrdering).toBe(true); expect(policy.validateGeminiTurns).toBe(true); expect(policy.validateAnthropicTurns).toBe(true); }); it("keeps OpenRouter on its existing turn-validation path", () => { const policy = resolveTranscriptPolicy({ provider: "openrouter", modelId: "openai/gpt-4.1", modelApi: "openai-completions", }); expect(policy.applyGoogleTurnOrdering).toBe(false); expect(policy.validateGeminiTurns).toBe(false); expect(policy.validateAnthropicTurns).toBe(false); }); it.each([ { provider: "openrouter", modelId: "google/gemini-2.5-pro-preview" }, { provider: "opencode", modelId: "google/gemini-2.5-flash" }, { provider: "kilocode", modelId: "gemini-2.0-flash" }, ])("sanitizes Gemini thought signatures for $provider routes", ({ provider, modelId }) => { const policy = resolveTranscriptPolicy({ provider, modelId, modelApi: "openai-completions", }); expect(policy.sanitizeThoughtSignatures).toEqual({ allowBase64Only: true, includeCamelCase: true, }); }); });