agent-device 0.21.14 → 0.21.15

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 (287) hide show
  1. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.21.14.apk → agent-device-android-ime-helper-0.21.15.apk} +0 -0
  2. package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.15.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.15.manifest.json} +4 -4
  4. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.15.apk +0 -0
  5. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.15.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.15.manifest.json} +6 -6
  7. package/apple/snapshot-bridge/SnapshotBridgeRuntime.m +11 -1
  8. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunner/AgentDeviceRunnerApp.m +87 -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 +8 -2
  12. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedTextEntry.m +26 -1
  13. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Alert.swift +40 -5
  14. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandDispatch.swift +179 -91
  15. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +5 -4
  16. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandJournal.swift +1 -1
  17. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+GestureExecution.swift +1 -0
  18. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Lifecycle.swift +36 -49
  19. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+MainThreadWork.swift +11 -15
  20. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +136 -29
  21. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Navigation.swift +4 -2
  22. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+PrivateAXPresentation.swift +3 -0
  23. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScreenRecorder.swift +6 -5
  24. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScrollDragExecution.swift +4 -0
  25. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SequenceExecution.swift +2 -0
  26. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +8 -4
  27. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotAcquisition.swift +56 -14
  28. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCapturePlan.swift +18 -8
  29. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCaptureTarget.swift +9 -7
  30. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotExecution.swift +5 -4
  31. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotTiming.swift +2 -2
  32. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedGesturePolicy.swift +4 -1
  33. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedInteraction.swift +5 -1
  34. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedTextEntry.swift +78 -3
  35. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntry.swift +32 -2
  36. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntryFocus.swift +7 -4
  37. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextTyping.swift +23 -0
  38. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Transport.swift +17 -10
  39. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TvRemote.swift +8 -9
  40. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TypeExecution.swift +2 -1
  41. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +12 -11
  42. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotGeometry.swift +7 -4
  43. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotModels.swift +15 -8
  44. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotPresentationProjection.swift +1 -0
  45. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotVisibilityFold.swift +26 -9
  46. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotVisibilityFoldProjection.swift +1 -0
  47. package/dist/src/adapter.js +1 -1
  48. package/dist/src/adb-executor.js +1 -1
  49. package/dist/src/adb-failure.js +1 -1
  50. package/dist/src/adb-transport.js +1 -1
  51. package/dist/src/agent-browser-provider.js +1 -1
  52. package/dist/src/agent-browser-tool.js +1 -1
  53. package/dist/src/agent-device-client.js +2 -2
  54. package/dist/src/ai-sdk.js +1 -1
  55. package/dist/src/alert-runtime.js +1 -1
  56. package/dist/src/android-adb.d.ts +1 -8
  57. package/dist/src/android-input-ownership.js +1 -1
  58. package/dist/src/android-snapshot-quality.js +1 -0
  59. package/dist/src/android-system-chrome.js +1 -1
  60. package/dist/src/android.js +1 -1
  61. package/dist/src/app-catalog.js +1 -1
  62. package/dist/src/app-deployment-resolution.js +1 -1
  63. package/dist/src/app-inventory.js +1 -1
  64. package/dist/src/app-launch.js +1 -1
  65. package/dist/src/app-lifecycle.js +1 -1
  66. package/dist/src/app-lifecycle2.js +1 -1
  67. package/dist/src/app-log-files.js +1 -1
  68. package/dist/src/app-log-runtime2.js +1 -1
  69. package/dist/src/app-resolution.js +2 -2
  70. package/dist/src/apple-runner-platform.js +1 -1
  71. package/dist/src/application-lifecycle-interaction.js +1 -1
  72. package/dist/src/archive.js +1 -1
  73. package/dist/src/artifact-download.js +1 -1
  74. package/dist/src/auth-session.js +1 -1
  75. package/dist/src/auth.js +1 -1
  76. package/dist/src/backend-snapshot-options.js +1 -1
  77. package/dist/src/boot-diagnostics.js +1 -1
  78. package/dist/src/capture-disclosure.js +1 -1
  79. package/dist/src/capture.js +1 -1
  80. package/dist/src/cli-config.js +1 -1
  81. package/dist/src/cli.js +1 -1
  82. package/dist/src/click-button.js +1 -1
  83. package/dist/src/client-metro.js +1 -1
  84. package/dist/src/client-types.d.ts +102 -95
  85. package/dist/src/clipboard-shell-response.js +1 -1
  86. package/dist/src/command-schema.js +1 -1
  87. package/dist/src/command-tools.js +3 -3
  88. package/dist/src/connection-runtime.js +1 -1
  89. package/dist/src/connection.js +1 -1
  90. package/dist/src/contracts.d.ts +1 -1
  91. package/dist/src/contracts.js +1 -1
  92. package/dist/src/daemon-client-lifecycle.js +1 -1
  93. package/dist/src/daemon.js +1 -1
  94. package/dist/src/debug-symbols-facade.js +1 -1
  95. package/dist/src/device-claim-rule.js +2 -2
  96. package/dist/src/device-input-state.d.ts +15 -2
  97. package/dist/src/device-inventory-context.js +1 -1
  98. package/dist/src/device-rotation.js +1 -1
  99. package/dist/src/device-selection-resolver.js +1 -1
  100. package/dist/src/device-session.js +1 -1
  101. package/dist/src/device-shell.js +1 -1
  102. package/dist/src/device2.js +1 -1
  103. package/dist/src/devicectl.js +1 -1
  104. package/dist/src/diagnostics.js +1 -1
  105. package/dist/src/dispatch-resolve.js +1 -1
  106. package/dist/src/doctor2.js +1 -1
  107. package/dist/src/engine-eval-script.js +1 -1
  108. package/dist/src/error.js +1 -1
  109. package/dist/src/exec.js +1 -1
  110. package/dist/src/file.js +1 -1
  111. package/dist/src/find.js +1 -1
  112. package/dist/src/format.js +1 -1
  113. package/dist/src/gesture-plan-types.d.ts +1 -1
  114. package/dist/src/gesture-plan.js +1 -1
  115. package/dist/src/gesture-runtime.js +1 -1
  116. package/dist/src/harmonyos.js +1 -1
  117. package/dist/src/helper.js +2 -2
  118. package/dist/src/human-control-contract.js +1 -1
  119. package/dist/src/human-control.js +1 -1
  120. package/dist/src/index.d.ts +7 -3
  121. package/dist/src/index.js +1 -1
  122. package/dist/src/input-actions.js +1 -1
  123. package/dist/src/input-actions2.js +1 -1
  124. package/dist/src/install-artifact-facade.js +1 -1
  125. package/dist/src/install-source-network-transport.js +1 -1
  126. package/dist/src/install-source2.js +1 -1
  127. package/dist/src/interaction-positionals.js +1 -1
  128. package/dist/src/interaction-ref-policy.js +1 -1
  129. package/dist/src/interaction2.js +1 -1
  130. package/dist/src/interactor-operation-binding.js +1 -1
  131. package/dist/src/interactor.js +4 -4
  132. package/dist/src/interactor2.js +1 -1
  133. package/dist/src/interactors.js +1 -1
  134. package/dist/src/internal/bin.js +1 -1
  135. package/dist/src/internal/daemon.js +2 -2
  136. package/dist/src/internal/png-worker.js +1 -1
  137. package/dist/src/internal/run-script-http-child.js +1 -1
  138. package/dist/src/inventory.js +1 -1
  139. package/dist/src/inventory2.js +1 -1
  140. package/dist/src/inventory3.js +1 -1
  141. package/dist/src/inventory5.js +1 -1
  142. package/dist/src/ios-snapshot-acquisition.js +1 -1
  143. package/dist/src/ios-snapshot-engine.js +1 -0
  144. package/dist/src/ios-snapshot-runtime.js +1 -1
  145. package/dist/src/lease.js +1 -1
  146. package/dist/src/limrun-download-file.js +1 -1
  147. package/dist/src/limrun-runtime-dependencies.js +1 -1
  148. package/dist/src/limrun.d.ts +16 -3
  149. package/dist/src/linux.js +1 -1
  150. package/dist/src/location-coordinates.js +1 -1
  151. package/dist/src/managed-device-scope.js +1 -1
  152. package/dist/src/mechanics.js +17 -17
  153. package/dist/src/mobile-snapshot-semantics.js +1 -1
  154. package/dist/src/multitouch-support.js +1 -1
  155. package/dist/src/native-build-cache.js +1 -1
  156. package/dist/src/observation.js +1 -1
  157. package/dist/src/overlay.js +3 -0
  158. package/dist/src/parameterized-recorded-fill.js +1 -1
  159. package/dist/src/perf-facade.js +1 -1
  160. package/dist/src/perf-runtime-operation-builder.js +1 -1
  161. package/dist/src/perf-runtime-plan.js +1 -1
  162. package/dist/src/perf-target.js +1 -1
  163. package/dist/src/perf.js +1 -1
  164. package/dist/src/physical-device-control.js +1 -1
  165. package/dist/src/platform-runtime-managed-owner.js +1 -1
  166. package/dist/src/platform-runtime-open-target.js +1 -1
  167. package/dist/src/platform-runtime-operation-host.js +4 -4
  168. package/dist/src/platform-runtime-runtime-hints.js +1 -1
  169. package/dist/src/platform-runtime-screen-recording-apple-simulator-host.js +1 -1
  170. package/dist/src/platform-runtime-screen-recording-finalizer-host.js +1 -1
  171. package/dist/src/platform-runtime-screen-recording-process-host.js +1 -1
  172. package/dist/src/platform-runtime.js +1 -1
  173. package/dist/src/platform-runtime2.js +1 -1
  174. package/dist/src/png-crop-bytes.js +1 -1
  175. package/dist/src/png-size.js +1 -1
  176. package/dist/src/png-worker-client.js +1 -1
  177. package/dist/src/png.js +1 -1
  178. package/dist/src/prepare-kind.js +1 -1
  179. package/dist/src/provider-session-ownership.js +1 -1
  180. package/dist/src/provider-webdriver.js +1 -1
  181. package/dist/src/proxy.js +1 -1
  182. package/dist/src/quality-warnings.js +1 -1
  183. package/dist/src/react-native.js +1 -1
  184. package/dist/src/record-runtime.js +1 -1
  185. package/dist/src/recording.js +1 -1
  186. package/dist/src/rect-center.js +1 -1
  187. package/dist/src/register-builtins.js +1 -1
  188. package/dist/src/registry.js +36 -36
  189. package/dist/src/remote-config2.js +1 -1
  190. package/dist/src/replay-divergence.js +1 -1
  191. package/dist/src/replay.js +1 -1
  192. package/dist/src/reporting.js +1 -1
  193. package/dist/src/request-admission.js +1 -1
  194. package/dist/src/request-cancel.js +1 -1
  195. package/dist/src/request-runtime-binding.js +1 -1
  196. package/dist/src/retry.js +1 -1
  197. package/dist/src/runner-cache-metadata.js +1 -1
  198. package/dist/src/runner-client.js +1 -1
  199. package/dist/src/runner-dev-tools-security.js +1 -1
  200. package/dist/src/runner-device-readiness.js +1 -1
  201. package/dist/src/runner-disposal.js +1 -1
  202. package/dist/src/runner-error-classification.js +2 -2
  203. package/dist/src/runner.js +1 -1
  204. package/dist/src/runtime-transport-hints.js +1 -1
  205. package/dist/src/runtime.js +1 -3
  206. package/dist/src/runtime10.js +2 -2
  207. package/dist/src/runtime2.js +4 -4
  208. package/dist/src/runtime3.js +1 -1
  209. package/dist/src/runtime4.js +5 -5
  210. package/dist/src/runtime5.js +1 -1
  211. package/dist/src/runtime6.js +1 -1
  212. package/dist/src/runtime7.js +1 -1
  213. package/dist/src/runtime8.js +1 -1
  214. package/dist/src/runtime9.js +1 -1
  215. package/dist/src/screen-recording-admission-ledger.js +1 -1
  216. package/dist/src/screen-recording-completion.js +1 -1
  217. package/dist/src/screen-recording-options.js +1 -1
  218. package/dist/src/screen-recording-resource-recovery.js +1 -1
  219. package/dist/src/screenshot-density.js +1 -1
  220. package/dist/src/screenshot-image.js +1 -1
  221. package/dist/src/screenshot-overlay-draw.js +1 -1
  222. package/dist/src/screenshot-runtime.js +1 -1
  223. package/dist/src/screenshot.js +1 -1
  224. package/dist/src/screenshot3.js +1 -1
  225. package/dist/src/script.js +1 -1
  226. package/dist/src/scroll-command.d.ts +13 -6
  227. package/dist/src/scroll-command.js +1 -1
  228. package/dist/src/scroll-gesture.js +1 -1
  229. package/dist/src/sdk-batch-runner.js +1 -1
  230. package/dist/src/sdk-batch.js +2 -2
  231. package/dist/src/sdk-contracts.d.ts +38 -10
  232. package/dist/src/sdk-contracts.js +2 -2
  233. package/dist/src/sdk-device.js +1 -1
  234. package/dist/src/sdk-finders.d.ts +1 -1
  235. package/dist/src/sdk-io.js +1 -1
  236. package/dist/src/sdk-remote-config.js +1 -1
  237. package/dist/src/sdk-selectors.d.ts +1 -1
  238. package/dist/src/selection.js +0 -0
  239. package/dist/src/selector-runtime.js +1 -1
  240. package/dist/src/server.js +1 -1
  241. package/dist/src/session-allocation.js +1 -1
  242. package/dist/src/session-routing.js +1 -1
  243. package/dist/src/session-runtime-admission.js +1 -1
  244. package/dist/src/session-snapshot.js +1 -1
  245. package/dist/src/session-store.js +2 -2
  246. package/dist/src/session.js +1 -1
  247. package/dist/src/session2.js +4 -4
  248. package/dist/src/settings.js +1 -1
  249. package/dist/src/settings2.js +1 -0
  250. package/dist/src/simulator.js +1 -1
  251. package/dist/src/snapshot-capture-annotations.js +1 -1
  252. package/dist/src/snapshot-desktop-projection.js +1 -1
  253. package/dist/src/snapshot-runtime.js +1 -1
  254. package/dist/src/snapshot-state.js +1 -1
  255. package/dist/src/snapshot2.js +1 -1
  256. package/dist/src/snapshot3.js +1 -1
  257. package/dist/src/snapshot4.js +1 -1
  258. package/dist/src/src.js +1 -1
  259. package/dist/src/src4.js +1 -1
  260. package/dist/src/src7.js +1 -1
  261. package/dist/src/src8.js +2 -2
  262. package/dist/src/string-enum.js +1 -1
  263. package/dist/src/surface-snapshot.js +1 -1
  264. package/dist/src/takeover.js +1 -1
  265. package/dist/src/text-entry-focus.js +1 -1
  266. package/dist/src/tool-provider.js +1 -1
  267. package/dist/src/tool-provider2.js +1 -1
  268. package/dist/src/touch-runtime.js +1 -1
  269. package/dist/src/transport.js +1 -1
  270. package/dist/src/tree.js +1 -1
  271. package/dist/src/tv-remote.js +1 -1
  272. package/dist/src/type-text-runtime.js +1 -1
  273. package/dist/src/verified-file.js +1 -1
  274. package/dist/src/video.js +1 -1
  275. package/dist/src/viewport.js +1 -1
  276. package/dist/src/web.js +1 -1
  277. package/dist/src/web2.js +1 -1
  278. package/dist/src/webdriver-source.js +1 -1
  279. package/package.json +1 -1
  280. package/server.json +2 -2
  281. package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.14.apk.sha256 +0 -1
  282. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.14.apk +0 -0
  283. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.14.apk.sha256 +0 -1
  284. package/dist/src/absence-observation-errors.js +0 -1
  285. package/dist/src/runner-presentation.js +0 -1
  286. package/dist/src/semantic-index.js +0 -1
  287. package/dist/src/snapshot-scope.js +0 -1
