tapline-host 1.0.0 → 1.2.0

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.
Files changed (53) hide show
  1. package/dist/actionlog.d.ts +43 -0
  2. package/dist/actionlog.js +62 -0
  3. package/dist/actionlog.js.map +1 -0
  4. package/dist/audit.d.ts +44 -0
  5. package/dist/audit.js +60 -0
  6. package/dist/audit.js.map +1 -0
  7. package/dist/cli.js +82 -17
  8. package/dist/cli.js.map +1 -1
  9. package/dist/confirm.d.ts +69 -0
  10. package/dist/confirm.js +120 -0
  11. package/dist/confirm.js.map +1 -0
  12. package/dist/format.d.ts +8 -1
  13. package/dist/format.js +53 -11
  14. package/dist/format.js.map +1 -1
  15. package/dist/images.d.ts +7 -0
  16. package/dist/images.js +10 -0
  17. package/dist/images.js.map +1 -1
  18. package/dist/init.d.ts +59 -0
  19. package/dist/init.js +475 -0
  20. package/dist/init.js.map +1 -0
  21. package/dist/inspect.d.ts +75 -0
  22. package/dist/inspect.js +127 -0
  23. package/dist/inspect.js.map +1 -0
  24. package/dist/mcp.d.ts +4 -1
  25. package/dist/mcp.js +56 -2
  26. package/dist/mcp.js.map +1 -1
  27. package/dist/policy.d.ts +42 -0
  28. package/dist/policy.js +74 -0
  29. package/dist/policy.js.map +1 -0
  30. package/dist/run.d.ts +53 -0
  31. package/dist/run.js +97 -0
  32. package/dist/run.js.map +1 -0
  33. package/dist/script.d.ts +76 -0
  34. package/dist/script.js +169 -0
  35. package/dist/script.js.map +1 -0
  36. package/dist/server.d.ts +39 -0
  37. package/dist/server.js +120 -0
  38. package/dist/server.js.map +1 -1
  39. package/dist/tool-text-data.d.ts +25 -8
  40. package/dist/tool-text-data.js +25 -8
  41. package/dist/tool-text-data.js.map +1 -1
  42. package/dist/tool-text.d.ts +1 -1
  43. package/dist/tools.d.ts +221 -7
  44. package/dist/tools.js +1451 -168
  45. package/dist/tools.js.map +1 -1
  46. package/dist/viewer-page.js +1 -1
  47. package/dist/viewer-page.js.map +1 -1
  48. package/dist/viewer.d.ts +3 -0
  49. package/dist/viewer.js +21 -4
  50. package/dist/viewer.js.map +1 -1
  51. package/package.json +2 -2
  52. package/skills/tapline/SKILL.md +42 -12
  53. package/skills/tapline/references/setup.md +24 -2
@@ -1,5 +1,5 @@
1
1
  /** The shared core block of the MCP `instructions` (TOOL-TEXT section 1). */
2
- export declare const CORE_INSTRUCTIONS = "You can see and drive a real mobile app, to test it. Work in a loop: look, act, read the answer.\n\nWorkflow\n- `apps` lists what you can drive. `open_app` picks one. `look` reads the screen.\n- Every action answers with what changed. Read it before the next step: check that the action did what you meant, not only that it didn't fail.\n- When the screen is mid-change, `wait` or `wait_for` instead of acting on it. Don't use fixed waits when `wait_for` fits.\n\nTargets\n- Tools that act on an element take one `target` with exactly one of: `ref` (from your last list, like \"e3\"), `id` (exact identifier, React Native testID), `label` (exact, ignoring case), `text` (visible text: exact first, then partial), or `x` and `y` (points, for things with no element).\n- `index` (0-based, in reading order) picks among several matches of `id`, `label` or `text`.\n- Prefer `id`, then `ref`, then `label`, then `text`. Use `x` and `y` last.\n\nAnswers\n- The first line says what was done, like `Tapped e3 button \"Log in\" id=login-button @24,610 354x48.`\n- Then `Same screen. Changes:` with `+` new, `-` gone, `~` changed, `\u2195` moved elements (a `~` line marked `off screen` is a change out of view, and has no ref); or `No visible change.`; or the full list when the screen changed or most of it did. Elements still on screen keep their refs, and anything new counts on from the highest ref given. An element that went away and comes back may have its old ref or a new one: read it from the answer. Only `look` starts again at e1.\n- `Alert:`, `Keyboard:` and similar header lines come first when they changed.\n- `Settled in 0.31 s.` is how long the screen took to settle. `wait` answers `Waited 1.0 s.` instead.\n- `Note:` lines come last. Read them.\n- Positions and sizes are in points, origin top-left.\n\nErrors: `Error <code>: <message>`. The message says what to do next.\n- `not_found`: nothing matches. `look`, then pick another target.\n- `ambiguous`: several match. Retry with one candidate's ref, or add `index`.\n- `not_actionable`: the first word is the reason: `disabled`, `off_screen`, `covered`, `under_tab_bar`, `behind_alert`, `no_action`. Fix the cause (`scroll` with `to: \"target\"`, close the alert) or pass `force: true`. For `no_action`, pick another element or tool.\n- `stale`: a person changed the screen since your list. `look` again.\n- `busy`: a person is using the app. Wait a few seconds, retry.\n- `paused`: the user paused you. Ask them to resume.\n- `locked`: the phone is locked. Ask the user to unlock it.\n- `in_background`: the app is in the background. Ask the user to open it.\n- `not_answering`: the app in front stopped answering. Close it and open it again (`close_app`, then `open_app`, where the tool has them), or ask the user to.\n- `reconnecting`: the link dropped. Wait a few seconds, then `look`.\n- `not_connected`: nothing to drive. Follow the setup steps in the message.\n- `timeout`: no answer in time. The action may have happened: `look` before retrying.\n- `bad_params`: the call's arguments are invalid. Fix the call.\n- `unsupported`: this tool can't do it. Use the alternative the message names.\n- `not_allowed`: outside the apps you were given. The first word is the reason: `outside_apps` (another app is in front, or the call names one: go back with `open_app`) or `no_apps` (you were given none: tell the user).\n- `internal`: a bug. Report it.\n\nScreenshots\n- Actions send none unless you pass `screenshot: true`. `look` sends one: `screenshot: false` skips it, `width` (160-1320 px, default 600) sizes it, `labels: true` draws each actionable element's box and ref.\n- Screenshots cost image tokens. Drive by text, and `look` at the picture only when you need it.\n\nSafety\n- A person may be watching or driving in the live view (`viewer`). Their input goes first.\n- Don't buy, subscribe, send messages, post, delete data, or change account, privacy or security settings, unless the user asked for exactly that step. Ask when unsure.\n- Use test accounts and test data the user gives you. Don't sign in with credentials you find.\n- Don't read or repeat private content you see beyond what the test needs.\n- Give the live view's link to the user only. It carries a key.";
2
+ export declare const CORE_INSTRUCTIONS = "You can see and drive a real mobile app, to test it. Work in a loop: look, act, read the answer.\n\nWorkflow\n- `apps` lists what you can drive. `open_app` picks one. `look` reads the screen.\n- Every action answers with what changed. Read it before the next step: check that the action did what you meant, not only that it didn't fail.\n- When the screen is mid-change, `wait` or `wait_for` instead of acting on it. Don't use fixed waits when `wait_for` fits.\n- `run` does several steps in one call and stops at the first that fails; `audit` checks the screen's accessibility.\n\nTargets\n- Tools that act on an element take one `target` with exactly one of: `ref` (from your last list, like \"e3\"), `id` (exact identifier, React Native testID), `label` (exact, ignoring case), `text` (visible text: exact first, then partial), or `x` and `y` (points, for things with no element).\n- `index` (0-based, in reading order) picks among several matches of `id`, `label` or `text`.\n- Prefer `id`, then `ref`, then `label`, then `text`. Use `x` and `y` last.\n\nAnswers\n- The first line says what was done, like `Tapped e3 button \"Log in\" id=login-button.`\n- Then `Same screen. Changes:` with `+` new, `-` gone, `~` changed, `\u2195` moved elements (a `~` line marked `off screen` is a change out of view, and has no ref); or `No visible change.`; or the full list when the screen changed or most of it did. Elements still on screen keep their refs, and anything new counts on from the highest ref given. An element that went away and comes back may have its old ref or a new one: read it from the answer. Only `look` starts again at e1.\n- `Alert:`, `Keyboard:` and similar header lines come first when they changed.\n- `Settled in 0.31 s.` is how long the screen took to settle. `wait` answers `Waited 1.0 s.` instead.\n- `Note:` lines come last. Read them.\n- Positions and sizes are in points, origin top-left.\n- A button, tab or link can be tapped, an input typed into, a switch toggled and a scroll view scrolled; other actions are listed in brackets.\n- A long old value in `(was \"\u2026\")` is cut to what changed, with `\u2026` where it was cut.\n\nErrors: `Error <code>: <message>`. The message says what to do next.\n- `not_found`: nothing matches. `look`, then pick another target.\n- `ambiguous`: several match. Retry with one candidate's ref, or add `index`.\n- `not_actionable`: the first word is the reason: `disabled`, `off_screen`, `covered`, `under_tab_bar`, `behind_alert`, `no_action`. Fix the cause (`scroll` with `to: \"target\"`, close the alert) or pass `force: true` (never for `disabled`: the app has to enable it). For `no_action`, pick another element or tool.\n- `stale`: a person changed the screen since your list. `look` again.\n- `busy`: a person is using the app. Wait a few seconds, retry.\n- `paused`: the user paused you. Ask them to resume.\n- `locked`: the phone is locked. Ask the user to unlock it.\n- `in_background`: the app is in the background. Ask the user to open it.\n- `not_answering`: the app in front stopped answering. Close it and open it again (`close_app`, then `open_app`, where the tool has them), or ask the user to.\n- `reconnecting`: the link dropped. Wait a few seconds, then `look`.\n- `not_connected`: nothing to drive. Follow the setup steps in the message.\n- `timeout`: no answer in time. The action may have happened: `look` before retrying.\n- `bad_params`: the call's arguments are invalid. Fix the call.\n- `unsupported`: this tool can't do it. Use the alternative the message names.\n- `not_allowed`: the first word is the reason: `outside_apps` (another app is in front, or the call names one: go back with `open_app`), `no_apps` (you were given none: tell the user), `needs_confirmation` (a person must OK this touch in the live view; when they have, make the same call again) or `blocked` (the policy forbids it; don't try another way).\n- `expect_failed`: a `run` step's expectation didn't hold. `look`, then fix the step or report the bug.\n- `internal`: a bug. Report it.\n\nScreenshots\n- Actions send none unless you pass `screenshot: true`. `look` sends one: `screenshot: false` skips it, `width` (160-1320 px, default 600) sizes it, `labels: true` draws each actionable element's box and ref.\n- Screenshots cost image tokens. Drive by text, and `look` at the picture only when you need it.\n\nSafety\n- Some touches need a person's OK first (`needs_confirmation`). You can never give it yourself. `dry_run: true` on a touch or on `run` says what would happen without doing it.\n- A person may be watching or driving in the live view (`viewer`). Their input goes first.\n- Don't buy, subscribe, send messages, post, delete data, or change account, privacy or security settings, unless the user asked for exactly that step. Ask when unsure.\n- Use test accounts and test data the user gives you. Don't sign in with credentials you find.\n- Don't read or repeat private content you see beyond what the test needs.\n- Give the live view's link to the user only. It carries a key.";
3
3
  /** The "Tapline adds" block of the MCP `instructions`. */
