@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
@@ -97,21 +97,211 @@ Captures a screenshot of the current page.
97
97
 
98
98
  Closes the browser session.
99
99
 
100
- ## Stable Error Code Arrays
100
+ ### `match(subject, options)`
101
101
 
102
- The root package exports stable top-level error code arrays for command
103
- branching:
102
+ Decides which caller-supplied candidate value fits an observed target or
103
+ fillable form. Pure and local — does not call the network and does not
104
+ mutate browser state. See
105
+ [Match / Resolve / Fill Guide](./match-resolve-fill.md) for the full
106
+ mental model.
104
107
 
105
- - `ACT_ERROR_CODES`
106
- - `ATTACH_ERROR_CODES`
107
- - `CLOSE_ERROR_CODES`
108
- - `EXTRACT_ERROR_CODES`
109
- - `LAUNCH_ERROR_CODES`
110
- - `NAVIGATE_ERROR_CODES`
111
- - `OBSERVE_ERROR_CODES`
112
- - `SCREENSHOT_ERROR_CODES`
108
+ ```ts
109
+ function match(
110
+ subject: TargetDescriptor | ProtectedFillForm,
111
+ options: AgentbrowseMatchOptions,
112
+ ): Promise<AgentbrowseMatchResult>;
113
+
114
+ interface AgentbrowseMatchOptions {
115
+ from: AgentbrowseMatchSource;
116
+ host?: string;
117
+ protectedTargetRefs?: ReadonlySet<string>;
118
+ }
119
+ ```
120
+
121
+ ### `resolve(plan, options)`
122
+
123
+ Turns one or many `needs_resolution` plans into ready match results
124
+ through a caller-supplied adapter. `ready` results pass through
125
+ untouched. Overloaded for single plan and batch arrays.
126
+
127
+ ```ts
128
+ function resolve(
129
+ plan: AgentbrowseMatchResult,
130
+ options: AgentbrowseResolveOptions,
131
+ ): Promise<AgentbrowseMatchResult>;
132
+
133
+ function resolve(
134
+ plans: ReadonlyArray<AgentbrowseMatchResult>,
135
+ options: AgentbrowseResolveOptions,
136
+ ): Promise<AgentbrowseMatchResult[]>;
137
+
138
+ interface AgentbrowseResolveOptions {
139
+ with: AgentbrowseMatchResolver;
140
+ }
141
+ ```
142
+
143
+ ### `fill(session, subject, plan, options?)`
144
+
145
+ Applies a match result to the browser. Dereferences the opaque
146
+ value/artifact ref internally and hands off to the standard `act(...)`
147
+ path (single targets) or `resolver.fill(...)` (grouped protected forms).
148
+
149
+ ```ts
150
+ function fill(
151
+ session: BrowserCommandSession,
152
+ subject: Pick<TargetDescriptor, 'ref'> | ProtectedFillForm,
153
+ plan: AgentbrowseMatchResult,
154
+ options?: AgentbrowseFillOptions,
155
+ ): Promise<AgentbrowseFillResult>;
156
+
157
+ interface AgentbrowseFillOptions {
158
+ resolver?: AgentbrowseMatchResolver;
159
+ }
160
+ ```
161
+
162
+ `fill(...)` can run `resolve` inline when you pass a `resolver` for a
163
+ plan that still needs resolution — equivalent to the two-call form. See
164
+ [match-resolve-fill.md → Walk-through 3](./match-resolve-fill.md#walk-through-3--collapsing-to-one-call).
113
165
 
114
- These arrays back the exported `*ErrorCode` types.
166
+ ## Result Shape
167
+
168
+ All main commands share the same top-level pattern.
169
+
170
+ ```ts
171
+ // success
172
+ { success: true, ...commandSpecificFields }
173
+
174
+ // failure
175
+ {
176
+ success: false,
177
+ error: <ErrorCode>,
178
+ outcomeType: <OutcomeType>,
179
+ message: string,
180
+ reason: string,
181
+ ...commandSpecificFields,
182
+ }
183
+ ```
184
+
185
+ - `error` — stable top-level code from the per-command table below.
186
+ Branch on this, not on `reason` or `message`.
187
+ - `outcomeType` — stable outcome category (e.g. `binding_stale`,
188
+ `blocked`). Same vocabulary per command as the `*_OUTCOME_TYPES`
189
+ exports.
190
+ - `message` — human-readable short message.
191
+ - `reason` — detailed reason string. Usually a lower-level error code,
192
+ a truncation detail, or an explanation; see the sticky-owner note
193
+ below for one common special case.
194
+
195
+ The root package exports stable arrays backing the `*ErrorCode` and
196
+ `*OutcomeType` types for every command: `ACT_ERROR_CODES`,
197
+ `ACT_OUTCOME_TYPES`, `ATTACH_ERROR_CODES`, `CLOSE_ERROR_CODES`,
198
+ `EXTRACT_ERROR_CODES`, `EXTRACT_OUTCOME_TYPES`, `LAUNCH_ERROR_CODES`,
199
+ `NAVIGATE_ERROR_CODES`, `OBSERVE_ERROR_CODES`, `OBSERVE_OUTCOME_TYPES`,
200
+ `SCREENSHOT_ERROR_CODES`, `SCREENSHOT_OUTCOME_TYPES`.
201
+
202
+ ### Cross-command codes
203
+
204
+ Any command that drives an already-open browser session may surface:
205
+
206
+ | `error` | When | Action |
207
+ | --- | --- | --- |
208
+ | `browser_connection_failed` | AgentBrowse could not reach the browser. | Check `reason`. A common special value is `sticky_owner_unrecoverable` — the prior browser session is lost; launch or attach a fresh session before retrying. |
209
+
210
+ ### Error codes by command
211
+
212
+ #### `launch`
213
+
214
+ | `error` | When | Action |
215
+ | --- | --- | --- |
216
+ | `browser_launch_failed` | The managed browser could not be started. | Inspect `reason`/`message`; verify the host can run the browser. |
217
+
218
+ #### `attach`
219
+
220
+ | `error` | When | Action |
221
+ | --- | --- | --- |
222
+ | `browser_attach_failed` | Could not attach to the provided CDP URL. | Verify the CDP URL is reachable and exposes the protocol. |
223
+
224
+ #### `navigate`
225
+
226
+ | `error` | When | Action |
227
+ | --- | --- | --- |
228
+ | `browser_connection_failed` | See Cross-command codes. | — |
229
+ | `navigation_failed` | Navigation did not complete. | Retry, or re-observe to see current page state. |
230
+
231
+ #### `observe`
232
+
233
+ | `error` | When | Action |
234
+ | --- | --- | --- |
235
+ | `browser_connection_failed` | See Cross-command codes. | — |
236
+ | `observe_failed` | Page inspection failed (DOM or assistive runtime error). | Retry; if persistent, check the assistive runtime. |
237
+ | `protected_observe_blocked` | The page is under active protected exposure; observation is blocked. | Complete or cancel the protected step first. |
238
+
239
+ #### `act`
240
+
241
+ | `error` | When | Action |
242
+ | --- | --- | --- |
243
+ | `act_failed` | Generic action failure not covered by a specific code. | Check `reason`/`message`. |
244
+ | `action_not_allowed_for_target` | The requested action (`click`/`fill`/`type`/`select`/`press`) is not valid for this target kind. | Use an action compatible with the target's capability. |
245
+ | `browser_connection_failed` | See Cross-command codes. | — |
246
+ | `no_observable_progress` | The action ran but no DOM/UI change was detected within the wait window. | Re-observe; the target may need a different interaction. |
247
+ | `stale_target` | The target binding is stale at execution time. | Re-observe and rebind. |
248
+ | `stale_target_ref` | The passed `targetRef` no longer maps to an observed element. | Re-observe. |
249
+ | `target_disabled` | Element is present but disabled. | Wait for enablement or resolve the blocker. |
250
+ | `target_gated` | Element is gated behind an intermediate step. | Resolve the gating step first. |
251
+ | `target_not_actionable` | Element exists but cannot be actioned (visibility or position). | Scroll/expand the owning scope, then re-observe. |
252
+ | `target_readonly` | Element is read-only; `fill`/`type` are not allowed. | Use a different action or a different target. |
253
+ | `target_surface_inactive` | The owning scope is currently inactive (e.g. a closed modal). | Activate or expand the scope first. |
254
+ | `target_surface_not_live` | The owning scope is no longer live on the page. | Re-observe. |
255
+ | `target_surface_unavailable` | The owning scope is not currently available. | Re-observe after any UI transition. |
256
+ | `unknown_target_ref` | The `targetRef` was not issued by a prior `observe(...)`. | Never synthesise refs — pass back what `observe` returned. |
257
+ | `validation_blocked` | The target accepted the input, but page-level validation blocks continuation. | Resolve the validation error and re-observe. |
258
+
259
+ #### `extract`
260
+
261
+ | `error` | When | Action |
262
+ | --- | --- | --- |
263
+ | `browser_connection_failed` | See Cross-command codes. | — |
264
+ | `expired_extract_scope` | The scope ref expired (page moved past its lifetime). | Re-observe and use the new scope. |
265
+ | `extract_failed` | Extraction failed at runtime. | Check `reason`/`message` and retry. |
266
+ | `extract_output_truncated` | The assistive runtime returned a structured output that was cut off. | Raise `maxOutputTokens` in your adapter. |
267
+ | `invalid_extract_schema` | The passed schema is not a supported shape. | See [Extraction Schema Rules](#extraction-schema-rules). |
268
+ | `invalid_extract_scope` | The scope ref was provided but is not valid. | Verify the ref came from `observe(...)`. |
269
+ | `stale_extract_scope` | The scope ref is stale against the current DOM. | Re-observe. |
270
+ | `unknown_scope_ref` | The scope ref was not issued by a prior `observe(...)`. | Never synthesise refs. |
271
+
272
+ #### `screenshot`
273
+
274
+ | `error` | When | Action |
275
+ | --- | --- | --- |
276
+ | `browser_connection_failed` | See Cross-command codes. | — |
277
+ | `protected_screenshot_blocked` | The page is under active protected exposure; screenshot is blocked. | Complete or cancel the protected step first. |
278
+ | `screenshot_failed` | The screenshot attempt failed. | Check `reason`/`message`. |
279
+
280
+ #### `close`
281
+
282
+ | `error` | When | Action |
283
+ | --- | --- | --- |
284
+ | `browser_close_failed` | The browser session could not be closed cleanly. | Usually cosmetic; the process will eventually clean up. |
285
+
286
+ #### `fill`
287
+
288
+ Contract failures from `fill(...)` share a dedicated shape,
289
+ `AgentbrowseFillFailureResult`, with `failureSurface: 'contract'` and
290
+ `action: 'fill'`. Browser-level failures (stale refs, validation,
291
+ connection) flow through the underlying `ActResult` / `resolver.fill`
292
+ result instead — see `act` codes above and
293
+ [protected-fill.md](./protected-fill.md).
294
+
295
+ | `error` | When | Action |
296
+ | --- | --- | --- |
297
+ | `match_no_match` | `fill` received a `no_match` / `no_match_group` plan, or a subject-shape mismatch (field plan on a form, form plan on a field). | Do not retry with the same input. Re-observe or branch on the match result first. |
298
+ | `match_ambiguous` | `fill` received an `ambiguous` / `ambiguous_group` plan. | Ask the caller to disambiguate before filling. |
299
+ | `match_resolver_required` | Plan needs external resolution and no `resolver` was supplied, or the resolver is missing the required capability (`.resolve` for needs-resolution, `.fill` for grouped). | Provide a resolver adapter that implements the capability. |
300
+ | `match_value_unavailable` | Internal value accessor is gone. Typically means a `ready` result was serialized across a process boundary and the non-enumerable accessor was lost. | Run `resolve` → `fill` inside the same process, or ship the `needs_resolution` plan instead. |
301
+ | `match_artifact_unavailable` | Same failure mode as above, for grouped ready plans. | Same fix. |
302
+
303
+ See [Protected Fill](./protected-fill.md) for the separate error and
304
+ execution-kind vocabulary used by `fillProtectedForm(...)`.
115
305
 
116
306
  ## Error Classes
117
307
 
@@ -123,13 +313,6 @@ For code paths that want `instanceof` checks instead of string matching on
123
313
  - `AssistiveStructuredOutputTruncatedError` — thrown when the assistive
124
314
  runtime returns a structured output that was cut off mid-response.
125
315
 
126
- ## Core Result Shapes
127
-
128
- All main commands use the same top-level pattern:
129
-
130
- - success: `{ success: true, ... }`
131
- - failure: `{ success: false, error, outcomeType, message, reason, ... }`
132
-
133
316
  ## Observe Types
134
317
 
135
318
  ### `ObserveTarget`
@@ -197,6 +380,115 @@ Fields:
197
380
  - `framePath`
198
381
  - `source`
199
382
 
383
+ ## Match / Resolve / Fill Types
384
+
385
+ The exported types for the `match` / `resolve` / `fill` primitives.
386
+ Read [match-resolve-fill.md](./match-resolve-fill.md) for how they
387
+ compose in practice; this section is lookup-only.
388
+
389
+ ### Candidate sources
390
+
391
+ - `AgentbrowseMatchSource` — union of `Record<string, AgentbrowseMatchValue>`,
392
+ `ReadonlyArray<AgentbrowseMatchCandidate>`,
393
+ `AgentbrowseMatchStore`,
394
+ `ReadonlyArray<AgentbrowseGroupMatchCandidate>`, or
395
+ `AgentbrowseGroupMatchStore`. All of these are valid `options.from`
396
+ values for `match(...)`.
397
+ - `AgentbrowseMatchValue` — `string | number`.
398
+ - `AgentbrowseMatchApplicability` — `{ target: 'global' | 'host'; value?: string }`.
399
+ - `AgentbrowseMatchCandidate` — single-field candidate with optional
400
+ `candidateRef`, `value`, `type`, `label`, `semanticTags`,
401
+ `applicability`, `resolve` plan.
402
+ - `AgentbrowseGroupMatchCandidate` — grouped candidate with required
403
+ `fieldKeys` plus optional `candidateRef`, `itemRef`, `label`,
404
+ `confidence`, `applicability`, `resolve` plan, `artifact`.
405
+ - `AgentbrowseMatchStore` — `{ entries(), read(candidateRef) }`.
406
+ Opaque single-field store; values stay behind `read`.
407
+ - `AgentbrowseGroupMatchStore` — `{ entries(), readArtifact(candidateRef) }`.
408
+ Opaque grouped store; artifacts stay behind `readArtifact`.
409
+
410
+ ### Match result union
411
+
412
+ `AgentbrowseMatchResult` is a discriminated union over `kind`:
413
+
414
+ - `AgentbrowseReadyMatchResult` — `kind: 'ready'` (single target).
415
+ - `AgentbrowseNeedsResolutionMatchResult` — `kind: 'needs_resolution'`
416
+ with `plan: AgentbrowseResolutionPlan`.
417
+ - `AgentbrowseAmbiguousMatchResult` — `kind: 'ambiguous'` with
418
+ `candidates: string[]`.
419
+ - `AgentbrowseNoMatchResult` — `kind: 'no_match'` with a stable
420
+ `reason`: `'protected_target' | 'no_candidate' | 'scope_ineligible' | 'incompatible_shape' | 'low_confidence'`.
421
+ - `AgentbrowseReadyGroupMatchResult` — `kind: 'ready_group'`.
422
+ - `AgentbrowseNeedsResolutionGroupMatchResult` — `kind: 'needs_resolution_group'`
423
+ with `plan: AgentbrowseGroupResolutionPlan`.
424
+ - `AgentbrowseAmbiguousGroupMatchResult` — `kind: 'ambiguous_group'`.
425
+ - `AgentbrowseNoGroupMatchResult` — `kind: 'no_match_group'` with the
426
+ same `reason` vocabulary.
427
+
428
+ `AgentbrowseResolvableMatchResult` is the narrower union of
429
+ `'needs_resolution' | 'needs_resolution_group'` — what `resolve(...)`
430
+ actually hands to your adapter.
431
+
432
+ ### Resolution plans
433
+
434
+ - `AgentbrowseResolutionPlan` — `{ targetRef, candidateRef, fieldKey, type?, resolve }`.
435
+ - `AgentbrowseGroupResolutionPlan` — `{ fillRef, pageRef, scopeRef?, purpose, candidateRef, itemRef?, fieldKeys, resolve }`.
436
+ - `AgentbrowseMatchResolutionRequest` — `{ kind: string; key?: string; params?: Record<string, unknown> }`.
437
+ The `resolve` field on a candidate or plan. AgentBrowse does not
438
+ interpret `kind` or `key` — they are opaque to the core and meaningful
439
+ only to your adapter.
440
+
441
+ ### Resolved resources
442
+
443
+ - `AgentbrowseResolvedResource` — `AgentbrowseResolvedValueResource | AgentbrowseResolvedArtifactResource`.
444
+ - `AgentbrowseResolvedValueResource` — `{ kind: 'value'; value }`.
445
+ - `AgentbrowseResolvedArtifactResource` — `{ kind: 'artifact'; artifact; itemRef?; requestId?; resolutionPath?; claimedAt? }`.
446
+ - `AgentbrowseReadyGroupFillInput` — the shape `resolver.fill` receives:
447
+ `{ candidateRef, itemRef?, fieldKeys, artifact, requestId?, resolutionPath?, claimedAt? }`.
448
+
449
+ ### Resolver adapter
450
+
451
+ Two exported interfaces. The `{ resolver }` slot on `fill(...)` accepts
452
+ either via a union; `resolve(plan, { with })` only accepts the main
453
+ resolver.
454
+
455
+ ```ts
456
+ interface AgentbrowseMatchResolver {
457
+ resolve(plan): Promise<AgentbrowseResolvedResource>;
458
+ resolveBatch?(plans): Promise<ReadonlyArray<AgentbrowseResolvedResource>>;
459
+ fill?(session, subject, ready): Promise<Record<string, unknown> & { success: boolean }>;
460
+ }
461
+
462
+ interface AgentbrowseGroupFillHandler {
463
+ fill(session, subject, ready): Promise<Record<string, unknown> & { success: boolean }>;
464
+ }
465
+ ```
466
+
467
+ - `AgentbrowseMatchResolver` — full adapter. `resolve` is required;
468
+ `resolveBatch` and `fill` are optional. Used by `resolve(plan, { with })`
469
+ and by `fill(...)` for all plan kinds.
470
+ - `AgentbrowseGroupFillHandler` — narrow handler used only at the
471
+ `fill(...)` boundary when the plan is `ready_group` and no resolution
472
+ is needed. A handler is not accepted by `resolve(...)`.
473
+
474
+ At runtime, `fill(...)` picks the right capability via an internal type
475
+ guard (`hasResolveCapability` / `hasGroupFillCapability`). When a
476
+ capability the current plan needs is missing on the passed object,
477
+ `fill(...)` returns a typed `match_resolver_required` failure instead
478
+ of throwing.
479
+
480
+ See [match-resolve-fill.md → Resolver Interface](./match-resolve-fill.md#resolver-interface)
481
+ for examples of each shape.
482
+
483
+ ### Fill result
484
+
485
+ - `AgentbrowseFillResult` — `ActResult | AgentbrowseFillFailureResult | (Record<string, unknown> & { success: boolean })`.
486
+ Single-target fill returns `ActResult`; grouped fill returns whatever
487
+ your `resolver.fill` returned; contract failures return
488
+ `AgentbrowseFillFailureResult`.
489
+ - `AgentbrowseFillFailureResult` — `{ success: false; failureSurface: 'contract'; error; outcomeType; message; reason; targetRef?; fillRef?; action: 'fill' }`.
490
+ See the [`fill` error table](#fill) above for `error` codes.
491
+
200
492
  ## Ref Glossary
201
493
 
202
494
  - `ref`
@@ -58,6 +58,9 @@ this usually means:
58
58
 
59
59
  1. Convert the Zod schema to JSON Schema (e.g. with
60
60
  `@browserbasehq/stagehand`'s `toJsonSchema`, or your own helper).
61
+ `@browserbasehq/stagehand` is a direct dependency of this package, so
62
+ `toJsonSchema` is available without a separate install. Alternatives
63
+ like `zod-to-json-schema` work too.
61
64
  2. Pass it as `response_format: { type: 'json_schema', json_schema: { ... } }`.
62
65
  3. Parse `choices[0].message.content` and return `{ data, usage? }`.
63
66
 
@@ -99,6 +102,9 @@ by hand.
99
102
 
100
103
  ## Recommended Setup
101
104
 
105
+ The preferred path is a per-client runtime: pass your runtime into
106
+ `createAgentbrowseClient({ assistiveRuntime })` and reuse that client.
107
+
102
108
  ```ts
103
109
  import { createAgentbrowseClient } from '@mercuryo-ai/agentbrowse';
104
110
 
@@ -106,16 +112,38 @@ const client = createAgentbrowseClient({
106
112
  assistiveRuntime: createOpenAiCompatibleAssistiveRuntime({
107
113
  baseUrl: 'https://api.openai.com/v1',
108
114
  apiKey: process.env.OPENAI_API_KEY!,
109
- model: 'gpt-4.1-mini',
115
+ // Any OpenAI-compatible model that supports structured outputs.
116
+ model: process.env.OPENAI_MODEL ?? '<your-model>',
110
117
  }),
111
118
  });
112
119
  ```
113
120
 
114
121
  This pattern works well when:
115
122
 
116
- - your app is multi-tenant
117
- - you run parallel tests
118
- - different consumers in one process need different LLM settings
123
+ - your app is multi-tenant;
124
+ - you run parallel tests;
125
+ - different consumers in one process need different LLM settings.
126
+
127
+ ### Per-client vs global runtime
128
+
129
+ | Setup | When to use |
130
+ | --- | --- |
131
+ | `createAgentbrowseClient({ assistiveRuntime })` | Default. Keeps the runtime scoped to one client, works with multi-tenant and parallel scenarios. |
132
+ | `configureAgentbrowseAssistiveRuntime(runtime)` | Fallback for small scripts and single-tenant processes — sets one global runtime for the whole process. Not recommended when multiple consumers may coexist. |
133
+
134
+ Global-runtime shape:
135
+
136
+ ```ts
137
+ import { configureAgentbrowseAssistiveRuntime } from '@mercuryo-ai/agentbrowse';
138
+
139
+ configureAgentbrowseAssistiveRuntime(
140
+ createOpenAiCompatibleAssistiveRuntime({
141
+ baseUrl: 'https://api.openai.com/v1',
142
+ apiKey: process.env.OPENAI_API_KEY!,
143
+ model: process.env.OPENAI_MODEL ?? '<your-model>',
144
+ })
145
+ );
146
+ ```
119
147
 
120
148
  ## OpenAI-Compatible Helper Example
121
149
 
@@ -232,21 +260,18 @@ Examples:
232
260
  - OpenRouter base URL:
233
261
  `https://openrouter.ai/api/v1`
234
262
 
235
- ## Small Script Fallback
236
-
237
- For small scripts, you can also use:
238
-
239
- ```ts
240
- import { configureAgentbrowseAssistiveRuntime } from '@mercuryo-ai/agentbrowse';
241
- ```
242
-
243
- This is a convenience fallback, not the preferred embedded pattern.
244
-
245
263
  ## What Happens Without Assistive Runtime
246
264
 
247
- - `extract(...)` cannot run successfully
265
+ - `extract(...)` cannot run successfully.
248
266
  - `observe(session, goal)` still runs, but quality may be lower because
249
- AgentBrowse falls back to local heuristics instead of LLM-assisted ranking
267
+ AgentBrowse falls back to local heuristics instead of LLM-assisted
268
+ ranking.
269
+ - `fillProtectedForm(...)` returns
270
+ `{ kind: 'unexpected_error', reason: 'assisted_value_resolution_failed' }`
271
+ for fields that require LLM-assisted resolution (split `full_name` into
272
+ given/family, localised dropdown values like nationality on a
273
+ non-English page, or any field pinned to the `llm_assisted` policy).
274
+ See [Protected Fill Guide](./protected-fill.md#split-fields-and-assistive-runtime).
250
275
 
251
276
  ## Testing Runtime
252
277
 
@@ -42,12 +42,14 @@ invalidates them:
42
42
 
43
43
  After any of the above, call `observe(...)` again and use the new refs.
44
44
 
45
- At a high level, AgentBrowse has three kinds of behavior:
45
+ At a high level, AgentBrowse has four kinds of behavior:
46
46
 
47
47
  - normal browser execution for `launch`, `navigate`, `observe`, `act`,
48
48
  `status`, `screenshot`, and `close`
49
49
  - assistive page understanding for `extract` and some goal-based
50
50
  `observe(session, goal)` calls
51
+ - deterministic field data-plane for deciding which caller-supplied
52
+ value belongs in which observed field (`match`, `resolve`, `fill`)
51
53
  - protected fill for applying sensitive values you already have through a
52
54
  guarded form execution path
53
55
 
@@ -155,6 +157,22 @@ serve different intents:
155
157
  - `observe(session)` is for general page inspection
156
158
  - `observe(session, goal)` is for a focused question
157
159
 
160
+ These examples share a shape that works well: each names one control,
161
+ optionally anchored to a surface. A useful goal looks like
162
+ `"find <target> in <surface>"`:
163
+
164
+ - one target — a single field, button, or grid cell
165
+ - one surface — the active form, the open datepicker, the visible banner
166
+ - one step — the goal describes what the next `act` will target,
167
+ not the rest of the plan
168
+
169
+ When the task takes several steps, run one `observe` per step:
170
+
171
+ 1. `observe(session, "find the date picker trigger in the top search form")`
172
+ 2. `act(session, trigger.ref, "click")`
173
+ 3. `observe(session, "find May 5, 2026 in the open calendar")`
174
+ 4. `act(session, cell.ref, "click")`
175
+
158
176
  ### `act(session, targetRef, action, value?)`
159
177
 
160
178
  Executes a browser action against a `targetRef` returned by `observe(...)`.
@@ -197,6 +215,31 @@ Closes the browser session.
197
215
  This also terminates the internal sticky owner. Repeated closes and already-
198
216
  dead owner hosts are treated as idempotent.
199
217
 
218
+ ### `match` / `resolve` / `fill`
219
+
220
+ Three primitives for the «key–value pairs into an observed form» problem.
221
+ Instead of calling `act(session, ref, 'fill', value)` by hand, the
222
+ primitives let you hand a source of candidate values to `match(...)`,
223
+ resolve externally stored values through a caller-supplied adapter, and
224
+ apply the result to the browser deterministically — without the values
225
+ passing through LLM prompts or public result objects.
226
+
227
+ The typical shape is `match → (resolve) → fill`:
228
+
229
+ ```ts
230
+ import { match, resolve, fill } from '@mercuryo-ai/agentbrowse';
231
+
232
+ const matched = await match(emailTarget, {
233
+ from: { email: 'traveler@example.com' },
234
+ });
235
+ await fill(session, emailTarget, matched);
236
+ ```
237
+
238
+ See the dedicated [Match / Resolve / Fill Guide](./match-resolve-fill.md)
239
+ for the full mental model, walk-throughs (value in hand, external
240
+ lookup, batch, grouped protected forms), and the design rules (no raw
241
+ values in public results, stable resolved refs, adapter boundary).
242
+
200
243
  ## How To Handle Results
201
244
 
202
245
  All main commands use the same broad pattern:
@@ -264,6 +307,7 @@ See:
264
307
  ## Next Docs
265
308
 
266
309
  - [API Reference](./api-reference.md)
310
+ - [Match / Resolve / Fill Guide](./match-resolve-fill.md)
267
311
  - [Configuration Guide](./configuration.md)
268
312
  - [Assistive Runtime Guide](./assistive-runtime.md)
269
313
  - [Protected Fill Guide](./protected-fill.md)
@@ -13,9 +13,38 @@ integration.
13
13
  - use `observeResult.targets` as the default flat inventory
14
14
  - use `target.ref` as the input to `act(...)`
15
15
  - pass a plain schema object or a Zod schema to `extract(...)`
16
- - branch on stable top-level `error` codes, not on `reason`
16
+ - branch on stable top-level `error` codes, not on `reason` — the
17
+ per-command list lives in
18
+ [`api-reference.md` → Error codes by command](./api-reference.md#error-codes-by-command)
17
19
  - treat `reason` as human-readable diagnostics, not as a stable switch key
18
20
 
21
+ ## Match / Resolve / Fill
22
+
23
+ - branch on `match(...)` result `kind` — never infer from the presence
24
+ of individual fields on the result object
25
+ - default match results carry `valueRef` / `artifactRef` strings, not
26
+ raw values; the accessor that dereferences them is a non-enumerable
27
+ symbol on the `ready` result and does not survive JSON serialization
28
+ or `structuredClone`. Do not serialize ready results across process
29
+ boundaries — ship the `needs_resolution` plan instead and let the
30
+ downstream side run `resolve → fill` in one shot
31
+ - the main `AgentbrowseMatchResolver` has a required `resolve` plus
32
+ optional `resolveBatch` and optional `fill`. A narrow
33
+ `AgentbrowseGroupFillHandler` with just `fill` is also accepted in
34
+ the `{ resolver }` slot on `fill(...)` and is the right shape when
35
+ the caller already has a ready grouped artifact and does not fetch
36
+ anything. `resolve(plan, { with })` only accepts the main interface.
37
+ Runtime `fill(...)` returns `match_resolver_required` when the passed
38
+ object lacks a capability the current plan needs
39
+ - batch `resolve([...])` preserves input order — downstream code can
40
+ rebuild the `target → ready` mapping by index
41
+ - use `@mercuryo-ai/agentbrowse/testing` fixture builders
42
+ (`createFixtureMatchStore`, `createFixtureGroupStore`,
43
+ `createFixtureResolver`, `fixtureResolvedValue`,
44
+ `fixtureResolvedArtifact`) in unit tests instead of fabricating match
45
+ results by hand — hand-built results bypass the accessor pattern and
46
+ fail to fill
47
+
19
48
  ## Assistive Runtime
20
49
 
21
50
  - the assistive runtime adapter receives `args.options`
@@ -25,8 +54,8 @@ integration.
25
54
 
26
55
  ## Testing
27
56
 
28
- - use `@mercuryo-ai/agentbrowse/testing` for the stable fetch-backed assistive
29
- runtime helper
57
+ - use `@mercuryo-ai/agentbrowse/testing` for both the fetch-backed
58
+ assistive runtime helper and the match/resolve/fill fixture builders
30
59
  - do not depend on internal fixtures or unexported runtime-state helpers
31
60
 
32
61
  ## Packaging