agent-device 0.21.11 → 0.21.13

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 (201) hide show
  1. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.21.11.apk → agent-device-android-ime-helper-0.21.13.apk} +0 -0
  2. package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.13.apk.sha256 +1 -0
  3. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.21.11.manifest.json → agent-device-android-ime-helper-0.21.13.manifest.json} +4 -4
  4. package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.21.11.apk → agent-device-android-snapshot-helper-0.21.13.apk} +0 -0
  5. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.13.apk.sha256 +1 -0
  6. package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.21.11.manifest.json → agent-device-android-snapshot-helper-0.21.13.manifest.json} +6 -6
  7. package/apple/fold-helper/Fold.m +97 -0
  8. package/apple/macos-helper/Package.swift +3 -6
  9. package/apple/macos-helper/Sources/AgentDeviceMacOSHelper/main.swift +3 -8
  10. package/apple/macos-helper/Tests/AgentDeviceMacOSHelperTests/ScreenshotResponseTests.swift +15 -0
  11. package/apple/snapshot-bridge/SnapshotBridgeRuntime.m +47 -1
  12. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunner/AgentDeviceRunnerApp.m +99 -42
  13. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunner/Info.plist +23 -0
  14. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/project.pbxproj +17 -0
  15. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAppScreenCapture.swift +21 -13
  16. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerScrollViewportPolicy.swift +1 -162
  17. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.h +0 -9
  18. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.m +0 -56
  19. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSystemSurfaceHostPolicy.swift +0 -48
  20. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTapPointPolicy.swift +0 -50
  21. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+AXSnapshotFallback.swift +22 -568
  22. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Alert.swift +21 -9
  23. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+BlockingSystemModalResolution.swift +0 -36
  24. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandDispatch.swift +630 -0
  25. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +18 -2236
  26. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandJournal.swift +1 -338
  27. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Environment.swift +0 -12
  28. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+GestureExecution.swift +165 -0
  29. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +0 -24
  30. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Keyboard.swift +0 -142
  31. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Lifecycle.swift +90 -39
  32. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+MainThreadWork.swift +78 -106
  33. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +23 -2
  34. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Navigation.swift +147 -95
  35. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+PrivateAXPresentation.swift +0 -209
  36. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScreenRecorder.swift +86 -10
  37. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScrollDragExecution.swift +287 -0
  38. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScrollGesture.swift +1 -200
  39. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SequenceExecution.swift +0 -185
  40. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +75 -285
  41. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotAcquisition.swift +23 -10
  42. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotBackendCapabilities.swift +0 -78
  43. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCapturePlan.swift +83 -578
  44. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCaptureTarget.swift +106 -0
  45. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotExecution.swift +142 -0
  46. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotTiming.swift +49 -78
  47. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedCommitDeadline.swift +0 -44
  48. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedGesturePolicy.swift +27 -97
  49. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedInteraction.swift +1 -11
  50. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedTextEntry.swift +11 -87
  51. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SystemModal.swift +2 -10
  52. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntry.swift +1 -1
  53. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntryFocus.swift +0 -35
  54. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextInputCandidatePolicy.swift +0 -31
  55. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextTyping.swift +2 -18
  56. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Transport.swift +34 -52
  57. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TvRemote.swift +0 -22
  58. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TypeExecution.swift +142 -0
  59. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +22 -6
  60. package/dist/apple/runner/AgentDeviceRunner/RecordingScripts/recording-overlay.swift +140 -20
  61. package/dist/src/absence-observation-errors.js +1 -1
  62. package/dist/src/adapter.js +1 -1
  63. package/dist/src/agent-device-client.js +1 -1
  64. package/dist/src/alert-runtime.js +1 -1
  65. package/dist/src/android-adb.d.ts +1 -1
  66. package/dist/src/app-launch.js +2 -2
  67. package/dist/src/app-log-runtime2.js +3 -3
  68. package/dist/src/app-resolution.js +2 -2
  69. package/dist/src/back-mode.js +1 -1
  70. package/dist/src/capture-disclosure.js +1 -1
  71. package/dist/src/capture.js +1 -1
  72. package/dist/src/cli-config.js +1 -1
  73. package/dist/src/cli-help.js +10 -7
  74. package/dist/src/cli-runner.js +1 -1
  75. package/dist/src/cli.js +1 -1
  76. package/dist/src/client-types.d.ts +5 -8
  77. package/dist/src/code-signature.js +1 -1
  78. package/dist/src/command-schema.js +3 -3
  79. package/dist/src/command-tools.js +3 -3
  80. package/dist/src/connection.js +1 -1
  81. package/dist/src/daemon-client-lifecycle.js +1 -1
  82. package/dist/src/daemon-resolution.js +1 -1
  83. package/dist/src/device-claim-rule.js +2 -2
  84. package/dist/src/device-input-state.d.ts +1 -1
  85. package/dist/src/device-rotation.js +1 -1
  86. package/dist/src/device-session.js +1 -1
  87. package/dist/src/devicectl.js +1 -1
  88. package/dist/src/display-inventory.js +1 -1
  89. package/dist/src/doctor-facade.js +1 -1
  90. package/dist/src/find.js +1 -1
  91. package/dist/src/gesture-plan-types.d.ts +38 -0
  92. package/dist/src/harmonyos.js +1 -1
  93. package/dist/src/helper.js +2 -2
  94. package/dist/src/host.js +1 -1
  95. package/dist/src/install-source-network-transport.js +1 -1
  96. package/dist/src/install-source2.js +1 -1
  97. package/dist/src/interaction-ref-policy.js +1 -1
  98. package/dist/src/interaction2.js +1 -1
  99. package/dist/src/interactor-operation-catalog.js +1 -1
  100. package/dist/src/interactor.js +4 -4
  101. package/dist/src/interactor2.js +1 -1
  102. package/dist/src/internal/bin.js +1 -1
  103. package/dist/src/internal/daemon.js +2 -2
  104. package/dist/src/inventory2.js +1 -1
  105. package/dist/src/ios-snapshot-acquisition.js +1 -1
  106. package/dist/src/ios-snapshot-runtime.js +1 -1
  107. package/dist/src/limrun-runtime-dependencies.js +1 -1
  108. package/dist/src/limrun.d.ts +435 -4
  109. package/dist/src/linux.js +1 -1
  110. package/dist/src/local-interactor-operation-set.js +1 -1
  111. package/dist/src/macos-facade.js +1 -1
  112. package/dist/src/managed-device-scope.js +1 -1
  113. package/dist/src/mechanics.js +4 -4
  114. package/dist/src/mobile-snapshot-semantics.js +1 -1
  115. package/dist/src/perf-facade.js +1 -2
  116. package/dist/src/perf-runtime-plan.js +1 -1
  117. package/dist/src/perf-target.js +2 -0
  118. package/dist/src/physical-device-apps.js +1 -0
  119. package/dist/src/physical-device-control.js +1 -1
  120. package/dist/src/platform-request-scope.js +1 -1
  121. package/dist/src/platform-runtime-operation-host.js +1 -1
  122. package/dist/src/platform-runtime-screen-recording-finalizer-host.js +1 -1
  123. package/dist/src/platform-runtime-unavailable.js +1 -1
  124. package/dist/src/platform-runtime.js +1 -1
  125. package/dist/src/platform-runtime2.js +1 -1
  126. package/dist/src/provider-webdriver.js +1 -1
  127. package/dist/src/proxy.js +1 -1
  128. package/dist/src/quality-warnings.js +1 -1
  129. package/dist/src/react-native.js +1 -1
  130. package/dist/src/record-runtime.js +1 -1
  131. package/dist/src/registry.js +35 -35
  132. package/dist/src/request-admission.js +1 -1
  133. package/dist/src/request-runtime-binding.js +1 -1
  134. package/dist/src/runner-client.js +1 -1
  135. package/dist/src/runner-dev-tools-security.js +1 -1
  136. package/dist/src/runner-device-readiness.js +1 -1
  137. package/dist/src/runner-disposal.js +1 -1
  138. package/dist/src/runner-error-classification.js +3 -0
  139. package/dist/src/runner-operations-facade.js +1 -1
  140. package/dist/src/runner-presentation.js +1 -1
  141. package/dist/src/runner.js +1 -1
  142. package/dist/src/runtime.js +3 -3
  143. package/dist/src/runtime10.js +1 -1
  144. package/dist/src/runtime2.js +2 -2
  145. package/dist/src/runtime4.js +5 -5
  146. package/dist/src/runtime5.js +1 -1
  147. package/dist/src/runtime6.js +1 -1
  148. package/dist/src/runtime7.js +1 -1
  149. package/dist/src/runtime8.js +1 -1
  150. package/dist/src/runtime9.js +1 -1
  151. package/dist/src/screen-recording-admission-ledger.js +1 -1
  152. package/dist/src/screen-recording-resource-recovery.js +1 -1
  153. package/dist/src/screen-recording-transport.js +1 -1
  154. package/dist/src/screenshot-runtime.js +1 -1
  155. package/dist/src/screenshot3.js +1 -1
  156. package/dist/src/script.js +1 -1
  157. package/dist/src/scroll-command.d.ts +377 -0
  158. package/dist/src/scroll-command.js +1 -1
  159. package/dist/src/sdk-batch.js +2 -2
  160. package/dist/src/sdk-contracts.d.ts +9 -0
  161. package/dist/src/sdk-device.js +1 -1
  162. package/dist/src/sdk-selectors.d.ts +23 -715
  163. package/dist/src/selection.js +0 -0
  164. package/dist/src/selector-runtime.js +1 -1
  165. package/dist/src/semantic-index.js +1 -1
  166. package/dist/src/server.js +1 -1
  167. package/dist/src/session-observation.js +1 -1
  168. package/dist/src/session-snapshot.js +1 -1
  169. package/dist/src/session2.js +2 -2
  170. package/dist/src/simctl-facade.js +1 -1
  171. package/dist/src/simctl.js +1 -1
  172. package/dist/src/simulator-facade.js +1 -1
  173. package/dist/src/simulator-state.js +1 -1
  174. package/dist/src/simulator.js +1 -1
  175. package/dist/src/snapshot-runtime-binding.js +1 -1
  176. package/dist/src/snapshot-runtime.js +1 -1
  177. package/dist/src/snapshot-state.js +1 -1
  178. package/dist/src/snapshot4.js +1 -1
  179. package/dist/src/src7.js +1 -1
  180. package/dist/src/text-entry-focus.js +1 -1
  181. package/dist/src/tool-provider.js +1 -1
  182. package/dist/src/touch-runtime.js +1 -1
  183. package/dist/src/tree.js +1 -1
  184. package/dist/src/update-check.js +1 -1
  185. package/dist/src/version.js +1 -1
  186. package/dist/src/web2.js +1 -1
  187. package/package.json +4 -2
  188. package/server.json +2 -2
  189. package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.11.apk.sha256 +0 -1
  190. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.11.apk.sha256 +0 -1
  191. package/apple/macos-helper/Sources/AgentDeviceMacOSDeviceHub/DeviceHubPose.swift +0 -47
  192. package/apple/macos-helper/Sources/AgentDeviceMacOSDeviceHub/DeviceHubWindowInventory.swift +0 -65
  193. package/apple/macos-helper/Sources/AgentDeviceMacOSHelper/DeviceHubPose.swift +0 -225
  194. package/apple/macos-helper/Sources/AgentDeviceMacOSHelper/DeviceHubSidebar.swift +0 -57
  195. package/apple/macos-helper/Sources/AgentDeviceMacOSHelper/DeviceHubWindowDiscovery.swift +0 -108
  196. package/apple/macos-helper/Tests/AgentDeviceMacOSDeviceHubTests/DeviceHubPoseTests.swift +0 -30
  197. package/apple/macos-helper/Tests/AgentDeviceMacOSDeviceHubTests/DeviceHubWindowInventoryTests.swift +0 -65
  198. package/dist/src/app-inventory.d.ts +0 -4
  199. package/dist/src/apps-simctl.js +0 -1
  200. package/dist/src/cloud-artifacts.d.ts +0 -61
  201. package/dist/src/runner-contract.js +0 -3
