agent-device 0.21.6 → 0.21.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (317) hide show
  1. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.21.6.apk → agent-device-android-ime-helper-0.21.8.apk} +0 -0
  2. package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.8.apk.sha256 +1 -0
  3. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.21.6.manifest.json → agent-device-android-ime-helper-0.21.8.manifest.json} +4 -4
  4. package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.21.6.apk → agent-device-android-snapshot-helper-0.21.8.apk} +0 -0
  5. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.8.apk.sha256 +1 -0
  6. package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.21.6.manifest.json → agent-device-android-snapshot-helper-0.21.8.manifest.json} +6 -6
  7. package/apple/macos-helper/Package.swift +16 -1
  8. package/apple/macos-helper/Sources/AgentDeviceMacOSDeviceHub/DeviceHubPose.swift +47 -0
  9. package/apple/macos-helper/Sources/AgentDeviceMacOSHelper/DeviceHubPose.swift +320 -0
  10. package/apple/macos-helper/Sources/AgentDeviceMacOSHelper/main.swift +66 -13
  11. package/apple/macos-helper/Sources/AgentDeviceMacOSInput/MouseClickDelivery.swift +116 -0
  12. package/apple/macos-helper/Sources/AgentDeviceMacOSInput/MouseClickSchedule.swift +61 -0
  13. package/apple/macos-helper/Tests/AgentDeviceMacOSDeviceHubTests/DeviceHubPoseTests.swift +30 -0
  14. package/apple/macos-helper/Tests/AgentDeviceMacOSInputTests/MouseClickScheduleTests.swift +58 -0
  15. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.h +4 -0
  16. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.m +20 -0
  17. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerScrollViewportPolicy.swift +1 -2
  18. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+AXSnapshotFallback.swift +25 -12
  19. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +56 -1
  20. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandJournal.swift +1 -1
  21. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +36 -108
  22. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Keyboard.swift +241 -0
  23. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Lifecycle.swift +29 -0
  24. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +46 -0
  25. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+PrivateAXPresentation.swift +85 -26
  26. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +134 -117
  27. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotBackendCapabilities.swift +1 -3
  28. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCapturePlan.swift +25 -4
  29. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SystemModal.swift +1 -1
  30. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +3 -0
  31. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotCoordinateSpace.swift +197 -0
  32. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotModels.swift +30 -4
  33. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotPresentation.swift +0 -81
  34. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotPresentationInvariant.swift +1 -9
  35. package/dist/src/absence-observation-errors.js +1 -1
  36. package/dist/src/adapter.js +1 -1
  37. package/dist/src/adb-executor.js +2 -2
  38. package/dist/src/adb-failure.js +1 -1
  39. package/dist/src/adb-transport.js +1 -1
  40. package/dist/src/adb.js +1 -1
  41. package/dist/src/agent-browser-lifecycle.js +1 -1
  42. package/dist/src/agent-browser-process-record.js +1 -1
  43. package/dist/src/agent-browser-provider.js +1 -1
  44. package/dist/src/agent-browser-tool.js +1 -1
  45. package/dist/src/agent-device-client.js +2 -2
  46. package/dist/src/ai-sdk.js +1 -1
  47. package/dist/src/alert-runtime.js +1 -1
  48. package/dist/src/android-adb.d.ts +29 -2
  49. package/dist/src/android-adb.js +1 -1
  50. package/dist/src/android-input-ownership.js +1 -1
  51. package/dist/src/android.js +1 -1
  52. package/dist/src/app-catalog.js +1 -1
  53. package/dist/src/app-deployment-resolution.js +1 -1
  54. package/dist/src/app-helpers.js +1 -1
  55. package/dist/src/app-inventory.js +1 -1
  56. package/dist/src/app-launch.js +2 -2
  57. package/dist/src/app-lifecycle.js +2 -2
  58. package/dist/src/app-lifecycle2.js +2 -2
  59. package/dist/src/app-log-files.js +1 -1
  60. package/dist/src/app-log-runtime2.js +3 -3
  61. package/dist/src/app-resolution.js +2 -2
  62. package/dist/src/app-state.js +1 -1
  63. package/dist/src/apple-runner-platform.js +1 -1
  64. package/dist/src/application-lifecycle-interaction.js +1 -1
  65. package/dist/src/archive.js +1 -1
  66. package/dist/src/artifact-download.js +1 -1
  67. package/dist/src/auth-session.js +1 -1
  68. package/dist/src/auth.js +1 -1
  69. package/dist/src/back-mode.js +1 -1
  70. package/dist/src/backend-snapshot-options.js +1 -1
  71. package/dist/src/boot-diagnostics.js +1 -1
  72. package/dist/src/capture-disclosure.js +1 -0
  73. package/dist/src/capture.js +1 -1
  74. package/dist/src/cli-config.js +1 -1
  75. package/dist/src/cli-help.js +18 -2
  76. package/dist/src/cli-output.js +1 -1
  77. package/dist/src/cli.js +4 -4
  78. package/dist/src/click-button.js +1 -1
  79. package/dist/src/client-metro.js +1 -1
  80. package/dist/src/client-types.d.ts +65 -3
  81. package/dist/src/clipboard-shell-response.js +1 -0
  82. package/dist/src/command-schema.js +1 -1
  83. package/dist/src/command-tools.js +4 -2
  84. package/dist/src/command.js +1 -1
  85. package/dist/src/config.js +1 -1
  86. package/dist/src/connection-runtime.js +1 -1
  87. package/dist/src/connection.js +1 -1
  88. package/dist/src/contracts.d.ts +1 -1
  89. package/dist/src/contracts.js +1 -1
  90. package/dist/src/daemon-client-lifecycle.js +1 -1
  91. package/dist/src/daemon.js +1 -1
  92. package/dist/src/debug-symbols-facade.js +1 -1
  93. package/dist/src/device-boot.js +1 -1
  94. package/dist/src/device-claim-inspection.js +1 -1
  95. package/dist/src/device-claim-rule.js +2 -1
  96. package/dist/src/device-input-state.d.ts +5 -5
  97. package/dist/src/device-inventory-context.js +1 -1
  98. package/dist/src/device-rotation.js +1 -1
  99. package/dist/src/device-selection-resolver.js +1 -1
  100. package/dist/src/device-session.js +1 -1
  101. package/dist/src/device-shell.js +1 -0
  102. package/dist/src/device.js +1 -1
  103. package/dist/src/device2.js +1 -1
  104. package/dist/src/devicectl.js +1 -0
  105. package/dist/src/diagnostics.js +1 -1
  106. package/dist/src/dispatch-resolve.js +1 -1
  107. package/dist/src/display-inventory.js +1 -0
  108. package/dist/src/doctor-facade.js +1 -1
  109. package/dist/src/doctor2.js +1 -1
  110. package/dist/src/durable-resource.js +1 -1
  111. package/dist/src/engine-eval-script.js +1 -1
  112. package/dist/src/error.js +1 -1
  113. package/dist/src/exec.js +1 -1
  114. package/dist/src/file.js +1 -1
  115. package/dist/src/find.js +1 -1
  116. package/dist/src/finders.js +1 -1
  117. package/dist/src/format.js +2 -1
  118. package/dist/src/generic.js +1 -1
  119. package/dist/src/gesture-admission.js +1 -1
  120. package/dist/src/gesture-plan.js +1 -1
  121. package/dist/src/gesture-runtime.js +1 -1
  122. package/dist/src/harmonyos.js +1 -1
  123. package/dist/src/hdc.js +1 -1
  124. package/dist/src/helper.js +2 -2
  125. package/dist/src/host.js +1 -0
  126. package/dist/src/human-control-contract.js +1 -1
  127. package/dist/src/human-control.js +1 -1
  128. package/dist/src/index.d.ts +1 -1
  129. package/dist/src/index.js +1 -1
  130. package/dist/src/input-actions.js +1 -1
  131. package/dist/src/input-actions2.js +1 -1
  132. package/dist/src/install-artifact-facade.js +1 -1
  133. package/dist/src/install-source-network-transport.js +1 -1
  134. package/dist/src/install-source2.js +1 -1
  135. package/dist/src/interaction-positionals.js +1 -1
  136. package/dist/src/interaction-ref-policy.js +1 -1
  137. package/dist/src/interaction.js +1 -1
  138. package/dist/src/interaction2.js +1 -1
  139. package/dist/src/interactor-operation-binding.js +1 -0
  140. package/dist/src/interactor-operation-catalog.js +1 -1
  141. package/dist/src/interactor.js +4 -5
  142. package/dist/src/interactor2.js +1 -1
  143. package/dist/src/interactors.js +1 -1
  144. package/dist/src/internal/bin.js +1 -1
  145. package/dist/src/internal/daemon.js +2 -2
  146. package/dist/src/internal/png-worker.d.ts +9 -1
  147. package/dist/src/internal/png-worker.js +1 -1
  148. package/dist/src/internal/run-script-http-child.js +1 -1
  149. package/dist/src/inventory.js +4 -4
  150. package/dist/src/inventory2.js +1 -1
  151. package/dist/src/inventory3.js +2 -2
  152. package/dist/src/inventory5.js +1 -1
  153. package/dist/src/ios-snapshot-acquisition.js +1 -1
  154. package/dist/src/ios-snapshot-runtime.js +1 -1
  155. package/dist/src/ios-target-activation.js +1 -0
  156. package/dist/src/lease-context.js +1 -1
  157. package/dist/src/lease-scope.js +1 -1
  158. package/dist/src/lease.js +1 -1
  159. package/dist/src/limrun-download-file.js +1 -0
  160. package/dist/src/limrun-runtime-dependencies.js +1 -1
  161. package/dist/src/limrun.d.ts +10 -4
  162. package/dist/src/linux.js +1 -1
  163. package/dist/src/local-interactor-operation-set.js +1 -1
  164. package/dist/src/location-coordinates.js +1 -1
  165. package/dist/src/macos-facade.js +1 -1
  166. package/dist/src/managed-device-scope.js +1 -1
  167. package/dist/src/mechanics.js +17 -16
  168. package/dist/src/mobile-snapshot-semantics.js +1 -1
  169. package/dist/src/multitouch-support.js +1 -1
  170. package/dist/src/observation.js +2 -2
  171. package/dist/src/output-common.js +5 -2
  172. package/dist/src/parameterized-recorded-fill.js +1 -1
  173. package/dist/src/perf-facade.js +2 -2
  174. package/dist/src/perf-runtime-operation-builder.js +1 -1
  175. package/dist/src/perf-runtime-plan.js +1 -1
  176. package/dist/src/perf.js +1 -1
  177. package/dist/src/physical-device-control.js +1 -1
  178. package/dist/src/platform-request-scope.js +1 -1
  179. package/dist/src/platform-runtime-android-adb-host.js +1 -1
  180. package/dist/src/platform-runtime-host.js +1 -1
  181. package/dist/src/platform-runtime-managed-owner.js +1 -1
  182. package/dist/src/platform-runtime-open-target.js +1 -1
  183. package/dist/src/platform-runtime-operation-host.js +4 -4
  184. package/dist/src/platform-runtime-runtime-hints.js +2 -2
  185. package/dist/src/platform-runtime-screen-recording-android-host.js +1 -1
  186. package/dist/src/platform-runtime-screen-recording-apple-runner-transport.js +1 -1
  187. package/dist/src/platform-runtime-screen-recording-apple-simulator-host.js +1 -1
  188. package/dist/src/platform-runtime-screen-recording-apple-transport.js +1 -1
  189. package/dist/src/platform-runtime-screen-recording-finalizer-host.js +1 -1
  190. package/dist/src/platform-runtime-screen-recording-harmony-host.js +1 -1
  191. package/dist/src/platform-runtime-screen-recording-process-host.js +1 -1
  192. package/dist/src/platform-runtime-unavailable.js +1 -1
  193. package/dist/src/platform-runtime.js +1 -1
  194. package/dist/src/platform-runtime2.js +1 -1
  195. package/dist/src/png-crop-bytes.js +1 -1
  196. package/dist/src/png-worker-client.js +1 -0
  197. package/dist/src/png.js +1 -1
  198. package/dist/src/prepare-kind.js +1 -1
  199. package/dist/src/provider-session-ownership.js +1 -1
  200. package/dist/src/provider-webdriver.js +1 -1
  201. package/dist/src/proxy.js +1 -1
  202. package/dist/src/quality-warnings.js +1 -1
  203. package/dist/src/react-native.js +1 -1
  204. package/dist/src/record-runtime.js +1 -1
  205. package/dist/src/record-trace.js +1 -1
  206. package/dist/src/rect-center.js +1 -1
  207. package/dist/src/register-builtins.js +1 -1
  208. package/dist/src/registry.js +35 -35
  209. package/dist/src/remote-config2.js +1 -1
  210. package/dist/src/replay-divergence.js +1 -1
  211. package/dist/src/replay.js +1 -1
  212. package/dist/src/reporting.js +1 -1
  213. package/dist/src/request-admission.js +1 -1
  214. package/dist/src/request-cancel.js +1 -1
  215. package/dist/src/request-runtime-binding.js +1 -1
  216. package/dist/src/result-serialization.js +1 -1
  217. package/dist/src/retry.js +1 -1
  218. package/dist/src/runner-cache-metadata.js +2 -2
  219. package/dist/src/runner-client.js +1 -1
  220. package/dist/src/runner-contract.js +3 -0
  221. package/dist/src/runner-dev-tools-security.js +1 -0
  222. package/dist/src/runner-device-readiness.js +1 -0
  223. package/dist/src/runner-disposal.js +1 -1
  224. package/dist/src/runner-operations-facade.js +1 -1
  225. package/dist/src/runner-presentation.js +1 -1
  226. package/dist/src/runner.js +1 -1
  227. package/dist/src/runtime-transport-hints.js +1 -1
  228. package/dist/src/runtime.js +3 -3
  229. package/dist/src/runtime10.js +1 -1
  230. package/dist/src/runtime2.js +4 -4
  231. package/dist/src/runtime3.js +1 -1
  232. package/dist/src/runtime4.js +5 -5
  233. package/dist/src/runtime5.js +1 -1
  234. package/dist/src/runtime6.js +1 -1
  235. package/dist/src/runtime7.js +1 -1
  236. package/dist/src/runtime8.js +1 -1
  237. package/dist/src/runtime9.js +1 -1
  238. package/dist/src/screen-recording-admission-ledger.js +1 -0
  239. package/dist/src/screen-recording-completion.js +1 -1
  240. package/dist/src/screen-recording-options.js +1 -1
  241. package/dist/src/screen-recording-resource-recovery.js +1 -1
  242. package/dist/src/screen-recording-transport.js +1 -0
  243. package/dist/src/screenshot-density.js +1 -1
  244. package/dist/src/screenshot-image.js +1 -0
  245. package/dist/src/screenshot-runtime.js +1 -1
  246. package/dist/src/screenshot.js +1 -1
  247. package/dist/src/screenshot3.js +1 -1
  248. package/dist/src/script.js +1 -1
  249. package/dist/src/scroll-command.js +1 -1
  250. package/dist/src/scroll-gesture.js +1 -1
  251. package/dist/src/sdk-batch-runner.js +2 -2
  252. package/dist/src/sdk-batch.js +2 -2
  253. package/dist/src/sdk-contracts.d.ts +76 -1
  254. package/dist/src/sdk-contracts.js +2 -2
  255. package/dist/src/sdk-device.js +1 -1
  256. package/dist/src/sdk-finders.d.ts +1 -1
  257. package/dist/src/sdk-finders.js +1 -1
  258. package/dist/src/sdk-io.js +1 -1
  259. package/dist/src/sdk-remote-config.js +1 -1
  260. package/dist/src/sdk-selectors.d.ts +29 -3
  261. package/dist/src/sdk-selectors.js +1 -1
  262. package/dist/src/selection.js +0 -0
  263. package/dist/src/selector-runtime.js +1 -1
  264. package/dist/src/selectors.js +1 -1
  265. package/dist/src/semantic-index.js +1 -1
  266. package/dist/src/server.js +1 -1
  267. package/dist/src/session-allocation.js +1 -1
  268. package/dist/src/session-observation.js +1 -1
  269. package/dist/src/session-routing.js +1 -1
  270. package/dist/src/session-runtime-admission.js +1 -1
  271. package/dist/src/session-snapshot.js +1 -1
  272. package/dist/src/session-store.js +3 -3
  273. package/dist/src/session2.js +4 -5
  274. package/dist/src/settings.js +1 -1
  275. package/dist/src/simctl-facade.js +1 -1
  276. package/dist/src/simctl.js +1 -1
  277. package/dist/src/simulator-boot.js +1 -1
  278. package/dist/src/simulator-facade.js +1 -1
  279. package/dist/src/simulator.js +1 -1
  280. package/dist/src/snapshot-capture-annotations.js +1 -1
  281. package/dist/src/snapshot-diagnostics.js +1 -1
  282. package/dist/src/snapshot-quality-backend-capabilities.js +1 -1
  283. package/dist/src/snapshot-runtime-binding.js +1 -1
  284. package/dist/src/snapshot-runtime.js +1 -1
  285. package/dist/src/snapshot-state.js +1 -1
  286. package/dist/src/snapshot.js +1 -1
  287. package/dist/src/snapshot2.js +1 -1
  288. package/dist/src/snapshot3.js +1 -1
  289. package/dist/src/snapshot4.js +1 -1
  290. package/dist/src/src.js +1 -1
  291. package/dist/src/src4.js +1 -1
  292. package/dist/src/src7.js +1 -1
  293. package/dist/src/src8.js +2 -2
  294. package/dist/src/stop-sequence.js +1 -1
  295. package/dist/src/string-enum.js +1 -1
  296. package/dist/src/takeover.js +1 -1
  297. package/dist/src/text-entry-focus.js +1 -1
  298. package/dist/src/tool-provider.js +1 -1
  299. package/dist/src/tool-provider2.js +1 -1
  300. package/dist/src/touch-runtime.js +1 -1
  301. package/dist/src/transport.js +1 -1
  302. package/dist/src/tree.js +1 -1
  303. package/dist/src/tv-remote.js +1 -1
  304. package/dist/src/type-text-runtime.js +1 -1
  305. package/dist/src/verified-file.js +1 -1
  306. package/dist/src/video.js +1 -1
  307. package/dist/src/viewport.js +1 -1
  308. package/dist/src/web.js +1 -1
  309. package/dist/src/web2.js +1 -1
  310. package/dist/src/webdriver-source.js +1 -1
  311. package/dist/src/window-state.js +1 -1
  312. package/package.json +4 -1
  313. package/server.json +2 -2
  314. package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.6.apk.sha256 +0 -1
  315. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.6.apk.sha256 +0 -1
  316. package/dist/src/session-target-evidence.js +0 -1
  317. package/dist/src/vars.js +0 -2
