opensteer 0.9.6 → 0.9.7

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 (33) hide show
  1. package/dist/{chunk-BVRIPCWA.js → chunk-3OHKIPBD.js} +316 -465
  2. package/dist/chunk-3OHKIPBD.js.map +1 -0
  3. package/dist/{chunk-L4NF74KI.js → chunk-52UNH5UW.js} +5 -5
  4. package/dist/{chunk-L4NF74KI.js.map → chunk-52UNH5UW.js.map} +1 -1
  5. package/dist/{chunk-3XBQRZZC.js → chunk-PJXN7HED.js} +115 -14
  6. package/dist/chunk-PJXN7HED.js.map +1 -0
  7. package/dist/{chunk-3I3A5OLB.js → chunk-R33BXCMQ.js} +16 -7
  8. package/dist/chunk-R33BXCMQ.js.map +1 -0
  9. package/dist/{chunk-T5P2QGZ3.js → chunk-U4BUCIZ4.js} +153 -12
  10. package/dist/chunk-U4BUCIZ4.js.map +1 -0
  11. package/dist/cli/bin.cjs +663 -544
  12. package/dist/cli/bin.cjs.map +1 -1
  13. package/dist/cli/bin.js +66 -50
  14. package/dist/cli/bin.js.map +1 -1
  15. package/dist/index.cjs +584 -495
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +26 -50
  18. package/dist/index.d.ts +26 -50
  19. package/dist/index.js +4 -4
  20. package/dist/local-view/public/assets/app.js +10 -1
  21. package/dist/local-view/serve-entry.cjs +687 -494
  22. package/dist/local-view/serve-entry.cjs.map +1 -1
  23. package/dist/local-view/serve-entry.js +2 -2
  24. package/dist/opensteer-CY2QUJEG.js +6 -0
  25. package/dist/{opensteer-UGA6YBRN.js.map → opensteer-CY2QUJEG.js.map} +1 -1
  26. package/dist/{session-control-U3L5H2ZI.js → session-control-FIP6ZJLH.js} +4 -4
  27. package/dist/{session-control-U3L5H2ZI.js.map → session-control-FIP6ZJLH.js.map} +1 -1
  28. package/package.json +7 -7
  29. package/dist/chunk-3I3A5OLB.js.map +0 -1
  30. package/dist/chunk-3XBQRZZC.js.map +0 -1
  31. package/dist/chunk-BVRIPCWA.js.map +0 -1
  32. package/dist/chunk-T5P2QGZ3.js.map +0 -1
  33. package/dist/opensteer-UGA6YBRN.js +0 -6
package/dist/cli/bin.cjs CHANGED
@@ -1662,31 +1662,131 @@ function isAlreadyExistsError(error) {
1662
1662
  }
1663
1663
  async function withFilesystemLock(lockPath, task) {
1664
1664
  await ensureDirectory(path10__default.default.dirname(lockPath));
1665
+ const ownerToken = crypto.randomUUID();
1665
1666
  let attempt = 0;
1666
1667
  while (true) {
1667
1668
  try {
1668
1669
  await promises.mkdir(lockPath);
1670
+ const acquiredAt = Date.now();
1671
+ await writeLockMetadata(lockPath, {
1672
+ version: LOCK_METADATA_VERSION,
1673
+ ownerToken,
1674
+ pid: process.pid,
1675
+ acquiredAt,
1676
+ heartbeatAt: acquiredAt
1677
+ });
1669
1678
  break;
1670
1679
  } catch (error) {
1671
1680
  if (!isAlreadyExistsError(error)) {
1672
1681
  throw error;
1673
1682
  }
1683
+ if (await tryRecoverFilesystemLock(lockPath)) {
1684
+ attempt = 0;
1685
+ continue;
1686
+ }
1674
1687
  const delayMs = LOCK_RETRY_DELAYS_MS[Math.min(attempt, LOCK_RETRY_DELAYS_MS.length - 1)];
1675
1688
  attempt += 1;
1676
1689
  await new Promise((resolve4) => setTimeout(resolve4, delayMs));
1677
1690
  }
1678
1691
  }
1692
+ const heartbeatTimer = setInterval(() => {
1693
+ void touchLockMetadata(lockPath, ownerToken);
1694
+ }, LOCK_HEARTBEAT_INTERVAL_MS);
1695
+ heartbeatTimer.unref?.();
1679
1696
  try {
1680
1697
  return await task();
1681
1698
  } finally {
1682
- await promises.rm(lockPath, { recursive: true, force: true });
1699
+ clearInterval(heartbeatTimer);
1700
+ const metadata = await readLockMetadata(lockPath);
1701
+ if (metadata?.ownerToken === ownerToken) {
1702
+ await promises.rm(lockPath, { recursive: true, force: true });
1703
+ }
1704
+ }
1705
+ }
1706
+ async function tryRecoverFilesystemLock(lockPath) {
1707
+ if (!await shouldRecoverFilesystemLock(lockPath)) {
1708
+ return false;
1709
+ }
1710
+ await promises.rm(lockPath, { recursive: true, force: true });
1711
+ return true;
1712
+ }
1713
+ async function shouldRecoverFilesystemLock(lockPath) {
1714
+ const metadata = await readLockMetadata(lockPath);
1715
+ if (metadata !== void 0) {
1716
+ if (isProcessRunning2(metadata.pid)) {
1717
+ return false;
1718
+ }
1719
+ return Date.now() - metadata.heartbeatAt >= LOCK_ORPHAN_GRACE_MS;
1720
+ }
1721
+ const lockStat = await promises.stat(lockPath).catch(() => void 0);
1722
+ if (lockStat === void 0) {
1723
+ return false;
1724
+ }
1725
+ return Date.now() - lockStat.mtimeMs >= LOCK_METADATALESS_STALE_MS;
1726
+ }
1727
+ async function readLockMetadata(lockPath) {
1728
+ const metadataPath = path10__default.default.join(lockPath, LOCK_METADATA_FILENAME);
1729
+ if (!await pathExists(metadataPath)) {
1730
+ return void 0;
1731
+ }
1732
+ try {
1733
+ const parsed = await readJsonFile(metadataPath);
1734
+ const pid = parsed.pid;
1735
+ const acquiredAt = parsed.acquiredAt;
1736
+ const heartbeatAt = parsed.heartbeatAt;
1737
+ if (parsed.version !== LOCK_METADATA_VERSION || typeof parsed.ownerToken !== "string" || parsed.ownerToken.length === 0 || typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || typeof acquiredAt !== "number" || !Number.isFinite(acquiredAt) || typeof heartbeatAt !== "number" || !Number.isFinite(heartbeatAt)) {
1738
+ return void 0;
1739
+ }
1740
+ return {
1741
+ version: LOCK_METADATA_VERSION,
1742
+ ownerToken: parsed.ownerToken,
1743
+ pid,
1744
+ acquiredAt,
1745
+ heartbeatAt
1746
+ };
1747
+ } catch {
1748
+ return void 0;
1749
+ }
1750
+ }
1751
+ async function writeLockMetadata(lockPath, metadata) {
1752
+ try {
1753
+ await writeJsonFileAtomic(path10__default.default.join(lockPath, LOCK_METADATA_FILENAME), metadata);
1754
+ } catch (error) {
1755
+ await promises.rm(lockPath, { recursive: true, force: true }).catch(() => void 0);
1756
+ throw error;
1757
+ }
1758
+ }
1759
+ async function touchLockMetadata(lockPath, ownerToken) {
1760
+ const metadata = await readLockMetadata(lockPath);
1761
+ if (metadata === void 0 || metadata.ownerToken !== ownerToken) {
1762
+ return;
1763
+ }
1764
+ await writeJsonFileAtomic(path10__default.default.join(lockPath, LOCK_METADATA_FILENAME), {
1765
+ ...metadata,
1766
+ heartbeatAt: Date.now()
1767
+ }).catch(() => void 0);
1768
+ }
1769
+ function isProcessRunning2(pid) {
1770
+ if (!Number.isInteger(pid) || pid <= 0) {
1771
+ return false;
1772
+ }
1773
+ try {
1774
+ process.kill(pid, 0);
1775
+ return true;
1776
+ } catch (error) {
1777
+ return error?.code === "EPERM";
1683
1778
  }
1684
1779
  }
1685
- var LOCK_RETRY_DELAYS_MS;
1780
+ var LOCK_RETRY_DELAYS_MS, LOCK_METADATA_FILENAME, LOCK_METADATA_VERSION, LOCK_HEARTBEAT_INTERVAL_MS, LOCK_ORPHAN_GRACE_MS, LOCK_METADATALESS_STALE_MS;
1686
1781
  var init_filesystem = __esm({
1687
1782
  "../runtime-core/src/internal/filesystem.ts"() {
1688
1783
  init_json();
1689
1784
  LOCK_RETRY_DELAYS_MS = [1, 2, 5, 10, 20, 50];
1785
+ LOCK_METADATA_FILENAME = "owner.json";
1786
+ LOCK_METADATA_VERSION = 1;
1787
+ LOCK_HEARTBEAT_INTERVAL_MS = 1e3;
1788
+ LOCK_ORPHAN_GRACE_MS = 2e3;
1789
+ LOCK_METADATALESS_STALE_MS = 3e4;
1690
1790
  }
1691
1791
  });
