@mercuryo-ai/agentbrowse 0.2.57 → 0.2.61

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 (100) hide show
  1. package/README.md +76 -57
  2. package/dist/browser-session-state.d.ts +39 -0
  3. package/dist/browser-session-state.d.ts.map +1 -1
  4. package/dist/browser-session-state.js +63 -1
  5. package/dist/command-name.js +1 -1
  6. package/dist/commands/act.d.ts.map +1 -1
  7. package/dist/commands/act.js +540 -528
  8. package/dist/commands/action-executor-helpers.d.ts.map +1 -1
  9. package/dist/commands/action-executor-helpers.js +10 -8
  10. package/dist/commands/attach.d.ts.map +1 -1
  11. package/dist/commands/attach.js +5 -10
  12. package/dist/commands/browser-connection-failure.d.ts +9 -0
  13. package/dist/commands/browser-connection-failure.d.ts.map +1 -0
  14. package/dist/commands/browser-connection-failure.js +15 -0
  15. package/dist/commands/browser-status.d.ts.map +1 -1
  16. package/dist/commands/browser-status.js +26 -30
  17. package/dist/commands/click-activation-policy.d.ts.map +1 -1
  18. package/dist/commands/click-activation-policy.js +6 -2
  19. package/dist/commands/close.d.ts.map +1 -1
  20. package/dist/commands/close.js +5 -0
  21. package/dist/commands/extract.d.ts.map +1 -1
  22. package/dist/commands/extract.js +147 -144
  23. package/dist/commands/launch.d.ts +0 -1
  24. package/dist/commands/launch.d.ts.map +1 -1
  25. package/dist/commands/launch.js +13 -16
  26. package/dist/commands/navigate.d.ts.map +1 -1
  27. package/dist/commands/navigate.js +79 -73
  28. package/dist/commands/observe-inventory.d.ts +6 -1
  29. package/dist/commands/observe-inventory.d.ts.map +1 -1
  30. package/dist/commands/observe-inventory.js +331 -8
  31. package/dist/commands/observe-persistence.d.ts.map +1 -1
  32. package/dist/commands/observe-persistence.js +2 -0
  33. package/dist/commands/observe-projection.d.ts +3 -2
  34. package/dist/commands/observe-projection.d.ts.map +1 -1
  35. package/dist/commands/observe-projection.js +1 -0
  36. package/dist/commands/observe-protected.d.ts +3 -1
  37. package/dist/commands/observe-protected.d.ts.map +1 -1
  38. package/dist/commands/observe-protected.js +23 -1
  39. package/dist/commands/observe-semantics.d.ts.map +1 -1
  40. package/dist/commands/observe-semantics.js +70 -0
  41. package/dist/commands/observe.d.ts +1 -0
  42. package/dist/commands/observe.d.ts.map +1 -1
  43. package/dist/commands/observe.js +260 -270
  44. package/dist/commands/screenshot.d.ts.map +1 -1
  45. package/dist/commands/screenshot.js +50 -64
  46. package/dist/control-semantics.d.ts.map +1 -1
  47. package/dist/control-semantics.js +5 -0
  48. package/dist/date-value-normalization.d.ts +16 -0
  49. package/dist/date-value-normalization.d.ts.map +1 -0
  50. package/dist/date-value-normalization.js +117 -0
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/index.js +5 -24
  53. package/dist/library.d.ts +5 -1
  54. package/dist/library.d.ts.map +1 -1
  55. package/dist/library.js +4 -1
  56. package/dist/protected-fill.d.ts +3 -2
  57. package/dist/protected-fill.d.ts.map +1 -1
  58. package/dist/protected-fill.js +46 -7
  59. package/dist/runtime-protected-state.d.ts.map +1 -1
  60. package/dist/runtime-protected-state.js +8 -1
  61. package/dist/runtime-state.d.ts +11 -0
  62. package/dist/runtime-state.d.ts.map +1 -1
  63. package/dist/secrets/form-matcher.d.ts +1 -2
  64. package/dist/secrets/form-matcher.d.ts.map +1 -1
  65. package/dist/secrets/form-matcher.js +125 -119
  66. package/dist/secrets/matching-helpers.d.ts +13 -0
  67. package/dist/secrets/matching-helpers.d.ts.map +1 -0
  68. package/dist/secrets/matching-helpers.js +147 -0
  69. package/dist/secrets/observed-field-resolution.d.ts +43 -0
  70. package/dist/secrets/observed-field-resolution.d.ts.map +1 -0
  71. package/dist/secrets/observed-field-resolution.js +223 -0
  72. package/dist/secrets/protected-field-semantics.d.ts.map +1 -1
  73. package/dist/secrets/protected-field-semantics.js +3 -2
  74. package/dist/secrets/protected-fill.d.ts +3 -1
  75. package/dist/secrets/protected-fill.d.ts.map +1 -1
  76. package/dist/secrets/protected-fill.js +31 -0
  77. package/dist/secrets/protected-value-adapters.d.ts.map +1 -1
  78. package/dist/secrets/protected-value-adapters.js +14 -22
  79. package/dist/secrets/types.d.ts +3 -0
  80. package/dist/secrets/types.d.ts.map +1 -1
  81. package/dist/sticky-owner-host-entry.d.ts +2 -0
  82. package/dist/sticky-owner-host-entry.d.ts.map +1 -0
  83. package/dist/sticky-owner-host-entry.js +97 -0
  84. package/dist/sticky-owner.d.ts +15 -0
  85. package/dist/sticky-owner.d.ts.map +1 -0
  86. package/dist/sticky-owner.js +431 -0
  87. package/docs/README.md +15 -2
  88. package/docs/api-reference.md +13 -3
  89. package/docs/assistive-runtime.md +63 -7
  90. package/docs/configuration.md +48 -8
  91. package/docs/getting-started.md +42 -9
  92. package/docs/integration-checklist.md +8 -7
  93. package/docs/protected-fill.md +40 -7
  94. package/docs/testing.md +4 -3
  95. package/docs/troubleshooting.md +126 -36
  96. package/examples/README.md +9 -2
  97. package/package.json +8 -3
  98. package/dist/protected-fill-browser.d.ts +0 -22
  99. package/dist/protected-fill-browser.d.ts.map +0 -1
  100. package/dist/protected-fill-browser.js +0 -52
