agent-device 0.20.5 → 0.20.7
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.
- package/README.md +5 -3
- package/android/ime-helper/dist/{agent-device-android-ime-helper-0.20.5.apk → agent-device-android-ime-helper-0.20.7.apk} +0 -0
- package/android/ime-helper/dist/agent-device-android-ime-helper-0.20.7.apk.sha256 +1 -0
- package/android/ime-helper/dist/{agent-device-android-ime-helper-0.20.5.manifest.json → agent-device-android-ime-helper-0.20.7.manifest.json} +4 -4
- package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.20.5.apk → agent-device-android-snapshot-helper-0.20.7.apk} +0 -0
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.7.apk.sha256 +1 -0
- package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.20.5.manifest.json → agent-device-android-snapshot-helper-0.20.7.manifest.json} +6 -6
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunner/AgentDeviceRunnerApp.m +48 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/AgentDeviceRunnerUITests-Bridging-Header.h +1 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.h +108 -1
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.m +630 -89
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSelectorMatchPolicy.swift +78 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.m +126 -187
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedTextEntry.h +32 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedTextEntry.m +212 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+AXSnapshotFallback.swift +149 -10
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +161 -32
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CoordinateTextEntryTests.swift +4 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +36 -28
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Keyboard.swift +23 -5
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Lifecycle.swift +10 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+LifecycleCacheTests.swift +0 -67
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +18 -1
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScreenRecorder.swift +2 -22
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SelectorMatchPolicyTests.swift +4 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +2 -1
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCapturePlan.swift +145 -20
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotTraversalIdentityTests.swift +0 -20
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedGesturePolicy.swift +4 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedTextEntry.swift +234 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntry.swift +106 -448
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntryPolicyTests.swift +4 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextTyping.swift +602 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +18 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerXCTestEventBridge.h +72 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerXCTestEventBridge.m +89 -0
- package/dist/src/agent-browser-tool.js +1 -1
- package/dist/src/agent-device-client.js +2 -2
- package/dist/src/android-adb.js +1 -1
- package/dist/src/android.js +1 -1
- package/dist/src/app-helpers.js +1 -1
- package/dist/src/app-lifecycle.js +7 -7
- package/dist/src/app-lifecycle2.js +2 -0
- package/dist/src/app-log-files.js +1 -0
- package/dist/src/app-log-reconnect.js +1 -0
- package/dist/src/app-log-runtime.js +4 -0
- package/dist/src/app-resolution.js +2 -1
- package/dist/src/apps.js +5 -5
- package/dist/src/audio-probe.js +1 -1
- package/dist/src/auth-session.js +1 -0
- package/dist/src/auth.js +2 -0
- package/dist/src/boot-diagnostics.js +2 -2
- package/dist/src/cli-config.js +1 -1
- package/dist/src/cli-grammar.js +1 -0
- package/dist/src/cli-help.js +204 -254
- package/dist/src/cli-output.js +1 -0
- package/dist/src/cli-runner.js +1 -2
- package/dist/src/cli.js +6 -16
- package/dist/src/client-metro.js +1 -1
- package/dist/src/cloud-artifacts.d.ts +2 -137
- package/dist/src/command-metadata.js +1 -1
- package/dist/src/command-schema.js +3 -3
- package/dist/src/connection-runtime.js +1 -0
- package/dist/src/connection.js +5 -0
- package/dist/src/context.js +1 -1
- package/dist/src/daemon-shutdown-report.js +1 -0
- package/dist/src/daemon.js +2 -0
- package/dist/src/debug-symbols.js +3 -3
- package/dist/src/device-claim-inspection.js +1 -0
- package/dist/src/device-input-state.js +2 -2
- package/dist/src/device.js +1 -1
- package/dist/src/device2.js +2 -0
- package/dist/src/find.js +1 -1
- package/dist/src/finders.js +1 -1
- package/dist/src/generic-settle.js +1 -0
- package/dist/src/generic.js +1 -1
- package/dist/src/harmonyos.js +1 -0
- package/dist/src/helper.js +1 -1
- package/dist/src/host-process.js +2 -2
- package/dist/src/ime-lifecycle.js +7 -7
- package/dist/src/index.d.ts +69 -18
- package/dist/src/input-actions.js +4 -4
- package/dist/src/input-actions2.js +1 -1
- package/dist/src/input-actions3.js +1 -0
- package/dist/src/install-artifact.js +1 -5
- package/dist/src/install-artifact2.js +1 -1
- package/dist/src/install-source2.js +1 -1
- package/dist/src/interaction-runtime.js +1 -0
- package/dist/src/interaction-snapshot.js +1 -1
- package/dist/src/interaction-touch-reference-frame.js +1 -0
- package/dist/src/interaction.js +1 -1
- package/dist/src/interactor.js +2 -2
- package/dist/src/interactor2.js +1 -1
- package/dist/src/internal/bin.js +2 -2
- package/dist/src/internal/daemon.js +9 -16
- package/dist/src/inventory.js +5 -0
- package/dist/src/inventory2.js +1 -0
- package/dist/src/inventory3.js +2 -0
- package/dist/src/inventory4.js +1 -0
- package/dist/src/inventory5.js +1 -0
- package/dist/src/inventory6.js +1 -0
- package/dist/src/lease-context.js +1 -1
- package/dist/src/lease.js +1 -1
- package/dist/src/limrun-runtime-dependencies.js +1 -0
- package/dist/src/limrun.d.ts +10 -104
- package/dist/src/limrun.js +1 -1
- package/dist/src/linux.js +1 -1
- package/dist/src/logcat.js +1 -1
- package/dist/src/manifest.js +1 -1
- package/dist/src/owner-identity.js +1 -0
- package/dist/src/physical-device-control.js +1 -1
- package/dist/src/platform-runtime-app-log-android-transport.js +1 -0
- package/dist/src/platform-runtime-app-log-process.js +1 -0
- package/dist/src/platform-runtime-host.js +1 -0
- package/dist/src/platform-runtime-network-web-transport.js +1 -0
- package/dist/src/platform-runtime-operation-host.js +5 -0
- package/dist/src/platform-runtime-toolchain-host.js +1 -0
- package/dist/src/platform-runtime.js +1 -0
- package/dist/src/prepare-kind.js +15 -0
- package/dist/src/process-entry.js +1 -0
- package/dist/src/process-exit.js +1 -0
- package/dist/src/process-lock.js +1 -1
- package/dist/src/provider-webdriver.js +1 -0
- package/dist/src/provider.js +1 -0
- package/dist/src/proxy.js +2 -0
- package/dist/src/react-native.js +1 -1
- package/dist/src/record-trace.js +1 -1
- package/dist/src/registry.js +25 -24
- package/dist/src/remote-config.js +1 -1
- package/dist/src/remote-config2.js +1 -1
- package/dist/src/replay.js +3 -0
- package/dist/src/reporting.js +4 -4
- package/dist/src/runner-client.js +1 -1
- package/dist/src/runner-disposal.js +1 -1
- package/dist/src/runner-lease.js +1 -1
- package/dist/src/runtime.js +3 -3
- package/dist/src/runtime2.js +4 -0
- package/dist/src/runtime3.js +3 -0
- package/dist/src/runtime4.js +1 -0
- package/dist/src/runtime5.js +1 -0
- package/dist/src/runtime6.js +1 -0
- package/dist/src/runtime7.js +1 -0
- package/dist/src/screenshot-result.js +4 -4
- package/dist/src/screenshot.js +1 -0
- package/dist/src/sdk-android-adb.d.ts +20 -2
- package/dist/src/sdk-android-adb.js +2 -2
- package/dist/src/sdk-batch-runner.js +3 -3
- package/dist/src/sdk-contracts.d.ts +47 -6
- package/dist/src/sdk-contracts.js +1 -1
- package/dist/src/sdk-device.js +1 -1
- package/dist/src/sdk-finders.d.ts +4 -20
- package/dist/src/sdk-finders.js +1 -1
- package/dist/src/sdk-remote-config.js +1 -1
- package/dist/src/sdk-selectors.d.ts +601 -0
- package/dist/src/sdk-selectors.js +1 -1
- package/dist/src/selector-runtime.js +1 -1
- package/dist/src/selector-vocabulary.js +1 -1
- package/dist/src/selectors.d.ts +1 -46
- package/dist/src/selectors.js +1 -1
- package/dist/src/server.js +3 -3
- package/dist/src/session-target-evidence.js +1 -1
- package/dist/src/session.js +10 -9
- package/dist/src/shared.js +2 -0
- package/dist/src/shell-quote.js +1 -0
- package/dist/src/simulator.js +1 -1
- package/dist/src/snapshot-runtime.js +1 -0
- package/dist/src/snapshot-session.js +1 -0
- package/dist/src/snapshot.js +1 -1
- package/dist/src/snapshot2.js +1 -1
- package/dist/src/src.js +1 -1
- package/dist/src/src2.js +2 -0
- package/dist/src/src3.js +1 -0
- package/dist/src/text-surface.js +1 -0
- package/dist/src/timing-safe-equal.js +1 -0
- package/dist/src/tool-provider.js +3 -3
- package/dist/src/tool-provider2.js +1 -1
- package/dist/src/tool-provider3.js +1 -1
- package/dist/src/update-check.js +1 -1
- package/dist/src/version.js +1 -1
- package/dist/src/web.js +1 -1
- package/package.json +72 -50
- package/server.json +3 -3
- package/skills/agent-device/SKILL.md +6 -31
- package/android/ime-helper/dist/agent-device-android-ime-helper-0.20.5.apk.sha256 +0 -1
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.5.apk.sha256 +0 -1
- package/dist/apple/runner/AgentDeviceRunner/RecordingScripts/recording-resize.swift +0 -225
- package/dist/src/app-log-request-scope.js +0 -1
- package/dist/src/devices.js +0 -1
- package/dist/src/devices2.js +0 -1
- package/dist/src/devices3.js +0 -1
- package/dist/src/devices4.js +0 -1
- package/dist/src/interaction.d.ts +0 -230
- package/dist/src/provider-limrun-runtime.js +0 -1
- package/dist/src/register-builtins.js +0 -1
- package/dist/src/screenshot-geometry.js +0 -1
- package/dist/src/viewport-dimension.js +0 -14
package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedTextEntry.m
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
#import "RunnerSynthesizedTextEntry.h"
|
|
2
|
+
#import "RunnerXCTestEventBridge.h"
|
|
3
|
+
|
|
4
|
+
#import <objc/message.h>
|
|
5
|
+
|
|
6
|
+
static NSString *const RunnerTextSynthesisSurface = @"text";
|
|
7
|
+
|
|
8
|
+
typedef id (*RunnerTextMsgSendInit)(id, SEL, NSString *);
|
|
9
|
+
typedef id (*RunnerTextMsgSendInitPath)(id, SEL);
|
|
10
|
+
typedef void (*RunnerTextMsgSendType)(id, SEL, NSString *, NSTimeInterval, NSUInteger, BOOL);
|
|
11
|
+
typedef void (*RunnerTextMsgSendTypeKey)(id, SEL, NSString *, NSUInteger, NSTimeInterval);
|
|
12
|
+
|
|
13
|
+
// Text-entry-specific extension of the shared bridge: the 1-arg `initWithName:`
|
|
14
|
+
// record initializer and the text-input path selectors, none of which gesture
|
|
15
|
+
// synthesis needs.
|
|
16
|
+
typedef struct {
|
|
17
|
+
RunnerXCTestEventBridge core;
|
|
18
|
+
SEL initRecordSelector;
|
|
19
|
+
SEL initPathSelector;
|
|
20
|
+
SEL typeTextSelector;
|
|
21
|
+
SEL typeKeySelector; // only required in `replace` mode
|
|
22
|
+
} RunnerTextEventBridge;
|
|
23
|
+
|
|
24
|
+
static NSString * _Nullable RunnerResolveTextEventBridge(
|
|
25
|
+
id application,
|
|
26
|
+
BOOL replace,
|
|
27
|
+
RunnerTextEventBridge *bridge
|
|
28
|
+
);
|
|
29
|
+
static RunnerSynthesizedTextEntryResult *RunnerTextEntryResult(
|
|
30
|
+
RunnerSynthesizedTextEntryStatus status,
|
|
31
|
+
NSString * _Nullable message
|
|
32
|
+
);
|
|
33
|
+
static RunnerSynthesizedTextEntryResult *RunnerTextEntryUnavailable(NSString *message);
|
|
34
|
+
static RunnerSynthesizedTextEntryResult *RunnerTextEntryFailed(NSString *message);
|
|
35
|
+
static RunnerSynthesizedTextEntryResult *RunnerSynthesizeTextWithMode(
|
|
36
|
+
id application,
|
|
37
|
+
NSString *text,
|
|
38
|
+
BOOL replace
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
@interface RunnerSynthesizedTextEntryResult ()
|
|
42
|
+
|
|
43
|
+
@property(nonatomic, readwrite) RunnerSynthesizedTextEntryStatus status;
|
|
44
|
+
@property(nonatomic, readwrite, nullable) NSString *message;
|
|
45
|
+
|
|
46
|
+
@end
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@implementation RunnerSynthesizedTextEntryResult
|
|
50
|
+
@end
|
|
51
|
+
|
|
52
|
+
@implementation RunnerSynthesizedTextEntry
|
|
53
|
+
|
|
54
|
+
+ (RunnerSynthesizedTextEntryResult *)synthesizeTextWithApplication:(id)application
|
|
55
|
+
text:(NSString *)text {
|
|
56
|
+
return RunnerSynthesizeTextWithMode(application, text, NO);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
+ (RunnerSynthesizedTextEntryResult *)replaceTextWithApplication:(id)application
|
|
60
|
+
text:(NSString *)text {
|
|
61
|
+
return RunnerSynthesizeTextWithMode(application, text, YES);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@end
|
|
65
|
+
|
|
66
|
+
static RunnerSynthesizedTextEntryResult *RunnerSynthesizeTextWithMode(
|
|
67
|
+
id application,
|
|
68
|
+
NSString *text,
|
|
69
|
+
BOOL replace
|
|
70
|
+
) {
|
|
71
|
+
@try {
|
|
72
|
+
RunnerTextEventBridge bridge;
|
|
73
|
+
NSString *missing = RunnerResolveTextEventBridge(application, replace, &bridge);
|
|
74
|
+
if (missing != nil) return RunnerTextEntryUnavailable(missing);
|
|
75
|
+
|
|
76
|
+
NSInteger targetProcessID =
|
|
77
|
+
((RunnerMsgSendInteger)objc_msgSend)(application, bridge.core.processIDSelector);
|
|
78
|
+
if (targetProcessID <= 0) {
|
|
79
|
+
return RunnerTextEntryUnavailable(
|
|
80
|
+
@"private XCTest text synthesis unavailable: could not resolve target process ID"
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (replace) {
|
|
85
|
+
id selectionRecord = ((RunnerTextMsgSendInit)objc_msgSend)(
|
|
86
|
+
[bridge.core.recordClass alloc], bridge.initRecordSelector, @"agent-device-fill-select-all"
|
|
87
|
+
);
|
|
88
|
+
id selectionPath =
|
|
89
|
+
((RunnerTextMsgSendInitPath)objc_msgSend)([bridge.core.pathClass alloc], bridge.initPathSelector);
|
|
90
|
+
if (selectionRecord == nil || selectionPath == nil) {
|
|
91
|
+
return RunnerTextEntryFailed(
|
|
92
|
+
@"private XCTest text synthesis failed: could not create the selection event"
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
((RunnerMsgSendSetInteger)objc_msgSend)(
|
|
96
|
+
selectionRecord, bridge.core.setTargetProcessIDSelector, targetProcessID
|
|
97
|
+
);
|
|
98
|
+
// XCUIKeyModifierCommand is public XCTest API (1 << 4). Keeping the value local
|
|
99
|
+
// avoids importing XCUIAutomation headers into the bridging helper. A text-input
|
|
100
|
+
// path only performs its first operation reliably, so selection and replacement
|
|
101
|
+
// use ordered records rather than one multi-operation path.
|
|
102
|
+
NSUInteger commandModifier = 1UL << 4;
|
|
103
|
+
((RunnerTextMsgSendTypeKey)objc_msgSend)(
|
|
104
|
+
selectionPath, bridge.typeKeySelector, @"a", commandModifier, 0.0
|
|
105
|
+
);
|
|
106
|
+
((RunnerMsgSendAddPath)objc_msgSend)(selectionRecord, bridge.core.addPathSelector, selectionPath);
|
|
107
|
+
NSError *selectionError = nil;
|
|
108
|
+
BOOL selected = ((RunnerMsgSendSynthesize)objc_msgSend)(
|
|
109
|
+
selectionRecord, bridge.core.synthesizeSelector, &selectionError
|
|
110
|
+
);
|
|
111
|
+
if (!selected) {
|
|
112
|
+
NSString *detail = selectionError.localizedDescription ?: @"synthesizeWithError returned false";
|
|
113
|
+
return RunnerTextEntryFailed(
|
|
114
|
+
[NSString stringWithFormat:@"private XCTest text selection failed: %@", detail]
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
id record = ((RunnerTextMsgSendInit)objc_msgSend)(
|
|
120
|
+
[bridge.core.recordClass alloc],
|
|
121
|
+
bridge.initRecordSelector,
|
|
122
|
+
replace ? @"agent-device-fill-text" : @"agent-device-type"
|
|
123
|
+
);
|
|
124
|
+
id path = ((RunnerTextMsgSendInitPath)objc_msgSend)([bridge.core.pathClass alloc], bridge.initPathSelector);
|
|
125
|
+
if (record == nil || path == nil) {
|
|
126
|
+
return RunnerTextEntryFailed(
|
|
127
|
+
@"private XCTest text synthesis failed: could not create the text event"
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
((RunnerMsgSendSetInteger)objc_msgSend)(record, bridge.core.setTargetProcessIDSelector, targetProcessID);
|
|
131
|
+
((RunnerTextMsgSendType)objc_msgSend)(path, bridge.typeTextSelector, text, 0.0, 60, YES);
|
|
132
|
+
((RunnerMsgSendAddPath)objc_msgSend)(record, bridge.core.addPathSelector, path);
|
|
133
|
+
|
|
134
|
+
NSError *error = nil;
|
|
135
|
+
BOOL ok = ((RunnerMsgSendSynthesize)objc_msgSend)(record, bridge.core.synthesizeSelector, &error);
|
|
136
|
+
if (!ok) {
|
|
137
|
+
NSString *detail = error.localizedDescription ?: @"synthesizeWithError returned false";
|
|
138
|
+
return RunnerTextEntryFailed(
|
|
139
|
+
[NSString stringWithFormat:@"private XCTest text synthesis failed: %@", detail]
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
return RunnerTextEntryResult(RunnerSynthesizedTextEntryStatusSucceeded, nil);
|
|
143
|
+
} @catch (NSException *exception) {
|
|
144
|
+
return RunnerTextEntryFailed(
|
|
145
|
+
RunnerFormatXCTestException(exception, @"private XCTest text synthesis failed")
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
static NSString * _Nullable RunnerResolveTextEventBridge(
|
|
151
|
+
id application,
|
|
152
|
+
BOOL replace,
|
|
153
|
+
RunnerTextEventBridge *bridge
|
|
154
|
+
) {
|
|
155
|
+
RunnerXCTestEventBridge core;
|
|
156
|
+
NSString *missing = RunnerResolveXCTestEventBridge(application, RunnerTextSynthesisSurface, &core);
|
|
157
|
+
if (missing != nil) return missing;
|
|
158
|
+
|
|
159
|
+
SEL initRecordSelector = NSSelectorFromString(@"initWithName:");
|
|
160
|
+
SEL initPathSelector = NSSelectorFromString(@"initForTextInput");
|
|
161
|
+
SEL typeTextSelector = NSSelectorFromString(@"typeText:atOffset:typingSpeed:shouldRedact:");
|
|
162
|
+
SEL typeKeySelector = NSSelectorFromString(@"typeKey:modifiers:atOffset:");
|
|
163
|
+
|
|
164
|
+
missing = RunnerRequireSelector(
|
|
165
|
+
core.recordClass, initRecordSelector, @"initWithName:", RunnerTextSynthesisSurface
|
|
166
|
+
);
|
|
167
|
+
if (missing != nil) return missing;
|
|
168
|
+
missing = RunnerRequireSelector(
|
|
169
|
+
core.pathClass, initPathSelector, @"initForTextInput", RunnerTextSynthesisSurface
|
|
170
|
+
);
|
|
171
|
+
if (missing != nil) return missing;
|
|
172
|
+
missing = RunnerRequireSelector(
|
|
173
|
+
core.pathClass,
|
|
174
|
+
typeTextSelector,
|
|
175
|
+
@"typeText:atOffset:typingSpeed:shouldRedact:",
|
|
176
|
+
RunnerTextSynthesisSurface
|
|
177
|
+
);
|
|
178
|
+
if (missing != nil) return missing;
|
|
179
|
+
if (replace) {
|
|
180
|
+
missing = RunnerRequireSelector(
|
|
181
|
+
core.pathClass, typeKeySelector, @"typeKey:modifiers:atOffset:", RunnerTextSynthesisSurface
|
|
182
|
+
);
|
|
183
|
+
if (missing != nil) return missing;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
*bridge = (RunnerTextEventBridge){
|
|
187
|
+
.core = core,
|
|
188
|
+
.initRecordSelector = initRecordSelector,
|
|
189
|
+
.initPathSelector = initPathSelector,
|
|
190
|
+
.typeTextSelector = typeTextSelector,
|
|
191
|
+
.typeKeySelector = typeKeySelector,
|
|
192
|
+
};
|
|
193
|
+
return nil;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
static RunnerSynthesizedTextEntryResult *RunnerTextEntryResult(
|
|
197
|
+
RunnerSynthesizedTextEntryStatus status,
|
|
198
|
+
NSString * _Nullable message
|
|
199
|
+
) {
|
|
200
|
+
RunnerSynthesizedTextEntryResult *result = [RunnerSynthesizedTextEntryResult new];
|
|
201
|
+
result.status = status;
|
|
202
|
+
result.message = message;
|
|
203
|
+
return result;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
static RunnerSynthesizedTextEntryResult *RunnerTextEntryUnavailable(NSString *message) {
|
|
207
|
+
return RunnerTextEntryResult(RunnerSynthesizedTextEntryStatusUnavailable, message);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
static RunnerSynthesizedTextEntryResult *RunnerTextEntryFailed(NSString *message) {
|
|
211
|
+
return RunnerTextEntryResult(RunnerSynthesizedTextEntryStatusFailed, message);
|
|
212
|
+
}
|
|
@@ -2,6 +2,34 @@ import XCTest
|
|
|
2
2
|
|
|
3
3
|
extension RunnerTests {
|
|
4
4
|
private static let privateAXSnapshotMaxNodes = 5_000
|
|
5
|
+
|
|
6
|
+
/// Upper bound on element-rooted follow-up snapshot requests per capture. A
|
|
7
|
+
/// depth-capped Bluesky-class tree resolves in 1-3 chained requests (~100-300ms
|
|
8
|
+
/// each); the bound exists so a pathological tree cannot stack requests past
|
|
9
|
+
/// the capture-plan deadline, which is also enforced per call.
|
|
10
|
+
private static let privateAXDeepExtensionCallLimit = 8
|
|
11
|
+
|
|
12
|
+
/// Upper bound on per-element custom-action reads per capture. Each is its own
|
|
13
|
+
/// AX round trip (~100ms on an idle simulator), so this caps the opt-in cost
|
|
14
|
+
/// at roughly a second on top of the capture; the capture-plan deadline stops
|
|
15
|
+
/// it earlier under load. A screenful of merged cards is well under this.
|
|
16
|
+
private static let privateAXCustomActionLimit = 12
|
|
17
|
+
|
|
18
|
+
/// A capture is depth-limited unless its frontier extension resolved every
|
|
19
|
+
/// capped node: frontiers left pending (budget/deadline) or missed (element
|
|
20
|
+
/// vanished, re-rooted request failed) mean subtrees are still absent, and
|
|
21
|
+
/// presenting such a capture as complete would hide exactly the content the
|
|
22
|
+
/// extension exists to recover. nil extension counts = extension never ran.
|
|
23
|
+
static func privateAXDepthLimited(
|
|
24
|
+
effectiveDepth: Int,
|
|
25
|
+
requestedDepth: Int,
|
|
26
|
+
pendingFrontiers: Int?,
|
|
27
|
+
missedFrontiers: Int?
|
|
28
|
+
) -> Bool {
|
|
29
|
+
guard effectiveDepth < requestedDepth else { return false }
|
|
30
|
+
guard let pendingFrontiers, let missedFrontiers else { return true }
|
|
31
|
+
return pendingFrontiers > 0 || missedFrontiers > 0
|
|
32
|
+
}
|
|
5
33
|
/// Deep React Native trees make the AX server reject bulk snapshot requests outright with
|
|
6
34
|
/// kAXErrorIllegalArgument once the requested depth crosses a tree-size-dependent limit
|
|
7
35
|
/// (observed between depth 56 and 64 on the Bluesky Home feed; the limit moves with live
|
|
@@ -10,6 +38,71 @@ extension RunnerTests {
|
|
|
10
38
|
/// apps where the AX surface is genuinely unavailable.
|
|
11
39
|
static let privateAXSnapshotDepthLadder = [56, 40, 24, 12]
|
|
12
40
|
|
|
41
|
+
/// Ladder rungs for one capture. A remembered accepted depth (recorded when a prior capture's
|
|
42
|
+
/// deep request was rejected) drops the rungs above it, so the known-rejected deep request is
|
|
43
|
+
/// not re-paid on every capture of the same screen class.
|
|
44
|
+
static func privateAXAttemptDepths(requestedDepth: Int, rememberedDepth: Int?) -> [Int] {
|
|
45
|
+
var depths = [requestedDepth]
|
|
46
|
+
depths.append(contentsOf: privateAXSnapshotDepthLadder.filter { $0 < requestedDepth })
|
|
47
|
+
guard let remembered = rememberedDepth, remembered < requestedDepth else { return depths }
|
|
48
|
+
return depths.filter { $0 <= remembered }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/// The bridge omits the key entirely when the capture did not ask for custom
|
|
52
|
+
/// actions, which is the difference between "nothing to disclose" and "read
|
|
53
|
+
/// none of them" — so a missing key must stay nil, never (0, 0).
|
|
54
|
+
static func privateAXCustomActionCoverage(_ raw: Any?) -> SnapshotCustomActionCoverage? {
|
|
55
|
+
guard let coverage = raw as? [String: Any],
|
|
56
|
+
let read = (coverage[RunnerAXSnapshotCustomActionsReadKey] as? NSNumber)?.intValue,
|
|
57
|
+
let candidates = (coverage[RunnerAXSnapshotCustomActionsCandidatesKey] as? NSNumber)?.intValue
|
|
58
|
+
else { return nil }
|
|
59
|
+
// read/candidates are the ratio and must both be present; the truncation
|
|
60
|
+
// count is additive, so an older bridge that omits it reads as zero rather
|
|
61
|
+
// than voiding the whole coverage.
|
|
62
|
+
let truncated =
|
|
63
|
+
(coverage[RunnerAXSnapshotCustomActionsTruncatedKey] as? NSNumber)?.intValue ?? 0
|
|
64
|
+
let blocked = (coverage[RunnerAXSnapshotCustomActionsBlockedKey] as? NSNumber)?.boolValue ?? false
|
|
65
|
+
return SnapshotCustomActionCoverage(
|
|
66
|
+
read: read, candidates: candidates, truncated: truncated, blocked: blocked)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
func rememberPrivateAXAcceptedDepth(bundleId: String?, processIdentifier: Int?, depth: Int) {
|
|
70
|
+
// No PID means no way to notice a relaunch later; record nothing rather than risk serving
|
|
71
|
+
// a stale shallow rung to a fresh process.
|
|
72
|
+
guard let processIdentifier else { return }
|
|
73
|
+
privateAXAcceptedDepthLock.lock()
|
|
74
|
+
privateAXAcceptedDepthBundleId = bundleId
|
|
75
|
+
privateAXAcceptedDepthProcessIdentifier = processIdentifier
|
|
76
|
+
privateAXAcceptedDepth = depth
|
|
77
|
+
privateAXAcceptedDepthUntil = Date().addingTimeInterval(snapshotXCTestChannelPenaltyDuration)
|
|
78
|
+
privateAXAcceptedDepthLock.unlock()
|
|
79
|
+
NSLog("AGENT_DEVICE_RUNNER_PRIVATE_AX_DEPTH_REMEMBERED depth=%ld bundle=%@", depth, bundleId ?? "")
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
func rememberedPrivateAXAcceptedDepth(bundleId: String?, processIdentifier: Int?) -> Int? {
|
|
83
|
+
privateAXAcceptedDepthLock.lock()
|
|
84
|
+
defer { privateAXAcceptedDepthLock.unlock() }
|
|
85
|
+
guard Date() < privateAXAcceptedDepthUntil else { return nil }
|
|
86
|
+
guard privateAXAcceptedDepthBundleId == bundleId else { return nil }
|
|
87
|
+
guard let processIdentifier, privateAXAcceptedDepthProcessIdentifier == processIdentifier else {
|
|
88
|
+
return nil
|
|
89
|
+
}
|
|
90
|
+
return privateAXAcceptedDepth
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
func clearPrivateAXAcceptedDepth(reason: String) {
|
|
94
|
+
privateAXAcceptedDepthLock.lock()
|
|
95
|
+
let hadMemory = privateAXAcceptedDepth != nil && Date() < privateAXAcceptedDepthUntil
|
|
96
|
+
privateAXAcceptedDepthBundleId = nil
|
|
97
|
+
privateAXAcceptedDepthProcessIdentifier = nil
|
|
98
|
+
privateAXAcceptedDepth = nil
|
|
99
|
+
privateAXAcceptedDepthUntil = .distantPast
|
|
100
|
+
privateAXAcceptedDepthLock.unlock()
|
|
101
|
+
if hadMemory {
|
|
102
|
+
NSLog("AGENT_DEVICE_RUNNER_PRIVATE_AX_DEPTH_MEMORY_CLEARED reason=%@", reason)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
13
106
|
func privateAXSnapshotCapture(
|
|
14
107
|
app: XCUIApplication,
|
|
15
108
|
options: SnapshotOptions,
|
|
@@ -17,9 +110,19 @@ extension RunnerTests {
|
|
|
17
110
|
) -> SnapshotBackendCapture? {
|
|
18
111
|
#if os(iOS) && targetEnvironment(simulator)
|
|
19
112
|
let requestedDepth = options.depth ?? 64
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
113
|
+
// An explicit --depth request is honored as asked: no accepted-depth
|
|
114
|
+
// memory, no frontier extension past it.
|
|
115
|
+
let exactDepthRequested = options.depth != nil
|
|
116
|
+
let rememberedDepth =
|
|
117
|
+
exactDepthRequested
|
|
118
|
+
? nil
|
|
119
|
+
: rememberedPrivateAXAcceptedDepth(
|
|
120
|
+
bundleId: currentBundleId,
|
|
121
|
+
processIdentifier: currentAppProcessIdentifier
|
|
122
|
+
)
|
|
123
|
+
let attemptDepths = Self.privateAXAttemptDepths(
|
|
124
|
+
requestedDepth: requestedDepth,
|
|
125
|
+
rememberedDepth: rememberedDepth
|
|
23
126
|
)
|
|
24
127
|
var response: [String: Any] = [:]
|
|
25
128
|
var effectiveDepth = requestedDepth
|
|
@@ -35,7 +138,10 @@ extension RunnerTests {
|
|
|
35
138
|
response = RunnerAXSnapshotBridge.snapshotTree(
|
|
36
139
|
for: app,
|
|
37
140
|
maxDepth: depth,
|
|
38
|
-
maxNodes: Self.privateAXSnapshotMaxNodes
|
|
141
|
+
maxNodes: Self.privateAXSnapshotMaxNodes,
|
|
142
|
+
deepExtensionCallLimit: exactDepthRequested ? 0 : Self.privateAXDeepExtensionCallLimit,
|
|
143
|
+
customActionLimit: options.customActions ? Self.privateAXCustomActionLimit : 0,
|
|
144
|
+
deadline: deadline
|
|
39
145
|
)
|
|
40
146
|
if response["ok"] as? Bool == true {
|
|
41
147
|
effectiveDepth = depth
|
|
@@ -52,6 +158,16 @@ extension RunnerTests {
|
|
|
52
158
|
NSLog("AGENT_DEVICE_RUNNER_PRIVATE_AX_SNAPSHOT_FAILED=%@", lastError)
|
|
53
159
|
return nil
|
|
54
160
|
}
|
|
161
|
+
// Only a capture that actually descended records memory: a first-rung success on a
|
|
162
|
+
// remembered depth deliberately does NOT refresh the TTL, so expiry re-probes the full
|
|
163
|
+
// requested depth once per window instead of capping this screen class forever.
|
|
164
|
+
if !exactDepthRequested, effectiveDepth != attemptDepths.first {
|
|
165
|
+
rememberPrivateAXAcceptedDepth(
|
|
166
|
+
bundleId: currentBundleId,
|
|
167
|
+
processIdentifier: currentAppProcessIdentifier,
|
|
168
|
+
depth: effectiveDepth
|
|
169
|
+
)
|
|
170
|
+
}
|
|
55
171
|
guard let root = response["root"] as? [String: Any] else {
|
|
56
172
|
NSLog("AGENT_DEVICE_RUNNER_PRIVATE_AX_SNAPSHOT_FAILED=missing root")
|
|
57
173
|
return nil
|
|
@@ -74,24 +190,46 @@ extension RunnerTests {
|
|
|
74
190
|
return nil
|
|
75
191
|
}
|
|
76
192
|
|
|
77
|
-
|
|
193
|
+
// A capture whose frontier extension resolved every capped node is
|
|
194
|
+
// complete despite the per-request depth cap — reporting it as
|
|
195
|
+
// depth-limited would send agents chasing deeper content that is not
|
|
196
|
+
// there. Pending or missed frontiers keep the depth-limited verdict.
|
|
197
|
+
let deepExtension = response[RunnerAXSnapshotDeepExtensionKey] as? [String: Any]
|
|
198
|
+
let depthLimited = Self.privateAXDepthLimited(
|
|
199
|
+
effectiveDepth: effectiveDepth,
|
|
200
|
+
requestedDepth: requestedDepth,
|
|
201
|
+
pendingFrontiers: deepExtension?[RunnerAXSnapshotDeepExtensionPendingKey] as? Int,
|
|
202
|
+
missedFrontiers: deepExtension?[RunnerAXSnapshotDeepExtensionMissedKey] as? Int
|
|
203
|
+
)
|
|
78
204
|
NSLog(
|
|
79
|
-
"AGENT_DEVICE_RUNNER_PRIVATE_AX_SNAPSHOT_USED nodes=%ld depth=%ld",
|
|
205
|
+
"AGENT_DEVICE_RUNNER_PRIVATE_AX_SNAPSHOT_USED nodes=%ld depth=%ld extended=%ld",
|
|
80
206
|
nodes.count,
|
|
81
|
-
effectiveDepth
|
|
207
|
+
effectiveDepth,
|
|
208
|
+
deepExtension?[RunnerAXSnapshotDeepExtensionNodesAddedKey] as? Int ?? 0
|
|
82
209
|
)
|
|
83
210
|
return SnapshotBackendCapture(
|
|
84
211
|
payload: DataPayload(nodes: nodes, truncated: (response["truncated"] as? Bool) == true),
|
|
85
|
-
effectiveDepth: depthLimited ? effectiveDepth : nil
|
|
212
|
+
effectiveDepth: depthLimited ? effectiveDepth : nil,
|
|
213
|
+
customActions: Self.privateAXCustomActionCoverage(
|
|
214
|
+
response[RunnerAXSnapshotCustomActionsKey]
|
|
215
|
+
)
|
|
86
216
|
)
|
|
87
217
|
#else
|
|
88
218
|
return nil
|
|
89
219
|
#endif
|
|
90
220
|
}
|
|
91
221
|
|
|
222
|
+
/// The viewport read is XCTest main-thread work — the exact channel the penalty marks as
|
|
223
|
+
/// grinding on this screen class. Under penalty it reliably burns its full timeout and
|
|
224
|
+
/// falls back anyway (~1s added to every private AX capture on the Bluesky bench feed),
|
|
225
|
+
/// so honor the penalty here the same way capture plans do.
|
|
226
|
+
func shouldReadPrivateAXViewportViaXCTest() -> Bool {
|
|
227
|
+
!hasAbandonedTreeCapture() && !isSnapshotXCTestChannelPenalized(bundleId: currentBundleId)
|
|
228
|
+
}
|
|
229
|
+
|
|
92
230
|
private func privateAXSnapshotViewport(app: XCUIApplication, rootFrame: CGRect) -> CGRect {
|
|
93
231
|
let fallback = rootFrame.isEmpty ? CGRect.infinite : rootFrame
|
|
94
|
-
guard
|
|
232
|
+
guard shouldReadPrivateAXViewportViaXCTest() else {
|
|
95
233
|
return fallback
|
|
96
234
|
}
|
|
97
235
|
do {
|
|
@@ -164,7 +302,8 @@ extension RunnerTests {
|
|
|
164
302
|
depth: depth,
|
|
165
303
|
parentIndex: parentIndex,
|
|
166
304
|
hiddenContentAbove: nil,
|
|
167
|
-
hiddenContentBelow: nil
|
|
305
|
+
hiddenContentBelow: nil,
|
|
306
|
+
actions: rawNode["actions"] as? [String]
|
|
168
307
|
)
|
|
169
308
|
)
|
|
170
309
|
} else {
|