@@ -0,0 +1,61 @@
1
+ import Foundation
2
+
3
+ /// AppKit and SwiftUI keep a mouse-down under track long enough to tell a click from a
4
+ /// drag, and a mouse-up posted in the same event tick as its mouse-down is never
5
+ /// delivered to the app. Measured on an `NSButton`: a 0 ms hold delivered 0 of 15
6
+ /// mouse-ups and activated 0 actions, 20 ms activated 14 of 15, and 40 ms and above
7
+ /// activated 15 of 15. Holds shorter than this are raised to it.
8
+ public let minimumMouseClickHoldMs = 40
9
+
10
+ /// Hold used when the caller does not name one. Comfortably above
11
+ /// `minimumMouseClickHoldMs` so a press does not sit on the measured cliff.
12
+ public let defaultMouseClickHoldMs = 60
13
+
14
+ /// The gap between the two presses of one double-click. Well inside the system
15
+ /// double-click interval (500 ms by default), and independent of the caller's repeat
16
+ /// interval, which separates whole presses rather than the halves of one.
17
+ public let mouseClickPairGapMs = 80
18
+
19
+ public func mouseClickHoldMs(requestedMs: Int) -> Int {
20
+ if requestedMs <= 0 {
21
+ return defaultMouseClickHoldMs
22
+ }
23
+ return max(requestedMs, minimumMouseClickHoldMs)
24
+ }
25
+
26
+ /// One press of the button: the click state it is posted with, and how long after the
27
+ /// previous release it starts.
28
+ public struct MouseClickPress: Equatable, Sendable {
29
+ /// `1` for an independent click; `2` for the second half of a double-click.
30
+ public let clickState: Int
31
+ public let delayBeforeMs: Int
32
+
33
+ public init(clickState: Int, delayBeforeMs: Int) {
34
+ self.clickState = clickState
35
+ self.delayBeforeMs = delayBeforeMs
36
+ }
37
+ }
38
+
39
+ /// The presses one request becomes. `clicks` is always a count of independent presses at
40
+ /// click state 1, which is what every other platform means by `--count`; only
41
+ /// `doubleClick` raises the state, and it does so per press, so `doubleClick` with three
42
+ /// clicks is three double-clicks rather than one triple-click.
43
+ public func mouseClickPresses(clicks: Int, doubleClick: Bool, intervalMs: Int) -> [MouseClickPress] {
44
+ let gap = max(intervalMs, 0)
45
+ var presses: [MouseClickPress] = []
46
+ for index in 0..<max(clicks, 1) {
47
+ presses.append(MouseClickPress(clickState: 1, delayBeforeMs: index == 0 ? 0 : gap))
48
+ if doubleClick {
49
+ presses.append(MouseClickPress(clickState: 2, delayBeforeMs: mouseClickPairGapMs))
50
+ }
51
+ }
52
+ return presses
53
+ }
54
+
55
+ /// How long the schedule keeps the helper busy: every hold plus every gap. The caller's
56
+ /// process timeout must cover this, or the helper is killed mid-schedule.
57
+ public func mouseClickScheduleMs(holdMs: Int, clicks: Int, doubleClick: Bool, intervalMs: Int) -> Int {
58
+ let hold = mouseClickHoldMs(requestedMs: holdMs)
59
+ return mouseClickPresses(clicks: clicks, doubleClick: doubleClick, intervalMs: intervalMs)
60
+ .reduce(0) { $0 + $1.delayBeforeMs + hold }
61
+ }
@@ -0,0 +1,30 @@
1
+ import XCTest
2
+
3
+ @testable import AgentDeviceMacOSDeviceHub
4
+
5
+ final class DeviceHubPoseTests: XCTestCase {
6
+ func testEveryPoseNamesTheActionBarControlDeviceHubDescribes() {
7
+ XCTAssertEqual(DeviceHubPose(argument: "closed")?.controlDescription, "Closed")
8
+ XCTAssertEqual(DeviceHubPose(argument: "book")?.controlDescription, "Book")
9
+ XCTAssertEqual(DeviceHubPose(argument: "OPEN")?.controlDescription, "Open")
10
+ XCTAssertNil(DeviceHubPose(argument: "half-open"), "the helper takes Device Hub's names, not the CLI's")
11
+ XCTAssertEqual(Set(DeviceHubPose.allCases.map(\.controlDescription)), ["Closed", "Book", "Open"])
12
+ }
13
+
14
+ // Device Hub titles the window `<name> – iOS 27.1`; a simulator whose name merely starts
15
+ // with another's must not claim that window.
16
+ func testWindowTitleMatchesTheDeviceNameAsAPrefixWord() {
17
+ XCTAssertTrue(deviceHubWindowShows(deviceName: "iPhone Duo", title: "iPhone Duo – iOS 27.1"))
18
+ XCTAssertTrue(deviceHubWindowShows(deviceName: "iPhone Duo", title: "iPhone Duo"))
19
+ XCTAssertFalse(deviceHubWindowShows(deviceName: "iPhone Duo", title: "iPhone Duo Lab – iOS 27.1"))
20
+ XCTAssertFalse(deviceHubWindowShows(deviceName: "iPhone Duo", title: "bench-golden-v1 – iOS 27.0"))
21
+ XCTAssertFalse(deviceHubWindowShows(deviceName: "iPhone Duo", title: nil))
22
+ }
23
+
24
+ func testSidebarRowIsKeyedByUdid() {
25
+ XCTAssertEqual(
26
+ deviceHubDeviceRowIdentifier(udid: "4F879835-4AB3-4046-B033-5AB769209DD4"),
27
+ "TableRow.Device.4F879835-4AB3-4046-B033-5AB769209DD4"
28
+ )
29
+ }
30
+ }
@@ -0,0 +1,58 @@
1
+ import XCTest
2
+
3
+ @testable import AgentDeviceMacOSInput
4
+
5
+ final class MouseClickScheduleTests: XCTestCase {
6
+ // The original schedule posted move, down and up back to back. AppKit and SwiftUI
7
+ // never delivered the release, so every surface press opened a tracking session that
8
+ // no control ever completed: 0 of 15 measured clicks activated their control.
9
+ func testHoldShorterThanTheDeliverableFloorIsRaised() {
10
+ XCTAssertEqual(mouseClickHoldMs(requestedMs: 0), defaultMouseClickHoldMs)
11
+ XCTAssertEqual(mouseClickHoldMs(requestedMs: 1), minimumMouseClickHoldMs)
12
+ XCTAssertEqual(mouseClickHoldMs(requestedMs: 39), minimumMouseClickHoldMs)
13
+ XCTAssertGreaterThan(defaultMouseClickHoldMs, minimumMouseClickHoldMs)
14
+ }
15
+
16
+ func testNamedLongPressHoldIsKept() {
17
+ XCTAssertEqual(mouseClickHoldMs(requestedMs: 800), 800)
18
+ }
19
+
20
+ // `--count N` means N independent presses on every platform; a rising click state would
21
+ // turn `--count 2` into a double-click and `--count 3` into a triple-click.
22
+ func testRepeatClicksAreIndependentPressesAtClickStateOne() {
23
+ let presses = mouseClickPresses(clicks: 3, doubleClick: false, intervalMs: 150)
24
+ XCTAssertEqual(presses.map(\.clickState), [1, 1, 1])
25
+ XCTAssertEqual(presses.map(\.delayBeforeMs), [0, 150, 150])
26
+ }
27
+
28
+ // Only `--double-tap` raises the click state, and it does so per press, so it composes
29
+ // with `--count` into that many double-clicks rather than one triple-click.
30
+ func testDoubleClickRaisesTheStateInsideEachPress() {
31
+ XCTAssertEqual(
32
+ mouseClickPresses(clicks: 1, doubleClick: true, intervalMs: 120).map(\.clickState),
33
+ [1, 2]
34
+ )
35
+ let three = mouseClickPresses(clicks: 3, doubleClick: true, intervalMs: 500)
36
+ XCTAssertEqual(three.map(\.clickState), [1, 2, 1, 2, 1, 2])
37
+ XCTAssertEqual(
38
+ three.map(\.delayBeforeMs),
39
+ [0, mouseClickPairGapMs, 500, mouseClickPairGapMs, 500, mouseClickPairGapMs]
40
+ )
41
+ XCTAssertLessThan(mouseClickPairGapMs, 500, "a pair must land inside the system double-click interval")
42
+ }
43
+
44
+ func testZeroClicksStillPostsOnePress() {
45
+ XCTAssertEqual(mouseClickPresses(clicks: 0, doubleClick: false, intervalMs: 0).count, 1)
46
+ }
47
+
48
+ // The process timeout the caller sets is derived from this: a schedule the timeout does
49
+ // not cover is killed mid-hold.
50
+ func testScheduleDurationSumsEveryHoldAndGap() {
51
+ XCTAssertEqual(mouseClickScheduleMs(holdMs: 0, clicks: 1, doubleClick: false, intervalMs: 120), defaultMouseClickHoldMs)
52
+ XCTAssertEqual(mouseClickScheduleMs(holdMs: 10_000, clicks: 4, doubleClick: false, intervalMs: 120), 4 * 10_000 + 3 * 120)
53
+ XCTAssertEqual(
54
+ mouseClickScheduleMs(holdMs: 60, clicks: 2, doubleClick: true, intervalMs: 100),
55
+ 4 * 60 + 2 * mouseClickPairGapMs + 100
56
+ )
57
+ }
58
+ }
@@ -118,6 +118,10 @@ FOUNDATION_EXPORT NSString *const RunnerAXSnapshotCustomActionsBlockedKey;
118
118
 
