opensteer 0.9.6 → 0.9.8

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 (35) hide show
  1. package/README.md +2 -2
  2. package/dist/{chunk-3I3A5OLB.js → chunk-BPGXP3RF.js} +257 -24
  3. package/dist/chunk-BPGXP3RF.js.map +1 -0
  4. package/dist/{chunk-3XBQRZZC.js → chunk-EXXRLPLI.js} +158 -46
  5. package/dist/chunk-EXXRLPLI.js.map +1 -0
  6. package/dist/{chunk-T5P2QGZ3.js → chunk-GKYBP3KD.js} +154 -13
  7. package/dist/chunk-GKYBP3KD.js.map +1 -0
  8. package/dist/{chunk-BVRIPCWA.js → chunk-LFWP5RXF.js} +500 -513
  9. package/dist/chunk-LFWP5RXF.js.map +1 -0
  10. package/dist/{chunk-L4NF74KI.js → chunk-SOJEWKSW.js} +5 -5
  11. package/dist/{chunk-L4NF74KI.js.map → chunk-SOJEWKSW.js.map} +1 -1
  12. package/dist/cli/bin.cjs +1230 -660
  13. package/dist/cli/bin.cjs.map +1 -1
  14. package/dist/cli/bin.js +166 -72
  15. package/dist/cli/bin.js.map +1 -1
  16. package/dist/index.cjs +793 -565
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +36 -51
  19. package/dist/index.d.ts +36 -51
  20. package/dist/index.js +4 -4
  21. package/dist/local-view/public/assets/app.js +10 -1
  22. package/dist/local-view/serve-entry.cjs +1022 -591
  23. package/dist/local-view/serve-entry.cjs.map +1 -1
  24. package/dist/local-view/serve-entry.js +2 -2
  25. package/dist/opensteer-XLPY343Y.js +6 -0
  26. package/dist/{opensteer-UGA6YBRN.js.map → opensteer-XLPY343Y.js.map} +1 -1
  27. package/dist/{session-control-U3L5H2ZI.js → session-control-FVKKD45R.js} +4 -4
  28. package/dist/{session-control-U3L5H2ZI.js.map → session-control-FVKKD45R.js.map} +1 -1
  29. package/package.json +5 -5
  30. package/skills/recorder/SKILL.md +2 -2
  31. package/dist/chunk-3I3A5OLB.js.map +0 -1
  32. package/dist/chunk-3XBQRZZC.js.map +0 -1
  33. package/dist/chunk-BVRIPCWA.js.map +0 -1
  34. package/dist/chunk-T5P2QGZ3.js.map +0 -1
  35. 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
+ }
1683
1704
  }
1684
1705
  }
1685
- var LOCK_RETRY_DELAYS_MS;
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";
1778
+ }
1779
+ }
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}`;
@@ -2840,6 +2940,9 @@ var init_metadata2 = __esm({
2840
2940
  {
2841
2941
  pageRef: pageRefSchema,
2842
2942
  sessionRef: sessionRefSchema,
2943
+ targetId: stringSchema({
2944
+ description: "Underlying browser target identifier when available."
2945
+ }),
2843
2946
  openerPageRef: pageRefSchema,
2844
2947
  url: stringSchema({
2845
2948
  description: "Current main-frame URL."
@@ -6677,15 +6780,15 @@ function assertValidSemanticOperationInput(name, input) {
6677
6780
  }
6678
6781
  );
6679
6782
  }
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;
6783
+ 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
6784
  var init_semantic = __esm({
6682
6785
  "../protocol/src/semantic.ts"() {
6683
6786
  init_json2();
6684
6787
  init_errors2();
6788
+ init_binary_location();
6685
6789
  init_identity2();
6686
6790
  init_geometry2();
6687
6791
  init_metadata2();
6688
- init_events2();
6689
6792
  init_envelopes();
6690
6793
  init_snapshots2();
6691
6794
  init_artifacts2();
@@ -6909,39 +7012,14 @@ var init_semantic = __esm({
6909
7012
  title: "OpensteerTargetInput"
6910
7013
  }
6911
7014
  );
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
7015
  opensteerActionResultSchema = objectSchema(
6938
7016
  {
6939
- target: opensteerResolvedTargetSchema,
6940
- point: pointSchema
7017
+ tagName: stringSchema({ minLength: 1 }),
7018
+ persist: stringSchema({ minLength: 1 })
6941
7019
  },
6942
7020
  {
6943
7021
  title: "OpensteerActionResult",
6944
- required: ["target"]
7022
+ required: ["tagName"]
6945
7023
  }
6946
7024
  );
6947
7025
  opensteerSnapshotCounterSchema = objectSchema(
@@ -6987,16 +7065,14 @@ var init_semantic = __esm({
6987
7065
  ]
6988
7066
  }
6989
7067
  );
6990
- opensteerSessionStateSchema = objectSchema(
7068
+ opensteerNavigationSummarySchema = objectSchema(
6991
7069
  {
6992
- sessionRef: sessionRefSchema,
6993
- pageRef: pageRefSchema,
6994
7070
  url: stringSchema(),
6995
7071
  title: stringSchema()
6996
7072
  },
6997
7073
  {
6998
- title: "OpensteerSessionState",
6999
- required: ["sessionRef", "pageRef", "url", "title"]
7074
+ title: "OpensteerNavigationSummary",
7075
+ required: ["url", "title"]
7000
7076
  }
7001
7077
  );
7002
7078
  opensteerOpenInputSchema = objectSchema(
@@ -7064,6 +7140,17 @@ var init_semantic = __esm({
7064
7140
  required: ["closedPageRef", "pages"]
7065
7141
  }
7066
7142
  );
7143
+ opensteerPageNewOutputSchema = objectSchema(
7144
+ {
7145
+ pageRef: pageRefSchema,
7146
+ url: stringSchema(),
7147
+ title: stringSchema()
7148
+ },
7149
+ {
7150
+ title: "OpensteerPageNewOutput",
7151
+ required: ["pageRef", "url", "title"]
7152
+ }
7153
+ );
7067
7154
  opensteerPageGotoInputSchema = objectSchema(
7068
7155
  {
7069
7156
  url: stringSchema(),
@@ -7450,72 +7537,28 @@ var init_semantic = __esm({
7450
7537
  required: ["action"]
7451
7538
  }
7452
7539
  );
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(
7540
+ opensteerScreenshotSummarySchema = objectSchema(
7486
7541
  {
7487
- x: numberSchema({ exclusiveMinimum: 0 }),
7488
- y: numberSchema({ exclusiveMinimum: 0 })
7542
+ payload: externalBinaryLocationSchema,
7543
+ format: screenshotFormatSchema,
7544
+ size: sizeSchema,
7545
+ coordinateSpace: coordinateSpaceSchema,
7546
+ clip: rectSchema
7489
7547
  },
7490
7548
  {
7491
- title: "OpensteerComputerDisplayScale",
7492
- required: ["x", "y"]
7549
+ title: "OpensteerScreenshotSummary",
7550
+ required: ["payload", "format", "size", "coordinateSpace"]
7493
7551
  }
7494
7552
  );
7495
7553
  opensteerComputerExecuteOutputSchema = objectSchema(
7496
7554
  {
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
7555
+ url: stringSchema(),
7556
+ title: stringSchema(),
7557
+ screenshot: opensteerScreenshotSummarySchema
7506
7558
  },
7507
7559
  {
7508
7560
  title: "OpensteerComputerExecuteOutput",
7509
- required: [
7510
- "action",
7511
- "pageRef",
7512
- "screenshot",
7513
- "displayViewport",
7514
- "nativeViewport",
7515
- "displayScale",
7516
- "events",
7517
- "timing"
7518
- ]
7561
+ required: ["url", "title", "screenshot"]
7519
7562
  }
7520
7563
  );
7521
7564
  opensteerSemanticOperationSpecificationsBase = [
@@ -7523,7 +7566,7 @@ var init_semantic = __esm({
7523
7566
  name: "session.open",
7524
7567
  description: "Open or resume the current Opensteer session and primary page.",
7525
7568
  inputSchema: opensteerOpenInputSchema,
7526
- outputSchema: opensteerSessionStateSchema,
7569
+ outputSchema: opensteerNavigationSummarySchema,
7527
7570
  requiredCapabilities: ["sessions.manage", "pages.manage"],
7528
7571
  resolveRequiredCapabilities: (input) => input.url === void 0 ? ["sessions.manage", "pages.manage"] : ["sessions.manage", "pages.manage", "pages.navigate"]
7529
7572
  }),
@@ -7538,7 +7581,7 @@ var init_semantic = __esm({
7538
7581
  name: "page.new",
7539
7582
  description: "Create and optionally navigate a new top-level page in the current session.",
7540
7583
  inputSchema: opensteerPageNewInputSchema,
7541
- outputSchema: opensteerSessionStateSchema,
7584
+ outputSchema: opensteerPageNewOutputSchema,
7542
7585
  requiredCapabilities: ["pages.manage"],
7543
7586
  resolveRequiredCapabilities: (input) => input.url === void 0 ? ["pages.manage"] : ["pages.manage", "pages.navigate"]
7544
7587
  }),
@@ -7546,7 +7589,7 @@ var init_semantic = __esm({
7546
7589
  name: "page.activate",
7547
7590
  description: "Activate an existing top-level page in the current session.",
7548
7591
  inputSchema: opensteerPageActivateInputSchema,
7549
- outputSchema: opensteerSessionStateSchema,
7592
+ outputSchema: opensteerNavigationSummarySchema,
7550
7593
  requiredCapabilities: ["pages.manage", "inspect.pages"]
7551
7594
  }),
7552
7595
  defineSemanticOperationSpec({
@@ -7560,7 +7603,7 @@ var init_semantic = __esm({
7560
7603
  name: "page.goto",
7561
7604
  description: "Navigate the current Opensteer page to a new URL.",
7562
7605
  inputSchema: opensteerPageGotoInputSchema,
7563
- outputSchema: opensteerSessionStateSchema,
7606
+ outputSchema: opensteerNavigationSummarySchema,
7564
7607
  requiredCapabilities: ["pages.navigate"]
7565
7608
  }),
7566
7609
  defineSemanticOperationSpec({
@@ -9103,6 +9146,9 @@ var init_observations = __esm({
9103
9146
  this.store = store;
9104
9147
  this.sessionId = sessionId;
9105
9148
  }
9149
+ configure(input) {
9150
+ return this.store.configureSession(this.sessionId, input);
9151
+ }
9106
9152
  append(input) {
9107
9153
  return this.store.appendEvent(this.sessionId, input);
9108
9154
  }
@@ -9133,40 +9179,14 @@ var init_observations = __esm({
9133
9179
  const sessionId = normalizeNonEmptyString("sessionId", input.sessionId);
9134
9180
  const openedAt = normalizeTimestamp("openedAt", input.openedAt ?? Date.now());
9135
9181
  const config = normalizeObservabilityConfig(input.config);
9136
- const redactor = createObservationRedactor(config);
9137
- this.redactors.set(sessionId, redactor);
9138
- const redactedLabels = redactor.redactLabels(config.labels);
9139
- const redactedTraceContext = redactor.redactTraceContext(config.traceContext);
9140
- await withFilesystemLock(this.sessionLockPath(sessionId), async () => {
9141
- const existing = await this.reconcileSessionManifest(sessionId);
9142
- if (existing === void 0) {
9143
- await ensureDirectory(this.sessionEventsDirectory(sessionId));
9144
- await ensureDirectory(this.sessionArtifactsDirectory(sessionId));
9145
- const session = {
9146
- sessionId,
9147
- profile: config.profile,
9148
- ...redactedLabels === void 0 ? {} : { labels: redactedLabels },
9149
- ...redactedTraceContext === void 0 ? {} : { traceContext: redactedTraceContext },
9150
- openedAt,
9151
- updatedAt: openedAt,
9152
- currentSequence: 0,
9153
- eventCount: 0,
9154
- artifactCount: 0
9155
- };
9156
- await writeJsonFileExclusive(this.sessionManifestPath(sessionId), session);
9157
- return;
9158
- }
9159
- const patched = {
9160
- ...existing,
9161
- profile: config.profile,
9162
- ...redactedLabels === void 0 ? {} : { labels: redactedLabels },
9163
- ...redactedTraceContext === void 0 ? {} : { traceContext: redactedTraceContext },
9164
- updatedAt: Math.max(existing.updatedAt, openedAt)
9165
- };
9166
- await writeJsonFileAtomic(this.sessionManifestPath(sessionId), patched);
9167
- });
9182
+ await this.applySessionConfiguration(sessionId, config, openedAt);
9168
9183
  return new FilesystemSessionSink(this, sessionId);
9169
9184
  }
9185
+ async configureSession(sessionId, input) {
9186
+ const updatedAt = normalizeTimestamp("updatedAt", input.updatedAt ?? Date.now());
9187
+ const config = normalizeObservabilityConfig(input.config);
9188
+ await this.applySessionConfiguration(sessionId, config, updatedAt);
9189
+ }
9170
9190
  async getSession(sessionId) {
9171
9191
  const manifestPath = this.sessionManifestPath(sessionId);
9172
9192
  if (!await pathExists(manifestPath)) {
@@ -9387,6 +9407,40 @@ var init_observations = __esm({
9387
9407
  sessionLockPath(sessionId) {
9388
9408
  return path10__default.default.join(this.sessionDirectory(sessionId), ".lock");
9389
9409
  }
9410
+ async applySessionConfiguration(sessionId, config, timestamp) {
9411
+ const redactor = createObservationRedactor(config);
9412
+ this.redactors.set(sessionId, redactor);
9413
+ const redactedLabels = redactor.redactLabels(config.labels);
9414
+ const redactedTraceContext = redactor.redactTraceContext(config.traceContext);
9415
+ await withFilesystemLock(this.sessionLockPath(sessionId), async () => {
9416
+ const existing = await this.reconcileSessionManifest(sessionId);
9417
+ if (existing === void 0) {
9418
+ await ensureDirectory(this.sessionEventsDirectory(sessionId));
9419
+ await ensureDirectory(this.sessionArtifactsDirectory(sessionId));
9420
+ const session = {
9421
+ sessionId,
9422
+ profile: config.profile,
9423
+ ...redactedLabels === void 0 ? {} : { labels: redactedLabels },
9424
+ ...redactedTraceContext === void 0 ? {} : { traceContext: redactedTraceContext },
9425
+ openedAt: timestamp,
9426
+ updatedAt: timestamp,
9427
+ currentSequence: 0,
9428
+ eventCount: 0,
9429
+ artifactCount: 0
9430
+ };
9431
+ await writeJsonFileExclusive(this.sessionManifestPath(sessionId), session);
9432
+ return;
9433
+ }
9434
+ const patched = {
9435
+ ...existing,
9436
+ profile: config.profile,
9437
+ ...redactedLabels === void 0 ? {} : { labels: redactedLabels },
9438
+ ...redactedTraceContext === void 0 ? {} : { traceContext: redactedTraceContext },
9439
+ updatedAt: Math.max(existing.updatedAt, timestamp)
9440
+ };
9441
+ await writeJsonFileAtomic(this.sessionManifestPath(sessionId), patched);
9442
+ });
9443
+ }
9390
9444
  async reconcileSessionManifest(sessionId) {
9391
9445
  const session = await this.getSession(sessionId);
9392
9446
  if (session === void 0) {
@@ -9802,16 +9856,37 @@ async function writePersistedSessionRecord(rootPath, record) {
9802
9856
  async function clearPersistedSessionRecord(rootPath, provider) {
9803
9857
  await promises.rm(resolveLiveSessionRecordPath(rootPath, provider), { force: true });
9804
9858
  }
9859
+ function getPersistedLocalBrowserSessionOwnership(record) {
9860
+ return record.ownership === "attached" ? "attached" : "owned";
9861
+ }
9862
+ async function isAttachedLocalBrowserSessionReachable(record) {
9863
+ if (getPersistedLocalBrowserSessionOwnership(record) !== "attached") {
9864
+ return false;
9865
+ }
9866
+ if (record.engine !== "playwright" || record.endpoint === void 0) {
9867
+ return false;
9868
+ }
9869
+ try {
9870
+ await inspectCdpEndpoint({
9871
+ endpoint: record.endpoint,
9872
+ timeoutMs: 1500
9873
+ });
9874
+ return true;
9875
+ } catch {
9876
+ return false;
9877
+ }
9878
+ }
9805
9879
  function isPersistedCloudSessionRecord(value) {
9806
- 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);
9880
+ return value.layout === OPENSTEER_LIVE_SESSION_LAYOUT && value.version === OPENSTEER_LIVE_SESSION_VERSION && value.provider === "cloud" && typeof value.sessionId === "string" && value.sessionId.length > 0 && (value.activePageUrl === void 0 || typeof value.activePageUrl === "string") && (value.activePageTitle === void 0 || typeof value.activePageTitle === "string") && typeof value.startedAt === "number" && Number.isFinite(value.startedAt) && typeof value.updatedAt === "number" && Number.isFinite(value.updatedAt);
9807
9881
  }
9808
9882
  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;
9883
+ 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") && (value.activePageUrl === void 0 || typeof value.activePageUrl === "string") && (value.activePageTitle === void 0 || typeof value.activePageTitle === "string") && 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
9884
  }
9811
9885
  var OPENSTEER_LIVE_SESSION_LAYOUT, OPENSTEER_LIVE_SESSION_VERSION;
9812
9886
  var init_live_session = __esm({
9813
9887
  "src/live-session.ts"() {
9814
9888
  init_filesystem2();
9889
+ init_cdp_discovery();
9815
9890
  OPENSTEER_LIVE_SESSION_LAYOUT = "opensteer-session";
9816
9891
  OPENSTEER_LIVE_SESSION_VERSION = 1;
9817
9892
  }
@@ -10300,19 +10375,40 @@ var init_service = __esm({
10300
10375
  }
10301
10376
  });
10302
10377
  function buildLocalViewSessionId(input) {
10378
+ const ownership = input.ownership ?? "owned";
10379
+ const identity = ownership === "attached" ? input.endpoint ?? input.remoteDebuggingUrl ?? input.baseUrl ?? "attached" : `pid:${String(input.pid ?? 0)}`;
10303
10380
  const hash = crypto.createHash("sha256").update(`${input.rootPath}
