@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
@@ -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,6 +21,8 @@ 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
27
  import { annotateDomTargets, compressSemanticallyDuplicateTargets, orderBySurfaceCompetition, prioritizeGoalActionTargets, } from './observe-semantics.js';
26
28
  import { buildGroupedObserveScopes, buildGoalProjectionScopeRefs, buildGoalObserveInventoryCandidates, compactFillableForms, compactSignals, expandWorkflowGraphTargets, projectPersistedTargetsForGoal, selectTargetsForGoalMatches, } from './observe-projection.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,49 +220,250 @@ 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 { url, title } = await syncSessionPage(session, pageRef, page);
229
- const protectedExposure = getProtectedExposure(session, pageRef);
230
- bumpPageScopeEpoch(session, pageRef);
231
- setCurrentPage(session, pageRef);
232
- const collectedTargets = await collectDomTargets(page);
233
- let observeAccessibilityStats;
234
- const domTargets = compressSemanticallyDuplicateTargets(orderBySurfaceCompetition(annotateDomTargets(await enrichDomTargetsWithAccessibility(page, collectedTargets, {
235
- onStats: (stats) => {
236
- observeAccessibilityStats = stats;
237
- },
238
- }))));
239
- if (observeAccessibilityStats) {
240
- incrementMetric(session, 'observeAxAttempts', observeAccessibilityStats.axAttempts);
241
- incrementMetric(session, 'observeAxHits', observeAccessibilityStats.axHits);
242
- 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 = compressSemanticallyDuplicateTargets(orderBySurfaceCompetition(annotateDomTargets(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
+ return buildObserveSuccessResult(session, observeStep, {
261
+ success: true,
262
+ observationMode: 'deterministic_dom',
263
+ pageRef,
264
+ resolvedBy: 'dom',
265
+ ...domRuntimeResolution(),
266
+ scopes: buildGroupedObserveScopes({
267
+ pageRef,
268
+ title,
269
+ scopes: selectScopesForOutput(observedScopes, targets),
270
+ targets,
271
+ }),
272
+ signals: compactSignals(pageSignals),
273
+ fillableForms: compactFillableForms(fillableForms),
274
+ metrics: session.runtime?.metrics,
275
+ 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
+ return buildObserveSuccessResult(session, observeStep, {
285
+ success: true,
286
+ observationMode: 'deterministic_dom',
287
+ pageRef,
288
+ resolvedBy: 'dom',
289
+ ...domRuntimeResolution(),
290
+ scopes: [],
291
+ signals: compactSignals(pageSignals),
292
+ fillableForms: [],
293
+ metrics: session.runtime?.metrics,
294
+ message: 'This observe pass returned zero targets.',
295
+ ...buildObservePageMetadata({
296
+ url,
297
+ title,
298
+ protectedExposure,
299
+ }),
300
+ });
301
+ }
302
+ stagehandFallbackReason = 'deterministic-observe-empty';
303
+ }
304
+ catch (err) {
305
+ domPassError = err instanceof Error ? err.message : String(err);
306
+ if (!allowAssistive) {
307
+ return buildObserveContractFailureResult(session, {
308
+ step: observeStep,
309
+ error: 'observe_failed',
310
+ outcomeType: 'blocked',
311
+ message: 'Observe failed.',
312
+ reason: domPassError,
313
+ pageRef,
314
+ runId: session.activeRunId,
315
+ stepId: observeStep?.stepId,
316
+ });
317
+ }
318
+ stagehandFallbackReason = 'deterministic-observe-failed';
319
+ }
243
320
  }
244
- const pageSignals = await collectPageSignals(page).catch(() => []);
245
- const pageState = classifyObservePageState(pageSignals);
246
- const persisted = persistObservedSurfacesForPage(session, pageRef, domTargets);
247
- observedScopes = persisted.observedScopes;
248
- const surfaceRefMap = persisted.surfaceRefMap;
249
- if (domTargets.length > 0) {
250
- const targets = replaceTargetsForPage(session, pageRef, domTargets.map((target) => toDomDescriptor(pageRef, target, surfaceRefMap)));
251
- reconcileObservedTargetsForPage(session, pageRef, targets);
252
- attachObservedTargetOwners(domTargets, targets);
253
- observedScopes = linkObservedSurfaceGraph(session, pageRef, domTargets, targets, observedScopes, surfaceRefMap);
254
- const fillableForms = shouldSuppressFillableFormsForObserve(pageState)
255
- ? clearProtectedFillableFormsForPage(session, pageRef)
256
- : await persistProtectedFillableFormsForPage(session, pageRef, url, targets, new Date().toISOString());
257
- await disconnectPlaywright(browser);
258
- browser = null;
321
+ if (instruction) {
322
+ try {
323
+ const resolvedPage = await resolveCurrentPageContext(browser, session);
324
+ pageRef = resolvedPage.pageRef;
325
+ const page = resolvedPage.page;
326
+ const previousPageUrl = session.runtime?.pages?.[pageRef]?.url;
327
+ const { url, title } = await syncSessionPage(session, pageRef, page);
328
+ const protectedExposure = getProtectedExposure(session, pageRef);
329
+ if (protectedExposure) {
330
+ return buildObserveContractFailureResult(session, {
331
+ step: observeStep,
332
+ ...buildProtectedObserveBlockedResult(protectedExposure, 'goal-rerank'),
333
+ pageRef,
334
+ runId: session.activeRunId,
335
+ stepId: observeStep?.stepId,
336
+ });
337
+ }
338
+ bumpPageScopeEpoch(session, pageRef);
339
+ setCurrentPage(session, pageRef);
340
+ const collectedTargets = await collectDomTargets(page, {
341
+ includeActivationAffordances: true,
342
+ });
343
+ let observeAccessibilityStats;
344
+ const domTargets = compressSemanticallyDuplicateTargets(orderBySurfaceCompetition(annotateDomTargets(await enrichDomTargetsWithAccessibility(page, collectedTargets, {
345
+ onStats: (stats) => {
346
+ observeAccessibilityStats = stats;
347
+ },
348
+ }))));
349
+ if (observeAccessibilityStats) {
350
+ incrementMetric(session, 'observeAxAttempts', observeAccessibilityStats.axAttempts);
351
+ incrementMetric(session, 'observeAxHits', observeAccessibilityStats.axHits);
352
+ incrementMetric(session, 'observeFallbackUses', observeAccessibilityStats.fallbackUses);
353
+ }
354
+ const pageSignals = await collectPageSignals(page).catch(() => []);
355
+ const pageState = classifyObservePageState(pageSignals);
356
+ const surfaceInputs = collectSurfaceDescriptors(pageRef, domTargets);
357
+ if (domTargets.length > 0) {
358
+ const rerankedCandidates = await tracedStepOperation(() => rerankDomTargetsForGoal(instruction, buildGoalObserveInventoryCandidates(domTargets, surfaceInputs), { session }), {
359
+ spanName: 'agentbrowse.observe.rerank_goal_candidates',
360
+ attributes: {
361
+ ...observePhaseAttributes,
362
+ 'agentbrowse.observe.target_count': domTargets.length,
363
+ },
364
+ });
365
+ const { targets: goalMatchedTargets, selectedSurfaceIds } = selectTargetsForGoalMatches(domTargets, rerankedCandidates);
366
+ const selectedTargets = prioritizeGoalActionTargets(instruction, expandWorkflowGraphTargets(domTargets, goalMatchedTargets, {
367
+ selectedSurfaceIds,
368
+ }));
369
+ const persisted = persistObservedSurfacesForPage(session, pageRef, domTargets, {
370
+ allSurfaceInputs: surfaceInputs,
371
+ explicitSurfaceIds: selectedSurfaceIds,
372
+ });
373
+ observedScopes = persisted.observedScopes;
374
+ const surfaceRefMap = persisted.surfaceRefMap;
375
+ const targets = replaceTargetsForPage(session, pageRef, domTargets.map((target) => toDomDescriptor(pageRef, target, surfaceRefMap)));
376
+ reconcileObservedTargetsForPage(session, pageRef, targets);
377
+ attachObservedTargetOwners(domTargets, targets);
378
+ observedScopes = linkObservedSurfaceGraph(session, pageRef, domTargets, targets, observedScopes, surfaceRefMap);
379
+ const fillableForms = shouldSuppressFillableFormsForObserve(pageState)
380
+ ? clearProtectedFillableFormsForPage(session, pageRef)
381
+ : await persistProtectedFillableFormsForPage(session, pageRef, url, targets, new Date().toISOString(), { previousPageUrl });
382
+ if (selectedTargets.length > 0 || selectedSurfaceIds.size > 0) {
383
+ const projectedTargets = projectPersistedTargetsForGoal(domTargets, targets, selectedTargets);
384
+ const explicitScopeRefs = buildGoalProjectionScopeRefs(projectedTargets, selectedSurfaceIds, surfaceRefMap);
385
+ return buildObserveSuccessResult(session, observeStep, {
386
+ success: true,
387
+ observationMode: canUseAssistiveLlm
388
+ ? 'goal_assistive_rerank'
389
+ : 'goal_heuristic_shortlist',
390
+ pageRef,
391
+ resolvedBy: 'dom-rerank',
392
+ ...domRuntimeResolution(),
393
+ scopes: buildGroupedObserveScopes({
394
+ pageRef,
395
+ title,
396
+ scopes: selectScopesForOutput(observedScopes, projectedTargets, explicitScopeRefs),
397
+ targets: projectedTargets,
398
+ }),
399
+ signals: compactSignals(pageSignals),
400
+ fillableForms: compactFillableForms(fillableForms),
401
+ metrics: session.runtime?.metrics,
402
+ url,
403
+ title,
404
+ });
405
+ }
406
+ return buildObserveSuccessResult(session, observeStep, {
407
+ success: true,
408
+ observationMode: canUseAssistiveLlm
409
+ ? 'goal_assistive_rerank'
410
+ : 'goal_heuristic_shortlist',
411
+ pageRef,
412
+ resolvedBy: 'dom-rerank',
413
+ ...domRuntimeResolution(),
414
+ scopes: [],
415
+ signals: compactSignals(pageSignals),
416
+ fillableForms: compactFillableForms(fillableForms),
417
+ metrics: session.runtime?.metrics,
418
+ message: 'This goal-based observe pass returned zero matching targets.',
419
+ url,
420
+ title,
421
+ });
422
+ }
423
+ stagehandFallbackReason = 'deterministic-observe-empty';
424
+ }
425
+ catch (err) {
426
+ domPassError = err instanceof Error ? err.message : String(err);
427
+ stagehandFallbackReason = 'deterministic-observe-failed';
428
+ }
429
+ }
430
+ try {
431
+ const resolvedPage = await resolveCurrentPageContext(browser, session);
432
+ pageRef = resolvedPage.pageRef;
433
+ const page = resolvedPage.page;
434
+ const { url, title } = await syncSessionPage(session, pageRef, page);
435
+ const protectedExposure = getProtectedExposure(session, pageRef);
436
+ if (protectedExposure) {
437
+ return buildObserveContractFailureResult(session, {
438
+ step: observeStep,
439
+ ...buildProtectedObserveBlockedResult(protectedExposure, 'stagehand-fallback'),
440
+ fallbackReason: stagehandFallbackReason ?? undefined,
441
+ deterministicObserveError: domPassError ?? undefined,
442
+ pageRef,
443
+ runId: session.activeRunId,
444
+ stepId: observeStep?.stepId,
445
+ });
446
+ }
447
+ bumpPageScopeEpoch(session, pageRef);
448
+ setCurrentPage(session, pageRef);
449
+ const pageSignals = await collectPageSignals(page).catch(() => []);
450
+ const actions = await withStagehand(session, async (stagehand) => {
451
+ incrementMetric(session, 'stagehandCalls');
452
+ return instruction
453
+ ? (await stagehand.observe(instruction, {
454
+ page,
455
+ }))
456
+ : (await stagehand.observe({ page }));
457
+ });
458
+ const targets = replaceTargetsForPage(session, pageRef, await Promise.all(actions.map((action) => toStagehandDescriptor(pageRef, action, page, normalizePageSignature(url)))));
459
+ const fillableForms = markProtectedFillableFormsUnknownForPage(session, pageRef);
259
460
  return buildObserveSuccessResult(session, observeStep, {
260
461
  success: true,
261
- observationMode: 'deterministic_dom',
462
+ observationMode: 'goal_assistive_stagehand',
262
463
  pageRef,
263
- resolvedBy: 'dom',
264
- ...domRuntimeResolution(),
464
+ resolvedBy: 'stagehand-observe',
465
+ ...stagehandRuntimeResolution(stagehandFallbackReason ?? 'deterministic-observe-failed'),
466
+ deterministicObserveError: domPassError ?? undefined,
265
467
  scopes: buildGroupedObserveScopes({
266
468
  pageRef,
267
469
  title,
@@ -272,258 +474,46 @@ export async function observeBrowser(session, instruction) {
272
474
  fillableForms: compactFillableForms(fillableForms),
273
475
  metrics: session.runtime?.metrics,
274
476
  message: targets.length === 0 ? 'This observe pass returned zero targets.' : undefined,
275
- ...buildObservePageMetadata({
276
- url,
277
- title,
278
- protectedExposure,
279
- }),
280
- });
281
- }
282
- if (!allowAssistive) {
283
- await disconnectPlaywright(browser);
284
- browser = null;
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
- }),
477
+ url,
478
+ title,
301
479
  });
302
480
  }
303
- stagehandFallbackReason = 'deterministic-observe-empty';
304
- }
305
- catch (err) {
306
- domPassError = err instanceof Error ? err.message : String(err);
307
- if (!allowAssistive) {
481
+ catch (err) {
482
+ const stagehandError = err instanceof Error ? err.message : String(err);
483
+ const details = domPassError
484
+ ? `${stagehandError} (deterministic observe failed earlier: ${domPassError})`
485
+ : stagehandError;
308
486
  return buildObserveContractFailureResult(session, {
309
487
  step: observeStep,
310
488
  error: 'observe_failed',
311
489
  outcomeType: 'blocked',
312
490
  message: 'Observe failed.',
313
- reason: domPassError,
491
+ reason: details,
314
492
  pageRef,
315
493
  runId: session.activeRunId,
316
494
  stepId: observeStep?.stepId,
317
495
  });
318
496
  }
319
- stagehandFallbackReason = 'deterministic-observe-failed';
320
- }
321
- }
322
- if (instruction) {
323
- try {
324
- if (!browser) {
325
- browser = await connectPlaywright(session.cdpUrl);
326
- }
327
- const resolvedPage = await resolveCurrentPageContext(browser, session);
328
- pageRef = resolvedPage.pageRef;
329
- const page = resolvedPage.page;
330
- const { url, title } = await syncSessionPage(session, pageRef, page);
331
- const protectedExposure = getProtectedExposure(session, pageRef);
332
- if (protectedExposure) {
333
- return buildObserveContractFailureResult(session, {
334
- step: observeStep,
335
- ...buildProtectedObserveBlockedResult(protectedExposure, 'goal-rerank'),
336
- pageRef,
337
- runId: session.activeRunId,
338
- stepId: observeStep?.stepId,
339
- });
340
- }
341
- bumpPageScopeEpoch(session, pageRef);
342
- setCurrentPage(session, pageRef);
343
- const collectedTargets = await collectDomTargets(page, {
344
- includeActivationAffordances: true,
345
- });
346
- let observeAccessibilityStats;
347
- const domTargets = compressSemanticallyDuplicateTargets(orderBySurfaceCompetition(annotateDomTargets(await enrichDomTargetsWithAccessibility(page, collectedTargets, {
348
- onStats: (stats) => {
349
- observeAccessibilityStats = stats;
350
- },
351
- }))));
352
- if (observeAccessibilityStats) {
353
- incrementMetric(session, 'observeAxAttempts', observeAccessibilityStats.axAttempts);
354
- incrementMetric(session, 'observeAxHits', observeAccessibilityStats.axHits);
355
- incrementMetric(session, 'observeFallbackUses', observeAccessibilityStats.fallbackUses);
356
- }
357
- const pageSignals = await collectPageSignals(page).catch(() => []);
358
- const pageState = classifyObservePageState(pageSignals);
359
- const surfaceInputs = collectSurfaceDescriptors(pageRef, domTargets);
360
- if (domTargets.length > 0) {
361
- const rerankedCandidates = await tracedStepOperation(() => rerankDomTargetsForGoal(instruction, buildGoalObserveInventoryCandidates(domTargets, surfaceInputs), { session }), {
362
- spanName: 'agentbrowse.observe.rerank_goal_candidates',
363
- attributes: {
364
- ...observePhaseAttributes,
365
- 'agentbrowse.observe.target_count': domTargets.length,
366
- },
367
- });
368
- const { targets: goalMatchedTargets, selectedSurfaceIds } = selectTargetsForGoalMatches(domTargets, rerankedCandidates);
369
- const selectedTargets = prioritizeGoalActionTargets(instruction, expandWorkflowGraphTargets(domTargets, goalMatchedTargets, {
370
- selectedSurfaceIds,
371
- }));
372
- const persisted = persistObservedSurfacesForPage(session, pageRef, domTargets, {
373
- allSurfaceInputs: surfaceInputs,
374
- explicitSurfaceIds: selectedSurfaceIds,
375
- });
376
- observedScopes = persisted.observedScopes;
377
- const surfaceRefMap = persisted.surfaceRefMap;
378
- const targets = replaceTargetsForPage(session, pageRef, domTargets.map((target) => toDomDescriptor(pageRef, target, surfaceRefMap)));
379
- reconcileObservedTargetsForPage(session, pageRef, targets);
380
- attachObservedTargetOwners(domTargets, targets);
381
- observedScopes = linkObservedSurfaceGraph(session, pageRef, domTargets, targets, observedScopes, surfaceRefMap);
382
- const fillableForms = shouldSuppressFillableFormsForObserve(pageState)
383
- ? clearProtectedFillableFormsForPage(session, pageRef)
384
- : await persistProtectedFillableFormsForPage(session, pageRef, url, targets, new Date().toISOString());
385
- if (selectedTargets.length > 0 || selectedSurfaceIds.size > 0) {
386
- const projectedTargets = projectPersistedTargetsForGoal(domTargets, targets, selectedTargets);
387
- const explicitScopeRefs = buildGoalProjectionScopeRefs(projectedTargets, selectedSurfaceIds, surfaceRefMap);
388
- await disconnectPlaywright(browser);
389
- browser = null;
390
- return buildObserveSuccessResult(session, observeStep, {
391
- success: true,
392
- observationMode: canUseAssistiveLlm
393
- ? 'goal_assistive_rerank'
394
- : 'goal_heuristic_shortlist',
395
- pageRef,
396
- resolvedBy: 'dom-rerank',
397
- ...domRuntimeResolution(),
398
- scopes: buildGroupedObserveScopes({
399
- pageRef,
400
- title,
401
- scopes: selectScopesForOutput(observedScopes, projectedTargets, explicitScopeRefs),
402
- targets: projectedTargets,
403
- }),
404
- signals: compactSignals(pageSignals),
405
- fillableForms: compactFillableForms(fillableForms),
406
- metrics: session.runtime?.metrics,
407
- url,
408
- title,
409
- });
410
- }
411
- await disconnectPlaywright(browser);
412
- browser = null;
413
- return buildObserveSuccessResult(session, observeStep, {
414
- success: true,
415
- observationMode: canUseAssistiveLlm
416
- ? 'goal_assistive_rerank'
417
- : 'goal_heuristic_shortlist',
418
- pageRef,
419
- resolvedBy: 'dom-rerank',
420
- ...domRuntimeResolution(),
421
- scopes: [],
422
- signals: compactSignals(pageSignals),
423
- fillableForms: compactFillableForms(fillableForms),
424
- metrics: session.runtime?.metrics,
425
- message: 'This goal-based observe pass returned zero matching targets.',
426
- url,
427
- title,
428
- });
429
- }
430
- stagehandFallbackReason = 'deterministic-observe-empty';
431
- }
432
- catch (err) {
433
- domPassError = err instanceof Error ? err.message : String(err);
434
- stagehandFallbackReason = 'deterministic-observe-failed';
435
- }
436
- }
437
- try {
438
- if (!browser) {
439
- browser = await connectPlaywright(session.cdpUrl);
440
- }
441
- }
442
- catch (err) {
443
- const domFailure = domPassError && domPassError.length > 0 ? `; dom observe failed: ${domPassError}` : '';
444
- return buildObserveContractFailureResult(session, {
445
- step: observeStep,
446
- error: 'browser_connection_failed',
447
- outcomeType: 'blocked',
448
- message: 'Observe could not start because AgentBrowse failed to connect to the browser.',
449
- reason: `${err instanceof Error ? err.message : String(err)}${domFailure}`,
450
- pageRef,
451
- runId: session.activeRunId,
452
- stepId: observeStep?.stepId,
453
- });
454
- }
455
- try {
456
- const resolvedPage = await resolveCurrentPageContext(browser, session);
457
- pageRef = resolvedPage.pageRef;
458
- const page = resolvedPage.page;
459
- const { url, title } = await syncSessionPage(session, pageRef, page);
460
- const protectedExposure = getProtectedExposure(session, pageRef);
461
- if (protectedExposure) {
462
- return buildObserveContractFailureResult(session, {
463
- step: observeStep,
464
- ...buildProtectedObserveBlockedResult(protectedExposure, 'stagehand-fallback'),
465
- fallbackReason: stagehandFallbackReason ?? undefined,
466
- deterministicObserveError: domPassError ?? undefined,
467
- pageRef,
468
- runId: session.activeRunId,
469
- stepId: observeStep?.stepId,
470
- });
471
- }
472
- bumpPageScopeEpoch(session, pageRef);
473
- setCurrentPage(session, pageRef);
474
- const pageSignals = await collectPageSignals(page).catch(() => []);
475
- const actions = await withStagehand(session, async (stagehand) => {
476
- incrementMetric(session, 'stagehandCalls');
477
- return instruction
478
- ? (await stagehand.observe(instruction, {
479
- page,
480
- }))
481
- : (await stagehand.observe({ page }));
482
- });
483
- const targets = replaceTargetsForPage(session, pageRef, await Promise.all(actions.map((action) => toStagehandDescriptor(pageRef, action, page, normalizePageSignature(url)))));
484
- const fillableForms = markProtectedFillableFormsUnknownForPage(session, pageRef);
485
- return buildObserveSuccessResult(session, observeStep, {
486
- success: true,
487
- observationMode: 'goal_assistive_stagehand',
488
- pageRef,
489
- resolvedBy: 'stagehand-observe',
490
- ...stagehandRuntimeResolution(stagehandFallbackReason ?? 'deterministic-observe-failed'),
491
- deterministicObserveError: domPassError ?? undefined,
492
- scopes: buildGroupedObserveScopes({
493
- pageRef,
494
- title,
495
- scopes: selectScopesForOutput(observedScopes, targets),
496
- targets,
497
- }),
498
- signals: compactSignals(pageSignals),
499
- fillableForms: compactFillableForms(fillableForms),
500
- metrics: session.runtime?.metrics,
501
- message: targets.length === 0 ? 'This observe pass returned zero targets.' : undefined,
502
- url,
503
- title,
504
497
  });
505
498
  }
506
499
  catch (err) {
507
- const stagehandError = err instanceof Error ? err.message : String(err);
508
- const details = domPassError
509
- ? `${stagehandError} (deterministic observe failed earlier: ${domPassError})`
510
- : stagehandError;
500
+ const browserConnectionFailure = describeBrowserConnectionFailure(err, {
501
+ defaultMessage: instruction
502
+ ? 'Observe could not start because AgentBrowse failed to connect to the browser.'
503
+ : 'Observe failed.',
504
+ unrecoverableSessionMessage: 'Observe could not start because the previous browser session is no longer reachable.',
505
+ });
511
506
  return buildObserveContractFailureResult(session, {
512
507
  step: observeStep,
513
- error: 'observe_failed',
508
+ error: instruction ? 'browser_connection_failed' : 'observe_failed',
514
509
  outcomeType: 'blocked',
515
- message: 'Observe failed.',
516
- reason: details,
510
+ message: browserConnectionFailure.message,
511
+ reason: browserConnectionFailure.reason,
517
512
  pageRef,
518
513
  runId: session.activeRunId,
519
514
  stepId: observeStep?.stepId,
520
515
  });
521
516
  }
522
- finally {
523
- if (browser) {
524
- await disconnectPlaywright(browser);
525
- }
526
- }
527
517
  });
528
518
  }
529
519
  /** 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"}