opensteer 0.9.6 → 0.9.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/{chunk-BVRIPCWA.js → chunk-3OHKIPBD.js} +316 -465
  2. package/dist/chunk-3OHKIPBD.js.map +1 -0
  3. package/dist/{chunk-L4NF74KI.js → chunk-52UNH5UW.js} +5 -5
  4. package/dist/{chunk-L4NF74KI.js.map → chunk-52UNH5UW.js.map} +1 -1
  5. package/dist/{chunk-3XBQRZZC.js → chunk-PJXN7HED.js} +115 -14
  6. package/dist/chunk-PJXN7HED.js.map +1 -0
  7. package/dist/{chunk-3I3A5OLB.js → chunk-R33BXCMQ.js} +16 -7
  8. package/dist/chunk-R33BXCMQ.js.map +1 -0
  9. package/dist/{chunk-T5P2QGZ3.js → chunk-U4BUCIZ4.js} +153 -12
  10. package/dist/chunk-U4BUCIZ4.js.map +1 -0
  11. package/dist/cli/bin.cjs +663 -544
  12. package/dist/cli/bin.cjs.map +1 -1
  13. package/dist/cli/bin.js +66 -50
  14. package/dist/cli/bin.js.map +1 -1
  15. package/dist/index.cjs +584 -495
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +26 -50
  18. package/dist/index.d.ts +26 -50
  19. package/dist/index.js +4 -4
  20. package/dist/local-view/public/assets/app.js +10 -1
  21. package/dist/local-view/serve-entry.cjs +687 -494
  22. package/dist/local-view/serve-entry.cjs.map +1 -1
  23. package/dist/local-view/serve-entry.js +2 -2
  24. package/dist/opensteer-CY2QUJEG.js +6 -0
  25. package/dist/{opensteer-UGA6YBRN.js.map → opensteer-CY2QUJEG.js.map} +1 -1
  26. package/dist/{session-control-U3L5H2ZI.js → session-control-FIP6ZJLH.js} +4 -4
  27. package/dist/{session-control-U3L5H2ZI.js.map → session-control-FIP6ZJLH.js.map} +1 -1
  28. package/package.json +7 -7
  29. package/dist/chunk-3I3A5OLB.js.map +0 -1
  30. package/dist/chunk-3XBQRZZC.js.map +0 -1
  31. package/dist/chunk-BVRIPCWA.js.map +0 -1
  32. package/dist/chunk-T5P2QGZ3.js.map +0 -1
  33. package/dist/opensteer-UGA6YBRN.js +0 -6
