@opensteer/engine-playwright 0.8.0 → 0.8.2

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.
package/dist/index.d.cts CHANGED
@@ -753,6 +753,19 @@ interface BrowserInstrumentation {
753
753
  interface BrowserCoreEngine extends BrowserExecutor, BrowserInspector, SessionTransportExecutor, BrowserInstrumentation {
754
754
  }
755
755
 
756
+ interface ActionBoundarySnapshot {
757
+ readonly pageRef: PageRef;
758
+ readonly documentRef: DocumentRef;
759
+ }
760
+ type ActionBoundarySettleTrigger = "dom-action" | "navigation";
761
+ type ActionBoundaryTimedOutPhase = "bootstrap";
762
+ interface ActionBoundaryOutcome {
763
+ readonly trigger: ActionBoundarySettleTrigger;
764
+ readonly crossDocument: boolean;
765
+ readonly bootstrapSettled: boolean;
766
+ readonly timedOutPhase?: ActionBoundaryTimedOutPhase;
767
+ }
768
+
756
769
  declare const opensteerComputerAnnotationNames: readonly ["clickable", "typeable", "scrollable", "grid", "selected"];
757
770
  type OpensteerComputerAnnotation = (typeof opensteerComputerAnnotationNames)[number];
758
771
  type OpensteerComputerMouseButton = "left" | "middle" | "right";
@@ -814,11 +827,12 @@ interface NormalizedComputerScreenshotOptions {
814
827
  }
815
828
  interface ComputerUseBridgeInput {
816
829
  readonly pageRef: PageRef;
830
+ readonly snapshot?: ActionBoundarySnapshot;
817
831
  readonly action: OpensteerComputerAction;
818
832
  readonly screenshot: NormalizedComputerScreenshotOptions;
819
833
  readonly signal: AbortSignal;
820
834
  remainingMs(): number | undefined;
821
- policySettle(pageRef: PageRef): Promise<void>;
835
+ policySettle(pageRef: PageRef, trigger: ActionBoundarySettleTrigger): Promise<void>;
822
836
  }
823
837
  interface ComputerUseBridgeOutput {
824
838
  readonly pageRef: PageRef;
@@ -826,6 +840,7 @@ interface ComputerUseBridgeOutput {
826
840
  readonly viewport: ViewportMetrics;
827
841
  readonly events: readonly StepEvent[];
828
842
  readonly timing: OpensteerComputerExecuteTiming;
843
+ readonly boundary?: ActionBoundaryOutcome;
829
844
  }
830
845
  interface ComputerUseBridge {
831
846
  execute(input: ComputerUseBridgeInput): Promise<ComputerUseBridgeOutput>;
@@ -886,9 +901,10 @@ interface DomActionKeyPressInput {
886
901
  }
887
902
  interface DomActionSettleOptions {
888
903
  readonly operation: "dom.click" | "dom.hover" | "dom.input" | "dom.scroll";
904
+ readonly snapshot?: ActionBoundarySnapshot;
889
905
  readonly signal: AbortSignal;
890
906
  remainingMs(): number | undefined;
891
- policySettle(pageRef: PageRef): Promise<void>;
907
+ policySettle(pageRef: PageRef, trigger: ActionBoundarySettleTrigger): Promise<void>;
892
908
  }
893
909
  type DomPointerHitRelation = "self" | "descendant" | "ancestor" | "same-owner" | "outside" | "unknown";
894
910
  interface DomPointerHitAssessment {
@@ -910,7 +926,7 @@ interface DomActionBridge {
910
926
  scrollNodeIntoView(locator: NodeLocator, options?: DomActionScrollOptions): Promise<void>;
911
927
  focusNode(locator: NodeLocator): Promise<void>;
912
928
  pressKey(locator: NodeLocator, input: DomActionKeyPressInput): Promise<void>;
913
- finalizeDomAction(pageRef: PageRef, options: DomActionSettleOptions): Promise<void>;
929
+ finalizeDomAction(pageRef: PageRef, options: DomActionSettleOptions): Promise<ActionBoundaryOutcome>;
914
930
  }
915
931
 
916
932
  interface PlaywrightChromiumLaunchOptions {
@@ -978,6 +994,7 @@ declare class PlaywrightBrowserCoreEngine implements BrowserCoreEngine {
978
994
  private readonly pageByPlaywrightPage;
979
995
  private readonly pendingPopupOpeners;
980
996
  private readonly preassignedPopupPageRefs;
997
+ private readonly actionSettler;
981
998
  private pageCounter;
982
999
  private frameCounter;
983
1000
  private documentCounter;
package/dist/index.d.ts CHANGED
@@ -753,6 +753,19 @@ interface BrowserInstrumentation {
753
753
  interface BrowserCoreEngine extends BrowserExecutor, BrowserInspector, SessionTransportExecutor, BrowserInstrumentation {
754
754
  }
755
755
 
756
+ interface ActionBoundarySnapshot {
757
+ readonly pageRef: PageRef;
758
+ readonly documentRef: DocumentRef;
759
+ }
760
+ type ActionBoundarySettleTrigger = "dom-action" | "navigation";
761
+ type ActionBoundaryTimedOutPhase = "bootstrap";
762
+ interface ActionBoundaryOutcome {
763
+ readonly trigger: ActionBoundarySettleTrigger;
764
+ readonly crossDocument: boolean;
765
+ readonly bootstrapSettled: boolean;
766
+ readonly timedOutPhase?: ActionBoundaryTimedOutPhase;
767
+ }
768
+
756
769
  declare const opensteerComputerAnnotationNames: readonly ["clickable", "typeable", "scrollable", "grid", "selected"];
757
770
  type OpensteerComputerAnnotation = (typeof opensteerComputerAnnotationNames)[number];
758
771
  type OpensteerComputerMouseButton = "left" | "middle" | "right";
@@ -814,11 +827,12 @@ interface NormalizedComputerScreenshotOptions {
814
827
  }
815
828
  interface ComputerUseBridgeInput {
816
829
  readonly pageRef: PageRef;
830
+ readonly snapshot?: ActionBoundarySnapshot;
817
831
  readonly action: OpensteerComputerAction;
818
832
  readonly screenshot: NormalizedComputerScreenshotOptions;
819
833
  readonly signal: AbortSignal;
820
834
  remainingMs(): number | undefined;
821
- policySettle(pageRef: PageRef): Promise<void>;
835
+ policySettle(pageRef: PageRef, trigger: ActionBoundarySettleTrigger): Promise<void>;
822
836
  }
823
837
  interface ComputerUseBridgeOutput {
824
838
  readonly pageRef: PageRef;
@@ -826,6 +840,7 @@ interface ComputerUseBridgeOutput {
826
840
  readonly viewport: ViewportMetrics;
827
841
  readonly events: readonly StepEvent[];
828
842
  readonly timing: OpensteerComputerExecuteTiming;
843
+ readonly boundary?: ActionBoundaryOutcome;
829
844
  }
830
845
  interface ComputerUseBridge {
831
846
  execute(input: ComputerUseBridgeInput): Promise<ComputerUseBridgeOutput>;
@@ -886,9 +901,10 @@ interface DomActionKeyPressInput {
886
901
  }
887
902
  interface DomActionSettleOptions {
888
903
  readonly operation: "dom.click" | "dom.hover" | "dom.input" | "dom.scroll";
904
+ readonly snapshot?: ActionBoundarySnapshot;
889
905
  readonly signal: AbortSignal;
890
906
  remainingMs(): number | undefined;
891
- policySettle(pageRef: PageRef): Promise<void>;
907
+ policySettle(pageRef: PageRef, trigger: ActionBoundarySettleTrigger): Promise<void>;
892
908
  }
893
909
  type DomPointerHitRelation = "self" | "descendant" | "ancestor" | "same-owner" | "outside" | "unknown";
894
910
  interface DomPointerHitAssessment {
@@ -910,7 +926,7 @@ interface DomActionBridge {
910
926
  scrollNodeIntoView(locator: NodeLocator, options?: DomActionScrollOptions): Promise<void>;
911
927
  focusNode(locator: NodeLocator): Promise<void>;
912
928
  pressKey(locator: NodeLocator, input: DomActionKeyPressInput): Promise<void>;
913
- finalizeDomAction(pageRef: PageRef, options: DomActionSettleOptions): Promise<void>;
929
+ finalizeDomAction(pageRef: PageRef, options: DomActionSettleOptions): Promise<ActionBoundaryOutcome>;
914
930
  }
915
931
 
916
932
  interface PlaywrightChromiumLaunchOptions {
@@ -978,6 +994,7 @@ declare class PlaywrightBrowserCoreEngine implements BrowserCoreEngine {
978
994
  private readonly pageByPlaywrightPage;
979
995
  private readonly pendingPopupOpeners;
980
996
  private readonly preassignedPopupPageRefs;
997
+ private readonly actionSettler;
981
998
  private pageCounter;
982
999
  private frameCounter;
983
1000
  private documentCounter;
package/dist/index.js CHANGED
@@ -1257,6 +1257,282 @@ function sleep(ms) {
1257
1257
  return new Promise((resolve) => setTimeout(resolve, ms));
1258
1258
  }
1259
1259
 
1260
+ // ../browser-core/src/post-load-tracker.ts
1261
+ var DEFAULT_POST_LOAD_TRACKER_QUIET_WINDOW_MS = 400;
1262
+ var DEFAULT_ACTION_BOUNDARY_POLL_INTERVAL_MS = 100;
1263
+ function isRecord(value) {
1264
+ return typeof value === "object" && value !== null;
1265
+ }
1266
+ function readFiniteNumber(value) {
1267
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
1268
+ }
1269
+ function readNonNegativeNumber(value) {
1270
+ const parsed = readFiniteNumber(value);
1271
+ return parsed === void 0 || parsed < 0 ? 0 : parsed;
1272
+ }
1273
+ function normalizePostLoadTrackerState(value) {
1274
+ if (!isRecord(value)) {
1275
+ return void 0;
1276
+ }
1277
+ const installedAt = readFiniteNumber(value.installedAt);
1278
+ const lastMutationAt = readFiniteNumber(value.lastMutationAt);
1279
+ const lastNetworkActivityAt = readFiniteNumber(value.lastNetworkActivityAt);
1280
+ const now = readFiniteNumber(value.now);
1281
+ const readyState = typeof value.readyState === "string" ? value.readyState : void 0;
1282
+ if (installedAt === void 0 || lastMutationAt === void 0 || lastNetworkActivityAt === void 0 || now === void 0 || readyState === void 0) {
1283
+ return void 0;
1284
+ }
1285
+ return {
1286
+ installedAt,
1287
+ lastMutationAt,
1288
+ lastNetworkActivityAt,
1289
+ now,
1290
+ pendingFetches: readNonNegativeNumber(value.pendingFetches),
1291
+ pendingTimeouts: readNonNegativeNumber(value.pendingTimeouts),
1292
+ pendingXhrs: readNonNegativeNumber(value.pendingXhrs),
1293
+ readyState
1294
+ };
1295
+ }
1296
+ function buildPostLoadTrackerInstallScript() {
1297
+ return `(() => {
1298
+ const globalObject = globalThis;
1299
+ if (globalObject.__opensteerActionBoundaryTrackerInstalled) {
1300
+ return true;
1301
+ }
1302
+
1303
+ const tracker = {
1304
+ installedAt: performance.now(),
1305
+ lastMutationAt: performance.now(),
1306
+ lastNetworkActivityAt: performance.now(),
1307
+ pendingFetches: 0,
1308
+ pendingTimeouts: 0,
1309
+ pendingXhrs: 0,
1310
+ readyState: document.readyState,
1311
+ timeoutIds: new Set(),
1312
+ };
1313
+ globalObject.__opensteerActionBoundaryTrackerInstalled = true;
1314
+ globalObject.__opensteerActionBoundaryTracker = tracker;
1315
+
1316
+ const markMutation = () => {
1317
+ tracker.lastMutationAt = performance.now();
1318
+ tracker.readyState = document.readyState;
1319
+ };
1320
+ const markNetwork = () => {
1321
+ tracker.lastNetworkActivityAt = performance.now();
1322
+ tracker.readyState = document.readyState;
1323
+ };
1324
+
1325
+ const startObserver = () => {
1326
+ const target = document.documentElement ?? document;
1327
+ if (!(target instanceof Node)) {
1328
+ return;
1329
+ }
1330
+ const observer = new MutationObserver(markMutation);
1331
+ observer.observe(target, {
1332
+ subtree: true,
1333
+ childList: true,
1334
+ characterData: true,
1335
+ attributes: true,
1336
+ });
1337
+ markMutation();
1338
+ };
1339
+
1340
+ if (document.documentElement) {
1341
+ startObserver();
1342
+ } else {
1343
+ document.addEventListener("DOMContentLoaded", startObserver, { once: true });
1344
+ }
1345
+
1346
+ document.addEventListener("readystatechange", markMutation);
1347
+ addEventListener("load", markMutation, { once: true });
1348
+
1349
+ const nativeSetTimeout = globalObject.setTimeout.bind(globalObject);
1350
+ const nativeClearTimeout = globalObject.clearTimeout.bind(globalObject);
1351
+ globalObject.setTimeout = function(callback, delay, ...args) {
1352
+ tracker.pendingTimeouts += 1;
1353
+ markNetwork();
1354
+ let handle;
1355
+ const wrapped =
1356
+ typeof callback === "function"
1357
+ ? (...callbackArgs) => {
1358
+ if (tracker.timeoutIds.delete(handle)) {
1359
+ tracker.pendingTimeouts = Math.max(0, tracker.pendingTimeouts - 1);
1360
+ }
1361
+ try {
1362
+ return callback(...callbackArgs);
1363
+ } finally {
1364
+ markMutation();
1365
+ }
1366
+ }
1367
+ : callback;
1368
+ handle = nativeSetTimeout(wrapped, delay, ...args);
1369
+ tracker.timeoutIds.add(handle);
1370
+ return handle;
1371
+ };
1372
+ globalObject.clearTimeout = function(handle) {
1373
+ if (tracker.timeoutIds.delete(handle)) {
1374
+ tracker.pendingTimeouts = Math.max(0, tracker.pendingTimeouts - 1);
1375
+ }
1376
+ return nativeClearTimeout(handle);
1377
+ };
1378
+
1379
+ if (typeof globalObject.fetch === "function") {
1380
+ const nativeFetch = globalObject.fetch.bind(globalObject);
1381
+ globalObject.fetch = (...args) => {
1382
+ tracker.pendingFetches += 1;
1383
+ markNetwork();
1384
+ return nativeFetch(...args)
1385
+ .finally(() => {
1386
+ tracker.pendingFetches = Math.max(0, tracker.pendingFetches - 1);
1387
+ markNetwork();
1388
+ });
1389
+ };
1390
+ }
1391
+
1392
+ if (typeof globalObject.XMLHttpRequest === "function") {
1393
+ const NativeXMLHttpRequest = globalObject.XMLHttpRequest;
1394
+ const nativeSend = NativeXMLHttpRequest.prototype.send;
1395
+ NativeXMLHttpRequest.prototype.send = function(...args) {
1396
+ tracker.pendingXhrs += 1;
1397
+ markNetwork();
1398
+ const finalize = () => {
1399
+ this.removeEventListener("loadend", finalize);
1400
+ tracker.pendingXhrs = Math.max(0, tracker.pendingXhrs - 1);
1401
+ markNetwork();
1402
+ };
1403
+ this.addEventListener("loadend", finalize, { once: true });
1404
+ return nativeSend.apply(this, args);
1405
+ };
1406
+ }
1407
+
1408
+ return true;
1409
+ })()`;
1410
+ }
1411
+ function buildPostLoadTrackerReadExpression() {
1412
+ return `(() => {
1413
+ const tracker = globalThis.__opensteerActionBoundaryTracker;
1414
+ if (!tracker) {
1415
+ return null;
1416
+ }
1417
+
1418
+ return {
1419
+ installedAt: Number(tracker.installedAt ?? 0),
1420
+ lastMutationAt: Number(tracker.lastMutationAt ?? 0),
1421
+ lastNetworkActivityAt: Number(tracker.lastNetworkActivityAt ?? 0),
1422
+ now: Number(performance.now()),
1423
+ pendingFetches: Number(tracker.pendingFetches ?? 0),
1424
+ pendingTimeouts: Number(tracker.pendingTimeouts ?? 0),
1425
+ pendingXhrs: Number(tracker.pendingXhrs ?? 0),
1426
+ readyState: String(document.readyState),
1427
+ };
1428
+ })()`;
1429
+ }
1430
+ function postLoadTrackerIsSettled(tracker, quietWindowMs = DEFAULT_POST_LOAD_TRACKER_QUIET_WINDOW_MS) {
1431
+ if (!tracker) {
1432
+ return false;
1433
+ }
1434
+ if (tracker.readyState !== "complete") {
1435
+ return false;
1436
+ }
1437
+ if (tracker.pendingFetches > 0 || tracker.pendingTimeouts > 0 || tracker.pendingXhrs > 0) {
1438
+ return false;
1439
+ }
1440
+ const lastActivityAt = Math.max(
1441
+ tracker.installedAt,
1442
+ tracker.lastMutationAt,
1443
+ tracker.lastNetworkActivityAt
1444
+ );
1445
+ return tracker.now - lastActivityAt >= quietWindowMs;
1446
+ }
1447
+
1448
+ // ../browser-core/src/action-boundary.ts
1449
+ var CROSS_DOCUMENT_INTERACTION_TIMEOUT_MS = 3e4;
1450
+ var CROSS_DOCUMENT_DETECTION_WINDOW_MS = 500;
1451
+ async function waitForActionBoundary(input) {
1452
+ if (input.timeoutMs <= 0) {
1453
+ return {
1454
+ trigger: "dom-action",
1455
+ crossDocument: false,
1456
+ bootstrapSettled: false,
1457
+ timedOutPhase: "bootstrap"
1458
+ };
1459
+ }
1460
+ const deadline = Date.now() + input.timeoutMs;
1461
+ const crossDocumentDetectionDeadline = input.snapshot === void 0 ? void 0 : Math.min(deadline, Date.now() + CROSS_DOCUMENT_DETECTION_WINDOW_MS);
1462
+ const pollIntervalMs = input.pollIntervalMs ?? DEFAULT_ACTION_BOUNDARY_POLL_INTERVAL_MS;
1463
+ let trigger = "dom-action";
1464
+ let crossDocument = false;
1465
+ let waitedForNavigationContentLoaded = false;
1466
+ while (Date.now() < deadline) {
1467
+ input.throwBackgroundError();
1468
+ if (input.isPageClosed()) {
1469
+ return {
1470
+ trigger,
1471
+ crossDocument,
1472
+ bootstrapSettled: true
1473
+ };
1474
+ }
1475
+ if (input.signal?.aborted) {
1476
+ if (isTimeoutAbort(input.signal.reason) && Date.now() >= deadline) {
1477
+ return {
1478
+ trigger,
1479
+ crossDocument,
1480
+ bootstrapSettled: false,
1481
+ timedOutPhase: "bootstrap"
1482
+ };
1483
+ }
1484
+ throw abortError(input.signal);
1485
+ }
1486
+ const currentDocumentRef = input.getCurrentMainFrameDocumentRef();
1487
+ if (input.snapshot !== void 0 && currentDocumentRef !== void 0 && currentDocumentRef !== input.snapshot.documentRef) {
1488
+ trigger = "navigation";
1489
+ crossDocument = true;
1490
+ if (!waitedForNavigationContentLoaded) {
1491
+ waitedForNavigationContentLoaded = true;
1492
+ const remaining = Math.max(0, deadline - Date.now());
1493
+ if (remaining > 0) {
1494
+ await input.waitForNavigationContentLoaded(remaining);
1495
+ }
1496
+ }
1497
+ }
1498
+ if (!crossDocument && crossDocumentDetectionDeadline !== void 0 && Date.now() >= crossDocumentDetectionDeadline) {
1499
+ return {
1500
+ trigger,
1501
+ crossDocument,
1502
+ bootstrapSettled: true
1503
+ };
1504
+ }
1505
+ if (crossDocument && postLoadTrackerIsSettled(await input.readTrackerState())) {
1506
+ return {
1507
+ trigger,
1508
+ crossDocument,
1509
+ bootstrapSettled: true
1510
+ };
1511
+ }
1512
+ await delay(Math.min(pollIntervalMs, Math.max(0, deadline - Date.now())));
1513
+ }
1514
+ return {
1515
+ trigger,
1516
+ crossDocument,
1517
+ bootstrapSettled: false,
1518
+ timedOutPhase: "bootstrap"
1519
+ };
1520
+ }
1521
+ function abortError(signal) {
1522
+ return signal.reason ?? new DOMException("The operation was aborted", "AbortError");
1523
+ }
1524
+ async function delay(ms) {
1525
+ if (ms <= 0) {
1526
+ return;
1527
+ }
1528
+ await new Promise((resolve) => {
1529
+ setTimeout(resolve, ms);
1530
+ });
1531
+ }
1532
+ function isTimeoutAbort(reason) {
1533
+ return typeof reason === "object" && reason !== null && "code" in reason && reason.code === "timeout";
1534
+ }
1535
+
1260
1536
  // ../protocol/src/computer-use-bridge.ts
1261
1537
  var OPENSTEER_COMPUTER_USE_BRIDGE_SYMBOL = /* @__PURE__ */ Symbol.for("@opensteer/computer-use-bridge");
1262
1538
 
@@ -1741,6 +2017,7 @@ function createPlaywrightComputerUseBridge(context) {
1741
2017
  const startedAt = Date.now();
1742
2018
  const actionController = context.resolveController(input.pageRef);
1743
2019
  const action = input.action;
2020
+ let boundary;
1744
2021
  let actionMs = 0;
1745
2022
  let waitMs = 0;
1746
2023
  const actionStartedAt = Date.now();
@@ -1806,7 +2083,12 @@ function createPlaywrightComputerUseBridge(context) {
1806
2083
  await context.flushPendingPageTasks(actionController.sessionRef);
1807
2084
  if (action.type !== "screenshot" && action.type !== "wait") {
1808
2085
  const waitStartedAt = Date.now();
1809
- await input.policySettle(actionController.pageRef);
2086
+ boundary = await context.settleActionBoundary(actionController, {
2087
+ signal: input.signal,
2088
+ ...input.snapshot === void 0 ? {} : { snapshot: input.snapshot },
2089
+ remainingMs: input.remainingMs,
2090
+ policySettle: input.policySettle
2091
+ });
1810
2092
  waitMs = Date.now() - waitStartedAt;
1811
2093
  } else if (action.type === "wait") {
1812
2094
  waitMs = actionMs;
@@ -1818,7 +2100,11 @@ function createPlaywrightComputerUseBridge(context) {
1818
2100
  let resultController = context.resolveController(resultPageRef);
1819
2101
  if (action.type !== "screenshot" && action.type !== "wait" && resultController.pageRef !== actionController.pageRef) {
1820
2102
  const popupWaitStartedAt = Date.now();
1821
- await input.policySettle(resultController.pageRef);
2103
+ await context.settleActionBoundary(resultController, {
2104
+ signal: input.signal,
2105
+ remainingMs: input.remainingMs,
2106
+ policySettle: input.policySettle
2107
+ });
1822
2108
  waitMs += Date.now() - popupWaitStartedAt;
1823
2109
  await context.flushPendingPageTasks(actionController.sessionRef);
1824
2110
  resultController = context.resolveController(resultController.pageRef);
@@ -1842,7 +2128,8 @@ function createPlaywrightComputerUseBridge(context) {
1842
2128
  actionMs,
1843
2129
  waitMs,
1844
2130
  totalMs: Date.now() - startedAt
1845
- }
2131
+ },
2132
+ ...boundary === void 0 ? {} : { boundary }
1846
2133
  };
1847
2134
  }
1848
2135
  };
@@ -2875,10 +3162,12 @@ function createPlaywrightDomActionBridge(context) {
2875
3162
  },
2876
3163
  async finalizeDomAction(pageRef, options) {
2877
3164
  const controller = context.resolveController(pageRef);
2878
- await context.flushPendingPageTasks(controller.sessionRef);
2879
- await options.policySettle(pageRef);
2880
- await context.flushPendingPageTasks(controller.sessionRef);
2881
- await context.flushDomUpdateTask(controller);
3165
+ return context.settleActionBoundary(controller, {
3166
+ signal: options.signal,
3167
+ ...options.snapshot === void 0 ? {} : { snapshot: options.snapshot },
3168
+ remainingMs: options.remainingMs,
3169
+ policySettle: options.policySettle
3170
+ });
2882
3171
  }
2883
3172
  };
2884
3173
  }
@@ -3146,6 +3435,121 @@ async function releaseObject(controller, objectId) {
3146
3435
  await controller.cdp.send("Runtime.releaseObject", { objectId }).catch(() => void 0);
3147
3436
  }
3148
3437
 
3438
+ // src/action-settle.ts
3439
+ var DEFAULT_PLAYWRIGHT_ACTION_SETTLE_TIMEOUT_MS = CROSS_DOCUMENT_INTERACTION_TIMEOUT_MS;
3440
+ function clampPlaywrightActionSettleTimeout(timeoutMs) {
3441
+ if (timeoutMs === void 0) {
3442
+ return DEFAULT_PLAYWRIGHT_ACTION_SETTLE_TIMEOUT_MS;
3443
+ }
3444
+ return Math.max(0, Math.min(DEFAULT_PLAYWRIGHT_ACTION_SETTLE_TIMEOUT_MS, timeoutMs));
3445
+ }
3446
+ function createPlaywrightActionSettler(context) {
3447
+ const installScript = buildPostLoadTrackerInstallScript();
3448
+ const readExpression = buildPostLoadTrackerReadExpression();
3449
+ async function installTracker(controller) {
3450
+ if (!controller.settleTrackerRegistered) {
3451
+ await controller.page.addInitScript(installScript);
3452
+ controller.settleTrackerRegistered = true;
3453
+ }
3454
+ try {
3455
+ await controller.cdp.send("Runtime.evaluate", {
3456
+ expression: installScript,
3457
+ returnByValue: true,
3458
+ awaitPromise: true
3459
+ });
3460
+ } catch (error) {
3461
+ if (controller.lifecycleState === "closed" || isContextClosedError(error)) {
3462
+ return;
3463
+ }
3464
+ throw normalizePlaywrightError(error, controller.pageRef);
3465
+ }
3466
+ }
3467
+ async function readTrackerState(controller) {
3468
+ try {
3469
+ const evaluated = await controller.cdp.send("Runtime.evaluate", {
3470
+ expression: readExpression,
3471
+ returnByValue: true,
3472
+ awaitPromise: true
3473
+ });
3474
+ return normalizePostLoadTrackerState(evaluated.result?.value);
3475
+ } catch (error) {
3476
+ if (isIgnorableTrackerReadError(error)) {
3477
+ return void 0;
3478
+ }
3479
+ throw normalizePlaywrightError(error, controller.pageRef);
3480
+ }
3481
+ }
3482
+ async function settle(options) {
3483
+ const { controller, timeoutMs, signal, snapshot, policySettle } = options;
3484
+ if (timeoutMs <= 0) {
3485
+ return {
3486
+ trigger: "dom-action",
3487
+ crossDocument: false,
3488
+ bootstrapSettled: false,
3489
+ timedOutPhase: "bootstrap"
3490
+ };
3491
+ }
3492
+ await context.flushPendingPageTasks(controller.sessionRef);
3493
+ let boundary;
3494
+ if (snapshot === void 0) {
3495
+ if (policySettle) {
3496
+ if (signal?.aborted) {
3497
+ throw signal.reason ?? abortError2();
3498
+ }
3499
+ await policySettle(controller.pageRef, "dom-action");
3500
+ }
3501
+ boundary = {
3502
+ trigger: "dom-action",
3503
+ crossDocument: false,
3504
+ bootstrapSettled: true
3505
+ };
3506
+ } else {
3507
+ await installTracker(controller);
3508
+ boundary = await waitForActionBoundary({
3509
+ timeoutMs,
3510
+ ...signal === void 0 ? {} : { signal },
3511
+ snapshot,
3512
+ getCurrentMainFrameDocumentRef: () => context.getMainFrameDocumentRef(controller),
3513
+ waitForNavigationContentLoaded: async (remainingMs) => {
3514
+ try {
3515
+ await controller.page.waitForLoadState("domcontentloaded", {
3516
+ timeout: remainingMs
3517
+ });
3518
+ } catch (error) {
3519
+ if (controller.lifecycleState === "closed" || isContextClosedError(error)) {
3520
+ return;
3521
+ }
3522
+ throw normalizePlaywrightError(error, controller.pageRef);
3523
+ }
3524
+ },
3525
+ readTrackerState: () => readTrackerState(controller),
3526
+ throwBackgroundError: () => context.throwBackgroundError(controller),
3527
+ isPageClosed: () => controller.lifecycleState === "closed"
3528
+ });
3529
+ if (policySettle) {
3530
+ await policySettle(controller.pageRef, boundary.trigger);
3531
+ }
3532
+ }
3533
+ await context.flushPendingPageTasks(controller.sessionRef);
3534
+ if (controller.lifecycleState !== "closed") {
3535
+ await context.flushDomUpdateTask(controller);
3536
+ }
3537
+ return boundary;
3538
+ }
3539
+ return {
3540
+ installTracker,
3541
+ settle
3542
+ };
3543
+ }
3544
+ function abortError2() {
3545
+ return new DOMException("The operation was aborted", "AbortError");
3546
+ }
3547
+ function isIgnorableTrackerReadError(error) {
3548
+ return isContextClosedError(error) || error instanceof Error && /Execution context was destroyed|Cannot find context|Inspected target navigated or closed/i.test(
3549
+ error.message
3550
+ );
3551
+ }
3552
+
3149
3553
  // src/storage-capture.ts
3150
3554
  var ACTIVATION_PATH = "/__opensteer_storage_capture__";
3151
3555
  var ACTIVATION_TIMEOUT_MS = 15e3;
@@ -3352,6 +3756,12 @@ var PlaywrightBrowserCoreEngine = class _PlaywrightBrowserCoreEngine {
3352
3756
  pageByPlaywrightPage = /* @__PURE__ */ new WeakMap();
3353
3757
  pendingPopupOpeners = /* @__PURE__ */ new WeakMap();
3354
3758
  preassignedPopupPageRefs = /* @__PURE__ */ new WeakMap();
3759
+ actionSettler = createPlaywrightActionSettler({
3760
+ flushPendingPageTasks: (sessionRef) => this.flushPendingPageTasks(sessionRef),
3761
+ flushDomUpdateTask: (controller) => this.flushDomUpdateTask(controller),
3762
+ getMainFrameDocumentRef: (controller) => controller.mainFrameRef === void 0 ? void 0 : this.frames.get(controller.mainFrameRef)?.currentDocument.documentRef,
3763
+ throwBackgroundError: (controller) => this.throwBackgroundError(controller)
3764
+ });
3355
3765
  pageCounter = 0;
3356
3766
  frameCounter = 0;
3357
3767
  documentCounter = 0;
@@ -3407,6 +3817,13 @@ var PlaywrightBrowserCoreEngine = class _PlaywrightBrowserCoreEngine {
3407
3817
  resolveController: (pageRef) => this.requirePage(pageRef),
3408
3818
  flushPendingPageTasks: (sessionRef) => this.flushPendingPageTasks(sessionRef),
3409
3819
  flushDomUpdateTask: (controller) => this.flushDomUpdateTask(controller),
3820
+ settleActionBoundary: (controller, options) => this.actionSettler.settle({
3821
+ controller,
3822
+ timeoutMs: clampPlaywrightActionSettleTimeout(options.remainingMs()),
3823
+ ...options.signal === void 0 ? {} : { signal: options.signal },
3824
+ ...options.snapshot === void 0 ? {} : { snapshot: options.snapshot },
3825
+ ...options.policySettle === void 0 ? {} : { policySettle: options.policySettle }
3826
+ }),
3410
3827
  requireMainFrame: (controller) => this.requireMainFrame(controller),
3411
3828
  drainQueuedEvents: (pageRef) => this.drainQueuedEvents(pageRef),
3412
3829
  withModifiers: (page, modifiers, action) => this.withModifiers(page, modifiers, action)
@@ -3418,6 +3835,13 @@ var PlaywrightBrowserCoreEngine = class _PlaywrightBrowserCoreEngine {
3418
3835
  resolveController: (pageRef) => this.requirePage(pageRef),
3419
3836
  flushPendingPageTasks: (sessionRef) => this.flushPendingPageTasks(sessionRef),
3420
3837
  flushDomUpdateTask: (controller) => this.flushDomUpdateTask(controller),
3838
+ settleActionBoundary: (controller, options) => this.actionSettler.settle({
3839
+ controller,
3840
+ timeoutMs: clampPlaywrightActionSettleTimeout(options.remainingMs()),
3841
+ ...options.signal === void 0 ? {} : { signal: options.signal },
3842
+ ...options.snapshot === void 0 ? {} : { snapshot: options.snapshot },
3843
+ ...options.policySettle === void 0 ? {} : { policySettle: options.policySettle }
3844
+ }),
3421
3845
  locateBackendNode: (document, backendNodeId) => createNodeLocator(
3422
3846
  document.documentRef,
3423
3847
  document.documentEpoch,
@@ -4512,6 +4936,7 @@ var PlaywrightBrowserCoreEngine = class _PlaywrightBrowserCoreEngine {
4512
4936
  backgroundTasks: /* @__PURE__ */ new Set(),
4513
4937
  domUpdateTask: void 0,
4514
4938
  backgroundError: void 0,
4939
+ settleTrackerRegistered: false,
4515
4940
  openerPageRef: void 0,
4516
4941
  mainFrameRef: void 0,
4517
4942
  lifecycleState: "open",
@@ -4528,6 +4953,7 @@ var PlaywrightBrowserCoreEngine = class _PlaywrightBrowserCoreEngine {
4528
4953
  await cdp.send("DOM.enable", { includeWhitespace: "none" });
4529
4954
  await cdp.send("DOMStorage.enable");
4530
4955
  await cdp.send("DOM.getDocument", { depth: 0 });
4956
+ await this.actionSettler.installTracker(controller);
4531
4957
  cdp.on(
4532
4958
  "Page.frameAttached",
4533
4959
  (payload) => this.runControllerEvent(