@nuanu-ai/agentbrowse 0.2.7 → 0.2.9

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 (194) hide show
  1. package/README.md +36 -8
  2. package/dist/agentpay-stagehand-llm.d.ts.map +1 -1
  3. package/dist/agentpay-stagehand-llm.js +5 -1
  4. package/dist/commands/act.d.ts +6 -2
  5. package/dist/commands/act.d.ts.map +1 -1
  6. package/dist/commands/act.js +840 -55
  7. package/dist/commands/act.test-harness.d.ts +19 -0
  8. package/dist/commands/act.test-harness.d.ts.map +1 -0
  9. package/dist/commands/act.test-harness.js +245 -0
  10. package/dist/commands/action-acceptance.d.ts +90 -0
  11. package/dist/commands/action-acceptance.d.ts.map +1 -0
  12. package/dist/commands/action-acceptance.js +1411 -0
  13. package/dist/commands/action-artifacts.d.ts +33 -0
  14. package/dist/commands/action-artifacts.d.ts.map +1 -0
  15. package/dist/commands/action-artifacts.js +104 -0
  16. package/dist/commands/action-execution-guards.d.ts +5 -0
  17. package/dist/commands/action-execution-guards.d.ts.map +1 -0
  18. package/dist/commands/action-execution-guards.js +3 -0
  19. package/dist/commands/action-executor-helpers.d.ts +21 -0
  20. package/dist/commands/action-executor-helpers.d.ts.map +1 -0
  21. package/dist/commands/action-executor-helpers.js +242 -0
  22. package/dist/commands/action-executor.d.ts +12 -0
  23. package/dist/commands/action-executor.d.ts.map +1 -0
  24. package/dist/commands/action-executor.js +45 -0
  25. package/dist/commands/action-fallbacks.d.ts +6 -0
  26. package/dist/commands/action-fallbacks.d.ts.map +1 -0
  27. package/dist/commands/action-fallbacks.js +43 -0
  28. package/dist/commands/action-value-projection.d.ts +32 -0
  29. package/dist/commands/action-value-projection.d.ts.map +1 -0
  30. package/dist/commands/action-value-projection.js +151 -0
  31. package/dist/commands/browse-actions.d.ts +4 -0
  32. package/dist/commands/browse-actions.d.ts.map +1 -0
  33. package/dist/commands/browse-actions.js +4 -0
  34. package/dist/commands/captcha-solve.d.ts.map +1 -1
  35. package/dist/commands/captcha-solve.js +13 -3
  36. package/dist/commands/click-action-executor.d.ts +10 -0
  37. package/dist/commands/click-action-executor.d.ts.map +1 -0
  38. package/dist/commands/click-action-executor.js +68 -0
  39. package/dist/commands/create-intent.d.ts +6 -0
  40. package/dist/commands/create-intent.d.ts.map +1 -0
  41. package/dist/commands/create-intent.js +75 -0
  42. package/dist/commands/datepicker-action-executor.d.ts +12 -0
  43. package/dist/commands/datepicker-action-executor.d.ts.map +1 -0
  44. package/dist/commands/datepicker-action-executor.js +218 -0
  45. package/dist/commands/descriptor-validation.d.ts +27 -0
  46. package/dist/commands/descriptor-validation.d.ts.map +1 -0
  47. package/dist/commands/descriptor-validation.js +333 -0
  48. package/dist/commands/extract-scope-resolution.d.ts +20 -0
  49. package/dist/commands/extract-scope-resolution.d.ts.map +1 -0
  50. package/dist/commands/extract-scope-resolution.js +100 -0
  51. package/dist/commands/extract-stagehand-executor.d.ts +17 -0
  52. package/dist/commands/extract-stagehand-executor.d.ts.map +1 -0
  53. package/dist/commands/extract-stagehand-executor.js +18 -0
  54. package/dist/commands/extract.d.ts +3 -2
  55. package/dist/commands/extract.d.ts.map +1 -1
  56. package/dist/commands/extract.js +256 -39
  57. package/dist/commands/fill-secret.d.ts +7 -0
  58. package/dist/commands/fill-secret.d.ts.map +1 -0
  59. package/dist/commands/fill-secret.js +371 -0
  60. package/dist/commands/get-secrets-catalog.d.ts +6 -0
  61. package/dist/commands/get-secrets-catalog.d.ts.map +1 -0
  62. package/dist/commands/get-secrets-catalog.js +23 -0
  63. package/dist/commands/launch.d.ts.map +1 -1
  64. package/dist/commands/launch.js +41 -7
  65. package/dist/commands/navigate.d.ts +2 -1
  66. package/dist/commands/navigate.d.ts.map +1 -1
  67. package/dist/commands/navigate.js +49 -12
  68. package/dist/commands/observe-inventory.d.ts +109 -0
  69. package/dist/commands/observe-inventory.d.ts.map +1 -0
  70. package/dist/commands/observe-inventory.js +2837 -0
  71. package/dist/commands/observe-persistence.d.ts +14 -0
  72. package/dist/commands/observe-persistence.d.ts.map +1 -0
  73. package/dist/commands/observe-persistence.js +170 -0
  74. package/dist/commands/observe-projection.d.ts +85 -0
  75. package/dist/commands/observe-projection.d.ts.map +1 -0
  76. package/dist/commands/observe-projection.js +141 -0
  77. package/dist/commands/observe-protected.d.ts +5 -0
  78. package/dist/commands/observe-protected.d.ts.map +1 -0
  79. package/dist/commands/observe-protected.js +18 -0
  80. package/dist/commands/observe-semantics.d.ts +10 -0
  81. package/dist/commands/observe-semantics.d.ts.map +1 -0
  82. package/dist/commands/observe-semantics.js +338 -0
  83. package/dist/commands/observe-stagehand.d.ts +48 -0
  84. package/dist/commands/observe-stagehand.d.ts.map +1 -0
  85. package/dist/commands/observe-stagehand.js +105 -0
  86. package/dist/commands/observe-surfaces.d.ts +9 -0
  87. package/dist/commands/observe-surfaces.d.ts.map +1 -0
  88. package/dist/commands/observe-surfaces.js +195 -0
  89. package/dist/commands/observe.d.ts +47 -1
  90. package/dist/commands/observe.d.ts.map +1 -1
  91. package/dist/commands/observe.js +173 -20
  92. package/dist/commands/observe.test-harness.d.ts +67 -0
  93. package/dist/commands/observe.test-harness.d.ts.map +1 -0
  94. package/dist/commands/observe.test-harness.js +107 -0
  95. package/dist/commands/poll-intent.d.ts +6 -0
  96. package/dist/commands/poll-intent.d.ts.map +1 -0
  97. package/dist/commands/poll-intent.js +57 -0
  98. package/dist/commands/screenshot.d.ts +2 -1
  99. package/dist/commands/screenshot.d.ts.map +1 -1
  100. package/dist/commands/screenshot.js +44 -12
  101. package/dist/commands/select-action-executor.d.ts +10 -0
  102. package/dist/commands/select-action-executor.d.ts.map +1 -0
  103. package/dist/commands/select-action-executor.js +91 -0
  104. package/dist/commands/semantic-observe.d.ts +24 -0
  105. package/dist/commands/semantic-observe.d.ts.map +1 -0
  106. package/dist/commands/semantic-observe.js +344 -0
  107. package/dist/commands/status.d.ts.map +1 -1
  108. package/dist/commands/status.js +75 -2
  109. package/dist/commands/structured-grid-action-executor.d.ts +3 -0
  110. package/dist/commands/structured-grid-action-executor.d.ts.map +1 -0
  111. package/dist/commands/structured-grid-action-executor.js +4 -0
  112. package/dist/commands/target-resolution.d.ts +4 -0
  113. package/dist/commands/target-resolution.d.ts.map +1 -0
  114. package/dist/commands/target-resolution.js +33 -0
  115. package/dist/commands/text-input-action-executor.d.ts +5 -0
  116. package/dist/commands/text-input-action-executor.d.ts.map +1 -0
  117. package/dist/commands/text-input-action-executor.js +116 -0
  118. package/dist/commands/user-actionable.d.ts +4 -0
  119. package/dist/commands/user-actionable.d.ts.map +1 -0
  120. package/dist/commands/user-actionable.js +95 -0
  121. package/dist/control-semantics.d.ts +29 -0
  122. package/dist/control-semantics.d.ts.map +1 -0
  123. package/dist/control-semantics.js +299 -0
  124. package/dist/index.d.ts.map +1 -1
  125. package/dist/index.js +95 -32
  126. package/dist/output.d.ts +14 -2
  127. package/dist/output.d.ts.map +1 -1
  128. package/dist/output.js +17 -29
  129. package/dist/playwright-runtime.d.ts +35 -0
  130. package/dist/playwright-runtime.d.ts.map +1 -0
  131. package/dist/playwright-runtime.js +224 -0
  132. package/dist/runtime-resolution.d.ts +9 -0
  133. package/dist/runtime-resolution.d.ts.map +1 -0
  134. package/dist/runtime-resolution.js +19 -0
  135. package/dist/runtime-state.d.ts +217 -0
  136. package/dist/runtime-state.d.ts.map +1 -0
  137. package/dist/runtime-state.js +629 -0
  138. package/dist/secrets/backend.d.ts +32 -0
  139. package/dist/secrets/backend.d.ts.map +1 -0
  140. package/dist/secrets/backend.js +169 -0
  141. package/dist/secrets/catalog-applicability.d.ts +5 -0
  142. package/dist/secrets/catalog-applicability.d.ts.map +1 -0
  143. package/dist/secrets/catalog-applicability.js +59 -0
  144. package/dist/secrets/catalog-sync.d.ts +14 -0
  145. package/dist/secrets/catalog-sync.d.ts.map +1 -0
  146. package/dist/secrets/catalog-sync.js +35 -0
  147. package/dist/secrets/field-policy.d.ts +3 -0
  148. package/dist/secrets/field-policy.d.ts.map +1 -0
  149. package/dist/secrets/field-policy.js +3 -0
  150. package/dist/secrets/fill-ordering.d.ts +11 -0
  151. package/dist/secrets/fill-ordering.d.ts.map +1 -0
  152. package/dist/secrets/fill-ordering.js +44 -0
  153. package/dist/secrets/form-matcher.d.ts +60 -0
  154. package/dist/secrets/form-matcher.d.ts.map +1 -0
  155. package/dist/secrets/form-matcher.js +608 -0
  156. package/dist/secrets/intent-output.d.ts +11 -0
  157. package/dist/secrets/intent-output.d.ts.map +1 -0
  158. package/dist/secrets/intent-output.js +64 -0
  159. package/dist/secrets/mock-agentpay-backend.d.ts +13 -0
  160. package/dist/secrets/mock-agentpay-backend.d.ts.map +1 -0
  161. package/dist/secrets/mock-agentpay-backend.js +87 -0
  162. package/dist/secrets/mock-agentpay-cabinet.d.ts +43 -0
  163. package/dist/secrets/mock-agentpay-cabinet.d.ts.map +1 -0
  164. package/dist/secrets/mock-agentpay-cabinet.js +195 -0
  165. package/dist/secrets/protected-artifact-guard.d.ts +25 -0
  166. package/dist/secrets/protected-artifact-guard.d.ts.map +1 -0
  167. package/dist/secrets/protected-artifact-guard.js +26 -0
  168. package/dist/secrets/protected-bindings.d.ts +10 -0
  169. package/dist/secrets/protected-bindings.d.ts.map +1 -0
  170. package/dist/secrets/protected-bindings.js +17 -0
  171. package/dist/secrets/protected-field-values.d.ts +13 -0
  172. package/dist/secrets/protected-field-values.d.ts.map +1 -0
  173. package/dist/secrets/protected-field-values.js +100 -0
  174. package/dist/secrets/protected-fill.d.ts +47 -0
  175. package/dist/secrets/protected-fill.d.ts.map +1 -0
  176. package/dist/secrets/protected-fill.js +512 -0
  177. package/dist/secrets/request-guidance.d.ts +24 -0
  178. package/dist/secrets/request-guidance.d.ts.map +1 -0
  179. package/dist/secrets/request-guidance.js +142 -0
  180. package/dist/secrets/types.d.ts +91 -0
  181. package/dist/secrets/types.d.ts.map +1 -0
  182. package/dist/secrets/types.js +27 -0
  183. package/dist/session.d.ts +22 -0
  184. package/dist/session.d.ts.map +1 -1
  185. package/dist/session.js +74 -2
  186. package/dist/solver/browser-launcher.d.ts.map +1 -1
  187. package/dist/solver/browser-launcher.js +6 -3
  188. package/dist/stagehand-runtime.d.ts +4 -0
  189. package/dist/stagehand-runtime.d.ts.map +1 -0
  190. package/dist/stagehand-runtime.js +10 -0
  191. package/dist/stagehand.d.ts +0 -5
  192. package/dist/stagehand.d.ts.map +1 -1
  193. package/dist/stagehand.js +0 -6
  194. package/package.json +5 -2
