Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion skills/agent-device/references/session-management.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Sessions isolate device context. A device can only be held by one session at a t

- Name sessions semantically.
- Close sessions when done.
- Use separate devices for parallel work.
- Use separate sessions for parallel work.

## Listing sessions

Expand Down
1 change: 1 addition & 0 deletions src/core/dispatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export async function dispatchCommand(
outPath?: string,
context?: {
appBundleId?: string;
activity?: string;
verbose?: boolean;
logPath?: string;
traceLogPath?: string;
Expand Down
24 changes: 23 additions & 1 deletion src/daemon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -843,7 +843,11 @@ async function handleRequest(req: DaemonRequest): Promise<DaemonResponse> {
if (!node) {
return { ok: false, error: { code: 'COMMAND_FAILED', message: 'find did not match any element' } };
}
const ref = `@${node.ref}`;
const resolvedNode =
action === 'click' || action === 'focus' || action === 'fill' || action === 'type'
? findNearestHittableAncestor(nodes, node) ?? node
: node;
const ref = `@${resolvedNode.ref}`;
const actionFlags = { ...(req.flags ?? {}), noRecord: true };
if (action === 'exists') {
if (session) {
Expand Down Expand Up @@ -1686,6 +1690,24 @@ function normalizeType(type: string): string {
return value;
}

function findNearestHittableAncestor(
nodes: SnapshotState['nodes'],
node: SnapshotState['nodes'][number],
): SnapshotState['nodes'][number] | null {
if (node.hittable) return node;
let current = node;
const visited = new Set<string>();
while (current.parentIndex !== undefined) {
if (visited.has(current.ref)) break;
visited.add(current.ref);
const parent = nodes[current.parentIndex];
if (!parent) break;
if (parent.hittable) return parent;
current = parent;
}
return null;
}

function readVersion(): string {
try {
const root = findProjectRoot();
Expand Down
12 changes: 7 additions & 5 deletions src/platforms/android/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -493,17 +493,19 @@ function parseUiHierarchy(
const scopedRoot = options.scope ? findScopeNode(tree, options.scope) : null;
const roots = scopedRoot ? [scopedRoot] : tree.children;

const walk = (node: AndroidNode, depth: number) => {
const walk = (node: AndroidNode, depth: number, parentIndex?: number) => {
if (nodes.length >= maxNodes) {
truncated = true;
return;
}
if (depth > maxDepth) return;

const include = options.raw ? true : shouldIncludeAndroidNode(node, options);
let currentIndex = parentIndex;
if (include) {
currentIndex = nodes.length;
nodes.push({
index: nodes.length,
index: currentIndex,
type: node.type ?? undefined,
label: node.label ?? undefined,
value: node.value ?? undefined,
Expand All @@ -512,17 +514,17 @@ function parseUiHierarchy(
enabled: node.enabled,
hittable: node.hittable,
depth,
parentIndex: node.parentIndex,
parentIndex,
});
}
for (const child of node.children) {
walk(child, depth + 1);
walk(child, depth + 1, currentIndex);
if (truncated) return;
}
};

for (const root of roots) {
walk(root, 0);
walk(root, 0, undefined);
if (truncated) break;
}

Expand Down
1 change: 1 addition & 0 deletions website/docs/docs/selectors.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ Tips:

- Use `find ... wait <timeoutMs>` to wait for UI to appear.
- Combine with scoped snapshots using `snapshot -s "<label>"` for speed.
- [Android] If a matched node is not hittable, agent-device will click/focus the nearest hittable ancestor.
Loading