From d35d1f6a09c25acbc64d2f0aa6cd38896bde3cfa Mon Sep 17 00:00:00 2001 From: "prompt.ac/@jeffrey" Date: Thu, 17 Sep 2026 15:19:04 -0700 Subject: [PATCH] Retry transient native dialog races while connecting --- slab/captutor/lib/modal-police.mjs | 2 +- slab/captutor/test/modal-police.test.mjs | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/slab/captutor/lib/modal-police.mjs b/slab/captutor/lib/modal-police.mjs index 35dc42c764..0eb342913b 100644 --- a/slab/captutor/lib/modal-police.mjs +++ b/slab/captutor/lib/modal-police.mjs @@ -89,7 +89,7 @@ export function approvedDebuggingPolicy() { // Watch only this connection attempt; never leave an unattended consent clicker. export async function connectWithModalPolice(connect,{police=createModalPolice({allowRemoteDebugging:approvedDebuggingPolicy()}),intervalMs=500}={}) { let pending=true;let failure; - const watch=(async()=>{while(pending){await new Promise(r=>{setTimeout(r,intervalMs);});if(!pending)break;try{await police.check('connecting',{mayHandle:()=>pending});}catch(error){failure=error;break;}}})(); + const watch=(async()=>{while(pending){await new Promise(r=>{setTimeout(r,intervalMs);});if(!pending)break;try{await police.check('connecting',{mayHandle:()=>pending});}catch(error){if(/Modal changed or action ambiguous|Modal fingerprint changed/.test(String(error.stderr||error.message)))continue;failure=error;break;}}})(); let result; try{result=await connect();}finally{pending=false;await watch;} if(failure){await result?.close?.();throw failure;} diff --git a/slab/captutor/test/modal-police.test.mjs b/slab/captutor/test/modal-police.test.mjs index 4efbb7a779..1f6298da8a 100644 --- a/slab/captutor/test/modal-police.test.mjs +++ b/slab/captutor/test/modal-police.test.mjs @@ -54,3 +54,8 @@ test('native banner recognition includes toolbar infobars without choosing tab c const result=runInNewContext(CHROME_MODAL_SCRIPT+';JSON.stringify(uniqueHits.map(({kind,buttons})=>({kind,buttons})))',{Application:()=>({processes:{byName:()=>({windows:()=>[window]})}})}); assert.deepEqual(JSON.parse(result),[{kind:'automation-banner',buttons:['Turn off in settings','Close']}]); }); + +test('connection watcher rescans a moved consent dialog instead of abandoning it',async()=>{ + let checks=0;const result=await connectWithModalPolice(async()=>{await new Promise(r=>setTimeout(r,20));return 'connected';},{intervalMs:1,police:{check:async()=>{if(++checks===1)throw Error('Modal changed or action ambiguous');}}}); + assert.equal(result,'connected');assert.ok(checks>1); +}); -- 2.51.2