agentiqa 1.1.24 → 1.1.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +4 -4
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -718,7 +718,7 @@ ${this.redactPII(r)}
718
718
  Layout: ${c.config.layoutPreset??"custom"} (${c.config.screenWidth}x${c.config.screenHeight})
719
719
  `+D}this.recordStartupMilestone("initial_state_ready",{platform:d?"mobile":"web"}),this.updateObservationScreenState(void 0,A),a=await this.setupScreencast(c);let R=[{text:A}];if(!w&&!p&&!h&&R.push({inlineData:{mimeType:"image/png",data:k}}),n?.length&&this.deps.attachmentStorageService){let j=await this.buildAttachmentParts(n);R.push(...j),this.log("info","ExplorerRuntime","Injected user attachments into initial context",{count:n.length,names:n.map(D=>D.originalName)})}else n?.length&&!this.deps.attachmentStorageService&&this.log("warn","ExplorerRuntime","Attachments present but attachmentStorageService missing \u2014 dropping user file (upload_file will fall back to samples)",{count:n.length});_.push({role:"user",parts:R}),this.stripOldScreenshots(_),await this.persistConversationTrace(c,_),this.stripOldPageSnapshots(_,w),this.stripOldFileAttachments(_),this.uploadAssetBatches=[],this._observationCoverageRejections=0,this._invalidDraftPlanRejections=0,this.currentAttachments=n??[];for(let j of n??[])this.knownAttachments.some(D=>D.id===j.id)||this.knownAttachments.push(j);this.lastResult=null,this.reportedIssues=[],this.loggedObservationCheckpoints=[],this.lastUnreachableToolError=void 0,this.lastToolProviderRegionUnsupported=!1;let U=c.config.maxIterationsPerTurn??300,z=await this.runLoop({session:c,maxIterations:U,snapshotOnly:w,isMobile:d,devicePlatform:m,taskDescription:r,preserveAllPageSnapshots:c.config?.preserveAllPageSnapshots,supervisorHints:c.config?.extensionPath?'Browser extension context: The agent is testing a web app with a browser extension (e.g. MetaMask). If the extension shows an unlock/login screen, the agent should enter the password \u2014 NEVER suggest clicking "Forgot password", "Import wallet", or resetting the wallet. The wallet is already set up; it just needs to be unlocked.':void 0,runJsLoopRecoveryPolicy:this.deps.isDiscoveryRun?"partial_sitemap_salvage":"warn_response"});o=z.blocked,this.lastResult||(this.lastResult=this.buildPostLoopFallbackResult(z),this.log("warn","ExplorerRuntime","Post-loop recovery: lastResult was null",{status:this.lastResult.status,blockedReason:z.blockedReason}))}catch(l){l=so(l)??oo(l)??io(l)??jl(l)??l;let c=String(l?.message||l);if(c.includes("cancelled")||l?.name==="AbortError"||c.toLowerCase().includes("aborted"))this.trimDanglingToolCalls(this.conversationTrace),await this.persistConversationTrace(e,this.conversationTrace);else{this.markRunErrored();let d=l instanceof jr?l.reason:j_(l);this.lastClassifiedError=d?$_(d):$l(c),this.lastProviderRegionUnsupported=Qr(c),this.lastDeviceInitError=F_(c);let m=l instanceof jr||d!==void 0,h=l instanceof Or,p=l instanceof en,y=l instanceof tn,g=l instanceof on,w=h||p||y||g,x=l instanceof Qn,S=Fn(c),E=S?qn({isChildAgent:this.deps.isChildAgent}):this.lastDeviceInitError?this.lastDeviceInitError.sanitized:c;if(this.lastResult={status:w?"blocked":"error",...y?{blockKind:"captcha"}:h?{blockKind:"cloudflare"}:p||g?{blockKind:"credentials"}:{},summary:E,issues:this.reportedIssues},w||(this.emit("session:error",{sessionId:this.sessionId,error:E}),this.deps.errorReporter?.captureException(l,{tags:{source:"agent_runtime",sessionId:this.sessionId}})),m&&this.baseDeps.sink.emit({kind:"log",ts:Date.now(),sessionId:this.sessionId,level:"info",message:`[preflight] rejected reason=${d}`}),x){let v=l;this.baseDeps.sink.emit({kind:"log",ts:Date.now(),sessionId:this.sessionId,level:"info",message:`[slow-site] url=${v.url} totalTimeoutMs=${v.totalTimeoutMs} attempts=${v.attemptCount}`})}if(h){let v=l;this.baseDeps.sink.emit({kind:"log",ts:Date.now(),sessionId:this.sessionId,level:"info",message:`[cloudflare-block] host=${v.host} signals=${v.signals.join(",")}`}),this.baseDeps.sink.emit({kind:"tool_call",ts:Date.now(),sessionId:this.sessionId,tool:"exploration_blocked",args:{reason:"cloudflare_challenge",host:v.host,signals:v.signals}})}if(p){let v=l;this.baseDeps.sink.emit({kind:"log",ts:Date.now(),sessionId:this.sessionId,level:"info",message:`[credentials-rejected] host=${v.host} evidence=${JSON.stringify(v.evidence)}`}),this.baseDeps.sink.emit({kind:"tool_call",ts:Date.now(),sessionId:this.sessionId,tool:"exploration_blocked",args:{reason:"credentials_rejected",host:v.host,evidence:v.evidence}})}if(y){let v=l;this.baseDeps.sink.emit({kind:"log",ts:Date.now(),sessionId:this.sessionId,level:"info",message:`[captcha-gated] host=${v.host} provider=${v.provider} evidence=${JSON.stringify(v.evidence)}`}),this.baseDeps.sink.emit({kind:"tool_call",ts:Date.now(),sessionId:this.sessionId,tool:"exploration_blocked",args:{reason:"captcha_gated",host:v.host,provider:v.provider}})}if(!(S&&this.deps.isChildAgent)){let v={id:fe("msg"),sessionId:this.sessionId,role:"model",text:m||w||x||S?E:`I stopped unexpectedly due to an error: ${E}. You can retry by sending another message.`,timestamp:Date.now(),...w?{actionName:"exploration_blocked",actionArgs:{attempted:y?`Complete the form on ${l.host}`:p?`Log in to ${l.host}`:`Load ${l.host}`,obstacle:y?"CAPTCHA-gated flow":p?"Credentials rejected":"Cloudflare anti-bot challenge",question:E}}:{}};await this.baseDeps.chatRepo.addMessage(v),this.emit("message:added",{sessionId:this.sessionId,message:v})}}}finally{await this.teardownScreencast(a,i??this.sessionId),this.endRun(),this.baseDeps.sink.emit({kind:"session_end",ts:Date.now(),sessionId:this.sessionId,status:"completed"}),this.baseDeps.sink.flush(),this.currentProjectName&&this.currentSessionKind!=="self_test"&&(o?(this.emit("session:blocked",{sessionId:this.sessionId}),this.deps.notificationService?.showAgentBlocked(this.sessionId,this.currentProjectName,this.currentProjectId??void 0)):this.deps.notificationService?.showAgentTurnComplete(this.sessionId,this.currentProjectName,this.currentProjectId??void 0)),this.currentProjectId&&this.emit("session:coverage-requested",{sessionId:this.sessionId,projectId:this.currentProjectId})}}};import{z as Sw}from"zod";function uw(t,e){if(e.forceLoggedOut||t.authMode==="under_test")return{mode:"run_all"};if(!e.persistBrowserProfile)return{mode:"run_all"};let r=t.steps.map((s,o)=>s.authRole?o:-1).filter(s=>s>=0);return r.length===0?{mode:"run_all"}:t.steps.some(s=>s.authRole==="probe"&&s.authCheck)?{mode:"probe",setupStepIndexes:r}:{mode:"run_all"}}function dw(t,e,r){if(t.length===0)return e;let n=new Set(t.map(i=>i+1)),s=t.map(i=>({stepIndex:i+1,status:"skipped",note:"Auth precondition already satisfied \u2014 session authenticated; setup step skipped.",step:r[i]})),o=e.filter(i=>!n.has(i.stepIndex));return[...s,...o].sort((i,a)=>i.stepIndex-a.stepIndex)}var l$={seeded:!1,cookies:0,origins:0};async function Ki(t){let e=Cd(t.projectCredentials??[]),r=c$(t),n=u$(t,r),s=t.sessionConfig.forceLoggedOut===!0||n==="logged_out",i=d$(t,s)?p$(t.capturedProfile):null,a={loginCredentialEmails:e,forceLoggedOut:s},l=l$;i?(a.seedCookies=i.seedCookies,a.seedLocalStorage=i.seedLocalStorage,l={seeded:!0,cookies:i.seedCookies.length,origins:i.seedLocalStorage.length}):s&&(a.seedCookies=void 0,a.seedLocalStorage=void 0);let c={runKind:t.runKind,authMode:r,startState:n,forceLoggedOut:s,freshContextRequired:n==="logged_out",loginCredentialEmails:e,profileSeed:l,sessionConfigPatch:a};return t.runKind==="runner_plan"&&(c.runner=m$(t,s)),c}function c$(t){return t.runKind==="live_child"?t.areaIntent?.authMode:t.testPlan?.authMode}function u$(t,e){return t.sessionConfig.forceLoggedOut===!0||e==="under_test"?"logged_out":t.areaIntent?.startState??"default"}function d$(t,e){return e?!1:t.runKind==="runner_plan"?t.sessionConfig.persistBrowserProfile===!0:t.sessionConfig.persistBrowserProfile!==!1}function p$(t){let e=xl(t);return e.seedCookies.length===0&&e.seedLocalStorage.length===0?null:e}function m$(t,e){if(!t.testPlan)return{prepassDecision:{mode:"run_all"},conditionalAuthStepIndexes:[],profileCaptureAllowed:!1};let r=uw(t.testPlan,{forceLoggedOut:e,persistBrowserProfile:t.sessionConfig.persistBrowserProfile});return{prepassDecision:r,conditionalAuthStepIndexes:r.mode==="probe"?r.setupStepIndexes:[],profileCaptureAllowed:t.sessionConfig.persistBrowserProfile===!0&&!e&&t.testPlan.authMode!=="under_test"}}var h$=[[/email\s*\/?\s*password|sign in with email|email address/i,"Email/Password"],[/microsoft|azure ad|entra/i,"Microsoft"],[/google/i,"Google"],[/\bsso\b|saml|okta|single sign/i,"SSO"],[/email[\s\S]{0,80}password|password[\s\S]{0,80}email/i,"Email/Password"]];function f$(t){let e=t.map(r=>r.text).join(`
720
720
  `);for(let[r,n]of h$)if(r.test(e))return n}function g$(t){let e=t.map(s=>s.text).join(`
