@@ -237,6 +237,11 @@ function outputBaseDir() {
237237 return global . output_dir || resolvePath ( process . cwd ( ) , 'output' )
238238}
239239
240+ function computeTraceDir ( test ) {
241+ const title = test . fullTitle ? test . fullTitle ( ) : test . title
242+ return traceDirFor ( test . file , title , outputBaseDir ( ) )
243+ }
244+
240245// In-process pause coordination. When a test running through run_test calls
241246// pause(), the handler registered via setPauseHandler resolves a "paused"
242247// promise that run_test is racing against test completion. The "pause" tool
@@ -247,6 +252,7 @@ let pendingRunResults = null // results array being collected while paused
247252let pendingRunCleanup = null // cleanup callback to detach test.after / step.after listeners
248253let pendingTestFile = null // file path of the test currently running
249254let pendingStepInfo = null // { index, name, status } of the last step that fired step.after
255+ let pendingTraceDir = null // aiTrace per-test dir for the running test
250256const pauseEvents = new EventEmitter ( )
251257
252258setPauseHandler ( ( { registeredVariables } ) => {
@@ -293,13 +299,16 @@ function collectRunCompletion(errorMessage) {
293299 passes : results . filter ( r => r . status === 'passed' ) . length ,
294300 failures : results . filter ( r => r . status === 'failed' ) . length ,
295301 }
302+ const aiTraceDir = pendingTraceDir
296303 if ( typeof pendingRunCleanup === 'function' ) pendingRunCleanup ( )
297304 pendingRunPromise = null
298305 pendingRunResults = null
299306 pendingTestFile = null
300307 pendingStepInfo = null
308+ pendingTraceDir = null
301309 return {
302310 status : 'completed' ,
311+ aiTraceDir,
303312 reporterJson : { stats, tests : results } ,
304313 error : errorMessage ,
305314 }
@@ -309,11 +318,13 @@ function pausedPayload() {
309318 return {
310319 status : 'paused' ,
311320 file : pendingTestFile ,
321+ aiTraceDir : pendingTraceDir ,
312322 pausedAfter : pendingStepInfo ,
313323 suggestions : [
314324 'Call snapshot to capture URL/HTML/ARIA/screenshot/console/storage at this point' ,
315325 'Call run_code to inspect or manipulate state (e.g. return await I.grabText("h1"))' ,
316326 'Call continue to release the pause and let the test run the next step (or finish)' ,
327+ 'Query a saved step snapshot offline: codeceptq <locator> --file <aiTraceDir>/<NNNN>_<step>_page.html' ,
317328 ] ,
318329 }
319330}
@@ -709,13 +720,17 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
709720 pendingStepInfo = null
710721 let stepIndex = 0
711722
723+ const onBefore = t => {
724+ pendingTraceDir = computeTraceDir ( t )
725+ }
712726 const onAfter = t => {
713727 pendingRunResults . push ( {
714728 title : t . title ,
715729 file : t . file ,
716730 status : t . err ? 'failed' : 'passed' ,
717731 error : t . err ?. message ,
718732 duration : t . duration ,
733+ aiTraceDir : pendingTraceDir ,
719734 } )
720735 }
721736 const onStepAfter = step => {
@@ -729,9 +744,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
729744 pauseNow ( )
730745 }
731746 }
747+ event . dispatcher . on ( event . test . before , onBefore )
732748 event . dispatcher . on ( event . test . after , onAfter )
733749 event . dispatcher . on ( event . step . after , onStepAfter )
734750 pendingRunCleanup = ( ) => {
751+ try { event . dispatcher . removeListener ( event . test . before , onBefore ) } catch { }
735752 try { event . dispatcher . removeListener ( event . test . after , onAfter ) } catch { }
736753 try { event . dispatcher . removeListener ( event . step . after , onStepAfter ) } catch { }
737754 pendingRunCleanup = null
@@ -802,13 +819,17 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
802819 pendingStepInfo = null
803820 let stepIndex = 0
804821
822+ const onBefore = t => {
823+ pendingTraceDir = computeTraceDir ( t )
824+ }
805825 const onAfter = t => {
806826 pendingRunResults . push ( {
807827 title : t . title ,
808828 file : t . file ,
809829 status : t . err ? 'failed' : 'passed' ,
810830 error : t . err ?. message ,
811831 duration : t . duration ,
832+ aiTraceDir : pendingTraceDir ,
812833 } )
813834 }
814835 const onStepAfter = step => {
@@ -821,9 +842,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
821842 // Pause after every step — agent calls continue to advance.
822843 pauseNow ( )
823844 }
845+ event . dispatcher . on ( event . test . before , onBefore )
824846 event . dispatcher . on ( event . test . after , onAfter )
825847 event . dispatcher . on ( event . step . after , onStepAfter )
826848 pendingRunCleanup = ( ) => {
849+ try { event . dispatcher . removeListener ( event . test . before , onBefore ) } catch { }
827850 try { event . dispatcher . removeListener ( event . test . after , onAfter ) } catch { }
828851 try { event . dispatcher . removeListener ( event . step . after , onStepAfter ) } catch { }
829852 pendingRunCleanup = null
0 commit comments