@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.cjs CHANGED
@@ -42,7 +42,7 @@ var vm__default = /*#__PURE__*/_interopDefault(vm);
42
42
 
43
43
  // package.json
44
44
  var package_default = {
45
- version: "0.1.0"};
45
+ version: "0.1.2"};
46
46
 
47
47
  // src/version.ts
48
48
  var OPENSTEER_RUNTIME_CORE_VERSION = package_default.version;
@@ -999,14 +999,10 @@ var networkRecordSchema = objectSchema(
999
999
  ]
1000
1000
  }
1001
1001
  );
1002
- var networkQuerySourceSchema = enumSchema(["live", "saved"], {
1003
- title: "NetworkQuerySource"
1004
- });
1005
1002
  var networkQueryRecordSchema = objectSchema(
1006
1003
  {
1007
1004
  recordId: stringSchema({ minLength: 1 }),
1008
- source: networkQuerySourceSchema,
1009
- actionId: stringSchema({ minLength: 1 }),
1005
+ capture: stringSchema({ minLength: 1 }),
1010
1006
  tags: arraySchema(stringSchema({ minLength: 1 }), {
1011
1007
  uniqueItems: true
1012
1008
  }),
@@ -1015,7 +1011,7 @@ var networkQueryRecordSchema = objectSchema(
1015
1011
  },
1016
1012
  {
1017
1013
  title: "NetworkQueryRecord",
1018
- required: ["recordId", "source", "record"]
1014
+ required: ["recordId", "record"]
1019
1015
  }
1020
1016
  );
1021
1017
  arraySchema(headerEntrySchema, {
@@ -1325,12 +1321,6 @@ var opensteerRegistryProvenanceSchema = objectSchema(
1325
1321
  required: ["source"]
1326
1322
  }
1327
1323
  );
1328
- var opensteerRequestPlanLifecycleSchema = enumSchema(
1329
- ["draft", "active", "deprecated", "retired"],
1330
- {
1331
- title: "OpensteerRequestPlanLifecycle"
1332
- }
1333
- );
1334
1324
  var opensteerRequestPlanFreshnessSchema = objectSchema(
1335
1325
  {
1336
1326
  lastValidatedAt: integerSchema({ minimum: 0 }),
@@ -1353,23 +1343,12 @@ var opensteerRequestPlanRecordSchema = objectSchema(
1353
1343
  uniqueItems: true
1354
1344
  }),
1355
1345
  provenance: opensteerRegistryProvenanceSchema,
1356
- lifecycle: opensteerRequestPlanLifecycleSchema,
1357
1346
  freshness: opensteerRequestPlanFreshnessSchema,
1358
1347
  payload: opensteerRequestPlanPayloadSchema
1359
1348
  },
1360
1349
  {
1361
1350
  title: "OpensteerRequestPlanRecord",
1362
- required: [
1363
- "id",
1364
- "key",
1365
- "version",
1366
- "createdAt",
1367
- "updatedAt",
1368
- "contentHash",
1369
- "tags",
1370
- "lifecycle",
1371
- "payload"
1372
- ]
1351
+ required: ["id", "key", "version", "createdAt", "updatedAt", "contentHash", "tags", "payload"]
1373
1352
  }
1374
1353
  );
1375
1354
  var jsonValueSchema = defineSchema({
@@ -1503,8 +1482,7 @@ var opensteerRecipeStepSchema = oneOfSchema(
1503
1482
  objectSchema(
1504
1483
  {
1505
1484
  kind: enumSchema(["goto"]),
1506
- url: stringSchema({ minLength: 1 }),
1507
- networkTag: stringSchema({ minLength: 1 })
1485
+ url: stringSchema({ minLength: 1 })
1508
1486
  },
1509
1487
  {
1510
1488
  title: "OpensteerAuthRecipeGotoStep",
@@ -1513,8 +1491,7 @@ var opensteerRecipeStepSchema = oneOfSchema(
1513
1491
  ),
1514
1492
  objectSchema(
1515
1493
  {
1516
- kind: enumSchema(["reload"]),
1517
- networkTag: stringSchema({ minLength: 1 })
1494
+ kind: enumSchema(["reload"])
1518
1495
  },
1519
1496
  {
1520
1497
  title: "OpensteerAuthRecipeReloadStep",
@@ -1728,13 +1705,10 @@ var opensteerRecipeRecordSchema = objectSchema(
1728
1705
  var opensteerAuthRecipeRecordSchema = opensteerRecipeRecordSchema;
1729
1706
  var opensteerNetworkQueryInputSchema = objectSchema(
1730
1707
  {
1731
- source: enumSchema(["live", "saved"], {
1732
- title: "OpensteerNetworkQuerySource"
1733
- }),
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 }),
@@ -1758,12 +1732,12 @@ var opensteerNetworkQueryOutputSchema = objectSchema(
1758
1732
  required: ["records"]
1759
1733
  }
1760
1734
  );
1761
- var opensteerNetworkSaveInputSchema = objectSchema(
1735
+ var opensteerNetworkTagInputSchema = objectSchema(
1762
1736
  {
1763
1737
  pageRef: pageRefSchema,
1764
1738
  recordId: stringSchema({ minLength: 1 }),
1765
1739
  requestId: stringSchema({ minLength: 1 }),
1766
- actionId: stringSchema({ minLength: 1 }),
1740
+ capture: stringSchema({ minLength: 1 }),
1767
1741
  tag: stringSchema({ minLength: 1 }),
1768
1742
  url: stringSchema({ minLength: 1 }),
1769
1743
  hostname: stringSchema({ minLength: 1 }),
@@ -1773,21 +1747,22 @@ var opensteerNetworkSaveInputSchema = objectSchema(
1773
1747
  resourceType: networkResourceTypeSchema
1774
1748
  },
1775
1749
  {
1776
- title: "OpensteerNetworkSaveInput",
1750
+ title: "OpensteerNetworkTagInput",
1777
1751
  required: ["tag"]
1778
1752
  }
1779
1753
  );
1780
- var opensteerNetworkSaveOutputSchema = objectSchema(
1754
+ var opensteerNetworkTagOutputSchema = objectSchema(
1781
1755
  {
1782
- savedCount: integerSchema({ minimum: 0 })
1756
+ taggedCount: integerSchema({ minimum: 0 })
1783
1757
  },
1784
1758
  {
1785
- title: "OpensteerNetworkSaveOutput",
1786
- required: ["savedCount"]
1759
+ title: "OpensteerNetworkTagOutput",
1760
+ required: ["taggedCount"]
1787
1761
  }
1788
1762
  );
1789
1763
  var opensteerNetworkClearInputSchema = objectSchema(
1790
1764
  {
1765
+ capture: stringSchema({ minLength: 1 }),
1791
1766
  tag: stringSchema({ minLength: 1 })
1792
1767
  },
1793
1768
  {
@@ -1812,7 +1787,6 @@ var opensteerWriteRequestPlanInputSchema = objectSchema(
1812
1787
  uniqueItems: true
1813
1788
  }),
1814
1789
  provenance: opensteerRegistryProvenanceSchema,
1815
- lifecycle: opensteerRequestPlanLifecycleSchema,
1816
1790
  freshness: opensteerRequestPlanFreshnessSchema,
1817
1791
  payload: opensteerRequestPlanPayloadSchema
1818
1792
  },
@@ -2053,7 +2027,7 @@ var opensteerInferRequestPlanInputSchema = objectSchema(
2053
2027
  recordId: stringSchema({ minLength: 1 }),
2054
2028
  key: stringSchema({ minLength: 1 }),
2055
2029
  version: stringSchema({ minLength: 1 }),
2056
- lifecycle: opensteerRequestPlanLifecycleSchema
2030
+ transport: transportKindSchema
2057
2031
  },
2058
2032
  {
2059
2033
  title: "OpensteerInferRequestPlanInput",
@@ -5246,7 +5220,7 @@ var opensteerSemanticOperationNames = [
5246
5220
  "dom.scroll",
5247
5221
  "dom.extract",
5248
5222
  "network.query",
5249
- "network.save",
5223
+ "network.tag",
5250
5224
  "network.clear",
5251
5225
  "network.minimize",
5252
5226
  "network.diff",
@@ -5307,7 +5281,7 @@ var opensteerPackageRunnableSemanticOperationNames = /* @__PURE__ */ new Set([
5307
5281
  "dom.scroll",
5308
5282
  "dom.extract",
5309
5283
  "network.query",
5310
- "network.save",
5284
+ "network.tag",
5311
5285
  "network.clear",
5312
5286
  "network.minimize",
5313
5287
  "network.diff",
@@ -5627,7 +5601,7 @@ var opensteerPageCloseOutputSchema = objectSchema(
5627
5601
  var opensteerPageGotoInputSchema = objectSchema(
5628
5602
  {
5629
5603
  url: stringSchema(),
5630
- networkTag: stringSchema({ minLength: 1 })
5604
+ captureNetwork: stringSchema({ minLength: 1 })
5631
5605
  },
5632
5606
  {
5633
5607
  title: "OpensteerPageGotoInput",
@@ -5778,7 +5752,7 @@ var opensteerDomClickInputSchema = objectSchema(
5778
5752
  {
5779
5753
  target: opensteerTargetInputSchema,
5780
5754
  persistAsDescription: stringSchema(),
5781
- networkTag: stringSchema({ minLength: 1 })
5755
+ captureNetwork: stringSchema({ minLength: 1 })
5782
5756
  },
5783
5757
  {
5784
5758
  title: "OpensteerDomClickInput",
@@ -5789,7 +5763,7 @@ var opensteerDomHoverInputSchema = objectSchema(
5789
5763
  {
5790
5764
  target: opensteerTargetInputSchema,
5791
5765
  persistAsDescription: stringSchema(),
5792
- networkTag: stringSchema({ minLength: 1 })
5766
+ captureNetwork: stringSchema({ minLength: 1 })
5793
5767
  },
5794
5768
  {
5795
5769
  title: "OpensteerDomHoverInput",
@@ -5802,7 +5776,7 @@ var opensteerDomInputInputSchema = objectSchema(
5802
5776
  text: stringSchema(),
5803
5777
  pressEnter: { type: "boolean" },
5804
5778
  persistAsDescription: stringSchema(),
5805
- networkTag: stringSchema({ minLength: 1 })
5779
+ captureNetwork: stringSchema({ minLength: 1 })
5806
5780
  },
5807
5781
  {
5808
5782
  title: "OpensteerDomInputInput",
@@ -5815,7 +5789,7 @@ var opensteerDomScrollInputSchema = objectSchema(
5815
5789
  direction: enumSchema(["up", "down", "left", "right"]),
5816
5790
  amount: integerSchema({ minimum: 1 }),
5817
5791
  persistAsDescription: stringSchema(),
5818
- networkTag: stringSchema({ minLength: 1 })
5792
+ captureNetwork: stringSchema({ minLength: 1 })
5819
5793
  },
5820
5794
  {
5821
5795
  title: "OpensteerDomScrollInput",
@@ -6016,7 +5990,7 @@ var opensteerComputerExecuteInputSchema = objectSchema(
6016
5990
  {
6017
5991
  action: opensteerComputerActionSchema,
6018
5992
  screenshot: opensteerComputerScreenshotOptionsSchema,
6019
- networkTag: stringSchema({ minLength: 1 })
5993
+ captureNetwork: stringSchema({ minLength: 1 })
6020
5994
  },
6021
5995
  {
6022
5996
  title: "OpensteerComputerExecuteInput",
@@ -6217,18 +6191,17 @@ var opensteerSemanticOperationSpecificationsBase = [
6217
6191
  }),
6218
6192
  defineSemanticOperationSpec({
6219
6193
  name: "network.query",
6220
- description: "Query live or saved network records for reverse engineering workflows.",
6194
+ description: "Query persisted network records for reverse engineering workflows.",
6221
6195
  inputSchema: opensteerNetworkQueryInputSchema,
6222
6196
  outputSchema: opensteerNetworkQueryOutputSchema,
6223
- requiredCapabilities: [],
6224
- resolveRequiredCapabilities: (input) => input.source === "saved" ? [] : input.includeBodies === true ? ["inspect.network", "inspect.networkBodies"] : ["inspect.network"]
6197
+ requiredCapabilities: []
6225
6198
  }),
6226
6199
  defineSemanticOperationSpec({
6227
- name: "network.save",
6228
- description: "Persist filtered live network records into the saved network registry under a tag.",
6229
- inputSchema: opensteerNetworkSaveInputSchema,
6230
- outputSchema: opensteerNetworkSaveOutputSchema,
6231
- requiredCapabilities: ["inspect.network"]
6200
+ name: "network.tag",
6201
+ description: "Apply a tag to persisted network records matching the provided filters.",
6202
+ inputSchema: opensteerNetworkTagInputSchema,
6203
+ outputSchema: opensteerNetworkTagOutputSchema,
6204
+ requiredCapabilities: []
6232
6205
  }),
6233
6206
  defineSemanticOperationSpec({
6234
6207
  name: "network.clear",
@@ -7049,6 +7022,49 @@ function mediaTypeExtension(mediaType) {
7049
7022
  }
7050
7023
  }
7051
7024
 
7025
+ // src/action-boundary.ts
7026
+ var actionBoundaryDiagnosticsBySignal = /* @__PURE__ */ new WeakMap();
7027
+ async function captureActionBoundarySnapshot(engine, pageRef) {
7028
+ const frames = await engine.listFrames({ pageRef });
7029
+ const mainFrame = frames.find((frame) => frame.isMainFrame);
7030
+ if (!mainFrame) {
7031
+ throw new Error(`page ${pageRef} does not expose a main frame`);
7032
+ }
7033
+ return {
7034
+ pageRef,
7035
+ documentRef: mainFrame.documentRef
7036
+ };
7037
+ }
7038
+ function createActionBoundaryDiagnostics(input) {
7039
+ return {
7040
+ trigger: input.boundary.trigger,
7041
+ crossDocument: input.boundary.crossDocument,
7042
+ bootstrapSettled: input.boundary.bootstrapSettled,
7043
+ visualSettled: input.visualSettled,
7044
+ ...input.boundary.timedOutPhase !== void 0 ? { timedOutPhase: input.boundary.timedOutPhase } : !input.visualSettled ? { timedOutPhase: "visual" } : {}
7045
+ };
7046
+ }
7047
+ function recordActionBoundaryDiagnostics(signal, diagnostics) {
7048
+ actionBoundaryDiagnosticsBySignal.set(signal, diagnostics);
7049
+ }
7050
+ function takeActionBoundaryDiagnostics(signal) {
7051
+ const diagnostics = actionBoundaryDiagnosticsBySignal.get(signal);
7052
+ actionBoundaryDiagnosticsBySignal.delete(signal);
7053
+ return diagnostics;
7054
+ }
7055
+ function isSoftSettleTimeoutError(error, signal) {
7056
+ if (isTimeoutError(error)) {
7057
+ return true;
7058
+ }
7059
+ return signal?.aborted === true && isTimeoutError(signal.reason) && (error === signal.reason || isAbortError(error));
7060
+ }
7061
+ function isAbortError(error) {
7062
+ return error instanceof Error && error.name === "AbortError";
7063
+ }
7064
+ function isTimeoutError(error) {
7065
+ return isOpensteerProtocolError(error) && error.code === "timeout";
7066
+ }
7067
+
7052
7068
  // src/internal/errors.ts
7053
7069
  function normalizeThrownOpensteerError(error, fallbackMessage) {
7054
7070
  if (isOpensteerProtocolError(error)) {
@@ -7081,13 +7097,13 @@ var DEFAULT_TIMEOUTS = {
7081
7097
  "page.add-init-script": 1e4,
7082
7098
  "page.snapshot": 15e3,
7083
7099
  "computer.execute": 3e4,
7084
- "dom.click": 1e4,
7100
+ "dom.click": 3e4,
7085
7101
  "dom.hover": 1e4,
7086
- "dom.input": 1e4,
7102
+ "dom.input": 3e4,
7087
7103
  "dom.scroll": 1e4,
7088
7104
  "dom.extract": 15e3,
7089
7105
  "network.query": 15e3,
7090
- "network.save": 15e3,
7106
+ "network.tag": 15e3,
7091
7107
  "network.clear": 1e4,
7092
7108
  "scripts.capture": 15e3,
7093
7109
  "request.raw": 3e4,
@@ -7118,8 +7134,74 @@ var defaultSnapshotSettleObserver = {
7118
7134
  }
7119
7135
  };
7120
7136
  Object.freeze(defaultSnapshotSettleObserver);
7137
+ var DOM_ACTION_VISUAL_STABILITY_PROFILES = {
7138
+ "dom.click": { settleMs: 750, scope: "visible-frames", timeoutMs: 7e3 },
7139
+ "dom.input": { settleMs: 750, scope: "visible-frames", timeoutMs: 7e3 },
7140
+ "dom.scroll": { settleMs: 600, scope: "visible-frames", timeoutMs: 7e3 },
7141
+ "dom.hover": { settleMs: 200, scope: "main-frame", timeoutMs: 2500 }
7142
+ };
7143
+ var DEFAULT_DOM_ACTION_VISUAL_STABILITY_PROFILE = {
7144
+ settleMs: 750,
7145
+ scope: "visible-frames",
7146
+ timeoutMs: 7e3
7147
+ };
7148
+ var NAVIGATION_VISUAL_STABILITY_PROFILE = {
7149
+ settleMs: 750,
7150
+ scope: "visible-frames",
7151
+ timeoutMs: 7e3
7152
+ };
7153
+ var defaultDomActionSettleObserver = {
7154
+ async settle(input) {
7155
+ if (input.trigger !== "dom-action") {
7156
+ return false;
7157
+ }
7158
+ const profile = DOM_ACTION_VISUAL_STABILITY_PROFILES[input.operation] ?? DEFAULT_DOM_ACTION_VISUAL_STABILITY_PROFILE;
7159
+ const effectiveTimeout = input.remainingMs === void 0 ? profile.timeoutMs : Math.min(profile.timeoutMs, input.remainingMs);
7160
+ if (effectiveTimeout <= 0) {
7161
+ return false;
7162
+ }
7163
+ try {
7164
+ await input.engine.waitForVisualStability({
7165
+ pageRef: input.pageRef,
7166
+ timeoutMs: effectiveTimeout,
7167
+ settleMs: profile.settleMs,
7168
+ scope: profile.scope
7169
+ });
7170
+ return true;
7171
+ } catch {
7172
+ return false;
7173
+ }
7174
+ }
7175
+ };
7176
+ Object.freeze(defaultDomActionSettleObserver);
7177
+ var defaultNavigationSettleObserver = {
7178
+ async settle(input) {
7179
+ if (input.trigger !== "navigation") {
7180
+ return false;
7181
+ }
7182
+ const profile = NAVIGATION_VISUAL_STABILITY_PROFILE;
7183
+ const effectiveTimeout = input.remainingMs === void 0 ? profile.timeoutMs : Math.min(profile.timeoutMs, input.remainingMs);
7184
+ if (effectiveTimeout <= 0) {
7185
+ return false;
7186
+ }
7187
+ try {
7188
+ await input.engine.waitForVisualStability({
7189
+ pageRef: input.pageRef,
7190
+ timeoutMs: effectiveTimeout,
7191
+ settleMs: profile.settleMs,
7192
+ scope: profile.scope
7193
+ });
7194
+ return true;
7195
+ } catch {
7196
+ return false;
7197
+ }
7198
+ }
7199
+ };
7200
+ Object.freeze(defaultNavigationSettleObserver);
7121
7201
  var DEFAULT_SETTLE_OBSERVERS = Object.freeze([
7122
- defaultSnapshotSettleObserver
7202
+ defaultSnapshotSettleObserver,
7203
+ defaultDomActionSettleObserver,
7204
+ defaultNavigationSettleObserver
7123
7205
  ]);
7124
7206
  var defaultTimeoutPolicy = {
7125
7207
  resolveTimeoutMs(input) {
@@ -7370,9 +7452,7 @@ var FilesystemRegistryStore = class {
7370
7452
  if (input.version !== void 0) {
7371
7453
  return this.resolveIndexedRecord(key, normalizeNonEmptyString("version", input.version));
7372
7454
  }
7373
- const matches = (await this.readAllRecords()).filter(
7374
- (record) => this.isActive(record) && record.key === key
7375
- );
7455
+ const matches = (await this.readAllRecords()).filter((record) => record.key === key);
7376
7456
  matches.sort(compareByCreatedAtAndId);
7377
7457
  return matches[0];
7378
7458
  }
@@ -7495,8 +7575,10 @@ var FilesystemDescriptorRegistry = class extends FilesystemRegistryStore {
7495
7575
  };
7496
7576
  return this.writeRecord(record);
7497
7577
  }
7498
- isActive(_record) {
7499
- return true;
7578
+ async list(input = {}) {
7579
+ const key = input.key === void 0 ? void 0 : normalizeNonEmptyString("key", input.key);
7580
+ const records = await this.readAllRecords();
7581
+ return key === void 0 ? records : records.filter((record) => record.key === key);
7500
7582
  }
7501
7583
  };
7502
7584
  var FilesystemRequestPlanRegistry = class extends FilesystemRegistryStore {
@@ -7526,7 +7608,6 @@ var FilesystemRequestPlanRegistry = class extends FilesystemRegistryStore {
7526
7608
  tags: normalizeTags(input.tags),
7527
7609
  ...provenance === void 0 ? {} : { provenance },
7528
7610
  payload,
7529
- lifecycle: input.lifecycle ?? "active",
7530
7611
  ...freshness === void 0 ? {} : { freshness }
7531
7612
  };
7532
7613
  return this.writeRecord(record);
@@ -7536,7 +7617,7 @@ var FilesystemRequestPlanRegistry = class extends FilesystemRegistryStore {
7536
7617
  const records = await this.readAllRecords();
7537
7618
  return key === void 0 ? records : records.filter((record) => record.key === key);
7538
7619
  }
7539
- async updateMetadata(input) {
7620
+ async updateFreshness(input) {
7540
7621
  const id = normalizeNonEmptyString("id", input.id);
7541
7622
  return withFilesystemLock(this.writeLockPath(), async () => {
7542
7623
  const existing = await this.getById(id);
@@ -7554,16 +7635,12 @@ var FilesystemRequestPlanRegistry = class extends FilesystemRegistryStore {
7554
7635
  const nextRecord = {
7555
7636
  ...existing,
7556
7637
  updatedAt: nextUpdatedAt,
7557
- lifecycle: input.lifecycle ?? existing.lifecycle,
7558
7638
  ...nextFreshness === void 0 ? {} : { freshness: nextFreshness }
7559
7639
  };
7560
7640
  await writeJsonFileAtomic(this.recordPath(id), nextRecord);
7561
7641
  return nextRecord;
7562
7642
  });
7563
7643
  }
7564
- isActive(record) {
7565
- return record.lifecycle === "active";
7566
- }
7567
7644
  };
7568
7645
  var FilesystemAuthRecipeRegistry = class extends FilesystemRegistryStore {
7569
7646
  constructor(rootPath) {
@@ -7599,9 +7676,6 @@ var FilesystemAuthRecipeRegistry = class extends FilesystemRegistryStore {
7599
7676
  const records = await this.readAllRecords();
7600
7677
  return key === void 0 ? records : records.filter((record) => record.key === key);
7601
7678
  }
7602
- isActive(_record) {
7603
- return true;
7604
- }
7605
7679
  };
7606
7680
  var FilesystemRecipeRegistry = class extends FilesystemRegistryStore {
7607
7681
  constructor(rootPath) {
@@ -7637,9 +7711,6 @@ var FilesystemRecipeRegistry = class extends FilesystemRegistryStore {
7637
7711
  const records = await this.readAllRecords();
7638
7712
  return key === void 0 ? records : records.filter((record) => record.key === key);
7639
7713
  }
7640
- isActive(_record) {
7641
- return true;
7642
- }
7643
7714
  };
7644
7715
  var FilesystemInteractionTraceRegistry = class extends FilesystemRegistryStore {
7645
7716
  constructor(rootPath) {
@@ -7675,9 +7746,6 @@ var FilesystemInteractionTraceRegistry = class extends FilesystemRegistryStore {
7675
7746
  const records = await this.readAllRecords();
7676
7747
  return key === void 0 ? records : records.filter((record) => record.key === key);
7677
7748
  }
7678
- isActive(_record) {
7679
- return true;
7680
- }
7681
7749
  };
7682
7750
  var FilesystemReverseCaseRegistry = class extends FilesystemRegistryStore {
7683
7751
  constructor(rootPath) {
@@ -7741,9 +7809,6 @@ var FilesystemReverseCaseRegistry = class extends FilesystemRegistryStore {
7741
7809
  return nextRecord;
7742
7810
  });
7743
7811
  }
7744
- isActive(_record) {
7745
- return true;
7746
- }
7747
7812
  };
7748
7813
  var FilesystemReversePackageRegistry = class extends FilesystemRegistryStore {
7749
7814
  constructor(rootPath) {
@@ -7779,9 +7844,6 @@ var FilesystemReversePackageRegistry = class extends FilesystemRegistryStore {
7779
7844
  const records = await this.readAllRecords();
7780
7845
  return key === void 0 ? records : records.filter((record) => record.key === key);
7781
7846
  }
7782
- isActive(_record) {
7783
- return true;
7784
- }
7785
7847
  };
7786
7848
  var FilesystemReverseReportRegistry = class extends FilesystemRegistryStore {
7787
7849
  constructor(rootPath) {
@@ -7817,9 +7879,6 @@ var FilesystemReverseReportRegistry = class extends FilesystemRegistryStore {
7817
7879
  const records = await this.readAllRecords();
7818
7880
  return key === void 0 ? records : records.filter((record) => record.key === key);
7819
7881
  }
7820
- isActive(_record) {
7821
- return true;
7822
- }
7823
7882
  };
7824
7883
  function createDescriptorRegistry(rootPath) {
7825
7884
  return new FilesystemDescriptorRegistry(rootPath);
@@ -7847,101 +7906,20 @@ function createReverseReportRegistry(rootPath) {
7847
7906
  }
7848
7907
  var TAG_DELIMITER = "";
7849
7908
  var NODE_SQLITE_SPECIFIER = `node:${"sqlite"}`;
7909
+ var SAVED_NETWORK_SQLITE_SUPPORT_ERROR = "Saved-network operations require Node's built-in SQLite support. Use a Node runtime with node:sqlite enabled.";
7850
7910
  var SqliteSavedNetworkStore = class {
7851
7911
  databasePath;
7852
7912
  database;
7913
+ directoryInitialization;
7914
+ databaseInitialization;
7853
7915
  constructor(rootPath) {
7854
7916
  this.databasePath = path6__default.default.join(rootPath, "registry", "saved-network.sqlite");
7855
7917
  }
7856
7918
  async initialize() {
7857
- await ensureDirectory(path6__default.default.dirname(this.databasePath));
7858
- const { DatabaseSync } = await import(NODE_SQLITE_SPECIFIER);
7859
- const database = new DatabaseSync(this.databasePath);
7860
- database.exec("PRAGMA journal_mode = WAL");
7861
- database.exec("PRAGMA foreign_keys = ON");
7862
- database.exec(`
7863
- CREATE TABLE IF NOT EXISTS saved_network_records (
7864
- record_id TEXT PRIMARY KEY,
7865
- request_id TEXT NOT NULL,
7866
- session_ref TEXT NOT NULL,
7867
- page_ref TEXT,
7868
- page_ref_key TEXT NOT NULL,
7869
- frame_ref TEXT,
7870
- document_ref TEXT,
7871
- action_id TEXT,
7872
- method TEXT NOT NULL,
7873
- method_lc TEXT NOT NULL,
7874
- url TEXT NOT NULL,
7875
- url_lc TEXT NOT NULL,
7876
- hostname TEXT NOT NULL,
7877
- hostname_lc TEXT NOT NULL,
7878
- path TEXT NOT NULL,
7879
- path_lc TEXT NOT NULL,
7880
- status INTEGER,
7881
- status_text TEXT,
7882
- resource_type TEXT NOT NULL,
7883
- navigation_request INTEGER NOT NULL,
7884
- request_headers_json TEXT NOT NULL,
7885
- response_headers_json TEXT NOT NULL,
7886
- request_body_json TEXT,
7887
- response_body_json TEXT,
7888
- initiator_json TEXT,
7889
- timing_json TEXT,
7890
- transfer_json TEXT,
7891
- source_json TEXT,
7892
- capture_state TEXT NOT NULL,
7893
- request_body_state TEXT NOT NULL,
7894
- response_body_state TEXT NOT NULL,
7895
- request_body_skip_reason TEXT,
7896
- response_body_skip_reason TEXT,
7897
- request_body_error TEXT,
7898
- response_body_error TEXT,
7899
- redirect_from_request_id TEXT,
7900
- redirect_to_request_id TEXT,
7901
- saved_at INTEGER NOT NULL
7902
- );
7903
-
7904
- CREATE UNIQUE INDEX IF NOT EXISTS saved_network_records_scope_request
7905
- ON saved_network_records (session_ref, page_ref_key, request_id);
7906
-
7907
- CREATE INDEX IF NOT EXISTS saved_network_records_saved_at
7908
- ON saved_network_records (saved_at DESC);
7909
-
7910
- CREATE TABLE IF NOT EXISTS saved_network_tags (
7911
- record_id TEXT NOT NULL REFERENCES saved_network_records(record_id) ON DELETE CASCADE,
7912
- tag TEXT NOT NULL,
7913
- PRIMARY KEY (record_id, tag)
7914
- );
7915
-
7916
- CREATE INDEX IF NOT EXISTS saved_network_tags_tag
7917
- ON saved_network_tags (tag);
7918
- `);
7919
- this.ensureColumn(
7920
- database,
7921
- "saved_network_records",
7922
- "capture_state",
7923
- "TEXT NOT NULL DEFAULT 'complete'"
7924
- );
7925
- this.ensureColumn(
7926
- database,
7927
- "saved_network_records",
7928
- "request_body_state",
7929
- "TEXT NOT NULL DEFAULT 'skipped'"
7930
- );
7931
- this.ensureColumn(
7932
- database,
7933
- "saved_network_records",
7934
- "response_body_state",
7935
- "TEXT NOT NULL DEFAULT 'skipped'"
7936
- );
7937
- this.ensureColumn(database, "saved_network_records", "request_body_skip_reason", "TEXT");
7938
- this.ensureColumn(database, "saved_network_records", "response_body_skip_reason", "TEXT");
7939
- this.ensureColumn(database, "saved_network_records", "request_body_error", "TEXT");
7940
- this.ensureColumn(database, "saved_network_records", "response_body_error", "TEXT");
7941
- this.database = database;
7919
+ await this.ensureDatabaseDirectory();
7942
7920
  }
7943
- async save(records, tag) {
7944
- const database = this.requireDatabase();
7921
+ async save(records, options) {
7922
+ const database = await this.requireDatabase();
7945
7923
  const readExisting = database.prepare(`
7946
7924
  SELECT record_id
7947
7925
  FROM saved_network_records
@@ -7949,123 +7927,7 @@ var SqliteSavedNetworkStore = class {
7949
7927
  AND page_ref_key = @page_ref_key
7950
7928
  AND request_id = @request_id
7951
7929
  `);
7952
- const upsertRecord = database.prepare(`
7953
- INSERT INTO saved_network_records (
7954
- record_id,
7955
- request_id,
7956
- session_ref,
7957
- page_ref,
7958
- page_ref_key,
7959
- frame_ref,
7960
- document_ref,
7961
- action_id,
7962
- method,
7963
- method_lc,
7964
- url,
7965
- url_lc,
7966
- hostname,
7967
- hostname_lc,
7968
- path,
7969
- path_lc,
7970
- status,
7971
- status_text,
7972
- resource_type,
7973
- navigation_request,
7974
- request_headers_json,
7975
- response_headers_json,
7976
- request_body_json,
7977
- response_body_json,
7978
- initiator_json,
7979
- timing_json,
7980
- transfer_json,
7981
- source_json,
7982
- capture_state,
7983
- request_body_state,
7984
- response_body_state,
7985
- request_body_skip_reason,
7986
- response_body_skip_reason,
7987
- request_body_error,
7988
- response_body_error,
7989
- redirect_from_request_id,
7990
- redirect_to_request_id,
7991
- saved_at
7992
- ) VALUES (
7993
- @record_id,
7994
- @request_id,
7995
- @session_ref,
7996
- @page_ref,
7997
- @page_ref_key,
7998
- @frame_ref,
7999
- @document_ref,
8000
- @action_id,
8001
- @method,
8002
- @method_lc,
8003
- @url,
8004
- @url_lc,
8005
- @hostname,
8006
- @hostname_lc,
8007
- @path,
8008
- @path_lc,
8009
- @status,
8010
- @status_text,
8011
- @resource_type,
8012
- @navigation_request,
8013
- @request_headers_json,
8014
- @response_headers_json,
8015
- @request_body_json,
8016
- @response_body_json,
8017
- @initiator_json,
8018
- @timing_json,
8019
- @transfer_json,
8020
- @source_json,
8021
- @capture_state,
8022
- @request_body_state,
8023
- @response_body_state,
8024
- @request_body_skip_reason,
8025
- @response_body_skip_reason,
8026
- @request_body_error,
8027
- @response_body_error,
8028
- @redirect_from_request_id,
8029
- @redirect_to_request_id,
8030
- @saved_at
8031
- )
8032
- ON CONFLICT(record_id) DO UPDATE SET
8033
- page_ref = excluded.page_ref,
8034
- page_ref_key = excluded.page_ref_key,
8035
- frame_ref = excluded.frame_ref,
8036
- document_ref = excluded.document_ref,
8037
- action_id = excluded.action_id,
8038
- method = excluded.method,
8039
- method_lc = excluded.method_lc,
8040
- url = excluded.url,
8041
- url_lc = excluded.url_lc,
8042
- hostname = excluded.hostname,
8043
- hostname_lc = excluded.hostname_lc,
8044
- path = excluded.path,
8045
- path_lc = excluded.path_lc,
8046
- status = excluded.status,
8047
- status_text = excluded.status_text,
8048
- resource_type = excluded.resource_type,
8049
- navigation_request = excluded.navigation_request,
8050
- request_headers_json = excluded.request_headers_json,
8051
- response_headers_json = excluded.response_headers_json,
8052
- request_body_json = excluded.request_body_json,
8053
- response_body_json = excluded.response_body_json,
8054
- initiator_json = excluded.initiator_json,
8055
- timing_json = excluded.timing_json,
8056
- transfer_json = excluded.transfer_json,
8057
- source_json = excluded.source_json,
8058
- capture_state = excluded.capture_state,
8059
- request_body_state = excluded.request_body_state,
8060
- response_body_state = excluded.response_body_state,
8061
- request_body_skip_reason = excluded.request_body_skip_reason,
8062
- response_body_skip_reason = excluded.response_body_skip_reason,
8063
- request_body_error = excluded.request_body_error,
8064
- response_body_error = excluded.response_body_error,
8065
- redirect_from_request_id = excluded.redirect_from_request_id,
8066
- redirect_to_request_id = excluded.redirect_to_request_id,
8067
- saved_at = excluded.saved_at
8068
- `);
7930
+ const upsertRecord = database.prepare(buildSavedNetworkUpsertSql(options.bodyWriteMode));
8069
7931
  const insertTag = database.prepare(`
8070
7932
  INSERT OR IGNORE INTO saved_network_tags (record_id, tag)
8071
7933
  VALUES (@record_id, @tag)
@@ -8089,7 +7951,7 @@ var SqliteSavedNetworkStore = class {
8089
7951
  page_ref_key: pageRefKey,
8090
7952
  frame_ref: entry.record.frameRef ?? null,
8091
7953
  document_ref: entry.record.documentRef ?? null,
8092
- action_id: entry.actionId ?? null,
7954
+ capture: entry.capture ?? null,
8093
7955
  method: entry.record.method,
8094
7956
  method_lc: entry.record.method.toLowerCase(),
8095
7957
  url: entry.record.url,
@@ -8121,10 +7983,14 @@ var SqliteSavedNetworkStore = class {
8121
7983
  redirect_to_request_id: entry.record.redirectToRequestId ?? null,
8122
7984
  saved_at: entry.savedAt ?? Date.now()
8123
7985
  });
8124
- if (tag !== void 0) {
7986
+ const tags = new Set(entry.tags ?? []);
7987
+ if (options.tag !== void 0) {
7988
+ tags.add(options.tag);
7989
+ }
7990
+ for (const currentTag of tags) {
8125
7991
  const result = insertTag.run({
8126
7992
  record_id: recordId,
8127
- tag
7993
+ tag: currentTag
8128
7994
  });
8129
7995
  savedCount += result.changes ?? 0;
8130
7996
  }
@@ -8132,8 +7998,41 @@ var SqliteSavedNetworkStore = class {
8132
7998
  return savedCount;
8133
7999
  });
8134
8000
  }
8001
+ async tagByFilter(filter, tag) {
8002
+ const database = await this.requireDatabase();
8003
+ const { whereSql, parameters } = buildSavedNetworkWhere(filter);
8004
+ const selectRecords = database.prepare(
8005
+ `
8006
+ SELECT r.record_id
8007
+ FROM saved_network_records r
8008
+ ${whereSql}
8009
+ `
8010
+ );
8011
+ const insertTag = database.prepare(`
8012
+ INSERT OR IGNORE INTO saved_network_tags (record_id, tag)
8013
+ VALUES (@record_id, @tag)
8014
+ `);
8015
+ return withSqliteTransaction(database, () => {
8016
+ let taggedCount = 0;
8017
+ const rows = selectRecords.all(
8018
+ ...parameters
8019
+ );
8020
+ for (const row of rows) {
8021
+ const recordId = row.record_id;
8022
+ if (typeof recordId !== "string") {
8023
+ continue;
8024
+ }
8025
+ const result = insertTag.run({
8026
+ record_id: recordId,
8027
+ tag
8028
+ });
8029
+ taggedCount += result.changes ?? 0;
8030
+ }
8031
+ return taggedCount;
8032
+ });
8033
+ }
8135
8034
  async query(input = {}) {
8136
- const database = this.requireDatabase();
8035
+ const database = await this.requireDatabase();
8137
8036
  const limit = Math.max(1, Math.min(input.limit ?? 50, 200));
8138
8037
  const { whereSql, parameters } = buildSavedNetworkWhere(input);
8139
8038
  const rows = database.prepare(
@@ -8164,48 +8063,160 @@ var SqliteSavedNetworkStore = class {
8164
8063
  return record;
8165
8064
  }
8166
8065
  async clear(input = {}) {
8167
- const database = this.requireDatabase();
8168
- const countAll = database.prepare(`
8169
- SELECT COUNT(*) AS cleared
8170
- FROM saved_network_records
8171
- `);
8172
- const countByTag = database.prepare(`
8173
- SELECT COUNT(DISTINCT record_id) AS cleared
8174
- FROM saved_network_tags
8175
- WHERE tag = @tag
8176
- `);
8177
- const deleteAllTags = database.prepare(`DELETE FROM saved_network_tags`);
8066
+ const database = await this.requireDatabase();
8067
+ const countAll = database.prepare(`SELECT COUNT(*) AS cleared FROM saved_network_records`);
8178
8068
  const deleteAllRecords = database.prepare(`DELETE FROM saved_network_records`);
8179
- const deleteTag = database.prepare(`
8180
- DELETE FROM saved_network_tags
8181
- WHERE tag = @tag
8069
+ const { whereSql, parameters } = buildSavedNetworkWhere(input);
8070
+ const countFiltered = database.prepare(`
8071
+ SELECT COUNT(*) AS cleared
8072
+ FROM saved_network_records r
8073
+ ${whereSql}
8182
8074
  `);
8183
- const deleteOrphans = database.prepare(`
8075
+ const deleteFiltered = database.prepare(`
8184
8076
  DELETE FROM saved_network_records
8185
- WHERE NOT EXISTS (
8186
- SELECT 1
8187
- FROM saved_network_tags t
8188
- WHERE t.record_id = saved_network_records.record_id
8077
+ WHERE record_id IN (
8078
+ SELECT r.record_id
8079
+ FROM saved_network_records r
8080
+ ${whereSql}
8189
8081
  )
8190
8082
  `);
8191
8083
  return withSqliteTransaction(database, () => {
8192
- const tag = input.tag;
8193
- const cleared = tag === void 0 ? countAll.get().cleared : countByTag.get({ tag }).cleared;
8194
- if (tag === void 0) {
8195
- deleteAllTags.run();
8084
+ if (input.capture === void 0 && input.tag === void 0) {
8085
+ const cleared2 = countAll.get().cleared;
8196
8086
  deleteAllRecords.run();
8197
- return cleared;
8087
+ return cleared2;
8198
8088
  }
8199
- deleteTag.run({ tag });
8200
- deleteOrphans.run();
8089
+ const args = parameters;
8090
+ const cleared = countFiltered.get(...args).cleared;
8091
+ deleteFiltered.run(...args);
8201
8092
  return cleared;
8202
8093
  });
8203
8094
  }
8204
- requireDatabase() {
8205
- if (!this.database) {
8206
- throw new Error("saved network store is not initialized");
8095
+ async requireDatabase() {
8096
+ if (this.database) {
8097
+ return this.database;
8098
+ }
8099
+ this.databaseInitialization ??= this.openDatabase();
8100
+ try {
8101
+ return await this.databaseInitialization;
8102
+ } catch (error) {
8103
+ this.databaseInitialization = void 0;
8104
+ throw error;
8105
+ }
8106
+ }
8107
+ async openDatabase() {
8108
+ await this.ensureDatabaseDirectory();
8109
+ let DatabaseSync;
8110
+ try {
8111
+ ({ DatabaseSync } = await import(NODE_SQLITE_SPECIFIER));
8112
+ } catch (error) {
8113
+ throw normalizeSqliteImportError(error);
8207
8114
  }
8208
- return this.database;
8115
+ const database = new DatabaseSync(this.databasePath);
8116
+ try {
8117
+ this.configureDatabase(database);
8118
+ this.database = database;
8119
+ return database;
8120
+ } catch (error) {
8121
+ closeSqliteDatabase(database);
8122
+ throw error;
8123
+ }
8124
+ }
8125
+ async ensureDatabaseDirectory() {
8126
+ this.directoryInitialization ??= ensureDirectory(path6__default.default.dirname(this.databasePath)).catch(
8127
+ (error) => {
8128
+ this.directoryInitialization = void 0;
8129
+ throw error;
8130
+ }
8131
+ );
8132
+ await this.directoryInitialization;
8133
+ }
8134
+ configureDatabase(database) {
8135
+ database.exec("PRAGMA journal_mode = WAL");
8136
+ database.exec("PRAGMA foreign_keys = ON");
8137
+ database.exec(`
8138
+ CREATE TABLE IF NOT EXISTS saved_network_records (
8139
+ record_id TEXT PRIMARY KEY,
8140
+ request_id TEXT NOT NULL,
8141
+ session_ref TEXT NOT NULL,
8142
+ page_ref TEXT,
8143
+ page_ref_key TEXT NOT NULL,
8144
+ frame_ref TEXT,
8145
+ document_ref TEXT,
8146
+ capture TEXT,
8147
+ method TEXT NOT NULL,
8148
+ method_lc TEXT NOT NULL,
8149
+ url TEXT NOT NULL,
8150
+ url_lc TEXT NOT NULL,
8151
+ hostname TEXT NOT NULL,
8152
+ hostname_lc TEXT NOT NULL,
8153
+ path TEXT NOT NULL,
8154
+ path_lc TEXT NOT NULL,
8155
+ status INTEGER,
8156
+ status_text TEXT,
8157
+ resource_type TEXT NOT NULL,
8158
+ navigation_request INTEGER NOT NULL,
8159
+ request_headers_json TEXT NOT NULL,
8160
+ response_headers_json TEXT NOT NULL,
8161
+ request_body_json TEXT,
8162
+ response_body_json TEXT,
8163
+ initiator_json TEXT,
8164
+ timing_json TEXT,
8165
+ transfer_json TEXT,
8166
+ source_json TEXT,
8167
+ capture_state TEXT NOT NULL,
8168
+ request_body_state TEXT NOT NULL,
8169
+ response_body_state TEXT NOT NULL,
8170
+ request_body_skip_reason TEXT,
8171
+ response_body_skip_reason TEXT,
8172
+ request_body_error TEXT,
8173
+ response_body_error TEXT,
8174
+ redirect_from_request_id TEXT,
8175
+ redirect_to_request_id TEXT,
8176
+ saved_at INTEGER NOT NULL
8177
+ );
8178
+
8179
+ CREATE UNIQUE INDEX IF NOT EXISTS saved_network_records_scope_request
8180
+ ON saved_network_records (session_ref, page_ref_key, request_id);
8181
+
8182
+ CREATE INDEX IF NOT EXISTS saved_network_records_saved_at
8183
+ ON saved_network_records (saved_at DESC);
8184
+
8185
+ CREATE INDEX IF NOT EXISTS saved_network_records_capture
8186
+ ON saved_network_records (capture);
8187
+
8188
+ CREATE TABLE IF NOT EXISTS saved_network_tags (
8189
+ record_id TEXT NOT NULL REFERENCES saved_network_records(record_id) ON DELETE CASCADE,
8190
+ tag TEXT NOT NULL,
8191
+ PRIMARY KEY (record_id, tag)
8192
+ );
8193
+
8194
+ CREATE INDEX IF NOT EXISTS saved_network_tags_tag
8195
+ ON saved_network_tags (tag);
8196
+ `);
8197
+ this.ensureColumn(
8198
+ database,
8199
+ "saved_network_records",
8200
+ "capture_state",
8201
+ "TEXT NOT NULL DEFAULT 'complete'"
8202
+ );
8203
+ this.ensureColumn(database, "saved_network_records", "capture", "TEXT");
8204
+ this.ensureColumn(
8205
+ database,
8206
+ "saved_network_records",
8207
+ "request_body_state",
8208
+ "TEXT NOT NULL DEFAULT 'skipped'"
8209
+ );
8210
+ this.ensureColumn(
8211
+ database,
8212
+ "saved_network_records",
8213
+ "response_body_state",
8214
+ "TEXT NOT NULL DEFAULT 'skipped'"
8215
+ );
8216
+ this.ensureColumn(database, "saved_network_records", "request_body_skip_reason", "TEXT");
8217
+ this.ensureColumn(database, "saved_network_records", "response_body_skip_reason", "TEXT");
8218
+ this.ensureColumn(database, "saved_network_records", "request_body_error", "TEXT");
8219
+ this.ensureColumn(database, "saved_network_records", "response_body_error", "TEXT");
8209
8220
  }
8210
8221
  ensureColumn(database, table, column, definition) {
8211
8222
  const rows = database.prepare(`PRAGMA table_info(${table})`).all();
@@ -8218,6 +8229,10 @@ var SqliteSavedNetworkStore = class {
8218
8229
  function buildSavedNetworkWhere(input) {
8219
8230
  const clauses = [];
8220
8231
  const parameters = [];
8232
+ if (input.pageRef !== void 0) {
8233
+ clauses.push("r.page_ref_key = ?");
8234
+ parameters.push(input.pageRef);
8235
+ }
8221
8236
  if (input.recordId !== void 0) {
8222
8237
  clauses.push("r.record_id = ?");
8223
8238
  parameters.push(input.recordId);
@@ -8226,9 +8241,9 @@ function buildSavedNetworkWhere(input) {
8226
8241
  clauses.push("r.request_id = ?");
8227
8242
  parameters.push(input.requestId);
8228
8243
  }
8229
- if (input.actionId !== void 0) {
8230
- clauses.push("r.action_id = ?");
8231
- parameters.push(input.actionId);
8244
+ if (input.capture !== void 0) {
8245
+ clauses.push("r.capture = ?");
8246
+ parameters.push(input.capture);
8232
8247
  }
8233
8248
  if (input.tag !== void 0) {
8234
8249
  clauses.push(`
@@ -8270,6 +8285,127 @@ function buildSavedNetworkWhere(input) {
8270
8285
  parameters
8271
8286
  };
8272
8287
  }
8288
+ function buildSavedNetworkUpsertSql(bodyWriteMode) {
8289
+ const bodyUpdateSql = bodyWriteMode === "authoritative" ? `
8290
+ request_body_json = excluded.request_body_json,
8291
+ response_body_json = excluded.response_body_json,
8292
+ request_body_state = excluded.request_body_state,
8293
+ response_body_state = excluded.response_body_state,
8294
+ request_body_skip_reason = excluded.request_body_skip_reason,
8295
+ response_body_skip_reason = excluded.response_body_skip_reason,
8296
+ request_body_error = excluded.request_body_error,
8297
+ response_body_error = excluded.response_body_error,
8298
+ ` : "";
8299
+ return `
8300
+ INSERT INTO saved_network_records (
8301
+ record_id,
8302
+ request_id,
8303
+ session_ref,
8304
+ page_ref,
8305
+ page_ref_key,
8306
+ frame_ref,
8307
+ document_ref,
8308
+ capture,
8309
+ method,
8310
+ method_lc,
8311
+ url,
8312
+ url_lc,
8313
+ hostname,
8314
+ hostname_lc,
8315
+ path,
8316
+ path_lc,
8317
+ status,
8318
+ status_text,
8319
+ resource_type,
8320
+ navigation_request,
8321
+ request_headers_json,
8322
+ response_headers_json,
8323
+ request_body_json,
8324
+ response_body_json,
8325
+ initiator_json,
8326
+ timing_json,
8327
+ transfer_json,
8328
+ source_json,
8329
+ capture_state,
8330
+ request_body_state,
8331
+ response_body_state,
8332
+ request_body_skip_reason,
8333
+ response_body_skip_reason,
8334
+ request_body_error,
8335
+ response_body_error,
8336
+ redirect_from_request_id,
8337
+ redirect_to_request_id,
8338
+ saved_at
8339
+ ) VALUES (
8340
+ @record_id,
8341
+ @request_id,
8342
+ @session_ref,
8343
+ @page_ref,
8344
+ @page_ref_key,
8345
+ @frame_ref,
8346
+ @document_ref,
8347
+ @capture,
8348
+ @method,
8349
+ @method_lc,
8350
+ @url,
8351
+ @url_lc,
8352
+ @hostname,
8353
+ @hostname_lc,
8354
+ @path,
8355
+ @path_lc,
8356
+ @status,
8357
+ @status_text,
8358
+ @resource_type,
8359
+ @navigation_request,
8360
+ @request_headers_json,
8361
+ @response_headers_json,
8362
+ @request_body_json,
8363
+ @response_body_json,
8364
+ @initiator_json,
8365
+ @timing_json,
8366
+ @transfer_json,
8367
+ @source_json,
8368
+ @capture_state,
8369
+ @request_body_state,
8370
+ @response_body_state,
8371
+ @request_body_skip_reason,
8372
+ @response_body_skip_reason,
8373
+ @request_body_error,
8374
+ @response_body_error,
8375
+ @redirect_from_request_id,
8376
+ @redirect_to_request_id,
8377
+ @saved_at
8378
+ )
8379
+ ON CONFLICT(record_id) DO UPDATE SET
8380
+ page_ref = excluded.page_ref,
8381
+ page_ref_key = excluded.page_ref_key,
8382
+ frame_ref = excluded.frame_ref,
8383
+ document_ref = excluded.document_ref,
8384
+ capture = excluded.capture,
8385
+ method = excluded.method,
8386
+ method_lc = excluded.method_lc,
8387
+ url = excluded.url,
8388
+ url_lc = excluded.url_lc,
8389
+ hostname = excluded.hostname,
8390
+ hostname_lc = excluded.hostname_lc,
8391
+ path = excluded.path,
8392
+ path_lc = excluded.path_lc,
8393
+ status = excluded.status,
8394
+ status_text = excluded.status_text,
8395
+ resource_type = excluded.resource_type,
8396
+ navigation_request = excluded.navigation_request,
8397
+ request_headers_json = excluded.request_headers_json,
8398
+ response_headers_json = excluded.response_headers_json,
8399
+ ${bodyUpdateSql} initiator_json = excluded.initiator_json,
8400
+ timing_json = excluded.timing_json,
8401
+ transfer_json = excluded.transfer_json,
8402
+ source_json = excluded.source_json,
8403
+ capture_state = excluded.capture_state,
8404
+ redirect_from_request_id = excluded.redirect_from_request_id,
8405
+ redirect_to_request_id = excluded.redirect_to_request_id,
8406
+ saved_at = MIN(saved_network_records.saved_at, excluded.saved_at)
8407
+ `;
8408
+ }
8273
8409
  function inflateSavedNetworkRow(row, includeBodies) {
8274
8410
  const requestBody = includeBodies && row.request_body_json !== null ? JSON.parse(row.request_body_json) : void 0;
8275
8411
  const responseBody = includeBodies && row.response_body_json !== null ? JSON.parse(row.response_body_json) : void 0;
@@ -8340,8 +8476,7 @@ function inflateSavedNetworkRow(row, includeBodies) {
8340
8476
  }
8341
8477
  return {
8342
8478
  recordId: row.record_id,
8343
- source: "saved",
8344
- ...row.action_id === null ? {} : { actionId: row.action_id },
8479
+ ...row.capture === null ? {} : { capture: row.capture },
8345
8480
  ...row.tags === null || row.tags.length === 0 ? {} : { tags: row.tags.split(TAG_DELIMITER).filter((tag) => tag.length > 0) },
8346
8481
  savedAt: row.saved_at,
8347
8482
  record
@@ -8350,6 +8485,20 @@ function inflateSavedNetworkRow(row, includeBodies) {
8350
8485
  function stringifyOptional(value) {
8351
8486
  return value === void 0 ? null : JSON.stringify(value);
8352
8487
  }
8488
+ function normalizeSqliteImportError(error) {
8489
+ if (error instanceof Error && error.code === "ERR_UNKNOWN_BUILTIN_MODULE" && error.message.includes(NODE_SQLITE_SPECIFIER)) {
8490
+ return new Error(SAVED_NETWORK_SQLITE_SUPPORT_ERROR, {
8491
+ cause: error
8492
+ });
8493
+ }
8494
+ return error instanceof Error ? error : new Error(String(error));
8495
+ }
8496
+ function closeSqliteDatabase(database) {
8497
+ try {
8498
+ database.close();
8499
+ } catch {
8500
+ }
8501
+ }
8353
8502
  function withSqliteTransaction(database, task) {
8354
8503
  database.exec("BEGIN IMMEDIATE");
8355
8504
  try {
@@ -8559,8 +8708,8 @@ async function createFilesystemOpensteerWorkspace(options) {
8559
8708
  const browserManifestPath = path6__default.default.join(browserPath, "manifest.json");
8560
8709
  const browserUserDataDir = path6__default.default.join(browserPath, "user-data");
8561
8710
  const livePath = path6__default.default.join(options.rootPath, "live");
8562
- const liveSessionPath = path6__default.default.join(livePath, "session.json");
8563
- const liveBrowserPath = path6__default.default.join(livePath, "browser.json");
8711
+ const liveLocalPath = path6__default.default.join(livePath, "local.json");
8712
+ const liveCloudPath = path6__default.default.join(livePath, "cloud.json");
8564
8713
  const artifactsPath = path6__default.default.join(options.rootPath, "artifacts");
8565
8714
  const tracesPath = path6__default.default.join(options.rootPath, "traces");
8566
8715
  const registryPath = path6__default.default.join(options.rootPath, "registry");
@@ -8635,8 +8784,8 @@ async function createFilesystemOpensteerWorkspace(options) {
8635
8784
  browserManifestPath,
8636
8785
  browserUserDataDir,
8637
8786
  livePath,
8638
- liveSessionPath,
8639
- liveBrowserPath,
8787
+ liveLocalPath,
8788
+ liveCloudPath,
8640
8789
  artifactsPath,
8641
8790
  tracesPath,
8642
8791
  registryPath,
@@ -8660,6 +8809,16 @@ async function createFilesystemOpensteerWorkspace(options) {
8660
8809
  };
8661
8810
  }
8662
8811
 
8812
+ // src/runtimes/dom/errors.ts
8813
+ var ElementPathError = class extends Error {
8814
+ code;
8815
+ constructor(code, message) {
8816
+ super(message);
8817
+ this.name = "ElementPathError";
8818
+ this.code = code;
8819
+ }
8820
+ };
8821
+
8663
8822
  // src/runtimes/dom/match-policy.ts
8664
8823
  var ATTRIBUTE_DENY_KEYS = /* @__PURE__ */ new Set([
8665
8824
  "style",
@@ -9220,16 +9379,6 @@ function readDescriptorToken(value, index) {
9220
9379
  nextIndex: cursor
9221
9380
  };
9222
9381
  }
9223
-
9224
- // src/runtimes/dom/errors.ts
9225
- var ElementPathError = class extends Error {
9226
- code;
9227
- constructor(code, message) {
9228
- super(message);
9229
- this.name = "ElementPathError";
9230
- this.code = code;
9231
- }
9232
- };
9233
9382
  var selectorAdapter = {
9234
9383
  isTag(node) {
9235
9384
  return node.kind === "element" && node.source.nodeType === 1;
@@ -9984,24 +10133,31 @@ function collectChildrenInScope(index, node, scopeHostNodeRef) {
9984
10133
  function getShadowScopeNodeRef(index, node) {
9985
10134
  return findContainingShadowHostNode(index, node)?.nodeRef;
9986
10135
  }
10136
+
10137
+ // src/runtimes/dom/descriptors.ts
9987
10138
  function createDomDescriptorStore(options) {
9988
- const namespace = normalizeNamespace(options.namespace);
10139
+ const namespace = normalizeDomDescriptorNamespace(options.namespace);
9989
10140
  if (options.root) {
9990
10141
  return new FilesystemDomDescriptorStore(options.root.registry.descriptors, namespace);
9991
10142
  }
9992
10143
  return new MemoryDomDescriptorStore(namespace);
9993
10144
  }
9994
- function descriptionKey(namespace, method, description) {
9995
- return `dom:${namespace}:${method}:${sha256Hex2(description.trim())}`;
10145
+ function hashDomDescriptorDescription(description) {
10146
+ return sha256Hex2(description.trim());
9996
10147
  }
9997
- function normalizeNamespace(namespace) {
10148
+ function buildDomDescriptorKey(options) {
10149
+ return `dom:${normalizeDomDescriptorNamespace(options.namespace)}:${options.method}:${hashDomDescriptorDescription(
10150
+ options.description
10151
+ )}`;
10152
+ }
10153
+ function normalizeDomDescriptorNamespace(namespace) {
9998
10154
  const normalized = String(namespace || "default").trim();
9999
10155
  return normalized.length === 0 ? "default" : normalized;
10000
10156
  }
10001
10157
  function sha256Hex2(value) {
10002
10158
  return crypto.createHash("sha256").update(value).digest("hex");
10003
10159
  }
10004
- function buildPayload(input) {
10160
+ function buildDomDescriptorPayload(input) {
10005
10161
  return {
10006
10162
  kind: "dom-target",
10007
10163
  method: input.method,
@@ -10010,6 +10166,9 @@ function buildPayload(input) {
10010
10166
  ...input.sourceUrl === void 0 ? {} : { sourceUrl: input.sourceUrl }
10011
10167
  };
10012
10168
  }
10169
+ function buildDomDescriptorVersion(payload) {
10170
+ return sha256Hex2(canonicalJsonString(payload));
10171
+ }
10013
10172
  function parseDomDescriptorRecord(record) {
10014
10173
  const payload = record.payload;
10015
10174
  if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
@@ -10051,7 +10210,11 @@ var FilesystemDomDescriptorStore = class {
10051
10210
  }
10052
10211
  async read(input) {
10053
10212
  const record = await this.registry.resolve({
10054
- key: descriptionKey(this.namespace, input.method, input.description)
10213
+ key: buildDomDescriptorKey({
10214
+ namespace: this.namespace,
10215
+ method: input.method,
10216
+ description: input.description
10217
+ })
10055
10218
  });
10056
10219
  if (!record) {
10057
10220
  return void 0;
@@ -10059,9 +10222,13 @@ var FilesystemDomDescriptorStore = class {
10059
10222
  return parseDomDescriptorRecord(record);
10060
10223
  }
10061
10224
  async write(input) {
10062
- const payload = buildPayload(input);
10063
- const key = descriptionKey(this.namespace, input.method, input.description);
10064
- const version = sha256Hex2(canonicalJsonString(payload));
10225
+ const payload = buildDomDescriptorPayload(input);
10226
+ const key = buildDomDescriptorKey({
10227
+ namespace: this.namespace,
10228
+ method: input.method,
10229
+ description: input.description
10230
+ });
10231
+ const version = buildDomDescriptorVersion(payload);
10065
10232
  const existing = await this.registry.resolve({ key, version });
10066
10233
  if (existing) {
10067
10234
  const parsed2 = parseDomDescriptorRecord(existing);
@@ -10099,12 +10266,22 @@ var MemoryDomDescriptorStore = class {
10099
10266
  latestByKey = /* @__PURE__ */ new Map();
10100
10267
  recordsByKey = /* @__PURE__ */ new Map();
10101
10268
  async read(input) {
10102
- return this.latestByKey.get(descriptionKey(this.namespace, input.method, input.description));
10269
+ return this.latestByKey.get(
10270
+ buildDomDescriptorKey({
10271
+ namespace: this.namespace,
10272
+ method: input.method,
10273
+ description: input.description
10274
+ })
10275
+ );
10103
10276
  }
10104
10277
  async write(input) {
10105
- const payload = buildPayload(input);
10106
- const key = descriptionKey(this.namespace, input.method, input.description);
10107
- const version = sha256Hex2(canonicalJsonString(payload));
10278
+ const payload = buildDomDescriptorPayload(input);
10279
+ const key = buildDomDescriptorKey({
10280
+ namespace: this.namespace,
10281
+ method: input.method,
10282
+ description: input.description
10283
+ });
10284
+ const version = buildDomDescriptorVersion(payload);
10108
10285
  const existing = this.recordsByKey.get(key)?.get(version);
10109
10286
  if (existing) {
10110
10287
  return existing;
@@ -10255,8 +10432,9 @@ var DomActionExecutor = class {
10255
10432
  })
10256
10433
  );
10257
10434
  let finalResolved = resolved;
10435
+ let finalSnapshot;
10258
10436
  if (input.pressEnter) {
10259
- await this.settle(resolved.pageRef, "dom.input", timeout);
10437
+ await this.waitForPressEnterReaction(timeout);
10260
10438
  const enterSession = this.options.createResolutionSession();
10261
10439
  const enterResolved = await timeout.runStep(
10262
10440
  () => this.options.resolveTarget(enterSession, {
@@ -10270,6 +10448,9 @@ var DomActionExecutor = class {
10270
10448
  () => bridge.inspectActionTarget(enterResolved.locator)
10271
10449
  );
10272
10450
  this.assertKeyboardActionable("dom.input", enterResolved, inspectionBeforeEnter);
10451
+ finalSnapshot = await timeout.runStep(
10452
+ () => captureActionBoundarySnapshot(this.options.engine, enterResolved.pageRef)
10453
+ );
10273
10454
  await timeout.runStep(
10274
10455
  () => bridge.pressKey(enterResolved.locator, {
10275
10456
  key: "Enter"
@@ -10277,7 +10458,15 @@ var DomActionExecutor = class {
10277
10458
  );
10278
10459
  finalResolved = enterResolved;
10279
10460
  }
10280
- await this.settle(finalResolved.pageRef, "dom.input", timeout);
10461
+ const settleDiagnostics = await this.settle(
10462
+ finalResolved.pageRef,
10463
+ "dom.input",
10464
+ timeout,
10465
+ finalSnapshot
10466
+ );
10467
+ if (finalSnapshot !== void 0) {
10468
+ recordActionBoundaryDiagnostics(timeout.signal, settleDiagnostics);
10469
+ }
10281
10470
  return finalResolved;
10282
10471
  } catch (error) {
10283
10472
  lastError = error;
@@ -10350,8 +10539,17 @@ var DomActionExecutor = class {
10350
10539
  );
10351
10540
  }
10352
10541
  }
10542
+ const actionBoundarySnapshot = await timeout.runStep(
10543
+ () => captureActionBoundarySnapshot(this.options.engine, pointerTarget.resolved.pageRef)
10544
+ );
10353
10545
  const outcome = await dispatch(pointerTarget, point, timeout);
10354
- await this.settle(pointerTarget.resolved.pageRef, input.operation, timeout);
10546
+ const settleDiagnostics = await this.settle(
10547
+ pointerTarget.resolved.pageRef,
10548
+ input.operation,
10549
+ timeout,
10550
+ actionBoundarySnapshot
10551
+ );
10552
+ recordActionBoundaryDiagnostics(timeout.signal, settleDiagnostics);
10355
10553
  return outcome;
10356
10554
  } catch (error) {
10357
10555
  lastError = error;
@@ -10369,23 +10567,49 @@ var DomActionExecutor = class {
10369
10567
  }
10370
10568
  return runWithPolicyTimeout(this.options.policy.timeout, { operation }, execute);
10371
10569
  }
10372
- async settle(pageRef, operation, timeout) {
10570
+ async settle(pageRef, operation, timeout, snapshot) {
10373
10571
  const bridge = this.requireBridge();
10374
- await timeout.runStep(
10572
+ let visualSettled = true;
10573
+ const boundary = await timeout.runStep(
10375
10574
  () => bridge.finalizeDomAction(pageRef, {
10376
10575
  operation,
10576
+ ...snapshot === void 0 ? {} : { snapshot },
10377
10577
  signal: timeout.signal,
10378
10578
  remainingMs: () => timeout.remainingMs(),
10379
- policySettle: (targetPageRef) => settleWithPolicy(this.options.policy.settle, {
10380
- operation,
10381
- trigger: "dom-action",
10382
- engine: this.options.engine,
10383
- pageRef: targetPageRef,
10384
- signal: timeout.signal,
10385
- remainingMs: timeout.remainingMs()
10386
- })
10579
+ policySettle: async (targetPageRef, trigger) => {
10580
+ try {
10581
+ await settleWithPolicy(this.options.policy.settle, {
10582
+ operation,
10583
+ trigger,
10584
+ engine: this.options.engine,
10585
+ pageRef: targetPageRef,
10586
+ signal: timeout.signal,
10587
+ remainingMs: timeout.remainingMs()
10588
+ });
10589
+ } catch (error) {
10590
+ if (snapshot !== void 0 && isSoftSettleTimeoutError(error, timeout.signal)) {
10591
+ visualSettled = false;
10592
+ return;
10593
+ }
10594
+ throw error;
10595
+ }
10596
+ }
10387
10597
  })
10388
10598
  );
10599
+ return createActionBoundaryDiagnostics({
10600
+ boundary,
10601
+ visualSettled
10602
+ });
10603
+ }
10604
+ async waitForPressEnterReaction(timeout) {
10605
+ const delayMs = this.options.policy.settle.resolveDelayMs({
10606
+ operation: "dom.input",
10607
+ trigger: "dom-action"
10608
+ });
10609
+ if (delayMs <= 0) {
10610
+ return;
10611
+ }
10612
+ await delayWithSignal(delayMs, timeout.signal);
10389
10613
  }
10390
10614
  requireBridge() {
10391
10615
  if (this.bridge !== void 0) {
@@ -10747,7 +10971,7 @@ var DefaultDomRuntime = class {
10747
10971
  bridge;
10748
10972
  constructor(options) {
10749
10973
  this.engine = options.engine;
10750
- this.descriptors = createDomDescriptorStore({
10974
+ this.descriptors = options.descriptorStore ?? createDomDescriptorStore({
10751
10975
  ...options.root === void 0 ? {} : { root: options.root },
10752
10976
  ...options.namespace === void 0 ? {} : { namespace: options.namespace }
10753
10977
  });
@@ -11652,23 +11876,47 @@ var DefaultComputerUseRuntime = class {
11652
11876
  const preActionDisplay = createComputerDisplayTransform(preActionNativeViewport);
11653
11877
  const nativeAction = toNativeComputerAction(input.input.action, preActionDisplay);
11654
11878
  const screenshot = normalizeScreenshotOptions(input.input.screenshot);
11879
+ const snapshot = await input.timeout.runStep(
11880
+ () => captureActionBoundarySnapshot(this.options.engine, input.pageRef)
11881
+ );
11882
+ let visualSettled = true;
11655
11883
  const executed = await input.timeout.runStep(
11656
11884
  () => bridge.execute({
11657
11885
  pageRef: input.pageRef,
11886
+ snapshot,
11658
11887
  action: nativeAction,
11659
11888
  screenshot,
11660
11889
  signal: input.timeout.signal,
11661
11890
  remainingMs: () => input.timeout.remainingMs(),
11662
- policySettle: async (pageRef) => settleWithPolicy(this.options.policy.settle, {
11663
- operation: "computer.execute",
11664
- trigger: "dom-action",
11665
- engine: this.options.engine,
11666
- pageRef,
11667
- signal: input.timeout.signal,
11668
- remainingMs: input.timeout.remainingMs()
11669
- })
11891
+ policySettle: async (pageRef, trigger) => {
11892
+ try {
11893
+ await settleWithPolicy(this.options.policy.settle, {
11894
+ operation: "computer.execute",
11895
+ trigger,
11896
+ engine: this.options.engine,
11897
+ pageRef,
11898
+ signal: input.timeout.signal,
11899
+ remainingMs: input.timeout.remainingMs()
11900
+ });
11901
+ } catch (error) {
11902
+ if (pageRef === input.pageRef && isSoftSettleTimeoutError(error, input.timeout.signal)) {
11903
+ visualSettled = false;
11904
+ return;
11905
+ }
11906
+ throw error;
11907
+ }
11908
+ }
11670
11909
  })
11671
11910
  );
11911
+ if (executed.boundary !== void 0 && executed.pageRef === input.pageRef) {
11912
+ recordActionBoundaryDiagnostics(
11913
+ input.timeout.signal,
11914
+ createActionBoundaryDiagnostics({
11915
+ boundary: executed.boundary,
11916
+ visualSettled
11917
+ })
11918
+ );
11919
+ }
11672
11920
  let trace = void 0;
11673
11921
  if (!input.timeout.signal.aborted) {
11674
11922
  try {
@@ -11781,8 +12029,8 @@ async function dispatchSemanticOperation(runtime, operation, input, options = {}
11781
12029
  input,
11782
12030
  options
11783
12031
  );
11784
- case "network.save":
11785
- return runtime.saveNetwork(
12032
+ case "network.tag":
12033
+ return runtime.tagNetwork(
11786
12034
  input,
11787
12035
  options
11788
12036
  );
@@ -12635,7 +12883,7 @@ function inferRequestPlanFromNetworkRecord(record, input, options = {}) {
12635
12883
  const body = inferRequestPlanBody(record.record.requestBody, requestContentType);
12636
12884
  const payload = normalizeRequestPlanPayload({
12637
12885
  transport: {
12638
- kind: "context-http"
12886
+ kind: input.transport ?? "context-http"
12639
12887
  },
12640
12888
  endpoint: {
12641
12889
  method: record.record.method,
@@ -12655,11 +12903,10 @@ function inferRequestPlanFromNetworkRecord(record, input, options = {}) {
12655
12903
  return {
12656
12904
  key: input.key,
12657
12905
  version: input.version,
12658
- lifecycle: input.lifecycle ?? "draft",
12659
12906
  provenance: {
12660
- source: record.source === "saved" ? "saved-network-record" : "live-network-record",
12907
+ source: record.savedAt === void 0 ? "network-record" : "saved-network-record",
12661
12908
  sourceId: record.recordId,
12662
- ...record.source === "saved" ? record.savedAt === void 0 ? {} : { capturedAt: record.savedAt } : options.observedAt === void 0 ? {} : { capturedAt: options.observedAt }
12909
+ ...record.savedAt === void 0 ? options.observedAt === void 0 ? {} : { capturedAt: options.observedAt } : { capturedAt: record.savedAt }
12663
12910
  },
12664
12911
  payload,
12665
12912
  ...record.tags === void 0 || record.tags.length === 0 ? {} : { tags: record.tags }
@@ -13039,63 +13286,67 @@ function resolveBodyEncoding(charset) {
13039
13286
  return "utf8";
13040
13287
  }
13041
13288
  }
13042
- var NetworkJournal = class {
13289
+ var NetworkHistory = class {
13043
13290
  metadataByRequestId = /* @__PURE__ */ new Map();
13044
13291
  requestIdByRecordId = /* @__PURE__ */ new Map();
13045
- requestIdsByActionId = /* @__PURE__ */ new Map();
13292
+ requestIdsByCapture = /* @__PURE__ */ new Map();
13046
13293
  requestIdsByTag = /* @__PURE__ */ new Map();
13047
- sync(records, options = {}) {
13294
+ tombstonedRequestIds = /* @__PURE__ */ new Set();
13295
+ materialize(records, options = {}) {
13048
13296
  const observedAt = Date.now();
13049
- return records.map((record) => this.materializeLiveRecord(record, observedAt, options));
13050
- }
13051
- materializeLiveRecord(record, observedAt, options = {}) {
13052
- let metadata = this.metadataByRequestId.get(record.requestId);
13053
- if (!metadata) {
13054
- metadata = {
13055
- recordId: `record:${crypto.randomUUID()}`,
13056
- observedAt,
13057
- ...record.pageRef === void 0 ? {} : { pageRef: record.pageRef },
13058
- tags: /* @__PURE__ */ new Set()
13059
- };
13060
- this.metadataByRequestId.set(record.requestId, metadata);
13061
- this.requestIdByRecordId.set(metadata.recordId, record.requestId);
13062
- } else if (metadata.pageRef === void 0 && record.pageRef !== void 0) {
13063
- metadata.pageRef = record.pageRef;
13297
+ const materialized = [];
13298
+ for (const record of records) {
13299
+ const entry = this.materializeRecord(record, observedAt, options);
13300
+ if (entry !== void 0) {
13301
+ materialized.push(entry);
13302
+ }
13064
13303
  }
13065
- return {
13066
- recordId: metadata.recordId,
13067
- source: "live",
13068
- ...metadata.actionId === void 0 ? {} : { actionId: metadata.actionId },
13069
- ...metadata.tags.size === 0 ? {} : { tags: [...metadata.tags].sort() },
13070
- record: toProtocolNetworkRecord(record, {
13071
- redactSecretHeaders: options.redactSecretHeaders ?? true
13072
- })
13073
- };
13304
+ return materialized;
13074
13305
  }
13075
- diffNewRequestIds(records, baselineRequestIds) {
13076
- const observedAt = Date.now();
13077
- const all = records.map(
13078
- (record) => this.materializeLiveRecord(record, observedAt, {
13079
- redactSecretHeaders: true
13080
- })
13081
- );
13082
- const delta = all.filter((entry) => !baselineRequestIds.has(entry.record.requestId));
13083
- return {
13084
- all,
13085
- delta
13086
- };
13306
+ async persist(records, store, options) {
13307
+ const observedAt = options.observedAt ?? Date.now();
13308
+ const metadataToSave = /* @__PURE__ */ new Set();
13309
+ const persisted = [];
13310
+ for (const record of records) {
13311
+ const entry = this.materializeRecord(record, observedAt, {
13312
+ ...options.redactSecretHeaders === void 0 ? {} : { redactSecretHeaders: options.redactSecretHeaders }
13313
+ });
13314
+ if (entry === void 0) {
13315
+ continue;
13316
+ }
13317
+ const requestId = entry.record.requestId;
13318
+ const metadata = this.metadataByRequestId.get(requestId);
13319
+ if (metadata === void 0) {
13320
+ continue;
13321
+ }
13322
+ const savedAt = metadata.savedAt ?? observedAt;
13323
+ metadataToSave.add(metadata);
13324
+ persisted.push({
13325
+ ...entry,
13326
+ savedAt
13327
+ });
13328
+ }
13329
+ if (persisted.length > 0) {
13330
+ await store.save(persisted, {
13331
+ bodyWriteMode: options.bodyWriteMode
13332
+ });
13333
+ for (const metadata of metadataToSave) {
13334
+ metadata.savedAt ??= observedAt;
13335
+ }
13336
+ }
13337
+ return persisted;
13087
13338
  }
13088
- assignActionId(records, actionId) {
13339
+ assignCapture(records, capture) {
13089
13340
  for (const record of records) {
13090
13341
  const metadata = this.metadataByRequestId.get(record.record.requestId);
13091
- if (!metadata || metadata.actionId === actionId) {
13342
+ if (!metadata || metadata.capture === capture) {
13092
13343
  continue;
13093
13344
  }
13094
- if (metadata.actionId !== void 0) {
13095
- this.requestIdsByActionId.get(metadata.actionId)?.delete(record.record.requestId);
13345
+ if (metadata.capture !== void 0) {
13346
+ this.requestIdsByCapture.get(metadata.capture)?.delete(record.record.requestId);
13096
13347
  }
13097
- metadata.actionId = actionId;
13098
- this.addIndexedRequestId(this.requestIdsByActionId, actionId, record.record.requestId);
13348
+ metadata.capture = capture;
13349
+ this.addIndexedRequestId(this.requestIdsByCapture, capture, record.record.requestId);
13099
13350
  }
13100
13351
  }
13101
13352
  addTag(records, tag) {
@@ -13115,8 +13366,8 @@ var NetworkJournal = class {
13115
13366
  getRequestId(recordId) {
13116
13367
  return this.requestIdByRecordId.get(recordId);
13117
13368
  }
13118
- getRequestIdsForActionId(actionId) {
13119
- return new Set(this.requestIdsByActionId.get(actionId) ?? []);
13369
+ getRequestIdsForCapture(capture) {
13370
+ return new Set(this.requestIdsByCapture.get(capture) ?? []);
13120
13371
  }
13121
13372
  getRequestIdsForTag(tag) {
13122
13373
  return new Set(this.requestIdsByTag.get(tag) ?? []);
@@ -13124,11 +13375,59 @@ var NetworkJournal = class {
13124
13375
  getPageRefForRequestId(requestId) {
13125
13376
  return this.metadataByRequestId.get(requestId)?.pageRef;
13126
13377
  }
13378
+ getKnownRequestIds() {
13379
+ return new Set(this.metadataByRequestId.keys());
13380
+ }
13381
+ tombstoneRequestIds(requestIds) {
13382
+ for (const requestId of requestIds) {
13383
+ this.tombstonedRequestIds.add(requestId);
13384
+ const metadata = this.metadataByRequestId.get(requestId);
13385
+ if (!metadata) {
13386
+ continue;
13387
+ }
13388
+ this.metadataByRequestId.delete(requestId);
13389
+ this.requestIdByRecordId.delete(metadata.recordId);
13390
+ if (metadata.capture !== void 0) {
13391
+ this.requestIdsByCapture.get(metadata.capture)?.delete(requestId);
13392
+ }
13393
+ for (const tag of metadata.tags) {
13394
+ this.requestIdsByTag.get(tag)?.delete(requestId);
13395
+ }
13396
+ }
13397
+ }
13127
13398
  clear() {
13128
13399
  this.metadataByRequestId.clear();
13129
13400
  this.requestIdByRecordId.clear();
13130
- this.requestIdsByActionId.clear();
13401
+ this.requestIdsByCapture.clear();
13131
13402
  this.requestIdsByTag.clear();
13403
+ this.tombstonedRequestIds.clear();
13404
+ }
13405
+ materializeRecord(record, observedAt, options) {
13406
+ if (this.tombstonedRequestIds.has(record.requestId)) {
13407
+ return void 0;
13408
+ }
13409
+ let metadata = this.metadataByRequestId.get(record.requestId);
13410
+ if (!metadata) {
13411
+ metadata = {
13412
+ recordId: `record:${crypto.randomUUID()}`,
13413
+ observedAt,
13414
+ ...record.pageRef === void 0 ? {} : { pageRef: record.pageRef },
13415
+ tags: /* @__PURE__ */ new Set()
13416
+ };
13417
+ this.metadataByRequestId.set(record.requestId, metadata);
13418
+ this.requestIdByRecordId.set(metadata.recordId, record.requestId);
13419
+ } else if (metadata.pageRef === void 0 && record.pageRef !== void 0) {
13420
+ metadata.pageRef = record.pageRef;
13421
+ }
13422
+ return {
13423
+ recordId: metadata.recordId,
13424
+ ...metadata.capture === void 0 ? {} : { capture: metadata.capture },
13425
+ ...metadata.tags.size === 0 ? {} : { tags: [...metadata.tags].sort() },
13426
+ ...metadata.savedAt === void 0 ? {} : { savedAt: metadata.savedAt },
13427
+ record: toProtocolNetworkRecord(record, {
13428
+ redactSecretHeaders: options.redactSecretHeaders ?? true
13429
+ })
13430
+ };
13132
13431
  }
13133
13432
  addIndexedRequestId(index, key, requestId) {
13134
13433
  const requestIds = index.get(key) ?? /* @__PURE__ */ new Set();
@@ -16927,7 +17226,6 @@ async function compileOpensteerExtractionFieldTargets(options) {
16927
17226
  await collectFieldTargetsFromSchemaObject({
16928
17227
  dom: options.dom,
16929
17228
  pageRef: options.pageRef,
16930
- latestSnapshotCounters: options.latestSnapshotCounters,
16931
17229
  value: options.schema,
16932
17230
  path: "",
16933
17231
  fields,
@@ -16945,22 +17243,11 @@ async function extractOpensteerExtractionFieldTargets(options) {
16945
17243
  source: "current_url"
16946
17244
  };
16947
17245
  }
16948
- if ("path" in field) {
16949
- return {
16950
- key: field.key,
16951
- target: {
16952
- kind: "path",
16953
- path: field.path
16954
- },
16955
- ...field.attribute === void 0 ? {} : { attribute: field.attribute }
16956
- };
16957
- }
16958
17246
  return {
16959
17247
  key: field.key,
16960
17248
  target: {
16961
- kind: "live",
16962
- locator: field.locator,
16963
- anchor: field.anchor
17249
+ kind: "path",
17250
+ path: field.path
16964
17251
  },
16965
17252
  ...field.attribute === void 0 ? {} : { attribute: field.attribute }
16966
17253
  };
@@ -16969,8 +17256,6 @@ async function extractOpensteerExtractionFieldTargets(options) {
16969
17256
  }
16970
17257
  async function compilePersistedOpensteerExtractionPayloadFromFieldTargets(options) {
16971
17258
  const fields = await resolvePersistableFieldTargets({
16972
- pageRef: options.pageRef,
16973
- dom: options.dom,
16974
17259
  fieldTargets: options.fieldTargets
16975
17260
  });
16976
17261
  const payload = buildPersistedOpensteerExtractionPayload(fields);
@@ -16984,7 +17269,7 @@ async function replayOpensteerExtractionPayload(options) {
16984
17269
  return extractPersistedObjectNode(options.pageRef, options.dom, options.payload);
16985
17270
  }
16986
17271
  function createOpensteerExtractionDescriptorStore(options) {
16987
- const namespace = normalizeNamespace2(options.namespace);
17272
+ const namespace = normalizeNamespace(options.namespace);
16988
17273
  if (options.root) {
16989
17274
  return new FilesystemOpensteerExtractionDescriptorStore(
16990
17275
  options.root.registry.descriptors,
@@ -17002,7 +17287,6 @@ async function collectFieldTargetsFromSchemaObject(options) {
17002
17287
  await collectFieldTargetsFromSchemaValue({
17003
17288
  dom: options.dom,
17004
17289
  pageRef: options.pageRef,
17005
- latestSnapshotCounters: options.latestSnapshotCounters,
17006
17290
  value: childValue,
17007
17291
  path: joinDataPath(options.path, normalizedKey),
17008
17292
  fields: options.fields,
@@ -17017,7 +17301,6 @@ async function collectFieldTargetsFromSchemaValue(options) {
17017
17301
  await compileFieldTarget({
17018
17302
  dom: options.dom,
17019
17303
  pageRef: options.pageRef,
17020
- latestSnapshotCounters: options.latestSnapshotCounters,
17021
17304
  field: normalizedField,
17022
17305
  path: options.path
17023
17306
  })
@@ -17046,7 +17329,6 @@ async function collectFieldTargetsFromSchemaValue(options) {
17046
17329
  await collectFieldTargetsFromSchemaObject({
17047
17330
  dom: options.dom,
17048
17331
  pageRef: options.pageRef,
17049
- latestSnapshotCounters: options.latestSnapshotCounters,
17050
17332
  value: itemValue,
17051
17333
  path: appendDataPathIndex(options.path, index),
17052
17334
  fields: options.fields,
@@ -17069,7 +17351,6 @@ async function collectFieldTargetsFromSchemaValue(options) {
17069
17351
  await collectFieldTargetsFromSchemaObject({
17070
17352
  dom: options.dom,
17071
17353
  pageRef: options.pageRef,
17072
- latestSnapshotCounters: options.latestSnapshotCounters,
17073
17354
  value: options.value,
17074
17355
  path: options.path,
17075
17356
  fields: options.fields,
@@ -17096,10 +17377,10 @@ async function compileFieldTarget(options) {
17096
17377
  }
17097
17378
  return {
17098
17379
  key: options.path,
17099
- ...await resolveLiveFieldTarget({
17100
- latestSnapshotCounters: options.latestSnapshotCounters,
17101
- field: options.field,
17102
- path: options.path
17380
+ path: await resolveSelectorFieldPath({
17381
+ dom: options.dom,
17382
+ pageRef: options.pageRef,
17383
+ selector: `[c="${String(options.field.element)}"]`
17103
17384
  }),
17104
17385
  ...options.field.attribute === void 0 ? {} : { attribute: options.field.attribute }
17105
17386
  };
@@ -17117,24 +17398,6 @@ async function resolveSelectorFieldPath(options) {
17117
17398
  locator: resolved.locator
17118
17399
  });
17119
17400
  }
17120
- async function resolveLiveFieldTarget(options) {
17121
- const counters = options.latestSnapshotCounters;
17122
- if (counters === void 0) {
17123
- throw new Error(
17124
- `Extraction schema field "${labelForPath(options.path)}" uses element ${String(options.field.element)} but no snapshot is available.`
17125
- );
17126
- }
17127
- const counter = counters.get(options.field.element);
17128
- if (!counter) {
17129
- throw new Error(
17130
- `Extraction schema field "${labelForPath(options.path)}" references missing counter ${String(options.field.element)}.`
17131
- );
17132
- }
17133
- return {
17134
- locator: counter.locator,
17135
- anchor: counter.anchor
17136
- };
17137
- }
17138
17401
  async function resolvePersistableFieldTargets(options) {
17139
17402
  const fields = [];
17140
17403
  for (const field of options.fieldTargets) {
@@ -17145,29 +17408,9 @@ async function resolvePersistableFieldTargets(options) {
17145
17408
  });
17146
17409
  continue;
17147
17410
  }
17148
- if ("path" in field) {
17149
- fields.push({
17150
- key: field.key,
17151
- path: sanitizeElementPath(field.path),
17152
- ...field.attribute === void 0 ? {} : { attribute: field.attribute }
17153
- });
17154
- continue;
17155
- }
17156
- const resolved = await options.dom.resolveTarget({
17157
- pageRef: options.pageRef,
17158
- method: "extract",
17159
- target: {
17160
- kind: "live",
17161
- locator: field.locator,
17162
- anchor: field.anchor
17163
- }
17164
- });
17165
- const path9 = resolved.replayPath ?? await options.dom.buildPath({
17166
- locator: resolved.locator
17167
- });
17168
17411
  fields.push({
17169
17412
  key: field.key,
17170
- path: sanitizeElementPath(path9),
17413
+ path: sanitizeElementPath(field.path),
17171
17414
  ...field.attribute === void 0 ? {} : { attribute: field.attribute }
17172
17415
  });
17173
17416
  }
@@ -17481,11 +17724,11 @@ function normalizeSchemaField(value) {
17481
17724
  ...attribute === void 0 ? {} : { attribute }
17482
17725
  };
17483
17726
  }
17484
- function normalizeNamespace2(namespace) {
17727
+ function normalizeNamespace(namespace) {
17485
17728
  const normalized = String(namespace ?? "default").trim();
17486
17729
  return normalized.length === 0 ? "default" : normalized;
17487
17730
  }
17488
- function descriptionKey2(namespace, description) {
17731
+ function descriptionKey(namespace, description) {
17489
17732
  return `extract:${namespace}:${sha256Hex3(description.trim())}`;
17490
17733
  }
17491
17734
  function parseExtractionDescriptorRecord(record) {
@@ -17588,7 +17831,7 @@ var FilesystemOpensteerExtractionDescriptorStore = class {
17588
17831
  }
17589
17832
  async read(input) {
17590
17833
  const record = await this.registry.resolve({
17591
- key: descriptionKey2(this.namespace, input.description)
17834
+ key: descriptionKey(this.namespace, input.description)
17592
17835
  });
17593
17836
  return record === void 0 ? void 0 : parseExtractionDescriptorRecord(record);
17594
17837
  }
@@ -17600,7 +17843,7 @@ var FilesystemOpensteerExtractionDescriptorStore = class {
17600
17843
  ...input.schemaHash === void 0 ? {} : { schemaHash: input.schemaHash },
17601
17844
  ...input.sourceUrl === void 0 ? {} : { sourceUrl: input.sourceUrl }
17602
17845
  };
17603
- const key = descriptionKey2(this.namespace, input.description);
17846
+ const key = descriptionKey(this.namespace, input.description);
17604
17847
  const version = sha256Hex3(canonicalJsonString(payload));
17605
17848
  const existing = await this.registry.resolve({ key, version });
17606
17849
  if (existing) {
@@ -17639,7 +17882,7 @@ var MemoryOpensteerExtractionDescriptorStore = class {
17639
17882
  latestByKey = /* @__PURE__ */ new Map();
17640
17883
  recordsByKey = /* @__PURE__ */ new Map();
17641
17884
  async read(input) {
17642
- return this.latestByKey.get(descriptionKey2(this.namespace, input.description));
17885
+ return this.latestByKey.get(descriptionKey(this.namespace, input.description));
17643
17886
  }
17644
17887
  async write(input) {
17645
17888
  const payload = {
@@ -17649,7 +17892,7 @@ var MemoryOpensteerExtractionDescriptorStore = class {
17649
17892
  ...input.schemaHash === void 0 ? {} : { schemaHash: input.schemaHash },
17650
17893
  ...input.sourceUrl === void 0 ? {} : { sourceUrl: input.sourceUrl }
17651
17894
  };
17652
- const key = descriptionKey2(this.namespace, input.description);
17895
+ const key = descriptionKey(this.namespace, input.description);
17653
17896
  const version = sha256Hex3(canonicalJsonString(payload));
17654
17897
  const existing = this.recordsByKey.get(key)?.get(version);
17655
17898
  if (existing) {
@@ -17693,6 +17936,7 @@ var OPENSTEER_INTERACTIVE_ATTR = "data-opensteer-interactive";
17693
17936
  var OPENSTEER_HIDDEN_ATTR = "data-opensteer-hidden";
17694
17937
  var OPENSTEER_SCROLLABLE_ATTR = "data-opensteer-scrollable";
17695
17938
  var OPENSTEER_NODE_ID_ATTR = "data-os-node-id";
17939
+ var OPENSTEER_SPARSE_COUNTER_ATTR = "data-os-c";
17696
17940
  var OPENSTEER_BOUNDARY_ATTR = "data-os-boundary";
17697
17941
  var OPENSTEER_UNAVAILABLE_ATTR = "data-os-unavailable";
17698
17942
  var OPENSTEER_IFRAME_BOUNDARY_TAG = "os-iframe-root";
@@ -18197,7 +18441,82 @@ var VOID_TAGS2 = /* @__PURE__ */ new Set([
18197
18441
  ]);
18198
18442
 
18199
18443
  // src/sdk/snapshot/compiler.ts
18444
+ async function assignSparseCountersToLiveDom(engine, pageRef) {
18445
+ try {
18446
+ await engine.evaluatePage({
18447
+ pageRef,
18448
+ script: `(() => {
18449
+ let counter = 1;
18450
+ const walk = (root) => {
18451
+ for (const child of root.children) {
18452
+ child.setAttribute('data-os-c', String(counter++));
18453
+ walk(child);
18454
+ if (child.shadowRoot) walk(child.shadowRoot);
18455
+ }
18456
+ };
18457
+ walk(document);
18458
+ })()`
18459
+ });
18460
+ return true;
18461
+ } catch {
18462
+ return false;
18463
+ }
18464
+ }
18465
+ async function syncDenseCountersToLiveDom(engine, pageRef, sparseToDirectMapping) {
18466
+ const mappingObj = Object.fromEntries(sparseToDirectMapping);
18467
+ await engine.evaluatePage({
18468
+ pageRef,
18469
+ script: `((mapping) => {
18470
+ const walk = (root) => {
18471
+ for (const child of root.children) {
18472
+ child.removeAttribute('c');
18473
+ const sparse = child.getAttribute('data-os-c');
18474
+ if (sparse !== null) {
18475
+ const dense = mapping[sparse];
18476
+ if (dense !== undefined) {
18477
+ child.setAttribute('c', String(dense));
18478
+ }
18479
+ child.removeAttribute('data-os-c');
18480
+ }
18481
+ walk(child);
18482
+ if (child.shadowRoot) walk(child.shadowRoot);
18483
+ }
18484
+ };
18485
+ walk(document);
18486
+ })`,
18487
+ args: [mappingObj]
18488
+ });
18489
+ }
18490
+ function renumberCountersDensely(cleanedHtml, counterRecords) {
18491
+ const $ = cheerio__namespace.load(cleanedHtml, { xmlMode: false });
18492
+ const newRecords = /* @__PURE__ */ new Map();
18493
+ const sparseToDirectMapping = /* @__PURE__ */ new Map();
18494
+ let nextDense = 1;
18495
+ $("[c]").each(function renumberElement() {
18496
+ const el = $(this);
18497
+ const oldC = Number.parseInt(String(el.attr("c") || ""), 10);
18498
+ if (!Number.isFinite(oldC)) {
18499
+ return;
18500
+ }
18501
+ const record = counterRecords.get(oldC);
18502
+ if (!record) {
18503
+ return;
18504
+ }
18505
+ const denseC = nextDense++;
18506
+ el.attr("c", String(denseC));
18507
+ newRecords.set(denseC, { ...record, element: denseC });
18508
+ if (record.sparseCounter !== void 0) {
18509
+ sparseToDirectMapping.set(record.sparseCounter, denseC);
18510
+ }
18511
+ });
18512
+ return {
18513
+ html: $.html(),
18514
+ counterRecords: newRecords,
18515
+ sparseToDirectMapping
18516
+ };
18517
+ }
18200
18518
  async function compileOpensteerSnapshot(options) {
18519
+ const liveCountersEnabled = await assignSparseCountersToLiveDom(options.engine, options.pageRef);
18201
18520
  const pageInfo = await options.engine.getPageInfo({ pageRef: options.pageRef });
18202
18521
  const mainSnapshot = await getMainDocumentSnapshot(options.engine, options.pageRef);
18203
18522
  const snapshotsByDocumentRef = await collectDocumentSnapshots(options.engine, mainSnapshot);
@@ -18216,13 +18535,23 @@ async function compileOpensteerSnapshot(options) {
18216
18535
  const compiledHtml = assignCounters(rawHtml, renderedNodes);
18217
18536
  const cleanedHtml = options.mode === "extraction" ? cleanForExtraction(compiledHtml.html) : cleanForAction(compiledHtml.html);
18218
18537
  const filtered = retainVisibleCounterRecords(cleanedHtml, compiledHtml.counterRecords);
18538
+ const dense = renumberCountersDensely(cleanedHtml, filtered);
18539
+ if (liveCountersEnabled && dense.sparseToDirectMapping.size > 0) {
18540
+ try {
18541
+ await syncDenseCountersToLiveDom(
18542
+ options.engine,
18543
+ options.pageRef,
18544
+ dense.sparseToDirectMapping
18545
+ );
18546
+ } catch {
18547
+ }
18548
+ }
18219
18549
  return {
18220
18550
  url: pageInfo.url,
18221
18551
  title: pageInfo.title,
18222
18552
  mode: options.mode,
18223
- html: cleanedHtml,
18224
- counters: [...filtered.values()].map(toPublicCounterRecord),
18225
- counterRecords: filtered
18553
+ html: dense.html,
18554
+ counters: [...dense.counterRecords.values()].map(toPublicCounterRecord)
18226
18555
  };
18227
18556
  }
18228
18557
  async function getMainDocumentSnapshot(engine, pageRef) {
@@ -18451,6 +18780,9 @@ function assignCounters(rawHtml, renderedNodes) {
18451
18780
  if (!rendered) {
18452
18781
  return;
18453
18782
  }
18783
+ const rawSparseCounter = el.attr(OPENSTEER_SPARSE_COUNTER_ATTR);
18784
+ el.removeAttr(OPENSTEER_SPARSE_COUNTER_ATTR);
18785
+ const sparseCounter = rawSparseCounter ? Number.parseInt(rawSparseCounter, 10) : void 0;
18454
18786
  const counter = nextCounter++;
18455
18787
  el.attr("c", String(counter));
18456
18788
  counterRecords.set(counter, {
@@ -18468,7 +18800,8 @@ function assignCounters(rawHtml, renderedNodes) {
18468
18800
  shadowDepth: rendered.shadowDepth,
18469
18801
  interactive: rendered.interactive,
18470
18802
  locator: rendered.locator,
18471
- anchor: rendered.anchor
18803
+ anchor: rendered.anchor,
18804
+ ...sparseCounter !== void 0 && Number.isFinite(sparseCounter) ? { sparseCounter } : {}
18472
18805
  });
18473
18806
  });
18474
18807
  return {
@@ -19829,6 +20162,7 @@ function diffInteractionTraces(left, right) {
19829
20162
 
19830
20163
  // src/sdk/runtime.ts
19831
20164
  var requireForAuthRecipeHook = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
20165
+ var MUTATION_CAPTURE_FINALIZE_TIMEOUT_MS = 5e3;
19832
20166
  var OpensteerSessionRuntime = class {
19833
20167
  workspace;
19834
20168
  rootPath;
@@ -19836,30 +20170,34 @@ var OpensteerSessionRuntime = class {
19836
20170
  injectedEngine;
19837
20171
  engineFactory;
19838
20172
  policy;
20173
+ injectedDescriptorStore;
20174
+ injectedExtractionDescriptorStore;
20175
+ registryOverrides;
19839
20176
  cleanupRootOnClose;
19840
20177
  sessionInfoBase;
19841
20178
  root;
19842
20179
  engine;
19843
20180
  dom;
19844
20181
  computer;
19845
- networkJournal = new NetworkJournal();
20182
+ networkHistory = new NetworkHistory();
19846
20183
  extractionDescriptors;
19847
20184
  sessionRef;
19848
20185
  pageRef;
19849
20186
  runId;
19850
- latestSnapshot;
19851
- backgroundNetworkPersistence = /* @__PURE__ */ new Set();
19852
20187
  cookieJars = /* @__PURE__ */ new Map();
19853
20188
  recipeCache = /* @__PURE__ */ new Map();
19854
20189
  ownsEngine = false;
19855
20190
  constructor(options) {
19856
- this.workspace = normalizeNamespace3(options.name);
20191
+ this.workspace = normalizeNamespace2(options.name);
19857
20192
  this.workspaceName = options.workspaceName?.trim() === void 0 || options.workspaceName?.trim().length === 0 ? void 0 : options.workspaceName.trim();
19858
20193
  this.root = options.workspace;
19859
20194
  this.rootPath = options.workspace?.rootPath ?? options.rootPath ?? path6__default.default.resolve(process.cwd(), ".opensteer", "temporary", crypto.randomUUID());
19860
20195
  this.injectedEngine = options.engine;
19861
20196
  this.engineFactory = options.engineFactory;
19862
20197
  this.policy = options.policy ?? defaultPolicy();
20198
+ this.injectedDescriptorStore = options.descriptorStore;
20199
+ this.injectedExtractionDescriptorStore = options.extractionDescriptorStore;
20200
+ this.registryOverrides = options.registryOverrides;
19863
20201
  this.cleanupRootOnClose = options.cleanupRootOnClose ?? options.workspace === void 0;
19864
20202
  this.sessionInfoBase = options.sessionInfo ?? {};
19865
20203
  if (this.injectedEngine === void 0 && this.engineFactory === void 0) {
@@ -19870,7 +20208,7 @@ var OpensteerSessionRuntime = class {
19870
20208
  const base = this.sessionInfoBase;
19871
20209
  return {
19872
20210
  provider: base.provider ?? {
19873
- kind: "local",
20211
+ mode: "local",
19874
20212
  ownership: "owned",
19875
20213
  engine: "playwright"
19876
20214
  },
@@ -19895,7 +20233,7 @@ var OpensteerSessionRuntime = class {
19895
20233
  }
19896
20234
  async open(input = {}, options = {}) {
19897
20235
  assertValidSemanticOperationInput("session.open", input);
19898
- if (input.workspace !== void 0 && normalizeNamespace3(input.workspace) !== this.workspace) {
20236
+ if (input.workspace !== void 0 && normalizeNamespace2(input.workspace) !== this.workspace) {
19899
20237
  throw new Error(
19900
20238
  `session.open requested workspace "${input.workspace}" but runtime is bound to "${this.workspace}"`
19901
20239
  );
@@ -19937,7 +20275,6 @@ var OpensteerSessionRuntime = class {
19937
20275
  timeout.throwIfAborted();
19938
20276
  this.sessionRef = sessionRef;
19939
20277
  this.pageRef = createdPage.data.pageRef;
19940
- this.latestSnapshot = void 0;
19941
20278
  await timeout.runStep(() => this.ensureSemantics());
19942
20279
  let frameRef2 = createdPage.frameRef;
19943
20280
  if (input.url !== void 0) {
@@ -20060,7 +20397,6 @@ var OpensteerSessionRuntime = class {
20060
20397
  })
20061
20398
  );
20062
20399
  this.pageRef = created.data.pageRef;
20063
- this.latestSnapshot = void 0;
20064
20400
  return this.readSessionState();
20065
20401
  },
20066
20402
  options
@@ -20103,7 +20439,6 @@ var OpensteerSessionRuntime = class {
20103
20439
  () => this.requireEngine().activatePage({ pageRef: input.pageRef })
20104
20440
  );
20105
20441
  this.pageRef = input.pageRef;
20106
- this.latestSnapshot = void 0;
20107
20442
  return this.readSessionState();
20108
20443
  },
20109
20444
  options
@@ -20167,7 +20502,6 @@ var OpensteerSessionRuntime = class {
20167
20502
  );
20168
20503
  }
20169
20504
  this.pageRef = activePageRef;
20170
- this.latestSnapshot = void 0;
20171
20505
  return {
20172
20506
  closedPageRef: targetPageRef,
20173
20507
  ...activePageRef === void 0 ? {} : { activePageRef },
@@ -20211,35 +20545,32 @@ var OpensteerSessionRuntime = class {
20211
20545
  assertValidSemanticOperationInput("page.goto", input);
20212
20546
  const pageRef = await this.ensurePageRef();
20213
20547
  const startedAt = Date.now();
20548
+ let mutationCaptureDiagnostics;
20214
20549
  try {
20215
- const { navigation, state } = await this.runWithOperationTimeout(
20550
+ const { navigation, state } = await this.runMutationCapturedOperation(
20216
20551
  "page.goto",
20217
- async (timeout) => {
20218
- const baselineRequestIds = await this.beginMutationCapture(timeout);
20219
- try {
20220
- const navigation2 = await this.navigatePage(
20221
- {
20222
- operation: "page.goto",
20223
- pageRef,
20224
- url: input.url
20225
- },
20226
- timeout
20227
- );
20228
- timeout.throwIfAborted();
20229
- this.latestSnapshot = void 0;
20230
- await this.completeMutationCapture(timeout, baselineRequestIds, input.networkTag);
20231
- return {
20232
- navigation: navigation2,
20233
- state: await timeout.runStep(() => this.readSessionState())
20234
- };
20235
- } catch (error) {
20236
- await this.completeMutationCapture(timeout, baselineRequestIds, input.networkTag).catch(
20237
- () => void 0
20238
- );
20239
- throw error;
20240
- }
20552
+ {
20553
+ ...input.captureNetwork === void 0 ? {} : { captureNetwork: input.captureNetwork },
20554
+ options
20555
+ },
20556
+ async (timeout) => {
20557
+ const navigation2 = await this.navigatePage(
20558
+ {
20559
+ operation: "page.goto",
20560
+ pageRef,
20561
+ url: input.url
20562
+ },
20563
+ timeout
20564
+ );
20565
+ timeout.throwIfAborted();
20566
+ return {
20567
+ navigation: navigation2,
20568
+ state: await timeout.runStep(() => this.readSessionState())
20569
+ };
20241
20570
  },
20242
- options
20571
+ (diagnostics) => {
20572
+ mutationCaptureDiagnostics = diagnostics;
20573
+ }
20243
20574
  );
20244
20575
  await this.appendTrace({
20245
20576
  operation: "page.goto",
@@ -20248,7 +20579,8 @@ var OpensteerSessionRuntime = class {
20248
20579
  outcome: "ok",
20249
20580
  data: {
20250
20581
  url: input.url,
20251
- state
20582
+ state,
20583
+ ...buildMutationCaptureTraceData(mutationCaptureDiagnostics)
20252
20584
  },
20253
20585
  context: buildRuntimeTraceContext({
20254
20586
  sessionRef: this.sessionRef,
@@ -20264,6 +20596,7 @@ var OpensteerSessionRuntime = class {
20264
20596
  completedAt: Date.now(),
20265
20597
  outcome: "error",
20266
20598
  error,
20599
+ data: buildMutationCaptureTraceData(mutationCaptureDiagnostics),
20267
20600
  context: buildRuntimeTraceContext({
20268
20601
  sessionRef: this.sessionRef,
20269
20602
  pageRef
@@ -20276,9 +20609,11 @@ var OpensteerSessionRuntime = class {
20276
20609
  assertValidSemanticOperationInput("page.evaluate", input);
20277
20610
  const pageRef = input.pageRef ?? await this.ensurePageRef();
20278
20611
  const startedAt = Date.now();
20612
+ let mutationCaptureDiagnostics;
20279
20613
  try {
20280
- const output = await this.runWithOperationTimeout(
20614
+ const output = await this.runMutationCapturedOperation(
20281
20615
  "page.evaluate",
20616
+ { options },
20282
20617
  async (timeout) => {
20283
20618
  const remainingMs = timeout.remainingMs();
20284
20619
  const evaluated = await timeout.runStep(
@@ -20294,7 +20629,9 @@ var OpensteerSessionRuntime = class {
20294
20629
  value: toJsonValueOrNull(evaluated.data)
20295
20630
  };
20296
20631
  },
20297
- options
20632
+ (diagnostics) => {
20633
+ mutationCaptureDiagnostics = diagnostics;
20634
+ }
20298
20635
  );
20299
20636
  await this.appendTrace({
20300
20637
  operation: "page.evaluate",
@@ -20303,7 +20640,8 @@ var OpensteerSessionRuntime = class {
20303
20640
  outcome: "ok",
20304
20641
  data: {
20305
20642
  pageRef: output.pageRef,
20306
- value: output.value
20643
+ value: output.value,
20644
+ ...buildMutationCaptureTraceData(mutationCaptureDiagnostics)
20307
20645
  },
20308
20646
  context: buildRuntimeTraceContext({
20309
20647
  sessionRef: this.sessionRef,
@@ -20318,6 +20656,7 @@ var OpensteerSessionRuntime = class {
20318
20656
  completedAt: Date.now(),
20319
20657
  outcome: "error",
20320
20658
  error,
20659
+ data: buildMutationCaptureTraceData(mutationCaptureDiagnostics),
20321
20660
  context: buildRuntimeTraceContext({
20322
20661
  sessionRef: this.sessionRef,
20323
20662
  pageRef
@@ -20396,7 +20735,6 @@ var OpensteerSessionRuntime = class {
20396
20735
  })
20397
20736
  );
20398
20737
  timeout.throwIfAborted();
20399
- this.latestSnapshot = compiled;
20400
20738
  const artifacts2 = await this.captureSnapshotArtifacts(
20401
20739
  pageRef,
20402
20740
  {
@@ -20546,8 +20884,7 @@ var OpensteerSessionRuntime = class {
20546
20884
  () => compileOpensteerExtractionFieldTargets({
20547
20885
  pageRef,
20548
20886
  schema: input.schema,
20549
- dom: this.requireDom(),
20550
- ...this.latestSnapshot?.counterRecords === void 0 ? {} : { latestSnapshotCounters: this.latestSnapshot.counterRecords }
20887
+ dom: this.requireDom()
20551
20888
  })
20552
20889
  );
20553
20890
  data = toCanonicalJsonValue(
@@ -20658,38 +20995,19 @@ var OpensteerSessionRuntime = class {
20658
20995
  }
20659
20996
  async queryNetwork(input = {}, options = {}) {
20660
20997
  assertValidSemanticOperationInput("network.query", input);
20661
- if (input.source !== "saved") {
20662
- await this.ensurePageRef();
20663
- }
20664
20998
  const root = await this.ensureRoot();
20665
20999
  const startedAt = Date.now();
20666
21000
  try {
20667
21001
  const output = await this.runWithOperationTimeout(
20668
21002
  "network.query",
20669
21003
  async (timeout) => {
20670
- if (input.source === "saved") {
20671
- await timeout.runStep(() => this.flushBackgroundNetworkPersistence());
20672
- return {
20673
- records: await timeout.runStep(
20674
- () => root.registry.savedNetwork.query({
20675
- ...input.recordId === void 0 ? {} : { recordId: input.recordId },
20676
- ...input.requestId === void 0 ? {} : { requestId: input.requestId },
20677
- ...input.actionId === void 0 ? {} : { actionId: input.actionId },
20678
- ...input.tag === void 0 ? {} : { tag: input.tag },
20679
- ...input.url === void 0 ? {} : { url: input.url },
20680
- ...input.hostname === void 0 ? {} : { hostname: input.hostname },
20681
- ...input.path === void 0 ? {} : { path: input.path },
20682
- ...input.method === void 0 ? {} : { method: input.method },
20683
- ...input.status === void 0 ? {} : { status: input.status },
20684
- ...input.resourceType === void 0 ? {} : { resourceType: input.resourceType },
20685
- ...input.includeBodies === void 0 ? {} : { includeBodies: input.includeBodies },
20686
- ...input.limit === void 0 ? {} : { limit: input.limit }
20687
- })
20688
- )
20689
- };
20690
- }
21004
+ await this.syncPersistedNetworkSelection(timeout, input, {
21005
+ includeBodies: input.includeBodies ?? false
21006
+ });
20691
21007
  return {
20692
- records: await this.queryLiveNetwork(input, timeout)
21008
+ records: await timeout.runStep(
21009
+ () => root.registry.savedNetwork.query(this.toSavedNetworkQueryInput(input))
21010
+ )
20693
21011
  };
20694
21012
  },
20695
21013
  options
@@ -20700,7 +21018,6 @@ var OpensteerSessionRuntime = class {
20700
21018
  completedAt: Date.now(),
20701
21019
  outcome: "ok",
20702
21020
  data: {
20703
- source: input.source ?? "live",
20704
21021
  includeBodies: input.includeBodies ?? false,
20705
21022
  limit: input.limit ?? 50,
20706
21023
  count: output.records.length
@@ -20726,56 +21043,42 @@ var OpensteerSessionRuntime = class {
20726
21043
  throw error;
20727
21044
  }
20728
21045
  }
20729
- async saveNetwork(input, options = {}) {
20730
- assertValidSemanticOperationInput("network.save", input);
20731
- await this.ensurePageRef();
21046
+ async tagNetwork(input, options = {}) {
21047
+ assertValidSemanticOperationInput("network.tag", input);
20732
21048
  const root = await this.ensureRoot();
21049
+ const filter = this.toQueryInputFromTagInput(input);
21050
+ const savedFilter = this.toSavedNetworkQueryInput(filter);
20733
21051
  const startedAt = Date.now();
20734
21052
  try {
20735
21053
  const output = await this.runWithOperationTimeout(
20736
- "network.save",
21054
+ "network.tag",
20737
21055
  async (timeout) => {
20738
- const records = await this.queryLiveNetwork(
20739
- {
20740
- includeBodies: true,
20741
- source: "live",
20742
- ...input.pageRef === void 0 ? {} : { pageRef: input.pageRef },
20743
- ...input.recordId === void 0 ? {} : { recordId: input.recordId },
20744
- ...input.requestId === void 0 ? {} : { requestId: input.requestId },
20745
- ...input.actionId === void 0 ? {} : { actionId: input.actionId },
20746
- ...input.url === void 0 ? {} : { url: input.url },
20747
- ...input.hostname === void 0 ? {} : { hostname: input.hostname },
20748
- ...input.path === void 0 ? {} : { path: input.path },
20749
- ...input.method === void 0 ? {} : { method: input.method },
20750
- ...input.status === void 0 ? {} : { status: input.status },
20751
- ...input.resourceType === void 0 ? {} : { resourceType: input.resourceType }
20752
- },
20753
- timeout,
20754
- { ignoreLimit: true, redactSecretHeaders: false }
20755
- );
20756
- this.networkJournal.addTag(records, input.tag);
21056
+ const records = await this.syncPersistedNetworkSelection(timeout, filter, {
21057
+ includeBodies: false
21058
+ });
21059
+ this.networkHistory.addTag(records, input.tag);
20757
21060
  return {
20758
- savedCount: await timeout.runStep(
20759
- () => root.registry.savedNetwork.save(records, input.tag)
21061
+ taggedCount: await timeout.runStep(
21062
+ () => root.registry.savedNetwork.tagByFilter(savedFilter, input.tag)
20760
21063
  )
20761
21064
  };
20762
21065
  },
20763
21066
  options
20764
21067
  );
20765
21068
  await this.appendTrace({
20766
- operation: "network.save",
21069
+ operation: "network.tag",
20767
21070
  startedAt,
20768
21071
  completedAt: Date.now(),
20769
21072
  outcome: "ok",
20770
21073
  data: {
20771
21074
  tag: input.tag,
20772
- savedCount: output.savedCount
21075
+ taggedCount: output.taggedCount
20773
21076
  }
20774
21077
  });
20775
21078
  return output;
20776
21079
  } catch (error) {
20777
21080
  await this.appendTrace({
20778
- operation: "network.save",
21081
+ operation: "network.tag",
20779
21082
  startedAt,
20780
21083
  completedAt: Date.now(),
20781
21084
  outcome: "error",
@@ -20792,7 +21095,31 @@ var OpensteerSessionRuntime = class {
20792
21095
  const output = await this.runWithOperationTimeout(
20793
21096
  "network.clear",
20794
21097
  async (timeout) => {
20795
- await timeout.runStep(() => this.flushBackgroundNetworkPersistence());
21098
+ if (this.sessionRef !== void 0) {
21099
+ if (input.capture !== void 0 || input.tag !== void 0) {
21100
+ const records = await this.queryLiveNetwork(
21101
+ {
21102
+ ...input.capture === void 0 ? {} : { capture: input.capture },
21103
+ ...input.tag === void 0 ? {} : { tag: input.tag }
21104
+ },
21105
+ timeout,
21106
+ {
21107
+ ignoreLimit: true
21108
+ }
21109
+ );
21110
+ this.networkHistory.tombstoneRequestIds(
21111
+ records.map((record) => record.record.requestId)
21112
+ );
21113
+ } else {
21114
+ const liveRequestIds = await this.readLiveRequestIds(timeout, {
21115
+ includeCurrentPageOnly: false
21116
+ });
21117
+ this.networkHistory.tombstoneRequestIds(liveRequestIds);
21118
+ }
21119
+ }
21120
+ if (input.capture === void 0 && input.tag === void 0) {
21121
+ this.networkHistory.tombstoneRequestIds(this.networkHistory.getKnownRequestIds());
21122
+ }
20796
21123
  return {
20797
21124
  clearedCount: await timeout.runStep(() => root.registry.savedNetwork.clear(input))
20798
21125
  };
@@ -20805,6 +21132,7 @@ var OpensteerSessionRuntime = class {
20805
21132
  completedAt: Date.now(),
20806
21133
  outcome: "ok",
20807
21134
  data: {
21135
+ ...input.capture === void 0 ? {} : { capture: input.capture },
20808
21136
  ...input.tag === void 0 ? {} : { tag: input.tag },
20809
21137
  clearedCount: output.clearedCount
20810
21138
  }
@@ -21509,7 +21837,6 @@ var OpensteerSessionRuntime = class {
21509
21837
  });
21510
21838
  const networkRecords = await this.queryLiveNetwork(
21511
21839
  {
21512
- source: "live",
21513
21840
  pageRef,
21514
21841
  ...input.network?.url === void 0 ? {} : { url: input.network.url },
21515
21842
  ...input.network?.hostname === void 0 ? {} : { hostname: input.network.hostname },
@@ -21525,7 +21852,7 @@ var OpensteerSessionRuntime = class {
21525
21852
  );
21526
21853
  const persistedNetwork = filterReverseObservationWindow(
21527
21854
  networkRecords.filter(isReverseRelevantNetworkRecord),
21528
- this.networkJournal,
21855
+ this.networkHistory,
21529
21856
  input.captureWindowMs
21530
21857
  );
21531
21858
  const fallbackSavedNetwork = persistedNetwork.length === 0 ? (await root.registry.savedNetwork.query({
@@ -21540,7 +21867,10 @@ var OpensteerSessionRuntime = class {
21540
21867
  const observationId = `observation:${crypto.randomUUID()}`;
21541
21868
  const networkTag = `reverse-case:${caseRecord.id}:${observationId}`;
21542
21869
  if (observationNetwork.length > 0) {
21543
- await root.registry.savedNetwork.save(observationNetwork, networkTag);
21870
+ await root.registry.savedNetwork.save(observationNetwork, {
21871
+ tag: networkTag,
21872
+ bodyWriteMode: input.network?.includeBodies === false ? "metadata-only" : "authoritative"
21873
+ });
21544
21874
  }
21545
21875
  const scriptArtifactIds = input.includeScripts === false ? [] : (await this.captureScriptsInternal(
21546
21876
  pageRef,
@@ -21636,7 +21966,7 @@ var OpensteerSessionRuntime = class {
21636
21966
  includeBodies: true,
21637
21967
  redactSecretHeaders: false
21638
21968
  }),
21639
- observedAt: this.networkJournal.getObservedAt(recordId)
21969
+ observedAt: this.networkHistory.getObservedAt(recordId)
21640
21970
  }))
21641
21971
  );
21642
21972
  const clusteredRecords = observationRecords.map((entry) => {
@@ -21984,7 +22314,9 @@ var OpensteerSessionRuntime = class {
21984
22314
  };
21985
22315
  }
21986
22316
  const bindings = /* @__PURE__ */ new Map();
21987
- const baselineRequestIds = await this.beginMutationCapture(timeout);
22317
+ const baselineRequestIds = await this.readLiveRequestIds(timeout, {
22318
+ includeCurrentPageOnly: true
22319
+ });
21988
22320
  const pageRef = explicitPageRef ?? await this.ensurePageRef();
21989
22321
  const validatorMap = new Map(
21990
22322
  packageRecord.payload.validators.map((validator) => [validator.id, validator])
@@ -22311,8 +22643,7 @@ var OpensteerSessionRuntime = class {
22311
22643
  const inferred = inferRequestPlanFromNetworkRecord(record, {
22312
22644
  recordId: candidate.recordId,
22313
22645
  key: input.key,
22314
- version: input.version,
22315
- lifecycle: "draft"
22646
+ version: input.version
22316
22647
  });
22317
22648
  const defaultHeaders = inferred.payload.endpoint.defaultHeaders === void 0 ? void 0 : stripManagedRequestHeaders(
22318
22649
  inferred.payload.endpoint.defaultHeaders,
@@ -22577,7 +22908,10 @@ var OpensteerSessionRuntime = class {
22577
22908
  timeout.signal
22578
22909
  )).filter((record) => !baselineRequestIds.has(record.record.requestId));
22579
22910
  if (deltaRecords.length > 0) {
22580
- await root.registry.savedNetwork.save(deltaRecords, `interaction:${pageRef}`);
22911
+ await root.registry.savedNetwork.save(deltaRecords, {
22912
+ tag: `interaction:${pageRef}`,
22913
+ bodyWriteMode: "authoritative"
22914
+ });
22581
22915
  }
22582
22916
  const trace = await root.registry.interactionTraces.write({
22583
22917
  key: input.key ?? buildInteractionTraceKey(pageInfo.url),
@@ -22920,7 +23254,7 @@ var OpensteerSessionRuntime = class {
22920
23254
  includeBodies: true
22921
23255
  });
22922
23256
  const inferred = inferRequestPlanFromNetworkRecord(source, input, {
22923
- ...this.networkJournal.getObservedAt(source.recordId) === void 0 ? {} : { observedAt: this.networkJournal.getObservedAt(source.recordId) }
23257
+ ...this.networkHistory.getObservedAt(source.recordId) === void 0 ? {} : { observedAt: this.networkHistory.getObservedAt(source.recordId) }
22924
23258
  });
22925
23259
  return timeout.runStep(
22926
23260
  () => root.registry.requestPlans.write({
@@ -22940,8 +23274,7 @@ var OpensteerSessionRuntime = class {
22940
23274
  recordId: input.recordId,
22941
23275
  id: record.id,
22942
23276
  key: record.key,
22943
- version: record.version,
22944
- lifecycle: record.lifecycle
23277
+ version: record.version
22945
23278
  }
22946
23279
  });
22947
23280
  return record;
@@ -22982,8 +23315,7 @@ var OpensteerSessionRuntime = class {
22982
23315
  data: {
22983
23316
  id: record.id,
22984
23317
  key: record.key,
22985
- version: record.version,
22986
- lifecycle: record.lifecycle
23318
+ version: record.version
22987
23319
  }
22988
23320
  });
22989
23321
  return record;
@@ -23029,8 +23361,7 @@ var OpensteerSessionRuntime = class {
23029
23361
  data: {
23030
23362
  id: record.id,
23031
23363
  key: record.key,
23032
- version: record.version,
23033
- lifecycle: record.lifecycle
23364
+ version: record.version
23034
23365
  }
23035
23366
  });
23036
23367
  return record;
@@ -23657,34 +23988,38 @@ var OpensteerSessionRuntime = class {
23657
23988
  assertValidSemanticOperationInput("computer.execute", input);
23658
23989
  const pageRef = await this.ensurePageRef();
23659
23990
  const startedAt = Date.now();
23991
+ let mutationCaptureDiagnostics;
23992
+ let boundaryDiagnostics;
23660
23993
  try {
23661
- const { artifacts, output } = await this.runWithOperationTimeout(
23994
+ const { artifacts, output } = await this.runMutationCapturedOperation(
23662
23995
  "computer.execute",
23996
+ {
23997
+ ...input.captureNetwork === void 0 ? {} : { captureNetwork: input.captureNetwork },
23998
+ options
23999
+ },
23663
24000
  async (timeout) => {
23664
- const baselineRequestIds = await this.beginMutationCapture(timeout);
23665
24001
  try {
23666
24002
  const output2 = await this.requireComputer().execute({
23667
24003
  pageRef,
23668
24004
  input,
23669
24005
  timeout
23670
24006
  });
24007
+ boundaryDiagnostics = takeActionBoundaryDiagnostics(timeout.signal);
23671
24008
  timeout.throwIfAborted();
23672
24009
  this.pageRef = output2.pageRef;
23673
- this.latestSnapshot = void 0;
23674
- await this.completeMutationCapture(timeout, baselineRequestIds, input.networkTag);
23675
24010
  const artifacts2 = await this.persistComputerArtifacts(output2, timeout);
23676
24011
  return {
23677
24012
  artifacts: { manifests: artifacts2.manifests },
23678
24013
  output: artifacts2.output
23679
24014
  };
23680
24015
  } catch (error) {
23681
- await this.completeMutationCapture(timeout, baselineRequestIds, input.networkTag).catch(
23682
- () => void 0
23683
- );
24016
+ boundaryDiagnostics ??= takeActionBoundaryDiagnostics(timeout.signal);
23684
24017
  throw error;
23685
24018
  }
23686
24019
  },
23687
- options
24020
+ (diagnostics) => {
24021
+ mutationCaptureDiagnostics = diagnostics;
24022
+ }
23688
24023
  );
23689
24024
  await this.appendTrace({
23690
24025
  operation: "computer.execute",
@@ -23700,6 +24035,8 @@ var OpensteerSessionRuntime = class {
23700
24035
  nativeViewport: output.nativeViewport,
23701
24036
  displayScale: output.displayScale,
23702
24037
  timing: output.timing,
24038
+ ...boundaryDiagnostics === void 0 ? {} : { settle: boundaryDiagnostics },
24039
+ ...buildMutationCaptureTraceData(mutationCaptureDiagnostics),
23703
24040
  ...output.trace === void 0 ? {} : { trace: output.trace }
23704
24041
  },
23705
24042
  context: buildRuntimeTraceContext({
@@ -23718,6 +24055,10 @@ var OpensteerSessionRuntime = class {
23718
24055
  completedAt: Date.now(),
23719
24056
  outcome: "error",
23720
24057
  error,
24058
+ data: {
24059
+ ...boundaryDiagnostics === void 0 ? {} : { settle: boundaryDiagnostics },
24060
+ ...buildMutationCaptureTraceData(mutationCaptureDiagnostics)
24061
+ },
23721
24062
  context: buildRuntimeTraceContext({
23722
24063
  sessionRef: this.sessionRef,
23723
24064
  pageRef: this.pageRef
@@ -23736,7 +24077,7 @@ var OpensteerSessionRuntime = class {
23736
24077
  await this.runWithOperationTimeout(
23737
24078
  "session.close",
23738
24079
  async (timeout) => {
23739
- await timeout.runStep(() => this.flushBackgroundNetworkPersistence());
24080
+ await timeout.runStep(() => this.flushPersistedNetworkHistory());
23740
24081
  if (engine === void 0) {
23741
24082
  return;
23742
24083
  }
@@ -23805,10 +24146,7 @@ var OpensteerSessionRuntime = class {
23805
24146
  }
23806
24147
  async disconnect() {
23807
24148
  try {
23808
- await this.flushBackgroundNetworkPersistence();
23809
- if (this.sessionRef !== void 0 && this.pageRef !== void 0) {
23810
- await this.saveNetwork({ tag: "auto" }).catch(() => void 0);
23811
- }
24149
+ await this.flushPersistedNetworkHistory();
23812
24150
  } finally {
23813
24151
  await this.resetRuntimeState({
23814
24152
  disposeEngine: true
@@ -23824,33 +24162,38 @@ var OpensteerSessionRuntime = class {
23824
24162
  async runDomAction(operation, input, executor, options = {}) {
23825
24163
  const pageRef = await this.ensurePageRef();
23826
24164
  const startedAt = Date.now();
24165
+ let mutationCaptureDiagnostics;
24166
+ let boundaryDiagnostics;
23827
24167
  try {
23828
- const { executed, preparedTarget } = await this.runWithOperationTimeout(
24168
+ const { executed, preparedTarget } = await this.runMutationCapturedOperation(
23829
24169
  operation,
24170
+ {
24171
+ ...input.captureNetwork === void 0 ? {} : { captureNetwork: input.captureNetwork },
24172
+ options
24173
+ },
23830
24174
  async (timeout) => {
23831
- const baselineRequestIds = await this.beginMutationCapture(timeout);
24175
+ const preparedTarget2 = await this.prepareDomTarget(
24176
+ pageRef,
24177
+ operation,
24178
+ input.target,
24179
+ input.persistAsDescription,
24180
+ timeout
24181
+ );
23832
24182
  try {
23833
- const preparedTarget2 = await this.prepareDomTarget(
23834
- pageRef,
23835
- operation,
23836
- input.target,
23837
- input.persistAsDescription,
23838
- timeout
23839
- );
23840
24183
  const executed2 = await executor(pageRef, preparedTarget2.target, timeout);
23841
- await this.completeMutationCapture(timeout, baselineRequestIds, input.networkTag);
24184
+ boundaryDiagnostics = takeActionBoundaryDiagnostics(timeout.signal);
23842
24185
  return {
23843
24186
  executed: executed2,
23844
24187
  preparedTarget: preparedTarget2
23845
24188
  };
23846
24189
  } catch (error) {
23847
- await this.completeMutationCapture(timeout, baselineRequestIds, input.networkTag).catch(
23848
- () => void 0
23849
- );
24190
+ boundaryDiagnostics ??= takeActionBoundaryDiagnostics(timeout.signal);
23850
24191
  throw error;
23851
24192
  }
23852
24193
  },
23853
- options
24194
+ (diagnostics) => {
24195
+ mutationCaptureDiagnostics = diagnostics;
24196
+ }
23854
24197
  );
23855
24198
  const output = toOpensteerActionResult(executed.result, preparedTarget.persistedDescription);
23856
24199
  await this.appendTrace({
@@ -23861,7 +24204,9 @@ var OpensteerSessionRuntime = class {
23861
24204
  data: {
23862
24205
  target: output.target,
23863
24206
  ...output.point === void 0 ? {} : { point: output.point },
23864
- ...output.persistedDescription === void 0 ? {} : { persistedDescription: output.persistedDescription }
24207
+ ...output.persistedDescription === void 0 ? {} : { persistedDescription: output.persistedDescription },
24208
+ ...boundaryDiagnostics === void 0 ? {} : { settle: boundaryDiagnostics },
24209
+ ...buildMutationCaptureTraceData(mutationCaptureDiagnostics)
23865
24210
  },
23866
24211
  context: buildRuntimeTraceContext({
23867
24212
  sessionRef: this.sessionRef,
@@ -23879,6 +24224,10 @@ var OpensteerSessionRuntime = class {
23879
24224
  completedAt: Date.now(),
23880
24225
  outcome: "error",
23881
24226
  error,
24227
+ data: {
24228
+ ...boundaryDiagnostics === void 0 ? {} : { settle: boundaryDiagnostics },
24229
+ ...buildMutationCaptureTraceData(mutationCaptureDiagnostics)
24230
+ },
23882
24231
  context: buildRuntimeTraceContext({
23883
24232
  sessionRef: this.sessionRef,
23884
24233
  pageRef
@@ -23900,19 +24249,15 @@ var OpensteerSessionRuntime = class {
23900
24249
  };
23901
24250
  }
23902
24251
  if (target.kind === "element") {
23903
- const counter = this.latestSnapshot?.counterRecords.get(target.element);
23904
- if (!counter) {
23905
- throw new Error(`no counter ${String(target.element)} is available in the latest snapshot`);
23906
- }
24252
+ const elementTarget = {
24253
+ kind: "selector",
24254
+ selector: `[c="${String(target.element)}"]`
24255
+ };
23907
24256
  const resolved2 = await timeout.runStep(
23908
24257
  () => this.requireDom().resolveTarget({
23909
24258
  pageRef,
23910
24259
  method,
23911
- target: {
23912
- kind: "live",
23913
- locator: counter.locator,
23914
- anchor: counter.anchor
23915
- }
24260
+ target: elementTarget
23916
24261
  })
23917
24262
  );
23918
24263
  const stablePath2 = resolved2.replayPath ?? await timeout.runStep(
@@ -23970,11 +24315,11 @@ var OpensteerSessionRuntime = class {
23970
24315
  };
23971
24316
  }
23972
24317
  async queryLiveNetwork(input, timeout, options = {}) {
23973
- const requestIds = resolveLiveQueryRequestIds(input, this.networkJournal);
24318
+ const requestIds = resolveLiveQueryRequestIds(input, this.networkHistory);
23974
24319
  if (requestIds !== void 0 && requestIds.length === 0) {
23975
24320
  return [];
23976
24321
  }
23977
- const pageRef = resolveLiveQueryPageRef(input, this.pageRef, requestIds, this.networkJournal);
24322
+ const pageRef = resolveLiveQueryPageRef(input, this.pageRef, requestIds, this.networkHistory);
23978
24323
  const includeCurrentPageOnly = pageRef === void 0 && input.recordId === void 0;
23979
24324
  const metadataRecords = await timeout.runStep(
23980
24325
  () => this.readLiveNetworkRecords(
@@ -23992,7 +24337,7 @@ var OpensteerSessionRuntime = class {
23992
24337
  const filtered = filterNetworkQueryRecords(metadataRecords, {
23993
24338
  ...input.recordId === void 0 ? {} : { recordId: input.recordId },
23994
24339
  ...input.requestId === void 0 ? {} : { requestId: input.requestId },
23995
- ...input.actionId === void 0 ? {} : { actionId: input.actionId },
24340
+ ...input.capture === void 0 ? {} : { capture: input.capture },
23996
24341
  ...input.tag === void 0 ? {} : { tag: input.tag },
23997
24342
  ...input.url === void 0 ? {} : { url: input.url },
23998
24343
  ...input.hostname === void 0 ? {} : { hostname: input.hostname },
@@ -24001,7 +24346,7 @@ var OpensteerSessionRuntime = class {
24001
24346
  ...input.status === void 0 ? {} : { status: input.status },
24002
24347
  ...input.resourceType === void 0 ? {} : { resourceType: input.resourceType }
24003
24348
  });
24004
- const sorted = sortLiveNetworkRecords(filtered, this.networkJournal);
24349
+ const sorted = sortLiveNetworkRecords(filtered, this.networkHistory);
24005
24350
  const limit = options.ignoreLimit ? sorted.length : Math.max(1, Math.min(input.limit ?? 50, 200));
24006
24351
  const limited = sorted.slice(0, limit);
24007
24352
  if (!(input.includeBodies ?? false) || limited.length === 0) {
@@ -24198,40 +24543,95 @@ var OpensteerSessionRuntime = class {
24198
24543
  artifactId: manifest.artifactId
24199
24544
  };
24200
24545
  }
24201
- beginMutationCapture(timeout) {
24202
- return this.readLiveRequestIds(timeout, {
24203
- includeCurrentPageOnly: true
24204
- });
24205
- }
24206
- async completeMutationCapture(timeout, baselineRequestIds, networkTag) {
24207
- const records = await timeout.runStep(
24208
- () => this.readLiveNetworkRecords(
24209
- {
24210
- includeBodies: false,
24211
- includeCurrentPageOnly: true
24546
+ async runMutationCapturedOperation(operation, input, execute, onFinalized) {
24547
+ let plan;
24548
+ try {
24549
+ const result = await this.runWithOperationTimeout(
24550
+ operation,
24551
+ async (timeout) => {
24552
+ plan = await this.beginMutationCapture(timeout, input.captureNetwork);
24553
+ return execute(timeout);
24212
24554
  },
24213
- timeout.signal
24214
- )
24555
+ input.options
24556
+ );
24557
+ const diagnostics = await this.finalizeMutationCaptureBestEffort(plan);
24558
+ onFinalized?.(diagnostics);
24559
+ return result;
24560
+ } catch (error) {
24561
+ const diagnostics = await this.finalizeMutationCaptureBestEffort(plan);
24562
+ onFinalized?.(diagnostics);
24563
+ throw error;
24564
+ }
24565
+ }
24566
+ async beginMutationCapture(timeout, capture) {
24567
+ if (capture === void 0) {
24568
+ return void 0;
24569
+ }
24570
+ return {
24571
+ baselineRequestIds: await this.readLiveRequestIds(timeout, {
24572
+ includeCurrentPageOnly: true
24573
+ }),
24574
+ capture
24575
+ };
24576
+ }
24577
+ async finalizeMutationCaptureBestEffort(plan) {
24578
+ if (plan === void 0) {
24579
+ return {};
24580
+ }
24581
+ try {
24582
+ await withDetachedTimeoutSignal(MUTATION_CAPTURE_FINALIZE_TIMEOUT_MS, async (signal) => {
24583
+ await this.completeMutationCaptureWithSignal(signal, plan);
24584
+ });
24585
+ return {};
24586
+ } catch (error) {
24587
+ return {
24588
+ finalizeError: normalizeOpensteerError(error)
24589
+ };
24590
+ }
24591
+ }
24592
+ async completeMutationCaptureWithSignal(signal, plan) {
24593
+ const records = await this.readLiveNetworkRecords(
24594
+ {
24595
+ includeBodies: false,
24596
+ includeCurrentPageOnly: true
24597
+ },
24598
+ signal
24215
24599
  );
24216
- const delta = records.filter((record) => !baselineRequestIds.has(record.record.requestId));
24600
+ const delta = records.filter((record) => !plan.baselineRequestIds.has(record.record.requestId));
24217
24601
  if (delta.length === 0) {
24218
24602
  return;
24219
24603
  }
24220
- this.networkJournal.assignActionId(delta, `action:${crypto.randomUUID()}`);
24221
- if (networkTag === void 0) {
24222
- return;
24223
- }
24224
- this.networkJournal.addTag(delta, networkTag);
24225
- this.scheduleBackgroundNetworkSaveByRequestIds(
24604
+ this.networkHistory.assignCapture(delta, plan.capture);
24605
+ await this.persistLiveRequestIdsWithSignal(
24226
24606
  delta.map((record) => record.record.requestId),
24227
- networkTag
24607
+ signal,
24608
+ {
24609
+ includeCurrentPageOnly: true
24610
+ }
24228
24611
  );
24229
24612
  }
24230
24613
  async resolveNetworkRecordByRecordId(recordId, timeout, options) {
24231
24614
  const root = await this.ensureRoot();
24615
+ await this.syncPersistedNetworkSelection(
24616
+ timeout,
24617
+ {
24618
+ recordId,
24619
+ includeBodies: options.includeBodies
24620
+ },
24621
+ {
24622
+ includeBodies: options.includeBodies
24623
+ }
24624
+ );
24625
+ const saved = await timeout.runStep(
24626
+ () => root.registry.savedNetwork.getByRecordId(recordId, {
24627
+ includeBodies: options.includeBodies
24628
+ })
24629
+ );
24630
+ if (saved) {
24631
+ return saved;
24632
+ }
24232
24633
  const live = await this.queryLiveNetwork(
24233
24634
  {
24234
- source: "live",
24235
24635
  recordId,
24236
24636
  includeBodies: options.includeBodies,
24237
24637
  limit: 1
@@ -24242,24 +24642,15 @@ var OpensteerSessionRuntime = class {
24242
24642
  ...options.redactSecretHeaders === void 0 ? {} : { redactSecretHeaders: options.redactSecretHeaders }
24243
24643
  }
24244
24644
  );
24245
- if (live.length > 0) {
24645
+ if (live[0] !== void 0) {
24246
24646
  return live[0];
24247
24647
  }
24248
- await timeout.runStep(() => this.flushBackgroundNetworkPersistence());
24249
- const saved = await timeout.runStep(
24250
- () => root.registry.savedNetwork.getByRecordId(recordId, {
24251
- includeBodies: options.includeBodies
24252
- })
24253
- );
24254
- if (!saved) {
24255
- throw new OpensteerProtocolError("not-found", `network record ${recordId} was not found`, {
24256
- details: {
24257
- recordId,
24258
- kind: "network-record"
24259
- }
24260
- });
24261
- }
24262
- return saved;
24648
+ throw new OpensteerProtocolError("not-found", `network record ${recordId} was not found`, {
24649
+ details: {
24650
+ recordId,
24651
+ kind: "network-record"
24652
+ }
24653
+ });
24263
24654
  }
24264
24655
  resolveCurrentStateSource() {
24265
24656
  const ownership = this.sessionInfoBase.provider?.ownership;
@@ -24499,7 +24890,6 @@ var OpensteerSessionRuntime = class {
24499
24890
  timeout.throwIfAborted();
24500
24891
  const records = await this.queryLiveNetwork(
24501
24892
  {
24502
- source: "live",
24503
24893
  pageRef,
24504
24894
  url,
24505
24895
  method,
@@ -24530,7 +24920,6 @@ var OpensteerSessionRuntime = class {
24530
24920
  timeout.throwIfAborted();
24531
24921
  const records = await this.queryLiveNetwork(
24532
24922
  {
24533
- source: "live",
24534
24923
  pageRef,
24535
24924
  ...filter.url === void 0 ? {} : { url: filter.url },
24536
24925
  ...filter.host === void 0 ? {} : { hostname: filter.host },
@@ -24596,12 +24985,12 @@ var OpensteerSessionRuntime = class {
24596
24985
  };
24597
24986
  }
24598
24987
  }
24599
- async readLiveNetworkRecords(input, signal) {
24988
+ async readBrowserNetworkRecords(input, signal) {
24600
24989
  const sessionRef = this.sessionRef;
24601
24990
  if (!sessionRef) {
24602
24991
  throw new Error("Opensteer session is not initialized");
24603
24992
  }
24604
- const records = await this.requireEngine().getNetworkRecords({
24993
+ return this.requireEngine().getNetworkRecords({
24605
24994
  sessionRef,
24606
24995
  ...input.includeCurrentPageOnly === false || input.pageRef !== void 0 ? input.pageRef === void 0 ? {} : { pageRef: input.pageRef } : this.pageRef === void 0 ? {} : { pageRef: this.pageRef },
24607
24996
  ...input.requestIds === void 0 ? {} : { requestIds: input.requestIds },
@@ -24614,10 +25003,103 @@ var OpensteerSessionRuntime = class {
24614
25003
  includeBodies: input.includeBodies,
24615
25004
  signal
24616
25005
  });
24617
- return this.networkJournal.sync(records, {
25006
+ }
25007
+ async readLiveNetworkRecords(input, signal) {
25008
+ const records = await this.readBrowserNetworkRecords(input, signal);
25009
+ return this.networkHistory.materialize(records, {
24618
25010
  redactSecretHeaders: input.redactSecretHeaders ?? true
24619
25011
  });
24620
25012
  }
25013
+ async persistLiveRequestIds(requestIds, timeout, options) {
25014
+ return timeout.runStep(
25015
+ () => this.persistLiveRequestIdsWithSignal(requestIds, timeout.signal, options)
25016
+ );
25017
+ }
25018
+ async persistLiveRequestIdsWithSignal(requestIds, signal, options) {
25019
+ if (requestIds.length === 0) {
25020
+ return [];
25021
+ }
25022
+ const root = await this.ensureRoot();
25023
+ const browserRecords = await this.readBrowserNetworkRecords(
25024
+ {
25025
+ includeBodies: true,
25026
+ includeCurrentPageOnly: options.includeCurrentPageOnly,
25027
+ ...options.pageRef === void 0 ? {} : { pageRef: options.pageRef },
25028
+ requestIds
25029
+ },
25030
+ signal
25031
+ );
25032
+ return this.networkHistory.persist(browserRecords, root.registry.savedNetwork, {
25033
+ bodyWriteMode: "authoritative",
25034
+ redactSecretHeaders: false
25035
+ });
25036
+ }
25037
+ async syncPersistedNetworkSelection(timeout, input, options) {
25038
+ if (this.sessionRef === void 0) {
25039
+ return [];
25040
+ }
25041
+ const requestIds = resolveLiveQueryRequestIds(input, this.networkHistory);
25042
+ if (requestIds !== void 0 && requestIds.length === 0) {
25043
+ return [];
25044
+ }
25045
+ const pageRef = resolveLiveQueryPageRef(input, this.pageRef, requestIds, this.networkHistory);
25046
+ const includeCurrentPageOnly = pageRef === void 0 && input.recordId === void 0;
25047
+ const browserRecords = await timeout.runStep(
25048
+ () => this.readBrowserNetworkRecords(
25049
+ {
25050
+ ...pageRef === void 0 ? {} : { pageRef },
25051
+ ...requestIds === void 0 ? {} : { requestIds },
25052
+ ...input.url === void 0 ? {} : { url: input.url },
25053
+ ...input.hostname === void 0 ? {} : { hostname: input.hostname },
25054
+ ...input.path === void 0 ? {} : { path: input.path },
25055
+ ...input.method === void 0 ? {} : { method: input.method },
25056
+ ...input.status === void 0 ? {} : { status: input.status },
25057
+ ...input.resourceType === void 0 ? {} : { resourceType: input.resourceType },
25058
+ includeBodies: options.includeBodies,
25059
+ includeCurrentPageOnly
25060
+ },
25061
+ timeout.signal
25062
+ )
25063
+ );
25064
+ const root = await this.ensureRoot();
25065
+ return timeout.runStep(
25066
+ () => this.networkHistory.persist(browserRecords, root.registry.savedNetwork, {
25067
+ bodyWriteMode: options.includeBodies ? "authoritative" : "metadata-only",
25068
+ redactSecretHeaders: false
25069
+ })
25070
+ );
25071
+ }
25072
+ toSavedNetworkQueryInput(input) {
25073
+ return {
25074
+ ...input.pageRef === void 0 ? {} : { pageRef: input.pageRef },
25075
+ ...input.recordId === void 0 ? {} : { recordId: input.recordId },
25076
+ ...input.requestId === void 0 ? {} : { requestId: input.requestId },
25077
+ ...input.capture === void 0 ? {} : { capture: input.capture },
25078
+ ...input.tag === void 0 ? {} : { tag: input.tag },
25079
+ ...input.url === void 0 ? {} : { url: input.url },
25080
+ ...input.hostname === void 0 ? {} : { hostname: input.hostname },
25081
+ ...input.path === void 0 ? {} : { path: input.path },
25082
+ ...input.method === void 0 ? {} : { method: input.method },
25083
+ ...input.status === void 0 ? {} : { status: input.status },
25084
+ ...input.resourceType === void 0 ? {} : { resourceType: input.resourceType },
25085
+ ...input.includeBodies === void 0 ? {} : { includeBodies: input.includeBodies },
25086
+ ...input.limit === void 0 ? {} : { limit: input.limit }
25087
+ };
25088
+ }
25089
+ toQueryInputFromTagInput(input) {
25090
+ return {
25091
+ ...input.pageRef === void 0 ? {} : { pageRef: input.pageRef },
25092
+ ...input.recordId === void 0 ? {} : { recordId: input.recordId },
25093
+ ...input.requestId === void 0 ? {} : { requestId: input.requestId },
25094
+ ...input.capture === void 0 ? {} : { capture: input.capture },
25095
+ ...input.url === void 0 ? {} : { url: input.url },
25096
+ ...input.hostname === void 0 ? {} : { hostname: input.hostname },
25097
+ ...input.path === void 0 ? {} : { path: input.path },
25098
+ ...input.method === void 0 ? {} : { method: input.method },
25099
+ ...input.status === void 0 ? {} : { status: input.status },
25100
+ ...input.resourceType === void 0 ? {} : { resourceType: input.resourceType }
25101
+ };
25102
+ }
24621
25103
  async readLiveRequestIds(timeout, options) {
24622
25104
  const records = await timeout.runStep(
24623
25105
  () => this.readLiveNetworkRecords(
@@ -24641,7 +25123,17 @@ var OpensteerSessionRuntime = class {
24641
25123
  )
24642
25124
  );
24643
25125
  const delta = records.filter((record) => !baselineRequestIds.has(record.record.requestId));
24644
- return sortLiveNetworkRecords(delta, this.networkJournal)[0]?.recordId;
25126
+ if (delta.length === 0) {
25127
+ return void 0;
25128
+ }
25129
+ await this.persistLiveRequestIds(
25130
+ delta.map((record) => record.record.requestId),
25131
+ timeout,
25132
+ {
25133
+ includeCurrentPageOnly: options.includeCurrentPageOnly
25134
+ }
25135
+ );
25136
+ return sortLiveNetworkRecords(delta, this.networkHistory)[0]?.recordId;
24645
25137
  }
24646
25138
  async executeTransportRequestWithJournal(request, timeout, sessionRef) {
24647
25139
  const baselineRequestIds = await this.readLiveRequestIds(timeout, {
@@ -24925,7 +25417,6 @@ var OpensteerSessionRuntime = class {
24925
25417
  const syntheticSessionRef = binding?.sessionRef ?? createSessionRef(`${transportLabel}-${this.workspace}`);
24926
25418
  const record = {
24927
25419
  recordId,
24928
- source: "saved",
24929
25420
  savedAt: now,
24930
25421
  record: {
24931
25422
  kind: "http",
@@ -24947,7 +25438,10 @@ var OpensteerSessionRuntime = class {
24947
25438
  ...response.body === void 0 ? {} : { responseBody: toProtocolBodyPayload(response.body) }
24948
25439
  }
24949
25440
  };
24950
- await root.registry.savedNetwork.save([record], tag);
25441
+ await root.registry.savedNetwork.save([record], {
25442
+ bodyWriteMode: "authoritative",
25443
+ ...tag === void 0 ? {} : { tag }
25444
+ });
24951
25445
  return recordId;
24952
25446
  }
24953
25447
  async executeResolvedRequestPlan(plan, input, timeout, binding) {
@@ -25155,7 +25649,7 @@ var OpensteerSessionRuntime = class {
25155
25649
  }
25156
25650
  async touchRequestPlanFreshness(plan) {
25157
25651
  const freshness = touchFreshness(plan.freshness);
25158
- await this.requireRoot().registry.requestPlans.updateMetadata({
25652
+ await this.requireRoot().registry.requestPlans.updateFreshness({
25159
25653
  id: plan.id,
25160
25654
  ...freshness === void 0 ? {} : { freshness }
25161
25655
  });
@@ -25320,7 +25814,6 @@ var OpensteerSessionRuntime = class {
25320
25814
  const record = await pollUntilResult(timeout, async () => {
25321
25815
  const matches = await this.queryLiveNetwork(
25322
25816
  {
25323
- source: "live",
25324
25817
  ...step.url === void 0 ? {} : { url: interpolateTemplate(step.url, variables) },
25325
25818
  ...step.hostname === void 0 ? {} : { hostname: interpolateTemplate(step.hostname, variables) },
25326
25819
  ...step.path === void 0 ? {} : { path: interpolateTemplate(step.path, variables) },
@@ -25778,37 +26271,7 @@ var OpensteerSessionRuntime = class {
25778
26271
  const pageUrl = step.pageUrl === void 0 ? void 0 : interpolateTemplate(step.pageUrl, variables);
25779
26272
  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;
25780
26273
  }
25781
- scheduleBackgroundNetworkSaveByRequestIds(requestIds, tag) {
25782
- const task = (async () => {
25783
- const root = await this.ensureRoot();
25784
- const requestIdSet = new Set(requestIds);
25785
- const records = await this.readLiveNetworkRecords(
25786
- {
25787
- includeBodies: true,
25788
- includeCurrentPageOnly: false,
25789
- ...this.pageRef === void 0 ? {} : { pageRef: this.pageRef },
25790
- requestIds,
25791
- redactSecretHeaders: false
25792
- },
25793
- new AbortController().signal
25794
- );
25795
- const filtered = records.filter((record) => requestIdSet.has(record.record.requestId));
25796
- if (filtered.length === 0) {
25797
- return;
25798
- }
25799
- await root.registry.savedNetwork.save(filtered, tag);
25800
- })();
25801
- this.backgroundNetworkPersistence.add(task);
25802
- task.finally(() => {
25803
- this.backgroundNetworkPersistence.delete(task);
25804
- });
25805
- void task.catch(() => void 0);
25806
- }
25807
- async flushBackgroundNetworkPersistence() {
25808
- if (this.backgroundNetworkPersistence.size === 0) {
25809
- return;
25810
- }
25811
- await Promise.all([...this.backgroundNetworkPersistence]);
26274
+ async flushPersistedNetworkHistory() {
25812
26275
  }
25813
26276
  toDomTargetRef(target) {
25814
26277
  if (target.kind === "description") {
@@ -25823,22 +26286,35 @@ var OpensteerSessionRuntime = class {
25823
26286
  selector: target.selector
25824
26287
  };
25825
26288
  }
25826
- const counter = this.latestSnapshot?.counterRecords.get(target.element);
25827
- if (!counter) {
25828
- throw new Error(`no counter ${String(target.element)} is available in the latest snapshot`);
25829
- }
25830
26289
  return {
25831
- kind: "live",
25832
- locator: counter.locator,
25833
- anchor: counter.anchor
26290
+ kind: "selector",
26291
+ selector: `[c="${String(target.element)}"]`
25834
26292
  };
25835
26293
  }
25836
26294
  async ensureRoot() {
25837
- this.root ??= await createFilesystemOpensteerWorkspace({
25838
- rootPath: this.rootPath,
25839
- ...this.workspaceName === void 0 ? {} : { workspace: this.workspaceName },
25840
- scope: this.workspaceName === void 0 ? "temporary" : "workspace"
25841
- });
26295
+ if (!this.root) {
26296
+ const workspace = await createFilesystemOpensteerWorkspace({
26297
+ rootPath: this.rootPath,
26298
+ ...this.workspaceName === void 0 ? {} : { workspace: this.workspaceName },
26299
+ scope: this.workspaceName === void 0 ? "temporary" : "workspace"
26300
+ });
26301
+ if (this.registryOverrides) {
26302
+ const overrides = this.registryOverrides;
26303
+ this.root = {
26304
+ ...workspace,
26305
+ registry: {
26306
+ ...workspace.registry,
26307
+ ...overrides.requestPlans === void 0 ? {} : { requestPlans: overrides.requestPlans },
26308
+ ...overrides.authRecipes === void 0 ? {} : { authRecipes: overrides.authRecipes },
26309
+ ...overrides.recipes === void 0 ? {} : { recipes: overrides.recipes },
26310
+ ...overrides.reverseCases === void 0 ? {} : { reverseCases: overrides.reverseCases },
26311
+ ...overrides.reversePackages === void 0 ? {} : { reversePackages: overrides.reversePackages }
26312
+ }
26313
+ };
26314
+ } else {
26315
+ this.root = workspace;
26316
+ }
26317
+ }
25842
26318
  return this.root;
25843
26319
  }
25844
26320
  async ensureEngine(overrides = {}) {
@@ -25864,6 +26340,7 @@ var OpensteerSessionRuntime = class {
25864
26340
  engine,
25865
26341
  root,
25866
26342
  namespace: this.workspace,
26343
+ ...this.injectedDescriptorStore === void 0 ? {} : { descriptorStore: this.injectedDescriptorStore },
25867
26344
  policy: this.policy
25868
26345
  });
25869
26346
  this.computer = createComputerUseRuntime({
@@ -25871,7 +26348,7 @@ var OpensteerSessionRuntime = class {
25871
26348
  dom: this.dom,
25872
26349
  policy: this.policy
25873
26350
  });
25874
- this.extractionDescriptors = createOpensteerExtractionDescriptorStore({
26351
+ this.extractionDescriptors = this.injectedExtractionDescriptorStore ?? createOpensteerExtractionDescriptorStore({
25875
26352
  root,
25876
26353
  namespace: this.workspace
25877
26354
  });
@@ -26088,11 +26565,9 @@ var OpensteerSessionRuntime = class {
26088
26565
  }
26089
26566
  async resetRuntimeState(options) {
26090
26567
  const engine = this.engine;
26091
- this.networkJournal.clear();
26092
- this.backgroundNetworkPersistence.clear();
26568
+ this.networkHistory.clear();
26093
26569
  this.sessionRef = void 0;
26094
26570
  this.pageRef = void 0;
26095
- this.latestSnapshot = void 0;
26096
26571
  this.runId = void 0;
26097
26572
  this.dom = void 0;
26098
26573
  this.computer = void 0;
@@ -26166,10 +26641,10 @@ function buildEngineNetworkRecordFilters(input) {
26166
26641
  ...input.resourceType === void 0 ? {} : { resourceType: input.resourceType }
26167
26642
  };
26168
26643
  }
26169
- function resolveLiveQueryRequestIds(input, journal) {
26644
+ function resolveLiveQueryRequestIds(input, history) {
26170
26645
  const requestIdCandidates = [];
26171
26646
  if (input.recordId !== void 0) {
26172
- const requestId = journal.getRequestId(input.recordId);
26647
+ const requestId = history.getRequestId(input.recordId);
26173
26648
  if (requestId === void 0) {
26174
26649
  return [];
26175
26650
  }
@@ -26178,25 +26653,25 @@ function resolveLiveQueryRequestIds(input, journal) {
26178
26653
  if (input.requestId !== void 0) {
26179
26654
  requestIdCandidates.push(/* @__PURE__ */ new Set([input.requestId]));
26180
26655
  }
26181
- if (input.actionId !== void 0) {
26182
- requestIdCandidates.push(journal.getRequestIdsForActionId(input.actionId));
26656
+ if (input.capture !== void 0) {
26657
+ requestIdCandidates.push(history.getRequestIdsForCapture(input.capture));
26183
26658
  }
26184
26659
  if (input.tag !== void 0) {
26185
- requestIdCandidates.push(journal.getRequestIdsForTag(input.tag));
26660
+ requestIdCandidates.push(history.getRequestIdsForTag(input.tag));
26186
26661
  }
26187
26662
  if (requestIdCandidates.length === 0) {
26188
26663
  return void 0;
26189
26664
  }
26190
26665
  return intersectRequestIdSets(requestIdCandidates);
26191
26666
  }
26192
- function resolveLiveQueryPageRef(input, currentPageRef, requestIds, journal) {
26667
+ function resolveLiveQueryPageRef(input, currentPageRef, requestIds, history) {
26193
26668
  const requestedPageRef = selectLiveQueryPageRef(input, currentPageRef);
26194
26669
  if (requestedPageRef !== void 0 || requestIds === void 0) {
26195
26670
  return requestedPageRef;
26196
26671
  }
26197
26672
  const pageRefs = /* @__PURE__ */ new Set();
26198
26673
  for (const requestId of requestIds) {
26199
- const pageRef = journal.getPageRefForRequestId(requestId);
26674
+ const pageRef = history.getPageRefForRequestId(requestId);
26200
26675
  if (pageRef === void 0) {
26201
26676
  continue;
26202
26677
  }
@@ -26226,7 +26701,7 @@ function filterNetworkQueryRecords(records, input) {
26226
26701
  if (input.requestId !== void 0 && record.record.requestId !== input.requestId) {
26227
26702
  return false;
26228
26703
  }
26229
- if (input.actionId !== void 0 && record.actionId !== input.actionId) {
26704
+ if (input.capture !== void 0 && record.capture !== input.capture) {
26230
26705
  return false;
26231
26706
  }
26232
26707
  if (input.tag !== void 0 && !(record.tags ?? []).includes(input.tag)) {
@@ -26238,10 +26713,10 @@ function filterNetworkQueryRecords(records, input) {
26238
26713
  return true;
26239
26714
  });
26240
26715
  }
26241
- function sortLiveNetworkRecords(records, journal) {
26716
+ function sortLiveNetworkRecords(records, history) {
26242
26717
  return [...records].sort((left, right) => {
26243
- const leftObservedAt = journal.getObservedAt(left.recordId) ?? 0;
26244
- const rightObservedAt = journal.getObservedAt(right.recordId) ?? 0;
26718
+ const leftObservedAt = history.getObservedAt(left.recordId) ?? 0;
26719
+ const rightObservedAt = history.getObservedAt(right.recordId) ?? 0;
26245
26720
  if (leftObservedAt !== rightObservedAt) {
26246
26721
  return rightObservedAt - leftObservedAt;
26247
26722
  }
@@ -26448,9 +26923,8 @@ function buildMinimizedRequestPlan(input) {
26448
26923
  provenance: {
26449
26924
  source: "network-minimize",
26450
26925
  sourceId: input.record.recordId,
26451
- ...input.record.source === "saved" && input.record.savedAt !== void 0 ? { capturedAt: input.record.savedAt } : {}
26926
+ ...input.record.savedAt === void 0 ? {} : { capturedAt: input.record.savedAt }
26452
26927
  },
26453
- lifecycle: "draft",
26454
26928
  payload: normalizeRequestPlanPayload({
26455
26929
  transport: {
26456
26930
  kind: input.transport
@@ -26668,12 +27142,12 @@ function originFromUrl(url) {
26668
27142
  return void 0;
26669
27143
  }
26670
27144
  }
26671
- function filterReverseObservationWindow(records, journal, captureWindowMs) {
27145
+ function filterReverseObservationWindow(records, history, captureWindowMs) {
26672
27146
  if (captureWindowMs === void 0) {
26673
27147
  return records;
26674
27148
  }
26675
27149
  const observedAfter = Date.now() - captureWindowMs;
26676
- return records.filter((record) => (journal.getObservedAt(record.recordId) ?? 0) >= observedAfter);
27150
+ return records.filter((record) => (history.getObservedAt(record.recordId) ?? 0) >= observedAfter);
26677
27151
  }
26678
27152
  function isReverseRelevantNetworkRecord(record) {
26679
27153
  return record.record.resourceType === "document" || record.record.resourceType === "fetch" || record.record.resourceType === "xhr" || record.record.resourceType === "websocket" || record.record.resourceType === "event-stream";
@@ -28605,7 +29079,7 @@ function directionToDelta(direction, amount) {
28605
29079
  return { x: amount, y: 0 };
28606
29080
  }
28607
29081
  }
28608
- function normalizeNamespace3(value) {
29082
+ function normalizeNamespace2(value) {
28609
29083
  const normalized = String(value ?? "default").trim();
28610
29084
  return normalized.length === 0 ? "default" : normalized;
28611
29085
  }
@@ -28637,9 +29111,40 @@ function toOpensteerResolvedTarget2(target) {
28637
29111
  function normalizeOpensteerError(error) {
28638
29112
  return normalizeThrownOpensteerError(error, "Unknown Opensteer runtime failure");
28639
29113
  }
29114
+ function buildMutationCaptureTraceData(diagnostics) {
29115
+ if (diagnostics?.finalizeError === void 0) {
29116
+ return {};
29117
+ }
29118
+ return {
29119
+ networkCapture: {
29120
+ finalizeError: diagnostics.finalizeError
29121
+ }
29122
+ };
29123
+ }
28640
29124
  function isIgnorableRuntimeBindingError(error) {
28641
29125
  return isBrowserCoreError(error) && (error.code === "not-found" || error.code === "page-closed" || error.code === "session-closed");
28642
29126
  }
29127
+ async function withDetachedTimeoutSignal(timeoutMs, operation) {
29128
+ const controller = new AbortController();
29129
+ const timeoutError = new OpensteerProtocolError(
29130
+ "timeout",
29131
+ `mutation capture finalization exceeded ${String(timeoutMs)}ms timeout`,
29132
+ {
29133
+ details: {
29134
+ policy: "mutation-capture-finalize",
29135
+ budgetMs: timeoutMs
29136
+ }
29137
+ }
29138
+ );
29139
+ const timer = setTimeout(() => {
29140
+ controller.abort(timeoutError);
29141
+ }, timeoutMs);
29142
+ try {
29143
+ return await operation(controller.signal);
29144
+ } finally {
29145
+ clearTimeout(timer);
29146
+ }
29147
+ }
28643
29148
  function screenshotMediaType(format2) {
28644
29149
  switch (format2) {
28645
29150
  case "png":
@@ -28655,9 +29160,18 @@ exports.OPENSTEER_FILESYSTEM_WORKSPACE_LAYOUT = OPENSTEER_FILESYSTEM_WORKSPACE_L
28655
29160
  exports.OPENSTEER_FILESYSTEM_WORKSPACE_VERSION = OPENSTEER_FILESYSTEM_WORKSPACE_VERSION;
28656
29161
  exports.OPENSTEER_RUNTIME_CORE_VERSION = OPENSTEER_RUNTIME_CORE_VERSION;
28657
29162
  exports.OpensteerSessionRuntime = OpensteerSessionRuntime;
29163
+ exports.buildDomDescriptorKey = buildDomDescriptorKey;
29164
+ exports.buildDomDescriptorPayload = buildDomDescriptorPayload;
29165
+ exports.buildDomDescriptorVersion = buildDomDescriptorVersion;
29166
+ exports.createDomDescriptorStore = createDomDescriptorStore;
28658
29167
  exports.createFilesystemOpensteerWorkspace = createFilesystemOpensteerWorkspace;
29168
+ exports.createOpensteerExtractionDescriptorStore = createOpensteerExtractionDescriptorStore;
28659
29169
  exports.dispatchSemanticOperation = dispatchSemanticOperation;
29170
+ exports.hashDomDescriptorDescription = hashDomDescriptorDescription;
28660
29171
  exports.normalizeWorkspaceId = normalizeWorkspaceId;
29172
+ exports.parseDomDescriptorRecord = parseDomDescriptorRecord;
29173
+ exports.parseExtractionDescriptorRecord = parseExtractionDescriptorRecord;
28661
29174
  exports.resolveFilesystemWorkspacePath = resolveFilesystemWorkspacePath;
29175
+ exports.sanitizeReplayElementPath = sanitizeReplayElementPath;
28662
29176
  //# sourceMappingURL=index.cjs.map
28663
29177
  //# sourceMappingURL=index.cjs.map