@@ -0,0 +1,512 @@
1
+ import { getSurface, getTarget } from '../runtime-state.js';
2
+ import { createAcceptanceProbe, rankLocatorCandidates, waitForAcceptanceProbe, } from '../commands/action-acceptance.js';
3
+ import { buildLocator, resolveLocatorRoot } from '../commands/action-fallbacks.js';
4
+ import { applyActionWithFallbacks } from '../commands/action-executor.js';
5
+ import { normalizePageSignature, readLocatorDomSignature } from '../commands/descriptor-validation.js';
6
+ import { resolveSurfaceScopeRoot } from '../commands/target-resolution.js';
7
+ import { resolveProtectedFieldPolicy } from './field-policy.js';
8
+ import { protectedBindingKey } from './protected-bindings.js';
9
+ import { resolveAssistedProtectedFieldValues } from './protected-field-values.js';
10
+ import { sortProtectedBindingsForExecution } from './fill-ordering.js';
11
+ function targetUsesSurfaceAsPrimaryLocator(target, surface) {
12
+ const surfaceCandidateKeys = new Set(surface.locatorCandidates.map((candidate) => [candidate.strategy, candidate.name ?? '', candidate.value, candidate.attribute ?? '', candidate.scope ?? ''].join(':')));
13
+ return target.locatorCandidates.some((candidate) => surfaceCandidateKeys.has([candidate.strategy, candidate.name ?? '', candidate.value, candidate.attribute ?? '', candidate.scope ?? ''].join(':')));
14
+ }
15
+ function resolveLocatorRootForCandidate(baseRoot, defaultRoot, surfaceRoot, scope) {
16
+ if (scope === 'root') {
17
+ return baseRoot;
18
+ }
19
+ if (scope === 'surface') {
20
+ return surfaceRoot;
21
+ }
22
+ return defaultRoot;
23
+ }
24
+ async function prepareLocator(locator, action, strategy, attempts, options) {
25
+ const count = await locator.count().catch(() => 0);
26
+ if (count === 0) {
27
+ attempts.push(`resolve.skip:${strategy}:empty`);
28
+ return { locator: null };
29
+ }
30
+ if (count > 1) {
31
+ attempts.push(`resolve.skip:${strategy}:ambiguous:${count}`);
32
+ return { locator: null };
33
+ }
34
+ const resolvedLocator = locator.first();
35
+ const visible = await resolvedLocator.isVisible().catch(() => false);
36
+ if (!visible) {
37
+ attempts.push(`resolve.skip:${strategy}:hidden`);
38
+ return { locator: null };
39
+ }
40
+ const disabled = await resolvedLocator.isDisabled?.().catch(() => false);
41
+ if (disabled) {
42
+ attempts.push(`resolve.skip:${strategy}:disabled`);
43
+ return { locator: null, blockedReason: 'disabled' };
44
+ }
45
+ if (action === 'fill' || action === 'type') {
46
+ const editable = await resolvedLocator.isEditable().catch(() => false);
47
+ if (!editable) {
48
+ const descendants = resolvedLocator.locator('input:not([type="hidden"]), textarea, select, [contenteditable="true"]');
49
+ const descendantCount = await descendants.count().catch(() => 0);
50
+ const visibleEditableDescendants = [];
51
+ for (let index = 0; index < descendantCount; index += 1) {
52
+ const descendant = descendants.nth(index);
53
+ const descendantVisible = await descendant.isVisible().catch(() => false);
54
+ if (!descendantVisible) {
55
+ continue;
56
+ }
57
+ const descendantEditable = await descendant.isEditable().catch(() => false);
58
+ if (!descendantEditable) {
59
+ continue;
60
+ }
61
+ visibleEditableDescendants.push(descendant);
62
+ }
63
+ if (visibleEditableDescendants.length === 1) {
64
+ attempts.push(`resolve.descendant-editable:${strategy}`);
65
+ return { locator: visibleEditableDescendants[0] ?? null };
66
+ }
67
+ if (visibleEditableDescendants.length > 1) {
68
+ attempts.push(`resolve.skip:${strategy}:descendant-ambiguous:${visibleEditableDescendants.length}`);
69
+ return { locator: null };
70
+ }
71
+ const readonly = await resolvedLocator
72
+ .getAttribute?.('readonly')
73
+ .then((value) => value !== null)
74
+ .catch(() => false);
75
+ if (readonly && !options?.allowReadonlyFallback) {
76
+ attempts.push(`resolve.skip:${strategy}:readonly`);
77
+ return { locator: null, blockedReason: 'readonly' };
78
+ }
79
+ }
80
+ }
81
+ return { locator: resolvedLocator };
82
+ }
83
+ function actionForTarget(target) {
84
+ if (target.controlFamily === 'select') {
85
+ return 'select';
86
+ }
87
+ if (target.allowedActions.includes('fill')) {
88
+ return 'fill';
89
+ }
90
+ if (target.allowedActions.includes('type')) {
91
+ return 'type';
92
+ }
93
+ if (target.allowedActions.includes('select')) {
94
+ return 'select';
95
+ }
96
+ return 'fill';
97
+ }
98
+ function formatCardExpiry(month, year) {
99
+ const normalizedMonth = month.trim().padStart(2, '0');
100
+ const normalizedYear = year.trim().length > 2 ? year.trim().slice(-2) : year.trim().padStart(2, '0');
101
+ return `${normalizedMonth}/${normalizedYear}`;
102
+ }
103
+ function resolveBindingValue(fields, protectedValues, fieldPolicies, assistedValues) {
104
+ if (fields.length === 1) {
105
+ const field = fields[0];
106
+ const policy = resolveProtectedFieldPolicy(fieldPolicies, field.fieldKey);
107
+ if (policy === 'llm_assisted') {
108
+ const assistedValue = assistedValues.get(protectedBindingKey(field));
109
+ if (typeof assistedValue === 'string' && assistedValue.length > 0) {
110
+ return assistedValue;
111
+ }
112
+ throw new Error('assisted_value_resolution_failed');
113
+ }
114
+ if ((field.valueHint ?? 'direct') !== 'direct') {
115
+ throw new Error('deterministic_only_resolution_failed');
116
+ }
117
+ const value = protectedValues[field.fieldKey];
118
+ if (typeof value === 'string' && value.length > 0) {
119
+ return value;
120
+ }
121
+ throw new Error('missing_protected_value');
122
+ }
123
+ const includesExpiry = fields.some((field) => field.fieldKey === 'exp_month') &&
124
+ fields.some((field) => field.fieldKey === 'exp_year') &&
125
+ fields.length === 2;
126
+ if (includesExpiry) {
127
+ const month = protectedValues.exp_month;
128
+ const year = protectedValues.exp_year;
129
+ if (typeof month === 'string' &&
130
+ month.length > 0 &&
131
+ typeof year === 'string' &&
132
+ year.length > 0) {
133
+ return formatCardExpiry(month, year);
134
+ }
135
+ throw new Error('missing_protected_value');
136
+ }
137
+ throw new Error('unsupported_protected_field_group');
138
+ }
139
+ function flattenFilledFields(fieldKeys, targetRef) {
140
+ return fieldKeys.map((fieldKey) => ({
141
+ fieldKey,
142
+ targetRef,
143
+ }));
144
+ }
145
+ function buildProtectedValidationDetails(validationText) {
146
+ return validationText ? { validationTextRedacted: true } : {};
147
+ }
148
+ function shouldIgnoreNativeInvalidForTarget(target) {
149
+ return Array.isArray(target.framePath) && target.framePath.length > 0;
150
+ }
151
+ async function readValidationState(locator) {
152
+ const payload = await locator
153
+ .evaluate((element) => {
154
+ if (!(element instanceof HTMLElement)) {
155
+ return null;
156
+ }
157
+ const maybeControl = element instanceof HTMLInputElement ||
158
+ element instanceof HTMLTextAreaElement ||
159
+ element instanceof HTMLSelectElement
160
+ ? element
161
+ : null;
162
+ const validationMessage = maybeControl?.validationMessage?.trim() ?? '';
163
+ const describedByIds = (element.getAttribute('aria-describedby') ?? '')
164
+ .split(/\s+/)
165
+ .map((value) => value.trim())
166
+ .filter(Boolean);
167
+ const describedByText = describedByIds
168
+ .map((id) => element.ownerDocument?.getElementById(id)?.textContent?.trim() ?? '')
169
+ .filter(Boolean)
170
+ .join(' ');
171
+ const ariaInvalid = element.getAttribute('aria-invalid') === 'true';
172
+ const candidate = validationMessage || describedByText || '';
173
+ return {
174
+ validationText: candidate || undefined,
175
+ ariaInvalid,
176
+ invalid: ariaInvalid ||
177
+ Boolean(maybeControl &&
178
+ typeof maybeControl.checkValidity === 'function' &&
179
+ maybeControl.checkValidity() === false),
180
+ };
181
+ })
182
+ .catch(() => null);
183
+ if (!payload) {
184
+ return {
185
+ invalid: false,
186
+ };
187
+ }
188
+ return {
189
+ invalid: payload.invalid,
190
+ validationText: payload.validationText ||
191
+ (payload.ariaInvalid ? 'Field rejected by client-side validation.' : undefined),
192
+ };
193
+ }
194
+ function groupBindingsByTarget(fillableForm, session) {
195
+ const grouped = new Map();
196
+ for (const field of fillableForm.fields) {
197
+ const target = getTarget(session, field.targetRef);
198
+ if (!target) {
199
+ return null;
200
+ }
201
+ const existing = grouped.get(field.targetRef);
202
+ if (existing) {
203
+ existing.fields.push(field);
204
+ existing.fieldKeys.push(field.fieldKey);
205
+ continue;
206
+ }
207
+ grouped.set(field.targetRef, {
208
+ targetRef: field.targetRef,
209
+ fields: [field],
210
+ fieldKeys: [field.fieldKey],
211
+ target,
212
+ });
213
+ }
214
+ return sortProtectedBindingsForExecution(fillableForm.purpose, [...grouped.values()]);
215
+ }
216
+ async function prepareBindings(session, page, fillableForm) {
217
+ const groupedBindings = groupBindingsByTarget(fillableForm, session);
218
+ if (!groupedBindings) {
219
+ const firstField = fillableForm.fields[0];
220
+ return {
221
+ kind: 'binding_stale',
222
+ targetRef: firstField?.targetRef ?? 'unknown',
223
+ fieldKeys: firstField ? [firstField.fieldKey] : [],
224
+ reason: 'target_missing',
225
+ attempts: [],
226
+ };
227
+ }
228
+ const preparedBindings = [];
229
+ for (const binding of groupedBindings) {
230
+ const attempts = [];
231
+ const { target } = binding;
232
+ if (target.lifecycle !== 'live') {
233
+ return {
234
+ kind: 'binding_stale',
235
+ targetRef: binding.targetRef,
236
+ fieldKeys: [...binding.fieldKeys],
237
+ reason: 'target_not_live',
238
+ attempts,
239
+ };
240
+ }
241
+ if (target.pageSignature && normalizePageSignature(page.url()) !== target.pageSignature) {
242
+ attempts.push('stale.page-signature:before-fill');
243
+ return {
244
+ kind: 'binding_stale',
245
+ targetRef: binding.targetRef,
246
+ fieldKeys: [...binding.fieldKeys],
247
+ reason: 'page_signature_mismatch',
248
+ attempts,
249
+ };
250
+ }
251
+ const surface = target.surfaceRef ? getSurface(session, target.surfaceRef) : null;
252
+ const baseRoot = resolveLocatorRoot(page, target.framePath ?? surface?.framePath);
253
+ let locatorRoot = baseRoot;
254
+ let surfaceRoot = null;
255
+ if (surface) {
256
+ surfaceRoot = await resolveSurfaceScopeRoot(page, surface, attempts).catch(() => null);
257
+ if (surfaceRoot && !targetUsesSurfaceAsPrimaryLocator(target, surface)) {
258
+ locatorRoot = surfaceRoot;
259
+ }
260
+ else if (!surfaceRoot) {
261
+ attempts.push('surface.resolve.fallback:page');
262
+ }
263
+ }
264
+ let resolvedLocator = null;
265
+ let sawDomSignatureMismatch = false;
266
+ let sawBlockedTarget = false;
267
+ const action = actionForTarget(target);
268
+ for (const candidate of rankLocatorCandidates(target.locatorCandidates, action)) {
269
+ const candidateRoot = resolveLocatorRootForCandidate(baseRoot, locatorRoot, surfaceRoot, candidate.scope);
270
+ if (!candidateRoot) {
271
+ attempts.push(`resolve.skip:${candidate.strategy}:surface-unavailable`);
272
+ continue;
273
+ }
274
+ const locator = buildLocator(candidateRoot, candidate);
275
+ if (!locator) {
276
+ continue;
277
+ }
278
+ const prepared = await prepareLocator(locator, action, candidate.strategy, attempts, {
279
+ allowReadonlyFallback: action === 'fill' && target.controlFamily === 'datepicker',
280
+ });
281
+ if (prepared.blockedReason === 'disabled' || prepared.blockedReason === 'readonly') {
282
+ sawBlockedTarget = true;
283
+ }
284
+ if (!prepared.locator) {
285
+ continue;
286
+ }
287
+ if (target.domSignature) {
288
+ const liveSignature = await readLocatorDomSignature(prepared.locator).catch(() => null);
289
+ if (liveSignature && liveSignature !== target.domSignature) {
290
+ sawDomSignatureMismatch = true;
291
+ attempts.push(`domSignature.mismatch:${candidate.strategy}`);
292
+ continue;
293
+ }
294
+ }
295
+ resolvedLocator = prepared.locator;
296
+ attempts.push(`resolve:${candidate.strategy}`);
297
+ break;
298
+ }
299
+ if (!resolvedLocator) {
300
+ return {
301
+ kind: 'binding_stale',
302
+ targetRef: binding.targetRef,
303
+ fieldKeys: [...binding.fieldKeys],
304
+ reason: sawDomSignatureMismatch
305
+ ? 'dom_signature_mismatch'
306
+ : sawBlockedTarget
307
+ ? 'target_blocked'
308
+ : 'locator_resolution_failed',
309
+ attempts,
310
+ };
311
+ }
312
+ preparedBindings.push({
313
+ targetRef: binding.targetRef,
314
+ fields: [...binding.fields],
315
+ fieldKeys: [...binding.fieldKeys],
316
+ action,
317
+ target,
318
+ root: locatorRoot,
319
+ locator: resolvedLocator,
320
+ attempts,
321
+ });
322
+ }
323
+ return preparedBindings;
324
+ }
325
+ async function assertBindingStillValid(page, binding, stage) {
326
+ if (binding.target.pageSignature &&
327
+ normalizePageSignature(page.url()) !== binding.target.pageSignature) {
328
+ throw new Error(`binding_stale:page_signature_mismatch:${stage}`);
329
+ }
330
+ const liveCount = await binding.locator.count().catch(() => 0);
331
+ if (liveCount === 0) {
332
+ throw new Error(`binding_stale:locator_resolution_failed:${stage}`);
333
+ }
334
+ if (binding.target.domSignature) {
335
+ const liveSignature = await readLocatorDomSignature(binding.locator).catch(() => null);
336
+ if (liveSignature && liveSignature !== binding.target.domSignature) {
337
+ throw new Error(`binding_stale:dom_signature_mismatch:${stage}`);
338
+ }
339
+ }
340
+ }
341
+ function staleReasonFromError(error) {
342
+ const message = error instanceof Error ? error.message : String(error);
343
+ if (message.includes('page_signature_mismatch')) {
344
+ return 'page_signature_mismatch';
345
+ }
346
+ if (message.includes('dom_signature_mismatch')) {
347
+ return 'dom_signature_mismatch';
348
+ }
349
+ if (message.includes('locator_resolution_failed')) {
350
+ return 'locator_resolution_failed';
351
+ }
352
+ return null;
353
+ }
354
+ export async function executeProtectedFill(params) {
355
+ const preparedBindings = await prepareBindings(params.session, params.page, params.fillableForm);
356
+ if (!Array.isArray(preparedBindings)) {
357
+ return preparedBindings;
358
+ }
359
+ let assistedValues;
360
+ try {
361
+ assistedValues = await resolveAssistedProtectedFieldValues({
362
+ page: params.page,
363
+ bindings: preparedBindings.map((binding) => ({
364
+ binding: binding.fields[0],
365
+ target: binding.target,
366
+ })),
367
+ protectedValues: params.protectedValues,
368
+ fieldPolicies: params.fieldPolicies,
369
+ });
370
+ }
371
+ catch {
372
+ return {
373
+ kind: 'unexpected_error',
374
+ reason: 'assisted_value_resolution_failed',
375
+ };
376
+ }
377
+ const filledFields = [];
378
+ for (const binding of preparedBindings) {
379
+ let actionValue;
380
+ let acceptanceProbe = null;
381
+ let acceptanceResult = null;
382
+ try {
383
+ actionValue = resolveBindingValue(binding.fields, params.protectedValues, params.fieldPolicies, assistedValues);
384
+ }
385
+ catch (error) {
386
+ const message = error instanceof Error ? error.message : String(error);
387
+ if (message === 'missing_protected_value') {
388
+ return {
389
+ kind: 'unexpected_error',
390
+ reason: 'missing_protected_value',
391
+ };
392
+ }
393
+ if (message === 'deterministic_only_resolution_failed') {
394
+ return {
395
+ kind: 'unexpected_error',
396
+ reason: 'deterministic_only_resolution_failed',
397
+ };
398
+ }
399
+ if (message === 'assisted_value_resolution_failed') {
400
+ return {
401
+ kind: 'unexpected_error',
402
+ reason: 'assisted_value_resolution_failed',
403
+ };
404
+ }
405
+ return {
406
+ kind: 'unexpected_error',
407
+ reason: 'unsupported_protected_field_group',
408
+ };
409
+ }
410
+ try {
411
+ acceptanceProbe = await createAcceptanceProbe({
412
+ session: params.session,
413
+ page: params.page,
414
+ target: binding.target,
415
+ action: binding.action,
416
+ actionValue,
417
+ locator: binding.locator,
418
+ beforePageObservation: null,
419
+ });
420
+ await applyActionWithFallbacks(params.page, binding.root, binding.locator, binding.action, actionValue, binding.attempts, binding.target.controlFamily, {
421
+ guards: {
422
+ assertStillValid: async (stage) => {
423
+ await assertBindingStillValid(params.page, binding, stage);
424
+ },
425
+ },
426
+ });
427
+ if (acceptanceProbe) {
428
+ acceptanceResult = await waitForAcceptanceProbe(acceptanceProbe);
429
+ const acceptance = acceptanceResult;
430
+ if (!acceptance.accepted) {
431
+ const validationState = await readValidationState(acceptanceProbe.readLocator);
432
+ return {
433
+ kind: 'validation_failed',
434
+ filledFields,
435
+ fieldErrors: binding.fieldKeys.map((fieldKey) => ({
436
+ fieldKey,
437
+ targetRef: binding.targetRef,
438
+ reason: validationState.validationText
439
+ ? 'client_validation_rejected'
440
+ : 'value_not_applied',
441
+ ...buildProtectedValidationDetails(validationState.validationText),
442
+ })),
443
+ };
444
+ }
445
+ const validationState = await readValidationState(acceptanceProbe.readLocator);
446
+ if (validationState.invalid && !shouldIgnoreNativeInvalidForTarget(binding.target)) {
447
+ return {
448
+ kind: 'validation_failed',
449
+ filledFields,
450
+ fieldErrors: binding.fieldKeys.map((fieldKey) => ({
451
+ fieldKey,
452
+ targetRef: binding.targetRef,
453
+ reason: 'client_validation_rejected',
454
+ ...buildProtectedValidationDetails(validationState.validationText),
455
+ })),
456
+ };
457
+ }
458
+ }
459
+ filledFields.push(...flattenFilledFields(binding.fieldKeys, binding.targetRef));
460
+ }
461
+ catch (error) {
462
+ if (acceptanceProbe) {
463
+ acceptanceResult =
464
+ acceptanceResult ?? (await waitForAcceptanceProbe(acceptanceProbe).catch(() => null));
465
+ if (acceptanceResult?.accepted) {
466
+ const validationState = await readValidationState(acceptanceProbe.readLocator);
467
+ if (validationState.invalid && !shouldIgnoreNativeInvalidForTarget(binding.target)) {
468
+ return {
469
+ kind: 'validation_failed',
470
+ filledFields,
471
+ fieldErrors: binding.fieldKeys.map((fieldKey) => ({
472
+ fieldKey,
473
+ targetRef: binding.targetRef,
474
+ reason: 'client_validation_rejected',
475
+ ...buildProtectedValidationDetails(validationState.validationText),
476
+ })),
477
+ };
478
+ }
479
+ filledFields.push(...flattenFilledFields(binding.fieldKeys, binding.targetRef));
480
+ continue;
481
+ }
482
+ }
483
+ const staleReason = staleReasonFromError(error);
484
+ if (staleReason) {
485
+ return {
486
+ kind: 'binding_stale',
487
+ targetRef: binding.targetRef,
488
+ fieldKeys: [...binding.fieldKeys],
489
+ reason: staleReason,
490
+ attempts: [...binding.attempts],
491
+ };
492
+ }
493
+ return {
494
+ kind: 'unexpected_error',
495
+ reason: 'action_failed',
496
+ };
497
+ }
498
+ }
499
+ return {
500
+ kind: 'success',
501
+ filledFields,
502
+ };
503
+ }
504
+ export const __testProtectedFill = {
505
+ actionForTarget,
506
+ formatCardExpiry,
507
+ resolveBindingValue: (fieldKeys, protectedValues) => resolveBindingValue(fieldKeys.map((fieldKey) => ({
508
+ fieldKey,
509
+ targetRef: 't_test',
510
+ valueHint: 'direct',
511
+ })), protectedValues, undefined, new Map()),
512
+ };
@@ -0,0 +1,24 @@
1
+ import type { TargetDescriptor } from '../runtime-state.js';
2
+ import type { FillableFormFieldBinding, FillableFormGuidanceHint, FillableFormRecommendedTiming, StoredSecretFieldKey } from './types.js';
3
+ declare function collectVisibleFieldEvidence(fields: ReadonlyArray<FillableFormFieldBinding>, targetByRef: ReadonlyMap<string, TargetDescriptor>): string;
4
+ declare function collectBlockingValidationEvidence(fields: ReadonlyArray<FillableFormFieldBinding>, targetByRef: ReadonlyMap<string, TargetDescriptor>): string[];
5
+ declare function collectPassiveRequiredEvidence(fields: ReadonlyArray<FillableFormFieldBinding>, targetByRef: ReadonlyMap<string, TargetDescriptor>): string[];
6
+ declare function hasCorePaymentCardFields(fieldKeys: ReadonlySet<StoredSecretFieldKey>): boolean;
7
+ declare function looksLikePrimaryLoginForm(fieldKeys: ReadonlySet<StoredSecretFieldKey>): boolean;
8
+ declare function looksLikeStrongIdentityForm(fieldKeys: ReadonlySet<StoredSecretFieldKey>): boolean;
9
+ export declare function buildFillableFormGuidance(params: {
10
+ purpose: string;
11
+ recommendedTiming?: FillableFormRecommendedTiming;
12
+ fields: ReadonlyArray<FillableFormFieldBinding>;
13
+ targetByRef: ReadonlyMap<string, TargetDescriptor>;
14
+ }): FillableFormGuidanceHint;
15
+ export declare const __testRequestGuidance: {
16
+ collectVisibleFieldEvidence: typeof collectVisibleFieldEvidence;
17
+ collectBlockingValidationEvidence: typeof collectBlockingValidationEvidence;
18
+ collectPassiveRequiredEvidence: typeof collectPassiveRequiredEvidence;
19
+ hasCorePaymentCardFields: typeof hasCorePaymentCardFields;
20
+ looksLikePrimaryLoginForm: typeof looksLikePrimaryLoginForm;
21
+ looksLikeStrongIdentityForm: typeof looksLikeStrongIdentityForm;
22
+ };
23
+ export {};
24
+ //# sourceMappingURL=request-guidance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-guidance.d.ts","sourceRoot":"","sources":["../../src/secrets/request-guidance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,EACV,wBAAwB,EACxB,wBAAwB,EACxB,6BAA6B,EAC7B,oBAAoB,EAErB,MAAM,YAAY,CAAC;AAoCpB,iBAAS,2BAA2B,CAClC,MAAM,EAAE,aAAa,CAAC,wBAAwB,CAAC,EAC/C,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC,GACjD,MAAM,CAYR;AAED,iBAAS,iCAAiC,CACxC,MAAM,EAAE,aAAa,CAAC,wBAAwB,CAAC,EAC/C,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC,GACjD,MAAM,EAAE,CA+BV;AAED,iBAAS,8BAA8B,CACrC,MAAM,EAAE,aAAa,CAAC,wBAAwB,CAAC,EAC/C,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC,GACjD,MAAM,EAAE,CAsBV;AAED,iBAAS,wBAAwB,CAAC,SAAS,EAAE,WAAW,CAAC,oBAAoB,CAAC,GAAG,OAAO,CAEvF;AAED,iBAAS,yBAAyB,CAAC,SAAS,EAAE,WAAW,CAAC,oBAAoB,CAAC,GAAG,OAAO,CAExF;AAED,iBAAS,2BAA2B,CAAC,SAAS,EAAE,WAAW,CAAC,oBAAoB,CAAC,GAAG,OAAO,CAK1F;AAmBD,wBAAgB,yBAAyB,CAAC,MAAM,EAAE;IAChD,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,CAAC,EAAE,6BAA6B,CAAC;IAClD,MAAM,EAAE,aAAa,CAAC,wBAAwB,CAAC,CAAC;IAChD,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;CACpD,GAAG,wBAAwB,CA4C3B;AAED,eAAO,MAAM,qBAAqB;;;;;;;CAOjC,CAAC"}
@@ -0,0 +1,142 @@
1
+ const PAYMENT_CARD_CORE_KEYS = new Set([
2
+ 'pan',
3
+ 'exp_month',
4
+ 'exp_year',
5
+ 'cvv',
6
+ ]);
7
+ const HIGH_SIGNAL_IDENTITY_KEYS = new Set([
8
+ 'document_number',
9
+ 'date_of_birth',
10
+ 'issue_date',
11
+ 'expiry_date',
12
+ 'issuing_country',
13
+ ]);
14
+ const IMMEDIATE_HINT = 'This secret is likely needed now.';
15
+ const DEFER_HINT = 'Hold off on requesting this secret for now.';
16
+ const DEFER_RECHECK = 'Re-check only if submit or validation explicitly points back to this form.';
17
+ function humanizeFieldKey(fieldKey) {
18
+ return fieldKey.replace(/_/g, ' ');
19
+ }
20
+ function labelForField(field, target) {
21
+ return target?.displayLabel ?? target?.label ?? field.label ?? humanizeFieldKey(field.fieldKey);
22
+ }
23
+ function unique(values) {
24
+ return [...new Set(values)];
25
+ }
26
+ function collectVisibleFieldEvidence(fields, targetByRef) {
27
+ const labels = unique(fields
28
+ .map((field) => labelForField(field, targetByRef.get(field.targetRef)))
29
+ .filter((value) => value.length > 0));
30
+ if (labels.length === 0) {
31
+ return 'Matched fields are visible on the page.';
32
+ }
33
+ return `Visible matched fields: ${labels.join(', ')}.`;
34
+ }
35
+ function collectBlockingValidationEvidence(fields, targetByRef) {
36
+ const evidence = [];
37
+ const seenTargets = new Set();
38
+ for (const field of fields) {
39
+ if (seenTargets.has(field.targetRef)) {
40
+ continue;
41
+ }
42
+ seenTargets.add(field.targetRef);
43
+ const target = targetByRef.get(field.targetRef);
44
+ const validation = target?.validation;
45
+ if (!validation) {
46
+ continue;
47
+ }
48
+ const label = labelForField(field, target);
49
+ if (validation.message) {
50
+ evidence.push(`"${label}" already shows validation: ${validation.message}`);
51
+ continue;
52
+ }
53
+ if (validation.invalid) {
54
+ evidence.push(`"${label}" is currently marked invalid.`);
55
+ continue;
56
+ }
57
+ if (validation.errorStyling) {
58
+ evidence.push(`"${label}" is already styled like an error.`);
59
+ }
60
+ }
61
+ return evidence;
62
+ }
63
+ function collectPassiveRequiredEvidence(fields, targetByRef) {
64
+ const evidence = [];
65
+ const seenTargets = new Set();
66
+ for (const field of fields) {
67
+ if (seenTargets.has(field.targetRef)) {
68
+ continue;
69
+ }
70
+ seenTargets.add(field.targetRef);
71
+ const target = targetByRef.get(field.targetRef);
72
+ if (!target?.validation?.required) {
73
+ continue;
74
+ }
75
+ if (target.validation.invalid || target.validation.message || target.validation.errorStyling) {
76
+ continue;
77
+ }
78
+ evidence.push(`"${labelForField(field, target)}" is marked required.`);
79
+ }
80
+ return evidence;
81
+ }
82
+ function hasCorePaymentCardFields(fieldKeys) {
83
+ return [...PAYMENT_CARD_CORE_KEYS].every((fieldKey) => fieldKeys.has(fieldKey));
84
+ }
85
+ function looksLikePrimaryLoginForm(fieldKeys) {
86
+ return fieldKeys.has('password');
87
+ }
88
+ function looksLikeStrongIdentityForm(fieldKeys) {
89
+ const strongFieldCount = [...HIGH_SIGNAL_IDENTITY_KEYS].filter((fieldKey) => fieldKeys.has(fieldKey)).length;
90
+ return strongFieldCount > 0 || fieldKeys.size >= 3;
91
+ }
92
+ function immediateGuidance(why, evidence) {
93
+ return {
94
+ hint: IMMEDIATE_HINT,
95
+ why,
96
+ evidence: evidence.filter((value) => value.length > 0),
97
+ };
98
+ }
99
+ function deferGuidance(why, evidence) {
100
+ return {
101
+ hint: DEFER_HINT,
102
+ why,
103
+ recheckWhen: DEFER_RECHECK,
104
+ evidence: evidence.filter((value) => value.length > 0),
105
+ };
106
+ }
107
+ export function buildFillableFormGuidance(params) {
108
+ const { purpose, recommendedTiming, fields, targetByRef } = params;
109
+ const visibleFieldEvidence = collectVisibleFieldEvidence(fields, targetByRef);
110
+ const blockingValidationEvidence = collectBlockingValidationEvidence(fields, targetByRef);
111
+ const passiveRequiredEvidence = collectPassiveRequiredEvidence(fields, targetByRef);
112
+ const fieldKeys = new Set(fields.map((field) => field.fieldKey));
113
+ if (blockingValidationEvidence.length > 0) {
114
+ return immediateGuidance('The page is already pointing at this form through active validation signals.', [visibleFieldEvidence, ...blockingValidationEvidence]);
115
+ }
116
+ if (purpose === 'payment_card' && hasCorePaymentCardFields(fieldKeys)) {
117
+ return immediateGuidance('This is the visible card form for the active checkout.', [
118
+ visibleFieldEvidence,
119
+ ...passiveRequiredEvidence,
120
+ ]);
121
+ }
122
+ if (purpose === 'login' && looksLikePrimaryLoginForm(fieldKeys)) {
123
+ return immediateGuidance('This looks like the main login form needed to continue.', [
124
+ visibleFieldEvidence,
125
+ ...passiveRequiredEvidence,
126
+ ]);
127
+ }
128
+ if (purpose === 'identity' && looksLikeStrongIdentityForm(fieldKeys)) {
129
+ return immediateGuidance('This looks like a full identity form rather than a one-off contact field.', [visibleFieldEvidence, ...passiveRequiredEvidence]);
130
+ }
131
+ const lateTimingEvidence = recommendedTiming === 'late' ? 'Matcher marked this form for late timing.' : '';
132
+ const noActiveBlockerEvidence = 'No matched field currently shows active validation that blocks progress.';
133
+ return deferGuidance('This form is visible, but there is not enough evidence yet that it is the blocker right now.', [visibleFieldEvidence, ...passiveRequiredEvidence, noActiveBlockerEvidence, lateTimingEvidence]);
134
+ }
135
+ export const __testRequestGuidance = {
136
+ collectVisibleFieldEvidence,
137
+ collectBlockingValidationEvidence,
138
+ collectPassiveRequiredEvidence,
139
+ hasCorePaymentCardFields,
140
+ looksLikePrimaryLoginForm,
141
+ looksLikeStrongIdentityForm,
142
+ };