agent-device 0.20.0 → 0.20.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/README.md +3 -2
  2. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.20.0.apk → agent-device-android-ime-helper-0.20.1.apk} +0 -0
  3. package/android/ime-helper/dist/agent-device-android-ime-helper-0.20.1.apk.sha256 +1 -0
  4. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.20.0.manifest.json → agent-device-android-ime-helper-0.20.1.manifest.json} +4 -4
  5. package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.20.0.apk → agent-device-android-snapshot-helper-0.20.1.apk} +0 -0
  6. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.1.apk.sha256 +1 -0
  7. package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.20.0.manifest.json → agent-device-android-snapshot-helper-0.20.1.manifest.json} +6 -6
  8. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +54 -6
  9. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandJournal.swift +1 -1
  10. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +15 -4
  11. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +8 -3
  12. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +4 -2
  13. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntry.swift +1 -1
  14. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +2 -1
  15. package/dist/src/agent-device-client.js +2 -2
  16. package/dist/src/android-input-method-overlays.js +1 -1
  17. package/dist/src/android-system-chrome.js +1 -1
  18. package/dist/src/android.js +1 -1
  19. package/dist/src/app-log-request-scope.js +1 -1
  20. package/dist/src/app-resolution.js +1 -1
  21. package/dist/src/apple-multitouch-support.js +1 -1
  22. package/dist/src/apps.js +5 -5
  23. package/dist/src/boot-diagnostics.js +2 -2
  24. package/dist/src/cli-config.js +1 -1
  25. package/dist/src/cli-help.js +76 -19
  26. package/dist/src/cli-runner.js +1 -1
  27. package/dist/src/cli.js +14 -14
  28. package/dist/src/command-metadata.js +1 -1
  29. package/dist/src/command-schema.js +1 -1
  30. package/dist/src/compatibility-policy.js +1 -0
  31. package/dist/src/context.js +1 -1
  32. package/dist/src/contracts.d.ts +2 -2
  33. package/dist/src/debug-symbols.js +1 -1
  34. package/dist/src/devices2.js +1 -1
  35. package/dist/src/devices4.js +1 -0
  36. package/dist/src/find.js +1 -1
  37. package/dist/src/finders.js +1 -1
  38. package/dist/src/generic.js +1 -1
  39. package/dist/src/index.d.ts +144 -9
  40. package/dist/src/input-actions.js +2 -2
  41. package/dist/src/interaction-snapshot.js +1 -1
  42. package/dist/src/interaction.d.ts +2 -2
  43. package/dist/src/interaction.js +1 -1
  44. package/dist/src/interactor.js +2 -2
  45. package/dist/src/interactor2.js +1 -0
  46. package/dist/src/internal/bin.js +1 -1
  47. package/dist/src/internal/daemon.js +20 -20
  48. package/dist/src/lease-context.js +1 -1
  49. package/dist/src/lease.js +1 -1
  50. package/dist/src/linux.js +1 -1
  51. package/dist/src/method-scope.js +1 -0
  52. package/dist/src/metro-types.d.ts +52 -0
  53. package/dist/src/metro.d.ts +2 -2
  54. package/dist/src/react-native.js +1 -1
  55. package/dist/src/recording-export-quality.js +15 -0
  56. package/dist/src/register-builtins.js +1 -1
  57. package/dist/src/registry.js +24 -24
  58. package/dist/src/remote-config.d.ts +1 -1
  59. package/dist/src/reporting.js +4 -4
  60. package/dist/src/runner-client.js +1 -1
  61. package/dist/src/runner-disposal.js +2 -2
  62. package/dist/src/runner-lease.js +1 -1
  63. package/dist/src/runner-provider.js +1 -1
  64. package/dist/src/runtime.js +3 -3
  65. package/dist/src/runtime2.js +1 -1
  66. package/dist/src/sdk-android-adb.d.ts +2 -24
  67. package/dist/src/sdk-android-adb.js +4 -4
  68. package/dist/src/sdk-batch-runner.js +3 -2
  69. package/dist/src/sdk-contracts.d.ts +4 -90
  70. package/dist/src/sdk-device.js +1 -1
  71. package/dist/src/sdk-finders.d.ts +3 -2
  72. package/dist/src/sdk-finders.js +1 -1
  73. package/dist/src/sdk-remote-config.d.ts +18 -103
  74. package/dist/src/sdk-selectors.js +1 -1
  75. package/dist/src/selector-runtime.js +1 -1
  76. package/dist/src/selector-vocabulary.js +1 -1
  77. package/dist/src/selectors.d.ts +1 -1
  78. package/dist/src/selectors.js +1 -1
  79. package/dist/src/server.js +2 -2
  80. package/dist/src/session-target-evidence.js +1 -1
  81. package/dist/src/session.js +8 -8
  82. package/dist/src/simctl.js +1 -0
  83. package/dist/src/simulator.js +1 -1
  84. package/dist/src/snapshot-diagnostics.js +4 -4
  85. package/dist/src/snapshot-quality.js +1 -1
  86. package/dist/src/snapshot2.js +1 -1
  87. package/dist/src/tool-provider3.js +1 -0
  88. package/dist/src/unsupported-interactor.js +1 -0
  89. package/dist/src/web.js +1 -1
  90. package/package.json +8 -6
  91. package/server.json +2 -2
  92. package/skills/agent-device/SKILL.md +5 -3
  93. package/android/ime-helper/dist/agent-device-android-ime-helper-0.20.0.apk.sha256 +0 -1
  94. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.0.apk.sha256 +0 -1
  95. package/dist/src/config.js +0 -1
  96. package/dist/src/daemon-shutdown-report.js +0 -1
  97. package/dist/src/sdk-batch.js +0 -2
  98. package/dist/src/tv-remote.js +0 -15
