agent-device 0.20.9 → 0.21.0

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 (492) hide show
  1. package/README.md +2 -2
  2. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.20.9.apk → agent-device-android-ime-helper-0.21.0.apk} +0 -0
  3. package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.0.apk.sha256 +1 -0
  4. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.20.9.manifest.json → agent-device-android-ime-helper-0.21.0.manifest.json} +4 -4
  5. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.0.apk +0 -0
  6. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.0.apk.sha256 +1 -0
  7. package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.20.9.manifest.json → agent-device-android-snapshot-helper-0.21.0.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 +49 -0
  12. package/apple/snapshot-bridge/SnapshotBridge.m +280 -0
  13. package/apple/snapshot-bridge/SnapshotBridgeRuntime.h +33 -0
  14. package/apple/snapshot-bridge/SnapshotBridgeRuntime.m +382 -0
  15. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunner/AgentDeviceRunnerApp.m +58 -0
  16. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/project.pbxproj +19 -0
  17. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAlertVerification.swift +36 -0
  18. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerStringNormalization.swift +8 -0
  19. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.h +12 -1
  20. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.m +100 -6
  21. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTapPointPolicy.swift +2 -2
  22. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+AXSnapshotFallback.swift +21 -12
  23. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Alert.swift +21 -88
  24. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+AlertObservation.swift +124 -0
  25. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +110 -107
  26. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandJournal.swift +6 -14
  27. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +85 -114
  28. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Lifecycle.swift +13 -6
  29. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +72 -175
  30. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Navigation.swift +10 -11
  31. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+PrivateAXElementTypes.swift +12 -0
  32. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+PrivateAXPresentation.swift +78 -64
  33. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScreenRecorder.swift +17 -9
  34. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScrollGesture.swift +47 -13
  35. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +232 -438
  36. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotBackendCapabilities.swift +98 -0
  37. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCapturePlan.swift +229 -168
  38. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotPresentationInvariant.swift +19 -0
  39. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotTiming.swift +110 -0
  40. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedCommitDeadline.swift +206 -0
  41. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedTextEntry.swift +147 -25
  42. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SystemModal.swift +21 -18
  43. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntry.swift +24 -307
  44. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntryFocus.swift +206 -0
  45. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntryReadiness.swift +158 -0
  46. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextInputCandidatePolicy.swift +19 -0
  47. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextTyping.swift +49 -16
  48. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Transport.swift +2 -11
  49. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TvRemote.swift +4 -19
  50. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +16 -20
  51. package/dist/apple/runner/AgentDeviceRunner/RecordingScripts/RecordingExportSupport.swift +111 -0
  52. package/dist/apple/runner/AgentDeviceRunner/RecordingScripts/recording-overlay.swift +42 -81
  53. package/dist/apple/runner/AgentDeviceRunner/RecordingScripts/recording-trim.swift +35 -82
  54. package/dist/apple/snapshot-presentation/Package.swift +21 -0
  55. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotGeometry.swift +56 -0
  56. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotModels.swift +259 -0
  57. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotPresentation.swift +153 -0
  58. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotPresentationFailure.swift +27 -0
  59. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotPresentationInvariant.swift +91 -0
  60. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotPresentationProjection.swift +161 -0
  61. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotScopePolicy.swift +53 -0
  62. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotVisibilityFold.swift +242 -0
  63. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotVisibilityFoldProjection.swift +97 -0
  64. package/dist/src/adapter.js +1 -0
  65. package/dist/src/adb-executor.js +2 -0
  66. package/dist/src/adb-failure.js +1 -0
  67. package/dist/src/adb-host.js +1 -0
  68. package/dist/src/adb.js +1 -0
  69. package/dist/src/agent-browser-lifecycle.js +1 -0
  70. package/dist/src/agent-browser-process-record.js +1 -0
  71. package/dist/src/agent-browser-provider.js +1 -0
  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.d.ts +61 -0
  75. package/dist/src/ai-sdk.js +1 -0
  76. package/dist/src/alert-contract.js +1 -0
  77. package/dist/src/alert-runtime.js +1 -0
  78. package/dist/src/android-adb.d.ts +27 -19
  79. package/dist/src/android-adb.js +1 -1
  80. package/dist/src/android-input-ownership.js +1 -0
  81. package/dist/src/android-system-chrome.js +1 -0
  82. package/dist/src/android.js +1 -1
  83. package/dist/src/api.js +11 -0
  84. package/dist/src/app-catalog.js +1 -0
  85. package/dist/src/app-deployment-resolution.js +2 -0
  86. package/dist/src/app-helpers.js +1 -1
  87. package/dist/src/app-inventory-contract.js +1 -0
  88. package/dist/src/app-inventory.js +1 -0
  89. package/dist/src/app-launch.js +2 -0
  90. package/dist/src/app-lifecycle-facade.js +1 -0
  91. package/dist/src/app-lifecycle.js +3 -6
  92. package/dist/src/app-lifecycle2.js +2 -2
  93. package/dist/src/app-lifecycle3.js +1 -0
  94. package/dist/src/app-log-files.js +1 -1
  95. package/dist/src/app-log-reconnect.js +1 -1
  96. package/dist/src/app-log-runtime.js +1 -4
  97. package/dist/src/app-log-runtime2.js +4 -0
  98. package/dist/src/app-parsers.js +4 -0
  99. package/dist/src/app-resolution-cache.js +1 -0
  100. package/dist/src/app-resolution-facade.js +1 -0
  101. package/dist/src/app-resolution.js +2 -2
  102. package/dist/src/app-state.js +1 -2
  103. package/dist/src/application-lifecycle-interaction.js +1 -0
  104. package/dist/src/application-lifecycle-runtime.js +1 -0
  105. package/dist/src/apps-simctl.js +1 -0
  106. package/dist/src/archive.js +4 -0
  107. package/dist/src/artifact-download.js +1 -0
  108. package/dist/src/artifacts.d.ts +6 -2
  109. package/dist/src/artifacts.js +1 -1
  110. package/dist/src/async-lifecycle.js +1 -0
  111. package/dist/src/audio-probe-result.js +1 -0
  112. package/dist/src/audio-probe-runtime.js +1 -0
  113. package/dist/src/audio-probe-support.js +1 -0
  114. package/dist/src/auth-session.js +1 -1
  115. package/dist/src/auth.js +1 -1
  116. package/dist/src/back-mode.js +1 -0
  117. package/dist/src/{url.js → base-url.js} +1 -1
  118. package/dist/src/boot-diagnostics.js +2 -2
  119. package/dist/src/byte-limit-stream.js +1 -1
  120. package/dist/src/capabilities.js +1 -0
  121. package/dist/src/capture.js +1 -0
  122. package/dist/src/catalog.js +1 -0
  123. package/dist/src/cli-command-aliases.js +1 -0
  124. package/dist/src/cli-config.js +1 -1
  125. package/dist/src/cli-help.js +77 -51
  126. package/dist/src/cli-output.js +1 -1
  127. package/dist/src/cli-runner.js +1 -1
  128. package/dist/src/cli.js +5 -7
  129. package/dist/src/click-button.js +1 -0
  130. package/dist/src/client-metro-companion.js +1 -0
  131. package/dist/src/client-metro.js +1 -1
  132. package/dist/src/client-types.d.ts +1606 -0
  133. package/dist/src/clipboard.js +1 -0
  134. package/dist/src/code-signature-cache.js +1 -0
  135. package/dist/src/code-signature.js +1 -0
  136. package/dist/src/color.js +1 -0
  137. package/dist/src/command-metadata.js +1 -1
  138. package/dist/src/command-schema.js +3 -3
  139. package/dist/src/command-surface.js +1 -1
  140. package/dist/src/command-tools.js +2 -0
  141. package/dist/src/command.js +1 -0
  142. package/dist/src/{selector-vocabulary.js → compatibility-policy.js} +1 -1
  143. package/dist/src/config.js +1 -1
  144. package/dist/src/config2.js +1 -0
  145. package/dist/src/connection-runtime.js +1 -1
  146. package/dist/src/connection.js +5 -5
  147. package/dist/src/contracts.d.ts +1 -1
  148. package/dist/src/contracts.js +1 -1
  149. package/dist/src/daemon-client-lifecycle.js +1 -0
  150. package/dist/src/daemon-process.js +1 -0
  151. package/dist/src/daemon-registration.js +1 -0
  152. package/dist/src/daemon-shutdown-report.js +1 -1
  153. package/dist/src/daemon.js +2 -2
  154. package/dist/src/debug-symbols-facade.js +5 -0
  155. package/dist/src/deploy-result-target.js +1 -0
  156. package/dist/src/device-claim-inspection.js +1 -1
  157. package/dist/src/device-claim-rule.js +1 -0
  158. package/dist/src/device-input-state.d.ts +190 -3
  159. package/dist/src/device-inventory-context.js +1 -0
  160. package/dist/src/device-isolation.js +1 -0
  161. package/dist/src/device-release.js +1 -0
  162. package/dist/src/device-rotation.js +1 -0
  163. package/dist/src/device-selection-resolver.js +1 -0
  164. package/dist/src/device-session.js +1 -0
  165. package/dist/src/device.js +1 -1
  166. package/dist/src/device2.js +3 -2
  167. package/dist/src/diagnostics.js +3 -0
  168. package/dist/src/diagnostics2.js +1 -0
  169. package/dist/src/dispatch-resolve.js +1 -0
  170. package/dist/src/doctor-facade.js +1 -0
  171. package/dist/src/doctor-output.js +1 -0
  172. package/dist/src/doctor.js +1 -0
  173. package/dist/src/doctor2.js +1 -0
  174. package/dist/src/doctor3.js +1 -0
  175. package/dist/src/durable-resource.js +1 -0
  176. package/dist/src/entities.js +1 -1
  177. package/dist/src/error.js +2 -0
  178. package/dist/src/exec.js +1 -0
  179. package/dist/src/file.js +1 -0
  180. package/dist/src/find.js +1 -1
  181. package/dist/src/finders.js +1 -1
  182. package/dist/src/format.js +2 -0
  183. package/dist/src/generic-settle.js +1 -1
  184. package/dist/src/generic.js +1 -1
  185. package/dist/src/gesture-admission.js +1 -0
  186. package/dist/src/gesture-plan-types.js +1 -0
  187. package/dist/src/gesture-plan.js +1 -0
  188. package/dist/src/gesture-runtime.js +1 -0
  189. package/dist/src/harmonyos.js +1 -1
  190. package/dist/src/hdc.js +1 -1
  191. package/dist/src/helper.js +2 -2
  192. package/dist/src/host-file.js +1 -0
  193. package/dist/src/human-control-contract.js +1 -0
  194. package/dist/src/human-control.js +1 -0
  195. package/dist/src/index.d.ts +2 -1578
  196. package/dist/src/index.js +1 -1
  197. package/dist/src/input-actions.js +1 -4
  198. package/dist/src/input-actions2.js +2 -1
  199. package/dist/src/install-artifact-facade.js +1 -0
  200. package/dist/src/install-source-network-transport.js +1 -0
  201. package/dist/src/install-source.d.ts +3 -10
  202. package/dist/src/install-source2.js +1 -1
  203. package/dist/src/interaction-positionals.js +1 -0
  204. package/dist/src/interaction-ref-policy.js +1 -0
  205. package/dist/src/interaction.js +1 -1
  206. package/dist/src/interaction2.js +1 -1
  207. package/dist/src/interactor-operation-catalog.js +1 -0
  208. package/dist/src/interactor-types.js +1 -0
  209. package/dist/src/interactor.js +6 -2
  210. package/dist/src/interactor2.js +1 -1
  211. package/dist/src/interactors.js +1 -1
  212. package/dist/src/internal/bin.d.ts +1 -1
  213. package/dist/src/internal/bin.js +1 -2
  214. package/dist/src/internal/companion-tunnel.d.ts +1 -1
  215. package/dist/src/internal/companion-tunnel.js +1 -1
  216. package/dist/src/internal/daemon.d.ts +1 -1
  217. package/dist/src/internal/daemon.js +2 -4
  218. package/dist/src/internal/png-worker.d.ts +4 -4
  219. package/dist/src/internal/png-worker.js +1 -1
  220. package/dist/src/internal/run-script-http-child.d.ts +4 -0
  221. package/dist/src/internal/run-script-http-child.js +1 -0
  222. package/dist/src/internal/update-check-entry.d.ts +1 -1
  223. package/dist/src/inventory.js +4 -4
  224. package/dist/src/inventory2.js +1 -1
  225. package/dist/src/inventory3.js +1 -1
  226. package/dist/src/inventory5.js +1 -1
  227. package/dist/src/inventory6.js +1 -1
  228. package/dist/src/ios-client.js +16 -0
  229. package/dist/src/ios-snapshot-acquisition.js +1 -0
  230. package/dist/src/ios-snapshot-adapter.js +1 -0
  231. package/dist/src/ios-snapshot-planning.js +1 -0
  232. package/dist/src/ios-snapshot-runtime.js +1 -0
  233. package/dist/src/json.js +1 -0
  234. package/dist/src/keyboard-actions.js +1 -0
  235. package/dist/src/launch-console.js +1 -0
  236. package/dist/src/lease-context.js +1 -1
  237. package/dist/src/lease-scope.js +1 -0
  238. package/dist/src/lease.js +1 -1
  239. package/dist/src/limrun-runtime-dependencies.js +1 -1
  240. package/dist/src/limrun.d.ts +1618 -7
  241. package/dist/src/limrun.js +1 -1
  242. package/dist/src/linux.js +1 -1
  243. package/dist/src/local-interactor-operation-set.js +1 -0
  244. package/dist/src/location-coordinates.js +1 -1
  245. package/dist/src/logcat.js +1 -1
  246. package/dist/src/macos-facade.js +1 -0
  247. package/dist/src/managed-device-scope.js +1 -0
  248. package/dist/src/mechanics.js +19 -0
  249. package/dist/src/metro.d.ts +4 -2
  250. package/dist/src/metro.js +1 -1
  251. package/dist/src/metro2.js +1 -1
  252. package/dist/src/mobile-snapshot-semantics.js +1 -0
  253. package/dist/src/multitouch-support.js +1 -0
  254. package/dist/src/numeric.js +1 -0
  255. package/dist/src/observability.js +1 -1
  256. package/dist/src/observation.js +2 -0
  257. package/dist/src/output-path.js +1 -1
  258. package/dist/src/owner-identity.js +3 -1
  259. package/dist/src/parameterized-recorded-fill.js +1 -0
  260. package/dist/src/parser.js +1 -1
  261. package/dist/src/perf-facade.js +2 -0
  262. package/dist/src/perf-runtime-operation-builder.js +1 -0
  263. package/dist/src/perf-runtime-plan.js +1 -0
  264. package/dist/src/perf-runtime.js +1 -0
  265. package/dist/src/perf.js +1 -4
  266. package/dist/src/physical-device-control.js +1 -1
  267. package/dist/src/physical-device-facade.js +1 -0
  268. package/dist/src/platform-request-scope.js +1 -0
  269. package/dist/src/platform-runtime-android-adb-host.js +1 -0
  270. package/dist/src/platform-runtime-android-mechanics.js +1 -0
  271. package/dist/src/platform-runtime-app-log-android-transport.js +1 -1
  272. package/dist/src/platform-runtime-app-log-process.js +1 -1
  273. package/dist/src/platform-runtime-apple-resources.js +1 -0
  274. package/dist/src/platform-runtime-host.js +1 -1
  275. package/dist/src/platform-runtime-managed-owner.js +1 -0
  276. package/dist/src/platform-runtime-network-web-transport.js +1 -1
  277. package/dist/src/platform-runtime-open-target.js +1 -1
  278. package/dist/src/platform-runtime-operation-host.js +4 -4
  279. package/dist/src/platform-runtime-runtime-hints.js +2 -2
  280. package/dist/src/platform-runtime-screen-recording-android-host.js +1 -1
  281. package/dist/src/platform-runtime-screen-recording-apple-runner-host.js +1 -1
  282. package/dist/src/platform-runtime-screen-recording-apple-runner-transport.js +1 -1
  283. package/dist/src/platform-runtime-screen-recording-apple-simulator-host.js +1 -1
  284. package/dist/src/platform-runtime-screen-recording-apple-transport.js +1 -1
  285. package/dist/src/platform-runtime-screen-recording-finalizer-host.js +1 -1
  286. package/dist/src/platform-runtime-screen-recording-harmony-host.js +1 -1
  287. package/dist/src/platform-runtime-screen-recording-process-host.js +1 -0
  288. package/dist/src/platform-runtime-screen-recording-web-host.js +1 -1
  289. package/dist/src/platform-runtime-unavailable.js +1 -0
  290. package/dist/src/platform-runtime.js +1 -1
  291. package/dist/src/platform-runtime2.js +1 -1
  292. package/dist/src/plist.js +1 -1
  293. package/dist/src/png-worker-contract.js +1 -1
  294. package/dist/src/prepare-kind.js +1 -15
  295. package/dist/src/process-exit.js +1 -1
  296. package/dist/src/process.js +1 -0
  297. package/dist/src/provider-device-runtime.js +1 -0
  298. package/dist/src/provider-session-ownership.js +2 -0
  299. package/dist/src/provider-webdriver.js +1 -1
  300. package/dist/src/provider.js +1 -1
  301. package/dist/src/providers.js +1 -0
  302. package/dist/src/proxy.js +2 -2
  303. package/dist/src/react-native-overlay.js +1 -0
  304. package/dist/src/react-native.js +1 -1
  305. package/dist/src/record-runtime.js +1 -1
  306. package/dist/src/record-trace.js +1 -1
  307. package/dist/src/rect-center.js +1 -0
  308. package/dist/src/register-builtins.js +1 -0
  309. package/dist/src/registry.js +39 -28
  310. package/dist/src/remote-config2.js +1 -1
  311. package/dist/src/replay-divergence.js +2 -0
  312. package/dist/src/replay.js +2 -2
  313. package/dist/src/reporting.js +8 -8
  314. package/dist/src/request-admission.js +1 -0
  315. package/dist/src/request-cancel.js +1 -0
  316. package/dist/src/request-cancellation.js +1 -0
  317. package/dist/src/request-progress-protocol.js +1 -0
  318. package/dist/src/request-providers.js +1 -0
  319. package/dist/src/request-runtime-binding.js +1 -0
  320. package/dist/src/request.js +1 -0
  321. package/dist/src/result-serialization.js +1 -0
  322. package/dist/src/retry.js +1 -0
  323. package/dist/src/rolldown-runtime.js +1 -1
  324. package/dist/src/runner-cache-metadata.js +2 -0
  325. package/dist/src/runner-client.js +1 -1
  326. package/dist/src/runner-disposal.js +1 -1
  327. package/dist/src/runner-operations-facade.js +1 -0
  328. package/dist/src/runner-owner-facade.js +1 -0
  329. package/dist/src/runner-owner-state.js +1 -0
  330. package/dist/src/runner-presentation.js +1 -0
  331. package/dist/src/runner-selector-query.js +1 -0
  332. package/dist/src/runner.js +1 -0
  333. package/dist/src/{runtime-transport.js → runtime-transport-hints.js} +1 -1
  334. package/dist/src/runtime.js +3 -3
  335. package/dist/src/runtime2.js +4 -4
  336. package/dist/src/runtime3.js +1 -1
  337. package/dist/src/runtime4.js +4 -3
  338. package/dist/src/runtime5.js +1 -1
  339. package/dist/src/runtime6.js +1 -1
  340. package/dist/src/runtime7.js +1 -1
  341. package/dist/src/runtime8.js +1 -1
  342. package/dist/src/runtime9.js +1 -1
  343. package/dist/src/scoped-provider.js +1 -1
  344. package/dist/src/screen-recording-completion.js +1 -0
  345. package/dist/src/screen-recording-live-handle.js +1 -0
  346. package/dist/src/screen-recording-options.js +1 -0
  347. package/dist/src/screen-recording-resource-recovery.js +1 -0
  348. package/dist/src/screen-recording-runtime.js +1 -0
  349. package/dist/src/screenshot-density.js +1 -1
  350. package/dist/src/screenshot-result.js +1 -6
  351. package/dist/src/screenshot-runtime.js +1 -0
  352. package/dist/src/screenshot.js +1 -1
  353. package/dist/src/screenshot2.js +1 -0
  354. package/dist/src/screenshot3.js +5 -0
  355. package/dist/src/script.js +1 -0
  356. package/dist/src/scroll-command.js +1 -0
  357. package/dist/src/scroll-gesture.js +1 -0
  358. package/dist/src/scroll-indicator.js +1 -0
  359. package/dist/src/sdk-batch-runner.d.ts +23 -6
  360. package/dist/src/sdk-batch-runner.js +2 -3
  361. package/dist/src/sdk-batch.js +2 -0
  362. package/dist/src/sdk-contracts.d.ts +222 -99
  363. package/dist/src/sdk-contracts.js +2 -2
  364. package/dist/src/sdk-device.js +1 -1
  365. package/dist/src/sdk-finders.d.ts +1 -1
  366. package/dist/src/sdk-finders.js +1 -1
  367. package/dist/src/sdk-io.js +1 -1
  368. package/dist/src/sdk-remote-config.d.ts +1 -1
  369. package/dist/src/sdk-remote-config.js +1 -1
  370. package/dist/src/sdk-selectors.d.ts +539 -199
  371. package/dist/src/sdk-selectors.js +1 -1
  372. package/dist/src/selection.js +1 -0
  373. package/dist/src/selector-runtime.js +1 -1
  374. package/dist/src/selectors.js +1 -1
  375. package/dist/src/semantic-index.js +1 -0
  376. package/dist/src/server.js +10 -3
  377. package/dist/src/session-allocation.js +1 -0
  378. package/dist/src/session-artifact-paths.js +1 -0
  379. package/dist/src/session-paths.js +1 -0
  380. package/dist/src/session-repair-tombstone.js +1 -0
  381. package/dist/src/session-routing.js +1 -0
  382. package/dist/src/session-runtime-admission.js +1 -0
  383. package/dist/src/session-snapshot.js +1 -0
  384. package/dist/src/session-store.js +4 -0
  385. package/dist/src/session-target-evidence.js +1 -1
  386. package/dist/src/session2.js +5 -28
  387. package/dist/src/settings.js +1 -0
  388. package/dist/src/shared.js +2 -2
  389. package/dist/src/simctl-facade.js +1 -0
  390. package/dist/src/simctl.js +1 -1
  391. package/dist/src/simulator-facade.js +1 -0
  392. package/dist/src/simulator.js +1 -1
  393. package/dist/src/snapshot-capture-annotations.js +1 -0
  394. package/dist/src/snapshot-desktop-projection.js +1 -0
  395. package/dist/src/snapshot-diagnostics.js +1 -0
  396. package/dist/src/snapshot-process.js +1 -0
  397. package/dist/src/snapshot-quality-backend-capabilities.js +1 -0
  398. package/dist/src/snapshot-quality-verdict.js +1 -0
  399. package/dist/src/snapshot-runtime-binding.js +1 -0
  400. package/dist/src/snapshot-runtime.js +1 -1
  401. package/dist/src/snapshot-scope.js +1 -0
  402. package/dist/src/snapshot-state.js +1 -0
  403. package/dist/src/snapshot.js +1 -8
  404. package/dist/src/snapshot2.js +1 -1
  405. package/dist/src/snapshot3.js +1 -1
  406. package/dist/src/snapshot4.js +1 -0
  407. package/dist/src/src.js +1 -1
  408. package/dist/src/src2.js +1 -1
  409. package/dist/src/src3.js +1 -2
  410. package/dist/src/src4.js +1 -0
  411. package/dist/src/src5.js +1 -0
  412. package/dist/src/src6.js +1 -0
  413. package/dist/src/src7.js +2 -0
  414. package/dist/src/src8.js +115 -0
  415. package/dist/src/startup-recovery-fence.js +1 -0
  416. package/dist/src/status-markers.js +1 -0
  417. package/dist/src/string-enum.js +1 -1
  418. package/dist/src/success-text.js +1 -0
  419. package/dist/src/surface-snapshot.js +1 -0
  420. package/dist/src/surface-snapshot2.js +1 -0
  421. package/dist/src/takeover.js +5 -0
  422. package/dist/src/tool-provider-facade.js +1 -0
  423. package/dist/src/tool-provider.js +4 -3
  424. package/dist/src/tool-provider2.js +1 -1
  425. package/dist/src/tool-provider3.js +1 -0
  426. package/dist/src/toolchain-probe.js +2 -0
  427. package/dist/src/touch-runtime.js +1 -0
  428. package/dist/src/transport.js +3 -0
  429. package/dist/src/tree.js +1 -0
  430. package/dist/src/ttl-memo.js +1 -0
  431. package/dist/src/tv-remote.js +1 -0
  432. package/dist/src/type-text-runtime.js +1 -0
  433. package/dist/src/undici.js +99 -0
  434. package/dist/src/update-check.js +1 -1
  435. package/dist/src/verified-file.js +1 -1
  436. package/dist/src/version.js +1 -1
  437. package/dist/src/video.js +2 -2
  438. package/dist/src/web.js +2 -1
  439. package/dist/src/web2.js +1 -0
  440. package/dist/src/webdriver-ios-snapshot.js +1 -0
  441. package/dist/src/webdriver-source.js +1 -0
  442. package/linux/atspi-dump.py +54 -9
  443. package/package.json +73 -38
  444. package/server.json +9 -3
  445. package/android/ime-helper/dist/agent-device-android-ime-helper-0.20.9.apk.sha256 +0 -1
  446. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.9.apk +0 -0
  447. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.9.apk.sha256 +0 -1
  448. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CoordinateTextEntryTests.swift +0 -4
  449. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+FlatSnapshotFiltering.swift +0 -75
  450. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+LifecycleCacheTests.swift +0 -6
  451. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+RecordingTests.swift +0 -4
  452. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SelectorMatchPolicyTests.swift +0 -4
  453. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotTraversalIdentityTests.swift +0 -4
  454. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntryPolicyTests.swift +0 -4
  455. package/dist/src/appearance.js +0 -1
  456. package/dist/src/apps.js +0 -5
  457. package/dist/src/audio-probe.js +0 -1
  458. package/dist/src/cancel.js +0 -1
  459. package/dist/src/cli-grammar.js +0 -1
  460. package/dist/src/command-catalog.js +0 -1
  461. package/dist/src/debug-symbols.js +0 -5
  462. package/dist/src/device-input-state.js +0 -2
  463. package/dist/src/device-ready.js +0 -1
  464. package/dist/src/dispatch.js +0 -1
  465. package/dist/src/handler-utils.js +0 -1
  466. package/dist/src/helper-package-install.js +0 -1
  467. package/dist/src/host-process.js +0 -3
  468. package/dist/src/ime-lifecycle.js +0 -2
  469. package/dist/src/ime-recovery-marker.js +0 -1
  470. package/dist/src/input-actions3.js +0 -1
  471. package/dist/src/install-artifact.js +0 -1
  472. package/dist/src/install-artifact2.js +0 -1
  473. package/dist/src/interaction-common.js +0 -1
  474. package/dist/src/interaction-runtime.js +0 -1
  475. package/dist/src/interaction-snapshot.js +0 -1
  476. package/dist/src/manifest.js +0 -1
  477. package/dist/src/method-scope.js +0 -1
  478. package/dist/src/png-resize.js +0 -1
  479. package/dist/src/process-lock.js +0 -1
  480. package/dist/src/runner-lease.js +0 -1
  481. package/dist/src/runner-sequence.js +0 -1
  482. package/dist/src/screenshot-geometry.js +0 -1
  483. package/dist/src/sdk-android-adb.d.ts +0 -245
  484. package/dist/src/sdk-android-adb.js +0 -4
  485. package/dist/src/sdk.js +0 -1
  486. package/dist/src/setting-state.js +0 -1
  487. package/dist/src/shell-quote.js +0 -1
  488. package/dist/src/snapshot-session.js +0 -1
  489. package/dist/src/timing-safe-equal.js +0 -1
  490. package/dist/src/unsupported-interactor.js +0 -1
  491. package/dist/src/verdict.js +0 -1
  492. /package/dist/src/{recording-telemetry.js → telemetry.js} +0 -0
