* fix: strip pre-PNG garbage from Android screenshots on multi-display devices
On Android emulators with multiple displays (e.g. Galaxy Z Fold), `adb
exec-out screencap -p` writes a warning to stdout before the PNG data,
producing corrupt files. Locate the PNG signature in the buffer and
discard everything before it.
https://claude.ai/code/session_01Lfb3WKqXCSqYtCg8dbS8JW
* refactor: simplify PNG stripping and test scripts
- Remove unnecessary ternary; subarray(0) is a no-op
- Replace hex-encoded printf mock scripts with file-based payloads
https://claude.ai/code/session_01Lfb3WKqXCSqYtCg8dbS8JW
* fix: trim Android screenshot output to complete PNG payload
---------
Co-authored-by: Claude <noreply@anthropic.com>
Add strict key-set assertions to find click handler tests to prevent
accidental schema drift in the machine-consumed JSON response.
Closes#182
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add --shutdown option to close command for iOS simulator teardown (#172)
When --shutdown is passed, close ends the session then runs xcrun simctl
shutdown on the associated simulator. Shutdown result (success, exitCode,
stdout, stderr) is included in the JSON response data.
The option is silently ignored for non-simulator targets (physical iOS
devices, Android). A shutdownSimulator override is injected into
handleSessionCommands for testability.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs: document close --shutdown option for iOS simulator teardown
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: keep close --shutdown successful when simulator shutdown fails
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: add ensure-simulator command for scoped iOS device sets (#169)
Adds a new first-class `ensure-simulator` command that ensures an iOS
simulator exists (and optionally boots it) inside a scoped device set,
without requiring custom simctl scripting outside agent-device.
Usage:
agent-device ensure-simulator --device "iPhone 16" [--runtime <id>] [--boot] [--ios-simulator-device-set <path>]
JSON output includes udid, device, runtime, ios_simulator_device_set,
and whether the simulator was created vs reused.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs: document ensure-simulator in commands reference and skill
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: return deterministic matched-target metadata for find click responses (#170)
On success, build the response from the matched snapshot node rather than
passing through whatever the underlying click/press handler returns.
Response now includes: ref, locator, query, and x/y derived from the
matched element's rect — all stable across runs.
Also makes `dispatch` injectable in `handleFindCommands` (matching the
pattern in `handleInteractionCommands`) and adds tests that assert the
deterministic shape and verify that non-deterministic platform runner
data does not bleed into the response.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs: document find click response shape in selectors and skill references
Add response shape documentation for `find "<query>" click --json` to:
- website/docs/docs/selectors.md (new "Response shape (click)" section)
- skills/agent-device/SKILL.md (guardrail note)
- skills/agent-device/references/snapshot-refs.md (new "find click response" section)
Follows the fix in #178 that made the response deterministic.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: retry alert accept/dismiss on transient 'alert not found' errors
Adds a 2 s internal retry window for `alert accept` and `alert dismiss`
when the runner returns "alert not found" or "no alert". Errors from
other causes break the loop immediately and are re-thrown unchanged.
Also injects `runnerCommand` as an optional param to enable unit testing
without spawning a real runner.
Closes#171
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs: document alert accept/dismiss retry behaviour in skill and permissions reference
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: enrich DEVICE_NOT_FOUND error for empty scoped iOS simulator sets (#168)
When selectDevice finds no candidates and a simulatorSetPath context is
provided for iOS, throw a detailed error including the set path and a
suggested xcrun simctl --set ... create command to provision the first
simulator.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs: note enriched DEVICE_NOT_FOUND guidance for empty scoped iOS simulator sets
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Add device identity fields to open, appstate, and session_list responses
for iOS targets, enabling isolation verification in concurrent multi-session
runs without extra simctl calls.
Closes#173
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Extract resolveTargetDevice, series/jitter helpers, and push payload
parsing into dedicated modules to reduce dispatch.ts from ~706 to ~564 LOC.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: use snapshots for raw iOS tree traversal
Fixes#147
* refactor: unify snapshot hittable computation across modes
* refactor: account for cousin occlusion in hittable
* refactor: tighten snapshot occlusion heuristic
* fix: add daemon remote HTTP server mode with isolation controls
* feat: add tenant lease lifecycle and admission control
* refactor: simplify lease scope and rpc method routing
* fix: correct smoke expectations for close without session
* fix(ios): use XCTest runner for screenshots on physical devices
`xcrun devicectl device screenshot` was removed in Xcode 26.x, causing
`agent-device screenshot` to fail on physical iOS devices with exit code 64.
This fix routes physical device screenshots through the AgentDeviceRunner
XCTest runner (already used for snapshot/interaction commands) instead:
- Add `screenshot` command to the Swift runner's `CommandType` enum
- Implement the handler using `XCUIScreen.main.screenshot()`, writing a
timestamped PNG to the app's temp directory (returned as `tmp/<file>`)
- If `appBundleId` is provided, activate the target app before capturing
so the screenshot shows the app under test rather than the runner itself
- In `screenshotIos()`, pass `appBundleId` to the runner command then pull
the file back to the host via `xcrun devicectl device copy from` (same
mechanism used by the recording feature)
- Thread `appBundleId` through `Interactor.screenshot()` → `dispatch.ts`
→ `screenshotIos()` so the active session's app is always captured
- Extract `IOS_RUNNER_CONTAINER_BUNDLE_IDS` to `runner-client.ts` so it
can be shared between `apps.ts` and `record-trace.ts`
Fixes: screenshots failing with "Failed to capture iOS screenshot" on
devices using Xcode 26.x / devicectl 506.6+
* fix: clean up iOS screenshot fallback follow-ups
---------
Co-authored-by: Michał Pierzchała <thymikee@gmail.com>