@@ -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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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=sticky-owner-host-entry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sticky-owner-host-entry.d.ts","sourceRoot":"","sources":["../src/sticky-owner-host-entry.ts"],"names":[],"mappings":""}
@@ -0,0 +1,97 @@
1
+ import { existsSync, rmSync, statSync, writeFileSync } from 'node:fs';
2
+ import { connectPlaywright, disconnectPlaywright } from './playwright-runtime.js';
3
+ function readArgValue(argv, name) {
4
+ const index = argv.indexOf(name);
5
+ const value = index >= 0 ? argv[index + 1] : undefined;
6
+ if (!value || value.startsWith('--')) {
7
+ throw new Error(`Missing ${name} argument.`);
8
+ }
9
+ return value;
10
+ }
11
+ function parseArgs(argv = process.argv) {
12
+ const ttlRaw = readArgValue(argv, '--ttl-ms');
13
+ const ttlMs = Number(ttlRaw);
14
+ if (!Number.isFinite(ttlMs) || ttlMs <= 0) {
15
+ throw new Error('Invalid --ttl-ms argument.');
16
+ }
17
+ return {
18
+ manifestPath: readArgValue(argv, '--manifest'),
19
+ cdpUrl: readArgValue(argv, '--cdp-url'),
20
+ hostId: readArgValue(argv, '--host-id'),
21
+ browserSessionId: readArgValue(argv, '--browser-session-id'),
22
+ touchPath: readArgValue(argv, '--touch-path'),
23
+ ttlMs,
24
+ };
25
+ }
26
+ function touchLeaseFile(path) {
27
+ writeFileSync(path, new Date().toISOString());
28
+ }
29
+ function readLeaseAgeMs(path) {
30
+ if (!existsSync(path)) {
31
+ return Number.POSITIVE_INFINITY;
32
+ }
33
+ try {
34
+ return Math.max(0, Date.now() - statSync(path).mtimeMs);
35
+ }
36
+ catch {
37
+ return Number.POSITIVE_INFINITY;
38
+ }
39
+ }
40
+ async function main() {
41
+ const args = parseArgs();
42
+ const browser = await connectPlaywright(args.cdpUrl);
43
+ const binding = await browser.bind(args.hostId, {
44
+ metadata: {
45
+ hostId: args.hostId,
46
+ browserSessionId: args.browserSessionId,
47
+ pid: process.pid,
48
+ startedAt: new Date().toISOString(),
49
+ },
50
+ });
51
+ touchLeaseFile(args.touchPath);
52
+ writeFileSync(args.manifestPath, JSON.stringify({
53
+ endpoint: binding.endpoint,
54
+ pid: process.pid,
55
+ startedAt: new Date().toISOString(),
56
+ }, null, 2));
57
+ let shuttingDown = false;
58
+ let ttlInterval = null;
59
+ const shutdown = async () => {
60
+ if (shuttingDown) {
61
+ return;
62
+ }
63
+ shuttingDown = true;
64
+ if (ttlInterval) {
65
+ clearInterval(ttlInterval);
66
+ ttlInterval = null;
67
+ }
68
+ await browser.unbind().catch(() => undefined);
69
+ await disconnectPlaywright(browser).catch(() => undefined);
70
+ rmSync(args.touchPath, { force: true });
71
+ process.exit(0);
72
+ };
73
+ const reapExpiredOwner = () => {
74
+ if (readLeaseAgeMs(args.touchPath) >= args.ttlMs) {
75
+ void shutdown();
76
+ }
77
+ };
78
+ browser.on('disconnected', () => {
79
+ void shutdown();
80
+ });
81
+ process.on('SIGINT', () => {
82
+ void shutdown();
83
+ });
84
+ process.on('SIGTERM', () => {
85
+ void shutdown();
86
+ });
87
+ const ttlCheckMs = Math.max(5_000, Math.min(Math.floor(args.ttlMs / 6), 60_000));
88
+ ttlInterval = setInterval(reapExpiredOwner, ttlCheckMs);
89
+ ttlInterval.unref?.();
90
+ reapExpiredOwner();
91
+ await new Promise(() => { });
92
+ }
93
+ void main().catch((error) => {
94
+ const message = error instanceof Error ? error.message : String(error);
95
+ console.error(message);
96
+ process.exit(1);
97
+ });
@@ -0,0 +1,15 @@
1
+ import { type Browser } from 'playwright-core';
2
+ import { type BrowserSessionState } from './browser-session-state.js';
3
+ export type RestoreBrowserSessionOwnerResult = {
4
+ success: true;
5
+ session: BrowserSessionState;
6
+ restored: boolean;
7
+ } | {
8
+ success: false;
9
+ reason: 'sticky_owner_unrecoverable';
10
+ };
11
+ export declare function initializeBrowserSessionOwner(session: BrowserSessionState): Promise<BrowserSessionState>;
12
+ export declare function terminateBrowserSessionOwner(session: BrowserSessionState | null): Promise<void>;
13
+ export declare function restoreBrowserSessionOwner(session: BrowserSessionState): Promise<RestoreBrowserSessionOwnerResult>;
14
+ export declare function withStickyOwnerBrowser<TResult>(session: BrowserSessionState, operation: (browser: Browser) => Promise<TResult>): Promise<TResult>;
15
+ //# sourceMappingURL=sticky-owner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sticky-owner.d.ts","sourceRoot":"","sources":["../src/sticky-owner.ts"],"names":[],"mappings":"AAMA,OAAO,EAAY,KAAK,OAAO,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAIL,KAAK,mBAAmB,EAGzB,MAAM,4BAA4B,CAAC;AAWpC,MAAM,MAAM,gCAAgC,GACxC;IACE,OAAO,EAAE,IAAI,CAAC;IACd,OAAO,EAAE,mBAAmB,CAAC;IAC7B,QAAQ,EAAE,OAAO,CAAC;CACnB,GACD;IACE,OAAO,EAAE,KAAK,CAAC;IACf,MAAM,EAAE,4BAA4B,CAAC;CACtC,CAAC;AAyVN,wBAAsB,6BAA6B,CACjD,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,mBAAmB,CAAC,CAU9B;AAED,wBAAsB,4BAA4B,CAChD,OAAO,EAAE,mBAAmB,GAAG,IAAI,GAClC,OAAO,CAAC,IAAI,CAAC,CA2Bf;AAED,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,gCAAgC,CAAC,CAiG3C;AAED,wBAAsB,sBAAsB,CAAC,OAAO,EAClD,OAAO,EAAE,mBAAmB,EAC5B,SAAS,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,GAChD,OAAO,CAAC,OAAO,CAAC,CA0BlB"}