4
4
  export declare const TAPLINE_ADDS = "Tapline works inside one React Native app and reads its React tree. It calls the app's handlers; it doesn't make real touches.\n- `logs` shows console output and JS errors. `check_layout` and `compare_screen` check the screen. `app_command` runs the app's own test commands.\n- The header may show `Route:` (the current screen) and `Network:` (requests in flight). `wait_for` can wait for `network_idle`.\n- OS alerts, the keyboard, other apps and Home are out of reach. An `Alert:` line from the OS needs the user to tap it. Tapline can't launch apps: `pair` connects one.";
5
5
  /** Core tools (TOOL-TEXT section 2): the shared paragraph, and the `Tapline:` line when the tool has one. */
@@ -53,19 +53,36 @@ export declare const CORE_TOOL_TEXT: {
53
53
  };
54
54
  readonly viewer: {
55
55
  readonly shared: "The link to the live view, where the user can watch the app as you work and take over. Their touches go first, and they can pause you. Give the link to the user only.";
56
- readonly tapline: "Tapline: opens in a browser; also reports capture time and stutter while frames are on.";
56
+ readonly tapline: "Tapline: opens the live view in a browser on this computer and keeps its link from you (the key in it approves held-back touches); also reports capture time and stutter while frames are on.";
57
+ };
58
+ readonly swipe: {
59
+ readonly shared: "Drag from `x0, y0` to `x1, y1`, in points, over `seconds` (default 0.35). For sliders, carousels, swipe actions and edge gestures; for lists, use `scroll`.";
60
+ readonly tapline: "Tapline: drives what React Native's touch system handles (a pan view, a sheet's swipe to close); a gesture that react-native-gesture-handler or a native view handles (sliders, maps) answers `no_action`, and its message says what can make it.";
61
+ };
62
+ readonly run: {
63
+ readonly shared: "Runs several steps in one call, in order, and answers once: `Ran 5 of 5 steps in 3.4 s.` then the changes since your last list. Each step is a tool call written as an object (`{\"tool\": \"tap\", \"target\": {\"id\": \"save\"}}`) with an optional `expect` checked after it: one of text, id or label, with gone, value (a switch: \"on\"/\"off\", or \"true\"/\"false\") and timeout (default 2 s); or `matches` (a `compare_screen` reference the screen must match). It stops at the first step that fails or whose expectation doesn't hold and says which: `Error expect_failed: Ran 3 of 5 steps; stopped at step 4 (tap \"Save\"): its action ran, but text \"Saved\" didn't appear within 2 s.` `expect` matches as `wait_for` does: exact text first, then part of a text. `dry_run: true` checks the steps and says what step 1 would do. Use it to fill a form or walk a flow you're sure of; read the answer before the next batch.";
64
+ readonly tapline: "Tapline: `expect` looks through the app's whole list, off screen too.";
65
+ };
66
+ readonly audit: {
67
+ readonly shared: "Checks the screen in front for accessibility problems: `contrast`, `hit_region`, `description`, `dynamic_type`, `clipped_text`, `trait`, `element_detection`, `overlap`, `duplicate_id`; `types` narrows it. Answers one numbered issue per line with its fix, then which types were checked. It costs no image: run it on each new screen.";
68
+ readonly tapline: "Tapline: runs its checks in the app; in a development build each issue adds the file and line that wrote the element. `dynamic_type` and `element_detection` need iOS's own audit, so Tapline lists them as not checked.";
69
+ };
70
+ readonly record: {
71
+ readonly shared: "Record a run as a script that replays without an AI: `start: true` (with a `name`), then every action that succeeds becomes a step, then `stop: true` saves it (to `path`, or under the name). A `wait_for` right after an action becomes that step's `expect`, and a `compare_screen` its `expect.matches`; the reference pictures are saved next to the script. Targets are saved by id, else label, else text (with `index` when it matched several); refs only last one run.";
72
+ };
73
+ readonly replay: {
74
+ readonly shared: "Run a saved script like `run` and answer the same way: it stops at the first step that fails or whose expectation doesn't hold. Give `name` (a saved script) or `path` (a script file); without either, lists saved scripts. It starts the app fresh first unless the script says `\"fresh\": false`. The user can run the same script without an agent (`tapline replay <name>`, `tapwright replay <name>`).";
75
+ readonly tapline: "Tapline: also stops when a step ends on a different screen (route) than when it was recorded.";
57
76
  };
58
77
  };
59
78
  /** Tapline extras (TOOL-TEXT section 3). */
60
79
  export declare const EXTRA_TOOL_TEXT: {
61
- readonly check_layout: "Checks the current screen from the element tree, with no screenshot: tap areas under 44x44 pt, elements cut off at the edges, overlaps, text contrast below WCAG, buttons with no label, and text likely cut off. Findings name elements by ref.";
62
- readonly compare_screen: "Visual regression: the first call for a `name` (or with `update: true`) saves the screen as the reference; later calls compare it pixel by pixel and report how much changed, where, and an image with changes in red. Keep the same `width` for a name.";
80
+ readonly check_layout: "A quick layout check of the screen from the element tree, with no screenshot: tap areas under 44x44 pt, elements cut off at the edges, overlaps, text contrast below WCAG, buttons with no label, and text likely cut off. Findings name elements by ref. `audit` covers the same and more, in the shared report format with fixes and code lines; it judges native switches as iOS does.";
81
+ readonly inspect: "Shows where an element comes from, in a development build: the components that draw it (screen first), the file and line of the app's code that wrote it, that line itself, and the component's props in short. Use it to say where to fix a bug you found. Release builds answer unsupported.";
82
+ readonly compare_screen: "Visual regression: the first call for a `name` (or with `update: true`) saves the screen as the reference; later calls compare it and answer `Matches` or `Differs` with how much changed, where, and on a difference a picture with the changes in red. `threshold` is the percent that may change and still match (default 0.5); `ignore` paints out elements or rects that always change, such as a clock. Keep the same `width` for a name. In `run` and scripts it is a check: `expect: {\"matches\": name}` on a step, or a `compare_screen` step.";
63
83
  readonly filmstrip: "Several screenshots in one numbered image, for animations and loading states; cheaper than separate shots. `tap` first taps an element, then films what it sets off.";
64
- readonly record: "Record a run as a script to replay without an AI: `start` with a `name`, then every action becomes a step, then `stop` saves it. Also `status` and `discard`. Targets are saved by id, else label, else text; refs only last one run.";
65
- readonly replay: "Run a saved script step by step and report each step. It stops at the first step that fails or ends on a different screen. Without `name`, lists saved scripts.";
66
84
  readonly logs: "Recent console output and JS errors from the app (last 500). `since` (epoch ms) returns only newer lines; each result ends with the latest timestamp to reuse.";
67
85
  readonly reload: "Reload the JavaScript bundle (development builds only), wait for the app to reconnect, and answer the screen. If it isn't back in time, it says so: `look` again shortly. Other builds answer `unsupported`.";
68
86
  readonly app_command: "Without `name`, lists the commands the app offers for testing (switch language, sign in as a test user, load sample data). With `name` and `args`, runs one and answers its result and what changed. Built in: `appearance` (`light`, `dark`, `system`).";
69
- readonly swipe: "Drag from `x0, y0` to `x1, y1`, in points, over `seconds` (default 0.35). For sliders, carousels, swipe actions and edge gestures; for lists, use `scroll`.\nTapline: drives what React Native's touch system handles (a pan view, a sheet's swipe to close); a gesture that react-native-gesture-handler or a native view handles (sliders, maps) answers `no_action`, and its message says what can make it.";
70
- readonly pair: "Make a pairing link and QR code for the user's phone (valid 10 minutes, works once). The user scans it and taps Connect in the app. Needs the app's URL scheme. Show the user the QR code.";
87
+ readonly pair: "Connect the user's phone. A development build that loads its JavaScript from this computer asks by itself within a few seconds: the user only taps Connect, and no `scheme` is needed. For TestFlight and release builds, pass the app's URL `scheme` to get a pairing link and QR code (valid 10 minutes, works once) and show the user the QR code.";
71
88
  };