@@ -48,6 +48,39 @@ extension RunnerTests {
48
48
  )
49
49
  }
50
50
 
51
+
52
+
53
+ @MainActor
54
+ func executeAppState(command: Command) -> Response {
55
+ guard let bundleId = command.appBundleId?.trimmedNonEmpty else {
56
+ return Response(
57
+ ok: false,
58
+ error: ErrorPayload(
59
+ code: "INVALID_ARGS",
60
+ message: "appState requires appBundleId",
61
+ hint: "Set appBundleId to the session app's bundle identifier."
62
+ )
63
+ )
64
+ }
65
+ let state = XCUIApplication(bundleIdentifier: bundleId).state
66
+ return Response(ok: true, data: DataPayload(applicationState: Self.applicationStateName(state)))
67
+ }
68
+
69
+
70
+
71
+ static func applicationStateName(_ state: XCUIApplication.State) -> String {
72
+ switch state {
73
+ case .unknown: return "unknown"
74
+ case .notRunning: return "notRunning"
75
+ case .runningBackground: return "runningBackground"
76
+ case .runningForeground: return "runningForeground"
77
+ #if !os(macOS)
78
+ case .runningBackgroundSuspended: return "runningBackgroundSuspended"
79
+ #endif
80
+ @unknown default: return "unknown"
81
+ }
82
+ }
83
+
51
84
  struct ActiveCommandContext {
52
85
  let app: XCUIApplication
53
86
 
@@ -133,7 +166,7 @@ extension RunnerTests {
133
166
  return try runMainThreadWork(
134
167
  "command_execution",
135
168
  timeout: max(0.001, deadline.timeIntervalSinceNow),
136
- timeoutError: mainThreadExecutionTimeoutError
169
+ timeoutError: Self.mainThreadExecutionTimeoutError
137
170
  ) {
138
171
  try self.executeOnMainSafely(
139
172
  command: command,
@@ -144,8 +177,8 @@ extension RunnerTests {
144
177
  }
145
178
  return try runMainThreadWork(
146
179
  "command_execution",
147
- timeout: mainThreadExecutionTimeout,
148
- timeoutError: mainThreadExecutionTimeoutError
180
+ timeout: Self.mainThreadExecutionTimeout,
181
+ timeoutError: Self.mainThreadExecutionTimeoutError
149
182
  ) {
150
183
  try self.executeOnMainSafely(command: command, routeToSpringboard: routeToSpringboard)
151
184
  }
@@ -153,6 +186,7 @@ extension RunnerTests {
153
186
 
154
187
 
155
188
 
189
+ @MainActor
156
190
  private func executeOnMainSafely(
157
191
  command: Command,
158
192
  alertDeadline: Date? = nil,
@@ -222,7 +256,7 @@ extension RunnerTests {
222
256
  invalidateCachedTarget(reason: "xctest_recorded_failure")
223
257
  return failureResponse
224
258
  }
225
- if !hasRetried, shouldRetryCommand(command), shouldRetryResponse(response) {
259
+ if !hasRetried, command.traits.retryOnSessionLoss, shouldRetryResponse(response) {
226
260
  NSLog(
227
261
  "AGENT_DEVICE_RUNNER_RETRY command=%@ reason=response_unavailable",
228
262
  command.command.rawValue
@@ -247,8 +281,8 @@ extension RunnerTests {
247
281
  while true {
248
282
  let failureCountBefore = try runMainThreadWork(
249
283
  "recorded_failure_count",
250
- timeout: mainThreadExecutionTimeout,
251
- timeoutError: mainThreadExecutionTimeoutError
284
+ timeout: Self.mainThreadExecutionTimeout,
285
+ timeoutError: Self.mainThreadExecutionTimeoutError
252
286
  ) {
253
287
  self.currentXCTestFailureCount()
254
288
  }
@@ -264,8 +298,8 @@ extension RunnerTests {
264
298
  }
265
299
  let recordedFailureResponse = try runMainThreadWork(
266
300
  "recorded_failure_count",
267
- timeout: mainThreadExecutionTimeout,
268
- timeoutError: mainThreadExecutionTimeoutError
301
+ timeout: Self.mainThreadExecutionTimeout,
302
+ timeoutError: Self.mainThreadExecutionTimeoutError
269
303
  ) {
270
304
  self.didRecordXCTestFailure(since: failureCountBefore)
271
305
  ? self.xctestRecordedFailureResponse(command: command, response: response)
@@ -274,14 +308,14 @@ extension RunnerTests {
274
308
  if let recordedFailureResponse {
275
309
  try runMainThreadWork(
276
310
  "target_invalidation",
277
- timeout: mainThreadExecutionTimeout,
278
- timeoutError: mainThreadExecutionTimeoutError
311
+ timeout: Self.mainThreadExecutionTimeout,
312
+ timeoutError: Self.mainThreadExecutionTimeoutError
279
313
  ) {
280
314
  self.invalidateCachedTarget(reason: "xctest_recorded_failure")
281
315
  }
282
316
  return recordedFailureResponse
283
317
  }
284
- if !hasRetried, shouldRetryCommand(command), shouldRetryResponse(response) {
318
+ if !hasRetried, command.traits.retryOnSessionLoss, shouldRetryResponse(response) {
285
319
  NSLog(
286
320
  "AGENT_DEVICE_RUNNER_RETRY command=%@ reason=response_unavailable",
287
321
  command.command.rawValue
@@ -289,8 +323,8 @@ extension RunnerTests {
289
323
  hasRetried = true
290
324
  try runMainThreadWork(
291
325
  "target_invalidation",
292
- timeout: mainThreadExecutionTimeout,
293
- timeoutError: mainThreadExecutionTimeoutError
326
+ timeout: Self.mainThreadExecutionTimeout,
327
+ timeoutError: Self.mainThreadExecutionTimeoutError
294
328
  ) {
295
329
  self.invalidateCachedTarget(reason: "response_unavailable")
296
330
  self.sleepFor(self.retryCooldown)
@@ -301,6 +335,7 @@ extension RunnerTests {
301
335
  }
302
336
  }
303
337
 
338
+ @MainActor
304
339
  private func executeOnMain(
305
340
  command: Command,
306
341
  alertDeadline: Date?,
@@ -386,6 +421,8 @@ extension RunnerTests {
386
421
  }
387
422
  case .uptime:
388
423
  return executeUptime()
424
+ case .appState:
425
+ return executeAppState(command: command)
389
426
  case .activate:
390
427
  guard
391
428
  let bundleId = command.appBundleId?.trimmingCharacters(in: .whitespacesAndNewlines),
@@ -404,7 +441,7 @@ extension RunnerTests {
404
441
  return Response(ok: false, error: ErrorPayload(message: "terminate requires appBundleId"))
405
442
  }
406
443
  XCUIApplication(bundleIdentifier: bundleId).terminate()
407
- if currentBundleId == bundleId {
444
+ if mainOwned.bundleId == bundleId {
408
445
  invalidateCachedTarget(reason: "target_terminated")
409
446
  }
410
447
  return Response(ok: true, data: DataPayload(message: "app terminated"))
@@ -418,88 +455,130 @@ extension RunnerTests {
418
455
  )
419
456
  }
420
457
 
458
+
459
+
460
+
461
+ @MainActor
421
462
  func prepareActiveCommandContext(
422
463
  command: Command,
423
464
  routeToSpringboard: Bool = false
424
465
  ) -> ActiveCommandPreparation {
425
- var activeApp = currentApp ?? app
426
- var systemSurface: SystemSurfaceHost? = nil
427
466
  if routeToSpringboard {
428
- activeApp = springboard
429
- } else if shouldSkipAppActivationPreflight(command) {
430
- activeApp = resolveAppWithoutActivation(command: command)
431
- } else if let presented = presentedSystemSurfaceHost() {
467
+ return .context(ActiveCommandContext(app: springboard))
468
+ }
469
+ switch command.traits.launchPolicy {
470
+ case .noApp:
471
+
472
+
473
+ if let presented = presentedSystemSurfaceHost() {
474
+ return .context(ActiveCommandContext(app: presented.app, systemSurface: presented.host))
475
+ }
476
+
477
+
478
+ return .context(ActiveCommandContext(app: mainOwned.app ?? app))
479
+ case .presentedSurface:
480
+
481
+
482
+ #if os(iOS)
483
+ return .context(ActiveCommandContext(app: resolveAppWithoutActivation(command: command)))
484
+ #else
485
+
486
+
487
+
488
+ return prepareActivatedTarget(command: command)
489
+ #endif
490
+ case .existingApp:
491
+
492
+
493
+ return prepareActivatedTarget(command: command)
494
+ case .mayLaunch:
495
+ if shouldSkipAppActivationPreflight(command) {
496
+
497
+
498
+ return .context(ActiveCommandContext(app: resolveAppWithoutActivation(command: command)))
499
+ }
500
+ return prepareActivatedTarget(command: command)
501
+ }
502
+ }
503
+
504
+
505
+
506
+
507
+
508
+ @MainActor
509
+ private func prepareActivatedTarget(command: Command) -> ActiveCommandPreparation {
510
+ if let presented = presentedSystemSurfaceHost() {
432
511
 
433
512
 
434
513
 
435
- activeApp = presented.app
436
- systemSurface = presented.host
437
- if isInteractionCommand(command.command) {
514
+ if command.traits.isInteraction {
438
515
  applyInteractionStabilizationIfNeeded()
439
516
  }
440
- } else if !isRunnerLifecycleCommand(command.command) {
441
- let normalizedBundleId = command.appBundleId?
442
- .trimmingCharacters(in: .whitespacesAndNewlines)
443
- let requestedBundleId = (normalizedBundleId?.isEmpty == true) ? nil : normalizedBundleId
444
- if let bundleId = requestedBundleId,
445
- let notRunning = notRunningReadResponse(command: command, bundleId: bundleId)
446
- {
447
- return .response(notRunning)
517
+ return .context(ActiveCommandContext(app: presented.app, systemSurface: presented.host))
518
+ }
519
+
520
+ let normalizedBundleId = command.appBundleId?
521
+ .trimmingCharacters(in: .whitespacesAndNewlines)
522
+ let requestedBundleId = (normalizedBundleId?.isEmpty == true) ? nil : normalizedBundleId
523
+ if let bundleId = requestedBundleId,
524
+ let notRunning = notRunningRefusal(command: command, bundleId: bundleId)
525
+ {
526
+ return .response(notRunning)
527
+ }
528
+ if let bundleId = requestedBundleId {
529
+ if mainOwned.bundleId != bundleId || mainOwned.app == nil {
530
+ _ = activateTarget(bundleId: bundleId, reason: "bundle_changed")
531
+ } else {
532
+ refreshCachedTargetIfProcessChanged(bundleId: bundleId)
448
533
  }
534
+ } else {
535
+
536
+ invalidateCachedTarget(reason: "missing_app_bundle")
537
+ }
538
+
539
+
540
+ var activeApp = mainOwned.app ?? app
541
+ if let bundleId = requestedBundleId, targetNeedsActivation(activeApp) {
542
+ activeApp = activateTarget(bundleId: bundleId, reason: "stale_target")
543
+ } else if requestedBundleId == nil, targetNeedsActivation(activeApp) {
544
+ ensureRunnerHostAppActive(reason: "missing_app_bundle")
545
+ activeApp = app
546
+ }
547
+
548
+ let skipExistenceWait = canUseFastForegroundAppGuard(
549
+ activeApp: activeApp,
550
+ requestedBundleId: requestedBundleId
551
+ )
552
+ if !skipExistenceWait && !activeApp.waitForExistence(timeout: appExistenceTimeout) {
449
553
  if let bundleId = requestedBundleId {
450
- if currentBundleId != bundleId || currentApp == nil {
451
- _ = activateTarget(bundleId: bundleId, reason: "bundle_changed")
452
- } else {
453
- refreshCachedTargetIfProcessChanged(bundleId: bundleId)
554
+ activeApp = activateTarget(bundleId: bundleId, reason: "missing_after_wait")
555
+ guard activeApp.waitForExistence(timeout: appExistenceTimeout) else {
556
+ return .response(Response(ok: false, error: .targetAppUnavailable(bundleId: bundleId)))
454
557
  }
455
558
  } else {
456
-
457
- invalidateCachedTarget(reason: "missing_app_bundle")
559
+ return .response(Response(ok: false, error: .targetAppUnavailable(bundleId: nil)))
458
560
  }
561
+ }
459
562
 
460
- activeApp = currentApp ?? app
461
- if let bundleId = requestedBundleId, targetNeedsActivation(activeApp) {
462
- activeApp = activateTarget(bundleId: bundleId, reason: "stale_target")
463
- } else if requestedBundleId == nil, targetNeedsActivation(activeApp) {
464
- ensureRunnerHostAppActive(reason: "missing_app_bundle")
563
+ if command.traits.isInteraction {
564
+ if let bundleId = requestedBundleId, activeApp.state != .runningForeground {
565
+ activeApp = activateTarget(bundleId: bundleId, reason: "interaction_foreground_guard")
566
+ } else if requestedBundleId == nil, activeApp.state != .runningForeground {
567
+ ensureRunnerHostAppActive(reason: "interaction_missing_app_bundle")
465
568
  activeApp = app
466
569
  }
467
-
468
- let skipExistenceWait = canUseFastForegroundAppGuard(
570
+ let skipInteractionExistenceWait = canUseFastForegroundAppGuard(
469
571
  activeApp: activeApp,
470
572
  requestedBundleId: requestedBundleId
471
573
  )
472
- if !skipExistenceWait && !activeApp.waitForExistence(timeout: appExistenceTimeout) {
473
- if let bundleId = requestedBundleId {
474
- activeApp = activateTarget(bundleId: bundleId, reason: "missing_after_wait")
475
- guard activeApp.waitForExistence(timeout: appExistenceTimeout) else {
476
- return .response(Response(ok: false, error: .targetAppUnavailable(bundleId: bundleId)))
477
- }
478
- } else {
479
- return .response(Response(ok: false, error: .targetAppUnavailable(bundleId: nil)))
480
- }
481
- }
482
-
483
- if isInteractionCommand(command.command) {
484
- if let bundleId = requestedBundleId, activeApp.state != .runningForeground {
485
- activeApp = activateTarget(bundleId: bundleId, reason: "interaction_foreground_guard")
486
- } else if requestedBundleId == nil, activeApp.state != .runningForeground {
487
- ensureRunnerHostAppActive(reason: "interaction_missing_app_bundle")
488
- activeApp = app
489
- }
490
- let skipInteractionExistenceWait = canUseFastForegroundAppGuard(
491
- activeApp: activeApp,
492
- requestedBundleId: requestedBundleId
574
+ if !skipInteractionExistenceWait && !activeApp.waitForExistence(timeout: 2) {
575
+ return .response(
576
+ Response(ok: false, error: .targetAppUnavailable(bundleId: requestedBundleId))
493
577
  )
494
- if !skipInteractionExistenceWait && !activeApp.waitForExistence(timeout: 2) {
495
- return .response(
496
- Response(ok: false, error: .targetAppUnavailable(bundleId: requestedBundleId))
497
- )
498
- }
499
- applyInteractionStabilizationIfNeeded()
500
578
  }
579
+ applyInteractionStabilizationIfNeeded()
501
580
  }
502
- return .context(ActiveCommandContext(app: activeApp, systemSurface: systemSurface))
581
+ return .context(ActiveCommandContext(app: activeApp))
503
582
  }
504
583
 
505
584
 
@@ -509,9 +588,8 @@ extension RunnerTests {
509
588
  private func presentedSystemSurfaceHost() -> (host: SystemSurfaceHost, app: XCUIApplication)? {
510
589
  #if os(iOS)
511
590
  for host in SystemSurfaceHostRegistry.hosts {
512
- let candidate = XCUIApplication(bundleIdentifier: host.bundleId)
513
- if candidate.state == .runningForeground {
514
- return (host, candidate)
591
+ if systemSurfaceHostState(host) == .runningForeground {
592
+ return (host, XCUIApplication(bundleIdentifier: host.bundleId))
515
593
  }
516
594
  }
517
595
  return nil
@@ -520,6 +598,21 @@ extension RunnerTests {
520
598
  #endif
521
599
  }
522
600
 
601
+
602
+
603
+
604
+
605
+
606
+
607
+ private func systemSurfaceHostState(_ host: SystemSurfaceHost) -> XCUIApplication.State {
608
+
609
+
610
+
611
+
612
+
613
+ return XCUIApplication(bundleIdentifier: host.bundleId).state
614
+ }
615
+
523
616
  func currentXCTestFailureCount() -> Int {
524
617
  return testRun?.failureCount ?? 0
525
618
  }
@@ -533,7 +626,7 @@ extension RunnerTests {
533
626
  if response.data?.runnerFatal == true {
534
627
  return nil
535
628
  }
536
- guard !isReadOnlyCommand(command), !isRunnerLifecycleCommand(command.command) else {
629
+ guard command.traits.convertsRecordedFailure else {
537
630
  return nil
538
631
  }
539
632
  return Response(
@@ -546,19 +639,12 @@ extension RunnerTests {
546
639
  )
547
640
  }
548
641
 
549
- func shouldSkipAppActivationPreflight(_ command: Command) -> Bool {
550
- #if os(iOS)
551
- if command.command == .alert {
552
- return true
553
- }
554
-
555
642
 
556
643
 
557
644
 
558
-
559
- if command.command == .actionButton {
560
- return true
561
- }
645
+ @MainActor
646
+ func shouldSkipAppActivationPreflight(_ command: Command) -> Bool {
647
+ #if os(iOS)
562
648
 
563
649
 
564
650
 
@@ -605,25 +691,27 @@ extension RunnerTests {
605
691
  && command.y != nil
606
692
  }
607
693
 
694
+ @MainActor
608
695
  private func hasCachedTargetForActivationSkip(command: Command) -> Bool {
609
- guard let currentApp, currentApp.state == .runningForeground else { return false }
696
+ guard let boundApp = mainOwned.app, boundApp.state == .runningForeground else { return false }
610
697
  guard let bundleId = command.appBundleId?.trimmingCharacters(in: .whitespacesAndNewlines),
611
698
  !bundleId.isEmpty
612
699
  else {
613
700
  return true
614
701
  }
615
- return currentBundleId == bundleId
702
+ return mainOwned.bundleId == bundleId
616
703
  }
617
704
 
705
+ @MainActor
618
706
  func resolveAppWithoutActivation(command: Command) -> XCUIApplication {
619
707
  guard let bundleId = command.appBundleId?
620
708
  .trimmingCharacters(in: .whitespacesAndNewlines),
621
709
  !bundleId.isEmpty
622
710
  else {
623
- return currentApp ?? app
711
+ return mainOwned.app ?? app
624
712
  }
625
- if currentBundleId == bundleId, let currentApp {
626
- return currentApp
713
+ if mainOwned.bundleId == bundleId, let boundApp = mainOwned.app {
714
+ return boundApp
627
715
  }
628
716
  return XCUIApplication(bundleIdentifier: bundleId)
629
717
  }
@@ -2,18 +2,19 @@ import XCTest
2
2
  import AgentDeviceSnapshotPresentation
3
3
 
4
4
  extension RunnerTests {
5
+ @MainActor
5
6
  func executeOnMainPrepared(
6
7
  command: Command,
7
8
  activeApp: XCUIApplication,
8
9
  alertDeadline: Date? = nil
9
10
  ) throws -> Response {
10
11
  var activeApp = activeApp
11
- if command.command != .tap && command.command != .type && !isReadOnlyCommand(command) {
12
+ if command.invalidatesRememberedTextEntryTap {
12
13
  clearRememberedTextEntryTap()
13
14
  }
14
15
  switch command.command {
15
16
  case .status, .activate, .terminate, .targetReset, .shutdown, .recordStart, .recordStop, .uptime,
16
- .snapshot:
17
+ .appState, .snapshot:
17
18
  return Response(
18
19
  ok: false,
19
20
  error: ErrorPayload(
@@ -143,7 +144,7 @@ extension RunnerTests {
143
144
  }
144
145
  if let x = command.x, let y = command.y {
145
146
  let xCTestChannelPenalized = isSnapshotXCTestChannelPenalized(
146
- bundleId: currentBundleId
147
+ bundleId: mainOwned.bundleId
147
148
  )
148
149
  let xCTestTextInputProbeSkipped = !shouldProbeCoordinateTapTextInput(
149
150
  xCTestChannelPenalized: xCTestChannelPenalized
@@ -156,7 +157,7 @@ extension RunnerTests {
156
157
  textInput = nil
157
158
  NSLog(
158
159
  "AGENT_DEVICE_RUNNER_COORDINATE_TAP_TEXT_INPUT_PROBE_SKIPPED bundle=%@",
159
- currentBundleId ?? ""
160
+ mainOwned.bundleId ?? ""
160
161
  )
161
162
  }
162
163
  var fallback: GestureFallback?
@@ -139,7 +139,7 @@ final class RunnerCommandJournal {
139
139
  .remotePress, .type, .swipe, .scroll, .desktopScroll, .findText, .querySelector, .readText,
140
140
  .backInApp, .backSystem, .home, .rotate, .appSwitcher, .actionButton, .keyboardDismiss, .keyboardReturn,
141
141
  .alert, .sequence, .gesture, .gestureViewport, .recordStart, .recordStop,
142
- .status, .uptime, .activate, .terminate, .targetReset, .shutdown:
142
+ .status, .uptime, .appState, .activate, .terminate, .targetReset, .shutdown:
143
143
  return true
144
144
  }
145
145
  }
@@ -57,6 +57,7 @@ extension RunnerTests {
57
57
 
58
58
 
59
59
 
60
+ @MainActor
60
61
  func performGesture(
61
62
  _ app: XCUIApplication,
62
63
  idleTimeout: Bool = true,