@@ -1544,6 +1544,8 @@ export function useChat(
15441544 const resourcesRef = useRef ( resources )
15451545 resourcesRef . current = resources
15461546 const pendingPersistResourceKeysRef = useRef < Set < string > > ( new Set ( ) )
1547+ const inFlightResourceAddsRef = useRef < Map < string , Promise < unknown > > > ( new Map ( ) )
1548+ const reorderNeededAfterFlushRef = useRef ( false )
15471549
15481550 // Derive the effective active resource ID — auto-selects the last resource when the stored ID is
15491551 // absent or no longer in the list, avoiding a separate Effect-based state correction loop.
@@ -1971,6 +1973,8 @@ export function useChat(
19711973 setResources ( [ ] )
19721974 setActiveResourceId ( null )
19731975 pendingPersistResourceKeysRef . current . clear ( )
1976+ inFlightResourceAddsRef . current . clear ( )
1977+ reorderNeededAfterFlushRef . current = false
19741978 resetEphemeralPreviewState ( )
19751979 setMessageQueue ( [ ] )
19761980 clearQueueDispatchState ( )
@@ -1983,20 +1987,42 @@ export function useChat(
19831987 setTransportIdle ,
19841988 ] )
19851989
1986- const flushPendingResources = useCallback ( ( chatId : string ) => {
1990+ const flushPendingResources = useCallback ( async ( chatId : string ) => {
19871991 const pendingKeys = pendingPersistResourceKeysRef . current
19881992 if ( pendingKeys . size === 0 ) return
1993+ const flushPromises : Array < Promise < unknown > > = [ ]
19891994 for ( const resource of resourcesRef . current ) {
1995+ if ( resource . id === 'streaming-file' ) continue
19901996 const key = `${ resource . type } :${ resource . id } `
19911997 if ( ! pendingKeys . has ( key ) ) continue
19921998 pendingKeys . delete ( key )
1993- requestJson ( addMothershipChatResourceContract , { body : { chatId, resource } } ) . catch (
1994- ( err ) => {
1999+ const promise = requestJson ( addMothershipChatResourceContract , {
2000+ body : { chatId, resource } ,
2001+ } )
2002+ . catch ( ( err ) => {
19952003 pendingPersistResourceKeysRef . current . add ( key )
19962004 logger . warn ( 'Failed to flush pending resource; will retry on next hydration' , err )
1997- }
1998- )
2005+ } )
2006+ . finally ( ( ) => {
2007+ inFlightResourceAddsRef . current . delete ( key )
2008+ } )
2009+ inFlightResourceAddsRef . current . set ( key , promise )
2010+ flushPromises . push ( promise )
19992011 }
2012+ if ( flushPromises . length === 0 ) return
2013+ await Promise . allSettled ( flushPromises )
2014+ if ( ! reorderNeededAfterFlushRef . current ) return
2015+ reorderNeededAfterFlushRef . current = false
2016+ const localOrder = resourcesRef . current . filter (
2017+ ( r ) =>
2018+ r . id !== 'streaming-file' && ! pendingPersistResourceKeysRef . current . has ( `${ r . type } :${ r . id } ` )
2019+ )
2020+ if ( localOrder . length === 0 ) return
2021+ requestJson ( reorderMothershipChatResourcesContract , {
2022+ body : { chatId, resources : localOrder } ,
2023+ } ) . catch ( ( err ) => {
2024+ logger . warn ( 'Failed to sync resource order after flush' , err )
2025+ } )
20002026 } , [ ] )
20012027
20022028 const adoptResolvedChatId = useCallback (
@@ -2046,12 +2072,17 @@ export function useChat(
20462072 const persistChatId = chatIdRef . current ?? selectedChatIdRef . current
20472073 const key = `${ resource . type } :${ resource . id } `
20482074 if ( persistChatId ) {
2049- requestJson ( addMothershipChatResourceContract , {
2075+ const promise = requestJson ( addMothershipChatResourceContract , {
20502076 body : { chatId : persistChatId , resource } ,
2051- } ) . catch ( ( err ) => {
2052- pendingPersistResourceKeysRef . current . add ( key )
2053- logger . warn ( 'Failed to persist resource; will retry on next hydration' , err )
20542077 } )
2078+ . catch ( ( err ) => {
2079+ pendingPersistResourceKeysRef . current . add ( key )
2080+ logger . warn ( 'Failed to persist resource; will retry on next hydration' , err )
2081+ } )
2082+ . finally ( ( ) => {
2083+ inFlightResourceAddsRef . current . delete ( key )
2084+ } )
2085+ inFlightResourceAddsRef . current . set ( key , promise )
20552086 } else {
20562087 pendingPersistResourceKeysRef . current . add ( key )
20572088 }
@@ -2064,23 +2095,33 @@ export function useChat(
20642095
20652096 const key = `${ resourceType } :${ resourceId } `
20662097 const wasPending = pendingPersistResourceKeysRef . current . delete ( key )
2067- if ( wasPending ) return
2098+ const inFlightAdd = inFlightResourceAddsRef . current . get ( key )
2099+ if ( wasPending && ! inFlightAdd ) return
20682100
20692101 const persistChatId = chatIdRef . current ?? selectedChatIdRef . current
2070- if ( persistChatId ) {
2102+ if ( ! persistChatId ) return
2103+ const fireDelete = ( ) => {
20712104 requestJson ( removeMothershipChatResourceContract , {
20722105 body : { chatId : persistChatId , resourceType, resourceId } ,
20732106 } ) . catch ( ( err ) => {
20742107 logger . warn ( 'Failed to persist resource removal' , err )
20752108 } )
20762109 }
2110+ if ( inFlightAdd ) {
2111+ inFlightAdd . finally ( fireDelete )
2112+ } else {
2113+ fireDelete ( )
2114+ }
20772115 } , [ ] )
20782116
20792117 const reorderResources = useCallback ( ( newOrder : MothershipResource [ ] ) => {
20802118 setResources ( newOrder )
2119+ const pendingKeys = pendingPersistResourceKeysRef . current
2120+ if ( pendingKeys . size > 0 ) {
2121+ reorderNeededAfterFlushRef . current = true
2122+ }
20812123 const persistChatId = chatIdRef . current ?? selectedChatIdRef . current
20822124 if ( ! persistChatId ) return
2083- const pendingKeys = pendingPersistResourceKeysRef . current
20842125 const persistableResources = newOrder . filter (
20852126 ( r ) => r . id !== 'streaming-file' && ! pendingKeys . has ( `${ r . type } :${ r . id } ` )
20862127 )
@@ -2220,6 +2261,8 @@ export function useChat(
22202261 setResources ( [ ] )
22212262 setActiveResourceId ( null )
22222263 pendingPersistResourceKeysRef . current . clear ( )
2264+ inFlightResourceAddsRef . current . clear ( )
2265+ reorderNeededAfterFlushRef . current = false
22232266 resetEphemeralPreviewState ( )
22242267 setMessageQueue ( [ ] )
22252268 clearQueueDispatchState ( )
0 commit comments