@@ -85,7 +85,26 @@ const chatResponses: {
8585 events : { type : string | null ; seq : number | null } [ ]
8686 error ?: string
8787} [ ] = [ ]
88+ const uploadRequests : { event : string ; stage : string ; method : string ; status ?: number } [ ] = [ ]
8889const responseDiagnostics : Promise < void > [ ] = [ ]
90+ const chatTurns = new Map < string , number > ( )
91+ const chatAttempts : {
92+ attempt : number
93+ turn : number | null
94+ check : string
95+ scenario : string
96+ phase : string
97+ scope : string
98+ sender : string
99+ } [ ] = [ ]
100+ const dropReceipts : {
101+ scope : string
102+ phase : string
103+ receipt : Awaited < ReturnType < typeof dropOnChat > >
104+ } [ ] = [ ]
105+ let currentCheck = ''
106+ let scenario = ''
107+ let phase = ''
89108let failureState : { path : string ; chatId : string | null ; text : string } [ ] = [ ]
90109let pendingAtFailure : { path : string ; method : string ; resourceType : string } [ ] = [ ]
91110const uploadedPaths = new Set < string > ( )
@@ -145,6 +164,9 @@ async function check(
145164 run : ( ) => Promise < void >
146165) {
147166 if ( group !== 'all' && group !== owner && ! ( group === 'native' && owner !== 'access' ) ) return
167+ currentCheck = name
168+ scenario = ''
169+ phase = ''
148170 const started = performance . now ( )
149171 try {
150172 await run ( )
@@ -325,8 +347,8 @@ try {
325347 // Only the synthetic page is pregranted; native permission prompts are a separate acceptance check.
326348 manifest . host_permissions . push ( `${ fixtureOrigin } /*` )
327349 await writeFile ( resolve ( extensionPath , 'manifest.json' ) , JSON . stringify ( manifest ) )
328- const launchBrowserContext = ( ) =>
329- chromium . launchPersistentContext ( resolve ( temporary , 'profile' ) , {
350+ const launchBrowserContext = async ( ) => {
351+ const opened = await chromium . launchPersistentContext ( resolve ( temporary , 'profile' ) , {
330352 channel : 'chromium' ,
331353 headless : process . env . EXTENSION_APP_E2E_HEADED !== '1' ,
332354 viewport : { width : 390 , height : 844 } ,
@@ -336,6 +358,38 @@ try {
336358 '--use-fake-device-for-media-stream' ,
337359 ] ,
338360 } )
361+ opened . on ( 'request' , ( request ) => {
362+ const url = new URL ( request . url ( ) )
363+ if (
364+ url . origin !== base . origin ||
365+ url . pathname !== '/api/mothership/chat' ||
366+ request . method ( ) !== 'POST'
367+ )
368+ return
369+ const body = toRecord ( request . postDataJSON ( ) )
370+ const id = toStringOrNull ( body . userMessageId )
371+ if ( id && ! chatTurns . has ( id ) ) chatTurns . set ( id , chatTurns . size + 1 )
372+ let sender = 'unknown'
373+ try {
374+ sender = request . frame ( ) . page ( ) === panel ? 'current-panel' : 'other-page'
375+ } catch { }
376+ chatAttempts . push ( {
377+ attempt : chatAttempts . length + 1 ,
378+ turn : id ? ( chatTurns . get ( id ) ?? null ) : null ,
379+ check : currentCheck ,
380+ scenario,
381+ phase,
382+ scope :
383+ body . organizationId === organizationId
384+ ? 'organization'
385+ : body . workspaceId === alphaId
386+ ? 'workspace'
387+ : 'other' ,
388+ sender,
389+ } )
390+ } )
391+ return opened
392+ }
339393 context = await launchBrowserContext ( )
340394 let browserContext = context
341395 context . setDefaultTimeout ( 30_000 )
@@ -366,7 +420,16 @@ try {
366420 route . fulfill ( { json : { sttAvailable : true } } )
367421 )
368422 page . on ( 'pageerror' , ( error ) => browserErrors . push ( error . message ) )
369- page . on ( 'request' , ( request ) => pendingRequests . add ( request ) )
423+ page . on ( 'request' , ( request ) => {
424+ pendingRequests . add ( request )
425+ const url = new URL ( request . url ( ) )
426+ if ( url . origin === base . origin && url . pathname . startsWith ( '/api/files/uploads' ) )
427+ uploadRequests . push ( {
428+ event : 'request' ,
429+ stage : url . pathname . endsWith ( '/complete' ) ? 'complete' : 'session' ,
430+ method : request . method ( ) ,
431+ } )
432+ } )
370433 page . on ( 'requestfinished' , ( request ) => pendingRequests . delete ( request ) )
371434 page . on ( 'requestfailed' , ( request ) => {
372435 pendingRequests . delete ( request )
@@ -377,6 +440,13 @@ try {
377440 } )
378441 page . on ( 'response' , ( response ) => {
379442 const url = new URL ( response . url ( ) )
443+ if ( url . origin === base . origin && url . pathname . startsWith ( '/api/files/uploads' ) )
444+ uploadRequests . push ( {
445+ event : 'response' ,
446+ stage : url . pathname . endsWith ( '/complete' ) ? 'complete' : 'session' ,
447+ method : response . request ( ) . method ( ) ,
448+ status : response . status ( ) ,
449+ } )
380450 if ( url . origin === base . origin && url . pathname === '/api/mothership/chat' ) {
381451 const entry : ( typeof chatResponses ) [ number ] = {
382452 status : response . status ( ) ,
@@ -558,9 +628,12 @@ try {
558628 )
559629 return { name, png }
560630 }
561- const attachScreenshot = async ( target : Locator ) => {
631+ const attachScreenshot = async ( target : Locator , scope ?: string ) => {
562632 const { name, png } = await screenshotFile ( )
563- return uploadFile ( name , ( ) => dropOnChat ( shell , target , { files : [ png ] } ) )
633+ return uploadFile ( name , async ( ) => {
634+ const receipt = await dropOnChat ( shell , target , { files : [ png ] } )
635+ if ( scope ) dropReceipts . push ( { scope, phase : 'recovery' , receipt } )
636+ } )
564637 }
565638 const installCaptureGate = ( ) =>
566639 shell . addInitScript ( ( parentOrigin ) => {
@@ -689,7 +762,8 @@ try {
689762 const { name, png } = await screenshotFile ( )
690763 const pageCount = browserContext . pages ( ) . length
691764 const previousUrl = app . url ( )
692- await dropOnChat ( shell , greeting , { files : [ png ] } )
765+ const receipt = await dropOnChat ( shell , greeting , { files : [ png ] } )
766+ dropReceipts . push ( { scope, phase : 'held-capture' , receipt } )
693767 await app . evaluate (
694768 ( ) => new Promise < void > ( ( resolve ) => requestAnimationFrame ( ( ) => resolve ( ) ) )
695769 )
@@ -730,7 +804,7 @@ try {
730804 ) . toHaveCount ( 0 )
731805 assert . equal ( uploadAttempts , 0 )
732806 await browserContext . unroute ( uploadEndpoint , interceptUpload )
733- await attachScreenshot ( input )
807+ await attachScreenshot ( input , scope )
734808 await screenshot ( `pending-capture-drop-${ scope } -recovered` )
735809 await app . getByRole ( 'button' , { name : `Remove ${ name } ` , exact : true } ) . click ( )
736810 assert . equal ( submitted . length , 1 , `${ scope } : recovering drops duplicated the turn` )
@@ -1840,13 +1914,26 @@ try {
18401914 if ( enabled ) await expect ( globe ) . toHaveText ( 'Current page' )
18411915 else await expect ( globe ) . not . toHaveText ( 'Current page' )
18421916 }
1843- try {
1917+ const openScenario = async ( target : string , name : string ) => {
1918+ scenario = name
1919+ phase = 'opening'
1920+ await shell . close ( )
1921+ panel = await openPanel ( )
1922+ shell = panel
18441923 await setPanelViewport ( 320 , 844 )
1924+ await shell . goto ( `chrome-extension://${ extensionId } /sidepanel.html` )
1925+ await observeCaptureReceipts ( )
1926+ await navigate ( base . origin + target )
1927+ await expect (
1928+ frame ( ) . getByRole ( 'heading' , { name : 'How can I help?' , exact : true } )
1929+ ) . toBeVisible ( )
1930+ }
1931+ try {
18451932 for ( const org of [ false , true ] ) {
18461933 const target = org ? `/extension/o/${ organizationId } ` : `/extension/${ alphaId } `
18471934 const input = ( ) =>
18481935 frame ( ) . getByRole ( 'textbox' , { name : org ? 'Ask Sim' : 'Message' , exact : true } )
1849- await navigate ( base . origin + target )
1936+ await openScenario ( target , ` ${ org ? 'organization' : 'workspace' } -reference` )
18501937 await expect ( input ( ) ) . toBeVisible ( )
18511938 await setReference ( true )
18521939 await input ( ) . fill ( 'Summarize the current page' )
@@ -1858,6 +1945,7 @@ try {
18581945 await screenshot ( `reference-${ org ? 'organization' : 'workspace' } -enabled-320` )
18591946 await second . bringToFront ( )
18601947 let count = received . length
1948+ phase = 'send'
18611949 await input ( ) . press ( 'Enter' )
18621950 await expect . poll ( ( ) => received . length ) . toBe ( count + 1 )
18631951 const pages = submittedPages ( received [ count ] )
@@ -1869,24 +1957,26 @@ try {
18691957 org ? organizationId : alphaId
18701958 )
18711959
1872- await navigate ( base . origin + target )
1960+ await openScenario ( target , ` ${ org ? 'organization' : 'workspace' } -manual` )
18731961 await expect ( input ( ) ) . toBeVisible ( )
18741962 await capture ( )
18751963 await second . bringToFront ( )
18761964 await input ( ) . fill ( 'Compare the attached snapshot' )
18771965 count = received . length
1966+ phase = 'send'
18781967 await input ( ) . press ( 'Enter' )
18791968 await expect . poll ( ( ) => received . length ) . toBe ( count + 1 )
18801969 const manual = submittedPages ( received [ count ] )
18811970 assert . equal ( manual . length , 1 )
18821971 assert ( manual [ 0 ] . text . includes ( 'ORCHID-482' ) && ! manual [ 0 ] . text . includes ( 'JADE-231' ) )
18831972
1884- await navigate ( base . origin + target )
1973+ await openScenario ( target , ` ${ org ? 'organization' : 'workspace' } -denied` )
18851974 await expect ( input ( ) ) . toBeVisible ( )
18861975 await restricted . bringToFront ( )
18871976 const draft = 'Keep this draft if access is unavailable'
18881977 await input ( ) . fill ( draft )
18891978 count = received . length
1979+ phase = 'denied-send'
18901980 await input ( ) . press ( 'Enter' )
18911981 await expect (
18921982 shell . getByRole ( 'button' , { name : 'Request site access' , exact : true } )
@@ -1905,6 +1995,7 @@ try {
19051995 'Access guidance covered the composer twice'
19061996 )
19071997 await setReference ( false )
1998+ phase = 'opt-out-send'
19081999 await input ( ) . press ( 'Enter' )
19092000 await expect . poll ( ( ) => received . length ) . toBe ( count + 1 )
19102001 assert . equal ( submittedPages ( received [ count ] ) . length , 0 )
@@ -2864,6 +2955,9 @@ try {
28642955 failureState,
28652956 httpErrors,
28662957 chatResponses,
2958+ chatAttempts,
2959+ dropReceipts,
2960+ uploadRequests,
28672961 } ,
28682962 null ,
28692963 2
0 commit comments