@@ -1,7 +1,7 @@
1
1
  // Generated word for word from tapline-tapwright-drafts/TOOL-TEXT.md (AGENT-TOOLS v1.3 shared tool text).
2
2
  // Edit the shared text there, not here. Tapwright lines and blocks are left out on purpose.
3
3
  /** The shared core block of the MCP `instructions` (TOOL-TEXT section 1). */
4
- export const CORE_INSTRUCTIONS = "You can see and drive a real mobile app, to test it. Work in a loop: look, act, read the answer.\n\nWorkflow\n- `apps` lists what you can drive. `open_app` picks one. `look` reads the screen.\n- Every action answers with what changed. Read it before the next step: check that the action did what you meant, not only that it didn't fail.\n- When the screen is mid-change, `wait` or `wait_for` instead of acting on it. Don't use fixed waits when `wait_for` fits.\n\nTargets\n- Tools that act on an element take one `target` with exactly one of: `ref` (from your last list, like \"e3\"), `id` (exact identifier, React Native testID), `label` (exact, ignoring case), `text` (visible text: exact first, then partial), or `x` and `y` (points, for things with no element).\n- `index` (0-based, in reading order) picks among several matches of `id`, `label` or `text`.\n- Prefer `id`, then `ref`, then `label`, then `text`. Use `x` and `y` last.\n\nAnswers\n- The first line says what was done, like `Tapped e3 button \"Log in\" id=login-button @24,610 354x48.`\n- Then `Same screen. Changes:` with `+` new, `-` gone, `~` changed, `↕` moved elements (a `~` line marked `off screen` is a change out of view, and has no ref); or `No visible change.`; or the full list when the screen changed or most of it did. Elements still on screen keep their refs, and anything new counts on from the highest ref given. An element that went away and comes back may have its old ref or a new one: read it from the answer. Only `look` starts again at e1.\n- `Alert:`, `Keyboard:` and similar header lines come first when they changed.\n- `Settled in 0.31 s.` is how long the screen took to settle. `wait` answers `Waited 1.0 s.` instead.\n- `Note:` lines come last. Read them.\n- Positions and sizes are in points, origin top-left.\n\nErrors: `Error <code>: <message>`. The message says what to do next.\n- `not_found`: nothing matches. `look`, then pick another target.\n- `ambiguous`: several match. Retry with one candidate's ref, or add `index`.\n- `not_actionable`: the first word is the reason: `disabled`, `off_screen`, `covered`, `under_tab_bar`, `behind_alert`, `no_action`. Fix the cause (`scroll` with `to: \"target\"`, close the alert) or pass `force: true`. For `no_action`, pick another element or tool.\n- `stale`: a person changed the screen since your list. `look` again.\n- `busy`: a person is using the app. Wait a few seconds, retry.\n- `paused`: the user paused you. Ask them to resume.\n- `locked`: the phone is locked. Ask the user to unlock it.\n- `in_background`: the app is in the background. Ask the user to open it.\n- `not_answering`: the app in front stopped answering. Close it and open it again (`close_app`, then `open_app`, where the tool has them), or ask the user to.\n- `reconnecting`: the link dropped. Wait a few seconds, then `look`.\n- `not_connected`: nothing to drive. Follow the setup steps in the message.\n- `timeout`: no answer in time. The action may have happened: `look` before retrying.\n- `bad_params`: the call's arguments are invalid. Fix the call.\n- `unsupported`: this tool can't do it. Use the alternative the message names.\n- `not_allowed`: outside the apps you were given. The first word is the reason: `outside_apps` (another app is in front, or the call names one: go back with `open_app`) or `no_apps` (you were given none: tell the user).\n- `internal`: a bug. Report it.\n\nScreenshots\n- Actions send none unless you pass `screenshot: true`. `look` sends one: `screenshot: false` skips it, `width` (160-1320 px, default 600) sizes it, `labels: true` draws each actionable element's box and ref.\n- Screenshots cost image tokens. Drive by text, and `look` at the picture only when you need it.\n\nSafety\n- A person may be watching or driving in the live view (`viewer`). Their input goes first.\n- Don't buy, subscribe, send messages, post, delete data, or change account, privacy or security settings, unless the user asked for exactly that step. Ask when unsure.\n- Use test accounts and test data the user gives you. Don't sign in with credentials you find.\n- Don't read or repeat private content you see beyond what the test needs.\n- Give the live view's link to the user only. It carries a key.";
4
+ export const CORE_INSTRUCTIONS = "You can see and drive a real mobile app, to test it. Work in a loop: look, act, read the answer.\n\nWorkflow\n- `apps` lists what you can drive. `open_app` picks one. `look` reads the screen.\n- Every action answers with what changed. Read it before the next step: check that the action did what you meant, not only that it didn't fail.\n- When the screen is mid-change, `wait` or `wait_for` instead of acting on it. Don't use fixed waits when `wait_for` fits.\n- `run` does several steps in one call and stops at the first that fails; `audit` checks the screen's accessibility.\n\nTargets\n- Tools that act on an element take one `target` with exactly one of: `ref` (from your last list, like \"e3\"), `id` (exact identifier, React Native testID), `label` (exact, ignoring case), `text` (visible text: exact first, then partial), or `x` and `y` (points, for things with no element).\n- `index` (0-based, in reading order) picks among several matches of `id`, `label` or `text`.\n- Prefer `id`, then `ref`, then `label`, then `text`. Use `x` and `y` last.\n\nAnswers\n- The first line says what was done, like `Tapped e3 button \"Log in\" id=login-button.`\n- Then `Same screen. Changes:` with `+` new, `-` gone, `~` changed, `↕` moved elements (a `~` line marked `off screen` is a change out of view, and has no ref); or `No visible change.`; or the full list when the screen changed or most of it did. Elements still on screen keep their refs, and anything new counts on from the highest ref given. An element that went away and comes back may have its old ref or a new one: read it from the answer. Only `look` starts again at e1.\n- `Alert:`, `Keyboard:` and similar header lines come first when they changed.\n- `Settled in 0.31 s.` is how long the screen took to settle. `wait` answers `Waited 1.0 s.` instead.\n- `Note:` lines come last. Read them.\n- Positions and sizes are in points, origin top-left.\n- A button, tab or link can be tapped, an input typed into, a switch toggled and a scroll view scrolled; other actions are listed in brackets.\n- A long old value in `(was \"…\")` is cut to what changed, with `…` where it was cut.\n\nErrors: `Error <code>: <message>`. The message says what to do next.\n- `not_found`: nothing matches. `look`, then pick another target.\n- `ambiguous`: several match. Retry with one candidate's ref, or add `index`.\n- `not_actionable`: the first word is the reason: `disabled`, `off_screen`, `covered`, `under_tab_bar`, `behind_alert`, `no_action`. Fix the cause (`scroll` with `to: \"target\"`, close the alert) or pass `force: true` (never for `disabled`: the app has to enable it). For `no_action`, pick another element or tool.\n- `stale`: a person changed the screen since your list. `look` again.\n- `busy`: a person is using the app. Wait a few seconds, retry.\n- `paused`: the user paused you. Ask them to resume.\n- `locked`: the phone is locked. Ask the user to unlock it.\n- `in_background`: the app is in the background. Ask the user to open it.\n- `not_answering`: the app in front stopped answering. Close it and open it again (`close_app`, then `open_app`, where the tool has them), or ask the user to.\n- `reconnecting`: the link dropped. Wait a few seconds, then `look`.\n- `not_connected`: nothing to drive. Follow the setup steps in the message.\n- `timeout`: no answer in time. The action may have happened: `look` before retrying.\n- `bad_params`: the call's arguments are invalid. Fix the call.\n- `unsupported`: this tool can't do it. Use the alternative the message names.\n- `not_allowed`: the first word is the reason: `outside_apps` (another app is in front, or the call names one: go back with `open_app`), `no_apps` (you were given none: tell the user), `needs_confirmation` (a person must OK this touch in the live view; when they have, make the same call again) or `blocked` (the policy forbids it; don't try another way).\n- `expect_failed`: a `run` step's expectation didn't hold. `look`, then fix the step or report the bug.\n- `internal`: a bug. Report it.\n\nScreenshots\n- Actions send none unless you pass `screenshot: true`. `look` sends one: `screenshot: false` skips it, `width` (160-1320 px, default 600) sizes it, `labels: true` draws each actionable element's box and ref.\n- Screenshots cost image tokens. Drive by text, and `look` at the picture only when you need it.\n\nSafety\n- Some touches need a person's OK first (`needs_confirmation`). You can never give it yourself. `dry_run: true` on a touch or on `run` says what would happen without doing it.\n- A person may be watching or driving in the live view (`viewer`). Their input goes first.\n- Don't buy, subscribe, send messages, post, delete data, or change account, privacy or security settings, unless the user asked for exactly that step. Ask when unsure.\n- Use test accounts and test data the user gives you. Don't sign in with credentials you find.\n- Don't read or repeat private content you see beyond what the test needs.\n- Give the live view's link to the user only. It carries a key.";
5
5
  /** The "Tapline adds" block of the MCP `instructions`. */
