@nuanu-ai/agentbrowse 0.2.7 → 0.2.8

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 (191) 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 +84 -0
  75. package/dist/commands/observe-projection.d.ts.map +1 -0
  76. package/dist/commands/observe-projection.js +140 -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 +596 -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/types.d.ts +84 -0
  178. package/dist/secrets/types.d.ts.map +1 -0
  179. package/dist/secrets/types.js +27 -0
  180. package/dist/session.d.ts +22 -0
  181. package/dist/session.d.ts.map +1 -1
  182. package/dist/session.js +74 -2
  183. package/dist/solver/browser-launcher.d.ts.map +1 -1
  184. package/dist/solver/browser-launcher.js +6 -3
  185. package/dist/stagehand-runtime.d.ts +4 -0
  186. package/dist/stagehand-runtime.d.ts.map +1 -0
  187. package/dist/stagehand-runtime.js +10 -0
  188. package/dist/stagehand.d.ts +0 -5
  189. package/dist/stagehand.d.ts.map +1 -1
  190. package/dist/stagehand.js +0 -6
  191. package/package.json +5 -2
@@ -0,0 +1,57 @@
1
+ /**
2
+ * browse poll-intent <intentId> — Poll stored-secret intent state and capture one-time delivery.
3
+ */
4
+ import { outputFailure, outputJSON } from '../output.js';
5
+ import { saveSecretIntentSnapshot } from '../runtime-state.js';
6
+ import { cacheTransientSecret, deleteCachedTransientSecret, saveSession } from '../session.js';
7
+ import { getSecretBackend } from '../secrets/backend.js';
8
+ import { describeSecretIntentStatus, serializeSecretIntent } from '../secrets/intent-output.js';
9
+ export async function pollIntent(session, intentId) {
10
+ const backend = getSecretBackend();
11
+ const result = await backend.getSecretIntent(intentId);
12
+ if (!result) {
13
+ return outputFailure({
14
+ error: 'secret_intent_not_found',
15
+ message: 'Protected intent lookup failed because the requested intent does not exist.',
16
+ outcomeType: 'blocked',
17
+ reason: 'The provided intentId was not found in AgentPay Cabinet.',
18
+ intentId,
19
+ });
20
+ }
21
+ const snapshot = saveSecretIntentSnapshot(session, result.snapshot);
22
+ if (result.deliveredValues) {
23
+ cacheTransientSecret(session, {
24
+ intentId: snapshot.intentId,
25
+ fillRef: snapshot.fillRef,
26
+ storedSecretRef: snapshot.storedSecretRef,
27
+ cachedAt: new Date().toISOString(),
28
+ approvedAt: snapshot.approvedAt,
29
+ expiresAt: snapshot.expiresAt,
30
+ values: result.deliveredValues,
31
+ });
32
+ }
33
+ else if (snapshot.status === 'denied' ||
34
+ snapshot.status === 'timed_out' ||
35
+ snapshot.status === 'completed') {
36
+ deleteCachedTransientSecret(session, snapshot.intentId);
37
+ }
38
+ saveSession(session);
39
+ if (result.deliveredValues && snapshot.status === 'completed') {
40
+ return outputJSON({
41
+ success: true,
42
+ ...serializeSecretIntent(snapshot),
43
+ message: 'Protected intent delivered a one-time secret payload and is ready to fill.',
44
+ reason: 'AgentPay approved this request, returned the protected values, and completed the intent.',
45
+ nextAction: 'fill-secret',
46
+ });
47
+ }
48
+ const statusContract = describeSecretIntentStatus(snapshot.status);
49
+ outputJSON({
50
+ success: true,
51
+ ...serializeSecretIntent(snapshot),
52
+ ...(statusContract.outcomeType ? { outcomeType: statusContract.outcomeType } : {}),
53
+ message: statusContract.message,
54
+ reason: statusContract.reason,
55
+ ...(statusContract.nextAction ? { nextAction: statusContract.nextAction } : {}),
56
+ });
57
+ }
@@ -1,5 +1,6 @@
1
1
  /**
2
2
  * browse screenshot [--path <file>] — Capture a screenshot of the current page.
3
3
  */
