fix: preserve thinking sections during consecutive tool uses

- Add isConsecutiveToolUse() method to Task class to detect when assistant is making consecutive tool calls without user intervention
- Conditionally preserve <thinking> tags in presentAssistantMessage when consecutive tool use is detected
- This fixes issue #8065 where reasoning models were losing context between tool uses

The solution checks the conversation history to determine if the current message follows another assistant message (consecutive tool use) or a user message (new interaction). When consecutive tool use is detected, thinking sections are preserved to maintain context for reasoning models.
This commit is contained in:
Roo Code 2025-09-17 13:00:36 +00:00
parent dcc6db00c7
commit c5efea55e1
2 changed files with 62 additions and 7 deletions

View file

@ -101,13 +101,19 @@ export async function presentAssistantMessage(cline: Task) {
// here for reference.
// content = content.replace(/<\/?t(?:h(?:i(?:n(?:k(?:i(?:n(?:g)?)?)?$/, "")
//
// Remove all instances of <thinking> (with optional line break
// after) and </thinking> (with optional line break before).
// - Needs to be separate since we dont want to remove the line
// break before the first tag.
// - Needs to happen before the xml parsing below.
content = content.replace(/<thinking>\s?/g, "")
content = content.replace(/\s?<\/thinking>/g, "")
// Check if we should preserve thinking sections
// Preserve thinking sections during consecutive tool uses (no user messages in between)
const shouldPreserveThinking = cline.isConsecutiveToolUse()
if (!shouldPreserveThinking) {
// Remove all instances of <thinking> (with optional line break
// after) and </thinking> (with optional line break before).
// - Needs to be separate since we dont want to remove the line
// break before the first tag.
// - Needs to happen before the xml parsing below.
content = content.replace(/<thinking>\s?/g, "")
content = content.replace(/\s?<\/thinking>/g, "")
}
// Remove partial XML tag at the very end of the content (for
// tool use and thinking tags), Prevents scrollview from

View file

@ -2932,4 +2932,53 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
console.error(`[Task] Queue processing error:`, e)
}
}
/**
* Check if the current message is a consecutive tool use without user intervention.
* This is used to determine whether to preserve thinking sections for reasoning models.
*
* @returns true if the last non-system message was from the assistant (consecutive tool use),
* false if it was from the user or if there are no previous messages
*/
public isConsecutiveToolUse(): boolean {
// Look at the conversation history to determine if this is consecutive tool use
if (this.apiConversationHistory.length === 0) {
return false
}
// Find the last message that isn't the current one being processed
// We need to check if the previous message was from the assistant (consecutive tool use)
// or from the user (new interaction)
const lastMessage = this.apiConversationHistory[this.apiConversationHistory.length - 1]
// If the last message is from the user, this is not a consecutive tool use
if (lastMessage.role === "user") {
// Check if this is just environment details or actual user content
// Environment details are added automatically and shouldn't count as user intervention
if (Array.isArray(lastMessage.content)) {
// Check if the content only contains environment details
const hasUserContent = lastMessage.content.some((block) => {
if (block.type === "text") {
const text = block.text.trim()
// Check if this is just environment details or tool results
return (
!text.startsWith("environment_details:") &&
!text.includes("[Tool Use:") &&
!text.includes("Result:") &&
text.length > 0
)
}
// Images or other content types indicate user interaction
return block.type === "image"
})
// If there's actual user content, this is not consecutive tool use
return !hasUserContent
}
return false
}
// If the last message is from the assistant, this is consecutive tool use
return lastMessage.role === "assistant"
}
}