opensteer 0.9.5 → 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 (35) hide show
  1. package/dist/{chunk-7LQL5YUR.js → chunk-3OHKIPBD.js} +571 -738
  2. package/dist/chunk-3OHKIPBD.js.map +1 -0
  3. package/dist/chunk-52UNH5UW.js +458 -0
  4. package/dist/chunk-52UNH5UW.js.map +1 -0
  5. package/dist/{chunk-GSCQQKZZ.js → chunk-PJXN7HED.js} +334 -18
  6. package/dist/chunk-PJXN7HED.js.map +1 -0
  7. package/dist/{chunk-ZRF7WMS3.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 +1421 -824
  12. package/dist/cli/bin.cjs.map +1 -1
  13. package/dist/cli/bin.js +286 -129
  14. package/dist/cli/bin.js.map +1 -1
  15. package/dist/index.cjs +1117 -703
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +58 -53
  18. package/dist/index.d.ts +58 -53
  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 +6815 -1272
  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-T2JENADR.js.map → opensteer-CY2QUJEG.js.map} +1 -1
  26. package/dist/{session-control-M3JD7ZKA.js → session-control-FIP6ZJLH.js} +4 -4
  27. package/dist/{session-control-M3JD7ZKA.js.map → session-control-FIP6ZJLH.js.map} +1 -1
  28. package/package.json +7 -7
  29. package/dist/chunk-7D45QUZ3.js +0 -332
  30. package/dist/chunk-7D45QUZ3.js.map +0 -1
  31. package/dist/chunk-7LQL5YUR.js.map +0 -1
  32. package/dist/chunk-GSCQQKZZ.js.map +0 -1
  33. package/dist/chunk-T5P2QGZ3.js.map +0 -1
  34. package/dist/chunk-ZRF7WMS3.js.map +0 -1
  35. package/dist/opensteer-T2JENADR.js +0 -6
@@ -1,5 +1,5 @@
1
- import { resolveFilesystemWorkspacePath, createFilesystemOpensteerWorkspace, DEFAULT_OPENSTEER_ENGINE, assertSupportedEngineOptions, normalizeObservabilityConfig, manifestToExternalBinaryLocation, normalizeObservationContext, OpensteerBrowserManager } from './chunk-GSCQQKZZ.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, normalizeObservationContext, OpensteerBrowserManager, isOpensteerProtocolError, toOpensteerError } from './chunk-PJXN7HED.js';
2
+ import { canonicalJsonString, toCanonicalJsonValue, readPersistedCloudSessionRecord, writePersistedSessionRecord, clearPersistedSessionRecord, resolveBrandUserDataDir, sha256Hex, getBrowserBrand, detectInstalledBrowserBrands, __require } from './chunk-U4BUCIZ4.js';
3
3
  import { selectAll } from 'css-select';
4
4
  import { createHash, randomUUID, pbkdf2Sync, createDecipheriv } from 'crypto';
5
5
  import { existsSync, readFileSync } from 'fs';
@@ -208,9 +208,6 @@ function isBrowserCoreError(value) {
208
208
  return value instanceof BrowserCoreError;
209
209
  }
210
210
 
211
- // ../browser-core/src/cdp-visual-stability.ts
212
- var DEFAULT_VISUAL_STABILITY_SETTLE_MS = 750;
213
-
214
211
  // ../browser-core/src/post-load-tracker.ts
215
212
  var DEFAULT_POST_LOAD_TRACKER_QUIET_WINDOW_MS = 400;
216
213
 
@@ -240,30 +237,15 @@ var DEFAULT_SETTLE_DELAYS = {
240
237
  "dom-action": 100,
241
238
  snapshot: 0
242
239
  };
243
- var defaultSnapshotSettleObserver = {
244
- async settle(input) {
245
- if (input.trigger !== "snapshot") {
246
- return false;
247
- }
248
- await input.engine.waitForVisualStability({
249
- pageRef: input.pageRef,
250
- ...input.remainingMs === void 0 ? {} : { timeoutMs: input.remainingMs },
251
- settleMs: DEFAULT_VISUAL_STABILITY_SETTLE_MS,
252
- scope: "visible-frames"
253
- });
254
- return true;
255
- }
256
- };
257
- Object.freeze(defaultSnapshotSettleObserver);
258
240
  var DOM_ACTION_VISUAL_STABILITY_PROFILES = {
259
- "dom.click": { settleMs: 750, scope: "visible-frames", timeoutMs: 7e3 },
260
- "dom.input": { settleMs: 750, scope: "visible-frames", timeoutMs: 7e3 },
261
- "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 },
262
244
  "dom.hover": { settleMs: 200, scope: "main-frame", timeoutMs: 2500 }
263
245
  };
264
246
  var DEFAULT_DOM_ACTION_VISUAL_STABILITY_PROFILE = {
265
247
  settleMs: 750,
266
- scope: "visible-frames",
248
+ scope: "main-frame",
267
249
  timeoutMs: 7e3
268
250
  };
269
251
  var NAVIGATION_VISUAL_STABILITY_PROFILE = {
@@ -287,6 +269,7 @@ var defaultDomActionSettleObserver = {
287
269
  pageRef: input.pageRef,
288
270
  timeoutMs: effectiveTimeout,
289
271
  settleMs: profile.settleMs,
272
+ ...input.observedMutationQuietMs === void 0 ? {} : { initialQuietMs: input.observedMutationQuietMs },
290
273
  scope: profile.scope
291
274
  });
292
275
  return true;
@@ -307,15 +290,20 @@ var defaultNavigationSettleObserver = {
307
290
  return false;
308
291
  }
309
292
  try {
310
- const startedAt = Date.now();
311
- await input.engine.waitForPostLoadQuiet({
312
- pageRef: input.pageRef,
313
- timeoutMs: effectiveTimeout,
314
- quietMs: DEFAULT_POST_LOAD_TRACKER_QUIET_WINDOW_MS,
315
- captureWindowMs: Math.min(NAVIGATION_POST_LOAD_CAPTURE_WINDOW_MS, effectiveTimeout),
316
- signal: input.signal
317
- });
318
- 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
+ }
319
307
  if (visualTimeout <= 0) {
320
308
  return true;
321
309
  }
@@ -323,6 +311,7 @@ var defaultNavigationSettleObserver = {
323
311
  pageRef: input.pageRef,
324
312
  timeoutMs: visualTimeout,
325
313
  settleMs: profile.settleMs,
314
+ ...initialQuietMs <= 0 ? {} : { initialQuietMs },
326
315
  scope: profile.scope
327
316
  });
328
317
  return true;
@@ -333,7 +322,6 @@ var defaultNavigationSettleObserver = {
333
322
  };
334
323
  Object.freeze(defaultNavigationSettleObserver);
335
324
  var DEFAULT_SETTLE_OBSERVERS = Object.freeze([
336
- defaultSnapshotSettleObserver,
337
325
  defaultDomActionSettleObserver,
338
326
  defaultNavigationSettleObserver
339
327
  ]);
@@ -412,72 +400,6 @@ function abortError() {
412
400
  return error;
413
401
  }
414
402
 
415
- // ../protocol/src/json.ts
416
- var JSON_SCHEMA_DRAFT_2020_12 = "https://json-schema.org/draft/2020-12/schema";
417
- function defineSchema(schema) {
418
- return schema;
419
- }
420
- function stringSchema(options = {}) {
421
- return defineSchema({
422
- type: "string",
423
- ...options
424
- });
425
- }
426
- function numberSchema(options = {}) {
427
- return defineSchema({
428
- type: "number",
429
- ...options
430
- });
431
- }
432
- function integerSchema(options = {}) {
433
- return defineSchema({
434
- type: "integer",
435
- ...options
436
- });
437
- }
438
- function literalSchema(value, options = {}) {
439
- return defineSchema({
440
- const: value,
441
- ...options
442
- });
443
- }
444
- function enumSchema(values, options = {}) {
445
- return defineSchema({
446
- enum: values,
447
- ...options
448
- });
449
- }
450
- function arraySchema(items, options = {}) {
451
- return defineSchema({
452
- type: "array",
453
- items,
454
- ...options
455
- });
456
- }
457
- function objectSchema(properties, options = {}) {
458
- const { required, additionalProperties, ...rest } = options;
459
- return defineSchema({
460
- type: "object",
461
- properties,
462
- ...rest,
463
- ...required === void 0 ? {} : { required },
464
- ...additionalProperties === void 0 ? { additionalProperties: false } : { additionalProperties }
465
- });
466
- }
467
- function recordSchema(valueSchema, options = {}) {
468
- return defineSchema({
469
- type: "object",
470
- additionalProperties: valueSchema,
471
- ...options
472
- });
473
- }
474
- function oneOfSchema(members, options = {}) {
475
- return defineSchema({
476
- oneOf: members,
477
- ...options
478
- });
479
- }
480
-
481
403
  // ../protocol/src/validation.ts