4
- export declare function screenshot(cdpUrl: string, filePath?: string): Promise<void>;
4
+ import type { BrowseSession } from '../session.js';
5
+ export declare function screenshot(session: BrowseSession, filePath?: string): Promise<void>;
5
6
  //# sourceMappingURL=screenshot.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"screenshot.d.ts","sourceRoot":"","sources":["../../src/commands/screenshot.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAyBjF"}
1
+ {"version":3,"file":"screenshot.d.ts","sourceRoot":"","sources":["../../src/commands/screenshot.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAYnD,wBAAsB,UAAU,CAAC,OAAO,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAqDzF"}
@@ -1,27 +1,59 @@
1
1
  /**
2
2
  * browse screenshot [--path <file>] — Capture a screenshot of the current page.
3
3
  */
4
- import { connectStagehand, getPageInfo } from '../stagehand.js';
5
- import { outputJSON, outputError, info } from '../output.js';
6
- export async function screenshot(cdpUrl, filePath) {
4
+ import { getProtectedExposure } from '../runtime-state.js';
5
+ import { saveSession } from '../session.js';
6
+ import { outputFailure, outputJSON, outputContractFailure, info } from '../output.js';
7
+ import { connectPlaywright, disconnectPlaywright, listPages, resolvePageByRef, syncSessionPage, } from '../playwright-runtime.js';
8
+ import { buildProtectedScreenshotBlockedResult } from '../secrets/protected-artifact-guard.js';
9
+ export async function screenshot(session, filePath) {
7
10
  const outputPath = filePath ?? `/tmp/browse-screenshot-${Date.now()}.png`;
8
- let stagehand;
11
+ const pageRef = session.runtime?.currentPageRef ?? 'p0';
12
+ const protectedExposure = getProtectedExposure(session, pageRef);
13
+ if (protectedExposure) {
14
+ return outputFailure(buildProtectedScreenshotBlockedResult(protectedExposure));
15
+ }
16
+ let browser = null;
17
+ let failureMessage = null;
9
18
  try {
10
- stagehand = await connectStagehand(cdpUrl);
19
+ browser = await connectPlaywright(session.cdpUrl);
11
20
  }
12
21
  catch (err) {
13
- outputError(`Failed to connect to browser: ${err instanceof Error ? err.message : String(err)}`);
22
+ return outputContractFailure({
23
+ error: 'browser_connection_failed',
24
+ outcomeType: 'blocked',
25
+ message: 'Screenshot capture could not start because AgentBrowse failed to connect to the browser.',
26
+ reason: err instanceof Error ? err.message : String(err),
27
+ });
14
28
  }
15
29
  try {
16
- const page = stagehand.context.pages()[0];
30
+ const pages = listPages(browser);
31
+ const page = session.runtime?.pages[pageRef] || pages.length > 1
32
+ ? await resolvePageByRef(browser, session, pageRef)
33
+ : pages[0];
34
+ if (!page) {
35
+ throw new Error('no_open_pages');
36
+ }
17
37
  await page.screenshot({ path: outputPath });
18
38
  info(`Screenshot saved: ${outputPath}`);
19
- const { url, title } = await getPageInfo(stagehand);
20
- await stagehand.close();
21
- outputJSON({ success: true, path: outputPath, url, title });
39
+ const { url, title } = await syncSessionPage(session, pageRef, page);
40
+ saveSession(session);
41
+ outputJSON({ success: true, pageRef, path: outputPath, url, title });
22
42
  }
23
43
  catch (err) {
24
- await stagehand.close();
25
- outputError(`Screenshot failed: ${err instanceof Error ? err.message : String(err)}`);
44
+ failureMessage = `Screenshot failed: ${err instanceof Error ? err.message : String(err)}`;
45
+ }
46
+ finally {
47
+ if (browser) {
48
+ disconnectPlaywright(browser);
49
+ }
50
+ }
51
+ if (failureMessage) {
52
+ outputContractFailure({
53
+ error: 'screenshot_failed',
54
+ outcomeType: 'blocked',
55
+ message: 'Screenshot capture failed.',
56
+ reason: failureMessage.replace(/^Screenshot failed:\s*/, ''),
57
+ });
26
58
  }
27
59
  }
@@ -0,0 +1,10 @@
1
+ import type { FrameLocator, Locator, Page } from 'playwright-core';
2
+ import { type ActionExecutionGuards } from './action-execution-guards.js';
3
+ type LocatorRoot = Page | FrameLocator | Locator;
4
+ type SelectActionHelpers = {
5
+ focusLocator: (page: Page, locator: Locator, attempts: string[]) => Promise<void>;
6
+ clearLocatorForReplacement: (locator: Locator, attempts: string[]) => Promise<void>;
7
+ };
8
+ export declare function applySelectLikeAction(page: Page, root: LocatorRoot, locator: Locator, value: string, attempts: string[], helpers: SelectActionHelpers, guards?: ActionExecutionGuards): Promise<boolean>;
9
+ export {};
10
+ //# sourceMappingURL=select-action-executor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select-action-executor.d.ts","sourceRoot":"","sources":["../../src/commands/select-action-executor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAEL,KAAK,qBAAqB,EAC3B,MAAM,8BAA8B,CAAC;AAEtC,KAAK,WAAW,GAAG,IAAI,GAAG,YAAY,GAAG,OAAO,CAAC;AAMjD,KAAK,mBAAmB,GAAG;IACzB,YAAY,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClF,0BAA0B,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrF,CAAC;AAoFF,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,IAAI,EACV,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAAE,EAClB,OAAO,EAAE,mBAAmB,EAC5B,MAAM,CAAC,EAAE,qBAAqB,GAC7B,OAAO,CAAC,OAAO,CAAC,CA0BlB"}
@@ -0,0 +1,91 @@
1
+ import { runActionExecutionGuard, } from './action-execution-guards.js';
2
+ const OPTION_RESOLVE_TIMEOUT_MS = 250;
3
+ const ASYNC_OPTION_WAIT_MS = 1_000;
4
+ const OPTION_POLL_INTERVAL_MS = 100;
5
+ async function clickOptionFromRoot(root, value, attempts, options, guards) {
6
+ const deadline = Date.now() + Math.max(0, options?.waitForMs ?? 0);
7
+ while (true) {
8
+ await runActionExecutionGuard(guards, 'select.option-resolve');
9
+ const roleCandidates = ['option', 'menuitem'];
10
+ for (const role of roleCandidates) {
11
+ attempts.push(`option.resolve:role:${role}`);
12
+ try {
13
+ const option = root.getByRole(role, { name: value }).first();
14
+ await option.click({ timeout: OPTION_RESOLVE_TIMEOUT_MS });
15
+ attempts.push(`option.click:role:${role}`);
16
+ return true;
17
+ }
18
+ catch {
19
+ // Try the next candidate.
20
+ }
21
+ }
22
+ attempts.push('option.resolve:text');
23
+ try {
24
+ const option = root.getByText(value).first();
25
+ await option.click({ timeout: OPTION_RESOLVE_TIMEOUT_MS });
26
+ attempts.push('option.click:text');
27
+ return true;
28
+ }
29
+ catch {
30
+ if (Date.now() >= deadline) {
31
+ return false;
32
+ }
33
+ }
34
+ await runActionExecutionGuard(guards, 'select.option-await');
35
+ attempts.push('option.await');
36
+ await new Promise((resolve) => setTimeout(resolve, OPTION_POLL_INTERVAL_MS));
37
+ }
38
+ }
39
+ async function tryTypeToFilterSelectOption(root, locator, value, attempts, helpers, guards) {
40
+ if (typeof locator.pressSequentially !== 'function') {
41
+ return false;
42
+ }
43
+ await helpers.clearLocatorForReplacement(locator, attempts);
44
+ attempts.push('locator.pressSequentially.select');
45
+ try {
46
+ await runActionExecutionGuard(guards, 'select.press-sequentially');
47
+ await locator.pressSequentially(value);
48
+ }
49
+ catch {
50
+ return false;
51
+ }
52
+ return clickOptionFromRoot(root, value, attempts, { waitForMs: ASYNC_OPTION_WAIT_MS }, guards);
53
+ }
54
+ async function pressSelectTriggerKey(locator, key, attempts) {
55
+ attempts.push(`locator.press:${key}`);
56
+ try {
57
+ await locator.press(key);
58
+ return true;
59
+ }
60
+ catch {
61
+ return false;
62
+ }
63
+ }
64
+ export async function applySelectLikeAction(page, root, locator, value, attempts, helpers, guards) {
65
+ attempts.push('locator.selectOption');
66
+ try {
67
+ await locator.selectOption(value);
68
+ return false;
69
+ }
70
+ catch {
71
+ await runActionExecutionGuard(guards, 'select.after-error');
72
+ await helpers.focusLocator(page, locator, attempts);
73
+ if (await clickOptionFromRoot(root, value, attempts, undefined, guards)) {
74
+ return true;
75
+ }
76
+ if (await tryTypeToFilterSelectOption(root, locator, value, attempts, helpers, guards)) {
77
+ return true;
78
+ }
79
+ for (const key of ['Enter', 'Space', 'ArrowDown']) {
80
+ await runActionExecutionGuard(guards, `select.press:${key.toLowerCase()}`);
81
+ const pressed = await pressSelectTriggerKey(locator, key, attempts);
82
+ if (!pressed) {
83
+ continue;
84
+ }
85
+ if (await clickOptionFromRoot(root, value, attempts, undefined, guards)) {
86
+ return true;
87
+ }
88
+ }
89
+ throw new Error('select_option_not_found');
90
+ }
91
+ }
@@ -0,0 +1,24 @@
1
+ import type { TargetAcceptancePolicy, TargetAllowedAction, TargetCapability, TargetContext, TargetControlFamily, TargetStructure } from '../runtime-state.js';
2
+ type ObserveInventoryTarget = {
3
+ kind?: string;
4
+ label?: string;
5
+ role?: string;
6
+ interactionHint?: 'click';
7
+ placeholder?: string;
8
+ title?: string;
9
+ states?: Record<string, string | boolean | number>;
10
+ context?: TargetContext;
11
+ capability?: TargetCapability;
12
+ allowedActions?: TargetAllowedAction[];
13
+ acceptancePolicy?: TargetAcceptancePolicy;
14
+ controlFamily?: TargetControlFamily;
15
+ surfaceRef?: string;
16
+ surfacePriority?: number;
17
+ structure?: TargetStructure;
18
+ framePath?: string[];
19
+ frameUrl?: string;
20
+ formSelector?: string;
21
+ };
22
+ export declare function rerankDomTargetsForGoal<T extends ObserveInventoryTarget>(instruction: string, targets: ReadonlyArray<T>): Promise<T[]>;
23
+ export {};
24
+ //# sourceMappingURL=semantic-observe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semantic-observe.d.ts","sourceRoot":"","sources":["../../src/commands/semantic-observe.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,sBAAsB,EACtB,mBAAmB,EACnB,gBAAgB,EAChB,aAAa,EACb,mBAAmB,EACnB,eAAe,EAChB,MAAM,qBAAqB,CAAC;AAE7B,KAAK,sBAAsB,GAAG;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC,CAAC;IACnD,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,cAAc,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACvC,gBAAgB,CAAC,EAAE,sBAAsB,CAAC;IAC1C,aAAa,CAAC,EAAE,mBAAmB,CAAC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AA0UF,wBAAsB,uBAAuB,CAAC,CAAC,SAAS,sBAAsB,EAC5E,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,GACxB,OAAO,CAAC,CAAC,EAAE,CAAC,CAkDd"}
@@ -0,0 +1,344 @@
1
+ import { z } from 'zod';
2
+ import { AgentpayStagehandLlmClient } from '../agentpay-stagehand-llm.js';
3
+ import { resolveAgentpayGatewayConfig } from '../agentpay-gateway.js';
4
+ const rerankSchema = z.object({
5
+ matches: z
6
+ .array(z.object({
7
+ candidateId: z.string(),
8
+ }))
9
+ .max(8),
10
+ });
11
+ const RERANK_CANDIDATE_LIMIT = 120;
12
+ const FORM_BUCKET_RESERVE_LIMIT = 48;
13
+ const FORM_BUCKET_RESERVE_PER_BUCKET = 8;
14
+ const SCOPE_BUCKET_RESERVE_LIMIT = 24;
15
+ const SCOPE_BUCKET_RESERVE_PER_BUCKET = 2;
16
+ const IFRAME_BUCKET_RESERVE_LIMIT = 24;
17
+ const IFRAME_BUCKET_RESERVE_PER_BUCKET = 2;
18
+ const HIGH_SIGNAL_SCOPE_KINDS = new Set([
19
+ 'dialog',
20
+ 'listbox',
21
+ 'menu',
22
+ 'grid',
23
+ 'tabpanel',
24
+ 'popover',
25
+ 'dropdown',
26
+ 'datepicker',
27
+ 'card',
28
+ 'form',
29
+ ]);
30
+ function isFieldLikeTarget(target) {
31
+ const kind = (target.kind ?? '').trim().toLowerCase();
32
+ const role = (target.role ?? '').trim().toLowerCase();
33
+ return (['input', 'textarea', 'select', 'combobox'].includes(kind) ||
34
+ ['textbox', 'combobox'].includes(role));
35
+ }
36
+ function semanticFormContextKey(context) {
37
+ for (const node of [context?.landmark, context?.container, context?.group, context?.item]) {
38
+ const kind = (node?.kind ?? '').trim().toLowerCase();
39
+ if (kind !== 'form') {
40
+ continue;
41
+ }
42
+ const label = (node?.label ?? node?.text ?? '').replace(/\s+/g, ' ').trim().toLowerCase();
43
+ return `${kind}|${label}`;
44
+ }
45
+ return undefined;
46
+ }
47
+ function formBucketKey(target) {
48
+ const formSelector = target.formSelector?.trim();
49
+ if (formSelector) {
50
+ return `selector:${formSelector}`;
51
+ }
52
+ const contextKey = semanticFormContextKey(target.context);
53
+ return contextKey ? `context:${contextKey}` : undefined;
54
+ }
55
+ function candidateBucketKey(target, options = {}) {
56
+ const frameKey = target.framePath?.join('>') ?? 'top';
57
+ const formKey = formBucketKey(target);
58
+ const surfaceKey = (options.preferFormBucket ? formKey : undefined) ??
59
+ target.surfaceRef ??
60
+ formKey ??
61
+ 'page-root';
62
+ return `${frameKey}|${surfaceKey}`;
63
+ }
64
+ function isPrimaryFormControlTarget(target) {
65
+ const kind = (target.kind ?? '').trim().toLowerCase();
66
+ const role = (target.role ?? '').trim().toLowerCase();
67
+ const controlFamily = (target.controlFamily ?? '').trim().toLowerCase();
68
+ const acceptancePolicy = (target.acceptancePolicy ?? '').trim().toLowerCase();
69
+ if (target.allowedActions?.includes('fill') || target.allowedActions?.includes('type')) {
70
+ return true;
71
+ }
72
+ if (target.allowedActions?.includes('select')) {
73
+ return true;
74
+ }
75
+ if (['text-input', 'select', 'datepicker'].includes(controlFamily)) {
76
+ return true;
77
+ }
78
+ if (acceptancePolicy === 'submit' || acceptancePolicy === 'date-selection') {
79
+ return true;
80
+ }
81
+ return (Boolean(formBucketKey(target)) &&
82
+ acceptancePolicy === 'disclosure' &&
83
+ (kind === 'button' || role === 'button'));
84
+ }
85
+ function primaryFormTargetPriority(target) {
86
+ const acceptancePolicy = (target.acceptancePolicy ?? '').trim().toLowerCase();
87
+ if (isFieldLikeTarget(target)) {
88
+ return 0;
89
+ }
90
+ if (acceptancePolicy === 'submit') {
91
+ return 1;
92
+ }
93
+ if (acceptancePolicy === 'selection' || acceptancePolicy === 'date-selection') {
94
+ return 2;
95
+ }
96
+ if (acceptancePolicy === 'disclosure') {
97
+ return 3;
98
+ }
99
+ return 4;
100
+ }
101
+ function isHighSignalScopeCandidate(target) {
102
+ if ((target.capability ?? '').trim().toLowerCase() !== 'scope') {
103
+ return false;
104
+ }
105
+ const kind = (target.kind ?? '').trim().toLowerCase();
106
+ return HIGH_SIGNAL_SCOPE_KINDS.has(kind);
107
+ }
108
+ function scopeCandidatePriority(target) {
109
+ const kind = (target.kind ?? '').trim().toLowerCase();
110
+ if (kind === 'dialog' || kind === 'listbox' || kind === 'menu') {
111
+ return 0;
112
+ }
113
+ if (kind === 'card') {
114
+ return 1;
115
+ }
116
+ if (kind === 'grid' || kind === 'tabpanel' || kind === 'datepicker') {
117
+ return 2;
118
+ }
119
+ if (kind === 'form') {
120
+ return 3;
121
+ }
122
+ return 4;
123
+ }
124
+ function collectBucketedCandidateIndexes(targets, options) {
125
+ const bucketEntries = new Map();
126
+ for (const [index, target] of targets.entries()) {
127
+ if (!options.predicate(target)) {
128
+ continue;
129
+ }
130
+ const bucketKey = options.bucketKeyOf(target);
131
+ if (!bucketKey) {
132
+ continue;
133
+ }
134
+ const entries = bucketEntries.get(bucketKey) ?? [];
135
+ entries.push({ index, target });
136
+ bucketEntries.set(bucketKey, entries);
137
+ }
138
+ const bucketQueues = [...bucketEntries.values()]
139
+ .map((entries) => entries
140
+ .sort((left, right) => {
141
+ const leftPriority = options.priorityOf?.(left.target) ?? 0;
142
+ const rightPriority = options.priorityOf?.(right.target) ?? 0;
143
+ if (leftPriority !== rightPriority) {
144
+ return leftPriority - rightPriority;
145
+ }
146
+ return left.index - right.index;
147
+ })
148
+ .slice(0, options.perBucketLimit))
149
+ .sort((left, right) => left[0].index - right[0].index);
150
+ const selectedIndexes = [];
151
+ while (selectedIndexes.length < options.totalLimit) {
152
+ let progressed = false;
153
+ for (const queue of bucketQueues) {
154
+ const next = queue.shift();
155
+ if (!next) {
156
+ continue;
157
+ }
158
+ selectedIndexes.push(next.index);
159
+ progressed = true;
160
+ if (selectedIndexes.length >= options.totalLimit) {
161
+ break;
162
+ }
163
+ }
164
+ if (!progressed) {
165
+ break;
166
+ }
167
+ }
168
+ return selectedIndexes;
169
+ }
170
+ function diversifyCandidates(targets) {
171
+ if (targets.length <= RERANK_CANDIDATE_LIMIT) {
172
+ return [...targets];
173
+ }
174
+ const orderedIndexes = [];
175
+ const selectedIndexes = new Set();
176
+ const pushSelectedIndexes = (indexes) => {
177
+ for (const index of indexes) {
178
+ if (selectedIndexes.has(index)) {
179
+ continue;
180
+ }
181
+ selectedIndexes.add(index);
182
+ orderedIndexes.push(index);
183
+ if (orderedIndexes.length >= RERANK_CANDIDATE_LIMIT) {
184
+ break;
185
+ }
186
+ }
187
+ };
188
+ pushSelectedIndexes(collectBucketedCandidateIndexes(targets, {
189
+ totalLimit: FORM_BUCKET_RESERVE_LIMIT,
190
+ perBucketLimit: FORM_BUCKET_RESERVE_PER_BUCKET,
191
+ bucketKeyOf: (target) => candidateBucketKey(target, { preferFormBucket: true }),
192
+ predicate: (target) => Boolean(formBucketKey(target)) && isPrimaryFormControlTarget(target),
193
+ priorityOf: primaryFormTargetPriority,
194
+ }));
195
+ pushSelectedIndexes(collectBucketedCandidateIndexes(targets, {
196
+ totalLimit: SCOPE_BUCKET_RESERVE_LIMIT,
197
+ perBucketLimit: SCOPE_BUCKET_RESERVE_PER_BUCKET,
198
+ bucketKeyOf: (target) => candidateBucketKey(target),
199
+ predicate: isHighSignalScopeCandidate,
200
+ priorityOf: scopeCandidatePriority,
201
+ }));
202
+ pushSelectedIndexes(collectBucketedCandidateIndexes(targets, {
203
+ totalLimit: IFRAME_BUCKET_RESERVE_LIMIT,
204
+ perBucketLimit: IFRAME_BUCKET_RESERVE_PER_BUCKET,
205
+ bucketKeyOf: (target) => candidateBucketKey(target),
206
+ predicate: (target) => Boolean(target.framePath?.length) && isFieldLikeTarget(target),
207
+ }));
208
+ for (let index = 0; index < targets.length && orderedIndexes.length < RERANK_CANDIDATE_LIMIT; index += 1) {
209
+ if (selectedIndexes.has(index)) {
210
+ continue;
211
+ }
212
+ selectedIndexes.add(index);
213
+ orderedIndexes.push(index);
214
+ }
215
+ return orderedIndexes.map((index) => targets[index]).slice(0, RERANK_CANDIDATE_LIMIT);
216
+ }
217
+ function buildCandidateSummary(target, index) {
218
+ const parts = [`id=c${index + 1}`];
219
+ const container = target.context?.container;
220
+ const item = target.context?.item;
221
+ const group = target.context?.group;
222
+ const landmark = target.context?.landmark;
223
+ const layout = target.context?.layout;
224
+ const visual = target.context?.visual;
225
+ if (target.kind)
226
+ parts.push(`kind=${JSON.stringify(target.kind)}`);
227
+ if (target.role)
228
+ parts.push(`role=${JSON.stringify(target.role)}`);
229
+ if (target.label)
230
+ parts.push(`label=${JSON.stringify(target.label)}`);
231
+ if (target.interactionHint) {
232
+ parts.push(`interactionHint=${JSON.stringify(target.interactionHint)}`);
233
+ }
234
+ if (target.capability)
235
+ parts.push(`capability=${JSON.stringify(target.capability)}`);
236
+ if (target.allowedActions?.length) {
237
+ parts.push(`allowedActions=${JSON.stringify(target.allowedActions)}`);
238
+ }
239
+ if (target.acceptancePolicy) {
240
+ parts.push(`acceptancePolicy=${JSON.stringify(target.acceptancePolicy)}`);
241
+ }
242
+ if (target.controlFamily) {
243
+ parts.push(`controlFamily=${JSON.stringify(target.controlFamily)}`);
244
+ }
245
+ if (target.surfaceRef)
246
+ parts.push(`surfaceRef=${JSON.stringify(target.surfaceRef)}`);
247
+ if (typeof target.surfacePriority === 'number') {
248
+ parts.push(`surfacePriority=${JSON.stringify(target.surfacePriority)}`);
249
+ }
250
+ if (target.framePath?.length)
251
+ parts.push(`framePath=${JSON.stringify(target.framePath)}`);
252
+ if (target.frameUrl)
253
+ parts.push(`frameUrl=${JSON.stringify(target.frameUrl)}`);
254
+ if (target.formSelector)
255
+ parts.push(`formSelector=${JSON.stringify(target.formSelector)}`);
256
+ if (target.structure)
257
+ parts.push(`structure=${JSON.stringify(target.structure)}`);
258
+ if (target.placeholder)
259
+ parts.push(`placeholder=${JSON.stringify(target.placeholder)}`);
260
+ if (target.title)
261
+ parts.push(`title=${JSON.stringify(target.title)}`);
262
+ if (target.states)
263
+ parts.push(`state=${JSON.stringify(target.states)}`);
264
+ if (item?.kind)
265
+ parts.push(`itemKind=${JSON.stringify(item.kind)}`);
266
+ if (item?.label)
267
+ parts.push(`itemLabel=${JSON.stringify(item.label)}`);
268
+ if (item?.text)
269
+ parts.push(`itemText=${JSON.stringify(item.text)}`);
270
+ if (group?.kind)
271
+ parts.push(`groupKind=${JSON.stringify(group.kind)}`);
272
+ if (group?.label)
273
+ parts.push(`groupLabel=${JSON.stringify(group.label)}`);
274
+ if (group?.text)
275
+ parts.push(`groupText=${JSON.stringify(group.text)}`);
276
+ if (container?.kind)
277
+ parts.push(`containerKind=${JSON.stringify(container.kind)}`);
278
+ if (container?.label)
279
+ parts.push(`containerLabel=${JSON.stringify(container.label)}`);
280
+ if (container?.text)
281
+ parts.push(`containerText=${JSON.stringify(container.text)}`);
282
+ if (landmark?.kind)
283
+ parts.push(`landmarkKind=${JSON.stringify(landmark.kind)}`);
284
+ if (landmark?.label)
285
+ parts.push(`landmarkLabel=${JSON.stringify(landmark.label)}`);
286
+ if (landmark?.text)
287
+ parts.push(`landmarkText=${JSON.stringify(landmark.text)}`);
288
+ if (layout?.lane)
289
+ parts.push(`lane=${JSON.stringify(layout.lane)}`);
290
+ if (layout?.band)
291
+ parts.push(`band=${JSON.stringify(layout.band)}`);
292
+ if (visual)
293
+ parts.push(`visual=${JSON.stringify(visual)}`);
294
+ if (target.context?.hintText)
295
+ parts.push(`hintText=${JSON.stringify(target.context.hintText)}`);
296
+ return parts.join(' | ');
297
+ }
298
+ function normalizeCandidateId(value) {
299
+ return value.trim().toLowerCase();
300
+ }
301
+ export async function rerankDomTargetsForGoal(instruction, targets) {
302
+ if (targets.length === 0) {
303
+ return [];
304
+ }
305
+ const gateway = resolveAgentpayGatewayConfig();
306
+ const client = new AgentpayStagehandLlmClient(gateway);
307
+ const candidates = diversifyCandidates(targets);
308
+ const prompt = [
309
+ 'You are choosing from already discovered visible actionable candidates on a webpage.',
310
+ 'Select only the candidate IDs that directly satisfy the user goal.',
311
+ 'Use container/landmark/layout/state information to disambiguate similar labels.',
312
+ 'Prefer candidates whose surrounding container clearly corroborates the goal.',
313
+ 'Prefer actionable candidates over scope or informational candidates unless the goal explicitly asks for a container or region.',
314
+ 'Prefer direct visible targets over indirect controls when the direct visible target is present.',
315
+ 'Prefer candidates that belong to an active local surface (dialog/listbox/popover/card container) over unrelated page-root candidates when both are plausible matches.',
316
+ 'For date cells, seat cells, and other structured-grid targets, use row/column/zone/cell metadata and state to distinguish plausible matches.',
317
+ 'When the goal refers to an input field or typed value, prefer the directly editable input/textarea/select over a non-editable wrapper such as a combobox shell.',
318
+ 'When the goal refers to a specific field or control, an exact disabled/readonly match is still relevant. Prefer the exact field over nearby prerequisite controls and use state/context to indicate that it is gated.',
319
+ 'Prefer candidates whose explicit state matches the goal and avoid candidates whose state contradicts the goal.',
320
+ 'Use visual cues only as supporting evidence when they help distinguish active vs muted targets in the same group.',
321
+ 'When the goal is about setting or choosing a form/search/filter value, prefer the primary form control or open picker inside a form/dialog/combobox over lower informational charts, summaries, or content collections that merely mention similar values.',
322
+ 'When the goal asks for a form or a set of related controls, return the direct controls from the relevant form instead of nearby navigation links or surrounding section headings.',
323
+ 'If a secondary chart/list mirrors the same options as a primary form control, do not select the secondary surface unless the goal explicitly asks for that chart/list.',
324
+ 'Avoid navigation, filter, and summary controls when the goal refers to a concrete content item unless the goal explicitly asks for those controls.',
325
+ 'Do not invent IDs. Return an empty list if nothing clearly matches.',
326
+ '',
327
+ `Goal: ${instruction}`,
328
+ '',
329
+ 'Candidates:',
330
+ ...candidates.map((target, index) => buildCandidateSummary(target, index)),
331
+ ].join('\n');
332
+ const result = await client.createChatCompletion({
333
+ logger: () => { },
334
+ options: {
335
+ messages: [{ role: 'user', content: prompt }],
336
+ response_model: {
337
+ name: 'Observation',
338
+ schema: rerankSchema,
339
+ },
340
+ },
341
+ });
342
+ const selectedIds = new Set(result.data.matches.map((match) => normalizeCandidateId(match.candidateId)));
343
+ return candidates.filter((_, index) => selectedIds.has(`c${index + 1}`));
344
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,wBAAsB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAgD5C"}
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAAA;;GAEG;AAqDH,wBAAsB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CA8F5C"}