@mercuryo-ai/agentbrowse 0.2.57 → 0.2.60

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 (72) hide show
  1. package/README.md +50 -56
  2. package/dist/command-name.js +1 -1
  3. package/dist/commands/act.d.ts.map +1 -1
  4. package/dist/commands/act.js +13 -5
  5. package/dist/commands/action-executor-helpers.d.ts.map +1 -1
  6. package/dist/commands/action-executor-helpers.js +10 -8
  7. package/dist/commands/click-activation-policy.d.ts.map +1 -1
  8. package/dist/commands/click-activation-policy.js +6 -2
  9. package/dist/commands/launch.d.ts +0 -1
  10. package/dist/commands/launch.d.ts.map +1 -1
  11. package/dist/commands/launch.js +2 -8
  12. package/dist/commands/observe-inventory.d.ts +5 -1
  13. package/dist/commands/observe-inventory.d.ts.map +1 -1
  14. package/dist/commands/observe-inventory.js +316 -5
  15. package/dist/commands/observe-persistence.d.ts.map +1 -1
  16. package/dist/commands/observe-persistence.js +2 -0
  17. package/dist/commands/observe-projection.d.ts +3 -2
  18. package/dist/commands/observe-projection.d.ts.map +1 -1
  19. package/dist/commands/observe-projection.js +1 -0
  20. package/dist/commands/observe-protected.d.ts +3 -1
  21. package/dist/commands/observe-protected.d.ts.map +1 -1
  22. package/dist/commands/observe-protected.js +23 -1
  23. package/dist/commands/observe-semantics.d.ts.map +1 -1
  24. package/dist/commands/observe-semantics.js +70 -0
  25. package/dist/commands/observe.d.ts +1 -0
  26. package/dist/commands/observe.d.ts.map +1 -1
  27. package/dist/commands/observe.js +4 -2
  28. package/dist/control-semantics.d.ts.map +1 -1
  29. package/dist/control-semantics.js +5 -0
  30. package/dist/date-value-normalization.d.ts +16 -0
  31. package/dist/date-value-normalization.d.ts.map +1 -0
  32. package/dist/date-value-normalization.js +117 -0
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +5 -24
  35. package/dist/library.d.ts +3 -0
  36. package/dist/library.d.ts.map +1 -1
  37. package/dist/library.js +2 -0
  38. package/dist/protected-fill.d.ts +3 -2
  39. package/dist/protected-fill.d.ts.map +1 -1
  40. package/dist/runtime-protected-state.d.ts.map +1 -1
  41. package/dist/runtime-protected-state.js +8 -1
  42. package/dist/runtime-state.d.ts +11 -0
  43. package/dist/runtime-state.d.ts.map +1 -1
  44. package/dist/secrets/form-matcher.d.ts +1 -2
  45. package/dist/secrets/form-matcher.d.ts.map +1 -1
  46. package/dist/secrets/form-matcher.js +125 -119
  47. package/dist/secrets/matching-helpers.d.ts +13 -0
  48. package/dist/secrets/matching-helpers.d.ts.map +1 -0
  49. package/dist/secrets/matching-helpers.js +147 -0
  50. package/dist/secrets/observed-field-resolution.d.ts +43 -0
  51. package/dist/secrets/observed-field-resolution.d.ts.map +1 -0
  52. package/dist/secrets/observed-field-resolution.js +223 -0
  53. package/dist/secrets/protected-field-semantics.d.ts.map +1 -1
  54. package/dist/secrets/protected-field-semantics.js +3 -2
  55. package/dist/secrets/protected-fill.d.ts +3 -1
  56. package/dist/secrets/protected-fill.d.ts.map +1 -1
  57. package/dist/secrets/protected-fill.js +31 -0
  58. package/dist/secrets/protected-value-adapters.d.ts.map +1 -1
  59. package/dist/secrets/protected-value-adapters.js +14 -22
  60. package/dist/secrets/types.d.ts +3 -0
  61. package/dist/secrets/types.d.ts.map +1 -1
  62. package/docs/README.md +15 -2
  63. package/docs/api-reference.md +13 -3
  64. package/docs/assistive-runtime.md +63 -7
  65. package/docs/configuration.md +16 -8
  66. package/docs/getting-started.md +17 -8
  67. package/docs/integration-checklist.md +8 -7
  68. package/docs/protected-fill.md +40 -7
  69. package/docs/testing.md +4 -3
  70. package/docs/troubleshooting.md +88 -34
  71. package/examples/README.md +9 -2
  72. package/package.json +8 -3
