Files

339 lines
131 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="en">
<head><meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Replay &amp; E2E Testing - agent-device</title>
<script>{;const saved = localStorage.getItem('rspress-theme-appearance');const preferDark = window.matchMedia('(prefers-color-scheme: dark)').matches;const isDark = !saved || saved === 'auto' ? preferDark : saved === 'dark';document.documentElement.classList.toggle('dark', isDark);document.documentElement.classList.toggle('rp-dark', isDark);document.documentElement.style.colorScheme = isDark ? 'dark' : 'light';}</script>
<link href="/agent-device/static/css/styles.cf5e9ab9a4.css" rel="stylesheet">
<script defer src="/agent-device/static/js/styles.308ea4bafe.js"></script>
<script defer src="/agent-device/static/js/lib-react.e84adf13b9.js"></script>
<script defer src="/agent-device/static/js/lib-router.270f17bfa6.js"></script>
<script defer src="/agent-device/static/js/476.52ed165336.js"></script>
<script defer src="/agent-device/static/js/index.8722046b0d.js"></script>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="generator" content="Rspress v2.0.12">
<meta property="og:url" content="https://oss.callstack.com/agent-device">
<meta property="og:type" content="website">
<meta property="og:title" content="Replay & E2E Testing - agent-device">
<meta property="og:image" content="https://oss.callstack.com/agent-device/og-image.jpg">
<meta property="og:description" content="Agents use refs for exploration and authoring. Replay scripts are deterministic runs that can be used for E2E testing.">
<link rel="icon" href="/agent-device/logo.svg" type="image/svg+xml">
<meta name="description" content="Agents use refs for exploration and authoring. Replay scripts are deterministic runs that can be used for E2E testing."></head>
<body>
<div id="__rspress_root"><link rel="preload" as="image" href="/agent-device/logo-light.svg"/><link rel="preload" as="image" href="/agent-device/logo-dark.svg"/><link rel="preload" as="image" href="/agent-device/static/image/abstract-atom.4eca0300ff.avif"/><header class="rp-nav"><div class="rp-nav__left"><div class="rp-nav__title"><a href="/agent-device/" class="rp-nav__title__link rp-link"><div class="rp-nav__title__logo"><img src="/agent-device/logo-light.svg" alt="logo" id="logo" class="rspress-logo rp-nav__title__logo-image rp-nav__title__logo-image--light"/><img src="/agent-device/logo-dark.svg" alt="logo" id="logo" class="rspress-logo rp-nav__title__logo-image rp-nav__title__logo-image--dark"/></div></a></div></div><div class="rp-nav__right"><button class="rp-search-button"><div class="rp-search-button__content"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none" viewBox="0 0 16 16" class="rp-search-button__icon"><path fill="#838289" d="M14.667 14.666h-1.333v-1.333h1.332zm-1.333-1.333H12V12h1.333zm-4-1.333H4v-1.334h5.334zM12 12h-1.335v-1.334h1.335zm-8-1.334H2.665V9.333H4zm6.665 0H9.334V9.333h1.332zm-8-1.333H1.335V4h1.333zm9.335 0h-1.335V4h1.335zM4 4H2.665V2.666H4zm6.665 0H9.334V2.666h1.332zM9.334 2.666H4V1.333h5.334z"></path></svg><span class="rp-search-button__word">Search</span></div><div class="rp-search-button__hotkey" style="opacity:0"><span></span><span>K</span></div></button><div class="rp-search-button--mobile"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none" viewBox="0 0 16 16"><path fill="#838289" d="M14.667 14.666h-1.333v-1.333h1.332zm-1.333-1.333H12V12h1.333zm-4-1.333H4v-1.334h5.334zM12 12h-1.335v-1.334h1.335zm-8-1.334H2.665V9.333H4zm6.665 0H9.334V9.333h1.332zm-8-1.333H1.335V4h1.333zm9.335 0h-1.335V4h1.335zM4 4H2.665V2.666H4zm6.665 0H9.334V2.666h1.332zM9.334 2.666H4V1.333h5.334z"></path></svg></div><ul class="rp-nav-menu rp-nav-menu--right"><li class="rp-nav-menu__item"><a href="/agent-device/docs/introduction" class="rp-nav-menu__item__container rp-link">Docs</a></li></ul><div class="rp-nav__others"><div class="rp-nav-menu__divider"></div><div role="button" tabindex="0" class="rspress-nav-appearance"><div class="rp-p-1 rp-border rp-border-solid rp-border-gray-300 rp-text-gray-400 rp-cursor-pointer rp-rounded-md rp-transition-all rp-duration-300 rp-w-7 rp-h-7"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 24 24"><path fill="currentColor" d="M18 22H8v-2h10zM8 20H6v-2h2zm12 0h-2v-2h2zM6 18H4v-2h2zm16 0h-2v-4h-2v-2h2v-2h2zM4 16H2V6h2zm14 0h-6v-2h6zm-6-2h-2v-2h2zm-2-2H8V6h2zM6 6H4V4h2zm8-2h-2v2h-2V4H6V2h8z"></path></svg></div></div><div class="rp-social-links"><a href="https://github.com/callstack/agent-device" target="_blank" rel="noopener noreferrer" class="rp-social-links__item"><div class="rp-social-links__icon"><div class="rp-social-links__icon"><svg xmlns="http://www.w3.org/2000/svg" width="100%" viewBox="0 0 24 24"><path fill="currentColor" d="M12 .297c-6.63 0-12 5.373-12 12c0 5.303 3.438 9.8 8.205 11.385c.6.113.82-.258.82-.577c0-.285-.01-1.04-.015-2.04c-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729c1.205.084 1.838 1.236 1.838 1.236c1.07 1.835 2.809 1.305 3.495.998c.108-.776.417-1.305.76-1.605c-2.665-.3-5.466-1.332-5.466-5.93c0-1.31.465-2.38 1.235-3.22c-.135-.303-.54-1.523.105-3.176c0 0 1.005-.322 3.3 1.23c.96-.267 1.98-.399 3-.405c1.02.006 2.04.138 3 .405c2.28-1.552 3.285-1.23 3.285-1.23c.645 1.653.24 2.873.12 3.176c.765.84 1.23 1.91 1.23 3.22c0 4.61-2.805 5.625-5.475 5.92c.42.36.81 1.096.81 2.22c0 1.606-.015 2.896-.015 3.286c0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg></div></div></a><a href="https://discord.gg/eYapw6F3" target="_blank" rel="noopener noreferrer" class="rp-social-links__item"><div class="rp-social-links__icon"><div class="rp-social-links__icon"><svg xmlns="http://www.w3.org/2000/svg" width="100%" viewBox="0 0 24 24"><path fill="currentColor" d="M20.317 4.37a19.8 19.8 0 0 0-4.885-1.515a.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.3 18.3 0 0 0-5.487 0a13 13 0 0 0-.617-1.25a.08.08 0 0 0-.079-.037A19.7 19.7 0 0 0 3.677 4.37a.1.1 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.08.08 0 0 0 .031.057a19.9 19.9 0 0 0 5.993 3.03a.08.08 0 0 0 .084-.028a14 14 0 0 0 1.226-1.994a.076.076 0 0 0-.041-.106a13 13 0 0 1-1.872-.892a.077.077 0 0 1-.008-.128a10 10 0 0 0 .372-.292a.07.07 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.07.07 0 0 1 .078.01q.181.149.373.292a.077.077 0 0 1-.006.127a12.3 12.3 0 0 1-1.873.892a.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.08.08 0 0 0 .084.028a19.8 19.8 0 0 0 6.002-3.03a.08.08 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.06.06 0 0 0-.031-.03M8.02 15.33c-1.182 0-2.157-1.085-2.157-2.419c0-1.333.956-2.419 2.157-2.419c1.21 0 2.176 1.096 2.157 2.42c0 1.333-.956 2.418-2.157 2.418m7.975 0c-1.183 0-2.157-1.085-2.157-2.419c0-1.333.955-2.419 2.157-2.419c1.21 0 2.176 1.096 2.157 2.42c0 1.333-.946 2.418-2.157 2.418"/></svg></div></div></a><ul class="rp-hover-group rp-hover-group--hidden rp-hover-group--right"></ul></div></div><button aria-label="mobile hamburger" class="rp-nav-hamburger rp-nav-hamburger__sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="#838289" fill-rule="evenodd" d="M1 9h6v6H1zm2 2v2h2v-2zm6-2h6v6H9zm2 2v2h2v-2zm6-2h6v6h-6zm2 2v2h2v-2z" clip-rule="evenodd"></path></svg></button><button aria-label="mobile hamburger" class="rp-nav-hamburger rp-nav-hamburger__md"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="#838289" fill-rule="evenodd" d="M1 9h6v6H1zm2 2v2h2v-2zm6-2h6v6H9zm2 2v2h2v-2zm6-2h6v6h-6zm2 2v2h2v-2z" clip-rule="evenodd"></path></svg><ul class="rp-hover-group rp-hover-group--hidden rp-hover-group--right"><div class="rp-nav-menu__others-mobile__container"><div class="rp-nav-hamburger__md__hover-group"><div class="rp-nav-screen-appearance"><div class="rp-nav-screen-appearance__left">Theme</div><div class="rp-nav-screen-appearance__right"></div></div><div class="rp-nav-screen-divider"></div><div class="rp-social-links"><a href="https://github.com/callstack/agent-device" target="_blank" rel="noopener noreferrer" class="rp-social-links__item"><div class="rp-social-links__icon"><div class="rp-social-links__icon"><svg xmlns="http://www.w3.org/2000/svg" width="100%" viewBox="0 0 24 24"><path fill="currentColor" d="M12 .297c-6.63 0-12 5.373-12 12c0 5.303 3.438 9.8 8.205 11.385c.6.113.82-.258.82-.577c0-.285-.01-1.04-.015-2.04c-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729c1.205.084 1.838 1.236 1.838 1.236c1.07 1.835 2.809 1.305 3.495.998c.108-.776.417-1.305.76-1.605c-2.665-.3-5.466-1.332-5.466-5.93c0-1.31.465-2.38 1.235-3.22c-.135-.303-.54-1.523.105-3.176c0 0 1.005-.322 3.3 1.23c.96-.267 1.98-.399 3-.405c1.02.006 2.04.138 3 .405c2.28-1.552 3.285-1.23 3.285-1.23c.645 1.653.24 2.873.12 3.176c.765.84 1.23 1.91 1.23 3.22c0 4.61-2.805 5.625-5.475 5.92c.42.36.81 1.096.81 2.22c0 1.606-.015 2.896-.015 3.286c0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg></div></div></a><a href="https://discord.gg/eYapw6F3" target="_blank" rel="noopener noreferrer" class="rp-social-links__item"><div class="rp-social-links__icon"><div class="rp-social-links__icon"><svg xmlns="http://www.w3.org/2000/svg" width="100%" viewBox="0 0 24 24"><path fill="currentColor" d="M20.317 4.37a19.8 19.8 0 0 0-4.885-1.515a.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.3 18.3 0 0 0-5.487 0a13 13 0 0 0-.617-1.25a.08.08 0 0 0-.079-.037A19.7 19.7 0 0 0 3.677 4.37a.1.1 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.08.08 0 0 0 .031.057a19.9 19.9 0 0 0 5.993 3.03a.08.08 0 0 0 .084-.028a14 14 0 0 0 1.226-1.994a.076.076 0 0 0-.041-.106a13 13 0 0 1-1.872-.892a.077.077 0 0 1-.008-.128a10 10 0 0 0 .372-.292a.07.07 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.07.07 0 0 1 .078.01q.181.149.373.292a.077.077 0 0 1-.006.127a12.3 12.3 0 0 1-1.873.892a.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.08.08 0 0 0 .084.028a19.8 19.8 0 0 0 6.002-3.03a.08.08 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.06.06 0 0 0-.031-.03M8.02 15.33c-1.182 0-2.157-1.085-2.157-2.419c0-1.333.956-2.419 2.157-2.419c1.21 0 2.176 1.096 2.157 2.42c0 1.333-.956 2.418-2.157 2.418m7.975 0c-1.183 0-2.157-1.085-2.157-2.419c0-1.333.955-2.419 2.157-2.419c1.21 0 2.176 1.096 2.157 2.42c0 1.333-.946 2.418-2.157 2.418"/></svg></div></div></a><ul class="rp-hover-group rp-hover-group--hidden rp-hover-group--right"></ul></div></div></div></ul></button></div></header><div class="rp-doc-layout__menu"><div class="rp-sidebar-menu"><button type="button" class="rp-sidebar-menu__left"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="#838289" d="M20 18H4v-2h16zm0-5H4v-2h16zm0-5H4V6h16z"></path></svg><span>Menu</span></button><button type="button" class="rp-sidebar-menu__right"><span class="rp-sidebar-menu__right__text">Contents</span><svg width="14" height="14" class="rp-progress-circle"><circle cx="7" cy="7" r="6" stroke-linecap="round" fill="none" stroke="var(--rp-c-divider-light)" stroke-width="2"></circle><circle cx="7" cy="7" r="6" stroke-linecap="round" fill="none" stroke="var(--rp-c-brand)" stroke-width="2" stroke-dasharray="37.69911184307752" stroke-dashoffset="37.69911184307752" transform="rotate(-90 7 7)" style="transition:stroke-dashoffset 0.3s"></circle></svg><svg xmlns="http://www.w3.org/2000/svg" width="12" height="13" fill="none" viewBox="0 0 12 13" class="rp-sidebar-menu__right__icon" style="transform:rotate(0deg);transition:transform 0.2s ease-out"><path fill="#BCBBC2" d="M6.5 4.5h1v-1h-1v-1h-1v1h-1v1h1v6h1zm-2 0h-1v1h1zm4 0h-1v1h1zm-5 2v-1h-1v1zm6-1h-1v1h1z"></path></svg></button></div></div><div class="rp-doc-layout__container"><aside class="rp-doc-layout__sidebar rp-scrollbar"><div class="rp-sidebar-item rp-sidebar-group" style="padding-left:12px" data-depth="0"><div class="rp-sidebar-item__left"><span class="rp-doc">Getting Started</span></div><div class="rp-sidebar-item__right"></div></div><div style="display:grid;grid-template-rows:1fr;transition:grid-template-rows 0.2s ease-out"><div style="overflow:hidden"><a href="/agent-device/docs/introduction" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Introduction</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/installation" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Installation</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/agent-setup" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">AI Agent Setup</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/quick-start" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Quick Start</span></div><div class="rp-sidebar-item__right"></div></a></div></div><div class="rp-sidebar-item rp-sidebar-group" style="padding-left:12px" data-depth="0"><div class="rp-sidebar-item__left"><span class="rp-doc">Using agent-device</span></div><div class="rp-sidebar-item__right"></div></div><div style="display:grid;grid-template-rows:1fr;transition:grid-template-rows 0.2s ease-out"><div style="overflow:hidden"><a href="/agent-device/docs/commands" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Commands</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/configuration" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Configuration</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/selectors" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Selectors</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/sessions" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Sessions</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/batching" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Batching</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/snapshots" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Snapshots</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/replay-e2e" class="rp-sidebar-item rp-sidebar-item--active rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Replay &amp; E2E testing</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/debugging-profiling" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Debugging &amp; Profiling</span></div><div class="rp-sidebar-item__right"></div></a></div></div><div class="rp-sidebar-item rp-sidebar-group" style="padding-left:12px" data-depth="0"><div class="rp-sidebar-item__left"><span class="rp-doc">Integrations</span></div><div class="rp-sidebar-item__right"></div></div><div style="display:grid;grid-template-rows:1fr;transition:grid-template-rows 0.2s ease-out"><div style="overflow:hidden"><a href="/agent-device/docs/client-api" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Node.js API</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/ai-sdk" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">AI SDK</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/eve" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Eve</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/remote-proxy" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Remote Proxy</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/device-clouds" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Device Clouds</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/browserstack" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">BrowserStack</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/aws-device-farm" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">AWS Device Farm</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/limrun" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Limrun</span></div><div class="rp-sidebar-item__right"></div></a></div></div><div class="rp-sidebar-item rp-sidebar-group" style="padding-left:12px" data-depth="0"><div class="rp-sidebar-item__left"><span class="rp-doc">Reference</span></div><div class="rp-sidebar-item__right"></div></div><div style="display:grid;grid-template-rows:1fr;transition:grid-template-rows 0.2s ease-out"><div style="overflow:hidden"><a href="/agent-device/docs/security-trust" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Security &amp; Trust</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/known-limitations" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Known Limitations</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/migrating-gestures" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Migrating Gestures</span></div><div class="rp-sidebar-item__right"></div></a></div></div></aside><div class="rp-doc-layout__doc"><main class="rp-doc-layout__doc-container"><div class="rp-doc rspress-doc"><!--$--><h1 class="rp-toc-include" id="replay--e2e-testing"><a href="#replay--e2e-testing" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Replay &amp; E2E Testing<!-- --> </h1><div class="rp-not-doc rp-llms-container"><button class="rp-not-doc rp-llms-button rp-llms-copy-button"><div class="rp-llms-copy-button__icon-wrapper"><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-llms-copy-button__icon-success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-llms-copy-button__icon-copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg></div><span>Copy Markdown</span></button><button class="rp-llms-button rp-llms-view-options__trigger "><svg xmlns="http://www.w3.org/2000/svg" width="12" height="13" viewBox="0 0 12 13" class="rp-llms-view-options__arrow "><path fill="currentColor" d="M6.5 8.5h1v1h-1v1h-1v-1h-1v-1h1v-6h1zm-2 0h-1v-1h1zm4 0h-1v-1h1zm-5-2v1h-1v-1zm6 1h-1v-1h1z"></path></svg></button></div>
<p>Agents use refs for exploration and authoring. Replay scripts are deterministic runs that can be used for E2E testing.</p>
<h2 class="rp-toc-include" id="core-model"><a href="#core-model" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Core model</h2>
<p>Two-pass workflow:</p>
<ol>
<li>Agent pass: discover and interact with refs (<code>snapshot</code> -&gt; <code>click @e..</code> / <code>fill @e..</code>).</li>
<li>Deterministic pass: run recorded <code>.ad</code> script with <code>replay</code>.</li>
</ol>
<h2 class="rp-toc-include" id="record-a-replay-script"><a href="#record-a-replay-script" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Record a replay script</h2>
<p>Enable recording during a session:</p>
<div class="rp-codeblock language-bash"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="bash"><code><span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> open</span><span style="color:var(--shiki-token-string)"> Settings</span><span style="color:var(--shiki-token-string)"> --platform</span><span style="color:var(--shiki-token-string)"> ios</span><span style="color:var(--shiki-token-string)"> --session</span><span style="color:var(--shiki-token-string)"> e2e</span><span style="color:var(--shiki-token-string)"> --save-script</span></span>
<span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> snapshot</span><span style="color:var(--shiki-token-string)"> -i</span><span style="color:var(--shiki-token-string)"> --session</span><span style="color:var(--shiki-token-string)"> e2e</span></span>
<span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> click</span><span style="color:var(--shiki-token-string)"> @e13</span><span style="color:var(--shiki-token-string)"> --session</span><span style="color:var(--shiki-token-string)"> e2e</span></span>
<span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> close</span><span style="color:var(--shiki-token-string)"> --session</span><span style="color:var(--shiki-token-string)"> e2e</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
<p>By default, on <code>close</code>, a replay script is written to:</p>
<div class="rp-codeblock language-text"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="text"><code><span class="line"><span>~/.agent-device/sessions/&lt;session&gt;-&lt;timestamp&gt;.ad</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
<p>You can also provide a custom output file path:</p>
<div class="rp-codeblock language-bash"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="bash"><code><span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> open</span><span style="color:var(--shiki-token-string)"> Settings</span><span style="color:var(--shiki-token-string)"> --platform</span><span style="color:var(--shiki-token-string)"> ios</span><span style="color:var(--shiki-token-string)"> --session</span><span style="color:var(--shiki-token-string)"> e2e</span><span style="color:var(--shiki-token-string)"> --save-script</span><span style="color:var(--shiki-token-string)"> ./workflows/e2e-settings.ad</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
<ul>
<li><code>--save-script</code> value is treated as a file path.</li>
<li>Parent directories are created automatically when they do not exist.</li>
<li>For ambiguous bare values, use <code>--save-script=workflow.ad</code> or a path-like value such as <code>./workflow.ad</code>.</li>
</ul>
<h2 class="rp-toc-include" id="run-replay"><a href="#run-replay" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Run replay</h2>
<div class="rp-codeblock language-bash"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="bash"><code><span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> replay</span><span style="color:var(--shiki-token-string)"> ~/.agent-device/sessions/e2e-2026-02-09T12-00-00-000Z.ad</span><span style="color:var(--shiki-token-string)"> --session</span><span style="color:var(--shiki-token-string)"> e2e-run</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
<ul>
<li>
<p>Replay reads <code>.ad</code> scripts.</p>
</li>
<li>
<p>A script without a terminal <code>close</code> already leaves its session active. For an existing script that
does end in <code>close</code>, pass <code>--keep-session</code> to suppress only that final action and continue with
interactive commands in the same session:</p>
<div class="rp-codeblock language-bash"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="bash"><code><span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> replay</span><span style="color:var(--shiki-token-string)"> ./checkout.ad</span><span style="color:var(--shiki-token-string)"> --session</span><span style="color:var(--shiki-token-string)"> e2e-run</span><span style="color:var(--shiki-token-string)"> --keep-session</span></span>
<span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> snapshot</span><span style="color:var(--shiki-token-string)"> -i</span><span style="color:var(--shiki-token-string)"> --session</span><span style="color:var(--shiki-token-string)"> e2e-run</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
<p>Interior <code>close</code> actions still run. The flag is intentionally unavailable to <code>test</code> because suite
attempts own cleanup, and it is rejected for Maestro YAML because that runtime owns its lifecycle.</p>
</li>
</ul>
<h2 class="rp-toc-include" id="run-maestro-compatibility-flows"><a href="#run-maestro-compatibility-flows" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Run Maestro compatibility flows</h2>
<p>Agent Device can run a supported subset of Maestro YAML through its typed Maestro compatibility runtime:</p>
<div class="rp-codeblock language-bash"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="bash"><code><span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> replay</span><span style="color:var(--shiki-token-string)"> ./flow.yaml</span><span style="color:var(--shiki-token-string)"> --maestro</span><span style="color:var(--shiki-token-string)"> --platform</span><span style="color:var(--shiki-token-string)"> ios</span><span style="color:var(--shiki-token-string)"> --session</span><span style="color:var(--shiki-token-string)"> e2e-run</span></span>
<span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> test</span><span style="color:var(--shiki-token-string)"> ./maestro-flows</span><span style="color:var(--shiki-token-string)"> --maestro</span><span style="color:var(--shiki-token-string)"> --platform</span><span style="color:var(--shiki-token-string)"> android</span><span style="color:var(--shiki-token-string)"> --artifacts-dir</span><span style="color:var(--shiki-token-string)"> ./tmp/maestro-artifacts</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
<p>Supported subset:</p>
<ul>
<li>Flows: <code>launchApp</code>; <code>runFlow</code> file/inline with platform, visibility, and limited boolean conditions; <code>onFlowStart</code>/<code>onFlowComplete</code>; <code>repeat.times</code> and retry.</li>
<li>Interactions: <code>tapOn</code>, <code>doubleTapOn</code>, <code>longPressOn</code>, <code>inputText</code> on the focused element, <code>eraseText</code>, <code>openLink</code>, <code>hideKeyboard</code>, basic <code>pressKey</code>, and <code>back</code>; selector targets poll until available and support recursive <code>index</code>, <code>childOf</code>, <code>above</code>, <code>below</code>, <code>leftOf</code>, <code>rightOf</code>, <code>containsChild</code>, <code>containsDescendants</code>, points, and <code>optional</code>; outer command labels are metadata, not target selectors.</li>
<li>Assertions and navigation: <code>assertVisible</code>, <code>assertNotVisible</code>, <code>assertTrue</code> (literal values and <code>${VAR}</code> lookups only; <code>&quot;&quot;</code>, <code>&quot;false&quot;</code>, <code>&quot;0&quot;</code>, <code>&quot;null&quot;</code>, and <code>&quot;undefined&quot;</code> are falsy, everything else is truthy), <code>extendedWaitUntil</code>, <code>scroll</code>, <code>scrollUntilVisible</code>, absolute/percentage/target <code>swipe</code>, <code>takeScreenshot</code>, <code>waitForAnimationToEnd</code>, <code>clearState</code>, and <code>stopApp</code>.</li>
<li>Scripts: ordered <code>runScript</code> file/env scripts with <code>http.post</code>, <code>json</code>, and <code>output</code> variables.</li>
</ul>
<p>Boundaries:</p>
<ul>
<li>Runtime: iOS and Android only; <code>launchApp.clearState</code> and standalone <code>clearState</code> support Android and iOS simulators, launch arguments are Apple-only, and other standalone device utility/state commands are unsupported.</li>
<li>Expressions: <code>when.true</code> supports boolean literals and <code>maestro.platform</code> comparisons; <code>assertTrue</code> supports literal values and <code>${VAR}</code> lookups only; <code>repeat.while</code>, <code>evalScript</code>, and broader JavaScript expressions are unsupported.</li>
<li>Environment: flow <code>env</code> is the default, <code>AD_VAR_*</code> overrides it, and CLI <code>-e KEY=VALUE</code> wins over both.</li>
<li>Failure diagnostics: resolved targets and <code>runFlow</code> paths are rendered, while <code>inputText</code> payloads remain hidden; do not place secrets in diagnostic identifiers.</li>
<li>Trust: <code>runScript</code> executes trusted scripts, may make <code>http.post</code> network requests, and is not a security sandbox; output keys cannot contain a dot.</li>
<li>Errors and tracking: unsupported commands and fields fail with source context when available; open a focused issue only when implementation work is planned.</li>
<li>Session takeover: <code>--keep-session</code> is a native <code>.ad</code> replay option and is rejected for Maestro YAML.</li>
</ul>
<p>See <a href="https://github.com/callstack/agent-device/blob/main/docs/adr/0015-direct-maestro-engine.md" target="_blank" rel="noopener noreferrer" class="rp-link">ADR 0015</a> for architecture, performance tradeoffs, and deliberate deviations. If a missing feature matters for your suite, <a href="https://github.com/callstack/agent-device/issues/new" target="_blank" rel="noopener noreferrer" class="rp-link">open a focused issue</a> with a small flow snippet.</p>
<h2 class="rp-toc-include" id="export-ad-scripts-to-maestro-yaml"><a href="#export-ad-scripts-to-maestro-yaml" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Export <code>.ad</code> scripts to Maestro YAML</h2>
<p>Replay scripts can be exported to a Maestro YAML subset when you need to hand a recorded Agent Device flow to a Maestro runner:</p>
<div class="rp-codeblock language-bash"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="bash"><code><span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> replay</span><span style="color:var(--shiki-token-string)"> export</span><span style="color:var(--shiki-token-string)"> ./workflows/checkout.ad</span><span style="color:var(--shiki-token-string)"> --out</span><span style="color:var(--shiki-token-string)"> ./maestro/checkout.yaml</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
<p><code>replay export</code> is a local file transform. It does not start the daemon or contact a device. If <code>--out</code> is omitted, the YAML is printed to stdout.</p>
<p>Each <code>open &lt;appId&gt;</code> exports with an explicit <code>launchApp.appId</code>, so a flow can switch between apps and return to the original app. The first app remains the flow&#x27;s default <code>appId</code>; relaunch options and app-specific deep links stay attached to their authored targets.</p>
<p>Deep links, including schemes without <code>//</code> such as <code>tel:</code> and <code>mailto:</code>, export as <code>openLink</code>. A standalone <code>open tel:+15551234567</code> emits only the link command; <code>open com.example.app mailto:agent@example.test</code> emits the app launch followed by the link.</p>
<p>The exporter is intentionally strict. It writes Maestro YAML for compatible flow actions such as app launch, taps, long press, text input, keyboard dismiss/enter, back, home, text visibility assertions, coordinate swipes, basic scroll, screenshots, and <code>.ad</code> <code>env</code> directives. <code>home</code> exports as <code>pressKey: Home</code>, so flows that visit the home screen and reopen the app can be exported. Agent-only inspection or maintenance actions such as <code>snapshot</code>, <code>get</code>, <code>record</code>, <code>trace</code>, <code>settings</code>, and unsupported selector shapes fail with the source line and action instead of being silently dropped. Known semantic differences are reported as warnings; for example, <code>.ad</code> <code>fill</code> exports as <code>tapOn</code> plus <code>inputText</code>, which may append text in Maestro rather than replacing existing field contents. Native <code>.ad</code> <code>label=</code> selectors export as Maestro <code>text:</code> selectors and warn because Maestro text matching is broader than label-only matching.</p>
<h2 class="rp-toc-include" id="run-a-lightweight-ad-suite"><a href="#run-a-lightweight-ad-suite" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Run a lightweight <code>.ad</code> suite</h2>
<div class="rp-codeblock language-bash"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="bash"><code><span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> test</span><span style="color:var(--shiki-token-string)"> ./workflows</span></span>
<span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> test</span><span style="color:var(--shiki-token-string-expression)"> &quot;./workflows/**/*.ad&quot;</span><span style="color:var(--shiki-token-string)"> --platform</span><span style="color:var(--shiki-token-string)"> android</span></span>
<span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> test</span><span style="color:var(--shiki-token-string)"> ./workflows</span><span style="color:var(--shiki-token-string)"> --timeout</span><span style="color:var(--shiki-token-constant)"> 60000</span><span style="color:var(--shiki-token-string)"> --retries</span><span style="color:var(--shiki-token-constant)"> 1</span></span>
<span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> test</span><span style="color:var(--shiki-token-string)"> ./workflows</span><span style="color:var(--shiki-token-string)"> --artifacts-dir</span><span style="color:var(--shiki-token-string)"> ./tmp/agent-device-artifacts</span></span>
<span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> test</span><span style="color:var(--shiki-token-string)"> ./workflows</span><span style="color:var(--shiki-token-string)"> --reporter</span><span style="color:var(--shiki-token-string)"> default</span><span style="color:var(--shiki-token-string)"> --reporter</span><span style="color:var(--shiki-token-string)"> junit:./tmp/junit.xml</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
<ul>
<li><code>test</code> discovers <code>.ad</code> files from files, directories, or globs and runs them serially.</li>
<li>Quote relative globs to expand them on the caller from its working directory, including when the directory name contains glob characters such as <code>[</code> or <code>{</code>. A missing file input without glob characters reports an error.</li>
<li><code>context platform=...</code> inside each <code>.ad</code> file is the target source of truth for suite execution.</li>
<li><code>--platform</code> is a filter for suite discovery; files without platform metadata are skipped when a filter is present.</li>
<li><code>context timeout=...</code> and <code>context retries=...</code> can be declared per script; CLI flags override metadata. Retries are capped at <code>3</code>, and duplicate keys in the context header fail fast instead of silently overriding each other.</li>
<li>By default, suite artifacts are written under <code>.agent-device/test-artifacts/&lt;run-id&gt;/...</code>. Each attempt writes <code>replay.ad</code>, <code>result.txt</code>, and <code>replay-timing.ndjson</code>. Failed attempts also keep copied logs and artifact files when the replay produced them.</li>
<li>Copied diagnostic artifacts receive numbered filenames when their names collide with another artifact, a replay source, timing trace, or attempt manifest. <code>result.txt</code> lists the retained names in <code>copiedArtifacts</code>.</li>
<li><code>replay-timing.ndjson</code> records attempt, cleanup, and per-step start/stop events with durations. Upload it from CI even for passing runs when comparing local and CI performance.</li>
<li>Timeouts are cooperative: the runner marks the attempt failed at the timeout boundary, then gives the underlying replay a short grace period to stop before session cleanup.</li>
<li>The default text reporter streams live progress on stderr while a suite runs, then prints the final summary, failed tests, and passed-on-retry flaky tests. Use <code>--verbose</code> to include step traces in completed-test progress output.</li>
<li><code>--reporter</code> is repeatable. Built-ins are <code>default</code> for the console summary and <code>junit:&lt;path&gt;</code> for JUnit XML. Passing any explicit reporter list replaces the implicit default reporter, so include <code>--reporter default</code> when you also want terminal output. <code>--report-junit &lt;path&gt;</code> remains a compatibility alias for <code>--reporter junit:&lt;path&gt;</code>.</li>
<li>JUnit reports preserve legal Unicode and whitespace, and replace characters forbidden by XML 1.0 (such as terminal ESC or NUL) with <code>U+FFFD</code> (<code><EFBFBD></code>) so CI parsers can read the report. JSON and other reporters retain the original suite values.</li>
<li>When <code>--fail-fast</code> and retries are both set, the current test still consumes its retries before the suite stops.</li>
</ul>
<h3 class="rp-toc-include" id="custom-test-reporters"><a href="#custom-test-reporters" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Custom test reporters</h3>
<p>Custom reporters are CLI-only presentation adapters. The daemon streams progress and returns the structured replay suite result; reporters run in the local CLI process and can render both live progress and final output.</p>
<div class="rp-codeblock language-bash"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="bash"><code><span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> test</span><span style="color:var(--shiki-token-string)"> ./workflows</span><span style="color:var(--shiki-token-string)"> --reporter</span><span style="color:var(--shiki-token-string)"> ./scripts/replay-reporter.mjs</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
<p>Reporter modules can export a reporter object, <code>reporter</code>, <code>createReporter</code>, or a default factory. Factories receive load context. Reporter hooks receive replay test domain objects and an IO context with <code>stdout</code> and <code>stderr</code> streams:</p>
<div class="rp-codeblock language-js"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="js"><code><span class="line"><span style="color:var(--shiki-token-comment)">// scripts/replay-reporter.mjs</span></span>
<span class="line"><span style="color:var(--shiki-token-keyword)">import</span><span style="color:var(--shiki-foreground)"> fs </span><span style="color:var(--shiki-token-keyword)">from</span><span style="color:var(--shiki-token-string-expression)"> &#x27;node:fs&#x27;</span><span style="color:var(--shiki-foreground)">;</span></span>
<span class="line"><span style="color:var(--shiki-token-keyword)">import</span><span style="color:var(--shiki-foreground)"> path </span><span style="color:var(--shiki-token-keyword)">from</span><span style="color:var(--shiki-token-string-expression)"> &#x27;node:path&#x27;</span><span style="color:var(--shiki-foreground)">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--shiki-token-keyword)">export</span><span style="color:var(--shiki-token-keyword)"> default</span><span style="color:var(--shiki-token-keyword)"> function</span><span style="color:var(--shiki-token-function)"> createReporter</span><span style="color:var(--shiki-foreground)">(loadContext) {</span></span>
<span class="line"><span style="color:var(--shiki-token-keyword)"> return</span><span style="color:var(--shiki-foreground)"> {</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> name</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-string-expression)"> &#x27;summary-file&#x27;</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--shiki-token-function)"> onTestStep</span><span style="color:var(--shiki-foreground)">(test</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-foreground)"> context) {</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> context</span><span style="color:var(--shiki-token-function)">.</span><span style="color:var(--shiki-token-constant)">stderr</span><span style="color:var(--shiki-token-function)">.write</span><span style="color:var(--shiki-foreground)">(</span><span style="color:var(--shiki-token-string-expression)">`running </span><span style="color:var(--shiki-token-keyword)">${</span><span style="color:var(--shiki-token-constant)">test</span><span style="color:var(--shiki-foreground)">.file</span><span style="color:var(--shiki-token-keyword)">}</span><span style="color:var(--shiki-token-keyword)"> ${</span><span style="color:var(--shiki-token-constant)">test</span><span style="color:var(--shiki-foreground)">.stepIndex</span><span style="color:var(--shiki-token-keyword)">}</span><span style="color:var(--shiki-token-string-expression)">/</span><span style="color:var(--shiki-token-keyword)">${</span><span style="color:var(--shiki-token-constant)">test</span><span style="color:var(--shiki-foreground)">.stepTotal</span><span style="color:var(--shiki-token-keyword)">}</span><span style="color:var(--shiki-token-string-expression)">\n`</span><span style="color:var(--shiki-foreground)">);</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> }</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--shiki-token-function)"> onSuiteEnd</span><span style="color:var(--shiki-foreground)">(suite</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-foreground)"> context) {</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> context</span><span style="color:var(--shiki-token-function)">.</span><span style="color:var(--shiki-token-constant)">stdout</span><span style="color:var(--shiki-token-function)">.write</span><span style="color:var(--shiki-foreground)">(</span><span style="color:var(--shiki-token-string-expression)">`finished </span><span style="color:var(--shiki-token-keyword)">${</span><span style="color:var(--shiki-token-constant)">suite</span><span style="color:var(--shiki-foreground)">.total</span><span style="color:var(--shiki-token-keyword)">}</span><span style="color:var(--shiki-token-string-expression)"> tests\n`</span><span style="color:var(--shiki-foreground)">);</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> fs</span><span style="color:var(--shiki-token-function)">.mkdirSync</span><span style="color:var(--shiki-foreground)">(</span><span style="color:var(--shiki-token-string-expression)">&#x27;./tmp&#x27;</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-foreground)"> { recursive</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> true</span><span style="color:var(--shiki-foreground)"> });</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> fs</span><span style="color:var(--shiki-token-function)">.writeFileSync</span><span style="color:var(--shiki-foreground)">(</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> path</span><span style="color:var(--shiki-token-function)">.join</span><span style="color:var(--shiki-foreground)">(</span><span style="color:var(--shiki-token-string-expression)">&#x27;./tmp&#x27;</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-token-string-expression)"> &#x27;report.txt&#x27;</span><span style="color:var(--shiki-foreground)">)</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> JSON</span><span style="color:var(--shiki-token-function)">.stringify</span><span style="color:var(--shiki-foreground)">(</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> {</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> total</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> suite</span><span style="color:var(--shiki-foreground)">.total</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> passed</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> suite</span><span style="color:var(--shiki-foreground)">.passed</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> failed</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> suite</span><span style="color:var(--shiki-foreground)">.failed</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> modulePath</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> loadContext</span><span style="color:var(--shiki-foreground)">.modulePath</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> }</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> null</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> 2</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> )</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--shiki-token-string-expression)"> &#x27;utf8&#x27;</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> );</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> }</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--shiki-token-function)"> getExitCode</span><span style="color:var(--shiki-foreground)">(suite) {</span></span>
<span class="line"><span style="color:var(--shiki-token-keyword)"> return</span><span style="color:var(--shiki-token-constant)"> suite</span><span style="color:var(--shiki-foreground)">.failed </span><span style="color:var(--shiki-token-keyword)">&gt;</span><span style="color:var(--shiki-token-constant)"> 0</span><span style="color:var(--shiki-token-keyword)"> ?</span><span style="color:var(--shiki-token-constant)"> 1</span><span style="color:var(--shiki-token-keyword)"> :</span><span style="color:var(--shiki-token-constant)"> 0</span><span style="color:var(--shiki-foreground)">;</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> }</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> };</span></span>
<span class="line"><span style="color:var(--shiki-foreground)">}</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
<p>For a live terminal reporter that prints each completed test as an emoji, title, and duration:</p>
<div class="rp-codeblock language-js"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="js"><code><span class="line"><span style="color:var(--shiki-token-comment)">// scripts/emoji-reporter.mjs</span></span>
<span class="line"><span style="color:var(--shiki-token-keyword)">export</span><span style="color:var(--shiki-token-keyword)"> default</span><span style="color:var(--shiki-foreground)"> {</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> name</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-string-expression)"> &#x27;emoji-status&#x27;</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--shiki-token-function)"> onTestResult</span><span style="color:var(--shiki-foreground)">(test</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-foreground)"> context) {</span></span>
<span class="line"><span style="color:var(--shiki-token-keyword)"> const</span><span style="color:var(--shiki-token-constant)"> icon</span><span style="color:var(--shiki-token-keyword)"> =</span><span style="color:var(--shiki-token-constant)"> test</span><span style="color:var(--shiki-foreground)">.status </span><span style="color:var(--shiki-token-keyword)">===</span><span style="color:var(--shiki-token-string-expression)"> &#x27;pass&#x27;</span><span style="color:var(--shiki-token-keyword)"> ?</span><span style="color:var(--shiki-token-string-expression)"> &#x27;✓&#x27;</span><span style="color:var(--shiki-token-keyword)"> :</span><span style="color:var(--shiki-token-constant)"> test</span><span style="color:var(--shiki-foreground)">.status </span><span style="color:var(--shiki-token-keyword)">===</span><span style="color:var(--shiki-token-string-expression)"> &#x27;fail&#x27;</span><span style="color:var(--shiki-token-keyword)"> ?</span><span style="color:var(--shiki-token-string-expression)"> &#x27;⨯&#x27;</span><span style="color:var(--shiki-token-keyword)"> :</span><span style="color:var(--shiki-token-string-expression)"> &#x27;-&#x27;</span><span style="color:var(--shiki-foreground)">;</span></span>
<span class="line"><span style="color:var(--shiki-token-keyword)"> const</span><span style="color:var(--shiki-token-constant)"> title</span><span style="color:var(--shiki-token-keyword)"> =</span><span style="color:var(--shiki-token-constant)"> test</span><span style="color:var(--shiki-token-function)">.</span><span style="color:var(--shiki-token-constant)">title</span><span style="color:var(--shiki-token-function)">?.trim</span><span style="color:var(--shiki-foreground)">() </span><span style="color:var(--shiki-token-keyword)">||</span><span style="color:var(--shiki-token-constant)"> test</span><span style="color:var(--shiki-foreground)">.file;</span></span>
<span class="line"><span style="color:var(--shiki-token-keyword)"> const</span><span style="color:var(--shiki-token-constant)"> duration</span><span style="color:var(--shiki-token-keyword)"> =</span></span>
<span class="line"><span style="color:var(--shiki-token-keyword)"> typeof</span><span style="color:var(--shiki-token-constant)"> test</span><span style="color:var(--shiki-foreground)">.durationMs </span><span style="color:var(--shiki-token-keyword)">===</span><span style="color:var(--shiki-token-string-expression)"> &#x27;number&#x27;</span><span style="color:var(--shiki-token-keyword)"> ?</span><span style="color:var(--shiki-token-string-expression)"> ` </span><span style="color:var(--shiki-token-keyword)">${</span><span style="color:var(--shiki-foreground)">(</span><span style="color:var(--shiki-token-constant)">test</span><span style="color:var(--shiki-foreground)">.durationMs </span><span style="color:var(--shiki-token-keyword)">/</span><span style="color:var(--shiki-token-constant)"> 1000</span><span style="color:var(--shiki-foreground)">)</span><span style="color:var(--shiki-token-function)">.toFixed</span><span style="color:var(--shiki-foreground)">(</span><span style="color:var(--shiki-token-constant)">2</span><span style="color:var(--shiki-foreground)">)</span><span style="color:var(--shiki-token-keyword)">}</span><span style="color:var(--shiki-token-string-expression)">s`</span><span style="color:var(--shiki-token-keyword)"> :</span><span style="color:var(--shiki-token-string-expression)"> &#x27;&#x27;</span><span style="color:var(--shiki-foreground)">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> context</span><span style="color:var(--shiki-token-function)">.</span><span style="color:var(--shiki-token-constant)">stderr</span><span style="color:var(--shiki-token-function)">.write</span><span style="color:var(--shiki-foreground)">(</span><span style="color:var(--shiki-token-string-expression)">`</span><span style="color:var(--shiki-token-keyword)">${</span><span style="color:var(--shiki-foreground)">icon</span><span style="color:var(--shiki-token-keyword)">}</span><span style="color:var(--shiki-token-keyword)"> ${</span><span style="color:var(--shiki-foreground)">title</span><span style="color:var(--shiki-token-keyword)">}${</span><span style="color:var(--shiki-foreground)">duration</span><span style="color:var(--shiki-token-keyword)">}</span><span style="color:var(--shiki-token-string-expression)">\n`</span><span style="color:var(--shiki-foreground)">);</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> }</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--shiki-foreground)">};</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
<p>TypeScript reporters use the same object shape; compile them to JavaScript before passing them to <code>--reporter</code>:</p>
<div class="rp-codeblock language-ts"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="ts"><code><span class="line"><span style="color:var(--shiki-token-keyword)">const</span><span style="color:var(--shiki-token-function)"> createReporter</span><span style="color:var(--shiki-token-keyword)"> =</span><span style="color:var(--shiki-foreground)"> () </span><span style="color:var(--shiki-token-keyword)">=&gt;</span><span style="color:var(--shiki-foreground)"> ({</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> name</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-string-expression)"> &#x27;typed-reporter&#x27;</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--shiki-token-function)"> onSuiteStart</span><span style="color:var(--shiki-foreground)">(suite</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-foreground)"> context) {</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> context</span><span style="color:var(--shiki-token-function)">.</span><span style="color:var(--shiki-token-constant)">stderr</span><span style="color:var(--shiki-token-function)">.write</span><span style="color:var(--shiki-foreground)">(</span><span style="color:var(--shiki-token-string-expression)">`starting </span><span style="color:var(--shiki-token-keyword)">${</span><span style="color:var(--shiki-token-constant)">suite</span><span style="color:var(--shiki-foreground)">.runnable</span><span style="color:var(--shiki-token-keyword)">}</span><span style="color:var(--shiki-token-string-expression)"> tests\n`</span><span style="color:var(--shiki-foreground)">);</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> }</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--shiki-token-function)"> onTestResult</span><span style="color:var(--shiki-foreground)">(test</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-foreground)"> context) {</span></span>
<span class="line"><span style="color:var(--shiki-token-constant)"> context</span><span style="color:var(--shiki-token-function)">.</span><span style="color:var(--shiki-token-constant)">stderr</span><span style="color:var(--shiki-token-function)">.write</span><span style="color:var(--shiki-foreground)">(</span><span style="color:var(--shiki-token-string-expression)">`</span><span style="color:var(--shiki-token-keyword)">${</span><span style="color:var(--shiki-token-constant)">test</span><span style="color:var(--shiki-foreground)">.status</span><span style="color:var(--shiki-token-keyword)">}</span><span style="color:var(--shiki-token-keyword)"> ${</span><span style="color:var(--shiki-token-constant)">test</span><span style="color:var(--shiki-foreground)">.title </span><span style="color:var(--shiki-token-keyword)">??</span><span style="color:var(--shiki-token-constant)"> test</span><span style="color:var(--shiki-foreground)">.file</span><span style="color:var(--shiki-token-keyword)">}</span><span style="color:var(--shiki-token-string-expression)">\n`</span><span style="color:var(--shiki-foreground)">);</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> }</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--shiki-token-function)"> onSuiteEnd</span><span style="color:var(--shiki-foreground)">(suite) {</span></span>
<span class="line"><span style="color:var(--shiki-token-comment)"> // Write artifacts, annotations, or summaries from suite.</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> }</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--shiki-foreground)">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--shiki-token-keyword)">export</span><span style="color:var(--shiki-token-keyword)"> default</span><span style="color:var(--shiki-foreground)"> createReporter;</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
<p>The CLI loads reporter modules with Node dynamic <code>import()</code>. Use <code>.mjs</code> or <code>.js</code> files at runtime; for TypeScript, compile the reporter to JavaScript before passing it to <code>--reporter</code>. Loading <code>.ts</code> files directly depends on Node&#x27;s type-stripping behavior and is not part of the supported reporter contract.</p>
<p>Live reporter hooks are semantic: <code>onSuiteStart</code>, <code>onTestStart</code>, <code>onTestStep</code>, and <code>onTestResult</code> run while the daemon request is active; generic command progress frames are not exposed to test reporters. These live hooks are synchronous — they run from the progress stream as events arrive and are not awaited, so keep their work synchronous and defer anything async to <code>onSuiteEnd</code>, which the CLI awaits before exiting. <code>onSuiteEnd</code> receives the final suite result. <code>getExitCode</code> can only raise the suite exit code, never lower it: the highest reporter-provided code wins and failed tests still exit with <code>1</code> when no reporter raises it further, so a reporter cannot mask a failing suite. Return an integer from <code>0</code> to <code>255</code>, or <code>undefined</code> to leave the exit code unchanged. Other values fail with <code>INVALID_ARGS</code>; in particular, codes such as <code>256</code> are rejected before they can wrap to a successful process exit.</p>
<h2 class="rp-toc-include" id="parametrise-ad-scripts"><a href="#parametrise-ad-scripts" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Parametrise <code>.ad</code> scripts</h2>
<p>Substitute <code>${VAR}</code> tokens in <code>.ad</code> scripts using values from the CLI, shell env, script-local <code>env</code> directives, or built-ins.</p>
<div class="rp-codeblock language-sh"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="sh"><code><span class="line"><span style="color:var(--shiki-token-function)">context</span><span style="color:var(--shiki-token-string)"> platform=android</span></span>
<span class="line"><span style="color:var(--shiki-token-function)">env</span><span style="color:var(--shiki-token-string)"> APP_ID=settings</span></span>
<span class="line"><span style="color:var(--shiki-token-function)">env</span><span style="color:var(--shiki-token-string)"> WAIT_SHORT=</span><span style="color:var(--shiki-token-constant)">500</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--shiki-token-function)">open</span><span style="color:var(--shiki-foreground)"> ${APP_ID} </span><span style="color:var(--shiki-token-string)">--relaunch</span></span>
<span class="line"><span style="color:var(--shiki-token-function)">wait</span><span style="color:var(--shiki-foreground)"> ${WAIT_SHORT}</span></span>
<span class="line"><span style="color:var(--shiki-token-function)">click</span><span style="color:var(--shiki-token-string-expression)"> &quot;label=${APP_ID}&quot;</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
<h3 class="rp-toc-include" id="precedence"><a href="#precedence" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Precedence</h3>
<div class="rp-table-scroll-container rp-scrollbar"><table><thead><tr><th>Source</th><th>Priority</th><th>Example</th></tr></thead><tbody><tr><td>CLI <code>-e KEY=VALUE</code></td><td>highest</td><td><code>agent-device test flow.ad -e APP_ID=demo</code></td></tr><tr><td>Shell env prefixed <code>AD_VAR_</code></td><td></td><td><code>AD_VAR_APP_ID=demo agent-device test flow.ad</code> (imported as <code>APP_ID</code>)</td></tr><tr><td>Script <code>env KEY=VALUE</code></td><td></td><td><code>env APP_ID=settings</code> in header</td></tr><tr><td>Built-ins</td><td>runtime</td><td><code>AD_PLATFORM</code>, <code>AD_SESSION</code>, <code>AD_FILENAME</code>, <code>AD_DEVICE</code>, <code>AD_ARTIFACTS</code></td></tr></tbody></table></div>
<h3 class="rp-toc-include" id="built-ins"><a href="#built-ins" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Built-ins</h3>
<p>Built-ins are provided by replay/test runtime and use the reserved <code>AD_*</code> namespace.</p>
<ul>
<li><code>AD_PLATFORM</code> - matches <code>context platform=...</code> or the selected platform when available</li>
<li><code>AD_SESSION</code> - active session name</li>
<li><code>AD_FILENAME</code> - path of the running <code>.ad</code> file</li>
<li><code>AD_DEVICE</code> - device identifier (when <code>--device</code> is set)</li>
<li><code>AD_ARTIFACTS</code> - attempt artifacts directory (when running under <code>test</code>)</li>
</ul>
<p>User-defined keys starting with <code>AD_</code> are rejected in <code>env</code>, <code>-e</code>, and shell imports such as <code>AD_VAR_AD_FOO</code>, so built-ins cannot be overridden.</p>
<p>Substitution happens inside parsed string values. It does not create extra arguments, so quote selectors or text values that contain spaces:</p>
<div class="rp-codeblock language-sh"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="sh"><code><span class="line"><span style="color:var(--shiki-token-function)">env</span><span style="color:var(--shiki-token-string)"> SETTINGS=</span><span style="color:var(--shiki-token-string-expression)">&quot;label=Account || label=Profile&quot;</span></span>
<span class="line"><span style="color:var(--shiki-token-function)">click</span><span style="color:var(--shiki-token-string-expression)"> &quot;${SETTINGS}&quot;</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
<h3 class="rp-toc-include" id="fallback-and-escape"><a href="#fallback-and-escape" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Fallback and escape</h3>
<div class="rp-codeblock language-sh"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="sh"><code><span class="line"><span style="color:var(--shiki-token-function)">wait</span><span style="color:var(--shiki-foreground)"> ${WAIT_MS</span><span style="color:var(--shiki-token-keyword)">:-</span><span style="color:var(--shiki-foreground)">500}</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
<p><code>${VAR:-default}</code> yields <code>default</code> when <code>VAR</code> is unset.</p>
<div class="rp-codeblock language-sh"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="sh"><code><span class="line"><span style="color:var(--shiki-token-function)">echo</span><span style="color:var(--shiki-token-string-expression)"> &quot;Price: \${APP}&quot;</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
<p><code>\${APP}</code> emits a literal <code>${APP}</code> with no substitution.</p>
<h3 class="rp-toc-include" id="recipes"><a href="#recipes" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Recipes</h3>
<p>Run one flow against two app variants in CI:</p>
<div class="rp-codeblock language-sh"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="sh"><code><span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> test</span><span style="color:var(--shiki-token-string)"> ./flows/login.ad</span><span style="color:var(--shiki-token-string)"> -e</span><span style="color:var(--shiki-token-string)"> APP_ID=com.example.debug</span></span>
<span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> test</span><span style="color:var(--shiki-token-string)"> ./flows/login.ad</span><span style="color:var(--shiki-token-string)"> -e</span><span style="color:var(--shiki-token-string)"> APP_ID=com.example.release</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
<p>Tune timings locally without editing the script:</p>
<div class="rp-codeblock language-sh"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="sh"><code><span class="line"><span style="color:var(--shiki-foreground)">AD_VAR_WAIT_SHORT</span><span style="color:var(--shiki-token-keyword)">=</span><span style="color:var(--shiki-token-string)">2000</span><span style="color:var(--shiki-token-function)"> agent-device</span><span style="color:var(--shiki-token-string)"> replay</span><span style="color:var(--shiki-token-string)"> ./flow.ad</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
<p>Extract a reusable selector. Before:</p>
<div class="rp-codeblock language-sh"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="sh"><code><span class="line"><span style="color:var(--shiki-token-function)">click</span><span style="color:var(--shiki-token-string-expression)"> &quot;label=Account || label=Profile || label=User&quot;</span></span>
<span class="line"><span style="color:var(--shiki-token-function)">wait</span><span style="color:var(--shiki-token-constant)"> 500</span></span>
<span class="line"><span style="color:var(--shiki-token-function)">click</span><span style="color:var(--shiki-token-string-expression)"> &quot;label=Account || label=Profile || label=User&quot;</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
<p>After:</p>
<div class="rp-codeblock language-sh"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="sh"><code><span class="line"><span style="color:var(--shiki-token-function)">env</span><span style="color:var(--shiki-token-string)"> SETTINGS=</span><span style="color:var(--shiki-token-string-expression)">&quot;label=Account || label=Profile || label=User&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--shiki-token-function)">click</span><span style="color:var(--shiki-token-string-expression)"> &quot;${SETTINGS}&quot;</span></span>
<span class="line"><span style="color:var(--shiki-token-function)">wait</span><span style="color:var(--shiki-token-constant)"> 500</span></span>
<span class="line"><span style="color:var(--shiki-token-function)">click</span><span style="color:var(--shiki-token-string-expression)"> &quot;${SETTINGS}&quot;</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
<p>Quote <code>${VAR}</code> inside selector expressions so the whole expression is treated as a single argument.</p>
<h3 class="rp-toc-include" id="notes"><a href="#notes" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Notes</h3>
<ul>
<li>Shell env (<code>AD_VAR_*</code>) is collected on the CLI/client side at request time, so the same values are seen whether the daemon runs locally or remotely.</li>
<li>No nested fallback. <code>${A:-${B}}</code> is not supported.</li>
<li>Unresolved <code>${VAR}</code> fails with a <code>file:line</code> reference. Typos are loud.</li>
</ul>
<h2 class="rp-toc-include" id="replay-divergence-and-resume"><a href="#replay-divergence-and-resume" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Replay divergence and resume</h2>
<p>A failing <code>replay</code>/<code>test</code> step returns a structured <code>REPLAY_DIVERGENCE</code> error instead of a bare failure. The report carries, bounded and redacted:</p>
<ul>
<li><strong><code>step</code></strong> — the 1-based plan index and its source file/line (through Maestro <code>runFlow</code> includes).</li>
<li><strong><code>screen</code></strong> — a fresh post-failure snapshot digest with actionable refs, or <code>unavailable</code> with a reason/hint when capture failed or was sparse (never a stale tree).</li>
<li><strong><code>suggestions</code></strong> — up to 5 ranked, re-resolved candidates for the failing selector (id match ranks above role+label, which ranks above label-only), each with a <code>basis</code> you can inspect before acting.</li>
<li><strong><code>resume</code></strong> — whether and how to continue without re-running the script from the top.</li>
</ul>
<div class="rp-codeblock language-jsonc"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="jsonc"><code><span class="line"><span style="color:var(--shiki-foreground)">{</span></span>
<span class="line"><span style="color:var(--shiki-token-keyword)"> &quot;code&quot;</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-token-string-expression)"> &quot;REPLAY_DIVERGENCE&quot;</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--shiki-token-keyword)"> &quot;details&quot;</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-foreground)"> {</span></span>
<span class="line"><span style="color:var(--shiki-token-keyword)"> &quot;divergence&quot;</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-foreground)"> {</span></span>
<span class="line"><span style="color:var(--shiki-token-keyword)"> &quot;step&quot;</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-foreground)"> { </span><span style="color:var(--shiki-token-keyword)">&quot;index&quot;</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-token-constant)"> 4</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-token-keyword)"> &quot;source&quot;</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-foreground)"> { </span><span style="color:var(--shiki-token-keyword)">&quot;path&quot;</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-token-string-expression)"> &quot;flow.ad&quot;</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-token-keyword)"> &quot;line&quot;</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-token-constant)"> 6</span><span style="color:var(--shiki-foreground)"> } }</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--shiki-token-keyword)"> &quot;screen&quot;</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-foreground)"> {</span></span>
<span class="line"><span style="color:var(--shiki-token-keyword)"> &quot;state&quot;</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-token-string-expression)"> &quot;available&quot;</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--shiki-token-keyword)"> &quot;refsGeneration&quot;</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-token-constant)"> 3</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--shiki-token-keyword)"> &quot;refs&quot;</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-foreground)"> [</span></span>
<span class="line"><span style="color:var(--shiki-token-comment)"> /* ... */</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> ]</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> }</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--shiki-token-keyword)"> &quot;suggestions&quot;</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-foreground)"> [{ </span><span style="color:var(--shiki-token-keyword)">&quot;selector&quot;</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-token-string-expression)"> &quot;id=\&quot;auth_continue\&quot;&quot;</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-token-keyword)"> &quot;basis&quot;</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-token-string-expression)"> &quot;id&quot;</span><span style="color:var(--shiki-foreground)"> }]</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--shiki-token-keyword)"> &quot;resume&quot;</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-foreground)"> { </span><span style="color:var(--shiki-token-keyword)">&quot;allowed&quot;</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-token-constant)"> true</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-token-keyword)"> &quot;from&quot;</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-token-constant)"> 4</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-token-keyword)"> &quot;planDigest&quot;</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-token-string-expression)"> &quot;…64 hex chars…&quot;</span><span style="color:var(--shiki-foreground)"> }</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> }</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--shiki-foreground)"> }</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--shiki-foreground)">}</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
<p>Text output prints a compact summary of the same fields; <code>--json</code>/MCP carry the full object.</p>
<h3 class="rp-toc-include" id="resuming-a-failed-replay"><a href="#resuming-a-failed-replay" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Resuming a failed replay</h3>
<p><code>replay --from &lt;n&gt; --plan-digest &lt;sha256&gt;</code> resumes <strong>at</strong> plan step <code>n</code>, not after it, skipping <code>1..n-1</code> without executing them. Both flags come from a divergence report&#x27;s <code>resume</code> field — <code>from</code> is the failed step, <code>planDigest</code> is the digest of the exact unchanged plan that produced it.</p>
<p>Choose one recovery workflow:</p>
<ol>
<li><strong>Change the replay script.</strong> Review the suggestion, edit the selector or include, then run a fresh full <code>replay ./flow.ad</code>. The old digest is intentionally invalid after any plan edit; do not combine it with the edited script. A later divergence supplies a new digest.</li>
<li><strong>Keep the replay plan unchanged.</strong> Repair app/device state so the reported failed step can succeed when retried, then resume with the report&#x27;s unchanged <code>from</code> and <code>planDigest</code>. If you manually complete the failed action itself, the reported <code>from</code> will execute it again; only do that when repeating the action is safe.</li>
</ol>
<p>The unchanged-plan resume loop is:</p>
<ol>
<li>Run <code>replay ./flow.ad</code>. On failure, read <code>resume</code> from the divergence.</li>
<li>Leave the script, includes, platform, and target unchanged. Repair app state yourself so the failed step can be retried safely. The daemon never infers or reconstructs app state — it only skips execution of the earlier steps.</li>
<li><code>replay ./flow.ad --from &lt;resume.from&gt; --plan-digest &lt;resume.planDigest&gt;</code>.</li>
</ol>
<div class="rp-codeblock language-bash"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="bash"><code><span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> replay</span><span style="color:var(--shiki-token-string)"> ./flow.ad</span></span>
<span class="line"><span style="color:var(--shiki-token-comment)"># ... REPLAY_DIVERGENCE, resume: { allowed: true, from: 4, planDigest: &quot;ab12...“ }</span></span>
<span class="line"><span style="color:var(--shiki-token-comment)"># (repair app state on the device)</span></span>
<span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> replay</span><span style="color:var(--shiki-token-string)"> ./flow.ad</span><span style="color:var(--shiki-token-string)"> --from</span><span style="color:var(--shiki-token-constant)"> 4</span><span style="color:var(--shiki-token-string)"> --plan-digest</span><span style="color:var(--shiki-token-string)"> ab12...</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
<p>For Maestro flows, <code>--from</code> addresses the immutable top-level typed plan. Compact runtime control nodes
remain single plan steps and nested commands are not independently addressable. As with generic <code>.ad</code>
replay, the caller is responsible for restoring any state and environment values established by skipped
steps before resuming.</p>
<p>Passing <code>--plan-digest</code> that no longer matches the current script — because you edited it, an include changed, or platform-conditioned expansion differs — fails <code>INVALID_ARGS</code> before any action; run a fresh full replay to get a new digest. <code>--from</code> is <code>replay</code>-only; <code>test</code> rejects it (a suite run must stay full and deterministic).</p>
<h2 class="rp-toc-include" id="--update-u-retired"><a href="#--update-u-retired" class="rp-header-anchor rp-link" aria-hidden="true">#</a><code>--update</code>/<code>-u</code> (retired)</h2>
<p><code>--update</code>/<code>-u</code> no longer rewrites <code>.ad</code> files. Historically it retried a failing step against the recorded selector&#x27;s candidate material and rewrote the line in place; the audit behind <a href="https://github.com/callstack/agent-device/blob/main/docs/adr/0012-interactive-replay.md" target="_blank" rel="noopener noreferrer" class="rp-link">ADR 0012</a> found that mechanism rarely able to act (it can only recover drift the original selector still matches, never a rename) and a silent rewrite is a target-binding risk on its own. The flag is kept, accepted, and is a complete no-op: every replay divergence already carries the same ranked <code>suggestions</code> the old heal path used to apply blind, whether or not <code>--update</code> is passed. Review a suggestion, then edit the <code>.ad</code> file yourself if it&#x27;s right.</p>
<h2 class="rp-toc-include" id="troubleshooting"><a href="#troubleshooting" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Troubleshooting</h2>
<ul>
<li>Replay fails after UI/layout changes:
<ul>
<li>Read the divergence report&#x27;s <code>suggestions</code> and repair the selector by hand; there is no automated rewrite. Because the edit changes the plan digest, run a fresh full replay instead of using the old resume flags.</li>
</ul>
</li>
<li>Repeated re-runs are slow or the app is stateful, but the script is still correct:
<ul>
<li>Leave the replay plan unchanged, repair app state so the reported failed step can be retried, then use its <code>--from</code>/<code>--plan-digest</code>. Resume starts at <code>--from</code>; it does not skip that step.</li>
</ul>
</li>
<li>Replay file parse error:
<ul>
<li>Validate quoting in <code>.ad</code> lines (unclosed quotes are rejected).</li>
</ul>
</li>
<li>Maestro compatibility flow fails on unsupported syntax:
<ul>
<li>Check <a href="https://github.com/callstack/agent-device/blob/main/docs/adr/0015-direct-maestro-engine.md" target="_blank" rel="noopener noreferrer" class="rp-link">ADR 0015</a>. If the missing feature matters to your suite, open a focused issue with a small flow snippet.</li>
</ul>
</li>
</ul><!--/$--></div><div class="container-uhcWmO"><img class="logo-IwzwG8" src="/agent-device/static/image/abstract-atom.4eca0300ff.avif" alt="Abstract Logo" aria-hidden="true"/><div class="content-uJxcIj"><div class="headlineContainer-NHNT4y"><h1 class="headline-iCUUUl">Need React or React Native
expertise you can count on?</h1></div><div class="buttonContainer-wOj8Y6"><div class="dark"><a href="https://www.callstack.com/contact?utm_campaign=open_source&amp;utm_source=agent-device&amp;utm_medium=referral&amp;utm_content=FOOTER_CTA" class="button-DogSnd brand-rT2ahT ">Let&#x27;s talk<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none" viewBox="0 0 16 16" class="button-icon-Okk0hT" aria-hidden="true" focusable="false"><path fill="currentColor" d="M9.333 13.333h1.334V12H9.333zm-6.666-2.666h8V12H12v-1.333h1.333V9.333H12V8h-1.333v1.333H4V2.667H2.667zM9.333 8h1.334V6.667H9.333z"></path></svg></a></div></div></div></div><footer class="rp-doc-footer"><div class="rp-doc-footer__edit"><a href="https://github.com/callstack/agent-device/edit/main/website/docs/replay-e2e.md" target="_blank" rel="noopener noreferrer" class="rp-edit-link rp-link">Edit this page on GitHub</a></div><div class="rp-doc-footer__divider"></div><div class="container-aZLxg0"><a href="/agent-device/docs/snapshots" class="pagerLink-_WIlZJ rp-link"><span class="iconBox-AUO0Le"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 24 24" class="icon-WRLCnG"><path d="M4 20h2V4H4zm8-5v2h2v-2zm-2-4H8v2h2v2h2v-2h8v-2h-8V9h-2zm2-2h2V7h-2z"></path></svg></span><span class="textWrap-lmdyLc"><span class="desc-sp3vRh">Previous page</span><span class="title-hrit_i">Snapshots</span></span></a><a href="/agent-device/docs/debugging-profiling" class="pagerLink-_WIlZJ next-axkYXz rp-link"><span class="textWrap-lmdyLc"><span class="desc-sp3vRh">Next page</span><span class="title-hrit_i">Debugging &amp; Profiling</span></span><span class="iconBox-AUO0Le"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 24 24" class="icon-WRLCnG"><path d="M20 20h-2V4h2zm-8-5v2h-2v-2zm2-4h2v2h-2v2h-2v-2H4v-2h8V9h2zm-2-2h-2V7h2z"></path></svg></span></a></div></footer></main></div><aside class="rp-doc-layout__outline rp-scrollbar"><div class="rp-outline"><div class="rp-outline__title">Contents<svg width="14" height="14" class="rp-progress-circle"><circle cx="7" cy="7" r="6" stroke-linecap="round" fill="none" stroke="var(--rp-c-divider-light)" stroke-width="2"></circle><circle cx="7" cy="7" r="6" stroke-linecap="round" fill="none" stroke="var(--rp-c-brand)" stroke-width="2" stroke-dasharray="37.69911184307752" stroke-dashoffset="37.69911184307752" transform="rotate(-90 7 7)" style="transition:stroke-dashoffset 0.3s"></circle></svg></div><nav class="rp-outline__toc rp-scrollbar"><a href="#core-model" class="rp-toc-item rp-link" title="Core model" style="padding-left:0" data-depth="0"><span class="rp-toc-item__text rp-doc">Core model</span></a><a href="#record-a-replay-script" class="rp-toc-item rp-link" title="Record a replay script" style="padding-left:0" data-depth="0"><span class="rp-toc-item__text rp-doc">Record a replay script</span></a><a href="#run-replay" class="rp-toc-item rp-link" title="Run replay" style="padding-left:0" data-depth="0"><span class="rp-toc-item__text rp-doc">Run replay</span></a><a href="#run-maestro-compatibility-flows" class="rp-toc-item rp-link" title="Run Maestro compatibility flows" style="padding-left:0" data-depth="0"><span class="rp-toc-item__text rp-doc">Run Maestro compatibility flows</span></a><a href="#export-ad-scripts-to-maestro-yaml" class="rp-toc-item rp-link" title="Export .ad scripts to Maestro YAML" style="padding-left:0" data-depth="0"><span class="rp-toc-item__text rp-doc">Export <code>.ad</code> scripts to Maestro YAML</span></a><a href="#run-a-lightweight-ad-suite" class="rp-toc-item rp-link" title="Run a lightweight .ad suite" style="padding-left:0" data-depth="0"><span class="rp-toc-item__text rp-doc">Run a lightweight <code>.ad</code> suite</span></a><a href="#custom-test-reporters" class="rp-toc-item rp-link" title="Custom test reporters" style="padding-left:12px" data-depth="1"><span class="rp-toc-item__text rp-doc">Custom test reporters</span></a><a href="#parametrise-ad-scripts" class="rp-toc-item rp-link" title="Parametrise .ad scripts" style="padding-left:0" data-depth="0"><span class="rp-toc-item__text rp-doc">Parametrise <code>.ad</code> scripts</span></a><a href="#precedence" class="rp-toc-item rp-link" title="Precedence" style="padding-left:12px" data-depth="1"><span class="rp-toc-item__text rp-doc">Precedence</span></a><a href="#built-ins" class="rp-toc-item rp-link" title="Built-ins" style="padding-left:12px" data-depth="1"><span class="rp-toc-item__text rp-doc">Built-ins</span></a><a href="#fallback-and-escape" class="rp-toc-item rp-link" title="Fallback and escape" style="padding-left:12px" data-depth="1"><span class="rp-toc-item__text rp-doc">Fallback and escape</span></a><a href="#recipes" class="rp-toc-item rp-link" title="Recipes" style="padding-left:12px" data-depth="1"><span class="rp-toc-item__text rp-doc">Recipes</span></a><a href="#notes" class="rp-toc-item rp-link" title="Notes" style="padding-left:12px" data-depth="1"><span class="rp-toc-item__text rp-doc">Notes</span></a><a href="#replay-divergence-and-resume" class="rp-toc-item rp-link" title="Replay divergence and resume" style="padding-left:0" data-depth="0"><span class="rp-toc-item__text rp-doc">Replay divergence and resume</span></a><a href="#resuming-a-failed-replay" class="rp-toc-item rp-link" title="Resuming a failed replay" style="padding-left:12px" data-depth="1"><span class="rp-toc-item__text rp-doc">Resuming a failed replay</span></a><a href="#--update-u-retired" class="rp-toc-item rp-link" title="--update/-u (retired)" style="padding-left:0" data-depth="0"><span class="rp-toc-item__text rp-doc"><code>--update</code>/<code>-u</code> (retired)</span></a><a href="#troubleshooting" class="rp-toc-item rp-link" title="Troubleshooting" style="padding-left:0" data-depth="0"><span class="rp-toc-item__text rp-doc">Troubleshooting</span></a></nav><div class="rp-outline__divider"></div><div class="rp-outline__bottom"><a href="https://github.com/callstack/agent-device/edit/main/website/docs/replay-e2e.md" target="_blank" rel="noopener noreferrer" class="rp-outline__action-row rp-edit-link rp-link"><svg width="16" height="16" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24"><path d="M17 3a2.828 2.828 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5z"></path></svg><span>Edit this page on GitHub</span></a></div></div><div class="container-LzbylO"><div><div class="headline-y75_LS">Curious about developing mobile apps with AI agents?</div><div class="description-muYVs3">We can help you take your agentic workflows to the next level and ship faster.</div></div><a href="https://www.callstack.com/contact?utm_campaign=open_source&amp;utm_source=agent-device&amp;utm_medium=referral&amp;utm_content=OUTLINE_CTA" class="button-DogSnd brand-rT2ahT ">Book a call<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none" viewBox="0 0 16 16" class="button-icon-Okk0hT" aria-hidden="true" focusable="false"><path fill="currentColor" d="M9.333 13.333h1.334V12H9.333zm-6.666-2.666h8V12H12v-1.333h1.333V9.333H12V8h-1.333v1.333H4V2.667H2.667zM9.333 8h1.334V6.667H9.333z"></path></svg></a></div></aside></div></div>
<div id="__rspress_modal_container"></div>
</body>
</html>