@mercuryo-ai/agentbrowse 0.2.61 → 0.2.63
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 +33 -1
- package/README.md +102 -9
- package/dist/browser-session-state.d.ts +2 -11
- package/dist/browser-session-state.d.ts.map +1 -1
- package/dist/browser-session-state.js +0 -4
- package/dist/commands/act.d.ts.map +1 -1
- package/dist/commands/act.js +14 -5
- package/dist/commands/attach.d.ts +1 -3
- package/dist/commands/attach.d.ts.map +1 -1
- package/dist/commands/attach.js +0 -2
- package/dist/commands/browser-status.d.ts +0 -2
- package/dist/commands/browser-status.d.ts.map +1 -1
- package/dist/commands/browser-status.js +1 -7
- package/dist/commands/interaction-kernel.d.ts +1 -1
- package/dist/commands/interaction-kernel.d.ts.map +1 -1
- package/dist/commands/interaction-kernel.js +1 -1
- package/dist/commands/launch.d.ts +0 -1
- package/dist/commands/launch.d.ts.map +1 -1
- package/dist/commands/launch.js +0 -4
- package/dist/commands/observe-accessibility.d.ts.map +1 -1
- package/dist/commands/observe-accessibility.js +36 -2
- package/dist/commands/observe-inventory.d.ts +49 -7
- package/dist/commands/observe-inventory.d.ts.map +1 -1
- package/dist/commands/observe-inventory.js +807 -96
- package/dist/commands/observe-persistence.d.ts.map +1 -1
- package/dist/commands/observe-persistence.js +49 -6
- package/dist/commands/observe-projection.d.ts +6 -2
- package/dist/commands/observe-projection.d.ts.map +1 -1
- package/dist/commands/observe-projection.js +251 -27
- package/dist/commands/observe-semantics.d.ts +1 -0
- package/dist/commands/observe-semantics.d.ts.map +1 -1
- package/dist/commands/observe-semantics.js +541 -135
- package/dist/commands/observe-signals.d.ts +4 -4
- package/dist/commands/observe-signals.d.ts.map +1 -1
- package/dist/commands/observe-signals.js +2 -2
- package/dist/commands/observe-surfaces.d.ts +2 -1
- package/dist/commands/observe-surfaces.d.ts.map +1 -1
- package/dist/commands/observe-surfaces.js +143 -45
- package/dist/commands/observe.d.ts +5 -1
- package/dist/commands/observe.d.ts.map +1 -1
- package/dist/commands/observe.js +15 -11
- package/dist/commands/semantic-observe.d.ts.map +1 -1
- package/dist/commands/semantic-observe.js +43 -0
- package/dist/library.d.ts +2 -1
- package/dist/library.d.ts.map +1 -1
- package/dist/library.js +2 -1
- package/dist/match-resolve-fill.d.ts +196 -0
- package/dist/match-resolve-fill.d.ts.map +1 -0
- package/dist/match-resolve-fill.js +700 -0
- package/dist/match-resolve-fill.test-support.d.ts +34 -0
- package/dist/match-resolve-fill.test-support.d.ts.map +1 -0
- package/dist/match-resolve-fill.test-support.js +81 -0
- package/dist/runtime-protected-state.d.ts.map +1 -1
- package/dist/runtime-protected-state.js +12 -0
- package/dist/runtime-state.d.ts +6 -0
- package/dist/runtime-state.d.ts.map +1 -1
- package/dist/runtime-state.js +6 -0
- package/dist/secrets/form-matcher.d.ts.map +1 -1
- package/dist/secrets/form-matcher.js +76 -27
- package/dist/secrets/protected-exact-value-redaction.d.ts.map +1 -1
- package/dist/secrets/protected-exact-value-redaction.js +6 -0
- package/dist/secrets/protected-fill.js +3 -3
- package/dist/session.d.ts +3 -3
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +2 -2
- package/dist/solver/browser-launcher.d.ts.map +1 -1
- package/dist/solver/browser-launcher.js +2 -1
- package/dist/testing.d.ts +1 -0
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +1 -0
- package/docs/README.md +28 -11
- package/docs/api-reference.md +311 -19
- package/docs/assistive-runtime.md +41 -16
- package/docs/getting-started.md +45 -1
- package/docs/integration-checklist.md +32 -3
- package/docs/match-resolve-fill.md +699 -0
- package/docs/protected-fill.md +373 -91
- package/docs/testing.md +147 -15
- package/docs/troubleshooting.md +5 -0
- package/examples/README.md +7 -0
- package/examples/match-resolve-fill.ts +107 -0
- package/package.json +4 -2
package/docs/testing.md
CHANGED
|
@@ -1,30 +1,36 @@
|
|
|
1
1
|
# AgentBrowse Testing Guide
|
|
2
2
|
|
|
3
|
-
Use the published testing subpath when your package wraps AgentBrowse
|
|
4
|
-
|
|
3
|
+
Use the published `/testing` subpath when your package wraps AgentBrowse
|
|
4
|
+
and needs a stable, typed set of testing helpers. The subpath covers two
|
|
5
|
+
separate testing concerns: the assistive runtime (needed by
|
|
6
|
+
`extract(...)` and goal-based `observe(...)` callers) and the
|
|
7
|
+
`match` / `resolve` / `fill` primitives.
|
|
5
8
|
|
|
6
9
|
## Testing Export
|
|
7
10
|
|
|
8
11
|
```ts
|
|
9
12
|
import {
|
|
13
|
+
// Match / resolve / fill fixtures
|
|
14
|
+
createFixtureMatchStore,
|
|
15
|
+
createFixtureGroupStore,
|
|
16
|
+
createFixtureResolver,
|
|
17
|
+
fixtureResolvedValue,
|
|
18
|
+
fixtureResolvedArtifact,
|
|
19
|
+
// Assistive runtime fixture
|
|
10
20
|
installFetchBackedTestAssistiveRuntime,
|
|
11
21
|
uninstallTestAssistiveRuntime,
|
|
12
22
|
} from '@mercuryo-ai/agentbrowse/testing';
|
|
13
23
|
```
|
|
14
24
|
|
|
15
|
-
|
|
25
|
+
All exports are stable across minor versions within the package. Rely on
|
|
26
|
+
them instead of reaching into `src/**/*.test-support.ts` files
|
|
27
|
+
directly — those are implementation detail.
|
|
16
28
|
|
|
17
|
-
|
|
18
|
-
assistive runtime that sends structured chat requests over `fetch`.
|
|
29
|
+
## Assistive Runtime Fixture
|
|
19
30
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
-
|
|
23
|
-
- your tests cover goal-based `observe(session, goal)`
|
|
24
|
-
- your package wants to exercise the current public assistive runtime
|
|
25
|
-
shape
|
|
26
|
-
|
|
27
|
-
## Example
|
|
31
|
+
Installs a process-global assistive runtime that sends structured chat
|
|
32
|
+
requests over `fetch`. Use it when your tests wrap `extract(...)` or
|
|
33
|
+
goal-based `observe(session, goal)`.
|
|
28
34
|
|
|
29
35
|
```ts
|
|
30
36
|
import {
|
|
@@ -45,7 +51,133 @@ afterEach(() => {
|
|
|
45
51
|
});
|
|
46
52
|
```
|
|
47
53
|
|
|
48
|
-
## Scope
|
|
49
|
-
|
|
50
54
|
This helper targets the assistive runtime only. It does not mock browser
|
|
51
55
|
sessions, pages, or observed targets.
|
|
56
|
+
|
|
57
|
+
## Match / Resolve / Fill Fixtures
|
|
58
|
+
|
|
59
|
+
Five builders let you exercise the primitives without constructing the
|
|
60
|
+
internal match result objects by hand (which would bypass the accessor
|
|
61
|
+
pattern and produce results that fail to fill). Prefer these over raw
|
|
62
|
+
object literals in unit tests.
|
|
63
|
+
|
|
64
|
+
### `createFixtureMatchStore(entries)`
|
|
65
|
+
|
|
66
|
+
Builds an `AgentbrowseMatchStore` — an opaque single-field candidate
|
|
67
|
+
source. The store's `entries()` returns candidate metadata without
|
|
68
|
+
values; values are only read back through `store.read(candidateRef)`
|
|
69
|
+
when `match(...)` needs them. Use this when your test wants to assert
|
|
70
|
+
that raw values stay inside the store.
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import { match } from '@mercuryo-ai/agentbrowse';
|
|
74
|
+
import { createFixtureMatchStore } from '@mercuryo-ai/agentbrowse/testing';
|
|
75
|
+
|
|
76
|
+
const store = createFixtureMatchStore([
|
|
77
|
+
{
|
|
78
|
+
candidateRef: 'profile.email.primary',
|
|
79
|
+
fieldKey: 'email',
|
|
80
|
+
type: 'email',
|
|
81
|
+
value: 'traveler@example.com',
|
|
82
|
+
},
|
|
83
|
+
]);
|
|
84
|
+
|
|
85
|
+
const matched = await match(emailTarget, { from: store });
|
|
86
|
+
// matched.kind === 'ready' — and JSON.stringify(matched) does not
|
|
87
|
+
// contain 'traveler@example.com'.
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### `createFixtureGroupStore(entries)`
|
|
91
|
+
|
|
92
|
+
The grouped counterpart — builds an `AgentbrowseGroupMatchStore` for
|
|
93
|
+
whole-form candidates with artifacts. Same opacity guarantee:
|
|
94
|
+
artifacts only come out through `store.readArtifact(candidateRef)`.
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
import { createFixtureGroupStore } from '@mercuryo-ai/agentbrowse/testing';
|
|
98
|
+
|
|
99
|
+
const store = createFixtureGroupStore([
|
|
100
|
+
{
|
|
101
|
+
candidateRef: 'vault_login_1',
|
|
102
|
+
itemRef: 'vault_login_1',
|
|
103
|
+
label: 'Airline Login',
|
|
104
|
+
fieldKeys: ['username', 'password'],
|
|
105
|
+
confidence: 'high',
|
|
106
|
+
artifact: {
|
|
107
|
+
kind: 'values',
|
|
108
|
+
values: { username: 'traveler@example.com', password: 'secret' },
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
]);
|
|
112
|
+
|
|
113
|
+
const matched = await match(fillableForm, { from: store });
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### `createFixtureResolver(resourcesByCandidateRef, { enableBatch? })`
|
|
117
|
+
|
|
118
|
+
Builds an `AgentbrowseMatchResolver` adapter that satisfies the
|
|
119
|
+
interface with recorded call history. Returns
|
|
120
|
+
`{ adapter, resolveCalls, resolveBatchCalls }`. Pass
|
|
121
|
+
`{ enableBatch: true }` to also wire up `resolveBatch`.
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
import { resolve, fill } from '@mercuryo-ai/agentbrowse';
|
|
125
|
+
import {
|
|
126
|
+
createFixtureResolver,
|
|
127
|
+
fixtureResolvedValue,
|
|
128
|
+
} from '@mercuryo-ai/agentbrowse/testing';
|
|
129
|
+
|
|
130
|
+
const resolver = createFixtureResolver({
|
|
131
|
+
'candidate:password:0': fixtureResolvedValue('top-secret-password'),
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
const ready = await resolve(pendingMatch, { with: resolver.adapter });
|
|
135
|
+
await fill(session, passwordTarget, ready);
|
|
136
|
+
|
|
137
|
+
expect(resolver.resolveCalls).toHaveLength(1);
|
|
138
|
+
expect(resolver.resolveCalls[0].candidateRef).toBe('candidate:password:0');
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### `fixtureResolvedValue(value)` and `fixtureResolvedArtifact(artifact, options?)`
|
|
142
|
+
|
|
143
|
+
Typed builders for the resource shapes returned by a resolver. Use them
|
|
144
|
+
to avoid redeclaring `{ kind: 'value', ... }` in every fixture call.
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
fixtureResolvedValue('top-secret-password');
|
|
148
|
+
// => { kind: 'value', value: 'top-secret-password' }
|
|
149
|
+
|
|
150
|
+
fixtureResolvedArtifact(
|
|
151
|
+
{ kind: 'values', values: { username: '…', password: '…' } },
|
|
152
|
+
{ itemRef: 'vault_login_1', requestId: 'req_123' },
|
|
153
|
+
);
|
|
154
|
+
// => { kind: 'artifact', artifact, itemRef, requestId }
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Typing a grouped-only handler
|
|
158
|
+
|
|
159
|
+
When a test only exercises the `ready_group` → `fill(...)` path, a
|
|
160
|
+
bare `{ fill }` object typed as `AgentbrowseGroupFillHandler` is
|
|
161
|
+
enough — no need to construct a full `AgentbrowseMatchResolver` with a
|
|
162
|
+
stub `resolve`:
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
import type { AgentbrowseGroupFillHandler } from '@mercuryo-ai/agentbrowse';
|
|
166
|
+
|
|
167
|
+
const applier: AgentbrowseGroupFillHandler = {
|
|
168
|
+
fill: vi.fn(async () => ({ success: true, outcomeType: 'filled' })),
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
await fill(session, fillableForm, readyGroupMatch, { resolver: applier });
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Scope
|
|
175
|
+
|
|
176
|
+
These fixtures target two concerns only: the assistive runtime used by
|
|
177
|
+
`extract` and goal-based `observe`, and the `match` / `resolve` / `fill`
|
|
178
|
+
primitives. They do not mock browser sessions, pages, observed targets,
|
|
179
|
+
or the underlying `act(...)` path — build those from real `launch(...)`
|
|
180
|
+
sessions against a data: URL when an integration test needs them.
|
|
181
|
+
|
|
182
|
+
See the [Match / Resolve / Fill Guide](./match-resolve-fill.md) for the
|
|
183
|
+
full mental model of the primitives these fixtures exercise.
|
package/docs/troubleshooting.md
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
Common failures and what to do about each.
|
|
4
4
|
|
|
5
|
+
For the full per-command `error` code vocabulary (with short "when /
|
|
6
|
+
action" notes), see
|
|
7
|
+
[`api-reference.md` → Error codes by command](./api-reference.md#error-codes-by-command).
|
|
8
|
+
This guide covers diagnosis patterns; the reference covers the codes.
|
|
9
|
+
|
|
5
10
|
## `launch(...)` Fails
|
|
6
11
|
|
|
7
12
|
Most `launch(...)` failures come from the environment, not from AgentBrowse
|
package/examples/README.md
CHANGED
|
@@ -15,6 +15,7 @@ Then run the examples from `packages/agentbrowse`:
|
|
|
15
15
|
npx tsx examples/basic.ts
|
|
16
16
|
npx tsx examples/attach.ts
|
|
17
17
|
npx tsx examples/extract.ts
|
|
18
|
+
npx tsx examples/match-resolve-fill.ts
|
|
18
19
|
```
|
|
19
20
|
|
|
20
21
|
Examples:
|
|
@@ -27,6 +28,12 @@ Examples:
|
|
|
27
28
|
- `extract.ts`
|
|
28
29
|
Runs structured extraction with an assistive runtime and a plain schema
|
|
29
30
|
object.
|
|
31
|
+
- `match-resolve-fill.ts`
|
|
32
|
+
Demonstrates `match`, `resolve`, and `fill` against a self-contained
|
|
33
|
+
data: URL form: `match(...)` picks candidates, `resolve(...)` walks an
|
|
34
|
+
unresolved plan through a stub vault resolver, and `fill(...)` applies
|
|
35
|
+
both values through the browser without placing them in the match
|
|
36
|
+
result itself.
|
|
30
37
|
|
|
31
38
|
For protected-fill usage (paying with a card, filling login credentials
|
|
32
39
|
from a user's vault), see [../docs/protected-fill.md](../docs/protected-fill.md).
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import {
|
|
2
|
+
close,
|
|
3
|
+
fill,
|
|
4
|
+
launch,
|
|
5
|
+
match,
|
|
6
|
+
observe,
|
|
7
|
+
resolve,
|
|
8
|
+
type AgentbrowseMatchResolver,
|
|
9
|
+
} from '@mercuryo-ai/agentbrowse';
|
|
10
|
+
|
|
11
|
+
// Self-contained data URL with two inputs — keeps the example runnable
|
|
12
|
+
// without depending on any external page.
|
|
13
|
+
const FORM_PAGE = `data:text/html;charset=utf-8,${encodeURIComponent(`
|
|
14
|
+
<!doctype html>
|
|
15
|
+
<html lang="en">
|
|
16
|
+
<head><meta charset="utf-8"><title>Match / Resolve / Fill demo</title></head>
|
|
17
|
+
<body>
|
|
18
|
+
<form>
|
|
19
|
+
<label>Email <input type="email" name="email" autocomplete="email"></label>
|
|
20
|
+
<label>Password <input type="password" name="password" autocomplete="current-password"></label>
|
|
21
|
+
</form>
|
|
22
|
+
</body>
|
|
23
|
+
</html>
|
|
24
|
+
`)}`;
|
|
25
|
+
|
|
26
|
+
// Fake resolver that stands in for a vault lookup. In production it would
|
|
27
|
+
// call MagicPay, another secret manager, or an approval backend.
|
|
28
|
+
const vaultResolver: AgentbrowseMatchResolver = {
|
|
29
|
+
async resolve(plan) {
|
|
30
|
+
if ('fillRef' in plan) {
|
|
31
|
+
throw new Error('This example does not support grouped resolution plans.');
|
|
32
|
+
}
|
|
33
|
+
if (plan.resolve.kind !== 'example_vault' || plan.resolve.key !== 'demo.password') {
|
|
34
|
+
throw new Error(`Unknown resolution plan: ${plan.resolve.kind} / ${plan.resolve.key}`);
|
|
35
|
+
}
|
|
36
|
+
return { kind: 'value', value: 'top-secret-password' };
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const launchResult = await launch(FORM_PAGE, { headless: false });
|
|
41
|
+
if (!launchResult.success) {
|
|
42
|
+
throw new Error(launchResult.reason ?? launchResult.message);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const { session } = launchResult;
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
const observed = await observe(session);
|
|
49
|
+
if (!observed.success) {
|
|
50
|
+
throw new Error(observed.reason ?? observed.message);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const emailTarget = observed.targets.find((target) => target.inputType === 'email');
|
|
54
|
+
const passwordTarget = observed.targets.find((target) => target.inputType === 'password');
|
|
55
|
+
|
|
56
|
+
if (!emailTarget || !passwordTarget) {
|
|
57
|
+
throw new Error('Expected email and password targets on the demo page.');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ---- Simple path: match → fill ----------------------------------------
|
|
61
|
+
const emailMatch = await match(emailTarget, {
|
|
62
|
+
from: { email: 'traveler@example.com' },
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
if (emailMatch.kind !== 'ready') {
|
|
66
|
+
throw new Error(`Unexpected email match kind: ${emailMatch.kind}`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// JSON.stringify proves the value is not in the match result itself.
|
|
70
|
+
console.info('email match kind:', emailMatch.kind);
|
|
71
|
+
console.info('email match serialised:', JSON.stringify(emailMatch));
|
|
72
|
+
|
|
73
|
+
const emailFill = await fill(session, emailTarget, emailMatch);
|
|
74
|
+
console.info('email fill success:', emailFill.success);
|
|
75
|
+
|
|
76
|
+
// ---- Explicit resolver path: match → resolve → fill -------------------
|
|
77
|
+
const passwordMatch = await match(passwordTarget, {
|
|
78
|
+
from: [
|
|
79
|
+
{
|
|
80
|
+
fieldKey: 'password',
|
|
81
|
+
type: 'secret',
|
|
82
|
+
resolve: { kind: 'example_vault', key: 'demo.password' },
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
if (passwordMatch.kind !== 'needs_resolution') {
|
|
88
|
+
throw new Error(`Unexpected password match kind: ${passwordMatch.kind}`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// The match result carries the plan, not the value. It is safe to log
|
|
92
|
+
// or to ship across a process boundary.
|
|
93
|
+
console.info('password plan:', passwordMatch.plan);
|
|
94
|
+
|
|
95
|
+
const passwordReady = await resolve(passwordMatch, { with: vaultResolver });
|
|
96
|
+
const passwordFill = await fill(session, passwordTarget, passwordReady);
|
|
97
|
+
console.info('password fill success:', passwordFill.success);
|
|
98
|
+
|
|
99
|
+
// ---- Equivalent one-call path -----------------------------------------
|
|
100
|
+
// `fill(...)` can do the resolve step inline when a resolver is passed:
|
|
101
|
+
//
|
|
102
|
+
// await fill(session, passwordTarget, passwordMatch, { resolver: vaultResolver });
|
|
103
|
+
//
|
|
104
|
+
// Use it when you do not need to inspect the ready match between steps.
|
|
105
|
+
} finally {
|
|
106
|
+
await close(session);
|
|
107
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mercuryo-ai/agentbrowse",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.63",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Browser automation primitives library for AI agents",
|
|
6
6
|
"license": "MIT",
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
"ai-agent",
|
|
12
12
|
"browser-sdk",
|
|
13
13
|
"playwright",
|
|
14
|
-
"cdp"
|
|
14
|
+
"cdp",
|
|
15
|
+
"form-filling"
|
|
15
16
|
],
|
|
16
17
|
"repository": {
|
|
17
18
|
"type": "git",
|
|
@@ -81,6 +82,7 @@
|
|
|
81
82
|
"test:e2e:autonomous": "npm run test:autonomous:scenarios",
|
|
82
83
|
"test:e2e:autonomous:bare": "npm run test:autonomous:scenarios:bare",
|
|
83
84
|
"check-types": "tsc --noEmit",
|
|
85
|
+
"check-types:examples": "npm run build && tsc -p tsconfig.examples.json",
|
|
84
86
|
"lint": "biome check src docs README.md",
|
|
85
87
|
"pack:verify": "node scripts/verify-pack-artifact.mjs",
|
|
86
88
|
"smoke:pack-install": "node scripts/verify-pack-install-smoke.mjs"
|