@opensteer/runtime-core 0.1.0 → 0.1.2

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.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import path6 from 'path';
2
- import { randomUUID, createHash } from 'crypto';
2
+ import { createHash, randomUUID } from 'crypto';
3
3
  import { rm, mkdir, access, readFile, mkdtemp, writeFile, rename, open, readdir } from 'fs/promises';
4
4
  import { createRequire } from 'module';
5
5
  import { pathToFileURL } from 'url';
@@ -13,7 +13,7 @@ import vm from 'vm';
13
13
 
14
14
  // package.json
15
15
  var package_default = {
16
- version: "0.1.0"};
16
+ version: "0.1.2"};
17
17
 
18
18
  // src/version.ts
19
19
  var OPENSTEER_RUNTIME_CORE_VERSION = package_default.version;
@@ -970,14 +970,10 @@ var networkRecordSchema = objectSchema(
970
970
  ]
971
971
  }
972
972
  );
973
- var networkQuerySourceSchema = enumSchema(["live", "saved"], {
974
- title: "NetworkQuerySource"
975
- });
976
973
  var networkQueryRecordSchema = objectSchema(
977
974
  {
978
975
  recordId: stringSchema({ minLength: 1 }),
979
- source: networkQuerySourceSchema,
980
- actionId: stringSchema({ minLength: 1 }),
976
+ capture: stringSchema({ minLength: 1 }),
981
977
  tags: arraySchema(stringSchema({ minLength: 1 }), {
982
978
  uniqueItems: true
983
979
  }),
@@ -986,7 +982,7 @@ var networkQueryRecordSchema = objectSchema(
986
982
  },
987
983
  {
988
984
  title: "NetworkQueryRecord",
989
- required: ["recordId", "source", "record"]
985
+ required: ["recordId", "record"]
990
986
  }
991
987
  );
992
988
  arraySchema(headerEntrySchema, {
@@ -1296,12 +1292,6 @@ var opensteerRegistryProvenanceSchema = objectSchema(
1296
1292
  required: ["source"]
1297
1293
  }
1298
1294
  );
1299
- var opensteerRequestPlanLifecycleSchema = enumSchema(
1300
- ["draft", "active", "deprecated", "retired"],
1301
- {
1302
- title: "OpensteerRequestPlanLifecycle"
1303
- }
1304
- );
1305
1295
  var opensteerRequestPlanFreshnessSchema = objectSchema(
1306
1296
  {
1307
1297
  lastValidatedAt: integerSchema({ minimum: 0 }),
@@ -1324,23 +1314,12 @@ var opensteerRequestPlanRecordSchema = objectSchema(
1324
1314
  uniqueItems: true
1325
1315
  }),
1326
1316
  provenance: opensteerRegistryProvenanceSchema,
1327
- lifecycle: opensteerRequestPlanLifecycleSchema,
1328
1317
  freshness: opensteerRequestPlanFreshnessSchema,
1329
1318
  payload: opensteerRequestPlanPayloadSchema
1330
1319
  },
1331
1320
  {
1332
1321
  title: "OpensteerRequestPlanRecord",
1333
- required: [
1334
- "id",
1335
- "key",
1336
- "version",
1337
- "createdAt",
1338
- "updatedAt",
1339
- "contentHash",
1340
- "tags",
1341
- "lifecycle",
1342
- "payload"
1343
- ]
1322
+ required: ["id", "key", "version", "createdAt", "updatedAt", "contentHash", "tags", "payload"]
1344
1323
  }
1345
1324
  );
1346
1325
  var jsonValueSchema = defineSchema({
@@ -1474,8 +1453,7 @@ var opensteerRecipeStepSchema = oneOfSchema(
1474
1453
  objectSchema(
1475
1454
  {
1476
1455
  kind: enumSchema(["goto"]),
1477
- url: stringSchema({ minLength: 1 }),
1478
- networkTag: stringSchema({ minLength: 1 })
1456
+ url: stringSchema({ minLength: 1 })
1479
1457
  },
1480
1458
  {
1481
1459
  title: "OpensteerAuthRecipeGotoStep",
@@ -1484,8 +1462,7 @@ var opensteerRecipeStepSchema = oneOfSchema(
1484
1462
  ),
1485
1463
  objectSchema(
1486
1464
  {
1487
- kind: enumSchema(["reload"]),
1488
- networkTag: stringSchema({ minLength: 1 })
1465
+ kind: enumSchema(["reload"])
1489
1466
  },
1490
1467
  {
1491
1468
  title: "OpensteerAuthRecipeReloadStep",
@@ -1699,13 +1676,10 @@ var opensteerRecipeRecordSchema = objectSchema(
1699
1676
  var opensteerAuthRecipeRecordSchema = opensteerRecipeRecordSchema;
1700
1677
  var opensteerNetworkQueryInputSchema = objectSchema(
1701
1678
  {
1702
- source: enumSchema(["live", "saved"], {
1703
- title: "OpensteerNetworkQuerySource"
1704
- }),
1705
1679
  pageRef: pageRefSchema,
1706
1680
  recordId: stringSchema({ minLength: 1 }),
1707
1681
  requestId: stringSchema({ minLength: 1 }),
1708
- actionId: stringSchema({ minLength: 1 }),
1682
+ capture: stringSchema({ minLength: 1 }),
1709
1683
  tag: stringSchema({ minLength: 1 }),
1710
1684
  url: stringSchema({ minLength: 1 }),
1711
1685
  hostname: stringSchema({ minLength: 1 }),
@@ -1729,12 +1703,12 @@ var opensteerNetworkQueryOutputSchema = objectSchema(
1729
1703
  required: ["records"]
1730
1704
  }
1731
1705
  );
1732
- var opensteerNetworkSaveInputSchema = objectSchema(
1706
+ var opensteerNetworkTagInputSchema = objectSchema(
1733
1707
  {
1734
1708
  pageRef: pageRefSchema,
1735
1709
  recordId: stringSchema({ minLength: 1 }),
1736
1710
  requestId: stringSchema({ minLength: 1 }),
1737
- actionId: stringSchema({ minLength: 1 }),
1711
+ capture: stringSchema({ minLength: 1 }),
1738
1712
  tag: stringSchema({ minLength: 1 }),
1739
1713
  url: stringSchema({ minLength: 1 }),
1740
1714
  hostname: stringSchema({ minLength: 1 }),
@@ -1744,21 +1718,22 @@ var opensteerNetworkSaveInputSchema = objectSchema(
1744
1718
  resourceType: networkResourceTypeSchema
1745
1719
  },
1746
1720
  {
1747
- title: "OpensteerNetworkSaveInput",
1721
+ title: "OpensteerNetworkTagInput",
1748
1722
  required: ["tag"]
1749
1723
  }
1750
1724
  );
1751
- var opensteerNetworkSaveOutputSchema = objectSchema(
1725
+ var opensteerNetworkTagOutputSchema = objectSchema(
1752
1726
  {
1753
- savedCount: integerSchema({ minimum: 0 })
1727
+ taggedCount: integerSchema({ minimum: 0 })
1754
1728
  },
1755
1729
  {
1756
- title: "OpensteerNetworkSaveOutput",
1757
- required: ["savedCount"]
1730
+ title: "OpensteerNetworkTagOutput",
1731
+ required: ["taggedCount"]
1758
1732
  }
1759
1733
  );
1760
1734
  var opensteerNetworkClearInputSchema = objectSchema(
1761
1735
  {
1736
+ capture: stringSchema({ minLength: 1 }),
1762
1737
  tag: stringSchema({ minLength: 1 })
1763
1738
  },
1764
1739
  {
@@ -1783,7 +1758,6 @@ var opensteerWriteRequestPlanInputSchema = objectSchema(
1783
1758
  uniqueItems: true
1784
1759
  }),
1785
1760
  provenance: opensteerRegistryProvenanceSchema,
1786
- lifecycle: opensteerRequestPlanLifecycleSchema,
1787
1761
  freshness: opensteerRequestPlanFreshnessSchema,
1788
1762
  payload: opensteerRequestPlanPayloadSchema
1789
1763
  },
@@ -2024,7 +1998,7 @@ var opensteerInferRequestPlanInputSchema = objectSchema(
2024
1998
  recordId: stringSchema({ minLength: 1 }),
2025
1999
  key: stringSchema({ minLength: 1 }),
2026
2000
  version: stringSchema({ minLength: 1 }),
2027
- lifecycle: opensteerRequestPlanLifecycleSchema
2001
+ transport: transportKindSchema
2028
2002
  },
2029
2003
  {
2030
2004
  title: "OpensteerInferRequestPlanInput",
@@ -5217,7 +5191,7 @@ var opensteerSemanticOperationNames = [
5217
5191
  "dom.scroll",
5218
5192
  "dom.extract",
5219
5193
  "network.query",
5220
- "network.save",
5194
+ "network.tag",
5221
5195
  "network.clear",
5222
5196
  "network.minimize",
5223
5197
  "network.diff",
@@ -5278,7 +5252,7 @@ var opensteerPackageRunnableSemanticOperationNames = /* @__PURE__ */ new Set([
5278
5252
  "dom.scroll",
5279
5253
  "dom.extract",
5280
5254
  "network.query",
5281
- "network.save",
5255
+ "network.tag",
5282
5256
  "network.clear",
5283
5257
  "network.minimize",
5284
5258
  "network.diff",
@@ -5598,7 +5572,7 @@ var opensteerPageCloseOutputSchema = objectSchema(
5598
5572
  var opensteerPageGotoInputSchema = objectSchema(
5599
5573
  {
5600
5574
  url: stringSchema(),
5601
- networkTag: stringSchema({ minLength: 1 })
5575
+ captureNetwork: stringSchema({ minLength: 1 })
5602
5576
  },
5603
5577
  {
5604
5578
  title: "OpensteerPageGotoInput",
@@ -5749,7 +5723,7 @@ var opensteerDomClickInputSchema = objectSchema(
5749
5723
  {
5750
5724
  target: opensteerTargetInputSchema,
5751
5725
  persistAsDescription: stringSchema(),
5752
- networkTag: stringSchema({ minLength: 1 })
5726
+ captureNetwork: stringSchema({ minLength: 1 })
5753
5727
  },
5754
5728
  {
5755
5729
  title: "OpensteerDomClickInput",
@@ -5760,7 +5734,7 @@ var opensteerDomHoverInputSchema = objectSchema(
5760
5734
  {
5761
5735
  target: opensteerTargetInputSchema,
5762
5736
  persistAsDescription: stringSchema(),
5763
- networkTag: stringSchema({ minLength: 1 })
5737
+ captureNetwork: stringSchema({ minLength: 1 })
5764
5738
  },
5765
5739
  {
5766
5740
  title: "OpensteerDomHoverInput",
@@ -5773,7 +5747,7 @@ var opensteerDomInputInputSchema = objectSchema(
5773
5747
  text: stringSchema(),
5774
5748
  pressEnter: { type: "boolean" },
5775
5749
  persistAsDescription: stringSchema(),
5776
- networkTag: stringSchema({ minLength: 1 })
5750
+ captureNetwork: stringSchema({ minLength: 1 })
5777
5751
  },
5778
5752
  {
5779
5753
  title: "OpensteerDomInputInput",
@@ -5786,7 +5760,7 @@ var opensteerDomScrollInputSchema = objectSchema(
5786
5760
  direction: enumSchema(["up", "down", "left", "right"]),
5787
5761
  amount: integerSchema({ minimum: 1 }),
5788
5762
  persistAsDescription: stringSchema(),
5789
- networkTag: stringSchema({ minLength: 1 })
5763
+ captureNetwork: stringSchema({ minLength: 1 })
5790
5764
  },
5791
5765
  {
5792
5766
  title: "OpensteerDomScrollInput",
@@ -5987,7 +5961,7 @@ var opensteerComputerExecuteInputSchema = objectSchema(
5987
5961
  {
5988
5962
  action: opensteerComputerActionSchema,
5989
5963
  screenshot: opensteerComputerScreenshotOptionsSchema,
5990
- networkTag: stringSchema({ minLength: 1 })
5964
+ captureNetwork: stringSchema({ minLength: 1 })
5991
5965
  },
5992
5966
  {
5993
5967
  title: "OpensteerComputerExecuteInput",
@@ -6188,18 +6162,17 @@ var opensteerSemanticOperationSpecificationsBase = [
6188
6162
  }),
6189
6163
  defineSemanticOperationSpec({
6190
6164
  name: "network.query",
6191
- description: "Query live or saved network records for reverse engineering workflows.",
6165
+ description: "Query persisted network records for reverse engineering workflows.",
6192
6166
  inputSchema: opensteerNetworkQueryInputSchema,
6193
6167
  outputSchema: opensteerNetworkQueryOutputSchema,
6194
- requiredCapabilities: [],
6195
- resolveRequiredCapabilities: (input) => input.source === "saved" ? [] : input.includeBodies === true ? ["inspect.network", "inspect.networkBodies"] : ["inspect.network"]
6168
+ requiredCapabilities: []
6196
6169
  }),
6197
6170
  defineSemanticOperationSpec({
6198
- name: "network.save",
6199
- description: "Persist filtered live network records into the saved network registry under a tag.",
6200
- inputSchema: opensteerNetworkSaveInputSchema,
6201
- outputSchema: opensteerNetworkSaveOutputSchema,
6202
- requiredCapabilities: ["inspect.network"]
6171
+ name: "network.tag",
6172
+ description: "Apply a tag to persisted network records matching the provided filters.",
6173
+ inputSchema: opensteerNetworkTagInputSchema,
6174
+ outputSchema: opensteerNetworkTagOutputSchema,
6175
+ requiredCapabilities: []
6203
6176
  }),
6204
6177
  defineSemanticOperationSpec({
6205
6178
  name: "network.clear",
@@ -7020,6 +6993,49 @@ function mediaTypeExtension(mediaType) {
7020
6993
  }
7021
6994
  }
7022
6995
 
6996
+ // src/action-boundary.ts
6997
+ var actionBoundaryDiagnosticsBySignal = /* @__PURE__ */ new WeakMap();
6998
+ async function captureActionBoundarySnapshot(engine, pageRef) {
6999
+ const frames = await engine.listFrames({ pageRef });
7000
+ const mainFrame = frames.find((frame) => frame.isMainFrame);
7001
+ if (!mainFrame) {
7002
+ throw new Error(`page ${pageRef} does not expose a main frame`);
7003
+ }
7004
+ return {
7005
+ pageRef,
7006
+ documentRef: mainFrame.documentRef
7007
+ };
7008
+ }
7009
+ function createActionBoundaryDiagnostics(input) {
7010
+ return {
7011
+ trigger: input.boundary.trigger,
7012
+ crossDocument: input.boundary.crossDocument,
7013
+ bootstrapSettled: input.boundary.bootstrapSettled,
7014
+ visualSettled: input.visualSettled,
7015
+ ...input.boundary.timedOutPhase !== void 0 ? { timedOutPhase: input.boundary.timedOutPhase } : !input.visualSettled ? { timedOutPhase: "visual" } : {}
7016
+ };
7017
+ }
7018
+ function recordActionBoundaryDiagnostics(signal, diagnostics) {
7019
+ actionBoundaryDiagnosticsBySignal.set(signal, diagnostics);
7020
+ }
7021
+ function takeActionBoundaryDiagnostics(signal) {
7022
+ const diagnostics = actionBoundaryDiagnosticsBySignal.get(signal);
7023
+ actionBoundaryDiagnosticsBySignal.delete(signal);
7024
+ return diagnostics;
7025
+ }
7026
+ function isSoftSettleTimeoutError(error, signal) {
7027
+ if (isTimeoutError(error)) {
7028
+ return true;
7029
+ }
7030
+ return signal?.aborted === true && isTimeoutError(signal.reason) && (error === signal.reason || isAbortError(error));
7031
+ }
7032
+ function isAbortError(error) {
7033
+ return error instanceof Error && error.name === "AbortError";
7034
+ }
7035
+ function isTimeoutError(error) {
7036
+ return isOpensteerProtocolError(error) && error.code === "timeout";
7037
+ }
7038
+
7023
7039
  // src/internal/errors.ts
7024
7040
  function normalizeThrownOpensteerError(error, fallbackMessage) {
7025
7041
  if (isOpensteerProtocolError(error)) {
@@ -7052,13 +7068,13 @@ var DEFAULT_TIMEOUTS = {
7052
7068
  "page.add-init-script": 1e4,
7053
7069
  "page.snapshot": 15e3,
7054
7070
  "computer.execute": 3e4,
7055
- "dom.click": 1e4,
7071
+ "dom.click": 3e4,
7056
7072
  "dom.hover": 1e4,
7057
- "dom.input": 1e4,
7073
+ "dom.input": 3e4,
7058
7074
  "dom.scroll": 1e4,
7059
7075
  "dom.extract": 15e3,
7060
7076
  "network.query": 15e3,
7061
- "network.save": 15e3,
7077
+ "network.tag": 15e3,
7062
7078
  "network.clear": 1e4,
7063
7079
  "scripts.capture": 15e3,
7064
7080
  "request.raw": 3e4,
@@ -7089,8 +7105,74 @@ var defaultSnapshotSettleObserver = {
7089
7105
  }
7090
7106
  };
7091
7107
  Object.freeze(defaultSnapshotSettleObserver);
7108
+ var DOM_ACTION_VISUAL_STABILITY_PROFILES = {
7109
+ "dom.click": { settleMs: 750, scope: "visible-frames", timeoutMs: 7e3 },
7110
+ "dom.input": { settleMs: 750, scope: "visible-frames", timeoutMs: 7e3 },
7111
+ "dom.scroll": { settleMs: 600, scope: "visible-frames", timeoutMs: 7e3 },
7112
+ "dom.hover": { settleMs: 200, scope: "main-frame", timeoutMs: 2500 }
7113
+ };
7114
+ var DEFAULT_DOM_ACTION_VISUAL_STABILITY_PROFILE = {
7115
+ settleMs: 750,
7116
+ scope: "visible-frames",
7117
+ timeoutMs: 7e3
7118
+ };
7119
+ var NAVIGATION_VISUAL_STABILITY_PROFILE = {
7120
+ settleMs: 750,
7121
+ scope: "visible-frames",
7122
+ timeoutMs: 7e3
7123
+ };
7124
+ var defaultDomActionSettleObserver = {
7125
+ async settle(input) {
7126
+ if (input.trigger !== "dom-action") {
7127
+ return false;
7128
+ }
7129
+ const profile = DOM_ACTION_VISUAL_STABILITY_PROFILES[input.operation] ?? DEFAULT_DOM_ACTION_VISUAL_STABILITY_PROFILE;
7130
+ const effectiveTimeout = input.remainingMs === void 0 ? profile.timeoutMs : Math.min(profile.timeoutMs, input.remainingMs);
7131
+ if (effectiveTimeout <= 0) {
7132
+ return false;
7133
+ }
7134
+ try {
7135
+ await input.engine.waitForVisualStability({
7136
+ pageRef: input.pageRef,
7137
+ timeoutMs: effectiveTimeout,
7138
+ settleMs: profile.settleMs,
7139
+ scope: profile.scope
7140
+ });
7141
+ return true;
7142
+ } catch {
7143
+ return false;
7144
+ }
7145
+ }
7146
+ };
7147
+ Object.freeze(defaultDomActionSettleObserver);
7148
+ var defaultNavigationSettleObserver = {
7149
+ async settle(input) {
7150
+ if (input.trigger !== "navigation") {
7151
+ return false;
7152
+ }
7153
+ const profile = NAVIGATION_VISUAL_STABILITY_PROFILE;
7154
+ const effectiveTimeout = input.remainingMs === void 0 ? profile.timeoutMs : Math.min(profile.timeoutMs, input.remainingMs);
7155
+ if (effectiveTimeout <= 0) {
7156
+ return false;
7157
+ }
7158
+ try {
7159
+ await input.engine.waitForVisualStability({
7160
+ pageRef: input.pageRef,
7161
+ timeoutMs: effectiveTimeout,
7162
+ settleMs: profile.settleMs,
7163
+ scope: profile.scope
7164
+ });
7165
+ return true;
7166
+ } catch {
7167
+ return false;
7168
+ }
7169
+ }
7170
+ };
7171
+ Object.freeze(defaultNavigationSettleObserver);
7092
7172
  var DEFAULT_SETTLE_OBSERVERS = Object.freeze([
7093
- defaultSnapshotSettleObserver
7173
+ defaultSnapshotSettleObserver,
7174
+ defaultDomActionSettleObserver,
7175
+ defaultNavigationSettleObserver
7094
7176
  ]);
7095
7177
  var defaultTimeoutPolicy = {
7096
7178
  resolveTimeoutMs(input) {
@@ -7341,9 +7423,7 @@ var FilesystemRegistryStore = class {
7341
7423
  if (input.version !== void 0) {
7342
7424
  return this.resolveIndexedRecord(key, normalizeNonEmptyString("version", input.version));
7343
7425
  }
7344
- const matches = (await this.readAllRecords()).filter(
7345
- (record) => this.isActive(record) && record.key === key
7346
- );
7426
+ const matches = (await this.readAllRecords()).filter((record) => record.key === key);
7347
7427
  matches.sort(compareByCreatedAtAndId);
7348
7428
  return matches[0];
7349
7429
  }
@@ -7466,8 +7546,10 @@ var FilesystemDescriptorRegistry = class extends FilesystemRegistryStore {
7466
7546
  };
7467
7547
  return this.writeRecord(record);
7468
7548
  }
7469
- isActive(_record) {
7470
- return true;
7549
+ async list(input = {}) {
7550
+ const key = input.key === void 0 ? void 0 : normalizeNonEmptyString("key", input.key);
7551
+ const records = await this.readAllRecords();
7552
+ return key === void 0 ? records : records.filter((record) => record.key === key);
7471
7553
  }
7472
7554
  };
7473
7555
  var FilesystemRequestPlanRegistry = class extends FilesystemRegistryStore {
@@ -7497,7 +7579,6 @@ var FilesystemRequestPlanRegistry = class extends FilesystemRegistryStore {
7497
7579
  tags: normalizeTags(input.tags),
7498
7580
  ...provenance === void 0 ? {} : { provenance },
7499
7581
  payload,
7500
- lifecycle: input.lifecycle ?? "active",
7501
7582
  ...freshness === void 0 ? {} : { freshness }
7502
7583
  };
7503
7584
  return this.writeRecord(record);
@@ -7507,7 +7588,7 @@ var FilesystemRequestPlanRegistry = class extends FilesystemRegistryStore {
7507
7588
  const records = await this.readAllRecords();
7508
7589
  return key === void 0 ? records : records.filter((record) => record.key === key);
7509
7590
  }
7510
- async updateMetadata(input) {
7591
+ async updateFreshness(input) {
7511
7592
  const id = normalizeNonEmptyString("id", input.id);
7512
7593
  return withFilesystemLock(this.writeLockPath(), async () => {
7513
7594
  const existing = await this.getById(id);
@@ -7525,16 +7606,12 @@ var FilesystemRequestPlanRegistry = class extends FilesystemRegistryStore {
7525
7606
  const nextRecord = {
7526
7607
  ...existing,
7527
7608
  updatedAt: nextUpdatedAt,
7528
- lifecycle: input.lifecycle ?? existing.lifecycle,
7529
7609
  ...nextFreshness === void 0 ? {} : { freshness: nextFreshness }
7530
7610
  };
7531
7611
  await writeJsonFileAtomic(this.recordPath(id), nextRecord);
7532
7612
  return nextRecord;
7533
7613
  });
7534
7614
  }
7535
- isActive(record) {
7536
- return record.lifecycle === "active";
7537
- }
7538
7615
  };
7539
7616
  var FilesystemAuthRecipeRegistry = class extends FilesystemRegistryStore {
7540
7617
  constructor(rootPath) {
@@ -7570,9 +7647,6 @@ var FilesystemAuthRecipeRegistry = class extends FilesystemRegistryStore {
7570
7647
  const records = await this.readAllRecords();
7571
7648
  return key === void 0 ? records : records.filter((record) => record.key === key);
7572
7649
  }
7573
- isActive(_record) {
7574
- return true;
7575
- }
7576
7650
  };
7577
7651
  var FilesystemRecipeRegistry = class extends FilesystemRegistryStore {
7578
7652
  constructor(rootPath) {
@@ -7608,9 +7682,6 @@ var FilesystemRecipeRegistry = class extends FilesystemRegistryStore {
7608
7682
  const records = await this.readAllRecords();
7609
7683
  return key === void 0 ? records : records.filter((record) => record.key === key);
7610
7684
  }
7611
- isActive(_record) {
7612
- return true;
7613
- }
7614
7685
  };
7615
7686
  var FilesystemInteractionTraceRegistry = class extends FilesystemRegistryStore {
7616
7687
  constructor(rootPath) {
@@ -7646,9 +7717,6 @@ var FilesystemInteractionTraceRegistry = class extends FilesystemRegistryStore {
7646
7717
  const records = await this.readAllRecords();
7647
7718
  return key === void 0 ? records : records.filter((record) => record.key === key);
7648
7719
  }
7649
- isActive(_record) {
7650
- return true;
7651
- }
7652
7720
  };
7653
7721
  var FilesystemReverseCaseRegistry = class extends FilesystemRegistryStore {
7654
7722
  constructor(rootPath) {
@@ -7712,9 +7780,6 @@ var FilesystemReverseCaseRegistry = class extends FilesystemRegistryStore {
7712
7780
  return nextRecord;
7713
7781
  });
7714
7782
  }
7715
- isActive(_record) {
7716
- return true;
7717
- }
7718
7783
  };
7719
7784
  var FilesystemReversePackageRegistry = class extends FilesystemRegistryStore {
7720
7785
  constructor(rootPath) {
@@ -7750,9 +7815,6 @@ var FilesystemReversePackageRegistry = class extends FilesystemRegistryStore {
7750
7815
  const records = await this.readAllRecords();
7751
7816
  return key === void 0 ? records : records.filter((record) => record.key === key);
7752
7817
  }
7753
- isActive(_record) {
7754
- return true;
7755
- }
7756
7818
  };
7757
7819
  var FilesystemReverseReportRegistry = class extends FilesystemRegistryStore {
7758
7820
  constructor(rootPath) {
@@ -7788,9 +7850,6 @@ var FilesystemReverseReportRegistry = class extends FilesystemRegistryStore {
7788
7850
  const records = await this.readAllRecords();
7789
7851
  return key === void 0 ? records : records.filter((record) => record.key === key);
7790
7852
  }
7791
- isActive(_record) {
7792
- return true;
7793
- }
7794
7853
  };
7795
7854
  function createDescriptorRegistry(rootPath) {
7796
7855
  return new FilesystemDescriptorRegistry(rootPath);
@@ -7818,101 +7877,20 @@ function createReverseReportRegistry(rootPath) {
7818
7877
  }
7819
7878
  var TAG_DELIMITER = "";
7820
7879
  var NODE_SQLITE_SPECIFIER = `node:${"sqlite"}`;
7880
+ var SAVED_NETWORK_SQLITE_SUPPORT_ERROR = "Saved-network operations require Node's built-in SQLite support. Use a Node runtime with node:sqlite enabled.";
7821
7881
  var SqliteSavedNetworkStore = class {
7822
7882
  databasePath;
7823
7883
  database;
7884
+ directoryInitialization;
7885
+ databaseInitialization;
7824
7886
  constructor(rootPath) {
7825
7887
  this.databasePath = path6.join(rootPath, "registry", "saved-network.sqlite");
7826
7888
  }
7827
7889
  async initialize() {
7828
- await ensureDirectory(path6.dirname(this.databasePath));
7829
- const { DatabaseSync } = await import(NODE_SQLITE_SPECIFIER);
7830
- const database = new DatabaseSync(this.databasePath);
7831
- database.exec("PRAGMA journal_mode = WAL");
7832
- database.exec("PRAGMA foreign_keys = ON");
7833
- database.exec(`
7834
- CREATE TABLE IF NOT EXISTS saved_network_records (
7835
- record_id TEXT PRIMARY KEY,
7836
- request_id TEXT NOT NULL,
7837
- session_ref TEXT NOT NULL,
7838
- page_ref TEXT,
7839
- page_ref_key TEXT NOT NULL,
7840
- frame_ref TEXT,
7841
- document_ref TEXT,
7842
- action_id TEXT,
7843
- method TEXT NOT NULL,
7844
- method_lc TEXT NOT NULL,
7845
- url TEXT NOT NULL,
7846
- url_lc TEXT NOT NULL,
7847
- hostname TEXT NOT NULL,
7848
- hostname_lc TEXT NOT NULL,
7849
- path TEXT NOT NULL,
7850
- path_lc TEXT NOT NULL,
7851
- status INTEGER,
7852
- status_text TEXT,
7853
- resource_type TEXT NOT NULL,
7854
- navigation_request INTEGER NOT NULL,
7855
- request_headers_json TEXT NOT NULL,
7856
- response_headers_json TEXT NOT NULL,
7857
- request_body_json TEXT,
7858
- response_body_json TEXT,
7859
- initiator_json TEXT,
7860
- timing_json TEXT,
7861
- transfer_json TEXT,
7862
- source_json TEXT,
7863
- capture_state TEXT NOT NULL,
7864
- request_body_state TEXT NOT NULL,
7865
- response_body_state TEXT NOT NULL,
7866
- request_body_skip_reason TEXT,
7867
- response_body_skip_reason TEXT,
7868
- request_body_error TEXT,
7869
- response_body_error TEXT,
7870
- redirect_from_request_id TEXT,
7871
- redirect_to_request_id TEXT,
7872
- saved_at INTEGER NOT NULL
7873
- );
7874
-
7875
- CREATE UNIQUE INDEX IF NOT EXISTS saved_network_records_scope_request
7876
- ON saved_network_records (session_ref, page_ref_key, request_id);
7877
-
7878
- CREATE INDEX IF NOT EXISTS saved_network_records_saved_at
7879
- ON saved_network_records (saved_at DESC);
7880
-
7881
- CREATE TABLE IF NOT EXISTS saved_network_tags (
7882
- record_id TEXT NOT NULL REFERENCES saved_network_records(record_id) ON DELETE CASCADE,
7883
- tag TEXT NOT NULL,
7884
- PRIMARY KEY (record_id, tag)
7885
- );
7886
-
7887
- CREATE INDEX IF NOT EXISTS saved_network_tags_tag
7888
- ON saved_network_tags (tag);
7889
- `);
7890
- this.ensureColumn(
7891
- database,
7892
- "saved_network_records",
7893
- "capture_state",
7894
- "TEXT NOT NULL DEFAULT 'complete'"
7895
- );
7896
- this.ensureColumn(
7897
- database,
7898
- "saved_network_records",
7899
- "request_body_state",
7900
- "TEXT NOT NULL DEFAULT 'skipped'"
7901
- );
7902
- this.ensureColumn(
7903
- database,
7904
- "saved_network_records",
7905
- "response_body_state",
7906
- "TEXT NOT NULL DEFAULT 'skipped'"
7907
- );
7908
- this.ensureColumn(database, "saved_network_records", "request_body_skip_reason", "TEXT");
7909
- this.ensureColumn(database, "saved_network_records", "response_body_skip_reason", "TEXT");
7910
- this.ensureColumn(database, "saved_network_records", "request_body_error", "TEXT");
7911
- this.ensureColumn(database, "saved_network_records", "response_body_error", "TEXT");
7912
- this.database = database;
7890
+ await this.ensureDatabaseDirectory();
7913
7891
  }
7914
- async save(records, tag) {
7915
- const database = this.requireDatabase();
7892
+ async save(records, options) {
7893
+ const database = await this.requireDatabase();
7916
7894
  const readExisting = database.prepare(`
7917
7895
  SELECT record_id
7918
7896
  FROM saved_network_records
@@ -7920,123 +7898,7 @@ var SqliteSavedNetworkStore = class {
7920
7898
  AND page_ref_key = @page_ref_key
7921
7899
  AND request_id = @request_id
7922
7900
  `);
7923
- const upsertRecord = database.prepare(`
7924
- INSERT INTO saved_network_records (
7925
- record_id,
7926
- request_id,
7927
- session_ref,
7928
- page_ref,
7929
- page_ref_key,
7930
- frame_ref,
7931
- document_ref,
7932
- action_id,
7933
- method,
7934
- method_lc,
7935
- url,
7936
- url_lc,
7937
- hostname,
7938
- hostname_lc,
7939
- path,
7940
- path_lc,
7941
- status,
7942
- status_text,
7943
- resource_type,
7944
- navigation_request,
7945
- request_headers_json,
7946
- response_headers_json,
7947
- request_body_json,
7948
- response_body_json,
7949
- initiator_json,
7950
- timing_json,
7951
- transfer_json,
7952
- source_json,
7953
- capture_state,
7954
- request_body_state,
7955
- response_body_state,
7956
- request_body_skip_reason,
7957
- response_body_skip_reason,
7958
- request_body_error,
7959
- response_body_error,
7960
- redirect_from_request_id,
7961
- redirect_to_request_id,
7962
- saved_at
7963
- ) VALUES (
7964
- @record_id,
7965
- @request_id,
7966
- @session_ref,
7967
- @page_ref,
7968
- @page_ref_key,
7969
- @frame_ref,
7970
- @document_ref,
7971
- @action_id,
7972
- @method,
7973
- @method_lc,
7974
- @url,
7975
- @url_lc,
7976
- @hostname,
7977
- @hostname_lc,
7978
- @path,
7979
- @path_lc,
7980
- @status,
7981
- @status_text,
7982
- @resource_type,
7983
- @navigation_request,
7984
- @request_headers_json,
7985
- @response_headers_json,
7986
- @request_body_json,
7987
- @response_body_json,
7988
- @initiator_json,
7989
- @timing_json,
7990
- @transfer_json,
7991
- @source_json,
7992
- @capture_state,
7993
- @request_body_state,
7994
- @response_body_state,
7995
- @request_body_skip_reason,
7996
- @response_body_skip_reason,
7997
- @request_body_error,
7998
- @response_body_error,
7999
- @redirect_from_request_id,
8000
- @redirect_to_request_id,
8001
- @saved_at
8002
- )
8003
- ON CONFLICT(record_id) DO UPDATE SET
8004
- page_ref = excluded.page_ref,
8005
- page_ref_key = excluded.page_ref_key,
8006
- frame_ref = excluded.frame_ref,
8007
- document_ref = excluded.document_ref,
8008
- action_id = excluded.action_id,
8009
- method = excluded.method,
8010
- method_lc = excluded.method_lc,
8011
- url = excluded.url,
8012
- url_lc = excluded.url_lc,
8013
- hostname = excluded.hostname,
8014
- hostname_lc = excluded.hostname_lc,
8015
- path = excluded.path,
8016
- path_lc = excluded.path_lc,
8017
- status = excluded.status,
8018
- status_text = excluded.status_text,
8019
- resource_type = excluded.resource_type,
8020
- navigation_request = excluded.navigation_request,
8021
- request_headers_json = excluded.request_headers_json,
8022
- response_headers_json = excluded.response_headers_json,
8023
- request_body_json = excluded.request_body_json,
8024
- response_body_json = excluded.response_body_json,
8025
- initiator_json = excluded.initiator_json,
8026
- timing_json = excluded.timing_json,
8027
- transfer_json = excluded.transfer_json,
8028
- source_json = excluded.source_json,
8029
- capture_state = excluded.capture_state,
8030
- request_body_state = excluded.request_body_state,
8031
- response_body_state = excluded.response_body_state,
8032
- request_body_skip_reason = excluded.request_body_skip_reason,
8033
- response_body_skip_reason = excluded.response_body_skip_reason,
8034
- request_body_error = excluded.request_body_error,
8035
- response_body_error = excluded.response_body_error,
8036
- redirect_from_request_id = excluded.redirect_from_request_id,
8037
- redirect_to_request_id = excluded.redirect_to_request_id,
8038
- saved_at = excluded.saved_at
8039
- `);
7901
+ const upsertRecord = database.prepare(buildSavedNetworkUpsertSql(options.bodyWriteMode));
8040
7902
  const insertTag = database.prepare(`
8041
7903
  INSERT OR IGNORE INTO saved_network_tags (record_id, tag)
8042
7904
  VALUES (@record_id, @tag)
@@ -8060,7 +7922,7 @@ var SqliteSavedNetworkStore = class {
8060
7922
  page_ref_key: pageRefKey,
8061
7923
  frame_ref: entry.record.frameRef ?? null,
8062
7924
  document_ref: entry.record.documentRef ?? null,
8063
- action_id: entry.actionId ?? null,
7925
+ capture: entry.capture ?? null,
8064
7926
  method: entry.record.method,
8065
7927
  method_lc: entry.record.method.toLowerCase(),
8066
7928
  url: entry.record.url,
@@ -8092,10 +7954,14 @@ var SqliteSavedNetworkStore = class {
8092
7954
  redirect_to_request_id: entry.record.redirectToRequestId ?? null,
8093
7955
  saved_at: entry.savedAt ?? Date.now()
8094
7956
  });
8095
- if (tag !== void 0) {
7957
+ const tags = new Set(entry.tags ?? []);
7958
+ if (options.tag !== void 0) {
7959
+ tags.add(options.tag);
7960
+ }
7961
+ for (const currentTag of tags) {
8096
7962
  const result = insertTag.run({
8097
7963
  record_id: recordId,
8098
- tag
7964
+ tag: currentTag
8099
7965
  });
8100
7966
  savedCount += result.changes ?? 0;
8101
7967
  }
@@ -8103,8 +7969,41 @@ var SqliteSavedNetworkStore = class {
8103
7969
  return savedCount;
8104
7970
  });
8105
7971
  }
7972
+ async tagByFilter(filter, tag) {
7973
+ const database = await this.requireDatabase();
7974
+ const { whereSql, parameters } = buildSavedNetworkWhere(filter);
7975
+ const selectRecords = database.prepare(
7976
+ `
7977
+ SELECT r.record_id
7978
+ FROM saved_network_records r
7979
+ ${whereSql}
7980
+ `
7981
+ );
7982
+ const insertTag = database.prepare(`
7983
+ INSERT OR IGNORE INTO saved_network_tags (record_id, tag)
7984
+ VALUES (@record_id, @tag)
7985
+ `);
7986
+ return withSqliteTransaction(database, () => {
7987
+ let taggedCount = 0;
7988
+ const rows = selectRecords.all(
7989
+ ...parameters
7990
+ );
7991
+ for (const row of rows) {
7992
+ const recordId = row.record_id;
7993
+ if (typeof recordId !== "string") {
7994
+ continue;
7995
+ }
7996
+ const result = insertTag.run({
7997
+ record_id: recordId,
7998
+ tag
7999
+ });
8000
+ taggedCount += result.changes ?? 0;
8001
+ }
8002
+ return taggedCount;
8003
+ });
8004
+ }
8106
8005
  async query(input = {}) {
8107
- const database = this.requireDatabase();
8006
+ const database = await this.requireDatabase();
8108
8007
  const limit = Math.max(1, Math.min(input.limit ?? 50, 200));
8109
8008
  const { whereSql, parameters } = buildSavedNetworkWhere(input);
8110
8009
  const rows = database.prepare(
@@ -8135,48 +8034,160 @@ var SqliteSavedNetworkStore = class {
8135
8034
  return record;
8136
8035
  }
8137
8036
  async clear(input = {}) {
8138
- const database = this.requireDatabase();
8139
- const countAll = database.prepare(`
8140
- SELECT COUNT(*) AS cleared
8141
- FROM saved_network_records
8142
- `);
8143
- const countByTag = database.prepare(`
8144
- SELECT COUNT(DISTINCT record_id) AS cleared
8145
- FROM saved_network_tags
8146
- WHERE tag = @tag
8147
- `);
8148
- const deleteAllTags = database.prepare(`DELETE FROM saved_network_tags`);
8037
+ const database = await this.requireDatabase();
8038
+ const countAll = database.prepare(`SELECT COUNT(*) AS cleared FROM saved_network_records`);
8149
8039
  const deleteAllRecords = database.prepare(`DELETE FROM saved_network_records`);
8150
- const deleteTag = database.prepare(`
8151
- DELETE FROM saved_network_tags
8152
- WHERE tag = @tag
8040
+ const { whereSql, parameters } = buildSavedNetworkWhere(input);
8041
+ const countFiltered = database.prepare(`
8042
+ SELECT COUNT(*) AS cleared
8043
+ FROM saved_network_records r
8044
+ ${whereSql}
8153
8045
  `);
8154
- const deleteOrphans = database.prepare(`
8046
+ const deleteFiltered = database.prepare(`
8155
8047
  DELETE FROM saved_network_records
8156
- WHERE NOT EXISTS (
8157
- SELECT 1
8158
- FROM saved_network_tags t
8159
- WHERE t.record_id = saved_network_records.record_id
8048
+ WHERE record_id IN (
8049
+ SELECT r.record_id
8050
+ FROM saved_network_records r
8051
+ ${whereSql}
8160
8052
  )
8161
8053
  `);
8162
8054
  return withSqliteTransaction(database, () => {
8163
- const tag = input.tag;
8164
- const cleared = tag === void 0 ? countAll.get().cleared : countByTag.get({ tag }).cleared;
8165
- if (tag === void 0) {
8166
- deleteAllTags.run();
8055
+ if (input.capture === void 0 && input.tag === void 0) {
8056
+ const cleared2 = countAll.get().cleared;
8167
8057
  deleteAllRecords.run();
8168
- return cleared;
8058
+ return cleared2;
8169
8059
  }
8170
- deleteTag.run({ tag });
8171
- deleteOrphans.run();
8060
+ const args = parameters;
8061
+ const cleared = countFiltered.get(...args).cleared;
8062
+ deleteFiltered.run(...args);
8172
8063
  return cleared;
8173
8064
  });
8174
8065
  }
8175
- requireDatabase() {
8176
- if (!this.database) {
8177
- throw new Error("saved network store is not initialized");
8066
+ async requireDatabase() {
8067
+ if (this.database) {
8068
+ return this.database;
8069
+ }
8070
+ this.databaseInitialization ??= this.openDatabase();
8071
+ try {
8072
+ return await this.databaseInitialization;
8073
+ } catch (error) {
8074
+ this.databaseInitialization = void 0;
8075
+ throw error;
8076
+ }
8077
+ }
8078
+ async openDatabase() {
8079
+ await this.ensureDatabaseDirectory();
8080
+ let DatabaseSync;
8081
+ try {
8082
+ ({ DatabaseSync } = await import(NODE_SQLITE_SPECIFIER));
8083
+ } catch (error) {
8084
+ throw normalizeSqliteImportError(error);
8178
8085
  }
8179
- return this.database;
8086
+ const database = new DatabaseSync(this.databasePath);
8087
+ try {
8088
+ this.configureDatabase(database);
8089
+ this.database = database;
8090
+ return database;
8091
+ } catch (error) {
8092
+ closeSqliteDatabase(database);
8093
+ throw error;
8094
+ }
8095
+ }
8096
+ async ensureDatabaseDirectory() {
8097
+ this.directoryInitialization ??= ensureDirectory(path6.dirname(this.databasePath)).catch(
8098
+ (error) => {
8099
+ this.directoryInitialization = void 0;
8100
+ throw error;
8101
+ }
8102
+ );
8103
+ await this.directoryInitialization;
8104
+ }
8105
+ configureDatabase(database) {
8106
+ database.exec("PRAGMA journal_mode = WAL");
8107
+ database.exec("PRAGMA foreign_keys = ON");
8108
+ database.exec(`
8109
+ CREATE TABLE IF NOT EXISTS saved_network_records (
8110
+ record_id TEXT PRIMARY KEY,
8111
+ request_id TEXT NOT NULL,
8112
+ session_ref TEXT NOT NULL,
8113
+ page_ref TEXT,
8114
+ page_ref_key TEXT NOT NULL,
8115
+ frame_ref TEXT,
8116
+ document_ref TEXT,
8117
+ capture TEXT,
8118
+ method TEXT NOT NULL,
8119
+ method_lc TEXT NOT NULL,
8120
+ url TEXT NOT NULL,
8121
+ url_lc TEXT NOT NULL,
8122
+ hostname TEXT NOT NULL,
8123
+ hostname_lc TEXT NOT NULL,
8124
+ path TEXT NOT NULL,
8125
+ path_lc TEXT NOT NULL,
8126
+ status INTEGER,
8127
+ status_text TEXT,
8128
+ resource_type TEXT NOT NULL,
8129
+ navigation_request INTEGER NOT NULL,
8130
+ request_headers_json TEXT NOT NULL,
8131
+ response_headers_json TEXT NOT NULL,
8132
+ request_body_json TEXT,
8133
+ response_body_json TEXT,
8134
+ initiator_json TEXT,
8135
+ timing_json TEXT,
8136
+ transfer_json TEXT,
8137
+ source_json TEXT,
8138
+ capture_state TEXT NOT NULL,
8139
+ request_body_state TEXT NOT NULL,
8140
+ response_body_state TEXT NOT NULL,
8141
+ request_body_skip_reason TEXT,
8142
+ response_body_skip_reason TEXT,
8143
+ request_body_error TEXT,
8144
+ response_body_error TEXT,
8145
+ redirect_from_request_id TEXT,
8146
+ redirect_to_request_id TEXT,
8147
+ saved_at INTEGER NOT NULL
8148
+ );
8149
+
8150
+ CREATE UNIQUE INDEX IF NOT EXISTS saved_network_records_scope_request
8151
+ ON saved_network_records (session_ref, page_ref_key, request_id);
8152
+
8153
+ CREATE INDEX IF NOT EXISTS saved_network_records_saved_at
8154
+ ON saved_network_records (saved_at DESC);
8155
+
8156
+ CREATE INDEX IF NOT EXISTS saved_network_records_capture
8157
+ ON saved_network_records (capture);
8158
+
8159
+ CREATE TABLE IF NOT EXISTS saved_network_tags (
8160
+ record_id TEXT NOT NULL REFERENCES saved_network_records(record_id) ON DELETE CASCADE,
8161
+ tag TEXT NOT NULL,
8162
+ PRIMARY KEY (record_id, tag)
8163
+ );
8164
+
8165
+ CREATE INDEX IF NOT EXISTS saved_network_tags_tag
8166
+ ON saved_network_tags (tag);
8167
+ `);
8168
+ this.ensureColumn(
8169
+ database,
8170
+ "saved_network_records",
8171
+ "capture_state",
8172
+ "TEXT NOT NULL DEFAULT 'complete'"
8173
+ );
8174
+ this.ensureColumn(database, "saved_network_records", "capture", "TEXT");
8175
+ this.ensureColumn(
8176
+ database,
8177
+ "saved_network_records",
8178
+ "request_body_state",
8179
+ "TEXT NOT NULL DEFAULT 'skipped'"
8180
+ );
8181
+ this.ensureColumn(
8182
+ database,
8183
+ "saved_network_records",
8184
+ "response_body_state",
8185
+ "TEXT NOT NULL DEFAULT 'skipped'"
8186
+ );
8187
+ this.ensureColumn(database, "saved_network_records", "request_body_skip_reason", "TEXT");
8188
+ this.ensureColumn(database, "saved_network_records", "response_body_skip_reason", "TEXT");
8189
+ this.ensureColumn(database, "saved_network_records", "request_body_error", "TEXT");
8190
+ this.ensureColumn(database, "saved_network_records", "response_body_error", "TEXT");
8180
8191
  }
8181
8192
  ensureColumn(database, table, column, definition) {
8182
8193
  const rows = database.prepare(`PRAGMA table_info(${table})`).all();
@@ -8189,6 +8200,10 @@ var SqliteSavedNetworkStore = class {
8189
8200
  function buildSavedNetworkWhere(input) {
8190
8201
  const clauses = [];
8191
8202
  const parameters = [];
8203
+ if (input.pageRef !== void 0) {
8204
+ clauses.push("r.page_ref_key = ?");
8205
+ parameters.push(input.pageRef);
8206
+ }
8192
8207
  if (input.recordId !== void 0) {
8193
8208
  clauses.push("r.record_id = ?");
8194
8209
  parameters.push(input.recordId);
@@ -8197,9 +8212,9 @@ function buildSavedNetworkWhere(input) {
8197
8212
  clauses.push("r.request_id = ?");
8198
8213
  parameters.push(input.requestId);
8199
8214
  }
8200
- if (input.actionId !== void 0) {
8201
- clauses.push("r.action_id = ?");
8202
- parameters.push(input.actionId);
8215
+ if (input.capture !== void 0) {
8216
+ clauses.push("r.capture = ?");
8217
+ parameters.push(input.capture);
8203
8218
  }
8204
8219
  if (input.tag !== void 0) {
8205
8220
  clauses.push(`
@@ -8241,6 +8256,127 @@ function buildSavedNetworkWhere(input) {
8241
8256
  parameters
8242
8257
  };
8243
8258
  }
8259
+ function buildSavedNetworkUpsertSql(bodyWriteMode) {
8260
+ const bodyUpdateSql = bodyWriteMode === "authoritative" ? `
8261
+ request_body_json = excluded.request_body_json,
8262
+ response_body_json = excluded.response_body_json,
8263
+ request_body_state = excluded.request_body_state,
8264
+ response_body_state = excluded.response_body_state,
8265
+ request_body_skip_reason = excluded.request_body_skip_reason,
8266
+ response_body_skip_reason = excluded.response_body_skip_reason,
8267
+ request_body_error = excluded.request_body_error,
8268
+ response_body_error = excluded.response_body_error,
8269
+ ` : "";
8270
+ return `
8271
+ INSERT INTO saved_network_records (
8272
+ record_id,
8273
+ request_id,
8274
+ session_ref,
8275
+ page_ref,
8276
+ page_ref_key,
8277
+ frame_ref,
8278
+ document_ref,
8279
+ capture,
8280
+ method,
8281
+ method_lc,
8282
+ url,
8283
+ url_lc,
8284
+ hostname,
8285
+ hostname_lc,
8286
+ path,
8287
+ path_lc,
8288
+ status,
8289
+ status_text,
8290
+ resource_type,
8291
+ navigation_request,
8292
+ request_headers_json,
8293
+ response_headers_json,
8294
+ request_body_json,
8295
+ response_body_json,
8296
+ initiator_json,
8297
+ timing_json,
8298
+ transfer_json,
8299
+ source_json,
8300
+ capture_state,
8301
+ request_body_state,
8302
+ response_body_state,
8303
+ request_body_skip_reason,
8304
+ response_body_skip_reason,
8305
+ request_body_error,
8306
+ response_body_error,
8307
+ redirect_from_request_id,
8308
+ redirect_to_request_id,
8309
+ saved_at
8310
+ ) VALUES (
8311
+ @record_id,
8312
+ @request_id,
8313
+ @session_ref,
8314
+ @page_ref,
8315
+ @page_ref_key,
8316
+ @frame_ref,
8317
+ @document_ref,
8318
+ @capture,
8319
+ @method,
8320
+ @method_lc,
8321
+ @url,
8322
+ @url_lc,
8323
+ @hostname,
8324
+ @hostname_lc,
8325
+ @path,
8326
+ @path_lc,
8327
+ @status,
8328
+ @status_text,
8329
+ @resource_type,
8330
+ @navigation_request,
8331
+ @request_headers_json,
8332
+ @response_headers_json,
8333
+ @request_body_json,
8334
+ @response_body_json,
8335
+ @initiator_json,
8336
+ @timing_json,
8337
+ @transfer_json,
8338
+ @source_json,
8339
+ @capture_state,
8340
+ @request_body_state,
8341
+ @response_body_state,
8342
+ @request_body_skip_reason,
8343
+ @response_body_skip_reason,
8344
+ @request_body_error,
8345
+ @response_body_error,
8346
+ @redirect_from_request_id,
8347
+ @redirect_to_request_id,
8348
+ @saved_at
8349
+ )
8350
+ ON CONFLICT(record_id) DO UPDATE SET
8351
+ page_ref = excluded.page_ref,
8352
+ page_ref_key = excluded.page_ref_key,
8353
+ frame_ref = excluded.frame_ref,
8354
+ document_ref = excluded.document_ref,
8355
+ capture = excluded.capture,
8356
+ method = excluded.method,
8357
+ method_lc = excluded.method_lc,
8358
+ url = excluded.url,
8359
+ url_lc = excluded.url_lc,
8360
+ hostname = excluded.hostname,
8361
+ hostname_lc = excluded.hostname_lc,
8362
+ path = excluded.path,
8363
+ path_lc = excluded.path_lc,
8364
+ status = excluded.status,
8365
+ status_text = excluded.status_text,
8366
+ resource_type = excluded.resource_type,
8367
+ navigation_request = excluded.navigation_request,
8368
+ request_headers_json = excluded.request_headers_json,
8369
+ response_headers_json = excluded.response_headers_json,
8370
+ ${bodyUpdateSql} initiator_json = excluded.initiator_json,
8371
+ timing_json = excluded.timing_json,
8372
+ transfer_json = excluded.transfer_json,
8373
+ source_json = excluded.source_json,
8374
+ capture_state = excluded.capture_state,
8375
+ redirect_from_request_id = excluded.redirect_from_request_id,
8376
+ redirect_to_request_id = excluded.redirect_to_request_id,
8377
+ saved_at = MIN(saved_network_records.saved_at, excluded.saved_at)
8378
+ `;
8379
+ }
8244
8380
  function inflateSavedNetworkRow(row, includeBodies) {
8245
8381
  const requestBody = includeBodies && row.request_body_json !== null ? JSON.parse(row.request_body_json) : void 0;
8246
8382
  const responseBody = includeBodies && row.response_body_json !== null ? JSON.parse(row.response_body_json) : void 0;
@@ -8311,8 +8447,7 @@ function inflateSavedNetworkRow(row, includeBodies) {
8311
8447
  }
8312
8448
  return {
8313
8449
  recordId: row.record_id,
8314
- source: "saved",
8315
- ...row.action_id === null ? {} : { actionId: row.action_id },
8450
+ ...row.capture === null ? {} : { capture: row.capture },
8316
8451
  ...row.tags === null || row.tags.length === 0 ? {} : { tags: row.tags.split(TAG_DELIMITER).filter((tag) => tag.length > 0) },
8317
8452
  savedAt: row.saved_at,
8318
8453
  record
@@ -8321,6 +8456,20 @@ function inflateSavedNetworkRow(row, includeBodies) {
8321
8456
  function stringifyOptional(value) {
8322
8457
  return value === void 0 ? null : JSON.stringify(value);
8323
8458
  }
8459
+ function normalizeSqliteImportError(error) {
8460
+ if (error instanceof Error && error.code === "ERR_UNKNOWN_BUILTIN_MODULE" && error.message.includes(NODE_SQLITE_SPECIFIER)) {
8461
+ return new Error(SAVED_NETWORK_SQLITE_SUPPORT_ERROR, {
8462
+ cause: error
8463
+ });
8464
+ }
8465
+ return error instanceof Error ? error : new Error(String(error));
8466
+ }
8467
+ function closeSqliteDatabase(database) {
8468
+ try {
8469
+ database.close();
8470
+ } catch {
8471
+ }
8472
+ }
8324
8473
  function withSqliteTransaction(database, task) {
8325
8474
  database.exec("BEGIN IMMEDIATE");
8326
8475
  try {
@@ -8530,8 +8679,8 @@ async function createFilesystemOpensteerWorkspace(options) {
8530
8679
  const browserManifestPath = path6.join(browserPath, "manifest.json");
8531
8680
  const browserUserDataDir = path6.join(browserPath, "user-data");
8532
8681
  const livePath = path6.join(options.rootPath, "live");
8533
- const liveSessionPath = path6.join(livePath, "session.json");
8534
- const liveBrowserPath = path6.join(livePath, "browser.json");
8682
+ const liveLocalPath = path6.join(livePath, "local.json");
8683
+ const liveCloudPath = path6.join(livePath, "cloud.json");
8535
8684
  const artifactsPath = path6.join(options.rootPath, "artifacts");
8536
8685
  const tracesPath = path6.join(options.rootPath, "traces");
8537
8686
  const registryPath = path6.join(options.rootPath, "registry");
@@ -8606,8 +8755,8 @@ async function createFilesystemOpensteerWorkspace(options) {
8606
8755
  browserManifestPath,
8607
8756
  browserUserDataDir,
8608
8757
  livePath,
8609
- liveSessionPath,
8610
- liveBrowserPath,
8758
+ liveLocalPath,
8759
+ liveCloudPath,
8611
8760
  artifactsPath,
8612
8761
  tracesPath,
8613
8762
  registryPath,
@@ -8631,6 +8780,16 @@ async function createFilesystemOpensteerWorkspace(options) {
8631
8780
  };
8632
8781
  }
8633
8782
 
8783
+ // src/runtimes/dom/errors.ts
8784
+ var ElementPathError = class extends Error {
8785
+ code;
8786
+ constructor(code, message) {
8787
+ super(message);
8788
+ this.name = "ElementPathError";
8789
+ this.code = code;
8790
+ }
8791
+ };
8792
+
8634
8793
  // src/runtimes/dom/match-policy.ts
8635
8794
  var ATTRIBUTE_DENY_KEYS = /* @__PURE__ */ new Set([
8636
8795
  "style",
@@ -9191,16 +9350,6 @@ function readDescriptorToken(value, index) {
9191
9350
  nextIndex: cursor
9192
9351
  };
9193
9352
  }
9194
-
9195
- // src/runtimes/dom/errors.ts
9196
- var ElementPathError = class extends Error {
9197
- code;
9198
- constructor(code, message) {
9199
- super(message);
9200
- this.name = "ElementPathError";
9201
- this.code = code;
9202
- }
9203
- };
9204
9353
  var selectorAdapter = {
9205
9354
  isTag(node) {
9206
9355
  return node.kind === "element" && node.source.nodeType === 1;
@@ -9955,24 +10104,31 @@ function collectChildrenInScope(index, node, scopeHostNodeRef) {
9955
10104
  function getShadowScopeNodeRef(index, node) {
9956
10105
  return findContainingShadowHostNode(index, node)?.nodeRef;
9957
10106
  }
10107
+
10108
+ // src/runtimes/dom/descriptors.ts
9958
10109
  function createDomDescriptorStore(options) {
9959
- const namespace = normalizeNamespace(options.namespace);
10110
+ const namespace = normalizeDomDescriptorNamespace(options.namespace);
9960
10111
  if (options.root) {
9961
10112
  return new FilesystemDomDescriptorStore(options.root.registry.descriptors, namespace);
9962
10113
  }
9963
10114
  return new MemoryDomDescriptorStore(namespace);
9964
10115
  }
9965
- function descriptionKey(namespace, method, description) {
9966
- return `dom:${namespace}:${method}:${sha256Hex2(description.trim())}`;
10116
+ function hashDomDescriptorDescription(description) {
10117
+ return sha256Hex2(description.trim());
9967
10118
  }
9968
- function normalizeNamespace(namespace) {
10119
+ function buildDomDescriptorKey(options) {
10120
+ return `dom:${normalizeDomDescriptorNamespace(options.namespace)}:${options.method}:${hashDomDescriptorDescription(
10121
+ options.description
10122
+ )}`;
10123
+ }
10124
+ function normalizeDomDescriptorNamespace(namespace) {
9969
10125
  const normalized = String(namespace || "default").trim();
9970
10126
  return normalized.length === 0 ? "default" : normalized;
9971
10127
  }
9972
10128
  function sha256Hex2(value) {
9973
10129
  return createHash("sha256").update(value).digest("hex");
9974
10130
  }
9975
- function buildPayload(input) {
10131
+ function buildDomDescriptorPayload(input) {
9976
10132
  return {
9977
10133
  kind: "dom-target",
9978
10134
  method: input.method,
@@ -9981,6 +10137,9 @@ function buildPayload(input) {
9981
10137
  ...input.sourceUrl === void 0 ? {} : { sourceUrl: input.sourceUrl }
9982
10138
  };
9983
10139
  }
10140
+ function buildDomDescriptorVersion(payload) {
10141
+ return sha256Hex2(canonicalJsonString(payload));
10142
+ }
9984
10143
  function parseDomDescriptorRecord(record) {
9985
10144
  const payload = record.payload;
9986
10145
  if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
@@ -10022,7 +10181,11 @@ var FilesystemDomDescriptorStore = class {
10022
10181
  }
10023
10182
  async read(input) {
10024
10183
  const record = await this.registry.resolve({
10025
- key: descriptionKey(this.namespace, input.method, input.description)
10184
+ key: buildDomDescriptorKey({
10185
+ namespace: this.namespace,
10186
+ method: input.method,
10187
+ description: input.description
10188
+ })
10026
10189
  });
10027
10190
  if (!record) {
10028
10191
  return void 0;
@@ -10030,9 +10193,13 @@ var FilesystemDomDescriptorStore = class {
10030
10193
  return parseDomDescriptorRecord(record);
10031
10194
  }
10032
10195
  async write(input) {
10033
- const payload = buildPayload(input);
10034
- const key = descriptionKey(this.namespace, input.method, input.description);
10035
- const version = sha256Hex2(canonicalJsonString(payload));
10196
+ const payload = buildDomDescriptorPayload(input);
10197
+ const key = buildDomDescriptorKey({
10198
+ namespace: this.namespace,
10199
+ method: input.method,
10200
+ description: input.description
10201
+ });
10202
+ const version = buildDomDescriptorVersion(payload);
10036
10203
  const existing = await this.registry.resolve({ key, version });
10037
10204
  if (existing) {
10038
10205
  const parsed2 = parseDomDescriptorRecord(existing);
@@ -10070,12 +10237,22 @@ var MemoryDomDescriptorStore = class {
10070
10237
  latestByKey = /* @__PURE__ */ new Map();
10071
10238
  recordsByKey = /* @__PURE__ */ new Map();
10072
10239
  async read(input) {
10073
- return this.latestByKey.get(descriptionKey(this.namespace, input.method, input.description));
10240
+ return this.latestByKey.get(
10241
+ buildDomDescriptorKey({
10242
+ namespace: this.namespace,
10243
+ method: input.method,
10244
+ description: input.description
10245
+ })
10246
+ );
10074
10247
  }
10075
10248
  async write(input) {
10076
- const payload = buildPayload(input);
10077
- const key = descriptionKey(this.namespace, input.method, input.description);
10078
- const version = sha256Hex2(canonicalJsonString(payload));
10249
+ const payload = buildDomDescriptorPayload(input);
10250
+ const key = buildDomDescriptorKey({
10251
+ namespace: this.namespace,
10252
+ method: input.method,
10253
+ description: input.description
10254
+ });
10255
+ const version = buildDomDescriptorVersion(payload);
10079
10256
  const existing = this.recordsByKey.get(key)?.get(version);
10080
10257
  if (existing) {
10081
10258
  return existing;
@@ -10226,8 +10403,9 @@ var DomActionExecutor = class {
10226
10403
  })
10227
10404
  );
10228
10405
  let finalResolved = resolved;
10406
+ let finalSnapshot;
10229
10407
  if (input.pressEnter) {
10230
- await this.settle(resolved.pageRef, "dom.input", timeout);
10408
+ await this.waitForPressEnterReaction(timeout);
10231
10409
  const enterSession = this.options.createResolutionSession();
10232
10410
  const enterResolved = await timeout.runStep(
10233
10411
  () => this.options.resolveTarget(enterSession, {
@@ -10241,6 +10419,9 @@ var DomActionExecutor = class {
10241
10419
  () => bridge.inspectActionTarget(enterResolved.locator)
10242
10420
  );
10243
10421
  this.assertKeyboardActionable("dom.input", enterResolved, inspectionBeforeEnter);
10422
+ finalSnapshot = await timeout.runStep(
10423
+ () => captureActionBoundarySnapshot(this.options.engine, enterResolved.pageRef)
10424
+ );
10244
10425
  await timeout.runStep(
10245
10426
  () => bridge.pressKey(enterResolved.locator, {
10246
10427
  key: "Enter"
@@ -10248,7 +10429,15 @@ var DomActionExecutor = class {
10248
10429
  );
10249
10430
  finalResolved = enterResolved;
10250
10431
  }
10251
- await this.settle(finalResolved.pageRef, "dom.input", timeout);
10432
+ const settleDiagnostics = await this.settle(
10433
+ finalResolved.pageRef,
10434
+ "dom.input",
10435
+ timeout,
10436
+ finalSnapshot
10437
+ );
10438
+ if (finalSnapshot !== void 0) {
10439
+ recordActionBoundaryDiagnostics(timeout.signal, settleDiagnostics);
10440
+ }
10252
10441
  return finalResolved;
10253
10442
  } catch (error) {
10254
10443
  lastError = error;
@@ -10321,8 +10510,17 @@ var DomActionExecutor = class {
10321
10510
  );
10322
10511
  }
10323
10512
  }
10513
+ const actionBoundarySnapshot = await timeout.runStep(
10514
+ () => captureActionBoundarySnapshot(this.options.engine, pointerTarget.resolved.pageRef)
10515
+ );
10324
10516
  const outcome = await dispatch(pointerTarget, point, timeout);
10325
- await this.settle(pointerTarget.resolved.pageRef, input.operation, timeout);
10517
+ const settleDiagnostics = await this.settle(
10518
+ pointerTarget.resolved.pageRef,
10519
+ input.operation,
10520
+ timeout,
10521
+ actionBoundarySnapshot
10522
+ );
10523
+ recordActionBoundaryDiagnostics(timeout.signal, settleDiagnostics);
10326
10524
  return outcome;
10327
10525
  } catch (error) {
10328
10526
  lastError = error;
@@ -10340,23 +10538,49 @@ var DomActionExecutor = class {
10340
10538
  }
10341
10539
  return runWithPolicyTimeout(this.options.policy.timeout, { operation }, execute);
10342
10540
  }
10343
- async settle(pageRef, operation, timeout) {
10541
+ async settle(pageRef, operation, timeout, snapshot) {
10344
10542
  const bridge = this.requireBridge();
10345
- await timeout.runStep(
10543
+ let visualSettled = true;
10544
+ const boundary = await timeout.runStep(
10346
10545
  () => bridge.finalizeDomAction(pageRef, {
10347
10546
  operation,
10547
+ ...snapshot === void 0 ? {} : { snapshot },
10348
10548
  signal: timeout.signal,
10349
10549
  remainingMs: () => timeout.remainingMs(),
10350
- policySettle: (targetPageRef) => settleWithPolicy(this.options.policy.settle, {
10351
- operation,
10352
- trigger: "dom-action",
10353
- engine: this.options.engine,
10354
- pageRef: targetPageRef,
10355
- signal: timeout.signal,
10356
- remainingMs: timeout.remainingMs()
10357
- })
10550
+ policySettle: async (targetPageRef, trigger) => {
10551
+ try {
10552
+ await settleWithPolicy(this.options.policy.settle, {
10553
+ operation,
10554
+ trigger,
10555
+ engine: this.options.engine,
10556
+ pageRef: targetPageRef,
10557
+ signal: timeout.signal,
10558
+ remainingMs: timeout.remainingMs()
10559
+ });
10560
+ } catch (error) {
10561
+ if (snapshot !== void 0 && isSoftSettleTimeoutError(error, timeout.signal)) {
10562
+ visualSettled = false;
10563
+ return;
10564
+ }
10565
+ throw error;
10566
+ }
10567
+ }
10358
10568
  })
10359
10569
  );
10570
+ return createActionBoundaryDiagnostics({
10571
+ boundary,
10572
+ visualSettled
10573
+ });
10574
+ }
10575
+ async waitForPressEnterReaction(timeout) {
10576
+ const delayMs = this.options.policy.settle.resolveDelayMs({
10577
+ operation: "dom.input",
10578
+ trigger: "dom-action"
10579
+ });
10580
+ if (delayMs <= 0) {
10581
+ return;
10582
+ }
10583
+ await delayWithSignal(delayMs, timeout.signal);
10360
10584
  }
10361
10585
  requireBridge() {
10362
10586
  if (this.bridge !== void 0) {
@@ -10718,7 +10942,7 @@ var DefaultDomRuntime = class {
10718
10942
  bridge;
10719
10943
  constructor(options) {
10720
10944
  this.engine = options.engine;
10721
- this.descriptors = createDomDescriptorStore({
10945
+ this.descriptors = options.descriptorStore ?? createDomDescriptorStore({
10722
10946
  ...options.root === void 0 ? {} : { root: options.root },
10723
10947
  ...options.namespace === void 0 ? {} : { namespace: options.namespace }
10724
10948
  });
@@ -11623,23 +11847,47 @@ var DefaultComputerUseRuntime = class {
11623
11847
  const preActionDisplay = createComputerDisplayTransform(preActionNativeViewport);
11624
11848
  const nativeAction = toNativeComputerAction(input.input.action, preActionDisplay);
11625
11849
  const screenshot = normalizeScreenshotOptions(input.input.screenshot);
11850
+ const snapshot = await input.timeout.runStep(
11851
+ () => captureActionBoundarySnapshot(this.options.engine, input.pageRef)
11852
+ );
11853
+ let visualSettled = true;
11626
11854
  const executed = await input.timeout.runStep(
11627
11855
  () => bridge.execute({
11628
11856
  pageRef: input.pageRef,
11857
+ snapshot,
11629
11858
  action: nativeAction,
11630
11859
  screenshot,
11631
11860
  signal: input.timeout.signal,
11632
11861
  remainingMs: () => input.timeout.remainingMs(),
11633
- policySettle: async (pageRef) => settleWithPolicy(this.options.policy.settle, {
11634
- operation: "computer.execute",
11635
- trigger: "dom-action",
11636
- engine: this.options.engine,
11637
- pageRef,
11638
- signal: input.timeout.signal,
11639
- remainingMs: input.timeout.remainingMs()
11640
- })
11862
+ policySettle: async (pageRef, trigger) => {
11863
+ try {
11864
+ await settleWithPolicy(this.options.policy.settle, {
11865
+ operation: "computer.execute",
11866
+ trigger,
11867
+ engine: this.options.engine,
11868
+ pageRef,
11869
+ signal: input.timeout.signal,
11870
+ remainingMs: input.timeout.remainingMs()
11871
+ });
11872
+ } catch (error) {
11873
+ if (pageRef === input.pageRef && isSoftSettleTimeoutError(error, input.timeout.signal)) {
11874
+ visualSettled = false;
11875
+ return;
11876
+ }
11877
+ throw error;
11878
+ }
11879
+ }
11641
11880
  })
11642
11881
  );
11882
+ if (executed.boundary !== void 0 && executed.pageRef === input.pageRef) {
11883
+ recordActionBoundaryDiagnostics(
11884
+ input.timeout.signal,
11885
+ createActionBoundaryDiagnostics({
11886
+ boundary: executed.boundary,
11887
+ visualSettled
11888
+ })
11889
+ );
11890
+ }
11643
11891
  let trace = void 0;
11644
11892
  if (!input.timeout.signal.aborted) {
11645
11893
  try {
@@ -11752,8 +12000,8 @@ async function dispatchSemanticOperation(runtime, operation, input, options = {}
11752
12000
  input,
11753
12001
  options
11754
12002
  );
11755
- case "network.save":
11756
- return runtime.saveNetwork(
12003
+ case "network.tag":
12004
+ return runtime.tagNetwork(
11757
12005
  input,
11758
12006
  options
11759
12007
  );
@@ -12606,7 +12854,7 @@ function inferRequestPlanFromNetworkRecord(record, input, options = {}) {
12606
12854
  const body = inferRequestPlanBody(record.record.requestBody, requestContentType);
12607
12855
  const payload = normalizeRequestPlanPayload({
12608
12856
  transport: {
12609
- kind: "context-http"
12857
+ kind: input.transport ?? "context-http"
12610
12858
  },
12611
12859
  endpoint: {
12612
12860
  method: record.record.method,
@@ -12626,11 +12874,10 @@ function inferRequestPlanFromNetworkRecord(record, input, options = {}) {
12626
12874
  return {
12627
12875
  key: input.key,
12628
12876
  version: input.version,
12629
- lifecycle: input.lifecycle ?? "draft",
12630
12877
  provenance: {
12631
- source: record.source === "saved" ? "saved-network-record" : "live-network-record",
12878
+ source: record.savedAt === void 0 ? "network-record" : "saved-network-record",
12632
12879
  sourceId: record.recordId,
12633
- ...record.source === "saved" ? record.savedAt === void 0 ? {} : { capturedAt: record.savedAt } : options.observedAt === void 0 ? {} : { capturedAt: options.observedAt }
12880
+ ...record.savedAt === void 0 ? options.observedAt === void 0 ? {} : { capturedAt: options.observedAt } : { capturedAt: record.savedAt }
12634
12881
  },
12635
12882
  payload,
12636
12883
  ...record.tags === void 0 || record.tags.length === 0 ? {} : { tags: record.tags }
@@ -13010,63 +13257,67 @@ function resolveBodyEncoding(charset) {
13010
13257
  return "utf8";
13011
13258
  }
13012
13259
  }
13013
- var NetworkJournal = class {
13260
+ var NetworkHistory = class {
13014
13261
  metadataByRequestId = /* @__PURE__ */ new Map();
13015
13262
  requestIdByRecordId = /* @__PURE__ */ new Map();
13016
- requestIdsByActionId = /* @__PURE__ */ new Map();
13263
+ requestIdsByCapture = /* @__PURE__ */ new Map();
13017
13264
  requestIdsByTag = /* @__PURE__ */ new Map();
13018
- sync(records, options = {}) {
13265
+ tombstonedRequestIds = /* @__PURE__ */ new Set();
13266
+ materialize(records, options = {}) {
13019
13267
  const observedAt = Date.now();
13020
- return records.map((record) => this.materializeLiveRecord(record, observedAt, options));
13021
- }
13022
- materializeLiveRecord(record, observedAt, options = {}) {
13023
- let metadata = this.metadataByRequestId.get(record.requestId);
13024
- if (!metadata) {
13025
- metadata = {
13026
- recordId: `record:${randomUUID()}`,
13027
- observedAt,
13028
- ...record.pageRef === void 0 ? {} : { pageRef: record.pageRef },
13029
- tags: /* @__PURE__ */ new Set()
13030
- };
13031
- this.metadataByRequestId.set(record.requestId, metadata);
13032
- this.requestIdByRecordId.set(metadata.recordId, record.requestId);
13033
- } else if (metadata.pageRef === void 0 && record.pageRef !== void 0) {
13034
- metadata.pageRef = record.pageRef;
13268
+ const materialized = [];
13269
+ for (const record of records) {
13270
+ const entry = this.materializeRecord(record, observedAt, options);
13271
+ if (entry !== void 0) {
13272
+ materialized.push(entry);
13273
+ }
13035
13274
  }
13036
- return {
13037
- recordId: metadata.recordId,
13038
- source: "live",
13039
- ...metadata.actionId === void 0 ? {} : { actionId: metadata.actionId },
13040
- ...metadata.tags.size === 0 ? {} : { tags: [...metadata.tags].sort() },
13041
- record: toProtocolNetworkRecord(record, {
13042
- redactSecretHeaders: options.redactSecretHeaders ?? true
13043
- })
13044
- };
13275
+ return materialized;
13045
13276
  }
13046
- diffNewRequestIds(records, baselineRequestIds) {
13047
- const observedAt = Date.now();
13048
- const all = records.map(
13049
- (record) => this.materializeLiveRecord(record, observedAt, {
13050
- redactSecretHeaders: true
13051
- })
13052
- );
13053
- const delta = all.filter((entry) => !baselineRequestIds.has(entry.record.requestId));
13054
- return {
13055
- all,
13056
- delta
13057
- };
13277
+ async persist(records, store, options) {
13278
+ const observedAt = options.observedAt ?? Date.now();
13279
+ const metadataToSave = /* @__PURE__ */ new Set();
13280
+ const persisted = [];
13281
+ for (const record of records) {
13282
+ const entry = this.materializeRecord(record, observedAt, {
13283
+ ...options.redactSecretHeaders === void 0 ? {} : { redactSecretHeaders: options.redactSecretHeaders }
13284
+ });
13285
+ if (entry === void 0) {
13286
+ continue;
13287
+ }
13288
+ const requestId = entry.record.requestId;
13289
+ const metadata = this.metadataByRequestId.get(requestId);
13290
+ if (metadata === void 0) {
13291
+ continue;
13292
+ }
13293
+ const savedAt = metadata.savedAt ?? observedAt;
13294
+ metadataToSave.add(metadata);
13295
+ persisted.push({
13296
+ ...entry,
13297
+ savedAt
13298
+ });
13299
+ }
13300
+ if (persisted.length > 0) {
13301
+ await store.save(persisted, {
13302
+ bodyWriteMode: options.bodyWriteMode
13303
+ });
13304
+ for (const metadata of metadataToSave) {
13305
+ metadata.savedAt ??= observedAt;
13306
+ }
13307
+ }
13308
+ return persisted;
13058
13309
  }
13059
- assignActionId(records, actionId) {
13310
+ assignCapture(records, capture) {
13060
13311
  for (const record of records) {
13061
13312
  const metadata = this.metadataByRequestId.get(record.record.requestId);
13062
- if (!metadata || metadata.actionId === actionId) {
13313
+ if (!metadata || metadata.capture === capture) {
13063
13314
  continue;
13064
13315
  }
13065
- if (metadata.actionId !== void 0) {
13066
- this.requestIdsByActionId.get(metadata.actionId)?.delete(record.record.requestId);
13316
+ if (metadata.capture !== void 0) {
13317
+ this.requestIdsByCapture.get(metadata.capture)?.delete(record.record.requestId);
13067
13318
  }
13068
- metadata.actionId = actionId;
13069
- this.addIndexedRequestId(this.requestIdsByActionId, actionId, record.record.requestId);
13319
+ metadata.capture = capture;
13320
+ this.addIndexedRequestId(this.requestIdsByCapture, capture, record.record.requestId);
13070
13321
  }
13071
13322
  }
13072
13323
  addTag(records, tag) {
@@ -13086,8 +13337,8 @@ var NetworkJournal = class {
13086
13337
  getRequestId(recordId) {
13087
13338
  return this.requestIdByRecordId.get(recordId);
13088
13339
  }
13089
- getRequestIdsForActionId(actionId) {
13090
- return new Set(this.requestIdsByActionId.get(actionId) ?? []);
13340
+ getRequestIdsForCapture(capture) {
13341
+ return new Set(this.requestIdsByCapture.get(capture) ?? []);
13091
13342
  }
13092
13343
  getRequestIdsForTag(tag) {
13093
13344
  return new Set(this.requestIdsByTag.get(tag) ?? []);
@@ -13095,11 +13346,59 @@ var NetworkJournal = class {
13095
13346
  getPageRefForRequestId(requestId) {
13096
13347
  return this.metadataByRequestId.get(requestId)?.pageRef;
13097
13348
  }
13349
+ getKnownRequestIds() {
13350
+ return new Set(this.metadataByRequestId.keys());
13351
+ }
13352
+ tombstoneRequestIds(requestIds) {
13353
+ for (const requestId of requestIds) {
13354
+ this.tombstonedRequestIds.add(requestId);
13355
+ const metadata = this.metadataByRequestId.get(requestId);
13356
+ if (!metadata) {
13357
+ continue;
13358
+ }
13359
+ this.metadataByRequestId.delete(requestId);
13360
+ this.requestIdByRecordId.delete(metadata.recordId);
13361
+ if (metadata.capture !== void 0) {
13362
+ this.requestIdsByCapture.get(metadata.capture)?.delete(requestId);
13363
+ }
13364
+ for (const tag of metadata.tags) {
13365
+ this.requestIdsByTag.get(tag)?.delete(requestId);
13366
+ }
13367
+ }
13368
+ }
13098
13369
  clear() {
13099
13370
  this.metadataByRequestId.clear();
13100
13371
  this.requestIdByRecordId.clear();
13101
- this.requestIdsByActionId.clear();
13372
+ this.requestIdsByCapture.clear();
13102
13373
  this.requestIdsByTag.clear();
13374
+ this.tombstonedRequestIds.clear();
13375
+ }
13376
+ materializeRecord(record, observedAt, options) {
13377
+ if (this.tombstonedRequestIds.has(record.requestId)) {
13378
+ return void 0;
13379
+ }
13380
+ let metadata = this.metadataByRequestId.get(record.requestId);
13381
+ if (!metadata) {
13382
+ metadata = {
13383
+ recordId: `record:${randomUUID()}`,
13384
+ observedAt,
13385
+ ...record.pageRef === void 0 ? {} : { pageRef: record.pageRef },
13386
+ tags: /* @__PURE__ */ new Set()
13387
+ };
13388
+ this.metadataByRequestId.set(record.requestId, metadata);
13389
+ this.requestIdByRecordId.set(metadata.recordId, record.requestId);
13390
+ } else if (metadata.pageRef === void 0 && record.pageRef !== void 0) {
13391
+ metadata.pageRef = record.pageRef;
13392
+ }
13393
+ return {
13394
+ recordId: metadata.recordId,
13395
+ ...metadata.capture === void 0 ? {} : { capture: metadata.capture },
13396
+ ...metadata.tags.size === 0 ? {} : { tags: [...metadata.tags].sort() },
13397
+ ...metadata.savedAt === void 0 ? {} : { savedAt: metadata.savedAt },
13398
+ record: toProtocolNetworkRecord(record, {
13399
+ redactSecretHeaders: options.redactSecretHeaders ?? true
13400
+ })
13401
+ };
13103
13402
  }
13104
13403
  addIndexedRequestId(index, key, requestId) {
13105
13404
  const requestIds = index.get(key) ?? /* @__PURE__ */ new Set();
@@ -16898,7 +17197,6 @@ async function compileOpensteerExtractionFieldTargets(options) {
16898
17197
  await collectFieldTargetsFromSchemaObject({
16899
17198
  dom: options.dom,
16900
17199
  pageRef: options.pageRef,
16901
- latestSnapshotCounters: options.latestSnapshotCounters,
16902
17200
  value: options.schema,
16903
17201
  path: "",
16904
17202
  fields,
@@ -16916,22 +17214,11 @@ async function extractOpensteerExtractionFieldTargets(options) {
16916
17214
  source: "current_url"
16917
17215
  };
16918
17216
  }
16919
- if ("path" in field) {
16920
- return {
16921
- key: field.key,
16922
- target: {
16923
- kind: "path",
16924
- path: field.path
16925
- },
16926
- ...field.attribute === void 0 ? {} : { attribute: field.attribute }
16927
- };
16928
- }
16929
17217
  return {
16930
17218
  key: field.key,
16931
17219
  target: {
16932
- kind: "live",
16933
- locator: field.locator,
16934
- anchor: field.anchor
17220
+ kind: "path",
17221
+ path: field.path
16935
17222
  },
16936
17223
  ...field.attribute === void 0 ? {} : { attribute: field.attribute }
16937
17224
  };
@@ -16940,8 +17227,6 @@ async function extractOpensteerExtractionFieldTargets(options) {
16940
17227
  }
16941
17228
  async function compilePersistedOpensteerExtractionPayloadFromFieldTargets(options) {
16942
17229
  const fields = await resolvePersistableFieldTargets({
16943
- pageRef: options.pageRef,
16944
- dom: options.dom,
16945
17230
  fieldTargets: options.fieldTargets
16946
17231
  });
16947
17232
  const payload = buildPersistedOpensteerExtractionPayload(fields);
@@ -16955,7 +17240,7 @@ async function replayOpensteerExtractionPayload(options) {
16955
17240
  return extractPersistedObjectNode(options.pageRef, options.dom, options.payload);
16956
17241
  }
16957
17242
  function createOpensteerExtractionDescriptorStore(options) {
16958
- const namespace = normalizeNamespace2(options.namespace);
17243
+ const namespace = normalizeNamespace(options.namespace);
16959
17244
  if (options.root) {
16960
17245
  return new FilesystemOpensteerExtractionDescriptorStore(
16961
17246
  options.root.registry.descriptors,
@@ -16973,7 +17258,6 @@ async function collectFieldTargetsFromSchemaObject(options) {
16973
17258
  await collectFieldTargetsFromSchemaValue({
16974
17259
  dom: options.dom,
16975
17260
  pageRef: options.pageRef,
16976
- latestSnapshotCounters: options.latestSnapshotCounters,
16977
17261
  value: childValue,
16978
17262
  path: joinDataPath(options.path, normalizedKey),
16979
17263
  fields: options.fields,
@@ -16988,7 +17272,6 @@ async function collectFieldTargetsFromSchemaValue(options) {
16988
17272
  await compileFieldTarget({
16989
17273
  dom: options.dom,
16990
17274
  pageRef: options.pageRef,
16991
- latestSnapshotCounters: options.latestSnapshotCounters,
16992
17275
  field: normalizedField,
16993
17276
  path: options.path
16994
17277
  })
@@ -17017,7 +17300,6 @@ async function collectFieldTargetsFromSchemaValue(options) {
17017
17300
  await collectFieldTargetsFromSchemaObject({
17018
17301
  dom: options.dom,
17019
17302
  pageRef: options.pageRef,
17020
- latestSnapshotCounters: options.latestSnapshotCounters,
17021
17303
  value: itemValue,
17022
17304
  path: appendDataPathIndex(options.path, index),
17023
17305
  fields: options.fields,
@@ -17040,7 +17322,6 @@ async function collectFieldTargetsFromSchemaValue(options) {
17040
17322
  await collectFieldTargetsFromSchemaObject({
17041
17323
  dom: options.dom,
17042
17324
  pageRef: options.pageRef,
17043
- latestSnapshotCounters: options.latestSnapshotCounters,
17044
17325
  value: options.value,
17045
17326
  path: options.path,
17046
17327
  fields: options.fields,
@@ -17067,10 +17348,10 @@ async function compileFieldTarget(options) {
17067
17348
  }
17068
17349
  return {
17069
17350
  key: options.path,
17070
- ...await resolveLiveFieldTarget({
17071
- latestSnapshotCounters: options.latestSnapshotCounters,
17072
- field: options.field,
17073
- path: options.path
17351
+ path: await resolveSelectorFieldPath({
17352
+ dom: options.dom,
17353
+ pageRef: options.pageRef,
17354
+ selector: `[c="${String(options.field.element)}"]`
17074
17355
  }),
17075
17356
  ...options.field.attribute === void 0 ? {} : { attribute: options.field.attribute }
17076
17357
  };
@@ -17088,24 +17369,6 @@ async function resolveSelectorFieldPath(options) {
17088
17369
  locator: resolved.locator
17089
17370
  });
17090
17371
  }
17091
- async function resolveLiveFieldTarget(options) {
17092
- const counters = options.latestSnapshotCounters;
17093
- if (counters === void 0) {
17094
- throw new Error(
17095
- `Extraction schema field "${labelForPath(options.path)}" uses element ${String(options.field.element)} but no snapshot is available.`
17096
- );
17097
- }
17098
- const counter = counters.get(options.field.element);
17099
- if (!counter) {
17100
- throw new Error(
17101
- `Extraction schema field "${labelForPath(options.path)}" references missing counter ${String(options.field.element)}.`
17102
- );
17103
- }
17104
- return {
17105
- locator: counter.locator,
17106
- anchor: counter.anchor
17107
- };
17108
- }
17109
17372
  async function resolvePersistableFieldTargets(options) {
17110
17373
  const fields = [];
17111
17374
  for (const field of options.fieldTargets) {
@@ -17116,29 +17379,9 @@ async function resolvePersistableFieldTargets(options) {
17116
17379
  });
17117
17380
  continue;
17118
17381
  }
17119
- if ("path" in field) {
17120
- fields.push({
17121
- key: field.key,
17122
- path: sanitizeElementPath(field.path),
17123
- ...field.attribute === void 0 ? {} : { attribute: field.attribute }
17124
- });
17125
- continue;
17126
- }
17127
- const resolved = await options.dom.resolveTarget({
17128
- pageRef: options.pageRef,
17129
- method: "extract",
17130
- target: {
17131
- kind: "live",
17132
- locator: field.locator,
17133
- anchor: field.anchor
17134
- }
17135
- });
17136
- const path9 = resolved.replayPath ?? await options.dom.buildPath({
17137
- locator: resolved.locator
17138
- });
17139
17382
  fields.push({
17140
17383
  key: field.key,
17141
- path: sanitizeElementPath(path9),
17384
+ path: sanitizeElementPath(field.path),
17142
17385
  ...field.attribute === void 0 ? {} : { attribute: field.attribute }
17143
17386
  });
17144
17387
  }
@@ -17452,11 +17695,11 @@ function normalizeSchemaField(value) {
17452
17695
  ...attribute === void 0 ? {} : { attribute }
17453
17696
  };
17454
17697
  }
17455
- function normalizeNamespace2(namespace) {
17698
+ function normalizeNamespace(namespace) {
17456
17699
  const normalized = String(namespace ?? "default").trim();
17457
17700
  return normalized.length === 0 ? "default" : normalized;
17458
17701
  }
17459
- function descriptionKey2(namespace, description) {
17702
+ function descriptionKey(namespace, description) {
17460
17703
  return `extract:${namespace}:${sha256Hex3(description.trim())}`;
17461
17704
  }
17462
17705
  function parseExtractionDescriptorRecord(record) {
@@ -17559,7 +17802,7 @@ var FilesystemOpensteerExtractionDescriptorStore = class {
17559
17802
  }
17560
17803
  async read(input) {
17561
17804
  const record = await this.registry.resolve({
17562
- key: descriptionKey2(this.namespace, input.description)
17805
+ key: descriptionKey(this.namespace, input.description)
17563
17806
  });
17564
17807
  return record === void 0 ? void 0 : parseExtractionDescriptorRecord(record);
17565
17808
  }
@@ -17571,7 +17814,7 @@ var FilesystemOpensteerExtractionDescriptorStore = class {
17571
17814
  ...input.schemaHash === void 0 ? {} : { schemaHash: input.schemaHash },
17572
17815
  ...input.sourceUrl === void 0 ? {} : { sourceUrl: input.sourceUrl }
17573
17816
  };
17574
- const key = descriptionKey2(this.namespace, input.description);
17817
+ const key = descriptionKey(this.namespace, input.description);
17575
17818
  const version = sha256Hex3(canonicalJsonString(payload));
17576
17819
  const existing = await this.registry.resolve({ key, version });
17577
17820
  if (existing) {
@@ -17610,7 +17853,7 @@ var MemoryOpensteerExtractionDescriptorStore = class {
17610
17853
  latestByKey = /* @__PURE__ */ new Map();
17611
17854
  recordsByKey = /* @__PURE__ */ new Map();
17612
17855
  async read(input) {
17613
- return this.latestByKey.get(descriptionKey2(this.namespace, input.description));
17856
+ return this.latestByKey.get(descriptionKey(this.namespace, input.description));
17614
17857
  }
17615
17858
  async write(input) {
17616
17859
  const payload = {
@@ -17620,7 +17863,7 @@ var MemoryOpensteerExtractionDescriptorStore = class {
17620
17863
  ...input.schemaHash === void 0 ? {} : { schemaHash: input.schemaHash },
17621
17864
  ...input.sourceUrl === void 0 ? {} : { sourceUrl: input.sourceUrl }
17622
17865
  };
17623
- const key = descriptionKey2(this.namespace, input.description);
17866
+ const key = descriptionKey(this.namespace, input.description);
17624
17867
  const version = sha256Hex3(canonicalJsonString(payload));
17625
17868
  const existing = this.recordsByKey.get(key)?.get(version);
17626
17869
  if (existing) {
@@ -17664,6 +17907,7 @@ var OPENSTEER_INTERACTIVE_ATTR = "data-opensteer-interactive";
17664
17907
  var OPENSTEER_HIDDEN_ATTR = "data-opensteer-hidden";
17665
17908
  var OPENSTEER_SCROLLABLE_ATTR = "data-opensteer-scrollable";
17666
17909
  var OPENSTEER_NODE_ID_ATTR = "data-os-node-id";
17910
+ var OPENSTEER_SPARSE_COUNTER_ATTR = "data-os-c";
17667
17911
  var OPENSTEER_BOUNDARY_ATTR = "data-os-boundary";
17668
17912
  var OPENSTEER_UNAVAILABLE_ATTR = "data-os-unavailable";
17669
17913
  var OPENSTEER_IFRAME_BOUNDARY_TAG = "os-iframe-root";
@@ -18168,7 +18412,82 @@ var VOID_TAGS2 = /* @__PURE__ */ new Set([
18168
18412
  ]);
18169
18413
 
18170
18414
  // src/sdk/snapshot/compiler.ts
18415
+ async function assignSparseCountersToLiveDom(engine, pageRef) {
18416
+ try {
18417
+ await engine.evaluatePage({
18418
+ pageRef,
18419
+ script: `(() => {
18420
+ let counter = 1;
18421
+ const walk = (root) => {
18422
+ for (const child of root.children) {
18423
+ child.setAttribute('data-os-c', String(counter++));
18424
+ walk(child);
18425
+ if (child.shadowRoot) walk(child.shadowRoot);
18426
+ }
18427
+ };
18428
+ walk(document);
18429
+ })()`
18430
+ });
18431
+ return true;
18432
+ } catch {
18433
+ return false;
18434
+ }
18435
+ }
18436
+ async function syncDenseCountersToLiveDom(engine, pageRef, sparseToDirectMapping) {
18437
+ const mappingObj = Object.fromEntries(sparseToDirectMapping);
18438
+ await engine.evaluatePage({
18439
+ pageRef,
18440
+ script: `((mapping) => {
18441
+ const walk = (root) => {
18442
+ for (const child of root.children) {
18443
+ child.removeAttribute('c');
18444
+ const sparse = child.getAttribute('data-os-c');
18445
+ if (sparse !== null) {
18446
+ const dense = mapping[sparse];
18447
+ if (dense !== undefined) {
18448
+ child.setAttribute('c', String(dense));
18449
+ }
18450
+ child.removeAttribute('data-os-c');
18451
+ }
18452
+ walk(child);
18453
+ if (child.shadowRoot) walk(child.shadowRoot);
18454
+ }
18455
+ };
18456
+ walk(document);
18457
+ })`,
18458
+ args: [mappingObj]
18459
+ });
18460
+ }
18461
+ function renumberCountersDensely(cleanedHtml, counterRecords) {
18462
+ const $ = cheerio.load(cleanedHtml, { xmlMode: false });
18463
+ const newRecords = /* @__PURE__ */ new Map();
18464
+ const sparseToDirectMapping = /* @__PURE__ */ new Map();
18465
+ let nextDense = 1;
18466
+ $("[c]").each(function renumberElement() {
18467
+ const el = $(this);
18468
+ const oldC = Number.parseInt(String(el.attr("c") || ""), 10);
18469
+ if (!Number.isFinite(oldC)) {
18470
+ return;
18471
+ }
18472
+ const record = counterRecords.get(oldC);
18473
+ if (!record) {
18474
+ return;
18475
+ }
18476
+ const denseC = nextDense++;
18477
+ el.attr("c", String(denseC));
18478
+ newRecords.set(denseC, { ...record, element: denseC });
18479
+ if (record.sparseCounter !== void 0) {
18480
+ sparseToDirectMapping.set(record.sparseCounter, denseC);
18481
+ }
18482
+ });
18483
+ return {
18484
+ html: $.html(),
18485
+ counterRecords: newRecords,
18486
+ sparseToDirectMapping
18487
+ };
18488
+ }
18171
18489
  async function compileOpensteerSnapshot(options) {
18490
+ const liveCountersEnabled = await assignSparseCountersToLiveDom(options.engine, options.pageRef);
18172
18491
  const pageInfo = await options.engine.getPageInfo({ pageRef: options.pageRef });
18173
18492
  const mainSnapshot = await getMainDocumentSnapshot(options.engine, options.pageRef);
18174
18493
  const snapshotsByDocumentRef = await collectDocumentSnapshots(options.engine, mainSnapshot);
@@ -18187,13 +18506,23 @@ async function compileOpensteerSnapshot(options) {
18187
18506
  const compiledHtml = assignCounters(rawHtml, renderedNodes);
18188
18507
  const cleanedHtml = options.mode === "extraction" ? cleanForExtraction(compiledHtml.html) : cleanForAction(compiledHtml.html);
18189
18508
  const filtered = retainVisibleCounterRecords(cleanedHtml, compiledHtml.counterRecords);
18509
+ const dense = renumberCountersDensely(cleanedHtml, filtered);
18510
+ if (liveCountersEnabled && dense.sparseToDirectMapping.size > 0) {
18511
+ try {
18512
+ await syncDenseCountersToLiveDom(
18513
+ options.engine,
18514
+ options.pageRef,
18515
+ dense.sparseToDirectMapping
18516
+ );
18517
+ } catch {
18518
+ }
18519
+ }
18190
18520
  return {
18191
18521
  url: pageInfo.url,
18192
18522
  title: pageInfo.title,
18193
18523
  mode: options.mode,
18194
- html: cleanedHtml,
18195
- counters: [...filtered.values()].map(toPublicCounterRecord),
18196
- counterRecords: filtered
18524
+ html: dense.html,
18525
+ counters: [...dense.counterRecords.values()].map(toPublicCounterRecord)
18197
18526
  };
18198
18527
  }
18199
18528
  async function getMainDocumentSnapshot(engine, pageRef) {
@@ -18422,6 +18751,9 @@ function assignCounters(rawHtml, renderedNodes) {
18422
18751
  if (!rendered) {
18423
18752
  return;
18424
18753
  }
18754
+ const rawSparseCounter = el.attr(OPENSTEER_SPARSE_COUNTER_ATTR);
18755
+ el.removeAttr(OPENSTEER_SPARSE_COUNTER_ATTR);
18756
+ const sparseCounter = rawSparseCounter ? Number.parseInt(rawSparseCounter, 10) : void 0;
18425
18757
  const counter = nextCounter++;
18426
18758
  el.attr("c", String(counter));
18427
18759
  counterRecords.set(counter, {
@@ -18439,7 +18771,8 @@ function assignCounters(rawHtml, renderedNodes) {
18439
18771
  shadowDepth: rendered.shadowDepth,
18440
18772
  interactive: rendered.interactive,
18441
18773
  locator: rendered.locator,
18442
- anchor: rendered.anchor
18774
+ anchor: rendered.anchor,
18775
+ ...sparseCounter !== void 0 && Number.isFinite(sparseCounter) ? { sparseCounter } : {}
18443
18776
  });
18444
18777
  });
18445
18778
  return {
@@ -19800,6 +20133,7 @@ function diffInteractionTraces(left, right) {
19800
20133
 
19801
20134
  // src/sdk/runtime.ts
19802
20135
  var requireForAuthRecipeHook = createRequire(import.meta.url);
20136
+ var MUTATION_CAPTURE_FINALIZE_TIMEOUT_MS = 5e3;
19803
20137
  var OpensteerSessionRuntime = class {
19804
20138
  workspace;
19805
20139
  rootPath;
@@ -19807,30 +20141,34 @@ var OpensteerSessionRuntime = class {
19807
20141
  injectedEngine;
19808
20142
  engineFactory;
19809
20143
  policy;
20144
+ injectedDescriptorStore;
20145
+ injectedExtractionDescriptorStore;
20146
+ registryOverrides;
19810
20147
  cleanupRootOnClose;
19811
20148
  sessionInfoBase;
19812
20149
  root;
19813
20150
  engine;
19814
20151
  dom;
19815
20152
  computer;
19816
- networkJournal = new NetworkJournal();
20153
+ networkHistory = new NetworkHistory();
19817
20154
  extractionDescriptors;
19818
20155
  sessionRef;
19819
20156
  pageRef;
19820
20157
  runId;
19821
- latestSnapshot;
19822
- backgroundNetworkPersistence = /* @__PURE__ */ new Set();
19823
20158
  cookieJars = /* @__PURE__ */ new Map();
19824
20159
  recipeCache = /* @__PURE__ */ new Map();
19825
20160
  ownsEngine = false;
19826
20161
  constructor(options) {
19827
- this.workspace = normalizeNamespace3(options.name);
20162
+ this.workspace = normalizeNamespace2(options.name);
19828
20163
  this.workspaceName = options.workspaceName?.trim() === void 0 || options.workspaceName?.trim().length === 0 ? void 0 : options.workspaceName.trim();
19829
20164
  this.root = options.workspace;
19830
20165
  this.rootPath = options.workspace?.rootPath ?? options.rootPath ?? path6.resolve(process.cwd(), ".opensteer", "temporary", randomUUID());
19831
20166
  this.injectedEngine = options.engine;
19832
20167
  this.engineFactory = options.engineFactory;
19833
20168
  this.policy = options.policy ?? defaultPolicy();
20169
+ this.injectedDescriptorStore = options.descriptorStore;
20170
+ this.injectedExtractionDescriptorStore = options.extractionDescriptorStore;
20171
+ this.registryOverrides = options.registryOverrides;
19834
20172
  this.cleanupRootOnClose = options.cleanupRootOnClose ?? options.workspace === void 0;
19835
20173
  this.sessionInfoBase = options.sessionInfo ?? {};
19836
20174
  if (this.injectedEngine === void 0 && this.engineFactory === void 0) {
@@ -19841,7 +20179,7 @@ var OpensteerSessionRuntime = class {
19841
20179
  const base = this.sessionInfoBase;
19842
20180
  return {
19843
20181
  provider: base.provider ?? {
19844
- kind: "local",
20182
+ mode: "local",
19845
20183
  ownership: "owned",
19846
20184
  engine: "playwright"
19847
20185
  },
@@ -19866,7 +20204,7 @@ var OpensteerSessionRuntime = class {
19866
20204
  }
19867
20205
  async open(input = {}, options = {}) {
19868
20206
  assertValidSemanticOperationInput("session.open", input);
19869
- if (input.workspace !== void 0 && normalizeNamespace3(input.workspace) !== this.workspace) {
20207
+ if (input.workspace !== void 0 && normalizeNamespace2(input.workspace) !== this.workspace) {
19870
20208
  throw new Error(
19871
20209
  `session.open requested workspace "${input.workspace}" but runtime is bound to "${this.workspace}"`
19872
20210
  );
@@ -19908,7 +20246,6 @@ var OpensteerSessionRuntime = class {
19908
20246
  timeout.throwIfAborted();
19909
20247
  this.sessionRef = sessionRef;
19910
20248
  this.pageRef = createdPage.data.pageRef;
19911
- this.latestSnapshot = void 0;
19912
20249
  await timeout.runStep(() => this.ensureSemantics());
19913
20250
  let frameRef2 = createdPage.frameRef;
19914
20251
  if (input.url !== void 0) {
@@ -20031,7 +20368,6 @@ var OpensteerSessionRuntime = class {
20031
20368
  })
20032
20369
  );
20033
20370
  this.pageRef = created.data.pageRef;
20034
- this.latestSnapshot = void 0;
20035
20371
  return this.readSessionState();
20036
20372
  },
20037
20373
  options
@@ -20074,7 +20410,6 @@ var OpensteerSessionRuntime = class {
20074
20410
  () => this.requireEngine().activatePage({ pageRef: input.pageRef })
20075
20411
  );
20076
20412
  this.pageRef = input.pageRef;
20077
- this.latestSnapshot = void 0;
20078
20413
  return this.readSessionState();
20079
20414
  },
20080
20415
  options
@@ -20138,7 +20473,6 @@ var OpensteerSessionRuntime = class {
20138
20473
  );
20139
20474
  }
20140
20475
  this.pageRef = activePageRef;
20141
- this.latestSnapshot = void 0;
20142
20476
  return {
20143
20477
  closedPageRef: targetPageRef,
20144
20478
  ...activePageRef === void 0 ? {} : { activePageRef },
@@ -20182,35 +20516,32 @@ var OpensteerSessionRuntime = class {
20182
20516
  assertValidSemanticOperationInput("page.goto", input);
20183
20517
  const pageRef = await this.ensurePageRef();
20184
20518
  const startedAt = Date.now();
20519
+ let mutationCaptureDiagnostics;
20185
20520
  try {
20186
- const { navigation, state } = await this.runWithOperationTimeout(
20521
+ const { navigation, state } = await this.runMutationCapturedOperation(
20187
20522
  "page.goto",
20188
- async (timeout) => {
20189
- const baselineRequestIds = await this.beginMutationCapture(timeout);
20190
- try {
20191
- const navigation2 = await this.navigatePage(
20192
- {
20193
- operation: "page.goto",
20194
- pageRef,
20195
- url: input.url
20196
- },
20197
- timeout
20198
- );
20199
- timeout.throwIfAborted();
20200
- this.latestSnapshot = void 0;
20201
- await this.completeMutationCapture(timeout, baselineRequestIds, input.networkTag);
20202
- return {
20203
- navigation: navigation2,
20204
- state: await timeout.runStep(() => this.readSessionState())
20205
- };
20206
- } catch (error) {
20207
- await this.completeMutationCapture(timeout, baselineRequestIds, input.networkTag).catch(
20208
- () => void 0
20209
- );
20210
- throw error;
20211
- }
20523
+ {
20524
+ ...input.captureNetwork === void 0 ? {} : { captureNetwork: input.captureNetwork },
20525
+ options
20526
+ },
20527
+ async (timeout) => {
20528
+ const navigation2 = await this.navigatePage(
20529
+ {
20530
+ operation: "page.goto",
20531
+ pageRef,
20532
+ url: input.url
20533
+ },
20534
+ timeout
20535
+ );
20536
+ timeout.throwIfAborted();
20537
+ return {
20538
+ navigation: navigation2,
20539
+ state: await timeout.runStep(() => this.readSessionState())
20540
+ };
20212
20541
  },
20213
- options
20542
+ (diagnostics) => {
20543
+ mutationCaptureDiagnostics = diagnostics;
20544
+ }
20214
20545
  );
20215
20546
  await this.appendTrace({
20216
20547
  operation: "page.goto",
@@ -20219,7 +20550,8 @@ var OpensteerSessionRuntime = class {
20219
20550
  outcome: "ok",
20220
20551
  data: {
20221
20552
  url: input.url,
20222
- state
20553
+ state,
20554
+ ...buildMutationCaptureTraceData(mutationCaptureDiagnostics)
20223
20555
  },
20224
20556
  context: buildRuntimeTraceContext({
20225
20557
  sessionRef: this.sessionRef,
@@ -20235,6 +20567,7 @@ var OpensteerSessionRuntime = class {
20235
20567
  completedAt: Date.now(),
20236
20568
  outcome: "error",
20237
20569
  error,
20570
+ data: buildMutationCaptureTraceData(mutationCaptureDiagnostics),
20238
20571
  context: buildRuntimeTraceContext({
20239
20572
  sessionRef: this.sessionRef,
20240
20573
  pageRef
@@ -20247,9 +20580,11 @@ var OpensteerSessionRuntime = class {
20247
20580
  assertValidSemanticOperationInput("page.evaluate", input);
20248
20581
  const pageRef = input.pageRef ?? await this.ensurePageRef();
20249
20582
  const startedAt = Date.now();
20583
+ let mutationCaptureDiagnostics;
20250
20584
  try {
20251
- const output = await this.runWithOperationTimeout(
20585
+ const output = await this.runMutationCapturedOperation(
20252
20586
  "page.evaluate",
20587
+ { options },
20253
20588
  async (timeout) => {
20254
20589
  const remainingMs = timeout.remainingMs();
20255
20590
  const evaluated = await timeout.runStep(
@@ -20265,7 +20600,9 @@ var OpensteerSessionRuntime = class {
20265
20600
  value: toJsonValueOrNull(evaluated.data)
20266
20601
  };
20267
20602
  },
20268
- options
20603
+ (diagnostics) => {
20604
+ mutationCaptureDiagnostics = diagnostics;
20605
+ }
20269
20606
  );
20270
20607
  await this.appendTrace({
20271
20608
  operation: "page.evaluate",
@@ -20274,7 +20611,8 @@ var OpensteerSessionRuntime = class {
20274
20611
  outcome: "ok",
20275
20612
  data: {
20276
20613
  pageRef: output.pageRef,
20277
- value: output.value
20614
+ value: output.value,
20615
+ ...buildMutationCaptureTraceData(mutationCaptureDiagnostics)
20278
20616
  },
20279
20617
  context: buildRuntimeTraceContext({
20280
20618
  sessionRef: this.sessionRef,
@@ -20289,6 +20627,7 @@ var OpensteerSessionRuntime = class {
20289
20627
  completedAt: Date.now(),
20290
20628
  outcome: "error",
20291
20629
  error,
20630
+ data: buildMutationCaptureTraceData(mutationCaptureDiagnostics),
20292
20631
  context: buildRuntimeTraceContext({
20293
20632
  sessionRef: this.sessionRef,
20294
20633
  pageRef
@@ -20367,7 +20706,6 @@ var OpensteerSessionRuntime = class {
20367
20706
  })
20368
20707
  );
20369
20708
  timeout.throwIfAborted();
20370
- this.latestSnapshot = compiled;
20371
20709
  const artifacts2 = await this.captureSnapshotArtifacts(
20372
20710
  pageRef,
20373
20711
  {
@@ -20517,8 +20855,7 @@ var OpensteerSessionRuntime = class {
20517
20855
  () => compileOpensteerExtractionFieldTargets({
20518
20856
  pageRef,
20519
20857
  schema: input.schema,
20520
- dom: this.requireDom(),
20521
- ...this.latestSnapshot?.counterRecords === void 0 ? {} : { latestSnapshotCounters: this.latestSnapshot.counterRecords }
20858
+ dom: this.requireDom()
20522
20859
  })
20523
20860
  );
20524
20861
  data = toCanonicalJsonValue(
@@ -20629,38 +20966,19 @@ var OpensteerSessionRuntime = class {
20629
20966
  }
20630
20967
  async queryNetwork(input = {}, options = {}) {
20631
20968
  assertValidSemanticOperationInput("network.query", input);
20632
- if (input.source !== "saved") {
20633
- await this.ensurePageRef();
20634
- }
20635
20969
  const root = await this.ensureRoot();
20636
20970
  const startedAt = Date.now();
20637
20971
  try {
20638
20972
  const output = await this.runWithOperationTimeout(
20639
20973
  "network.query",
20640
20974
  async (timeout) => {
20641
- if (input.source === "saved") {
20642
- await timeout.runStep(() => this.flushBackgroundNetworkPersistence());
20643
- return {
20644
- records: await timeout.runStep(
20645
- () => root.registry.savedNetwork.query({
20646
- ...input.recordId === void 0 ? {} : { recordId: input.recordId },
20647
- ...input.requestId === void 0 ? {} : { requestId: input.requestId },
20648
- ...input.actionId === void 0 ? {} : { actionId: input.actionId },
20649
- ...input.tag === void 0 ? {} : { tag: input.tag },
20650
- ...input.url === void 0 ? {} : { url: input.url },
20651
- ...input.hostname === void 0 ? {} : { hostname: input.hostname },
20652
- ...input.path === void 0 ? {} : { path: input.path },
20653
- ...input.method === void 0 ? {} : { method: input.method },
20654
- ...input.status === void 0 ? {} : { status: input.status },
20655
- ...input.resourceType === void 0 ? {} : { resourceType: input.resourceType },
20656
- ...input.includeBodies === void 0 ? {} : { includeBodies: input.includeBodies },
20657
- ...input.limit === void 0 ? {} : { limit: input.limit }
20658
- })
20659
- )
20660
- };
20661
- }
20975
+ await this.syncPersistedNetworkSelection(timeout, input, {
20976
+ includeBodies: input.includeBodies ?? false
20977
+ });
20662
20978
  return {
20663
- records: await this.queryLiveNetwork(input, timeout)
20979
+ records: await timeout.runStep(
20980
+ () => root.registry.savedNetwork.query(this.toSavedNetworkQueryInput(input))
20981
+ )
20664
20982
  };
20665
20983
  },
20666
20984
  options
@@ -20671,7 +20989,6 @@ var OpensteerSessionRuntime = class {
20671
20989
  completedAt: Date.now(),
20672
20990
  outcome: "ok",
20673
20991
  data: {
20674
- source: input.source ?? "live",
20675
20992
  includeBodies: input.includeBodies ?? false,
20676
20993
  limit: input.limit ?? 50,
20677
20994
  count: output.records.length
@@ -20697,56 +21014,42 @@ var OpensteerSessionRuntime = class {
20697
21014
  throw error;
20698
21015
  }
20699
21016
  }
20700
- async saveNetwork(input, options = {}) {
20701
- assertValidSemanticOperationInput("network.save", input);
20702
- await this.ensurePageRef();
21017
+ async tagNetwork(input, options = {}) {
21018
+ assertValidSemanticOperationInput("network.tag", input);
20703
21019
  const root = await this.ensureRoot();
21020
+ const filter = this.toQueryInputFromTagInput(input);
21021
+ const savedFilter = this.toSavedNetworkQueryInput(filter);
20704
21022
  const startedAt = Date.now();
20705
21023
  try {
20706
21024
  const output = await this.runWithOperationTimeout(
20707
- "network.save",
21025
+ "network.tag",
20708
21026
  async (timeout) => {
20709
- const records = await this.queryLiveNetwork(
20710
- {
20711
- includeBodies: true,
20712
- source: "live",
20713
- ...input.pageRef === void 0 ? {} : { pageRef: input.pageRef },
20714
- ...input.recordId === void 0 ? {} : { recordId: input.recordId },
20715
- ...input.requestId === void 0 ? {} : { requestId: input.requestId },
20716
- ...input.actionId === void 0 ? {} : { actionId: input.actionId },
20717
- ...input.url === void 0 ? {} : { url: input.url },
20718
- ...input.hostname === void 0 ? {} : { hostname: input.hostname },
20719
- ...input.path === void 0 ? {} : { path: input.path },
20720
- ...input.method === void 0 ? {} : { method: input.method },
20721
- ...input.status === void 0 ? {} : { status: input.status },
20722
- ...input.resourceType === void 0 ? {} : { resourceType: input.resourceType }
20723
- },
20724
- timeout,
20725
- { ignoreLimit: true, redactSecretHeaders: false }
20726
- );
20727
- this.networkJournal.addTag(records, input.tag);
21027
+ const records = await this.syncPersistedNetworkSelection(timeout, filter, {
21028
+ includeBodies: false
21029
+ });
21030
+ this.networkHistory.addTag(records, input.tag);
20728
21031
  return {
20729
- savedCount: await timeout.runStep(
20730
- () => root.registry.savedNetwork.save(records, input.tag)
21032
+ taggedCount: await timeout.runStep(
21033
+ () => root.registry.savedNetwork.tagByFilter(savedFilter, input.tag)
20731
21034
  )
20732
21035
  };
20733
21036
  },
20734
21037
  options
20735
21038
  );
20736
21039
  await this.appendTrace({
20737
- operation: "network.save",
21040
+ operation: "network.tag",
20738
21041
  startedAt,
20739
21042
  completedAt: Date.now(),
20740
21043
  outcome: "ok",
20741
21044
  data: {
20742
21045
  tag: input.tag,
20743
- savedCount: output.savedCount
21046
+ taggedCount: output.taggedCount
20744
21047
  }
20745
21048
  });
20746
21049
  return output;
20747
21050
  } catch (error) {
20748
21051
  await this.appendTrace({
20749
- operation: "network.save",
21052
+ operation: "network.tag",
20750
21053
  startedAt,
20751
21054
  completedAt: Date.now(),
20752
21055
  outcome: "error",
@@ -20763,7 +21066,31 @@ var OpensteerSessionRuntime = class {
20763
21066
  const output = await this.runWithOperationTimeout(
20764
21067
  "network.clear",
20765
21068
  async (timeout) => {
20766
- await timeout.runStep(() => this.flushBackgroundNetworkPersistence());
21069
+ if (this.sessionRef !== void 0) {
21070
+ if (input.capture !== void 0 || input.tag !== void 0) {
21071
+ const records = await this.queryLiveNetwork(
21072
+ {
21073
+ ...input.capture === void 0 ? {} : { capture: input.capture },
21074
+ ...input.tag === void 0 ? {} : { tag: input.tag }
21075
+ },
21076
+ timeout,
21077
+ {
21078
+ ignoreLimit: true
21079
+ }
21080
+ );
21081
+ this.networkHistory.tombstoneRequestIds(
21082
+ records.map((record) => record.record.requestId)
21083
+ );
21084
+ } else {
21085
+ const liveRequestIds = await this.readLiveRequestIds(timeout, {
21086
+ includeCurrentPageOnly: false
21087
+ });
21088
+ this.networkHistory.tombstoneRequestIds(liveRequestIds);
21089
+ }
21090
+ }
21091
+ if (input.capture === void 0 && input.tag === void 0) {
21092
+ this.networkHistory.tombstoneRequestIds(this.networkHistory.getKnownRequestIds());
21093
+ }
20767
21094
  return {
20768
21095
  clearedCount: await timeout.runStep(() => root.registry.savedNetwork.clear(input))
20769
21096
  };
@@ -20776,6 +21103,7 @@ var OpensteerSessionRuntime = class {
20776
21103
  completedAt: Date.now(),
20777
21104
  outcome: "ok",
20778
21105
  data: {
21106
+ ...input.capture === void 0 ? {} : { capture: input.capture },
20779
21107
  ...input.tag === void 0 ? {} : { tag: input.tag },
20780
21108
  clearedCount: output.clearedCount
20781
21109
  }
@@ -21480,7 +21808,6 @@ var OpensteerSessionRuntime = class {
21480
21808
  });
21481
21809
  const networkRecords = await this.queryLiveNetwork(
21482
21810
  {
21483
- source: "live",
21484
21811
  pageRef,
21485
21812
  ...input.network?.url === void 0 ? {} : { url: input.network.url },
21486
21813
  ...input.network?.hostname === void 0 ? {} : { hostname: input.network.hostname },
@@ -21496,7 +21823,7 @@ var OpensteerSessionRuntime = class {
21496
21823
  );
21497
21824
  const persistedNetwork = filterReverseObservationWindow(
21498
21825
  networkRecords.filter(isReverseRelevantNetworkRecord),
21499
- this.networkJournal,
21826
+ this.networkHistory,
21500
21827
  input.captureWindowMs
21501
21828
  );
21502
21829
  const fallbackSavedNetwork = persistedNetwork.length === 0 ? (await root.registry.savedNetwork.query({
@@ -21511,7 +21838,10 @@ var OpensteerSessionRuntime = class {
21511
21838
  const observationId = `observation:${randomUUID()}`;
21512
21839
  const networkTag = `reverse-case:${caseRecord.id}:${observationId}`;
21513
21840
  if (observationNetwork.length > 0) {
21514
- await root.registry.savedNetwork.save(observationNetwork, networkTag);
21841
+ await root.registry.savedNetwork.save(observationNetwork, {
21842
+ tag: networkTag,
21843
+ bodyWriteMode: input.network?.includeBodies === false ? "metadata-only" : "authoritative"
21844
+ });
21515
21845
  }
21516
21846
  const scriptArtifactIds = input.includeScripts === false ? [] : (await this.captureScriptsInternal(
21517
21847
  pageRef,
@@ -21607,7 +21937,7 @@ var OpensteerSessionRuntime = class {
21607
21937
  includeBodies: true,
21608
21938
  redactSecretHeaders: false
21609
21939
  }),
21610
- observedAt: this.networkJournal.getObservedAt(recordId)
21940
+ observedAt: this.networkHistory.getObservedAt(recordId)
21611
21941
  }))
21612
21942
  );
21613
21943
  const clusteredRecords = observationRecords.map((entry) => {
@@ -21955,7 +22285,9 @@ var OpensteerSessionRuntime = class {
21955
22285
  };
21956
22286
  }
21957
22287
  const bindings = /* @__PURE__ */ new Map();
21958
- const baselineRequestIds = await this.beginMutationCapture(timeout);
22288
+ const baselineRequestIds = await this.readLiveRequestIds(timeout, {
22289
+ includeCurrentPageOnly: true
22290
+ });
21959
22291
  const pageRef = explicitPageRef ?? await this.ensurePageRef();
21960
22292
  const validatorMap = new Map(
21961
22293
  packageRecord.payload.validators.map((validator) => [validator.id, validator])
@@ -22282,8 +22614,7 @@ var OpensteerSessionRuntime = class {
22282
22614
  const inferred = inferRequestPlanFromNetworkRecord(record, {
22283
22615
  recordId: candidate.recordId,
22284
22616
  key: input.key,
22285
- version: input.version,
22286
- lifecycle: "draft"
22617
+ version: input.version
22287
22618
  });
22288
22619
  const defaultHeaders = inferred.payload.endpoint.defaultHeaders === void 0 ? void 0 : stripManagedRequestHeaders(
22289
22620
  inferred.payload.endpoint.defaultHeaders,
@@ -22548,7 +22879,10 @@ var OpensteerSessionRuntime = class {
22548
22879
  timeout.signal
22549
22880
  )).filter((record) => !baselineRequestIds.has(record.record.requestId));
22550
22881
  if (deltaRecords.length > 0) {
22551
- await root.registry.savedNetwork.save(deltaRecords, `interaction:${pageRef}`);
22882
+ await root.registry.savedNetwork.save(deltaRecords, {
22883
+ tag: `interaction:${pageRef}`,
22884
+ bodyWriteMode: "authoritative"
22885
+ });
22552
22886
  }
22553
22887
  const trace = await root.registry.interactionTraces.write({
22554
22888
  key: input.key ?? buildInteractionTraceKey(pageInfo.url),
@@ -22891,7 +23225,7 @@ var OpensteerSessionRuntime = class {
22891
23225
  includeBodies: true
22892
23226
  });
22893
23227
  const inferred = inferRequestPlanFromNetworkRecord(source, input, {
22894
- ...this.networkJournal.getObservedAt(source.recordId) === void 0 ? {} : { observedAt: this.networkJournal.getObservedAt(source.recordId) }
23228
+ ...this.networkHistory.getObservedAt(source.recordId) === void 0 ? {} : { observedAt: this.networkHistory.getObservedAt(source.recordId) }
22895
23229
  });
22896
23230
  return timeout.runStep(
22897
23231
  () => root.registry.requestPlans.write({
@@ -22911,8 +23245,7 @@ var OpensteerSessionRuntime = class {
22911
23245
  recordId: input.recordId,
22912
23246
  id: record.id,
22913
23247
  key: record.key,
22914
- version: record.version,
22915
- lifecycle: record.lifecycle
23248
+ version: record.version
22916
23249
  }
22917
23250
  });
22918
23251
  return record;
@@ -22953,8 +23286,7 @@ var OpensteerSessionRuntime = class {
22953
23286
  data: {
22954
23287
  id: record.id,
22955
23288
  key: record.key,
22956
- version: record.version,
22957
- lifecycle: record.lifecycle
23289
+ version: record.version
22958
23290
  }
22959
23291
  });
22960
23292
  return record;
@@ -23000,8 +23332,7 @@ var OpensteerSessionRuntime = class {
23000
23332
  data: {
23001
23333
  id: record.id,
23002
23334
  key: record.key,
23003
- version: record.version,
23004
- lifecycle: record.lifecycle
23335
+ version: record.version
23005
23336
  }
23006
23337
  });
23007
23338
  return record;
@@ -23628,34 +23959,38 @@ var OpensteerSessionRuntime = class {
23628
23959
  assertValidSemanticOperationInput("computer.execute", input);
23629
23960
  const pageRef = await this.ensurePageRef();
23630
23961
  const startedAt = Date.now();
23962
+ let mutationCaptureDiagnostics;
23963
+ let boundaryDiagnostics;
23631
23964
  try {
23632
- const { artifacts, output } = await this.runWithOperationTimeout(
23965
+ const { artifacts, output } = await this.runMutationCapturedOperation(
23633
23966
  "computer.execute",
23967
+ {
23968
+ ...input.captureNetwork === void 0 ? {} : { captureNetwork: input.captureNetwork },
23969
+ options
23970
+ },
23634
23971
  async (timeout) => {
23635
- const baselineRequestIds = await this.beginMutationCapture(timeout);
23636
23972
  try {
23637
23973
  const output2 = await this.requireComputer().execute({
23638
23974
  pageRef,
23639
23975
  input,
23640
23976
  timeout
23641
23977
  });
23978
+ boundaryDiagnostics = takeActionBoundaryDiagnostics(timeout.signal);
23642
23979
  timeout.throwIfAborted();
23643
23980
  this.pageRef = output2.pageRef;
23644
- this.latestSnapshot = void 0;
23645
- await this.completeMutationCapture(timeout, baselineRequestIds, input.networkTag);
23646
23981
  const artifacts2 = await this.persistComputerArtifacts(output2, timeout);
23647
23982
  return {
23648
23983
  artifacts: { manifests: artifacts2.manifests },
23649
23984
  output: artifacts2.output
23650
23985
  };
23651
23986
  } catch (error) {
23652
- await this.completeMutationCapture(timeout, baselineRequestIds, input.networkTag).catch(
23653
- () => void 0
23654
- );
23987
+ boundaryDiagnostics ??= takeActionBoundaryDiagnostics(timeout.signal);
23655
23988
  throw error;
23656
23989
  }
23657
23990
  },
23658
- options
23991
+ (diagnostics) => {
23992
+ mutationCaptureDiagnostics = diagnostics;
23993
+ }
23659
23994
  );
23660
23995
  await this.appendTrace({
23661
23996
  operation: "computer.execute",
@@ -23671,6 +24006,8 @@ var OpensteerSessionRuntime = class {
23671
24006
  nativeViewport: output.nativeViewport,
23672
24007
  displayScale: output.displayScale,
23673
24008
  timing: output.timing,
24009
+ ...boundaryDiagnostics === void 0 ? {} : { settle: boundaryDiagnostics },
24010
+ ...buildMutationCaptureTraceData(mutationCaptureDiagnostics),
23674
24011
  ...output.trace === void 0 ? {} : { trace: output.trace }
23675
24012
  },
23676
24013
  context: buildRuntimeTraceContext({
@@ -23689,6 +24026,10 @@ var OpensteerSessionRuntime = class {
23689
24026
  completedAt: Date.now(),
23690
24027
  outcome: "error",
23691
24028
  error,
24029
+ data: {
24030
+ ...boundaryDiagnostics === void 0 ? {} : { settle: boundaryDiagnostics },
24031
+ ...buildMutationCaptureTraceData(mutationCaptureDiagnostics)
24032
+ },
23692
24033
  context: buildRuntimeTraceContext({
23693
24034
  sessionRef: this.sessionRef,
23694
24035
  pageRef: this.pageRef
@@ -23707,7 +24048,7 @@ var OpensteerSessionRuntime = class {
23707
24048
  await this.runWithOperationTimeout(
23708
24049
  "session.close",
23709
24050
  async (timeout) => {
23710
- await timeout.runStep(() => this.flushBackgroundNetworkPersistence());
24051
+ await timeout.runStep(() => this.flushPersistedNetworkHistory());
23711
24052
  if (engine === void 0) {
23712
24053
  return;
23713
24054
  }
@@ -23776,10 +24117,7 @@ var OpensteerSessionRuntime = class {
23776
24117
  }
23777
24118
  async disconnect() {
23778
24119
  try {
23779
- await this.flushBackgroundNetworkPersistence();
23780
- if (this.sessionRef !== void 0 && this.pageRef !== void 0) {
23781
- await this.saveNetwork({ tag: "auto" }).catch(() => void 0);
23782
- }
24120
+ await this.flushPersistedNetworkHistory();
23783
24121
  } finally {
23784
24122
  await this.resetRuntimeState({
23785
24123
  disposeEngine: true
@@ -23795,33 +24133,38 @@ var OpensteerSessionRuntime = class {
23795
24133
  async runDomAction(operation, input, executor, options = {}) {
23796
24134
  const pageRef = await this.ensurePageRef();
23797
24135
  const startedAt = Date.now();
24136
+ let mutationCaptureDiagnostics;
24137
+ let boundaryDiagnostics;
23798
24138
  try {
23799
- const { executed, preparedTarget } = await this.runWithOperationTimeout(
24139
+ const { executed, preparedTarget } = await this.runMutationCapturedOperation(
23800
24140
  operation,
24141
+ {
24142
+ ...input.captureNetwork === void 0 ? {} : { captureNetwork: input.captureNetwork },
24143
+ options
24144
+ },
23801
24145
  async (timeout) => {
23802
- const baselineRequestIds = await this.beginMutationCapture(timeout);
24146
+ const preparedTarget2 = await this.prepareDomTarget(
24147
+ pageRef,
24148
+ operation,
24149
+ input.target,
24150
+ input.persistAsDescription,
24151
+ timeout
24152
+ );
23803
24153
  try {
23804
- const preparedTarget2 = await this.prepareDomTarget(
23805
- pageRef,
23806
- operation,
23807
- input.target,
23808
- input.persistAsDescription,
23809
- timeout
23810
- );
23811
24154
  const executed2 = await executor(pageRef, preparedTarget2.target, timeout);
23812
- await this.completeMutationCapture(timeout, baselineRequestIds, input.networkTag);
24155
+ boundaryDiagnostics = takeActionBoundaryDiagnostics(timeout.signal);
23813
24156
  return {
23814
24157
  executed: executed2,
23815
24158
  preparedTarget: preparedTarget2
23816
24159
  };
23817
24160
  } catch (error) {
23818
- await this.completeMutationCapture(timeout, baselineRequestIds, input.networkTag).catch(
23819
- () => void 0
23820
- );
24161
+ boundaryDiagnostics ??= takeActionBoundaryDiagnostics(timeout.signal);
23821
24162
  throw error;
23822
24163
  }
23823
24164
  },
23824
- options
24165
+ (diagnostics) => {
24166
+ mutationCaptureDiagnostics = diagnostics;
24167
+ }
23825
24168
  );
23826
24169
  const output = toOpensteerActionResult(executed.result, preparedTarget.persistedDescription);
23827
24170
  await this.appendTrace({
@@ -23832,7 +24175,9 @@ var OpensteerSessionRuntime = class {
23832
24175
  data: {
23833
24176
  target: output.target,
23834
24177
  ...output.point === void 0 ? {} : { point: output.point },
23835
- ...output.persistedDescription === void 0 ? {} : { persistedDescription: output.persistedDescription }
24178
+ ...output.persistedDescription === void 0 ? {} : { persistedDescription: output.persistedDescription },
24179
+ ...boundaryDiagnostics === void 0 ? {} : { settle: boundaryDiagnostics },
24180
+ ...buildMutationCaptureTraceData(mutationCaptureDiagnostics)
23836
24181
  },
23837
24182
  context: buildRuntimeTraceContext({
23838
24183
  sessionRef: this.sessionRef,
@@ -23850,6 +24195,10 @@ var OpensteerSessionRuntime = class {
23850
24195
  completedAt: Date.now(),
23851
24196
  outcome: "error",
23852
24197
  error,
24198
+ data: {
24199
+ ...boundaryDiagnostics === void 0 ? {} : { settle: boundaryDiagnostics },
24200
+ ...buildMutationCaptureTraceData(mutationCaptureDiagnostics)
24201
+ },
23853
24202
  context: buildRuntimeTraceContext({
23854
24203
  sessionRef: this.sessionRef,
23855
24204
  pageRef
@@ -23871,19 +24220,15 @@ var OpensteerSessionRuntime = class {
23871
24220
  };
23872
24221
  }
23873
24222
  if (target.kind === "element") {
23874
- const counter = this.latestSnapshot?.counterRecords.get(target.element);
23875
- if (!counter) {
23876
- throw new Error(`no counter ${String(target.element)} is available in the latest snapshot`);
23877
- }
24223
+ const elementTarget = {
24224
+ kind: "selector",
24225
+ selector: `[c="${String(target.element)}"]`
24226
+ };
23878
24227
  const resolved2 = await timeout.runStep(
23879
24228
  () => this.requireDom().resolveTarget({
23880
24229
  pageRef,
23881
24230
  method,
23882
- target: {
23883
- kind: "live",
23884
- locator: counter.locator,
23885
- anchor: counter.anchor
23886
- }
24231
+ target: elementTarget
23887
24232
  })
23888
24233
  );
23889
24234
  const stablePath2 = resolved2.replayPath ?? await timeout.runStep(
@@ -23941,11 +24286,11 @@ var OpensteerSessionRuntime = class {
23941
24286
  };
23942
24287
  }
23943
24288
  async queryLiveNetwork(input, timeout, options = {}) {
23944
- const requestIds = resolveLiveQueryRequestIds(input, this.networkJournal);
24289
+ const requestIds = resolveLiveQueryRequestIds(input, this.networkHistory);
23945
24290
  if (requestIds !== void 0 && requestIds.length === 0) {
23946
24291
  return [];
23947
24292
  }
23948
- const pageRef = resolveLiveQueryPageRef(input, this.pageRef, requestIds, this.networkJournal);
24293
+ const pageRef = resolveLiveQueryPageRef(input, this.pageRef, requestIds, this.networkHistory);
23949
24294
  const includeCurrentPageOnly = pageRef === void 0 && input.recordId === void 0;
23950
24295
  const metadataRecords = await timeout.runStep(
23951
24296
  () => this.readLiveNetworkRecords(
@@ -23963,7 +24308,7 @@ var OpensteerSessionRuntime = class {
23963
24308
  const filtered = filterNetworkQueryRecords(metadataRecords, {
23964
24309
  ...input.recordId === void 0 ? {} : { recordId: input.recordId },
23965
24310
  ...input.requestId === void 0 ? {} : { requestId: input.requestId },
23966
- ...input.actionId === void 0 ? {} : { actionId: input.actionId },
24311
+ ...input.capture === void 0 ? {} : { capture: input.capture },
23967
24312
  ...input.tag === void 0 ? {} : { tag: input.tag },
23968
24313
  ...input.url === void 0 ? {} : { url: input.url },
23969
24314
  ...input.hostname === void 0 ? {} : { hostname: input.hostname },
@@ -23972,7 +24317,7 @@ var OpensteerSessionRuntime = class {
23972
24317
  ...input.status === void 0 ? {} : { status: input.status },
23973
24318
  ...input.resourceType === void 0 ? {} : { resourceType: input.resourceType }
23974
24319
  });
23975
- const sorted = sortLiveNetworkRecords(filtered, this.networkJournal);
24320
+ const sorted = sortLiveNetworkRecords(filtered, this.networkHistory);
23976
24321
  const limit = options.ignoreLimit ? sorted.length : Math.max(1, Math.min(input.limit ?? 50, 200));
23977
24322
  const limited = sorted.slice(0, limit);
23978
24323
  if (!(input.includeBodies ?? false) || limited.length === 0) {
@@ -24169,40 +24514,95 @@ var OpensteerSessionRuntime = class {
24169
24514
  artifactId: manifest.artifactId
24170
24515
  };
24171
24516
  }
24172
- beginMutationCapture(timeout) {
24173
- return this.readLiveRequestIds(timeout, {
24174
- includeCurrentPageOnly: true
24175
- });
24176
- }
24177
- async completeMutationCapture(timeout, baselineRequestIds, networkTag) {
24178
- const records = await timeout.runStep(
24179
- () => this.readLiveNetworkRecords(
24180
- {
24181
- includeBodies: false,
24182
- includeCurrentPageOnly: true
24517
+ async runMutationCapturedOperation(operation, input, execute, onFinalized) {
24518
+ let plan;
24519
+ try {
24520
+ const result = await this.runWithOperationTimeout(
24521
+ operation,
24522
+ async (timeout) => {
24523
+ plan = await this.beginMutationCapture(timeout, input.captureNetwork);
24524
+ return execute(timeout);
24183
24525
  },
24184
- timeout.signal
24185
- )
24526
+ input.options
24527
+ );
24528
+ const diagnostics = await this.finalizeMutationCaptureBestEffort(plan);
24529
+ onFinalized?.(diagnostics);
24530
+ return result;
24531
+ } catch (error) {
24532
+ const diagnostics = await this.finalizeMutationCaptureBestEffort(plan);
24533
+ onFinalized?.(diagnostics);
24534
+ throw error;
24535
+ }
24536
+ }
24537
+ async beginMutationCapture(timeout, capture) {
24538
+ if (capture === void 0) {
24539
+ return void 0;
24540
+ }
24541
+ return {
24542
+ baselineRequestIds: await this.readLiveRequestIds(timeout, {
24543
+ includeCurrentPageOnly: true
24544
+ }),
24545
+ capture
24546
+ };
24547
+ }
24548
+ async finalizeMutationCaptureBestEffort(plan) {
24549
+ if (plan === void 0) {
24550
+ return {};
24551
+ }
24552
+ try {
24553
+ await withDetachedTimeoutSignal(MUTATION_CAPTURE_FINALIZE_TIMEOUT_MS, async (signal) => {
24554
+ await this.completeMutationCaptureWithSignal(signal, plan);
24555
+ });
24556
+ return {};
24557
+ } catch (error) {
24558
+ return {
24559
+ finalizeError: normalizeOpensteerError(error)
24560
+ };
24561
+ }
24562
+ }
24563
+ async completeMutationCaptureWithSignal(signal, plan) {
24564
+ const records = await this.readLiveNetworkRecords(
24565
+ {
24566
+ includeBodies: false,
24567
+ includeCurrentPageOnly: true
24568
+ },
24569
+ signal
24186
24570
  );
24187
- const delta = records.filter((record) => !baselineRequestIds.has(record.record.requestId));
24571
+ const delta = records.filter((record) => !plan.baselineRequestIds.has(record.record.requestId));
24188
24572
  if (delta.length === 0) {
24189
24573
  return;
24190
24574
  }
24191
- this.networkJournal.assignActionId(delta, `action:${randomUUID()}`);
24192
- if (networkTag === void 0) {
24193
- return;
24194
- }
24195
- this.networkJournal.addTag(delta, networkTag);
24196
- this.scheduleBackgroundNetworkSaveByRequestIds(
24575
+ this.networkHistory.assignCapture(delta, plan.capture);
24576
+ await this.persistLiveRequestIdsWithSignal(
24197
24577
  delta.map((record) => record.record.requestId),
24198
- networkTag
24578
+ signal,
24579
+ {
24580
+ includeCurrentPageOnly: true
24581
+ }
24199
24582
  );
24200
24583
  }
24201
24584
  async resolveNetworkRecordByRecordId(recordId, timeout, options) {
24202
24585
  const root = await this.ensureRoot();
24586
+ await this.syncPersistedNetworkSelection(
24587
+ timeout,
24588
+ {
24589
+ recordId,
24590
+ includeBodies: options.includeBodies
24591
+ },
24592
+ {
24593
+ includeBodies: options.includeBodies
24594
+ }
24595
+ );
24596
+ const saved = await timeout.runStep(
24597
+ () => root.registry.savedNetwork.getByRecordId(recordId, {
24598
+ includeBodies: options.includeBodies
24599
+ })
24600
+ );
24601
+ if (saved) {
24602
+ return saved;
24603
+ }
24203
24604
  const live = await this.queryLiveNetwork(
24204
24605
  {
24205
- source: "live",
24206
24606
  recordId,
24207
24607
  includeBodies: options.includeBodies,
24208
24608
  limit: 1
@@ -24213,24 +24613,15 @@ var OpensteerSessionRuntime = class {
24213
24613
  ...options.redactSecretHeaders === void 0 ? {} : { redactSecretHeaders: options.redactSecretHeaders }
24214
24614
  }
24215
24615
  );
24216
- if (live.length > 0) {
24616
+ if (live[0] !== void 0) {
24217
24617
  return live[0];
24218
24618
  }
24219
- await timeout.runStep(() => this.flushBackgroundNetworkPersistence());
24220
- const saved = await timeout.runStep(
24221
- () => root.registry.savedNetwork.getByRecordId(recordId, {
24222
- includeBodies: options.includeBodies
24223
- })
24224
- );
24225
- if (!saved) {
24226
- throw new OpensteerProtocolError("not-found", `network record ${recordId} was not found`, {
24227
- details: {
24228
- recordId,
24229
- kind: "network-record"
24230
- }
24231
- });
24232
- }
24233
- return saved;
24619
+ throw new OpensteerProtocolError("not-found", `network record ${recordId} was not found`, {
24620
+ details: {
24621
+ recordId,
24622
+ kind: "network-record"
24623
+ }
24624
+ });
24234
24625
  }
24235
24626
  resolveCurrentStateSource() {
24236
24627
  const ownership = this.sessionInfoBase.provider?.ownership;
@@ -24470,7 +24861,6 @@ var OpensteerSessionRuntime = class {
24470
24861
  timeout.throwIfAborted();
24471
24862
  const records = await this.queryLiveNetwork(
24472
24863
  {
24473
- source: "live",
24474
24864
  pageRef,
24475
24865
  url,
24476
24866
  method,
@@ -24501,7 +24891,6 @@ var OpensteerSessionRuntime = class {
24501
24891
  timeout.throwIfAborted();
24502
24892
  const records = await this.queryLiveNetwork(
24503
24893
  {
24504
- source: "live",
24505
24894
  pageRef,
24506
24895
  ...filter.url === void 0 ? {} : { url: filter.url },
24507
24896
  ...filter.host === void 0 ? {} : { hostname: filter.host },
@@ -24567,12 +24956,12 @@ var OpensteerSessionRuntime = class {
24567
24956
  };
24568
24957
  }
24569
24958
  }
24570
- async readLiveNetworkRecords(input, signal) {
24959
+ async readBrowserNetworkRecords(input, signal) {
24571
24960
  const sessionRef = this.sessionRef;
24572
24961
  if (!sessionRef) {
24573
24962
  throw new Error("Opensteer session is not initialized");
24574
24963
  }
24575
- const records = await this.requireEngine().getNetworkRecords({
24964
+ return this.requireEngine().getNetworkRecords({
24576
24965
  sessionRef,
24577
24966
  ...input.includeCurrentPageOnly === false || input.pageRef !== void 0 ? input.pageRef === void 0 ? {} : { pageRef: input.pageRef } : this.pageRef === void 0 ? {} : { pageRef: this.pageRef },
24578
24967
  ...input.requestIds === void 0 ? {} : { requestIds: input.requestIds },
@@ -24585,10 +24974,103 @@ var OpensteerSessionRuntime = class {
24585
24974
  includeBodies: input.includeBodies,
24586
24975
  signal
24587
24976
  });
24588
- return this.networkJournal.sync(records, {
24977
+ }
24978
+ async readLiveNetworkRecords(input, signal) {
24979
+ const records = await this.readBrowserNetworkRecords(input, signal);
24980
+ return this.networkHistory.materialize(records, {
24589
24981
  redactSecretHeaders: input.redactSecretHeaders ?? true
24590
24982
  });
24591
24983
  }
24984
+ async persistLiveRequestIds(requestIds, timeout, options) {
24985
+ return timeout.runStep(
24986
+ () => this.persistLiveRequestIdsWithSignal(requestIds, timeout.signal, options)
24987
+ );
24988
+ }
24989
+ async persistLiveRequestIdsWithSignal(requestIds, signal, options) {
24990
+ if (requestIds.length === 0) {
24991
+ return [];
24992
+ }
24993
+ const root = await this.ensureRoot();
24994
+ const browserRecords = await this.readBrowserNetworkRecords(
24995
+ {
24996
+ includeBodies: true,
24997
+ includeCurrentPageOnly: options.includeCurrentPageOnly,
24998
+ ...options.pageRef === void 0 ? {} : { pageRef: options.pageRef },
24999
+ requestIds
25000
+ },
25001
+ signal
25002
+ );
25003
+ return this.networkHistory.persist(browserRecords, root.registry.savedNetwork, {
25004
+ bodyWriteMode: "authoritative",
25005
+ redactSecretHeaders: false
25006
+ });
25007
+ }
25008
+ async syncPersistedNetworkSelection(timeout, input, options) {
25009
+ if (this.sessionRef === void 0) {
25010
+ return [];
25011
+ }
25012
+ const requestIds = resolveLiveQueryRequestIds(input, this.networkHistory);
25013
+ if (requestIds !== void 0 && requestIds.length === 0) {
25014
+ return [];
25015
+ }
25016
+ const pageRef = resolveLiveQueryPageRef(input, this.pageRef, requestIds, this.networkHistory);
25017
+ const includeCurrentPageOnly = pageRef === void 0 && input.recordId === void 0;
25018
+ const browserRecords = await timeout.runStep(
25019
+ () => this.readBrowserNetworkRecords(
25020
+ {
25021
+ ...pageRef === void 0 ? {} : { pageRef },
25022
+ ...requestIds === void 0 ? {} : { requestIds },
25023
+ ...input.url === void 0 ? {} : { url: input.url },
25024
+ ...input.hostname === void 0 ? {} : { hostname: input.hostname },
25025
+ ...input.path === void 0 ? {} : { path: input.path },
25026
+ ...input.method === void 0 ? {} : { method: input.method },
25027
+ ...input.status === void 0 ? {} : { status: input.status },
25028
+ ...input.resourceType === void 0 ? {} : { resourceType: input.resourceType },
25029
+ includeBodies: options.includeBodies,
25030
+ includeCurrentPageOnly
25031
+ },
25032
+ timeout.signal
25033
+ )
25034
+ );
25035
+ const root = await this.ensureRoot();
25036
+ return timeout.runStep(
25037
+ () => this.networkHistory.persist(browserRecords, root.registry.savedNetwork, {
25038
+ bodyWriteMode: options.includeBodies ? "authoritative" : "metadata-only",
25039
+ redactSecretHeaders: false
25040
+ })
25041
+ );
25042
+ }
25043
+ toSavedNetworkQueryInput(input) {
25044
+ return {
25045
+ ...input.pageRef === void 0 ? {} : { pageRef: input.pageRef },
25046
+ ...input.recordId === void 0 ? {} : { recordId: input.recordId },
25047
+ ...input.requestId === void 0 ? {} : { requestId: input.requestId },
25048
+ ...input.capture === void 0 ? {} : { capture: input.capture },
25049
+ ...input.tag === void 0 ? {} : { tag: input.tag },
25050
+ ...input.url === void 0 ? {} : { url: input.url },
25051
+ ...input.hostname === void 0 ? {} : { hostname: input.hostname },
25052
+ ...input.path === void 0 ? {} : { path: input.path },
25053
+ ...input.method === void 0 ? {} : { method: input.method },
25054
+ ...input.status === void 0 ? {} : { status: input.status },
25055
+ ...input.resourceType === void 0 ? {} : { resourceType: input.resourceType },
25056
+ ...input.includeBodies === void 0 ? {} : { includeBodies: input.includeBodies },
25057
+ ...input.limit === void 0 ? {} : { limit: input.limit }
25058
+ };
25059
+ }
25060
+ toQueryInputFromTagInput(input) {
25061
+ return {
25062
+ ...input.pageRef === void 0 ? {} : { pageRef: input.pageRef },
25063
+ ...input.recordId === void 0 ? {} : { recordId: input.recordId },
25064
+ ...input.requestId === void 0 ? {} : { requestId: input.requestId },
25065
+ ...input.capture === void 0 ? {} : { capture: input.capture },
25066
+ ...input.url === void 0 ? {} : { url: input.url },
25067
+ ...input.hostname === void 0 ? {} : { hostname: input.hostname },
25068
+ ...input.path === void 0 ? {} : { path: input.path },
25069
+ ...input.method === void 0 ? {} : { method: input.method },
25070
+ ...input.status === void 0 ? {} : { status: input.status },
25071
+ ...input.resourceType === void 0 ? {} : { resourceType: input.resourceType }
25072
+ };
25073
+ }
24592
25074
  async readLiveRequestIds(timeout, options) {
24593
25075
  const records = await timeout.runStep(
24594
25076
  () => this.readLiveNetworkRecords(
@@ -24612,7 +25094,17 @@ var OpensteerSessionRuntime = class {
24612
25094
  )
24613
25095
  );
24614
25096
  const delta = records.filter((record) => !baselineRequestIds.has(record.record.requestId));
24615
- return sortLiveNetworkRecords(delta, this.networkJournal)[0]?.recordId;
25097
+ if (delta.length === 0) {
25098
+ return void 0;
25099
+ }
25100
+ await this.persistLiveRequestIds(
25101
+ delta.map((record) => record.record.requestId),
25102
+ timeout,
25103
+ {
25104
+ includeCurrentPageOnly: options.includeCurrentPageOnly
25105
+ }
25106
+ );
25107
+ return sortLiveNetworkRecords(delta, this.networkHistory)[0]?.recordId;
24616
25108
  }
24617
25109
  async executeTransportRequestWithJournal(request, timeout, sessionRef) {
24618
25110
  const baselineRequestIds = await this.readLiveRequestIds(timeout, {
@@ -24896,7 +25388,6 @@ var OpensteerSessionRuntime = class {
24896
25388
  const syntheticSessionRef = binding?.sessionRef ?? createSessionRef(`${transportLabel}-${this.workspace}`);
24897
25389
  const record = {
24898
25390
  recordId,
24899
- source: "saved",
24900
25391
  savedAt: now,
24901
25392
  record: {
24902
25393
  kind: "http",
@@ -24918,7 +25409,10 @@ var OpensteerSessionRuntime = class {
24918
25409
  ...response.body === void 0 ? {} : { responseBody: toProtocolBodyPayload(response.body) }
24919
25410
  }
24920
25411
  };
24921
- await root.registry.savedNetwork.save([record], tag);
25412
+ await root.registry.savedNetwork.save([record], {
25413
+ bodyWriteMode: "authoritative",
25414
+ ...tag === void 0 ? {} : { tag }
25415
+ });
24922
25416
  return recordId;
24923
25417
  }
24924
25418
  async executeResolvedRequestPlan(plan, input, timeout, binding) {
@@ -25126,7 +25620,7 @@ var OpensteerSessionRuntime = class {
25126
25620
  }
25127
25621
  async touchRequestPlanFreshness(plan) {
25128
25622
  const freshness = touchFreshness(plan.freshness);
25129
- await this.requireRoot().registry.requestPlans.updateMetadata({
25623
+ await this.requireRoot().registry.requestPlans.updateFreshness({
25130
25624
  id: plan.id,
25131
25625
  ...freshness === void 0 ? {} : { freshness }
25132
25626
  });
@@ -25291,7 +25785,6 @@ var OpensteerSessionRuntime = class {
25291
25785
  const record = await pollUntilResult(timeout, async () => {
25292
25786
  const matches = await this.queryLiveNetwork(
25293
25787
  {
25294
- source: "live",
25295
25788
  ...step.url === void 0 ? {} : { url: interpolateTemplate(step.url, variables) },
25296
25789
  ...step.hostname === void 0 ? {} : { hostname: interpolateTemplate(step.hostname, variables) },
25297
25790
  ...step.path === void 0 ? {} : { path: interpolateTemplate(step.path, variables) },
@@ -25749,37 +26242,7 @@ var OpensteerSessionRuntime = class {
25749
26242
  const pageUrl = step.pageUrl === void 0 ? void 0 : interpolateTemplate(step.pageUrl, variables);
25750
26243
  return snapshot.sessionStorage?.filter((entry) => entry.origin === origin).find((entry) => pageUrl === void 0 || entry.origin === new URL(pageUrl).origin)?.entries.find((entry) => entry.key === key)?.value;
25751
26244
  }
25752
- scheduleBackgroundNetworkSaveByRequestIds(requestIds, tag) {
25753
- const task = (async () => {
25754
- const root = await this.ensureRoot();
25755
- const requestIdSet = new Set(requestIds);
25756
- const records = await this.readLiveNetworkRecords(
25757
- {
25758
- includeBodies: true,
25759
- includeCurrentPageOnly: false,
25760
- ...this.pageRef === void 0 ? {} : { pageRef: this.pageRef },
25761
- requestIds,
25762
- redactSecretHeaders: false
25763
- },
25764
- new AbortController().signal
25765
- );
25766
- const filtered = records.filter((record) => requestIdSet.has(record.record.requestId));
25767
- if (filtered.length === 0) {
25768
- return;
25769
- }
25770
- await root.registry.savedNetwork.save(filtered, tag);
25771
- })();
25772
- this.backgroundNetworkPersistence.add(task);
25773
- task.finally(() => {
25774
- this.backgroundNetworkPersistence.delete(task);
25775
- });
25776
- void task.catch(() => void 0);
25777
- }
25778
- async flushBackgroundNetworkPersistence() {
25779
- if (this.backgroundNetworkPersistence.size === 0) {
25780
- return;
25781
- }
25782
- await Promise.all([...this.backgroundNetworkPersistence]);
26245
+ async flushPersistedNetworkHistory() {
25783
26246
  }
25784
26247
  toDomTargetRef(target) {
25785
26248
  if (target.kind === "description") {
@@ -25794,22 +26257,35 @@ var OpensteerSessionRuntime = class {
25794
26257
  selector: target.selector
25795
26258
  };
25796
26259
  }
25797
- const counter = this.latestSnapshot?.counterRecords.get(target.element);
25798
- if (!counter) {
25799
- throw new Error(`no counter ${String(target.element)} is available in the latest snapshot`);
25800
- }
25801
26260
  return {
25802
- kind: "live",
25803
- locator: counter.locator,
25804
- anchor: counter.anchor
26261
+ kind: "selector",
26262
+ selector: `[c="${String(target.element)}"]`
25805
26263
  };
25806
26264
  }
25807
26265
  async ensureRoot() {
25808
- this.root ??= await createFilesystemOpensteerWorkspace({
25809
- rootPath: this.rootPath,
25810
- ...this.workspaceName === void 0 ? {} : { workspace: this.workspaceName },
25811
- scope: this.workspaceName === void 0 ? "temporary" : "workspace"
25812
- });
26266
+ if (!this.root) {
26267
+ const workspace = await createFilesystemOpensteerWorkspace({
26268
+ rootPath: this.rootPath,
26269
+ ...this.workspaceName === void 0 ? {} : { workspace: this.workspaceName },
26270
+ scope: this.workspaceName === void 0 ? "temporary" : "workspace"
26271
+ });
26272
+ if (this.registryOverrides) {
26273
+ const overrides = this.registryOverrides;
26274
+ this.root = {
26275
+ ...workspace,
26276
+ registry: {
26277
+ ...workspace.registry,
26278
+ ...overrides.requestPlans === void 0 ? {} : { requestPlans: overrides.requestPlans },
26279
+ ...overrides.authRecipes === void 0 ? {} : { authRecipes: overrides.authRecipes },
26280
+ ...overrides.recipes === void 0 ? {} : { recipes: overrides.recipes },
26281
+ ...overrides.reverseCases === void 0 ? {} : { reverseCases: overrides.reverseCases },
26282
+ ...overrides.reversePackages === void 0 ? {} : { reversePackages: overrides.reversePackages }
26283
+ }
26284
+ };
26285
+ } else {
26286
+ this.root = workspace;
26287
+ }
26288
+ }
25813
26289
  return this.root;
25814
26290
  }
25815
26291
  async ensureEngine(overrides = {}) {
@@ -25835,6 +26311,7 @@ var OpensteerSessionRuntime = class {
25835
26311
  engine,
25836
26312
  root,
25837
26313
  namespace: this.workspace,
26314
+ ...this.injectedDescriptorStore === void 0 ? {} : { descriptorStore: this.injectedDescriptorStore },
25838
26315
  policy: this.policy
25839
26316
  });
25840
26317
  this.computer = createComputerUseRuntime({
@@ -25842,7 +26319,7 @@ var OpensteerSessionRuntime = class {
25842
26319
  dom: this.dom,
25843
26320
  policy: this.policy
25844
26321
  });
25845
- this.extractionDescriptors = createOpensteerExtractionDescriptorStore({
26322
+ this.extractionDescriptors = this.injectedExtractionDescriptorStore ?? createOpensteerExtractionDescriptorStore({
25846
26323
  root,
25847
26324
  namespace: this.workspace
25848
26325
  });
@@ -26059,11 +26536,9 @@ var OpensteerSessionRuntime = class {
26059
26536
  }
26060
26537
  async resetRuntimeState(options) {
26061
26538
  const engine = this.engine;
26062
- this.networkJournal.clear();
26063
- this.backgroundNetworkPersistence.clear();
26539
+ this.networkHistory.clear();
26064
26540
  this.sessionRef = void 0;
26065
26541
  this.pageRef = void 0;
26066
- this.latestSnapshot = void 0;
26067
26542
  this.runId = void 0;
26068
26543
  this.dom = void 0;
26069
26544
  this.computer = void 0;
@@ -26137,10 +26612,10 @@ function buildEngineNetworkRecordFilters(input) {
26137
26612
  ...input.resourceType === void 0 ? {} : { resourceType: input.resourceType }
26138
26613
  };
26139
26614
  }
26140
- function resolveLiveQueryRequestIds(input, journal) {
26615
+ function resolveLiveQueryRequestIds(input, history) {
26141
26616
  const requestIdCandidates = [];
26142
26617
  if (input.recordId !== void 0) {
26143
- const requestId = journal.getRequestId(input.recordId);
26618
+ const requestId = history.getRequestId(input.recordId);
26144
26619
  if (requestId === void 0) {
26145
26620
  return [];
26146
26621
  }
@@ -26149,25 +26624,25 @@ function resolveLiveQueryRequestIds(input, journal) {
26149
26624
  if (input.requestId !== void 0) {
26150
26625
  requestIdCandidates.push(/* @__PURE__ */ new Set([input.requestId]));
26151
26626
  }
26152
- if (input.actionId !== void 0) {
26153
- requestIdCandidates.push(journal.getRequestIdsForActionId(input.actionId));
26627
+ if (input.capture !== void 0) {
26628
+ requestIdCandidates.push(history.getRequestIdsForCapture(input.capture));
26154
26629
  }
26155
26630
  if (input.tag !== void 0) {
26156
- requestIdCandidates.push(journal.getRequestIdsForTag(input.tag));
26631
+ requestIdCandidates.push(history.getRequestIdsForTag(input.tag));
26157
26632
  }
26158
26633
  if (requestIdCandidates.length === 0) {
26159
26634
  return void 0;
26160
26635
  }
26161
26636
  return intersectRequestIdSets(requestIdCandidates);
26162
26637
  }
26163
- function resolveLiveQueryPageRef(input, currentPageRef, requestIds, journal) {
26638
+ function resolveLiveQueryPageRef(input, currentPageRef, requestIds, history) {
26164
26639
  const requestedPageRef = selectLiveQueryPageRef(input, currentPageRef);
26165
26640
  if (requestedPageRef !== void 0 || requestIds === void 0) {
26166
26641
  return requestedPageRef;
26167
26642
  }
26168
26643
  const pageRefs = /* @__PURE__ */ new Set();
26169
26644
  for (const requestId of requestIds) {
26170
- const pageRef = journal.getPageRefForRequestId(requestId);
26645
+ const pageRef = history.getPageRefForRequestId(requestId);
26171
26646
  if (pageRef === void 0) {
26172
26647
  continue;
26173
26648
  }
@@ -26197,7 +26672,7 @@ function filterNetworkQueryRecords(records, input) {
26197
26672
  if (input.requestId !== void 0 && record.record.requestId !== input.requestId) {
26198
26673
  return false;
26199
26674
  }
26200
- if (input.actionId !== void 0 && record.actionId !== input.actionId) {
26675
+ if (input.capture !== void 0 && record.capture !== input.capture) {
26201
26676
  return false;
26202
26677
  }
26203
26678
  if (input.tag !== void 0 && !(record.tags ?? []).includes(input.tag)) {
@@ -26209,10 +26684,10 @@ function filterNetworkQueryRecords(records, input) {
26209
26684
  return true;
26210
26685
  });
26211
26686
  }
26212
- function sortLiveNetworkRecords(records, journal) {
26687
+ function sortLiveNetworkRecords(records, history) {
26213
26688
  return [...records].sort((left, right) => {
26214
- const leftObservedAt = journal.getObservedAt(left.recordId) ?? 0;
26215
- const rightObservedAt = journal.getObservedAt(right.recordId) ?? 0;
26689
+ const leftObservedAt = history.getObservedAt(left.recordId) ?? 0;
26690
+ const rightObservedAt = history.getObservedAt(right.recordId) ?? 0;
26216
26691
  if (leftObservedAt !== rightObservedAt) {
26217
26692
  return rightObservedAt - leftObservedAt;
26218
26693
  }
@@ -26419,9 +26894,8 @@ function buildMinimizedRequestPlan(input) {
26419
26894
  provenance: {
26420
26895
  source: "network-minimize",
26421
26896
  sourceId: input.record.recordId,
26422
- ...input.record.source === "saved" && input.record.savedAt !== void 0 ? { capturedAt: input.record.savedAt } : {}
26897
+ ...input.record.savedAt === void 0 ? {} : { capturedAt: input.record.savedAt }
26423
26898
  },
26424
- lifecycle: "draft",
26425
26899
  payload: normalizeRequestPlanPayload({
26426
26900
  transport: {
26427
26901
  kind: input.transport
@@ -26639,12 +27113,12 @@ function originFromUrl(url) {
26639
27113
  return void 0;
26640
27114
  }
26641
27115
  }
26642
- function filterReverseObservationWindow(records, journal, captureWindowMs) {
27116
+ function filterReverseObservationWindow(records, history, captureWindowMs) {
26643
27117
  if (captureWindowMs === void 0) {
26644
27118
  return records;
26645
27119
  }
26646
27120
  const observedAfter = Date.now() - captureWindowMs;
26647
- return records.filter((record) => (journal.getObservedAt(record.recordId) ?? 0) >= observedAfter);
27121
+ return records.filter((record) => (history.getObservedAt(record.recordId) ?? 0) >= observedAfter);
26648
27122
  }
26649
27123
  function isReverseRelevantNetworkRecord(record) {
26650
27124
  return record.record.resourceType === "document" || record.record.resourceType === "fetch" || record.record.resourceType === "xhr" || record.record.resourceType === "websocket" || record.record.resourceType === "event-stream";
@@ -28576,7 +29050,7 @@ function directionToDelta(direction, amount) {
28576
29050
  return { x: amount, y: 0 };
28577
29051
  }
28578
29052
  }
28579
- function normalizeNamespace3(value) {
29053
+ function normalizeNamespace2(value) {
28580
29054
  const normalized = String(value ?? "default").trim();
28581
29055
  return normalized.length === 0 ? "default" : normalized;
28582
29056
  }
@@ -28608,9 +29082,40 @@ function toOpensteerResolvedTarget2(target) {
28608
29082
  function normalizeOpensteerError(error) {
28609
29083
  return normalizeThrownOpensteerError(error, "Unknown Opensteer runtime failure");
28610
29084
  }
29085
+ function buildMutationCaptureTraceData(diagnostics) {
29086
+ if (diagnostics?.finalizeError === void 0) {
29087
+ return {};
29088
+ }
29089
+ return {
29090
+ networkCapture: {
29091
+ finalizeError: diagnostics.finalizeError
29092
+ }
29093
+ };
29094
+ }
28611
29095
  function isIgnorableRuntimeBindingError(error) {
28612
29096
  return isBrowserCoreError(error) && (error.code === "not-found" || error.code === "page-closed" || error.code === "session-closed");
28613
29097
  }
29098
+ async function withDetachedTimeoutSignal(timeoutMs, operation) {
29099
+ const controller = new AbortController();
29100
+ const timeoutError = new OpensteerProtocolError(
29101
+ "timeout",
29102
+ `mutation capture finalization exceeded ${String(timeoutMs)}ms timeout`,
29103
+ {
29104
+ details: {
29105
+ policy: "mutation-capture-finalize",
29106
+ budgetMs: timeoutMs
29107
+ }
29108
+ }
29109
+ );
29110
+ const timer = setTimeout(() => {
29111
+ controller.abort(timeoutError);
29112
+ }, timeoutMs);
29113
+ try {
29114
+ return await operation(controller.signal);
29115
+ } finally {
29116
+ clearTimeout(timer);
29117
+ }
29118
+ }
28614
29119
  function screenshotMediaType(format2) {
28615
29120
  switch (format2) {
28616
29121
  case "png":
@@ -28622,6 +29127,6 @@ function screenshotMediaType(format2) {
28622
29127
  }
28623
29128
  }
28624
29129
 
28625
- export { OPENSTEER_FILESYSTEM_WORKSPACE_LAYOUT, OPENSTEER_FILESYSTEM_WORKSPACE_VERSION, OPENSTEER_RUNTIME_CORE_VERSION, OpensteerSessionRuntime, createFilesystemOpensteerWorkspace, dispatchSemanticOperation, normalizeWorkspaceId, resolveFilesystemWorkspacePath };
29130
+ export { OPENSTEER_FILESYSTEM_WORKSPACE_LAYOUT, OPENSTEER_FILESYSTEM_WORKSPACE_VERSION, OPENSTEER_RUNTIME_CORE_VERSION, OpensteerSessionRuntime, buildDomDescriptorKey, buildDomDescriptorPayload, buildDomDescriptorVersion, createDomDescriptorStore, createFilesystemOpensteerWorkspace, createOpensteerExtractionDescriptorStore, dispatchSemanticOperation, hashDomDescriptorDescription, normalizeWorkspaceId, parseDomDescriptorRecord, parseExtractionDescriptorRecord, resolveFilesystemWorkspacePath, sanitizeReplayElementPath };
28626
29131
  //# sourceMappingURL=index.js.map
28627
29132
  //# sourceMappingURL=index.js.map