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
@@ -1,5 +1,5 @@
1
- import { objectSchema, stringSchema, literalSchema, JSON_SCHEMA_DRAFT_2020_12, integerSchema, enumSchema, oneOfSchema, numberSchema, arraySchema, recordSchema, defineSchema, opensteerCapabilitySetSchema, opensteerErrorSchema, OpensteerProtocolError, resolveFilesystemWorkspacePath, createFilesystemOpensteerWorkspace, DEFAULT_OPENSTEER_ENGINE, createOpensteerError, assertSupportedEngineOptions, normalizeObservabilityConfig, manifestToExternalBinaryLocation, normalizeObservationContext, OpensteerBrowserManager, isOpensteerProtocolError, toOpensteerError } from './chunk-3XBQRZZC.js';
2
- import { canonicalJsonString, toCanonicalJsonValue, readPersistedCloudSessionRecord, writePersistedSessionRecord, clearPersistedSessionRecord, resolveBrandUserDataDir, sha256Hex, getBrowserBrand, detectInstalledBrowserBrands, __require } from './chunk-T5P2QGZ3.js';
1
+ import { objectSchema, stringSchema, literalSchema, JSON_SCHEMA_DRAFT_2020_12, integerSchema, enumSchema, oneOfSchema, numberSchema, arraySchema, recordSchema, defineSchema, opensteerCapabilitySetSchema, opensteerErrorSchema, OpensteerProtocolError, resolveFilesystemWorkspacePath, createFilesystemOpensteerWorkspace, DEFAULT_OPENSTEER_ENGINE, createOpensteerError, assertSupportedEngineOptions, normalizeObservabilityConfig, manifestToExternalBinaryLocation, OpensteerBrowserManager, isOpensteerProtocolError, normalizeObservationContext, toOpensteerError } from './chunk-EXXRLPLI.js';
2
+ import { canonicalJsonString, toCanonicalJsonValue, readPersistedCloudSessionRecord, writePersistedSessionRecord, clearPersistedSessionRecord, resolveBrandUserDataDir, getBrowserBrand, detectInstalledBrowserBrands, __require, normalizeNonEmptyString, sha256Hex, readPersistedLocalBrowserSessionRecord } from './chunk-GKYBP3KD.js';
3
3
  import { selectAll } from 'css-select';
4
4
  import { createHash, randomUUID, pbkdf2Sync, createDecipheriv } from 'crypto';
5
5
  import { existsSync, readFileSync } from 'fs';
@@ -7,7 +7,7 @@ import path2, { join } from 'path';
7
7
  import { promisify } from 'util';
8
8
  import { gzip as gzip$1 } from 'zlib';
9
9
  import { execFile as execFile$1, spawn } from 'child_process';
10
- import { rm, mkdtemp, copyFile, writeFile, readFile } from 'fs/promises';
10
+ import { rm, mkdtemp, copyFile, readFile, writeFile } from 'fs/promises';
11
11
  import { tmpdir } from 'os';
12
12
  import { AsyncLocalStorage } from 'async_hooks';
13
13
  import sharp from 'sharp';
@@ -238,14 +238,14 @@ var DEFAULT_SETTLE_DELAYS = {
238
238
  snapshot: 0
239
239
  };
240
240
  var DOM_ACTION_VISUAL_STABILITY_PROFILES = {
241
- "dom.click": { settleMs: 750, scope: "visible-frames", timeoutMs: 7e3 },
242
- "dom.input": { settleMs: 750, scope: "visible-frames", timeoutMs: 7e3 },
243
- "dom.scroll": { settleMs: 600, scope: "visible-frames", timeoutMs: 7e3 },
241
+ "dom.click": { settleMs: 750, scope: "main-frame", timeoutMs: 7e3 },
242
+ "dom.input": { settleMs: 750, scope: "main-frame", timeoutMs: 7e3 },
243
+ "dom.scroll": { settleMs: 600, scope: "main-frame", timeoutMs: 7e3 },
244
244
  "dom.hover": { settleMs: 200, scope: "main-frame", timeoutMs: 2500 }
245
245
  };
246
246
  var DEFAULT_DOM_ACTION_VISUAL_STABILITY_PROFILE = {
247
247
  settleMs: 750,
248
- scope: "visible-frames",
248
+ scope: "main-frame",
249
249
  timeoutMs: 7e3
250
250
  };
251
251
  var NAVIGATION_VISUAL_STABILITY_PROFILE = {
@@ -269,6 +269,7 @@ var defaultDomActionSettleObserver = {
269
269
  pageRef: input.pageRef,
270
270
  timeoutMs: effectiveTimeout,
271
271
  settleMs: profile.settleMs,
272
+ ...input.observedMutationQuietMs === void 0 ? {} : { initialQuietMs: input.observedMutationQuietMs },
272
273
  scope: profile.scope
273
274
  });
274
275
  return true;
@@ -289,15 +290,20 @@ var defaultNavigationSettleObserver = {
289
290
  return false;
290
291
  }
291
292
  try {
292
- const startedAt = Date.now();
293
- await input.engine.waitForPostLoadQuiet({
294
- pageRef: input.pageRef,
295
- timeoutMs: effectiveTimeout,
296
- quietMs: DEFAULT_POST_LOAD_TRACKER_QUIET_WINDOW_MS,
297
- captureWindowMs: Math.min(NAVIGATION_POST_LOAD_CAPTURE_WINDOW_MS, effectiveTimeout),
298
- signal: input.signal
299
- });
300
- const visualTimeout = Math.max(0, effectiveTimeout - (Date.now() - startedAt));
293
+ let visualTimeout = effectiveTimeout;
294
+ let initialQuietMs = input.observedMutationQuietMs ?? 0;
295
+ if (!input.postLoadHandled) {
296
+ const startedAt = Date.now();
297
+ await input.engine.waitForPostLoadQuiet({
298
+ pageRef: input.pageRef,
299
+ timeoutMs: effectiveTimeout,
300
+ quietMs: DEFAULT_POST_LOAD_TRACKER_QUIET_WINDOW_MS,
301
+ captureWindowMs: Math.min(NAVIGATION_POST_LOAD_CAPTURE_WINDOW_MS, effectiveTimeout),
302
+ signal: input.signal
303
+ });
304
+ visualTimeout = Math.max(0, effectiveTimeout - (Date.now() - startedAt));
305
+ initialQuietMs = Math.max(initialQuietMs, DEFAULT_POST_LOAD_TRACKER_QUIET_WINDOW_MS);
306
+ }
301
307
  if (visualTimeout <= 0) {
302
308
  return true;
303
309
  }
@@ -305,6 +311,7 @@ var defaultNavigationSettleObserver = {
305
311
  pageRef: input.pageRef,
306
312
  timeoutMs: visualTimeout,
307
313
  settleMs: profile.settleMs,
314
+ ...initialQuietMs <= 0 ? {} : { initialQuietMs },
308
315
  scope: profile.scope
309
316
  });
310
317
  return true;
