opensteer 0.8.7 → 0.8.9

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.
@@ -1,6 +1,6 @@
1
- import path6, { join, resolve, basename, relative } from 'path';
2
- import { createHash, randomUUID } from 'crypto';
3
- import { access, rm, mkdtemp, mkdir, readFile, cp, readdir, writeFile, rename, stat, copyFile, open } from 'fs/promises';
1
+ import path7, { join, resolve, basename, relative } from 'path';
2
+ import { createHash, randomUUID, pbkdf2Sync, createDecipheriv } from 'crypto';
3
+ import { access, rm, mkdtemp, mkdir, readFile, cp, readdir, open, writeFile, rename, stat, copyFile } from 'fs/promises';
4
4
  import { pathToFileURL } from 'url';
5
5
  import { selectAll } from 'css-select';
6
6
  import { execFileSync, execFile, spawn } from 'child_process';
@@ -8,6 +8,7 @@ import { existsSync, readFileSync } from 'fs';
8
8
  import { tmpdir, homedir } from 'os';
9
9
  import { connectPlaywrightChromiumBrowser, createPlaywrightBrowserCoreEngine, disconnectPlaywrightChromiumBrowser } from '@opensteer/engine-playwright';
10
10
  import { promisify } from 'util';
11
+ import { AsyncLocalStorage } from 'async_hooks';
11
12
  import { createRequire } from 'module';
12
13
  import sharp from 'sharp';
13
14
  import * as cheerio from 'cheerio';
@@ -16,15 +17,12 @@ import vm from 'vm';
16
17
  import { gzip as gzip$1 } from 'zlib';
17
18
  import WebSocket2 from 'ws';
18
19
 
19
- // ../protocol/src/dom-action-bridge.ts
20
- var OPENSTEER_DOM_ACTION_BRIDGE_SYMBOL = /* @__PURE__ */ Symbol.for("@opensteer/dom-action-bridge");
21
- function isDomActionBridgeFactory(value) {
22
- return typeof value === "function";
23
- }
24
- function resolveDomActionBridge(engine) {
25
- const candidate = Reflect.get(engine, OPENSTEER_DOM_ACTION_BRIDGE_SYMBOL);
26
- return isDomActionBridgeFactory(candidate) ? candidate.call(engine) : void 0;
27
- }
20
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
21
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
22
+ }) : x)(function(x) {
23
+ if (typeof require !== "undefined") return require.apply(this, arguments);
24
+ throw Error('Dynamic require of "' + x + '" is not supported');
25
+ });
28
26
 
29
27
  // ../runtime-core/src/json.ts
