@@ -12,6 +12,7 @@ import {
1212 type OrganizationAllowList ,
1313 type CloudOrganizationMembership ,
1414 type ExtensionMessage ,
15+ type ClineMessage ,
1516 type ExtensionState ,
1617 type MarketplaceInstalledMetadata ,
1718 type SkillMetadata ,
@@ -186,6 +187,12 @@ export const mergeExtensionState = (prevState: ExtensionState, newState: Partial
186187 ) {
187188 rest . clineMessages = prevState . clineMessages
188189 rest . clineMessagesSeq = prevState . clineMessagesSeq
190+ rest . currentTaskId = prevState . currentTaskId
191+ } else if ( newState . clineMessages !== undefined ) {
192+ rest . currentTaskId = newState . currentTaskId
193+ } else {
194+ // Pushes without messages cannot establish task identity.
195+ rest . currentTaskId = prevState . currentTaskId
189196 }
190197
191198 // Note that we completely replace the previous apiConfiguration and customSupportPrompts objects
@@ -291,6 +298,7 @@ export const ExtensionStateContextProvider: React.FC<{
291298} > = ( { children, initialState } ) => {
292299 const [ historyCache ] = useState ( ( ) => new StringCache ( ) )
293300 const [ messageCache ] = useState ( ( ) => new StringCache ( ( value ) => ! ( "partial" in value ) || value . partial !== true ) )
301+ const messagesRef = useRef ( initialState ?. clineMessages ?? [ ] )
294302 const currentTaskIdRef = useRef ( initialState ?. currentTaskId )
295303 const clineMessagesSeqRef = useRef ( initialState ?. clineMessagesSeq )
296304
@@ -360,23 +368,38 @@ export const ExtensionStateContextProvider: React.FC<{
360368 switch ( message . type ) {
361369 case "state" : {
362370 const newState = message . state ?? { }
363- if ( Object . prototype . hasOwnProperty . call ( newState , "currentTaskId" ) ) {
364- if ( newState . currentTaskId !== currentTaskIdRef . current ) {
365- messageCache . clear ( )
366- currentTaskIdRef . current = newState . currentTaskId
367- }
368- }
369371
370372 const messagesAreStale =
371373 newState . clineMessages !== undefined &&
372374 newState . clineMessagesSeq !== undefined &&
373375 clineMessagesSeqRef . current !== undefined &&
374376 newState . clineMessagesSeq <= clineMessagesSeqRef . current
375377
378+ if ( ! messagesAreStale && newState . clineMessages !== undefined ) {
379+ if ( newState . currentTaskId !== currentTaskIdRef . current ) {
380+ messageCache . clear ( )
381+ messagesRef . current = [ ]
382+ currentTaskIdRef . current = newState . currentTaskId
383+ }
384+ }
385+
376386 if ( newState . clineMessages && ! messagesAreStale ) {
387+ const previousMessages = new Map ( messagesRef . current . map ( ( item ) => [ item . ts , item ] ) )
377388 for ( const clineMessage of newState . clineMessages ) {
378- messageCache . intern ( clineMessage )
389+ const previous = previousMessages . get ( clineMessage . ts )
390+ if (
391+ previous &&
392+ Object . keys ( clineMessage ) . length === Object . keys ( previous ) . length &&
393+ Object . entries ( clineMessage ) . every (
394+ ( [ key , value ] ) => previous [ key as keyof ClineMessage ] === value ,
395+ )
396+ ) {
397+ Object . assign ( clineMessage , previous )
398+ } else {
399+ messageCache . intern ( clineMessage )
400+ }
379401 }
402+ messagesRef . current = newState . clineMessages
380403 }
381404 if (
382405 newState . clineMessagesSeq !== undefined &&
@@ -386,6 +409,7 @@ export const ExtensionStateContextProvider: React.FC<{
386409 clineMessagesSeqRef . current = newState . clineMessagesSeq
387410 }
388411 if ( newState . taskHistory ) {
412+ historyCache . clear ( )
389413 historyCache . intern ( newState . taskHistory )
390414 }
391415 if ( newState . currentTaskItem ) {
@@ -455,11 +479,17 @@ export const ExtensionStateContextProvider: React.FC<{
455479 }
456480 case "messageUpdated" : {
457481 const clineMessage = message . clineMessage !
482+ const messageIndex = messagesRef . current . findIndex ( ( item ) => item . ts === clineMessage . ts )
483+ if ( messageIndex !== - 1 ) {
484+ messageCache . intern ( clineMessage )
485+ messagesRef . current = messagesRef . current . map ( ( item , index ) =>
486+ index === messageIndex ? clineMessage : item ,
487+ )
488+ }
458489 setState ( ( prevState ) => {
459490 // worth noting it will never be possible for a more up-to-date message to be sent here or in normal messages post since the presentAssistantContent function uses lock
460491 const lastIndex = findLastIndex ( prevState . clineMessages , ( msg ) => msg . ts === clineMessage . ts )
461492 if ( lastIndex !== - 1 ) {
462- messageCache . intern ( clineMessage )
463493 const newClineMessages = [ ...prevState . clineMessages ]
464494 newClineMessages [ lastIndex ] = clineMessage
465495 return { ...prevState , clineMessages : newClineMessages }
@@ -520,6 +550,7 @@ export const ExtensionStateContextProvider: React.FC<{
520550 case "taskHistoryUpdated" : {
521551 // Efficiently update just the task history without replacing entire state
522552 if ( message . taskHistory !== undefined ) {
553+ historyCache . clear ( )
523554 historyCache . intern ( message . taskHistory )
524555 setState ( ( prevState ) => ( {
525556 ...prevState ,
0 commit comments