@@ -644,6 +644,9 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
644644
645645 private async updateClineMessage ( message : ClineMessage ) {
646646 const provider = this . providerRef . deref ( )
647+
648+ // Messages now store both formats, so no conversion needed
649+ // The 'images' field already contains webview URIs for display
647650 await provider ?. postMessageToWebview ( { type : "messageUpdated" , clineMessage : message } )
648651 this . emit ( RooCodeEventName . Message , { action : "updated" , message } )
649652
@@ -735,6 +738,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
735738 lastMessage . partial = partial
736739 lastMessage . progressStatus = progressStatus
737740 lastMessage . isProtected = isProtected
741+ // Note: ask messages don't typically have images, so we don't update them here
738742 // TODO: Be more efficient about saving and posting only new
739743 // data or one whole message at a time so ignore partial for
740744 // saves, and only post parts of partial message instead of
@@ -877,16 +881,8 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
877881 text : this . askResponseText ,
878882 images : this . askResponseImages ,
879883 }
880- // Normalize any image refs to base64 data URLs before handing back to callers
881- if ( Array . isArray ( result . images ) && result . images . length > 0 ) {
882- try {
883- const { normalizeImageRefsToDataUrls } = await import ( "../../integrations/misc/imageDataUrl" )
884- const normalized = await normalizeImageRefsToDataUrls ( result . images )
885- result . images = normalized
886- } catch ( e ) {
887- console . error ( "[Task#ask] Failed to normalize image refs:" , e )
888- }
889- }
884+ // Images from askResponse are already webview URIs from the frontend,
885+ // so no conversion needed here
890886 this . askResponse = undefined
891887 this . askResponseText = undefined
892888 this . askResponseImages = undefined
@@ -1084,13 +1080,23 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
10841080 throw new Error ( `[RooCode#say] task ${ this . taskId } .${ this . instanceId } aborted` )
10851081 }
10861082
1087- // Ensure any image refs are normalized to base64 data URLs before persisting or sending to APIs
1083+ // Convert images to both formats for efficient dual storage
1084+ let webviewUris : string [ ] | undefined
1085+ let base64Images : string [ ] | undefined
1086+
10881087 if ( Array . isArray ( images ) && images . length > 0 ) {
10891088 try {
10901089 const { normalizeImageRefsToDataUrls } = await import ( "../../integrations/misc/imageDataUrl" )
1091- images = await normalizeImageRefsToDataUrls ( images )
1090+
1091+ // Store original webview URIs/file paths for frontend
1092+ webviewUris = images
1093+
1094+ // Convert to base64 for API calls
1095+ base64Images = await normalizeImageRefsToDataUrls ( images )
10921096 } catch ( e ) {
10931097 console . error ( "[Task#say] Failed to normalize image refs:" , e )
1098+ // Fall back to original images if conversion fails
1099+ webviewUris = images
10941100 }
10951101 }
10961102
@@ -1104,7 +1110,8 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
11041110 if ( isUpdatingPreviousPartial ) {
11051111 // Existing partial message, so update it.
11061112 lastMessage . text = text
1107- lastMessage . images = images
1113+ lastMessage . images = webviewUris
1114+ lastMessage . imagesBase64 = base64Images
11081115 lastMessage . partial = partial
11091116 lastMessage . progressStatus = progressStatus
11101117 this . updateClineMessage ( lastMessage )
@@ -1121,7 +1128,8 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
11211128 type : "say" ,
11221129 say : type ,
11231130 text,
1124- images,
1131+ images : webviewUris ,
1132+ imagesBase64 : base64Images ,
11251133 partial,
11261134 contextCondense,
11271135 metadata : options . metadata ,
@@ -1137,7 +1145,8 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
11371145 }
11381146
11391147 lastMessage . text = text
1140- lastMessage . images = images
1148+ lastMessage . images = webviewUris
1149+ lastMessage . imagesBase64 = base64Images
11411150 lastMessage . partial = false
11421151 lastMessage . progressStatus = progressStatus
11431152 if ( options . metadata ) {
@@ -1168,7 +1177,8 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
11681177 type : "say" ,
11691178 say : type ,
11701179 text,
1171- images,
1180+ images : webviewUris ,
1181+ imagesBase64 : base64Images ,
11721182 contextCondense,
11731183 metadata : options . metadata ,
11741184 } )
@@ -1191,7 +1201,8 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
11911201 type : "say" ,
11921202 say : type ,
11931203 text,
1194- images,
1204+ images : webviewUris ,
1205+ imagesBase64 : base64Images ,
11951206 checkpoint,
11961207 contextCondense,
11971208 } )
@@ -1236,14 +1247,16 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
12361247
12371248 await this . providerRef . deref ( ) ?. postStateToWebview ( )
12381249
1239- // Convert webview URIs to base64 data URLs for backend storage (one-time conversion)
1240- const { normalizeImageRefsToDataUrls } = await import ( "../../integrations/misc/imageDataUrl" )
1241- const base64Images = images ? await normalizeImageRefsToDataUrls ( images ) : undefined
1242-
1243- await this . say ( "text" , task , base64Images ) // Store base64 in backend messages
1250+ // Store the task message with both webview URIs and base64
1251+ // This is now handled in say() method which stores both formats
1252+ await this . say ( "text" , task , images )
12441253 this . isInitialized = true
12451254
1246- // Convert base64 to image blocks for API (no conversion needed, already base64)
1255+ // Get base64 from the stored message for API call
1256+ const lastMessage = this . clineMessages . at ( - 1 )
1257+ const base64Images = lastMessage ?. imagesBase64
1258+
1259+ // Convert base64 to image blocks for API
12471260 const { formatResponse } = await import ( "../prompts/responses" )
12481261 let imageBlocks : Anthropic . ImageBlockParam [ ] = formatResponse . imageBlocks ( base64Images )
12491262
@@ -1509,11 +1522,11 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
15091522 }
15101523
15111524 if ( responseImages && responseImages . length > 0 ) {
1512- // Convert webview URIs to base64 data URLs for backend storage (one-time conversion)
1525+ // Images from user response are webview URIs, convert to base64 for API
15131526 const { normalizeImageRefsToDataUrls } = await import ( "../../integrations/misc/imageDataUrl" )
15141527 const base64ResponseImages = await normalizeImageRefsToDataUrls ( responseImages )
15151528
1516- // Convert base64 to image blocks for API (no conversion needed, already base64)
1529+ // Convert base64 to image blocks for API
15171530 const { formatResponse } = await import ( "../prompts/responses" )
15181531 const responseImageBlocks = formatResponse . imageBlocks ( base64ResponseImages )
15191532 newUserContent . push ( ...responseImageBlocks )
@@ -1778,15 +1791,19 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
17781791 )
17791792
17801793 if ( response === "messageResponse" ) {
1794+ await this . say ( "user_feedback" , text , images )
1795+
1796+ // Get base64 from the just-stored message for API call
1797+ const lastMessage = this . clineMessages . at ( - 1 )
1798+ const base64Images = lastMessage ?. imagesBase64
1799+
17811800 currentUserContent . push (
17821801 ...[
17831802 { type : "text" as const , text : formatResponse . tooManyMistakes ( text ) } ,
1784- ...formatResponse . imageBlocks ( images ) ,
1803+ ...formatResponse . imageBlocks ( base64Images ) ,
17851804 ] ,
17861805 )
17871806
1788- await this . say ( "user_feedback" , text , images )
1789-
17901807 // Track consecutive mistake errors in telemetry.
17911808 TelemetryService . instance . captureConsecutiveMistakeError ( this . taskId )
17921809 }
0 commit comments