@@ -7,10 +7,12 @@ import useWorkflowEditorStore from '@/pages/platform/workflow-editor/stores/useW
77import useWorkflowNodeDetailsPanelStore from '@/pages/platform/workflow-editor/stores/useWorkflowNodeDetailsPanelStore' ;
88import useWorkflowTestChatStore from '@/pages/platform/workflow-editor/stores/useWorkflowTestChatStore' ;
99import { useAnalytics } from '@/shared/hooks/useAnalytics' ;
10+ import { useWorkflowTestStream } from '@/shared/hooks/useWorkflowTestStream' ;
1011import { WorkflowTestApi } from '@/shared/middleware/platform/workflow/test' ;
1112import { useEnvironmentStore } from '@/shared/stores/useEnvironmentStore' ;
13+ import { getTestWorkflowAttachRequest , getTestWorkflowStreamPostRequest } from '@/shared/util/testWorkflow-utils' ;
1214import { useQueryClient } from '@tanstack/react-query' ;
13- import { RefObject , useCallback , useEffect } from 'react' ;
15+ import { RefObject , useCallback , useEffect , useState } from 'react' ;
1416import { ImperativePanelHandle } from 'react-resizable-panels' ;
1517import { useNavigate , useParams , useSearchParams } from 'react-router-dom' ;
1618import { useShallow } from 'zustand/react/shallow' ;
@@ -24,6 +26,7 @@ interface UseProjectHeaderProps {
2426}
2527
2628export const useWorkflowBuilderHeader = ( { bottomResizablePanelRef, chatTrigger, projectId} : UseProjectHeaderProps ) => {
29+ const [ jobId , setJobId ] = useState < string | null > ( null ) ;
2730 const setDataPillPanelOpen = useDataPillPanelStore ( ( state ) => state . setDataPillPanelOpen ) ;
2831 const currentEnvironmentId = useEnvironmentStore ( ( state ) => state . currentEnvironmentId ) ;
2932 const workflow = useWorkflowDataStore ( ( state ) => state . workflow ) ;
@@ -98,7 +101,20 @@ export const useWorkflowBuilderHeader = ({bottomResizablePanelRef, chatTrigger,
98101 ) ;
99102 } ;
100103
101- const handleRunClick = ( ) => {
104+ const { close, error, getPersistedJobId, persistJobId, setStreamRequest} = useWorkflowTestStream (
105+ workflow . id ! ,
106+ ( ) => {
107+ if ( bottomResizablePanelRef . current && bottomResizablePanelRef . current . getSize ( ) === 0 ) {
108+ bottomResizablePanelRef . current . resize ( 35 ) ;
109+ }
110+
111+ setJobId ( null ) ;
112+ } ,
113+ ( ) => setJobId ( null ) ,
114+ ( jobId ) => setJobId ( jobId )
115+ ) ;
116+
117+ const handleRunClick = useCallback ( ( ) => {
102118 setShowBottomPanelOpen ( true ) ;
103119 setWorkflowTestExecution ( undefined ) ;
104120
@@ -116,27 +132,33 @@ export const useWorkflowBuilderHeader = ({bottomResizablePanelRef, chatTrigger,
116132 setWorkflowTestChatPanelOpen ( true ) ;
117133 } else {
118134 setWorkflowIsRunning ( true ) ;
135+ setJobId ( null ) ;
136+ persistJobId ( null ) ;
119137
120- workflowTestApi
121- . testWorkflow ( {
122- environmentId : currentEnvironmentId ,
123- id : workflow . id ,
124- } )
125- . then ( ( workflowTestExecution ) => {
126- setWorkflowTestExecution ( workflowTestExecution ) ;
127- setWorkflowIsRunning ( false ) ;
128-
129- if ( bottomResizablePanelRef . current && bottomResizablePanelRef . current . getSize ( ) === 0 ) {
130- bottomResizablePanelRef . current . resize ( 35 ) ;
131- }
132- } )
133- . catch ( ( ) => {
134- setWorkflowIsRunning ( false ) ;
135- setWorkflowTestExecution ( undefined ) ;
136- } ) ;
138+ const request = getTestWorkflowStreamPostRequest ( {
139+ environmentId : currentEnvironmentId ,
140+ id : workflow . id ,
141+ } ) ;
142+
143+ setStreamRequest ( request ) ;
137144 }
138145 }
139- } ;
146+ } , [
147+ captureProjectWorkflowTested ,
148+ currentEnvironmentId ,
149+ bottomResizablePanelRef ,
150+ chatTrigger ,
151+ persistJobId ,
152+ resetMessages ,
153+ setDataPillPanelOpen ,
154+ setShowBottomPanelOpen ,
155+ setStreamRequest ,
156+ setWorkflowIsRunning ,
157+ setWorkflowNodeDetailsPanelOpen ,
158+ setWorkflowTestExecution ,
159+ setWorkflowTestChatPanelOpen ,
160+ workflow . id ,
161+ ] ) ;
140162
141163 const handleShowOutputClick = ( ) => {
142164 setShowBottomPanelOpen ( ! showBottomPanel ) ;
@@ -148,6 +170,15 @@ export const useWorkflowBuilderHeader = ({bottomResizablePanelRef, chatTrigger,
148170
149171 const handleStopClick = useCallback ( ( ) => {
150172 setWorkflowIsRunning ( false ) ;
173+ close ( ) ;
174+ setStreamRequest ( null ) ;
175+
176+ if ( jobId ) {
177+ workflowTestApi . stopWorkflowTest ( { jobId} ) . finally ( ( ) => {
178+ persistJobId ( null ) ;
179+ setJobId ( null ) ;
180+ } ) ;
181+ }
151182
152183 if ( chatTrigger ) {
153184 setWorkflowTestChatPanelOpen ( false ) ;
@@ -156,13 +187,52 @@ export const useWorkflowBuilderHeader = ({bottomResizablePanelRef, chatTrigger,
156187 bottomResizablePanelRef . current . resize ( 0 ) ;
157188 }
158189 }
159- } , [ bottomResizablePanelRef , chatTrigger , setWorkflowIsRunning , setWorkflowTestChatPanelOpen ] ) ;
190+ } , [
191+ bottomResizablePanelRef ,
192+ chatTrigger ,
193+ close ,
194+ jobId ,
195+ persistJobId ,
196+ setStreamRequest ,
197+ setWorkflowIsRunning ,
198+ setWorkflowTestChatPanelOpen ,
199+ ] ) ;
200+
201+ // On mount: try to restore an ongoing run using jobId persisted in localStorage.
202+ // Attach-first approach: immediately call attach with the exact jobId string.
203+ useEffect ( ( ) => {
204+ if ( ! workflow . id || currentEnvironmentId === undefined ) return ;
205+
206+ const jobId = getPersistedJobId ( ) ;
207+
208+ if ( ! jobId ) {
209+ return ;
210+ }
211+
212+ setWorkflowIsRunning ( true ) ;
213+ setJobId ( jobId ) ;
214+
215+ setStreamRequest ( getTestWorkflowAttachRequest ( { jobId} ) ) ;
216+ // eslint-disable-next-line react-hooks/exhaustive-deps
217+ } , [ workflow . id , currentEnvironmentId , getPersistedJobId ] ) ;
160218
161219 useEffect ( ( ) => {
162- if ( workflowNodeDetailsPanelOpen || ! workflowTestChatPanelOpen ) {
220+ // Stop only when:
221+ // - Node details panel is opened (always cancels runs), or
222+ // - We are in chat mode and the chat panel is not open
223+ if ( workflowNodeDetailsPanelOpen || ( chatTrigger && ! workflowTestChatPanelOpen ) ) {
163224 handleStopClick ( ) ;
164225 }
165- } , [ handleStopClick , workflowNodeDetailsPanelOpen , workflowTestChatPanelOpen ] ) ;
226+ } , [ chatTrigger , handleStopClick , workflowNodeDetailsPanelOpen , workflowTestChatPanelOpen ] ) ;
227+
228+ useEffect ( ( ) => {
229+ if ( error ) {
230+ setWorkflowIsRunning ( false ) ;
231+ setStreamRequest ( null ) ;
232+ persistJobId ( null ) ;
233+ setJobId ( null ) ;
234+ }
235+ } , [ error , persistJobId , setWorkflowIsRunning , setStreamRequest ] ) ;
166236
167237 return {
168238 handleProjectWorkflowValueChange,
0 commit comments