agent-device 0.21.14 → 0.21.16

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 (310) hide show
  1. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.21.14.apk → agent-device-android-ime-helper-0.21.16.apk} +0 -0
  2. package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.16.apk.sha256 +1 -0
  3. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.21.14.manifest.json → agent-device-android-ime-helper-0.21.16.manifest.json} +4 -4
  4. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.16.apk +0 -0
  5. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.16.apk.sha256 +1 -0
  6. package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.21.14.manifest.json → agent-device-android-snapshot-helper-0.21.16.manifest.json} +6 -6
  7. package/apple/snapshot-bridge/SnapshotBridgeRuntime.m +11 -1
  8. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunner/AgentDeviceRunnerApp.m +84 -2
  9. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.m +4 -2
  10. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerMainOwnedState.swift +27 -0
  11. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedTextEntry.h +10 -4
  12. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedTextEntry.m +21 -5
  13. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSystemSurfaceHostPolicy.swift +0 -9
  14. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Alert.swift +40 -5
  15. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandDispatch.swift +179 -91
  16. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +5 -4
  17. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandJournal.swift +1 -1
  18. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+GestureExecution.swift +1 -0
  19. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Lifecycle.swift +36 -49
  20. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+MainThreadWork.swift +11 -15
  21. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +136 -29
  22. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Navigation.swift +4 -2
  23. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+PrivateAXPresentation.swift +3 -0
  24. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScreenRecorder.swift +6 -5
  25. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScrollDragExecution.swift +4 -0
  26. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SequenceExecution.swift +2 -0
  27. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +8 -4
  28. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotAcquisition.swift +56 -14
  29. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCapturePlan.swift +18 -8
  30. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCaptureTarget.swift +9 -7
  31. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotExecution.swift +5 -4
  32. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotTiming.swift +2 -2
  33. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedGesturePolicy.swift +4 -1
  34. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedInteraction.swift +5 -1
  35. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedTextEntry.swift +245 -41
  36. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntry.swift +47 -2
  37. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntryFocus.swift +7 -4
  38. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextTyping.swift +115 -111
  39. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Transport.swift +17 -10
  40. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TvRemote.swift +8 -9
  41. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TypeExecution.swift +2 -1
  42. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +12 -11
  43. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotGeometry.swift +7 -4
  44. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotModels.swift +15 -8
  45. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotPresentationProjection.swift +1 -0
  46. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotVisibilityFold.swift +26 -9
  47. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotVisibilityFoldProjection.swift +1 -0
  48. package/dist/src/adapter.js +1 -1
  49. package/dist/src/adb-executor.js +1 -1
  50. package/dist/src/adb-failure.js +1 -1
  51. package/dist/src/adb-transport.js +1 -1
  52. package/dist/src/agent-browser-provider.js +1 -1
  53. package/dist/src/agent-browser-tool.js +1 -1
  54. package/dist/src/agent-device-client.js +2 -2
  55. package/dist/src/ai-sdk.js +1 -1
  56. package/dist/src/alert-runtime.js +1 -1
  57. package/dist/src/android-adb.d.ts +1 -8
  58. package/dist/src/android-input-ownership.js +1 -1
  59. package/dist/src/android-snapshot-quality.js +1 -0
  60. package/dist/src/android-system-chrome.js +1 -1
  61. package/dist/src/android.js +1 -1
  62. package/dist/src/app-catalog.js +1 -1
  63. package/dist/src/app-deployment-resolution.js +1 -1
  64. package/dist/src/app-inventory.js +1 -1
  65. package/dist/src/app-launch.js +1 -1
  66. package/dist/src/app-lifecycle.js +1 -1
  67. package/dist/src/app-lifecycle2.js +1 -1
  68. package/dist/src/app-log-files.js +1 -1
  69. package/dist/src/app-log-runtime2.js +1 -1
  70. package/dist/src/app-resolution.js +2 -2
  71. package/dist/src/apple-runner-platform.js +1 -1
  72. package/dist/src/application-lifecycle-interaction.js +1 -1
  73. package/dist/src/archive.js +1 -1
  74. package/dist/src/artifact-download.js +1 -1
  75. package/dist/src/auth-session.js +1 -1
  76. package/dist/src/auth.js +1 -1
  77. package/dist/src/backend-snapshot-options.js +1 -1
  78. package/dist/src/boot-diagnostics.js +1 -1
  79. package/dist/src/capture-disclosure.js +1 -1
  80. package/dist/src/capture.js +1 -1
  81. package/dist/src/cli-config.js +1 -1
  82. package/dist/src/cli-help.js +3 -2
  83. package/dist/src/cli.js +4 -4
  84. package/dist/src/click-button.js +1 -1
  85. package/dist/src/client-metro.js +1 -1
  86. package/dist/src/client-types.d.ts +103 -96
  87. package/dist/src/clipboard-shell-response.js +1 -1
  88. package/dist/src/command-schema.js +1 -1
  89. package/dist/src/command-tools.js +3 -3
  90. package/dist/src/connection-runtime.js +1 -1
  91. package/dist/src/connection.js +5 -5
  92. package/dist/src/contracts.d.ts +1 -1
  93. package/dist/src/contracts.js +1 -1
  94. package/dist/src/daemon-client-lease-beat.js +1 -0
  95. package/dist/src/daemon-client-lifecycle.js +1 -1
  96. package/dist/src/daemon-client-transport.js +1 -0
  97. package/dist/src/daemon-shutdown-report.js +1 -1
  98. package/dist/src/daemon.js +1 -1
  99. package/dist/src/debug-symbols-facade.js +1 -1
  100. package/dist/src/device-claim-inspection.js +1 -1
  101. package/dist/src/device-claim-rule.js +2 -2
  102. package/dist/src/device-input-state.d.ts +15 -2
  103. package/dist/src/device-inventory-context.js +1 -1
  104. package/dist/src/device-rotation.js +1 -1
  105. package/dist/src/device-selection-resolver.js +1 -1
  106. package/dist/src/device-session.js +1 -1
  107. package/dist/src/device-shell.js +1 -1
  108. package/dist/src/device.js +1 -1
  109. package/dist/src/device2.js +1 -1
  110. package/dist/src/devicectl.js +1 -1
  111. package/dist/src/diagnostics.js +1 -1
  112. package/dist/src/dispatch-resolve.js +1 -1
  113. package/dist/src/doctor-facade.js +1 -1
  114. package/dist/src/doctor2.js +1 -1
  115. package/dist/src/durable-resource.js +1 -1
  116. package/dist/src/engine-eval-script.js +1 -1
  117. package/dist/src/error.js +1 -1
  118. package/dist/src/exec.js +1 -1
  119. package/dist/src/file.js +1 -1
  120. package/dist/src/find.js +1 -1
  121. package/dist/src/format.js +1 -1
  122. package/dist/src/gesture-admission.js +1 -1
  123. package/dist/src/gesture-plan-types.d.ts +1 -1
  124. package/dist/src/gesture-plan.js +1 -1
  125. package/dist/src/gesture-runtime.js +1 -1
  126. package/dist/src/harmonyos.js +1 -1
  127. package/dist/src/helper.js +2 -2
  128. package/dist/src/host.js +1 -1
  129. package/dist/src/human-control-contract.js +1 -1
  130. package/dist/src/human-control.js +1 -1
  131. package/dist/src/index.d.ts +7 -3
  132. package/dist/src/index.js +1 -1
  133. package/dist/src/input-actions.js +1 -1
  134. package/dist/src/input-actions2.js +1 -1
  135. package/dist/src/install-artifact-facade.js +1 -1
  136. package/dist/src/install-source-network-transport.js +1 -1
  137. package/dist/src/install-source2.js +1 -1
  138. package/dist/src/interaction-positionals.js +1 -1
  139. package/dist/src/interaction-ref-policy.js +1 -1
  140. package/dist/src/interaction2.js +1 -1
  141. package/dist/src/interactor-operation-binding.js +1 -1
  142. package/dist/src/interactor.js +5 -4
  143. package/dist/src/interactor2.js +1 -1
  144. package/dist/src/interactors.js +1 -1
  145. package/dist/src/internal/bin.js +1 -1
  146. package/dist/src/internal/daemon.js +2 -2
  147. package/dist/src/internal/png-worker.js +1 -1
  148. package/dist/src/internal/run-script-http-child.js +1 -1
  149. package/dist/src/inventory.js +1 -1
  150. package/dist/src/inventory2.js +1 -1
  151. package/dist/src/inventory3.js +1 -1
  152. package/dist/src/inventory5.js +1 -1
  153. package/dist/src/ios-snapshot-acquisition.js +1 -1
  154. package/dist/src/ios-snapshot-engine.js +1 -0
  155. package/dist/src/ios-snapshot-runtime.js +1 -1
  156. package/dist/src/lease-context.js +1 -1
  157. package/dist/src/lease-scope.js +1 -1
  158. package/dist/src/lease.js +1 -1
  159. package/dist/src/limrun-download-file.js +1 -1
  160. package/dist/src/limrun-runtime-dependencies.js +1 -1
  161. package/dist/src/limrun.d.ts +16 -3
  162. package/dist/src/linux.js +1 -1
  163. package/dist/src/location-coordinates.js +1 -1
  164. package/dist/src/managed-device-scope.js +1 -1
  165. package/dist/src/mechanics.js +17 -17
  166. package/dist/src/mobile-snapshot-semantics.js +1 -1
  167. package/dist/src/multitouch-support.js +1 -1
  168. package/dist/src/native-build-cache.js +1 -1
  169. package/dist/src/observation.js +1 -1
  170. package/dist/src/overlay.js +3 -0
  171. package/dist/src/parameterized-recorded-fill.js +1 -1
  172. package/dist/src/perf-facade.js +1 -1
  173. package/dist/src/perf-runtime-operation-builder.js +1 -1
  174. package/dist/src/perf-runtime-plan.js +1 -1
  175. package/dist/src/perf-target.js +1 -1
  176. package/dist/src/perf.js +1 -1
  177. package/dist/src/physical-device-control.js +1 -1
  178. package/dist/src/platform-request-scope.js +1 -1
  179. package/dist/src/platform-runtime-managed-owner.js +1 -1
  180. package/dist/src/platform-runtime-open-target.js +1 -1
  181. package/dist/src/platform-runtime-operation-host.js +4 -4
  182. package/dist/src/platform-runtime-runtime-hints.js +1 -1
  183. package/dist/src/platform-runtime-screen-recording-apple-simulator-host.js +1 -1
  184. package/dist/src/platform-runtime-screen-recording-finalizer-host.js +1 -1
  185. package/dist/src/platform-runtime-screen-recording-process-host.js +1 -1
  186. package/dist/src/platform-runtime-unavailable.js +1 -1
  187. package/dist/src/platform-runtime.js +1 -1
  188. package/dist/src/platform-runtime2.js +1 -1
  189. package/dist/src/png-crop-bytes.js +1 -1
  190. package/dist/src/png-size.js +1 -1
  191. package/dist/src/png-worker-client.js +1 -1
  192. package/dist/src/png.js +1 -1
  193. package/dist/src/prepare-kind.js +1 -1
  194. package/dist/src/provider-session-ownership.js +1 -1
  195. package/dist/src/provider-webdriver.js +1 -1
  196. package/dist/src/proxy.js +1 -1
  197. package/dist/src/quality-warnings.js +1 -1
  198. package/dist/src/react-native.js +1 -1
  199. package/dist/src/record-runtime.js +1 -1
  200. package/dist/src/recording.js +1 -1
  201. package/dist/src/rect-center.js +1 -1
  202. package/dist/src/register-builtins.js +1 -1
  203. package/dist/src/registry.js +36 -36
  204. package/dist/src/remote-config2.js +1 -1
  205. package/dist/src/replay-divergence.js +1 -1
  206. package/dist/src/replay.js +1 -1
  207. package/dist/src/reporting.js +1 -1
  208. package/dist/src/request-admission.js +1 -1
  209. package/dist/src/request-cancel.js +1 -1
  210. package/dist/src/request-runtime-binding.js +1 -1
  211. package/dist/src/result-serialization.js +1 -1
  212. package/dist/src/retry.js +1 -1
  213. package/dist/src/runner-artifact-manifest.js +1 -0
  214. package/dist/src/runner-cache-metadata.js +3 -2
  215. package/dist/src/runner-client.js +1 -1
  216. package/dist/src/runner-dev-tools-security.js +1 -1
  217. package/dist/src/runner-device-readiness.js +1 -1
  218. package/dist/src/runner-disposal.js +1 -1
  219. package/dist/src/runner-error-classification.js +3 -3
  220. package/dist/src/runner-operations-facade.js +1 -1
  221. package/dist/src/runner.js +1 -1
  222. package/dist/src/runtime-transport-hints.js +1 -1
  223. package/dist/src/runtime.js +1 -3
  224. package/dist/src/runtime10.js +2 -2
  225. package/dist/src/runtime2.js +4 -4
  226. package/dist/src/runtime3.js +1 -1
  227. package/dist/src/runtime4.js +5 -5
  228. package/dist/src/runtime5.js +1 -1
  229. package/dist/src/runtime6.js +1 -1
  230. package/dist/src/runtime7.js +1 -1
  231. package/dist/src/runtime8.js +1 -1
  232. package/dist/src/runtime9.js +1 -1
  233. package/dist/src/screen-recording-admission-ledger.js +1 -1
  234. package/dist/src/screen-recording-completion.js +1 -1
  235. package/dist/src/screen-recording-options.js +1 -1
  236. package/dist/src/screen-recording-resource-recovery.js +1 -1
  237. package/dist/src/screen-recording-transport.js +1 -1
  238. package/dist/src/screenshot-density.js +1 -1
  239. package/dist/src/screenshot-image.js +1 -1
  240. package/dist/src/screenshot-overlay-draw.js +1 -1
  241. package/dist/src/screenshot-runtime.js +1 -1
  242. package/dist/src/screenshot.js +1 -1
  243. package/dist/src/screenshot3.js +1 -1
  244. package/dist/src/script.js +1 -1
  245. package/dist/src/scroll-command.d.ts +13 -6
  246. package/dist/src/scroll-command.js +1 -1
  247. package/dist/src/scroll-gesture.js +1 -1
  248. package/dist/src/sdk-batch-runner.js +2 -2
  249. package/dist/src/sdk-batch.js +2 -2
  250. package/dist/src/sdk-contracts.d.ts +38 -10
  251. package/dist/src/sdk-contracts.js +2 -2
  252. package/dist/src/sdk-device.js +1 -1
  253. package/dist/src/sdk-finders.d.ts +1 -1
  254. package/dist/src/sdk-io.js +1 -1
  255. package/dist/src/sdk-remote-config.js +1 -1
  256. package/dist/src/sdk-selectors.d.ts +1 -1
  257. package/dist/src/selection.js +0 -0
  258. package/dist/src/selector-runtime.js +1 -1
  259. package/dist/src/server.js +1 -1
  260. package/dist/src/session-allocation.js +1 -1
  261. package/dist/src/session-routing.js +1 -1
  262. package/dist/src/session-runtime-admission.js +1 -1
  263. package/dist/src/session-snapshot.js +1 -1
  264. package/dist/src/session-store.js +3 -3
  265. package/dist/src/session.js +1 -1
  266. package/dist/src/session2.js +4 -4
  267. package/dist/src/settings.js +1 -1
  268. package/dist/src/settings2.js +1 -0
  269. package/dist/src/simctl.js +1 -1
  270. package/dist/src/simulator-boot.js +1 -1
  271. package/dist/src/simulator.js +1 -1
  272. package/dist/src/snapshot-capture-annotations.js +1 -1
  273. package/dist/src/snapshot-desktop-projection.js +1 -1
  274. package/dist/src/snapshot-runtime-binding.js +1 -1
  275. package/dist/src/snapshot-runtime.js +1 -1
  276. package/dist/src/snapshot-state.js +1 -1
  277. package/dist/src/snapshot2.js +1 -1
  278. package/dist/src/snapshot3.js +1 -1
  279. package/dist/src/snapshot4.js +1 -1
  280. package/dist/src/src.js +1 -1
  281. package/dist/src/src4.js +1 -1
  282. package/dist/src/src7.js +1 -1
  283. package/dist/src/src8.js +2 -2
  284. package/dist/src/stop-sequence.js +1 -1
  285. package/dist/src/string-enum.js +1 -1
  286. package/dist/src/surface-snapshot.js +1 -1
  287. package/dist/src/takeover.js +1 -1
  288. package/dist/src/text-entry-focus.js +1 -1
  289. package/dist/src/tool-provider.js +1 -1
  290. package/dist/src/tool-provider2.js +1 -1
  291. package/dist/src/touch-runtime.js +1 -1
  292. package/dist/src/transport.js +1 -1
  293. package/dist/src/tree.js +1 -1
  294. package/dist/src/tv-remote.js +1 -1
  295. package/dist/src/type-text-runtime.js +1 -1
  296. package/dist/src/verified-file.js +1 -1
  297. package/dist/src/video.js +1 -1
  298. package/dist/src/viewport.js +1 -1
  299. package/dist/src/web.js +1 -1
  300. package/dist/src/web2.js +1 -1
  301. package/dist/src/webdriver-source.js +1 -1
  302. package/package.json +2 -2
  303. package/server.json +2 -2
  304. package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.14.apk.sha256 +0 -1
  305. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.14.apk +0 -0
  306. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.14.apk.sha256 +0 -1
  307. package/dist/src/absence-observation-errors.js +0 -1
  308. package/dist/src/runner-presentation.js +0 -1
  309. package/dist/src/semantic-index.js +0 -1
  310. package/dist/src/snapshot-scope.js +0 -1