721
- `),r=e.match(/credential\s+'([^']+)'|'([^']+@[^']+)'/i);return r?r[1]??r[2]:e.match(/[\w.+-]+@[\w.-]+\.\w+/)?.[0]}function pw(t){let e=t.map((u,d)=>u.authRole==="login"?d:-1).filter(u=>u>=0);if(e.length<=1)return t;let r=e.map(u=>t[u]),n=f$(r),s=g$(r),o=s?` as '${s}'`:"",a={text:n?`Sign in with ${n}${o}`:`Log in${o}`,type:"setup",authRole:"login"},l=e[0],c=[];for(let u=0;u<t.length;u++){if(t[u].authRole==="login"){u===l&&c.push(a);continue}c.push(t[u])}return c}function Wl(t,e){if(e?.authModeHint==="under_test")return{steps:t,authMode:"under_test"};let r=t.filter(a=>a.authRole);if(r.length===0)return{steps:t};let n=r.filter(a=>a.authRole==="probe"),s=r.filter(a=>a.authRole==="login"),o=t.filter(a=>!a.authRole);return{steps:pw([...n,...s,...o]),authMode:"precondition"}}function y$(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function mw(t){let e;try{e=new URL(t)}catch{return null}let r=e.pathname,n=r&&r!=="/"?r:e.origin;return n?{kind:"urlMatches",pattern:y$(n)}:null}function hw(t){return t.length===0?null:Math.max(...t)}function fw(t,e){return t.some((r,n)=>n>e&&r.type==="action")}var v$="Verify the session is already authenticated";function gw(t,e,r){if(t.authMode==="precondition")return t;let n={text:v$,type:"setup",authRole:"probe",authCheck:r},s=t.steps.map((a,l)=>l<=e?{...a,type:"setup",authRole:"login"}:{...a}),{steps:o,authMode:i}=Wl([n,...s]);return{...t,steps:o,authMode:i,updatedAt:Date.now()}}import{z as $e}from"zod";var b$=$e.object({stepIndex:$e.number().describe("1-based step number from the test plan (step 1, 2, 3...)")}),_$={description:"Signal that you are starting work on a specific step. Call this BEFORE performing actions for each step to track progress.",inputSchema:b$},w$=$e.object({check:$e.string(),passed:$e.boolean(),note:$e.string().describe('For criteria about messages or text: include the exact text you see on screen (e.g., "Actual text: Incorrect code. Try again."). This captures real UI copy for QA records.').optional()}),S$=$e.object({stepIndex:$e.number(),status:$e.enum(["passed","failed","warning","skipped"]),note:$e.string().optional(),criteriaResults:$e.array(w$).optional()}),T$=$e.object({status:$e.enum(["passed","failed"]),summary:$e.string(),stepResults:$e.array(S$),reflection:$e.string().describe("Brief self-assessment: wrong clicks, retries, confusing UI elements during the test run.")}),x$={description:"Complete test run with results.",inputSchema:T$},yw=$e.object({text:$e.string().describe('Describe WHAT to do, not HOW. NEVER include tool names, coordinates, or implementation details. For relative dates (today, tomorrow, next week), use ONLY the relative term\u2014never the specific date. For values that must be unique per run, use {{unique}} for name/text fields (e.g., "Set Name to User{{unique}}") or {{timestamp}} for emails/IDs (e.g., "Set Email to test-{{timestamp}}@example.com"). NEVER hardcode example values for unique fields. Steps must read like user instructions.'),type:$e.enum(["setup","action","verify"]),criteria:$e.array($e.object({check:$e.string(),strict:$e.boolean()})).optional()}),I$=$e.object({reason:$e.string().describe("Why this change is needed"),stepIndex:$e.number().describe("1-based step number to insert/update (step 1, 2, 3...). For add: steps inserted starting here."),action:$e.enum(["update","add","remove"]),newStep:yw.describe("For update: the updated step. For single add.").optional(),newSteps:$e.array(yw).describe("For adding multiple steps at once. Preferred for extending test coverage.").optional()}),vw={description:"Propose changes to the test plan. User must approve before applying. Use newSteps array for adding multiple steps.",inputSchema:I$},k$=$e.object({title:$e.string().describe("Short, descriptive title for the issue"),description:$e.string().describe("Detailed description of what is wrong"),severity:$e.enum(["high","medium","low"]).describe("Issue severity"),category:$e.enum(["visual","content","logical","ux"]).describe("Issue category"),confidence:$e.number().describe("Confidence level 0.0-1.0 that this is a real issue"),reproSteps:$e.array($e.string()).describe("Human-readable reproduction steps anyone could follow")}),E$={description:"Report a quality issue detected in the current screenshot. Use for visual glitches, content problems, logical inconsistencies, or UX issues.",inputSchema:k$},A$=$e.object({stepIndex:$e.number().describe("1-based step number that is blocked (step 1, 2, 3...)"),attempted:$e.string().describe("What you tried to do"),obstacle:$e.string().describe("What prevented you from succeeding"),question:$e.string().describe("Specific question for the user about how to proceed")}),R$={description:"Report that a step cannot be completed and you need user guidance. Use when: element unresponsive, expected content missing, step instructions unclear, action failed, or application returned an error. Report the issue first (report_issue), then call this. Do NOT improvise workarounds.",inputSchema:A$},C$=$e.object({email:$e.string().describe("Email address to check. The runtime normalizes this to the canonical testing email configured for the session.")}),M$={description:"Check recent messages for the session canonical testing email. Use after signup when a page asks for an email verification link or code.",inputSchema:C$},P$=$e.object({key:$e.string().describe('Name for the value (e.g., "last short term booking contract#")'),value:$e.string().describe("The value to save")}),bw={description:"Save a value to run memory. The value will be visible under RUN MEMORY in the system prompt for the remainder of this run (and subsequent plans in a sequential batch). Use whenever the test plan asks you to save data to memory.",inputSchema:P$},zd={signal_step:_$,run_complete:x$,propose_update:vw,report_issue:E$,exploration_blocked:R$,check_email:M$,read_source_document:Od},Gl={propose_update:vw},Kl={...xs,...zd},Yl={...xo,...zd};function Jl(t){return{...Io(t),...zd}}function An(t){let e=_w(t.trim()).trim();return!e||e==="about:blank"||/^[a-z][a-z0-9+.-]*:\/\//i.test(e)?e:`https://${e}`}function _w(t){return t.replace(/^['"`‘’“”]+|['"`‘’“”]+$/g,"")}function Xl(t){return t.replace(/^[\s*_~`'"([{<‘’“”]+/,"").replace(/[\s*_~`'")\]}>.,;:!?‘’“”]+$/,"")}var O$=/\.(pdf|docx?|xlsx?|pptx?|csv|tsv|rtf|odt|ods|odp|txt|md|json|xml|ya?ml|png|jpe?g|gif|webp|bmp|svg|tiff?|heic|heif|ico|zip|rar|7z|gz|tar|eml|msg|mp4|mov|avi|mkv|webm|mp3|wav|m4a)$/i;function Co(t){return t?O$.test(_w(t.trim())):!1}async function N$(t,e){let r=await import("node:os"),n=await import("node:fs"),s=await import("node:path"),i=(await import("node:crypto")).createHash("sha256").update(t).digest("hex").slice(0,16),a=s.join(r.tmpdir(),"agentiqa-attachments",i);n.existsSync(a)||n.mkdirSync(a,{recursive:!0});let l=e.replace(/[/\\]/g,"_"),c=s.join(a,l);if(n.existsSync(c))return c;let u=await fetch(t);if(!u.ok)throw new Error(`Failed to download ${t}: ${u.status}`);let d=new Uint8Array(await u.arrayBuffer()),m=`${c}.partial-${process.pid}`;return n.writeFileSync(m,d),n.renameSync(m,c),c}async function Vd(t,e){let r;return t.storedPath.startsWith("/")&&(await import("node:fs")).existsSync(t.storedPath)&&(r=t.storedPath),r||(r=await e?.testAssetStorageService?.getAbsolutePath(t.storedPath).catch(()=>{})),r||(r=await e?.attachmentStorageService?.getAbsolutePath(t.storedPath).catch(()=>{})),!r&&t.r2Url&&(r=await N$(t.r2Url,t.originalName)),r}var D$=/["'“”‘’`]([^"'“”‘’`\n]{1,120})["'“”‘’`]/g;function L$(t){if(!t)return null;for(let e of t.matchAll(D$)){let r=e[1].trim();if(Co(r))return r}for(let e of t.split(/\s+/)){let r=Xl(e);if(r&&Co(r))return r}return null}async function ww(t,e){let r=[];if(!Array.isArray(t))return r;let n=null,s=async()=>{if(n)return n;try{n=(await e?.sampleFilesService?.list()??[]).map(i=>i.absolutePath.split(/[/\\]/).pop()?.toLowerCase()??"").filter(Boolean)}catch{n=[]}return n};for(let[o,i]of t.entries()){if(!Fi(i))continue;let a=o+1;if(i.fileAssets&&i.fileAssets.length>0){let u=[],d=!1;for(let m of i.fileAssets)try{if(await Vd(m,e)){d=!0;break}u.push(`"${m.originalName}" (${m.storedPath}): not found in any storage and no r2Url`)}catch(h){u.push(`"${m.originalName}": ${h instanceof Error?h.message:String(h)}`)}d||r.push({stepNumber:a,stepText:i.text??"",fileName:i.fileAssets[0].originalName,reason:"assets_unresolvable",errors:u});continue}let l=L$(i.text);!l||(await s()).includes(l.toLowerCase())||r.push({stepNumber:a,stepText:i.text??"",fileName:l,reason:"named_file_without_assets"})}return r}var $$=new Set(["screenshot","full_page_screenshot","snapshot","wait_for_element","run_js"]);function Kd(t,{allowBareToken:e=!0}={}){if(!t)return null;let r=t.trim(),n=bn(r),s=n||!e?null:j$(r),o=n??s;if(!o)return null;let i=bn(An(o));if(!i)return null;try{let a=new URL(i);if(a.username||a.password)return null;let l=a.host.split(":")[0].startsWith("[")?a.host.match(/\[[^\]]+\]/)?.[0]??a.hostname:a.hostname;if(!l||Xn(l)==="unspecified"||Co(l))return null}catch{return null}return i}function j$(t){for(let e of U$(t)){let r=Xl(e);if(!r||Co(r)||!/[a-z]/i.test(r))continue;let n=An(r),s=bn(n);if(s)try{let o=new URL(s).hostname;if(F$(o))return r}catch{}}return null}function U$(t){let e=[],r="";for(let n of t)n<=" "?(r&&e.push(r),r=""):r+=n;return r&&e.push(r),e}function F$(t){let e=t.toLowerCase();if(e==="localhost"||e.charAt(0)==="["&&e.charAt(e.length-1)==="]")return!0;for(let r of e)if(r===".")return!0;return!1}function Tw(t=[],e="other-step"){if(!t.length)return null;let r=e==="setup-step"?t.filter(s=>s.type==="setup"):t.filter(s=>s.type==="action"&&q$(s.text)),n=e==="setup-step";for(let s of r){let o=Kd(s.text,{allowBareToken:n});if(o)return o}return null}function q$(t){return t?/\b(?:open|navigate(?:\s+to)?|go\s+to|visit|load|browse\s+to)\b/i.test(t):!1}function B$(t,e){let r=Kd(e.config?.initialUrl);if(r)return{source:"config",url:r};let n=Tw(e.steps,"setup-step");if(n)return{source:"setup-step",url:n};let s=Kd(t);if(s)return{source:"config",url:s};let o=Tw(e.steps,"other-step");return o?{source:"other-step",url:o}:{source:null,url:null}}function Hd(t){return $$.has(t)}function xw(t){return t.replace(/\s+/g," ").trim().toLowerCase()}function z$(t){let e=[],r=/['"]([^'"]+)['"]/g,n;for(;n=r.exec(t);)n[1].trim()&&e.push(n[1].trim());return e}function V$(t){let e=new Set;for(let r of[t.text,...(t.criteria??[]).map(n=>n.check)]){for(let n of z$(r))e.add(n);e.add(r)}return[...e].filter(r=>r.trim().length>0)}function Ew(t){let e=`${t.text} ${(t.criteria??[]).map(r=>r.check).join(" ")}`;return/validation|error|message/i.test(e)}function H$(t){return t.type==="verify"&&!Ew(t)}function W$(t,e){if(!t||e.type!=="verify"||!Ew(e))return!1;let r=xw(t);return V$(e).some(n=>r.includes(xw(n)))}async function G$(t,e,r){let s=`Classify the user message as "edit" or "explore".
721
+ `),r=e.match(/credential\s+'([^']+)'|'([^']+@[^']+)'/i);return r?r[1]??r[2]:e.match(/[\w.+-]+@[\w.-]+\.\w+/)?.[0]}function pw(t){let e=t.map((u,d)=>u.authRole==="login"?d:-1).filter(u=>u>=0);if(e.length<=1)return t;let r=e.map(u=>t[u]),n=f$(r),s=g$(r),o=s?` as '${s}'`:"",a={text:n?`Sign in with ${n}${o}`:`Log in${o}`,type:"setup",authRole:"login"},l=e[0],c=[];for(let u=0;u<t.length;u++){if(t[u].authRole==="login"){u===l&&c.push(a);continue}c.push(t[u])}return c}function Wl(t,e){if(e?.authModeHint==="under_test")return{steps:t,authMode:"under_test"};let r=t.filter(a=>a.authRole);if(r.length===0)return{steps:t};let n=r.filter(a=>a.authRole==="probe"),s=r.filter(a=>a.authRole==="login"),o=t.filter(a=>!a.authRole);return{steps:pw([...n,...s,...o]),authMode:"precondition"}}function y$(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function mw(t){let e;try{e=new URL(t)}catch{return null}let r=e.pathname,n=r&&r!=="/"?r:e.origin;return n?{kind:"urlMatches",pattern:y$(n)}:null}function hw(t){return t.length===0?null:Math.max(...t)}function fw(t,e){return t.some((r,n)=>n>e&&r.type==="action")}var v$="Verify the session is already authenticated";function gw(t,e,r){if(t.authMode==="precondition")return t;let n={text:v$,type:"setup",authRole:"probe",authCheck:r},s=t.steps.map((a,l)=>l<=e?{...a,type:"setup",authRole:"login"}:{...a}),{steps:o,authMode:i}=Wl([n,...s]);return{...t,steps:o,authMode:i,updatedAt:Date.now()}}import{z as $e}from"zod";var b$=$e.object({stepIndex:$e.number().describe("1-based step number from the test plan (step 1, 2, 3...)")}),_$={description:"Signal that you are starting work on a specific step. Call this BEFORE performing actions for each step to track progress.",inputSchema:b$},w$=$e.object({check:$e.string(),passed:$e.boolean(),note:$e.string().describe('For criteria about messages or text: include the exact text you see on screen (e.g., "Actual text: Incorrect code. Try again."). This captures real UI copy for QA records.').optional()}),S$=$e.object({stepIndex:$e.number().describe("1-based test-plan step number (step 1, 2, 3...)"),status:$e.enum(["passed","failed","warning","skipped"]),note:$e.string().optional(),criteriaResults:$e.array(w$).optional()}),T$=$e.object({status:$e.enum(["passed","failed"]),summary:$e.string(),stepResults:$e.array(S$).describe("Exactly one entry per test-plan step, covering EVERY step \u2014 including setup steps and steps that passed trivially. A run_complete missing any step verdict is rejected."),reflection:$e.string().describe("Brief self-assessment: wrong clicks, retries, confusing UI elements during the test run.")}),x$={description:"Complete test run with results.",inputSchema:T$},yw=$e.object({text:$e.string().describe('Describe WHAT to do, not HOW. NEVER include tool names, coordinates, or implementation details. For relative dates (today, tomorrow, next week), use ONLY the relative term\u2014never the specific date. For values that must be unique per run, use {{unique}} for name/text fields (e.g., "Set Name to User{{unique}}") or {{timestamp}} for emails/IDs (e.g., "Set Email to test-{{timestamp}}@example.com"). NEVER hardcode example values for unique fields. Steps must read like user instructions.'),type:$e.enum(["setup","action","verify"]),criteria:$e.array($e.object({check:$e.string(),strict:$e.boolean()})).optional()}),I$=$e.object({reason:$e.string().describe("Why this change is needed"),stepIndex:$e.number().describe("1-based step number to insert/update (step 1, 2, 3...). For add: steps inserted starting here."),action:$e.enum(["update","add","remove"]),newStep:yw.describe("For update: the updated step. For single add.").optional(),newSteps:$e.array(yw).describe("For adding multiple steps at once. Preferred for extending test coverage.").optional()}),vw={description:"Propose changes to the test plan. User must approve before applying. Use newSteps array for adding multiple steps.",inputSchema:I$},k$=$e.object({title:$e.string().describe("Short, descriptive title for the issue"),description:$e.string().describe("Detailed description of what is wrong"),severity:$e.enum(["high","medium","low"]).describe("Issue severity"),category:$e.enum(["visual","content","logical","ux"]).describe("Issue category"),confidence:$e.number().describe("Confidence level 0.0-1.0 that this is a real issue"),reproSteps:$e.array($e.string()).describe("Human-readable reproduction steps anyone could follow")}),E$={description:"Report a quality issue detected in the current screenshot. Use for visual glitches, content problems, logical inconsistencies, or UX issues.",inputSchema:k$},A$=$e.object({stepIndex:$e.number().describe("1-based step number that is blocked (step 1, 2, 3...)"),attempted:$e.string().describe("What you tried to do"),obstacle:$e.string().describe("What prevented you from succeeding"),question:$e.string().describe("Specific question for the user about how to proceed")}),R$={description:"Report that a step cannot be completed and you need user guidance. Use when: element unresponsive, expected content missing, step instructions unclear, action failed, or application returned an error. Report the issue first (report_issue), then call this. Do NOT improvise workarounds.",inputSchema:A$},C$=$e.object({email:$e.string().describe("Email address to check. The runtime normalizes this to the canonical testing email configured for the session.")}),M$={description:"Check recent messages for the session canonical testing email. Use after signup when a page asks for an email verification link or code.",inputSchema:C$},P$=$e.object({key:$e.string().describe('Name for the value (e.g., "last short term booking contract#")'),value:$e.string().describe("The value to save")}),bw={description:"Save a value to run memory. The value will be visible under RUN MEMORY in the system prompt for the remainder of this run (and subsequent plans in a sequential batch). Use whenever the test plan asks you to save data to memory.",inputSchema:P$},zd={signal_step:_$,run_complete:x$,propose_update:vw,report_issue:E$,exploration_blocked:R$,check_email:M$,read_source_document:Od},Gl={propose_update:vw},Kl={...xs,...zd},Yl={...xo,...zd};function Jl(t){return{...Io(t),...zd}}function An(t){let e=_w(t.trim()).trim();return!e||e==="about:blank"||/^[a-z][a-z0-9+.-]*:\/\//i.test(e)?e:`https://${e}`}function _w(t){return t.replace(/^['"`‘’“”]+|['"`‘’“”]+$/g,"")}function Xl(t){return t.replace(/^[\s*_~`'"([{<‘’“”]+/,"").replace(/[\s*_~`'")\]}>.,;:!?‘’“”]+$/,"")}var O$=/\.(pdf|docx?|xlsx?|pptx?|csv|tsv|rtf|odt|ods|odp|txt|md|json|xml|ya?ml|png|jpe?g|gif|webp|bmp|svg|tiff?|heic|heif|ico|zip|rar|7z|gz|tar|eml|msg|mp4|mov|avi|mkv|webm|mp3|wav|m4a)$/i;function Co(t){return t?O$.test(_w(t.trim())):!1}async function N$(t,e){let r=await import("node:os"),n=await import("node:fs"),s=await import("node:path"),i=(await import("node:crypto")).createHash("sha256").update(t).digest("hex").slice(0,16),a=s.join(r.tmpdir(),"agentiqa-attachments",i);n.existsSync(a)||n.mkdirSync(a,{recursive:!0});let l=e.replace(/[/\\]/g,"_"),c=s.join(a,l);if(n.existsSync(c))return c;let u=await fetch(t);if(!u.ok)throw new Error(`Failed to download ${t}: ${u.status}`);let d=new Uint8Array(await u.arrayBuffer()),m=`${c}.partial-${process.pid}`;return n.writeFileSync(m,d),n.renameSync(m,c),c}async function Vd(t,e){let r;return t.storedPath.startsWith("/")&&(await import("node:fs")).existsSync(t.storedPath)&&(r=t.storedPath),r||(r=await e?.testAssetStorageService?.getAbsolutePath(t.storedPath).catch(()=>{})),r||(r=await e?.attachmentStorageService?.getAbsolutePath(t.storedPath).catch(()=>{})),!r&&t.r2Url&&(r=await N$(t.r2Url,t.originalName)),r}var D$=/["'“”‘’`]([^"'“”‘’`\n]{1,120})["'“”‘’`]/g;function L$(t){if(!t)return null;for(let e of t.matchAll(D$)){let r=e[1].trim();if(Co(r))return r}for(let e of t.split(/\s+/)){let r=Xl(e);if(r&&Co(r))return r}return null}async function ww(t,e){let r=[];if(!Array.isArray(t))return r;let n=null,s=async()=>{if(n)return n;try{n=(await e?.sampleFilesService?.list()??[]).map(i=>i.absolutePath.split(/[/\\]/).pop()?.toLowerCase()??"").filter(Boolean)}catch{n=[]}return n};for(let[o,i]of t.entries()){if(!Fi(i))continue;let a=o+1;if(i.fileAssets&&i.fileAssets.length>0){let u=[],d=!1;for(let m of i.fileAssets)try{if(await Vd(m,e)){d=!0;break}u.push(`"${m.originalName}" (${m.storedPath}): not found in any storage and no r2Url`)}catch(h){u.push(`"${m.originalName}": ${h instanceof Error?h.message:String(h)}`)}d||r.push({stepNumber:a,stepText:i.text??"",fileName:i.fileAssets[0].originalName,reason:"assets_unresolvable",errors:u});continue}let l=L$(i.text);!l||(await s()).includes(l.toLowerCase())||r.push({stepNumber:a,stepText:i.text??"",fileName:l,reason:"named_file_without_assets"})}return r}var $$=new Set(["screenshot","full_page_screenshot","snapshot","wait_for_element","run_js"]);function Kd(t,{allowBareToken:e=!0}={}){if(!t)return null;let r=t.trim(),n=bn(r),s=n||!e?null:j$(r),o=n??s;if(!o)return null;let i=bn(An(o));if(!i)return null;try{let a=new URL(i);if(a.username||a.password)return null;let l=a.host.split(":")[0].startsWith("[")?a.host.match(/\[[^\]]+\]/)?.[0]??a.hostname:a.hostname;if(!l||Xn(l)==="unspecified"||Co(l))return null}catch{return null}return i}function j$(t){for(let e of U$(t)){let r=Xl(e);if(!r||Co(r)||!/[a-z]/i.test(r))continue;let n=An(r),s=bn(n);if(s)try{let o=new URL(s).hostname;if(F$(o))return r}catch{}}return null}function U$(t){let e=[],r="";for(let n of t)n<=" "?(r&&e.push(r),r=""):r+=n;return r&&e.push(r),e}function F$(t){let e=t.toLowerCase();if(e==="localhost"||e.charAt(0)==="["&&e.charAt(e.length-1)==="]")return!0;for(let r of e)if(r===".")return!0;return!1}function Tw(t=[],e="other-step"){if(!t.length)return null;let r=e==="setup-step"?t.filter(s=>s.type==="setup"):t.filter(s=>s.type==="action"&&q$(s.text)),n=e==="setup-step";for(let s of r){let o=Kd(s.text,{allowBareToken:n});if(o)return o}return null}function q$(t){return t?/\b(?:open|navigate(?:\s+to)?|go\s+to|visit|load|browse\s+to)\b/i.test(t):!1}function B$(t,e){let r=Kd(e.config?.initialUrl);if(r)return{source:"config",url:r};let n=Tw(e.steps,"setup-step");if(n)return{source:"setup-step",url:n};let s=Kd(t);if(s)return{source:"config",url:s};let o=Tw(e.steps,"other-step");return o?{source:"other-step",url:o}:{source:null,url:null}}function Hd(t){return $$.has(t)}function xw(t){return t.replace(/\s+/g," ").trim().toLowerCase()}function z$(t){let e=[],r=/['"]([^'"]+)['"]/g,n;for(;n=r.exec(t);)n[1].trim()&&e.push(n[1].trim());return e}function V$(t){let e=new Set;for(let r of[t.text,...(t.criteria??[]).map(n=>n.check)]){for(let n of z$(r))e.add(n);e.add(r)}return[...e].filter(r=>r.trim().length>0)}function Ew(t){let e=`${t.text} ${(t.criteria??[]).map(r=>r.check).join(" ")}`;return/validation|error|message/i.test(e)}function H$(t){return t.type==="verify"&&!Ew(t)}function W$(t,e){if(!t||e.type!=="verify"||!Ew(e))return!1;let r=xw(t);return V$(e).some(n=>r.includes(xw(n)))}async function G$(t,e,r){let s=`Classify the user message as "edit" or "explore".
722
722
 
