@@ -12,6 +12,7 @@ import { VFS_DIR_TO_RESOURCE } from '@/lib/copilot/resource-types'
1212import { isWorkflowToolName } from '@/lib/copilot/workflow-tools'
1313import { getNextWorkflowColor } from '@/lib/workflows/colors'
1414import { invalidateResourceQueries } from '@/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-registry'
15+ import { deploymentKeys } from '@/hooks/queries/deployments'
1516import {
1617 type TaskChatHistory ,
1718 type TaskStoredContentBlock ,
@@ -21,6 +22,7 @@ import {
2122 taskKeys ,
2223 useChatHistory ,
2324} from '@/hooks/queries/tasks'
25+ import { workflowKeys } from '@/hooks/queries/workflows'
2426import { getTopInsertionSortOrder } from '@/hooks/queries/utils/top-insertion-sort-order'
2527import { useExecutionStream } from '@/hooks/use-execution-stream'
2628import { useExecutionStore } from '@/stores/execution/store'
@@ -74,6 +76,8 @@ const STATE_TO_STATUS: Record<string, ToolCallStatus> = {
7476 skipped : 'success' ,
7577} as const
7678
79+ const DEPLOY_TOOL_NAMES = new Set ( [ 'deploy_api' , 'deploy_chat' , 'deploy_mcp' , 'redeploy' ] )
80+
7781function mapStoredBlock ( block : TaskStoredContentBlock ) : ContentBlock {
7882 const mapped : ContentBlock = {
7983 type : block . type as ContentBlockType ,
@@ -361,6 +365,15 @@ export function useChat(
361365
362366 useEffect ( ( ) => {
363367 if ( ! chatHistory || appliedChatIdRef . current === chatHistory . id ) return
368+
369+ const activeStreamId = chatHistory . activeStreamId
370+ const snapshot = chatHistory . streamSnapshot
371+
372+ if ( activeStreamId && ! snapshot && ! sendingRef . current ) {
373+ queryClient . invalidateQueries ( { queryKey : taskKeys . detail ( chatHistory . id ) } )
374+ return
375+ }
376+
364377 appliedChatIdRef . current = chatHistory . id
365378 setMessages ( chatHistory . messages . map ( mapStoredMessage ) )
366379
@@ -374,11 +387,6 @@ export function useChat(
374387 }
375388 }
376389
377- // Kick off stream reconnection immediately if there's an active stream.
378- // The stream snapshot was fetched in parallel with the chat history (same
379- // API call), so there's no extra round-trip.
380- const activeStreamId = chatHistory . activeStreamId
381- const snapshot = chatHistory . streamSnapshot
382390 if ( activeStreamId && ! sendingRef . current ) {
383391 const gen = ++ streamGenRef . current
384392 const abortController = new AbortController ( )
@@ -396,8 +404,7 @@ export function useChat(
396404 const batchEvents = snapshot ?. events ?? [ ]
397405 const streamStatus = snapshot ?. status ?? ''
398406
399- if ( ! snapshot || ( batchEvents . length === 0 && streamStatus === 'unknown' ) ) {
400- // No snapshot available — stream buffer expired. Clean up.
407+ if ( batchEvents . length === 0 && streamStatus === 'unknown' ) {
401408 const cid = chatIdRef . current
402409 if ( cid ) {
403410 fetch ( '/api/mothership/chat/stop' , {
@@ -462,7 +469,7 @@ export function useChat(
462469 }
463470 reconnect ( )
464471 }
465- } , [ chatHistory , workspaceId ] )
472+ } , [ chatHistory , workspaceId , queryClient ] )
466473
467474 useEffect ( ( ) => {
468475 if ( resources . length === 0 ) {
@@ -686,6 +693,33 @@ export function useChat(
686693 onResourceEventRef . current ?.( )
687694 }
688695 }
696+
697+ if ( DEPLOY_TOOL_NAMES . has ( tc . name ) && tc . status === 'success' ) {
698+ const output = tc . result ?. output as Record < string , unknown > | undefined
699+ const deployedWorkflowId = ( output ?. workflowId as string ) ?? undefined
700+ if ( deployedWorkflowId && typeof output ?. isDeployed === 'boolean' ) {
701+ const isDeployed = output . isDeployed as boolean
702+ const serverDeployedAt = output . deployedAt
703+ ? new Date ( output . deployedAt as string )
704+ : undefined
705+ useWorkflowRegistry
706+ . getState ( )
707+ . setDeploymentStatus (
708+ deployedWorkflowId ,
709+ isDeployed ,
710+ isDeployed ? ( serverDeployedAt ?? new Date ( ) ) : undefined
711+ )
712+ queryClient . invalidateQueries ( {
713+ queryKey : deploymentKeys . info ( deployedWorkflowId ) ,
714+ } )
715+ queryClient . invalidateQueries ( {
716+ queryKey : deploymentKeys . versions ( deployedWorkflowId ) ,
717+ } )
718+ queryClient . invalidateQueries ( {
719+ queryKey : workflowKeys . list ( workspaceId ) ,
720+ } )
721+ }
722+ }
689723 }
690724
691725 break
@@ -1116,11 +1150,6 @@ export function useChat(
11161150 useEffect ( ( ) => {
11171151 return ( ) => {
11181152 streamGenRef . current ++
1119- // Only drop the browser→Sim read; the Sim→Go stream stays open
1120- // so the backend can finish persisting. Explicit abort is only
1121- // triggered by the stop button via /api/copilot/chat/abort.
1122- abortControllerRef . current ?. abort ( )
1123- abortControllerRef . current = null
11241153 sendingRef . current = false
11251154 }
11261155 } , [ ] )
0 commit comments