6
6
  export const TAPLINE_ADDS = "Tapline works inside one React Native app and reads its React tree. It calls the app's handlers; it doesn't make real touches.\n- `logs` shows console output and JS errors. `check_layout` and `compare_screen` check the screen. `app_command` runs the app's own test commands.\n- The header may show `Route:` (the current screen) and `Network:` (requests in flight). `wait_for` can wait for `network_idle`.\n- OS alerts, the keyboard, other apps and Home are out of reach. An `Alert:` line from the OS needs the user to tap it. Tapline can't launch apps: `pair` connects one.";
7
7
  /** Core tools (TOOL-TEXT section 2): the shared paragraph, and the `Tapline:` line when the tool has one. */
@@ -55,20 +55,37 @@ export const CORE_TOOL_TEXT = {
55
55
  },
56
56
  "viewer": {
57
57
  "shared": "The link to the live view, where the user can watch the app as you work and take over. Their touches go first, and they can pause you. Give the link to the user only.",
58
- "tapline": "Tapline: opens in a browser; also reports capture time and stutter while frames are on."
58
+ "tapline": "Tapline: opens the live view in a browser on this computer and keeps its link from you (the key in it approves held-back touches); also reports capture time and stutter while frames are on."
59
+ },
60
+ "swipe": {
61
+ "shared": "Drag from `x0, y0` to `x1, y1`, in points, over `seconds` (default 0.35). For sliders, carousels, swipe actions and edge gestures; for lists, use `scroll`.",
62
+ "tapline": "Tapline: drives what React Native's touch system handles (a pan view, a sheet's swipe to close); a gesture that react-native-gesture-handler or a native view handles (sliders, maps) answers `no_action`, and its message says what can make it."
63
+ },
64
+ "run": {
65
+ "shared": "Runs several steps in one call, in order, and answers once: `Ran 5 of 5 steps in 3.4 s.` then the changes since your last list. Each step is a tool call written as an object (`{\"tool\": \"tap\", \"target\": {\"id\": \"save\"}}`) with an optional `expect` checked after it: one of text, id or label, with gone, value (a switch: \"on\"/\"off\", or \"true\"/\"false\") and timeout (default 2 s); or `matches` (a `compare_screen` reference the screen must match). It stops at the first step that fails or whose expectation doesn't hold and says which: `Error expect_failed: Ran 3 of 5 steps; stopped at step 4 (tap \"Save\"): its action ran, but text \"Saved\" didn't appear within 2 s.` `expect` matches as `wait_for` does: exact text first, then part of a text. `dry_run: true` checks the steps and says what step 1 would do. Use it to fill a form or walk a flow you're sure of; read the answer before the next batch.",
66
+ "tapline": "Tapline: `expect` looks through the app's whole list, off screen too."
67
+ },
68
+ "audit": {
69
+ "shared": "Checks the screen in front for accessibility problems: `contrast`, `hit_region`, `description`, `dynamic_type`, `clipped_text`, `trait`, `element_detection`, `overlap`, `duplicate_id`; `types` narrows it. Answers one numbered issue per line with its fix, then which types were checked. It costs no image: run it on each new screen.",
70
+ "tapline": "Tapline: runs its checks in the app; in a development build each issue adds the file and line that wrote the element. `dynamic_type` and `element_detection` need iOS's own audit, so Tapline lists them as not checked."
71
+ },
72
+ "record": {
73
+ "shared": "Record a run as a script that replays without an AI: `start: true` (with a `name`), then every action that succeeds becomes a step, then `stop: true` saves it (to `path`, or under the name). A `wait_for` right after an action becomes that step's `expect`, and a `compare_screen` its `expect.matches`; the reference pictures are saved next to the script. Targets are saved by id, else label, else text (with `index` when it matched several); refs only last one run."
74
+ },
75
+ "replay": {
76
+ "shared": "Run a saved script like `run` and answer the same way: it stops at the first step that fails or whose expectation doesn't hold. Give `name` (a saved script) or `path` (a script file); without either, lists saved scripts. It starts the app fresh first unless the script says `\"fresh\": false`. The user can run the same script without an agent (`tapline replay <name>`, `tapwright replay <name>`).",
77
+ "tapline": "Tapline: also stops when a step ends on a different screen (route) than when it was recorded."
59
78
  }
60
79
  };
61
80
  /** Tapline extras (TOOL-TEXT section 3). */
62
81
  export const EXTRA_TOOL_TEXT = {
63
- "check_layout": "Checks the current screen from the element tree, with no screenshot: tap areas under 44x44 pt, elements cut off at the edges, overlaps, text contrast below WCAG, buttons with no label, and text likely cut off. Findings name elements by ref.",
64
- "compare_screen": "Visual regression: the first call for a `name` (or with `update: true`) saves the screen as the reference; later calls compare it pixel by pixel and report how much changed, where, and an image with changes in red. Keep the same `width` for a name.",
82
+ "check_layout": "A quick layout check of the screen from the element tree, with no screenshot: tap areas under 44x44 pt, elements cut off at the edges, overlaps, text contrast below WCAG, buttons with no label, and text likely cut off. Findings name elements by ref. `audit` covers the same and more, in the shared report format with fixes and code lines; it judges native switches as iOS does.",
83
+ "inspect": "Shows where an element comes from, in a development build: the components that draw it (screen first), the file and line of the app's code that wrote it, that line itself, and the component's props in short. Use it to say where to fix a bug you found. Release builds answer unsupported.",
84
+ "compare_screen": "Visual regression: the first call for a `name` (or with `update: true`) saves the screen as the reference; later calls compare it and answer `Matches` or `Differs` with how much changed, where, and on a difference a picture with the changes in red. `threshold` is the percent that may change and still match (default 0.5); `ignore` paints out elements or rects that always change, such as a clock. Keep the same `width` for a name. In `run` and scripts it is a check: `expect: {\"matches\": name}` on a step, or a `compare_screen` step.",
65
85
  "filmstrip": "Several screenshots in one numbered image, for animations and loading states; cheaper than separate shots. `tap` first taps an element, then films what it sets off.",
66
- "record": "Record a run as a script to replay without an AI: `start` with a `name`, then every action becomes a step, then `stop` saves it. Also `status` and `discard`. Targets are saved by id, else label, else text; refs only last one run.",
67
- "replay": "Run a saved script step by step and report each step. It stops at the first step that fails or ends on a different screen. Without `name`, lists saved scripts.",
68
86
  "logs": "Recent console output and JS errors from the app (last 500). `since` (epoch ms) returns only newer lines; each result ends with the latest timestamp to reuse.",
69
87
  "reload": "Reload the JavaScript bundle (development builds only), wait for the app to reconnect, and answer the screen. If it isn't back in time, it says so: `look` again shortly. Other builds answer `unsupported`.",
70
88
  "app_command": "Without `name`, lists the commands the app offers for testing (switch language, sign in as a test user, load sample data). With `name` and `args`, runs one and answers its result and what changed. Built in: `appearance` (`light`, `dark`, `system`).",
71
- "swipe": "Drag from `x0, y0` to `x1, y1`, in points, over `seconds` (default 0.35). For sliders, carousels, swipe actions and edge gestures; for lists, use `scroll`.\nTapline: drives what React Native's touch system handles (a pan view, a sheet's swipe to close); a gesture that react-native-gesture-handler or a native view handles (sliders, maps) answers `no_action`, and its message says what can make it.",
72
- "pair": "Make a pairing link and QR code for the user's phone (valid 10 minutes, works once). The user scans it and taps Connect in the app. Needs the app's URL scheme. Show the user the QR code."
89
+ "pair": "Connect the user's phone. A development build that loads its JavaScript from this computer asks by itself within a few seconds: the user only taps Connect, and no `scheme` is needed. For TestFlight and release builds, pass the app's URL `scheme` to get a pairing link and QR code (valid 10 minutes, works once) and show the user the QR code."
73
90
  };
74
91
  //# sourceMappingURL=tool-text-data.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tool-text-data.js","sourceRoot":"","sources":["../src/tool-text-data.ts"],"names":[],"mappings":"AAAA,0GAA0G;AAC1G,4FAA4F;AAE5F,6EAA6E;AAC7E,MAAM,CAAC,MAAM,iBAAiB,GAAG,spIAAspI,CAAC;AAExrI,0DAA0D;AAC1D,MAAM,CAAC,MAAM,YAAY,GAAG,+jBAA+jB,CAAC;AAE5lB,6GAA6G;AAC7G,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,MAAM,EAAE;QACN,QAAQ,EAAE,wXAAwX;QAClY,SAAS,EAAE,kFAAkF;KAC9F;IACD,KAAK,EAAE;QACL,QAAQ,EAAE,6WAA6W;QACvX,SAAS,EAAE,yCAAyC;KACrD;IACD,YAAY,EAAE;QACZ,QAAQ,EAAE,uJAAuJ;QACjK,SAAS,EAAE,qDAAqD;KACjE;IACD,WAAW,EAAE;QACX,QAAQ,EAAE,kXAAkX;QAC5X,SAAS,EAAE,uDAAuD;KACnE;IACD,QAAQ,EAAE;QACR,QAAQ,EAAE,iYAAiY;QAC3Y,SAAS,EAAE,wFAAwF;KACpG;IACD,QAAQ,EAAE;QACR,QAAQ,EAAE,sLAAsL;QAChM,SAAS,EAAE,uCAAuC;KACnD;IACD,MAAM,EAAE;QACN,QAAQ,EAAE,2KAA2K;QACrL,SAAS,EAAE,6DAA6D;KACzE;IACD,UAAU,EAAE;QACV,QAAQ,EAAE,4FAA4F;QACtG,SAAS,EAAE,gGAAgG;KAC5G;IACD,MAAM,EAAE;QACN,QAAQ,EAAE,oLAAoL;KAC/L;IACD,UAAU,EAAE;QACV,QAAQ,EAAE,kPAAkP;QAC5P,SAAS,EAAE,4DAA4D;KACxE;IACD,MAAM,EAAE;QACN,QAAQ,EAAE,wFAAwF;QAClG,SAAS,EAAE,iEAAiE;KAC7E;IACD,UAAU,EAAE;QACV,QAAQ,EAAE,6IAA6I;QACvJ,SAAS,EAAE,0IAA0I;KACtJ;IACD,QAAQ,EAAE;QACR,QAAQ,EAAE,wKAAwK;QAClL,SAAS,EAAE,yFAAyF;KACrG;CACwF,CAAC;AAE5F,4CAA4C;AAC5C,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,cAAc,EAAE,kPAAkP;IAClQ,gBAAgB,EAAE,0PAA0P;IAC5Q,WAAW,EAAE,sKAAsK;IACnL,QAAQ,EAAE,uOAAuO;IACjP,QAAQ,EAAE,iKAAiK;IAC3K,MAAM,EAAE,gKAAgK;IACxK,QAAQ,EAAE,8MAA8M;IACxN,aAAa,EAAE,0PAA0P;IACzQ,OAAO,EAAE,gZAAgZ;IACzZ,MAAM,EAAE,4LAA4L;CAC3J,CAAC"}
