Skip to content

Commit 6105c49

Browse files
committed
chore(gates): integrate merged proxy package declarations
2 parents 7d8cfab + d87fd77 commit 6105c49

72 files changed

Lines changed: 1891 additions & 739 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.fallowrc.json‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
"src/sdk/selectors.ts",
2121
"src/sdk/finders.ts",
2222
"src/ai-sdk/index.ts",
23+
"packages/proxy/src/index.ts",
24+
"packages/proxy/src/node.ts",
25+
"packages/proxy/tsdown.config.ts",
2326
"src/bin.ts",
2427
"src/client/companion-tunnel.ts",
2528
"src/daemon.ts",
@@ -290,8 +293,8 @@
290293
"exports": ["*"]
291294
},
292295
{
293-
"comment": "Published package surface (see package.json#exports); consumed by SDK users, not internally.",
294-
"file": "{src/sdk/*.ts,src/ai-sdk/index.ts}",
296+
"comment": "Published package surfaces (see each package.json#exports); consumers may be external or reach them through workspace package exports Fallow does not follow.",
297+
"file": "{src/sdk/*.ts,src/ai-sdk/index.ts,packages/proxy/src/index.ts,packages/proxy/src/node.ts}",
295298
"exports": ["*"]
296299
},
297300
{
@@ -301,7 +304,7 @@
301304
},
302305
{
303306
"comment": "Tool config default exports, loaded by the tool rather than imported.",
304-
"file": "{oxlint.config.ts,tsdown.config.ts,packages/provider-*/tsdown.config.ts,vitest.mutation.config.ts,website/rspress.config.ts}",
307+
"file": "{oxlint.config.ts,tsdown.config.ts,packages/provider-*/tsdown.config.ts,packages/proxy/tsdown.config.ts,vitest.mutation.config.ts,website/rspress.config.ts}",
305308
"exports": ["default"]
306309
},
307310
{

‎android/snapshot-helper/README.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ The XML node attributes intentionally mirror acquisition facts decoded by the ho
106106
metadata on window roots. The helper emits `drawing-order` on Android API 24+ and omits it on API
107107
23, where the platform API is unavailable. The host keeps that fact as private capture evidence;
108108
the daemon uses it to annotate covered actions without adding it to normalized snapshot nodes.
109+
On API 33+ an input method window root also carries `window-region-rect`, whether its touchable
110+
region is a single rectangle (`window-bounds` is that region's bounding box). It is omitted on other
111+
window types, on API 32 and lower, and when the region reads empty.
109112

110113
Each XML chunk is sent with:
111114

‎android/snapshot-helper/src/main/java/com/callstack/agentdevice/snapshothelper/AccessibilityTreeCapture.java‎

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import android.os.Build;
66
import android.view.accessibility.AccessibilityNodeInfo;
77
import android.view.accessibility.AccessibilityWindowInfo;
8+
import java.util.ArrayList;
89
import java.util.List;
910

1011
/** Captures and serializes the Android accessibility window tree. */
@@ -25,7 +26,10 @@ private static Result captureOnce(UiAutomation automation, int maxDepth, int max
2526
StringBuilder xml = new StringBuilder();
2627
xml.append("<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>");
2728
xml.append("<hierarchy rotation=\"0\">");
28-
int windowCount = appendInteractiveWindowRoots(xml, automation, maxDepth, maxNodes, stats);
29+
List<Integer> missingRootWindowTypes = new ArrayList<>();
30+
int windowCount =
31+
appendInteractiveWindowRoots(
32+
xml, automation, maxDepth, maxNodes, stats, missingRootWindowTypes);
2933
String captureMode = "interactive-windows";
3034
if (AccessibilityCaptureStabilizer.requiresActiveWindowFallback(
3135
windowCount, stats.activeWindowRootMissing)) {
@@ -61,7 +65,8 @@ private static Result captureOnce(UiAutomation automation, int maxDepth, int max
6165
captureMode,
6266
windowCount,
6367
stats.nodeCount,
64-
stats.truncated);
68+
stats.truncated,
69+
missingRootWindowTypes);
6570
}
6671

6772
private static void clearAccessibilityCache(UiAutomation automation) {
@@ -118,7 +123,8 @@ private static int appendInteractiveWindowRoots(
118123
UiAutomation automation,
119124
int maxDepth,
120125
int maxNodes,
121-
AccessibilityTreeXml.Stats stats) {
126+
AccessibilityTreeXml.Stats stats,
127+
List<Integer> missingRootWindowTypes) {
122128
List<AccessibilityWindowInfo> windows;
123129
try {
124130
windows = automation.getWindows();
@@ -140,6 +146,7 @@ private static int appendInteractiveWindowRoots(
140146
windowMetadata = AccessibilityTreeXml.readWindowMetadata(window, windowCount);
141147
root = window.getRoot();
142148
if (root == null) {
149+
missingRootWindowTypes.add(windowMetadata.type);
143150
stats.activeWindowRootMissing |= activeWindow;
144151
stats.focusedNonActiveWindowRootMissing |= focusedNonActiveWindow;
145152
if (activeWindow) {
@@ -165,6 +172,9 @@ private static int appendInteractiveWindowRoots(
165172
// Accessibility windows can disappear while traversing; keep the rest of the snapshot.
166173
stats.activeWindowRootMissing |= activeWindow;
167174
stats.focusedNonActiveWindowRootMissing |= focusedNonActiveWindow;
175+
if (windowMetadata != null) {
176+
missingRootWindowTypes.add(windowMetadata.type);
177+
}
168178
if (activeWindow && windowMetadata != null) {
169179
stats.activeWindowMetadata = windowMetadata;
170180
}
@@ -202,6 +212,9 @@ static final class Result implements AccessibilityCaptureStabilizer.Capture {
202212
final int windowCount;
203213
final int nodeCount;
204214
final boolean truncated;
215+
// AccessibilityWindowInfo types of listed windows whose root read null or threw, so the host
216+
// can tell a window it could not read (an input method, say) from one that was not on screen.
217+
final List<Integer> missingRootWindowTypes;
205218

206219
Result(
207220
String xml,
@@ -210,14 +223,16 @@ static final class Result implements AccessibilityCaptureStabilizer.Capture {
210223
String captureMode,
211224
int windowCount,
212225
int nodeCount,
213-
boolean truncated) {
226+
boolean truncated,
227+
List<Integer> missingRootWindowTypes) {
214228
this.xml = xml;
215229
this.rootPresent = rootPresent;
216230
this.foregroundWindowRootsPresent = foregroundWindowRootsPresent;
217231
this.captureMode = captureMode;
218232
this.windowCount = windowCount;
219233
this.nodeCount = nodeCount;
220234
this.truncated = truncated;
235+
this.missingRootWindowTypes = missingRootWindowTypes;
221236
}
222237

223238
@Override

‎android/snapshot-helper/src/main/java/com/callstack/agentdevice/snapshothelper/AccessibilityTreeXml.java‎

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.callstack.agentdevice.snapshothelper;
22

33
import android.graphics.Rect;
4+
import android.graphics.Region;
45
import android.os.Build;
56
import android.view.accessibility.AccessibilityNodeInfo;
67
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
@@ -133,8 +134,29 @@ static void appendNode(
133134
static WindowMetadata readWindowMetadata(AccessibilityWindowInfo window, int index) {
134135
Rect bounds = new Rect();
135136
window.getBoundsInScreen(bounds);
137+
int type = window.getType();
136138
return new WindowMetadata(
137-
index, window.getType(), window.getLayer(), window.isActive(), window.isFocused(), bounds);
139+
index,
140+
type,
141+
window.getLayer(),
142+
window.isActive(),
143+
window.isFocused(),
144+
bounds,
145+
readInputMethodRegionRect(window, type));
146+
}
147+
148+
// The window bounds are the box around the window's touchable region. A floating keyboard's region
149+
// is several rects (the panel and the gesture strip), so the box claims app content between them;
150+
// the host must not read such a box as the keyboard band. getRegionInScreen arrived in API 33, so
151+
// older releases, and a region that reads empty, report nothing and keep the bounds-only answer.
152+
private static Boolean readInputMethodRegionRect(AccessibilityWindowInfo window, int type) {
153+
if (type != AccessibilityWindowInfo.TYPE_INPUT_METHOD
154+
|| Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
155+
return null;
156+
}
157+
Region region = new Region();
158+
window.getRegionInScreen(region);
159+
return region.isEmpty() ? null : region.isRect();
138160
}
139161

140162
private static void appendNonEmptyAttribute(
@@ -194,6 +216,9 @@ private static void appendWindowMetadata(StringBuilder xml, WindowMetadata metad
194216
metadata.bounds.top,
195217
metadata.bounds.right,
196218
metadata.bounds.bottom));
219+
if (metadata.regionRect != null) {
220+
appendAttribute(xml, "window-region-rect", Boolean.toString(metadata.regionRect));
221+
}
197222
}
198223

199224
private static void appendAttribute(StringBuilder xml, String name, CharSequence value) {
@@ -279,18 +304,28 @@ static final class WindowMetadata {
279304
final boolean active;
280305
final boolean focused;
281306
final Rect bounds;
307+
// Null unless an input method window's touchable region was read (API 33+).
308+
final Boolean regionRect;
282309

283-
WindowMetadata(int index, int type, int layer, boolean active, boolean focused, Rect bounds) {
310+
WindowMetadata(
311+
int index,
312+
int type,
313+
int layer,
314+
boolean active,
315+
boolean focused,
316+
Rect bounds,
317+
Boolean regionRect) {
284318
this.index = index;
285319
this.type = type;
286320
this.layer = layer;
287321
this.active = active;
288322
this.focused = focused;
289323
this.bounds = bounds;
324+
this.regionRect = regionRect;
290325
}
291326

292327
WindowMetadata withIndex(int nextIndex) {
293-
return new WindowMetadata(nextIndex, type, layer, active, focused, bounds);
328+
return new WindowMetadata(nextIndex, type, layer, active, focused, bounds, regionRect);
294329
}
295330
}
296331
}

‎android/snapshot-helper/src/main/java/com/callstack/agentdevice/snapshothelper/SnapshotInstrumentation.java‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import android.app.UiAutomation;
55
import android.content.res.Resources;
66
import android.os.Bundle;
7+
import android.text.TextUtils;
78
import android.util.Base64;
89
import android.util.DisplayMetrics;
910
import java.io.BufferedReader;
@@ -125,6 +126,8 @@ private static void putCaptureMetadata(
125126
result.putString("rootPresent", Boolean.toString(capture.rootPresent));
126127
result.putString("captureMode", capture.captureMode);
127128
result.putString("windowCount", Integer.toString(capture.windowCount));
129+
result.putString(
130+
"missingRootWindowTypes", TextUtils.join(",", capture.missingRootWindowTypes));
128131
result.putString("nodeCount", Integer.toString(capture.nodeCount));
129132
result.putString("truncated", Boolean.toString(capture.truncated));
130133
result.putString("elapsedMs", Long.toString(elapsedMs));

‎package.json‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@
179179
"check:unit": "pnpm test:unit && pnpm check:tmpdir-leaks && pnpm test:smoke",
180180
"check": "pnpm check:tooling && pnpm check:fallow && pnpm check:unit",
181181
"prepack": "npm_config_dry_run=false pnpm release:prepare",
182-
"typecheck": "tsc -b packages/xml packages/kernel packages/contracts packages/device-selection packages/host-kit packages/capture-kit packages/managed-allocation packages/provision-kit packages/platform-apple packages/platform-android packages/platform-harmonyos packages/platform-vega packages/platform-linux packages/platform-web packages/ad-script packages/selectors packages/command-registry packages/session-journal packages/ad-replay packages/maestro packages/replay-port packages/replay-test packages/provider-webdriver packages/provider-limrun packages/provider-testmu && tsc -p tsconfig.json && tsc -p examples/sdk/tsconfig.json",
182+
"typecheck": "tsc -b packages/xml packages/kernel packages/contracts packages/device-selection packages/host-kit packages/capture-kit packages/managed-allocation packages/provision-kit packages/platform-apple packages/platform-android packages/platform-harmonyos packages/platform-vega packages/platform-linux packages/platform-web packages/ad-script packages/selectors packages/command-registry packages/session-journal packages/ad-replay packages/maestro packages/replay-port packages/replay-test packages/provider-webdriver packages/provider-limrun packages/proxy packages/provider-testmu && tsc -p tsconfig.json && tsc -p examples/sdk/tsconfig.json",
183183
"test-app:install": "pnpm install --dir examples/test-app",
184184
"test-app:start": "pnpm --dir examples/test-app start",
185185
"test-app:ios": "pnpm --dir examples/test-app ios",
@@ -308,14 +308,15 @@
308308
"@agent-device/provider-limrun": "workspace:*",
309309
"@agent-device/provider-webdriver": "workspace:*",
310310
"@agent-device/provision-kit": "workspace:*",
311+
"@agent-device/proxy": "workspace:*",
311312
"@agent-device/replay-port": "workspace:*",
312313
"@agent-device/replay-test": "workspace:*",
313314
"@agent-device/selectors": "workspace:*",
314315
"@agent-device/session-journal": "workspace:*",
315316
"@agent-device/xml": "workspace:*",
316317
"@arethetypeswrong/cli": "^0.18.5",
317318
"@chenglou/freerange": "^0.0.4",
318-
"@limrun/api": "^0.49.3",
319+
"@limrun/api": "^0.59.0",
319320
"@nkzw/eslint-plugin": "^2.0.0",
320321
"@nkzw/oxlint-config": "^1.3.0",
321322
"@stryker-mutator/core": "10.0.0",

‎packages/capture-kit/src/ios-snapshot-engine/transitions.ts‎

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { RawSnapshotNode, Rect } from '@agent-device/kernel/snapshot';
2-
import { rectContains } from '@agent-device/kernel/rect';
2+
import { rectContains, unionRects } from '@agent-device/kernel/rect';
33
import { extractNodeText, normalizeType } from '@agent-device/contracts/snapshot';
44
import { collectChildrenByParent, mergeReplacement, type SnapshotTreeRuleContext } from './tree.ts';
55
import { collectIosReplacedActionShelves } from './action-shelf.ts';
@@ -116,11 +116,3 @@ function formsNavigationTitleAffordance(image: Rect, field: Rect, title: Rect, b
116116
function verticallyOverlaps(left: Rect, right: Rect): boolean {
117117
return Math.max(left.y, right.y) <= Math.min(left.y + left.height, right.y + right.height);
118118
}
119-
120-
function unionRects(rects: Rect[]): Rect {
121-
const x = Math.min(...rects.map((rect) => rect.x));
122-
const y = Math.min(...rects.map((rect) => rect.y));
123-
const right = Math.max(...rects.map((rect) => rect.x + rect.width));
124-
const bottom = Math.max(...rects.map((rect) => rect.y + rect.height));
125-
return { x, y, width: right - x, height: bottom - y };
126-
}

‎packages/capture-kit/src/screenshot-overlay-android.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { Rect, SnapshotNode, SnapshotState } from '@agent-device/kernel/snapshot';
22
import { isViewportRootNode, normalizeType } from '@agent-device/contracts/snapshot';
3-
import { hasPositiveRect, rectArea, rectContains, unionRects } from './screenshot-overlay-rects.ts';
3+
import { unionRects } from '@agent-device/kernel/rect';
4+
import { hasPositiveRect, rectArea, rectContains } from './screenshot-overlay-rects.ts';
45

56
/**
67
* Android overlay policy (#1983): which Android nodes earn an overlay ref, and what rectangle

‎packages/capture-kit/src/screenshot-overlay-rects.ts‎

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,3 @@ export function clamp(value: number, min: number, max: number): number {
1515
export function rectArea(rect: Rect): number {
1616
return rect.width * rect.height;
1717
}
18-
19-
export function unionRects(rects: Rect[]): Rect {
20-
const firstRect = rects[0];
21-
if (firstRect === undefined) {
22-
throw new Error('unionRects requires at least one rect');
23-
}
24-
let minX = firstRect.x;
25-
let minY = firstRect.y;
26-
let maxRight = firstRect.x + firstRect.width;
27-
let maxBottom = firstRect.y + firstRect.height;
28-
for (const rect of rects.slice(1)) {
29-
minX = Math.min(minX, rect.x);
30-
minY = Math.min(minY, rect.y);
31-
maxRight = Math.max(maxRight, rect.x + rect.width);
32-
maxBottom = Math.max(maxBottom, rect.y + rect.height);
33-
}
34-
return {
35-
x: minX,
36-
y: minY,
37-
width: maxRight - minX,
38-
height: maxBottom - minY,
39-
};
40-
}

‎packages/contracts/src/snapshot-types.ts‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ export type AndroidSnapshotBackendMetadata = {
7373
captureMode?: string;
7474
systemSurfaceOnly?: boolean;
7575
windowCount?: number;
76+
/**
77+
* `AccessibilityWindowInfo` types of the listed windows the helper could not serialize because
78+
* reading their root returned null or threw (`2` is an input method window); `windowCount` counts
79+
* only the roots it serialized. Empty when every listed window was read. Absent on an older helper.
80+
*/
81+
missingRootWindowTypes?: number[];
7682
nodeCount?: number;
7783
helperTruncated?: boolean;
7884
elapsedMs?: number;

0 commit comments

Comments
 (0)