Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions src/core/assistant-message/presentAssistantMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider renaming shouldPreserveThinking to something more descriptive like isConsecutiveToolUseWithoutUserInput to better convey what this variable represents.


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
Expand Down
49 changes: 49 additions & 0 deletions src/core/task/Task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new isConsecutiveToolUse() method lacks unit tests. Given that this is critical functionality determining when thinking sections are preserved, we should add comprehensive test coverage to ensure it correctly identifies consecutive tool uses vs user interventions.

// 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 (
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The text pattern matching here (environment_details:, [Tool Use:, Result:) could be fragile if these patterns change. Consider extracting these as constants or using a more robust method to identify system-generated content vs actual user input.

!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"
}
}
Loading