agent-device 0.20.0 → 0.20.2

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 (128) hide show
  1. package/README.md +2 -3
  2. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.20.0.apk → agent-device-android-ime-helper-0.20.2.apk} +0 -0
  3. package/android/ime-helper/dist/agent-device-android-ime-helper-0.20.2.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.2.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.2.apk} +0 -0
  6. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.2.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.2.manifest.json} +6 -6
  8. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Alert.swift +2 -0
  9. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +30 -16
  10. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandJournal.swift +1 -1
  11. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +0 -21
  12. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +8 -3
  13. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Orientation.swift +92 -0
  14. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +4 -2
  15. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntry.swift +1 -1
  16. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +2 -1
  17. package/dist/src/agent-device-client.js +2 -2
  18. package/dist/src/android-input-method-overlays.js +1 -1
  19. package/dist/src/android.js +1 -1
  20. package/dist/src/app-lifecycle.js +5 -5
  21. package/dist/src/app-log-request-scope.js +1 -1
  22. package/dist/src/app-resolution.js +1 -1
  23. package/dist/src/apple-multitouch-support.js +1 -1
  24. package/dist/src/apps.js +5 -5
  25. package/dist/src/boot-diagnostics.js +2 -2
  26. package/dist/src/cli-config.js +1 -1
  27. package/dist/src/cli-help.js +81 -24
  28. package/dist/src/cli-runner.js +1 -1
  29. package/dist/src/cli.js +14 -14
  30. package/dist/src/click-button.d.ts +5 -0
  31. package/dist/src/client-metro.js +1 -0
  32. package/dist/src/command-metadata.js +1 -1
  33. package/dist/src/command-schema.js +3 -3
  34. package/dist/src/compatibility-policy.js +1 -0
  35. package/dist/src/context.js +1 -1
  36. package/dist/src/contracts.d.ts +2 -2
  37. package/dist/src/debug-symbols.js +1 -1
  38. package/dist/src/device-input-state.js +1 -1
  39. package/dist/src/device.js +9 -0
  40. package/dist/src/devices2.js +1 -1
  41. package/dist/src/devices4.js +1 -0
  42. package/dist/src/find.js +1 -1
  43. package/dist/src/finders.js +1 -1
  44. package/dist/src/generic.js +1 -1
  45. package/dist/src/gesture-plan-types.js +1 -0
  46. package/dist/src/gesture-plan.js +1 -0
  47. package/dist/src/index.d.ts +1205 -1309
  48. package/dist/src/input-actions.js +4 -4
  49. package/dist/src/install-artifact.js +5 -1
  50. package/dist/src/install-artifact2.js +1 -0
  51. package/dist/src/interaction-snapshot.js +1 -1
  52. package/dist/src/interaction.d.ts +3 -6
  53. package/dist/src/interaction.js +1 -1
  54. package/dist/src/interactor.js +2 -2
  55. package/dist/src/interactor2.js +1 -0
  56. package/dist/src/internal/bin.js +1 -1
  57. package/dist/src/internal/daemon.js +14 -22
  58. package/dist/src/lease-context.js +1 -1
  59. package/dist/src/lease.js +1 -1
  60. package/dist/src/limrun.d.ts +361 -0
  61. package/dist/src/limrun.js +1 -0
  62. package/dist/src/linux.js +1 -1
  63. package/dist/src/logcat.js +1 -5
  64. package/dist/src/method-scope.js +1 -0
  65. package/dist/src/metro.d.ts +72 -3
  66. package/dist/src/metro.js +1 -1
  67. package/dist/src/metro2.js +1 -1
  68. package/dist/src/notifications.js +1 -1
  69. package/dist/src/physical-device-control.js +1 -0
  70. package/dist/src/plist.js +1 -0
  71. package/dist/src/react-native.js +1 -1
  72. package/dist/src/recording-export-quality.js +15 -0
  73. package/dist/src/register-builtins.js +1 -1
  74. package/dist/src/registry.js +24 -24
  75. package/dist/src/remote-config.d.ts +5 -2
  76. package/dist/src/remote-config.js +1 -1
  77. package/dist/src/remote-config2.js +1 -0
  78. package/dist/src/reporting.js +8 -8
  79. package/dist/src/runner-client.js +1 -1
  80. package/dist/src/runner-disposal.js +1 -2
  81. package/dist/src/runner-lease.js +1 -1
  82. package/dist/src/runner-provider.js +1 -1
  83. package/dist/src/runtime-transport.js +1 -1
  84. package/dist/src/runtime.js +3 -3
  85. package/dist/src/runtime2.js +1 -1
  86. package/dist/src/sdk-android-adb.d.ts +4 -24
  87. package/dist/src/sdk-android-adb.js +4 -4
  88. package/dist/src/sdk-batch-runner.d.ts +13 -3
  89. package/dist/src/sdk-batch-runner.js +3 -2
  90. package/dist/src/sdk-contracts.d.ts +31 -91
  91. package/dist/src/sdk-contracts.js +2 -2
  92. package/dist/src/sdk-device.js +1 -1
  93. package/dist/src/sdk-finders.d.ts +3 -2
  94. package/dist/src/sdk-finders.js +1 -1
  95. package/dist/src/sdk-remote-config.d.ts +83 -53
  96. package/dist/src/sdk-selectors.js +1 -1
  97. package/dist/src/selector-runtime.js +1 -1
  98. package/dist/src/selector-vocabulary.js +1 -1
  99. package/dist/src/selectors.d.ts +1 -1
  100. package/dist/src/selectors.js +1 -1
  101. package/dist/src/server.js +2 -2
  102. package/dist/src/session-target-evidence.js +1 -1
  103. package/dist/src/session.js +8 -8
  104. package/dist/src/simulator.js +1 -1
  105. package/dist/src/snapshot-diagnostics.js +4 -4
  106. package/dist/src/snapshot2.js +1 -1
  107. package/dist/src/tool-provider.js +3 -3
  108. package/dist/src/tool-provider2.js +1 -1
  109. package/dist/src/tool-provider3.js +1 -0
  110. package/dist/src/tv-remote.d.ts +213 -0
  111. package/dist/src/unsupported-interactor.js +1 -0
  112. package/dist/src/web.js +1 -1
  113. package/dist/src/xml.js +2 -0
  114. package/package.json +41 -19
  115. package/server.json +2 -2
  116. package/skills/agent-device/SKILL.md +5 -3
  117. package/smithery.yaml +1 -1
  118. package/android/ime-helper/dist/agent-device-android-ime-helper-0.20.0.apk.sha256 +0 -1
  119. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.0.apk.sha256 +0 -1
  120. package/dist/src/android-system-chrome.js +0 -1
  121. package/dist/src/client-metro-companion.js +0 -1
  122. package/dist/src/config.js +0 -1
  123. package/dist/src/daemon-shutdown-report.js +0 -1
  124. package/dist/src/project-runtime.js +0 -1
  125. package/dist/src/scoped-provider.js +0 -1
  126. package/dist/src/sdk-batch.js +0 -2
  127. package/dist/src/snapshot-quality.js +0 -1
  128. package/dist/src/tv-remote.js +0 -15
@@ -1,244 +1,55 @@
1
- import { B as DeviceKind, C as DebugSymbolsResult, D as isAgentDeviceError, I as SnapshotUnchanged, L as SnapshotVisibility, M as ScreenshotOverlayRef, N as SnapshotNode, O as normalizeAgentDeviceError, P as SnapshotQualityVerdict, R as centerOfRect, S as DebugSymbolsOptions, T as NormalizedError, U as PlatformSelector, V as DeviceTarget, W as PublicPlatform, _ as NetworkIncludeMode, a as DaemonLockPolicy, b as SessionIsolationMode, c as DaemonResponseData, i as DaemonInstallSource, m as LeaseBackend, n as DaemonArtifactType, o as DaemonRequest, r as DaemonError, s as DaemonResponse, t as DaemonArtifact, w as AppError, x as SessionRuntimeHints, y as ResponseLevel, z as AppleOS } from "./sdk-contracts.js";
2
- import { a as GesturePointerCount, c as SwipePreset, l as TransformGestureParams, o as ScrollDirection, s as SwipePattern, u as AndroidSnapshotBackendMetadata } from "./sdk-android-adb.js";
1
+ import { A as SnapshotNode, B as DeviceTarget, C as normalizeAgentDeviceError, F as SnapshotVisibility, H as PlatformSelector, I as centerOfRect, L as AppleOS, P as SnapshotUnchanged, S as isAgentDeviceError, U as PublicPlatform, _ as SessionIsolationMode, a as DaemonLockPolicy, b as NormalizedError, c as DaemonResponseData, g as ResponseLevel, i as DaemonInstallSource, m as NetworkIncludeMode, o as DaemonRequest, p as LeaseBackend, r as DaemonError, s as DaemonResponse, t as DaemonArtifact, v as SessionRuntimeHints, y as AppError, z as DeviceKind } from "./sdk-contracts.js";
2
+ import { c as ScrollInputDirection, d as TransformGestureParams, l as SwipePattern, o as GesturePointerCount, s as ScrollDirection, u as SwipePreset } from "./sdk-android-adb.js";
3
3
  import { t as AppsFilter } from "./sdk-app-inventory.js";
4
4
  import { t as BatchRunResult } from "./sdk-batch-runner.js";
5
5
  import { r as FindLocator } from "./sdk-finders.js";
6
- import { a as LongPressCommandResponseData, i as FindCommandResponseData, o as PressCommandResponseData, r as FillCommandResponseData, s as ClickButton, t as ClickCommandResponseData } from "./interaction.js";
6
+ import { a as RemoteConnectionProfileFields, c as MetroPrepareResult, i as CloudProviderProfileFields, l as MetroReloadOptions, s as MetroPrepareOptions, u as MetroReloadResult } from "./sdk-remote-config.js";
7
+ import { c as RecordingExportQuality, d as PublicSnapshotCaptureAnnotations, i as AgentArtifactsResult, l as ScreenshotResultData, m as JsonObject, n as DeviceRotation, o as CloudProviderSessionResult, p as SessionSurface, r as AlertAction, s as BackMode, t as TvRemoteButton, u as SnapshotDiagnosticsSummary } from "./tv-remote.js";
8
+ import { t as ClickButton } from "./click-button.js";
9
+ import { a as LongPressCommandResponseData, i as FindCommandResponseData, o as PressCommandResponseData, r as FillCommandResponseData, t as ClickCommandResponseData } from "./interaction.js";
7
10
  import { p as createLocalArtifactAdapter } from "./sdk-io.js";