119
119
  + (NSInteger)processIdentifierForApplication:(XCUIApplication *)application;
120
120
 
121
+ /// Pids of the applications the AX client currently reports as active. Same private accessor the
122
+ /// snapshot target match uses; pids only — the client resolves no bundle id for an arbitrary app.
123
+ + (NSArray<NSNumber *> *)activeApplicationProcessIdentifiers;
124
+
121
125
  /// The frontier-extension loop, exposed for the runner unit bundle: a frontier
122
126
  /// whose element vanished or whose re-rooted request fails must count as
123
127
  /// missed (see the deepExtension keys above) — an all-miss extension reporting
@@ -707,6 +707,26 @@ typedef id (*RunnerAXSnapshotMsgSend)(id, SEL, id, id, id, NSError **);
707
707
  return [self integerFrom:application selectorName:@"processID"];
708
708
  }
709
709
 
710
+ + (NSArray<NSNumber *> *)activeApplicationProcessIdentifiers
711
+ {
712
+ id axClient = [self accessibilityClient];
713
+ if (nil == axClient) {
714
+ return @[];
715
+ }
716
+ id activeApplications = [self objectFrom:axClient selectorName:@"activeApplications"];
717
+ if (![activeApplications isKindOfClass:NSArray.class]) {
718
+ return @[];
719
+ }
720
+ NSMutableArray<NSNumber *> *processIdentifiers = [NSMutableArray array];
721
+ for (id candidate in (NSArray *)activeApplications) {
722
+ NSInteger processIdentifier = [self integerFrom:candidate selectorName:@"processIdentifier"];
723
+ if (processIdentifier > 0) {
724
+ [processIdentifiers addObject:@(processIdentifier)];
725
+ }
726
+ }
727
+ return processIdentifiers;
728
+ }
729
+
710
730
  + (id)accessibilityApplicationForApplication:(XCUIApplication *)application axClient:(id)axClient
