@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.
Files changed (82) hide show
  1. package/CHANGELOG.md +33 -1
  2. package/README.md +102 -9
  3. package/dist/browser-session-state.d.ts +2 -11
  4. package/dist/browser-session-state.d.ts.map +1 -1
  5. package/dist/browser-session-state.js +0 -4
  6. package/dist/commands/act.d.ts.map +1 -1
  7. package/dist/commands/act.js +14 -5
  8. package/dist/commands/attach.d.ts +1 -3
  9. package/dist/commands/attach.d.ts.map +1 -1
  10. package/dist/commands/attach.js +0 -2
  11. package/dist/commands/browser-status.d.ts +0 -2
  12. package/dist/commands/browser-status.d.ts.map +1 -1
  13. package/dist/commands/browser-status.js +1 -7
  14. package/dist/commands/interaction-kernel.d.ts +1 -1
  15. package/dist/commands/interaction-kernel.d.ts.map +1 -1
  16. package/dist/commands/interaction-kernel.js +1 -1
  17. package/dist/commands/launch.d.ts +0 -1
  18. package/dist/commands/launch.d.ts.map +1 -1
  19. package/dist/commands/launch.js +0 -4
  20. package/dist/commands/observe-accessibility.d.ts.map +1 -1
  21. package/dist/commands/observe-accessibility.js +36 -2
  22. package/dist/commands/observe-inventory.d.ts +49 -7
  23. package/dist/commands/observe-inventory.d.ts.map +1 -1
  24. package/dist/commands/observe-inventory.js +807 -96
  25. package/dist/commands/observe-persistence.d.ts.map +1 -1
  26. package/dist/commands/observe-persistence.js +49 -6
  27. package/dist/commands/observe-projection.d.ts +6 -2
  28. package/dist/commands/observe-projection.d.ts.map +1 -1
  29. package/dist/commands/observe-projection.js +251 -27
  30. package/dist/commands/observe-semantics.d.ts +1 -0
  31. package/dist/commands/observe-semantics.d.ts.map +1 -1
  32. package/dist/commands/observe-semantics.js +541 -135
  33. package/dist/commands/observe-signals.d.ts +4 -4
  34. package/dist/commands/observe-signals.d.ts.map +1 -1
  35. package/dist/commands/observe-signals.js +2 -2
  36. package/dist/commands/observe-surfaces.d.ts +2 -1
  37. package/dist/commands/observe-surfaces.d.ts.map +1 -1
  38. package/dist/commands/observe-surfaces.js +143 -45
  39. package/dist/commands/observe.d.ts +5 -1
  40. package/dist/commands/observe.d.ts.map +1 -1
  41. package/dist/commands/observe.js +15 -11
  42. package/dist/commands/semantic-observe.d.ts.map +1 -1
  43. package/dist/commands/semantic-observe.js +43 -0
  44. package/dist/library.d.ts +2 -1
  45. package/dist/library.d.ts.map +1 -1
  46. package/dist/library.js +2 -1
  47. package/dist/match-resolve-fill.d.ts +196 -0
  48. package/dist/match-resolve-fill.d.ts.map +1 -0
  49. package/dist/match-resolve-fill.js +700 -0
  50. package/dist/match-resolve-fill.test-support.d.ts +34 -0
  51. package/dist/match-resolve-fill.test-support.d.ts.map +1 -0
  52. package/dist/match-resolve-fill.test-support.js +81 -0
  53. package/dist/runtime-protected-state.d.ts.map +1 -1
  54. package/dist/runtime-protected-state.js +12 -0
  55. package/dist/runtime-state.d.ts +6 -0
  56. package/dist/runtime-state.d.ts.map +1 -1
  57. package/dist/runtime-state.js +6 -0
  58. package/dist/secrets/form-matcher.d.ts.map +1 -1
  59. package/dist/secrets/form-matcher.js +76 -27
  60. package/dist/secrets/protected-exact-value-redaction.d.ts.map +1 -1
  61. package/dist/secrets/protected-exact-value-redaction.js +6 -0
  62. package/dist/secrets/protected-fill.js +3 -3
  63. package/dist/session.d.ts +3 -3
  64. package/dist/session.d.ts.map +1 -1
  65. package/dist/session.js +2 -2
  66. package/dist/solver/browser-launcher.d.ts.map +1 -1
  67. package/dist/solver/browser-launcher.js +2 -1
  68. package/dist/testing.d.ts +1 -0
  69. package/dist/testing.d.ts.map +1 -1
  70. package/dist/testing.js +1 -0
  71. package/docs/README.md +28 -11
  72. package/docs/api-reference.md +311 -19
  73. package/docs/assistive-runtime.md +41 -16
  74. package/docs/getting-started.md +45 -1
  75. package/docs/integration-checklist.md +32 -3
  76. package/docs/match-resolve-fill.md +699 -0
  77. package/docs/protected-fill.md +373 -91
  78. package/docs/testing.md +147 -15
  79. package/docs/troubleshooting.md +5 -0
  80. package/examples/README.md +7 -0
  81. package/examples/match-resolve-fill.ts +107 -0
  82. 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 and you
4
- need a stable assistive-runtime helper in tests.
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
- ## What It Does
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
- `installFetchBackedTestAssistiveRuntime(...)` installs a process-global
18
- assistive runtime that sends structured chat requests over `fetch`.
29
+ ## Assistive Runtime Fixture
19
30
 
20
- Use it when:
21
-
22
- - your tests wrap `extract(...)`
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.
@@ -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
@@ -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.61",
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"