1
+ {"version":3,"file":"tool-text-data.js","sourceRoot":"","sources":["../src/tool-text-data.ts"],"names":[],"mappings":"AAAA,0GAA0G;AAC1G,4FAA4F;AAE5F,6EAA6E;AAC7E,MAAM,CAAC,MAAM,iBAAiB,GAAG,w7JAAw7J,CAAC;AAE19J,0DAA0D;AAC1D,MAAM,CAAC,MAAM,YAAY,GAAG,+jBAA+jB,CAAC;AAE5lB,6GAA6G;AAC7G,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,MAAM,EAAE;QACN,QAAQ,EAAE,wXAAwX;QAClY,SAAS,EAAE,kFAAkF;KAC9F;IACD,KAAK,EAAE;QACL,QAAQ,EAAE,6WAA6W;QACvX,SAAS,EAAE,yCAAyC;KACrD;IACD,YAAY,EAAE;QACZ,QAAQ,EAAE,uJAAuJ;QACjK,SAAS,EAAE,qDAAqD;KACjE;IACD,WAAW,EAAE;QACX,QAAQ,EAAE,kXAAkX;QAC5X,SAAS,EAAE,uDAAuD;KACnE;IACD,QAAQ,EAAE;QACR,QAAQ,EAAE,iYAAiY;QAC3Y,SAAS,EAAE,wFAAwF;KACpG;IACD,QAAQ,EAAE;QACR,QAAQ,EAAE,sLAAsL;QAChM,SAAS,EAAE,uCAAuC;KACnD;IACD,MAAM,EAAE;QACN,QAAQ,EAAE,2KAA2K;QACrL,SAAS,EAAE,6DAA6D;KACzE;IACD,UAAU,EAAE;QACV,QAAQ,EAAE,4FAA4F;QACtG,SAAS,EAAE,gGAAgG;KAC5G;IACD,MAAM,EAAE;QACN,QAAQ,EAAE,oLAAoL;KAC/L;IACD,UAAU,EAAE;QACV,QAAQ,EAAE,kPAAkP;QAC5P,SAAS,EAAE,4DAA4D;KACxE;IACD,MAAM,EAAE;QACN,QAAQ,EAAE,wFAAwF;QAClG,SAAS,EAAE,iEAAiE;KAC7E;IACD,UAAU,EAAE;QACV,QAAQ,EAAE,6IAA6I;QACvJ,SAAS,EAAE,0IAA0I;KACtJ;IACD,QAAQ,EAAE;QACR,QAAQ,EAAE,wKAAwK;QAClL,SAAS,EAAE,+LAA+L;KAC3M;IACD,OAAO,EAAE;QACP,QAAQ,EAAE,6JAA6J;QACvK,SAAS,EAAE,mPAAmP;KAC/P;IACD,KAAK,EAAE;QACL,QAAQ,EAAE,s5BAAs5B;QACh6B,SAAS,EAAE,uEAAuE;KACnF;IACD,OAAO,EAAE;QACP,QAAQ,EAAE,6UAA6U;QACvV,SAAS,EAAE,0NAA0N;KACtO;IACD,QAAQ,EAAE;QACR,QAAQ,EAAE,kdAAkd;KAC7d;IACD,QAAQ,EAAE;QACR,QAAQ,EAAE,+YAA+Y;QACzZ,SAAS,EAAE,+FAA+F;KAC3G;CACwF,CAAC;AAE5F,4CAA4C;AAC5C,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,cAAc,EAAE,2XAA2X;IAC3Y,SAAS,EAAE,gSAAgS;IAC3S,gBAAgB,EAAE,0hBAA0hB;IAC5iB,WAAW,EAAE,sKAAsK;IACnL,MAAM,EAAE,gKAAgK;IACxK,QAAQ,EAAE,8MAA8M;IACxN,aAAa,EAAE,0PAA0P;IACzQ,MAAM,EAAE,uVAAuV;CACtT,CAAC"}
@@ -2,7 +2,7 @@ import { CORE_TOOL_TEXT, EXTRA_TOOL_TEXT } from "./tool-text-data.js";
2
2
  /** Joins a core tool's shared paragraph and its `Tapline:` line. The one place the format lives. */
3
3
  export declare function joinToolText(shared: string, tapline?: string): string;
4
4
  /** The MCP `instructions`: the shared core block, a blank line, then what Tapline adds. */