package/dist/index.cjs CHANGED
@@ -102,6 +102,11 @@ function stableJsonString(value) {
102
102
 
103
103
  // ../runtime-core/src/internal/filesystem.ts
104
104
  var LOCK_RETRY_DELAYS_MS = [1, 2, 5, 10, 20, 50];
105
+ var LOCK_METADATA_FILENAME = "owner.json";
106
+ var LOCK_METADATA_VERSION = 1;
107
+ var LOCK_HEARTBEAT_INTERVAL_MS = 1e3;
108
+ var LOCK_ORPHAN_GRACE_MS = 2e3;
109
+ var LOCK_METADATALESS_STALE_MS = 3e4;
105
110
  function normalizeNonEmptyString(name, value) {
106
111
  const normalized = value.trim();
107
112
  if (normalized.length === 0) {
@@ -207,24 +212,119 @@ function isAlreadyExistsError(error) {
207
212
  }
208
213
  async function withFilesystemLock(lockPath, task) {
209
214
  await ensureDirectory(path10__default.default.dirname(lockPath));
215
+ const ownerToken = crypto.randomUUID();
210
216
  let attempt = 0;
211
217
  while (true) {
212
218
  try {
213
219
  await promises.mkdir(lockPath);
220
+ const acquiredAt = Date.now();
221
+ await writeLockMetadata(lockPath, {
222
+ version: LOCK_METADATA_VERSION,
223
+ ownerToken,
224
+ pid: process.pid,
225
+ acquiredAt,
226
+ heartbeatAt: acquiredAt
227
+ });
214
228
  break;
215
229
  } catch (error) {
216
230
  if (!isAlreadyExistsError(error)) {
217
231
  throw error;
218
232
  }
233
+ if (await tryRecoverFilesystemLock(lockPath)) {
234
+ attempt = 0;
235
+ continue;
236
+ }
219
237
  const delayMs = LOCK_RETRY_DELAYS_MS[Math.min(attempt, LOCK_RETRY_DELAYS_MS.length - 1)];
220
238
  attempt += 1;
221
239
  await new Promise((resolve4) => setTimeout(resolve4, delayMs));
222
240
  }
223
241
  }
242
+ const heartbeatTimer = setInterval(() => {
243
+ void touchLockMetadata(lockPath, ownerToken);
244
+ }, LOCK_HEARTBEAT_INTERVAL_MS);
245
+ heartbeatTimer.unref?.();
224
246
  try {
225
247
  return await task();
226
248
  } finally {
227
- await promises.rm(lockPath, { recursive: true, force: true });
249
+ clearInterval(heartbeatTimer);
250
+ const metadata = await readLockMetadata(lockPath);
251
+ if (metadata?.ownerToken === ownerToken) {
252
+ await promises.rm(lockPath, { recursive: true, force: true });
253
+ }
254
+ }
255
+ }
256
+ async function tryRecoverFilesystemLock(lockPath) {
257
+ if (!await shouldRecoverFilesystemLock(lockPath)) {
258
+ return false;
259
+ }
260
+ await promises.rm(lockPath, { recursive: true, force: true });
261
+ return true;
262
+ }
263
+ async function shouldRecoverFilesystemLock(lockPath) {
264
+ const metadata = await readLockMetadata(lockPath);
265
+ if (metadata !== void 0) {
266
+ if (isProcessRunning(metadata.pid)) {
267
+ return false;
268
+ }
269
+ return Date.now() - metadata.heartbeatAt >= LOCK_ORPHAN_GRACE_MS;
270
+ }
271
+ const lockStat = await promises.stat(lockPath).catch(() => void 0);
272
+ if (lockStat === void 0) {
273
+ return false;
274
+ }
275
+ return Date.now() - lockStat.mtimeMs >= LOCK_METADATALESS_STALE_MS;
276
+ }
277
+ async function readLockMetadata(lockPath) {
278
+ const metadataPath = path10__default.default.join(lockPath, LOCK_METADATA_FILENAME);
279
+ if (!await pathExists(metadataPath)) {
280
+ return void 0;
281
+ }
282
+ try {
283
+ const parsed = await readJsonFile(metadataPath);
284
+ const pid = parsed.pid;
285
+ const acquiredAt = parsed.acquiredAt;
286
+ const heartbeatAt = parsed.heartbeatAt;
287
+ 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)) {
288
+ return void 0;
289
+ }
290
+ return {
291
+ version: LOCK_METADATA_VERSION,
292
+ ownerToken: parsed.ownerToken,
293
+ pid,
294
+ acquiredAt,
295
+ heartbeatAt
296
+ };
297
+ } catch {
298
+ return void 0;
299
+ }
300
+ }
301
+ async function writeLockMetadata(lockPath, metadata) {
302
+ try {
303
+ await writeJsonFileAtomic(path10__default.default.join(lockPath, LOCK_METADATA_FILENAME), metadata);
304
+ } catch (error) {
305
+ await promises.rm(lockPath, { recursive: true, force: true }).catch(() => void 0);
306
+ throw error;
307
+ }
308
+ }
309
+ async function touchLockMetadata(lockPath, ownerToken) {
310
+ const metadata = await readLockMetadata(lockPath);
311
+ if (metadata === void 0 || metadata.ownerToken !== ownerToken) {
312
+ return;
313
+ }
314
+ await writeJsonFileAtomic(path10__default.default.join(lockPath, LOCK_METADATA_FILENAME), {
315
+ ...metadata,
316
+ heartbeatAt: Date.now()
317
+ }).catch(() => void 0);
318
+ }
319
+ function isProcessRunning(pid) {
320
+ if (!Number.isInteger(pid) || pid <= 0) {
321
+ return false;
322
+ }
323
+ try {
324
+ process.kill(pid, 0);
325
+ return true;
326
+ } catch (error) {
327
+ return error?.code === "EPERM";
228
328
  }
229
329
  }
230
330
 
@@ -814,7 +914,7 @@ function isJsonValueEqual(expected, actual) {
814
914
 
815
915
  // ../protocol/src/version.ts
816
916
  var OPENSTEER_PROTOCOL_NAME = "opensteer";
817
- var OPENSTEER_PROTOCOL_COMPATIBILITY_REVISION = 2;
917
+ var OPENSTEER_PROTOCOL_COMPATIBILITY_REVISION = 3;
818
918
  var OPENSTEER_PROTOCOL_VERSION = `0.${OPENSTEER_PROTOCOL_COMPATIBILITY_REVISION}.0`;
819
919
  var OPENSTEER_PROTOCOL_REST_BASE_PATH = `/api/v${OPENSTEER_PROTOCOL_COMPATIBILITY_REVISION}`;
820
920
  objectSchema(
@@ -1188,7 +1288,7 @@ var visualViewportSchema = objectSchema(
1188
1288
  required: ["origin", "offsetWithinLayoutViewport", "size"]
1189
1289
  }
1190
1290
  );
1191
- var viewportMetricsSchema = objectSchema(
1291
+ objectSchema(
1192
1292
  {
1193
1293
  layoutViewport: layoutViewportSchema,
1194
1294
  visualViewport: visualViewportSchema,
@@ -2639,7 +2739,7 @@ var domSnapshotSchema = objectSchema(
2639
2739
  ]
2640
2740
  }
2641
2741
  );
2642
- var hitTestResultSchema = objectSchema(
2742
+ objectSchema(
2643
2743
  {
2644
2744
  inputPoint: pointSchema,
2645
2745
  inputCoordinateSpace: coordinateSpaceSchema,
@@ -4092,39 +4192,14 @@ var opensteerTargetInputSchema = oneOfSchema(
4092
4192
  title: "OpensteerTargetInput"
4093
4193
  }
4094
4194
  );
4095
- var opensteerResolvedTargetSchema = objectSchema(
4096
- {
4097
- pageRef: pageRefSchema,
4098
- frameRef: frameRefSchema,
4099
- documentRef: documentRefSchema,
4100
- documentEpoch: documentEpochSchema,
4101
- nodeRef: nodeRefSchema,
4102
- tagName: stringSchema(),
4103
- pathHint: stringSchema(),
4104
- persist: stringSchema(),
4105
- selectorUsed: stringSchema()
4106
- },
4107
- {
4108
- title: "OpensteerResolvedTarget",
4109
- required: [
4110
- "pageRef",
4111
- "frameRef",
4112
- "documentRef",
4113
- "documentEpoch",
4114
- "nodeRef",
4115
- "tagName",
4116
- "pathHint"
4117
- ]
4118
- }
4119
- );
4120
4195
  var opensteerActionResultSchema = objectSchema(
4121
4196
  {
4122
- target: opensteerResolvedTargetSchema,
4123
- point: pointSchema
4197
+ tagName: stringSchema({ minLength: 1 }),
4198
+ persist: stringSchema({ minLength: 1 })
4124
4199
  },
4125
4200
  {
4126
4201
  title: "OpensteerActionResult",
4127
- required: ["target"]
4202
+ required: ["tagName"]
4128
4203
  }
4129
4204
  );
4130
4205
  var opensteerSnapshotCounterSchema = objectSchema(
@@ -4170,16 +4245,14 @@ var opensteerSnapshotCounterSchema = objectSchema(
4170
4245
  ]
4171
4246
  }
4172
4247
  );
4173
- var opensteerSessionStateSchema = objectSchema(
4248
+ var opensteerNavigationSummarySchema = objectSchema(
4174
4249
  {
4175
- sessionRef: sessionRefSchema,
4176
- pageRef: pageRefSchema,
4177
4250
  url: stringSchema(),
4178
4251
  title: stringSchema()
4179
4252
  },
4180
4253
  {
4181
- title: "OpensteerSessionState",
4182
- required: ["sessionRef", "pageRef", "url", "title"]
4254
+ title: "OpensteerNavigationSummary",
4255
+ required: ["url", "title"]
4183
4256
  }
4184
4257
  );
4185
4258
  var opensteerOpenInputSchema = objectSchema(
@@ -4247,6 +4320,17 @@ var opensteerPageCloseOutputSchema = objectSchema(
4247
4320
  required: ["closedPageRef", "pages"]
4248
4321
  }
4249
4322
  );
4323
+ var opensteerPageNewOutputSchema = objectSchema(
4324
+ {
4325
+ pageRef: pageRefSchema,
4326
+ url: stringSchema(),
4327
+ title: stringSchema()
4328
+ },
4329
+ {
4330
+ title: "OpensteerPageNewOutput",
4331
+ required: ["pageRef", "url", "title"]
4332
+ }
4333
+ );
4250
4334
  var opensteerPageGotoInputSchema = objectSchema(
4251
4335
  {
4252
4336
  url: stringSchema(),
@@ -4633,72 +4717,28 @@ var opensteerComputerExecuteInputSchema = objectSchema(
4633
4717
  required: ["action"]
4634
4718
  }
4635
4719
  );
4636
- var opensteerComputerTracePointSchema = objectSchema(
4720
+ var opensteerScreenshotSummarySchema = objectSchema(
4637
4721
  {
4638
- role: enumSchema(["point", "start", "end"]),
4639
- point: pointSchema,
4640
- hitTest: hitTestResultSchema,
4641
- target: opensteerResolvedTargetSchema
4642
- },
4643
- {
4644
- title: "OpensteerComputerTracePoint",
4645
- required: ["role", "point"]
4646
- }
4647
- );
4648
- var opensteerComputerTraceEnrichmentSchema = objectSchema(
4649
- {
4650
- points: arraySchema(opensteerComputerTracePointSchema)
4651
- },
4652
- {
4653
- title: "OpensteerComputerTraceEnrichment",
4654
- required: ["points"]
4655
- }
4656
- );
4657
- var opensteerComputerExecuteTimingSchema = objectSchema(
4658
- {
4659
- actionMs: integerSchema({ minimum: 0 }),
4660
- waitMs: integerSchema({ minimum: 0 }),
4661
- totalMs: integerSchema({ minimum: 0 })
4662
- },
4663
- {
4664
- title: "OpensteerComputerExecuteTiming",
4665
- required: ["actionMs", "waitMs", "totalMs"]
4666
- }
4667
- );
4668
- var opensteerComputerDisplayScaleSchema = objectSchema(
4669
- {
4670
- x: numberSchema({ exclusiveMinimum: 0 }),
4671
- y: numberSchema({ exclusiveMinimum: 0 })
4722
+ payload: externalBinaryLocationSchema,
4723
+ format: screenshotFormatSchema,
4724
+ size: sizeSchema,
4725
+ coordinateSpace: coordinateSpaceSchema,
4726
+ clip: rectSchema
4672
4727
  },
4673
4728
  {
4674
- title: "OpensteerComputerDisplayScale",
4675
- required: ["x", "y"]
4729
+ title: "OpensteerScreenshotSummary",
4730
+ required: ["payload", "format", "size", "coordinateSpace"]
4676
4731
  }
4677
4732
  );
4678
4733
  var opensteerComputerExecuteOutputSchema = objectSchema(
4679
4734
  {
4680
- action: opensteerComputerActionSchema,
4681
- pageRef: pageRefSchema,
4682
- screenshot: screenshotArtifactSchema,
4683
- displayViewport: viewportMetricsSchema,
4684
- nativeViewport: viewportMetricsSchema,
4685
- displayScale: opensteerComputerDisplayScaleSchema,
4686
- events: arraySchema(opensteerEventSchema),
4687
- timing: opensteerComputerExecuteTimingSchema,
4688
- trace: opensteerComputerTraceEnrichmentSchema
4735
+ url: stringSchema(),
4736
+ title: stringSchema(),
4737
+ screenshot: opensteerScreenshotSummarySchema
4689
4738
  },
4690
4739
  {
4691
4740
  title: "OpensteerComputerExecuteOutput",
4692
- required: [
4693
- "action",
4694
- "pageRef",
4695
- "screenshot",
4696
- "displayViewport",
4697
- "nativeViewport",
4698
- "displayScale",
4699
- "events",
4700
- "timing"
4701
- ]
4741
+ required: ["url", "title", "screenshot"]
4702
4742
  }
4703
4743
  );
4704
4744
  function assertValidSemanticOperationInput(name, input) {
@@ -4724,7 +4764,7 @@ var opensteerSemanticOperationSpecificationsBase = [
4724
4764
  name: "session.open",
4725
4765
  description: "Open or resume the current Opensteer session and primary page.",
4726
4766
  inputSchema: opensteerOpenInputSchema,
4727
- outputSchema: opensteerSessionStateSchema,
4767
+ outputSchema: opensteerNavigationSummarySchema,
4728
4768
  requiredCapabilities: ["sessions.manage", "pages.manage"],
4729
4769
  resolveRequiredCapabilities: (input) => input.url === void 0 ? ["sessions.manage", "pages.manage"] : ["sessions.manage", "pages.manage", "pages.navigate"]
4730
4770
  }),
@@ -4739,7 +4779,7 @@ var opensteerSemanticOperationSpecificationsBase = [
4739
4779
  name: "page.new",
4740
4780
  description: "Create and optionally navigate a new top-level page in the current session.",
4741
4781
  inputSchema: opensteerPageNewInputSchema,
4742
- outputSchema: opensteerSessionStateSchema,
4782
+ outputSchema: opensteerPageNewOutputSchema,
4743
4783
  requiredCapabilities: ["pages.manage"],
4744
4784
  resolveRequiredCapabilities: (input) => input.url === void 0 ? ["pages.manage"] : ["pages.manage", "pages.navigate"]
4745
4785
  }),
@@ -4747,7 +4787,7 @@ var opensteerSemanticOperationSpecificationsBase = [
4747
4787
  name: "page.activate",
4748
4788
  description: "Activate an existing top-level page in the current session.",
4749
4789
  inputSchema: opensteerPageActivateInputSchema,
4750
- outputSchema: opensteerSessionStateSchema,
4790
+ outputSchema: opensteerNavigationSummarySchema,
4751
4791
  requiredCapabilities: ["pages.manage", "inspect.pages"]
4752
4792
  }),
4753
4793
  defineSemanticOperationSpec({
@@ -4761,7 +4801,7 @@ var opensteerSemanticOperationSpecificationsBase = [
4761
4801
  name: "page.goto",
4762
4802
  description: "Navigate the current Opensteer page to a new URL.",
4763
4803
  inputSchema: opensteerPageGotoInputSchema,
4764
- outputSchema: opensteerSessionStateSchema,
4804
+ outputSchema: opensteerNavigationSummarySchema,
4765
4805
  requiredCapabilities: ["pages.navigate"]
4766
4806
  }),
4767
4807
  defineSemanticOperationSpec({
@@ -6843,14 +6883,14 @@ var DEFAULT_SETTLE_DELAYS = {
6843
6883
  snapshot: 0
6844
6884
  };
6845
6885
  var DOM_ACTION_VISUAL_STABILITY_PROFILES = {
6846
- "dom.click": { settleMs: 750, scope: "visible-frames", timeoutMs: 7e3 },
6847
- "dom.input": { settleMs: 750, scope: "visible-frames", timeoutMs: 7e3 },
6848
- "dom.scroll": { settleMs: 600, scope: "visible-frames", timeoutMs: 7e3 },
6886
+ "dom.click": { settleMs: 750, scope: "main-frame", timeoutMs: 7e3 },
6887
+ "dom.input": { settleMs: 750, scope: "main-frame", timeoutMs: 7e3 },
6888
+ "dom.scroll": { settleMs: 600, scope: "main-frame", timeoutMs: 7e3 },
6849
6889
  "dom.hover": { settleMs: 200, scope: "main-frame", timeoutMs: 2500 }
6850
6890
  };
6851
6891
  var DEFAULT_DOM_ACTION_VISUAL_STABILITY_PROFILE = {
6852
6892
  settleMs: 750,
6853
- scope: "visible-frames",
6893
+ scope: "main-frame",
6854
6894
  timeoutMs: 7e3
6855
6895
  };
6856
6896
  var NAVIGATION_VISUAL_STABILITY_PROFILE = {
@@ -6874,6 +6914,7 @@ var defaultDomActionSettleObserver = {
6874
6914
  pageRef: input.pageRef,
6875
6915
  timeoutMs: effectiveTimeout,
6876
6916
  settleMs: profile.settleMs,
6917
+ ...input.observedMutationQuietMs === void 0 ? {} : { initialQuietMs: input.observedMutationQuietMs },
6877
6918
  scope: profile.scope
6878
6919
  });
6879
6920
  return true;
@@ -6894,15 +6935,20 @@ var defaultNavigationSettleObserver = {
6894
6935
  return false;
6895
6936
  }
6896
6937
  try {
6897
- const startedAt = Date.now();
6898
- await input.engine.waitForPostLoadQuiet({
6899
- pageRef: input.pageRef,
6900
- timeoutMs: effectiveTimeout,
6901
- quietMs: DEFAULT_POST_LOAD_TRACKER_QUIET_WINDOW_MS,
6902
- captureWindowMs: Math.min(NAVIGATION_POST_LOAD_CAPTURE_WINDOW_MS, effectiveTimeout),
6903
- signal: input.signal
6904
- });
6905
- const visualTimeout = Math.max(0, effectiveTimeout - (Date.now() - startedAt));
6938
+ let visualTimeout = effectiveTimeout;
6939
+ let initialQuietMs = input.observedMutationQuietMs ?? 0;
6940
+ if (!input.postLoadHandled) {
6941
+ const startedAt = Date.now();
6942
+ await input.engine.waitForPostLoadQuiet({
6943
+ pageRef: input.pageRef,
6944
+ timeoutMs: effectiveTimeout,
6945
+ quietMs: DEFAULT_POST_LOAD_TRACKER_QUIET_WINDOW_MS,
6946
+ captureWindowMs: Math.min(NAVIGATION_POST_LOAD_CAPTURE_WINDOW_MS, effectiveTimeout),
6947
+ signal: input.signal
6948
+ });
6949
+ visualTimeout = Math.max(0, effectiveTimeout - (Date.now() - startedAt));
6950
+ initialQuietMs = Math.max(initialQuietMs, DEFAULT_POST_LOAD_TRACKER_QUIET_WINDOW_MS);
6951
+ }
6906
6952
  if (visualTimeout <= 0) {
6907
6953
  return true;
6908
6954
  }
@@ -6910,6 +6956,7 @@ var defaultNavigationSettleObserver = {
6910
6956
  pageRef: input.pageRef,
6911
6957
  timeoutMs: visualTimeout,
6912
6958
  settleMs: profile.settleMs,
6959
+ ...initialQuietMs <= 0 ? {} : { initialQuietMs },
6913
6960
  scope: profile.scope
6914
6961
  });
6915
6962
  return true;
@@ -8993,7 +9040,7 @@ var DomActionExecutor = class {
8993
9040
  ...snapshot === void 0 ? {} : { snapshot },
8994
9041
  signal: timeout.signal,
8995
9042
  remainingMs: () => timeout.remainingMs(),
8996
- policySettle: async (targetPageRef, trigger) => {
9043
+ policySettle: async (targetPageRef, trigger, boundary2) => {
8997
9044
  try {
8998
9045
  await settleWithPolicy(this.options.policy.settle, {
8999
9046
  operation,
@@ -9001,7 +9048,9 @@ var DomActionExecutor = class {
9001
9048
  engine: this.options.engine,
9002
9049
  pageRef: targetPageRef,
9003
9050
  signal: timeout.signal,
9004
- remainingMs: timeout.remainingMs()
9051
+ remainingMs: timeout.remainingMs(),
9052
+ ...boundary2?.observedMutationQuietMs === void 0 ? {} : { observedMutationQuietMs: boundary2.observedMutationQuietMs },
9053
+ ...boundary2?.postLoadHandled === true ? { postLoadHandled: true } : {}
9005
9054
  });
9006
9055
  } catch (error) {
9007
9056
  if (snapshot !== void 0 && isSoftSettleTimeoutError(error, timeout.signal)) {
@@ -10389,29 +10438,29 @@ function buildVariantDescriptorFromCluster(descriptors) {
10389
10438
  const keyStats = /* @__PURE__ */ new Map();
10390
10439
  for (const descriptor of descriptors) {
10391
10440
  for (const field of descriptor.fields) {
10392
- const stat2 = keyStats.get(field.path) ?? {
10441
+ const stat3 = keyStats.get(field.path) ?? {
10393
10442
  indices: /* @__PURE__ */ new Set(),
10394
10443
  pathNodes: [],
10395
10444
  attributes: [],
10396
10445
  sources: []
10397
10446
  };
10398
- stat2.indices.add(descriptor.index);
10447
+ stat3.indices.add(descriptor.index);
10399
10448
  if (isPersistedOpensteerExtractionValueNode(field.node)) {
10400
- stat2.pathNodes.push(field.node.$path);
10401
- stat2.attributes.push(field.node.attribute);
10449
+ stat3.pathNodes.push(field.node.$path);
10450
+ stat3.attributes.push(field.node.attribute);
10402
10451
  } else if (isPersistedOpensteerExtractionSourceNode(field.node)) {
10403
- stat2.sources.push("current_url");
10452
+ stat3.sources.push("current_url");
10404
10453
  }
10405
- keyStats.set(field.path, stat2);
10454
+ keyStats.set(field.path, stat3);
10406
10455
  }
10407
10456
  }
10408
10457
  const mergedFields = [];
10409
- for (const [fieldPath, stat2] of keyStats) {
10410
- if (stat2.indices.size < threshold) {
10458
+ for (const [fieldPath, stat3] of keyStats) {
10459
+ if (stat3.indices.size < threshold) {
10411
10460
  continue;
10412
10461
  }
10413
- if (stat2.pathNodes.length >= threshold) {
10414
- let mergedFieldPath = stat2.pathNodes.length === 1 ? sanitizeElementPath(stat2.pathNodes[0]) : mergeElementPathsByMajority(stat2.pathNodes);
10462
+ if (stat3.pathNodes.length >= threshold) {
10463
+ let mergedFieldPath = stat3.pathNodes.length === 1 ? sanitizeElementPath(stat3.pathNodes[0]) : mergeElementPathsByMajority(stat3.pathNodes);
10415
10464
  if (!mergedFieldPath) {
10416
10465
  continue;
10417
10466
  }
@@ -10419,8 +10468,8 @@ function buildVariantDescriptorFromCluster(descriptors) {
10419
10468
  mergedFieldPath = relaxPathForSingleSample(mergedFieldPath, "field");
10420
10469
  }
10421
10470
  mergedFieldPath = minimizePathMatchClauses(mergedFieldPath, "field");
10422
- const attrThreshold = stat2.pathNodes.length === 1 ? 1 : majorityThreshold(stat2.pathNodes.length);
10423
- const attribute = pickModeString(stat2.attributes, attrThreshold);
10471
+ const attrThreshold = stat3.pathNodes.length === 1 ? 1 : majorityThreshold(stat3.pathNodes.length);
10472
+ const attribute = pickModeString(stat3.attributes, attrThreshold);
10424
10473
  mergedFields.push({
10425
10474
  path: fieldPath,
10426
10475
  node: createValueNode({
@@ -10430,7 +10479,7 @@ function buildVariantDescriptorFromCluster(descriptors) {
10430
10479
  });
10431
10480
  continue;
10432
10481
  }
10433
- const dominantSource = pickModeString(stat2.sources, threshold);
10482
+ const dominantSource = pickModeString(stat3.sources, threshold);
10434
10483
  if (dominantSource === "current_url") {
10435
10484
  mergedFields.push({
10436
10485
  path: fieldPath,
@@ -12013,9 +12062,9 @@ async function getProcessLiveness(owner) {
12013
12062
  if (typeof startedAtMs === "number") {
12014
12063
  return hasMatchingProcessStartTime(owner.processStartedAtMs, startedAtMs) ? "live" : "dead";
12015
12064
  }
12016
- return isProcessRunning(owner.pid) ? "unknown" : "dead";
12065
+ return isProcessRunning2(owner.pid) ? "unknown" : "dead";
12017
12066
  }
12018
- function isProcessRunning(pid) {
12067
+ function isProcessRunning2(pid) {
12019
12068
  try {
12020
12069
  process.kill(pid, 0);
12021
12070
  return true;
@@ -13435,11 +13484,31 @@ async function writePersistedSessionRecord(rootPath, record) {
13435
13484
  async function clearPersistedSessionRecord(rootPath, provider) {
13436
13485
  await promises.rm(resolveLiveSessionRecordPath(rootPath, provider), { force: true });
13437
13486
  }
13487
+ function getPersistedLocalBrowserSessionOwnership(record) {
13488
+ return record.ownership === "attached" ? "attached" : "owned";
13489
+ }
13490
+ async function isAttachedLocalBrowserSessionReachable(record) {
13491
+ if (getPersistedLocalBrowserSessionOwnership(record) !== "attached") {
13492
+ return false;
13493
+ }
13494
+ if (record.engine !== "playwright" || record.endpoint === void 0) {
13495
+ return false;
13496
+ }
13497
+ try {
13498
+ await inspectCdpEndpoint({
13499
+ endpoint: record.endpoint,
13500
+ timeoutMs: 1500
13501
+ });
13502
+ return true;
13503
+ } catch {
13504
+ return false;
13505
+ }
13506
+ }
13438
13507
  function isPersistedCloudSessionRecord(value) {
13439
13508
  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);
13440
13509
  }
13441
13510
  function isPersistedLocalBrowserSessionRecord(value) {
13442
- 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;
13511
+ return value.layout === OPENSTEER_LIVE_SESSION_LAYOUT && value.version === OPENSTEER_LIVE_SESSION_VERSION && value.provider === "local" && (value.engine === "playwright" || value.engine === "abp") && (value.ownership === void 0 || value.ownership === "owned" || value.ownership === "attached") && typeof value.pid === "number" && Number.isFinite(value.pid) && typeof value.startedAt === "number" && Number.isFinite(value.startedAt) && typeof value.updatedAt === "number" && Number.isFinite(value.updatedAt) && typeof value.userDataDir === "string" && value.userDataDir.length > 0;
13443
13512
  }
13444
13513
  function resolveOpensteerStateDir() {
13445
13514
  const explicit = process.env.OPENSTEER_HOME?.trim();
@@ -13828,19 +13897,40 @@ function delay(ms) {
13828
13897
  var OPENSTEER_LOCAL_VIEW_SESSION_LAYOUT = "opensteer-local-view-session";
13829
13898
  var OPENSTEER_LOCAL_VIEW_SESSION_VERSION = 1;
13830
13899
  function buildLocalViewSessionId(input) {
13900
+ const ownership = input.ownership ?? "owned";
13901
+ const identity = ownership === "attached" ? input.endpoint ?? input.remoteDebuggingUrl ?? input.baseUrl ?? "attached" : `pid:${String(input.pid ?? 0)}`;
13831
13902
  const hash = crypto.createHash("sha256").update(`${input.rootPath}
13832
- ${String(input.pid)}
13903
+ ${ownership}
13904
+ ${identity}
13833
13905
  ${String(input.startedAt)}`).digest("hex");
13834
13906
  return `local_${hash.slice(0, 24)}`;
13835
13907
  }
13908
+ function buildLocalViewSessionIdForRecord(input) {
13909
+ const ownership = getPersistedLocalBrowserSessionOwnership(input.live);
13910
+ if (ownership === "attached") {
13911
+ return buildLocalViewSessionId({
13912
+ rootPath: input.rootPath,
13913
+ ownership,
13914
+ startedAt: input.live.startedAt,
13915
+ ...input.live.endpoint === void 0 ? {} : { endpoint: input.live.endpoint },
13916
+ ...input.live.baseUrl === void 0 ? {} : { baseUrl: input.live.baseUrl },
13917
+ ...input.live.remoteDebuggingUrl === void 0 ? {} : { remoteDebuggingUrl: input.live.remoteDebuggingUrl }
13918
+ });
13919
+ }
13920
+ return buildLocalViewSessionId({
13921
+ rootPath: input.rootPath,
13922
+ ownership,
13923
+ startedAt: input.live.startedAt,
13924
+ pid: input.live.pid
13925
+ });
13926
+ }
13836
13927
  function createLocalViewSessionManifest(input) {
13837
13928
  return {
13838
13929
  layout: OPENSTEER_LOCAL_VIEW_SESSION_LAYOUT,
13839
13930
  version: OPENSTEER_LOCAL_VIEW_SESSION_VERSION,
13840
- sessionId: buildLocalViewSessionId({
13931
+ sessionId: buildLocalViewSessionIdForRecord({
13841
13932
  rootPath: input.rootPath,
13842
- pid: input.live.pid,
13843
- startedAt: input.live.startedAt
13933
+ live: input.live
13844
13934
  }),
13845
13935
  rootPath: input.rootPath,
13846
13936
  ...input.workspace === void 0 ? {} : { workspace: input.workspace },
@@ -14041,7 +14131,7 @@ var OpensteerBrowserManager = class {
14041
14131
  }
14042
14132
  const liveRecord = await this.readLivePersistentBrowser(await this.ensureWorkspaceStore());
14043
14133
  return {
14044
- mode: this.mode,
14134
+ mode: liveRecord?.ownership === "attached" ? "attach" : this.mode,
14045
14135
  engine: liveRecord?.engine ?? this.engineName,
14046
14136
  ...this.workspace === void 0 ? {} : { workspace: this.workspace },
14047
14137
  live: liveRecord !== void 0
@@ -14169,6 +14259,7 @@ var OpensteerBrowserManager = class {
14169
14259
  });
14170
14260
  const liveRecord = {
14171
14261
  mode: "persistent",
14262
+ ownership: "owned",
14172
14263
  engine: "abp",
14173
14264
  baseUrl: launched.baseUrl,
14174
14265
  remoteDebuggingUrl: launched.remoteDebuggingUrl,
@@ -14255,11 +14346,78 @@ var OpensteerBrowserManager = class {
14255
14346
  }
14256
14347
  async createAttachEngine() {
14257
14348
  const endpoint = await resolveAttachEndpoint(this.browserOptions);
14258
- return this.createAttachedEngine({
14259
- endpoint,
14260
- ...this.browserOptions?.headers === void 0 ? {} : { headers: this.browserOptions.headers },
14261
- freshTab: this.browserOptions?.freshTab ?? true,
14262
- onDispose: async () => void 0
14349
+ if (this.workspace === void 0) {
14350
+ return this.createAttachedEngine({
14351
+ endpoint,
14352
+ ...this.browserOptions?.headers === void 0 ? {} : { headers: this.browserOptions.headers },
14353
+ freshTab: this.browserOptions?.freshTab ?? true,
14354
+ onDispose: async () => void 0
14355
+ });
14356
+ }
14357
+ const workspace = await this.ensureWorkspaceStore();
14358
+ return workspace.lock(async () => {
14359
+ const live = await this.readLivePersistentBrowser(workspace);
14360
+ if (live) {
14361
+ if (live.engine !== "playwright") {
14362
+ throw new Error(
14363
+ `workspace "${this.workspace}" already has a live ${live.engine} browser. Close it before attaching a Playwright browser.`
14364
+ );
14365
+ }
14366
+ if (live.ownership !== "attached") {
14367
+ throw new Error(
14368
+ `workspace "${this.workspace}" already has a live Opensteer-owned browser. Close it before attaching another browser.`
14369
+ );
14370
+ }
14371
+ if (live.endpoint === void 0) {
14372
+ throw new Error("workspace live browser record is missing a DevTools endpoint.");
14373
+ }
14374
+ if (live.endpoint !== endpoint) {
14375
+ throw new Error(
14376
+ `workspace "${this.workspace}" is already attached to a different browser endpoint. Close it before reattaching.`
14377
+ );
14378
+ }
14379
+ await bestEffortRegisterLocalViewSession({
14380
+ rootPath: workspace.rootPath,
14381
+ ...this.workspace === void 0 ? {} : { workspace: this.workspace },
14382
+ live: toPersistedLocalBrowserSessionRecord(this.workspace, live),
14383
+ ownership: "attached"
14384
+ });
14385
+ return this.createAttachedEngine({
14386
+ endpoint: live.endpoint,
14387
+ ...this.browserOptions?.headers === void 0 ? {} : { headers: this.browserOptions.headers },
14388
+ freshTab: this.browserOptions?.freshTab ?? true,
14389
+ onDispose: async () => void 0
14390
+ });
14391
+ }
14392
+ const liveRecord = {
14393
+ mode: "persistent",
14394
+ ownership: "attached",
14395
+ engine: "playwright",
14396
+ endpoint,
14397
+ pid: 0,
14398
+ startedAt: Date.now(),
14399
+ userDataDir: workspace.browserUserDataDir
14400
+ };
14401
+ await this.writeLivePersistentBrowser(workspace, liveRecord);
14402
+ const persistedLiveRecord = toPersistedLocalBrowserSessionRecord(this.workspace, liveRecord);
14403
+ await bestEffortRegisterLocalViewSession({
14404
+ rootPath: workspace.rootPath,
14405
+ ...this.workspace === void 0 ? {} : { workspace: this.workspace },
14406
+ live: persistedLiveRecord,
14407
+ ownership: "attached"
14408
+ });
14409
+ try {
14410
+ return await this.createAttachedEngine({
14411
+ endpoint,
14412
+ ...this.browserOptions?.headers === void 0 ? {} : { headers: this.browserOptions.headers },
14413
+ freshTab: this.browserOptions?.freshTab ?? true,
14414
+ onDispose: async () => void 0
14415
+ });
14416
+ } catch (error) {
14417
+ await this.unregisterLocalViewSessionForRecord(workspace.rootPath, persistedLiveRecord);
14418
+ await clearPersistedSessionRecord(workspace.rootPath, "local").catch(() => void 0);
14419
+ throw error;
14420
+ }
14263
14421
  });
14264
14422
  }
14265
14423
  async createPersistentEngine() {
@@ -14279,7 +14437,7 @@ var OpensteerBrowserManager = class {
14279
14437
  rootPath: workspace.rootPath,
14280
14438
  ...this.workspace === void 0 ? {} : { workspace: this.workspace },
14281
14439
  live: toPersistedLocalBrowserSessionRecord(this.workspace, live),
14282
- ownership: "owned"
14440
+ ownership: live.ownership
14283
14441
  });
14284
14442
  return this.createAttachedEngine({
14285
14443
  endpoint: live.endpoint,
@@ -14295,6 +14453,7 @@ var OpensteerBrowserManager = class {
14295
14453
  });
14296
14454
  const liveRecord = {
14297
14455
  mode: "persistent",
14456
+ ownership: "owned",
14298
14457
  engine: "playwright",
14299
14458
  endpoint: launched.endpoint,
14300
14459
  pid: launched.pid,
@@ -14424,7 +14583,20 @@ var OpensteerBrowserManager = class {
14424
14583
  if (live === void 0) {
14425
14584
  return void 0;
14426
14585
  }
14427
- if (!isProcessRunning(live.pid)) {
14586
+ if (live.ownership === "attached") {
14587
+ const attachedRecord = toPersistedLocalBrowserSessionRecord(this.workspace, live);
14588
+ if (!await isAttachedLocalBrowserSessionReachable(attachedRecord)) {
14589
+ await this.unregisterLocalViewSessionForRecord(workspace.rootPath, attachedRecord);
14590
+ await clearPersistedSessionRecord(workspace.rootPath, "local").catch(() => void 0);
14591
+ return void 0;
14592
+ }
14593
+ return live;
14594
+ }
14595
+ if (!isProcessRunning2(live.pid)) {
14596
+ await this.unregisterLocalViewSessionForRecord(
14597
+ workspace.rootPath,
14598
+ toPersistedLocalBrowserSessionRecord(this.workspace, live)
14599
+ );
14428
14600
  await clearPersistedSessionRecord(workspace.rootPath, "local").catch(() => void 0);
14429
14601
  return void 0;
14430
14602
  }
@@ -14472,6 +14644,10 @@ var OpensteerBrowserManager = class {
14472
14644
  workspace.rootPath,
14473
14645
  toPersistedLocalBrowserSessionRecord(this.workspace, live)
14474
14646
  );
14647
+ if (live.ownership === "attached") {
14648
+ await clearPersistedSessionRecord(workspace.rootPath, "local").catch(() => void 0);
14649
+ return;
14650
+ }
14475
14651
  if (live.engine === "playwright") {
14476
14652
  if (live.endpoint !== void 0) {
14477
14653
  await requestBrowserClose(live.endpoint).catch(() => void 0);
@@ -14500,10 +14676,18 @@ var OpensteerBrowserManager = class {
14500
14676
  }
14501
14677
  async unregisterLocalViewSessionForRecord(rootPath, record) {
14502
14678
  await bestEffortUnregisterLocalViewSession(
14503
- buildLocalViewSessionId({
14679
+ getPersistedLocalBrowserSessionOwnership(record) === "attached" ? buildLocalViewSessionId({
14680
+ rootPath,
14681
+ startedAt: record.startedAt,
14682
+ ownership: "attached",
14683
+ ...record.endpoint === void 0 ? {} : { endpoint: record.endpoint },
14684
+ ...record.baseUrl === void 0 ? {} : { baseUrl: record.baseUrl },
14685
+ ...record.remoteDebuggingUrl === void 0 ? {} : { remoteDebuggingUrl: record.remoteDebuggingUrl }
14686
+ }) : buildLocalViewSessionId({
14504
14687
  rootPath,
14505
- pid: record.pid,
14506
- startedAt: record.startedAt
14688
+ startedAt: record.startedAt,
14689
+ ownership: "owned",
14690
+ pid: record.pid
14507
14691
  })
14508
14692
  );
14509
14693
  }
@@ -14538,6 +14722,7 @@ function toPersistedLocalBrowserSessionRecord(workspace, live) {
14538
14722
  version: 1,
14539
14723
  provider: "local",
14540
14724
  ...workspace === void 0 ? {} : { workspace },
14725
+ ownership: live.ownership,
14541
14726
  engine: live.engine,
14542
14727
  ...live.endpoint === void 0 ? {} : { endpoint: live.endpoint },
14543
14728
  ...live.baseUrl === void 0 ? {} : { baseUrl: live.baseUrl },
@@ -14553,6 +14738,7 @@ function toPersistedLocalBrowserSessionRecord(workspace, live) {
14553
14738
  function toWorkspaceLiveBrowserRecord(record) {
14554
14739
  return {
14555
14740
  mode: "persistent",
14741
+ ownership: getPersistedLocalBrowserSessionOwnership(record),
14556
14742
  engine: record.engine,
14557
14743
  ...record.endpoint === void 0 ? {} : { endpoint: record.endpoint },
14558
14744
  ...record.baseUrl === void 0 ? {} : { baseUrl: record.baseUrl },
@@ -14579,7 +14765,12 @@ function isAttachBrowserOptions(browser) {
14579
14765
  async function resolveAttachEndpoint(browser) {
14580
14766
  const endpoint = browser?.endpoint?.trim();
14581
14767
  if (endpoint && endpoint.length > 0) {
14582
- return endpoint;
14768
+ const inspected = await inspectCdpEndpoint({
14769
+ endpoint,
14770
+ ...browser?.headers === void 0 ? {} : { headers: browser.headers },
14771
+ timeoutMs: DEFAULT_TIMEOUT_MS
14772
+ });
14773
+ return inspected.endpoint;
14583
14774
  }
14584
14775
  const selection = await selectAttachBrowserCandidate({
14585
14776
  timeoutMs: DEFAULT_TIMEOUT_MS
@@ -14843,12 +15034,12 @@ async function waitForProcessExit(pid, timeoutMs) {
14843
15034
  }
14844
15035
  const deadline = Date.now() + timeoutMs;
14845
15036
  while (Date.now() < deadline) {
14846
- if (!isProcessRunning(pid)) {
15037
+ if (!isProcessRunning2(pid)) {
14847
15038
  return true;
14848
15039
  }
14849
15040
  await sleep2(50);
14850
15041
  }
14851
- return !isProcessRunning(pid);
15042
+ return !isProcessRunning2(pid);
14852
15043
  }
14853
15044
  function resolveAbpSessionDir(workspace) {
14854
15045
  return path10__default.default.join(workspace.livePath, "abp-session");
@@ -15827,7 +16018,7 @@ function resolveCloudConfig(input = {}) {
15827
16018
 
15828
16019
  // ../runtime-core/package.json
15829
16020
  var package_default = {
15830
- version: "0.2.5"};
16021
+ version: "0.2.6"};
15831
16022
 
15832
16023
  // ../runtime-core/src/version.ts
15833
16024
  var OPENSTEER_RUNTIME_CORE_VERSION = package_default.version;
@@ -16098,12 +16289,16 @@ function toOpensteerResolvedTarget(target) {
16098
16289
  documentRef: target.documentRef,
16099
16290
  documentEpoch: target.documentEpoch,
16100
16291
  nodeRef: target.nodeRef,
16101
- tagName: target.node.nodeName.toUpperCase(),
16292
+ tagName: toOpensteerTagName(target.node.nodeName),
16102
16293
  pathHint: buildPathSelectorHint(target.replayPath ?? target.anchor),
16103
16294
  ...target.persist === void 0 ? {} : { persist: target.persist },
16104
16295
  ...target.selectorUsed === void 0 ? {} : { selectorUsed: target.selectorUsed }
16105
16296
  };
16106
16297
  }
16298
+ function toOpensteerTagName(nodeName) {
16299
+ const tagName = String(nodeName).trim().toLowerCase();
16300
+ return tagName.length === 0 ? "element" : tagName;
16301
+ }
16107
16302
 
16108
16303
  // ../runtime-core/src/runtimes/computer-use/runtime.ts
16109
16304
  function createComputerUseRuntime(options) {
@@ -16137,7 +16332,7 @@ var DefaultComputerUseRuntime = class {
16137
16332
  screenshot,
16138
16333
  signal: input.timeout.signal,
16139
16334
  remainingMs: () => input.timeout.remainingMs(),
16140
- policySettle: async (pageRef, trigger) => {
16335
+ policySettle: async (pageRef, trigger, boundary) => {
16141
16336
  try {
16142
16337
  await settleWithPolicy(this.options.policy.settle, {
16143
16338
  operation: "computer.execute",
@@ -16145,7 +16340,9 @@ var DefaultComputerUseRuntime = class {
16145
16340
  engine: this.options.engine,
16146
16341
  pageRef,
16147
16342
  signal: input.timeout.signal,
16148
- remainingMs: input.timeout.remainingMs()
16343
+ remainingMs: input.timeout.remainingMs(),
16344
+ ...boundary?.observedMutationQuietMs === void 0 ? {} : { observedMutationQuietMs: boundary.observedMutationQuietMs },
16345
+ ...boundary?.postLoadHandled === true ? { postLoadHandled: true } : {}
16149
16346
  });
16150
16347
  } catch (error) {
16151
16348
  if (pageRef === input.pageRef && isSoftSettleTimeoutError(error, input.timeout.signal)) {
@@ -17323,28 +17520,24 @@ function restoreBoundedAttr(el, attr, value) {
17323
17520
  }
17324
17521
  setBoundedAttr(el, attr, value);
17325
17522
  }
17326
- function deduplicateImages(html) {
17523
+ function deduplicateImagesInDom($) {
17327
17524
  const seen = /* @__PURE__ */ new Set();
17328
- return html.replace(/<img\b([^>]*)>/gi, (full, attrContent) => {
17329
- if (/\bc\s*=/.test(attrContent)) {
17330
- return full;
17331
- }
17332
- const srcMatch = attrContent.match(/\bsrc\s*=\s*(["']?)(.*?)\1/);
17333
- const srcsetMatch = attrContent.match(/\bsrcset\s*=\s*(["'])(.*?)\1/);
17334
- let src = null;
17335
- if (srcMatch && srcMatch[2]) {
17336
- src = srcMatch[2].trim();
17337
- } else if (srcsetMatch && srcsetMatch[2]) {
17338
- src = srcsetMatch[2].split(",")[0]?.trim().split(" ")[0] ?? null;
17525
+ $("img").each(function deduplicateDomImage() {
17526
+ const el = $(this);
17527
+ if (el.attr("c") !== void 0) {
17528
+ return;
17339
17529
  }
17530
+ const srcValue = el.attr("src")?.trim();
17531
+ const srcsetValue = el.attr("srcset");
17532
+ const src = srcValue && srcValue.length > 0 ? srcValue : srcsetValue?.split(",")[0]?.trim().split(/\s+/u)[0];
17340
17533
  if (!src) {
17341
- return full;
17534
+ return;
17342
17535
  }
17343
17536
  if (seen.has(src)) {
17344
- return "";
17537
+ el.remove();
17538
+ return;
17345
17539
  }
17346
17540
  seen.add(src);
17347
- return full;
17348
17541
  });
17349
17542
  }
17350
17543
  function hasAttribute2(node, attr) {
@@ -17402,23 +17595,6 @@ function isPreservedImageElement(node) {
17402
17595
  function getElementsInReverseDocumentOrder($) {
17403
17596
  return $.root().find("*").toArray().reverse().filter((node) => node.type === "tag");
17404
17597
  }
17405
- function getNodeDepth(node) {
17406
- let depth = 0;
17407
- let current = node.parent;
17408
- while (current) {
17409
- depth++;
17410
- current = current.parent;
17411
- }
17412
- return depth;
17413
- }
17414
- function getElementsByDepthDescending($) {
17415
- const elements = $.root().find("*").toArray().filter((node) => node.type === "tag");
17416
- const depths = /* @__PURE__ */ new Map();
17417
- for (const el of elements) {
17418
- depths.set(el, getNodeDepth(el));
17419
- }
17420
- return elements.sort((a, b) => (depths.get(b) ?? 0) - (depths.get(a) ?? 0));
17421
- }
17422
17598
  function flattenExtractionTree($) {
17423
17599
  for (const node of getElementsInReverseDocumentOrder($)) {
17424
17600
  const el = $(node);
@@ -17436,19 +17612,6 @@ function flattenExtractionTree($) {
17436
17612
  el.replaceWith(el.contents());
17437
17613
  }
17438
17614
  }
17439
- function hasMarkedAncestor(el, attr) {
17440
- let current = el[0]?.parent;
17441
- while (current) {
17442
- if (!isElementLikeNode(current)) {
17443
- return false;
17444
- }
17445
- if (current.attribs?.[attr] !== void 0) {
17446
- return true;
17447
- }
17448
- current = current.parent;
17449
- }
17450
- return false;
17451
- }
17452
17615
  function isIndicatorImage(node) {
17453
17616
  return (node?.tagName || "").toLowerCase() === "img" && (hasAttribute2(node, "alt") || hasAttribute2(node, "src") || hasAttribute2(node, "srcset"));
17454
17617
  }
@@ -17566,7 +17729,7 @@ function serializeForExtraction($, root) {
17566
17729
  traverse(root, 0);
17567
17730
  return lines.map((l) => l.trim()).filter((l) => l.length > 0).join("");
17568
17731
  }
17569
- function isClickable($, el, context) {
17732
+ function isClickable(el, context) {
17570
17733
  if (context.hasPreMarked) {
17571
17734
  return el.attr(OPENSTEER_INTERACTIVE_ATTR) !== void 0;
17572
17735
  }
@@ -17600,21 +17763,17 @@ function isClickable($, el, context) {
17600
17763
  }
17601
17764
  return false;
17602
17765
  }
17603
- function cleanForExtraction(html) {
17766
+ function prepareExtractionSnapshotDom(html) {
17604
17767
  if (!html.trim()) {
17605
- return "";
17768
+ return void 0;
17606
17769
  }
17607
17770
  const $ = cheerio__namespace.load(html, { xmlMode: false });
17608
17771
  removeNoise($);
17609
17772
  removeComments($);
17610
17773
  markInlineSelfHiddenFallback($);
17611
17774
  pruneSelfHiddenNodes($);
17612
- const $clean = cheerio__namespace.load(
17613
- $.html().replace(/\n{2,}/g, "\n").trim(),
17614
- { xmlMode: false }
17615
- );
17616
- $clean("*").each(function stripAndRestoreExtractionAttrs() {
17617
- const el = $clean(this);
17775
+ $("*").each(function stripAndRestoreExtractionAttrs() {
17776
+ const el = $(this);
17618
17777
  const node = el[0];
17619
17778
  if (!node) {
17620
17779
  return;
@@ -17655,16 +17814,20 @@ function cleanForExtraction(html) {
17655
17814
  restoreBoundedAttr(el, "href", hrefValue);
17656
17815
  }
17657
17816
  });
17658
- flattenExtractionTree($clean);
17659
- const root = $clean.root()[0];
17817
+ flattenExtractionTree($);
17818
+ deduplicateImagesInDom($);
17819
+ return $;
17820
+ }
17821
+ function serializePreparedExtractionSnapshot($) {
17822
+ const root = $.root()[0];
17660
17823
  if (root === void 0) {
17661
17824
  return "";
17662
17825
  }
17663
- return deduplicateImages(serializeForExtraction($clean, root));
17826
+ return serializeForExtraction($, root);
17664
17827
  }
17665
- function cleanForAction(html) {
17828
+ function prepareActionSnapshotDom(html) {
17666
17829
  if (!html.trim()) {
17667
- return "";
17830
+ return void 0;
17668
17831
  }
17669
17832
  const $ = cheerio__namespace.load(html, { xmlMode: false });
17670
17833
  removeNoise($);
@@ -17673,13 +17836,12 @@ function cleanForAction(html) {
17673
17836
  pruneSelfHiddenNodes($);
17674
17837
  const clickableMark = "data-clickable-marker";
17675
17838
  const indicatorMark = "data-keep-indicator";
17676
- const branchMark = "data-keep-branch";
17677
17839
  const context = {
17678
17840
  hasPreMarked: $(`[${OPENSTEER_INTERACTIVE_ATTR}]`).length > 0
17679
17841
  };
17680
17842
  $("*").each(function markClickables() {
17681
17843
  const el = $(this);
17682
- if (isClickable($, el, context)) {
17844
+ if (isClickable(el, context)) {
17683
17845
  el.attr(clickableMark, "1");
17684
17846
  }
17685
17847
  });
@@ -17709,25 +17871,7 @@ function cleanForAction(html) {
17709
17871
  el.remove();
17710
17872
  }
17711
17873
  });
17712
- $(`[${clickableMark}], [${indicatorMark}]`).each(function markBranches() {
17713
- let current = $(this).parent();
17714
- while (current.length > 0) {
17715
- const node = current[0];
17716
- if (!node || node.type !== "tag") {
17717
- break;
17718
- }
17719
- const ancestor = current;
17720
- const tag = (node.tagName || "").toLowerCase();
17721
- if (ROOT_TAGS.has(tag) || ancestor.attr(clickableMark) !== void 0) {
17722
- break;
17723
- }
17724
- if (!isBoundaryTag(tag)) {
17725
- ancestor.attr(branchMark, "1");
17726
- }
17727
- current = ancestor.parent();
17728
- }
17729
- });
17730
- for (const node of getElementsByDepthDescending($)) {
17874
+ for (const node of getElementsInReverseDocumentOrder($)) {
17731
17875
  const el = $(node);
17732
17876
  const tag = (node.tagName || "").toLowerCase();
17733
17877
  if (ROOT_TAGS.has(tag) || isBoundaryTag(tag)) {
@@ -17736,17 +17880,7 @@ function cleanForAction(html) {
17736
17880
  if (el.attr(clickableMark) !== void 0 || el.attr(indicatorMark) !== void 0) {
17737
17881
  continue;
17738
17882
  }
17739
- const insideClickable = hasMarkedAncestor(el, clickableMark);
17740
- const preserveBranch = el.attr(branchMark) !== void 0;
17741
17883
  const hasContent = hasElementChildren(node) || hasDirectText(node);
17742
- if (insideClickable || preserveBranch) {
17743
- if (!hasContent) {
17744
- el.remove();
17745
- } else {
17746
- unwrapActionNode($, el);
17747
- }
17748
- continue;
17749
- }
17750
17884
  if (!hasContent) {
17751
17885
  el.remove();
17752
17886
  continue;
@@ -17847,13 +17981,20 @@ function cleanForAction(html) {
17847
17981
  }
17848
17982
  el.removeAttr(clickableMark);
17849
17983
  el.removeAttr(indicatorMark);
17850
- el.removeAttr(branchMark);
17851
17984
  el.removeAttr(OPENSTEER_INTERACTIVE_ATTR);
17852
17985
  el.removeAttr(OPENSTEER_HIDDEN_ATTR);
17853
17986
  el.removeAttr(OPENSTEER_SCROLLABLE_ATTR);
17854
17987
  el.removeAttr(OPENSTEER_SELF_HIDDEN_ATTR);
17855
17988
  });
17856
- return compactHtml(deduplicateImages($.html()));
17989
+ deduplicateImagesInDom($);
17990
+ return $;
17991
+ }
17992
+ function serializePreparedActionSnapshot($) {
17993
+ const normalized = compactHtml($.html());
17994
+ if (normalized.length === 0) {
17995
+ return "";
17996
+ }
17997
+ return cheerio__namespace.load(normalized, { xmlMode: false }).html();
17857
17998
  }
17858
17999
  var VOID_TAGS2 = /* @__PURE__ */ new Set([
17859
18000
  "area",
@@ -18076,27 +18217,32 @@ async function markLiveSnapshotSemantics(options) {
18076
18217
  const frames = await options.engine.listFrames({
18077
18218
  pageRef: options.pageRef
18078
18219
  });
18079
- for (const frame of frames) {
18080
- await evaluateFrameBestEffort(
18081
- options.engine,
18082
- frame.frameRef,
18083
- MARK_SNAPSHOT_SEMANTICS_SCRIPT,
18084
- SNAPSHOT_SEMANTIC_ARGS
18085
- );
18086
- }
18087
- return async () => {
18088
- for (const frame of frames) {
18089
- await evaluateFrameBestEffort(
18220
+ await Promise.all(
18221
+ frames.map(
18222
+ (frame) => evaluateFrameBestEffort(
18090
18223
  options.engine,
18091
18224
  frame.frameRef,
18092
- CLEAR_SNAPSHOT_SEMANTICS_SCRIPT,
18093
- CLEAR_SNAPSHOT_SEMANTIC_ARGS
18094
- );
18095
- }
18225
+ MARK_SNAPSHOT_SEMANTICS_SCRIPT,
18226
+ SNAPSHOT_SEMANTIC_ARGS
18227
+ )
18228
+ )
18229
+ );
18230
+ return async () => {
18231
+ await Promise.all(
18232
+ frames.map(
18233
+ (frame) => evaluateFrameBestEffort(
18234
+ options.engine,
18235
+ frame.frameRef,
18236
+ CLEAR_SNAPSHOT_SEMANTICS_SCRIPT,
18237
+ CLEAR_SNAPSHOT_SEMANTIC_ARGS
18238
+ )
18239
+ )
18240
+ );
18096
18241
  };
18097
18242
  }
18098
18243
 
18099
18244
  // ../runtime-core/src/sdk/snapshot/compiler.ts
18245
+ var EXTRACTION_SKIPPED_COUNTER_TAGS = /* @__PURE__ */ new Set(["html", "head", "body"]);
18100
18246
  var INTERNAL_SNAPSHOT_ATTRIBUTE_NAMES = /* @__PURE__ */ new Set([
18101
18247
  "c",
18102
18248
  OPENSTEER_BOUNDARY_ATTR,
@@ -18111,7 +18257,7 @@ var INTERNAL_SNAPSHOT_ATTRIBUTE_NAMES = /* @__PURE__ */ new Set([
18111
18257
  var MAX_LIVE_COUNTER_SYNC_ATTEMPTS = 4;
18112
18258
  var CLEAR_LIVE_COUNTERS_SCRIPT = `(({ sparseCounterAttr }) => {
18113
18259
  const walk = (root) => {
18114
- for (const child of root.children) {
18260
+ for (const child of Array.from(root?.children || [])) {
18115
18261
  child.removeAttribute("c");
18116
18262
  child.removeAttribute(sparseCounterAttr);
18117
18263
  walk(child);
@@ -18127,7 +18273,7 @@ var CLEAR_LIVE_COUNTERS_SCRIPT = `(({ sparseCounterAttr }) => {
18127
18273
  var ASSIGN_SPARSE_COUNTERS_SCRIPT = `(({ sparseCounterAttr, startCounter }) => {
18128
18274
  let counter = startCounter;
18129
18275
  const walk = (root) => {
18130
- for (const child of root.children) {
18276
+ for (const child of Array.from(root?.children || [])) {
18131
18277
  child.setAttribute(sparseCounterAttr, String(counter++));
18132
18278
  walk(child);
18133
18279
  if (child.shadowRoot) {
@@ -18141,7 +18287,7 @@ var ASSIGN_SPARSE_COUNTERS_SCRIPT = `(({ sparseCounterAttr, startCounter }) => {
18141
18287
  })`;
18142
18288
  var APPLY_DENSE_COUNTERS_SCRIPT = `(({ sparseCounterAttr, mapping }) => {
18143
18289
  const walk = (root) => {
18144
- for (const child of root.children) {
18290
+ for (const child of Array.from(root?.children || [])) {
18145
18291
  child.removeAttribute("c");
18146
18292
  const sparse = child.getAttribute(sparseCounterAttr);
18147
18293
  if (sparse !== null) {
@@ -18204,20 +18350,22 @@ function ensureSparseCountersForAllRecords(counterRecords) {
18204
18350
  async function clearOpensteerLiveCounters(engine, pageRef) {
18205
18351
  const frames = await engine.listFrames({ pageRef });
18206
18352
  const failures = [];
18207
- for (const frame of frames) {
18208
- try {
18209
- await engine.evaluateFrame({
18210
- frameRef: frame.frameRef,
18211
- script: CLEAR_LIVE_COUNTERS_SCRIPT,
18212
- args: [{ sparseCounterAttr: OPENSTEER_SPARSE_COUNTER_ATTR }]
18213
- });
18214
- } catch (error) {
18215
- if (isDetachedFrameSyncError(error)) {
18216
- continue;
18353
+ await Promise.all(
18354
+ frames.map(async (frame) => {
18355
+ try {
18356
+ await engine.evaluateFrame({
18357
+ frameRef: frame.frameRef,
18358
+ script: CLEAR_LIVE_COUNTERS_SCRIPT,
18359
+ args: [{ sparseCounterAttr: OPENSTEER_SPARSE_COUNTER_ATTR }]
18360
+ });
18361
+ } catch (error) {
18362
+ if (isDetachedFrameSyncError(error)) {
18363
+ return;
18364
+ }
18365
+ failures.push(`frame ${frame.frameRef} could not be cleared (${describeError(error)}).`);
18217
18366
  }
18218
- failures.push(`frame ${frame.frameRef} could not be cleared (${describeError(error)}).`);
18219
- }
18220
- }
18367
+ })
18368
+ );
18221
18369
  if (failures.length > 0) {
18222
18370
  throw buildLiveCounterSyncError("clear live counters", failures);
18223
18371
  }
@@ -18266,25 +18414,29 @@ async function syncDenseCountersToLiveDom(engine, pageRef, sparseToDirectMapping
18266
18414
  denseCounter
18267
18415
  ])
18268
18416
  );
18269
- for (const frame of frames) {
18270
- try {
18271
- await engine.evaluateFrame({
18272
- frameRef: frame.frameRef,
18273
- script: APPLY_DENSE_COUNTERS_SCRIPT,
18274
- args: [
18275
- {
18276
- sparseCounterAttr: OPENSTEER_SPARSE_COUNTER_ATTR,
18277
- mapping: mappingObj
18278
- }
18279
- ]
18280
- });
18281
- } catch (error) {
18282
- if (isDetachedFrameSyncError(error)) {
18283
- continue;
18417
+ await Promise.all(
18418
+ frames.map(async (frame) => {
18419
+ try {
18420
+ await engine.evaluateFrame({
18421
+ frameRef: frame.frameRef,
18422
+ script: APPLY_DENSE_COUNTERS_SCRIPT,
18423
+ args: [
18424
+ {
18425
+ sparseCounterAttr: OPENSTEER_SPARSE_COUNTER_ATTR,
18426
+ mapping: mappingObj
18427
+ }
18428
+ ]
18429
+ });
18430
+ } catch (error) {
18431
+ if (isDetachedFrameSyncError(error)) {
18432
+ return;
18433
+ }
18434
+ failures.push(
18435
+ `frame ${frame.frameRef} could not be synchronized (${describeError(error)}).`
18436
+ );
18284
18437
  }
18285
- failures.push(`frame ${frame.frameRef} could not be synchronized (${describeError(error)}).`);
18286
- }
18287
- }
18438
+ })
18439
+ );
18288
18440
  if (failures.length > 0) {
18289
18441
  throw buildLiveCounterSyncError("synchronize dense counters", failures);
18290
18442
  }
@@ -18302,26 +18454,26 @@ async function compileOpensteerSnapshot(options) {
18302
18454
  await clearOpensteerLiveCounters(options.engine, options.pageRef);
18303
18455
  await assignSparseCountersToLiveDom(options.engine, options.pageRef);
18304
18456
  const pageInfo = await options.engine.getPageInfo({ pageRef: options.pageRef });
18305
- const mainSnapshot = await getMainDocumentSnapshot(options.engine, options.pageRef);
18306
- const snapshotsByDocumentRef = await collectDocumentSnapshots(options.engine, mainSnapshot);
18457
+ const { mainSnapshot, snapshotsByDocumentRef } = await getPageDocumentSnapshots(
18458
+ options.engine,
18459
+ options.pageRef
18460
+ );
18307
18461
  await cleanupLiveSemantics();
18308
18462
  cleanupLiveSemantics = async () => {
18309
18463
  };
18310
- const snapshotIndices = /* @__PURE__ */ new Map();
18311
18464
  const renderedNodes = /* @__PURE__ */ new Map();
18312
18465
  const rawHtml = renderDocumentSnapshot(
18313
18466
  mainSnapshot.documentRef,
18314
18467
  snapshotsByDocumentRef,
18315
- snapshotIndices,
18316
18468
  renderedNodes,
18317
18469
  {
18318
18470
  iframeDepth: 0,
18319
18471
  shadowDepth: 0
18320
18472
  }
18321
18473
  );
18322
- const cleanedHtml = options.mode === "extraction" ? cleanForExtraction(rawHtml) : cleanForAction(rawHtml);
18323
- const compiledHtml = assignCounters(cleanedHtml, renderedNodes);
18324
- const finalHtml = options.mode === "extraction" ? unwrapExtractionHtml(compiledHtml.html) : compiledHtml.html;
18474
+ const preparedSnapshotDom = options.mode === "extraction" ? prepareExtractionSnapshotDom(rawHtml) : prepareActionSnapshotDom(rawHtml);
18475
+ const compiledHtml = assignCountersInDom(preparedSnapshotDom, renderedNodes, options.mode);
18476
+ const finalHtml = preparedSnapshotDom === void 0 ? "" : options.mode === "extraction" ? serializePreparedExtractionSnapshot(preparedSnapshotDom) : serializePreparedActionSnapshot(preparedSnapshotDom);
18325
18477
  ensureSparseCountersForAllRecords(compiledHtml.counterRecords);
18326
18478
  await syncDenseCountersToLiveDom(
18327
18479
  options.engine,
@@ -18356,6 +18508,25 @@ async function getMainDocumentSnapshot(engine, pageRef) {
18356
18508
  }
18357
18509
  return engine.getDomSnapshot({ frameRef: mainFrame.frameRef });
18358
18510
  }
18511
+ async function getPageDocumentSnapshots(engine, pageRef) {
18512
+ const bundleEngine = engine;
18513
+ const bundledSnapshots = await bundleEngine.getPageDomSnapshots?.({ pageRef });
18514
+ if (bundledSnapshots && bundledSnapshots.length > 0) {
18515
+ const mainSnapshot2 = bundledSnapshots.find((snapshot) => snapshot.parentDocumentRef === void 0) ?? bundledSnapshots[0];
18516
+ return {
18517
+ mainSnapshot: mainSnapshot2,
18518
+ snapshotsByDocumentRef: new Map(
18519
+ bundledSnapshots.map((snapshot) => [snapshot.documentRef, snapshot])
18520
+ )
18521
+ };
18522
+ }
18523
+ const mainSnapshot = await getMainDocumentSnapshot(engine, pageRef);
18524
+ const snapshotsByDocumentRef = await collectDocumentSnapshots(engine, mainSnapshot);
18525
+ return {
18526
+ mainSnapshot,
18527
+ snapshotsByDocumentRef
18528
+ };
18529
+ }
18359
18530
  async function collectDocumentSnapshots(engine, mainSnapshot) {
18360
18531
  const snapshotsByDocumentRef = /* @__PURE__ */ new Map([
18361
18532
  [mainSnapshot.documentRef, mainSnapshot]
@@ -18374,7 +18545,7 @@ async function collectDocumentSnapshots(engine, mainSnapshot) {
18374
18545
  }
18375
18546
  return snapshotsByDocumentRef;
18376
18547
  }
18377
- function renderDocumentSnapshot(documentRef, snapshotsByDocumentRef, snapshotIndices, renderedNodes, depth) {
18548
+ function renderDocumentSnapshot(documentRef, snapshotsByDocumentRef, renderedNodes, depth) {
18378
18549
  const snapshot = snapshotsByDocumentRef.get(documentRef);
18379
18550
  if (!snapshot) {
18380
18551
  return "";
@@ -18386,17 +18557,9 @@ function renderDocumentSnapshot(documentRef, snapshotsByDocumentRef, snapshotInd
18386
18557
  `snapshot ${snapshot.documentRef} is missing root node ${String(snapshot.rootSnapshotNodeId)}`
18387
18558
  );
18388
18559
  }
18389
- return renderNode(
18390
- snapshot,
18391
- rootNode,
18392
- nodesById,
18393
- snapshotsByDocumentRef,
18394
- snapshotIndices,
18395
- renderedNodes,
18396
- depth
18397
- );
18560
+ return renderNode(snapshot, rootNode, nodesById, snapshotsByDocumentRef, renderedNodes, depth);
18398
18561
  }
18399
- function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotIndices, renderedNodes, depth) {
18562
+ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, renderedNodes, depth) {
18400
18563
  if (node.nodeType === 3) {
18401
18564
  return escapeHtml(node.nodeValue || node.textContent || "");
18402
18565
  }
@@ -18404,56 +18567,26 @@ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotI
18404
18567
  return "";
18405
18568
  }
18406
18569
  if (node.nodeType === 9 || node.nodeType === 11) {
18407
- return renderChildren(
18408
- snapshot,
18409
- node,
18410
- nodesById,
18411
- snapshotsByDocumentRef,
18412
- snapshotIndices,
18413
- renderedNodes,
18414
- depth
18415
- );
18570
+ return renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, renderedNodes, depth);
18416
18571
  }
18417
18572
  if (node.nodeType !== 1) {
18418
- return renderChildren(
18419
- snapshot,
18420
- node,
18421
- nodesById,
18422
- snapshotsByDocumentRef,
18423
- snapshotIndices,
18424
- renderedNodes,
18425
- depth
18426
- );
18573
+ return renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, renderedNodes, depth);
18427
18574
  }
18428
18575
  const tagName = normalizeTagName(node.nodeName);
18429
18576
  if (isPseudoElementTagName(tagName)) {
18430
- return renderChildren(
18431
- snapshot,
18432
- node,
18433
- nodesById,
18434
- snapshotsByDocumentRef,
18435
- snapshotIndices,
18436
- renderedNodes,
18437
- depth
18438
- );
18577
+ return renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, renderedNodes, depth);
18439
18578
  }
18440
18579
  if ((depth.iframeDepth > 0 || depth.shadowDepth > 0) && (tagName === "html" || tagName === "head" || tagName === "body")) {
18441
- return renderChildren(
18442
- snapshot,
18443
- node,
18444
- nodesById,
18445
- snapshotsByDocumentRef,
18446
- snapshotIndices,
18447
- renderedNodes,
18448
- depth
18449
- );
18580
+ return renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, renderedNodes, depth);
18450
18581
  }
18451
18582
  const snapshotAttributes = normalizeNodeAttributes(node.attributes);
18583
+ const snapshotAttributeIndex = indexNodeAttributes(snapshotAttributes);
18452
18584
  const authoredAttributes = stripInternalSnapshotAttributes(snapshotAttributes);
18585
+ const authoredAttributeIndex = indexNodeAttributes(authoredAttributes);
18453
18586
  const attributes = [...authoredAttributes];
18454
- const subtreeHidden = hasAttribute3(snapshotAttributes, OPENSTEER_HIDDEN_ATTR) || isLikelySubtreeHidden(node);
18455
- const selfHidden = !subtreeHidden && (hasAttribute3(snapshotAttributes, OPENSTEER_SELF_HIDDEN_ATTR) || isLikelySelfHidden(node, nodesById));
18456
- const interactive = !subtreeHidden && !selfHidden && (hasAttribute3(snapshotAttributes, OPENSTEER_INTERACTIVE_ATTR) || isLikelyInteractive(tagName, node, authoredAttributes));
18587
+ const subtreeHidden = snapshotAttributeIndex.has(OPENSTEER_HIDDEN_ATTR) || isLikelySubtreeHidden(node);
18588
+ const selfHidden = !subtreeHidden && (snapshotAttributeIndex.has(OPENSTEER_SELF_HIDDEN_ATTR) || isLikelySelfHidden(node, nodesById));
18589
+ const interactive = !subtreeHidden && !selfHidden && (snapshotAttributeIndex.has(OPENSTEER_INTERACTIVE_ATTR) || isLikelyInteractive(tagName, node, authoredAttributes, authoredAttributeIndex));
18457
18590
  if (interactive) {
18458
18591
  attributes.push({ name: OPENSTEER_INTERACTIVE_ATTR, value: "1" });
18459
18592
  }
@@ -18462,7 +18595,7 @@ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotI
18462
18595
  } else if (selfHidden) {
18463
18596
  attributes.push({ name: OPENSTEER_SELF_HIDDEN_ATTR, value: "1" });
18464
18597
  }
18465
- const sparseCounter = findAttributeValue(snapshotAttributes, OPENSTEER_SPARSE_COUNTER_ATTR);
18598
+ const sparseCounter = snapshotAttributeIndex.get(OPENSTEER_SPARSE_COUNTER_ATTR);
18466
18599
  if (sparseCounter !== void 0) {
18467
18600
  attributes.push({ name: OPENSTEER_SPARSE_COUNTER_ATTR, value: sparseCounter });
18468
18601
  }
@@ -18473,21 +18606,18 @@ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotI
18473
18606
  const syntheticNodeId = buildSyntheticNodeId(snapshot, node);
18474
18607
  attributes.push({ name: OPENSTEER_NODE_ID_ATTR, value: syntheticNodeId });
18475
18608
  renderedNodes.set(syntheticNodeId, {
18476
- locator: createNodeLocator(snapshot.documentRef, snapshot.documentEpoch, node.nodeRef),
18477
- anchor: buildSnapshotElementAnchor(snapshot, node, snapshotsByDocumentRef, snapshotIndices),
18478
18609
  pageRef: snapshot.pageRef,
18479
18610
  frameRef: snapshot.frameRef,
18480
18611
  documentRef: snapshot.documentRef,
18481
18612
  documentEpoch: snapshot.documentEpoch,
18482
18613
  nodeRef: node.nodeRef,
18483
18614
  tagName: tagName.toUpperCase(),
18484
- pathHint: buildPathHint(tagName, authoredAttributes),
18485
- ...buildTextSnippet(node.textContent) === void 0 ? {} : { text: buildTextSnippet(node.textContent) },
18486
18615
  ...authoredAttributes.length === 0 ? {} : { attributes: authoredAttributes },
18487
18616
  iframeDepth: depth.iframeDepth,
18488
18617
  shadowDepth: depth.shadowDepth,
18489
18618
  interactive,
18490
- liveCounterSyncEligible: isLiveCounterSyncEligible(node, nodesById)
18619
+ liveCounterSyncEligible: isLiveCounterSyncEligible(node, nodesById),
18620
+ ...node.textContent === void 0 ? {} : { textContent: node.textContent }
18491
18621
  });
18492
18622
  }
18493
18623
  const attributeText = attributesToHtml(attributes);
@@ -18496,7 +18626,6 @@ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotI
18496
18626
  node,
18497
18627
  nodesById,
18498
18628
  snapshotsByDocumentRef,
18499
- snapshotIndices,
18500
18629
  renderedNodes,
18501
18630
  depth
18502
18631
  );
@@ -18507,7 +18636,6 @@ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotI
18507
18636
  const iframeHtml = renderDocumentSnapshot(
18508
18637
  node.contentDocumentRef,
18509
18638
  snapshotsByDocumentRef,
18510
- snapshotIndices,
18511
18639
  renderedNodes,
18512
18640
  {
18513
18641
  iframeDepth: depth.iframeDepth + 1,
@@ -18519,7 +18647,7 @@ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotI
18519
18647
  }
18520
18648
  return `${elementHtml}<${OPENSTEER_IFRAME_BOUNDARY_TAG} ${OPENSTEER_BOUNDARY_ATTR}="iframe">${iframeHtml}</${OPENSTEER_IFRAME_BOUNDARY_TAG}>`;
18521
18649
  }
18522
- function renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotIndices, renderedNodes, depth) {
18650
+ function renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, renderedNodes, depth) {
18523
18651
  const regularChildren = [];
18524
18652
  const shadowChildren = [];
18525
18653
  for (const childSnapshotNodeId of node.childSnapshotNodeIds) {
@@ -18536,18 +18664,10 @@ function renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, snaps
18536
18664
  const chunks = [];
18537
18665
  if (shadowChildren.length > 0) {
18538
18666
  const shadowHtml = shadowChildren.map(
18539
- (child) => renderNode(
18540
- snapshot,
18541
- child,
18542
- nodesById,
18543
- snapshotsByDocumentRef,
18544
- snapshotIndices,
18545
- renderedNodes,
18546
- {
18547
- iframeDepth: depth.iframeDepth,
18548
- shadowDepth: depth.shadowDepth + 1
18549
- }
18550
- )
18667
+ (child) => renderNode(snapshot, child, nodesById, snapshotsByDocumentRef, renderedNodes, {
18668
+ iframeDepth: depth.iframeDepth,
18669
+ shadowDepth: depth.shadowDepth + 1
18670
+ })
18551
18671
  ).join("");
18552
18672
  chunks.push(
18553
18673
  `<${OPENSTEER_SHADOW_BOUNDARY_TAG} ${OPENSTEER_BOUNDARY_ATTR}="shadow">${shadowHtml}</${OPENSTEER_SHADOW_BOUNDARY_TAG}>`
@@ -18555,24 +18675,21 @@ function renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, snaps
18555
18675
  }
18556
18676
  for (const child of regularChildren) {
18557
18677
  chunks.push(
18558
- renderNode(
18559
- snapshot,
18560
- child,
18561
- nodesById,
18562
- snapshotsByDocumentRef,
18563
- snapshotIndices,
18564
- renderedNodes,
18565
- depth
18566
- )
18678
+ renderNode(snapshot, child, nodesById, snapshotsByDocumentRef, renderedNodes, depth)
18567
18679
  );
18568
18680
  }
18569
18681
  return chunks.join("");
18570
18682
  }
18571
- function assignCounters(cleanedHtml, renderedNodes) {
18572
- const $ = cheerio__namespace.load(cleanedHtml, { xmlMode: false });
18683
+ function assignCountersInDom($, renderedNodes, mode) {
18573
18684
  const counterRecords = /* @__PURE__ */ new Map();
18574
18685
  const sparseToDirectMapping = /* @__PURE__ */ new Map();
18575
18686
  let nextCounter = 1;
18687
+ if (!$) {
18688
+ return {
18689
+ counterRecords,
18690
+ sparseToDirectMapping
18691
+ };
18692
+ }
18576
18693
  $("*").each(function assignElementCounter() {
18577
18694
  const el = $(this);
18578
18695
  const syntheticNodeId = el.attr(OPENSTEER_NODE_ID_ATTR);
@@ -18584,14 +18701,24 @@ function assignCounters(cleanedHtml, renderedNodes) {
18584
18701
  if (!rendered) {
18585
18702
  return;
18586
18703
  }
18704
+ if (mode === "extraction" && EXTRACTION_SKIPPED_COUNTER_TAGS.has(rendered.tagName.toLowerCase())) {
18705
+ el.removeAttr(OPENSTEER_SPARSE_COUNTER_ATTR);
18706
+ return;
18707
+ }
18587
18708
  const rawSparseCounter = el.attr(OPENSTEER_SPARSE_COUNTER_ATTR);
18588
18709
  el.removeAttr(OPENSTEER_SPARSE_COUNTER_ATTR);
18589
18710
  const sparseCounter = rawSparseCounter ? Number.parseInt(rawSparseCounter, 10) : void 0;
18711
+ const replayableSparseCounter = typeof sparseCounter === "number" && Number.isFinite(sparseCounter) ? sparseCounter : void 0;
18712
+ if (rendered.liveCounterSyncEligible && replayableSparseCounter === void 0) {
18713
+ return;
18714
+ }
18590
18715
  const counter = nextCounter++;
18591
18716
  el.attr("c", String(counter));
18592
- if (sparseCounter !== void 0 && Number.isFinite(sparseCounter)) {
18593
- sparseToDirectMapping.set(sparseCounter, counter);
18717
+ if (replayableSparseCounter !== void 0) {
18718
+ sparseToDirectMapping.set(replayableSparseCounter, counter);
18594
18719
  }
18720
+ const pathHint = buildPathHint(rendered.tagName.toLowerCase(), rendered.attributes ?? []);
18721
+ const text = buildTextSnippet(rendered.textContent);
18595
18722
  counterRecords.set(counter, {
18596
18723
  element: counter,
18597
18724
  pageRef: rendered.pageRef,
@@ -18600,20 +18727,17 @@ function assignCounters(cleanedHtml, renderedNodes) {
18600
18727
  documentEpoch: rendered.documentEpoch,
18601
18728
  nodeRef: rendered.nodeRef,
18602
18729
  tagName: rendered.tagName,
18603
- pathHint: rendered.pathHint,
18604
- ...rendered.text === void 0 ? {} : { text: rendered.text },
18730
+ pathHint,
18731
+ ...text === void 0 ? {} : { text },
18605
18732
  ...rendered.attributes === void 0 ? {} : { attributes: rendered.attributes },
18606
18733
  iframeDepth: rendered.iframeDepth,
18607
18734
  shadowDepth: rendered.shadowDepth,
18608
18735
  interactive: rendered.interactive,
18609
18736
  liveCounterSyncEligible: rendered.liveCounterSyncEligible,
18610
- locator: rendered.locator,
18611
- anchor: rendered.anchor,
18612
- ...sparseCounter !== void 0 && Number.isFinite(sparseCounter) ? { sparseCounter } : {}
18737
+ ...replayableSparseCounter === void 0 ? {} : { sparseCounter: replayableSparseCounter }
18613
18738
  });
18614
18739
  });
18615
18740
  return {
18616
- html: $.html(),
18617
18741
  counterRecords,
18618
18742
  sparseToDirectMapping
18619
18743
  };
@@ -18687,28 +18811,28 @@ function isLikelySelfHidden(node, nodesById) {
18687
18811
  }
18688
18812
  return !hasVisibleOutOfFlowChild(node, nodesById);
18689
18813
  }
18690
- function isLikelyInteractive(tagName, node, attributes) {
18814
+ function isLikelyInteractive(tagName, node, attributes, attributeIndex) {
18691
18815
  if (NATIVE_INTERACTIVE_TAGS.has(tagName)) {
18692
- if (tagName === "input" && findAttributeValue(attributes, "type")?.toLowerCase() === "hidden") {
18816
+ if (tagName === "input" && attributeIndex.get("type")?.toLowerCase() === "hidden") {
18693
18817
  return false;
18694
18818
  }
18695
18819
  if (tagName !== "a") {
18696
18820
  return true;
18697
18821
  }
18698
18822
  }
18699
- if (tagName === "a" && findAttributeValue(attributes, "href") !== void 0) {
18823
+ if (tagName === "a" && attributeIndex.has("href")) {
18700
18824
  return true;
18701
18825
  }
18702
- 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) {
18826
+ if (attributeIndex.has("onclick") || attributeIndex.has("onmousedown") || attributeIndex.has("onmouseup") || attributeIndex.has("data-action") || attributeIndex.has("data-click") || attributeIndex.has("data-toggle")) {
18703
18827
  return true;
18704
18828
  }
18705
- if (hasNonNegativeTabIndex(findAttributeValue(attributes, "tabindex"))) {
18829
+ if (hasNonNegativeTabIndex(attributeIndex.get("tabindex"))) {
18706
18830
  return true;
18707
18831
  }
18708
- if (findAttributeValue(attributes, "contenteditable")?.toLowerCase() === "true") {
18832
+ if (attributeIndex.get("contenteditable")?.toLowerCase() === "true") {
18709
18833
  return true;
18710
18834
  }
18711
- const role = findAttributeValue(attributes, "role")?.toLowerCase();
18835
+ const role = attributeIndex.get("role")?.toLowerCase();
18712
18836
  return role !== void 0 && INTERACTIVE_ROLE_SET.has(role);
18713
18837
  }
18714
18838
  function hasVisibleOutOfFlowChild(node, nodesById) {
@@ -18771,14 +18895,6 @@ function parseOpacity(value) {
18771
18895
  const parsed = Number.parseFloat(value);
18772
18896
  return Number.isFinite(parsed) ? parsed : Number.NaN;
18773
18897
  }
18774
- function hasAttribute3(attributes, name) {
18775
- const normalizedName = name.toLowerCase();
18776
- return attributes.some((attribute) => attribute.name.toLowerCase() === normalizedName);
18777
- }
18778
- function unwrapExtractionHtml(html) {
18779
- const $ = cheerio__namespace.load(html, { xmlMode: false });
18780
- return $("body").html()?.trim() || html;
18781
- }
18782
18898
  function buildSyntheticNodeId(snapshot, node) {
18783
18899
  return `${snapshot.documentRef}:${String(snapshot.documentEpoch)}:${String(node.snapshotNodeId)}`;
18784
18900
  }
@@ -18825,6 +18941,13 @@ function findAttributeValue(attributes, name) {
18825
18941
  const normalizedName = name.toLowerCase();
18826
18942
  return attributes.find((attribute) => attribute.name.toLowerCase() === normalizedName)?.value;
18827
18943
  }
18944
+ function indexNodeAttributes(attributes) {
18945
+ const indexed = /* @__PURE__ */ new Map();
18946
+ for (const attribute of attributes) {
18947
+ indexed.set(attribute.name.toLowerCase(), attribute.value);
18948
+ }
18949
+ return indexed;
18950
+ }
18828
18951
  function attributesToHtml(attributes) {
18829
18952
  if (attributes.length === 0) {
18830
18953
  return "";
@@ -18834,56 +18957,6 @@ function attributesToHtml(attributes) {
18834
18957
  function escapeAttribute(value) {
18835
18958
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
18836
18959
  }
18837
- function buildSnapshotElementAnchor(snapshot, node, snapshotsByDocumentRef, snapshotIndices) {
18838
- const index = getSnapshotIndex(snapshot.documentRef, snapshotsByDocumentRef, snapshotIndices);
18839
- const localAnchor = buildLocalStructuralElementAnchor(index, node);
18840
- return prefixIframeContext(snapshot, localAnchor, snapshotsByDocumentRef, snapshotIndices);
18841
- }
18842
- function prefixIframeContext(snapshot, localPath, snapshotsByDocumentRef, snapshotIndices) {
18843
- if (snapshot.parentDocumentRef === void 0) {
18844
- return sanitizeStructuralElementAnchor(localPath);
18845
- }
18846
- const parentSnapshot = snapshotsByDocumentRef.get(snapshot.parentDocumentRef);
18847
- if (!parentSnapshot) {
18848
- throw new Error(
18849
- `document ${snapshot.documentRef} has parent ${snapshot.parentDocumentRef} but no parent snapshot`
18850
- );
18851
- }
18852
- const parentIndex = getSnapshotIndex(
18853
- parentSnapshot.documentRef,
18854
- snapshotsByDocumentRef,
18855
- snapshotIndices
18856
- );
18857
- const iframeHost = findIframeHostNode(parentIndex, snapshot.documentRef);
18858
- if (!iframeHost) {
18859
- throw new Error(
18860
- `document ${snapshot.documentRef} has parent ${snapshot.parentDocumentRef} but no iframe host`
18861
- );
18862
- }
18863
- const hostPath = buildSnapshotElementAnchor(
18864
- parentSnapshot,
18865
- iframeHost,
18866
- snapshotsByDocumentRef,
18867
- snapshotIndices
18868
- );
18869
- return sanitizeStructuralElementAnchor({
18870
- context: [...hostPath.context, { kind: "iframe", host: hostPath.nodes }, ...localPath.context],
18871
- nodes: localPath.nodes
18872
- });
18873
- }
18874
- function getSnapshotIndex(documentRef, snapshotsByDocumentRef, snapshotIndices) {
18875
- const existing = snapshotIndices.get(documentRef);
18876
- if (existing) {
18877
- return existing;
18878
- }
18879
- const snapshot = snapshotsByDocumentRef.get(documentRef);
18880
- if (!snapshot) {
18881
- throw new Error(`missing DOM snapshot for ${documentRef}`);
18882
- }
18883
- const index = createSnapshotIndex(snapshot);
18884
- snapshotIndices.set(documentRef, index);
18885
- return index;
18886
- }
18887
18960
  function escapeHtml(value) {
18888
18961
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
18889
18962
  }
@@ -20096,7 +20169,7 @@ var OpensteerSessionRuntime = class {
20096
20169
  options
20097
20170
  );
20098
20171
  }
20099
- return this.readSessionState();
20172
+ return this.readNavigationSummary();
20100
20173
  }
20101
20174
  const startedAt = Date.now();
20102
20175
  const root = await this.ensureRoot();
@@ -20117,7 +20190,8 @@ var OpensteerSessionRuntime = class {
20117
20190
  openedSessionRef = sessionRef;
20118
20191
  const createdPage = await timeout.runStep(
20119
20192
  () => engine.createPage({
20120
- sessionRef
20193
+ sessionRef,
20194
+ ...input.url === void 0 ? {} : { url: input.url }
20121
20195
  })
20122
20196
  );
20123
20197
  openedPageRef = createdPage.data.pageRef;
@@ -20127,18 +20201,19 @@ var OpensteerSessionRuntime = class {
20127
20201
  await timeout.runStep(() => this.ensureSemantics());
20128
20202
  let frameRef2 = createdPage.frameRef;
20129
20203
  if (input.url !== void 0) {
20130
- const navigation = await this.navigatePage(
20131
- {
20204
+ await timeout.runStep(
20205
+ () => settleWithPolicy(this.policy.settle, {
20132
20206
  operation: "session.open",
20207
+ trigger: "navigation",
20208
+ engine: this.requireEngine(),
20133
20209
  pageRef: createdPage.data.pageRef,
20134
- url: input.url
20135
- },
20136
- timeout
20210
+ signal: timeout.signal,
20211
+ remainingMs: timeout.remainingMs()
20212
+ })
20137
20213
  );
20138
- frameRef2 = navigation.data.mainFrame.frameRef;
20139
20214
  }
20140
20215
  return {
20141
- state: await timeout.runStep(() => this.readSessionState()),
20216
+ state: await timeout.runStep(() => this.readNavigationSummary()),
20142
20217
  frameRef: frameRef2
20143
20218
  };
20144
20219
  },
@@ -20231,7 +20306,11 @@ var OpensteerSessionRuntime = class {
20231
20306
  "page.new cannot use openerPageRef before a session exists"
20232
20307
  );
20233
20308
  }
20234
- return this.open(input.url === void 0 ? {} : { url: input.url }, options);
20309
+ const summary = await this.open(input.url === void 0 ? {} : { url: input.url }, options);
20310
+ return {
20311
+ pageRef: await this.ensurePageRef(),
20312
+ ...summary
20313
+ };
20235
20314
  }
20236
20315
  const startedAt = Date.now();
20237
20316
  try {
@@ -20246,7 +20325,7 @@ var OpensteerSessionRuntime = class {
20246
20325
  })
20247
20326
  );
20248
20327
  this.pageRef = created.data.pageRef;
20249
- return this.readSessionState();
20328
+ return this.readCreatedPageOutput(created.data.pageRef);
20250
20329
  },
20251
20330
  options
20252
20331
  );
@@ -20288,7 +20367,7 @@ var OpensteerSessionRuntime = class {
20288
20367
  () => this.requireEngine().activatePage({ pageRef: input.pageRef })
20289
20368
  );
20290
20369
  this.pageRef = input.pageRef;
20291
- return this.readSessionState();
20370
+ return this.readNavigationSummary(input.pageRef);
20292
20371
  },
20293
20372
  options
20294
20373
  );
@@ -20414,7 +20493,7 @@ var OpensteerSessionRuntime = class {
20414
20493
  timeout.throwIfAborted();
20415
20494
  return {
20416
20495
  navigation: navigation2,
20417
- state: await timeout.runStep(() => this.readSessionState())
20496
+ state: await timeout.runStep(() => this.readNavigationSummary(pageRef))
20418
20497
  };
20419
20498
  },
20420
20499
  (diagnostics) => {
@@ -21648,7 +21727,7 @@ var OpensteerSessionRuntime = class {
21648
21727
  let mutationCaptureDiagnostics;
21649
21728
  let boundaryDiagnostics;
21650
21729
  try {
21651
- const { artifacts, output } = await this.runMutationCapturedOperation(
21730
+ const { artifacts, output, result } = await this.runMutationCapturedOperation(
21652
21731
  "computer.execute",
21653
21732
  {
21654
21733
  ...input.captureNetwork === void 0 ? {} : { captureNetwork: input.captureNetwork },
@@ -21666,9 +21745,14 @@ var OpensteerSessionRuntime = class {
21666
21745
  await this.invalidateLiveSnapshotCounters([pageRef, output2.pageRef], timeout);
21667
21746
  this.pageRef = output2.pageRef;
21668
21747
  const artifacts2 = await this.persistComputerArtifacts(output2, timeout);
21748
+ const result2 = {
21749
+ ...await timeout.runStep(() => this.readNavigationSummary(output2.pageRef)),
21750
+ screenshot: artifacts2.screenshot
21751
+ };
21669
21752
  return {
21670
21753
  artifacts: { manifests: artifacts2.manifests },
21671
- output: artifacts2.output
21754
+ output: output2,
21755
+ result: result2
21672
21756
  };
21673
21757
  } catch (error) {
21674
21758
  boundaryDiagnostics ??= takeActionBoundaryDiagnostics(timeout.signal);
@@ -21705,7 +21789,7 @@ var OpensteerSessionRuntime = class {
21705
21789
  documentEpoch: output.screenshot.documentEpoch
21706
21790
  })
21707
21791
  });
21708
- return output;
21792
+ return result;
21709
21793
  } catch (error) {
21710
21794
  await this.appendTrace({
21711
21795
  operation: "computer.execute",
@@ -21853,8 +21937,9 @@ var OpensteerSessionRuntime = class {
21853
21937
  mutationCaptureDiagnostics = diagnostics;
21854
21938
  }
21855
21939
  );
21856
- const output = toOpensteerActionResult(executed.result);
21940
+ const output = toOpensteerActionResult(executed.result.resolved);
21857
21941
  const actionEvents = "events" in executed.result ? executed.result.events : void 0;
21942
+ const resolvedTarget = toOpensteerResolvedTarget2(executed.result.resolved);
21858
21943
  await this.appendTrace({
21859
21944
  operation,
21860
21945
  startedAt,
@@ -21862,8 +21947,13 @@ var OpensteerSessionRuntime = class {
21862
21947
  outcome: "ok",
21863
21948
  ...actionEvents === void 0 ? {} : { events: actionEvents },
21864
21949
  data: {
21865
- target: output.target,
21866
- ...output.point === void 0 ? {} : { point: output.point },
21950
+ target: resolvedTarget,
21951
+ ..."point" in executed.result && executed.result.point !== void 0 ? {
21952
+ point: {
21953
+ x: executed.result.point.x,
21954
+ y: executed.result.point.y
21955
+ }
21956
+ } : {},
21867
21957
  ...boundaryDiagnostics === void 0 ? {} : { settle: boundaryDiagnostics },
21868
21958
  ...buildMutationCaptureTraceData(mutationCaptureDiagnostics)
21869
21959
  },
@@ -23328,20 +23418,20 @@ var OpensteerSessionRuntime = class {
23328
23418
  throw error;
23329
23419
  }
23330
23420
  }
23331
- async readSessionState() {
23332
- const pageRef = await this.ensurePageRef();
23421
+ async readNavigationSummary(targetPageRef) {
23422
+ const pageRef = targetPageRef ?? await this.ensurePageRef();
23333
23423
  const pageInfo = await this.requireEngine().getPageInfo({ pageRef });
23334
- const sessionRef = this.sessionRef;
23335
- if (!sessionRef) {
23336
- throw new Error("Opensteer session is not initialized");
23337
- }
23338
23424
  return {
23339
- sessionRef,
23340
- pageRef,
23341
23425
  url: pageInfo.url,
23342
23426
  title: pageInfo.title
23343
23427
  };
23344
23428
  }
23429
+ async readCreatedPageOutput(pageRef) {
23430
+ return {
23431
+ pageRef,
23432
+ ...await this.readNavigationSummary(pageRef)
23433
+ };
23434
+ }
23345
23435
  async captureSnapshotArtifacts(pageRef, options, timeout) {
23346
23436
  const root = this.requireRoot();
23347
23437
  const mainFrame = await timeout.runStep(() => getMainFrame(this.requireEngine(), pageRef));
@@ -23413,12 +23503,12 @@ var OpensteerSessionRuntime = class {
23413
23503
  const screenshotPayload = manifestToExternalBinaryLocation(root.rootPath, screenshotManifest);
23414
23504
  return {
23415
23505
  manifests,
23416
- output: {
23417
- ...output,
23418
- screenshot: {
23419
- ...output.screenshot,
23420
- payload: screenshotPayload
23421
- }
23506
+ screenshot: {
23507
+ payload: screenshotPayload,
23508
+ format: output.screenshot.format,
23509
+ size: output.screenshot.size,
23510
+ coordinateSpace: output.screenshot.coordinateSpace,
23511
+ ...output.screenshot.clip === void 0 ? {} : { clip: output.screenshot.clip }
23422
23512
  }
23423
23513
  };
23424
23514
  }
@@ -24941,15 +25031,10 @@ function normalizeNamespace2(value) {
24941
25031
  const normalized = String(value ?? "default").trim();
24942
25032
  return normalized.length === 0 ? "default" : normalized;
24943
25033
  }
24944
- function toOpensteerActionResult(result) {
25034
+ function toOpensteerActionResult(target) {
24945
25035
  return {
24946
- target: toOpensteerResolvedTarget2(result.resolved),
24947
- ...result.point === void 0 ? {} : {
24948
- point: {
24949
- x: result.point.x,
24950
- y: result.point.y
24951
- }
24952
- }
25036
+ tagName: toOpensteerTagName2(target.node.nodeName),
25037
+ ...target.persist === void 0 ? {} : { persist: target.persist }
24953
25038
  };
24954
25039
  }
24955
25040
  function toOpensteerResolvedTarget2(target) {
@@ -24959,12 +25044,16 @@ function toOpensteerResolvedTarget2(target) {
24959
25044
  documentRef: target.documentRef,
24960
25045
  documentEpoch: target.documentEpoch,
24961
25046
  nodeRef: target.nodeRef,
24962
- tagName: target.node.nodeName.toUpperCase(),
25047
+ tagName: toOpensteerTagName2(target.node.nodeName),
24963
25048
  pathHint: buildPathSelectorHint(target.replayPath ?? target.anchor),
24964
25049
  ...target.persist === void 0 ? {} : { persist: target.persist },
24965
25050
  ...target.selectorUsed === void 0 ? {} : { selectorUsed: target.selectorUsed }
24966
25051
  };
24967
25052
  }
25053
+ function toOpensteerTagName2(nodeName) {
25054
+ const tagName = String(nodeName).trim().toLowerCase();
25055
+ return tagName.length === 0 ? "element" : tagName;
25056
+ }
24968
25057
  function normalizeOpensteerError(error) {
24969
25058
  return normalizeThrownOpensteerError2(error, "Unknown Opensteer runtime failure");
24970
25059
  }