8
- import { a as PrepareMetroRuntimeResult, c as MetroBridgeScope, i as MetroPrepareKind, o as ReloadMetroResult, r as RemoteConnectionProfileFields, t as CloudProviderProfileFields } from "./sdk-remote-config.js";
9
- //#region src/snapshot-capture-annotations.d.ts
10
- type SnapshotCaptureAnalysis = {
11
- rawNodeCount: number;
12
- maxDepth: number;
13
- };
14
- type SnapshotCaptureFreshness = {
15
- action: string;
16
- retryCount: number;
17
- staleAfterRetries: boolean;
18
- reason?: 'empty-interactive' | 'sharp-drop' | 'stuck-route';
19
- };
20
- type SnapshotCaptureAnnotations = {
21
- analysis?: SnapshotCaptureAnalysis;
22
- androidSnapshot?: AndroidSnapshotBackendMetadata;
23
- freshness?: SnapshotCaptureFreshness;
24
- quality?: SnapshotQualityVerdict;
25
- warnings?: string[];
26
- };
27
- type PublicSnapshotCaptureAnnotations = Pick<SnapshotCaptureAnnotations, 'androidSnapshot' | 'warnings'> & {
28
- snapshotQuality?: SnapshotQualityVerdict;
29
- };
30
- //#endregion
31
- //#region src/snapshot-diagnostics.d.ts
32
- type SnapshotTimingStats = {
33
- count: number;
34
- p50Ms: number;
35
- p95Ms: number;
36
- maxMs: number;
37
- slowThresholdMs: number;
38
- platform?: PublicPlatform;
39
- backends?: Record<string, number>;
40
- };
41
- type SnapshotDiagnosticsSummary = {
42
- stats: SnapshotTimingStats;
43
- warning?: string;
44
- };
45
- //#endregion
46
- //#region src/contracts/back-mode.d.ts
47
- declare const BACK_MODES: readonly ['in-app', 'system'];
48
- type BackMode = (typeof BACK_MODES)[number];
49
- //#endregion
50
- //#region src/contracts/device-rotation.d.ts
51
- declare const DEVICE_ROTATIONS: readonly ['portrait', 'portrait-upside-down', 'landscape-left', 'landscape-right'];
52
- type DeviceRotation = (typeof DEVICE_ROTATIONS)[number];
53
- //#endregion
54
- //#region src/contracts/tv-remote.d.ts
55
- declare const TV_REMOTE_BUTTON_DEFINITIONS: {
56
- readonly up: {
57
- readonly aliases: readonly [];
58
- readonly androidKeyevent: 'KEYCODE_DPAD_UP';
59
- readonly appleRemoteButton: 'up';
60
- };
61
- readonly down: {
62
- readonly aliases: readonly [];
63
- readonly androidKeyevent: 'KEYCODE_DPAD_DOWN';
64
- readonly appleRemoteButton: 'down';
65
- };
66
- readonly left: {
67
- readonly aliases: readonly [];
68
- readonly androidKeyevent: 'KEYCODE_DPAD_LEFT';
69
- readonly appleRemoteButton: 'left';
70
- };
71
- readonly right: {
72
- readonly aliases: readonly [];
73
- readonly androidKeyevent: 'KEYCODE_DPAD_RIGHT';
74
- readonly appleRemoteButton: 'right';
75
- };
76
- readonly select: {
77
- readonly aliases: readonly ["ok", "center", "enter"];
78
- readonly androidKeyevent: 'KEYCODE_DPAD_CENTER';
79
- readonly appleRemoteButton: 'select';
80
- };
81
- readonly menu: {
82
- readonly aliases: readonly [];
83
- readonly androidKeyevent: 'KEYCODE_MENU';
84
- readonly appleRemoteButton: 'menu';
85
- };
86
- readonly home: {
87
- readonly aliases: readonly [];
88
- readonly androidKeyevent: 'KEYCODE_HOME';
89
- readonly appleRemoteButton: 'home';
90
- };
91
- readonly back: {
92
- readonly aliases: readonly [];
93
- readonly androidKeyevent: 'KEYCODE_BACK';
94
- readonly appleRemoteButton: 'menu';
95
- };
96
- };
97
- declare const TV_REMOTE_BUTTONS: (keyof typeof TV_REMOTE_BUTTON_DEFINITIONS)[];
98
- type TvRemoteButton = (typeof TV_REMOTE_BUTTONS)[number];
99
- //#endregion
100
- //#region src/contracts/navigation.d.ts
101
- /**
102
- * Closed results of the navigation/global action commands. Each mirrors the
103
- * dispatch handler's literal return EXACTLY (src/core/dispatch.ts
104
- * `DISPATCH_HANDLERS`): a fixed `action` discriminant plus the always-present
105
- * `successText` message (the handlers always pass a non-empty message, so it is
106
- * required here). The handlers spread nothing else, so the shapes are closed —
107
- * consistent with the `viewport` contract, the generic-dispatch Android
108
- * dialog-recovery `warning` annotation is intentionally not part of the contract.
109
- */
110
- /** `home` — `{ action: 'home', message: 'Home' }`. */
111
- type HomeCommandResult = {
112
- action: 'home';
113
- message: string;
114
- };
115
- /** `back` — `{ action: 'back', mode, message: 'Back' }`; `mode` defaults to `'in-app'`. */
116
- type BackCommandResult = {
117
- action: 'back';
118
- mode: BackMode;
119
- message: string;
120
- };
121
- /** `orientation` — `{ action: 'orientation', orientation, message: 'Rotated to <orientation>' }`. */
122
- type OrientationCommandResult = {
123
- action: 'orientation';
124
- orientation: DeviceRotation;
125
- message: string;
126
- };
127
- /**
128
- * @deprecated The `rotate` command was renamed to `orientation`. This is the
129
- * legacy response contract (`action: 'rotate'`) that shipped in v0.18/v0.19,
130
- * retained for existing SDK consumers until the next major version. New code
131
- * should use {@link OrientationCommandResult}.
132
- */
133
- type RotateCommandResult = {
134
- action: 'rotate';
135
- orientation: DeviceRotation;
136
- message: string;
137
- };
138
- /** `app-switcher` — `{ action: 'app-switcher', message: 'Opened app switcher' }`. */
139
- type AppSwitcherCommandResult = {
140
- action: 'app-switcher';
141
- message: string;
142
- };
143
- /** `tv-remote` — `{ action: 'tv-remote', button, durationMs?, message }`. */
144
- type TvRemoteCommandResult = {
145
- action: 'tv-remote';
146
- button: TvRemoteButton;
147
- durationMs?: number;
148
- message: string;
149
- };
150
- //#endregion
151
- //#region src/core/recording-export-quality.d.ts
152
- declare const RECORDING_EXPORT_QUALITIES: readonly ['medium', 'high'];
153
- type RecordingExportQuality = (typeof RECORDING_EXPORT_QUALITIES)[number];
154
- //#endregion
155
- //#region src/contracts/recording-scope.d.ts
156
- declare const RECORDING_SCOPE_VALUES: readonly ['app', 'device', 'system'];
157
- type RecordingScope = (typeof RECORDING_SCOPE_VALUES)[number];
158
- //#endregion
159
- //#region src/alert-contract.d.ts
160
- declare const ALERT_ACTIONS: readonly ['get', 'accept', 'dismiss', 'wait'];
161
- type AlertAction = (typeof ALERT_ACTIONS)[number];
162
- //#endregion
163
- //#region src/contracts/json.d.ts
164
- type JsonPrimitive = string | number | boolean | null;
165
- type JsonValue = JsonPrimitive | JsonObject | JsonValue[];
166
- type JsonObject = {
167
- [key: string]: JsonValue;
168
- };
169
- //#endregion
170
- //#region src/commands/command-contract.d.ts
171
- type JsonSchema = {
172
- type?: string | readonly string[];
173
- description?: string;
174
- properties?: Record<string, JsonSchema>;
175
- required?: readonly string[];
176
- additionalProperties?: boolean | JsonSchema;
177
- items?: JsonSchema;
178
- prefixItems?: readonly JsonSchema[];
179
- oneOf?: readonly JsonSchema[];
180
- enum?: readonly unknown[];
181
- const?: unknown;
182
- minimum?: number;
183
- maximum?: number;
184
- };
185
- type ExecutableCommandProjection<ClientMethod extends string = string> = {
186
- clientMethod: ClientMethod;
187
- outputSchema: JsonSchema;
188
- };
189
- //#endregion
190
- //#region src/contracts/session-surface.d.ts
191
- declare const SESSION_SURFACES: readonly ['app', 'frontmost-app', 'desktop', 'menubar'];
192
- type SessionSurface = (typeof SESSION_SURFACES)[number];
193
- //#endregion
194
- //#region src/utils/screenshot-result.d.ts
195
- type ScreenshotResultData = {
196
- path?: string;
197
- width?: number;
198
- height?: number;
199
- logicalWidth?: number;
200
- logicalHeight?: number;
201
- pixelDensity?: number;
202
- overlayRefs?: ScreenshotOverlayRef[];
11
+ //#region src/contracts/client-connection.d.ts
12
+ type AgentDeviceDaemonTransport = (req: Omit<DaemonRequest, 'token'>) => Promise<DaemonResponse>;
13
+ type AgentDeviceClientConfig = RemoteConnectionProfileFields & CloudProviderProfileFields & {
14
+ session?: string;
15
+ lockPolicy?: DaemonLockPolicy;
16
+ lockPlatform?: PlatformSelector;
17
+ requestId?: string;
18
+ sessionIsolation?: SessionIsolationMode;
19
+ leaseBackend?: LeaseBackend;
20
+ leaseTtlMs?: number;
21
+ runtime?: SessionRuntimeHints;
22
+ cwd?: string;
23
+ debug?: boolean;
24
+ cost?: boolean;
25
+ responseLevel?: ResponseLevel;
26
+ iosXctestrunFile?: string;
27
+ iosXctestDerivedDataPath?: string;
28
+ iosXctestEnvDir?: string;
203
29
  };
204
- //#endregion
205
- //#region src/commands/interaction/runtime/gestures.d.ts
206
- declare const SCROLL_INPUT_DIRECTIONS: readonly ['up', 'down', 'left', 'right', 'top', 'bottom'];
207
- type ScrollInputDirection = (typeof SCROLL_INPUT_DIRECTIONS)[number];
208
- //#endregion
209
- //#region src/commands/system/navigation-projection.d.ts
210
- declare const navigationCommandProjectionType: unique symbol;
211
- type NavigationCommandProjection<Options, Result, Required extends boolean, ClientMethod extends string> = ExecutableCommandProjection<ClientMethod> & {
212
- readonly [navigationCommandProjectionType]?: {
213
- options: Options;
214
- result: Result;
215
- required: Required;
216
- };
30
+ type AgentDeviceRequestOverrides = Pick<AgentDeviceClientConfig, 'session' | 'lockPolicy' | 'lockPlatform' | 'requestId' | 'daemonBaseUrl' | 'daemonAuthToken' | 'daemonTransport' | 'daemonServerMode' | 'tenant' | 'sessionIsolation' | 'runId' | 'leaseId' | 'leaseBackend' | 'leaseProvider' | 'deviceKey' | 'clientId' | 'providerApp' | 'providerOsVersion' | 'providerProject' | 'providerBuild' | 'providerSessionName' | 'awsProjectArn' | 'awsDeviceArn' | 'awsAppArn' | 'awsRegion' | 'awsInteractionMode' | 'leaseTtlMs' | 'cwd' | 'debug' | 'cost' | 'responseLevel' | 'iosXctestrunFile' | 'iosXctestDerivedDataPath' | 'iosXctestEnvDir'>;
31
+ type AgentDeviceIdentifiers = {
32
+ session?: string;
33
+ deviceId?: string;
34
+ deviceName?: string;
35
+ udid?: string;
36
+ serial?: string;
37
+ appId?: string;
38
+ appBundleId?: string;
39
+ package?: string;
217
40
  };
218
- declare const NAVIGATION_COMMAND_PROJECTIONS: {
219
- readonly back: NavigationCommandProjection<{
220
- mode?: BackMode;
221
- }, BackCommandResult, false, "back">;
222
- readonly home: NavigationCommandProjection<{}, HomeCommandResult, false, "home">;
223
- readonly orientation: NavigationCommandProjection<{
224
- orientation: DeviceRotation;
225
- }, OrientationCommandResult, true, "orientation">;
226
- readonly 'app-switcher': NavigationCommandProjection<{}, AppSwitcherCommandResult, false, "appSwitcher">;
227
- readonly 'tv-remote': NavigationCommandProjection<{
228
- button: TvRemoteButton;
229
- durationMs?: number;
230
- }, TvRemoteCommandResult, true, "tvRemote">;
41
+ type AgentDeviceSelectionOptions = {
42
+ platform?: PlatformSelector;
43
+ target?: DeviceTarget;
44
+ device?: string;
45
+ udid?: string;
46
+ serial?: string;
47
+ iosSimulatorDeviceSet?: string;
48
+ androidDeviceAllowlist?: string;
231
49
  };
232
- type NavigationCommandName = keyof typeof NAVIGATION_COMMAND_PROJECTIONS;
233
- type NavigationCommandOptions<Name extends NavigationCommandName> = (typeof NAVIGATION_COMMAND_PROJECTIONS)[Name] extends NavigationCommandProjection<infer Options, unknown, boolean, string> ? Options : never;
234
- type ProjectedNavigationCommandMethod<BaseOptions, Projection> = Projection extends NavigationCommandProjection<infer Options, infer Result, infer Required, string> ? Required extends true ? (options: BaseOptions & Options) => Promise<Result> : (options?: BaseOptions & Options) => Promise<Result> : never;
235
- type ProjectedNavigationCommandClient<BaseOptions> = { [Name in NavigationCommandName as (typeof NAVIGATION_COMMAND_PROJECTIONS)[Name]['clientMethod']]: ProjectedNavigationCommandMethod<BaseOptions, (typeof NAVIGATION_COMMAND_PROJECTIONS)[Name]>; };
236
- //#endregion
237
- //#region src/contracts/logs.d.ts
238
- declare const LOG_ACTION_VALUES: readonly ['path', 'start', 'stop', 'doctor', 'mark', 'clear'];
239
- type LogAction = (typeof LOG_ACTION_VALUES)[number];
50
+ type DeviceCommandBaseOptions = AgentDeviceRequestOverrides & AgentDeviceSelectionOptions;
240
51
  //#endregion
241
- //#region src/target-shutdown-contract.d.ts
52
+ //#region src/contracts/target-shutdown-contract.d.ts
242
53
  type TargetShutdownResult = {
243
54
  success: boolean;
244
55
  exitCode: number;
@@ -247,736 +58,382 @@ type TargetShutdownResult = {
247
58
  error?: NormalizedError;
248
59
  };
249
60
  //#endregion
250
- //#region src/contracts/perf.d.ts
251
- declare const PERF_AREA_VALUES: readonly ['metrics', 'frames', 'memory', 'cpu', 'trace'];
252
- declare const PERF_ACTION_VALUES: readonly ['sample', 'snapshot', 'start', 'stop', 'report'];
253
- declare const PERF_SUBJECT_VALUES: readonly ['profile'];
254
- declare const PERF_KIND_VALUES: readonly ['xctrace', 'simpleperf', 'perfetto', 'android-hprof', 'memgraph'];
255
- type PerfArea = (typeof PERF_AREA_VALUES)[number];
256
- type PerfAction = (typeof PERF_ACTION_VALUES)[number];
257
- type PerfSubject = (typeof PERF_SUBJECT_VALUES)[number];
258
- type PerfKind = (typeof PERF_KIND_VALUES)[number];
259
- //#endregion
260
- //#region src/contracts/device.d.ts
261
- /**
262
- * Closed result of the `boot` command. Mirrors the daemon handler's only
263
- * success return EXACTLY (src/daemon/handlers/session-state.ts) — the fixed
264
- * object literal `{ platform, target, device, id, kind, booted }` plus the
265
- * additive `appleOs` discriminant, emitted only for Apple devices.
266
- */
267
- type BootCommandResult = {
61
+ //#region src/contracts/client-device-view.d.ts
62
+ type AgentDeviceDevice = {
268
63
  platform: PublicPlatform;
269
64
  target: DeviceTarget;
270
- /** Human-readable device name (`device.name`). */
271
- device: string;
272
- /** Stable device id (`device.id`). */
273
- id: string;
274
65
  kind: DeviceKind;
275
- /** Always `true` on the success path. */
276
- booted: true;
66
+ id: string;
67
+ name: string;
68
+ booted?: boolean;
277
69
  /**
278
- * Additive Apple-OS discriminant (`device.appleOs`): iPhone/iPad/tvOS/visionOS/macOS.
279
- * Present only for Apple devices; absent for non-Apple platforms. `platform` stays the
280
- * leaf (`ios`/`macos`) — this is an extra field, not a replacement.
70
+ * Additive Apple-OS discriminant (iPhone/iPad/tvOS/visionOS/macOS). Present only for
71
+ * Apple devices; `platform` still carries the leaf (`ios`/`macos`).
281
72
  */
282
73
  appleOs?: AppleOS;
74
+ identifiers: AgentDeviceIdentifiers;
75
+ ios?: {
76
+ udid: string;
77
+ };
78
+ android?: {
79
+ serial: string;
80
+ };
81
+ vega?: {
82
+ serial: string;
83
+ };
283
84
  };
284
- /**
285
- * Closed result of the `shutdown` command. Mirrors the daemon handler's success
286
- * return EXACTLY (src/daemon/handlers/session-state.ts) — the fixed object
287
- * literal `{ platform, target, device, id, kind, shutdown }` plus the additive
288
- * `appleOs` discriminant (Apple devices only). The `shutdown` field is the raw
289
- * {@link TargetShutdownResult} from `shutdownDeviceTarget`.
290
- */
291
- type ShutdownCommandResult = {
85
+ type AgentDeviceCapabilitiesResult = {
86
+ device: AgentDeviceDevice;
87
+ availableCommands: string[];
88
+ };
89
+ type AgentDeviceSessionDevice = {
292
90
  platform: PublicPlatform;
293
91
  target: DeviceTarget;
294
- /** Human-readable device name (`device.name`). */
295
- device: string;
296
- /** Stable device id (`device.id`). */
297
92
  id: string;
298
- kind: DeviceKind;
299
- shutdown: TargetShutdownResult;
93
+ name: string;
300
94
  /**
301
- * Additive Apple-OS discriminant (`device.appleOs`): iPhone/iPad/tvOS/visionOS/macOS.
302
- * Present only for Apple devices; absent for non-Apple platforms. `platform` stays the
303
- * leaf (`ios`/`macos`) — this is an extra field, not a replacement.
95
+ * Additive Apple-OS discriminant (iPhone/iPad/tvOS/visionOS/macOS). Present only for
96
+ * Apple devices; `platform` still carries the leaf (`ios`/`macos`).
304
97
  */
305
98
  appleOs?: AppleOS;
99
+ identifiers: AgentDeviceIdentifiers;
100
+ ios?: {
101
+ udid: string;
102
+ simulatorSetPath?: string | null;
103
+ };
104
+ android?: {
105
+ serial: string;
106
+ };
107
+ vega?: {
108
+ serial: string;
109
+ };
306
110
  };
307
- //#endregion
308
- //#region src/contracts/viewport.d.ts
309
- /**
310
- * Closed result of the `viewport` command. Mirrors the dispatch handler's return
311
- * EXACTLY (src/core/dispatch.ts `handleViewportCommand`) — `{ width, height }`
312
- * plus the always-present `successText` message. The generic dispatch path
313
- * returns this object unchanged (viewport has no Android dialog guard, so no
314
- * `warning` is ever appended), so the shape is intentionally closed.
315
- */
316
- type ViewportCommandResult = {
317
- width: number;
318
- height: number;
319
- message: string;
320
- };
321
- //#endregion
322
- //#region src/contracts/clipboard.d.ts
323
- /**
324
- * Closed result of the `clipboard` command. Mirrors the dispatch handler's
325
- * literal return EXACTLY (src/core/dispatch.ts `handleClipboardCommand`): a
326
- * discriminated union on `action`. `read` returns the clipboard `text`; `write`
327
- * reports the written `textLength` plus the `successText` message. The handler
328
- * spreads nothing else, so each branch is closed.
329
- */
330
- type ClipboardCommandResult = {
331
- action: 'read';
332
- text: string;
333
- } | {
334
- action: 'write';
335
- textLength: number;
336
- message: string;
111
+ type AgentDeviceSession = {
112
+ name: string;
113
+ createdAt: number;
114
+ sessionStateDir?: string;
115
+ runnerLogPath?: string;
116
+ device: AgentDeviceSessionDevice;
117
+ identifiers: AgentDeviceIdentifiers;
337
118
  };
338
- //#endregion
339
- //#region src/contracts/app-state.d.ts
340
- /**
341
- * Closed result of the `appstate` command, grounded in the daemon handler's
342
- * success returns (src/daemon/handlers/session-state.ts `handleAppStateCommand`).
343
- * A discriminated union on `platform`:
344
- * - Apple (`ios` / `macos`) session state, with iOS-only device locators that
345
- * the previous hand-written mirror omitted; and
346
- * - Android foreground `package` / `activity`.
347
- *
348
- * The handler returns one of these fixed objects (errors take the `ok: false`
349
- * path), so each branch is closed.
350
- */
351
- type AppStateCommandResult = {
352
- platform: 'ios' | 'macos';
353
- appName: string;
119
+ type StartupPerfSample = {
120
+ durationMs: number;
121
+ measuredAt: string;
122
+ method: string;
123
+ appTarget?: string;
354
124
  appBundleId?: string;
355
- source: 'session';
356
- surface: SessionSurface;
357
- /** iOS only — the session device's UDID. */
358
- device_udid?: string;
359
- /** iOS only — the simulator set path, or `null` when unknown. */
360
- ios_simulator_device_set?: string | null;
361
- } | {
362
- platform: 'android';
363
- package: string;
364
- activity: string;
365
125
  };
366
- //#endregion
367
- //#region src/contracts/keyboard.d.ts
368
- /**
369
- * Closed result of the `keyboard` command, grounded in the dispatch handlers'
370
- * literal returns (src/core/dispatch.ts `handleAndroidKeyboardCommand` /
371
- * `handleIosKeyboardCommand`).
372
- *
373
- * `platform` and `action` are always present; the remaining fields appear per
374
- * branch (Android `status`/`dismiss` carry the keyboard-state fields; `enter`
375
- * and iOS `dismiss` carry a `message`). It is kept as a flat closed shape rather
376
- * than a five-way `platform`×`action` union because the per-branch field sets
377
- * overlap heavily and the underlying Android keyboard-state types live in the
378
- * platform layer (below the public contract). The `Record` index signature of
379
- * the previous hand-written mirror is dropped, and the spurious `| null`s are
380
- * removed (the handler never returns `null` for these).
381
- */
382
- type KeyboardCommandResult = {
383
- platform: 'android' | 'ios';
384
- action: 'status' | 'dismiss' | 'enter';
385
- visible?: boolean;
386
- wasVisible?: boolean;
387
- dismissed?: boolean;
388
- attempts?: number;
389
- inputType?: string;
390
- type?: 'text' | 'number' | 'email' | 'phone' | 'password' | 'datetime' | 'unknown';
391
- inputMethodPackage?: string;
392
- focusedPackage?: string;
393
- focusedResourceId?: string;
394
- inputOwner?: 'app' | 'ime' | 'unknown';
395
- message?: string;
126
+ type DeviceBootOptions = DeviceCommandBaseOptions & {
127
+ headless?: boolean;
396
128
  };
129
+ type DeviceShutdownOptions = DeviceCommandBaseOptions;
397
130
  //#endregion
398
- //#region src/contracts/wait.d.ts
399
- /**
400
- * Public daemon result for `wait`. The runtime-local result carries a `kind`
401
- * discriminant, but `toDaemonWaitData` intentionally projects the normal daemon
402
- * payload to this compact shape. The direct iOS selector fast path may still
403
- * include `kind: 'selector'` additively.
404
- */
405
- type WaitCommandResult = {
406
- waitedMs: number;
407
- kind?: 'selector';
408
- text?: string;
409
- selector?: string;
410
- captures?: number;
411
- nodeCount?: number;
412
- hint?: string;
413
- warning?: string;
131
+ //#region src/contracts/client-app.d.ts
132
+ type AppInstallOptions = AgentDeviceRequestOverrides & AgentDeviceSelectionOptions & {
133
+ app?: string;
134
+ appPath: string;
414
135
  };
415
- //#endregion
416
- //#region src/contracts/prepare.d.ts
417
- type PrepareIosRunnerCacheKind = 'exact' | 'restore-key' | 'miss' | 'external';
418
- type PrepareIosRunnerArtifactState = 'valid' | 'rebuilt';
419
- type PrepareIosRunnerTiming = {
420
- totalMs: number;
421
- additiveParts: {
422
- buildMs?: number;
423
- connectAfterBuildMs: number;
424
- healthCheckMs: number;
425
- };
426
- containment: {
427
- connectMs?: ['buildMs'];
428
- healthCheckMs: [];
429
- };
430
- note: string;
136
+ type AppDeployOptions = AgentDeviceRequestOverrides & AgentDeviceSelectionOptions & {
137
+ app: string;
138
+ appPath: string;
431
139
  };
432
- /**
433
- * Public daemon result for `prepare ios-runner`. The runner-local prepare result
434
- * is projected by `prepareIosRunnerResponseData` with device identity and timing
435
- * guidance before it reaches the client.
436
- */
437
- type PrepareCommandResult = {
438
- action: 'ios-runner';
140
+ type AppDeployResult = {
141
+ app: string;
142
+ appPath: string;
439
143
  platform: PublicPlatform;
440
- deviceId: string;
441
- deviceName: string;
442
- kind: DeviceKind;
443
- durationMs: number;
444
- runner: JsonObject;
445
- cache?: PrepareIosRunnerCacheKind;
446
- artifact?: PrepareIosRunnerArtifactState;
447
- buildMs?: number;
448
- connectMs: number;
449
- healthCheckMs: number;
450
- xctestrunPath?: string;
451
- recoveryReason?: string;
452
- failureReason?: string;
453
- timing: PrepareIosRunnerTiming;
454
- message: string;
455
- };
456
- //#endregion
457
- //#region src/contracts/push.d.ts
458
- type PushCommandResult = {
459
- platform: 'ios';
460
- bundleId: string;
461
- message: string;
462
- } | {
463
- platform: 'android';
464
- package: string;
465
- action: string;
466
- extrasCount: number;
467
- message: string;
144
+ appId?: string;
145
+ bundleId?: string;
146
+ package?: string;
147
+ identifiers: AgentDeviceIdentifiers;
468
148
  };
469
- //#endregion
470
- //#region src/contracts/app-events.d.ts
471
- type TriggerAppEventCommandResult = {
472
- event: string;
473
- eventUrl: string;
474
- transport: 'deep-link';
475
- message: string;
149
+ type AppOpenOptions = AgentDeviceRequestOverrides & AgentDeviceSelectionOptions & {
150
+ app?: string;
151
+ url?: string;
152
+ surface?: SessionSurface;
153
+ activity?: string;
154
+ launchConsole?: string;
155
+ launchArgs?: string[];
156
+ relaunch?: boolean;
157
+ saveScript?: boolean | string;
158
+ /** #1258: overwrite an existing --save-script target instead of refusing. Alias: --overwrite. */
159
+ force?: boolean;
160
+ deviceHub?: boolean;
161
+ testIme?: boolean;
162
+ noRecord?: boolean;
163
+ runtime?: SessionRuntimeHints;
476
164
  };
477
- //#endregion
478
- //#region src/contracts/doctor.d.ts
479
- type DoctorStatus = 'pass' | 'warn' | 'fail' | 'info';
480
- type DoctorKind = 'auto' | 'react-native' | 'expo' | 'repack';
481
- type DoctorCheck = {
482
- id: string;
483
- status: DoctorStatus;
484
- summary: string;
485
- hint?: string;
486
- command?: string;
487
- evidence?: Record<string, unknown>;
165
+ type AppOpenResult = {
166
+ session: string;
167
+ warnings?: string[];
168
+ sessionStateDir?: string;
169
+ runnerLogPath?: string;
170
+ requestLogPath?: string;
171
+ eventLogPath?: string;
172
+ appName?: string;
173
+ appBundleId?: string;
174
+ appId?: string;
175
+ startup?: StartupPerfSample;
176
+ runtime?: SessionRuntimeHints;
177
+ device?: AgentDeviceSessionDevice;
178
+ identifiers: AgentDeviceIdentifiers;
488
179
  };
489
- type DoctorCommandResult = {
490
- status: DoctorStatus;
491
- summary: string;
492
- kind: DoctorKind;
493
- platform?: PlatformSelector;
494
- target?: DeviceTarget;
495
- targetApp?: string;
496
- metro?: {
497
- host: string;
498
- port: number;
499
- };
500
- checks: DoctorCheck[];
180
+ type AppCloseOptions = AgentDeviceRequestOverrides & {
181
+ app?: string;
182
+ shutdown?: boolean;
183
+ saveScript?: boolean | string;
184
+ /** #1258: overwrite an existing --save-script target instead of refusing. Alias: --overwrite. */
185
+ force?: boolean;
501
186
  };
502
- //#endregion
503
- //#region src/snapshot/snapshot-diff.d.ts
504
- type SnapshotDiffLine = {
505
- kind: 'added' | 'removed' | 'unchanged';
506
- text: string;
187
+ type AppCloseResult = {
188
+ session: string;
189
+ closedApp?: string;
190
+ shutdown?: TargetShutdownResult;
507
191
  /**
508
- * Plain ref body (`e12`) of the CURRENT-tree node behind an added line.
509
- * Only populated with `withRefs` (interaction `--settle`, #1101) so the
510
- * `diff` command's wire shape stays byte-identical. Removed/unchanged lines
511
- * never carry it: a removed line's ref names a node of the replaced tree.
192
+ * #1258: absolute path of the committed session/healed script when this close
193
+ * published one (`close --save-script`, or a repair-armed session's finalize)
194
+ * — so a client that requested publication learns where the file landed.
512
195
  */
513
- ref?: string;
196
+ savedScript?: string;
197
+ identifiers: AgentDeviceIdentifiers;
514
198
  };
515
- type SnapshotDiffSummary = {
516
- additions: number;
517
- removals: number;
518
- unchanged: number;
199
+ type AppInstallFromSourceOptions = AgentDeviceRequestOverrides & AgentDeviceSelectionOptions & {
200
+ source: DaemonInstallSource;
201
+ retainPaths?: boolean;
202
+ retentionMs?: number;
519
203
  };
520
- //#endregion
521
- //#region src/contracts/diff.d.ts
522
- type DiffSnapshotCommandResult = {
523
- mode: 'snapshot';
524
- baselineInitialized: boolean;
525
- summary: SnapshotDiffSummary;
526
- lines: SnapshotDiffLine[];
527
- warnings?: string[];
204
+ type AppInstallFromSourceResult = {
205
+ appName?: string;
206
+ appId?: string;
207
+ bundleId?: string;
208
+ packageName?: string;
209
+ launchTarget: string;
210
+ installablePath?: string;
211
+ archivePath?: string;
212
+ materializationId?: string;
213
+ materializationExpiresAt?: string;
214
+ identifiers: AgentDeviceIdentifiers;
528
215
  };
529
- //#endregion
530
- //#region src/contracts/recording.d.ts
531
- type RecordingAppIdentity = {
532
- bundleId: string;
533
- name?: string;
216
+ type AppListOptions = AgentDeviceRequestOverrides & AgentDeviceSelectionOptions & {
217
+ appsFilter?: AppsFilter;
534
218
  };
535
- type RecordingStartCommandResult = {
536
- recording: 'started';
537
- outPath: string;
538
- sessionStateDir: string;
539
- recordingBackend?: string;
540
- recordingScope?: RecordingScope;
541
- recordOnlySession?: boolean;
542
- activeSessionApp?: RecordingAppIdentity;
543
- showTouches: boolean;
219
+ type AppPushOptions = DeviceCommandBaseOptions & {
220
+ app: string;
221
+ payload: string | JsonObject;
544
222
  };
545
- type RecordingStopCommandResult = {
546
- recording: 'stopped';
547
- outPath: string;
548
- telemetryPath?: string;
549
- artifacts: DaemonArtifact[];
550
- recordingBackend?: string;
551
- recordingScope?: RecordingScope;
552
- recordOnlySession?: boolean;
553
- activeSessionApp?: RecordingAppIdentity;
554
- durationMs: number;
555
- showTouches: boolean;
556
- warning?: string;
557
- overlayWarning?: string;
558
- chunks?: Array<{
559
- index: number;
560
- path: string;
561
- }>;
223
+ type AppTriggerEventOptions = DeviceCommandBaseOptions & {
224
+ event: string;
225
+ payload?: JsonObject;
562
226
  };
563
- type RecordingCommandResult = RecordingStartCommandResult | RecordingStopCommandResult;
564
- type TraceCommandResult = {
565
- trace: 'started';
566
- outPath: string;
567
- } | {
568
- trace: 'stopped';
569
- outPath: string;
227
+ type MaterializationReleaseOptions = AgentDeviceRequestOverrides & {
228
+ materializationId: string;
229
+ };
230
+ type MaterializationReleaseResult = {
231
+ released: boolean;
232
+ materializationId: string;
233
+ identifiers: AgentDeviceIdentifiers;
570
234
  };
571
235
  //#endregion
572
- //#region src/contracts/replay.d.ts
573
- type ReplayCommandResult = {
574
- replayed: number;
575
- healed: number;
576
- session: string;
577
- artifactPaths: string[];
578
- warnings?: string[];
236
+ //#region src/contracts/client-capture.d.ts
237
+ type CaptureSnapshotOptions = AgentDeviceRequestOverrides & AgentDeviceSelectionOptions & {
238
+ interactiveOnly?: boolean;
239
+ depth?: number;
240
+ scope?: string;
241
+ raw?: boolean;
242
+ forceFull?: boolean;
243
+ timeoutMs?: number;
244
+ /**
245
+ * #1271 stage 2 (ADR 0012 amendment): `snapshot` is observation-only and
246
+ * excluded from a repair-armed heal by default; `record` forces it
247
+ * through. Mutually exclusive with `noRecord`.
248
+ */
249
+ noRecord?: boolean;
250
+ record?: boolean;
251
+ };
252
+ type CaptureSnapshotResult = {
253
+ nodes: SnapshotNode[];
254
+ truncated: boolean;
255
+ appName?: string;
256
+ appBundleId?: string;
257
+ visibility?: SnapshotVisibility;
258
+ unchanged?: SnapshotUnchanged;
579
259
  snapshotDiagnostics?: SnapshotDiagnosticsSummary;
580
- message: string;
260
+ identifiers: AgentDeviceIdentifiers;
261
+ /**
262
+ * ADR 0014: the response-level ref-frame epoch the plain node refs were minted
263
+ * from. A ref-issuing snapshot carries it ONCE (nodes stay plain `@e12` for the
264
+ * token budget); pair a ref with it (`@e12~s<refsGeneration>`) before a mutation.
265
+ */
266
+ refsGeneration?: number;
267
+ /**
268
+ * Digest response view only: a capped list of `{ ref, label? }` pairs taken
269
+ * from the full `nodes` tree so the MCP layer can still pin refs when the
270
+ * default-level `nodes` payload is intentionally omitted.
271
+ */
272
+ refs?: Array<{
273
+ ref: string;
274
+ label?: string;
275
+ }>;
276
+ } & PublicSnapshotCaptureAnnotations;
277
+ type CaptureScreenshotOptions = AgentDeviceRequestOverrides & {
278
+ path?: string;
279
+ overlayRefs?: boolean;
280
+ pixelDensity?: number;
281
+ fullscreen?: boolean;
282
+ maxSize?: number;
283
+ stabilize?: boolean;
284
+ normalizeStatusBar?: boolean;
285
+ surface?: SessionSurface;
581
286
  };
582
- type ReplaySuiteTestSkipReason = 'skipped-by-filter';
583
- type ReplaySuiteAttemptFailure = {
584
- attempt: number;
585
- message: string;
586
- durationMs?: number;
287
+ type CaptureScreenshotResult = ScreenshotResultData & {
288
+ path: string;
289
+ identifiers: AgentDeviceIdentifiers;
587
290
  };
588
- type ReplaySuiteTestPassed = {
589
- file: string;
590
- title?: string;
591
- session: string;
592
- status: 'passed';
593
- durationMs: number;
594
- finalAttemptDurationMs?: number;
595
- attempts: number;
596
- artifactsDir?: string;
597
- replayed: number;
598
- healed: number;
599
- warnings?: string[];
600
- attemptFailures?: ReplaySuiteAttemptFailure[];
601
- shardIndex?: number;
602
- shardCount?: number;
603
- deviceId?: string;
604
- deviceName?: string;
605
- snapshotDiagnostics?: SnapshotDiagnosticsSummary;
291
+ type CaptureDiffOptions = DeviceCommandBaseOptions & Pick<CaptureSnapshotOptions, 'interactiveOnly' | 'depth' | 'scope' | 'raw'> & {
292
+ kind: 'snapshot';
293
+ out?: string;
606
294
  };
607
- type ReplaySuiteTestFailed = {
608
- file: string;
609
- title?: string;
610
- session: string;
611
- status: 'failed';
612
- durationMs: number;
613
- attempts: number;
614
- artifactsDir?: string;
615
- error: DaemonError;
616
- shardIndex?: number;
617
- shardCount?: number;
618
- deviceId?: string;
619
- deviceName?: string;
620
- snapshotDiagnostics?: SnapshotDiagnosticsSummary;
295
+ type SelectorSnapshotCommandOptions = Pick<CaptureSnapshotOptions, 'depth' | 'scope' | 'raw'>;
296
+ type FindSnapshotCommandOptions = Pick<CaptureSnapshotOptions, 'depth' | 'raw'>;
297
+ //#endregion
298
+ //#region src/contracts/client-target.d.ts
299
+ type PointTarget = {
300
+ x: number;
301
+ y: number;
302
+ ref?: never;
303
+ selector?: never;
304
+ label?: never;
621
305
  };
622
- type ReplaySuiteTestSkipped = {
623
- file: string;
624
- title?: string;
625
- status: 'skipped';
626
- durationMs: 0;
627
- reason: ReplaySuiteTestSkipReason;
628
- message: string;
306
+ type RefTarget = {
307
+ ref: string;
308
+ label?: string;
309
+ x?: never;
310
+ y?: never;
311
+ selector?: never;
629
312
  };
630
- type ReplaySuiteTestResult = ReplaySuiteTestPassed | ReplaySuiteTestFailed | ReplaySuiteTestSkipped;
631
- type ReplaySuiteResult = {
632
- total: number;
633
- executed: number;
634
- passed: number;
635
- failed: number;
636
- skipped: number;
637
- notRun: number;
638
- durationMs: number;
639
- failures: ReplaySuiteTestFailed[];
640
- tests: ReplaySuiteTestResult[];
641
- snapshotDiagnostics?: SnapshotDiagnosticsSummary;
313
+ type SelectorTarget = {
314
+ selector: string;
315
+ x?: never;
316
+ y?: never;
317
+ ref?: never;
318
+ label?: never;
642
319
  };
320
+ type InteractionTarget = PointTarget | RefTarget | SelectorTarget;
321
+ type ElementTarget = RefTarget | SelectorTarget;
643
322
  //#endregion
644
- //#region src/core/command-descriptor/command-result.d.ts
323
+ //#region src/contracts/client-gesture.d.ts
324
+ type RepeatedPressOptions = {
325
+ count?: number;
326
+ intervalMs?: number;
327
+ holdMs?: number;
328
+ jitterPx?: number;
329
+ doubleTap?: boolean;
330
+ };
645
331
  /**
646
- * The additive typed-result spine (ADR-0008, Phase 1 step 6).
647
- *
648
- * Maps a command name to the per-command result type from `src/contracts/*`. It
649
- * is SEEDED, not exhaustive: a command is listed here only once its accurate,
650
- * closed result shape lives in the contracts layer. Commands whose daemon
651
- * handler spreads dynamic/Record data (screenshot overlays, gesture
652
- * visualization, perf, logs, …) are deliberately omitted rather than given an
653
- * invented shape.
654
- *
655
- * Batches 1-2 wired `boot` / `shutdown` / `viewport` and the navigation/action
656
- * commands `home` / `back` / `orientation` / `app-switcher` alongside the seed
657
- * interaction quartet. Batch 3 adds `clipboard` (a closed `read`/`write` union) and
658
- * `appstate` (a closed `platform` union — Apple session state with the iOS-only
659
- * device locators, or Android package/activity). Batch 4 adds `keyboard` (a
660
- * closed flat shape). Batch 5 adds the compact daemon projections for `wait`,
661
- * `prepare`, `push`, and `trigger-app-event`. Each entry is grounded in a
662
- * re-read of the handler's literal return; see the per-type docstrings.
332
+ * Opt-in (#1101): after the action, wait for the UI to go quiet and return the
333
+ * settled diff vs the pre-action tree (`settle` on the result) in the same
334
+ * response. Best-effort — never fails the action. `settleQuietMs` tunes the
335
+ * quiet window (default 500ms); `timeoutMs` bounds the settle wait (default
336
+ * 10s) when `settle` is true. A bare `timeoutMs` without `settle` is ignored
337
+ * for compatibility; `settleQuietMs` still requires `settle`.
663
338
  */
664
- interface CommandResultMap {
665
- press: PressCommandResponseData;
666
- click: ClickCommandResponseData;
667
- fill: FillCommandResponseData;
668
- longpress: LongPressCommandResponseData;
669
- find: FindCommandResponseData;
670
- boot: BootCommandResult;
671
- shutdown: ShutdownCommandResult;
672
- viewport: ViewportCommandResult;
673
- home: HomeCommandResult;
674
- back: BackCommandResult;
675
- orientation: OrientationCommandResult;
676
- 'app-switcher': AppSwitcherCommandResult;
677
- clipboard: ClipboardCommandResult;
678
- appstate: AppStateCommandResult;
679
- keyboard: KeyboardCommandResult;
680
- 'tv-remote': TvRemoteCommandResult;
681
- wait: WaitCommandResult;
682
- prepare: PrepareCommandResult;
683
- push: PushCommandResult;
684
- 'trigger-app-event': TriggerAppEventCommandResult;
685
- doctor: DoctorCommandResult;
686
- diff: DiffSnapshotCommandResult;
687
- replay: ReplayCommandResult;
688
- test: ReplaySuiteResult;
689
- record: RecordingCommandResult;
690
- trace: TraceCommandResult;
691
- }
692
- /**
693
- * The typed result for a command named `N`. Seeded commands resolve to their
694
- * contract result type from {@link CommandResultMap}; every other (unmigrated)
695
- * command falls back to the untyped `Record<string, unknown>` bag. That default
696
- * branch is what keeps the mapping total over every command name, so consumers
697
- * can switch to `CommandResult<Name>` without first migrating every command.
698
- */
699
- type CommandResult<N extends string> = N extends keyof CommandResultMap ? CommandResultMap[N] : Record<string, unknown>;
700
- //#endregion
701
- //#region src/cloud-artifacts.d.ts
702
- declare const CLOUD_ARTIFACT_KINDS: readonly ['video', 'appium-log', 'device-log', 'automation-log', 'provider-session', 'raw'];
703
- type CloudArtifactKind = (typeof CLOUD_ARTIFACT_KINDS)[number];
704
- type CloudArtifactAvailability = 'ready' | 'pending' | 'unavailable' | 'expired';
705
- type CloudArtifact = {
706
- provider: string;
707
- kind: CloudArtifactKind;
708
- name: string;
709
- url?: string;
710
- providerSessionId?: string;
711
- providerArtifactId?: string;
712
- contentType?: string;
713
- extension?: string;
714
- availability?: CloudArtifactAvailability;
715
- metadata?: Record<string, unknown>;
716
- };
717
- type CloudArtifactsStatus = 'ready' | 'pending' | 'unavailable';
718
- type CloudArtifactsResult = {
719
- provider: string;
720
- status: CloudArtifactsStatus;
721
- cloudArtifacts: CloudArtifact[];
722
- providerSessionId?: string;
723
- message?: string;
724
- };
725
- type DaemonArtifactInventoryEntry = {
726
- id: string;
727
- artifactType?: DaemonArtifactType;
728
- filename: string;
729
- mimeType: string;
730
- sizeBytes: number;
731
- createdAt: string;
732
- expiresAt: string;
733
- };
734
- type DaemonArtifactsResult = {
735
- source: 'daemon';
736
- status: 'ready';
737
- artifacts: DaemonArtifactInventoryEntry[];
738
- message?: string;
339
+ type SettleCommandOptions = {
340
+ settle?: boolean;
341
+ settleQuietMs?: number;
342
+ timeoutMs?: number;
739
343
  };
740
- type AgentArtifactsResult = CloudArtifactsResult | DaemonArtifactsResult;
741
- type CloudProviderSessionResult = {
742
- provider?: string;
743
- providerSessionId?: string;
744
- cloudArtifacts?: CloudArtifactsResult;
745
- } & Record<string, unknown>;
746
- //#endregion
747
- //#region src/client/client-types.d.ts
748
- type AgentDeviceDaemonTransport = (req: Omit<DaemonRequest, 'token'>) => Promise<DaemonResponse>;
749
- type AgentDeviceClientConfig = RemoteConnectionProfileFields & CloudProviderProfileFields & {
750
- session?: string;
751
- lockPolicy?: DaemonLockPolicy;
752
- lockPlatform?: PlatformSelector;
753
- requestId?: string;
754
- sessionIsolation?: SessionIsolationMode;
755
- leaseBackend?: LeaseBackend;
756
- leaseTtlMs?: number;
757
- runtime?: SessionRuntimeHints;
758
- cwd?: string;
759
- debug?: boolean;
760
- cost?: boolean;
761
- responseLevel?: ResponseLevel;
762
- iosXctestrunFile?: string;
763
- iosXctestDerivedDataPath?: string;
764
- iosXctestEnvDir?: string;
344
+ type ClickOptions = DeviceCommandBaseOptions & SelectorSnapshotCommandOptions & InteractionTarget & RepeatedPressOptions & SettleCommandOptions & {
345
+ button?: ClickButton;
346
+ /**
347
+ * Opt-in (#1047): return cheap post-action evidence (AX digest, node counts,
348
+ * changedFromBefore) in the response instead of requiring a follow-up
349
+ * snapshot to confirm the action had an effect.
350
+ */
351
+ verify?: boolean;
765
352
  };
766
- type AgentDeviceRequestOverrides = Pick<AgentDeviceClientConfig, 'session' | 'lockPolicy' | 'lockPlatform' | 'requestId' | 'daemonBaseUrl' | 'daemonAuthToken' | 'daemonTransport' | 'daemonServerMode' | 'tenant' | 'sessionIsolation' | 'runId' | 'leaseId' | 'leaseBackend' | 'leaseProvider' | 'deviceKey' | 'clientId' | 'providerApp' | 'providerOsVersion' | 'providerProject' | 'providerBuild' | 'providerSessionName' | 'awsProjectArn' | 'awsDeviceArn' | 'awsAppArn' | 'awsRegion' | 'awsInteractionMode' | 'leaseTtlMs' | 'cwd' | 'debug' | 'cost' | 'responseLevel' | 'iosXctestrunFile' | 'iosXctestDerivedDataPath' | 'iosXctestEnvDir'>;
767
- type AgentDeviceIdentifiers = {
768
- session?: string;
769
- deviceId?: string;
770
- deviceName?: string;
771
- udid?: string;
772
- serial?: string;
773
- appId?: string;
774
- appBundleId?: string;
775
- package?: string;
353
+ type PressOptions = DeviceCommandBaseOptions & SelectorSnapshotCommandOptions & InteractionTarget & RepeatedPressOptions & SettleCommandOptions & {
354
+ verify?: boolean;
776
355
  };
777
- type AgentDeviceSelectionOptions = {
778
- platform?: PlatformSelector;
779
- target?: DeviceTarget;
780
- device?: string;
781
- udid?: string;
782
- serial?: string;
783
- iosSimulatorDeviceSet?: string;
784
- androidDeviceAllowlist?: string;
356
+ type LongPressOptions = DeviceCommandBaseOptions & SelectorSnapshotCommandOptions & InteractionTarget & SettleCommandOptions & {
357
+ durationMs?: number;
785
358
  };
786
- type AgentDeviceDevice = {
787
- platform: PublicPlatform;
788
- target: DeviceTarget;
789
- kind: DeviceKind;
790
- id: string;
791
- name: string;
792
- booted?: boolean;
793
- /**
794
- * Additive Apple-OS discriminant (iPhone/iPad/tvOS/visionOS/macOS). Present only for
795
- * Apple devices; `platform` still carries the leaf (`ios`/`macos`).
796
- */
797
- appleOs?: AppleOS;
798
- identifiers: AgentDeviceIdentifiers;
799
- ios?: {
800
- udid: string;
359
+ type SwipeOptions = DeviceCommandBaseOptions & {
360
+ from: {
361
+ x: number;
362
+ y: number;
801
363
  };
802
- android?: {
803
- serial: string;
364
+ to: {
365
+ x: number;
366
+ y: number;
804
367
  };
368
+ count?: number;
369
+ pauseMs?: number;
370
+ pattern?: SwipePattern;
805
371
  };
806
- type AgentDeviceCapabilitiesResult = {
807
- device: AgentDeviceDevice;
808
- availableCommands: string[];
372
+ type PanOptions = DeviceCommandBaseOptions & {
373
+ x: number;
374
+ y: number;
375
+ dx: number;
376
+ dy: number;
377
+ pointerCount?: GesturePointerCount;
378
+ durationMs?: number;
809
379
  };
810
- type AgentDeviceSessionDevice = {
811
- platform: PublicPlatform;
812
- target: DeviceTarget;
813
- id: string;
814
- name: string;
815
- /**
816
- * Additive Apple-OS discriminant (iPhone/iPad/tvOS/visionOS/macOS). Present only for
817
- * Apple devices; `platform` still carries the leaf (`ios`/`macos`).
818
- */
819
- appleOs?: AppleOS;
820
- identifiers: AgentDeviceIdentifiers;
821
- ios?: {
822
- udid: string;
823
- simulatorSetPath?: string | null;
824
- };
825
- android?: {
826
- serial: string;
827
- };
380
+ type FlingOptions = DeviceCommandBaseOptions & {
381
+ direction: ScrollDirection;
382
+ x: number;
383
+ y: number;
384
+ distance?: number;
828
385
  };
829
- type AgentDeviceSession = {
830
- name: string;
831
- createdAt: number;
832
- sessionStateDir?: string;
833
- runnerLogPath?: string;
834
- device: AgentDeviceSessionDevice;
835
- identifiers: AgentDeviceIdentifiers;
386
+ type SwipeGestureOptions = DeviceCommandBaseOptions & {
387
+ preset: SwipePreset;
836
388
  };
837
- type StartupPerfSample = {
838
- durationMs: number;
839
- measuredAt: string;
840
- method: string;
841
- appTarget?: string;
842
- appBundleId?: string;
389
+ type FocusOptions = DeviceCommandBaseOptions & {
390
+ x: number;
391
+ y: number;
843
392
  };
844
- type SessionCloseResult = {
845
- session: string;
846
- shutdown?: TargetShutdownResult;
847
- provider?: CloudProviderSessionResult;
848
- /**
849
- * #1258: absolute path of the committed session/healed script when this close
850
- * published one (`close --save-script`, or a repair-armed session's finalize)
851
- * — so a client that requested publication learns where the file landed.
852
- */
853
- savedScript?: string;
854
- identifiers: AgentDeviceIdentifiers;
393
+ type TypeTextOptions = DeviceCommandBaseOptions & {
394
+ text: string;
395
+ delayMs?: number;
855
396
  };
856
- type SessionSaveScriptOptions = AgentDeviceRequestOverrides & {
857
- path?: string;
858
- /** Atomically replace an existing target instead of refusing publication. */
859
- force?: boolean;
397
+ type FillOptions = DeviceCommandBaseOptions & SelectorSnapshotCommandOptions & InteractionTarget & SettleCommandOptions & {
398
+ text: string;
399
+ delayMs?: number;
400
+ /** Publish this fill value as `${VAR}` when script recording is armed. */
401
+ recordAs?: string;
402
+ verify?: boolean;
860
403
  };
861
- type SessionSaveScriptResult = {
862
- session: string;
863
- savedScript: string;
864
- actionCount: number;
865
- identifiers: AgentDeviceIdentifiers;
404
+ type PinchOptions = DeviceCommandBaseOptions & {
405
+ scale: number;
406
+ x?: number;
407
+ y?: number;
866
408
  };
867
- type CloudArtifactsOptions = AgentDeviceRequestOverrides & {
868
- provider?: string;
869
- providerSessionId?: string;
409
+ type RotateGestureOptions = DeviceCommandBaseOptions & {
410
+ degrees: number;
411
+ x?: number;
412
+ y?: number;
870
413
  };
871
- type AppInstallOptions = AgentDeviceRequestOverrides & AgentDeviceSelectionOptions & {
872
- app?: string;
873
- appPath: string;
414
+ type TransformGestureOptions = DeviceCommandBaseOptions & TransformGestureParams;
415
+ type ScrollOptions = DeviceCommandBaseOptions & {
416
+ direction: ScrollInputDirection;
417
+ amount?: number;
418
+ pixels?: number;
419
+ durationMs?: number;
874
420
  };
875
- type AppDeployOptions = AgentDeviceRequestOverrides & AgentDeviceSelectionOptions & {
876
- app: string;
877
- appPath: string;
421
+ //#endregion
422
+ //#region src/contracts/client-lease.d.ts
423
+ type Lease = {
424
+ leaseId: string;
425
+ tenantId: string;
426
+ runId: string;
427
+ backend: LeaseBackend;
428
+ leaseProvider?: string;
429
+ deviceKey?: string;
430
+ clientId?: string;
431
+ createdAt?: number;
432
+ heartbeatAt?: number;
433
+ expiresAt?: number;
878
434
  };
879
- type AppDeployResult = {
880
- app: string;
881
- appPath: string;
882
- platform: PublicPlatform;
883
- appId?: string;
884
- bundleId?: string;
885
- package?: string;
886
- identifiers: AgentDeviceIdentifiers;
887
- };
888
- type AppOpenOptions = AgentDeviceRequestOverrides & AgentDeviceSelectionOptions & {
889
- app?: string;
890
- url?: string;
891
- surface?: SessionSurface;
892
- activity?: string;
893
- launchConsole?: string;
894
- launchArgs?: string[];
895
- relaunch?: boolean;
896
- saveScript?: boolean | string;
897
- /** #1258: overwrite an existing --save-script target instead of refusing. Alias: --overwrite. */
898
- force?: boolean;
899
- deviceHub?: boolean;
900
- testIme?: boolean;
901
- noRecord?: boolean;
902
- runtime?: SessionRuntimeHints;
903
- };
904
- type AppOpenResult = {
905
- session: string;
906
- warnings?: string[];
907
- sessionStateDir?: string;
908
- runnerLogPath?: string;
909
- requestLogPath?: string;
910
- eventLogPath?: string;
911
- appName?: string;
912
- appBundleId?: string;
913
- appId?: string;
914
- startup?: StartupPerfSample;
915
- runtime?: SessionRuntimeHints;
916
- device?: AgentDeviceSessionDevice;
917
- identifiers: AgentDeviceIdentifiers;
918
- };
919
- type AppCloseOptions = AgentDeviceRequestOverrides & {
920
- app?: string;
921
- shutdown?: boolean;
922
- saveScript?: boolean | string;
923
- /** #1258: overwrite an existing --save-script target instead of refusing. Alias: --overwrite. */
924
- force?: boolean;
925
- };
926
- type AppCloseResult = {
927
- session: string;
928
- closedApp?: string;
929
- shutdown?: TargetShutdownResult;
930
- /**
931
- * #1258: absolute path of the committed session/healed script when this close
932
- * published one (`close --save-script`, or a repair-armed session's finalize)
933
- * — so a client that requested publication learns where the file landed.
934
- */
935
- savedScript?: string;
936
- identifiers: AgentDeviceIdentifiers;
937
- };
938
- type AppInstallFromSourceOptions = AgentDeviceRequestOverrides & AgentDeviceSelectionOptions & {
939
- source: DaemonInstallSource;
940
- retainPaths?: boolean;
941
- retentionMs?: number;
942
- };
943
- type AppInstallFromSourceResult = {
944
- appName?: string;
945
- appId?: string;
946
- bundleId?: string;
947
- packageName?: string;
948
- launchTarget: string;
949
- installablePath?: string;
950
- archivePath?: string;
951
- materializationId?: string;
952
- materializationExpiresAt?: string;
953
- identifiers: AgentDeviceIdentifiers;
954
- };
955
- type AppListOptions = AgentDeviceRequestOverrides & AgentDeviceSelectionOptions & {
956
- appsFilter?: AppsFilter;
957
- };
958
- type MaterializationReleaseOptions = AgentDeviceRequestOverrides & {
959
- materializationId: string;
960
- };
961
- type MaterializationReleaseResult = {
962
- released: boolean;
963
- materializationId: string;
964
- identifiers: AgentDeviceIdentifiers;
965
- };
966
- type Lease = {
967
- leaseId: string;
968
- tenantId: string;
969
- runId: string;
970
- backend: LeaseBackend;
971
- leaseProvider?: string;
972
- deviceKey?: string;
973
- clientId?: string;
974
- createdAt?: number;
975
- heartbeatAt?: number;
976
- expiresAt?: number;
977
- };
978
- type LeaseOptions = AgentDeviceRequestOverrides & AgentDeviceSelectionOptions & {
979
- ttlMs?: number;
435
+ type LeaseOptions = AgentDeviceRequestOverrides & AgentDeviceSelectionOptions & {
436
+ ttlMs?: number;
980
437
  };
981
438
  type LeaseAllocateOptions = LeaseOptions & {
982
439
  tenant: string;
@@ -997,117 +454,268 @@ type LeaseScopedOptions = LeaseOptions & {
997
454
  deviceKey?: string;
998
455
  clientId?: string;
999
456
  };
1000
- type MetroPrepareOptions = {
1001
- projectRoot?: string;
1002
- kind?: MetroPrepareKind;
1003
- publicBaseUrl?: string;
1004
- proxyBaseUrl?: string;
1005
- bearerToken?: string;
1006
- bridgeScope?: MetroBridgeScope;
1007
- launchUrl?: string;
1008
- companionProfileKey?: string;
1009
- companionConsumerKey?: string;
1010
- port?: number;
1011
- listenHost?: string;
1012
- statusHost?: string;
1013
- startupTimeoutMs?: number;
1014
- probeTimeoutMs?: number;
1015
- reuseExisting?: boolean;
1016
- installDependenciesIfNeeded?: boolean;
1017
- runtimeFilePath?: string;
1018
- logPath?: string;
1019
- };
1020
- type MetroPrepareResult = PrepareMetroRuntimeResult;
1021
- type MetroReloadOptions = {
1022
- metroHost?: string;
1023
- metroPort?: number;
1024
- bundleUrl?: string;
1025
- timeoutMs?: number;
457
+ type CloudArtifactsOptions = AgentDeviceRequestOverrides & {
458
+ provider?: string;
459
+ providerSessionId?: string;
1026
460
  };
1027
- type MetroReloadResult = ReloadMetroResult;
1028
- type CaptureSnapshotOptions = AgentDeviceRequestOverrides & AgentDeviceSelectionOptions & {
1029
- interactiveOnly?: boolean;
1030
- depth?: number;
1031
- scope?: string;
1032
- raw?: boolean;
1033
- forceFull?: boolean;
1034
- timeoutMs?: number;
461
+ //#endregion
462
+ //#region src/contracts/logs.d.ts
463
+ declare const LOG_ACTION_VALUES: readonly ['path', 'start', 'stop', 'doctor', 'mark', 'clear'];
464
+ type LogAction = (typeof LOG_ACTION_VALUES)[number];
465
+ //#endregion
466
+ //#region src/contracts/perf.d.ts
467
+ declare const PERF_AREA_VALUES: readonly ['metrics', 'frames', 'memory', 'cpu', 'trace'];
468
+ declare const PERF_ACTION_VALUES: readonly ['sample', 'snapshot', 'start', 'stop', 'report'];
469
+ declare const PERF_SUBJECT_VALUES: readonly ['profile'];
470
+ declare const PERF_KIND_VALUES: readonly ['xctrace', 'simpleperf', 'perfetto', 'android-hprof', 'memgraph'];
471
+ type PerfArea = (typeof PERF_AREA_VALUES)[number];
472
+ type PerfAction = (typeof PERF_ACTION_VALUES)[number];
473
+ type PerfSubject = (typeof PERF_SUBJECT_VALUES)[number];
474
+ type PerfKind = (typeof PERF_KIND_VALUES)[number];
475
+ //#endregion
476
+ //#region src/contracts/recording-scope.d.ts
477
+ declare const RECORDING_SCOPE_VALUES: readonly ['app', 'device', 'system'];
478
+ type RecordingScope = (typeof RECORDING_SCOPE_VALUES)[number];
479
+ //#endregion
480
+ //#region src/contracts/client-observability.d.ts
481
+ type PerfOptions = DeviceCommandBaseOptions & {
482
+ area?: PerfArea;
483
+ subject?: PerfSubject;
484
+ action?: PerfAction;
485
+ kind?: PerfKind;
486
+ template?: string;
487
+ out?: string;
488
+ tracePath?: string;
489
+ };
490
+ type LogsOptions = AgentDeviceRequestOverrides & {
491
+ action?: LogAction;
492
+ message?: string;
493
+ restart?: boolean;
494
+ };
495
+ type EventsOptions = AgentDeviceRequestOverrides & {
496
+ cursor?: string;
497
+ limit?: number;
498
+ };
499
+ type NetworkOptions = DeviceCommandBaseOptions & {
500
+ action?: 'dump' | 'log';
501
+ limit?: number;
502
+ include?: NetworkIncludeMode;
503
+ };
504
+ type AudioOptions = DeviceCommandBaseOptions & {
505
+ action?: 'probe';
506
+ probeAction?: 'start' | 'status' | 'stop';
507
+ durationMs?: number;
508
+ bucketMs?: number;
509
+ };
510
+ type RecordOptions = AgentDeviceRequestOverrides & {
511
+ action: 'start' | 'stop';
512
+ path?: string;
513
+ fps?: number;
514
+ maxSize?: number;
515
+ quality?: RecordingExportQuality;
516
+ hideTouches?: boolean;
517
+ recordingScope?: RecordingScope;
518
+ };
519
+ type TraceOptions = AgentDeviceRequestOverrides & {
520
+ action: 'start' | 'stop';
521
+ path?: string;
522
+ };
523
+ //#endregion
524
+ //#region src/contracts/client-replay.d.ts
525
+ type ReplayRunOptions = AgentDeviceRequestOverrides & AgentDeviceSelectionOptions & {
526
+ path: string;
527
+ runtime?: SessionRuntimeHints;
1035
528
  /**
1036
- * #1271 stage 2 (ADR 0012 amendment): `snapshot` is observation-only and
1037
- * excluded from a repair-armed heal by default; `record` forces it
1038
- * through. Mutually exclusive with `noRecord`.
529
+ * @deprecated ADR 0012 migration step 6: `--update` no longer rewrites
530
+ * the script. Accepted for backward compatibility; every divergence
531
+ * already carries ranked selector suggestions regardless of this flag.
1039
532
  */
1040
- noRecord?: boolean;
1041
- record?: boolean;
1042
- };
1043
- type CaptureSnapshotResult = {
1044
- nodes: SnapshotNode[];
1045
- truncated: boolean;
1046
- appName?: string;
1047
- appBundleId?: string;
1048
- visibility?: SnapshotVisibility;
1049
- unchanged?: SnapshotUnchanged;
1050
- snapshotDiagnostics?: SnapshotDiagnosticsSummary;
1051
- identifiers: AgentDeviceIdentifiers;
533
+ update?: boolean;
534
+ /** @deprecated Use backend: 'maestro'. */
535
+ maestro?: boolean;
536
+ backend?: string;
537
+ env?: string[];
538
+ timeoutMs?: number;
1052
539
  /**
1053
- * ADR 0014: the response-level ref-frame epoch the plain node refs were minted
1054
- * from. A ref-issuing snapshot carries it ONCE (nodes stay plain `@e12` for the
1055
- * token budget); pair a ref with it (`@e12~s<refsGeneration>`) before a mutation.
540
+ * ADR 0012 decision 4 / migration step 5: resume at this 1-based plan
541
+ * step, skipping `1..resumeFrom-1` without executing them. Requires
542
+ * `resumePlanDigest` from the divergence report that reported this
543
+ * step as the failure. `replay` only — `test` has no resume fields.
1056
544
  */
1057
- refsGeneration?: number;
545
+ resumeFrom?: number;
546
+ /** The `resume.planDigest` from the divergence report `resumeFrom` came from. */
547
+ resumePlanDigest?: string;
1058
548
  /**
1059
- * Digest response view only: a capped list of `{ ref, label? }` pairs taken
1060
- * from the full `nodes` tree so the MCP layer can still pin refs when the
1061
- * default-level `nodes` payload is intentionally omitted.
549
+ * ADR 0012 decision 6, R1/R6: arms agent-supervised re-record repair
550
+ * from this replay attempt onward. Optional string value is the healed
551
+ * `.ad`'s output path; absent one, it defaults to the `<path>` sibling
552
+ * `<stem>.healed.ad` when the repair ends with `close --save-script`.
1062
553
  */
1063
- refs?: Array<{
1064
- ref: string;
1065
- label?: string;
1066
- }>;
1067
- } & PublicSnapshotCaptureAnnotations;
1068
- type CaptureScreenshotOptions = AgentDeviceRequestOverrides & {
1069
- path?: string;
1070
- overlayRefs?: boolean;
1071
- pixelDensity?: number;
1072
- fullscreen?: boolean;
1073
- maxSize?: number;
1074
- stabilize?: boolean;
1075
- normalizeStatusBar?: boolean;
1076
- surface?: SessionSurface;
1077
- };
1078
- type CaptureScreenshotResult = ScreenshotResultData & {
1079
- path: string;
1080
- identifiers: AgentDeviceIdentifiers;
554
+ saveScript?: boolean | string;
555
+ /** #1258: overwrite an existing --save-script target instead of refusing. Alias: --overwrite. */
556
+ force?: boolean;
1081
557
  };
1082
- type DeviceCommandBaseOptions = AgentDeviceRequestOverrides & AgentDeviceSelectionOptions;
1083
- type WaitCommandTarget = {
1084
- durationMs: number;
1085
- text?: never;
1086
- ref?: never;
1087
- selector?: never;
1088
- stable?: never;
1089
- quietMs?: never;
1090
- timeoutMs?: never;
1091
- } | (SelectorSnapshotCommandOptions & {
1092
- text: string;
1093
- durationMs?: never;
1094
- ref?: never;
1095
- selector?: never;
1096
- stable?: never;
1097
- quietMs?: never;
1098
- timeoutMs?: number;
1099
- }) | (SelectorSnapshotCommandOptions & {
1100
- ref: string;
1101
- durationMs?: never;
1102
- text?: never;
1103
- selector?: never;
1104
- stable?: never;
1105
- quietMs?: never;
558
+ type ReplayTestOptions = AgentDeviceRequestOverrides & AgentDeviceSelectionOptions & {
559
+ paths: string[];
560
+ runtime?: SessionRuntimeHints;
561
+ update?: boolean;
562
+ /** @deprecated Use backend: 'maestro'. */
563
+ maestro?: boolean;
564
+ backend?: string;
565
+ env?: string[];
566
+ failFast?: boolean;
1106
567
  timeoutMs?: number;
1107
- }) | (SelectorSnapshotCommandOptions & {
1108
- selector: string;
1109
- durationMs?: never;
1110
- text?: never;
568
+ retries?: number;
569
+ recordVideo?: boolean;
570
+ artifactsDir?: string;
571
+ /** @deprecated Use the CLI --reporter junit:<path> or --report-junit <path>. */
572
+ reportJunit?: string;
573
+ shardAll?: number;
574
+ shardSplit?: number;
575
+ };
576
+ type BatchStep = {
577
+ command: string;
578
+ input: Record<string, unknown>;
579
+ runtime?: SessionRuntimeHints;
580
+ };
581
+ type BatchRunOptions = AgentDeviceRequestOverrides & {
582
+ steps: BatchStep[];
583
+ onError?: 'stop';
584
+ maxSteps?: number;
585
+ out?: string;
586
+ };
587
+ //#endregion
588
+ //#region src/contracts/client-request.d.ts
589
+ type CommandRequestResult = DaemonResponseData;
590
+ //#endregion
591
+ //#region src/contracts/client-selector-read.d.ts
592
+ /**
593
+ * #1271 stage 2 (ADR 0012 amendment): `get`/`is`/`find` are observation-only
594
+ * and excluded from a repair-armed heal by default. `record` forces this
595
+ * action through (the corrective-read case); `noRecord` continues to opt the
596
+ * action out entirely. Mutually exclusive.
597
+ */
598
+ type RecordControlOptions = {
599
+ noRecord?: boolean;
600
+ record?: boolean;
601
+ };
602
+ type GetOptions = DeviceCommandBaseOptions & SelectorSnapshotCommandOptions & ElementTarget & RecordControlOptions & {
603
+ format: 'text' | 'attrs';
604
+ };
605
+ type IsTextPredicateOptions = DeviceCommandBaseOptions & SelectorSnapshotCommandOptions & RecordControlOptions & {
606
+ predicate: 'text';
607
+ selector: string;
608
+ value: string;
609
+ };
610
+ type IsStatePredicateOptions = DeviceCommandBaseOptions & SelectorSnapshotCommandOptions & RecordControlOptions & {
611
+ predicate: 'visible' | 'hidden' | 'exists' | 'editable' | 'selected' | 'focused';
612
+ selector: string;
613
+ value?: never;
614
+ };
615
+ type IsOptions = IsTextPredicateOptions | IsStatePredicateOptions;
616
+ type FindBaseOptions = DeviceCommandBaseOptions & FindSnapshotCommandOptions & RecordControlOptions & {
617
+ locator?: FindLocator;
618
+ query: string;
619
+ first?: boolean;
620
+ last?: boolean;
621
+ };
622
+ type FindOptions = (FindBaseOptions & {
623
+ action?: 'click' | 'focus' | 'exists' | 'getText' | 'getAttrs';
624
+ }) | (FindBaseOptions & {
625
+ action: 'wait';
626
+ timeoutMs?: number;
627
+ }) | (FindBaseOptions & {
628
+ action: 'fill' | 'type';
629
+ value: string;
630
+ });
631
+ //#endregion
632
+ //#region src/contracts/client-session.d.ts
633
+ type SessionCloseResult = {
634
+ session: string;
635
+ shutdown?: TargetShutdownResult;
636
+ provider?: CloudProviderSessionResult;
637
+ /**
638
+ * #1258: absolute path of the committed session/healed script when this close
639
+ * published one (`close --save-script`, or a repair-armed session's finalize)
640
+ * — so a client that requested publication learns where the file landed.
641
+ */
642
+ savedScript?: string;
643
+ identifiers: AgentDeviceIdentifiers;
644
+ };
645
+ type SessionSaveScriptOptions = AgentDeviceRequestOverrides & {
646
+ path?: string;
647
+ /** Atomically replace an existing target instead of refusing publication. */
648
+ force?: boolean;
649
+ };
650
+ type SessionSaveScriptResult = {
651
+ session: string;
652
+ savedScript: string;
653
+ actionCount: number;
654
+ identifiers: AgentDeviceIdentifiers;
655
+ };
656
+ //#endregion
657
+ //#region src/contracts/client-settings.d.ts
658
+ type PermissionTarget = 'camera' | 'microphone' | 'photos' | 'contacts' | 'contacts-limited' | 'notifications' | 'calendar' | 'location' | 'location-always' | 'media-library' | 'motion' | 'reminders' | 'siri' | 'accessibility' | 'screen-recording' | 'input-monitoring';
659
+ type SettingsUpdateOptions = (DeviceCommandBaseOptions & {
660
+ setting: 'clear-app-state';
661
+ state: 'clear';
662
+ app?: string;
663
+ }) | (DeviceCommandBaseOptions & {
664
+ setting: 'wifi' | 'airplane' | 'location';
665
+ state: 'on' | 'off';
666
+ }) | (DeviceCommandBaseOptions & {
667
+ setting: 'location';
668
+ state: 'set';
669
+ latitude: number;
670
+ longitude: number;
671
+ }) | (DeviceCommandBaseOptions & {
672
+ setting: 'animations';
673
+ state: 'on' | 'off';
674
+ }) | (DeviceCommandBaseOptions & {
675
+ setting: 'appearance';
676
+ state: 'light' | 'dark' | 'toggle';
677
+ }) | (DeviceCommandBaseOptions & {
678
+ setting: 'faceid' | 'touchid';
679
+ state: 'match' | 'nonmatch' | 'enroll' | 'unenroll';
680
+ }) | (DeviceCommandBaseOptions & {
681
+ setting: 'fingerprint';
682
+ state: 'match' | 'nonmatch';
683
+ }) | (DeviceCommandBaseOptions & {
684
+ setting: 'permission';
685
+ state: 'grant' | 'deny' | 'reset';
686
+ permission: PermissionTarget;
687
+ mode?: 'full' | 'limited';
688
+ });
689
+ //#endregion
690
+ //#region src/contracts/client-system.d.ts
691
+ type WaitCommandTarget = {
692
+ durationMs: number;
693
+ text?: never;
694
+ ref?: never;
695
+ selector?: never;
696
+ stable?: never;
697
+ quietMs?: never;
698
+ timeoutMs?: never;
699
+ } | (SelectorSnapshotCommandOptions & {
700
+ text: string;
701
+ durationMs?: never;
702
+ ref?: never;
703
+ selector?: never;
704
+ stable?: never;
705
+ quietMs?: never;
706
+ timeoutMs?: number;
707
+ }) | (SelectorSnapshotCommandOptions & {
708
+ ref: string;
709
+ durationMs?: never;
710
+ text?: never;
711
+ selector?: never;
712
+ stable?: never;
713
+ quietMs?: never;
714
+ timeoutMs?: number;
715
+ }) | (SelectorSnapshotCommandOptions & {
716
+ selector: string;
717
+ durationMs?: never;
718
+ text?: never;
1111
719
  ref?: never;
1112
720
  stable?: never;
1113
721
  quietMs?: never;
@@ -1127,9 +735,6 @@ type AlertCommandOptions = DeviceCommandBaseOptions & {
1127
735
  timeoutMs?: number;
1128
736
  };
1129
737
  type AppStateCommandOptions = DeviceCommandBaseOptions;
1130
- type OrientationCommandOptions = DeviceCommandBaseOptions & NavigationCommandOptions<'orientation'>;
1131
- /** @deprecated Renamed to `OrientationCommandOptions`. Retained until the next major. */
1132
- type RotateCommandOptions = OrientationCommandOptions;
1133
738
  type KeyboardCommandOptions = DeviceCommandBaseOptions & {
1134
739
  action?: 'status' | 'dismiss' | 'enter' | 'return';
1135
740
  };
@@ -1154,341 +759,632 @@ type ViewportCommandOptions = DeviceCommandBaseOptions & {
1154
759
  width: number;
1155
760
  height: number;
1156
761
  };
1157
- type NonNavigationCommandClient = {
1158
- wait: (options: WaitCommandOptions) => Promise<CommandResult<'wait'>>;
1159
- alert: (options?: AlertCommandOptions) => Promise<CommandRequestResult>;
1160
- appState: (options?: AppStateCommandOptions) => Promise<CommandResult<'appstate'>>;
1161
- keyboard: (options?: KeyboardCommandOptions) => Promise<CommandResult<'keyboard'>>;
1162
- clipboard: (options: ClipboardCommandOptions) => Promise<CommandResult<'clipboard'>>;
1163
- reactNative: (options: ReactNativeCommandOptions) => Promise<CommandRequestResult>;
1164
- doctor: (options?: DoctorCommandOptions) => Promise<CommandResult<'doctor'>>;
1165
- /**
1166
- * JSON prepare results include timing.additiveParts for additive wall-clock phases.
1167
- * Top-level buildMs/connectMs/healthCheckMs are diagnostics and may overlap.
1168
- */
1169
- prepare: (options: PrepareCommandOptions) => Promise<CommandResult<'prepare'>>;
1170
- viewport: (options: ViewportCommandOptions) => Promise<CommandResult<'viewport'>>;
1171
- };
1172
- type AgentDeviceCommandClient = ProjectedNavigationCommandClient<DeviceCommandBaseOptions> & NonNavigationCommandClient & DeprecatedCommandClient;
1173
- /** Renamed command methods retained for existing consumers until the next major. */
1174
- type DeprecatedCommandClient = {
1175
- /**
1176
- * @deprecated Renamed to `orientation`. Delegates to it and returns the legacy
1177
- * `action: 'rotate'` response contract. Retained until the next major version.
1178
- */
1179
- rotate: (options: RotateCommandOptions) => Promise<RotateCommandResult>;
1180
- };
1181
- type SelectorSnapshotCommandOptions = Pick<CaptureSnapshotOptions, 'depth' | 'scope' | 'raw'>;
1182
- type FindSnapshotCommandOptions = Pick<CaptureSnapshotOptions, 'depth' | 'raw'>;
1183
- type PointTarget = {
1184
- x: number;
1185
- y: number;
1186
- ref?: never;
1187
- selector?: never;
1188
- label?: never;
1189
- };
1190
- type RefTarget = {
1191
- ref: string;
1192
- label?: string;
1193
- x?: never;
1194
- y?: never;
1195
- selector?: never;
762
+ //#endregion
763
+ //#region src/contracts/navigation.d.ts
764
+ /**
765
+ * Closed results of the navigation/global action commands. Each mirrors the
766
+ * dispatch handler's literal return EXACTLY (src/core/dispatch.ts
767
+ * `DISPATCH_HANDLERS`): a fixed `action` discriminant plus the always-present
768
+ * `successText` message (the handlers always pass a non-empty message, so it is
769
+ * required here). The handlers spread nothing else, so the shapes are closed —
770
+ * consistent with the `viewport` contract, the generic-dispatch Android
771
+ * dialog-recovery `warning` annotation is intentionally not part of the contract.
772
+ */
773
+ /** `home` — `{ action: 'home', message: 'Home' }`. */
774
+ type HomeCommandResult = {
775
+ action: 'home';
776
+ message: string;
1196
777
  };
1197
- type SelectorTarget = {
1198
- selector: string;
1199
- x?: never;
1200
- y?: never;
1201
- ref?: never;
1202
- label?: never;
778
+ /** `back` — `{ action: 'back', mode, message: 'Back' }`; `mode` defaults to `'in-app'`. */
779
+ type BackCommandResult = {
780
+ action: 'back';
781
+ mode: BackMode;
782
+ message: string;
1203
783
  };
1204
- type InteractionTarget = PointTarget | RefTarget | SelectorTarget;
1205
- type ElementTarget = RefTarget | SelectorTarget;
1206
- type RepeatedPressOptions = {
1207
- count?: number;
1208
- intervalMs?: number;
1209
- holdMs?: number;
1210
- jitterPx?: number;
1211
- doubleTap?: boolean;
784
+ /** `orientation` — `{ action: 'orientation', orientation, message: 'Rotated to <orientation>' }`. */
785
+ type OrientationCommandResult = {
786
+ action: 'orientation';
787
+ orientation: DeviceRotation;
788
+ message: string;
1212
789
  };
1213
- type DeviceBootOptions = DeviceCommandBaseOptions & {
1214
- headless?: boolean;
790
+ /**
791
+ * @deprecated The `rotate` command was renamed to `orientation`. This is the
792
+ * legacy response contract (`action: 'rotate'`) that shipped in v0.18/v0.19,
793
+ * retained for existing SDK consumers until the next major version. New code
794
+ * should use {@link OrientationCommandResult}.
795
+ */
796
+ type RotateCommandResult = {
797
+ action: 'rotate';
798
+ orientation: DeviceRotation;
799
+ message: string;
1215
800
  };
1216
- type DeviceShutdownOptions = DeviceCommandBaseOptions;
1217
- type AppPushOptions = DeviceCommandBaseOptions & {
1218
- app: string;
1219
- payload: string | JsonObject;
801
+ /** `app-switcher` — `{ action: 'app-switcher', message: 'Opened app switcher' }`. */
802
+ type AppSwitcherCommandResult = {
803
+ action: 'app-switcher';
804
+ message: string;
1220
805
  };
1221
- type AppTriggerEventOptions = DeviceCommandBaseOptions & {
1222
- event: string;
1223
- payload?: JsonObject;
806
+ /** `tv-remote` — `{ action: 'tv-remote', button, durationMs?, message }`. */
807
+ type TvRemoteCommandResult = {
808
+ action: 'tv-remote';
809
+ button: TvRemoteButton;
810
+ durationMs?: number;
811
+ message: string;
1224
812
  };
1225
- type CaptureDiffOptions = DeviceCommandBaseOptions & Pick<CaptureSnapshotOptions, 'interactiveOnly' | 'depth' | 'scope' | 'raw'> & {
1226
- kind: 'snapshot';
1227
- out?: string;
1228
- };
1229
- /**
1230
- * Opt-in (#1101): after the action, wait for the UI to go quiet and return the
1231
- * settled diff vs the pre-action tree (`settle` on the result) in the same
1232
- * response. Best-effort — never fails the action. `settleQuietMs` tunes the
1233
- * quiet window (default 500ms); `timeoutMs` bounds the settle wait (default
1234
- * 10s) when `settle` is true. A bare `timeoutMs` without `settle` is ignored
1235
- * for compatibility; `settleQuietMs` still requires `settle`.
1236
- */
1237
- type SettleCommandOptions = {
1238
- settle?: boolean;
1239
- settleQuietMs?: number;
1240
- timeoutMs?: number;
1241
- };
1242
- type ClickOptions = DeviceCommandBaseOptions & SelectorSnapshotCommandOptions & InteractionTarget & RepeatedPressOptions & SettleCommandOptions & {
1243
- button?: ClickButton;
1244
- /**
1245
- * Opt-in (#1047): return cheap post-action evidence (AX digest, node counts,
1246
- * changedFromBefore) in the response instead of requiring a follow-up
1247
- * snapshot to confirm the action had an effect.
1248
- */
1249
- verify?: boolean;
1250
- };
1251
- type PressOptions = DeviceCommandBaseOptions & SelectorSnapshotCommandOptions & InteractionTarget & RepeatedPressOptions & SettleCommandOptions & {
1252
- verify?: boolean;
813
+ //#endregion
814
+ //#region src/commands/command-contract.d.ts
815
+ type JsonSchema = {
816
+ type?: string | readonly string[];
817
+ description?: string;
818
+ properties?: Record<string, JsonSchema>;
819
+ required?: readonly string[];
820
+ additionalProperties?: boolean | JsonSchema;
821
+ items?: JsonSchema;
822
+ prefixItems?: readonly JsonSchema[];
823
+ oneOf?: readonly JsonSchema[];
824
+ enum?: readonly unknown[];
825
+ const?: unknown;
826
+ minimum?: number;
827
+ maximum?: number;
1253
828
  };
1254
- type LongPressOptions = DeviceCommandBaseOptions & SelectorSnapshotCommandOptions & InteractionTarget & SettleCommandOptions & {
1255
- durationMs?: number;
829
+ type ExecutableCommandProjection<ClientMethod extends string = string> = {
830
+ clientMethod: ClientMethod;
831
+ outputSchema: JsonSchema;
1256
832
  };
1257
- type SwipeOptions = DeviceCommandBaseOptions & {
1258
- from: {
1259
- x: number;
1260
- y: number;
1261
- };
1262
- to: {
1263
- x: number;
1264
- y: number;
833
+ //#endregion
834
+ //#region src/commands/system/navigation-projection.d.ts
835
+ declare const navigationCommandProjectionType: unique symbol;
836
+ type NavigationCommandProjection<Options, Result, Required extends boolean, ClientMethod extends string> = ExecutableCommandProjection<ClientMethod> & {
837
+ readonly [navigationCommandProjectionType]?: {
838
+ options: Options;
839
+ result: Result;
840
+ required: Required;
1265
841
  };
1266
- count?: number;
1267
- pauseMs?: number;
1268
- pattern?: SwipePattern;
1269
- };
1270
- type PanOptions = DeviceCommandBaseOptions & {
1271
- x: number;
1272
- y: number;
1273
- dx: number;
1274
- dy: number;
1275
- pointerCount?: GesturePointerCount;
1276
- durationMs?: number;
1277
- };
1278
- type FlingOptions = DeviceCommandBaseOptions & {
1279
- direction: ScrollDirection;
1280
- x: number;
1281
- y: number;
1282
- distance?: number;
1283
- };
1284
- type SwipeGestureOptions = DeviceCommandBaseOptions & {
1285
- preset: SwipePreset;
1286
- };
1287
- type FocusOptions = DeviceCommandBaseOptions & {
1288
- x: number;
1289
- y: number;
1290
- };
1291
- type TypeTextOptions = DeviceCommandBaseOptions & {
1292
- text: string;
1293
- delayMs?: number;
1294
- };
1295
- type FillOptions = DeviceCommandBaseOptions & SelectorSnapshotCommandOptions & InteractionTarget & SettleCommandOptions & {
1296
- text: string;
1297
- delayMs?: number;
1298
- verify?: boolean;
1299
842
  };
1300
- type ScrollOptions = DeviceCommandBaseOptions & {
1301
- direction: ScrollInputDirection;
1302
- amount?: number;
1303
- pixels?: number;
1304
- durationMs?: number;
843
+ declare const NAVIGATION_COMMAND_PROJECTIONS: {
844
+ readonly back: NavigationCommandProjection<{
845
+ mode?: BackMode;
846
+ }, BackCommandResult, false, "back">;
847
+ readonly home: NavigationCommandProjection<{}, HomeCommandResult, false, "home">;
848
+ readonly orientation: NavigationCommandProjection<{
849
+ orientation: DeviceRotation;
850
+ }, OrientationCommandResult, true, "orientation">;
851
+ readonly 'app-switcher': NavigationCommandProjection<{}, AppSwitcherCommandResult, false, "appSwitcher">;
852
+ readonly 'tv-remote': NavigationCommandProjection<{
853
+ button: TvRemoteButton;
854
+ durationMs?: number;
855
+ }, TvRemoteCommandResult, true, "tvRemote">;
1305
856
  };
1306
- type PinchOptions = DeviceCommandBaseOptions & {
1307
- scale: number;
1308
- x?: number;
1309
- y?: number;
857
+ type NavigationCommandName = keyof typeof NAVIGATION_COMMAND_PROJECTIONS;
858
+ type NavigationCommandOptions<Name extends NavigationCommandName> = (typeof NAVIGATION_COMMAND_PROJECTIONS)[Name] extends NavigationCommandProjection<infer Options, unknown, boolean, string> ? Options : never;
859
+ type ProjectedNavigationCommandMethod<BaseOptions, Projection> = Projection extends NavigationCommandProjection<infer Options, infer Result, infer Required, string> ? Required extends true ? (options: BaseOptions & Options) => Promise<Result> : (options?: BaseOptions & Options) => Promise<Result> : never;
860
+ type ProjectedNavigationCommandClient<BaseOptions> = { [Name in NavigationCommandName as (typeof NAVIGATION_COMMAND_PROJECTIONS)[Name]['clientMethod']]: ProjectedNavigationCommandMethod<BaseOptions, (typeof NAVIGATION_COMMAND_PROJECTIONS)[Name]>; };
861
+ //#endregion
862
+ //#region src/contracts/debug-symbols.d.ts
863
+ type DebugSymbolsOptions = {
864
+ action?: 'symbols';
865
+ artifact: string;
866
+ dsym?: string;
867
+ searchPath?: string;
868
+ out?: string;
869
+ cwd?: string;
1310
870
  };
1311
- type RotateGestureOptions = DeviceCommandBaseOptions & {
1312
- degrees: number;
1313
- x?: number;
1314
- y?: number;
871
+ type DebugSymbolsImage = {
872
+ name: string;
873
+ uuid: string;
874
+ arch?: string;
875
+ dsymPath: string;
876
+ binaryPath: string;
877
+ };
878
+ type DebugSymbolsCrashFrame = {
879
+ index: number;
880
+ image: string;
881
+ address: string;
882
+ symbol?: string;
883
+ };
884
+ type DebugSymbolsCrashSummary = {
885
+ format: 'ips' | 'text';
886
+ appName?: string;
887
+ bundleId?: string;
888
+ version?: string;
889
+ incident?: string;
890
+ timestamp?: string;
891
+ exceptionType?: string;
892
+ exceptionCodes?: string;
893
+ terminationReason?: string;
894
+ crashedThread?: number;
895
+ topFrames: DebugSymbolsCrashFrame[];
896
+ findings: string[];
897
+ };
898
+ type DebugSymbolsResult = {
899
+ kind: 'debugSymbols';
900
+ platform: 'apple';
901
+ artifactPath: string;
902
+ outPath: string;
903
+ crash: DebugSymbolsCrashSummary;
904
+ matchedImages: DebugSymbolsImage[];
905
+ symbolicatedFrames: number;
906
+ skippedImages: number;
907
+ warnings?: string[];
908
+ message: string;
1315
909
  };
1316
- type TransformGestureOptions = DeviceCommandBaseOptions & TransformGestureParams;
910
+ //#endregion
911
+ //#region src/contracts/device.d.ts
1317
912
  /**
1318
- * #1271 stage 2 (ADR 0012 amendment): `get`/`is`/`find` are observation-only
1319
- * and excluded from a repair-armed heal by default. `record` forces this
1320
- * action through (the corrective-read case); `noRecord` continues to opt the
1321
- * action out entirely. Mutually exclusive.
913
+ * Closed result of the `boot` command. Mirrors the daemon handler's only
914
+ * success return EXACTLY (src/daemon/handlers/session-state.ts) — the fixed
915
+ * object literal `{ platform, target, device, id, kind, booted }` plus the
916
+ * additive `appleOs` discriminant, emitted only for Apple devices.
1322
917
  */
1323
- type RecordControlOptions = {
1324
- noRecord?: boolean;
1325
- record?: boolean;
1326
- };
1327
- type GetOptions = DeviceCommandBaseOptions & SelectorSnapshotCommandOptions & ElementTarget & RecordControlOptions & {
1328
- format: 'text' | 'attrs';
1329
- };
1330
- type IsTextPredicateOptions = DeviceCommandBaseOptions & SelectorSnapshotCommandOptions & RecordControlOptions & {
1331
- predicate: 'text';
1332
- selector: string;
1333
- value: string;
1334
- };
1335
- type IsStatePredicateOptions = DeviceCommandBaseOptions & SelectorSnapshotCommandOptions & RecordControlOptions & {
1336
- predicate: 'visible' | 'hidden' | 'exists' | 'editable' | 'selected' | 'focused';
1337
- selector: string;
1338
- value?: never;
1339
- };
1340
- type IsOptions = IsTextPredicateOptions | IsStatePredicateOptions;
1341
- type FindBaseOptions = DeviceCommandBaseOptions & FindSnapshotCommandOptions & RecordControlOptions & {
1342
- locator?: FindLocator;
1343
- query: string;
1344
- first?: boolean;
1345
- last?: boolean;
1346
- };
1347
- type FindOptions = (FindBaseOptions & {
1348
- action?: 'click' | 'focus' | 'exists' | 'getText' | 'getAttrs';
1349
- }) | (FindBaseOptions & {
1350
- action: 'wait';
1351
- timeoutMs?: number;
1352
- }) | (FindBaseOptions & {
1353
- action: 'fill' | 'type';
1354
- value: string;
1355
- });
1356
- type ReplayRunOptions = AgentDeviceRequestOverrides & AgentDeviceSelectionOptions & {
1357
- path: string;
1358
- runtime?: SessionRuntimeHints;
1359
- /**
1360
- * @deprecated ADR 0012 migration step 6: `--update` no longer rewrites
1361
- * the script. Accepted for backward compatibility; every divergence
1362
- * already carries ranked selector suggestions regardless of this flag.
1363
- */
1364
- update?: boolean;
1365
- /** @deprecated Use backend: 'maestro'. */
1366
- maestro?: boolean;
1367
- backend?: string;
1368
- env?: string[];
1369
- timeoutMs?: number;
918
+ type BootCommandResult = {
919
+ platform: PublicPlatform;
920
+ target: DeviceTarget;
921
+ /** Human-readable device name (`device.name`). */
922
+ device: string;
923
+ /** Stable device id (`device.id`). */
924
+ id: string;
925
+ kind: DeviceKind;
926
+ /** Always `true` on the success path. */
927
+ booted: true;
1370
928
  /**
1371
- * ADR 0012 decision 4 / migration step 5: resume at this 1-based plan
1372
- * step, skipping `1..resumeFrom-1` without executing them. Requires
1373
- * `resumePlanDigest` from the divergence report that reported this
1374
- * step as the failure. `replay` only — `test` has no resume fields.
929
+ * Additive Apple-OS discriminant (`device.appleOs`): iPhone/iPad/tvOS/visionOS/macOS.
930
+ * Present only for Apple devices; absent for non-Apple platforms. `platform` stays the
931
+ * leaf (`ios`/`macos`) — this is an extra field, not a replacement.
1375
932
  */
1376
- resumeFrom?: number;
1377
- /** The `resume.planDigest` from the divergence report `resumeFrom` came from. */
1378
- resumePlanDigest?: string;
933
+ appleOs?: AppleOS;
934
+ };
935
+ /**
936
+ * Closed result of the `shutdown` command. Mirrors the daemon handler's success
937
+ * return EXACTLY (src/daemon/handlers/session-state.ts) — the fixed object
938
+ * literal `{ platform, target, device, id, kind, shutdown }` plus the additive
939
+ * `appleOs` discriminant (Apple devices only). The `shutdown` field is the raw
940
+ * {@link TargetShutdownResult} from `shutdownDeviceTarget`.
941
+ */
942
+ type ShutdownCommandResult = {
943
+ platform: PublicPlatform;
944
+ target: DeviceTarget;
945
+ /** Human-readable device name (`device.name`). */
946
+ device: string;
947
+ /** Stable device id (`device.id`). */
948
+ id: string;
949
+ kind: DeviceKind;
950
+ shutdown: TargetShutdownResult;
1379
951
  /**
1380
- * ADR 0012 decision 6, R1/R6: arms agent-supervised re-record repair
1381
- * from this replay attempt onward. Optional string value is the healed
1382
- * `.ad`'s output path; absent one, it defaults to the `<path>` sibling
1383
- * `<stem>.healed.ad` when the repair ends with `close --save-script`.
952
+ * Additive Apple-OS discriminant (`device.appleOs`): iPhone/iPad/tvOS/visionOS/macOS.
953
+ * Present only for Apple devices; absent for non-Apple platforms. `platform` stays the
954
+ * leaf (`ios`/`macos`) — this is an extra field, not a replacement.
1384
955
  */
1385
- saveScript?: boolean | string;
1386
- /** #1258: overwrite an existing --save-script target instead of refusing. Alias: --overwrite. */
1387
- force?: boolean;
956
+ appleOs?: AppleOS;
1388
957
  };
1389
- type ReplayTestOptions = AgentDeviceRequestOverrides & AgentDeviceSelectionOptions & {
1390
- paths: string[];
1391
- runtime?: SessionRuntimeHints;
1392
- update?: boolean;
1393
- /** @deprecated Use backend: 'maestro'. */
1394
- maestro?: boolean;
1395
- backend?: string;
1396
- env?: string[];
1397
- failFast?: boolean;
1398
- timeoutMs?: number;
1399
- retries?: number;
1400
- recordVideo?: boolean;
1401
- artifactsDir?: string;
1402
- /** @deprecated Use the CLI --reporter junit:<path> or --report-junit <path>. */
1403
- reportJunit?: string;
1404
- shardAll?: number;
1405
- shardSplit?: number;
1406
- };
1407
- type BatchStep = {
1408
- command: string;
1409
- input: Record<string, unknown>;
1410
- runtime?: SessionRuntimeHints;
958
+ //#endregion
959
+ //#region src/contracts/viewport.d.ts
960
+ /**
961
+ * Closed result of the `viewport` command. Mirrors the dispatch handler's return
962
+ * EXACTLY (src/core/dispatch.ts `handleViewportCommand`) — `{ width, height }`
963
+ * plus the always-present `successText` message. The generic dispatch path
964
+ * returns this object unchanged (viewport has no Android dialog guard, so no
965
+ * `warning` is ever appended), so the shape is intentionally closed.
966
+ */
967
+ type ViewportCommandResult = {
968
+ width: number;
969
+ height: number;
970
+ message: string;
1411
971
  };
1412
- type BatchRunOptions = AgentDeviceRequestOverrides & {
1413
- steps: BatchStep[];
1414
- onError?: 'stop';
1415
- maxSteps?: number;
1416
- out?: string;
972
+ //#endregion
973
+ //#region src/contracts/clipboard.d.ts
974
+ /**
975
+ * Closed result of the `clipboard` command. Mirrors the dispatch handler's
976
+ * literal return EXACTLY (src/core/dispatch.ts `handleClipboardCommand`): a
977
+ * discriminated union on `action`. `read` returns the clipboard `text`; `write`
978
+ * reports the written `textLength` plus the `successText` message. The handler
979
+ * spreads nothing else, so each branch is closed.
980
+ */
981
+ type ClipboardCommandResult = {
982
+ action: 'read';
983
+ text: string;
984
+ } | {
985
+ action: 'write';
986
+ textLength: number;
987
+ message: string;
1417
988
  };
1418
- type PerfOptions = DeviceCommandBaseOptions & {
1419
- area?: PerfArea;
1420
- subject?: PerfSubject;
1421
- action?: PerfAction;
1422
- kind?: PerfKind;
1423
- template?: string;
1424
- out?: string;
1425
- tracePath?: string;
989
+ //#endregion
990
+ //#region src/contracts/app-state.d.ts
991
+ /**
992
+ * Closed result of the `appstate` command, grounded in the daemon handler's
993
+ * success returns (src/daemon/handlers/session-state.ts `handleAppStateCommand`).
994
+ * A discriminated union on `platform`:
995
+ * - Apple (`ios` / `macos`) session state, with iOS-only device locators that
996
+ * the previous hand-written mirror omitted; and
997
+ * - Android foreground `package` / `activity`.
998
+ *
999
+ * The handler returns one of these fixed objects (errors take the `ok: false`
1000
+ * path), so each branch is closed.
1001
+ */
1002
+ type AppStateCommandResult = {
1003
+ platform: 'ios' | 'macos';
1004
+ appName: string;
1005
+ appBundleId?: string;
1006
+ source: 'session';
1007
+ surface: SessionSurface;
1008
+ /** iOS only — the session device's UDID. */
1009
+ device_udid?: string;
1010
+ /** iOS only — the simulator set path, or `null` when unknown. */
1011
+ ios_simulator_device_set?: string | null;
1012
+ } | {
1013
+ platform: 'android';
1014
+ package: string;
1015
+ activity: string;
1426
1016
  };
1427
- type LogsOptions = AgentDeviceRequestOverrides & {
1428
- action?: LogAction;
1017
+ //#endregion
1018
+ //#region src/contracts/keyboard.d.ts
1019
+ /**
1020
+ * Closed result of the `keyboard` command, grounded in the dispatch handlers'
1021
+ * literal returns (src/core/dispatch.ts `handleAndroidKeyboardCommand` /
1022
+ * `handleIosKeyboardCommand`).
1023
+ *
1024
+ * `platform` and `action` are always present; the remaining fields appear per
1025
+ * branch (Android `status`/`dismiss` carry the keyboard-state fields; `enter`
1026
+ * and iOS `dismiss` carry a `message`). It is kept as a flat closed shape rather
1027
+ * than a five-way `platform`×`action` union because the per-branch field sets
1028
+ * overlap heavily and the underlying Android keyboard-state types live in the
1029
+ * platform layer (below the public contract). The `Record` index signature of
1030
+ * the previous hand-written mirror is dropped, and the spurious `| null`s are
1031
+ * removed (the handler never returns `null` for these).
1032
+ */
1033
+ type KeyboardCommandResult = {
1034
+ platform: 'android' | 'ios';
1035
+ action: 'status' | 'dismiss' | 'enter';
1036
+ visible?: boolean;
1037
+ wasVisible?: boolean;
1038
+ dismissed?: boolean;
1039
+ attempts?: number;
1040
+ inputType?: string;
1041
+ type?: 'text' | 'number' | 'email' | 'phone' | 'password' | 'datetime' | 'unknown';
1042
+ inputMethodPackage?: string;
1043
+ focusedPackage?: string;
1044
+ focusedResourceId?: string;
1045
+ inputOwner?: 'app' | 'ime' | 'unknown';
1429
1046
  message?: string;
1430
- restart?: boolean;
1431
1047
  };
1432
- type EventsOptions = AgentDeviceRequestOverrides & {
1433
- cursor?: string;
1434
- limit?: number;
1048
+ //#endregion
1049
+ //#region src/contracts/wait.d.ts
1050
+ /**
1051
+ * Public daemon result for `wait`. The runtime-local result carries a `kind`
1052
+ * discriminant, but `toDaemonWaitData` intentionally projects the normal daemon
1053
+ * payload to this compact shape. The direct iOS selector fast path may still
1054
+ * include `kind: 'selector'` additively.
1055
+ */
1056
+ type WaitCommandResult = {
1057
+ waitedMs: number;
1058
+ kind?: 'selector';
1059
+ text?: string;
1060
+ selector?: string;
1061
+ captures?: number;
1062
+ nodeCount?: number;
1063
+ hint?: string;
1064
+ warning?: string;
1435
1065
  };
1436
- type NetworkOptions = AgentDeviceRequestOverrides & {
1437
- action?: 'dump' | 'log';
1438
- limit?: number;
1439
- include?: NetworkIncludeMode;
1066
+ //#endregion
1067
+ //#region src/contracts/prepare.d.ts
1068
+ type PrepareIosRunnerCacheKind = 'exact' | 'restore-key' | 'miss' | 'external';
1069
+ type PrepareIosRunnerArtifactState = 'valid' | 'rebuilt';
1070
+ type PrepareIosRunnerTiming = {
1071
+ totalMs: number;
1072
+ additiveParts: {
1073
+ buildMs?: number;
1074
+ connectAfterBuildMs: number;
1075
+ healthCheckMs: number;
1076
+ };
1077
+ containment: {
1078
+ connectMs?: ['buildMs'];
1079
+ healthCheckMs: [];
1080
+ };
1081
+ note: string;
1440
1082
  };
1441
- type AudioOptions = AgentDeviceRequestOverrides & {
1442
- action?: 'probe';
1443
- probeAction?: 'start' | 'status' | 'stop';
1444
- durationMs?: number;
1445
- bucketMs?: number;
1083
+ /**
1084
+ * Public daemon result for `prepare ios-runner`. The runner-local prepare result
1085
+ * is projected by `prepareIosRunnerResponseData` with device identity and timing
1086
+ * guidance before it reaches the client.
1087
+ */
1088
+ type PrepareCommandResult = {
1089
+ action: 'ios-runner';
1090
+ platform: PublicPlatform;
1091
+ deviceId: string;
1092
+ deviceName: string;
1093
+ kind: DeviceKind;
1094
+ durationMs: number;
1095
+ runner: JsonObject;
1096
+ cache?: PrepareIosRunnerCacheKind;
1097
+ artifact?: PrepareIosRunnerArtifactState;
1098
+ buildMs?: number;
1099
+ connectMs: number;
1100
+ healthCheckMs: number;
1101
+ xctestrunPath?: string;
1102
+ recoveryReason?: string;
1103
+ failureReason?: string;
1104
+ timing: PrepareIosRunnerTiming;
1105
+ message: string;
1446
1106
  };
1447
- type RecordOptions = AgentDeviceRequestOverrides & {
1448
- action: 'start' | 'stop';
1449
- path?: string;
1450
- fps?: number;
1451
- maxSize?: number;
1452
- quality?: RecordingExportQuality;
1453
- hideTouches?: boolean;
1107
+ //#endregion
1108
+ //#region src/contracts/push.d.ts
1109
+ type PushCommandResult = {
1110
+ platform: 'ios';
1111
+ bundleId: string;
1112
+ message: string;
1113
+ } | {
1114
+ platform: 'android';
1115
+ package: string;
1116
+ action: string;
1117
+ extrasCount: number;
1118
+ message: string;
1119
+ };
1120
+ //#endregion
1121
+ //#region src/contracts/app-events.d.ts
1122
+ type TriggerAppEventCommandResult = {
1123
+ event: string;
1124
+ eventUrl: string;
1125
+ transport: 'deep-link';
1126
+ message: string;
1127
+ };
1128
+ //#endregion
1129
+ //#region src/contracts/doctor.d.ts
1130
+ type DoctorStatus = 'pass' | 'warn' | 'fail' | 'info';
1131
+ type DoctorKind = 'auto' | 'react-native' | 'expo' | 'repack';
1132
+ type DoctorCheck = {
1133
+ id: string;
1134
+ status: DoctorStatus;
1135
+ summary: string;
1136
+ hint?: string;
1137
+ command?: string;
1138
+ evidence?: Record<string, unknown>;
1139
+ };
1140
+ type DoctorCommandResult = {
1141
+ status: DoctorStatus;
1142
+ summary: string;
1143
+ kind: DoctorKind;
1144
+ platform?: PlatformSelector;
1145
+ target?: DeviceTarget;
1146
+ targetApp?: string;
1147
+ metro?: {
1148
+ host: string;
1149
+ port: number;
1150
+ };
1151
+ checks: DoctorCheck[];
1152
+ };
1153
+ //#endregion
1154
+ //#region src/snapshot/snapshot-diff.d.ts
1155
+ type SnapshotDiffLine = {
1156
+ kind: 'added' | 'removed' | 'unchanged';
1157
+ text: string;
1158
+ /**
1159
+ * Plain ref body (`e12`) of the CURRENT-tree node behind an added line.
1160
+ * Only populated with `withRefs` (interaction `--settle`, #1101) so the
1161
+ * `diff` command's wire shape stays byte-identical. Removed/unchanged lines
1162
+ * never carry it: a removed line's ref names a node of the replaced tree.
1163
+ */
1164
+ ref?: string;
1165
+ };
1166
+ type SnapshotDiffSummary = {
1167
+ additions: number;
1168
+ removals: number;
1169
+ unchanged: number;
1170
+ };
1171
+ //#endregion
1172
+ //#region src/contracts/diff.d.ts
1173
+ type DiffSnapshotCommandResult = {
1174
+ mode: 'snapshot';
1175
+ baselineInitialized: boolean;
1176
+ summary: SnapshotDiffSummary;
1177
+ lines: SnapshotDiffLine[];
1178
+ warnings?: string[];
1179
+ };
1180
+ //#endregion
1181
+ //#region src/contracts/recording.d.ts
1182
+ type RecordingAppIdentity = {
1183
+ bundleId: string;
1184
+ name?: string;
1185
+ };
1186
+ type RecordingStartCommandResult = {
1187
+ recording: 'started';
1188
+ outPath: string;
1189
+ sessionStateDir: string;
1190
+ recordingBackend?: string;
1454
1191
  recordingScope?: RecordingScope;
1192
+ recordOnlySession?: boolean;
1193
+ activeSessionApp?: RecordingAppIdentity;
1194
+ showTouches: boolean;
1455
1195
  };
1456
- type TraceOptions = AgentDeviceRequestOverrides & {
1457
- action: 'start' | 'stop';
1458
- path?: string;
1196
+ type RecordingStopCommandResult = {
1197
+ recording: 'stopped';
1198
+ outPath: string;
1199
+ telemetryPath?: string;
1200
+ artifacts: DaemonArtifact[];
1201
+ recordingBackend?: string;
1202
+ recordingScope?: RecordingScope;
1203
+ recordOnlySession?: boolean;
1204
+ activeSessionApp?: RecordingAppIdentity;
1205
+ durationMs: number;
1206
+ showTouches: boolean;
1207
+ warning?: string;
1208
+ overlayWarning?: string;
1209
+ chunks?: Array<{
1210
+ index: number;
1211
+ path: string;
1212
+ }>;
1213
+ };
1214
+ type RecordingCommandResult = RecordingStartCommandResult | RecordingStopCommandResult;
1215
+ type TraceCommandResult = {
1216
+ trace: 'started';
1217
+ outPath: string;
1218
+ } | {
1219
+ trace: 'stopped';
1220
+ outPath: string;
1221
+ };
1222
+ //#endregion
1223
+ //#region src/contracts/replay.d.ts
1224
+ type ReplayCommandResult = {
1225
+ replayed: number;
1226
+ healed: number;
1227
+ session: string;
1228
+ /**
1229
+ * True iff `session` still exists in the daemon's session store when the
1230
+ * response is built — i.e. the replayed script had no terminal `close`
1231
+ * (ADR 0016's consumption contract). The client uses this, not script
1232
+ * parsing, to decide whether an owned one-shot daemon must stay alive so
1233
+ * the caller can keep addressing this session.
1234
+ */
1235
+ sessionActive: boolean;
1236
+ artifactPaths: string[];
1237
+ warnings?: string[];
1238
+ snapshotDiagnostics?: SnapshotDiagnosticsSummary;
1239
+ message: string;
1240
+ };
1241
+ type ReplaySuiteTestSkipReason = 'skipped-by-filter';
1242
+ type ReplaySuiteAttemptFailure = {
1243
+ attempt: number;
1244
+ message: string;
1245
+ durationMs?: number;
1246
+ };
1247
+ type ReplaySuiteTestPassed = {
1248
+ file: string;
1249
+ title?: string;
1250
+ session: string;
1251
+ status: 'passed';
1252
+ durationMs: number;
1253
+ finalAttemptDurationMs?: number;
1254
+ attempts: number;
1255
+ artifactsDir?: string;
1256
+ replayed: number;
1257
+ healed: number;
1258
+ warnings?: string[];
1259
+ attemptFailures?: ReplaySuiteAttemptFailure[];
1260
+ shardIndex?: number;
1261
+ shardCount?: number;
1262
+ deviceId?: string;
1263
+ deviceName?: string;
1264
+ snapshotDiagnostics?: SnapshotDiagnosticsSummary;
1265
+ };
1266
+ type ReplaySuiteTestFailed = {
1267
+ file: string;
1268
+ title?: string;
1269
+ session: string;
1270
+ status: 'failed';
1271
+ durationMs: number;
1272
+ attempts: number;
1273
+ artifactsDir?: string;
1274
+ error: DaemonError;
1275
+ shardIndex?: number;
1276
+ shardCount?: number;
1277
+ deviceId?: string;
1278
+ deviceName?: string;
1279
+ snapshotDiagnostics?: SnapshotDiagnosticsSummary;
1280
+ };
1281
+ type ReplaySuiteTestSkipped = {
1282
+ file: string;
1283
+ title?: string;
1284
+ status: 'skipped';
1285
+ durationMs: 0;
1286
+ reason: ReplaySuiteTestSkipReason;
1287
+ message: string;
1288
+ };
1289
+ type ReplaySuiteTestResult = ReplaySuiteTestPassed | ReplaySuiteTestFailed | ReplaySuiteTestSkipped;
1290
+ type ReplaySuiteResult = {
1291
+ total: number;
1292
+ executed: number;
1293
+ passed: number;
1294
+ failed: number;
1295
+ skipped: number;
1296
+ notRun: number;
1297
+ durationMs: number;
1298
+ failures: ReplaySuiteTestFailed[];
1299
+ tests: ReplaySuiteTestResult[];
1300
+ snapshotDiagnostics?: SnapshotDiagnosticsSummary;
1301
+ };
1302
+ //#endregion
1303
+ //#region src/core/command-descriptor/command-result.d.ts
1304
+ /**
1305
+ * The additive typed-result spine (ADR-0008, Phase 1 step 6).
1306
+ *
1307
+ * Maps a command name to the per-command result type from `src/contracts/*`. It
1308
+ * is SEEDED, not exhaustive: a command is listed here only once its accurate,
1309
+ * closed result shape lives in the contracts layer. Commands whose daemon
1310
+ * handler spreads dynamic/Record data (screenshot overlays, gesture
1311
+ * visualization, perf, logs, …) are deliberately omitted rather than given an
1312
+ * invented shape.
1313
+ *
1314
+ * Batches 1-2 wired `boot` / `shutdown` / `viewport` and the navigation/action
1315
+ * commands `home` / `back` / `orientation` / `app-switcher` alongside the seed
1316
+ * interaction quartet. Batch 3 adds `clipboard` (a closed `read`/`write` union) and
1317
+ * `appstate` (a closed `platform` union — Apple session state with the iOS-only
1318
+ * device locators, or Android package/activity). Batch 4 adds `keyboard` (a
1319
+ * closed flat shape). Batch 5 adds the compact daemon projections for `wait`,
1320
+ * `prepare`, `push`, and `trigger-app-event`. Each entry is grounded in a
1321
+ * re-read of the handler's literal return; see the per-type docstrings.
1322
+ */
1323
+ interface CommandResultMap {
1324
+ press: PressCommandResponseData;
1325
+ click: ClickCommandResponseData;
1326
+ fill: FillCommandResponseData;
1327
+ longpress: LongPressCommandResponseData;
1328
+ find: FindCommandResponseData;
1329
+ boot: BootCommandResult;
1330
+ shutdown: ShutdownCommandResult;
1331
+ viewport: ViewportCommandResult;
1332
+ home: HomeCommandResult;
1333
+ back: BackCommandResult;
1334
+ orientation: OrientationCommandResult;
1335
+ 'app-switcher': AppSwitcherCommandResult;
1336
+ clipboard: ClipboardCommandResult;
1337
+ appstate: AppStateCommandResult;
1338
+ keyboard: KeyboardCommandResult;
1339
+ 'tv-remote': TvRemoteCommandResult;
1340
+ wait: WaitCommandResult;
1341
+ prepare: PrepareCommandResult;
1342
+ push: PushCommandResult;
1343
+ 'trigger-app-event': TriggerAppEventCommandResult;
1344
+ doctor: DoctorCommandResult;
1345
+ diff: DiffSnapshotCommandResult;
1346
+ replay: ReplayCommandResult;
1347
+ test: ReplaySuiteResult;
1348
+ record: RecordingCommandResult;
1349
+ trace: TraceCommandResult;
1350
+ }
1351
+ /**
1352
+ * The typed result for a command named `N`. Seeded commands resolve to their
1353
+ * contract result type from {@link CommandResultMap}; every other (unmigrated)
1354
+ * command falls back to the untyped `Record<string, unknown>` bag. That default
1355
+ * branch is what keeps the mapping total over every command name, so consumers
1356
+ * can switch to `CommandResult<Name>` without first migrating every command.
1357
+ */
1358
+ type CommandResult<N extends string> = N extends keyof CommandResultMap ? CommandResultMap[N] : Record<string, unknown>;
1359
+ //#endregion
1360
+ //#region src/client/client-types.d.ts
1361
+ type OrientationCommandOptions = DeviceCommandBaseOptions & NavigationCommandOptions<'orientation'>;
1362
+ /** @deprecated Renamed to `OrientationCommandOptions`. Retained until the next major. */
1363
+ type RotateCommandOptions = OrientationCommandOptions;
1364
+ type NonNavigationCommandClient = {
1365
+ wait: (options: WaitCommandOptions) => Promise<CommandResult<'wait'>>;
1366
+ alert: (options?: AlertCommandOptions) => Promise<CommandRequestResult>;
1367
+ appState: (options?: AppStateCommandOptions) => Promise<CommandResult<'appstate'>>;
1368
+ keyboard: (options?: KeyboardCommandOptions) => Promise<CommandResult<'keyboard'>>;
1369
+ clipboard: (options: ClipboardCommandOptions) => Promise<CommandResult<'clipboard'>>;
1370
+ reactNative: (options: ReactNativeCommandOptions) => Promise<CommandRequestResult>;
1371
+ doctor: (options?: DoctorCommandOptions) => Promise<CommandResult<'doctor'>>;
1372
+ /**
1373
+ * JSON prepare results include timing.additiveParts for additive wall-clock phases.
1374
+ * Top-level buildMs/connectMs/healthCheckMs are diagnostics and may overlap.
1375
+ */
1376
+ prepare: (options: PrepareCommandOptions) => Promise<CommandResult<'prepare'>>;
1377
+ viewport: (options: ViewportCommandOptions) => Promise<CommandResult<'viewport'>>;
1378
+ };
1379
+ type AgentDeviceCommandClient = ProjectedNavigationCommandClient<DeviceCommandBaseOptions> & NonNavigationCommandClient & DeprecatedCommandClient;
1380
+ /** Renamed command methods retained for existing consumers until the next major. */
1381
+ type DeprecatedCommandClient = {
1382
+ /**
1383
+ * @deprecated Renamed to `orientation`. Delegates to it and returns the legacy
1384
+ * `action: 'rotate'` response contract. Retained until the next major version.
1385
+ */
1386
+ rotate: (options: RotateCommandOptions) => Promise<RotateCommandResult>;
1459
1387
  };
1460
- type PermissionTarget = 'camera' | 'microphone' | 'photos' | 'contacts' | 'contacts-limited' | 'notifications' | 'calendar' | 'location' | 'location-always' | 'media-library' | 'motion' | 'reminders' | 'siri' | 'accessibility' | 'screen-recording' | 'input-monitoring';
1461
- type SettingsUpdateOptions = (DeviceCommandBaseOptions & {
1462
- setting: 'clear-app-state';
1463
- state: 'clear';
1464
- app?: string;
1465
- }) | (DeviceCommandBaseOptions & {
1466
- setting: 'wifi' | 'airplane' | 'location';
1467
- state: 'on' | 'off';
1468
- }) | (DeviceCommandBaseOptions & {
1469
- setting: 'location';
1470
- state: 'set';
1471
- latitude: number;
1472
- longitude: number;
1473
- }) | (DeviceCommandBaseOptions & {
1474
- setting: 'animations';
1475
- state: 'on' | 'off';
1476
- }) | (DeviceCommandBaseOptions & {
1477
- setting: 'appearance';
1478
- state: 'light' | 'dark' | 'toggle';
1479
- }) | (DeviceCommandBaseOptions & {
1480
- setting: 'faceid' | 'touchid';
1481
- state: 'match' | 'nonmatch' | 'enroll' | 'unenroll';
1482
- }) | (DeviceCommandBaseOptions & {
1483
- setting: 'fingerprint';
1484
- state: 'match' | 'nonmatch';
1485
- }) | (DeviceCommandBaseOptions & {
1486
- setting: 'permission';
1487
- state: 'grant' | 'deny' | 'reset';
1488
- permission: PermissionTarget;
1489
- mode?: 'full' | 'limited';
1490
- });
1491
- type CommandRequestResult = DaemonResponseData;
1492
1388
  type AgentDeviceClient = {
1493
1389
  command: AgentDeviceCommandClient;
1494
1390
  devices: {