@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
@@ -1 +1 @@
1
- {"version":3,"file":"action-executor-helpers.d.ts","sourceRoot":"","sources":["../../src/commands/action-executor-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAgBrD,eAAO,MAAM,wBAAwB,OAAQ,CAAC;AAC9C,eAAO,MAAM,uBAAuB,OAAQ,CAAC;AAkB7C,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAWpE;AAED,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAqB7F;AAED,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAAE,GACjB,OAAO,CAAC,IAAI,CAAC,CAsCf;AAED,wBAAsB,YAAY,CAChC,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,MAAM,EAAE,GACjB,OAAO,CAAC,IAAI,CAAC,CAmBf;AAED,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAAE,GACjB,OAAO,CAAC,MAAM,CAAC,CAOjB;AA2GD,wBAAsB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAE/E;AAED,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAAE,GACjB,OAAO,CAAC;IACT,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,aAAa,EAAE,OAAO,CAAC;CACxB,CAAC,CAqBD;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,MAAM,EACrB,kBAAkB,EAAE,MAAM,GAAG,IAAI,EACjC,QAAQ,EAAE,MAAM,EAAE,GACjB,MAAM,CAkBR;AAED,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,MAAM,EAAE,GACjB,OAAO,CAAC,IAAI,CAAC,CAYf;AAED,wBAAsB,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAarF"}
1
+ {"version":3,"file":"action-executor-helpers.d.ts","sourceRoot":"","sources":["../../src/commands/action-executor-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAiBrD,eAAO,MAAM,wBAAwB,OAAQ,CAAC;AAC9C,eAAO,MAAM,uBAAuB,OAAQ,CAAC;AAgB7C,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAWpE;AAED,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAqB7F;AAED,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAAE,GACjB,OAAO,CAAC,IAAI,CAAC,CAsCf;AAED,wBAAsB,YAAY,CAChC,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,MAAM,EAAE,GACjB,OAAO,CAAC,IAAI,CAAC,CAmBf;AAED,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAAE,GACjB,OAAO,CAAC,MAAM,CAAC,CAOjB;AA6GD,wBAAsB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAE/E;AAED,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAAE,GACjB,OAAO,CAAC;IACT,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,aAAa,EAAE,OAAO,CAAC;CACxB,CAAC,CAqBD;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,MAAM,EACrB,kBAAkB,EAAE,MAAM,GAAG,IAAI,EACjC,QAAQ,EAAE,MAAM,EAAE,GACjB,MAAM,CAkBR;AAED,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,MAAM,EAAE,GACjB,OAAO,CAAC,IAAI,CAAC,CAYf;AAED,wBAAsB,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAarF"}
@@ -1,4 +1,5 @@
1
1
  import { inferComparableValueTypeFromFacts } from '../control-semantics.js';
2
+ import { normalizeStructuredDateValue } from '../date-value-normalization.js';
2
3
  const OVERLAY_DISMISS_SELECTORS = [
3
4
  '[aria-label="Close"]',
4
5
  '[aria-label="Dismiss"]',
@@ -15,7 +16,6 @@ export const LOCATOR_CLICK_TIMEOUT_MS = 1_500;
15
16
  export const LOCATOR_FILL_TIMEOUT_MS = 1_500;
16
17
  const DATE_MASK_PLACEHOLDER_RE = /(?:^|\b)(?:dd|d|дд|д)\s*[./-]\s*(?:mm|m|мм|м)\s*[./-]\s*(?:yyyy|yyy|yy|y|гггг|гг|г)(?:\b|$)/i;
17
18
  const DATE_FIELD_HINT_RE = /\b(?:birth|dob|date|dates|expire|expires|expiry|expir|bday)\b|(?:дата|рожд|срок)/i;
18
- const ISO_DATE_RE = /^(\d{4})-(\d{2})-(\d{2})$/;
19
19
  export function looksLikeOverlayInterference(error) {
20
20
  const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
21
21
  return (message.includes('intercept') ||
@@ -177,18 +177,20 @@ async function readTextEntryMetadata(locator) {
177
177
  };
178
178
  }
179
179
  function normalizeDateLikeValue(value, metadata) {
180
- if (metadata.nativeDateInput) {
180
+ if (!metadata.nativeDateInput && !metadata.maskedDateLike) {
181
181
  return value;
182
182
  }
183
- if (!metadata.maskedDateLike) {
183
+ const normalizedDate = normalizeStructuredDateValue(value);
184
+ if (normalizedDate.kind === 'not_date_like') {
184
185
  return value;
185
186
  }
186
- const isoMatch = value.match(ISO_DATE_RE);
187
- if (!isoMatch) {
188
- return value;
187
+ if (normalizedDate.kind === 'error') {
188
+ throw new Error(normalizedDate.reason);
189
+ }
190
+ if (metadata.nativeDateInput) {
191
+ return normalizedDate.iso;
189
192
  }
190
- const [, year, month, day] = isoMatch;
191
- return `${day}${metadata.dateSeparator}${month}${metadata.dateSeparator}${year}`;
193
+ return `${normalizedDate.day}${metadata.dateSeparator}${normalizedDate.month}${metadata.dateSeparator}${normalizedDate.year}`;
192
194
  }
193
195
  export async function readLocatorCurrentValue(locator) {
194
196
  return typeof locator.inputValue === 'function' ? locator.inputValue().catch(() => '') : '';
@@ -1 +1 @@
1
- {"version":3,"file":"attach.d.ts","sourceRoot":"","sources":["../../src/commands/attach.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAGL,KAAK,mBAAmB,EACxB,KAAK,0BAA0B,EAChC,MAAM,6BAA6B,CAAC;AAErC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAE1E,8DAA8D;AAC9D,eAAO,MAAM,kBAAkB,oCAAqC,CAAC;AAErE,0DAA0D;AAC1D,eAAO,MAAM,oBAAoB,sBAAuB,CAAC;AAEzD,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAClE,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEtE,kEAAkE;AAClE,MAAM,MAAM,aAAa,GAAG;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,0BAA0B,CAAC;IAC1C,SAAS,CAAC,EAAE,sBAAsB,CAAC;CACpC,CAAC;AAEF,oEAAoE;AACpE,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,IAAI,CAAC;IACd,OAAO,EAAE,UAAU,CAAC;IACpB,OAAO,EAAE,mBAAmB,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB,EAAE,OAAO,CAAC;CAC9B,CAAC;AAEF,+DAA+D;AAC/D,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,EAAE,eAAe,CAAC;IACvB,WAAW,EAAE,iBAAiB,CAAC;IAC/B,OAAO,EAAE,wBAAwB,CAAC;IAClC,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,mBAAmB,GAAG,mBAAmB,CAAC;AAErE,wFAAwF;AACxF,wBAAsB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,YAAY,CAAC,CA2C/F"}
1
+ {"version":3,"file":"attach.d.ts","sourceRoot":"","sources":["../../src/commands/attach.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAGL,KAAK,mBAAmB,EACxB,KAAK,0BAA0B,EAChC,MAAM,6BAA6B,CAAC;AAErC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAG1E,8DAA8D;AAC9D,eAAO,MAAM,kBAAkB,oCAAqC,CAAC;AAErE,0DAA0D;AAC1D,eAAO,MAAM,oBAAoB,sBAAuB,CAAC;AAEzD,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAClE,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEtE,kEAAkE;AAClE,MAAM,MAAM,aAAa,GAAG;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,0BAA0B,CAAC;IAC1C,SAAS,CAAC,EAAE,sBAAsB,CAAC;CACpC,CAAC;AAEF,oEAAoE;AACpE,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,IAAI,CAAC;IACd,OAAO,EAAE,UAAU,CAAC;IACpB,OAAO,EAAE,mBAAmB,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB,EAAE,OAAO,CAAC;CAC9B,CAAC;AAEF,+DAA+D;AAC/D,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,EAAE,eAAe,CAAC;IACvB,WAAW,EAAE,iBAAiB,CAAC;IAC/B,OAAO,EAAE,wBAAwB,CAAC;IAClC,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,mBAAmB,GAAG,mBAAmB,CAAC;AAErE,wFAAwF;AACxF,wBAAsB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,YAAY,CAAC,CAsC/F"}
@@ -2,14 +2,14 @@
2
2
  * attach <cdp-url> — Attach AgentBrowse to an existing browser session.
3
3
  */
4
4
  import { buildAttachedSession, buildCdpHttpEndpointUrl, } from '../browser-session-state.js';
5
- import { connectPlaywright, disconnectPlaywright, syncLaunchPage } from '../playwright-runtime.js';
5
+ import { syncLaunchPage } from '../playwright-runtime.js';
6
+ import { initializeBrowserSessionOwner, withStickyOwnerBrowser } from '../sticky-owner.js';
6
7
  /** Stable top-level error codes returned by `attach(...)`. */
7
8
  export const ATTACH_ERROR_CODES = ['browser_attach_failed'];
8
9
  /** Stable outcome categories emitted by `attach(...)`. */
9
10
  export const ATTACH_OUTCOME_TYPES = ['blocked'];
10
11
  /** Attaches AgentBrowse to an existing browser via a CDP websocket or HTTP endpoint. */
11
12
  export async function attach(cdpUrl, options = {}) {
12
- let browser = null;
13
13
  try {
14
14
  const normalizedCdpUrl = await resolveAttachEndpoint(cdpUrl);
15
15
  const session = buildAttachedSession({
@@ -19,11 +19,11 @@ export async function attach(cdpUrl, options = {}) {
19
19
  ...(options.capabilities ? { capabilities: options.capabilities } : {}),
20
20
  ...(options.transport ? { transport: options.transport } : {}),
21
21
  });
22
- browser = await connectPlaywright(normalizedCdpUrl);
23
- const syncedPage = await syncLaunchPage(session, browser, {
22
+ await initializeBrowserSessionOwner(session);
23
+ const syncedPage = await withStickyOwnerBrowser(session, (browser) => syncLaunchPage(session, browser, {
24
24
  fallbackUrl: '',
25
25
  fallbackTitle: '',
26
- });
26
+ }));
27
27
  return {
28
28
  success: true,
29
29
  runtime: 'attached',
@@ -44,11 +44,6 @@ export async function attach(cdpUrl, options = {}) {
44
44
  reason: formatUnknownError(error),
45
45
  };
46
46
  }
47
- finally {
48
- if (browser) {
49
- await disconnectPlaywright(browser);
50
- }
51
- }
52
47
  }
53
48
  async function resolveAttachEndpoint(input) {
54
49
  const normalized = input.trim();
@@ -0,0 +1,9 @@
1
+ export declare const STICKY_OWNER_UNRECOVERABLE_REASON = "sticky_owner_unrecoverable";
2
+ export declare function describeBrowserConnectionFailure(error: unknown, options: {
3
+ defaultMessage: string;
4
+ unrecoverableSessionMessage: string;
5
+ }): {
6
+ message: string;
7
+ reason: string;
8
+ };
9
+ //# sourceMappingURL=browser-connection-failure.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-connection-failure.d.ts","sourceRoot":"","sources":["../../src/commands/browser-connection-failure.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iCAAiC,+BAA+B,CAAC;AAK9E,wBAAgB,gCAAgC,CAC9C,KAAK,EAAE,OAAO,EACd,OAAO,EAAE;IACP,cAAc,EAAE,MAAM,CAAC;IACvB,2BAA2B,EAAE,MAAM,CAAC;CACrC,GACA;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAcrC"}
@@ -0,0 +1,15 @@
1
+ export const STICKY_OWNER_UNRECOVERABLE_REASON = 'sticky_owner_unrecoverable';
2
+ const STICKY_OWNER_UNRECOVERABLE_PUBLIC_REASON = 'The previous browser session is no longer reachable, so AgentBrowse could not restore control of the browser. Launch or attach a new browser session and try again.';
3
+ export function describeBrowserConnectionFailure(error, options) {
4
+ const reason = error instanceof Error ? error.message : String(error);
5
+ if (reason === STICKY_OWNER_UNRECOVERABLE_REASON) {
6
+ return {
7
+ message: options.unrecoverableSessionMessage,
8
+ reason: STICKY_OWNER_UNRECOVERABLE_PUBLIC_REASON,
9
+ };
10
+ }
11
+ return {
12
+ message: options.defaultMessage,
13
+ reason,
14
+ };
15
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"browser-status.d.ts","sourceRoot":"","sources":["../../src/commands/browser-status.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAIL,KAAK,mBAAmB,EACzB,MAAM,6BAA6B,CAAC;AAoBrC,KAAK,mBAAmB,GAAG;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,QAAQ,GAAG,gBAAgB,CAAC;CAC5C,CAAC;AAEF,0DAA0D;AAC1D,eAAO,MAAM,4BAA4B,gFAI/B,CAAC;AAEX,MAAM,MAAM,wBAAwB,GAAG,CAAC,OAAO,4BAA4B,CAAC,CAAC,MAAM,CAAC,CAAC;AACrF,MAAM,MAAM,2BAA2B,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAClE,MAAM,MAAM,gCAAgC,GAAG,mBAAmB,CAAC;AAEnE,uGAAuG;AACvG,MAAM,MAAM,oCAAoC,GAAG;IACjD,OAAO,EAAE,IAAI,CAAC;IACd,KAAK,EAAE,IAAI,CAAC;IACZ,WAAW,EAAE,OAAO,CAAC,wBAAwB,EAAE,2BAA2B,CAAC,CAAC;IAC5E,OAAO,CAAC,EAAE,2BAA2B,CAAC;IACtC,mBAAmB,CAAC,EAAE,gCAAgC,CAAC;IACvD,uBAAuB,EAAE,IAAI,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B,CAAC;AAEF,qGAAqG;AACrG,MAAM,MAAM,wBAAwB,GAAG;IACrC,OAAO,EAAE,IAAI,CAAC;IACd,KAAK,EAAE,IAAI,CAAC;IACZ,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,2BAA2B,CAAC;IACtC,mBAAmB,CAAC,EAAE,gCAAgC,CAAC;IACvD,qBAAqB,CAAC,EAAE,IAAI,CAAC;CAC9B,CAAC;AAEF,kEAAkE;AAClE,MAAM,MAAM,6BAA6B,GAAG;IAC1C,OAAO,EAAE,IAAI,CAAC;IACd,KAAK,EAAE,KAAK,CAAC;IACb,OAAO,CAAC,EAAE,2BAA2B,CAAC;IACtC,qBAAqB,CAAC,EAAE,IAAI,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAC3B,oCAAoC,GACpC,wBAAwB,GACxB,6BAA6B,CAAC;AAuLlC,qFAAqF;AACrF,wBAAsB,aAAa,CACjC,OAAO,EAAE,mBAAmB,GAAG,IAAI,GAClC,OAAO,CAAC,mBAAmB,CAAC,CAgG9B"}
1
+ {"version":3,"file":"browser-status.d.ts","sourceRoot":"","sources":["../../src/commands/browser-status.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAIL,KAAK,mBAAmB,EACzB,MAAM,6BAA6B,CAAC;AAiBrC,KAAK,mBAAmB,GAAG;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,QAAQ,GAAG,gBAAgB,CAAC;CAC5C,CAAC;AAEF,0DAA0D;AAC1D,eAAO,MAAM,4BAA4B,gFAI/B,CAAC;AAEX,MAAM,MAAM,wBAAwB,GAAG,CAAC,OAAO,4BAA4B,CAAC,CAAC,MAAM,CAAC,CAAC;AACrF,MAAM,MAAM,2BAA2B,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAClE,MAAM,MAAM,gCAAgC,GAAG,mBAAmB,CAAC;AAEnE,uGAAuG;AACvG,MAAM,MAAM,oCAAoC,GAAG;IACjD,OAAO,EAAE,IAAI,CAAC;IACd,KAAK,EAAE,IAAI,CAAC;IACZ,WAAW,EAAE,OAAO,CAAC,wBAAwB,EAAE,2BAA2B,CAAC,CAAC;IAC5E,OAAO,CAAC,EAAE,2BAA2B,CAAC;IACtC,mBAAmB,CAAC,EAAE,gCAAgC,CAAC;IACvD,uBAAuB,EAAE,IAAI,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B,CAAC;AAEF,qGAAqG;AACrG,MAAM,MAAM,wBAAwB,GAAG;IACrC,OAAO,EAAE,IAAI,CAAC;IACd,KAAK,EAAE,IAAI,CAAC;IACZ,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,2BAA2B,CAAC;IACtC,mBAAmB,CAAC,EAAE,gCAAgC,CAAC;IACvD,qBAAqB,CAAC,EAAE,IAAI,CAAC;CAC9B,CAAC;AAEF,kEAAkE;AAClE,MAAM,MAAM,6BAA6B,GAAG;IAC1C,OAAO,EAAE,IAAI,CAAC;IACd,KAAK,EAAE,KAAK,CAAC;IACb,OAAO,CAAC,EAAE,2BAA2B,CAAC;IACtC,qBAAqB,CAAC,EAAE,IAAI,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAC3B,oCAAoC,GACpC,wBAAwB,GACxB,6BAA6B,CAAC;AAmLlC,qFAAqF;AACrF,wBAAsB,aAAa,CACjC,OAAO,EAAE,mBAAmB,GAAG,IAAI,GAClC,OAAO,CAAC,mBAAmB,CAAC,CAgG9B"}
@@ -2,9 +2,10 @@
2
2
  * browse browser-status — Check live browser/page/runtime state.
3
3
  */
4
4
  import { buildCdpHttpEndpointUrl, getSessionPort, supportsCaptchaSolve, } from '../browser-session-state.js';
5
- import { connectPlaywright, disconnectPlaywright, resolveCurrentPageContext, } from '../playwright-runtime.js';
5
+ import { resolveCurrentPageContext } from '../playwright-runtime.js';
6
6
  import { captureDiagnosticSnapshotBestEffort, finishDiagnosticStepBestEffort, recordCommandLifecycleEventBestEffort, startDiagnosticStep, } from '../diagnostics.js';
7
7
  import { scrubProtectedExactValues } from '../secrets/protected-exact-value-redaction.js';
8
+ import { withStickyOwnerBrowser } from '../sticky-owner.js';
8
9
  /** Stable outcome categories exposed by `status(...)`. */
9
10
  export const BROWSER_STATUS_OUTCOME_TYPES = [
10
11
  'browser_alive',
@@ -56,40 +57,35 @@ function buildProtectedStatusPayload(params) {
56
57
  };
57
58
  }
58
59
  async function readCanonicalStatus(session) {
59
- let browser = null;
60
60
  try {
61
- browser = await connectPlaywright(session.cdpUrl);
62
- const resolved = await resolveCurrentPageContext(browser, session);
63
- const persisted = session.runtime?.currentPageRef;
64
- const title = (await resolved.page
65
- .title()
66
- .catch(() => session.runtime?.pages[resolved.pageRef]?.title ?? '')) ||
67
- session.runtime?.pages[resolved.pageRef]?.title ||
68
- 'unknown';
69
- const url = resolved.page.url() || session.runtime?.pages[resolved.pageRef]?.url || 'unknown';
70
- return {
71
- pageRef: resolved.pageRef,
72
- url,
73
- title,
74
- ...(persisted && (persisted !== resolved.pageRef || resolved.recoveredVia)
75
- ? {
76
- currentPageMismatch: {
77
- persistedPageRef: persisted,
78
- ...(persisted !== resolved.pageRef ? { livePageRef: resolved.pageRef } : {}),
79
- ...(resolved.recoveredVia ? { recoveredVia: resolved.recoveredVia } : {}),
80
- },
81
- }
82
- : {}),
83
- };
61
+ return await withStickyOwnerBrowser(session, async (browser) => {
62
+ const resolved = await resolveCurrentPageContext(browser, session);
63
+ const persisted = session.runtime?.currentPageRef;
64
+ const title = (await resolved.page
65
+ .title()
66
+ .catch(() => session.runtime?.pages[resolved.pageRef]?.title ?? '')) ||
67
+ session.runtime?.pages[resolved.pageRef]?.title ||
68
+ 'unknown';
69
+ const url = resolved.page.url() || session.runtime?.pages[resolved.pageRef]?.url || 'unknown';
70
+ return {
71
+ pageRef: resolved.pageRef,
72
+ url,
73
+ title,
74
+ ...(persisted && (persisted !== resolved.pageRef || resolved.recoveredVia)
75
+ ? {
76
+ currentPageMismatch: {
77
+ persistedPageRef: persisted,
78
+ ...(persisted !== resolved.pageRef ? { livePageRef: resolved.pageRef } : {}),
79
+ ...(resolved.recoveredVia ? { recoveredVia: resolved.recoveredVia } : {}),
80
+ },
81
+ }
82
+ : {}),
83
+ };
84
+ });
84
85
  }
85
86
  catch {
86
87
  return null;
87
88
  }
88
- finally {
89
- if (browser) {
90
- await disconnectPlaywright(browser);
91
- }
92
- }
93
89
  }
94
90
  function buildStatusEndpointUrl(session, port, resourcePath) {
95
91
  if (session) {
@@ -1 +1 @@
1
- {"version":3,"file":"click-activation-policy.d.ts","sourceRoot":"","sources":["../../src/commands/click-activation-policy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,MAAM,MAAM,uBAAuB,GAAG,SAAS,GAAG,KAAK,CAAC;AAExD,wBAAgB,gCAAgC,CAC9C,MAAM,EAAE,IAAI,CAAC,gBAAgB,EAAE,MAAM,GAAG,WAAW,GAAG,WAAW,CAAC,EAClE,MAAM,EAAE,YAAY,GACnB,uBAAuB,CAezB"}
1
+ {"version":3,"file":"click-activation-policy.d.ts","sourceRoot":"","sources":["../../src/commands/click-activation-policy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,MAAM,MAAM,uBAAuB,GAAG,SAAS,GAAG,KAAK,CAAC;AAExD,wBAAgB,gCAAgC,CAC9C,MAAM,EAAE,IAAI,CAAC,gBAAgB,EAAE,MAAM,GAAG,WAAW,GAAG,WAAW,CAAC,EAClE,MAAM,EAAE,YAAY,GACnB,uBAAuB,CAoBzB"}
@@ -2,12 +2,16 @@ export function clickActivationStrategyForTarget(target, action) {
2
2
  if (action !== 'click') {
3
3
  return 'pointer';
4
4
  }
5
+ const kind = target.kind?.trim().toLowerCase();
6
+ const role = target.semantics?.role?.trim().toLowerCase();
7
+ const presentationalAffordance = kind === 'presentation' || role === 'presentation';
8
+ if (presentationalAffordance) {
9
+ return 'dom';
10
+ }
5
11
  const withinIframe = Boolean(target.framePath?.length);
6
12
  if (!withinIframe) {
7
13
  return 'pointer';
8
14
  }
9
- const kind = target.kind?.trim().toLowerCase();
10
- const role = target.semantics?.role?.trim().toLowerCase();
11
15
  const isTab = kind === 'tab' || role === 'tab';
12
16
  return isTab ? 'dom' : 'pointer';
13
17
  }
@@ -1 +1 @@
1
- {"version":3,"file":"close.d.ts","sourceRoot":"","sources":["../../src/commands/close.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAkB,KAAK,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAWvF,6DAA6D;AAC7D,eAAO,MAAM,iBAAiB,mCAAoC,CAAC;AAEnE,yDAAyD;AACzD,eAAO,MAAM,mBAAmB,gEAAiE,CAAC;AAElG,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAChE,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEpE,uCAAuC;AACvC,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,EAAE,IAAI,CAAC;CACf,CAAC;AAEF,mCAAmC;AACnC,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,EAAE,cAAc,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC,gBAAgB,EAAE,SAAS,CAAC,CAAC;IAClD,OAAO,EAAE,uBAAuB,CAAC;IACjC,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,kBAAkB,GAAG,kBAAkB,CAAC;AAsClE,sFAAsF;AACtF,wBAAsB,KAAK,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC,CAsErF"}
1
+ {"version":3,"file":"close.d.ts","sourceRoot":"","sources":["../../src/commands/close.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAkB,KAAK,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAYvF,6DAA6D;AAC7D,eAAO,MAAM,iBAAiB,mCAAoC,CAAC;AAEnE,yDAAyD;AACzD,eAAO,MAAM,mBAAmB,gEAAiE,CAAC;AAElG,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAChE,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEpE,uCAAuC;AACvC,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,EAAE,IAAI,CAAC;CACf,CAAC;AAEF,mCAAmC;AACnC,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,EAAE,cAAc,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC,gBAAgB,EAAE,SAAS,CAAC,CAAC;IAClD,OAAO,EAAE,uBAAuB,CAAC;IACjC,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,kBAAkB,GAAG,kBAAkB,CAAC;AAsClE,sFAAsF;AACtF,wBAAsB,KAAK,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC,CAyErF"}
@@ -6,6 +6,7 @@ import { captureDiagnosticSnapshotBestEffort, finishDiagnosticStepBestEffort, re
6
6
  import { info } from '../output.js';
7
7
  import { closeOwnedBrowser } from '../owned-browser.js';
8
8
  import { isManagedBrowserPid } from '../owned-process.js';
9
+ import { terminateBrowserSessionOwner } from '../sticky-owner.js';
9
10
  /** Stable top-level error codes returned by `close(...)`. */
10
11
  export const CLOSE_ERROR_CODES = ['browser_close_failed'];
11
12
  /** Stable outcome categories emitted by `close(...)`. */
@@ -64,6 +65,7 @@ export async function close(session) {
64
65
  },
65
66
  });
66
67
  if (isCloseableManagedSession(session)) {
68
+ await terminateBrowserSessionOwner(session);
67
69
  const closeResult = await closeOwnedBrowser(session);
68
70
  if (!closeResult.success) {
69
71
  recordCommandLifecycleEventBestEffort({
@@ -90,6 +92,9 @@ export async function close(session) {
90
92
  };
91
93
  }
92
94
  }
95
+ else if (session?.stickyOwner) {
96
+ await terminateBrowserSessionOwner(session);
97
+ }
93
98
  recordCommandLifecycleEventBestEffort({
94
99
  step: closeStep,
95
100
  phase: 'completed',
@@ -1 +1 @@
1
- {"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../../src/commands/extract.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AAUzE,OAAO,EAIL,KAAK,YAAY,EAClB,MAAM,cAAc,CAAC;AAsBtB,KAAK,sBAAsB,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE9D,+EAA+E;AAC/E,MAAM,MAAM,kBAAkB,GAC1B,sBAAsB,GACtB,uBAAuB,GACvB,kBAAkB,EAAE,CAAC;AAEzB,6EAA6E;AAC7E,MAAM,WAAW,uBAAuB;IACtC,CAAC,GAAG,EAAE,MAAM,GAAG,kBAAkB,CAAC;CACnC;AAED,4DAA4D;AAC5D,MAAM,MAAM,kBAAkB,GAAG,uBAAuB,GAAG,CAAC,CAAC,UAAU,CAAC;AAExE,+DAA+D;AAC/D,eAAO,MAAM,mBAAmB,8MAStB,CAAC;AAEX,2DAA2D;AAC3D,eAAO,MAAM,qBAAqB,8EAKxB,CAAC;AAEX,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AACpE,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC;AAExE,+CAA+C;AAC/C,MAAM,MAAM,oBAAoB,GAAG,YAAY,GAAG;IAChD,OAAO,EAAE,IAAI,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,KAAK,qBAAqB,GAAG;IAC3B,KAAK,EAAE,gBAAgB,CAAC;IACxB,WAAW,EAAE,OAAO,CAAC,kBAAkB,EAAE,eAAe,GAAG,SAAS,GAAG,aAAa,CAAC,CAAC;IACtF,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,2CAA2C;AAC3C,MAAM,MAAM,oBAAoB,GAAG;IAAE,OAAO,EAAE,KAAK,CAAA;CAAE,GAAG,qBAAqB,CAAC;AAE9E,MAAM,MAAM,aAAa,GAAG,oBAAoB,GAAG,oBAAoB,CAAC;AAibxE;;;;GAIG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,qBAAqB,EAC9B,WAAW,EAAE,kBAAkB,EAC/B,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,aAAa,CAAC,CA8RxB;AAED,sFAAsF;AACtF,wBAAsB,OAAO,CAC3B,OAAO,EAAE,qBAAqB,EAC9B,WAAW,EAAE,MAAM,GAAG,kBAAkB,EACxC,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAgCf"}
1
+ {"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../../src/commands/extract.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AAUzE,OAAO,EAIL,KAAK,YAAY,EAClB,MAAM,cAAc,CAAC;AAsBtB,KAAK,sBAAsB,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE9D,+EAA+E;AAC/E,MAAM,MAAM,kBAAkB,GAC1B,sBAAsB,GACtB,uBAAuB,GACvB,kBAAkB,EAAE,CAAC;AAEzB,6EAA6E;AAC7E,MAAM,WAAW,uBAAuB;IACtC,CAAC,GAAG,EAAE,MAAM,GAAG,kBAAkB,CAAC;CACnC;AAED,4DAA4D;AAC5D,MAAM,MAAM,kBAAkB,GAAG,uBAAuB,GAAG,CAAC,CAAC,UAAU,CAAC;AAExE,+DAA+D;AAC/D,eAAO,MAAM,mBAAmB,8MAStB,CAAC;AAEX,2DAA2D;AAC3D,eAAO,MAAM,qBAAqB,8EAKxB,CAAC;AAEX,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AACpE,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC;AAExE,+CAA+C;AAC/C,MAAM,MAAM,oBAAoB,GAAG,YAAY,GAAG;IAChD,OAAO,EAAE,IAAI,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,KAAK,qBAAqB,GAAG;IAC3B,KAAK,EAAE,gBAAgB,CAAC;IACxB,WAAW,EAAE,OAAO,CAAC,kBAAkB,EAAE,eAAe,GAAG,SAAS,GAAG,aAAa,CAAC,CAAC;IACtF,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,2CAA2C;AAC3C,MAAM,MAAM,oBAAoB,GAAG;IAAE,OAAO,EAAE,KAAK,CAAA;CAAE,GAAG,qBAAqB,CAAC;AAE9E,MAAM,MAAM,aAAa,GAAG,oBAAoB,GAAG,oBAAoB,CAAC;AAibxE;;;;GAIG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,qBAAqB,EAC9B,WAAW,EAAE,kBAAkB,EAC/B,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,aAAa,CAAC,CAgSxB;AAED,sFAAsF;AACtF,wBAAsB,OAAO,CAC3B,OAAO,EAAE,qBAAqB,EAC9B,WAAW,EAAE,MAAM,GAAG,kBAAkB,EACxC,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAgCf"}
@@ -8,12 +8,14 @@ import { getSurface, getTarget, markSurfaceLifecycle, markTargetLifecycle, } fro
8
8
  import { getPageScopeEpoch, setCurrentPage } from '../runtime-page-state.js';
9
9
  import { outputContractFailure, outputJSON, } from '../output.js';
10
10
  import { captureDiagnosticSnapshotBestEffort, finishDiagnosticStepBestEffort, recordCommandLifecycleEventBestEffort, startDiagnosticStep, } from '../diagnostics.js';
11
- import { connectPlaywright, disconnectPlaywright, resolveCurrentPageContext, resolvePageByRef as resolvePlaywrightPageByRef, syncSessionPage, } from '../playwright-runtime.js';
11
+ import { resolveCurrentPageContext, resolvePageByRef as resolvePlaywrightPageByRef, syncSessionPage, } from '../playwright-runtime.js';
12
12
  import { withApiTraceContext } from '../command-api-tracing.js';
13
13
  import { normalizePageSignature } from './descriptor-validation.js';
14
14
  import { readScopedDialogText } from './extract-scoped-dialog-text.js';
15
15
  import { resolveScopedExtractContext } from './extract-scope-resolution.js';
16
16
  import { executeStagehandExtract } from './extract-stagehand-executor.js';
17
+ import { describeBrowserConnectionFailure } from './browser-connection-failure.js';
18
+ import { withStickyOwnerBrowser } from '../sticky-owner.js';
17
19
  /** Stable top-level error codes returned by `extract(...)`. */
18
20
  export const EXTRACT_ERROR_CODES = [
19
21
  'browser_connection_failed',
@@ -459,180 +461,181 @@ export async function extractBrowser(session, schemaInput, scopeRef) {
459
461
  scopeRef,
460
462
  });
461
463
  }
462
- let browser = null;
463
464
  let failureMessage = null;
464
465
  let cleanupScopedExtract = null;
465
466
  let staleScope = false;
466
467
  let staleReason = null;
467
468
  try {
468
- browser = await connectPlaywright(session.cdpUrl);
469
- }
470
- catch (err) {
471
- return buildExtractContractFailureResult(session, {
472
- step: extractStep,
473
- error: 'browser_connection_failed',
474
- outcomeType: 'blocked',
475
- message: 'Extraction could not start because AgentBrowse failed to connect to the browser.',
476
- reason: err instanceof Error ? err.message : String(err),
477
- scopeRef,
478
- pageRef,
479
- });
480
- }
481
- try {
482
- let sourcePage;
483
- if (scopeTarget) {
484
- sourcePage = await resolvePlaywrightPageByRef(browser, session, pageRef);
485
- }
486
- else {
487
- const resolvedPage = await resolveCurrentPageContext(browser, session);
488
- pageRef = resolvedPage.pageRef;
489
- sourcePage = resolvedPage.page;
490
- }
491
- let page = sourcePage;
492
- let scopedResolution = null;
493
- const { url, title } = await syncSessionPage(session, pageRef, sourcePage);
494
- if (scopeTarget?.pageSignature &&
495
- normalizePageSignature(url) !== scopeTarget.pageSignature) {
496
- staleScope = true;
497
- staleReason = 'page-signature-mismatch';
498
- throw new Error('stale_scope_target_page_signature_changed');
499
- }
500
- let effectiveSelector;
501
- if (scopeTarget) {
469
+ return await withStickyOwnerBrowser(session, async (browser) => {
502
470
  try {
503
- scopedResolution = await resolveScopedExtractContext({
504
- page: sourcePage,
505
- scopeTarget,
506
- validateDomSignature: Boolean(targetScope),
471
+ let sourcePage;
472
+ if (scopeTarget) {
473
+ sourcePage = await resolvePlaywrightPageByRef(browser, session, pageRef);
474
+ }
475
+ else {
476
+ const resolvedPage = await resolveCurrentPageContext(browser, session);
477
+ pageRef = resolvedPage.pageRef;
478
+ sourcePage = resolvedPage.page;
479
+ }
480
+ let page = sourcePage;
481
+ let scopedResolution = null;
482
+ const { url, title } = await syncSessionPage(session, pageRef, sourcePage);
483
+ if (scopeTarget?.pageSignature &&
484
+ normalizePageSignature(url) !== scopeTarget.pageSignature) {
485
+ staleScope = true;
486
+ staleReason = 'page-signature-mismatch';
487
+ throw new Error('stale_scope_target_page_signature_changed');
488
+ }
489
+ let effectiveSelector;
490
+ if (scopeTarget) {
491
+ try {
492
+ scopedResolution = await resolveScopedExtractContext({
493
+ page: sourcePage,
494
+ scopeTarget,
495
+ validateDomSignature: Boolean(targetScope),
496
+ });
497
+ cleanupScopedExtract = scopedResolution.cleanup;
498
+ page = scopedResolution.page;
499
+ effectiveSelector = scopedResolution.selector;
500
+ }
501
+ catch (error) {
502
+ if (error instanceof Error &&
503
+ error.message === 'stale_scope_target_dom_signature_changed') {
504
+ staleScope = true;
505
+ staleReason = 'dom-signature-mismatch';
506
+ }
507
+ else if (surfaceScope &&
508
+ surfaceExtractScopeLifetime(surfaceScope) === 'snapshot' &&
509
+ error instanceof Error &&
510
+ error.message === 'scope_target_unresolvable') {
511
+ return buildExtractContractFailureResult(session, {
512
+ step: extractStep,
513
+ error: 'expired_extract_scope',
514
+ outcomeType: 'binding_stale',
515
+ message: 'Extraction failed because the requested snapshot scope expired before it could be rebound.',
516
+ reason: `Snapshot scope ${scopeRef} is no longer present in the current visible page state.`,
517
+ scopeRef,
518
+ pageRef,
519
+ staleScope: true,
520
+ staleReason: 'snapshot-scope-expired',
521
+ });
522
+ }
523
+ throw error;
524
+ }
525
+ }
526
+ setCurrentPage(session, pageRef);
527
+ const execution = await executeStagehandExtract({
528
+ session,
529
+ instruction,
530
+ schema: normalizedSchema.schema,
531
+ page,
532
+ selector: effectiveSelector,
533
+ degradationReason: scopedResolution?.degraded
534
+ ? scopedResolution.degradationReason
535
+ : undefined,
536
+ });
537
+ let data = execution.data;
538
+ if (scopeTarget &&
539
+ effectiveSelector &&
540
+ normalizedSchema.requestsScopedDialogText &&
541
+ data &&
542
+ typeof data === 'object' &&
543
+ !Array.isArray(data)) {
544
+ const dialogText = await readScopedDialogText(page, effectiveSelector);
545
+ if (typeof dialogText === 'string' && dialogText.trim().length > 0) {
546
+ data = {
547
+ ...data,
548
+ dialog_text: dialogText,
549
+ };
550
+ }
551
+ }
552
+ return buildExtractSuccessResult(session, extractStep, {
553
+ success: true,
554
+ ...execution,
555
+ data,
556
+ pageRef,
557
+ scopeRef,
558
+ metrics: session.runtime?.metrics,
559
+ url,
560
+ title,
507
561
  });
508
- cleanupScopedExtract = scopedResolution.cleanup;
509
- page = scopedResolution.page;
510
- effectiveSelector = scopedResolution.selector;
511
562
  }
512
- catch (error) {
513
- if (error instanceof Error &&
514
- error.message === 'stale_scope_target_dom_signature_changed') {
515
- staleScope = true;
516
- staleReason = 'dom-signature-mismatch';
563
+ catch (err) {
564
+ if (staleScope && scopeRef) {
565
+ if (targetScope) {
566
+ markTargetLifecycle(session, scopeRef, 'stale', staleReason ?? 'unknown');
567
+ }
568
+ else if (surfaceScope) {
569
+ markSurfaceLifecycle(session, scopeRef, 'stale', staleReason ?? 'unknown');
570
+ }
517
571
  }
518
- else if (surfaceScope &&
519
- surfaceExtractScopeLifetime(surfaceScope) === 'snapshot' &&
520
- error instanceof Error &&
521
- error.message === 'scope_target_unresolvable') {
572
+ if (!staleScope && err instanceof AssistiveStructuredOutputTruncatedError) {
522
573
  return buildExtractContractFailureResult(session, {
523
574
  step: extractStep,
524
- error: 'expired_extract_scope',
525
- outcomeType: 'binding_stale',
526
- message: 'Extraction failed because the requested snapshot scope expired before it could be rebound.',
527
- reason: `Snapshot scope ${scopeRef} is no longer present in the current visible page state.`,
575
+ error: 'extract_output_truncated',
576
+ outcomeType: 'blocked',
577
+ message: 'Extraction failed because the provider truncated structured output.',
578
+ reason: buildTruncationReason(err),
528
579
  scopeRef,
529
580
  pageRef,
530
- staleScope: true,
531
- staleReason: 'snapshot-scope-expired',
581
+ staleScope: false,
582
+ provider: err.provider,
583
+ model: err.model,
584
+ finishReason: err.finishReason,
585
+ maxOutputTokens: err.maxOutputTokens,
586
+ completionTokens: err.completionTokens,
532
587
  });
533
588
  }
534
- throw error;
535
- }
536
- }
537
- setCurrentPage(session, pageRef);
538
- const execution = await executeStagehandExtract({
539
- session,
540
- instruction,
541
- schema: normalizedSchema.schema,
542
- page,
543
- selector: effectiveSelector,
544
- degradationReason: scopedResolution?.degraded
545
- ? scopedResolution.degradationReason
546
- : undefined,
547
- });
548
- let data = execution.data;
549
- if (scopeTarget &&
550
- effectiveSelector &&
551
- normalizedSchema.requestsScopedDialogText &&
552
- data &&
553
- typeof data === 'object' &&
554
- !Array.isArray(data)) {
555
- const dialogText = await readScopedDialogText(page, effectiveSelector);
556
- if (typeof dialogText === 'string' && dialogText.trim().length > 0) {
557
- data = {
558
- ...data,
559
- dialog_text: dialogText,
560
- };
589
+ failureMessage = `Extract failed: ${err instanceof Error ? err.message : String(err)}`;
561
590
  }
562
- }
563
- return buildExtractSuccessResult(session, extractStep, {
564
- success: true,
565
- ...execution,
566
- data,
567
- pageRef,
568
- scopeRef,
569
- metrics: session.runtime?.metrics,
570
- url,
571
- title,
572
- });
573
- }
574
- catch (err) {
575
- if (staleScope && scopeRef) {
576
- if (targetScope) {
577
- markTargetLifecycle(session, scopeRef, 'stale', staleReason ?? 'unknown');
591
+ finally {
592
+ if (cleanupScopedExtract) {
593
+ await cleanupScopedExtract().catch(() => undefined);
594
+ }
578
595
  }
579
- else if (surfaceScope) {
580
- markSurfaceLifecycle(session, scopeRef, 'stale', staleReason ?? 'unknown');
596
+ if (failureMessage) {
597
+ return buildExtractContractFailureResult(session, {
598
+ step: extractStep,
599
+ error: staleScope ? 'stale_extract_scope' : 'extract_failed',
600
+ outcomeType: staleScope ? 'binding_stale' : 'blocked',
601
+ message: staleScope
602
+ ? 'Extraction failed because the requested scope became stale.'
603
+ : 'Extraction failed.',
604
+ reason: staleScope && scopeRef
605
+ ? `${failureMessage} (${scopeRef} marked stale: ${staleReason ?? 'stale'})`
606
+ : failureMessage.replace(/^Extract failed:\s*/, ''),
607
+ scopeRef,
608
+ pageRef,
609
+ staleScope,
610
+ staleReason: staleReason ?? undefined,
611
+ });
581
612
  }
582
- }
583
- if (!staleScope && err instanceof AssistiveStructuredOutputTruncatedError) {
584
613
  return buildExtractContractFailureResult(session, {
585
614
  step: extractStep,
586
- error: 'extract_output_truncated',
615
+ error: 'extract_failed',
587
616
  outcomeType: 'blocked',
588
- message: 'Extraction failed because the provider truncated structured output.',
589
- reason: buildTruncationReason(err),
617
+ message: 'Extraction failed.',
618
+ reason: 'Extraction did not produce a success or failure result.',
590
619
  scopeRef,
591
620
  pageRef,
592
- staleScope: false,
593
- provider: err.provider,
594
- model: err.model,
595
- finishReason: err.finishReason,
596
- maxOutputTokens: err.maxOutputTokens,
597
- completionTokens: err.completionTokens,
598
621
  });
599
- }
600
- failureMessage = `Extract failed: ${err instanceof Error ? err.message : String(err)}`;
601
- }
602
- finally {
603
- if (cleanupScopedExtract) {
604
- await cleanupScopedExtract().catch(() => undefined);
605
- }
606
- if (browser) {
607
- await disconnectPlaywright(browser);
608
- }
622
+ });
609
623
  }
610
- if (failureMessage) {
624
+ catch (err) {
625
+ const browserConnectionFailure = describeBrowserConnectionFailure(err, {
626
+ defaultMessage: 'Extraction could not start because AgentBrowse failed to connect to the browser.',
627
+ unrecoverableSessionMessage: 'Extraction could not start because the previous browser session is no longer reachable.',
628
+ });
611
629
  return buildExtractContractFailureResult(session, {
612
630
  step: extractStep,
613
- error: staleScope ? 'stale_extract_scope' : 'extract_failed',
614
- outcomeType: staleScope ? 'binding_stale' : 'blocked',
615
- message: staleScope
616
- ? 'Extraction failed because the requested scope became stale.'
617
- : 'Extraction failed.',
618
- reason: staleScope && scopeRef
619
- ? `${failureMessage} (${scopeRef} marked stale: ${staleReason ?? 'stale'})`
620
- : failureMessage.replace(/^Extract failed:\s*/, ''),
631
+ error: 'browser_connection_failed',
632
+ outcomeType: 'blocked',
633
+ message: browserConnectionFailure.message,
634
+ reason: browserConnectionFailure.reason,
621
635
  scopeRef,
622
636
  pageRef,
623
- staleScope,
624
- staleReason: staleReason ?? undefined,
625
637
  });
626
638
  }
627
- return buildExtractContractFailureResult(session, {
628
- step: extractStep,
629
- error: 'extract_failed',
630
- outcomeType: 'blocked',
631
- message: 'Extraction failed.',
632
- reason: 'Extraction did not produce a success or failure result.',
633
- scopeRef,
634
- pageRef,
635
- });
636
639
  });
637
640
  }
638
641
  /** CLI wrapper for `extractBrowser(...)` that accepts JSON text or schema objects. */