711
731
  {
712
732
  NSInteger targetProcessID = [self integerFrom:application selectorName:@"processID"];
@@ -1,4 +1,5 @@
1
1
  import XCTest
2
+ import AgentDeviceSnapshotPresentation
2
3
 
3
4
 
4
5
 
@@ -406,8 +407,6 @@ extension RunnerTests {
406
407
 
407
408
 
408
409
 
409
-
410
-
411
410
 
412
411
 
413
412
 
@@ -216,11 +216,11 @@ extension RunnerTests {
216
216
  }
217
217
 
218
218
  let rootFrame = privateAXRect(root["frame"])
219
- let viewport = privateAXSnapshotViewport(app: app, rootFrame: rootFrame)
219
+ let geometry = privateAXSnapshotGeometry(app: app, rootFrame: rootFrame)
220
+ let viewport = geometry.viewport
220
221
  let nodes = privateAXAcquisition(
221
222
  rawRoot: root,
222
- hint: hint,
223
- viewport: viewport
223
+ hint: hint
224
224
  )
225
225
 
226
226
 
@@ -254,7 +254,8 @@ extension RunnerTests {
254
254
  customActions: Self.privateAXCustomActionCoverage(
255
255
  response[RunnerAXSnapshotCustomActionsKey]
256
256
  ),
257
- viewport: viewport
257
+ viewport: viewport,
258
+ interfaceOrientation: geometry.interfaceOrientation
258
259
  )
259
260
  #else
260
261
  return nil
@@ -269,26 +270,36 @@ extension RunnerTests {
269
270
  !hasAbandonedMainThreadWork() && !isSnapshotXCTestChannelPenalized(bundleId: currentBundleId)
270
271
  }
271
272
 
272
- private func privateAXSnapshotViewport(app: XCUIApplication, rootFrame: CGRect) -> CGRect {
273
+
274
+
275
+
276
+
277
+ private func privateAXSnapshotGeometry(
278
+ app: XCUIApplication,
279
+ rootFrame: CGRect
280
+ ) -> (viewport: CGRect, interfaceOrientation: Int) {
273
281
  let fallback = rootFrame.isEmpty ? CGRect.infinite : rootFrame
274
282
  guard shouldReadPrivateAXViewportViaXCTest() else {
275
- return fallback
283
+ return (fallback, RunnerInterfaceOrientation.unknown)
276
284
  }
277
285
  do {
278
- let viewport = try runMainThreadWork(
286
+ let anchor = try runMainThreadWork(
279
287
  "private_ax_viewport",
280
288
  timeout: 1,
281
289
  timeoutError: snapshotMainThreadTimeoutError("reading private AX viewport")
282
290
  ) {
283
- self.safeSnapshotViewport(app: app)
291
+ (
292
+ viewport: self.safeSnapshotViewport(app: app),
293
+ interfaceOrientation: self.capturedInterfaceOrientation(app: app)
294
+ )
284
295
  }
285
- if viewport.isInfinite || viewport.isNull || viewport.isEmpty {
286
- return fallback
296
+ if anchor.viewport.isInfinite || anchor.viewport.isNull || anchor.viewport.isEmpty {
297
+ return (fallback, RunnerInterfaceOrientation.unknown)
287
298
  }
288
- return viewport
299
+ return (anchor.viewport, anchor.interfaceOrientation)
289
300
  } catch {
290
301
  NSLog("AGENT_DEVICE_RUNNER_PRIVATE_AX_VIEWPORT_FALLBACK=%@", String(describing: error))
291
- return fallback
302
+ return (fallback, RunnerInterfaceOrientation.unknown)
292
303
  }
293
304
  }
294
305
 
@@ -857,6 +868,8 @@ extension RunnerTests {
857
868
 
858
869
 
859
870
 
871
+
872
+
860
873
 
861
874
 
862
875
 
@@ -898,6 +898,34 @@ extension RunnerTests {
898
898
 
899
899
 
900
900
 
901
+
902
+
903
+
904
+
905
+
906
+
907
+
908
+
909
+
910
+
911
+
912
+
913
+
914
+
915
+
916
+
917
+
918
+
919
+
920
+
921
+
922
+
923
+
924
+
925
+
926
+
927
+
928
+
901
929
 
902
930
 
903
931
 
@@ -921,11 +949,18 @@ extension RunnerTests {
921
949
 
922
950
  func executeAccepted(command: Command) throws -> Response {
923
951
  commandJournal.start(command: command)
952
+ pendingTargetActivation = nil
924
953
  do {
925
954
  let response = try executeDispatched(command: command)
926
955
  commandJournal.finish(command: command, response: response)
927
- return response
956
+ guard let fact = pendingTargetActivation else { return response }
957
+
958
+
959
+
960
+ pendingTargetActivation = nil
961
+ return response.stampingTargetActivation(fact)
928
962
  } catch {
963
+ pendingTargetActivation = nil
929
964
  commandJournal.fail(command: command, error: error)
930
965
  throw error
931
966
  }
@@ -2080,6 +2115,18 @@ extension RunnerTests {
2080
2115
  case .appSwitcher:
2081
2116
  performAppSwitcherGesture(app: activeApp)
2082
2117
  return Response(ok: true, data: DataPayload(message: "appSwitcher"))
2118
+ case .actionButton:
2119
+ guard pressActionButton() else {
2120
+ return Response(
2121
+ ok: false,
2122
+ error: ErrorPayload(
2123
+ code: "UNSUPPORTED_OPERATION",
2124
+ message: "actionButton requires a device model with an Action Button",
2125
+ hint: "The Action Button is on iPhone 15 Pro and later and iPad Pro (M4) and later. Assign a Shortcut or App Intent to it in Settings > Action Button."
2126
+ )
2127
+ )
2128
+ }
2129
+ return Response(ok: true, data: DataPayload(message: "actionButton"))
2083
2130
  case .keyboardDismiss:
2084
2131
  let result = dismissKeyboard(app: activeApp)
2085
2132
  if result.wasVisible && !result.dismissed {
@@ -2544,6 +2591,14 @@ extension RunnerTests {
2544
2591
 
2545
2592
 
2546
2593
 
2594
+
2595
+ if command.command == .actionButton {
2596
+ return true
2597
+ }
2598
+
2599
+
2600
+
2601
+
2547
2602
  guard command.text == nil, command.selectorKey == nil else { return false }
2548
2603
  guard hasCachedTargetForActivationSkip(command: command) else { return false }
2549
2604
  return isCoordinateOnlyTap(command)
@@ -137,7 +137,7 @@ final class RunnerCommandJournal {
137
137
  return false
138
138
  case .tap, .mouseClick, .longPress, .drag,
139
139
  .remotePress, .type, .swipe, .scroll, .desktopScroll, .findText, .querySelector, .readText, .back,
140
- .backInApp, .backSystem, .home, .rotate, .appSwitcher, .keyboardDismiss, .keyboardReturn,
140
+ .backInApp, .backSystem, .home, .rotate, .appSwitcher, .actionButton, .keyboardDismiss, .keyboardReturn,
141
141
  .alert, .sequence, .gesture, .gestureViewport, .recordStart, .recordStop,
142
142
  .status, .uptime, .activate, .terminate, .targetReset, .shutdown:
143
143
  return true
@@ -11,16 +11,6 @@ private struct RunnerUnsupportedOperationError: LocalizedError {
11
11
  var errorDescription: String? { message }
12
12
  }
13
13
 
14
- enum RunnerInterfaceOrientation {
15
-
16
-
17
-
18
- static let portrait = 1
19
- static let portraitUpsideDown = 2
20
- static let landscapeRight = 3
21
- static let landscapeLeft = 4
22
- }
23
-
24
14
  extension RunnerTests {
25
15
  enum PlannedGestureExecution: Equatable {
26
16
  case fastSwipe
@@ -134,6 +124,24 @@ extension RunnerTests {
134
124
  #endif
135
125
  }
136
126
 
127
+
128
+
129
+
130
+
131
+
132
+
133
+ @discardableResult
134
+ func pressActionButton() -> Bool {
135
+ #if os(iOS)
136
+ guard #available(iOS 16.0, *) else { return false }
137
+ guard XCUIDevice.shared.hasHardwareButton(.action) else { return false }
138
+ XCUIDevice.shared.press(.action)
139
+ return true
140
+ #else
141
+ return false
142
+ #endif
143
+ }
144
+
137
145
  func findElement(app: XCUIApplication, text: String) -> XCUIElement? {
138
146
  let predicate = NSPredicate(format: "label CONTAINS[c] %@ OR identifier CONTAINS[c] %@ OR value CONTAINS[c] %@", text, text, text)
139
147
  let element = app.descendants(matching: .any).matching(predicate).firstMatch
@@ -252,7 +260,7 @@ extension RunnerTests {
252
260
  label: label.isEmpty ? nil : label,
253
261
  identifier: identifier.isEmpty ? nil : identifier,
254
262
  value: valueText.isEmpty ? nil : valueText,
255
- rect: snapshotRect(from: element.frame),
263
+ rect: SnapshotRect(element.frame),
256
264
  enabled: element.isEnabled,
257
265
  focused: nil,
258
266
  selected: element.isSelected ? true : nil,
@@ -497,50 +505,6 @@ extension RunnerTests {
497
505
  return performCoordinateDrag(app: app, x: x, y: y, x2: x2, y2: y2, holdDuration: holdDuration)
498
506
  }
499
507
 
500
-
501
-
502
-
503
- func nativeSynthesizedPoint(
504
- orientedX x: Double,
505
- orientedY y: Double,
506
- in frame: CGRect,
507
- interfaceOrientation: Int
508
- ) -> CGPoint {
509
- let localX = x - Double(frame.minX)
510
- let localY = y - Double(frame.minY)
511
- let width = Double(frame.width)
512
- let height = Double(frame.height)
513
- switch interfaceOrientation {
514
- case RunnerInterfaceOrientation.landscapeRight:
515
- return CGPoint(x: height - localY, y: localX)
516
- case RunnerInterfaceOrientation.landscapeLeft:
517
- return CGPoint(x: localY, y: width - localX)
518
- case RunnerInterfaceOrientation.portraitUpsideDown:
519
- return CGPoint(x: width - localX, y: height - localY)
520
- default:
521
- return CGPoint(x: localX, y: localY)
522
- }
523
- }
524
-
525
-
526
-
527
- func nativeSynthesizedVector(
528
- orientedDx dx: Double,
529
- orientedDy dy: Double,
530
- interfaceOrientation: Int
531
- ) -> CGVector {
532
- switch interfaceOrientation {
533
- case RunnerInterfaceOrientation.landscapeRight:
534
- return CGVector(dx: -dy, dy: dx)
535
- case RunnerInterfaceOrientation.landscapeLeft:
536
- return CGVector(dx: dy, dy: -dx)
537
- case RunnerInterfaceOrientation.portraitUpsideDown:
538
- return CGVector(dx: -dx, dy: -dy)
539
- default:
540
- return CGVector(dx: dx, dy: dy)
541
- }
542
- }
543
-
544
508
  func synthesizedDragAt(
545
509
  app: XCUIApplication,
546
510
  x: Double,
@@ -569,8 +533,16 @@ extension RunnerTests {
569
533
  )
570
534
  }
571
535
  let frame = context.referenceFrame
572
- let start = nativeSynthesizedPoint(orientedX: x, orientedY: y, in: frame, interfaceOrientation: orientation)
573
- let end = nativeSynthesizedPoint(orientedX: x2, orientedY: y2, in: frame, interfaceOrientation: orientation)
536
+ let start = CoordinateSpaceRotation.native(
537
+ point: CGPoint(x: x, y: y),
538
+ in: frame,
539
+ interfaceOrientation: orientation
540
+ )
541
+ let end = CoordinateSpaceRotation.native(
542
+ point: CGPoint(x: x2, y: y2),
543
+ in: frame,
544
+ interfaceOrientation: orientation
545
+ )
574
546
  let message = switch profile {
575
547
  case .continuous:
576
548
  RunnerSynthesizedGesture.synthesizeContinuousDrag(
@@ -644,7 +616,11 @@ extension RunnerTests {
644
616
  )
645
617
  }
646
618
  let frame = context.referenceFrame
647
- let point = nativeSynthesizedPoint(orientedX: x, orientedY: y, in: frame, interfaceOrientation: orientation)
619
+ let point = CoordinateSpaceRotation.native(
620
+ point: CGPoint(x: x, y: y),
621
+ in: frame,
622
+ interfaceOrientation: orientation
623
+ )
648
624
  if let message = RunnerSynthesizedGesture.synthesizeTap(
649
625
  withApplication: app,
650
626
  x: Double(point.x),
@@ -1055,9 +1031,8 @@ extension RunnerTests {
1055
1031
  )
1056
1032
  let pointerSamples: [[[String: NSNumber]]] = plan.pointers.map { pointer in
1057
1033
  pointer.samples.map { sample in
1058
- let point = nativeSynthesizedPoint(
1059
- orientedX: sample.point.x,
1060
- orientedY: sample.point.y,
1034
+ let point = CoordinateSpaceRotation.native(
1035
+ point: CGPoint(x: sample.point.x, y: sample.point.y),
1061
1036
  in: frame,
1062
1037
  interfaceOrientation: orientation
1063
1038
  )
@@ -1325,53 +1300,6 @@ extension RunnerTests {
1325
1300
 
1326
1301
 
1327
1302
 
1328
-
1329
-
1330
-
1331
-
1332
-
1333
-
1334
-
1335
-
1336
-
1337
-
1338
-
1339
-
1340
-
1341
-
1342
-
1343
-
1344
-
1345
-
1346
-
1347
-
1348
-
1349
-
1350
-
1351
-
1352
-
1353
-
1354
-
1355
-
1356
-
1357
-
1358
-
1359
-
1360
-
1361
-
1362
-
1363
-
1364
-
1365
-
1366
-
1367
-
1368
-
1369
-
1370
-
1371
-
1372
-
1373
-
1374
-
1375
1303
 
1376
1304
 
1377
1305