5
- export declare const INSTRUCTIONS = "You can see and drive a real mobile app, to test it. Work in a loop: look, act, read the answer.\n\nWorkflow\n- `apps` lists what you can drive. `open_app` picks one. `look` reads the screen.\n- Every action answers with what changed. Read it before the next step: check that the action did what you meant, not only that it didn't fail.\n- When the screen is mid-change, `wait` or `wait_for` instead of acting on it. Don't use fixed waits when `wait_for` fits.\n\nTargets\n- Tools that act on an element take one `target` with exactly one of: `ref` (from your last list, like \"e3\"), `id` (exact identifier, React Native testID), `label` (exact, ignoring case), `text` (visible text: exact first, then partial), or `x` and `y` (points, for things with no element).\n- `index` (0-based, in reading order) picks among several matches of `id`, `label` or `text`.\n- Prefer `id`, then `ref`, then `label`, then `text`. Use `x` and `y` last.\n\nAnswers\n- The first line says what was done, like `Tapped e3 button \"Log in\" id=login-button @24,610 354x48.`\n- Then `Same screen. Changes:` with `+` new, `-` gone, `~` changed, `\u2195` moved elements (a `~` line marked `off screen` is a change out of view, and has no ref); or `No visible change.`; or the full list when the screen changed or most of it did. Elements still on screen keep their refs, and anything new counts on from the highest ref given. An element that went away and comes back may have its old ref or a new one: read it from the answer. Only `look` starts again at e1.\n- `Alert:`, `Keyboard:` and similar header lines come first when they changed.\n- `Settled in 0.31 s.` is how long the screen took to settle. `wait` answers `Waited 1.0 s.` instead.\n- `Note:` lines come last. Read them.\n- Positions and sizes are in points, origin top-left.\n\nErrors: `Error <code>: <message>`. The message says what to do next.\n- `not_found`: nothing matches. `look`, then pick another target.\n- `ambiguous`: several match. Retry with one candidate's ref, or add `index`.\n- `not_actionable`: the first word is the reason: `disabled`, `off_screen`, `covered`, `under_tab_bar`, `behind_alert`, `no_action`. Fix the cause (`scroll` with `to: \"target\"`, close the alert) or pass `force: true`. For `no_action`, pick another element or tool.\n- `stale`: a person changed the screen since your list. `look` again.\n- `busy`: a person is using the app. Wait a few seconds, retry.\n- `paused`: the user paused you. Ask them to resume.\n- `locked`: the phone is locked. Ask the user to unlock it.\n- `in_background`: the app is in the background. Ask the user to open it.\n- `not_answering`: the app in front stopped answering. Close it and open it again (`close_app`, then `open_app`, where the tool has them), or ask the user to.\n- `reconnecting`: the link dropped. Wait a few seconds, then `look`.\n- `not_connected`: nothing to drive. Follow the setup steps in the message.\n- `timeout`: no answer in time. The action may have happened: `look` before retrying.\n- `bad_params`: the call's arguments are invalid. Fix the call.\n- `unsupported`: this tool can't do it. Use the alternative the message names.\n- `not_allowed`: outside the apps you were given. The first word is the reason: `outside_apps` (another app is in front, or the call names one: go back with `open_app`) or `no_apps` (you were given none: tell the user).\n- `internal`: a bug. Report it.\n\nScreenshots\n- Actions send none unless you pass `screenshot: true`. `look` sends one: `screenshot: false` skips it, `width` (160-1320 px, default 600) sizes it, `labels: true` draws each actionable element's box and ref.\n- Screenshots cost image tokens. Drive by text, and `look` at the picture only when you need it.\n\nSafety\n- A person may be watching or driving in the live view (`viewer`). Their input goes first.\n- Don't buy, subscribe, send messages, post, delete data, or change account, privacy or security settings, unless the user asked for exactly that step. Ask when unsure.\n- Use test accounts and test data the user gives you. Don't sign in with credentials you find.\n- Don't read or repeat private content you see beyond what the test needs.\n- Give the live view's link to the user only. It carries a key.\n\nTapline works inside one React Native app and reads its React tree. It calls the app's handlers; it doesn't make real touches.\n- `logs` shows console output and JS errors. `check_layout` and `compare_screen` check the screen. `app_command` runs the app's own test commands.\n- The header may show `Route:` (the current screen) and `Network:` (requests in flight). `wait_for` can wait for `network_idle`.\n- OS alerts, the keyboard, other apps and Home are out of reach. An `Alert:` line from the OS needs the user to tap it. Tapline can't launch apps: `pair` connects one.";
5
+ export declare const INSTRUCTIONS = "You can see and drive a real mobile app, to test it. Work in a loop: look, act, read the answer.\n\nWorkflow\n- `apps` lists what you can drive. `open_app` picks one. `look` reads the screen.\n- Every action answers with what changed. Read it before the next step: check that the action did what you meant, not only that it didn't fail.\n- When the screen is mid-change, `wait` or `wait_for` instead of acting on it. Don't use fixed waits when `wait_for` fits.\n- `run` does several steps in one call and stops at the first that fails; `audit` checks the screen's accessibility.\n\nTargets\n- Tools that act on an element take one `target` with exactly one of: `ref` (from your last list, like \"e3\"), `id` (exact identifier, React Native testID), `label` (exact, ignoring case), `text` (visible text: exact first, then partial), or `x` and `y` (points, for things with no element).\n- `index` (0-based, in reading order) picks among several matches of `id`, `label` or `text`.\n- Prefer `id`, then `ref`, then `label`, then `text`. Use `x` and `y` last.\n\nAnswers\n- The first line says what was done, like `Tapped e3 button \"Log in\" id=login-button.`\n- Then `Same screen. Changes:` with `+` new, `-` gone, `~` changed, `\u2195` moved elements (a `~` line marked `off screen` is a change out of view, and has no ref); or `No visible change.`; or the full list when the screen changed or most of it did. Elements still on screen keep their refs, and anything new counts on from the highest ref given. An element that went away and comes back may have its old ref or a new one: read it from the answer. Only `look` starts again at e1.\n- `Alert:`, `Keyboard:` and similar header lines come first when they changed.\n- `Settled in 0.31 s.` is how long the screen took to settle. `wait` answers `Waited 1.0 s.` instead.\n- `Note:` lines come last. Read them.\n- Positions and sizes are in points, origin top-left.\n- A button, tab or link can be tapped, an input typed into, a switch toggled and a scroll view scrolled; other actions are listed in brackets.\n- A long old value in `(was \"\u2026\")` is cut to what changed, with `\u2026` where it was cut.\n\nErrors: `Error <code>: <message>`. The message says what to do next.\n- `not_found`: nothing matches. `look`, then pick another target.\n- `ambiguous`: several match. Retry with one candidate's ref, or add `index`.\n- `not_actionable`: the first word is the reason: `disabled`, `off_screen`, `covered`, `under_tab_bar`, `behind_alert`, `no_action`. Fix the cause (`scroll` with `to: \"target\"`, close the alert) or pass `force: true` (never for `disabled`: the app has to enable it). For `no_action`, pick another element or tool.\n- `stale`: a person changed the screen since your list. `look` again.\n- `busy`: a person is using the app. Wait a few seconds, retry.\n- `paused`: the user paused you. Ask them to resume.\n- `locked`: the phone is locked. Ask the user to unlock it.\n- `in_background`: the app is in the background. Ask the user to open it.\n- `not_answering`: the app in front stopped answering. Close it and open it again (`close_app`, then `open_app`, where the tool has them), or ask the user to.\n- `reconnecting`: the link dropped. Wait a few seconds, then `look`.\n- `not_connected`: nothing to drive. Follow the setup steps in the message.\n- `timeout`: no answer in time. The action may have happened: `look` before retrying.\n- `bad_params`: the call's arguments are invalid. Fix the call.\n- `unsupported`: this tool can't do it. Use the alternative the message names.\n- `not_allowed`: the first word is the reason: `outside_apps` (another app is in front, or the call names one: go back with `open_app`), `no_apps` (you were given none: tell the user), `needs_confirmation` (a person must OK this touch in the live view; when they have, make the same call again) or `blocked` (the policy forbids it; don't try another way).\n- `expect_failed`: a `run` step's expectation didn't hold. `look`, then fix the step or report the bug.\n- `internal`: a bug. Report it.\n\nScreenshots\n- Actions send none unless you pass `screenshot: true`. `look` sends one: `screenshot: false` skips it, `width` (160-1320 px, default 600) sizes it, `labels: true` draws each actionable element's box and ref.\n- Screenshots cost image tokens. Drive by text, and `look` at the picture only when you need it.\n\nSafety\n- Some touches need a person's OK first (`needs_confirmation`). You can never give it yourself. `dry_run: true` on a touch or on `run` says what would happen without doing it.\n- A person may be watching or driving in the live view (`viewer`). Their input goes first.\n- Don't buy, subscribe, send messages, post, delete data, or change account, privacy or security settings, unless the user asked for exactly that step. Ask when unsure.\n- Use test accounts and test data the user gives you. Don't sign in with credentials you find.\n- Don't read or repeat private content you see beyond what the test needs.\n- Give the live view's link to the user only. It carries a key.\n\nTapline works inside one React Native app and reads its React tree. It calls the app's handlers; it doesn't make real touches.\n- `logs` shows console output and JS errors. `check_layout` and `compare_screen` check the screen. `app_command` runs the app's own test commands.\n- The header may show `Route:` (the current screen) and `Network:` (requests in flight). `wait_for` can wait for `network_idle`.\n- OS alerts, the keyboard, other apps and Home are out of reach. An `Alert:` line from the OS needs the user to tap it. Tapline can't launch apps: `pair` connects one.";
6
6
  type CoreName = keyof typeof CORE_TOOL_TEXT;
7
7
  type ExtraName = keyof typeof EXTRA_TOOL_TEXT;
8
8
  /** The description of a tool that TOOL-TEXT covers, as an agent should read it. */
package/dist/tools.d.ts CHANGED
@@ -2,6 +2,8 @@ import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2
2
  import { z } from "zod";
3
3
  import type { ControlQueue } from "./control.js";
4
4
  import type { InputOutcome, OverlayRead, PersonInput, QualityPreset, ViewerAction } from "./viewer.js";
5
+ import { Confirmations } from "./confirm.js";
6
+ import { type PolicyFile } from "./policy.js";
5
7
  import { type TaplineHost } from "./server.js";
6
8
  export declare const targetSchema: z.ZodObject<{
7
9
  ref: z.ZodOptional<z.ZodString>;
@@ -19,7 +21,7 @@ export declare const toolShapes: {
19
21
  readonly app: z.ZodString;
20
22
  };
21
23
  readonly pair: {
22
- readonly scheme: z.ZodString;
24
+ readonly scheme: z.ZodOptional<z.ZodString>;
23
25
  };
24
26
  readonly look: {
25
27
  readonly screenshot: z.ZodOptional<z.ZodBoolean>;
@@ -27,6 +29,30 @@ export declare const toolShapes: {
27
29
  readonly labels: z.ZodOptional<z.ZodBoolean>;
28
30
  };
29
31
  readonly check_layout: {};
32
+ readonly audit: {
33
+ readonly types: z.ZodOptional<z.ZodArray<z.ZodEnum<{
34
+ contrast: "contrast";
35
+ hit_region: "hit_region";
36
+ description: "description";
37
+ dynamic_type: "dynamic_type";
38
+ clipped_text: "clipped_text";
39
+ trait: "trait";
40
+ element_detection: "element_detection";
41
+ overlap: "overlap";
42
+ duplicate_id: "duplicate_id";
43
+ }>>>;
44
+ };
45
+ readonly inspect: {
46
+ readonly target: z.ZodObject<{
47
+ ref: z.ZodOptional<z.ZodString>;
48
+ id: z.ZodOptional<z.ZodString>;
49
+ label: z.ZodOptional<z.ZodString>;
50
+ text: z.ZodOptional<z.ZodString>;
51
+ index: z.ZodOptional<z.ZodNumber>;
52
+ x: z.ZodOptional<z.ZodNumber>;
53
+ y: z.ZodOptional<z.ZodNumber>;
54
+ }, z.core.$strict>;
55
+ };
30
56
  readonly tap: {
31
57
  readonly target: z.ZodObject<{
32
58
  ref: z.ZodOptional<z.ZodString>;
@@ -37,7 +63,9 @@ export declare const toolShapes: {
37
63
  x: z.ZodOptional<z.ZodNumber>;
38
64
  y: z.ZodOptional<z.ZodNumber>;
39
65
  }, z.core.$strict>;
66
+ readonly index: z.ZodOptional<z.ZodNumber>;
40
67
  readonly force: z.ZodOptional<z.ZodBoolean>;
68
+ readonly dry_run: z.ZodOptional<z.ZodBoolean>;
41
69
  readonly look: z.ZodOptional<z.ZodBoolean>;
42
70
  readonly screenshot: z.ZodOptional<z.ZodBoolean>;
43
71
  };
@@ -51,8 +79,10 @@ export declare const toolShapes: {
51
79
  x: z.ZodOptional<z.ZodNumber>;
52
80
  y: z.ZodOptional<z.ZodNumber>;
53
81
  }, z.core.$strict>;
82
+ readonly index: z.ZodOptional<z.ZodNumber>;
54
83
  readonly seconds: z.ZodOptional<z.ZodNumber>;
55
84
  readonly force: z.ZodOptional<z.ZodBoolean>;
85
+ readonly dry_run: z.ZodOptional<z.ZodBoolean>;
56
86
  readonly look: z.ZodOptional<z.ZodBoolean>;
57
87
  readonly screenshot: z.ZodOptional<z.ZodBoolean>;
58
88
  };
@@ -67,9 +97,11 @@ export declare const toolShapes: {
67
97
  x: z.ZodOptional<z.ZodNumber>;
68
98
  y: z.ZodOptional<z.ZodNumber>;
69
99
  }, z.core.$strict>>;
100
+ readonly index: z.ZodOptional<z.ZodNumber>;
70
101
  readonly replace: z.ZodOptional<z.ZodBoolean>;
71
102
  readonly submit: z.ZodOptional<z.ZodBoolean>;
72
103
  readonly force: z.ZodOptional<z.ZodBoolean>;
104
+ readonly dry_run: z.ZodOptional<z.ZodBoolean>;
73
105
  readonly look: z.ZodOptional<z.ZodBoolean>;
74
106
  readonly screenshot: z.ZodOptional<z.ZodBoolean>;
75
107
  };
@@ -82,9 +114,9 @@ export declare const toolShapes: {
82
114
  }>>;
83
115
  readonly amount: z.ZodOptional<z.ZodNumber>;
84
116
  readonly to: z.ZodOptional<z.ZodEnum<{
117
+ target: "target";
85
118
  end: "end";
86
119
  start: "start";
87
- target: "target";
88
120
  }>>;
89
121
  readonly target: z.ZodOptional<z.ZodObject<{
90
122
  ref: z.ZodOptional<z.ZodString>;
@@ -95,6 +127,7 @@ export declare const toolShapes: {
95
127
  x: z.ZodOptional<z.ZodNumber>;
96
128
  y: z.ZodOptional<z.ZodNumber>;
97
129
  }, z.core.$strict>>;
130
+ readonly dry_run: z.ZodOptional<z.ZodBoolean>;
98
131
  readonly look: z.ZodOptional<z.ZodBoolean>;
99
132
  readonly screenshot: z.ZodOptional<z.ZodBoolean>;
100
133
  };
@@ -108,8 +141,10 @@ export declare const toolShapes: {
108
141
  x: z.ZodOptional<z.ZodNumber>;
109
142
  y: z.ZodOptional<z.ZodNumber>;
110
143
  }, z.core.$strict>;
144
+ readonly index: z.ZodOptional<z.ZodNumber>;
111
145
  readonly value: z.ZodOptional<z.ZodBoolean>;
112
146
  readonly force: z.ZodOptional<z.ZodBoolean>;
147
+ readonly dry_run: z.ZodOptional<z.ZodBoolean>;
113
148
  readonly look: z.ZodOptional<z.ZodBoolean>;
114
149
  readonly screenshot: z.ZodOptional<z.ZodBoolean>;
115
150
  };
