Releases: callstack/agent-device
Release list
v0.21.0
Highlights
v0.21 makes device automation faster and easier to control.
- iOS Simulator apps open faster. Cold opens took 27% to 40% less time across six benchmark screens. Snapshots are faster too, and cold-start commands respect the timeout you set.
- Take over a remote device without ending the agent’s session. Pause its actions, handle a step yourself, then let it continue.
- Agents get clearer results. Absence checks distinguish a missing element from a failed capture. Android snapshots expose field details such as whether an input is editable or showing placeholder text.
- Testing takes fewer steps. Crop a screenshot to a selected element, clear a field with
fill <target> "", or reset the whole Simulator keychain for a fresh-login test. Maestro gains more selectors andclearStatesupport. - Remote runs are easier to debug. Cancellation reaches the daemon, failure logs are accessible through the proxy, and incompatible versions fail earlier.
- Installation is smaller. Dependency bundling reduced the measured install size from 9.89 MB to 4.44 MB.
Under the hood, commands now use platform-owned runtimes and shared snapshot handling, with less duplicated logic.
Before upgrading (breaking changes): batch steps now use structured input; perf requires a specific operation such as frames or memory; the deprecated SDK rotate wrapper has been removed.
What's Changed
- refactor: migrate viewport to request runtime by @thymikee in #1864
- refactor(daemon): admit-before-bind as an admitted-plan token; retire the R32 syntax policy by @thymikee in #1841
- refactor: tighten viewport runtime facts by @thymikee in #1873
- fix(layering): list the whole zone when R10's type-cycle ceiling is exceeded by @thymikee in #1852
- fix(daemon): cap events.ndjson with cursor-safe rotation by @thymikee in #1867
- fix(layering): resolve relative imports inside workspace packages referencing #1781 by @thymikee in #1872
- fix(utils): throw AppError from app-log-files and verified-file guards by @thymikee in #1853
- test: refuse foreign-pid signals from unit-test workers (Coverage fork death, #1824) by @thymikee in #1854
- test: classify the runner XCTests — pure decisions to a macOS host lane, simulator semantics gated os(iOS) (#1781 A7) by @thymikee in #1861
- docs: keep size review in CI and local feedback fast by @thymikee in #1842
- fix: stabilize global flag declaration type by @kkkhs in #1879
- fix(daemon,kernel): device-selection safety — identity conflicts and ambiguity fail instead of retargeting by @thymikee in #1880
- refactor: migrate screenshot to request-bound runtime by @thymikee in #1878
- refactor(daemon): one capture-input builder and one admit-then-bind step by @thymikee in #1876
- ci: give check:affected real Apple ownership rules and route ios.yml on them (#1781 A9-2) by @thymikee in #1857
- fix(android): warn when a permission revoke kills the session app by @thymikee in #1856
- fix: surface remote install network causes by @thymikee in #1863
- refactor(android): raw is the acquired tree; one presentation for dialog recovery; residues declared (#1832 C3) by @thymikee in #1865
- test(fuzz): structured CLI/Maestro generators that reach command validation and assert error codes (#1781 B2) by @thymikee in #1866
- feat(snapshot): move scope into presentation by @thymikee in #1855
- fix(maestro): refresh targets after transient selector misses by @thymikee in #1886
- test: make hard-coded caps overridable behind seams (#1781 B5) by @thymikee in #1858
- test: catch daemon lifecycle and durable-state leaks (#1781 B1) by @thymikee in #1859
- ci: bound the Linux apt install so a stalled mirror fails fast by @thymikee in #1887
- fix(maestro): restore conformance invariants by @thymikee in #1889
- refactor: reuse kernel uniqueStrings instead of local copies by @thymikee in #1892
- test(bench): falsification fixtures for oracles + typed runner outcomes by @thymikee in #1893
- docs: simplify AI SDK integration guide by @thymikee in #1891
- refactor(maestro): remove obsolete presentation mapping by @thymikee in #1896
- fix(maestro): make iOS presentation correspondence authoritative by @thymikee in #1899
- fix(android): warn when recording readiness inspection fails open by @thymikee in #1898
- fix: calibrate iOS scroll execution by @thymikee in #1905
- refactor: migrate get to the request-bound device runtime by @thymikee in #1877
- refactor: migrate wait to request-bound runtime by @thymikee in #1875
- test: add web platform command coverage manifest by @thymikee in #1902
- test: cover host runner tv remote and recording paths by @thymikee in #1903
- perf(check-affected): stop running coverage locally, CI stays authoritative by @thymikee in #1908
- docs(agents): restore and enforce progressive disclosure by @thymikee in #1888
- fix(maestro): align label metadata ownership by @thymikee in #1909
- feat(maestro): add recursive tree selectors by @thymikee in #1910
- feat(maestro): add positional selectors by @thymikee in #1911
- test: add filesystem boundary fault matrix by @thymikee in #1904
- test(gesture): assert pan duration in the iOS gesture-lab replay by @thymikee in #1901
- fix: make iOS scroll release controlled by @thymikee in #1906
- docs: capture gesture verification lessons by @thymikee in #1913
- feat(ios): split raw and regular snapshot projections behind one capture hint (#1797) by @thymikee in #1926
- perf: reduce cold iOS runner startup latency by @thymikee in #1927
- test: add tvOS platform command coverage manifest by @thymikee in #1919
- test: add Linux platform command coverage manifest by @thymikee in #1921
- docs: record visionOS/watchOS support decision by @thymikee in #1918
- refactor(web): drop duplicate viewport facts spread in webRuntimeFacts by @thymikee in #1928
- fix: preserve Maestro clickable-first ordering by @thymikee in #1917
- test: complete boundary fault matrix by @thymikee in #1920
- feat(ios): move the regular-projection clip fold into snapshot presentation (#1797) by @thymikee in #1929
- feat(ios): publish effective snapshot geometry by @thymikee in #1931
- refactor: migrate focus to the request-bound device runtime by @thymikee in #1925
- chore: refresh root development dependencies by @thymikee in #1923
- test: add macOS platform command coverage manifest by @thymikee in #1922
- fix(ios): separate snapshot actionability from occlusion by @thymikee in #1933
- ci: attribute native package size and trim Apple runner by @thymikee in #1934
- feat(ios): add snapshot backend conformance by @thymikee in #1930
- fix(ios): report an unobserved text commit instead of a partial success by @thymikee in #1924
- refactor(apple): drop dead runner code and collapse duplicated helpers by @thymikee in #1936
- fix(ios): make snapshot presentation construction private by @thymikee in #1937
- refactor: migrate type to the request-bound device runtime by @thymikee in https://github.com/callsta...
v0.20.10
Highlights
- AI SDK and MCP integrations: A first-party
agent-device/ai-sdktool set makes agent-device available to AI SDK applications, while compact MCP instructions and an MCP-only help tool reduce setup and discovery overhead. #1804, #1839 - More accurate iOS snapshots: Private-AX scroll visibility is unified, offscreen subtree leaks and clamped descendants are removed, snapshot presentation becomes an explicit seam, and transition snapshots derive from the visible capture. #1798, #1807, #1811, #1845, #1831, #1848, #1850, #1851
- Better Android visibility and gestures: Covering siblings hide only the content they actually cover, snapshot scope resolves once with the API 23 evidence gap disclosed, and saturated scroll gestures stay below the status bar. #1808, #1846, #1820
- Web hover automation: The new
hovercommand supports hover-gated menus, tooltips, and other web UI that cannot be exercised with click or touch alone. #1786, #1817 - Safer sessions, devices, and cloud runs: Invalid path-like session names are rejected, sessionless mutations enforce device claims, remote diagnostics reach the caller, and WebDriver cloud creation has a bounded budget without leaking billed sessions. #1815, #1809, #1814, #1782
- Lean, more trustworthy CI: Native lanes skip docs-only changes, parked suites are dispatch-only, nightly iOS runs the full XCTest suite, transient comment failures no longer fail bundle-size checks, and redundant or cascading jobs are removed. #1791, #1794, #1789, #1795, #1787, #1822
Release breakdown
- 46 commits
- 44 merged pull requests
- 1 contributor
What's Changed
- ci: drop duplicate nightly-macos replay job (#1781 A2) by @thymikee in #1787
- fix: unify private AX scroll visibility by @thymikee in #1798
- chore: restore unreleased version by @thymikee in #1805
- fix: prevent private AX subtree leaks at source by @thymikee in #1807
- ci: skip device lanes for root-level docs-only changes (#1781 A9) by @thymikee in #1791
- test(android): restore full-tier lifecycle and observability scenarios (#1781 A1) by @thymikee in #1793
- ci: move parked device replay suites to a dispatch-only workflow (#1781 A1) by @thymikee in #1794
- test: assert the specific error code instead of any failure (#1781 B4) by @thymikee in #1790
- chore(format): ignore .claude/** in oxfmt so a main-checkout format can't sweep worktrees by @thymikee in #1803
- fix: hide clamped descendants of offscreen iOS rows by @thymikee in #1811
- feat(web): add hover command for hover-gated UI (#1783) by @thymikee in #1786
- ci: keep Bundle Size job green on transient GitHub comment failures by @thymikee in #1795
- fix(daemon): reject '.', '..' and empty session names so session artifacts stay under sessions/ by @thymikee in #1815
- feat(ai-sdk): add agent-device/ai-sdk tool set and document the MCP zero-code path by @thymikee in #1804
- fix: reject selector-shaped wait arguments instead of reading them as text by @thymikee in #1813
- ci(ios): run the full XCTest suite nightly and check the PR test list (#1781 A7) by @thymikee in #1789
- fix(ios-runner): reject CGRect.infinite in navigation frame guards by @thymikee in #1816
- fix: declare frameworkTier on the hover descriptor by @thymikee in #1817
- fix: enforce device claims for sessionless device mutations (#1799) by @thymikee in #1809
- fix(scroll): keep saturated scroll gestures out of the status bar; gate Android replays from android/emulator (#1781 A1) by @thymikee in #1820
- fix(android): let a covering sibling hide only what its content covers by @thymikee in #1808
- fix: report real claim results from daemon stop by @thymikee in #1818
- fix: read replay scripts on the caller and ship them with the request by @thymikee in #1810
- chore(test): remove the contention retry (0 fires since 07-29) and shrink the subprocess-stub project to its 3 real spawners (#1781 A4) by @thymikee in #1827
- chore(layering): document R12/R18/R19, retire R8, make R9 shrink mandatory (#1781 A6) by @thymikee in #1825
- fix: serve remote daemon request diagnostics to the caller by @thymikee in #1814
- fix(webdriver): give cloud session creation its own budget and stop leaking billed sessions by @thymikee in #1782
- refactor: migrate snapshot to device runtime by @thymikee in #1779
- refactor: split the test-suite command out of the replay handler by @thymikee in #1826
- fix(layering): keep the snapshot interactor seam out of the type cycle by @thymikee in #1838
- refactor(ios): establish snapshot presentation seam by @thymikee in #1845
- ci: park perf-nightly to dispatch and stop the coverage-gate cascade double-red (#1781 A3, A5) by @thymikee in #1822
- chore(mutation): shrink to report-only — drop the ratchet, baseline and graduation (#1457, #1781) by @thymikee in #1828
- test: prune abandoned test-run tmp directories at run setup by @thymikee in #1834
- feat(mcp): compact server instructions in both eras + MCP-only help tool by @thymikee in #1839
- fix: derive iOS transition snapshots from visible presentation by @thymikee in #1831
- refactor(ios): route snapshot backends through presentation by @thymikee in #1848
- docs(agents): ADR 0019 unit checklist, owning-seam mock rule, worktree and rebase guidance by @thymikee in #1836
- fix(android): resolve snapshot scope once and disclose the API 23 occlusion-scan gap (#1832 C1/C2) by @thymikee in #1846
- feat(ios): unify snapshot eligibility by @thymikee in #1850
- refactor: migrate diff to request-bound runtime by @thymikee in #1847
- test: pin test files over 1,000 lines at their exact length so they can only shrink by @thymikee in #1843
- test(ratchet): pin snapshot-handler.test.ts at main's 2654 lines by @thymikee in #1860
- fix: derive transition backend from capture by @thymikee in #1851
Contributors
- @thymikee — 44 pull requests
Full Changelog: v0.20.9...v0.20.10
v0.20.9
Highlights
- More resilient snapshots across Android and iOS: Android no longer lets empty focusable overlays hide app content, sparse captures now include a fallback screenshot, compact action labels survive presentation, and private-AX settling/custom-action reads are more stable. #1737, #1764, #1773, #1749, #1784
- Safer device ownership and command execution: Local device claims recover stale owners, while boot, apps, app state, shutdown, installation, and application lifecycle move onto request-bound runtimes for consistent cancellation, admission, and provider behavior. #1735, #1747, #1755, #1756, #1757, #1758, #1759
- Better failure evidence: Batch recovery keeps typed signals, URL credential errors are preserved, request-timeout cleanup is platform-aware, recording evidence survives faults, and iOS final-probe diagnostics remain available. #1761, #1762, #1751, #1734, #1776
- Physical-device and recording fixes: Physical iOS screenshots use the current
devicectlsyntax, iOS runner releases install their dependencies, and macOS recording finalization is validated before success is reported. #1769, #1785, #1732 - Stronger release and architecture gates: CLI help examples are checked against the schema, the built daemon is fingerprinted from its real import graph, previously missing CI checks now run, and runtime cutovers are enforced structurally. #1766, #1771, #1770, #1745, #1753
Release breakdown
- 40 commits
- 39 merged pull requests
- 3 contributors
What's Changed
- refactor: tighten capture ownership boundaries by @thymikee in #1736
- refactor: narrow perf API to actionable evidence by @thymikee in #1731
- feat: enforce local device claims and reconcile stale owners by @thymikee in #1735
- docs: amend ADR 0019 with broader-migration governance by @thymikee in #1738
- refactor(daemon): absorb androidAdbExecutor DI slot into the generic provider scope by @thymikee in #1742
- refactor(contracts): consolidate per-domain defineUse wrappers into one neutral defineUse by @thymikee in #1741
- fix(android): stop empty focusable overlays from hiding app content by @thymikee in #1737
- refactor(daemon): split touch interaction orchestration into semantic modules by @thymikee in #1748
- fix(layering): stop double-reporting contracts-authority violations by @thymikee in #1746
- refactor: route selector-resolution structural stages into typed policy by @thymikee in #1744
- fix(batch): preserve typed error recovery signals by @cosin2077 in #1761
- fix(install-source): preserve URL credential validation error by @cosin2077 in #1762
- refactor(registry): exhaustive platformExecution discriminator (ADR 0019 §6) by @thymikee in #1740
- chore: drive unused production exports to zero by @thymikee in #1743
- refactor(layering): give each colliding rule id its own number by @thymikee in #1750
- refactor(layering): one parametrized runtime-command-cutover gate (ADR 0019 §8) by @thymikee in #1745
- refactor: route boot through readiness runtime by @thymikee in #1747
- refactor: route apps through request runtime by @thymikee in #1756
- fix(ios): use current devicectl capture-screenshot syntax for physical devices by @thymikee in #1769
- test(cli-help): gate every help example against the CLI schema by @thymikee in #1766
- fix: preserve recording evidence across fault boundaries by @thymikee in #1734
- fix(ci): run the three checks no workflow was running by @thymikee in #1770
- fix: validate macOS recording finalization by @thymikee in #1732
- fix(ios): make custom-action reads atomically single-flight by @thymikee in #1749
- fix: harden iOS alert smoke scenario by @thymikee in #1767
- refactor(snapshot): clean snapshot ownership by @thymikee in #1754
- refactor(ci): make gate ownership structural (#1429) by @thymikee in #1753
- refactor: route appstate through platform runtime by @thymikee in #1755
- fix(daemon): make request-timeout cleanup and hints platform-aware by @thymikee in #1751
- fix: fingerprint the built daemon's real import graph (#1545) by @thymikee in #1771
- refactor: route shutdown through device runtime by @thymikee in #1757
- refactor: route install commands through platform runtime by @thymikee in #1758
- refactor: route application lifecycle through runtime facts by @thymikee in #1759
- fix: preserve compact snapshot action labels by @thymikee in #1773
- feat(snapshot): attach a fallback screenshot to sparse captures by @thymikee in #1764
- fix(ios): preserve final-probe xcodebuild diagnostics by @emmanuelchucks in #1776
- docs: reposition README around mobile app automation for AI agents by @thymikee in #1780
- fix: install dependencies for iOS runner releases by @thymikee in #1785
- fix: stabilize private AX settle snapshots by @thymikee in #1784
Contributors
- @thymikee — 36 pull requests
- @cosin2077 — 2 pull requests (first contribution)
- @emmanuelchucks — 1 pull request (first contribution)
Full Changelog: v0.20.8...v0.20.9
v0.20.7
Highlights
-
HarmonyOS support: Automate HarmonyOS devices through HDC, including discovery, snapshots, app lifecycle, HAP deployment, interactions, gestures, keyboard actions, logs, performance sampling, screen recording, and diagnostics. [#1683](#1683)
-
Start working in fewer turns:
open --foregroundcan attach to the active app on an unambiguous iOS simulator and return the first interactive snapshot immediately. [#1670](#1670), [#1671](#1671), [#1693](#1693) -
Faster CLI startup: Common warm commands are up to 54% faster by keeping HTTP and ICU initialization off the local CLI startup path.
appstateimproved from roughly 146 ms to 68 ms in benchmarks. [#1681](#1681) -
Better iOS automation:
snapshot --actionsexposes custom accessibility actions on merged elements, such as React Native feed cards. Hidden-keyboard text entry now waits for the complete text commit before reporting success. [#1665](#1665), [#1676](#1676) -
More reliable Android interactions: Snapshot recovery now retires unhealthy helper sessions, masked fills return target-bound verification evidence, and free-text shell arguments are safely quoted. [#1708](#1708), [#1645](#1645)
-
Stronger cloud and remote-device support: BrowserStack and AWS Device Farm iOS sessions can capture snapshots reliably, while cloud fills wait for evidence that the intended field received focus. Concurrent remote connections are isolated correctly. [#1666](#1666), [#1675](#1675)
-
Modern MCP compatibility: The MCP server supports the stateless
2026-07-28revision alongside legacy handshake clients, including discovery and cacheable tool metadata. [#1678](#1678) -
Safer targeting and recovery: Mutating selectors now reject genuinely distinct ambiguous matches instead of silently choosing one. Device inventory, logs, and network capture also moved onto request-bound platform runtimes, improving cancellation, recovery, and process-relaunch handling. [#1667](#1667), [#1699](#1699), [#1701](#1701), [#1702](#1702)
What's Changed
- perf(cli): lazy-load dedicated client command handlers by @thymikee in #1660
- fix(daemon): SESSION_NOT_FOUND hint names the detected foreground app by @thymikee in #1662
- [RFC] prototype: open --foreground collapses the snapshot->hint->open->snapshot dance into one call by @thymikee in #1670
- fix(test): deterministic temp-dir cleanup across node --test lanes by @thymikee in #1661
- feat(cli): compact workflow help card + version header by @thymikee in #1663
- fix: heal dangling XCTestDevices redirect and prove lock-owner death before reclaim by @thymikee in #1672
- fix(cloud): unblock iOS snapshot and gate cloud fill on text-entry focus (#1658) by @thymikee in #1666
- fix(test): clean Swift toolchain temporary directories by @thymikee in #1664
- fix: enforce replay timeout envelope by @thymikee in #1674
- fix(ios): wait for hidden-keyboard synthesized text to commit before responding by @thymikee in #1676
- feat(ios): expose AX custom actions on merged accessibility elements by @thymikee in #1665
- refactor(find): resolve a mutating find's target once, not twice (#1654) by @thymikee in #1669
- fix(daemon): open --foreground P1 hotfix — selector rejection, interactive snapshot, capture-failure masking by @thymikee in #1671
- fix: isolate concurrent remote connections by @thymikee in #1675
- perf(cli): keep node:http and ICU off the CLI startup path (-54% warm call) by @thymikee in #1681
- fix: reject distinct ambiguous mutation targets by @thymikee in #1667
- test: measure oracle liveness suite-wide; pin the one dead-path oracle by @thymikee in #1679
- docs(cli): advertise open --foreground and snapshot --actions in the workflow card by @thymikee in #1682
- feat(mcp): serve the stateless 2026-07-28 revision alongside the legacy handshake by @thymikee in #1678
- fix: harden artifact ingestion boundaries by @thymikee in #1692
- perf: collapse app-driving startup turns by @thymikee in #1693
- docs: split device cloud integration guides by @thymikee in #1695
- feat: add HarmonyOS support by @vwww-droid in #1683
- docs: adopt request-bound platform runtime by @thymikee in #1697
- refactor: extract platform device inventory runtime by @thymikee in #1699
- fix: harden Android snapshot and fill reliability by @thymikee in #1708
- fix(android): shell-quote free-text arguments reaching the device shell by @thymikee in #1645
- test: cap Vitest workers for parallel worktrees by @thymikee in #1710
- fix(test): include workspace packages in changed-line coverage by @thymikee in #1711
- docs: align snapshot fallback and actions guidance by @thymikee in #1713
- refactor: extract platform log runtime by @thymikee in #1701
- refactor: extract platform network runtime by @thymikee in #1702
- docs: record platform runtime adoption checkpoint by @thymikee in #1703
- fix(record): make an aborted authoring recording terminal by construction by @thymikee in #1712
- perf: speed up iOS agent recovery and streamline CLI guidance by @thymikee in #1700
- test: add daemon RPC wire-surface compatibility gate by @thymikee in #1717
- docs: prefer design fixes over regression-only guards by @thymikee in #1722
- feat: add simulator verification skills by @thymikee in #1716
New Contributors
- @vwww-droid made their first contribution in #1683
Full Changelog: v0.20.6...v0.20.7
v0.20.6
Highlights
- Faster, more reliable iOS automation — hostile-screen captures are up to 2× faster, deep private-AX snapshots recover more useful elements, and tap/text-entry handling is substantially more resilient on dynamic apps such as Bluesky.
- Evidence-based interaction outcomes — waits, taps, scrolls, swipes, and other gestures now distinguish confirmed effects from no-ops, surface ambiguity candidates, and explain when success could not be proven.
- Selector-targeted drag gestures — drag elements using selectors instead of brittle coordinates, with improved scroll-container selection across Android and Maestro paths.
- More control over captures and settling — scale screenshots directly and use
--settlewithscrollandbackfor more deterministic workflows. - Stronger recording and replay reliability — Android recording now survives static screens, slow video finalization, and dead processes, while replay artifacts use a single authoritative ledger.
- Safer remote connections — daemon bearer tokens are no longer persisted, and forced reconnects authenticate release requests correctly.
What's Changed
- fix: make wait verdicts evidence-based by @thymikee in #1570
- test(layering): pin exact façade symbols for all workspace packages by @thymikee in #1574
- refactor(replay): make the daemon's artifact set the run's one ledger (#1478 P5 follow-up) by @thymikee in #1575
- docs: remove superseded architecture proposals and design prototypes (#1478 P7) by @thymikee in #1580
- build: verify the published tarball instead of grepping the bundle by @thymikee in #1578
- fix(ios): preserve timed pan duration by @thymikee in #1572
- refactor: extract snapshot and replay identity semantics by @thymikee in #1582
- refactor(android): enforce transport lowering in the type system by @thymikee in #1583
- perf(ios): halve hostile-screen capture cost under the XCTest-channel penalty by @thymikee in #1587
- chore: scan packages/** with fallow instead of ignoring it by @thymikee in #1591
- refactor: move selector engine into workspace package by @thymikee in #1589
- fix(snapshot): one-shot recovered warning for internally armed penalties by @thymikee in #1590
- fix(ios): stop shipping runner unit tests in the npm package by @thymikee in #1594
- fix: surface AMBIGUOUS_MATCH candidates and name find's supported actions by @thymikee in #1602
- fix(daemon): surface proven no-effect gestures to the agent by @thymikee in #1601
- fix: flush stdout/stderr before every CLI process.exit() (#1596) by @thymikee in #1603
- fix(ios): give keyboard dismiss a safe-area-tap fallback (#1598) by @thymikee in #1606
- fix(maestro): unify the scrollable-ancestor walks and fix Android scroll-container selection by @thymikee in #1592
- fix(daemon): judge post-gesture movement by identity, not by the intersection by @thymikee in #1573
- fix(ios): harden Bluesky-class interaction reliability by @thymikee in #1588
- fix: stop the unit suite from leaking temp directories by @thymikee in #1593
- fix: prevent iOS text entry runner wedge by @thymikee in #1604
- feat: add selector-targeted drag gestures by @thiagobrez in #1567
- fix: stabilize Replay Nightly fixture boundaries by @thymikee in #1610
- docs: clarify iOS drag synthesis profiles by @thymikee in #1616
- refactor(daemon): give the Maestro fallback and ambiguous-match details real types by @thymikee in #1612
- refactor(ios): share one private-XCTest event bridge between gesture and text synthesis by @thymikee in #1608
- refactor(contracts): name façade exports explicitly and retire the pin table by @thymikee in #1614
- refactor(contracts): one viewport-root predicate for the whole repo by @thymikee in #1613
- build: eliminate tsdown bundle warnings by @thymikee in #1607
- fix(ios): never signal a recycled runner pid from a stale lease by @thymikee in #1621
- fix(ios): corroborate recorded tap outcomes by @thymikee in #1605
- fix: update MCP registry namespace by @thymikee in #1618
- fix: resolve Dependabot security alerts by @thymikee in #1623
- feat(ios): extend depth-capped private-AX captures via element-rooted requests by @thymikee in #1627
- refactor(daemon): one interface for the deferred interaction outcome by @thymikee in #1633
- refactor: sink backend.ts's cycle-closing types — R9 cycle 76 → 49 by @thymikee in #1636
- fix(ios): pin tap-outcome corroboration probes to the baseline's backend by @thymikee in #1634
- ci: remove package smoke workflow by @thymikee in #1624
- fix: stop the slow-snapshot warning from firing on a single cold start by @thymikee in #1628
- feat: add scale-only screenshot sizing by @szdziedzic in #1617
- fix: honest find contract — press/tap aliases, read-only list action, selector uniqueness by @thymikee in #1637
- refactor: import package types directly — no internal re-export laundering by @thymikee in #1640
- docs: pin the version-skew invariant and the four legitimate compat axes by @thymikee in #1643
- test: pin own-pid start time in device-claim tests to stop load flakes by @thymikee in #1642
- refactor(daemon): consolidate post-gesture surface-evidence helpers and their rationale by @thymikee in #1615
- fix: make Android record stop survive static screens, slow moov finalization, and dead-pid recovery by @thymikee in #1651
- feat: support --settle on scroll and back (#1638) by @thymikee in #1650
- perf(cli): route command aliases through the help fast path by @thymikee in #1641
- fix(daemon): say why a no-effect claim was withheld by @thymikee in #1655
- refactor: declare selector resolution policy as data by @thymikee in #1649
- refactor(ios): runner error classification as data; recovery tested at the transport seam by @thymikee in #1644
- fix(remote): stop persisting the daemon bearer token, and authenticate forced-reconnect release correctly by @thymikee in #1648
- fix(ios): keep the baseline presentation when a tap corroboration has no request flags by @thymikee in #1646
- fix(ios): synthesize hidden-keyboard text through responder by @thymikee in #1657
- fix(daemon): report the device claim retained by a failed close by @thymikee in #1647
Full Changelog: v0.20.5...v0.20.6
v0.20.5
Highlights
-
Restored npm installations. Fixes the 0.20.4 packaging regression that left private workspace imports in the published bundle, causing commands such as
npx agent-device devicesto fail with a missing@agent-device/ad-scriptpackage. -
Release integrity is now enforced. Packaging fails if any private
@agent-device/*import escapes into the built JavaScript or declarations, and CI runsdevices --jsonfrom the globally installed tarball before release. -
Replay internals have a durable package boundary. Native
.adreplay execution now lives in@agent-device/ad-replay, reducing daemon coupling while preserving the existing replay behavior and compatibility contract.
What's Changed
- refactor(daemon): extract native .ad replay to packages/ad-replay (#1478 P5) by @thymikee in #1555
- fix: prevent publishing unresolved workspace imports by @thymikee in #1577
Full Changelog: v0.20.4...v0.20.5
v0.20.4
Highlights
-
Continue interactively after replay. Native
.adreplays support--keep-sessionand SDKkeepSession, allowing agents to suppress a terminalcloseand take over the active session. -
More trustworthy replay execution. Replay gained stronger save-script validation, launch-time capture retries, session-lifecycle handling, post-gesture stability checks, off-screen verification, and divergence evidence.
-
Verify cloud access without allocating a device.
connectvalidates BrowserStack, AWS Device Farm, and Limrun credentials and resources, reports readiness, and provides exact next steps without creating a provider session. -
More capable BrowserStack sessions. Configure orientation, location, timezone, language, locale, networking, and app resigning; cloud orientation now uses valid WebDriver transports.
-
Faster and more reliable device automation. Android permission prompts, targeted text reads, and snapshot recovery are more dependable. Concurrent device discovery, usbmux-first physical iOS transport, and memoized snapshot occlusion reduce latency and prevent CPU stalls.
Security: Project-local configuration can no longer control credentials, remote endpoints, provider settings, headers, or other operator-sensitive values. Thanks to [@glmgbj233](https://github.com/glmgbj233) for responsibly reporting the issue. Action possibly needed: move sensitive or operator-specific values out of project-local agent-device.json files and into user configuration, explicitly selected configuration, environment variables, CLI arguments, or saved remote profiles.
What's Changed
- fix: resolve qs Dependabot alert #55 by @thymikee in #1493
- refactor: pnpm workspace + @agent-device/kernel pilot (#1490 W0) by @thymikee in #1494
- test(ios): fix nightly fixture selector drift by @thymikee in #1492
- refactor: consolidate daemon errors by @thymikee in #1496
- refactor: restore pngjs codec by @thymikee in #1498
- test: stabilize parallel provider scenarios by @thymikee in #1497
- Simplify screenshot diff output by @thymikee in #1495
- test(android): add comprehensive emulator E2E coverage by @thymikee in #1482
- docs: require complete change migrations by @thymikee in #1500
- refactor: extract contracts workspace package by @thymikee in #1499
- fix(daemon): reject unsupported raw --save-script flags at request ingress by @thymikee in #1501
- docs: organize navigation and Node.js integrations by @thymikee in #1503
- test: characterize replay-test reporter contract and extend R10 by @thymikee in #1505
- fix(ios): deny viewport during capability admission on Apple targets by @thymikee in #1507
- refactor: isolate maestro engine package by @thymikee in #1506
- test: add Android full emulator coverage by @thymikee in #1484
- refactor: isolate Limrun construction dependencies by @thymikee in #1502
- refactor(daemon): consolidate session-script test factories and drop saveScriptDefaultedHealedPath by @thymikee in #1508
- fix(ci): drop pipefail from the nightly Android emulator script by @thymikee in #1512
- feat(ios): usbmux runner route override + #1403 transport experiment evidence by @thymikee in #1510
- refactor(replay-test): neutralize the values crossing the scheduler seam (#1478 P3, part 1) by @thymikee in #1509
- refactor: extract WebDriver provider package by @thymikee in #1504
- fix(test): give the iOS scroll search a capture-sized wait budget by @thymikee in #1514
- fix(android): re-capture past a transient helper content verdict by @thymikee in #1513
- test(replay-test): pin the retry-path reporter hint by @thymikee in #1516
- feat(ios): reach physical devices through usbmux first, tunnel as fallback by @thymikee in #1517
- fix(ios): map usbmux connect result codes to the right verdict by @thymikee in #1523
- fix(device): state when nothing is claimed and reap dead claims by @thymikee in #1519
- perf(devices): probe platform inventories concurrently by @thymikee in #1524
- refactor: move Limrun provider behind package facade by @thymikee in #1518
- fix: normalize physical iOS landscape taps by @thymikee in #1520
- test: stop two tests failing on timing and megapixels by @thymikee in #1526
- test: narrow Limrun public type contract by @thymikee in #1528
- fix(ios): name Developer Mode and pairing as the real launch blockers by @thymikee in #1527
- test: fix Android observability scenario to Android contracts by @thymikee in #1531
- refactor(replay-test): extract packages/replay-test (#1478 P3b) by @thymikee in #1525
- fix(ios): diagnose a runner that cannot install, instead of blaming the screen by @thymikee in #1529
- fix: treat Android permission prompts as pending alerts, not tap escapes by @thymikee in #1530
- fix(cli): drop the removed durationMs positional from swipe help by @thymikee in #1534
- refactor(daemon): session script publication behind one capability (#1478 P4a) by @thymikee in #1532
- test(daemon): pin resolveScriptTarget's retention table directly by @thymikee in #1539
- fix(test): repair Android replay fixtures against live device reality by @thymikee in #1538
- refactor(daemon): serialize replay transactions behind a locked coordinator (#1478 P4b) by @thymikee in #1535
- fix: clear the polynomial-redos class across main by @thymikee in #1546
- docs(agents): a green check is evidence only once you have seen it red by @thymikee in #1547
- fix: memoize snapshot occlusion coverage checks to stop a daemon CPU-spin by @thymikee in #1541
- refactor: extract the .ad script codec into packages/ad-script (#1478) by @thymikee in #1536
- fix: restore scheduled workflow health by @thymikee in #1543
- refactor(ios): drop the transport seam usbmux-first made dead by @thymikee in #1540
- feat(cloud-webdriver): BrowserStack device-feature capabilities, and fix cloud orientation by @leovarmak in #1544
- test(replay-test): cover failFast, retry exhaustion, plan-prep failure, JUnit escaping by @thymikee in #1550
- refactor: tidy three post-extraction seams by @thymikee in #1551
- fix(test): tolerate dead session in iOS e2e full-tier cleanup by @thymikee in #1548
- feat(mutation): add target-annotation-serde + snapshot-occlusion kernels by @thymikee in #1553
- fix: clear the last polynomial-redos instance in swift-cache by @thymikee in #1549
- test: pin selector-port behavior ahead of the P5 extraction (#1478) by @thymikee in #1552
- test: make pid-liveness fixtures deterministic under load by @thymikee in #1556
- feat: keep replay session active on request by @thymikee in #1554
- fix(daemon): reject unarmed close --save-script before teardown by @thymikee in #1558
- fix(ios): keyboard-dismiss content settle race (#1542) — partial, defect 2 needs a decision by @thymikee in #1559
- fix: read Android get text from target field by @thymikee in #1561
- fix(ios): double-check off-screen click refusals against a direct element read by @thymikee in #1566
- fix(daemon): distrust post-gesture stability that matches the pre-gesture baseline by @thymikee in https://gith...
v0.20.3
Highlights
A focused reliability release for replay and iOS lifecycle handling.
- Safer replay references. Replay now publishes only element refs actually exposed in the response, keeping them session-bound and one-shot. [#1489](#1489)
- More reliable iOS launches. Clear-state behavior is preserved when opening with launch URLs, and simulator relaunches now use the URL as the atomic cold-launch trigger. [#1486](#1486), [#1483](#1483)
- Expanded Limrun session support. Reusable capabilities now cover app management, input, logs, recording, installation, Android port forwarding, and iOS simulator operations. [#1485](#1485)
What's Changed
- fix: relaunch iOS simulator app URLs atomically by @thymikee in #1483
- feat: expose Limrun device session capabilities by @thymikee in #1485
- test: add scroll-edge-state suite, close mutation gap (#1455) by @thymikee in #1474
- test: pin daemon modularity migration contracts by @thymikee in #1487
- fix: preserve clear state before iOS launch URLs by @thymikee in #1486
- perf: speed up unit tests and streamline checks by @thymikee in #1488
- refactor(kernel): relocate non-foundational stragglers before packaging (#1490 W-pre) by @thymikee in #1491
- fix: publish replay refs after response projection by @thymikee in #1489
Full Changelog: v0.20.2...v0.20.3
v0.20.2
Highlights
This release makes session cleanup safer, replay failures easier to understand, and the SDK more useful for integrations.
- No more leaked device claims after failed script saves — closing a session now releases the device and removes the session even when close-time
.adpersistence fails. - Clearer, safer Maestro failures — resolved target labels and
runFlowpaths appear in diagnostics, sensitiveinputTextremains redacted, and malformed environment entries return typedINVALID_ARGSerrors. - Public Limrun runtime — cloud and provider integrations can now import
LimrunRuntimefromagent-device/limrunwithout depending directly on@limrun/api. - Fail-closed CLI parsing — extra positional arguments are rejected instead of silently ignored, preventing malformed agent plans such as
open app url closefrom partially executing. - Runnable Node.js SDK examples — new examples cover client sessions, batch orchestration, Metro runtime resolution, contracts, and typed error handling.
- More stable replay progress output — terminal output remains intact when the viewport resizes or reflow ends.
- Stronger release confidence — expanded iOS simulator smoke coverage, replay-compat and mutation gates, property-based testing, concurrency checks, and narrowly controlled timeout-only CI retries.
Compatibility note
Commands with excess positional arguments now fail with INVALID_ARGS. Any scripts relying on previously ignored trailing arguments must use the command’s documented syntax.
What's Changed
- fix(android): stamp status-bar chrome during the walk instead of reconstructing it downstream (#1319) by @thymikee in #1359
- fix(apple): parse parenthesized xctrace physical device format by @devin-ai-integration[bot] in #1360
- Generalize subprocess stub tests to support multiple platforms by @thymikee in #1362
- chore: remove dead code and tune fallow's dead-code rules by @thymikee in #1363
- chore: remove dead type re-exports, prune stale suppressions, gate unused-types by @thymikee in #1364
- chore: remove verified dead code and migration scaffolding (~700 LOC) by @thymikee in #1367
- test: serialize client-metro and harden the unit suite against ambient daemon env by @thymikee in #1365
- chore(deps): resolve dependabot alerts by @thymikee in #1372
- refactor(cli): decompose runCli into explicit phase functions by @thymikee in #1373
- refactor(interaction): resolve the CapturedSnapshot name collision by @thymikee in #1371
- refactor(commands): derive pass-through command bindings with a generic BoundOf helper by @thymikee in #1368
- fix: select iOS simulator by installed app by @thymikee in #1376
- fix(android): preserve fixed siblings after snapshot filtering by @thymikee in #1378
- feat(events): enrich session event details by @szdziedzic in #1379
- fix: report bundle sizes with two decimal MB precision by @thymikee in #1382
- fix(interaction): direction-named, selector-first off-screen recovery hints (#1366) by @thymikee in #1374
- fix(daemon): explicit abort message on uncommitted repair close by @jibin7jose in #1383
- feat(replay): recorded landmark identity for wait, is coverage — read-only step identity (#1349) by @thymikee in #1381
- fix(daemon): keep an active replay session's daemon alive over the CLI path by @thymikee in #1390
- feat: parameterize sensitive recorded inputs by @thymikee in #1369
- feat: add WebView accessibility lab by @thymikee in #1397
- fix(cli): compact stale device status by @thymikee in #1388
- docs: restructure AGENTS.md and CONTEXT.md for progressive disclosure by @thymikee in #1402
- fix(replay): preserve cwd scope for opened sessions by @Bortlesboat in #1401
- feat: add first-class Vega VVD TV support by @thymikee in #1396
- docs(adr): rules-first ADR restructure + ADR 0017 proposal (unified event journal) by @thymikee in #1399
- feat(apple): injectable Apple runner transport seam for provider interactors by @thymikee in #1389
- fix(gestures): fail pre-removal gesture forms at .ad parse time (#1216) by @thymikee in #1393
- refactor(boundaries): put shared contracts below their consumers, gate the result by @thymikee in #1405
- feat: strengthen agent help benchmarks by @thymikee in #1404
- fix: select app-bearing simulator for replay by @thymikee in #1387
- fix: support xctrace-only iOS physical devices by @thymikee in #1375
- fix(replay): bounded retry on pre-dispatch capture racing app launch (#1385) by @thymikee in #1386
- feat(scripts): dependency-graph report over the layering gate's model by @thymikee in #1410
- feat(bench): renderer-pinned samples, topic-coverage gate, error-recovery quizzes; trim skillgym to agentic checks by @thymikee in #1411
- docs: architecture principles mapped to enforcing gates and open ledgers by @thymikee in #1428
- docs: bless the works-today iOS SpringBoard/widget workflow (#1296 PR A) by @thymikee in #1395
- fix: update vulnerable dependencies by @thymikee in #1434
- fix(daemon): keep close-time script-save failures from leaking the session/device claim by @thymikee in #1392
- test: property-based testing foundation (fast-check) over parse/print and geometry kernels by @devin-ai-integration[bot] in #1437
- refactor(contracts): declare shared vocabulary below its consumers, ratchet what remains by @thymikee in #1435
- fix(cli): reject excess positionals by @Bortlesboat in #1433
- test: frozen replay-compat corpus with expected verdicts (#1417) by @devin-ai-integration[bot] in #1436
- test: ratchet mutation score over enumerated decision kernels by @devin-ai-integration[bot] in #1441
- test(daemon): seeded concurrency torture lane for session/lease/lock invariants by @devin-ai-integration[bot] in #1439
- test: nightly parser fuzz lane — parser input fails as typed AppErrors, never hangs (#1414) by @devin-ai-integration[bot] in #1438
- fix(ci): upload hidden diagnostics by @thymikee in #1443
- test: gate public command surface against commands.md reference by @devin-ai-integration[bot] in #1446
- chore(deps): Renovate config, packageManager-derived pnpm in CI, repo-wide format by @devin-ai-integration[bot] in #1444
- test(ci): gate PRs on changed-line coverage (#1418) by @devin-ai-integration[bot] in #1447
- test: enumerate error-code recovery quizzes in a unit-lane gate by @devin-ai-integration[bot] in #1445
- test: smoke every package.json export subpath from the packed package by @thymikee in #1452
- fix(replay): resolve Maestro failures and resize progress by @thymikee in #1450
- test(replay): split session-replay-vars.test.ts by actual subject (#1460) by @thymikee in #1468
- refactor(daemon): table-ify handleSessionCommands by @thymikee in #1467
- build(android): unify the 4 helper build/package scripts behind one parameterized pair by @thymikee in #1466
- refactor(layering): declare R1-R3 as a policy table, and test them by @thymikee in #1449
- feat(scripts): blast-radius query — dependents, owning gates, live-coverage owners (#1425) by @devin-ai-integration[bot] in #1470
- test(apple): split the 3,487-line core index.test.ts along its import boundaries by @thymikee in #1472
- feat: expose Limrun runtime by @thymikee in https://github.com/cal...
v0.20.1
Highlights
This release expands platform coverage and makes recorded workflows safer and more reliable.
- Amazon Vega OS TV support — control apps in the Vega Virtual Device with discovery, lifecycle commands, navigation, and the complete TV remote surface.
- Broader physical iOS support — xctrace-only devices can now use XCTest-backed app automation through usbmux, including interactions, snapshots, and screenshots.
- Secret-safe replay recording —
fill --record-as <VAR>sends the real value to the app while storing only a${VAR}placeholder in the recorded.adscript. - Stronger replay verification — recorded
waitlandmarks now retain element identity, preventing same-label elements on changed screens from producing false-positive replays. - More resilient replay startup — bounded retries handle transient sparse captures while an app is launching, without masking permanent transport failures.
- Smarter device selection — replay can select the simulator containing the requested app and better preserves authored platform and deep-link intent.
- Better recovery guidance — off-screen interaction errors now recommend a direction, bounded movement, and selector-based retries.
- iOS system UI guidance — new help covers SpringBoard and simulator widget add, edit, and removal workflows.
- Improved diagnostics — richer, sanitized session-event details make automation failures easier to investigate.
- Stronger release confidence — substantially expanded registry-derived iOS simulator coverage, replay scenarios, architecture gates, and help-conformance checks.
Compatibility note
Legacy recorded gesture forms containing removed duration or velocity positionals now fail during .ad parsing with migration guidance. Convert timed movement to gesture pan or use the current canonical gesture syntax.
What's Changed
- fix(android): stamp status-bar chrome during the walk instead of reconstructing it downstream (#1319) by @thymikee in #1359
- fix(apple): parse parenthesized xctrace physical device format by @devin-ai-integration[bot] in #1360
- Generalize subprocess stub tests to support multiple platforms by @thymikee in #1362
- chore: remove dead code and tune fallow's dead-code rules by @thymikee in #1363
- chore: remove dead type re-exports, prune stale suppressions, gate unused-types by @thymikee in #1364
- chore: remove verified dead code and migration scaffolding (~700 LOC) by @thymikee in #1367
- test: serialize client-metro and harden the unit suite against ambient daemon env by @thymikee in #1365
- chore(deps): resolve dependabot alerts by @thymikee in #1372
- refactor(cli): decompose runCli into explicit phase functions by @thymikee in #1373
- refactor(interaction): resolve the CapturedSnapshot name collision by @thymikee in #1371
- refactor(commands): derive pass-through command bindings with a generic BoundOf helper by @thymikee in #1368
- fix: select iOS simulator by installed app by @thymikee in #1376
- fix(android): preserve fixed siblings after snapshot filtering by @thymikee in #1378
- feat(events): enrich session event details by @szdziedzic in #1379
- fix: report bundle sizes with two decimal MB precision by @thymikee in #1382
- fix(interaction): direction-named, selector-first off-screen recovery hints (#1366) by @thymikee in #1374
- fix(daemon): explicit abort message on uncommitted repair close by @jibin7jose in #1383
- feat(replay): recorded landmark identity for wait, is coverage — read-only step identity (#1349) by @thymikee in #1381
- fix(daemon): keep an active replay session's daemon alive over the CLI path by @thymikee in #1390
- feat: parameterize sensitive recorded inputs by @thymikee in #1369
- feat: add WebView accessibility lab by @thymikee in #1397
- fix(cli): compact stale device status by @thymikee in #1388
- docs: restructure AGENTS.md and CONTEXT.md for progressive disclosure by @thymikee in #1402
- fix(replay): preserve cwd scope for opened sessions by @Bortlesboat in #1401
- feat: add first-class Vega VVD TV support by @thymikee in #1396
- docs(adr): rules-first ADR restructure + ADR 0017 proposal (unified event journal) by @thymikee in #1399
- feat(apple): injectable Apple runner transport seam for provider interactors by @thymikee in #1389
- fix(gestures): fail pre-removal gesture forms at .ad parse time (#1216) by @thymikee in #1393
- refactor(boundaries): put shared contracts below their consumers, gate the result by @thymikee in #1405
- feat: strengthen agent help benchmarks by @thymikee in #1404
- fix: select app-bearing simulator for replay by @thymikee in #1387
- fix: support xctrace-only iOS physical devices by @thymikee in #1375
- fix(replay): bounded retry on pre-dispatch capture racing app launch (#1385) by @thymikee in #1386
- feat(scripts): dependency-graph report over the layering gate's model by @thymikee in #1410
- feat(bench): renderer-pinned samples, topic-coverage gate, error-recovery quizzes; trim skillgym to agentic checks by @thymikee in #1411
- docs: architecture principles mapped to enforcing gates and open ledgers by @thymikee in #1428
- docs: bless the works-today iOS SpringBoard/widget workflow (#1296 PR A) by @thymikee in #1395
- fix: update vulnerable dependencies by @thymikee in #1434
New Contributors
- @jibin7jose made their first contribution in #1383
- @Bortlesboat made their first contribution in #1401
Full Changelog: v0.20.0...v0.20.1