diff --git a/src/core/Cline.ts b/src/core/Cline.ts index b9b243dbc13..df3ff0e2835 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -980,6 +980,8 @@ export class Cline { const previousRequest = this.clineMessages[previousApiReqIndex]?.text if (!previousRequest) return + this.deduplicateReadFileHistory() + const { tokensIn = 0, tokensOut = 0, @@ -1091,7 +1093,40 @@ export class Cline { // this delegates to another generator or iterable object. In this case, it's saying "yield all remaining values from this iterator". This effectively passes along all subsequent chunks from the original stream. yield* iterator } + deduplicateReadFileHistory() { + for (let i = this.apiConversationHistory.length - 1; i >= 0; i--) { + const conversation = this.apiConversationHistory[i] + + if (conversation.role !== "user") continue + + const content = conversation.content + if (typeof content === "string") continue + + const firstItem = content[0] + if (typeof firstItem === "string" || !("type" in firstItem) || firstItem.type !== "text") continue + + const toolUseText = firstItem.text + if (!toolUseText || !toolUseText.startsWith("[read_file for ")) continue + + for (let j = i - 1; j >= 0; j--) { + const prevConversation = this.apiConversationHistory[j] + + if (prevConversation.role === "assistant") continue + const prevContent = prevConversation.content + if (typeof prevContent === "string") continue + + const prevFirstItem = prevContent[0] + if (typeof prevFirstItem === "string" || !("type" in prevFirstItem) || prevFirstItem.type !== "text") + continue + + if (prevFirstItem.text === toolUseText && prevContent.length === 3) { + prevContent.splice(1, 1) + break + } + } + } + } async presentAssistantMessage() { if (this.abort) { throw new Error("Roo Code instance aborted")