agent-device 0.20.6 → 0.20.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. package/README.md +3 -3
  2. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.20.6.apk → agent-device-android-ime-helper-0.20.8.apk} +0 -0
  3. package/android/ime-helper/dist/agent-device-android-ime-helper-0.20.8.apk.sha256 +1 -0
  4. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.20.6.manifest.json → agent-device-android-ime-helper-0.20.8.manifest.json} +4 -4
  5. package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.20.6.apk → agent-device-android-snapshot-helper-0.20.8.apk} +0 -0
  6. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.8.apk.sha256 +1 -0
  7. package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.20.6.manifest.json → agent-device-android-snapshot-helper-0.20.8.manifest.json} +6 -6
  8. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunner/AgentDeviceRunnerApp.m +20 -0
  9. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.h +67 -0
  10. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.m +339 -4
  11. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSelectorMatchPolicy.swift +78 -0
  12. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+AXSnapshotFallback.swift +31 -2
  13. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +34 -8
  14. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CoordinateTextEntryTests.swift +4 -0
  15. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +36 -28
  16. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +7 -0
  17. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+RecordingTests.swift +4 -0
  18. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SelectorMatchPolicyTests.swift +4 -0
  19. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +2 -1
  20. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCapturePlan.swift +100 -12
  21. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedGesturePolicy.swift +4 -0
  22. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedTextEntry.swift +57 -0
  23. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntry.swift +1 -0
  24. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextTyping.swift +18 -3
  25. package/dist/src/agent-browser-tool.js +1 -1
  26. package/dist/src/agent-device-client.js +2 -2
  27. package/dist/src/android-adb.js +1 -1
  28. package/dist/src/android.js +1 -1
  29. package/dist/src/app-helpers.js +1 -1
  30. package/dist/src/app-lifecycle.js +7 -7
  31. package/dist/src/app-lifecycle2.js +2 -0
  32. package/dist/src/app-log-files.js +1 -0
  33. package/dist/src/app-log-reconnect.js +1 -0
  34. package/dist/src/app-log-runtime.js +4 -0
  35. package/dist/src/app-resolution.js +2 -1
  36. package/dist/src/apps.js +5 -5
  37. package/dist/src/audio-probe.js +1 -1
  38. package/dist/src/auth-session.js +1 -0
  39. package/dist/src/auth.js +2 -0
  40. package/dist/src/boot-diagnostics.js +2 -2
  41. package/dist/src/cli-config.js +1 -1
  42. package/dist/src/cli-grammar.js +1 -0
  43. package/dist/src/cli-help.js +199 -259
  44. package/dist/src/cli-output.js +1 -0
  45. package/dist/src/cli-runner.js +1 -2
  46. package/dist/src/cli.js +6 -16
  47. package/dist/src/client-metro.js +1 -1
  48. package/dist/src/cloud-artifacts.d.ts +2 -137
  49. package/dist/src/command-schema.js +3 -3
  50. package/dist/src/connection-runtime.js +1 -0
  51. package/dist/src/connection.js +5 -0
  52. package/dist/src/daemon-shutdown-report.js +1 -0
  53. package/dist/src/daemon.js +2 -0
  54. package/dist/src/debug-symbols.js +1 -1
  55. package/dist/src/device-claim-inspection.js +1 -0
  56. package/dist/src/device-input-state.js +2 -2
  57. package/dist/src/device-ready.js +1 -0
  58. package/dist/src/device.js +1 -1
  59. package/dist/src/device2.js +2 -0
  60. package/dist/src/dispatch.js +1 -0
  61. package/dist/src/find.js +1 -1
  62. package/dist/src/finders.js +1 -1
  63. package/dist/src/generic-settle.js +1 -1
  64. package/dist/src/generic.js +1 -1
  65. package/dist/src/handler-utils.js +1 -0
  66. package/dist/src/harmonyos.js +1 -0
  67. package/dist/src/hdc.js +1 -0
  68. package/dist/src/host-process.js +3 -2
  69. package/dist/src/ime-lifecycle.js +7 -7
  70. package/dist/src/index.d.ts +52 -21
  71. package/dist/src/input-actions.js +4 -4
  72. package/dist/src/input-actions2.js +1 -1
  73. package/dist/src/input-actions3.js +1 -0
  74. package/dist/src/install-artifact.js +1 -5
  75. package/dist/src/install-artifact2.js +1 -1
  76. package/dist/src/install-source2.js +1 -1
  77. package/dist/src/interaction-runtime.js +1 -1
  78. package/dist/src/interaction-snapshot.js +1 -1
  79. package/dist/src/interaction-touch-reference-frame.js +1 -1
  80. package/dist/src/interaction.js +1 -1
  81. package/dist/src/interactor.js +2 -2
  82. package/dist/src/interactor2.js +1 -1
  83. package/dist/src/internal/bin.js +2 -2
  84. package/dist/src/internal/daemon.js +7 -36
  85. package/dist/src/inventory.js +5 -0
  86. package/dist/src/inventory2.js +1 -0
  87. package/dist/src/inventory3.js +2 -0
  88. package/dist/src/inventory4.js +1 -0
  89. package/dist/src/inventory5.js +1 -0
  90. package/dist/src/inventory6.js +1 -0
  91. package/dist/src/lease-context.js +1 -1
  92. package/dist/src/lease.js +1 -1
  93. package/dist/src/limrun-runtime-dependencies.js +1 -0
  94. package/dist/src/limrun.d.ts +10 -125
  95. package/dist/src/limrun.js +1 -1
  96. package/dist/src/linux.js +1 -1
  97. package/dist/src/logcat.js +1 -1
  98. package/dist/src/manifest.js +1 -1
  99. package/dist/src/notifications.js +1 -1
  100. package/dist/src/output-path.js +1 -0
  101. package/dist/src/owner-identity.js +1 -0
  102. package/dist/src/physical-device-control.js +1 -1
  103. package/dist/src/platform-runtime-app-log-android-transport.js +1 -0
  104. package/dist/src/platform-runtime-app-log-process.js +1 -0
  105. package/dist/src/platform-runtime-host.js +1 -0
  106. package/dist/src/platform-runtime-network-web-transport.js +1 -0
  107. package/dist/src/platform-runtime-operation-host.js +5 -0
  108. package/dist/src/platform-runtime-screen-recording-android-host.js +1 -0
  109. package/dist/src/platform-runtime-screen-recording-apple-host.js +1 -0
  110. package/dist/src/platform-runtime-screen-recording-apple-runner-host.js +1 -0
  111. package/dist/src/platform-runtime-screen-recording-apple-runner-transport.js +1 -0
  112. package/dist/src/platform-runtime-screen-recording-apple-simulator-host.js +1 -0
  113. package/dist/src/platform-runtime-screen-recording-apple-transport.js +1 -0
  114. package/dist/src/platform-runtime-screen-recording-finalizer-host.js +1 -0
  115. package/dist/src/platform-runtime-screen-recording-harmony-host.js +1 -0
  116. package/dist/src/platform-runtime-screen-recording-output-host.js +1 -0
  117. package/dist/src/platform-runtime-screen-recording-web-host.js +1 -0
  118. package/dist/src/platform-runtime-toolchain-host.js +1 -0
  119. package/dist/src/platform-runtime.js +1 -0
  120. package/dist/src/prepare-kind.js +15 -0
  121. package/dist/src/process-entry.js +1 -0
  122. package/dist/src/process-lock.js +1 -1
  123. package/dist/src/provider-webdriver.js +1 -0
  124. package/dist/src/proxy.js +2 -0
  125. package/dist/src/react-native.js +1 -1
  126. package/dist/src/record-runtime.js +1 -0
  127. package/dist/src/record-trace.js +1 -1
  128. package/dist/src/recording-telemetry.js +1 -0
  129. package/dist/src/registry.js +24 -24
  130. package/dist/src/replay.js +3 -0
  131. package/dist/src/reporting.js +3 -3
  132. package/dist/src/runner-disposal.js +1 -1
  133. package/dist/src/runner-lease.js +1 -1
  134. package/dist/src/runtime.js +3 -3
  135. package/dist/src/runtime2.js +4 -0
  136. package/dist/src/runtime3.js +3 -0
  137. package/dist/src/runtime4.js +1 -0
  138. package/dist/src/runtime5.js +1 -0
  139. package/dist/src/runtime6.js +1 -0
  140. package/dist/src/runtime7.js +1 -0
  141. package/dist/src/screenshot-result.js +4 -4
  142. package/dist/src/screenshot.js +1 -0
  143. package/dist/src/sdk-android-adb.js +4 -4
  144. package/dist/src/sdk-batch-runner.js +3 -3
  145. package/dist/src/sdk-contracts.d.ts +31 -5
  146. package/dist/src/sdk-contracts.js +1 -1
  147. package/dist/src/sdk-device.js +1 -1
  148. package/dist/src/sdk-finders.js +1 -1
  149. package/dist/src/sdk-remote-config.js +1 -1
  150. package/dist/src/sdk-selectors.d.ts +285 -5
  151. package/dist/src/sdk-selectors.js +1 -1
  152. package/dist/src/selector-runtime.js +1 -1
  153. package/dist/src/selector-vocabulary.js +1 -1
  154. package/dist/src/selectors.js +1 -1
  155. package/dist/src/server.js +3 -3
  156. package/dist/src/session-target-evidence.js +1 -1
  157. package/dist/src/session.js +10 -9
  158. package/dist/src/shared.js +2 -0
  159. package/dist/src/shell-quote.js +1 -0
  160. package/dist/src/simulator.js +1 -1
  161. package/dist/src/snapshot-runtime.js +1 -0
  162. package/dist/src/snapshot-session.js +1 -0
  163. package/dist/src/snapshot2.js +1 -1
  164. package/dist/src/src.js +1 -1
  165. package/dist/src/src2.js +2 -0
  166. package/dist/src/src3.js +1 -0
  167. package/dist/src/text-surface.js +1 -1
  168. package/dist/src/timing-safe-equal.js +1 -0
  169. package/dist/src/tool-provider.js +2 -2
  170. package/dist/src/update-check.js +1 -1
  171. package/dist/src/version.js +1 -1
  172. package/dist/src/video.js +23 -0
  173. package/dist/src/web.js +1 -1
  174. package/package.json +34 -20
  175. package/server.json +2 -2
  176. package/android/ime-helper/dist/agent-device-android-ime-helper-0.20.6.apk.sha256 +0 -1
  177. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.6.apk.sha256 +0 -1
  178. package/dist/src/app-log-request-scope.js +0 -1
  179. package/dist/src/context.js +0 -1
  180. package/dist/src/devices.js +0 -1
  181. package/dist/src/devices2.js +0 -1
  182. package/dist/src/devices3.js +0 -1
  183. package/dist/src/devices4.js +0 -1
  184. package/dist/src/provider-limrun-runtime.js +0 -1
  185. package/dist/src/register-builtins.js +0 -1
  186. package/dist/src/runner-client.js +0 -1
  187. package/dist/src/runner-provider.js +0 -1
  188. package/dist/src/screenshot-geometry.js +0 -1
  189. package/dist/src/viewport-dimension.js +0 -15
  190. package/skills/agent-device/SKILL.md +0 -45
  191. package/skills/dogfood/SKILL.md +0 -27
