@@ -1345,12 +1345,6 @@ export function useChat(
13451345 const queueDispatchActionsRef = useRef < QueueDispatchAction [ ] > ( [ ] )
13461346 const queueDispatchTaskRef = useRef < Promise < void > | null > ( null )
13471347 const queueDispatchEpochRef = useRef ( 0 )
1348- /**
1349- * Set when the in-flight dispatch was killed by the unmount cleanup before
1350- * reaching the server. Lets the restore path re-queue the message across the
1351- * epoch bump that same cleanup performs.
1352- */
1353- const restorableCleanupAbortRef = useRef ( false )
13541348 const queueDispatchLoopRef = useRef < ( ) => Promise < void > > ( async ( ) => { } )
13551349 const enqueueQueueDispatchRef = useRef < ( action : QueueDispatchActionInput ) => Promise < void > > (
13561350 async ( ) => { }
@@ -3842,14 +3836,12 @@ export function useChat(
38423836 ( err instanceof Error && err . name === 'AbortError' ) || sendAbortSignal ?. aborted === true
38433837 if ( sendWasAborted ) {
38443838 if ( sendAbortSignal ?. reason === 'unmount:client_cleanup' && ! sendReachedServer ) {
3845- /* The mount-settling effect cycle (Suspense hide/reveal) ran the
3846- unmount cleanup while this send was still pre-dispatch. Nothing
3847- reached the server, so the send is fully recoverable: withdraw
3848- the optimistic pair and report not-consumed so the queued entry
3849- is restored and re-dispatched when effects re-run. */
3839+ /* The mount-settling remount ran the unmount cleanup while this
3840+ send was still pre-dispatch. Nothing reached the server, so the
3841+ send is fully recoverable: withdraw the optimistic pair and
3842+ report the distinct outcome so the dispatcher redelivers it. */
38503843 rollbackOptimisticSend ( )
3851- restorableCleanupAbortRef . current = true
3852- return false
3844+ return 'recoverable_cleanup_abort'
38533845 }
38543846 return consumedByTranscript
38553847 }
@@ -4491,15 +4483,18 @@ export function useChat(
44914483 useMothershipQueueStore . getState ( ) . remove ( dispatchChatKey , msg . id )
44924484 }
44934485
4494- const restoreQueuedMessage = ( handoff ?: QueuedSendHandoffSeed ) => {
4486+ const restoreQueuedMessage = (
4487+ handoff ?: QueuedSendHandoffSeed ,
4488+ recoverableCleanupAbort = false
4489+ ) => {
44954490 if ( ! handoff ) {
44964491 clearQueuedSendHandoffState ( msg . id )
44974492 }
44984493 clearQueuedSendHandoffClaim ( msg . id )
44994494 if ( ! removedFromQueue ) {
45004495 return
45014496 }
4502- if ( options . epoch !== queueDispatchEpochRef . current && ! restorableCleanupAbortRef . current ) {
4497+ if ( options . epoch !== queueDispatchEpochRef . current && ! recoverableCleanupAbort ) {
45034498 return
45044499 }
45054500 // If the user explicitly removed this message during dispatch, honor
@@ -4515,7 +4510,7 @@ export function useChat(
45154510 key is the stable chat id). Attachment payloads exceed what the
45164511 handoff carries, so they fall back to the queue restore. */
45174512 if (
4518- restorableCleanupAbortRef . current &&
4513+ recoverableCleanupAbort &&
45194514 dispatchChatKey . startsWith ( PENDING_CHAT_KEY_PREFIX ) &&
45204515 ! msg . fileAttachments ?. length
45214516 ) {
@@ -4552,8 +4547,7 @@ export function useChat(
45524547 // between dispatch scheduling and this send.
45534548 const liveMsg = queueAtSend [ currentIndex ]
45544549 activeQueuedSendHandoff = options . queuedSendHandoff ?? liveMsg . queuedSendHandoff
4555- restorableCleanupAbortRef . current = false
4556- const consumed = await startSendMessage (
4550+ const sendResult = await startSendMessage (
45574551 liveMsg . content ,
45584552 liveMsg . fileAttachments ,
45594553 liveMsg . contexts ,
@@ -4562,13 +4556,12 @@ export function useChat(
45624556 activeQueuedSendHandoff
45634557 )
45644558
4565- if ( ! consumed ) {
4566- restoreQueuedMessage ( activeQueuedSendHandoff )
4559+ if ( sendResult !== true ) {
4560+ restoreQueuedMessage ( activeQueuedSendHandoff , sendResult === 'recoverable_cleanup_abort' )
45674561 }
45684562 } catch {
45694563 restoreQueuedMessage ( activeQueuedSendHandoff )
45704564 } finally {
4571- restorableCleanupAbortRef . current = false
45724565 setDispatchingHeadId ( ( current ) => ( current === msg . id ? null : current ) )
45734566 queuedMessageDispatchIdsRef . current . delete ( msg . id )
45744567 userRemovedDuringDispatchRef . current . delete ( msg . id )
0 commit comments