agent-device 0.20.10 → 0.21.1

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 (510) hide show
  1. package/README.md +2 -2
  2. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.20.10.apk → agent-device-android-ime-helper-0.21.1.apk} +0 -0
  3. package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.1.apk.sha256 +1 -0
  4. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.20.10.manifest.json → agent-device-android-ime-helper-0.21.1.manifest.json} +4 -4
  5. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.1.apk +0 -0
  6. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.1.apk.sha256 +1 -0
  7. package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.20.10.manifest.json → agent-device-android-snapshot-helper-0.21.1.manifest.json} +6 -6
  8. package/apple/macos-helper/Sources/AgentDeviceMacOSHelper/AudioProbe.swift +35 -33
  9. package/apple/macos-helper/Sources/AgentDeviceMacOSHelper/main.swift +6 -1
  10. package/apple/snapshot-bridge/LICENSE.idb +23 -0
  11. package/apple/snapshot-bridge/README.md +106 -0
  12. package/apple/snapshot-bridge/SnapshotBridge.m +286 -0
  13. package/apple/snapshot-bridge/SnapshotBridgeCapture.h +25 -0
  14. package/apple/snapshot-bridge/SnapshotBridgeCapture.m +138 -0
  15. package/apple/snapshot-bridge/SnapshotBridgeRuntime.h +34 -0
  16. package/apple/snapshot-bridge/SnapshotBridgeRuntime.m +411 -0
  17. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunner/AgentDeviceRunnerApp.m +58 -0
  18. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/project.pbxproj +19 -0
  19. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.h +12 -0
  20. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.m +20 -0
  21. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAlertVerification.swift +36 -0
  22. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSelectorMatchPolicy.swift +15 -15
  23. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerStringNormalization.swift +8 -0
  24. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.h +12 -1
  25. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.m +100 -6
  26. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSystemSurfaceHostPolicy.swift +72 -0
  27. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTapPointPolicy.swift +63 -14
  28. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+AXSnapshotFallback.swift +677 -83
  29. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Alert.swift +35 -92
  30. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+AlertObservation.swift +124 -0
  31. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+BlockingSystemModalResolution.swift +39 -4
  32. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +1058 -213
  33. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandJournal.swift +296 -14
  34. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Environment.swift +1 -1
  35. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Exceptions.swift +10 -10
  36. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +309 -149
  37. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Keyboard.swift +101 -42
  38. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Lifecycle.swift +24 -17
  39. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +124 -199
  40. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Navigation.swift +71 -4
  41. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+PrivateAXElementTypes.swift +12 -0
  42. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+PrivateAXPresentation.swift +211 -78
  43. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScreenRecorder.swift +30 -10
  44. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScrollGesture.swift +254 -24
  45. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SequenceExecution.swift +206 -22
  46. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +465 -494
  47. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotBackendCapabilities.swift +168 -0
  48. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCapturePlan.swift +802 -247
  49. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotPresentationInvariant.swift +19 -0
  50. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotTiming.swift +185 -0
  51. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedCommitDeadline.swift +206 -0
  52. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedGesturePolicy.swift +83 -5
  53. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedTextEntry.swift +156 -34
  54. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SystemModal.swift +5 -4
  55. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntry.swift +35 -318
  56. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntryFocus.swift +206 -0
  57. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntryReadiness.swift +158 -0
  58. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextInputCandidatePolicy.swift +28 -0
  59. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextInputProbe.swift +101 -0
  60. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextTyping.swift +56 -23
  61. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Transport.swift +56 -27
  62. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TvRemote.swift +30 -24
  63. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +126 -80
  64. package/dist/apple/runner/AgentDeviceRunner/RecordingScripts/RecordingExportSupport.swift +111 -0
  65. package/dist/apple/runner/AgentDeviceRunner/RecordingScripts/recording-overlay.swift +47 -86
  66. package/dist/apple/runner/AgentDeviceRunner/RecordingScripts/recording-trim.swift +32 -79
  67. package/dist/apple/snapshot-presentation/Package.swift +21 -0
  68. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotGeometry.swift +56 -0
  69. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotModels.swift +259 -0
  70. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotPresentation.swift +153 -0
  71. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotPresentationFailure.swift +27 -0
  72. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotPresentationInvariant.swift +91 -0
  73. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotPresentationProjection.swift +161 -0
  74. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotScopePolicy.swift +53 -0
  75. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotVisibilityFold.swift +242 -0
  76. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotVisibilityFoldProjection.swift +97 -0
  77. package/dist/src/adapter.js +1 -0
  78. package/dist/src/adb-executor.js +2 -0
  79. package/dist/src/adb-failure.js +1 -0
  80. package/dist/src/adb-host.js +1 -0
  81. package/dist/src/adb.js +1 -0
  82. package/dist/src/agent-browser-lifecycle.js +1 -0
  83. package/dist/src/agent-browser-process-record.js +1 -0
  84. package/dist/src/agent-browser-provider.js +1 -0
  85. package/dist/src/agent-browser-tool.js +1 -1
  86. package/dist/src/agent-device-client.js +2 -2
  87. package/dist/src/ai-sdk.d.ts +2 -2
  88. package/dist/src/ai-sdk.js +1 -1
  89. package/dist/src/alert-contract.js +1 -0
  90. package/dist/src/alert-runtime.js +1 -0
  91. package/dist/src/android-adb.d.ts +27 -19
  92. package/dist/src/android-adb.js +1 -1
  93. package/dist/src/android-input-ownership.js +1 -0
  94. package/dist/src/android-system-chrome.js +1 -0
  95. package/dist/src/android.js +1 -1
  96. package/dist/src/api.js +11 -0
  97. package/dist/src/app-catalog.js +1 -0
  98. package/dist/src/app-deployment-resolution.js +2 -0
  99. package/dist/src/app-helpers.js +1 -1
  100. package/dist/src/app-inventory-contract.js +1 -0
  101. package/dist/src/app-inventory.js +1 -0
  102. package/dist/src/app-launch.js +2 -0
  103. package/dist/src/app-lifecycle-facade.js +1 -0
  104. package/dist/src/app-lifecycle.js +3 -6
  105. package/dist/src/app-lifecycle2.js +2 -2
  106. package/dist/src/app-lifecycle3.js +1 -0
  107. package/dist/src/app-log-files.js +1 -1
  108. package/dist/src/app-log-reconnect.js +1 -1
  109. package/dist/src/app-log-runtime.js +1 -4
  110. package/dist/src/app-log-runtime2.js +4 -0
  111. package/dist/src/app-parsers.js +4 -0
  112. package/dist/src/app-resolution-cache.js +1 -0
  113. package/dist/src/app-resolution-facade.js +1 -0
  114. package/dist/src/app-resolution.js +2 -2
  115. package/dist/src/app-state.js +1 -2
  116. package/dist/src/apple-runner-platform.js +1 -0
  117. package/dist/src/application-lifecycle-interaction.js +1 -0
  118. package/dist/src/application-lifecycle-runtime.js +1 -0
  119. package/dist/src/apps-simctl.js +1 -0
  120. package/dist/src/archive.js +4 -0
  121. package/dist/src/artifact-download.js +1 -0
  122. package/dist/src/artifacts.d.ts +6 -2
  123. package/dist/src/artifacts.js +1 -1
  124. package/dist/src/async-lifecycle.js +1 -0
  125. package/dist/src/audio-probe-result.js +1 -0
  126. package/dist/src/audio-probe-runtime.js +1 -0
  127. package/dist/src/audio-probe-support.js +1 -0
  128. package/dist/src/auth-session.js +1 -1
  129. package/dist/src/auth.js +1 -1
  130. package/dist/src/backend-snapshot-options.js +1 -1
  131. package/dist/src/{url.js → base-url.js} +1 -1
  132. package/dist/src/boot-diagnostics.js +2 -2
  133. package/dist/src/byte-limit-stream.js +1 -1
  134. package/dist/src/capabilities.js +1 -0
  135. package/dist/src/capture.js +1 -0
  136. package/dist/src/catalog.js +1 -0
  137. package/dist/src/cli-command-aliases.js +1 -0
  138. package/dist/src/cli-config.js +1 -1
  139. package/dist/src/cli-help.js +74 -51
  140. package/dist/src/cli-output.js +1 -1
  141. package/dist/src/cli-runner.js +1 -1
  142. package/dist/src/cli.js +4 -6
  143. package/dist/src/click-button.js +1 -0
  144. package/dist/src/client-metro-companion.js +1 -0
  145. package/dist/src/client-metro.js +1 -1
  146. package/dist/src/client-types.d.ts +280 -242
  147. package/dist/src/clipboard.js +1 -0
  148. package/dist/src/code-signature-cache.js +1 -0
  149. package/dist/src/code-signature.js +1 -0
  150. package/dist/src/color.js +1 -0
  151. package/dist/src/command-metadata.js +1 -1
  152. package/dist/src/command-schema.js +3 -3
  153. package/dist/src/command-surface.js +1 -1
  154. package/dist/src/command-tools.js +2 -2
  155. package/dist/src/command.js +1 -0
  156. package/dist/src/compatibility-policy.js +1 -0
  157. package/dist/src/config.js +1 -1
  158. package/dist/src/config2.js +1 -0
  159. package/dist/src/connection-runtime.js +1 -1
  160. package/dist/src/connection.js +5 -5
  161. package/dist/src/contracts.d.ts +1 -1
  162. package/dist/src/contracts.js +1 -1
  163. package/dist/src/daemon-client-lifecycle.js +1 -0
  164. package/dist/src/daemon-process.js +1 -0
  165. package/dist/src/daemon-registration.js +1 -0
  166. package/dist/src/daemon-shutdown-report.js +1 -1
  167. package/dist/src/daemon.js +2 -2
  168. package/dist/src/debug-symbols-facade.js +5 -0
  169. package/dist/src/deploy-result-target.js +1 -0
  170. package/dist/src/device-claim-inspection.js +1 -1
  171. package/dist/src/device-claim-rule.js +1 -0
  172. package/dist/src/device-input-state.d.ts +190 -3
  173. package/dist/src/device-inventory-context.js +1 -0
  174. package/dist/src/device-isolation.js +1 -0
  175. package/dist/src/device-release.js +1 -0
  176. package/dist/src/device-rotation.js +1 -0
  177. package/dist/src/device-selection-resolver.js +1 -0
  178. package/dist/src/device-session.js +1 -0
  179. package/dist/src/device.js +1 -1
  180. package/dist/src/device2.js +3 -2
  181. package/dist/src/diagnostics.js +3 -0
  182. package/dist/src/diagnostics2.js +1 -0
  183. package/dist/src/dispatch-resolve.js +1 -0
  184. package/dist/src/doctor-facade.js +1 -0
  185. package/dist/src/doctor-output.js +1 -0
  186. package/dist/src/doctor.js +1 -0
  187. package/dist/src/doctor2.js +1 -0
  188. package/dist/src/doctor3.js +1 -0
  189. package/dist/src/durable-resource.js +1 -0
  190. package/dist/src/entities.js +1 -1
  191. package/dist/src/error.js +2 -0
  192. package/dist/src/exec.js +1 -0
  193. package/dist/src/file.js +1 -0
  194. package/dist/src/find.js +1 -1
  195. package/dist/src/finders.js +1 -1
  196. package/dist/src/generic-settle.js +1 -1
  197. package/dist/src/generic.js +1 -1
  198. package/dist/src/gesture-admission.js +1 -0
  199. package/dist/src/gesture-plan-types.js +1 -0
  200. package/dist/src/gesture-plan.js +1 -0
  201. package/dist/src/gesture-runtime.js +1 -0
  202. package/dist/src/harmonyos.js +1 -1
  203. package/dist/src/hdc.js +1 -1
  204. package/dist/src/helper.js +2 -2
  205. package/dist/src/host-file.js +1 -0
  206. package/dist/src/human-control-contract.js +1 -0
  207. package/dist/src/human-control.js +1 -0
  208. package/dist/src/index.d.ts +1 -1
  209. package/dist/src/index.js +1 -1
  210. package/dist/src/input-actions.js +1 -4
  211. package/dist/src/input-actions2.js +2 -1
  212. package/dist/src/install-artifact-facade.js +1 -0
  213. package/dist/src/install-source-network-transport.js +1 -0
  214. package/dist/src/install-source.d.ts +3 -10
  215. package/dist/src/install-source2.js +1 -1
  216. package/dist/src/interaction-positionals.js +1 -0
  217. package/dist/src/interaction-ref-policy.js +1 -0
  218. package/dist/src/interaction.js +1 -1
  219. package/dist/src/interaction2.js +1 -1
  220. package/dist/src/interactor-operation-catalog.js +1 -0
  221. package/dist/src/interactor-types.js +1 -0
  222. package/dist/src/interactor.js +6 -2
  223. package/dist/src/interactor2.js +1 -1
  224. package/dist/src/interactors.js +1 -1
  225. package/dist/src/internal/bin.d.ts +1 -1
  226. package/dist/src/internal/bin.js +1 -2
  227. package/dist/src/internal/companion-tunnel.d.ts +1 -1
  228. package/dist/src/internal/companion-tunnel.js +1 -1
  229. package/dist/src/internal/daemon.d.ts +1 -1
  230. package/dist/src/internal/daemon.js +2 -4
  231. package/dist/src/internal/png-worker.d.ts +4 -4
  232. package/dist/src/internal/png-worker.js +1 -1
  233. package/dist/src/internal/run-script-http-child.d.ts +4 -0
  234. package/dist/src/internal/run-script-http-child.js +1 -0
  235. package/dist/src/internal/update-check-entry.d.ts +1 -1
  236. package/dist/src/inventory.js +4 -4
  237. package/dist/src/inventory2.js +1 -1
  238. package/dist/src/inventory3.js +1 -1
  239. package/dist/src/inventory5.js +1 -1
  240. package/dist/src/inventory6.js +1 -1
  241. package/dist/src/ios-client.js +16 -0
  242. package/dist/src/ios-snapshot-acquisition.js +1 -0
  243. package/dist/src/ios-snapshot-adapter.js +1 -0
  244. package/dist/src/ios-snapshot-planning.js +1 -0
  245. package/dist/src/ios-snapshot-runtime.js +1 -0
  246. package/dist/src/ios-system-surface.js +1 -0
  247. package/dist/src/json.js +1 -0
  248. package/dist/src/keyboard-actions.js +1 -0
  249. package/dist/src/launch-console.js +1 -0
  250. package/dist/src/lease-context.js +1 -1
  251. package/dist/src/lease-scope.js +1 -0
  252. package/dist/src/lease.js +1 -1
  253. package/dist/src/limrun-runtime-dependencies.js +1 -1
  254. package/dist/src/limrun.d.ts +1610 -7
  255. package/dist/src/limrun.js +1 -1
  256. package/dist/src/linux.js +1 -1
  257. package/dist/src/local-interactor-operation-set.js +1 -0
  258. package/dist/src/location-coordinates.js +1 -1
  259. package/dist/src/logcat.js +1 -1
  260. package/dist/src/macos-facade.js +1 -0
  261. package/dist/src/managed-device-scope.js +1 -0
  262. package/dist/src/mechanics.js +19 -0
  263. package/dist/src/metro.d.ts +4 -2
  264. package/dist/src/metro.js +1 -1
  265. package/dist/src/metro2.js +1 -1
  266. package/dist/src/mobile-snapshot-semantics.js +1 -1
  267. package/dist/src/multitouch-support.js +1 -0
  268. package/dist/src/numeric.js +1 -0
  269. package/dist/src/observability.js +1 -1
  270. package/dist/src/observation.js +2 -0
  271. package/dist/src/open-target-resolution.js +1 -0
  272. package/dist/src/output-path.js +1 -1
  273. package/dist/src/owner-identity.js +3 -1
  274. package/dist/src/parameterized-recorded-fill.js +1 -0
  275. package/dist/src/parser.js +1 -1
  276. package/dist/src/perf-facade.js +2 -0
  277. package/dist/src/perf-process-identity.js +1 -0
  278. package/dist/src/perf-runtime-operation-builder.js +1 -0
  279. package/dist/src/perf-runtime-plan.js +1 -0
  280. package/dist/src/perf-runtime.js +1 -0
  281. package/dist/src/perf.js +1 -4
  282. package/dist/src/physical-device-control.js +1 -1
  283. package/dist/src/physical-device-facade.js +1 -0
  284. package/dist/src/platform-request-scope.js +1 -0
  285. package/dist/src/platform-runtime-android-adb-host.js +1 -0
  286. package/dist/src/platform-runtime-android-mechanics.js +1 -0
  287. package/dist/src/platform-runtime-app-log-android-transport.js +1 -1
  288. package/dist/src/platform-runtime-app-log-process.js +1 -1
  289. package/dist/src/platform-runtime-host.js +1 -1
  290. package/dist/src/platform-runtime-managed-owner.js +1 -0
  291. package/dist/src/platform-runtime-network-web-transport.js +1 -1
  292. package/dist/src/platform-runtime-open-target.js +1 -1
  293. package/dist/src/platform-runtime-operation-host.js +4 -4
  294. package/dist/src/platform-runtime-runtime-hints.js +2 -2
  295. package/dist/src/platform-runtime-screen-recording-android-host.js +1 -1
  296. package/dist/src/platform-runtime-screen-recording-apple-runner-host.js +1 -1
  297. package/dist/src/platform-runtime-screen-recording-apple-runner-transport.js +1 -1
  298. package/dist/src/platform-runtime-screen-recording-apple-simulator-host.js +1 -1
  299. package/dist/src/platform-runtime-screen-recording-apple-transport.js +1 -1
  300. package/dist/src/platform-runtime-screen-recording-finalizer-host.js +1 -1
  301. package/dist/src/platform-runtime-screen-recording-harmony-host.js +1 -1
  302. package/dist/src/platform-runtime-screen-recording-process-host.js +1 -0
  303. package/dist/src/platform-runtime-screen-recording-web-host.js +1 -1
  304. package/dist/src/platform-runtime-unavailable.js +1 -0
  305. package/dist/src/platform-runtime.js +1 -1
  306. package/dist/src/platform-runtime2.js +1 -1
  307. package/dist/src/plist.js +1 -1
  308. package/dist/src/png-worker-contract.js +1 -1
  309. package/dist/src/prepare-kind.js +1 -0
  310. package/dist/src/process-exit.js +1 -1
  311. package/dist/src/process.js +1 -0
  312. package/dist/src/provider-device-runtime.js +1 -0
  313. package/dist/src/provider-session-ownership.js +2 -0
  314. package/dist/src/provider-webdriver.js +1 -1
  315. package/dist/src/provider.js +1 -1
  316. package/dist/src/providers.js +1 -0
  317. package/dist/src/proxy.js +2 -2
  318. package/dist/src/react-native-overlay.js +1 -0
  319. package/dist/src/react-native.js +1 -1
  320. package/dist/src/record-runtime.js +1 -1
  321. package/dist/src/record-trace.js +1 -1
  322. package/dist/src/rect-center.js +1 -0
  323. package/dist/src/register-builtins.js +1 -0
  324. package/dist/src/registry.js +40 -28
  325. package/dist/src/remote-config2.js +1 -1
  326. package/dist/src/replay-divergence.js +2 -0
  327. package/dist/src/replay.js +2 -2
  328. package/dist/src/reporting.js +8 -7
  329. package/dist/src/request-admission.js +1 -0
  330. package/dist/src/request-cancel.js +1 -0
  331. package/dist/src/request-cancellation.js +1 -0
  332. package/dist/src/request-progress-protocol.js +1 -0
  333. package/dist/src/request-providers.js +1 -0
  334. package/dist/src/request-runtime-binding.js +1 -0
  335. package/dist/src/request.js +1 -0
  336. package/dist/src/result-serialization.js +1 -0
  337. package/dist/src/retry.js +1 -0
  338. package/dist/src/rolldown-runtime.js +1 -1
  339. package/dist/src/runner-cache-metadata.js +2 -0
  340. package/dist/src/runner-client.js +1 -1
  341. package/dist/src/runner-disposal.js +1 -1
  342. package/dist/src/runner-operations-facade.js +1 -0
  343. package/dist/src/runner-owner-facade.js +1 -0
  344. package/dist/src/runner-owner-state.js +1 -0
  345. package/dist/src/runner-presentation.js +1 -0
  346. package/dist/src/runner.js +1 -0
  347. package/dist/src/{runtime-transport.js → runtime-transport-hints.js} +1 -1
  348. package/dist/src/runtime.js +3 -3
  349. package/dist/src/runtime2.js +4 -4
  350. package/dist/src/runtime3.js +1 -1
  351. package/dist/src/runtime4.js +5 -3
  352. package/dist/src/runtime5.js +1 -1
  353. package/dist/src/runtime6.js +1 -1
  354. package/dist/src/runtime7.js +1 -1
  355. package/dist/src/runtime8.js +1 -1
  356. package/dist/src/runtime9.js +1 -1
  357. package/dist/src/scoped-provider.js +1 -1
  358. package/dist/src/screen-recording-completion.js +1 -0
  359. package/dist/src/screen-recording-live-handle.js +1 -0
  360. package/dist/src/screen-recording-options.js +1 -0
  361. package/dist/src/screen-recording-resource-recovery.js +1 -0
  362. package/dist/src/screen-recording-runtime.js +1 -0
  363. package/dist/src/screenshot-density.js +1 -1
  364. package/dist/src/screenshot-result.js +1 -6
  365. package/dist/src/screenshot-runtime.js +1 -0
  366. package/dist/src/screenshot.js +1 -1
  367. package/dist/src/screenshot2.js +1 -0
  368. package/dist/src/screenshot3.js +5 -0
  369. package/dist/src/script.js +1 -0
  370. package/dist/src/scroll-command.js +1 -0
  371. package/dist/src/scroll-gesture.js +1 -0
  372. package/dist/src/scroll-indicator.js +1 -0
  373. package/dist/src/sdk-batch-runner.d.ts +23 -6
  374. package/dist/src/sdk-batch-runner.js +2 -3
  375. package/dist/src/sdk-batch.js +2 -0
  376. package/dist/src/sdk-contracts.d.ts +170 -41
  377. package/dist/src/sdk-contracts.js +2 -2
  378. package/dist/src/sdk-device.js +1 -1
  379. package/dist/src/sdk-finders.d.ts +1 -1
  380. package/dist/src/sdk-finders.js +1 -1
  381. package/dist/src/sdk-io.js +1 -1
  382. package/dist/src/sdk-remote-config.d.ts +1 -1
  383. package/dist/src/sdk-remote-config.js +1 -1
  384. package/dist/src/sdk-selectors.d.ts +524 -140
  385. package/dist/src/sdk-selectors.js +1 -1
  386. package/dist/src/selection.js +1 -0
  387. package/dist/src/selector-runtime.js +1 -1
  388. package/dist/src/selectors.js +1 -1
  389. package/dist/src/semantic-index.js +1 -0
  390. package/dist/src/server.js +4 -4
  391. package/dist/src/session-allocation.js +1 -0
  392. package/dist/src/session-artifact-paths.js +1 -0
  393. package/dist/src/session-observation.js +1 -0
  394. package/dist/src/session-paths.js +1 -0
  395. package/dist/src/session-repair-tombstone.js +1 -0
  396. package/dist/src/session-routing.js +1 -0
  397. package/dist/src/session-runtime-admission.js +1 -0
  398. package/dist/src/session-snapshot.js +1 -0
  399. package/dist/src/session-store.js +4 -0
  400. package/dist/src/session-target-evidence.js +1 -1
  401. package/dist/src/session2.js +5 -27
  402. package/dist/src/settings.js +1 -0
  403. package/dist/src/shared.js +2 -2
  404. package/dist/src/simctl-facade.js +1 -0
  405. package/dist/src/simctl.js +1 -1
  406. package/dist/src/simulator-facade.js +1 -0
  407. package/dist/src/simulator.js +1 -1
  408. package/dist/src/snapshot-capture-annotations.js +1 -0
  409. package/dist/src/snapshot-desktop-projection.js +1 -0
  410. package/dist/src/snapshot-diagnostics.js +1 -0
  411. package/dist/src/snapshot-process.js +1 -0
  412. package/dist/src/snapshot-quality-backend-capabilities.js +1 -0
  413. package/dist/src/snapshot-quality-verdict.js +1 -0
  414. package/dist/src/snapshot-runtime-binding.js +1 -0
  415. package/dist/src/snapshot-runtime.js +1 -1
  416. package/dist/src/snapshot-scope.js +1 -1
  417. package/dist/src/snapshot-state.js +1 -0
  418. package/dist/src/snapshot.js +1 -8
  419. package/dist/src/snapshot2.js +1 -1
  420. package/dist/src/snapshot3.js +1 -1
  421. package/dist/src/snapshot4.js +1 -0
  422. package/dist/src/src.js +1 -1
  423. package/dist/src/src2.js +1 -1
  424. package/dist/src/src3.js +1 -2
  425. package/dist/src/src4.js +1 -1
  426. package/dist/src/src5.js +1 -0
  427. package/dist/src/src6.js +1 -0
  428. package/dist/src/src7.js +2 -0
  429. package/dist/src/src8.js +115 -0
  430. package/dist/src/startup-recovery-fence.js +1 -0
  431. package/dist/src/status-markers.js +1 -0
  432. package/dist/src/string-enum.js +1 -1
  433. package/dist/src/success-text.js +1 -0
  434. package/dist/src/surface-snapshot.js +1 -0
  435. package/dist/src/surface-snapshot2.js +1 -0
  436. package/dist/src/takeover.js +5 -0
  437. package/dist/src/tool-provider-facade.js +1 -0
  438. package/dist/src/tool-provider.js +4 -3
  439. package/dist/src/tool-provider2.js +1 -1
  440. package/dist/src/tool-provider3.js +1 -1
  441. package/dist/src/toolchain-probe.js +2 -0
  442. package/dist/src/touch-reference-frame.js +1 -0
  443. package/dist/src/touch-runtime.js +1 -0
  444. package/dist/src/transport.js +3 -0
  445. package/dist/src/tree.js +1 -0
  446. package/dist/src/ttl-memo.js +1 -0
  447. package/dist/src/tv-remote.js +1 -0
  448. package/dist/src/type-text-runtime.js +1 -0
  449. package/dist/src/undici.js +99 -0
  450. package/dist/src/update-check.js +1 -1
  451. package/dist/src/verified-file.js +1 -1
  452. package/dist/src/version.js +1 -1
  453. package/dist/src/video.js +2 -2
  454. package/dist/src/web.js +2 -1
  455. package/dist/src/web2.js +1 -0
  456. package/dist/src/webdriver-ios-snapshot.js +1 -0
  457. package/dist/src/webdriver-source.js +1 -0
  458. package/dist/src/window-state.js +1 -0
  459. package/linux/atspi-dump.py +54 -9
  460. package/package.json +58 -35
  461. package/server.json +9 -3
  462. package/android/ime-helper/dist/agent-device-android-ime-helper-0.20.10.apk.sha256 +0 -1
  463. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.10.apk +0 -0
  464. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.10.apk.sha256 +0 -1
  465. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CoordinateTextEntryTests.swift +0 -4
  466. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+FlatSnapshotFiltering.swift +0 -168
  467. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+LifecycleCacheTests.swift +0 -6
  468. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+RecordingTests.swift +0 -4
  469. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SelectorMatchPolicyTests.swift +0 -4
  470. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotPresentation.swift +0 -183
  471. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotPresentationTests.swift +0 -0
  472. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotTraversalIdentityTests.swift +0 -4
  473. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntryPolicyTests.swift +0 -4
  474. package/dist/src/appearance.js +0 -1
  475. package/dist/src/apps.js +0 -5
  476. package/dist/src/audio-probe.js +0 -1
  477. package/dist/src/cli-grammar.js +0 -1
  478. package/dist/src/command-catalog.js +0 -1
  479. package/dist/src/debug-symbols.js +0 -5
  480. package/dist/src/device-input-state.js +0 -2
  481. package/dist/src/device-ready.js +0 -1
  482. package/dist/src/dispatch.js +0 -1
  483. package/dist/src/handler-utils.js +0 -1
  484. package/dist/src/helper-package-install.js +0 -1
  485. package/dist/src/host-process.js +0 -3
  486. package/dist/src/ime-lifecycle.js +0 -2
  487. package/dist/src/ime-recovery-marker.js +0 -1
  488. package/dist/src/input-actions3.js +0 -1
  489. package/dist/src/install-artifact.js +0 -1
  490. package/dist/src/install-artifact2.js +0 -1
  491. package/dist/src/interaction-common.js +0 -1
  492. package/dist/src/interaction-runtime.js +0 -1
  493. package/dist/src/interaction-snapshot.js +0 -1
  494. package/dist/src/manifest.js +0 -1
  495. package/dist/src/method-scope.js +0 -1
  496. package/dist/src/png-resize.js +0 -1
  497. package/dist/src/process-lock.js +0 -1
  498. package/dist/src/runner-lease.js +0 -1
  499. package/dist/src/runner-sequence.js +0 -1
  500. package/dist/src/script-source-bundle.js +0 -16
  501. package/dist/src/sdk-android-adb.d.ts +0 -245
  502. package/dist/src/sdk-android-adb.js +0 -4
  503. package/dist/src/sdk.js +0 -1
  504. package/dist/src/setting-state.js +0 -1
  505. package/dist/src/shell-quote.js +0 -1
  506. package/dist/src/snapshot-session.js +0 -1
  507. package/dist/src/timing-safe-equal.js +0 -1
  508. package/dist/src/unsupported-interactor.js +0 -1
  509. package/dist/src/verdict.js +0 -1
  510. /package/dist/src/{recording-telemetry.js → telemetry.js} +0 -0