@@ -1,12 +1,61 @@
1
- import{Jt as e}from"./viewport-dimension.js";import{g as t,p as n,w as r}from"./registry.js";import{i,r as a}from"./command-schema.js";const o=[`Flows: launchApp; runFlow file/inline with platform, visibility, and limited boolean conditions; onFlowStart/onFlowComplete; repeat.times and retry.`,`Interactions: tapOn, doubleTapOn, longPressOn, inputText, eraseText, openLink, hideKeyboard, basic pressKey, and back; targets support index, childOf, label, points, and optional.`,`Assertions and navigation: assertVisible, assertNotVisible, extendedWaitUntil, scroll, scrollUntilVisible, absolute/percentage/target swipe, takeScreenshot, waitForAnimationToEnd, and stopApp.`,`Scripts: ordered runScript file/env scripts with http.post, json, and output variables.`],s=[`Runtime: iOS and Android only; launchApp.clearState supports Android and iOS simulators, launch arguments are Apple-only, and standalone device utility/state commands are unsupported.`,`Expressions: when.true supports boolean literals and maestro.platform comparisons; repeat.while, evalScript, and broader JavaScript expressions are unsupported.`,`Environment: flow env is the default, AD_VAR_* overrides it, and CLI -e KEY=VALUE wins over both.`,`Failure diagnostics: resolved targets and runFlow paths are rendered, while inputText payloads remain hidden; do not place secrets in diagnostic identifiers.`,`Trust: runScript executes trusted scripts, may make http.post network requests, and is not a security sandbox; output keys cannot contain a dot.`,`Errors and tracking: unsupported commands and fields fail with source context when available; open a focused issue only when implementation work is planned.`];function c(){return[`Supported subset:`,...o.map(e=>` - ${e}`),``,`Boundaries:`,...s.map(e=>` - ${e}`)].join(`
2
- `)}function l(e){let t=e.endsWith(`?`),n=t?e.slice(0,-1):e;return t?`[${n}]`:`<${n}>`}function u(e){return r().filter(t=>t.key===e)}function d(e,t){if(t.usageOverride)return t.usageOverride;let n=(t.positionalArgs??[]).map(l),r=(t.allowedFlags??[]).flatMap(e=>u(e).map(e=>e.usageLabel??e.names[0])).map(e=>`[${e}]`);return[e,...n,...r].join(` `)}const f=[{label:`agent-device help manual-qa`,description:`Follow a manual test script with exact interactions and verification`},{label:`agent-device help dogfood`,description:`Explore an app and report issues with evidence`},{label:`agent-device help validate`,description:`Validate code changes, perf, visuals, logs, and cleanup`},{label:`agent-device help workflow`,description:`Full app automation reference for commands, refs, selectors, and waits`},{label:`agent-device help debugging`,description:`Use when logs, network, audio, perf memory, traces, alerts, or diagnostics matter`},{label:`agent-device help tv`,description:`Use when navigating Android TV, tvOS, or Vega VVD focus-first surfaces`},{label:`agent-device help react-native`,description:`Use when the target app is React Native, Expo, or a dev client`},{label:`agent-device help react-devtools`,description:`Use when inspecting components, props/state/hooks, renders, or profiles`},{label:`agent-device help cdp`,description:`Use when investigating JS heap growth, heap snapshots, or retainers`},{label:`agent-device help physical-device`,description:`Use when using a connected phone/tablet or iOS signing setup`},{label:`agent-device help remote`,description:`Use when working through cloud config, tenants, leases, or local tunnels`},{label:`agent-device help web`,description:`Use when automating a browser through agent-device sessions`},{label:`agent-device help macos`,description:`Use when targeting desktop, frontmost app, or menu bar surfaces`}],p=[`Write full command lines starting with agent-device; do not output pseudo commands, helper prose, pipes, grep, jq, or hidden stderr.`,`Default app loop: agent-device open <app> -> agent-device snapshot -i -> mutate a current target with --settle -> continue from that settled diff -> agent-device close.`,`Use --settle only on planned press, click, fill, longpress, scroll, or back commands; never add it to open, snapshot, or close. type never accepts --settle: run agent-device type "text", then diff snapshot if verification is needed. Once the task's requested end state or an explicit success confirmation is visible, stop; do not tap transient follow-up controls or navigate away only to re-verify.`,`Follow structured command hints before choosing a recovery action.`,`Targets are concrete refs or selectors: @e12, label="Query", role=button label="Submit".`,`Selector keys are only: id, role, text, label, value, appname, windowtitle, visible, hidden, editable, selected, focused, enabled, hittable. placeholder, index, and key are not selector keys.`,`A literal @ handle is a label such as label="@account.example", never a bare @ref.`,`Boundary shapes: agent-device fill 'label="Query"' "text" --settle; agent-device press 'role=button label="Submit"' --settle.`,`fill takes a target then text. press targets an accessible element; key=Enter is not a supported target.`,`Pick the help mode below when the task is manual QA, dogfooding, engineering validation, or debugging.`],m=[`Planning output contract: when asked to plan commands, output command lines only: no prose, numbering, Markdown fences, pipes, or shell helpers.`,`If you did not use --settle, verify a mutation with diff snapshot (or diff snapshot -i), not a full snapshot: it prints only the added/removed/changed lines since the last snapshot in this session.`,`Network-backed or debounced results may arrive after the --settle quiet window; follow the settled action with wait text "Expected result" or wait <selector> instead of polling full snapshots.`,`Pin a raw CLI ref to the response that minted it with ~s<n> (n = refsGeneration): press @e12~s4. On iOS, stale refs are rejected for mutations; refresh refs or use a stable selector.`,`Plain snapshot reads state; snapshot -i refreshes current interactive refs only.`,`Default snapshot text is an agent-facing, token-efficient view for planning and targeting actions.`,`Read-only visible/state question: use snapshot/get/is/find; use snapshot -i only when refs are needed.`,`Truncated text/input preview: expand first with snapshot -s @e12, not get text.`,`React Native apps: read help react-native.`,`Text fields: use fill <target> <text> --settle to replace a field value. Use type <text> only to append after focusing a field with press.`,`Clearing text: do not use fill <target> ""; use a visible clear/reset control or report that clearing is unsupported.`,`Implicit default sessions are scoped to the current worktree; if a prompt names a Session, include --session <name> on every command in that flow.`,`Run mutating commands serially within one session; parallelize only read-only commands or separate sessions/devices.`,`After mutation: refs are stale. If the next target is known, use its selector directly; otherwise refresh with snapshot -i, scoped with -s when a stable container is known. Use press/click for taps.`,`macOS context menus use click <ref> --button secondary, then snapshot -i. Longpress is for mobile hold gestures, not macOS secondary-click menus.`,`Remote lifecycle: use connect, then open, commands, close, and disconnect. Read help remote for proxy, cloud, and device-cloud provider flows.`,`TV/D-pad targets: read help tv. Web browser sessions: read help web.`,`Debug evidence: Session state contains request diagnostics and runner.log; use logs clear --restart/mark/path, trace, and network dump --include headers for app evidence.`,`Routine QA loop with concrete command shapes: agent-device help manual-qa. Full operating guide: agent-device help workflow. Exploratory QA: agent-device help dogfood.`],h=[`Default config files: ~/.agent-device/config.json, ./agent-device.json (project-safe defaults only).`,`Use --config <path> or AGENT_DEVICE_CONFIG for explicit connection/provider defaults; project config cannot select endpoints or credentials.`],g=[{label:`AGENT_DEVICE_SESSION`,description:`Explicit session name`},{label:`AGENT_DEVICE_PLATFORM`,description:`Default platform binding`},{label:`AGENT_DEVICE_SCREENSHOT_SCALE`,description:`Default screenshot scale factor`},{label:`AGENT_DEVICE_SESSION_LOCK`,description:`Bound-session conflict mode`},{label:`AGENT_DEVICE_DAEMON_BASE_URL`,description:`Connect to remote daemon`},{label:`AGENT_DEVICE_DAEMON_AUTH_TOKEN`,description:`Remote daemon service/API token`},{label:`AGENT_DEVICE_CLOUD_BASE_URL`,description:`Bridge/control-plane API origin for cloud auth and /api-keys`}],_=[`agent-device open Settings --platform ios`,`agent-device open https://example.com --platform web`,`agent-device snapshot -i`,`agent-device fill @e3 "test@example.com"`,`agent-device replay ./session.ad`,`agent-device test ./suite --platform android`],v=`Wait failure contract:
3
- Read wait failures from error.details.reason in --json output; do not infer the verdict from the message.
4
- wait_target_absent means at least one readable capture saw no matching target. It includes readableCaptures and waitedMs, and may include currentSurface details.
5
- wait_capture_stalled means no readable capture established an observation before the deadline. It is retriable; retry or use screenshot to inspect the surface.
6
- wait_deadline_exceeded means a later capture consumed the remaining budget after an earlier readable capture; it includes captureTruncated and readableCaptures.
7
- wait_landmark_identity_mismatch means a replay destination guard found the selector but not the recorded target identity.
8
- wait_stable_timeout means wait stable did not observe a stable UI; it is not an element-absence verdict.
9
- `,y={"manual-qa":{summary:`Follow manual test scripts with exact interactions and verification`,body:`agent-device help manual-qa
1
+ import{n as e}from"./version.js";import{mn as t}from"./sdk-batch-runner.js";import{Qt as n}from"./prepare-kind.js";import{S as r,d as i,m as a}from"./registry.js";import{i as o,r as s}from"./command-schema.js";const c=[`Flows: launchApp; runFlow file/inline with platform, visibility, and limited boolean conditions; onFlowStart/onFlowComplete; repeat.times and retry.`,`Interactions: tapOn, doubleTapOn, longPressOn, inputText, eraseText, openLink, hideKeyboard, basic pressKey, and back; targets support index, childOf, label, points, and optional.`,`Assertions and navigation: assertVisible, assertNotVisible, extendedWaitUntil, scroll, scrollUntilVisible, absolute/percentage/target swipe, takeScreenshot, waitForAnimationToEnd, and stopApp.`,`Scripts: ordered runScript file/env scripts with http.post, json, and output variables.`],l=[`Runtime: iOS and Android only; launchApp.clearState supports Android and iOS simulators, launch arguments are Apple-only, and standalone device utility/state commands are unsupported.`,`Expressions: when.true supports boolean literals and maestro.platform comparisons; repeat.while, evalScript, and broader JavaScript expressions are unsupported.`,`Environment: flow env is the default, AD_VAR_* overrides it, and CLI -e KEY=VALUE wins over both.`,`Failure diagnostics: resolved targets and runFlow paths are rendered, while inputText payloads remain hidden; do not place secrets in diagnostic identifiers.`,`Trust: runScript executes trusted scripts, may make http.post network requests, and is not a security sandbox; output keys cannot contain a dot.`,`Errors and tracking: unsupported commands and fields fail with source context when available; open a focused issue only when implementation work is planned.`];function u(){return[`Supported subset:`,...c.map(e=>` - ${e}`),``,`Boundaries:`,...l.map(e=>` - ${e}`)].join(`
2
+ `)}function d(e){let t=e.endsWith(`?`),n=t?e.slice(0,-1):e;return t?`[${n}]`:`<${n}>`}function f(e){return r().filter(t=>t.key===e)}function p(e,t){if(t.usageOverride)return t.usageOverride;let n=(t.positionalArgs??[]).map(d),r=(t.allowedFlags??[]).flatMap(e=>f(e).map(e=>e.usageLabel??e.names[0])).map(e=>`[${e}]`);return[e,...n,...r].join(` `)}function m(){return`agent-device <command> [args] [--json]
3
+
4
+ Automates iOS, Android, macOS, TV, and web apps for AI agents.
5
+ All ${n().length} commands: agent-device help commands
6
+
7
+ Start:
8
+ When starting a task with a known app, first run:
9
+ agent-device open <app> --foreground
10
+ It starts the session and returns the initial interactive snapshot with @refs.
11
+ Do not probe first with devices, apps, appstate, snapshot, or screenshot.
12
+ Unknown app id: devices, then apps, then open <discovered-id>. Never invent ids.
13
+ Resuming an existing session: continue from its current state; do not reopen it.
14
+
15
+ Loop:
16
+ press|click|fill|longpress <target> ... --settle
17
+ scroll <direction|top|bottom> [amount] --settle; back --settle
18
+ acts, waits for quiet, and prints the UI diff. Continue from that diff.
19
+ Run snapshot -i only when the diff lacks the next target or did not settle.
20
+ Verify a named expectation with the diff, wait text "...", wait <selector>,
21
+ is, get, or find. A bare screenshot is not verification.
22
+ End with: agent-device close
23
+
24
+ Targets:
25
+ Copy refs exactly: @e12, @e12~s4. Keep @ and any ~sN pin; refs go stale
26
+ after mutations. A literal @handle is label="@handle", not a bare ref.
27
+ Prefer refs, then id/label/role selectors. Selector keys: ${t.join(` `)}.
28
+ Coordinates are last resort: after snapshot -i shows no semantic target, or a
29
+ sparse/AX-unavailable warning says its refs and selectors are invalid.
30
+ Then screenshot, press <x> <y>, and re-snapshot on the changed screen.
31
+
32
+ Rules:
33
+ --settle is only for press/click/fill/longpress/scroll/back; never open,
34
+ snapshot, or close. type never accepts --settle.
35
+ fill <target> <text> --settle replaces; type <text> appends after focus.
36
+ Late network/debounce result: wait text "Expected", not snapshot polling.
37
+ Output full agent-device commands; no pipes, grep, jq, or pseudo-commands.
38
+ Stop when the requested end state is visible. Mutations run serially.
39
+
40
+ More commands (exact shapes: agent-device help <command>):
41
+ open install devices apps boot close app and device lifecycle
42
+ screenshot record logs network perf trace evidence and diagnostics
43
+ replay test batch session scripted flows
44
+ alert keyboard clipboard settings gesture system and input
45
+
46
+ Guides (agent-device help <topic>):
47
+ workflow full refs, selectors, waits, recovery, and platform limits
48
+ manual-qa / dogfood / validate / debugging / scripting / gestures
49
+ react-native / react-devtools / cdp / tv / web / macos / remote
50
+ physical-device / ios-system-ui / maestro
51
+ `}const h=[`Default config files: ~/.agent-device/config.json, ./agent-device.json (project-safe defaults only).`,`Use --config <path> or AGENT_DEVICE_CONFIG for explicit connection/provider defaults; project config cannot select endpoints or credentials.`],g=[{label:`AGENT_DEVICE_SESSION`,description:`Explicit session name`},{label:`AGENT_DEVICE_PLATFORM`,description:`Default platform binding`},{label:`AGENT_DEVICE_SCREENSHOT_SCALE`,description:`Default screenshot scale factor`},{label:`AGENT_DEVICE_SESSION_LOCK`,description:`Bound-session conflict mode`},{label:`AGENT_DEVICE_DAEMON_BASE_URL`,description:`Connect to remote daemon`},{label:`AGENT_DEVICE_DAEMON_AUTH_TOKEN`,description:`Remote daemon service/API token`},{label:`AGENT_DEVICE_CLOUD_BASE_URL`,description:`Bridge/control-plane API origin for cloud auth and /api-keys`}],_=[`agent-device open Settings --platform ios`,`agent-device open https://example.com --platform web`,`agent-device snapshot -i`,`agent-device fill @e3 "test@example.com"`,`agent-device replay ./session.ad`,`agent-device test ./suite --platform android`],v=`Wait failure contract:
52
+ Read the verdict from error.details.reason in --json, not the message text.
53
+ wait_target_absent: a readable capture ran and found no match.
54
+ wait_capture_stalled: no readable capture finished before the deadline -- retriable.
55
+ wait_deadline_exceeded: a later capture used the remaining budget after an earlier readable one.
56
+ wait_landmark_identity_mismatch: a replay destination guard found the selector but not the recorded identity.
57
+ wait_stable_timeout: wait stable never saw a stable UI -- not an absence verdict.
58
+ `,y={commands:{summary:`Full command catalog, global flags, configuration, and environment`,body:`agent-device help commands`},"manual-qa":{summary:`Follow manual test scripts with exact interactions and verification`,body:`agent-device help manual-qa
10
59
 
11
60
  Use this when asked to follow a manual QA script, test case, checklist, acceptance flow, or user-provided instructions.
12
61
 
@@ -50,257 +99,142 @@ ${v}`},maestro:{summary:`Supported Maestro YAML commands, grammar, and runtime b
50
99
 
51
100
  Run Maestro compatibility flows with replay <flow.yaml> --maestro or test <path> --maestro. Bind an iOS or Android target with --platform or an existing session.
52
101
 
53
- ${c()}
102
+ ${u()}
54
103
 
55
104
  Unsupported syntax fails loudly rather than being skipped. Architecture, performance tradeoffs, and declared conformance divergences: https://github.com/callstack/agent-device/blob/main/docs/adr/0015-direct-maestro-engine.md
56
105
  Focused compatibility request: https://github.com/callstack/agent-device/issues/new`},workflow:{summary:`Normal agent-device bootstrap, exploration, and validation loop`,body:`agent-device help workflow
57
106
 
58
- Version-matched operating guide for normal agent-device work.
59
-
60
- Core loop:
61
- Start with the top-level Agent Starting Point for the default settle-first loop. This topic is the full reference for command shapes, refs, selectors, waits, recovery, and platform limits.
62
- If you intentionally skip --settle or use a command that does not support it, verify a mutation with diff snapshot (or diff snapshot -i) instead of a full snapshot: it diffs the rendered snapshot lines against the previous one in this session and prints only what changed.
63
- Once the task's requested end state or an explicit success confirmation is visible, stop; do not tap transient follow-up controls or navigate away only to re-verify.
107
+ Command shapes, refs, selectors, waits, recovery, and platform limits for the default open -> snapshot -i -> settle -> verify -> close loop.
64
108
 
65
109
  Command shape:
66
- Plans should use agent-device commands, not raw platform tools, pseudo commands, package-manager aliases, or helper prose.
67
- If the user asks for a command plan, final output should be command lines only: no intro sentence, numbered list, Markdown fence, shell pipe, grep/head/tail helper, or explanatory bullets.
68
- While exploring, do not pipe or redirect agent-device output through jq/grep/head/tail or 2>/dev/null; raw output carries refs, warnings, hints, and diagnostics needed for the next step.
69
- Put subcommand first, then positionals, then flags:
70
- agent-device open com.example.app --session checkout --platform android --relaunch
71
- agent-device record start ./checkout.mp4 --session checkout
72
- Snapshot refs look like @e12. After snapshot -i, use the exact @eN ref from that output.
73
- If the exact ref is not known yet, first output snapshot -i, then use a concrete example shape like press @e12 in the next command; do not write @<ref>, @ref, @Label_Name, or @eN placeholders.
74
- Close means agent-device close. App-owned back means back; system back means back --system.
75
- type never accepts --settle: run agent-device type "text", then diff snapshot if verification is needed.
76
- Taps are press or click; tap is an alias for press. On Android TV, tvOS, and Vega OS, read help tv and use tv-remote press up|down|left|right|select to move D-pad/remote focus before activating controls; use tv-remote longpress <button> for a held remote button. Gestures use swipe, longpress, or gesture <pan|fling|swipe|pinch|rotate|transform|drag>. Use gesture swipe left|right for reliable in-page horizontal swipes, and gesture swipe right-edge for left-edge navigation/back gestures. gesture pan is one finger by default; add --pointer-count 2 for a parallel two-finger pan. gesture drag resolves source and destination selectors or refs, then keeps one pointer down through source hold, movement, and optional destination hold. Android swipe and multi-touch gestures use provider-native touch injection when available, then the bundled touch helper. iOS simulator multi-touch uses private XCTest synthesis for a continuous two-pointer path; otherwise it reports UNSUPPORTED_OPERATION.
110
+ Command lines only -- no prose, numbering, fences, pipes, or grep/head/tail/jq on agent-device output; raw output carries the refs/hints the next step needs. Subcommand first, then positionals, then flags: agent-device open com.example.app --session checkout --platform android --relaunch
111
+ Chain confident consecutive steps with &&: press 'label="Search"' --settle && fill 'label="Search"' "query" --settle. Fall back to one command at a time when a step is uncertain (ambiguous match, network-backed result, unseen screen).
112
+ Refs look like @e12; use the exact ref from the latest snapshot -i, never a placeholder (@ref, @eN, @Label_Name). Pin with ~s<n> (press @e12~s4); iOS rejects a stale pinned ref -- refresh with snapshot -i or use a selector.
113
+ close = agent-device close. App back is back; system back is back --system. Taps are press/click. type never takes --settle: run type, then diff snapshot to verify. Known flow: batch ./steps.json (help scripting).
114
+ Gestures: scroll/swipe for lists/flicks; gesture pan|fling|pinch|rotate|transform|drag for multi-touch. Shapes and platform quirks: help gestures.
77
115
 
78
116
  Bootstrap:
79
117
  agent-device devices --platform ios
80
- agent-device capabilities --platform android
81
- agent-device apps --platform android
82
118
  agent-device open MyApp --platform ios --device "iPhone 17 Pro"
83
- agent-device open <discovered-app-id> --session checkout --platform android
84
- agent-device install com.example.app ./dist/app.apk --platform android
85
- agent-device reinstall com.example.app ./build/MyApp.app --platform ios
86
- agent-device install-from-source --github-actions-artifact org/repo:app-debug --platform android
87
- agent-device open com.example.app --platform android --relaunch
88
- agent-device prepare ios-runner --platform ios --timeout 240000
89
- If app id is unknown, plan devices, apps, then open <discovered-app-id>. Use capabilities only when a dynamic integration needs the command names supported by the selected target; normal app-driving loops do not need it. Discovery is not enough when the task asks to open/start the app.
90
- Install arguments are app/package id then artifact path. If the task says install, use install; use reinstall only when explicitly requested. Fresh runtime state is open --relaunch after install.
91
- In Apple CI, run prepare ios-runner after boot/install and before replay/test. prepare ios-runner builds/reuses the XCTest runner, health-checks it with a lightweight command, and retries one stuck/non-connecting runner launch before the first snapshot pays that setup cost. It is not a recovery step for "runner already owned by another agent-device daemon"; stop the owning daemon on the Mac with simulator access instead. If the replay/test step starts a separate daemon, stop the prepare daemon before replay/test so the prepared runner does not keep a live lease owned by that daemon.
92
- CI may cache ~/.agent-device/apple-runner/derived with an exact key that includes the agent-device package and Xcode version. Avoid broad restore-key fallbacks; prepare ios-runner already recovers bad restored runner artifacts and one retryable non-connecting runner launch. Runner build/start output is written to the session's runner.log; daemon.log is for daemon lifecycle/startup issues.
93
- Do not open artifact paths or invent package ids. If apps lookup misses the target and no URL/artifact is provided, ask or stop.
94
-
95
- Reusable open-to-destination scripts:
96
- Arm recording on the first open, perform the full journey, verify the ready destination with a selective selector-targeted wait, then publish without closing:
97
- agent-device open com.example.app --relaunch --save-script=screen-x.ad
98
- agent-device press 'id="continue"' --settle
99
- agent-device wait 'role="heading" label="Screen X"'
100
- agent-device session save-script
101
- session save-script [path] [--force] publishes the sole recorded open through the destination guard, omits close, and leaves the session active. The guard is a selector wait on a labeled or id-bearing landmark: its recorded identity is captured while armed, and replay verifies that identity after the wait's selector resolves, so a reshuffled screen with the same label elsewhere fails closed instead of false-passing. A duration wait, wait stable, wait @ref, or a selector wait on an unlabeled element is not a destination guard. A second successful open aborts publication; start a fresh session to author again.
102
- Unparameterized fill/type inputs are literal .ad script content. For a sensitive fill, arm recording first, keep the live value in an environment variable, and name its replay placeholder explicitly:
103
- export AD_VAR_PASSWORD='<secret>'
104
- agent-device fill 'id="password"' "$AD_VAR_PASSWORD" --record-as PASSWORD
105
- The live app receives the value, while recording state and the published script contain only \${PASSWORD}. Reuse the same name for repeated values and choose distinct names for distinct inputs. --record-as accepts uppercase replay variable names, is fill-only, requires an armed recording, and cannot be combined with --no-record.
106
- Publish with session save-script, then replay with AD_VAR_PASSWORD still set or pass --env PASSWORD=<value>. A missing value fails before that fill runs. Do not record passwords, tokens, or other secrets without --record-as; their literal text will be written to the .ad target.
119
+ Known app: open <app> --foreground -> snapshot. Bare form needs one running app on one iOS sim; capture failure keeps session open.
120
+ Install arguments are app/package id then artifact path: agent-device install com.example.app ./dist/app.apk --platform android, then open <id> --relaunch for fresh state. Use reinstall only when explicitly requested.
121
+ Unknown app id: devices, then apps, then open <discovered-app-id>. Never open artifact paths or invent package ids; ask if lookup misses the target.
122
+ Apple CI: prepare ios-runner after boot/install, before replay/test (help prepare). Remote/cloud: connect -> open -> commands -> close -> disconnect (help remote). Reusable scripts, secret-safe fills, replay repair: help scripting.
107
123
 
108
124
  Snapshots and refs:
109
- snapshot reads visible state. snapshot -i gets current interactive refs only; it is the fast path when the next step is an interaction.
110
- Default snapshot text is an agent-facing, token-efficient view for planning and targeting actions; use --raw or --json only when you need the full provider tree.
111
- Snapshot legend:
112
- @e12 [button] label="Add to cart" id="add-cart" enabled hittable -> press @e12 or press 'id="add-cart"'.
113
- @e13 [textinput] label="Notes" preview="Leave at side..." truncated -> snapshot -s @e13 before reading.
114
- @e14 [cell] label="Profiles" focused -> tvOS focus is currently on this row.
115
- [off-screen below] 4 items: "Privacy", "About" -> scroll down, then snapshot -i; those are hints, not refs.
116
- For press/fill/click/longpress, prefer --settle and continue from its settled diff when it exposes the next target or evidence. Refresh with snapshot -i only when you did not settle, settle printed not settled, or the settle output lacks what you need.
117
- Anti-pattern: snapshot -i followed by snapshot -i | grep ..., or adding 2>/dev/null | jq ... before reading the raw command output.
118
- Refs from the first snapshot remain valid until you press, click, fill, type, scroll, go back, wait for async UI, or otherwise change app state.
119
- Pinned refs (@e12~s4, generation from refsGeneration or settle.refsGeneration) identify their source tree. On iOS, stale refs are rejected for press/fill/click/longpress before dispatch; refresh with snapshot -i or use a stable selector. Read-only commands report a staleness warning, and other platforms retain their existing warning behavior.
120
- After a mutation, prefer a known selector/label directly (for example press 'label="Send"') because interaction commands refresh interactive state internally. A settled diff with no added refs (for example a modal dismiss) also lists an "unchanged interactive" tail of still-present refs, so check that before falling back. If you need to discover a new control not shown by settle or its tail, use snapshot -i, or snapshot -i -s "Composer" when a stable container label/id can scope the refresh.
121
- If typing/fill opened the keyboard or changed layout and the next target has no stable selector, run snapshot -i, use the fresh ref, then verify with wait/find or diff snapshot -i.
122
- For a targeted query, use find/get/is. If you truly need the full tree again, pass --force-full.
123
- Off-screen summaries are scroll hints; use scroll, not swipe, then snapshot -i.
124
- Missing target in a long list: use a short manual scroll + snapshot loop with a max attempt count. If a named target is summarized as off-screen below/above, use scroll down/up, then snapshot -i; do not use scroll bottom/top because the target may appear before the absolute list edge. Use scroll bottom/top only when the task explicitly asks for the list edge. Edge scrolls verify hidden content with snapshots and stop when no matching hidden content remains.
125
- Truncated text/input previews: do not use get text first; expand with snapshot -s @ref (for example snapshot -s @e7), then read the scoped output.
126
- Rare iOS accessibility gaps: if a row ref is shown disabled/hittable:false and press @ref reports success but no UI change, or a horizontal tab/filter bar is collapsed into one composite/seekbar with no child refs, run agent-device snapshot -i --json to read rects, compute the target center, press x y, then diff snapshot -i. Coordinates are fallback-only; document why you used them.
127
- TV focus gaps: read help tv. If a fresh snapshot exposes focused nodes, verify with is focused <selector>; use wait focused=true only on apps where repeated snapshots preserve focus metadata. If the app exposes only a surface view or focus metadata is transient, use screenshot/snapshot diff as visual truth and tv-remote press directions/select; do not switch to raw adb keyevent in command plans.
125
+ snapshot reads visible state; snapshot -i gets current interactive refs only -- the fast path before an interaction. Default text is agent-facing and token-efficient; --raw/--json only for the full provider tree.
126
+ Legend: @e12 [button] label="Add to cart" enabled hittable -> press @e12. [off-screen below] -> scroll down (a hint, not a ref).
127
+ Refs stay valid until you press/click/fill/type/scroll/back/wait-for-async-UI, or otherwise change app state; open/--relaunch clears the stored snapshot outright.
128
+ Prefer --settle and continue from its settled diff when it shows the next target; refresh with snapshot -i only when you did not settle, settle reported not settled, or its output lacks what you need. A known selector/label after a mutation is often enough, since interaction commands refresh state internally.
129
+ Truncated preview: snapshot -s @e12 (the current concrete ref), not get text. Missing target in a list: scroll down/up (not bottom/top unless the task wants the edge), then snapshot -i. TV/D-pad focus: help tv.
128
130
 
129
131
  Selectors:
130
- Use selectors as positional targets: id="field-email" or label="Allow".
131
- Selector terms are key=value filters such as id="submit", label="Search", text="Search", or role=button label="Search". Do not write role names as keys, such as button="Search"; use role=button label="Search" or the @ref from the latest snapshot/settle diff.
132
- Do not use CSS selectors, pseudo refs, --selector, --text, or raw x/y when refs/selectors exist.
133
- agent-device fill 'id="catalog-search"' "tart" --delay-ms 80
134
- agent-device press 'id="submit-order"'
135
- agent-device is visible 'label="Online"'
136
- agent-device get text 'id="quantity-value"'
137
- Ambiguous selector disambiguation: a selector on an interactive command (press/click/fill/focus/longpress/scroll/swipe/pinch) that matches multiple elements does not fail by default. It auto-resolves deepest node first (largest depth in the tree), then smallest on-screen area; only an exact tie on both depth and area fails with "Selector did not resolve uniquely". replay's suggestion re-resolution (in a divergence report) applies the same depth-then-area policy for touch/fill/get-text, so recorded flows and live commands pick the same candidate. This exists because short/reused labels (tab + header + button with the same text, or a duplicated list-row label) are common in real apps; add id="..." or a longer/more specific text to force a different match instead of assuming ambiguous selectors always fail.
138
- Selector match can still land on a non-interactive node (for example an off-screen map annotation that exact-matches text= while the real control has a longer label). Success responses for press/fill/click/ref targets carry targetHittable: false and a hint when the resolved element reports hittable: false, since the tap may have had no visible effect; treat that as a signal to verify with a snapshot or re-target by @ref/longer text, not as a command failure.
132
+ id="field-email", label="Allow", role=button label="Search" -- not bare role keys (button="Search"); no CSS selectors/--selector/--text/raw x-y when refs/selectors exist.
133
+ Mutating selector ambiguity: press/click/fill/longpress collapse duplicate accessibility wrappers only when every match is one ancestor-descendant chain resolving to the same actionable node. Matches in distinct subtrees fail with AMBIGUOUS_MATCH and a bounded candidate list; geometry never chooses a winner. Retry one printed candidate ref (pinned to refsGeneration) or narrow the selector with role/id/longer text. Read-only commands and replay suggestions retain their declared resolution policies.
134
+ hittable: false on a resolved element does not block dispatch (iOS AX flags are unreliable on deep RN trees); press/fill/click return targetHittable: false plus a hint -- verify or re-target, not a failure.
139
135
 
140
136
  Text entry:
141
- fill replaces; type appends to focused field.
142
- agent-device fill @e5 "qa@example.com"
143
- agent-device fill 'id="field-email"' "qa@example.com"
144
- agent-device press 'id="product-note"'
145
- agent-device type "Handle with care" --delay-ms 80
146
- Empty replacement is not a supported clear-field command: do not plan fill <target> "" or fill <target> ''. Prefer a visible clear/reset control; if the app exposes none, report the tool gap instead of inventing a clear command.
147
- Debounced field with no result selector: agent-device wait 1000. Keyboard read-only: keyboard status/get. The on-screen keyboard usually does not block agent-device interactions; press the next target directly instead of dismissing. If that press fails or reports no visible effect, scroll the target into view or use keyboard enter when submission is wanted.
148
- Only dismiss the keyboard when hiding it is the actual goal. To hide the keyboard, use keyboard dismiss. It taps the keyboard's own dismiss/hide key when one is exposed (common on iPad, rare on iPhone) and verifies the keyboard closed. When no dismiss key exists it reports UNSUPPORTED_OPERATION rather than tapping elsewhere — no tap outside the keyboard can be proven side-effect-free. Then prefer submitting (type "\n" on single-line fields) or pressing a known on-screen control that does not mutate state.
149
- On iOS, if it still returns UNSUPPORTED_OPERATION, both mechanisms were exhausted: do not assume a static text or heading is safe to press, because it can belong to a tappable parent. Use an app-provided dismiss control only when its action is explicitly intended; otherwise report that keyboard dismissal is unavailable.
150
- On Android, keyboard dismiss first avoids navigation. If it returns UNSUPPORTED_OPERATION because the current IME needs back navigation, use back only when normal back behavior is acceptable; otherwise report that keyboard dismissal is unavailable.
151
- Use plain fill/type first for ordinary login and form fields. If an iOS debounced or search-as-you-type field actually drops characters, or must receive incremental updates, retry with --delay-ms before trying clipboard paste; --delay-ms intentionally paces character entry.
152
- iOS Allow Paste prompt cannot be exercised under XCUITest. To test paste-driven app behavior, prefill first with agent-device clipboard write "some text"; test the system prompt manually.
153
- Android Gboard handwriting/stylus UI can capture text in an IME-owned input instead of the app field. If fill reports that input was captured by the keyboard/IME, use the diagnostic targetInput/actualInput details, inspect keyboard status/get if needed, and switch or disable handwriting outside the command plan before retrying. Do not keep retrying fill/type against the same field while the IME owns focus.
154
- Android text entry is owned by agent-device: provider-native text injection when available, else the bundled test IME helper (emulators activate it automatically; real devices need open --test-ime), else chunk-safe ASCII shell input. Do not switch to raw adb, clipboard, or paste as an agent fallback. If non-ASCII text still fails, report the tool/device gap.
137
+ fill replaces; type appends to an already-focused field: fill 'id="field-email"' "qa@example.com"; type "Handle with care" --delay-ms 80
138
+ Empty replacement is not a clear-field command (do not plan fill <target> ""); use a visible clear/reset control, or report the gap.
139
+ Plain fill/type first; if an iOS debounced/search-as-you-type field drops characters, retry with --delay-ms before clipboard paste.
140
+ The keyboard usually does not block interactions -- press the next target directly. keyboard dismiss taps its own dismiss key when one exists, else UNSUPPORTED_OPERATION. Android: try dismiss before back. iOS: when both fail, do not tap a static text/heading hoping it is safe; prefer type "\\n" to submit.
141
+ iOS paste-prompt limits and Android IME/handwriting capture quirks: help debugging.
155
142
 
156
143
  Session ordering:
157
- Stateful commands within one session must run serially. Do not run open/press/fill/type/scroll/back/alert/replay/batch/close commands in parallel against the same session.
158
- It is fine to parallelize independent read-only collection or commands that use different sessions/devices.
144
+ Stateful commands (open/press/fill/type/scroll/back/alert/replay/batch/close) run serially within one session. Parallelize only read-only commands, or separate sessions/devices.
159
145
 
160
146
  Read-only and waits:
161
147
  ${v}
162
- Read-only visible/state question: use snapshot/get/is/find.
163
- agent-device snapshot
164
- agent-device get text 'id="product-title"'
165
- agent-device get attrs @e4
166
- agent-device is visible 'label="Online"'
167
- agent-device wait text "Refreshing metrics..." 3000
168
- agent-device wait 'label="Ready"' 3000
169
- agent-device wait stable
170
- agent-device wait stable 500 10000
171
- For network-backed search/typeahead, --settle confirms the local UI quieted after fill/press; use wait text "Expected result" or wait <result selector> for server-loaded content that can arrive later.
172
- agent-device find "Increment" press --json
173
- For async/list text presence, prefer wait text over is visible when no interaction is needed.
174
- wait stable [quietMs] [timeoutMs] (defaults 500/10000) is a fallback for open/relaunch/navigation, unsupported mutating commands, or a mutation where you intentionally did not use --settle. Do not insert wait stable after press/fill/click/longpress --settle when the settled diff already shows what changed. wait stable polls the interactive-only tree and resolves once two or more consecutive captures are unchanged for quietMs, or fails with the standard wait-timeout shape plus capture stats (captures, nodeCount).
175
- Use snapshot -i only when refs are needed for an action or targeted query.
176
- Ambiguous find: add --first or --last. If info is not visible/exposed, report that gap instead of typing/searching/navigating to reveal it.
177
-
178
- Navigation and gestures:
179
- Use scroll for lists; swipe for quick coordinate gestures/carousels; gesture pan for deliberate timed drags; gesture fling for fast directional throws.
180
- For fast macOS desktop list traversal, prefer fixed pixel wheel steps and batch them when no snapshot is needed between passes:
181
- agent-device scroll down --pixels 200 --duration-ms 50 --platform macos
182
- agent-device batch --steps '[{"command":"scroll","input":{"direction":"down","pixels":200,"durationMs":50}},{"command":"scroll","input":{"direction":"down","pixels":200,"durationMs":50}}]' --platform macos
183
- For raw coordinate gestures, run snapshot -i first and choose a point near the center of the intended app-owned target. Avoid screen edges, tab bars, navigation bars, and home indicators because those areas can trigger system or app navigation instead of the gesture under test.
184
- If app-owned back is ambiguous or has just misrouted, prefer a visible nav/back button ref, tab-bar ref, or deep link over repeated back/system back.
185
- App-owned action sheets, menus, and camera/scan screens are normal UI. After opening one, run snapshot -i or wait for the option, press by label/ref, handle visible permission sheets through UI or platform-supported native alerts, then wait for a concrete result before returning to chat/form state.
186
- Keep count/pause/pattern on one swipe; flags are --count, --pause-ms, --pattern ping-pong. Count is capped at 200, pause at 10000ms, and the combined swipe/pause schedule at 60000ms.
187
- For repeated iOS gesture smoke checks, use press <x> <y> --count <n> --jitter-px <n> for tap series and swipe <x1> <y1> <x2> <y2> --count <n> for drag series.
188
- longpress accepts coordinates, @refs, or selectors. Prefer @ref/selector from snapshot -i; use coordinates only as a fallback when accessibility refs miss the exact target. Duration and gesture scale/center are positional:
189
- agent-device longpress 300 500 800
190
- agent-device longpress @e12 800
191
- agent-device swipe 320 500 40 500 --count 8 --pause-ms 30 --pattern ping-pong
192
- agent-device gesture pan 200 420 0 -80 500
193
- agent-device gesture pan 200 420 80 -40 700 --pointer-count 2
194
- agent-device gesture fling right 200 420 180
195
- agent-device gesture pinch 0.5 200 400
196
- agent-device gesture rotate 35 200 420
197
- agent-device gesture transform 200 420 80 -40 2 35 700
198
- iOS simulator transform uses private XCTest synthesis for a continuous two-finger pan/scale/rotation path; verify app metrics instead of assuming requested values map exactly to recognizer output.
199
- Android transform injects a geometric two-finger path; app recognizers may report non-exact pan/scale/rotation. For Android combined transforms, verify semantic app state or coarse per-component effects instead of exact numeric deltas unless the app explicitly exposes stable metrics.
200
- agent-device gesture transform 200 420 80 -40 2 35 700 --platform android
201
- agent-device wait text "pan changed yes" 3000 --platform android
202
- agent-device wait text "pinch changed yes" 3000 --platform android
203
- agent-device wait text "rotate changed yes" 3000 --platform android
204
- If Android needs exact app-state values, prefer isolated gesture pan --pointer-count 2, gesture pinch, or gesture rotate commands over one combined transform.
205
- Gesture planning prefers the active-app frame. A backend without a gesture viewport resolver falls back to the visible snapshot union, which can be less accurate near edges.
206
- tvOS coordinate pan and fling preserve only the dominant direction as a remote swipe; authored endpoints and duration are not preserved.
207
- macOS context menus are secondary clicks, not long presses:
208
- agent-device click @e66 --button secondary --platform macos
209
- agent-device snapshot -i --platform macos
148
+ snapshot/get/is/find answer read-only questions; snapshot -i only when refs are needed. --settle confirms local UI quieted; for results that arrive later (network/debounce), follow with wait text "Expected result" or wait <selector> instead of polling.
149
+ wait stable [quietMs] [timeoutMs] (defaults 500/10000) is the fallback for open/relaunch/navigation, or an intentionally-unsettled mutation -- not after a --settle whose diff already shows the change. Ambiguous find: add --first or --last.
150
+
151
+ Navigation:
152
+ Pick a coordinate gesture point near the target's center, away from edges/tab bars/nav bars/the home indicator (they trigger system navigation instead); macOS context menus are secondary clicks (help macos). Action sheets/menus/camera screens are normal UI: snapshot -i, press by label/ref, handle permission sheets via UI/alert. If back is ambiguous, prefer a nav/back ref, tab-bar ref, or deep link over repeating it.
210
153
 
211
154
  Validation and evidence:
212
- Nearby mutation diff: agent-device diff snapshot -i.
213
- Expected text/selector verification must include the exact text or selector via wait, is, get, or find; bare screenshots/snapshots are insufficient for named expectations.
214
- When an action is only a means to reveal or reach an expected target, do not stop at the action itself. Follow it with exact target verification using the id, selector, or text named by the task.
215
- Prefer provided testIDs/ids/selectors for verification; use visible text when no durable selector is provided.
216
- If task says snapshot, use snapshot. If it asks visual evidence, use screenshot.
217
- Icon/tappable visual proof: screenshot --overlay-refs. Flag is --overlay-refs.
218
- If snapshot returns a sparse/AX-unavailable state, refs are not reliable. Use plain screenshot, not screenshot --overlay-refs, navigate with coordinates if needed, then retry snapshot -i after reaching another screen; the AX failure may be screen-specific.
219
- agent-device screenshot
220
- agent-device press 124 817
221
- agent-device snapshot -i
222
- Startup/CPU/memory/frame first pass: perf metrics --json (bare perf and metrics are aliases). Focused frame/jank health: perf frames --json. Memory-only sample: perf memory sample --json returns compact JSON with bounded top offenders. Heap/memgraph artifact escalation: perf memory snapshot --out heap.artifact; use --kind android-hprof on Android or --kind memgraph on supported Apple simulator/macOS app sessions. Android native profiling: perf cpu profile start|stop|report --kind simpleperf --out <path>; Android native traces: perf trace start|stop --kind perfetto --out <path>. Artifact collectors return compact state/path/size metadata only; raw heap/profile/trace files stay on disk. Treat native perf output as the agent evidence: for example, a Perfetto stop can return state=stopped, outPath=/tmp/app.perfetto-trace, sizeBytes=5392410, and method=adb-shell-perfetto while the 5.3 MB raw trace stays in the artifact. This is better than raw dumps for agents because it is stable, bounded, and keeps large artifacts out of context. heapprofd is deferred until Perfetto plumbing is available. Replay divergence and resume: a failing replay/test step returns REPLAY_DIVERGENCE with a bounded report (screen digest, ranked selector suggestions, resume). Repair app state, then resume with replay --from <n> --plan-digest <sha256> (both from the report's resume field) to continue from the failed step without re-running earlier ones; resume never re-executes skipped steps, so app state is the caller's responsibility, and it is rejected with INVALID_ARGS when the plan digest is stale, --from is out of range, or the skipped range/target touches runtime control flow. The plan digest binds the script, its includes, the effective --platform/--target, and per-action runtime/identity. Native .ad interpolation is late-bound after planning, so changing only its values keeps the digest; Maestro environment substitution occurs during compatibility parsing and can change action inputs, includes, or control expansion, so it can change the digest. --from is replay-only; test rejects it. Native .ad session takeover: replay <file>.ad --keep-session suppresses exactly an authored terminal close and returns the surviving session for continued commands; interior closes still run, close-less scripts are unchanged, and test/Maestro reject the option. --update/-u no longer rewrites the script (ADR 0012) — it is a no-op kept for compatibility; every divergence already carries the same ranked suggestions. Agent-supervised repair (heal-by-doing): arm replay <file>.ad --save-script[=<out>] before step 1 (armed once; --from continuations do not need it again). Every divergence carries a repairHint: record-and-heal means press the correct control via a blessed @ref from the divergence's screen.refs, recorded (no --no-record), then continue with replay --from <n+1> --plan-digest <sha256>; state-repair means the script is correct but app state is not, so fix state with --no-record actions, then replay --from <n> --plan-digest <sha256> to re-run the unchanged step; caution means something already matches the recorded selector, so a blind re-press may repeat the mistake; manual means no safe automated repair could be proven. (If close --save-script is run before a required resume, the repair is aborted and no script is written). While armed, read-only inspection YOU run to locate the repair target (snapshot -i, get attrs, find, is) is excluded from the healed script by default — no --no-record needed; the script's own authored get/is/find steps are unaffected and stay in the heal. If the step you are repairing is itself a read, pass --record on that one command so it lands in the heal (--record is accepted only on snapshot/get/is and a read-only find; it is mutually exclusive with --no-record). Ending the repair with close --save-script[=<out>] writes only the steps recorded since the arming replay as <out>, defaulting to the <file> sibling <stem>.healed.ad — review its diff before promoting it over the original.
223
- Recording: record start/stop. The default scope is app and expects an active session created by open <app>; this keeps app proof videos tied to the intended app session. Use record start --scope device/system to explicitly request whole-screen capture where the selected backend supports it, such as recordings that intentionally span multiple apps, home screen, settings, or app transitions. Use --quality medium|high to choose output quality across Android and Apple targets. By default, stop burns touch overlays into the video; use record start --hide-touches for the fastest raw recording. Android record start publishes a durable device manifest. Android adb screenrecord has a 180s platform limit, so longer Android recordings are returned as multiple MP4 chunks while the daemon stays alive; after daemon restart, record stop recovers only manifest-owned chunks and warns when gesture overlays are unavailable. For gesture-heavy iOS simulator proof videos, prefer --hide-touches because overlay timing depends on a stable runner session while gestures are executing. Tracing: trace start ./trace.log, trace stop ./trace.log. Paths are positional.
224
- Stable known flow: batch ./steps.json, not workflow batch.
225
- Inline batch JSON example:
226
- agent-device batch --steps '[{"command":"open","input":{"app":"settings"}},{"command":"wait","input":{"kind":"duration","durationMs":100}}]'
227
- Batch step keys are command, input, and optional runtime. Put command arguments inside input using the same fields as the MCP/Node command. CLI still accepts legacy positionals/flags steps with a deprecation warning until the next major version.
228
- Never use args, step positionals, or flags for new batch JSON; put command inputs under input.
229
- Android animations: settings animations off/on, not animations disable/restore.
230
- Debug logs: logs clear --restart, logs mark, reproduce, then logs path; do not split clear/restart into separate stop/start commands.
231
- Network headers: network dump --include headers; do not write network log headers.
232
- Remote lifecycle: cloud, remote-config, direct proxy, and limrun use the same flow: connect, open, commands, close, disconnect.
233
- Remote config profile: agent-device connect --remote-config ./remote-config.json; then run normal commands and disconnect.
234
- Direct proxy to a Mac you control: cloud/Linux clients can use local/proxy iOS devices through the proxied Mac. Run agent-device connect proxy --daemon-base-url <proxy-agent-device-url> first. Device leases are automatic on open and expire after five minutes of inactivity.
235
- Web: agent-device uses a managed, pinned agent-browser backend as an implementation detail. Use --platform web when a browser step belongs inside an agent-device session, replay, batch, MCP, or typed-client flow; use agent-browser directly for standalone web automation. Run agent-device web setup before first use, then agent-device web doctor for backend health checks. Web automation requires Node 24+. For audio probe start, the first timing positional is duration in seconds and the second is bucket size in milliseconds. On web, audio probe samples HTML media elements, and URL-backed media may be routed through the probe AudioContext while observed. On macOS hosts, audio probe samples host system audio through ScreenCaptureKit for macOS sessions, iOS simulators, and Android emulators; Screen Recording permission is required.
236
- agent-device web setup
237
- agent-device web doctor
238
- agent-device open https://example.com --platform web
239
- agent-device snapshot -i --platform web
240
- agent-device get text @e2 --platform web
241
- agent-device is visible 'label="Welcome"' --platform web
242
- agent-device find text "Welcome" exists --platform web
243
- agent-device click @e12 --platform web
244
- agent-device fill @e13 "qa@example.com" --platform web
245
- agent-device wait text "Welcome" 3000 --platform web
246
- agent-device record start ./artifacts/web-flow.webm --platform web
247
- agent-device network dump 25 --include headers --platform web
248
- agent-device audio probe start 10 1000 --platform web
249
- agent-device screenshot ./artifacts/web-home.png --platform web
250
- agent-device screenshot ./artifacts/web-full.png --platform web --fullscreen
251
- agent-device viewport 1280 900 --platform web
252
- agent-device record stop --platform web
253
- agent-device close --platform web
254
- Minimal web support is for browser sessions with open, snapshot, find, get, is, click/press, fill/type, wait, network dump, audio probe, screenshot, record start/stop with WebM output, close, and replay over those commands. Use agent-browser directly for browser-specific features that agent-device does not surface, such as tab/devtools management, advanced page scripting, network routing/HAR, or raw browser debugging.
255
- macOS menu bar: open ... --platform macos --surface menubar; snapshot -i --platform macos --surface menubar.
256
- Host audio: audio probe start 10 1000 --platform macos|ios|android samples host system audio through ScreenCaptureKit for macOS sessions, iOS simulators, and Android emulators on macOS hosts; grant Screen Recording permission first.
257
- Maestro full-suite validation on explicit connected devices uses one test command with a comma-separated --device list and --shard-all. Use --shard-split only when splitting suite entries across devices:
258
- agent-device test ./e2e/maestro --maestro --device udid1,emulator-5554 --shard-all 2
155
+ Nearby mutation diff: diff snapshot -i; with no prior snapshot it initializes the baseline (zero changes) instead of failing.
156
+ Named expectations need the exact text/selector via wait/is/get/find -- a bare screenshot/snapshot is not verification. Before declaring a task done, confirm the requested end state is actually visible on the current screen, scrolling it into view if needed; get text alone, or stopping one screen early, is not enough.
157
+ When an action only reveals or reaches a target, verify the exact target named, not just the action. Prefer testIDs/ids/selectors over visible text. Icon/tappable proof: screenshot --overlay-refs; if snapshot is sparse/AX-unavailable, use plain screenshot and coordinates, then retry snapshot -i on another screen.
158
+ iOS merged: child ref => press it; else press parent @ref --settle. Names are not selectors.
159
+ Perf/memory/log/network/trace/crash: help debugging. Recording, save-script, batch, replay repair: help scripting.
259
160
 
260
- React Native dev loop:
261
- JS-only change with Metro or Re.Pack connected:
262
- agent-device metro reload
263
- agent-device find "Home"
264
- Do not use agent-device reload. Use open --relaunch for native startup reset.
265
- React Native apps: use help react-native for Metro/Re.Pack Fast Refresh, DevTools routing, and RN-specific blockers; use react-native dismiss-overlay for LogBox/RedBox overlays.
266
- Android RN/Expo/Re.Pack dev server: direct Android URL opens to localhost/127.0.0.1/[::1] with a port auto-configure host reachability. Manual adb reverse tcp:<port> tcp:<port> is only needed for app/package launches or unsupported flows where the app cannot reach the local dev server.
267
- Expo Go is a host shell. Use a provided project URL instead of inventing a bundle id; if no URL is provided but a target/app name is provided, open that target and do not inspect project files to find one. On iOS, prefer host + URL when the host shell is known because direct URL open can report success while leaving the runner/shell focused; verify with snapshot -i after opening:
268
- agent-device open "Expo Go" exp://127.0.0.1:8081 --platform ios
269
- agent-device snapshot -i --platform ios
270
- If recovery follows a runner/shell splash screen, use snapshot -i --platform ios; do not substitute plain snapshot or snapshot --diff.
271
- There is no open-url command; use open with the URL target or host + URL form.
272
- Direct iOS URL open remains valid when no host shell is known, but verify that the app UI loaded:
273
- agent-device open exp://127.0.0.1:8081 --platform ios
274
- Android uses the URL target directly; do not write open <app> <url> there:
275
- agent-device open exp://127.0.0.1:8081 --platform android
276
- Android URL/deep-link opens infer the foreground package after launch when possible, so logs/perf can remain package-bound. If perf still says no package is associated, open the host package/app id first, then open the URL in the same session.
277
- If apps lookup misses the project but shows Expo Go/dev-client and a project URL is available, open the URL/host shell; if no URL is available, ask instead of inventing an app id.
278
- Expo Dev Client/development builds: open the installed dev-client app id/name; if a dev-client URL is provided, open that URL next. For Expo setup use metro prepare --kind expo.
279
- Re.Pack/Rspack apps: use metro prepare --kind repack, or rely on auto-detection when @callstack/repack is in the selected package.json. The command name remains metro for compatibility, but prepare/reload use the shared React Native dev-server /status, /reload, and bundle URL protocol. prepare runs react-native rspack-start when rspack.config.* exists, and react-native webpack-start when webpack.config.* exists.
280
- Module Federation super-apps: treat the native host and each JS-only remote as separate dev-server endpoints. Prepare or reload the host/root with its port, and pass a remote's --bundle-url or --metro-port when you need to target that remote's Re.Pack server.
281
-
282
- Guarantees:
283
- Statements of fact for agents to reason from without probing them via trial commands. Each is backed by source in the agent-device repo; behavior changes land with an updated statement here.
284
- Selector ambiguity: a selector on an interactive command that matches multiple elements does not fail by default. Resolution auto-disambiguates deepest node first, then smallest on-screen area; only an exact tie on both fails with "Selector did not resolve uniquely (...)". replay's suggestion re-resolution applies the same depth-then-area policy, so recorded and live commands pick the same candidate.
285
- Hittability: iOS AX hittable:false on a resolved node does not block resolution or fail the command; non-hittable resolution is allowed by design because iOS AX hittable flags are unreliable on deep React Native trees. press/fill/click success responses carry targetHittable: false plus a hint when the resolved ref or selector target reports hittable: false, so treat that as a signal to verify with a snapshot or re-target, not as a failure.
286
- Open: on iOS, open <app> without --relaunch dispatches a plain simctl launch, which is idempotent-foreground for an already-running app (it brings the process forward; it does not restart it). open --relaunch restarts the app; on iOS simulators (not real devices or macOS) this collapses to one simctl launch --terminate-running-process call instead of a separate terminate-then-launch, so relaunch is a single step there.
287
- Close and runner retention: close keeps a healthy iOS simulator XCTest runner warm by default so the next open on that device skips the runner build, unless --shutdown was requested, the session was recording, the session held a device lease, or the device used a scoped (non-default) simulator set — any of those tear the runner down on close. A retained runner auto-stops after an idle window (default 5 minutes) to release the device's runner lease for other daemons; set AGENT_DEVICE_IOS_RUNNER_IDLE_STOP_MS to override the window, or 0 to disable idle stop and retain until daemon exit.
288
- Daemon lifetime and stale lease takeover: each AGENT_DEVICE_STATE_DIR runs its own daemon. It self-exits after an idle window (default 5 minutes, matching the runner idle-stop default) once it has no open sessions, no in-flight requests, and no active recording; an open session always blocks this even if quiet for minutes between commands. Set AGENT_DEVICE_DAEMON_IDLE_TIMEOUT_MS to override the window, or 0 to disable idle reap and run until killed. A stale iOS runner lease — its owner process dead, or its AGENT_DEVICE_STATE_DIR deleted — is reclaimed automatically instead of failing with "is already owned by another agent-device daemon"; a genuinely live owner whose state dir still exists still rejects with that error.
289
- Ref lifetime: refs from a snapshot/snapshot -i are only valid until the next state-changing command. open and open --relaunch clear the session's stored snapshot outright, so every ref from before an open/relaunch is invalid; press/fill/click/scroll/back and similar mutations invalidate refs from prior turns even though the session snapshot itself is refreshed internally by those commands.
290
- Snapshot diff: diff snapshot compares the current capture against the session's last stored snapshot (from any prior snapshot, snapshot -i, or diff snapshot call), not a fixed baseline from session start. If no prior snapshot exists yet, diff snapshot initializes the baseline and reports zero additions/removals instead of failing.
291
- Wait: wait text|selector|@ref polls on a fixed interval (300ms) up to a timeout (10s default, override with the trailing timeoutMs positional) by re-capturing state each poll; it does not push/subscribe. Timing out raises a command failure rather than returning a not-found result.
161
+ React Native: help react-native for Metro/Re.Pack reload, DevTools, RN overlays. JS-only change: metro reload, find "Home"; open --relaunch for native reset.
162
+
163
+ Lifecycle facts (trust these instead of probing): open without --relaunch is idempotent-foreground; --relaunch restarts it. close keeps a healthy iOS runner warm by default; runners and daemons both self-idle after 5 minutes, and a stale lease reclaims automatically -- a live owner still rejects with "already owned by another agent-device daemon". Env vars: help physical-device.
292
164
 
293
165
  Escalate:
294
- help manual-qa scripted manual QA and acceptance checks
295
- help dogfood exploratory QA report workflow
296
- help validate engineering self-validation loops
297
- help debugging logs, network, alerts, traces, flaky runtime failures
298
- help tv Android TV, tvOS, and Vega VVD focus-first remote navigation
299
- help react-devtools React Native performance, profiling, props/state/hooks, slow renders, rerenders
300
- help react-native React Native app automation hazards, overlays, Metro/Re.Pack, and routing
301
- help remote remote/cloud config, tenant, lease, local service tunnels
302
- help macos desktop, frontmost-app, menu bar surfaces
303
- help ios-system-ui iOS SpringBoard, widget add/edit/remove, system-UI surfaces`},tv:{summary:`Android TV, tvOS, and Vega VVD focus-first remote navigation`,body:`agent-device help tv
166
+ help manual-qa scripted manual QA
167
+ help dogfood exploratory QA report
168
+ help validate engineering self-validation
169
+ help debugging logs, network, alerts, traces, text-entry
170
+ help scripting recording, save-script, batch, replay repair
171
+ help gestures multi-touch gesture shapes/quirks
172
+ help tv Android TV, tvOS, Vega VVD remote
173
+ help react-devtools RN perf/profiling, hooks, renders
174
+ help react-native RN hazards, Metro/Re.Pack, routing
175
+ help remote remote/cloud config, lease, tunnels
176
+ help macos desktop, frontmost-app, menu bar
177
+ help web minimal browser loop
178
+ help ios-system-ui SpringBoard, widget, system-UI`},scripting:{summary:`Reusable scripts, secret-safe fills, batch JSON, and replay repair`,body:`agent-device help scripting
179
+
180
+ Use this for reusable .ad script authoring (save-script), scripted destination guards, secret-safe fills, batch multi-step JSON, replay divergence/repair, and evidence recording.
181
+
182
+ Reusable open-to-destination scripts:
183
+ Arm recording on the first open, perform the full journey, verify the destination with a selector-targeted wait, then publish without closing:
184
+ agent-device open com.example.app --relaunch --save-script=screen-x.ad
185
+ agent-device press 'id="continue"' --settle
186
+ agent-device wait 'role="heading" label="Screen X"'
187
+ agent-device session save-script
188
+ session save-script [path] [--force] publishes the sole recorded open through the destination guard, omits close, and leaves the session active. The guard is a selector wait on a labeled/id-bearing landmark: its identity is captured while armed and re-verified after the wait resolves at replay time, so a reshuffled screen with the same label elsewhere fails closed instead of false-passing. A duration wait, wait stable, wait @ref, or a selector wait on an unlabeled element is not a destination guard. A second successful open aborts publication; start a fresh session to author again.
189
+ Unparameterized fill/type inputs are literal .ad script content. For a sensitive fill, arm recording first, keep the live value in an env var, and name its replay placeholder explicitly:
190
+ export AD_VAR_PASSWORD='<secret>'
191
+ agent-device fill 'id="password"' "$AD_VAR_PASSWORD" --record-as PASSWORD
192
+ The live app receives the value; recording state and the published script contain only \${PASSWORD}. Reuse the same name for repeated values; --record-as is fill-only, requires an armed recording, and is mutually exclusive with --no-record. Replay with AD_VAR_PASSWORD still set, or pass --env PASSWORD=<value>. Do not record passwords/tokens without --record-as; their literal text is written to the .ad target.
193
+
194
+ Replay divergence and repair:
195
+ A failing replay/test step returns REPLAY_DIVERGENCE with a bounded report (screen digest, ranked selector suggestions, resume). Fix app state, then resume with replay --from <n> --plan-digest <sha256> (both from the report's resume field) to continue without re-running earlier steps; resume never re-executes skipped steps, so app state there is the caller's responsibility. --from is replay-only; test rejects it. The digest binds the script, includes, effective --platform/--target, and per-action runtime/identity; native .ad interpolation is late-bound so changing only its values keeps the digest, while Maestro environment substitution can change it.
196
+ Native .ad session takeover: replay <file>.ad --keep-session suppresses exactly an authored terminal close and returns the surviving session for continued commands. --update/-u is a no-op (ADR 0012); every divergence already carries the same ranked suggestions.
197
+ Agent-supervised repair: arm replay <file>.ad --save-script[=<out>] before step 1 (armed once; --from continuations do not need it again). Every divergence carries a repairHint: record-and-heal means press the correct control via a blessed @ref from the divergence's screen.refs, recorded, then replay --from <n+1> --plan-digest <sha256>; state-repair means the script is correct but app state is not -- fix state with --no-record actions, then replay --from <n> to re-run the unchanged step; caution means a blind re-press may repeat the mistake; manual means no safe automated repair could be proven. Read-only inspection you run to locate the repair target (snapshot -i, get attrs, find, is) is excluded from the healed script by default; pass --record on a read step you want kept. End the repair with close --save-script[=<out>] (default <stem>.healed.ad); review its diff before promoting it over the original. Running close --save-script before a required resume aborts the repair with no script written.
198
+
199
+ Batch:
200
+ agent-device batch --steps '[{"command":"open","input":{"app":"settings"}},{"command":"wait","input":{"kind":"duration","durationMs":100}}]'
201
+ Step keys are command, input, and optional runtime; put command arguments inside input using the same fields as the MCP/Node command. Legacy positionals/flags steps still work with a deprecation warning. Never use args, step positionals, or flags in new batch JSON.
202
+ Maestro full-suite validation on connected devices uses one test command with a comma-separated --device list and --shard-all (--shard-split only to split suite entries across devices):
203
+ agent-device test ./e2e/maestro --maestro --device udid1,emulator-5554 --shard-all 2
204
+
205
+ Recording:
206
+ record start/stop. Default scope is app (needs an active open session); use --scope device/system for whole-screen capture spanning multiple apps/home/settings. --quality medium|high on Android and Apple targets. stop burns touch overlays into the video by default; --hide-touches skips that for the fastest raw recording, and is recommended for gesture-heavy iOS simulator proof videos since overlay timing depends on a stable runner session. Android adb screenrecord has a 180s limit, so long Android recordings return as multiple MP4 chunks while the daemon stays alive; after a daemon restart, record stop recovers only manifest-owned chunks.
207
+ Tracing: trace start ./trace.log, trace stop ./trace.log (path is positional, not --path).`},gestures:{summary:`Full multi-touch gesture shapes and platform quirks`,body:`agent-device help gestures
208
+
209
+ Full command shapes and platform quirks for touch/pointer gestures beyond scroll/swipe. Read this when a task needs multi-touch, a repeated gesture series, or exact per-platform verification.
210
+
211
+ Shapes:
212
+ agent-device longpress 300 500 800
213
+ agent-device longpress @e12 800
214
+ agent-device swipe 320 500 40 500 --count 8 --pause-ms 30 --pattern ping-pong
215
+ agent-device gesture pan 200 420 0 -80 500
216
+ agent-device gesture pan 200 420 80 -40 700 --pointer-count 2
217
+ agent-device gesture fling right 200 420 180
218
+ agent-device gesture pinch 0.5 200 400
219
+ agent-device gesture rotate 35 200 420
220
+ agent-device gesture transform 200 420 80 -40 2 35 700
221
+ longpress accepts coordinates, @refs, or selectors; prefer @ref/selector, coordinates only as a fallback. Duration and gesture scale/center are positional. gesture pan is one finger by default; add --pointer-count 2 for a parallel two-finger pan. Keep count/pause/pattern on one swipe: --count (cap 200), --pause-ms (cap 10000ms), --pattern ping-pong; the combined swipe/pause schedule is capped at 60000ms.
222
+ For repeated iOS smoke checks: press <x> <y> --count <n> --jitter-px <n> for tap series, swipe <x1> <y1> <x2> <y2> --count <n> for drag series.
223
+
224
+ Platform quirks:
225
+ iOS simulator transform/pinch/rotate use private XCTest synthesis for a continuous two-finger pan/scale/rotation path; verify app metrics instead of assuming requested values map exactly to recognizer output.
226
+ Android transform injects a geometric two-finger path; app recognizers may report non-exact pan/scale/rotation -- verify semantic app state or coarse per-component effects instead of exact numeric deltas unless the app exposes stable metrics. If Android needs exact values, prefer isolated gesture pan --pointer-count 2, gesture pinch, or gesture rotate over one combined transform:
227
+ agent-device gesture transform 200 420 80 -40 2 35 700 --platform android
228
+ agent-device wait text "pan changed yes" 3000 --platform android
229
+ tvOS coordinate pan and fling preserve only the dominant direction as a remote swipe; authored endpoints and duration are not preserved.
230
+ Gesture planning prefers the active-app frame; a backend without a gesture viewport resolver falls back to the visible snapshot union, which can be less accurate near edges.
231
+ Rare iOS accessibility gap: a row shown disabled/hittable:false where press reports success but no UI change, or a collapsed composite control with no child refs -- run snapshot -i --json, compute the target center from rects, press x y, then diff snapshot -i. Coordinates are fallback-only; document why you used them.
232
+
233
+ macOS:
234
+ Context menus are secondary clicks, not long presses: agent-device click @e66 --button secondary --platform macos, then snapshot -i.
235
+ For fast desktop list traversal, prefer fixed pixel wheel steps and batch them when no snapshot is needed between passes:
236
+ agent-device scroll down --pixels 200 --duration-ms 50 --platform macos
237
+ agent-device batch --steps '[{"command":"scroll","input":{"direction":"down","pixels":200,"durationMs":50}},{"command":"scroll","input":{"direction":"down","pixels":200,"durationMs":50}}]' --platform macos`},tv:{summary:`Android TV, tvOS, and Vega VVD focus-first remote navigation`,body:`agent-device help tv
304
238
 
305
239
  Use this when the target is Android TV, Apple TV/tvOS, or an Amazon Vega OS TV app running in the Vega Virtual Device (VVD). TV surfaces are focus-first: move focus with remote/D-pad buttons, then activate the focused control.
306
240
 
@@ -463,6 +397,10 @@ Stabilizers:
463
397
  agent-device settings animations on
464
398
  Re-enable settings you changed before finishing.
465
399
 
400
+ Text-entry quirks:
401
+ iOS Allow Paste cannot be exercised under XCUITest; prefill with clipboard write "some text" instead and test the system prompt manually.
402
+ Android Gboard handwriting/stylus UI can capture text in an IME-owned input instead of the app field. If fill reports that input was captured by the keyboard/IME, use the diagnostic targetInput/actualInput details, inspect keyboard status/get if needed, and switch or disable handwriting outside the command plan before retrying. Do not keep retrying fill/type against the same field while the IME owns focus. If the exact target changes but app-owned formatting prevents raw equality, fill succeeds with verification: "unconfirmed" plus target-bound requested/before/after evidence; inspect that evidence instead of retrying the same mutation.
403
+
466
404
  React Native internals:
467
405
  If the question is about React Native performance, profiling, props, state, hooks, render causes, slow components, or rerenders, use help react-devtools instead of inferring from screenshots or logs.`},"react-devtools":{summary:`React Native performance, profiling, and component internals`,body:`agent-device help react-devtools
468
406
 
@@ -682,6 +620,12 @@ Android physical-device prerequisites:
682
620
  Enable USB debugging and confirm the device appears in agent-device devices --platform android.
683
621
  Android does not need the iOS runner signing setup. For React Native/Expo Metro reachability, read help react-native.
684
622
 
623
+ Runner and daemon lifecycle (applies to simulators too):
624
+ open without --relaunch is idempotent-foreground for an already-running app (it brings the process forward; it does not restart it). open --relaunch restarts the app; on iOS simulators this collapses to one simctl launch --terminate-running-process call instead of a separate terminate-then-launch.
625
+ close keeps a healthy iOS simulator XCTest runner warm by default so the next open on that device skips the runner build, unless --shutdown was requested, the session was recording, the session held a device lease, or the device used a scoped (non-default) simulator set. A retained runner auto-stops after an idle window (default 5 minutes); set AGENT_DEVICE_IOS_RUNNER_IDLE_STOP_MS to override, or 0 to disable idle stop and retain until daemon exit.
626
+ Each AGENT_DEVICE_STATE_DIR runs its own daemon. It self-exits after an idle window (default 5 minutes, matching the runner idle-stop default) once it has no open sessions, no in-flight requests, and no active recording; set AGENT_DEVICE_DAEMON_IDLE_TIMEOUT_MS to override, or 0 to disable idle reap.
627
+ A stale iOS runner lease — its owner process dead, or its AGENT_DEVICE_STATE_DIR deleted — is reclaimed automatically instead of failing with "is already owned by another agent-device daemon"; a genuinely live owner whose state dir still exists still rejects with that error.
628
+
685
629
  For iOS SpringBoard, widget, or other system-UI surfaces, read agent-device help ios-system-ui.`},"ios-system-ui":{summary:`iOS SpringBoard, widget, and system-surface workflow`,body:`agent-device help ios-system-ui
686
630
 
687
631
  Use this when a task needs iOS SpringBoard (home screen), widget add/edit/remove, or other system-UI surfaces instead of the app under test.
@@ -725,7 +669,7 @@ After direct-provider connect:
725
669
  Read the printed Device, App, Next, and workflow-note lines. They are also available as verification/device/app/liveSession/nextSteps/notes in --json output.
726
670
  BrowserStack and AWS Device Farm create the hosted session on open. open needs the installed package or bundle identifier, not the app artifact name or ARN.
727
671
  A new Limrun instance has no user app. Run install <package-or-bundle-id> <app-path-or-url> first; install allocates the instance, then open launches the installed id.
728
- AWS Device Farm cannot install after allocation. If connect reports no attached app, reconnect with --aws-app-arn <arn> --force before open.
672
+ AWS Device Farm cannot install after allocation. If connect reports no attached app, run its printed reconnect command, which includes --session <name> --force, before open.
729
673
  Do not run devices or apps as a pre-open catalog probe for direct providers; those commands can allocate the deferred provider session and only inspect that live device.
730
674
 
731
675
  Device cloud interfaces:
@@ -800,6 +744,8 @@ Rules:
800
744
  Use Limrun, BrowserStack, and AWS Device Farm through local provider profiles; they do not accept a remote agent-device daemon URL.
801
745
  Device cloud credentials must be available before the command starts. Limrun uses LIMRUN_API_KEY. BrowserStack uses BROWSERSTACK_USERNAME and BROWSERSTACK_ACCESS_KEY. AWS Device Farm uses the AWS CLI credential chain, including CI-provided AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY/AWS_SESSION_TOKEN, AWS profiles, or web identity role variables.
802
746
  Direct-provider connect performs read-only provider calls and saves active connection state only after verification succeeds. It never creates a device, instance, App Automate session, or AWS remote access session.
747
+ connect without --session always creates a fresh remote session and prints that session in its next-step commands. Concurrent callers must pass the returned --session on every command; the ambient active connection is only a single-workflow convenience.
748
+ To replace an existing connection, pass its returned session explicitly with --session <name> --force. --force without --session creates another fresh session and does not release or overwrite an unrelated active connection.
803
749
  Prefer short-lived AWS role credentials in CI. Generated connection profiles store app/device selectors and ARNs, not Limrun API keys, BrowserStack access keys, or AWS credentials.
804
750
  Limrun Android supports direct ADB port reverse for local Metro. Limrun iOS requires a public Metro/React DevTools URL because it cannot reach local host ports directly.
805
751
  After closing a device cloud session, run agent-device artifacts --json to retrieve provider video/log/dashboard URLs when the provider has made them available.
@@ -959,6 +905,7 @@ Required freshness gate before device verification:
959
905
  Before local Android verification, run pnpm build:android before pnpm clean:daemon so the bundled helpers match current source.
960
906
  For an Apple runner change, run pnpm build:xcuitest and avoid inherited retained runners from older source. Do not build the Apple runner for TypeScript-only changes.
961
907
  Use open --relaunch when startup state matters. Use a purpose-specific --session for multi-step validation.
908
+ CI may cache ~/.agent-device/apple-runner/derived with an exact key that includes the agent-device package and Xcode version. Avoid broad restore-key fallbacks; prepare ios-runner already recovers bad restored runner artifacts and one retryable non-connecting runner launch.
962
909
 
963
910
  Loop:
964
911
  1. Build or prepare the changed surface with the repo command that owns it.
@@ -979,38 +926,31 @@ Evidence:
979
926
 
980
927
  Report:
981
928
  Summarize what changed, exact validation commands, pass/fail observations, artifact paths, and residual risk.
982
- If live validation is blocked, state the blocker, device/session, and exact next command needed.`}};function b(e,t,n){let r=n.endsWith(`?`),i=r?n.slice(0,-1):n,a=/^[a-z-]+(?:\|[a-z-]+)+$/i.test(i),o=a||t.usageOverride!==void 0&&t.usageOverride.startsWith(`${e} ${i}`);return r?a?`[${i}]`:o?i:`[${i}]`:o?i:`<${i}>`}function x(e,t){return t.listUsageOverride?t.listUsageOverride:[e,...(t.positionalArgs??[]).map(n=>b(e,t,n))].join(` `)}function S(){let t=k(e().map(e=>{let t=a(e);return{name:e,schema:t,usage:x(e,t)}})),r=E(`Global Flags:`,w(n)),i=O(`Agent Starting Point:`,p),o=O(`Agent Quickstart:`,m);return`agent-device <command> [args] [--json]
983
-
984
- CLI to automate supported app, device, desktop, and web targets for AI agents.
985
-
986
- ${i}
987
-
988
- ${D(`Agent Workflows:`,f)}
929
+ If live validation is blocked, state the blocker, device/session, and exact next command needed.`}};function b(e,t,n){let r=n.endsWith(`?`),i=r?n.slice(0,-1):n,a=/^[a-z-]+(?:\|[a-z-]+)+$/i.test(i),o=a||t.usageOverride!==void 0&&t.usageOverride.startsWith(`${e} ${i}`);return r?a?`[${i}]`:o?i:`[${i}]`:o?i:`<${i}>`}function x(e,t){return t.listUsageOverride?t.listUsageOverride:[e,...(t.positionalArgs??[]).map(n=>b(e,t,n))].join(` `)}function S(){return m()}function C(){return`agent-device help commands
989
930
 
990
- ${t}
931
+ Full command catalog. Use agent-device help <command> for exact flags and behavior.
991
932
 
992
- ${r}
933
+ ${A(n().map(e=>{let t=s(e);return{name:e,schema:t,usage:x(e,t)}}))}
993
934
 
994
- ${o}
935
+ ${D(`Global Flags:`,T(i))}
995
936
 
996
- ${O(`Configuration:`,h)}
937
+ ${k(`Configuration:`,h)}
997
938
 
998
- ${D(`Environment:`,g)}
939
+ ${O(`Environment:`,g)}
999
940
 
1000
- ${O(`Examples:`,_)}
1001
- `}function C(){return S()}function w(e){return r().filter(t=>e.has(t.key)&&t.usageLabel!==void 0&&t.usageDescription!==void 0)}function T(e,t){return t?e.map(e=>{let n=t[e.key];return n===void 0?e:{...e,usageDescription:n}}):e}function E(e,t){return D(e,t.map(e=>({label:e.usageLabel??``,description:e.usageDescription??``})))}function D(e,t){if(t.length===0)return`${e}\n (none)`;let n=Math.max(...t.map(e=>Math.min(e.label.length,26)))+2,r=[e];for(let e of t){let t=e.label.length<=26?e.label.padEnd(n):`${e.label} `;r.push(` ${t}${e.description}`)}return r.join(`
1002
- `)}function O(e,t){return t.length===0?`${e}\n (none)`:[e,...t.map(e=>` ${e}`)].join(`
1003
- `)}function k(e){return D(`Commands:`,e.map(e=>({label:e.usage,description:e.schema.text.summary})))}function A(e){let n=j(e);if(n)return n;let r=i(e);if(!r)return null;let a=d(e,r),o=T(w(new Set(r.allowedFlags??[])),r.flagDescriptionOverrides),s=[];o.length>0&&s.push(E(`Command flags:`,o));let c=s.length>0?`\n\n${s.join(`
941
+ ${k(`Examples:`,_)}
942
+ `}function w(){return S()}function T(e){return r().filter(t=>e.has(t.key)&&t.usageLabel!==void 0&&t.usageDescription!==void 0)}function E(e,t){return t?e.map(e=>{let n=t[e.key];return n===void 0?e:{...e,usageDescription:n}}):e}function D(e,t){return O(e,t.map(e=>({label:e.usageLabel??``,description:e.usageDescription??``})))}function O(e,t){if(t.length===0)return`${e}\n (none)`;let n=Math.max(...t.map(e=>Math.min(e.label.length,26)))+2,r=[e];for(let e of t){let t=e.label.length<=26?e.label.padEnd(n):`${e.label} `;r.push(` ${t}${e.description}`)}return r.join(`
943
+ `)}function k(e,t){return t.length===0?`${e}\n (none)`:[e,...t.map(e=>` ${e}`)].join(`
944
+ `)}function A(e){return O(`Commands:`,e.map(e=>({label:e.usage,description:e.schema.text.summary})))}function j(e){let t=M(e);if(t)return t;let n=o(e);if(!n)return null;let r=p(e,n),i=E(T(new Set(n.allowedFlags??[])),n.flagDescriptionOverrides),s=[];i.length>0&&s.push(D(`Command flags:`,i));let c=s.length>0?`\n\n${s.join(`
1004
945
 
1005
946
  `)}`:``;return`Usage:
1006
- agent-device ${a}
947
+ agent-device ${r}
1007
948
 
1008
- ${t(r.text)}${c}
1009
- `}function j(e){let t=y[e];return t?`${t.body}
949
+ ${a(n.text)}${c}
950
+ `}function M(e){let t=y[e];return t?`${N(e,e===`commands`?C():t.body)}
1010
951
 
1011
952
  Related:
1012
- agent-device help command list and global flags
1013
- agent-device help <command> command-specific flags
1014
- agent-device help manual-qa routine QA loop with concrete command shapes
1015
- agent-device help workflow full app automation reference
1016
- `:null}export{A as buildCommandUsageText,C as buildUsageText};
953
+ agent-device help <command> command-specific flags
954
+ agent-device help manual-qa routine QA loop
955
+ agent-device help workflow full automation reference
956
+ `:null}function N(t,n){let r=`agent-device help ${t}`;return n.startsWith(r)?`agent-device ${e()} — ${t}${n.slice(r.length)}`:n}export{j as buildCommandUsageText,w as buildUsageText};