agent-device 0.21.13 → 0.21.15

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 (298) hide show
  1. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.21.13.apk → agent-device-android-ime-helper-0.21.15.apk} +0 -0
  2. package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.15.apk.sha256 +1 -0
  3. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.21.13.manifest.json → agent-device-android-ime-helper-0.21.15.manifest.json} +4 -4
  4. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.15.apk +0 -0
  5. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.15.apk.sha256 +1 -0
  6. package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.21.13.manifest.json → agent-device-android-snapshot-helper-0.21.15.manifest.json} +6 -6
  7. package/apple/snapshot-bridge/SnapshotBridgeRuntime.m +11 -1
  8. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunner/AgentDeviceRunnerApp.m +110 -2
  9. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.m +4 -2
  10. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerMainOwnedState.swift +27 -0
  11. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedTextEntry.h +8 -2
  12. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedTextEntry.m +26 -1
  13. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+AXSnapshotFallback.swift +12 -19
  14. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Alert.swift +59 -20
  15. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+AlertObservation.swift +2 -3
  16. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandDispatch.swift +179 -91
  17. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +21 -24
  18. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandJournal.swift +1 -1
  19. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+GestureExecution.swift +9 -13
  20. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Lifecycle.swift +36 -49
  21. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+MainThreadWork.swift +11 -15
  22. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +136 -29
  23. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Navigation.swift +20 -21
  24. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+PrivateAXPresentation.swift +3 -0
  25. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScreenRecorder.swift +6 -5
  26. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScrollDragExecution.swift +12 -24
  27. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SequenceExecution.swift +22 -42
  28. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +15 -18
  29. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotAcquisition.swift +73 -31
  30. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCapturePlan.swift +20 -11
  31. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCaptureTarget.swift +9 -7
  32. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotExecution.swift +5 -4
  33. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotTiming.swift +2 -2
  34. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedGesturePolicy.swift +51 -19
  35. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedInteraction.swift +5 -2
  36. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedTextEntry.swift +78 -10
  37. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntry.swift +32 -2
  38. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntryFocus.swift +7 -4
  39. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextTyping.swift +23 -0
  40. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Transport.swift +17 -10
  41. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TvRemote.swift +8 -9
  42. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TypeExecution.swift +9 -15
  43. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +12 -11
  44. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotCoordinateSpace.swift +16 -19
  45. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotGeometry.swift +24 -9
  46. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotModels.swift +66 -10
  47. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotPresentationInvariant.swift +12 -10
  48. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotPresentationProjection.swift +1 -0
  49. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotVisibilityFold.swift +27 -5
  50. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotVisibilityFoldProjection.swift +1 -0
  51. package/dist/src/adapter.js +1 -1
  52. package/dist/src/adb-executor.js +1 -1
  53. package/dist/src/adb-failure.js +1 -1
  54. package/dist/src/adb-transport.js +1 -1
  55. package/dist/src/agent-browser-provider.js +1 -1
  56. package/dist/src/agent-browser-tool.js +1 -1
  57. package/dist/src/agent-device-client.js +2 -2
  58. package/dist/src/ai-sdk.js +1 -1
  59. package/dist/src/alert-runtime.js +1 -1
  60. package/dist/src/android-adb.d.ts +1 -8
  61. package/dist/src/android-input-ownership.js +1 -1
  62. package/dist/src/android-snapshot-quality.js +1 -0
  63. package/dist/src/android-system-chrome.js +1 -1
  64. package/dist/src/android.js +1 -1
  65. package/dist/src/app-catalog.js +1 -1
  66. package/dist/src/app-deployment-resolution.js +1 -1
  67. package/dist/src/app-inventory.js +1 -1
  68. package/dist/src/app-launch.js +1 -1
  69. package/dist/src/app-lifecycle.js +1 -1
  70. package/dist/src/app-lifecycle2.js +1 -1
  71. package/dist/src/app-log-files.js +1 -1
  72. package/dist/src/app-log-runtime2.js +1 -1
  73. package/dist/src/app-resolution.js +2 -2
  74. package/dist/src/apple-runner-platform.js +1 -1
  75. package/dist/src/application-lifecycle-interaction.js +1 -1
  76. package/dist/src/archive.js +1 -1
  77. package/dist/src/artifact-download.js +1 -1
  78. package/dist/src/auth-session.js +1 -1
  79. package/dist/src/auth.js +1 -1
  80. package/dist/src/backend-snapshot-options.js +1 -1
  81. package/dist/src/boot-diagnostics.js +1 -1
  82. package/dist/src/capture-disclosure.js +1 -1
  83. package/dist/src/capture.js +1 -1
  84. package/dist/src/cli-config.js +1 -1
  85. package/dist/src/cli-help.js +2 -2
  86. package/dist/src/cli.js +4 -4
  87. package/dist/src/click-button.js +1 -1
  88. package/dist/src/client-metro.js +1 -1
  89. package/dist/src/client-types.d.ts +102 -95
  90. package/dist/src/clipboard-shell-response.js +1 -1
  91. package/dist/src/command-schema.js +1 -1
  92. package/dist/src/command-tools.js +3 -3
  93. package/dist/src/connection-runtime.js +1 -1
  94. package/dist/src/connection.js +1 -1
  95. package/dist/src/contracts.d.ts +1 -1
  96. package/dist/src/contracts.js +1 -1
  97. package/dist/src/daemon-client-lifecycle.js +1 -1
  98. package/dist/src/daemon.js +1 -1
  99. package/dist/src/debug-symbols-facade.js +1 -1
  100. package/dist/src/device-claim-rule.js +2 -2
  101. package/dist/src/device-input-state.d.ts +15 -2
  102. package/dist/src/device-inventory-context.js +1 -1
  103. package/dist/src/device-rotation.js +1 -1
  104. package/dist/src/device-selection-resolver.js +1 -1
  105. package/dist/src/device-session.js +1 -1
  106. package/dist/src/device-shell.js +1 -1
  107. package/dist/src/device2.js +1 -1
  108. package/dist/src/devicectl.js +1 -1
  109. package/dist/src/diagnostics.js +1 -1
  110. package/dist/src/dispatch-resolve.js +1 -1
  111. package/dist/src/doctor2.js +1 -1
  112. package/dist/src/engine-eval-script.js +1 -1
  113. package/dist/src/error.js +1 -1
  114. package/dist/src/exec.js +1 -1
  115. package/dist/src/file.js +1 -1
  116. package/dist/src/find.js +1 -1
  117. package/dist/src/format.js +1 -1
  118. package/dist/src/gesture-plan-types.d.ts +1 -1
  119. package/dist/src/gesture-plan.js +1 -1
  120. package/dist/src/gesture-runtime.js +1 -1
  121. package/dist/src/harmonyos.js +1 -1
  122. package/dist/src/helper.js +2 -2
  123. package/dist/src/human-control-contract.js +1 -1
  124. package/dist/src/human-control.js +1 -1
  125. package/dist/src/index.d.ts +7 -3
  126. package/dist/src/index.js +1 -1
  127. package/dist/src/input-actions.js +1 -1
  128. package/dist/src/input-actions2.js +1 -1
  129. package/dist/src/install-artifact-facade.js +1 -1
  130. package/dist/src/install-source-network-transport.js +1 -1
  131. package/dist/src/install-source2.js +1 -1
  132. package/dist/src/interaction-positionals.js +1 -1
  133. package/dist/src/interaction-ref-policy.js +1 -1
  134. package/dist/src/interaction2.js +1 -1
  135. package/dist/src/interactor-operation-binding.js +1 -1
  136. package/dist/src/interactor.js +4 -4
  137. package/dist/src/interactor2.js +1 -1
  138. package/dist/src/interactors.js +1 -1
  139. package/dist/src/internal/bin.js +1 -1
  140. package/dist/src/internal/daemon.js +2 -2
  141. package/dist/src/internal/png-worker.js +1 -1
  142. package/dist/src/internal/run-script-http-child.js +1 -1
  143. package/dist/src/inventory.js +1 -1
  144. package/dist/src/inventory2.js +1 -1
  145. package/dist/src/inventory3.js +1 -1
  146. package/dist/src/inventory5.js +1 -1
  147. package/dist/src/ios-snapshot-acquisition.js +1 -1
  148. package/dist/src/ios-snapshot-engine.js +1 -0
  149. package/dist/src/ios-snapshot-runtime.js +1 -1
  150. package/dist/src/lease.js +1 -1
  151. package/dist/src/limrun-download-file.js +1 -1
  152. package/dist/src/limrun-runtime-dependencies.js +1 -1
  153. package/dist/src/limrun.d.ts +16 -3
  154. package/dist/src/linux.js +1 -1
  155. package/dist/src/location-coordinates.js +1 -1
  156. package/dist/src/managed-device-scope.js +1 -1
  157. package/dist/src/mechanics.js +17 -17
  158. package/dist/src/mobile-snapshot-semantics.js +1 -1
  159. package/dist/src/multitouch-support.js +1 -1
  160. package/dist/src/native-build-cache.js +1 -0
  161. package/dist/src/observation.js +1 -1
  162. package/dist/src/overlay.js +3 -0
  163. package/dist/src/parameterized-recorded-fill.js +1 -1
  164. package/dist/src/perf-facade.js +1 -1
  165. package/dist/src/perf-runtime-operation-builder.js +1 -1
  166. package/dist/src/perf-runtime-plan.js +1 -1
  167. package/dist/src/perf-target.js +2 -2
  168. package/dist/src/perf.js +1 -1
  169. package/dist/src/physical-device-control.js +1 -1
  170. package/dist/src/platform-runtime-host.js +1 -1
  171. package/dist/src/platform-runtime-managed-owner.js +1 -1
  172. package/dist/src/platform-runtime-open-target.js +1 -1
  173. package/dist/src/platform-runtime-operation-host.js +4 -4
  174. package/dist/src/platform-runtime-runtime-hints.js +1 -1
  175. package/dist/src/platform-runtime-screen-recording-apple-simulator-host.js +1 -1
  176. package/dist/src/platform-runtime-screen-recording-finalizer-host.js +1 -1
  177. package/dist/src/platform-runtime-screen-recording-process-host.js +1 -1
  178. package/dist/src/platform-runtime.js +1 -1
  179. package/dist/src/platform-runtime2.js +1 -1
  180. package/dist/src/png-crop-bytes.js +1 -1
  181. package/dist/src/png-size.js +1 -1
  182. package/dist/src/png-worker-client.js +1 -1
  183. package/dist/src/png.js +1 -1
  184. package/dist/src/prepare-kind.js +1 -1
  185. package/dist/src/provider-session-ownership.js +1 -1
  186. package/dist/src/provider-webdriver.js +1 -1
  187. package/dist/src/proxy.js +1 -1
  188. package/dist/src/quality-warnings.js +1 -1
  189. package/dist/src/react-native.js +1 -1
  190. package/dist/src/record-runtime.js +1 -1
  191. package/dist/src/recording.js +1 -1
  192. package/dist/src/rect-center.js +1 -1
  193. package/dist/src/register-builtins.js +1 -1
  194. package/dist/src/registry.js +36 -36
  195. package/dist/src/remote-config2.js +1 -1
  196. package/dist/src/replay-divergence.js +1 -1
  197. package/dist/src/replay.js +1 -1
  198. package/dist/src/reporting.js +1 -1
  199. package/dist/src/request-admission.js +1 -1
  200. package/dist/src/request-cancel.js +1 -1
  201. package/dist/src/request-runtime-binding.js +1 -1
  202. package/dist/src/retry.js +1 -1
  203. package/dist/src/runner-cache-metadata.js +1 -1
  204. package/dist/src/runner-client.js +1 -1
  205. package/dist/src/runner-dev-tools-security.js +1 -1
  206. package/dist/src/runner-device-readiness.js +1 -1
  207. package/dist/src/runner-disposal.js +1 -1
  208. package/dist/src/runner-error-classification.js +1 -1
  209. package/dist/src/runner.js +1 -1
  210. package/dist/src/runtime-transport-hints.js +1 -1
  211. package/dist/src/runtime.js +1 -3
  212. package/dist/src/runtime10.js +2 -2
  213. package/dist/src/runtime2.js +4 -4
  214. package/dist/src/runtime3.js +1 -1
  215. package/dist/src/runtime4.js +5 -5
  216. package/dist/src/runtime5.js +1 -1
  217. package/dist/src/runtime6.js +1 -1
  218. package/dist/src/runtime7.js +1 -1
  219. package/dist/src/runtime8.js +1 -1
  220. package/dist/src/runtime9.js +1 -1
  221. package/dist/src/screen-recording-admission-ledger.js +1 -1
  222. package/dist/src/screen-recording-completion.js +1 -1
  223. package/dist/src/screen-recording-options.js +1 -1
  224. package/dist/src/screen-recording-resource-recovery.js +1 -1
  225. package/dist/src/screenshot-density.js +1 -1
  226. package/dist/src/screenshot-image.js +1 -1
  227. package/dist/src/screenshot-overlay-draw.js +1 -1
  228. package/dist/src/screenshot-runtime.js +1 -1
  229. package/dist/src/screenshot.js +1 -1
  230. package/dist/src/screenshot3.js +1 -1
  231. package/dist/src/script.js +1 -1
  232. package/dist/src/scroll-command.d.ts +13 -6
  233. package/dist/src/scroll-command.js +1 -1
  234. package/dist/src/scroll-gesture.js +1 -1
  235. package/dist/src/sdk-batch-runner.js +2 -2
  236. package/dist/src/sdk-batch.js +2 -2
  237. package/dist/src/sdk-contracts.d.ts +59 -12
  238. package/dist/src/sdk-contracts.js +2 -2
  239. package/dist/src/sdk-device.js +1 -1
  240. package/dist/src/sdk-finders.d.ts +1 -1
  241. package/dist/src/sdk-io.js +1 -1
  242. package/dist/src/sdk-remote-config.js +1 -1
  243. package/dist/src/sdk-selectors.d.ts +1 -1
  244. package/dist/src/selection.js +0 -0
  245. package/dist/src/selector-runtime.js +1 -1
  246. package/dist/src/server.js +1 -1
  247. package/dist/src/session-allocation.js +1 -1
  248. package/dist/src/session-routing.js +1 -1
  249. package/dist/src/session-runtime-admission.js +1 -1
  250. package/dist/src/session-snapshot.js +1 -1
  251. package/dist/src/session-store.js +1 -1
  252. package/dist/src/session.js +1 -1
  253. package/dist/src/session2.js +4 -4
  254. package/dist/src/settings.js +1 -1
  255. package/dist/src/settings2.js +1 -0
  256. package/dist/src/simctl.js +1 -1
  257. package/dist/src/simulator-state.js +1 -1
  258. package/dist/src/simulator.js +1 -1
  259. package/dist/src/snapshot-capture-annotations.js +1 -1
  260. package/dist/src/snapshot-desktop-projection.js +1 -1
  261. package/dist/src/snapshot-runtime-binding.js +1 -1
  262. package/dist/src/snapshot-runtime.js +1 -1
  263. package/dist/src/snapshot-state.js +1 -1
  264. package/dist/src/snapshot2.js +1 -1
  265. package/dist/src/snapshot3.js +1 -1
  266. package/dist/src/snapshot4.js +1 -1
  267. package/dist/src/src.js +1 -1
  268. package/dist/src/src4.js +1 -1
  269. package/dist/src/src7.js +1 -1
  270. package/dist/src/src8.js +1 -1
  271. package/dist/src/string-enum.js +1 -1
  272. package/dist/src/surface-snapshot.js +1 -1
  273. package/dist/src/takeover.js +1 -1
  274. package/dist/src/text-entry-focus.js +1 -1
  275. package/dist/src/tool-provider-facade.js +1 -1
  276. package/dist/src/tool-provider.js +2 -2
  277. package/dist/src/tool-provider2.js +1 -1
  278. package/dist/src/touch-runtime.js +1 -1
  279. package/dist/src/transport.js +1 -1
  280. package/dist/src/tree.js +1 -1
  281. package/dist/src/tv-remote.js +1 -1
  282. package/dist/src/type-text-runtime.js +1 -1
  283. package/dist/src/verified-file.js +1 -1
  284. package/dist/src/video.js +1 -1
  285. package/dist/src/viewport.js +1 -1
  286. package/dist/src/web.js +1 -1
  287. package/dist/src/web2.js +1 -1
  288. package/dist/src/webdriver-source.js +1 -1
  289. package/package.json +1 -1
  290. package/server.json +2 -2
  291. package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.13.apk.sha256 +0 -1
  292. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.13.apk +0 -0
  293. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.13.apk.sha256 +0 -1
  294. package/dist/src/absence-observation-errors.js +0 -1
  295. package/dist/src/runner-presentation.js +0 -1
  296. package/dist/src/semantic-index.js +0 -1
  297. package/dist/src/snapshot-process.js +0 -1
  298. package/dist/src/snapshot-scope.js +0 -1