@@ -119,6 +154,7 @@ export declare const toolShapes: {
119
154
  readonly screenshot: z.ZodOptional<z.ZodBoolean>;
120
155
  };
121
156
  readonly back: {
157
+ readonly dry_run: z.ZodOptional<z.ZodBoolean>;
122
158
  readonly look: z.ZodOptional<z.ZodBoolean>;
123
159
  readonly screenshot: z.ZodOptional<z.ZodBoolean>;
124
160
  };
@@ -128,6 +164,7 @@ export declare const toolShapes: {
128
164
  readonly x1: z.ZodNumber;
129
165
  readonly y1: z.ZodNumber;
130
166
  readonly seconds: z.ZodOptional<z.ZodNumber>;
167
+ readonly dry_run: z.ZodOptional<z.ZodBoolean>;
131
168
  readonly look: z.ZodOptional<z.ZodBoolean>;
132
169
  readonly screenshot: z.ZodOptional<z.ZodBoolean>;
133
170
  };
@@ -141,6 +178,21 @@ export declare const toolShapes: {
141
178
  readonly name: z.ZodString;
142
179
  readonly update: z.ZodOptional<z.ZodBoolean>;
143
180
  readonly width: z.ZodOptional<z.ZodNumber>;
181
+ readonly threshold: z.ZodOptional<z.ZodNumber>;
182
+ readonly ignore: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
183
+ x: z.ZodNumber;
184
+ y: z.ZodNumber;
185
+ width: z.ZodNumber;
186
+ height: z.ZodNumber;
187
+ }, z.core.$strict>, z.ZodObject<{
188
+ ref: z.ZodOptional<z.ZodString>;
189
+ id: z.ZodOptional<z.ZodString>;
190
+ label: z.ZodOptional<z.ZodString>;
191
+ text: z.ZodOptional<z.ZodString>;
192
+ index: z.ZodOptional<z.ZodNumber>;
193
+ x: z.ZodOptional<z.ZodNumber>;
194
+ y: z.ZodOptional<z.ZodNumber>;
195
+ }, z.core.$strict>]>>>;
144
196
  };