@@ -132,7 +132,6 @@ extension RunnerTests {
132
132
 
133
133
 
134
134
 
135
-
136
135
  let commit = awaitSynthesizedReplacementCommit(
137
136
  app: request.app,
138
137
  target: request.target,
@@ -168,37 +167,16 @@ extension RunnerTests {
168
167
  !hasResolvedElement && hasRefreshPoint && xCTestChannelPenalized
169
168
  }
170
169
 
170
+
171
+
172
+
171
173
  static func shouldUseSynthesizedFirstResponderType(
172
174
  repairMode: TextTypingRepairMode,
175
+ text: String,
173
176
  fromTapWitness: Bool,
174
177
  softwareKeyboardVisible: Bool
175
178
  ) -> Bool {
176
- repairMode == .none && fromTapWitness && !softwareKeyboardVisible
177
- }
178
-
179
- enum SynthesizedTextCommitProgress: Equatable {
180
- case committed
181
- case pending
182
- case diverged
183
- }
184
-
185
-
186
-
187
-
188
-
189
-
190
-
191
- static func synthesizedTextCommitProgress(
192
- observedText: String?,
193
- expectedText: String
194
- ) -> SynthesizedTextCommitProgress {
195
- guard let observedText else {
196
- return .diverged
197
- }
198
- if observedText == expectedText {
199
- return .committed
200
- }
201
- return expectedText.hasPrefix(observedText) ? .pending : .diverged
179
+ repairMode == .none && text == "\n" && fromTapWitness && !softwareKeyboardVisible
202
180
  }
203
181
 
204
182
 
@@ -215,15 +193,8 @@ extension RunnerTests {
215
193
 
216
194
 
217
195
 
218
-
219
-
220
-
221
196
  enum SynthesizedTextCommitOutcome: Equatable {
222
197
 
223
-
224
-
225
-
226
-
227
198
  case settled
228
199
 
229
200
  case unobservable
@@ -233,53 +204,6 @@ extension RunnerTests {
233
204
 
234
205
 
235
206
 
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) {
264
- case .committed, .diverged:
265
- return .settled
266
- case .pending:
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
207
  static func textEntryFailure(
284
208
  forCommitOutcome outcome: SynthesizedTextCommitOutcome
285
209
  ) -> TextEntryFailure? {
@@ -301,12 +225,6 @@ extension RunnerTests {
301
225
 
302
226
 
303
227
 
304
-
305
-
306
-
307
-
308
-
309
-
310
228
  static func awaitSynthesizedReplacementCommitOutcome(
311
229
  expectedText: String,
312
230
  placeholder: String?,
@@ -316,10 +234,16 @@ extension RunnerTests {
316
234
  observe: () -> String?,
317
235
  waitForNextObservation: () -> Void
318
236
  ) -> SynthesizedTextCommitOutcome {
237
+
238
+
239
+
319
240
  if Self.textMatchesPlaceholder(expectedText, placeholder: placeholder) {
320
241
  return .notObserved
321
242
  }
322
243
  var deadline = SynthesizedCommitDeadline(startedAt: now(), stallBudget: stallBudget, ceiling: ceiling)
244
+
245
+
246
+
323
247
  while true {
324
248
  let observedText = observe()
325
249
  if observedText == expectedText {
@@ -69,15 +69,7 @@ extension RunnerTests {
69
69
  in springboard: XCUIApplication,
70
70
  deadline: Date = .distantFuture
71
71
  ) -> XCUIElement? {
72
- let disableSafeProbe = RunnerEnv.isTruthy("AGENT_DEVICE_RUNNER_DISABLE_SAFE_MODAL_PROBE")
73
- let queryElements: (() -> [XCUIElement]) -> [XCUIElement] = { fetch in
74
- if disableSafeProbe {
75
- return fetch()
76
- }
77
- return self.safeElementsQuery(fetch)
78
- }
79
-
80
- let alerts = queryElements {
72
+ let alerts = safeElementsQuery {
81
73
  springboard.alerts.allElementsBoundByIndex
82
74
  }
83
75
  for alert in alerts {
@@ -92,7 +84,7 @@ extension RunnerTests {
92
84
  return nil
93
85
  }
94
86
 
95
- let sheets = queryElements {
87
+ let sheets = safeElementsQuery {
96
88
  springboard.sheets.allElementsBoundByIndex
97
89
  }
98
90
  for sheet in sheets {
@@ -25,7 +25,7 @@ extension RunnerTests {
25
25
  case .notFocused:
26
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."
27
27
  case .synthesisUnavailable:
28
- return "Show the software keyboard, then retry type or fill."
28
+ return "Show the software keyboard, then retry type."
29
29
  case .commitNotObserved:
30
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."
31
31
  }
@@ -152,41 +152,6 @@ extension RunnerTests {
152
152
  )
153
153
  }
154
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
-
162
-
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
155
  private func textEntryRefreshPoint(for element: XCUIElement?) -> CGPoint? {
191
156
  guard let element else {
192
157
  return nil
@@ -13,35 +13,4 @@ func isCoordinateTextInputCandidate(
13
13
  && point.x <= frame.maxX + tolerance
14
14
  && point.y >= frame.minY - tolerance
15
15
  && point.y <= frame.maxY + tolerance
16
- }
17
-
18
- extension RunnerTests {
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
-
28
-
29
-
30
-
31
-
32
-
33
-
34
-
35
-
36
-
37
-
38
-
39
-
40
-
41
-
42
-
43
-
44
-
45
-
46
-
47
16
  }
@@ -77,6 +77,7 @@ extension RunnerTests {
77
77
  }
78
78
  let shouldUseSynthesizedFirstResponderType = Self.shouldUseSynthesizedFirstResponderType(
79
79
  repairMode: repairMode,
80
+ text: text,
80
81
  fromTapWitness: activeTarget.fromTapWitness,
81
82
  softwareKeyboardVisible: isKeyboardVisible(app: app)
82
83
  )
@@ -142,26 +143,9 @@ extension RunnerTests {
142
143
  }
143
144
  textEntryRoute = "synthesized-first-responder"
144
145
  NSLog("AGENT_DEVICE_RUNNER_TEXT_ENTRY_ROUTE route=synthesized-first-responder")
145
- let textBefore = editableTextValue(for: currentTarget, treatingPlaceholderAsEmpty: true)
146
146
  switch synthesizer.enterText(app: app, text: value, replacingExistingText: false) {
147
147
  case .continueTyping:
148
-
149
-
150
- let commit = awaitSynthesizedFirstResponderCommit(
151
- app: app,
152
- target: TextEntryTarget(
153
- element: currentTarget,
154
- refreshPoint: nil,
155
- prefersFocusedElement: false,
156
- fromTapWitness: true
157
- ),
158
- textBefore: textBefore,
159
- typedText: value
160
- )
161
-
162
-
163
-
164
- return (currentTarget, Self.textEntryFailure(forCommitOutcome: commit))
148
+ return (currentTarget, nil)
165
149
  case .fallback:
166
150
  return (nil, .synthesisUnavailable)
167
151
  case .raise(let message):
@@ -108,12 +108,8 @@ extension RunnerTests {
108
108
 
109
109
  do {
110
110
  let command = try JSONDecoder().decode(Command.self, from: body)
111
- if command.command == .status {
112
- completion((jsonResponse(status: 200, response: executeStatus(command: command)), false))
113
- return
114
- }
115
- if command.command == .uptime {
116
- completion((jsonResponse(status: 200, response: executeUptime()), false))
111
+ if let response = inlineResponse(for: command) {
112
+ completion((jsonResponse(status: 200, response: response), false))
117
113
  return
118
114
  }
119
115
 
@@ -127,10 +123,9 @@ extension RunnerTests {
127
123
  command.command.rawValue,
128
124
  command.commandId ?? ""
129
125
  )
130
- commandJournal.accept(command: command)
131
- commandExecutionQueue.async {
132
- do {
133
- let response = try self.executeAccepted(command: command)
126
+ enqueueAccepted(command: command) { result in
127
+ switch result {
128
+ case .success(let response):
134
129
  NSLog(
135
130
  "AGENT_DEVICE_RUNNER_COMMAND_COMPLETED command=%@ commandId=%@ ok=%d",
136
131
  command.command.rawValue,
@@ -142,7 +137,7 @@ extension RunnerTests {
142
137
  result: (self.jsonResponse(status: 200, response: response), command.command == .shutdown),
143
138
  completion: completion
144
139
  )
145
- } catch {
140
+ case .failure(let error):
146
141
  NSLog(
147
142
  "AGENT_DEVICE_RUNNER_COMMAND_FAILED command=%@ commandId=%@ error=%@",
148
143
  command.command.rawValue,
@@ -177,9 +172,35 @@ extension RunnerTests {
177
172
 
178
173
 
179
174
 
175
+ func inlineResponse(for command: Command) -> Response? {
176
+ switch command.command {
177
+ case .status:
178
+ return executeStatus(command: command)
179
+ case .uptime:
180
+ return executeUptime()
181
+ default:
182
+ return nil
183
+ }
184
+ }
185
+
186
+
187
+
188
+ func enqueueAccepted(
189
+ command: Command,
190
+ completion: @escaping (Result<Response, Error>) -> Void
191
+ ) {
192
+ commandJournal.accept(command: command)
193
+ commandExecutionQueue.async {
194
+ completion(Result { try self.executeAccepted(command: command) })
195
+ }
196
+ }
197
+
198
+
199
+
200
+
180
201
 
181
202
 
182
- private func attachToInFlightCommandIfNeeded(
203
+ func attachToInFlightCommandIfNeeded(
183
204
  command: Command,
184
205
  completion: @escaping ((data: Data, shouldFinish: Bool)) -> Void
185
206
  ) -> Bool {
@@ -200,7 +221,7 @@ extension RunnerTests {
200
221
  return false
201
222
  }
202
223
 
203
- private func deliverCommandResult(
224
+ func deliverCommandResult(
204
225
  command: Command,
205
226
  result: (data: Data, shouldFinish: Bool),
206
227
  completion: ((data: Data, shouldFinish: Bool)) -> Void
@@ -220,45 +241,6 @@ extension RunnerTests {
220
241
 
221
242
 
222
243
 
223
-
224
-
225
-
226
-
227
-
228
-
229
-
230
-
231
-
232
-
233
-
234
-
235
-
236
-
237
-
238
-
239
-
240
-
241
-
242
-
243
-
244
-
245
-
246
-
247
-
248
-
249
-
250
-
251
-
252
-
253
-
254
-
255
-
256
-
257
-
258
-
259
-
260
-
261
-
262
244
  private func jsonResponse(status: Int, response: Response) -> Data {
263
245
 
264
246
 
@@ -18,28 +18,6 @@ enum TvRemoteButton: String {
18
18
  }
19
19
 
20
20
  extension RunnerTests {
21
-
22
-
23
-
24
-
25
-
26
-
27
-
28
-
29
-
30
-
31
-
32
-
33
-
34
-
35
-
36
-
37
-
38
-
39
-
40
-
41
-
42
-
43
21
  func resolveTvRemoteDoublePressDelay() -> TimeInterval {
44
22
  guard
45
23
  let raw = ProcessInfo.processInfo.environment["AGENT_DEVICE_TV_REMOTE_DOUBLE_PRESS_DELAY_MS"],
@@ -0,0 +1,142 @@
1
+ import XCTest
2
+ import AgentDeviceSnapshotPresentation
3
+
4
+ extension RunnerTests {
5
+ func executeTypeCommand(activeApp: XCUIApplication, command: Command) -> Response {
6
+ guard let text = command.text else {
7
+ return Response(ok: false, error: ErrorPayload(message: "type requires text"))
8
+ }
9
+ let delaySeconds = Double(max(command.delayMs ?? 0, 0)) / 1000.0
10
+ let textEntryMode = resolveTextEntryMode(command)
11
+ let target: TextEntryTarget
12
+ var resolvedCoordinateContext: SynthesizedCoordinateContext?
13
+
14
+
15
+ let maestroNonHittableCoordinateFallbackUsed: Bool? =
16
+ command.allowNonHittableCoordinateFallback == true && command.x != nil && command.y != nil
17
+ ? true
18
+ : nil
19
+ let focusStartedAt = Date()
20
+ #if os(iOS)
21
+ let xCTestChannelPenalized = isSnapshotXCTestChannelPenalized(bundleId: currentBundleId)
22
+ var resolvedCoordinateTarget: TextEntryTarget?
23
+ if Self.shouldUseResolvedCoordinateTextEntryRoute(
24
+ repairMode: textEntryMode,
25
+ hasX: command.x != nil,
26
+ hasY: command.y != nil,
27
+ xCTestChannelPenalized: xCTestChannelPenalized
28
+ ), let x = command.x, let y = command.y {
29
+ let policyKind = SynthesizedGesturePolicyKind.coordinateTap
30
+ let context = synthesizedCoordinateContext(
31
+ app: activeApp,
32
+ policy: synthesizedGesturePolicy(policyKind)
33
+ )
34
+ let (_, outcome) = performGesture(activeApp, idleTimeout: false) {
35
+ synthesizedTapAt(app: activeApp, x: x, y: y, context: context)
36
+ }
37
+ if Self.shouldFallbackFromSynthesizedTextEntryFocus(outcome) {
38
+ logSynthesizedGesturePolicyDecision(
39
+ kind: policyKind,
40
+ context: context,
41
+ fallbackAttempted: true
42
+ )
43
+ } else {
44
+ logSynthesizedGesturePolicyDecision(
45
+ kind: policyKind,
46
+ context: context,
47
+ fallbackAttempted: false
48
+ )
49
+ resolvedCoordinateContext = context
50
+ resolvedCoordinateTarget = TextEntryTarget(
51
+ element: nil,
52
+ refreshPoint: CGPoint(x: x, y: y),
53
+ prefersFocusedElement: false
54
+ )
55
+ }
56
+ }
57
+ #else
58
+ let xCTestChannelPenalized = false
59
+ let resolvedCoordinateTarget: TextEntryTarget? = nil
60
+ #endif
61
+ if let resolvedCoordinateTarget {
62
+ target = resolvedCoordinateTarget
63
+ } else {
64
+ target = focusTextInputForTextEntry(app: activeApp, x: command.x, y: command.y)
65
+ }
66
+ NSLog(
67
+ "AGENT_DEVICE_RUNNER_TEXT_ENTRY_PHASE commandId=%@ phase=focus durationMs=%.1f chars=%d mode=%@",
68
+ command.commandId ?? "",
69
+ Date().timeIntervalSince(focusStartedAt) * 1000.0,
70
+ text.count,
71
+ textEntryModeName(textEntryMode)
72
+ )
73
+ if textEntryMode == .replacement {
74
+ #if os(iOS)
75
+ let canReplaceResolvedFirstResponder = Self.shouldUseSynthesizedFirstResponderReplacement(
76
+ hasResolvedElement: target.element != nil,
77
+ hasRefreshPoint: target.refreshPoint != nil,
78
+ xCTestChannelPenalized: xCTestChannelPenalized
79
+ )
80
+ #else
81
+ let canReplaceResolvedFirstResponder = false
82
+ #endif
83
+ guard target.element != nil || canReplaceResolvedFirstResponder else {
84
+ let message =
85
+ (command.x != nil && command.y != nil)
86
+ ? "no text input found at the provided coordinates to clear"
87
+ : "no focused text input to clear"
88
+ return Response(ok: false, error: ErrorPayload(message: message))
89
+ }
90
+ }
91
+ let textResult = typeTextReliably(
92
+ app: activeApp,
93
+ target: target,
94
+ text: text,
95
+ delaySeconds: delaySeconds,
96
+ repairMode: textEntryMode,
97
+ xCTestChannelPenalized: xCTestChannelPenalized,
98
+ synthesizer: PrivateXCTestTextEntrySynthesizer(),
99
+ commandId: command.commandId
100
+ )
101
+ if let failure = textResult.failure {
102
+ return Response(
103
+ ok: false,
104
+ error: ErrorPayload(code: failure.rawValue, message: failure.message, hint: failure.hint)
105
+ )
106
+ }
107
+ if textResult.verified == false {
108
+ let expected = textResult.expectedText ?? ""
109
+ let observed = textResult.observedText ?? ""
110
+ return Response(
111
+ ok: false,
112
+ error: ErrorPayload(
113
+ code: "TEXT_ENTRY_MISMATCH",
114
+ message: "text entry verification failed: expected \"\(expected)\", observed \"\(observed)\""
115
+ )
116
+ )
117
+ }
118
+ let point = target.refreshPoint
119
+ let frame: CGRect
120
+ if let resolvedCoordinateContext {
121
+ frame = resolvedCoordinateContext.referenceFrame
122
+ } else if point != nil {
123
+ frame = activeApp.frame
124
+ } else {
125
+
126
+
127
+ frame = .zero
128
+ }
129
+ return Response(
130
+ ok: true,
131
+ data: DataPayload(
132
+ message: textResult.repaired ? "typed after repair" : "typed",
133
+ x: point.map { Double($0.x) },
134
+ y: point.map { Double($0.y) },
135
+ referenceWidth: frame.isEmpty ? nil : Double(frame.width),
136
+ referenceHeight: frame.isEmpty ? nil : Double(frame.height),
137
+ maestroNonHittableCoordinateFallbackUsed: maestroNonHittableCoordinateFallbackUsed,
138
+ textEntryRoute: textResult.textEntryRoute
139
+ )
140
+ )
141
+ }
142
+ }
@@ -31,8 +31,10 @@ final class RunnerTests: XCTestCase {
31
31
 
32
32
 
33
33
 
34
+
34
35
  enum RunnerWireErrorCode {
35
36
  static let mainThreadTimeout = "MAIN_THREAD_TIMEOUT"
37
+ static let appNotRunning = "APP_NOT_RUNNING"
36
38
  }
37
39
 
38
40
  static let springboardBundleId = "com.apple.springboard"
@@ -52,6 +54,8 @@ final class RunnerTests: XCTestCase {
52
54
  let commandExecutionQueue = DispatchQueue(label: "agent-device.runner.commands")
53
55
  let app = XCUIApplication()
54
56
  lazy var springboard = XCUIApplication(bundleIdentifier: Self.springboardBundleId)
57
+
58
+
55
59
  var currentApp: XCUIApplication?
56
60
  var currentBundleId: String?
57
61
  var currentAppProcessIdentifier: Int?
@@ -75,11 +79,22 @@ final class RunnerTests: XCTestCase {
75
79
  let xctestIdleKeepaliveInterval: TimeInterval = 60.0
76
80
  let minRecordingFps = 1
77
81
  let maxRecordingFps = 120
82
+
83
+
84
+
85
+ let recordingFrameCaptureTimeout: TimeInterval = 1
78
86
  var needsPostSnapshotInteractionDelay = false
79
87
 
80
88
 
81
89
 
82
90
 
91
+ var lastLoggedFastAppGuardLine: String?
92
+ var lastLoggedGesturePolicyLines: [SynthesizedGesturePolicyKind: String] = [:]
93
+ var runnerMarkerWriter: (String) -> Void = { NSLog("%@", $0) }
94
+
95
+
96
+
97
+
83
98
  var firstInteractionReadyUptime: TimeInterval?
84
99
  var runnerAccessibilityHealth: RunnerAccessibilityHealth = .unknown
85
100
  var activeRecording: ScreenRecorder?
@@ -95,6 +110,9 @@ final class RunnerTests: XCTestCase {
95
110
 
96
111
  let mainThreadWorkLock = NSLock()
97
112
  var abandonedMainThreadWorkCount = 0
113
+
114
+
115
+ var mainThreadWorkInFlightCount = 0
98
116
  var abandonedMainThreadWorkSince: Date?
99
117
 
100
118
 
@@ -107,7 +125,7 @@ final class RunnerTests: XCTestCase {
107
125
  var snapshotXCTestChannelPenaltyBundleId: String?
108
126
  var snapshotXCTestChannelPenaltyUntil = Date.distantPast
109
127
  let snapshotXCTestChannelPenaltyDuration: TimeInterval = 120
110
- var snapshotXCTestPenaltyWarmupExemptionPending = false
128
+ let snapshotXCTestPenaltyWarmupExemption = SnapshotXCTestPenaltyWarmupExemption()
111
129
 
112
130
 
113
131
 
@@ -189,6 +207,9 @@ final class RunnerTests: XCTestCase {
189
207
 
190
208
 
191
209
 
210
+
211
+
212
+
192
213
 
193
214
 
194
215
 
@@ -263,11 +284,6 @@ final class RunnerTests: XCTestCase {
263
284
 
264
285
  @MainActor
265
286
  func testCommand() throws {
266
- if RunnerEnv.isTruthy("AGENT_DEVICE_RUNNER_NOOP_STARTUP") {
267
- NSLog("AGENT_DEVICE_RUNNER_NOOP_STARTUP=1")
268
- return
269
- }
270
-
271
287
  doneExpectation = expectation(description: "agent-device command handled")
272
288
  NSLog("AGENT_DEVICE_RUNNER_HEADLESS_STARTUP=1")
273
289
  let desiredPort = RunnerEnv.resolvePort()