Skip to content

Commit 9233be0

Browse files
committed
fix: require reasoning block before injecting context for Claude models
- Add hasReasoningInCurrentAssistantTurn to verify Claude has emitted its required thinking block before we inject context - Add isIgnoredUserMessage to skip UI-only messages when checking turn state - Update getLastUserMessage to skip ignored messages for accurate model detection - Split GitHub Copilot and Anthropic injection guards into separate checks
1 parent 3926883 commit 9233be0

File tree

3 files changed

+48
-3
lines changed

3 files changed

+48
-3
lines changed

lib/messages/inject.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import {
77
extractParameterKey,
88
buildToolIdList,
99
createSyntheticAssistantMessageWithToolPart,
10+
isIgnoredUserMessage,
11+
hasReasoningInCurrentAssistantTurn,
1012
} from "./utils"
1113
import { getFilePathFromParameters, isProtectedFilePath } from "../protected-file-patterns"
1214
import { getLastUserMessage } from "../shared-utils"
@@ -144,9 +146,17 @@ export const insertPruneToolContext = (
144146
providerID === "github-copilot" || providerID === "github-copilot-enterprise"
145147
const isAnthropic = modelID.includes("claude")
146148

147-
if (isGitHubCopilot || isAnthropic) {
149+
if (isGitHubCopilot) {
148150
const lastMessage = messages[messages.length - 1]
149-
if (lastMessage?.info?.role === "user") {
151+
if (lastMessage?.info?.role === "user" && !isIgnoredUserMessage(lastMessage)) {
152+
return
153+
}
154+
}
155+
156+
// Anthropic extended thinking models require a thinking block at the start of its turn
157+
// This can probably be improved further to only trigger for the appropriate thinking settings
158+
if (isAnthropic) {
159+
if (!hasReasoningInCurrentAssistantTurn(messages)) {
150160
return
151161
}
152162
}

lib/messages/utils.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,3 +193,37 @@ export function buildToolIdList(
193193
}
194194
return toolIds
195195
}
196+
197+
export const isIgnoredUserMessage = (message: WithParts): boolean => {
198+
if (!message.parts || message.parts.length === 0) {
199+
return true
200+
}
201+
202+
for (const part of message.parts) {
203+
if (!(part as any).ignored) {
204+
return false
205+
}
206+
}
207+
208+
return true
209+
}
210+
211+
export const hasReasoningInCurrentAssistantTurn = (messages: WithParts[]): boolean => {
212+
for (let i = messages.length - 1; i >= 0; i--) {
213+
const message = messages[i]
214+
if (message.info?.role === "user") {
215+
if (isIgnoredUserMessage(message)) {
216+
continue
217+
}
218+
return false
219+
}
220+
if (message.info?.role === "assistant" && message.parts) {
221+
for (const part of message.parts) {
222+
if (part.type === "reasoning") {
223+
return true
224+
}
225+
}
226+
}
227+
}
228+
return false
229+
}

lib/shared-utils.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { SessionState, WithParts } from "./state"
2+
import { isIgnoredUserMessage } from "./messages/utils"
23

34
export const isMessageCompacted = (state: SessionState, msg: WithParts): boolean => {
45
return msg.info.time.created < state.lastCompaction
@@ -7,7 +8,7 @@ export const isMessageCompacted = (state: SessionState, msg: WithParts): boolean
78
export const getLastUserMessage = (messages: WithParts[]): WithParts | null => {
89
for (let i = messages.length - 1; i >= 0; i--) {
910
const msg = messages[i]
10-
if (msg.info.role === "user") {
11+
if (msg.info.role === "user" && !isIgnoredUserMessage(msg)) {
1112
return msg
1213
}
1314
}

0 commit comments

Comments
 (0)