723
723
  CURRENT TEST PLAN STEPS:
724
724
  ${e.map((o,i)=>`${i+1}. ${o.text}`).join(`
@@ -827,7 +827,7 @@ When typing into form fields, ALWAYS verify you are targeting the correct field:
827
827
  `:`- For VERIFY: take a screenshot FIRST, then evaluate each criteria entry (\u2022 = strict, \u25CB = warning). The screenshot is mandatory; without it you have no evidence the validation message actually appeared. Capture the actual on-screen text in criteriaResult.note.
828
828
  - For a RUN of consecutive static field-value verifies on one settled page (e.g. checking many extracted or displayed values), do NOT scroll the viewport field by field: call snapshot ONCE \u2014 the full-page accessibility tree contains every field's text regardless of scroll position \u2014 and grade the whole run of verifies from it plus the screenshot above. Scroll only if a value is genuinely absent from the snapshot.
829
829
  `)+`- For criteria about messages, text, or UI state: read the EXACT text on screen and include it in the criteriaResult note (e.g., note: "Actual text: Incorrect code. Try again.")
830
- - When done, call run_complete with all step results
830
+ - When done, call run_complete with a stepResults entry for EVERY plan step (one per step, 1-based stepIndex) \u2014 a run_complete missing any step's verdict is rejected and must be resubmitted
831
831
 
832
832
  === TRANSIENT VERIFY EVIDENCE ===
833
833
  Tool results may include a pageSnapshot section named [Between screenshots]. Lines like transient: "Settings saved" describe DOM text that appeared and disappeared between still screenshots. Treat matching transient text as valid evidence for verify criteria, and quote the annotation in criteriaResult.note.
@@ -897,7 +897,7 @@ SCOPE GUIDANCE:
897
897
 
898
898
  FORMATTING:
899
899
  - Do NOT use emojis in any text responses or summaries
900
- `+A+z+R}var Rr=class extends In{deps;pendingUserMessages=[];_activeRun=void 0;_activeTestPlan=void 0;_currentStepIndex=null;_currentStepText=null;_screenshots=[];_suppressNotifications=!1;_editOnly=!1;_runMemory=new Map;_onRunMemoryUpdate=null;_forcedErrorSummary=null;_lastSeededSessionId=void 0;_verifyEvidenceStepIndexes=new Set;_autoVerifyEvidenceStepIndexes=new Set;_stableVerifyPageSnapshot=null;_stableVerifySnapshotStep=null;_prepassSkippedStepIndexes=[];_migrationEligible=!1;_profileCaptureAllowed=!1;_observedLoginStepIndexes=new Set;_authLandingUrl=null;_lastObservedActionUrl=null;_captureAuthLandingNext=!1;constructor(e,r){super(e,r),this.deps=r}onEndRun(){let e=this._lastSeededSessionId??this.sessionId;this.deps.computerUseService?.clearCredentials?.(e),this._lastSeededSessionId=void 0}repairDanglingToolCalls(e){for(let r=0;r<e.length;r++){let n=e[r];if(n.role!=="model")continue;let s=(n.parts??[]).filter(l=>l?.functionCall?.id).map(l=>({id:l.functionCall.id,name:l.functionCall.name}));if(s.length===0)continue;let o=new Set;for(let l=r+1;l<e.length&&e[l].role==="user";l++)for(let c of e[l].parts??[])c?.functionResponse?.id&&o.add(c.functionResponse.id);let i=s.filter(l=>!o.has(l.id));if(i.length===0)continue;this.log("info","RunnerRuntime","Repairing dangling tool calls",{count:i.length,tools:i.map(l=>l.name)});let a=r+1;for(;a<e.length&&e[a].role!=="user";)a++;(a>=e.length||!e[a].parts)&&(e.splice(r+1,0,{role:"user",parts:[]}),a=r+1);for(let l of i)e[a].parts.push({functionResponse:{name:l.name,id:l.id,response:{status:"skipped",reason:"execution stopped"}}})}}injectUserMessage(e){this.pendingUserMessages.push(e)}async resetForNextPlan(e={}){let r=e.keepMemory??!0;if(this._isRunning)throw new Error("resetForNextPlan: cannot reset while a run is in progress \u2014 stop() first");this._lastSeededSessionId&&(this.deps.computerUseService?.clearCredentials?.(this._lastSeededSessionId),this._lastSeededSessionId=void 0),this.log("info","RunnerRuntime","reset_for_next_plan",{keepMemory:r,hadActiveRun:this._activeRun!==void 0,traceLength:this.conversationTrace.length,pendingUserMessages:this.pendingUserMessages.length});try{await this.deps.computerUseService.cleanupSession(this.sessionId)}catch(n){this.log("warn","RunnerRuntime","reset_for_next_plan:cleanup_session_failed",{error:n instanceof Error?n.message:String(n)})}this.clearConversationTrace(),this.pendingUserMessages=[],this._screenshots=[],this._currentStepIndex=null,this._currentStepText=null,this._editOnly=!1,this._suppressNotifications=!1,this._verifyEvidenceStepIndexes.clear(),this._autoVerifyEvidenceStepIndexes.clear(),this._stableVerifyPageSnapshot=null,this._stableVerifySnapshotStep=null,this._migrationEligible=!1,this._observedLoginStepIndexes=new Set,this._authLandingUrl=null,this._lastObservedActionUrl=null,this._captureAuthLandingNext=!1,this._activeRun=void 0,this._activeTestPlan=void 0,this._currentRunId=void 0,this._runMemory=new Map,this._onRunMemoryUpdate=null,this._checkEmailCallCount=0,this._canonicalEmailWritten=!1,this._emailVerificationExpected=!1}emit(e,r){return super.emit(e,r)}async persistConversationTrace(e,r){this.stripOldScreenshots(r),await this.baseDeps.chatRepo.upsertSession({...e,updatedAt:Date.now(),conversationTrace:r})}extractErrorMessage(e){try{let n=e.match(/\{[\s\S]*\}/);if(n){let s=JSON.parse(n[0]);if(s.error?.message)return s.error.message;if(s.message)return s.message}}catch{}let r=e.match(/page\.goto:\s*(.+)/);return r?r[1]:e}isVerifyStep(e){return!e||!this._activeTestPlan?!1:this._activeTestPlan.steps[e-1]?.type==="verify"}findUnverifiedVerifyStepBefore(e){if(!this._activeTestPlan||!this._currentStepIndex||e<=this._currentStepIndex)return null;let r=this.isVerifyStep(this._currentStepIndex)?this._currentStepIndex:this._currentStepIndex+1;for(let n=r;n<e;n++)if(this.isVerifyStep(n)&&!this._verifyEvidenceStepIndexes.has(n))return n;return null}findFirstUnverifiedVerifyStep(){if(!this._activeTestPlan)return null;for(let e=1;e<=this._activeTestPlan.steps.length;e++)if(this.isVerifyStep(e)&&!this._verifyEvidenceStepIndexes.has(e))return e;return null}buildMissingEmailVerificationRejection(e,r){return e.args?.status!=="passed"||!Lt(r.session?.config??this._activeTestPlan?.config)?.canCheckInbox||!this._emailVerificationExpected||this._checkEmailCallCount>0?null:{response:{status:"error",error:"Account creation appears to require email verification but check_email was never called. Read the inbox via check_email and complete verification (click the verification link or enter the verification code) before reporting complete."},isMetaTool:!0,resetLoopDetector:!0}}buildVerifyEvidenceRequiredResult(e){let r=this._activeTestPlan?.steps[e-1]?.text??"the verify step";return{response:{status:"error",error:`Step ${e} is a VERIFY step and requires explicit verification evidence before advancing. Call screenshot or wait_for_element for: ${r}`},isMetaTool:!0,resetLoopDetector:!0}}maybeBlockActionUntilVerifyEvidence(e){if(!this.isVerifyStep(this._currentStepIndex)||this._verifyEvidenceStepIndexes.has(this._currentStepIndex)||Hd(e))return null;let r=this._currentStepIndex,n=this._currentStepText??this._activeTestPlan?.steps[r-1]?.text??"";return{response:{status:"error",error:`Step ${r} requires explicit verification evidence before any other browser action. Call screenshot or wait_for_element for: ${n}`},isMetaTool:!0,resetLoopDetector:!0}}currentPlanStepIndex(){return this._currentStepIndex??void 0}maybeUseAutoCapturedVerifyEvidence(e){if(!this.isVerifyStep(this._currentStepIndex))return null;let r=this._currentStepIndex;if(!this._autoVerifyEvidenceStepIndexes.has(r)||!Hd(e))return null;let n=this._currentStepText??this._activeTestPlan?.steps[r-1]?.text??"",s=this._stableVerifyPageSnapshot??"",o=this._stableVerifySnapshotStep;return{response:{status:"ok",note:o&&o!==r?`Stable page snapshot reused from step ${o}. The page state is unchanged \u2014 use it to evaluate: ${n}`:`Matching evidence for step ${r} was already captured in the previous action result. Use that evidence for: ${n}`,...s?{pageSnapshot:s}:{}},isMetaTool:!0,resetLoopDetector:!0}}recordVerifyEvidenceFromToolResult(e,r){this.isVerifyStep(this._currentStepIndex)&&Hd(r)&&this._verifyEvidenceStepIndexes.add(this._currentStepIndex);let n=this._currentStepIndex?this._currentStepIndex+1:null,s=n?this._activeTestPlan?.steps[n-1]:void 0,o=typeof e.response?.pageSnapshot=="string"?e.response.pageSnapshot:"";if(n&&s?.type==="verify"&&W$(o,s)){this._verifyEvidenceStepIndexes.add(n),this._autoVerifyEvidenceStepIndexes.add(n);let i=`Captured matching evidence for upcoming verify step ${n}: ${s.text}. Use this evidence for the verify step before changing the form state.`;e.response={...e.response,verifyEvidence:i,pageSnapshot:`${o}
900
+ `+A+z+R}var Rr=class extends In{deps;pendingUserMessages=[];_activeRun=void 0;_activeTestPlan=void 0;_currentStepIndex=null;_currentStepText=null;_screenshots=[];_suppressNotifications=!1;_editOnly=!1;_runMemory=new Map;_onRunMemoryUpdate=null;_forcedErrorSummary=null;_lastSeededSessionId=void 0;_verifyEvidenceStepIndexes=new Set;_autoVerifyEvidenceStepIndexes=new Set;_stableVerifyPageSnapshot=null;_stableVerifySnapshotStep=null;_prepassSkippedStepIndexes=[];_incompleteRunCompleteRejections=0;_migrationEligible=!1;_profileCaptureAllowed=!1;_observedLoginStepIndexes=new Set;_authLandingUrl=null;_lastObservedActionUrl=null;_captureAuthLandingNext=!1;constructor(e,r){super(e,r),this.deps=r}onEndRun(){let e=this._lastSeededSessionId??this.sessionId;this.deps.computerUseService?.clearCredentials?.(e),this._lastSeededSessionId=void 0}repairDanglingToolCalls(e){for(let r=0;r<e.length;r++){let n=e[r];if(n.role!=="model")continue;let s=(n.parts??[]).filter(l=>l?.functionCall?.id).map(l=>({id:l.functionCall.id,name:l.functionCall.name}));if(s.length===0)continue;let o=new Set;for(let l=r+1;l<e.length&&e[l].role==="user";l++)for(let c of e[l].parts??[])c?.functionResponse?.id&&o.add(c.functionResponse.id);let i=s.filter(l=>!o.has(l.id));if(i.length===0)continue;this.log("info","RunnerRuntime","Repairing dangling tool calls",{count:i.length,tools:i.map(l=>l.name)});let a=r+1;for(;a<e.length&&e[a].role!=="user";)a++;(a>=e.length||!e[a].parts)&&(e.splice(r+1,0,{role:"user",parts:[]}),a=r+1);for(let l of i)e[a].parts.push({functionResponse:{name:l.name,id:l.id,response:{status:"skipped",reason:"execution stopped"}}})}}injectUserMessage(e){this.pendingUserMessages.push(e)}async resetForNextPlan(e={}){let r=e.keepMemory??!0;if(this._isRunning)throw new Error("resetForNextPlan: cannot reset while a run is in progress \u2014 stop() first");this._lastSeededSessionId&&(this.deps.computerUseService?.clearCredentials?.(this._lastSeededSessionId),this._lastSeededSessionId=void 0),this.log("info","RunnerRuntime","reset_for_next_plan",{keepMemory:r,hadActiveRun:this._activeRun!==void 0,traceLength:this.conversationTrace.length,pendingUserMessages:this.pendingUserMessages.length});try{await this.deps.computerUseService.cleanupSession(this.sessionId)}catch(n){this.log("warn","RunnerRuntime","reset_for_next_plan:cleanup_session_failed",{error:n instanceof Error?n.message:String(n)})}this.clearConversationTrace(),this.pendingUserMessages=[],this._screenshots=[],this._currentStepIndex=null,this._currentStepText=null,this._editOnly=!1,this._suppressNotifications=!1,this._verifyEvidenceStepIndexes.clear(),this._autoVerifyEvidenceStepIndexes.clear(),this._stableVerifyPageSnapshot=null,this._stableVerifySnapshotStep=null,this._migrationEligible=!1,this._observedLoginStepIndexes=new Set,this._authLandingUrl=null,this._lastObservedActionUrl=null,this._captureAuthLandingNext=!1,this._activeRun=void 0,this._activeTestPlan=void 0,this._currentRunId=void 0,this._runMemory=new Map,this._onRunMemoryUpdate=null,this._checkEmailCallCount=0,this._canonicalEmailWritten=!1,this._emailVerificationExpected=!1}emit(e,r){return super.emit(e,r)}async persistConversationTrace(e,r){this.stripOldScreenshots(r),await this.baseDeps.chatRepo.upsertSession({...e,updatedAt:Date.now(),conversationTrace:r})}extractErrorMessage(e){try{let n=e.match(/\{[\s\S]*\}/);if(n){let s=JSON.parse(n[0]);if(s.error?.message)return s.error.message;if(s.message)return s.message}}catch{}let r=e.match(/page\.goto:\s*(.+)/);return r?r[1]:e}isVerifyStep(e){return!e||!this._activeTestPlan?!1:this._activeTestPlan.steps[e-1]?.type==="verify"}findUnverifiedVerifyStepBefore(e){if(!this._activeTestPlan||!this._currentStepIndex||e<=this._currentStepIndex)return null;let r=this.isVerifyStep(this._currentStepIndex)?this._currentStepIndex:this._currentStepIndex+1;for(let n=r;n<e;n++)if(this.isVerifyStep(n)&&!this._verifyEvidenceStepIndexes.has(n))return n;return null}findFirstUnverifiedVerifyStep(){if(!this._activeTestPlan)return null;for(let e=1;e<=this._activeTestPlan.steps.length;e++)if(this.isVerifyStep(e)&&!this._verifyEvidenceStepIndexes.has(e))return e;return null}buildMissingEmailVerificationRejection(e,r){return e.args?.status!=="passed"||!Lt(r.session?.config??this._activeTestPlan?.config)?.canCheckInbox||!this._emailVerificationExpected||this._checkEmailCallCount>0?null:{response:{status:"error",error:"Account creation appears to require email verification but check_email was never called. Read the inbox via check_email and complete verification (click the verification link or enter the verification code) before reporting complete."},isMetaTool:!0,resetLoopDetector:!0}}buildVerifyEvidenceRequiredResult(e){let r=this._activeTestPlan?.steps[e-1]?.text??"the verify step";return{response:{status:"error",error:`Step ${e} is a VERIFY step and requires explicit verification evidence before advancing. Call screenshot or wait_for_element for: ${r}`},isMetaTool:!0,resetLoopDetector:!0}}maybeBlockActionUntilVerifyEvidence(e){if(!this.isVerifyStep(this._currentStepIndex)||this._verifyEvidenceStepIndexes.has(this._currentStepIndex)||Hd(e))return null;let r=this._currentStepIndex,n=this._currentStepText??this._activeTestPlan?.steps[r-1]?.text??"";return{response:{status:"error",error:`Step ${r} requires explicit verification evidence before any other browser action. Call screenshot or wait_for_element for: ${n}`},isMetaTool:!0,resetLoopDetector:!0}}currentPlanStepIndex(){return this._currentStepIndex??void 0}maybeUseAutoCapturedVerifyEvidence(e){if(!this.isVerifyStep(this._currentStepIndex))return null;let r=this._currentStepIndex;if(!this._autoVerifyEvidenceStepIndexes.has(r)||!Hd(e))return null;let n=this._currentStepText??this._activeTestPlan?.steps[r-1]?.text??"",s=this._stableVerifyPageSnapshot??"",o=this._stableVerifySnapshotStep;return{response:{status:"ok",note:o&&o!==r?`Stable page snapshot reused from step ${o}. The page state is unchanged \u2014 use it to evaluate: ${n}`:`Matching evidence for step ${r} was already captured in the previous action result. Use that evidence for: ${n}`,...s?{pageSnapshot:s}:{}},isMetaTool:!0,resetLoopDetector:!0}}recordVerifyEvidenceFromToolResult(e,r){this.isVerifyStep(this._currentStepIndex)&&Hd(r)&&this._verifyEvidenceStepIndexes.add(this._currentStepIndex);let n=this._currentStepIndex?this._currentStepIndex+1:null,s=n?this._activeTestPlan?.steps[n-1]:void 0,o=typeof e.response?.pageSnapshot=="string"?e.response.pageSnapshot:"";if(n&&s?.type==="verify"&&W$(o,s)){this._verifyEvidenceStepIndexes.add(n),this._autoVerifyEvidenceStepIndexes.add(n);let i=`Captured matching evidence for upcoming verify step ${n}: ${s.text}. Use this evidence for the verify step before changing the form state.`;e.response={...e.response,verifyEvidence:i,pageSnapshot:`${o}
901
901
 
902
902
  [Verify evidence]
903
903
  ${i}`}}if(o&&this.isVerifyStep(this._currentStepIndex)&&this._activeTestPlan){this._stableVerifyPageSnapshot=o,this._stableVerifySnapshotStep=this._currentStepIndex;let i=this._currentStepIndex+1;for(;i<=this._activeTestPlan.steps.length;){let a=this._activeTestPlan.steps[i-1];if(!a||!H$(a))break;this._verifyEvidenceStepIndexes.has(i)||(this._verifyEvidenceStepIndexes.add(i),this._autoVerifyEvidenceStepIndexes.add(i)),i++}}}maybeRecordAuthObservation(e,r){if(!this._migrationEligible)return;let n=r.loopDetectorUpdate?.url;if(typeof n=="string"&&n&&(this._lastObservedActionUrl=n,this._captureAuthLandingNext&&(this._authLandingUrl=n,this._captureAuthLandingNext=!1)),ou(e)){if(this._currentStepIndex!=null){let s=this._currentStepIndex-1;s>=0&&(this._observedLoginStepIndexes.add(s),this.log("info","RunnerRuntime","authRecipe:observed_login_step",{stepIndex0:s}))}this._captureAuthLandingNext=!0}}async handleToolCall(e,r){switch(e.name){case"run_complete":{let i=e.args.status==="passed"?this.findFirstUnverifiedVerifyStep():null;if(i!==null)return this.buildVerifyEvidenceRequiredResult(i);let a=this.buildMissingEmailVerificationRejection(e,r);return a||this.handleRunComplete(e,r)}case"signal_step":return this.handleSignalStep(e,r);case"propose_update":return this.handleProposeUpdate(e,r);case"report_issue":{let i=this.maybeRejectUnconfirmedNegativeState(e,"issue");return i||this.handleReportIssue(e,r)}case"exploration_blocked":{let i=this.maybeRejectUnconfirmedNegativeState(e,"block");if(i)return i;let a=await this.maybeRejectRecoveredBlockedNegativeState(e,r);return a||this.handleBlocked(e,r)}case"save_to_memory":return this.handleSaveToMemory(e);case"check_email":return this.handleCheckEmail(e,r);case"read_source_document":return this.handleReadSourceDocument(e)}let n=this.maybeUseAutoCapturedVerifyEvidence(e.name);if(n)return n;let s=this.maybeBlockActionUntilVerifyEvidence(e.name);if(s)return s;let o=await this.executeAction(e,r);return this.recordVerifyEvidenceFromToolResult(o,e.name),this.maybeRecordAuthObservation(e.name,o),o.screenshotBase64&&this._screenshots.push({base64:o.screenshotBase64,actionName:e.name,timestamp:Date.now(),stepIndex:this._currentStepIndex??void 0,stepText:this._currentStepText??void 0,intent:typeof e.args?.intent=="string"?e.args.intent:void 0}),o.message&&this._activeRun&&(o.message={...o.message,runId:this._activeRun.id}),o}maybeRejectUnconfirmedNegativeState(e,r){return ql({claimText:an(e.args??{}),evidenceActions:this.recentActionsSnapshot(),terminalAction:r})}async maybeRejectRecoveredBlockedNegativeState(e,r){let n=an(e.args??{});if(!n||!En(n)||Eo(n)||r.isMobile)return null;let s=await this.deps.computerUseService.invoke({sessionId:r.session.id,action:"screenshot",args:{},config:r.session.config});return Ao(e.args??{},s.aiSnapshot)?{response:{status:"block_rejected",reason:"negative_state_recovered",instruction:Is()},isMetaTool:!0}:null}async buildSystemPrompt(e){return""}getToolSet(e){return this._editOnly?Gl:{...e.isMobile?Jl(e.devicePlatform):e.snapshotOnly?Yl:Kl,save_to_memory:bw}}async onIterationStart(e,r,n){let s=this.pendingUserMessages.shift();if(s){let o={id:fe("msg"),sessionId:r.id,role:"user",text:s,timestamp:Date.now()};await this.baseDeps.chatRepo.addMessage(o),this.emit("message:added",{sessionId:r.id,message:o}),e.push({role:"user",parts:[{text:s}]})}}async onLoopExhausted(e,r){let n=this._activeRun;if(n){n.status="error",n.summary="Run exceeded iteration limit",n.endedAt=Date.now(),n.updatedAt=Date.now(),await this.deps.testPlanV2RunRepo.upsert(n);let s={id:fe("msg"),sessionId:e.id,role:"model",text:`Test run stopped: exceeded the maximum of ${r} iterations before completing all steps.`,timestamp:Date.now(),runId:n.id};await this.baseDeps.chatRepo.addMessage(s),this.emit("message:added",{sessionId:e.id,message:s}),this.emit("run:completed",{sessionId:e.id,run:n,runMemory:Object.fromEntries(this._runMemory)})}else await super.onLoopExhausted(e,r)}getSupervisorTaskDescription(e){let r=this._activeTestPlan;if(!r||!r.steps||r.steps.length===0)return e;let n=this._currentStepIndex,s=r.steps.length,o=Math.floor(Date.now()/1e3),i=r.steps.map((l,c)=>{let u=c+1,d=n===u?" <-- CURRENT STEP":"";return`${n===u?"\u2192":" "} ${u}. [${l.type.toUpperCase()}] ${vn(l.text,o)}${d}`}).join(`
@@ -906,7 +906,7 @@ ${i}`}}if(o&&this.isVerifyStep(this._currentStepIndex)&&this._activeTestPlan){th
906
906
  ${a}
907
907
 
908
908
  STEPS:
909
- ${i}`}mergePrepassSkippedResults(e,r){return dw(this._prepassSkippedStepIndexes,e,r.steps)}getMissingPassedStepIndexes(e,r){let n=new Set(this._prepassSkippedStepIndexes.map(i=>i+1)),s=new Set(r.map(i=>typeof i.stepIndex=="number"?i.stepIndex:-1)),o=[];for(let i=0;i<e.steps.length;i++){let a=i+1;n.has(a)||s.has(a)||o.push(a)}return o}isRunAuthenticated(e,r,n){let s=r.steps.some(o=>o.authRole==="login")&&r.steps.every((o,i)=>o.authRole==="login"?n.find(a=>a.stepIndex===i+1)?.status==="passed":!0);return e==="passed"||this._prepassSkippedStepIndexes.length>0||s}async maybeCaptureProjectProfile(e,r,n,s){if(!(!this._profileCaptureAllowed||!this.isRunAuthenticated(n,r,s)))try{let i=await this.deps.computerUseService.getStorageState?.(this.sessionId);i&&(await this.deps.computerUseService.saveProjectProfile?.(r.projectId,i),this.log("info","RunnerRuntime","authPrepass:captured_profile",{projectId:r.projectId,reason:n==="passed"?"run_passed":this._prepassSkippedStepIndexes.length>0?"probe_passed":"login_passed"}))}catch(i){this.log("warn","RunnerRuntime","authPrepass:capture_error",{error:String(i?.message??i)})}}computeMigrationEligible(e,r){return(this._profileCaptureAllowed||!!e.config?.persistBrowserProfile&&!e.config?.forceLoggedOut&&r.authMode!=="under_test")&&r.authMode!=="precondition"&&r.authMode!=="under_test"&&!!this.deps.testPlanV2Repo?.upsert}async maybeMigratePlanFromCapture(e,r,n){if(!this._migrationEligible||this._observedLoginStepIndexes.size===0||!this.isRunAuthenticated(r,e,n))return;let s=hw([...this._observedLoginStepIndexes]);if(s===null)return;if(!fw(e.steps,s)){this.log("info","RunnerRuntime","authRecipe:skip_no_post_login_action",{planId:e.id,loginBlockEnd:s});return}let o=this._authLandingUrl??this._lastObservedActionUrl,i=o?mw(o):null;if(!i){this.log("info","RunnerRuntime","authRecipe:skip_no_authcheck",{planId:e.id,landingUrl:o??null});return}try{let a=gw(e,s,i);await this.deps.testPlanV2Repo.upsert(a),this.log("info","RunnerRuntime","authRecipe:migrated_plan",{planId:e.id,loginBlockEnd:s,authCheck:i})}catch(a){this.log("warn","RunnerRuntime","authRecipe:migrate_error",{planId:e.id,error:String(a?.message??a)})}}async handleRunComplete(e,r){let n=this._activeRun,s=this._activeTestPlan,{session:o}=r;if(!n)return{response:{status:"ok",note:"No active run \u2014 stopping loop"},done:!0,isMetaTool:!0};let i=e.args.status==="passed"?"passed":"failed",a=String(e.args.summary??""),l=String(e.args.reflection??"").trim(),c=(e.args.stepResults??[]).map((h,p)=>{let y=h.stepIndex??p+1,g=y-1,w=(h.criteriaResults??[]).map(E=>({check:E.check,strict:s.steps[g]?.criteria?.find(v=>v.check===E.check)?.strict??!0,passed:E.passed,note:E.note})),x=h.status??"passed",S=Aw(x,w);return S!==x&&this.log("warn","RunnerRuntime","run_complete:step_status_derived_from_criteria",{stepIndex:y,reported:x,derived:S}),{stepIndex:y,status:S,note:h.note,step:s.steps[g],criteriaResults:w}}),u=this.mergePrepassSkippedResults(c,s);if(i==="passed"){let h=this.getMissingPassedStepIndexes(s,u);if(h.length>0){this.log("warn","RunnerRuntime","run_complete:incomplete_step_results",{status:i,missingStepIndexes:h}),i="failed";for(let p of h)u.push({stepIndex:p,status:"warning",note:"No run-complete verdict was returned for this step.",step:s.steps[p-1]})}}i==="passed"&&u.some(h=>h.status==="failed")&&(this.log("warn","RunnerRuntime","run_complete:status_downgraded_by_step_failures",{failedSteps:u.filter(h=>h.status==="failed").map(h=>h.stepIndex)}),i="failed"),n.status=i,n.summary=a,n.stepResults=u,n.terminationReason="completed",n.endedAt=Date.now(),n.updatedAt=Date.now(),await this.deps.testPlanV2RunRepo.upsert(n),await this.maybeCaptureProjectProfile(o,s,i,u),await this.maybeMigratePlanFromCapture(s,i,u);let d=this._screenshots.map(({base64:h,...p})=>p),m={id:fe("msg"),sessionId:o.id,role:"model",text:`Test ${i}. ${a}`,timestamp:Date.now(),actionName:"run_complete",actionArgs:{status:i,stepResults:u,screenshots:d,reflection:l},runId:n.id};return await this.baseDeps.chatRepo.addMessage(m),this.emit("message:added",{sessionId:o.id,message:m}),this.baseDeps.sink.emit({kind:"user_action",ts:Date.now(),sessionId:o.id,action:"test_plan_run_complete",targetId:n.id,metadata:{testPlanId:n.testPlanId,status:n.status,summary:n.summary,stepResults:n.stepResults,testPlanTitle:s?.title}}),this.emit("run:completed",{sessionId:o.id,run:n,runMemory:Object.fromEntries(this._runMemory)}),this._suppressNotifications||this.deps.notificationService?.showTestRunComplete(o.id,s.title,n.status,{projectId:o.projectId,testPlanId:s.id}),{response:{status:"ok"},done:!0,isMetaTool:!0}}handleSignalStep(e,r){let n=e.args.stepIndex,s=this.findUnverifiedVerifyStepBefore(n);return s!==null?(this._currentStepIndex=s,this._currentStepText=this._activeTestPlan.steps[s-1]?.text??null,Promise.resolve(this.buildVerifyEvidenceRequiredResult(s))):(this._currentStepIndex=n,this._currentStepText=this._activeTestPlan.steps[n-1]?.text??null,Promise.resolve({response:{status:"ok",stepIndex:n},isMetaTool:!0,resetLoopDetector:!0}))}async handleProposeUpdate(e,r){let{session:n}=r,s=this._activeRun,o={id:fe("msg"),sessionId:n.id,role:"model",text:"",timestamp:Date.now(),actionName:"propose_update",actionArgs:e.args,runId:s?.id};if(await this.baseDeps.chatRepo.addMessage(o),this.emit("message:added",{sessionId:n.id,message:o}),s){let i=typeof e.args?.reason=="string"&&e.args.reason?`Proposed test plan update: ${e.args.reason}`:"Agent proposed a test plan update";s.status="blocked",s.summary=i,s.terminationReason="supervisor_halted",s.endedAt=Date.now(),s.updatedAt=Date.now(),await this.deps.testPlanV2RunRepo.upsert(s),this.emit("run:completed",{sessionId:n.id,run:s,runMemory:Object.fromEntries(this._runMemory)})}return{response:{status:"awaiting_approval"},done:!0,isMetaTool:!0}}async handleReadSourceDocument(e){let r=await Vl({documentName:typeof e.args?.documentName=="string"?e.args.documentName:void 0,focus:typeof e.args?.focus=="string"?e.args.focus:void 0,refs:Y_(this._activeTestPlan),readBytes:n=>zl(n,{attachmentStorageService:this.deps.attachmentStorageService,testAssetStorageService:this.deps.testAssetStorageService}),llm:this.sourceDocumentLlm()});return this.log("info","RunnerRuntime","read_source_document",{requested:e.args?.documentName??"(auto)",status:r.status,documentName:r.documentName}),{response:r,isMetaTool:!0}}async handleReportIssue(e,r){let{session:n}=r,s=this._activeRun,o=this._activeTestPlan,i=Hi(this.recentActionsSnapshot(),an(e.args??{}));if(i)return{response:{status:"issue_rejected",reason:"disabled_control_prerequisite",instruction:Fl(i)},isMetaTool:!0};if(!zi(an(e.args??{}))&&Wi({category:e.args?.category,reproSteps:e.args?.reproSteps,evidenceActions:this.recentActionsSnapshot()}))return{response:{status:"issue_rejected",reason:"empty_evidence_bundle",instruction:Bl()},isMetaTool:!0};let a=await this.deps.computerUseService.invoke({sessionId:n.id,action:"screenshot",args:{},config:n.config});if(En(an(e.args??{}))&&Ao(e.args??{},a.aiSnapshot))return{response:{status:"issue_rejected",reason:"negative_state_recovered",instruction:Is()},isMetaTool:!0};let l=fe("issue"),c=!1,u;if(a.screenshot)try{let w=await this.baseDeps.imageStorageService?.save({projectId:o.projectId,issueId:l,type:"issue",base64:a.screenshot});c=!0,w&&typeof w=="object"&&w.url&&(u=w.url)}catch(w){this.log("error","RunnerRuntime","Failed to save issue screenshot",{error:w?.message})}let d=Date.now(),m=this.recentActionsSnapshot(),h=m.length>0?{capturedAt:d,actions:m}:void 0,p={id:l,projectId:o.projectId,status:"pending",title:e.args.title,description:e.args.description,severity:e.args.severity,category:e.args.category,confidence:e.args.confidence,reproSteps:e.args.reproSteps??[],hasScreenshot:c,screenshotUrl:u,url:a.url??"",detectedAt:d,detectedInRunId:s?.id,detectedInSessionId:n.id,relatedTestPlanId:o.id,relatedStepIndex:this._currentStepIndex??void 0,evidence:h,createdAt:d,updatedAt:d};await this.deps.issuesRepo.upsert(p);let y=p,g={id:fe("msg"),sessionId:n.id,role:"model",text:"",timestamp:Date.now(),actionName:"report_issue",actionArgs:{issueId:y.id,...e.args,evidence:h,screenshotUrl:u},runId:s?.id};return await this.baseDeps.chatRepo.addMessage(g),this.emit("message:added",{sessionId:n.id,message:g}),{response:{status:"reported",issueId:y.id},isMetaTool:!0}}async handleBlocked(e,r){let{session:n}=r,s=this._activeRun,o=Number(e.args?.stepIndex??this._currentStepIndex??1),i=String(e.args?.attempted??"").trim(),a=String(e.args?.obstacle??"").trim(),l=String(e.args?.question??"").trim(),c={sessionId:n.id,id:fe("msg"),role:"model",text:l,timestamp:Date.now(),actionName:"exploration_blocked",actionArgs:{stepIndex:o,attempted:i,obstacle:a,question:l},runId:s?.id};return await this.baseDeps.chatRepo.addMessage(c),this.emit("message:added",{sessionId:n.id,message:c}),s&&(s.status="blocked",s.summary=a||"Agent blocked",s.terminationReason="blocked_by_issue",s.endedAt=Date.now(),s.updatedAt=Date.now(),await this.deps.testPlanV2RunRepo.upsert(s),this.emit("run:completed",{sessionId:n.id,run:s,runMemory:Object.fromEntries(this._runMemory)})),{response:{status:"awaiting_user_guidance"},done:!0,isMetaTool:!0}}handleSaveToMemory(e){this.log("info","RunMemory","handleSaveToMemory:entry",{rawArgs:e.args,argsType:typeof e.args,argKeys:e.args?Object.keys(e.args):[],hasUpdateCallback:!!this._onRunMemoryUpdate,currentKeys:[...this._runMemory.keys()]});let r=String(e.args?.key??"").trim(),n=String(e.args?.value??"");if(!r)return this.log("warn","RunMemory","save_to_memory rejected: missing key",{rawArgs:e.args,valueGiven:n}),{response:{status:"error",error:"key is required"},isMetaTool:!0};this._runMemory.set(r,n);let s=Object.fromEntries(this._runMemory);return this._onRunMemoryUpdate&&this._onRunMemoryUpdate(s),this.log("info","RunMemory","save_to_memory:saved",{key:r,value:n,totalKeys:this._runMemory.size}),{response:{status:"ok",saved:r},isMetaTool:!0}}async blockMissingRunMemoryReferences(e,r,n){let s=n.map(m=>m.key),o=[...this._runMemory.keys()],i=s.length===1?"key":"keys",a=o.length>0?` Available run memory keys: ${Gd(o)}.`:" Run memory is empty.",l=`Missing run memory ${i}: ${Gd(s)}.`+a,c=`The test plan references missing run memory ${i}. Run the prerequisite plan that saves the value before retrying, or provide the value in run memory.`,u=n[0];this._currentStepIndex=u?.stepIndex??1,this._currentStepText=u?.stepText??null;let d={sessionId:e.id,id:fe("msg"),role:"model",text:c,timestamp:Date.now(),actionName:"exploration_blocked",actionArgs:{stepIndex:this._currentStepIndex,attempted:`Use run memory ${i}: ${Gd(s)}`,obstacle:l,question:c},runId:r.id};await this.baseDeps.chatRepo.addMessage(d),this.emit("message:added",{sessionId:e.id,message:d}),r.status="blocked",r.summary=l,r.terminationReason="supervisor_halted",r.endedAt=Date.now(),r.updatedAt=Date.now(),await this.deps.testPlanV2RunRepo.upsert(r),this.emit("run:completed",{sessionId:e.id,run:r,runMemory:Object.fromEntries(this._runMemory)})}async blockUnresolvableUploadAssets(e,r,n){let s=n[0],o=`Required file asset missing for step ${s.stepNumber}: "${s.fileName}" \u2014 the plan step has no usable fileAssets (re-save the plan with the file attached, or attach the file in chat and rebuild the plan).`+(n.length>1?` ${n.length-1} more upload step(s) have the same problem.`:""),i="This plan has upload steps whose file cannot be provided in this run. Re-save the plan with the file attached, or attach the file in chat and rebuild the plan, then retry.";this.log("error","UploadAssetGuard","uploadAssetGuard:blocked",{runId:r.id,testPlanId:r.testPlanId,failures:n.map(l=>({stepNumber:l.stepNumber,fileName:l.fileName,reason:l.reason,errors:l.errors}))}),this._currentStepIndex=s.stepNumber,this._currentStepText=s.stepText||null;let a={sessionId:e.id,id:fe("msg"),role:"model",text:o,timestamp:Date.now(),actionName:"exploration_blocked",actionArgs:{stepIndex:s.stepNumber,attempted:`Resolve the file asset for upload step ${s.stepNumber}`,obstacle:o,question:i},runId:r.id};await this.baseDeps.chatRepo.addMessage(a),this.emit("message:added",{sessionId:e.id,message:a}),r.status="blocked",r.summary=o,r.terminationReason="supervisor_halted",r.endedAt=Date.now(),r.updatedAt=Date.now(),await this.deps.testPlanV2RunRepo.upsert(r),this.emit("run:completed",{sessionId:e.id,run:r,runMemory:Object.fromEntries(this._runMemory)})}async startRun(e,r,n){if(this.deps.authService.isAuthRequired()&&!await this.deps.authService.ensureAuthenticated()){this.emit("auth:required",{sessionId:this.sessionId,action:"start_run"});return}if(this._isRunning){this.emit("session:error",{sessionId:this.sessionId,error:"Already running"});return}if(!await(this.baseDeps.llmAccessService?.hasApiKey()??Promise.resolve(!0))){this.emit("session:error",{sessionId:this.sessionId,error:"Gemini API key not set"});return}this.beginRun(),e=await this.ensureTestingEmailPolicyAttached(e,{forceRefresh:!0}),e.config&&(e.config.isTestPlanReplay=!0),this._runMemory=new Map(Object.entries(n?.initialMemory??{})),this._onRunMemoryUpdate=n?.onMemoryUpdate??null;let{url:o,source:i}=B$(e.config?.initialUrl,r);if(o&&o!==e.config?.initialUrl){let m=e.config?.initialUrl;e.config={...e.config,initialUrl:o},this.log("info","RunnerRuntime","startRun:resolvedInitialUrl",{source:i,resolvedInitialUrl:o,originalInitialUrl:m})}this.log("info","RunMemory","startRun:options",{hasInitialMemoryOption:n?.initialMemory!==void 0,hasUpdateCallbackOption:typeof n?.onMemoryUpdate=="function",initialKeys:[...this._runMemory.keys()],initialEntryCount:this._runMemory.size}),this.baseDeps.sink.emit({kind:"user_action",ts:Date.now(),sessionId:e.id,action:"run_test_plan",targetId:r.id,metadata:{title:r.title,stepCount:r.steps.length,steps:r.steps.map(m=>m.text)}});let a={id:fe("run"),testPlanId:r.id,projectId:r.projectId,sessionId:e.id,status:"running",origin:Y$(this.baseDeps.sessionMetaExtras?.platform),createdAt:Date.now(),updatedAt:Date.now(),stepResults:[],batchRunId:n?.batchRunId,batchSeq:n?.batchSeq};await this.deps.testPlanV2RunRepo.upsert(a),this._currentRunId=a.id,this._activeRun=a,this._activeTestPlan=r,this._currentStepIndex=null,this._currentStepText=null,this._screenshots=[],this._verifyEvidenceStepIndexes.clear(),this._autoVerifyEvidenceStepIndexes.clear(),this._stableVerifyPageSnapshot=null,this._stableVerifySnapshotStep=null,this._prepassSkippedStepIndexes=[],this._migrationEligible=!1,this._profileCaptureAllowed=!1,this._observedLoginStepIndexes=new Set,this._authLandingUrl=null,this._lastObservedActionUrl=null,this._captureAuthLandingNext=!1,this._suppressNotifications=n?.suppressNotifications??!1,this._editOnly=!1,this.emit("run:started",{sessionId:e.id,runId:a.id,startedAt:a.createdAt});let l={id:fe("msg"),sessionId:e.id,role:"user",actionName:"run_test_plan",text:"Run test plan",timestamp:Date.now(),runId:a.id};await this.baseDeps.chatRepo.addMessage(l),this.emit("message:added",{sessionId:e.id,message:l});let c=Date.now(),u=null,d=!1;try{let m=nj(r,this._runMemory);if(m.length>0){await this.blockMissingRunMemoryReferences(e,a,m);return}let h=await cr(ww(r.steps,this.deps),c,"upload asset preflight");if(h.length>0){await this.blockUnresolvableUploadAssets(e,a,h);return}await cr(this.deps.computerUseService.cleanupSession(this.sessionId),c,"browser cleanup"),d=!0;let p=(e.config?.platform||"web")==="mobile",y=p?e.config?.mobileConfig?.platform||"android":void 0,g=y==="ios",w=p&&gs(e.config?.mobileConfig),x=!p&&(e.config?.snapshotOnly??!1),S=await cr(this.deps.memoryRepo.list(r.projectId),c,"memory load"),E=await cr(this.deps.secretsService.listProjectCredentials(r.projectId),c,"credential metadata load");await cr(Jn(e.id,r.projectId,this.deps),c,"credential seeding"),this._lastSeededSessionId=e.id;let v=await cr(this.deps.issuesRepo.list(r.projectId,{status:["confirmed","dismissed"]}),c,"known issue load");this.baseDeps.sink.emit({kind:"session_start",ts:Date.now(),sessionId:e.id,sessionMeta:{...ps(e,this.baseDeps.sessionMetaExtras),memoryItems:S.map(F=>F.text),credentialNames:E.map(F=>F.name)}}),this.conversationTrace=[],await cr(this.baseDeps.chatRepo.upsertSession({...e,conversationTrace:[],updatedAt:Date.now()}),c,"conversation reset");let T=await cr(this.ensureConversationTraceLoaded(e),c,"conversation trace load"),_=null;if(e.config?.persistBrowserProfile===!0)try{_=await cr(this.deps.computerUseService.getProjectProfile?.(r.projectId)??Promise.resolve(null),c,"auth profile load")??null}catch(F){this.log("warn","RunnerRuntime","authPrepass:seed_error",{error:String(F?.message??F)})}let I=await Ki({projectId:r.projectId,runKind:"runner_plan",sessionConfig:e.config??{},testPlan:r,capturedProfile:_,projectCredentials:await cr(Il(r.projectId,this.deps.secretsService),c,"credential login-policy load")});e.config={...e.config,...I.sessionConfigPatch},I.profileSeed.seeded&&this.log("info","RunnerRuntime","authPrepass:seeded_profile",{cookies:I.profileSeed.cookies,origins:I.profileSeed.origins}),this._profileCaptureAllowed=I.runner?.profileCaptureAllowed===!0,this._migrationEligible=this.computeMigrationEligible(e,r);let k=I.runner?.prepassDecision??{mode:"run_all"},A=I.runner?.conditionalAuthStepIndexes??[],R=Object.fromEntries(this._runMemory);this.log("info","RunMemory","buildRunnerPrompt:calling",{runMemoryKeys:Object.keys(R)}),this.systemPromptText=await cr(kw(r,"run",S,E,v,p,x,w,this.deps,y,R,Lt(e.config),[],A),c,"runner prompt build"),this.log("info","RunMemory","buildRunnerPrompt:result",{promptLength:this.systemPromptText.length,hasRunMemorySection:this.systemPromptText.includes("RUN MEMORY"),hasToolMention:this.systemPromptText.includes("save_to_memory")}),T.push({role:"user",parts:[{text:this.systemPromptText}]});let U,z;if(p){let F=e.config?.mobileConfig,{screenSize:se,screenshot:Z,initWarnings:ne}=await cr(this.baseDeps.mobileMcpService.initializeSession(e.id,{deviceType:y,deviceMode:F.deviceMode,avdName:F?.avdName,deviceId:F?.deviceId,simulatorUdid:F?.simulatorUdid,deviceUdid:F?.deviceUdid,apkPath:F?.apkPath,appPath:F?.appPath,appIdentifier:F?.appIdentifier,shouldReinstallApp:F?.shouldReinstallApp??!0,appLoadWaitSeconds:F?.appLoadWaitSeconds??5}),c,"mobile session initialization");this.mobileActionExecutor.setScreenSize(se),U=Z.base64,z=`Execute the test plan now.
909
+ ${i}`}mergePrepassSkippedResults(e,r){return dw(this._prepassSkippedStepIndexes,e,r.steps)}getMissingPassedStepIndexes(e,r){let n=new Set(this._prepassSkippedStepIndexes.map(i=>i+1)),s=new Set(r.map(i=>typeof i.stepIndex=="number"?i.stepIndex:-1)),o=[];for(let i=0;i<e.steps.length;i++){let a=i+1;n.has(a)||s.has(a)||o.push(a)}return o}isRunAuthenticated(e,r,n){let s=r.steps.some(o=>o.authRole==="login")&&r.steps.every((o,i)=>o.authRole==="login"?n.find(a=>a.stepIndex===i+1)?.status==="passed":!0);return e==="passed"||this._prepassSkippedStepIndexes.length>0||s}async maybeCaptureProjectProfile(e,r,n,s){if(!(!this._profileCaptureAllowed||!this.isRunAuthenticated(n,r,s)))try{let i=await this.deps.computerUseService.getStorageState?.(this.sessionId);i&&(await this.deps.computerUseService.saveProjectProfile?.(r.projectId,i),this.log("info","RunnerRuntime","authPrepass:captured_profile",{projectId:r.projectId,reason:n==="passed"?"run_passed":this._prepassSkippedStepIndexes.length>0?"probe_passed":"login_passed"}))}catch(i){this.log("warn","RunnerRuntime","authPrepass:capture_error",{error:String(i?.message??i)})}}computeMigrationEligible(e,r){return(this._profileCaptureAllowed||!!e.config?.persistBrowserProfile&&!e.config?.forceLoggedOut&&r.authMode!=="under_test")&&r.authMode!=="precondition"&&r.authMode!=="under_test"&&!!this.deps.testPlanV2Repo?.upsert}async maybeMigratePlanFromCapture(e,r,n){if(!this._migrationEligible||this._observedLoginStepIndexes.size===0||!this.isRunAuthenticated(r,e,n))return;let s=hw([...this._observedLoginStepIndexes]);if(s===null)return;if(!fw(e.steps,s)){this.log("info","RunnerRuntime","authRecipe:skip_no_post_login_action",{planId:e.id,loginBlockEnd:s});return}let o=this._authLandingUrl??this._lastObservedActionUrl,i=o?mw(o):null;if(!i){this.log("info","RunnerRuntime","authRecipe:skip_no_authcheck",{planId:e.id,landingUrl:o??null});return}try{let a=gw(e,s,i);await this.deps.testPlanV2Repo.upsert(a),this.log("info","RunnerRuntime","authRecipe:migrated_plan",{planId:e.id,loginBlockEnd:s,authCheck:i})}catch(a){this.log("warn","RunnerRuntime","authRecipe:migrate_error",{planId:e.id,error:String(a?.message??a)})}}async handleRunComplete(e,r){let n=this._activeRun,s=this._activeTestPlan,{session:o}=r;if(!n)return{response:{status:"ok",note:"No active run \u2014 stopping loop"},done:!0,isMetaTool:!0};let i=e.args.status==="passed"?"passed":"failed",a=String(e.args.summary??""),l=String(e.args.reflection??"").trim(),c=[],u=(e.args.stepResults??[]).map((p,y)=>{let g=p.stepIndex??y+1,w=g-1,x=(p.criteriaResults??[]).map(I=>({check:I.check,strict:s.steps[w]?.criteria?.find(k=>k.check===I.check)?.strict??!0,passed:I.passed,note:I.note})),S=p.status??"passed",E=Aw(S,x),v=s.steps[w]?.criteria??[],T=s.steps[w]?.type==="verify"&&v.length>0&&x.length===0&&E==="passed";T?(E=v.some(I=>I.strict)?"failed":"warning",c.push(g),this.log("warn","RunnerRuntime","run_complete:verify_step_unsubstantiated",{stepIndex:g,reported:S,derived:E,planCriteria:v.length})):E!==S&&this.log("warn","RunnerRuntime","run_complete:step_status_derived_from_criteria",{stepIndex:g,reported:S,derived:E});let _=T?`Reported passed but no verification criteria were graded \u2014 assertion unconfirmed.${p.note?` ${p.note}`:""}`:p.note;return{stepIndex:g,status:E,note:_,step:s.steps[w],criteriaResults:x}}),d=this.mergePrepassSkippedResults(u,s);if(i==="passed"){let p=this.getMissingPassedStepIndexes(s,d);if(p.length>0){if(this._incompleteRunCompleteRejections===0)return this._incompleteRunCompleteRejections++,this.log("warn","RunnerRuntime","run_complete:incomplete_step_results_rejected",{status:i,missingStepIndexes:p}),{response:{status:"incomplete_step_results",missingStepIndexes:p,instruction:`Your run_complete is missing a verdict for step(s) ${p.join(", ")} of the ${s.steps.length}-step plan. Resubmit run_complete with a stepResults entry for EVERY plan step (1-based stepIndex), including the steps you already reported.`},isMetaTool:!0};this.log("warn","RunnerRuntime","run_complete:incomplete_step_results",{status:i,missingStepIndexes:p}),i="failed";for(let y of p)d.push({stepIndex:y,status:"warning",note:"No run-complete verdict was returned for this step.",step:s.steps[y-1]});a=`Failed closed: no verdict was returned for step(s) ${p.join(", ")} even after a retry. Model summary: ${a}`}}i==="passed"&&d.some(p=>p.status==="failed")&&(this.log("warn","RunnerRuntime","run_complete:status_downgraded_by_step_failures",{failedSteps:d.filter(p=>p.status==="failed").map(p=>p.stepIndex)}),i="failed",c.length>0&&(a=`Failed: verify step(s) ${c.join(", ")} were reported passed without any graded criteria \u2014 the assertion was never confirmed. Model summary: ${a}`)),n.status=i,n.summary=a,n.stepResults=d,n.terminationReason="completed",n.endedAt=Date.now(),n.updatedAt=Date.now(),await this.deps.testPlanV2RunRepo.upsert(n),await this.maybeCaptureProjectProfile(o,s,i,d),await this.maybeMigratePlanFromCapture(s,i,d);let m=this._screenshots.map(({base64:p,...y})=>y),h={id:fe("msg"),sessionId:o.id,role:"model",text:`Test ${i}. ${a}`,timestamp:Date.now(),actionName:"run_complete",actionArgs:{status:i,stepResults:d,screenshots:m,reflection:l},runId:n.id};return await this.baseDeps.chatRepo.addMessage(h),this.emit("message:added",{sessionId:o.id,message:h}),this.baseDeps.sink.emit({kind:"user_action",ts:Date.now(),sessionId:o.id,action:"test_plan_run_complete",targetId:n.id,metadata:{testPlanId:n.testPlanId,status:n.status,summary:n.summary,stepResults:n.stepResults,testPlanTitle:s?.title}}),this.emit("run:completed",{sessionId:o.id,run:n,runMemory:Object.fromEntries(this._runMemory)}),this._suppressNotifications||this.deps.notificationService?.showTestRunComplete(o.id,s.title,n.status,{projectId:o.projectId,testPlanId:s.id}),{response:{status:"ok"},done:!0,isMetaTool:!0}}handleSignalStep(e,r){let n=e.args.stepIndex,s=this.findUnverifiedVerifyStepBefore(n);return s!==null?(this._currentStepIndex=s,this._currentStepText=this._activeTestPlan.steps[s-1]?.text??null,Promise.resolve(this.buildVerifyEvidenceRequiredResult(s))):(this._currentStepIndex=n,this._currentStepText=this._activeTestPlan.steps[n-1]?.text??null,Promise.resolve({response:{status:"ok",stepIndex:n},isMetaTool:!0,resetLoopDetector:!0}))}async handleProposeUpdate(e,r){let{session:n}=r,s=this._activeRun,o={id:fe("msg"),sessionId:n.id,role:"model",text:"",timestamp:Date.now(),actionName:"propose_update",actionArgs:e.args,runId:s?.id};if(await this.baseDeps.chatRepo.addMessage(o),this.emit("message:added",{sessionId:n.id,message:o}),s){let i=typeof e.args?.reason=="string"&&e.args.reason?`Proposed test plan update: ${e.args.reason}`:"Agent proposed a test plan update";s.status="blocked",s.summary=i,s.terminationReason="supervisor_halted",s.endedAt=Date.now(),s.updatedAt=Date.now(),await this.deps.testPlanV2RunRepo.upsert(s),this.emit("run:completed",{sessionId:n.id,run:s,runMemory:Object.fromEntries(this._runMemory)})}return{response:{status:"awaiting_approval"},done:!0,isMetaTool:!0}}async handleReadSourceDocument(e){let r=await Vl({documentName:typeof e.args?.documentName=="string"?e.args.documentName:void 0,focus:typeof e.args?.focus=="string"?e.args.focus:void 0,refs:Y_(this._activeTestPlan),readBytes:n=>zl(n,{attachmentStorageService:this.deps.attachmentStorageService,testAssetStorageService:this.deps.testAssetStorageService}),llm:this.sourceDocumentLlm()});return this.log("info","RunnerRuntime","read_source_document",{requested:e.args?.documentName??"(auto)",status:r.status,documentName:r.documentName}),{response:r,isMetaTool:!0}}async handleReportIssue(e,r){let{session:n}=r,s=this._activeRun,o=this._activeTestPlan,i=Hi(this.recentActionsSnapshot(),an(e.args??{}));if(i)return{response:{status:"issue_rejected",reason:"disabled_control_prerequisite",instruction:Fl(i)},isMetaTool:!0};if(!zi(an(e.args??{}))&&Wi({category:e.args?.category,reproSteps:e.args?.reproSteps,evidenceActions:this.recentActionsSnapshot()}))return{response:{status:"issue_rejected",reason:"empty_evidence_bundle",instruction:Bl()},isMetaTool:!0};let a=await this.deps.computerUseService.invoke({sessionId:n.id,action:"screenshot",args:{},config:n.config});if(En(an(e.args??{}))&&Ao(e.args??{},a.aiSnapshot))return{response:{status:"issue_rejected",reason:"negative_state_recovered",instruction:Is()},isMetaTool:!0};let l=fe("issue"),c=!1,u;if(a.screenshot)try{let w=await this.baseDeps.imageStorageService?.save({projectId:o.projectId,issueId:l,type:"issue",base64:a.screenshot});c=!0,w&&typeof w=="object"&&w.url&&(u=w.url)}catch(w){this.log("error","RunnerRuntime","Failed to save issue screenshot",{error:w?.message})}let d=Date.now(),m=this.recentActionsSnapshot(),h=m.length>0?{capturedAt:d,actions:m}:void 0,p={id:l,projectId:o.projectId,status:"pending",title:e.args.title,description:e.args.description,severity:e.args.severity,category:e.args.category,confidence:e.args.confidence,reproSteps:e.args.reproSteps??[],hasScreenshot:c,screenshotUrl:u,url:a.url??"",detectedAt:d,detectedInRunId:s?.id,detectedInSessionId:n.id,relatedTestPlanId:o.id,relatedStepIndex:this._currentStepIndex??void 0,evidence:h,createdAt:d,updatedAt:d};await this.deps.issuesRepo.upsert(p);let y=p,g={id:fe("msg"),sessionId:n.id,role:"model",text:"",timestamp:Date.now(),actionName:"report_issue",actionArgs:{issueId:y.id,...e.args,evidence:h,screenshotUrl:u},runId:s?.id};return await this.baseDeps.chatRepo.addMessage(g),this.emit("message:added",{sessionId:n.id,message:g}),{response:{status:"reported",issueId:y.id},isMetaTool:!0}}async handleBlocked(e,r){let{session:n}=r,s=this._activeRun,o=Number(e.args?.stepIndex??this._currentStepIndex??1),i=String(e.args?.attempted??"").trim(),a=String(e.args?.obstacle??"").trim(),l=String(e.args?.question??"").trim(),c={sessionId:n.id,id:fe("msg"),role:"model",text:l,timestamp:Date.now(),actionName:"exploration_blocked",actionArgs:{stepIndex:o,attempted:i,obstacle:a,question:l},runId:s?.id};return await this.baseDeps.chatRepo.addMessage(c),this.emit("message:added",{sessionId:n.id,message:c}),s&&(s.status="blocked",s.summary=a||"Agent blocked",s.terminationReason="blocked_by_issue",s.endedAt=Date.now(),s.updatedAt=Date.now(),await this.deps.testPlanV2RunRepo.upsert(s),this.emit("run:completed",{sessionId:n.id,run:s,runMemory:Object.fromEntries(this._runMemory)})),{response:{status:"awaiting_user_guidance"},done:!0,isMetaTool:!0}}handleSaveToMemory(e){this.log("info","RunMemory","handleSaveToMemory:entry",{rawArgs:e.args,argsType:typeof e.args,argKeys:e.args?Object.keys(e.args):[],hasUpdateCallback:!!this._onRunMemoryUpdate,currentKeys:[...this._runMemory.keys()]});let r=String(e.args?.key??"").trim(),n=String(e.args?.value??"");if(!r)return this.log("warn","RunMemory","save_to_memory rejected: missing key",{rawArgs:e.args,valueGiven:n}),{response:{status:"error",error:"key is required"},isMetaTool:!0};this._runMemory.set(r,n);let s=Object.fromEntries(this._runMemory);return this._onRunMemoryUpdate&&this._onRunMemoryUpdate(s),this.log("info","RunMemory","save_to_memory:saved",{key:r,value:n,totalKeys:this._runMemory.size}),{response:{status:"ok",saved:r},isMetaTool:!0}}async blockMissingRunMemoryReferences(e,r,n){let s=n.map(m=>m.key),o=[...this._runMemory.keys()],i=s.length===1?"key":"keys",a=o.length>0?` Available run memory keys: ${Gd(o)}.`:" Run memory is empty.",l=`Missing run memory ${i}: ${Gd(s)}.`+a,c=`The test plan references missing run memory ${i}. Run the prerequisite plan that saves the value before retrying, or provide the value in run memory.`,u=n[0];this._currentStepIndex=u?.stepIndex??1,this._currentStepText=u?.stepText??null;let d={sessionId:e.id,id:fe("msg"),role:"model",text:c,timestamp:Date.now(),actionName:"exploration_blocked",actionArgs:{stepIndex:this._currentStepIndex,attempted:`Use run memory ${i}: ${Gd(s)}`,obstacle:l,question:c},runId:r.id};await this.baseDeps.chatRepo.addMessage(d),this.emit("message:added",{sessionId:e.id,message:d}),r.status="blocked",r.summary=l,r.terminationReason="supervisor_halted",r.endedAt=Date.now(),r.updatedAt=Date.now(),await this.deps.testPlanV2RunRepo.upsert(r),this.emit("run:completed",{sessionId:e.id,run:r,runMemory:Object.fromEntries(this._runMemory)})}async blockUnresolvableUploadAssets(e,r,n){let s=n[0],o=`Required file asset missing for step ${s.stepNumber}: "${s.fileName}" \u2014 the plan step has no usable fileAssets (re-save the plan with the file attached, or attach the file in chat and rebuild the plan).`+(n.length>1?` ${n.length-1} more upload step(s) have the same problem.`:""),i="This plan has upload steps whose file cannot be provided in this run. Re-save the plan with the file attached, or attach the file in chat and rebuild the plan, then retry.";this.log("error","UploadAssetGuard","uploadAssetGuard:blocked",{runId:r.id,testPlanId:r.testPlanId,failures:n.map(l=>({stepNumber:l.stepNumber,fileName:l.fileName,reason:l.reason,errors:l.errors}))}),this._currentStepIndex=s.stepNumber,this._currentStepText=s.stepText||null;let a={sessionId:e.id,id:fe("msg"),role:"model",text:o,timestamp:Date.now(),actionName:"exploration_blocked",actionArgs:{stepIndex:s.stepNumber,attempted:`Resolve the file asset for upload step ${s.stepNumber}`,obstacle:o,question:i},runId:r.id};await this.baseDeps.chatRepo.addMessage(a),this.emit("message:added",{sessionId:e.id,message:a}),r.status="blocked",r.summary=o,r.terminationReason="supervisor_halted",r.endedAt=Date.now(),r.updatedAt=Date.now(),await this.deps.testPlanV2RunRepo.upsert(r),this.emit("run:completed",{sessionId:e.id,run:r,runMemory:Object.fromEntries(this._runMemory)})}async startRun(e,r,n){if(this.deps.authService.isAuthRequired()&&!await this.deps.authService.ensureAuthenticated()){this.emit("auth:required",{sessionId:this.sessionId,action:"start_run"});return}if(this._isRunning){this.emit("session:error",{sessionId:this.sessionId,error:"Already running"});return}if(!await(this.baseDeps.llmAccessService?.hasApiKey()??Promise.resolve(!0))){this.emit("session:error",{sessionId:this.sessionId,error:"Gemini API key not set"});return}this.beginRun(),e=await this.ensureTestingEmailPolicyAttached(e,{forceRefresh:!0}),e.config&&(e.config.isTestPlanReplay=!0),this._runMemory=new Map(Object.entries(n?.initialMemory??{})),this._onRunMemoryUpdate=n?.onMemoryUpdate??null;let{url:o,source:i}=B$(e.config?.initialUrl,r);if(o&&o!==e.config?.initialUrl){let m=e.config?.initialUrl;e.config={...e.config,initialUrl:o},this.log("info","RunnerRuntime","startRun:resolvedInitialUrl",{source:i,resolvedInitialUrl:o,originalInitialUrl:m})}this.log("info","RunMemory","startRun:options",{hasInitialMemoryOption:n?.initialMemory!==void 0,hasUpdateCallbackOption:typeof n?.onMemoryUpdate=="function",initialKeys:[...this._runMemory.keys()],initialEntryCount:this._runMemory.size}),this.baseDeps.sink.emit({kind:"user_action",ts:Date.now(),sessionId:e.id,action:"run_test_plan",targetId:r.id,metadata:{title:r.title,stepCount:r.steps.length,steps:r.steps.map(m=>m.text)}});let a={id:fe("run"),testPlanId:r.id,projectId:r.projectId,sessionId:e.id,status:"running",origin:Y$(this.baseDeps.sessionMetaExtras?.platform),createdAt:Date.now(),updatedAt:Date.now(),stepResults:[],batchRunId:n?.batchRunId,batchSeq:n?.batchSeq};await this.deps.testPlanV2RunRepo.upsert(a),this._currentRunId=a.id,this._activeRun=a,this._activeTestPlan=r,this._currentStepIndex=null,this._currentStepText=null,this._screenshots=[],this._verifyEvidenceStepIndexes.clear(),this._autoVerifyEvidenceStepIndexes.clear(),this._stableVerifyPageSnapshot=null,this._stableVerifySnapshotStep=null,this._prepassSkippedStepIndexes=[],this._incompleteRunCompleteRejections=0,this._migrationEligible=!1,this._profileCaptureAllowed=!1,this._observedLoginStepIndexes=new Set,this._authLandingUrl=null,this._lastObservedActionUrl=null,this._captureAuthLandingNext=!1,this._suppressNotifications=n?.suppressNotifications??!1,this._editOnly=!1,this.emit("run:started",{sessionId:e.id,runId:a.id,startedAt:a.createdAt});let l={id:fe("msg"),sessionId:e.id,role:"user",actionName:"run_test_plan",text:"Run test plan",timestamp:Date.now(),runId:a.id};await this.baseDeps.chatRepo.addMessage(l),this.emit("message:added",{sessionId:e.id,message:l});let c=Date.now(),u=null,d=!1;try{let m=nj(r,this._runMemory);if(m.length>0){await this.blockMissingRunMemoryReferences(e,a,m);return}let h=await cr(ww(r.steps,this.deps),c,"upload asset preflight");if(h.length>0){await this.blockUnresolvableUploadAssets(e,a,h);return}await cr(this.deps.computerUseService.cleanupSession(this.sessionId),c,"browser cleanup"),d=!0;let p=(e.config?.platform||"web")==="mobile",y=p?e.config?.mobileConfig?.platform||"android":void 0,g=y==="ios",w=p&&gs(e.config?.mobileConfig),x=!p&&(e.config?.snapshotOnly??!1),S=await cr(this.deps.memoryRepo.list(r.projectId),c,"memory load"),E=await cr(this.deps.secretsService.listProjectCredentials(r.projectId),c,"credential metadata load");await cr(Jn(e.id,r.projectId,this.deps),c,"credential seeding"),this._lastSeededSessionId=e.id;let v=await cr(this.deps.issuesRepo.list(r.projectId,{status:["confirmed","dismissed"]}),c,"known issue load");this.baseDeps.sink.emit({kind:"session_start",ts:Date.now(),sessionId:e.id,sessionMeta:{...ps(e,this.baseDeps.sessionMetaExtras),memoryItems:S.map(F=>F.text),credentialNames:E.map(F=>F.name)}}),this.conversationTrace=[],await cr(this.baseDeps.chatRepo.upsertSession({...e,conversationTrace:[],updatedAt:Date.now()}),c,"conversation reset");let T=await cr(this.ensureConversationTraceLoaded(e),c,"conversation trace load"),_=null;if(e.config?.persistBrowserProfile===!0)try{_=await cr(this.deps.computerUseService.getProjectProfile?.(r.projectId)??Promise.resolve(null),c,"auth profile load")??null}catch(F){this.log("warn","RunnerRuntime","authPrepass:seed_error",{error:String(F?.message??F)})}let I=await Ki({projectId:r.projectId,runKind:"runner_plan",sessionConfig:e.config??{},testPlan:r,capturedProfile:_,projectCredentials:await cr(Il(r.projectId,this.deps.secretsService),c,"credential login-policy load")});e.config={...e.config,...I.sessionConfigPatch},I.profileSeed.seeded&&this.log("info","RunnerRuntime","authPrepass:seeded_profile",{cookies:I.profileSeed.cookies,origins:I.profileSeed.origins}),this._profileCaptureAllowed=I.runner?.profileCaptureAllowed===!0,this._migrationEligible=this.computeMigrationEligible(e,r);let k=I.runner?.prepassDecision??{mode:"run_all"},A=I.runner?.conditionalAuthStepIndexes??[],R=Object.fromEntries(this._runMemory);this.log("info","RunMemory","buildRunnerPrompt:calling",{runMemoryKeys:Object.keys(R)}),this.systemPromptText=await cr(kw(r,"run",S,E,v,p,x,w,this.deps,y,R,Lt(e.config),[],A),c,"runner prompt build"),this.log("info","RunMemory","buildRunnerPrompt:result",{promptLength:this.systemPromptText.length,hasRunMemorySection:this.systemPromptText.includes("RUN MEMORY"),hasToolMention:this.systemPromptText.includes("save_to_memory")}),T.push({role:"user",parts:[{text:this.systemPromptText}]});let U,z;if(p){let F=e.config?.mobileConfig,{screenSize:se,screenshot:Z,initWarnings:ne}=await cr(this.baseDeps.mobileMcpService.initializeSession(e.id,{deviceType:y,deviceMode:F.deviceMode,avdName:F?.avdName,deviceId:F?.deviceId,simulatorUdid:F?.simulatorUdid,deviceUdid:F?.deviceUdid,apkPath:F?.apkPath,appPath:F?.appPath,appIdentifier:F?.appIdentifier,shouldReinstallApp:F?.shouldReinstallApp??!0,appLoadWaitSeconds:F?.appLoadWaitSeconds??5}),c,"mobile session initialization");this.mobileActionExecutor.setScreenSize(se),U=Z.base64,z=`Execute the test plan now.
910
910
  Platform: mobile (${g?"iOS":"Android"})
911
911
  Device: ${F?.deviceMode==="connected"?F?.deviceId??"unknown":F?.deviceMode==="device"?F?.deviceUdid??"unknown":F?.avdName??"unknown"}`+(ne?.length?`
912
912
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentiqa",
3
- "version": "1.1.24",
3
+ "version": "1.1.25",
4
4
  "description": "AI-powered testing for web and mobile apps",
5
5
  "type": "module",
6
6
  "bin": "dist/cli.js",