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
@@ -7,6 +7,7 @@ extension RunnerTests {
7
7
  enum TextEntryFailure: String {
8
8
  case notFocused = "TEXT_INPUT_NOT_FOCUSED"
9
9
  case synthesisUnavailable = "TEXT_INPUT_SYNTHESIS_UNAVAILABLE"
10
+ case commitNotObserved = "TEXT_INPUT_COMMIT_NOT_OBSERVED"
10
11
 
11
12
  var message: String {
12
13
  switch self {
@@ -14,6 +15,8 @@ extension RunnerTests {
14
15
  return "No focused text input was available for typing."
15
16
  case .synthesisUnavailable:
16
17
  return "Reliable text synthesis is unavailable while the software keyboard is hidden."
18
+ case .commitNotObserved:
19
+ return "The runner could not confirm the typed text reached the field."
17
20
  }
18
21
  }
19
22
 
@@ -23,6 +26,8 @@ extension RunnerTests {
23
26
  return "Focus a visible text input, then retry type or fill. If the input is not exposed by accessibility, use a coordinate focus command before typing."
24
27
  case .synthesisUnavailable:
25
28
  return "Show the software keyboard, then retry type or fill."
29
+ case .commitNotObserved:
30
+ return "The field may hold none, part, or all of the text. Run snapshot -i and inspect the field: if it already matches, continue; otherwise retry fill with the full text quoted and --delay-ms 80. Do not use type, which appends to whatever committed."
26
31
  }
27
32
  }
28
33
  }
@@ -40,7 +45,15 @@ extension RunnerTests {
40
45
  static let pollInterval: TimeInterval = 0.02
41
46
  static let warmupValueTimeout: TimeInterval = 0.4
42
47
  static let verificationStabilityWindow: TimeInterval = 0.2
43
- static let synthesizedCommitTimeout: TimeInterval = 3.0
48
+ /// How long the commit wait tolerates seeing NO further progress toward the expected value.
49
+ /// Numerically the flat deadline this replaced, so a pipeline that delivers nothing is
50
+ /// condemned at exactly the same instant it always was (see `SynthesizedCommitDeadline`).
51
+ static let synthesizedCommitStallTimeout: TimeInterval = 3.0
52
+ /// The commit wait's absolute bound, however long characters keep arriving. Sits well inside
53
+ /// the daemon's per-command budget (`RUNNER_COMMAND_TIMEOUT_MS`, 45s), which also has to cover
54
+ /// focus, clear and verification around this wait.
55
+ static let synthesizedCommitCeiling: TimeInterval = 10.0
56
+ static let synthesizedCommitPollInterval: TimeInterval = 0.2
44
57
  }
45
58
 
46
59
  struct TextEntryResult {
@@ -121,211 +134,6 @@ extension RunnerTests {
121
134
  element.typeText(deletes)
122
135
  }
123
136
 
124
- func focusedTextInput(app: XCUIApplication) -> XCUIElement? {
125
- #if os(iOS)
126
- // iOS focus predicates can return stale or misleading text-input matches
127
- // under XCUITest, so text entry readiness is driven by tap/keyboard state.
128
- return nil
129
- #else
130
- return safely("FOCUSED_INPUT_QUERY") {
131
- let candidates = app
132
- .descendants(matching: .any)
133
- .matching(NSPredicate(format: "hasKeyboardFocus == 1"))
134
- .allElementsBoundByIndex
135
- for candidate in candidates where candidate.exists {
136
- switch candidate.elementType {
137
- case .textField, .secureTextField, .searchField, .textView:
138
- return candidate
139
- default:
140
- continue
141
- }
142
- }
143
- return nil
144
- }
145
- #endif
146
- }
147
-
148
- func rememberTextEntryTap(_ element: XCUIElement?) {
149
- guard let element, isTextEntryElement(element) else {
150
- clearRememberedTextEntryTap()
151
- return
152
- }
153
- textEntryTapWitness = TextEntryTapWitness(
154
- element: element,
155
- bundleId: currentBundleId,
156
- processIdentifier: currentAppProcessIdentifier
157
- )
158
- }
159
-
160
- func clearRememberedTextEntryTap() {
161
- textEntryTapWitness = nil
162
- }
163
-
164
- private func rememberedTextEntryTarget() -> TextEntryTarget? {
165
- guard let witness = textEntryTapWitness else {
166
- return nil
167
- }
168
- // The tap is proof for one immediately-following bare type only. Consume it before checking
169
- // the element so a failed or interrupted type cannot reuse stale focus evidence.
170
- clearRememberedTextEntryTap()
171
- guard witness.matches(
172
- bundleId: currentBundleId,
173
- processIdentifier: currentAppProcessIdentifier
174
- ) else {
175
- return nil
176
- }
177
- let element = witness.element
178
- // XCUIElement is query-backed rather than a stable node identity. A same-identifier field
179
- // introduced by app-side navigation between tap and this immediate type can therefore
180
- // re-resolve here; keep the witness one-shot and fail closed on every observable identity
181
- // boundary instead of using frame equality, which would reject legitimate layout changes.
182
- guard safely("LAST_TAPPED_TEXT_INPUT_EXISTS", false, { element.exists }) else {
183
- return nil
184
- }
185
- // Keep the target scoped to the element that the preceding tap actually selected. Do not
186
- // attach a refresh point: if that element disappeared, bare type must fail closed rather
187
- // than rediscovering a different field or dispatching unscoped app.typeText.
188
- return TextEntryTarget(
189
- element: element,
190
- refreshPoint: nil,
191
- prefersFocusedElement: false,
192
- fromTapWitness: true
193
- )
194
- }
195
-
196
- func stabilizeTextInputBeforeTyping(
197
- app: XCUIApplication,
198
- target: XCUIElement?,
199
- keyboardVisibleBeforeTap: Bool? = nil
200
- ) -> TextEntryStabilization {
201
- #if os(tvOS)
202
- return TextEntryStabilization(element: target, focusConfirmed: true)
203
- #else
204
- let latest = target
205
- let keyboardVisibleAtEntry = keyboardVisibleBeforeTap ?? isKeyboardVisible(app: app)
206
- let deadline = Date().addingTimeInterval(TextEntryTiming.focusTimeout)
207
- while Date() < deadline {
208
- if let focused = focusedTextInput(app: app) {
209
- return TextEntryStabilization(element: focused, focusConfirmed: true)
210
- }
211
- // focusedTextInput is intentionally nil on iOS; treat the keyboard transitioning to
212
- // visible after our tap as the focus-moved signal. Don't fast-path when it was already up.
213
- if keyboardBecameVisible(app: app, wasVisibleAtEntry: keyboardVisibleAtEntry) {
214
- return TextEntryStabilization(element: latest, focusConfirmed: true)
215
- }
216
- sleepFor(TextEntryTiming.pollInterval)
217
- }
218
- return TextEntryStabilization(element: latest, focusConfirmed: false)
219
- #endif
220
- }
221
-
222
- func focusTextInputForTextEntry(app: XCUIApplication, x: Double?, y: Double?) -> TextEntryTarget {
223
- guard let x, let y else {
224
- let softwareKeyboardVisible = isKeyboardVisible(app: app)
225
- if !softwareKeyboardVisible, let rememberedTarget = rememberedTextEntryTarget() {
226
- return rememberedTarget
227
- }
228
- // Bare `type` targets the current first responder. On iOS we intentionally do not trust
229
- // `hasKeyboardFocus`, but an already-visible software keyboard is sufficient evidence that
230
- // app.typeText has a receiver; waiting the full readiness timeout cannot prove a stronger
231
- // target because there is no selector/coordinate focus move to validate.
232
- if softwareKeyboardVisible {
233
- return TextEntryTarget(
234
- element: focusedTextInput(app: app),
235
- refreshPoint: nil,
236
- prefersFocusedElement: true
237
- )
238
- }
239
- let focused = waitForTextEntryReadiness(
240
- app: app,
241
- target: TextEntryTarget(
242
- element: focusedTextInput(app: app),
243
- refreshPoint: nil,
244
- prefersFocusedElement: true
245
- )
246
- )
247
- return TextEntryTarget(element: focused, refreshPoint: nil, prefersFocusedElement: true)
248
- }
249
-
250
- let keyboardVisibleBeforeTap = isKeyboardVisible(app: app)
251
- let target = textInputAt(app: app, x: x, y: y)
252
- let requestedPoint = CGPoint(x: x, y: y)
253
- if let target {
254
- let frame = target.frame
255
- if !frame.isEmpty {
256
- _ = tapAt(app: app, x: frame.midX, y: frame.midY)
257
- } else {
258
- _ = tapAt(app: app, x: x, y: y)
259
- }
260
- } else {
261
- _ = tapAt(app: app, x: x, y: y)
262
- }
263
- // A visible keyboard is not enough evidence for app.typeText, because focus may still
264
- // belong to a previous field. With a concrete target we type through XCUIElement.typeText,
265
- // so after tapping it the iOS readiness timeout cannot prove anything stronger.
266
- if keyboardVisibleBeforeTap, let target {
267
- return TextEntryTarget(
268
- element: target,
269
- refreshPoint: textEntryRefreshPoint(for: target) ?? requestedPoint,
270
- prefersFocusedElement: false
271
- )
272
- }
273
- let stabilized = stabilizeTextInputBeforeTyping(
274
- app: app,
275
- target: target,
276
- keyboardVisibleBeforeTap: keyboardVisibleBeforeTap
277
- )
278
- let readyTarget = TextEntryTarget(
279
- element: stabilized.element ?? target,
280
- refreshPoint: requestedPoint,
281
- prefersFocusedElement: false
282
- )
283
- let concreteTargetReady = keyboardVisibleBeforeTap && readyTarget.element != nil
284
- let element = stabilized.focusConfirmed || concreteTargetReady
285
- ? (stabilized.element ?? target)
286
- : (waitForTextEntryReadiness(app: app, target: readyTarget) ?? stabilized.element ?? target)
287
- return TextEntryTarget(
288
- element: element,
289
- refreshPoint: textEntryRefreshPoint(for: element) ?? requestedPoint,
290
- prefersFocusedElement: false
291
- )
292
- }
293
-
294
- func focusTextInputForTextEntry(app: XCUIApplication, element: XCUIElement) -> TextEntryTarget {
295
- let point = textEntryRefreshPoint(for: element)
296
- let keyboardVisibleBeforeTap = isKeyboardVisible(app: app)
297
- if let point {
298
- _ = tapAt(app: app, x: point.x, y: point.y)
299
- }
300
- // See the coordinate-target path above: direct element typing keeps this scoped to the
301
- // tapped target, while the first-character warmup and final verify still catch dropped input.
302
- if keyboardVisibleBeforeTap {
303
- return TextEntryTarget(
304
- element: element,
305
- refreshPoint: textEntryRefreshPoint(for: element) ?? point,
306
- prefersFocusedElement: false
307
- )
308
- }
309
- let stabilized = stabilizeTextInputBeforeTyping(
310
- app: app,
311
- target: element,
312
- keyboardVisibleBeforeTap: keyboardVisibleBeforeTap
313
- )
314
- let readyTarget = TextEntryTarget(
315
- element: stabilized.element ?? element,
316
- refreshPoint: point,
317
- prefersFocusedElement: false
318
- )
319
- let resolved = stabilized.focusConfirmed
320
- ? (stabilized.element ?? element)
321
- : (waitForTextEntryReadiness(app: app, target: readyTarget) ?? stabilized.element ?? element)
322
- return TextEntryTarget(
323
- element: resolved,
324
- refreshPoint: textEntryRefreshPoint(for: resolved) ?? point,
325
- prefersFocusedElement: false
326
- )
327
- }
328
-
329
137
  func isTextEntryElement(_ element: XCUIElement) -> Bool {
330
138
  switch element.elementType {
331
139
  case .textField, .secureTextField, .searchField, .textView:
@@ -369,103 +177,6 @@ extension RunnerTests {
369
177
  return nil
370
178
  }
371
179
 
372
- private func waitForTextEntryReadiness(
373
- app: XCUIApplication,
374
- target: TextEntryTarget,
375
- timeout: TimeInterval = TextEntryTiming.readinessTimeout
376
- ) -> XCUIElement? {
377
- #if os(iOS)
378
- var latest = resolveTextEntryElement(app: app, target: target)
379
- let keyboardVisibleAtEntry = isKeyboardVisible(app: app)
380
- let deadline = Date().addingTimeInterval(timeout)
381
- let hardwareKeyboardFallback = Date().addingTimeInterval(
382
- min(TextEntryTiming.hardwareKeyboardFallbackTimeout, timeout)
383
- )
384
- var sawSoftwareKeyboard = false
385
- while Date() < deadline {
386
- if let focused = focusedTextInput(app: app) {
387
- latest = focused
388
- if isKeyboardVisible(app: app) {
389
- return focused
390
- }
391
- }
392
- // Fast-path on a keyboard hidden->visible transition: our tapped field gained focus, so
393
- // return immediately instead of burning the full readinessTimeout (warmup-first-char echo
394
- // + post-type verify/repair remain as drop safety nets). When the keyboard was ALREADY up
395
- // (back-to-back fills), this isn't a focus signal — fall through to the settle/timeout so
396
- // text isn't sent to the previously-focused field.
397
- if keyboardBecameVisible(app: app, wasVisibleAtEntry: keyboardVisibleAtEntry) {
398
- return latest
399
- }
400
- sawSoftwareKeyboard = sawSoftwareKeyboard || keyboardElementExists(app: app)
401
- if !sawSoftwareKeyboard && Date() >= hardwareKeyboardFallback && latest != nil {
402
- return latest
403
- }
404
- sleepFor(TextEntryTiming.pollInterval)
405
- }
406
- return focusedTextInput(app: app) ?? latest
407
- #else
408
- return resolveTextEntryElement(app: app, target: target)
409
- #endif
410
- }
411
-
412
- func waitForTextEntryReadinessAfterTap(app: XCUIApplication, element: XCUIElement) {
413
- #if os(iOS)
414
- switch element.elementType {
415
- case .textField, .secureTextField, .searchField, .textView:
416
- if waitForFocusedTextInput(app: app, timeout: TextEntryTiming.readinessTimeout) != nil {
417
- return
418
- }
419
- let frame = element.frame
420
- if !frame.isEmpty {
421
- _ = tapAt(app: app, x: frame.midX, y: frame.midY)
422
- _ = waitForFocusedTextInput(app: app, timeout: TextEntryTiming.readinessTimeout)
423
- }
424
- default:
425
- return
426
- }
427
- #endif
428
- }
429
-
430
- private func waitForFocusedTextInput(app: XCUIApplication, timeout: TimeInterval) -> XCUIElement? {
431
- let deadline = Date().addingTimeInterval(timeout)
432
- while Date() < deadline {
433
- if let focused = focusedTextInput(app: app) {
434
- return focused
435
- }
436
- sleepFor(TextEntryTiming.pollInterval)
437
- }
438
- return focusedTextInput(app: app)
439
- }
440
-
441
- private func textEntryRefreshPoint(for element: XCUIElement?) -> CGPoint? {
442
- guard let element else {
443
- return nil
444
- }
445
- let frame = element.frame
446
- guard !frame.isEmpty else {
447
- return nil
448
- }
449
- return CGPoint(x: frame.midX, y: frame.midY)
450
- }
451
-
452
- /// A focus-moved signal for iOS text entry, where `focusedTextInput` is intentionally nil.
453
- /// The software keyboard TRANSITIONING from hidden (at entry) to visible means the field we
454
- /// just tapped gained first-responder. If the keyboard was ALREADY up (e.g. back-to-back
455
- /// fills into different fields), its visibility is not evidence focus moved to the new field,
456
- /// so callers must keep waiting rather than typing into the previously-focused field.
457
- private func keyboardBecameVisible(app: XCUIApplication, wasVisibleAtEntry: Bool) -> Bool {
458
- return !wasVisibleAtEntry && isKeyboardVisible(app: app)
459
- }
460
-
461
- private func keyboardElementExists(app: XCUIApplication) -> Bool {
462
- #if os(iOS)
463
- return safely("KEYBOARD_EXISTS", false) { app.keyboards.firstMatch.exists }
464
- #else
465
- return false
466
- #endif
467
- }
468
-
469
180
  private func moveCaretToEnd(element: XCUIElement) {
470
181
  #if os(tvOS)
471
182
  return
@@ -516,14 +227,13 @@ extension RunnerTests {
516
227
  }
517
228
 
518
229
  private func isPlaceholderValue(_ value: String, for element: XCUIElement) -> Bool {
230
+ if Self.textMatchesPlaceholder(value, placeholder: element.placeholderValue) {
231
+ return true
232
+ }
519
233
  let normalizedValue = value.trimmingCharacters(in: .whitespacesAndNewlines)
520
234
  guard !normalizedValue.isEmpty else {
521
235
  return false
522
236
  }
523
- let placeholder = element.placeholderValue?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
524
- if !placeholder.isEmpty && normalizedValue == placeholder {
525
- return true
526
- }
527
237
  if isGenericTextInputLabel(normalizedValue) {
528
238
  return true
529
239
  }
@@ -531,6 +241,13 @@ extension RunnerTests {
531
241
  return normalizedLabel == normalizedValue && isGenericTextInputLabel(normalizedLabel)
532
242
  }
533
243
 
244
+ static func textMatchesPlaceholder(_ text: String, placeholder: String?) -> Bool {
245
+ let normalizedText = text.trimmingCharacters(in: .whitespacesAndNewlines)
246
+ guard !normalizedText.isEmpty else { return false }
247
+ let normalizedPlaceholder = placeholder?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
248
+ return !normalizedPlaceholder.isEmpty && normalizedText == normalizedPlaceholder
249
+ }
250
+
534
251
  private func isGenericTextInputLabel(_ value: String) -> Bool {
535
252
  switch value {
536
253
  case "Text input field":
@@ -0,0 +1,206 @@
1
+ import XCTest
2
+
3
+ // Text-entry target acquisition: choosing the element a `type`/`fill` will address and getting
4
+ // focus onto it — the one-shot tap witness, the post-tap stabilization, and the two
5
+ // `focusTextInputForTextEntry` entry points the command layer calls. Whether that target is ready
6
+ // is RunnerTests+TextEntryReadiness.swift's question, and this file asks it rather than answering
7
+ // it.
8
+ extension RunnerTests {
9
+ func rememberTextEntryTap(_ element: XCUIElement?) {
10
+ guard let element, isTextEntryElement(element) else {
11
+ clearRememberedTextEntryTap()
12
+ return
13
+ }
14
+ textEntryTapWitness = TextEntryTapWitness(
15
+ element: element,
16
+ bundleId: currentBundleId,
17
+ processIdentifier: currentAppProcessIdentifier
18
+ )
19
+ }
20
+
21
+ func clearRememberedTextEntryTap() {
22
+ textEntryTapWitness = nil
23
+ }
24
+
25
+ private func rememberedTextEntryTarget() -> TextEntryTarget? {
26
+ guard let witness = textEntryTapWitness else {
27
+ return nil
28
+ }
29
+ // The tap is proof for one immediately-following bare type only. Consume it before checking
30
+ // the element so a failed or interrupted type cannot reuse stale focus evidence.
31
+ clearRememberedTextEntryTap()
32
+ guard witness.matches(
33
+ bundleId: currentBundleId,
34
+ processIdentifier: currentAppProcessIdentifier
35
+ ) else {
36
+ return nil
37
+ }
38
+ let element = witness.element
39
+ // XCUIElement is query-backed rather than a stable node identity. A same-identifier field
40
+ // introduced by app-side navigation between tap and this immediate type can therefore
41
+ // re-resolve here; keep the witness one-shot and fail closed on every observable identity
42
+ // boundary instead of using frame equality, which would reject legitimate layout changes.
43
+ guard safely("LAST_TAPPED_TEXT_INPUT_EXISTS", false, { element.exists }) else {
44
+ return nil
45
+ }
46
+ // Keep the target scoped to the element that the preceding tap actually selected. Do not
47
+ // attach a refresh point: if that element disappeared, bare type must fail closed rather
48
+ // than rediscovering a different field or dispatching unscoped app.typeText.
49
+ return TextEntryTarget(
50
+ element: element,
51
+ refreshPoint: nil,
52
+ prefersFocusedElement: false,
53
+ fromTapWitness: true
54
+ )
55
+ }
56
+
57
+ func stabilizeTextInputBeforeTyping(
58
+ app: XCUIApplication,
59
+ target: XCUIElement?,
60
+ keyboardVisibleBeforeTap: Bool? = nil
61
+ ) -> TextEntryStabilization {
62
+ #if os(tvOS)
63
+ return TextEntryStabilization(element: target, focusConfirmed: true)
64
+ #else
65
+ let latest = target
66
+ let keyboardVisibleAtEntry = keyboardVisibleBeforeTap ?? isKeyboardVisible(app: app)
67
+ let deadline = Date().addingTimeInterval(TextEntryTiming.focusTimeout)
68
+ while Date() < deadline {
69
+ if let focused = focusedTextInput(app: app) {
70
+ return TextEntryStabilization(element: focused, focusConfirmed: true)
71
+ }
72
+ // focusedTextInput is intentionally nil on iOS; treat the keyboard transitioning to
73
+ // visible after our tap as the focus-moved signal. Don't fast-path when it was already up.
74
+ if keyboardBecameVisible(app: app, wasVisibleAtEntry: keyboardVisibleAtEntry) {
75
+ return TextEntryStabilization(element: latest, focusConfirmed: true)
76
+ }
77
+ sleepFor(TextEntryTiming.pollInterval)
78
+ }
79
+ return TextEntryStabilization(element: latest, focusConfirmed: false)
80
+ #endif
81
+ }
82
+
83
+ func focusTextInputForTextEntry(app: XCUIApplication, x: Double?, y: Double?) -> TextEntryTarget {
84
+ guard let x, let y else {
85
+ let softwareKeyboardVisible = isKeyboardVisible(app: app)
86
+ if !softwareKeyboardVisible, let rememberedTarget = rememberedTextEntryTarget() {
87
+ return rememberedTarget
88
+ }
89
+ // Bare `type` targets the current first responder. On iOS we intentionally do not trust
90
+ // `hasKeyboardFocus`, but an already-visible software keyboard is sufficient evidence that
91
+ // app.typeText has a receiver; waiting the full readiness timeout cannot prove a stronger
92
+ // target because there is no selector/coordinate focus move to validate.
93
+ if softwareKeyboardVisible {
94
+ return TextEntryTarget(
95
+ element: focusedTextInput(app: app),
96
+ refreshPoint: nil,
97
+ prefersFocusedElement: true
98
+ )
99
+ }
100
+ let focused = waitForTextEntryReadiness(
101
+ app: app,
102
+ target: TextEntryTarget(
103
+ element: focusedTextInput(app: app),
104
+ refreshPoint: nil,
105
+ prefersFocusedElement: true
106
+ )
107
+ )
108
+ return TextEntryTarget(element: focused, refreshPoint: nil, prefersFocusedElement: true)
109
+ }
110
+
111
+ let keyboardVisibleBeforeTap = isKeyboardVisible(app: app)
112
+ let target = textInputAt(app: app, x: x, y: y)
113
+ let requestedPoint = CGPoint(x: x, y: y)
114
+ if let target {
115
+ let frame = target.frame
116
+ if !frame.isEmpty {
117
+ _ = tapAt(app: app, x: frame.midX, y: frame.midY)
118
+ } else {
119
+ _ = tapAt(app: app, x: x, y: y)
120
+ }
121
+ } else {
122
+ _ = tapAt(app: app, x: x, y: y)
123
+ }
124
+ // A visible keyboard is not enough evidence for app.typeText, because focus may still
125
+ // belong to a previous field. With a concrete target we type through XCUIElement.typeText,
126
+ // so after tapping it the iOS readiness timeout cannot prove anything stronger.
127
+ if keyboardVisibleBeforeTap, let target {
128
+ return TextEntryTarget(
129
+ element: target,
130
+ refreshPoint: textEntryRefreshPoint(for: target) ?? requestedPoint,
131
+ prefersFocusedElement: false
132
+ )
133
+ }
134
+ let stabilized = stabilizeTextInputBeforeTyping(
135
+ app: app,
136
+ target: target,
137
+ keyboardVisibleBeforeTap: keyboardVisibleBeforeTap
138
+ )
139
+ let readyTarget = TextEntryTarget(
140
+ element: stabilized.element ?? target,
141
+ refreshPoint: requestedPoint,
142
+ prefersFocusedElement: false
143
+ )
144
+ let concreteTargetReady = keyboardVisibleBeforeTap && readyTarget.element != nil
145
+ let element = stabilized.focusConfirmed || concreteTargetReady
146
+ ? (stabilized.element ?? target)
147
+ : (waitForTextEntryReadiness(app: app, target: readyTarget) ?? stabilized.element ?? target)
148
+ return TextEntryTarget(
149
+ element: element,
150
+ refreshPoint: textEntryRefreshPoint(for: element) ?? requestedPoint,
151
+ prefersFocusedElement: false
152
+ )
153
+ }
154
+
155
+ func focusTextInputForTextEntry(app: XCUIApplication, element: XCUIElement) -> TextEntryTarget {
156
+ let point = textEntryRefreshPoint(for: element)
157
+ let keyboardVisibleBeforeTap = isKeyboardVisible(app: app)
158
+ if let point {
159
+ _ = tapAt(app: app, x: point.x, y: point.y)
160
+ }
161
+ // See the coordinate-target path above: direct element typing keeps this scoped to the
162
+ // tapped target, while the first-character warmup and final verify still catch dropped input.
163
+ if keyboardVisibleBeforeTap {
164
+ return TextEntryTarget(
165
+ element: element,
166
+ refreshPoint: textEntryRefreshPoint(for: element) ?? point,
167
+ prefersFocusedElement: false
168
+ )
169
+ }
170
+ let stabilized = stabilizeTextInputBeforeTyping(
171
+ app: app,
172
+ target: element,
173
+ keyboardVisibleBeforeTap: keyboardVisibleBeforeTap
174
+ )
175
+ let readyTarget = TextEntryTarget(
176
+ element: stabilized.element ?? element,
177
+ refreshPoint: point,
178
+ prefersFocusedElement: false
179
+ )
180
+ let resolved = stabilized.focusConfirmed
181
+ ? (stabilized.element ?? element)
182
+ : (waitForTextEntryReadiness(app: app, target: readyTarget) ?? stabilized.element ?? element)
183
+ return TextEntryTarget(
184
+ element: resolved,
185
+ refreshPoint: textEntryRefreshPoint(for: resolved) ?? point,
186
+ prefersFocusedElement: false
187
+ )
188
+ }
189
+
190
+ private func textEntryRefreshPoint(for element: XCUIElement?) -> CGPoint? {
191
+ guard let element else {
192
+ return nil
193
+ }
194
+ let frame = element.frame
195
+ guard !frame.isEmpty else {
196
+ return nil
197
+ }
198
+ return CGPoint(x: frame.midX, y: frame.midY)
199
+ }
200
+
201
+ /// A focus-moved signal for iOS text entry, where `focusedTextInput` is intentionally nil.
202
+ /// The software keyboard TRANSITIONING from hidden (at entry) to visible means the field we
203
+ /// just tapped gained first-responder. If the keyboard was ALREADY up (e.g. back-to-back
204
+ /// fills into different fields), its visibility is not evidence focus moved to the new field,
205
+ /// so callers must keep waiting rather than typing into the previously-focused field.
206
+ }