From f05c2123f27a0e45bddda502bd6250a98f92dc0b Mon Sep 17 00:00:00 2001 From: Roo Code Date: Tue, 25 Nov 2025 16:09:10 +0000 Subject: [PATCH] fix: preserve model names in exported task history - Add modelId field to ClineMessage type to store model ID with each message - Update Task.ts to capture and store model ID when messages are created - Update getEnvironmentDetails to accept optional model ID parameter - Ensures exported task history shows correct model names for each message Fixes #9580 --- packages/types/src/message.ts | 1 + src/core/environment/getEnvironmentDetails.ts | 10 ++++-- src/core/task/Task.ts | 35 +++++++++++++++++-- 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/packages/types/src/message.ts b/packages/types/src/message.ts index 548da850b6..e62faea054 100644 --- a/packages/types/src/message.ts +++ b/packages/types/src/message.ts @@ -227,6 +227,7 @@ export const clineMessageSchema = z.object({ isProtected: z.boolean().optional(), apiProtocol: z.union([z.literal("openai"), z.literal("anthropic")]).optional(), isAnswered: z.boolean().optional(), + modelId: z.string().optional(), // Store the model ID used when creating this message }) export type ClineMessage = z.infer diff --git a/src/core/environment/getEnvironmentDetails.ts b/src/core/environment/getEnvironmentDetails.ts index e42db79d40..1e94e9ed83 100644 --- a/src/core/environment/getEnvironmentDetails.ts +++ b/src/core/environment/getEnvironmentDetails.ts @@ -22,7 +22,11 @@ import { getGitStatus } from "../../utils/git" import { Task } from "../task/Task" import { formatReminderSection } from "./reminder" -export async function getEnvironmentDetails(cline: Task, includeFileDetails: boolean = false) { +export async function getEnvironmentDetails( + cline: Task, + includeFileDetails: boolean = false, + overrideModelId?: string, +) { let details = "" const clineProvider = cline.providerRef.deref() @@ -215,7 +219,9 @@ export async function getEnvironmentDetails(cline: Task, includeFileDetails: boo details += `\n\n# Current Cost\n${totalCost !== null ? `$${totalCost.toFixed(2)}` : "(Not available)"}` } - const { id: modelId } = cline.api.getModel() + // Use the override model ID if provided (e.g., for preserving historical model in exports) + // Otherwise use the current model from the task + const modelId = overrideModelId || cline.api.getModel().id // Add current mode and any mode-specific warnings. const { diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index f7b0adc28a..07c5cfeb85 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -798,6 +798,10 @@ export class Task extends EventEmitter implements TaskLike { } private async addToClineMessages(message: ClineMessage) { + // Add the current model ID to the message if not already present + if (!message.modelId) { + message.modelId = this.api.getModel().id + } this.clineMessages.push(message) const provider = this.providerRef.deref() await provider?.postStateToWebview() @@ -941,7 +945,15 @@ export class Task extends EventEmitter implements TaskLike { askTs = Date.now() this.lastMessageTs = askTs console.log(`Task#ask: new partial ask -> ${type} @ ${askTs}`) - await this.addToClineMessages({ ts: askTs, type: "ask", ask: type, text, partial, isProtected }) + await this.addToClineMessages({ + ts: askTs, + type: "ask", + ask: type, + text, + partial, + isProtected, + modelId: this.api.getModel().id, + }) // console.log("Task#ask: current ask promise was ignored (#2)") throw new Error("Current ask promise was ignored (#2)") } @@ -981,7 +993,14 @@ export class Task extends EventEmitter implements TaskLike { askTs = Date.now() console.log(`Task#ask: new complete ask -> ${type} @ ${askTs}`) this.lastMessageTs = askTs - await this.addToClineMessages({ ts: askTs, type: "ask", ask: type, text, isProtected }) + await this.addToClineMessages({ + ts: askTs, + type: "ask", + ask: type, + text, + isProtected, + modelId: this.api.getModel().id, + }) } } } else { @@ -992,7 +1011,14 @@ export class Task extends EventEmitter implements TaskLike { askTs = Date.now() console.log(`Task#ask: new complete ask -> ${type} @ ${askTs}`) this.lastMessageTs = askTs - await this.addToClineMessages({ ts: askTs, type: "ask", ask: type, text, isProtected }) + await this.addToClineMessages({ + ts: askTs, + type: "ask", + ask: type, + text, + isProtected, + modelId: this.api.getModel().id, + }) } let timeouts: NodeJS.Timeout[] = [] @@ -1364,6 +1390,7 @@ export class Task extends EventEmitter implements TaskLike { images, partial, contextCondense, + modelId: this.api.getModel().id, }) } } else { @@ -1401,6 +1428,7 @@ export class Task extends EventEmitter implements TaskLike { text, images, contextCondense, + modelId: this.api.getModel().id, }) } } @@ -1424,6 +1452,7 @@ export class Task extends EventEmitter implements TaskLike { images, checkpoint, contextCondense, + modelId: this.api.getModel().id, }) }