@swmansion/argent 0.17.1-next.2 → 0.17.1-next.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -116365,6 +116365,15 @@ async function withDeadline(p, fallback, label) {
116365
116365
  var zodSchema = external_exports.object({});
116366
116366
  var listDevicesTool = {
116367
116367
  id: "list-devices",
116368
+ interaction: {
116369
+ startedMsg: () => "Listing devices",
116370
+ completedMsg: ({ result }) => {
116371
+ const deviceLabel = result.devices.length === 1 ? "device" : "devices";
116372
+ const avdLabel = result.avds.length === 1 ? "AVD" : "AVDs";
116373
+ return `Listed ${result.devices.length} ${deviceLabel} and ${result.avds.length} ${avdLabel}`;
116374
+ },
116375
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to list devices: ${failureSignal2.error_code}`
116376
+ },
116368
116377
  description: `List iOS simulators, Android emulators, connected physical Android devices, running Chromium apps, and Vega (Fire TV) devices in one place.
116369
116378
  Use at the start of a session to pick a target id ('udid' for iOS entries, 'serial' for Android/Vega entries, 'id' for Chromium) to pass to interaction tools, and to see which targets are already running.
116370
116379
  Returns { devices, avds } where each device carries a 'platform' discriminator ('ios', 'android', 'chromium', or 'vega'); 'avds' lists Android AVDs bootable via boot-device. A Vega VVD is listed under 'devices' whether running or stopped (state 'running'/'stopped'); start a stopped one with boot-device using its 'vvdImage'.
@@ -121817,6 +121826,11 @@ var zodSchema2 = external_exports.object({
121817
121826
  });
121818
121827
  var nativeDevtoolsStatusTool = {
121819
121828
  id: "native-devtools-status",
121829
+ interaction: {
121830
+ startedMsg: ({ params }) => `Checking native inspection for ${params.bundleId}`,
121831
+ completedMsg: ({ params }) => `Checked native inspection for ${params.bundleId}`,
121832
+ failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to check native inspection for ${params.bundleId}: ${failureSignal2.error_code}`
121833
+ },
121820
121834
  capability: { apple: { simulator: true, device: true }, appleRemote: { simulator: true } },
121821
121835
  // The "injectable is false" recovery sentence inlines NON_INJECTABLE_RECOVERY
121822
121836
  // verbatim: the description must stay a plain literal so scripts/extract-tools.mjs
@@ -121895,6 +121909,11 @@ var zodSchema3 = external_exports.object({
121895
121909
  });
121896
121910
  var nativeNetworkLogsTool = {
121897
121911
  id: "native-network-logs",
121912
+ interaction: {
121913
+ startedMsg: ({ params }) => `Reading native network activity for ${params.bundleId}`,
121914
+ completedMsg: ({ params }) => `Read native network activity for ${params.bundleId}`,
121915
+ failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to read native network activity for ${params.bundleId}: ${failureSignal2.error_code}`
121916
+ },
121898
121917
  capability: { apple: { simulator: true, device: true }, appleRemote: { simulator: true } },
121899
121918
  description: `Retrieve network requests captured at the native NSURLProtocol level.
121900
121919
  Unlike the JS-level network inspector (view-network-logs), this captures ALL network traffic from the app including native modules, Swift/Objective-C networking, and background transfers that bypass JS fetch.
@@ -121937,6 +121956,14 @@ var zodSchema4 = external_exports.object({
121937
121956
  });
121938
121957
  var nativeFindViewsTool = {
121939
121958
  id: "native-find-views",
121959
+ interaction: {
121960
+ startedMsg: ({ params }) => `Searching native views in ${params.bundleId}`,
121961
+ completedMsg: ({ params, result }) => {
121962
+ const count = result.status === "ok" ? result.matches.length : 0;
121963
+ return `Found ${count} native ${count === 1 ? "view" : "views"} in ${params.bundleId}`;
121964
+ },
121965
+ failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to search native views in ${params.bundleId}: ${failureSignal2.error_code}`
121966
+ },
121940
121967
  capability: { apple: { simulator: true, device: true }, appleRemote: { simulator: true } },
121941
121968
  description: `Search for specific UIViews in the running app by class name, accessibility identifier, label, tag, or React Native nativeID.
121942
121969
  Use when you need to locate a specific view by its properties without dumping the entire hierarchy.
@@ -121999,6 +122026,11 @@ var zodSchema5 = external_exports.object({
121999
122026
  });
122000
122027
  var nativeFullHierarchyTool = {
122001
122028
  id: "native-full-hierarchy",
122029
+ interaction: {
122030
+ startedMsg: ({ params }) => `Reading native view hierarchy for ${params.bundleId}`,
122031
+ completedMsg: ({ params }) => `Read native view hierarchy for ${params.bundleId}`,
122032
+ failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to read native view hierarchy for ${params.bundleId}: ${failureSignal2.error_code}`
122033
+ },
122002
122034
  capability: { apple: { simulator: true, device: true }, appleRemote: { simulator: true } },
122003
122035
  description: `Get the complete UIKit view tree for the running app.
122004
122036
  WARNING: Output can be extremely large (100KB\u2013500KB+) for complex apps, especially those built with SwiftUI. Prefer native-find-views for targeted queries.
@@ -122046,6 +122078,11 @@ var zodSchema6 = external_exports.object({
122046
122078
  });
122047
122079
  var nativeDescribeScreenTool = {
122048
122080
  id: "native-describe-screen",
122081
+ interaction: {
122082
+ startedMsg: ({ params }) => `Reading native screen for ${params.bundleId}`,
122083
+ completedMsg: ({ params }) => `Read native screen for ${params.bundleId}`,
122084
+ failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to read native screen for ${params.bundleId}: ${failureSignal2.error_code}`
122085
+ },
122049
122086
  capability: { apple: { simulator: true, device: true }, appleRemote: { simulator: true } },
122050
122087
  description: `Read the running app's native accessibility screen description via injected native devtools.
122051
122088
 
@@ -122116,6 +122153,11 @@ var zodSchema7 = external_exports.object({
122116
122153
  });
122117
122154
  var nativeViewAtPointTool = {
122118
122155
  id: "native-view-at-point",
122156
+ interaction: {
122157
+ startedMsg: ({ params }) => `Inspecting native view at (${params.x}, ${params.y})`,
122158
+ completedMsg: ({ params }) => `Inspected native view at (${params.x}, ${params.y})`,
122159
+ failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to inspect native view at (${params.x}, ${params.y}): ${failureSignal2.error_code}`
122160
+ },
122119
122161
  capability: { apple: { simulator: true, device: true }, appleRemote: { simulator: true } },
122120
122162
  description: `Inspect the deepest visible UIView at a raw native window point.
122121
122163
 
@@ -122187,6 +122229,11 @@ var zodSchema8 = external_exports.object({
122187
122229
  });
122188
122230
  var nativeUserInteractableViewAtPointTool = {
122189
122231
  id: "native-user-interactable-view-at-point",
122232
+ interaction: {
122233
+ startedMsg: ({ params }) => `Finding interactive view at (${params.x}, ${params.y})`,
122234
+ completedMsg: ({ params }) => `Found interactive view at (${params.x}, ${params.y})`,
122235
+ failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to find interactive view at (${params.x}, ${params.y}): ${failureSignal2.error_code}`
122236
+ },
122190
122237
  capability: { apple: { simulator: true, device: true }, appleRemote: { simulator: true } },
122191
122238
  description: `Inspect the deepest UIView at a raw native window point that would actually receive touch input.
122192
122239
 
@@ -124235,6 +124282,9 @@ var zodSchema9 = external_exports.object({
124235
124282
  "Electron-only: extra CLI arguments forwarded to the Electron binary after the app path."
124236
124283
  )
124237
124284
  });
124285
+ function bootTarget(params) {
124286
+ return params.udid ?? params.avdName ?? params.vvdImage ?? params.electronAppPath ?? "device";
124287
+ }
124238
124288
  var LAUNCH_HARDENING_ARGS = [
124239
124289
  "-noaudio",
124240
124290
  "-no-boot-anim",
@@ -124969,6 +125019,11 @@ var capability = {
124969
125019
  function createBootDeviceTool(registry2) {
124970
125020
  return {
124971
125021
  id: "boot-device",
125022
+ interaction: {
125023
+ startedMsg: ({ params }) => `Starting ${bootTarget(params)}`,
125024
+ completedMsg: ({ params }) => `Started ${bootTarget(params)}`,
125025
+ failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to start ${bootTarget(params)}: ${failureSignal2.error_code}`
125026
+ },
124972
125027
  description: `Start an iOS simulator, launch an Android emulator, start a Vega (Fire TV) Virtual Device, or spawn an Electron app and wait until it is ready to accept interactions.
124973
125028
  Pick the platform by which argument you pass: 'udid' for an iOS simulator from list-devices, 'avdName' for an Android AVD (a serial is assigned automatically), 'vvdImage' for a Vega VVD (the 'vvdImage' of a vega device from list-devices, e.g. 'tv'), or 'electronAppPath' for an Electron app (a CDP remote-debugging port is picked automatically, or pass 'electronPort' to fix one).
124974
125029
  Use at the start of a session once you have picked a target.
@@ -125293,6 +125348,11 @@ var capability2 = {
125293
125348
  function createLaunchAppTool(registry2) {
125294
125349
  return {
125295
125350
  id: "launch-app",
125351
+ interaction: {
125352
+ startedMsg: ({ params }) => `Launching ${params.bundleId}`,
125353
+ completedMsg: ({ params }) => `Launched ${params.bundleId}`,
125354
+ failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to launch ${params.bundleId}: ${failureSignal2.error_code}`
125355
+ },
125296
125356
  description: `Open an app by its bundle id (iOS) or package name (Android), or confirm the running renderer (Chromium).
125297
125357
  Use when starting any app \u2014 prefer this over tapping home-screen / launcher icons. Also prepares the native-devtools injection before the app starts (the iOS slice on iOS, the tvOS slice on Apple TV); on tvOS, interaction is focus-driven \u2014 use the tv-* tools rather than coordinate taps.
125298
125358
  Returns { launched, bundleId }. Fails if the app is not installed on the target device (iOS / Android).
@@ -125478,6 +125538,11 @@ var capability3 = {
125478
125538
  function createRestartAppTool(registry2) {
125479
125539
  return {
125480
125540
  id: "restart-app",
125541
+ interaction: {
125542
+ startedMsg: ({ params }) => `Restarting ${params.bundleId}`,
125543
+ completedMsg: ({ params }) => `Restarted ${params.bundleId}`,
125544
+ failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to restart ${params.bundleId}: ${failureSignal2.error_code}`
125545
+ },
125481
125546
  description: `Terminate then relaunch an app by bundle id / package name.
125482
125547
  Use when you need a clean in-memory state without a full reinstall. Also refreshes the native-devtools injection before the relaunch (the iOS slice on iOS, the tvOS slice on Apple TV); on tvOS, interaction is focus-driven \u2014 use the tv-* tools rather than coordinate taps.
125483
125548
  Returns { restarted, bundleId }. Fails if the app is not installed.`,
@@ -125639,6 +125704,11 @@ var capability4 = {
125639
125704
  };