1692
1792
  function normalizeScope(scope) {
@@ -2294,7 +2394,7 @@ var init_version = __esm({
2294
2394
  "../protocol/src/version.ts"() {
2295
2395
  init_json2();
2296
2396
  OPENSTEER_PROTOCOL_NAME = "opensteer";
2297
- OPENSTEER_PROTOCOL_COMPATIBILITY_REVISION = 2;
2397
+ OPENSTEER_PROTOCOL_COMPATIBILITY_REVISION = 3;
2298
2398
  OPENSTEER_PROTOCOL_VERSION = `0.${OPENSTEER_PROTOCOL_COMPATIBILITY_REVISION}.0`;
2299
2399
  OPENSTEER_PROTOCOL_MEDIA_TYPE = `application/vnd.${OPENSTEER_PROTOCOL_NAME}+json;version=${OPENSTEER_PROTOCOL_VERSION}`;
2300
2400
  OPENSTEER_PROTOCOL_REST_BASE_PATH = `/api/v${OPENSTEER_PROTOCOL_COMPATIBILITY_REVISION}`;
@@ -6677,15 +6777,15 @@ function assertValidSemanticOperationInput(name, input) {
6677
6777
  }
6678
6778
  );
6679
6779
  }
6680
- var opensteerComputerAnnotationNames, opensteerExposedSemanticOperationNames, opensteerPackageRunnableSemanticOperationNames, snapshotModeSchema, viewportSchema, opensteerBrowserLaunchOptionsSchema, attachBrowserOptionsSchema, opensteerBrowserOptionsSchema, opensteerBrowserContextOptionsSchema, targetByElementSchema2, targetByPersistSchema2, targetBySelectorSchema2, opensteerTargetInputSchema, opensteerResolvedTargetSchema, opensteerActionResultSchema, opensteerSnapshotCounterSchema, opensteerSessionStateSchema, opensteerOpenInputSchema, opensteerPageListInputSchema, opensteerPageListOutputSchema, opensteerPageNewInputSchema, opensteerPageActivateInputSchema, opensteerPageCloseInputSchema, opensteerPageCloseOutputSchema, opensteerPageGotoInputSchema, opensteerPageEvaluateInputSchema, opensteerPageEvaluateOutputSchema, opensteerAddInitScriptInputSchema, opensteerAddInitScriptOutputSchema, opensteerCapturedScriptSchema, opensteerCaptureScriptsInputSchema, opensteerCaptureScriptsOutputSchema, opensteerPageSnapshotInputSchema, opensteerPageSnapshotOutputSchema, opensteerComputerMouseButtonSchema, opensteerComputerKeyModifierSchema, opensteerDomClickInputSchema, opensteerDomHoverInputSchema, opensteerDomInputInputSchema, opensteerDomScrollInputSchema, opensteerExtractTemplateSchema, opensteerDomExtractInputSchema, jsonValueSchema2, opensteerDomExtractOutputSchema, opensteerSessionCloseInputSchema, opensteerSessionCloseOutputSchema, opensteerComputerAnnotationSchema, opensteerComputerClickActionSchema, opensteerComputerMoveActionSchema, opensteerComputerScrollActionSchema, opensteerComputerTypeActionSchema, opensteerComputerKeyActionSchema, opensteerComputerDragActionSchema, opensteerComputerScreenshotActionSchema, opensteerComputerWaitActionSchema, opensteerComputerActionSchema, opensteerComputerScreenshotOptionsSchema, opensteerComputerExecuteInputSchema, opensteerComputerTracePointSchema, opensteerComputerTraceEnrichmentSchema, opensteerComputerExecuteTimingSchema, opensteerComputerDisplayScaleSchema, opensteerComputerExecuteOutputSchema, opensteerSemanticOperationSpecificationsBase, exposedSemanticOperationNameSet, opensteerSemanticOperationSpecificationsInternal, opensteerSemanticOperationSpecifications, opensteerSemanticOperationSpecificationMap, semanticRestBasePath, opensteerSemanticRestEndpoints;
6780
+ var opensteerComputerAnnotationNames, opensteerExposedSemanticOperationNames, opensteerPackageRunnableSemanticOperationNames, snapshotModeSchema, viewportSchema, opensteerBrowserLaunchOptionsSchema, attachBrowserOptionsSchema, opensteerBrowserOptionsSchema, opensteerBrowserContextOptionsSchema, targetByElementSchema2, targetByPersistSchema2, targetBySelectorSchema2, opensteerTargetInputSchema, opensteerActionResultSchema, opensteerSnapshotCounterSchema, opensteerNavigationSummarySchema, opensteerOpenInputSchema, opensteerPageListInputSchema, opensteerPageListOutputSchema, opensteerPageNewInputSchema, opensteerPageActivateInputSchema, opensteerPageCloseInputSchema, opensteerPageCloseOutputSchema, opensteerPageNewOutputSchema, opensteerPageGotoInputSchema, opensteerPageEvaluateInputSchema, opensteerPageEvaluateOutputSchema, opensteerAddInitScriptInputSchema, opensteerAddInitScriptOutputSchema, opensteerCapturedScriptSchema, opensteerCaptureScriptsInputSchema, opensteerCaptureScriptsOutputSchema, opensteerPageSnapshotInputSchema, opensteerPageSnapshotOutputSchema, opensteerComputerMouseButtonSchema, opensteerComputerKeyModifierSchema, opensteerDomClickInputSchema, opensteerDomHoverInputSchema, opensteerDomInputInputSchema, opensteerDomScrollInputSchema, opensteerExtractTemplateSchema, opensteerDomExtractInputSchema, jsonValueSchema2, opensteerDomExtractOutputSchema, opensteerSessionCloseInputSchema, opensteerSessionCloseOutputSchema, opensteerComputerAnnotationSchema, opensteerComputerClickActionSchema, opensteerComputerMoveActionSchema, opensteerComputerScrollActionSchema, opensteerComputerTypeActionSchema, opensteerComputerKeyActionSchema, opensteerComputerDragActionSchema, opensteerComputerScreenshotActionSchema, opensteerComputerWaitActionSchema, opensteerComputerActionSchema, opensteerComputerScreenshotOptionsSchema, opensteerComputerExecuteInputSchema, opensteerScreenshotSummarySchema, opensteerComputerExecuteOutputSchema, opensteerSemanticOperationSpecificationsBase, exposedSemanticOperationNameSet, opensteerSemanticOperationSpecificationsInternal, opensteerSemanticOperationSpecifications, opensteerSemanticOperationSpecificationMap, semanticRestBasePath, opensteerSemanticRestEndpoints;
6681
6781
  var init_semantic = __esm({
6682
6782
  "../protocol/src/semantic.ts"() {
6683
6783
  init_json2();
6684
6784
  init_errors2();
6785
+ init_binary_location();
6685
6786
  init_identity2();
6686
6787
  init_geometry2();
6687
6788
  init_metadata2();
6688
- init_events2();
6689
6789
  init_envelopes();
6690
6790
  init_snapshots2();
6691
6791
  init_artifacts2();
@@ -6909,39 +7009,14 @@ var init_semantic = __esm({
6909
7009
  title: "OpensteerTargetInput"
6910
7010
  }
6911
7011
  );
6912
- opensteerResolvedTargetSchema = objectSchema(
6913
- {
6914
- pageRef: pageRefSchema,
6915
- frameRef: frameRefSchema,
6916
- documentRef: documentRefSchema,
6917
- documentEpoch: documentEpochSchema,
6918
- nodeRef: nodeRefSchema,
6919
- tagName: stringSchema(),
6920
- pathHint: stringSchema(),
6921
- persist: stringSchema(),
6922
- selectorUsed: stringSchema()
6923
- },
6924
- {
6925
- title: "OpensteerResolvedTarget",
6926
- required: [
6927
- "pageRef",
6928
- "frameRef",
6929
- "documentRef",
6930
- "documentEpoch",
6931
- "nodeRef",
6932
- "tagName",
6933
- "pathHint"
6934
- ]
6935
- }
6936
- );
6937
7012
  opensteerActionResultSchema = objectSchema(
6938
7013
  {
6939
- target: opensteerResolvedTargetSchema,
6940
- point: pointSchema
7014
+ tagName: stringSchema({ minLength: 1 }),
7015
+ persist: stringSchema({ minLength: 1 })
6941
7016
  },
6942
7017
  {
6943
7018
  title: "OpensteerActionResult",
6944
- required: ["target"]
7019
+ required: ["tagName"]
6945
7020
  }
6946
7021
  );
6947
7022
  opensteerSnapshotCounterSchema = objectSchema(
@@ -6987,16 +7062,14 @@ var init_semantic = __esm({
6987
7062
  ]
6988
7063
  }
6989
7064
  );
6990
- opensteerSessionStateSchema = objectSchema(
7065
+ opensteerNavigationSummarySchema = objectSchema(
6991
7066
  {
6992
- sessionRef: sessionRefSchema,
6993
- pageRef: pageRefSchema,
6994
7067
  url: stringSchema(),
6995
7068
  title: stringSchema()
6996
7069
  },
6997
7070
  {
6998
- title: "OpensteerSessionState",
6999
- required: ["sessionRef", "pageRef", "url", "title"]
7071
+ title: "OpensteerNavigationSummary",
7072
+ required: ["url", "title"]
7000
7073
  }
7001
7074
  );
7002
7075
  opensteerOpenInputSchema = objectSchema(
@@ -7064,6 +7137,17 @@ var init_semantic = __esm({
7064
7137
  required: ["closedPageRef", "pages"]
7065
7138
  }
7066
7139
  );
7140
+ opensteerPageNewOutputSchema = objectSchema(
7141
+ {
7142
+ pageRef: pageRefSchema,
7143
+ url: stringSchema(),
7144
+ title: stringSchema()
7145
+ },
7146
+ {
7147
+ title: "OpensteerPageNewOutput",
7148
+ required: ["pageRef", "url", "title"]
7149
+ }
7150
+ );
7067
7151
  opensteerPageGotoInputSchema = objectSchema(
7068
7152
  {
7069
7153
  url: stringSchema(),
@@ -7450,72 +7534,28 @@ var init_semantic = __esm({
7450
7534
  required: ["action"]
7451
7535
  }
7452
7536
  );
7453
- opensteerComputerTracePointSchema = objectSchema(
7454
- {
7455
- role: enumSchema(["point", "start", "end"]),
7456
- point: pointSchema,
7457
- hitTest: hitTestResultSchema,
7458
- target: opensteerResolvedTargetSchema
7459
- },
7460
- {
7461
- title: "OpensteerComputerTracePoint",
7462
- required: ["role", "point"]
7463
- }
7464
- );
7465
- opensteerComputerTraceEnrichmentSchema = objectSchema(
7466
- {
7467
- points: arraySchema(opensteerComputerTracePointSchema)
7468
- },
7469
- {
7470
- title: "OpensteerComputerTraceEnrichment",
7471
- required: ["points"]
7472
- }
7473
- );
7474
- opensteerComputerExecuteTimingSchema = objectSchema(
7475
- {
7476
- actionMs: integerSchema({ minimum: 0 }),
7477
- waitMs: integerSchema({ minimum: 0 }),
7478
- totalMs: integerSchema({ minimum: 0 })
7479
- },
7480
- {
7481
- title: "OpensteerComputerExecuteTiming",
7482
- required: ["actionMs", "waitMs", "totalMs"]
7483
- }
7484
- );
7485
- opensteerComputerDisplayScaleSchema = objectSchema(
7537
+ opensteerScreenshotSummarySchema = objectSchema(
7486
7538
  {
7487
- x: numberSchema({ exclusiveMinimum: 0 }),
7488
- y: numberSchema({ exclusiveMinimum: 0 })
7539
+ payload: externalBinaryLocationSchema,
7540
+ format: screenshotFormatSchema,
7541
+ size: sizeSchema,
7542
+ coordinateSpace: coordinateSpaceSchema,
7543
+ clip: rectSchema
7489
7544
  },
7490
7545
  {
7491
- title: "OpensteerComputerDisplayScale",
7492
- required: ["x", "y"]
7546
+ title: "OpensteerScreenshotSummary",
7547
+ required: ["payload", "format", "size", "coordinateSpace"]
7493
7548
  }
7494
7549
  );
7495
7550
  opensteerComputerExecuteOutputSchema = objectSchema(
7496
7551
  {
7497
- action: opensteerComputerActionSchema,
7498
- pageRef: pageRefSchema,
7499
- screenshot: screenshotArtifactSchema,
7500
- displayViewport: viewportMetricsSchema,
7501
- nativeViewport: viewportMetricsSchema,
7502
- displayScale: opensteerComputerDisplayScaleSchema,
7503
- events: arraySchema(opensteerEventSchema),
7504
- timing: opensteerComputerExecuteTimingSchema,
7505
- trace: opensteerComputerTraceEnrichmentSchema
7552
+ url: stringSchema(),
7553
+ title: stringSchema(),
7554
+ screenshot: opensteerScreenshotSummarySchema
7506
7555
  },
7507
7556
  {
7508
7557
  title: "OpensteerComputerExecuteOutput",
7509
- required: [
7510
- "action",
7511
- "pageRef",
7512
- "screenshot",
7513
- "displayViewport",
7514
- "nativeViewport",
7515
- "displayScale",
7516
- "events",
7517
- "timing"
7518
- ]
7558
+ required: ["url", "title", "screenshot"]
7519
7559
  }
7520
7560
  );
7521
7561
  opensteerSemanticOperationSpecificationsBase = [
@@ -7523,7 +7563,7 @@ var init_semantic = __esm({
7523
7563
  name: "session.open",
7524
7564
  description: "Open or resume the current Opensteer session and primary page.",
7525
7565
  inputSchema: opensteerOpenInputSchema,
7526
- outputSchema: opensteerSessionStateSchema,
7566
+ outputSchema: opensteerNavigationSummarySchema,
7527
7567
  requiredCapabilities: ["sessions.manage", "pages.manage"],
7528
7568
  resolveRequiredCapabilities: (input) => input.url === void 0 ? ["sessions.manage", "pages.manage"] : ["sessions.manage", "pages.manage", "pages.navigate"]
7529
7569
  }),
@@ -7538,7 +7578,7 @@ var init_semantic = __esm({
7538
7578
  name: "page.new",
7539
7579
  description: "Create and optionally navigate a new top-level page in the current session.",
7540
7580
  inputSchema: opensteerPageNewInputSchema,
7541
- outputSchema: opensteerSessionStateSchema,
7581
+ outputSchema: opensteerPageNewOutputSchema,
7542
7582
  requiredCapabilities: ["pages.manage"],
7543
7583
  resolveRequiredCapabilities: (input) => input.url === void 0 ? ["pages.manage"] : ["pages.manage", "pages.navigate"]
7544
7584
  }),
@@ -7546,7 +7586,7 @@ var init_semantic = __esm({
7546
7586
  name: "page.activate",
7547
7587
  description: "Activate an existing top-level page in the current session.",
7548
7588
  inputSchema: opensteerPageActivateInputSchema,
7549
- outputSchema: opensteerSessionStateSchema,
7589
+ outputSchema: opensteerNavigationSummarySchema,
7550
7590
  requiredCapabilities: ["pages.manage", "inspect.pages"]
7551
7591
  }),
7552
7592
  defineSemanticOperationSpec({
@@ -7560,7 +7600,7 @@ var init_semantic = __esm({
7560
7600
  name: "page.goto",
7561
7601
  description: "Navigate the current Opensteer page to a new URL.",
7562
7602
  inputSchema: opensteerPageGotoInputSchema,
7563
- outputSchema: opensteerSessionStateSchema,
7603
+ outputSchema: opensteerNavigationSummarySchema,
7564
7604
  requiredCapabilities: ["pages.navigate"]
7565
7605
  }),
7566
7606
  defineSemanticOperationSpec({
@@ -9802,16 +9842,37 @@ async function writePersistedSessionRecord(rootPath, record) {
9802
9842
  async function clearPersistedSessionRecord(rootPath, provider) {
9803
9843
  await promises.rm(resolveLiveSessionRecordPath(rootPath, provider), { force: true });
9804
9844
  }
9845
+ function getPersistedLocalBrowserSessionOwnership(record) {
9846
+ return record.ownership === "attached" ? "attached" : "owned";
9847
+ }
9848
+ async function isAttachedLocalBrowserSessionReachable(record) {
9849
+ if (getPersistedLocalBrowserSessionOwnership(record) !== "attached") {
9850
+ return false;
9851
+ }
9852
+ if (record.engine !== "playwright" || record.endpoint === void 0) {
9853
+ return false;
9854
+ }
9855
+ try {
9856
+ await inspectCdpEndpoint({
9857
+ endpoint: record.endpoint,
9858
+ timeoutMs: 1500
9859
+ });
9860
+ return true;
9861
+ } catch {
9862
+ return false;
9863
+ }
9864
+ }
9805
9865
  function isPersistedCloudSessionRecord(value) {
9806
9866
  return value.layout === OPENSTEER_LIVE_SESSION_LAYOUT && value.version === OPENSTEER_LIVE_SESSION_VERSION && value.provider === "cloud" && typeof value.sessionId === "string" && value.sessionId.length > 0 && typeof value.startedAt === "number" && Number.isFinite(value.startedAt) && typeof value.updatedAt === "number" && Number.isFinite(value.updatedAt);
9807
9867
  }
9808
9868
  function isPersistedLocalBrowserSessionRecord(value) {
9809
- return value.layout === OPENSTEER_LIVE_SESSION_LAYOUT && value.version === OPENSTEER_LIVE_SESSION_VERSION && value.provider === "local" && (value.engine === "playwright" || value.engine === "abp") && typeof value.pid === "number" && Number.isFinite(value.pid) && typeof value.startedAt === "number" && Number.isFinite(value.startedAt) && typeof value.updatedAt === "number" && Number.isFinite(value.updatedAt) && typeof value.userDataDir === "string" && value.userDataDir.length > 0;
9869
+ return value.layout === OPENSTEER_LIVE_SESSION_LAYOUT && value.version === OPENSTEER_LIVE_SESSION_VERSION && value.provider === "local" && (value.engine === "playwright" || value.engine === "abp") && (value.ownership === void 0 || value.ownership === "owned" || value.ownership === "attached") && typeof value.pid === "number" && Number.isFinite(value.pid) && typeof value.startedAt === "number" && Number.isFinite(value.startedAt) && typeof value.updatedAt === "number" && Number.isFinite(value.updatedAt) && typeof value.userDataDir === "string" && value.userDataDir.length > 0;
9810
9870
  }
9811
9871
  var OPENSTEER_LIVE_SESSION_LAYOUT, OPENSTEER_LIVE_SESSION_VERSION;
9812
9872
  var init_live_session = __esm({
9813
9873
  "src/live-session.ts"() {
9814
9874
  init_filesystem2();
9875
+ init_cdp_discovery();
9815
9876
  OPENSTEER_LIVE_SESSION_LAYOUT = "opensteer-session";
9816
9877
  OPENSTEER_LIVE_SESSION_VERSION = 1;
9817
9878
  }
@@ -10300,19 +10361,40 @@ var init_service = __esm({
10300
10361
  }
10301
10362
  });
10302
10363
  function buildLocalViewSessionId(input) {
10364
+ const ownership = input.ownership ?? "owned";
10365
+ const identity = ownership === "attached" ? input.endpoint ?? input.remoteDebuggingUrl ?? input.baseUrl ?? "attached" : `pid:${String(input.pid ?? 0)}`;
10303
10366
  const hash = crypto.createHash("sha256").update(`${input.rootPath}
10304
- ${String(input.pid)}
10367
+ ${ownership}
10368
+ ${identity}
10305
10369
  ${String(input.startedAt)}`).digest("hex");
10306
10370
  return `local_${hash.slice(0, 24)}`;
10307
10371
  }
10372
+ function buildLocalViewSessionIdForRecord(input) {
10373
+ const ownership = getPersistedLocalBrowserSessionOwnership(input.live);
10374
+ if (ownership === "attached") {
10375
+ return buildLocalViewSessionId({
10376
+ rootPath: input.rootPath,
10377
+ ownership,
10378
+ startedAt: input.live.startedAt,
10379
+ ...input.live.endpoint === void 0 ? {} : { endpoint: input.live.endpoint },
10380
+ ...input.live.baseUrl === void 0 ? {} : { baseUrl: input.live.baseUrl },
10381
+ ...input.live.remoteDebuggingUrl === void 0 ? {} : { remoteDebuggingUrl: input.live.remoteDebuggingUrl }
10382
+ });
10383
+ }
10384
+ return buildLocalViewSessionId({
10385
+ rootPath: input.rootPath,
10386
+ ownership,
10387
+ startedAt: input.live.startedAt,
10388
+ pid: input.live.pid
10389
+ });
10390
+ }
10308
10391
  function createLocalViewSessionManifest(input) {
10309
10392
  return {
10310
10393
  layout: OPENSTEER_LOCAL_VIEW_SESSION_LAYOUT,
10311
10394
  version: OPENSTEER_LOCAL_VIEW_SESSION_VERSION,
10312
- sessionId: buildLocalViewSessionId({
10395
+ sessionId: buildLocalViewSessionIdForRecord({
10313
10396
  rootPath: input.rootPath,
10314
- pid: input.live.pid,
10315
- startedAt: input.live.startedAt
10397
+ live: input.live
10316
10398
  }),
10317
10399
  rootPath: input.rootPath,
10318
10400
  ...input.workspace === void 0 ? {} : { workspace: input.workspace },
@@ -10363,6 +10445,7 @@ var OPENSTEER_LOCAL_VIEW_SESSION_LAYOUT, OPENSTEER_LOCAL_VIEW_SESSION_VERSION;
10363
10445
  var init_session_manifest = __esm({
10364
10446
  "src/local-view/session-manifest.ts"() {
10365
10447
  init_filesystem2();
10448
+ init_live_session();
10366
10449
  init_runtime_dir();
10367
10450
  OPENSTEER_LOCAL_VIEW_SESSION_LAYOUT = "opensteer-local-view-session";
10368
10451
  OPENSTEER_LOCAL_VIEW_SESSION_VERSION = 1;
@@ -10531,6 +10614,7 @@ function toPersistedLocalBrowserSessionRecord(workspace, live) {
10531
10614
  version: 1,
10532
10615
  provider: "local",
10533
10616
  ...workspace === void 0 ? {} : { workspace },
10617
+ ownership: live.ownership,
10534
10618
  engine: live.engine,
10535
10619
  ...live.endpoint === void 0 ? {} : { endpoint: live.endpoint },
10536
10620
  ...live.baseUrl === void 0 ? {} : { baseUrl: live.baseUrl },
@@ -10546,6 +10630,7 @@ function toPersistedLocalBrowserSessionRecord(workspace, live) {
10546
10630
  function toWorkspaceLiveBrowserRecord(record) {
10547
10631
  return {
10548
10632
  mode: "persistent",
10633
+ ownership: getPersistedLocalBrowserSessionOwnership(record),
10549
10634
  engine: record.engine,
10550
10635
  ...record.endpoint === void 0 ? {} : { endpoint: record.endpoint },
10551
10636
  ...record.baseUrl === void 0 ? {} : { baseUrl: record.baseUrl },
@@ -10572,7 +10657,12 @@ function isAttachBrowserOptions(browser) {
10572
10657
  async function resolveAttachEndpoint(browser) {
10573
10658
  const endpoint = browser?.endpoint?.trim();
10574
10659
  if (endpoint && endpoint.length > 0) {
10575
- return endpoint;
10660
+ const inspected = await inspectCdpEndpoint({
10661
+ endpoint,
10662
+ ...browser?.headers === void 0 ? {} : { headers: browser.headers },
10663
+ timeoutMs: DEFAULT_TIMEOUT_MS
10664
+ });
10665
+ return inspected.endpoint;
10576
10666
  }
10577
10667
  const selection = await selectAttachBrowserCandidate({
10578
10668
  timeoutMs: DEFAULT_TIMEOUT_MS
@@ -10998,7 +11088,7 @@ var init_browser_manager = __esm({
10998
11088
  }
10999
11089
  const liveRecord = await this.readLivePersistentBrowser(await this.ensureWorkspaceStore());
11000
11090
  return {
11001
- mode: this.mode,
11091
+ mode: liveRecord?.ownership === "attached" ? "attach" : this.mode,
11002
11092
  engine: liveRecord?.engine ?? this.engineName,
11003
11093
  ...this.workspace === void 0 ? {} : { workspace: this.workspace },
11004
11094
  live: liveRecord !== void 0
@@ -11126,6 +11216,7 @@ var init_browser_manager = __esm({
11126
11216
  });
11127
11217
  const liveRecord = {
11128
11218
  mode: "persistent",
11219
+ ownership: "owned",
11129
11220
  engine: "abp",
11130
11221
  baseUrl: launched.baseUrl,
11131
11222
  remoteDebuggingUrl: launched.remoteDebuggingUrl,
@@ -11212,11 +11303,78 @@ var init_browser_manager = __esm({
11212
11303
  }
11213
11304
  async createAttachEngine() {
11214
11305
  const endpoint = await resolveAttachEndpoint(this.browserOptions);
11215
- return this.createAttachedEngine({
11216
- endpoint,
11217
- ...this.browserOptions?.headers === void 0 ? {} : { headers: this.browserOptions.headers },
11218
- freshTab: this.browserOptions?.freshTab ?? true,
11219
- onDispose: async () => void 0
11306
+ if (this.workspace === void 0) {
11307
+ return this.createAttachedEngine({
11308
+ endpoint,
11309
+ ...this.browserOptions?.headers === void 0 ? {} : { headers: this.browserOptions.headers },
11310
+ freshTab: this.browserOptions?.freshTab ?? true,
11311
+ onDispose: async () => void 0
11312
+ });
11313
+ }
11314
+ const workspace = await this.ensureWorkspaceStore();
11315
+ return workspace.lock(async () => {
11316
+ const live = await this.readLivePersistentBrowser(workspace);
11317
+ if (live) {
11318
+ if (live.engine !== "playwright") {
11319
+ throw new Error(
11320
+ `workspace "${this.workspace}" already has a live ${live.engine} browser. Close it before attaching a Playwright browser.`
11321
+ );
11322
+ }
11323
+ if (live.ownership !== "attached") {
11324
+ throw new Error(
11325
+ `workspace "${this.workspace}" already has a live Opensteer-owned browser. Close it before attaching another browser.`
11326
+ );
11327
+ }
11328
+ if (live.endpoint === void 0) {
11329
+ throw new Error("workspace live browser record is missing a DevTools endpoint.");
11330
+ }
11331
+ if (live.endpoint !== endpoint) {
11332
+ throw new Error(
11333
+ `workspace "${this.workspace}" is already attached to a different browser endpoint. Close it before reattaching.`
11334
+ );
11335
+ }
11336
+ await bestEffortRegisterLocalViewSession({
11337
+ rootPath: workspace.rootPath,
11338
+ ...this.workspace === void 0 ? {} : { workspace: this.workspace },
11339
+ live: toPersistedLocalBrowserSessionRecord(this.workspace, live),
11340
+ ownership: "attached"
11341
+ });
11342
+ return this.createAttachedEngine({
11343
+ endpoint: live.endpoint,
11344
+ ...this.browserOptions?.headers === void 0 ? {} : { headers: this.browserOptions.headers },
11345
+ freshTab: this.browserOptions?.freshTab ?? true,
11346
+ onDispose: async () => void 0
11347
+ });
11348
+ }
11349
+ const liveRecord = {
11350
+ mode: "persistent",
11351
+ ownership: "attached",
11352
+ engine: "playwright",
11353
+ endpoint,
11354
+ pid: 0,
11355
+ startedAt: Date.now(),
11356
+ userDataDir: workspace.browserUserDataDir
11357
+ };
11358
+ await this.writeLivePersistentBrowser(workspace, liveRecord);
11359
+ const persistedLiveRecord = toPersistedLocalBrowserSessionRecord(this.workspace, liveRecord);
11360
+ await bestEffortRegisterLocalViewSession({
11361
+ rootPath: workspace.rootPath,
11362
+ ...this.workspace === void 0 ? {} : { workspace: this.workspace },
11363
+ live: persistedLiveRecord,
11364
+ ownership: "attached"
11365
+ });
11366
+ try {
11367
+ return await this.createAttachedEngine({
11368
+ endpoint,
11369
+ ...this.browserOptions?.headers === void 0 ? {} : { headers: this.browserOptions.headers },
11370
+ freshTab: this.browserOptions?.freshTab ?? true,
11371
+ onDispose: async () => void 0
11372
+ });
11373
+ } catch (error) {
11374
+ await this.unregisterLocalViewSessionForRecord(workspace.rootPath, persistedLiveRecord);
11375
+ await clearPersistedSessionRecord(workspace.rootPath, "local").catch(() => void 0);
11376
+ throw error;
11377
+ }
11220
11378
  });
11221
11379
  }
11222
11380
  async createPersistentEngine() {
@@ -11236,7 +11394,7 @@ var init_browser_manager = __esm({
11236
11394
  rootPath: workspace.rootPath,
11237
11395
  ...this.workspace === void 0 ? {} : { workspace: this.workspace },
11238
11396
  live: toPersistedLocalBrowserSessionRecord(this.workspace, live),
11239
- ownership: "owned"
11397
+ ownership: live.ownership
11240
11398
  });
11241
11399
  return this.createAttachedEngine({
11242
11400
  endpoint: live.endpoint,
@@ -11252,6 +11410,7 @@ var init_browser_manager = __esm({
11252
11410
  });
11253
11411
  const liveRecord = {
11254
11412
  mode: "persistent",
11413
+ ownership: "owned",
11255
11414
  engine: "playwright",
11256
11415
  endpoint: launched.endpoint,
11257
11416
  pid: launched.pid,
@@ -11381,7 +11540,20 @@ var init_browser_manager = __esm({
11381
11540
  if (live === void 0) {
11382
11541
  return void 0;
11383
11542
  }
11543
+ if (live.ownership === "attached") {
11544
+ const attachedRecord = toPersistedLocalBrowserSessionRecord(this.workspace, live);
11545
+ if (!await isAttachedLocalBrowserSessionReachable(attachedRecord)) {
11546
+ await this.unregisterLocalViewSessionForRecord(workspace.rootPath, attachedRecord);
11547
+ await clearPersistedSessionRecord(workspace.rootPath, "local").catch(() => void 0);
11548
+ return void 0;
11549
+ }
11550
+ return live;
11551
+ }
11384
11552
  if (!isProcessRunning(live.pid)) {
11553
+ await this.unregisterLocalViewSessionForRecord(
11554
+ workspace.rootPath,
11555
+ toPersistedLocalBrowserSessionRecord(this.workspace, live)
11556
+ );
11385
11557
  await clearPersistedSessionRecord(workspace.rootPath, "local").catch(() => void 0);
11386
11558
  return void 0;
11387
11559
  }
@@ -11429,6 +11601,10 @@ var init_browser_manager = __esm({
11429
11601
  workspace.rootPath,
11430
11602
  toPersistedLocalBrowserSessionRecord(this.workspace, live)
11431
11603
  );
11604
+ if (live.ownership === "attached") {
11605
+ await clearPersistedSessionRecord(workspace.rootPath, "local").catch(() => void 0);
11606
+ return;
11607
+ }
11432
11608
  if (live.engine === "playwright") {
11433
11609
  if (live.endpoint !== void 0) {
11434
11610
  await requestBrowserClose(live.endpoint).catch(() => void 0);
@@ -11457,10 +11633,18 @@ var init_browser_manager = __esm({
11457
11633
  }
11458
11634
  async unregisterLocalViewSessionForRecord(rootPath, record) {
11459
11635
  await bestEffortUnregisterLocalViewSession(
11460
- buildLocalViewSessionId({
11636
+ getPersistedLocalBrowserSessionOwnership(record) === "attached" ? buildLocalViewSessionId({
11637
+ rootPath,
11638
+ startedAt: record.startedAt,
11639
+ ownership: "attached",
11640
+ ...record.endpoint === void 0 ? {} : { endpoint: record.endpoint },
11641
+ ...record.baseUrl === void 0 ? {} : { baseUrl: record.baseUrl },
11642
+ ...record.remoteDebuggingUrl === void 0 ? {} : { remoteDebuggingUrl: record.remoteDebuggingUrl }
11643
+ }) : buildLocalViewSessionId({
11461
11644
  rootPath,
11462
- pid: record.pid,
11463
- startedAt: record.startedAt
11645
+ startedAt: record.startedAt,
11646
+ ownership: "owned",
11647
+ pid: record.pid
11464
11648
  })
11465
11649
  );
11466
11650
  }
@@ -12277,7 +12461,7 @@ var init_package = __esm({
12277
12461
  "../runtime-core/package.json"() {
12278
12462
  package_default2 = {
12279
12463
  name: "@opensteer/runtime-core",
12280
- version: "0.2.5",
12464
+ version: "0.2.6",
12281
12465
  description: "Shared semantic runtime for Opensteer local and cloud execution.",
12282
12466
  license: "MIT",
12283
12467
  type: "module",
@@ -12459,14 +12643,14 @@ var init_defaults = __esm({
12459
12643
  snapshot: 0
12460
12644
  };
12461
12645
  DOM_ACTION_VISUAL_STABILITY_PROFILES = {
12462
- "dom.click": { settleMs: 750, scope: "visible-frames", timeoutMs: 7e3 },
12463
- "dom.input": { settleMs: 750, scope: "visible-frames", timeoutMs: 7e3 },
12464
- "dom.scroll": { settleMs: 600, scope: "visible-frames", timeoutMs: 7e3 },
12646
+ "dom.click": { settleMs: 750, scope: "main-frame", timeoutMs: 7e3 },
12647
+ "dom.input": { settleMs: 750, scope: "main-frame", timeoutMs: 7e3 },
12648
+ "dom.scroll": { settleMs: 600, scope: "main-frame", timeoutMs: 7e3 },
12465
12649
  "dom.hover": { settleMs: 200, scope: "main-frame", timeoutMs: 2500 }
12466
12650
  };
12467
12651
  DEFAULT_DOM_ACTION_VISUAL_STABILITY_PROFILE = {
12468
12652
  settleMs: 750,
12469
- scope: "visible-frames",
12653
+ scope: "main-frame",
12470
12654
  timeoutMs: 7e3
12471
12655
  };
12472
12656
  NAVIGATION_VISUAL_STABILITY_PROFILE = {
@@ -12490,6 +12674,7 @@ var init_defaults = __esm({
12490
12674
  pageRef: input.pageRef,
12491
12675
  timeoutMs: effectiveTimeout,
12492
12676
  settleMs: profile.settleMs,
12677
+ ...input.observedMutationQuietMs === void 0 ? {} : { initialQuietMs: input.observedMutationQuietMs },
12493
12678
  scope: profile.scope
12494
12679
  });
12495
12680
  return true;
@@ -12510,15 +12695,20 @@ var init_defaults = __esm({
12510
12695
  return false;
12511
12696
  }
12512
12697
  try {
12513
- const startedAt = Date.now();
12514
- await input.engine.waitForPostLoadQuiet({
12515
- pageRef: input.pageRef,
12516
- timeoutMs: effectiveTimeout,
12517
- quietMs: DEFAULT_POST_LOAD_TRACKER_QUIET_WINDOW_MS,
12518
- captureWindowMs: Math.min(NAVIGATION_POST_LOAD_CAPTURE_WINDOW_MS, effectiveTimeout),
12519
- signal: input.signal
12520
- });
12521
- const visualTimeout = Math.max(0, effectiveTimeout - (Date.now() - startedAt));
12698
+ let visualTimeout = effectiveTimeout;
12699
+ let initialQuietMs = input.observedMutationQuietMs ?? 0;
12700
+ if (!input.postLoadHandled) {
12701
+ const startedAt = Date.now();
12702
+ await input.engine.waitForPostLoadQuiet({
12703
+ pageRef: input.pageRef,
12704
+ timeoutMs: effectiveTimeout,
12705
+ quietMs: DEFAULT_POST_LOAD_TRACKER_QUIET_WINDOW_MS,
12706
+ captureWindowMs: Math.min(NAVIGATION_POST_LOAD_CAPTURE_WINDOW_MS, effectiveTimeout),
12707
+ signal: input.signal
12708
+ });
12709
+ visualTimeout = Math.max(0, effectiveTimeout - (Date.now() - startedAt));
12710
+ initialQuietMs = Math.max(initialQuietMs, DEFAULT_POST_LOAD_TRACKER_QUIET_WINDOW_MS);
12711
+ }
12522
12712
  if (visualTimeout <= 0) {
12523
12713
  return true;
12524
12714
  }
@@ -12526,6 +12716,7 @@ var init_defaults = __esm({
12526
12716
  pageRef: input.pageRef,
12527
12717
  timeoutMs: visualTimeout,
12528
12718
  settleMs: profile.settleMs,
12719
+ ...initialQuietMs <= 0 ? {} : { initialQuietMs },
12529
12720
  scope: profile.scope
12530
12721
  });
12531
12722
  return true;
@@ -14693,7 +14884,7 @@ var init_executor = __esm({
14693
14884
  ...snapshot === void 0 ? {} : { snapshot },
14694
14885
  signal: timeout.signal,
14695
14886
  remainingMs: () => timeout.remainingMs(),
14696
- policySettle: async (targetPageRef, trigger) => {
14887
+ policySettle: async (targetPageRef, trigger, boundary2) => {
14697
14888
  try {
14698
14889
  await settleWithPolicy(this.options.policy.settle, {
14699
14890
  operation,
@@ -14701,7 +14892,9 @@ var init_executor = __esm({
14701
14892
  engine: this.options.engine,
14702
14893
  pageRef: targetPageRef,
14703
14894
  signal: timeout.signal,
14704
- remainingMs: timeout.remainingMs()
14895
+ remainingMs: timeout.remainingMs(),
14896
+ ...boundary2?.observedMutationQuietMs === void 0 ? {} : { observedMutationQuietMs: boundary2.observedMutationQuietMs },
14897
+ ...boundary2?.postLoadHandled === true ? { postLoadHandled: true } : {}
14705
14898
  });
14706
14899
  } catch (error) {
14707
14900
  if (snapshot !== void 0 && isSoftSettleTimeoutError(error, timeout.signal)) {
@@ -15959,12 +16152,16 @@ function toOpensteerResolvedTarget(target) {
15959
16152
  documentRef: target.documentRef,
15960
16153
  documentEpoch: target.documentEpoch,
15961
16154
  nodeRef: target.nodeRef,
15962
- tagName: target.node.nodeName.toUpperCase(),
16155
+ tagName: toOpensteerTagName(target.node.nodeName),
15963
16156
  pathHint: buildPathSelectorHint(target.replayPath ?? target.anchor),
15964
16157
  ...target.persist === void 0 ? {} : { persist: target.persist },
15965
16158
  ...target.selectorUsed === void 0 ? {} : { selectorUsed: target.selectorUsed }
15966
16159
  };
15967
16160
  }
16161
+ function toOpensteerTagName(nodeName) {
16162
+ const tagName = String(nodeName).trim().toLowerCase();
16163
+ return tagName.length === 0 ? "element" : tagName;
16164
+ }
15968
16165
  var init_trace_enrichment = __esm({
15969
16166
  "../runtime-core/src/runtimes/computer-use/trace-enrichment.ts"() {
15970
16167
  init_src();
@@ -16020,7 +16217,7 @@ var init_runtime2 = __esm({
16020
16217
  screenshot,
16021
16218
  signal: input.timeout.signal,
16022
16219
  remainingMs: () => input.timeout.remainingMs(),
16023
- policySettle: async (pageRef, trigger) => {
16220
+ policySettle: async (pageRef, trigger, boundary) => {
16024
16221
  try {
16025
16222
  await settleWithPolicy(this.options.policy.settle, {
16026
16223
  operation: "computer.execute",
@@ -16028,7 +16225,9 @@ var init_runtime2 = __esm({
16028
16225
  engine: this.options.engine,
16029
16226
  pageRef,
16030
16227
  signal: input.timeout.signal,
16031
- remainingMs: input.timeout.remainingMs()
16228
+ remainingMs: input.timeout.remainingMs(),
16229
+ ...boundary?.observedMutationQuietMs === void 0 ? {} : { observedMutationQuietMs: boundary.observedMutationQuietMs },
16230
+ ...boundary?.postLoadHandled === true ? { postLoadHandled: true } : {}
16032
16231
  });
16033
16232
  } catch (error) {
16034
16233
  if (pageRef === input.pageRef && isSoftSettleTimeoutError(error, input.timeout.signal)) {
@@ -17024,29 +17223,29 @@ function buildVariantDescriptorFromCluster(descriptors) {
17024
17223
  const keyStats = /* @__PURE__ */ new Map();
17025
17224
  for (const descriptor of descriptors) {
17026
17225
  for (const field of descriptor.fields) {
17027
- const stat2 = keyStats.get(field.path) ?? {
17226
+ const stat3 = keyStats.get(field.path) ?? {
17028
17227
  indices: /* @__PURE__ */ new Set(),
17029
17228
  pathNodes: [],
17030
17229
  attributes: [],
17031
17230
  sources: []
17032
17231
  };
17033
- stat2.indices.add(descriptor.index);
17232
+ stat3.indices.add(descriptor.index);
17034
17233
  if (isPersistedOpensteerExtractionValueNode(field.node)) {
17035
- stat2.pathNodes.push(field.node.$path);
17036
- stat2.attributes.push(field.node.attribute);
17234
+ stat3.pathNodes.push(field.node.$path);
17235
+ stat3.attributes.push(field.node.attribute);
17037
17236
  } else if (isPersistedOpensteerExtractionSourceNode(field.node)) {
17038
- stat2.sources.push("current_url");
17237
+ stat3.sources.push("current_url");
17039
17238
  }
17040
- keyStats.set(field.path, stat2);
17239
+ keyStats.set(field.path, stat3);
17041
17240
  }
17042
17241
  }
17043
17242
  const mergedFields = [];
17044
- for (const [fieldPath, stat2] of keyStats) {
17045
- if (stat2.indices.size < threshold) {
17243
+ for (const [fieldPath, stat3] of keyStats) {
17244
+ if (stat3.indices.size < threshold) {
17046
17245
  continue;
17047
17246
  }
17048
- if (stat2.pathNodes.length >= threshold) {
17049
- let mergedFieldPath = stat2.pathNodes.length === 1 ? sanitizeElementPath(stat2.pathNodes[0]) : mergeElementPathsByMajority(stat2.pathNodes);
17247
+ if (stat3.pathNodes.length >= threshold) {
17248
+ let mergedFieldPath = stat3.pathNodes.length === 1 ? sanitizeElementPath(stat3.pathNodes[0]) : mergeElementPathsByMajority(stat3.pathNodes);
17050
17249
  if (!mergedFieldPath) {
17051
17250
  continue;
17052
17251
  }
@@ -17054,8 +17253,8 @@ function buildVariantDescriptorFromCluster(descriptors) {
17054
17253
  mergedFieldPath = relaxPathForSingleSample(mergedFieldPath, "field");
17055
17254
  }
17056
17255
  mergedFieldPath = minimizePathMatchClauses(mergedFieldPath, "field");
17057
- const attrThreshold = stat2.pathNodes.length === 1 ? 1 : majorityThreshold(stat2.pathNodes.length);
17058
- const attribute = pickModeString(stat2.attributes, attrThreshold);
17256
+ const attrThreshold = stat3.pathNodes.length === 1 ? 1 : majorityThreshold(stat3.pathNodes.length);
17257
+ const attribute = pickModeString(stat3.attributes, attrThreshold);
17059
17258
  mergedFields.push({
17060
17259
  path: fieldPath,
17061
17260
  node: createValueNode({
@@ -17065,7 +17264,7 @@ function buildVariantDescriptorFromCluster(descriptors) {
17065
17264
  });
17066
17265
  continue;
17067
17266
  }
17068
- const dominantSource = pickModeString(stat2.sources, threshold);
17267
+ const dominantSource = pickModeString(stat3.sources, threshold);
17069
17268
  if (dominantSource === "current_url") {
17070
17269
  mergedFields.push({
17071
17270
  path: fieldPath,
@@ -19173,28 +19372,24 @@ function restoreBoundedAttr(el, attr, value) {
19173
19372
  }
19174
19373
  setBoundedAttr(el, attr, value);
19175
19374
  }
19176
- function deduplicateImages(html) {
19375
+ function deduplicateImagesInDom($) {
19177
19376
  const seen = /* @__PURE__ */ new Set();
19178
- return html.replace(/<img\b([^>]*)>/gi, (full, attrContent) => {
19179
- if (/\bc\s*=/.test(attrContent)) {
19180
- return full;
19181
- }
19182
- const srcMatch = attrContent.match(/\bsrc\s*=\s*(["']?)(.*?)\1/);
19183
- const srcsetMatch = attrContent.match(/\bsrcset\s*=\s*(["'])(.*?)\1/);
19184
- let src = null;
19185
- if (srcMatch && srcMatch[2]) {
19186
- src = srcMatch[2].trim();
19187
- } else if (srcsetMatch && srcsetMatch[2]) {
19188
- src = srcsetMatch[2].split(",")[0]?.trim().split(" ")[0] ?? null;
19377
+ $("img").each(function deduplicateDomImage() {
19378
+ const el = $(this);
19379
+ if (el.attr("c") !== void 0) {
19380
+ return;
19189
19381
  }
19382
+ const srcValue = el.attr("src")?.trim();
19383
+ const srcsetValue = el.attr("srcset");
19384
+ const src = srcValue && srcValue.length > 0 ? srcValue : srcsetValue?.split(",")[0]?.trim().split(/\s+/u)[0];
19190
19385
  if (!src) {
19191
- return full;
19386
+ return;
19192
19387
  }
19193
19388
  if (seen.has(src)) {
19194
- return "";
19389
+ el.remove();
19390
+ return;
19195
19391
  }
19196
19392
  seen.add(src);
19197
- return full;
19198
19393
  });
19199
19394
  }
19200
19395
  function hasAttribute2(node, attr) {
@@ -19252,23 +19447,6 @@ function isPreservedImageElement(node) {
19252
19447
  function getElementsInReverseDocumentOrder($) {
19253
19448
  return $.root().find("*").toArray().reverse().filter((node) => node.type === "tag");
19254
19449
  }
19255
- function getNodeDepth(node) {
19256
- let depth = 0;
19257
- let current = node.parent;
19258
- while (current) {
19259
- depth++;
19260
- current = current.parent;
19261
- }
19262
- return depth;
19263
- }
19264
- function getElementsByDepthDescending($) {
19265
- const elements = $.root().find("*").toArray().filter((node) => node.type === "tag");
19266
- const depths = /* @__PURE__ */ new Map();
19267
- for (const el of elements) {
19268
- depths.set(el, getNodeDepth(el));
19269
- }
19270
- return elements.sort((a, b) => (depths.get(b) ?? 0) - (depths.get(a) ?? 0));
19271
- }
19272
19450
  function flattenExtractionTree($) {
19273
19451
  for (const node of getElementsInReverseDocumentOrder($)) {
19274
19452
  const el = $(node);
@@ -19286,19 +19464,6 @@ function flattenExtractionTree($) {
19286
19464
  el.replaceWith(el.contents());
19287
19465
  }
19288
19466
  }
19289
- function hasMarkedAncestor(el, attr) {
19290
- let current = el[0]?.parent;
19291
- while (current) {
19292
- if (!isElementLikeNode(current)) {
19293
- return false;
19294
- }
19295
- if (current.attribs?.[attr] !== void 0) {
19296
- return true;
19297
- }
19298
- current = current.parent;
19299
- }
19300
- return false;
19301
- }
19302
19467
  function isIndicatorImage(node) {
19303
19468
  return (node?.tagName || "").toLowerCase() === "img" && (hasAttribute2(node, "alt") || hasAttribute2(node, "src") || hasAttribute2(node, "srcset"));
19304
19469
  }
@@ -19416,7 +19581,7 @@ function serializeForExtraction($, root) {
19416
19581
  traverse(root, 0);
19417
19582
  return lines.map((l) => l.trim()).filter((l) => l.length > 0).join("");
19418
19583
  }
19419
- function isClickable($, el, context) {
19584
+ function isClickable(el, context) {
19420
19585
  if (context.hasPreMarked) {
19421
19586
  return el.attr(OPENSTEER_INTERACTIVE_ATTR) !== void 0;
19422
19587
  }
@@ -19450,21 +19615,17 @@ function isClickable($, el, context) {
19450
19615
  }
19451
19616
  return false;
19452
19617
  }
19453
- function cleanForExtraction(html) {
19618
+ function prepareExtractionSnapshotDom(html) {
19454
19619
  if (!html.trim()) {
19455
- return "";
19620
+ return void 0;
19456
19621
  }
19457
19622
  const $ = cheerio__namespace.load(html, { xmlMode: false });
19458
19623
  removeNoise($);
19459
19624
  removeComments($);
19460
19625
  markInlineSelfHiddenFallback($);
19461
19626
  pruneSelfHiddenNodes($);
19462
- const $clean = cheerio__namespace.load(
19463
- $.html().replace(/\n{2,}/g, "\n").trim(),
19464
- { xmlMode: false }
19465
- );
19466
- $clean("*").each(function stripAndRestoreExtractionAttrs() {
19467
- const el = $clean(this);
19627
+ $("*").each(function stripAndRestoreExtractionAttrs() {
19628
+ const el = $(this);
19468
19629
  const node = el[0];
19469
19630
  if (!node) {
19470
19631
  return;
@@ -19505,16 +19666,20 @@ function cleanForExtraction(html) {
19505
19666
  restoreBoundedAttr(el, "href", hrefValue);
19506
19667
  }
19507
19668
  });
19508
- flattenExtractionTree($clean);
19509
- const root = $clean.root()[0];
19669
+ flattenExtractionTree($);
19670
+ deduplicateImagesInDom($);
19671
+ return $;
19672
+ }
19673
+ function serializePreparedExtractionSnapshot($) {
19674
+ const root = $.root()[0];
19510
19675
  if (root === void 0) {
19511
19676
  return "";
19512
19677
  }
19513
- return deduplicateImages(serializeForExtraction($clean, root));
19678
+ return serializeForExtraction($, root);
19514
19679
  }
19515
- function cleanForAction(html) {
19680
+ function prepareActionSnapshotDom(html) {
19516
19681
  if (!html.trim()) {
19517
- return "";
19682
+ return void 0;
19518
19683
  }
19519
19684
  const $ = cheerio__namespace.load(html, { xmlMode: false });
19520
19685
  removeNoise($);
@@ -19523,13 +19688,12 @@ function cleanForAction(html) {
19523
19688
  pruneSelfHiddenNodes($);
19524
19689
  const clickableMark = "data-clickable-marker";
19525
19690
  const indicatorMark = "data-keep-indicator";
19526
- const branchMark = "data-keep-branch";
19527
19691
  const context = {
19528
19692
  hasPreMarked: $(`[${OPENSTEER_INTERACTIVE_ATTR}]`).length > 0
19529
19693
  };
19530
19694
  $("*").each(function markClickables() {
19531
19695
  const el = $(this);
19532
- if (isClickable($, el, context)) {
19696
+ if (isClickable(el, context)) {
19533
19697
  el.attr(clickableMark, "1");
19534
19698
  }
19535
19699
  });
@@ -19559,25 +19723,7 @@ function cleanForAction(html) {
19559
19723
  el.remove();
19560
19724
  }
19561
19725
  });
19562
- $(`[${clickableMark}], [${indicatorMark}]`).each(function markBranches() {
19563
- let current = $(this).parent();
19564
- while (current.length > 0) {
19565
- const node = current[0];
19566
- if (!node || node.type !== "tag") {
19567
- break;
19568
- }
19569
- const ancestor = current;
19570
- const tag = (node.tagName || "").toLowerCase();
19571
- if (ROOT_TAGS.has(tag) || ancestor.attr(clickableMark) !== void 0) {
19572
- break;
19573
- }
19574
- if (!isBoundaryTag(tag)) {
19575
- ancestor.attr(branchMark, "1");
19576
- }
19577
- current = ancestor.parent();
19578
- }
19579
- });
19580
- for (const node of getElementsByDepthDescending($)) {
19726
+ for (const node of getElementsInReverseDocumentOrder($)) {
19581
19727
  const el = $(node);
19582
19728
  const tag = (node.tagName || "").toLowerCase();
19583
19729
  if (ROOT_TAGS.has(tag) || isBoundaryTag(tag)) {
@@ -19586,17 +19732,7 @@ function cleanForAction(html) {
19586
19732
  if (el.attr(clickableMark) !== void 0 || el.attr(indicatorMark) !== void 0) {
19587
19733
  continue;
19588
19734
  }
19589
- const insideClickable = hasMarkedAncestor(el, clickableMark);
19590
- const preserveBranch = el.attr(branchMark) !== void 0;
19591
19735
  const hasContent = hasElementChildren(node) || hasDirectText(node);
19592
- if (insideClickable || preserveBranch) {
19593
- if (!hasContent) {
19594
- el.remove();
19595
- } else {
19596
- unwrapActionNode($, el);
19597
- }
19598
- continue;
19599
- }
19600
19736
  if (!hasContent) {
19601
19737
  el.remove();
19602
19738
  continue;
@@ -19697,13 +19833,20 @@ function cleanForAction(html) {
19697
19833
  }
19698
19834
  el.removeAttr(clickableMark);
19699
19835
  el.removeAttr(indicatorMark);
19700
- el.removeAttr(branchMark);
19701
19836
  el.removeAttr(OPENSTEER_INTERACTIVE_ATTR);
19702
19837
  el.removeAttr(OPENSTEER_HIDDEN_ATTR);
19703
19838
  el.removeAttr(OPENSTEER_SCROLLABLE_ATTR);
19704
19839
  el.removeAttr(OPENSTEER_SELF_HIDDEN_ATTR);
19705
19840
  });
19706
- return compactHtml(deduplicateImages($.html()));
19841
+ deduplicateImagesInDom($);
19842
+ return $;
19843
+ }
19844
+ function serializePreparedActionSnapshot($) {
19845
+ const normalized = compactHtml($.html());
19846
+ if (normalized.length === 0) {
19847
+ return "";
19848
+ }
19849
+ return cheerio__namespace.load(normalized, { xmlMode: false }).html();
19707
19850
  }
19708
19851
  var STRIP_TAGS, TEXT_ATTR_MAX, SRCSET_ATTR_MAX, MIDDLE_TRUNCATED_URL_ATTRS, TEXT_ATTRS, TRUNCATION_SUFFIX, MIDDLE_TRUNCATION_MARKER, MIDDLE_TRUNCATION_HEAD_MAX, MIDDLE_TRUNCATION_TAIL_MAX, SRCSET_CANDIDATE_HEAD_MAX, SRCSET_CANDIDATE_TAIL_MAX, SRCSET_COMPACT_CANDIDATE_HEAD_MAX, SRCSET_COMPACT_CANDIDATE_TAIL_MAX, SRCSET_FALLBACK_HEAD_MAX, SRCSET_FALLBACK_TAIL_MAX, NOISE_SELECTORS, VOID_TAGS2;
19709
19852
  var init_cleaner = __esm({
@@ -19764,23 +19907,27 @@ async function markLiveSnapshotSemantics(options) {
19764
19907
  const frames = await options.engine.listFrames({
19765
19908
  pageRef: options.pageRef
19766
19909
  });
19767
- for (const frame of frames) {
19768
- await evaluateFrameBestEffort(
19769
- options.engine,
19770
- frame.frameRef,
19771
- MARK_SNAPSHOT_SEMANTICS_SCRIPT,
19772
- SNAPSHOT_SEMANTIC_ARGS
19773
- );
19774
- }
19775
- return async () => {
19776
- for (const frame of frames) {
19777
- await evaluateFrameBestEffort(
19910
+ await Promise.all(
19911
+ frames.map(
19912
+ (frame) => evaluateFrameBestEffort(
19778
19913
  options.engine,
19779
19914
  frame.frameRef,
19780
- CLEAR_SNAPSHOT_SEMANTICS_SCRIPT,
19781
- CLEAR_SNAPSHOT_SEMANTIC_ARGS
19782
- );
19783
- }
19915
+ MARK_SNAPSHOT_SEMANTICS_SCRIPT,
19916
+ SNAPSHOT_SEMANTIC_ARGS
19917
+ )
19918
+ )
19919
+ );
19920
+ return async () => {
19921
+ await Promise.all(
19922
+ frames.map(
19923
+ (frame) => evaluateFrameBestEffort(
19924
+ options.engine,
19925
+ frame.frameRef,
19926
+ CLEAR_SNAPSHOT_SEMANTICS_SCRIPT,
19927
+ CLEAR_SNAPSHOT_SEMANTIC_ARGS
19928
+ )
19929
+ )
19930
+ );
19784
19931
  };
19785
19932
  }
19786
19933
  var MARK_SNAPSHOT_SEMANTICS_SCRIPT, CLEAR_SNAPSHOT_SEMANTICS_SCRIPT, SNAPSHOT_SEMANTIC_ARGS, CLEAR_SNAPSHOT_SEMANTIC_ARGS;
@@ -19978,6 +20125,8 @@ var init_marking = __esm({
19978
20125
  ];
19979
20126
  }
19980
20127
  });
20128
+
20129
+ // ../runtime-core/src/sdk/snapshot/compiler.ts
19981
20130
  function isLiveCounterSyncError(error) {
19982
20131
  return error instanceof LiveCounterSyncError;
19983
20132
  }
@@ -20015,20 +20164,22 @@ function ensureSparseCountersForAllRecords(counterRecords) {
20015
20164
  async function clearOpensteerLiveCounters(engine, pageRef) {
20016
20165
  const frames = await engine.listFrames({ pageRef });
20017
20166
  const failures = [];
20018
- for (const frame of frames) {
20019
- try {
20020
- await engine.evaluateFrame({
20021
- frameRef: frame.frameRef,
20022
- script: CLEAR_LIVE_COUNTERS_SCRIPT,
20023
- args: [{ sparseCounterAttr: OPENSTEER_SPARSE_COUNTER_ATTR }]
20024
- });
20025
- } catch (error) {
20026
- if (isDetachedFrameSyncError(error)) {
20027
- continue;
20167
+ await Promise.all(
20168
+ frames.map(async (frame) => {
20169
+ try {
20170
+ await engine.evaluateFrame({
20171
+ frameRef: frame.frameRef,
20172
+ script: CLEAR_LIVE_COUNTERS_SCRIPT,
20173
+ args: [{ sparseCounterAttr: OPENSTEER_SPARSE_COUNTER_ATTR }]
20174
+ });
20175
+ } catch (error) {
20176
+ if (isDetachedFrameSyncError(error)) {
20177
+ return;
20178
+ }
20179
+ failures.push(`frame ${frame.frameRef} could not be cleared (${describeError(error)}).`);
20028
20180
  }
20029
- failures.push(`frame ${frame.frameRef} could not be cleared (${describeError(error)}).`);
20030
- }
20031
- }
20181
+ })
20182
+ );
20032
20183
  if (failures.length > 0) {
20033
20184
  throw buildLiveCounterSyncError("clear live counters", failures);
20034
20185
  }
@@ -20077,25 +20228,29 @@ async function syncDenseCountersToLiveDom(engine, pageRef, sparseToDirectMapping
20077
20228
  denseCounter
20078
20229
  ])
20079
20230
  );
20080
- for (const frame of frames) {
20081
- try {
20082
- await engine.evaluateFrame({
20083
- frameRef: frame.frameRef,
20084
- script: APPLY_DENSE_COUNTERS_SCRIPT,
20085
- args: [
20086
- {
20087
- sparseCounterAttr: OPENSTEER_SPARSE_COUNTER_ATTR,
20088
- mapping: mappingObj
20089
- }
20090
- ]
20091
- });
20092
- } catch (error) {
20093
- if (isDetachedFrameSyncError(error)) {
20094
- continue;
20231
+ await Promise.all(
20232
+ frames.map(async (frame) => {
20233
+ try {
20234
+ await engine.evaluateFrame({
20235
+ frameRef: frame.frameRef,
20236
+ script: APPLY_DENSE_COUNTERS_SCRIPT,
20237
+ args: [
20238
+ {
20239
+ sparseCounterAttr: OPENSTEER_SPARSE_COUNTER_ATTR,
20240
+ mapping: mappingObj
20241
+ }
20242
+ ]
20243
+ });
20244
+ } catch (error) {
20245
+ if (isDetachedFrameSyncError(error)) {
20246
+ return;
20247
+ }
20248
+ failures.push(
20249
+ `frame ${frame.frameRef} could not be synchronized (${describeError(error)}).`
20250
+ );
20095
20251
  }
20096
- failures.push(`frame ${frame.frameRef} could not be synchronized (${describeError(error)}).`);
20097
- }
20098
- }
20252
+ })
20253
+ );
20099
20254
  if (failures.length > 0) {
20100
20255
  throw buildLiveCounterSyncError("synchronize dense counters", failures);
20101
20256
  }
@@ -20113,26 +20268,26 @@ async function compileOpensteerSnapshot(options) {
20113
20268
  await clearOpensteerLiveCounters(options.engine, options.pageRef);
20114
20269
  await assignSparseCountersToLiveDom(options.engine, options.pageRef);
20115
20270
  const pageInfo = await options.engine.getPageInfo({ pageRef: options.pageRef });
20116
- const mainSnapshot = await getMainDocumentSnapshot(options.engine, options.pageRef);
20117
- const snapshotsByDocumentRef = await collectDocumentSnapshots(options.engine, mainSnapshot);
20271
+ const { mainSnapshot, snapshotsByDocumentRef } = await getPageDocumentSnapshots(
20272
+ options.engine,
20273
+ options.pageRef
20274
+ );
20118
20275
  await cleanupLiveSemantics();
20119
20276
  cleanupLiveSemantics = async () => {
20120
20277
  };
20121
- const snapshotIndices = /* @__PURE__ */ new Map();
20122
20278
  const renderedNodes = /* @__PURE__ */ new Map();
20123
20279
  const rawHtml = renderDocumentSnapshot(
20124
20280
  mainSnapshot.documentRef,
20125
20281
  snapshotsByDocumentRef,
20126
- snapshotIndices,
20127
20282
  renderedNodes,
20128
20283
  {
20129
20284
  iframeDepth: 0,
20130
20285
  shadowDepth: 0
20131
20286
  }
20132
20287
  );
20133
- const cleanedHtml = options.mode === "extraction" ? cleanForExtraction(rawHtml) : cleanForAction(rawHtml);
20134
- const compiledHtml = assignCounters(cleanedHtml, renderedNodes);
20135
- const finalHtml = options.mode === "extraction" ? unwrapExtractionHtml(compiledHtml.html) : compiledHtml.html;
20288
+ const preparedSnapshotDom = options.mode === "extraction" ? prepareExtractionSnapshotDom(rawHtml) : prepareActionSnapshotDom(rawHtml);
20289
+ const compiledHtml = assignCountersInDom(preparedSnapshotDom, renderedNodes, options.mode);
20290
+ const finalHtml = preparedSnapshotDom === void 0 ? "" : options.mode === "extraction" ? serializePreparedExtractionSnapshot(preparedSnapshotDom) : serializePreparedActionSnapshot(preparedSnapshotDom);
20136
20291
  ensureSparseCountersForAllRecords(compiledHtml.counterRecords);
20137
20292
  await syncDenseCountersToLiveDom(
20138
20293
  options.engine,
@@ -20167,6 +20322,25 @@ async function getMainDocumentSnapshot(engine, pageRef) {
20167
20322
  }
20168
20323
  return engine.getDomSnapshot({ frameRef: mainFrame.frameRef });
20169
20324
  }
20325
+ async function getPageDocumentSnapshots(engine, pageRef) {
20326
+ const bundleEngine = engine;
20327
+ const bundledSnapshots = await bundleEngine.getPageDomSnapshots?.({ pageRef });
20328
+ if (bundledSnapshots && bundledSnapshots.length > 0) {
20329
+ const mainSnapshot2 = bundledSnapshots.find((snapshot) => snapshot.parentDocumentRef === void 0) ?? bundledSnapshots[0];
20330
+ return {
20331
+ mainSnapshot: mainSnapshot2,
20332
+ snapshotsByDocumentRef: new Map(
20333
+ bundledSnapshots.map((snapshot) => [snapshot.documentRef, snapshot])
20334
+ )
20335
+ };
20336
+ }
20337
+ const mainSnapshot = await getMainDocumentSnapshot(engine, pageRef);
20338
+ const snapshotsByDocumentRef = await collectDocumentSnapshots(engine, mainSnapshot);
20339
+ return {
20340
+ mainSnapshot,
20341
+ snapshotsByDocumentRef
20342
+ };
20343
+ }
20170
20344
  async function collectDocumentSnapshots(engine, mainSnapshot) {
20171
20345
  const snapshotsByDocumentRef = /* @__PURE__ */ new Map([
20172
20346
  [mainSnapshot.documentRef, mainSnapshot]
@@ -20185,7 +20359,7 @@ async function collectDocumentSnapshots(engine, mainSnapshot) {
20185
20359
  }
20186
20360
  return snapshotsByDocumentRef;
20187
20361
  }
20188
- function renderDocumentSnapshot(documentRef, snapshotsByDocumentRef, snapshotIndices, renderedNodes, depth) {
20362
+ function renderDocumentSnapshot(documentRef, snapshotsByDocumentRef, renderedNodes, depth) {
20189
20363
  const snapshot = snapshotsByDocumentRef.get(documentRef);
20190
20364
  if (!snapshot) {
20191
20365
  return "";
@@ -20197,17 +20371,9 @@ function renderDocumentSnapshot(documentRef, snapshotsByDocumentRef, snapshotInd
20197
20371
  `snapshot ${snapshot.documentRef} is missing root node ${String(snapshot.rootSnapshotNodeId)}`
20198
20372
  );
20199
20373
  }
20200
- return renderNode(
20201
- snapshot,
20202
- rootNode,
20203
- nodesById,
20204
- snapshotsByDocumentRef,
20205
- snapshotIndices,
20206
- renderedNodes,
20207
- depth
20208
- );
20374
+ return renderNode(snapshot, rootNode, nodesById, snapshotsByDocumentRef, renderedNodes, depth);
20209
20375
  }
20210
- function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotIndices, renderedNodes, depth) {
20376
+ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, renderedNodes, depth) {
20211
20377
  if (node.nodeType === 3) {
20212
20378
  return escapeHtml(node.nodeValue || node.textContent || "");
20213
20379
  }
@@ -20215,56 +20381,26 @@ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotI
20215
20381
  return "";
20216
20382
  }
20217
20383
  if (node.nodeType === 9 || node.nodeType === 11) {
20218
- return renderChildren(
20219
- snapshot,
20220
- node,
20221
- nodesById,
20222
- snapshotsByDocumentRef,
20223
- snapshotIndices,
20224
- renderedNodes,
20225
- depth
20226
- );
20384
+ return renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, renderedNodes, depth);
20227
20385
  }
20228
20386
  if (node.nodeType !== 1) {
20229
- return renderChildren(
20230
- snapshot,
20231
- node,
20232
- nodesById,
20233
- snapshotsByDocumentRef,
20234
- snapshotIndices,
20235
- renderedNodes,
20236
- depth
20237
- );
20387
+ return renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, renderedNodes, depth);
20238
20388
  }
20239
20389
  const tagName = normalizeTagName(node.nodeName);
20240
20390
  if (isPseudoElementTagName(tagName)) {
20241
- return renderChildren(
20242
- snapshot,
20243
- node,
20244
- nodesById,
20245
- snapshotsByDocumentRef,
20246
- snapshotIndices,
20247
- renderedNodes,
20248
- depth
20249
- );
20391
+ return renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, renderedNodes, depth);
20250
20392
  }
20251
20393
  if ((depth.iframeDepth > 0 || depth.shadowDepth > 0) && (tagName === "html" || tagName === "head" || tagName === "body")) {
20252
- return renderChildren(
20253
- snapshot,
20254
- node,
20255
- nodesById,
20256
- snapshotsByDocumentRef,
20257
- snapshotIndices,
20258
- renderedNodes,
20259
- depth
20260
- );
20394
+ return renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, renderedNodes, depth);
20261
20395
  }
20262
20396
  const snapshotAttributes = normalizeNodeAttributes(node.attributes);
20397
+ const snapshotAttributeIndex = indexNodeAttributes(snapshotAttributes);
20263
20398
  const authoredAttributes = stripInternalSnapshotAttributes(snapshotAttributes);
20399
+ const authoredAttributeIndex = indexNodeAttributes(authoredAttributes);
20264
20400
  const attributes = [...authoredAttributes];
20265
- const subtreeHidden = hasAttribute3(snapshotAttributes, OPENSTEER_HIDDEN_ATTR) || isLikelySubtreeHidden(node);
20266
- const selfHidden = !subtreeHidden && (hasAttribute3(snapshotAttributes, OPENSTEER_SELF_HIDDEN_ATTR) || isLikelySelfHidden(node, nodesById));
20267
- const interactive = !subtreeHidden && !selfHidden && (hasAttribute3(snapshotAttributes, OPENSTEER_INTERACTIVE_ATTR) || isLikelyInteractive(tagName, node, authoredAttributes));
20401
+ const subtreeHidden = snapshotAttributeIndex.has(OPENSTEER_HIDDEN_ATTR) || isLikelySubtreeHidden(node);
20402
+ const selfHidden = !subtreeHidden && (snapshotAttributeIndex.has(OPENSTEER_SELF_HIDDEN_ATTR) || isLikelySelfHidden(node, nodesById));
20403
+ const interactive = !subtreeHidden && !selfHidden && (snapshotAttributeIndex.has(OPENSTEER_INTERACTIVE_ATTR) || isLikelyInteractive(tagName, node, authoredAttributes, authoredAttributeIndex));
20268
20404
  if (interactive) {
20269
20405
  attributes.push({ name: OPENSTEER_INTERACTIVE_ATTR, value: "1" });
20270
20406
  }
@@ -20273,7 +20409,7 @@ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotI
20273
20409
  } else if (selfHidden) {
20274
20410
  attributes.push({ name: OPENSTEER_SELF_HIDDEN_ATTR, value: "1" });
20275
20411
  }
20276
- const sparseCounter = findAttributeValue(snapshotAttributes, OPENSTEER_SPARSE_COUNTER_ATTR);
20412
+ const sparseCounter = snapshotAttributeIndex.get(OPENSTEER_SPARSE_COUNTER_ATTR);
20277
20413
  if (sparseCounter !== void 0) {
20278
20414
  attributes.push({ name: OPENSTEER_SPARSE_COUNTER_ATTR, value: sparseCounter });
20279
20415
  }
@@ -20284,21 +20420,18 @@ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotI
20284
20420
  const syntheticNodeId = buildSyntheticNodeId(snapshot, node);
20285
20421
  attributes.push({ name: OPENSTEER_NODE_ID_ATTR, value: syntheticNodeId });
20286
20422
  renderedNodes.set(syntheticNodeId, {
20287
- locator: createNodeLocator(snapshot.documentRef, snapshot.documentEpoch, node.nodeRef),
20288
- anchor: buildSnapshotElementAnchor(snapshot, node, snapshotsByDocumentRef, snapshotIndices),
20289
20423
  pageRef: snapshot.pageRef,
20290
20424
  frameRef: snapshot.frameRef,
20291
20425
  documentRef: snapshot.documentRef,
20292
20426
  documentEpoch: snapshot.documentEpoch,
20293
20427
  nodeRef: node.nodeRef,
20294
20428
  tagName: tagName.toUpperCase(),
20295
- pathHint: buildPathHint(tagName, authoredAttributes),
20296
- ...buildTextSnippet(node.textContent) === void 0 ? {} : { text: buildTextSnippet(node.textContent) },
20297
20429
  ...authoredAttributes.length === 0 ? {} : { attributes: authoredAttributes },
20298
20430
  iframeDepth: depth.iframeDepth,
20299
20431
  shadowDepth: depth.shadowDepth,
20300
20432
  interactive,
20301
- liveCounterSyncEligible: isLiveCounterSyncEligible(node, nodesById)
20433
+ liveCounterSyncEligible: isLiveCounterSyncEligible(node, nodesById),
20434
+ ...node.textContent === void 0 ? {} : { textContent: node.textContent }
20302
20435
  });
20303
20436
  }
20304
20437
  const attributeText = attributesToHtml(attributes);
@@ -20307,7 +20440,6 @@ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotI
20307
20440
  node,
20308
20441
  nodesById,
20309
20442
  snapshotsByDocumentRef,
20310
- snapshotIndices,
20311
20443
  renderedNodes,
20312
20444
  depth
20313
20445
  );
@@ -20318,7 +20450,6 @@ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotI
20318
20450
  const iframeHtml = renderDocumentSnapshot(
20319
20451
  node.contentDocumentRef,
20320
20452
  snapshotsByDocumentRef,
20321
- snapshotIndices,
20322
20453
  renderedNodes,
20323
20454
  {
20324
20455
  iframeDepth: depth.iframeDepth + 1,
@@ -20330,7 +20461,7 @@ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotI
20330
20461
  }
20331
20462
  return `${elementHtml}<${OPENSTEER_IFRAME_BOUNDARY_TAG} ${OPENSTEER_BOUNDARY_ATTR}="iframe">${iframeHtml}</${OPENSTEER_IFRAME_BOUNDARY_TAG}>`;
20332
20463
  }
20333
- function renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotIndices, renderedNodes, depth) {
20464
+ function renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, renderedNodes, depth) {
20334
20465
  const regularChildren = [];
20335
20466
  const shadowChildren = [];
20336
20467
  for (const childSnapshotNodeId of node.childSnapshotNodeIds) {
@@ -20347,18 +20478,10 @@ function renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, snaps
20347
20478
  const chunks = [];
20348
20479
  if (shadowChildren.length > 0) {
20349
20480
  const shadowHtml = shadowChildren.map(
20350
- (child) => renderNode(
20351
- snapshot,
20352
- child,
20353
- nodesById,
20354
- snapshotsByDocumentRef,
20355
- snapshotIndices,
20356
- renderedNodes,
20357
- {
20358
- iframeDepth: depth.iframeDepth,
20359
- shadowDepth: depth.shadowDepth + 1
20360
- }
20361
- )
20481
+ (child) => renderNode(snapshot, child, nodesById, snapshotsByDocumentRef, renderedNodes, {
20482
+ iframeDepth: depth.iframeDepth,
20483
+ shadowDepth: depth.shadowDepth + 1
20484
+ })
20362
20485
  ).join("");
20363
20486
  chunks.push(
20364
20487
  `<${OPENSTEER_SHADOW_BOUNDARY_TAG} ${OPENSTEER_BOUNDARY_ATTR}="shadow">${shadowHtml}</${OPENSTEER_SHADOW_BOUNDARY_TAG}>`
@@ -20366,24 +20489,21 @@ function renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, snaps
20366
20489
  }
20367
20490
  for (const child of regularChildren) {
20368
20491
  chunks.push(
20369
- renderNode(
20370
- snapshot,
20371
- child,
20372
- nodesById,
20373
- snapshotsByDocumentRef,
20374
- snapshotIndices,
20375
- renderedNodes,
20376
- depth
20377
- )
20492
+ renderNode(snapshot, child, nodesById, snapshotsByDocumentRef, renderedNodes, depth)
20378
20493
  );
20379
20494
  }
20380
20495
  return chunks.join("");
20381
20496
  }
20382
- function assignCounters(cleanedHtml, renderedNodes) {
20383
- const $ = cheerio__namespace.load(cleanedHtml, { xmlMode: false });
20497
+ function assignCountersInDom($, renderedNodes, mode) {
20384
20498
  const counterRecords = /* @__PURE__ */ new Map();
20385
20499
  const sparseToDirectMapping = /* @__PURE__ */ new Map();
20386
20500
  let nextCounter = 1;
20501
+ if (!$) {
20502
+ return {
20503
+ counterRecords,
20504
+ sparseToDirectMapping
20505
+ };
20506
+ }
20387
20507
  $("*").each(function assignElementCounter() {
20388
20508
  const el = $(this);
20389
20509
  const syntheticNodeId = el.attr(OPENSTEER_NODE_ID_ATTR);
@@ -20395,14 +20515,24 @@ function assignCounters(cleanedHtml, renderedNodes) {
20395
20515
  if (!rendered) {
20396
20516
  return;
20397
20517
  }
20518
+ if (mode === "extraction" && EXTRACTION_SKIPPED_COUNTER_TAGS.has(rendered.tagName.toLowerCase())) {
20519
+ el.removeAttr(OPENSTEER_SPARSE_COUNTER_ATTR);
20520
+ return;
20521
+ }
20398
20522
  const rawSparseCounter = el.attr(OPENSTEER_SPARSE_COUNTER_ATTR);
20399
20523
  el.removeAttr(OPENSTEER_SPARSE_COUNTER_ATTR);
20400
20524
  const sparseCounter = rawSparseCounter ? Number.parseInt(rawSparseCounter, 10) : void 0;
20525
+ const replayableSparseCounter = typeof sparseCounter === "number" && Number.isFinite(sparseCounter) ? sparseCounter : void 0;
20526
+ if (rendered.liveCounterSyncEligible && replayableSparseCounter === void 0) {
20527
+ return;
20528
+ }
20401
20529
  const counter = nextCounter++;
20402
20530
  el.attr("c", String(counter));
20403
- if (sparseCounter !== void 0 && Number.isFinite(sparseCounter)) {
20404
- sparseToDirectMapping.set(sparseCounter, counter);
20531
+ if (replayableSparseCounter !== void 0) {
20532
+ sparseToDirectMapping.set(replayableSparseCounter, counter);
20405
20533
  }
20534
+ const pathHint = buildPathHint(rendered.tagName.toLowerCase(), rendered.attributes ?? []);
20535
+ const text = buildTextSnippet(rendered.textContent);
20406
20536
  counterRecords.set(counter, {
20407
20537
  element: counter,
20408
20538
  pageRef: rendered.pageRef,
@@ -20411,20 +20541,17 @@ function assignCounters(cleanedHtml, renderedNodes) {
20411
20541
  documentEpoch: rendered.documentEpoch,
20412
20542
  nodeRef: rendered.nodeRef,
20413
20543
  tagName: rendered.tagName,
20414
- pathHint: rendered.pathHint,
20415
- ...rendered.text === void 0 ? {} : { text: rendered.text },
20544
+ pathHint,
20545
+ ...text === void 0 ? {} : { text },
20416
20546
  ...rendered.attributes === void 0 ? {} : { attributes: rendered.attributes },
20417
20547
  iframeDepth: rendered.iframeDepth,
20418
20548
  shadowDepth: rendered.shadowDepth,
20419
20549
  interactive: rendered.interactive,
20420
20550
  liveCounterSyncEligible: rendered.liveCounterSyncEligible,
20421
- locator: rendered.locator,
20422
- anchor: rendered.anchor,
20423
- ...sparseCounter !== void 0 && Number.isFinite(sparseCounter) ? { sparseCounter } : {}
20551
+ ...replayableSparseCounter === void 0 ? {} : { sparseCounter: replayableSparseCounter }
20424
20552
  });
20425
20553
  });
20426
20554
  return {
20427
- html: $.html(),
20428
20555
  counterRecords,
20429
20556
  sparseToDirectMapping
20430
20557
  };
@@ -20498,28 +20625,28 @@ function isLikelySelfHidden(node, nodesById) {
20498
20625
  }
20499
20626
  return !hasVisibleOutOfFlowChild(node, nodesById);
20500
20627
  }
20501
- function isLikelyInteractive(tagName, node, attributes) {
20628
+ function isLikelyInteractive(tagName, node, attributes, attributeIndex) {
20502
20629
  if (NATIVE_INTERACTIVE_TAGS.has(tagName)) {
20503
- if (tagName === "input" && findAttributeValue(attributes, "type")?.toLowerCase() === "hidden") {
20630
+ if (tagName === "input" && attributeIndex.get("type")?.toLowerCase() === "hidden") {
20504
20631
  return false;
20505
20632
  }
20506
20633
  if (tagName !== "a") {
20507
20634
  return true;
20508
20635
  }
20509
20636
  }
20510
- if (tagName === "a" && findAttributeValue(attributes, "href") !== void 0) {
20637
+ if (tagName === "a" && attributeIndex.has("href")) {
20511
20638
  return true;
20512
20639
  }
20513
- if (findAttributeValue(attributes, "onclick") !== void 0 || findAttributeValue(attributes, "onmousedown") !== void 0 || findAttributeValue(attributes, "onmouseup") !== void 0 || findAttributeValue(attributes, "data-action") !== void 0 || findAttributeValue(attributes, "data-click") !== void 0 || findAttributeValue(attributes, "data-toggle") !== void 0) {
20640
+ if (attributeIndex.has("onclick") || attributeIndex.has("onmousedown") || attributeIndex.has("onmouseup") || attributeIndex.has("data-action") || attributeIndex.has("data-click") || attributeIndex.has("data-toggle")) {
20514
20641
  return true;
20515
20642
  }
20516
- if (hasNonNegativeTabIndex(findAttributeValue(attributes, "tabindex"))) {
20643
+ if (hasNonNegativeTabIndex(attributeIndex.get("tabindex"))) {
20517
20644
  return true;
20518
20645
  }
20519
- if (findAttributeValue(attributes, "contenteditable")?.toLowerCase() === "true") {
20646
+ if (attributeIndex.get("contenteditable")?.toLowerCase() === "true") {
20520
20647
  return true;
20521
20648
  }
20522
- const role = findAttributeValue(attributes, "role")?.toLowerCase();
20649
+ const role = attributeIndex.get("role")?.toLowerCase();
20523
20650
  return role !== void 0 && INTERACTIVE_ROLE_SET.has(role);
20524
20651
  }
20525
20652
  function hasVisibleOutOfFlowChild(node, nodesById) {
@@ -20582,14 +20709,6 @@ function parseOpacity(value) {
20582
20709
  const parsed = Number.parseFloat(value);
20583
20710
  return Number.isFinite(parsed) ? parsed : Number.NaN;
20584
20711
  }
20585
- function hasAttribute3(attributes, name) {
20586
- const normalizedName = name.toLowerCase();
20587
- return attributes.some((attribute) => attribute.name.toLowerCase() === normalizedName);
20588
- }
20589
- function unwrapExtractionHtml(html) {
20590
- const $ = cheerio__namespace.load(html, { xmlMode: false });
20591
- return $("body").html()?.trim() || html;
20592
- }
20593
20712
  function buildSyntheticNodeId(snapshot, node) {
20594
20713
  return `${snapshot.documentRef}:${String(snapshot.documentEpoch)}:${String(node.snapshotNodeId)}`;
20595
20714
  }
@@ -20636,6 +20755,13 @@ function findAttributeValue(attributes, name) {
20636
20755
  const normalizedName = name.toLowerCase();
20637
20756
  return attributes.find((attribute) => attribute.name.toLowerCase() === normalizedName)?.value;
20638
20757
  }
20758
+ function indexNodeAttributes(attributes) {
20759
+ const indexed = /* @__PURE__ */ new Map();
20760
+ for (const attribute of attributes) {
20761
+ indexed.set(attribute.name.toLowerCase(), attribute.value);
20762
+ }
20763
+ return indexed;
20764
+ }
20639
20765
  function attributesToHtml(attributes) {
20640
20766
  if (attributes.length === 0) {
20641
20767
  return "";
@@ -20645,68 +20771,16 @@ function attributesToHtml(attributes) {
20645
20771
  function escapeAttribute(value) {
20646
20772
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
20647
20773
  }
20648
- function buildSnapshotElementAnchor(snapshot, node, snapshotsByDocumentRef, snapshotIndices) {
20649
- const index = getSnapshotIndex(snapshot.documentRef, snapshotsByDocumentRef, snapshotIndices);
20650
- const localAnchor = buildLocalStructuralElementAnchor(index, node);
20651
- return prefixIframeContext(snapshot, localAnchor, snapshotsByDocumentRef, snapshotIndices);
20652
- }
20653
- function prefixIframeContext(snapshot, localPath, snapshotsByDocumentRef, snapshotIndices) {
20654
- if (snapshot.parentDocumentRef === void 0) {
20655
- return sanitizeStructuralElementAnchor(localPath);
20656
- }
20657
- const parentSnapshot = snapshotsByDocumentRef.get(snapshot.parentDocumentRef);
20658
- if (!parentSnapshot) {
20659
- throw new Error(
20660
- `document ${snapshot.documentRef} has parent ${snapshot.parentDocumentRef} but no parent snapshot`
20661
- );
20662
- }
20663
- const parentIndex = getSnapshotIndex(
20664
- parentSnapshot.documentRef,
20665
- snapshotsByDocumentRef,
20666
- snapshotIndices
20667
- );
20668
- const iframeHost = findIframeHostNode(parentIndex, snapshot.documentRef);
20669
- if (!iframeHost) {
20670
- throw new Error(
20671
- `document ${snapshot.documentRef} has parent ${snapshot.parentDocumentRef} but no iframe host`
20672
- );
20673
- }
20674
- const hostPath = buildSnapshotElementAnchor(
20675
- parentSnapshot,
20676
- iframeHost,
20677
- snapshotsByDocumentRef,
20678
- snapshotIndices
20679
- );
20680
- return sanitizeStructuralElementAnchor({
20681
- context: [...hostPath.context, { kind: "iframe", host: hostPath.nodes }, ...localPath.context],
20682
- nodes: localPath.nodes
20683
- });
20684
- }
20685
- function getSnapshotIndex(documentRef, snapshotsByDocumentRef, snapshotIndices) {
20686
- const existing = snapshotIndices.get(documentRef);
20687
- if (existing) {
20688
- return existing;
20689
- }
20690
- const snapshot = snapshotsByDocumentRef.get(documentRef);
20691
- if (!snapshot) {
20692
- throw new Error(`missing DOM snapshot for ${documentRef}`);
20693
- }
20694
- const index = createSnapshotIndex(snapshot);
20695
- snapshotIndices.set(documentRef, index);
20696
- return index;
20697
- }
20698
20774
  function escapeHtml(value) {
20699
20775
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
20700
20776
  }
20701
- var INTERNAL_SNAPSHOT_ATTRIBUTE_NAMES, MAX_LIVE_COUNTER_SYNC_ATTEMPTS, CLEAR_LIVE_COUNTERS_SCRIPT, ASSIGN_SPARSE_COUNTERS_SCRIPT, APPLY_DENSE_COUNTERS_SCRIPT, LiveCounterSyncError;
20777
+ var EXTRACTION_SKIPPED_COUNTER_TAGS, INTERNAL_SNAPSHOT_ATTRIBUTE_NAMES, MAX_LIVE_COUNTER_SYNC_ATTEMPTS, CLEAR_LIVE_COUNTERS_SCRIPT, ASSIGN_SPARSE_COUNTERS_SCRIPT, APPLY_DENSE_COUNTERS_SCRIPT, LiveCounterSyncError;
20702
20778
  var init_compiler = __esm({
20703
20779
  "../runtime-core/src/sdk/snapshot/compiler.ts"() {
20704
- init_src();
20705
- init_path();
20706
- init_selectors();
20707
20780
  init_cleaner();
20708
20781
  init_constants();
20709
20782
  init_marking();
20783
+ EXTRACTION_SKIPPED_COUNTER_TAGS = /* @__PURE__ */ new Set(["html", "head", "body"]);
20710
20784
  INTERNAL_SNAPSHOT_ATTRIBUTE_NAMES = /* @__PURE__ */ new Set([
20711
20785
  "c",
20712
20786
  OPENSTEER_BOUNDARY_ATTR,
@@ -20721,7 +20795,7 @@ var init_compiler = __esm({
20721
20795
  MAX_LIVE_COUNTER_SYNC_ATTEMPTS = 4;
20722
20796
  CLEAR_LIVE_COUNTERS_SCRIPT = `(({ sparseCounterAttr }) => {
20723
20797
  const walk = (root) => {
20724
- for (const child of root.children) {
20798
+ for (const child of Array.from(root?.children || [])) {
20725
20799
  child.removeAttribute("c");
20726
20800
  child.removeAttribute(sparseCounterAttr);
20727
20801
  walk(child);
@@ -20737,7 +20811,7 @@ var init_compiler = __esm({
20737
20811
  ASSIGN_SPARSE_COUNTERS_SCRIPT = `(({ sparseCounterAttr, startCounter }) => {
20738
20812
  let counter = startCounter;
20739
20813
  const walk = (root) => {
20740
- for (const child of root.children) {
20814
+ for (const child of Array.from(root?.children || [])) {
20741
20815
  child.setAttribute(sparseCounterAttr, String(counter++));
20742
20816
  walk(child);
20743
20817
  if (child.shadowRoot) {
@@ -20751,7 +20825,7 @@ var init_compiler = __esm({
20751
20825
  })`;
20752
20826
  APPLY_DENSE_COUNTERS_SCRIPT = `(({ sparseCounterAttr, mapping }) => {
20753
20827
  const walk = (root) => {
20754
- for (const child of root.children) {
20828
+ for (const child of Array.from(root?.children || [])) {
20755
20829
  child.removeAttribute("c");
20756
20830
  const sparse = child.getAttribute(sparseCounterAttr);
20757
20831
  if (sparse !== null) {
@@ -22935,15 +23009,10 @@ function normalizeNamespace2(value) {
22935
23009
  const normalized = String(value ?? "default").trim();
22936
23010
  return normalized.length === 0 ? "default" : normalized;
22937
23011
  }
22938
- function toOpensteerActionResult(result) {
23012
+ function toOpensteerActionResult(target) {
22939
23013
  return {
22940
- target: toOpensteerResolvedTarget2(result.resolved),
22941
- ...result.point === void 0 ? {} : {
22942
- point: {
22943
- x: result.point.x,
22944
- y: result.point.y
22945
- }
22946
- }
23014
+ tagName: toOpensteerTagName2(target.node.nodeName),
23015
+ ...target.persist === void 0 ? {} : { persist: target.persist }
22947
23016
  };
22948
23017
  }
22949
23018
  function toOpensteerResolvedTarget2(target) {
@@ -22953,12 +23022,16 @@ function toOpensteerResolvedTarget2(target) {
22953
23022
  documentRef: target.documentRef,
22954
23023
  documentEpoch: target.documentEpoch,
22955
23024
  nodeRef: target.nodeRef,
22956
- tagName: target.node.nodeName.toUpperCase(),
23025
+ tagName: toOpensteerTagName2(target.node.nodeName),
22957
23026
  pathHint: buildPathSelectorHint(target.replayPath ?? target.anchor),
22958
23027
  ...target.persist === void 0 ? {} : { persist: target.persist },
22959
23028
  ...target.selectorUsed === void 0 ? {} : { selectorUsed: target.selectorUsed }
22960
23029
  };
22961
23030
  }
23031
+ function toOpensteerTagName2(nodeName) {
23032
+ const tagName = String(nodeName).trim().toLowerCase();
23033
+ return tagName.length === 0 ? "element" : tagName;
23034
+ }
22962
23035
  function normalizeOpensteerError(error) {
22963
23036
  return normalizeThrownOpensteerError(error, "Unknown Opensteer runtime failure");
22964
23037
  }
@@ -23276,7 +23349,7 @@ var init_runtime3 = __esm({
23276
23349
  options
23277
23350
  );
23278
23351
  }
23279
- return this.readSessionState();
23352
+ return this.readNavigationSummary();
23280
23353
  }
23281
23354
  const startedAt = Date.now();
23282
23355
  const root = await this.ensureRoot();
@@ -23297,7 +23370,8 @@ var init_runtime3 = __esm({
23297
23370
  openedSessionRef = sessionRef;
23298
23371
  const createdPage = await timeout.runStep(
23299
23372
  () => engine.createPage({
23300
- sessionRef
23373
+ sessionRef,
23374
+ ...input.url === void 0 ? {} : { url: input.url }
23301
23375
  })
23302
23376
  );
23303
23377
  openedPageRef = createdPage.data.pageRef;
@@ -23307,18 +23381,19 @@ var init_runtime3 = __esm({
23307
23381
  await timeout.runStep(() => this.ensureSemantics());
23308
23382
  let frameRef2 = createdPage.frameRef;
23309
23383
  if (input.url !== void 0) {
23310
- const navigation = await this.navigatePage(
23311
- {
23384
+ await timeout.runStep(
23385
+ () => settleWithPolicy(this.policy.settle, {
23312
23386
  operation: "session.open",
23387
+ trigger: "navigation",
23388
+ engine: this.requireEngine(),
23313
23389
  pageRef: createdPage.data.pageRef,
23314
- url: input.url
23315
- },
23316
- timeout
23390
+ signal: timeout.signal,
23391
+ remainingMs: timeout.remainingMs()
23392
+ })
23317
23393
  );
23318
- frameRef2 = navigation.data.mainFrame.frameRef;
23319
23394
  }
23320
23395
  return {
23321
- state: await timeout.runStep(() => this.readSessionState()),
23396
+ state: await timeout.runStep(() => this.readNavigationSummary()),
23322
23397
  frameRef: frameRef2
23323
23398
  };
23324
23399
  },
@@ -23411,7 +23486,11 @@ var init_runtime3 = __esm({
23411
23486
  "page.new cannot use openerPageRef before a session exists"
23412
23487
  );
23413
23488
  }
23414
- return this.open(input.url === void 0 ? {} : { url: input.url }, options);
23489
+ const summary = await this.open(input.url === void 0 ? {} : { url: input.url }, options);
23490
+ return {
23491
+ pageRef: await this.ensurePageRef(),
23492
+ ...summary
23493
+ };
23415
23494
  }
23416
23495
  const startedAt = Date.now();
23417
23496
  try {
@@ -23426,7 +23505,7 @@ var init_runtime3 = __esm({
23426
23505
  })
23427
23506
  );
23428
23507
  this.pageRef = created.data.pageRef;
23429
- return this.readSessionState();
23508
+ return this.readCreatedPageOutput(created.data.pageRef);
23430
23509
  },
23431
23510
  options
23432
23511
  );
@@ -23468,7 +23547,7 @@ var init_runtime3 = __esm({
23468
23547
  () => this.requireEngine().activatePage({ pageRef: input.pageRef })
23469
23548
  );
23470
23549
  this.pageRef = input.pageRef;
23471
- return this.readSessionState();
23550
+ return this.readNavigationSummary(input.pageRef);
23472
23551
  },
23473
23552
  options
23474
23553
  );
@@ -23594,7 +23673,7 @@ var init_runtime3 = __esm({
23594
23673
  timeout.throwIfAborted();
23595
23674
  return {
23596
23675
  navigation: navigation2,
23597
- state: await timeout.runStep(() => this.readSessionState())
23676
+ state: await timeout.runStep(() => this.readNavigationSummary(pageRef))
23598
23677
  };
23599
23678
  },
23600
23679
  (diagnostics) => {
@@ -24828,7 +24907,7 @@ var init_runtime3 = __esm({
24828
24907
  let mutationCaptureDiagnostics;
24829
24908
  let boundaryDiagnostics;
24830
24909
  try {
24831
- const { artifacts, output } = await this.runMutationCapturedOperation(
24910
+ const { artifacts, output, result } = await this.runMutationCapturedOperation(
24832
24911
  "computer.execute",
24833
24912
  {
24834
24913
  ...input.captureNetwork === void 0 ? {} : { captureNetwork: input.captureNetwork },
@@ -24846,9 +24925,14 @@ var init_runtime3 = __esm({
24846
24925
  await this.invalidateLiveSnapshotCounters([pageRef, output2.pageRef], timeout);
24847
24926
  this.pageRef = output2.pageRef;
24848
24927
  const artifacts2 = await this.persistComputerArtifacts(output2, timeout);
24928
+ const result2 = {
24929
+ ...await timeout.runStep(() => this.readNavigationSummary(output2.pageRef)),
24930
+ screenshot: artifacts2.screenshot
24931
+ };
24849
24932
  return {
24850
24933
  artifacts: { manifests: artifacts2.manifests },
24851
- output: artifacts2.output
24934
+ output: output2,
24935
+ result: result2
24852
24936
  };
24853
24937
  } catch (error) {
24854
24938
  boundaryDiagnostics ??= takeActionBoundaryDiagnostics(timeout.signal);
@@ -24885,7 +24969,7 @@ var init_runtime3 = __esm({
24885
24969
  documentEpoch: output.screenshot.documentEpoch
24886
24970
  })
24887
24971
  });
24888
- return output;
24972
+ return result;
24889
24973
  } catch (error) {
24890
24974
  await this.appendTrace({
24891
24975
  operation: "computer.execute",
@@ -25033,8 +25117,9 @@ var init_runtime3 = __esm({
25033
25117
  mutationCaptureDiagnostics = diagnostics;
25034
25118
  }
25035
25119
  );
25036
- const output = toOpensteerActionResult(executed.result);
25120
+ const output = toOpensteerActionResult(executed.result.resolved);
25037
25121
  const actionEvents = "events" in executed.result ? executed.result.events : void 0;
25122
+ const resolvedTarget = toOpensteerResolvedTarget2(executed.result.resolved);
25038
25123
  await this.appendTrace({
25039
25124
  operation,
25040
25125
  startedAt,
@@ -25042,8 +25127,13 @@ var init_runtime3 = __esm({
25042
25127
  outcome: "ok",
25043
25128
  ...actionEvents === void 0 ? {} : { events: actionEvents },
25044
25129
  data: {
25045
- target: output.target,
25046
- ...output.point === void 0 ? {} : { point: output.point },
25130
+ target: resolvedTarget,
25131
+ ..."point" in executed.result && executed.result.point !== void 0 ? {
25132
+ point: {
25133
+ x: executed.result.point.x,
25134
+ y: executed.result.point.y
25135
+ }
25136
+ } : {},
25047
25137
  ...boundaryDiagnostics === void 0 ? {} : { settle: boundaryDiagnostics },
25048
25138
  ...buildMutationCaptureTraceData(mutationCaptureDiagnostics)
25049
25139
  },
@@ -26508,20 +26598,20 @@ var init_runtime3 = __esm({
26508
26598
  throw error;
26509
26599
  }
26510
26600
  }
26511
- async readSessionState() {
26512
- const pageRef = await this.ensurePageRef();
26601
+ async readNavigationSummary(targetPageRef) {
26602
+ const pageRef = targetPageRef ?? await this.ensurePageRef();
26513
26603
  const pageInfo = await this.requireEngine().getPageInfo({ pageRef });
26514
- const sessionRef = this.sessionRef;
26515
- if (!sessionRef) {
26516
- throw new Error("Opensteer session is not initialized");
26517
- }
26518
26604
  return {
26519
- sessionRef,
26520
- pageRef,
26521
26605
  url: pageInfo.url,
26522
26606
  title: pageInfo.title
26523
26607
  };
26524
26608
  }
26609
+ async readCreatedPageOutput(pageRef) {
26610
+ return {
26611
+ pageRef,
26612
+ ...await this.readNavigationSummary(pageRef)
26613
+ };
26614
+ }
26525
26615
  async captureSnapshotArtifacts(pageRef, options, timeout) {
26526
26616
  const root = this.requireRoot();
26527
26617
  const mainFrame = await timeout.runStep(() => getMainFrame(this.requireEngine(), pageRef));
@@ -26593,12 +26683,12 @@ var init_runtime3 = __esm({
26593
26683
  const screenshotPayload = manifestToExternalBinaryLocation(root.rootPath, screenshotManifest);
26594
26684
  return {
26595
26685
  manifests,
26596
- output: {
26597
- ...output,
26598
- screenshot: {
26599
- ...output.screenshot,
26600
- payload: screenshotPayload
26601
- }
26686
+ screenshot: {
26687
+ payload: screenshotPayload,
26688
+ format: output.screenshot.format,
26689
+ size: output.screenshot.size,
26690
+ coordinateSpace: output.screenshot.coordinateSpace,
26691
+ ...output.screenshot.clip === void 0 ? {} : { clip: output.screenshot.clip }
26602
26692
  }
26603
26693
  };
26604
26694
  }
@@ -28683,7 +28773,11 @@ function generateReplayScript(options) {
28683
28773
  ``,
28684
28774
  ...renderOpensteerBootstrap(replayTarget),
28685
28775
  ``,
28686
- `const ${initialPageId} = (await opensteer.open(${JSON.stringify(initialPages[0]?.initialUrl ?? "")})).pageRef;`,
28776
+ `await opensteer.open(${JSON.stringify(initialPages[0]?.initialUrl ?? "")});`,
28777
+ `const ${initialPageId} = (await opensteer.listPages()).activePageRef;`,
28778
+ `if (!${initialPageId}) {`,
28779
+ ` throw new Error("Opensteer did not report an active page after open().");`,
28780
+ `}`,
28687
28781
  `let activePageRef: string | undefined = ${initialPageId};`,
28688
28782
  ``,
28689
28783
  `async function ensureActive(pageRef: string): Promise<void> {`,
@@ -30944,7 +31038,7 @@ var init_opensteer = __esm({
30944
31038
 
30945
31039
  // package.json
30946
31040
  var package_default = {
30947
- version: "0.9.6"};
31041
+ version: "0.9.7"};
30948
31042
 
30949
31043
  // src/cli/bin.ts
30950
31044
  init_browser_manager();
@@ -32102,6 +32196,7 @@ function joinRest(rest, startIndex) {
32102
32196
  function renderOperationOutput(operation, result, input) {
32103
32197
  switch (operation) {
32104
32198
  case "session.open":
32199
+ case "page.activate":
32105
32200
  case "page.goto":
32106
32201
  return renderJson(formatNavigationOutput(result));
32107
32202
  case "page.snapshot":
@@ -32110,14 +32205,14 @@ function renderOperationOutput(operation, result, input) {
32110
32205
  case "dom.hover":
32111
32206
  case "dom.input":
32112
32207
  case "dom.scroll":
32113
- return renderJson(formatActionOutput(result, input));
32208
+ return renderJson(formatActionOutput(result));
32114
32209
  case "dom.extract":
32115
32210
  return renderJson(formatExtractOutput(result));
32116
32211
  case "page.list":
32117
- case "page.new":
32118
- case "page.activate":
32119
32212
  case "page.close":
32120
32213
  return formatTabOutput(result);
32214
+ case "page.new":
32215
+ return renderJson(formatCreatedPageOutput(result));
32121
32216
  case "network.query":
32122
32217
  return formatNetworkQueryOutput(result, input);
32123
32218
  case "network.detail":
@@ -32155,6 +32250,12 @@ function formatNavigationOutput(result) {
32155
32250
  ...readStringField(result, "title") === void 0 ? {} : { title: readStringField(result, "title") }
32156
32251
  };
32157
32252
  }
32253
+ function formatCreatedPageOutput(result) {
32254
+ return {
32255
+ ...readStringField(result, "pageRef") === void 0 ? {} : { pageRef: readStringField(result, "pageRef") },
32256
+ ...formatNavigationOutput(result)
32257
+ };
32258
+ }
32158
32259
  function formatSnapshotOutput(result) {
32159
32260
  if (result !== null && typeof result === "object" && typeof result.html === "string") {
32160
32261
  return `${result.html}
@@ -32162,36 +32263,11 @@ function formatSnapshotOutput(result) {
32162
32263
  }
32163
32264
  return renderJson(result);
32164
32265
  }
32165
- function formatActionOutput(result, input) {
32166
- const target = readObjectField(result, "target");
32167
- const output = {
32168
- ...readStringField(target, "tagName") === void 0 ? {} : { tagName: readStringField(target, "tagName") },
32169
- ...readStringField(target, "pathHint") === void 0 ? {} : { pathHint: readStringField(target, "pathHint") }
32266
+ function formatActionOutput(result) {
32267
+ return {
32268
+ ...readStringField(result, "tagName") === void 0 ? {} : { tagName: readStringField(result, "tagName") },
32269
+ ...readStringField(result, "persist") === void 0 ? {} : { persist: readStringField(result, "persist") }
32170
32270
  };
32171
- const point = readObjectField(result, "point");
32172
- if (point !== void 0) {
32173
- output.point = {
32174
- ...readNumberField(point, "x") === void 0 ? {} : { x: readNumberField(point, "x") },
32175
- ...readNumberField(point, "y") === void 0 ? {} : { y: readNumberField(point, "y") }
32176
- };
32177
- }
32178
- const persist = readStringField(target, "persist");
32179
- if (persist !== void 0) {
32180
- output.persist = persist;
32181
- }
32182
- const text = readStringField(input, "text");
32183
- if (text !== void 0) {
32184
- output.text = text;
32185
- }
32186
- const direction = readStringField(input, "direction");
32187
- if (direction !== void 0) {
32188
- output.direction = direction;
32189
- }
32190
- const amount = readNumberField(input, "amount");
32191
- if (amount !== void 0) {
32192
- output.amount = amount;
32193
- }
32194
- return output;
32195
32271
  }
32196
32272
  function formatExtractOutput(result) {
32197
32273
  const data = readUnknownField(result, "data");
@@ -32423,20 +32499,18 @@ function formatStateOutput(result) {
32423
32499
  `;
32424
32500
  }
32425
32501
  function formatComputerOutput(result) {
32426
- const action = readObjectField(result, "action");
32427
32502
  const screenshot = readObjectField(result, "screenshot");
32428
32503
  const payload = readObjectField(screenshot, "payload");
32429
- const timing = readObjectField(result, "timing");
32430
32504
  return {
32431
- ...action === void 0 ? {} : { action },
32505
+ ...readStringField(result, "url") === void 0 ? {} : { url: readStringField(result, "url") },
32506
+ ...readStringField(result, "title") === void 0 ? {} : { title: readStringField(result, "title") },
32432
32507
  ...payload === void 0 ? {} : {
32433
32508
  screenshot: {
32434
32509
  ...readStringField(payload, "uri") === void 0 ? {} : { uri: readStringField(payload, "uri") },
32435
32510
  ...readStringField(screenshot, "format") === void 0 ? {} : { format: readStringField(screenshot, "format") },
32436
32511
  ...readObjectField(screenshot, "size") === void 0 ? {} : { size: readObjectField(screenshot, "size") }
32437
32512
  }
32438
- },
32439
- ...timing === void 0 ? {} : { timingMs: timing }
32513
+ }
32440
32514
  };
32441
32515
  }
32442
32516
  function formatScriptsCaptureOutput(result) {
@@ -33098,7 +33172,7 @@ async function collectOpensteerStatus(input) {
33098
33172
  ...output,
33099
33173
  rootPath,
33100
33174
  lanes: {
33101
- local: describeLocalLane(localRecord, input.provider.mode === "local"),
33175
+ local: await describeLocalLane(localRecord, input.provider.mode === "local"),
33102
33176
  cloud: await describeCloudLane({
33103
33177
  record: cloudRecord,
33104
33178
  current: input.provider.mode === "cloud",
@@ -33150,8 +33224,38 @@ async function readWorkspaceCloudRecord(rootPath) {
33150
33224
  }
33151
33225
  return readPersistedCloudSessionRecord(rootPath);
33152
33226
  }
33153
- function describeLocalLane(record, current) {
33154
- if (record === void 0 || !isProcessRunning(record.pid)) {
33227
+ async function describeLocalLane(record, current) {
33228
+ if (record === void 0) {
33229
+ return {
33230
+ provider: "local",
33231
+ status: "idle",
33232
+ current,
33233
+ summary: "none"
33234
+ };
33235
+ }
33236
+ if (getPersistedLocalBrowserSessionOwnership(record) === "attached") {
33237
+ if (!await isAttachedLocalBrowserSessionReachable(record)) {
33238
+ return {
33239
+ provider: "local",
33240
+ status: "stale",
33241
+ current,
33242
+ summary: "attached browser unavailable",
33243
+ detail: record.engine,
33244
+ engine: record.engine
33245
+ };
33246
+ }
33247
+ const browser2 = record.executablePath ? path10__default.default.basename(record.executablePath).replace(/\.[A-Za-z0-9]+$/u, "") : void 0;
33248
+ return {
33249
+ provider: "local",
33250
+ status: "active",
33251
+ current,
33252
+ summary: "attached browser",
33253
+ detail: browser2 ?? record.engine,
33254
+ engine: record.engine,
33255
+ ...browser2 === void 0 ? {} : { browser: browser2 }
33256
+ };
33257
+ }
33258
+ if (!isProcessRunning(record.pid)) {
33155
33259
  return {
33156
33260
  provider: "local",
33157
33261
  status: "idle",
@@ -33302,13 +33406,13 @@ async function resolveSessionSummary(manifest) {
33302
33406
  return {
33303
33407
  sessionId: manifest.sessionId,
33304
33408
  label: manifest.workspace ?? (path10__default.default.basename(manifest.rootPath) || manifest.sessionId),
33305
- status: isProcessRunning(record.pid) ? "live" : "stale",
33409
+ status: getPersistedLocalBrowserSessionOwnership(record) === "attached" || isProcessRunning(record.pid) ? "live" : "stale",
33306
33410
  ...manifest.workspace === void 0 ? {} : { workspace: manifest.workspace },
33307
33411
  rootPath: manifest.rootPath,
33308
33412
  engine: record.engine,
33309
33413
  ownership: manifest.ownership,
33310
- pid: record.pid,
33311
33414
  startedAt: record.startedAt,
33415
+ ...record.pid > 0 ? { pid: record.pid } : {},
33312
33416
  ...browserName === void 0 ? {} : { browserName }
33313
33417
  };
33314
33418
  }
@@ -33336,7 +33440,16 @@ async function readLiveRecord(manifest) {
33336
33440
  if (!record) {
33337
33441
  return void 0;
33338
33442
  }
33339
- if (record.pid !== manifest.pid || record.startedAt !== manifest.startedAt || !isProcessRunning(record.pid)) {
33443
+ if (buildLocalViewSessionIdForRecord({
33444
+ rootPath: manifest.rootPath,
33445
+ live: record
33446
+ }) !== manifest.sessionId || record.startedAt !== manifest.startedAt || record.engine !== manifest.engine || getPersistedLocalBrowserSessionOwnership(record) !== manifest.ownership) {
33447
+ return void 0;
33448
+ }
33449
+ if (getPersistedLocalBrowserSessionOwnership(record) === "attached") {
33450
+ return await isAttachedLocalBrowserSessionReachable(record) ? record : void 0;
33451
+ }
33452
+ if (record.pid !== manifest.pid || !isProcessRunning(record.pid)) {
33340
33453
  return void 0;
33341
33454
  }
33342
33455
  return record;
@@ -35105,13 +35218,19 @@ async function resolveWorkspaceSessionId(input) {
35105
35218
  workspace: input.workspace
35106
35219
  });
35107
35220
  const live = await readPersistedLocalBrowserSessionRecord(rootPath);
35108
- if (!live || !isProcessRunning(live.pid)) {
35221
+ if (!live) {
35109
35222
  return void 0;
35110
35223
  }
35111
- return buildLocalViewSessionId({
35224
+ if (getPersistedLocalBrowserSessionOwnership(live) === "attached") {
35225
+ if (!await isAttachedLocalBrowserSessionReachable(live)) {
35226
+ return void 0;
35227
+ }
35228
+ } else if (!isProcessRunning(live.pid)) {
35229
+ return void 0;
35230
+ }
35231
+ return buildLocalViewSessionIdForRecord({
35112
35232
  rootPath,
35113
- pid: live.pid,
35114
- startedAt: live.startedAt
35233
+ live
35115
35234
  });
35116
35235
  }
35117
35236
  function assertNoViewPreferenceFlag(parsed) {