agent-device 0.21.12 → 0.21.14

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 (317) hide show
  1. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.21.12.apk → agent-device-android-ime-helper-0.21.14.apk} +0 -0
  2. package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.14.apk.sha256 +1 -0
  3. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.21.12.manifest.json → agent-device-android-ime-helper-0.21.14.manifest.json} +4 -4
  4. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.14.apk +0 -0
  5. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.14.apk.sha256 +1 -0
  6. package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.21.12.manifest.json → agent-device-android-snapshot-helper-0.21.14.manifest.json} +6 -6
  7. package/apple/macos-helper/Package.swift +4 -0
  8. package/apple/macos-helper/Sources/AgentDeviceMacOSHelper/main.swift +3 -6
  9. package/apple/macos-helper/Tests/AgentDeviceMacOSHelperTests/ScreenshotResponseTests.swift +15 -0
  10. package/apple/snapshot-bridge/SnapshotBridgeRuntime.m +47 -1
  11. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunner/AgentDeviceRunnerApp.m +122 -42
  12. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunner/Info.plist +23 -0
  13. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/project.pbxproj +17 -0
  14. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAppScreenCapture.swift +21 -13
  15. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerScrollViewportPolicy.swift +1 -162
  16. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.h +0 -9
  17. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.m +0 -56
  18. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSystemSurfaceHostPolicy.swift +0 -48
  19. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTapPointPolicy.swift +0 -50
  20. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+AXSnapshotFallback.swift +33 -586
  21. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Alert.swift +40 -24
  22. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+AlertObservation.swift +2 -3
  23. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+BlockingSystemModalResolution.swift +0 -36
  24. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandDispatch.swift +630 -0
  25. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +34 -2256
  26. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandJournal.swift +1 -338
  27. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Environment.swift +0 -12
  28. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+GestureExecution.swift +160 -0
  29. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +0 -24
  30. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Keyboard.swift +0 -142
  31. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Lifecycle.swift +90 -39
  32. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+MainThreadWork.swift +78 -106
  33. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +23 -2
  34. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Navigation.swift +162 -113
  35. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+PrivateAXPresentation.swift +0 -209
  36. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScreenRecorder.swift +86 -10
  37. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScrollDragExecution.swift +271 -0
  38. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScrollGesture.swift +1 -200
  39. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SequenceExecution.swift +20 -227
  40. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +78 -295
  41. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotAcquisition.swift +40 -27
  42. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotBackendCapabilities.swift +0 -78
  43. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCapturePlan.swift +85 -581
  44. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCaptureTarget.swift +106 -0
  45. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotExecution.swift +142 -0
  46. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotTiming.swift +49 -78
  47. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedCommitDeadline.swift +0 -44
  48. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedGesturePolicy.swift +73 -114
  49. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedInteraction.swift +1 -12
  50. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedTextEntry.swift +11 -94
  51. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SystemModal.swift +2 -10
  52. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntry.swift +1 -1
  53. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntryFocus.swift +0 -35
  54. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextInputCandidatePolicy.swift +0 -31
  55. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextTyping.swift +2 -18
  56. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Transport.swift +34 -52
  57. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TvRemote.swift +0 -22
  58. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TypeExecution.swift +135 -0
  59. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +22 -6
  60. package/dist/apple/runner/AgentDeviceRunner/RecordingScripts/recording-overlay.swift +140 -20
  61. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotCoordinateSpace.swift +16 -19
  62. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotGeometry.swift +21 -9
  63. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotModels.swift +59 -10
  64. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotPresentationInvariant.swift +12 -10
  65. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotVisibilityFold.swift +13 -8
  66. package/dist/src/absence-observation-errors.js +1 -1
  67. package/dist/src/adapter.js +1 -1
  68. package/dist/src/adb-executor.js +1 -1
  69. package/dist/src/adb-failure.js +1 -1
  70. package/dist/src/adb-transport.js +1 -1
  71. package/dist/src/agent-browser-provider.js +1 -1
  72. package/dist/src/agent-browser-tool.js +1 -1
  73. package/dist/src/agent-device-client.js +2 -2
  74. package/dist/src/ai-sdk.js +1 -1
  75. package/dist/src/alert-runtime.js +1 -1
  76. package/dist/src/android-adb.d.ts +1 -1
  77. package/dist/src/app-catalog.js +1 -1
  78. package/dist/src/app-deployment-resolution.js +1 -1
  79. package/dist/src/app-inventory.js +1 -1
  80. package/dist/src/app-launch.js +2 -2
  81. package/dist/src/app-lifecycle.js +1 -1
  82. package/dist/src/app-lifecycle2.js +1 -1
  83. package/dist/src/app-log-files.js +1 -1
  84. package/dist/src/app-log-runtime2.js +3 -3
  85. package/dist/src/app-resolution.js +2 -2
  86. package/dist/src/apple-runner-platform.js +1 -1
  87. package/dist/src/application-lifecycle-interaction.js +1 -1
  88. package/dist/src/archive.js +1 -1
  89. package/dist/src/artifact-download.js +1 -1
  90. package/dist/src/auth-session.js +1 -1
  91. package/dist/src/auth.js +1 -1
  92. package/dist/src/back-mode.js +1 -1
  93. package/dist/src/backend-snapshot-options.js +1 -1
  94. package/dist/src/boot-diagnostics.js +1 -1
  95. package/dist/src/capture-disclosure.js +1 -1
  96. package/dist/src/capture.js +1 -1
  97. package/dist/src/cli-config.js +1 -1
  98. package/dist/src/cli-help.js +8 -7
  99. package/dist/src/cli-runner.js +1 -1
  100. package/dist/src/cli.js +4 -4
  101. package/dist/src/click-button.js +1 -1
  102. package/dist/src/client-metro.js +1 -1
  103. package/dist/src/client-types.d.ts +4 -5
  104. package/dist/src/clipboard-shell-response.js +1 -1
  105. package/dist/src/code-signature.js +1 -1
  106. package/dist/src/command-schema.js +3 -3
  107. package/dist/src/command-tools.js +3 -3
  108. package/dist/src/connection-runtime.js +1 -1
  109. package/dist/src/connection.js +1 -1
  110. package/dist/src/contracts.js +1 -1
  111. package/dist/src/daemon-client-lifecycle.js +1 -1
  112. package/dist/src/daemon-resolution.js +1 -1
  113. package/dist/src/daemon.js +1 -1
  114. package/dist/src/debug-symbols-facade.js +1 -1
  115. package/dist/src/device-claim-rule.js +2 -2
  116. package/dist/src/device-input-state.d.ts +1 -1
  117. package/dist/src/device-inventory-context.js +1 -1
  118. package/dist/src/device-rotation.js +1 -1
  119. package/dist/src/device-selection-resolver.js +1 -1
  120. package/dist/src/device-session.js +1 -1
  121. package/dist/src/device-shell.js +1 -1
  122. package/dist/src/device2.js +1 -1
  123. package/dist/src/devicectl.js +1 -1
  124. package/dist/src/diagnostics.js +1 -1
  125. package/dist/src/dispatch-resolve.js +1 -1
  126. package/dist/src/display-inventory.js +1 -1
  127. package/dist/src/doctor-facade.js +1 -1
  128. package/dist/src/doctor2.js +1 -1
  129. package/dist/src/engine-eval-script.js +1 -1
  130. package/dist/src/error.js +1 -1
  131. package/dist/src/exec.js +1 -1
  132. package/dist/src/file.js +1 -1
  133. package/dist/src/find.js +1 -1
  134. package/dist/src/format.js +1 -1
  135. package/dist/src/gesture-plan-types.d.ts +38 -0
  136. package/dist/src/gesture-plan.js +1 -1
  137. package/dist/src/gesture-runtime.js +1 -1
  138. package/dist/src/harmonyos.js +1 -1
  139. package/dist/src/helper.js +2 -2
  140. package/dist/src/host.js +1 -1
  141. package/dist/src/human-control-contract.js +1 -1
  142. package/dist/src/human-control.js +1 -1
  143. package/dist/src/index.js +1 -1
  144. package/dist/src/input-actions.js +1 -1
  145. package/dist/src/input-actions2.js +1 -1
  146. package/dist/src/install-artifact-facade.js +1 -1
  147. package/dist/src/install-source-network-transport.js +1 -1
  148. package/dist/src/install-source2.js +1 -1
  149. package/dist/src/interaction-positionals.js +1 -1
  150. package/dist/src/interaction-ref-policy.js +1 -1
  151. package/dist/src/interaction2.js +1 -1
  152. package/dist/src/interactor-operation-binding.js +1 -1
  153. package/dist/src/interactor-operation-catalog.js +1 -1
  154. package/dist/src/interactor.js +4 -4
  155. package/dist/src/interactor2.js +1 -1
  156. package/dist/src/interactors.js +1 -1
  157. package/dist/src/internal/bin.js +1 -1
  158. package/dist/src/internal/daemon.js +2 -2
  159. package/dist/src/internal/png-worker.js +1 -1
  160. package/dist/src/internal/run-script-http-child.js +1 -1
  161. package/dist/src/inventory.js +1 -1
  162. package/dist/src/inventory2.js +1 -1
  163. package/dist/src/inventory3.js +1 -1
  164. package/dist/src/inventory5.js +1 -1
  165. package/dist/src/ios-snapshot-acquisition.js +1 -1
  166. package/dist/src/ios-snapshot-runtime.js +1 -1
  167. package/dist/src/lease.js +1 -1
  168. package/dist/src/limrun-download-file.js +1 -1
  169. package/dist/src/limrun-runtime-dependencies.js +1 -1
  170. package/dist/src/limrun.d.ts +435 -4
  171. package/dist/src/linux.js +1 -1
  172. package/dist/src/local-interactor-operation-set.js +1 -1
  173. package/dist/src/location-coordinates.js +1 -1
  174. package/dist/src/managed-device-scope.js +1 -1
  175. package/dist/src/mechanics.js +5 -5
  176. package/dist/src/mobile-snapshot-semantics.js +1 -1
  177. package/dist/src/multitouch-support.js +1 -1
  178. package/dist/src/native-build-cache.js +1 -0
  179. package/dist/src/observation.js +1 -1
  180. package/dist/src/parameterized-recorded-fill.js +1 -1
  181. package/dist/src/perf-facade.js +1 -2
  182. package/dist/src/perf-runtime-operation-builder.js +1 -1
  183. package/dist/src/perf-runtime-plan.js +1 -1
  184. package/dist/src/perf-target.js +2 -0
  185. package/dist/src/perf.js +1 -1
  186. package/dist/src/physical-device-apps.js +1 -0
  187. package/dist/src/physical-device-control.js +1 -1
  188. package/dist/src/platform-request-scope.js +1 -1
  189. package/dist/src/platform-runtime-host.js +1 -1
  190. package/dist/src/platform-runtime-managed-owner.js +1 -1
  191. package/dist/src/platform-runtime-open-target.js +1 -1
  192. package/dist/src/platform-runtime-operation-host.js +2 -2
  193. package/dist/src/platform-runtime-runtime-hints.js +1 -1
  194. package/dist/src/platform-runtime-screen-recording-apple-simulator-host.js +1 -1
  195. package/dist/src/platform-runtime-screen-recording-finalizer-host.js +1 -1
  196. package/dist/src/platform-runtime-screen-recording-process-host.js +1 -1
  197. package/dist/src/platform-runtime-unavailable.js +1 -1
  198. package/dist/src/platform-runtime.js +1 -1
  199. package/dist/src/platform-runtime2.js +1 -1
  200. package/dist/src/png-crop-bytes.js +1 -1
  201. package/dist/src/png-size.js +1 -1
  202. package/dist/src/png-worker-client.js +1 -1
  203. package/dist/src/png.js +1 -1
  204. package/dist/src/prepare-kind.js +1 -1
  205. package/dist/src/provider-session-ownership.js +1 -1
  206. package/dist/src/provider-webdriver.js +1 -1
  207. package/dist/src/proxy.js +1 -1
  208. package/dist/src/quality-warnings.js +1 -1
  209. package/dist/src/react-native.js +1 -1
  210. package/dist/src/record-runtime.js +1 -1
  211. package/dist/src/recording.js +1 -1
  212. package/dist/src/register-builtins.js +1 -1
  213. package/dist/src/registry.js +35 -35
  214. package/dist/src/remote-config2.js +1 -1
  215. package/dist/src/replay-divergence.js +1 -1
  216. package/dist/src/replay.js +1 -1
  217. package/dist/src/reporting.js +1 -1
  218. package/dist/src/request-admission.js +1 -1
  219. package/dist/src/request-cancel.js +1 -1
  220. package/dist/src/request-runtime-binding.js +1 -1
  221. package/dist/src/retry.js +1 -1
  222. package/dist/src/runner-cache-metadata.js +1 -1
  223. package/dist/src/runner-client.js +1 -1
  224. package/dist/src/runner-dev-tools-security.js +1 -1
  225. package/dist/src/runner-device-readiness.js +1 -1
  226. package/dist/src/runner-disposal.js +1 -1
  227. package/dist/src/runner-error-classification.js +3 -0
  228. package/dist/src/runner-operations-facade.js +1 -1
  229. package/dist/src/runner-presentation.js +1 -1
  230. package/dist/src/runner.js +1 -1
  231. package/dist/src/runtime-transport-hints.js +1 -1
  232. package/dist/src/runtime.js +3 -3
  233. package/dist/src/runtime10.js +2 -2
  234. package/dist/src/runtime2.js +2 -2
  235. package/dist/src/runtime3.js +1 -1
  236. package/dist/src/runtime4.js +4 -4
  237. package/dist/src/runtime5.js +1 -1
  238. package/dist/src/runtime6.js +1 -1
  239. package/dist/src/runtime7.js +1 -1
  240. package/dist/src/runtime8.js +1 -1
  241. package/dist/src/runtime9.js +1 -1
  242. package/dist/src/screen-recording-admission-ledger.js +1 -1
  243. package/dist/src/screen-recording-completion.js +1 -1
  244. package/dist/src/screen-recording-options.js +1 -1
  245. package/dist/src/screen-recording-resource-recovery.js +1 -1
  246. package/dist/src/screen-recording-transport.js +1 -1
  247. package/dist/src/screenshot-density.js +1 -1
  248. package/dist/src/screenshot-image.js +1 -1
  249. package/dist/src/screenshot-runtime.js +1 -1
  250. package/dist/src/screenshot.js +1 -1
  251. package/dist/src/screenshot3.js +1 -1
  252. package/dist/src/script.js +1 -1
  253. package/dist/src/scroll-command.d.ts +377 -0
  254. package/dist/src/scroll-command.js +1 -1
  255. package/dist/src/scroll-gesture.js +1 -1
  256. package/dist/src/sdk-batch-runner.js +2 -2
  257. package/dist/src/sdk-batch.js +2 -2
  258. package/dist/src/sdk-contracts.d.ts +28 -0
  259. package/dist/src/sdk-contracts.js +1 -1
  260. package/dist/src/sdk-device.js +1 -1
  261. package/dist/src/sdk-io.js +1 -1
  262. package/dist/src/sdk-remote-config.js +1 -1
  263. package/dist/src/sdk-selectors.d.ts +23 -726
  264. package/dist/src/selection.js +0 -0
  265. package/dist/src/selector-runtime.js +1 -1
  266. package/dist/src/semantic-index.js +1 -1
  267. package/dist/src/server.js +1 -1
  268. package/dist/src/session-allocation.js +1 -1
  269. package/dist/src/session-observation.js +1 -1
  270. package/dist/src/session-routing.js +1 -1
  271. package/dist/src/session-runtime-admission.js +1 -1
  272. package/dist/src/session-snapshot.js +1 -1
  273. package/dist/src/session-store.js +2 -2
  274. package/dist/src/session2.js +3 -3
  275. package/dist/src/settings.js +1 -1
  276. package/dist/src/simctl-facade.js +1 -1
  277. package/dist/src/simctl.js +1 -1
  278. package/dist/src/simulator-state.js +1 -1
  279. package/dist/src/simulator.js +1 -1
  280. package/dist/src/snapshot-runtime-binding.js +1 -1
  281. package/dist/src/snapshot-runtime.js +1 -1
  282. package/dist/src/snapshot-state.js +1 -1
  283. package/dist/src/snapshot2.js +1 -1
  284. package/dist/src/snapshot3.js +1 -1
  285. package/dist/src/snapshot4.js +1 -1
  286. package/dist/src/src.js +1 -1
  287. package/dist/src/src7.js +1 -1
  288. package/dist/src/src8.js +2 -2
  289. package/dist/src/string-enum.js +1 -1
  290. package/dist/src/takeover.js +1 -1
  291. package/dist/src/text-entry-focus.js +1 -1
  292. package/dist/src/tool-provider-facade.js +1 -1
  293. package/dist/src/tool-provider.js +2 -2
  294. package/dist/src/tool-provider2.js +1 -1
  295. package/dist/src/touch-runtime.js +1 -1
  296. package/dist/src/transport.js +1 -1
  297. package/dist/src/tree.js +1 -1
  298. package/dist/src/tv-remote.js +1 -1
  299. package/dist/src/type-text-runtime.js +1 -1
  300. package/dist/src/update-check.js +1 -1
  301. package/dist/src/verified-file.js +1 -1
  302. package/dist/src/version.js +1 -1
  303. package/dist/src/video.js +1 -1
  304. package/dist/src/viewport.js +1 -1
  305. package/dist/src/web.js +1 -1
  306. package/dist/src/web2.js +1 -1
  307. package/dist/src/webdriver-source.js +1 -1
  308. package/package.json +3 -2
  309. package/server.json +2 -2
  310. package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.12.apk.sha256 +0 -1
  311. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.12.apk +0 -0
  312. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.12.apk.sha256 +0 -1
  313. package/dist/src/app-inventory.d.ts +0 -4
  314. package/dist/src/apps-simctl.js +0 -1
  315. package/dist/src/cloud-artifacts.d.ts +0 -61
  316. package/dist/src/runner-contract.js +0 -3
  317. package/dist/src/snapshot-process.js +0 -1