125640
125705
  var reinstallAppTool = {
125641
125706
  id: "reinstall-app",
125707
+ interaction: {
125708
+ startedMsg: ({ params }) => `Reinstalling ${params.bundleId}`,
125709
+ completedMsg: ({ params }) => `Reinstalled ${params.bundleId}`,
125710
+ failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to reinstall ${params.bundleId}: ${failureSignal2.error_code}`
125711
+ },
125642
125712
  description: `Install or reinstall an app on the device. The previous installation (if any) is uninstalled first so app data and runtime permissions are cleared.
125643
125713
  Use for a full reinstall after rebuilding, or to start from a clean app state.
125644
125714
  Returns { reinstalled, bundleId }. Fails if the app path does not exist or the package does not match the platform (.app for iOS, .apk for Android, .vpkg for Vega).`,
@@ -125947,6 +126017,11 @@ var zodSchema13 = external_exports.object({
125947
126017
  "App to change the permission for \u2014 required for every action. iOS: bundle id (e.g. com.example.app). Android: package name. `reset` is per-app too: simctl's device-wide reset (no bundleId) silently leaves existing per-app grants untouched on recent iOS, so the permission is always reset for this one app."
125948
126018
  )
125949
126019
  });
126020
+ var permissionAction = {
126021
+ grant: { started: "Granting", completed: "Granted" },
126022
+ deny: { started: "Denying", completed: "Denied" },
126023
+ reset: { started: "Resetting", completed: "Reset" }
126024
+ };
125950
126025
  var capability5 = {
125951
126026
  // `simctl privacy` edits the simulator's TCC store — physical iPhones have no
125952
126027
  // equivalent host-side switch, so no `device: true` on apple.
@@ -125959,6 +126034,11 @@ var capability5 = {
125959
126034
  };
125960
126035
  var settingsPermissionsTool = {
125961
126036
  id: "settings-permissions",
126037
+ interaction: {
126038
+ startedMsg: ({ params }) => `${permissionAction[params.action].started} ${params.permission} permission for ${params.bundleId}`,
126039
+ completedMsg: ({ params }) => `${permissionAction[params.action].completed} ${params.permission} permission for ${params.bundleId}`,
126040
+ failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to ${params.action} ${params.permission} permission for ${params.bundleId}: ${failureSignal2.error_code}`
126041
+ },
125962
126042
  description: `Grant, deny, or reset a runtime permission for an app without navigating the system Settings UI. Use during test setup to pre-authorize (or explicitly deny) a service before the app asks, or \`reset\` so the permission dialog appears again on next use. Always per-app: bundleId is required.
125963
126043
  Permissions: camera, microphone, photos, contacts, notifications, calendar, location, location-always, media-library, motion, reminders.
125964
126044
  iOS simulator: edits the simulator's TCC store, always per-app. \`notifications\` is not supported (no iOS equivalent). \`reset\` is per-app \u2014 a device-wide reset is a no-op for existing grants on recent iOS, so it is not offered. \`grant location\`/\`location-always\` needs the app already installed (location auth isn't stored in TCC and isn't applied to a bundle id until the app exists) \u2014 enforced on local simulators; a remote simulator can't be probed for install state, so ensure the app is installed there first. Other services can be granted before install.
@@ -126065,6 +126145,14 @@ var zodSchema14 = external_exports.object({
126065
126145
  "URL or scheme to open (e.g. https://example.com, messages://, tel:555, geo:37.0,-122.0). For Chromium this navigates the renderer."
126066
126146
  )
126067
126147
  });
126148
+ function safeDestination(value) {
126149
+ try {
126150
+ const url2 = new URL(value);
126151
+ return url2.protocol === "http:" || url2.protocol === "https:" ? url2.hostname : url2.protocol.replace(/:$/, "");
126152
+ } catch {
126153
+ return "URL";
126154
+ }
126155
+ }
126068
126156
  var capability6 = {
126069
126157
  apple: { simulator: true, device: true },
126070
126158
  appleRemote: { simulator: true },
@@ -126073,6 +126161,11 @@ var capability6 = {
126073
126161
  };
126074
126162
  var openUrlTool = {
126075
126163
  id: "open-url",
126164
+ interaction: {
126165
+ startedMsg: ({ params }) => `Opening ${safeDestination(params.url)}`,
126166
+ completedMsg: ({ params }) => `Opened ${safeDestination(params.url)}`,
126167
+ failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to open ${safeDestination(params.url)}: ${failureSignal2.error_code}`
126168
+ },
126076
126169
  description: `Open a URL or URL scheme on the device.
126077
126170
  Use to navigate to a web page or deep-link into an app. On Chromium, this navigates the primary renderer to the given URL.
126078
126171
  Cross-platform schemes: https://, tel:, mailto:. iOS also: messages://, settings://, maps://. Android also: geo:, plus any app-specific deep link.
@@ -126369,6 +126462,11 @@ async function tvTargetLongSide(file2, scale) {
126369
126462
  function createScreenshotTool(registry2) {
126370
126463
  return {
126371
126464
  id: "screenshot",
126465
+ interaction: {
126466
+ startedMsg: () => "Capturing screenshot",
126467
+ completedMsg: ({ result }) => `Captured screenshot ${result.image.filename}`,
126468
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to capture screenshot: ${failureSignal2.error_code}`
126469
+ },
126372
126470
  description: `Capture a screenshot of the device screen (iOS simulator, Android emulator, Apple TV simulator, Vega, or Chromium app). Returns { image }; the MCP adapter renders it as a visible image unless the caller passed includeImageInContext: false.
126373
126471
  Use when you need a baseline image before an interaction or to inspect the current screen state after a delay.
126374
126472
  Fails if the simulator-server / emulator backend / Chromium CDP is not reachable for the given device.`,
@@ -126431,6 +126529,11 @@ var zodSchema16 = external_exports.object({
126431
126529
  "Number of taps/clicks dispatched as ONE multi-tap gesture (2 = double-tap / double-click). The taps land inside the OS double-tap window; on Chromium each click carries an escalating CDP clickCount so dblclick actually fires. Default 1."
126432
126530
  )
126433
126531
  });
