Skip to content

Commit a2ae844

Browse files
committed
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.
1 parent 018a0d6 commit a2ae844

File tree

1 file changed

+44
-18
lines changed

1 file changed

+44
-18
lines changed

lib/messages/utils.ts

Lines changed: 44 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,32 +7,58 @@ const SYNTHETIC_MESSAGE_ID = "msg_01234567890123456789012345"
77
const SYNTHETIC_PART_ID = "prt_01234567890123456789012345"
88
const SYNTHETIC_CALL_ID = "call_01234567890123456789012345"
99

10+
const isGeminiModel = (modelID: string): boolean => {
11+
const lowerModelID = modelID.toLowerCase()
12+
return lowerModelID.includes("gemini")
13+
}
14+
1015
export const createSyntheticAssistantMessageWithToolPart = (
1116
baseMessage: WithParts,
1217
content: string,
1318
variant?: string,
1419
): WithParts => {
1520
const userInfo = baseMessage.info as UserMessage
1621
const now = Date.now()
17-
return {
18-
info: {
19-
id: SYNTHETIC_MESSAGE_ID,
20-
sessionID: userInfo.sessionID,
21-
role: "assistant",
22-
agent: userInfo.agent || "code",
23-
parentID: userInfo.id,
24-
modelID: userInfo.model.modelID,
25-
providerID: userInfo.model.providerID,
26-
mode: "default",
27-
path: {
28-
cwd: "/",
29-
root: "/",
30-
},
31-
time: { created: now, completed: now },
32-
cost: 0,
33-
tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
34-
...(variant !== undefined && { variant }),
22+
23+
const baseInfo = {
24+
id: SYNTHETIC_MESSAGE_ID,
25+
sessionID: userInfo.sessionID,
26+
role: "assistant" as const,
27+
agent: userInfo.agent || "code",
28+
parentID: userInfo.id,
29+
modelID: userInfo.model.modelID,
30+
providerID: userInfo.model.providerID,
31+
mode: "default",
32+
path: {
33+
cwd: "/",
34+
root: "/",
3535
},
36+
time: { created: now, completed: now },
37+
cost: 0,
38+
tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
39+
...(variant !== undefined && { variant }),
40+
}
41+
42+
// For Gemini models, inject as text to avoid thought signature requirements
43+
// Gemini 3+ has strict validation requiring thoughtSignature on functionCall parts
44+
if (isGeminiModel(userInfo.model.modelID)) {
45+
return {
46+
info: baseInfo,
47+
parts: [
48+
{
49+
id: SYNTHETIC_PART_ID,
50+
sessionID: userInfo.sessionID,
51+
messageID: SYNTHETIC_MESSAGE_ID,
52+
type: "text",
53+
text: content,
54+
},
55+
],
56+
}
57+
}
58+
59+
// For other models, use tool part for cleaner context
60+
return {
61+
info: baseInfo,
3662
parts: [
3763
{
3864
id: SYNTHETIC_PART_ID,

0 commit comments

Comments
 (0)