@@ -0,0 +1,223 @@
1
+ import { canonicalFieldKeyOf, directFieldSignalValuesOf, fieldKeyEvidenceTerms, normalizeText, selectorRootOf, signalValuesOf, } from './matching-helpers.js';
2
+ function tokensOf(value) {
3
+ return value.split(/[^a-z0-9]+/).filter((token) => token.length > 0);
4
+ }
5
+ function evidenceMatchScore(term, signals) {
6
+ const termTokens = tokensOf(term);
7
+ let best = 0;
8
+ for (const signal of signals) {
9
+ if (signal === term) {
10
+ return 6;
11
+ }
12
+ if (signal.includes(term) || term.includes(signal)) {
13
+ best = Math.max(best, 4);
14
+ continue;
15
+ }
16
+ const signalTokens = new Set(tokensOf(signal));
17
+ const sharedTokenCount = termTokens.filter((token) => signalTokens.has(token)).length;
18
+ if (sharedTokenCount >= 2) {
19
+ best = Math.max(best, 3);
20
+ continue;
21
+ }
22
+ if (sharedTokenCount === 1 &&
23
+ termTokens.some((token) => token.length >= 4 && signalTokens.has(token))) {
24
+ best = Math.max(best, 2);
25
+ }
26
+ }
27
+ return best;
28
+ }
29
+ function candidateEvidenceTerms(candidate) {
30
+ const terms = new Set();
31
+ const push = (value) => {
32
+ const normalized = normalizeText(value);
33
+ if (normalized) {
34
+ terms.add(normalized);
35
+ }
36
+ };
37
+ for (const term of fieldKeyEvidenceTerms(candidate.fieldKey)) {
38
+ push(term);
39
+ }
40
+ push(candidate.label);
41
+ for (const tag of candidate.semanticTags ?? []) {
42
+ push(tag);
43
+ }
44
+ return [...terms];
45
+ }
46
+ function targetValueTypeOf(target) {
47
+ const inputType = normalizeText(target.inputType);
48
+ const autocomplete = normalizeText(target.autocomplete);
49
+ const signals = directFieldSignalValuesOf(target);
50
+ if (inputType === 'email' || autocomplete.includes('email')) {
51
+ return 'email';
52
+ }
53
+ if (inputType === 'url') {
54
+ return 'url';
55
+ }
56
+ if (inputType === 'number') {
57
+ return 'number';
58
+ }
59
+ if (inputType === 'date' ||
60
+ autocomplete === 'bday' ||
61
+ signals.some((signal) => /\b(date of birth|birth date|dob|birthday)\b/.test(signal))) {
62
+ return 'date';
63
+ }
64
+ return 'text';
65
+ }
66
+ function isEmailValue(value) {
67
+ return typeof value === 'string' && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value.trim());
68
+ }
69
+ function isDateValue(value) {
70
+ return typeof value === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(value.trim());
71
+ }
72
+ function isNumberValue(value) {
73
+ if (typeof value === 'number') {
74
+ return Number.isFinite(value);
75
+ }
76
+ return typeof value === 'string' && /^-?\d+(?:\.\d+)?$/.test(value.trim());
77
+ }
78
+ function isUrlValue(value) {
79
+ return typeof value === 'string' && /^https?:\/\//.test(value.trim());
80
+ }
81
+ function isShapeCompatible(target, candidate) {
82
+ if (candidate.type === 'secret') {
83
+ return false;
84
+ }
85
+ const targetValueType = targetValueTypeOf(target);
86
+ switch (targetValueType) {
87
+ case 'email':
88
+ return (isEmailValue(candidate.value) &&
89
+ (!candidate.type || candidate.type === 'email' || candidate.type === 'text'));
90
+ case 'date':
91
+ return (isDateValue(candidate.value) &&
92
+ (!candidate.type || candidate.type === 'date' || candidate.type === 'text'));
93
+ case 'number':
94
+ return (isNumberValue(candidate.value) &&
95
+ (!candidate.type || candidate.type === 'number' || candidate.type === 'text'));
96
+ case 'url':
97
+ return (isUrlValue(candidate.value) &&
98
+ (!candidate.type || candidate.type === 'url' || candidate.type === 'text'));
99
+ case 'text':
100
+ return ((typeof candidate.value === 'string' || typeof candidate.value === 'number') &&
101
+ (!candidate.type ||
102
+ candidate.type === 'text' ||
103
+ candidate.type === 'email' ||
104
+ candidate.type === 'number' ||
105
+ candidate.type === 'date' ||
106
+ candidate.type === 'url'));
107
+ }
108
+ }
109
+ function isScopeApplicable(candidate, host) {
110
+ if (!candidate.applicability || candidate.applicability.target === 'global') {
111
+ return true;
112
+ }
113
+ return Boolean(host && normalizeText(candidate.applicability.value) === normalizeText(host));
114
+ }
115
+ function contextCompatibilityScore(target, candidate) {
116
+ let score = 0;
117
+ const targetSelectorRoot = normalizeText(selectorRootOf(target) ?? undefined);
118
+ if (targetSelectorRoot && normalizeText(candidate.selectorRoot) === targetSelectorRoot) {
119
+ score += 2;
120
+ }
121
+ const targetPageFormSelector = normalizeText(target.pageFormSelector?.trim());
122
+ if (targetPageFormSelector &&
123
+ normalizeText(candidate.pageFormSelector?.trim()) === targetPageFormSelector) {
124
+ score += 2;
125
+ }
126
+ return score;
127
+ }
128
+ function candidatePriorityScore(candidate, host) {
129
+ let score = 0;
130
+ if (candidate.applicability?.target === 'host' &&
131
+ normalizeText(candidate.applicability.value) === normalizeText(host)) {
132
+ score += 3;
133
+ }
134
+ if (candidate.source === 'session_open_value') {
135
+ score += 2;
136
+ }
137
+ return score;
138
+ }
139
+ function evidenceScore(target, candidate) {
140
+ const directSignals = directFieldSignalValuesOf(target);
141
+ const broadSignals = signalValuesOf(target);
142
+ const terms = candidateEvidenceTerms(candidate);
143
+ let bestTermScore = 0;
144
+ for (const term of terms) {
145
+ bestTermScore = Math.max(bestTermScore, evidenceMatchScore(term, directSignals));
146
+ bestTermScore = Math.max(bestTermScore, evidenceMatchScore(term, broadSignals) - 1);
147
+ }
148
+ return bestTermScore + contextCompatibilityScore(target, candidate) + 1;
149
+ }
150
+ function rankCandidates(target, candidates, options = {}) {
151
+ return candidates
152
+ .map((candidate) => ({
153
+ candidate,
154
+ canonicalFieldKey: canonicalFieldKeyOf(candidate.fieldKey) || candidate.fieldKey,
155
+ score: evidenceScore(target, candidate) + candidatePriorityScore(candidate, options.host),
156
+ }))
157
+ .sort((left, right) => {
158
+ if (right.score !== left.score) {
159
+ return right.score - left.score;
160
+ }
161
+ return left.candidate.candidateRef.localeCompare(right.candidate.candidateRef);
162
+ });
163
+ }
164
+ export function resolveObservedField(target, candidates, options = {}) {
165
+ if (options.protectedTargetRefs?.has(target.ref)) {
166
+ return {
167
+ status: 'no_match',
168
+ targetRef: target.ref,
169
+ reason: 'protected_target',
170
+ };
171
+ }
172
+ if (candidates.length === 0) {
173
+ return {
174
+ status: 'no_match',
175
+ targetRef: target.ref,
176
+ reason: 'no_candidate',
177
+ };
178
+ }
179
+ const scopeEligible = candidates.filter((candidate) => isScopeApplicable(candidate, options.host));
180
+ if (scopeEligible.length === 0) {
181
+ return {
182
+ status: 'no_match',
183
+ targetRef: target.ref,
184
+ reason: 'scope_ineligible',
185
+ };
186
+ }
187
+ const shapeEligible = scopeEligible.filter((candidate) => isShapeCompatible(target, candidate));
188
+ if (shapeEligible.length === 0) {
189
+ return {
190
+ status: 'no_match',
191
+ targetRef: target.ref,
192
+ reason: 'incompatible_shape',
193
+ };
194
+ }
195
+ const minimumScore = options.minimumScore ?? 5;
196
+ const clearWinnerDelta = options.clearWinnerDelta ?? 2;
197
+ const ranked = rankCandidates(target, shapeEligible, { host: options.host });
198
+ const best = ranked[0];
199
+ if (!best || best.score < minimumScore) {
200
+ return {
201
+ status: 'no_match',
202
+ targetRef: target.ref,
203
+ reason: 'low_confidence',
204
+ };
205
+ }
206
+ const ambiguousCandidates = ranked.filter((candidate) => best.score - candidate.score < clearWinnerDelta);
207
+ if (ambiguousCandidates.length > 1) {
208
+ return {
209
+ status: 'ambiguous',
210
+ targetRef: target.ref,
211
+ candidates: ambiguousCandidates.map((candidate) => candidate.candidate.candidateRef),
212
+ };
213
+ }
214
+ return {
215
+ status: 'matched',
216
+ targetRef: target.ref,
217
+ fieldKey: best.canonicalFieldKey,
218
+ value: best.candidate.value,
219
+ source: best.candidate.source,
220
+ confidence: best.score >= 7 ? 'high' : 'medium',
221
+ candidateRef: best.candidate.candidateRef,
222
+ };
223
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"protected-field-semantics.d.ts","sourceRoot":"","sources":["../../src/secrets/protected-field-semantics.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,EAAE,yBAAyB,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEpG,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,gBAAgB,CAAC;IACvB,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,SAAS,EAAE,yBAAyB,CAAC;CACtC;AAiED,wBAAgB,oCAAoC,CAClD,MAAM,EAAE,gBAAgB,GACvB,yBAAyB,EAAE,CAwJ7B"}
1
+ {"version":3,"file":"protected-field-semantics.d.ts","sourceRoot":"","sources":["../../src/secrets/protected-field-semantics.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,EAAE,yBAAyB,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEpG,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,gBAAgB,CAAC;IACvB,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,SAAS,EAAE,yBAAyB,CAAC;CACtC;AAiED,wBAAgB,oCAAoC,CAClD,MAAM,EAAE,gBAAgB,GACvB,yBAAyB,EAAE,CAyJ7B"}
@@ -1,5 +1,5 @@
1
1
  import { inferComparableValueTypeFromFacts } from '../control-semantics.js';
2
- const LOGIN_USERNAME_SIGNAL_RE = /\b(email|e-mail|username|user name|login|account(?: email| name)?|member(?: email| name| id)?)\b/i;
2
+ const LOGIN_USERNAME_SIGNAL_RE = /\b(email|e-mail|username|user name|login|account(?:[ _-]?(?:email|name|id|login|username))|member(?:[ _-]?(?:email|name|id)))\b/i;
3
3
  const LOGIN_PASSWORD_SIGNAL_RE = /\b(password|passcode|pass word)\b/i;
4
4
  const PAYMENT_CARD_NAME_SIGNAL_RE = /\b(cardholder|card holder|name on card|cardholder name|full name)\b/i;
5
5
  const PAYMENT_CARD_PAN_SIGNAL_RE = /\b(card number|card no|cc-number|cc number)\b/i;
@@ -65,7 +65,8 @@ export function inferProtectedFieldMeaningFromTarget(target) {
65
65
  if (autocomplete.includes('username') ||
66
66
  autocomplete.includes('email') ||
67
67
  inputType === 'email' ||
68
- /\b(user(name)?|login|email|account)\b/.test(inputName) ||
68
+ /\b(user(name)?|login|email)\b/.test(inputName) ||
69
+ /\b(account|member)(?:_?(?:email|name|id|login|username))\b/.test(inputName) ||
69
70
  signals.some((signal) => LOGIN_USERNAME_SIGNAL_RE.test(signal))) {
70
71
  hints.push({ kind: 'login', fieldKey: 'username', valueHint: 'direct' });
71
72
  }
@@ -1,5 +1,6 @@
1
1
  import type { Page } from 'playwright-core';
2
2
  import type { BrowserSessionState } from '../browser-session-state.js';
3
+ import { type DateValueNormalizationErrorReason } from '../date-value-normalization.js';
3
4
  import { type TargetDescriptor } from '../runtime-state.js';
4
5
  import type { PersistedFillableForm, StoredSecretFieldPolicies, StoredSecretFieldKey } from './types.js';
5
6
  type ProtectedFillAction = 'fill' | 'select' | 'type';
@@ -11,6 +12,7 @@ export interface ProtectedFieldError extends ProtectedFilledField {
11
12
  reason: 'client_validation_rejected' | 'value_not_applied';
12
13
  validationTextRedacted?: true;
13
14
  }
15
+ type ProtectedUnexpectedErrorReason = 'missing_protected_value' | 'unsupported_protected_field_group' | 'deterministic_only_resolution_failed' | 'assisted_value_resolution_failed' | 'action_failed' | DateValueNormalizationErrorReason;
14
16
  type ProtectedBindingStaleReason = 'target_missing' | 'target_not_live' | 'page_signature_mismatch' | 'dom_signature_mismatch' | 'locator_resolution_failed' | 'target_blocked';
15
17
  export type ProtectedFillExecutionResult = {
16
18
  kind: 'success';
@@ -27,7 +29,7 @@ export type ProtectedFillExecutionResult = {
27
29
  fieldErrors: ProtectedFieldError[];
28
30
  } | {
29
31
  kind: 'unexpected_error';
30
- reason: 'missing_protected_value' | 'unsupported_protected_field_group' | 'deterministic_only_resolution_failed' | 'assisted_value_resolution_failed' | 'action_failed';
32
+ reason: ProtectedUnexpectedErrorReason;
31
33
  };
32
34
  declare function actionForTarget(target: TargetDescriptor): ProtectedFillAction;
33
35
  declare function formatCardExpiry(month: string, year: string): string;
@@ -1 +1 @@
1
- {"version":3,"file":"protected-fill.d.ts","sourceRoot":"","sources":["../../src/secrets/protected-fill.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAErD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,EAAyB,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAiBnF,OAAO,KAAK,EAEV,qBAAqB,EACrB,yBAAyB,EACzB,oBAAoB,EACrB,MAAM,YAAY,CAAC;AAGpB,KAAK,mBAAmB,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEtD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAoB,SAAQ,oBAAoB;IAC/D,MAAM,EAAE,4BAA4B,GAAG,mBAAmB,CAAC;IAC3D,sBAAsB,CAAC,EAAE,IAAI,CAAC;CAC/B;AAED,KAAK,2BAA2B,GAC5B,gBAAgB,GAChB,iBAAiB,GACjB,yBAAyB,GACzB,wBAAwB,GACxB,2BAA2B,GAC3B,gBAAgB,CAAC;AAarB,MAAM,MAAM,4BAA4B,GACpC;IACE,IAAI,EAAE,SAAS,CAAC;IAChB,YAAY,EAAE,oBAAoB,EAAE,CAAC;CACtC,GACD;IACE,IAAI,EAAE,eAAe,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,oBAAoB,EAAE,CAAC;IAClC,MAAM,EAAE,2BAA2B,CAAC;IACpC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB,GACD;IACE,IAAI,EAAE,mBAAmB,CAAC;IAC1B,YAAY,EAAE,oBAAoB,EAAE,CAAC;IACrC,WAAW,EAAE,mBAAmB,EAAE,CAAC;CACpC,GACD;IACE,IAAI,EAAE,kBAAkB,CAAC;IACzB,MAAM,EACF,yBAAyB,GACzB,mCAAmC,GACnC,sCAAsC,GACtC,kCAAkC,GAClC,eAAe,CAAC;CACrB,CAAC;AAEN,iBAAS,eAAe,CAAC,MAAM,EAAE,gBAAgB,GAAG,mBAAmB,CActE;AAED,iBAAS,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAK7D;AAsTD,wBAAsB,oBAAoB,CAAC,MAAM,EAAE;IACjD,OAAO,EAAE,mBAAmB,CAAC;IAC7B,IAAI,EAAE,IAAI,CAAC;IACX,YAAY,EAAE,qBAAqB,CAAC;IACpC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,aAAa,CAAC,EAAE,yBAAyB,CAAC;CAC3C,GAAG,OAAO,CAAC,4BAA4B,CAAC,CAgNxC;AAED,eAAO,MAAM,mBAAmB;;;qCAIjB,aAAa,CAAC,oBAAoB,CAAC,mBAC7B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,WAC9B,gBAAgB;CAuB5B,CAAC"}
1
+ {"version":3,"file":"protected-fill.d.ts","sourceRoot":"","sources":["../../src/secrets/protected-fill.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAErD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,EAEL,KAAK,iCAAiC,EAEvC,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAyB,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAiBnF,OAAO,KAAK,EAEV,qBAAqB,EACrB,yBAAyB,EACzB,oBAAoB,EACrB,MAAM,YAAY,CAAC;AAGpB,KAAK,mBAAmB,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEtD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAoB,SAAQ,oBAAoB;IAC/D,MAAM,EAAE,4BAA4B,GAAG,mBAAmB,CAAC;IAC3D,sBAAsB,CAAC,EAAE,IAAI,CAAC;CAC/B;AAED,KAAK,8BAA8B,GAC/B,yBAAyB,GACzB,mCAAmC,GACnC,sCAAsC,GACtC,kCAAkC,GAClC,eAAe,GACf,iCAAiC,CAAC;AAEtC,KAAK,2BAA2B,GAC5B,gBAAgB,GAChB,iBAAiB,GACjB,yBAAyB,GACzB,wBAAwB,GACxB,2BAA2B,GAC3B,gBAAgB,CAAC;AAarB,MAAM,MAAM,4BAA4B,GACpC;IACE,IAAI,EAAE,SAAS,CAAC;IAChB,YAAY,EAAE,oBAAoB,EAAE,CAAC;CACtC,GACD;IACE,IAAI,EAAE,eAAe,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,oBAAoB,EAAE,CAAC;IAClC,MAAM,EAAE,2BAA2B,CAAC;IACpC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB,GACD;IACE,IAAI,EAAE,mBAAmB,CAAC;IAC1B,YAAY,EAAE,oBAAoB,EAAE,CAAC;IACrC,WAAW,EAAE,mBAAmB,EAAE,CAAC;CACpC,GACD;IACE,IAAI,EAAE,kBAAkB,CAAC;IACzB,MAAM,EAAE,8BAA8B,CAAC;CACxC,CAAC;AAEN,iBAAS,eAAe,CAAC,MAAM,EAAE,gBAAgB,GAAG,mBAAmB,CActE;AAED,iBAAS,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAK7D;AAwUD,wBAAsB,oBAAoB,CAAC,MAAM,EAAE;IACjD,OAAO,EAAE,mBAAmB,CAAC;IAC7B,IAAI,EAAE,IAAI,CAAC;IACX,YAAY,EAAE,qBAAqB,CAAC;IACpC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,aAAa,CAAC,EAAE,yBAAyB,CAAC;CAC3C,GAAG,OAAO,CAAC,4BAA4B,CAAC,CAgOxC;AAED,eAAO,MAAM,mBAAmB;;;qCAIjB,aAAa,CAAC,oBAAoB,CAAC,mBAC7B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,WAC9B,gBAAgB;CAuB5B,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import { AgentbrowseAssistiveRuntimeMissingError } from '../assistive-runtime.js';
2
+ import { isDateValueNormalizationErrorReason, normalizeStructuredDateValue, } from '../date-value-normalization.js';
2
3
  import { getSurface, getTarget } from '../runtime-state.js';
3
4
  import { createAcceptanceProbe, waitForAcceptanceProbe } from '../commands/action-acceptance.js';
4
5
  import { applyActionWithFallbacks } from '../commands/action-executor.js';
@@ -44,6 +45,18 @@ function resolveBindingValue(fields, target, protectedValues, fieldPolicies, ass
44
45
  if (typeof deterministicValue === 'string' && deterministicValue.length > 0) {
45
46
  return deterministicValue;
46
47
  }
48
+ if (field.fieldKey === 'date_of_birth') {
49
+ const rawDateValue = protectedValues.date_of_birth;
50
+ if (typeof rawDateValue === 'string' && rawDateValue.trim().length > 0) {
51
+ const normalizedDate = normalizeStructuredDateValue(rawDateValue);
52
+ if (normalizedDate.kind === 'error') {
53
+ throw new Error(normalizedDate.reason);
54
+ }
55
+ if (normalizedDate.kind === 'not_date_like') {
56
+ throw new Error('invalid_date_value');
57
+ }
58
+ }
59
+ }
47
60
  if ((field.valueHint ?? 'direct') !== 'direct') {
48
61
  throw new Error('deterministic_only_resolution_failed');
49
62
  }
@@ -253,6 +266,10 @@ function staleReasonFromError(error) {
253
266
  }
254
267
  return null;
255
268
  }
269
+ function unexpectedReasonFromError(error) {
270
+ const message = error instanceof Error ? error.message : String(error);
271
+ return isDateValueNormalizationErrorReason(message) ? message : null;
272
+ }
256
273
  export async function executeProtectedFill(params) {
257
274
  const preparedBindings = await prepareBindings(params.session, params.page, params.fillableForm);
258
275
  if (!Array.isArray(preparedBindings)) {
@@ -305,6 +322,13 @@ export async function executeProtectedFill(params) {
305
322
  actionValue = resolveBindingValue(binding.fields, binding.target, params.protectedValues, params.fieldPolicies, assistedValues);
306
323
  }
307
324
  catch (error) {
325
+ const unexpectedReason = unexpectedReasonFromError(error);
326
+ if (unexpectedReason) {
327
+ return {
328
+ kind: 'unexpected_error',
329
+ reason: unexpectedReason,
330
+ };
331
+ }
308
332
  const message = error instanceof Error ? error.message : String(error);
309
333
  if (message === 'missing_protected_value') {
310
334
  return {
@@ -383,6 +407,13 @@ export async function executeProtectedFill(params) {
383
407
  filledFields.push(...flattenFilledFields(binding.fieldKeys, binding.targetRef));
384
408
  }
385
409
  catch (error) {
410
+ const unexpectedReason = unexpectedReasonFromError(error);
411
+ if (unexpectedReason) {
412
+ return {
413
+ kind: 'unexpected_error',
414
+ reason: unexpectedReason,
415
+ };
416
+ }
386
417
  if (acceptanceProbe) {
387
418
  acceptanceResult =
388
419
  acceptanceResult ?? (await waitForAcceptanceProbe(acceptanceProbe).catch(() => null));
@@ -1 +1 @@
1
- {"version":3,"file":"protected-value-adapters.d.ts","sourceRoot":"","sources":["../../src/secrets/protected-value-adapters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,EAAE,wBAAwB,EAAwB,MAAM,YAAY,CAAC;AAwFjF,wBAAgB,yCAAyC,CACvD,OAAO,EAAE,IAAI,CAAC,wBAAwB,EAAE,UAAU,GAAG,WAAW,CAAC,EACjE,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACvC,MAAM,CAAC,EAAE,IAAI,CAAC,gBAAgB,EAAE,OAAO,GAAG,cAAc,GAAG,SAAS,CAAC,GACpE,MAAM,GAAG,IAAI,CAqEf"}
1
+ {"version":3,"file":"protected-value-adapters.d.ts","sourceRoot":"","sources":["../../src/secrets/protected-value-adapters.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,EAAE,wBAAwB,EAAwB,MAAM,YAAY,CAAC;AA+EjF,wBAAgB,yCAAyC,CACvD,OAAO,EAAE,IAAI,CAAC,wBAAwB,EAAE,UAAU,GAAG,WAAW,CAAC,EACjE,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACvC,MAAM,CAAC,EAAE,IAAI,CAAC,gBAAgB,EAAE,OAAO,GAAG,cAAc,GAAG,SAAS,CAAC,GACpE,MAAM,GAAG,IAAI,CAqEf"}
@@ -1,3 +1,4 @@
1
+ import { normalizeStructuredDateValue } from '../date-value-normalization.js';
1
2
  function normalizeWhitespace(value) {
2
3
  return value.replace(/\s+/g, ' ').trim();
3
4
  }
@@ -11,18 +12,9 @@ function directStoredValue(protectedValues, fieldKey) {
11
12
  const value = protectedValues[fieldKey];
12
13
  return typeof value === 'string' && value.trim().length > 0 ? value : null;
13
14
  }
14
- function parseIsoDate(value) {
15
- const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value.trim());
16
- if (!match) {
17
- return null;
18
- }
19
- const year = match[1];
20
- const month = match[2];
21
- const day = match[3];
22
- if (!year || !month || !day) {
23
- return null;
24
- }
25
- return { year, month, day };
15
+ function normalizeDateValue(value) {
16
+ const normalized = normalizeStructuredDateValue(value);
17
+ return normalized.kind === 'normalized' ? normalized : null;
26
18
  }
27
19
  function monthName(month) {
28
20
  const names = [
@@ -65,29 +57,29 @@ export function resolveDeterministicProtectedBindingValue(binding, protectedValu
65
57
  if (!dateOfBirth) {
66
58
  return null;
67
59
  }
60
+ const normalizedDate = normalizeDateValue(dateOfBirth);
61
+ if (!normalizedDate) {
62
+ return null;
63
+ }
68
64
  const valueHint = binding.valueHint ?? 'direct';
69
65
  if (valueHint === 'direct') {
70
- return dateOfBirth;
71
- }
72
- const parsed = parseIsoDate(dateOfBirth);
73
- if (!parsed) {
74
- return null;
66
+ return normalizedDate.iso;
75
67
  }
76
68
  if (valueHint === 'date_of_birth.day') {
77
- return String(Number(parsed.day));
69
+ return String(Number(normalizedDate.day));
78
70
  }
79
71
  if (valueHint === 'date_of_birth.year') {
80
- return parsed.year;
72
+ return normalizedDate.year;
81
73
  }
82
74
  if (valueHint === 'date_of_birth.month') {
83
75
  const style = monthProjectionStyle(target);
84
76
  if (style === 'name') {
85
- return monthName(parsed.month);
77
+ return monthName(normalizedDate.month);
86
78
  }
87
79
  if (style === 'short') {
88
- return monthShortName(parsed.month);
80
+ return monthShortName(normalizedDate.month);
89
81
  }
90
- return parsed.month;
82
+ return normalizedDate.month;
91
83
  }
92
84
  return null;
93
85
  }
@@ -11,6 +11,7 @@ export type LoginFieldKey = (typeof LOGIN_FIELD_KEYS)[number];
11
11
  export type IdentityFieldKey = (typeof IDENTITY_FIELD_KEYS)[number];
12
12
  export type PaymentCardFieldKey = (typeof PAYMENT_CARD_FIELD_KEYS)[number];
13
13
  export type StoredSecretFieldKey = LoginFieldKey | IdentityFieldKey | PaymentCardFieldKey;
14
+ export type LoginStepKind = 'full' | 'identifier' | 'password';
14
15
  export type StoredSecretScope = 'site' | 'global';
15
16
  export type ProtectedFieldPolicy = 'deterministic_only' | 'llm_assisted';
16
17
  export declare const PROTECTED_BINDING_VALUE_HINTS: readonly ["direct", "full_name.given", "full_name.family", "date_of_birth.day", "date_of_birth.month", "date_of_birth.year"];
@@ -60,7 +61,9 @@ export interface PersistedFillableForm {
60
61
  fillRef: string;
61
62
  pageRef: string;
62
63
  scopeRef?: string;
64
+ pageFormSelector?: string;
63
65
  purpose: string;
66
+ loginStep?: LoginStepKind;
64
67
  presence?: FillableFormPresence;
65
68
  scopeEpoch?: number;
66
69
  fields: FillableFormFieldBinding[];
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/secrets/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/secrets/types.ts"],"names":[],"mappings":"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"}
package/docs/README.md CHANGED
@@ -2,7 +2,20 @@
2
2
 
3
3
  Public guides for `@mercuryo-ai/agentbrowse`.
4
4
 
5
- Start here:
5
+ **Reading order for a new integration:**
6
+
7
+ 1. [Package README](../README.md) — 5-minute overview and Quick Start.
8
+ 2. [getting-started.md](./getting-started.md) — extended tutorial that
9
+ turns the Quick Start into a working model for every main API.
10
+ 3. [api-reference.md](./api-reference.md) — lookup for types, result
11
+ shapes, and error codes (keep open while coding).
12
+ 4. Only-when-needed: [configuration.md](./configuration.md),
13
+ [assistive-runtime.md](./assistive-runtime.md),
14
+ [protected-fill.md](./protected-fill.md).
15
+ 5. Before shipping: [integration-checklist.md](./integration-checklist.md)
16
+ and [troubleshooting.md](./troubleshooting.md).
17
+
18
+ Full list:
6
19
 
7
20
  - [getting-started.md](./getting-started.md)
8
21
  The first guide to read after the package README. It explains the normal
@@ -18,7 +31,7 @@ Start here:
18
31
  - [protected-fill.md](./protected-fill.md)
19
32
  How protected fill works and when to use it instead of a normal fill action.
20
33
  - [integration-checklist.md](./integration-checklist.md)
21
- Current contract checklist for packages and services that integrate AgentBrowse.
34
+ Checklist for packages and services that integrate AgentBrowse.
22
35
  - [testing.md](./testing.md)
23
36
  Stable testing helpers for packages that wrap AgentBrowse.
24
37
  - [troubleshooting.md](./troubleshooting.md)
@@ -1,7 +1,7 @@
1
1
  # AgentBrowse API Reference
2
2
 
3
3
  This page is the compact reference for the public `@mercuryo-ai/agentbrowse`
4
- library surface.
4
+ library API.
5
5
 
6
6
  ## Main Functions
7
7
 
@@ -113,6 +113,16 @@ branching:
113
113
 
114
114
  These arrays back the exported `*ErrorCode` types.
115
115
 
116
+ ## Error Classes
117
+
118
+ For code paths that want `instanceof` checks instead of string matching on
119
+ `error` codes:
120
+
121
+ - `AgentbrowseAssistiveRuntimeMissingError` — thrown when an assistive-only
122
+ command runs without an assistive runtime configured.
123
+ - `AssistiveStructuredOutputTruncatedError` — thrown when the assistive
124
+ runtime returns a structured output that was cut off mid-response.
125
+
116
126
  ## Core Result Shapes
117
127
 
118
128
  All main commands use the same top-level pattern:
@@ -213,7 +223,7 @@ fields and does not treat the schema stringification format as part of the API.
213
223
 
214
224
  ## Assistive Runtime Types
215
225
 
216
- The root package exports the current assistive runtime contract:
226
+ The root package exports the types that describe the assistive runtime:
217
227
 
218
228
  - `AgentbrowseAssistiveChatCompletionRequest`
219
229
  - `AgentbrowseAssistiveChatCompletionOptions`
@@ -229,7 +239,7 @@ Your adapter receives `args.options.messages`, optional
229
239
 
230
240
  ## Testing Subpath
231
241
 
232
- The package publishes a dedicated testing surface:
242
+ The package publishes a dedicated `/testing` subpath:
233
243
 
234
244
  ```ts
235
245
  import {
@@ -18,15 +18,13 @@ Today, assistive behavior matters mainly for:
18
18
  If you only need browser actions and normal page inspection, you can ignore
19
19
  assistive runtime completely.
20
20
 
21
- ## The Runtime Contract
21
+ ## The Runtime Shape
22
22
 
23
23
  AgentBrowse does not ship a built-in OpenAI or OpenRouter adapter.
24
24
 
25
25
  Instead, you provide a small runtime object with one responsibility:
26
26
  create a chat-completions client.
27
27
 
28
- The shape is:
29
-
30
28
  ```ts
31
29
  {
32
30
  createLlmClient: () => ({
@@ -38,8 +36,66 @@ The shape is:
38
36
  }
39
37
  ```
40
38
 
41
- That means any OpenAI-compatible chat-completions backend can work, as long as
42
- your adapter returns the expected response shape.
39
+ Any OpenAI-compatible chat-completions backend can work, as long as your
40
+ adapter returns the expected response shape.
41
+
42
+ ### `options.response_model`
43
+
44
+ When AgentBrowse needs structured JSON (from `extract(...)` or certain
45
+ goal-driven `observe(...)` paths), it passes `response_model` describing
46
+ the expected output:
47
+
48
+ ```ts
49
+ {
50
+ name: string; // short identifier for the schema (e.g. "checkout_total")
51
+ schema: ZodType; // a Zod schema AgentBrowse wants the LLM to conform to
52
+ }
53
+ ```
54
+
55
+ Your adapter is responsible for translating `response_model.schema` into
56
+ whatever structured-output mechanism your provider supports. For OpenAI,
57
+ this usually means:
58
+
59
+ 1. Convert the Zod schema to JSON Schema (e.g. with
60
+ `@browserbasehq/stagehand`'s `toJsonSchema`, or your own helper).
61
+ 2. Pass it as `response_format: { type: 'json_schema', json_schema: { ... } }`.
62
+ 3. Parse `choices[0].message.content` and return `{ data, usage? }`.
63
+
64
+ If your provider does not support structured outputs, you can prompt the
65
+ model to return JSON and parse the result yourself — as long as the
66
+ returned `data` conforms to the schema, AgentBrowse does not care how it
67
+ was produced.
68
+
69
+ ### `options.image`
70
+
71
+ For vision-assisted extraction, AgentBrowse passes a screenshot:
72
+
73
+ ```ts
74
+ {
75
+ buffer: Buffer; // raw image bytes
76
+ description?: string; // short hint about what's in the image
77
+ }
78
+ ```
79
+
80
+ Most adapters encode the buffer as a base64 data URL and inject it as a
81
+ user-turn content block. See the helper below.
82
+
83
+ ### Errors
84
+
85
+ Your adapter throws on provider errors; AgentBrowse maps them to typed
86
+ failures on the calling command:
87
+
88
+ - HTTP errors (401/403/429/5xx): throw a normal `Error` with a descriptive
89
+ message. AgentBrowse surfaces it as a command failure.
90
+ - Missing/empty content: throw; do not return `{ data: null }`.
91
+ - Truncated structured output: AgentBrowse detects this and throws
92
+ `AssistiveStructuredOutputTruncatedError` — usually a signal to raise
93
+ `maxOutputTokens`.
94
+
95
+ The `AgentbrowseAssistiveLlmClient` type exported from the root package
96
+ describes the client object returned by `createLlmClient()`. Use it when you
97
+ want your adapter to conform to the expected shape without redefining it
98
+ by hand.
43
99
 
44
100
  ## Recommended Setup
45
101
 
@@ -203,5 +259,5 @@ import {
203
259
  } from '@mercuryo-ai/agentbrowse/testing';
204
260
  ```
205
261
 
206
- That helper installs a fetch-backed runtime with the same public assistive
207
- runtime contract used by the main package.
262
+ That helper installs a fetch-backed runtime with the same public
263
+ assistive-runtime shape used by the main package.
@@ -40,8 +40,8 @@ const attached = await attach(remoteCdpUrl, {
40
40
  });
41
41
  ```
42
42
 
43
- That provider label is metadata only. AgentBrowse still treats this as a
44
- generic CDP-attached browser session.
43
+ The provider label is metadata only AgentBrowse treats the connection as
44
+ a generic CDP-attached browser session regardless of the label.
45
45
 
46
46
  ## Client Configuration
47
47
 
@@ -71,10 +71,19 @@ after a process restart.
71
71
  ### Default Store
72
72
 
73
73
  ```ts
74
- import { loadBrowserSession, saveBrowserSession } from '@mercuryo-ai/agentbrowse';
74
+ import { loadBrowserSession, saveBrowserSession, status } from '@mercuryo-ai/agentbrowse';
75
75
 
76
76
  saveBrowserSession(session);
77
77
  const restored = loadBrowserSession();
78
+
79
+ // Always check a restored session before using it — the browser it points
80
+ // at may already be gone.
81
+ if (restored) {
82
+ const check = await status(restored);
83
+ if (!check.success) {
84
+ // The session is no longer reachable. Discard and relaunch.
85
+ }
86
+ }
78
87
  ```
79
88
 
80
89
  Default path:
@@ -276,12 +285,11 @@ if your diagnostics implementation throws.
276
285
 
277
286
  ## Process-Global Convenience Helpers
278
287
 
279
- AgentBrowse still exposes process-global convenience functions such as:
288
+ For small scripts and quick experiments, AgentBrowse exposes process-global
289
+ helpers:
280
290
 
281
291
  - `configureAgentbrowseAssistiveRuntime(...)`
282
292
  - `configureAgentbrowseDiagnostics(...)`
283
293
 
284
- These are useful for small scripts and quick experiments.
285
-
286
- For embedded production usage, client-scoped configuration is the better
287
- default.
294
+ For embedded production usage, prefer client-scoped configuration through
295
+ `createAgentbrowseClient(...)`.