Skip to content

Commit 1a3b7f3

Browse files
committed
refactor: simplify context injection to use tool parts universally
Remove model-specific branching (DeepSeek/Kimi check) and unused helper functions. Now all non-user-message injections use synthetic tool parts appended to the last message, eliminating ~100 lines of code.
1 parent 16c8f25 commit 1a3b7f3

2 files changed

Lines changed: 6 additions & 139 deletions

File tree

lib/messages/inject.ts

Lines changed: 6 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,7 @@ import {
88
buildToolIdList,
99
createSyntheticTextPart,
1010
createSyntheticToolPart,
11-
createSyntheticAssistantMessage,
12-
createSyntheticAssistantMessageWithToolPart,
1311
isIgnoredUserMessage,
14-
isDeepSeekOrKimi,
1512
} from "./utils"
1613
import { getFilePathFromParameters, isProtectedFilePath } from "../protected-file-patterns"
1714
import { getLastUserMessage, isMessageCompacted } from "../shared-utils"
@@ -195,37 +192,16 @@ export const insertPruneToolContext = (
195192
return
196193
}
197194

198-
// It's not safe to inject assistant role messages following a user message as models such
199-
// as Claude expect the assistant "turn" to start with reasoning parts. Reasoning parts in many
200-
// cases also cannot be faked as they may be encrypted by the model.
201-
// Gemini only accepts synth reasoning text if it is "skip_thought_signature_validator"
195+
// When following a user message, append a synthetic text part since models like Claude
196+
// expect assistant turns to start with reasoning parts which cannot be easily faked.
197+
// For all other cases, append a synthetic tool part to the last message which works
198+
// across all models without disrupting their behavior.
202199
if (lastNonIgnoredMessage.info.role === "user") {
203200
const textPart = createSyntheticTextPart(lastNonIgnoredMessage, combinedContent)
204201
lastNonIgnoredMessage.parts.push(textPart)
205202
} else {
206-
// For non-user message case: push a new synthetic assistant message or append tool part
207-
// for DeepSeek/Kimi. DeepSeek and Kimi don't output reasoning parts following an
208-
// assistant injection containing text parts. Tool parts appended to the last assistant
209-
// message are the safest way to inject context without disrupting model behavior.
210-
const providerID = userInfo.model?.providerID || ""
211203
const modelID = userInfo.model?.modelID || ""
212-
213-
if (isDeepSeekOrKimi(providerID, modelID)) {
214-
const toolPart = createSyntheticToolPart(
215-
lastNonIgnoredMessage,
216-
combinedContent,
217-
modelID,
218-
)
219-
lastNonIgnoredMessage.parts.push(toolPart)
220-
} else {
221-
messages.push(
222-
createSyntheticAssistantMessageWithToolPart(
223-
lastUserMessage,
224-
combinedContent,
225-
modelID,
226-
variant,
227-
),
228-
)
229-
}
204+
const toolPart = createSyntheticToolPart(lastNonIgnoredMessage, combinedContent, modelID)
205+
lastNonIgnoredMessage.parts.push(toolPart)
230206
}
231207
}

lib/messages/utils.ts

Lines changed: 0 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,6 @@ const isGeminiModel = (modelID: string): boolean => {
1313
return lowerModelID.includes("gemini")
1414
}
1515

16-
export const isDeepSeekOrKimi = (providerID: string, modelID: string): boolean => {
17-
const lowerProviderID = providerID.toLowerCase()
18-
const lowerModelID = modelID.toLowerCase()
19-
return (
20-
lowerProviderID.includes("deepseek") ||
21-
lowerProviderID.includes("kimi") ||
22-
lowerModelID.includes("deepseek") ||
23-
lowerModelID.includes("kimi")
24-
)
25-
}
26-
2716
export const createSyntheticUserMessage = (
2817
baseMessage: WithParts,
2918
content: string,
@@ -56,104 +45,6 @@ export const createSyntheticUserMessage = (
5645
}
5746
}
5847

59-
export const createSyntheticAssistantMessage = (
60-
baseMessage: WithParts,
61-
content: string,
62-
variant?: string,
63-
): WithParts => {
64-
const userInfo = baseMessage.info as UserMessage
65-
const now = Date.now()
66-
const messageId = generateUniqueId("msg")
67-
const partId = generateUniqueId("prt")
68-
69-
return {
70-
info: {
71-
id: messageId,
72-
sessionID: userInfo.sessionID,
73-
role: "assistant" as const,
74-
agent: userInfo.agent || "code",
75-
parentID: userInfo.id,
76-
modelID: userInfo.model.modelID,
77-
providerID: userInfo.model.providerID,
78-
mode: "default",
79-
path: {
80-
cwd: "/",
81-
root: "/",
82-
},
83-
time: { created: now, completed: now },
84-
cost: 0,
85-
tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
86-
...(variant !== undefined && { variant }),
87-
},
88-
parts: [
89-
{
90-
id: partId,
91-
sessionID: userInfo.sessionID,
92-
messageID: messageId,
93-
type: "text" as const,
94-
text: content,
95-
},
96-
],
97-
}
98-
}
99-
100-
export const createSyntheticAssistantMessageWithToolPart = (
101-
baseMessage: WithParts,
102-
content: string,
103-
modelID: string,
104-
variant?: string,
105-
): WithParts => {
106-
const userInfo = baseMessage.info as UserMessage
107-
const now = Date.now()
108-
const messageId = generateUniqueId("msg")
109-
const partId = generateUniqueId("prt")
110-
const callId = generateUniqueId("call")
111-
112-
// Gemini requires thoughtSignature bypass to accept synthetic tool parts
113-
const toolPartMetadata = isGeminiModel(modelID)
114-
? { google: { thoughtSignature: "skip_thought_signature_validator" } }
115-
: {}
116-
117-
return {
118-
info: {
119-
id: messageId,
120-
sessionID: userInfo.sessionID,
121-
role: "assistant" as const,
122-
agent: userInfo.agent || "code",
123-
parentID: userInfo.id,
124-
modelID: userInfo.model.modelID,
125-
providerID: userInfo.model.providerID,
126-
mode: "default",
127-
path: {
128-
cwd: "/",
129-
root: "/",
130-
},
131-
time: { created: now, completed: now },
132-
cost: 0,
133-
tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
134-
...(variant !== undefined && { variant }),
135-
},
136-
parts: [
137-
{
138-
id: partId,
139-
sessionID: userInfo.sessionID,
140-
messageID: messageId,
141-
type: "tool" as const,
142-
callID: callId,
143-
tool: "context_info",
144-
state: {
145-
status: "completed" as const,
146-
input: {},
147-
output: content,
148-
title: "Context Info",
149-
metadata: toolPartMetadata,
150-
time: { start: now, end: now },
151-
},
152-
},
153-
],
154-
}
155-
}
156-
15748
export const createSyntheticTextPart = (baseMessage: WithParts, content: string) => {
15849
const userInfo = baseMessage.info as UserMessage
15950
const partId = generateUniqueId("prt")

0 commit comments

Comments
 (0)