@@ -0,0 +1,630 @@
1
+ import XCTest
2
+ import AgentDeviceSnapshotPresentation
3
+
4
+ extension RunnerTests {
5
+
6
+
7
+ func executeAccepted(command: Command) throws -> Response {
8
+ commandJournal.start(command: command)
9
+ pendingTargetActivation = nil
10
+ do {
11
+ let response = try executeDispatched(command: command)
12
+ commandJournal.finish(command: command, response: response)
13
+ guard let fact = pendingTargetActivation else { return response }
14
+
15
+
16
+
17
+ pendingTargetActivation = nil
18
+ return response.stampingTargetActivation(fact)
19
+ } catch {
20
+ pendingTargetActivation = nil
21
+ commandJournal.fail(command: command, error: error)
22
+ throw error
23
+ }
24
+ }
25
+
26
+ func executeStatus(command: Command) -> Response {
27
+ guard
28
+ let statusCommandId = command.statusCommandId?.trimmedNonEmpty
29
+ else {
30
+ return Response(
31
+ ok: false,
32
+ error: ErrorPayload(
33
+ code: "INVALID_ARGS",
34
+ message: "status requires statusCommandId",
35
+ hint: "Set statusCommandId to the commandId of the runner command to inspect."
36
+ )
37
+ )
38
+ }
39
+ return Response(ok: true, data: commandJournal.status(normalizedCommandId: statusCommandId))
40
+ }
41
+
42
+ func executeUptime() -> Response {
43
+
44
+
45
+ Response(
46
+ ok: true,
47
+ data: DataPayload(currentUptimeMs: currentUptimeMs())
48
+ )
49
+ }
50
+
51
+ struct ActiveCommandContext {
52
+ let app: XCUIApplication
53
+
54
+ var systemSurface: SystemSurfaceHost? = nil
55
+ }
56
+
57
+ enum ActiveCommandPreparation {
58
+ case response(Response)
59
+ case context(ActiveCommandContext)
60
+ }
61
+
62
+ private func runnerBusyResponse(command: Command, abandonedForSeconds: TimeInterval) -> Response {
63
+ NSLog(
64
+ "AGENT_DEVICE_RUNNER_BUSY command=%@ commandId=%@ abandonedForSeconds=%.1f",
65
+ command.command.rawValue,
66
+ command.commandId ?? "",
67
+ abandonedForSeconds
68
+ )
69
+ return Response(
70
+ ok: false,
71
+ error: ErrorPayload(
72
+ code: "RUNNER_BUSY",
73
+ message:
74
+ "The iOS runner is still finishing a previous command that exceeded its execution watchdog (usually an accessibility capture on a heavy or animating screen).",
75
+ hint:
76
+ "Wait a few seconds and retry. If snapshots keep failing on this screen, use screenshot as visual truth and interact by coordinates, or navigate to another screen."
77
+ )
78
+ )
79
+ }
80
+
81
+ private func runnerWedgedResponse(command: Command, abandonedForSeconds: TimeInterval) -> Response {
82
+ NSLog(
83
+ "AGENT_DEVICE_RUNNER_WEDGED command=%@ commandId=%@ abandonedForSeconds=%.1f",
84
+ command.command.rawValue,
85
+ command.commandId ?? "",
86
+ abandonedForSeconds
87
+ )
88
+ return Response(
89
+ ok: false,
90
+ error: ErrorPayload(
91
+ code: "RUNNER_WEDGED",
92
+ message:
93
+ "The iOS runner main thread has been stuck in abandoned work for \(Int(abandonedForSeconds)) seconds and cannot recover on its own.",
94
+ hint:
95
+ "The runner session will be restarted. Retry the command after the restart; if this screen keeps wedging captures, use screenshot as visual truth and interact by coordinates."
96
+ )
97
+ )
98
+ }
99
+
100
+ private func runnerUnavailableResponse(command: Command) -> Response? {
101
+ switch currentMainThreadBusyState() {
102
+ case .idle:
103
+ return nil
104
+ case .busy(let abandonedForSeconds):
105
+ return runnerBusyResponse(command: command, abandonedForSeconds: abandonedForSeconds)
106
+ case .wedged(let abandonedForSeconds):
107
+ return runnerWedgedResponse(command: command, abandonedForSeconds: abandonedForSeconds)
108
+ }
109
+ }
110
+
111
+ func executeDispatched(command: Command) throws -> Response {
112
+
113
+
114
+
115
+
116
+ if let unavailable = runnerUnavailableResponse(command: command) {
117
+ return unavailable
118
+ }
119
+ let alertDeadline = command.command == .alert
120
+ ? Date().addingTimeInterval(Self.alertCommandTimeout(timeoutMs: command.timeoutMs))
121
+ : nil
122
+
123
+
124
+
125
+ let routeToSpringboard = shouldRouteToSpringboardBlockingSystemModal(command)
126
+ if let unavailable = runnerUnavailableResponse(command: command) {
127
+ return unavailable
128
+ }
129
+ if command.command == .snapshot {
130
+ return try executeSnapshotDispatched(command: command)
131
+ }
132
+ if command.command == .alert, let deadline = alertDeadline {
133
+ return try runMainThreadWork(
134
+ "command_execution",
135
+ timeout: max(0.001, deadline.timeIntervalSinceNow),
136
+ timeoutError: mainThreadExecutionTimeoutError
137
+ ) {
138
+ try self.executeOnMainSafely(
139
+ command: command,
140
+ alertDeadline: deadline,
141
+ routeToSpringboard: routeToSpringboard
142
+ )
143
+ }
144
+ }
145
+ return try runMainThreadWork(
146
+ "command_execution",
147
+ timeout: mainThreadExecutionTimeout,
148
+ timeoutError: mainThreadExecutionTimeoutError
149
+ ) {
150
+ try self.executeOnMainSafely(command: command, routeToSpringboard: routeToSpringboard)
151
+ }
152
+ }
153
+
154
+
155
+
156
+ private func executeOnMainSafely(
157
+ command: Command,
158
+ alertDeadline: Date? = nil,
159
+ routeToSpringboard: Bool
160
+ ) throws -> Response {
161
+ var hasRetried = false
162
+ while true {
163
+ var response: Response?
164
+ var swiftError: Error?
165
+ let failureCountBefore = currentXCTestFailureCount()
166
+ let exceptionMessage = RunnerObjCExceptionCatcher.catchException({
167
+ do {
168
+ response = try self.executeOnMain(
169
+ command: command,
170
+ alertDeadline: alertDeadline,
171
+ routeToSpringboard: routeToSpringboard
172
+ )
173
+ } catch {
174
+ swiftError = error
175
+ }
176
+ })
177
+
178
+ if let exceptionMessage {
179
+ invalidateCachedTarget(reason: "objc_exception")
180
+ if !hasRetried, shouldRetryException(command, message: exceptionMessage) {
181
+ NSLog(
182
+ "AGENT_DEVICE_RUNNER_RETRY command=%@ reason=objc_exception",
183
+ command.command.rawValue
184
+ )
185
+ hasRetried = true
186
+ sleepFor(retryCooldown)
187
+ continue
188
+ }
189
+ throw NSError(
190
+ domain: RunnerErrorDomain.exception,
191
+ code: RunnerErrorCode.objcException,
192
+ userInfo: [NSLocalizedDescriptionKey: exceptionMessage]
193
+ )
194
+ }
195
+ if let swiftError {
196
+ throw swiftError
197
+ }
198
+ guard let response else {
199
+ throw NSError(
200
+ domain: RunnerErrorDomain.general,
201
+ code: RunnerErrorCode.commandReturnedNoResponse,
202
+ userInfo: [NSLocalizedDescriptionKey: "command returned no response"]
203
+ )
204
+ }
205
+
206
+
207
+
208
+
209
+
210
+
211
+
212
+
213
+
214
+
215
+
216
+
217
+
218
+
219
+ if didRecordXCTestFailure(since: failureCountBefore),
220
+ let failureResponse = xctestRecordedFailureResponse(command: command, response: response)
221
+ {
222
+ invalidateCachedTarget(reason: "xctest_recorded_failure")
223
+ return failureResponse
224
+ }
225
+ if !hasRetried, shouldRetryCommand(command), shouldRetryResponse(response) {
226
+ NSLog(
227
+ "AGENT_DEVICE_RUNNER_RETRY command=%@ reason=response_unavailable",
228
+ command.command.rawValue
229
+ )
230
+ hasRetried = true
231
+ invalidateCachedTarget(reason: "response_unavailable")
232
+ sleepFor(retryCooldown)
233
+ continue
234
+ }
235
+ return response
236
+ }
237
+ }
238
+
239
+
240
+
241
+
242
+ func executeDispatchedWithRecovery(
243
+ command: Command,
244
+ perform: () throws -> Response
245
+ ) throws -> Response {
246
+ var hasRetried = false
247
+ while true {
248
+ let failureCountBefore = try runMainThreadWork(
249
+ "recorded_failure_count",
250
+ timeout: mainThreadExecutionTimeout,
251
+ timeoutError: mainThreadExecutionTimeoutError
252
+ ) {
253
+ self.currentXCTestFailureCount()
254
+ }
255
+ let response = try perform()
256
+
257
+
258
+ if hasAbandonedMainThreadWork() {
259
+ NSLog(
260
+ "AGENT_DEVICE_RUNNER_DISPATCH_RECOVERY_SKIPPED_XCTEST_OCCUPIED command=%@",
261
+ command.command.rawValue
262
+ )
263
+ return response
264
+ }
265
+ let recordedFailureResponse = try runMainThreadWork(
266
+ "recorded_failure_count",
267
+ timeout: mainThreadExecutionTimeout,
268
+ timeoutError: mainThreadExecutionTimeoutError
269
+ ) {
270
+ self.didRecordXCTestFailure(since: failureCountBefore)
271
+ ? self.xctestRecordedFailureResponse(command: command, response: response)
272
+ : nil
273
+ }
274
+ if let recordedFailureResponse {
275
+ try runMainThreadWork(
276
+ "target_invalidation",
277
+ timeout: mainThreadExecutionTimeout,
278
+ timeoutError: mainThreadExecutionTimeoutError
279
+ ) {
280
+ self.invalidateCachedTarget(reason: "xctest_recorded_failure")
281
+ }
282
+ return recordedFailureResponse
283
+ }
284
+ if !hasRetried, shouldRetryCommand(command), shouldRetryResponse(response) {
285
+ NSLog(
286
+ "AGENT_DEVICE_RUNNER_RETRY command=%@ reason=response_unavailable",
287
+ command.command.rawValue
288
+ )
289
+ hasRetried = true
290
+ try runMainThreadWork(
291
+ "target_invalidation",
292
+ timeout: mainThreadExecutionTimeout,
293
+ timeoutError: mainThreadExecutionTimeoutError
294
+ ) {
295
+ self.invalidateCachedTarget(reason: "response_unavailable")
296
+ self.sleepFor(self.retryCooldown)
297
+ }
298
+ continue
299
+ }
300
+ return response
301
+ }
302
+ }
303
+
304
+ private func executeOnMain(
305
+ command: Command,
306
+ alertDeadline: Date?,
307
+ routeToSpringboard: Bool
308
+ ) throws -> Response {
309
+ let preparation = prepareActiveCommandContext(
310
+ command: command,
311
+ routeToSpringboard: routeToSpringboard
312
+ )
313
+ let activeApp: XCUIApplication
314
+ switch preparation {
315
+ case .response(let response):
316
+ return response
317
+ case .context(let context):
318
+ activeApp = context.app
319
+ }
320
+
321
+ switch command.command {
322
+ case .status:
323
+ return executeStatus(command: command)
324
+ case .targetReset:
325
+ return resetTargetAfterExternalRelaunch()
326
+ case .shutdown:
327
+ stopRecordingIfNeeded()
328
+ return Response(ok: true, data: DataPayload(message: "shutdown"))
329
+ case .recordStart:
330
+ guard
331
+ let requestedOutPath = command.outPath?.trimmingCharacters(in: .whitespacesAndNewlines),
332
+ !requestedOutPath.isEmpty
333
+ else {
334
+ return Response(ok: false, error: ErrorPayload(message: "recordStart requires outPath"))
335
+ }
336
+ let hasAppBundleId = !(command.appBundleId?
337
+ .trimmingCharacters(in: .whitespacesAndNewlines)
338
+ .isEmpty ?? true)
339
+ guard hasAppBundleId else {
340
+ return Response(ok: false, error: ErrorPayload(message: "recordStart requires appBundleId"))
341
+ }
342
+ if activeRecording != nil {
343
+ return Response(ok: false, error: ErrorPayload(message: "recording already in progress"))
344
+ }
345
+ if let requestedFps = command.fps, (requestedFps < minRecordingFps || requestedFps > maxRecordingFps) {
346
+ return Response(ok: false, error: ErrorPayload(message: "recordStart fps must be between \(minRecordingFps) and \(maxRecordingFps)"))
347
+ }
348
+ do {
349
+ let resolvedOutPath = resolveRecordingOutPath(requestedOutPath)
350
+ let fpsLabel = command.fps.map(String.init) ?? String(RunnerTests.defaultRecordingFps)
351
+ NSLog(
352
+ "AGENT_DEVICE_RUNNER_RECORD_START requestedOutPath=%@ resolvedOutPath=%@ fps=%@",
353
+ requestedOutPath,
354
+ resolvedOutPath,
355
+ fpsLabel
356
+ )
357
+ let recorder = ScreenRecorder(
358
+ outputPath: resolvedOutPath,
359
+ fps: command.fps.map { Int32($0) }
360
+ )
361
+ try startRecording(recorder) { [weak self] in
362
+ guard let self else { return .failure(.unresolvedScreen) }
363
+ return self.captureRunnerFrameResult(app: activeApp)
364
+ }
365
+ activeRecording = recorder
366
+ return Response(ok: true, data: DataPayload(message: "recording started"))
367
+ } catch {
368
+ activeRecording = nil
369
+ return Response(ok: false, error: Self.recordingStartErrorPayload(for: error))
370
+ }
371
+ case .recordStop:
372
+ guard let recorder = activeRecording else {
373
+
374
+
375
+
376
+
377
+ return Response(ok: true, data: DataPayload(message: "recording already stopped"))
378
+ }
379
+ do {
380
+ try recorder.stop()
381
+ activeRecording = nil
382
+ return Response(ok: true, data: DataPayload(message: "recording stopped"))
383
+ } catch {
384
+ activeRecording = nil
385
+ return Response(ok: false, error: ErrorPayload(message: "failed to stop recording: \(error.localizedDescription)"))
386
+ }
387
+ case .uptime:
388
+ return executeUptime()
389
+ case .activate:
390
+ guard
391
+ let bundleId = command.appBundleId?.trimmingCharacters(in: .whitespacesAndNewlines),
392
+ !bundleId.isEmpty
393
+ else {
394
+ return Response(ok: false, error: ErrorPayload(message: "activate requires appBundleId"))
395
+ }
396
+
397
+
398
+ return Response(ok: true, data: DataPayload(message: "app activated"))
399
+ case .terminate:
400
+ guard
401
+ let bundleId = command.appBundleId?.trimmingCharacters(in: .whitespacesAndNewlines),
402
+ !bundleId.isEmpty
403
+ else {
404
+ return Response(ok: false, error: ErrorPayload(message: "terminate requires appBundleId"))
405
+ }
406
+ XCUIApplication(bundleIdentifier: bundleId).terminate()
407
+ if currentBundleId == bundleId {
408
+ invalidateCachedTarget(reason: "target_terminated")
409
+ }
410
+ return Response(ok: true, data: DataPayload(message: "app terminated"))
411
+ default:
412
+ break
413
+ }
414
+ return try executeOnMainPrepared(
415
+ command: command,
416
+ activeApp: activeApp,
417
+ alertDeadline: alertDeadline
418
+ )
419
+ }
420
+
421
+ func prepareActiveCommandContext(
422
+ command: Command,
423
+ routeToSpringboard: Bool = false
424
+ ) -> ActiveCommandPreparation {
425
+ var activeApp = currentApp ?? app
426
+ var systemSurface: SystemSurfaceHost? = nil
427
+ if routeToSpringboard {
428
+ activeApp = springboard
429
+ } else if shouldSkipAppActivationPreflight(command) {
430
+ activeApp = resolveAppWithoutActivation(command: command)
431
+ } else if let presented = presentedSystemSurfaceHost() {
432
+
433
+
434
+
435
+ activeApp = presented.app
436
+ systemSurface = presented.host
437
+ if isInteractionCommand(command.command) {
438
+ applyInteractionStabilizationIfNeeded()
439
+ }
440
+ } else if !isRunnerLifecycleCommand(command.command) {
441
+ let normalizedBundleId = command.appBundleId?
442
+ .trimmingCharacters(in: .whitespacesAndNewlines)
443
+ let requestedBundleId = (normalizedBundleId?.isEmpty == true) ? nil : normalizedBundleId
444
+ if let bundleId = requestedBundleId,
445
+ let notRunning = notRunningReadResponse(command: command, bundleId: bundleId)
446
+ {
447
+ return .response(notRunning)
448
+ }
449
+ if let bundleId = requestedBundleId {
450
+ if currentBundleId != bundleId || currentApp == nil {
451
+ _ = activateTarget(bundleId: bundleId, reason: "bundle_changed")
452
+ } else {
453
+ refreshCachedTargetIfProcessChanged(bundleId: bundleId)
454
+ }
455
+ } else {
456
+
457
+ invalidateCachedTarget(reason: "missing_app_bundle")
458
+ }
459
+
460
+ activeApp = currentApp ?? app
461
+ if let bundleId = requestedBundleId, targetNeedsActivation(activeApp) {
462
+ activeApp = activateTarget(bundleId: bundleId, reason: "stale_target")
463
+ } else if requestedBundleId == nil, targetNeedsActivation(activeApp) {
464
+ ensureRunnerHostAppActive(reason: "missing_app_bundle")
465
+ activeApp = app
466
+ }
467
+
468
+ let skipExistenceWait = canUseFastForegroundAppGuard(
469
+ activeApp: activeApp,
470
+ requestedBundleId: requestedBundleId
471
+ )
472
+ if !skipExistenceWait && !activeApp.waitForExistence(timeout: appExistenceTimeout) {
473
+ if let bundleId = requestedBundleId {
474
+ activeApp = activateTarget(bundleId: bundleId, reason: "missing_after_wait")
475
+ guard activeApp.waitForExistence(timeout: appExistenceTimeout) else {
476
+ return .response(Response(ok: false, error: .targetAppUnavailable(bundleId: bundleId)))
477
+ }
478
+ } else {
479
+ return .response(Response(ok: false, error: .targetAppUnavailable(bundleId: nil)))
480
+ }
481
+ }
482
+
483
+ if isInteractionCommand(command.command) {
484
+ if let bundleId = requestedBundleId, activeApp.state != .runningForeground {
485
+ activeApp = activateTarget(bundleId: bundleId, reason: "interaction_foreground_guard")
486
+ } else if requestedBundleId == nil, activeApp.state != .runningForeground {
487
+ ensureRunnerHostAppActive(reason: "interaction_missing_app_bundle")
488
+ activeApp = app
489
+ }
490
+ let skipInteractionExistenceWait = canUseFastForegroundAppGuard(
491
+ activeApp: activeApp,
492
+ requestedBundleId: requestedBundleId
493
+ )
494
+ if !skipInteractionExistenceWait && !activeApp.waitForExistence(timeout: 2) {
495
+ return .response(
496
+ Response(ok: false, error: .targetAppUnavailable(bundleId: requestedBundleId))
497
+ )
498
+ }
499
+ applyInteractionStabilizationIfNeeded()
500
+ }
501
+ }
502
+ return .context(ActiveCommandContext(app: activeApp, systemSurface: systemSurface))
503
+ }
504
+
505
+
506
+
507
+
508
+
509
+ private func presentedSystemSurfaceHost() -> (host: SystemSurfaceHost, app: XCUIApplication)? {
510
+ #if os(iOS)
511
+ for host in SystemSurfaceHostRegistry.hosts {
512
+ let candidate = XCUIApplication(bundleIdentifier: host.bundleId)
513
+ if candidate.state == .runningForeground {
514
+ return (host, candidate)
515
+ }
516
+ }
517
+ return nil
518
+ #else
519
+ return nil
520
+ #endif
521
+ }
522
+
523
+ func currentXCTestFailureCount() -> Int {
524
+ return testRun?.failureCount ?? 0
525
+ }
526
+
527
+ func didRecordXCTestFailure(since failureCountBefore: Int) -> Bool {
528
+ return currentXCTestFailureCount() > failureCountBefore
529
+ }
530
+
531
+ func xctestRecordedFailureResponse(command: Command, response: Response) -> Response? {
532
+ guard response.ok else { return nil }
533
+ if response.data?.runnerFatal == true {
534
+ return nil
535
+ }
536
+ guard !isReadOnlyCommand(command), !isRunnerLifecycleCommand(command.command) else {
537
+ return nil
538
+ }
539
+ return Response(
540
+ ok: false,
541
+ error: ErrorPayload(
542
+ code: "XCTEST_RECORDED_FAILURE",
543
+ message: "XCTest recorded a failure while executing \(command.command.rawValue); the action may not have been performed.",
544
+ hint: "The iOS runner session was invalidated. Re-observe with a fresh snapshot before retrying; if the accessibility tree is unavailable, use screenshot plus coordinate commands instead of retrying the tap blindly."
545
+ )
546
+ )
547
+ }
548
+
549
+ func shouldSkipAppActivationPreflight(_ command: Command) -> Bool {
550
+ #if os(iOS)
551
+ if command.command == .alert {
552
+ return true
553
+ }
554
+
555
+
556
+
557
+
558
+
559
+ if command.command == .actionButton {
560
+ return true
561
+ }
562
+
563
+
564
+
565
+
566
+ guard command.text == nil, command.selectorKey == nil else { return false }
567
+ guard hasCachedTargetForActivationSkip(command: command) else { return false }
568
+ return isCoordinateOnlyTap(command)
569
+ #else
570
+ return false
571
+ #endif
572
+ }
573
+
574
+ func shouldRouteToSpringboardBlockingSystemModal(
575
+ _ command: Command
576
+ ) -> Bool {
577
+ #if os(iOS)
578
+ guard isCoordinateOnlyTap(command) else {
579
+ return false
580
+ }
581
+
582
+
583
+
584
+
585
+
586
+ let probeDeadline = Date().addingTimeInterval(systemModalProbeBudget)
587
+
588
+
589
+
590
+
591
+ return boundedBlockingSystemAlertSnapshot(
592
+ deadline: probeDeadline,
593
+ penaltyTarget: .mainOwnedTarget
594
+ ) != nil
595
+ #else
596
+ return false
597
+ #endif
598
+ }
599
+
600
+ private func isCoordinateOnlyTap(_ command: Command) -> Bool {
601
+ return command.command == .tap
602
+ && command.text == nil
603
+ && command.selectorKey == nil
604
+ && command.x != nil
605
+ && command.y != nil
606
+ }
607
+
608
+ private func hasCachedTargetForActivationSkip(command: Command) -> Bool {
609
+ guard let currentApp, currentApp.state == .runningForeground else { return false }
610
+ guard let bundleId = command.appBundleId?.trimmingCharacters(in: .whitespacesAndNewlines),
611
+ !bundleId.isEmpty
612
+ else {
613
+ return true
614
+ }
615
+ return currentBundleId == bundleId
616
+ }
617
+
618
+ func resolveAppWithoutActivation(command: Command) -> XCUIApplication {
619
+ guard let bundleId = command.appBundleId?
620
+ .trimmingCharacters(in: .whitespacesAndNewlines),
621
+ !bundleId.isEmpty
622
+ else {
623
+ return currentApp ?? app
624
+ }
625
+ if currentBundleId == bundleId, let currentApp {
626
+ return currentApp
627
+ }
628
+ return XCUIApplication(bundleIdentifier: bundleId)
629
+ }
630
+ }