diff --git a/src/core/condense/__tests__/nested-condense.spec.ts b/src/core/condense/__tests__/nested-condense.spec.ts index fbccc15ead..39aebc64d5 100644 --- a/src/core/condense/__tests__/nested-condense.spec.ts +++ b/src/core/condense/__tests__/nested-condense.spec.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from "vitest" -import { ApiMessage } from "../../task-persistence/apiMessages" +import { LegacyApiMessage } from "../../task-persistence/apiMessages" import { getEffectiveApiHistory, getMessagesSinceLastSummary } from "../index" describe("nested condensing scenarios", () => { diff --git a/src/core/condense/__tests__/rewind-after-condense.spec.ts b/src/core/condense/__tests__/rewind-after-condense.spec.ts index b5e8c4c06b..eebb7152fd 100644 --- a/src/core/condense/__tests__/rewind-after-condense.spec.ts +++ b/src/core/condense/__tests__/rewind-after-condense.spec.ts @@ -12,7 +12,7 @@ import { TelemetryService } from "@roo-code/telemetry" import { getEffectiveApiHistory, cleanupAfterTruncation } from "../index" -import { ApiMessage } from "../../task-persistence/apiMessages" +import { LegacyApiMessage } from "../../task-persistence/apiMessages" describe("Rewind After Condense - Issue #8295", () => { beforeEach(() => { diff --git a/src/core/message-manager/index.ts b/src/core/message-manager/index.ts index 71a5f3ae2d..58cc1667dc 100644 --- a/src/core/message-manager/index.ts +++ b/src/core/message-manager/index.ts @@ -1,7 +1,7 @@ import * as path from "path" import { Task } from "../task/Task" import { ClineMessage } from "@roo-code/types" -import { ApiMessage } from "../task-persistence/apiMessages" +import { LegacyApiMessage } from "../task-persistence/apiMessages" import { cleanupAfterTruncation } from "../condense" import { OutputInterceptor } from "../../integrations/terminal/OutputInterceptor" import { getTaskDirectoryPath } from "../../utils/storage" diff --git a/src/core/task-persistence/__tests__/rooMessages.spec.ts b/src/core/task-persistence/__tests__/rooMessages.spec.ts index 55f3b7c9c7..dc687dae04 100644 --- a/src/core/task-persistence/__tests__/rooMessages.spec.ts +++ b/src/core/task-persistence/__tests__/rooMessages.spec.ts @@ -4,8 +4,8 @@ import * as os from "os" import * as path from "path" import * as fs from "fs/promises" -import { detectFormat, readRooMessages, saveRooMessages } from "../apiMessages" -import type { ApiMessage } from "../apiMessages" +import { detectFormat, readRooMessages, saveRooMessages, stripCacheProviderOptions } from "../apiMessages" +import type { LegacyApiMessage } from "../apiMessages" import type { RooMessage, RooMessageHistory } from "../rooMessage" import { ROO_MESSAGE_VERSION } from "../rooMessage" import * as safeWriteJsonModule from "../../../utils/safeWriteJson" @@ -46,7 +46,7 @@ const sampleV2Envelope: RooMessageHistory = { messages: sampleRooMessages, } -const sampleLegacyMessages: ApiMessage[] = [ +const sampleLegacyMessages: LegacyApiMessage[] = [ { role: "user", content: "Hello from legacy", ts: 1000 }, { role: "assistant", content: "Legacy response", ts: 2000 }, ] @@ -275,3 +275,198 @@ describe("round-trip", () => { expect(detectFormat(parsed)).toBe("v2") }) }) + +// ──────────────────────────────────────────────────────────────────────────── +// stripCacheProviderOptions +// ──────────────────────────────────────────────────────────────────────────── + +describe("stripCacheProviderOptions", () => { + it("returns messages unchanged when they have no providerOptions", () => { + const messages: RooMessage[] = [ + { role: "user" as const, content: [{ type: "text" as const, text: "hi" }] }, + { role: "assistant" as const, content: [{ type: "text" as const, text: "hello" }] }, + ] + + const result = stripCacheProviderOptions(messages) + + expect(result).toEqual(messages) + }) + + it("strips anthropic.cacheControl and removes empty providerOptions", () => { + const messages: RooMessage[] = [ + { + role: "assistant" as const, + content: [{ type: "text" as const, text: "hello" }], + providerOptions: { + anthropic: { cacheControl: { type: "ephemeral" } }, + }, + }, + ] + + const result = stripCacheProviderOptions(messages) + + expect(result).toEqual([ + { + role: "assistant" as const, + content: [{ type: "text" as const, text: "hello" }], + }, + ]) + expect(result[0]).not.toHaveProperty("providerOptions") + }) + + it("strips bedrock.cachePoint and removes empty providerOptions", () => { + const messages: RooMessage[] = [ + { + role: "assistant" as const, + content: [{ type: "text" as const, text: "hello" }], + providerOptions: { + bedrock: { cachePoint: { type: "default" } }, + }, + }, + ] + + const result = stripCacheProviderOptions(messages) + + expect(result).toEqual([ + { + role: "assistant" as const, + content: [{ type: "text" as const, text: "hello" }], + }, + ]) + expect(result[0]).not.toHaveProperty("providerOptions") + }) + + it("strips both anthropic.cacheControl and bedrock.cachePoint simultaneously", () => { + const messages: RooMessage[] = [ + { + role: "assistant" as const, + content: [{ type: "text" as const, text: "hello" }], + providerOptions: { + anthropic: { cacheControl: { type: "ephemeral" } }, + bedrock: { cachePoint: { type: "default" } }, + }, + }, + ] + + const result = stripCacheProviderOptions(messages) + + expect(result).toEqual([ + { + role: "assistant" as const, + content: [{ type: "text" as const, text: "hello" }], + }, + ]) + expect(result[0]).not.toHaveProperty("providerOptions") + }) + + it("preserves anthropic.signature while stripping anthropic.cacheControl", () => { + const messages: RooMessage[] = [ + { + role: "assistant" as const, + content: [{ type: "text" as const, text: "hello" }], + providerOptions: { + anthropic: { cacheControl: { type: "ephemeral" }, signature: "abc123" }, + }, + }, + ] + + const result = stripCacheProviderOptions(messages) + + expect(result).toEqual([ + { + role: "assistant" as const, + content: [{ type: "text" as const, text: "hello" }], + providerOptions: { + anthropic: { signature: "abc123" }, + }, + }, + ]) + }) + + it("preserves openrouter.reasoning_details unchanged", () => { + const reasoningDetails = [{ type: "thinking", thinking: "hmm" }] + const messages: RooMessage[] = [ + { + role: "assistant" as const, + content: [{ type: "text" as const, text: "hello" }], + providerOptions: { + openrouter: { reasoning_details: reasoningDetails }, + }, + }, + ] + + const result = stripCacheProviderOptions(messages) + + expect(result).toEqual([ + { + role: "assistant" as const, + content: [{ type: "text" as const, text: "hello" }], + providerOptions: { + openrouter: { reasoning_details: reasoningDetails }, + }, + }, + ]) + }) + + it("strips cache keys while preserving non-cache keys across namespaces", () => { + const reasoningDetails = [{ type: "thinking", thinking: "hmm" }] + const messages: RooMessage[] = [ + { + role: "assistant" as const, + content: [{ type: "text" as const, text: "hello" }], + providerOptions: { + anthropic: { cacheControl: { type: "ephemeral" }, signature: "abc123" }, + bedrock: { cachePoint: { type: "default" } }, + openrouter: { reasoning_details: reasoningDetails }, + }, + }, + ] + + const result = stripCacheProviderOptions(messages) + + expect(result).toEqual([ + { + role: "assistant" as const, + content: [{ type: "text" as const, text: "hello" }], + providerOptions: { + anthropic: { signature: "abc123" }, + openrouter: { reasoning_details: reasoningDetails }, + }, + }, + ]) + // bedrock namespace should be fully removed + const resultOptions = (result[0] as unknown as Record).providerOptions as Record + expect(resultOptions).not.toHaveProperty("bedrock") + }) + + it("does not mutate the original array", () => { + const original: RooMessage[] = [ + { + role: "assistant" as const, + content: [{ type: "text" as const, text: "hello" }], + providerOptions: { + anthropic: { cacheControl: { type: "ephemeral" }, signature: "abc123" }, + }, + }, + ] + + // Snapshot original state before calling + const originalSnapshot = JSON.parse(JSON.stringify(original)) + + stripCacheProviderOptions(original) + + expect(original).toEqual(originalSnapshot) + // Verify the providerOptions still has cacheControl on the original + const originalOptions = (original[0] as unknown as Record).providerOptions as Record< + string, + Record + > + expect(originalOptions["anthropic"]["cacheControl"]).toEqual({ type: "ephemeral" }) + }) + + it("returns empty array for empty input", () => { + const result = stripCacheProviderOptions([]) + + expect(result).toEqual([]) + }) +}) diff --git a/src/core/task-persistence/apiMessages.ts b/src/core/task-persistence/apiMessages.ts index 3c2149ed44..615d221875 100644 --- a/src/core/task-persistence/apiMessages.ts +++ b/src/core/task-persistence/apiMessages.ts @@ -12,7 +12,10 @@ import type { RooMessage, RooMessageHistory } from "./rooMessage" import { ROO_MESSAGE_VERSION } from "./rooMessage" import { convertAnthropicToRooMessages } from "./converters/anthropicToRoo" -export type ApiMessage = Anthropic.MessageParam & { +/** + * @deprecated This is the legacy Anthropic message format. Use {@link RooMessage} for the current format. + */ +export type LegacyApiMessage = Anthropic.MessageParam & { ts?: number isSummary?: boolean id?: string @@ -40,13 +43,16 @@ export type ApiMessage = Anthropic.MessageParam & { isTruncationMarker?: boolean } +/** @deprecated Use {@link LegacyApiMessage} directly. This alias exists for backward compatibility only. */ +export type ApiMessage = LegacyApiMessage + export async function readApiMessages({ taskId, globalStoragePath, }: { taskId: string globalStoragePath: string -}): Promise { +}): Promise { const taskDir = await getTaskDirectoryPath(globalStoragePath, taskId) const filePath = path.join(taskDir, GlobalFileNames.apiConversationHistory) @@ -114,7 +120,7 @@ export async function saveApiMessages({ taskId, globalStoragePath, }: { - messages: ApiMessage[] + messages: LegacyApiMessage[] taskId: string globalStoragePath: string }) { @@ -194,7 +200,7 @@ export async function readRooMessages({ return [] } - return convertAnthropicToRooMessages(parsedData as ApiMessage[]) + return convertAnthropicToRooMessages(parsedData as LegacyApiMessage[]) } const primaryResult = await tryParseFile(filePath) @@ -214,6 +220,48 @@ export async function readRooMessages({ return [] } +/** + * Strip transient cache-control provider options that are applied at request + * time by applyCacheBreakpoints() and should not be persisted. + * + * Removes: + * - anthropic.cacheControl + * - bedrock.cachePoint + * + * Preserves all other providerOptions (e.g. anthropic.signature, openrouter.reasoning_details). + */ +export function stripCacheProviderOptions(messages: RooMessage[]): RooMessage[] { + const cloned = structuredClone(messages) + + for (const msg of cloned) { + if (!("providerOptions" in msg) || (msg as { providerOptions?: unknown }).providerOptions == null) { + continue + } + + const providerOptions = (msg as { providerOptions: Record> }).providerOptions + + if (providerOptions["anthropic"] != null) { + delete providerOptions["anthropic"]["cacheControl"] + if (Object.keys(providerOptions["anthropic"]).length === 0) { + delete providerOptions["anthropic"] + } + } + + if (providerOptions["bedrock"] != null) { + delete providerOptions["bedrock"]["cachePoint"] + if (Object.keys(providerOptions["bedrock"]).length === 0) { + delete providerOptions["bedrock"] + } + } + + if (Object.keys(providerOptions).length === 0) { + delete (msg as { providerOptions?: unknown }).providerOptions + } + } + + return cloned +} + /** * Save `RooMessage[]` wrapped in the versioned `RooMessageHistory` envelope. * @@ -234,9 +282,10 @@ export async function saveRooMessages({ try { const taskDir = await getTaskDirectoryPath(globalStoragePath, taskId) const filePath = path.join(taskDir, GlobalFileNames.apiConversationHistory) + const strippedMessages = stripCacheProviderOptions(messages) const envelope: RooMessageHistory = { version: ROO_MESSAGE_VERSION, - messages, + messages: strippedMessages, } await safeWriteJson(filePath, envelope) return true diff --git a/src/core/task-persistence/converters/__tests__/anthropicToRoo.spec.ts b/src/core/task-persistence/converters/__tests__/anthropicToRoo.spec.ts index 8cfc72a9e8..09cae0cb54 100644 --- a/src/core/task-persistence/converters/__tests__/anthropicToRoo.spec.ts +++ b/src/core/task-persistence/converters/__tests__/anthropicToRoo.spec.ts @@ -1,4 +1,4 @@ -import type { ApiMessage } from "../../apiMessages" +import type { LegacyApiMessage } from "../../apiMessages" import type { RooUserMessage, RooAssistantMessage, @@ -16,9 +16,9 @@ import { convertAnthropicToRooMessages } from "../anthropicToRoo" // Helpers // ──────────────────────────────────────────────────────────────────────────── -/** Shorthand to create an ApiMessage with required fields. */ -function apiMsg(overrides: Partial & Pick): ApiMessage { - return overrides as ApiMessage +/** Shorthand to create a LegacyApiMessage with required fields. */ +function apiMsg(overrides: Partial & Pick): LegacyApiMessage { + return overrides as LegacyApiMessage } // ──────────────────────────────────────────────────────────────────────────── @@ -139,7 +139,7 @@ describe("user messages with URL image content", () => { describe("user messages with tool_result blocks", () => { test("splits tool_result into RooToolMessage before RooUserMessage", () => { - const messages: ApiMessage[] = [ + const messages: LegacyApiMessage[] = [ apiMsg({ role: "assistant", content: [{ type: "tool_use", id: "call_1", name: "read_file", input: { path: "foo.ts" } }], @@ -179,7 +179,7 @@ describe("user messages with tool_result blocks", () => { }) test("handles tool_result with array content (joins text with newlines)", () => { - const messages: ApiMessage[] = [ + const messages: LegacyApiMessage[] = [ apiMsg({ role: "assistant", content: [{ type: "tool_use", id: "call_2", name: "list_files", input: {} }], @@ -204,7 +204,7 @@ describe("user messages with tool_result blocks", () => { }) test("handles tool_result with undefined content → (empty)", () => { - const messages: ApiMessage[] = [ + const messages: LegacyApiMessage[] = [ apiMsg({ role: "assistant", content: [{ type: "tool_use", id: "call_3", name: "run_command", input: {} }], @@ -220,7 +220,7 @@ describe("user messages with tool_result blocks", () => { }) test("handles tool_result with empty string content → (empty)", () => { - const messages: ApiMessage[] = [ + const messages: LegacyApiMessage[] = [ apiMsg({ role: "assistant", content: [{ type: "tool_use", id: "call_4", name: "run_command", input: {} }], @@ -242,7 +242,7 @@ describe("user messages with tool_result blocks", () => { describe("user messages with mixed tool_result and text", () => { test("separates tool results from text/image parts correctly", () => { - const messages: ApiMessage[] = [ + const messages: LegacyApiMessage[] = [ apiMsg({ role: "assistant", content: [ @@ -287,7 +287,7 @@ describe("user messages with mixed tool_result and text", () => { }) test("only emits tool message when no text/image parts exist", () => { - const messages: ApiMessage[] = [ + const messages: LegacyApiMessage[] = [ apiMsg({ role: "assistant", content: [{ type: "tool_use", id: "tc_only", name: "some_tool", input: {} }], @@ -733,7 +733,7 @@ describe("metadata preservation", () => { }) test("carries over metadata on tool messages (split from user)", () => { - const messages: ApiMessage[] = [ + const messages: LegacyApiMessage[] = [ apiMsg({ role: "assistant", content: [{ type: "tool_use", id: "tc_meta", name: "my_tool", input: {} }], @@ -787,7 +787,7 @@ describe("metadata preservation", () => { describe("tool name resolution", () => { test("resolves tool names from preceding assistant messages", () => { - const messages: ApiMessage[] = [ + const messages: LegacyApiMessage[] = [ apiMsg({ role: "assistant", content: [{ type: "tool_use", id: "tc_x", name: "execute_command", input: { command: "ls" } }], @@ -803,7 +803,7 @@ describe("tool name resolution", () => { }) test("falls back to unknown_tool when tool call ID is not found", () => { - const messages: ApiMessage[] = [ + const messages: LegacyApiMessage[] = [ apiMsg({ role: "user", content: [{ type: "tool_result", tool_use_id: "nonexistent_id", content: "result" }], @@ -815,7 +815,7 @@ describe("tool name resolution", () => { }) test("resolves tool names across multiple assistant messages", () => { - const messages: ApiMessage[] = [ + const messages: LegacyApiMessage[] = [ apiMsg({ role: "assistant", content: [{ type: "tool_use", id: "tc_first", name: "tool_alpha", input: {} }], @@ -878,7 +878,7 @@ describe("empty/undefined content edge cases", () => { }) test("handles tool_result with image content blocks", () => { - const messages: ApiMessage[] = [ + const messages: LegacyApiMessage[] = [ apiMsg({ role: "assistant", content: [{ type: "tool_use", id: "tc_img", name: "screenshot", input: {} }], @@ -914,7 +914,7 @@ describe("empty/undefined content edge cases", () => { describe("full conversation round-trip", () => { test("converts a realistic multi-turn conversation", () => { - const messages: ApiMessage[] = [ + const messages: LegacyApiMessage[] = [ // Turn 1: user asks a question apiMsg({ role: "user", content: "Can you read my config file?", ts: 1000 }), // Turn 2: assistant uses a tool diff --git a/src/core/task-persistence/converters/anthropicToRoo.ts b/src/core/task-persistence/converters/anthropicToRoo.ts index 24a0d9bc15..d94c83eb9e 100644 --- a/src/core/task-persistence/converters/anthropicToRoo.ts +++ b/src/core/task-persistence/converters/anthropicToRoo.ts @@ -1,5 +1,5 @@ /** - * Converter from Anthropic-format `ApiMessage` to the new `RooMessage` format. + * Converter from Anthropic-format `LegacyApiMessage` to the new `RooMessage` format. * * This is the critical backward-compatibility piece that allows old conversation * histories stored in Anthropic format to be read and converted to the new format. @@ -9,7 +9,7 @@ */ import type { TextPart, ImagePart, ToolCallPart, ToolResultPart, ReasoningPart } from "../rooMessage" -import type { ApiMessage } from "../apiMessages" +import type { LegacyApiMessage } from "../apiMessages" import type { RooMessage, RooUserMessage, @@ -28,10 +28,10 @@ import type { type LooseProviderOptions = Record> /** - * Extract Roo-specific metadata fields from an ApiMessage. + * Extract Roo-specific metadata fields from a LegacyApiMessage. * Only includes fields that are actually defined (avoids `undefined` keys). */ -function extractMetadata(message: ApiMessage): RooMessageMetadata { +function extractMetadata(message: LegacyApiMessage): RooMessageMetadata { const metadata: RooMessageMetadata = {} if (message.ts !== undefined) metadata.ts = message.ts if (message.condenseId !== undefined) metadata.condenseId = message.condenseId @@ -82,7 +82,7 @@ function attachReasoningDetails( } /** - * Convert an array of Anthropic-format `ApiMessage` objects to `RooMessage` format. + * Convert an array of Anthropic-format `LegacyApiMessage` objects to `RooMessage` format. * * Conversion rules: * - User string content → `RooUserMessage` with `content: string` @@ -93,10 +93,10 @@ function attachReasoningDetails( * - Standalone reasoning messages → `RooReasoningMessage` * - Metadata fields (ts, condenseId, etc.) are preserved on all output messages * - * @param messages - Array of ApiMessage (Anthropic format with metadata) + * @param messages - Array of LegacyApiMessage (Anthropic format with metadata) * @returns Array of RooMessage objects */ -export function convertAnthropicToRooMessages(messages: ApiMessage[]): RooMessage[] { +export function convertAnthropicToRooMessages(messages: LegacyApiMessage[]): RooMessage[] { const result: RooMessage[] = [] // First pass: build a map of tool call IDs to tool names from assistant messages. diff --git a/src/core/task-persistence/index.ts b/src/core/task-persistence/index.ts index a26fb30de5..4d73d2b0d1 100644 --- a/src/core/task-persistence/index.ts +++ b/src/core/task-persistence/index.ts @@ -1,5 +1,5 @@ -export { type ApiMessage, readApiMessages, saveApiMessages } from "./apiMessages" -export { detectFormat, readRooMessages, saveRooMessages } from "./apiMessages" +export { type LegacyApiMessage, type ApiMessage, readApiMessages, saveApiMessages } from "./apiMessages" +export { detectFormat, readRooMessages, saveRooMessages, stripCacheProviderOptions } from "./apiMessages" export { readTaskMessages, saveTaskMessages } from "./taskMessages" export { taskMetadata } from "./taskMetadata" export type { RooMessage, RooMessageHistory, RooMessageMetadata } from "./rooMessage" diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index d03c793095..d1722e9fd6 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -110,7 +110,7 @@ import { manageContext, willManageContext } from "../context-management" import { ClineProvider } from "../webview/ClineProvider" import { MultiSearchReplaceDiffStrategy } from "../diff/strategies/multi-search-replace" import { - type ApiMessage, + type LegacyApiMessage, readApiMessages, saveApiMessages, readTaskMessages, diff --git a/src/core/webview/__tests__/webviewMessageHandler.edit.spec.ts b/src/core/webview/__tests__/webviewMessageHandler.edit.spec.ts index 2f11281d2b..f81c71d6e9 100644 --- a/src/core/webview/__tests__/webviewMessageHandler.edit.spec.ts +++ b/src/core/webview/__tests__/webviewMessageHandler.edit.spec.ts @@ -40,7 +40,7 @@ vi.mock("../checkpointRestoreHandler", () => ({ import { webviewMessageHandler } from "../webviewMessageHandler" import type { ClineProvider } from "../ClineProvider" import type { ClineMessage } from "@roo-code/types" -import type { ApiMessage } from "../../task-persistence/apiMessages" +import type { LegacyApiMessage } from "../../task-persistence/apiMessages" import { MessageManager } from "../../message-manager" describe("webviewMessageHandler - Edit Message with Timestamp Fallback", () => { @@ -54,7 +54,7 @@ describe("webviewMessageHandler - Edit Message with Timestamp Fallback", () => { mockCurrentTask = { taskId: "test-task-id", clineMessages: [] as ClineMessage[], - apiConversationHistory: [] as ApiMessage[], + apiConversationHistory: [] as LegacyApiMessage[], overwriteClineMessages: vi.fn(), overwriteApiConversationHistory: vi.fn(), handleWebviewAskResponse: vi.fn(), @@ -126,7 +126,7 @@ describe("webviewMessageHandler - Edit Message with Timestamp Fallback", () => { }, ], }, - ] as ApiMessage[] + ] as LegacyApiMessage[] // Trigger edit confirmation await webviewMessageHandler(mockClineProvider, { @@ -184,7 +184,7 @@ describe("webviewMessageHandler - Edit Message with Timestamp Fallback", () => { role: "assistant", content: [{ type: "text", text: "Response" }], }, - ] as ApiMessage[] + ] as LegacyApiMessage[] await webviewMessageHandler(mockClineProvider, { type: "editMessageConfirm", @@ -244,7 +244,7 @@ describe("webviewMessageHandler - Edit Message with Timestamp Fallback", () => { role: "assistant", content: [{ type: "text", text: "Response" }], }, - ] as ApiMessage[] + ] as LegacyApiMessage[] await webviewMessageHandler(mockClineProvider, { type: "editMessageConfirm", @@ -282,7 +282,7 @@ describe("webviewMessageHandler - Edit Message with Timestamp Fallback", () => { role: "assistant", content: [{ type: "text", text: "Old message 2" }], }, - ] as ApiMessage[] + ] as LegacyApiMessage[] await webviewMessageHandler(mockClineProvider, { type: "editMessageConfirm", @@ -378,7 +378,7 @@ describe("webviewMessageHandler - Edit Message with Timestamp Fallback", () => { }, ], }, - ] as ApiMessage[] + ] as LegacyApiMessage[] // Edit the first user message await webviewMessageHandler(mockClineProvider, { diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index 041cc72983..0d6288b08c 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -25,7 +25,7 @@ import { customToolRegistry } from "@roo-code/core" import { CloudService } from "@roo-code/cloud" import { TelemetryService } from "@roo-code/telemetry" -import { type ApiMessage } from "../task-persistence/apiMessages" +import { type LegacyApiMessage } from "../task-persistence/apiMessages" import { saveTaskMessages } from "../task-persistence" import { ClineProvider } from "./ClineProvider" @@ -131,11 +131,11 @@ export const webviewMessageHandler = async ( // Find all matching API messages by timestamp const allApiMatches = currentCline.apiConversationHistory - .map((msg: ApiMessage, idx: number) => ({ msg, idx })) - .filter(({ msg }: { msg: ApiMessage }) => msg.ts === messageTs) + .map((msg: LegacyApiMessage, idx: number) => ({ msg, idx })) + .filter(({ msg }: { msg: LegacyApiMessage }) => msg.ts === messageTs) // Prefer non-summary message if multiple matches exist (handles timestamp collision after condense) - const preferred = allApiMatches.find(({ msg }: { msg: ApiMessage }) => !msg.isSummary) || allApiMatches[0] + const preferred = allApiMatches.find(({ msg }: { msg: LegacyApiMessage }) => !msg.isSummary) || allApiMatches[0] const apiConversationHistoryIndex = preferred?.idx ?? -1 return { messageIndex, apiConversationHistoryIndex } @@ -148,7 +148,7 @@ export const webviewMessageHandler = async ( const findFirstApiIndexAtOrAfter = (ts: number, currentCline: any) => { if (typeof ts !== "number") return -1 return currentCline.apiConversationHistory.findIndex( - (msg: ApiMessage) => typeof msg?.ts === "number" && (msg.ts as number) >= ts, + (msg: LegacyApiMessage) => typeof msg?.ts === "number" && (msg.ts as number) >= ts, ) }