@@ -42,8 +42,16 @@ extension RunnerTests {
42
42
  #if os(iOS)
43
43
  let postStartedAt = Date()
44
44
  let result = replacingExistingText
45
- ? RunnerSynthesizedTextEntry.replaceText(withApplication: app, text: text)
46
- : RunnerSynthesizedTextEntry.synthesizeText(withApplication: app, text: text)
45
+ ? RunnerSynthesizedTextEntry.replaceText(
46
+ withApplication: app,
47
+ text: text,
48
+ charactersPerSecond: TextEntryTiming.synthesizedCharactersPerSecond
49
+ )
50
+ : RunnerSynthesizedTextEntry.synthesizeText(
51
+ withApplication: app,
52
+ text: text,
53
+ charactersPerSecond: TextEntryTiming.synthesizedCharactersPerSecond
54
+ )
47
55
  NSLog(
48
56
  "[DEBUG-1874] synthesize posted %d chars status=%d tookMs=%.0f",
49
57
  text.count, result.status.rawValue, postStartedAt.timeIntervalSinceNow * -1000
@@ -73,59 +81,255 @@ extension RunnerTests {
73
81
  #endif
74
82
  }
75
83
 
76
- struct SynthesizedReplacementStep: Equatable {
77
- let text: String
78
- let replacesExistingText: Bool
84
+
85
+
86
+
87
+
88
+ struct SynthesizedTextPlan: Equatable {
89
+
90
+ struct Step: Equatable {
91
+ let characterCount: Int
92
+
93
+
94
+ var replacesExistingText = false
95
+
96
+
97
+
98
+
99
+ var pauseAfterSeconds: TimeInterval = 0
100
+
101
+ var warmsUpField = false
102
+
103
+
104
+ var synthesizeCallCount: Int {
105
+ replacesExistingText ? 2 : 1
106
+ }
107
+ }
108
+
109
+ let steps: [Step]
110
+
111
+
112
+
113
+ static func spacedSteps(
114
+ characterCount: Int,
115
+ delaySeconds: Double,
116
+ replacesExistingTextOnFirstPost: Bool = false
117
+ ) -> [Step] {
118
+ (0..<characterCount).map { index in
119
+ Step(
120
+ characterCount: 1,
121
+ replacesExistingText: replacesExistingTextOnFirstPost && index == 0,
122
+ pauseAfterSeconds: index + 1 < characterCount ? delaySeconds : 0
123
+ )
124
+ }
125
+ }
126
+
127
+
128
+
129
+ var seconds: TimeInterval {
130
+ steps.reduce(0) { total, step in
131
+ total
132
+ + Double(step.characterCount) * TextEntryTiming.synthesizedCharacterInterval
133
+ + Double(step.synthesizeCallCount) * TextEntryTiming.synthesizeCallOverhead
134
+ + step.pauseAfterSeconds
135
+ }
136
+ }
137
+
138
+
139
+
140
+
141
+ var pacesEveryCharacter: Bool {
142
+ steps.count > 1 && !steps.contains(where: \.warmsUpField)
143
+ }
79
144
  }
80
145
 
81
- static func synthesizedReplacementSteps(
82
- text: String,
83
- delaySeconds: Double
84
- ) -> [SynthesizedReplacementStep] {
85
- let characters = Array(text)
86
- guard delaySeconds > 0, characters.count > 1 else {
87
- return [SynthesizedReplacementStep(text: text, replacesExistingText: true)]
146
+
147
+
148
+ static func synthesizedDeliveryIsSpaced(characterCount: Int, delaySeconds: Double) -> Bool {
149
+ delaySeconds > 0 && characterCount > 1
150
+ }
151
+
152
+
153
+
154
+
155
+
156
+
157
+
158
+
159
+
160
+ static func synthesizedTextPlan(
161
+ characterCount: Int,
162
+ delaySeconds: Double,
163
+ selectsExistingText: Bool,
164
+ peelsWarmupCharacter: Bool = false
165
+ ) -> SynthesizedTextPlan {
166
+ if synthesizedDeliveryIsSpaced(characterCount: characterCount, delaySeconds: delaySeconds) {
167
+ return SynthesizedTextPlan(
168
+ steps: SynthesizedTextPlan.spacedSteps(
169
+ characterCount: characterCount,
170
+ delaySeconds: delaySeconds,
171
+ replacesExistingTextOnFirstPost: selectsExistingText
172
+ )
173
+ )
88
174
  }
89
- return characters.enumerated().map { index, character in
90
- SynthesizedReplacementStep(
91
- text: String(character),
92
- replacesExistingText: index == 0
175
+ guard peelsWarmupCharacter && characterCount > 1 else {
176
+ return SynthesizedTextPlan(
177
+ steps: [
178
+ SynthesizedTextPlan.Step(
179
+ characterCount: characterCount,
180
+ replacesExistingText: selectsExistingText
181
+ ),
182
+ ]
93
183
  )
94
184
  }
185
+ return SynthesizedTextPlan(
186
+ steps: [
187
+ SynthesizedTextPlan.Step(
188
+ characterCount: 1,
189
+ pauseAfterSeconds: TextEntryTiming.pollInterval,
190
+ warmsUpField: true
191
+ ),
192
+ SynthesizedTextPlan.Step(characterCount: characterCount - 1),
193
+ ]
194
+ )
195
+ }
196
+
197
+
198
+
199
+ enum SynthesizedDeliveryBudget {
200
+ static func exceeds(_ plan: SynthesizedTextPlan) -> Bool {
201
+ plan.seconds > TextEntryTiming.synthesizedDeliveryCeiling
202
+ }
203
+
204
+
205
+
206
+
207
+ static func maxTextLength(delaySeconds: TimeInterval) -> Int {
208
+ var length = 1
209
+ while !exceeds(
210
+ synthesizedTextPlan(
211
+ characterCount: length + 1,
212
+ delaySeconds: delaySeconds,
213
+ selectsExistingText: true
214
+ )
215
+ ) {
216
+ length += 1
217
+ }
218
+ return length
219
+ }
220
+ }
221
+
222
+
223
+
224
+ enum SynthesizedStepDispatch {
225
+ case posted
226
+ case stop
227
+ }
228
+
229
+ struct SynthesizedPlanRun {
230
+ let postedCharacterCount: Int
231
+
232
+ let stoppedEarly: Bool
233
+ }
234
+
235
+
236
+
237
+
238
+
239
+
240
+
241
+
242
+
243
+
244
+
245
+ @MainActor
246
+ func runSynthesizedTextPlan(
247
+ _ plan: SynthesizedTextPlan,
248
+ text: String,
249
+ post: (_ characters: String, _ step: SynthesizedTextPlan.Step) -> SynthesizedStepDispatch,
250
+ waitAfterWarmupCharacter: (_ characters: String) -> Void,
251
+ didPostStep: (_ step: SynthesizedTextPlan.Step) -> Void = { _ in }
252
+ ) -> SynthesizedPlanRun {
253
+ let characters = Array(text)
254
+ var postedCount = 0
255
+ for step in plan.steps {
256
+ let nextCount = postedCount + step.characterCount
257
+ if post(String(characters[postedCount..<nextCount]), step) == .stop {
258
+
259
+ return SynthesizedPlanRun(postedCharacterCount: postedCount, stoppedEarly: true)
260
+ }
261
+ postedCount = nextCount
262
+ didPostStep(step)
263
+ if step.warmsUpField {
264
+ waitAfterWarmupCharacter(String(characters[postedCount - step.characterCount..<postedCount]))
265
+ } else if step.pauseAfterSeconds > 0 {
266
+ sleepFor(step.pauseAfterSeconds)
267
+ }
268
+ }
269
+ return SynthesizedPlanRun(postedCharacterCount: postedCount, stoppedEarly: false)
95
270
  }
96
271
 
272
+ @MainActor
97
273
  func runSynthesizedReplacementRoute(
98
274
  _ request: SynthesizedReplacementRequest
99
275
  ) -> SynthesizedReplacementRouteOutcome {
100
276
  #if os(iOS)
101
277
  NSLog("AGENT_DEVICE_RUNNER_TEXT_ENTRY_ROUTE route=synthesized-first-responder-replacement")
102
- let steps = Self.synthesizedReplacementSteps(
103
- text: request.text,
104
- delaySeconds: request.delaySeconds
278
+ let plan = Self.synthesizedTextPlan(
279
+ characterCount: request.text.count,
280
+ delaySeconds: request.delaySeconds,
281
+ selectsExistingText: true
105
282
  )
106
- for (index, step) in steps.enumerated() {
107
- switch request.synthesizer.enterText(
108
- app: request.app,
109
- text: step.text,
110
- replacingExistingText: step.replacesExistingText
111
- ) {
112
- case .fallback:
113
- NSLog("AGENT_DEVICE_RUNNER_TEXT_ENTRY_ROUTE route=verified-fallback reason=synthesis-unavailable")
114
- guard let point = request.target.refreshPoint else { return .notApplicable }
115
- return .fallback(
116
- focusTextInputForTextEntry(app: request.app, x: point.x, y: point.y)
283
+ if SynthesizedDeliveryBudget.exceeds(plan) {
284
+ NSLog(
285
+ "AGENT_DEVICE_RUNNER_TEXT_ENTRY_ROUTE route=synthesized-first-responder-replacement "
286
+ + "reason=delivery-budget-refused chars=%d budgetChars=%d",
287
+ request.text.count,
288
+ SynthesizedDeliveryBudget.maxTextLength(delaySeconds: request.delaySeconds)
289
+ )
290
+ return .completed(
291
+ TextEntryResult(
292
+ verified: nil,
293
+ repaired: false,
294
+ expectedText: request.text,
295
+ observedText: nil,
296
+ textEntryRoute: "synthesized-first-responder-replacement",
297
+ failure: .synthesisBudgetExceeded
117
298
  )
118
- case .raise(let message):
119
- NSException(
120
- name: NSExceptionName.internalInconsistencyException,
121
- reason: message ?? "private XCTest text synthesis failed"
122
- ).raise()
123
- case .continueTyping:
124
- break
125
- }
126
- if index + 1 < steps.count {
127
- sleepFor(request.delaySeconds)
128
- }
299
+ )
300
+ }
301
+
302
+
303
+ let synthesisAvailable = runSynthesizedTextPlan(
304
+ plan,
305
+ text: request.text,
306
+ post: { slice, step in
307
+ switch request.synthesizer.enterText(
308
+ app: request.app,
309
+ text: slice,
310
+ replacingExistingText: step.replacesExistingText
311
+ ) {
312
+ case .continueTyping:
313
+ return .posted
314
+ case .fallback:
315
+ return .stop
316
+ case .raise(let message):
317
+ NSException(
318
+ name: NSExceptionName.internalInconsistencyException,
319
+ reason: message ?? "private XCTest text synthesis failed"
320
+ ).raise()
321
+ return .stop
322
+ }
323
+ },
324
+
325
+ waitAfterWarmupCharacter: { _ in }
326
+ )
327
+ if synthesisAvailable.stoppedEarly {
328
+ NSLog("AGENT_DEVICE_RUNNER_TEXT_ENTRY_ROUTE route=verified-fallback reason=synthesis-unavailable")
329
+ guard let point = request.target.refreshPoint else { return .notApplicable }
330
+ return .fallback(
331
+ focusTextInputForTextEntry(app: request.app, x: point.x, y: point.y)
332
+ )
129
333
  }
130
334
 
131
335
 
@@ -8,6 +8,7 @@ extension RunnerTests {
8
8
  case notFocused = "TEXT_INPUT_NOT_FOCUSED"
9
9
  case synthesisUnavailable = "TEXT_INPUT_SYNTHESIS_UNAVAILABLE"
10
10
  case commitNotObserved = "TEXT_INPUT_COMMIT_NOT_OBSERVED"
11
+ case synthesisBudgetExceeded = "TEXT_INPUT_SYNTHESIS_BUDGET_EXCEEDED"
11
12
 
12
13
  var message: String {
13
14
  switch self {
@@ -17,6 +18,8 @@ extension RunnerTests {
17
18
  return "Reliable text synthesis is unavailable while the software keyboard is hidden."
18
19
  case .commitNotObserved:
19
20
  return "The runner could not confirm the typed text reached the field."
21
+ case .synthesisBudgetExceeded:
22
+ return "The text is longer than one runner command can type at this pace."
20
23
  }
21
24
  }
22
25
 
@@ -27,7 +30,17 @@ extension RunnerTests {
27
30
  case .synthesisUnavailable:
28
31
  return "Show the software keyboard, then retry type."
29
32
  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."
33
+ 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 \(TextEntryTiming.recoveryDelayMilliseconds). Do not use type, which appends to whatever committed."
34
+ case .synthesisBudgetExceeded:
35
+ let recoveryDelay = TextEntryTiming.recoveryDelayMilliseconds
36
+ let recoveryBudget = SynthesizedDeliveryBudget.maxTextLength(
37
+ delaySeconds: Double(recoveryDelay) / 1000
38
+ )
39
+
40
+
41
+
42
+
43
+ return "Fill at most \(SynthesizedDeliveryBudget.maxTextLength(delaySeconds: 0)) characters per command without --delay-ms and append the rest with separate type commands. --delay-ms lowers the limit: each character then gets its own synthesize call and each gap pays the delay, so \(recoveryDelay) ms fits \(recoveryBudget). This route is chosen when the accessibility channel is already degraded, so a longer timeout does not help."
31
44
  }
32
45
  }
33
46
  }
@@ -51,8 +64,40 @@ extension RunnerTests {
51
64
  static let synthesizedCommitStallTimeout: TimeInterval = 3.0
52
65
 
53
66
 
54
-
55
67
  static let synthesizedCommitCeiling: TimeInterval = 10.0
68
+
69
+
70
+ static let synthesizedReplacementFocusAllowance: TimeInterval = 2.0
71
+
72
+
73
+
74
+
75
+ static let synthesizedDeliveryCeiling: TimeInterval = RunnerTests.mainThreadExecutionTimeout
76
+ - synthesizedReplacementFocusAllowance
77
+ - synthesizedCommitCeiling
78
+
79
+
80
+
81
+
82
+
83
+
84
+
85
+
86
+
87
+
88
+
89
+
90
+
91
+
92
+ static let synthesizedCharactersPerSecond: UInt = 12
93
+
94
+ static let synthesizedCharacterInterval: TimeInterval = 1.0 / Double(synthesizedCharactersPerSecond)
95
+
96
+
97
+
98
+ static let synthesizeCallOverhead: TimeInterval = 0.15
99
+
100
+ static let recoveryDelayMilliseconds = 80
56
101
  static let synthesizedCommitPollInterval: TimeInterval = 0.2
57
102
  }
58
103
 
@@ -6,6 +6,7 @@ import XCTest
6
6
 
7
7
 
8
8
  extension RunnerTests {
9
+ @MainActor
9
10
  func rememberTextEntryTap(_ element: XCUIElement?) {
10
11
  guard let element, isTextEntryElement(element) else {
11
12
  clearRememberedTextEntryTap()
@@ -13,8 +14,8 @@ extension RunnerTests {
13
14
  }
14
15
  textEntryTapWitness = TextEntryTapWitness(
15
16
  element: element,
16
- bundleId: currentBundleId,
17
- processIdentifier: currentAppProcessIdentifier
17
+ bundleId: mainOwned.bundleId,
18
+ processIdentifier: mainOwned.processIdentifier
18
19
  )
19
20
  }
20
21
 
@@ -22,6 +23,7 @@ extension RunnerTests {
22
23
  textEntryTapWitness = nil
23
24
  }
24
25
 
26
+ @MainActor
25
27
  private func rememberedTextEntryTarget() -> TextEntryTarget? {
26
28
  guard let witness = textEntryTapWitness else {
27
29
  return nil
@@ -30,8 +32,8 @@ extension RunnerTests {
30
32
 
31
33
  clearRememberedTextEntryTap()
32
34
  guard witness.matches(
33
- bundleId: currentBundleId,
34
- processIdentifier: currentAppProcessIdentifier
35
+ bundleId: mainOwned.bundleId,
36
+ processIdentifier: mainOwned.processIdentifier
35
37
  ) else {
36
38
  return nil
37
39
  }
@@ -80,6 +82,7 @@ extension RunnerTests {
80
82
  #endif
81
83
  }
82
84
 
85
+ @MainActor
83
86
  func focusTextInputForTextEntry(app: XCUIApplication, x: Double?, y: Double?) -> TextEntryTarget {
84
87
  guard let x, let y else {
85
88
  let softwareKeyboardVisible = isKeyboardVisible(app: app)