@@ -626,7 +633,7 @@ function isJsonValueEqual(expected, actual) {
626
633
 
627
634
  // ../protocol/src/version.ts
628
635
  var OPENSTEER_PROTOCOL_NAME = "opensteer";
629
- var OPENSTEER_PROTOCOL_COMPATIBILITY_REVISION = 2;
636
+ var OPENSTEER_PROTOCOL_COMPATIBILITY_REVISION = 3;
630
637
  var OPENSTEER_PROTOCOL_VERSION = `0.${OPENSTEER_PROTOCOL_COMPATIBILITY_REVISION}.0`;
631
638
  var OPENSTEER_PROTOCOL_REST_BASE_PATH = `/api/v${OPENSTEER_PROTOCOL_COMPATIBILITY_REVISION}`;
632
639
  objectSchema(
@@ -815,7 +822,7 @@ var visualViewportSchema = objectSchema(
815
822
  required: ["origin", "offsetWithinLayoutViewport", "size"]
816
823
  }
817
824
  );
818
- var viewportMetricsSchema = objectSchema(
825
+ objectSchema(
819
826
  {
820
827
  layoutViewport: layoutViewportSchema,
821
828
  visualViewport: visualViewportSchema,
@@ -850,6 +857,9 @@ var pageInfoSchema = objectSchema(
850
857
  {
851
858
  pageRef: pageRefSchema,
852
859
  sessionRef: sessionRefSchema,
860
+ targetId: stringSchema({
861
+ description: "Underlying browser target identifier when available."
862
+ }),
853
863
  openerPageRef: pageRefSchema,
854
864
  url: stringSchema({
855
865
  description: "Current main-frame URL."
@@ -2266,7 +2276,7 @@ var domSnapshotSchema = objectSchema(
2266
2276
  ]
2267
2277
  }
2268
2278
  );
2269
- var hitTestResultSchema = objectSchema(
2279
+ objectSchema(
2270
2280
  {
2271
2281
  inputPoint: pointSchema,
2272
2282
  inputCoordinateSpace: coordinateSpaceSchema,
@@ -3573,39 +3583,14 @@ var opensteerTargetInputSchema = oneOfSchema(
3573
3583
  title: "OpensteerTargetInput"
3574
3584
  }
3575
3585
  );
3576
- var opensteerResolvedTargetSchema = objectSchema(
3577
- {
3578
- pageRef: pageRefSchema,
3579
- frameRef: frameRefSchema,
3580
- documentRef: documentRefSchema,
3581
- documentEpoch: documentEpochSchema,
3582
- nodeRef: nodeRefSchema,
3583
- tagName: stringSchema(),
3584
- pathHint: stringSchema(),
3585
- persist: stringSchema(),
3586
- selectorUsed: stringSchema()
3587
- },
3588
- {
3589
- title: "OpensteerResolvedTarget",
3590
- required: [
3591
- "pageRef",
3592
- "frameRef",
3593
- "documentRef",
3594
- "documentEpoch",
3595
- "nodeRef",
3596
- "tagName",
3597
- "pathHint"
3598
- ]
3599
- }
3600
- );
3601
3586
  var opensteerActionResultSchema = objectSchema(
3602
3587
  {
3603
- target: opensteerResolvedTargetSchema,
3604
- point: pointSchema
3588
+ tagName: stringSchema({ minLength: 1 }),
3589
+ persist: stringSchema({ minLength: 1 })
3605
3590
  },
3606
3591
  {
3607
3592
  title: "OpensteerActionResult",
3608
- required: ["target"]
3593
+ required: ["tagName"]
3609
3594
  }
3610
3595
  );
3611
3596
  var opensteerSnapshotCounterSchema = objectSchema(
@@ -3651,16 +3636,14 @@ var opensteerSnapshotCounterSchema = objectSchema(
3651
3636
  ]
3652
3637
  }
3653
3638
  );
3654
- var opensteerSessionStateSchema = objectSchema(
3639
+ var opensteerNavigationSummarySchema = objectSchema(
3655
3640
  {
3656
- sessionRef: sessionRefSchema,
3657
- pageRef: pageRefSchema,
3658
3641
  url: stringSchema(),
3659
3642
  title: stringSchema()
3660
3643
  },
3661
3644
  {
3662
- title: "OpensteerSessionState",
3663
- required: ["sessionRef", "pageRef", "url", "title"]
3645
+ title: "OpensteerNavigationSummary",
3646
+ required: ["url", "title"]
3664
3647
  }
3665
3648
  );
3666
3649
  var opensteerOpenInputSchema = objectSchema(
@@ -3728,6 +3711,17 @@ var opensteerPageCloseOutputSchema = objectSchema(
3728
3711
  required: ["closedPageRef", "pages"]
3729
3712
  }
3730
3713
  );
3714
+ var opensteerPageNewOutputSchema = objectSchema(
3715
+ {
3716
+ pageRef: pageRefSchema,
3717
+ url: stringSchema(),
3718
+ title: stringSchema()
3719
+ },
3720
+ {
3721
+ title: "OpensteerPageNewOutput",
3722
+ required: ["pageRef", "url", "title"]
3723
+ }
3724
+ );
3731
3725
  var opensteerPageGotoInputSchema = objectSchema(
3732
3726
  {
3733
3727
  url: stringSchema(),
@@ -4114,72 +4108,28 @@ var opensteerComputerExecuteInputSchema = objectSchema(
4114
4108
  required: ["action"]
4115
4109
  }
4116
4110
  );
4117
- var opensteerComputerTracePointSchema = objectSchema(
4118
- {
4119
- role: enumSchema(["point", "start", "end"]),
4120
- point: pointSchema,
4121
- hitTest: hitTestResultSchema,
4122
- target: opensteerResolvedTargetSchema
4123
- },
4124
- {
4125
- title: "OpensteerComputerTracePoint",
4126
- required: ["role", "point"]
4127
- }
4128
- );
4129
- var opensteerComputerTraceEnrichmentSchema = objectSchema(
4130
- {
4131
- points: arraySchema(opensteerComputerTracePointSchema)
4132
- },
4133
- {
4134
- title: "OpensteerComputerTraceEnrichment",
4135
- required: ["points"]
4136
- }
4137
- );
4138
- var opensteerComputerExecuteTimingSchema = objectSchema(
4139
- {
4140
- actionMs: integerSchema({ minimum: 0 }),
4141
- waitMs: integerSchema({ minimum: 0 }),
4142
- totalMs: integerSchema({ minimum: 0 })
4143
- },
4144
- {
4145
- title: "OpensteerComputerExecuteTiming",
4146
- required: ["actionMs", "waitMs", "totalMs"]
4147
- }
4148
- );
4149
- var opensteerComputerDisplayScaleSchema = objectSchema(
4111
+ var opensteerScreenshotSummarySchema = objectSchema(
4150
4112
  {
4151
- x: numberSchema({ exclusiveMinimum: 0 }),
4152
- y: numberSchema({ exclusiveMinimum: 0 })
4113
+ payload: externalBinaryLocationSchema,
4114
+ format: screenshotFormatSchema,
4115
+ size: sizeSchema,
4116
+ coordinateSpace: coordinateSpaceSchema,
4117
+ clip: rectSchema
4153
4118
  },
4154
4119
  {
4155
- title: "OpensteerComputerDisplayScale",
4156
- required: ["x", "y"]
4120
+ title: "OpensteerScreenshotSummary",
4121
+ required: ["payload", "format", "size", "coordinateSpace"]
4157
4122
  }
4158
4123
  );
4159
4124
  var opensteerComputerExecuteOutputSchema = objectSchema(
4160
4125
  {
4161
- action: opensteerComputerActionSchema,
4162
- pageRef: pageRefSchema,
4163
- screenshot: screenshotArtifactSchema,
4164
- displayViewport: viewportMetricsSchema,
4165
- nativeViewport: viewportMetricsSchema,
4166
- displayScale: opensteerComputerDisplayScaleSchema,
4167
- events: arraySchema(opensteerEventSchema),
4168
- timing: opensteerComputerExecuteTimingSchema,
4169
- trace: opensteerComputerTraceEnrichmentSchema
4126
+ url: stringSchema(),
4127
+ title: stringSchema(),
4128
+ screenshot: opensteerScreenshotSummarySchema
4170
4129
  },
4171
4130
  {
4172
4131
  title: "OpensteerComputerExecuteOutput",
4173
- required: [
4174
- "action",
4175
- "pageRef",
4176
- "screenshot",
4177
- "displayViewport",
4178
- "nativeViewport",
4179
- "displayScale",
4180
- "events",
4181
- "timing"
4182
- ]
4132
+ required: ["url", "title", "screenshot"]
4183
4133
  }
4184
4134
  );
4185
4135
  function assertValidSemanticOperationInput(name, input) {
@@ -4205,7 +4155,7 @@ var opensteerSemanticOperationSpecificationsBase = [
4205
4155
  name: "session.open",
4206
4156
  description: "Open or resume the current Opensteer session and primary page.",
4207
4157
  inputSchema: opensteerOpenInputSchema,
4208
- outputSchema: opensteerSessionStateSchema,
4158
+ outputSchema: opensteerNavigationSummarySchema,
4209
4159
  requiredCapabilities: ["sessions.manage", "pages.manage"],
4210
4160
  resolveRequiredCapabilities: (input) => input.url === void 0 ? ["sessions.manage", "pages.manage"] : ["sessions.manage", "pages.manage", "pages.navigate"]
4211
4161
  }),
@@ -4220,7 +4170,7 @@ var opensteerSemanticOperationSpecificationsBase = [
4220
4170
  name: "page.new",
4221
4171
  description: "Create and optionally navigate a new top-level page in the current session.",
4222
4172
  inputSchema: opensteerPageNewInputSchema,
4223
- outputSchema: opensteerSessionStateSchema,
4173
+ outputSchema: opensteerPageNewOutputSchema,
4224
4174
  requiredCapabilities: ["pages.manage"],
4225
4175
  resolveRequiredCapabilities: (input) => input.url === void 0 ? ["pages.manage"] : ["pages.manage", "pages.navigate"]
4226
4176
  }),
@@ -4228,7 +4178,7 @@ var opensteerSemanticOperationSpecificationsBase = [
4228
4178
  name: "page.activate",
4229
4179
  description: "Activate an existing top-level page in the current session.",
4230
4180
  inputSchema: opensteerPageActivateInputSchema,
4231
- outputSchema: opensteerSessionStateSchema,
4181
+ outputSchema: opensteerNavigationSummarySchema,
4232
4182
  requiredCapabilities: ["pages.manage", "inspect.pages"]
4233
4183
  }),
4234
4184
  defineSemanticOperationSpec({
@@ -4242,7 +4192,7 @@ var opensteerSemanticOperationSpecificationsBase = [
4242
4192
  name: "page.goto",
4243
4193
  description: "Navigate the current Opensteer page to a new URL.",
4244
4194
  inputSchema: opensteerPageGotoInputSchema,
4245
- outputSchema: opensteerSessionStateSchema,
4195
+ outputSchema: opensteerNavigationSummarySchema,
4246
4196
  requiredCapabilities: ["pages.navigate"]
4247
4197
  }),
4248
4198
  defineSemanticOperationSpec({
@@ -5006,7 +4956,9 @@ function resolveExtractedValueInContext(normalizedValue, options) {
5006
4956
  function stripPositionClauses(nodes) {
5007
4957
  return (nodes || []).map((node) => ({
5008
4958
  ...node,
5009
- match: (node.match || []).filter((clause) => clause.kind !== "position" && clause.kind !== "text")
4959
+ match: (node.match || []).filter(
4960
+ (clause) => clause.kind !== "position" && clause.kind !== "text"
4961
+ )
5010
4962
  }));
5011
4963
  }
5012
4964
  function dedupeSelectors(selectors) {
@@ -6498,7 +6450,7 @@ var DomActionExecutor = class {
6498
6450
  ...snapshot === void 0 ? {} : { snapshot },
6499
6451
  signal: timeout.signal,
6500
6452
  remainingMs: () => timeout.remainingMs(),
6501
- policySettle: async (targetPageRef, trigger) => {
6453
+ policySettle: async (targetPageRef, trigger, boundary2) => {
6502
6454
  try {
6503
6455
  await settleWithPolicy(this.options.policy.settle, {
6504
6456
  operation,
@@ -6506,7 +6458,9 @@ var DomActionExecutor = class {
6506
6458
  engine: this.options.engine,
6507
6459
  pageRef: targetPageRef,
6508
6460
  signal: timeout.signal,
6509
- remainingMs: timeout.remainingMs()
6461
+ remainingMs: timeout.remainingMs(),
6462
+ ...boundary2?.observedMutationQuietMs === void 0 ? {} : { observedMutationQuietMs: boundary2.observedMutationQuietMs },
6463
+ ...boundary2?.postLoadHandled === true ? { postLoadHandled: true } : {}
6510
6464
  });
6511
6465
  } catch (error) {
6512
6466
  if (snapshot !== void 0 && isSoftSettleTimeoutError(error, timeout.signal)) {
@@ -7274,7 +7228,9 @@ var DefaultDomRuntime = class {
7274
7228
  `Unable to resolve structural anchor "${buildPathSelectorHint(anchor)}" in the current session`
7275
7229
  );
7276
7230
  }
7277
- const replayPath = await this.tryBuildPathFromNode(context.snapshot, target.node, { enableTextMatch: true });
7231
+ const replayPath = await this.tryBuildPathFromNode(context.snapshot, target.node, {
7232
+ enableTextMatch: true
7233
+ });
7278
7234
  return this.createResolvedTarget(source, context.snapshot, target.node, anchor, {
7279
7235
  ...persist === void 0 ? {} : { persist },
7280
7236
  ...replayPath === void 0 ? {} : { replayPath }
@@ -9240,7 +9196,7 @@ function normalizeTemplateField(value) {
9240
9196
  "Extraction field descriptors must specify exactly one of c/element, selector, or source."
9241
9197
  );
9242
9198
  }
9243
- const attribute = raw.attr !== void 0 ? normalizeNonEmptyString("attr", raw.attr) : raw.attribute === void 0 ? void 0 : normalizeNonEmptyString("attribute", raw.attribute);
9199
+ const attribute = raw.attr !== void 0 ? normalizeNonEmptyString2("attr", raw.attr) : raw.attribute === void 0 ? void 0 : normalizeNonEmptyString2("attribute", raw.attribute);
9244
9200
  if (hasSource) {
9245
9201
  if (raw.source !== "current_url") {
9246
9202
  throw new Error(`Unsupported extraction source "${String(raw.source)}".`);
@@ -9251,7 +9207,7 @@ function normalizeTemplateField(value) {
9251
9207
  }
9252
9208
  if (hasSelector) {
9253
9209
  return {
9254
- selector: normalizeNonEmptyString("selector", raw.selector),
9210
+ selector: normalizeNonEmptyString2("selector", raw.selector),
9255
9211
  ...attribute === void 0 ? {} : { attribute }
9256
9212
  };
9257
9213
  }
@@ -9459,7 +9415,7 @@ var MemoryOpensteerExtractionDescriptorStore = class {
9459
9415
  return record;
9460
9416
  }
9461
9417
  };
9462
- function normalizeNonEmptyString(name, value) {
9418
+ function normalizeNonEmptyString2(name, value) {
9463
9419
  const normalized = String(value ?? "").trim();
9464
9420
  if (normalized.length === 0) {
9465
9421
  throw new Error(`${name} must not be empty`);
@@ -10270,11 +10226,11 @@ function delay(ms) {
10270
10226
  function wrapCloudFetchError(error, input) {
10271
10227
  if (!(error instanceof Error)) {
10272
10228
  return new Error(
10273
- `Failed to reach Opensteer cloud endpoint ${input.method} ${input.url}. Check OPENSTEER_BASE_URL and network reachability from this environment.`
10229
+ `Failed to reach Opensteer cloud endpoint ${input.method} ${input.url}. Check the configured Opensteer cloud base URL and network reachability from this environment.`
10274
10230
  );
10275
10231
  }
10276
10232
  const wrapped = new Error(
10277
- `Failed to reach Opensteer cloud endpoint ${input.method} ${input.url}. Check OPENSTEER_BASE_URL and network reachability from this environment.`,
10233
+ `Failed to reach Opensteer cloud endpoint ${input.method} ${input.url}. Check the configured Opensteer cloud base URL and network reachability from this environment.`,
10278
10234
  {
10279
10235
  cause: error
10280
10236
  }
@@ -10314,6 +10270,7 @@ function asCloudErrorPayload(value) {
10314
10270
  }
10315
10271
 
10316
10272
  // src/cloud/config.ts
10273
+ var DEFAULT_OPENSTEER_CLOUD_BASE_URL = "https://api.opensteer.com";
10317
10274
  function resolveCloudConfig(input = {}) {
10318
10275
  const provider = resolveOpensteerProvider({
10319
10276
  ...input.provider === void 0 ? {} : { provider: input.provider },
@@ -10323,30 +10280,34 @@ function resolveCloudConfig(input = {}) {
10323
10280
  return void 0;
10324
10281
  }
10325
10282
  const cloudProvider = input.provider?.mode === "cloud" ? input.provider : void 0;
10326
- const apiKey = cloudProvider?.apiKey ?? input.environment?.OPENSTEER_API_KEY;
10327
- if (!apiKey || apiKey.trim().length === 0) {
10283
+ const apiKey = normalizeOptionalCloudConfigValue(cloudProvider?.apiKey) ?? normalizeOptionalCloudConfigValue(input.environment?.OPENSTEER_API_KEY);
10284
+ if (apiKey === void 0) {
10328
10285
  throw new Error("provider=cloud requires OPENSTEER_API_KEY or provider.apiKey.");
10329
10286
  }
10330
- const baseUrl = cloudProvider?.baseUrl ?? input.environment?.OPENSTEER_BASE_URL;
10331
- if (!baseUrl || baseUrl.trim().length === 0) {
10332
- throw new Error("provider=cloud requires OPENSTEER_BASE_URL or provider.baseUrl.");
10333
- }
10334
- const appBaseUrl = cloudProvider?.appBaseUrl ?? input.environment?.OPENSTEER_CLOUD_APP_BASE_URL;
10287
+ const baseUrl = normalizeOptionalCloudConfigValue(cloudProvider?.baseUrl) ?? normalizeOptionalCloudConfigValue(input.environment?.OPENSTEER_BASE_URL) ?? DEFAULT_OPENSTEER_CLOUD_BASE_URL;
10288
+ const appBaseUrl = normalizeOptionalCloudConfigValue(cloudProvider?.appBaseUrl) ?? normalizeOptionalCloudConfigValue(input.environment?.OPENSTEER_CLOUD_APP_BASE_URL);
10335
10289
  return {
10336
- apiKey: apiKey.trim(),
10337
- baseUrl: baseUrl.trim().replace(/\/+$/, ""),
10338
- ...appBaseUrl === void 0 || appBaseUrl.trim().length === 0 ? {} : { appBaseUrl: appBaseUrl.trim().replace(/\/+$/, "") },
10290
+ apiKey,
10291
+ baseUrl,
10292
+ ...appBaseUrl === void 0 ? {} : { appBaseUrl },
10339
10293
  ...cloudProvider?.browserProfile === void 0 ? {} : { browserProfile: cloudProvider.browserProfile }
10340
10294
  };
10341
10295
  }
10342
10296
  function requireCloudAppBaseUrl(cloudConfig) {
10343
- const appBaseUrl = cloudConfig.appBaseUrl;
10344
- if (!appBaseUrl || appBaseUrl.trim().length === 0) {
10297
+ const appBaseUrl = normalizeOptionalCloudConfigValue(cloudConfig.appBaseUrl);
10298
+ if (appBaseUrl === void 0) {
10345
10299
  throw new Error(
10346
10300
  'record with provider=cloud requires OPENSTEER_CLOUD_APP_BASE_URL or "--cloud-app-base-url".'
10347
10301
  );
10348
10302
  }
10349
- return appBaseUrl.trim().replace(/\/+$/, "");
10303
+ return appBaseUrl;
10304
+ }
10305
+ function normalizeOptionalCloudConfigValue(value) {
10306
+ if (typeof value !== "string") {
10307
+ return void 0;
10308
+ }
10309
+ const normalized = value.trim().replace(/\/+$/, "");
10310
+ return normalized.length === 0 ? void 0 : normalized;
10350
10311
  }
10351
10312
 
10352
10313
  // ../runtime-core/src/sdk/semantic-dispatch.ts
@@ -10487,7 +10448,7 @@ async function dispatchSemanticOperation(runtime, operation, input, options = {}
10487
10448
 
10488
10449
  // ../runtime-core/package.json
10489
10450
  var package_default = {
10490
- version: "0.2.5"};
10451
+ version: "0.2.7"};
10491
10452
 
10492
10453
  // ../runtime-core/src/version.ts
10493
10454
  var OPENSTEER_RUNTIME_CORE_VERSION = package_default.version;
@@ -10758,12 +10719,16 @@ function toOpensteerResolvedTarget(target) {
10758
10719
  documentRef: target.documentRef,
10759
10720
  documentEpoch: target.documentEpoch,
10760
10721
  nodeRef: target.nodeRef,
10761
- tagName: target.node.nodeName.toUpperCase(),
10722
+ tagName: toOpensteerTagName(target.node.nodeName),
10762
10723
  pathHint: buildPathSelectorHint(target.replayPath ?? target.anchor),
10763
10724
  ...target.persist === void 0 ? {} : { persist: target.persist },
10764
10725
  ...target.selectorUsed === void 0 ? {} : { selectorUsed: target.selectorUsed }
10765
10726
  };
10766
10727
  }
10728
+ function toOpensteerTagName(nodeName) {
10729
+ const tagName = String(nodeName).trim().toLowerCase();
10730
+ return tagName.length === 0 ? "element" : tagName;
10731
+ }
10767
10732
 
10768
10733
  // ../runtime-core/src/runtimes/computer-use/runtime.ts
10769
10734
  function createComputerUseRuntime(options) {
@@ -10797,7 +10762,7 @@ var DefaultComputerUseRuntime = class {
10797
10762
  screenshot,
10798
10763
  signal: input.timeout.signal,
10799
10764
  remainingMs: () => input.timeout.remainingMs(),
10800
- policySettle: async (pageRef, trigger) => {
10765
+ policySettle: async (pageRef, trigger, boundary) => {
10801
10766
  try {
10802
10767
  await settleWithPolicy(this.options.policy.settle, {
10803
10768
  operation: "computer.execute",
@@ -10805,7 +10770,9 @@ var DefaultComputerUseRuntime = class {
10805
10770
  engine: this.options.engine,
10806
10771
  pageRef,
10807
10772
  signal: input.timeout.signal,
10808
- remainingMs: input.timeout.remainingMs()
10773
+ remainingMs: input.timeout.remainingMs(),
10774
+ ...boundary?.observedMutationQuietMs === void 0 ? {} : { observedMutationQuietMs: boundary.observedMutationQuietMs },
10775
+ ...boundary?.postLoadHandled === true ? { postLoadHandled: true } : {}
10809
10776
  });
10810
10777
  } catch (error) {
10811
10778
  if (pageRef === input.pageRef && isSoftSettleTimeoutError(error, input.timeout.signal)) {
@@ -11983,28 +11950,24 @@ function restoreBoundedAttr(el, attr, value) {
11983
11950
  }
11984
11951
  setBoundedAttr(el, attr, value);
11985
11952
  }
11986
- function deduplicateImages(html) {
11953
+ function deduplicateImagesInDom($) {
11987
11954
  const seen = /* @__PURE__ */ new Set();
11988
- return html.replace(/<img\b([^>]*)>/gi, (full, attrContent) => {
11989
- if (/\bc\s*=/.test(attrContent)) {
11990
- return full;
11991
- }
11992
- const srcMatch = attrContent.match(/\bsrc\s*=\s*(["']?)(.*?)\1/);
11993
- const srcsetMatch = attrContent.match(/\bsrcset\s*=\s*(["'])(.*?)\1/);
11994
- let src = null;
11995
- if (srcMatch && srcMatch[2]) {
11996
- src = srcMatch[2].trim();
11997
- } else if (srcsetMatch && srcsetMatch[2]) {
11998
- src = srcsetMatch[2].split(",")[0]?.trim().split(" ")[0] ?? null;
11955
+ $("img").each(function deduplicateDomImage() {
11956
+ const el = $(this);
11957
+ if (el.attr("c") !== void 0) {
11958
+ return;
11999
11959
  }
11960
+ const srcValue = el.attr("src")?.trim();
11961
+ const srcsetValue = el.attr("srcset");
11962
+ const src = srcValue && srcValue.length > 0 ? srcValue : srcsetValue?.split(",")[0]?.trim().split(/\s+/u)[0];
12000
11963
  if (!src) {
12001
- return full;
11964
+ return;
12002
11965
  }
12003
11966
  if (seen.has(src)) {
12004
- return "";
11967
+ el.remove();
11968
+ return;
12005
11969
  }
12006
11970
  seen.add(src);
12007
- return full;
12008
11971
  });
12009
11972
  }
12010
11973
  function hasAttribute2(node, attr) {
@@ -12062,23 +12025,6 @@ function isPreservedImageElement(node) {
12062
12025
  function getElementsInReverseDocumentOrder($) {
12063
12026
  return $.root().find("*").toArray().reverse().filter((node) => node.type === "tag");
12064
12027
  }
12065
- function getNodeDepth(node) {
12066
- let depth = 0;
12067
- let current = node.parent;
12068
- while (current) {
12069
- depth++;
12070
- current = current.parent;
12071
- }
12072
- return depth;
12073
- }
12074
- function getElementsByDepthDescending($) {
12075
- const elements = $.root().find("*").toArray().filter((node) => node.type === "tag");
12076
- const depths = /* @__PURE__ */ new Map();
12077
- for (const el of elements) {
12078
- depths.set(el, getNodeDepth(el));
12079
- }
12080
- return elements.sort((a, b) => (depths.get(b) ?? 0) - (depths.get(a) ?? 0));
12081
- }
12082
12028
  function flattenExtractionTree($) {
12083
12029
  for (const node of getElementsInReverseDocumentOrder($)) {
12084
12030
  const el = $(node);
@@ -12096,19 +12042,6 @@ function flattenExtractionTree($) {
12096
12042
  el.replaceWith(el.contents());
12097
12043
  }
12098
12044
  }
12099
- function hasMarkedAncestor(el, attr) {
12100
- let current = el[0]?.parent;
12101
- while (current) {
12102
- if (!isElementLikeNode(current)) {
12103
- return false;
12104
- }
12105
- if (current.attribs?.[attr] !== void 0) {
12106
- return true;
12107
- }
12108
- current = current.parent;
12109
- }
12110
- return false;
12111
- }
12112
12045
  function isIndicatorImage(node) {
12113
12046
  return (node?.tagName || "").toLowerCase() === "img" && (hasAttribute2(node, "alt") || hasAttribute2(node, "src") || hasAttribute2(node, "srcset"));
12114
12047
  }
@@ -12226,7 +12159,7 @@ function serializeForExtraction($, root) {
12226
12159
  traverse(root, 0);
12227
12160
  return lines.map((l) => l.trim()).filter((l) => l.length > 0).join("");
12228
12161
  }
12229
- function isClickable($, el, context) {
12162
+ function isClickable(el, context) {
12230
12163
  if (context.hasPreMarked) {
12231
12164
  return el.attr(OPENSTEER_INTERACTIVE_ATTR) !== void 0;
12232
12165
  }
@@ -12260,21 +12193,17 @@ function isClickable($, el, context) {
12260
12193
  }
12261
12194
  return false;
12262
12195
  }
12263
- function cleanForExtraction(html) {
12196
+ function prepareExtractionSnapshotDom(html) {
12264
12197
  if (!html.trim()) {
12265
- return "";
12198
+ return void 0;
12266
12199
  }
12267
12200
  const $ = cheerio.load(html, { xmlMode: false });
12268
12201
  removeNoise($);
12269
12202
  removeComments($);
12270
12203
  markInlineSelfHiddenFallback($);
12271
12204
  pruneSelfHiddenNodes($);
12272
- const $clean = cheerio.load(
12273
- $.html().replace(/\n{2,}/g, "\n").trim(),
12274
- { xmlMode: false }
12275
- );
12276
- $clean("*").each(function stripAndRestoreExtractionAttrs() {
12277
- const el = $clean(this);
12205
+ $("*").each(function stripAndRestoreExtractionAttrs() {
12206
+ const el = $(this);
12278
12207
  const node = el[0];
12279
12208
  if (!node) {
12280
12209
  return;
@@ -12315,16 +12244,20 @@ function cleanForExtraction(html) {
12315
12244
  restoreBoundedAttr(el, "href", hrefValue);
12316
12245
  }
12317
12246
  });
12318
- flattenExtractionTree($clean);
12319
- const root = $clean.root()[0];
12247
+ flattenExtractionTree($);
12248
+ deduplicateImagesInDom($);
12249
+ return $;
12250
+ }
12251
+ function serializePreparedExtractionSnapshot($) {
12252
+ const root = $.root()[0];
12320
12253
  if (root === void 0) {
12321
12254
  return "";
12322
12255
  }
12323
- return deduplicateImages(serializeForExtraction($clean, root));
12256
+ return serializeForExtraction($, root);
12324
12257
  }
12325
- function cleanForAction(html) {
12258
+ function prepareActionSnapshotDom(html) {
12326
12259
  if (!html.trim()) {
12327
- return "";
12260
+ return void 0;
12328
12261
  }
12329
12262
  const $ = cheerio.load(html, { xmlMode: false });
12330
12263
  removeNoise($);
@@ -12333,13 +12266,12 @@ function cleanForAction(html) {
12333
12266
  pruneSelfHiddenNodes($);
12334
12267
  const clickableMark = "data-clickable-marker";
12335
12268
  const indicatorMark = "data-keep-indicator";
12336
- const branchMark = "data-keep-branch";
12337
12269
  const context = {
12338
12270
  hasPreMarked: $(`[${OPENSTEER_INTERACTIVE_ATTR}]`).length > 0
12339
12271
  };
12340
12272
  $("*").each(function markClickables() {
12341
12273
  const el = $(this);
12342
- if (isClickable($, el, context)) {
12274
+ if (isClickable(el, context)) {
12343
12275
  el.attr(clickableMark, "1");
12344
12276
  }
12345
12277
  });
@@ -12369,25 +12301,7 @@ function cleanForAction(html) {
12369
12301
  el.remove();
12370
12302
  }
12371
12303
  });
12372
- $(`[${clickableMark}], [${indicatorMark}]`).each(function markBranches() {
12373
- let current = $(this).parent();
12374
- while (current.length > 0) {
12375
- const node = current[0];
12376
- if (!node || node.type !== "tag") {
12377
- break;
12378
- }
12379
- const ancestor = current;
12380
- const tag = (node.tagName || "").toLowerCase();
12381
- if (ROOT_TAGS.has(tag) || ancestor.attr(clickableMark) !== void 0) {
12382
- break;
12383
- }
12384
- if (!isBoundaryTag(tag)) {
12385
- ancestor.attr(branchMark, "1");
12386
- }
12387
- current = ancestor.parent();
12388
- }
12389
- });
12390
- for (const node of getElementsByDepthDescending($)) {
12304
+ for (const node of getElementsInReverseDocumentOrder($)) {
12391
12305
  const el = $(node);
12392
12306
  const tag = (node.tagName || "").toLowerCase();
12393
12307
  if (ROOT_TAGS.has(tag) || isBoundaryTag(tag)) {
@@ -12396,17 +12310,7 @@ function cleanForAction(html) {
12396
12310
  if (el.attr(clickableMark) !== void 0 || el.attr(indicatorMark) !== void 0) {
12397
12311
  continue;
12398
12312
  }
12399
- const insideClickable = hasMarkedAncestor(el, clickableMark);
12400
- const preserveBranch = el.attr(branchMark) !== void 0;
12401
12313
  const hasContent = hasElementChildren(node) || hasDirectText(node);
12402
- if (insideClickable || preserveBranch) {
12403
- if (!hasContent) {
12404
- el.remove();
12405
- } else {
12406
- unwrapActionNode($, el);
12407
- }
12408
- continue;
12409
- }
12410
12314
  if (!hasContent) {
12411
12315
  el.remove();
12412
12316
  continue;
@@ -12507,13 +12411,20 @@ function cleanForAction(html) {
12507
12411
  }
12508
12412
  el.removeAttr(clickableMark);
12509
12413
  el.removeAttr(indicatorMark);
12510
- el.removeAttr(branchMark);
12511
12414
  el.removeAttr(OPENSTEER_INTERACTIVE_ATTR);
12512
12415
  el.removeAttr(OPENSTEER_HIDDEN_ATTR);
12513
12416
  el.removeAttr(OPENSTEER_SCROLLABLE_ATTR);
12514
12417
  el.removeAttr(OPENSTEER_SELF_HIDDEN_ATTR);
12515
12418
  });
12516
- return compactHtml(deduplicateImages($.html()));
12419
+ deduplicateImagesInDom($);
12420
+ return $;
12421
+ }
12422
+ function serializePreparedActionSnapshot($) {
12423
+ const normalized = compactHtml($.html());
12424
+ if (normalized.length === 0) {
12425
+ return "";
12426
+ }
12427
+ return cheerio.load(normalized, { xmlMode: false }).html();
12517
12428
  }
12518
12429
  var VOID_TAGS2 = /* @__PURE__ */ new Set([
12519
12430
  "area",
@@ -12736,27 +12647,32 @@ async function markLiveSnapshotSemantics(options) {
12736
12647
  const frames = await options.engine.listFrames({
12737
12648
  pageRef: options.pageRef
12738
12649
  });
12739
- for (const frame of frames) {
12740
- await evaluateFrameBestEffort(
12741
- options.engine,
12742
- frame.frameRef,
12743
- MARK_SNAPSHOT_SEMANTICS_SCRIPT,
12744
- SNAPSHOT_SEMANTIC_ARGS
12745
- );
12746
- }
12747
- return async () => {
12748
- for (const frame of frames) {
12749
- await evaluateFrameBestEffort(
12650
+ await Promise.all(
12651
+ frames.map(
12652
+ (frame) => evaluateFrameBestEffort(
12750
12653
  options.engine,
12751
12654
  frame.frameRef,
12752
- CLEAR_SNAPSHOT_SEMANTICS_SCRIPT,
12753
- CLEAR_SNAPSHOT_SEMANTIC_ARGS
12754
- );
12755
- }
12655
+ MARK_SNAPSHOT_SEMANTICS_SCRIPT,
12656
+ SNAPSHOT_SEMANTIC_ARGS
12657
+ )
12658
+ )
12659
+ );
12660
+ return async () => {
12661
+ await Promise.all(
12662
+ frames.map(
12663
+ (frame) => evaluateFrameBestEffort(
12664
+ options.engine,
12665
+ frame.frameRef,
12666
+ CLEAR_SNAPSHOT_SEMANTICS_SCRIPT,
12667
+ CLEAR_SNAPSHOT_SEMANTIC_ARGS
12668
+ )
12669
+ )
12670
+ );
12756
12671
  };
12757
12672
  }
12758
12673
 
12759
12674
  // ../runtime-core/src/sdk/snapshot/compiler.ts
12675
+ var EXTRACTION_SKIPPED_COUNTER_TAGS = /* @__PURE__ */ new Set(["html", "head", "body"]);
12760
12676
  var INTERNAL_SNAPSHOT_ATTRIBUTE_NAMES = /* @__PURE__ */ new Set([
12761
12677
  "c",
12762
12678
  OPENSTEER_BOUNDARY_ATTR,
@@ -12771,7 +12687,7 @@ var INTERNAL_SNAPSHOT_ATTRIBUTE_NAMES = /* @__PURE__ */ new Set([
12771
12687
  var MAX_LIVE_COUNTER_SYNC_ATTEMPTS = 4;
12772
12688
  var CLEAR_LIVE_COUNTERS_SCRIPT = `(({ sparseCounterAttr }) => {
12773
12689
  const walk = (root) => {
12774
- for (const child of root.children) {
12690
+ for (const child of Array.from(root?.children || [])) {
12775
12691
  child.removeAttribute("c");
12776
12692
  child.removeAttribute(sparseCounterAttr);
12777
12693
  walk(child);
@@ -12787,7 +12703,7 @@ var CLEAR_LIVE_COUNTERS_SCRIPT = `(({ sparseCounterAttr }) => {
12787
12703
  var ASSIGN_SPARSE_COUNTERS_SCRIPT = `(({ sparseCounterAttr, startCounter }) => {
12788
12704
  let counter = startCounter;
12789
12705
  const walk = (root) => {
12790
- for (const child of root.children) {
12706
+ for (const child of Array.from(root?.children || [])) {
12791
12707
  child.setAttribute(sparseCounterAttr, String(counter++));
12792
12708
  walk(child);
12793
12709
  if (child.shadowRoot) {
@@ -12801,7 +12717,7 @@ var ASSIGN_SPARSE_COUNTERS_SCRIPT = `(({ sparseCounterAttr, startCounter }) => {
12801
12717
  })`;
12802
12718
  var APPLY_DENSE_COUNTERS_SCRIPT = `(({ sparseCounterAttr, mapping }) => {
12803
12719
  const walk = (root) => {
12804
- for (const child of root.children) {
12720
+ for (const child of Array.from(root?.children || [])) {
12805
12721
  child.removeAttribute("c");
12806
12722
  const sparse = child.getAttribute(sparseCounterAttr);
12807
12723
  if (sparse !== null) {
@@ -12864,20 +12780,22 @@ function ensureSparseCountersForAllRecords(counterRecords) {
12864
12780
  async function clearOpensteerLiveCounters(engine, pageRef) {
12865
12781
  const frames = await engine.listFrames({ pageRef });
12866
12782
  const failures = [];
12867
- for (const frame of frames) {
12868
- try {
12869
- await engine.evaluateFrame({
12870
- frameRef: frame.frameRef,
12871
- script: CLEAR_LIVE_COUNTERS_SCRIPT,
12872
- args: [{ sparseCounterAttr: OPENSTEER_SPARSE_COUNTER_ATTR }]
12873
- });
12874
- } catch (error) {
12875
- if (isDetachedFrameSyncError(error)) {
12876
- continue;
12783
+ await Promise.all(
12784
+ frames.map(async (frame) => {
12785
+ try {
12786
+ await engine.evaluateFrame({
12787
+ frameRef: frame.frameRef,
12788
+ script: CLEAR_LIVE_COUNTERS_SCRIPT,
12789
+ args: [{ sparseCounterAttr: OPENSTEER_SPARSE_COUNTER_ATTR }]
12790
+ });
12791
+ } catch (error) {
12792
+ if (isDetachedFrameSyncError(error)) {
12793
+ return;
12794
+ }
12795
+ failures.push(`frame ${frame.frameRef} could not be cleared (${describeError(error)}).`);
12877
12796
  }
12878
- failures.push(`frame ${frame.frameRef} could not be cleared (${describeError(error)}).`);
12879
- }
12880
- }
12797
+ })
12798
+ );
12881
12799
  if (failures.length > 0) {
12882
12800
  throw buildLiveCounterSyncError("clear live counters", failures);
12883
12801
  }
@@ -12926,25 +12844,29 @@ async function syncDenseCountersToLiveDom(engine, pageRef, sparseToDirectMapping
12926
12844
  denseCounter
12927
12845
  ])
12928
12846
  );
12929
- for (const frame of frames) {
12930
- try {
12931
- await engine.evaluateFrame({
12932
- frameRef: frame.frameRef,
12933
- script: APPLY_DENSE_COUNTERS_SCRIPT,
12934
- args: [
12935
- {
12936
- sparseCounterAttr: OPENSTEER_SPARSE_COUNTER_ATTR,
12937
- mapping: mappingObj
12938
- }
12939
- ]
12940
- });
12941
- } catch (error) {
12942
- if (isDetachedFrameSyncError(error)) {
12943
- continue;
12847
+ await Promise.all(
12848
+ frames.map(async (frame) => {
12849
+ try {
12850
+ await engine.evaluateFrame({
12851
+ frameRef: frame.frameRef,
12852
+ script: APPLY_DENSE_COUNTERS_SCRIPT,
12853
+ args: [
12854
+ {
12855
+ sparseCounterAttr: OPENSTEER_SPARSE_COUNTER_ATTR,
12856
+ mapping: mappingObj
12857
+ }
12858
+ ]
12859
+ });
12860
+ } catch (error) {
12861
+ if (isDetachedFrameSyncError(error)) {
12862
+ return;
12863
+ }
12864
+ failures.push(
12865
+ `frame ${frame.frameRef} could not be synchronized (${describeError(error)}).`
12866
+ );
12944
12867
  }
12945
- failures.push(`frame ${frame.frameRef} could not be synchronized (${describeError(error)}).`);
12946
- }
12947
- }
12868
+ })
12869
+ );
12948
12870
  if (failures.length > 0) {
12949
12871
  throw buildLiveCounterSyncError("synchronize dense counters", failures);
12950
12872
  }
@@ -12962,26 +12884,26 @@ async function compileOpensteerSnapshot(options) {
12962
12884
  await clearOpensteerLiveCounters(options.engine, options.pageRef);
12963
12885
  await assignSparseCountersToLiveDom(options.engine, options.pageRef);
12964
12886
  const pageInfo = await options.engine.getPageInfo({ pageRef: options.pageRef });
12965
- const mainSnapshot = await getMainDocumentSnapshot(options.engine, options.pageRef);
12966
- const snapshotsByDocumentRef = await collectDocumentSnapshots(options.engine, mainSnapshot);
12887
+ const { mainSnapshot, snapshotsByDocumentRef } = await getPageDocumentSnapshots(
12888
+ options.engine,
12889
+ options.pageRef
12890
+ );
12967
12891
  await cleanupLiveSemantics();
12968
12892
  cleanupLiveSemantics = async () => {
12969
12893
  };
12970
- const snapshotIndices = /* @__PURE__ */ new Map();
12971
12894
  const renderedNodes = /* @__PURE__ */ new Map();
12972
12895
  const rawHtml = renderDocumentSnapshot(
12973
12896
  mainSnapshot.documentRef,
12974
12897
  snapshotsByDocumentRef,
12975
- snapshotIndices,
12976
12898
  renderedNodes,
12977
12899
  {
12978
12900
  iframeDepth: 0,
12979
12901
  shadowDepth: 0
12980
12902
  }
12981
12903
  );
12982
- const cleanedHtml = options.mode === "extraction" ? cleanForExtraction(rawHtml) : cleanForAction(rawHtml);
12983
- const compiledHtml = assignCounters(cleanedHtml, renderedNodes);
12984
- const finalHtml = options.mode === "extraction" ? unwrapExtractionHtml(compiledHtml.html) : compiledHtml.html;
12904
+ const preparedSnapshotDom = options.mode === "extraction" ? prepareExtractionSnapshotDom(rawHtml) : prepareActionSnapshotDom(rawHtml);
12905
+ const compiledHtml = assignCountersInDom(preparedSnapshotDom, renderedNodes, options.mode);
12906
+ const finalHtml = preparedSnapshotDom === void 0 ? "" : options.mode === "extraction" ? serializePreparedExtractionSnapshot(preparedSnapshotDom) : serializePreparedActionSnapshot(preparedSnapshotDom);
12985
12907
  ensureSparseCountersForAllRecords(compiledHtml.counterRecords);
12986
12908
  await syncDenseCountersToLiveDom(
12987
12909
  options.engine,
@@ -13016,6 +12938,25 @@ async function getMainDocumentSnapshot(engine, pageRef) {
13016
12938
  }
13017
12939
  return engine.getDomSnapshot({ frameRef: mainFrame.frameRef });
13018
12940
  }
12941
+ async function getPageDocumentSnapshots(engine, pageRef) {
12942
+ const bundleEngine = engine;
12943
+ const bundledSnapshots = await bundleEngine.getPageDomSnapshots?.({ pageRef });
12944
+ if (bundledSnapshots && bundledSnapshots.length > 0) {
12945
+ const mainSnapshot2 = bundledSnapshots.find((snapshot) => snapshot.parentDocumentRef === void 0) ?? bundledSnapshots[0];
12946
+ return {
12947
+ mainSnapshot: mainSnapshot2,
12948
+ snapshotsByDocumentRef: new Map(
12949
+ bundledSnapshots.map((snapshot) => [snapshot.documentRef, snapshot])
12950
+ )
12951
+ };
12952
+ }
12953
+ const mainSnapshot = await getMainDocumentSnapshot(engine, pageRef);
12954
+ const snapshotsByDocumentRef = await collectDocumentSnapshots(engine, mainSnapshot);
12955
+ return {
12956
+ mainSnapshot,
12957
+ snapshotsByDocumentRef
12958
+ };
12959
+ }
13019
12960
  async function collectDocumentSnapshots(engine, mainSnapshot) {
13020
12961
  const snapshotsByDocumentRef = /* @__PURE__ */ new Map([
13021
12962
  [mainSnapshot.documentRef, mainSnapshot]
@@ -13034,7 +12975,7 @@ async function collectDocumentSnapshots(engine, mainSnapshot) {
13034
12975
  }
13035
12976
  return snapshotsByDocumentRef;
13036
12977
  }
13037
- function renderDocumentSnapshot(documentRef, snapshotsByDocumentRef, snapshotIndices, renderedNodes, depth) {
12978
+ function renderDocumentSnapshot(documentRef, snapshotsByDocumentRef, renderedNodes, depth) {
13038
12979
  const snapshot = snapshotsByDocumentRef.get(documentRef);
13039
12980
  if (!snapshot) {
13040
12981
  return "";
@@ -13046,17 +12987,9 @@ function renderDocumentSnapshot(documentRef, snapshotsByDocumentRef, snapshotInd
13046
12987
  `snapshot ${snapshot.documentRef} is missing root node ${String(snapshot.rootSnapshotNodeId)}`
13047
12988
  );
13048
12989
  }
13049
- return renderNode(
13050
- snapshot,
13051
- rootNode,
13052
- nodesById,
13053
- snapshotsByDocumentRef,
13054
- snapshotIndices,
13055
- renderedNodes,
13056
- depth
13057
- );
12990
+ return renderNode(snapshot, rootNode, nodesById, snapshotsByDocumentRef, renderedNodes, depth);
13058
12991
  }
13059
- function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotIndices, renderedNodes, depth) {
12992
+ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, renderedNodes, depth) {
13060
12993
  if (node.nodeType === 3) {
13061
12994
  return escapeHtml(node.nodeValue || node.textContent || "");
13062
12995
  }
@@ -13064,56 +12997,26 @@ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotI
13064
12997
  return "";
13065
12998
  }
13066
12999
  if (node.nodeType === 9 || node.nodeType === 11) {
13067
- return renderChildren(
13068
- snapshot,
13069
- node,
13070
- nodesById,
13071
- snapshotsByDocumentRef,
13072
- snapshotIndices,
13073
- renderedNodes,
13074
- depth
13075
- );
13000
+ return renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, renderedNodes, depth);
13076
13001
  }
13077
13002
  if (node.nodeType !== 1) {
13078
- return renderChildren(
13079
- snapshot,
13080
- node,
13081
- nodesById,
13082
- snapshotsByDocumentRef,
13083
- snapshotIndices,
13084
- renderedNodes,
13085
- depth
13086
- );
13003
+ return renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, renderedNodes, depth);
13087
13004
  }
13088
13005
  const tagName = normalizeTagName(node.nodeName);
13089
13006
  if (isPseudoElementTagName(tagName)) {
13090
- return renderChildren(
13091
- snapshot,
13092
- node,
13093
- nodesById,
13094
- snapshotsByDocumentRef,
13095
- snapshotIndices,
13096
- renderedNodes,
13097
- depth
13098
- );
13007
+ return renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, renderedNodes, depth);
13099
13008
  }
13100
13009
  if ((depth.iframeDepth > 0 || depth.shadowDepth > 0) && (tagName === "html" || tagName === "head" || tagName === "body")) {
13101
- return renderChildren(
13102
- snapshot,
13103
- node,
13104
- nodesById,
13105
- snapshotsByDocumentRef,
13106
- snapshotIndices,
13107
- renderedNodes,
13108
- depth
13109
- );
13010
+ return renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, renderedNodes, depth);
13110
13011
  }
13111
13012
  const snapshotAttributes = normalizeNodeAttributes(node.attributes);
13013
+ const snapshotAttributeIndex = indexNodeAttributes(snapshotAttributes);
13112
13014
  const authoredAttributes = stripInternalSnapshotAttributes(snapshotAttributes);
13015
+ const authoredAttributeIndex = indexNodeAttributes(authoredAttributes);
13113
13016
  const attributes = [...authoredAttributes];
13114
- const subtreeHidden = hasAttribute3(snapshotAttributes, OPENSTEER_HIDDEN_ATTR) || isLikelySubtreeHidden(node);
13115
- const selfHidden = !subtreeHidden && (hasAttribute3(snapshotAttributes, OPENSTEER_SELF_HIDDEN_ATTR) || isLikelySelfHidden(node, nodesById));
13116
- const interactive = !subtreeHidden && !selfHidden && (hasAttribute3(snapshotAttributes, OPENSTEER_INTERACTIVE_ATTR) || isLikelyInteractive(tagName, node, authoredAttributes));
13017
+ const subtreeHidden = snapshotAttributeIndex.has(OPENSTEER_HIDDEN_ATTR) || isLikelySubtreeHidden(node);
13018
+ const selfHidden = !subtreeHidden && (snapshotAttributeIndex.has(OPENSTEER_SELF_HIDDEN_ATTR) || isLikelySelfHidden(node, nodesById));
13019
+ const interactive = !subtreeHidden && !selfHidden && (snapshotAttributeIndex.has(OPENSTEER_INTERACTIVE_ATTR) || isLikelyInteractive(tagName, node, authoredAttributes, authoredAttributeIndex));
13117
13020
  if (interactive) {
13118
13021
  attributes.push({ name: OPENSTEER_INTERACTIVE_ATTR, value: "1" });
13119
13022
  }
@@ -13122,7 +13025,7 @@ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotI
13122
13025
  } else if (selfHidden) {
13123
13026
  attributes.push({ name: OPENSTEER_SELF_HIDDEN_ATTR, value: "1" });
13124
13027
  }
13125
- const sparseCounter = findAttributeValue(snapshotAttributes, OPENSTEER_SPARSE_COUNTER_ATTR);
13028
+ const sparseCounter = snapshotAttributeIndex.get(OPENSTEER_SPARSE_COUNTER_ATTR);
13126
13029
  if (sparseCounter !== void 0) {
13127
13030
  attributes.push({ name: OPENSTEER_SPARSE_COUNTER_ATTR, value: sparseCounter });
13128
13031
  }
@@ -13133,21 +13036,18 @@ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotI
13133
13036
  const syntheticNodeId = buildSyntheticNodeId(snapshot, node);
13134
13037
  attributes.push({ name: OPENSTEER_NODE_ID_ATTR, value: syntheticNodeId });
13135
13038
  renderedNodes.set(syntheticNodeId, {
13136
- locator: createNodeLocator(snapshot.documentRef, snapshot.documentEpoch, node.nodeRef),
13137
- anchor: buildSnapshotElementAnchor(snapshot, node, snapshotsByDocumentRef, snapshotIndices),
13138
13039
  pageRef: snapshot.pageRef,
13139
13040
  frameRef: snapshot.frameRef,
13140
13041
  documentRef: snapshot.documentRef,
13141
13042
  documentEpoch: snapshot.documentEpoch,
13142
13043
  nodeRef: node.nodeRef,
13143
13044
  tagName: tagName.toUpperCase(),
13144
- pathHint: buildPathHint(tagName, authoredAttributes),
13145
- ...buildTextSnippet(node.textContent) === void 0 ? {} : { text: buildTextSnippet(node.textContent) },
13146
13045
  ...authoredAttributes.length === 0 ? {} : { attributes: authoredAttributes },
13147
13046
  iframeDepth: depth.iframeDepth,
13148
13047
  shadowDepth: depth.shadowDepth,
13149
13048
  interactive,
13150
- liveCounterSyncEligible: isLiveCounterSyncEligible(node, nodesById)
13049
+ liveCounterSyncEligible: isLiveCounterSyncEligible(node, nodesById),
13050
+ ...node.textContent === void 0 ? {} : { textContent: node.textContent }
13151
13051
  });
13152
13052
  }
13153
13053
  const attributeText = attributesToHtml(attributes);
@@ -13156,7 +13056,6 @@ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotI
13156
13056
  node,
13157
13057
  nodesById,
13158
13058
  snapshotsByDocumentRef,
13159
- snapshotIndices,
13160
13059
  renderedNodes,
13161
13060
  depth
13162
13061
  );
@@ -13167,7 +13066,6 @@ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotI
13167
13066
  const iframeHtml = renderDocumentSnapshot(
13168
13067
  node.contentDocumentRef,
13169
13068
  snapshotsByDocumentRef,
13170
- snapshotIndices,
13171
13069
  renderedNodes,
13172
13070
  {
13173
13071
  iframeDepth: depth.iframeDepth + 1,
@@ -13179,7 +13077,7 @@ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotI
13179
13077
  }
13180
13078
  return `${elementHtml}<${OPENSTEER_IFRAME_BOUNDARY_TAG} ${OPENSTEER_BOUNDARY_ATTR}="iframe">${iframeHtml}</${OPENSTEER_IFRAME_BOUNDARY_TAG}>`;
13181
13079
  }
13182
- function renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotIndices, renderedNodes, depth) {
13080
+ function renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, renderedNodes, depth) {
13183
13081
  const regularChildren = [];
13184
13082
  const shadowChildren = [];
13185
13083
  for (const childSnapshotNodeId of node.childSnapshotNodeIds) {
@@ -13196,18 +13094,10 @@ function renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, snaps
13196
13094
  const chunks = [];
13197
13095
  if (shadowChildren.length > 0) {
13198
13096
  const shadowHtml = shadowChildren.map(
13199
- (child) => renderNode(
13200
- snapshot,
13201
- child,
13202
- nodesById,
13203
- snapshotsByDocumentRef,
13204
- snapshotIndices,
13205
- renderedNodes,
13206
- {
13207
- iframeDepth: depth.iframeDepth,
13208
- shadowDepth: depth.shadowDepth + 1
13209
- }
13210
- )
13097
+ (child) => renderNode(snapshot, child, nodesById, snapshotsByDocumentRef, renderedNodes, {
13098
+ iframeDepth: depth.iframeDepth,
13099
+ shadowDepth: depth.shadowDepth + 1
13100
+ })
13211
13101
  ).join("");
13212
13102
  chunks.push(
13213
13103
  `<${OPENSTEER_SHADOW_BOUNDARY_TAG} ${OPENSTEER_BOUNDARY_ATTR}="shadow">${shadowHtml}</${OPENSTEER_SHADOW_BOUNDARY_TAG}>`
@@ -13215,24 +13105,21 @@ function renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, snaps
13215
13105
  }
13216
13106
  for (const child of regularChildren) {
13217
13107
  chunks.push(
13218
- renderNode(
13219
- snapshot,
13220
- child,
13221
- nodesById,
13222
- snapshotsByDocumentRef,
13223
- snapshotIndices,
13224
- renderedNodes,
13225
- depth
13226
- )
13108
+ renderNode(snapshot, child, nodesById, snapshotsByDocumentRef, renderedNodes, depth)
13227
13109
  );
13228
13110
  }
13229
13111
  return chunks.join("");
13230
13112
  }
13231
- function assignCounters(cleanedHtml, renderedNodes) {
13232
- const $ = cheerio.load(cleanedHtml, { xmlMode: false });
13113
+ function assignCountersInDom($, renderedNodes, mode) {
13233
13114
  const counterRecords = /* @__PURE__ */ new Map();
13234
13115
  const sparseToDirectMapping = /* @__PURE__ */ new Map();
13235
13116
  let nextCounter = 1;
13117
+ if (!$) {
13118
+ return {
13119
+ counterRecords,
13120
+ sparseToDirectMapping
13121
+ };
13122
+ }
13236
13123
  $("*").each(function assignElementCounter() {
13237
13124
  const el = $(this);
13238
13125
  const syntheticNodeId = el.attr(OPENSTEER_NODE_ID_ATTR);
@@ -13244,14 +13131,24 @@ function assignCounters(cleanedHtml, renderedNodes) {
13244
13131
  if (!rendered) {
13245
13132
  return;
13246
13133
  }
13134
+ if (mode === "extraction" && EXTRACTION_SKIPPED_COUNTER_TAGS.has(rendered.tagName.toLowerCase())) {
13135
+ el.removeAttr(OPENSTEER_SPARSE_COUNTER_ATTR);
13136
+ return;
13137
+ }
13247
13138
  const rawSparseCounter = el.attr(OPENSTEER_SPARSE_COUNTER_ATTR);
13248
13139
  el.removeAttr(OPENSTEER_SPARSE_COUNTER_ATTR);
13249
13140
  const sparseCounter = rawSparseCounter ? Number.parseInt(rawSparseCounter, 10) : void 0;
13141
+ const replayableSparseCounter = typeof sparseCounter === "number" && Number.isFinite(sparseCounter) ? sparseCounter : void 0;
13142
+ if (rendered.liveCounterSyncEligible && replayableSparseCounter === void 0) {
13143
+ return;
13144
+ }
13250
13145
  const counter = nextCounter++;
13251
13146
  el.attr("c", String(counter));
13252
- if (sparseCounter !== void 0 && Number.isFinite(sparseCounter)) {
13253
- sparseToDirectMapping.set(sparseCounter, counter);
13147
+ if (replayableSparseCounter !== void 0) {
13148
+ sparseToDirectMapping.set(replayableSparseCounter, counter);
13254
13149
  }
13150
+ const pathHint = buildPathHint(rendered.tagName.toLowerCase(), rendered.attributes ?? []);
13151
+ const text = buildTextSnippet(rendered.textContent);
13255
13152
  counterRecords.set(counter, {
13256
13153
  element: counter,
13257
13154
  pageRef: rendered.pageRef,
@@ -13260,20 +13157,17 @@ function assignCounters(cleanedHtml, renderedNodes) {
13260
13157
  documentEpoch: rendered.documentEpoch,
13261
13158
  nodeRef: rendered.nodeRef,
13262
13159
  tagName: rendered.tagName,
13263
- pathHint: rendered.pathHint,
13264
- ...rendered.text === void 0 ? {} : { text: rendered.text },
13160
+ pathHint,
13161
+ ...text === void 0 ? {} : { text },
13265
13162
  ...rendered.attributes === void 0 ? {} : { attributes: rendered.attributes },
13266
13163
  iframeDepth: rendered.iframeDepth,
13267
13164
  shadowDepth: rendered.shadowDepth,
13268
13165
  interactive: rendered.interactive,
13269
13166
  liveCounterSyncEligible: rendered.liveCounterSyncEligible,
13270
- locator: rendered.locator,
13271
- anchor: rendered.anchor,
13272
- ...sparseCounter !== void 0 && Number.isFinite(sparseCounter) ? { sparseCounter } : {}
13167
+ ...replayableSparseCounter === void 0 ? {} : { sparseCounter: replayableSparseCounter }
13273
13168
  });
13274
13169
  });
13275
13170
  return {
13276
- html: $.html(),
13277
13171
  counterRecords,
13278
13172
  sparseToDirectMapping
13279
13173
  };
@@ -13347,28 +13241,28 @@ function isLikelySelfHidden(node, nodesById) {
13347
13241
  }
13348
13242
  return !hasVisibleOutOfFlowChild(node, nodesById);
13349
13243
  }
13350
- function isLikelyInteractive(tagName, node, attributes) {
13244
+ function isLikelyInteractive(tagName, node, attributes, attributeIndex) {
13351
13245
  if (NATIVE_INTERACTIVE_TAGS.has(tagName)) {
13352
- if (tagName === "input" && findAttributeValue(attributes, "type")?.toLowerCase() === "hidden") {
13246
+ if (tagName === "input" && attributeIndex.get("type")?.toLowerCase() === "hidden") {
13353
13247
  return false;
13354
13248
  }
13355
13249
  if (tagName !== "a") {
13356
13250
  return true;
13357
13251
  }
13358
13252
  }
13359
- if (tagName === "a" && findAttributeValue(attributes, "href") !== void 0) {
13253
+ if (tagName === "a" && attributeIndex.has("href")) {
13360
13254
  return true;
13361
13255
  }
13362
- 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) {
13256
+ if (attributeIndex.has("onclick") || attributeIndex.has("onmousedown") || attributeIndex.has("onmouseup") || attributeIndex.has("data-action") || attributeIndex.has("data-click") || attributeIndex.has("data-toggle")) {
13363
13257
  return true;
13364
13258
  }
13365
- if (hasNonNegativeTabIndex(findAttributeValue(attributes, "tabindex"))) {
13259
+ if (hasNonNegativeTabIndex(attributeIndex.get("tabindex"))) {
13366
13260
  return true;
13367
13261
  }
13368
- if (findAttributeValue(attributes, "contenteditable")?.toLowerCase() === "true") {
13262
+ if (attributeIndex.get("contenteditable")?.toLowerCase() === "true") {
13369
13263
  return true;
13370
13264
  }
13371
- const role = findAttributeValue(attributes, "role")?.toLowerCase();
13265
+ const role = attributeIndex.get("role")?.toLowerCase();
13372
13266
  return role !== void 0 && INTERACTIVE_ROLE_SET.has(role);
13373
13267
  }
13374
13268
  function hasVisibleOutOfFlowChild(node, nodesById) {
@@ -13431,14 +13325,6 @@ function parseOpacity(value) {
13431
13325
  const parsed = Number.parseFloat(value);
13432
13326
  return Number.isFinite(parsed) ? parsed : Number.NaN;
13433
13327
  }
13434
- function hasAttribute3(attributes, name) {
13435
- const normalizedName = name.toLowerCase();
13436
- return attributes.some((attribute) => attribute.name.toLowerCase() === normalizedName);
13437
- }
13438
- function unwrapExtractionHtml(html) {
13439
- const $ = cheerio.load(html, { xmlMode: false });
13440
- return $("body").html()?.trim() || html;
13441
- }
13442
13328
  function buildSyntheticNodeId(snapshot, node) {
13443
13329
  return `${snapshot.documentRef}:${String(snapshot.documentEpoch)}:${String(node.snapshotNodeId)}`;
13444
13330
  }
@@ -13485,6 +13371,13 @@ function findAttributeValue(attributes, name) {
13485
13371
  const normalizedName = name.toLowerCase();
13486
13372
  return attributes.find((attribute) => attribute.name.toLowerCase() === normalizedName)?.value;
13487
13373
  }
13374
+ function indexNodeAttributes(attributes) {
13375
+ const indexed = /* @__PURE__ */ new Map();
13376
+ for (const attribute of attributes) {
13377
+ indexed.set(attribute.name.toLowerCase(), attribute.value);
13378
+ }
13379
+ return indexed;
13380
+ }
13488
13381
  function attributesToHtml(attributes) {
13489
13382
  if (attributes.length === 0) {
13490
13383
  return "";
@@ -13494,56 +13387,6 @@ function attributesToHtml(attributes) {
13494
13387
  function escapeAttribute(value) {
13495
13388
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
13496
13389
  }
13497
- function buildSnapshotElementAnchor(snapshot, node, snapshotsByDocumentRef, snapshotIndices) {
13498
- const index = getSnapshotIndex(snapshot.documentRef, snapshotsByDocumentRef, snapshotIndices);
13499
- const localAnchor = buildLocalStructuralElementAnchor(index, node);
13500
- return prefixIframeContext(snapshot, localAnchor, snapshotsByDocumentRef, snapshotIndices);
13501
- }
13502
- function prefixIframeContext(snapshot, localPath, snapshotsByDocumentRef, snapshotIndices) {
13503
- if (snapshot.parentDocumentRef === void 0) {
13504
- return sanitizeStructuralElementAnchor(localPath);
13505
- }
13506
- const parentSnapshot = snapshotsByDocumentRef.get(snapshot.parentDocumentRef);
13507
- if (!parentSnapshot) {
13508
- throw new Error(
13509
- `document ${snapshot.documentRef} has parent ${snapshot.parentDocumentRef} but no parent snapshot`
13510
- );
13511
- }
13512
- const parentIndex = getSnapshotIndex(
13513
- parentSnapshot.documentRef,
13514
- snapshotsByDocumentRef,
13515
- snapshotIndices
13516
- );
13517
- const iframeHost = findIframeHostNode(parentIndex, snapshot.documentRef);
13518
- if (!iframeHost) {
13519
- throw new Error(
13520
- `document ${snapshot.documentRef} has parent ${snapshot.parentDocumentRef} but no iframe host`
13521
- );
13522
- }
13523
- const hostPath = buildSnapshotElementAnchor(
13524
- parentSnapshot,
13525
- iframeHost,
13526
- snapshotsByDocumentRef,
13527
- snapshotIndices
13528
- );
13529
- return sanitizeStructuralElementAnchor({
13530
- context: [...hostPath.context, { kind: "iframe", host: hostPath.nodes }, ...localPath.context],
13531
- nodes: localPath.nodes
13532
- });
13533
- }
13534
- function getSnapshotIndex(documentRef, snapshotsByDocumentRef, snapshotIndices) {
13535
- const existing = snapshotIndices.get(documentRef);
13536
- if (existing) {
13537
- return existing;
13538
- }
13539
- const snapshot = snapshotsByDocumentRef.get(documentRef);
13540
- if (!snapshot) {
13541
- throw new Error(`missing DOM snapshot for ${documentRef}`);
13542
- }
13543
- const index = createSnapshotIndex(snapshot);
13544
- snapshotIndices.set(documentRef, index);
13545
- return index;
13546
- }
13547
13390
  function escapeHtml(value) {
13548
13391
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
13549
13392
  }
@@ -14676,7 +14519,10 @@ var OpensteerSessionRuntime = class {
14676
14519
  sessionRef;
14677
14520
  pageRef;
14678
14521
  runId;
14679
- observations;
14522
+ observationSessions = /* @__PURE__ */ new Map();
14523
+ openingObservationSessions = /* @__PURE__ */ new Map();
14524
+ openedObservationSessions = /* @__PURE__ */ new Set();
14525
+ observationSessionStorage = new AsyncLocalStorage();
14680
14526
  operationEventStorage = new AsyncLocalStorage();
14681
14527
  pendingOperationEventCaptures = [];
14682
14528
  ownsEngine = false;
@@ -14728,18 +14574,26 @@ var OpensteerSessionRuntime = class {
14728
14574
  }
14729
14575
  async setObservabilityConfig(input) {
14730
14576
  this.observationConfig = normalizeObservabilityConfig(input);
14731
- const observationSessionId = this.resolveObservationSessionId();
14732
- if (observationSessionId === void 0) {
14733
- return this.observationConfig;
14734
- }
14735
- const sink = this.injectedObservationSink ?? (await this.ensureRoot()).observations;
14736
- this.observations = await sink.openSession({
14737
- sessionId: observationSessionId,
14738
- openedAt: Date.now(),
14739
- config: this.observationConfig
14740
- });
14577
+ await this.ensureConfiguredObservationSession();
14741
14578
  return this.observationConfig;
14742
14579
  }
14580
+ async withObservationSessionId(sessionId, task) {
14581
+ return await this.observationSessionStorage.run(
14582
+ {
14583
+ mode: "session",
14584
+ sessionId: normalizeNonEmptyString("sessionId", sessionId)
14585
+ },
14586
+ task
14587
+ );
14588
+ }
14589
+ async withoutObservationSession(task) {
14590
+ return await this.observationSessionStorage.run(
14591
+ {
14592
+ mode: "disabled"
14593
+ },
14594
+ task
14595
+ );
14596
+ }
14743
14597
  async open(input = {}, options = {}) {
14744
14598
  assertValidSemanticOperationInput("session.open", input);
14745
14599
  if (input.workspace !== void 0 && normalizeNamespace2(input.workspace) !== this.workspace) {
@@ -14756,7 +14610,7 @@ var OpensteerSessionRuntime = class {
14756
14610
  options
14757
14611
  );
14758
14612
  }
14759
- return this.readSessionState();
14613
+ return this.readNavigationSummary();
14760
14614
  }
14761
14615
  const startedAt = Date.now();
14762
14616
  const root = await this.ensureRoot();
@@ -14777,7 +14631,8 @@ var OpensteerSessionRuntime = class {
14777
14631
  openedSessionRef = sessionRef;
14778
14632
  const createdPage = await timeout.runStep(
14779
14633
  () => engine.createPage({
14780
- sessionRef
14634
+ sessionRef,
14635
+ ...input.url === void 0 ? {} : { url: input.url }
14781
14636
  })
14782
14637
  );
14783
14638
  openedPageRef = createdPage.data.pageRef;
@@ -14787,18 +14642,19 @@ var OpensteerSessionRuntime = class {
14787
14642
  await timeout.runStep(() => this.ensureSemantics());
14788
14643
  let frameRef2 = createdPage.frameRef;
14789
14644
  if (input.url !== void 0) {
14790
- const navigation = await this.navigatePage(
14791
- {
14645
+ await timeout.runStep(
14646
+ () => settleWithPolicy(this.policy.settle, {
14792
14647
  operation: "session.open",
14648
+ trigger: "navigation",
14649
+ engine: this.requireEngine(),
14793
14650
  pageRef: createdPage.data.pageRef,
14794
- url: input.url
14795
- },
14796
- timeout
14651
+ signal: timeout.signal,
14652
+ remainingMs: timeout.remainingMs()
14653
+ })
14797
14654
  );
14798
- frameRef2 = navigation.data.mainFrame.frameRef;
14799
14655
  }
14800
14656
  return {
14801
- state: await timeout.runStep(() => this.readSessionState()),
14657
+ state: await timeout.runStep(() => this.readNavigationSummary()),
14802
14658
  frameRef: frameRef2
14803
14659
  };
14804
14660
  },
@@ -14891,7 +14747,11 @@ var OpensteerSessionRuntime = class {
14891
14747
  "page.new cannot use openerPageRef before a session exists"
14892
14748
  );
14893
14749
  }
14894
- return this.open(input.url === void 0 ? {} : { url: input.url }, options);
14750
+ const summary = await this.open(input.url === void 0 ? {} : { url: input.url }, options);
14751
+ return {
14752
+ pageRef: await this.ensurePageRef(),
14753
+ ...summary
14754
+ };
14895
14755
  }
14896
14756
  const startedAt = Date.now();
14897
14757
  try {
@@ -14906,7 +14766,7 @@ var OpensteerSessionRuntime = class {
14906
14766
  })
14907
14767
  );
14908
14768
  this.pageRef = created.data.pageRef;
14909
- return this.readSessionState();
14769
+ return this.readCreatedPageOutput(created.data.pageRef);
14910
14770
  },
14911
14771
  options
14912
14772
  );
@@ -14948,7 +14808,7 @@ var OpensteerSessionRuntime = class {
14948
14808
  () => this.requireEngine().activatePage({ pageRef: input.pageRef })
14949
14809
  );
14950
14810
  this.pageRef = input.pageRef;
14951
- return this.readSessionState();
14811
+ return this.readNavigationSummary(input.pageRef);
14952
14812
  },
14953
14813
  options
14954
14814
  );
@@ -15074,7 +14934,7 @@ var OpensteerSessionRuntime = class {
15074
14934
  timeout.throwIfAborted();
15075
14935
  return {
15076
14936
  navigation: navigation2,
15077
- state: await timeout.runStep(() => this.readSessionState())
14937
+ state: await timeout.runStep(() => this.readNavigationSummary(pageRef))
15078
14938
  };
15079
14939
  },
15080
14940
  (diagnostics) => {
@@ -16308,7 +16168,7 @@ var OpensteerSessionRuntime = class {
16308
16168
  let mutationCaptureDiagnostics;
16309
16169
  let boundaryDiagnostics;
16310
16170
  try {
16311
- const { artifacts, output } = await this.runMutationCapturedOperation(
16171
+ const { artifacts, output, result } = await this.runMutationCapturedOperation(
16312
16172
  "computer.execute",
16313
16173
  {
16314
16174
  ...input.captureNetwork === void 0 ? {} : { captureNetwork: input.captureNetwork },
@@ -16326,9 +16186,14 @@ var OpensteerSessionRuntime = class {
16326
16186
  await this.invalidateLiveSnapshotCounters([pageRef, output2.pageRef], timeout);
16327
16187
  this.pageRef = output2.pageRef;
16328
16188
  const artifacts2 = await this.persistComputerArtifacts(output2, timeout);
16189
+ const result2 = {
16190
+ ...await timeout.runStep(() => this.readNavigationSummary(output2.pageRef)),
16191
+ screenshot: artifacts2.screenshot
16192
+ };
16329
16193
  return {
16330
16194
  artifacts: { manifests: artifacts2.manifests },
16331
- output: artifacts2.output
16195
+ output: output2,
16196
+ result: result2
16332
16197
  };
16333
16198
  } catch (error) {
16334
16199
  boundaryDiagnostics ??= takeActionBoundaryDiagnostics(timeout.signal);
@@ -16365,7 +16230,7 @@ var OpensteerSessionRuntime = class {
16365
16230
  documentEpoch: output.screenshot.documentEpoch
16366
16231
  })
16367
16232
  });
16368
- return output;
16233
+ return result;
16369
16234
  } catch (error) {
16370
16235
  await this.appendTrace({
16371
16236
  operation: "computer.execute",
@@ -16513,8 +16378,9 @@ var OpensteerSessionRuntime = class {
16513
16378
  mutationCaptureDiagnostics = diagnostics;
16514
16379
  }
16515
16380
  );
16516
- const output = toOpensteerActionResult(executed.result);
16381
+ const output = toOpensteerActionResult(executed.result.resolved);
16517
16382
  const actionEvents = "events" in executed.result ? executed.result.events : void 0;
16383
+ const resolvedTarget = toOpensteerResolvedTarget2(executed.result.resolved);
16518
16384
  await this.appendTrace({
16519
16385
  operation,
16520
16386
  startedAt,
@@ -16522,8 +16388,13 @@ var OpensteerSessionRuntime = class {
16522
16388
  outcome: "ok",
16523
16389
  ...actionEvents === void 0 ? {} : { events: actionEvents },
16524
16390
  data: {
16525
- target: output.target,
16526
- ...output.point === void 0 ? {} : { point: output.point },
16391
+ target: resolvedTarget,
16392
+ ..."point" in executed.result && executed.result.point !== void 0 ? {
16393
+ point: {
16394
+ x: executed.result.point.x,
16395
+ y: executed.result.point.y
16396
+ }
16397
+ } : {},
16527
16398
  ...boundaryDiagnostics === void 0 ? {} : { settle: boundaryDiagnostics },
16528
16399
  ...buildMutationCaptureTraceData(mutationCaptureDiagnostics)
16529
16400
  },
@@ -17988,20 +17859,20 @@ var OpensteerSessionRuntime = class {
17988
17859
  throw error;
17989
17860
  }
17990
17861
  }
17991
- async readSessionState() {
17992
- const pageRef = await this.ensurePageRef();
17862
+ async readNavigationSummary(targetPageRef) {
17863
+ const pageRef = targetPageRef ?? await this.ensurePageRef();
17993
17864
  const pageInfo = await this.requireEngine().getPageInfo({ pageRef });
17994
- const sessionRef = this.sessionRef;
17995
- if (!sessionRef) {
17996
- throw new Error("Opensteer session is not initialized");
17997
- }
17998
17865
  return {
17999
- sessionRef,
18000
- pageRef,
18001
17866
  url: pageInfo.url,
18002
17867
  title: pageInfo.title
18003
17868
  };
18004
17869
  }
17870
+ async readCreatedPageOutput(pageRef) {
17871
+ return {
17872
+ pageRef,
17873
+ ...await this.readNavigationSummary(pageRef)
17874
+ };
17875
+ }
18005
17876
  async captureSnapshotArtifacts(pageRef, options, timeout) {
18006
17877
  const root = this.requireRoot();
18007
17878
  const mainFrame = await timeout.runStep(() => getMainFrame(this.requireEngine(), pageRef));
@@ -18073,12 +17944,12 @@ var OpensteerSessionRuntime = class {
18073
17944
  const screenshotPayload = manifestToExternalBinaryLocation(root.rootPath, screenshotManifest);
18074
17945
  return {
18075
17946
  manifests,
18076
- output: {
18077
- ...output,
18078
- screenshot: {
18079
- ...output.screenshot,
18080
- payload: screenshotPayload
18081
- }
17947
+ screenshot: {
17948
+ payload: screenshotPayload,
17949
+ format: output.screenshot.format,
17950
+ size: output.screenshot.size,
17951
+ coordinateSpace: output.screenshot.coordinateSpace,
17952
+ ...output.screenshot.clip === void 0 ? {} : { clip: output.screenshot.clip }
18082
17953
  }
18083
17954
  };
18084
17955
  }
@@ -18166,7 +18037,7 @@ var OpensteerSessionRuntime = class {
18166
18037
  }
18167
18038
  async resetRuntimeState(options) {
18168
18039
  const engine = this.engine;
18169
- const observations = this.observations;
18040
+ const observationSessions = [...this.openedObservationSessions];
18170
18041
  this.networkHistory.clear();
18171
18042
  this.sessionRef = void 0;
18172
18043
  this.pageRef = void 0;
@@ -18175,9 +18046,15 @@ var OpensteerSessionRuntime = class {
18175
18046
  this.computer = void 0;
18176
18047
  this.extractionDescriptors = void 0;
18177
18048
  this.engine = void 0;
18178
- this.observations = void 0;
18049
+ this.observationSessions.clear();
18050
+ this.openingObservationSessions.clear();
18051
+ this.openedObservationSessions.clear();
18179
18052
  this.pendingOperationEventCaptures.length = 0;
18180
- await observations?.close("runtime_reset").catch(() => void 0);
18053
+ await Promise.allSettled(
18054
+ observationSessions.map(
18055
+ (observationSession) => observationSession.close("runtime_reset").catch(() => void 0)
18056
+ )
18057
+ );
18181
18058
  if (options.disposeEngine && this.ownsEngine && engine?.dispose) {
18182
18059
  await engine.dispose();
18183
18060
  }
@@ -18187,23 +18064,64 @@ var OpensteerSessionRuntime = class {
18187
18064
  if (this.observationConfig.profile === "off") {
18188
18065
  return void 0;
18189
18066
  }
18190
- if (this.observations !== void 0) {
18191
- return this.observations;
18067
+ const observationSessionId = this.resolveObservationSessionId();
18068
+ if (observationSessionId === void 0) {
18069
+ return void 0;
18070
+ }
18071
+ const existingObservationSession = this.observationSessions.get(observationSessionId);
18072
+ if (existingObservationSession !== void 0) {
18073
+ return existingObservationSession;
18074
+ }
18075
+ const openingObservationSession = this.openingObservationSessions.get(observationSessionId);
18076
+ if (openingObservationSession !== void 0) {
18077
+ return await openingObservationSession;
18078
+ }
18079
+ const openObservationSessionTask = this.openObservationSession(observationSessionId).finally(
18080
+ () => {
18081
+ this.openingObservationSessions.delete(observationSessionId);
18082
+ }
18083
+ );
18084
+ this.openingObservationSessions.set(observationSessionId, openObservationSessionTask);
18085
+ return await openObservationSessionTask;
18086
+ }
18087
+ async ensureConfiguredObservationSession() {
18088
+ if (this.observationConfig.profile === "off") {
18089
+ return void 0;
18192
18090
  }
18193
18091
  const observationSessionId = this.resolveObservationSessionId();
18194
18092
  if (observationSessionId === void 0) {
18195
18093
  return void 0;
18196
18094
  }
18095
+ const hadObservationSession = this.observationSessions.has(observationSessionId) || this.openingObservationSessions.has(observationSessionId);
18096
+ const observationSession = await this.ensureObservationSession();
18097
+ if (observationSession !== void 0 && hadObservationSession) {
18098
+ await observationSession.configure?.({
18099
+ config: this.observationConfig,
18100
+ updatedAt: Date.now()
18101
+ });
18102
+ }
18103
+ return observationSession;
18104
+ }
18105
+ resolveObservationSessionId() {
18106
+ const scopedSession = this.observationSessionStorage.getStore();
18107
+ if (scopedSession?.mode === "session") {
18108
+ return scopedSession.sessionId;
18109
+ }
18110
+ if (scopedSession?.mode === "disabled") {
18111
+ return void 0;
18112
+ }
18113
+ return this.observationSessionId ?? this.sessionRef;
18114
+ }
18115
+ async openObservationSession(sessionId) {
18197
18116
  const sink = this.injectedObservationSink ?? (await this.ensureRoot()).observations;
18198
- this.observations = await sink.openSession({
18199
- sessionId: observationSessionId,
18117
+ const observationSession = await sink.openSession({
18118
+ sessionId,
18200
18119
  openedAt: Date.now(),
18201
18120
  config: this.observationConfig
18202
18121
  });
18203
- return this.observations;
18204
- }
18205
- resolveObservationSessionId() {
18206
- return this.observationSessionId ?? this.sessionRef;
18122
+ this.observationSessions.set(sessionId, observationSession);
18123
+ this.openedObservationSessions.add(observationSession);
18124
+ return observationSession;
18207
18125
  }
18208
18126
  runWithOperationTimeout(operation, callback, options = {}) {
18209
18127
  const timeoutPolicy = options.timeoutMs === void 0 ? this.policy.timeout : {
@@ -19601,15 +19519,10 @@ function normalizeNamespace2(value) {
19601
19519
  const normalized = String(value ?? "default").trim();
19602
19520
  return normalized.length === 0 ? "default" : normalized;
19603
19521
  }
19604
- function toOpensteerActionResult(result) {
19522
+ function toOpensteerActionResult(target) {
19605
19523
  return {
19606
- target: toOpensteerResolvedTarget2(result.resolved),
19607
- ...result.point === void 0 ? {} : {
19608
- point: {
19609
- x: result.point.x,
19610
- y: result.point.y
19611
- }
19612
- }
19524
+ tagName: toOpensteerTagName2(target.node.nodeName),
19525
+ ...target.persist === void 0 ? {} : { persist: target.persist }
19613
19526
  };
19614
19527
  }
19615
19528
  function toOpensteerResolvedTarget2(target) {
@@ -19619,12 +19532,16 @@ function toOpensteerResolvedTarget2(target) {
19619
19532
  documentRef: target.documentRef,
19620
19533
  documentEpoch: target.documentEpoch,
19621
19534
  nodeRef: target.nodeRef,
19622
- tagName: target.node.nodeName.toUpperCase(),
19535
+ tagName: toOpensteerTagName2(target.node.nodeName),
19623
19536
  pathHint: buildPathSelectorHint(target.replayPath ?? target.anchor),
19624
19537
  ...target.persist === void 0 ? {} : { persist: target.persist },
19625
19538
  ...target.selectorUsed === void 0 ? {} : { selectorUsed: target.selectorUsed }
19626
19539
  };
19627
19540
  }
19541
+ function toOpensteerTagName2(nodeName) {
19542
+ const tagName = String(nodeName).trim().toLowerCase();
19543
+ return tagName.length === 0 ? "element" : tagName;
19544
+ }
19628
19545
  function normalizeOpensteerError(error) {
19629
19546
  return normalizeThrownOpensteerError(error, "Unknown Opensteer runtime failure");
19630
19547
  }
@@ -21260,7 +21177,11 @@ function generateReplayScript(options) {
21260
21177
  ``,
21261
21178
  ...renderOpensteerBootstrap(replayTarget),
21262
21179
  ``,
21263
- `const ${initialPageId} = (await opensteer.open(${JSON.stringify(initialPages[0]?.initialUrl ?? "")})).pageRef;`,
21180
+ `await opensteer.open(${JSON.stringify(initialPages[0]?.initialUrl ?? "")});`,
21181
+ `const ${initialPageId} = (await opensteer.listPages()).activePageRef;`,
21182
+ `if (!${initialPageId}) {`,
21183
+ ` throw new Error("Opensteer did not report an active page after open().");`,
21184
+ `}`,
21264
21185
  `let activePageRef: string | undefined = ${initialPageId};`,
21265
21186
  ``,
21266
21187
  `async function ensureActive(pageRef: string): Promise<void> {`,
@@ -21464,26 +21385,35 @@ function renderOpensteerBootstrap(replayTarget) {
21464
21385
  `const opensteer = new Opensteer({`,
21465
21386
  ` provider: {`,
21466
21387
  ` mode: "cloud",`,
21467
- ` baseUrl: requireEnv(${JSON.stringify(replayTarget.baseUrlEnvVar ?? "OPENSTEER_BASE_URL")}),`,
21468
21388
  ` apiKey: requireEnv(${JSON.stringify(replayTarget.apiKeyEnvVar ?? "OPENSTEER_API_KEY")}),`,
21389
+ ...renderCloudBaseUrl(replayTarget),
21469
21390
  ...renderCloudBrowserProfile(replayTarget),
21470
21391
  ` },`,
21471
21392
  `});`
21472
21393
  ];
21473
21394
  }
21474
21395
  function renderRequireEnvHelper(replayTarget) {
21475
- const baseUrlEnvVar = replayTarget.baseUrlEnvVar ?? "OPENSTEER_BASE_URL";
21476
21396
  const apiKeyEnvVar = replayTarget.apiKeyEnvVar ?? "OPENSTEER_API_KEY";
21397
+ const requiredEnvVars = [
21398
+ ...replayTarget.baseUrlEnvVar === void 0 ? [] : [replayTarget.baseUrlEnvVar],
21399
+ apiKeyEnvVar
21400
+ ];
21477
21401
  return [
21478
21402
  `function requireEnv(name: string): string {`,
21479
21403
  ` const value = process.env[name];`,
21480
21404
  ` if (typeof value === "string" && value.trim().length > 0) {`,
21481
21405
  ` return value;`,
21482
21406
  ` }`,
21483
- ` throw new Error(\`Missing environment variable \${name}. Set ${baseUrlEnvVar} and ${apiKeyEnvVar} before replaying this recording.\`);`,
21407
+ ` throw new Error(\`Missing environment variable \${name}. Set ${requiredEnvVars.join(" and ")} before replaying this recording.\`);`,
21484
21408
  `}`
21485
21409
  ].join("\n");
21486
21410
  }
21411
+ function renderCloudBaseUrl(replayTarget) {
21412
+ if (replayTarget.baseUrlEnvVar === void 0) {
21413
+ return [];
21414
+ }
21415
+ return [` baseUrl: requireEnv(${JSON.stringify(replayTarget.baseUrlEnvVar)}),`];
21416
+ }
21487
21417
  function renderCloudBrowserProfile(replayTarget) {
21488
21418
  if (replayTarget.browserProfileId === void 0) {
21489
21419
  return [];
@@ -22644,7 +22574,29 @@ function isLoopbackBaseUrl(baseUrl) {
22644
22574
  }
22645
22575
  return url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "::1" || url.hostname === "[::1]";
22646
22576
  }
22647
- var OpensteerRuntime = class extends OpensteerSessionRuntime {
22577
+ var LocalActivePageHintRuntime = class extends OpensteerSessionRuntime {
22578
+ async completeWithLocalActivePageHint(operation) {
22579
+ const output = await operation();
22580
+ await persistLocalActivePageHint(this, this.rootPath);
22581
+ return output;
22582
+ }
22583
+ async open(input = {}, options = {}) {
22584
+ return this.completeWithLocalActivePageHint(() => super.open(input, options));
22585
+ }
22586
+ async newPage(input = {}, options = {}) {
22587
+ return this.completeWithLocalActivePageHint(() => super.newPage(input, options));
22588
+ }
22589
+ async activatePage(input, options = {}) {
22590
+ return this.completeWithLocalActivePageHint(() => super.activatePage(input, options));
22591
+ }
22592
+ async closePage(input = {}, options = {}) {
22593
+ return this.completeWithLocalActivePageHint(() => super.closePage(input, options));
22594
+ }
22595
+ async goto(input, options = {}) {
22596
+ return this.completeWithLocalActivePageHint(() => super.goto(input, options));
22597
+ }
22598
+ };
22599
+ var OpensteerRuntime = class extends LocalActivePageHintRuntime {
22648
22600
  constructor(options = {}) {
22649
22601
  const publicWorkspace = normalizeWorkspace(options.workspace);
22650
22602
  const rootPath = options.rootPath ?? (publicWorkspace === void 0 ? path2.resolve(options.rootDir ?? process.cwd(), ".opensteer", "temporary", randomUUID()) : resolveFilesystemWorkspacePath({
@@ -22682,6 +22634,41 @@ var OpensteerRuntime = class extends OpensteerSessionRuntime {
22682
22634
  );
22683
22635
  }
22684
22636
  };
22637
+ async function persistLocalActivePageHint(runtime, rootPath) {
22638
+ try {
22639
+ await syncPersistedLocalActivePageHint(runtime, rootPath);
22640
+ } catch {
22641
+ }
22642
+ }
22643
+ async function syncPersistedLocalActivePageHint(runtime, rootPath) {
22644
+ const record = await readPersistedLocalBrowserSessionRecord(rootPath);
22645
+ if (!record) {
22646
+ return;
22647
+ }
22648
+ const sessionInfo = await runtime.info();
22649
+ const activePageRef = sessionInfo.activePageRef;
22650
+ let activePageUrl;
22651
+ let activePageTitle;
22652
+ if (activePageRef !== void 0) {
22653
+ const pages = await runtime.listPages();
22654
+ const activePage = pages.pages.find((page) => page.pageRef === activePageRef);
22655
+ activePageUrl = activePage?.url;
22656
+ activePageTitle = activePage?.title;
22657
+ }
22658
+ const {
22659
+ activePageRef: _previousActivePageRef,
22660
+ activePageUrl: _previousActivePageUrl,
22661
+ activePageTitle: _previousActivePageTitle,
22662
+ ...restRecord
22663
+ } = record;
22664
+ await writePersistedSessionRecord(rootPath, {
22665
+ ...restRecord,
22666
+ updatedAt: Date.now(),
22667
+ ...activePageRef === void 0 ? {} : { activePageRef },
22668
+ ...activePageUrl === void 0 ? {} : { activePageUrl },
22669
+ ...activePageTitle === void 0 ? {} : { activePageTitle }
22670
+ });
22671
+ }
22685
22672
  function buildSharedRuntimeOptions(input) {
22686
22673
  const ownership = resolveOwnership(input.browser);
22687
22674
  const engineFactory = input.engineFactory ?? ((factoryOptions) => new OpensteerBrowserManager({
@@ -22773,6 +22760,6 @@ function createOpensteerSemanticRuntime(input = {}) {
22773
22760
  });
22774
22761
  }
22775
22762
 
22776
- export { CloudSessionProxy, DEFERRED_MATCH_ATTR_KEYS, ElementPathError, FlowRecorderCollector, MATCH_ATTRIBUTE_PRIORITY, OPENSTEER_DOM_ACTION_BRIDGE_SYMBOL, OpensteerCloudClient, STABLE_PRIMARY_ATTR_KEYS, assertProviderSupportsEngine, buildArrayFieldPathCandidates, buildDomDescriptorKey, buildDomDescriptorPayload, buildDomDescriptorVersion, buildPathCandidates, buildPathSelectorHint, buildSegmentSelector, cloneElementPath, cloneReplayElementPath, cloneStructuralElementAnchor, createDomDescriptorStore, createDomRuntime, createOpensteerExtractionDescriptorStore, createOpensteerSemanticRuntime, defaultFallbackPolicy, defaultPolicy, defaultRetryPolicy, defaultSettlePolicy, defaultTimeoutPolicy, delayWithSignal, dispatchSemanticOperation, generateReplayScript, hashDomDescriptorPersist, isBrowserCoreError, isCurrentUrlField, isValidCssAttributeKey, loadEnvironment, normalizeExtractedValue, normalizeOpensteerProviderMode, parseDomDescriptorRecord, parseExtractionDescriptorRecord, requireCloudAppBaseUrl, resolveCloudConfig, resolveDomActionBridge, resolveExtractedValueInContext, resolveOpensteerEnvironment, resolveOpensteerProvider, resolveOpensteerRuntimeConfig, runWithPolicyTimeout, sanitizeElementPath, sanitizeReplayElementPath, sanitizeStructuralElementAnchor, settleWithPolicy, shouldKeepAttributeForPath };
22777
- //# sourceMappingURL=chunk-BVRIPCWA.js.map
22778
- //# sourceMappingURL=chunk-BVRIPCWA.js.map
22763
+ export { CloudSessionProxy, DEFAULT_OPENSTEER_CLOUD_BASE_URL, DEFERRED_MATCH_ATTR_KEYS, ElementPathError, FlowRecorderCollector, MATCH_ATTRIBUTE_PRIORITY, OPENSTEER_DOM_ACTION_BRIDGE_SYMBOL, OpensteerCloudClient, STABLE_PRIMARY_ATTR_KEYS, assertProviderSupportsEngine, buildArrayFieldPathCandidates, buildDomDescriptorKey, buildDomDescriptorPayload, buildDomDescriptorVersion, buildPathCandidates, buildPathSelectorHint, buildSegmentSelector, cloneElementPath, cloneReplayElementPath, cloneStructuralElementAnchor, createDomDescriptorStore, createDomRuntime, createOpensteerExtractionDescriptorStore, createOpensteerSemanticRuntime, defaultFallbackPolicy, defaultPolicy, defaultRetryPolicy, defaultSettlePolicy, defaultTimeoutPolicy, delayWithSignal, dispatchSemanticOperation, generateReplayScript, hashDomDescriptorPersist, isBrowserCoreError, isCurrentUrlField, isValidCssAttributeKey, loadEnvironment, normalizeExtractedValue, normalizeOpensteerProviderMode, parseDomDescriptorRecord, parseExtractionDescriptorRecord, requireCloudAppBaseUrl, resolveCloudConfig, resolveDomActionBridge, resolveExtractedValueInContext, resolveOpensteerEnvironment, resolveOpensteerProvider, resolveOpensteerRuntimeConfig, runWithPolicyTimeout, sanitizeElementPath, sanitizeReplayElementPath, sanitizeStructuralElementAnchor, settleWithPolicy, shouldKeepAttributeForPath };
22764
+ //# sourceMappingURL=chunk-LFWP5RXF.js.map
22765
+ //# sourceMappingURL=chunk-LFWP5RXF.js.map