From a2ae844cf74d12ccb3d193c6731cf7b7fb86787d Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Thu, 15 Jan 2026 13:44:55 -0500 Subject: [PATCH] fix: use text injection for Gemini models to avoid thought signature errors Gemini 3+ models have strict validation requiring thoughtSignature on functionCall parts. When the plugin injects synthetic assistant messages with tool parts, providers without robust signature handling will fail with 400 errors. This change detects Gemini models and injects as text parts instead of tool parts, avoiding the thought signature requirement entirely. --- lib/messages/utils.ts | 62 ++++++++++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 18 deletions(-) diff --git a/lib/messages/utils.ts b/lib/messages/utils.ts index 756ecc7..fafdccd 100644 --- a/lib/messages/utils.ts +++ b/lib/messages/utils.ts @@ -7,6 +7,11 @@ const SYNTHETIC_MESSAGE_ID = "msg_01234567890123456789012345" const SYNTHETIC_PART_ID = "prt_01234567890123456789012345" const SYNTHETIC_CALL_ID = "call_01234567890123456789012345" +const isGeminiModel = (modelID: string): boolean => { + const lowerModelID = modelID.toLowerCase() + return lowerModelID.includes("gemini") +} + export const createSyntheticAssistantMessageWithToolPart = ( baseMessage: WithParts, content: string, @@ -14,25 +19,46 @@ export const createSyntheticAssistantMessageWithToolPart = ( ): WithParts => { const userInfo = baseMessage.info as UserMessage const now = Date.now() - return { - info: { - id: SYNTHETIC_MESSAGE_ID, - sessionID: userInfo.sessionID, - role: "assistant", - agent: userInfo.agent || "code", - parentID: userInfo.id, - modelID: userInfo.model.modelID, - providerID: userInfo.model.providerID, - mode: "default", - path: { - cwd: "/", - root: "/", - }, - time: { created: now, completed: now }, - cost: 0, - tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } }, - ...(variant !== undefined && { variant }), + + const baseInfo = { + id: SYNTHETIC_MESSAGE_ID, + sessionID: userInfo.sessionID, + role: "assistant" as const, + agent: userInfo.agent || "code", + parentID: userInfo.id, + modelID: userInfo.model.modelID, + providerID: userInfo.model.providerID, + mode: "default", + path: { + cwd: "/", + root: "/", }, + time: { created: now, completed: now }, + cost: 0, + tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } }, + ...(variant !== undefined && { variant }), + } + + // For Gemini models, inject as text to avoid thought signature requirements + // Gemini 3+ has strict validation requiring thoughtSignature on functionCall parts + if (isGeminiModel(userInfo.model.modelID)) { + return { + info: baseInfo, + parts: [ + { + id: SYNTHETIC_PART_ID, + sessionID: userInfo.sessionID, + messageID: SYNTHETIC_MESSAGE_ID, + type: "text", + text: content, + }, + ], + } + } + + // For other models, use tool part for cleaner context + return { + info: baseInfo, parts: [ { id: SYNTHETIC_PART_ID,