@revyl/verify 0.4.0 → 0.5.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.
- package/CHANGELOG.md +50 -0
- package/README.md +22 -15
- package/dist/agent/loop.d.ts +44 -2
- package/dist/agent/loop.js +118 -20
- package/dist/computer-session.d.ts +5 -0
- package/dist/computer-session.js +15 -0
- package/dist/index.d.ts +1 -1
- package/dist/session.d.ts +49 -11
- package/dist/session.js +140 -19
- package/docs/agents.md +33 -0
- package/docs/api.md +27 -9
- package/docs/configuration.md +1 -1
- package/docs/errors.md +2 -2
- package/docs/llms.txt +7 -1
- package/docs/mobile.md +34 -7
- package/docs/own-model.md +4 -4
- package/docs/web.md +38 -6
- package/package.json +1 -1
package/docs/mobile.md
CHANGED
|
@@ -50,7 +50,7 @@ For Android Settings, instruct the agent to scroll or navigate until the OS vers
|
|
|
50
50
|
|
|
51
51
|
## Exact actions
|
|
52
52
|
|
|
53
|
-
**
|
|
53
|
+
**Early access.** Since TypeScript (`@revyl/verify`) 0.4.0, Python (`revyl-verify`) 0.2.0 and Go v0.3.0. `doubleTap`, `longPress`, `drag`, `typeFocused`, `goHome`, and Web keys and modifiers in `pressKey`, are new in TypeScript 0.5.0, Python 0.3.0 and Go v0.4.0. The [Python](https://github.com/RevylAI/verify/blob/v0.5.0/python/README.md#exact-actions) and [Go](https://github.com/RevylAI/verify/blob/v0.5.0/go/README.md#exact-actions) READMEs show them with each language's names. When you know exactly what to press, skip the agent. Exact actions run on the device as given, with no model, and mix freely with `instruction()` and `validation()` in one script:
|
|
54
54
|
|
|
55
55
|
```ts
|
|
56
56
|
import { open } from '@revyl/verify';
|
|
@@ -64,7 +64,7 @@ try {
|
|
|
64
64
|
await session.tap({ target: 'Back button' });
|
|
65
65
|
|
|
66
66
|
await session.swipe('down'); // the finger moves down, so the list scrolls to the top
|
|
67
|
-
const search = await session.locate('Search field'); //
|
|
67
|
+
const search = await session.locate('Search field'); // a point in screenshot pixels
|
|
68
68
|
await session.tap(search); // a tap by coordinates
|
|
69
69
|
await session.type(search, 'Wallpaper');
|
|
70
70
|
await session.pressKey('enter');
|
|
@@ -75,14 +75,39 @@ try {
|
|
|
75
75
|
}
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
A location is `{ target: 'a description' }`, which Revyl's grounding model finds on the current screen, or `{ x, y }` from the top-left corner
|
|
78
|
+
A location is `{ target: 'a description' }`, which Revyl's grounding model finds on the current screen, or `{ x, y }` in screenshot pixels from the top-left corner; `screenSize()` gives the bounds. `type()` taps the field and clears it first unless you pass `{ clear: false }`. `pressKey()` takes `'enter'` or `'backspace'`; for other keys, tap the on-screen control.
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
More actions, new in 0.5.0, on the same Settings app:
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
import { open } from '@revyl/verify';
|
|
84
|
+
|
|
85
|
+
const session = await open({ platform: 'ios', app: { bundleId: 'com.apple.Preferences' } });
|
|
86
|
+
try {
|
|
87
|
+
await session.goHome(); // the home screen
|
|
88
|
+
await session.longPress({ target: 'Settings app icon' }); // press and hold, 1.5 s by default
|
|
89
|
+
await session.validation('A menu for the Settings app is shown');
|
|
90
|
+
await session.goHome(); // closes the menu
|
|
91
|
+
await session.launchApp('com.apple.Preferences');
|
|
92
|
+
await session.drag({ target: 'General row' }, { target: 'Search field' }); // the list follows the finger
|
|
93
|
+
await session.doubleTap({ target: 'Settings title' });
|
|
94
|
+
await session.longPress({ x: 660, y: 1500 }, { duration: 800 }); // a point in screenshot pixels, held for 800 ms
|
|
95
|
+
} finally {
|
|
96
|
+
await session.close();
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
`goHome()` goes to the home screen on iOS and Android. `longPress()` holds for 1500 ms unless you pass `duration`, from 1 to 5000. `drag(from, to)` presses at one location, moves to the other and releases. On iOS and Android, type into a field with `type(location, text)`, which taps it first; `typeFocused()` is Web only and throws `NOT_AVAILABLE_ON_PLATFORM` here.
|
|
101
|
+
|
|
102
|
+
On Android, `back()` presses the system back button. On both platforms, `goHome()` goes to the home screen, `launchApp(bundleId)` brings an installed app to the front and `openUrl('myapp://orders/42')` opens a deep link. A failed action throws `ACTION_FAILED`. One whose outcome Revyl can't confirm throws `ACTION_UNCERTAIN` or `TIMEOUT`, so check the session before acting again. Actions are never repeated. If Verify can't reach Revyl, the action fails with `API_ERROR` and isn't resent either, so check the session before trying it again. [api.md](api.md#exact-actions) lists every call.
|
|
81
103
|
|
|
82
104
|
| Call | Web | iOS | Android | Mac computer |
|
|
83
105
|
| --- | --- | --- | --- | --- |
|
|
84
|
-
| `tap`, `type`, `swipe`, `
|
|
106
|
+
| `tap`, `doubleTap`, `longPress`, `type`, `swipe`, `drag`, `locate`, `screenSize` | Yes | Yes | Yes | No, use `exec()` and files |
|
|
107
|
+
| `pressKey(key, { modifiers })` | Yes: `'enter'`, `'backspace'`, `'tab'`, `'escape'`, arrows and more, with modifiers | `'enter'` and `'backspace'` only | `'enter'` and `'backspace'` only | No |
|
|
108
|
+
| `typeFocused(text)` | Yes, types into the focused field | No, use `type(location, text)` | No, use `type(location, text)` | No |
|
|
85
109
|
| `back()` | Yes, browser history | No, tap the on-screen back control | Yes, the system back button | No |
|
|
110
|
+
| `goHome()` | No, use `openUrl` or `navigate` | Yes, the home screen | Yes, the home screen | No |
|
|
86
111
|
| `openUrl(url)` | Yes, loads an http(s) URL in the tab | Yes, a URL or deep link | Yes, a URL or deep link | No |
|
|
87
112
|
| `launchApp(bundleId)` | No, use `openUrl` or `navigate` | Yes | Yes | No |
|
|
88
113
|
| `navigate(url)` | Yes | No, use `openUrl` | No, use `openUrl` | No |
|
|
@@ -90,6 +115,8 @@ On Android, `back()` presses the system back button. On both platforms, `launchA
|
|
|
90
115
|
| `screenshot()` | Yes | Yes | Yes | Yes |
|
|
91
116
|
| `exec`, files, processes | No, open a Mac computer | No, open a Mac computer | No, open a Mac computer | Yes |
|
|
92
117
|
|
|
93
|
-
Coordinates
|
|
118
|
+
**Coordinates.** Prefer a description, such as `tap({ target: 'Sign in button' })`: Revyl finds the element on the current screen, and you never handle coordinates. Otherwise a point is `{ x, y }` in screenshot pixels on every platform: whole numbers from the top-left corner of the image `screenshot()` returns. So a position you read off a screenshot goes to `tap()`, `type()`, `swipe()` and every other exact action unchanged. `screenSize()` returns the screenshot's width and height, and `locate()` and the points `tap()`, `doubleTap()`, `longPress()` and `type()` return use the same pixels. A point outside the screenshot is refused with `INVALID_ARGUMENT` before any action is sent, and the message names the valid range. On iOS, Verify converts the pixels to the screen's points for you. On Web they equal CSS pixels at the default device scale factor of 1, and you still pass screenshot pixels at any other. This holds from TypeScript 0.4.1, Python 0.2.1 and Go v0.3.1; earlier versions took iOS points.
|
|
119
|
+
|
|
120
|
+
A call a platform doesn't support throws `NOT_AVAILABLE_ON_PLATFORM`, and its message names the alternative.
|
|
94
121
|
|
|
95
|
-
When you pass a [`model`](own-model.md), your model uses `tap`, `type`, `swipe`, `pressKey`, `openUrl` and, where the platform has
|
|
122
|
+
When you pass a [`model`](own-model.md), your model uses `tap`, `doubleTap`, `longPress`, `type`, `swipe`, `drag`, `pressKey`, `openUrl` and, where the platform has them, `typeFocused`, `back` and `goHome` inside `instruction()`.
|
package/docs/own-model.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Use your own model
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**Early access.** Since TypeScript (`@revyl/verify`) 0.4.0, Python (`revyl-verify`) 0.2.0 and Go v0.3.0; the [Python](https://github.com/RevylAI/verify/blob/v0.5.0/python/README.md#your-own-model) and [Go](https://github.com/RevylAI/verify/blob/v0.5.0/go/README.md#your-own-model) READMEs show them with each language's names. Verify normally runs `instruction()` and `validation()` on Revyl's agent. Pass a `model` and the agent loop runs in your process instead, on a model you choose, with your own key. Revyl still provides the cloud browser or device and, by default, finds the elements your model names on screen.
|
|
4
4
|
|
|
5
5
|
Use it when you want a specific model, your own prompts and key, or faster steps on a cheap model. Leave `model` out and nothing changes.
|
|
6
6
|
|
|
7
|
-
Your model uses the same [exact actions](api.md#exact-actions) you can call yourself: `tap`, `type`, `swipe`, `pressKey`, `openUrl` and, where the platform has
|
|
7
|
+
Your model uses the same [exact actions](api.md#exact-actions) you can call yourself: `tap`, `doubleTap`, `longPress`, `type`, `swipe`, `drag`, `pressKey`, `openUrl` and, where the platform has them, `typeFocused` and Web keys with modifiers (Web), `back` (Web and Android) and `goHome` (iOS and Android). So a script can mix your own actions, steps on your model and steps on Revyl's agent.
|
|
8
8
|
|
|
9
9
|
## The one-line form
|
|
10
10
|
|
|
@@ -52,7 +52,7 @@ log.Println(step.Model.ID, len(step.Model.Turns), step.Model.InputTokens, step.M
|
|
|
52
52
|
_, err = s.Validation(ctx, "The screen shows the iOS version")
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
The [Python](https://github.com/RevylAI/verify/blob/v0.
|
|
55
|
+
The [Python](https://github.com/RevylAI/verify/blob/v0.5.0/python/README.md#your-own-model) and [Go](https://github.com/RevylAI/verify/blob/v0.5.0/go/README.md#your-own-model) READMEs have the full reference for each.
|
|
56
56
|
|
|
57
57
|
Set `model` on `open()` to run every step on it, or on one step to override it:
|
|
58
58
|
|
|
@@ -169,7 +169,7 @@ const session = await open('ios', { agent }); // or session.instruction(text, {
|
|
|
169
169
|
|
|
170
170
|
**Your model sees** the system prompt, the step text, a short log of the actions it took so far and whether they worked, and the current screenshot, downscaled so its longest side is at most 1280 pixels (`maxImageSide`).
|
|
171
171
|
|
|
172
|
-
**Revyl sees** what it always sees for a session it hosts: the device and its screen. On top of that it receives each device action (taps, typed text, swipes, key presses, URLs), the element descriptions your model writes, such as "General row" (not with `grounding: 'model'`), and the model's name, such as `anthropic/claude-sonnet-5-5`, for the session report.
|
|
172
|
+
**Revyl sees** what it always sees for a session it hosts: the device and its screen. On top of that it receives each device action (taps, long presses, drags, typed text, swipes, key presses, URLs), the element descriptions your model writes, such as "General row" (not with `grounding: 'model'`), and the model's name, such as `anthropic/claude-sonnet-5-5`, for the session report.
|
|
173
173
|
|
|
174
174
|
**Revyl does not receive** the step text, the prompts, your model's output or reasoning, or your key. Keys go only to the provider's endpoint, are never logged, and are replaced with `[redacted]` in error messages, as are custom header values. A custom endpoint's address is never sent to Revyl.
|
|
175
175
|
|
package/docs/web.md
CHANGED
|
@@ -53,7 +53,7 @@ Revyl runs one step at a time on a session. A step sent while another is still r
|
|
|
53
53
|
|
|
54
54
|
## Exact actions
|
|
55
55
|
|
|
56
|
-
**
|
|
56
|
+
**Early access.** Since TypeScript (`@revyl/verify`) 0.4.0, Python (`revyl-verify`) 0.2.0 and Go v0.3.0. `doubleTap`, `longPress`, `drag`, `typeFocused`, `goHome`, and Web keys and modifiers in `pressKey`, are new in TypeScript 0.5.0, Python 0.3.0 and Go v0.4.0. The [Python](https://github.com/RevylAI/verify/blob/v0.5.0/python/README.md#exact-actions) and [Go](https://github.com/RevylAI/verify/blob/v0.5.0/go/README.md#exact-actions) READMEs show them with each language's names. When you know exactly what to click, skip the agent. Exact actions run in the browser as given, with no model, and mix freely with `instruction()` and `validation()`:
|
|
57
57
|
|
|
58
58
|
```ts
|
|
59
59
|
import { open } from '@revyl/verify';
|
|
@@ -66,7 +66,7 @@ try {
|
|
|
66
66
|
await session.validation('A list of products is shown');
|
|
67
67
|
await session.swipe('up'); // the finger moves up, so the page scrolls down
|
|
68
68
|
await session.instruction('Add the Sauce Labs Onesie to the cart'); // hand the fuzzy part to the agent
|
|
69
|
-
const cart = await session.locate('Shopping cart icon'); //
|
|
69
|
+
const cart = await session.locate('Shopping cart icon'); // a point in screenshot pixels
|
|
70
70
|
await session.tap(cart); // a tap by coordinates
|
|
71
71
|
await session.validation('The cart lists the Sauce Labs Onesie');
|
|
72
72
|
await session.back(); // browser history
|
|
@@ -76,12 +76,42 @@ try {
|
|
|
76
76
|
}
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
-
A location is `{ target: 'a description' }`, which Revyl's grounding model finds on the current page, or `{ x, y }` from the top-left corner
|
|
79
|
+
A location is `{ target: 'a description' }`, which Revyl's grounding model finds on the current page, or `{ x, y }` in screenshot pixels from the top-left corner; `screenSize()` gives the bounds and `locate(target)` gives an element's point without acting. `type()` clicks the field and clears it first unless you pass `{ clear: false }`. `openUrl(url)` loads a page like `navigate(url)`.
|
|
80
|
+
|
|
81
|
+
More actions, new in 0.5.0, on the same store:
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
import { open } from '@revyl/verify';
|
|
85
|
+
|
|
86
|
+
const session = await open('web'); // https://www.saucedemo.com in examples/web-store
|
|
87
|
+
try {
|
|
88
|
+
await session.tap({ target: 'Username field' });
|
|
89
|
+
await session.typeFocused('standard_user'); // types into the field that has focus, replacing its text
|
|
90
|
+
await session.pressKey('tab'); // focus moves to the password field
|
|
91
|
+
await session.typeFocused('secret_sauce');
|
|
92
|
+
await session.pressKey('enter');
|
|
93
|
+
await session.validation('A list of products is shown');
|
|
94
|
+
await session.tap({ target: 'Open Menu button' });
|
|
95
|
+
await session.pressKey('escape'); // closes the menu
|
|
96
|
+
await session.validation('The side menu is closed');
|
|
97
|
+
await session.doubleTap({ target: 'Products heading' }); // a double-click
|
|
98
|
+
await session.pressKey('a', { modifiers: ['ctrl'] }); // a chord: a lowercase letter or digit with modifiers
|
|
99
|
+
await session.longPress({ target: 'Products heading' }, { duration: 800 }); // press and hold for 800 ms
|
|
100
|
+
await session.drag({ target: 'Sauce Labs Backpack description' }, { target: 'Sauce Labs Bike Light description' }); // selects the text between
|
|
101
|
+
} finally {
|
|
102
|
+
await session.close();
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`typeFocused(text)` types where the focus already is, without clicking, and replaces the field's text unless you pass `{ clear: false }`. Focus the field first, with `tap()` or `pressKey('tab')`: with no field focused, nothing is typed and no error is thrown. `pressKey()` takes `'enter'`, `'backspace'`, `'tab'`, `'escape'`, `'delete'`, `'space'`, `'arrowup'`, `'arrowdown'`, `'arrowleft'`, `'arrowright'`, `'home'`, `'end'`, `'pageup'` and `'pagedown'`, with up to four `modifiers` held (`'ctrl'`, `'shift'`, `'alt'`, `'meta'`); with modifiers a lowercase letter or digit works too. `longPress()` holds for 1500 ms unless you pass `duration`, from 1 to 5000. `drag(from, to)` presses at one location, moves to the other and releases. A failed action throws `ACTION_FAILED`. One whose outcome Revyl can't confirm throws `ACTION_UNCERTAIN` or `TIMEOUT`, so check the session before acting again. Actions are never repeated. If Verify can't reach Revyl, the action fails with `API_ERROR` and isn't resent either, so check the session before trying it again. [api.md](api.md#exact-actions) lists every call.
|
|
80
107
|
|
|
81
108
|
| Call | Web | iOS | Android | Mac computer |
|
|
82
109
|
| --- | --- | --- | --- | --- |
|
|
83
|
-
| `tap`, `type`, `swipe`, `
|
|
110
|
+
| `tap`, `doubleTap`, `longPress`, `type`, `swipe`, `drag`, `locate`, `screenSize` | Yes | Yes | Yes | No, use `exec()` and files |
|
|
111
|
+
| `pressKey(key, { modifiers })` | Yes: `'enter'`, `'backspace'`, `'tab'`, `'escape'`, arrows and more, with modifiers | `'enter'` and `'backspace'` only | `'enter'` and `'backspace'` only | No |
|
|
112
|
+
| `typeFocused(text)` | Yes, types into the focused field | No, use `type(location, text)` | No, use `type(location, text)` | No |
|
|
84
113
|
| `back()` | Yes, browser history | No, tap the on-screen back control | Yes, the system back button | No |
|
|
114
|
+
| `goHome()` | No, use `openUrl` or `navigate` | Yes, the home screen | Yes, the home screen | No |
|
|
85
115
|
| `openUrl(url)` | Yes, loads an http(s) URL in the tab | Yes, a URL or deep link | Yes, a URL or deep link | No |
|
|
86
116
|
| `launchApp(bundleId)` | No, use `openUrl` or `navigate` | Yes | Yes | No |
|
|
87
117
|
| `navigate(url)` | Yes | No, use `openUrl` | No, use `openUrl` | No |
|
|
@@ -89,6 +119,8 @@ A location is `{ target: 'a description' }`, which Revyl's grounding model finds
|
|
|
89
119
|
| `screenshot()` | Yes | Yes | Yes | Yes |
|
|
90
120
|
| `exec`, files, processes | No, open a Mac computer | No, open a Mac computer | No, open a Mac computer | Yes |
|
|
91
121
|
|
|
92
|
-
Coordinates
|
|
122
|
+
**Coordinates.** Prefer a description, such as `tap({ target: 'Sign in button' })`: Revyl finds the element on the current screen, and you never handle coordinates. Otherwise a point is `{ x, y }` in screenshot pixels on every platform: whole numbers from the top-left corner of the image `screenshot()` returns. So a position you read off a screenshot goes to `tap()`, `type()`, `swipe()` and every other exact action unchanged. `screenSize()` returns the screenshot's width and height, and `locate()` and the points `tap()`, `doubleTap()`, `longPress()` and `type()` return use the same pixels. A point outside the screenshot is refused with `INVALID_ARGUMENT` before any action is sent, and the message names the valid range. On iOS, Verify converts the pixels to the screen's points for you. On Web they equal CSS pixels at the default device scale factor of 1, and you still pass screenshot pixels at any other. This holds from TypeScript 0.4.1, Python 0.2.1 and Go v0.3.1; earlier versions took iOS points.
|
|
123
|
+
|
|
124
|
+
A call a platform doesn't support throws `NOT_AVAILABLE_ON_PLATFORM`, and its message names the alternative.
|
|
93
125
|
|
|
94
|
-
When you pass a [`model`](own-model.md), your model uses `tap`, `type`, `swipe`, `pressKey`, `openUrl` and, where the platform has
|
|
126
|
+
When you pass a [`model`](own-model.md), your model uses `tap`, `doubleTap`, `longPress`, `type`, `swipe`, `drag`, `pressKey`, `openUrl` and, where the platform has them, `typeFocused`, `back` and `goHome` inside `instruction()`.
|