@mercuryo-ai/agentbrowse 0.2.60 → 0.2.63

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 (105) hide show
  1. package/CHANGELOG.md +33 -1
  2. package/README.md +132 -14
  3. package/dist/browser-session-state.d.ts +40 -10
  4. package/dist/browser-session-state.d.ts.map +1 -1
  5. package/dist/browser-session-state.js +63 -5
  6. package/dist/commands/act.d.ts.map +1 -1
  7. package/dist/commands/act.js +548 -535
  8. package/dist/commands/attach.d.ts +1 -3
  9. package/dist/commands/attach.d.ts.map +1 -1
  10. package/dist/commands/attach.js +5 -12
  11. package/dist/commands/browser-connection-failure.d.ts +9 -0
  12. package/dist/commands/browser-connection-failure.d.ts.map +1 -0
  13. package/dist/commands/browser-connection-failure.js +15 -0
  14. package/dist/commands/browser-status.d.ts +0 -2
  15. package/dist/commands/browser-status.d.ts.map +1 -1
  16. package/dist/commands/browser-status.js +27 -37
  17. package/dist/commands/close.d.ts.map +1 -1
  18. package/dist/commands/close.js +5 -0
  19. package/dist/commands/extract.d.ts.map +1 -1
  20. package/dist/commands/extract.js +147 -144
  21. package/dist/commands/interaction-kernel.d.ts +1 -1
  22. package/dist/commands/interaction-kernel.d.ts.map +1 -1
  23. package/dist/commands/interaction-kernel.js +1 -1
  24. package/dist/commands/launch.d.ts +0 -1
  25. package/dist/commands/launch.d.ts.map +1 -1
  26. package/dist/commands/launch.js +11 -12
  27. package/dist/commands/navigate.d.ts.map +1 -1
  28. package/dist/commands/navigate.js +79 -73
  29. package/dist/commands/observe-accessibility.d.ts.map +1 -1
  30. package/dist/commands/observe-accessibility.js +36 -2
  31. package/dist/commands/observe-inventory.d.ts +50 -7
  32. package/dist/commands/observe-inventory.d.ts.map +1 -1
  33. package/dist/commands/observe-inventory.js +822 -99
  34. package/dist/commands/observe-persistence.d.ts.map +1 -1
  35. package/dist/commands/observe-persistence.js +49 -6
  36. package/dist/commands/observe-projection.d.ts +6 -2
  37. package/dist/commands/observe-projection.d.ts.map +1 -1
  38. package/dist/commands/observe-projection.js +251 -27
  39. package/dist/commands/observe-semantics.d.ts +1 -0
  40. package/dist/commands/observe-semantics.d.ts.map +1 -1
  41. package/dist/commands/observe-semantics.js +541 -135
  42. package/dist/commands/observe-signals.d.ts +4 -4
  43. package/dist/commands/observe-signals.d.ts.map +1 -1
  44. package/dist/commands/observe-signals.js +2 -2
  45. package/dist/commands/observe-surfaces.d.ts +2 -1
  46. package/dist/commands/observe-surfaces.d.ts.map +1 -1
  47. package/dist/commands/observe-surfaces.js +143 -45
  48. package/dist/commands/observe.d.ts +5 -1
  49. package/dist/commands/observe.d.ts.map +1 -1
  50. package/dist/commands/observe.js +266 -274
  51. package/dist/commands/screenshot.d.ts.map +1 -1
  52. package/dist/commands/screenshot.js +50 -64
  53. package/dist/commands/semantic-observe.d.ts.map +1 -1
  54. package/dist/commands/semantic-observe.js +43 -0
  55. package/dist/library.d.ts +3 -1
  56. package/dist/library.d.ts.map +1 -1
  57. package/dist/library.js +3 -1
  58. package/dist/match-resolve-fill.d.ts +196 -0
  59. package/dist/match-resolve-fill.d.ts.map +1 -0
  60. package/dist/match-resolve-fill.js +700 -0
  61. package/dist/match-resolve-fill.test-support.d.ts +34 -0
  62. package/dist/match-resolve-fill.test-support.d.ts.map +1 -0
  63. package/dist/match-resolve-fill.test-support.js +81 -0
  64. package/dist/protected-fill.d.ts.map +1 -1
  65. package/dist/protected-fill.js +46 -7
  66. package/dist/runtime-protected-state.d.ts.map +1 -1
  67. package/dist/runtime-protected-state.js +12 -0
  68. package/dist/runtime-state.d.ts +6 -0
  69. package/dist/runtime-state.d.ts.map +1 -1
  70. package/dist/runtime-state.js +6 -0
  71. package/dist/secrets/form-matcher.d.ts.map +1 -1
  72. package/dist/secrets/form-matcher.js +76 -27
  73. package/dist/secrets/protected-exact-value-redaction.d.ts.map +1 -1
  74. package/dist/secrets/protected-exact-value-redaction.js +6 -0
  75. package/dist/secrets/protected-fill.js +3 -3
  76. package/dist/session.d.ts +3 -3
  77. package/dist/session.d.ts.map +1 -1
  78. package/dist/session.js +2 -2
  79. package/dist/solver/browser-launcher.d.ts.map +1 -1
  80. package/dist/solver/browser-launcher.js +2 -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/dist/testing.d.ts +1 -0
  88. package/dist/testing.d.ts.map +1 -1
  89. package/dist/testing.js +1 -0
  90. package/docs/README.md +28 -11
  91. package/docs/api-reference.md +311 -19
  92. package/docs/assistive-runtime.md +41 -16
  93. package/docs/configuration.md +36 -4
  94. package/docs/getting-started.md +73 -5
  95. package/docs/integration-checklist.md +32 -3
  96. package/docs/match-resolve-fill.md +699 -0
  97. package/docs/protected-fill.md +373 -91
  98. package/docs/testing.md +147 -15
  99. package/docs/troubleshooting.md +47 -6
  100. package/examples/README.md +7 -0
  101. package/examples/match-resolve-fill.ts +107 -0
  102. package/package.json +4 -2
  103. package/dist/protected-fill-browser.d.ts +0 -22
  104. package/dist/protected-fill-browser.d.ts.map +0 -1
  105. package/dist/protected-fill-browser.js +0 -52
