@@ -59,6 +59,7 @@ import { useCurrentWorkflow } from '@/app/workspace/[workspaceId]/w/[workflowId]
5959import { useWorkflowExecution } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-workflow-execution'
6060import { getWorkflowLockToggleIds } from '@/app/workspace/[workspaceId]/w/[workflowId]/utils'
6161import { useDeleteWorkflow , useImportWorkflow } from '@/app/workspace/[workspaceId]/w/hooks'
62+ import { useCopilotChatSelection } from '@/hooks/queries/copilot-chat-selection'
6263import { useDuplicateWorkflowMutation , useWorkflowMap } from '@/hooks/queries/workflows'
6364import { useCollaborativeWorkflow } from '@/hooks/use-collaborative-workflow'
6465import { usePermissionConfig } from '@/hooks/use-permission-config'
@@ -232,12 +233,48 @@ export const Panel = memo(function Panel({ workspaceId: propWorkspaceId }: Panel
232233 const copilotChatIdRef = useRef ( copilotChatId )
233234 copilotChatIdRef . current = copilotChatId
234235 const copilotInitialLoadDoneRef = useRef ( false )
236+ // Tracks the live workflow so async chat-list fetches can detect
237+ // workflow switches that happened mid-flight and bail out.
238+ const activeWorkflowIdRef = useRef ( activeWorkflowId )
239+ activeWorkflowIdRef . current = activeWorkflowId
240+
241+ // Per-workflow chat memory: switching A→B→A returns to A's last-used chat
242+ // instead of jumping to A's most recent. Backed by the React Query cache so
243+ // it survives in-session workflow switches and clears on hard refresh.
244+ const { getChatId : getRememberedChatId , setChatId : setRememberedChatId } =
245+ useCopilotChatSelection ( )
246+ const lastWorkflowIdRef = useRef < string | null > ( null )
247+
248+ useEffect ( ( ) => {
249+ const previous = lastWorkflowIdRef . current
250+ lastWorkflowIdRef . current = activeWorkflowId ?? null
251+ if ( previous === activeWorkflowId ) return
252+
253+ if ( previous && copilotChatIdRef . current ) {
254+ setRememberedChatId ( previous , copilotChatIdRef . current )
255+ }
256+
257+ if ( activeWorkflowId ) {
258+ const remembered = getRememberedChatId ( activeWorkflowId )
259+ setCopilotChatId ( remembered )
260+ setCopilotChatTitle ( null )
261+ } else {
262+ setCopilotChatId ( undefined )
263+ setCopilotChatTitle ( null )
264+ }
265+ } , [ activeWorkflowId , getRememberedChatId , setRememberedChatId ] )
235266
236267 const loadCopilotChats = useCallback ( ( ) => {
237268 if ( ! activeWorkflowId ) return
269+ const requestWorkflowId = activeWorkflowId
238270 fetch ( '/api/copilot/chats' )
239271 . then ( ( res ) => ( res . ok ? res . json ( ) : { chats : [ ] } ) )
240272 . then ( ( data ) => {
273+ // Stale-fetch guard: bail if the user switched workflows mid-flight.
274+ // Without this the in-flight response would clobber the new
275+ // workflow's state (filtering against the old workflow id, clearing
276+ // the restored chat, and auto-selecting the wrong list's first chat).
277+ if ( requestWorkflowId !== activeWorkflowIdRef . current ) return
241278 const allChats = Array . isArray ( data ?. chats ) ? data . chats : [ ]
242279 const filtered = allChats . filter (
243280 ( c : { workflowId ?: string } ) => c . workflowId === activeWorkflowId
@@ -250,20 +287,29 @@ export const Panel = memo(function Panel({ workspaceId: propWorkspaceId }: Panel
250287 setCopilotChatList ( filtered )
251288
252289 const currentId = copilotChatIdRef . current
290+ let resolvedCurrentId = currentId
253291 if ( currentId ) {
254292 const match = filtered . find ( ( c : { id : string } ) => c . id === currentId )
255- if ( match ?. title ) setCopilotChatTitle ( match . title )
293+ if ( match ) {
294+ if ( match . title ) setCopilotChatTitle ( match . title )
295+ } else {
296+ // Remembered chat was deleted (here or in another tab). Drop it
297+ // so the next send doesn't hit a 404, and forget it for next time.
298+ setRememberedChatId ( activeWorkflowId , undefined )
299+ setCopilotChatId ( undefined )
300+ setCopilotChatTitle ( null )
301+ resolvedCurrentId = undefined
302+ }
256303 }
257304
258- if ( ! copilotInitialLoadDoneRef . current && ! currentId && filtered . length > 0 ) {
259- copilotInitialLoadDoneRef . current = true
305+ if ( ! copilotInitialLoadDoneRef . current && ! resolvedCurrentId && filtered . length > 0 ) {
260306 setCopilotChatId ( filtered [ 0 ] . id )
261307 setCopilotChatTitle ( filtered [ 0 ] . title )
262308 }
263309 copilotInitialLoadDoneRef . current = true
264310 } )
265311 . catch ( ( ) => { } )
266- } , [ activeWorkflowId ] )
312+ } , [ activeWorkflowId , setRememberedChatId ] )
267313
268314 useEffect ( ( ) => {
269315 copilotInitialLoadDoneRef . current = false
@@ -291,12 +337,15 @@ export const Panel = memo(function Panel({ workspaceId: propWorkspaceId }: Panel
291337 if ( copilotChatId === chatId ) {
292338 setCopilotChatId ( undefined )
293339 setCopilotChatTitle ( null )
340+ if ( activeWorkflowId ) {
341+ setRememberedChatId ( activeWorkflowId , undefined )
342+ }
294343 }
295344 loadCopilotChats ( )
296345 } )
297346 . catch ( ( ) => { } )
298347 } ,
299- [ copilotChatId , loadCopilotChats ]
348+ [ copilotChatId , loadCopilotChats , activeWorkflowId , setRememberedChatId ]
300349 )
301350
302351 const handleCopilotToolResult = useCallback (
0 commit comments