v2.12.0
Physical iPhone screenshots
ios_screenshot reaches a USB-attached iPhone/iPad, not just the Simulator. Hardware-only work (camera, NFC, push, real notches) had no visual verification at all before this. Capture goes through pymobiledevice3 over usbmux and needs a mounted DeveloperDiskImage, but no tunnel and no root.
It is capture only, and the output says so rather than letting an agent retry a gesture that was never delivered: no pressable enrichment, and no tap/swipe/input_text, because iOS exposes no touch injection to a host below 17. list_devices lists physical devices in their own section, labelled screenshot-only. pymobiledevice3 is not bundled: missing simply means no physical devices are listed.
navigate can tell you the route names
navigate({routeTable: true}) with no destination is now a route listing rather than a failed navigation. It was the tool's only route-discovery affordance and required a destination to answer, which made it unreachable until you already knew what it would tell you. A rejected destination also carries the registered routes in its error now, so a wrong guess still hands back the right answers.
input_text verifies the write that actually landed
Three iOS failures shared one cause: the accessibility tree reshapes between the before and after reads, and verification aligned the two snapshots by index. Raising the keyboard drops the fields it covers out of the tree entirely, so every field read as changed and none was identified.
- The written field is now identified by content, which is order-independent: the only field that ends with the text just typed, when none did before.
replace: trueclears first whenever the prior text could not be read at all, not only when it was read as non-empty. An unresolvable field leftpreviousempty because nothing was read, so replace behaved as append and built up garbage across calls.- An append onto a field whose prior text was unreadable is verified by the tail that landed and no longer burns the retry, which cleared first and destroyed exactly the text the caller asked to append to.
Telemetry
execute_in_app no longer counts an exception thrown by the caller's own expression as a tool failure. The evaluation worked; the code threw.