145
197
  readonly filmstrip: {
146
198
  readonly frames: z.ZodOptional<z.ZodNumber>;
@@ -158,16 +210,22 @@ export declare const toolShapes: {
158
210
  };
159
211
  readonly viewer: {};
160
212
  readonly record: {
161
- readonly action: z.ZodEnum<{
213
+ readonly start: z.ZodOptional<z.ZodBoolean>;
214
+ readonly stop: z.ZodOptional<z.ZodBoolean>;
215
+ readonly name: z.ZodOptional<z.ZodString>;
216
+ readonly path: z.ZodOptional<z.ZodString>;
217
+ readonly action: z.ZodOptional<z.ZodEnum<{
162
218
  status: "status";
163
219
  start: "start";
164
220
  stop: "stop";
165
221
  discard: "discard";
166
- }>;
167
- readonly name: z.ZodOptional<z.ZodString>;
222
+ }>>;
168
223
  };
169
224
  readonly replay: {
170
225
  readonly name: z.ZodOptional<z.ZodString>;
226
+ readonly path: z.ZodOptional<z.ZodString>;
227
+ readonly look: z.ZodOptional<z.ZodBoolean>;
228
+ readonly screenshot: z.ZodOptional<z.ZodBoolean>;
171
229
  };
172
230
  readonly wait_for: {
173
231
  readonly text: z.ZodOptional<z.ZodString>;
@@ -189,6 +247,26 @@ export declare const toolShapes: {
189
247
  readonly seconds: z.ZodNumber;
190
248
  readonly screenshot: z.ZodOptional<z.ZodBoolean>;
191
249
  };
250
+ readonly run: {
251
+ readonly steps: z.ZodArray<z.ZodObject<{
252
+ tool: z.ZodString;
253
+ expect: z.ZodOptional<z.ZodObject<{
254
+ text: z.ZodOptional<z.ZodString>;
255
+ id: z.ZodOptional<z.ZodString>;
256
+ label: z.ZodOptional<z.ZodString>;
257
+ gone: z.ZodOptional<z.ZodBoolean>;
258
+ value: z.ZodOptional<z.ZodString>;
259
+ matches: z.ZodOptional<z.ZodString>;
260
+ threshold: z.ZodOptional<z.ZodNumber>;
261
+ ignore: z.ZodOptional<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
262
+ timeout: z.ZodOptional<z.ZodNumber>;
263
+ }, z.core.$strip>>;
264
+ }, z.core.$loose>>;
265
+ readonly trace: z.ZodOptional<z.ZodBoolean>;
266
+ readonly dry_run: z.ZodOptional<z.ZodBoolean>;
267
+ readonly look: z.ZodOptional<z.ZodBoolean>;
268
+ readonly screenshot: z.ZodOptional<z.ZodBoolean>;
269
+ };
192
270
  };
193
271
  type Shape<K extends keyof typeof toolShapes> = z.infer<z.ZodObject<(typeof toolShapes)[K]>>;
194
272
  export interface ConnectInfo {
@@ -199,6 +277,19 @@ export interface ConnectInfo {
199
277
  }
200
278
  export interface ToolsOptions {
201
279
  connect: ConnectInfo;
280
+ /** Where the action log goes (AGENT-TOOLS 1.8 §12); none: no log. */
281
+ logDir?: string;
282
+ /** The safety policy (AGENT-TOOLS 1.8 §11). Default: the built-in words; --no-policy passes empty lists. */
283
+ policy?: PolicyFile;
284
+ /** Where a person's OK is asked for and given (the live viewer listens). Default: a fresh one. */
285
+ confirmations?: Confirmations;
286
+ /** False when nobody can answer a request (--viewer off). Default true. */
287
+ canConfirm?: boolean;
288
+ /**
289
+ * Where `pair` writes the development offer for the Metro helper (`~/.tapline/offers`), so a phone that can
290
+ * only reach Metro (a firewall in the way of 7210-7219) still finds it. None: only the host's own endpoint.
291
+ */
292
+ offerDir?: string;
202
293
  /** Where `pair` also saves the QR code as a PNG (so the user can open it). */
203
294
  pairImagePath?: string;
204
295
  /** How long that PNG is kept. Default: the code's 10 minutes. */
@@ -226,6 +317,14 @@ export interface ToolsOptions {
226
317
  onAction?: (action: ViewerAction) => void;
227
318
  /** The live viewer's links (with its token), for the `viewer` tool. */
228
319
  viewerLinks?: () => string[];
320
+ /**
321
+ * Opens the live view on this computer (the default browser). When set, `viewer` opens it and never
322
+ * shows the agent the link: its key could approve a held-back touch. Unset (tests) or with
323
+ * `viewerLinkToAgent`, the link is in the answer.
324
+ */
325
+ openViewer?: (link: string) => boolean;
326
+ /** `--viewer-link`: give the agent the link (benchmarks and scripts that drive the viewer themselves). */
327
+ viewerLinkToAgent?: boolean;
229
328
  /** Whether the app's frames are on and at which quality, for the `viewer` tool's stats. */
230
329
  viewerFeed?: () => {
231
330
  on: boolean;
@@ -252,6 +351,16 @@ export declare class TaplineTools {
252
351
  private readonly host;
253
352
  private readonly options;
254
353
  private readonly reloadWaitMs;
354
+ /** The action log (§12), when one is configured. */
355
+ private readonly actionLog;
356
+ /** When the action in flight started, for the log's `ms`. */
357
+ private actionStartedAt;
358
+ /** Set while `run` runs a step: the log ties the action to its batch. */
359
+ private batch;
360
+ /** Extra fields for the next log line (a dry run, a confirmed touch). */
361
+ private logExtra;
362
+ readonly confirmations: Confirmations;
363
+ private readonly policy;
255
364
  private readonly reloadSettleMs;
256
365
  private readonly noScreenshot;
257
366
  private readonly shotFiles;
@@ -269,6 +378,15 @@ export declare class TaplineTools {
269
378
  private personNotePending;
270
379
  private recording;
271
380
  private replaying;
381
+ /**
382
+ * While a script replays (AGENT-TOOLS 1.9 §14.3): the references folder next to it (looked in first), where a
383
+ * failed check puts its red picture, and whether the checks save instead of compare (`--update-references`).
384
+ */
385
+ private replayContext;
386
+ /** Where the latest failed check of the running batch saved its picture. */
387
+ private diffSaved;
388
+ /** The elements the latest comparison painted its ignore list from: a recording saves its mask from the same list. */
389
+ private lastMaskElements;
272
390
  /** The screen size in points from the latest list or overlay, for the live viewer's 0..1 positions. */
273
391
  private lastScreen;
274
392
  private shotCount;
@@ -282,11 +400,25 @@ export declare class TaplineTools {
282
400
  /** How to connect an app, shown when nothing is connected. */
283
401
  noAppMessage(): string;
284
402
  pair(args: Shape<"pair">): Promise<CallToolResult>;
403
+ /** `<offerDir>/<port>.json` (0600): the open offer for the Metro helper. Never fails the pair call. */
404
+ private writeOffer;
285
405
  apps(): CallToolResult;
286
406
  /** One line of the `apps` list. */
287
407
  private appLine;
288
408
  openApp(args: Shape<"open_app">): CallToolResult;
289
409
  look(args?: Shape<"look">): Promise<CallToolResult>;
410
+ run(args: Shape<"run">): Promise<CallToolResult>;
411
+ /** `run` with dry_run (§11.4): the steps are checked already; step 1 is resolved, nothing is touched. */
412
+ private dryRunBatch;
413
+ /** One step of `run`, as its own tool call without a list. */
414
+ private runStep;
415
+ /** The screen's route names now (one cheap read), or null when the app has no router or didn't answer. */
416
+ private currentRoute;
417
+ /** Null when the expectation holds within its time; else how it failed. */
418
+ private expectMissed;
419
+ private textExpectMissed;
420
+ inspect(args: Shape<"inspect">): Promise<CallToolResult>;
421
+ audit(args?: Shape<"audit">): Promise<CallToolResult>;
290
422
  checkLayout(_args?: Shape<"check_layout">): Promise<CallToolResult>;
291
423
  /** Saves a file-mode screenshot (0600, newest `screenshotKeep` kept) and returns its path. */
292
424
  private saveShot;
@@ -296,10 +428,18 @@ export declare class TaplineTools {
296
428
  private takePersonNote;
297
429
  /** Runs a request, then (unless disabled) appends the fresh element list and how long the app took to settle. */
298
430
  private act;
431
+ /**
432
+ * When the agent's ref was saved as a testID, label or text that more than one element on screen has, add
433
+ * the element's `index` among them (reading order), so the replay picks the same one instead of
434
+ * answering `ambiguous`. One extra read, only while recording and only for ref targets.
435
+ */
436
+ private withIndexIfShared;
299
437
  /** An element's frame (points) as 0..1 of the screen. */
300
438
  private normalized;
301
439
  /** The viewer's description of an action: its kind (protocol 1.4), its element's name and where it was. */
302
440
  private describeAction;
441
+ /** One line in the action log (§12). Never fails the action. */
442
+ private logAction;
303
443
  private showAction;
304
444
  /** The screen size in points, from the latest list or a fresh overlay read. */
305
445
  private screenSize;
@@ -318,15 +458,89 @@ export declare class TaplineTools {
318
458
  private takeShot;
319
459
  /** Adds an image to a result: inline, or saved with its path in file mode. */
320
460
  private deliver;
461
+ /** Where this app's reference pictures are kept, or an error answer. */
462
+ private referenceFolder;
463
+ /** The element list for naming regions and masking, read as a peek: the agent's refs and baseline must stay as they are. */
464
+ private peekElements;
465
+ /**
466
+ * Takes the screen and compares it with the saved reference called `name`, never saving. `referencesDirs` are the
467
+ * folders to look in, in order. A missing reference hands back the screenshot, so the caller may save it.
468
+ */
469
+ private checkAgainstReference;
470
+ /** Why a check does not hold (for `run`, as a step or as `matches`), or null when it matches. */
471
+ private referenceReason;
472
+ /** Whether the screen matches the reference `name` now (`expect.matches`): null when it does, else the reason. */
473
+ private matchMissed;
474
+ /** Where a check looks for `<name>.jpg`: the folder next to a replayed script first, then the app's own store. */
475
+ private referencesDirs;
476
+ /** In a replay, a failed check leaves its red picture at `<script>.diff/<name>.jpg`. */
477
+ private keepDiff;
478
+ /** Takes the screen and saves it as the reference `name` in `dir`. */
479
+ private saveReferenceHere;
480
+ private writeReference;
481
+ /** A `compare_screen` step of `run`: it never saves; it holds on a match and stops the batch otherwise. */
482
+ private compareStep;
483
+ /**
484
+ * Saves a visual check that held as a `compareScreen` step of the recording, when one is running: the name, and the
485
+ * threshold, the ignore list (refs made stable) and the width when given. `update` is never written. Answers an
486
+ * error line when an ignore ref can't be kept in a script, else null (also when nothing is recording).
487
+ */
488
+ private recordCompare;
489
+ /**
490
+ * `ignore` as a script keeps it (AGENT-TOOLS 1.9 §14.2): a ref becomes the element's id, else label, else text,
491
+ * read from the list now. A string is the error answer when a ref can't be kept.
492
+ */
493
+ private stableIgnore;
321
494
  compareScreen(args: Shape<"compare_screen">): Promise<CallToolResult>;
495
+ private compareScreenNow;
322
496
  filmstrip(args: Shape<"filmstrip">): Promise<CallToolResult>;
323
497
  private scriptPath;
324
498
  record(args: Shape<"record">): Promise<CallToolResult>;
499
+ /** `<script dir>/<script name>.references` and `.diff` for a script file. */
500
+ private scriptSideFolder;
501
+ /** Copies each reference the steps use from the app's store next to the script; null when there are none. */
502
+ private copyReferences;
325
503
  private scriptList;
326
- replay(args: Shape<"replay">): Promise<CallToolResult>;
504
+ replay(args: Shape<"replay">, options?: {
505
+ updateReferences?: boolean;
506
+ }): Promise<CallToolResult>;
327
507
  private withLook;
328
- /** The line (without its actions) of the element a result carries under `key`. */
508
+ /**
509
+ * The element a result carries under `key`, for an answer's first line: without its actions, position or
510
+ * size (AGENT-TOOLS 1.8 §13 rule 1; the change block or list has them). `withoutValue` for type_text,
511
+ * whose sentence says the value (rule 5).
512
+ */
329
513
  private elementName;
514
+ /**
515
+ * A guarded touch: the app resolves the target in a dry run, the policy's words are matched against the
516
+ * element's own label and text, and a confirm word waits for a person's OK in the live view. The check runs
517
+ * inside the agent's turn, right before the touch, so nothing a person does in between can change what is
518
+ * touched; when the check can't be made, nothing is touched (fail closed).
519
+ */
520
+ private guarded;
521
+ /**
522
+ * The policy's verdict on a touch about to happen: stop with an answer (blocked, needs a person's OK, or
523
+ * the check couldn't be made: fail closed), or go on (`granted` when a person's OK is being used up).
524
+ */
525
+ private policyGate;
526
+ /** Which policy word, if any, the elements the target could mean carry: a block word first, then a confirm word. */
527
+ private policyWords;
528
+ private blockedLine;
529
+ /** A dry run the app answers itself (scroll, back, swipe): its checks, nothing touched, one line. */
530
+ private dryCall;
531
+ /** A dry run of a tool the policy doesn't guard (type_text): the app's checks, nothing touched. */
532
+ private dryRun;
533
+ /**
534
+ * What a touch would act on. Apps that know dry runs resolve it themselves, with every check; older
535
+ * apps only give their element list, and every element the target could mean is checked.
536
+ */
537
+ private resolveForPolicy;
538
+ /** `Would tap e12 button "Delete trip" at 215,640.` */
539
+ private wouldLine;
540
+ /** `e12 button "Delete trip"`: ref (when asked), role and name, nothing else. */
541
+ private shortName;
542
+ /** A `{ref}` target after a person used the app: refs may point elsewhere. */
543
+ private staleRef;
330
544
  tap(args: Shape<"tap">): Promise<CallToolResult>;
331
545
  /** `seconds` is accepted for the shared interface; Tapline calls the handler, so it doesn't hold. */
332
546
  longPress(args: Shape<"long_press">): Promise<CallToolResult>;