@@ -7,7 +7,7 @@ import { ensureRuntimeState, replaceTargetsForPage } from '../runtime-state.js';
7
7
  import { incrementMetric } from '../runtime-metrics.js';
8
8
  import { bumpPageScopeEpoch, setCurrentPage } from '../runtime-page-state.js';
9
9
  import { getProtectedExposure } from '../runtime-protected-state.js';
10
- import { connectPlaywright, disconnectPlaywright, resolveCurrentPageContext, syncSessionPage, } from '../playwright-runtime.js';
10
+ import { resolveCurrentPageContext, syncSessionPage } from '../playwright-runtime.js';
11
11
  import { tracedStepOperation, withApiTraceContext } from '../command-api-tracing.js';
12
12
  import { captureDiagnosticSnapshotBestEffort, finishDiagnosticStepBestEffort, recordCommandLifecycleEventBestEffort, startDiagnosticStep, } from '../diagnostics.js';
13
13
  import { outputContractFailure, outputJSON, } from '../output.js';
@@ -21,9 +21,11 @@ import { enrichDomTargetsWithAccessibility } from './observe-accessibility.js';
21
21
  import { collectPageSignals } from './observe-signals.js';
22
22
  import { attachObservedTargetOwners, linkObservedSurfaceGraph, reconcileObservedTargetsForPage, persistObservedSurfacesForPage, toDomDescriptor, } from './observe-persistence.js';
23
23
  import { clearProtectedFillableFormsForPage, markProtectedFillableFormsUnknownForPage, persistProtectedFillableFormsForPage, } from './observe-protected.js';
24
+ import { withStickyOwnerBrowser } from '../sticky-owner.js';
25
+ import { describeBrowserConnectionFailure } from './browser-connection-failure.js';
24
26
  import { classifyObservePageState, shouldSuppressFillableFormsForObserve, } from './observe-page-state.js';
25
- import { annotateDomTargets, compressSemanticallyDuplicateTargets, orderBySurfaceCompetition, prioritizeGoalActionTargets, } from './observe-semantics.js';
26
- import { buildGroupedObserveScopes, buildGoalProjectionScopeRefs, buildGoalObserveInventoryCandidates, compactFillableForms, compactSignals, expandWorkflowGraphTargets, projectPersistedTargetsForGoal, selectTargetsForGoalMatches, } from './observe-projection.js';
27
+ import { normalizePostEnrichmentDomTargets, prioritizeGoalActionTargets, } from './observe-semantics.js';
28
+ import { buildGroupedObserveScopes, buildGoalProjectionScopeRefs, buildGoalObserveInventoryCandidates, compactFillableForms, compactSignals, expandWorkflowGraphTargets, materializeObserveOutputTargets, projectPersistedTargetsForGoal, selectTargetsForGoalMatches, } from './observe-projection.js';
27
29
  import { collectSurfaceDescriptors, selectScopesForOutput } from './observe-surfaces.js';
28
30
  import { toStagehandDescriptor } from './observe-stagehand.js';
29
31
  import { rerankDomTargetsForGoal } from './semantic-observe.js';
