@@ -721,8 +721,8 @@ export function useChat(
721721 const clearQueueDispatchState = useCallback ( ( ) => {
722722 queueDispatchEpochRef . current ++
723723 queueDispatchActionsRef . current = [ ]
724- queuedMessageDispatchIdsRef . current . clear ( )
725- userRemovedDuringDispatchRef . current . clear ( )
724+ queuedMessageDispatchIds . clear ( )
725+ userRemovedDuringDispatch . clear ( )
726726 queueDispatchTaskRef . current = null
727727 setDispatchingHeadId ( null )
728728 } , [ ] )
@@ -868,11 +868,13 @@ export function useChat(
868868 )
869869 const editingQueuedId = useMothershipQueueStore ( ( state ) => state . editing [ chatKey ] ?? null )
870870 const [ dispatchingHeadId , setDispatchingHeadId ] = useState < string | null > ( null )
871- const queuedMessageDispatchIdsRef = useRef < Set < string > > ( new Set ( ) )
871+ const queuedMessageDispatchIdsRef = useRef < Set < string > | null > ( null )
872+ const queuedMessageDispatchIds = ( queuedMessageDispatchIdsRef . current ??= new Set ( ) )
872873 // Ids the user explicitly removed while a dispatch was in flight — used to
873874 // suppress the dispatch's failure-restore path, which would otherwise undo
874875 // the user's removal silently.
875- const userRemovedDuringDispatchRef = useRef < Set < string > > ( new Set ( ) )
876+ const userRemovedDuringDispatchRef = useRef < Set < string > | null > ( null )
877+ const userRemovedDuringDispatch = ( userRemovedDuringDispatchRef . current ??= new Set ( ) )
876878 const queueDispatchActionsRef = useRef < QueueDispatchAction [ ] > ( [ ] )
877879 const queueDispatchTaskRef = useRef < Promise < void > | null > ( null )
878880 const queueDispatchEpochRef = useRef ( 0 )
@@ -921,7 +923,9 @@ export function useChat(
921923 const reconnectExhaustedRecheckTimerRef = useRef < ReturnType < typeof setTimeout > | null > ( null )
922924
923925 const abortControllerRef = useRef < AbortController | null > ( null )
924- const detachedChatResolutionControllersRef = useRef < Set < AbortController > > ( new Set ( ) )
926+ const detachedChatResolutionControllersRef = useRef < Set < AbortController > | null > ( null )
927+ const detachedChatResolutionControllers = ( detachedChatResolutionControllersRef . current ??=
928+ new Set ( ) )
925929 const streamReaderRef = useRef < ReadableStreamDefaultReader < Uint8Array > | null > ( null )
926930 const chatIdRef = useRef < string | undefined > ( initialChatId )
927931 const tableViewContextsRef = useRef ( {
@@ -974,9 +978,13 @@ export function useChat(
974978 const resourceActivityTrackerRef = useRef < ResourceActivityTracker | null > ( null )
975979 const streamingContentRef = useRef ( '' )
976980 const streamingBlocksRef = useRef < ContentBlock [ ] > ( [ ] )
977- const handledClientWorkflowToolIdsRef = useRef < Set < string > > ( new Set ( ) )
978- const handledClientLocalFilesystemToolIdsRef = useRef < Set < string > > ( new Set ( ) )
979- const recoveringClientWorkflowToolIdsRef = useRef < Set < string > > ( new Set ( ) )
981+ const handledClientWorkflowToolIdsRef = useRef < Set < string > | null > ( null )
982+ const handledClientWorkflowToolIds = ( handledClientWorkflowToolIdsRef . current ??= new Set ( ) )
983+ const handledClientLocalFilesystemToolIdsRef = useRef < Set < string > | null > ( null )
984+ const handledClientLocalFilesystemToolIds = ( handledClientLocalFilesystemToolIdsRef . current ??=
985+ new Set ( ) )
986+ const recoveringClientWorkflowToolIdsRef = useRef < Set < string > | null > ( null )
987+ const recoveringClientWorkflowToolIds = ( recoveringClientWorkflowToolIdsRef . current ??= new Set ( ) )
980988 const isHomePage = pathname . endsWith ( '/home' )
981989
982990 const setTransportIdle = useCallback ( ( ) => {
@@ -1532,13 +1540,13 @@ export function useChat(
15321540 if ( ! isWorkflowToolName ( toolName ) ) {
15331541 return
15341542 }
1535- if ( handledClientWorkflowToolIdsRef . current . has ( toolCallId ) ) {
1543+ if ( handledClientWorkflowToolIds . has ( toolCallId ) ) {
15361544 return
15371545 }
1538- if ( recoveringClientWorkflowToolIdsRef . current . has ( toolCallId ) ) {
1546+ if ( recoveringClientWorkflowToolIds . has ( toolCallId ) ) {
15391547 return
15401548 }
1541- handledClientWorkflowToolIdsRef . current . add ( toolCallId )
1549+ handledClientWorkflowToolIds . add ( toolCallId )
15421550
15431551 ensureWorkflowToolResource ( toolArgs )
15441552 executeRunToolOnClient ( toolCallId , toolName , toolArgs )
@@ -1554,10 +1562,10 @@ export function useChat(
15541562 ) {
15551563 return
15561564 }
1557- if ( handledClientLocalFilesystemToolIdsRef . current . has ( toolCallId ) ) {
1565+ if ( handledClientLocalFilesystemToolIds . has ( toolCallId ) ) {
15581566 return
15591567 }
1560- handledClientLocalFilesystemToolIdsRef . current . add ( toolCallId )
1568+ handledClientLocalFilesystemToolIds . add ( toolCallId )
15611569 const options = {
15621570 workspaceId,
15631571 chatId : chatIdRef . current ?? selectedChatIdRef . current ,
@@ -1677,12 +1685,12 @@ export function useChat(
16771685 if ( ! toolCall || ! isWorkflowToolName ( toolCall . name ) ) continue
16781686 if ( toolCall . status !== 'executing' ) continue
16791687 if (
1680- handledClientWorkflowToolIdsRef . current . has ( toolCall . id ) ||
1681- recoveringClientWorkflowToolIdsRef . current . has ( toolCall . id )
1688+ handledClientWorkflowToolIds . has ( toolCall . id ) ||
1689+ recoveringClientWorkflowToolIds . has ( toolCall . id )
16821690 ) {
16831691 continue
16841692 }
1685- recoveringClientWorkflowToolIdsRef . current . add ( toolCall . id )
1693+ recoveringClientWorkflowToolIds . add ( toolCall . id )
16861694 pending . push ( toolCall )
16871695 }
16881696 }
@@ -1695,15 +1703,15 @@ export function useChat(
16951703 if ( targetWorkflowId ) {
16961704 const rebound = await bindRunToolToExecution ( toolCall . id , targetWorkflowId )
16971705 if ( rebound ) {
1698- handledClientWorkflowToolIdsRef . current . add ( toolCall . id )
1706+ handledClientWorkflowToolIds . add ( toolCall . id )
16991707 continue
17001708 }
17011709 }
17021710
1703- recoveringClientWorkflowToolIdsRef . current . delete ( toolCall . id )
1711+ recoveringClientWorkflowToolIds . delete ( toolCall . id )
17041712 startClientWorkflowTool ( toolCall . id , toolCall . name , toolArgs )
17051713 } finally {
1706- recoveringClientWorkflowToolIdsRef . current . delete ( toolCall . id )
1714+ recoveringClientWorkflowToolIds . delete ( toolCall . id )
17071715 }
17081716 }
17091717 } ,
@@ -1736,7 +1744,7 @@ export function useChat(
17361744 // background so its native resources are re-keyed onto the server
17371745 // chat even though this reader is intentionally being cancelled.
17381746 const detachedResolutionController = new AbortController ( )
1739- detachedChatResolutionControllersRef . current . add ( detachedResolutionController )
1747+ detachedChatResolutionControllers . add ( detachedResolutionController )
17401748 void ( async ( ) => {
17411749 const resolution = await waitForDetachedChatResolution (
17421750 ( ) =>
@@ -1781,7 +1789,7 @@ export function useChat(
17811789 } )
17821790 } )
17831791 . finally ( ( ) => {
1784- detachedChatResolutionControllersRef . current . delete ( detachedResolutionController )
1792+ detachedChatResolutionControllers . delete ( detachedResolutionController )
17851793 } )
17861794 }
17871795 // Detach the current UI from the old stream without cancelling it on the server.
@@ -4591,17 +4599,17 @@ export function useChat(
45914599 queuedSendHandoff ?: QueuedSendHandoffSeed
45924600 }
45934601 ) => {
4594- if ( queuedMessageDispatchIdsRef . current . has ( msg . id ) ) {
4602+ if ( queuedMessageDispatchIds . has ( msg . id ) ) {
45954603 return
45964604 }
4597- queuedMessageDispatchIdsRef . current . add ( msg . id )
4605+ queuedMessageDispatchIds . add ( msg . id )
45984606
45994607 const dispatchChatKey = chatKeyRef . current
46004608 const queueAtStart =
46014609 useMothershipQueueStore . getState ( ) . queues [ dispatchChatKey ] ?? EMPTY_MESSAGE_QUEUE
46024610 let originalIndex = queueAtStart . findIndex ( ( queued ) => queued . id === msg . id )
46034611 if ( originalIndex === - 1 ) {
4604- queuedMessageDispatchIdsRef . current . delete ( msg . id )
4612+ queuedMessageDispatchIds . delete ( msg . id )
46054613 return
46064614 }
46074615
@@ -4645,7 +4653,7 @@ export function useChat(
46454653 }
46464654 // If the user explicitly removed this message during dispatch, honor
46474655 // that and don't re-insert on failure.
4648- if ( userRemovedDuringDispatchRef . current . delete ( msg . id ) ) {
4656+ if ( userRemovedDuringDispatch . delete ( msg . id ) ) {
46494657 clearQueuedSendHandoffState ( msg . id )
46504658 return
46514659 }
@@ -4724,8 +4732,8 @@ export function useChat(
47244732 restoreQueuedMessage ( activeQueuedSendHandoff )
47254733 } finally {
47264734 setDispatchingHeadId ( ( current ) => ( current === msg . id ? null : current ) )
4727- queuedMessageDispatchIdsRef . current . delete ( msg . id )
4728- userRemovedDuringDispatchRef . current . delete ( msg . id )
4735+ queuedMessageDispatchIds . delete ( msg . id )
4736+ userRemovedDuringDispatch . delete ( msg . id )
47294737 }
47304738 } ,
47314739 [ startSendMessage , handOffWithdrawnSend ]
@@ -4781,8 +4789,8 @@ export function useChat(
47814789 const removeFromQueue = useCallback ( ( id : string ) => {
47824790 // If the message is mid-dispatch, mark it so the dispatch's failure-restore
47834791 // path won't silently undo the user's removal.
4784- if ( queuedMessageDispatchIdsRef . current . has ( id ) ) {
4785- userRemovedDuringDispatchRef . current . add ( id )
4792+ if ( queuedMessageDispatchIds . has ( id ) ) {
4793+ userRemovedDuringDispatch . add ( id )
47864794 }
47874795 clearQueuedSendHandoffState ( id )
47884796 clearQueuedSendHandoffClaim ( id )
@@ -4796,7 +4804,7 @@ export function useChat(
47964804 const queue = queueState . queues [ chatKey ]
47974805 const msg = id === undefined ? queue ?. [ 0 ] : queue ?. find ( ( queued ) => queued . id === id )
47984806 if ( ! msg || queueState . editing [ chatKey ] === msg . id ) return
4799- if ( queuedMessageDispatchIdsRef . current . has ( msg . id ) ) return
4807+ if ( queuedMessageDispatchIds . has ( msg . id ) ) return
48004808 const admissionPending = hasPendingChatAdmission ( )
48014809
48024810 // Explicit queue sends should supersede any older auto-drain work scheduled by finalize().
@@ -4861,7 +4869,7 @@ export function useChat(
48614869 const editQueuedMessage = useCallback ( ( id : string ) : QueuedMessage | undefined => {
48624870 // Reject edits on a message already mid-dispatch; the slot is about to be
48634871 // dropped. UI also disables this via `dispatchingHeadId`.
4864- if ( queuedMessageDispatchIdsRef . current . has ( id ) ) return undefined
4872+ if ( queuedMessageDispatchIds . has ( id ) ) return undefined
48654873 const activeChatKey = chatKeyRef . current
48664874 const queue = useMothershipQueueStore . getState ( ) . queues [ activeChatKey ] ?? EMPTY_MESSAGE_QUEUE
48674875 const msg = queue . find ( ( m ) => m . id === id )
@@ -4890,7 +4898,7 @@ export function useChat(
48904898 )
48914899 if ( chatHistory . activeStreamId ) acceptedMessageIds . add ( chatHistory . activeStreamId )
48924900 for ( const queued of messageQueue ) {
4893- if ( queuedMessageDispatchIdsRef . current . has ( queued . id ) ) continue
4901+ if ( queuedMessageDispatchIds . has ( queued . id ) ) continue
48944902 const requestId = queued . queuedSendHandoff ?. userMessageId ?? queued . resumeUserMessageId
48954903 if ( ! requestId || ! acceptedMessageIds . has ( requestId ) ) continue
48964904 clearQueuedSendHandoffState ( queued . id )
@@ -4931,10 +4939,10 @@ export function useChat(
49314939 cancelActiveStreamReader ( )
49324940 abortControllerRef . current ?. abort ( 'unmount:client_cleanup' )
49334941 abortControllerRef . current = null
4934- for ( const controller of detachedChatResolutionControllersRef . current ) {
4942+ for ( const controller of detachedChatResolutionControllers ) {
49354943 controller . abort ( 'unmount:detached_chat_resolution' )
49364944 }
4937- detachedChatResolutionControllersRef . current . clear ( )
4945+ detachedChatResolutionControllers . clear ( )
49384946 clearActiveTurn ( )
49394947 sendingRef . current = false
49404948 // Release the editing slot — the composer it binds to is unmounting.
0 commit comments