package/README.md CHANGED
@@ -15,7 +15,7 @@
15
15
 
16
16
  Let your coding agent verify its changes in the running app.
17
17
 
18
- `agent-device` lets coding agents inspect, control, and verify apps on iOS, Android, TV, web, macOS, and Linux. Agents can read token-efficient accessibility snapshots, find elements by ref or selector, run device actions, and save evidence for review.
18
+ `agent-device` lets coding agents inspect, control, and verify apps on iOS, Android, tvOS, Android TV, Amazon Vega OS TV through the Vega Virtual Device (VVD), web, macOS, and Linux. Agents can read token-efficient accessibility snapshots where supported, find elements by ref or selector, run device actions, and save evidence for review. Initial Vega OS support is VVD-only and covers discovery, app lifecycle, and complete TV-remote control; physical Fire TV, capture, and selector backends remain unsupported.
19
19
 
20
20
  Your coding agent or QA tool reads each result and chooses the next command. `agent-device` runs the command and saves evidence when asked.
21
21
 
@@ -30,6 +30,7 @@ npm install -g agent-device@latest
30
30
  agent-device doctor
31
31
  agent-device --version
32
32
  agent-device help workflow
33
+ agent-device help tv
33
34
  ```
34
35
 
35
36
  Run `agent-device doctor` yourself before handing the CLI to an agent. The installed CLI help defines current behavior. `agent-device help workflow` links to guides for debugging, replay, React Native profiling, and other tasks.
@@ -108,7 +109,7 @@ See [Commands](https://oss.callstack.com/agent-device/docs/commands) for the com
108
109
 
109
110
  ## How it works
110
111
 
111
- `agent-device` keeps device state in sessions. It sends commands to XCTest on iOS and tvOS, ADB and the snapshot helper on Android, a local helper on macOS, and AT-SPI on Linux.
112
+ `agent-device` keeps device state in sessions. It sends commands to XCTest on iOS and tvOS, ADB and the snapshot helper on Android, Vega CLI/VDA on the Vega Virtual Device, a local helper on macOS, and AT-SPI on Linux.
112
113
 
113
114
  Node.js apps can use the typed client or public subpaths. `agent-device/android-adb` provides the Android ADB provider interface, helpers for logcat, the clipboard, the keyboard, and apps, and port reverse management.
114
115
 
@@ -0,0 +1 @@
1
+ 8dc8135211465ef283d1c5175024638becb783b6975ecfd0b9a3ea38637eaa1c agent-device-android-ime-helper-0.20.1.apk
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "android-ime-helper",
3
- "version": "0.20.0",
4
- "assetName": "agent-device-android-ime-helper-0.20.0.apk",
5
- "sha256": "315907205b4d573cbfe4dfa4bcb7bafd360d9f6d93427f255574d7f02ea6656e",
3
+ "version": "0.20.1",
4
+ "assetName": "agent-device-android-ime-helper-0.20.1.apk",
5
+ "sha256": "8dc8135211465ef283d1c5175024638becb783b6975ecfd0b9a3ea38637eaa1c",
6
6
  "packageName": "com.callstack.agentdevice.imehelper",
7
- "versionCode": 20000,
7
+ "versionCode": 20001,
8
8
  "serviceComponent": "com.callstack.agentdevice.imehelper/.TestInputMethodService",
9
9
  "broadcastProtocol": "android-ime-helper-v1"
10
10
  }
@@ -0,0 +1 @@
1
+ 26ae795374725ac91a513dc2943521fdc16bf67c25e91cb1f49d0fa62f61c85e agent-device-android-snapshot-helper-0.20.1.apk
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "android-snapshot-helper",
3
- "version": "0.20.0",
4
- "releaseTag": "v0.20.0",
5
- "assetName": "agent-device-android-snapshot-helper-0.20.0.apk",
3
+ "version": "0.20.1",
4
+ "releaseTag": "v0.20.1",
5
+ "assetName": "agent-device-android-snapshot-helper-0.20.1.apk",
6
6
  "apkUrl": null,
7
- "sha256": "d635fe2e577ece9d5924719f907232693231644d69ba08b7d21d7bea04baa652",
8
- "checksumName": "agent-device-android-snapshot-helper-0.20.0.apk.sha256",
7
+ "sha256": "26ae795374725ac91a513dc2943521fdc16bf67c25e91cb1f49d0fa62f61c85e",
8
+ "checksumName": "agent-device-android-snapshot-helper-0.20.1.apk.sha256",
9
9
  "packageName": "com.callstack.agentdevice.snapshothelper",
10
- "versionCode": 20000,
10
+ "versionCode": 20001,
11
11
  "instrumentationRunner": "com.callstack.agentdevice.snapshothelper/.SnapshotInstrumentation",
12
12
  "minSdk": 23,
13
13
  "targetSdk": 36,
@@ -755,6 +755,28 @@ extension RunnerTests {
755
755
  }
756
756
  case .uptime:
757
757
  return executeUptime()
758
+ case .activate:
759
+ guard
760
+ let bundleId = command.appBundleId?.trimmingCharacters(in: .whitespacesAndNewlines),
761
+ !bundleId.isEmpty
762
+ else {
763
+ return Response(ok: false, error: ErrorPayload(message: "activate requires appBundleId"))
764
+ }
765
+ // prepareActiveCommandContext already activated this bundle. Keep this case as the
766
+ // explicit acknowledgement after that preflight, not as a second activation.
767
+ return Response(ok: true, data: DataPayload(message: "app activated"))
768
+ case .terminate:
769
+ guard
770
+ let bundleId = command.appBundleId?.trimmingCharacters(in: .whitespacesAndNewlines),
771
+ !bundleId.isEmpty
772
+ else {
773
+ return Response(ok: false, error: ErrorPayload(message: "terminate requires appBundleId"))
774
+ }
775
+ XCUIApplication(bundleIdentifier: bundleId).terminate()
776
+ if currentBundleId == bundleId {
777
+ invalidateCachedTarget(reason: "target_terminated")
778
+ }
779
+ return Response(ok: true, data: DataPayload(message: "app terminated"))
758
780
  default:
759
781
  break
760
782
  }
@@ -844,7 +866,7 @@ extension RunnerTests {
844
866
  ) throws -> Response {
845
867
  var activeApp = activeApp
846
868
  switch command.command {
847
- case .status, .targetReset, .shutdown, .recordStart, .recordStop, .uptime:
869
+ case .status, .activate, .terminate, .targetReset, .shutdown, .recordStart, .recordStop, .uptime:
848
870
  return Response(
849
871
  ok: false,
850
872
  error: ErrorPayload(
@@ -1231,6 +1253,12 @@ extension RunnerTests {
1231
1253
  guard let pngData = runnerPngData(for: screenshot.image) else {
1232
1254
  return Response(ok: false, error: ErrorPayload(message: "Failed to encode screenshot as PNG"))
1233
1255
  }
1256
+ if command.inlineScreenshot == true {
1257
+ return Response(
1258
+ ok: true,
1259
+ data: DataPayload(imageBase64: pngData.base64EncodedString())
1260
+ )
1261
+ }
1234
1262
  let fileName = "screenshot-\(Int(Date().timeIntervalSince1970 * 1000)).png"
1235
1263
  let filePath = (NSTemporaryDirectory() as NSString).appendingPathComponent(fileName)
1236
1264
  do {
@@ -1264,15 +1292,35 @@ extension RunnerTests {
1264
1292
  else {
1265
1293
  return Response(ok: false, error: ErrorPayload(message: "rotate requires orientation"))
1266
1294
  }
1267
- if rotateDevice(to: orientation) {
1295
+ let supportedOrientations = [
1296
+ "portrait",
1297
+ "portrait-upside-down",
1298
+ "landscape-left",
1299
+ "landscape-right",
1300
+ ]
1301
+ guard supportedOrientations.contains(orientation) else {
1268
1302
  return Response(
1269
- ok: true,
1270
- data: DataPayload(message: "rotate", orientation: orientation)
1303
+ ok: false,
1304
+ error: ErrorPayload(message: "unsupported rotate orientation: \(orientation)")
1305
+ )
1306
+ }
1307
+ guard let observedOrientation = rotateDevice(to: orientation) else {
1308
+ return Response(
1309
+ ok: false,
1310
+ error: ErrorPayload(message: "unable to observe device orientation after rotate")
1311
+ )
1312
+ }
1313
+ guard observedOrientation == orientation else {
1314
+ return Response(
1315
+ ok: false,
1316
+ error: ErrorPayload(
1317
+ message: "device orientation is \(observedOrientation), expected \(orientation)"
1318
+ )
1271
1319
  )
1272
1320
  }
1273
1321
  return Response(
1274
- ok: false,
1275
- error: ErrorPayload(message: "unsupported rotate orientation: \(orientation)")
1322
+ ok: true,
1323
+ data: DataPayload(message: "rotate", orientation: observedOrientation)
1276
1324
  )
1277
1325
  case .appSwitcher:
1278
1326
  performAppSwitcherGesture(app: activeApp)
@@ -147,7 +147,7 @@ final class RunnerCommandJournal {
147
147
  .remotePress, .type, .swipe, .scroll, .desktopScroll, .findText, .querySelector, .readText, .back,
148
148
  .backInApp, .backSystem, .home, .rotate, .appSwitcher, .keyboardDismiss, .keyboardReturn,
149
149
  .alert, .sequence, .gesture, .gestureViewport, .recordStart, .recordStop,
150
- .status, .uptime, .targetReset, .shutdown:
150
+ .status, .uptime, .activate, .terminate, .targetReset, .shutdown:
151
151
  return true
152
152
  }
153
153
  }
@@ -118,9 +118,9 @@ extension RunnerTests {
118
118
  #endif
119
119
  }
120
120
 
121
- func rotateDevice(to orientationName: String) -> Bool {
121
+ func rotateDevice(to orientationName: String) -> String? {
122
122
  #if os(macOS) || os(tvOS) || os(visionOS)
123
- return false
123
+ return nil
124
124
  #else
125
125
  switch orientationName {
126
126
  case "portrait":
@@ -132,10 +132,21 @@ extension RunnerTests {
132
132
  case "landscape-right":
133
133
  XCUIDevice.shared.orientation = .landscapeRight
134
134
  default:
135
- return false
135
+ return nil
136
136
  }
137
137
  sleepFor(0.2)
138
- return true
138
+ switch XCUIDevice.shared.orientation {
139
+ case .portrait:
140
+ return "portrait"
141
+ case .portraitUpsideDown:
142
+ return "portrait-upside-down"
143
+ case .landscapeLeft:
144
+ return "landscape-left"
145
+ case .landscapeRight:
146
+ return "landscape-right"
147
+ default:
148
+ return nil
149
+ }
139
150
  #endif
140
151
  }
141
152
 
@@ -31,6 +31,8 @@ enum CommandType: String, Codable {
31
31
  case recordStop
32
32
  case status
33
33
  case uptime
34
+ case activate
35
+ case terminate
34
36
  case targetReset
35
37
  case shutdown
36
38
  }
@@ -90,7 +92,7 @@ extension CommandType {
90
92
  return CommandTraits(isInteraction: false, readOnly: .conditional, isLifecycle: false)
91
93
 
92
94
  // Runner-lifecycle commands: skip the app-activation preflight.
93
- case .recordStop, .uptime, .targetReset, .shutdown:
95
+ case .recordStop, .uptime, .terminate, .targetReset, .shutdown:
94
96
  return CommandTraits(isInteraction: false, readOnly: .never, isLifecycle: true)
95
97
 
96
98
  case .status:
@@ -101,7 +103,7 @@ extension CommandType {
101
103
  // guard interacts with bespoke macOS activation, so classifying it needs a macOS smoke
102
104
  // check first (tracked as a follow-up). Also preserved: querySelector is NOT read-only;
103
105
  // recordStart is NOT a lifecycle command; home/alert remain non-interaction by design.
104
- case .mouseClick, .querySelector, .home, .recordStart:
106
+ case .mouseClick, .querySelector, .home, .recordStart, .activate:
105
107
  return CommandTraits(isInteraction: false, readOnly: .never, isLifecycle: false)
106
108
  }
107
109
  }
@@ -118,7 +120,6 @@ struct Command: Codable {
118
120
  let allowNonHittableCoordinateFallback: Bool?
119
121
  let delayMs: Int?
120
122
  let textEntryMode: String?
121
- let clearFirst: Bool?
122
123
  let action: String?
123
124
  let x: Double?
124
125
  let y: Double?
@@ -141,6 +142,7 @@ struct Command: Codable {
141
142
  let scope: String?
142
143
  let raw: Bool?
143
144
  let fullscreen: Bool?
145
+ let inlineScreenshot: Bool?
144
146
  let synthesized: Bool?
145
147
  let steps: [SequenceStep]?
146
148
  }
@@ -228,6 +230,7 @@ extension Response {
228
230
 
229
231
  struct DataPayload: Codable {
230
232
  let message: String?
233
+ let imageBase64: String?
231
234
  let text: String?
232
235
  let found: Bool?
233
236
  let items: [String]?
@@ -267,6 +270,7 @@ struct DataPayload: Codable {
267
270
 
268
271
  init(
269
272
  message: String? = nil,
273
+ imageBase64: String? = nil,
270
274
  text: String? = nil,
271
275
  found: Bool? = nil,
272
276
  items: [String]? = nil,
@@ -305,6 +309,7 @@ struct DataPayload: Codable {
305
309
  sequenceResults: [SequenceStepResult]? = nil
306
310
  ) {
307
311
  self.message = message
312
+ self.imageBase64 = imageBase64
308
313
  self.text = text
309
314
  self.found = found
310
315
  self.items = items
@@ -57,12 +57,16 @@ extension RunnerTests {
57
57
  case .collectionView: return "CollectionView"
58
58
  case .table: return "Table"
59
59
  case .scrollView: return "ScrollView"
60
+ case .toolbar: return "Toolbar"
60
61
  case .searchField: return "SearchField"
61
62
  case .segmentedControl: return "SegmentedControl"
62
63
  case .stepper: return "Stepper"
63
64
  case .picker: return "Picker"
65
+ case .activityIndicator: return "ActivityIndicator"
66
+ case .progressIndicator: return "ProgressIndicator"
64
67
  case .checkBox: return "CheckBox"
65
68
  case .menuItem: return "MenuItem"
69
+ case .webView: return "WebView"
66
70
  case .other: return "Other"
67
71
  default:
68
72
  switch type.rawValue {
@@ -70,8 +74,6 @@ extension RunnerTests {
70
74
  return "Keyboard"
71
75
  case 20:
72
76
  return "Key"
73
- case 24:
74
- return "SearchField"
75
77
  default:
76
78
  return "Element(\(type.rawValue))"
77
79
  }
@@ -240,7 +240,7 @@ extension RunnerTests {
240
240
  case "replace":
241
241
  return .replacement
242
242
  default:
243
- return command.clearFirst == true ? .replacement : .none
243
+ return .none
244
244
  }
245
245
  }
246
246
 
@@ -121,7 +121,8 @@ final class RunnerTests: XCTestCase {
121
121
  .tabBar,
122
122
  .textField,
123
123
  .secureTextField,
124
- .textView
124
+ .textView,
125
+ .webView
125
126
  ]
126
127
  // Keep blocker actions narrow to avoid false positives from generic hittable containers.
127
128
  let actionableTypes: Set<XCUIElement.ElementType> = [