opensteer 0.8.8 → 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
  }
@@ -7048,9 +7596,9 @@ function compareByCreatedAtAndId(left, right) {
7048
7596
  var FilesystemRegistryStore = class {
7049
7597
  constructor(rootPath, registryRelativePath) {
7050
7598
  this.registryRelativePath = registryRelativePath;
7051
- const basePath = path6.join(rootPath, ...registryRelativePath);
7052
- this.recordsDirectory = path6.join(basePath, "records");
7053
- 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");
7054
7602
  }
7055
7603
  recordsDirectory;
7056
7604
  indexesDirectory;
@@ -7119,7 +7667,7 @@ var FilesystemRegistryStore = class {
7119
7667
  async readRecordsFromDirectory() {
7120
7668
  const files = await listJsonFiles(this.recordsDirectory);
7121
7669
  const records = await Promise.all(
7122
- files.map((fileName) => readJsonFile(path6.join(this.recordsDirectory, fileName)))
7670
+ files.map((fileName) => readJsonFile(path7.join(this.recordsDirectory, fileName)))
7123
7671
  );
7124
7672
  records.sort(compareByCreatedAtAndId);
7125
7673
  return records;
@@ -7151,17 +7699,17 @@ var FilesystemRegistryStore = class {
7151
7699
  return record;
7152
7700
  }
7153
7701
  recordPath(id) {
7154
- return path6.join(this.recordsDirectory, `${encodePathSegment(id)}.json`);
7702
+ return path7.join(this.recordsDirectory, `${encodePathSegment(id)}.json`);
7155
7703
  }
7156
7704
  indexPath(key, version) {
7157
- return path6.join(
7705
+ return path7.join(
7158
7706
  this.indexesDirectory,
7159
7707
  encodePathSegment(key),
7160
7708
  `${encodePathSegment(version)}.json`
7161
7709
  );
7162
7710
  }
7163
7711
  writeLockPath() {
7164
- return path6.join(path6.dirname(this.recordsDirectory), ".write.lock");
7712
+ return path7.join(path7.dirname(this.recordsDirectory), ".write.lock");
7165
7713
  }
7166
7714
  };
7167
7715
  var FilesystemDescriptorRegistry = class extends FilesystemRegistryStore {
@@ -7531,7 +8079,7 @@ var SqliteSavedNetworkStore = class {
7531
8079
  directoryInitialization;
7532
8080
  databaseInitialization;
7533
8081
  constructor(rootPath) {
7534
- this.databasePath = path6.join(rootPath, "registry", "saved-network.sqlite");
8082
+ this.databasePath = path7.join(rootPath, "registry", "saved-network.sqlite");
7535
8083
  }
7536
8084
  async initialize() {
7537
8085
  await this.ensureDatabaseDirectory();
@@ -7741,7 +8289,7 @@ var SqliteSavedNetworkStore = class {
7741
8289
  }
7742
8290
  }
7743
8291
  async ensureDatabaseDirectory() {
7744
- this.directoryInitialization ??= ensureDirectory(path6.dirname(this.databasePath)).catch(
8292
+ this.directoryInitialization ??= ensureDirectory(path7.dirname(this.databasePath)).catch(
7745
8293
  (error) => {
7746
8294
  this.directoryInitialization = void 0;
7747
8295
  throw error;
@@ -8147,7 +8695,7 @@ var FilesystemTraceStore = class {
8147
8695
  constructor(rootPath, artifacts) {
8148
8696
  this.rootPath = rootPath;
8149
8697
  this.artifacts = artifacts;
8150
- this.runsDirectory = path6.join(this.rootPath, "traces", "runs");
8698
+ this.runsDirectory = path7.join(this.rootPath, "traces", "runs");
8151
8699
  }
8152
8700
  runsDirectory;
8153
8701
  async initialize() {
@@ -8224,7 +8772,7 @@ var FilesystemTraceStore = class {
8224
8772
  ...input.error === void 0 ? {} : { error: input.error }
8225
8773
  };
8226
8774
  await writeJsonFileExclusive(
8227
- path6.join(this.runEntriesDirectory(runId), sequenceFileName(sequence)),
8775
+ path7.join(this.runEntriesDirectory(runId), sequenceFileName(sequence)),
8228
8776
  entry
8229
8777
  );
8230
8778
  await writeJsonFileAtomic(this.runManifestPath(runId), {
@@ -8243,7 +8791,7 @@ var FilesystemTraceStore = class {
8243
8791
  const files = await listJsonFiles(entriesDirectory);
8244
8792
  return Promise.all(
8245
8793
  files.map(
8246
- (fileName) => readJsonFile(path6.join(entriesDirectory, fileName))
8794
+ (fileName) => readJsonFile(path7.join(entriesDirectory, fileName))
8247
8795
  )
8248
8796
  );
8249
8797
  }
@@ -8289,16 +8837,16 @@ var FilesystemTraceStore = class {
8289
8837
  return { trace, artifacts };
8290
8838
  }
8291
8839
  runDirectory(runId) {
8292
- return path6.join(this.runsDirectory, encodeURIComponent(runId));
8840
+ return path7.join(this.runsDirectory, encodeURIComponent(runId));
8293
8841
  }
8294
8842
  runEntriesDirectory(runId) {
8295
- return path6.join(this.runDirectory(runId), "entries");
8843
+ return path7.join(this.runDirectory(runId), "entries");
8296
8844
  }
8297
8845
  runManifestPath(runId) {
8298
- return path6.join(this.runDirectory(runId), "manifest.json");
8846
+ return path7.join(this.runDirectory(runId), "manifest.json");
8299
8847
  }
8300
8848
  runWriteLockPath(runId) {
8301
- return path6.join(this.runDirectory(runId), ".append.lock");
8849
+ return path7.join(this.runDirectory(runId), ".append.lock");
8302
8850
  }
8303
8851
  };
8304
8852
  function createTraceStore(rootPath, artifacts) {
@@ -8312,7 +8860,7 @@ function normalizeWorkspaceId(workspace) {
8312
8860
  return encodePathSegment(workspace);
8313
8861
  }
8314
8862
  function resolveFilesystemWorkspacePath(input) {
8315
- return path6.join(
8863
+ return path7.join(
8316
8864
  input.rootDir,
8317
8865
  ".opensteer",
8318
8866
  "workspaces",
@@ -8321,17 +8869,18 @@ function resolveFilesystemWorkspacePath(input) {
8321
8869
  }
8322
8870
  async function createFilesystemOpensteerWorkspace(options) {
8323
8871
  await ensureDirectory(options.rootPath);
8324
- const manifestPath = path6.join(options.rootPath, "workspace.json");
8325
- const browserPath = path6.join(options.rootPath, "browser");
8326
- const browserManifestPath = path6.join(browserPath, "manifest.json");
8327
- const browserUserDataDir = path6.join(browserPath, "user-data");
8328
- const livePath = path6.join(options.rootPath, "live");
8329
- const liveLocalPath = path6.join(livePath, "local.json");
8330
- const liveCloudPath = path6.join(livePath, "cloud.json");
8331
- const artifactsPath = path6.join(options.rootPath, "artifacts");
8332
- const tracesPath = path6.join(options.rootPath, "traces");
8333
- const registryPath = path6.join(options.rootPath, "registry");
8334
- 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");
8335
8884
  let manifest;
8336
8885
  if (await pathExists(manifestPath)) {
8337
8886
  manifest = await readJsonFile(manifestPath);
@@ -8345,6 +8894,17 @@ async function createFilesystemOpensteerWorkspace(options) {
8345
8894
  `workspace ${options.rootPath} uses unsupported version ${String(manifest.version)}`
8346
8895
  );
8347
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
+ }
8348
8908
  } else {
8349
8909
  const createdAt = normalizeTimestamp("createdAt", options.createdAt ?? Date.now());
8350
8910
  manifest = {
@@ -8359,6 +8919,7 @@ async function createFilesystemOpensteerWorkspace(options) {
8359
8919
  live: "live",
8360
8920
  artifacts: "artifacts",
8361
8921
  traces: "traces",
8922
+ observations: "observations",
8362
8923
  registry: "registry"
8363
8924
  }
8364
8925
  };
@@ -8370,6 +8931,7 @@ async function createFilesystemOpensteerWorkspace(options) {
8370
8931
  ensureDirectory(livePath),
8371
8932
  ensureDirectory(artifactsPath),
8372
8933
  ensureDirectory(tracesPath),
8934
+ ensureDirectory(observationsPath),
8373
8935
  ensureDirectory(registryPath)
8374
8936
  ]);
8375
8937
  const artifacts = createArtifactStore(options.rootPath);
@@ -8394,6 +8956,8 @@ async function createFilesystemOpensteerWorkspace(options) {
8394
8956
  await reverseReports.initialize();
8395
8957
  const traces = createTraceStore(options.rootPath, artifacts);
8396
8958
  await traces.initialize();
8959
+ const observations = createObservationStore(options.rootPath, artifacts);
8960
+ await observations.initialize();
8397
8961
  return {
8398
8962
  rootPath: options.rootPath,
8399
8963
  manifestPath,
@@ -8406,10 +8970,12 @@ async function createFilesystemOpensteerWorkspace(options) {
8406
8970
  liveCloudPath,
8407
8971
  artifactsPath,
8408
8972
  tracesPath,
8973
+ observationsPath,
8409
8974
  registryPath,
8410
8975
  lockPath,
8411
8976
  artifacts,
8412
8977
  traces,
8978
+ observations,
8413
8979
  registry: {
8414
8980
  descriptors,
8415
8981
  requestPlans,
@@ -8596,10 +9162,10 @@ function delayWithSignal(delayMs, signal) {
8596
9162
  if (signal.aborted) {
8597
9163
  return Promise.reject(signal.reason ?? abortError());
8598
9164
  }
8599
- return new Promise((resolve5, reject) => {
9165
+ return new Promise((resolve4, reject) => {
8600
9166
  const timer = setTimeout(() => {
8601
9167
  signal.removeEventListener("abort", onAbort);
8602
- resolve5();
9168
+ resolve4();
8603
9169
  }, delayMs);
8604
9170
  const onAbort = () => {
8605
9171
  clearTimeout(timer);
@@ -9064,9 +9630,9 @@ var IFRAME_URL_ATTRIBUTES = /* @__PURE__ */ new Set([
9064
9630
  "poster",
9065
9631
  "ping"
9066
9632
  ]);
9067
- function buildArrayFieldPathCandidates(path13) {
9068
- const strict = path13.nodes.length ? buildPathCandidates(path13.nodes) : [];
9069
- const relaxedNodes = stripPositionClauses(path13.nodes);
9633
+ function buildArrayFieldPathCandidates(path15) {
9634
+ const strict = path15.nodes.length ? buildPathCandidates(path15.nodes) : [];
9635
+ const relaxedNodes = stripPositionClauses(path15.nodes);
9070
9636
  const relaxed = relaxedNodes.length ? buildPathCandidates(relaxedNodes) : [];
9071
9637
  return dedupeSelectors([...strict, ...relaxed]);
9072
9638
  }
@@ -9596,18 +10162,18 @@ function cloneStructuralElementAnchor(anchor) {
9596
10162
  nodes: anchor.nodes.map(clonePathNode)
9597
10163
  };
9598
10164
  }
9599
- function cloneReplayElementPath(path13) {
10165
+ function cloneReplayElementPath(path15) {
9600
10166
  return {
9601
10167
  resolution: "deterministic",
9602
- context: cloneContext(path13.context),
9603
- nodes: path13.nodes.map(clonePathNode)
10168
+ context: cloneContext(path15.context),
10169
+ nodes: path15.nodes.map(clonePathNode)
9604
10170
  };
9605
10171
  }
9606
- function cloneElementPath(path13) {
9607
- return cloneReplayElementPath(path13);
10172
+ function cloneElementPath(path15) {
10173
+ return cloneReplayElementPath(path15);
9608
10174
  }
9609
- function buildPathSelectorHint(path13) {
9610
- const nodes = path13?.nodes || [];
10175
+ function buildPathSelectorHint(path15) {
10176
+ const nodes = path15?.nodes || [];
9611
10177
  const last = nodes[nodes.length - 1];
9612
10178
  if (!last) {
9613
10179
  return "*";
@@ -9656,15 +10222,15 @@ function sanitizeStructuralElementAnchor(anchor) {
9656
10222
  nodes: sanitizeNodes(anchor.nodes)
9657
10223
  };
9658
10224
  }
9659
- function sanitizeReplayElementPath(path13) {
10225
+ function sanitizeReplayElementPath(path15) {
9660
10226
  return {
9661
10227
  resolution: "deterministic",
9662
- context: sanitizeContext(path13.context),
9663
- nodes: sanitizeNodes(path13.nodes)
10228
+ context: sanitizeContext(path15.context),
10229
+ nodes: sanitizeNodes(path15.nodes)
9664
10230
  };
9665
10231
  }
9666
- function sanitizeElementPath(path13) {
9667
- return sanitizeReplayElementPath(path13);
10232
+ function sanitizeElementPath(path15) {
10233
+ return sanitizeReplayElementPath(path15);
9668
10234
  }
9669
10235
  function buildLocalStructuralElementAnchor(index, rawTargetNode) {
9670
10236
  const targetNode = requireElementNode(index, rawTargetNode);
@@ -9787,8 +10353,8 @@ function buildTargetNotFoundMessage(domPath, diagnostics) {
9787
10353
  }
9788
10354
  return `${base} Target depth ${String(depth)}. Candidate counts: ${sample}.`;
9789
10355
  }
9790
- function buildArrayFieldCandidates(path13) {
9791
- return buildArrayFieldPathCandidates(path13);
10356
+ function buildArrayFieldCandidates(path15) {
10357
+ return buildArrayFieldPathCandidates(path15);
9792
10358
  }
9793
10359
  function firstDefinedAttribute(node, keys) {
9794
10360
  for (const key of keys) {
@@ -10295,14 +10861,16 @@ var DomActionExecutor = class {
10295
10861
  ...input.timeout === void 0 ? {} : { timeout: input.timeout }
10296
10862
  },
10297
10863
  async (pointerTarget, point, timeout) => {
10298
- await timeout.runStep(
10864
+ const events = [];
10865
+ const moved = await timeout.runStep(
10299
10866
  () => this.options.engine.mouseMove({
10300
10867
  pageRef: pointerTarget.resolved.pageRef,
10301
10868
  point,
10302
10869
  coordinateSpace: "document-css"
10303
10870
  })
10304
10871
  );
10305
- await timeout.runStep(
10872
+ events.push(...moved.events);
10873
+ const clicked = await timeout.runStep(
10306
10874
  () => this.options.engine.mouseClick({
10307
10875
  pageRef: pointerTarget.resolved.pageRef,
10308
10876
  point,
@@ -10312,7 +10880,12 @@ var DomActionExecutor = class {
10312
10880
  ...input.modifiers === void 0 ? {} : { modifiers: input.modifiers }
10313
10881
  })
10314
10882
  );
10315
- return { resolved: pointerTarget.original, point };
10883
+ events.push(...clicked.events);
10884
+ return {
10885
+ resolved: pointerTarget.original,
10886
+ point,
10887
+ ...events.length === 0 ? {} : { events }
10888
+ };
10316
10889
  }
10317
10890
  );
10318
10891
  }
@@ -10326,14 +10899,18 @@ var DomActionExecutor = class {
10326
10899
  ...input.timeout === void 0 ? {} : { timeout: input.timeout }
10327
10900
  },
10328
10901
  async (pointerTarget, point, timeout) => {
10329
- await timeout.runStep(
10902
+ const moved = await timeout.runStep(
10330
10903
  () => this.options.engine.mouseMove({
10331
10904
  pageRef: pointerTarget.resolved.pageRef,
10332
10905
  point,
10333
10906
  coordinateSpace: "document-css"
10334
10907
  })
10335
10908
  );
10336
- return { resolved: pointerTarget.original, point };
10909
+ return {
10910
+ resolved: pointerTarget.original,
10911
+ point,
10912
+ ...moved.events.length === 0 ? {} : { events: moved.events }
10913
+ };
10337
10914
  }
10338
10915
  );
10339
10916
  }
@@ -10347,14 +10924,16 @@ var DomActionExecutor = class {
10347
10924
  ...input.timeout === void 0 ? {} : { timeout: input.timeout }
10348
10925
  },
10349
10926
  async (pointerTarget, point, timeout) => {
10350
- await timeout.runStep(
10927
+ const events = [];
10928
+ const moved = await timeout.runStep(
10351
10929
  () => this.options.engine.mouseMove({
10352
10930
  pageRef: pointerTarget.resolved.pageRef,
10353
10931
  point,
10354
10932
  coordinateSpace: "document-css"
10355
10933
  })
10356
10934
  );
10357
- await timeout.runStep(
10935
+ events.push(...moved.events);
10936
+ const scrolled = await timeout.runStep(
10358
10937
  () => this.options.engine.mouseScroll({
10359
10938
  pageRef: pointerTarget.resolved.pageRef,
10360
10939
  point,
@@ -10362,7 +10941,12 @@ var DomActionExecutor = class {
10362
10941
  delta: input.delta
10363
10942
  })
10364
10943
  );
10365
- return { resolved: pointerTarget.original, point };
10944
+ events.push(...scrolled.events);
10945
+ return {
10946
+ resolved: pointerTarget.original,
10947
+ point,
10948
+ ...events.length === 0 ? {} : { events }
10949
+ };
10366
10950
  }
10367
10951
  );
10368
10952
  }
@@ -11288,21 +11872,21 @@ var DefaultDomRuntime = class {
11288
11872
  return match;
11289
11873
  }
11290
11874
  async resolvePathTarget(session, pageRef, rawPath, source, description, descriptor) {
11291
- const path13 = sanitizeReplayElementPath(rawPath);
11292
- const context = await this.resolvePathContext(session, pageRef, path13.context);
11293
- 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);
11294
11878
  if (!target) {
11295
- throwTargetNotFound(context.index, path13.nodes, context.scope);
11879
+ throwTargetNotFound(context.index, path15.nodes, context.scope);
11296
11880
  }
11297
11881
  if (target.node.nodeRef === void 0) {
11298
11882
  throw new Error(
11299
- `resolved path "${buildPathSelectorHint(path13)}" does not point to a live element`
11883
+ `resolved path "${buildPathSelectorHint(path15)}" does not point to a live element`
11300
11884
  );
11301
11885
  }
11302
11886
  const anchor = await this.buildAnchorFromSnapshotNode(session, context.snapshot, target.node);
11303
11887
  return this.createResolvedTarget(source, context.snapshot, target.node, anchor, {
11304
11888
  ...description === void 0 ? {} : { description },
11305
- replayPath: path13,
11889
+ replayPath: path15,
11306
11890
  ...source === "path" || source === "descriptor" ? { selectorUsed: target.selector } : {},
11307
11891
  ...descriptor === void 0 ? {} : { descriptor }
11308
11892
  });
@@ -11323,9 +11907,9 @@ var DefaultDomRuntime = class {
11323
11907
  });
11324
11908
  }
11325
11909
  async queryAllByElementPath(session, pageRef, rawPath) {
11326
- const path13 = sanitizeReplayElementPath(rawPath);
11327
- const context = await this.resolvePathContext(session, pageRef, path13.context);
11328
- 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(
11329
11913
  (node) => node.nodeRef !== void 0
11330
11914
  ).map((node) => this.createSnapshotTarget(context.snapshot, node));
11331
11915
  }
@@ -11511,16 +12095,16 @@ var DefaultDomRuntime = class {
11511
12095
  const index = createSnapshotIndex(item.snapshot);
11512
12096
  return this.resolveFirstArrayFieldTargetInNode(index, item.node, field.path);
11513
12097
  }
11514
- resolveFirstArrayFieldTargetInNode(index, rootNode, path13) {
11515
- const normalizedPath = sanitizeElementPath(path13);
12098
+ resolveFirstArrayFieldTargetInNode(index, rootNode, path15) {
12099
+ const normalizedPath = sanitizeElementPath(path15);
11516
12100
  const selectors = buildArrayFieldCandidates(normalizedPath);
11517
12101
  if (!selectors.length) {
11518
12102
  return rootNode;
11519
12103
  }
11520
12104
  return resolveFirstWithinNodeBySelectors(index, rootNode, selectors);
11521
12105
  }
11522
- resolveUniqueArrayFieldTargetInNode(index, rootNode, path13) {
11523
- const normalizedPath = sanitizeElementPath(path13);
12106
+ resolveUniqueArrayFieldTargetInNode(index, rootNode, path15) {
12107
+ const normalizedPath = sanitizeElementPath(path15);
11524
12108
  const selectors = buildArrayFieldCandidates(normalizedPath);
11525
12109
  if (!selectors.length) {
11526
12110
  return rootNode;
@@ -11609,8 +12193,8 @@ function encodeDataPath(tokens) {
11609
12193
  }
11610
12194
  return out;
11611
12195
  }
11612
- function parseDataPath(path13) {
11613
- const input = path13.trim();
12196
+ function parseDataPath(path15) {
12197
+ const input = path15.trim();
11614
12198
  if (input.length === 0) {
11615
12199
  return [];
11616
12200
  }
@@ -11660,8 +12244,8 @@ function parseDataPath(path13) {
11660
12244
  function inflateDataPathObject(flat) {
11661
12245
  let root = {};
11662
12246
  let initialized = false;
11663
- for (const [path13, value] of Object.entries(flat)) {
11664
- const tokens = parseDataPath(path13);
12247
+ for (const [path15, value] of Object.entries(flat)) {
12248
+ const tokens = parseDataPath(path15);
11665
12249
  if (!tokens || tokens.length === 0) {
11666
12250
  continue;
11667
12251
  }
@@ -11993,8 +12577,8 @@ function buildVariantDescriptorFromCluster(descriptors) {
11993
12577
  fields: mergedFields
11994
12578
  };
11995
12579
  }
11996
- function minimizePathMatchClauses(path13, mode) {
11997
- const normalized = sanitizeElementPath(path13);
12580
+ function minimizePathMatchClauses(path15, mode) {
12581
+ const normalized = sanitizeElementPath(path15);
11998
12582
  const nodes = normalized.nodes.map((node, index) => {
11999
12583
  const isLast = index === normalized.nodes.length - 1;
12000
12584
  const attrs = node.attrs || {};
@@ -12098,8 +12682,8 @@ function seedMinimalAttrClause(attrs) {
12098
12682
  }
12099
12683
  return null;
12100
12684
  }
12101
- function relaxPathForSingleSample(path13, mode) {
12102
- const normalized = sanitizeElementPath(path13);
12685
+ function relaxPathForSingleSample(path15, mode) {
12686
+ const normalized = sanitizeElementPath(path15);
12103
12687
  const relaxedNodes = normalized.nodes.map((node, index) => {
12104
12688
  const isLast = index === normalized.nodes.length - 1;
12105
12689
  const attrs = normalizeAttrsForSingleSample(node.attrs || {});
@@ -12184,8 +12768,8 @@ function shouldKeepAttrForSingleSample(key) {
12184
12768
  }
12185
12769
  return true;
12186
12770
  }
12187
- function buildPathStructureKey(path13) {
12188
- const normalized = sanitizeElementPath(path13);
12771
+ function buildPathStructureKey(path15) {
12772
+ const normalized = sanitizeElementPath(path15);
12189
12773
  return canonicalJsonString({
12190
12774
  context: (normalized.context || []).map((hop) => ({
12191
12775
  kind: hop.kind,
@@ -12312,30 +12896,30 @@ function buildArrayItemNode(fields) {
12312
12896
  }
12313
12897
  return node;
12314
12898
  }
12315
- function insertNodeAtPath(root, path13, node) {
12316
- const tokens = parseDataPath(path13);
12899
+ function insertNodeAtPath(root, path15, node) {
12900
+ const tokens = parseDataPath(path15);
12317
12901
  if (!tokens || !tokens.length) {
12318
12902
  throw new Error(
12319
- `Invalid persisted extraction path "${path13}": expected a non-empty object path.`
12903
+ `Invalid persisted extraction path "${path15}": expected a non-empty object path.`
12320
12904
  );
12321
12905
  }
12322
12906
  if (tokens.some((token) => token.kind === "index")) {
12323
12907
  throw new Error(
12324
- `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.`
12325
12909
  );
12326
12910
  }
12327
12911
  let current = root;
12328
12912
  for (let index = 0; index < tokens.length; index += 1) {
12329
12913
  const token = tokens[index];
12330
12914
  if (!token || token.kind !== "prop") {
12331
- throw new Error(`Invalid persisted extraction path "${path13}": expected object segment.`);
12915
+ throw new Error(`Invalid persisted extraction path "${path15}": expected object segment.`);
12332
12916
  }
12333
12917
  const isLast = index === tokens.length - 1;
12334
12918
  if (isLast) {
12335
12919
  const existing = current[token.key];
12336
12920
  if (existing) {
12337
12921
  throw new Error(
12338
- `Conflicting persisted extraction path "${path13}" detected while building descriptor tree.`
12922
+ `Conflicting persisted extraction path "${path15}" detected while building descriptor tree.`
12339
12923
  );
12340
12924
  }
12341
12925
  current[token.key] = node;
@@ -12350,7 +12934,7 @@ function insertNodeAtPath(root, path13, node) {
12350
12934
  }
12351
12935
  if (!isPersistedObjectNode(next)) {
12352
12936
  throw new Error(
12353
- `Conflicting persisted extraction path "${path13}" detected at "${token.key}".`
12937
+ `Conflicting persisted extraction path "${path15}" detected at "${token.key}".`
12354
12938
  );
12355
12939
  }
12356
12940
  current = next;
@@ -12385,7 +12969,7 @@ function buildItemRootForArrayIndex(entries) {
12385
12969
  }
12386
12970
  const paths = entries.map(
12387
12971
  (entry) => isPersistablePathField(entry.source) ? sanitizeElementPath(entry.source.path) : null
12388
- ).filter((path13) => path13 !== null);
12972
+ ).filter((path15) => path15 !== null);
12389
12973
  if (!paths.length) {
12390
12974
  return null;
12391
12975
  }
@@ -12406,7 +12990,7 @@ function getCommonPathPrefixLength(paths) {
12406
12990
  if (!paths.length) {
12407
12991
  return 0;
12408
12992
  }
12409
- const nodeChains = paths.map((path13) => path13.nodes);
12993
+ const nodeChains = paths.map((path15) => path15.nodes);
12410
12994
  const minLength = Math.min(...nodeChains.map((nodes) => nodes.length));
12411
12995
  if (!Number.isFinite(minLength) || minLength <= 0) {
12412
12996
  return 0;
@@ -12475,30 +13059,30 @@ function mergeElementPathsByMajority(paths) {
12475
13059
  if (!paths.length) {
12476
13060
  return null;
12477
13061
  }
12478
- const normalized = paths.map((path13) => sanitizeElementPath(path13));
13062
+ const normalized = paths.map((path15) => sanitizeElementPath(path15));
12479
13063
  const contextKey = pickModeString(
12480
- normalized.map((path13) => canonicalJsonString(path13.context)),
13064
+ normalized.map((path15) => canonicalJsonString(path15.context)),
12481
13065
  1
12482
13066
  );
12483
13067
  if (!contextKey) {
12484
13068
  return null;
12485
13069
  }
12486
- const sameContext = normalized.filter((path13) => canonicalJsonString(path13.context) === contextKey);
13070
+ const sameContext = normalized.filter((path15) => canonicalJsonString(path15.context) === contextKey);
12487
13071
  if (!sameContext.length) {
12488
13072
  return null;
12489
13073
  }
12490
13074
  const targetLength = pickModeNumber(
12491
- sameContext.map((path13) => path13.nodes.length),
13075
+ sameContext.map((path15) => path15.nodes.length),
12492
13076
  1
12493
13077
  ) ?? sameContext[0]?.nodes.length ?? 0;
12494
- const aligned = sameContext.filter((path13) => path13.nodes.length === targetLength);
13078
+ const aligned = sameContext.filter((path15) => path15.nodes.length === targetLength);
12495
13079
  if (!aligned.length) {
12496
13080
  return null;
12497
13081
  }
12498
13082
  const threshold = majorityThreshold(aligned.length);
12499
13083
  const nodes = [];
12500
13084
  for (let index = 0; index < targetLength; index += 1) {
12501
- 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);
12502
13086
  if (!nodesAtIndex.length) {
12503
13087
  return null;
12504
13088
  }
@@ -12744,8 +13328,8 @@ function clonePathContext(context) {
12744
13328
  function clonePathNodes(nodes) {
12745
13329
  return JSON.parse(JSON.stringify(nodes || []));
12746
13330
  }
12747
- function cloneElementPath2(path13) {
12748
- return JSON.parse(JSON.stringify(path13));
13331
+ function cloneElementPath2(path15) {
13332
+ return JSON.parse(JSON.stringify(path15));
12749
13333
  }
12750
13334
  function clonePersistedOpensteerExtractionNode(node) {
12751
13335
  return JSON.parse(JSON.stringify(node));
@@ -13063,8 +13647,8 @@ function collectPersistedValueNodeRefs(node) {
13063
13647
  return [
13064
13648
  {
13065
13649
  path: sanitizeElementPath(node.$path),
13066
- replacePath: (path13) => {
13067
- node.$path = sanitizeElementPath(path13);
13650
+ replacePath: (path15) => {
13651
+ node.$path = sanitizeElementPath(path15);
13068
13652
  }
13069
13653
  }
13070
13654
  ];
@@ -13078,13 +13662,13 @@ function collectPersistedValueNodeRefs(node) {
13078
13662
  }
13079
13663
  return refs;
13080
13664
  }
13081
- function hasPositionClause(path13) {
13082
- 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"));
13083
13667
  }
13084
- function stripPositionClauses2(path13) {
13668
+ function stripPositionClauses2(path15) {
13085
13669
  return sanitizeElementPath({
13086
- context: path13.context,
13087
- nodes: path13.nodes.map((node) => ({
13670
+ context: path15.context,
13671
+ nodes: path15.nodes.map((node) => ({
13088
13672
  ...node,
13089
13673
  match: node.match.filter((clause) => clause.kind !== "position")
13090
13674
  }))
@@ -13494,14 +14078,13 @@ function normalizeNonEmptyString2(name, value) {
13494
14078
  function normalizeKey(value) {
13495
14079
  return String(value ?? "").trim();
13496
14080
  }
13497
- function labelForPath(path13) {
13498
- return path13.trim().length === 0 ? "$" : path13;
14081
+ function labelForPath(path15) {
14082
+ return path15.trim().length === 0 ? "$" : path15;
13499
14083
  }
13500
14084
  function sha256Hex3(value) {
13501
14085
  return createHash("sha256").update(value).digest("hex");
13502
14086
  }
13503
- var PROCESS_LIST_MAX_BUFFER_BYTES = 16 * 1024 * 1024;
13504
- var PS_COMMAND_ENV = { ...process.env, LC_ALL: "C" };
14087
+ ({ ...process.env});
13505
14088
  var WINDOWS_PROGRAM_FILES = process.env.PROGRAMFILES ?? "C:\\Program Files";
13506
14089
  var WINDOWS_PROGRAM_FILES_X86 = process.env["PROGRAMFILES(X86)"] ?? "C:\\Program Files (x86)";
13507
14090
  var BROWSER_BRANDS = [
@@ -13687,9 +14270,6 @@ var BROWSER_BRANDS = [
13687
14270
  }
13688
14271
  }
13689
14272
  ];
13690
- function getAllBrowserBrands() {
13691
- return BROWSER_BRANDS;
13692
- }
13693
14273
  function getBrowserBrand(id) {
13694
14274
  const brand2 = BROWSER_BRANDS.find((candidate) => candidate.id === id);
13695
14275
  if (!brand2) {
@@ -13732,28 +14312,6 @@ function detectInstalledBrowserBrands() {
13732
14312
  }
13733
14313
  return installations;
13734
14314
  }
13735
- function resolveBrandExecutablePath(brand2, explicitPath) {
13736
- if (explicitPath !== void 0) {
13737
- const resolvedPath2 = resolve(expandHome(explicitPath));
13738
- if (!existsSync(resolvedPath2)) {
13739
- throw new Error(`${brand2.displayName} executable was not found at "${resolvedPath2}".`);
13740
- }
13741
- return resolvedPath2;
13742
- }
13743
- const platformConfig = resolveBrandPlatformConfig(brand2);
13744
- if (!platformConfig) {
13745
- throw new Error(`${brand2.displayName} is not supported on ${process.platform}.`);
13746
- }
13747
- const resolvedPath = firstExistingPath(
13748
- resolveExecutableCandidates(platformConfig.executableCandidates)
13749
- );
13750
- if (!resolvedPath) {
13751
- throw new Error(
13752
- `Could not find a ${brand2.displayName} executable. Pass --executable-path or browser.executablePath.`
13753
- );
13754
- }
13755
- return resolvedPath;
13756
- }
13757
14315
  function resolveBrandUserDataDir(brand2, explicitDir) {
13758
14316
  if (explicitDir !== void 0) {
13759
14317
  return resolve(expandHome(explicitDir));
@@ -13764,129 +14322,9 @@ function resolveBrandUserDataDir(brand2, explicitDir) {
13764
14322
  }
13765
14323
  return resolve(expandHome(platformConfig.userDataDir));
13766
14324
  }
13767
- function isBrandProcess(brand2, commandLine) {
13768
- const normalizedCommand = normalizeCommand(commandLine);
13769
- if (!normalizedCommand) {
13770
- return false;
13771
- }
13772
- if (normalizedCommand.includes("crashpad_handler")) {
13773
- return false;
13774
- }
13775
- if (/\s--type=/.test(normalizedCommand)) {
13776
- return false;
13777
- }
13778
- return getBrandProcessMarkers(brand2).some((marker) => normalizedCommand.includes(marker));
13779
- }
13780
- function findBrandProcess(brand2) {
13781
- for (const processEntry of listProcesses()) {
13782
- if (isBrandProcess(brand2, processEntry.commandLine)) {
13783
- return { pid: processEntry.pid };
13784
- }
13785
- }
13786
- return null;
13787
- }
13788
- function getBrandProcessMarkers(brand2) {
13789
- const markers = /* @__PURE__ */ new Set();
13790
- for (const config of [brand2.darwin, brand2.win32, brand2.linux]) {
13791
- if (!config) {
13792
- continue;
13793
- }
13794
- for (const processName of config.processNames) {
13795
- const normalized = normalizeCommand(processName);
13796
- if (normalized) {
13797
- markers.add(normalized);
13798
- }
13799
- }
13800
- for (const candidate of config.executableCandidates) {
13801
- if (!candidate) {
13802
- continue;
13803
- }
13804
- const normalized = normalizeCommand(resolve(expandHome(candidate)));
13805
- if (normalized) {
13806
- markers.add(normalized);
13807
- }
13808
- }
13809
- }
13810
- return [...markers];
13811
- }
13812
14325
  function resolveExecutableCandidates(candidates) {
13813
14326
  return candidates.map((candidate) => candidate ? resolve(expandHome(candidate)) : null);
13814
14327
  }
13815
- function listProcesses() {
13816
- if (process.platform === "win32") {
13817
- return listWindowsProcesses();
13818
- }
13819
- return listUnixProcesses();
13820
- }
13821
- function listUnixProcesses() {
13822
- try {
13823
- const output = execFileSync("ps", ["-A", "-o", "pid=,command="], {
13824
- encoding: "utf8",
13825
- env: PS_COMMAND_ENV,
13826
- maxBuffer: PROCESS_LIST_MAX_BUFFER_BYTES,
13827
- stdio: ["ignore", "pipe", "ignore"]
13828
- });
13829
- return output.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0).map((line) => {
13830
- const match = /^(\d+)\s+(.*)$/.exec(line);
13831
- if (!match) {
13832
- return null;
13833
- }
13834
- const pid = Number.parseInt(match[1] ?? "", 10);
13835
- const commandLine = match[2]?.trim() ?? "";
13836
- if (!Number.isInteger(pid) || pid <= 0 || commandLine.length === 0) {
13837
- return null;
13838
- }
13839
- return {
13840
- pid,
13841
- commandLine
13842
- };
13843
- }).filter(
13844
- (entry) => entry !== null
13845
- ).sort((left, right) => left.pid - right.pid);
13846
- } catch {
13847
- return [];
13848
- }
13849
- }
13850
- function listWindowsProcesses() {
13851
- try {
13852
- const output = execFileSync(
13853
- "powershell.exe",
13854
- [
13855
- "-NoProfile",
13856
- "-Command",
13857
- "Get-CimInstance Win32_Process | Select-Object ProcessId,CommandLine | ConvertTo-Json -Compress"
13858
- ],
13859
- {
13860
- encoding: "utf8",
13861
- maxBuffer: PROCESS_LIST_MAX_BUFFER_BYTES,
13862
- stdio: ["ignore", "pipe", "ignore"]
13863
- }
13864
- ).trim();
13865
- if (!output) {
13866
- return [];
13867
- }
13868
- const parsed = JSON.parse(output);
13869
- const records = Array.isArray(parsed) ? parsed : [parsed];
13870
- return records.map((record) => {
13871
- const pid = Number(record.ProcessId);
13872
- const commandLine = typeof record.CommandLine === "string" ? record.CommandLine.trim() : "";
13873
- if (!Number.isInteger(pid) || pid <= 0 || commandLine.length === 0) {
13874
- return null;
13875
- }
13876
- return {
13877
- pid,
13878
- commandLine
13879
- };
13880
- }).filter(
13881
- (entry) => entry !== null
13882
- ).sort((left, right) => left.pid - right.pid);
13883
- } catch {
13884
- return [];
13885
- }
13886
- }
13887
- function normalizeCommand(value) {
13888
- return value.trim().replaceAll("\\", "/").toLowerCase();
13889
- }
13890
14328
 
13891
14329
  // src/local-browser/chrome-discovery.ts
13892
14330
  function expandHome(value) {
@@ -14253,8 +14691,8 @@ function buildBrowserWebSocketUrl(httpUrl, webSocketPath) {
14253
14691
  const protocol = httpUrl.protocol === "https:" ? "wss:" : "ws:";
14254
14692
  return `${protocol}//${httpUrl.host}${normalizeWebSocketPath(webSocketPath)}`;
14255
14693
  }
14256
- function normalizeWebSocketPath(path13) {
14257
- return path13.startsWith("/") ? path13 : `/${path13}`;
14694
+ function normalizeWebSocketPath(path15) {
14695
+ return path15.startsWith("/") ? path15 : `/${path15}`;
14258
14696
  }
14259
14697
  function rewriteBrowserWebSocketHost(browserWsUrl, requestedUrl) {
14260
14698
  try {
@@ -14274,7 +14712,7 @@ function readPort(url) {
14274
14712
  var OPENSTEER_LIVE_SESSION_LAYOUT = "opensteer-session";
14275
14713
  var OPENSTEER_LIVE_SESSION_VERSION = 1;
14276
14714
  function resolveLiveSessionRecordPath(rootPath, provider) {
14277
- return path6.join(rootPath, "live", provider === "local" ? "local.json" : "cloud.json");
14715
+ return path7.join(rootPath, "live", provider === "local" ? "local.json" : "cloud.json");
14278
14716
  }
14279
14717
  function resolveLocalSessionRecordPath(rootPath) {
14280
14718
  return resolveLiveSessionRecordPath(rootPath, "local");
@@ -15079,8 +15517,8 @@ var OpensteerBrowserManager = class {
15079
15517
  ...options.browser === void 0 ? {} : { browser: options.browser },
15080
15518
  ...this.contextOptions === void 0 ? {} : { context: this.contextOptions }
15081
15519
  });
15082
- this.rootPath = options.rootPath ?? (this.workspace === void 0 ? path6.join(tmpdir(), `${TEMPORARY_WORKSPACE_PREFIX}${randomUUID()}`) : resolveFilesystemWorkspacePath({
15083
- 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()),
15084
15522
  workspace: this.workspace
15085
15523
  }));
15086
15524
  this.cleanupRootOnDisconnect = this.workspace === void 0;
@@ -15150,7 +15588,7 @@ var OpensteerBrowserManager = class {
15150
15588
  userDataDir: "browser/user-data",
15151
15589
  bootstrap: {
15152
15590
  kind: "cloneLocalProfile",
15153
- sourceUserDataDir: path6.resolve(input.sourceUserDataDir),
15591
+ sourceUserDataDir: path7.resolve(input.sourceUserDataDir),
15154
15592
  ...input.sourceProfileDirectory === void 0 ? {} : { sourceProfileDirectory: input.sourceProfileDirectory }
15155
15593
  }
15156
15594
  };
@@ -15277,7 +15715,7 @@ var OpensteerBrowserManager = class {
15277
15715
  });
15278
15716
  }
15279
15717
  async createTemporaryEngine() {
15280
- const userDataDir = await mkdtemp(path6.join(tmpdir(), "opensteer-temporary-browser-"));
15718
+ const userDataDir = await mkdtemp(path7.join(tmpdir(), "opensteer-temporary-browser-"));
15281
15719
  await clearChromeSingletonEntries(userDataDir);
15282
15720
  const launched = await launchOwnedBrowser({
15283
15721
  userDataDir,
@@ -15724,7 +16162,7 @@ async function terminateProcess(pid) {
15724
16162
  }
15725
16163
  }
15726
16164
  async function requestBrowserClose(endpoint) {
15727
- await new Promise((resolve5, reject) => {
16165
+ await new Promise((resolve4, reject) => {
15728
16166
  const socket = new WebSocket(endpoint);
15729
16167
  const timeout = setTimeout(() => {
15730
16168
  socket.close();
@@ -15741,7 +16179,7 @@ async function requestBrowserClose(endpoint) {
15741
16179
  reject(error);
15742
16180
  return;
15743
16181
  }
15744
- resolve5();
16182
+ resolve4();
15745
16183
  };
15746
16184
  socket.addEventListener("open", () => {
15747
16185
  socket.send(JSON.stringify({ id: 1, method: "Browser.close" }));
@@ -15779,7 +16217,7 @@ async function waitForProcessExit(pid, timeoutMs) {
15779
16217
  return !isProcessRunning(pid);
15780
16218
  }
15781
16219
  function resolveAbpSessionDir(workspace) {
15782
- return path6.join(workspace.livePath, "abp-session");
16220
+ return path7.join(workspace.livePath, "abp-session");
15783
16221
  }
15784
16222
  async function allocateEphemeralPort() {
15785
16223
  const { allocatePort } = await loadAbpModule();
@@ -15837,7 +16275,7 @@ function isStealthProfile(input) {
15837
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;
15838
16276
  }
15839
16277
  async function sleep(ms) {
15840
- await new Promise((resolve5) => setTimeout(resolve5, ms));
16278
+ await new Promise((resolve4) => setTimeout(resolve4, ms));
15841
16279
  }
15842
16280
 
15843
16281
  // ../runtime-core/src/sdk/semantic-dispatch.ts
@@ -17451,7 +17889,7 @@ function diffStringMap(prefix, left, right, includeUnchanged, output) {
17451
17889
  diffScalarField(`${prefix}.${key}`, left[key], right[key], includeUnchanged, output);
17452
17890
  }
17453
17891
  }
17454
- function diffScalarField(path13, left, right, includeUnchanged, output) {
17892
+ function diffScalarField(path15, left, right, includeUnchanged, output) {
17455
17893
  const leftValue = stringifyFieldValue(left);
17456
17894
  const rightValue = stringifyFieldValue(right);
17457
17895
  const kind = leftValue === void 0 ? rightValue === void 0 ? "unchanged" : "added" : rightValue === void 0 ? "removed" : leftValue === rightValue ? "unchanged" : "changed";
@@ -17459,7 +17897,7 @@ function diffScalarField(path13, left, right, includeUnchanged, output) {
17459
17897
  return;
17460
17898
  }
17461
17899
  output.push({
17462
- path: path13,
17900
+ path: path15,
17463
17901
  kind,
17464
17902
  ...leftValue === void 0 ? {} : { leftValue },
17465
17903
  ...rightValue === void 0 ? {} : { rightValue },
@@ -18465,9 +18903,9 @@ function matchReverseTargetHints(channel, codec, targetHints) {
18465
18903
  matches.add(`host:${host}`);
18466
18904
  }
18467
18905
  }
18468
- for (const path13 of targetHints.paths ?? []) {
18469
- if (url.pathname.includes(path13)) {
18470
- matches.add(`path:${path13}`);
18906
+ for (const path15 of targetHints.paths ?? []) {
18907
+ if (url.pathname.includes(path15)) {
18908
+ matches.add(`path:${path15}`);
18471
18909
  }
18472
18910
  }
18473
18911
  for (const operationName of targetHints.operationNames ?? []) {
@@ -19428,11 +19866,11 @@ function inferClusterRelationship(seed, record) {
19428
19866
  var MATCHED_TLS_BINARY_NAMES = ["curl-impersonate-chrome", "curl_chrome"];
19429
19867
  async function executeMatchedTlsTransportRequest(input) {
19430
19868
  const binary = await resolveMatchedTlsBinary();
19431
- const workingDirectory = await mkdtemp(path6.join(tmpdir(), "opensteer-matched-tls-"));
19432
- const headersPath = path6.join(workingDirectory, "headers.txt");
19433
- const bodyPath = path6.join(workingDirectory, "body.bin");
19434
- const cookiesPath = path6.join(workingDirectory, "cookies.txt");
19435
- 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");
19436
19874
  try {
19437
19875
  await writeFile(cookiesPath, toNetscapeCookieJar(input.cookies ?? []), "utf8");
19438
19876
  if (input.request.body !== void 0) {
@@ -19489,10 +19927,10 @@ async function executeMatchedTlsTransportRequest(input) {
19489
19927
  }
19490
19928
  }
19491
19929
  async function resolveMatchedTlsBinary() {
19492
- 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);
19493
19931
  for (const directory of pathEntries) {
19494
19932
  for (const name of MATCHED_TLS_BINARY_NAMES) {
19495
- const candidate = path6.join(directory, name);
19933
+ const candidate = path7.join(directory, name);
19496
19934
  if (await isExecutable(candidate)) {
19497
19935
  return candidate;
19498
19936
  }
@@ -19500,7 +19938,7 @@ async function resolveMatchedTlsBinary() {
19500
19938
  const files = await readDirSafe(directory);
19501
19939
  const discovered = files.find((file) => file.startsWith("curl_chrome"));
19502
19940
  if (discovered !== void 0) {
19503
- const candidate = path6.join(directory, discovered);
19941
+ const candidate = path7.join(directory, discovered);
19504
19942
  if (await isExecutable(candidate)) {
19505
19943
  return candidate;
19506
19944
  }
@@ -19511,7 +19949,7 @@ async function resolveMatchedTlsBinary() {
19511
19949
  );
19512
19950
  }
19513
19951
  async function spawnAndCollect(command, args, signal) {
19514
- return await new Promise((resolve5, reject) => {
19952
+ return await new Promise((resolve4, reject) => {
19515
19953
  const child = spawn(command, args, {
19516
19954
  stdio: ["ignore", "pipe", "pipe"]
19517
19955
  });
@@ -19544,7 +19982,7 @@ async function spawnAndCollect(command, args, signal) {
19544
19982
  );
19545
19983
  return;
19546
19984
  }
19547
- resolve5({ stdout, stderr });
19985
+ resolve4({ stdout, stderr });
19548
19986
  });
19549
19987
  });
19550
19988
  }
@@ -22206,8 +22644,8 @@ function readString(value) {
22206
22644
  return typeof value === "string" && value.length > 0 ? value : void 0;
22207
22645
  }
22208
22646
  function sleep2(ms, signal) {
22209
- return new Promise((resolve5, reject) => {
22210
- const timeout = setTimeout(resolve5, ms);
22647
+ return new Promise((resolve4, reject) => {
22648
+ const timeout = setTimeout(resolve4, ms);
22211
22649
  const abort = () => {
22212
22650
  clearTimeout(timeout);
22213
22651
  reject(new Error("captcha solve aborted"));
@@ -22304,8 +22742,8 @@ function readString2(value) {
22304
22742
  return typeof value === "string" && value.length > 0 ? value : void 0;
22305
22743
  }
22306
22744
  function sleep3(ms, signal) {
22307
- return new Promise((resolve5, reject) => {
22308
- const timeout = setTimeout(resolve5, ms);
22745
+ return new Promise((resolve4, reject) => {
22746
+ const timeout = setTimeout(resolve4, ms);
22309
22747
  const abort = () => {
22310
22748
  clearTimeout(timeout);
22311
22749
  reject(new Error("captcha solve aborted"));
@@ -22504,6 +22942,8 @@ function diffInteractionTraces(left, right) {
22504
22942
  // ../runtime-core/src/sdk/runtime.ts
22505
22943
  var requireForAuthRecipeHook = createRequire(import.meta.url);
22506
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;
22507
22947
  var OpensteerSessionRuntime = class {
22508
22948
  workspace;
22509
22949
  rootPath;
@@ -22516,6 +22956,9 @@ var OpensteerSessionRuntime = class {
22516
22956
  registryOverrides;
22517
22957
  cleanupRootOnClose;
22518
22958
  sessionInfoBase;
22959
+ observationConfig;
22960
+ observationSessionId;
22961
+ injectedObservationSink;
22519
22962
  root;
22520
22963
  engine;
22521
22964
  dom;
@@ -22525,6 +22968,9 @@ var OpensteerSessionRuntime = class {
22525
22968
  sessionRef;
22526
22969
  pageRef;
22527
22970
  runId;
22971
+ observations;
22972
+ operationEventStorage = new AsyncLocalStorage();
22973
+ pendingOperationEventCaptures = [];
22528
22974
  cookieJars = /* @__PURE__ */ new Map();
22529
22975
  recipeCache = /* @__PURE__ */ new Map();
22530
22976
  ownsEngine = false;
@@ -22532,7 +22978,7 @@ var OpensteerSessionRuntime = class {
22532
22978
  this.workspace = normalizeNamespace2(options.name);
22533
22979
  this.workspaceName = options.workspaceName?.trim() === void 0 || options.workspaceName?.trim().length === 0 ? void 0 : options.workspaceName.trim();
22534
22980
  this.root = options.workspace;
22535
- 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());
22536
22982
  this.injectedEngine = options.engine;
22537
22983
  this.engineFactory = options.engineFactory;
22538
22984
  this.policy = options.policy ?? defaultPolicy();
@@ -22541,6 +22987,9 @@ var OpensteerSessionRuntime = class {
22541
22987
  this.registryOverrides = options.registryOverrides;
22542
22988
  this.cleanupRootOnClose = options.cleanupRootOnClose ?? options.workspace === void 0;
22543
22989
  this.sessionInfoBase = options.sessionInfo ?? {};
22990
+ this.observationConfig = normalizeObservabilityConfig(options.observability);
22991
+ this.observationSessionId = options.observationSessionId;
22992
+ this.injectedObservationSink = options.observationSink;
22544
22993
  if (this.injectedEngine === void 0 && this.engineFactory === void 0) {
22545
22994
  throw new Error("OpensteerSessionRuntime requires an engine or engineFactory.");
22546
22995
  }
@@ -22572,6 +23021,20 @@ var OpensteerSessionRuntime = class {
22572
23021
  }
22573
23022
  };
22574
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
+ }
22575
23038
  async open(input = {}, options = {}) {
22576
23039
  assertValidSemanticOperationInput("session.open", input);
22577
23040
  if (input.workspace !== void 0 && normalizeNamespace2(input.workspace) !== this.workspace) {
@@ -22670,6 +23133,10 @@ var OpensteerSessionRuntime = class {
22670
23133
  return { pages: [] };
22671
23134
  }
22672
23135
  const startedAt = Date.now();
23136
+ const context = buildRuntimeTraceContext({
23137
+ sessionRef: this.sessionRef,
23138
+ pageRef: this.pageRef
23139
+ });
22673
23140
  try {
22674
23141
  const output = await this.runWithOperationTimeout(
22675
23142
  "page.list",
@@ -22684,19 +23151,18 @@ var OpensteerSessionRuntime = class {
22684
23151
  },
22685
23152
  options
22686
23153
  );
23154
+ const events = await this.drainPendingEngineEvents(context);
22687
23155
  await this.appendTrace({
22688
23156
  operation: "page.list",
22689
23157
  startedAt,
22690
23158
  completedAt: Date.now(),
22691
23159
  outcome: "ok",
23160
+ ...events === void 0 ? {} : { events },
22692
23161
  data: {
22693
23162
  count: output.pages.length,
22694
23163
  ...output.activePageRef === void 0 ? {} : { activePageRef: output.activePageRef }
22695
23164
  },
22696
- context: buildRuntimeTraceContext({
22697
- sessionRef: this.sessionRef,
22698
- pageRef: this.pageRef
22699
- })
23165
+ context
22700
23166
  });
22701
23167
  return output;
22702
23168
  } catch (error) {
@@ -22706,10 +23172,7 @@ var OpensteerSessionRuntime = class {
22706
23172
  completedAt: Date.now(),
22707
23173
  outcome: "error",
22708
23174
  error,
22709
- context: buildRuntimeTraceContext({
22710
- sessionRef: this.sessionRef,
22711
- pageRef: this.pageRef
22712
- })
23175
+ context
22713
23176
  });
22714
23177
  throw error;
22715
23178
  }
@@ -23097,22 +23560,25 @@ var OpensteerSessionRuntime = class {
23097
23560
  },
23098
23561
  options
23099
23562
  );
23563
+ const context = buildRuntimeTraceContext({
23564
+ sessionRef: this.sessionRef,
23565
+ pageRef
23566
+ });
23567
+ const events = await this.drainPendingEngineEvents(context);
23100
23568
  await this.appendTrace({
23101
23569
  operation: "page.snapshot",
23102
23570
  startedAt,
23103
23571
  completedAt: Date.now(),
23104
23572
  outcome: "ok",
23105
23573
  artifacts,
23574
+ ...events === void 0 ? {} : { events },
23106
23575
  data: {
23107
23576
  mode,
23108
23577
  url: output.url,
23109
23578
  title: output.title,
23110
23579
  counterCount: output.counters.length
23111
23580
  },
23112
- context: buildRuntimeTraceContext({
23113
- sessionRef: this.sessionRef,
23114
- pageRef
23115
- })
23581
+ context
23116
23582
  });
23117
23583
  return output;
23118
23584
  } catch (error) {
@@ -26537,11 +27003,13 @@ var OpensteerSessionRuntime = class {
26537
27003
  }
26538
27004
  );
26539
27005
  const output = toOpensteerActionResult(executed.result, preparedTarget.persistedDescription);
27006
+ const actionEvents = "events" in executed.result ? executed.result.events : void 0;
26540
27007
  await this.appendTrace({
26541
27008
  operation,
26542
27009
  startedAt,
26543
27010
  completedAt: Date.now(),
26544
27011
  outcome: "ok",
27012
+ ...actionEvents === void 0 ? {} : { events: actionEvents },
26545
27013
  data: {
26546
27014
  target: output.target,
26547
27015
  ...output.point === void 0 ? {} : { point: output.point },
@@ -28291,7 +28759,7 @@ var OpensteerSessionRuntime = class {
28291
28759
  }
28292
28760
  async executeAuthRecipeHook(step, variables) {
28293
28761
  const resolved = requireForAuthRecipeHook.resolve(step.hook.specifier, {
28294
- paths: [path6.dirname(this.rootPath)]
28762
+ paths: [path7.dirname(this.rootPath)]
28295
28763
  });
28296
28764
  const module = await import(pathToFileURL(resolved).href);
28297
28765
  const handler = module[step.hook.export];
@@ -28663,14 +29131,18 @@ var OpensteerSessionRuntime = class {
28663
29131
  return this.engine;
28664
29132
  }
28665
29133
  if (this.injectedEngine) {
28666
- this.engine = this.injectedEngine;
29134
+ this.engine = this.wrapEngineWithObservationCapture(
29135
+ this.injectedEngine
29136
+ );
28667
29137
  this.ownsEngine = false;
28668
29138
  return this.engine;
28669
29139
  }
28670
29140
  if (this.engineFactory === void 0) {
28671
29141
  throw new Error("Opensteer engine factory is not initialized");
28672
29142
  }
28673
- this.engine = await this.engineFactory(overrides);
29143
+ this.engine = this.wrapEngineWithObservationCapture(
29144
+ await this.engineFactory(overrides)
29145
+ );
28674
29146
  this.ownsEngine = true;
28675
29147
  return this.engine;
28676
29148
  }
@@ -28870,6 +29342,15 @@ var OpensteerSessionRuntime = class {
28870
29342
  return;
28871
29343
  }
28872
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);
28873
29354
  const artifacts = input.artifacts === void 0 ? void 0 : await Promise.all(
28874
29355
  input.artifacts.manifests.map(async (manifest) => {
28875
29356
  const reference = await root.artifacts.toProtocolArtifactReference(
@@ -28882,19 +29363,56 @@ var OpensteerSessionRuntime = class {
28882
29363
  return reference;
28883
29364
  })
28884
29365
  );
28885
- await root.traces.append(runId, {
29366
+ const traceEntry = await root.traces.append(runId, {
28886
29367
  operation: input.operation,
28887
29368
  outcome: input.outcome,
28888
29369
  startedAt: input.startedAt,
28889
29370
  completedAt: input.completedAt,
28890
29371
  ...input.context === void 0 ? {} : { context: input.context },
28891
- ...input.events === void 0 ? {} : { events: input.events },
29372
+ ...stepEvents === void 0 ? {} : { events: stepEvents },
28892
29373
  ...artifacts === void 0 ? {} : { artifacts },
28893
- ...input.data === void 0 ? {} : { data: toCanonicalJsonValue(input.data) },
28894
- ...input.error === void 0 ? {} : {
28895
- error: normalizeOpensteerError(input.error)
29374
+ ...normalizedData === void 0 ? {} : { data: normalizedData },
29375
+ ...normalizedError === void 0 ? {} : {
29376
+ error: normalizedError
28896
29377
  }
28897
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
+ }
28898
29416
  }
28899
29417
  async cleanupSessionResources(engine, pageRef, sessionRef) {
28900
29418
  if (pageRef !== void 0) {
@@ -28906,6 +29424,7 @@ var OpensteerSessionRuntime = class {
28906
29424
  }
28907
29425
  async resetRuntimeState(options) {
28908
29426
  const engine = this.engine;
29427
+ const observations = this.observations;
28909
29428
  this.networkHistory.clear();
28910
29429
  this.sessionRef = void 0;
28911
29430
  this.pageRef = void 0;
@@ -28914,20 +29433,140 @@ var OpensteerSessionRuntime = class {
28914
29433
  this.computer = void 0;
28915
29434
  this.extractionDescriptors = void 0;
28916
29435
  this.engine = void 0;
29436
+ this.observations = void 0;
29437
+ this.pendingOperationEventCaptures.length = 0;
29438
+ await observations?.close("runtime_reset").catch(() => void 0);
28917
29439
  if (options.disposeEngine && this.ownsEngine && engine?.dispose) {
28918
29440
  await engine.dispose();
28919
29441
  }
28920
29442
  this.ownsEngine = false;
28921
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
+ }
28922
29466
  runWithOperationTimeout(operation, callback, options = {}) {
28923
- return runWithPolicyTimeout(
28924
- this.policy.timeout,
28925
- {
28926
- operation,
28927
- ...options.signal === void 0 ? {} : { signal: options.signal }
28928
- },
28929
- callback
28930
- );
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;
28931
29570
  }
28932
29571
  async navigatePage(input, timeout) {
28933
29572
  const remainingMs = timeout.remainingMs();
@@ -28963,6 +29602,34 @@ function buildRuntimeTraceContext(input) {
28963
29602
  function buildArtifactScope(input) {
28964
29603
  return buildRuntimeTraceContext(input);
28965
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
+ }
28966
29633
  function selectLiveQueryPageRef(input, currentPageRef) {
28967
29634
  if (input.pageRef !== void 0) {
28968
29635
  return input.pageRef;
@@ -29925,12 +30592,12 @@ function extractReverseRuntimeValue(value, pointer) {
29925
30592
  }
29926
30593
  return readDotPath(value, pointer);
29927
30594
  }
29928
- function readDotPath(value, path13) {
29929
- if (path13.length === 0) {
30595
+ function readDotPath(value, path15) {
30596
+ if (path15.length === 0) {
29930
30597
  return value;
29931
30598
  }
29932
30599
  let current = value;
29933
- for (const segment of path13.split(".").filter((entry) => entry.length > 0)) {
30600
+ for (const segment of path15.split(".").filter((entry) => entry.length > 0)) {
29934
30601
  if (current === null || current === void 0) {
29935
30602
  return void 0;
29936
30603
  }
@@ -30299,7 +30966,7 @@ function normalizeRuntimeErrorMessage(error) {
30299
30966
  return error instanceof Error ? error.message : String(error);
30300
30967
  }
30301
30968
  function runtimeDelay(ms) {
30302
- return new Promise((resolve5) => setTimeout(resolve5, ms));
30969
+ return new Promise((resolve4) => setTimeout(resolve4, ms));
30303
30970
  }
30304
30971
  function applyBrowserCookiesToTransportRequest(request, cookies) {
30305
30972
  if (cookies.length === 0) {
@@ -30403,7 +31070,7 @@ function parseSetCookieHeader(value, requestUrl) {
30403
31070
  }
30404
31071
  const url = new URL(requestUrl);
30405
31072
  let domain = url.hostname;
30406
- let path13 = defaultCookiePath(url.pathname);
31073
+ let path15 = defaultCookiePath(url.pathname);
30407
31074
  let secure = url.protocol === "https:";
30408
31075
  let expiresAt;
30409
31076
  const cookieValue = rawValueParts.join("=").trim();
@@ -30416,7 +31083,7 @@ function parseSetCookieHeader(value, requestUrl) {
30416
31083
  continue;
30417
31084
  }
30418
31085
  if (key === "path" && attributeValue.length > 0) {
30419
- path13 = attributeValue;
31086
+ path15 = attributeValue;
30420
31087
  continue;
30421
31088
  }
30422
31089
  if (key === "secure") {
@@ -30442,7 +31109,7 @@ function parseSetCookieHeader(value, requestUrl) {
30442
31109
  name,
30443
31110
  value: cookieValue,
30444
31111
  domain,
30445
- path: path13,
31112
+ path: path15,
30446
31113
  secure,
30447
31114
  ...expiresAt === void 0 ? {} : { expiresAt }
30448
31115
  }
@@ -31394,7 +32061,7 @@ async function pollUntilResult(timeout, producer) {
31394
32061
  if (produced !== void 0) {
31395
32062
  return produced;
31396
32063
  }
31397
- await new Promise((resolve5) => setTimeout(resolve5, 100));
32064
+ await new Promise((resolve4) => setTimeout(resolve4, 100));
31398
32065
  }
31399
32066
  }
31400
32067
  async function getMainFrame(engine, pageRef) {
@@ -31452,6 +32119,133 @@ function toOpensteerResolvedTarget2(target) {
31452
32119
  function normalizeOpensteerError(error) {
31453
32120
  return normalizeThrownOpensteerError(error, "Unknown Opensteer runtime failure");
31454
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
+ }
31455
32249
  function buildMutationCaptureTraceData(diagnostics) {
31456
32250
  if (diagnostics?.finalizeError === void 0) {
31457
32251
  return {};
@@ -31501,8 +32295,8 @@ function screenshotMediaType(format2) {
31501
32295
  var OpensteerRuntime = class extends OpensteerSessionRuntime {
31502
32296
  constructor(options = {}) {
31503
32297
  const publicWorkspace = normalizeWorkspace2(options.workspace);
31504
- const rootPath = options.rootPath ?? (publicWorkspace === void 0 ? path6.resolve(options.rootDir ?? process.cwd(), ".opensteer", "temporary", randomUUID()) : resolveFilesystemWorkspacePath({
31505
- 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()),
31506
32300
  workspace: publicWorkspace
31507
32301
  }));
31508
32302
  const cleanupRootOnClose = options.cleanupRootOnClose ?? publicWorkspace === void 0;
@@ -31527,14 +32321,17 @@ var OpensteerRuntime = class extends OpensteerSessionRuntime {
31527
32321
  ...options.descriptorStore === void 0 ? {} : { descriptorStore: options.descriptorStore },
31528
32322
  ...options.extractionDescriptorStore === void 0 ? {} : { extractionDescriptorStore: options.extractionDescriptorStore },
31529
32323
  ...options.registryOverrides === void 0 ? {} : { registryOverrides: options.registryOverrides },
31530
- 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 }
31531
32328
  })
31532
32329
  );
31533
32330
  }
31534
32331
  };
31535
32332
  var OpensteerSessionRuntime2 = class extends OpensteerSessionRuntime {
31536
32333
  constructor(options) {
31537
- const rootPath = options.rootPath ?? path6.resolve(options.rootDir ?? process.cwd());
32334
+ const rootPath = options.rootPath ?? path7.resolve(options.rootDir ?? process.cwd());
31538
32335
  const cleanupRootOnClose = options.cleanupRootOnClose ?? false;
31539
32336
  const engineName = options.engineName ?? DEFAULT_OPENSTEER_ENGINE;
31540
32337
  assertSupportedEngineOptions({
@@ -31556,7 +32353,10 @@ var OpensteerSessionRuntime2 = class extends OpensteerSessionRuntime {
31556
32353
  ...options.descriptorStore === void 0 ? {} : { descriptorStore: options.descriptorStore },
31557
32354
  ...options.extractionDescriptorStore === void 0 ? {} : { extractionDescriptorStore: options.extractionDescriptorStore },
31558
32355
  ...options.registryOverrides === void 0 ? {} : { registryOverrides: options.registryOverrides },
31559
- 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 }
31560
32360
  })
31561
32361
  );
31562
32362
  }
@@ -31582,6 +32382,9 @@ function buildSharedRuntimeOptions(input) {
31582
32382
  ...input.extractionDescriptorStore === void 0 ? {} : { extractionDescriptorStore: input.extractionDescriptorStore },
31583
32383
  ...input.registryOverrides === void 0 ? {} : { registryOverrides: input.registryOverrides },
31584
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 },
31585
32388
  sessionInfo: {
31586
32389
  provider: {
31587
32390
  mode: "local",
@@ -31644,345 +32447,267 @@ function resolveOpensteerProvider(input = {}) {
31644
32447
  };
31645
32448
  }
31646
32449
  var execFile2 = promisify(execFile);
31647
- var DEFAULT_CAPTURE_TIMEOUT_MS = 3e4;
31648
- var DEFAULT_STOP_TIMEOUT_MS = 15e3;
31649
- var DEVTOOLS_POLL_INTERVAL_MS2 = 50;
31650
- var PROCESS_LIST_MAX_BUFFER_BYTES3 = 16 * 1024 * 1024;
31651
- async function resolveCookieCaptureStrategy(input = {}) {
31652
- const timeoutMs = input.timeoutMs ?? DEFAULT_CAPTURE_TIMEOUT_MS;
31653
- if (input.attachEndpoint !== void 0) {
31654
- if (input.strategy !== void 0 && input.strategy !== "attach") {
31655
- throw new Error(
31656
- `Strategy "${input.strategy}" is incompatible with an explicit attach endpoint.`
31657
- );
31658
- }
31659
- return {
31660
- strategy: "attach",
31661
- attachEndpoint: input.attachEndpoint,
31662
- ...input.profileDirectory === void 0 ? {} : { profileDirectory: input.profileDirectory },
31663
- timeoutMs
31664
- };
31665
- }
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 = {}) {
31666
32454
  const brand2 = resolveRequestedBrand(input);
31667
- const executablePath = resolveBrandExecutablePath(brand2, input.executablePath);
31668
32455
  const userDataDir = resolveBrandUserDataDir(brand2, input.userDataDir);
31669
- const profileDirectory = input.profileDirectory;
31670
- const attachEndpoint = await resolveReachableAttachEndpoint(userDataDir, timeoutMs);
31671
- const runningProcess = findBrandProcess(brand2);
31672
- const autoStrategy = attachEndpoint !== void 0 ? "attach" : runningProcess !== null ? "managed-relaunch" : "headless";
31673
- const strategy = input.strategy ?? autoStrategy;
31674
- validateRequestedStrategy({
31675
- strategy,
31676
- brand: brand2,
31677
- ...attachEndpoint === void 0 ? {} : { attachEndpoint },
31678
- ...runningProcess?.pid === void 0 ? {} : { runningPid: runningProcess.pid }
31679
- });
31680
- return {
31681
- strategy,
31682
- brandId: brand2.id,
31683
- brandDisplayName: brand2.displayName,
31684
- executablePath,
31685
- userDataDir,
31686
- ...profileDirectory === void 0 ? {} : { profileDirectory },
31687
- ...attachEndpoint === void 0 ? {} : { attachEndpoint },
31688
- ...runningProcess === null ? {} : { runningPid: runningProcess.pid },
31689
- timeoutMs
31690
- };
31691
- }
31692
- async function acquireCdpEndpoint(resolved) {
31693
- if (resolved.strategy === "attach") {
31694
- if (!resolved.attachEndpoint) {
31695
- throw new Error("Attach capture requires a debuggable browser endpoint.");
31696
- }
31697
- const inspected = await inspectCdpEndpoint({
31698
- endpoint: resolved.attachEndpoint,
31699
- timeoutMs: Math.min(2e3, resolved.timeoutMs)
31700
- });
31701
- return {
31702
- strategy: "attach",
31703
- cdpEndpoint: inspected.endpoint,
31704
- ...resolved.brandId === void 0 ? {} : { brandId: resolved.brandId },
31705
- ...resolved.brandDisplayName === void 0 ? {} : { brandDisplayName: resolved.brandDisplayName },
31706
- ...resolved.userDataDir === void 0 ? {} : { userDataDir: resolved.userDataDir },
31707
- ...resolved.profileDirectory === void 0 ? {} : { profileDirectory: resolved.profileDirectory },
31708
- cleanup: async () => void 0
31709
- };
31710
- }
31711
- 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)) {
31712
32459
  throw new Error(
31713
- "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.`
31714
32461
  );
31715
32462
  }
31716
- const userDataDir = resolved.userDataDir;
31717
- if (resolved.strategy === "managed-relaunch") {
31718
- if (resolved.runningPid === void 0) {
31719
- throw new Error("Managed relaunch requires a running browser process.");
31720
- }
31721
- await gracefullyStopBrowser(
31722
- getBrowserBrand(resolved.brandId),
31723
- resolved.runningPid,
31724
- resolved.timeoutMs
31725
- );
31726
- }
31727
- await clearChromeSingletonEntries(userDataDir);
32463
+ const tempDir = await mkdtemp(join(tmpdir(), "opensteer-cookies-"));
31728
32464
  try {
31729
- const capture = await launchCaptureChrome({
31730
- brandDisplayName: resolved.brandDisplayName,
31731
- executablePath: resolved.executablePath,
31732
- userDataDir,
31733
- ...resolved.profileDirectory === void 0 ? {} : { profileDirectory: resolved.profileDirectory },
31734
- timeoutMs: resolved.timeoutMs
31735
- });
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);
31736
32469
  return {
31737
- strategy: resolved.strategy,
31738
- cdpEndpoint: capture.endpoint,
31739
- brandId: resolved.brandId,
31740
- brandDisplayName: resolved.brandDisplayName,
31741
- userDataDir: resolved.userDataDir,
31742
- ...resolved.profileDirectory === void 0 ? {} : { profileDirectory: resolved.profileDirectory },
31743
- cleanup: async () => {
31744
- await capture.kill().catch(() => void 0);
31745
- await clearChromeSingletonEntries(userDataDir).catch(() => void 0);
31746
- }
31747
- };
31748
- } catch (error) {
31749
- await clearChromeSingletonEntries(userDataDir).catch(() => void 0);
31750
- throw error;
31751
- }
31752
- }
31753
- async function gracefullyStopBrowser(brand2, pid, timeoutMs = DEFAULT_STOP_TIMEOUT_MS) {
31754
- if (pid <= 0) {
31755
- return;
31756
- }
31757
- const platformConfig = resolveBrandPlatformConfig(brand2);
31758
- if (process.platform === "darwin" && platformConfig?.bundleId) {
31759
- await execFile2(
31760
- "osascript",
31761
- ["-e", `tell application id "${platformConfig.bundleId}" to quit`],
31762
- {
31763
- maxBuffer: PROCESS_LIST_MAX_BUFFER_BYTES3
31764
- }
31765
- ).catch(() => void 0);
31766
- } else if (process.platform === "win32") {
31767
- await execFile2("taskkill", ["/PID", String(pid)], {
31768
- maxBuffer: PROCESS_LIST_MAX_BUFFER_BYTES3
31769
- }).catch(() => void 0);
31770
- } else {
31771
- try {
31772
- process.kill(pid, "SIGTERM");
31773
- } catch {
31774
- }
31775
- }
31776
- if (await waitForProcessExit2(pid, timeoutMs)) {
31777
- return;
31778
- }
31779
- if (process.platform === "win32") {
31780
- await execFile2("taskkill", ["/F", "/PID", String(pid)], {
31781
- maxBuffer: PROCESS_LIST_MAX_BUFFER_BYTES3
31782
- }).catch(() => void 0);
31783
- } else {
31784
- try {
31785
- process.kill(pid, "SIGKILL");
31786
- } catch {
31787
- }
31788
- }
31789
- await waitForProcessExit2(pid, Math.min(5e3, timeoutMs));
31790
- }
31791
- async function launchCaptureChrome(input) {
31792
- const stderrLines = [];
31793
- const child = spawn(input.executablePath, buildCaptureChromeArgs(input), {
31794
- detached: process.platform !== "win32",
31795
- stdio: ["ignore", "ignore", "pipe"]
31796
- });
31797
- child.unref();
31798
- child.stderr?.setEncoding("utf8");
31799
- child.stderr?.on("data", (chunk) => {
31800
- stderrLines.push(String(chunk));
31801
- });
31802
- try {
31803
- const endpoint = await waitForCaptureEndpoint({
31804
- brandDisplayName: input.brandDisplayName,
31805
- child,
31806
- stderrLines,
31807
- timeoutMs: input.timeoutMs,
31808
- userDataDir: input.userDataDir
31809
- });
31810
- return {
31811
- endpoint,
31812
- kill: async () => {
31813
- await terminateChild(child);
31814
- }
32470
+ cookies,
32471
+ brandId: brand2.id,
32472
+ brandDisplayName: brand2.displayName,
32473
+ userDataDir,
32474
+ profileDirectory
31815
32475
  };
31816
- } catch (error) {
31817
- await terminateChild(child).catch(() => void 0);
31818
- throw error;
32476
+ } finally {
32477
+ await rm(tempDir, { recursive: true, force: true }).catch(() => void 0);
31819
32478
  }
31820
32479
  }
31821
- function relaunchBrowserNormally(executablePath) {
31822
- const child = spawn(executablePath, [], {
31823
- detached: true,
31824
- stdio: "ignore"
31825
- });
31826
- child.unref();
31827
- }
31828
32480
  function resolveRequestedBrand(input) {
31829
32481
  if (input.brandId !== void 0) {
31830
32482
  return getBrowserBrand(input.brandId);
31831
32483
  }
31832
- if (input.userDataDir !== void 0) {
31833
- const inferred = inferBrandFromUserDataDir(input.userDataDir);
31834
- if (!inferred) {
31835
- throw new Error(
31836
- `Could not infer a browser brand from user-data-dir "${input.userDataDir}". Pass --browser explicitly.`
31837
- );
31838
- }
31839
- return inferred;
31840
- }
31841
- if (input.executablePath !== void 0) {
31842
- const inferred = inferBrandFromExecutablePath(input.executablePath);
31843
- if (!inferred) {
31844
- throw new Error(
31845
- `Could not infer a browser brand from executable path "${input.executablePath}". Pass --browser explicitly.`
31846
- );
31847
- }
31848
- return inferred;
31849
- }
31850
32484
  const installed = detectInstalledBrowserBrands()[0];
31851
32485
  if (!installed) {
31852
32486
  throw new Error(
31853
- "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."
31854
32488
  );
31855
32489
  }
31856
32490
  return installed.brand;
31857
32491
  }
31858
- async function resolveReachableAttachEndpoint(userDataDir, timeoutMs) {
31859
- const activePort = readDevToolsActivePort(userDataDir);
31860
- if (!activePort) {
31861
- 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
+ }
31862
32499
  }
32500
+ }
32501
+ function queryAllCookies(dbPath) {
32502
+ let DatabaseSync;
31863
32503
  try {
31864
- return (await inspectCdpEndpoint({
31865
- endpoint: `http://127.0.0.1:${String(activePort.port)}`,
31866
- timeoutMs: Math.min(2e3, timeoutMs)
31867
- })).endpoint;
32504
+ ({ DatabaseSync } = __require(NODE_SQLITE_SPECIFIER2));
31868
32505
  } catch {
31869
- 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();
31870
32521
  }
31871
32522
  }
31872
- function validateRequestedStrategy(input) {
31873
- 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 {
31874
32559
  throw new Error(
31875
- `${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.`
31876
32561
  );
31877
32562
  }
31878
- 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 {
31879
32570
  throw new Error(
31880
- `${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.`
31881
32572
  );
31882
32573
  }
31883
- 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 {
31884
32594
  throw new Error(
31885
- `${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."
31886
32596
  );
31887
32597
  }
31888
32598
  }
31889
- function inferBrandFromUserDataDir(userDataDir) {
31890
- const normalized = normalizePath(userDataDir);
31891
- return getAllBrowserBrands().find((brand2) => {
31892
- const config = resolveBrandPlatformConfig(brand2);
31893
- if (!config) {
31894
- 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;
31895
32607
  }
31896
- const defaultDir = normalizePath(config.userDataDir);
31897
- return normalized === defaultDir || normalized.startsWith(`${defaultDir}/`);
31898
- });
31899
- }
31900
- function inferBrandFromExecutablePath(executablePath) {
31901
- const normalized = normalizePath(executablePath);
31902
- return getAllBrowserBrands().find((brand2) => {
31903
- const config = resolveBrandPlatformConfig(brand2);
31904
- if (!config) {
31905
- return false;
32608
+ const value = decryptCookieValue(row, decryptionKey);
32609
+ if (value === null) {
32610
+ continue;
31906
32611
  }
31907
- return config.executableCandidates.some(
31908
- (candidate) => candidate !== null && normalizePath(candidate) === normalized
31909
- );
31910
- });
31911
- }
31912
- function buildCaptureChromeArgs(input) {
31913
- const args = [
31914
- "--remote-debugging-port=0",
31915
- "--headless=new",
31916
- "--no-first-run",
31917
- "--no-default-browser-check",
31918
- "--disable-background-networking",
31919
- "--disable-sync",
31920
- "--disable-component-update",
31921
- `--user-data-dir=${input.userDataDir}`
31922
- ];
31923
- if (input.profileDirectory !== void 0) {
31924
- args.push(`--profile-directory=${input.profileDirectory}`);
31925
- }
31926
- return args;
31927
- }
31928
- async function waitForCaptureEndpoint(input) {
31929
- const deadline = Date.now() + input.timeoutMs;
31930
- while (Date.now() < deadline) {
31931
- const activePort = readDevToolsActivePort(input.userDataDir);
31932
- if (activePort) {
31933
- try {
31934
- return (await inspectCdpEndpoint({
31935
- endpoint: `http://127.0.0.1:${String(activePort.port)}`,
31936
- timeoutMs: Math.min(2e3, input.timeoutMs)
31937
- })).endpoint;
31938
- } catch {
31939
- return `ws://127.0.0.1:${String(activePort.port)}${activePort.webSocketPath}`;
31940
- }
32612
+ const expiresSeconds = chromeDateToUnixSeconds(row.expires_utc);
32613
+ const isSession = expiresSeconds <= 0;
32614
+ if (!isSession && expiresSeconds < nowSeconds) {
32615
+ continue;
31941
32616
  }
31942
- if (input.child.exitCode !== null) {
31943
- break;
32617
+ const sameSite = chromeSameSiteToString(row.samesite);
32618
+ let secure = Number(row.is_secure) === 1;
32619
+ if (sameSite === "None") {
32620
+ secure = true;
31944
32621
  }
31945
- 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
+ });
31946
32632
  }
31947
- throw new Error(formatCaptureLaunchError(input.brandDisplayName, input.stderrLines));
32633
+ return cookies;
31948
32634
  }
31949
- function formatCaptureLaunchError(brandDisplayName, stderrLines) {
31950
- const relevantLines = stderrLines.join("").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
31951
- if (relevantLines.length === 0) {
31952
- 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;
31953
32638
  }
31954
- const focusedLines = relevantLines.filter(
31955
- (line) => /(error|fatal|sandbox|namespace|permission|cannot|failed|abort)/i.test(line)
31956
- );
31957
- return `${brandDisplayName} failed to launch before exposing a DevTools endpoint.
31958
- ${(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;
31959
32655
  }
31960
- async function terminateChild(child) {
31961
- if (child.exitCode !== null) {
31962
- 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;
31963
32670
  }
32671
+ }
32672
+ function decryptAes256Gcm(ciphertext, key) {
31964
32673
  try {
31965
- 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");
31966
32684
  } catch {
31967
- return;
32685
+ return null;
31968
32686
  }
31969
- await sleep4(50);
31970
32687
  }
31971
- async function waitForProcessExit2(pid, timeoutMs) {
31972
- const deadline = Date.now() + timeoutMs;
31973
- while (Date.now() < deadline) {
31974
- 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) {
31975
32693
  return true;
31976
32694
  }
31977
- await sleep4(50);
31978
32695
  }
31979
- return !isProcessRunning(pid);
32696
+ return false;
31980
32697
  }
31981
- function normalizePath(value) {
31982
- 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);
31983
32704
  }
31984
- async function sleep4(ms) {
31985
- 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;
31986
32711
  }
31987
32712
 
31988
32713
  // src/cloud/cookie-sync.ts
@@ -32038,14 +32763,14 @@ function toPortableBrowserProfileCookieRecord(cookie) {
32038
32763
  if (!name || !domain) {
32039
32764
  return null;
32040
32765
  }
32041
- 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 : "/";
32042
32767
  const expiresAt = typeof cookie.expires === "number" && Number.isFinite(cookie.expires) && cookie.expires > 0 ? Math.floor(cookie.expires * 1e3) : null;
32043
32768
  const sameSite = normalizeSameSite(cookie.sameSite);
32044
32769
  return {
32045
32770
  name,
32046
32771
  value: cookie.value,
32047
32772
  domain,
32048
- path: path13,
32773
+ path: path15,
32049
32774
  secure: cookie.secure,
32050
32775
  httpOnly: cookie.httpOnly,
32051
32776
  ...sameSite === void 0 ? {} : { sameSite },
@@ -32061,114 +32786,48 @@ function normalizeSameSite(value) {
32061
32786
  return void 0;
32062
32787
  }
32063
32788
 
32064
- // src/cloud/portable-cookie-snapshot.ts
32065
- var gzip = promisify(gzip$1);
32066
- async function capturePortableBrowserProfileSnapshot(input = {}) {
32067
- const attached = input.attachEndpoint ? await inspectCdpEndpoint({
32068
- endpoint: input.attachEndpoint,
32069
- ...input.timeoutMs === void 0 ? {} : { timeoutMs: input.timeoutMs }
32070
- }) : await selectAttachBrowserCandidate({
32071
- ...input.timeoutMs === void 0 ? {} : { timeoutMs: input.timeoutMs }
32072
- });
32073
- const browser = await connectPlaywrightChromiumBrowser({
32074
- url: attached.endpoint
32075
- });
32076
- try {
32077
- const context = browser.contexts()[0];
32078
- if (!context) {
32079
- throw new Error("Attached browser did not expose a default browser context.");
32080
- }
32081
- const prepared = prepareBrowserProfileSyncCookies({
32082
- cookies: await context.cookies(),
32083
- ...input.domains === void 0 ? {} : { domains: input.domains }
32084
- });
32085
- if (prepared.cookies.length === 0) {
32086
- throw new Error("No syncable cookies found for the selected browser and scope.");
32087
- }
32088
- const browserVersion = browser.version();
32089
- const source = parseSnapshotSource(attached.browser ?? browserVersion);
32090
- return {
32091
- version: "portable-cookies-v1",
32092
- source: {
32093
- browserFamily: "chromium",
32094
- ...source.browserName === void 0 ? {} : { browserName: source.browserName },
32095
- ...source.browserMajor === void 0 ? {} : { browserMajor: source.browserMajor },
32096
- ...input.browserBrand === void 0 ? {} : { browserBrand: input.browserBrand },
32097
- ...input.captureMethod === void 0 ? {} : { captureMethod: input.captureMethod },
32098
- platform: normalizePlatform(process.platform),
32099
- capturedAt: Date.now()
32100
- },
32101
- cookies: prepared.cookies
32102
- };
32103
- } finally {
32104
- await browser.close().catch(() => void 0);
32105
- }
32106
- }
32107
- async function encodePortableBrowserProfileSnapshot(snapshot) {
32108
- return gzip(Buffer.from(JSON.stringify(snapshot), "utf8"));
32109
- }
32110
- function parseSnapshotSource(value) {
32111
- if (!value) {
32112
- return {};
32113
- }
32114
- const trimmed = value.trim();
32115
- const browserName = trimmed.split("/")[0]?.trim() || void 0;
32116
- const majorMatch = trimmed.match(/(\d+)/);
32117
- return {
32118
- ...browserName === void 0 ? {} : { browserName },
32119
- ...majorMatch?.[1] === void 0 ? {} : { browserMajor: majorMatch[1] }
32120
- };
32121
- }
32122
- function normalizePlatform(platform) {
32123
- if (platform === "darwin") return "macos";
32124
- if (platform === "win32") return "windows";
32125
- return platform;
32126
- }
32127
-
32128
32789
  // src/cloud/profile-sync.ts
32790
+ var gzip = promisify(gzip$1);
32129
32791
  var DEFAULT_POLL_INTERVAL_MS = 1e3;
32130
32792
  var DEFAULT_POLL_TIMEOUT_MS = 5 * 6e4;
32131
32793
  async function syncBrowserProfileCookies(client, input) {
32132
- const resolved = await resolveCookieCaptureStrategy({
32133
- ...input.attachEndpoint === void 0 ? {} : { attachEndpoint: input.attachEndpoint },
32794
+ const result = await readBrowserCookies({
32134
32795
  ...input.brandId === void 0 ? {} : { brandId: input.brandId },
32135
32796
  ...input.userDataDir === void 0 ? {} : { userDataDir: input.userDataDir },
32136
- ...input.profileDirectory === void 0 ? {} : { profileDirectory: input.profileDirectory },
32137
- ...input.executablePath === void 0 ? {} : { executablePath: input.executablePath },
32138
- ...input.strategy === void 0 ? {} : { strategy: input.strategy },
32139
- ...input.timeoutMs === void 0 ? {} : { timeoutMs: input.timeoutMs }
32797
+ ...input.profileDirectory === void 0 ? {} : { profileDirectory: input.profileDirectory }
32140
32798
  });
32141
- const shouldRestoreBrowser = (input.restoreBrowser ?? true) && resolved.strategy === "managed-relaunch";
32142
- let captureSource;
32143
- try {
32144
- captureSource = await acquireCdpEndpoint(resolved);
32145
- const snapshot = await capturePortableBrowserProfileSnapshot({
32146
- attachEndpoint: captureSource.cdpEndpoint,
32147
- ...captureSource.brandId === void 0 ? {} : { browserBrand: captureSource.brandId },
32148
- captureMethod: captureSource.strategy,
32149
- ...input.domains === void 0 ? {} : { domains: input.domains },
32150
- ...input.timeoutMs === void 0 ? {} : { timeoutMs: input.timeoutMs }
32151
- });
32152
- const payload = await encodePortableBrowserProfileSnapshot(snapshot);
32153
- const created = await client.createBrowserProfileImport({
32154
- profileId: input.profileId
32155
- });
32156
- if (payload.length > created.maxUploadBytes) {
32157
- throw new Error(
32158
- `Compressed cookie snapshot is ${String(payload.length)} bytes, exceeding the ${String(created.maxUploadBytes)} byte upload limit.`
32159
- );
32160
- }
32161
- const uploaded = await client.uploadBrowserProfileImportPayload({
32162
- uploadUrl: created.uploadUrl,
32163
- payload
32164
- });
32165
- return uploaded.status === "ready" ? uploaded : waitForBrowserProfileImport(client, created.importId);
32166
- } finally {
32167
- await captureSource?.cleanup().catch(() => void 0);
32168
- if (shouldRestoreBrowser && resolved.executablePath !== void 0) {
32169
- relaunchBrowserNormally(resolved.executablePath);
32170
- }
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
+ );
32171
32825
  }
32826
+ const uploaded = await client.uploadBrowserProfileImportPayload({
32827
+ uploadUrl: created.uploadUrl,
32828
+ payload
32829
+ });
32830
+ return uploaded.status === "ready" ? uploaded : waitForBrowserProfileImport(client, created.importId);
32172
32831
  }
32173
32832
  async function waitForBrowserProfileImport(client, importId) {
32174
32833
  const deadline = Date.now() + DEFAULT_POLL_TIMEOUT_MS;
@@ -32180,12 +32839,17 @@ async function waitForBrowserProfileImport(client, importId) {
32180
32839
  if (current.status === "failed") {
32181
32840
  throw new Error(current.error ?? "Browser profile sync failed.");
32182
32841
  }
32183
- await sleep5(DEFAULT_POLL_INTERVAL_MS);
32842
+ await sleep4(DEFAULT_POLL_INTERVAL_MS);
32184
32843
  }
32185
32844
  throw new Error(`Timed out waiting for browser profile sync "${importId}" to finish.`);
32186
32845
  }
32187
- async function sleep5(ms) {
32188
- 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));
32189
32853
  }
32190
32854
 
32191
32855
  // src/cloud/client.ts
@@ -32205,7 +32869,8 @@ var OpensteerCloudClient = class {
32205
32869
  ...input.name === void 0 ? {} : { name: input.name },
32206
32870
  ...input.browser === void 0 ? {} : { browser: input.browser },
32207
32871
  ...input.context === void 0 ? {} : { context: input.context },
32208
- ...input.browserProfile === void 0 ? {} : { browserProfile: input.browserProfile }
32872
+ ...input.browserProfile === void 0 ? {} : { browserProfile: input.browserProfile },
32873
+ ...input.observability === void 0 ? {} : { observability: input.observability }
32209
32874
  }
32210
32875
  });
32211
32876
  return await response.json();
@@ -32383,8 +33048,8 @@ var OpensteerCloudClient = class {
32383
33048
  }
32384
33049
  };
32385
33050
  function delay(ms) {
32386
- return new Promise((resolve5) => {
32387
- setTimeout(resolve5, ms);
33051
+ return new Promise((resolve4) => {
33052
+ setTimeout(resolve4, ms);
32388
33053
  });
32389
33054
  }
32390
33055
  function wrapCloudFetchError(error, input) {
@@ -32407,17 +33072,17 @@ function wrapCloudFetchError(error, input) {
32407
33072
  function resolveCloudConfig(input = {}) {
32408
33073
  const provider = resolveOpensteerProvider({
32409
33074
  ...input.provider === void 0 ? {} : { provider: input.provider },
32410
- ...input.environmentProvider === void 0 ? {} : { environmentProvider: input.environmentProvider }
33075
+ ...input.environment?.OPENSTEER_PROVIDER === void 0 ? {} : { environmentProvider: input.environment.OPENSTEER_PROVIDER }
32411
33076
  });
32412
33077
  if (provider.mode !== "cloud") {
32413
33078
  return void 0;
32414
33079
  }
32415
33080
  const cloudProvider = input.provider?.mode === "cloud" ? input.provider : void 0;
32416
- const apiKey = cloudProvider?.apiKey ?? process.env.OPENSTEER_API_KEY;
33081
+ const apiKey = cloudProvider?.apiKey ?? input.environment?.OPENSTEER_API_KEY;
32417
33082
  if (!apiKey || apiKey.trim().length === 0) {
32418
33083
  throw new Error("provider=cloud requires OPENSTEER_API_KEY or provider.apiKey.");
32419
33084
  }
32420
- const baseUrl = cloudProvider?.baseUrl ?? process.env.OPENSTEER_BASE_URL;
33085
+ const baseUrl = cloudProvider?.baseUrl ?? input.environment?.OPENSTEER_BASE_URL;
32421
33086
  if (!baseUrl || baseUrl.trim().length === 0) {
32422
33087
  throw new Error("provider=cloud requires OPENSTEER_BASE_URL or provider.baseUrl.");
32423
33088
  }
@@ -32519,9 +33184,9 @@ var OpensteerCloudAutomationClient = class {
32519
33184
  sentAt: Date.now(),
32520
33185
  ...input === void 0 ? {} : { input }
32521
33186
  };
32522
- return new Promise((resolve5, reject) => {
33187
+ return new Promise((resolve4, reject) => {
32523
33188
  this.pending.set(requestId, {
32524
- resolve: (value) => resolve5(value),
33189
+ resolve: (value) => resolve4(value),
32525
33190
  reject
32526
33191
  });
32527
33192
  try {
@@ -32584,8 +33249,8 @@ var OpensteerCloudAutomationClient = class {
32584
33249
  pending.reject(new Error(`automation connection closed before ${requestId} completed`));
32585
33250
  }
32586
33251
  this.pending.clear();
32587
- await new Promise((resolve5) => {
32588
- socket.once("close", () => resolve5());
33252
+ await new Promise((resolve4) => {
33253
+ socket.once("close", () => resolve4());
32589
33254
  socket.close();
32590
33255
  }).catch(() => void 0);
32591
33256
  }
@@ -32627,8 +33292,8 @@ var OpensteerCloudAutomationClient = class {
32627
33292
  }
32628
33293
  this.pending.clear();
32629
33294
  });
32630
- await new Promise((resolve5, reject) => {
32631
- socket.once("open", () => resolve5());
33295
+ await new Promise((resolve4, reject) => {
33296
+ socket.once("open", () => resolve4());
32632
33297
  socket.once("error", reject);
32633
33298
  });
32634
33299
  this.send({
@@ -32969,6 +33634,7 @@ var CloudSessionProxy = class {
32969
33634
  workspace;
32970
33635
  cleanupRootOnClose;
32971
33636
  cloud;
33637
+ observability;
32972
33638
  sessionId;
32973
33639
  sessionBaseUrl;
32974
33640
  client;
@@ -32977,8 +33643,9 @@ var CloudSessionProxy = class {
32977
33643
  constructor(cloud, options = {}) {
32978
33644
  this.cloud = cloud;
32979
33645
  this.workspace = options.workspace;
32980
- this.rootPath = options.rootPath ?? (this.workspace === void 0 ? path6.join(tmpdir(), `${TEMPORARY_CLOUD_WORKSPACE_PREFIX}${randomUUID()}`) : resolveFilesystemWorkspacePath({
32981
- 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()),
32982
33649
  workspace: this.workspace
32983
33650
  }));
32984
33651
  this.cleanupRootOnClose = options.cleanupRootOnClose ?? this.workspace === void 0;
@@ -33321,6 +33988,7 @@ var CloudSessionProxy = class {
33321
33988
  ...this.workspace === void 0 ? {} : { name: this.workspace },
33322
33989
  ...input.launch === void 0 ? {} : { browser: input.launch },
33323
33990
  ...input.context === void 0 ? {} : { context: input.context },
33991
+ ...this.observability === void 0 ? {} : { observability: this.observability },
33324
33992
  ...resolveCloudBrowserProfile(this.cloud, input) === void 0 ? {} : { browserProfile: resolveCloudBrowserProfile(this.cloud, input) }
33325
33993
  });
33326
33994
  const record = {
@@ -33418,16 +34086,17 @@ function isMissingCloudSessionError(error) {
33418
34086
 
33419
34087
  // src/sdk/runtime-resolution.ts
33420
34088
  function resolveOpensteerRuntimeConfig(input = {}) {
34089
+ const environment = input.environment ?? process.env;
33421
34090
  const provider = resolveOpensteerProvider({
33422
34091
  ...input.provider === void 0 ? {} : { provider: input.provider },
33423
- ...input.environmentProvider === void 0 ? {} : { environmentProvider: input.environmentProvider }
34092
+ ...environment.OPENSTEER_PROVIDER === void 0 ? {} : { environmentProvider: environment.OPENSTEER_PROVIDER }
33424
34093
  });
33425
34094
  if (provider.mode === "cloud") {
33426
34095
  return {
33427
34096
  provider,
33428
34097
  cloud: resolveCloudConfig({
33429
34098
  ...input.provider === void 0 ? {} : { provider: input.provider },
33430
- ...input.environmentProvider === void 0 ? {} : { environmentProvider: input.environmentProvider }
34099
+ environment
33431
34100
  })
33432
34101
  };
33433
34102
  }
@@ -33438,7 +34107,7 @@ function createOpensteerSemanticRuntime(input = {}) {
33438
34107
  const engine = input.engine ?? runtimeOptions.engineName ?? DEFAULT_OPENSTEER_ENGINE;
33439
34108
  const config = resolveOpensteerRuntimeConfig({
33440
34109
  ...input.provider === void 0 ? {} : { provider: input.provider },
33441
- ...process.env.OPENSTEER_PROVIDER === void 0 ? {} : { environmentProvider: process.env.OPENSTEER_PROVIDER }
34110
+ ...input.environment === void 0 ? {} : { environment: input.environment }
33442
34111
  });
33443
34112
  assertProviderSupportsEngine(config.provider.mode, engine);
33444
34113
  if (config.provider.mode === "cloud") {
@@ -33446,7 +34115,8 @@ function createOpensteerSemanticRuntime(input = {}) {
33446
34115
  ...runtimeOptions.rootDir === void 0 ? {} : { rootDir: runtimeOptions.rootDir },
33447
34116
  ...runtimeOptions.rootPath === void 0 ? {} : { rootPath: runtimeOptions.rootPath },
33448
34117
  ...runtimeOptions.workspace === void 0 ? {} : { workspace: runtimeOptions.workspace },
33449
- ...runtimeOptions.cleanupRootOnClose === void 0 ? {} : { cleanupRootOnClose: runtimeOptions.cleanupRootOnClose }
34118
+ ...runtimeOptions.cleanupRootOnClose === void 0 ? {} : { cleanupRootOnClose: runtimeOptions.cleanupRootOnClose },
34119
+ ...runtimeOptions.observability === void 0 ? {} : { observability: runtimeOptions.observability }
33450
34120
  });
33451
34121
  }
33452
34122
  return new OpensteerRuntime({
@@ -33454,7 +34124,109 @@ function createOpensteerSemanticRuntime(input = {}) {
33454
34124
  engineName: engine
33455
34125
  });
33456
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
+ }
33457
34229
 
33458
- 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 };
33459
- //# sourceMappingURL=chunk-3QJGBVWT.js.map
33460
- //# sourceMappingURL=chunk-3QJGBVWT.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