agent-device 0.20.10 → 0.21.1

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 (510) hide show
  1. package/README.md +2 -2
  2. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.20.10.apk → agent-device-android-ime-helper-0.21.1.apk} +0 -0
  3. package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.1.apk.sha256 +1 -0
  4. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.20.10.manifest.json → agent-device-android-ime-helper-0.21.1.manifest.json} +4 -4
  5. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.1.apk +0 -0
  6. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.1.apk.sha256 +1 -0
  7. package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.20.10.manifest.json → agent-device-android-snapshot-helper-0.21.1.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 +106 -0
  12. package/apple/snapshot-bridge/SnapshotBridge.m +286 -0
  13. package/apple/snapshot-bridge/SnapshotBridgeCapture.h +25 -0
  14. package/apple/snapshot-bridge/SnapshotBridgeCapture.m +138 -0
  15. package/apple/snapshot-bridge/SnapshotBridgeRuntime.h +34 -0
  16. package/apple/snapshot-bridge/SnapshotBridgeRuntime.m +411 -0
  17. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunner/AgentDeviceRunnerApp.m +58 -0
  18. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/project.pbxproj +19 -0
  19. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.h +12 -0
  20. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.m +20 -0
  21. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAlertVerification.swift +36 -0
  22. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSelectorMatchPolicy.swift +15 -15
  23. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerStringNormalization.swift +8 -0
  24. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.h +12 -1
  25. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.m +100 -6
  26. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSystemSurfaceHostPolicy.swift +72 -0
  27. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTapPointPolicy.swift +63 -14
  28. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+AXSnapshotFallback.swift +677 -83
  29. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Alert.swift +35 -92
  30. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+AlertObservation.swift +124 -0
  31. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+BlockingSystemModalResolution.swift +39 -4
  32. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +1058 -213
  33. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandJournal.swift +296 -14
  34. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Environment.swift +1 -1
  35. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Exceptions.swift +10 -10
  36. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +309 -149
  37. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Keyboard.swift +101 -42
  38. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Lifecycle.swift +24 -17
  39. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +124 -199
  40. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Navigation.swift +71 -4
  41. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+PrivateAXElementTypes.swift +12 -0
  42. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+PrivateAXPresentation.swift +211 -78
  43. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScreenRecorder.swift +30 -10
  44. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScrollGesture.swift +254 -24
  45. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SequenceExecution.swift +206 -22
  46. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +465 -494
  47. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotBackendCapabilities.swift +168 -0
  48. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCapturePlan.swift +802 -247
  49. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotPresentationInvariant.swift +19 -0
  50. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotTiming.swift +185 -0
  51. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedCommitDeadline.swift +206 -0
  52. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedGesturePolicy.swift +83 -5
  53. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedTextEntry.swift +156 -34
  54. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SystemModal.swift +5 -4
  55. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntry.swift +35 -318
  56. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntryFocus.swift +206 -0
  57. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntryReadiness.swift +158 -0
  58. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextInputCandidatePolicy.swift +28 -0
  59. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextInputProbe.swift +101 -0
  60. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextTyping.swift +56 -23
  61. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Transport.swift +56 -27
  62. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TvRemote.swift +30 -24
  63. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +126 -80
  64. package/dist/apple/runner/AgentDeviceRunner/RecordingScripts/RecordingExportSupport.swift +111 -0
  65. package/dist/apple/runner/AgentDeviceRunner/RecordingScripts/recording-overlay.swift +47 -86
  66. package/dist/apple/runner/AgentDeviceRunner/RecordingScripts/recording-trim.swift +32 -79
  67. package/dist/apple/snapshot-presentation/Package.swift +21 -0
  68. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotGeometry.swift +56 -0
  69. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotModels.swift +259 -0
  70. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotPresentation.swift +153 -0
  71. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotPresentationFailure.swift +27 -0
  72. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotPresentationInvariant.swift +91 -0
  73. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotPresentationProjection.swift +161 -0
  74. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotScopePolicy.swift +53 -0
  75. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotVisibilityFold.swift +242 -0
  76. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotVisibilityFoldProjection.swift +97 -0
  77. package/dist/src/adapter.js +1 -0
  78. package/dist/src/adb-executor.js +2 -0
  79. package/dist/src/adb-failure.js +1 -0
  80. package/dist/src/adb-host.js +1 -0
  81. package/dist/src/adb.js +1 -0
  82. package/dist/src/agent-browser-lifecycle.js +1 -0
  83. package/dist/src/agent-browser-process-record.js +1 -0
  84. package/dist/src/agent-browser-provider.js +1 -0
  85. package/dist/src/agent-browser-tool.js +1 -1
  86. package/dist/src/agent-device-client.js +2 -2
  87. package/dist/src/ai-sdk.d.ts +2 -2
  88. package/dist/src/ai-sdk.js +1 -1
  89. package/dist/src/alert-contract.js +1 -0
  90. package/dist/src/alert-runtime.js +1 -0
  91. package/dist/src/android-adb.d.ts +27 -19
  92. package/dist/src/android-adb.js +1 -1
  93. package/dist/src/android-input-ownership.js +1 -0
  94. package/dist/src/android-system-chrome.js +1 -0
  95. package/dist/src/android.js +1 -1
  96. package/dist/src/api.js +11 -0
  97. package/dist/src/app-catalog.js +1 -0
  98. package/dist/src/app-deployment-resolution.js +2 -0
  99. package/dist/src/app-helpers.js +1 -1
  100. package/dist/src/app-inventory-contract.js +1 -0
  101. package/dist/src/app-inventory.js +1 -0
  102. package/dist/src/app-launch.js +2 -0
  103. package/dist/src/app-lifecycle-facade.js +1 -0
  104. package/dist/src/app-lifecycle.js +3 -6
  105. package/dist/src/app-lifecycle2.js +2 -2
  106. package/dist/src/app-lifecycle3.js +1 -0
  107. package/dist/src/app-log-files.js +1 -1
  108. package/dist/src/app-log-reconnect.js +1 -1
  109. package/dist/src/app-log-runtime.js +1 -4
  110. package/dist/src/app-log-runtime2.js +4 -0
  111. package/dist/src/app-parsers.js +4 -0
  112. package/dist/src/app-resolution-cache.js +1 -0
  113. package/dist/src/app-resolution-facade.js +1 -0
  114. package/dist/src/app-resolution.js +2 -2
  115. package/dist/src/app-state.js +1 -2
  116. package/dist/src/apple-runner-platform.js +1 -0
  117. package/dist/src/application-lifecycle-interaction.js +1 -0
  118. package/dist/src/application-lifecycle-runtime.js +1 -0
  119. package/dist/src/apps-simctl.js +1 -0
  120. package/dist/src/archive.js +4 -0
  121. package/dist/src/artifact-download.js +1 -0
  122. package/dist/src/artifacts.d.ts +6 -2
  123. package/dist/src/artifacts.js +1 -1
  124. package/dist/src/async-lifecycle.js +1 -0
  125. package/dist/src/audio-probe-result.js +1 -0
  126. package/dist/src/audio-probe-runtime.js +1 -0
  127. package/dist/src/audio-probe-support.js +1 -0
  128. package/dist/src/auth-session.js +1 -1
  129. package/dist/src/auth.js +1 -1
  130. package/dist/src/backend-snapshot-options.js +1 -1
  131. package/dist/src/{url.js → base-url.js} +1 -1
  132. package/dist/src/boot-diagnostics.js +2 -2
  133. package/dist/src/byte-limit-stream.js +1 -1
  134. package/dist/src/capabilities.js +1 -0
  135. package/dist/src/capture.js +1 -0
  136. package/dist/src/catalog.js +1 -0
  137. package/dist/src/cli-command-aliases.js +1 -0
  138. package/dist/src/cli-config.js +1 -1
  139. package/dist/src/cli-help.js +74 -51
  140. package/dist/src/cli-output.js +1 -1
  141. package/dist/src/cli-runner.js +1 -1
  142. package/dist/src/cli.js +4 -6
  143. package/dist/src/click-button.js +1 -0
  144. package/dist/src/client-metro-companion.js +1 -0
  145. package/dist/src/client-metro.js +1 -1
  146. package/dist/src/client-types.d.ts +280 -242
  147. package/dist/src/clipboard.js +1 -0
  148. package/dist/src/code-signature-cache.js +1 -0
  149. package/dist/src/code-signature.js +1 -0
  150. package/dist/src/color.js +1 -0
  151. package/dist/src/command-metadata.js +1 -1
  152. package/dist/src/command-schema.js +3 -3
  153. package/dist/src/command-surface.js +1 -1
  154. package/dist/src/command-tools.js +2 -2
  155. package/dist/src/command.js +1 -0
  156. package/dist/src/compatibility-policy.js +1 -0
  157. package/dist/src/config.js +1 -1
  158. package/dist/src/config2.js +1 -0
  159. package/dist/src/connection-runtime.js +1 -1
  160. package/dist/src/connection.js +5 -5
  161. package/dist/src/contracts.d.ts +1 -1
  162. package/dist/src/contracts.js +1 -1
  163. package/dist/src/daemon-client-lifecycle.js +1 -0
  164. package/dist/src/daemon-process.js +1 -0
  165. package/dist/src/daemon-registration.js +1 -0
  166. package/dist/src/daemon-shutdown-report.js +1 -1
  167. package/dist/src/daemon.js +2 -2
  168. package/dist/src/debug-symbols-facade.js +5 -0
  169. package/dist/src/deploy-result-target.js +1 -0
  170. package/dist/src/device-claim-inspection.js +1 -1
  171. package/dist/src/device-claim-rule.js +1 -0
  172. package/dist/src/device-input-state.d.ts +190 -3
  173. package/dist/src/device-inventory-context.js +1 -0
  174. package/dist/src/device-isolation.js +1 -0
  175. package/dist/src/device-release.js +1 -0
  176. package/dist/src/device-rotation.js +1 -0
  177. package/dist/src/device-selection-resolver.js +1 -0
  178. package/dist/src/device-session.js +1 -0
  179. package/dist/src/device.js +1 -1
  180. package/dist/src/device2.js +3 -2
  181. package/dist/src/diagnostics.js +3 -0
  182. package/dist/src/diagnostics2.js +1 -0
  183. package/dist/src/dispatch-resolve.js +1 -0
  184. package/dist/src/doctor-facade.js +1 -0
  185. package/dist/src/doctor-output.js +1 -0
  186. package/dist/src/doctor.js +1 -0
  187. package/dist/src/doctor2.js +1 -0
  188. package/dist/src/doctor3.js +1 -0
  189. package/dist/src/durable-resource.js +1 -0
  190. package/dist/src/entities.js +1 -1
  191. package/dist/src/error.js +2 -0
  192. package/dist/src/exec.js +1 -0
  193. package/dist/src/file.js +1 -0
  194. package/dist/src/find.js +1 -1
  195. package/dist/src/finders.js +1 -1
  196. package/dist/src/generic-settle.js +1 -1
  197. package/dist/src/generic.js +1 -1
  198. package/dist/src/gesture-admission.js +1 -0
  199. package/dist/src/gesture-plan-types.js +1 -0
  200. package/dist/src/gesture-plan.js +1 -0
  201. package/dist/src/gesture-runtime.js +1 -0
  202. package/dist/src/harmonyos.js +1 -1
  203. package/dist/src/hdc.js +1 -1
  204. package/dist/src/helper.js +2 -2
  205. package/dist/src/host-file.js +1 -0
  206. package/dist/src/human-control-contract.js +1 -0
  207. package/dist/src/human-control.js +1 -0
  208. package/dist/src/index.d.ts +1 -1
  209. package/dist/src/index.js +1 -1
  210. package/dist/src/input-actions.js +1 -4
  211. package/dist/src/input-actions2.js +2 -1
  212. package/dist/src/install-artifact-facade.js +1 -0
  213. package/dist/src/install-source-network-transport.js +1 -0
  214. package/dist/src/install-source.d.ts +3 -10
  215. package/dist/src/install-source2.js +1 -1
  216. package/dist/src/interaction-positionals.js +1 -0
  217. package/dist/src/interaction-ref-policy.js +1 -0
  218. package/dist/src/interaction.js +1 -1
  219. package/dist/src/interaction2.js +1 -1
  220. package/dist/src/interactor-operation-catalog.js +1 -0
  221. package/dist/src/interactor-types.js +1 -0
  222. package/dist/src/interactor.js +6 -2
  223. package/dist/src/interactor2.js +1 -1
  224. package/dist/src/interactors.js +1 -1
  225. package/dist/src/internal/bin.d.ts +1 -1
  226. package/dist/src/internal/bin.js +1 -2
  227. package/dist/src/internal/companion-tunnel.d.ts +1 -1
  228. package/dist/src/internal/companion-tunnel.js +1 -1
  229. package/dist/src/internal/daemon.d.ts +1 -1
  230. package/dist/src/internal/daemon.js +2 -4
  231. package/dist/src/internal/png-worker.d.ts +4 -4
  232. package/dist/src/internal/png-worker.js +1 -1
  233. package/dist/src/internal/run-script-http-child.d.ts +4 -0
  234. package/dist/src/internal/run-script-http-child.js +1 -0
  235. package/dist/src/internal/update-check-entry.d.ts +1 -1
  236. package/dist/src/inventory.js +4 -4
  237. package/dist/src/inventory2.js +1 -1
  238. package/dist/src/inventory3.js +1 -1
  239. package/dist/src/inventory5.js +1 -1
  240. package/dist/src/inventory6.js +1 -1
  241. package/dist/src/ios-client.js +16 -0
  242. package/dist/src/ios-snapshot-acquisition.js +1 -0
  243. package/dist/src/ios-snapshot-adapter.js +1 -0
  244. package/dist/src/ios-snapshot-planning.js +1 -0
  245. package/dist/src/ios-snapshot-runtime.js +1 -0
  246. package/dist/src/ios-system-surface.js +1 -0
  247. package/dist/src/json.js +1 -0
  248. package/dist/src/keyboard-actions.js +1 -0
  249. package/dist/src/launch-console.js +1 -0
  250. package/dist/src/lease-context.js +1 -1
  251. package/dist/src/lease-scope.js +1 -0
  252. package/dist/src/lease.js +1 -1
  253. package/dist/src/limrun-runtime-dependencies.js +1 -1
  254. package/dist/src/limrun.d.ts +1610 -7
  255. package/dist/src/limrun.js +1 -1
  256. package/dist/src/linux.js +1 -1
  257. package/dist/src/local-interactor-operation-set.js +1 -0
  258. package/dist/src/location-coordinates.js +1 -1
  259. package/dist/src/logcat.js +1 -1
  260. package/dist/src/macos-facade.js +1 -0
  261. package/dist/src/managed-device-scope.js +1 -0
  262. package/dist/src/mechanics.js +19 -0
  263. package/dist/src/metro.d.ts +4 -2
  264. package/dist/src/metro.js +1 -1
  265. package/dist/src/metro2.js +1 -1
  266. package/dist/src/mobile-snapshot-semantics.js +1 -1
  267. package/dist/src/multitouch-support.js +1 -0
  268. package/dist/src/numeric.js +1 -0
  269. package/dist/src/observability.js +1 -1
  270. package/dist/src/observation.js +2 -0
  271. package/dist/src/open-target-resolution.js +1 -0
  272. package/dist/src/output-path.js +1 -1
  273. package/dist/src/owner-identity.js +3 -1
  274. package/dist/src/parameterized-recorded-fill.js +1 -0
  275. package/dist/src/parser.js +1 -1
  276. package/dist/src/perf-facade.js +2 -0
  277. package/dist/src/perf-process-identity.js +1 -0
  278. package/dist/src/perf-runtime-operation-builder.js +1 -0
  279. package/dist/src/perf-runtime-plan.js +1 -0
  280. package/dist/src/perf-runtime.js +1 -0
  281. package/dist/src/perf.js +1 -4
  282. package/dist/src/physical-device-control.js +1 -1
  283. package/dist/src/physical-device-facade.js +1 -0
  284. package/dist/src/platform-request-scope.js +1 -0
  285. package/dist/src/platform-runtime-android-adb-host.js +1 -0
  286. package/dist/src/platform-runtime-android-mechanics.js +1 -0
  287. package/dist/src/platform-runtime-app-log-android-transport.js +1 -1
  288. package/dist/src/platform-runtime-app-log-process.js +1 -1
  289. package/dist/src/platform-runtime-host.js +1 -1
  290. package/dist/src/platform-runtime-managed-owner.js +1 -0
  291. package/dist/src/platform-runtime-network-web-transport.js +1 -1
  292. package/dist/src/platform-runtime-open-target.js +1 -1
  293. package/dist/src/platform-runtime-operation-host.js +4 -4
  294. package/dist/src/platform-runtime-runtime-hints.js +2 -2
  295. package/dist/src/platform-runtime-screen-recording-android-host.js +1 -1
  296. package/dist/src/platform-runtime-screen-recording-apple-runner-host.js +1 -1
  297. package/dist/src/platform-runtime-screen-recording-apple-runner-transport.js +1 -1
  298. package/dist/src/platform-runtime-screen-recording-apple-simulator-host.js +1 -1
  299. package/dist/src/platform-runtime-screen-recording-apple-transport.js +1 -1
  300. package/dist/src/platform-runtime-screen-recording-finalizer-host.js +1 -1
  301. package/dist/src/platform-runtime-screen-recording-harmony-host.js +1 -1
  302. package/dist/src/platform-runtime-screen-recording-process-host.js +1 -0
  303. package/dist/src/platform-runtime-screen-recording-web-host.js +1 -1
  304. package/dist/src/platform-runtime-unavailable.js +1 -0
  305. package/dist/src/platform-runtime.js +1 -1
  306. package/dist/src/platform-runtime2.js +1 -1
  307. package/dist/src/plist.js +1 -1
  308. package/dist/src/png-worker-contract.js +1 -1
  309. package/dist/src/prepare-kind.js +1 -0
  310. package/dist/src/process-exit.js +1 -1
  311. package/dist/src/process.js +1 -0
  312. package/dist/src/provider-device-runtime.js +1 -0
  313. package/dist/src/provider-session-ownership.js +2 -0
  314. package/dist/src/provider-webdriver.js +1 -1
  315. package/dist/src/provider.js +1 -1
  316. package/dist/src/providers.js +1 -0
  317. package/dist/src/proxy.js +2 -2
  318. package/dist/src/react-native-overlay.js +1 -0
  319. package/dist/src/react-native.js +1 -1
  320. package/dist/src/record-runtime.js +1 -1
  321. package/dist/src/record-trace.js +1 -1
  322. package/dist/src/rect-center.js +1 -0
  323. package/dist/src/register-builtins.js +1 -0
  324. package/dist/src/registry.js +40 -28
  325. package/dist/src/remote-config2.js +1 -1
  326. package/dist/src/replay-divergence.js +2 -0
  327. package/dist/src/replay.js +2 -2
  328. package/dist/src/reporting.js +8 -7
  329. package/dist/src/request-admission.js +1 -0
  330. package/dist/src/request-cancel.js +1 -0
  331. package/dist/src/request-cancellation.js +1 -0
  332. package/dist/src/request-progress-protocol.js +1 -0
  333. package/dist/src/request-providers.js +1 -0
  334. package/dist/src/request-runtime-binding.js +1 -0
  335. package/dist/src/request.js +1 -0
  336. package/dist/src/result-serialization.js +1 -0
  337. package/dist/src/retry.js +1 -0
  338. package/dist/src/rolldown-runtime.js +1 -1
  339. package/dist/src/runner-cache-metadata.js +2 -0
  340. package/dist/src/runner-client.js +1 -1
  341. package/dist/src/runner-disposal.js +1 -1
  342. package/dist/src/runner-operations-facade.js +1 -0
  343. package/dist/src/runner-owner-facade.js +1 -0
  344. package/dist/src/runner-owner-state.js +1 -0
  345. package/dist/src/runner-presentation.js +1 -0
  346. package/dist/src/runner.js +1 -0
  347. package/dist/src/{runtime-transport.js → runtime-transport-hints.js} +1 -1
  348. package/dist/src/runtime.js +3 -3
  349. package/dist/src/runtime2.js +4 -4
  350. package/dist/src/runtime3.js +1 -1
  351. package/dist/src/runtime4.js +5 -3
  352. package/dist/src/runtime5.js +1 -1
  353. package/dist/src/runtime6.js +1 -1
  354. package/dist/src/runtime7.js +1 -1
  355. package/dist/src/runtime8.js +1 -1
  356. package/dist/src/runtime9.js +1 -1
  357. package/dist/src/scoped-provider.js +1 -1
  358. package/dist/src/screen-recording-completion.js +1 -0
  359. package/dist/src/screen-recording-live-handle.js +1 -0
  360. package/dist/src/screen-recording-options.js +1 -0
  361. package/dist/src/screen-recording-resource-recovery.js +1 -0
  362. package/dist/src/screen-recording-runtime.js +1 -0
  363. package/dist/src/screenshot-density.js +1 -1
  364. package/dist/src/screenshot-result.js +1 -6
  365. package/dist/src/screenshot-runtime.js +1 -0
  366. package/dist/src/screenshot.js +1 -1
  367. package/dist/src/screenshot2.js +1 -0
  368. package/dist/src/screenshot3.js +5 -0
  369. package/dist/src/script.js +1 -0
  370. package/dist/src/scroll-command.js +1 -0
  371. package/dist/src/scroll-gesture.js +1 -0
  372. package/dist/src/scroll-indicator.js +1 -0
  373. package/dist/src/sdk-batch-runner.d.ts +23 -6
  374. package/dist/src/sdk-batch-runner.js +2 -3
  375. package/dist/src/sdk-batch.js +2 -0
  376. package/dist/src/sdk-contracts.d.ts +170 -41
  377. package/dist/src/sdk-contracts.js +2 -2
  378. package/dist/src/sdk-device.js +1 -1
  379. package/dist/src/sdk-finders.d.ts +1 -1
  380. package/dist/src/sdk-finders.js +1 -1
  381. package/dist/src/sdk-io.js +1 -1
  382. package/dist/src/sdk-remote-config.d.ts +1 -1
  383. package/dist/src/sdk-remote-config.js +1 -1
  384. package/dist/src/sdk-selectors.d.ts +524 -140
  385. package/dist/src/sdk-selectors.js +1 -1
  386. package/dist/src/selection.js +1 -0
  387. package/dist/src/selector-runtime.js +1 -1
  388. package/dist/src/selectors.js +1 -1
  389. package/dist/src/semantic-index.js +1 -0
  390. package/dist/src/server.js +4 -4
  391. package/dist/src/session-allocation.js +1 -0
  392. package/dist/src/session-artifact-paths.js +1 -0
  393. package/dist/src/session-observation.js +1 -0
  394. package/dist/src/session-paths.js +1 -0
  395. package/dist/src/session-repair-tombstone.js +1 -0
  396. package/dist/src/session-routing.js +1 -0
  397. package/dist/src/session-runtime-admission.js +1 -0
  398. package/dist/src/session-snapshot.js +1 -0
  399. package/dist/src/session-store.js +4 -0
  400. package/dist/src/session-target-evidence.js +1 -1
  401. package/dist/src/session2.js +5 -27
  402. package/dist/src/settings.js +1 -0
  403. package/dist/src/shared.js +2 -2
  404. package/dist/src/simctl-facade.js +1 -0
  405. package/dist/src/simctl.js +1 -1
  406. package/dist/src/simulator-facade.js +1 -0
  407. package/dist/src/simulator.js +1 -1
  408. package/dist/src/snapshot-capture-annotations.js +1 -0
  409. package/dist/src/snapshot-desktop-projection.js +1 -0
  410. package/dist/src/snapshot-diagnostics.js +1 -0
  411. package/dist/src/snapshot-process.js +1 -0
  412. package/dist/src/snapshot-quality-backend-capabilities.js +1 -0
  413. package/dist/src/snapshot-quality-verdict.js +1 -0
  414. package/dist/src/snapshot-runtime-binding.js +1 -0
  415. package/dist/src/snapshot-runtime.js +1 -1
  416. package/dist/src/snapshot-scope.js +1 -1
  417. package/dist/src/snapshot-state.js +1 -0
  418. package/dist/src/snapshot.js +1 -8
  419. package/dist/src/snapshot2.js +1 -1
  420. package/dist/src/snapshot3.js +1 -1
  421. package/dist/src/snapshot4.js +1 -0
  422. package/dist/src/src.js +1 -1
  423. package/dist/src/src2.js +1 -1
  424. package/dist/src/src3.js +1 -2
  425. package/dist/src/src4.js +1 -1
  426. package/dist/src/src5.js +1 -0
  427. package/dist/src/src6.js +1 -0
  428. package/dist/src/src7.js +2 -0
  429. package/dist/src/src8.js +115 -0
  430. package/dist/src/startup-recovery-fence.js +1 -0
  431. package/dist/src/status-markers.js +1 -0
  432. package/dist/src/string-enum.js +1 -1
  433. package/dist/src/success-text.js +1 -0
  434. package/dist/src/surface-snapshot.js +1 -0
  435. package/dist/src/surface-snapshot2.js +1 -0
  436. package/dist/src/takeover.js +5 -0
  437. package/dist/src/tool-provider-facade.js +1 -0
  438. package/dist/src/tool-provider.js +4 -3
  439. package/dist/src/tool-provider2.js +1 -1
  440. package/dist/src/tool-provider3.js +1 -1
  441. package/dist/src/toolchain-probe.js +2 -0
  442. package/dist/src/touch-reference-frame.js +1 -0
  443. package/dist/src/touch-runtime.js +1 -0
  444. package/dist/src/transport.js +3 -0
  445. package/dist/src/tree.js +1 -0
  446. package/dist/src/ttl-memo.js +1 -0
  447. package/dist/src/tv-remote.js +1 -0
  448. package/dist/src/type-text-runtime.js +1 -0
  449. package/dist/src/undici.js +99 -0
  450. package/dist/src/update-check.js +1 -1
  451. package/dist/src/verified-file.js +1 -1
  452. package/dist/src/version.js +1 -1
  453. package/dist/src/video.js +2 -2
  454. package/dist/src/web.js +2 -1
  455. package/dist/src/web2.js +1 -0
  456. package/dist/src/webdriver-ios-snapshot.js +1 -0
  457. package/dist/src/webdriver-source.js +1 -0
  458. package/dist/src/window-state.js +1 -0
  459. package/linux/atspi-dump.py +54 -9
  460. package/package.json +58 -35
  461. package/server.json +9 -3
  462. package/android/ime-helper/dist/agent-device-android-ime-helper-0.20.10.apk.sha256 +0 -1
  463. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.10.apk +0 -0
  464. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.10.apk.sha256 +0 -1
  465. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CoordinateTextEntryTests.swift +0 -4
  466. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+FlatSnapshotFiltering.swift +0 -168
  467. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+LifecycleCacheTests.swift +0 -6
  468. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+RecordingTests.swift +0 -4
  469. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SelectorMatchPolicyTests.swift +0 -4
  470. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotPresentation.swift +0 -183
  471. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotPresentationTests.swift +0 -0
  472. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotTraversalIdentityTests.swift +0 -4
  473. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntryPolicyTests.swift +0 -4
  474. package/dist/src/appearance.js +0 -1
  475. package/dist/src/apps.js +0 -5
  476. package/dist/src/audio-probe.js +0 -1
  477. package/dist/src/cli-grammar.js +0 -1
  478. package/dist/src/command-catalog.js +0 -1
  479. package/dist/src/debug-symbols.js +0 -5
  480. package/dist/src/device-input-state.js +0 -2
  481. package/dist/src/device-ready.js +0 -1
  482. package/dist/src/dispatch.js +0 -1
  483. package/dist/src/handler-utils.js +0 -1
  484. package/dist/src/helper-package-install.js +0 -1
  485. package/dist/src/host-process.js +0 -3
  486. package/dist/src/ime-lifecycle.js +0 -2
  487. package/dist/src/ime-recovery-marker.js +0 -1
  488. package/dist/src/input-actions3.js +0 -1
  489. package/dist/src/install-artifact.js +0 -1
  490. package/dist/src/install-artifact2.js +0 -1
  491. package/dist/src/interaction-common.js +0 -1
  492. package/dist/src/interaction-runtime.js +0 -1
  493. package/dist/src/interaction-snapshot.js +0 -1
  494. package/dist/src/manifest.js +0 -1
  495. package/dist/src/method-scope.js +0 -1
  496. package/dist/src/png-resize.js +0 -1
  497. package/dist/src/process-lock.js +0 -1
  498. package/dist/src/runner-lease.js +0 -1
  499. package/dist/src/runner-sequence.js +0 -1
  500. package/dist/src/script-source-bundle.js +0 -16
  501. package/dist/src/sdk-android-adb.d.ts +0 -245
  502. package/dist/src/sdk-android-adb.js +0 -4
  503. package/dist/src/sdk.js +0 -1
  504. package/dist/src/setting-state.js +0 -1
  505. package/dist/src/shell-quote.js +0 -1
  506. package/dist/src/snapshot-session.js +0 -1
  507. package/dist/src/timing-safe-equal.js +0 -1
  508. package/dist/src/unsupported-interactor.js +0 -1
  509. package/dist/src/verdict.js +0 -1
  510. /package/dist/src/{recording-telemetry.js → telemetry.js} +0 -0