@@ -180,6 +180,7 @@ extension RunnerTests {
180
180
 
181
181
 
182
182
 
183
+ @MainActor
183
184
  func ensureRunnerHostAppActive(reason: String) {
184
185
  NSLog(
185
186
  "AGENT_DEVICE_RUNNER_HOST_ACTIVATE state=%d reason=%@",
@@ -191,9 +192,9 @@ extension RunnerTests {
191
192
  } else if app.state != .runningForeground {
192
193
  app.activate()
193
194
  }
194
- currentApp = app
195
- currentBundleId = nil
196
- currentAppProcessIdentifier = nil
195
+ mainOwned.app = app
196
+ mainOwned.bundleId = nil
197
+ mainOwned.processIdentifier = nil
197
198
  resetTargetBoundState()
198
199
  }
199
200
 
@@ -207,16 +208,18 @@ extension RunnerTests {
207
208
  lastLoggedGesturePolicyLines.removeAll()
208
209
  }
209
210
 
211
+ @MainActor
210
212
  func invalidateCachedTarget(reason: String) {
211
- if currentApp != nil || currentBundleId != nil {
213
+ if mainOwned.app != nil || mainOwned.bundleId != nil {
212
214
  NSLog("AGENT_DEVICE_RUNNER_TARGET_CACHE_INVALIDATE reason=%@", reason)
213
215
  }
214
- currentApp = nil
215
- currentBundleId = nil
216
- currentAppProcessIdentifier = nil
216
+ mainOwned.app = nil
217
+ mainOwned.bundleId = nil
218
+ mainOwned.processIdentifier = nil
217
219
  resetTargetBoundState()
218
220
  }
219
221
 
222
+ @MainActor
220
223
  func resetTargetAfterExternalRelaunch() -> Response {
221
224
  invalidateCachedTarget(reason: "external_app_relaunch")
222
225
 
@@ -228,22 +231,23 @@ extension RunnerTests {
228
231
  return Response(ok: true, data: DataPayload(message: "target reset"))
229
232
  }
230
233
 
234
+ @MainActor
231
235
  func refreshCachedTargetIfProcessChanged(bundleId: String) {
232
- guard currentBundleId == bundleId, currentApp != nil else { return }
236
+ guard mainOwned.bundleId == bundleId, mainOwned.app != nil else { return }
233
237
  let candidate = XCUIApplication(bundleIdentifier: bundleId)
234
238
  let observedProcessIdentifier = Self.processIdentifier(of: candidate)
235
239
  guard Self.shouldRefreshCachedTarget(
236
- cachedProcessIdentifier: currentAppProcessIdentifier,
240
+ cachedProcessIdentifier: mainOwned.processIdentifier,
237
241
  observedProcessIdentifier: observedProcessIdentifier
238
242
  ) else { return }
239
243
  NSLog(
240
244
  "AGENT_DEVICE_RUNNER_TARGET_CACHE_REFRESH bundle=%@ previousPid=%d currentPid=%d",
241
245
  bundleId,
242
- currentAppProcessIdentifier ?? 0,
246
+ mainOwned.processIdentifier ?? 0,
243
247
  observedProcessIdentifier ?? 0
244
248
  )
245
- currentApp = candidate
246
- currentAppProcessIdentifier = observedProcessIdentifier
249
+ mainOwned.app = candidate
250
+ mainOwned.processIdentifier = observedProcessIdentifier
247
251
  resetTargetBoundState()
248
252
  clearSnapshotXCTestChannelPenalty(reason: "target_process_changed")
249
253
  clearPrivateAXAcceptedDepth(reason: "target_process_changed")
@@ -280,11 +284,12 @@ extension RunnerTests {
280
284
  return false
281
285
  }
282
286
 
287
+ @MainActor
283
288
  func canUseFastForegroundAppGuard(
284
289
  activeApp: XCUIApplication,
285
290
  requestedBundleId: String?
286
291
  ) -> Bool {
287
- guard let requestedBundleId, currentBundleId == requestedBundleId, currentApp != nil else {
292
+ guard let requestedBundleId, mainOwned.bundleId == requestedBundleId, mainOwned.app != nil else {
288
293
  return false
289
294
  }
290
295
  guard activeApp.state == .runningForeground else { return false }
@@ -292,6 +297,7 @@ extension RunnerTests {
292
297
  return true
293
298
  }
294
299
 
300
+ @MainActor
295
301
  func writeFastAppGuardMarker(bundleId: String, state: XCUIApplication.State) {
296
302
 
297
303
 
@@ -318,9 +324,10 @@ extension RunnerTests {
318
324
 
319
325
 
320
326
 
321
- func notRunningReadResponse(command: Command, bundleId: String) -> Response? {
327
+
328
+ func notRunningRefusal(command: Command, bundleId: String) -> Response? {
322
329
  #if os(iOS)
323
- guard isReadOnlyCommand(command),
330
+ guard command.traits.launchPolicy == .existingApp,
324
331
  XCUIApplication(bundleIdentifier: bundleId).state == .notRunning
325
332
  else { return nil }
326
333
  NSLog(
@@ -341,6 +348,7 @@ extension RunnerTests {
341
348
  #endif
342
349
  }
343
350
 
351
+ @MainActor
344
352
  func activateTarget(bundleId: String, reason: String) -> XCUIApplication {
345
353
  let target = XCUIApplication(bundleIdentifier: bundleId)
346
354
  let initialState = target.state
@@ -374,9 +382,9 @@ extension RunnerTests {
374
382
  otherActiveApplicationPid.map(String.init) ?? "-"
375
383
  )
376
384
  }
377
- currentApp = target
378
- currentBundleId = bundleId
379
- currentAppProcessIdentifier = Self.processIdentifier(of: target)
385
+ mainOwned.app = target
386
+ mainOwned.bundleId = bundleId
387
+ mainOwned.processIdentifier = Self.processIdentifier(of: target)
380
388
  resetTargetBoundState()
381
389
  beginFirstInteractionStabilization()
382
390
  return target
@@ -387,10 +395,11 @@ extension RunnerTests {
387
395
 
388
396
 
389
397
 
398
+ @MainActor
390
399
  func withBoundedInteractionIdleTimeoutIfSupported(
391
400
  _ target: XCUIApplication,
392
401
  waits: RunnerInteractionIdleWaits,
393
- operation: () -> Void
402
+ operation: @MainActor () -> Void
394
403
  ) {
395
404
  let setter = NSSelectorFromString("setWaitForIdleTimeout:")
396
405
  let supportsWaitForIdleTimeout = target.responds(to: setter)
@@ -409,10 +418,11 @@ extension RunnerTests {
409
418
  }
410
419
 
411
420
 
421
+ @MainActor
412
422
  private func performWithQuiescenceSkippedIfSupported(
413
423
  _ target: XCUIApplication,
414
424
  waits: RunnerInteractionIdleWaits,
415
- operation: () -> Void
425
+ operation: @MainActor () -> Void
416
426
  ) {
417
427
  let selector = NSSelectorFromString("_performWithInteractionOptions:block:")
418
428
  guard target.responds(to: selector) else {
@@ -440,7 +450,7 @@ extension RunnerTests {
440
450
  options = skipPreEventQuiescence
441
451
  }
442
452
  withoutActuallyEscaping(operation) { escapableOperation in
443
- let block: @convention(block) () -> Void = escapableOperation
453
+ let block: @convention(block) () -> Void = { _ = runOnMainActor(escapableOperation) }
444
454
  performWithOptions(
445
455
  target,
446
456
  selector,
@@ -450,49 +460,26 @@ extension RunnerTests {
450
460
  }
451
461
  }
452
462
 
453
- func shouldRetryCommand(_ command: Command) -> Bool {
454
- isReadOnlyCommand(command)
455
- }
463
+
456
464
 
457
465
  func shouldRetryException(_ command: Command, message: String) -> Bool {
458
- guard shouldRetryCommand(command) else { return false }
466
+ guard command.traits.retryOnSessionLoss else { return false }
459
467
 
460
468
  return message.lowercased().contains("kaxerrorservernotfound")
461
469
  }
462
470
 
463
-
464
-
465
- func isReadOnlyCommand(_ command: Command) -> Bool {
466
- switch command.command.traits.readOnly {
467
- case .always:
468
- return true
469
- case .never:
470
- return false
471
- case .conditional:
472
-
473
- return (command.action ?? "get").lowercased() == "get"
474
- }
475
- }
476
-
477
471
  func shouldRetryResponse(_ response: Response) -> Bool {
478
472
  guard response.ok == false else { return false }
479
473
  return response.error?.retryableFailure != nil
480
474
  }
481
475
 
482
- func isInteractionCommand(_ command: CommandType) -> Bool {
483
- return command.traits.isInteraction
484
- }
485
-
486
- func isRunnerLifecycleCommand(_ command: CommandType) -> Bool {
487
- return command.traits.isLifecycle
488
- }
489
-
490
476
 
491
477
 
478
+ @MainActor
492
479
  func applyInteractionStabilizationIfNeeded() {
493
- if needsPostSnapshotInteractionDelay {
480
+ if mainOwned.needsPostSnapshotInteractionDelay {
494
481
  sleepFor(postSnapshotInteractionDelay)
495
- needsPostSnapshotInteractionDelay = false
482
+ mainOwned.needsPostSnapshotInteractionDelay = false
496
483
  }
497
484
  if let readyUptime = firstInteractionReadyUptime {
498
485
  sleepFor(readyUptime - ProcessInfo.processInfo.systemUptime)
@@ -57,12 +57,12 @@ extension RunnerTests {
57
57
  func runMainThreadWork<T>(
58
58
  _ operation: String,
59
59
  timeout: TimeInterval,
60
- timeoutError: @escaping () -> Error,
61
- onAbandoned: (() -> Void)? = nil,
62
- _ work: @escaping () throws -> T
60
+ timeoutError: @escaping @Sendable () -> Error,
61
+ onAbandoned: (@Sendable () -> Void)? = nil,
62
+ _ work: @escaping @MainActor () throws -> T
63
63
  ) throws -> T {
64
64
  if Thread.isMainThread {
65
- return try work()
65
+ return try runOnMainActor(work).get()
66
66
  }
67
67
  mainThreadWorkLock.lock()
68
68
  let state = enqueueMainThreadWorkLocked(operation, work)
@@ -85,8 +85,8 @@ extension RunnerTests {
85
85
  func runMainThreadWorkIfIdle<T>(
86
86
  _ operation: String,
87
87
  timeout: TimeInterval,
88
- timeoutError: @escaping () -> Error,
89
- _ work: @escaping () throws -> T
88
+ timeoutError: @escaping @Sendable () -> Error,
89
+ _ work: @escaping @MainActor () throws -> T
90
90
  ) throws -> T? {
91
91
  if Thread.isMainThread {
92
92
  return nil
@@ -109,16 +109,12 @@ extension RunnerTests {
109
109
 
110
110
  private func enqueueMainThreadWorkLocked<T>(
111
111
  _ operation: String,
112
- _ work: @escaping () throws -> T
112
+ _ work: @escaping @MainActor () throws -> T
113
113
  ) -> MainThreadWorkState<T> {
114
114
  let state = MainThreadWorkState<T>()
115
115
  mainThreadWorkInFlightCount += 1
116
116
  DispatchQueue.main.async {
117
- do {
118
- state.result = .success(try work())
119
- } catch {
120
- state.result = .failure(error)
121
- }
117
+ state.result = runOnMainActor(work)
122
118
  self.mainThreadWorkLock.lock()
123
119
  self.mainThreadWorkInFlightCount -= 1
124
120
  let abandoned = state.abandoned
@@ -147,8 +143,8 @@ extension RunnerTests {
147
143
  _ state: MainThreadWorkState<T>,
148
144
  operation: String,
149
145
  timeout: TimeInterval,
150
- timeoutError: @escaping () -> Error,
151
- onAbandoned: (() -> Void)?
146
+ timeoutError: @escaping @Sendable () -> Error,
147
+ onAbandoned: (@Sendable () -> Void)?
152
148
  ) throws -> T {
153
149
  let waitResult = state.completed.wait(timeout: .now() + timeout)
154
150
  if waitResult == .timedOut {
@@ -191,7 +187,7 @@ extension RunnerTests {
191
187
  }
192
188
  }
193
189
 
194
- func mainThreadExecutionTimeoutError() -> Error {
190
+ static func mainThreadExecutionTimeoutError() -> Error {
195
191
  NSError(
196
192
  domain: RunnerErrorDomain.general,
197
193
  code: RunnerErrorCode.mainThreadExecutionTimedOut,
@@ -2,7 +2,7 @@ import AgentDeviceSnapshotPresentation
2
2
 
3
3
 
4
4
 
5
- enum CommandType: String, Codable {
5
+ enum CommandType: String, Codable, CaseIterable {
6
6
  case tap
7
7
  case mouseClick
8
8
  case longPress
@@ -33,6 +33,7 @@ enum CommandType: String, Codable {
33
33
  case recordStop
34
34
  case status
35
35
  case uptime
36
+ case appState
36
37
  case activate
37
38
  case terminate
38
39
  case targetReset
@@ -42,6 +43,46 @@ enum CommandType: String, Codable {
42
43
 
43
44
 
44
45
 
46
+ enum CommandLaunchPolicy: Equatable {
47
+
48
+
49
+
50
+
51
+
52
+
53
+
54
+
55
+
56
+ case noApp
57
+
58
+
59
+
60
+
61
+ case presentedSurface
62
+
63
+
64
+
65
+
66
+
67
+
68
+ case existingApp
69
+
70
+ case mayLaunch
71
+ }
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+
80
+
81
+
82
+
83
+
84
+
85
+
45
86
 
46
87
 
47
88
 
@@ -50,72 +91,94 @@ struct CommandTraits {
50
91
 
51
92
  let isInteraction: Bool
52
93
 
94
+ let retryOnSessionLoss: Bool
53
95
 
54
- let readOnly: ReadOnly
55
96
 
56
- let isLifecycle: Bool
97
+ let launchPolicy: CommandLaunchPolicy
57
98
 
58
- enum ReadOnly {
59
- case always
60
- case never
61
99
 
62
100
 
63
101
 
64
- case conditional
102
+ let convertsRecordedFailure: Bool
103
+
104
+ init(
105
+ isInteraction: Bool = false,
106
+ retryOnSessionLoss: Bool = false,
107
+ launchPolicy: CommandLaunchPolicy,
108
+ convertsRecordedFailure: Bool = false
109
+ ) {
110
+ self.isInteraction = isInteraction
111
+ self.retryOnSessionLoss = retryOnSessionLoss
112
+ self.launchPolicy = launchPolicy
113
+ self.convertsRecordedFailure = convertsRecordedFailure
65
114
  }
66
115
  }
67
116
 
68
- extension CommandType {
117
+ fileprivate extension CommandTraits {
69
118
 
70
119
 
120
+ static let interaction = CommandTraits(
121
+ isInteraction: true,
122
+ launchPolicy: .mayLaunch,
123
+ convertsRecordedFailure: true
124
+ )
71
125
 
72
- var traits: CommandTraits {
73
- switch self {
74
126
 
75
127
 
76
128
 
77
129
 
130
+ static let appMutation = CommandTraits(launchPolicy: .mayLaunch, convertsRecordedFailure: true)
78
131
 
79
- case .tap, .longPress, .drag, .remotePress, .type, .swipe, .scroll, .desktopScroll,
80
- .backInApp, .backSystem, .rotate, .appSwitcher,
81
- .keyboardDismiss, .keyboardReturn, .sequence, .gesture:
82
- return CommandTraits(isInteraction: true, readOnly: .never, isLifecycle: false)
83
132
 
84
133
 
85
- case .findText, .readText, .snapshot, .gestureViewport:
86
- return CommandTraits(isInteraction: false, readOnly: .always, isLifecycle: false)
134
+ static let appRead = CommandTraits(retryOnSessionLoss: true, launchPolicy: .existingApp)
87
135
 
88
136
 
89
- case .screenshot:
90
- return CommandTraits(isInteraction: false, readOnly: .always, isLifecycle: true)
91
137
 
92
138
 
93
- case .alert:
94
- return CommandTraits(isInteraction: false, readOnly: .conditional, isLifecycle: false)
95
139
 
140
+ static let selectorResolution = CommandTraits(
141
+ launchPolicy: .existingApp,
142
+ convertsRecordedFailure: true
143
+ )
96
144
 
97
- case .recordStop, .uptime, .terminate, .targetReset, .shutdown:
98
- return CommandTraits(isInteraction: false, readOnly: .never, isLifecycle: true)
99
145
 
100
146
 
147
+ static let runnerCaptureRead = CommandTraits(retryOnSessionLoss: true, launchPolicy: .noApp)
101
148
 
102
149
 
150
+ static let runnerLifecycle = CommandTraits(launchPolicy: .noApp)
103
151
 
104
152
 
105
- case .actionButton:
106
- return CommandTraits(isInteraction: false, readOnly: .never, isLifecycle: false)
107
153
 
108
- case .status:
109
- return CommandTraits(isInteraction: false, readOnly: .always, isLifecycle: true)
110
154
 
155
+ static let presentedSurfaceMutation = CommandTraits(
156
+ launchPolicy: .presentedSurface,
157
+ convertsRecordedFailure: true
158
+ )
111
159
 
112
160
 
161
+ static let presentedSurfaceQuery = CommandTraits(
162
+ retryOnSessionLoss: true,
163
+ launchPolicy: .presentedSurface
164
+ )
165
+ }
113
166
 
167
+ extension CommandTraits {
114
168
 
115
169
 
116
- case .mouseClick, .querySelector, .home, .recordStart, .activate:
117
- return CommandTraits(isInteraction: false, readOnly: .never, isLifecycle: false)
118
- }
170
+
171
+ static let textEntryWitnessOwners: Set<CommandType> = [.tap, .type]
172
+ }
173
+
174
+ extension Command {
175
+
176
+
177
+
178
+
179
+
180
+ var invalidatesRememberedTextEntryTap: Bool {
181
+ traits.convertsRecordedFailure && !CommandTraits.textEntryWitnessOwners.contains(command)
119
182
  }
120
183
  }
121
184
 
@@ -159,6 +222,49 @@ struct Command: Codable {
159
222
  let steps: [SequenceStep]?
160
223
  }
161
224
 
225
+ extension Command {
226
+
227
+
228
+
229
+ var traits: CommandTraits {
230
+ switch command {
231
+
232
+
233
+
234
+
235
+ case .tap, .type, .longPress, .drag, .remotePress, .swipe, .scroll, .desktopScroll,
236
+ .backInApp, .backSystem, .rotate, .appSwitcher,
237
+ .keyboardDismiss, .keyboardReturn, .sequence, .gesture:
238
+ return .interaction
239
+
240
+ case .findText, .readText, .snapshot, .gestureViewport:
241
+ return .appRead
242
+
243
+
244
+
245
+ case .screenshot, .status, .appState:
246
+ return .runnerCaptureRead
247
+
248
+ case .alert:
249
+ return (action ?? "get").lowercased() == "get"
250
+ ? .presentedSurfaceQuery
251
+ : .presentedSurfaceMutation
252
+
253
+ case .recordStop, .uptime, .terminate, .targetReset, .shutdown:
254
+ return .runnerLifecycle
255
+
256
+ case .actionButton:
257
+ return .presentedSurfaceMutation
258
+
259
+ case .querySelector:
260
+ return .selectorResolution
261
+
262
+ case .mouseClick, .home, .recordStart, .activate:
263
+ return .appMutation
264
+ }
265
+ }
266
+ }
267
+
162
268
  enum ScrollReleaseBehavior: String, Codable {
163
269
  case controlled
164
270
  case inertial
@@ -306,6 +412,7 @@ struct DataPayload: Codable {
306
412
  var referenceWidth: Double?
307
413
  var referenceHeight: Double?
308
414
  var currentUptimeMs: Double?
415
+ var applicationState: String?
309
416
  var commandId: String?
310
417
  var lifecycleState: String?
311
418
  var lifecycleCommand: String?
@@ -1,3 +1,4 @@
1
+ import AgentDeviceSnapshotPresentation
1
2
  import XCTest
2
3
 
3
4
  extension RunnerTests {
@@ -43,6 +44,7 @@ extension RunnerTests {
43
44
  }
44
45
  }
45
46
 
47
+ @MainActor
46
48
  func tapInAppBackControl(app: XCUIApplication) -> InAppBackOutcome {
47
49
  #if os(macOS)
48
50
  if let back = macOSNavigationBackElement(app: app) {
@@ -59,8 +61,8 @@ extension RunnerTests {
59
61
  back.tap()
60
62
  return .performed
61
63
  }
62
- if isSnapshotXCTestChannelPenalized(bundleId: currentBundleId) {
63
- NSLog("AGENT_DEVICE_RUNNER_IN_APP_BACK_SKIPPED_XCTEST_ENUMERATION bundle=%@", currentBundleId ?? "")
64
+ if isSnapshotXCTestChannelPenalized(bundleId: mainOwned.bundleId) {
65
+ NSLog("AGENT_DEVICE_RUNNER_IN_APP_BACK_SKIPPED_XCTEST_ENUMERATION bundle=%@", mainOwned.bundleId ?? "")
64
66
  } else if let back = topNavigationBackElement(app: app) {
65
67
  tapElementCenter(app: app, element: back)
66
68
  return .performed
@@ -120,16 +122,9 @@ extension RunnerTests {
120
122
  return navigationBackKeywords.firstIndex { text.contains($0) }
121
123
  }
122
124
 
123
-
124
- static func isUsableNavigationFrame(_ frame: CGRect) -> Bool {
125
- guard frame.width.isFinite, frame.height.isFinite, frame.width > 0, frame.height > 0 else {
126
- return false
127
- }
128
- return !frame.isInfinite
129
- }
130
-
131
125
  static func isTopNavigationControlFrame(_ candidate: CGRect, in window: CGRect) -> Bool {
132
- guard isUsableNavigationFrame(candidate), isUsableNavigationFrame(window) else {
126
+ guard SnapshotGeometry.isPositiveFinite(candidate), SnapshotGeometry.isPositiveFinite(window)
127
+ else {
133
128
  return false
134
129
  }
135
130
 
@@ -138,7 +133,7 @@ extension RunnerTests {
138
133
  }
139
134
 
140
135
  static func topLeadingNavigationFallbackPoint(in frame: CGRect) -> CGPoint? {
141
- guard isUsableNavigationFrame(frame) else {
136
+ guard SnapshotGeometry.isPositiveFinite(frame) else {
142
137
  return nil
143
138
  }
144
139
 
@@ -148,6 +143,7 @@ extension RunnerTests {
148
143
  return CGPoint(x: frame.minX + xOffset, y: frame.minY + yOffset)
149
144
  }
150
145
 
146
+ @MainActor
151
147
  private func tapTopLeadingNavigationFallback(app: XCUIApplication) -> InAppBackOutcome {
152
148
  #if os(iOS)
153
149
  let frame = onScreenWindowFrame(app: app)
@@ -159,17 +155,20 @@ extension RunnerTests {
159
155
  app: app,
160
156
  policy: synthesizedGesturePolicy(.coordinateTap)
161
157
  )?.withReferenceFrame(frame)
162
- let synthesized = performGesture(app, idleTimeout: false) {
158
+ switch performSynthesizedGesture(app, kind: .coordinateTap, context: context, synthesize: {
163
159
  synthesizedTapAt(app: app, x: point.x, y: point.y, context: context)
164
- }
165
- if case .performed = synthesized.outcome {
166
- return verifyNavigationFallbackOutcome(app: app, before: before)
167
- }
168
- let fallback = performGesture(app) {
169
- tapAt(app: app, x: point.x, y: point.y)
170
- }
171
- if case .performed = fallback.outcome {
160
+ }) {
161
+ case .performed:
172
162
  return verifyNavigationFallbackOutcome(app: app, before: before)
163
+ case .refused:
164
+ return .unavailable
165
+ case .xctestFallback:
166
+ let fallback = performGesture(app) {
167
+ tapAt(app: app, x: point.x, y: point.y)
168
+ }
169
+ if case .performed = fallback.outcome {
170
+ return verifyNavigationFallbackOutcome(app: app, before: before)
171
+ }
173
172
  }
174
173
  #endif
175
174
  return .unavailable
@@ -8,6 +8,7 @@ struct PrivateAXFields {
8
8
  let label: String
9
9
  let identifier: String
10
10
  let value: String
11
+ let placeholder: String
11
12
  let rawType: Int
12
13
  let elementType: XCUIElement.ElementType?
13
14
  let enabled: Bool
@@ -52,6 +53,7 @@ extension RunnerTests {
52
53
  label: privateAXPresentationString(raw["label"]),
53
54
  identifier: privateAXPresentationString(raw["identifier"]),
54
55
  value: privateAXPresentationString(raw["value"]),
56
+ placeholder: privateAXPresentationString(raw["placeholder"]),
55
57
  rawType: rawType,
56
58
  elementType: privateAXElementType(rawElementType: rawType),
57
59
  enabled: privateAXPresentationBool(raw["enabled"]) ?? true,
@@ -70,6 +72,7 @@ extension RunnerTests {
70
72
  label: fields.label.isEmpty ? nil : fields.label,
71
73
  identifier: fields.identifier.isEmpty ? nil : fields.identifier,
72
74
  value: fields.value.isEmpty ? nil : fields.value,
75
+ placeholder: fields.placeholder.isEmpty ? nil : fields.placeholder,
73
76
  rect: SnapshotRect(fields.rect), enabled: fields.enabled,
74
77
  focused: fields.focused, selected: fields.selected,
75
78
  hittable: false,
@@ -36,8 +36,8 @@ extension RunnerTests {
36
36
 
37
37
 
38
38
  func start(
39
- bootstrap: @escaping () -> Result<CapturedAppScreen, RunnerAppScreenCaptureFailure>,
40
- frame: @escaping () -> RunnerImage?
39
+ bootstrap: () -> Result<CapturedAppScreen, RunnerAppScreenCaptureFailure>,
40
+ frame: @escaping @Sendable () -> RunnerImage?
41
41
  ) throws {
42
42
  let url = URL(fileURLWithPath: outputPath)
43
43
  let directory = url.deletingLastPathComponent()
@@ -132,7 +132,7 @@ extension RunnerTests {
132
132
 
133
133
  let timer = DispatchSource.makeTimerSource(queue: queue)
134
134
  timer.schedule(deadline: .now() + frameInterval, repeating: frameInterval)
135
- timer.setEventHandler { [weak self] in
135
+ timer.setEventHandler { @Sendable [weak self] in
136
136
  guard let self else { return }
137
137
  if self.shouldStop() { return }
138
138
  guard let image = frame() else { return }
@@ -299,16 +299,17 @@ extension RunnerTests {
299
299
 
300
300
 
301
301
 
302
+ @MainActor
302
303
  func startRecording(
303
304
  _ recorder: ScreenRecorder,
304
- capture: @escaping () -> Result<CapturedAppScreen, RunnerAppScreenCaptureFailure>
305
+ capture: @escaping @MainActor () -> Result<CapturedAppScreen, RunnerAppScreenCaptureFailure>
305
306
  ) throws {
306
307
  try recorder.start(bootstrap: capture) { [weak self] in
307
308
  guard let self else { return nil }
308
309
  return try? self.runMainThreadWorkIfIdle(
309
310
  "recording_frame",
310
311
  timeout: self.recordingFrameCaptureTimeout,
311
- timeoutError: self.mainThreadExecutionTimeoutError
312
+ timeoutError: Self.mainThreadExecutionTimeoutError
312
313
  ) {
313
314
  try capture().get().image
314
315
  }