@@ -1,8 +1,33 @@
1
1
  import XCTest
2
+ import AgentDeviceSnapshotPresentation
3
+
4
+
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+
2
27
 
3
28
 
4
29
  extension RunnerTests {
5
- // MARK: - Main Thread Dispatch
30
+
6
31
 
7
32
  private func currentUptimeMs() -> Double {
8
33
  ProcessInfo.processInfo.systemUptime * 1000
@@ -34,7 +59,7 @@ extension RunnerTests {
34
59
  }
35
60
  }
36
61
 
37
- /// Optional visualization frame returned with a gesture response.
62
+
38
63
  enum GestureFrame {
39
64
  case none
40
65
  case touch(TouchVisualizationFrame?)
@@ -57,13 +82,13 @@ extension RunnerTests {
57
82
  }
58
83
 
59
84
 
60
- /// Runs a gesture action with uniform timing capture. Touch gestures pass `idleTimeout: true`
61
- /// (the default) to run inside the scroll idle-timeout + quiescence-skip wrapper; synthesis
62
- /// pointer-plan gestures pass `false` because RunnerSynthesizedGesture governs their
63
- /// own timing. Returns the captured timing and the action's outcome.
64
- ///
65
- /// NOTE: a new SYNTHESIS gesture must pass `idleTimeout: false` — the default `true` would wrap
66
- /// it in the scroll idle-timeout/quiescence-skip path and change its runtime behavior.
85
+
86
+
87
+
88
+
89
+
90
+
91
+
67
92
  func performGesture(
68
93
  _ app: XCUIApplication,
69
94
  idleTimeout: Bool = true,
@@ -80,8 +105,8 @@ extension RunnerTests {
80
105
  return (timing, outcome)
81
106
  }
82
107
 
83
- /// Single factory for the success payload every gesture returns (message + gesture timing +
84
- /// an optional touch/drag visualization frame), so the field shape lives in one place.
108
+
109
+
85
110
  private func gestureResponse(
86
111
  message: String,
87
112
  timing: (gestureStartUptimeMs: Double, gestureEndUptimeMs: Double),
@@ -134,46 +159,786 @@ extension RunnerTests {
134
159
  return Response(ok: true, data: data)
135
160
  }
136
161
 
137
- /// Gesture plans already return canonical centroid endpoints from the portable runtime.
138
- /// Keep runner timing/fallback diagnostics, but do not leak the coordinate-drag adapter's
139
- /// visualization frame into only the fast-fling response shape.
140
- private func canonicalPlannedGestureResponse(_ response: Response) -> Response {
141
- guard response.ok, let data = response.data else { return response }
142
- return Response(
143
- ok: true,
144
- data: DataPayload(
145
- message: data.message,
146
- gestureStartUptimeMs: data.gestureStartUptimeMs,
147
- gestureEndUptimeMs: data.gestureEndUptimeMs,
148
- gestureFallback: data.gestureFallback,
149
- gestureFallbackMessage: data.gestureFallbackMessage,
150
- gestureFallbackHint: data.gestureFallbackHint
151
- )
152
- )
153
- }
154
162
 
155
- private func plannedGestureResponse(
156
- plan: RunnerGesturePlan,
157
- timing: (gestureStartUptimeMs: Double, gestureEndUptimeMs: Double),
158
- outcome: RunnerInteractionOutcome
159
- ) -> Response {
160
- if let response = unsupportedResponse(for: outcome) {
161
- return response
162
- }
163
- return gestureResponse(message: plan.intent, timing: timing)
164
- }
165
163
 
166
164
 
167
- func execute(command: Command) throws -> Response {
168
- if command.command == .status {
169
- return executeStatus(command: command)
170
- }
171
- if command.command == .uptime {
172
- return executeUptime()
173
- }
174
- commandJournal.accept(command: command)
175
- return try executeAccepted(command: command)
176
- }
165
+ private func canonicalPlannedGestureResponse(_ response: Response) -> Response {
166
+ guard response.ok, let data = response.data else { return response }
167
+ return Response(
168
+ ok: true,
169
+ data: DataPayload(
170
+ message: data.message,
171
+ gestureStartUptimeMs: data.gestureStartUptimeMs,
172
+ gestureEndUptimeMs: data.gestureEndUptimeMs,
173
+ gestureFallback: data.gestureFallback,
174
+ gestureFallbackMessage: data.gestureFallbackMessage,
175
+ gestureFallbackHint: data.gestureFallbackHint
176
+ )
177
+ )
178
+ }
179
+
180
+ private func plannedGestureResponse(
181
+ plan: RunnerGesturePlan,
182
+ timing: (gestureStartUptimeMs: Double, gestureEndUptimeMs: Double),
183
+ outcome: RunnerInteractionOutcome
184
+ ) -> Response {
185
+ if let response = unsupportedResponse(for: outcome) {
186
+ return response
187
+ }
188
+ return gestureResponse(message: plan.intent, timing: timing)
189
+ }
190
+
191
+
192
+
193
+
194
+
195
+
196
+
197
+
198
+
199
+
200
+
201
+
202
+
203
+
204
+
205
+
206
+
207
+
208
+
209
+
210
+
211
+
212
+
213
+
214
+
215
+
216
+
217
+
218
+
219
+
220
+
221
+
222
+
223
+
224
+
225
+
226
+
227
+
228
+
229
+
230
+
231
+
232
+
233
+
234
+
235
+
236
+
237
+
238
+
239
+
240
+
241
+
242
+
243
+
244
+
245
+
246
+
247
+
248
+
249
+
250
+
251
+
252
+
253
+
254
+
255
+
256
+
257
+
258
+
259
+
260
+
261
+
262
+
263
+
264
+
265
+
266
+
267
+
268
+
269
+
270
+
271
+
272
+
273
+
274
+
275
+
276
+
277
+
278
+
279
+
280
+
281
+
282
+
283
+
284
+
285
+
286
+
287
+
288
+
289
+
290
+
291
+
292
+
293
+
294
+
295
+
296
+
297
+
298
+
299
+
300
+
301
+
302
+
303
+
304
+
305
+
306
+
307
+
308
+
309
+
310
+
311
+
312
+
313
+
314
+
315
+
316
+
317
+
318
+
319
+
320
+
321
+
322
+
323
+
324
+
325
+
326
+
327
+
328
+
329
+
330
+
331
+
332
+
333
+
334
+
335
+
336
+
337
+
338
+
339
+
340
+
341
+
342
+
343
+
344
+
345
+
346
+
347
+
348
+
349
+
350
+
351
+
352
+
353
+
354
+
355
+
356
+
357
+
358
+
359
+
360
+
361
+
362
+
363
+
364
+
365
+
366
+
367
+
368
+
369
+
370
+
371
+
372
+
373
+
374
+
375
+
376
+
377
+
378
+
379
+
380
+
381
+
382
+
383
+
384
+
385
+
386
+
387
+
388
+
389
+
390
+
391
+
392
+
393
+
394
+
395
+
396
+
397
+
398
+
399
+
400
+
401
+
402
+
403
+
404
+
405
+
406
+
407
+
408
+
409
+
410
+
411
+
412
+
413
+
414
+
415
+
416
+
417
+
418
+
419
+
420
+
421
+
422
+
423
+
424
+
425
+
426
+
427
+
428
+
429
+
430
+
431
+
432
+
433
+
434
+
435
+
436
+
437
+
438
+
439
+
440
+
441
+
442
+
443
+
444
+
445
+
446
+
447
+
448
+
449
+
450
+
451
+
452
+
453
+
454
+
455
+
456
+
457
+
458
+
459
+
460
+
461
+
462
+
463
+
464
+
465
+
466
+
467
+
468
+
469
+
470
+
471
+
472
+
473
+
474
+
475
+
476
+
477
+
478
+
479
+
480
+
481
+
482
+
483
+
484
+
485
+
486
+
487
+
488
+
489
+
490
+
491
+
492
+
493
+
494
+
495
+
496
+
497
+
498
+
499
+
500
+
501
+
502
+
503
+
504
+
505
+
506
+
507
+
508
+
509
+
510
+
511
+
512
+
513
+
514
+
515
+
516
+
517
+
518
+
519
+
520
+
521
+
522
+
523
+
524
+
525
+
526
+
527
+
528
+
529
+
530
+
531
+
532
+
533
+
534
+
535
+
536
+
537
+
538
+
539
+
540
+
541
+
542
+
543
+
544
+
545
+
546
+
547
+
548
+
549
+
550
+
551
+
552
+
553
+
554
+
555
+
556
+
557
+
558
+
559
+
560
+
561
+
562
+
563
+
564
+
565
+
566
+
567
+
568
+
569
+
570
+
571
+
572
+
573
+
574
+
575
+
576
+
577
+
578
+
579
+
580
+
581
+
582
+
583
+
584
+
585
+
586
+
587
+
588
+
589
+
590
+
591
+
592
+
593
+
594
+
595
+
596
+
597
+
598
+
599
+
600
+
601
+
602
+
603
+
604
+
605
+
606
+
607
+
608
+
609
+
610
+
611
+
612
+
613
+
614
+
615
+
616
+
617
+
618
+
619
+
620
+
621
+
622
+
623
+
624
+
625
+
626
+
627
+
628
+
629
+
630
+
631
+
632
+
633
+
634
+
635
+
636
+
637
+
638
+
639
+
640
+
641
+
642
+
643
+
644
+
645
+
646
+
647
+
648
+
649
+
650
+
651
+
652
+
653
+
654
+
655
+
656
+
657
+
658
+
659
+
660
+
661
+
662
+
663
+
664
+
665
+
666
+
667
+
668
+
669
+
670
+
671
+
672
+
673
+
674
+
675
+
676
+
677
+
678
+
679
+
680
+
681
+
682
+
683
+
684
+
685
+
686
+
687
+
688
+
689
+
690
+
691
+
692
+
693
+
694
+
695
+
696
+
697
+
698
+
699
+
700
+
701
+
702
+
703
+
704
+
705
+
706
+
707
+
708
+
709
+
710
+
711
+
712
+
713
+
714
+
715
+
716
+
717
+
718
+
719
+
720
+
721
+
722
+
723
+
724
+
725
+
726
+
727
+
728
+
729
+
730
+
731
+
732
+
733
+
734
+
735
+
736
+
737
+
738
+
739
+
740
+
741
+
742
+
743
+
744
+
745
+
746
+
747
+
748
+
749
+
750
+
751
+
752
+
753
+
754
+
755
+
756
+
757
+
758
+
759
+
760
+
761
+
762
+
763
+
764
+
765
+
766
+
767
+
768
+
769
+
770
+
771
+
772
+
773
+
774
+
775
+
776
+
777
+
778
+
779
+
780
+
781
+
782
+
783
+
784
+
785
+
786
+
787
+
788
+
789
+
790
+
791
+
792
+
793
+
794
+
795
+
796
+
797
+
798
+
799
+
800
+
801
+
802
+
803
+
804
+
805
+
806
+
807
+
808
+
809
+
810
+
811
+
812
+
813
+
814
+
815
+
816
+
817
+
818
+
819
+
820
+
821
+
822
+
823
+
824
+
825
+
826
+
827
+
828
+
829
+
830
+
831
+
832
+
833
+
834
+
835
+
836
+
837
+
838
+
839
+
840
+
841
+
842
+
843
+
844
+
845
+
846
+
847
+
848
+
849
+
850
+
851
+
852
+
853
+
854
+
855
+
856
+
857
+
858
+
859
+
860
+
861
+
862
+
863
+
864
+
865
+
866
+
867
+
868
+
869
+
870
+
871
+
872
+
873
+
874
+
875
+
876
+
877
+
878
+
879
+
880
+
881
+
882
+
883
+
884
+
885
+
886
+
887
+
888
+
889
+
890
+
891
+
892
+
893
+
894
+
895
+
896
+
897
+
898
+
899
+
900
+
901
+
902
+
903
+
904
+
905
+
906
+
907
+
908
+
909
+
910
+
911
+
912
+
913
+
914
+
915
+
916
+
917
+
918
+
919
+
920
+
921
+
922
+
923
+
924
+
925
+
926
+
927
+
928
+
929
+
930
+
931
+
932
+
933
+
934
+
935
+
936
+
937
+
938
+
939
+
940
+
941
+
177
942
 
178
943
  func executeAccepted(command: Command) throws -> Response {
179
944
  commandJournal.start(command: command)
@@ -189,9 +954,7 @@ extension RunnerTests {
189
954
 
190
955
  func executeStatus(command: Command) -> Response {
191
956
  guard
192
- let statusCommandId = command.statusCommandId?
193
- .trimmingCharacters(in: .whitespacesAndNewlines),
194
- !statusCommandId.isEmpty
957
+ let statusCommandId = command.statusCommandId?.trimmedNonEmpty
195
958
  else {
196
959
  return Response(
197
960
  ok: false,
@@ -202,20 +965,20 @@ extension RunnerTests {
202
965
  )
203
966
  )
204
967
  }
205
- return Response(ok: true, data: commandJournal.status(commandId: statusCommandId))
968
+ return Response(ok: true, data: commandJournal.status(normalizedCommandId: statusCommandId))
206
969
  }
207
970
 
208
971
  func executeUptime() -> Response {
209
- // Placeholder value: the transport layer (jsonResponse) overwrites currentUptimeMs with a
210
- // fresher send-time stamp on every ok response; kept so direct callers still get a value.
972
+
973
+
211
974
  Response(
212
975
  ok: true,
213
976
  data: DataPayload(currentUptimeMs: currentUptimeMs())
214
977
  )
215
978
  }
216
979
 
217
- /// Tracks one main-queue dispatch so the watchdog and the dispatched block can agree —
218
- /// under `mainThreadWorkLock` — on exactly one of: finished in time, or abandoned.
980
+
981
+
219
982
  private final class MainThreadWorkState {
220
983
  var finished = false
221
984
  var abandoned = false
@@ -223,6 +986,8 @@ extension RunnerTests {
223
986
 
224
987
  struct ActiveCommandContext {
225
988
  let app: XCUIApplication
989
+
990
+ var systemSurface: SystemSurfaceHost? = nil
226
991
  }
227
992
 
228
993
  enum ActiveCommandPreparation {
@@ -297,10 +1062,10 @@ extension RunnerTests {
297
1062
  }
298
1063
 
299
1064
  private func executeDispatched(command: Command) throws -> Response {
300
- // XCTest work cannot be cancelled mid-flight: once the watchdog abandons a main-queue
301
- // block, queueing more main-thread commands behind it only buries the runner deeper.
302
- // Refuse fast instead so the daemon backs off while the abandoned work drains; past the
303
- // wedge threshold, escalate so the daemon recycles this runner (#1105).
1065
+
1066
+
1067
+
1068
+
304
1069
  if let unavailable = runnerUnavailableResponse(command: command) {
305
1070
  return unavailable
306
1071
  }
@@ -308,23 +1073,17 @@ extension RunnerTests {
308
1073
  ? Date().addingTimeInterval(Self.alertCommandTimeout(timeoutMs: command.timeoutMs))
309
1074
  : nil
310
1075
  if Thread.isMainThread {
311
- let routeToSpringboard = shouldRouteToSpringboardBlockingSystemModal(
312
- command,
313
- deadline: alertDeadline
314
- )
1076
+ let routeToSpringboard = shouldRouteToSpringboardBlockingSystemModal(command)
315
1077
  return try executeOnMainSafely(
316
1078
  command: command,
317
1079
  alertDeadline: alertDeadline,
318
1080
  routeToSpringboard: routeToSpringboard
319
1081
  )
320
1082
  }
321
- // Resolve this before the command's outer main-thread block. If the bounded probe abandons
322
- // slow XCTest enumeration, return the established recoverable response instead of queueing
323
- // command preparation behind work that may outlive the 30-second command watchdog.
324
- let routeToSpringboard = shouldRouteToSpringboardBlockingSystemModal(
325
- command,
326
- deadline: alertDeadline
327
- )
1083
+
1084
+
1085
+
1086
+ let routeToSpringboard = shouldRouteToSpringboardBlockingSystemModal(command)
328
1087
  if let unavailable = runnerUnavailableResponse(command: command) {
329
1088
  return unavailable
330
1089
  }
@@ -333,7 +1092,6 @@ extension RunnerTests {
333
1092
  }
334
1093
  if command.command == .alert, let deadline = alertDeadline {
335
1094
  return try runMainThreadWork(
336
- command: command,
337
1095
  timeout: max(0.001, deadline.timeIntervalSinceNow),
338
1096
  timeoutError: mainThreadExecutionTimeoutError
339
1097
  ) {
@@ -345,7 +1103,6 @@ extension RunnerTests {
345
1103
  }
346
1104
  }
347
1105
  return try runMainThreadWork(
348
- command: command,
349
1106
  timeout: mainThreadExecutionTimeout,
350
1107
  timeoutError: mainThreadExecutionTimeoutError
351
1108
  ) {
@@ -354,7 +1111,6 @@ extension RunnerTests {
354
1111
  }
355
1112
 
356
1113
  func runMainThreadWork<T>(
357
- command: Command?,
358
1114
  timeout: TimeInterval,
359
1115
  timeoutError: @escaping () -> Error,
360
1116
  onAbandoned: (() -> Void)? = nil,
@@ -425,7 +1181,7 @@ extension RunnerTests {
425
1181
  )
426
1182
  }
427
1183
 
428
- // MARK: - Command Handling
1184
+
429
1185
 
430
1186
  private func executeOnMainSafely(
431
1187
  command: Command,
@@ -476,6 +1232,20 @@ extension RunnerTests {
476
1232
  userInfo: [NSLocalizedDescriptionKey: "command returned no response"]
477
1233
  )
478
1234
  }
1235
+
1236
+
1237
+
1238
+
1239
+
1240
+
1241
+
1242
+
1243
+
1244
+
1245
+
1246
+
1247
+
1248
+
479
1249
  if didRecordXCTestFailure(since: failureCountBefore),
480
1250
  let failureResponse = xctestRecordedFailureResponse(command: command, response: response)
481
1251
  {
@@ -502,9 +1272,9 @@ extension RunnerTests {
502
1272
  }
503
1273
  }
504
1274
 
505
- /// The dispatched snapshot recovery loop: read-only retry + XCTest-recorded-failure invalidation,
506
- /// matching what `executeOnMainSafely` gives the generic path. `perform` runs the capture and its
507
- /// own bounded main-thread work.
1275
+
1276
+
1277
+
508
1278
  func executeDispatchedWithRecovery(
509
1279
  command: Command,
510
1280
  perform: () throws -> Response
@@ -512,15 +1282,14 @@ extension RunnerTests {
512
1282
  var hasRetried = false
513
1283
  while true {
514
1284
  let failureCountBefore = try runMainThreadWork(
515
- command: command,
516
1285
  timeout: mainThreadExecutionTimeout,
517
1286
  timeoutError: mainThreadExecutionTimeoutError
518
1287
  ) {
519
1288
  self.currentXCTestFailureCount()
520
1289
  }
521
1290
  let response = try perform()
522
- // Recovered independently — re-entering main for bookkeeping would queue behind the still-
523
- // abandoned XCTest query and re-stall the command (#1244), so skip it until that work drains.
1291
+
1292
+
524
1293
  if hasAbandonedTreeCapture() {
525
1294
  NSLog(
526
1295
  "AGENT_DEVICE_RUNNER_DISPATCH_RECOVERY_SKIPPED_XCTEST_OCCUPIED command=%@",
@@ -529,7 +1298,6 @@ extension RunnerTests {
529
1298
  return response
530
1299
  }
531
1300
  let recordedFailureResponse = try runMainThreadWork(
532
- command: command,
533
1301
  timeout: mainThreadExecutionTimeout,
534
1302
  timeoutError: mainThreadExecutionTimeoutError
535
1303
  ) {
@@ -539,7 +1307,6 @@ extension RunnerTests {
539
1307
  }
540
1308
  if let recordedFailureResponse {
541
1309
  try runMainThreadWork(
542
- command: command,
543
1310
  timeout: mainThreadExecutionTimeout,
544
1311
  timeoutError: mainThreadExecutionTimeoutError
545
1312
  ) {
@@ -554,7 +1321,6 @@ extension RunnerTests {
554
1321
  )
555
1322
  hasRetried = true
556
1323
  try runMainThreadWork(
557
- command: command,
558
1324
  timeout: mainThreadExecutionTimeout,
559
1325
  timeoutError: mainThreadExecutionTimeoutError
560
1326
  ) {
@@ -569,7 +1335,6 @@ extension RunnerTests {
569
1335
 
570
1336
  private func executeSnapshotDispatchedOnce(command: Command) throws -> Response {
571
1337
  let preparation = try runMainThreadWork(
572
- command: command,
573
1338
  timeout: mainThreadExecutionTimeout,
574
1339
  timeoutError: mainThreadExecutionTimeoutError
575
1340
  ) {
@@ -579,12 +1344,16 @@ extension RunnerTests {
579
1344
  case .response(let response):
580
1345
  return response
581
1346
  case .context(let context):
582
- return try executeSnapshotPrepared(command: command, activeApp: context.app)
1347
+ return try executeSnapshotPrepared(
1348
+ command: command,
1349
+ activeApp: context.app,
1350
+ systemSurface: context.systemSurface
1351
+ )
583
1352
  }
584
1353
  }
585
1354
 
586
- /// Pure command→options projection, extracted so the runner unit bundle can
587
- /// prove the decoded wire field actually reaches presentation options (#1634 P2).
1355
+
1356
+
588
1357
  static func presentationOptions(from command: Command) -> PresentationOptions {
589
1358
  let customActions = command.customActions ?? false
590
1359
  return PresentationOptions(
@@ -592,24 +1361,34 @@ extension RunnerTests {
592
1361
  depth: command.depth,
593
1362
  scope: command.scope,
594
1363
  raw: command.raw ?? false,
595
- // Custom actions are only readable through the private AX client, so
596
- // asking for them pins that backend rather than silently returning a
597
- // capture that structurally cannot carry them. An explicit pin wins.
1364
+
1365
+
1366
+
598
1367
  preferredBackend: command.preferredBackend
599
1368
  ?? (customActions ? SnapshotBackendKind.privateAX.rawValue : nil),
600
1369
  customActions: customActions
601
1370
  )
602
1371
  }
603
1372
 
604
- private func executeSnapshotPrepared(command: Command, activeApp: XCUIApplication) throws -> Response {
1373
+ private func executeSnapshotPrepared(
1374
+ command: Command,
1375
+ activeApp: XCUIApplication,
1376
+ systemSurface: SystemSurfaceHost? = nil
1377
+ ) throws -> Response {
605
1378
  let options = Self.presentationOptions(from: command)
606
1379
  do {
607
- let payload: DataPayload
1380
+ var payload: DataPayload
608
1381
  if options.raw {
609
1382
  payload = try snapshotRaw(app: activeApp, options: options)
610
1383
  } else {
611
1384
  payload = try snapshotFast(app: activeApp, options: options)
612
1385
  }
1386
+ if let systemSurface {
1387
+ payload.systemSurface = SystemSurfaceProvenancePayload(
1388
+ bundleId: systemSurface.bundleId,
1389
+ kind: systemSurface.kind.rawValue
1390
+ )
1391
+ }
613
1392
  setNeedsPostSnapshotInteractionDelay()
614
1393
  return Response(ok: true, data: payload)
615
1394
  } catch let failure as SnapshotCaptureFailure {
@@ -636,7 +1415,6 @@ extension RunnerTests {
636
1415
  }
637
1416
  do {
638
1417
  try runMainThreadWork(
639
- command: nil,
640
1418
  timeout: 1,
641
1419
  timeoutError: mainThreadExecutionTimeoutError
642
1420
  ) {
@@ -654,7 +1432,6 @@ extension RunnerTests {
654
1432
  }
655
1433
  do {
656
1434
  try runMainThreadWork(
657
- command: nil,
658
1435
  timeout: 1,
659
1436
  timeoutError: mainThreadExecutionTimeoutError
660
1437
  ) {
@@ -761,10 +1538,10 @@ extension RunnerTests {
761
1538
  }
762
1539
  case .recordStop:
763
1540
  guard let recorder = activeRecording else {
764
- // The runner protocol is the durable cleanup primitive. A daemon may crash after the
765
- // native stop succeeds but before it commits the resource transition, so exact-owner
766
- // recovery must be able to repeat this command safely. Public `record stop` still owns
767
- // its user-facing no-active validation through the daemon session manifest.
1541
+
1542
+
1543
+
1544
+
768
1545
  return Response(ok: true, data: DataPayload(message: "recording already stopped"))
769
1546
  }
770
1547
  do {
@@ -784,8 +1561,8 @@ extension RunnerTests {
784
1561
  else {
785
1562
  return Response(ok: false, error: ErrorPayload(message: "activate requires appBundleId"))
786
1563
  }
787
- // prepareActiveCommandContext already activated this bundle. Keep this case as the
788
- // explicit acknowledgement after that preflight, not as a second activation.
1564
+
1565
+
789
1566
  return Response(ok: true, data: DataPayload(message: "app activated"))
790
1567
  case .terminate:
791
1568
  guard
@@ -814,10 +1591,20 @@ extension RunnerTests {
814
1591
  routeToSpringboard: Bool = false
815
1592
  ) -> ActiveCommandPreparation {
816
1593
  var activeApp = currentApp ?? app
1594
+ var systemSurface: SystemSurfaceHost? = nil
817
1595
  if routeToSpringboard {
818
1596
  activeApp = springboard
819
1597
  } else if shouldSkipAppActivationPreflight(command) {
820
1598
  activeApp = resolveAppWithoutActivation(command: command)
1599
+ } else if let presented = presentedSystemSurfaceHost() {
1600
+
1601
+
1602
+
1603
+ activeApp = presented.app
1604
+ systemSurface = presented.host
1605
+ if isInteractionCommand(command.command) {
1606
+ applyInteractionStabilizationIfNeeded()
1607
+ }
821
1608
  } else if !isRunnerLifecycleCommand(command.command) {
822
1609
  let normalizedBundleId = command.appBundleId?
823
1610
  .trimmingCharacters(in: .whitespacesAndNewlines)
@@ -829,7 +1616,7 @@ extension RunnerTests {
829
1616
  refreshCachedTargetIfProcessChanged(bundleId: bundleId)
830
1617
  }
831
1618
  } else {
832
- // Do not reuse stale bundle targets when the caller does not explicitly request one.
1619
+
833
1620
  invalidateCachedTarget(reason: "missing_app_bundle")
834
1621
  }
835
1622
 
@@ -878,7 +1665,25 @@ extension RunnerTests {
878
1665
  applyInteractionStabilizationIfNeeded()
879
1666
  }
880
1667
  }
881
- return .context(ActiveCommandContext(app: activeApp))
1668
+ return .context(ActiveCommandContext(app: activeApp, systemSurface: systemSurface))
1669
+ }
1670
+
1671
+
1672
+
1673
+
1674
+
1675
+ private func presentedSystemSurfaceHost() -> (host: SystemSurfaceHost, app: XCUIApplication)? {
1676
+ #if os(iOS)
1677
+ for host in SystemSurfaceHostRegistry.hosts {
1678
+ let candidate = XCUIApplication(bundleIdentifier: host.bundleId)
1679
+ if candidate.state == .runningForeground {
1680
+ return (host, candidate)
1681
+ }
1682
+ }
1683
+ return nil
1684
+ #else
1685
+ return nil
1686
+ #endif
882
1687
  }
883
1688
 
884
1689
  func executeOnMainPrepared(
@@ -923,15 +1728,15 @@ extension RunnerTests {
923
1728
  }
924
1729
  if let element = match.element {
925
1730
  let frame = element.frame
926
- // XCTest reports closed-drawer/off-viewport items as hittable, then
927
- // "taps" coordinates outside the visible window as a silent no-op.
928
- // Refuse instead; the daemon falls back to tree-based resolution,
929
- // which can prefer an on-screen candidate or explain the off-screen
930
- // state. The check uses the main window frame, not app.frame: on RN
931
- // apps app.frame unions transformed subtrees (a closed drawer at
932
- // negative x), so it happily "contains" unreachable coordinates.
933
- // The DECISION lives in TapPointPolicy (golden parity table with the
934
- // TS twin); onScreenWindowFrame only supplies the frame.
1731
+
1732
+
1733
+
1734
+
1735
+
1736
+
1737
+
1738
+
1739
+
935
1740
  if !match.usedNonHittableFallback
936
1741
  && !TapPointPolicy.isAllowed(
937
1742
  elementFrame: frame,
@@ -995,10 +1800,10 @@ extension RunnerTests {
995
1800
  }
996
1801
  let (timing, outcome) = performGesture(activeApp) {
997
1802
  if expectedPoint != nil || match.usedNonHittableFallback {
998
- // Maestro compatibility: RN E2E backdoor controls can be 1x1 and
999
- // reported non-hittable by XCTest. Snapshot-resolved targets also
1000
- // need coordinate delivery because XCUIElement.activate() does not
1001
- // invoke every React Native accessibility wrapper.
1803
+
1804
+
1805
+
1806
+
1002
1807
  return tapAt(app: activeApp, x: touchPoint.x, y: touchPoint.y)
1003
1808
  }
1004
1809
  return activateElement(app: activeApp, element: element, action: "tap by selector")
@@ -1034,9 +1839,9 @@ extension RunnerTests {
1034
1839
  )
1035
1840
  let textInput: XCUIElement?
1036
1841
  if !xCTestTextInputProbeSkipped {
1037
- textInput = textInputAt(app: activeApp, x: x, y: y)
1842
+ textInput = coordinateTapTextInputAt(app: activeApp, x: x, y: y)
1038
1843
  } else {
1039
- // A process-scoped tap cannot authorize later typing without concrete element identity.
1844
+
1040
1845
  textInput = nil
1041
1846
  NSLog(
1042
1847
  "AGENT_DEVICE_RUNNER_COORDINATE_TAP_TEXT_INPUT_PROBE_SKIPPED bundle=%@",
@@ -1083,8 +1888,8 @@ extension RunnerTests {
1083
1888
  }
1084
1889
  let touchFrame = resolvedTouchVisualizationFrame(app: activeApp, x: x, y: y)
1085
1890
  do {
1086
- // mouseClick throws (it has no RunnerInteractionOutcome), so it keeps raw measureGesture
1087
- // and only routes the success payload through gestureResponse.
1891
+
1892
+
1088
1893
  var clickError: Error?
1089
1894
  let timing = measureGesture {
1090
1895
  do {
@@ -1117,31 +1922,26 @@ extension RunnerTests {
1117
1922
  guard let x = command.x, let y = command.y, let x2 = command.x2, let y2 = command.y2 else {
1118
1923
  return Response(ok: false, error: ErrorPayload(message: "drag requires x, y, x2, and y2"))
1119
1924
  }
1925
+ let defaults = runnerDragCommandDefaults(command)
1120
1926
  return executeDragGesture(
1121
1927
  activeApp: activeApp,
1122
1928
  x: x,
1123
1929
  y: y,
1124
1930
  x2: x2,
1125
1931
  y2: y2,
1126
- durationMs: command.durationMs,
1932
+ durationMs: defaults.durationMs,
1127
1933
  synthesized: command.synthesized == true,
1128
1934
  message: "dragged",
1129
1935
  synthesizedPolicyKind: .synthesizedDrag
1130
1936
  )
1131
1937
  case .scroll:
1132
- // Fused frame-resolve + drag scroll for non-tvOS. On iOS this intentionally stays on the
1133
- // AX-free synthesized coordinate lane so scroll keeps working when XCTest cannot serialize
1134
- // the accessibility tree.
1135
- guard let direction = command.direction,
1136
- direction == "up" || direction == "down" || direction == "left" || direction == "right"
1938
+
1939
+
1940
+
1941
+ guard let rawDirection = command.direction,
1942
+ let direction = RunnerScrollDirection(rawValue: rawDirection)
1137
1943
  else {
1138
- return Response(
1139
- ok: false,
1140
- error: ErrorPayload(
1141
- code: "INVALID_ARGS",
1142
- message: "scroll requires direction up|down|left|right"
1143
- )
1144
- )
1944
+ return invalidScrollDirectionResponse(commandName: "scroll")
1145
1945
  }
1146
1946
  let scrollPolicyKind = SynthesizedGesturePolicyKind.scroll
1147
1947
  guard let scrollContext = synthesizedCoordinateContext(
@@ -1160,9 +1960,10 @@ extension RunnerTests {
1160
1960
  error: ErrorPayload(message: "scroll could not resolve a usable interaction frame")
1161
1961
  )
1162
1962
  }
1963
+ let defaults = runnerDragCommandDefaults(command)
1163
1964
  guard let plan = runnerScrollGesturePlan(
1164
1965
  direction: direction,
1165
- amount: command.amount,
1966
+ amount: defaults.scrollAmount,
1166
1967
  pixels: command.pixels,
1167
1968
  referenceWidth: frame.width,
1168
1969
  referenceHeight: frame.height
@@ -1175,40 +1976,25 @@ extension RunnerTests {
1175
1976
  )
1176
1977
  )
1177
1978
  }
1178
- if let durationMs = command.durationMs,
1179
- durationMs.isFinite == false || durationMs < 0 || durationMs > 10000
1180
- {
1181
- return Response(
1182
- ok: false,
1183
- error: ErrorPayload(
1184
- code: "INVALID_ARGS",
1185
- message: "scroll durationMs must be between 0 and 10000"
1186
- )
1187
- )
1979
+ guard scrollDurationIsValid(command.durationMs) else {
1980
+ return invalidScrollDurationResponse(commandName: "scroll")
1188
1981
  }
1189
- return executeDragGesture(
1982
+ return executeScrollDragGesture(
1190
1983
  activeApp: activeApp,
1191
1984
  x: frame.minX + plan.x1,
1192
1985
  y: frame.minY + plan.y1,
1193
1986
  x2: frame.minX + plan.x2,
1194
1987
  y2: frame.minY + plan.y2,
1195
- durationMs: command.durationMs,
1196
- synthesized: shouldUseSynthesizedScrollPath(),
1988
+ durationMs: defaults.durationMs,
1197
1989
  message: "scrolled",
1198
- synthesizedContext: scrollContext.withReferenceFrame(frame),
1199
- synthesizedPolicyKind: scrollPolicyKind
1990
+ context: scrollContext.withReferenceFrame(frame),
1991
+ releaseBehavior: command.scrollReleaseBehavior
1200
1992
  )
1201
1993
  case .desktopScroll:
1202
- guard let direction = command.direction,
1203
- direction == "up" || direction == "down" || direction == "left" || direction == "right"
1994
+ guard let rawDirection = command.direction,
1995
+ let direction = RunnerScrollDirection(rawValue: rawDirection)
1204
1996
  else {
1205
- return Response(
1206
- ok: false,
1207
- error: ErrorPayload(
1208
- code: "INVALID_ARGS",
1209
- message: "desktopScroll requires direction up|down|left|right"
1210
- )
1211
- )
1997
+ return invalidScrollDirectionResponse(commandName: "desktopScroll")
1212
1998
  }
1213
1999
  let appFrame = activeApp.frame
1214
2000
  let frame = resolvedTouchReferenceFrame(app: activeApp, appFrame: appFrame)
@@ -1237,16 +2023,8 @@ extension RunnerTests {
1237
2023
  let y = frame.midY
1238
2024
  let localX = x - (appFrame.isEmpty ? frame.minX : appFrame.minX)
1239
2025
  let localY = y - (appFrame.isEmpty ? frame.minY : appFrame.minY)
1240
- if let durationMs = command.durationMs,
1241
- durationMs.isFinite == false || durationMs < 0 || durationMs > 10000
1242
- {
1243
- return Response(
1244
- ok: false,
1245
- error: ErrorPayload(
1246
- code: "INVALID_ARGS",
1247
- message: "desktopScroll durationMs must be between 0 and 10000"
1248
- )
1249
- )
2026
+ guard scrollDurationIsValid(command.durationMs) else {
2027
+ return invalidScrollDurationResponse(commandName: "desktopScroll")
1250
2028
  }
1251
2029
  let touchFrame = resolvedTouchVisualizationFrame(
1252
2030
  app: activeApp,
@@ -1298,8 +2076,8 @@ extension RunnerTests {
1298
2076
  guard let direction = command.direction else {
1299
2077
  return Response(ok: false, error: ErrorPayload(message: "swipe requires direction"))
1300
2078
  }
1301
- // swipe returns an optional frame (tvOS-only) rather than a RunnerInteractionOutcome, so it
1302
- // keeps raw measureGesture and only routes the success payload through gestureResponse.
2079
+
2080
+
1303
2081
  var executedFrame: DragVisualizationFrame?
1304
2082
  let timing = measureGesture {
1305
2083
  withTemporaryScrollIdleTimeoutIfSupported(activeApp) {
@@ -1334,12 +2112,12 @@ extension RunnerTests {
1334
2112
  case .screenshot:
1335
2113
  let screenshot: XCUIScreenshot
1336
2114
  #if os(macOS)
1337
- // macOS keeps the app-targeted capture behavior for window-level screenshots.
2115
+
1338
2116
  if let bundleId = command.appBundleId, !bundleId.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
1339
2117
  let targetApp = XCUIApplication(bundleIdentifier: bundleId)
1340
2118
  targetApp.activate()
1341
2119
  activeApp = targetApp
1342
- // Brief wait for the app transition animation to complete
2120
+
1343
2121
  sleepFor(0.5)
1344
2122
  }
1345
2123
  if command.fullscreen == true {
@@ -1371,7 +2149,7 @@ extension RunnerTests {
1371
2149
  #if os(macOS)
1372
2150
  return Response(ok: true, data: DataPayload(message: filePath))
1373
2151
  #else
1374
- // Return path relative to app container root (tmp/ maps to NSTemporaryDirectory)
2152
+
1375
2153
  return Response(ok: true, data: DataPayload(message: "tmp/\(fileName)"))
1376
2154
  #endif
1377
2155
  case .back, .backInApp:
@@ -1441,7 +2219,12 @@ extension RunnerTests {
1441
2219
  Self.alertCommandTimeout(timeoutMs: command.timeoutMs)
1442
2220
  )
1443
2221
  guard let alert = resolveAlert(app: activeApp, deadline: deadline) else {
1444
- return Response(ok: false, error: ErrorPayload(message: "alert not found"))
2222
+
2223
+
2224
+ return Response(
2225
+ ok: false,
2226
+ error: ErrorPayload(code: "ALERT_NOT_FOUND", message: "alert not found")
2227
+ )
1445
2228
  }
1446
2229
  return handleAlert(alert, action: action, deadline: deadline)
1447
2230
  case .gesture:
@@ -1459,7 +2242,7 @@ extension RunnerTests {
1459
2242
  }
1460
2243
  switch plannedGestureExecution(for: plan) {
1461
2244
  case .fastSwipe:
1462
- // Validation above guarantees a non-empty, single-pointer path for this execution kind.
2245
+
1463
2246
  let first = plan.pointers[0].samples.first!.point
1464
2247
  let last = plan.pointers[0].samples.last!.point
1465
2248
  return canonicalPlannedGestureResponse(
@@ -1493,9 +2276,73 @@ extension RunnerTests {
1493
2276
  }
1494
2277
  }
1495
2278
 
1496
- /// Shared drag execution for `.drag` and the fused `.scroll`. The iOS synthesized lane keeps
1497
- /// each command's fallback policy explicit: scroll requires private synthesis, while explicit
1498
- /// synthesized drag can still use the coordinate fallback unless AX is known unavailable.
2279
+ private func invalidScrollDirectionResponse(commandName: String) -> Response {
2280
+ Response(
2281
+ ok: false,
2282
+ error: ErrorPayload(
2283
+ code: "INVALID_ARGS",
2284
+ message: "\(commandName) requires direction up|down|left|right"
2285
+ )
2286
+ )
2287
+ }
2288
+
2289
+ private func scrollDurationIsValid(_ durationMs: Double?) -> Bool {
2290
+ guard let durationMs else { return true }
2291
+ return durationMs.isFinite && durationMs >= 0 && durationMs <= 10000
2292
+ }
2293
+
2294
+ private func invalidScrollDurationResponse(commandName: String) -> Response {
2295
+ return Response(
2296
+ ok: false,
2297
+ error: ErrorPayload(
2298
+ code: "INVALID_ARGS",
2299
+ message: "\(commandName) durationMs must be between 0 and 10000"
2300
+ )
2301
+ )
2302
+ }
2303
+
2304
+ private func executeScrollDragGesture(
2305
+ activeApp: XCUIApplication,
2306
+ x: Double,
2307
+ y: Double,
2308
+ x2: Double,
2309
+ y2: Double,
2310
+ durationMs: Double,
2311
+ message: String,
2312
+ context: SynthesizedCoordinateContext,
2313
+ releaseBehavior: ScrollReleaseBehavior?
2314
+ ) -> Response {
2315
+ #if os(iOS)
2316
+ return executeDragGesture(
2317
+ activeApp: activeApp,
2318
+ x: x,
2319
+ y: y,
2320
+ x2: x2,
2321
+ y2: y2,
2322
+ durationMs: durationMs,
2323
+ synthesized: true,
2324
+ message: message,
2325
+ synthesizedContext: context,
2326
+ synthesizedPolicyKind: .scroll,
2327
+ synthesizedProfile: scrollDragProfile(releaseBehavior: releaseBehavior)
2328
+ )
2329
+ #else
2330
+ return executeDragGesture(
2331
+ activeApp: activeApp,
2332
+ x: x,
2333
+ y: y,
2334
+ x2: x2,
2335
+ y2: y2,
2336
+ durationMs: durationMs,
2337
+ synthesized: false,
2338
+ message: message,
2339
+ synthesizedPolicyKind: .scroll
2340
+ )
2341
+ #endif
2342
+ }
2343
+
2344
+
2345
+
1499
2346
  private func executeDragGesture(
1500
2347
  activeApp: XCUIApplication,
1501
2348
  x: Double,
@@ -1509,6 +2356,7 @@ extension RunnerTests {
1509
2356
  synthesizedPolicyKind: SynthesizedGesturePolicyKind,
1510
2357
  synthesizedProfile: SynthesizedDragProfile = .continuous
1511
2358
  ) -> Response {
2359
+ let durationMs = durationMs ?? runnerDefaultDragDurationMs
1512
2360
  let commandName = dragCommandName(message: message)
1513
2361
  guard x.isFinite, y.isFinite, x2.isFinite, y2.isFinite else {
1514
2362
  return Response(
@@ -1540,7 +2388,7 @@ extension RunnerTests {
1540
2388
  )
1541
2389
  var fallback: GestureFallback?
1542
2390
  if synthesized {
1543
- let durationMs = min(max(durationMs ?? 250, 16), 10000)
2391
+ let durationMs = min(max(durationMs, 16), 10000)
1544
2392
  let context = synthesizedCoordinateContext(
1545
2393
  app: activeApp,
1546
2394
  policy: synthesizedGesturePolicy(synthesizedPolicyKind)
@@ -1563,7 +2411,7 @@ extension RunnerTests {
1563
2411
  fallback = gestureFallback(strategy: "xctest-coordinate-drag", from: outcome)
1564
2412
  }
1565
2413
  let holdDuration = synthesized
1566
- ? synthesizedSwipeFallbackHoldDuration(durationMs: durationMs ?? 250)
2414
+ ? synthesizedSwipeFallbackHoldDuration(durationMs: durationMs)
1567
2415
  : coordinateDragHoldDuration()
1568
2416
  let (timing, outcome) = performGesture(activeApp) {
1569
2417
  dragAt(
@@ -1592,7 +2440,7 @@ extension RunnerTests {
1592
2440
  y: Double,
1593
2441
  x2: Double,
1594
2442
  y2: Double,
1595
- durationMs: Double?,
2443
+ durationMs: Double,
1596
2444
  message: String,
1597
2445
  context: SynthesizedCoordinateContext?,
1598
2446
  policyKind: SynthesizedGesturePolicyKind,
@@ -1632,7 +2480,7 @@ extension RunnerTests {
1632
2480
  )
1633
2481
  )
1634
2482
  }
1635
- let durationMs = min(max(durationMs ?? 250, 16), 10000)
2483
+ let durationMs = min(max(durationMs, 16), 10000)
1636
2484
  let dragFrame = axFreeDragVisualizationFrame(
1637
2485
  x: plan.points.x,
1638
2486
  y: plan.points.y,
@@ -1682,7 +2530,7 @@ extension RunnerTests {
1682
2530
  y: Double,
1683
2531
  x2: Double,
1684
2532
  y2: Double,
1685
- durationMs: Double?,
2533
+ durationMs: Double,
1686
2534
  message: String,
1687
2535
  fallback: GestureFallback?
1688
2536
  ) -> Response {
@@ -1694,7 +2542,7 @@ extension RunnerTests {
1694
2542
  x2: dragPoints.x2,
1695
2543
  y2: dragPoints.y2
1696
2544
  )
1697
- let holdDuration = synthesizedSwipeFallbackHoldDuration(durationMs: durationMs ?? 250)
2545
+ let holdDuration = synthesizedSwipeFallbackHoldDuration(durationMs: durationMs)
1698
2546
  let (timing, outcome) = performGesture(activeApp) {
1699
2547
  dragAt(
1700
2548
  app: activeApp,
@@ -1724,14 +2572,6 @@ extension RunnerTests {
1724
2572
  #endif
1725
2573
  }
1726
2574
 
1727
- private func shouldUseSynthesizedScrollPath() -> Bool {
1728
- #if os(iOS)
1729
- return true
1730
- #else
1731
- return false
1732
- #endif
1733
- }
1734
-
1735
2575
  private func dragCommandName(message: String) -> String {
1736
2576
  return message == "scrolled" ? "scroll" : "drag"
1737
2577
  }
@@ -1762,19 +2602,21 @@ extension RunnerTests {
1762
2602
  )
1763
2603
  }
1764
2604
 
1765
- func runnerCommandFixture(_ json: String) throws -> Command {
1766
- try JSONDecoder().decode(Command.self, from: Data(json.utf8))
1767
- }
2605
+
2606
+
2607
+
2608
+
2609
+
1768
2610
 
1769
2611
  private func shouldSkipAppActivationPreflight(_ command: Command) -> Bool {
1770
2612
  #if os(iOS)
1771
2613
  if command.command == .alert {
1772
2614
  return true
1773
2615
  }
1774
- // Coordinate-only synthesized taps can run after an AX-fatal foreground screen because they do not
1775
- // need app activation, window lookup, keyboard lookup, or element resolution. Selector/text
1776
- // interactions intentionally stay on the normal AX path because they need an element query.
1777
- // Scroll/drag/sequence keep the normal foreground guard and stabilization path.
2616
+
2617
+
2618
+
2619
+
1778
2620
  guard command.text == nil, command.selectorKey == nil else { return false }
1779
2621
  guard hasCachedTargetForActivationSkip(command: command) else { return false }
1780
2622
  return isCoordinateOnlyTap(command)
@@ -1784,18 +2626,21 @@ extension RunnerTests {
1784
2626
  }
1785
2627
 
1786
2628
  private func shouldRouteToSpringboardBlockingSystemModal(
1787
- _ command: Command,
1788
- deadline: Date? = nil
2629
+ _ command: Command
1789
2630
  ) -> Bool {
1790
2631
  #if os(iOS)
1791
- guard command.command == .alert || isCoordinateOnlyTap(command) else {
2632
+ guard isCoordinateOnlyTap(command) else {
1792
2633
  return false
1793
2634
  }
1794
- let budgetDeadline = Date().addingTimeInterval(systemModalProbeBudget)
1795
- let probeDeadline = deadline.map { min($0, budgetDeadline) } ?? budgetDeadline
1796
- // `runMainThreadWork` executes inline for a main-thread caller, so that path cannot use its
1797
- // timeout machinery. Direct main-thread dispatch keeps the prior synchronous modal check;
1798
- // normal off-main command dispatch uses the bounded probe and post-probe busy recovery.
2635
+
2636
+
2637
+
2638
+
2639
+
2640
+ let probeDeadline = Date().addingTimeInterval(systemModalProbeBudget)
2641
+
2642
+
2643
+
1799
2644
  if Thread.isMainThread {
1800
2645
  return firstBlockingSystemModal(
1801
2646
  in: springboard,
@@ -1854,8 +2699,8 @@ extension RunnerTests {
1854
2699
  command.x != nil,
1855
2700
  command.y != nil
1856
2701
  {
1857
- // The shared runtime has already resolved this node as non-hittable and
1858
- // deliberately selected Maestro's coordinate compatibility route.
2702
+
2703
+
1859
2704
  maestroNonHittableCoordinateFallbackUsed = true
1860
2705
  }
1861
2706
  let focusStartedAt = Date()
@@ -1903,8 +2748,8 @@ extension RunnerTests {
1903
2748
  if let resolvedCoordinateTarget {
1904
2749
  target = resolvedCoordinateTarget
1905
2750
  } else if let selectorKey = command.selectorKey, let selectorValue = command.selectorValue {
1906
- // Released daemons may still send selector-keyed type commands even though current
1907
- // daemons resolve fill selectors through the runtime tree before reaching the runner.
2751
+
2752
+
1908
2753
  let match = findElement(
1909
2754
  app: activeApp,
1910
2755
  selectorKey: selectorKey,
@@ -1986,8 +2831,8 @@ extension RunnerTests {
1986
2831
  } else if point != nil {
1987
2832
  frame = activeApp.frame
1988
2833
  } else {
1989
- // Bare `type` has no coordinate response to normalize. Avoid serializing the
1990
- // application AX tree only to emit unused reference dimensions.
2834
+
2835
+
1991
2836
  frame = .zero
1992
2837
  }
1993
2838
  return Response(