@@ -40,12 +40,14 @@ export function useThreadData(threadId: string, projectId: string): UseThreadDat
40
40
const messagesLoadedRef = useRef ( false ) ;
41
41
const agentRunsCheckedRef = useRef ( false ) ;
42
42
const hasInitiallyScrolled = useRef < boolean > ( false ) ;
43
+
43
44
44
45
const threadQuery = useThreadQuery ( threadId ) ;
45
46
const messagesQuery = useMessagesQuery ( threadId ) ;
46
47
const projectQuery = useProjectQuery ( projectId ) ;
47
48
const agentRunsQuery = useAgentRunsQuery ( threadId ) ;
48
-
49
+
50
+ // (debug logs removed)
49
51
50
52
useEffect ( ( ) => {
51
53
let isMounted = true ;
@@ -54,6 +56,9 @@ export function useThreadData(threadId: string, projectId: string): UseThreadDat
54
56
agentRunsCheckedRef . current = false ;
55
57
messagesLoadedRef . current = false ;
56
58
initialLoadCompleted . current = false ;
59
+
60
+ // Clear messages on thread change; fresh data will set messages
61
+ setMessages ( [ ] ) ;
57
62
58
63
async function initializeData ( ) {
59
64
if ( ! initialLoadCompleted . current ) setIsLoading ( true ) ;
@@ -78,7 +83,7 @@ export function useThreadData(threadId: string, projectId: string): UseThreadDat
78
83
}
79
84
80
85
if ( messagesQuery . data && ! messagesLoadedRef . current ) {
81
-
86
+ // (debug logs removed)
82
87
83
88
const unifiedMessages = ( messagesQuery . data || [ ] )
84
89
. filter ( ( msg ) => msg . type !== 'status' )
@@ -91,9 +96,28 @@ export function useThreadData(threadId: string, projectId: string): UseThreadDat
91
96
metadata : msg . metadata || '{}' ,
92
97
created_at : msg . created_at || new Date ( ) . toISOString ( ) ,
93
98
updated_at : msg . updated_at || new Date ( ) . toISOString ( ) ,
99
+ agent_id : ( msg as any ) . agent_id ,
100
+ agents : ( msg as any ) . agents ,
94
101
} ) ) ;
95
102
96
- setMessages ( unifiedMessages ) ;
103
+ // Merge with any local messages that are not present in server data yet
104
+ const serverIds = new Set (
105
+ unifiedMessages . map ( ( m ) => m . message_id ) . filter ( Boolean ) as string [ ] ,
106
+ ) ;
107
+ const localExtras = ( messages || [ ] ) . filter (
108
+ ( m ) =>
109
+ ! m . message_id ||
110
+ ( typeof m . message_id === 'string' && m . message_id . startsWith ( 'temp-' ) ) ||
111
+ ! serverIds . has ( m . message_id as string ) ,
112
+ ) ;
113
+ const mergedMessages = [ ...unifiedMessages , ...localExtras ] . sort ( ( a , b ) => {
114
+ const aTime = a . created_at ? new Date ( a . created_at ) . getTime ( ) : 0 ;
115
+ const bTime = b . created_at ? new Date ( b . created_at ) . getTime ( ) : 0 ;
116
+ return aTime - bTime ;
117
+ } ) ;
118
+
119
+ setMessages ( mergedMessages ) ;
120
+ // Messages set only from server merge; no cross-thread cache
97
121
messagesLoadedRef . current = true ;
98
122
99
123
if ( ! hasInitiallyScrolled . current ) {
@@ -102,7 +126,7 @@ export function useThreadData(threadId: string, projectId: string): UseThreadDat
102
126
}
103
127
104
128
if ( agentRunsQuery . data && ! agentRunsCheckedRef . current && isMounted ) {
105
-
129
+ // (debug logs removed)
106
130
107
131
agentRunsCheckedRef . current = true ;
108
132
@@ -169,12 +193,17 @@ export function useThreadData(threadId: string, projectId: string): UseThreadDat
169
193
agentRunsQuery . data
170
194
] ) ;
171
195
172
- // Disabled automatic message replacement to prevent optimistic message deletion
173
- // Messages are now only loaded on initial page load and updated via streaming
196
+ // Force message reload when thread changes or new data arrives
174
197
useEffect ( ( ) => {
175
- if ( messagesQuery . data && messagesQuery . status === 'success' ) {
176
- // Only load messages on initial load, not when agent status changes
177
- if ( ! isLoading && messages . length === 0 ) {
198
+ if ( messagesQuery . data && messagesQuery . status === 'success' && ! isLoading ) {
199
+ // (debug logs removed)
200
+
201
+ // Always reload messages when thread data changes or we have more raw messages than processed
202
+ const shouldReload = messages . length === 0 || messagesQuery . data . length > messages . length + 50 ; // Allow for status messages
203
+
204
+ if ( shouldReload ) {
205
+ // (debug logs removed)
206
+
178
207
const unifiedMessages = ( messagesQuery . data || [ ] )
179
208
. filter ( ( msg ) => msg . type !== 'status' )
180
209
. map ( ( msg : ApiMessageType ) => ( {
@@ -190,7 +219,28 @@ export function useThreadData(threadId: string, projectId: string): UseThreadDat
190
219
agents : ( msg as any ) . agents ,
191
220
} ) ) ;
192
221
193
- setMessages ( unifiedMessages ) ;
222
+ // Merge strategy: preserve any local (optimistic/streamed) messages not in server yet
223
+ setMessages ( ( prev ) => {
224
+ const serverIds = new Set (
225
+ unifiedMessages . map ( ( m ) => m . message_id ) . filter ( Boolean ) as string [ ] ,
226
+ ) ;
227
+ const localExtras = ( prev || [ ] ) . filter (
228
+ ( m ) =>
229
+ ! m . message_id ||
230
+ ( typeof m . message_id === 'string' && m . message_id . startsWith ( 'temp-' ) ) ||
231
+ ! serverIds . has ( m . message_id as string ) ,
232
+ ) ;
233
+ const merged = [ ...unifiedMessages , ...localExtras ] . sort ( ( a , b ) => {
234
+ const aTime = a . created_at ? new Date ( a . created_at ) . getTime ( ) : 0 ;
235
+ const bTime = b . created_at ? new Date ( b . created_at ) . getTime ( ) : 0 ;
236
+ return aTime - bTime ;
237
+ } ) ;
238
+
239
+ // Messages set only from server merge; no cross-thread cache
240
+ return merged ;
241
+ } ) ;
242
+ } else {
243
+ // (debug logs removed)
194
244
}
195
245
}
196
246
} , [ messagesQuery . data , messagesQuery . status , isLoading , messages . length , threadId ] ) ;
0 commit comments