From e7e81cd5d94dddd7f609f3f330fd635ba48bd973 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Wed, 4 Nov 2026 18:42:55 +0000 Subject: [PATCH] fix(server): pin agent-device 0.21.33 so orientation rotates foldable simulators On an iPhone Duo simulator, agent-device 0.21.31 reported "Rotated to landscape-left" without rotating the screen. 0.21.33 routes foldable orientation through `fold-helper rotate`, which sends the simulator's orientation-picker-control HID event (callstack/agent-device#3968). Its CLI surface matches 0.21.31: the same global and per-command flag definitions, the same commands, and an identical command schema, so the launcher's value-flag list and its refused/managed command lists are unchanged. The eval's captured help transcript is regenerated from 0.21.33, because the evals check it against this pin. Tests: vp test run apps/server/src/device, evals/mcp-tool-use/devices.test.ts and scripts/lib/third-party-licenses.test.ts; vp run check:changed. CLI surface compared from npm pack of both versions (flag definitions, command schema, help output). AI-assisted; verified with the tests and tools listed above. --- apps/server/src/device/AgentDeviceShim.ts | 4 ++-- apps/server/src/device/AgentDeviceTarget.test.ts | 2 +- apps/server/src/device/deviceToolVersions.json | 2 +- evals/mcp-tool-use/agent-device-help.txt | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/server/src/device/AgentDeviceShim.ts b/apps/server/src/device/AgentDeviceShim.ts index b50f4dfbf2a40..8103ad86dd808 100644 --- a/apps/server/src/device/AgentDeviceShim.ts +++ b/apps/server/src/device/AgentDeviceShim.ts @@ -12,7 +12,7 @@ const STALE_RUNTIME = const quoteShell = (value: string) => "'" + value.replaceAll("'", "'\"'\"'") + "'"; -/** agent-device 0.21.31's flags that take a value, so the launcher can find the command past them. */ +/** agent-device 0.21.33's flags that take a value, so the launcher can find the command past them. */ const VALUE_FLAGS: ReadonlyArray = [ "--activity", "--all", @@ -149,7 +149,7 @@ const VALUE_FLAGS: ReadonlyArray = [ ]; // Help gets no bound flags (agent-device reads --config even for help); only args before `--` are flags. -// agent-device 0.21.31 refuses some bound flags per command; connect/disconnect/auth/daemon would +// agent-device 0.21.33 refuses some bound flags per command; connect/disconnect/auth/daemon would // replace T3's connection; device status reads this machine's claims. It reports a sandboxed shell and a // dropped tunnel alike, so check first, at --daemon-base-url if given, as agent-device prefers it. const launcherSource = (node: string, entryPath: string, boundArgs: ReadonlyArray) => diff --git a/apps/server/src/device/AgentDeviceTarget.test.ts b/apps/server/src/device/AgentDeviceTarget.test.ts index d8c99eb3321f2..0a679b88ee63c 100644 --- a/apps/server/src/device/AgentDeviceTarget.test.ts +++ b/apps/server/src/device/AgentDeviceTarget.test.ts @@ -206,7 +206,7 @@ exec '${process.execPath.replaceAll("'", "'\"'\"'")}' "$@" path.join(path.dirname(command), "agent-device-launcher.mjs"), ...args, ]).then((result) => JSON.parse(result.stdout)); - // agent-device 0.21.31 rejects these flags with "unknown option" or "not supported for command". + // agent-device 0.21.33 rejects these flags with "unknown option" or "not supported for command". const cdp = ["cdp", "target", "list", "--url", "http://127.0.0.1:8081"]; expect(yield* Effect.promise(() => launch(cdp))).toEqual(cdp); expect(yield* Effect.promise(() => launch(["connection"]))).toEqual([ diff --git a/apps/server/src/device/deviceToolVersions.json b/apps/server/src/device/deviceToolVersions.json index e441cc55a9d34..990c3635a78ea 100644 --- a/apps/server/src/device/deviceToolVersions.json +++ b/apps/server/src/device/deviceToolVersions.json @@ -1,4 +1,4 @@ { - "agent-device": "0.21.31", + "agent-device": "0.21.33", "expo-device-hub": "0.19.1" } diff --git a/evals/mcp-tool-use/agent-device-help.txt b/evals/mcp-tool-use/agent-device-help.txt index a45698bfb2f04..7a018f7817ee0 100644 --- a/evals/mcp-tool-use/agent-device-help.txt +++ b/evals/mcp-tool-use/agent-device-help.txt @@ -1,4 +1,4 @@ -agent-device 0.21.31 prints this help; apps/server/src/device/deviceToolVersions.json pins that version. Regenerate from +agent-device 0.21.33 prints this help; apps/server/src/device/deviceToolVersions.json pins that version. Regenerate from the repo root with: node scripts/node_modules/agent-device/bin/agent-device.mjs help [commands|record|screenshot|open|settings|fill] $ agent-device help @@ -56,7 +56,7 @@ Guides (agent-device help ): $ agent-device help commands -agent-device 0.21.31 — commands +agent-device 0.21.33 — commands Full command catalog. Use agent-device help for exact flags and behavior. @@ -215,7 +215,7 @@ $ agent-device help open Usage: agent-device open [appOrUrl] [url] [--activity ] [--launch-console ] [--launch-args ] [--test-ime] [--no-test-ime] [--save-script [path]] [--force, --overwrite] [--no-record] [--relaunch] [--foreground] [--timeout ] [--wait ] [--surface app|frontmost-app|desktop|menubar] [--metro-host ] [--metro-port ] [--bundle-url ] [--launch-url ] -Boot the selected device when needed, then open an app, deep link, or URL in a session. Use the app or URL inputs to choose what becomes the foreground automation target. Use --platform to bind URL/deep-link opens to the target platform. For iOS simulator initial stdout/stderr, put --launch-console on this open command, for example agent-device open "Agent Device Tester" --platform ios --launch-console artifacts/launch-console.log. Expo Go/dev-client shells accept host + URL, for example agent-device open "Expo Go" exp://127.0.0.1:8081 --platform ios. macOS also supports --surface app|frontmost-app|desktop|menubar. --metro-host/--metro-port/--bundle-url choose the app's Metro server and save that address for later metro reload calls in the same session. A fresh open without Metro hints clears an older same-name binding; close also clears it. --launch-url supplies the app's launch URL; it does not select the reload server. On an iOS simulator without --launch-url, open connects a detected Expo development client through its expo-development-client URL. The app must declare a single exp+ scheme that no other installed app owns. If detection is inconclusive, the app opens normally. Bare React Native apps receive their server settings before launch. On an iOS simulator, when --launch-url raises an "Open in ?" prompt that keeps open from seeing the app, open accepts it if the URL scheme belongs to the session app (data.launchConfirmation: "accepted"); if the scheme belongs to another installed app, the prompt stays on screen and open fails with details.reason launch_confirmation_foreign_app; if the scheme owner cannot be resolved because no single installed app owns it or the lookup failed, the prompt stays on screen and open returns with data.launchConfirmation: "unanswered", launchConfirmationReason url-owner-unresolved, and a warning; a prompt that is seen but not recognized is reported the same way with launchConfirmationReason alert-unrecognized and its own warning; in both cases inspect the warning and answer with alert get then alert accept; when agent-device cannot read whether a prompt is up at all (the alert read, the accept, or the runner itself failed, including after a re-hand of the URL), open stays green, reports no launchConfirmation plus data.launchConfirmationProbeFailure naming the failed step (alert-read, alert-accept, or runner), and appends a warning that says to check with alert get. The prompt is read whenever open cannot see the app afterwards, including on a slow host where the app's target discovery fails on its own deadline. When an accept leaves the app provably not running, open hands the same URL over once more and reads again; still not running, it fails with details.reason launch_confirmation_unanswered. A recognized prompt still visible after a failed accept also fails with this reason, even if the app is running behind it; answer it with alert accept or alert dismiss. A simulator openurl that CoreSimulator never answers fails within 20s with details.reason ios-simulator-openurl-timeout. Only the English prompt title with Cancel and Open buttons is recognized, so on a Simulator set to another language the prompt stays on screen. A prompt that appears after open has already seen the app is not answered; use alert accept. A device another session is holding refuses at once; add --wait to block up to that budget for it and only then fail with DEVICE_IN_USE naming the owning session. +Boot the selected device when needed, then open an app, deep link, or URL in a session. Use the app or URL inputs to choose what becomes the foreground automation target. Use --platform to bind URL/deep-link opens to the target platform. For iOS simulator initial stdout/stderr, put --launch-console on this open command, for example agent-device open "Agent Device Tester" --platform ios --launch-console artifacts/launch-console.log. Expo Go/dev-client shells accept host + URL, for example agent-device open "Expo Go" exp://127.0.0.1:8081 --platform ios. macOS also supports --surface app|frontmost-app|desktop|menubar. --metro-host/--metro-port/--bundle-url choose the app's Metro server and save that address for later metro reload calls in the same session. A fresh open without Metro hints clears an older same-name binding; close also clears it. --launch-url supplies the app's launch URL; it does not select the reload server. On an iOS simulator without --launch-url, open connects a detected Expo development client through its expo-development-client URL. The app must declare a single exp+ scheme that no other installed app owns. If detection is inconclusive, the app opens normally. Bare React Native apps receive their server settings before launch. On an iOS simulator, when --launch-url raises an "Open in ?" prompt that keeps open from seeing the app, open accepts it if the URL scheme belongs to the session app (data.launchConfirmation: "accepted"); if the scheme belongs to another installed app, the prompt stays on screen and open fails with details.reason launch_confirmation_foreign_app; if the scheme owner cannot be resolved because no single installed app owns it or the lookup failed, the prompt stays on screen and open returns with data.launchConfirmation: "unanswered", launchConfirmationReason url-owner-unresolved, and a warning; a prompt that is seen but not recognized is reported the same way with launchConfirmationReason alert-unrecognized and its own warning; for url-owner-unresolved, answer the prompt with alert accept; for alert-unrecognized, read the alert with alert get first, because it can be the app's own alert (such as a permission prompt) rather than a translated launch confirmation, and answer with alert accept only when it is the launch confirmation; when agent-device cannot read whether a prompt is up at all (the alert read, the accept, or the runner itself failed, including after a re-hand of the URL), open stays green, reports no launchConfirmation plus data.launchConfirmationProbeFailure naming the failed step (alert-read, alert-accept, or runner), and appends a warning that says to check with alert get. The prompt is read whenever open cannot see the app afterwards, including on a slow host where the app's target discovery fails on its own deadline. When an accept leaves the app provably not running, open hands the same URL over once more and reads again; still not running, it fails with details.reason launch_confirmation_unanswered. A recognized prompt still visible after a failed accept also fails with this reason, even if the app is running behind it; answer it with alert accept or alert dismiss. A simulator openurl that CoreSimulator never answers fails within 20s with details.reason ios-simulator-openurl-timeout. Only the English prompt title with Cancel and Open buttons is recognized, so on a Simulator set to another language the prompt stays on screen. A prompt that appears after open has already seen the app is not answered; use alert accept. A device another session is holding refuses at once; add --wait to block up to that budget for it and only then fail with DEVICE_IN_USE naming the owning session. Command flags: --force, --overwrite Force replacement of existing state (connect: replace an active connection; --save-script: overwrite an existing target instead of refusing)