@@ -174,7 +176,6 @@ export async function observeBrowser(session, instruction) {
174
176
  let pageRef = runtime.currentPageRef;
175
177
  let domPassError = null;
176
178
  let stagehandFallbackReason = null;
177
- let browser = null;
178
179
  let observedScopes = [];
179
180
  const observeStep = startDiagnosticStep({
180
181
  runId: session.activeRunId,
@@ -219,50 +220,254 @@ export async function observeBrowser(session, instruction) {
219
220
  stepId: observeStep?.stepId,
220
221
  });
221
222
  }
222
- if (!instruction) {
223
- try {
224
- browser = await connectPlaywright(session.cdpUrl);
225
- const resolvedPage = await resolveCurrentPageContext(browser, session);
226
- pageRef = resolvedPage.pageRef;
227
- const page = resolvedPage.page;
228
- const previousPageUrl = session.runtime?.pages?.[pageRef]?.url;
229
- const { url, title } = await syncSessionPage(session, pageRef, page);
230
- const protectedExposure = getProtectedExposure(session, pageRef);
231
- bumpPageScopeEpoch(session, pageRef);
232
- setCurrentPage(session, pageRef);
233
- const collectedTargets = await collectDomTargets(page);
234
- let observeAccessibilityStats;
235
- const domTargets = compressSemanticallyDuplicateTargets(orderBySurfaceCompetition(annotateDomTargets(await enrichDomTargetsWithAccessibility(page, collectedTargets, {
236
- onStats: (stats) => {
237
- observeAccessibilityStats = stats;
238
- },
239
- }))));
240
- if (observeAccessibilityStats) {
241
- incrementMetric(session, 'observeAxAttempts', observeAccessibilityStats.axAttempts);
242
- incrementMetric(session, 'observeAxHits', observeAccessibilityStats.axHits);
243
- incrementMetric(session, 'observeFallbackUses', observeAccessibilityStats.fallbackUses);
223
+ try {
224
+ return await withStickyOwnerBrowser(session, async (browser) => {
225
+ if (!instruction) {
226
+ try {
227
+ const resolvedPage = await resolveCurrentPageContext(browser, session);
228
+ pageRef = resolvedPage.pageRef;
229
+ const page = resolvedPage.page;
230
+ const previousPageUrl = session.runtime?.pages?.[pageRef]?.url;
231
+ const { url, title } = await syncSessionPage(session, pageRef, page);
232
+ const protectedExposure = getProtectedExposure(session, pageRef);
233
+ bumpPageScopeEpoch(session, pageRef);
234
+ setCurrentPage(session, pageRef);
235
+ const collectedTargets = await collectDomTargets(page);
236
+ let observeAccessibilityStats;
237
+ const domTargets = normalizePostEnrichmentDomTargets(await enrichDomTargetsWithAccessibility(page, collectedTargets, {
238
+ onStats: (stats) => {
239
+ observeAccessibilityStats = stats;
240
+ },
241
+ }));
242
+ if (observeAccessibilityStats) {
243
+ incrementMetric(session, 'observeAxAttempts', observeAccessibilityStats.axAttempts);
244
+ incrementMetric(session, 'observeAxHits', observeAccessibilityStats.axHits);
245
+ incrementMetric(session, 'observeFallbackUses', observeAccessibilityStats.fallbackUses);
246
+ }
247
+ const pageSignals = await collectPageSignals(page).catch(() => []);
248
+ const pageState = classifyObservePageState(pageSignals);
249
+ const persisted = persistObservedSurfacesForPage(session, pageRef, domTargets);
250
+ observedScopes = persisted.observedScopes;
251
+ const surfaceRefMap = persisted.surfaceRefMap;
252
+ if (domTargets.length > 0) {
253
+ const targets = replaceTargetsForPage(session, pageRef, domTargets.map((target) => toDomDescriptor(pageRef, target, surfaceRefMap)));
254
+ reconcileObservedTargetsForPage(session, pageRef, targets);
255
+ attachObservedTargetOwners(domTargets, targets);
256
+ observedScopes = linkObservedSurfaceGraph(session, pageRef, domTargets, targets, observedScopes, surfaceRefMap);
257
+ const fillableForms = shouldSuppressFillableFormsForObserve(pageState)
258
+ ? clearProtectedFillableFormsForPage(session, pageRef)
259
+ : await persistProtectedFillableFormsForPage(session, pageRef, url, targets, new Date().toISOString(), { previousPageUrl });
260
+ const outputTargets = materializeObserveOutputTargets(domTargets, targets);
261
+ return buildObserveSuccessResult(session, observeStep, {
262
+ success: true,
263
+ observationMode: 'deterministic_dom',
264
+ pageRef,
265
+ resolvedBy: 'dom',
266
+ ...domRuntimeResolution(),
267
+ scopes: buildGroupedObserveScopes({
268
+ pageRef,
269
+ title,
270
+ scopes: selectScopesForOutput(observedScopes, targets),
271
+ targets: outputTargets,
272
+ }),
273
+ signals: compactSignals(pageSignals),
274
+ fillableForms: compactFillableForms(fillableForms),
275
+ metrics: session.runtime?.metrics,
276
+ message: targets.length === 0 ? 'This observe pass returned zero targets.' : undefined,
277
+ ...buildObservePageMetadata({
278
+ url,
279
+ title,
280
+ protectedExposure,
281
+ }),
282
+ });
283
+ }
284
+ if (!allowAssistive) {
285
+ return buildObserveSuccessResult(session, observeStep, {
286
+ success: true,
287
+ observationMode: 'deterministic_dom',
288
+ pageRef,
289
+ resolvedBy: 'dom',
290
+ ...domRuntimeResolution(),
291
+ scopes: [],
292
+ signals: compactSignals(pageSignals),
293
+ fillableForms: [],
294
+ metrics: session.runtime?.metrics,
295
+ message: 'This observe pass returned zero targets.',
296
+ ...buildObservePageMetadata({
297
+ url,
298
+ title,
299
+ protectedExposure,
300
+ }),
301
+ });
302
+ }
303
+ stagehandFallbackReason = 'deterministic-observe-empty';
304
+ }
305
+ catch (err) {
306
+ domPassError = err instanceof Error ? err.message : String(err);
307
+ if (!allowAssistive) {
308
+ return buildObserveContractFailureResult(session, {
309
+ step: observeStep,
310
+ error: 'observe_failed',
311
+ outcomeType: 'blocked',
312
+ message: 'Observe failed.',
313
+ reason: domPassError,
314
+ pageRef,
315
+ runId: session.activeRunId,
316
+ stepId: observeStep?.stepId,
317
+ });
318
+ }
319
+ stagehandFallbackReason = 'deterministic-observe-failed';
320
+ }
244
321
  }
245
- const pageSignals = await collectPageSignals(page).catch(() => []);
246
- const pageState = classifyObservePageState(pageSignals);
247
- const persisted = persistObservedSurfacesForPage(session, pageRef, domTargets);
248
- observedScopes = persisted.observedScopes;
249
- const surfaceRefMap = persisted.surfaceRefMap;
250
- if (domTargets.length > 0) {
251
- const targets = replaceTargetsForPage(session, pageRef, domTargets.map((target) => toDomDescriptor(pageRef, target, surfaceRefMap)));
252
- reconcileObservedTargetsForPage(session, pageRef, targets);
253
- attachObservedTargetOwners(domTargets, targets);
254
- observedScopes = linkObservedSurfaceGraph(session, pageRef, domTargets, targets, observedScopes, surfaceRefMap);
255
- const fillableForms = shouldSuppressFillableFormsForObserve(pageState)
256
- ? clearProtectedFillableFormsForPage(session, pageRef)
257
- : await persistProtectedFillableFormsForPage(session, pageRef, url, targets, new Date().toISOString(), { previousPageUrl });
258
- await disconnectPlaywright(browser);
259
- browser = null;
322
+ if (instruction) {
323
+ try {
324
+ const resolvedPage = await resolveCurrentPageContext(browser, session);
325
+ pageRef = resolvedPage.pageRef;
326
+ const page = resolvedPage.page;
327
+ const previousPageUrl = session.runtime?.pages?.[pageRef]?.url;
328
+ const { url, title } = await syncSessionPage(session, pageRef, page);
329
+ const protectedExposure = getProtectedExposure(session, pageRef);
330
+ if (protectedExposure) {
331
+ return buildObserveContractFailureResult(session, {
332
+ step: observeStep,
333
+ ...buildProtectedObserveBlockedResult(protectedExposure, 'goal-rerank'),
334
+ pageRef,
335
+ runId: session.activeRunId,
336
+ stepId: observeStep?.stepId,
337
+ });
338
+ }
339
+ bumpPageScopeEpoch(session, pageRef);
340
+ setCurrentPage(session, pageRef);
341
+ const collectedTargets = await collectDomTargets(page, {
342
+ includeActivationAffordances: true,
343
+ });
344
+ let observeAccessibilityStats;
345
+ const domTargets = normalizePostEnrichmentDomTargets(await enrichDomTargetsWithAccessibility(page, collectedTargets, {
346
+ onStats: (stats) => {
347
+ observeAccessibilityStats = stats;
348
+ },
349
+ }));
350
+ if (observeAccessibilityStats) {
351
+ incrementMetric(session, 'observeAxAttempts', observeAccessibilityStats.axAttempts);
352
+ incrementMetric(session, 'observeAxHits', observeAccessibilityStats.axHits);
353
+ incrementMetric(session, 'observeFallbackUses', observeAccessibilityStats.fallbackUses);
354
+ }
355
+ const pageSignals = await collectPageSignals(page).catch(() => []);
356
+ const pageState = classifyObservePageState(pageSignals);
357
+ const surfaceInputs = collectSurfaceDescriptors(pageRef, domTargets);
358
+ if (domTargets.length > 0) {
359
+ const rerankedCandidates = await tracedStepOperation(() => rerankDomTargetsForGoal(instruction, buildGoalObserveInventoryCandidates(domTargets, surfaceInputs), { session }), {
360
+ spanName: 'agentbrowse.observe.rerank_goal_candidates',
361
+ attributes: {
362
+ ...observePhaseAttributes,
363
+ 'agentbrowse.observe.target_count': domTargets.length,
364
+ },
365
+ });
366
+ const { targets: goalMatchedTargets, selectedSurfaceIds } = selectTargetsForGoalMatches(domTargets, rerankedCandidates);
367
+ const selectedTargets = prioritizeGoalActionTargets(instruction, expandWorkflowGraphTargets(domTargets, goalMatchedTargets, {
368
+ selectedSurfaceIds,
369
+ }));
370
+ const persisted = persistObservedSurfacesForPage(session, pageRef, domTargets, {
371
+ allSurfaceInputs: surfaceInputs,
372
+ explicitSurfaceIds: selectedSurfaceIds,
373
+ });
374
+ observedScopes = persisted.observedScopes;
375
+ const surfaceRefMap = persisted.surfaceRefMap;
376
+ const targets = replaceTargetsForPage(session, pageRef, domTargets.map((target) => toDomDescriptor(pageRef, target, surfaceRefMap)));
377
+ reconcileObservedTargetsForPage(session, pageRef, targets);
378
+ attachObservedTargetOwners(domTargets, targets);
379
+ observedScopes = linkObservedSurfaceGraph(session, pageRef, domTargets, targets, observedScopes, surfaceRefMap);
380
+ const fillableForms = shouldSuppressFillableFormsForObserve(pageState)
381
+ ? clearProtectedFillableFormsForPage(session, pageRef)
382
+ : await persistProtectedFillableFormsForPage(session, pageRef, url, targets, new Date().toISOString(), { previousPageUrl });
383
+ if (selectedTargets.length > 0 || selectedSurfaceIds.size > 0) {
384
+ const projectedTargets = projectPersistedTargetsForGoal(domTargets, targets, selectedTargets);
385
+ const outputProjectedTargets = materializeObserveOutputTargets(domTargets, targets, projectedTargets);
386
+ const explicitScopeRefs = buildGoalProjectionScopeRefs(outputProjectedTargets, selectedSurfaceIds, surfaceRefMap);
387
+ return buildObserveSuccessResult(session, observeStep, {
388
+ success: true,
389
+ observationMode: canUseAssistiveLlm
390
+ ? 'goal_assistive_rerank'
391
+ : 'goal_heuristic_shortlist',
392
+ pageRef,
393
+ resolvedBy: 'dom-rerank',
394
+ ...domRuntimeResolution(),
395
+ scopes: buildGroupedObserveScopes({
396
+ pageRef,
397
+ title,
398
+ scopes: selectScopesForOutput(observedScopes, outputProjectedTargets, explicitScopeRefs),
399
+ targets: outputProjectedTargets,
400
+ }),
401
+ signals: compactSignals(pageSignals),
402
+ fillableForms: compactFillableForms(fillableForms),
403
+ metrics: session.runtime?.metrics,
404
+ url,
405
+ title,
406
+ });
407
+ }
408
+ return buildObserveSuccessResult(session, observeStep, {
409
+ success: true,
410
+ observationMode: canUseAssistiveLlm
411
+ ? 'goal_assistive_rerank'
412
+ : 'goal_heuristic_shortlist',
413
+ pageRef,
414
+ resolvedBy: 'dom-rerank',
415
+ ...domRuntimeResolution(),
416
+ scopes: [],
417
+ signals: compactSignals(pageSignals),
418
+ fillableForms: compactFillableForms(fillableForms),
419
+ metrics: session.runtime?.metrics,
420
+ message: 'This goal-based observe pass returned zero matching targets.',
421
+ url,
422
+ title,
423
+ });
424
+ }
425
+ stagehandFallbackReason = 'deterministic-observe-empty';
426
+ }
427
+ catch (err) {
428
+ domPassError = err instanceof Error ? err.message : String(err);
429
+ stagehandFallbackReason = 'deterministic-observe-failed';
430
+ }
431
+ }
432
+ try {
433
+ const resolvedPage = await resolveCurrentPageContext(browser, session);
434
+ pageRef = resolvedPage.pageRef;
435
+ const page = resolvedPage.page;
436
+ const { url, title } = await syncSessionPage(session, pageRef, page);
437
+ const protectedExposure = getProtectedExposure(session, pageRef);
438
+ if (protectedExposure) {
439
+ return buildObserveContractFailureResult(session, {
440
+ step: observeStep,
441
+ ...buildProtectedObserveBlockedResult(protectedExposure, 'stagehand-fallback'),
442
+ fallbackReason: stagehandFallbackReason ?? undefined,
443
+ deterministicObserveError: domPassError ?? undefined,
444
+ pageRef,
445
+ runId: session.activeRunId,
446
+ stepId: observeStep?.stepId,
447
+ });
448
+ }
449
+ bumpPageScopeEpoch(session, pageRef);
450
+ setCurrentPage(session, pageRef);
451
+ const pageSignals = await collectPageSignals(page).catch(() => []);
452
+ const actions = await withStagehand(session, async (stagehand) => {
453
+ incrementMetric(session, 'stagehandCalls');
454
+ return instruction
455
+ ? (await stagehand.observe(instruction, {
456
+ page,
457
+ }))
458
+ : (await stagehand.observe({
459
+ page,
460
+ }));
461
+ });
462
+ const targets = replaceTargetsForPage(session, pageRef, await Promise.all(actions.map((action) => toStagehandDescriptor(pageRef, action, page, normalizePageSignature(url)))));
463
+ const fillableForms = markProtectedFillableFormsUnknownForPage(session, pageRef);
260
464
  return buildObserveSuccessResult(session, observeStep, {
261
465
  success: true,
262
- observationMode: 'deterministic_dom',
466
+ observationMode: 'goal_assistive_stagehand',
263
467
  pageRef,
264
- resolvedBy: 'dom',
265
- ...domRuntimeResolution(),
468
+ resolvedBy: 'stagehand-observe',
469
+ ...stagehandRuntimeResolution(stagehandFallbackReason ?? 'deterministic-observe-failed'),
470
+ deterministicObserveError: domPassError ?? undefined,
266
471
  scopes: buildGroupedObserveScopes({
267
472
  pageRef,
268
473
  title,
@@ -273,259 +478,46 @@ export async function observeBrowser(session, instruction) {
273
478
  fillableForms: compactFillableForms(fillableForms),
274
479
  metrics: session.runtime?.metrics,
275
480
  message: targets.length === 0 ? 'This observe pass returned zero targets.' : undefined,
276
- ...buildObservePageMetadata({
277
- url,
278
- title,
279
- protectedExposure,
280
- }),
281
- });
282
- }
283
- if (!allowAssistive) {
284
- await disconnectPlaywright(browser);
285
- browser = null;
286
- return buildObserveSuccessResult(session, observeStep, {
287
- success: true,
288
- observationMode: 'deterministic_dom',
289
- pageRef,
290
- resolvedBy: 'dom',
291
- ...domRuntimeResolution(),
292
- scopes: [],
293
- signals: compactSignals(pageSignals),
294
- fillableForms: [],
295
- metrics: session.runtime?.metrics,
296
- message: 'This observe pass returned zero targets.',
297
- ...buildObservePageMetadata({
298
- url,
299
- title,
300
- protectedExposure,
301
- }),
481
+ url,
482
+ title,
302
483
  });
303
484
  }
304
- stagehandFallbackReason = 'deterministic-observe-empty';
305
- }
306
- catch (err) {
307
- domPassError = err instanceof Error ? err.message : String(err);
308
- if (!allowAssistive) {
485
+ catch (err) {
486
+ const stagehandError = err instanceof Error ? err.message : String(err);
487
+ const details = domPassError
488
+ ? `${stagehandError} (deterministic observe failed earlier: ${domPassError})`
489
+ : stagehandError;
309
490
  return buildObserveContractFailureResult(session, {
310
491
  step: observeStep,
311
492
  error: 'observe_failed',
312
493
  outcomeType: 'blocked',
313
494
  message: 'Observe failed.',
314
- reason: domPassError,
495
+ reason: details,
315
496
  pageRef,
316
497
  runId: session.activeRunId,
317
498
  stepId: observeStep?.stepId,
318
499
  });
319
500
  }
320
- stagehandFallbackReason = 'deterministic-observe-failed';
321
- }
322
- }
323
- if (instruction) {
324
- try {
325
- if (!browser) {
326
- browser = await connectPlaywright(session.cdpUrl);
327
- }
328
- const resolvedPage = await resolveCurrentPageContext(browser, session);
329
- pageRef = resolvedPage.pageRef;
330
- const page = resolvedPage.page;
331
- const previousPageUrl = session.runtime?.pages?.[pageRef]?.url;
332
- const { url, title } = await syncSessionPage(session, pageRef, page);
333
- const protectedExposure = getProtectedExposure(session, pageRef);
334
- if (protectedExposure) {
335
- return buildObserveContractFailureResult(session, {
336
- step: observeStep,
337
- ...buildProtectedObserveBlockedResult(protectedExposure, 'goal-rerank'),
338
- pageRef,
339
- runId: session.activeRunId,
340
- stepId: observeStep?.stepId,
341
- });
342
- }
343
- bumpPageScopeEpoch(session, pageRef);
344
- setCurrentPage(session, pageRef);
345
- const collectedTargets = await collectDomTargets(page, {
346
- includeActivationAffordances: true,
347
- });
348
- let observeAccessibilityStats;
349
- const domTargets = compressSemanticallyDuplicateTargets(orderBySurfaceCompetition(annotateDomTargets(await enrichDomTargetsWithAccessibility(page, collectedTargets, {
350
- onStats: (stats) => {
351
- observeAccessibilityStats = stats;
352
- },
353
- }))));
354
- if (observeAccessibilityStats) {
355
- incrementMetric(session, 'observeAxAttempts', observeAccessibilityStats.axAttempts);
356
- incrementMetric(session, 'observeAxHits', observeAccessibilityStats.axHits);
357
- incrementMetric(session, 'observeFallbackUses', observeAccessibilityStats.fallbackUses);
358
- }
359
- const pageSignals = await collectPageSignals(page).catch(() => []);
360
- const pageState = classifyObservePageState(pageSignals);
361
- const surfaceInputs = collectSurfaceDescriptors(pageRef, domTargets);
362
- if (domTargets.length > 0) {
363
- const rerankedCandidates = await tracedStepOperation(() => rerankDomTargetsForGoal(instruction, buildGoalObserveInventoryCandidates(domTargets, surfaceInputs), { session }), {
364
- spanName: 'agentbrowse.observe.rerank_goal_candidates',
365
- attributes: {
366
- ...observePhaseAttributes,
367
- 'agentbrowse.observe.target_count': domTargets.length,
368
- },
369
- });
370
- const { targets: goalMatchedTargets, selectedSurfaceIds } = selectTargetsForGoalMatches(domTargets, rerankedCandidates);
371
- const selectedTargets = prioritizeGoalActionTargets(instruction, expandWorkflowGraphTargets(domTargets, goalMatchedTargets, {
372
- selectedSurfaceIds,
373
- }));
374
- const persisted = persistObservedSurfacesForPage(session, pageRef, domTargets, {
375
- allSurfaceInputs: surfaceInputs,
376
- explicitSurfaceIds: selectedSurfaceIds,
377
- });
378
- observedScopes = persisted.observedScopes;
379
- const surfaceRefMap = persisted.surfaceRefMap;
380
- const targets = replaceTargetsForPage(session, pageRef, domTargets.map((target) => toDomDescriptor(pageRef, target, surfaceRefMap)));
381
- reconcileObservedTargetsForPage(session, pageRef, targets);
382
- attachObservedTargetOwners(domTargets, targets);
383
- observedScopes = linkObservedSurfaceGraph(session, pageRef, domTargets, targets, observedScopes, surfaceRefMap);
384
- const fillableForms = shouldSuppressFillableFormsForObserve(pageState)
385
- ? clearProtectedFillableFormsForPage(session, pageRef)
386
- : await persistProtectedFillableFormsForPage(session, pageRef, url, targets, new Date().toISOString(), { previousPageUrl });
387
- if (selectedTargets.length > 0 || selectedSurfaceIds.size > 0) {
388
- const projectedTargets = projectPersistedTargetsForGoal(domTargets, targets, selectedTargets);
389
- const explicitScopeRefs = buildGoalProjectionScopeRefs(projectedTargets, selectedSurfaceIds, surfaceRefMap);
390
- await disconnectPlaywright(browser);
391
- browser = null;
392
- return buildObserveSuccessResult(session, observeStep, {
393
- success: true,
394
- observationMode: canUseAssistiveLlm
395
- ? 'goal_assistive_rerank'
396
- : 'goal_heuristic_shortlist',
397
- pageRef,
398
- resolvedBy: 'dom-rerank',
399
- ...domRuntimeResolution(),
400
- scopes: buildGroupedObserveScopes({
401
- pageRef,
402
- title,
403
- scopes: selectScopesForOutput(observedScopes, projectedTargets, explicitScopeRefs),
404
- targets: projectedTargets,
405
- }),
406
- signals: compactSignals(pageSignals),
407
- fillableForms: compactFillableForms(fillableForms),
408
- metrics: session.runtime?.metrics,
409
- url,
410
- title,
411
- });
412
- }
413
- await disconnectPlaywright(browser);
414
- browser = null;
415
- return buildObserveSuccessResult(session, observeStep, {
416
- success: true,
417
- observationMode: canUseAssistiveLlm
418
- ? 'goal_assistive_rerank'
419
- : 'goal_heuristic_shortlist',
420
- pageRef,
421
- resolvedBy: 'dom-rerank',
422
- ...domRuntimeResolution(),
423
- scopes: [],
424
- signals: compactSignals(pageSignals),
425
- fillableForms: compactFillableForms(fillableForms),
426
- metrics: session.runtime?.metrics,
427
- message: 'This goal-based observe pass returned zero matching targets.',
428
- url,
429
- title,
430
- });
431
- }
432
- stagehandFallbackReason = 'deterministic-observe-empty';
433
- }
434
- catch (err) {
435
- domPassError = err instanceof Error ? err.message : String(err);
436
- stagehandFallbackReason = 'deterministic-observe-failed';
437
- }
438
- }
439
- try {
440
- if (!browser) {
441
- browser = await connectPlaywright(session.cdpUrl);
442
- }
443
- }
444
- catch (err) {
445
- const domFailure = domPassError && domPassError.length > 0 ? `; dom observe failed: ${domPassError}` : '';
446
- return buildObserveContractFailureResult(session, {
447
- step: observeStep,
448
- error: 'browser_connection_failed',
449
- outcomeType: 'blocked',
450
- message: 'Observe could not start because AgentBrowse failed to connect to the browser.',
451
- reason: `${err instanceof Error ? err.message : String(err)}${domFailure}`,
452
- pageRef,
453
- runId: session.activeRunId,
454
- stepId: observeStep?.stepId,
455
- });
456
- }
457
- try {
458
- const resolvedPage = await resolveCurrentPageContext(browser, session);
459
- pageRef = resolvedPage.pageRef;
460
- const page = resolvedPage.page;
461
- const { url, title } = await syncSessionPage(session, pageRef, page);
462
- const protectedExposure = getProtectedExposure(session, pageRef);
463
- if (protectedExposure) {
464
- return buildObserveContractFailureResult(session, {
465
- step: observeStep,
466
- ...buildProtectedObserveBlockedResult(protectedExposure, 'stagehand-fallback'),
467
- fallbackReason: stagehandFallbackReason ?? undefined,
468
- deterministicObserveError: domPassError ?? undefined,
469
- pageRef,
470
- runId: session.activeRunId,
471
- stepId: observeStep?.stepId,
472
- });
473
- }
474
- bumpPageScopeEpoch(session, pageRef);
475
- setCurrentPage(session, pageRef);
476
- const pageSignals = await collectPageSignals(page).catch(() => []);
477
- const actions = await withStagehand(session, async (stagehand) => {
478
- incrementMetric(session, 'stagehandCalls');
479
- return instruction
480
- ? (await stagehand.observe(instruction, {
481
- page,
482
- }))
483
- : (await stagehand.observe({ page }));
484
- });
485
- const targets = replaceTargetsForPage(session, pageRef, await Promise.all(actions.map((action) => toStagehandDescriptor(pageRef, action, page, normalizePageSignature(url)))));
486
- const fillableForms = markProtectedFillableFormsUnknownForPage(session, pageRef);
487
- return buildObserveSuccessResult(session, observeStep, {
488
- success: true,
489
- observationMode: 'goal_assistive_stagehand',
490
- pageRef,
491
- resolvedBy: 'stagehand-observe',
492
- ...stagehandRuntimeResolution(stagehandFallbackReason ?? 'deterministic-observe-failed'),
493
- deterministicObserveError: domPassError ?? undefined,
494
- scopes: buildGroupedObserveScopes({
495
- pageRef,
496
- title,
497
- scopes: selectScopesForOutput(observedScopes, targets),
498
- targets,
499
- }),
500
- signals: compactSignals(pageSignals),
501
- fillableForms: compactFillableForms(fillableForms),
502
- metrics: session.runtime?.metrics,
503
- message: targets.length === 0 ? 'This observe pass returned zero targets.' : undefined,
504
- url,
505
- title,
506
501
  });
507
502
  }
508
503
  catch (err) {
509
- const stagehandError = err instanceof Error ? err.message : String(err);
510
- const details = domPassError
511
- ? `${stagehandError} (deterministic observe failed earlier: ${domPassError})`
512
- : stagehandError;
504
+ const browserConnectionFailure = describeBrowserConnectionFailure(err, {
505
+ defaultMessage: instruction
506
+ ? 'Observe could not start because AgentBrowse failed to connect to the browser.'
507
+ : 'Observe failed.',
508
+ unrecoverableSessionMessage: 'Observe could not start because the previous browser session is no longer reachable.',
509
+ });
513
510
  return buildObserveContractFailureResult(session, {
514
511
  step: observeStep,
515
- error: 'observe_failed',
512
+ error: instruction ? 'browser_connection_failed' : 'observe_failed',
516
513
  outcomeType: 'blocked',
517
- message: 'Observe failed.',
518
- reason: details,
514
+ message: browserConnectionFailure.message,
515
+ reason: browserConnectionFailure.reason,
519
516
  pageRef,
520
517
  runId: session.activeRunId,
521
518
  stepId: observeStep?.stepId,
522
519
  });
523
520
  }
524
- finally {
525
- if (browser) {
526
- await disconnectPlaywright(browser);
527
- }
528
- }
529
521
  });
530
522
  }
531
523
  /** CLI wrapper for `observeBrowser(...)` that persists the observed runtime state. */
@@ -1 +1 @@
1
- {"version":3,"file":"screenshot.d.ts","sourceRoot":"","sources":["../../src/commands/screenshot.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AAqBzE,kEAAkE;AAClE,eAAO,MAAM,sBAAsB,6FAIzB,CAAC;AAEX,8DAA8D;AAC9D,eAAO,MAAM,wBAAwB,0EAI3B,CAAC;AAEX,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,sBAAsB,CAAC,CAAC,MAAM,CAAC,CAAC;AAC1E,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,wBAAwB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE9E,4CAA4C;AAC5C,MAAM,MAAM,uBAAuB,GAAG;IACpC,OAAO,EAAE,IAAI,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,wCAAwC;AACxC,MAAM,MAAM,uBAAuB,GAAG;IACpC,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,EAAE,mBAAmB,CAAC;IAC3B,WAAW,EAAE,OAAO,CAAC,qBAAqB,EAAE,SAAS,GAAG,2BAA2B,CAAC,CAAC;IACrF,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,uBAAuB,GAAG,uBAAuB,CAAC;AAkJjF,0DAA0D;AAC1D,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,qBAAqB,EAC9B,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,gBAAgB,CAAC,CAwH3B;AAED,qGAAqG;AACrG,wBAAsB,UAAU,CAAC,OAAO,EAAE,qBAAqB,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBjG"}
1
+ {"version":3,"file":"screenshot.d.ts","sourceRoot":"","sources":["../../src/commands/screenshot.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AAkBzE,kEAAkE;AAClE,eAAO,MAAM,sBAAsB,6FAIzB,CAAC;AAEX,8DAA8D;AAC9D,eAAO,MAAM,wBAAwB,0EAI3B,CAAC;AAEX,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,sBAAsB,CAAC,CAAC,MAAM,CAAC,CAAC;AAC1E,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,wBAAwB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE9E,4CAA4C;AAC5C,MAAM,MAAM,uBAAuB,GAAG;IACpC,OAAO,EAAE,IAAI,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,wCAAwC;AACxC,MAAM,MAAM,uBAAuB,GAAG;IACpC,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,EAAE,mBAAmB,CAAC;IAC3B,WAAW,EAAE,OAAO,CAAC,qBAAqB,EAAE,SAAS,GAAG,2BAA2B,CAAC,CAAC;IACrF,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,uBAAuB,GAAG,uBAAuB,CAAC;AAkJjF,0DAA0D;AAC1D,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,qBAAqB,EAC9B,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,gBAAgB,CAAC,CA2G3B;AAED,qGAAqG;AACrG,wBAAsB,UAAU,CAAC,OAAO,EAAE,qBAAqB,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBjG"}