30
28
  function isPlainObject(value) {
@@ -34,30 +32,30 @@ function isPlainObject(value) {
34
32
  const prototype = Object.getPrototypeOf(value);
35
33
  return prototype === Object.prototype || prototype === null;
36
34
  }
37
- function canonicalizeJsonValue(value, path13) {
35
+ function canonicalizeJsonValue(value, path15) {
38
36
  if (value === null || typeof value === "string" || typeof value === "boolean") {
39
37
  return value;
40
38
  }
41
39
  if (typeof value === "number") {
42
40
  if (!Number.isFinite(value)) {
43
- throw new TypeError(`${path13} must be a finite JSON number`);
41
+ throw new TypeError(`${path15} must be a finite JSON number`);
44
42
  }
45
43
  return value;
46
44
  }
47
45
  if (Array.isArray(value)) {
48
- return value.map((entry, index) => canonicalizeJsonValue(entry, `${path13}[${index}]`));
46
+ return value.map((entry, index) => canonicalizeJsonValue(entry, `${path15}[${index}]`));
49
47
  }
50
48
  if (!isPlainObject(value)) {
51
- throw new TypeError(`${path13} must be a plain JSON object`);
49
+ throw new TypeError(`${path15} must be a plain JSON object`);
52
50
  }
53
51
  const sorted = Object.keys(value).sort((left, right) => left.localeCompare(right));
54
52
  const result = {};
55
53
  for (const key of sorted) {
56
54
  const entry = value[key];
57
55
  if (entry === void 0) {
58
- throw new TypeError(`${path13}.${key} must not be undefined`);
56
+ throw new TypeError(`${path15}.${key} must not be undefined`);
59
57
  }
60
- result[key] = canonicalizeJsonValue(entry, `${path13}.${key}`);
58
+ result[key] = canonicalizeJsonValue(entry, `${path15}.${key}`);
61
59
  }
62
60
  return result;
63
61
  }
@@ -94,7 +92,7 @@ function joinStoragePath(...segments) {
94
92
  return segments.join("/");
95
93
  }
96
94
  function resolveStoragePath(rootPath, relativePath) {
97
- if (path6.isAbsolute(relativePath)) {
95
+ if (path7.isAbsolute(relativePath)) {
98
96
  throw new TypeError(`storage path ${relativePath} must be relative`);
99
97
  }
100
98
  const segments = relativePath.split("/");
@@ -106,7 +104,7 @@ function resolveStoragePath(rootPath, relativePath) {
106
104
  throw new TypeError(`storage path ${relativePath} must not contain path traversal`);
107
105
  }
108
106
  }
109
- return path6.join(rootPath, ...segments);
107
+ return path7.join(rootPath, ...segments);
110
108
  }
111
109
  async function ensureDirectory(directoryPath) {
112
110
  await mkdir(directoryPath, { recursive: true });
@@ -126,7 +124,7 @@ async function writeJsonFileAtomic(filePath, value) {
126
124
  await writeTextFileAtomic(filePath, stableJsonString(value));
127
125
  }
128
126
  async function writeTextFileAtomic(filePath, value) {
129
- await ensureDirectory(path6.dirname(filePath));
127
+ await ensureDirectory(path7.dirname(filePath));
130
128
  const temporaryPath = `${filePath}.${randomUUID()}.tmp`;
131
129
  await writeFile(temporaryPath, value, "utf8");
132
130
  await rename(temporaryPath, filePath);
@@ -135,7 +133,7 @@ async function writeJsonFileExclusive(filePath, value) {
135
133
  await writeTextFileExclusive(filePath, stableJsonString(value));
136
134
  }
137
135
  async function writeTextFileExclusive(filePath, value) {
138
- await ensureDirectory(path6.dirname(filePath));
136
+ await ensureDirectory(path7.dirname(filePath));
139
137
  const handle = await open(filePath, "wx");
140
138
  try {
141
139
  await handle.writeFile(value, "utf8");
@@ -144,7 +142,7 @@ async function writeTextFileExclusive(filePath, value) {
144
142
  }
145
143
  }
146
144
  async function writeBufferIfMissing(filePath, value) {
147
- await ensureDirectory(path6.dirname(filePath));
145
+ await ensureDirectory(path7.dirname(filePath));
148
146
  try {
149
147
  const handle = await open(filePath, "wx");
150
148
  try {
@@ -178,7 +176,7 @@ function isAlreadyExistsError(error) {
178
176
  return error?.code === "EEXIST";
179
177
  }
180
178
  async function withFilesystemLock(lockPath, task) {
181
- await ensureDirectory(path6.dirname(lockPath));
179
+ await ensureDirectory(path7.dirname(lockPath));
182
180
  let attempt = 0;
183
181
  while (true) {
184
182
  try {
@@ -190,7 +188,7 @@ async function withFilesystemLock(lockPath, task) {
190
188
  }
191
189
  const delayMs = LOCK_RETRY_DELAYS_MS[Math.min(attempt, LOCK_RETRY_DELAYS_MS.length - 1)];
192
190
  attempt += 1;
193
- await new Promise((resolve5) => setTimeout(resolve5, delayMs));
191
+ await new Promise((resolve4) => setTimeout(resolve4, delayMs));
194
192
  }
195
193
  }
196
194
  try {
@@ -233,8 +231,8 @@ async function readStructuredPayload(objectPath) {
233
231
  var FilesystemArtifactStore = class {
234
232
  constructor(rootPath) {
235
233
  this.rootPath = rootPath;
236
- this.manifestsDirectory = path6.join(this.rootPath, "artifacts", "manifests");
237
- this.objectsDirectory = path6.join(this.rootPath, "artifacts", "objects", "sha256");
234
+ this.manifestsDirectory = path7.join(this.rootPath, "artifacts", "manifests");
235
+ this.objectsDirectory = path7.join(this.rootPath, "artifacts", "objects", "sha256");
238
236
  }
239
237
  manifestsDirectory;
240
238
  objectsDirectory;
@@ -459,7 +457,7 @@ var FilesystemArtifactStore = class {
459
457
  }
460
458
  }
461
459
  manifestPath(artifactId) {
462
- return path6.join(this.manifestsDirectory, `${encodePathSegment(artifactId)}.json`);
460
+ return path7.join(this.manifestsDirectory, `${encodePathSegment(artifactId)}.json`);
463
461
  }
464
462
  };
465
463
  function createArtifactStore(rootPath) {
@@ -487,6 +485,556 @@ function mediaTypeExtension(mediaType) {
487
485
  }
488
486
  }
489
487
 
488
+ // ../protocol/src/dom-action-bridge.ts
489
+ var OPENSTEER_DOM_ACTION_BRIDGE_SYMBOL = /* @__PURE__ */ Symbol.for("@opensteer/dom-action-bridge");
490
+ function isDomActionBridgeFactory(value) {
491
+ return typeof value === "function";
492
+ }
493
+ function resolveDomActionBridge(engine) {
494
+ const candidate = Reflect.get(engine, OPENSTEER_DOM_ACTION_BRIDGE_SYMBOL);
495
+ return isDomActionBridgeFactory(candidate) ? candidate.call(engine) : void 0;
496
+ }
497
+
498
+ // ../runtime-core/src/observation-utils.ts
499
+ var REDACTED = "[REDACTED]";
500
+ var SENSITIVE_KEY_PATTERN = /(authorization|proxy[_-]?authorization|cookie|set-cookie|api[_-]?key|access[_-]?token|refresh[_-]?token|auth[_-]?token|token|secret|password|passwd|private[_-]?key|database[_-]?url|db[_-]?url|session(?:id)?|csrf(?:token)?)/i;
501
+ var SENSITIVE_VALUE_PATTERNS = [
502
+ /\bsk-[A-Za-z0-9_-]{20,}\b/g,
503
+ /\bAIza[0-9A-Za-z_-]{20,}\b/g,
504
+ /\b(?:gh[pousr]_[A-Za-z0-9]{20,}|github_pat_[A-Za-z0-9_]{20,})\b/g,
505
+ /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g,
506
+ /\bBearer\s+[A-Za-z0-9._~+/=-]{16,}\b/gi
507
+ ];
508
+ function normalizeObservationContext(context) {
509
+ if (context === void 0) {
510
+ return void 0;
511
+ }
512
+ const normalized = {
513
+ ...context.sessionRef === void 0 ? {} : { sessionRef: context.sessionRef },
514
+ ...context.pageRef === void 0 ? {} : { pageRef: context.pageRef },
515
+ ...context.frameRef === void 0 ? {} : { frameRef: context.frameRef },
516
+ ...context.documentRef === void 0 ? {} : { documentRef: context.documentRef },
517
+ ...context.documentEpoch === void 0 ? {} : { documentEpoch: context.documentEpoch }
518
+ };
519
+ return Object.keys(normalized).length === 0 ? void 0 : normalized;
520
+ }
521
+ function createObservationRedactor(config) {
522
+ const state = createRedactionState(config);
523
+ return {
524
+ redactText(value) {
525
+ return redactString(value, state);
526
+ },
527
+ redactJson(value) {
528
+ return value === void 0 ? void 0 : redactUnknown(value, state, /* @__PURE__ */ new WeakSet());
529
+ },
530
+ redactError(error) {
531
+ if (error === void 0) {
532
+ return void 0;
533
+ }
534
+ return {
535
+ ...error.code === void 0 ? {} : { code: redactString(error.code, state) },
536
+ message: redactString(error.message, state),
537
+ ...error.retriable === void 0 ? {} : { retriable: error.retriable },
538
+ ...error.details === void 0 ? {} : { details: toCanonicalJsonValue(redactUnknown(error.details, state, /* @__PURE__ */ new WeakSet())) }
539
+ };
540
+ },
541
+ redactLabels(labels) {
542
+ if (labels === void 0) {
543
+ return void 0;
544
+ }
545
+ const next = Object.entries(labels).reduce(
546
+ (accumulator, [key, value]) => {
547
+ accumulator[key] = isSensitiveKey(key, state) ? REDACTED : redactString(value, state);
548
+ return accumulator;
549
+ },
550
+ {}
551
+ );
552
+ return Object.keys(next).length === 0 ? void 0 : next;
553
+ },
554
+ redactTraceContext(traceContext) {
555
+ if (traceContext === void 0) {
556
+ return void 0;
557
+ }
558
+ const next = {
559
+ ...traceContext.traceparent === void 0 ? {} : { traceparent: redactString(traceContext.traceparent, state) },
560
+ ...traceContext.baggage === void 0 ? {} : { baggage: redactString(traceContext.baggage, state) }
561
+ };
562
+ return Object.keys(next).length === 0 ? void 0 : next;
563
+ }
564
+ };
565
+ }
566
+ function createRedactionState(config) {
567
+ return {
568
+ sensitiveKeys: new Set(
569
+ (config?.redaction?.sensitiveKeys ?? []).map((value) => value.trim().toLowerCase()).filter((value) => value.length > 0)
570
+ ),
571
+ sensitiveValues: [
572
+ ...new Set(
573
+ (config?.redaction?.sensitiveValues ?? []).map((value) => value.trim()).filter((value) => value.length > 0)
574
+ )
575
+ ]
576
+ };
577
+ }
578
+ function redactUnknown(value, state, seen) {
579
+ if (value === null || value === void 0) {
580
+ return value;
581
+ }
582
+ if (typeof value === "string") {
583
+ return redactString(value, state);
584
+ }
585
+ if (typeof value !== "object") {
586
+ return value;
587
+ }
588
+ if (seen.has(value)) {
589
+ return REDACTED;
590
+ }
591
+ seen.add(value);
592
+ if (Array.isArray(value)) {
593
+ return value.map((entry) => redactUnknown(entry, state, seen));
594
+ }
595
+ const next = {};
596
+ for (const [key, nestedValue] of Object.entries(value)) {
597
+ next[key] = isSensitiveKey(key, state) ? REDACTED : redactUnknown(nestedValue, state, seen);
598
+ }
599
+ return next;
600
+ }
601
+ function redactString(value, state) {
602
+ let next = value;
603
+ for (const secret of state.sensitiveValues) {
604
+ next = next.split(secret).join(REDACTED);
605
+ }
606
+ for (const pattern of SENSITIVE_VALUE_PATTERNS) {
607
+ next = next.replace(pattern, REDACTED);
608
+ }
609
+ return redactUrlString(next, state);
610
+ }
611
+ function redactUrlString(value, state) {
612
+ let parsed;
613
+ try {
614
+ parsed = new URL(value);
615
+ } catch {
616
+ return value;
617
+ }
618
+ let changed = false;
619
+ if (parsed.username) {
620
+ parsed.username = REDACTED;
621
+ changed = true;
622
+ }
623
+ if (parsed.password) {
624
+ parsed.password = REDACTED;
625
+ changed = true;
626
+ }
627
+ for (const [key] of parsed.searchParams) {
628
+ if (!isSensitiveKey(key, state)) {
629
+ continue;
630
+ }
631
+ parsed.searchParams.set(key, REDACTED);
632
+ changed = true;
633
+ }
634
+ return changed ? parsed.toString() : value;
635
+ }
636
+ function isSensitiveKey(key, state) {
637
+ return state.sensitiveKeys.has(key.trim().toLowerCase()) || SENSITIVE_KEY_PATTERN.test(key);
638
+ }
639
+
640
+ // ../runtime-core/src/observations.ts
641
+ function normalizeObservabilityConfig(input) {
642
+ const profile = input?.profile ?? "diagnostic";
643
+ const labels = input?.labels === void 0 ? void 0 : Object.entries(input.labels).reduce((accumulator, [key, value]) => {
644
+ const normalizedKey = key.trim();
645
+ const normalizedValue = value.trim();
646
+ if (normalizedKey.length === 0 || normalizedValue.length === 0) {
647
+ return accumulator;
648
+ }
649
+ if (Object.keys(accumulator).length >= 20) {
650
+ return accumulator;
651
+ }
652
+ accumulator[normalizedKey] = normalizedValue;
653
+ return accumulator;
654
+ }, {});
655
+ const redaction = input?.redaction === void 0 ? void 0 : {
656
+ ...input.redaction.sensitiveKeys === void 0 ? {} : {
657
+ sensitiveKeys: input.redaction.sensitiveKeys.map((value) => value.trim()).filter((value) => value.length > 0)
658
+ },
659
+ ...input.redaction.sensitiveValues === void 0 ? {} : {
660
+ sensitiveValues: input.redaction.sensitiveValues.map((value) => value.trim()).filter((value) => value.length > 0)
661
+ }
662
+ };
663
+ return {
664
+ profile,
665
+ ...labels === void 0 || Object.keys(labels).length === 0 ? {} : { labels },
666
+ ...input?.traceContext === void 0 ? {} : {
667
+ traceContext: {
668
+ ...input.traceContext.traceparent === void 0 ? {} : { traceparent: input.traceContext.traceparent.trim() },
669
+ ...input.traceContext.baggage === void 0 ? {} : { baggage: input.traceContext.baggage.trim() }
670
+ }
671
+ },
672
+ ...redaction === void 0 ? {} : { redaction }
673
+ };
674
+ }
675
+ function eventFileName(sequence) {
676
+ return `${String(sequence).padStart(12, "0")}.json`;
677
+ }
678
+ var FilesystemSessionSink = class {
679
+ constructor(store, sessionId) {
680
+ this.store = store;
681
+ this.sessionId = sessionId;
682
+ }
683
+ append(input) {
684
+ return this.store.appendEvent(this.sessionId, input);
685
+ }
686
+ appendBatch(input) {
687
+ return this.store.appendEvents(this.sessionId, input);
688
+ }
689
+ writeArtifact(input) {
690
+ return this.store.writeArtifact(this.sessionId, input);
691
+ }
692
+ async flush() {
693
+ }
694
+ close(reason) {
695
+ return this.store.closeSession(this.sessionId, reason);
696
+ }
697
+ };
698
+ var FilesystemObservationStoreImpl = class {
699
+ constructor(rootPath, artifacts) {
700
+ this.rootPath = rootPath;
701
+ this.artifacts = artifacts;
702
+ this.sessionsDirectory = path7.join(this.rootPath, "observations", "sessions");
703
+ }
704
+ sessionsDirectory;
705
+ redactors = /* @__PURE__ */ new Map();
706
+ async initialize() {
707
+ await ensureDirectory(this.sessionsDirectory);
708
+ }
709
+ async openSession(input) {
710
+ const sessionId = normalizeNonEmptyString("sessionId", input.sessionId);
711
+ const openedAt = normalizeTimestamp("openedAt", input.openedAt ?? Date.now());
712
+ const config = normalizeObservabilityConfig(input.config);
713
+ const redactor = createObservationRedactor(config);
714
+ this.redactors.set(sessionId, redactor);
715
+ const redactedLabels = redactor.redactLabels(config.labels);
716
+ const redactedTraceContext = redactor.redactTraceContext(config.traceContext);
717
+ await withFilesystemLock(this.sessionLockPath(sessionId), async () => {
718
+ const existing = await this.reconcileSessionManifest(sessionId);
719
+ if (existing === void 0) {
720
+ await ensureDirectory(this.sessionEventsDirectory(sessionId));
721
+ await ensureDirectory(this.sessionArtifactsDirectory(sessionId));
722
+ const session = {
723
+ sessionId,
724
+ profile: config.profile,
725
+ ...redactedLabels === void 0 ? {} : { labels: redactedLabels },
726
+ ...redactedTraceContext === void 0 ? {} : { traceContext: redactedTraceContext },
727
+ openedAt,
728
+ updatedAt: openedAt,
729
+ currentSequence: 0,
730
+ eventCount: 0,
731
+ artifactCount: 0
732
+ };
733
+ await writeJsonFileExclusive(this.sessionManifestPath(sessionId), session);
734
+ return;
735
+ }
736
+ const patched = {
737
+ ...existing,
738
+ profile: config.profile,
739
+ ...redactedLabels === void 0 ? {} : { labels: redactedLabels },
740
+ ...redactedTraceContext === void 0 ? {} : { traceContext: redactedTraceContext },
741
+ updatedAt: Math.max(existing.updatedAt, openedAt)
742
+ };
743
+ await writeJsonFileAtomic(this.sessionManifestPath(sessionId), patched);
744
+ });
745
+ return new FilesystemSessionSink(this, sessionId);
746
+ }
747
+ async getSession(sessionId) {
748
+ const manifestPath = this.sessionManifestPath(sessionId);
749
+ if (!await pathExists(manifestPath)) {
750
+ return void 0;
751
+ }
752
+ return readJsonFile(manifestPath);
753
+ }
754
+ async appendEvent(sessionId, input) {
755
+ const [event] = await this.appendEvents(sessionId, [input]);
756
+ if (event === void 0) {
757
+ throw new Error(`failed to append observation event for session ${sessionId}`);
758
+ }
759
+ return event;
760
+ }
761
+ async appendEvents(sessionId, input) {
762
+ if (input.length === 0) {
763
+ return [];
764
+ }
765
+ return withFilesystemLock(this.sessionLockPath(sessionId), async () => {
766
+ const session = await this.reconcileSessionManifest(sessionId);
767
+ if (session === void 0) {
768
+ throw new Error(`observation session ${sessionId} was not found`);
769
+ }
770
+ const redactor = this.redactors.get(sessionId) ?? createObservationRedactor(void 0);
771
+ const events = [];
772
+ let sequence = session.currentSequence;
773
+ let updatedAt = session.updatedAt;
774
+ for (const raw of input) {
775
+ sequence += 1;
776
+ const createdAt = normalizeTimestamp("createdAt", raw.createdAt);
777
+ const context = normalizeObservationContext(raw.context);
778
+ const redactedData = raw.data === void 0 ? void 0 : redactor.redactJson(toCanonicalJsonValue(raw.data));
779
+ const redactedError = redactor.redactError(raw.error);
780
+ const event = {
781
+ eventId: normalizeNonEmptyString(
782
+ "eventId",
783
+ raw.eventId ?? `observation:${sessionId}:${String(sequence).padStart(12, "0")}`
784
+ ),
785
+ sessionId,
786
+ sequence,
787
+ kind: raw.kind,
788
+ phase: raw.phase,
789
+ createdAt,
790
+ correlationId: normalizeNonEmptyString("correlationId", raw.correlationId),
791
+ ...raw.spanId === void 0 ? {} : { spanId: normalizeNonEmptyString("spanId", raw.spanId) },
792
+ ...raw.parentSpanId === void 0 ? {} : { parentSpanId: normalizeNonEmptyString("parentSpanId", raw.parentSpanId) },
793
+ ...context === void 0 ? {} : { context },
794
+ ...redactedData === void 0 ? {} : { data: redactedData },
795
+ ...redactedError === void 0 ? {} : { error: redactedError },
796
+ ...raw.artifactIds === void 0 || raw.artifactIds.length === 0 ? {} : { artifactIds: [...raw.artifactIds] }
797
+ };
798
+ await writeJsonFileExclusive(
799
+ path7.join(this.sessionEventsDirectory(sessionId), eventFileName(sequence)),
800
+ event
801
+ );
802
+ updatedAt = Math.max(updatedAt, createdAt);
803
+ events.push(event);
804
+ }
805
+ await writeJsonFileAtomic(this.sessionManifestPath(sessionId), {
806
+ ...session,
807
+ currentSequence: sequence,
808
+ eventCount: session.eventCount + events.length,
809
+ updatedAt
810
+ });
811
+ return events;
812
+ });
813
+ }
814
+ async writeArtifact(sessionId, input) {
815
+ return withFilesystemLock(this.sessionLockPath(sessionId), async () => {
816
+ const session = await this.reconcileSessionManifest(sessionId);
817
+ if (session === void 0) {
818
+ throw new Error(`observation session ${sessionId} was not found`);
819
+ }
820
+ const redactor = this.redactors.get(sessionId) ?? createObservationRedactor(void 0);
821
+ const createdAt = normalizeTimestamp("createdAt", input.createdAt);
822
+ const context = normalizeObservationContext(input.context);
823
+ const redactedStorageKey = input.storageKey === void 0 ? void 0 : redactor.redactText(input.storageKey);
824
+ const redactedMetadata = input.metadata === void 0 ? void 0 : redactor.redactJson(toCanonicalJsonValue(input.metadata));
825
+ const artifact = {
826
+ artifactId: normalizeNonEmptyString("artifactId", input.artifactId),
827
+ sessionId,
828
+ kind: input.kind,
829
+ createdAt,
830
+ ...context === void 0 ? {} : { context },
831
+ ...input.mediaType === void 0 ? {} : { mediaType: input.mediaType },
832
+ ...input.byteLength === void 0 ? {} : { byteLength: input.byteLength },
833
+ ...input.sha256 === void 0 ? {} : { sha256: input.sha256 },
834
+ ...input.opensteerArtifactId === void 0 ? {} : { opensteerArtifactId: input.opensteerArtifactId },
835
+ ...redactedStorageKey === void 0 ? {} : { storageKey: redactedStorageKey },
836
+ ...redactedMetadata === void 0 ? {} : { metadata: redactedMetadata }
837
+ };
838
+ const artifactPath = this.sessionArtifactPath(sessionId, artifact.artifactId);
839
+ if (!await pathExists(artifactPath)) {
840
+ await writeJsonFileExclusive(artifactPath, artifact);
841
+ await writeJsonFileAtomic(this.sessionManifestPath(sessionId), {
842
+ ...session,
843
+ artifactCount: session.artifactCount + 1,
844
+ updatedAt: Math.max(session.updatedAt, createdAt)
845
+ });
846
+ }
847
+ return artifact;
848
+ });
849
+ }
850
+ async listEvents(sessionId, input = {}) {
851
+ const directoryPath = this.sessionEventsDirectory(sessionId);
852
+ if (!await pathExists(directoryPath)) {
853
+ return [];
854
+ }
855
+ const files = await listJsonFiles(directoryPath);
856
+ const events = await Promise.all(
857
+ files.map((fileName) => readJsonFile(path7.join(directoryPath, fileName)))
858
+ );
859
+ const filtered = events.filter((event) => {
860
+ if (input.kind !== void 0 && event.kind !== input.kind) return false;
861
+ if (input.phase !== void 0 && event.phase !== input.phase) return false;
862
+ if (input.correlationId !== void 0 && event.correlationId !== input.correlationId) {
863
+ return false;
864
+ }
865
+ if (input.pageRef !== void 0 && event.context?.pageRef !== input.pageRef) return false;
866
+ if (input.afterSequence !== void 0 && event.sequence <= input.afterSequence) return false;
867
+ if (input.from !== void 0 && event.createdAt < input.from) return false;
868
+ if (input.to !== void 0 && event.createdAt > input.to) return false;
869
+ return true;
870
+ });
871
+ if (input.limit === void 0 || filtered.length <= input.limit) {
872
+ return filtered;
873
+ }
874
+ return filtered.slice(-input.limit);
875
+ }
876
+ async listArtifacts(sessionId, input = {}) {
877
+ const directoryPath = this.sessionArtifactsDirectory(sessionId);
878
+ if (!await pathExists(directoryPath)) {
879
+ return [];
880
+ }
881
+ const files = await listJsonFiles(directoryPath);
882
+ const artifacts = await Promise.all(
883
+ files.map(
884
+ (fileName) => readJsonFile(path7.join(directoryPath, fileName))
885
+ )
886
+ );
887
+ const filtered = artifacts.filter((artifact) => {
888
+ if (input.kind !== void 0 && artifact.kind !== input.kind) return false;
889
+ if (input.pageRef !== void 0 && artifact.context?.pageRef !== input.pageRef) return false;
890
+ return true;
891
+ });
892
+ if (input.limit === void 0 || filtered.length <= input.limit) {
893
+ return filtered;
894
+ }
895
+ return filtered.slice(-input.limit);
896
+ }
897
+ async getArtifact(sessionId, artifactId) {
898
+ const artifactPath = this.sessionArtifactPath(sessionId, artifactId);
899
+ if (!await pathExists(artifactPath)) {
900
+ return void 0;
901
+ }
902
+ return readJsonFile(artifactPath);
903
+ }
904
+ async closeSession(sessionId, _reason) {
905
+ await withFilesystemLock(this.sessionLockPath(sessionId), async () => {
906
+ const session = await this.reconcileSessionManifest(sessionId);
907
+ if (session === void 0 || session.closedAt !== void 0) {
908
+ return;
909
+ }
910
+ const now = Date.now();
911
+ await writeJsonFileAtomic(this.sessionManifestPath(sessionId), {
912
+ ...session,
913
+ updatedAt: Math.max(session.updatedAt, now),
914
+ closedAt: now
915
+ });
916
+ });
917
+ this.redactors.delete(sessionId);
918
+ }
919
+ async writeArtifactFromManifest(sessionId, manifest, kind, metadata) {
920
+ return this.writeArtifact(sessionId, {
921
+ artifactId: manifest.artifactId,
922
+ kind,
923
+ createdAt: manifest.createdAt,
924
+ context: manifest.scope,
925
+ mediaType: manifest.mediaType,
926
+ byteLength: manifest.byteLength,
927
+ sha256: manifest.sha256,
928
+ opensteerArtifactId: manifest.artifactId,
929
+ storageKey: manifestToExternalBinaryLocation(this.rootPath, manifest).uri,
930
+ ...metadata === void 0 ? {} : { metadata }
931
+ });
932
+ }
933
+ async ensureArtifactLinked(sessionId, manifest) {
934
+ const existing = await this.getArtifact(sessionId, manifest.artifactId);
935
+ if (existing !== void 0) {
936
+ return existing;
937
+ }
938
+ const kind = toObservationArtifactKind(manifest.kind);
939
+ return this.writeArtifactFromManifest(sessionId, manifest, kind);
940
+ }
941
+ async hydrateArtifactManifests(artifactIds) {
942
+ return (await Promise.all(
943
+ artifactIds.map(async (artifactId) => this.artifacts.getManifest(artifactId))
944
+ )).filter((value) => value !== void 0);
945
+ }
946
+ sessionDirectory(sessionId) {
947
+ return path7.join(this.sessionsDirectory, encodePathSegment(sessionId));
948
+ }
949
+ sessionManifestPath(sessionId) {
950
+ return path7.join(this.sessionDirectory(sessionId), "session.json");
951
+ }
952
+ sessionEventsDirectory(sessionId) {
953
+ return path7.join(this.sessionDirectory(sessionId), "events");
954
+ }
955
+ sessionArtifactsDirectory(sessionId) {
956
+ return path7.join(this.sessionDirectory(sessionId), "artifacts");
957
+ }
958
+ sessionArtifactPath(sessionId, artifactId) {
959
+ return path7.join(
960
+ this.sessionArtifactsDirectory(sessionId),
961
+ `${encodePathSegment(artifactId)}.json`
962
+ );
963
+ }
964
+ sessionLockPath(sessionId) {
965
+ return path7.join(this.sessionDirectory(sessionId), ".lock");
966
+ }
967
+ async reconcileSessionManifest(sessionId) {
968
+ const session = await this.getSession(sessionId);
969
+ if (session === void 0) {
970
+ return void 0;
971
+ }
972
+ const [hasEventDirectory, hasArtifactDirectory] = await Promise.all([
973
+ pathExists(this.sessionEventsDirectory(sessionId)),
974
+ pathExists(this.sessionArtifactsDirectory(sessionId))
975
+ ]);
976
+ const [eventFiles, artifactFiles] = await Promise.all([
977
+ hasEventDirectory ? listJsonFiles(this.sessionEventsDirectory(sessionId)) : Promise.resolve([]),
978
+ hasArtifactDirectory ? listJsonFiles(this.sessionArtifactsDirectory(sessionId)) : Promise.resolve([])
979
+ ]);
980
+ const currentSequence = eventFiles.reduce((maxSequence, fileName) => {
981
+ const parsed = Number.parseInt(fileName.replace(/\.json$/u, ""), 10);
982
+ return Number.isFinite(parsed) ? Math.max(maxSequence, parsed) : maxSequence;
983
+ }, 0);
984
+ const eventCount = eventFiles.length;
985
+ const artifactCount = artifactFiles.length;
986
+ if (session.currentSequence === currentSequence && session.eventCount === eventCount && session.artifactCount === artifactCount) {
987
+ return session;
988
+ }
989
+ const [events, artifacts] = await Promise.all([
990
+ Promise.all(
991
+ eventFiles.map(
992
+ (fileName) => readJsonFile(
993
+ path7.join(this.sessionEventsDirectory(sessionId), fileName)
994
+ )
995
+ )
996
+ ),
997
+ Promise.all(
998
+ artifactFiles.map(
999
+ (fileName) => readJsonFile(
1000
+ path7.join(this.sessionArtifactsDirectory(sessionId), fileName)
1001
+ )
1002
+ )
1003
+ )
1004
+ ]);
1005
+ const updatedAt = Math.max(
1006
+ session.openedAt,
1007
+ session.closedAt ?? 0,
1008
+ ...events.map((event) => event.createdAt),
1009
+ ...artifacts.map((artifact) => artifact.createdAt)
1010
+ );
1011
+ const reconciled = {
1012
+ ...session,
1013
+ currentSequence,
1014
+ eventCount,
1015
+ artifactCount,
1016
+ updatedAt
1017
+ };
1018
+ await writeJsonFileAtomic(this.sessionManifestPath(sessionId), reconciled);
1019
+ return reconciled;
1020
+ }
1021
+ };
1022
+ function toObservationArtifactKind(kind) {
1023
+ switch (kind) {
1024
+ case "screenshot":
1025
+ return "screenshot";
1026
+ case "dom-snapshot":
1027
+ return "dom-snapshot";
1028
+ case "html-snapshot":
1029
+ return "html-snapshot";
1030
+ default:
1031
+ return "other";
1032
+ }
1033
+ }
1034
+ function createObservationStore(rootPath, artifacts) {
1035
+ return new FilesystemObservationStoreImpl(rootPath, artifacts);
1036
+ }
1037
+
490
1038
  // ../protocol/src/json.ts
491
1039
  var JSON_SCHEMA_DRAFT_2020_12 = "https://json-schema.org/draft/2020-12/schema";
492
1040
  function defineSchema(schema) {
@@ -554,31 +1102,31 @@ function oneOfSchema(members, options = {}) {
554
1102
  }
555
1103
 
556
1104
  // ../protocol/src/validation.ts
557
- function validateJsonSchema(schema, value, path13 = "$") {
558
- return validateSchemaNode(schema, value, path13);
1105
+ function validateJsonSchema(schema, value, path15 = "$") {
1106
+ return validateSchemaNode(schema, value, path15);
559
1107
  }
560
- function validateSchemaNode(schema, value, path13) {
1108
+ function validateSchemaNode(schema, value, path15) {
561
1109
  const issues = [];
562
1110
  if ("const" in schema && !isJsonValueEqual(schema.const, value)) {
563
1111
  issues.push({
564
- path: path13,
1112
+ path: path15,
565
1113
  message: `must equal ${JSON.stringify(schema.const)}`
566
1114
  });
567
1115
  return issues;
568
1116
  }
569
1117
  if (schema.enum !== void 0 && !schema.enum.some((candidate) => isJsonValueEqual(candidate, value))) {
570
1118
  issues.push({
571
- path: path13,
1119
+ path: path15,
572
1120
  message: `must be one of ${schema.enum.map((candidate) => JSON.stringify(candidate)).join(", ")}`
573
1121
  });
574
1122
  return issues;
575
1123
  }
576
1124
  if (schema.oneOf !== void 0) {
577
- const branchIssues = schema.oneOf.map((member) => validateSchemaNode(member, value, path13));
1125
+ const branchIssues = schema.oneOf.map((member) => validateSchemaNode(member, value, path15));
578
1126
  const validBranches = branchIssues.filter((current) => current.length === 0).length;
579
1127
  if (validBranches !== 1) {
580
1128
  issues.push({
581
- path: path13,
1129
+ path: path15,
582
1130
  message: validBranches === 0 ? "must match exactly one supported shape" : "matches multiple supported shapes"
583
1131
  });
584
1132
  return issues;
@@ -586,11 +1134,11 @@ function validateSchemaNode(schema, value, path13) {
586
1134
  }
587
1135
  if (schema.anyOf !== void 0) {
588
1136
  const hasMatch = schema.anyOf.some(
589
- (member) => validateSchemaNode(member, value, path13).length === 0
1137
+ (member) => validateSchemaNode(member, value, path15).length === 0
590
1138
  );
591
1139
  if (!hasMatch) {
592
1140
  issues.push({
593
- path: path13,
1141
+ path: path15,
594
1142
  message: "must match at least one supported shape"
595
1143
  });
596
1144
  return issues;
@@ -598,7 +1146,7 @@ function validateSchemaNode(schema, value, path13) {
598
1146
  }
599
1147
  if (schema.allOf !== void 0) {
600
1148
  for (const member of schema.allOf) {
601
- issues.push(...validateSchemaNode(member, value, path13));
1149
+ issues.push(...validateSchemaNode(member, value, path15));
602
1150
  }
603
1151
  if (issues.length > 0) {
604
1152
  return issues;
@@ -606,7 +1154,7 @@ function validateSchemaNode(schema, value, path13) {
606
1154
  }
607
1155
  if (schema.type !== void 0 && !matchesSchemaType(schema.type, value)) {
608
1156
  issues.push({
609
- path: path13,
1157
+ path: path15,
610
1158
  message: `must be ${describeSchemaType(schema.type)}`
611
1159
  });
612
1160
  return issues;
@@ -614,19 +1162,19 @@ function validateSchemaNode(schema, value, path13) {
614
1162
  if (typeof value === "string") {
615
1163
  if (schema.minLength !== void 0 && value.length < schema.minLength) {
616
1164
  issues.push({
617
- path: path13,
1165
+ path: path15,
618
1166
  message: `must have length >= ${String(schema.minLength)}`
619
1167
  });
620
1168
  }
621
1169
  if (schema.maxLength !== void 0 && value.length > schema.maxLength) {
622
1170
  issues.push({
623
- path: path13,
1171
+ path: path15,
624
1172
  message: `must have length <= ${String(schema.maxLength)}`
625
1173
  });
626
1174
  }
627
1175
  if (schema.pattern !== void 0 && !new RegExp(schema.pattern).test(value)) {
628
1176
  issues.push({
629
- path: path13,
1177
+ path: path15,
630
1178
  message: `must match pattern ${schema.pattern}`
631
1179
  });
632
1180
  }
@@ -635,25 +1183,25 @@ function validateSchemaNode(schema, value, path13) {
635
1183
  if (typeof value === "number") {
636
1184
  if (schema.minimum !== void 0 && value < schema.minimum) {
637
1185
  issues.push({
638
- path: path13,
1186
+ path: path15,
639
1187
  message: `must be >= ${String(schema.minimum)}`
640
1188
  });
641
1189
  }
642
1190
  if (schema.maximum !== void 0 && value > schema.maximum) {
643
1191
  issues.push({
644
- path: path13,
1192
+ path: path15,
645
1193
  message: `must be <= ${String(schema.maximum)}`
646
1194
  });
647
1195
  }
648
1196
  if (schema.exclusiveMinimum !== void 0 && value <= schema.exclusiveMinimum) {
649
1197
  issues.push({
650
- path: path13,
1198
+ path: path15,
651
1199
  message: `must be > ${String(schema.exclusiveMinimum)}`
652
1200
  });
653
1201
  }
654
1202
  if (schema.exclusiveMaximum !== void 0 && value >= schema.exclusiveMaximum) {
655
1203
  issues.push({
656
- path: path13,
1204
+ path: path15,
657
1205
  message: `must be < ${String(schema.exclusiveMaximum)}`
658
1206
  });
659
1207
  }
@@ -662,13 +1210,13 @@ function validateSchemaNode(schema, value, path13) {
662
1210
  if (Array.isArray(value)) {
663
1211
  if (schema.minItems !== void 0 && value.length < schema.minItems) {
664
1212
  issues.push({
665
- path: path13,
1213
+ path: path15,
666
1214
  message: `must have at least ${String(schema.minItems)} items`
667
1215
  });
668
1216
  }
669
1217
  if (schema.maxItems !== void 0 && value.length > schema.maxItems) {
670
1218
  issues.push({
671
- path: path13,
1219
+ path: path15,
672
1220
  message: `must have at most ${String(schema.maxItems)} items`
673
1221
  });
674
1222
  }
@@ -678,7 +1226,7 @@ function validateSchemaNode(schema, value, path13) {
678
1226
  const key = JSON.stringify(item);
679
1227
  if (seen.has(key)) {
680
1228
  issues.push({
681
- path: path13,
1229
+ path: path15,
682
1230
  message: "must not contain duplicate items"
683
1231
  });
684
1232
  break;
@@ -688,7 +1236,7 @@ function validateSchemaNode(schema, value, path13) {
688
1236
  }
689
1237
  if (schema.items !== void 0) {
690
1238
  for (let index = 0; index < value.length; index += 1) {
691
- issues.push(...validateSchemaNode(schema.items, value[index], `${path13}[${String(index)}]`));
1239
+ issues.push(...validateSchemaNode(schema.items, value[index], `${path15}[${String(index)}]`));
692
1240
  }
693
1241
  }
694
1242
  return issues;
@@ -698,7 +1246,7 @@ function validateSchemaNode(schema, value, path13) {
698
1246
  for (const requiredKey of schema.required ?? []) {
699
1247
  if (!(requiredKey in value)) {
700
1248
  issues.push({
701
- path: joinObjectPath(path13, requiredKey),
1249
+ path: joinObjectPath(path15, requiredKey),
702
1250
  message: "is required"
703
1251
  });
704
1252
  }
@@ -707,13 +1255,13 @@ function validateSchemaNode(schema, value, path13) {
707
1255
  const propertySchema = properties[key];
708
1256
  if (propertySchema !== void 0) {
709
1257
  issues.push(
710
- ...validateSchemaNode(propertySchema, propertyValue, joinObjectPath(path13, key))
1258
+ ...validateSchemaNode(propertySchema, propertyValue, joinObjectPath(path15, key))
711
1259
  );
712
1260
  continue;
713
1261
  }
714
1262
  if (schema.additionalProperties === false) {
715
1263
  issues.push({
716
- path: joinObjectPath(path13, key),
1264
+ path: joinObjectPath(path15, key),
717
1265
  message: "is not allowed"
718
1266
  });
719
1267
  continue;
@@ -723,7 +1271,7 @@ function validateSchemaNode(schema, value, path13) {
723
1271
  ...validateSchemaNode(
724
1272
  schema.additionalProperties,
725
1273
  propertyValue,
726
- joinObjectPath(path13, key)
1274
+ joinObjectPath(path15, key)
727
1275
  )
728
1276
  );
729
1277
  }
@@ -967,8 +1515,8 @@ function matchesNetworkRecordFilters(record, filters) {
967
1515
  }
968
1516
  }
969
1517
  if (filters.path !== void 0) {
970
- const path13 = getParsedUrl().pathname;
971
- if (!includesCaseInsensitive(path13, filters.path)) {
1518
+ const path15 = getParsedUrl().pathname;
1519
+ if (!includesCaseInsensitive(path15, filters.path)) {
972
1520
  return false;
973
1521
  }
974
1522
  }
@@ -1442,7 +1990,7 @@ var networkRecordSchema = objectSchema(
1442
1990
  var networkQueryRecordSchema = objectSchema(
1443
1991
  {
1444
1992
  recordId: stringSchema({ minLength: 1 }),
1445
- actionId: stringSchema({ minLength: 1 }),
1993
+ capture: stringSchema({ minLength: 1 }),
1446
1994
  tags: arraySchema(stringSchema({ minLength: 1 }), {
1447
1995
  uniqueItems: true
1448
1996
  }),
@@ -1922,8 +2470,7 @@ var opensteerRecipeStepSchema = oneOfSchema(
1922
2470
  objectSchema(
1923
2471
  {
1924
2472
  kind: enumSchema(["goto"]),
1925
- url: stringSchema({ minLength: 1 }),
1926
- networkTag: stringSchema({ minLength: 1 })
2473
+ url: stringSchema({ minLength: 1 })
1927
2474
  },
1928
2475
  {
1929
2476
  title: "OpensteerAuthRecipeGotoStep",
@@ -1932,8 +2479,7 @@ var opensteerRecipeStepSchema = oneOfSchema(
1932
2479
  ),
1933
2480
  objectSchema(
1934
2481
  {
1935
- kind: enumSchema(["reload"]),
1936
- networkTag: stringSchema({ minLength: 1 })
2482
+ kind: enumSchema(["reload"])
1937
2483
  },
1938
2484
  {
1939
2485
  title: "OpensteerAuthRecipeReloadStep",
@@ -2150,7 +2696,7 @@ var opensteerNetworkQueryInputSchema = objectSchema(
2150
2696
  pageRef: pageRefSchema,
2151
2697
  recordId: stringSchema({ minLength: 1 }),
2152
2698
  requestId: stringSchema({ minLength: 1 }),
2153
- actionId: stringSchema({ minLength: 1 }),
2699
+ capture: stringSchema({ minLength: 1 }),
2154
2700
  tag: stringSchema({ minLength: 1 }),
2155
2701
  url: stringSchema({ minLength: 1 }),
2156
2702
  hostname: stringSchema({ minLength: 1 }),
@@ -2179,7 +2725,7 @@ var opensteerNetworkTagInputSchema = objectSchema(
2179
2725
  pageRef: pageRefSchema,
2180
2726
  recordId: stringSchema({ minLength: 1 }),
2181
2727
  requestId: stringSchema({ minLength: 1 }),
2182
- actionId: stringSchema({ minLength: 1 }),
2728
+ capture: stringSchema({ minLength: 1 }),
2183
2729
  tag: stringSchema({ minLength: 1 }),
2184
2730
  url: stringSchema({ minLength: 1 }),
2185
2731
  hostname: stringSchema({ minLength: 1 }),
@@ -2204,6 +2750,7 @@ var opensteerNetworkTagOutputSchema = objectSchema(
2204
2750
  );
2205
2751
  var opensteerNetworkClearInputSchema = objectSchema(
2206
2752
  {
2753
+ capture: stringSchema({ minLength: 1 }),
2207
2754
  tag: stringSchema({ minLength: 1 })
2208
2755
  },
2209
2756
  {
@@ -6055,7 +6602,7 @@ var opensteerPageCloseOutputSchema = objectSchema(
6055
6602
  var opensteerPageGotoInputSchema = objectSchema(
6056
6603
  {
6057
6604
  url: stringSchema(),
6058
- networkTag: stringSchema({ minLength: 1 })
6605
+ captureNetwork: stringSchema({ minLength: 1 })
6059
6606
  },
6060
6607
  {
6061
6608
  title: "OpensteerPageGotoInput",
@@ -6206,7 +6753,7 @@ var opensteerDomClickInputSchema = objectSchema(
6206
6753
  {
6207
6754
  target: opensteerTargetInputSchema,
6208
6755
  persistAsDescription: stringSchema(),
6209
- networkTag: stringSchema({ minLength: 1 })
6756
+ captureNetwork: stringSchema({ minLength: 1 })
6210
6757
  },
6211
6758
  {
6212
6759
  title: "OpensteerDomClickInput",
@@ -6217,7 +6764,7 @@ var opensteerDomHoverInputSchema = objectSchema(
6217
6764
  {
6218
6765
  target: opensteerTargetInputSchema,
6219
6766
  persistAsDescription: stringSchema(),
6220
- networkTag: stringSchema({ minLength: 1 })
6767
+ captureNetwork: stringSchema({ minLength: 1 })
6221
6768
  },
6222
6769
  {
6223
6770
  title: "OpensteerDomHoverInput",
@@ -6230,7 +6777,7 @@ var opensteerDomInputInputSchema = objectSchema(
6230
6777
  text: stringSchema(),
6231
6778
  pressEnter: { type: "boolean" },
6232
6779
  persistAsDescription: stringSchema(),
6233
- networkTag: stringSchema({ minLength: 1 })
6780
+ captureNetwork: stringSchema({ minLength: 1 })
6234
6781
  },
6235
6782
  {
6236
6783
  title: "OpensteerDomInputInput",
@@ -6243,7 +6790,7 @@ var opensteerDomScrollInputSchema = objectSchema(
6243
6790
  direction: enumSchema(["up", "down", "left", "right"]),
6244
6791
  amount: integerSchema({ minimum: 1 }),
6245
6792
  persistAsDescription: stringSchema(),
6246
- networkTag: stringSchema({ minLength: 1 })
6793
+ captureNetwork: stringSchema({ minLength: 1 })
6247
6794
  },
6248
6795
  {
6249
6796
  title: "OpensteerDomScrollInput",
@@ -6444,7 +6991,7 @@ var opensteerComputerExecuteInputSchema = objectSchema(
6444
6991
  {
6445
6992
  action: opensteerComputerActionSchema,
6446
6993
  screenshot: opensteerComputerScreenshotOptionsSchema,
6447
- networkTag: stringSchema({ minLength: 1 })
6994
+ captureNetwork: stringSchema({ minLength: 1 })
6448
6995
  },
6449
6996
  {
6450
6997
  title: "OpensteerComputerExecuteInput",
@@ -7049,9 +7596,9 @@ function compareByCreatedAtAndId(left, right) {
7049
7596
  var FilesystemRegistryStore = class {
7050
7597
  constructor(rootPath, registryRelativePath) {
7051
7598
  this.registryRelativePath = registryRelativePath;
7052
- const basePath = path6.join(rootPath, ...registryRelativePath);
7053
- this.recordsDirectory = path6.join(basePath, "records");
7054
- this.indexesDirectory = path6.join(basePath, "indexes", "by-key");
7599
+ const basePath = path7.join(rootPath, ...registryRelativePath);
7600
+ this.recordsDirectory = path7.join(basePath, "records");
7601
+ this.indexesDirectory = path7.join(basePath, "indexes", "by-key");
7055
7602
  }
7056
7603
  recordsDirectory;
7057
7604
  indexesDirectory;
@@ -7120,7 +7667,7 @@ var FilesystemRegistryStore = class {
7120
7667
  async readRecordsFromDirectory() {
7121
7668
  const files = await listJsonFiles(this.recordsDirectory);
7122
7669
  const records = await Promise.all(
7123
- files.map((fileName) => readJsonFile(path6.join(this.recordsDirectory, fileName)))
7670
+ files.map((fileName) => readJsonFile(path7.join(this.recordsDirectory, fileName)))
7124
7671
  );
7125
7672
  records.sort(compareByCreatedAtAndId);
7126
7673
  return records;
@@ -7152,17 +7699,17 @@ var FilesystemRegistryStore = class {
7152
7699
  return record;
7153
7700
  }
7154
7701
  recordPath(id) {
7155
- return path6.join(this.recordsDirectory, `${encodePathSegment(id)}.json`);
7702
+ return path7.join(this.recordsDirectory, `${encodePathSegment(id)}.json`);
7156
7703
  }
7157
7704
  indexPath(key, version) {
7158
- return path6.join(
7705
+ return path7.join(
7159
7706
  this.indexesDirectory,
7160
7707
  encodePathSegment(key),
7161
7708
  `${encodePathSegment(version)}.json`
7162
7709
  );
7163
7710
  }
7164
7711
  writeLockPath() {
7165
- return path6.join(path6.dirname(this.recordsDirectory), ".write.lock");
7712
+ return path7.join(path7.dirname(this.recordsDirectory), ".write.lock");
7166
7713
  }
7167
7714
  };
7168
7715
  var FilesystemDescriptorRegistry = class extends FilesystemRegistryStore {
@@ -7532,7 +8079,7 @@ var SqliteSavedNetworkStore = class {
7532
8079
  directoryInitialization;
7533
8080
  databaseInitialization;
7534
8081
  constructor(rootPath) {
7535
- this.databasePath = path6.join(rootPath, "registry", "saved-network.sqlite");
8082
+ this.databasePath = path7.join(rootPath, "registry", "saved-network.sqlite");
7536
8083
  }
7537
8084
  async initialize() {
7538
8085
  await this.ensureDatabaseDirectory();
@@ -7570,7 +8117,7 @@ var SqliteSavedNetworkStore = class {
7570
8117
  page_ref_key: pageRefKey,
7571
8118
  frame_ref: entry.record.frameRef ?? null,
7572
8119
  document_ref: entry.record.documentRef ?? null,
7573
- action_id: entry.actionId ?? null,
8120
+ capture: entry.capture ?? null,
7574
8121
  method: entry.record.method,
7575
8122
  method_lc: entry.record.method.toLowerCase(),
7576
8123
  url: entry.record.url,
@@ -7683,39 +8230,31 @@ var SqliteSavedNetworkStore = class {
7683
8230
  }
7684
8231
  async clear(input = {}) {
7685
8232
  const database = await this.requireDatabase();
7686
- const countAll = database.prepare(`
7687
- SELECT COUNT(*) AS cleared
7688
- FROM saved_network_records
7689
- `);
7690
- const countByTag = database.prepare(`
7691
- SELECT COUNT(DISTINCT record_id) AS cleared
7692
- FROM saved_network_tags
7693
- WHERE tag = @tag
7694
- `);
7695
- const deleteAllTags = database.prepare(`DELETE FROM saved_network_tags`);
8233
+ const countAll = database.prepare(`SELECT COUNT(*) AS cleared FROM saved_network_records`);
7696
8234
  const deleteAllRecords = database.prepare(`DELETE FROM saved_network_records`);
7697
- const deleteTag = database.prepare(`
7698
- DELETE FROM saved_network_tags
7699
- WHERE tag = @tag
8235
+ const { whereSql, parameters } = buildSavedNetworkWhere(input);
8236
+ const countFiltered = database.prepare(`
8237
+ SELECT COUNT(*) AS cleared
8238
+ FROM saved_network_records r
8239
+ ${whereSql}
7700
8240
  `);
7701
- const deleteOrphans = database.prepare(`
8241
+ const deleteFiltered = database.prepare(`
7702
8242
  DELETE FROM saved_network_records
7703
- WHERE NOT EXISTS (
7704
- SELECT 1
7705
- FROM saved_network_tags t
7706
- WHERE t.record_id = saved_network_records.record_id
8243
+ WHERE record_id IN (
8244
+ SELECT r.record_id
8245
+ FROM saved_network_records r
8246
+ ${whereSql}
7707
8247
  )
7708
8248
  `);
7709
8249
  return withSqliteTransaction(database, () => {
7710
- const tag = input.tag;
7711
- const cleared = tag === void 0 ? countAll.get().cleared : countByTag.get({ tag }).cleared;
7712
- if (tag === void 0) {
7713
- deleteAllTags.run();
8250
+ if (input.capture === void 0 && input.tag === void 0) {
8251
+ const cleared2 = countAll.get().cleared;
7714
8252
  deleteAllRecords.run();
7715
- return cleared;
8253
+ return cleared2;
7716
8254
  }
7717
- deleteTag.run({ tag });
7718
- deleteOrphans.run();
8255
+ const args = parameters;
8256
+ const cleared = countFiltered.get(...args).cleared;
8257
+ deleteFiltered.run(...args);
7719
8258
  return cleared;
7720
8259
  });
7721
8260
  }
@@ -7750,7 +8289,7 @@ var SqliteSavedNetworkStore = class {
7750
8289
  }
7751
8290
  }
7752
8291
  async ensureDatabaseDirectory() {
7753
- this.directoryInitialization ??= ensureDirectory(path6.dirname(this.databasePath)).catch(
8292
+ this.directoryInitialization ??= ensureDirectory(path7.dirname(this.databasePath)).catch(
7754
8293
  (error) => {
7755
8294
  this.directoryInitialization = void 0;
7756
8295
  throw error;
@@ -7770,7 +8309,7 @@ var SqliteSavedNetworkStore = class {
7770
8309
  page_ref_key TEXT NOT NULL,
7771
8310
  frame_ref TEXT,
7772
8311
  document_ref TEXT,
7773
- action_id TEXT,
8312
+ capture TEXT,
7774
8313
  method TEXT NOT NULL,
7775
8314
  method_lc TEXT NOT NULL,
7776
8315
  url TEXT NOT NULL,
@@ -7809,6 +8348,9 @@ var SqliteSavedNetworkStore = class {
7809
8348
  CREATE INDEX IF NOT EXISTS saved_network_records_saved_at
7810
8349
  ON saved_network_records (saved_at DESC);
7811
8350
 
8351
+ CREATE INDEX IF NOT EXISTS saved_network_records_capture
8352
+ ON saved_network_records (capture);
8353
+
7812
8354
  CREATE TABLE IF NOT EXISTS saved_network_tags (
7813
8355
  record_id TEXT NOT NULL REFERENCES saved_network_records(record_id) ON DELETE CASCADE,
7814
8356
  tag TEXT NOT NULL,
@@ -7824,6 +8366,7 @@ var SqliteSavedNetworkStore = class {
7824
8366
  "capture_state",
7825
8367
  "TEXT NOT NULL DEFAULT 'complete'"
7826
8368
  );
8369
+ this.ensureColumn(database, "saved_network_records", "capture", "TEXT");
7827
8370
  this.ensureColumn(
7828
8371
  database,
7829
8372
  "saved_network_records",
@@ -7864,9 +8407,9 @@ function buildSavedNetworkWhere(input) {
7864
8407
  clauses.push("r.request_id = ?");
7865
8408
  parameters.push(input.requestId);
7866
8409
  }
7867
- if (input.actionId !== void 0) {
7868
- clauses.push("r.action_id = ?");
7869
- parameters.push(input.actionId);
8410
+ if (input.capture !== void 0) {
8411
+ clauses.push("r.capture = ?");
8412
+ parameters.push(input.capture);
7870
8413
  }
7871
8414
  if (input.tag !== void 0) {
7872
8415
  clauses.push(`
@@ -7928,7 +8471,7 @@ function buildSavedNetworkUpsertSql(bodyWriteMode) {
7928
8471
  page_ref_key,
7929
8472
  frame_ref,
7930
8473
  document_ref,
7931
- action_id,
8474
+ capture,
7932
8475
  method,
7933
8476
  method_lc,
7934
8477
  url,
@@ -7967,7 +8510,7 @@ function buildSavedNetworkUpsertSql(bodyWriteMode) {
7967
8510
  @page_ref_key,
7968
8511
  @frame_ref,
7969
8512
  @document_ref,
7970
- @action_id,
8513
+ @capture,
7971
8514
  @method,
7972
8515
  @method_lc,
7973
8516
  @url,
@@ -8004,7 +8547,7 @@ function buildSavedNetworkUpsertSql(bodyWriteMode) {
8004
8547
  page_ref_key = excluded.page_ref_key,
8005
8548
  frame_ref = excluded.frame_ref,
8006
8549
  document_ref = excluded.document_ref,
8007
- action_id = excluded.action_id,
8550
+ capture = excluded.capture,
8008
8551
  method = excluded.method,
8009
8552
  method_lc = excluded.method_lc,
8010
8553
  url = excluded.url,
@@ -8099,7 +8642,7 @@ function inflateSavedNetworkRow(row, includeBodies) {
8099
8642
  }
8100
8643
  return {
8101
8644
  recordId: row.record_id,
8102
- ...row.action_id === null ? {} : { actionId: row.action_id },
8645
+ ...row.capture === null ? {} : { capture: row.capture },
8103
8646
  ...row.tags === null || row.tags.length === 0 ? {} : { tags: row.tags.split(TAG_DELIMITER).filter((tag) => tag.length > 0) },
8104
8647
  savedAt: row.saved_at,
8105
8648
  record
@@ -8152,7 +8695,7 @@ var FilesystemTraceStore = class {
8152
8695
  constructor(rootPath, artifacts) {
8153
8696
  this.rootPath = rootPath;
8154
8697
  this.artifacts = artifacts;
8155
- this.runsDirectory = path6.join(this.rootPath, "traces", "runs");
8698
+ this.runsDirectory = path7.join(this.rootPath, "traces", "runs");
8156
8699
  }
8157
8700
  runsDirectory;
8158
8701
  async initialize() {
@@ -8229,7 +8772,7 @@ var FilesystemTraceStore = class {
8229
8772
  ...input.error === void 0 ? {} : { error: input.error }
8230
8773
  };
8231
8774
  await writeJsonFileExclusive(
8232
- path6.join(this.runEntriesDirectory(runId), sequenceFileName(sequence)),
8775
+ path7.join(this.runEntriesDirectory(runId), sequenceFileName(sequence)),
8233
8776
  entry
8234
8777
  );
8235
8778
  await writeJsonFileAtomic(this.runManifestPath(runId), {
@@ -8248,7 +8791,7 @@ var FilesystemTraceStore = class {
8248
8791
  const files = await listJsonFiles(entriesDirectory);
8249
8792
  return Promise.all(
8250
8793
  files.map(
8251
- (fileName) => readJsonFile(path6.join(entriesDirectory, fileName))
8794
+ (fileName) => readJsonFile(path7.join(entriesDirectory, fileName))
8252
8795
  )
8253
8796
  );
8254
8797
  }
@@ -8294,16 +8837,16 @@ var FilesystemTraceStore = class {
8294
8837
  return { trace, artifacts };
8295
8838
  }
8296
8839
  runDirectory(runId) {
8297
- return path6.join(this.runsDirectory, encodeURIComponent(runId));
8840
+ return path7.join(this.runsDirectory, encodeURIComponent(runId));
8298
8841
  }
8299
8842
  runEntriesDirectory(runId) {
8300
- return path6.join(this.runDirectory(runId), "entries");
8843
+ return path7.join(this.runDirectory(runId), "entries");
8301
8844
  }
8302
8845
  runManifestPath(runId) {
8303
- return path6.join(this.runDirectory(runId), "manifest.json");
8846
+ return path7.join(this.runDirectory(runId), "manifest.json");
8304
8847
  }
8305
8848
  runWriteLockPath(runId) {
8306
- return path6.join(this.runDirectory(runId), ".append.lock");
8849
+ return path7.join(this.runDirectory(runId), ".append.lock");
8307
8850
  }
8308
8851
  };
8309
8852
  function createTraceStore(rootPath, artifacts) {
@@ -8317,7 +8860,7 @@ function normalizeWorkspaceId(workspace) {
8317
8860
  return encodePathSegment(workspace);
8318
8861
  }
8319
8862
  function resolveFilesystemWorkspacePath(input) {
8320
- return path6.join(
8863
+ return path7.join(
8321
8864
  input.rootDir,
8322
8865
  ".opensteer",
8323
8866
  "workspaces",
@@ -8326,17 +8869,18 @@ function resolveFilesystemWorkspacePath(input) {
8326
8869
  }
8327
8870
  async function createFilesystemOpensteerWorkspace(options) {
8328
8871
  await ensureDirectory(options.rootPath);
8329
- const manifestPath = path6.join(options.rootPath, "workspace.json");
8330
- const browserPath = path6.join(options.rootPath, "browser");
8331
- const browserManifestPath = path6.join(browserPath, "manifest.json");
8332
- const browserUserDataDir = path6.join(browserPath, "user-data");
8333
- const livePath = path6.join(options.rootPath, "live");
8334
- const liveLocalPath = path6.join(livePath, "local.json");
8335
- const liveCloudPath = path6.join(livePath, "cloud.json");
8336
- const artifactsPath = path6.join(options.rootPath, "artifacts");
8337
- const tracesPath = path6.join(options.rootPath, "traces");
8338
- const registryPath = path6.join(options.rootPath, "registry");
8339
- const lockPath = path6.join(options.rootPath, ".lock");
8872
+ const manifestPath = path7.join(options.rootPath, "workspace.json");
8873
+ const browserPath = path7.join(options.rootPath, "browser");
8874
+ const browserManifestPath = path7.join(browserPath, "manifest.json");
8875
+ const browserUserDataDir = path7.join(browserPath, "user-data");
8876
+ const livePath = path7.join(options.rootPath, "live");
8877
+ const liveLocalPath = path7.join(livePath, "local.json");
8878
+ const liveCloudPath = path7.join(livePath, "cloud.json");
8879
+ const artifactsPath = path7.join(options.rootPath, "artifacts");
8880
+ const tracesPath = path7.join(options.rootPath, "traces");
8881
+ const observationsPath = path7.join(options.rootPath, "observations");
8882
+ const registryPath = path7.join(options.rootPath, "registry");
8883
+ const lockPath = path7.join(options.rootPath, ".lock");
8340
8884
  let manifest;
8341
8885
  if (await pathExists(manifestPath)) {
8342
8886
  manifest = await readJsonFile(manifestPath);
@@ -8350,6 +8894,17 @@ async function createFilesystemOpensteerWorkspace(options) {
8350
8894
  `workspace ${options.rootPath} uses unsupported version ${String(manifest.version)}`
8351
8895
  );
8352
8896
  }
8897
+ if (manifest.paths.observations === void 0) {
8898
+ manifest = {
8899
+ ...manifest,
8900
+ updatedAt: Date.now(),
8901
+ paths: {
8902
+ ...manifest.paths,
8903
+ observations: "observations"
8904
+ }
8905
+ };
8906
+ await writeJsonFileAtomic(manifestPath, manifest);
8907
+ }
8353
8908
  } else {
8354
8909
  const createdAt = normalizeTimestamp("createdAt", options.createdAt ?? Date.now());
8355
8910
  manifest = {
@@ -8364,6 +8919,7 @@ async function createFilesystemOpensteerWorkspace(options) {
8364
8919
  live: "live",
8365
8920
  artifacts: "artifacts",
8366
8921
  traces: "traces",
8922
+ observations: "observations",
8367
8923
  registry: "registry"
8368
8924
  }
8369
8925
  };
@@ -8375,6 +8931,7 @@ async function createFilesystemOpensteerWorkspace(options) {
8375
8931
  ensureDirectory(livePath),
8376
8932
  ensureDirectory(artifactsPath),
8377
8933
  ensureDirectory(tracesPath),
8934
+ ensureDirectory(observationsPath),
8378
8935
  ensureDirectory(registryPath)
8379
8936
  ]);
8380
8937
  const artifacts = createArtifactStore(options.rootPath);
@@ -8399,6 +8956,8 @@ async function createFilesystemOpensteerWorkspace(options) {
8399
8956
  await reverseReports.initialize();
8400
8957
  const traces = createTraceStore(options.rootPath, artifacts);
8401
8958
  await traces.initialize();
8959
+ const observations = createObservationStore(options.rootPath, artifacts);
8960
+ await observations.initialize();
8402
8961
  return {
8403
8962
  rootPath: options.rootPath,
8404
8963
  manifestPath,
@@ -8411,10 +8970,12 @@ async function createFilesystemOpensteerWorkspace(options) {
8411
8970
  liveCloudPath,
8412
8971
  artifactsPath,
8413
8972
  tracesPath,
8973
+ observationsPath,
8414
8974
  registryPath,
8415
8975
  lockPath,
8416
8976
  artifacts,
8417
8977
  traces,
8978
+ observations,
8418
8979
  registry: {
8419
8980
  descriptors,
8420
8981
  requestPlans,
@@ -8439,9 +9000,9 @@ var DEFAULT_TIMEOUTS = {
8439
9000
  "page.add-init-script": 1e4,
8440
9001
  "page.snapshot": 15e3,
8441
9002
  "computer.execute": 3e4,
8442
- "dom.click": 1e4,
9003
+ "dom.click": 3e4,
8443
9004
  "dom.hover": 1e4,
8444
- "dom.input": 1e4,
9005
+ "dom.input": 3e4,
8445
9006
  "dom.scroll": 1e4,
8446
9007
  "dom.extract": 15e3,
8447
9008
  "network.query": 15e3,
@@ -8601,10 +9162,10 @@ function delayWithSignal(delayMs, signal) {
8601
9162
  if (signal.aborted) {
8602
9163
  return Promise.reject(signal.reason ?? abortError());
8603
9164
  }
8604
- return new Promise((resolve5, reject) => {
9165
+ return new Promise((resolve4, reject) => {
8605
9166
  const timer = setTimeout(() => {
8606
9167
  signal.removeEventListener("abort", onAbort);
8607
- resolve5();
9168
+ resolve4();
8608
9169
  }, delayMs);
8609
9170
  const onAbort = () => {
8610
9171
  clearTimeout(timer);
@@ -9069,9 +9630,9 @@ var IFRAME_URL_ATTRIBUTES = /* @__PURE__ */ new Set([
9069
9630
  "poster",
9070
9631
  "ping"
9071
9632
  ]);
9072
- function buildArrayFieldPathCandidates(path13) {
9073
- const strict = path13.nodes.length ? buildPathCandidates(path13.nodes) : [];
9074
- const relaxedNodes = stripPositionClauses(path13.nodes);
9633
+ function buildArrayFieldPathCandidates(path15) {
9634
+ const strict = path15.nodes.length ? buildPathCandidates(path15.nodes) : [];
9635
+ const relaxedNodes = stripPositionClauses(path15.nodes);
9075
9636
  const relaxed = relaxedNodes.length ? buildPathCandidates(relaxedNodes) : [];
9076
9637
  return dedupeSelectors([...strict, ...relaxed]);
9077
9638
  }
@@ -9601,18 +10162,18 @@ function cloneStructuralElementAnchor(anchor) {
9601
10162
  nodes: anchor.nodes.map(clonePathNode)
9602
10163
  };
9603
10164
  }
9604
- function cloneReplayElementPath(path13) {
10165
+ function cloneReplayElementPath(path15) {
9605
10166
  return {
9606
10167
  resolution: "deterministic",
9607
- context: cloneContext(path13.context),
9608
- nodes: path13.nodes.map(clonePathNode)
10168
+ context: cloneContext(path15.context),
10169
+ nodes: path15.nodes.map(clonePathNode)
9609
10170
  };
9610
10171
  }
9611
- function cloneElementPath(path13) {
9612
- return cloneReplayElementPath(path13);
10172
+ function cloneElementPath(path15) {
10173
+ return cloneReplayElementPath(path15);
9613
10174
  }
9614
- function buildPathSelectorHint(path13) {
9615
- const nodes = path13?.nodes || [];
10175
+ function buildPathSelectorHint(path15) {
10176
+ const nodes = path15?.nodes || [];
9616
10177
  const last = nodes[nodes.length - 1];
9617
10178
  if (!last) {
9618
10179
  return "*";
@@ -9661,15 +10222,15 @@ function sanitizeStructuralElementAnchor(anchor) {
9661
10222
  nodes: sanitizeNodes(anchor.nodes)
9662
10223
  };
9663
10224
  }
9664
- function sanitizeReplayElementPath(path13) {
10225
+ function sanitizeReplayElementPath(path15) {
9665
10226
  return {
9666
10227
  resolution: "deterministic",
9667
- context: sanitizeContext(path13.context),
9668
- nodes: sanitizeNodes(path13.nodes)
10228
+ context: sanitizeContext(path15.context),
10229
+ nodes: sanitizeNodes(path15.nodes)
9669
10230
  };
9670
10231
  }
9671
- function sanitizeElementPath(path13) {
9672
- return sanitizeReplayElementPath(path13);
10232
+ function sanitizeElementPath(path15) {
10233
+ return sanitizeReplayElementPath(path15);
9673
10234
  }
9674
10235
  function buildLocalStructuralElementAnchor(index, rawTargetNode) {
9675
10236
  const targetNode = requireElementNode(index, rawTargetNode);
@@ -9792,8 +10353,8 @@ function buildTargetNotFoundMessage(domPath, diagnostics) {
9792
10353
  }
9793
10354
  return `${base} Target depth ${String(depth)}. Candidate counts: ${sample}.`;
9794
10355
  }
9795
- function buildArrayFieldCandidates(path13) {
9796
- return buildArrayFieldPathCandidates(path13);
10356
+ function buildArrayFieldCandidates(path15) {
10357
+ return buildArrayFieldPathCandidates(path15);
9797
10358
  }
9798
10359
  function firstDefinedAttribute(node, keys) {
9799
10360
  for (const key of keys) {
@@ -10236,6 +10797,7 @@ var MemoryDomDescriptorStore = class {
10236
10797
  };
10237
10798
 
10238
10799
  // ../runtime-core/src/action-boundary.ts
10800
+ var actionBoundaryDiagnosticsBySignal = /* @__PURE__ */ new WeakMap();
10239
10801
  async function captureActionBoundarySnapshot(engine, pageRef) {
10240
10802
  const frames = await engine.listFrames({ pageRef });
10241
10803
  const mainFrame = frames.find((frame) => frame.isMainFrame);
@@ -10247,6 +10809,35 @@ async function captureActionBoundarySnapshot(engine, pageRef) {
10247
10809
  documentRef: mainFrame.documentRef
10248
10810
  };
10249
10811
  }
10812
+ function createActionBoundaryDiagnostics(input) {
10813
+ return {
10814
+ trigger: input.boundary.trigger,
10815
+ crossDocument: input.boundary.crossDocument,
10816
+ bootstrapSettled: input.boundary.bootstrapSettled,
10817
+ visualSettled: input.visualSettled,
10818
+ ...input.boundary.timedOutPhase !== void 0 ? { timedOutPhase: input.boundary.timedOutPhase } : !input.visualSettled ? { timedOutPhase: "visual" } : {}
10819
+ };
10820
+ }
10821
+ function recordActionBoundaryDiagnostics(signal, diagnostics) {
10822
+ actionBoundaryDiagnosticsBySignal.set(signal, diagnostics);
10823
+ }
10824
+ function takeActionBoundaryDiagnostics(signal) {
10825
+ const diagnostics = actionBoundaryDiagnosticsBySignal.get(signal);
10826
+ actionBoundaryDiagnosticsBySignal.delete(signal);
10827
+ return diagnostics;
10828
+ }
10829
+ function isSoftSettleTimeoutError(error, signal) {
10830
+ if (isTimeoutError(error)) {
10831
+ return true;
10832
+ }
10833
+ return signal?.aborted === true && isTimeoutError(signal.reason) && (error === signal.reason || isAbortError(error));
10834
+ }
10835
+ function isAbortError(error) {
10836
+ return error instanceof Error && error.name === "AbortError";
10837
+ }
10838
+ function isTimeoutError(error) {
10839
+ return isOpensteerProtocolError(error) && error.code === "timeout";
10840
+ }
10250
10841
 
10251
10842
  // ../runtime-core/src/runtimes/dom/executor.ts
10252
10843
  var MAX_DOM_ACTION_ATTEMPTS = 3;
@@ -10270,14 +10861,16 @@ var DomActionExecutor = class {
10270
10861
  ...input.timeout === void 0 ? {} : { timeout: input.timeout }
10271
10862
  },
10272
10863
  async (pointerTarget, point, timeout) => {
10273
- await timeout.runStep(
10864
+ const events = [];
10865
+ const moved = await timeout.runStep(
10274
10866
  () => this.options.engine.mouseMove({
10275
10867
  pageRef: pointerTarget.resolved.pageRef,
10276
10868
  point,
10277
10869
  coordinateSpace: "document-css"
10278
10870
  })
10279
10871
  );
10280
- await timeout.runStep(
10872
+ events.push(...moved.events);
10873
+ const clicked = await timeout.runStep(
10281
10874
  () => this.options.engine.mouseClick({
10282
10875
  pageRef: pointerTarget.resolved.pageRef,
10283
10876
  point,
@@ -10287,7 +10880,12 @@ var DomActionExecutor = class {
10287
10880
  ...input.modifiers === void 0 ? {} : { modifiers: input.modifiers }
10288
10881
  })
10289
10882
  );
10290
- return { resolved: pointerTarget.original, point };
10883
+ events.push(...clicked.events);
10884
+ return {
10885
+ resolved: pointerTarget.original,
10886
+ point,
10887
+ ...events.length === 0 ? {} : { events }
10888
+ };
10291
10889
  }
10292
10890
  );
10293
10891
  }
@@ -10301,14 +10899,18 @@ var DomActionExecutor = class {
10301
10899
  ...input.timeout === void 0 ? {} : { timeout: input.timeout }
10302
10900
  },
10303
10901
  async (pointerTarget, point, timeout) => {
10304
- await timeout.runStep(
10902
+ const moved = await timeout.runStep(
10305
10903
  () => this.options.engine.mouseMove({
10306
10904
  pageRef: pointerTarget.resolved.pageRef,
10307
10905
  point,
10308
10906
  coordinateSpace: "document-css"
10309
10907
  })
10310
10908
  );
10311
- return { resolved: pointerTarget.original, point };
10909
+ return {
10910
+ resolved: pointerTarget.original,
10911
+ point,
10912
+ ...moved.events.length === 0 ? {} : { events: moved.events }
10913
+ };
10312
10914
  }
10313
10915
  );
10314
10916
  }
@@ -10322,14 +10924,16 @@ var DomActionExecutor = class {
10322
10924
  ...input.timeout === void 0 ? {} : { timeout: input.timeout }
10323
10925
  },
10324
10926
  async (pointerTarget, point, timeout) => {
10325
- await timeout.runStep(
10927
+ const events = [];
10928
+ const moved = await timeout.runStep(
10326
10929
  () => this.options.engine.mouseMove({
10327
10930
  pageRef: pointerTarget.resolved.pageRef,
10328
10931
  point,
10329
10932
  coordinateSpace: "document-css"
10330
10933
  })
10331
10934
  );
10332
- await timeout.runStep(
10935
+ events.push(...moved.events);
10936
+ const scrolled = await timeout.runStep(
10333
10937
  () => this.options.engine.mouseScroll({
10334
10938
  pageRef: pointerTarget.resolved.pageRef,
10335
10939
  point,
@@ -10337,7 +10941,12 @@ var DomActionExecutor = class {
10337
10941
  delta: input.delta
10338
10942
  })
10339
10943
  );
10340
- return { resolved: pointerTarget.original, point };
10944
+ events.push(...scrolled.events);
10945
+ return {
10946
+ resolved: pointerTarget.original,
10947
+ point,
10948
+ ...events.length === 0 ? {} : { events }
10949
+ };
10341
10950
  }
10342
10951
  );
10343
10952
  }
@@ -10379,7 +10988,7 @@ var DomActionExecutor = class {
10379
10988
  let finalResolved = resolved;
10380
10989
  let finalSnapshot;
10381
10990
  if (input.pressEnter) {
10382
- await this.settle(resolved.pageRef, "dom.input", timeout);
10991
+ await this.waitForPressEnterReaction(timeout);
10383
10992
  const enterSession = this.options.createResolutionSession();
10384
10993
  const enterResolved = await timeout.runStep(
10385
10994
  () => this.options.resolveTarget(enterSession, {
@@ -10403,7 +11012,15 @@ var DomActionExecutor = class {
10403
11012
  );
10404
11013
  finalResolved = enterResolved;
10405
11014
  }
10406
- await this.settle(finalResolved.pageRef, "dom.input", timeout, finalSnapshot);
11015
+ const settleDiagnostics = await this.settle(
11016
+ finalResolved.pageRef,
11017
+ "dom.input",
11018
+ timeout,
11019
+ finalSnapshot
11020
+ );
11021
+ if (finalSnapshot !== void 0) {
11022
+ recordActionBoundaryDiagnostics(timeout.signal, settleDiagnostics);
11023
+ }
10407
11024
  return finalResolved;
10408
11025
  } catch (error) {
10409
11026
  lastError = error;
@@ -10480,12 +11097,13 @@ var DomActionExecutor = class {
10480
11097
  () => captureActionBoundarySnapshot(this.options.engine, pointerTarget.resolved.pageRef)
10481
11098
  );
10482
11099
  const outcome = await dispatch(pointerTarget, point, timeout);
10483
- await this.settle(
11100
+ const settleDiagnostics = await this.settle(
10484
11101
  pointerTarget.resolved.pageRef,
10485
11102
  input.operation,
10486
11103
  timeout,
10487
11104
  actionBoundarySnapshot
10488
11105
  );
11106
+ recordActionBoundaryDiagnostics(timeout.signal, settleDiagnostics);
10489
11107
  return outcome;
10490
11108
  } catch (error) {
10491
11109
  lastError = error;
@@ -10505,22 +11123,47 @@ var DomActionExecutor = class {
10505
11123
  }
10506
11124
  async settle(pageRef, operation, timeout, snapshot) {
10507
11125
  const bridge = this.requireBridge();
10508
- await timeout.runStep(
11126
+ let visualSettled = true;
11127
+ const boundary = await timeout.runStep(
10509
11128
  () => bridge.finalizeDomAction(pageRef, {
10510
11129
  operation,
10511
11130
  ...snapshot === void 0 ? {} : { snapshot },
10512
11131
  signal: timeout.signal,
10513
11132
  remainingMs: () => timeout.remainingMs(),
10514
- policySettle: (targetPageRef, trigger) => settleWithPolicy(this.options.policy.settle, {
10515
- operation,
10516
- trigger,
10517
- engine: this.options.engine,
10518
- pageRef: targetPageRef,
10519
- signal: timeout.signal,
10520
- remainingMs: timeout.remainingMs()
10521
- })
11133
+ policySettle: async (targetPageRef, trigger) => {
11134
+ try {
11135
+ await settleWithPolicy(this.options.policy.settle, {
11136
+ operation,
11137
+ trigger,
11138
+ engine: this.options.engine,
11139
+ pageRef: targetPageRef,
11140
+ signal: timeout.signal,
11141
+ remainingMs: timeout.remainingMs()
11142
+ });
11143
+ } catch (error) {
11144
+ if (snapshot !== void 0 && isSoftSettleTimeoutError(error, timeout.signal)) {
11145
+ visualSettled = false;
11146
+ return;
11147
+ }
11148
+ throw error;
11149
+ }
11150
+ }
10522
11151
  })
10523
11152
  );
11153
+ return createActionBoundaryDiagnostics({
11154
+ boundary,
11155
+ visualSettled
11156
+ });
11157
+ }
11158
+ async waitForPressEnterReaction(timeout) {
11159
+ const delayMs = this.options.policy.settle.resolveDelayMs({
11160
+ operation: "dom.input",
11161
+ trigger: "dom-action"
11162
+ });
11163
+ if (delayMs <= 0) {
11164
+ return;
11165
+ }
11166
+ await delayWithSignal(delayMs, timeout.signal);
10524
11167
  }
10525
11168
  requireBridge() {
10526
11169
  if (this.bridge !== void 0) {
@@ -11229,21 +11872,21 @@ var DefaultDomRuntime = class {
11229
11872
  return match;
11230
11873
  }
11231
11874
  async resolvePathTarget(session, pageRef, rawPath, source, description, descriptor) {
11232
- const path13 = sanitizeReplayElementPath(rawPath);
11233
- const context = await this.resolvePathContext(session, pageRef, path13.context);
11234
- const target = resolveDomPathInScope(context.index, path13.nodes, context.scope);
11875
+ const path15 = sanitizeReplayElementPath(rawPath);
11876
+ const context = await this.resolvePathContext(session, pageRef, path15.context);
11877
+ const target = resolveDomPathInScope(context.index, path15.nodes, context.scope);
11235
11878
  if (!target) {
11236
- throwTargetNotFound(context.index, path13.nodes, context.scope);
11879
+ throwTargetNotFound(context.index, path15.nodes, context.scope);
11237
11880
  }
11238
11881
  if (target.node.nodeRef === void 0) {
11239
11882
  throw new Error(
11240
- `resolved path "${buildPathSelectorHint(path13)}" does not point to a live element`
11883
+ `resolved path "${buildPathSelectorHint(path15)}" does not point to a live element`
11241
11884
  );
11242
11885
  }
11243
11886
  const anchor = await this.buildAnchorFromSnapshotNode(session, context.snapshot, target.node);
11244
11887
  return this.createResolvedTarget(source, context.snapshot, target.node, anchor, {
11245
11888
  ...description === void 0 ? {} : { description },
11246
- replayPath: path13,
11889
+ replayPath: path15,
11247
11890
  ...source === "path" || source === "descriptor" ? { selectorUsed: target.selector } : {},
11248
11891
  ...descriptor === void 0 ? {} : { descriptor }
11249
11892
  });
@@ -11264,9 +11907,9 @@ var DefaultDomRuntime = class {
11264
11907
  });
11265
11908
  }
11266
11909
  async queryAllByElementPath(session, pageRef, rawPath) {
11267
- const path13 = sanitizeReplayElementPath(rawPath);
11268
- const context = await this.resolvePathContext(session, pageRef, path13.context);
11269
- return queryAllDomPathInScope(context.index, path13.nodes, context.scope).filter(
11910
+ const path15 = sanitizeReplayElementPath(rawPath);
11911
+ const context = await this.resolvePathContext(session, pageRef, path15.context);
11912
+ return queryAllDomPathInScope(context.index, path15.nodes, context.scope).filter(
11270
11913
  (node) => node.nodeRef !== void 0
11271
11914
  ).map((node) => this.createSnapshotTarget(context.snapshot, node));
11272
11915
  }
@@ -11452,16 +12095,16 @@ var DefaultDomRuntime = class {
11452
12095
  const index = createSnapshotIndex(item.snapshot);
11453
12096
  return this.resolveFirstArrayFieldTargetInNode(index, item.node, field.path);
11454
12097
  }
11455
- resolveFirstArrayFieldTargetInNode(index, rootNode, path13) {
11456
- const normalizedPath = sanitizeElementPath(path13);
12098
+ resolveFirstArrayFieldTargetInNode(index, rootNode, path15) {
12099
+ const normalizedPath = sanitizeElementPath(path15);
11457
12100
  const selectors = buildArrayFieldCandidates(normalizedPath);
11458
12101
  if (!selectors.length) {
11459
12102
  return rootNode;
11460
12103
  }
11461
12104
  return resolveFirstWithinNodeBySelectors(index, rootNode, selectors);
11462
12105
  }
11463
- resolveUniqueArrayFieldTargetInNode(index, rootNode, path13) {
11464
- const normalizedPath = sanitizeElementPath(path13);
12106
+ resolveUniqueArrayFieldTargetInNode(index, rootNode, path15) {
12107
+ const normalizedPath = sanitizeElementPath(path15);
11465
12108
  const selectors = buildArrayFieldCandidates(normalizedPath);
11466
12109
  if (!selectors.length) {
11467
12110
  return rootNode;
@@ -11550,8 +12193,8 @@ function encodeDataPath(tokens) {
11550
12193
  }
11551
12194
  return out;
11552
12195
  }
11553
- function parseDataPath(path13) {
11554
- const input = path13.trim();
12196
+ function parseDataPath(path15) {
12197
+ const input = path15.trim();
11555
12198
  if (input.length === 0) {
11556
12199
  return [];
11557
12200
  }
@@ -11601,8 +12244,8 @@ function parseDataPath(path13) {
11601
12244
  function inflateDataPathObject(flat) {
11602
12245
  let root = {};
11603
12246
  let initialized = false;
11604
- for (const [path13, value] of Object.entries(flat)) {
11605
- const tokens = parseDataPath(path13);
12247
+ for (const [path15, value] of Object.entries(flat)) {
12248
+ const tokens = parseDataPath(path15);
11606
12249
  if (!tokens || tokens.length === 0) {
11607
12250
  continue;
11608
12251
  }
@@ -11934,8 +12577,8 @@ function buildVariantDescriptorFromCluster(descriptors) {
11934
12577
  fields: mergedFields
11935
12578
  };
11936
12579
  }
11937
- function minimizePathMatchClauses(path13, mode) {
11938
- const normalized = sanitizeElementPath(path13);
12580
+ function minimizePathMatchClauses(path15, mode) {
12581
+ const normalized = sanitizeElementPath(path15);
11939
12582
  const nodes = normalized.nodes.map((node, index) => {
11940
12583
  const isLast = index === normalized.nodes.length - 1;
11941
12584
  const attrs = node.attrs || {};
@@ -12039,8 +12682,8 @@ function seedMinimalAttrClause(attrs) {
12039
12682
  }
12040
12683
  return null;
12041
12684
  }
12042
- function relaxPathForSingleSample(path13, mode) {
12043
- const normalized = sanitizeElementPath(path13);
12685
+ function relaxPathForSingleSample(path15, mode) {
12686
+ const normalized = sanitizeElementPath(path15);
12044
12687
  const relaxedNodes = normalized.nodes.map((node, index) => {
12045
12688
  const isLast = index === normalized.nodes.length - 1;
12046
12689
  const attrs = normalizeAttrsForSingleSample(node.attrs || {});
@@ -12125,8 +12768,8 @@ function shouldKeepAttrForSingleSample(key) {
12125
12768
  }
12126
12769
  return true;
12127
12770
  }
12128
- function buildPathStructureKey(path13) {
12129
- const normalized = sanitizeElementPath(path13);
12771
+ function buildPathStructureKey(path15) {
12772
+ const normalized = sanitizeElementPath(path15);
12130
12773
  return canonicalJsonString({
12131
12774
  context: (normalized.context || []).map((hop) => ({
12132
12775
  kind: hop.kind,
@@ -12253,30 +12896,30 @@ function buildArrayItemNode(fields) {
12253
12896
  }
12254
12897
  return node;
12255
12898
  }
12256
- function insertNodeAtPath(root, path13, node) {
12257
- const tokens = parseDataPath(path13);
12899
+ function insertNodeAtPath(root, path15, node) {
12900
+ const tokens = parseDataPath(path15);
12258
12901
  if (!tokens || !tokens.length) {
12259
12902
  throw new Error(
12260
- `Invalid persisted extraction path "${path13}": expected a non-empty object path.`
12903
+ `Invalid persisted extraction path "${path15}": expected a non-empty object path.`
12261
12904
  );
12262
12905
  }
12263
12906
  if (tokens.some((token) => token.kind === "index")) {
12264
12907
  throw new Error(
12265
- `Invalid persisted extraction path "${path13}": nested array indices are not supported in cached descriptors.`
12908
+ `Invalid persisted extraction path "${path15}": nested array indices are not supported in cached descriptors.`
12266
12909
  );
12267
12910
  }
12268
12911
  let current = root;
12269
12912
  for (let index = 0; index < tokens.length; index += 1) {
12270
12913
  const token = tokens[index];
12271
12914
  if (!token || token.kind !== "prop") {
12272
- throw new Error(`Invalid persisted extraction path "${path13}": expected object segment.`);
12915
+ throw new Error(`Invalid persisted extraction path "${path15}": expected object segment.`);
12273
12916
  }
12274
12917
  const isLast = index === tokens.length - 1;
12275
12918
  if (isLast) {
12276
12919
  const existing = current[token.key];
12277
12920
  if (existing) {
12278
12921
  throw new Error(
12279
- `Conflicting persisted extraction path "${path13}" detected while building descriptor tree.`
12922
+ `Conflicting persisted extraction path "${path15}" detected while building descriptor tree.`
12280
12923
  );
12281
12924
  }
12282
12925
  current[token.key] = node;
@@ -12291,7 +12934,7 @@ function insertNodeAtPath(root, path13, node) {
12291
12934
  }
12292
12935
  if (!isPersistedObjectNode(next)) {
12293
12936
  throw new Error(
12294
- `Conflicting persisted extraction path "${path13}" detected at "${token.key}".`
12937
+ `Conflicting persisted extraction path "${path15}" detected at "${token.key}".`
12295
12938
  );
12296
12939
  }
12297
12940
  current = next;
@@ -12326,7 +12969,7 @@ function buildItemRootForArrayIndex(entries) {
12326
12969
  }
12327
12970
  const paths = entries.map(
12328
12971
  (entry) => isPersistablePathField(entry.source) ? sanitizeElementPath(entry.source.path) : null
12329
- ).filter((path13) => path13 !== null);
12972
+ ).filter((path15) => path15 !== null);
12330
12973
  if (!paths.length) {
12331
12974
  return null;
12332
12975
  }
@@ -12347,7 +12990,7 @@ function getCommonPathPrefixLength(paths) {
12347
12990
  if (!paths.length) {
12348
12991
  return 0;
12349
12992
  }
12350
- const nodeChains = paths.map((path13) => path13.nodes);
12993
+ const nodeChains = paths.map((path15) => path15.nodes);
12351
12994
  const minLength = Math.min(...nodeChains.map((nodes) => nodes.length));
12352
12995
  if (!Number.isFinite(minLength) || minLength <= 0) {
12353
12996
  return 0;
@@ -12416,30 +13059,30 @@ function mergeElementPathsByMajority(paths) {
12416
13059
  if (!paths.length) {
12417
13060
  return null;
12418
13061
  }
12419
- const normalized = paths.map((path13) => sanitizeElementPath(path13));
13062
+ const normalized = paths.map((path15) => sanitizeElementPath(path15));
12420
13063
  const contextKey = pickModeString(
12421
- normalized.map((path13) => canonicalJsonString(path13.context)),
13064
+ normalized.map((path15) => canonicalJsonString(path15.context)),
12422
13065
  1
12423
13066
  );
12424
13067
  if (!contextKey) {
12425
13068
  return null;
12426
13069
  }
12427
- const sameContext = normalized.filter((path13) => canonicalJsonString(path13.context) === contextKey);
13070
+ const sameContext = normalized.filter((path15) => canonicalJsonString(path15.context) === contextKey);
12428
13071
  if (!sameContext.length) {
12429
13072
  return null;
12430
13073
  }
12431
13074
  const targetLength = pickModeNumber(
12432
- sameContext.map((path13) => path13.nodes.length),
13075
+ sameContext.map((path15) => path15.nodes.length),
12433
13076
  1
12434
13077
  ) ?? sameContext[0]?.nodes.length ?? 0;
12435
- const aligned = sameContext.filter((path13) => path13.nodes.length === targetLength);
13078
+ const aligned = sameContext.filter((path15) => path15.nodes.length === targetLength);
12436
13079
  if (!aligned.length) {
12437
13080
  return null;
12438
13081
  }
12439
13082
  const threshold = majorityThreshold(aligned.length);
12440
13083
  const nodes = [];
12441
13084
  for (let index = 0; index < targetLength; index += 1) {
12442
- const nodesAtIndex = aligned.map((path13) => path13.nodes[index]).filter((node) => node !== void 0);
13085
+ const nodesAtIndex = aligned.map((path15) => path15.nodes[index]).filter((node) => node !== void 0);
12443
13086
  if (!nodesAtIndex.length) {
12444
13087
  return null;
12445
13088
  }
@@ -12685,8 +13328,8 @@ function clonePathContext(context) {
12685
13328
  function clonePathNodes(nodes) {
12686
13329
  return JSON.parse(JSON.stringify(nodes || []));
12687
13330
  }
12688
- function cloneElementPath2(path13) {
12689
- return JSON.parse(JSON.stringify(path13));
13331
+ function cloneElementPath2(path15) {
13332
+ return JSON.parse(JSON.stringify(path15));
12690
13333
  }
12691
13334
  function clonePersistedOpensteerExtractionNode(node) {
12692
13335
  return JSON.parse(JSON.stringify(node));
@@ -13004,8 +13647,8 @@ function collectPersistedValueNodeRefs(node) {
13004
13647
  return [
13005
13648
  {
13006
13649
  path: sanitizeElementPath(node.$path),
13007
- replacePath: (path13) => {
13008
- node.$path = sanitizeElementPath(path13);
13650
+ replacePath: (path15) => {
13651
+ node.$path = sanitizeElementPath(path15);
13009
13652
  }
13010
13653
  }
13011
13654
  ];
@@ -13019,13 +13662,13 @@ function collectPersistedValueNodeRefs(node) {
13019
13662
  }
13020
13663
  return refs;
13021
13664
  }
13022
- function hasPositionClause(path13) {
13023
- return path13.nodes.some((node) => node.match.some((clause) => clause.kind === "position"));
13665
+ function hasPositionClause(path15) {
13666
+ return path15.nodes.some((node) => node.match.some((clause) => clause.kind === "position"));
13024
13667
  }
13025
- function stripPositionClauses2(path13) {
13668
+ function stripPositionClauses2(path15) {
13026
13669
  return sanitizeElementPath({
13027
- context: path13.context,
13028
- nodes: path13.nodes.map((node) => ({
13670
+ context: path15.context,
13671
+ nodes: path15.nodes.map((node) => ({
13029
13672
  ...node,
13030
13673
  match: node.match.filter((clause) => clause.kind !== "position")
13031
13674
  }))
@@ -13435,14 +14078,13 @@ function normalizeNonEmptyString2(name, value) {
13435
14078
  function normalizeKey(value) {
13436
14079
  return String(value ?? "").trim();
13437
14080
  }
13438
- function labelForPath(path13) {
13439
- return path13.trim().length === 0 ? "$" : path13;
14081
+ function labelForPath(path15) {
14082
+ return path15.trim().length === 0 ? "$" : path15;
13440
14083
  }
13441
14084
  function sha256Hex3(value) {
13442
14085
  return createHash("sha256").update(value).digest("hex");
13443
14086
  }
13444
- var PROCESS_LIST_MAX_BUFFER_BYTES = 16 * 1024 * 1024;
13445
- var PS_COMMAND_ENV = { ...process.env, LC_ALL: "C" };
14087
+ ({ ...process.env});
13446
14088
  var WINDOWS_PROGRAM_FILES = process.env.PROGRAMFILES ?? "C:\\Program Files";
13447
14089
  var WINDOWS_PROGRAM_FILES_X86 = process.env["PROGRAMFILES(X86)"] ?? "C:\\Program Files (x86)";
13448
14090
  var BROWSER_BRANDS = [
@@ -13628,9 +14270,6 @@ var BROWSER_BRANDS = [
13628
14270
  }
13629
14271
  }
13630
14272
  ];
13631
- function getAllBrowserBrands() {
13632
- return BROWSER_BRANDS;
13633
- }
13634
14273
  function getBrowserBrand(id) {
13635
14274
  const brand2 = BROWSER_BRANDS.find((candidate) => candidate.id === id);
13636
14275
  if (!brand2) {
@@ -13673,28 +14312,6 @@ function detectInstalledBrowserBrands() {
13673
14312
  }
13674
14313
  return installations;
13675
14314
  }
13676
- function resolveBrandExecutablePath(brand2, explicitPath) {
13677
- if (explicitPath !== void 0) {
13678
- const resolvedPath2 = resolve(expandHome(explicitPath));
13679
- if (!existsSync(resolvedPath2)) {
13680
- throw new Error(`${brand2.displayName} executable was not found at "${resolvedPath2}".`);
13681
- }
13682
- return resolvedPath2;
13683
- }
13684
- const platformConfig = resolveBrandPlatformConfig(brand2);
13685
- if (!platformConfig) {
13686
- throw new Error(`${brand2.displayName} is not supported on ${process.platform}.`);
13687
- }
13688
- const resolvedPath = firstExistingPath(
13689
- resolveExecutableCandidates(platformConfig.executableCandidates)
13690
- );
13691
- if (!resolvedPath) {
13692
- throw new Error(
13693
- `Could not find a ${brand2.displayName} executable. Pass --executable-path or browser.executablePath.`
13694
- );
13695
- }
13696
- return resolvedPath;
13697
- }
13698
14315
  function resolveBrandUserDataDir(brand2, explicitDir) {
13699
14316
  if (explicitDir !== void 0) {
13700
14317
  return resolve(expandHome(explicitDir));
@@ -13705,129 +14322,9 @@ function resolveBrandUserDataDir(brand2, explicitDir) {
13705
14322
  }
13706
14323
  return resolve(expandHome(platformConfig.userDataDir));
13707
14324
  }
13708
- function isBrandProcess(brand2, commandLine) {
13709
- const normalizedCommand = normalizeCommand(commandLine);
13710
- if (!normalizedCommand) {
13711
- return false;
13712
- }
13713
- if (normalizedCommand.includes("crashpad_handler")) {
13714
- return false;
13715
- }
13716
- if (/\s--type=/.test(normalizedCommand)) {
13717
- return false;
13718
- }
13719
- return getBrandProcessMarkers(brand2).some((marker) => normalizedCommand.includes(marker));
13720
- }
13721
- function findBrandProcess(brand2) {
13722
- for (const processEntry of listProcesses()) {
13723
- if (isBrandProcess(brand2, processEntry.commandLine)) {
13724
- return { pid: processEntry.pid };
13725
- }
13726
- }
13727
- return null;
13728
- }
13729
- function getBrandProcessMarkers(brand2) {
13730
- const markers = /* @__PURE__ */ new Set();
13731
- for (const config of [brand2.darwin, brand2.win32, brand2.linux]) {
13732
- if (!config) {
13733
- continue;
13734
- }
13735
- for (const processName of config.processNames) {
13736
- const normalized = normalizeCommand(processName);
13737
- if (normalized) {
13738
- markers.add(normalized);
13739
- }
13740
- }
13741
- for (const candidate of config.executableCandidates) {
13742
- if (!candidate) {
13743
- continue;
13744
- }
13745
- const normalized = normalizeCommand(resolve(expandHome(candidate)));
13746
- if (normalized) {
13747
- markers.add(normalized);
13748
- }
13749
- }
13750
- }
13751
- return [...markers];
13752
- }
13753
14325
  function resolveExecutableCandidates(candidates) {
13754
14326
  return candidates.map((candidate) => candidate ? resolve(expandHome(candidate)) : null);
13755
14327
  }
13756
- function listProcesses() {
13757
- if (process.platform === "win32") {
13758
- return listWindowsProcesses();
13759
- }
13760
- return listUnixProcesses();
13761
- }
13762
- function listUnixProcesses() {
13763
- try {
13764
- const output = execFileSync("ps", ["-A", "-o", "pid=,command="], {
13765
- encoding: "utf8",
13766
- env: PS_COMMAND_ENV,
13767
- maxBuffer: PROCESS_LIST_MAX_BUFFER_BYTES,
13768
- stdio: ["ignore", "pipe", "ignore"]
13769
- });
13770
- return output.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0).map((line) => {
13771
- const match = /^(\d+)\s+(.*)$/.exec(line);
13772
- if (!match) {
13773
- return null;
13774
- }
13775
- const pid = Number.parseInt(match[1] ?? "", 10);
13776
- const commandLine = match[2]?.trim() ?? "";
13777
- if (!Number.isInteger(pid) || pid <= 0 || commandLine.length === 0) {
13778
- return null;
13779
- }
13780
- return {
13781
- pid,
13782
- commandLine
13783
- };
13784
- }).filter(
13785
- (entry) => entry !== null
13786
- ).sort((left, right) => left.pid - right.pid);
13787
- } catch {
13788
- return [];
13789
- }
13790
- }
13791
- function listWindowsProcesses() {
13792
- try {
13793
- const output = execFileSync(
13794
- "powershell.exe",
13795
- [
13796
- "-NoProfile",
13797
- "-Command",
13798
- "Get-CimInstance Win32_Process | Select-Object ProcessId,CommandLine | ConvertTo-Json -Compress"
13799
- ],
13800
- {
13801
- encoding: "utf8",
13802
- maxBuffer: PROCESS_LIST_MAX_BUFFER_BYTES,
13803
- stdio: ["ignore", "pipe", "ignore"]
13804
- }
13805
- ).trim();
13806
- if (!output) {
13807
- return [];
13808
- }
13809
- const parsed = JSON.parse(output);
13810
- const records = Array.isArray(parsed) ? parsed : [parsed];
13811
- return records.map((record) => {
13812
- const pid = Number(record.ProcessId);
13813
- const commandLine = typeof record.CommandLine === "string" ? record.CommandLine.trim() : "";
13814
- if (!Number.isInteger(pid) || pid <= 0 || commandLine.length === 0) {
13815
- return null;
13816
- }
13817
- return {
13818
- pid,
13819
- commandLine
13820
- };
13821
- }).filter(
13822
- (entry) => entry !== null
13823
- ).sort((left, right) => left.pid - right.pid);
13824
- } catch {
13825
- return [];
13826
- }
13827
- }
13828
- function normalizeCommand(value) {
13829
- return value.trim().replaceAll("\\", "/").toLowerCase();
13830
- }
13831
14328
 
13832
14329
  // src/local-browser/chrome-discovery.ts
13833
14330
  function expandHome(value) {
@@ -14194,8 +14691,8 @@ function buildBrowserWebSocketUrl(httpUrl, webSocketPath) {
14194
14691
  const protocol = httpUrl.protocol === "https:" ? "wss:" : "ws:";
14195
14692
  return `${protocol}//${httpUrl.host}${normalizeWebSocketPath(webSocketPath)}`;
14196
14693
  }
14197
- function normalizeWebSocketPath(path13) {
14198
- return path13.startsWith("/") ? path13 : `/${path13}`;
14694
+ function normalizeWebSocketPath(path15) {
14695
+ return path15.startsWith("/") ? path15 : `/${path15}`;
14199
14696
  }
14200
14697
  function rewriteBrowserWebSocketHost(browserWsUrl, requestedUrl) {
14201
14698
  try {
@@ -14215,7 +14712,7 @@ function readPort(url) {
14215
14712
  var OPENSTEER_LIVE_SESSION_LAYOUT = "opensteer-session";
14216
14713
  var OPENSTEER_LIVE_SESSION_VERSION = 1;
14217
14714
  function resolveLiveSessionRecordPath(rootPath, provider) {
14218
- return path6.join(rootPath, "live", provider === "local" ? "local.json" : "cloud.json");
14715
+ return path7.join(rootPath, "live", provider === "local" ? "local.json" : "cloud.json");
14219
14716
  }
14220
14717
  function resolveLocalSessionRecordPath(rootPath) {
14221
14718
  return resolveLiveSessionRecordPath(rootPath, "local");
@@ -15020,8 +15517,8 @@ var OpensteerBrowserManager = class {
15020
15517
  ...options.browser === void 0 ? {} : { browser: options.browser },
15021
15518
  ...this.contextOptions === void 0 ? {} : { context: this.contextOptions }
15022
15519
  });
15023
- this.rootPath = options.rootPath ?? (this.workspace === void 0 ? path6.join(tmpdir(), `${TEMPORARY_WORKSPACE_PREFIX}${randomUUID()}`) : resolveFilesystemWorkspacePath({
15024
- rootDir: path6.resolve(options.rootDir ?? process.cwd()),
15520
+ this.rootPath = options.rootPath ?? (this.workspace === void 0 ? path7.join(tmpdir(), `${TEMPORARY_WORKSPACE_PREFIX}${randomUUID()}`) : resolveFilesystemWorkspacePath({
15521
+ rootDir: path7.resolve(options.rootDir ?? process.cwd()),
15025
15522
  workspace: this.workspace
15026
15523
  }));
15027
15524
  this.cleanupRootOnDisconnect = this.workspace === void 0;
@@ -15091,7 +15588,7 @@ var OpensteerBrowserManager = class {
15091
15588
  userDataDir: "browser/user-data",
15092
15589
  bootstrap: {
15093
15590
  kind: "cloneLocalProfile",
15094
- sourceUserDataDir: path6.resolve(input.sourceUserDataDir),
15591
+ sourceUserDataDir: path7.resolve(input.sourceUserDataDir),
15095
15592
  ...input.sourceProfileDirectory === void 0 ? {} : { sourceProfileDirectory: input.sourceProfileDirectory }
15096
15593
  }
15097
15594
  };
@@ -15218,7 +15715,7 @@ var OpensteerBrowserManager = class {
15218
15715
  });
15219
15716
  }
15220
15717
  async createTemporaryEngine() {
15221
- const userDataDir = await mkdtemp(path6.join(tmpdir(), "opensteer-temporary-browser-"));
15718
+ const userDataDir = await mkdtemp(path7.join(tmpdir(), "opensteer-temporary-browser-"));
15222
15719
  await clearChromeSingletonEntries(userDataDir);
15223
15720
  const launched = await launchOwnedBrowser({
15224
15721
  userDataDir,
@@ -15665,7 +16162,7 @@ async function terminateProcess(pid) {
15665
16162
  }
15666
16163
  }
15667
16164
  async function requestBrowserClose(endpoint) {
15668
- await new Promise((resolve5, reject) => {
16165
+ await new Promise((resolve4, reject) => {
15669
16166
  const socket = new WebSocket(endpoint);
15670
16167
  const timeout = setTimeout(() => {
15671
16168
  socket.close();
@@ -15682,7 +16179,7 @@ async function requestBrowserClose(endpoint) {
15682
16179
  reject(error);
15683
16180
  return;
15684
16181
  }
15685
- resolve5();
16182
+ resolve4();
15686
16183
  };
15687
16184
  socket.addEventListener("open", () => {
15688
16185
  socket.send(JSON.stringify({ id: 1, method: "Browser.close" }));
@@ -15720,7 +16217,7 @@ async function waitForProcessExit(pid, timeoutMs) {
15720
16217
  return !isProcessRunning(pid);
15721
16218
  }
15722
16219
  function resolveAbpSessionDir(workspace) {
15723
- return path6.join(workspace.livePath, "abp-session");
16220
+ return path7.join(workspace.livePath, "abp-session");
15724
16221
  }
15725
16222
  async function allocateEphemeralPort() {
15726
16223
  const { allocatePort } = await loadAbpModule();
@@ -15778,7 +16275,7 @@ function isStealthProfile(input) {
15778
16275
  return input.id !== void 0 && input.platform !== void 0 && input.browserBrand !== void 0 && input.browserVersion !== void 0 && input.userAgent !== void 0 && input.viewport !== void 0 && input.screenResolution !== void 0 && input.devicePixelRatio !== void 0 && input.maxTouchPoints !== void 0 && input.webglVendor !== void 0 && input.webglRenderer !== void 0 && input.fonts !== void 0 && input.canvasNoiseSeed !== void 0 && input.audioNoiseSeed !== void 0 && input.locale !== void 0 && input.timezoneId !== void 0;
15779
16276
  }
15780
16277
  async function sleep(ms) {
15781
- await new Promise((resolve5) => setTimeout(resolve5, ms));
16278
+ await new Promise((resolve4) => setTimeout(resolve4, ms));
15782
16279
  }
15783
16280
 
15784
16281
  // ../runtime-core/src/sdk/semantic-dispatch.ts
@@ -16044,7 +16541,7 @@ async function dispatchSemanticOperation(runtime, operation, input, options = {}
16044
16541
 
16045
16542
  // ../runtime-core/package.json
16046
16543
  var package_default = {
16047
- version: "0.1.1"};
16544
+ version: "0.1.2"};
16048
16545
 
16049
16546
  // ../runtime-core/src/version.ts
16050
16547
  var OPENSTEER_RUNTIME_CORE_VERSION = package_default.version;
@@ -16345,6 +16842,7 @@ var DefaultComputerUseRuntime = class {
16345
16842
  const snapshot = await input.timeout.runStep(
16346
16843
  () => captureActionBoundarySnapshot(this.options.engine, input.pageRef)
16347
16844
  );
16845
+ let visualSettled = true;
16348
16846
  const executed = await input.timeout.runStep(
16349
16847
  () => bridge.execute({
16350
16848
  pageRef: input.pageRef,
@@ -16353,16 +16851,35 @@ var DefaultComputerUseRuntime = class {
16353
16851
  screenshot,
16354
16852
  signal: input.timeout.signal,
16355
16853
  remainingMs: () => input.timeout.remainingMs(),
16356
- policySettle: async (pageRef, trigger) => settleWithPolicy(this.options.policy.settle, {
16357
- operation: "computer.execute",
16358
- trigger,
16359
- engine: this.options.engine,
16360
- pageRef,
16361
- signal: input.timeout.signal,
16362
- remainingMs: input.timeout.remainingMs()
16363
- })
16854
+ policySettle: async (pageRef, trigger) => {
16855
+ try {
16856
+ await settleWithPolicy(this.options.policy.settle, {
16857
+ operation: "computer.execute",
16858
+ trigger,
16859
+ engine: this.options.engine,
16860
+ pageRef,
16861
+ signal: input.timeout.signal,
16862
+ remainingMs: input.timeout.remainingMs()
16863
+ });
16864
+ } catch (error) {
16865
+ if (pageRef === input.pageRef && isSoftSettleTimeoutError(error, input.timeout.signal)) {
16866
+ visualSettled = false;
16867
+ return;
16868
+ }
16869
+ throw error;
16870
+ }
16871
+ }
16364
16872
  })
16365
16873
  );
16874
+ if (executed.boundary !== void 0 && executed.pageRef === input.pageRef) {
16875
+ recordActionBoundaryDiagnostics(
16876
+ input.timeout.signal,
16877
+ createActionBoundaryDiagnostics({
16878
+ boundary: executed.boundary,
16879
+ visualSettled
16880
+ })
16881
+ );
16882
+ }
16366
16883
  let trace = void 0;
16367
16884
  if (!input.timeout.signal.aborted) {
16368
16885
  try {
@@ -17372,7 +17889,7 @@ function diffStringMap(prefix, left, right, includeUnchanged, output) {
17372
17889
  diffScalarField(`${prefix}.${key}`, left[key], right[key], includeUnchanged, output);
17373
17890
  }
17374
17891
  }
17375
- function diffScalarField(path13, left, right, includeUnchanged, output) {
17892
+ function diffScalarField(path15, left, right, includeUnchanged, output) {
17376
17893
  const leftValue = stringifyFieldValue(left);
17377
17894
  const rightValue = stringifyFieldValue(right);
17378
17895
  const kind = leftValue === void 0 ? rightValue === void 0 ? "unchanged" : "added" : rightValue === void 0 ? "removed" : leftValue === rightValue ? "unchanged" : "changed";
@@ -17380,7 +17897,7 @@ function diffScalarField(path13, left, right, includeUnchanged, output) {
17380
17897
  return;
17381
17898
  }
17382
17899
  output.push({
17383
- path: path13,
17900
+ path: path15,
17384
17901
  kind,
17385
17902
  ...leftValue === void 0 ? {} : { leftValue },
17386
17903
  ...rightValue === void 0 ? {} : { rightValue },
@@ -17474,7 +17991,7 @@ function resolveBodyEncoding(charset) {
17474
17991
  var NetworkHistory = class {
17475
17992
  metadataByRequestId = /* @__PURE__ */ new Map();
17476
17993
  requestIdByRecordId = /* @__PURE__ */ new Map();
17477
- requestIdsByActionId = /* @__PURE__ */ new Map();
17994
+ requestIdsByCapture = /* @__PURE__ */ new Map();
17478
17995
  requestIdsByTag = /* @__PURE__ */ new Map();
17479
17996
  tombstonedRequestIds = /* @__PURE__ */ new Set();
17480
17997
  materialize(records, options = {}) {
@@ -17521,17 +18038,17 @@ var NetworkHistory = class {
17521
18038
  }
17522
18039
  return persisted;
17523
18040
  }
17524
- assignActionId(records, actionId) {
18041
+ assignCapture(records, capture) {
17525
18042
  for (const record of records) {
17526
18043
  const metadata = this.metadataByRequestId.get(record.record.requestId);
17527
- if (!metadata || metadata.actionId === actionId) {
18044
+ if (!metadata || metadata.capture === capture) {
17528
18045
  continue;
17529
18046
  }
17530
- if (metadata.actionId !== void 0) {
17531
- this.requestIdsByActionId.get(metadata.actionId)?.delete(record.record.requestId);
18047
+ if (metadata.capture !== void 0) {
18048
+ this.requestIdsByCapture.get(metadata.capture)?.delete(record.record.requestId);
17532
18049
  }
17533
- metadata.actionId = actionId;
17534
- this.addIndexedRequestId(this.requestIdsByActionId, actionId, record.record.requestId);
18050
+ metadata.capture = capture;
18051
+ this.addIndexedRequestId(this.requestIdsByCapture, capture, record.record.requestId);
17535
18052
  }
17536
18053
  }
17537
18054
  addTag(records, tag) {
@@ -17551,8 +18068,8 @@ var NetworkHistory = class {
17551
18068
  getRequestId(recordId) {
17552
18069
  return this.requestIdByRecordId.get(recordId);
17553
18070
  }
17554
- getRequestIdsForActionId(actionId) {
17555
- return new Set(this.requestIdsByActionId.get(actionId) ?? []);
18071
+ getRequestIdsForCapture(capture) {
18072
+ return new Set(this.requestIdsByCapture.get(capture) ?? []);
17556
18073
  }
17557
18074
  getRequestIdsForTag(tag) {
17558
18075
  return new Set(this.requestIdsByTag.get(tag) ?? []);
@@ -17563,20 +18080,6 @@ var NetworkHistory = class {
17563
18080
  getKnownRequestIds() {
17564
18081
  return new Set(this.metadataByRequestId.keys());
17565
18082
  }
17566
- clearTag(tag) {
17567
- const requestIds = [...this.requestIdsByTag.get(tag) ?? []];
17568
- this.requestIdsByTag.delete(tag);
17569
- for (const requestId of requestIds) {
17570
- const metadata = this.metadataByRequestId.get(requestId);
17571
- if (!metadata) {
17572
- continue;
17573
- }
17574
- metadata.tags.delete(tag);
17575
- if (metadata.tags.size === 0) {
17576
- this.tombstoneRequestIds([requestId]);
17577
- }
17578
- }
17579
- }
17580
18083
  tombstoneRequestIds(requestIds) {
17581
18084
  for (const requestId of requestIds) {
17582
18085
  this.tombstonedRequestIds.add(requestId);
@@ -17586,8 +18089,8 @@ var NetworkHistory = class {
17586
18089
  }
17587
18090
  this.metadataByRequestId.delete(requestId);
17588
18091
  this.requestIdByRecordId.delete(metadata.recordId);
17589
- if (metadata.actionId !== void 0) {
17590
- this.requestIdsByActionId.get(metadata.actionId)?.delete(requestId);
18092
+ if (metadata.capture !== void 0) {
18093
+ this.requestIdsByCapture.get(metadata.capture)?.delete(requestId);
17591
18094
  }
17592
18095
  for (const tag of metadata.tags) {
17593
18096
  this.requestIdsByTag.get(tag)?.delete(requestId);
@@ -17597,7 +18100,7 @@ var NetworkHistory = class {
17597
18100
  clear() {
17598
18101
  this.metadataByRequestId.clear();
17599
18102
  this.requestIdByRecordId.clear();
17600
- this.requestIdsByActionId.clear();
18103
+ this.requestIdsByCapture.clear();
17601
18104
  this.requestIdsByTag.clear();
17602
18105
  this.tombstonedRequestIds.clear();
17603
18106
  }
@@ -17620,7 +18123,7 @@ var NetworkHistory = class {
17620
18123
  }
17621
18124
  return {
17622
18125
  recordId: metadata.recordId,
17623
- ...metadata.actionId === void 0 ? {} : { actionId: metadata.actionId },
18126
+ ...metadata.capture === void 0 ? {} : { capture: metadata.capture },
17624
18127
  ...metadata.tags.size === 0 ? {} : { tags: [...metadata.tags].sort() },
17625
18128
  ...metadata.savedAt === void 0 ? {} : { savedAt: metadata.savedAt },
17626
18129
  record: toProtocolNetworkRecord(record, {
@@ -18400,9 +18903,9 @@ function matchReverseTargetHints(channel, codec, targetHints) {
18400
18903
  matches.add(`host:${host}`);
18401
18904
  }
18402
18905
  }
18403
- for (const path13 of targetHints.paths ?? []) {
18404
- if (url.pathname.includes(path13)) {
18405
- matches.add(`path:${path13}`);
18906
+ for (const path15 of targetHints.paths ?? []) {
18907
+ if (url.pathname.includes(path15)) {
18908
+ matches.add(`path:${path15}`);
18406
18909
  }
18407
18910
  }
18408
18911
  for (const operationName of targetHints.operationNames ?? []) {
@@ -19363,11 +19866,11 @@ function inferClusterRelationship(seed, record) {
19363
19866
  var MATCHED_TLS_BINARY_NAMES = ["curl-impersonate-chrome", "curl_chrome"];
19364
19867
  async function executeMatchedTlsTransportRequest(input) {
19365
19868
  const binary = await resolveMatchedTlsBinary();
19366
- const workingDirectory = await mkdtemp(path6.join(tmpdir(), "opensteer-matched-tls-"));
19367
- const headersPath = path6.join(workingDirectory, "headers.txt");
19368
- const bodyPath = path6.join(workingDirectory, "body.bin");
19369
- const cookiesPath = path6.join(workingDirectory, "cookies.txt");
19370
- const requestBodyPath = path6.join(workingDirectory, "request-body.bin");
19869
+ const workingDirectory = await mkdtemp(path7.join(tmpdir(), "opensteer-matched-tls-"));
19870
+ const headersPath = path7.join(workingDirectory, "headers.txt");
19871
+ const bodyPath = path7.join(workingDirectory, "body.bin");
19872
+ const cookiesPath = path7.join(workingDirectory, "cookies.txt");
19873
+ const requestBodyPath = path7.join(workingDirectory, "request-body.bin");
19371
19874
  try {
19372
19875
  await writeFile(cookiesPath, toNetscapeCookieJar(input.cookies ?? []), "utf8");
19373
19876
  if (input.request.body !== void 0) {
@@ -19424,10 +19927,10 @@ async function executeMatchedTlsTransportRequest(input) {
19424
19927
  }
19425
19928
  }
19426
19929
  async function resolveMatchedTlsBinary() {
19427
- const pathEntries = (process.env.PATH ?? "").split(path6.delimiter).filter((entry) => entry.length > 0);
19930
+ const pathEntries = (process.env.PATH ?? "").split(path7.delimiter).filter((entry) => entry.length > 0);
19428
19931
  for (const directory of pathEntries) {
19429
19932
  for (const name of MATCHED_TLS_BINARY_NAMES) {
19430
- const candidate = path6.join(directory, name);
19933
+ const candidate = path7.join(directory, name);
19431
19934
  if (await isExecutable(candidate)) {
19432
19935
  return candidate;
19433
19936
  }
@@ -19435,7 +19938,7 @@ async function resolveMatchedTlsBinary() {
19435
19938
  const files = await readDirSafe(directory);
19436
19939
  const discovered = files.find((file) => file.startsWith("curl_chrome"));
19437
19940
  if (discovered !== void 0) {
19438
- const candidate = path6.join(directory, discovered);
19941
+ const candidate = path7.join(directory, discovered);
19439
19942
  if (await isExecutable(candidate)) {
19440
19943
  return candidate;
19441
19944
  }
@@ -19446,7 +19949,7 @@ async function resolveMatchedTlsBinary() {
19446
19949
  );
19447
19950
  }
19448
19951
  async function spawnAndCollect(command, args, signal) {
19449
- return await new Promise((resolve5, reject) => {
19952
+ return await new Promise((resolve4, reject) => {
19450
19953
  const child = spawn(command, args, {
19451
19954
  stdio: ["ignore", "pipe", "pipe"]
19452
19955
  });
@@ -19479,7 +19982,7 @@ async function spawnAndCollect(command, args, signal) {
19479
19982
  );
19480
19983
  return;
19481
19984
  }
19482
- resolve5({ stdout, stderr });
19985
+ resolve4({ stdout, stderr });
19483
19986
  });
19484
19987
  });
19485
19988
  }
@@ -22141,8 +22644,8 @@ function readString(value) {
22141
22644
  return typeof value === "string" && value.length > 0 ? value : void 0;
22142
22645
  }
22143
22646
  function sleep2(ms, signal) {
22144
- return new Promise((resolve5, reject) => {
22145
- const timeout = setTimeout(resolve5, ms);
22647
+ return new Promise((resolve4, reject) => {
22648
+ const timeout = setTimeout(resolve4, ms);
22146
22649
  const abort = () => {
22147
22650
  clearTimeout(timeout);
22148
22651
  reject(new Error("captcha solve aborted"));
@@ -22239,8 +22742,8 @@ function readString2(value) {
22239
22742
  return typeof value === "string" && value.length > 0 ? value : void 0;
22240
22743
  }
22241
22744
  function sleep3(ms, signal) {
22242
- return new Promise((resolve5, reject) => {
22243
- const timeout = setTimeout(resolve5, ms);
22745
+ return new Promise((resolve4, reject) => {
22746
+ const timeout = setTimeout(resolve4, ms);
22244
22747
  const abort = () => {
22245
22748
  clearTimeout(timeout);
22246
22749
  reject(new Error("captcha solve aborted"));
@@ -22438,6 +22941,9 @@ function diffInteractionTraces(left, right) {
22438
22941
 
22439
22942
  // ../runtime-core/src/sdk/runtime.ts
22440
22943
  var requireForAuthRecipeHook = createRequire(import.meta.url);
22944
+ var MUTATION_CAPTURE_FINALIZE_TIMEOUT_MS = 5e3;
22945
+ var PENDING_OPERATION_EVENT_CAPTURE_LIMIT = 64;
22946
+ var PENDING_OPERATION_EVENT_CAPTURE_SKEW_MS = 1e3;
22441
22947
  var OpensteerSessionRuntime = class {
22442
22948
  workspace;
22443
22949
  rootPath;
@@ -22450,6 +22956,9 @@ var OpensteerSessionRuntime = class {
22450
22956
  registryOverrides;
22451
22957
  cleanupRootOnClose;
22452
22958
  sessionInfoBase;
22959
+ observationConfig;
22960
+ observationSessionId;
22961
+ injectedObservationSink;
22453
22962
  root;
22454
22963
  engine;
22455
22964
  dom;
@@ -22459,6 +22968,9 @@ var OpensteerSessionRuntime = class {
22459
22968
  sessionRef;
22460
22969
  pageRef;
22461
22970
  runId;
22971
+ observations;
22972
+ operationEventStorage = new AsyncLocalStorage();
22973
+ pendingOperationEventCaptures = [];
22462
22974
  cookieJars = /* @__PURE__ */ new Map();
22463
22975
  recipeCache = /* @__PURE__ */ new Map();
22464
22976
  ownsEngine = false;
@@ -22466,7 +22978,7 @@ var OpensteerSessionRuntime = class {
22466
22978
  this.workspace = normalizeNamespace2(options.name);
22467
22979
  this.workspaceName = options.workspaceName?.trim() === void 0 || options.workspaceName?.trim().length === 0 ? void 0 : options.workspaceName.trim();
22468
22980
  this.root = options.workspace;
22469
- this.rootPath = options.workspace?.rootPath ?? options.rootPath ?? path6.resolve(process.cwd(), ".opensteer", "temporary", randomUUID());
22981
+ this.rootPath = options.workspace?.rootPath ?? options.rootPath ?? path7.resolve(process.cwd(), ".opensteer", "temporary", randomUUID());
22470
22982
  this.injectedEngine = options.engine;
22471
22983
  this.engineFactory = options.engineFactory;
22472
22984
  this.policy = options.policy ?? defaultPolicy();
@@ -22475,6 +22987,9 @@ var OpensteerSessionRuntime = class {
22475
22987
  this.registryOverrides = options.registryOverrides;
22476
22988
  this.cleanupRootOnClose = options.cleanupRootOnClose ?? options.workspace === void 0;
22477
22989
  this.sessionInfoBase = options.sessionInfo ?? {};
22990
+ this.observationConfig = normalizeObservabilityConfig(options.observability);
22991
+ this.observationSessionId = options.observationSessionId;
22992
+ this.injectedObservationSink = options.observationSink;
22478
22993
  if (this.injectedEngine === void 0 && this.engineFactory === void 0) {
22479
22994
  throw new Error("OpensteerSessionRuntime requires an engine or engineFactory.");
22480
22995
  }
@@ -22506,6 +23021,20 @@ var OpensteerSessionRuntime = class {
22506
23021
  }
22507
23022
  };
22508
23023
  }
23024
+ async setObservabilityConfig(input) {
23025
+ this.observationConfig = normalizeObservabilityConfig(input);
23026
+ const observationSessionId = this.resolveObservationSessionId();
23027
+ if (observationSessionId === void 0) {
23028
+ return this.observationConfig;
23029
+ }
23030
+ const sink = this.injectedObservationSink ?? (await this.ensureRoot()).observations;
23031
+ this.observations = await sink.openSession({
23032
+ sessionId: observationSessionId,
23033
+ openedAt: Date.now(),
23034
+ config: this.observationConfig
23035
+ });
23036
+ return this.observationConfig;
23037
+ }
22509
23038
  async open(input = {}, options = {}) {
22510
23039
  assertValidSemanticOperationInput("session.open", input);
22511
23040
  if (input.workspace !== void 0 && normalizeNamespace2(input.workspace) !== this.workspace) {
@@ -22604,6 +23133,10 @@ var OpensteerSessionRuntime = class {
22604
23133
  return { pages: [] };
22605
23134
  }
22606
23135
  const startedAt = Date.now();
23136
+ const context = buildRuntimeTraceContext({
23137
+ sessionRef: this.sessionRef,
23138
+ pageRef: this.pageRef
23139
+ });
22607
23140
  try {
22608
23141
  const output = await this.runWithOperationTimeout(
22609
23142
  "page.list",
@@ -22618,19 +23151,18 @@ var OpensteerSessionRuntime = class {
22618
23151
  },
22619
23152
  options
22620
23153
  );
23154
+ const events = await this.drainPendingEngineEvents(context);
22621
23155
  await this.appendTrace({
22622
23156
  operation: "page.list",
22623
23157
  startedAt,
22624
23158
  completedAt: Date.now(),
22625
23159
  outcome: "ok",
23160
+ ...events === void 0 ? {} : { events },
22626
23161
  data: {
22627
23162
  count: output.pages.length,
22628
23163
  ...output.activePageRef === void 0 ? {} : { activePageRef: output.activePageRef }
22629
23164
  },
22630
- context: buildRuntimeTraceContext({
22631
- sessionRef: this.sessionRef,
22632
- pageRef: this.pageRef
22633
- })
23165
+ context
22634
23166
  });
22635
23167
  return output;
22636
23168
  } catch (error) {
@@ -22640,10 +23172,7 @@ var OpensteerSessionRuntime = class {
22640
23172
  completedAt: Date.now(),
22641
23173
  outcome: "error",
22642
23174
  error,
22643
- context: buildRuntimeTraceContext({
22644
- sessionRef: this.sessionRef,
22645
- pageRef: this.pageRef
22646
- })
23175
+ context
22647
23176
  });
22648
23177
  throw error;
22649
23178
  }
@@ -22820,34 +23349,32 @@ var OpensteerSessionRuntime = class {
22820
23349
  assertValidSemanticOperationInput("page.goto", input);
22821
23350
  const pageRef = await this.ensurePageRef();
22822
23351
  const startedAt = Date.now();
23352
+ let mutationCaptureDiagnostics;
22823
23353
  try {
22824
- const { navigation, state } = await this.runWithOperationTimeout(
23354
+ const { navigation, state } = await this.runMutationCapturedOperation(
22825
23355
  "page.goto",
23356
+ {
23357
+ ...input.captureNetwork === void 0 ? {} : { captureNetwork: input.captureNetwork },
23358
+ options
23359
+ },
22826
23360
  async (timeout) => {
22827
- const baselineRequestIds = await this.beginMutationCapture(timeout);
22828
- try {
22829
- const navigation2 = await this.navigatePage(
22830
- {
22831
- operation: "page.goto",
22832
- pageRef,
22833
- url: input.url
22834
- },
22835
- timeout
22836
- );
22837
- timeout.throwIfAborted();
22838
- await this.completeMutationCapture(timeout, baselineRequestIds, input.networkTag);
22839
- return {
22840
- navigation: navigation2,
22841
- state: await timeout.runStep(() => this.readSessionState())
22842
- };
22843
- } catch (error) {
22844
- await this.completeMutationCapture(timeout, baselineRequestIds, input.networkTag).catch(
22845
- () => void 0
22846
- );
22847
- throw error;
22848
- }
23361
+ const navigation2 = await this.navigatePage(
23362
+ {
23363
+ operation: "page.goto",
23364
+ pageRef,
23365
+ url: input.url
23366
+ },
23367
+ timeout
23368
+ );
23369
+ timeout.throwIfAborted();
23370
+ return {
23371
+ navigation: navigation2,
23372
+ state: await timeout.runStep(() => this.readSessionState())
23373
+ };
22849
23374
  },
22850
- options
23375
+ (diagnostics) => {
23376
+ mutationCaptureDiagnostics = diagnostics;
23377
+ }
22851
23378
  );
22852
23379
  await this.appendTrace({
22853
23380
  operation: "page.goto",
@@ -22856,7 +23383,8 @@ var OpensteerSessionRuntime = class {
22856
23383
  outcome: "ok",
22857
23384
  data: {
22858
23385
  url: input.url,
22859
- state
23386
+ state,
23387
+ ...buildMutationCaptureTraceData(mutationCaptureDiagnostics)
22860
23388
  },
22861
23389
  context: buildRuntimeTraceContext({
22862
23390
  sessionRef: this.sessionRef,
@@ -22872,6 +23400,7 @@ var OpensteerSessionRuntime = class {
22872
23400
  completedAt: Date.now(),
22873
23401
  outcome: "error",
22874
23402
  error,
23403
+ data: buildMutationCaptureTraceData(mutationCaptureDiagnostics),
22875
23404
  context: buildRuntimeTraceContext({
22876
23405
  sessionRef: this.sessionRef,
22877
23406
  pageRef
@@ -22884,34 +23413,29 @@ var OpensteerSessionRuntime = class {
22884
23413
  assertValidSemanticOperationInput("page.evaluate", input);
22885
23414
  const pageRef = input.pageRef ?? await this.ensurePageRef();
22886
23415
  const startedAt = Date.now();
23416
+ let mutationCaptureDiagnostics;
22887
23417
  try {
22888
- const output = await this.runWithOperationTimeout(
23418
+ const output = await this.runMutationCapturedOperation(
22889
23419
  "page.evaluate",
23420
+ { options },
22890
23421
  async (timeout) => {
22891
- const baselineRequestIds = await this.beginMutationCapture(timeout);
22892
- try {
22893
- const remainingMs = timeout.remainingMs();
22894
- const evaluated = await timeout.runStep(
22895
- () => this.requireEngine().evaluatePage({
22896
- pageRef,
22897
- script: input.script,
22898
- ...input.args === void 0 ? {} : { args: input.args },
22899
- ...remainingMs === void 0 ? {} : { timeoutMs: remainingMs }
22900
- })
22901
- );
22902
- await this.completeMutationCapture(timeout, baselineRequestIds, void 0);
22903
- return {
23422
+ const remainingMs = timeout.remainingMs();
23423
+ const evaluated = await timeout.runStep(
23424
+ () => this.requireEngine().evaluatePage({
22904
23425
  pageRef,
22905
- value: toJsonValueOrNull(evaluated.data)
22906
- };
22907
- } catch (error) {
22908
- await this.completeMutationCapture(timeout, baselineRequestIds, void 0).catch(
22909
- () => void 0
22910
- );
22911
- throw error;
22912
- }
23426
+ script: input.script,
23427
+ ...input.args === void 0 ? {} : { args: input.args },
23428
+ ...remainingMs === void 0 ? {} : { timeoutMs: remainingMs }
23429
+ })
23430
+ );
23431
+ return {
23432
+ pageRef,
23433
+ value: toJsonValueOrNull(evaluated.data)
23434
+ };
22913
23435
  },
22914
- options
23436
+ (diagnostics) => {
23437
+ mutationCaptureDiagnostics = diagnostics;
23438
+ }
22915
23439
  );
22916
23440
  await this.appendTrace({
22917
23441
  operation: "page.evaluate",
@@ -22920,7 +23444,8 @@ var OpensteerSessionRuntime = class {
22920
23444
  outcome: "ok",
22921
23445
  data: {
22922
23446
  pageRef: output.pageRef,
22923
- value: output.value
23447
+ value: output.value,
23448
+ ...buildMutationCaptureTraceData(mutationCaptureDiagnostics)
22924
23449
  },
22925
23450
  context: buildRuntimeTraceContext({
22926
23451
  sessionRef: this.sessionRef,
@@ -22935,6 +23460,7 @@ var OpensteerSessionRuntime = class {
22935
23460
  completedAt: Date.now(),
22936
23461
  outcome: "error",
22937
23462
  error,
23463
+ data: buildMutationCaptureTraceData(mutationCaptureDiagnostics),
22938
23464
  context: buildRuntimeTraceContext({
22939
23465
  sessionRef: this.sessionRef,
22940
23466
  pageRef
@@ -23034,22 +23560,25 @@ var OpensteerSessionRuntime = class {
23034
23560
  },
23035
23561
  options
23036
23562
  );
23563
+ const context = buildRuntimeTraceContext({
23564
+ sessionRef: this.sessionRef,
23565
+ pageRef
23566
+ });
23567
+ const events = await this.drainPendingEngineEvents(context);
23037
23568
  await this.appendTrace({
23038
23569
  operation: "page.snapshot",
23039
23570
  startedAt,
23040
23571
  completedAt: Date.now(),
23041
23572
  outcome: "ok",
23042
23573
  artifacts,
23574
+ ...events === void 0 ? {} : { events },
23043
23575
  data: {
23044
23576
  mode,
23045
23577
  url: output.url,
23046
23578
  title: output.title,
23047
23579
  counterCount: output.counters.length
23048
23580
  },
23049
- context: buildRuntimeTraceContext({
23050
- sessionRef: this.sessionRef,
23051
- pageRef
23052
- })
23581
+ context
23053
23582
  });
23054
23583
  return output;
23055
23584
  } catch (error) {
@@ -23374,17 +23903,29 @@ var OpensteerSessionRuntime = class {
23374
23903
  "network.clear",
23375
23904
  async (timeout) => {
23376
23905
  if (this.sessionRef !== void 0) {
23377
- const liveRequestIds = await this.readLiveRequestIds(timeout, {
23378
- includeCurrentPageOnly: false
23379
- });
23380
- if (input.tag === void 0) {
23906
+ if (input.capture !== void 0 || input.tag !== void 0) {
23907
+ const records = await this.queryLiveNetwork(
23908
+ {
23909
+ ...input.capture === void 0 ? {} : { capture: input.capture },
23910
+ ...input.tag === void 0 ? {} : { tag: input.tag }
23911
+ },
23912
+ timeout,
23913
+ {
23914
+ ignoreLimit: true
23915
+ }
23916
+ );
23917
+ this.networkHistory.tombstoneRequestIds(
23918
+ records.map((record) => record.record.requestId)
23919
+ );
23920
+ } else {
23921
+ const liveRequestIds = await this.readLiveRequestIds(timeout, {
23922
+ includeCurrentPageOnly: false
23923
+ });
23381
23924
  this.networkHistory.tombstoneRequestIds(liveRequestIds);
23382
23925
  }
23383
23926
  }
23384
- if (input.tag === void 0) {
23927
+ if (input.capture === void 0 && input.tag === void 0) {
23385
23928
  this.networkHistory.tombstoneRequestIds(this.networkHistory.getKnownRequestIds());
23386
- } else {
23387
- this.networkHistory.clearTag(input.tag);
23388
23929
  }
23389
23930
  return {
23390
23931
  clearedCount: await timeout.runStep(() => root.registry.savedNetwork.clear(input))
@@ -23398,6 +23939,7 @@ var OpensteerSessionRuntime = class {
23398
23939
  completedAt: Date.now(),
23399
23940
  outcome: "ok",
23400
23941
  data: {
23942
+ ...input.capture === void 0 ? {} : { capture: input.capture },
23401
23943
  ...input.tag === void 0 ? {} : { tag: input.tag },
23402
23944
  clearedCount: output.clearedCount
23403
23945
  }
@@ -24579,7 +25121,9 @@ var OpensteerSessionRuntime = class {
24579
25121
  };
24580
25122
  }
24581
25123
  const bindings = /* @__PURE__ */ new Map();
24582
- const baselineRequestIds = await this.beginMutationCapture(timeout);
25124
+ const baselineRequestIds = await this.readLiveRequestIds(timeout, {
25125
+ includeCurrentPageOnly: true
25126
+ });
24583
25127
  const pageRef = explicitPageRef ?? await this.ensurePageRef();
24584
25128
  const validatorMap = new Map(
24585
25129
  packageRecord.payload.validators.map((validator) => [validator.id, validator])
@@ -26251,33 +26795,38 @@ var OpensteerSessionRuntime = class {
26251
26795
  assertValidSemanticOperationInput("computer.execute", input);
26252
26796
  const pageRef = await this.ensurePageRef();
26253
26797
  const startedAt = Date.now();
26798
+ let mutationCaptureDiagnostics;
26799
+ let boundaryDiagnostics;
26254
26800
  try {
26255
- const { artifacts, output } = await this.runWithOperationTimeout(
26801
+ const { artifacts, output } = await this.runMutationCapturedOperation(
26256
26802
  "computer.execute",
26803
+ {
26804
+ ...input.captureNetwork === void 0 ? {} : { captureNetwork: input.captureNetwork },
26805
+ options
26806
+ },
26257
26807
  async (timeout) => {
26258
- const baselineRequestIds = await this.beginMutationCapture(timeout);
26259
26808
  try {
26260
26809
  const output2 = await this.requireComputer().execute({
26261
26810
  pageRef,
26262
26811
  input,
26263
26812
  timeout
26264
26813
  });
26814
+ boundaryDiagnostics = takeActionBoundaryDiagnostics(timeout.signal);
26265
26815
  timeout.throwIfAborted();
26266
26816
  this.pageRef = output2.pageRef;
26267
- await this.completeMutationCapture(timeout, baselineRequestIds, input.networkTag);
26268
26817
  const artifacts2 = await this.persistComputerArtifacts(output2, timeout);
26269
26818
  return {
26270
26819
  artifacts: { manifests: artifacts2.manifests },
26271
26820
  output: artifacts2.output
26272
26821
  };
26273
26822
  } catch (error) {
26274
- await this.completeMutationCapture(timeout, baselineRequestIds, input.networkTag).catch(
26275
- () => void 0
26276
- );
26823
+ boundaryDiagnostics ??= takeActionBoundaryDiagnostics(timeout.signal);
26277
26824
  throw error;
26278
26825
  }
26279
26826
  },
26280
- options
26827
+ (diagnostics) => {
26828
+ mutationCaptureDiagnostics = diagnostics;
26829
+ }
26281
26830
  );
26282
26831
  await this.appendTrace({
26283
26832
  operation: "computer.execute",
@@ -26293,6 +26842,8 @@ var OpensteerSessionRuntime = class {
26293
26842
  nativeViewport: output.nativeViewport,
26294
26843
  displayScale: output.displayScale,
26295
26844
  timing: output.timing,
26845
+ ...boundaryDiagnostics === void 0 ? {} : { settle: boundaryDiagnostics },
26846
+ ...buildMutationCaptureTraceData(mutationCaptureDiagnostics),
26296
26847
  ...output.trace === void 0 ? {} : { trace: output.trace }
26297
26848
  },
26298
26849
  context: buildRuntimeTraceContext({
@@ -26311,6 +26862,10 @@ var OpensteerSessionRuntime = class {
26311
26862
  completedAt: Date.now(),
26312
26863
  outcome: "error",
26313
26864
  error,
26865
+ data: {
26866
+ ...boundaryDiagnostics === void 0 ? {} : { settle: boundaryDiagnostics },
26867
+ ...buildMutationCaptureTraceData(mutationCaptureDiagnostics)
26868
+ },
26314
26869
  context: buildRuntimeTraceContext({
26315
26870
  sessionRef: this.sessionRef,
26316
26871
  pageRef: this.pageRef
@@ -26414,44 +26969,53 @@ var OpensteerSessionRuntime = class {
26414
26969
  async runDomAction(operation, input, executor, options = {}) {
26415
26970
  const pageRef = await this.ensurePageRef();
26416
26971
  const startedAt = Date.now();
26972
+ let mutationCaptureDiagnostics;
26973
+ let boundaryDiagnostics;
26417
26974
  try {
26418
- const { executed, preparedTarget } = await this.runWithOperationTimeout(
26975
+ const { executed, preparedTarget } = await this.runMutationCapturedOperation(
26419
26976
  operation,
26977
+ {
26978
+ ...input.captureNetwork === void 0 ? {} : { captureNetwork: input.captureNetwork },
26979
+ options
26980
+ },
26420
26981
  async (timeout) => {
26421
- const baselineRequestIds = await this.beginMutationCapture(timeout);
26982
+ const preparedTarget2 = await this.prepareDomTarget(
26983
+ pageRef,
26984
+ operation,
26985
+ input.target,
26986
+ input.persistAsDescription,
26987
+ timeout
26988
+ );
26422
26989
  try {
26423
- const preparedTarget2 = await this.prepareDomTarget(
26424
- pageRef,
26425
- operation,
26426
- input.target,
26427
- input.persistAsDescription,
26428
- timeout
26429
- );
26430
26990
  const executed2 = await executor(pageRef, preparedTarget2.target, timeout);
26431
- await this.completeMutationCapture(timeout, baselineRequestIds, input.networkTag);
26991
+ boundaryDiagnostics = takeActionBoundaryDiagnostics(timeout.signal);
26432
26992
  return {
26433
26993
  executed: executed2,
26434
26994
  preparedTarget: preparedTarget2
26435
26995
  };
26436
26996
  } catch (error) {
26437
- await this.completeMutationCapture(timeout, baselineRequestIds, input.networkTag).catch(
26438
- () => void 0
26439
- );
26997
+ boundaryDiagnostics ??= takeActionBoundaryDiagnostics(timeout.signal);
26440
26998
  throw error;
26441
26999
  }
26442
27000
  },
26443
- options
27001
+ (diagnostics) => {
27002
+ mutationCaptureDiagnostics = diagnostics;
27003
+ }
26444
27004
  );
26445
27005
  const output = toOpensteerActionResult(executed.result, preparedTarget.persistedDescription);
27006
+ const actionEvents = "events" in executed.result ? executed.result.events : void 0;
26446
27007
  await this.appendTrace({
26447
27008
  operation,
26448
27009
  startedAt,
26449
27010
  completedAt: Date.now(),
26450
27011
  outcome: "ok",
27012
+ ...actionEvents === void 0 ? {} : { events: actionEvents },
26451
27013
  data: {
26452
27014
  target: output.target,
26453
27015
  ...output.point === void 0 ? {} : { point: output.point },
26454
- ...output.persistedDescription === void 0 ? {} : { persistedDescription: output.persistedDescription }
27016
+ ...output.persistedDescription === void 0 ? {} : { persistedDescription: output.persistedDescription },
27017
+ ...boundaryDiagnostics === void 0 ? {} : { settle: boundaryDiagnostics },
27018
+ ...buildMutationCaptureTraceData(mutationCaptureDiagnostics)
26455
27019
  },
26456
27020
  context: buildRuntimeTraceContext({
26457
27021
  sessionRef: this.sessionRef,
@@ -26469,6 +27033,10 @@ var OpensteerSessionRuntime = class {
26469
27033
  completedAt: Date.now(),
26470
27034
  outcome: "error",
26471
27035
  error,
27036
+ data: {
27037
+ ...boundaryDiagnostics === void 0 ? {} : { settle: boundaryDiagnostics },
27038
+ ...buildMutationCaptureTraceData(mutationCaptureDiagnostics)
27039
+ },
26472
27040
  context: buildRuntimeTraceContext({
26473
27041
  sessionRef: this.sessionRef,
26474
27042
  pageRef
@@ -26578,7 +27146,7 @@ var OpensteerSessionRuntime = class {
26578
27146
  const filtered = filterNetworkQueryRecords(metadataRecords, {
26579
27147
  ...input.recordId === void 0 ? {} : { recordId: input.recordId },
26580
27148
  ...input.requestId === void 0 ? {} : { requestId: input.requestId },
26581
- ...input.actionId === void 0 ? {} : { actionId: input.actionId },
27149
+ ...input.capture === void 0 ? {} : { capture: input.capture },
26582
27150
  ...input.tag === void 0 ? {} : { tag: input.tag },
26583
27151
  ...input.url === void 0 ? {} : { url: input.url },
26584
27152
  ...input.hostname === void 0 ? {} : { hostname: input.hostname },
@@ -26784,32 +27352,68 @@ var OpensteerSessionRuntime = class {
26784
27352
  artifactId: manifest.artifactId
26785
27353
  };
26786
27354
  }
26787
- beginMutationCapture(timeout) {
26788
- return this.readLiveRequestIds(timeout, {
26789
- includeCurrentPageOnly: true
26790
- });
26791
- }
26792
- async completeMutationCapture(timeout, baselineRequestIds, networkTag) {
26793
- const records = await timeout.runStep(
26794
- () => this.readLiveNetworkRecords(
26795
- {
26796
- includeBodies: false,
26797
- includeCurrentPageOnly: true
27355
+ async runMutationCapturedOperation(operation, input, execute, onFinalized) {
27356
+ let plan;
27357
+ try {
27358
+ const result = await this.runWithOperationTimeout(
27359
+ operation,
27360
+ async (timeout) => {
27361
+ plan = await this.beginMutationCapture(timeout, input.captureNetwork);
27362
+ return execute(timeout);
26798
27363
  },
26799
- timeout.signal
26800
- )
27364
+ input.options
27365
+ );
27366
+ const diagnostics = await this.finalizeMutationCaptureBestEffort(plan);
27367
+ onFinalized?.(diagnostics);
27368
+ return result;
27369
+ } catch (error) {
27370
+ const diagnostics = await this.finalizeMutationCaptureBestEffort(plan);
27371
+ onFinalized?.(diagnostics);
27372
+ throw error;
27373
+ }
27374
+ }
27375
+ async beginMutationCapture(timeout, capture) {
27376
+ if (capture === void 0) {
27377
+ return void 0;
27378
+ }
27379
+ return {
27380
+ baselineRequestIds: await this.readLiveRequestIds(timeout, {
27381
+ includeCurrentPageOnly: true
27382
+ }),
27383
+ capture
27384
+ };
27385
+ }
27386
+ async finalizeMutationCaptureBestEffort(plan) {
27387
+ if (plan === void 0) {
27388
+ return {};
27389
+ }
27390
+ try {
27391
+ await withDetachedTimeoutSignal(MUTATION_CAPTURE_FINALIZE_TIMEOUT_MS, async (signal) => {
27392
+ await this.completeMutationCaptureWithSignal(signal, plan);
27393
+ });
27394
+ return {};
27395
+ } catch (error) {
27396
+ return {
27397
+ finalizeError: normalizeOpensteerError(error)
27398
+ };
27399
+ }
27400
+ }
27401
+ async completeMutationCaptureWithSignal(signal, plan) {
27402
+ const records = await this.readLiveNetworkRecords(
27403
+ {
27404
+ includeBodies: false,
27405
+ includeCurrentPageOnly: true
27406
+ },
27407
+ signal
26801
27408
  );
26802
- const delta = records.filter((record) => !baselineRequestIds.has(record.record.requestId));
27409
+ const delta = records.filter((record) => !plan.baselineRequestIds.has(record.record.requestId));
26803
27410
  if (delta.length === 0) {
26804
27411
  return;
26805
27412
  }
26806
- this.networkHistory.assignActionId(delta, `action:${randomUUID()}`);
26807
- if (networkTag !== void 0) {
26808
- this.networkHistory.addTag(delta, networkTag);
26809
- }
26810
- await this.persistLiveRequestIds(
27413
+ this.networkHistory.assignCapture(delta, plan.capture);
27414
+ await this.persistLiveRequestIdsWithSignal(
26811
27415
  delta.map((record) => record.record.requestId),
26812
- timeout,
27416
+ signal,
26813
27417
  {
26814
27418
  includeCurrentPageOnly: true
26815
27419
  }
@@ -27216,27 +27820,28 @@ var OpensteerSessionRuntime = class {
27216
27820
  });
27217
27821
  }
27218
27822
  async persistLiveRequestIds(requestIds, timeout, options) {
27823
+ return timeout.runStep(
27824
+ () => this.persistLiveRequestIdsWithSignal(requestIds, timeout.signal, options)
27825
+ );
27826
+ }
27827
+ async persistLiveRequestIdsWithSignal(requestIds, signal, options) {
27219
27828
  if (requestIds.length === 0) {
27220
27829
  return [];
27221
27830
  }
27222
27831
  const root = await this.ensureRoot();
27223
- const browserRecords = await timeout.runStep(
27224
- () => this.readBrowserNetworkRecords(
27225
- {
27226
- includeBodies: true,
27227
- includeCurrentPageOnly: options.includeCurrentPageOnly,
27228
- ...options.pageRef === void 0 ? {} : { pageRef: options.pageRef },
27229
- requestIds
27230
- },
27231
- timeout.signal
27232
- )
27233
- );
27234
- return timeout.runStep(
27235
- () => this.networkHistory.persist(browserRecords, root.registry.savedNetwork, {
27236
- bodyWriteMode: "authoritative",
27237
- redactSecretHeaders: false
27238
- })
27832
+ const browserRecords = await this.readBrowserNetworkRecords(
27833
+ {
27834
+ includeBodies: true,
27835
+ includeCurrentPageOnly: options.includeCurrentPageOnly,
27836
+ ...options.pageRef === void 0 ? {} : { pageRef: options.pageRef },
27837
+ requestIds
27838
+ },
27839
+ signal
27239
27840
  );
27841
+ return this.networkHistory.persist(browserRecords, root.registry.savedNetwork, {
27842
+ bodyWriteMode: "authoritative",
27843
+ redactSecretHeaders: false
27844
+ });
27240
27845
  }
27241
27846
  async syncPersistedNetworkSelection(timeout, input, options) {
27242
27847
  if (this.sessionRef === void 0) {
@@ -27278,7 +27883,7 @@ var OpensteerSessionRuntime = class {
27278
27883
  ...input.pageRef === void 0 ? {} : { pageRef: input.pageRef },
27279
27884
  ...input.recordId === void 0 ? {} : { recordId: input.recordId },
27280
27885
  ...input.requestId === void 0 ? {} : { requestId: input.requestId },
27281
- ...input.actionId === void 0 ? {} : { actionId: input.actionId },
27886
+ ...input.capture === void 0 ? {} : { capture: input.capture },
27282
27887
  ...input.tag === void 0 ? {} : { tag: input.tag },
27283
27888
  ...input.url === void 0 ? {} : { url: input.url },
27284
27889
  ...input.hostname === void 0 ? {} : { hostname: input.hostname },
@@ -27295,7 +27900,7 @@ var OpensteerSessionRuntime = class {
27295
27900
  ...input.pageRef === void 0 ? {} : { pageRef: input.pageRef },
27296
27901
  ...input.recordId === void 0 ? {} : { recordId: input.recordId },
27297
27902
  ...input.requestId === void 0 ? {} : { requestId: input.requestId },
27298
- ...input.actionId === void 0 ? {} : { actionId: input.actionId },
27903
+ ...input.capture === void 0 ? {} : { capture: input.capture },
27299
27904
  ...input.url === void 0 ? {} : { url: input.url },
27300
27905
  ...input.hostname === void 0 ? {} : { hostname: input.hostname },
27301
27906
  ...input.path === void 0 ? {} : { path: input.path },
@@ -28154,7 +28759,7 @@ var OpensteerSessionRuntime = class {
28154
28759
  }
28155
28760
  async executeAuthRecipeHook(step, variables) {
28156
28761
  const resolved = requireForAuthRecipeHook.resolve(step.hook.specifier, {
28157
- paths: [path6.dirname(this.rootPath)]
28762
+ paths: [path7.dirname(this.rootPath)]
28158
28763
  });
28159
28764
  const module = await import(pathToFileURL(resolved).href);
28160
28765
  const handler = module[step.hook.export];
@@ -28526,14 +29131,18 @@ var OpensteerSessionRuntime = class {
28526
29131
  return this.engine;
28527
29132
  }
28528
29133
  if (this.injectedEngine) {
28529
- this.engine = this.injectedEngine;
29134
+ this.engine = this.wrapEngineWithObservationCapture(
29135
+ this.injectedEngine
29136
+ );
28530
29137
  this.ownsEngine = false;
28531
29138
  return this.engine;
28532
29139
  }
28533
29140
  if (this.engineFactory === void 0) {
28534
29141
  throw new Error("Opensteer engine factory is not initialized");
28535
29142
  }
28536
- this.engine = await this.engineFactory(overrides);
29143
+ this.engine = this.wrapEngineWithObservationCapture(
29144
+ await this.engineFactory(overrides)
29145
+ );
28537
29146
  this.ownsEngine = true;
28538
29147
  return this.engine;
28539
29148
  }
@@ -28733,6 +29342,15 @@ var OpensteerSessionRuntime = class {
28733
29342
  return;
28734
29343
  }
28735
29344
  const root = await this.ensureRoot();
29345
+ const capturedStepEvents = input.events ?? this.consumePendingOperationEventCapture(
29346
+ input.operation,
29347
+ input.startedAt,
29348
+ input.completedAt
29349
+ );
29350
+ const drainedStepEvents = input.events === void 0 ? await this.drainPendingEngineEvents(input.context) : void 0;
29351
+ const stepEvents = mergeObservedStepEvents(capturedStepEvents, drainedStepEvents);
29352
+ const normalizedData = input.data === void 0 ? void 0 : toCanonicalJsonValue(input.data);
29353
+ const normalizedError = input.error === void 0 ? void 0 : normalizeOpensteerError(input.error);
28736
29354
  const artifacts = input.artifacts === void 0 ? void 0 : await Promise.all(
28737
29355
  input.artifacts.manifests.map(async (manifest) => {
28738
29356
  const reference = await root.artifacts.toProtocolArtifactReference(
@@ -28745,19 +29363,56 @@ var OpensteerSessionRuntime = class {
28745
29363
  return reference;
28746
29364
  })
28747
29365
  );
28748
- await root.traces.append(runId, {
29366
+ const traceEntry = await root.traces.append(runId, {
28749
29367
  operation: input.operation,
28750
29368
  outcome: input.outcome,
28751
29369
  startedAt: input.startedAt,
28752
29370
  completedAt: input.completedAt,
28753
29371
  ...input.context === void 0 ? {} : { context: input.context },
28754
- ...input.events === void 0 ? {} : { events: input.events },
29372
+ ...stepEvents === void 0 ? {} : { events: stepEvents },
28755
29373
  ...artifacts === void 0 ? {} : { artifacts },
28756
- ...input.data === void 0 ? {} : { data: toCanonicalJsonValue(input.data) },
28757
- ...input.error === void 0 ? {} : {
28758
- error: normalizeOpensteerError(input.error)
29374
+ ...normalizedData === void 0 ? {} : { data: normalizedData },
29375
+ ...normalizedError === void 0 ? {} : {
29376
+ error: normalizedError
28759
29377
  }
28760
29378
  });
29379
+ const observationSession = await this.ensureObservationSession().catch(() => void 0);
29380
+ if (observationSession === void 0 || this.observationConfig.profile === "off") {
29381
+ return;
29382
+ }
29383
+ const observationArtifactIds = input.artifacts === void 0 ? void 0 : (await Promise.allSettled(
29384
+ input.artifacts.manifests.map(async (manifest) => {
29385
+ const artifact = await observationSession.writeArtifact({
29386
+ artifactId: manifest.artifactId,
29387
+ kind: observationArtifactKindFromManifest(manifest.kind),
29388
+ createdAt: manifest.createdAt,
29389
+ context: manifest.scope,
29390
+ mediaType: manifest.mediaType,
29391
+ byteLength: manifest.byteLength,
29392
+ sha256: manifest.sha256,
29393
+ opensteerArtifactId: manifest.artifactId,
29394
+ storageKey: manifestToExternalBinaryLocation(root.rootPath, manifest).uri
29395
+ });
29396
+ return artifact.artifactId;
29397
+ })
29398
+ )).flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
29399
+ const observationEvents = buildObservationEventsFromTrace({
29400
+ traceId: traceEntry.traceId,
29401
+ stepId: traceEntry.stepId,
29402
+ operation: input.operation,
29403
+ outcome: input.outcome,
29404
+ startedAt: input.startedAt,
29405
+ completedAt: input.completedAt,
29406
+ ...input.context === void 0 ? {} : { context: input.context },
29407
+ ...stepEvents === void 0 ? {} : { events: stepEvents },
29408
+ ...normalizedData === void 0 ? {} : { data: normalizedData },
29409
+ ...normalizedError === void 0 ? {} : { error: normalizedError },
29410
+ ...observationArtifactIds === void 0 ? {} : { artifactIds: observationArtifactIds },
29411
+ profile: this.observationConfig.profile
29412
+ });
29413
+ if (observationEvents.length > 0) {
29414
+ await observationSession.appendBatch(observationEvents).catch(() => void 0);
29415
+ }
28761
29416
  }
28762
29417
  async cleanupSessionResources(engine, pageRef, sessionRef) {
28763
29418
  if (pageRef !== void 0) {
@@ -28769,6 +29424,7 @@ var OpensteerSessionRuntime = class {
28769
29424
  }
28770
29425
  async resetRuntimeState(options) {
28771
29426
  const engine = this.engine;
29427
+ const observations = this.observations;
28772
29428
  this.networkHistory.clear();
28773
29429
  this.sessionRef = void 0;
28774
29430
  this.pageRef = void 0;
@@ -28777,20 +29433,140 @@ var OpensteerSessionRuntime = class {
28777
29433
  this.computer = void 0;
28778
29434
  this.extractionDescriptors = void 0;
28779
29435
  this.engine = void 0;
29436
+ this.observations = void 0;
29437
+ this.pendingOperationEventCaptures.length = 0;
29438
+ await observations?.close("runtime_reset").catch(() => void 0);
28780
29439
  if (options.disposeEngine && this.ownsEngine && engine?.dispose) {
28781
29440
  await engine.dispose();
28782
29441
  }
28783
29442
  this.ownsEngine = false;
28784
29443
  }
29444
+ async ensureObservationSession() {
29445
+ if (this.observationConfig.profile === "off") {
29446
+ return void 0;
29447
+ }
29448
+ if (this.observations !== void 0) {
29449
+ return this.observations;
29450
+ }
29451
+ const observationSessionId = this.resolveObservationSessionId();
29452
+ if (observationSessionId === void 0) {
29453
+ return void 0;
29454
+ }
29455
+ const sink = this.injectedObservationSink ?? (await this.ensureRoot()).observations;
29456
+ this.observations = await sink.openSession({
29457
+ sessionId: observationSessionId,
29458
+ openedAt: Date.now(),
29459
+ config: this.observationConfig
29460
+ });
29461
+ return this.observations;
29462
+ }
29463
+ resolveObservationSessionId() {
29464
+ return this.observationSessionId ?? this.sessionRef;
29465
+ }
28785
29466
  runWithOperationTimeout(operation, callback, options = {}) {
28786
- return runWithPolicyTimeout(
28787
- this.policy.timeout,
28788
- {
28789
- operation,
28790
- ...options.signal === void 0 ? {} : { signal: options.signal }
28791
- },
28792
- callback
28793
- );
29467
+ const existingCollector = this.operationEventStorage.getStore();
29468
+ if (existingCollector !== void 0) {
29469
+ return runWithPolicyTimeout(
29470
+ this.policy.timeout,
29471
+ {
29472
+ operation,
29473
+ ...options.signal === void 0 ? {} : { signal: options.signal }
29474
+ },
29475
+ callback
29476
+ );
29477
+ }
29478
+ const collector = [];
29479
+ const startedAt = Date.now();
29480
+ return this.operationEventStorage.run(collector, async () => {
29481
+ try {
29482
+ return await runWithPolicyTimeout(
29483
+ this.policy.timeout,
29484
+ {
29485
+ operation,
29486
+ ...options.signal === void 0 ? {} : { signal: options.signal }
29487
+ },
29488
+ callback
29489
+ );
29490
+ } finally {
29491
+ this.recordPendingOperationEventCapture({
29492
+ operation,
29493
+ startedAt,
29494
+ completedAt: Date.now(),
29495
+ events: collector
29496
+ });
29497
+ }
29498
+ });
29499
+ }
29500
+ wrapEngineWithObservationCapture(engine) {
29501
+ return new Proxy(engine, {
29502
+ get: (target, property, receiver) => {
29503
+ const value = Reflect.get(target, property, receiver);
29504
+ if (typeof value !== "function") {
29505
+ return value;
29506
+ }
29507
+ return (...args) => {
29508
+ const result = Reflect.apply(value, target, args);
29509
+ if (!(result instanceof Promise)) {
29510
+ return result;
29511
+ }
29512
+ return result.then((resolved) => {
29513
+ this.captureObservedStepEvents(resolved);
29514
+ return resolved;
29515
+ });
29516
+ };
29517
+ }
29518
+ });
29519
+ }
29520
+ captureObservedStepEvents(value) {
29521
+ const collector = this.operationEventStorage.getStore();
29522
+ if (collector === void 0) {
29523
+ return;
29524
+ }
29525
+ const events = readStepResultEvents(value);
29526
+ if (events === void 0 || events.length === 0) {
29527
+ return;
29528
+ }
29529
+ collector.push(...events);
29530
+ }
29531
+ recordPendingOperationEventCapture(capture) {
29532
+ if (capture.events.length === 0) {
29533
+ return;
29534
+ }
29535
+ this.pendingOperationEventCaptures.push({
29536
+ ...capture,
29537
+ events: [...capture.events]
29538
+ });
29539
+ if (this.pendingOperationEventCaptures.length > PENDING_OPERATION_EVENT_CAPTURE_LIMIT) {
29540
+ this.pendingOperationEventCaptures.splice(
29541
+ 0,
29542
+ this.pendingOperationEventCaptures.length - PENDING_OPERATION_EVENT_CAPTURE_LIMIT
29543
+ );
29544
+ }
29545
+ }
29546
+ consumePendingOperationEventCapture(operation, startedAt, completedAt) {
29547
+ for (let index = this.pendingOperationEventCaptures.length - 1; index >= 0; index -= 1) {
29548
+ const capture = this.pendingOperationEventCaptures[index];
29549
+ if (capture === void 0) {
29550
+ continue;
29551
+ }
29552
+ if (capture.operation !== operation) {
29553
+ continue;
29554
+ }
29555
+ if (capture.startedAt < startedAt - PENDING_OPERATION_EVENT_CAPTURE_SKEW_MS || capture.completedAt > completedAt + PENDING_OPERATION_EVENT_CAPTURE_SKEW_MS) {
29556
+ continue;
29557
+ }
29558
+ this.pendingOperationEventCaptures.splice(index, 1);
29559
+ return capture.events;
29560
+ }
29561
+ return void 0;
29562
+ }
29563
+ async drainPendingEngineEvents(context) {
29564
+ const pageRef = context?.pageRef ?? this.pageRef;
29565
+ if (pageRef === void 0 || this.engine === void 0) {
29566
+ return void 0;
29567
+ }
29568
+ const events = await this.engine.drainEvents({ pageRef }).catch(() => []);
29569
+ return events.length > 0 ? events : void 0;
28794
29570
  }
28795
29571
  async navigatePage(input, timeout) {
28796
29572
  const remainingMs = timeout.remainingMs();
@@ -28826,6 +29602,34 @@ function buildRuntimeTraceContext(input) {
28826
29602
  function buildArtifactScope(input) {
28827
29603
  return buildRuntimeTraceContext(input);
28828
29604
  }
29605
+ function readStepResultEvents(value) {
29606
+ if (value === null || typeof value !== "object") {
29607
+ return void 0;
29608
+ }
29609
+ if (!("events" in value)) {
29610
+ return void 0;
29611
+ }
29612
+ const events = value.events;
29613
+ return Array.isArray(events) ? events : void 0;
29614
+ }
29615
+ function mergeObservedStepEvents(primary, secondary) {
29616
+ if (primary === void 0 || primary.length === 0) {
29617
+ return secondary === void 0 || secondary.length === 0 ? void 0 : secondary;
29618
+ }
29619
+ if (secondary === void 0 || secondary.length === 0) {
29620
+ return primary;
29621
+ }
29622
+ const merged = /* @__PURE__ */ new Map();
29623
+ for (const event of primary) {
29624
+ merged.set(event.eventId, event);
29625
+ }
29626
+ for (const event of secondary) {
29627
+ merged.set(event.eventId, event);
29628
+ }
29629
+ return [...merged.values()].sort(
29630
+ (left, right) => (left.timestamp ?? 0) - (right.timestamp ?? 0)
29631
+ );
29632
+ }
28829
29633
  function selectLiveQueryPageRef(input, currentPageRef) {
28830
29634
  if (input.pageRef !== void 0) {
28831
29635
  return input.pageRef;
@@ -28857,8 +29661,8 @@ function resolveLiveQueryRequestIds(input, history) {
28857
29661
  if (input.requestId !== void 0) {
28858
29662
  requestIdCandidates.push(/* @__PURE__ */ new Set([input.requestId]));
28859
29663
  }
28860
- if (input.actionId !== void 0) {
28861
- requestIdCandidates.push(history.getRequestIdsForActionId(input.actionId));
29664
+ if (input.capture !== void 0) {
29665
+ requestIdCandidates.push(history.getRequestIdsForCapture(input.capture));
28862
29666
  }
28863
29667
  if (input.tag !== void 0) {
28864
29668
  requestIdCandidates.push(history.getRequestIdsForTag(input.tag));
@@ -28905,7 +29709,7 @@ function filterNetworkQueryRecords(records, input) {
28905
29709
  if (input.requestId !== void 0 && record.record.requestId !== input.requestId) {
28906
29710
  return false;
28907
29711
  }
28908
- if (input.actionId !== void 0 && record.actionId !== input.actionId) {
29712
+ if (input.capture !== void 0 && record.capture !== input.capture) {
28909
29713
  return false;
28910
29714
  }
28911
29715
  if (input.tag !== void 0 && !(record.tags ?? []).includes(input.tag)) {
@@ -29788,12 +30592,12 @@ function extractReverseRuntimeValue(value, pointer) {
29788
30592
  }
29789
30593
  return readDotPath(value, pointer);
29790
30594
  }
29791
- function readDotPath(value, path13) {
29792
- if (path13.length === 0) {
30595
+ function readDotPath(value, path15) {
30596
+ if (path15.length === 0) {
29793
30597
  return value;
29794
30598
  }
29795
30599
  let current = value;
29796
- for (const segment of path13.split(".").filter((entry) => entry.length > 0)) {
30600
+ for (const segment of path15.split(".").filter((entry) => entry.length > 0)) {
29797
30601
  if (current === null || current === void 0) {
29798
30602
  return void 0;
29799
30603
  }
@@ -30162,7 +30966,7 @@ function normalizeRuntimeErrorMessage(error) {
30162
30966
  return error instanceof Error ? error.message : String(error);
30163
30967
  }
30164
30968
  function runtimeDelay(ms) {
30165
- return new Promise((resolve5) => setTimeout(resolve5, ms));
30969
+ return new Promise((resolve4) => setTimeout(resolve4, ms));
30166
30970
  }
30167
30971
  function applyBrowserCookiesToTransportRequest(request, cookies) {
30168
30972
  if (cookies.length === 0) {
@@ -30266,7 +31070,7 @@ function parseSetCookieHeader(value, requestUrl) {
30266
31070
  }
30267
31071
  const url = new URL(requestUrl);
30268
31072
  let domain = url.hostname;
30269
- let path13 = defaultCookiePath(url.pathname);
31073
+ let path15 = defaultCookiePath(url.pathname);
30270
31074
  let secure = url.protocol === "https:";
30271
31075
  let expiresAt;
30272
31076
  const cookieValue = rawValueParts.join("=").trim();
@@ -30279,7 +31083,7 @@ function parseSetCookieHeader(value, requestUrl) {
30279
31083
  continue;
30280
31084
  }
30281
31085
  if (key === "path" && attributeValue.length > 0) {
30282
- path13 = attributeValue;
31086
+ path15 = attributeValue;
30283
31087
  continue;
30284
31088
  }
30285
31089
  if (key === "secure") {
@@ -30305,7 +31109,7 @@ function parseSetCookieHeader(value, requestUrl) {
30305
31109
  name,
30306
31110
  value: cookieValue,
30307
31111
  domain,
30308
- path: path13,
31112
+ path: path15,
30309
31113
  secure,
30310
31114
  ...expiresAt === void 0 ? {} : { expiresAt }
30311
31115
  }
@@ -31257,7 +32061,7 @@ async function pollUntilResult(timeout, producer) {
31257
32061
  if (produced !== void 0) {
31258
32062
  return produced;
31259
32063
  }
31260
- await new Promise((resolve5) => setTimeout(resolve5, 100));
32064
+ await new Promise((resolve4) => setTimeout(resolve4, 100));
31261
32065
  }
31262
32066
  }
31263
32067
  async function getMainFrame(engine, pageRef) {
@@ -31315,9 +32119,167 @@ function toOpensteerResolvedTarget2(target) {
31315
32119
  function normalizeOpensteerError(error) {
31316
32120
  return normalizeThrownOpensteerError(error, "Unknown Opensteer runtime failure");
31317
32121
  }
32122
+ function observationArtifactKindFromManifest(kind) {
32123
+ switch (kind) {
32124
+ case "screenshot":
32125
+ return "screenshot";
32126
+ case "dom-snapshot":
32127
+ return "dom-snapshot";
32128
+ case "html-snapshot":
32129
+ return "html-snapshot";
32130
+ default:
32131
+ return "other";
32132
+ }
32133
+ }
32134
+ function buildObservationEventsFromTrace(input) {
32135
+ const context = normalizeObservationContext(input.context);
32136
+ const baseCorrelationId = input.traceId;
32137
+ const startedEvent = {
32138
+ kind: input.operation === "session.open" || input.operation === "session.close" ? "session" : "operation",
32139
+ phase: "started",
32140
+ createdAt: input.startedAt,
32141
+ correlationId: baseCorrelationId,
32142
+ spanId: input.stepId,
32143
+ ...context === void 0 ? {} : { context },
32144
+ data: {
32145
+ operation: input.operation
32146
+ }
32147
+ };
32148
+ const stepEvents = (input.events ?? []).filter((event) => shouldCaptureObservationStepEvent(event, input.profile)).map((event) => {
32149
+ const eventContext = buildObservationContextFromEvent(event);
32150
+ return {
32151
+ kind: observationKindForStepEvent(event),
32152
+ phase: "occurred",
32153
+ createdAt: event.timestamp,
32154
+ correlationId: baseCorrelationId,
32155
+ parentSpanId: input.stepId,
32156
+ ...eventContext === void 0 ? {} : { context: eventContext },
32157
+ data: stripObservationStepEvent(event),
32158
+ ...event.kind === "page-error" ? {
32159
+ error: {
32160
+ message: event.message,
32161
+ ...event.stack === void 0 ? {} : { details: { stack: event.stack } }
32162
+ }
32163
+ } : {}
32164
+ };
32165
+ });
32166
+ const completedEvent = {
32167
+ kind: input.operation === "session.open" || input.operation === "session.close" ? "session" : "operation",
32168
+ phase: input.outcome === "ok" ? "completed" : "failed",
32169
+ createdAt: input.completedAt,
32170
+ correlationId: baseCorrelationId,
32171
+ spanId: input.stepId,
32172
+ ...context === void 0 ? {} : { context },
32173
+ data: {
32174
+ operation: input.operation,
32175
+ startedAt: input.startedAt,
32176
+ completedAt: input.completedAt,
32177
+ durationMs: input.completedAt - input.startedAt,
32178
+ ...input.data === void 0 ? {} : { output: input.data }
32179
+ },
32180
+ ...input.error === void 0 ? {} : {
32181
+ error: {
32182
+ ...input.error.code === void 0 ? {} : { code: input.error.code },
32183
+ message: input.error.message,
32184
+ ...input.error.retriable === void 0 ? {} : { retriable: input.error.retriable },
32185
+ ...input.error.details === void 0 ? {} : { details: toCanonicalJsonValue(input.error.details) }
32186
+ }
32187
+ },
32188
+ ...input.artifactIds === void 0 || input.artifactIds.length === 0 ? {} : { artifactIds: input.artifactIds }
32189
+ };
32190
+ return [startedEvent, ...stepEvents, completedEvent];
32191
+ }
32192
+ function buildObservationContextFromEvent(event) {
32193
+ return normalizeObservationContext({
32194
+ sessionRef: event.sessionRef,
32195
+ ...event.pageRef === void 0 ? {} : { pageRef: event.pageRef },
32196
+ ...event.frameRef === void 0 ? {} : { frameRef: event.frameRef },
32197
+ ...event.documentRef === void 0 ? {} : { documentRef: event.documentRef },
32198
+ ...event.documentEpoch === void 0 ? {} : { documentEpoch: event.documentEpoch }
32199
+ });
32200
+ }
32201
+ function shouldCaptureObservationStepEvent(event, profile) {
32202
+ if (profile === "diagnostic") {
32203
+ return true;
32204
+ }
32205
+ switch (event.kind) {
32206
+ case "page-created":
32207
+ case "popup-opened":
32208
+ case "page-closed":
32209
+ case "page-error":
32210
+ return true;
32211
+ case "console":
32212
+ return event.level === "warn" || event.level === "error";
32213
+ default:
32214
+ return false;
32215
+ }
32216
+ }
32217
+ function observationKindForStepEvent(event) {
32218
+ switch (event.kind) {
32219
+ case "console":
32220
+ return "console";
32221
+ case "page-error":
32222
+ return "error";
32223
+ case "paused":
32224
+ case "resumed":
32225
+ case "frozen":
32226
+ return "runtime";
32227
+ default:
32228
+ return "page";
32229
+ }
32230
+ }
32231
+ function stripObservationStepEvent(event) {
32232
+ const {
32233
+ eventId: _eventId,
32234
+ kind,
32235
+ timestamp,
32236
+ sessionRef: _sessionRef,
32237
+ pageRef: _pageRef,
32238
+ frameRef: _frameRef,
32239
+ documentRef: _documentRef,
32240
+ documentEpoch: _documentEpoch,
32241
+ ...rest
32242
+ } = event;
32243
+ return toCanonicalJsonValue({
32244
+ eventKind: kind,
32245
+ timestamp,
32246
+ ...rest
32247
+ });
32248
+ }
32249
+ function buildMutationCaptureTraceData(diagnostics) {
32250
+ if (diagnostics?.finalizeError === void 0) {
32251
+ return {};
32252
+ }
32253
+ return {
32254
+ networkCapture: {
32255
+ finalizeError: diagnostics.finalizeError
32256
+ }
32257
+ };
32258
+ }
31318
32259
  function isIgnorableRuntimeBindingError(error) {
31319
32260
  return isBrowserCoreError(error) && (error.code === "not-found" || error.code === "page-closed" || error.code === "session-closed");
31320
32261
  }
32262
+ async function withDetachedTimeoutSignal(timeoutMs, operation) {
32263
+ const controller = new AbortController();
32264
+ const timeoutError = new OpensteerProtocolError(
32265
+ "timeout",
32266
+ `mutation capture finalization exceeded ${String(timeoutMs)}ms timeout`,
32267
+ {
32268
+ details: {
32269
+ policy: "mutation-capture-finalize",
32270
+ budgetMs: timeoutMs
32271
+ }
32272
+ }
32273
+ );
32274
+ const timer = setTimeout(() => {
32275
+ controller.abort(timeoutError);
32276
+ }, timeoutMs);
32277
+ try {
32278
+ return await operation(controller.signal);
32279
+ } finally {
32280
+ clearTimeout(timer);
32281
+ }
32282
+ }
31321
32283
  function screenshotMediaType(format2) {
31322
32284
  switch (format2) {
31323
32285
  case "png":
@@ -31333,8 +32295,8 @@ function screenshotMediaType(format2) {
31333
32295
  var OpensteerRuntime = class extends OpensteerSessionRuntime {
31334
32296
  constructor(options = {}) {
31335
32297
  const publicWorkspace = normalizeWorkspace2(options.workspace);
31336
- const rootPath = options.rootPath ?? (publicWorkspace === void 0 ? path6.resolve(options.rootDir ?? process.cwd(), ".opensteer", "temporary", randomUUID()) : resolveFilesystemWorkspacePath({
31337
- rootDir: path6.resolve(options.rootDir ?? process.cwd()),
32298
+ const rootPath = options.rootPath ?? (publicWorkspace === void 0 ? path7.resolve(options.rootDir ?? process.cwd(), ".opensteer", "temporary", randomUUID()) : resolveFilesystemWorkspacePath({
32299
+ rootDir: path7.resolve(options.rootDir ?? process.cwd()),
31338
32300
  workspace: publicWorkspace
31339
32301
  }));
31340
32302
  const cleanupRootOnClose = options.cleanupRootOnClose ?? publicWorkspace === void 0;
@@ -31359,14 +32321,17 @@ var OpensteerRuntime = class extends OpensteerSessionRuntime {
31359
32321
  ...options.descriptorStore === void 0 ? {} : { descriptorStore: options.descriptorStore },
31360
32322
  ...options.extractionDescriptorStore === void 0 ? {} : { extractionDescriptorStore: options.extractionDescriptorStore },
31361
32323
  ...options.registryOverrides === void 0 ? {} : { registryOverrides: options.registryOverrides },
31362
- cleanupRootOnClose
32324
+ cleanupRootOnClose,
32325
+ ...options.observability === void 0 ? {} : { observability: options.observability },
32326
+ ...options.observationSessionId === void 0 ? {} : { observationSessionId: options.observationSessionId },
32327
+ ...options.observationSink === void 0 ? {} : { observationSink: options.observationSink }
31363
32328
  })
31364
32329
  );
31365
32330
  }
31366
32331
  };
31367
32332
  var OpensteerSessionRuntime2 = class extends OpensteerSessionRuntime {
31368
32333
  constructor(options) {
31369
- const rootPath = options.rootPath ?? path6.resolve(options.rootDir ?? process.cwd());
32334
+ const rootPath = options.rootPath ?? path7.resolve(options.rootDir ?? process.cwd());
31370
32335
  const cleanupRootOnClose = options.cleanupRootOnClose ?? false;
31371
32336
  const engineName = options.engineName ?? DEFAULT_OPENSTEER_ENGINE;
31372
32337
  assertSupportedEngineOptions({
@@ -31388,7 +32353,10 @@ var OpensteerSessionRuntime2 = class extends OpensteerSessionRuntime {
31388
32353
  ...options.descriptorStore === void 0 ? {} : { descriptorStore: options.descriptorStore },
31389
32354
  ...options.extractionDescriptorStore === void 0 ? {} : { extractionDescriptorStore: options.extractionDescriptorStore },
31390
32355
  ...options.registryOverrides === void 0 ? {} : { registryOverrides: options.registryOverrides },
31391
- cleanupRootOnClose
32356
+ cleanupRootOnClose,
32357
+ ...options.observability === void 0 ? {} : { observability: options.observability },
32358
+ ...options.observationSessionId === void 0 ? {} : { observationSessionId: options.observationSessionId },
32359
+ ...options.observationSink === void 0 ? {} : { observationSink: options.observationSink }
31392
32360
  })
31393
32361
  );
31394
32362
  }
@@ -31414,6 +32382,9 @@ function buildSharedRuntimeOptions(input) {
31414
32382
  ...input.extractionDescriptorStore === void 0 ? {} : { extractionDescriptorStore: input.extractionDescriptorStore },
31415
32383
  ...input.registryOverrides === void 0 ? {} : { registryOverrides: input.registryOverrides },
31416
32384
  cleanupRootOnClose: input.cleanupRootOnClose,
32385
+ ...input.observability === void 0 ? {} : { observability: input.observability },
32386
+ ...input.observationSessionId === void 0 ? {} : { observationSessionId: input.observationSessionId },
32387
+ ...input.observationSink === void 0 ? {} : { observationSink: input.observationSink },
31417
32388
  sessionInfo: {
31418
32389
  provider: {
31419
32390
  mode: "local",
@@ -31476,345 +32447,267 @@ function resolveOpensteerProvider(input = {}) {
31476
32447
  };
31477
32448
  }
31478
32449
  var execFile2 = promisify(execFile);
31479
- var DEFAULT_CAPTURE_TIMEOUT_MS = 3e4;
31480
- var DEFAULT_STOP_TIMEOUT_MS = 15e3;
31481
- var DEVTOOLS_POLL_INTERVAL_MS2 = 50;
31482
- var PROCESS_LIST_MAX_BUFFER_BYTES3 = 16 * 1024 * 1024;
31483
- async function resolveCookieCaptureStrategy(input = {}) {
31484
- const timeoutMs = input.timeoutMs ?? DEFAULT_CAPTURE_TIMEOUT_MS;
31485
- if (input.attachEndpoint !== void 0) {
31486
- if (input.strategy !== void 0 && input.strategy !== "attach") {
31487
- throw new Error(
31488
- `Strategy "${input.strategy}" is incompatible with an explicit attach endpoint.`
31489
- );
31490
- }
31491
- return {
31492
- strategy: "attach",
31493
- attachEndpoint: input.attachEndpoint,
31494
- ...input.profileDirectory === void 0 ? {} : { profileDirectory: input.profileDirectory },
31495
- timeoutMs
31496
- };
31497
- }
32450
+ var NODE_SQLITE_SPECIFIER2 = `node:${"sqlite"}`;
32451
+ var CHROME_EPOCH_OFFSET = 11644473600000000n;
32452
+ var CHROME_HMAC_PREFIX_LENGTH = 32;
32453
+ async function readBrowserCookies(input = {}) {
31498
32454
  const brand2 = resolveRequestedBrand(input);
31499
- const executablePath = resolveBrandExecutablePath(brand2, input.executablePath);
31500
32455
  const userDataDir = resolveBrandUserDataDir(brand2, input.userDataDir);
31501
- const profileDirectory = input.profileDirectory;
31502
- const attachEndpoint = await resolveReachableAttachEndpoint(userDataDir, timeoutMs);
31503
- const runningProcess = findBrandProcess(brand2);
31504
- const autoStrategy = attachEndpoint !== void 0 ? "attach" : runningProcess !== null ? "managed-relaunch" : "headless";
31505
- const strategy = input.strategy ?? autoStrategy;
31506
- validateRequestedStrategy({
31507
- strategy,
31508
- brand: brand2,
31509
- ...attachEndpoint === void 0 ? {} : { attachEndpoint },
31510
- ...runningProcess?.pid === void 0 ? {} : { runningPid: runningProcess.pid }
31511
- });
31512
- return {
31513
- strategy,
31514
- brandId: brand2.id,
31515
- brandDisplayName: brand2.displayName,
31516
- executablePath,
31517
- userDataDir,
31518
- ...profileDirectory === void 0 ? {} : { profileDirectory },
31519
- ...attachEndpoint === void 0 ? {} : { attachEndpoint },
31520
- ...runningProcess === null ? {} : { runningPid: runningProcess.pid },
31521
- timeoutMs
31522
- };
31523
- }
31524
- async function acquireCdpEndpoint(resolved) {
31525
- if (resolved.strategy === "attach") {
31526
- if (!resolved.attachEndpoint) {
31527
- throw new Error("Attach capture requires a debuggable browser endpoint.");
31528
- }
31529
- const inspected = await inspectCdpEndpoint({
31530
- endpoint: resolved.attachEndpoint,
31531
- timeoutMs: Math.min(2e3, resolved.timeoutMs)
31532
- });
31533
- return {
31534
- strategy: "attach",
31535
- cdpEndpoint: inspected.endpoint,
31536
- ...resolved.brandId === void 0 ? {} : { brandId: resolved.brandId },
31537
- ...resolved.brandDisplayName === void 0 ? {} : { brandDisplayName: resolved.brandDisplayName },
31538
- ...resolved.userDataDir === void 0 ? {} : { userDataDir: resolved.userDataDir },
31539
- ...resolved.profileDirectory === void 0 ? {} : { profileDirectory: resolved.profileDirectory },
31540
- cleanup: async () => void 0
31541
- };
31542
- }
31543
- if (!resolved.brandId || !resolved.brandDisplayName || !resolved.executablePath || !resolved.userDataDir) {
32456
+ const profileDirectory = input.profileDirectory ?? "Default";
32457
+ const cookiesPath = join(userDataDir, profileDirectory, "Cookies");
32458
+ if (!existsSync(cookiesPath)) {
31544
32459
  throw new Error(
31545
- "Headless cookie capture requires a resolved browser brand, executable, and user-data-dir."
32460
+ `Cookies database not found at "${cookiesPath}". Verify the browser brand, user-data-dir, and profile-directory are correct.`
31546
32461
  );
31547
32462
  }
31548
- const userDataDir = resolved.userDataDir;
31549
- if (resolved.strategy === "managed-relaunch") {
31550
- if (resolved.runningPid === void 0) {
31551
- throw new Error("Managed relaunch requires a running browser process.");
31552
- }
31553
- await gracefullyStopBrowser(
31554
- getBrowserBrand(resolved.brandId),
31555
- resolved.runningPid,
31556
- resolved.timeoutMs
31557
- );
31558
- }
31559
- await clearChromeSingletonEntries(userDataDir);
31560
- try {
31561
- const capture = await launchCaptureChrome({
31562
- brandDisplayName: resolved.brandDisplayName,
31563
- executablePath: resolved.executablePath,
31564
- userDataDir,
31565
- ...resolved.profileDirectory === void 0 ? {} : { profileDirectory: resolved.profileDirectory },
31566
- timeoutMs: resolved.timeoutMs
31567
- });
31568
- return {
31569
- strategy: resolved.strategy,
31570
- cdpEndpoint: capture.endpoint,
31571
- brandId: resolved.brandId,
31572
- brandDisplayName: resolved.brandDisplayName,
31573
- userDataDir: resolved.userDataDir,
31574
- ...resolved.profileDirectory === void 0 ? {} : { profileDirectory: resolved.profileDirectory },
31575
- cleanup: async () => {
31576
- await capture.kill().catch(() => void 0);
31577
- await clearChromeSingletonEntries(userDataDir).catch(() => void 0);
31578
- }
31579
- };
31580
- } catch (error) {
31581
- await clearChromeSingletonEntries(userDataDir).catch(() => void 0);
31582
- throw error;
31583
- }
31584
- }
31585
- async function gracefullyStopBrowser(brand2, pid, timeoutMs = DEFAULT_STOP_TIMEOUT_MS) {
31586
- if (pid <= 0) {
31587
- return;
31588
- }
31589
- const platformConfig = resolveBrandPlatformConfig(brand2);
31590
- if (process.platform === "darwin" && platformConfig?.bundleId) {
31591
- await execFile2(
31592
- "osascript",
31593
- ["-e", `tell application id "${platformConfig.bundleId}" to quit`],
31594
- {
31595
- maxBuffer: PROCESS_LIST_MAX_BUFFER_BYTES3
31596
- }
31597
- ).catch(() => void 0);
31598
- } else if (process.platform === "win32") {
31599
- await execFile2("taskkill", ["/PID", String(pid)], {
31600
- maxBuffer: PROCESS_LIST_MAX_BUFFER_BYTES3
31601
- }).catch(() => void 0);
31602
- } else {
31603
- try {
31604
- process.kill(pid, "SIGTERM");
31605
- } catch {
31606
- }
31607
- }
31608
- if (await waitForProcessExit2(pid, timeoutMs)) {
31609
- return;
31610
- }
31611
- if (process.platform === "win32") {
31612
- await execFile2("taskkill", ["/F", "/PID", String(pid)], {
31613
- maxBuffer: PROCESS_LIST_MAX_BUFFER_BYTES3
31614
- }).catch(() => void 0);
31615
- } else {
31616
- try {
31617
- process.kill(pid, "SIGKILL");
31618
- } catch {
31619
- }
31620
- }
31621
- await waitForProcessExit2(pid, Math.min(5e3, timeoutMs));
31622
- }
31623
- async function launchCaptureChrome(input) {
31624
- const stderrLines = [];
31625
- const child = spawn(input.executablePath, buildCaptureChromeArgs(input), {
31626
- detached: process.platform !== "win32",
31627
- stdio: ["ignore", "ignore", "pipe"]
31628
- });
31629
- child.unref();
31630
- child.stderr?.setEncoding("utf8");
31631
- child.stderr?.on("data", (chunk) => {
31632
- stderrLines.push(String(chunk));
31633
- });
32463
+ const tempDir = await mkdtemp(join(tmpdir(), "opensteer-cookies-"));
31634
32464
  try {
31635
- const endpoint = await waitForCaptureEndpoint({
31636
- brandDisplayName: input.brandDisplayName,
31637
- child,
31638
- stderrLines,
31639
- timeoutMs: input.timeoutMs,
31640
- userDataDir: input.userDataDir
31641
- });
32465
+ await copyCookiesDatabase(cookiesPath, tempDir);
32466
+ const decryptionKey = await resolveDecryptionKey(brand2.id, userDataDir);
32467
+ const rows = queryAllCookies(join(tempDir, "Cookies"));
32468
+ const cookies = decryptCookieRows(rows, decryptionKey);
31642
32469
  return {
31643
- endpoint,
31644
- kill: async () => {
31645
- await terminateChild(child);
31646
- }
32470
+ cookies,
32471
+ brandId: brand2.id,
32472
+ brandDisplayName: brand2.displayName,
32473
+ userDataDir,
32474
+ profileDirectory
31647
32475
  };
31648
- } catch (error) {
31649
- await terminateChild(child).catch(() => void 0);
31650
- throw error;
32476
+ } finally {
32477
+ await rm(tempDir, { recursive: true, force: true }).catch(() => void 0);
31651
32478
  }
31652
32479
  }
31653
- function relaunchBrowserNormally(executablePath) {
31654
- const child = spawn(executablePath, [], {
31655
- detached: true,
31656
- stdio: "ignore"
31657
- });
31658
- child.unref();
31659
- }
31660
32480
  function resolveRequestedBrand(input) {
31661
32481
  if (input.brandId !== void 0) {
31662
32482
  return getBrowserBrand(input.brandId);
31663
32483
  }
31664
- if (input.userDataDir !== void 0) {
31665
- const inferred = inferBrandFromUserDataDir(input.userDataDir);
31666
- if (!inferred) {
31667
- throw new Error(
31668
- `Could not infer a browser brand from user-data-dir "${input.userDataDir}". Pass --browser explicitly.`
31669
- );
31670
- }
31671
- return inferred;
31672
- }
31673
- if (input.executablePath !== void 0) {
31674
- const inferred = inferBrandFromExecutablePath(input.executablePath);
31675
- if (!inferred) {
31676
- throw new Error(
31677
- `Could not infer a browser brand from executable path "${input.executablePath}". Pass --browser explicitly.`
31678
- );
31679
- }
31680
- return inferred;
31681
- }
31682
32484
  const installed = detectInstalledBrowserBrands()[0];
31683
32485
  if (!installed) {
31684
32486
  throw new Error(
31685
- "No Chromium browser found. Install a supported browser or pass --browser explicitly."
32487
+ "No Chromium browser found. Install a supported browser or pass brandId explicitly."
31686
32488
  );
31687
32489
  }
31688
32490
  return installed.brand;
31689
32491
  }
31690
- async function resolveReachableAttachEndpoint(userDataDir, timeoutMs) {
31691
- const activePort = readDevToolsActivePort(userDataDir);
31692
- if (!activePort) {
31693
- return void 0;
32492
+ async function copyCookiesDatabase(cookiesPath, destDir) {
32493
+ await copyFile(cookiesPath, join(destDir, "Cookies"));
32494
+ for (const suffix of ["-wal", "-journal", "-shm"]) {
32495
+ const src = cookiesPath + suffix;
32496
+ if (existsSync(src)) {
32497
+ await copyFile(src, join(destDir, "Cookies" + suffix)).catch(() => void 0);
32498
+ }
31694
32499
  }
32500
+ }
32501
+ function queryAllCookies(dbPath) {
32502
+ let DatabaseSync;
31695
32503
  try {
31696
- return (await inspectCdpEndpoint({
31697
- endpoint: `http://127.0.0.1:${String(activePort.port)}`,
31698
- timeoutMs: Math.min(2e3, timeoutMs)
31699
- })).endpoint;
32504
+ ({ DatabaseSync } = __require(NODE_SQLITE_SPECIFIER2));
31700
32505
  } catch {
31701
- return void 0;
32506
+ throw new Error(
32507
+ "Reading browser cookies requires Node's built-in SQLite support. Use Node 22.5+ or a build with node:sqlite enabled."
32508
+ );
32509
+ }
32510
+ const database = new DatabaseSync(dbPath, { readOnly: true });
32511
+ try {
32512
+ const stmt = database.prepare(
32513
+ `SELECT host_key, name, value, encrypted_value, path,
32514
+ expires_utc, is_secure, is_httponly, samesite, is_persistent
32515
+ FROM cookies`
32516
+ );
32517
+ stmt.setReadBigInts(true);
32518
+ return stmt.all();
32519
+ } finally {
32520
+ database.close();
31702
32521
  }
31703
32522
  }
31704
- function validateRequestedStrategy(input) {
31705
- if (input.strategy === "attach" && input.attachEndpoint === void 0) {
32523
+ async function resolveDecryptionKey(brandId, userDataDir) {
32524
+ if (process.platform === "darwin") {
32525
+ const password = await resolveKeychainPassword(brandId);
32526
+ const key = pbkdf2Sync(password, "saltysalt", 1003, 16, "sha1");
32527
+ return { platform: "darwin", key, algorithm: "aes-128-cbc" };
32528
+ }
32529
+ if (process.platform === "linux") {
32530
+ const key = pbkdf2Sync("peanuts", "saltysalt", 1, 16, "sha1");
32531
+ return { platform: "linux", key, algorithm: "aes-128-cbc" };
32532
+ }
32533
+ if (process.platform === "win32") {
32534
+ const key = await resolveWindowsMasterKey(userDataDir);
32535
+ return { platform: "win32", key, algorithm: "aes-256-gcm" };
32536
+ }
32537
+ throw new Error(`Unsupported platform "${process.platform}" for cookie decryption.`);
32538
+ }
32539
+ var BRAND_KEYCHAIN_SERVICE = {
32540
+ chrome: "Chrome Safe Storage",
32541
+ "chrome-canary": "Chrome Safe Storage",
32542
+ chromium: "Chromium Safe Storage",
32543
+ brave: "Brave Safe Storage",
32544
+ edge: "Microsoft Edge Safe Storage",
32545
+ vivaldi: "Chrome Safe Storage",
32546
+ helium: "Chrome Safe Storage"
32547
+ };
32548
+ async function resolveKeychainPassword(brandId) {
32549
+ const service = BRAND_KEYCHAIN_SERVICE[brandId];
32550
+ try {
32551
+ const { stdout } = await execFile2("security", [
32552
+ "find-generic-password",
32553
+ "-s",
32554
+ service,
32555
+ "-w"
32556
+ ]);
32557
+ return stdout.trim();
32558
+ } catch {
31706
32559
  throw new Error(
31707
- `${input.brand.displayName} is not currently exposing a debuggable CDP endpoint for attach mode.`
32560
+ `Failed to retrieve "${service}" from macOS Keychain. Ensure the browser has been opened at least once and Keychain access is allowed.`
31708
32561
  );
31709
32562
  }
31710
- if (input.strategy === "headless" && input.runningPid !== void 0) {
32563
+ }
32564
+ async function resolveWindowsMasterKey(userDataDir) {
32565
+ const localStatePath = join(userDataDir, "Local State");
32566
+ let localState;
32567
+ try {
32568
+ localState = JSON.parse(await readFile(localStatePath, "utf8"));
32569
+ } catch {
31711
32570
  throw new Error(
31712
- `${input.brand.displayName} is already running. Close it first or use managed-relaunch.`
32571
+ `Failed to read "${localStatePath}". Ensure the browser has been opened at least once.`
31713
32572
  );
31714
32573
  }
31715
- if (input.strategy === "managed-relaunch" && input.runningPid === void 0) {
32574
+ const encodedKey = localState.os_crypt?.encrypted_key;
32575
+ if (!encodedKey) {
32576
+ throw new Error(`No encrypted key found in "${localStatePath}".`);
32577
+ }
32578
+ const rawKey = Buffer.from(encodedKey, "base64").subarray(5);
32579
+ const psScript = `
32580
+ Add-Type -AssemblyName System.Security
32581
+ $bytes = [byte[]]@(${Array.from(rawKey).join(",")})
32582
+ $decrypted = [System.Security.Cryptography.ProtectedData]::Unprotect($bytes, $null, 'CurrentUser')
32583
+ [Convert]::ToBase64String($decrypted)
32584
+ `;
32585
+ try {
32586
+ const { stdout } = await execFile2("powershell", [
32587
+ "-NoProfile",
32588
+ "-NonInteractive",
32589
+ "-Command",
32590
+ psScript
32591
+ ]);
32592
+ return Buffer.from(stdout.trim(), "base64");
32593
+ } catch {
31716
32594
  throw new Error(
31717
- `${input.brand.displayName} is not currently running, so managed-relaunch is not available.`
32595
+ "Failed to decrypt browser master key via Windows DPAPI. Ensure you are running as the same user who owns the browser profile."
31718
32596
  );
31719
32597
  }
31720
32598
  }
31721
- function inferBrandFromUserDataDir(userDataDir) {
31722
- const normalized = normalizePath(userDataDir);
31723
- return getAllBrowserBrands().find((brand2) => {
31724
- const config = resolveBrandPlatformConfig(brand2);
31725
- if (!config) {
31726
- return false;
32599
+ function decryptCookieRows(rows, decryptionKey) {
32600
+ const cookies = [];
32601
+ const nowSeconds = Math.floor(Date.now() / 1e3);
32602
+ for (const row of rows) {
32603
+ const name = row.name.trim();
32604
+ const domain = row.host_key.trim();
32605
+ if (!name || !domain) {
32606
+ continue;
31727
32607
  }
31728
- const defaultDir = normalizePath(config.userDataDir);
31729
- return normalized === defaultDir || normalized.startsWith(`${defaultDir}/`);
31730
- });
31731
- }
31732
- function inferBrandFromExecutablePath(executablePath) {
31733
- const normalized = normalizePath(executablePath);
31734
- return getAllBrowserBrands().find((brand2) => {
31735
- const config = resolveBrandPlatformConfig(brand2);
31736
- if (!config) {
31737
- return false;
32608
+ const value = decryptCookieValue(row, decryptionKey);
32609
+ if (value === null) {
32610
+ continue;
31738
32611
  }
31739
- return config.executableCandidates.some(
31740
- (candidate) => candidate !== null && normalizePath(candidate) === normalized
31741
- );
31742
- });
31743
- }
31744
- function buildCaptureChromeArgs(input) {
31745
- const args = [
31746
- "--remote-debugging-port=0",
31747
- "--headless=new",
31748
- "--no-first-run",
31749
- "--no-default-browser-check",
31750
- "--disable-background-networking",
31751
- "--disable-sync",
31752
- "--disable-component-update",
31753
- `--user-data-dir=${input.userDataDir}`
31754
- ];
31755
- if (input.profileDirectory !== void 0) {
31756
- args.push(`--profile-directory=${input.profileDirectory}`);
31757
- }
31758
- return args;
31759
- }
31760
- async function waitForCaptureEndpoint(input) {
31761
- const deadline = Date.now() + input.timeoutMs;
31762
- while (Date.now() < deadline) {
31763
- const activePort = readDevToolsActivePort(input.userDataDir);
31764
- if (activePort) {
31765
- try {
31766
- return (await inspectCdpEndpoint({
31767
- endpoint: `http://127.0.0.1:${String(activePort.port)}`,
31768
- timeoutMs: Math.min(2e3, input.timeoutMs)
31769
- })).endpoint;
31770
- } catch {
31771
- return `ws://127.0.0.1:${String(activePort.port)}${activePort.webSocketPath}`;
31772
- }
32612
+ const expiresSeconds = chromeDateToUnixSeconds(row.expires_utc);
32613
+ const isSession = expiresSeconds <= 0;
32614
+ if (!isSession && expiresSeconds < nowSeconds) {
32615
+ continue;
31773
32616
  }
31774
- if (input.child.exitCode !== null) {
31775
- break;
32617
+ const sameSite = chromeSameSiteToString(row.samesite);
32618
+ let secure = Number(row.is_secure) === 1;
32619
+ if (sameSite === "None") {
32620
+ secure = true;
31776
32621
  }
31777
- await sleep4(DEVTOOLS_POLL_INTERVAL_MS2);
32622
+ cookies.push({
32623
+ name,
32624
+ value,
32625
+ domain,
32626
+ path: row.path || "/",
32627
+ secure,
32628
+ httpOnly: Number(row.is_httponly) === 1,
32629
+ ...isSession ? {} : { expires: expiresSeconds },
32630
+ ...sameSite !== void 0 ? { sameSite } : {}
32631
+ });
31778
32632
  }
31779
- throw new Error(formatCaptureLaunchError(input.brandDisplayName, input.stderrLines));
32633
+ return cookies;
31780
32634
  }
31781
- function formatCaptureLaunchError(brandDisplayName, stderrLines) {
31782
- const relevantLines = stderrLines.join("").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
31783
- if (relevantLines.length === 0) {
31784
- return `${brandDisplayName} failed to launch before exposing a DevTools endpoint.`;
32635
+ function decryptCookieValue(row, decryptionKey) {
32636
+ if (row.value && row.value.length > 0) {
32637
+ return row.value;
31785
32638
  }
31786
- const focusedLines = relevantLines.filter(
31787
- (line) => /(error|fatal|sandbox|namespace|permission|cannot|failed|abort)/i.test(line)
31788
- );
31789
- return `${brandDisplayName} failed to launch before exposing a DevTools endpoint.
31790
- ${(focusedLines.length > 0 ? focusedLines : relevantLines).slice(-5).join("\n")}`;
32639
+ const encrypted = Buffer.isBuffer(row.encrypted_value) ? row.encrypted_value : Buffer.from(row.encrypted_value);
32640
+ if (encrypted.length === 0) {
32641
+ return "";
32642
+ }
32643
+ const prefix = encrypted.subarray(0, 3).toString("ascii");
32644
+ if (prefix !== "v10" && prefix !== "v11") {
32645
+ return encrypted.toString("utf8");
32646
+ }
32647
+ const ciphertext = encrypted.subarray(3);
32648
+ if (decryptionKey.algorithm === "aes-128-cbc") {
32649
+ return decryptAes128Cbc(ciphertext, decryptionKey.key);
32650
+ }
32651
+ if (decryptionKey.algorithm === "aes-256-gcm") {
32652
+ return decryptAes256Gcm(ciphertext, decryptionKey.key);
32653
+ }
32654
+ return null;
31791
32655
  }
31792
- async function terminateChild(child) {
31793
- if (child.exitCode !== null) {
31794
- return;
32656
+ function decryptAes128Cbc(ciphertext, key) {
32657
+ try {
32658
+ const iv = Buffer.alloc(16, 32);
32659
+ const decipher = createDecipheriv("aes-128-cbc", key, iv);
32660
+ let decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
32661
+ if (decrypted.length > CHROME_HMAC_PREFIX_LENGTH && containsNonPrintableAscii(decrypted, CHROME_HMAC_PREFIX_LENGTH)) {
32662
+ decrypted = decrypted.subarray(CHROME_HMAC_PREFIX_LENGTH);
32663
+ }
32664
+ if (containsNonPrintableAscii(decrypted, decrypted.length)) {
32665
+ return null;
32666
+ }
32667
+ return decrypted.toString("utf8");
32668
+ } catch {
32669
+ return null;
31795
32670
  }
32671
+ }
32672
+ function decryptAes256Gcm(ciphertext, key) {
31796
32673
  try {
31797
- child.kill("SIGKILL");
32674
+ const nonce = ciphertext.subarray(0, 12);
32675
+ const authTag = ciphertext.subarray(ciphertext.length - 16);
32676
+ const encrypted = ciphertext.subarray(12, ciphertext.length - 16);
32677
+ const decipher = createDecipheriv("aes-256-gcm", key, nonce);
32678
+ decipher.setAuthTag(authTag);
32679
+ const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);
32680
+ if (containsNonPrintableAscii(decrypted, decrypted.length)) {
32681
+ return null;
32682
+ }
32683
+ return decrypted.toString("utf8");
31798
32684
  } catch {
31799
- return;
32685
+ return null;
31800
32686
  }
31801
- await sleep4(50);
31802
32687
  }
31803
- async function waitForProcessExit2(pid, timeoutMs) {
31804
- const deadline = Date.now() + timeoutMs;
31805
- while (Date.now() < deadline) {
31806
- if (!isProcessRunning(pid)) {
32688
+ function containsNonPrintableAscii(buffer, length) {
32689
+ const end = Math.min(length, buffer.length);
32690
+ for (let i = 0; i < end; i++) {
32691
+ const byte = buffer[i];
32692
+ if (byte < 32 || byte > 126) {
31807
32693
  return true;
31808
32694
  }
31809
- await sleep4(50);
31810
32695
  }
31811
- return !isProcessRunning(pid);
32696
+ return false;
31812
32697
  }
31813
- function normalizePath(value) {
31814
- return resolve(expandHome(value)).replaceAll("\\", "/").toLowerCase();
32698
+ function chromeDateToUnixSeconds(chromeTimestamp) {
32699
+ const ts = BigInt(chromeTimestamp);
32700
+ if (ts <= 0n) {
32701
+ return -1;
32702
+ }
32703
+ return Number((ts - CHROME_EPOCH_OFFSET) / 1000000n);
31815
32704
  }
31816
- async function sleep4(ms) {
31817
- await new Promise((resolve5) => setTimeout(resolve5, ms));
32705
+ function chromeSameSiteToString(value) {
32706
+ const v = Number(value);
32707
+ if (v === 0) return "None";
32708
+ if (v === 1) return "Lax";
32709
+ if (v === 2) return "Strict";
32710
+ return void 0;
31818
32711
  }
31819
32712
 
31820
32713
  // src/cloud/cookie-sync.ts
@@ -31870,14 +32763,14 @@ function toPortableBrowserProfileCookieRecord(cookie) {
31870
32763
  if (!name || !domain) {
31871
32764
  return null;
31872
32765
  }
31873
- const path13 = typeof cookie.path === "string" && cookie.path.trim().length > 0 ? cookie.path : "/";
32766
+ const path15 = typeof cookie.path === "string" && cookie.path.trim().length > 0 ? cookie.path : "/";
31874
32767
  const expiresAt = typeof cookie.expires === "number" && Number.isFinite(cookie.expires) && cookie.expires > 0 ? Math.floor(cookie.expires * 1e3) : null;
31875
32768
  const sameSite = normalizeSameSite(cookie.sameSite);
31876
32769
  return {
31877
32770
  name,
31878
32771
  value: cookie.value,
31879
32772
  domain,
31880
- path: path13,
32773
+ path: path15,
31881
32774
  secure: cookie.secure,
31882
32775
  httpOnly: cookie.httpOnly,
31883
32776
  ...sameSite === void 0 ? {} : { sameSite },
@@ -31893,114 +32786,48 @@ function normalizeSameSite(value) {
31893
32786
  return void 0;
31894
32787
  }
31895
32788
 
31896
- // src/cloud/portable-cookie-snapshot.ts
31897
- var gzip = promisify(gzip$1);
31898
- async function capturePortableBrowserProfileSnapshot(input = {}) {
31899
- const attached = input.attachEndpoint ? await inspectCdpEndpoint({
31900
- endpoint: input.attachEndpoint,
31901
- ...input.timeoutMs === void 0 ? {} : { timeoutMs: input.timeoutMs }
31902
- }) : await selectAttachBrowserCandidate({
31903
- ...input.timeoutMs === void 0 ? {} : { timeoutMs: input.timeoutMs }
31904
- });
31905
- const browser = await connectPlaywrightChromiumBrowser({
31906
- url: attached.endpoint
31907
- });
31908
- try {
31909
- const context = browser.contexts()[0];
31910
- if (!context) {
31911
- throw new Error("Attached browser did not expose a default browser context.");
31912
- }
31913
- const prepared = prepareBrowserProfileSyncCookies({
31914
- cookies: await context.cookies(),
31915
- ...input.domains === void 0 ? {} : { domains: input.domains }
31916
- });
31917
- if (prepared.cookies.length === 0) {
31918
- throw new Error("No syncable cookies found for the selected browser and scope.");
31919
- }
31920
- const browserVersion = browser.version();
31921
- const source = parseSnapshotSource(attached.browser ?? browserVersion);
31922
- return {
31923
- version: "portable-cookies-v1",
31924
- source: {
31925
- browserFamily: "chromium",
31926
- ...source.browserName === void 0 ? {} : { browserName: source.browserName },
31927
- ...source.browserMajor === void 0 ? {} : { browserMajor: source.browserMajor },
31928
- ...input.browserBrand === void 0 ? {} : { browserBrand: input.browserBrand },
31929
- ...input.captureMethod === void 0 ? {} : { captureMethod: input.captureMethod },
31930
- platform: normalizePlatform(process.platform),
31931
- capturedAt: Date.now()
31932
- },
31933
- cookies: prepared.cookies
31934
- };
31935
- } finally {
31936
- await browser.close().catch(() => void 0);
31937
- }
31938
- }
31939
- async function encodePortableBrowserProfileSnapshot(snapshot) {
31940
- return gzip(Buffer.from(JSON.stringify(snapshot), "utf8"));
31941
- }
31942
- function parseSnapshotSource(value) {
31943
- if (!value) {
31944
- return {};
31945
- }
31946
- const trimmed = value.trim();
31947
- const browserName = trimmed.split("/")[0]?.trim() || void 0;
31948
- const majorMatch = trimmed.match(/(\d+)/);
31949
- return {
31950
- ...browserName === void 0 ? {} : { browserName },
31951
- ...majorMatch?.[1] === void 0 ? {} : { browserMajor: majorMatch[1] }
31952
- };
31953
- }
31954
- function normalizePlatform(platform) {
31955
- if (platform === "darwin") return "macos";
31956
- if (platform === "win32") return "windows";
31957
- return platform;
31958
- }
31959
-
31960
32789
  // src/cloud/profile-sync.ts
32790
+ var gzip = promisify(gzip$1);
31961
32791
  var DEFAULT_POLL_INTERVAL_MS = 1e3;
31962
32792
  var DEFAULT_POLL_TIMEOUT_MS = 5 * 6e4;
31963
32793
  async function syncBrowserProfileCookies(client, input) {
31964
- const resolved = await resolveCookieCaptureStrategy({
31965
- ...input.attachEndpoint === void 0 ? {} : { attachEndpoint: input.attachEndpoint },
32794
+ const result = await readBrowserCookies({
31966
32795
  ...input.brandId === void 0 ? {} : { brandId: input.brandId },
31967
32796
  ...input.userDataDir === void 0 ? {} : { userDataDir: input.userDataDir },
31968
- ...input.profileDirectory === void 0 ? {} : { profileDirectory: input.profileDirectory },
31969
- ...input.executablePath === void 0 ? {} : { executablePath: input.executablePath },
31970
- ...input.strategy === void 0 ? {} : { strategy: input.strategy },
31971
- ...input.timeoutMs === void 0 ? {} : { timeoutMs: input.timeoutMs }
32797
+ ...input.profileDirectory === void 0 ? {} : { profileDirectory: input.profileDirectory }
31972
32798
  });
31973
- const shouldRestoreBrowser = (input.restoreBrowser ?? true) && resolved.strategy === "managed-relaunch";
31974
- let captureSource;
31975
- try {
31976
- captureSource = await acquireCdpEndpoint(resolved);
31977
- const snapshot = await capturePortableBrowserProfileSnapshot({
31978
- attachEndpoint: captureSource.cdpEndpoint,
31979
- ...captureSource.brandId === void 0 ? {} : { browserBrand: captureSource.brandId },
31980
- captureMethod: captureSource.strategy,
31981
- ...input.domains === void 0 ? {} : { domains: input.domains },
31982
- ...input.timeoutMs === void 0 ? {} : { timeoutMs: input.timeoutMs }
31983
- });
31984
- const payload = await encodePortableBrowserProfileSnapshot(snapshot);
31985
- const created = await client.createBrowserProfileImport({
31986
- profileId: input.profileId
31987
- });
31988
- if (payload.length > created.maxUploadBytes) {
31989
- throw new Error(
31990
- `Compressed cookie snapshot is ${String(payload.length)} bytes, exceeding the ${String(created.maxUploadBytes)} byte upload limit.`
31991
- );
31992
- }
31993
- const uploaded = await client.uploadBrowserProfileImportPayload({
31994
- uploadUrl: created.uploadUrl,
31995
- payload
31996
- });
31997
- return uploaded.status === "ready" ? uploaded : waitForBrowserProfileImport(client, created.importId);
31998
- } finally {
31999
- await captureSource?.cleanup().catch(() => void 0);
32000
- if (shouldRestoreBrowser && resolved.executablePath !== void 0) {
32001
- relaunchBrowserNormally(resolved.executablePath);
32002
- }
32799
+ const prepared = prepareBrowserProfileSyncCookies({
32800
+ cookies: result.cookies,
32801
+ ...input.domains === void 0 ? {} : { domains: input.domains }
32802
+ });
32803
+ if (prepared.cookies.length === 0) {
32804
+ throw new Error("No syncable cookies found for the selected browser and scope.");
32805
+ }
32806
+ const snapshot = {
32807
+ version: "portable-cookies-v1",
32808
+ source: {
32809
+ browserFamily: "chromium",
32810
+ browserBrand: result.brandId,
32811
+ captureMethod: "sqlite",
32812
+ platform: normalizePlatform(process.platform),
32813
+ capturedAt: Date.now()
32814
+ },
32815
+ cookies: prepared.cookies
32816
+ };
32817
+ const payload = await gzip(Buffer.from(JSON.stringify(snapshot), "utf8"));
32818
+ const created = await client.createBrowserProfileImport({
32819
+ profileId: input.profileId
32820
+ });
32821
+ if (payload.length > created.maxUploadBytes) {
32822
+ throw new Error(
32823
+ `Compressed cookie snapshot is ${String(payload.length)} bytes, exceeding the ${String(created.maxUploadBytes)} byte upload limit.`
32824
+ );
32003
32825
  }
32826
+ const uploaded = await client.uploadBrowserProfileImportPayload({
32827
+ uploadUrl: created.uploadUrl,
32828
+ payload
32829
+ });
32830
+ return uploaded.status === "ready" ? uploaded : waitForBrowserProfileImport(client, created.importId);
32004
32831
  }
32005
32832
  async function waitForBrowserProfileImport(client, importId) {
32006
32833
  const deadline = Date.now() + DEFAULT_POLL_TIMEOUT_MS;
@@ -32012,12 +32839,17 @@ async function waitForBrowserProfileImport(client, importId) {
32012
32839
  if (current.status === "failed") {
32013
32840
  throw new Error(current.error ?? "Browser profile sync failed.");
32014
32841
  }
32015
- await sleep5(DEFAULT_POLL_INTERVAL_MS);
32842
+ await sleep4(DEFAULT_POLL_INTERVAL_MS);
32016
32843
  }
32017
32844
  throw new Error(`Timed out waiting for browser profile sync "${importId}" to finish.`);
32018
32845
  }
32019
- async function sleep5(ms) {
32020
- await new Promise((resolve5) => setTimeout(resolve5, ms));
32846
+ function normalizePlatform(platform) {
32847
+ if (platform === "darwin") return "macos";
32848
+ if (platform === "win32") return "windows";
32849
+ return platform;
32850
+ }
32851
+ async function sleep4(ms) {
32852
+ await new Promise((resolve4) => setTimeout(resolve4, ms));
32021
32853
  }
32022
32854
 
32023
32855
  // src/cloud/client.ts
@@ -32037,7 +32869,8 @@ var OpensteerCloudClient = class {
32037
32869
  ...input.name === void 0 ? {} : { name: input.name },
32038
32870
  ...input.browser === void 0 ? {} : { browser: input.browser },
32039
32871
  ...input.context === void 0 ? {} : { context: input.context },
32040
- ...input.browserProfile === void 0 ? {} : { browserProfile: input.browserProfile }
32872
+ ...input.browserProfile === void 0 ? {} : { browserProfile: input.browserProfile },
32873
+ ...input.observability === void 0 ? {} : { observability: input.observability }
32041
32874
  }
32042
32875
  });
32043
32876
  return await response.json();
@@ -32215,8 +33048,8 @@ var OpensteerCloudClient = class {
32215
33048
  }
32216
33049
  };
32217
33050
  function delay(ms) {
32218
- return new Promise((resolve5) => {
32219
- setTimeout(resolve5, ms);
33051
+ return new Promise((resolve4) => {
33052
+ setTimeout(resolve4, ms);
32220
33053
  });
32221
33054
  }
32222
33055
  function wrapCloudFetchError(error, input) {
@@ -32239,17 +33072,17 @@ function wrapCloudFetchError(error, input) {
32239
33072
  function resolveCloudConfig(input = {}) {
32240
33073
  const provider = resolveOpensteerProvider({
32241
33074
  ...input.provider === void 0 ? {} : { provider: input.provider },
32242
- ...input.environmentProvider === void 0 ? {} : { environmentProvider: input.environmentProvider }
33075
+ ...input.environment?.OPENSTEER_PROVIDER === void 0 ? {} : { environmentProvider: input.environment.OPENSTEER_PROVIDER }
32243
33076
  });
32244
33077
  if (provider.mode !== "cloud") {
32245
33078
  return void 0;
32246
33079
  }
32247
33080
  const cloudProvider = input.provider?.mode === "cloud" ? input.provider : void 0;
32248
- const apiKey = cloudProvider?.apiKey ?? process.env.OPENSTEER_API_KEY;
33081
+ const apiKey = cloudProvider?.apiKey ?? input.environment?.OPENSTEER_API_KEY;
32249
33082
  if (!apiKey || apiKey.trim().length === 0) {
32250
33083
  throw new Error("provider=cloud requires OPENSTEER_API_KEY or provider.apiKey.");
32251
33084
  }
32252
- const baseUrl = cloudProvider?.baseUrl ?? process.env.OPENSTEER_BASE_URL;
33085
+ const baseUrl = cloudProvider?.baseUrl ?? input.environment?.OPENSTEER_BASE_URL;
32253
33086
  if (!baseUrl || baseUrl.trim().length === 0) {
32254
33087
  throw new Error("provider=cloud requires OPENSTEER_BASE_URL or provider.baseUrl.");
32255
33088
  }
@@ -32351,9 +33184,9 @@ var OpensteerCloudAutomationClient = class {
32351
33184
  sentAt: Date.now(),
32352
33185
  ...input === void 0 ? {} : { input }
32353
33186
  };
32354
- return new Promise((resolve5, reject) => {
33187
+ return new Promise((resolve4, reject) => {
32355
33188
  this.pending.set(requestId, {
32356
- resolve: (value) => resolve5(value),
33189
+ resolve: (value) => resolve4(value),
32357
33190
  reject
32358
33191
  });
32359
33192
  try {
@@ -32416,8 +33249,8 @@ var OpensteerCloudAutomationClient = class {
32416
33249
  pending.reject(new Error(`automation connection closed before ${requestId} completed`));
32417
33250
  }
32418
33251
  this.pending.clear();
32419
- await new Promise((resolve5) => {
32420
- socket.once("close", () => resolve5());
33252
+ await new Promise((resolve4) => {
33253
+ socket.once("close", () => resolve4());
32421
33254
  socket.close();
32422
33255
  }).catch(() => void 0);
32423
33256
  }
@@ -32459,8 +33292,8 @@ var OpensteerCloudAutomationClient = class {
32459
33292
  }
32460
33293
  this.pending.clear();
32461
33294
  });
32462
- await new Promise((resolve5, reject) => {
32463
- socket.once("open", () => resolve5());
33295
+ await new Promise((resolve4, reject) => {
33296
+ socket.once("open", () => resolve4());
32464
33297
  socket.once("error", reject);
32465
33298
  });
32466
33299
  this.send({
@@ -32801,6 +33634,7 @@ var CloudSessionProxy = class {
32801
33634
  workspace;
32802
33635
  cleanupRootOnClose;
32803
33636
  cloud;
33637
+ observability;
32804
33638
  sessionId;
32805
33639
  sessionBaseUrl;
32806
33640
  client;
@@ -32809,8 +33643,9 @@ var CloudSessionProxy = class {
32809
33643
  constructor(cloud, options = {}) {
32810
33644
  this.cloud = cloud;
32811
33645
  this.workspace = options.workspace;
32812
- this.rootPath = options.rootPath ?? (this.workspace === void 0 ? path6.join(tmpdir(), `${TEMPORARY_CLOUD_WORKSPACE_PREFIX}${randomUUID()}`) : resolveFilesystemWorkspacePath({
32813
- rootDir: path6.resolve(options.rootDir ?? process.cwd()),
33646
+ this.observability = options.observability;
33647
+ this.rootPath = options.rootPath ?? (this.workspace === void 0 ? path7.join(tmpdir(), `${TEMPORARY_CLOUD_WORKSPACE_PREFIX}${randomUUID()}`) : resolveFilesystemWorkspacePath({
33648
+ rootDir: path7.resolve(options.rootDir ?? process.cwd()),
32814
33649
  workspace: this.workspace
32815
33650
  }));
32816
33651
  this.cleanupRootOnClose = options.cleanupRootOnClose ?? this.workspace === void 0;
@@ -33153,6 +33988,7 @@ var CloudSessionProxy = class {
33153
33988
  ...this.workspace === void 0 ? {} : { name: this.workspace },
33154
33989
  ...input.launch === void 0 ? {} : { browser: input.launch },
33155
33990
  ...input.context === void 0 ? {} : { context: input.context },
33991
+ ...this.observability === void 0 ? {} : { observability: this.observability },
33156
33992
  ...resolveCloudBrowserProfile(this.cloud, input) === void 0 ? {} : { browserProfile: resolveCloudBrowserProfile(this.cloud, input) }
33157
33993
  });
33158
33994
  const record = {
@@ -33250,16 +34086,17 @@ function isMissingCloudSessionError(error) {
33250
34086
 
33251
34087
  // src/sdk/runtime-resolution.ts
33252
34088
  function resolveOpensteerRuntimeConfig(input = {}) {
34089
+ const environment = input.environment ?? process.env;
33253
34090
  const provider = resolveOpensteerProvider({
33254
34091
  ...input.provider === void 0 ? {} : { provider: input.provider },
33255
- ...input.environmentProvider === void 0 ? {} : { environmentProvider: input.environmentProvider }
34092
+ ...environment.OPENSTEER_PROVIDER === void 0 ? {} : { environmentProvider: environment.OPENSTEER_PROVIDER }
33256
34093
  });
33257
34094
  if (provider.mode === "cloud") {
33258
34095
  return {
33259
34096
  provider,
33260
34097
  cloud: resolveCloudConfig({
33261
34098
  ...input.provider === void 0 ? {} : { provider: input.provider },
33262
- ...input.environmentProvider === void 0 ? {} : { environmentProvider: input.environmentProvider }
34099
+ environment
33263
34100
  })
33264
34101
  };
33265
34102
  }
@@ -33270,7 +34107,7 @@ function createOpensteerSemanticRuntime(input = {}) {
33270
34107
  const engine = input.engine ?? runtimeOptions.engineName ?? DEFAULT_OPENSTEER_ENGINE;
33271
34108
  const config = resolveOpensteerRuntimeConfig({
33272
34109
  ...input.provider === void 0 ? {} : { provider: input.provider },
33273
- ...process.env.OPENSTEER_PROVIDER === void 0 ? {} : { environmentProvider: process.env.OPENSTEER_PROVIDER }
34110
+ ...input.environment === void 0 ? {} : { environment: input.environment }
33274
34111
  });
33275
34112
  assertProviderSupportsEngine(config.provider.mode, engine);
33276
34113
  if (config.provider.mode === "cloud") {
@@ -33278,7 +34115,8 @@ function createOpensteerSemanticRuntime(input = {}) {
33278
34115
  ...runtimeOptions.rootDir === void 0 ? {} : { rootDir: runtimeOptions.rootDir },
33279
34116
  ...runtimeOptions.rootPath === void 0 ? {} : { rootPath: runtimeOptions.rootPath },
33280
34117
  ...runtimeOptions.workspace === void 0 ? {} : { workspace: runtimeOptions.workspace },
33281
- ...runtimeOptions.cleanupRootOnClose === void 0 ? {} : { cleanupRootOnClose: runtimeOptions.cleanupRootOnClose }
34118
+ ...runtimeOptions.cleanupRootOnClose === void 0 ? {} : { cleanupRootOnClose: runtimeOptions.cleanupRootOnClose },
34119
+ ...runtimeOptions.observability === void 0 ? {} : { observability: runtimeOptions.observability }
33282
34120
  });
33283
34121
  }
33284
34122
  return new OpensteerRuntime({
@@ -33286,7 +34124,109 @@ function createOpensteerSemanticRuntime(input = {}) {
33286
34124
  engineName: engine
33287
34125
  });
33288
34126
  }
34127
+ var ENV_FILENAMES = [".env", ".env.local"];
34128
+ var OPENSTEER_ENV_PREFIX = "OPENSTEER_";
34129
+ var opensteerEnvironmentCache = /* @__PURE__ */ new Map();
34130
+ function resolveOpensteerEnvironment(cwd = process.cwd(), baseEnv = process.env) {
34131
+ const resolvedCwd = path7.resolve(cwd);
34132
+ const signature = buildEnvironmentSignature(baseEnv, isOpensteerEnvironmentKey);
34133
+ const cached = opensteerEnvironmentCache.get(resolvedCwd);
34134
+ if (cached && cached.signature === signature) {
34135
+ return { ...cached.values };
34136
+ }
34137
+ const resolved = resolveEnvironmentFiles(resolvedCwd, baseEnv, isOpensteerEnvironmentKey);
34138
+ opensteerEnvironmentCache.set(resolvedCwd, {
34139
+ signature,
34140
+ values: { ...resolved }
34141
+ });
34142
+ return { ...resolved };
34143
+ }
34144
+ function loadEnvironment(cwd = process.cwd()) {
34145
+ const resolved = resolveEnvironmentFiles(path7.resolve(cwd), process.env);
34146
+ for (const [key, value] of Object.entries(resolved)) {
34147
+ process.env[key] = value;
34148
+ }
34149
+ }
34150
+ function collectDirectories(cwd) {
34151
+ const directories = [];
34152
+ let current = path7.resolve(cwd);
34153
+ for (; ; ) {
34154
+ directories.unshift(current);
34155
+ const parent = path7.dirname(current);
34156
+ if (parent === current) {
34157
+ return directories;
34158
+ }
34159
+ current = parent;
34160
+ }
34161
+ }
34162
+ function parseEnvFile(contents) {
34163
+ const parsed = {};
34164
+ for (const rawLine of contents.split(/\r?\n/u)) {
34165
+ const trimmed = rawLine.trim();
34166
+ if (!trimmed || trimmed.startsWith("#")) {
34167
+ continue;
34168
+ }
34169
+ const line = trimmed.startsWith("export ") ? trimmed.slice("export ".length) : trimmed;
34170
+ const separatorIndex = line.indexOf("=");
34171
+ if (separatorIndex <= 0) {
34172
+ continue;
34173
+ }
34174
+ const key = line.slice(0, separatorIndex).trim();
34175
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/u.test(key)) {
34176
+ continue;
34177
+ }
34178
+ const rawValue = line.slice(separatorIndex + 1).trim();
34179
+ parsed[key] = parseEnvValue(rawValue);
34180
+ }
34181
+ return parsed;
34182
+ }
34183
+ function parseEnvValue(rawValue) {
34184
+ if (rawValue.length >= 2 && rawValue.startsWith('"') && rawValue.endsWith('"')) {
34185
+ return rawValue.slice(1, -1).replace(/\\n/g, "\n").replace(/\\r/g, "\r").replace(/\\t/g, " ").replace(/\\"/g, '"');
34186
+ }
34187
+ if (rawValue.length >= 2 && rawValue.startsWith("'") && rawValue.endsWith("'")) {
34188
+ return rawValue.slice(1, -1);
34189
+ }
34190
+ return rawValue.replace(/\s+#.*$/u, "").trimEnd();
34191
+ }
34192
+ function resolveEnvironmentFiles(cwd, baseEnv, predicate) {
34193
+ const resolved = collectEnvironment(baseEnv, predicate);
34194
+ const protectedKeys = new Set(Object.keys(resolved));
34195
+ const directories = collectDirectories(cwd);
34196
+ for (const directory of directories) {
34197
+ for (const filename of ENV_FILENAMES) {
34198
+ const filePath = path7.join(directory, filename);
34199
+ if (!existsSync(filePath)) {
34200
+ continue;
34201
+ }
34202
+ const parsed = parseEnvFile(readFileSync(filePath, "utf8"));
34203
+ for (const [key, value] of Object.entries(parsed)) {
34204
+ if (predicate && !predicate(key) || protectedKeys.has(key)) {
34205
+ continue;
34206
+ }
34207
+ resolved[key] = value;
34208
+ }
34209
+ }
34210
+ }
34211
+ return resolved;
34212
+ }
34213
+ function collectEnvironment(baseEnv, predicate) {
34214
+ const resolved = {};
34215
+ for (const [key, value] of Object.entries(baseEnv)) {
34216
+ if (predicate && !predicate(key) || value === void 0) {
34217
+ continue;
34218
+ }
34219
+ resolved[key] = value;
34220
+ }
34221
+ return resolved;
34222
+ }
34223
+ function buildEnvironmentSignature(baseEnv, predicate) {
34224
+ return Object.entries(baseEnv).filter(([key, value]) => predicate(key) && value !== void 0).sort(([leftKey], [rightKey]) => leftKey.localeCompare(rightKey)).map(([key, value]) => `${key}=${value}`).join("\n");
34225
+ }
34226
+ function isOpensteerEnvironmentKey(key) {
34227
+ return key.startsWith(OPENSTEER_ENV_PREFIX);
34228
+ }
33289
34229
 
33290
- export { CloudSessionProxy, DEFAULT_OPENSTEER_ENGINE, DEFERRED_MATCH_ATTR_KEYS, ElementPathError, MATCH_ATTRIBUTE_PRIORITY, OPENSTEER_DOM_ACTION_BRIDGE_SYMBOL, OPENSTEER_ENGINE_NAMES, OPENSTEER_FILESYSTEM_WORKSPACE_LAYOUT, OPENSTEER_FILESYSTEM_WORKSPACE_VERSION, OpensteerAttachAmbiguousError, OpensteerBrowserManager, OpensteerCloudClient, OpensteerRuntime, OpensteerSessionRuntime2 as OpensteerSessionRuntime, STABLE_PRIMARY_ATTR_KEYS, assertProviderSupportsEngine, buildArrayFieldPathCandidates, buildDomDescriptorKey, buildDomDescriptorPayload, buildDomDescriptorVersion, buildPathCandidates, buildPathSelectorHint, buildSegmentSelector, clearPersistedSessionRecord, cloneElementPath, cloneReplayElementPath, cloneStructuralElementAnchor, createDomRuntime, createFilesystemOpensteerWorkspace, createOpensteerExtractionDescriptorStore, createOpensteerSemanticRuntime, defaultFallbackPolicy, defaultPolicy, defaultRetryPolicy, defaultSettlePolicy, defaultTimeoutPolicy, delayWithSignal, discoverLocalCdpBrowsers, dispatchSemanticOperation, hashDomDescriptorDescription, inspectCdpEndpoint, isCurrentUrlField, isProcessRunning, isValidCssAttributeKey, listLocalChromeProfiles, normalizeExtractedValue, normalizeOpensteerEngineName, normalizeOpensteerProviderMode, normalizeWorkspaceId, parseDomDescriptorRecord, parseExtractionDescriptorRecord, pathExists, readPersistedCloudSessionRecord, readPersistedLocalBrowserSessionRecord, readPersistedSessionRecord, resolveCloudConfig, resolveCloudSessionRecordPath, resolveDomActionBridge, resolveExtractedValueInContext, resolveFilesystemWorkspacePath, resolveLiveSessionRecordPath, resolveLocalSessionRecordPath, resolveOpensteerEngineName, resolveOpensteerProvider, resolveOpensteerRuntimeConfig, runWithPolicyTimeout, sanitizeElementPath, sanitizeReplayElementPath, sanitizeStructuralElementAnchor, settleWithPolicy, shouldKeepAttributeForPath, writePersistedSessionRecord };
33291
- //# sourceMappingURL=chunk-IAA57HNW.js.map
33292
- //# sourceMappingURL=chunk-IAA57HNW.js.map
34230
+ export { CloudSessionProxy, DEFAULT_OPENSTEER_ENGINE, DEFERRED_MATCH_ATTR_KEYS, ElementPathError, MATCH_ATTRIBUTE_PRIORITY, OPENSTEER_DOM_ACTION_BRIDGE_SYMBOL, OPENSTEER_ENGINE_NAMES, OPENSTEER_FILESYSTEM_WORKSPACE_LAYOUT, OPENSTEER_FILESYSTEM_WORKSPACE_VERSION, OpensteerAttachAmbiguousError, OpensteerBrowserManager, OpensteerCloudClient, OpensteerRuntime, OpensteerSessionRuntime2 as OpensteerSessionRuntime, STABLE_PRIMARY_ATTR_KEYS, assertProviderSupportsEngine, buildArrayFieldPathCandidates, buildDomDescriptorKey, buildDomDescriptorPayload, buildDomDescriptorVersion, buildPathCandidates, buildPathSelectorHint, buildSegmentSelector, clearPersistedSessionRecord, cloneElementPath, cloneReplayElementPath, cloneStructuralElementAnchor, createArtifactStore, createDomRuntime, createFilesystemOpensteerWorkspace, createObservationStore, createOpensteerExtractionDescriptorStore, createOpensteerSemanticRuntime, defaultFallbackPolicy, defaultPolicy, defaultRetryPolicy, defaultSettlePolicy, defaultTimeoutPolicy, delayWithSignal, discoverLocalCdpBrowsers, dispatchSemanticOperation, hashDomDescriptorDescription, inspectCdpEndpoint, isCurrentUrlField, isProcessRunning, isValidCssAttributeKey, listLocalChromeProfiles, loadEnvironment, manifestToExternalBinaryLocation, normalizeExtractedValue, normalizeObservabilityConfig, normalizeOpensteerEngineName, normalizeOpensteerProviderMode, normalizeWorkspaceId, parseDomDescriptorRecord, parseExtractionDescriptorRecord, pathExists, readPersistedCloudSessionRecord, readPersistedLocalBrowserSessionRecord, readPersistedSessionRecord, resolveCloudConfig, resolveCloudSessionRecordPath, resolveDomActionBridge, resolveExtractedValueInContext, resolveFilesystemWorkspacePath, resolveLiveSessionRecordPath, resolveLocalSessionRecordPath, resolveOpensteerEngineName, resolveOpensteerEnvironment, resolveOpensteerProvider, resolveOpensteerRuntimeConfig, runWithPolicyTimeout, sanitizeElementPath, sanitizeReplayElementPath, sanitizeStructuralElementAnchor, settleWithPolicy, shouldKeepAttributeForPath, writePersistedSessionRecord };
34231
+ //# sourceMappingURL=chunk-RO6WAWWG.js.map
34232
+ //# sourceMappingURL=chunk-RO6WAWWG.js.map