@nuanu-ai/agentbrowse 0.2.30 → 0.2.31

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.
@@ -1,7 +1,9 @@
1
1
  import { z } from 'zod';
2
2
  import { AgentpayStagehandLlmClient } from '../agentpay-stagehand-llm.js';
3
3
  import { tryResolveAgentpayGatewayConfig } from '../agentpay-gateway.js';
4
+ import { recordLlmUsage, recordPayloadBudget } from '../runtime-state.js';
4
5
  import { resolveProtectedFieldPolicy } from './field-policy.js';
6
+ import { resolveDeterministicProtectedBindingValue } from './protected-value-adapters.js';
5
7
  import { protectedBindingKey, protectedBindingValueHintSchema } from './protected-bindings.js';
6
8
  const protectedFieldValuesSchema = z.object({
7
9
  values: z
@@ -22,31 +24,56 @@ function buildBindingSummary(binding, target) {
22
24
  ];
23
25
  if (target?.label)
24
26
  parts.push(`label=${JSON.stringify(target.label)}`);
25
- if (target?.displayLabel)
26
- parts.push(`displayLabel=${JSON.stringify(target.displayLabel)}`);
27
- if (target?.kind)
28
- parts.push(`kind=${JSON.stringify(target.kind)}`);
29
- if (target?.semantics?.role)
30
- parts.push(`role=${JSON.stringify(target.semantics.role)}`);
31
- if (target?.controlFamily)
32
- parts.push(`controlFamily=${JSON.stringify(target.controlFamily)}`);
33
- if (target?.context?.hintText)
34
- parts.push(`hintText=${JSON.stringify(target.context.hintText)}`);
35
- if (target?.context?.group?.label) {
36
- parts.push(`groupLabel=${JSON.stringify(target.context.group.label)}`);
27
+ if (target?.kind || target?.semantics?.role) {
28
+ parts.push(`control=${JSON.stringify({
29
+ kind: target?.kind,
30
+ role: target?.semantics?.role,
31
+ })}`);
37
32
  }
38
- if (target?.context?.container?.label) {
39
- parts.push(`containerLabel=${JSON.stringify(target.context.container.label)}`);
33
+ const ownerLabel = target?.context?.container?.label ?? target?.context?.group?.label ?? target?.displayLabel;
34
+ if (ownerLabel) {
35
+ parts.push(`owner=${JSON.stringify(ownerLabel)}`);
40
36
  }
41
37
  return parts.join(' | ');
42
38
  }
39
+ function canUseDeterministicValueForPolicy(binding, fieldPolicies) {
40
+ const policy = resolveProtectedFieldPolicy(fieldPolicies, binding.fieldKey);
41
+ if (policy !== 'llm_assisted') {
42
+ return true;
43
+ }
44
+ return (binding.valueHint ?? 'direct') !== 'direct';
45
+ }
43
46
  export async function resolveAssistedProtectedFieldValues(params) {
44
47
  const assistedBindings = params.bindings.filter(({ binding }) => {
45
48
  return resolveProtectedFieldPolicy(params.fieldPolicies, binding.fieldKey) === 'llm_assisted';
46
49
  });
50
+ if (params.session) {
51
+ recordPayloadBudget(params.session, {
52
+ protectedBindingsSeen: params.bindings.length,
53
+ });
54
+ }
47
55
  if (assistedBindings.length === 0) {
48
56
  return new Map();
49
57
  }
58
+ const resolvedValues = new Map();
59
+ const unresolvedBindings = assistedBindings.filter(({ binding }) => {
60
+ const deterministicValue = resolveDeterministicProtectedBindingValue(binding, params.protectedValues);
61
+ if (typeof deterministicValue !== 'string' ||
62
+ deterministicValue.trim().length === 0 ||
63
+ !canUseDeterministicValueForPolicy(binding, params.fieldPolicies)) {
64
+ return true;
65
+ }
66
+ resolvedValues.set(protectedBindingKey(binding), deterministicValue.trim());
67
+ return false;
68
+ });
69
+ if (params.session) {
70
+ recordPayloadBudget(params.session, {
71
+ protectedBindingsSent: unresolvedBindings.length,
72
+ });
73
+ }
74
+ if (unresolvedBindings.length === 0) {
75
+ return resolvedValues;
76
+ }
50
77
  const gateway = tryResolveAgentpayGatewayConfig();
51
78
  if (!gateway) {
52
79
  throw new Error('protected_field_value_resolver_gateway_missing');
@@ -68,7 +95,7 @@ export async function resolveAssistedProtectedFieldValues(params) {
68
95
  '',
69
96
  `pageLocale=${JSON.stringify(pageLocale)}`,
70
97
  'Bindings:',
71
- ...assistedBindings.map(({ binding, target }) => {
98
+ ...unresolvedBindings.map(({ binding, target }) => {
72
99
  const storedValue = params.protectedValues[binding.fieldKey] ?? '';
73
100
  return `${buildBindingSummary(binding, target)} | storedValue=${JSON.stringify(storedValue)}`;
74
101
  }),
@@ -83,8 +110,14 @@ export async function resolveAssistedProtectedFieldValues(params) {
83
110
  },
84
111
  },
85
112
  });
86
- const allowedKeys = new Set(assistedBindings.map(({ binding }) => protectedBindingKey(binding)));
87
- const resolvedValues = new Map();
113
+ if (params.session) {
114
+ recordLlmUsage(params.session, {
115
+ purpose: 'browse.protected_fill.resolve',
116
+ usage: result.usage,
117
+ inputChars: prompt.length,
118
+ });
119
+ }
120
+ const allowedKeys = new Set(unresolvedBindings.map(({ binding }) => protectedBindingKey(binding)));
88
121
  for (const entry of result.data.values) {
89
122
  const key = protectedBindingKey(entry);
90
123
  if (!allowedKeys.has(key) || entry.confidence === 'low') {
@@ -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;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAyB,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAoBnF,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;AAoIN,iBAAS,eAAe,CAAC,MAAM,EAAE,gBAAgB,GAAG,mBAAmB,CActE;AAED,iBAAS,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAK7D;AAqVD,wBAAsB,oBAAoB,CAAC,MAAM,EAAE;IACjD,OAAO,EAAE,aAAa,CAAC;IACvB,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,CA4KxC;AAED,eAAO,MAAM,mBAAmB;;;qCAIjB,aAAa,CAAC,oBAAoB,CAAC,mBAC7B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;CAY1C,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;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAyB,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAqBnF,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;AAoIN,iBAAS,eAAe,CAAC,MAAM,EAAE,gBAAgB,GAAG,mBAAmB,CActE;AAED,iBAAS,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAK7D;AA2VD,wBAAsB,oBAAoB,CAAC,MAAM,EAAE;IACjD,OAAO,EAAE,aAAa,CAAC;IACvB,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,CA6KxC;AAED,eAAO,MAAM,mBAAmB;;;qCAIjB,aAAa,CAAC,oBAAoB,CAAC,mBAC7B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;CAY1C,CAAC"}
@@ -7,6 +7,7 @@ import { resolveSurfaceScopeRoot } from '../commands/target-resolution.js';
7
7
  import { resolveProtectedFieldPolicy } from './field-policy.js';
8
8
  import { protectedBindingKey } from './protected-bindings.js';
9
9
  import { resolveAssistedProtectedFieldValues } from './protected-field-values.js';
10
+ import { resolveDeterministicProtectedBindingValue } from './protected-value-adapters.js';
10
11
  import { sortProtectedBindingsForExecution } from './fill-ordering.js';
11
12
  function targetUsesSurfaceAsPrimaryLocator(target, surface) {
12
13
  const surfaceCandidateKeys = new Set(surface.locatorCandidates.map((candidate) => [
@@ -116,6 +117,7 @@ function resolveBindingValue(fields, protectedValues, fieldPolicies, assistedVal
116
117
  if (fields.length === 1) {
117
118
  const field = fields[0];
118
119
  const policy = resolveProtectedFieldPolicy(fieldPolicies, field.fieldKey);
120
+ const deterministicValue = resolveDeterministicProtectedBindingValue(field, protectedValues);
119
121
  if (policy === 'llm_assisted') {
120
122
  const assistedValue = assistedValues.get(protectedBindingKey(field));
121
123
  if (typeof assistedValue === 'string' && assistedValue.length > 0) {
@@ -123,6 +125,9 @@ function resolveBindingValue(fields, protectedValues, fieldPolicies, assistedVal
123
125
  }
124
126
  throw new Error('assisted_value_resolution_failed');
125
127
  }
128
+ if (typeof deterministicValue === 'string' && deterministicValue.length > 0) {
129
+ return deterministicValue;
130
+ }
126
131
  if ((field.valueHint ?? 'direct') !== 'direct') {
127
132
  throw new Error('deterministic_only_resolution_failed');
128
133
  }
@@ -371,6 +376,7 @@ export async function executeProtectedFill(params) {
371
376
  let assistedValues;
372
377
  try {
373
378
  assistedValues = await resolveAssistedProtectedFieldValues({
379
+ session: params.session,
374
380
  page: params.page,
375
381
  bindings: preparedBindings.map((binding) => ({
376
382
  binding: binding.fields[0],
@@ -0,0 +1,3 @@
1
+ import type { FillableFormFieldBinding } from './types.js';
2
+ export declare function resolveDeterministicProtectedBindingValue(binding: Pick<FillableFormFieldBinding, 'fieldKey' | 'valueHint'>, protectedValues: Record<string, string>): string | null;
3
+ //# sourceMappingURL=protected-value-adapters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protected-value-adapters.d.ts","sourceRoot":"","sources":["../../src/secrets/protected-value-adapters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAwB,MAAM,YAAY,CAAC;AAqBjF,wBAAgB,yCAAyC,CACvD,OAAO,EAAE,IAAI,CAAC,wBAAwB,EAAE,UAAU,GAAG,WAAW,CAAC,EACjE,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACtC,MAAM,GAAG,IAAI,CA+Bf"}
@@ -0,0 +1,39 @@
1
+ function normalizeWhitespace(value) {
2
+ return value.replace(/\s+/g, ' ').trim();
3
+ }
4
+ function splitFullName(value) {
5
+ return normalizeWhitespace(value)
6
+ .split(' ')
7
+ .map((part) => part.trim())
8
+ .filter(Boolean);
9
+ }
10
+ function directStoredValue(protectedValues, fieldKey) {
11
+ const value = protectedValues[fieldKey];
12
+ return typeof value === 'string' && value.trim().length > 0 ? value : null;
13
+ }
14
+ export function resolveDeterministicProtectedBindingValue(binding, protectedValues) {
15
+ if (binding.fieldKey !== 'full_name') {
16
+ return binding.valueHint === 'direct'
17
+ ? directStoredValue(protectedValues, binding.fieldKey)
18
+ : null;
19
+ }
20
+ const fullName = directStoredValue(protectedValues, 'full_name');
21
+ if (!fullName) {
22
+ return null;
23
+ }
24
+ const valueHint = binding.valueHint ?? 'direct';
25
+ if (valueHint === 'direct') {
26
+ return fullName;
27
+ }
28
+ const parts = splitFullName(fullName);
29
+ if (parts.length === 0) {
30
+ return null;
31
+ }
32
+ if (valueHint === 'full_name.given') {
33
+ return parts[0] ?? fullName;
34
+ }
35
+ if (valueHint === 'full_name.family') {
36
+ return parts.length > 1 ? (parts.at(-1) ?? fullName) : fullName;
37
+ }
38
+ return null;
39
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuanu-ai/agentbrowse",
3
- "version": "0.2.30",
3
+ "version": "0.2.31",
4
4
  "type": "module",
5
5
  "description": "Browser automation CLI for AI agents: control a CDP browser, observe UI surfaces, act on refs, extract data, capture screenshots, complete protected fills, and solve captchas",
6
6
  "keywords": [