@@ -1,4 +1,5 @@
1
1
  import XCTest
2
+ import AgentDeviceSnapshotPresentation
2
3
 
3
4
  extension RunnerTests {
4
5
  private static let axSnapshotErrorCode = "IOS_AX_SNAPSHOT_FAILED"
@@ -15,69 +16,76 @@ extension RunnerTests {
15
16
  let queryRoot: XCUIElement
16
17
  let rootSnapshot: XCUIElementSnapshot
17
18
  let viewport: CGRect
18
- let flatSnapshots: [XCUIElementSnapshot]
19
- let snapshotRanges: [ObjectIdentifier: (Int, Int)]
20
- let maxDepth: Int
21
19
  }
22
20
 
23
21
  private struct SnapshotEvaluation {
24
22
  let label: String
25
23
  let identifier: String
26
24
  let valueText: String?
27
- let hittable: Bool
28
25
  let focused: Bool
29
26
  let selected: Bool
30
- let visible: Bool
27
+ }
28
+
29
+ private struct SnapshotTraversalEntry {
30
+ let snapshot: XCUIElementSnapshot
31
+ let depth: Int
32
+ let parentIndex: Int?
33
+ let parentPresentedDepth: Int
34
+ let parentTraversal: SnapshotVisibilityFold.TraversalState
31
35
  }
32
36
 
33
37
  struct SnapshotCaptureFailure: Error {
34
38
  let code: String
35
39
  let message: String
36
40
  let hint: String
41
+ let qualityReasonCode: String?
42
+
43
+ init(code: String, message: String, hint: String, qualityReasonCode: String? = nil) {
44
+ self.code = code
45
+ self.message = message
46
+ self.hint = hint
47
+ self.qualityReasonCode = qualityReasonCode
48
+ }
37
49
  }
38
50
 
39
51
  // MARK: - Snapshot Entry
40
52
 
53
+ /// One raw-value table covers public XCTest cases and the SDK-hidden Keyboard/Key values.
54
+ static let elementTypeNamesByRawValue = [
55
+ XCUIElement.ElementType.application.rawValue: "Application",
56
+ XCUIElement.ElementType.window.rawValue: "Window",
57
+ XCUIElement.ElementType.button.rawValue: "Button",
58
+ XCUIElement.ElementType.cell.rawValue: "Cell",
59
+ XCUIElement.ElementType.staticText.rawValue: "StaticText",
60
+ XCUIElement.ElementType.textField.rawValue: "TextField",
61
+ XCUIElement.ElementType.textView.rawValue: "TextView",
62
+ XCUIElement.ElementType.secureTextField.rawValue: "SecureTextField",
63
+ XCUIElement.ElementType.switch.rawValue: "Switch",
64
+ XCUIElement.ElementType.slider.rawValue: "Slider",
65
+ XCUIElement.ElementType.link.rawValue: "Link",
66
+ XCUIElement.ElementType.image.rawValue: "Image",
67
+ XCUIElement.ElementType.navigationBar.rawValue: "NavigationBar",
68
+ XCUIElement.ElementType.tabBar.rawValue: "TabBar",
69
+ XCUIElement.ElementType.collectionView.rawValue: "CollectionView",
70
+ XCUIElement.ElementType.table.rawValue: "Table",
71
+ XCUIElement.ElementType.scrollView.rawValue: "ScrollView",
72
+ XCUIElement.ElementType.toolbar.rawValue: "Toolbar",
73
+ XCUIElement.ElementType.searchField.rawValue: "SearchField",
74
+ XCUIElement.ElementType.segmentedControl.rawValue: "SegmentedControl",
75
+ XCUIElement.ElementType.stepper.rawValue: "Stepper",
76
+ XCUIElement.ElementType.picker.rawValue: "Picker",
77
+ XCUIElement.ElementType.activityIndicator.rawValue: "ActivityIndicator",
78
+ XCUIElement.ElementType.progressIndicator.rawValue: "ProgressIndicator",
79
+ XCUIElement.ElementType.checkBox.rawValue: "CheckBox",
80
+ XCUIElement.ElementType.menuItem.rawValue: "MenuItem",
81
+ XCUIElement.ElementType.webView.rawValue: "WebView",
82
+ XCUIElement.ElementType.other.rawValue: "Other",
83
+ 19: "Keyboard",
84
+ 20: "Key"
85
+ ]
86
+
41
87
  func elementTypeName(_ type: XCUIElement.ElementType) -> String {
42
- switch type {
43
- case .application: return "Application"
44
- case .window: return "Window"
45
- case .button: return "Button"
46
- case .cell: return "Cell"
47
- case .staticText: return "StaticText"
48
- case .textField: return "TextField"
49
- case .textView: return "TextView"
50
- case .secureTextField: return "SecureTextField"
51
- case .switch: return "Switch"
52
- case .slider: return "Slider"
53
- case .link: return "Link"
54
- case .image: return "Image"
55
- case .navigationBar: return "NavigationBar"
56
- case .tabBar: return "TabBar"
57
- case .collectionView: return "CollectionView"
58
- case .table: return "Table"
59
- case .scrollView: return "ScrollView"
60
- case .toolbar: return "Toolbar"
61
- case .searchField: return "SearchField"
62
- case .segmentedControl: return "SegmentedControl"
63
- case .stepper: return "Stepper"
64
- case .picker: return "Picker"
65
- case .activityIndicator: return "ActivityIndicator"
66
- case .progressIndicator: return "ProgressIndicator"
67
- case .checkBox: return "CheckBox"
68
- case .menuItem: return "MenuItem"
69
- case .webView: return "WebView"
70
- case .other: return "Other"
71
- default:
72
- switch type.rawValue {
73
- case 19:
74
- return "Keyboard"
75
- case 20:
76
- return "Key"
77
- default:
78
- return "Element(\(type.rawValue))"
79
- }
80
- }
88
+ Self.elementTypeNamesByRawValue[type.rawValue] ?? "Element(\(type.rawValue))"
81
89
  }
82
90
 
83
91
  static let structuralOnlyNodeTypes: Set<String> = [
@@ -108,7 +116,7 @@ extension RunnerTests {
108
116
  // `boundedBlockingSystemAlertSnapshot`'s probe closure (see `systemModalProbeOverrideForTesting`
109
117
  // in RunnerTests.swift), so reverting this entry point to bypass the bounded probe fails the
110
118
  // regression test.
111
- func snapshotFast(app: XCUIApplication, options: SnapshotOptions) throws -> DataPayload {
119
+ func snapshotFast(app: XCUIApplication, options: PresentationOptions) throws -> DataPayload {
112
120
  let deadline = Date().addingTimeInterval(Self.snapshotPlanBudget)
113
121
  if let blocking = boundedBlockingSystemAlertSnapshot(deadline: deadline) {
114
122
  return blocking
@@ -122,10 +130,10 @@ extension RunnerTests {
122
130
  )
123
131
  }
124
132
 
125
- func recursiveTreeSnapshotPayload(
133
+ func recursiveTreeSnapshotAcquisition(
126
134
  context: SnapshotTraversalContext,
127
- options: SnapshotOptions
128
- ) -> DataPayload {
135
+ hint: CaptureHint
136
+ ) -> SnapshotAcquisition {
129
137
  var cachedDescendantElements: [XCUIElement]?
130
138
  func collapsedTabDescendants() -> [XCUIElement] {
131
139
  if let cachedDescendantElements {
@@ -138,66 +146,68 @@ extension RunnerTests {
138
146
  return result.elements
139
147
  }
140
148
 
141
- var nodes: [SnapshotNode] = []
142
- var hiddenContentHintsByNodeIndex: [Int: (above: Bool, below: Bool)] = [:]
143
- let rootEvaluation = evaluateSnapshot(context.rootSnapshot, in: context)
149
+ // Acquisition serializes facts: every traversed node is emitted at raw traversal depth, and
150
+ // the regular projection's clip fold runs once inside `SnapshotPresentation` (#1797). The two
151
+ // walks this backend keeps are the raw budget or regular presented-depth frontier, plus
152
+ // collapsed-tab augmentation which needs live element handles; neither walk publishes a
153
+ // presentation node.
154
+ var nodes: [RawAXNode] = []
155
+ let rootEvaluation = evaluateSnapshot(context.rootSnapshot)
144
156
  nodes.append(
145
157
  makeSnapshotNode(
146
158
  snapshot: context.rootSnapshot,
147
159
  evaluation: rootEvaluation,
148
160
  depth: 0,
149
161
  index: 0,
150
- parentIndex: nil
162
+ parentIndex: nil,
163
+ viewport: context.viewport
151
164
  )
152
165
  )
153
- if context.maxDepth > 0 {
166
+ let shouldVisitRootChildren = SnapshotPresentation.shouldAcquireChildren(
167
+ for: hint,
168
+ rawDepth: 0,
169
+ regularPresentedDepth: 0
170
+ )
171
+ if shouldVisitRootChildren {
154
172
  appendCollapsedTabFallbackNodes(
155
173
  to: &nodes,
156
174
  containerSnapshot: context.rootSnapshot,
157
175
  resolveElements: collapsedTabDescendants,
158
176
  depth: 1,
159
- parentIndex: 0
177
+ parentIndex: 0,
178
+ viewport: context.viewport
160
179
  )
161
180
  }
162
181
 
163
182
  var seen = Set<String>()
164
- let rootScrollAnchor = scrollContainerAnchor(
165
- for: context.rootSnapshot,
166
- visible: rootEvaluation.visible,
167
- nodeIndex: 0
168
- )
169
- var stack: [(XCUIElementSnapshot, Int, Int, Int?, (index: Int, rect: CGRect)?)] =
170
- context.rootSnapshot.children.map {
171
- ($0, 1, 1, 0, rootScrollAnchor)
183
+ var stack: [SnapshotTraversalEntry] = []
184
+ if shouldVisitRootChildren {
185
+ stack = context.rootSnapshot.children.map {
186
+ SnapshotTraversalEntry(
187
+ snapshot: $0,
188
+ depth: 1,
189
+ parentIndex: 0,
190
+ parentPresentedDepth: 0,
191
+ parentTraversal: .root
192
+ )
172
193
  }
194
+ }
173
195
 
174
- while let (snapshot, depth, visibleDepth, parentIndex, nearestScrollAnchor) = stack.popLast() {
175
- if let limit = options.depth, depth > limit { continue }
196
+ while let entry = stack.popLast() {
197
+ let snapshot = entry.snapshot
198
+ let depth = entry.depth
199
+ let parentIndex = entry.parentIndex
200
+ if let limit = hint.rawTraversalDepth, depth > limit { continue }
176
201
 
177
- let evaluation = evaluateSnapshot(snapshot, in: context)
178
- let regularVisible = isVisibleInRegularSnapshot(
179
- snapshot.frame,
180
- viewport: context.viewport,
181
- scrollContainerAnchor: nearestScrollAnchor
182
- )
183
- if !regularVisible, let nearestScrollAnchor {
184
- rememberHiddenContentHint(
185
- for: snapshot.frame,
186
- relativeTo: nearestScrollAnchor,
187
- hints: &hiddenContentHintsByNodeIndex
188
- )
189
- }
190
- let include = shouldInclude(
202
+ let evaluation = evaluateSnapshot(snapshot)
203
+ let node = makeSnapshotNode(
191
204
  snapshot: snapshot,
192
- label: evaluation.label,
193
- identifier: evaluation.identifier,
194
- valueText: evaluation.valueText,
195
- options: options,
196
- hittable: evaluation.hittable,
197
- visible: regularVisible,
198
- regularSnapshot: true
205
+ evaluation: evaluation,
206
+ depth: depth,
207
+ index: nodes.count,
208
+ parentIndex: parentIndex,
209
+ viewport: context.viewport
199
210
  )
200
-
201
211
  let key = Self.snapshotTraversalIdentity(
202
212
  elementType: snapshot.elementType,
203
213
  label: evaluation.label,
@@ -209,58 +219,58 @@ extension RunnerTests {
209
219
  seen.insert(key)
210
220
  }
211
221
 
212
- let currentIndex = include && !isDuplicate ? nodes.count : parentIndex
213
- if depth < context.maxDepth {
214
- let nextVisibleDepth = include && !isDuplicate ? visibleDepth + 1 : visibleDepth
215
- let nextScrollContainerAnchor: (index: Int, rect: CGRect)?
216
- if include && !isDuplicate {
217
- nextScrollContainerAnchor =
218
- scrollContainerAnchor(
219
- for: snapshot,
220
- visible: regularVisible,
221
- nodeIndex: currentIndex
222
- )
223
- ?? nearestScrollAnchor
224
- } else {
225
- nextScrollContainerAnchor = nearestScrollAnchor
226
- }
222
+ let currentIndex = !isDuplicate ? nodes.count : parentIndex
223
+ let transition = SnapshotPresentation.regularTraversalTransition(
224
+ for: node,
225
+ parentPresentedDepth: entry.parentPresentedDepth,
226
+ parentTraversal: entry.parentTraversal,
227
+ hint: hint,
228
+ rawDepth: depth,
229
+ viewport: context.viewport,
230
+ hasChildren: !snapshot.children.isEmpty,
231
+ isDuplicate: isDuplicate,
232
+ policy: .platformDefault
233
+ )
234
+ if transition.shouldVisitChildren {
227
235
  for child in snapshot.children.reversed() {
228
- stack.append((child, depth + 1, nextVisibleDepth, currentIndex, nextScrollContainerAnchor))
236
+ stack.append(
237
+ SnapshotTraversalEntry(
238
+ snapshot: child,
239
+ depth: depth + 1,
240
+ parentIndex: currentIndex,
241
+ parentPresentedDepth: transition.presentedDepth,
242
+ parentTraversal: transition.traversal
243
+ )
244
+ )
229
245
  }
230
246
  }
231
247
 
232
- if !include || isDuplicate { continue }
248
+ if isDuplicate { continue }
233
249
 
234
- let index = nodes.count
235
- nodes.append(
236
- makeSnapshotNode(
237
- snapshot: snapshot,
238
- evaluation: evaluation,
239
- depth: min(context.maxDepth, visibleDepth),
240
- index: index,
241
- parentIndex: parentIndex
242
- )
243
- )
244
- if visibleDepth < context.maxDepth {
250
+ nodes.append(node)
251
+ if transition.shouldVisitChildren {
245
252
  appendCollapsedTabFallbackNodes(
246
253
  to: &nodes,
247
254
  containerSnapshot: snapshot,
248
255
  resolveElements: collapsedTabDescendants,
249
- depth: visibleDepth + 1,
250
- parentIndex: index
256
+ depth: depth + 1,
257
+ parentIndex: node.index,
258
+ viewport: context.viewport
251
259
  )
252
260
  }
253
-
254
261
  }
255
262
 
256
- return DataPayload(
257
- nodes: applyHiddenContentHints(hiddenContentHintsByNodeIndex, to: nodes),
258
- truncated: false
263
+ return SnapshotAcquisition(
264
+ hint: hint,
265
+ nodes: nodes,
266
+ truncated: false,
267
+ effectiveDepth: nil,
268
+ viewport: context.viewport
259
269
  )
260
270
  }
261
271
 
262
272
  // See `snapshotFast` above: the single production entry point, no unit-test overload.
263
- func snapshotRaw(app: XCUIApplication, options: SnapshotOptions) throws -> DataPayload {
273
+ func snapshotRaw(app: XCUIApplication, options: PresentationOptions) throws -> DataPayload {
264
274
  let deadline = Date().addingTimeInterval(Self.snapshotPlanBudget)
265
275
  if let blocking = boundedBlockingSystemAlertSnapshot(deadline: deadline) {
266
276
  return blocking
@@ -306,7 +316,6 @@ extension RunnerTests {
306
316
  let startedAt = Date()
307
317
  do {
308
318
  return try runMainThreadWork(
309
- command: nil,
310
319
  timeout: slice,
311
320
  timeoutError: {
312
321
  SnapshotCaptureFailure(
@@ -351,40 +360,30 @@ extension RunnerTests {
351
360
  return min(budget, deadlineRemaining)
352
361
  }
353
362
 
354
- func rawTreeSnapshotPayload(
363
+ func rawTreeSnapshotAcquisition(
355
364
  context: SnapshotTraversalContext,
356
- options: SnapshotOptions
357
- ) throws -> DataPayload {
358
- var nodes: [SnapshotNode] = []
365
+ hint: CaptureHint
366
+ ) throws -> SnapshotAcquisition {
367
+ var nodes: [RawAXNode] = []
359
368
 
360
369
  func walk(_ snapshot: XCUIElementSnapshot, depth: Int, parentIndex: Int?) throws {
361
- if let limit = options.depth, depth > limit { return }
370
+ if let limit = hint.rawTraversalDepth, depth > limit { return }
362
371
 
363
- let evaluation = evaluateSnapshot(snapshot, in: context)
364
- let include = shouldInclude(
365
- snapshot: snapshot,
366
- label: evaluation.label,
367
- identifier: evaluation.identifier,
368
- valueText: evaluation.valueText,
369
- options: options,
370
- hittable: evaluation.hittable,
371
- visible: evaluation.visible
372
- )
373
- let currentIndex = include ? nodes.count : parentIndex
374
- if include {
375
- if nodes.count >= Self.rawSnapshotMaxNodes {
376
- throw rawSnapshotTooLargeFailure(nodeCount: nodes.count + 1)
377
- }
378
- nodes.append(
379
- makeSnapshotNode(
380
- snapshot: snapshot,
381
- evaluation: evaluation,
382
- depth: depth,
383
- index: nodes.count,
384
- parentIndex: parentIndex
385
- )
386
- )
372
+ let evaluation = evaluateSnapshot(snapshot)
373
+ if nodes.count >= Self.rawSnapshotMaxNodes {
374
+ throw rawSnapshotTooLargeFailure(nodeCount: nodes.count + 1)
387
375
  }
376
+ let currentIndex = nodes.count
377
+ nodes.append(
378
+ makeSnapshotNode(
379
+ snapshot: snapshot,
380
+ evaluation: evaluation,
381
+ depth: depth,
382
+ index: currentIndex,
383
+ parentIndex: parentIndex,
384
+ viewport: context.viewport
385
+ )
386
+ )
388
387
 
389
388
  let children = snapshot.children
390
389
  for child in children {
@@ -393,30 +392,42 @@ extension RunnerTests {
393
392
  }
394
393
 
395
394
  try walk(context.rootSnapshot, depth: 0, parentIndex: nil)
396
- return DataPayload(nodes: nodes, truncated: false)
395
+ return SnapshotAcquisition(
396
+ hint: hint,
397
+ nodes: nodes,
398
+ truncated: false,
399
+ effectiveDepth: nil,
400
+ viewport: context.viewport
401
+ )
397
402
  }
398
403
 
399
- func snapshotFlatInteractive(
404
+ func querySweepSnapshotAcquisition(
400
405
  app: XCUIApplication,
401
- options: SnapshotOptions,
406
+ hint: CaptureHint,
402
407
  planDeadline: Date = .distantFuture
403
- ) -> DataPayload {
404
- var nodes: [SnapshotNode] = [
408
+ ) -> SnapshotAcquisition {
409
+ var nodes: [RawAXNode] = [
405
410
  interactiveRootNode(rect: .zero)
406
411
  ]
407
- if options.depth == 0 {
408
- return DataPayload(nodes: nodes, truncated: false)
412
+ if hint.rawTraversalDepth == 0 || hint.regularPresentedDepth == 0 {
413
+ return SnapshotAcquisition(
414
+ hint: hint,
415
+ nodes: nodes,
416
+ truncated: false,
417
+ effectiveDepth: nil,
418
+ viewport: .infinite
419
+ )
409
420
  }
410
421
 
411
422
  // Bounded by both its own sweep budget and the umbrella capture-plan deadline, so a
412
423
  // chained recovery tier can never push the plan past the main-thread watchdog (#1105).
413
- let sweepDeadline = options.interactiveOnly
424
+ let sweepDeadline = hint.interactiveOnly
414
425
  ? Date().addingTimeInterval(Self.flatInteractiveFallbackBudget)
415
426
  : Date.distantFuture
416
427
  let deadline = min(sweepDeadline, planDeadline)
417
428
  let viewport = safeSnapshotViewport(app: app)
418
429
  var seen = Set<String>()
419
- var candidates: [SnapshotNode] = []
430
+ var candidates: [RawAXNode] = []
420
431
  let flatElements = flatInteractiveElements(app: app, deadline: deadline)
421
432
  var truncated = flatElements.truncated
422
433
  for element in flatElements.elements {
@@ -429,8 +440,7 @@ extension RunnerTests {
429
440
  element: element,
430
441
  index: 0,
431
442
  parentIndex: 0,
432
- viewport: viewport,
433
- options: options
443
+ viewport: viewport
434
444
  ) else {
435
445
  continue
436
446
  }
@@ -458,7 +468,7 @@ extension RunnerTests {
458
468
  nodes[0] = interactiveRootNode(rect: rootRect)
459
469
  for candidate in candidates {
460
470
  nodes.append(
461
- SnapshotNode(
471
+ RawAXNode(
462
472
  index: nodes.count,
463
473
  type: candidate.type,
464
474
  label: candidate.label,
@@ -476,7 +486,13 @@ extension RunnerTests {
476
486
  )
477
487
  )
478
488
  }
479
- return DataPayload(nodes: nodes, truncated: truncated)
489
+ return SnapshotAcquisition(
490
+ hint: hint,
491
+ nodes: nodes,
492
+ truncated: truncated,
493
+ effectiveDepth: nil,
494
+ viewport: viewport
495
+ )
480
496
  }
481
497
 
482
498
  func snapshotAccessibilityUnavailable(failure: SnapshotCaptureFailure) -> DataPayload {
@@ -521,7 +537,7 @@ extension RunnerTests {
521
537
  ) -> DataPayload {
522
538
  return DataPayload(
523
539
  message: message,
524
- nodes: [interactiveRootNode(rect: .zero)],
540
+ nodes: [SnapshotPresentation.singleElementRead(interactiveRootNode(rect: .zero))],
525
541
  truncated: true,
526
542
  snapshotQuality: snapshotQuality,
527
543
  runnerFatal: runnerFatal,
@@ -530,8 +546,8 @@ extension RunnerTests {
530
546
  }
531
547
 
532
548
 
533
- private func interactiveRootNode(rect: CGRect) -> SnapshotNode {
534
- SnapshotNode(
549
+ private func interactiveRootNode(rect: CGRect) -> RawAXNode {
550
+ RawAXNode(
535
551
  index: 0,
536
552
  type: "Application",
537
553
  label: nil,
@@ -549,7 +565,7 @@ extension RunnerTests {
549
565
  )
550
566
  }
551
567
 
552
- private func interactiveRootFrame(for candidates: [SnapshotNode]) -> CGRect {
568
+ private func interactiveRootFrame(for candidates: [RawAXNode]) -> CGRect {
553
569
  guard !candidates.isEmpty else {
554
570
  return .zero
555
571
  }
@@ -569,87 +585,29 @@ extension RunnerTests {
569
585
 
570
586
  // MARK: - Snapshot Filtering
571
587
 
572
- private func shouldInclude(
573
- snapshot: XCUIElementSnapshot,
574
- label: String,
575
- identifier: String,
576
- valueText: String?,
577
- options: SnapshotOptions,
578
- hittable: Bool,
579
- visible: Bool,
580
- regularSnapshot: Bool = false
581
- ) -> Bool {
582
- let type = snapshot.elementType
583
- let hasContent = !label.isEmpty || !identifier.isEmpty || (valueText != nil)
584
- if options.interactiveOnly {
585
- if isScrollableContainer(snapshot, visible: visible) { return true }
586
- #if os(macOS)
587
- if !visible && type != .application {
588
- return false
589
- }
590
- #endif
591
- if interactiveTypes.contains(type) { return true }
592
- if hittable && type != .other { return true }
593
- if hasContent { return true }
594
- return false
595
- }
596
- if regularSnapshot {
597
- if type == .application || type == .window { return true }
598
- return visible
599
- }
600
- return true
601
- }
602
-
603
- private func computedSnapshotHittable(
604
- _ snapshot: XCUIElementSnapshot,
605
- viewport: CGRect,
606
- laterNodes: ArraySlice<XCUIElementSnapshot>
607
- ) -> Bool {
608
- guard snapshot.isEnabled else { return false }
609
- let frame = snapshot.frame
610
- if frame.isNull || frame.isEmpty { return false }
611
- let center = CGPoint(x: frame.midX, y: frame.midY)
612
- if !viewport.contains(center) { return false }
613
- for node in laterNodes {
614
- if !isOccludingType(node.elementType) { continue }
615
- let nodeFrame = node.frame
616
- if nodeFrame.isNull || nodeFrame.isEmpty { continue }
617
- if nodeFrame.contains(center) { return false }
618
- }
619
- return true
620
- }
621
-
622
588
  func makeSnapshotTraversalContext(
623
589
  app: XCUIApplication,
624
- options: SnapshotOptions,
590
+ hint: CaptureHint,
625
591
  captureDeadline: Date = .distantFuture,
626
592
  treeCaptureSliceBudgetOverride: TimeInterval? = nil
627
593
  ) throws -> SnapshotTraversalContext? {
628
- let (viewport, queryRoot) = try runMainThreadWork(
629
- command: nil,
594
+ let viewport = try runMainThreadWork(
630
595
  timeout: min(1.0, max(0.1, captureDeadline.timeIntervalSinceNow)),
631
596
  timeoutError: snapshotMainThreadTimeoutError("preparing tree snapshot")
632
597
  ) {
633
- (
634
- self.safeSnapshotViewport(app: app),
635
- options.scope.flatMap { self.findScopeElement(app: app, scope: $0) } ?? app
636
- )
598
+ self.safeSnapshotViewport(app: app)
637
599
  }
638
600
 
639
601
  let treeSliceBudget = treeCaptureSliceBudgetOverride ?? treeCaptureSliceBudget
640
602
  let slice = min(treeSliceBudget, max(0.5, captureDeadline.timeIntervalSinceNow))
641
- guard let rootSnapshot = try captureSnapshotRootBounded(queryRoot, sliceSeconds: slice) else {
603
+ guard let rootSnapshot = try captureSnapshotRootBounded(app, sliceSeconds: slice) else {
642
604
  return nil
643
605
  }
644
606
 
645
- let (flatSnapshots, snapshotRanges) = flattenedSnapshots(rootSnapshot)
646
607
  return SnapshotTraversalContext(
647
- queryRoot: queryRoot,
608
+ queryRoot: app,
648
609
  rootSnapshot: rootSnapshot,
649
- viewport: viewport,
650
- flatSnapshots: flatSnapshots,
651
- snapshotRanges: snapshotRanges,
652
- maxDepth: options.depth ?? Int.max
610
+ viewport: viewport
653
611
  )
654
612
  }
655
613
 
@@ -688,7 +646,6 @@ extension RunnerTests {
688
646
  return try captureSnapshotRoot(element)
689
647
  }
690
648
  return try runMainThreadWork(
691
- command: nil,
692
649
  timeout: sliceSeconds,
693
650
  timeoutError: treeCaptureTimeoutError(sliceSeconds: sliceSeconds),
694
651
  onAbandoned: {
@@ -786,26 +743,16 @@ extension RunnerTests {
786
743
  failure.code == Self.axSnapshotErrorCode || isAxIllegalArgument(failure.message)
787
744
  }
788
745
 
789
- private func evaluateSnapshot(
790
- _ snapshot: XCUIElementSnapshot,
791
- in context: SnapshotTraversalContext
792
- ) -> SnapshotEvaluation {
746
+ private func evaluateSnapshot(_ snapshot: XCUIElementSnapshot) -> SnapshotEvaluation {
793
747
  let label = aggregatedLabel(for: snapshot) ?? snapshot.label.trimmingCharacters(in: .whitespacesAndNewlines)
794
748
  let identifier = snapshot.identifier.trimmingCharacters(in: .whitespacesAndNewlines)
795
749
  let valueText = snapshotValueText(snapshot)
796
- let laterNodes = laterSnapshots(
797
- for: snapshot,
798
- in: context.flatSnapshots,
799
- ranges: context.snapshotRanges
800
- )
801
750
  return SnapshotEvaluation(
802
751
  label: label,
803
752
  identifier: identifier,
804
753
  valueText: valueText,
805
- hittable: computedSnapshotHittable(snapshot, viewport: context.viewport, laterNodes: laterNodes),
806
754
  focused: snapshotHasFocus(snapshot),
807
- selected: snapshotIsSelected(snapshot),
808
- visible: isVisibleInViewport(snapshot.frame, context.viewport)
755
+ selected: snapshotIsSelected(snapshot)
809
756
  )
810
757
  }
811
758
 
@@ -814,9 +761,10 @@ extension RunnerTests {
814
761
  evaluation: SnapshotEvaluation,
815
762
  depth: Int,
816
763
  index: Int,
817
- parentIndex: Int?
818
- ) -> SnapshotNode {
819
- return SnapshotNode(
764
+ parentIndex: Int?,
765
+ viewport: CGRect
766
+ ) -> RawAXNode {
767
+ return RawAXNode(
820
768
  index: index,
821
769
  type: elementTypeName(snapshot.elementType),
822
770
  label: evaluation.label.isEmpty ? nil : evaluation.label,
@@ -826,7 +774,11 @@ extension RunnerTests {
826
774
  enabled: snapshot.isEnabled,
827
775
  focused: evaluation.focused ? true : nil,
828
776
  selected: evaluation.selected ? true : nil,
829
- hittable: evaluation.hittable,
777
+ hittable: parentIndex != nil && SnapshotGeometry.isGeometricallyActionable(
778
+ enabled: snapshot.isEnabled,
779
+ frame: snapshot.frame,
780
+ viewport: viewport
781
+ ),
830
782
  depth: depth,
831
783
  parentIndex: parentIndex,
832
784
  hiddenContentAbove: nil,
@@ -834,52 +786,6 @@ extension RunnerTests {
834
786
  )
835
787
  }
836
788
 
837
- private func isOccludingType(_ type: XCUIElement.ElementType) -> Bool {
838
- switch type {
839
- case .application, .window:
840
- return false
841
- default:
842
- return true
843
- }
844
- }
845
-
846
- private func flattenedSnapshots(
847
- _ root: XCUIElementSnapshot
848
- ) -> ([XCUIElementSnapshot], [ObjectIdentifier: (Int, Int)]) {
849
- var ordered: [XCUIElementSnapshot] = []
850
- var ranges: [ObjectIdentifier: (Int, Int)] = [:]
851
-
852
- @discardableResult
853
- func visit(_ snapshot: XCUIElementSnapshot) -> Int {
854
- let start = ordered.count
855
- ordered.append(snapshot)
856
- var end = start
857
- for child in snapshot.children {
858
- end = max(end, visit(child))
859
- }
860
- ranges[ObjectIdentifier(snapshot)] = (start, end)
861
- return end
862
- }
863
-
864
- _ = visit(root)
865
- return (ordered, ranges)
866
- }
867
-
868
- private func laterSnapshots(
869
- for snapshot: XCUIElementSnapshot,
870
- in ordered: [XCUIElementSnapshot],
871
- ranges: [ObjectIdentifier: (Int, Int)]
872
- ) -> ArraySlice<XCUIElementSnapshot> {
873
- guard let (_, subtreeEnd) = ranges[ObjectIdentifier(snapshot)] else {
874
- return ordered.suffix(from: ordered.count)
875
- }
876
- let nextIndex = subtreeEnd + 1
877
- if nextIndex >= ordered.count {
878
- return ordered.suffix(from: ordered.count)
879
- }
880
- return ordered.suffix(from: nextIndex)
881
- }
882
-
883
789
  private func snapshotValueText(_ snapshot: XCUIElementSnapshot) -> String? {
884
790
  guard let value = snapshot.value else { return nil }
885
791
  let text = String(describing: value).trimmingCharacters(in: .whitespacesAndNewlines)
@@ -920,121 +826,33 @@ extension RunnerTests {
920
826
  return nil
921
827
  }
922
828
 
923
- func isVisibleInViewport(_ rect: CGRect, _ viewport: CGRect) -> Bool {
924
- if rect.isNull || rect.isEmpty { return false }
925
- return rect.intersects(viewport)
926
- }
927
-
928
- func isVisibleInRegularSnapshot(
929
- _ rect: CGRect,
930
- viewport: CGRect,
931
- scrollContainerAnchor: (index: Int, rect: CGRect)?
932
- ) -> Bool {
933
- if !isVisibleInViewport(rect, viewport) { return false }
934
- guard let scrollContainerAnchor else { return true }
935
- return isVisibleInViewport(rect, scrollContainerAnchor.rect)
936
- }
937
-
938
829
  private func appendCollapsedTabFallbackNodes(
939
- to nodes: inout [SnapshotNode],
830
+ to nodes: inout [RawAXNode],
940
831
  containerSnapshot: XCUIElementSnapshot,
941
832
  resolveElements: () -> [XCUIElement],
942
833
  depth: Int,
943
- parentIndex: Int
834
+ parentIndex: Int,
835
+ viewport: CGRect
944
836
  ) {
945
837
  let fallbackNodes = collapsedTabFallbackNodes(
946
838
  for: containerSnapshot,
947
839
  resolveElements: resolveElements,
948
840
  startingIndex: nodes.count,
949
841
  depth: depth,
950
- parentIndex: parentIndex
842
+ parentIndex: parentIndex,
843
+ viewport: viewport
951
844
  )
952
845
  nodes.append(contentsOf: fallbackNodes)
953
846
  }
954
847
 
955
- private func scrollContainerAnchor(
956
- for snapshot: XCUIElementSnapshot,
957
- visible: Bool,
958
- nodeIndex: Int?
959
- ) -> (index: Int, rect: CGRect)? {
960
- return scrollContainerAnchor(
961
- for: snapshot.elementType,
962
- hasChildren: !snapshot.children.isEmpty,
963
- visible: visible,
964
- frame: snapshot.frame,
965
- nodeIndex: nodeIndex
966
- )
967
- }
968
-
969
- func scrollContainerAnchor(
970
- for elementType: XCUIElement.ElementType,
971
- hasChildren: Bool,
972
- visible: Bool,
973
- frame: CGRect,
974
- nodeIndex: Int?
975
- ) -> (index: Int, rect: CGRect)? {
976
- guard let nodeIndex else { return nil }
977
- if !isScrollableContainer(
978
- elementType: elementType,
979
- hasChildren: hasChildren,
980
- visible: visible
981
- ) { return nil }
982
- return (nodeIndex, frame)
983
- }
984
-
985
- func rememberHiddenContentHint(
986
- for frame: CGRect,
987
- relativeTo scrollContainerAnchor: (index: Int, rect: CGRect),
988
- hints: inout [Int: (above: Bool, below: Bool)]
989
- ) {
990
- if frame.isNull || frame.isEmpty { return }
991
- var hint = hints[scrollContainerAnchor.index] ?? (above: false, below: false)
992
- if frame.maxY <= scrollContainerAnchor.rect.minY {
993
- hint.above = true
994
- } else if frame.minY >= scrollContainerAnchor.rect.maxY {
995
- hint.below = true
996
- } else {
997
- return
998
- }
999
- hints[scrollContainerAnchor.index] = hint
1000
- }
1001
-
1002
- func applyHiddenContentHints(
1003
- _ hints: [Int: (above: Bool, below: Bool)],
1004
- to nodes: [SnapshotNode]
1005
- ) -> [SnapshotNode] {
1006
- if hints.isEmpty { return nodes }
1007
- return nodes.map { node in
1008
- guard let hint = hints[node.index] else { return node }
1009
- let hiddenContentAbove: Bool? = (node.hiddenContentAbove == true || hint.above) ? true : nil
1010
- let hiddenContentBelow: Bool? = (node.hiddenContentBelow == true || hint.below) ? true : nil
1011
- return SnapshotNode(
1012
- index: node.index,
1013
- type: node.type,
1014
- label: node.label,
1015
- identifier: node.identifier,
1016
- value: node.value,
1017
- rect: node.rect,
1018
- enabled: node.enabled,
1019
- focused: node.focused,
1020
- selected: node.selected,
1021
- hittable: node.hittable,
1022
- depth: node.depth,
1023
- parentIndex: node.parentIndex,
1024
- hiddenContentAbove: hiddenContentAbove,
1025
- hiddenContentBelow: hiddenContentBelow,
1026
- actions: node.actions
1027
- )
1028
- }
1029
- }
1030
-
1031
848
  private func collapsedTabFallbackNodes(
1032
849
  for containerSnapshot: XCUIElementSnapshot,
1033
850
  resolveElements: () -> [XCUIElement],
1034
851
  startingIndex: Int,
1035
852
  depth: Int,
1036
- parentIndex: Int
1037
- ) -> [SnapshotNode] {
853
+ parentIndex: Int,
854
+ viewport: CGRect
855
+ ) -> [RawAXNode] {
1038
856
  if !containerSnapshot.children.isEmpty { return [] }
1039
857
  guard shouldExpandCollapsedTabContainer(containerSnapshot) else { return [] }
1040
858
  let containerFrame = containerSnapshot.frame
@@ -1047,7 +865,8 @@ extension RunnerTests {
1047
865
  collapsedTabCandidateNode(
1048
866
  element: element,
1049
867
  containerSnapshot: containerSnapshot,
1050
- containerFrame: containerFrame
868
+ containerFrame: containerFrame,
869
+ viewport: viewport
1051
870
  )
1052
871
  }
1053
872
  .sorted { left, right in
@@ -1073,7 +892,7 @@ extension RunnerTests {
1073
892
  if uniqueCandidates.count < 2 { return [] }
1074
893
 
1075
894
  return uniqueCandidates.enumerated().map { offset, node in
1076
- SnapshotNode(
895
+ RawAXNode(
1077
896
  index: startingIndex + offset,
1078
897
  type: node.type,
1079
898
  label: node.label,
@@ -1095,9 +914,10 @@ extension RunnerTests {
1095
914
  private func collapsedTabCandidateNode(
1096
915
  element: XCUIElement,
1097
916
  containerSnapshot: XCUIElementSnapshot,
1098
- containerFrame: CGRect
1099
- ) -> SnapshotNode? {
1100
- var node: SnapshotNode?
917
+ containerFrame: CGRect,
918
+ viewport: CGRect
919
+ ) -> RawAXNode? {
920
+ var node: RawAXNode?
1101
921
  let exceptionMessage = RunnerObjCExceptionCatcher.catchException({
1102
922
  if !element.exists { return }
1103
923
  let elementType = element.elementType
@@ -1125,7 +945,7 @@ extension RunnerTests {
1125
945
  return
1126
946
  }
1127
947
 
1128
- node = SnapshotNode(
948
+ node = RawAXNode(
1129
949
  index: 0,
1130
950
  type: elementTypeName(elementType),
1131
951
  label: label.isEmpty ? nil : label,
@@ -1135,7 +955,11 @@ extension RunnerTests {
1135
955
  enabled: element.isEnabled,
1136
956
  focused: elementHasFocus(element) ? true : nil,
1137
957
  selected: element.isSelected ? true : nil,
1138
- hittable: element.isHittable,
958
+ hittable: SnapshotGeometry.isGeometricallyActionable(
959
+ enabled: element.isEnabled,
960
+ frame: frame,
961
+ viewport: viewport
962
+ ),
1139
963
  depth: 0,
1140
964
  parentIndex: nil,
1141
965
  hiddenContentAbove: nil,
@@ -1261,42 +1085,27 @@ extension RunnerTests {
1261
1085
  element: XCUIElement,
1262
1086
  index: Int,
1263
1087
  parentIndex: Int?,
1264
- viewport: CGRect,
1265
- options: SnapshotOptions
1266
- ) -> SnapshotNode? {
1267
- var node: SnapshotNode?
1088
+ viewport: CGRect
1089
+ ) -> RawAXNode? {
1090
+ var node: RawAXNode?
1268
1091
  let exceptionMessage = RunnerObjCExceptionCatcher.catchException({
1269
1092
  if !element.exists { return }
1093
+ // Declared residue: a flat element query has no hierarchy for geometryless semantics to
1094
+ // attach to, so frameless elements are dropped at acquisition rather than presented.
1270
1095
  let frame = element.frame
1271
1096
  if frame.isNull || frame.isEmpty { return }
1272
- let visible = isVisibleInViewport(frame, viewport)
1273
- if options.interactiveOnly && !visible { return }
1274
- #if os(macOS)
1275
- if !visible { return }
1276
- #endif
1277
1097
  let label = element.label.trimmingCharacters(in: .whitespacesAndNewlines)
1278
1098
  let identifier = element.identifier.trimmingCharacters(in: .whitespacesAndNewlines)
1279
1099
  let valueText = snapshotValueText(element)
1280
1100
  let elementType = element.elementType
1281
1101
  let enabled = element.isEnabled
1282
- let hittable = visible && enabled && element.isHittable
1283
- let filterNode = FlatSnapshotFilterNode(
1284
- isRoot: false,
1285
- label: label,
1286
- identifier: identifier,
1287
- valueText: valueText,
1288
- visible: visible
1102
+ let hittable = SnapshotGeometry.isGeometricallyActionable(
1103
+ enabled: enabled,
1104
+ frame: frame,
1105
+ viewport: viewport
1289
1106
  )
1290
- if !flatSnapshotFilterDecision(
1291
- filterNode,
1292
- options: options,
1293
- visibilityPolicy: .interactiveOnly,
1294
- insideMatchedScope: false
1295
- ).include {
1296
- return
1297
- }
1298
1107
 
1299
- node = SnapshotNode(
1108
+ node = RawAXNode(
1300
1109
  index: index,
1301
1110
  type: elementTypeName(elementType),
1302
1111
  label: label.isEmpty ? nil : label,
@@ -1320,19 +1129,4 @@ extension RunnerTests {
1320
1129
  return node
1321
1130
  }
1322
1131
 
1323
- private func isScrollableContainer(_ snapshot: XCUIElementSnapshot, visible: Bool) -> Bool {
1324
- return isScrollableContainer(
1325
- elementType: snapshot.elementType,
1326
- hasChildren: !snapshot.children.isEmpty,
1327
- visible: visible
1328
- )
1329
- }
1330
-
1331
- private func isScrollableContainer(
1332
- elementType: XCUIElement.ElementType,
1333
- hasChildren: Bool,
1334
- visible: Bool
1335
- ) -> Bool {
1336
- return visible && hasChildren && Self.scrollContainerTypes.contains(elementType)
1337
- }
1338
1132
  }