@@ -1,7 +1,7 @@
1
1
  import XCTest
2
2
 
3
- // AX-independent text synthesis: private-XCTest boundary, pacing plan, and route policy.
4
- // The verified XCUIElement path remains in TextEntry/TextTyping.
3
+
4
+
5
5
  extension RunnerTests {
6
6
  enum SynthesizedReplacementRouteOutcome {
7
7
  case notApplicable
@@ -40,9 +40,14 @@ extension RunnerTests {
40
40
  replacingExistingText: Bool
41
41
  ) -> SynthesizedTextEntryAction {
42
42
  #if os(iOS)
43
+ let postStartedAt = Date()
43
44
  let result = replacingExistingText
44
45
  ? RunnerSynthesizedTextEntry.replaceText(withApplication: app, text: text)
45
46
  : RunnerSynthesizedTextEntry.synthesizeText(withApplication: app, text: text)
47
+ NSLog(
48
+ "[DEBUG-1874] synthesize posted %d chars status=%d tookMs=%.0f",
49
+ text.count, result.status.rawValue, postStartedAt.timeIntervalSinceNow * -1000
50
+ )
46
51
  return Self.action(status: result.status, message: result.message)
47
52
  #else
48
53
  return .fallback
@@ -122,6 +127,17 @@ extension RunnerTests {
122
127
  sleepFor(request.delaySeconds)
123
128
  }
124
129
  }
130
+
131
+
132
+
133
+
134
+
135
+
136
+ let commit = awaitSynthesizedReplacementCommit(
137
+ app: request.app,
138
+ target: request.target,
139
+ expectedText: request.text
140
+ )
125
141
  logTextEntryPhase(
126
142
  commandId: request.commandId,
127
143
  phase: "total",
@@ -135,7 +151,8 @@ extension RunnerTests {
135
151
  repaired: false,
136
152
  expectedText: request.text,
137
153
  observedText: nil,
138
- textEntryRoute: "synthesized-first-responder-replacement"
154
+ textEntryRoute: "synthesized-first-responder-replacement",
155
+ failure: Self.textEntryFailure(forCommitOutcome: commit)
139
156
  )
140
157
  )
141
158
  #else
@@ -165,12 +182,12 @@ extension RunnerTests {
165
182
  case diverged
166
183
  }
167
184
 
168
- // The private synthesize call returns once the event record is posted, not once the target
169
- // app has committed the characters, so intermediate reads walk prefix-by-prefix toward the
170
- // expected value. Anything off that prefix path means the app transformed the input
171
- // (formatter, mid-text caret, autocomplete) and the runner must not second-guess it. An
172
- // unreadable value — secure field, or the element stopped resolving — ends the wait the
173
- // same way.
185
+
186
+
187
+
188
+
189
+
190
+
174
191
  static func synthesizedTextCommitProgress(
175
192
  observedText: String?,
176
193
  expectedText: String
@@ -184,35 +201,140 @@ extension RunnerTests {
184
201
  return expectedText.hasPrefix(observedText) ? .pending : .diverged
185
202
  }
186
203
 
187
- /// Blocks until the synthesized bare-type text is observable in the target field, so `type`
188
- /// cannot report ok while trailing characters are still uncommitted on a slow simulator.
189
- ///
190
- /// Observation only. A stalled prefix cannot be told apart from a suffix still queued in the
191
- /// event stream, so re-synthesizing the difference risks committing it twice after the command
192
- /// already reported success. Text carrying a submit key is skipped outright: the app may clear
193
- /// or rewrite the field on submit, so `textBefore + typedText` is not the value to wait for.
194
- func awaitSynthesizedFirstResponderCommit(
195
- app: XCUIApplication,
196
- target: TextEntryTarget,
197
- textBefore: String?,
198
- typedText: String
199
- ) {
200
- guard let textBefore, !typedText.contains("\n"), !typedText.contains("\r") else {
201
- return
204
+
205
+
206
+
207
+ static func commonPrefixLength(_ lhs: String, _ rhs: String) -> Int {
208
+ var length = 0
209
+ for (l, r) in zip(lhs, rhs) {
210
+ if l != r { break }
211
+ length += 1
202
212
  }
203
- let expectedText = textBefore + typedText
204
- let deadline = Date().addingTimeInterval(TextEntryTiming.synthesizedCommitTimeout)
205
- while Date() < deadline {
206
- let observedText = editableTextValue(
207
- for: resolveTextEntryElement(app: app, target: target),
208
- treatingPlaceholderAsEmpty: true
209
- )
210
- switch Self.synthesizedTextCommitProgress(observedText: observedText, expectedText: expectedText) {
213
+ return length
214
+ }
215
+
216
+
217
+
218
+
219
+
220
+
221
+ enum SynthesizedTextCommitOutcome: Equatable {
222
+
223
+
224
+
225
+
226
+
227
+ case settled
228
+
229
+ case unobservable
230
+
231
+ case notObserved
232
+ }
233
+
234
+
235
+
236
+
237
+
238
+
239
+
240
+
241
+
242
+ static func awaitSynthesizedCommitOutcome(
243
+ expectedText: String,
244
+ placeholder: String?,
245
+ stallBudget: TimeInterval = TextEntryTiming.synthesizedCommitStallTimeout,
246
+ ceiling: TimeInterval = TextEntryTiming.synthesizedCommitCeiling,
247
+ now: () -> Date = { Date() },
248
+ observe: () -> String?,
249
+ waitForNextObservation: () -> Void
250
+ ) -> SynthesizedTextCommitOutcome {
251
+
252
+
253
+
254
+ if Self.textMatchesPlaceholder(expectedText, placeholder: placeholder) {
255
+ return .notObserved
256
+ }
257
+ var deadline = SynthesizedCommitDeadline(startedAt: now(), stallBudget: stallBudget, ceiling: ceiling)
258
+
259
+
260
+
261
+ while true {
262
+ let observedText = observe()
263
+ switch synthesizedTextCommitProgress(observedText: observedText, expectedText: expectedText) {
211
264
  case .committed, .diverged:
212
- return
265
+ return .settled
213
266
  case .pending:
214
- sleepFor(TextEntryTiming.pollInterval)
267
+
268
+
269
+ let sampledAt = now()
270
+ deadline.record(
271
+ expectedPrefixLength: Self.commonPrefixLength(observedText ?? "", expectedText),
272
+ at: sampledAt
273
+ )
274
+ if deadline.isExpired(at: sampledAt) { return .notObserved }
275
+ waitForNextObservation()
276
+ }
277
+ }
278
+ }
279
+
280
+
281
+
282
+
283
+ static func textEntryFailure(
284
+ forCommitOutcome outcome: SynthesizedTextCommitOutcome
285
+ ) -> TextEntryFailure? {
286
+ switch outcome {
287
+ case .settled, .unobservable:
288
+ return nil
289
+ case .notObserved:
290
+ return .commitNotObserved
291
+ }
292
+ }
293
+
294
+
295
+
296
+
297
+
298
+
299
+
300
+
301
+
302
+
303
+
304
+
305
+
306
+
307
+
308
+
309
+
310
+ static func awaitSynthesizedReplacementCommitOutcome(
311
+ expectedText: String,
312
+ placeholder: String?,
313
+ stallBudget: TimeInterval = TextEntryTiming.synthesizedCommitStallTimeout,
314
+ ceiling: TimeInterval = TextEntryTiming.synthesizedCommitCeiling,
315
+ now: () -> Date = { Date() },
316
+ observe: () -> String?,
317
+ waitForNextObservation: () -> Void
318
+ ) -> SynthesizedTextCommitOutcome {
319
+ if Self.textMatchesPlaceholder(expectedText, placeholder: placeholder) {
320
+ return .notObserved
321
+ }
322
+ var deadline = SynthesizedCommitDeadline(startedAt: now(), stallBudget: stallBudget, ceiling: ceiling)
323
+ while true {
324
+ let observedText = observe()
325
+ if observedText == expectedText {
326
+ return .settled
215
327
  }
328
+
329
+
330
+
331
+ let sampledAt = now()
332
+ deadline.record(
333
+ expectedPrefixLength: Self.commonPrefixLength(observedText ?? "", expectedText),
334
+ at: sampledAt
335
+ )
336
+ if deadline.isExpired(at: sampledAt) { return .notObserved }
337
+ waitForNextObservation()
216
338
  }
217
339
  }
218
340
 
@@ -1,7 +1,8 @@
1
1
  import XCTest
2
+ import AgentDeviceSnapshotPresentation
2
3
 
3
4
  extension RunnerTests {
4
- // MARK: - Blocking System Modal Snapshot
5
+
5
6
 
6
7
  func blockingSystemAlertSnapshot(deadline: Date = .distantFuture) -> DataPayload? {
7
8
  #if os(macOS)
@@ -41,8 +42,8 @@ extension RunnerTests {
41
42
  nodes.append(actionNode)
42
43
  }
43
44
 
44
- // Action controls are the interaction contract. Informative text is useful context, but
45
- // must not make a usable modal miss the bounded system-modal probe deadline.
45
+
46
+
46
47
  let contentDeadline = deadline.addingTimeInterval(-0.5)
47
48
  for content in informativeElements(in: modal, deadline: contentDeadline) {
48
49
  guard Date() < contentDeadline,
@@ -85,7 +86,7 @@ extension RunnerTests {
85
86
  }
86
87
  }
87
88
 
88
- // Don't start the second (sheet) enumeration once the shared probe deadline is spent (#1244).
89
+
89
90
  if Date() >= deadline {
90
91
  NSLog("AGENT_DEVICE_RUNNER_SYSTEM_MODAL_PROBE_DEADLINE stage=sheets")
91
92
  return nil
@@ -1,12 +1,13 @@
1
1
  import XCTest
2
2
 
3
- // Text entry & keyboard-readiness for the runner: the focus -> type -> verify -> repair
4
- // pipeline, readiness polling, and field clearing. Behavior-preserving extraction from
5
- // RunnerTests+Interaction.swift (no logic changes) to keep that file navigable.
3
+
4
+
5
+
6
6
  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
+
49
+
50
+
51
+ static let synthesizedCommitStallTimeout: TimeInterval = 3.0
52
+
53
+
54
+
55
+ static let synthesizedCommitCeiling: TimeInterval = 10.0
56
+ static let synthesizedCommitPollInterval: TimeInterval = 0.2
44
57
  }
45
58
 
46
59
  struct TextEntryResult {
@@ -101,14 +114,14 @@ extension RunnerTests {
101
114
  }
102
115
 
103
116
  func clearTextInput(_ element: XCUIElement) {
104
- // Skip the clear (delete burst + moveCaretToEnd edge-tap) ONLY when we can confirm the
105
- // field is empty. Why skip: the edge-tap computes a point from the element frame, which can
106
- // be stale after the field repositions on focus (e.g. the Settings search bar jumps
107
- // bottom->top and reveals a "Suggestions" list) — tapping there navigates away instead of
108
- // clearing; and replacing into an already-empty field is a no-op anyway.
109
- // editableTextValue returns nil for secure (and unknown) fields, where we CANNOT confirm
110
- // emptiness — those must still be cleared, or replace would concatenate stale + new text.
111
- // So distinguish nil (clear) from "" (skip).
117
+
118
+
119
+
120
+
121
+
122
+
123
+
124
+
112
125
  if let existing = editableTextValue(for: element, treatingPlaceholderAsEmpty: true),
113
126
  existing.isEmpty {
114
127
  return
@@ -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":