10304
- ${String(input.pid)}
10381
+ ${ownership}
10382
+ ${identity}
10305
10383
  ${String(input.startedAt)}`).digest("hex");
10306
10384
  return `local_${hash.slice(0, 24)}`;
10307
10385
  }
10386
+ function buildLocalViewSessionIdForRecord(input) {
10387
+ const ownership = getPersistedLocalBrowserSessionOwnership(input.live);
10388
+ if (ownership === "attached") {
10389
+ return buildLocalViewSessionId({
10390
+ rootPath: input.rootPath,
10391
+ ownership,
10392
+ startedAt: input.live.startedAt,
10393
+ ...input.live.endpoint === void 0 ? {} : { endpoint: input.live.endpoint },
10394
+ ...input.live.baseUrl === void 0 ? {} : { baseUrl: input.live.baseUrl },
10395
+ ...input.live.remoteDebuggingUrl === void 0 ? {} : { remoteDebuggingUrl: input.live.remoteDebuggingUrl }
10396
+ });
10397
+ }
10398
+ return buildLocalViewSessionId({
10399
+ rootPath: input.rootPath,
10400
+ ownership,
10401
+ startedAt: input.live.startedAt,
10402
+ pid: input.live.pid
10403
+ });
10404
+ }
10308
10405
  function createLocalViewSessionManifest(input) {
10309
10406
  return {
10310
10407
  layout: OPENSTEER_LOCAL_VIEW_SESSION_LAYOUT,
10311
10408
  version: OPENSTEER_LOCAL_VIEW_SESSION_VERSION,
10312
- sessionId: buildLocalViewSessionId({
10409
+ sessionId: buildLocalViewSessionIdForRecord({
10313
10410
  rootPath: input.rootPath,
10314
- pid: input.live.pid,
10315
- startedAt: input.live.startedAt
10411
+ live: input.live
10316
10412
  }),
10317
10413
  rootPath: input.rootPath,
10318
10414
  ...input.workspace === void 0 ? {} : { workspace: input.workspace },
@@ -10363,6 +10459,7 @@ var OPENSTEER_LOCAL_VIEW_SESSION_LAYOUT, OPENSTEER_LOCAL_VIEW_SESSION_VERSION;
10363
10459
  var init_session_manifest = __esm({
10364
10460
  "src/local-view/session-manifest.ts"() {
10365
10461
  init_filesystem2();
10462
+ init_live_session();
10366
10463
  init_runtime_dir();
10367
10464
  OPENSTEER_LOCAL_VIEW_SESSION_LAYOUT = "opensteer-local-view-session";
10368
10465
  OPENSTEER_LOCAL_VIEW_SESSION_VERSION = 1;
@@ -10531,6 +10628,7 @@ function toPersistedLocalBrowserSessionRecord(workspace, live) {
10531
10628
  version: 1,
10532
10629
  provider: "local",
10533
10630
  ...workspace === void 0 ? {} : { workspace },
10631
+ ownership: live.ownership,
10534
10632
  engine: live.engine,
10535
10633
  ...live.endpoint === void 0 ? {} : { endpoint: live.endpoint },
10536
10634
  ...live.baseUrl === void 0 ? {} : { baseUrl: live.baseUrl },
@@ -10546,6 +10644,7 @@ function toPersistedLocalBrowserSessionRecord(workspace, live) {
10546
10644
  function toWorkspaceLiveBrowserRecord(record) {
10547
10645
  return {
10548
10646
  mode: "persistent",
10647
+ ownership: getPersistedLocalBrowserSessionOwnership(record),
10549
10648
  engine: record.engine,
10550
10649
  ...record.endpoint === void 0 ? {} : { endpoint: record.endpoint },
10551
10650
  ...record.baseUrl === void 0 ? {} : { baseUrl: record.baseUrl },
@@ -10572,7 +10671,12 @@ function isAttachBrowserOptions(browser) {
10572
10671
  async function resolveAttachEndpoint(browser) {
10573
10672
  const endpoint = browser?.endpoint?.trim();
10574
10673
  if (endpoint && endpoint.length > 0) {
10575
- return endpoint;
10674
+ const inspected = await inspectCdpEndpoint({
10675
+ endpoint,
10676
+ ...browser?.headers === void 0 ? {} : { headers: browser.headers },
10677
+ timeoutMs: DEFAULT_TIMEOUT_MS
10678
+ });
10679
+ return inspected.endpoint;
10576
10680
  }
10577
10681
  const selection = await selectAttachBrowserCandidate({
10578
10682
  timeoutMs: DEFAULT_TIMEOUT_MS
@@ -10998,7 +11102,7 @@ var init_browser_manager = __esm({
10998
11102
  }
10999
11103
  const liveRecord = await this.readLivePersistentBrowser(await this.ensureWorkspaceStore());
11000
11104
  return {
11001
- mode: this.mode,
11105
+ mode: liveRecord?.ownership === "attached" ? "attach" : this.mode,
11002
11106
  engine: liveRecord?.engine ?? this.engineName,
11003
11107
  ...this.workspace === void 0 ? {} : { workspace: this.workspace },
11004
11108
  live: liveRecord !== void 0
@@ -11126,6 +11230,7 @@ var init_browser_manager = __esm({
11126
11230
  });
11127
11231
  const liveRecord = {
11128
11232
  mode: "persistent",
11233
+ ownership: "owned",
11129
11234
  engine: "abp",
11130
11235
  baseUrl: launched.baseUrl,
11131
11236
  remoteDebuggingUrl: launched.remoteDebuggingUrl,
@@ -11212,11 +11317,78 @@ var init_browser_manager = __esm({
11212
11317
  }
11213
11318
  async createAttachEngine() {
11214
11319
  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
11320
+ if (this.workspace === void 0) {
11321
+ return this.createAttachedEngine({
11322
+ endpoint,
11323
+ ...this.browserOptions?.headers === void 0 ? {} : { headers: this.browserOptions.headers },
11324
+ freshTab: this.browserOptions?.freshTab ?? true,
11325
+ onDispose: async () => void 0
11326
+ });
11327
+ }
11328
+ const workspace = await this.ensureWorkspaceStore();
11329
+ return workspace.lock(async () => {
11330
+ const live = await this.readLivePersistentBrowser(workspace);
11331
+ if (live) {
11332
+ if (live.engine !== "playwright") {
11333
+ throw new Error(
11334
+ `workspace "${this.workspace}" already has a live ${live.engine} browser. Close it before attaching a Playwright browser.`
11335
+ );
11336
+ }
11337
+ if (live.ownership !== "attached") {
11338
+ throw new Error(
11339
+ `workspace "${this.workspace}" already has a live Opensteer-owned browser. Close it before attaching another browser.`
11340
+ );
11341
+ }
11342
+ if (live.endpoint === void 0) {
11343
+ throw new Error("workspace live browser record is missing a DevTools endpoint.");
11344
+ }
11345
+ if (live.endpoint !== endpoint) {
11346
+ throw new Error(
11347
+ `workspace "${this.workspace}" is already attached to a different browser endpoint. Close it before reattaching.`
11348
+ );
11349
+ }
11350
+ await bestEffortRegisterLocalViewSession({
11351
+ rootPath: workspace.rootPath,
11352
+ ...this.workspace === void 0 ? {} : { workspace: this.workspace },
11353
+ live: toPersistedLocalBrowserSessionRecord(this.workspace, live),
11354
+ ownership: "attached"
11355
+ });
11356
+ return this.createAttachedEngine({
11357
+ endpoint: live.endpoint,
11358
+ ...this.browserOptions?.headers === void 0 ? {} : { headers: this.browserOptions.headers },
11359
+ freshTab: this.browserOptions?.freshTab ?? true,
11360
+ onDispose: async () => void 0
11361
+ });
11362
+ }
11363
+ const liveRecord = {
11364
+ mode: "persistent",
11365
+ ownership: "attached",
11366
+ engine: "playwright",
11367
+ endpoint,
11368
+ pid: 0,
11369
+ startedAt: Date.now(),
11370
+ userDataDir: workspace.browserUserDataDir
11371
+ };
11372
+ await this.writeLivePersistentBrowser(workspace, liveRecord);
11373
+ const persistedLiveRecord = toPersistedLocalBrowserSessionRecord(this.workspace, liveRecord);
11374
+ await bestEffortRegisterLocalViewSession({
11375
+ rootPath: workspace.rootPath,
11376
+ ...this.workspace === void 0 ? {} : { workspace: this.workspace },
11377
+ live: persistedLiveRecord,
11378
+ ownership: "attached"
11379
+ });
11380
+ try {
11381
+ return await this.createAttachedEngine({
11382
+ endpoint,
11383
+ ...this.browserOptions?.headers === void 0 ? {} : { headers: this.browserOptions.headers },
11384
+ freshTab: this.browserOptions?.freshTab ?? true,
11385
+ onDispose: async () => void 0
11386
+ });
11387
+ } catch (error) {
11388
+ await this.unregisterLocalViewSessionForRecord(workspace.rootPath, persistedLiveRecord);
11389
+ await clearPersistedSessionRecord(workspace.rootPath, "local").catch(() => void 0);
11390
+ throw error;
11391
+ }
11220
11392
  });
11221
11393
  }
11222
11394
  async createPersistentEngine() {
@@ -11236,7 +11408,7 @@ var init_browser_manager = __esm({
11236
11408
  rootPath: workspace.rootPath,
11237
11409
  ...this.workspace === void 0 ? {} : { workspace: this.workspace },
11238
11410
  live: toPersistedLocalBrowserSessionRecord(this.workspace, live),
11239
- ownership: "owned"
11411
+ ownership: live.ownership
11240
11412
  });
11241
11413
  return this.createAttachedEngine({
11242
11414
  endpoint: live.endpoint,
@@ -11252,6 +11424,7 @@ var init_browser_manager = __esm({
11252
11424
  });
11253
11425
  const liveRecord = {
11254
11426
  mode: "persistent",
11427
+ ownership: "owned",
11255
11428
  engine: "playwright",
11256
11429
  endpoint: launched.endpoint,
11257
11430
  pid: launched.pid,
@@ -11381,7 +11554,20 @@ var init_browser_manager = __esm({
11381
11554
  if (live === void 0) {
11382
11555
  return void 0;
11383
11556
  }
11557
+ if (live.ownership === "attached") {
11558
+ const attachedRecord = toPersistedLocalBrowserSessionRecord(this.workspace, live);
11559
+ if (!await isAttachedLocalBrowserSessionReachable(attachedRecord)) {
11560
+ await this.unregisterLocalViewSessionForRecord(workspace.rootPath, attachedRecord);
11561
+ await clearPersistedSessionRecord(workspace.rootPath, "local").catch(() => void 0);
11562
+ return void 0;
11563
+ }
11564
+ return live;
11565
+ }
11384
11566
  if (!isProcessRunning(live.pid)) {
11567
+ await this.unregisterLocalViewSessionForRecord(
11568
+ workspace.rootPath,
11569
+ toPersistedLocalBrowserSessionRecord(this.workspace, live)
11570
+ );
11385
11571
  await clearPersistedSessionRecord(workspace.rootPath, "local").catch(() => void 0);
11386
11572
  return void 0;
11387
11573
  }
@@ -11429,6 +11615,10 @@ var init_browser_manager = __esm({
11429
11615
  workspace.rootPath,
11430
11616
  toPersistedLocalBrowserSessionRecord(this.workspace, live)
11431
11617
  );
11618
+ if (live.ownership === "attached") {
11619
+ await clearPersistedSessionRecord(workspace.rootPath, "local").catch(() => void 0);
11620
+ return;
11621
+ }
11432
11622
  if (live.engine === "playwright") {
11433
11623
  if (live.endpoint !== void 0) {
11434
11624
  await requestBrowserClose(live.endpoint).catch(() => void 0);
@@ -11457,10 +11647,18 @@ var init_browser_manager = __esm({
11457
11647
  }
11458
11648
  async unregisterLocalViewSessionForRecord(rootPath, record) {
11459
11649
  await bestEffortUnregisterLocalViewSession(
11460
- buildLocalViewSessionId({
11650
+ getPersistedLocalBrowserSessionOwnership(record) === "attached" ? buildLocalViewSessionId({
11651
+ rootPath,
11652
+ startedAt: record.startedAt,
11653
+ ownership: "attached",
11654
+ ...record.endpoint === void 0 ? {} : { endpoint: record.endpoint },
11655
+ ...record.baseUrl === void 0 ? {} : { baseUrl: record.baseUrl },
11656
+ ...record.remoteDebuggingUrl === void 0 ? {} : { remoteDebuggingUrl: record.remoteDebuggingUrl }
11657
+ }) : buildLocalViewSessionId({
11461
11658
  rootPath,
11462
- pid: record.pid,
11463
- startedAt: record.startedAt
11659
+ startedAt: record.startedAt,
11660
+ ownership: "owned",
11661
+ pid: record.pid
11464
11662
  })
11465
11663
  );
11466
11664
  }
@@ -11961,11 +12159,11 @@ function delay2(ms) {
11961
12159
  function wrapCloudFetchError(error, input) {
11962
12160
  if (!(error instanceof Error)) {
11963
12161
  return new Error(
11964
- `Failed to reach Opensteer cloud endpoint ${input.method} ${input.url}. Check OPENSTEER_BASE_URL and network reachability from this environment.`
12162
+ `Failed to reach Opensteer cloud endpoint ${input.method} ${input.url}. Check the configured Opensteer cloud base URL and network reachability from this environment.`
11965
12163
  );
11966
12164
  }
11967
12165
  const wrapped = new Error(
11968
- `Failed to reach Opensteer cloud endpoint ${input.method} ${input.url}. Check OPENSTEER_BASE_URL and network reachability from this environment.`,
12166
+ `Failed to reach Opensteer cloud endpoint ${input.method} ${input.url}. Check the configured Opensteer cloud base URL and network reachability from this environment.`,
11969
12167
  {
11970
12168
  cause: error
11971
12169
  }
@@ -12240,34 +12438,40 @@ function resolveCloudConfig(input = {}) {
12240
12438
  return void 0;
12241
12439
  }
12242
12440
  const cloudProvider = input.provider?.mode === "cloud" ? input.provider : void 0;
12243
- const apiKey = cloudProvider?.apiKey ?? input.environment?.OPENSTEER_API_KEY;
12244
- if (!apiKey || apiKey.trim().length === 0) {
12441
+ const apiKey = normalizeOptionalCloudConfigValue(cloudProvider?.apiKey) ?? normalizeOptionalCloudConfigValue(input.environment?.OPENSTEER_API_KEY);
12442
+ if (apiKey === void 0) {
12245
12443
  throw new Error("provider=cloud requires OPENSTEER_API_KEY or provider.apiKey.");
12246
12444
  }
12247
- const baseUrl = cloudProvider?.baseUrl ?? input.environment?.OPENSTEER_BASE_URL;
12248
- if (!baseUrl || baseUrl.trim().length === 0) {
12249
- throw new Error("provider=cloud requires OPENSTEER_BASE_URL or provider.baseUrl.");
12250
- }
12251
- const appBaseUrl = cloudProvider?.appBaseUrl ?? input.environment?.OPENSTEER_CLOUD_APP_BASE_URL;
12445
+ const baseUrl = normalizeOptionalCloudConfigValue(cloudProvider?.baseUrl) ?? normalizeOptionalCloudConfigValue(input.environment?.OPENSTEER_BASE_URL) ?? DEFAULT_OPENSTEER_CLOUD_BASE_URL;
12446
+ const appBaseUrl = normalizeOptionalCloudConfigValue(cloudProvider?.appBaseUrl) ?? normalizeOptionalCloudConfigValue(input.environment?.OPENSTEER_CLOUD_APP_BASE_URL);
12252
12447
  return {
12253
- apiKey: apiKey.trim(),
12254
- baseUrl: baseUrl.trim().replace(/\/+$/, ""),
12255
- ...appBaseUrl === void 0 || appBaseUrl.trim().length === 0 ? {} : { appBaseUrl: appBaseUrl.trim().replace(/\/+$/, "") },
12448
+ apiKey,
12449
+ baseUrl,
12450
+ ...appBaseUrl === void 0 ? {} : { appBaseUrl },
12256
12451
  ...cloudProvider?.browserProfile === void 0 ? {} : { browserProfile: cloudProvider.browserProfile }
12257
12452
  };
12258
12453
  }
12259
12454
  function requireCloudAppBaseUrl(cloudConfig) {
12260
- const appBaseUrl = cloudConfig.appBaseUrl;
12261
- if (!appBaseUrl || appBaseUrl.trim().length === 0) {
12455
+ const appBaseUrl = normalizeOptionalCloudConfigValue(cloudConfig.appBaseUrl);
12456
+ if (appBaseUrl === void 0) {
12262
12457
  throw new Error(
12263
12458
  'record with provider=cloud requires OPENSTEER_CLOUD_APP_BASE_URL or "--cloud-app-base-url".'
12264
12459
  );
12265
12460
  }
12266
- return appBaseUrl.trim().replace(/\/+$/, "");
12461
+ return appBaseUrl;
12462
+ }
12463
+ function normalizeOptionalCloudConfigValue(value) {
12464
+ if (typeof value !== "string") {
12465
+ return void 0;
12466
+ }
12467
+ const normalized = value.trim().replace(/\/+$/, "");
12468
+ return normalized.length === 0 ? void 0 : normalized;
12267
12469
  }
12470
+ var DEFAULT_OPENSTEER_CLOUD_BASE_URL;
12268
12471
  var init_config2 = __esm({
12269
12472
  "src/cloud/config.ts"() {
12270
12473
  init_config();
12474
+ DEFAULT_OPENSTEER_CLOUD_BASE_URL = "https://api.opensteer.com";
12271
12475
  }
12272
12476
  });
12273
12477
 
@@ -12277,7 +12481,7 @@ var init_package = __esm({
12277
12481
  "../runtime-core/package.json"() {
12278
12482
  package_default2 = {
12279
12483
  name: "@opensteer/runtime-core",
12280
- version: "0.2.5",
12484
+ version: "0.2.7",
12281
12485
  description: "Shared semantic runtime for Opensteer local and cloud execution.",
12282
12486
  license: "MIT",
12283
12487
  type: "module",
@@ -12459,14 +12663,14 @@ var init_defaults = __esm({
12459
12663
  snapshot: 0
12460
12664
  };
12461
12665
  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 },
12666
+ "dom.click": { settleMs: 750, scope: "main-frame", timeoutMs: 7e3 },
12667
+ "dom.input": { settleMs: 750, scope: "main-frame", timeoutMs: 7e3 },
12668
+ "dom.scroll": { settleMs: 600, scope: "main-frame", timeoutMs: 7e3 },
12465
12669
  "dom.hover": { settleMs: 200, scope: "main-frame", timeoutMs: 2500 }
12466
12670
  };
12467
12671
  DEFAULT_DOM_ACTION_VISUAL_STABILITY_PROFILE = {
12468
12672
  settleMs: 750,
12469
- scope: "visible-frames",
12673
+ scope: "main-frame",
12470
12674
  timeoutMs: 7e3
12471
12675
  };
12472
12676
  NAVIGATION_VISUAL_STABILITY_PROFILE = {
@@ -12490,6 +12694,7 @@ var init_defaults = __esm({
12490
12694
  pageRef: input.pageRef,
12491
12695
  timeoutMs: effectiveTimeout,
12492
12696
  settleMs: profile.settleMs,
12697
+ ...input.observedMutationQuietMs === void 0 ? {} : { initialQuietMs: input.observedMutationQuietMs },
12493
12698
  scope: profile.scope
12494
12699
  });
12495
12700
  return true;
@@ -12510,15 +12715,20 @@ var init_defaults = __esm({
12510
12715
  return false;
12511
12716
  }
12512
12717
  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));
12718
+ let visualTimeout = effectiveTimeout;
12719
+ let initialQuietMs = input.observedMutationQuietMs ?? 0;
12720
+ if (!input.postLoadHandled) {
12721
+ const startedAt = Date.now();
12722
+ await input.engine.waitForPostLoadQuiet({
12723
+ pageRef: input.pageRef,
12724
+ timeoutMs: effectiveTimeout,
12725
+ quietMs: DEFAULT_POST_LOAD_TRACKER_QUIET_WINDOW_MS,
12726
+ captureWindowMs: Math.min(NAVIGATION_POST_LOAD_CAPTURE_WINDOW_MS, effectiveTimeout),
12727
+ signal: input.signal
12728
+ });
12729
+ visualTimeout = Math.max(0, effectiveTimeout - (Date.now() - startedAt));
12730
+ initialQuietMs = Math.max(initialQuietMs, DEFAULT_POST_LOAD_TRACKER_QUIET_WINDOW_MS);
12731
+ }
12522
12732
  if (visualTimeout <= 0) {
12523
12733
  return true;
12524
12734
  }
@@ -12526,6 +12736,7 @@ var init_defaults = __esm({
12526
12736
  pageRef: input.pageRef,
12527
12737
  timeoutMs: visualTimeout,
12528
12738
  settleMs: profile.settleMs,
12739
+ ...initialQuietMs <= 0 ? {} : { initialQuietMs },
12529
12740
  scope: profile.scope
12530
12741
  });
12531
12742
  return true;
@@ -13141,7 +13352,9 @@ function resolveExtractedValueInContext(normalizedValue, options) {
13141
13352
  function stripPositionClauses(nodes) {
13142
13353
  return (nodes || []).map((node) => ({
13143
13354
  ...node,
13144
- match: (node.match || []).filter((clause) => clause.kind !== "position" && clause.kind !== "text")
13355
+ match: (node.match || []).filter(
13356
+ (clause) => clause.kind !== "position" && clause.kind !== "text"
13357
+ )
13145
13358
  }));
13146
13359
  }
13147
13360
  function dedupeSelectors(selectors) {
@@ -14693,7 +14906,7 @@ var init_executor = __esm({
14693
14906
  ...snapshot === void 0 ? {} : { snapshot },
14694
14907
  signal: timeout.signal,
14695
14908
  remainingMs: () => timeout.remainingMs(),
14696
- policySettle: async (targetPageRef, trigger) => {
14909
+ policySettle: async (targetPageRef, trigger, boundary2) => {
14697
14910
  try {
14698
14911
  await settleWithPolicy(this.options.policy.settle, {
14699
14912
  operation,
@@ -14701,7 +14914,9 @@ var init_executor = __esm({
14701
14914
  engine: this.options.engine,
14702
14915
  pageRef: targetPageRef,
14703
14916
  signal: timeout.signal,
14704
- remainingMs: timeout.remainingMs()
14917
+ remainingMs: timeout.remainingMs(),
14918
+ ...boundary2?.observedMutationQuietMs === void 0 ? {} : { observedMutationQuietMs: boundary2.observedMutationQuietMs },
14919
+ ...boundary2?.postLoadHandled === true ? { postLoadHandled: true } : {}
14705
14920
  });
14706
14921
  } catch (error) {
14707
14922
  if (snapshot !== void 0 && isSoftSettleTimeoutError(error, timeout.signal)) {
@@ -15448,7 +15663,9 @@ var init_runtime = __esm({
15448
15663
  `Unable to resolve structural anchor "${buildPathSelectorHint(anchor)}" in the current session`
15449
15664
  );
15450
15665
  }
15451
- const replayPath = await this.tryBuildPathFromNode(context.snapshot, target.node, { enableTextMatch: true });
15666
+ const replayPath = await this.tryBuildPathFromNode(context.snapshot, target.node, {
15667
+ enableTextMatch: true
15668
+ });
15452
15669
  return this.createResolvedTarget(source, context.snapshot, target.node, anchor, {
15453
15670
  ...persist === void 0 ? {} : { persist },
15454
15671
  ...replayPath === void 0 ? {} : { replayPath }
@@ -15959,12 +16176,16 @@ function toOpensteerResolvedTarget(target) {
15959
16176
  documentRef: target.documentRef,
15960
16177
  documentEpoch: target.documentEpoch,
15961
16178
  nodeRef: target.nodeRef,
15962
- tagName: target.node.nodeName.toUpperCase(),
16179
+ tagName: toOpensteerTagName(target.node.nodeName),
15963
16180
  pathHint: buildPathSelectorHint(target.replayPath ?? target.anchor),
15964
16181
  ...target.persist === void 0 ? {} : { persist: target.persist },
15965
16182
  ...target.selectorUsed === void 0 ? {} : { selectorUsed: target.selectorUsed }
15966
16183
  };
15967
16184
  }
16185
+ function toOpensteerTagName(nodeName) {
16186
+ const tagName = String(nodeName).trim().toLowerCase();
16187
+ return tagName.length === 0 ? "element" : tagName;
16188
+ }
15968
16189
  var init_trace_enrichment = __esm({
15969
16190
  "../runtime-core/src/runtimes/computer-use/trace-enrichment.ts"() {
15970
16191
  init_src();
@@ -16020,7 +16241,7 @@ var init_runtime2 = __esm({
16020
16241
  screenshot,
16021
16242
  signal: input.timeout.signal,
16022
16243
  remainingMs: () => input.timeout.remainingMs(),
16023
- policySettle: async (pageRef, trigger) => {
16244
+ policySettle: async (pageRef, trigger, boundary) => {
16024
16245
  try {
16025
16246
  await settleWithPolicy(this.options.policy.settle, {
16026
16247
  operation: "computer.execute",
@@ -16028,7 +16249,9 @@ var init_runtime2 = __esm({
16028
16249
  engine: this.options.engine,
16029
16250
  pageRef,
16030
16251
  signal: input.timeout.signal,
16031
- remainingMs: input.timeout.remainingMs()
16252
+ remainingMs: input.timeout.remainingMs(),
16253
+ ...boundary?.observedMutationQuietMs === void 0 ? {} : { observedMutationQuietMs: boundary.observedMutationQuietMs },
16254
+ ...boundary?.postLoadHandled === true ? { postLoadHandled: true } : {}
16032
16255
  });
16033
16256
  } catch (error) {
16034
16257
  if (pageRef === input.pageRef && isSoftSettleTimeoutError(error, input.timeout.signal)) {
@@ -17024,29 +17247,29 @@ function buildVariantDescriptorFromCluster(descriptors) {
17024
17247
  const keyStats = /* @__PURE__ */ new Map();
17025
17248
  for (const descriptor of descriptors) {
17026
17249
  for (const field of descriptor.fields) {
17027
- const stat2 = keyStats.get(field.path) ?? {
17250
+ const stat3 = keyStats.get(field.path) ?? {
17028
17251
  indices: /* @__PURE__ */ new Set(),
17029
17252
  pathNodes: [],
17030
17253
  attributes: [],
17031
17254
  sources: []
17032
17255
  };
17033
- stat2.indices.add(descriptor.index);
17256
+ stat3.indices.add(descriptor.index);
17034
17257
  if (isPersistedOpensteerExtractionValueNode(field.node)) {
17035
- stat2.pathNodes.push(field.node.$path);
17036
- stat2.attributes.push(field.node.attribute);
17258
+ stat3.pathNodes.push(field.node.$path);
17259
+ stat3.attributes.push(field.node.attribute);
17037
17260
  } else if (isPersistedOpensteerExtractionSourceNode(field.node)) {
17038
- stat2.sources.push("current_url");
17261
+ stat3.sources.push("current_url");
17039
17262
  }
17040
- keyStats.set(field.path, stat2);
17263
+ keyStats.set(field.path, stat3);
17041
17264
  }
17042
17265
  }
17043
17266
  const mergedFields = [];
17044
- for (const [fieldPath, stat2] of keyStats) {
17045
- if (stat2.indices.size < threshold) {
17267
+ for (const [fieldPath, stat3] of keyStats) {
17268
+ if (stat3.indices.size < threshold) {
17046
17269
  continue;
17047
17270
  }
17048
- if (stat2.pathNodes.length >= threshold) {
17049
- let mergedFieldPath = stat2.pathNodes.length === 1 ? sanitizeElementPath(stat2.pathNodes[0]) : mergeElementPathsByMajority(stat2.pathNodes);
17271
+ if (stat3.pathNodes.length >= threshold) {
17272
+ let mergedFieldPath = stat3.pathNodes.length === 1 ? sanitizeElementPath(stat3.pathNodes[0]) : mergeElementPathsByMajority(stat3.pathNodes);
17050
17273
  if (!mergedFieldPath) {
17051
17274
  continue;
17052
17275
  }
@@ -17054,8 +17277,8 @@ function buildVariantDescriptorFromCluster(descriptors) {
17054
17277
  mergedFieldPath = relaxPathForSingleSample(mergedFieldPath, "field");
17055
17278
  }
17056
17279
  mergedFieldPath = minimizePathMatchClauses(mergedFieldPath, "field");
17057
- const attrThreshold = stat2.pathNodes.length === 1 ? 1 : majorityThreshold(stat2.pathNodes.length);
17058
- const attribute = pickModeString(stat2.attributes, attrThreshold);
17280
+ const attrThreshold = stat3.pathNodes.length === 1 ? 1 : majorityThreshold(stat3.pathNodes.length);
17281
+ const attribute = pickModeString(stat3.attributes, attrThreshold);
17059
17282
  mergedFields.push({
17060
17283
  path: fieldPath,
17061
17284
  node: createValueNode({
@@ -17065,7 +17288,7 @@ function buildVariantDescriptorFromCluster(descriptors) {
17065
17288
  });
17066
17289
  continue;
17067
17290
  }
17068
- const dominantSource = pickModeString(stat2.sources, threshold);
17291
+ const dominantSource = pickModeString(stat3.sources, threshold);
17069
17292
  if (dominantSource === "current_url") {
17070
17293
  mergedFields.push({
17071
17294
  path: fieldPath,
@@ -19173,28 +19396,24 @@ function restoreBoundedAttr(el, attr, value) {
19173
19396
  }
19174
19397
  setBoundedAttr(el, attr, value);
19175
19398
  }
19176
- function deduplicateImages(html) {
19399
+ function deduplicateImagesInDom($) {
19177
19400
  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;
19401
+ $("img").each(function deduplicateDomImage() {
19402
+ const el = $(this);
19403
+ if (el.attr("c") !== void 0) {
19404
+ return;
19189
19405
  }
19406
+ const srcValue = el.attr("src")?.trim();
19407
+ const srcsetValue = el.attr("srcset");
19408
+ const src = srcValue && srcValue.length > 0 ? srcValue : srcsetValue?.split(",")[0]?.trim().split(/\s+/u)[0];
19190
19409
  if (!src) {
19191
- return full;
19410
+ return;
19192
19411
  }
19193
19412
  if (seen.has(src)) {
19194
- return "";
19413
+ el.remove();
19414
+ return;
19195
19415
  }
19196
19416
  seen.add(src);
19197
- return full;
19198
19417
  });
19199
19418
  }
19200
19419
  function hasAttribute2(node, attr) {
@@ -19252,23 +19471,6 @@ function isPreservedImageElement(node) {
19252
19471
  function getElementsInReverseDocumentOrder($) {
19253
19472
  return $.root().find("*").toArray().reverse().filter((node) => node.type === "tag");
19254
19473
  }
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
19474
  function flattenExtractionTree($) {
19273
19475
  for (const node of getElementsInReverseDocumentOrder($)) {
19274
19476
  const el = $(node);
@@ -19286,19 +19488,6 @@ function flattenExtractionTree($) {
19286
19488
  el.replaceWith(el.contents());
19287
19489
  }
19288
19490
  }
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
19491
  function isIndicatorImage(node) {
19303
19492
  return (node?.tagName || "").toLowerCase() === "img" && (hasAttribute2(node, "alt") || hasAttribute2(node, "src") || hasAttribute2(node, "srcset"));
19304
19493
  }
@@ -19416,7 +19605,7 @@ function serializeForExtraction($, root) {
19416
19605
  traverse(root, 0);
19417
19606
  return lines.map((l) => l.trim()).filter((l) => l.length > 0).join("");
19418
19607
  }
19419
- function isClickable($, el, context) {
19608
+ function isClickable(el, context) {
19420
19609
  if (context.hasPreMarked) {
19421
19610
  return el.attr(OPENSTEER_INTERACTIVE_ATTR) !== void 0;
19422
19611
  }
@@ -19450,21 +19639,17 @@ function isClickable($, el, context) {
19450
19639
  }
19451
19640
  return false;
19452
19641
  }
19453
- function cleanForExtraction(html) {
19642
+ function prepareExtractionSnapshotDom(html) {
19454
19643
  if (!html.trim()) {
19455
- return "";
19644
+ return void 0;
19456
19645
  }
19457
19646
  const $ = cheerio__namespace.load(html, { xmlMode: false });
19458
19647
  removeNoise($);
19459
19648
  removeComments($);
19460
19649
  markInlineSelfHiddenFallback($);
19461
19650
  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);
19651
+ $("*").each(function stripAndRestoreExtractionAttrs() {
19652
+ const el = $(this);
19468
19653
  const node = el[0];
19469
19654
  if (!node) {
19470
19655
  return;
@@ -19505,16 +19690,20 @@ function cleanForExtraction(html) {
19505
19690
  restoreBoundedAttr(el, "href", hrefValue);
19506
19691
  }
19507
19692
  });
19508
- flattenExtractionTree($clean);
19509
- const root = $clean.root()[0];
19693
+ flattenExtractionTree($);
19694
+ deduplicateImagesInDom($);
19695
+ return $;
19696
+ }
19697
+ function serializePreparedExtractionSnapshot($) {
19698
+ const root = $.root()[0];
19510
19699
  if (root === void 0) {
19511
19700
  return "";
19512
19701
  }
19513
- return deduplicateImages(serializeForExtraction($clean, root));
19702
+ return serializeForExtraction($, root);
19514
19703
  }
19515
- function cleanForAction(html) {
19704
+ function prepareActionSnapshotDom(html) {
19516
19705
  if (!html.trim()) {
19517
- return "";
19706
+ return void 0;
19518
19707
  }
19519
19708
  const $ = cheerio__namespace.load(html, { xmlMode: false });
19520
19709
  removeNoise($);
@@ -19523,13 +19712,12 @@ function cleanForAction(html) {
19523
19712
  pruneSelfHiddenNodes($);
19524
19713
  const clickableMark = "data-clickable-marker";
19525
19714
  const indicatorMark = "data-keep-indicator";
19526
- const branchMark = "data-keep-branch";
19527
19715
  const context = {
19528
19716
  hasPreMarked: $(`[${OPENSTEER_INTERACTIVE_ATTR}]`).length > 0
19529
19717
  };
19530
19718
  $("*").each(function markClickables() {
19531
19719
  const el = $(this);
19532
- if (isClickable($, el, context)) {
19720
+ if (isClickable(el, context)) {
19533
19721
  el.attr(clickableMark, "1");
19534
19722
  }
19535
19723
  });
@@ -19559,25 +19747,7 @@ function cleanForAction(html) {
19559
19747
  el.remove();
19560
19748
  }
19561
19749
  });
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($)) {
19750
+ for (const node of getElementsInReverseDocumentOrder($)) {
19581
19751
  const el = $(node);
19582
19752
  const tag = (node.tagName || "").toLowerCase();
19583
19753
  if (ROOT_TAGS.has(tag) || isBoundaryTag(tag)) {
@@ -19586,17 +19756,7 @@ function cleanForAction(html) {
19586
19756
  if (el.attr(clickableMark) !== void 0 || el.attr(indicatorMark) !== void 0) {
19587
19757
  continue;
19588
19758
  }
19589
- const insideClickable = hasMarkedAncestor(el, clickableMark);
19590
- const preserveBranch = el.attr(branchMark) !== void 0;
19591
19759
  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
19760
  if (!hasContent) {
19601
19761
  el.remove();
19602
19762
  continue;
@@ -19697,13 +19857,20 @@ function cleanForAction(html) {
19697
19857
  }
19698
19858
  el.removeAttr(clickableMark);
19699
19859
  el.removeAttr(indicatorMark);
19700
- el.removeAttr(branchMark);
19701
19860
  el.removeAttr(OPENSTEER_INTERACTIVE_ATTR);
19702
19861
  el.removeAttr(OPENSTEER_HIDDEN_ATTR);
19703
19862
  el.removeAttr(OPENSTEER_SCROLLABLE_ATTR);
19704
19863
  el.removeAttr(OPENSTEER_SELF_HIDDEN_ATTR);
19705
19864
  });
19706
- return compactHtml(deduplicateImages($.html()));
19865
+ deduplicateImagesInDom($);
19866
+ return $;
19867
+ }
19868
+ function serializePreparedActionSnapshot($) {
19869
+ const normalized = compactHtml($.html());
19870
+ if (normalized.length === 0) {
19871
+ return "";
19872
+ }
19873
+ return cheerio__namespace.load(normalized, { xmlMode: false }).html();
19707
19874
  }
19708
19875
  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
19876
  var init_cleaner = __esm({
@@ -19764,23 +19931,27 @@ async function markLiveSnapshotSemantics(options) {
19764
19931
  const frames = await options.engine.listFrames({
19765
19932
  pageRef: options.pageRef
19766
19933
  });
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(
19934
+ await Promise.all(
19935
+ frames.map(
19936
+ (frame) => evaluateFrameBestEffort(
19778
19937
  options.engine,
19779
19938
  frame.frameRef,
19780
- CLEAR_SNAPSHOT_SEMANTICS_SCRIPT,
19781
- CLEAR_SNAPSHOT_SEMANTIC_ARGS
19782
- );
19783
- }
19939
+ MARK_SNAPSHOT_SEMANTICS_SCRIPT,
19940
+ SNAPSHOT_SEMANTIC_ARGS
19941
+ )
19942
+ )
19943
+ );
19944
+ return async () => {
19945
+ await Promise.all(
19946
+ frames.map(
19947
+ (frame) => evaluateFrameBestEffort(
19948
+ options.engine,
19949
+ frame.frameRef,
19950
+ CLEAR_SNAPSHOT_SEMANTICS_SCRIPT,
19951
+ CLEAR_SNAPSHOT_SEMANTIC_ARGS
19952
+ )
19953
+ )
19954
+ );
19784
19955
  };
19785
19956
  }
19786
19957
  var MARK_SNAPSHOT_SEMANTICS_SCRIPT, CLEAR_SNAPSHOT_SEMANTICS_SCRIPT, SNAPSHOT_SEMANTIC_ARGS, CLEAR_SNAPSHOT_SEMANTIC_ARGS;
@@ -19978,6 +20149,8 @@ var init_marking = __esm({
19978
20149
  ];
19979
20150
  }
19980
20151
  });
20152
+
20153
+ // ../runtime-core/src/sdk/snapshot/compiler.ts
19981
20154
  function isLiveCounterSyncError(error) {
19982
20155
  return error instanceof LiveCounterSyncError;
19983
20156
  }
@@ -20015,20 +20188,22 @@ function ensureSparseCountersForAllRecords(counterRecords) {
20015
20188
  async function clearOpensteerLiveCounters(engine, pageRef) {
20016
20189
  const frames = await engine.listFrames({ pageRef });
20017
20190
  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;
20191
+ await Promise.all(
20192
+ frames.map(async (frame) => {
20193
+ try {
20194
+ await engine.evaluateFrame({
20195
+ frameRef: frame.frameRef,
20196
+ script: CLEAR_LIVE_COUNTERS_SCRIPT,
20197
+ args: [{ sparseCounterAttr: OPENSTEER_SPARSE_COUNTER_ATTR }]
20198
+ });
20199
+ } catch (error) {
20200
+ if (isDetachedFrameSyncError(error)) {
20201
+ return;
20202
+ }
20203
+ failures.push(`frame ${frame.frameRef} could not be cleared (${describeError(error)}).`);
20028
20204
  }
20029
- failures.push(`frame ${frame.frameRef} could not be cleared (${describeError(error)}).`);
20030
- }
20031
- }
20205
+ })
20206
+ );
20032
20207
  if (failures.length > 0) {
20033
20208
  throw buildLiveCounterSyncError("clear live counters", failures);
20034
20209
  }
@@ -20077,25 +20252,29 @@ async function syncDenseCountersToLiveDom(engine, pageRef, sparseToDirectMapping
20077
20252
  denseCounter
20078
20253
  ])
20079
20254
  );
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;
20255
+ await Promise.all(
20256
+ frames.map(async (frame) => {
20257
+ try {
20258
+ await engine.evaluateFrame({
20259
+ frameRef: frame.frameRef,
20260
+ script: APPLY_DENSE_COUNTERS_SCRIPT,
20261
+ args: [
20262
+ {
20263
+ sparseCounterAttr: OPENSTEER_SPARSE_COUNTER_ATTR,
20264
+ mapping: mappingObj
20265
+ }
20266
+ ]
20267
+ });
20268
+ } catch (error) {
20269
+ if (isDetachedFrameSyncError(error)) {
20270
+ return;
20271
+ }
20272
+ failures.push(
20273
+ `frame ${frame.frameRef} could not be synchronized (${describeError(error)}).`
20274
+ );
20095
20275
  }
20096
- failures.push(`frame ${frame.frameRef} could not be synchronized (${describeError(error)}).`);
20097
- }
20098
- }
20276
+ })
20277
+ );
20099
20278
  if (failures.length > 0) {
20100
20279
  throw buildLiveCounterSyncError("synchronize dense counters", failures);
20101
20280
  }
@@ -20113,26 +20292,26 @@ async function compileOpensteerSnapshot(options) {
20113
20292
  await clearOpensteerLiveCounters(options.engine, options.pageRef);
20114
20293
  await assignSparseCountersToLiveDom(options.engine, options.pageRef);
20115
20294
  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);
20295
+ const { mainSnapshot, snapshotsByDocumentRef } = await getPageDocumentSnapshots(
20296
+ options.engine,
20297
+ options.pageRef
20298
+ );
20118
20299
  await cleanupLiveSemantics();
20119
20300
  cleanupLiveSemantics = async () => {
20120
20301
  };
20121
- const snapshotIndices = /* @__PURE__ */ new Map();
20122
20302
  const renderedNodes = /* @__PURE__ */ new Map();
20123
20303
  const rawHtml = renderDocumentSnapshot(
20124
20304
  mainSnapshot.documentRef,
20125
20305
  snapshotsByDocumentRef,
20126
- snapshotIndices,
20127
20306
  renderedNodes,
20128
20307
  {
20129
20308
  iframeDepth: 0,
20130
20309
  shadowDepth: 0
20131
20310
  }
20132
20311
  );
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;
20312
+ const preparedSnapshotDom = options.mode === "extraction" ? prepareExtractionSnapshotDom(rawHtml) : prepareActionSnapshotDom(rawHtml);
20313
+ const compiledHtml = assignCountersInDom(preparedSnapshotDom, renderedNodes, options.mode);
20314
+ const finalHtml = preparedSnapshotDom === void 0 ? "" : options.mode === "extraction" ? serializePreparedExtractionSnapshot(preparedSnapshotDom) : serializePreparedActionSnapshot(preparedSnapshotDom);
20136
20315
  ensureSparseCountersForAllRecords(compiledHtml.counterRecords);
20137
20316
  await syncDenseCountersToLiveDom(
20138
20317
  options.engine,
@@ -20167,6 +20346,25 @@ async function getMainDocumentSnapshot(engine, pageRef) {
20167
20346
  }
20168
20347
  return engine.getDomSnapshot({ frameRef: mainFrame.frameRef });
20169
20348
  }
20349
+ async function getPageDocumentSnapshots(engine, pageRef) {
20350
+ const bundleEngine = engine;
20351
+ const bundledSnapshots = await bundleEngine.getPageDomSnapshots?.({ pageRef });
20352
+ if (bundledSnapshots && bundledSnapshots.length > 0) {
20353
+ const mainSnapshot2 = bundledSnapshots.find((snapshot) => snapshot.parentDocumentRef === void 0) ?? bundledSnapshots[0];
20354
+ return {
20355
+ mainSnapshot: mainSnapshot2,
20356
+ snapshotsByDocumentRef: new Map(
20357
+ bundledSnapshots.map((snapshot) => [snapshot.documentRef, snapshot])
20358
+ )
20359
+ };
20360
+ }
20361
+ const mainSnapshot = await getMainDocumentSnapshot(engine, pageRef);
20362
+ const snapshotsByDocumentRef = await collectDocumentSnapshots(engine, mainSnapshot);
20363
+ return {
20364
+ mainSnapshot,
20365
+ snapshotsByDocumentRef
20366
+ };
20367
+ }
20170
20368
  async function collectDocumentSnapshots(engine, mainSnapshot) {
20171
20369
  const snapshotsByDocumentRef = /* @__PURE__ */ new Map([
20172
20370
  [mainSnapshot.documentRef, mainSnapshot]
@@ -20185,7 +20383,7 @@ async function collectDocumentSnapshots(engine, mainSnapshot) {
20185
20383
  }
20186
20384
  return snapshotsByDocumentRef;
20187
20385
  }
20188
- function renderDocumentSnapshot(documentRef, snapshotsByDocumentRef, snapshotIndices, renderedNodes, depth) {
20386
+ function renderDocumentSnapshot(documentRef, snapshotsByDocumentRef, renderedNodes, depth) {
20189
20387
  const snapshot = snapshotsByDocumentRef.get(documentRef);
20190
20388
  if (!snapshot) {
20191
20389
  return "";
@@ -20197,17 +20395,9 @@ function renderDocumentSnapshot(documentRef, snapshotsByDocumentRef, snapshotInd
20197
20395
  `snapshot ${snapshot.documentRef} is missing root node ${String(snapshot.rootSnapshotNodeId)}`
20198
20396
  );
20199
20397
  }
20200
- return renderNode(
20201
- snapshot,
20202
- rootNode,
20203
- nodesById,
20204
- snapshotsByDocumentRef,
20205
- snapshotIndices,
20206
- renderedNodes,
20207
- depth
20208
- );
20398
+ return renderNode(snapshot, rootNode, nodesById, snapshotsByDocumentRef, renderedNodes, depth);
20209
20399
  }
20210
- function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotIndices, renderedNodes, depth) {
20400
+ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, renderedNodes, depth) {
20211
20401
  if (node.nodeType === 3) {
20212
20402
  return escapeHtml(node.nodeValue || node.textContent || "");
20213
20403
  }
@@ -20215,56 +20405,26 @@ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotI
20215
20405
  return "";
20216
20406
  }
20217
20407
  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
- );
20408
+ return renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, renderedNodes, depth);
20227
20409
  }
20228
20410
  if (node.nodeType !== 1) {
20229
- return renderChildren(
20230
- snapshot,
20231
- node,
20232
- nodesById,
20233
- snapshotsByDocumentRef,
20234
- snapshotIndices,
20235
- renderedNodes,
20236
- depth
20237
- );
20411
+ return renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, renderedNodes, depth);
20238
20412
  }
20239
20413
  const tagName = normalizeTagName(node.nodeName);
20240
20414
  if (isPseudoElementTagName(tagName)) {
20241
- return renderChildren(
20242
- snapshot,
20243
- node,
20244
- nodesById,
20245
- snapshotsByDocumentRef,
20246
- snapshotIndices,
20247
- renderedNodes,
20248
- depth
20249
- );
20415
+ return renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, renderedNodes, depth);
20250
20416
  }
20251
20417
  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
- );
20418
+ return renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, renderedNodes, depth);
20261
20419
  }
20262
20420
  const snapshotAttributes = normalizeNodeAttributes(node.attributes);
20421
+ const snapshotAttributeIndex = indexNodeAttributes(snapshotAttributes);
20263
20422
  const authoredAttributes = stripInternalSnapshotAttributes(snapshotAttributes);
20423
+ const authoredAttributeIndex = indexNodeAttributes(authoredAttributes);
20264
20424
  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));
20425
+ const subtreeHidden = snapshotAttributeIndex.has(OPENSTEER_HIDDEN_ATTR) || isLikelySubtreeHidden(node);
20426
+ const selfHidden = !subtreeHidden && (snapshotAttributeIndex.has(OPENSTEER_SELF_HIDDEN_ATTR) || isLikelySelfHidden(node, nodesById));
20427
+ const interactive = !subtreeHidden && !selfHidden && (snapshotAttributeIndex.has(OPENSTEER_INTERACTIVE_ATTR) || isLikelyInteractive(tagName, node, authoredAttributes, authoredAttributeIndex));
20268
20428
  if (interactive) {
20269
20429
  attributes.push({ name: OPENSTEER_INTERACTIVE_ATTR, value: "1" });
20270
20430
  }
@@ -20273,7 +20433,7 @@ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotI
20273
20433
  } else if (selfHidden) {
20274
20434
  attributes.push({ name: OPENSTEER_SELF_HIDDEN_ATTR, value: "1" });
20275
20435
  }
20276
- const sparseCounter = findAttributeValue(snapshotAttributes, OPENSTEER_SPARSE_COUNTER_ATTR);
20436
+ const sparseCounter = snapshotAttributeIndex.get(OPENSTEER_SPARSE_COUNTER_ATTR);
20277
20437
  if (sparseCounter !== void 0) {
20278
20438
  attributes.push({ name: OPENSTEER_SPARSE_COUNTER_ATTR, value: sparseCounter });
20279
20439
  }
@@ -20284,21 +20444,18 @@ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotI
20284
20444
  const syntheticNodeId = buildSyntheticNodeId(snapshot, node);
20285
20445
  attributes.push({ name: OPENSTEER_NODE_ID_ATTR, value: syntheticNodeId });
20286
20446
  renderedNodes.set(syntheticNodeId, {
20287
- locator: createNodeLocator(snapshot.documentRef, snapshot.documentEpoch, node.nodeRef),
20288
- anchor: buildSnapshotElementAnchor(snapshot, node, snapshotsByDocumentRef, snapshotIndices),
20289
20447
  pageRef: snapshot.pageRef,
20290
20448
  frameRef: snapshot.frameRef,
20291
20449
  documentRef: snapshot.documentRef,
20292
20450
  documentEpoch: snapshot.documentEpoch,
20293
20451
  nodeRef: node.nodeRef,
20294
20452
  tagName: tagName.toUpperCase(),
20295
- pathHint: buildPathHint(tagName, authoredAttributes),
20296
- ...buildTextSnippet(node.textContent) === void 0 ? {} : { text: buildTextSnippet(node.textContent) },
20297
20453
  ...authoredAttributes.length === 0 ? {} : { attributes: authoredAttributes },
20298
20454
  iframeDepth: depth.iframeDepth,
20299
20455
  shadowDepth: depth.shadowDepth,
20300
20456
  interactive,
20301
- liveCounterSyncEligible: isLiveCounterSyncEligible(node, nodesById)
20457
+ liveCounterSyncEligible: isLiveCounterSyncEligible(node, nodesById),
20458
+ ...node.textContent === void 0 ? {} : { textContent: node.textContent }
20302
20459
  });
20303
20460
  }
20304
20461
  const attributeText = attributesToHtml(attributes);
@@ -20307,7 +20464,6 @@ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotI
20307
20464
  node,
20308
20465
  nodesById,
20309
20466
  snapshotsByDocumentRef,
20310
- snapshotIndices,
20311
20467
  renderedNodes,
20312
20468
  depth
20313
20469
  );
@@ -20318,7 +20474,6 @@ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotI
20318
20474
  const iframeHtml = renderDocumentSnapshot(
20319
20475
  node.contentDocumentRef,
20320
20476
  snapshotsByDocumentRef,
20321
- snapshotIndices,
20322
20477
  renderedNodes,
20323
20478
  {
20324
20479
  iframeDepth: depth.iframeDepth + 1,
@@ -20330,7 +20485,7 @@ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotI
20330
20485
  }
20331
20486
  return `${elementHtml}<${OPENSTEER_IFRAME_BOUNDARY_TAG} ${OPENSTEER_BOUNDARY_ATTR}="iframe">${iframeHtml}</${OPENSTEER_IFRAME_BOUNDARY_TAG}>`;
20332
20487
  }
20333
- function renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotIndices, renderedNodes, depth) {
20488
+ function renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, renderedNodes, depth) {
20334
20489
  const regularChildren = [];
20335
20490
  const shadowChildren = [];
20336
20491
  for (const childSnapshotNodeId of node.childSnapshotNodeIds) {
@@ -20347,18 +20502,10 @@ function renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, snaps
20347
20502
  const chunks = [];
20348
20503
  if (shadowChildren.length > 0) {
20349
20504
  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
- )
20505
+ (child) => renderNode(snapshot, child, nodesById, snapshotsByDocumentRef, renderedNodes, {
20506
+ iframeDepth: depth.iframeDepth,
20507
+ shadowDepth: depth.shadowDepth + 1
20508
+ })
20362
20509
  ).join("");
20363
20510
  chunks.push(
20364
20511
  `<${OPENSTEER_SHADOW_BOUNDARY_TAG} ${OPENSTEER_BOUNDARY_ATTR}="shadow">${shadowHtml}</${OPENSTEER_SHADOW_BOUNDARY_TAG}>`
@@ -20366,24 +20513,21 @@ function renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, snaps
20366
20513
  }
20367
20514
  for (const child of regularChildren) {
20368
20515
  chunks.push(
20369
- renderNode(
20370
- snapshot,
20371
- child,
20372
- nodesById,
20373
- snapshotsByDocumentRef,
20374
- snapshotIndices,
20375
- renderedNodes,
20376
- depth
20377
- )
20516
+ renderNode(snapshot, child, nodesById, snapshotsByDocumentRef, renderedNodes, depth)
20378
20517
  );
20379
20518
  }
20380
20519
  return chunks.join("");
20381
20520
  }
20382
- function assignCounters(cleanedHtml, renderedNodes) {
20383
- const $ = cheerio__namespace.load(cleanedHtml, { xmlMode: false });
20521
+ function assignCountersInDom($, renderedNodes, mode) {
20384
20522
  const counterRecords = /* @__PURE__ */ new Map();
20385
20523
  const sparseToDirectMapping = /* @__PURE__ */ new Map();
20386
20524
  let nextCounter = 1;
20525
+ if (!$) {
20526
+ return {
20527
+ counterRecords,
20528
+ sparseToDirectMapping
20529
+ };
20530
+ }
20387
20531
  $("*").each(function assignElementCounter() {
20388
20532
  const el = $(this);
20389
20533
  const syntheticNodeId = el.attr(OPENSTEER_NODE_ID_ATTR);
@@ -20395,14 +20539,24 @@ function assignCounters(cleanedHtml, renderedNodes) {
20395
20539
  if (!rendered) {
20396
20540
  return;
20397
20541
  }
20542
+ if (mode === "extraction" && EXTRACTION_SKIPPED_COUNTER_TAGS.has(rendered.tagName.toLowerCase())) {
20543
+ el.removeAttr(OPENSTEER_SPARSE_COUNTER_ATTR);
20544
+ return;
20545
+ }
20398
20546
  const rawSparseCounter = el.attr(OPENSTEER_SPARSE_COUNTER_ATTR);
20399
20547
  el.removeAttr(OPENSTEER_SPARSE_COUNTER_ATTR);
20400
20548
  const sparseCounter = rawSparseCounter ? Number.parseInt(rawSparseCounter, 10) : void 0;
20549
+ const replayableSparseCounter = typeof sparseCounter === "number" && Number.isFinite(sparseCounter) ? sparseCounter : void 0;
20550
+ if (rendered.liveCounterSyncEligible && replayableSparseCounter === void 0) {
20551
+ return;
20552
+ }
20401
20553
  const counter = nextCounter++;
20402
20554
  el.attr("c", String(counter));
20403
- if (sparseCounter !== void 0 && Number.isFinite(sparseCounter)) {
20404
- sparseToDirectMapping.set(sparseCounter, counter);
20555
+ if (replayableSparseCounter !== void 0) {
20556
+ sparseToDirectMapping.set(replayableSparseCounter, counter);
20405
20557
  }
20558
+ const pathHint = buildPathHint(rendered.tagName.toLowerCase(), rendered.attributes ?? []);
20559
+ const text = buildTextSnippet(rendered.textContent);
20406
20560
  counterRecords.set(counter, {
20407
20561
  element: counter,
20408
20562
  pageRef: rendered.pageRef,
@@ -20411,20 +20565,17 @@ function assignCounters(cleanedHtml, renderedNodes) {
20411
20565
  documentEpoch: rendered.documentEpoch,
20412
20566
  nodeRef: rendered.nodeRef,
20413
20567
  tagName: rendered.tagName,
20414
- pathHint: rendered.pathHint,
20415
- ...rendered.text === void 0 ? {} : { text: rendered.text },
20568
+ pathHint,
20569
+ ...text === void 0 ? {} : { text },
20416
20570
  ...rendered.attributes === void 0 ? {} : { attributes: rendered.attributes },
20417
20571
  iframeDepth: rendered.iframeDepth,
20418
20572
  shadowDepth: rendered.shadowDepth,
20419
20573
  interactive: rendered.interactive,
20420
20574
  liveCounterSyncEligible: rendered.liveCounterSyncEligible,
20421
- locator: rendered.locator,
20422
- anchor: rendered.anchor,
20423
- ...sparseCounter !== void 0 && Number.isFinite(sparseCounter) ? { sparseCounter } : {}
20575
+ ...replayableSparseCounter === void 0 ? {} : { sparseCounter: replayableSparseCounter }
20424
20576
  });
20425
20577
  });
20426
20578
  return {
20427
- html: $.html(),
20428
20579
  counterRecords,
20429
20580
  sparseToDirectMapping
20430
20581
  };
@@ -20498,28 +20649,28 @@ function isLikelySelfHidden(node, nodesById) {
20498
20649
  }
20499
20650
  return !hasVisibleOutOfFlowChild(node, nodesById);
20500
20651
  }
20501
- function isLikelyInteractive(tagName, node, attributes) {
20652
+ function isLikelyInteractive(tagName, node, attributes, attributeIndex) {
20502
20653
  if (NATIVE_INTERACTIVE_TAGS.has(tagName)) {
20503
- if (tagName === "input" && findAttributeValue(attributes, "type")?.toLowerCase() === "hidden") {
20654
+ if (tagName === "input" && attributeIndex.get("type")?.toLowerCase() === "hidden") {
20504
20655
  return false;
20505
20656
  }
20506
20657
  if (tagName !== "a") {
20507
20658
  return true;
20508
20659
  }
20509
20660
  }
20510
- if (tagName === "a" && findAttributeValue(attributes, "href") !== void 0) {
20661
+ if (tagName === "a" && attributeIndex.has("href")) {
20511
20662
  return true;
20512
20663
  }
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) {
20664
+ if (attributeIndex.has("onclick") || attributeIndex.has("onmousedown") || attributeIndex.has("onmouseup") || attributeIndex.has("data-action") || attributeIndex.has("data-click") || attributeIndex.has("data-toggle")) {
20514
20665
  return true;
20515
20666
  }
20516
- if (hasNonNegativeTabIndex(findAttributeValue(attributes, "tabindex"))) {
20667
+ if (hasNonNegativeTabIndex(attributeIndex.get("tabindex"))) {
20517
20668
  return true;
20518
20669
  }
20519
- if (findAttributeValue(attributes, "contenteditable")?.toLowerCase() === "true") {
20670
+ if (attributeIndex.get("contenteditable")?.toLowerCase() === "true") {
20520
20671
  return true;
20521
20672
  }
20522
- const role = findAttributeValue(attributes, "role")?.toLowerCase();
20673
+ const role = attributeIndex.get("role")?.toLowerCase();
20523
20674
  return role !== void 0 && INTERACTIVE_ROLE_SET.has(role);
20524
20675
  }
20525
20676
  function hasVisibleOutOfFlowChild(node, nodesById) {
@@ -20582,14 +20733,6 @@ function parseOpacity(value) {
20582
20733
  const parsed = Number.parseFloat(value);
20583
20734
  return Number.isFinite(parsed) ? parsed : Number.NaN;
20584
20735
  }
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
20736
  function buildSyntheticNodeId(snapshot, node) {
20594
20737
  return `${snapshot.documentRef}:${String(snapshot.documentEpoch)}:${String(node.snapshotNodeId)}`;
20595
20738
  }
@@ -20636,6 +20779,13 @@ function findAttributeValue(attributes, name) {
20636
20779
  const normalizedName = name.toLowerCase();
20637
20780
  return attributes.find((attribute) => attribute.name.toLowerCase() === normalizedName)?.value;
20638
20781
  }
20782
+ function indexNodeAttributes(attributes) {
20783
+ const indexed = /* @__PURE__ */ new Map();
20784
+ for (const attribute of attributes) {
20785
+ indexed.set(attribute.name.toLowerCase(), attribute.value);
20786
+ }
20787
+ return indexed;
20788
+ }
20639
20789
  function attributesToHtml(attributes) {
20640
20790
  if (attributes.length === 0) {
20641
20791
  return "";
@@ -20645,68 +20795,16 @@ function attributesToHtml(attributes) {
20645
20795
  function escapeAttribute(value) {
20646
20796
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
20647
20797
  }
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
20798
  function escapeHtml(value) {
20699
20799
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
20700
20800
  }
20701
- var INTERNAL_SNAPSHOT_ATTRIBUTE_NAMES, MAX_LIVE_COUNTER_SYNC_ATTEMPTS, CLEAR_LIVE_COUNTERS_SCRIPT, ASSIGN_SPARSE_COUNTERS_SCRIPT, APPLY_DENSE_COUNTERS_SCRIPT, LiveCounterSyncError;
20801
+ 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
20802
  var init_compiler = __esm({
20703
20803
  "../runtime-core/src/sdk/snapshot/compiler.ts"() {
20704
- init_src();
20705
- init_path();
20706
- init_selectors();
20707
20804
  init_cleaner();
20708
20805
  init_constants();
20709
20806
  init_marking();
20807
+ EXTRACTION_SKIPPED_COUNTER_TAGS = /* @__PURE__ */ new Set(["html", "head", "body"]);
20710
20808
  INTERNAL_SNAPSHOT_ATTRIBUTE_NAMES = /* @__PURE__ */ new Set([
20711
20809
  "c",
20712
20810
  OPENSTEER_BOUNDARY_ATTR,
@@ -20721,7 +20819,7 @@ var init_compiler = __esm({
20721
20819
  MAX_LIVE_COUNTER_SYNC_ATTEMPTS = 4;
20722
20820
  CLEAR_LIVE_COUNTERS_SCRIPT = `(({ sparseCounterAttr }) => {
20723
20821
  const walk = (root) => {
20724
- for (const child of root.children) {
20822
+ for (const child of Array.from(root?.children || [])) {
20725
20823
  child.removeAttribute("c");
20726
20824
  child.removeAttribute(sparseCounterAttr);
20727
20825
  walk(child);
@@ -20737,7 +20835,7 @@ var init_compiler = __esm({
20737
20835
  ASSIGN_SPARSE_COUNTERS_SCRIPT = `(({ sparseCounterAttr, startCounter }) => {
20738
20836
  let counter = startCounter;
20739
20837
  const walk = (root) => {
20740
- for (const child of root.children) {
20838
+ for (const child of Array.from(root?.children || [])) {
20741
20839
  child.setAttribute(sparseCounterAttr, String(counter++));
20742
20840
  walk(child);
20743
20841
  if (child.shadowRoot) {
@@ -20751,7 +20849,7 @@ var init_compiler = __esm({
20751
20849
  })`;
20752
20850
  APPLY_DENSE_COUNTERS_SCRIPT = `(({ sparseCounterAttr, mapping }) => {
20753
20851
  const walk = (root) => {
20754
- for (const child of root.children) {
20852
+ for (const child of Array.from(root?.children || [])) {
20755
20853
  child.removeAttribute("c");
20756
20854
  const sparse = child.getAttribute(sparseCounterAttr);
20757
20855
  if (sparse !== null) {
@@ -22935,15 +23033,10 @@ function normalizeNamespace2(value) {
22935
23033
  const normalized = String(value ?? "default").trim();
22936
23034
  return normalized.length === 0 ? "default" : normalized;
22937
23035
  }
22938
- function toOpensteerActionResult(result) {
23036
+ function toOpensteerActionResult(target) {
22939
23037
  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
- }
23038
+ tagName: toOpensteerTagName2(target.node.nodeName),
23039
+ ...target.persist === void 0 ? {} : { persist: target.persist }
22947
23040
  };
22948
23041
  }
22949
23042
  function toOpensteerResolvedTarget2(target) {
@@ -22953,12 +23046,16 @@ function toOpensteerResolvedTarget2(target) {
22953
23046
  documentRef: target.documentRef,
22954
23047
  documentEpoch: target.documentEpoch,
22955
23048
  nodeRef: target.nodeRef,
22956
- tagName: target.node.nodeName.toUpperCase(),
23049
+ tagName: toOpensteerTagName2(target.node.nodeName),
22957
23050
  pathHint: buildPathSelectorHint(target.replayPath ?? target.anchor),
22958
23051
  ...target.persist === void 0 ? {} : { persist: target.persist },
22959
23052
  ...target.selectorUsed === void 0 ? {} : { selectorUsed: target.selectorUsed }
22960
23053
  };
22961
23054
  }
23055
+ function toOpensteerTagName2(nodeName) {
23056
+ const tagName = String(nodeName).trim().toLowerCase();
23057
+ return tagName.length === 0 ? "element" : tagName;
23058
+ }
22962
23059
  function normalizeOpensteerError(error) {
22963
23060
  return normalizeThrownOpensteerError(error, "Unknown Opensteer runtime failure");
22964
23061
  }
@@ -23196,7 +23293,10 @@ var init_runtime3 = __esm({
23196
23293
  sessionRef;
23197
23294
  pageRef;
23198
23295
  runId;
23199
- observations;
23296
+ observationSessions = /* @__PURE__ */ new Map();
23297
+ openingObservationSessions = /* @__PURE__ */ new Map();
23298
+ openedObservationSessions = /* @__PURE__ */ new Set();
23299
+ observationSessionStorage = new async_hooks.AsyncLocalStorage();
23200
23300
  operationEventStorage = new async_hooks.AsyncLocalStorage();
23201
23301
  pendingOperationEventCaptures = [];
23202
23302
  ownsEngine = false;
@@ -23248,18 +23348,26 @@ var init_runtime3 = __esm({
23248
23348
  }
23249
23349
  async setObservabilityConfig(input) {
23250
23350
  this.observationConfig = normalizeObservabilityConfig(input);
23251
- const observationSessionId = this.resolveObservationSessionId();
23252
- if (observationSessionId === void 0) {
23253
- return this.observationConfig;
23254
- }
23255
- const sink = this.injectedObservationSink ?? (await this.ensureRoot()).observations;
23256
- this.observations = await sink.openSession({
23257
- sessionId: observationSessionId,
23258
- openedAt: Date.now(),
23259
- config: this.observationConfig
23260
- });
23351
+ await this.ensureConfiguredObservationSession();
23261
23352
  return this.observationConfig;
23262
23353
  }
23354
+ async withObservationSessionId(sessionId, task) {
23355
+ return await this.observationSessionStorage.run(
23356
+ {
23357
+ mode: "session",
23358
+ sessionId: normalizeNonEmptyString("sessionId", sessionId)
23359
+ },
23360
+ task
23361
+ );
23362
+ }
23363
+ async withoutObservationSession(task) {
23364
+ return await this.observationSessionStorage.run(
23365
+ {
23366
+ mode: "disabled"
23367
+ },
23368
+ task
23369
+ );
23370
+ }
23263
23371
  async open(input = {}, options = {}) {
23264
23372
  assertValidSemanticOperationInput("session.open", input);
23265
23373
  if (input.workspace !== void 0 && normalizeNamespace2(input.workspace) !== this.workspace) {
@@ -23276,7 +23384,7 @@ var init_runtime3 = __esm({
23276
23384
  options
23277
23385
  );
23278
23386
  }
23279
- return this.readSessionState();
23387
+ return this.readNavigationSummary();
23280
23388
  }
23281
23389
  const startedAt = Date.now();
23282
23390
  const root = await this.ensureRoot();
@@ -23297,7 +23405,8 @@ var init_runtime3 = __esm({
23297
23405
  openedSessionRef = sessionRef;
23298
23406
  const createdPage = await timeout.runStep(
23299
23407
  () => engine.createPage({
23300
- sessionRef
23408
+ sessionRef,
23409
+ ...input.url === void 0 ? {} : { url: input.url }
23301
23410
  })
23302
23411
  );
23303
23412
  openedPageRef = createdPage.data.pageRef;
@@ -23307,18 +23416,19 @@ var init_runtime3 = __esm({
23307
23416
  await timeout.runStep(() => this.ensureSemantics());
23308
23417
  let frameRef2 = createdPage.frameRef;
23309
23418
  if (input.url !== void 0) {
23310
- const navigation = await this.navigatePage(
23311
- {
23419
+ await timeout.runStep(
23420
+ () => settleWithPolicy(this.policy.settle, {
23312
23421
  operation: "session.open",
23422
+ trigger: "navigation",
23423
+ engine: this.requireEngine(),
23313
23424
  pageRef: createdPage.data.pageRef,
23314
- url: input.url
23315
- },
23316
- timeout
23425
+ signal: timeout.signal,
23426
+ remainingMs: timeout.remainingMs()
23427
+ })
23317
23428
  );
23318
- frameRef2 = navigation.data.mainFrame.frameRef;
23319
23429
  }
23320
23430
  return {
23321
- state: await timeout.runStep(() => this.readSessionState()),
23431
+ state: await timeout.runStep(() => this.readNavigationSummary()),
23322
23432
  frameRef: frameRef2
23323
23433
  };
23324
23434
  },
@@ -23411,7 +23521,11 @@ var init_runtime3 = __esm({
23411
23521
  "page.new cannot use openerPageRef before a session exists"
23412
23522
  );
23413
23523
  }
23414
- return this.open(input.url === void 0 ? {} : { url: input.url }, options);
23524
+ const summary = await this.open(input.url === void 0 ? {} : { url: input.url }, options);
23525
+ return {
23526
+ pageRef: await this.ensurePageRef(),
23527
+ ...summary
23528
+ };
23415
23529
  }
23416
23530
  const startedAt = Date.now();
23417
23531
  try {
@@ -23426,7 +23540,7 @@ var init_runtime3 = __esm({
23426
23540
  })
23427
23541
  );
23428
23542
  this.pageRef = created.data.pageRef;
23429
- return this.readSessionState();
23543
+ return this.readCreatedPageOutput(created.data.pageRef);
23430
23544
  },
23431
23545
  options
23432
23546
  );
@@ -23468,7 +23582,7 @@ var init_runtime3 = __esm({
23468
23582
  () => this.requireEngine().activatePage({ pageRef: input.pageRef })
23469
23583
  );
23470
23584
  this.pageRef = input.pageRef;
23471
- return this.readSessionState();
23585
+ return this.readNavigationSummary(input.pageRef);
23472
23586
  },
23473
23587
  options
23474
23588
  );
@@ -23594,7 +23708,7 @@ var init_runtime3 = __esm({
23594
23708
  timeout.throwIfAborted();
23595
23709
  return {
23596
23710
  navigation: navigation2,
23597
- state: await timeout.runStep(() => this.readSessionState())
23711
+ state: await timeout.runStep(() => this.readNavigationSummary(pageRef))
23598
23712
  };
23599
23713
  },
23600
23714
  (diagnostics) => {
@@ -24828,7 +24942,7 @@ var init_runtime3 = __esm({
24828
24942
  let mutationCaptureDiagnostics;
24829
24943
  let boundaryDiagnostics;
24830
24944
  try {
24831
- const { artifacts, output } = await this.runMutationCapturedOperation(
24945
+ const { artifacts, output, result } = await this.runMutationCapturedOperation(
24832
24946
  "computer.execute",
24833
24947
  {
24834
24948
  ...input.captureNetwork === void 0 ? {} : { captureNetwork: input.captureNetwork },
@@ -24846,9 +24960,14 @@ var init_runtime3 = __esm({
24846
24960
  await this.invalidateLiveSnapshotCounters([pageRef, output2.pageRef], timeout);
24847
24961
  this.pageRef = output2.pageRef;
24848
24962
  const artifacts2 = await this.persistComputerArtifacts(output2, timeout);
24963
+ const result2 = {
24964
+ ...await timeout.runStep(() => this.readNavigationSummary(output2.pageRef)),
24965
+ screenshot: artifacts2.screenshot
24966
+ };
24849
24967
  return {
24850
24968
  artifacts: { manifests: artifacts2.manifests },
24851
- output: artifacts2.output
24969
+ output: output2,
24970
+ result: result2
24852
24971
  };
24853
24972
  } catch (error) {
24854
24973
  boundaryDiagnostics ??= takeActionBoundaryDiagnostics(timeout.signal);
@@ -24885,7 +25004,7 @@ var init_runtime3 = __esm({
24885
25004
  documentEpoch: output.screenshot.documentEpoch
24886
25005
  })
24887
25006
  });
24888
- return output;
25007
+ return result;
24889
25008
  } catch (error) {
24890
25009
  await this.appendTrace({
24891
25010
  operation: "computer.execute",
@@ -25033,8 +25152,9 @@ var init_runtime3 = __esm({
25033
25152
  mutationCaptureDiagnostics = diagnostics;
25034
25153
  }
25035
25154
  );
25036
- const output = toOpensteerActionResult(executed.result);
25155
+ const output = toOpensteerActionResult(executed.result.resolved);
25037
25156
  const actionEvents = "events" in executed.result ? executed.result.events : void 0;
25157
+ const resolvedTarget = toOpensteerResolvedTarget2(executed.result.resolved);
25038
25158
  await this.appendTrace({
25039
25159
  operation,
25040
25160
  startedAt,
@@ -25042,8 +25162,13 @@ var init_runtime3 = __esm({
25042
25162
  outcome: "ok",
25043
25163
  ...actionEvents === void 0 ? {} : { events: actionEvents },
25044
25164
  data: {
25045
- target: output.target,
25046
- ...output.point === void 0 ? {} : { point: output.point },
25165
+ target: resolvedTarget,
25166
+ ..."point" in executed.result && executed.result.point !== void 0 ? {
25167
+ point: {
25168
+ x: executed.result.point.x,
25169
+ y: executed.result.point.y
25170
+ }
25171
+ } : {},
25047
25172
  ...boundaryDiagnostics === void 0 ? {} : { settle: boundaryDiagnostics },
25048
25173
  ...buildMutationCaptureTraceData(mutationCaptureDiagnostics)
25049
25174
  },
@@ -26508,20 +26633,20 @@ var init_runtime3 = __esm({
26508
26633
  throw error;
26509
26634
  }
26510
26635
  }
26511
- async readSessionState() {
26512
- const pageRef = await this.ensurePageRef();
26636
+ async readNavigationSummary(targetPageRef) {
26637
+ const pageRef = targetPageRef ?? await this.ensurePageRef();
26513
26638
  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
26639
  return {
26519
- sessionRef,
26520
- pageRef,
26521
26640
  url: pageInfo.url,
26522
26641
  title: pageInfo.title
26523
26642
  };
26524
26643
  }
26644
+ async readCreatedPageOutput(pageRef) {
26645
+ return {
26646
+ pageRef,
26647
+ ...await this.readNavigationSummary(pageRef)
26648
+ };
26649
+ }
26525
26650
  async captureSnapshotArtifacts(pageRef, options, timeout) {
26526
26651
  const root = this.requireRoot();
26527
26652
  const mainFrame = await timeout.runStep(() => getMainFrame(this.requireEngine(), pageRef));
@@ -26593,12 +26718,12 @@ var init_runtime3 = __esm({
26593
26718
  const screenshotPayload = manifestToExternalBinaryLocation(root.rootPath, screenshotManifest);
26594
26719
  return {
26595
26720
  manifests,
26596
- output: {
26597
- ...output,
26598
- screenshot: {
26599
- ...output.screenshot,
26600
- payload: screenshotPayload
26601
- }
26721
+ screenshot: {
26722
+ payload: screenshotPayload,
26723
+ format: output.screenshot.format,
26724
+ size: output.screenshot.size,
26725
+ coordinateSpace: output.screenshot.coordinateSpace,
26726
+ ...output.screenshot.clip === void 0 ? {} : { clip: output.screenshot.clip }
26602
26727
  }
26603
26728
  };
26604
26729
  }
@@ -26686,7 +26811,7 @@ var init_runtime3 = __esm({
26686
26811
  }
26687
26812
  async resetRuntimeState(options) {
26688
26813
  const engine = this.engine;
26689
- const observations = this.observations;
26814
+ const observationSessions = [...this.openedObservationSessions];
26690
26815
  this.networkHistory.clear();
26691
26816
  this.sessionRef = void 0;
26692
26817
  this.pageRef = void 0;
@@ -26695,9 +26820,15 @@ var init_runtime3 = __esm({
26695
26820
  this.computer = void 0;
26696
26821
  this.extractionDescriptors = void 0;
26697
26822
  this.engine = void 0;
26698
- this.observations = void 0;
26823
+ this.observationSessions.clear();
26824
+ this.openingObservationSessions.clear();
26825
+ this.openedObservationSessions.clear();
26699
26826
  this.pendingOperationEventCaptures.length = 0;
26700
- await observations?.close("runtime_reset").catch(() => void 0);
26827
+ await Promise.allSettled(
26828
+ observationSessions.map(
26829
+ (observationSession) => observationSession.close("runtime_reset").catch(() => void 0)
26830
+ )
26831
+ );
26701
26832
  if (options.disposeEngine && this.ownsEngine && engine?.dispose) {
26702
26833
  await engine.dispose();
26703
26834
  }
@@ -26707,23 +26838,64 @@ var init_runtime3 = __esm({
26707
26838
  if (this.observationConfig.profile === "off") {
26708
26839
  return void 0;
26709
26840
  }
26710
- if (this.observations !== void 0) {
26711
- return this.observations;
26841
+ const observationSessionId = this.resolveObservationSessionId();
26842
+ if (observationSessionId === void 0) {
26843
+ return void 0;
26844
+ }
26845
+ const existingObservationSession = this.observationSessions.get(observationSessionId);
26846
+ if (existingObservationSession !== void 0) {
26847
+ return existingObservationSession;
26848
+ }
26849
+ const openingObservationSession = this.openingObservationSessions.get(observationSessionId);
26850
+ if (openingObservationSession !== void 0) {
26851
+ return await openingObservationSession;
26852
+ }
26853
+ const openObservationSessionTask = this.openObservationSession(observationSessionId).finally(
26854
+ () => {
26855
+ this.openingObservationSessions.delete(observationSessionId);
26856
+ }
26857
+ );
26858
+ this.openingObservationSessions.set(observationSessionId, openObservationSessionTask);
26859
+ return await openObservationSessionTask;
26860
+ }
26861
+ async ensureConfiguredObservationSession() {
26862
+ if (this.observationConfig.profile === "off") {
26863
+ return void 0;
26712
26864
  }
26713
26865
  const observationSessionId = this.resolveObservationSessionId();
26714
26866
  if (observationSessionId === void 0) {
26715
26867
  return void 0;
26716
26868
  }
26869
+ const hadObservationSession = this.observationSessions.has(observationSessionId) || this.openingObservationSessions.has(observationSessionId);
26870
+ const observationSession = await this.ensureObservationSession();
26871
+ if (observationSession !== void 0 && hadObservationSession) {
26872
+ await observationSession.configure?.({
26873
+ config: this.observationConfig,
26874
+ updatedAt: Date.now()
26875
+ });
26876
+ }
26877
+ return observationSession;
26878
+ }
26879
+ resolveObservationSessionId() {
26880
+ const scopedSession = this.observationSessionStorage.getStore();
26881
+ if (scopedSession?.mode === "session") {
26882
+ return scopedSession.sessionId;
26883
+ }
26884
+ if (scopedSession?.mode === "disabled") {
26885
+ return void 0;
26886
+ }
26887
+ return this.observationSessionId ?? this.sessionRef;
26888
+ }
26889
+ async openObservationSession(sessionId) {
26717
26890
  const sink = this.injectedObservationSink ?? (await this.ensureRoot()).observations;
26718
- this.observations = await sink.openSession({
26719
- sessionId: observationSessionId,
26891
+ const observationSession = await sink.openSession({
26892
+ sessionId,
26720
26893
  openedAt: Date.now(),
26721
26894
  config: this.observationConfig
26722
26895
  });
26723
- return this.observations;
26724
- }
26725
- resolveObservationSessionId() {
26726
- return this.observationSessionId ?? this.sessionRef;
26896
+ this.observationSessions.set(sessionId, observationSession);
26897
+ this.openedObservationSessions.add(observationSession);
26898
+ return observationSession;
26727
26899
  }
26728
26900
  runWithOperationTimeout(operation, callback, options = {}) {
26729
26901
  const timeoutPolicy = options.timeoutMs === void 0 ? this.policy.timeout : {
@@ -28683,7 +28855,11 @@ function generateReplayScript(options) {
28683
28855
  ``,
28684
28856
  ...renderOpensteerBootstrap(replayTarget),
28685
28857
  ``,
28686
- `const ${initialPageId} = (await opensteer.open(${JSON.stringify(initialPages[0]?.initialUrl ?? "")})).pageRef;`,
28858
+ `await opensteer.open(${JSON.stringify(initialPages[0]?.initialUrl ?? "")});`,
28859
+ `const ${initialPageId} = (await opensteer.listPages()).activePageRef;`,
28860
+ `if (!${initialPageId}) {`,
28861
+ ` throw new Error("Opensteer did not report an active page after open().");`,
28862
+ `}`,
28687
28863
  `let activePageRef: string | undefined = ${initialPageId};`,
28688
28864
  ``,
28689
28865
  `async function ensureActive(pageRef: string): Promise<void> {`,
@@ -28887,26 +29063,35 @@ function renderOpensteerBootstrap(replayTarget) {
28887
29063
  `const opensteer = new Opensteer({`,
28888
29064
  ` provider: {`,
28889
29065
  ` mode: "cloud",`,
28890
- ` baseUrl: requireEnv(${JSON.stringify(replayTarget.baseUrlEnvVar ?? "OPENSTEER_BASE_URL")}),`,
28891
29066
  ` apiKey: requireEnv(${JSON.stringify(replayTarget.apiKeyEnvVar ?? "OPENSTEER_API_KEY")}),`,
29067
+ ...renderCloudBaseUrl(replayTarget),
28892
29068
  ...renderCloudBrowserProfile(replayTarget),
28893
29069
  ` },`,
28894
29070
  `});`
28895
29071
  ];
28896
29072
  }
28897
29073
  function renderRequireEnvHelper(replayTarget) {
28898
- const baseUrlEnvVar = replayTarget.baseUrlEnvVar ?? "OPENSTEER_BASE_URL";
28899
29074
  const apiKeyEnvVar = replayTarget.apiKeyEnvVar ?? "OPENSTEER_API_KEY";
29075
+ const requiredEnvVars = [
29076
+ ...replayTarget.baseUrlEnvVar === void 0 ? [] : [replayTarget.baseUrlEnvVar],
29077
+ apiKeyEnvVar
29078
+ ];
28900
29079
  return [
28901
29080
  `function requireEnv(name: string): string {`,
28902
29081
  ` const value = process.env[name];`,
28903
29082
  ` if (typeof value === "string" && value.trim().length > 0) {`,
28904
29083
  ` return value;`,
28905
29084
  ` }`,
28906
- ` throw new Error(\`Missing environment variable \${name}. Set ${baseUrlEnvVar} and ${apiKeyEnvVar} before replaying this recording.\`);`,
29085
+ ` throw new Error(\`Missing environment variable \${name}. Set ${requiredEnvVars.join(" and ")} before replaying this recording.\`);`,
28907
29086
  `}`
28908
29087
  ].join("\n");
28909
29088
  }
29089
+ function renderCloudBaseUrl(replayTarget) {
29090
+ if (replayTarget.baseUrlEnvVar === void 0) {
29091
+ return [];
29092
+ }
29093
+ return [` baseUrl: requireEnv(${JSON.stringify(replayTarget.baseUrlEnvVar)}),`];
29094
+ }
28910
29095
  function renderCloudBrowserProfile(replayTarget) {
28911
29096
  if (replayTarget.browserProfileId === void 0) {
28912
29097
  return [];
@@ -30169,6 +30354,41 @@ var init_session_proxy = __esm({
30169
30354
  };
30170
30355
  }
30171
30356
  });
30357
+ async function persistLocalActivePageHint(runtime, rootPath) {
30358
+ try {
30359
+ await syncPersistedLocalActivePageHint(runtime, rootPath);
30360
+ } catch {
30361
+ }
30362
+ }
30363
+ async function syncPersistedLocalActivePageHint(runtime, rootPath) {
30364
+ const record = await readPersistedLocalBrowserSessionRecord(rootPath);
30365
+ if (!record) {
30366
+ return;
30367
+ }
30368
+ const sessionInfo = await runtime.info();
30369
+ const activePageRef = sessionInfo.activePageRef;
30370
+ let activePageUrl;
30371
+ let activePageTitle;
30372
+ if (activePageRef !== void 0) {
30373
+ const pages = await runtime.listPages();
30374
+ const activePage = pages.pages.find((page) => page.pageRef === activePageRef);
30375
+ activePageUrl = activePage?.url;
30376
+ activePageTitle = activePage?.title;
30377
+ }
30378
+ const {
30379
+ activePageRef: _previousActivePageRef,
30380
+ activePageUrl: _previousActivePageUrl,
30381
+ activePageTitle: _previousActivePageTitle,
30382
+ ...restRecord
30383
+ } = record;
30384
+ await writePersistedSessionRecord(rootPath, {
30385
+ ...restRecord,
30386
+ updatedAt: Date.now(),
30387
+ ...activePageRef === void 0 ? {} : { activePageRef },
30388
+ ...activePageUrl === void 0 ? {} : { activePageUrl },
30389
+ ...activePageTitle === void 0 ? {} : { activePageTitle }
30390
+ });
30391
+ }
30172
30392
  function buildSharedRuntimeOptions(input) {
30173
30393
  const ownership = resolveOwnership(input.browser);
30174
30394
  const engineFactory = input.engineFactory ?? ((factoryOptions) => new OpensteerBrowserManager({
@@ -30215,14 +30435,37 @@ function normalizeWorkspace2(workspace) {
30215
30435
  function resolveOwnership(browser) {
30216
30436
  return typeof browser === "object" && browser.mode === "attach" ? "attached" : "owned";
30217
30437
  }
30218
- var OpensteerRuntime;
30438
+ var LocalActivePageHintRuntime, OpensteerRuntime;
30219
30439
  var init_runtime4 = __esm({
30220
30440
  "src/sdk/runtime.ts"() {
30221
30441
  init_src3();
30222
30442
  init_browser_manager();
30223
30443
  init_engine_selection2();
30444
+ init_live_session();
30224
30445
  init_root2();
30225
- OpensteerRuntime = class extends OpensteerSessionRuntime {
30446
+ LocalActivePageHintRuntime = class extends OpensteerSessionRuntime {
30447
+ async completeWithLocalActivePageHint(operation) {
30448
+ const output = await operation();
30449
+ await persistLocalActivePageHint(this, this.rootPath);
30450
+ return output;
30451
+ }
30452
+ async open(input = {}, options = {}) {
30453
+ return this.completeWithLocalActivePageHint(() => super.open(input, options));
30454
+ }
30455
+ async newPage(input = {}, options = {}) {
30456
+ return this.completeWithLocalActivePageHint(() => super.newPage(input, options));
30457
+ }
30458
+ async activatePage(input, options = {}) {
30459
+ return this.completeWithLocalActivePageHint(() => super.activatePage(input, options));
30460
+ }
30461
+ async closePage(input = {}, options = {}) {
30462
+ return this.completeWithLocalActivePageHint(() => super.closePage(input, options));
30463
+ }
30464
+ async goto(input, options = {}) {
30465
+ return this.completeWithLocalActivePageHint(() => super.goto(input, options));
30466
+ }
30467
+ };
30468
+ OpensteerRuntime = class extends LocalActivePageHintRuntime {
30226
30469
  constructor(options = {}) {
30227
30470
  const publicWorkspace = normalizeWorkspace2(options.workspace);
30228
30471
  const rootPath = options.rootPath ?? (publicWorkspace === void 0 ? path10__default.default.resolve(options.rootDir ?? process.cwd(), ".opensteer", "temporary", crypto.randomUUID()) : resolveFilesystemWorkspacePath({
@@ -30944,7 +31187,7 @@ var init_opensteer = __esm({
30944
31187
 
30945
31188
  // package.json
30946
31189
  var package_default = {
30947
- version: "0.9.6"};
31190
+ version: "0.9.8"};
30948
31191
 
30949
31192
  // src/cli/bin.ts
30950
31193
  init_browser_manager();
@@ -31599,14 +31842,22 @@ async function buildOperationInput(operation, parsed, runtime) {
31599
31842
  return parsed.rest[0] === void 0 ? {} : { mode: parsed.rest[0] };
31600
31843
  case "page.evaluate":
31601
31844
  if (parsed.rest[0] === void 0) {
31602
- throw new CliError("missing_arguments", "evaluate requires a script.", CLI_USAGE_HINTS[operation]);
31845
+ throw new CliError(
31846
+ "missing_arguments",
31847
+ "evaluate requires a script.",
31848
+ CLI_USAGE_HINTS[operation]
31849
+ );
31603
31850
  }
31604
31851
  return {
31605
31852
  script: joinRest(parsed.rest, 0)
31606
31853
  };
31607
31854
  case "page.add-init-script":
31608
31855
  if (parsed.rest[0] === void 0) {
31609
- throw new CliError("missing_arguments", "init-script requires a script.", CLI_USAGE_HINTS[operation]);
31856
+ throw new CliError(
31857
+ "missing_arguments",
31858
+ "init-script requires a script.",
31859
+ CLI_USAGE_HINTS[operation]
31860
+ );
31610
31861
  }
31611
31862
  return {
31612
31863
  script: joinRest(parsed.rest, 0)
@@ -31622,7 +31873,11 @@ async function buildOperationInput(operation, parsed, runtime) {
31622
31873
  return buildElementTargetInput(parsed, "hover");
31623
31874
  case "dom.input": {
31624
31875
  if (parsed.rest[1] === void 0) {
31625
- throw new CliError("missing_arguments", "input requires an element number and text.", CLI_USAGE_HINTS[operation]);
31876
+ throw new CliError(
31877
+ "missing_arguments",
31878
+ "input requires an element number and text.",
31879
+ CLI_USAGE_HINTS[operation]
31880
+ );
31626
31881
  }
31627
31882
  const pressEnter = readOptionalBoolean(parsed.rawOptions, "press-enter");
31628
31883
  return {
@@ -31653,7 +31908,11 @@ async function buildOperationInput(operation, parsed, runtime) {
31653
31908
  }
31654
31909
  case "dom.extract": {
31655
31910
  if (parsed.rest[0] === void 0) {
31656
- throw new CliError("missing_arguments", "extract requires a template.", CLI_USAGE_HINTS[operation]);
31911
+ throw new CliError(
31912
+ "missing_arguments",
31913
+ "extract requires a template.",
31914
+ CLI_USAGE_HINTS[operation]
31915
+ );
31657
31916
  }
31658
31917
  const persist = readPersistKey(parsed, "extract");
31659
31918
  return {
@@ -31689,7 +31948,11 @@ async function buildOperationInput(operation, parsed, runtime) {
31689
31948
  }
31690
31949
  case "network.detail": {
31691
31950
  if (parsed.rest[0] === void 0) {
31692
- throw new CliError("missing_arguments", "network detail requires a record id.", CLI_USAGE_HINTS[operation]);
31951
+ throw new CliError(
31952
+ "missing_arguments",
31953
+ "network detail requires a record id.",
31954
+ CLI_USAGE_HINTS[operation]
31955
+ );
31693
31956
  }
31694
31957
  const probeFlag = readOptionalBoolean(parsed.rawOptions, "probe");
31695
31958
  return {
@@ -31700,7 +31963,11 @@ async function buildOperationInput(operation, parsed, runtime) {
31700
31963
  case "session.fetch": {
31701
31964
  const url = parsed.rest[0];
31702
31965
  if (url === void 0) {
31703
- throw new CliError("missing_arguments", "fetch requires a URL.", CLI_USAGE_HINTS[operation]);
31966
+ throw new CliError(
31967
+ "missing_arguments",
31968
+ "fetch requires a URL.",
31969
+ CLI_USAGE_HINTS[operation]
31970
+ );
31704
31971
  }
31705
31972
  const bodyJson = readJsonValue(parsed.rawOptions, "body");
31706
31973
  const bodyText = readSingle(parsed.rawOptions, "body-text");
@@ -31739,7 +32006,11 @@ async function buildOperationInput(operation, parsed, runtime) {
31739
32006
  const siteKey = readSingle(parsed.rawOptions, "site-key");
31740
32007
  const pageUrl = readSingle(parsed.rawOptions, "page-url");
31741
32008
  if (provider === void 0 || apiKey === void 0) {
31742
- throw new CliError("missing_arguments", 'captcha solve requires "--provider" and "--api-key".', CLI_USAGE_HINTS[operation]);
32009
+ throw new CliError(
32010
+ "missing_arguments",
32011
+ 'captcha solve requires "--provider" and "--api-key".',
32012
+ CLI_USAGE_HINTS[operation]
32013
+ );
31743
32014
  }
31744
32015
  return {
31745
32016
  provider: readCaptchaProvider(provider),
@@ -31769,7 +32040,11 @@ async function buildOperationInput(operation, parsed, runtime) {
31769
32040
  case "scripts.beautify":
31770
32041
  case "scripts.deobfuscate": {
31771
32042
  if (parsed.rest[0] === void 0) {
31772
- throw new CliError("missing_arguments", `${parsed.command.join(" ")} requires an artifact id.`, CLI_USAGE_HINTS[operation]);
32043
+ throw new CliError(
32044
+ "missing_arguments",
32045
+ `${parsed.command.join(" ")} requires an artifact id.`,
32046
+ CLI_USAGE_HINTS[operation]
32047
+ );
31773
32048
  }
31774
32049
  const persist = readOptionalBoolean(parsed.rawOptions, "persist");
31775
32050
  return {
@@ -31779,7 +32054,11 @@ async function buildOperationInput(operation, parsed, runtime) {
31779
32054
  }
31780
32055
  case "scripts.sandbox":
31781
32056
  if (parsed.rest[0] === void 0) {
31782
- throw new CliError("missing_arguments", "scripts sandbox requires an artifact id.", CLI_USAGE_HINTS[operation]);
32057
+ throw new CliError(
32058
+ "missing_arguments",
32059
+ "scripts sandbox requires an artifact id.",
32060
+ CLI_USAGE_HINTS[operation]
32061
+ );
31783
32062
  }
31784
32063
  {
31785
32064
  const fidelity = readSingle(parsed.rawOptions, "fidelity");
@@ -31828,14 +32107,22 @@ async function buildOperationInput(operation, parsed, runtime) {
31828
32107
  case "interaction.get":
31829
32108
  case "interaction.replay":
31830
32109
  if (parsed.rest[0] === void 0) {
31831
- throw new CliError("missing_arguments", `${parsed.command.join(" ")} requires a trace id.`, CLI_USAGE_HINTS[operation]);
32110
+ throw new CliError(
32111
+ "missing_arguments",
32112
+ `${parsed.command.join(" ")} requires a trace id.`,
32113
+ CLI_USAGE_HINTS[operation]
32114
+ );
31832
32115
  }
31833
32116
  return {
31834
32117
  traceId: parsed.rest[0]
31835
32118
  };
31836
32119
  case "interaction.diff":
31837
32120
  if (parsed.rest[0] === void 0 || parsed.rest[1] === void 0) {
31838
- throw new CliError("missing_arguments", "interaction diff requires two trace ids.", CLI_USAGE_HINTS[operation]);
32121
+ throw new CliError(
32122
+ "missing_arguments",
32123
+ "interaction diff requires two trace ids.",
32124
+ CLI_USAGE_HINTS[operation]
32125
+ );
31839
32126
  }
31840
32127
  return {
31841
32128
  leftTraceId: parsed.rest[0],
@@ -31843,7 +32130,11 @@ async function buildOperationInput(operation, parsed, runtime) {
31843
32130
  };
31844
32131
  case "artifact.read":
31845
32132
  if (parsed.rest[0] === void 0) {
31846
- throw new CliError("missing_arguments", "artifact read requires an artifact id.", CLI_USAGE_HINTS[operation]);
32133
+ throw new CliError(
32134
+ "missing_arguments",
32135
+ "artifact read requires an artifact id.",
32136
+ CLI_USAGE_HINTS[operation]
32137
+ );
31847
32138
  }
31848
32139
  return {
31849
32140
  artifactId: parsed.rest[0]
@@ -31981,7 +32272,10 @@ function buildComputerExecuteInput(parsed) {
31981
32272
  }
31982
32273
  };
31983
32274
  default:
31984
- throw new CliError("unknown_command", `Unknown computer command: ${parsed.command.join(" ")}`);
32275
+ throw new CliError(
32276
+ "unknown_command",
32277
+ `Unknown computer command: ${parsed.command.join(" ")}`
32278
+ );
31985
32279
  }
31986
32280
  }
31987
32281
  async function resolvePageRefByIndex(runtime, index) {
@@ -32011,7 +32305,10 @@ function readRequiredNumber(value, message) {
32011
32305
  }
32012
32306
  function readSingleDirection(value) {
32013
32307
  if (value === void 0 || !SCROLL_DIRECTIONS.has(value)) {
32014
- throw new CliError("missing_arguments", "scroll requires a direction: up, down, left, or right.");
32308
+ throw new CliError(
32309
+ "missing_arguments",
32310
+ "scroll requires a direction: up, down, left, or right."
32311
+ );
32015
32312
  }
32016
32313
  return value;
32017
32314
  }
@@ -32038,13 +32335,19 @@ function readCaptchaProvider(value) {
32038
32335
  }
32039
32336
  function readCaptchaType(value) {
32040
32337
  if (value === void 0 || !CAPTCHA_TYPES.has(value)) {
32041
- throw new CliError("invalid_value", 'Expected "--type" to be one of: recaptcha-v2, hcaptcha, turnstile.');
32338
+ throw new CliError(
32339
+ "invalid_value",
32340
+ 'Expected "--type" to be one of: recaptcha-v2, hcaptcha, turnstile.'
32341
+ );
32042
32342
  }
32043
32343
  return value;
32044
32344
  }
32045
32345
  function readSandboxFidelity(value) {
32046
32346
  if (value === void 0 || !SANDBOX_FIDELITIES.has(value)) {
32047
- throw new CliError("invalid_value", 'Expected "--fidelity" to be one of: minimal, standard, full.');
32347
+ throw new CliError(
32348
+ "invalid_value",
32349
+ 'Expected "--fidelity" to be one of: minimal, standard, full.'
32350
+ );
32048
32351
  }
32049
32352
  return value;
32050
32353
  }
@@ -32067,7 +32370,10 @@ function readKeyModifiers(value) {
32067
32370
  }
32068
32371
  for (const modifier of modifiers) {
32069
32372
  if (!KEY_MODIFIERS.has(modifier)) {
32070
- throw new CliError("invalid_value", 'Expected "--modifiers" to contain only: Shift, Control, Alt, Meta.');
32373
+ throw new CliError(
32374
+ "invalid_value",
32375
+ 'Expected "--modifiers" to contain only: Shift, Control, Alt, Meta.'
32376
+ );
32071
32377
  }
32072
32378
  }
32073
32379
  return [...new Set(modifiers)];
@@ -32102,6 +32408,7 @@ function joinRest(rest, startIndex) {
32102
32408
  function renderOperationOutput(operation, result, input) {
32103
32409
  switch (operation) {
32104
32410
  case "session.open":
32411
+ case "page.activate":
32105
32412
  case "page.goto":
32106
32413
  return renderJson(formatNavigationOutput(result));
32107
32414
  case "page.snapshot":
@@ -32110,14 +32417,14 @@ function renderOperationOutput(operation, result, input) {
32110
32417
  case "dom.hover":
32111
32418
  case "dom.input":
32112
32419
  case "dom.scroll":
32113
- return renderJson(formatActionOutput(result, input));
32420
+ return renderJson(formatActionOutput(result));
32114
32421
  case "dom.extract":
32115
32422
  return renderJson(formatExtractOutput(result));
32116
32423
  case "page.list":
32117
- case "page.new":
32118
- case "page.activate":
32119
32424
  case "page.close":
32120
32425
  return formatTabOutput(result);
32426
+ case "page.new":
32427
+ return renderJson(formatCreatedPageOutput(result));
32121
32428
  case "network.query":
32122
32429
  return formatNetworkQueryOutput(result, input);
32123
32430
  case "network.detail":
@@ -32155,6 +32462,12 @@ function formatNavigationOutput(result) {
32155
32462
  ...readStringField(result, "title") === void 0 ? {} : { title: readStringField(result, "title") }
32156
32463
  };
32157
32464
  }
32465
+ function formatCreatedPageOutput(result) {
32466
+ return {
32467
+ ...readStringField(result, "pageRef") === void 0 ? {} : { pageRef: readStringField(result, "pageRef") },
32468
+ ...formatNavigationOutput(result)
32469
+ };
32470
+ }
32158
32471
  function formatSnapshotOutput(result) {
32159
32472
  if (result !== null && typeof result === "object" && typeof result.html === "string") {
32160
32473
  return `${result.html}
@@ -32162,36 +32475,11 @@ function formatSnapshotOutput(result) {
32162
32475
  }
32163
32476
  return renderJson(result);
32164
32477
  }
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") }
32478
+ function formatActionOutput(result) {
32479
+ return {
32480
+ ...readStringField(result, "tagName") === void 0 ? {} : { tagName: readStringField(result, "tagName") },
32481
+ ...readStringField(result, "persist") === void 0 ? {} : { persist: readStringField(result, "persist") }
32170
32482
  };
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
32483
  }
32196
32484
  function formatExtractOutput(result) {
32197
32485
  const data = readUnknownField(result, "data");
@@ -32423,20 +32711,18 @@ function formatStateOutput(result) {
32423
32711
  `;
32424
32712
  }
32425
32713
  function formatComputerOutput(result) {
32426
- const action = readObjectField(result, "action");
32427
32714
  const screenshot = readObjectField(result, "screenshot");
32428
32715
  const payload = readObjectField(screenshot, "payload");
32429
- const timing = readObjectField(result, "timing");
32430
32716
  return {
32431
- ...action === void 0 ? {} : { action },
32717
+ ...readStringField(result, "url") === void 0 ? {} : { url: readStringField(result, "url") },
32718
+ ...readStringField(result, "title") === void 0 ? {} : { title: readStringField(result, "title") },
32432
32719
  ...payload === void 0 ? {} : {
32433
32720
  screenshot: {
32434
32721
  ...readStringField(payload, "uri") === void 0 ? {} : { uri: readStringField(payload, "uri") },
32435
32722
  ...readStringField(screenshot, "format") === void 0 ? {} : { format: readStringField(screenshot, "format") },
32436
32723
  ...readObjectField(screenshot, "size") === void 0 ? {} : { size: readObjectField(screenshot, "size") }
32437
32724
  }
32438
- },
32439
- ...timing === void 0 ? {} : { timingMs: timing }
32725
+ }
32440
32726
  };
32441
32727
  }
32442
32728
  function formatScriptsCaptureOutput(result) {
@@ -33098,7 +33384,7 @@ async function collectOpensteerStatus(input) {
33098
33384
  ...output,
33099
33385
  rootPath,
33100
33386
  lanes: {
33101
- local: describeLocalLane(localRecord, input.provider.mode === "local"),
33387
+ local: await describeLocalLane(localRecord, input.provider.mode === "local"),
33102
33388
  cloud: await describeCloudLane({
33103
33389
  record: cloudRecord,
33104
33390
  current: input.provider.mode === "cloud",
@@ -33150,8 +33436,38 @@ async function readWorkspaceCloudRecord(rootPath) {
33150
33436
  }
33151
33437
  return readPersistedCloudSessionRecord(rootPath);
33152
33438
  }
33153
- function describeLocalLane(record, current) {
33154
- if (record === void 0 || !isProcessRunning(record.pid)) {
33439
+ async function describeLocalLane(record, current) {
33440
+ if (record === void 0) {
33441
+ return {
33442
+ provider: "local",
33443
+ status: "idle",
33444
+ current,
33445
+ summary: "none"
33446
+ };
33447
+ }
33448
+ if (getPersistedLocalBrowserSessionOwnership(record) === "attached") {
33449
+ if (!await isAttachedLocalBrowserSessionReachable(record)) {
33450
+ return {
33451
+ provider: "local",
33452
+ status: "stale",
33453
+ current,
33454
+ summary: "attached browser unavailable",
33455
+ detail: record.engine,
33456
+ engine: record.engine
33457
+ };
33458
+ }
33459
+ const browser2 = record.executablePath ? path10__default.default.basename(record.executablePath).replace(/\.[A-Za-z0-9]+$/u, "") : void 0;
33460
+ return {
33461
+ provider: "local",
33462
+ status: "active",
33463
+ current,
33464
+ summary: "attached browser",
33465
+ detail: browser2 ?? record.engine,
33466
+ engine: record.engine,
33467
+ ...browser2 === void 0 ? {} : { browser: browser2 }
33468
+ };
33469
+ }
33470
+ if (!isProcessRunning(record.pid)) {
33155
33471
  return {
33156
33472
  provider: "local",
33157
33473
  status: "idle",
@@ -33302,13 +33618,13 @@ async function resolveSessionSummary(manifest) {
33302
33618
  return {
33303
33619
  sessionId: manifest.sessionId,
33304
33620
  label: manifest.workspace ?? (path10__default.default.basename(manifest.rootPath) || manifest.sessionId),
33305
- status: isProcessRunning(record.pid) ? "live" : "stale",
33621
+ status: getPersistedLocalBrowserSessionOwnership(record) === "attached" || isProcessRunning(record.pid) ? "live" : "stale",
33306
33622
  ...manifest.workspace === void 0 ? {} : { workspace: manifest.workspace },
33307
33623
  rootPath: manifest.rootPath,
33308
33624
  engine: record.engine,
33309
33625
  ownership: manifest.ownership,
33310
- pid: record.pid,
33311
33626
  startedAt: record.startedAt,
33627
+ ...record.pid > 0 ? { pid: record.pid } : {},
33312
33628
  ...browserName === void 0 ? {} : { browserName }
33313
33629
  };
33314
33630
  }
@@ -33336,7 +33652,16 @@ async function readLiveRecord(manifest) {
33336
33652
  if (!record) {
33337
33653
  return void 0;
33338
33654
  }
33339
- if (record.pid !== manifest.pid || record.startedAt !== manifest.startedAt || !isProcessRunning(record.pid)) {
33655
+ if (buildLocalViewSessionIdForRecord({
33656
+ rootPath: manifest.rootPath,
33657
+ live: record
33658
+ }) !== manifest.sessionId || record.startedAt !== manifest.startedAt || record.engine !== manifest.engine || getPersistedLocalBrowserSessionOwnership(record) !== manifest.ownership) {
33659
+ return void 0;
33660
+ }
33661
+ if (getPersistedLocalBrowserSessionOwnership(record) === "attached") {
33662
+ return await isAttachedLocalBrowserSessionReachable(record) ? record : void 0;
33663
+ }
33664
+ if (record.pid !== manifest.pid || !isProcessRunning(record.pid)) {
33340
33665
  return void 0;
33341
33666
  }
33342
33667
  return record;
@@ -33630,6 +33955,172 @@ init_process_owner();
33630
33955
  init_service_state();
33631
33956
  init_service_state();
33632
33957
 
33958
+ // src/local-view/browser-target-order.ts
33959
+ async function readPageTargetId(page) {
33960
+ const cdp = await page.context().newCDPSession(page);
33961
+ try {
33962
+ const result = await cdp.send("Target.getTargetInfo");
33963
+ const targetId = result?.targetInfo?.targetId;
33964
+ return typeof targetId === "string" && targetId.length > 0 ? targetId : null;
33965
+ } finally {
33966
+ await cdp.detach().catch(() => void 0);
33967
+ }
33968
+ }
33969
+ async function readBrowserPageTargetOrder(browserContext) {
33970
+ const browser = browserContext.browser();
33971
+ if (!browser || !hasBrowserCdpSession(browser)) {
33972
+ return [];
33973
+ }
33974
+ const cdp = await browser.newBrowserCDPSession();
33975
+ try {
33976
+ const result = await cdp.send("Target.getTargets");
33977
+ return normalizeBrowserPageTargetOrder(result?.targetInfos ?? []);
33978
+ } catch {
33979
+ return [];
33980
+ } finally {
33981
+ await cdp.detach().catch(() => void 0);
33982
+ }
33983
+ }
33984
+ async function orderPagesByBrowserTargetOrder(browserContext, pages) {
33985
+ if (pages.length < 2) {
33986
+ return pages;
33987
+ }
33988
+ const orderedTargetIds = await readBrowserPageTargetOrder(browserContext);
33989
+ if (orderedTargetIds.length === 0) {
33990
+ return pages;
33991
+ }
33992
+ const rankByTargetId = new Map(orderedTargetIds.map((targetId, index) => [targetId, index]));
33993
+ const targetIds = await Promise.all(
33994
+ pages.map((page) => readPageTargetId(page).catch(() => null))
33995
+ );
33996
+ return pages.map((page, index) => {
33997
+ const targetId = targetIds[index] ?? void 0;
33998
+ return {
33999
+ page,
34000
+ index,
34001
+ rank: targetId === void 0 ? void 0 : rankByTargetId.get(targetId)
34002
+ };
34003
+ }).sort((left, right) => {
34004
+ if (left.rank !== void 0 && right.rank !== void 0) {
34005
+ return left.rank - right.rank;
34006
+ }
34007
+ if (left.rank !== void 0) {
34008
+ return -1;
34009
+ }
34010
+ if (right.rank !== void 0) {
34011
+ return 1;
34012
+ }
34013
+ return left.index - right.index;
34014
+ }).map((entry) => entry.page);
34015
+ }
34016
+ function hasBrowserCdpSession(browser) {
34017
+ return typeof browser.newBrowserCDPSession === "function";
34018
+ }
34019
+ function normalizeBrowserPageTargetOrder(targetInfos) {
34020
+ const reversedPageInfos = [];
34021
+ for (const targetInfo of targetInfos) {
34022
+ if (targetInfo.type !== "page") {
34023
+ continue;
34024
+ }
34025
+ const targetId = typeof targetInfo.targetId === "string" && targetInfo.targetId.length > 0 ? targetInfo.targetId : void 0;
34026
+ if (targetId === void 0) {
34027
+ continue;
34028
+ }
34029
+ reversedPageInfos.push({
34030
+ targetId,
34031
+ openerId: typeof targetInfo.openerId === "string" && targetInfo.openerId.length > 0 ? targetInfo.openerId : void 0
34032
+ });
34033
+ }
34034
+ reversedPageInfos.reverse();
34035
+ const rawTargetInfoById = new Map(
34036
+ reversedPageInfos.map((targetInfo) => [targetInfo.targetId, targetInfo])
34037
+ );
34038
+ const targetInfoById = new Map(
34039
+ reversedPageInfos.map(
34040
+ (targetInfo) => [
34041
+ targetInfo.targetId,
34042
+ {
34043
+ ...targetInfo,
34044
+ openerId: resolveAcyclicOpenerId(targetInfo.targetId, rawTargetInfoById)
34045
+ }
34046
+ ]
34047
+ )
34048
+ );
34049
+ const orderedTargetIds = [];
34050
+ const placed = /* @__PURE__ */ new Set();
34051
+ const placeTarget = (targetId) => {
34052
+ if (placed.has(targetId)) {
34053
+ return;
34054
+ }
34055
+ const targetInfo = targetInfoById.get(targetId);
34056
+ if (!targetInfo) {
34057
+ return;
34058
+ }
34059
+ const openerId = targetInfo.openerId;
34060
+ if (openerId === void 0) {
34061
+ orderedTargetIds.push(targetId);
34062
+ placed.add(targetId);
34063
+ return;
34064
+ }
34065
+ placeTarget(openerId);
34066
+ const openerIndex = orderedTargetIds.indexOf(openerId);
34067
+ const insertionIndex = openerIndex === -1 ? orderedTargetIds.length : findPopupInsertionIndex(orderedTargetIds, openerIndex, openerId, targetInfoById);
34068
+ orderedTargetIds.splice(insertionIndex, 0, targetId);
34069
+ placed.add(targetId);
34070
+ };
34071
+ for (const targetInfo of reversedPageInfos) {
34072
+ placeTarget(targetInfo.targetId);
34073
+ }
34074
+ return orderedTargetIds;
34075
+ }
34076
+ function resolveAcyclicOpenerId(targetId, targetInfoById) {
34077
+ const openerId = targetInfoById.get(targetId)?.openerId;
34078
+ if (openerId === void 0 || !targetInfoById.has(openerId)) {
34079
+ return void 0;
34080
+ }
34081
+ const visitedTargetIds = /* @__PURE__ */ new Set([targetId]);
34082
+ let currentTargetId = openerId;
34083
+ while (currentTargetId !== void 0) {
34084
+ if (visitedTargetIds.has(currentTargetId)) {
34085
+ return void 0;
34086
+ }
34087
+ visitedTargetIds.add(currentTargetId);
34088
+ currentTargetId = targetInfoById.get(currentTargetId)?.openerId;
34089
+ }
34090
+ return openerId;
34091
+ }
34092
+ function findPopupInsertionIndex(orderedTargetIds, openerIndex, openerTargetId, targetInfoById) {
34093
+ let index = openerIndex + 1;
34094
+ while (index < orderedTargetIds.length) {
34095
+ const candidateTargetId = orderedTargetIds[index];
34096
+ if (!candidateTargetId || !isDescendantTarget(candidateTargetId, openerTargetId, targetInfoById)) {
34097
+ break;
34098
+ }
34099
+ index += 1;
34100
+ }
34101
+ return index;
34102
+ }
34103
+ function isDescendantTarget(targetId, ancestorTargetId, targetInfoById) {
34104
+ const visitedTargetIds = /* @__PURE__ */ new Set();
34105
+ let currentTargetId = targetId;
34106
+ while (currentTargetId !== void 0) {
34107
+ if (visitedTargetIds.has(currentTargetId)) {
34108
+ return false;
34109
+ }
34110
+ visitedTargetIds.add(currentTargetId);
34111
+ const currentTargetInfo = targetInfoById.get(currentTargetId);
34112
+ const openerId = currentTargetInfo?.openerId;
34113
+ if (openerId === void 0) {
34114
+ return false;
34115
+ }
34116
+ if (openerId === ancestorTargetId) {
34117
+ return true;
34118
+ }
34119
+ currentTargetId = openerId;
34120
+ }
34121
+ return false;
34122
+ }
34123
+
33633
34124
  // src/local-view/tab-state-tracker.ts
33634
34125
  var ACTIVATION_INTENT_DISCOVERY_GRACE_MS = 2e3;
33635
34126
  var TabStateTracker = class {
@@ -33645,6 +34136,7 @@ var TabStateTracker = class {
33645
34136
  boundContextCleanup = null;
33646
34137
  constructor(deps) {
33647
34138
  this.deps = deps;
34139
+ this.lastActivePage = deps.initialActivePage ?? null;
33648
34140
  }
33649
34141
  start() {
33650
34142
  if (this.running) {
@@ -33769,7 +34261,7 @@ var TabStateTracker = class {
33769
34261
  this.updatePolling(pages.length);
33770
34262
  const preferredActivePage = this.lastActivePage ?? pages[0] ?? null;
33771
34263
  const pageStates = await Promise.all(
33772
- pages.map(async (page, index) => {
34264
+ pages.map(async (page, originalIndex) => {
33773
34265
  const metadata = await this.readPageMetadata(page, {
33774
34266
  refresh: args.refreshMetadata
33775
34267
  });
@@ -33779,7 +34271,7 @@ var TabStateTracker = class {
33779
34271
  };
33780
34272
  return {
33781
34273
  page,
33782
- index,
34274
+ originalIndex,
33783
34275
  targetId: metadata.targetId,
33784
34276
  url: page.url(),
33785
34277
  title: metadata.title,
@@ -33788,17 +34280,18 @@ var TabStateTracker = class {
33788
34280
  };
33789
34281
  })
33790
34282
  );
34283
+ const orderedPageStates = await this.orderPageStates(pageStates);
33791
34284
  const activePage = this.pickActivePage(
33792
- pageStates,
34285
+ orderedPageStates,
33793
34286
  this.lastActivePage,
33794
34287
  preferredActivePage,
33795
- this.resolveIntentPage(pageStates)
34288
+ this.resolveIntentPage(orderedPageStates)
33796
34289
  );
33797
34290
  if (activePage && activePage !== this.lastActivePage) {
33798
34291
  this.lastActivePage = activePage;
33799
34292
  this.deps.onActivePageChanged(activePage);
33800
34293
  }
33801
- const tabs = pageStates.map((state) => ({
34294
+ const tabs = orderedPageStates.map((state) => ({
33802
34295
  index: state.index,
33803
34296
  ...state.targetId === void 0 ? {} : { targetId: state.targetId },
33804
34297
  url: state.url,
@@ -33848,18 +34341,11 @@ var TabStateTracker = class {
33848
34341
  if (cached) {
33849
34342
  return cached;
33850
34343
  }
33851
- const cdp = await page.context().newCDPSession(page);
33852
- try {
33853
- const result = await cdp.send("Target.getTargetInfo");
33854
- const targetId = result?.targetInfo?.targetId;
33855
- if (typeof targetId === "string" && targetId.length > 0) {
33856
- this.targetIdByPage.set(page, targetId);
33857
- return targetId;
33858
- }
33859
- return null;
33860
- } finally {
33861
- await cdp.detach().catch(() => void 0);
34344
+ const targetId = await readPageTargetId(page);
34345
+ if (targetId) {
34346
+ this.targetIdByPage.set(page, targetId);
33862
34347
  }
34348
+ return targetId;
33863
34349
  }
33864
34350
  async readFocusState(page) {
33865
34351
  try {
@@ -33927,6 +34413,33 @@ var TabStateTracker = class {
33927
34413
  this.deps.runtimeState.clearPageActivationIntent(this.deps.sessionId, intent.targetId);
33928
34414
  return { page: matched.page };
33929
34415
  }
34416
+ async orderPageStates(pageStates) {
34417
+ if (pageStates.length < 2) {
34418
+ return pageStates.map(({ originalIndex: _originalIndex, ...state }, index) => ({
34419
+ ...state,
34420
+ index
34421
+ }));
34422
+ }
34423
+ const orderedTargetIds = await readBrowserPageTargetOrder(this.deps.browserContext);
34424
+ const rankByTargetId = new Map(orderedTargetIds.map((targetId, index) => [targetId, index]));
34425
+ return [...pageStates].sort((left, right) => {
34426
+ const leftRank = left.targetId === void 0 ? void 0 : rankByTargetId.get(left.targetId);
34427
+ const rightRank = right.targetId === void 0 ? void 0 : rankByTargetId.get(right.targetId);
34428
+ if (leftRank !== void 0 && rightRank !== void 0) {
34429
+ return leftRank - rightRank;
34430
+ }
34431
+ if (leftRank !== void 0) {
34432
+ return -1;
34433
+ }
34434
+ if (rightRank !== void 0) {
34435
+ return 1;
34436
+ }
34437
+ return left.originalIndex - right.originalIndex;
34438
+ }).map(({ originalIndex: _originalIndex, ...state }, index) => ({
34439
+ ...state,
34440
+ index
34441
+ }));
34442
+ }
33930
34443
  };
33931
34444
 
33932
34445
  // src/local-view/view-stream-capture-policy.ts
@@ -34290,6 +34803,7 @@ var SessionViewStreamProducer = class {
34290
34803
  sessionId: this.deps.sessionId,
34291
34804
  pollMs: TAB_STATE_POLL_MS,
34292
34805
  runtimeState: this.deps.runtimeState,
34806
+ initialActivePage: session.page,
34293
34807
  onActivePageChanged: (page) => {
34294
34808
  this.activePage = page;
34295
34809
  void this.queueBindToPage(page).catch(() => void 0);
@@ -34388,7 +34902,20 @@ var SessionViewStreamProducer = class {
34388
34902
  if (!context) {
34389
34903
  throw new Error("Connected browser did not expose a Chromium browser context.");
34390
34904
  }
34391
- const page = context.pages()[0] ?? await context.newPage();
34905
+ const existingPages = context.pages();
34906
+ if (existingPages.length === 0) {
34907
+ const page2 = await context.newPage();
34908
+ return {
34909
+ browser,
34910
+ context,
34911
+ page: page2
34912
+ };
34913
+ }
34914
+ const orderedPages = await orderPagesByBrowserTargetOrder(context, existingPages);
34915
+ const page = await resolvePersistedActivePage(orderedPages, {
34916
+ ...resolved.record.activePageUrl === void 0 ? {} : { activePageUrl: resolved.record.activePageUrl },
34917
+ ...resolved.record.activePageTitle === void 0 ? {} : { activePageTitle: resolved.record.activePageTitle }
34918
+ }) ?? orderedPages[0];
34392
34919
  return {
34393
34920
  browser,
34394
34921
  context,
@@ -34733,6 +35260,28 @@ async function disconnectPlaywrightChromiumBrowser2(browser) {
34733
35260
  const { disconnectPlaywrightChromiumBrowser: disconnect } = await import('@opensteer/engine-playwright');
34734
35261
  await disconnect(browser);
34735
35262
  }
35263
+ async function resolvePersistedActivePage(pages, input) {
35264
+ if (pages.length === 0) {
35265
+ return null;
35266
+ }
35267
+ if (input.activePageUrl === void 0 && input.activePageTitle === void 0) {
35268
+ return null;
35269
+ }
35270
+ const matchesByUrl = input.activePageUrl === void 0 ? pages : pages.filter((page) => page.url() === input.activePageUrl);
35271
+ if (matchesByUrl.length === 0) {
35272
+ return null;
35273
+ }
35274
+ if (input.activePageTitle === void 0) {
35275
+ return matchesByUrl[0] ?? null;
35276
+ }
35277
+ for (const page of matchesByUrl) {
35278
+ const title = await page.title().catch(() => "");
35279
+ if (title === input.activePageTitle) {
35280
+ return page;
35281
+ }
35282
+ }
35283
+ return matchesByUrl[0] ?? null;
35284
+ }
34736
35285
 
34737
35286
  // src/local-view/server.ts
34738
35287
  var DEFAULT_MAX_FPS = 12;
@@ -35105,13 +35654,19 @@ async function resolveWorkspaceSessionId(input) {
35105
35654
  workspace: input.workspace
35106
35655
  });
35107
35656
  const live = await readPersistedLocalBrowserSessionRecord(rootPath);
35108
- if (!live || !isProcessRunning(live.pid)) {
35657
+ if (!live) {
35109
35658
  return void 0;
35110
35659
  }
35111
- return buildLocalViewSessionId({
35660
+ if (getPersistedLocalBrowserSessionOwnership(live) === "attached") {
35661
+ if (!await isAttachedLocalBrowserSessionReachable(live)) {
35662
+ return void 0;
35663
+ }
35664
+ } else if (!isProcessRunning(live.pid)) {
35665
+ return void 0;
35666
+ }
35667
+ return buildLocalViewSessionIdForRecord({
35112
35668
  rootPath,
35113
- pid: live.pid,
35114
- startedAt: live.startedAt
35669
+ live
35115
35670
  });
35116
35671
  }
35117
35672
  function assertNoViewPreferenceFlag(parsed) {
@@ -35207,7 +35762,10 @@ async function main() {
35207
35762
  throw new CliError("unknown_command", `Unknown command: ${parsed.command.join(" ")}`);
35208
35763
  }
35209
35764
  if (parsed.options.workspace === void 0) {
35210
- throw new CliError("missing_workspace", 'Stateful commands require "--workspace <id>" or OPENSTEER_WORKSPACE.');
35765
+ throw new CliError(
35766
+ "missing_workspace",
35767
+ 'Stateful commands require "--workspace <id>" or OPENSTEER_WORKSPACE.'
35768
+ );
35211
35769
  }
35212
35770
  const { engineName, provider, runtimeProvider } = resolveCliRuntimeSelection(parsed);
35213
35771
  if (operation === "session.close") {
@@ -35254,7 +35812,10 @@ async function main() {
35254
35812
  }
35255
35813
  async function handleExecCommand(parsed) {
35256
35814
  if (parsed.options.workspace === void 0) {
35257
- throw new CliError("missing_workspace", 'exec requires "--workspace <id>" or OPENSTEER_WORKSPACE.');
35815
+ throw new CliError(
35816
+ "missing_workspace",
35817
+ 'exec requires "--workspace <id>" or OPENSTEER_WORKSPACE.'
35818
+ );
35258
35819
  }
35259
35820
  const expression = parsed.rest.join(" ");
35260
35821
  if (!expression) {
@@ -35397,7 +35958,10 @@ async function handleCloseCommand(parsed, engineName, providerMode, runtimeProvi
35397
35958
  }
35398
35959
  async function handleRecordCommandEntry(parsed) {
35399
35960
  if (parsed.options.workspace === void 0) {
35400
- throw new CliError("missing_workspace", 'record requires "--workspace <id>" or OPENSTEER_WORKSPACE.');
35961
+ throw new CliError(
35962
+ "missing_workspace",
35963
+ 'record requires "--workspace <id>" or OPENSTEER_WORKSPACE.'
35964
+ );
35401
35965
  }
35402
35966
  const url = parsed.options.url ?? parsed.rest[0];
35403
35967
  if (url === void 0) {
@@ -35437,7 +36001,10 @@ async function handleRecordCommandEntry(parsed) {
35437
36001
  return;
35438
36002
  }
35439
36003
  if (parsed.options.launch?.headless === true) {
35440
- throw new CliError("config_conflict", 'record requires a headed browser. Remove "--headless true".');
36004
+ throw new CliError(
36005
+ "config_conflict",
36006
+ 'record requires a headed browser. Remove "--headless true".'
36007
+ );
35441
36008
  }
35442
36009
  if (typeof recordBrowser === "object") {
35443
36010
  throw new CliError("config_conflict", 'record does not support browser.mode="attach".');
@@ -35509,7 +36076,10 @@ function resolveCliBootstrapAction(argv) {
35509
36076
  }
35510
36077
  function buildCliBrowserProfile(parsed) {
35511
36078
  if (parsed.options.cloudProfileReuseIfActive === true && parsed.options.cloudProfileId === void 0) {
35512
- throw new CliError("invalid_option", '"--cloud-profile-reuse-if-active" requires "--cloud-profile-id <id>".');
36079
+ throw new CliError(
36080
+ "invalid_option",
36081
+ '"--cloud-profile-reuse-if-active" requires "--cloud-profile-id <id>".'
36082
+ );
35513
36083
  }
35514
36084
  return parsed.options.cloudProfileId === void 0 ? void 0 : {
35515
36085
  profileId: parsed.options.cloudProfileId,