482
404
  function validateJsonSchema(schema, value, path6 = "$") {
483
405
  return validateSchemaNode(schema, value, path6);
@@ -711,7 +633,7 @@ function isJsonValueEqual(expected, actual) {
711
633
 
712
634
  // ../protocol/src/version.ts
713
635
  var OPENSTEER_PROTOCOL_NAME = "opensteer";
714
- var OPENSTEER_PROTOCOL_COMPATIBILITY_REVISION = 2;
636
+ var OPENSTEER_PROTOCOL_COMPATIBILITY_REVISION = 3;
715
637
  var OPENSTEER_PROTOCOL_VERSION = `0.${OPENSTEER_PROTOCOL_COMPATIBILITY_REVISION}.0`;
716
638
  var OPENSTEER_PROTOCOL_REST_BASE_PATH = `/api/v${OPENSTEER_PROTOCOL_COMPATIBILITY_REVISION}`;
717
639
  objectSchema(
@@ -900,7 +822,7 @@ var visualViewportSchema = objectSchema(
900
822
  required: ["origin", "offsetWithinLayoutViewport", "size"]
901
823
  }
902
824
  );
903
- var viewportMetricsSchema = objectSchema(
825
+ objectSchema(
904
826
  {
905
827
  layoutViewport: layoutViewportSchema,
906
828
  visualViewport: visualViewportSchema,
@@ -2351,7 +2273,7 @@ var domSnapshotSchema = objectSchema(
2351
2273
  ]
2352
2274
  }
2353
2275
  );
2354
- var hitTestResultSchema = objectSchema(
2276
+ objectSchema(
2355
2277
  {
2356
2278
  inputPoint: pointSchema,
2357
2279
  inputCoordinateSpace: coordinateSpaceSchema,
@@ -2387,152 +2309,6 @@ var hitTestResultSchema = objectSchema(
2387
2309
  }
2388
2310
  );
2389
2311
 
2390
- // ../protocol/src/capabilities.ts
2391
- var opensteerCapabilities = [
2392
- "sessions.manage",
2393
- "pages.manage",
2394
- "pages.navigate",
2395
- "input.pointer",
2396
- "input.keyboard",
2397
- "input.touch",
2398
- "artifacts.screenshot",
2399
- "execution.pause",
2400
- "execution.resume",
2401
- "execution.freeze",
2402
- "inspect.pages",
2403
- "inspect.frames",
2404
- "inspect.html",
2405
- "inspect.domSnapshot",
2406
- "inspect.text",
2407
- "inspect.attributes",
2408
- "inspect.hitTest",
2409
- "inspect.viewportMetrics",
2410
- "inspect.network",
2411
- "inspect.networkBodies",
2412
- "inspect.cookies",
2413
- "inspect.localStorage",
2414
- "inspect.sessionStorage",
2415
- "inspect.indexedDb",
2416
- "transport.sessionHttp",
2417
- "instrumentation.initScripts",
2418
- "instrumentation.routing",
2419
- "events.pageLifecycle",
2420
- "events.dialog",
2421
- "events.download",
2422
- "events.chooser",
2423
- "events.worker",
2424
- "events.console",
2425
- "events.pageError",
2426
- "events.websocket",
2427
- "events.eventStream",
2428
- "events.executionState",
2429
- "surface.rest",
2430
- "surface.mcp"
2431
- ];
2432
- var opensteerCapabilitySchema = enumSchema(opensteerCapabilities, {
2433
- title: "OpensteerCapability"
2434
- });
2435
- var opensteerCapabilitySetSchema = arraySchema(opensteerCapabilitySchema, {
2436
- title: "OpensteerCapabilitySet",
2437
- uniqueItems: true
2438
- });
2439
- var opensteerCapabilityDescriptorSchema = objectSchema(
2440
- {
2441
- key: opensteerCapabilitySchema,
2442
- description: stringSchema(),
2443
- stability: enumSchema(["stable", "experimental"])
2444
- },
2445
- {
2446
- title: "OpensteerCapabilityDescriptor",
2447
- required: ["key", "description", "stability"]
2448
- }
2449
- );
2450
- arraySchema(
2451
- opensteerCapabilityDescriptorSchema,
2452
- {
2453
- title: "OpensteerCapabilityDescriptorList",
2454
- uniqueItems: true
2455
- }
2456
- );
2457
-
2458
- // ../protocol/src/errors.ts
2459
- var opensteerErrorCodes = [
2460
- "invalid-request",
2461
- "invalid-argument",
2462
- "invalid-ref",
2463
- "unsupported-version",
2464
- "unsupported-operation",
2465
- "unsupported-capability",
2466
- "not-found",
2467
- "stale-node-ref",
2468
- "session-closed",
2469
- "page-closed",
2470
- "frame-detached",
2471
- "timeout",
2472
- "navigation-failed",
2473
- "permission-denied",
2474
- "conflict",
2475
- "profile-unavailable",
2476
- "auth-failure",
2477
- "auth-recovery-failed",
2478
- "browser-required",
2479
- "rate-limited",
2480
- "operation-failed",
2481
- "internal"
2482
- ];
2483
- var OpensteerProtocolError = class extends Error {
2484
- code;
2485
- retriable;
2486
- capability;
2487
- details;
2488
- constructor(code, message, options = {}) {
2489
- super(message, { cause: options.cause });
2490
- this.name = "OpensteerProtocolError";
2491
- this.code = code;
2492
- this.retriable = options.retriable ?? false;
2493
- this.capability = options.capability;
2494
- this.details = options.details;
2495
- }
2496
- };
2497
- function createOpensteerError(code, message, options = {}) {
2498
- return {
2499
- code,
2500
- message,
2501
- retriable: options.retriable ?? false,
2502
- ...options.capability === void 0 ? {} : { capability: options.capability },
2503
- ...options.details === void 0 ? {} : { details: options.details }
2504
- };
2505
- }
2506
- function isOpensteerProtocolError(value) {
2507
- return value instanceof OpensteerProtocolError;
2508
- }
2509
- function toOpensteerError(error) {
2510
- return createOpensteerError(error.code, error.message, {
2511
- retriable: error.retriable,
2512
- ...error.capability === void 0 ? {} : { capability: error.capability },
2513
- ...error.details === void 0 ? {} : { details: error.details }
2514
- });
2515
- }
2516
- var opensteerErrorCodeSchema = {
2517
- title: "OpensteerErrorCode",
2518
- enum: opensteerErrorCodes
2519
- };
2520
- var opensteerErrorSchema = objectSchema(
2521
- {
2522
- code: opensteerErrorCodeSchema,
2523
- message: stringSchema(),
2524
- retriable: {
2525
- type: "boolean"
2526
- },
2527
- capability: opensteerCapabilitySchema,
2528
- details: recordSchema({})
2529
- },
2530
- {
2531
- title: "OpensteerError",
2532
- required: ["code", "message", "retriable"]
2533
- }
2534
- );
2535
-
2536
2312
  // ../protocol/src/events.ts
2537
2313
  function eventBaseSchema(kind) {
2538
2314
  return {
@@ -3804,39 +3580,14 @@ var opensteerTargetInputSchema = oneOfSchema(
3804
3580
  title: "OpensteerTargetInput"
3805
3581
  }
3806
3582
  );
3807
- var opensteerResolvedTargetSchema = objectSchema(
3808
- {
3809
- pageRef: pageRefSchema,
3810
- frameRef: frameRefSchema,
3811
- documentRef: documentRefSchema,
3812
- documentEpoch: documentEpochSchema,
3813
- nodeRef: nodeRefSchema,
3814
- tagName: stringSchema(),
3815
- pathHint: stringSchema(),
3816
- persist: stringSchema(),
3817
- selectorUsed: stringSchema()
3818
- },
3819
- {
3820
- title: "OpensteerResolvedTarget",
3821
- required: [
3822
- "pageRef",
3823
- "frameRef",
3824
- "documentRef",
3825
- "documentEpoch",
3826
- "nodeRef",
3827
- "tagName",
3828
- "pathHint"
3829
- ]
3830
- }
3831
- );
3832
3583
  var opensteerActionResultSchema = objectSchema(
3833
3584
  {
3834
- target: opensteerResolvedTargetSchema,
3835
- point: pointSchema
3585
+ tagName: stringSchema({ minLength: 1 }),
3586
+ persist: stringSchema({ minLength: 1 })
3836
3587
  },
3837
3588
  {
3838
3589
  title: "OpensteerActionResult",
3839
- required: ["target"]
3590
+ required: ["tagName"]
3840
3591
  }
3841
3592
  );
3842
3593
  var opensteerSnapshotCounterSchema = objectSchema(
@@ -3882,16 +3633,14 @@ var opensteerSnapshotCounterSchema = objectSchema(
3882
3633
  ]
3883
3634
  }
3884
3635
  );
3885
- var opensteerSessionStateSchema = objectSchema(
3636
+ var opensteerNavigationSummarySchema = objectSchema(
3886
3637
  {
3887
- sessionRef: sessionRefSchema,
3888
- pageRef: pageRefSchema,
3889
3638
  url: stringSchema(),
3890
3639
  title: stringSchema()
3891
3640
  },
3892
3641
  {
3893
- title: "OpensteerSessionState",
3894
- required: ["sessionRef", "pageRef", "url", "title"]
3642
+ title: "OpensteerNavigationSummary",
3643
+ required: ["url", "title"]
3895
3644
  }
3896
3645
  );
3897
3646
  var opensteerOpenInputSchema = objectSchema(
@@ -3959,6 +3708,17 @@ var opensteerPageCloseOutputSchema = objectSchema(
3959
3708
  required: ["closedPageRef", "pages"]
3960
3709
  }
3961
3710
  );
3711
+ var opensteerPageNewOutputSchema = objectSchema(
3712
+ {
3713
+ pageRef: pageRefSchema,
3714
+ url: stringSchema(),
3715
+ title: stringSchema()
3716
+ },
3717
+ {
3718
+ title: "OpensteerPageNewOutput",
3719
+ required: ["pageRef", "url", "title"]
3720
+ }
3721
+ );
3962
3722
  var opensteerPageGotoInputSchema = objectSchema(
3963
3723
  {
3964
3724
  url: stringSchema(),
@@ -4345,72 +4105,28 @@ var opensteerComputerExecuteInputSchema = objectSchema(
4345
4105
  required: ["action"]
4346
4106
  }
4347
4107
  );
4348
- var opensteerComputerTracePointSchema = objectSchema(
4349
- {
4350
- role: enumSchema(["point", "start", "end"]),
4351
- point: pointSchema,
4352
- hitTest: hitTestResultSchema,
4353
- target: opensteerResolvedTargetSchema
4354
- },
4355
- {
4356
- title: "OpensteerComputerTracePoint",
4357
- required: ["role", "point"]
4358
- }
4359
- );
4360
- var opensteerComputerTraceEnrichmentSchema = objectSchema(
4361
- {
4362
- points: arraySchema(opensteerComputerTracePointSchema)
4363
- },
4364
- {
4365
- title: "OpensteerComputerTraceEnrichment",
4366
- required: ["points"]
4367
- }
4368
- );
4369
- var opensteerComputerExecuteTimingSchema = objectSchema(
4108
+ var opensteerScreenshotSummarySchema = objectSchema(
4370
4109
  {
4371
- actionMs: integerSchema({ minimum: 0 }),
4372
- waitMs: integerSchema({ minimum: 0 }),
4373
- totalMs: integerSchema({ minimum: 0 })
4374
- },
4375
- {
4376
- title: "OpensteerComputerExecuteTiming",
4377
- required: ["actionMs", "waitMs", "totalMs"]
4378
- }
4379
- );
4380
- var opensteerComputerDisplayScaleSchema = objectSchema(
4381
- {
4382
- x: numberSchema({ exclusiveMinimum: 0 }),
4383
- y: numberSchema({ exclusiveMinimum: 0 })
4110
+ payload: externalBinaryLocationSchema,
4111
+ format: screenshotFormatSchema,
4112
+ size: sizeSchema,
4113
+ coordinateSpace: coordinateSpaceSchema,
4114
+ clip: rectSchema
4384
4115
  },
4385
4116
  {
4386
- title: "OpensteerComputerDisplayScale",
4387
- required: ["x", "y"]
4117
+ title: "OpensteerScreenshotSummary",
4118
+ required: ["payload", "format", "size", "coordinateSpace"]
4388
4119
  }
4389
4120
  );
4390
4121
  var opensteerComputerExecuteOutputSchema = objectSchema(
4391
4122
  {
4392
- action: opensteerComputerActionSchema,
4393
- pageRef: pageRefSchema,
4394
- screenshot: screenshotArtifactSchema,
4395
- displayViewport: viewportMetricsSchema,
4396
- nativeViewport: viewportMetricsSchema,
4397
- displayScale: opensteerComputerDisplayScaleSchema,
4398
- events: arraySchema(opensteerEventSchema),
4399
- timing: opensteerComputerExecuteTimingSchema,
4400
- trace: opensteerComputerTraceEnrichmentSchema
4123
+ url: stringSchema(),
4124
+ title: stringSchema(),
4125
+ screenshot: opensteerScreenshotSummarySchema
4401
4126
  },
4402
4127
  {
4403
4128
  title: "OpensteerComputerExecuteOutput",
4404
- required: [
4405
- "action",
4406
- "pageRef",
4407
- "screenshot",
4408
- "displayViewport",
4409
- "nativeViewport",
4410
- "displayScale",
4411
- "events",
4412
- "timing"
4413
- ]
4129
+ required: ["url", "title", "screenshot"]
4414
4130
  }
4415
4131
  );
4416
4132
  function assertValidSemanticOperationInput(name, input) {
@@ -4436,7 +4152,7 @@ var opensteerSemanticOperationSpecificationsBase = [
4436
4152
  name: "session.open",
4437
4153
  description: "Open or resume the current Opensteer session and primary page.",
4438
4154
  inputSchema: opensteerOpenInputSchema,
4439
- outputSchema: opensteerSessionStateSchema,
4155
+ outputSchema: opensteerNavigationSummarySchema,
4440
4156
  requiredCapabilities: ["sessions.manage", "pages.manage"],
4441
4157
  resolveRequiredCapabilities: (input) => input.url === void 0 ? ["sessions.manage", "pages.manage"] : ["sessions.manage", "pages.manage", "pages.navigate"]
4442
4158
  }),
@@ -4451,7 +4167,7 @@ var opensteerSemanticOperationSpecificationsBase = [
4451
4167
  name: "page.new",
4452
4168
  description: "Create and optionally navigate a new top-level page in the current session.",
4453
4169
  inputSchema: opensteerPageNewInputSchema,
4454
- outputSchema: opensteerSessionStateSchema,
4170
+ outputSchema: opensteerPageNewOutputSchema,
4455
4171
  requiredCapabilities: ["pages.manage"],
4456
4172
  resolveRequiredCapabilities: (input) => input.url === void 0 ? ["pages.manage"] : ["pages.manage", "pages.navigate"]
4457
4173
  }),
@@ -4459,7 +4175,7 @@ var opensteerSemanticOperationSpecificationsBase = [
4459
4175
  name: "page.activate",
4460
4176
  description: "Activate an existing top-level page in the current session.",
4461
4177
  inputSchema: opensteerPageActivateInputSchema,
4462
- outputSchema: opensteerSessionStateSchema,
4178
+ outputSchema: opensteerNavigationSummarySchema,
4463
4179
  requiredCapabilities: ["pages.manage", "inspect.pages"]
4464
4180
  }),
4465
4181
  defineSemanticOperationSpec({
@@ -4473,7 +4189,7 @@ var opensteerSemanticOperationSpecificationsBase = [
4473
4189
  name: "page.goto",
4474
4190
  description: "Navigate the current Opensteer page to a new URL.",
4475
4191
  inputSchema: opensteerPageGotoInputSchema,
4476
- outputSchema: opensteerSessionStateSchema,
4192
+ outputSchema: opensteerNavigationSummarySchema,
4477
4193
  requiredCapabilities: ["pages.navigate"]
4478
4194
  }),
4479
4195
  defineSemanticOperationSpec({
@@ -5110,6 +4826,9 @@ function buildClauseSelector(node, clause) {
5110
4826
  if (!clause || typeof clause !== "object") {
5111
4827
  return "";
5112
4828
  }
4829
+ if (clause.kind === "text") {
4830
+ return "";
4831
+ }
5113
4832
  if (clause.kind === "position") {
5114
4833
  if (clause.axis === "nthOfType") {
5115
4834
  return `:nth-of-type(${Math.max(1, Number(node.position?.nthOfType || 1))})`;
@@ -5234,7 +4953,7 @@ function resolveExtractedValueInContext(normalizedValue, options) {
5234
4953
  function stripPositionClauses(nodes) {
5235
4954
  return (nodes || []).map((node) => ({
5236
4955
  ...node,
5237
- match: (node.match || []).filter((clause) => clause.kind !== "position")
4956
+ match: (node.match || []).filter((clause) => clause.kind !== "position" && clause.kind !== "text")
5238
4957
  }));
5239
4958
  }
5240
4959
  function dedupeSelectors(selectors) {
@@ -5811,9 +5530,17 @@ function resolveDomPathInScope(index, domPath, scope) {
5811
5530
  if (!candidates.length) {
5812
5531
  return null;
5813
5532
  }
5533
+ const lastNode = domPath[domPath.length - 1];
5534
+ const textClauses = lastNode?.match.filter((c) => c.kind === "text") ?? [];
5814
5535
  let fallback = null;
5815
5536
  for (const selector of candidates) {
5816
- const matches = querySelectorAllInScope(index, selector, scope);
5537
+ let matches = querySelectorAllInScope(index, selector, scope);
5538
+ if (textClauses.length > 0 && matches.length > 1) {
5539
+ const filtered = matches.filter((node) => matchesTextClauses(node, textClauses));
5540
+ if (filtered.length > 0) {
5541
+ matches = filtered;
5542
+ }
5543
+ }
5817
5544
  if (matches.length === 1) {
5818
5545
  return {
5819
5546
  node: matches[0],
@@ -5833,6 +5560,10 @@ function resolveDomPathInScope(index, domPath, scope) {
5833
5560
  }
5834
5561
  return fallback;
5835
5562
  }
5563
+ function matchesTextClauses(node, clauses) {
5564
+ const text = (node.textContent ?? "").replace(/\s+/g, " ").trim();
5565
+ return clauses.every((clause) => text.includes(clause.value));
5566
+ }
5836
5567
  function queryAllDomPathInScope(index, domPath, scope) {
5837
5568
  const selectors = buildPathCandidates(domPath);
5838
5569
  for (const selector of selectors) {
@@ -5991,7 +5722,13 @@ function clonePathNode(node) {
5991
5722
  };
5992
5723
  }
5993
5724
  function cloneMatchClause(clause) {
5994
- return clause.kind === "position" ? { kind: "position", axis: clause.axis } : {
5725
+ if (clause.kind === "position") {
5726
+ return { kind: "position", axis: clause.axis };
5727
+ }
5728
+ if (clause.kind === "text") {
5729
+ return { kind: "text", value: clause.value };
5730
+ }
5731
+ return {
5995
5732
  kind: "attr",
5996
5733
  key: clause.key,
5997
5734
  ...clause.op === void 0 ? {} : { op: clause.op },
@@ -6046,6 +5783,13 @@ function normalizeMatch(rawMatch, attrs, position, tag) {
6046
5783
  op,
6047
5784
  ...value === void 0 ? {} : { value }
6048
5785
  });
5786
+ continue;
5787
+ }
5788
+ if (record.kind === "text") {
5789
+ const textValue = typeof record.value === "string" ? record.value.trim() : "";
5790
+ if (textValue) {
5791
+ push({ kind: "text", value: textValue.slice(0, 80) });
5792
+ }
6049
5793
  }
6050
5794
  }
6051
5795
  }
@@ -6411,7 +6155,7 @@ function isTimeoutError(error) {
6411
6155
  }
6412
6156
 
6413
6157
  // ../runtime-core/src/runtimes/dom/executor.ts
6414
- var MAX_DOM_ACTION_ATTEMPTS = 3;
6158
+ var MAX_DOM_ACTION_ATTEMPTS = 2;
6415
6159
  var DEFAULT_SCROLL_OPTIONS = {
6416
6160
  block: "center",
6417
6161
  inline: "center"
@@ -6701,7 +6445,7 @@ var DomActionExecutor = class {
6701
6445
  ...snapshot === void 0 ? {} : { snapshot },
6702
6446
  signal: timeout.signal,
6703
6447
  remainingMs: () => timeout.remainingMs(),
6704
- policySettle: async (targetPageRef, trigger) => {
6448
+ policySettle: async (targetPageRef, trigger, boundary2) => {
6705
6449
  try {
6706
6450
  await settleWithPolicy(this.options.policy.settle, {
6707
6451
  operation,
@@ -6709,7 +6453,9 @@ var DomActionExecutor = class {
6709
6453
  engine: this.options.engine,
6710
6454
  pageRef: targetPageRef,
6711
6455
  signal: timeout.signal,
6712
- remainingMs: timeout.remainingMs()
6456
+ remainingMs: timeout.remainingMs(),
6457
+ ...boundary2?.observedMutationQuietMs === void 0 ? {} : { observedMutationQuietMs: boundary2.observedMutationQuietMs },
6458
+ ...boundary2?.postLoadHandled === true ? { postLoadHandled: true } : {}
6713
6459
  });
6714
6460
  } catch (error) {
6715
6461
  if (snapshot !== void 0 && isSoftSettleTimeoutError(error, timeout.signal)) {
@@ -6898,11 +6644,12 @@ var DomActionExecutor = class {
6898
6644
  throw this.createActionabilityError(
6899
6645
  operation,
6900
6646
  "obscured",
6901
- `hit test resolved ${hit.nodeRef} outside the target subtree rooted at ${resolved.nodeRef}`,
6647
+ `target is obscured by ${assessment.blockingDescription ?? "another element"} at the click point`,
6902
6648
  {
6903
6649
  ...details,
6904
6650
  hitRelation: assessment.relation,
6905
6651
  ...assessment.ambiguous === void 0 ? {} : { hitAmbiguous: assessment.ambiguous },
6652
+ ...assessment.blockingDescription === void 0 ? {} : { blockingDescription: assessment.blockingDescription },
6906
6653
  ...assessment.canonicalTarget === void 0 ? {} : {
6907
6654
  canonicalNodeRef: assessment.canonicalTarget.nodeRef,
6908
6655
  canonicalDocumentRef: assessment.canonicalTarget.documentRef,
@@ -6916,8 +6663,7 @@ var DomActionExecutor = class {
6916
6663
  hitMissingFromSnapshot: !resolved.snapshot.nodes.some(
6917
6664
  (node) => node.nodeRef === hit.nodeRef
6918
6665
  )
6919
- },
6920
- true
6666
+ }
6921
6667
  );
6922
6668
  }
6923
6669
  async resolveActionablePointerTarget(session, operation, resolved) {
@@ -7129,7 +6875,12 @@ var DefaultDomRuntime = class {
7129
6875
  });
7130
6876
  }
7131
6877
  async buildPath(input) {
7132
- return sanitizeReplayElementPath(await this.requireBridge().buildReplayPath(input.locator));
6878
+ return sanitizeReplayElementPath(
6879
+ await this.requireBridge().buildReplayPath(
6880
+ input.locator,
6881
+ input.enableTextMatch ? { enableTextMatch: true } : void 0
6882
+ )
6883
+ );
7133
6884
  }
7134
6885
  async resolveTarget(input) {
7135
6886
  return this.withSnapshotSession((session) => this.resolveTargetWithSession(session, input));
@@ -7349,7 +7100,7 @@ var DefaultDomRuntime = class {
7349
7100
  if (resolvedByLocator) {
7350
7101
  const { snapshot, node } = resolvedByLocator;
7351
7102
  const anchor = await this.buildAnchorFromSnapshotNode(session, snapshot, node);
7352
- const replayPath = await this.tryBuildPathFromNode(snapshot, node);
7103
+ const replayPath = await this.tryBuildPathFromNode(snapshot, node, { enableTextMatch: true });
7353
7104
  return this.createResolvedTarget("live", snapshot, node, anchor, {
7354
7105
  ...target.persist === void 0 ? {} : { persist: target.persist },
7355
7106
  ...replayPath === void 0 ? {} : { replayPath }
@@ -7367,11 +7118,12 @@ var DefaultDomRuntime = class {
7367
7118
  const { snapshot, node } = resolved;
7368
7119
  const anchor = await this.buildAnchorFromSnapshotNode(session, snapshot, node);
7369
7120
  const writeDescriptor = descriptorWriter ?? ((input) => this.descriptors.write(input));
7370
- const replayPath = await this.tryBuildPathFromNode(snapshot, node);
7121
+ const enableTextMatch = method !== "extract";
7122
+ const replayPath = await this.tryBuildPathFromNode(snapshot, node, { enableTextMatch });
7371
7123
  const descriptor = target.persist === void 0 ? void 0 : await writeDescriptor({
7372
7124
  method,
7373
7125
  persist: target.persist,
7374
- path: replayPath ?? await this.buildPathForNode(snapshot, node),
7126
+ path: replayPath ?? await this.buildPathForNode(snapshot, node, { enableTextMatch }),
7375
7127
  sourceUrl: snapshot.url
7376
7128
  });
7377
7129
  return this.createResolvedTarget("selector", snapshot, node, anchor, {
@@ -7471,7 +7223,7 @@ var DefaultDomRuntime = class {
7471
7223
  `Unable to resolve structural anchor "${buildPathSelectorHint(anchor)}" in the current session`
7472
7224
  );
7473
7225
  }
7474
- const replayPath = await this.tryBuildPathFromNode(context.snapshot, target.node);
7226
+ const replayPath = await this.tryBuildPathFromNode(context.snapshot, target.node, { enableTextMatch: true });
7475
7227
  return this.createResolvedTarget(source, context.snapshot, target.node, anchor, {
7476
7228
  ...persist === void 0 ? {} : { persist },
7477
7229
  ...replayPath === void 0 ? {} : { replayPath }
@@ -7642,19 +7394,20 @@ var DefaultDomRuntime = class {
7642
7394
  }
7643
7395
  return this.bridge;
7644
7396
  }
7645
- async buildPathForNode(snapshot, node) {
7397
+ async buildPathForNode(snapshot, node, options) {
7646
7398
  if (node.nodeRef === void 0) {
7647
7399
  throw new Error(
7648
7400
  `snapshot node ${String(node.snapshotNodeId)} does not expose a live node reference`
7649
7401
  );
7650
7402
  }
7651
7403
  return this.buildPath({
7652
- locator: createNodeLocator(snapshot.documentRef, snapshot.documentEpoch, node.nodeRef)
7404
+ locator: createNodeLocator(snapshot.documentRef, snapshot.documentEpoch, node.nodeRef),
7405
+ ...options?.enableTextMatch ? { enableTextMatch: true } : {}
7653
7406
  });
7654
7407
  }
7655
- async tryBuildPathFromNode(snapshot, node) {
7408
+ async tryBuildPathFromNode(snapshot, node, options) {
7656
7409
  try {
7657
- return await this.buildPathForNode(snapshot, node);
7410
+ return await this.buildPathForNode(snapshot, node, options);
7658
7411
  } catch {
7659
7412
  return void 0;
7660
7413
  }
@@ -8268,6 +8021,9 @@ function relaxPathForSingleSample(path6, mode) {
8268
8021
  }
8269
8022
  return !isLast;
8270
8023
  }
8024
+ if (clause.kind === "text") {
8025
+ return false;
8026
+ }
8271
8027
  const key = String(clause.key || "").trim().toLowerCase();
8272
8028
  if (!key || !shouldKeepAttrForSingleSample(key)) {
8273
8029
  return false;
@@ -8370,9 +8126,11 @@ function buildNodeStructure(node) {
8370
8126
  }
8371
8127
  structuralAttrs[key] = value;
8372
8128
  }
8373
- const matchClauses = (node.match || []).map(
8374
- (clause) => clause.kind === "position" ? `position:${clause.axis}` : `attr:${String(clause.key || "").trim().toLowerCase()}`
8375
- ).sort();
8129
+ const matchClauses = (node.match || []).map((clause) => {
8130
+ if (clause.kind === "position") return `position:${clause.axis}`;
8131
+ if (clause.kind === "text") return `text:${clause.value}`;
8132
+ return `attr:${String(clause.key || "").trim().toLowerCase()}`;
8133
+ }).sort();
8376
8134
  return {
8377
8135
  tag,
8378
8136
  attrs: structuralAttrs,
@@ -8778,6 +8536,9 @@ function mergeMatchByMajority(matchLists, attrs, threshold, positionFlags = {
8778
8536
  });
8779
8537
  continue;
8780
8538
  }
8539
+ if (clause.kind === "text") {
8540
+ continue;
8541
+ }
8781
8542
  if (clause.axis === "nthOfType") {
8782
8543
  if (positionFlags.hasNthOfType) {
8783
8544
  merged.push({ kind: "position", axis: "nthOfType" });
@@ -9773,7 +9534,8 @@ function assertProviderSupportsEngine(provider, engine) {
9773
9534
  return;
9774
9535
  }
9775
9536
  if (provider === "cloud") {
9776
- throw new Error(
9537
+ throw new OpensteerProtocolError(
9538
+ "invalid-argument",
9777
9539
  "ABP is not supported for provider=cloud. Cloud provider currently requires Playwright."
9778
9540
  );
9779
9541
  }
@@ -9783,7 +9545,8 @@ function normalizeOpensteerProviderMode(value, source = "OPENSTEER_PROVIDER") {
9783
9545
  if (normalized === OPENSTEER_PROVIDER_MODES[0] || normalized === OPENSTEER_PROVIDER_MODES[1]) {
9784
9546
  return normalized;
9785
9547
  }
9786
- throw new Error(
9548
+ throw new OpensteerProtocolError(
9549
+ "invalid-argument",
9787
9550
  `${source} must be one of ${OPENSTEER_PROVIDER_MODES.join(", ")}; received "${value}".`
9788
9551
  );
9789
9552
  }
@@ -10223,6 +9986,24 @@ async function sleep(ms) {
10223
9986
  // src/cloud/client.ts
10224
9987
  var CLOUD_CLOSE_TIMEOUT_MS = 6e4;
10225
9988
  var CLOUD_CLOSE_POLL_INTERVAL_MS = 250;
9989
+ var OpensteerCloudRequestError = class extends Error {
9990
+ statusCode;
9991
+ code;
9992
+ details;
9993
+ method;
9994
+ pathname;
9995
+ url;
9996
+ constructor(args) {
9997
+ super(args.message);
9998
+ this.name = "OpensteerCloudRequestError";
9999
+ this.statusCode = args.statusCode;
10000
+ this.code = args.code;
10001
+ this.details = args.details;
10002
+ this.method = args.method;
10003
+ this.pathname = args.pathname;
10004
+ this.url = args.url;
10005
+ }
10006
+ };
10226
10007
  var OpensteerCloudClient = class {
10227
10008
  constructor(config) {
10228
10009
  this.config = config;
@@ -10398,7 +10179,11 @@ var OpensteerCloudClient = class {
10398
10179
  });
10399
10180
  }
10400
10181
  if (!response.ok) {
10401
- throw new Error(`${init.method} ${pathname} failed with ${String(response.status)}.`);
10182
+ throw await createCloudRequestError(response, {
10183
+ method: init.method,
10184
+ pathname,
10185
+ url
10186
+ });
10402
10187
  }
10403
10188
  return response;
10404
10189
  }
@@ -10446,6 +10231,36 @@ function wrapCloudFetchError(error, input) {
10446
10231
  wrapped.name = error.name;
10447
10232
  return wrapped;
10448
10233
  }
10234
+ async function createCloudRequestError(response, input) {
10235
+ const payload = await readCloudErrorPayload(response);
10236
+ return new OpensteerCloudRequestError({
10237
+ statusCode: response.status,
10238
+ method: input.method,
10239
+ pathname: input.pathname,
10240
+ url: input.url,
10241
+ message: payload?.error ?? `${input.method} ${input.pathname} failed with ${String(response.status)}.`,
10242
+ ...payload?.code === void 0 ? {} : { code: payload.code },
10243
+ ...payload?.details === void 0 ? {} : { details: payload.details }
10244
+ });
10245
+ }
10246
+ async function readCloudErrorPayload(response) {
10247
+ try {
10248
+ return asCloudErrorPayload(await response.json());
10249
+ } catch {
10250
+ return void 0;
10251
+ }
10252
+ }
10253
+ function asCloudErrorPayload(value) {
10254
+ if (value === null || typeof value !== "object") {
10255
+ return void 0;
10256
+ }
10257
+ const payload = value;
10258
+ return {
10259
+ ...typeof payload.error === "string" ? { error: payload.error } : {},
10260
+ ...typeof payload.code === "string" ? { code: payload.code } : {},
10261
+ ..."details" in payload ? { details: payload.details } : {}
10262
+ };
10263
+ }
10449
10264
 
10450
10265
  // src/cloud/config.ts
10451
10266
  function resolveCloudConfig(input = {}) {
@@ -10621,7 +10436,7 @@ async function dispatchSemanticOperation(runtime, operation, input, options = {}
10621
10436
 
10622
10437
  // ../runtime-core/package.json
10623
10438
  var package_default = {
10624
- version: "0.2.4"};
10439
+ version: "0.2.6"};
10625
10440
 
10626
10441
  // ../runtime-core/src/version.ts
10627
10442
  var OPENSTEER_RUNTIME_CORE_VERSION = package_default.version;
@@ -10892,12 +10707,16 @@ function toOpensteerResolvedTarget(target) {
10892
10707
  documentRef: target.documentRef,
10893
10708
  documentEpoch: target.documentEpoch,
10894
10709
  nodeRef: target.nodeRef,
10895
- tagName: target.node.nodeName.toUpperCase(),
10710
+ tagName: toOpensteerTagName(target.node.nodeName),
10896
10711
  pathHint: buildPathSelectorHint(target.replayPath ?? target.anchor),
10897
10712
  ...target.persist === void 0 ? {} : { persist: target.persist },
10898
10713
  ...target.selectorUsed === void 0 ? {} : { selectorUsed: target.selectorUsed }
10899
10714
  };
10900
10715
  }
10716
+ function toOpensteerTagName(nodeName) {
10717
+ const tagName = String(nodeName).trim().toLowerCase();
10718
+ return tagName.length === 0 ? "element" : tagName;
10719
+ }
10901
10720
 
10902
10721
  // ../runtime-core/src/runtimes/computer-use/runtime.ts
10903
10722
  function createComputerUseRuntime(options) {
@@ -10931,7 +10750,7 @@ var DefaultComputerUseRuntime = class {
10931
10750
  screenshot,
10932
10751
  signal: input.timeout.signal,
10933
10752
  remainingMs: () => input.timeout.remainingMs(),
10934
- policySettle: async (pageRef, trigger) => {
10753
+ policySettle: async (pageRef, trigger, boundary) => {
10935
10754
  try {
10936
10755
  await settleWithPolicy(this.options.policy.settle, {
10937
10756
  operation: "computer.execute",
@@ -10939,7 +10758,9 @@ var DefaultComputerUseRuntime = class {
10939
10758
  engine: this.options.engine,
10940
10759
  pageRef,
10941
10760
  signal: input.timeout.signal,
10942
- remainingMs: input.timeout.remainingMs()
10761
+ remainingMs: input.timeout.remainingMs(),
10762
+ ...boundary?.observedMutationQuietMs === void 0 ? {} : { observedMutationQuietMs: boundary.observedMutationQuietMs },
10763
+ ...boundary?.postLoadHandled === true ? { postLoadHandled: true } : {}
10943
10764
  });
10944
10765
  } catch (error) {
10945
10766
  if (pageRef === input.pageRef && isSoftSettleTimeoutError(error, input.timeout.signal)) {
@@ -12117,28 +11938,24 @@ function restoreBoundedAttr(el, attr, value) {
12117
11938
  }
12118
11939
  setBoundedAttr(el, attr, value);
12119
11940
  }
12120
- function deduplicateImages(html) {
11941
+ function deduplicateImagesInDom($) {
12121
11942
  const seen = /* @__PURE__ */ new Set();
12122
- return html.replace(/<img\b([^>]*)>/gi, (full, attrContent) => {
12123
- if (/\bc\s*=/.test(attrContent)) {
12124
- return full;
12125
- }
12126
- const srcMatch = attrContent.match(/\bsrc\s*=\s*(["']?)(.*?)\1/);
12127
- const srcsetMatch = attrContent.match(/\bsrcset\s*=\s*(["'])(.*?)\1/);
12128
- let src = null;
12129
- if (srcMatch && srcMatch[2]) {
12130
- src = srcMatch[2].trim();
12131
- } else if (srcsetMatch && srcsetMatch[2]) {
12132
- src = srcsetMatch[2].split(",")[0]?.trim().split(" ")[0] ?? null;
11943
+ $("img").each(function deduplicateDomImage() {
11944
+ const el = $(this);
11945
+ if (el.attr("c") !== void 0) {
11946
+ return;
12133
11947
  }
11948
+ const srcValue = el.attr("src")?.trim();
11949
+ const srcsetValue = el.attr("srcset");
11950
+ const src = srcValue && srcValue.length > 0 ? srcValue : srcsetValue?.split(",")[0]?.trim().split(/\s+/u)[0];
12134
11951
  if (!src) {
12135
- return full;
11952
+ return;
12136
11953
  }
12137
11954
  if (seen.has(src)) {
12138
- return "";
11955
+ el.remove();
11956
+ return;
12139
11957
  }
12140
11958
  seen.add(src);
12141
- return full;
12142
11959
  });
12143
11960
  }
12144
11961
  function hasAttribute2(node, attr) {
@@ -12196,23 +12013,6 @@ function isPreservedImageElement(node) {
12196
12013
  function getElementsInReverseDocumentOrder($) {
12197
12014
  return $.root().find("*").toArray().reverse().filter((node) => node.type === "tag");
12198
12015
  }
12199
- function getNodeDepth(node) {
12200
- let depth = 0;
12201
- let current = node.parent;
12202
- while (current) {
12203
- depth++;
12204
- current = current.parent;
12205
- }
12206
- return depth;
12207
- }
12208
- function getElementsByDepthDescending($) {
12209
- const elements = $.root().find("*").toArray().filter((node) => node.type === "tag");
12210
- const depths = /* @__PURE__ */ new Map();
12211
- for (const el of elements) {
12212
- depths.set(el, getNodeDepth(el));
12213
- }
12214
- return elements.sort((a, b) => (depths.get(b) ?? 0) - (depths.get(a) ?? 0));
12215
- }
12216
12016
  function flattenExtractionTree($) {
12217
12017
  for (const node of getElementsInReverseDocumentOrder($)) {
12218
12018
  const el = $(node);
@@ -12230,19 +12030,6 @@ function flattenExtractionTree($) {
12230
12030
  el.replaceWith(el.contents());
12231
12031
  }
12232
12032
  }
12233
- function hasMarkedAncestor(el, attr) {
12234
- let current = el[0]?.parent;
12235
- while (current) {
12236
- if (!isElementLikeNode(current)) {
12237
- return false;
12238
- }
12239
- if (current.attribs?.[attr] !== void 0) {
12240
- return true;
12241
- }
12242
- current = current.parent;
12243
- }
12244
- return false;
12245
- }
12246
12033
  function isIndicatorImage(node) {
12247
12034
  return (node?.tagName || "").toLowerCase() === "img" && (hasAttribute2(node, "alt") || hasAttribute2(node, "src") || hasAttribute2(node, "srcset"));
12248
12035
  }
@@ -12360,7 +12147,7 @@ function serializeForExtraction($, root) {
12360
12147
  traverse(root, 0);
12361
12148
  return lines.map((l) => l.trim()).filter((l) => l.length > 0).join("");
12362
12149
  }
12363
- function isClickable($, el, context) {
12150
+ function isClickable(el, context) {
12364
12151
  if (context.hasPreMarked) {
12365
12152
  return el.attr(OPENSTEER_INTERACTIVE_ATTR) !== void 0;
12366
12153
  }
@@ -12394,21 +12181,17 @@ function isClickable($, el, context) {
12394
12181
  }
12395
12182
  return false;
12396
12183
  }
12397
- function cleanForExtraction(html) {
12184
+ function prepareExtractionSnapshotDom(html) {
12398
12185
  if (!html.trim()) {
12399
- return "";
12186
+ return void 0;
12400
12187
  }
12401
12188
  const $ = cheerio.load(html, { xmlMode: false });
12402
12189
  removeNoise($);
12403
12190
  removeComments($);
12404
12191
  markInlineSelfHiddenFallback($);
12405
12192
  pruneSelfHiddenNodes($);
12406
- const $clean = cheerio.load(
12407
- $.html().replace(/\n{2,}/g, "\n").trim(),
12408
- { xmlMode: false }
12409
- );
12410
- $clean("*").each(function stripAndRestoreExtractionAttrs() {
12411
- const el = $clean(this);
12193
+ $("*").each(function stripAndRestoreExtractionAttrs() {
12194
+ const el = $(this);
12412
12195
  const node = el[0];
12413
12196
  if (!node) {
12414
12197
  return;
@@ -12449,16 +12232,20 @@ function cleanForExtraction(html) {
12449
12232
  restoreBoundedAttr(el, "href", hrefValue);
12450
12233
  }
12451
12234
  });
12452
- flattenExtractionTree($clean);
12453
- const root = $clean.root()[0];
12235
+ flattenExtractionTree($);
12236
+ deduplicateImagesInDom($);
12237
+ return $;
12238
+ }
12239
+ function serializePreparedExtractionSnapshot($) {
12240
+ const root = $.root()[0];
12454
12241
  if (root === void 0) {
12455
12242
  return "";
12456
12243
  }
12457
- return deduplicateImages(serializeForExtraction($clean, root));
12244
+ return serializeForExtraction($, root);
12458
12245
  }
12459
- function cleanForAction(html) {
12246
+ function prepareActionSnapshotDom(html) {
12460
12247
  if (!html.trim()) {
12461
- return "";
12248
+ return void 0;
12462
12249
  }
12463
12250
  const $ = cheerio.load(html, { xmlMode: false });
12464
12251
  removeNoise($);
@@ -12467,13 +12254,12 @@ function cleanForAction(html) {
12467
12254
  pruneSelfHiddenNodes($);
12468
12255
  const clickableMark = "data-clickable-marker";
12469
12256
  const indicatorMark = "data-keep-indicator";
12470
- const branchMark = "data-keep-branch";
12471
12257
  const context = {
12472
12258
  hasPreMarked: $(`[${OPENSTEER_INTERACTIVE_ATTR}]`).length > 0
12473
12259
  };
12474
12260
  $("*").each(function markClickables() {
12475
12261
  const el = $(this);
12476
- if (isClickable($, el, context)) {
12262
+ if (isClickable(el, context)) {
12477
12263
  el.attr(clickableMark, "1");
12478
12264
  }
12479
12265
  });
@@ -12503,25 +12289,7 @@ function cleanForAction(html) {
12503
12289
  el.remove();
12504
12290
  }
12505
12291
  });
12506
- $(`[${clickableMark}], [${indicatorMark}]`).each(function markBranches() {
12507
- let current = $(this).parent();
12508
- while (current.length > 0) {
12509
- const node = current[0];
12510
- if (!node || node.type !== "tag") {
12511
- break;
12512
- }
12513
- const ancestor = current;
12514
- const tag = (node.tagName || "").toLowerCase();
12515
- if (ROOT_TAGS.has(tag) || ancestor.attr(clickableMark) !== void 0) {
12516
- break;
12517
- }
12518
- if (!isBoundaryTag(tag)) {
12519
- ancestor.attr(branchMark, "1");
12520
- }
12521
- current = ancestor.parent();
12522
- }
12523
- });
12524
- for (const node of getElementsByDepthDescending($)) {
12292
+ for (const node of getElementsInReverseDocumentOrder($)) {
12525
12293
  const el = $(node);
12526
12294
  const tag = (node.tagName || "").toLowerCase();
12527
12295
  if (ROOT_TAGS.has(tag) || isBoundaryTag(tag)) {
@@ -12530,17 +12298,7 @@ function cleanForAction(html) {
12530
12298
  if (el.attr(clickableMark) !== void 0 || el.attr(indicatorMark) !== void 0) {
12531
12299
  continue;
12532
12300
  }
12533
- const insideClickable = hasMarkedAncestor(el, clickableMark);
12534
- const preserveBranch = el.attr(branchMark) !== void 0;
12535
12301
  const hasContent = hasElementChildren(node) || hasDirectText(node);
12536
- if (insideClickable || preserveBranch) {
12537
- if (!hasContent) {
12538
- el.remove();
12539
- } else {
12540
- unwrapActionNode($, el);
12541
- }
12542
- continue;
12543
- }
12544
12302
  if (!hasContent) {
12545
12303
  el.remove();
12546
12304
  continue;
@@ -12641,13 +12399,20 @@ function cleanForAction(html) {
12641
12399
  }
12642
12400
  el.removeAttr(clickableMark);
12643
12401
  el.removeAttr(indicatorMark);
12644
- el.removeAttr(branchMark);
12645
12402
  el.removeAttr(OPENSTEER_INTERACTIVE_ATTR);
12646
12403
  el.removeAttr(OPENSTEER_HIDDEN_ATTR);
12647
12404
  el.removeAttr(OPENSTEER_SCROLLABLE_ATTR);
12648
12405
  el.removeAttr(OPENSTEER_SELF_HIDDEN_ATTR);
12649
12406
  });
12650
- return compactHtml(deduplicateImages($.html()));
12407
+ deduplicateImagesInDom($);
12408
+ return $;
12409
+ }
12410
+ function serializePreparedActionSnapshot($) {
12411
+ const normalized = compactHtml($.html());
12412
+ if (normalized.length === 0) {
12413
+ return "";
12414
+ }
12415
+ return cheerio.load(normalized, { xmlMode: false }).html();
12651
12416
  }
12652
12417
  var VOID_TAGS2 = /* @__PURE__ */ new Set([
12653
12418
  "area",
@@ -12870,27 +12635,32 @@ async function markLiveSnapshotSemantics(options) {
12870
12635
  const frames = await options.engine.listFrames({
12871
12636
  pageRef: options.pageRef
12872
12637
  });
12873
- for (const frame of frames) {
12874
- await evaluateFrameBestEffort(
12875
- options.engine,
12876
- frame.frameRef,
12877
- MARK_SNAPSHOT_SEMANTICS_SCRIPT,
12878
- SNAPSHOT_SEMANTIC_ARGS
12879
- );
12880
- }
12881
- return async () => {
12882
- for (const frame of frames) {
12883
- await evaluateFrameBestEffort(
12638
+ await Promise.all(
12639
+ frames.map(
12640
+ (frame) => evaluateFrameBestEffort(
12884
12641
  options.engine,
12885
12642
  frame.frameRef,
12886
- CLEAR_SNAPSHOT_SEMANTICS_SCRIPT,
12887
- CLEAR_SNAPSHOT_SEMANTIC_ARGS
12888
- );
12889
- }
12643
+ MARK_SNAPSHOT_SEMANTICS_SCRIPT,
12644
+ SNAPSHOT_SEMANTIC_ARGS
12645
+ )
12646
+ )
12647
+ );
12648
+ return async () => {
12649
+ await Promise.all(
12650
+ frames.map(
12651
+ (frame) => evaluateFrameBestEffort(
12652
+ options.engine,
12653
+ frame.frameRef,
12654
+ CLEAR_SNAPSHOT_SEMANTICS_SCRIPT,
12655
+ CLEAR_SNAPSHOT_SEMANTIC_ARGS
12656
+ )
12657
+ )
12658
+ );
12890
12659
  };
12891
12660
  }
12892
12661
 
12893
12662
  // ../runtime-core/src/sdk/snapshot/compiler.ts
12663
+ var EXTRACTION_SKIPPED_COUNTER_TAGS = /* @__PURE__ */ new Set(["html", "head", "body"]);
12894
12664
  var INTERNAL_SNAPSHOT_ATTRIBUTE_NAMES = /* @__PURE__ */ new Set([
12895
12665
  "c",
12896
12666
  OPENSTEER_BOUNDARY_ATTR,
@@ -12905,7 +12675,7 @@ var INTERNAL_SNAPSHOT_ATTRIBUTE_NAMES = /* @__PURE__ */ new Set([
12905
12675
  var MAX_LIVE_COUNTER_SYNC_ATTEMPTS = 4;
12906
12676
  var CLEAR_LIVE_COUNTERS_SCRIPT = `(({ sparseCounterAttr }) => {
12907
12677
  const walk = (root) => {
12908
- for (const child of root.children) {
12678
+ for (const child of Array.from(root?.children || [])) {
12909
12679
  child.removeAttribute("c");
12910
12680
  child.removeAttribute(sparseCounterAttr);
12911
12681
  walk(child);
@@ -12921,7 +12691,7 @@ var CLEAR_LIVE_COUNTERS_SCRIPT = `(({ sparseCounterAttr }) => {
12921
12691
  var ASSIGN_SPARSE_COUNTERS_SCRIPT = `(({ sparseCounterAttr, startCounter }) => {
12922
12692
  let counter = startCounter;
12923
12693
  const walk = (root) => {
12924
- for (const child of root.children) {
12694
+ for (const child of Array.from(root?.children || [])) {
12925
12695
  child.setAttribute(sparseCounterAttr, String(counter++));
12926
12696
  walk(child);
12927
12697
  if (child.shadowRoot) {
@@ -12935,7 +12705,7 @@ var ASSIGN_SPARSE_COUNTERS_SCRIPT = `(({ sparseCounterAttr, startCounter }) => {
12935
12705
  })`;
12936
12706
  var APPLY_DENSE_COUNTERS_SCRIPT = `(({ sparseCounterAttr, mapping }) => {
12937
12707
  const walk = (root) => {
12938
- for (const child of root.children) {
12708
+ for (const child of Array.from(root?.children || [])) {
12939
12709
  child.removeAttribute("c");
12940
12710
  const sparse = child.getAttribute(sparseCounterAttr);
12941
12711
  if (sparse !== null) {
@@ -12998,20 +12768,22 @@ function ensureSparseCountersForAllRecords(counterRecords) {
12998
12768
  async function clearOpensteerLiveCounters(engine, pageRef) {
12999
12769
  const frames = await engine.listFrames({ pageRef });
13000
12770
  const failures = [];
13001
- for (const frame of frames) {
13002
- try {
13003
- await engine.evaluateFrame({
13004
- frameRef: frame.frameRef,
13005
- script: CLEAR_LIVE_COUNTERS_SCRIPT,
13006
- args: [{ sparseCounterAttr: OPENSTEER_SPARSE_COUNTER_ATTR }]
13007
- });
13008
- } catch (error) {
13009
- if (isDetachedFrameSyncError(error)) {
13010
- continue;
12771
+ await Promise.all(
12772
+ frames.map(async (frame) => {
12773
+ try {
12774
+ await engine.evaluateFrame({
12775
+ frameRef: frame.frameRef,
12776
+ script: CLEAR_LIVE_COUNTERS_SCRIPT,
12777
+ args: [{ sparseCounterAttr: OPENSTEER_SPARSE_COUNTER_ATTR }]
12778
+ });
12779
+ } catch (error) {
12780
+ if (isDetachedFrameSyncError(error)) {
12781
+ return;
12782
+ }
12783
+ failures.push(`frame ${frame.frameRef} could not be cleared (${describeError(error)}).`);
13011
12784
  }
13012
- failures.push(`frame ${frame.frameRef} could not be cleared (${describeError(error)}).`);
13013
- }
13014
- }
12785
+ })
12786
+ );
13015
12787
  if (failures.length > 0) {
13016
12788
  throw buildLiveCounterSyncError("clear live counters", failures);
13017
12789
  }
@@ -13060,25 +12832,29 @@ async function syncDenseCountersToLiveDom(engine, pageRef, sparseToDirectMapping
13060
12832
  denseCounter
13061
12833
  ])
13062
12834
  );
13063
- for (const frame of frames) {
13064
- try {
13065
- await engine.evaluateFrame({
13066
- frameRef: frame.frameRef,
13067
- script: APPLY_DENSE_COUNTERS_SCRIPT,
13068
- args: [
13069
- {
13070
- sparseCounterAttr: OPENSTEER_SPARSE_COUNTER_ATTR,
13071
- mapping: mappingObj
13072
- }
13073
- ]
13074
- });
13075
- } catch (error) {
13076
- if (isDetachedFrameSyncError(error)) {
13077
- continue;
12835
+ await Promise.all(
12836
+ frames.map(async (frame) => {
12837
+ try {
12838
+ await engine.evaluateFrame({
12839
+ frameRef: frame.frameRef,
12840
+ script: APPLY_DENSE_COUNTERS_SCRIPT,
12841
+ args: [
12842
+ {
12843
+ sparseCounterAttr: OPENSTEER_SPARSE_COUNTER_ATTR,
12844
+ mapping: mappingObj
12845
+ }
12846
+ ]
12847
+ });
12848
+ } catch (error) {
12849
+ if (isDetachedFrameSyncError(error)) {
12850
+ return;
12851
+ }
12852
+ failures.push(
12853
+ `frame ${frame.frameRef} could not be synchronized (${describeError(error)}).`
12854
+ );
13078
12855
  }
13079
- failures.push(`frame ${frame.frameRef} could not be synchronized (${describeError(error)}).`);
13080
- }
13081
- }
12856
+ })
12857
+ );
13082
12858
  if (failures.length > 0) {
13083
12859
  throw buildLiveCounterSyncError("synchronize dense counters", failures);
13084
12860
  }
@@ -13096,26 +12872,26 @@ async function compileOpensteerSnapshot(options) {
13096
12872
  await clearOpensteerLiveCounters(options.engine, options.pageRef);
13097
12873
  await assignSparseCountersToLiveDom(options.engine, options.pageRef);
13098
12874
  const pageInfo = await options.engine.getPageInfo({ pageRef: options.pageRef });
13099
- const mainSnapshot = await getMainDocumentSnapshot(options.engine, options.pageRef);
13100
- const snapshotsByDocumentRef = await collectDocumentSnapshots(options.engine, mainSnapshot);
12875
+ const { mainSnapshot, snapshotsByDocumentRef } = await getPageDocumentSnapshots(
12876
+ options.engine,
12877
+ options.pageRef
12878
+ );
13101
12879
  await cleanupLiveSemantics();
13102
12880
  cleanupLiveSemantics = async () => {
13103
12881
  };
13104
- const snapshotIndices = /* @__PURE__ */ new Map();
13105
12882
  const renderedNodes = /* @__PURE__ */ new Map();
13106
12883
  const rawHtml = renderDocumentSnapshot(
13107
12884
  mainSnapshot.documentRef,
13108
12885
  snapshotsByDocumentRef,
13109
- snapshotIndices,
13110
12886
  renderedNodes,
13111
12887
  {
13112
12888
  iframeDepth: 0,
13113
12889
  shadowDepth: 0
13114
12890
  }
13115
12891
  );
13116
- const cleanedHtml = options.mode === "extraction" ? cleanForExtraction(rawHtml) : cleanForAction(rawHtml);
13117
- const compiledHtml = assignCounters(cleanedHtml, renderedNodes);
13118
- const finalHtml = options.mode === "extraction" ? unwrapExtractionHtml(compiledHtml.html) : compiledHtml.html;
12892
+ const preparedSnapshotDom = options.mode === "extraction" ? prepareExtractionSnapshotDom(rawHtml) : prepareActionSnapshotDom(rawHtml);
12893
+ const compiledHtml = assignCountersInDom(preparedSnapshotDom, renderedNodes, options.mode);
12894
+ const finalHtml = preparedSnapshotDom === void 0 ? "" : options.mode === "extraction" ? serializePreparedExtractionSnapshot(preparedSnapshotDom) : serializePreparedActionSnapshot(preparedSnapshotDom);
13119
12895
  ensureSparseCountersForAllRecords(compiledHtml.counterRecords);
13120
12896
  await syncDenseCountersToLiveDom(
13121
12897
  options.engine,
@@ -13150,6 +12926,25 @@ async function getMainDocumentSnapshot(engine, pageRef) {
13150
12926
  }
13151
12927
  return engine.getDomSnapshot({ frameRef: mainFrame.frameRef });
13152
12928
  }
12929
+ async function getPageDocumentSnapshots(engine, pageRef) {
12930
+ const bundleEngine = engine;
12931
+ const bundledSnapshots = await bundleEngine.getPageDomSnapshots?.({ pageRef });
12932
+ if (bundledSnapshots && bundledSnapshots.length > 0) {
12933
+ const mainSnapshot2 = bundledSnapshots.find((snapshot) => snapshot.parentDocumentRef === void 0) ?? bundledSnapshots[0];
12934
+ return {
12935
+ mainSnapshot: mainSnapshot2,
12936
+ snapshotsByDocumentRef: new Map(
12937
+ bundledSnapshots.map((snapshot) => [snapshot.documentRef, snapshot])
12938
+ )
12939
+ };
12940
+ }
12941
+ const mainSnapshot = await getMainDocumentSnapshot(engine, pageRef);
12942
+ const snapshotsByDocumentRef = await collectDocumentSnapshots(engine, mainSnapshot);
12943
+ return {
12944
+ mainSnapshot,
12945
+ snapshotsByDocumentRef
12946
+ };
12947
+ }
13153
12948
  async function collectDocumentSnapshots(engine, mainSnapshot) {
13154
12949
  const snapshotsByDocumentRef = /* @__PURE__ */ new Map([
13155
12950
  [mainSnapshot.documentRef, mainSnapshot]
@@ -13168,7 +12963,7 @@ async function collectDocumentSnapshots(engine, mainSnapshot) {
13168
12963
  }
13169
12964
  return snapshotsByDocumentRef;
13170
12965
  }
13171
- function renderDocumentSnapshot(documentRef, snapshotsByDocumentRef, snapshotIndices, renderedNodes, depth) {
12966
+ function renderDocumentSnapshot(documentRef, snapshotsByDocumentRef, renderedNodes, depth) {
13172
12967
  const snapshot = snapshotsByDocumentRef.get(documentRef);
13173
12968
  if (!snapshot) {
13174
12969
  return "";
@@ -13180,17 +12975,9 @@ function renderDocumentSnapshot(documentRef, snapshotsByDocumentRef, snapshotInd
13180
12975
  `snapshot ${snapshot.documentRef} is missing root node ${String(snapshot.rootSnapshotNodeId)}`
13181
12976
  );
13182
12977
  }
13183
- return renderNode(
13184
- snapshot,
13185
- rootNode,
13186
- nodesById,
13187
- snapshotsByDocumentRef,
13188
- snapshotIndices,
13189
- renderedNodes,
13190
- depth
13191
- );
12978
+ return renderNode(snapshot, rootNode, nodesById, snapshotsByDocumentRef, renderedNodes, depth);
13192
12979
  }
13193
- function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotIndices, renderedNodes, depth) {
12980
+ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, renderedNodes, depth) {
13194
12981
  if (node.nodeType === 3) {
13195
12982
  return escapeHtml(node.nodeValue || node.textContent || "");
13196
12983
  }
@@ -13198,56 +12985,26 @@ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotI
13198
12985
  return "";
13199
12986
  }
13200
12987
  if (node.nodeType === 9 || node.nodeType === 11) {
13201
- return renderChildren(
13202
- snapshot,
13203
- node,
13204
- nodesById,
13205
- snapshotsByDocumentRef,
13206
- snapshotIndices,
13207
- renderedNodes,
13208
- depth
13209
- );
12988
+ return renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, renderedNodes, depth);
13210
12989
  }
13211
12990
  if (node.nodeType !== 1) {
13212
- return renderChildren(
13213
- snapshot,
13214
- node,
13215
- nodesById,
13216
- snapshotsByDocumentRef,
13217
- snapshotIndices,
13218
- renderedNodes,
13219
- depth
13220
- );
12991
+ return renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, renderedNodes, depth);
13221
12992
  }
13222
12993
  const tagName = normalizeTagName(node.nodeName);
13223
12994
  if (isPseudoElementTagName(tagName)) {
13224
- return renderChildren(
13225
- snapshot,
13226
- node,
13227
- nodesById,
13228
- snapshotsByDocumentRef,
13229
- snapshotIndices,
13230
- renderedNodes,
13231
- depth
13232
- );
12995
+ return renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, renderedNodes, depth);
13233
12996
  }
13234
12997
  if ((depth.iframeDepth > 0 || depth.shadowDepth > 0) && (tagName === "html" || tagName === "head" || tagName === "body")) {
13235
- return renderChildren(
13236
- snapshot,
13237
- node,
13238
- nodesById,
13239
- snapshotsByDocumentRef,
13240
- snapshotIndices,
13241
- renderedNodes,
13242
- depth
13243
- );
12998
+ return renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, renderedNodes, depth);
13244
12999
  }
13245
13000
  const snapshotAttributes = normalizeNodeAttributes(node.attributes);
13001
+ const snapshotAttributeIndex = indexNodeAttributes(snapshotAttributes);
13246
13002
  const authoredAttributes = stripInternalSnapshotAttributes(snapshotAttributes);
13003
+ const authoredAttributeIndex = indexNodeAttributes(authoredAttributes);
13247
13004
  const attributes = [...authoredAttributes];
13248
- const subtreeHidden = hasAttribute3(snapshotAttributes, OPENSTEER_HIDDEN_ATTR) || isLikelySubtreeHidden(node);
13249
- const selfHidden = !subtreeHidden && (hasAttribute3(snapshotAttributes, OPENSTEER_SELF_HIDDEN_ATTR) || isLikelySelfHidden(node, nodesById));
13250
- const interactive = !subtreeHidden && !selfHidden && (hasAttribute3(snapshotAttributes, OPENSTEER_INTERACTIVE_ATTR) || isLikelyInteractive(tagName, node, authoredAttributes));
13005
+ const subtreeHidden = snapshotAttributeIndex.has(OPENSTEER_HIDDEN_ATTR) || isLikelySubtreeHidden(node);
13006
+ const selfHidden = !subtreeHidden && (snapshotAttributeIndex.has(OPENSTEER_SELF_HIDDEN_ATTR) || isLikelySelfHidden(node, nodesById));
13007
+ const interactive = !subtreeHidden && !selfHidden && (snapshotAttributeIndex.has(OPENSTEER_INTERACTIVE_ATTR) || isLikelyInteractive(tagName, node, authoredAttributes, authoredAttributeIndex));
13251
13008
  if (interactive) {
13252
13009
  attributes.push({ name: OPENSTEER_INTERACTIVE_ATTR, value: "1" });
13253
13010
  }
@@ -13256,7 +13013,7 @@ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotI
13256
13013
  } else if (selfHidden) {
13257
13014
  attributes.push({ name: OPENSTEER_SELF_HIDDEN_ATTR, value: "1" });
13258
13015
  }
13259
- const sparseCounter = findAttributeValue(snapshotAttributes, OPENSTEER_SPARSE_COUNTER_ATTR);
13016
+ const sparseCounter = snapshotAttributeIndex.get(OPENSTEER_SPARSE_COUNTER_ATTR);
13260
13017
  if (sparseCounter !== void 0) {
13261
13018
  attributes.push({ name: OPENSTEER_SPARSE_COUNTER_ATTR, value: sparseCounter });
13262
13019
  }
@@ -13267,21 +13024,18 @@ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotI
13267
13024
  const syntheticNodeId = buildSyntheticNodeId(snapshot, node);
13268
13025
  attributes.push({ name: OPENSTEER_NODE_ID_ATTR, value: syntheticNodeId });
13269
13026
  renderedNodes.set(syntheticNodeId, {
13270
- locator: createNodeLocator(snapshot.documentRef, snapshot.documentEpoch, node.nodeRef),
13271
- anchor: buildSnapshotElementAnchor(snapshot, node, snapshotsByDocumentRef, snapshotIndices),
13272
13027
  pageRef: snapshot.pageRef,
13273
13028
  frameRef: snapshot.frameRef,
13274
13029
  documentRef: snapshot.documentRef,
13275
13030
  documentEpoch: snapshot.documentEpoch,
13276
13031
  nodeRef: node.nodeRef,
13277
13032
  tagName: tagName.toUpperCase(),
13278
- pathHint: buildPathHint(tagName, authoredAttributes),
13279
- ...buildTextSnippet(node.textContent) === void 0 ? {} : { text: buildTextSnippet(node.textContent) },
13280
13033
  ...authoredAttributes.length === 0 ? {} : { attributes: authoredAttributes },
13281
13034
  iframeDepth: depth.iframeDepth,
13282
13035
  shadowDepth: depth.shadowDepth,
13283
13036
  interactive,
13284
- liveCounterSyncEligible: isLiveCounterSyncEligible(node, nodesById)
13037
+ liveCounterSyncEligible: isLiveCounterSyncEligible(node, nodesById),
13038
+ ...node.textContent === void 0 ? {} : { textContent: node.textContent }
13285
13039
  });
13286
13040
  }
13287
13041
  const attributeText = attributesToHtml(attributes);
@@ -13290,7 +13044,6 @@ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotI
13290
13044
  node,
13291
13045
  nodesById,
13292
13046
  snapshotsByDocumentRef,
13293
- snapshotIndices,
13294
13047
  renderedNodes,
13295
13048
  depth
13296
13049
  );
@@ -13301,7 +13054,6 @@ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotI
13301
13054
  const iframeHtml = renderDocumentSnapshot(
13302
13055
  node.contentDocumentRef,
13303
13056
  snapshotsByDocumentRef,
13304
- snapshotIndices,
13305
13057
  renderedNodes,
13306
13058
  {
13307
13059
  iframeDepth: depth.iframeDepth + 1,
@@ -13313,7 +13065,7 @@ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotI
13313
13065
  }
13314
13066
  return `${elementHtml}<${OPENSTEER_IFRAME_BOUNDARY_TAG} ${OPENSTEER_BOUNDARY_ATTR}="iframe">${iframeHtml}</${OPENSTEER_IFRAME_BOUNDARY_TAG}>`;
13315
13067
  }
13316
- function renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotIndices, renderedNodes, depth) {
13068
+ function renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, renderedNodes, depth) {
13317
13069
  const regularChildren = [];
13318
13070
  const shadowChildren = [];
13319
13071
  for (const childSnapshotNodeId of node.childSnapshotNodeIds) {
@@ -13330,18 +13082,10 @@ function renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, snaps
13330
13082
  const chunks = [];
13331
13083
  if (shadowChildren.length > 0) {
13332
13084
  const shadowHtml = shadowChildren.map(
13333
- (child) => renderNode(
13334
- snapshot,
13335
- child,
13336
- nodesById,
13337
- snapshotsByDocumentRef,
13338
- snapshotIndices,
13339
- renderedNodes,
13340
- {
13341
- iframeDepth: depth.iframeDepth,
13342
- shadowDepth: depth.shadowDepth + 1
13343
- }
13344
- )
13085
+ (child) => renderNode(snapshot, child, nodesById, snapshotsByDocumentRef, renderedNodes, {
13086
+ iframeDepth: depth.iframeDepth,
13087
+ shadowDepth: depth.shadowDepth + 1
13088
+ })
13345
13089
  ).join("");
13346
13090
  chunks.push(
13347
13091
  `<${OPENSTEER_SHADOW_BOUNDARY_TAG} ${OPENSTEER_BOUNDARY_ATTR}="shadow">${shadowHtml}</${OPENSTEER_SHADOW_BOUNDARY_TAG}>`
@@ -13349,24 +13093,21 @@ function renderChildren(snapshot, node, nodesById, snapshotsByDocumentRef, snaps
13349
13093
  }
13350
13094
  for (const child of regularChildren) {
13351
13095
  chunks.push(
13352
- renderNode(
13353
- snapshot,
13354
- child,
13355
- nodesById,
13356
- snapshotsByDocumentRef,
13357
- snapshotIndices,
13358
- renderedNodes,
13359
- depth
13360
- )
13096
+ renderNode(snapshot, child, nodesById, snapshotsByDocumentRef, renderedNodes, depth)
13361
13097
  );
13362
13098
  }
13363
13099
  return chunks.join("");
13364
13100
  }
13365
- function assignCounters(cleanedHtml, renderedNodes) {
13366
- const $ = cheerio.load(cleanedHtml, { xmlMode: false });
13101
+ function assignCountersInDom($, renderedNodes, mode) {
13367
13102
  const counterRecords = /* @__PURE__ */ new Map();
13368
13103
  const sparseToDirectMapping = /* @__PURE__ */ new Map();
13369
13104
  let nextCounter = 1;
13105
+ if (!$) {
13106
+ return {
13107
+ counterRecords,
13108
+ sparseToDirectMapping
13109
+ };
13110
+ }
13370
13111
  $("*").each(function assignElementCounter() {
13371
13112
  const el = $(this);
13372
13113
  const syntheticNodeId = el.attr(OPENSTEER_NODE_ID_ATTR);
@@ -13378,14 +13119,24 @@ function assignCounters(cleanedHtml, renderedNodes) {
13378
13119
  if (!rendered) {
13379
13120
  return;
13380
13121
  }
13122
+ if (mode === "extraction" && EXTRACTION_SKIPPED_COUNTER_TAGS.has(rendered.tagName.toLowerCase())) {
13123
+ el.removeAttr(OPENSTEER_SPARSE_COUNTER_ATTR);
13124
+ return;
13125
+ }
13381
13126
  const rawSparseCounter = el.attr(OPENSTEER_SPARSE_COUNTER_ATTR);
13382
13127
  el.removeAttr(OPENSTEER_SPARSE_COUNTER_ATTR);
13383
13128
  const sparseCounter = rawSparseCounter ? Number.parseInt(rawSparseCounter, 10) : void 0;
13129
+ const replayableSparseCounter = typeof sparseCounter === "number" && Number.isFinite(sparseCounter) ? sparseCounter : void 0;
13130
+ if (rendered.liveCounterSyncEligible && replayableSparseCounter === void 0) {
13131
+ return;
13132
+ }
13384
13133
  const counter = nextCounter++;
13385
13134
  el.attr("c", String(counter));
13386
- if (sparseCounter !== void 0 && Number.isFinite(sparseCounter)) {
13387
- sparseToDirectMapping.set(sparseCounter, counter);
13135
+ if (replayableSparseCounter !== void 0) {
13136
+ sparseToDirectMapping.set(replayableSparseCounter, counter);
13388
13137
  }
13138
+ const pathHint = buildPathHint(rendered.tagName.toLowerCase(), rendered.attributes ?? []);
13139
+ const text = buildTextSnippet(rendered.textContent);
13389
13140
  counterRecords.set(counter, {
13390
13141
  element: counter,
13391
13142
  pageRef: rendered.pageRef,
@@ -13394,20 +13145,17 @@ function assignCounters(cleanedHtml, renderedNodes) {
13394
13145
  documentEpoch: rendered.documentEpoch,
13395
13146
  nodeRef: rendered.nodeRef,
13396
13147
  tagName: rendered.tagName,
13397
- pathHint: rendered.pathHint,
13398
- ...rendered.text === void 0 ? {} : { text: rendered.text },
13148
+ pathHint,
13149
+ ...text === void 0 ? {} : { text },
13399
13150
  ...rendered.attributes === void 0 ? {} : { attributes: rendered.attributes },
13400
13151
  iframeDepth: rendered.iframeDepth,
13401
13152
  shadowDepth: rendered.shadowDepth,
13402
13153
  interactive: rendered.interactive,
13403
13154
  liveCounterSyncEligible: rendered.liveCounterSyncEligible,
13404
- locator: rendered.locator,
13405
- anchor: rendered.anchor,
13406
- ...sparseCounter !== void 0 && Number.isFinite(sparseCounter) ? { sparseCounter } : {}
13155
+ ...replayableSparseCounter === void 0 ? {} : { sparseCounter: replayableSparseCounter }
13407
13156
  });
13408
13157
  });
13409
13158
  return {
13410
- html: $.html(),
13411
13159
  counterRecords,
13412
13160
  sparseToDirectMapping
13413
13161
  };
@@ -13481,28 +13229,28 @@ function isLikelySelfHidden(node, nodesById) {
13481
13229
  }
13482
13230
  return !hasVisibleOutOfFlowChild(node, nodesById);
13483
13231
  }
13484
- function isLikelyInteractive(tagName, node, attributes) {
13232
+ function isLikelyInteractive(tagName, node, attributes, attributeIndex) {
13485
13233
  if (NATIVE_INTERACTIVE_TAGS.has(tagName)) {
13486
- if (tagName === "input" && findAttributeValue(attributes, "type")?.toLowerCase() === "hidden") {
13234
+ if (tagName === "input" && attributeIndex.get("type")?.toLowerCase() === "hidden") {
13487
13235
  return false;
13488
13236
  }
13489
13237
  if (tagName !== "a") {
13490
13238
  return true;
13491
13239
  }
13492
13240
  }
13493
- if (tagName === "a" && findAttributeValue(attributes, "href") !== void 0) {
13241
+ if (tagName === "a" && attributeIndex.has("href")) {
13494
13242
  return true;
13495
13243
  }
13496
- 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) {
13244
+ if (attributeIndex.has("onclick") || attributeIndex.has("onmousedown") || attributeIndex.has("onmouseup") || attributeIndex.has("data-action") || attributeIndex.has("data-click") || attributeIndex.has("data-toggle")) {
13497
13245
  return true;
13498
13246
  }
13499
- if (hasNonNegativeTabIndex(findAttributeValue(attributes, "tabindex"))) {
13247
+ if (hasNonNegativeTabIndex(attributeIndex.get("tabindex"))) {
13500
13248
  return true;
13501
13249
  }
13502
- if (findAttributeValue(attributes, "contenteditable")?.toLowerCase() === "true") {
13250
+ if (attributeIndex.get("contenteditable")?.toLowerCase() === "true") {
13503
13251
  return true;
13504
13252
  }
13505
- const role = findAttributeValue(attributes, "role")?.toLowerCase();
13253
+ const role = attributeIndex.get("role")?.toLowerCase();
13506
13254
  return role !== void 0 && INTERACTIVE_ROLE_SET.has(role);
13507
13255
  }
13508
13256
  function hasVisibleOutOfFlowChild(node, nodesById) {
@@ -13565,14 +13313,6 @@ function parseOpacity(value) {
13565
13313
  const parsed = Number.parseFloat(value);
13566
13314
  return Number.isFinite(parsed) ? parsed : Number.NaN;
13567
13315
  }
13568
- function hasAttribute3(attributes, name) {
13569
- const normalizedName = name.toLowerCase();
13570
- return attributes.some((attribute) => attribute.name.toLowerCase() === normalizedName);
13571
- }
13572
- function unwrapExtractionHtml(html) {
13573
- const $ = cheerio.load(html, { xmlMode: false });
13574
- return $("body").html()?.trim() || html;
13575
- }
13576
13316
  function buildSyntheticNodeId(snapshot, node) {
13577
13317
  return `${snapshot.documentRef}:${String(snapshot.documentEpoch)}:${String(node.snapshotNodeId)}`;
13578
13318
  }
@@ -13619,6 +13359,13 @@ function findAttributeValue(attributes, name) {
13619
13359
  const normalizedName = name.toLowerCase();
13620
13360
  return attributes.find((attribute) => attribute.name.toLowerCase() === normalizedName)?.value;
13621
13361
  }
13362
+ function indexNodeAttributes(attributes) {
13363
+ const indexed = /* @__PURE__ */ new Map();
13364
+ for (const attribute of attributes) {
13365
+ indexed.set(attribute.name.toLowerCase(), attribute.value);
13366
+ }
13367
+ return indexed;
13368
+ }
13622
13369
  function attributesToHtml(attributes) {
13623
13370
  if (attributes.length === 0) {
13624
13371
  return "";
@@ -13628,56 +13375,6 @@ function attributesToHtml(attributes) {
13628
13375
  function escapeAttribute(value) {
13629
13376
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
13630
13377
  }
13631
- function buildSnapshotElementAnchor(snapshot, node, snapshotsByDocumentRef, snapshotIndices) {
13632
- const index = getSnapshotIndex(snapshot.documentRef, snapshotsByDocumentRef, snapshotIndices);
13633
- const localAnchor = buildLocalStructuralElementAnchor(index, node);
13634
- return prefixIframeContext(snapshot, localAnchor, snapshotsByDocumentRef, snapshotIndices);
13635
- }
13636
- function prefixIframeContext(snapshot, localPath, snapshotsByDocumentRef, snapshotIndices) {
13637
- if (snapshot.parentDocumentRef === void 0) {
13638
- return sanitizeStructuralElementAnchor(localPath);
13639
- }
13640
- const parentSnapshot = snapshotsByDocumentRef.get(snapshot.parentDocumentRef);
13641
- if (!parentSnapshot) {
13642
- throw new Error(
13643
- `document ${snapshot.documentRef} has parent ${snapshot.parentDocumentRef} but no parent snapshot`
13644
- );
13645
- }
13646
- const parentIndex = getSnapshotIndex(
13647
- parentSnapshot.documentRef,
13648
- snapshotsByDocumentRef,
13649
- snapshotIndices
13650
- );
13651
- const iframeHost = findIframeHostNode(parentIndex, snapshot.documentRef);
13652
- if (!iframeHost) {
13653
- throw new Error(
13654
- `document ${snapshot.documentRef} has parent ${snapshot.parentDocumentRef} but no iframe host`
13655
- );
13656
- }
13657
- const hostPath = buildSnapshotElementAnchor(
13658
- parentSnapshot,
13659
- iframeHost,
13660
- snapshotsByDocumentRef,
13661
- snapshotIndices
13662
- );
13663
- return sanitizeStructuralElementAnchor({
13664
- context: [...hostPath.context, { kind: "iframe", host: hostPath.nodes }, ...localPath.context],
13665
- nodes: localPath.nodes
13666
- });
13667
- }
13668
- function getSnapshotIndex(documentRef, snapshotsByDocumentRef, snapshotIndices) {
13669
- const existing = snapshotIndices.get(documentRef);
13670
- if (existing) {
13671
- return existing;
13672
- }
13673
- const snapshot = snapshotsByDocumentRef.get(documentRef);
13674
- if (!snapshot) {
13675
- throw new Error(`missing DOM snapshot for ${documentRef}`);
13676
- }
13677
- const index = createSnapshotIndex(snapshot);
13678
- snapshotIndices.set(documentRef, index);
13679
- return index;
13680
- }
13681
13378
  function escapeHtml(value) {
13682
13379
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
13683
13380
  }
@@ -14890,7 +14587,7 @@ var OpensteerSessionRuntime = class {
14890
14587
  options
14891
14588
  );
14892
14589
  }
14893
- return this.readSessionState();
14590
+ return this.readNavigationSummary();
14894
14591
  }
14895
14592
  const startedAt = Date.now();
14896
14593
  const root = await this.ensureRoot();
@@ -14911,7 +14608,8 @@ var OpensteerSessionRuntime = class {
14911
14608
  openedSessionRef = sessionRef;
14912
14609
  const createdPage = await timeout.runStep(
14913
14610
  () => engine.createPage({
14914
- sessionRef
14611
+ sessionRef,
14612
+ ...input.url === void 0 ? {} : { url: input.url }
14915
14613
  })
14916
14614
  );
14917
14615
  openedPageRef = createdPage.data.pageRef;
@@ -14921,18 +14619,19 @@ var OpensteerSessionRuntime = class {
14921
14619
  await timeout.runStep(() => this.ensureSemantics());
14922
14620
  let frameRef2 = createdPage.frameRef;
14923
14621
  if (input.url !== void 0) {
14924
- const navigation = await this.navigatePage(
14925
- {
14622
+ await timeout.runStep(
14623
+ () => settleWithPolicy(this.policy.settle, {
14926
14624
  operation: "session.open",
14625
+ trigger: "navigation",
14626
+ engine: this.requireEngine(),
14927
14627
  pageRef: createdPage.data.pageRef,
14928
- url: input.url
14929
- },
14930
- timeout
14628
+ signal: timeout.signal,
14629
+ remainingMs: timeout.remainingMs()
14630
+ })
14931
14631
  );
14932
- frameRef2 = navigation.data.mainFrame.frameRef;
14933
14632
  }
14934
14633
  return {
14935
- state: await timeout.runStep(() => this.readSessionState()),
14634
+ state: await timeout.runStep(() => this.readNavigationSummary()),
14936
14635
  frameRef: frameRef2
14937
14636
  };
14938
14637
  },
@@ -15025,7 +14724,11 @@ var OpensteerSessionRuntime = class {
15025
14724
  "page.new cannot use openerPageRef before a session exists"
15026
14725
  );
15027
14726
  }
15028
- return this.open(input.url === void 0 ? {} : { url: input.url }, options);
14727
+ const summary = await this.open(input.url === void 0 ? {} : { url: input.url }, options);
14728
+ return {
14729
+ pageRef: await this.ensurePageRef(),
14730
+ ...summary
14731
+ };
15029
14732
  }
15030
14733
  const startedAt = Date.now();
15031
14734
  try {
@@ -15040,7 +14743,7 @@ var OpensteerSessionRuntime = class {
15040
14743
  })
15041
14744
  );
15042
14745
  this.pageRef = created.data.pageRef;
15043
- return this.readSessionState();
14746
+ return this.readCreatedPageOutput(created.data.pageRef);
15044
14747
  },
15045
14748
  options
15046
14749
  );
@@ -15082,7 +14785,7 @@ var OpensteerSessionRuntime = class {
15082
14785
  () => this.requireEngine().activatePage({ pageRef: input.pageRef })
15083
14786
  );
15084
14787
  this.pageRef = input.pageRef;
15085
- return this.readSessionState();
14788
+ return this.readNavigationSummary(input.pageRef);
15086
14789
  },
15087
14790
  options
15088
14791
  );
@@ -15208,7 +14911,7 @@ var OpensteerSessionRuntime = class {
15208
14911
  timeout.throwIfAborted();
15209
14912
  return {
15210
14913
  navigation: navigation2,
15211
- state: await timeout.runStep(() => this.readSessionState())
14914
+ state: await timeout.runStep(() => this.readNavigationSummary(pageRef))
15212
14915
  };
15213
14916
  },
15214
14917
  (diagnostics) => {
@@ -16442,7 +16145,7 @@ var OpensteerSessionRuntime = class {
16442
16145
  let mutationCaptureDiagnostics;
16443
16146
  let boundaryDiagnostics;
16444
16147
  try {
16445
- const { artifacts, output } = await this.runMutationCapturedOperation(
16148
+ const { artifacts, output, result } = await this.runMutationCapturedOperation(
16446
16149
  "computer.execute",
16447
16150
  {
16448
16151
  ...input.captureNetwork === void 0 ? {} : { captureNetwork: input.captureNetwork },
@@ -16460,9 +16163,14 @@ var OpensteerSessionRuntime = class {
16460
16163
  await this.invalidateLiveSnapshotCounters([pageRef, output2.pageRef], timeout);
16461
16164
  this.pageRef = output2.pageRef;
16462
16165
  const artifacts2 = await this.persistComputerArtifacts(output2, timeout);
16166
+ const result2 = {
16167
+ ...await timeout.runStep(() => this.readNavigationSummary(output2.pageRef)),
16168
+ screenshot: artifacts2.screenshot
16169
+ };
16463
16170
  return {
16464
16171
  artifacts: { manifests: artifacts2.manifests },
16465
- output: artifacts2.output
16172
+ output: output2,
16173
+ result: result2
16466
16174
  };
16467
16175
  } catch (error) {
16468
16176
  boundaryDiagnostics ??= takeActionBoundaryDiagnostics(timeout.signal);
@@ -16499,7 +16207,7 @@ var OpensteerSessionRuntime = class {
16499
16207
  documentEpoch: output.screenshot.documentEpoch
16500
16208
  })
16501
16209
  });
16502
- return output;
16210
+ return result;
16503
16211
  } catch (error) {
16504
16212
  await this.appendTrace({
16505
16213
  operation: "computer.execute",
@@ -16647,8 +16355,9 @@ var OpensteerSessionRuntime = class {
16647
16355
  mutationCaptureDiagnostics = diagnostics;
16648
16356
  }
16649
16357
  );
16650
- const output = toOpensteerActionResult(executed.result);
16358
+ const output = toOpensteerActionResult(executed.result.resolved);
16651
16359
  const actionEvents = "events" in executed.result ? executed.result.events : void 0;
16360
+ const resolvedTarget = toOpensteerResolvedTarget2(executed.result.resolved);
16652
16361
  await this.appendTrace({
16653
16362
  operation,
16654
16363
  startedAt,
@@ -16656,8 +16365,13 @@ var OpensteerSessionRuntime = class {
16656
16365
  outcome: "ok",
16657
16366
  ...actionEvents === void 0 ? {} : { events: actionEvents },
16658
16367
  data: {
16659
- target: output.target,
16660
- ...output.point === void 0 ? {} : { point: output.point },
16368
+ target: resolvedTarget,
16369
+ ..."point" in executed.result && executed.result.point !== void 0 ? {
16370
+ point: {
16371
+ x: executed.result.point.x,
16372
+ y: executed.result.point.y
16373
+ }
16374
+ } : {},
16661
16375
  ...boundaryDiagnostics === void 0 ? {} : { settle: boundaryDiagnostics },
16662
16376
  ...buildMutationCaptureTraceData(mutationCaptureDiagnostics)
16663
16377
  },
@@ -18122,20 +17836,20 @@ var OpensteerSessionRuntime = class {
18122
17836
  throw error;
18123
17837
  }
18124
17838
  }
18125
- async readSessionState() {
18126
- const pageRef = await this.ensurePageRef();
17839
+ async readNavigationSummary(targetPageRef) {
17840
+ const pageRef = targetPageRef ?? await this.ensurePageRef();
18127
17841
  const pageInfo = await this.requireEngine().getPageInfo({ pageRef });
18128
- const sessionRef = this.sessionRef;
18129
- if (!sessionRef) {
18130
- throw new Error("Opensteer session is not initialized");
18131
- }
18132
17842
  return {
18133
- sessionRef,
18134
- pageRef,
18135
17843
  url: pageInfo.url,
18136
17844
  title: pageInfo.title
18137
17845
  };
18138
17846
  }
17847
+ async readCreatedPageOutput(pageRef) {
17848
+ return {
17849
+ pageRef,
17850
+ ...await this.readNavigationSummary(pageRef)
17851
+ };
17852
+ }
18139
17853
  async captureSnapshotArtifacts(pageRef, options, timeout) {
18140
17854
  const root = this.requireRoot();
18141
17855
  const mainFrame = await timeout.runStep(() => getMainFrame(this.requireEngine(), pageRef));
@@ -18207,12 +17921,12 @@ var OpensteerSessionRuntime = class {
18207
17921
  const screenshotPayload = manifestToExternalBinaryLocation(root.rootPath, screenshotManifest);
18208
17922
  return {
18209
17923
  manifests,
18210
- output: {
18211
- ...output,
18212
- screenshot: {
18213
- ...output.screenshot,
18214
- payload: screenshotPayload
18215
- }
17924
+ screenshot: {
17925
+ payload: screenshotPayload,
17926
+ format: output.screenshot.format,
17927
+ size: output.screenshot.size,
17928
+ coordinateSpace: output.screenshot.coordinateSpace,
17929
+ ...output.screenshot.clip === void 0 ? {} : { clip: output.screenshot.clip }
18216
17930
  }
18217
17931
  };
18218
17932
  }
@@ -19735,15 +19449,10 @@ function normalizeNamespace2(value) {
19735
19449
  const normalized = String(value ?? "default").trim();
19736
19450
  return normalized.length === 0 ? "default" : normalized;
19737
19451
  }
19738
- function toOpensteerActionResult(result) {
19452
+ function toOpensteerActionResult(target) {
19739
19453
  return {
19740
- target: toOpensteerResolvedTarget2(result.resolved),
19741
- ...result.point === void 0 ? {} : {
19742
- point: {
19743
- x: result.point.x,
19744
- y: result.point.y
19745
- }
19746
- }
19454
+ tagName: toOpensteerTagName2(target.node.nodeName),
19455
+ ...target.persist === void 0 ? {} : { persist: target.persist }
19747
19456
  };
19748
19457
  }
19749
19458
  function toOpensteerResolvedTarget2(target) {
@@ -19753,12 +19462,16 @@ function toOpensteerResolvedTarget2(target) {
19753
19462
  documentRef: target.documentRef,
19754
19463
  documentEpoch: target.documentEpoch,
19755
19464
  nodeRef: target.nodeRef,
19756
- tagName: target.node.nodeName.toUpperCase(),
19465
+ tagName: toOpensteerTagName2(target.node.nodeName),
19757
19466
  pathHint: buildPathSelectorHint(target.replayPath ?? target.anchor),
19758
19467
  ...target.persist === void 0 ? {} : { persist: target.persist },
19759
19468
  ...target.selectorUsed === void 0 ? {} : { selectorUsed: target.selectorUsed }
19760
19469
  };
19761
19470
  }
19471
+ function toOpensteerTagName2(nodeName) {
19472
+ const tagName = String(nodeName).trim().toLowerCase();
19473
+ return tagName.length === 0 ? "element" : tagName;
19474
+ }
19762
19475
  function normalizeOpensteerError(error) {
19763
19476
  return normalizeThrownOpensteerError(error, "Unknown Opensteer runtime failure");
19764
19477
  }
@@ -21394,7 +21107,11 @@ function generateReplayScript(options) {
21394
21107
  ``,
21395
21108
  ...renderOpensteerBootstrap(replayTarget),
21396
21109
  ``,
21397
- `const ${initialPageId} = (await opensteer.open(${JSON.stringify(initialPages[0]?.initialUrl ?? "")})).pageRef;`,
21110
+ `await opensteer.open(${JSON.stringify(initialPages[0]?.initialUrl ?? "")});`,
21111
+ `const ${initialPageId} = (await opensteer.listPages()).activePageRef;`,
21112
+ `if (!${initialPageId}) {`,
21113
+ ` throw new Error("Opensteer did not report an active page after open().");`,
21114
+ `}`,
21398
21115
  `let activePageRef: string | undefined = ${initialPageId};`,
21399
21116
  ``,
21400
21117
  `async function ensureActive(pageRef: string): Promise<void> {`,
@@ -22224,6 +21941,7 @@ function payloadByteLength(value) {
22224
21941
 
22225
21942
  // src/cloud/session-proxy.ts
22226
21943
  var TEMPORARY_CLOUD_WORKSPACE_PREFIX = "opensteer-cloud-workspace-";
21944
+ var CLOUD_SESSION_REUSE_EXPIRY_SKEW_MS = 1e4;
22227
21945
  var CloudSessionProxy = class {
22228
21946
  rootPath;
22229
21947
  workspace;
@@ -22237,6 +21955,8 @@ var CloudSessionProxy = class {
22237
21955
  automation;
22238
21956
  workspaceStore;
22239
21957
  syncWorkspaceOnClose = false;
21958
+ liveSessionStateEstablished = false;
21959
+ storedInstrumentation = [];
22240
21960
  constructor(cloud, options = {}) {
22241
21961
  this.cloud = cloud;
22242
21962
  this.workspace = options.workspace;
@@ -22266,15 +21986,12 @@ var CloudSessionProxy = class {
22266
21986
  if (this.client === void 0 && this.sessionId === void 0 && persisted !== void 0 && await this.isReusableCloudSession(persisted.sessionId)) {
22267
21987
  this.bindClient(persisted);
22268
21988
  }
22269
- if (this.automation) {
22270
- try {
22271
- const sessionInfo = await this.automation.getSessionInfo();
22272
- return {
22273
- ...sessionInfo,
22274
- ...this.workspace === void 0 ? {} : { workspace: this.workspace }
22275
- };
22276
- } catch {
22277
- }
21989
+ const sessionInfo = this.automation ? await this.automation.getSessionInfo().catch(() => void 0) : void 0;
21990
+ if (sessionInfo !== void 0) {
21991
+ return {
21992
+ ...sessionInfo,
21993
+ ...this.workspace === void 0 ? {} : { workspace: this.workspace }
21994
+ };
22278
21995
  }
22279
21996
  return {
22280
21997
  provider: {
@@ -22386,12 +22103,26 @@ var CloudSessionProxy = class {
22386
22103
  return this.invokeSemanticOperation("session.cookies", input);
22387
22104
  }
22388
22105
  async route(input) {
22389
- await this.ensureSession();
22390
- return this.requireAutomation().route(input);
22106
+ const registration = await this.invokeBootstrapInstrumentationOperation(
22107
+ "instrumentation.route",
22108
+ (automation) => automation.route(input)
22109
+ );
22110
+ this.storedInstrumentation.push({
22111
+ kind: "route",
22112
+ input
22113
+ });
22114
+ return registration;
22391
22115
  }
22392
22116
  async interceptScript(input) {
22393
- await this.ensureSession();
22394
- return this.requireAutomation().interceptScript(input);
22117
+ const registration = await this.invokeBootstrapInstrumentationOperation(
22118
+ "instrumentation.intercept-script",
22119
+ (automation) => automation.interceptScript(input)
22120
+ );
22121
+ this.storedInstrumentation.push({
22122
+ kind: "intercept-script",
22123
+ input
22124
+ });
22125
+ return registration;
22395
22126
  }
22396
22127
  async getStorageSnapshot(input = {}) {
22397
22128
  return this.invokeSemanticOperation("session.storage", input);
@@ -22439,6 +22170,8 @@ var CloudSessionProxy = class {
22439
22170
  this.client = void 0;
22440
22171
  this.sessionId = void 0;
22441
22172
  this.semanticGrant = void 0;
22173
+ this.liveSessionStateEstablished = false;
22174
+ this.storedInstrumentation.length = 0;
22442
22175
  if (this.cleanupRootOnClose) {
22443
22176
  await rm(this.rootPath, { recursive: true, force: true }).catch(() => void 0);
22444
22177
  }
@@ -22466,6 +22199,8 @@ var CloudSessionProxy = class {
22466
22199
  this.automation = void 0;
22467
22200
  this.sessionId = void 0;
22468
22201
  this.semanticGrant = void 0;
22202
+ this.liveSessionStateEstablished = false;
22203
+ this.storedInstrumentation.length = 0;
22469
22204
  if (syncError !== void 0) {
22470
22205
  throw syncError;
22471
22206
  }
@@ -22513,6 +22248,7 @@ var CloudSessionProxy = class {
22513
22248
  };
22514
22249
  await this.writePersistedSession(record);
22515
22250
  this.bindClient(record, session.initialGrants?.semantic);
22251
+ await this.restoreStoredInstrumentation();
22516
22252
  }
22517
22253
  async syncWorkspaceToCloud() {
22518
22254
  if (this.workspace === void 0) {
@@ -22524,6 +22260,7 @@ var CloudSessionProxy = class {
22524
22260
  bindClient(record, initialSemanticGrant) {
22525
22261
  this.sessionId = record.sessionId;
22526
22262
  this.semanticGrant = initialSemanticGrant?.kind === "semantic" ? initialSemanticGrant : void 0;
22263
+ this.liveSessionStateEstablished = false;
22527
22264
  this.client = new OpensteerSemanticRestClient({
22528
22265
  getBaseUrl: async () => (await this.ensureSemanticGrant()).url,
22529
22266
  getAuthorizationHeader: async () => `Bearer ${(await this.ensureSemanticGrant()).token}`,
@@ -22531,6 +22268,19 @@ var CloudSessionProxy = class {
22531
22268
  });
22532
22269
  this.automation = new OpensteerCloudAutomationClient(this.cloud, record.sessionId);
22533
22270
  }
22271
+ async restoreStoredInstrumentation() {
22272
+ if (this.storedInstrumentation.length === 0) {
22273
+ return;
22274
+ }
22275
+ const automation = this.requireAutomation();
22276
+ for (const registration of this.storedInstrumentation) {
22277
+ if (registration.kind === "route") {
22278
+ await automation.route(registration.input);
22279
+ } else {
22280
+ await automation.interceptScript(registration.input);
22281
+ }
22282
+ }
22283
+ }
22534
22284
  async ensureWorkspaceStore() {
22535
22285
  if (this.workspaceStore !== void 0) {
22536
22286
  return this.workspaceStore;
@@ -22553,13 +22303,22 @@ var CloudSessionProxy = class {
22553
22303
  async clearPersistedSession() {
22554
22304
  await clearPersistedSessionRecord(this.rootPath, "cloud").catch(() => void 0);
22555
22305
  }
22306
+ async invalidateSession() {
22307
+ await this.automation?.close().catch(() => void 0);
22308
+ this.automation = void 0;
22309
+ this.client = void 0;
22310
+ this.sessionId = void 0;
22311
+ this.semanticGrant = void 0;
22312
+ this.liveSessionStateEstablished = false;
22313
+ await this.clearPersistedSession();
22314
+ }
22556
22315
  async isReusableCloudSession(sessionId, timeout) {
22557
22316
  try {
22558
22317
  const session = await this.cloud.getSession(sessionId, {
22559
22318
  signal: timeout?.signal,
22560
22319
  timeoutMs: timeout?.remainingMs()
22561
22320
  });
22562
- return session.status !== "closed" && session.status !== "failed";
22321
+ return isReusableCloudSessionState(session);
22563
22322
  } catch (error) {
22564
22323
  if (isMissingCloudSessionError(error)) {
22565
22324
  return false;
@@ -22608,26 +22367,79 @@ var CloudSessionProxy = class {
22608
22367
  try {
22609
22368
  await this.ensureSemanticGrant(true);
22610
22369
  return true;
22611
- } catch {
22370
+ } catch (refreshError) {
22371
+ if (await this.resetStaleSession(refreshError)) {
22372
+ throw refreshError;
22373
+ }
22612
22374
  return false;
22613
22375
  }
22614
22376
  }
22615
22377
  async invokeSemanticOperation(operation, input, sessionInit = {}) {
22616
- return this.runOperationWithPolicy(operation, async (timeout) => {
22378
+ return this.runOperationWithSessionRecovery(operation, async (timeout) => {
22617
22379
  await this.ensureSession(sessionInit, timeout);
22618
22380
  await this.ensureSemanticGrant(false, timeout);
22619
- return this.requireClient().invoke(operation, input, {
22381
+ const output = await this.requireClient().invoke(operation, input, {
22620
22382
  signal: timeout.signal,
22621
22383
  timeoutMs: timeout.remainingMs()
22622
22384
  });
22385
+ this.noteSuccessfulLiveOperation(operation);
22386
+ return output;
22623
22387
  });
22624
22388
  }
22625
22389
  async invokeAutomationOperation(operation, invoke, sessionInit = {}) {
22626
- return this.runOperationWithPolicy(operation, async (timeout) => {
22390
+ return this.runOperationWithSessionRecovery(operation, async (timeout) => {
22627
22391
  await this.ensureSession(sessionInit, timeout);
22628
- return invoke(this.requireAutomation());
22392
+ const output = await invoke(this.requireAutomation());
22393
+ this.noteSuccessfulLiveOperation(operation);
22394
+ return output;
22629
22395
  });
22630
22396
  }
22397
+ async invokeBootstrapInstrumentationOperation(operation, invoke) {
22398
+ let recovered = false;
22399
+ while (true) {
22400
+ try {
22401
+ await this.ensureSession();
22402
+ return await invoke(this.requireAutomation());
22403
+ } catch (error) {
22404
+ const stale = await this.resetStaleSession(error);
22405
+ if (!stale || recovered || !this.canRecoverWithFreshSession(operation)) {
22406
+ throw error;
22407
+ }
22408
+ recovered = true;
22409
+ }
22410
+ }
22411
+ }
22412
+ async runOperationWithSessionRecovery(operation, invoke) {
22413
+ return this.runOperationWithPolicy(operation, async (timeout) => {
22414
+ let recovered = false;
22415
+ while (true) {
22416
+ try {
22417
+ return await invoke(timeout);
22418
+ } catch (error) {
22419
+ const stale = await this.resetStaleSession(error);
22420
+ if (!stale || recovered || !this.canRecoverWithFreshSession(operation)) {
22421
+ throw error;
22422
+ }
22423
+ recovered = true;
22424
+ }
22425
+ }
22426
+ });
22427
+ }
22428
+ async resetStaleSession(error) {
22429
+ if (!isRecoverableCloudSessionError(error)) {
22430
+ return false;
22431
+ }
22432
+ await this.invalidateSession();
22433
+ return true;
22434
+ }
22435
+ canRecoverWithFreshSession(operation) {
22436
+ return !this.liveSessionStateEstablished && isBootstrapRecoveryOperation(operation);
22437
+ }
22438
+ noteSuccessfulLiveOperation(operation) {
22439
+ if (operation === "session.open" || operation === "page.list" || operation === "page.new" || operation === "page.activate" || operation === "page.close" || operation === "page.goto" || operation === "page.evaluate" || operation === "page.add-init-script" || operation === "page.snapshot" || operation === "dom.click" || operation === "dom.hover" || operation === "dom.input" || operation === "dom.scroll" || operation === "dom.extract" || operation === "network.query" || operation === "network.detail" || operation === "interaction.capture" || operation === "interaction.get" || operation === "interaction.diff" || operation === "interaction.replay" || operation === "scripts.capture" || operation === "artifact.read" || operation === "scripts.beautify" || operation === "scripts.deobfuscate" || operation === "scripts.sandbox" || operation === "captcha.solve" || operation === "session.cookies" || operation === "session.storage" || operation === "session.state" || operation === "session.fetch" || operation === "computer.execute") {
22440
+ this.liveSessionStateEstablished = true;
22441
+ }
22442
+ }
22631
22443
  async runOperationWithPolicy(operation, invoke) {
22632
22444
  return runWithPolicyTimeout(this.policy.timeout, { operation }, invoke);
22633
22445
  }
@@ -22651,8 +22463,29 @@ function assertSupportedCloudBrowserMode(browser) {
22651
22463
  }
22652
22464
  }
22653
22465
  function isMissingCloudSessionError(error) {
22466
+ if (error instanceof OpensteerCloudRequestError) {
22467
+ return error.statusCode === 404 && (error.code === void 0 || error.code === "CLOUD_SESSION_NOT_FOUND");
22468
+ }
22654
22469
  return error instanceof Error && /\b404\b/.test(error.message);
22655
22470
  }
22471
+ function isRecoverableCloudSessionError(error) {
22472
+ if (!(error instanceof OpensteerCloudRequestError)) {
22473
+ return false;
22474
+ }
22475
+ if (error.statusCode === 404) {
22476
+ return error.code === void 0 || error.code === "CLOUD_SESSION_NOT_FOUND";
22477
+ }
22478
+ return error.statusCode === 409 && error.code === "CLOUD_SESSION_STALE";
22479
+ }
22480
+ function isBootstrapRecoveryOperation(operation) {
22481
+ return operation === "session.open" || operation === "instrumentation.route" || operation === "instrumentation.intercept-script";
22482
+ }
22483
+ function isReusableCloudSessionState(session) {
22484
+ if (session.status === "closing" || session.status === "closed" || session.status === "failed") {
22485
+ return false;
22486
+ }
22487
+ return !(typeof session.expiresAt === "number" && session.expiresAt <= Date.now() + CLOUD_SESSION_REUSE_EXPIRY_SKEW_MS);
22488
+ }
22656
22489
  function isLoopbackBaseUrl(baseUrl) {
22657
22490
  let url;
22658
22491
  try {
@@ -22791,6 +22624,6 @@ function createOpensteerSemanticRuntime(input = {}) {
22791
22624
  });
22792
22625
  }
22793
22626
 
22794
- 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, isCurrentUrlField, isValidCssAttributeKey, loadEnvironment, normalizeExtractedValue, normalizeOpensteerProviderMode, parseDomDescriptorRecord, parseExtractionDescriptorRecord, requireCloudAppBaseUrl, resolveCloudConfig, resolveDomActionBridge, resolveExtractedValueInContext, resolveOpensteerEnvironment, resolveOpensteerProvider, resolveOpensteerRuntimeConfig, runWithPolicyTimeout, sanitizeElementPath, sanitizeReplayElementPath, sanitizeStructuralElementAnchor, settleWithPolicy, shouldKeepAttributeForPath };
22795
- //# sourceMappingURL=chunk-7LQL5YUR.js.map
22796
- //# sourceMappingURL=chunk-7LQL5YUR.js.map
22627
+ 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 };
22628
+ //# sourceMappingURL=chunk-3OHKIPBD.js.map
22629
+ //# sourceMappingURL=chunk-3OHKIPBD.js.map