126532
+ function tapDescription(params, tense) {
126533
+ const count = params.clickCount ?? 1;
126534
+ const action = count === 1 ? tense === "present" ? "Tapping" : "Tapped" : count === 2 ? tense === "present" ? "Double-tapping" : "Double-tapped" : `${tense === "present" ? "Tapping" : "Tapped"} ${count} times`;
126535
+ return `${action} at (${Math.round(params.x * 100)}%, ${Math.round(params.y * 100)}%)`;
126536
+ }
126434
126537
  var capability8 = {
126435
126538
  apple: { simulator: true, device: true },
126436
126539
  appleRemote: { simulator: true },
@@ -126453,6 +126556,11 @@ async function tapChromium(api, x, y, clickCount) {
126453
126556
  }
126454
126557
  var gestureTapTool = {
126455
126558
  id: "gesture-tap",
126559
+ interaction: {
126560
+ startedMsg: ({ params }) => tapDescription(params, "present"),
126561
+ completedMsg: ({ params }) => tapDescription(params, "past"),
126562
+ failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to tap at (${Math.round(params.x * 100)}%, ${Math.round(params.y * 100)}%): ${failureSignal2.error_code}`
126563
+ },
126456
126564
  description: `Press the device screen (iOS simulator, Android emulator, or Chromium app) at normalized coordinates: x and y are fractions of screen width and height in 0.0\u20131.0 (not pixels).
126457
126565
  Sends a Down event followed by an Up event at the same point. For Chromium, this dispatches a CDP mouse-press/release on the renderer.
126458
126566
  Set clickCount: 2 for a double-tap / double-click \u2014 the taps are dispatched as one gesture with proper click counting, which two separate tap calls cannot guarantee.
@@ -126526,6 +126634,11 @@ var capability9 = {
126526
126634
  };
126527
126635
  var gestureSwipeTool = {
126528
126636
  id: "gesture-swipe",
126637
+ interaction: {
126638
+ startedMsg: ({ params }) => `Swiping from (${Math.round(params.fromX * 100)}%, ${Math.round(params.fromY * 100)}%) to (${Math.round(params.toX * 100)}%, ${Math.round(params.toY * 100)}%)`,
126639
+ completedMsg: ({ params }) => `Swiped from (${Math.round(params.fromX * 100)}%, ${Math.round(params.fromY * 100)}%) to (${Math.round(params.toX * 100)}%, ${Math.round(params.toY * 100)}%)`,
126640
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to swipe: ${failureSignal2.error_code}`
126641
+ },
126529
126642
  description: `Execute a smooth swipe / drag touch gesture between two points on the device (iOS simulator or Android emulator). All from/to positions are normalized 0.0\u20131.0 (fractions of screen width/height, not pixels), same as gesture-tap.
126530
126643
  Generates interpolated Move events for a natural feel (~60fps).
126531
126644
  Swipe up (fromY > toY) to scroll content down.
@@ -126608,11 +126721,23 @@ var zodSchema18 = external_exports.object({
126608
126721
  }).refine((p) => (p.deltaX ?? 0) !== 0 || (p.deltaY ?? 0) !== 0, {
126609
126722
  message: "Pass a non-zero deltaX and/or deltaY \u2014 a scroll with no delta is a no-op."
126610
126723
  });
126724
+ function scrollDirection(params) {
126725
+ const horizontal = (params.deltaX ?? 0) > 0 ? "right" : "left";
126726
+ const vertical = (params.deltaY ?? 0) > 0 ? "down" : "up";
126727
+ if ((params.deltaX ?? 0) === 0) return vertical;
126728
+ if ((params.deltaY ?? 0) === 0) return horizontal;
126729
+ return `${vertical} and ${horizontal}`;
126730
+ }
126611
126731
  var capability10 = {
126612
126732
  chromium: { app: true }
126613
126733
  };
126614
126734
  var gestureScrollTool = {
126615
126735
  id: "gesture-scroll",
126736
+ interaction: {
126737
+ startedMsg: ({ params }) => `Scrolling ${scrollDirection(params)}`,
126738
+ completedMsg: ({ params }) => `Scrolled ${scrollDirection(params)}`,
126739
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to scroll: ${failureSignal2.error_code}`
126740
+ },
126616
126741
  description: `Scroll content in a Chromium app by dispatching mouse-wheel events at a point. Anchor x/y are normalized 0.0\u20131.0 (fractions of the window, not pixels), same coordinate space as gesture-tap and describe. Deltas are fractions of the window too: deltaY 0.5 scrolls down half a window; negative scrolls back up.
126617
126742
  Use when content is below/above the fold (describe shows off-screen elements with zero height) or a list needs scrolling. Chromium only \u2014 on iOS/Android use gesture-swipe.
126618
126743
  Returns { scrolled: true, timestampMs }. Fails if the Chromium CDP session is not reachable for the given device.`,
@@ -126657,6 +126782,11 @@ var capability11 = {
126657
126782
  };
126658
126783
  var gestureDragTool = {
126659
126784
  id: "gesture-drag",
126785
+ interaction: {
126786
+ startedMsg: ({ params }) => `Dragging from (${Math.round(params.fromX * 100)}%, ${Math.round(params.fromY * 100)}%) to (${Math.round(params.toX * 100)}%, ${Math.round(params.toY * 100)}%)`,
126787
+ completedMsg: ({ params }) => `Dragged from (${Math.round(params.fromX * 100)}%, ${Math.round(params.fromY * 100)}%) to (${Math.round(params.toX * 100)}%, ${Math.round(params.toY * 100)}%)`,
126788
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to drag: ${failureSignal2.error_code}`
126789
+ },
126660
126790
  description: `Press the left mouse button at a start point, move to an end point, and release \u2014 a desktop mouse drag in a Chromium app. All positions are normalized 0.0\u20131.0 (fractions of the window, not pixels), same coordinate space as gesture-tap and describe. Interpolates mouse-move events at ~60fps over durationMs for a natural drag.
126661
126791
  Use for slider thumbs, drag-and-drop, text selection, or draggable UI elements. Dragging never scrolls content on desktop \u2014 use gesture-scroll for lists/pages. Chromium only \u2014 on iOS/Android use gesture-swipe.
126662
126792
  Returns { dragged: true, timestampMs }. Fails if the Chromium CDP session is not reachable for the given device.`,
@@ -126777,6 +126907,11 @@ var capability12 = {
126777
126907
  };
126778
126908
  var gestureCustomTool = {
126779
126909
  id: "gesture-custom",
126910
+ interaction: {
126911
+ startedMsg: () => "Performing custom gesture",
126912
+ completedMsg: () => "Performed custom gesture",
126913
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to perform custom gesture: ${failureSignal2.error_code}`
126914
+ },
126780
126915
  description: `Send a sequence of touch events for complex gestures.
126781
126916
  Use for: long press, drag-and-drop, custom scroll, pinch (second touch point).
126782
126917
  For simple taps use the gesture-tap tool. For straight-line scrolling use the gesture-swipe tool.
@@ -126852,6 +126987,11 @@ var capability13 = {
126852
126987
  };
126853
126988
  var gesturePinchTool = {
126854
126989
  id: "gesture-pinch",
126990
+ interaction: {
126991
+ startedMsg: ({ params }) => `Pinching ${params.endDistance > params.startDistance ? "out" : "in"} at (${Math.round(params.centerX * 100)}%, ${Math.round(params.centerY * 100)}%)`,
126992
+ completedMsg: ({ params }) => `Pinched ${params.endDistance > params.startDistance ? "out" : "in"} at (${Math.round(params.centerX * 100)}%, ${Math.round(params.centerY * 100)}%)`,
126993
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to pinch: ${failureSignal2.error_code}`
126994
+ },
126855
126995
  description: `Execute a pinch-to-zoom gesture by moving two fingers toward or away from a center point to change the scale of on-screen content. All positions and distances are normalized 0.0\u20131.0 (fractions of screen width/height, not pixels)\u2014same coordinate space as gesture-tap and gesture-swipe.
126856
126996
  startDistance > endDistance = pinch in (zoom out). startDistance < endDistance = pinch out (zoom in).
126857
126997
  Typical values: startDistance 0.2, endDistance 0.6 for a zoom-in pinch at screen center.
@@ -126937,6 +127077,19 @@ var capability14 = {
126937
127077
  };
126938
127078
  var gestureRotateTool = {
126939
127079
  id: "gesture-rotate",
127080
+ interaction: {
127081
+ startedMsg: ({ params }) => {
127082
+ const degrees = Math.abs(params.endAngle - params.startAngle);
127083
+ const direction = params.endAngle > params.startAngle ? "clockwise" : "counterclockwise";
127084
+ return `Rotating gesture ${degrees}\xB0 ${direction}`;
127085
+ },
127086
+ completedMsg: ({ params }) => {
127087
+ const degrees = Math.abs(params.endAngle - params.startAngle);
127088
+ const direction = params.endAngle > params.startAngle ? "clockwise" : "counterclockwise";
127089
+ return `Rotated gesture ${degrees}\xB0 ${direction}`;
127090
+ },
127091
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to rotate gesture: ${failureSignal2.error_code}`
127092
+ },
126940
127093
  description: `Send a two-finger circular arc gesture to rotate on-screen content by a specified angle. Two fingers are placed opposite each other at a fixed radius from the center, then swept from startAngle to endAngle degrees. All positions and radii are normalized 0.0\u20131.0 (fractions of screen width/height, not pixels)\u2014same coordinate space as gesture-tap and gesture-swipe.
126941
127094
  endAngle > startAngle = clockwise rotation. Typical values: radius 0.15, startAngle 0, endAngle 90 for a 90\xB0 clockwise turn. A single radius applies to both axes, so on a non-square screen it traces a physical ellipse (finger separation varies through the turn); pass radiusX+radiusY (fractions of width/height with radiusX\xB7width = radiusY\xB7height) for a physically circular orbit instead.
126942
127095
  Auto-generates interpolated frames at ~60fps.
@@ -127015,6 +127168,11 @@ var capability15 = {
127015
127168
  };
127016
127169
  var buttonTool = {
127017
127170
  id: "button",
127171
+ interaction: {
127172
+ startedMsg: ({ params }) => `Pressing ${params.button} button`,
127173
+ completedMsg: ({ params }) => `Pressed ${params.button} button`,
127174
+ failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to press ${params.button} button: ${failureSignal2.error_code}`
127175
+ },
127018
127176
  description: `Press a device hardware button (iOS simulator, Android emulator or device). iOS sends a Down then Up event automatically; Android injects a single \`adb\` key event.
127019
127177
  Supported buttons depend on the platform: home, back, power, volumeUp, volumeDown, appSwitch, actionButton \u2014 buttons not present on the target platform (e.g. 'back' on iOS, 'actionButton' on Android) are rejected with a clear error.
127020
127178
  Use when you need to trigger hardware button events.
@@ -127616,6 +127774,22 @@ function createKeyboardTool(registry2) {
127616
127774
  });
127617
127775
  return {
127618
127776
  id: "keyboard",
127777
+ interaction: {
127778
+ // Treat both text and key as sensitive. `key` is an unrestricted string at
127779
+ // this boundary, so a value must not reach the event log before execution
127780
+ // validates whether it is a supported named key.
127781
+ startedMsg: ({ params }) => {
127782
+ if (params.text === void 0) return "Pressing a key";
127783
+ if (params.key === void 0) return "Entering text";
127784
+ return "Entering text and pressing a key";
127785
+ },
127786
+ completedMsg: ({ params }) => {
127787
+ if (params.text === void 0) return "Pressed a key";
127788
+ if (params.key === void 0) return "Entered text";
127789
+ return "Entered text and pressed a key";
127790
+ },
127791
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to use keyboard: ${failureSignal2.error_code}`
127792
+ },
127619
127793
  description: `Type text or press special keys on the device (iOS simulator, Android emulator or device, Chromium app, Vega Virtual Device, or Apple TV / Android TV) using keyboard events.
127620
127794
  Use when you need to enter text or trigger a named key such as enter, escape, or arrow keys. On Vega and Apple TV / Android TV, prefer the remote tools for D-pad navigation; use keyboard to type into a focused text field (e.g. a search or login box).
127621
127795
  Returns { typed: string, keys: number }. Fails if an unsupported key name is provided or the device's input backend is not reachable.
@@ -127657,6 +127831,11 @@ var capability17 = {
127657
127831
  };
127658
127832
  var rotateTool = {
127659
127833
  id: "rotate",
127834
+ interaction: {
127835
+ startedMsg: ({ params }) => `Rotating device to ${params.orientation}`,
127836
+ completedMsg: ({ params }) => `Rotated device to ${params.orientation}`,
127837
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to rotate device: ${failureSignal2.error_code}`
127838
+ },
127660
127839
  description: `Set the device orientation to Portrait, LandscapeLeft, LandscapeRight, or PortraitUpsideDown.
127661
127840
  Use to test layout in a different orientation. Re-run \`describe\` afterwards \u2014 frame coordinates change with the orientation.
127662
127841
  Returns { orientation }. Fails if the target device is not booted.`,
@@ -127776,6 +127955,11 @@ var capability18 = {
127776
127955
  function createTvRemoteTool(registry2) {
127777
127956
  return {
127778
127957
  id: "tv-remote",
127958
+ interaction: {
127959
+ startedMsg: ({ params }) => `Pressing ${params.button} on TV remote${(params.repeat ?? 1) > 1 ? ` ${params.repeat} times` : ""}`,
127960
+ completedMsg: ({ params }) => `Pressed ${params.button} on TV remote${(params.repeat ?? 1) > 1 ? ` ${params.repeat} times` : ""}`,
127961
+ failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to press ${params.button} on TV remote: ${failureSignal2.error_code}`
127962
+ },
127779
127963
  description: `Press a TV remote / D-pad button (or a whole path of them) on a TV device \u2014 Apple TV (tvOS), Android TV (leanback), or Vega (Fire TV).
127780
127964
  A TV is navigated with a directional remote, not touch \u2014 use this instead of gesture-tap/swipe (which do not apply on a TV). Move focus with up/down/left/right, confirm with select, go back with back/menu, exit with home, and use playPause/rewind/fastForward/next/previous/volumeUp/volumeDown/mute for the corresponding remote keys. (On the Apple TV simulator the media-transport and volume keys are rejected \u2014 its HID stack ignores them; they work on Android TV and Vega.)
127781
127965
  Single press: { button: "down" }. Repeat the same button: { button: "down", repeat: 3 }.
@@ -133654,6 +133838,9 @@ function textMatches(actual, expected, mode) {
133654
133838
  }
133655
133839
  return mode === "equals" ? equalsCI(actual, expected) : includesCI(actual, expected);
133656
133840
  }
133841
+ function hasOwnConstraint(selector) {
133842
+ return selector.text !== void 0 || selector.textMatches !== void 0 || selector.identifier !== void 0 || selector.role !== void 0;
133843
+ }
133657
133844
  function matchNodeWithRegex(node, selector, textRegex) {
133658
133845
  if (selector.text !== void 0) {
133659
133846
  if (!includesCI(node.label, selector.text) && !includesCI(node.value, selector.text)) {
@@ -133676,15 +133863,111 @@ function matchNodeWithRegex(node, selector, textRegex) {
133676
133863
  function selectorTextRegex(selector) {
133677
133864
  return selector.textMatches === void 0 ? void 0 : uiTreeMatchInternals.createRegExp(selector.textMatches);
133678
133865
  }
133679
- function collectMatches(node, selector, textRegex, acc) {
133680
- if (matchNodeWithRegex(node, selector, textRegex)) acc.push(node);
133681
- for (const child of node.children) collectMatches(child, selector, textRegex, acc);
133866
+ var WITHIN_EPS = 5e-3;
133867
+ function frameWithin(inner, outer) {
133868
+ return inner.x >= outer.x - WITHIN_EPS && inner.y >= outer.y - WITHIN_EPS && inner.x + inner.width <= outer.x + outer.width + WITHIN_EPS && inner.y + inner.height <= outer.y + outer.height + WITHIN_EPS;
133869
+ }
133870
+ var CONTAINMENT_GRID_N = 16;
133871
+ var CONTAINMENT_GRID_MIN = 32;
133872
+ function gridCell(coord) {
133873
+ const c = Math.floor(coord * CONTAINMENT_GRID_N);
133874
+ return c < 0 ? 0 : c >= CONTAINMENT_GRID_N ? CONTAINMENT_GRID_N - 1 : c;
133875
+ }
133876
+ function containmentTester(containers) {
133877
+ if (containers.length < CONTAINMENT_GRID_MIN) {
133878
+ return (node) => containers.some((c) => c !== node && frameWithin(node.frame, c.frame));
133879
+ }
133880
+ const cells = /* @__PURE__ */ new Map();
133881
+ for (const c of containers) {
133882
+ const f = c.frame;
133883
+ const colEnd = gridCell(f.x + f.width + WITHIN_EPS);
133884
+ const rowEnd = gridCell(f.y + f.height + WITHIN_EPS);
133885
+ for (let row = gridCell(f.y - WITHIN_EPS); row <= rowEnd; row++) {
133886
+ for (let col = gridCell(f.x - WITHIN_EPS); col <= colEnd; col++) {
133887
+ const key = row * CONTAINMENT_GRID_N + col;
133888
+ const bucket = cells.get(key);
133889
+ if (bucket) bucket.push(c);
133890
+ else cells.set(key, [c]);
133891
+ }
133892
+ }
133893
+ }
133894
+ return (node) => {
133895
+ const bucket = cells.get(gridCell(node.frame.y) * CONTAINMENT_GRID_N + gridCell(node.frame.x));
133896
+ return bucket !== void 0 && bucket.some((c) => c !== node && frameWithin(node.frame, c.frame));
133897
+ };
133898
+ }
133899
+ function frameAbove(a, b) {
133900
+ return a.y + a.height <= b.y + WITHIN_EPS;
133901
+ }
133902
+ function followKind(node, anchor) {
133903
+ const below = frameAbove(anchor, node);
133904
+ const above = frameAbove(node, anchor);
133905
+ if (below !== above) return below ? "below" : "no";
133906
+ const right = anchor.x + anchor.width <= node.x + WITHIN_EPS;
133907
+ const left = node.x + node.width <= anchor.x + WITHIN_EPS;
133908
+ return right !== left && right ? "band" : "no";
133909
+ }
133910
+ function frameAfter(node, anchor) {
133911
+ return followKind(node, anchor) !== "no";
133912
+ }
133913
+ function afterTester(anchors) {
133914
+ return (node) => anchors.some((a) => a !== node && frameAfter(node.frame, a.frame));
133915
+ }
133916
+ function comparePick(a, b) {
133917
+ return frameArea(a) - frameArea(b) || a.width - b.width || a.height - b.height;
133918
+ }
133919
+ function compareBandPick(a, b) {
133920
+ return a.x - b.x || a.y - b.y || comparePick(a, b);
133921
+ }
133922
+ function compareBelowPick(a, b) {
133923
+ return a.y - b.y || a.x - b.x || comparePick(a, b);
133924
+ }
133925
+ function nearestAfter(candidates, anchors) {
133926
+ if (candidates.length === 0 || anchors.length === 0) return [];
133927
+ const picked = /* @__PURE__ */ new Set();
133928
+ for (const anchor of anchors) {
133929
+ const af = anchor.frame;
133930
+ let band;
133931
+ let below;
133932
+ for (const c of candidates) {
133933
+ if (c === anchor) continue;
133934
+ const f = c.frame;
133935
+ const kind = followKind(f, af);
133936
+ if (kind === "band") {
133937
+ if (band === void 0 || compareBandPick(f, band.frame) < 0) band = c;
133938
+ } else if (kind === "below") {
133939
+ if (below === void 0 || compareBelowPick(f, below.frame) < 0) below = c;
133940
+ }
133941
+ }
133942
+ const best = band ?? below;
133943
+ if (best !== void 0) picked.add(best);
133944
+ }
133945
+ return candidates.filter((c) => picked.has(c));
133682
133946
  }
133683
133947
  function findAll(root2, selector) {
133684
- const acc = [];
133685
- const textRegex = selectorTextRegex(selector);
133686
- for (const child of root2.children) collectMatches(child, selector, textRegex, acc);
133687
- return acc;
133948
+ const all = [];
133949
+ const collect = (node) => {
133950
+ all.push(node);
133951
+ for (const child of node.children) collect(child);
133952
+ };
133953
+ for (const child of root2.children) collect(child);
133954
+ return resolveSelector(all, selector);
133955
+ }
133956
+ var SELECTOR_RELATIONS = ["within", "after", "next"];
133957
+ var RELATION_RESOLVERS = {
133958
+ within: (matches2, scope) => matches2.filter(containmentTester(scope)),
133959
+ after: (matches2, scope) => matches2.filter(afterTester(scope)),
133960
+ next: (matches2, scope) => nearestAfter(matches2, scope)
133961
+ };
133962
+ function resolveSelector(all, selector) {
133963
+ const regex = selectorTextRegex(selector);
133964
+ let matches2 = all.filter((n) => matchNodeWithRegex(n, selector, regex));
133965
+ for (const relation of SELECTOR_RELATIONS) {
133966
+ const scope = selector[relation];
133967
+ if (scope === void 0) continue;
133968
+ matches2 = RELATION_RESOLVERS[relation](matches2, resolveSelector(all, scope));
133969
+ }
133970
+ return matches2;
133688
133971
  }
133689
133972
  function isVisible(node) {
133690
133973
  return node.frame.width > 0 && node.frame.height > 0;
@@ -133765,6 +134048,13 @@ function exactFieldCount(node, selector, fullTextRegex) {
133765
134048
  function selectorToFrame(root2, selector) {
133766
134049
  const visible = findAll(root2, selector).filter(isVisible);
133767
134050
  if (visible.length === 0) return void 0;
134051
+ if (!hasOwnConstraint(selector)) {
134052
+ let first;
134053
+ for (const n of visible) {
134054
+ if (first === void 0 || compareBelowPick(n.frame, first.frame) < 0) first = n;
134055
+ }
134056
+ return first?.frame;
134057
+ }
133768
134058
  const fullTextRegex = fullConsumptionRegex(selector);
133769
134059
  let best;
133770
134060
  let bestExact = -1;
@@ -133850,6 +134140,18 @@ var zodSchema27 = external_exports.object({
133850
134140
  message: "condition `text` requires expectedText",
133851
134141
  path: ["expectedText"]
133852
134142
  });
134143
+ var conditionDescription = {
134144
+ exists: "appear",
134145
+ visible: "become visible",
134146
+ hidden: "become hidden",
134147
+ text: "match expected text"
134148
+ };
134149
+ var conditionCompleted = {
134150
+ exists: "UI element appeared",
134151
+ visible: "UI element became visible",
134152
+ hidden: "UI element became hidden",
134153
+ text: "UI element matched expected text"
134154
+ };
133853
134155
  var capability19 = {
133854
134156
  apple: { simulator: true, device: true },
133855
134157
  android: { emulator: true, device: true, unknown: true },
@@ -133911,6 +134213,11 @@ function createAwaitUiElementTool(registry2) {
133911
134213
  }
133912
134214
  return {
133913
134215
  id: AWAIT_UI_ELEMENT_TOOL_ID,
134216
+ interaction: {
134217
+ startedMsg: ({ params }) => `Waiting for UI element to ${conditionDescription[params.condition]}`,
134218
+ completedMsg: ({ params }) => conditionCompleted[params.condition],
134219
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed while waiting for UI element: ${failureSignal2.error_code}`
134220
+ },
133914
134221
  description: `Block until a UI element reaches an expected state or a timeout elapses, so you don't have to poll screenshot/describe yourself.
133915
134222
 
133916
134223
  Conditions:
@@ -134041,6 +134348,11 @@ var capability20 = {
134041
134348
  function createRunSequenceTool(registry2) {
134042
134349
  return {
134043
134350
  id: "run-sequence",
134351
+ interaction: {
134352
+ startedMsg: ({ params }) => `Running ${params.steps.length}-step interaction sequence`,
134353
+ completedMsg: ({ params }) => `Ran ${params.steps.length}-step interaction sequence`,
134354
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to run interaction sequence: ${failureSignal2.error_code}`
134355
+ },
134044
134356
  description: `Execute multiple device interaction steps in a single call (iOS simulator, Android emulator, Apple TV / Android TV, or Chromium app).
134045
134357
  Use when you need sequential actions and do NOT need to observe the screen between them
134046
134358
  (e.g. scrolling multiple times, typing then pressing enter, rotating back and forth).
@@ -134201,6 +134513,11 @@ var zodSchema29 = external_exports.object({
134201
134513
  });
134202
134514
  var debuggerConnectTool = {
134203
134515
  id: "debugger-connect",
134516
+ interaction: {
134517
+ startedMsg: () => "Connecting JavaScript debugger",
134518
+ completedMsg: ({ result }) => `Connected JavaScript debugger to ${result.appName || result.deviceName}`,
134519
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to connect JavaScript debugger: ${failureSignal2.error_code}`
134520
+ },
134204
134521
  description: `Connect to a JS runtime CDP debugger.
134205
134522
  iOS / Android / Vega: connects to Metro's CDP endpoint on the given port. Chromium: re-uses the page CDP session opened by boot-device \u2014 port is ignored.
134206
134523
  Returns connection info including port, projectRoot (empty on Chromium and on legacy Metro, e.g. Vega), deviceName, appName, logicalDeviceId (absent on Vega), and isNewDebugger. If already connected, returns the existing connection.
@@ -134234,6 +134551,11 @@ var zodSchema30 = external_exports.object({
134234
134551
  });
134235
134552
  var debuggerStatusTool = {
134236
134553
  id: "debugger-status",
134554
+ interaction: {
134555
+ startedMsg: () => "Checking JavaScript debugger",
134556
+ completedMsg: () => "Checked JavaScript debugger",
134557
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to check JavaScript debugger: ${failureSignal2.error_code}`
134558
+ },
134237
134559
  description: `Get JS runtime debugger connection status and diagnostic info.
134238
134560
  Use when you need to verify connectivity before using other debugger tools. Returns port, projectRoot (empty on Chromium and on legacy Metro, e.g. Vega), deviceName, appName, logicalDeviceId (absent on Vega), isNewDebugger (false on the legacy inspector), connected flag, loadedScripts count, and sourceMapReady (always true \u2014 waits for pending source maps before returning; no-op on Chromium). Fails if the runtime is unreachable.`,
134239
134561
  zodSchema: zodSchema30,
@@ -134270,6 +134592,11 @@ var zodSchema31 = external_exports.object({
134270
134592
  });
134271
134593
  var debuggerEvaluateTool = {
134272
134594
  id: "debugger-evaluate",
134595
+ interaction: {
134596
+ startedMsg: () => "Running JavaScript in the app",
134597
+ completedMsg: () => "Ran JavaScript in the app",
134598
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to run JavaScript in the app: ${failureSignal2.error_code}`
134599
+ },
134273
134600
  description: `Execute arbitrary JavaScript in the app's JS runtime via CDP \u2014 Hermes on iOS / Android / Vega, V8 on Chromium.
134274
134601
  Returns the evaluation result as a JSON-serializable value, along with deviceName, appName, and logicalDeviceId for context. Use when you need to read app state, call app functions, or test logic at runtime. The result is serialized by value, so cyclic objects (many RN runtime values \u2014 fiber nodes, navigation refs, global \u2014 are cyclic) fail with a serialization error rather than returning silently. Fails if the expression throws or the runtime is not connected.`,
134275
134602
  zodSchema: zodSchema31,
@@ -134300,6 +134627,11 @@ var zodSchema32 = external_exports.object({
134300
134627
  });
134301
134628
  var debuggerReloadMetroTool = {
134302
134629
  id: "debugger-reload-metro",
134630
+ interaction: {
134631
+ startedMsg: () => "Reloading the app through Metro",
134632
+ completedMsg: () => "Reloaded the app through Metro",
134633
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to reload the app through Metro: ${failureSignal2.error_code}`
134634
+ },
134303
134635
  description: `Restart the Metro JS bundle in the connected React Native app without restarting the native process.
134304
134636
  Use when you want to apply code changes or reset JS state. Returns { reloaded, port, method, deviceName, appName, logicalDeviceId } indicating which reload path was used and which device/app was targeted. Fails if Metro is not running on the given port.`,
134305
134637
  zodSchema: zodSchema32,
@@ -135127,6 +135459,11 @@ var zodSchema33 = external_exports.object({
135127
135459
  });
135128
135460
  var debuggerComponentTreeTool = {
135129
135461
  id: "debugger-component-tree",
135462
+ interaction: {
135463
+ startedMsg: () => "Reading React component tree",
135464
+ completedMsg: () => "Read React component tree",
135465
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to read React component tree: ${failureSignal2.error_code}`
135466
+ },
135130
135467
  description: `Fetch the current screen of a running React Native app as a compact component text tree.
135131
135468
  Only shows on-screen components with unique positions \u2014 off-screen (scrolled) content,
135132
135469
  full-screen transparent wrappers, and implementation-detail components are pruned.
@@ -135637,6 +135974,11 @@ var zodSchema34 = external_exports.object({
135637
135974
  });
135638
135975
  var debuggerInspectElementTool = {
135639
135976
  id: "debugger-inspect-element",
135977
+ interaction: {
135978
+ startedMsg: ({ params }) => `Inspecting element at (${params.x}, ${params.y})`,
135979
+ completedMsg: ({ params }) => `Inspected element at (${params.x}, ${params.y})`,
135980
+ failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to inspect element at (${params.x}, ${params.y}): ${failureSignal2.error_code}`
135981
+ },
135640
135982
  description: `Inspect the React component hierarchy at a screen coordinate (x, y).
135641
135983
  Returns components from the tapped element upward through its parent hierarchy,
135642
135984
  each with its source file:line and a code fragment.
@@ -135759,6 +136101,11 @@ var zodSchema35 = external_exports.object({
135759
136101
  });
135760
136102
  var debuggerLogRegistryTool = {
135761
136103
  id: "debugger-log-registry",
136104
+ interaction: {
136105
+ startedMsg: () => "Reading app logs",
136106
+ completedMsg: () => "Read app logs",
136107
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to read app logs: ${failureSignal2.error_code}`
136108
+ },
135762
136109
  description: `Get a summary of all console logs captured from the app's JS runtime.
135763
136110
  Returns the log file path, entry counts by level, and message clusters (grouped by similarity). Works against Hermes (iOS / Android / Vega) and V8 (Chromium).
135764
136111
  Use when investigating warnings, errors, or unexpected output \u2014 call this first for an overview, then read the returned file for details. Returns empty stats if no log data has been captured yet.`,
@@ -135851,6 +136198,11 @@ var zodSchema36 = external_exports.object({
135851
136198
  });
135852
136199
  var networkLogsTool = {
135853
136200
  id: "view-network-logs",
136201
+ interaction: {
136202
+ startedMsg: () => "Reading network activity",
136203
+ completedMsg: () => "Read network activity",
136204
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to read network activity: ${failureSignal2.error_code}`
136205
+ },
135854
136206
  description: `Retrieve captured network (HTTP) requests from the running app.
135855
136207
  Returns a paginated list of requests with method, URL, status, resource type, size, and duration.
135856
136208
  Each entry includes a requestId that can be passed to view-network-request-details for full details.
@@ -135938,6 +136290,11 @@ var zodSchema37 = external_exports.object({
135938
136290
  });
135939
136291
  var networkRequestTool = {
135940
136292
  id: "view-network-request-details",
136293
+ interaction: {
136294
+ startedMsg: ({ params }) => `Reading network request ${params.requestId}`,
136295
+ completedMsg: ({ params }) => `Read network request ${params.requestId}`,
136296
+ failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to read network request ${params.requestId}: ${failureSignal2.error_code}`
136297
+ },
135941
136298
  description: `Get full details of a specific network request by its requestId (from view-network-logs).
135942
136299
  Returns request/response headers (sensitive headers redacted), status, timing, and optionally the response body.
135943
136300
  Large response bodies are truncated. Use when you need headers, body, or timing for a specific request after listing logs.
@@ -136310,6 +136667,11 @@ function makeDescribeExecute(registry2) {
136310
136667
  function createDescribeTool(registry2) {
136311
136668
  return {
136312
136669
  id: "describe",
136670
+ interaction: {
136671
+ startedMsg: () => "Reading screen",
136672
+ completedMsg: () => "Read screen",
136673
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to read screen: ${failureSignal2.error_code}`
136674
+ },
136313
136675
  description: `Get the accessibility / DOM element tree for the current screen.
136314
136676
  On iOS, uses the AXRuntime accessibility service to inspect whatever is currently visible \u2014 including
136315
136677
  system dialogs, permission prompts, and any foreground app content. On Android, runs \`uiautomator dump\`.
@@ -136402,6 +136764,11 @@ function createAwaitScreenIdleTool(registry2) {
136402
136764
  }
136403
136765
  return {
136404
136766
  id: AWAIT_SCREEN_IDLE_TOOL_ID,
136767
+ interaction: {
136768
+ startedMsg: () => "Waiting for screen to settle",
136769
+ completedMsg: ({ result }) => result.settled ? "Screen settled" : "Screen did not settle before timeout",
136770
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed while waiting for screen to settle: ${failureSignal2.error_code}`
136771
+ },
136405
136772
  description: `Block until the screen has rendered content and stopped changing, or a timeout elapses.
136406
136773
 
136407
136774
  Polls the same accessibility / DOM tree as \`describe\` every pollIntervalMs (default ${DEFAULT_POLL_INTERVAL_MS2}ms) until it
@@ -136527,6 +136894,11 @@ function safeGetState(registry2, urn) {
136527
136894
  function createReactProfilerStartTool(registry2) {
136528
136895
  return {
136529
136896
  id: "react-profiler-start",
136897
+ interaction: {
136898
+ startedMsg: () => "Starting React profiler",
136899
+ completedMsg: ({ result }) => result.already_running ? "React profiler was already running" : "Started React profiler",
136900
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to start React profiler: ${failureSignal2.error_code}`
136901
+ },
136530
136902
  description: `Start CPU profiling + React commit capture on the connected Hermes runtime.
136531
136903
  Delegates React commit capture to the in-app React DevTools backend (ri.startProfiling).
136532
136904
  If another tool-server already owns the session, returns { already_running: true, owner, stale, how_to_reclaim } without clobbering their data. Pass { force: true } to reclaim a fresh owner's session, but BEFORE OVERTAKING - ask the user for approval first, see relevant skill for guidance.
@@ -136884,6 +137256,11 @@ function flattenProfilingData(merged, displayNameById, fiberMeta) {
136884
137256
  function createReactProfilerStopTool(registry2) {
136885
137257
  return {
136886
137258
  id: "react-profiler-stop",
137259
+ interaction: {
137260
+ startedMsg: () => "Stopping React profiler",
137261
+ completedMsg: () => "Stopped React profiler",
137262
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to stop React profiler: ${failureSignal2.error_code}`
137263
+ },
136887
137264
  description: `Stop CPU profiling and collect the cpuProfile + React commit tree.
136888
137265
  Reads commit data from the in-app React DevTools backend.
136889
137266
  Stores results in the ReactProfilerSession for later use by react-profiler-analyze or react-profiler-cpu-summary.
@@ -137128,6 +137505,11 @@ var zodSchema42 = external_exports.object({
137128
137505
  function createReactProfilerStatusTool(registry2) {
137129
137506
  return {
137130
137507
  id: "react-profiler-status",
137508
+ interaction: {
137509
+ startedMsg: () => "Checking React profiler",
137510
+ completedMsg: ({ result }) => result.is_running ? "React profiler is running" : "React profiler is stopped",
137511
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to check React profiler: ${failureSignal2.error_code}`
137512
+ },
137131
137513
  description: `Check the state of the React profiler session without side effects. Use after an interruption (debugger disconnect, unexpected error, agent pause) to decide whether to continue with react-profiler-stop, start a new session, or reconnect the debugger. Ownership is verified server-side against this tool-server's in-memory session \u2014 no token-threading is required. Returns { session_status, is_running, current_owner, \u2026 }. If this tool-server process restarted after react-profiler-start, status will report 'taken_over'; use react-profiler-start { force: true } to reclaim.`,
137132
137514
  zodSchema: zodSchema42,
137133
137515
  // RN-only: companion to react-profiler-start.
@@ -138638,6 +139020,11 @@ var zodSchema43 = external_exports.object({
138638
139020
  });
138639
139021
  var reactProfilerAnalyzeTool = {
138640
139022
  id: "react-profiler-analyze",
139023
+ interaction: {
139024
+ startedMsg: () => "Analyzing React profile",
139025
+ completedMsg: () => "Analyzed React profile",
139026
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to analyze React profile: ${failureSignal2.error_code}`
139027
+ },
138641
139028
  description: `Analyze stored profiling data and return a markdown performance report.
138642
139029
  Returns { report, reportFile, hotCommitsTotal, hotCommitsShown, sessionFiles }.
138643
139030
  The report is structured around hot React commits (\u226516ms absolute floor) with per-commit
@@ -138798,6 +139185,11 @@ var fileInputs = [
138798
139185
  ];
138799
139186
  var reactProfilerComponentSourceTool = {
138800
139187
  id: "react-profiler-component-source",
139188
+ interaction: {
139189
+ startedMsg: ({ params }) => `Finding source for ${params.component_name}`,
139190
+ completedMsg: ({ params, result }) => `${result.found ? "Found" : "Could not find"} source for ${params.component_name}`,
139191
+ failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to find source for ${params.component_name}: ${failureSignal2.error_code}`
139192
+ },
138801
139193
  description: `Find a React component's source via tree-sitter AST lookup: returns file path, line number, memoization status (isMemoized, hasUseCallback, hasUseMemo), and 50 lines of source for a named React component.
138802
139194
  Call this per-finding after react-profiler-analyze to inspect source before proposing a fix.
138803
139195
  Returns found: false if the component is not found in user-owned code (e.g. lives in node_modules).
@@ -138926,6 +139318,11 @@ function renderMarkdownTable(entries) {
138926
139318
  }
138927
139319
  var reactProfilerCpuSummaryTool = {
138928
139320
  id: "react-profiler-cpu-summary",
139321
+ interaction: {
139322
+ startedMsg: () => "Summarizing React CPU profile",
139323
+ completedMsg: () => "Summarized React CPU profile",
139324
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to summarize React CPU profile: ${failureSignal2.error_code}`
139325
+ },
138929
139326
  description: `Return a raw Hermes CPU flamegraph summary (top hotspot functions by self-time).
138930
139327
  FOR DEDICATED CPU INVESTIGATION ONLY \u2014 do NOT call this as part of a normal profiling session.
138931
139328
  Use react-profiler-analyze instead; it covers all React rendering performance analysis.
@@ -139040,6 +139437,11 @@ var zodSchema46 = external_exports.object({
139040
139437
  });
139041
139438
  var reactProfilerRendersTool = {
139042
139439
  id: "react-profiler-renders",
139440
+ interaction: {
139441
+ startedMsg: () => "Reading React render activity",
139442
+ completedMsg: () => "Read React render activity",
139443
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to read React render activity: ${failureSignal2.error_code}`
139444
+ },
139043
139445
  description: `Scan the live React fiber tree to collect component render counts and durations.
139044
139446
  Returns a markdown table of the top re-rendering components. No profiling session required \u2014 works on a live connected app.
139045
139447
  Use when you want a quick snapshot of render counts without a full profiling session.
@@ -139211,6 +139613,11 @@ var zodSchema47 = external_exports.object({
139211
139613
  });
139212
139614
  var reactProfilerFiberTreeTool = {
139213
139615
  id: "react-profiler-fiber-tree",
139616
+ interaction: {
139617
+ startedMsg: () => "Reading React fiber tree",
139618
+ completedMsg: () => "Read React fiber tree",
139619
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to read React fiber tree: ${failureSignal2.error_code}`
139620
+ },
139214
139621
  description: `Inspect the React fiber tree and return a JSON representation of the component hierarchy.
139215
139622
  Use when tracing ancestry of a library component or checking for useMemoCache hook (confirms React Compiler is active on a component).
139216
139623
  Returns a nested JSON tree of fiber nodes with name, tag, actualDuration, selfBaseDuration, and children.
@@ -142899,6 +143306,11 @@ var capability23 = {
142899
143306
  };
142900
143307
  var nativeProfilerStartTool = {
142901
143308
  id: "native-profiler-start",
143309
+ interaction: {
143310
+ startedMsg: () => "Starting native profiler",
143311
+ completedMsg: () => "Started native profiler",
143312
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to start native profiler: ${failureSignal2.error_code}`
143313
+ },
142902
143314
  capability: capability23,
142903
143315
  description: `Start native profiling on a booted device. iOS: Instruments via xctrace (CPU, hangs, memory). Android: Perfetto (CPU, jank, RSS-growth weak signal).
142904
143316
  Auto-detects the running app process unless app_process is explicitly provided.
@@ -142944,6 +143356,11 @@ function registerTrace(store, traceFile) {
142944
143356
  }
142945
143357
  var nativeProfilerStopTool = {
142946
143358
  id: "native-profiler-stop",
143359
+ interaction: {
143360
+ startedMsg: () => "Stopping native profiler",
143361
+ completedMsg: ({ result }) => `Saved native profile ${result.traceFile.filename}`,
143362
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to stop native profiler: ${failureSignal2.error_code}`
143363
+ },
142947
143364
  capability: capability24,
142948
143365
  // Packaging plus the export passes routinely exceed the 30s fetch timeout.
142949
143366
  longRunning: true,
@@ -142997,6 +143414,11 @@ var capability25 = {
142997
143414
  };
142998
143415
  var nativeProfilerAnalyzeTool = {
142999
143416
  id: "native-profiler-analyze",
143417
+ interaction: {
143418
+ startedMsg: () => "Analyzing native profile",
143419
+ completedMsg: () => "Analyzed native profile",
143420
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to analyze native profile: ${failureSignal2.error_code}`
143421
+ },
143000
143422
  capability: capability25,
143001
143423
  description: `Analyze exported native trace data and return an LLM-optimized markdown report.
143002
143424
  iOS: parses CPU time profile, UI hangs, and memory leaks from the exported XML files.
@@ -143989,6 +144411,11 @@ var capability26 = {
143989
144411
  function createScreenRecordingStartTool(registry2) {
143990
144412
  return {
143991
144413
  id: "screen-recording-start",
144414
+ interaction: {
144415
+ startedMsg: () => "Starting screen recording",
144416
+ completedMsg: () => "Started screen recording",
144417
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to start screen recording: ${failureSignal2.error_code}`
144418
+ },
143992
144419
  capability: capability26,
143993
144420
  description: `Start recording the device screen to a video file (h264 mp4, 30fps at the device's native resolution).
143994
144421
  By default stretches where the screen does not change are trimmed out (see trimStatic), so a long session with only brief activity comes back as a short clip instead of minutes of dead air.
@@ -144090,6 +144517,11 @@ var capability27 = {
144090
144517
  };
144091
144518
  var screenRecordingStopTool = {
144092
144519
  id: "screen-recording-stop",
144520
+ interaction: {
144521
+ startedMsg: () => "Stopping screen recording",
144522
+ completedMsg: ({ result }) => `Saved screen recording ${result.video.filename}`,
144523
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to stop screen recording: ${failureSignal2.error_code}`
144524
+ },
144093
144525
  capability: capability27,
144094
144526
  description: `Stop the screen recording started by \`screen-recording-start\` and retrieve the video: frame capture ends and ffmpeg finalizes the mp4.
144095
144527
  Also retrieves the video when the recording already ended on its own (time limit reached, capture process died) \u2014 call it even after the cap fired.
@@ -144356,6 +144788,11 @@ function shortenUrl(url2) {
144356
144788
  }
144357
144789
  var profilerCpuQueryTool = {
144358
144790
  id: "profiler-cpu-query",
144791
+ interaction: {
144792
+ startedMsg: ({ params }) => `Querying CPU profile by ${params.mode.replaceAll("_", " ")}`,
144793
+ completedMsg: ({ params }) => `Queried CPU profile by ${params.mode.replaceAll("_", " ")}`,
144794
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to query CPU profile: ${failureSignal2.error_code}`
144795
+ },
144359
144796
  description: `Query Hermes CPU profile data with targeted modes for iterative investigation.
144360
144797
  Requires react-profiler-stop (and ideally react-profiler-analyze) to have been called first.
144361
144798
  Modes:
@@ -144684,6 +145121,11 @@ function getTopComponents(entries, topN2) {
144684
145121
  }
144685
145122
  var profilerCommitQueryTool = {
144686
145123
  id: "profiler-commit-query",
145124
+ interaction: {
145125
+ startedMsg: ({ params }) => `Querying React commits ${params.mode.replace(/^by_/, "by ").replaceAll("_", " ")}`,
145126
+ completedMsg: ({ params }) => `Queried React commits ${params.mode.replace(/^by_/, "by ").replaceAll("_", " ")}`,
145127
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to query React commits: ${failureSignal2.error_code}`
145128
+ },
144687
145129
  description: `Query React commit data for iterative investigation of render performance.
144688
145130
  Requires react-profiler-stop to have been called first.
144689
145131
  Modes:
@@ -144777,6 +145219,12 @@ var zodSchema55 = external_exports.object({
144777
145219
  object_type: external_exports.string().optional().describe("Object type filter for leak_stacks mode"),
144778
145220
  top_n: external_exports.coerce.number().int().positive().default(15).describe("Max results to return (default 15)")
144779
145221
  });
145222
+ var stackQueryMode = {
145223
+ hang_stacks: "hang stacks",
145224
+ function_callers: "function callers",
145225
+ thread_breakdown: "thread breakdown",
145226
+ leak_stacks: "leak stacks"
145227
+ };
144780
145228
  function getIosParsedData(api) {
144781
145229
  if (!api.parsedData) {
144782
145230
  throw new FailureError(
@@ -145061,6 +145509,11 @@ async function executeAndroid(api, params) {
145061
145509
  }
145062
145510
  var profilerStackQueryTool = {
145063
145511
  id: "profiler-stack-query",
145512
+ interaction: {
145513
+ startedMsg: ({ params }) => `Querying native stacks by ${stackQueryMode[params.mode]}`,
145514
+ completedMsg: ({ params }) => `Queried native stacks by ${stackQueryMode[params.mode]}`,
145515
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to query native stacks: ${failureSignal2.error_code}`
145516
+ },
145064
145517
  description: `Query native profiler trace data for iterative investigation of native performance.
145065
145518
  Requires native-profiler-stop \u2192 native-profiler-analyze to have been called first.
145066
145519
  Modes:
@@ -145133,6 +145586,11 @@ var zodSchema56 = external_exports.object({
145133
145586
  });
145134
145587
  var profilerCombinedReportTool = {
145135
145588
  id: "profiler-combined-report",
145589
+ interaction: {
145590
+ startedMsg: () => "Building combined performance report",
145591
+ completedMsg: () => "Built combined performance report",
145592
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to build combined performance report: ${failureSignal2.error_code}`
145593
+ },
145136
145594
  description: `Generate a cross-correlated report combining React Profiler and native profiler data.
145137
145595
  Maps native hangs to React commits using wall-clock time alignment.
145138
145596
  Requires both react-profiler-analyze and native-profiler-analyze to have been called first.
@@ -145779,8 +146237,30 @@ async function loadNativeSession(debugDir, sessionId, api, appProcessOverride) {
145779
146237
  ];
145780
146238
  return lines.join("\n");
145781
146239
  }
146240
+ var profilerLoadAction = {
146241
+ list: {
146242
+ started: () => "Listing saved profiles",
146243
+ completed: () => "Listed saved profiles",
146244
+ failure: "list"
146245
+ },
146246
+ load_react: {
146247
+ started: (params) => `Loading React profile ${params.session_id}`,
146248
+ completed: (params) => `Loaded React profile ${params.session_id}`,
146249
+ failure: "load"
146250
+ },
146251
+ load_native: {
146252
+ started: (params) => `Loading native profile ${params.session_id}`,
146253
+ completed: (params) => `Loaded native profile ${params.session_id}`,
146254
+ failure: "load"
146255
+ }
146256
+ };
145782
146257
  var profilerLoadTool = {
145783
146258
  id: "profiler-load",
146259
+ interaction: {
146260
+ startedMsg: ({ params }) => profilerLoadAction[params.mode].started(params),
146261
+ completedMsg: ({ params }) => profilerLoadAction[params.mode].completed(params),
146262
+ failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to ${profilerLoadAction[params.mode].failure} saved profiles: ${failureSignal2.error_code}`
146263
+ },
145784
146264
  description: `Fetch and restore a previously captured profiling session from disk into memory so query tools can operate on it.
145785
146265
  This is the disk-restore counterpart to react-profiler-stop/native-profiler-stop, which write data, and to the query tools (profiler-cpu-query, profiler-commit-query, profiler-stack-query), which read it.
145786
146266
  Use when you need to revisit past session data without capturing a new recording.
@@ -145859,6 +146339,11 @@ var zodSchema58 = external_exports.object({
145859
146339
  function createStopSimulatorServerTool(registry2) {
145860
146340
  return {
145861
146341
  id: "stop-simulator-server",
146342
+ interaction: {
146343
+ startedMsg: ({ params }) => `Stopping simulator server for ${params.udid}`,
146344
+ completedMsg: ({ params }) => `Stopped simulator server for ${params.udid}`,
146345
+ failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to stop simulator server for ${params.udid}: ${failureSignal2.error_code}`
146346
+ },
145862
146347
  description: `Stop the transport session for a specific device (iOS / Android: simulator-server process; Chromium: CDP WebSocket) and free its resources. Use when you are done interacting with one device but want to keep others running. Returns { stopped, udid }. Fails silently if no session is open for the given id.`,
145863
146348
  zodSchema: zodSchema58,
145864
146349
  services: () => ({}),
@@ -145899,6 +146384,11 @@ var PREFIXES = [
145899
146384
  function createStopAllSimulatorServersTool(registry2) {
145900
146385
  return {
145901
146386
  id: "stop-all-simulator-servers",
146387
+ interaction: {
146388
+ startedMsg: () => "Stopping all simulator servers",
146389
+ completedMsg: ({ result }) => `Stopped ${result.stopped.length} simulator ${result.stopped.length === 1 ? "server" : "servers"}`,
146390
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to stop simulator servers: ${failureSignal2.error_code}`
146391
+ },
145902
146392
  description: `Stop all running simulator-server processes (iOS + Android), native devtools services, and Chromium CDP sessions, freeing their resources. Call this when your session ends or the user says they are done. Returns { stopped } \u2014 an array of URNs that were shut down. Fails silently if no servers are running.`,
145903
146393
  services: () => ({}),
145904
146394
  async execute() {
@@ -145956,6 +146446,11 @@ var zodSchema59 = external_exports.object({
145956
146446
  });
145957
146447
  var stopMetroTool = {
145958
146448
  id: "stop-metro",
146449
+ interaction: {
146450
+ startedMsg: ({ params }) => `Stopping Metro on port ${params.port}`,
146451
+ completedMsg: ({ params }) => `Stopped Metro on port ${params.port}`,
146452
+ failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to stop Metro on port ${params.port}: ${failureSignal2.error_code}`
146453
+ },
145959
146454
  description: `Stop the Metro bundler process listening on a given port (default 8081). Use when ending a React Native session or when Metro must be restarted. Returns { stopped, port, pids }; stopped=false if no process is found on the port. Fails if the port lookup command times out or the process cannot be killed. This is DESTRUCTIVE \u2014 always ask the user for confirmation before calling this tool.`,
145960
146455
  zodSchema: zodSchema59,
145961
146456
  services: () => ({}),
@@ -146213,6 +146708,18 @@ function clearActiveFlow() {
146213
146708
  activeFlowName = null;
146214
146709
  recordingSession = null;
146215
146710
  }
146711
+ function selectorTree(sel) {
146712
+ const out = [];
146713
+ const walk = (s) => {
146714
+ out.push(s);
146715
+ for (const relation of SELECTOR_RELATIONS) {
146716
+ const nested = s[relation];
146717
+ if (nested !== void 0) walk(nested);
146718
+ }
146719
+ };
146720
+ walk(sel);
146721
+ return out;
146722
+ }
146216
146723
  function isE2eFlow(flow) {
146217
146724
  const first = flow.steps.find((s) => s.kind !== "echo");
146218
146725
  return first?.kind === "launch";
@@ -146246,11 +146753,35 @@ function selectorToYaml(sel) {
146246
146753
  "Cannot serialize flow selector: `text` must contain at least one visible character (icon-font/private-use and zero-width characters render as nothing). Select by identifier or role, or use a coordinate tap."
146247
146754
  );
146248
146755
  }
146249
- if (sel.loose && (sel.text === void 0 || sel.textMatches !== void 0 || sel.identifier !== void 0 || sel.role !== void 0)) {
146756
+ const scopeCount = SELECTOR_RELATIONS.filter((relation) => sel[relation] !== void 0).length;
146757
+ if (sel.any !== void 0) {
146758
+ if (sel.any !== true) {
146759
+ throw new Error(
146760
+ "Cannot serialize flow selector: `any` is the universal selector and takes only `true` \u2014 omit it to select by text/id/role."
146761
+ );
146762
+ }
146763
+ if (sel.text !== void 0 || sel.textMatches !== void 0 || sel.identifier || sel.role) {
146764
+ throw new Error(
146765
+ "Cannot serialize flow selector: `any` already matches every element, so it cannot be combined with text/id/role \u2014 keep one or the other."
146766
+ );
146767
+ }
146768
+ if (scopeCount === 0) {
146769
+ throw new Error(
146770
+ `Cannot serialize flow selector: \`any\` matches every element on screen, so it needs a scope (${SELECTOR_RELATIONS.join("/")}) to narrow what it selects.`
146771
+ );
146772
+ }
146773
+ } else if (scopeCount > 0 && sel.text === void 0 && sel.textMatches === void 0 && !sel.identifier && !sel.role) {
146774
+ throw new Error(
146775
+ `Cannot serialize flow selector: a scope (${SELECTOR_RELATIONS.join("/")}) only narrows where to look \u2014 the selector still needs its own text/id/role naming what to find there, or \`any: true\` for any element.`
146776
+ );
146777
+ }
146778
+ if (sel.loose && (sel.text === void 0 || sel.textMatches !== void 0 || sel.identifier !== void 0 || sel.role !== void 0 || sel.any !== void 0 || SELECTOR_RELATIONS.some((relation) => sel[relation] !== void 0))) {
146250
146779
  const incompatible = [
146251
146780
  sel.textMatches !== void 0 ? "textMatches" : void 0,
146252
146781
  sel.identifier !== void 0 ? "identifier" : void 0,
146253
- sel.role !== void 0 ? "role" : void 0
146782
+ sel.role !== void 0 ? "role" : void 0,
146783
+ sel.any !== void 0 ? "any" : void 0,
146784
+ ...SELECTOR_RELATIONS.map((relation) => sel[relation] !== void 0 ? relation : void 0)
146254
146785
  ].filter((field) => field !== void 0);
146255
146786
  throw new Error(
146256
146787
  "Cannot serialize loose flow selector without changing its meaning: bare-string YAML can represent only a loose text-only selector" + (incompatible.length > 0 ? `; incompatible fields: ${incompatible.join(", ")}` : "") + "."
@@ -146259,16 +146790,30 @@ function selectorToYaml(sel) {
146259
146790
  if (sel.loose && sel.text !== void 0 && sel.identifier === void 0 && sel.role === void 0) {
146260
146791
  return sel.text;
146261
146792
  }
146262
- const { loose: _loose, identifier, textMatches: textMatches2, ...rest } = sel;
146793
+ const { loose: _loose, any: any2, identifier, textMatches: textMatches2, within, after, next, ...rest } = sel;
146794
+ const scopes = { within, after, next };
146263
146795
  const out = { ...rest };
146796
+ if (any2) out.any = true;
146264
146797
  if (textMatches2 !== void 0) out.text = { matches: textMatches2 };
146265
146798
  if (identifier !== void 0) out.id = identifier;
146799
+ for (const relation of SELECTOR_RELATIONS) {
146800
+ const scope = scopes[relation];
146801
+ if (scope !== void 0) out[relation] = selectorToYaml(scope);
146802
+ }
146266
146803
  return out;
146267
146804
  }
146268
146805
  function describeSelector(s) {
146269
- return Object.entries(s).filter(([k]) => k !== "loose").map(
146806
+ const { loose: _loose, any: any2, within, after, next, ...rest } = s;
146807
+ const scopes = { within, after, next };
146808
+ const fields = Object.entries(rest).map(
146270
146809
  ([k, v]) => k === "textMatches" ? `text=/${v}/` : `${k === "identifier" ? "id" : k}="${v}"`
146271
146810
  ).join(" ");
146811
+ const parts = [any2 ? "*" : void 0, fields || void 0].filter((p) => p !== void 0);
146812
+ for (const relation of SELECTOR_RELATIONS) {
146813
+ const scope = scopes[relation];
146814
+ if (scope !== void 0) parts.push(`${relation} (${describeSelector(scope)})`);
146815
+ }
146816
+ return parts.join(" ");
146272
146817
  }
146273
146818
  function describeTextExpectation(expectedText, textMatch, verbForm = "mode") {
146274
146819
  const expected = expectedText ?? "";
@@ -146510,8 +147055,22 @@ function rejectUnknownKeys(raw, body, allowed, where) {
146510
147055
  `${where} has ${describeUnknownKeys(unknown2, allowed)} \u2014 allowed keys: ${allowed.join(", ")}`
146511
147056
  );
146512
147057
  }
146513
- var SELECTOR_KEYS = ["text", "id", "identifier", "role"];
146514
- function parseSelector(raw, where) {
147058
+ var SELECTOR_KEYS = [
147059
+ "text",
147060
+ "id",
147061
+ "identifier",
147062
+ "role",
147063
+ "any",
147064
+ ...SELECTOR_RELATIONS
147065
+ ];
147066
+ var MAX_SELECTOR_SCOPES = 6;
147067
+ function parseSelector(raw, where, budget = { scopes: MAX_SELECTOR_SCOPES }) {
147068
+ if (budget.scopes < 0) {
147069
+ badEntry(
147070
+ raw,
147071
+ `${where}: a selector carries more than ${MAX_SELECTOR_SCOPES} scopes (${SELECTOR_RELATIONS.join("/")}) in total \u2014 check for a cyclic YAML alias (\`&s { \u2026, within: *s }\`)`
147072
+ );
147073
+ }
146515
147074
  if (typeof raw === "string") {
146516
147075
  const r2 = selectorSchema.safeParse({ text: raw });
146517
147076
  if (!r2.success) badEntry(raw, `${where}: ${r2.error.issues[0]?.message ?? "invalid selector"}`);
@@ -146520,9 +147079,55 @@ function parseSelector(raw, where) {
146520
147079
  if (raw !== null && typeof raw === "object" && !Array.isArray(raw)) {
146521
147080
  rejectUnknownKeys(raw, raw, SELECTOR_KEYS, `${where}: selector`);
146522
147081
  }
146523
- let normalized = raw;
146524
- if (raw !== null && typeof raw === "object" && "id" in raw) {
146525
- const { id, ...rest } = raw;
147082
+ const scopes = {};
147083
+ let universal = false;
147084
+ let fieldsRaw = raw;
147085
+ if (raw !== null && typeof raw === "object" && !Array.isArray(raw)) {
147086
+ const restRaw = { ...raw };
147087
+ const present = SELECTOR_RELATIONS.filter((relation) => relation in restRaw);
147088
+ for (const relation of present) {
147089
+ budget.scopes--;
147090
+ scopes[relation] = parseSelector(restRaw[relation], `${where}.${relation}`, budget);
147091
+ delete restRaw[relation];
147092
+ }
147093
+ if ("any" in restRaw) {
147094
+ if (restRaw.any !== true) {
147095
+ badEntry(
147096
+ raw,
147097
+ `${where}: \`any\` takes only \`true\` \u2014 it is the CSS \`*\` universal selector (drop the key to select by text/id/role instead)`
147098
+ );
147099
+ }
147100
+ delete restRaw.any;
147101
+ if (Object.keys(restRaw).length > 0) {
147102
+ badEntry(
147103
+ raw,
147104
+ `${where}: \`any: true\` already matches every element \u2014 drop it, or drop the ${Object.keys(
147105
+ restRaw
147106
+ ).map((k) => `\`${k}\``).join("/")} it makes redundant`
147107
+ );
147108
+ }
147109
+ if (present.length === 0) {
147110
+ badEntry(
147111
+ raw,
147112
+ `${where}: \`any: true\` matches every element on screen \u2014 pair it with a scope (${SELECTOR_RELATIONS.join(
147113
+ "/"
147114
+ )}) so it selects something specific`
147115
+ );
147116
+ }
147117
+ universal = true;
147118
+ } else if (present.length > 0 && Object.keys(restRaw).length === 0) {
147119
+ badEntry(
147120
+ raw,
147121
+ `${where}: a selector's \`${present.join("`/`")}\` only scopes where to look \u2014 the selector still needs its own text/id/role naming what to find there (or \`any: true\` for any element)`
147122
+ );
147123
+ }
147124
+ fieldsRaw = restRaw;
147125
+ }
147126
+ const attachScopes = (sel) => ({ ...sel, ...scopes });
147127
+ if (universal) return attachScopes({ any: true });
147128
+ let normalized = fieldsRaw;
147129
+ if (fieldsRaw !== null && typeof fieldsRaw === "object" && "id" in fieldsRaw) {
147130
+ const { id, ...rest } = fieldsRaw;
146526
147131
  if ("identifier" in rest) {
146527
147132
  badEntry(raw, `${where}: selector takes \`id\` or \`identifier\` (its alias), not both`);
146528
147133
  }
@@ -146555,12 +147160,12 @@ function parseSelector(raw, where) {
146555
147160
  if (!fields.success) {
146556
147161
  badEntry(raw, `${where}: ${fields.error.issues[0]?.message ?? "invalid selector"}`);
146557
147162
  }
146558
- return { ...fields.data, textMatches: pattern };
147163
+ return attachScopes({ ...fields.data, textMatches: pattern });
146559
147164
  }
146560
147165
  }
146561
147166
  const r = selectorSchema.safeParse(normalized);
146562
147167
  if (!r.success) badEntry(raw, `${where}: ${r.error.issues[0]?.message ?? "invalid selector"}`);
146563
- return r.data;
147168
+ return attachScopes(r.data);
146564
147169
  }
146565
147170
  var WAIT_CONDITIONS = ["exists", "visible", "hidden", "text"];
146566
147171
  var TEXT_MATCH_MODES = Object.keys({
@@ -146711,11 +147316,14 @@ function parseTapTimes(raw, entry) {
146711
147316
  }
146712
147317
  return raw === 1 ? void 0 : raw;
146713
147318
  }
147319
+ function hasSelectorField(obj) {
147320
+ return obj.text !== void 0 || obj.id !== void 0 || obj.identifier !== void 0 || obj.role !== void 0 || obj.any !== void 0 || SELECTOR_RELATIONS.some((relation) => obj[relation] !== void 0);
147321
+ }
146714
147322
  function parseTarget(raw, where) {
146715
147323
  if (raw !== null && typeof raw === "object") {
146716
147324
  const obj = raw;
146717
147325
  if (obj.x !== void 0 || obj.y !== void 0) {
146718
- if (obj.text !== void 0 || obj.id !== void 0 || obj.identifier !== void 0 || obj.role !== void 0) {
147326
+ if (hasSelectorField(obj)) {
146719
147327
  badEntry(raw, `${where} takes a selector or x/y coordinates, not both`);
146720
147328
  }
146721
147329
  if (typeof obj.x !== "number" || typeof obj.y !== "number") {
@@ -146738,7 +147346,7 @@ function parseTarget(raw, where) {
146738
147346
  function parseTap(body, entry) {
146739
147347
  const obj = body !== null && typeof body === "object" ? body : {};
146740
147348
  if (obj.on !== void 0 || obj.times !== void 0) {
146741
- if (obj.text !== void 0 || obj.id !== void 0 || obj.identifier !== void 0 || obj.role !== void 0) {
147349
+ if (hasSelectorField(obj)) {
146742
147350
  badEntry(
146743
147351
  entry,
146744
147352
  'the tap options form takes a nested selector \u2014 e.g. tap: { on: { text: "Photo" }, times: 2 }'
@@ -146766,7 +147374,7 @@ function parseTap(body, entry) {
146766
147374
  function parseLongPress(body, entry) {
146767
147375
  const obj = body !== null && typeof body === "object" ? body : {};
146768
147376
  if (obj.on !== void 0 || obj.duration !== void 0) {
146769
- if (obj.text !== void 0 || obj.id !== void 0 || obj.identifier !== void 0 || obj.role !== void 0) {
147377
+ if (hasSelectorField(obj)) {
146770
147378
  badEntry(
146771
147379
  entry,
146772
147380
  'the long-press options form takes a nested selector \u2014 e.g. long-press: { on: { text: "Row" }, duration: 1200 }'
@@ -146806,7 +147414,7 @@ function parsePinch(body, entry) {
146806
147414
  );
146807
147415
  }
146808
147416
  const obj = body;
146809
- if (obj.text !== void 0 || obj.id !== void 0 || obj.identifier !== void 0 || obj.role !== void 0) {
147417
+ if (hasSelectorField(obj)) {
146810
147418
  badEntry(entry, 'pinch takes a nested selector \u2014 e.g. pinch: { on: "Map", scale: 3 }');
146811
147419
  }
146812
147420
  rejectUnknownKeys(entry, obj, ["on", "scale"], "pinch");
@@ -146873,13 +147481,11 @@ function parseWhenCondition(raw) {
146873
147481
  }
146874
147482
  const { timeout: _timeout, ...cond } = parseWaitFields(raw, "when");
146875
147483
  const { selector, expectedText } = cond;
146876
- for (const s of [
146877
- expectedText,
146878
- selector.text,
146879
- selector.textMatches,
146880
- selector.identifier,
146881
- selector.role
146882
- ]) {
147484
+ const guardStrings = [expectedText];
147485
+ for (const s of selectorTree(selector)) {
147486
+ guardStrings.push(s.text, s.textMatches, s.identifier, s.role);
147487
+ }
147488
+ for (const s of guardStrings) {
146883
147489
  if (s !== void 0 && s.includes(SECRET_PLACEHOLDER_MARKER)) {
146884
147490
  badEntry(
146885
147491
  { when: raw },
@@ -147004,6 +147610,12 @@ function fromYamlStep(raw, whenDepth = 0) {
147004
147610
  if (b.direction !== void 0 && (typeof b.direction !== "string" || !SCROLL_DIRECTIONS.includes(b.direction))) {
147005
147611
  badEntry(raw, `scroll-to direction must be one of ${SCROLL_DIRECTIONS.join(", ")}`);
147006
147612
  }
147613
+ if (b.target === void 0) {
147614
+ badEntry(
147615
+ raw,
147616
+ `scroll-to needs a \`target\` \u2014 its own \`within\` only anchors the gesture to a scroll container, e.g. scroll-to: { target: <selector>, within: { id: list } }. A selector scope (${SELECTOR_RELATIONS.join("/")}) goes inside \`target\`.`
147617
+ );
147618
+ }
147007
147619
  const step = {
147008
147620
  kind: "scroll-to",
147009
147621
  target: parseSelector(b.target, "scroll-to.target"),
@@ -147159,6 +147771,11 @@ var fileInputs2 = [
147159
147771
  ];
147160
147772
  var flowStartRecordingTool = {
147161
147773
  id: "flow-start-recording",
147774
+ interaction: {
147775
+ startedMsg: () => "Starting flow recording",
147776
+ completedMsg: () => "Started flow recording",
147777
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to start flow recording: ${failureSignal2.error_code}`
147778
+ },
147162
147779
  description: `Start recording a new flow. Creates a .yaml file in the .argent/flows/ directory.
147163
147780
  Use when you want to capture a reusable sequence of device interactions for later replay.
147164
147781
  Returns { message, flowFile, savedTo } and optionally { previousFlow } if a prior recording was abandoned.
@@ -147780,6 +148397,11 @@ async function captureRunTarget(session, args) {
147780
148397
  function createFlowAddStepTool(registry2) {
147781
148398
  return {
147782
148399
  id: "flow-add-step",
148400
+ interaction: {
148401
+ startedMsg: ({ params }) => `Adding ${params.command} step to recorded flow`,
148402
+ completedMsg: ({ params }) => `Added ${params.command} step to recorded flow`,
148403
+ failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to add ${params.command} step to recorded flow: ${failureSignal2.error_code}`
148404
+ },
147783
148405
  description: `Execute a tool call and record it as a step in the active flow. Use when recording a flow with flow-start-recording and you want to run and capture each action. A coordinate \`gesture-tap\` is recorded as a portable \`tap: { selector }\` step when the tapped element has stable text/identifier (otherwise coordinates are kept with a warning); a \`restart-app\` is recorded as a \`launch\` step (record one FIRST to make the flow a self-contained e2e flow). Returns { message, toolResult, flowFile } on success. If it fails an error is returned and nothing is recorded.
147784
148406
  If a step was recorded by mistake, edit the .yaml file directly to remove it.`,
147785
148407
  zodSchema: zodSchema61,
@@ -147841,6 +148463,11 @@ var zodSchema62 = external_exports.object({
147841
148463
  });
147842
148464
  var flowInsertEchoTool = {
147843
148465
  id: "flow-add-echo",
148466
+ interaction: {
148467
+ startedMsg: () => "Adding note to recorded flow",
148468
+ completedMsg: () => "Added note to recorded flow",
148469
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to add note to recorded flow: ${failureSignal2.error_code}`
148470
+ },
147844
148471
  description: `Record an echo step in the active flow. Echo steps print a message when the flow is replayed \u2014 useful as labels between tool calls.
147845
148472
  Use when you want to annotate a recorded flow with a human-readable label or checkpoint message.
147846
148473
  Returns { message, flowFile }. Fails if no active flow recording is in progress.`,
@@ -147874,6 +148501,14 @@ function textConditionLabel(sel, expectedText, textMatch) {
147874
148501
  var zodSchema63 = external_exports.object({});
147875
148502
  var flowFinishRecordingTool = {
147876
148503
  id: "flow-finish-recording",
148504
+ interaction: {
148505
+ startedMsg: () => "Finishing flow recording",
148506
+ completedMsg: ({ result }) => {
148507
+ const flowName = result.path.split(/[\\/]/).pop()?.replace(/\.ya?ml$/, "") ?? "flow";
148508
+ return `Saved recorded flow ${flowName}`;
148509
+ },
148510
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to finish flow recording: ${failureSignal2.error_code}`
148511
+ },
147877
148512
  description: `Finish recording the active flow. Returns a summary of all recorded steps and the final YAML content. Use when you have added all desired steps and want to finalize the flow file. Fails if no active flow recording is in progress.
147878
148513
  You can still edit the .yaml file directly afterwards to remove or reorder steps.`,
147879
148514
  zodSchema: zodSchema63,
@@ -148002,7 +148637,16 @@ function probeWhenCondition(env, cond) {
148002
148637
  return waitForCondition(env, cond, DEFAULT_ASSERT_TIMEOUT_MS);
148003
148638
  }
148004
148639
  function selectorAlternatives(sel) {
148005
- return sel.loose && sel.text !== void 0 ? [{ identifier: sel.text }, { text: sel.text }] : [sel];
148640
+ const { loose, any: _any2, within, after, next, ...own } = sel;
148641
+ const scopes = { within, after, next };
148642
+ let alts = loose && own.text !== void 0 ? [{ identifier: own.text }, { text: own.text }] : [own];
148643
+ for (const relation of SELECTOR_RELATIONS) {
148644
+ const scope = scopes[relation];
148645
+ if (scope === void 0) continue;
148646
+ const scopeAlts = selectorAlternatives(scope);
148647
+ alts = alts.flatMap((o) => scopeAlts.map((s) => ({ ...o, [relation]: s })));
148648
+ }
148649
+ return alts;
148006
148650
  }
148007
148651
  function flowFindAll(tree, sel) {
148008
148652
  let fallback = [];
@@ -150889,11 +151533,22 @@ async function runLaunch(state3, app) {
150889
151533
  function createRunFlowTool(registry2) {
150890
151534
  return {
150891
151535
  id: "flow-execute",
151536
+ interaction: {
151537
+ startedMsg: ({ params }) => `Running flow ${params.name}`,
151538
+ completedMsg: ({ params }) => `Ran flow ${params.name}`,
151539
+ failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to run flow ${params.name}: ${failureSignal2.error_code}`
151540
+ },
150892
151541
  description: `Run a saved flow from the .argent/flows/ directory.
150893
151542
  Steps run in order: \`launch\` starts an app from scratch (terminate + relaunch) and waits until it is
150894
151543
  ready; \`tool\` calls dispatch through the registry; \`tap\`/\`long-press\`/\`type\` resolve a selector to an
150895
151544
  element and act on it (\`tap: { on, times: 2 }\` double-taps; \`long-press: { on, duration }\` presses and
150896
- holds; \`tap\`/\`long-press\` alternatively take a raw normalized point \u2014 bare \`{ x, y }\` or \`on: { x, y }\`);
151545
+ holds; \`tap\`/\`long-press\` alternatively take a raw normalized point \u2014 bare \`{ x, y }\` or \`on: { x, y }\`;
151546
+ any selector may scope its matches geometrically, the CSS combinators read off frames: \`within: <selector>\`
151547
+ (descendant \u2014 inside that container's frame), \`after: <selector>\` (CSS \`~\` \u2014 following it in reading
151548
+ order), \`next: <selector>\` (CSS \`+\` \u2014 the nearest such follower, which unlike CSS reaches past a
151549
+ non-matching neighbour rather than failing), plus \`any: true\` (CSS \`*\` \u2014 legal only WITH a scope and
151550
+ never beside text/id/role). Scopes nest to disambiguate \u2014 \`within: { id: card, within: { id: list } }\`
151551
+ reads "inside card inside list", each container's frame inside the next);
150897
151552
  \`scroll-to\` scrolls (momentum-free) until a target is visible; \`pinch\` zooms
150898
151553
  (\`pinch: { on?, scale }\` \u2014 scale > 1 in, < 1 out; screen center when \`on\` is omitted); \`rotate\` is the
150899
151554
  two-finger rotation gesture (\`rotate: { on?, by }\` \u2014 degrees, + clockwise, within \xB13000\xB0; screen center
@@ -151052,10 +151707,15 @@ function pushReport(state3, report) {
151052
151707
  }
151053
151708
  function selectorLabel2(sel) {
151054
151709
  const parts = [];
151710
+ if (sel.any) parts.push("*");
151055
151711
  if (sel.text !== void 0) parts.push(`"${sel.text}"`);
151056
151712
  if (sel.textMatches !== void 0) parts.push(`/${sel.textMatches}/`);
151057
151713
  if (sel.identifier) parts.push(`id=${sel.identifier}`);
151058
151714
  if (sel.role) parts.push(`role=${sel.role}`);
151715
+ for (const relation of SELECTOR_RELATIONS) {
151716
+ const scope = sel[relation];
151717
+ if (scope !== void 0) parts.push(`${relation} (${selectorLabel2(scope)})`);
151718
+ }
151059
151719
  return parts.join(" ");
151060
151720
  }
151061
151721
  function conditionLabel(cond, renderSelector) {
@@ -151376,6 +152036,11 @@ var fileInputs4 = [
151376
152036
  ];
151377
152037
  var flowReadPrerequisiteTool = {
151378
152038
  id: "flow-read-prerequisite",
152039
+ interaction: {
152040
+ startedMsg: () => "Reading flow prerequisite",
152041
+ completedMsg: () => "Read flow prerequisite",
152042
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to read flow prerequisite: ${failureSignal2.error_code}`
152043
+ },
151379
152044
  description: `Read the execution prerequisite of a saved flow without running it.
151380
152045
  Returns the prerequisite description so you can verify the required state is met before calling flow-execute.
151381
152046
  Use when you need to check what app/simulator state is required before executing a flow.
@@ -151701,6 +152366,11 @@ var fileInputs5 = [
151701
152366
  ];
151702
152367
  var gatherWorkspaceDataTool = {
151703
152368
  id: "gather-workspace-data",
152369
+ interaction: {
152370
+ startedMsg: () => "Gathering workspace data",
152371
+ completedMsg: () => "Gathered workspace data",
152372
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to gather workspace data: ${failureSignal2.error_code}`
152373
+ },
151704
152374
  description: `Fetch a structured snapshot of a mobile app project's workspace.
151705
152375
 
151706
152376
  Returns package.json contents, metro/babel config text, app.json, eas.json, tsconfig,
@@ -151801,6 +152471,11 @@ var zodSchema67 = external_exports.object({
151801
152471
  var updateScheduled = false;
151802
152472
  var updateArgentTool = {
151803
152473
  id: "update-argent",
152474
+ interaction: {
152475
+ startedMsg: () => `Updating Argent to ${getUpdateState().installableVersion ?? "latest"}`,
152476
+ completedMsg: () => `Updated Argent to ${getUpdateState().installableVersion ?? "latest"}`,
152477
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to update Argent: ${failureSignal2.error_code}`
152478
+ },
151804
152479
  description: "Apply a pending Argent update. Only call this tool when the user has explicitly consented to updating Argent in this conversation. Use when an update notification indicates a new version is available and the user agrees to update. By default updates the install serving this session; pass `target` to choose global/local/both. Returns { message } with the update status and version info. The tool server will restart automatically after the update. Fails if no update is available or an update is already in progress.",
151805
152480
  zodSchema: zodSchema67,
151806
152481
  services: () => ({}),
@@ -151892,6 +152567,11 @@ var zodSchema68 = external_exports.object({
151892
152567
  });
151893
152568
  var dismissUpdateTool = {
151894
152569
  id: "dismiss-update",
152570
+ interaction: {
152571
+ startedMsg: () => "Dismissing Argent update",
152572
+ completedMsg: () => "Dismissed Argent update",
152573
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to dismiss Argent update: ${failureSignal2.error_code}`
152574
+ },
151895
152575
  description: "Clear the Argent update notification for the given number of hours. Use when the user asks to postpone or silence update reminders. Returns { message } confirming the suppression duration. Fails if the hours value is negative or the suppression state cannot be persisted.",
151896
152576
  zodSchema: zodSchema68,
151897
152577
  services: () => ({}),
@@ -151937,6 +152617,11 @@ var fileInputs6 = [
151937
152617
  ];
151938
152618
  var screenshotDiffTool = {
151939
152619
  id: "screenshot-diff",
152620
+ interaction: {
152621
+ startedMsg: () => "Comparing screenshots",
152622
+ completedMsg: () => "Compared screenshots",
152623
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to compare screenshots: ${failureSignal2.error_code}`
152624
+ },
151940
152625
  description: `Compare two PNG screenshots and return a compact visual-diff summary.
151941
152626
  Accepts saved baseline/current PNG paths, or one saved PNG plus one live full-resolution capture from a device. Always provide udid so the simulator-server dependency can be resolved.
151942
152627
  Use when stable before/after screenshots exist and the expected result is pixel-visible: layout, spacing, color, typography, image/icon rendering, clipping, overflow, or text rendering.
@@ -152200,6 +152885,11 @@ var zodSchema70 = external_exports.object({
152200
152885
  function createProposeVariantTool(registry2) {
152201
152886
  return {
152202
152887
  id: "propose_variant",
152888
+ interaction: {
152889
+ startedMsg: () => "Presenting variant options",
152890
+ completedMsg: () => "Presented variant options",
152891
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to present variant options: ${failureSignal2.error_code}`
152892
+ },
152203
152893
  featureFlag: "argent-lens",
152204
152894
  description: `Stage ONE design variant for ONE on-screen element, then return immediately (non-blocking).
152205
152895
 
@@ -152250,6 +152940,11 @@ var zodSchema71 = external_exports.object({
152250
152940
  });
152251
152941
  var awaitUserSelectionTool = {
152252
152942
  id: "await_user_selection",
152943
+ interaction: {
152944
+ startedMsg: () => "Waiting for variant selection",
152945
+ completedMsg: () => "Received variant selection",
152946
+ failedMsg: ({ failureSignal: failureSignal2 }) => `Failed while waiting for variant selection: ${failureSignal2.error_code}`
152947
+ },
152253
152948
  featureFlag: "argent-lens",
152254
152949
  // Hidden entirely while an `argent lens` CLI session owns the preview window:
152255
152950
  // there, the user's picks are relayed into the agent's terminal as a normal
@@ -152305,11 +153000,38 @@ var zodSchema72 = external_exports.object({
152305
153000
  url: external_exports.string().optional().describe("`new` only: URL to open (defaults to about:blank)."),
152306
153001
  label: external_exports.string().optional().describe("`new` only: a memorable label usable interchangeably with the tabId.")
152307
153002
  });
153003
+ var tabAction = {
153004
+ list: {
153005
+ started: () => "Listing tabs",
153006
+ completed: (_params, result) => `Listed ${result.tabs.length} ${result.tabs.length === 1 ? "tab" : "tabs"}`,
153007
+ failure: "list"
153008
+ },
153009
+ select: {
153010
+ started: (params) => `Selecting tab ${params.tab}`,
153011
+ completed: (params) => `Selected tab ${params.tab}`,
153012
+ failure: "select"
153013
+ },
153014
+ new: {
153015
+ started: () => "Opening a new tab",
153016
+ completed: () => "Opened a new tab",
153017
+ failure: "open"
153018
+ },
153019
+ close: {
153020
+ started: (params) => `Closing ${params.tab ? `tab ${params.tab}` : "active tab"}`,
153021
+ completed: (params) => `Closed ${params.tab ? `tab ${params.tab}` : "active tab"}`,
153022
+ failure: "close"
153023
+ }
153024
+ };
152308
153025
  var capability29 = {
152309
153026
  chromium: { app: true }
152310
153027
  };
152311
153028
  var chromiumTabsTool = {
152312
153029
  id: "chromium-tabs",
153030
+ interaction: {
153031
+ startedMsg: ({ params }) => tabAction[params.action].started(params),
153032
+ completedMsg: ({ params, result }) => tabAction[params.action].completed(params, result),
153033
+ failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to ${tabAction[params.action].failure} browser tabs: ${failureSignal2.error_code}`
153034
+ },
152313
153035
  description: `List and switch the tabs / windows of a Chromium (CDP) app (an Electron app's BrowserWindows or a Chromium browser's tabs), and open or close them.
152314
153036
  - action="list": enumerate page targets with stable ids (\`t1\`, \`t2\`, \u2026), title, url, and which is active.
152315
153037
  - action="select" (tab=<tabId|label>): make that tab the active one. The active tab is what describe / gesture-tap / screenshot / debugger-evaluate / open-url all operate on, so switch before driving a different tab.
@@ -152436,11 +153158,22 @@ var zodSchema73 = external_exports.object({
152436
153158
  sameSite: external_exports.enum(["Strict", "Lax", "None"]).optional().describe("set: SameSite policy."),
152437
153159
  expires: external_exports.number().optional().describe("set: expiry as a Unix timestamp (seconds). Omit for a session cookie.")
152438
153160
  });
153161
+ var cookieAction = {
153162
+ get: { started: "Reading", completed: "Read", failure: "read" },
153163
+ set: { started: "Setting", completed: "Set", failure: "set" },
153164
+ delete: { started: "Deleting", completed: "Deleted", failure: "delete" },
153165
+ clear: { started: "Clearing", completed: "Cleared", failure: "clear" }
153166
+ };
152439
153167
  var capability30 = {
152440
153168
  chromium: { app: true }
152441
153169
  };
152442
153170
  var chromiumCookiesTool = {
152443
153171
  id: "chromium-cookies",
153172
+ interaction: {
153173
+ startedMsg: ({ params }) => `${cookieAction[params.action].started} browser cookies`,
153174
+ completedMsg: ({ params }) => `${cookieAction[params.action].completed} browser cookies`,
153175
+ failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to ${cookieAction[params.action].failure} browser cookies: ${failureSignal2.error_code}`
153176
+ },
152444
153177
  description: `Read and write cookies of a Chromium (CDP) app (via the Network domain, so HttpOnly cookies are included).
152445
153178
  - action="get" (url?): list cookies, optionally restricted to given URLs (defaults to the active page).
152446
153179
  - action="set" (name, value, + url OR domain, optional path/secure/httpOnly/sameSite/expires): create or update a cookie.
@@ -152523,11 +153256,22 @@ var zodSchema74 = external_exports.object({
152523
153256
  key: external_exports.string().optional().describe("get (optional) / set / remove: the storage key."),
152524
153257
  value: external_exports.string().optional().describe("set: the value to store.")
152525
153258
  });
153259
+ var storageAction = {
153260
+ get: { started: "Reading", completed: "Read", failure: "read" },
153261
+ set: { started: "Updating", completed: "Updated", failure: "update" },
153262
+ remove: { started: "Removing from", completed: "Removed from", failure: "remove from" },
153263
+ clear: { started: "Clearing", completed: "Cleared", failure: "clear" }
153264
+ };
152526
153265
  var capability31 = {
152527
153266
  chromium: { app: true }
152528
153267
  };
152529
153268
  var chromiumStorageTool = {
152530
153269
  id: "chromium-storage",
153270
+ interaction: {
153271
+ startedMsg: ({ params }) => `${storageAction[params.action].started} ${params.store} storage`,
153272
+ completedMsg: ({ params }) => `${storageAction[params.action].completed} ${params.store} storage`,
153273
+ failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to ${storageAction[params.action].failure} ${params.store} storage: ${failureSignal2.error_code}`
153274
+ },
152531
153275
  description: `Read and write localStorage / sessionStorage of a Chromium (CDP) app's active page.
152532
153276
  - action="get": with \`key\`, returns that value; without \`key\`, returns all entries.
152533
153277
  - action="set" (key, value): write an entry.