@opensteer/runtime-core 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -13,7 +13,7 @@ import vm from 'vm';
13
13
 
14
14
  // package.json
15
15
  var package_default = {
16
- version: "0.1.1"};
16
+ version: "0.1.2"};
17
17
 
18
18
  // src/version.ts
19
19
  var OPENSTEER_RUNTIME_CORE_VERSION = package_default.version;
@@ -970,14 +970,10 @@ var networkRecordSchema = objectSchema(
970
970
  ]
971
971
  }
972
972
  );
973
- var networkQuerySourceSchema = enumSchema(["live", "saved"], {
974
- title: "NetworkQuerySource"
975
- });
976
973
  var networkQueryRecordSchema = objectSchema(
977
974
  {
978
975
  recordId: stringSchema({ minLength: 1 }),
979
- source: networkQuerySourceSchema,
980
- actionId: stringSchema({ minLength: 1 }),
976
+ capture: stringSchema({ minLength: 1 }),
981
977
  tags: arraySchema(stringSchema({ minLength: 1 }), {
982
978
  uniqueItems: true
983
979
  }),
@@ -986,7 +982,7 @@ var networkQueryRecordSchema = objectSchema(
986
982
  },
987
983
  {
988
984
  title: "NetworkQueryRecord",
989
- required: ["recordId", "source", "record"]
985
+ required: ["recordId", "record"]
990
986
  }
991
987
  );
992
988
  arraySchema(headerEntrySchema, {
@@ -1457,8 +1453,7 @@ var opensteerRecipeStepSchema = oneOfSchema(
1457
1453
  objectSchema(
1458
1454
  {
1459
1455
  kind: enumSchema(["goto"]),
1460
- url: stringSchema({ minLength: 1 }),
1461
- networkTag: stringSchema({ minLength: 1 })
1456
+ url: stringSchema({ minLength: 1 })
1462
1457
  },
1463
1458
  {
1464
1459
  title: "OpensteerAuthRecipeGotoStep",
@@ -1467,8 +1462,7 @@ var opensteerRecipeStepSchema = oneOfSchema(
1467
1462
  ),
1468
1463
  objectSchema(
1469
1464
  {
1470
- kind: enumSchema(["reload"]),
1471
- networkTag: stringSchema({ minLength: 1 })
1465
+ kind: enumSchema(["reload"])
1472
1466
  },
1473
1467
  {
1474
1468
  title: "OpensteerAuthRecipeReloadStep",
@@ -1682,13 +1676,10 @@ var opensteerRecipeRecordSchema = objectSchema(
1682
1676
  var opensteerAuthRecipeRecordSchema = opensteerRecipeRecordSchema;
1683
1677
  var opensteerNetworkQueryInputSchema = objectSchema(
1684
1678
  {
1685
- source: enumSchema(["live", "saved"], {
1686
- title: "OpensteerNetworkQuerySource"
1687
- }),
1688
1679
  pageRef: pageRefSchema,
1689
1680
  recordId: stringSchema({ minLength: 1 }),
1690
1681
  requestId: stringSchema({ minLength: 1 }),
1691
- actionId: stringSchema({ minLength: 1 }),
1682
+ capture: stringSchema({ minLength: 1 }),
1692
1683
  tag: stringSchema({ minLength: 1 }),
1693
1684
  url: stringSchema({ minLength: 1 }),
1694
1685
  hostname: stringSchema({ minLength: 1 }),
@@ -1712,12 +1703,12 @@ var opensteerNetworkQueryOutputSchema = objectSchema(
1712
1703
  required: ["records"]
1713
1704
  }
1714
1705
  );
1715
- var opensteerNetworkSaveInputSchema = objectSchema(
1706
+ var opensteerNetworkTagInputSchema = objectSchema(
1716
1707
  {
1717
1708
  pageRef: pageRefSchema,
1718
1709
  recordId: stringSchema({ minLength: 1 }),
1719
1710
  requestId: stringSchema({ minLength: 1 }),
1720
- actionId: stringSchema({ minLength: 1 }),
1711
+ capture: stringSchema({ minLength: 1 }),
1721
1712
  tag: stringSchema({ minLength: 1 }),
1722
1713
  url: stringSchema({ minLength: 1 }),
1723
1714
  hostname: stringSchema({ minLength: 1 }),
@@ -1727,21 +1718,22 @@ var opensteerNetworkSaveInputSchema = objectSchema(
1727
1718
  resourceType: networkResourceTypeSchema
1728
1719
  },
1729
1720
  {
1730
- title: "OpensteerNetworkSaveInput",
1721
+ title: "OpensteerNetworkTagInput",
1731
1722
  required: ["tag"]
1732
1723
  }
1733
1724
  );
1734
- var opensteerNetworkSaveOutputSchema = objectSchema(
1725
+ var opensteerNetworkTagOutputSchema = objectSchema(
1735
1726
  {
1736
- savedCount: integerSchema({ minimum: 0 })
1727
+ taggedCount: integerSchema({ minimum: 0 })
1737
1728
  },
1738
1729
  {
1739
- title: "OpensteerNetworkSaveOutput",
1740
- required: ["savedCount"]
1730
+ title: "OpensteerNetworkTagOutput",
1731
+ required: ["taggedCount"]
1741
1732
  }
1742
1733
  );
1743
1734
  var opensteerNetworkClearInputSchema = objectSchema(
1744
1735
  {
1736
+ capture: stringSchema({ minLength: 1 }),
1745
1737
  tag: stringSchema({ minLength: 1 })
1746
1738
  },
1747
1739
  {
@@ -5199,7 +5191,7 @@ var opensteerSemanticOperationNames = [
5199
5191
  "dom.scroll",
5200
5192
  "dom.extract",
5201
5193
  "network.query",
5202
- "network.save",
5194
+ "network.tag",
5203
5195
  "network.clear",
5204
5196
  "network.minimize",
5205
5197
  "network.diff",
@@ -5260,7 +5252,7 @@ var opensteerPackageRunnableSemanticOperationNames = /* @__PURE__ */ new Set([
5260
5252
  "dom.scroll",
5261
5253
  "dom.extract",
5262
5254
  "network.query",
5263
- "network.save",
5255
+ "network.tag",
5264
5256
  "network.clear",
5265
5257
  "network.minimize",
5266
5258
  "network.diff",
@@ -5580,7 +5572,7 @@ var opensteerPageCloseOutputSchema = objectSchema(
5580
5572
  var opensteerPageGotoInputSchema = objectSchema(
5581
5573
  {
5582
5574
  url: stringSchema(),
5583
- networkTag: stringSchema({ minLength: 1 })
5575
+ captureNetwork: stringSchema({ minLength: 1 })
5584
5576
  },
5585
5577
  {
5586
5578
  title: "OpensteerPageGotoInput",
@@ -5731,7 +5723,7 @@ var opensteerDomClickInputSchema = objectSchema(
5731
5723
  {
5732
5724
  target: opensteerTargetInputSchema,
5733
5725
  persistAsDescription: stringSchema(),
5734
- networkTag: stringSchema({ minLength: 1 })
5726
+ captureNetwork: stringSchema({ minLength: 1 })
5735
5727
  },
5736
5728
  {
5737
5729
  title: "OpensteerDomClickInput",
@@ -5742,7 +5734,7 @@ var opensteerDomHoverInputSchema = objectSchema(
5742
5734
  {
5743
5735
  target: opensteerTargetInputSchema,
5744
5736
  persistAsDescription: stringSchema(),
5745
- networkTag: stringSchema({ minLength: 1 })
5737
+ captureNetwork: stringSchema({ minLength: 1 })
5746
5738
  },
5747
5739
  {
5748
5740
  title: "OpensteerDomHoverInput",
@@ -5755,7 +5747,7 @@ var opensteerDomInputInputSchema = objectSchema(
5755
5747
  text: stringSchema(),
5756
5748
  pressEnter: { type: "boolean" },
5757
5749
  persistAsDescription: stringSchema(),
5758
- networkTag: stringSchema({ minLength: 1 })
5750
+ captureNetwork: stringSchema({ minLength: 1 })
5759
5751
  },
5760
5752
  {
5761
5753
  title: "OpensteerDomInputInput",
@@ -5768,7 +5760,7 @@ var opensteerDomScrollInputSchema = objectSchema(
5768
5760
  direction: enumSchema(["up", "down", "left", "right"]),
5769
5761
  amount: integerSchema({ minimum: 1 }),
5770
5762
  persistAsDescription: stringSchema(),
5771
- networkTag: stringSchema({ minLength: 1 })
5763
+ captureNetwork: stringSchema({ minLength: 1 })
5772
5764
  },
5773
5765
  {
5774
5766
  title: "OpensteerDomScrollInput",
@@ -5969,7 +5961,7 @@ var opensteerComputerExecuteInputSchema = objectSchema(
5969
5961
  {
5970
5962
  action: opensteerComputerActionSchema,
5971
5963
  screenshot: opensteerComputerScreenshotOptionsSchema,
5972
- networkTag: stringSchema({ minLength: 1 })
5964
+ captureNetwork: stringSchema({ minLength: 1 })
5973
5965
  },
5974
5966
  {
5975
5967
  title: "OpensteerComputerExecuteInput",
@@ -6170,18 +6162,17 @@ var opensteerSemanticOperationSpecificationsBase = [
6170
6162
  }),
6171
6163
  defineSemanticOperationSpec({
6172
6164
  name: "network.query",
6173
- description: "Query live or saved network records for reverse engineering workflows.",
6165
+ description: "Query persisted network records for reverse engineering workflows.",
6174
6166
  inputSchema: opensteerNetworkQueryInputSchema,
6175
6167
  outputSchema: opensteerNetworkQueryOutputSchema,
6176
- requiredCapabilities: [],
6177
- resolveRequiredCapabilities: (input) => input.source === "saved" ? [] : input.includeBodies === true ? ["inspect.network", "inspect.networkBodies"] : ["inspect.network"]
6168
+ requiredCapabilities: []
6178
6169
  }),
6179
6170
  defineSemanticOperationSpec({
6180
- name: "network.save",
6181
- description: "Persist filtered live network records into the saved network registry under a tag.",
6182
- inputSchema: opensteerNetworkSaveInputSchema,
6183
- outputSchema: opensteerNetworkSaveOutputSchema,
6184
- requiredCapabilities: ["inspect.network"]
6171
+ name: "network.tag",
6172
+ description: "Apply a tag to persisted network records matching the provided filters.",
6173
+ inputSchema: opensteerNetworkTagInputSchema,
6174
+ outputSchema: opensteerNetworkTagOutputSchema,
6175
+ requiredCapabilities: []
6185
6176
  }),
6186
6177
  defineSemanticOperationSpec({
6187
6178
  name: "network.clear",
@@ -7002,6 +6993,49 @@ function mediaTypeExtension(mediaType) {
7002
6993
  }
7003
6994
  }
7004
6995
 
6996
+ // src/action-boundary.ts
6997
+ var actionBoundaryDiagnosticsBySignal = /* @__PURE__ */ new WeakMap();
6998
+ async function captureActionBoundarySnapshot(engine, pageRef) {
6999
+ const frames = await engine.listFrames({ pageRef });
7000
+ const mainFrame = frames.find((frame) => frame.isMainFrame);
7001
+ if (!mainFrame) {
7002
+ throw new Error(`page ${pageRef} does not expose a main frame`);
7003
+ }
7004
+ return {
7005
+ pageRef,
7006
+ documentRef: mainFrame.documentRef
7007
+ };
7008
+ }
7009
+ function createActionBoundaryDiagnostics(input) {
7010
+ return {
7011
+ trigger: input.boundary.trigger,
7012
+ crossDocument: input.boundary.crossDocument,
7013
+ bootstrapSettled: input.boundary.bootstrapSettled,
7014
+ visualSettled: input.visualSettled,
7015
+ ...input.boundary.timedOutPhase !== void 0 ? { timedOutPhase: input.boundary.timedOutPhase } : !input.visualSettled ? { timedOutPhase: "visual" } : {}
7016
+ };
7017
+ }
7018
+ function recordActionBoundaryDiagnostics(signal, diagnostics) {
7019
+ actionBoundaryDiagnosticsBySignal.set(signal, diagnostics);
7020
+ }
7021
+ function takeActionBoundaryDiagnostics(signal) {
7022
+ const diagnostics = actionBoundaryDiagnosticsBySignal.get(signal);
7023
+ actionBoundaryDiagnosticsBySignal.delete(signal);
7024
+ return diagnostics;
7025
+ }
7026
+ function isSoftSettleTimeoutError(error, signal) {
7027
+ if (isTimeoutError(error)) {
7028
+ return true;
7029
+ }
7030
+ return signal?.aborted === true && isTimeoutError(signal.reason) && (error === signal.reason || isAbortError(error));
7031
+ }
7032
+ function isAbortError(error) {
7033
+ return error instanceof Error && error.name === "AbortError";
7034
+ }
7035
+ function isTimeoutError(error) {
7036
+ return isOpensteerProtocolError(error) && error.code === "timeout";
7037
+ }
7038
+
7005
7039
  // src/internal/errors.ts
7006
7040
  function normalizeThrownOpensteerError(error, fallbackMessage) {
7007
7041
  if (isOpensteerProtocolError(error)) {
@@ -7034,13 +7068,13 @@ var DEFAULT_TIMEOUTS = {
7034
7068
  "page.add-init-script": 1e4,
7035
7069
  "page.snapshot": 15e3,
7036
7070
  "computer.execute": 3e4,
7037
- "dom.click": 1e4,
7071
+ "dom.click": 3e4,
7038
7072
  "dom.hover": 1e4,
7039
- "dom.input": 1e4,
7073
+ "dom.input": 3e4,
7040
7074
  "dom.scroll": 1e4,
7041
7075
  "dom.extract": 15e3,
7042
7076
  "network.query": 15e3,
7043
- "network.save": 15e3,
7077
+ "network.tag": 15e3,
7044
7078
  "network.clear": 1e4,
7045
7079
  "scripts.capture": 15e3,
7046
7080
  "request.raw": 3e4,
@@ -7855,7 +7889,7 @@ var SqliteSavedNetworkStore = class {
7855
7889
  async initialize() {
7856
7890
  await this.ensureDatabaseDirectory();
7857
7891
  }
7858
- async save(records, tag) {
7892
+ async save(records, options) {
7859
7893
  const database = await this.requireDatabase();
7860
7894
  const readExisting = database.prepare(`
7861
7895
  SELECT record_id
@@ -7864,123 +7898,7 @@ var SqliteSavedNetworkStore = class {
7864
7898
  AND page_ref_key = @page_ref_key
7865
7899
  AND request_id = @request_id
7866
7900
  `);
7867
- const upsertRecord = database.prepare(`
7868
- INSERT INTO saved_network_records (
7869
- record_id,
7870
- request_id,
7871
- session_ref,
7872
- page_ref,
7873
- page_ref_key,
7874
- frame_ref,
7875
- document_ref,
7876
- action_id,
7877
- method,
7878
- method_lc,
7879
- url,
7880
- url_lc,
7881
- hostname,
7882
- hostname_lc,
7883
- path,
7884
- path_lc,
7885
- status,
7886
- status_text,
7887
- resource_type,
7888
- navigation_request,
7889
- request_headers_json,
7890
- response_headers_json,
7891
- request_body_json,
7892
- response_body_json,
7893
- initiator_json,
7894
- timing_json,
7895
- transfer_json,
7896
- source_json,
7897
- capture_state,
7898
- request_body_state,
7899
- response_body_state,
7900
- request_body_skip_reason,
7901
- response_body_skip_reason,
7902
- request_body_error,
7903
- response_body_error,
7904
- redirect_from_request_id,
7905
- redirect_to_request_id,
7906
- saved_at
7907
- ) VALUES (
7908
- @record_id,
7909
- @request_id,
7910
- @session_ref,
7911
- @page_ref,
7912
- @page_ref_key,
7913
- @frame_ref,
7914
- @document_ref,
7915
- @action_id,
7916
- @method,
7917
- @method_lc,
7918
- @url,
7919
- @url_lc,
7920
- @hostname,
7921
- @hostname_lc,
7922
- @path,
7923
- @path_lc,
7924
- @status,
7925
- @status_text,
7926
- @resource_type,
7927
- @navigation_request,
7928
- @request_headers_json,
7929
- @response_headers_json,
7930
- @request_body_json,
7931
- @response_body_json,
7932
- @initiator_json,
7933
- @timing_json,
7934
- @transfer_json,
7935
- @source_json,
7936
- @capture_state,
7937
- @request_body_state,
7938
- @response_body_state,
7939
- @request_body_skip_reason,
7940
- @response_body_skip_reason,
7941
- @request_body_error,
7942
- @response_body_error,
7943
- @redirect_from_request_id,
7944
- @redirect_to_request_id,
7945
- @saved_at
7946
- )
7947
- ON CONFLICT(record_id) DO UPDATE SET
7948
- page_ref = excluded.page_ref,
7949
- page_ref_key = excluded.page_ref_key,
7950
- frame_ref = excluded.frame_ref,
7951
- document_ref = excluded.document_ref,
7952
- action_id = excluded.action_id,
7953
- method = excluded.method,
7954
- method_lc = excluded.method_lc,
7955
- url = excluded.url,
7956
- url_lc = excluded.url_lc,
7957
- hostname = excluded.hostname,
7958
- hostname_lc = excluded.hostname_lc,
7959
- path = excluded.path,
7960
- path_lc = excluded.path_lc,
7961
- status = excluded.status,
7962
- status_text = excluded.status_text,
7963
- resource_type = excluded.resource_type,
7964
- navigation_request = excluded.navigation_request,
7965
- request_headers_json = excluded.request_headers_json,
7966
- response_headers_json = excluded.response_headers_json,
7967
- request_body_json = excluded.request_body_json,
7968
- response_body_json = excluded.response_body_json,
7969
- initiator_json = excluded.initiator_json,
7970
- timing_json = excluded.timing_json,
7971
- transfer_json = excluded.transfer_json,
7972
- source_json = excluded.source_json,
7973
- capture_state = excluded.capture_state,
7974
- request_body_state = excluded.request_body_state,
7975
- response_body_state = excluded.response_body_state,
7976
- request_body_skip_reason = excluded.request_body_skip_reason,
7977
- response_body_skip_reason = excluded.response_body_skip_reason,
7978
- request_body_error = excluded.request_body_error,
7979
- response_body_error = excluded.response_body_error,
7980
- redirect_from_request_id = excluded.redirect_from_request_id,
7981
- redirect_to_request_id = excluded.redirect_to_request_id,
7982
- saved_at = excluded.saved_at
7983
- `);
7901
+ const upsertRecord = database.prepare(buildSavedNetworkUpsertSql(options.bodyWriteMode));
7984
7902
  const insertTag = database.prepare(`
7985
7903
  INSERT OR IGNORE INTO saved_network_tags (record_id, tag)
7986
7904
  VALUES (@record_id, @tag)
@@ -8004,7 +7922,7 @@ var SqliteSavedNetworkStore = class {
8004
7922
  page_ref_key: pageRefKey,
8005
7923
  frame_ref: entry.record.frameRef ?? null,
8006
7924
  document_ref: entry.record.documentRef ?? null,
8007
- action_id: entry.actionId ?? null,
7925
+ capture: entry.capture ?? null,
8008
7926
  method: entry.record.method,
8009
7927
  method_lc: entry.record.method.toLowerCase(),
8010
7928
  url: entry.record.url,
@@ -8036,10 +7954,14 @@ var SqliteSavedNetworkStore = class {
8036
7954
  redirect_to_request_id: entry.record.redirectToRequestId ?? null,
8037
7955
  saved_at: entry.savedAt ?? Date.now()
8038
7956
  });
8039
- if (tag !== void 0) {
7957
+ const tags = new Set(entry.tags ?? []);
7958
+ if (options.tag !== void 0) {
7959
+ tags.add(options.tag);
7960
+ }
7961
+ for (const currentTag of tags) {
8040
7962
  const result = insertTag.run({
8041
7963
  record_id: recordId,
8042
- tag
7964
+ tag: currentTag
8043
7965
  });
8044
7966
  savedCount += result.changes ?? 0;
8045
7967
  }
@@ -8047,6 +7969,39 @@ var SqliteSavedNetworkStore = class {
8047
7969
  return savedCount;
8048
7970
  });
8049
7971
  }
7972
+ async tagByFilter(filter, tag) {
7973
+ const database = await this.requireDatabase();
7974
+ const { whereSql, parameters } = buildSavedNetworkWhere(filter);
7975
+ const selectRecords = database.prepare(
7976
+ `
7977
+ SELECT r.record_id
7978
+ FROM saved_network_records r
7979
+ ${whereSql}
7980
+ `
7981
+ );
7982
+ const insertTag = database.prepare(`
7983
+ INSERT OR IGNORE INTO saved_network_tags (record_id, tag)
7984
+ VALUES (@record_id, @tag)
7985
+ `);
7986
+ return withSqliteTransaction(database, () => {
7987
+ let taggedCount = 0;
7988
+ const rows = selectRecords.all(
7989
+ ...parameters
7990
+ );
7991
+ for (const row of rows) {
7992
+ const recordId = row.record_id;
7993
+ if (typeof recordId !== "string") {
7994
+ continue;
7995
+ }
7996
+ const result = insertTag.run({
7997
+ record_id: recordId,
7998
+ tag
7999
+ });
8000
+ taggedCount += result.changes ?? 0;
8001
+ }
8002
+ return taggedCount;
8003
+ });
8004
+ }
8050
8005
  async query(input = {}) {
8051
8006
  const database = await this.requireDatabase();
8052
8007
  const limit = Math.max(1, Math.min(input.limit ?? 50, 200));
@@ -8080,39 +8035,31 @@ var SqliteSavedNetworkStore = class {
8080
8035
  }
8081
8036
  async clear(input = {}) {
8082
8037
  const database = await this.requireDatabase();
8083
- const countAll = database.prepare(`
8084
- SELECT COUNT(*) AS cleared
8085
- FROM saved_network_records
8086
- `);
8087
- const countByTag = database.prepare(`
8088
- SELECT COUNT(DISTINCT record_id) AS cleared
8089
- FROM saved_network_tags
8090
- WHERE tag = @tag
8091
- `);
8092
- const deleteAllTags = database.prepare(`DELETE FROM saved_network_tags`);
8038
+ const countAll = database.prepare(`SELECT COUNT(*) AS cleared FROM saved_network_records`);
8093
8039
  const deleteAllRecords = database.prepare(`DELETE FROM saved_network_records`);
8094
- const deleteTag = database.prepare(`
8095
- DELETE FROM saved_network_tags
8096
- WHERE tag = @tag
8040
+ const { whereSql, parameters } = buildSavedNetworkWhere(input);
8041
+ const countFiltered = database.prepare(`
8042
+ SELECT COUNT(*) AS cleared
8043
+ FROM saved_network_records r
8044
+ ${whereSql}
8097
8045
  `);
8098
- const deleteOrphans = database.prepare(`
8046
+ const deleteFiltered = database.prepare(`
8099
8047
  DELETE FROM saved_network_records
8100
- WHERE NOT EXISTS (
8101
- SELECT 1
8102
- FROM saved_network_tags t
8103
- WHERE t.record_id = saved_network_records.record_id
8048
+ WHERE record_id IN (
8049
+ SELECT r.record_id
8050
+ FROM saved_network_records r
8051
+ ${whereSql}
8104
8052
  )
8105
8053
  `);
8106
8054
  return withSqliteTransaction(database, () => {
8107
- const tag = input.tag;
8108
- const cleared = tag === void 0 ? countAll.get().cleared : countByTag.get({ tag }).cleared;
8109
- if (tag === void 0) {
8110
- deleteAllTags.run();
8055
+ if (input.capture === void 0 && input.tag === void 0) {
8056
+ const cleared2 = countAll.get().cleared;
8111
8057
  deleteAllRecords.run();
8112
- return cleared;
8058
+ return cleared2;
8113
8059
  }
8114
- deleteTag.run({ tag });
8115
- deleteOrphans.run();
8060
+ const args = parameters;
8061
+ const cleared = countFiltered.get(...args).cleared;
8062
+ deleteFiltered.run(...args);
8116
8063
  return cleared;
8117
8064
  });
8118
8065
  }
@@ -8167,7 +8114,7 @@ var SqliteSavedNetworkStore = class {
8167
8114
  page_ref_key TEXT NOT NULL,
8168
8115
  frame_ref TEXT,
8169
8116
  document_ref TEXT,
8170
- action_id TEXT,
8117
+ capture TEXT,
8171
8118
  method TEXT NOT NULL,
8172
8119
  method_lc TEXT NOT NULL,
8173
8120
  url TEXT NOT NULL,
@@ -8206,6 +8153,9 @@ var SqliteSavedNetworkStore = class {
8206
8153
  CREATE INDEX IF NOT EXISTS saved_network_records_saved_at
8207
8154
  ON saved_network_records (saved_at DESC);
8208
8155
 
8156
+ CREATE INDEX IF NOT EXISTS saved_network_records_capture
8157
+ ON saved_network_records (capture);
8158
+
8209
8159
  CREATE TABLE IF NOT EXISTS saved_network_tags (
8210
8160
  record_id TEXT NOT NULL REFERENCES saved_network_records(record_id) ON DELETE CASCADE,
8211
8161
  tag TEXT NOT NULL,
@@ -8221,6 +8171,7 @@ var SqliteSavedNetworkStore = class {
8221
8171
  "capture_state",
8222
8172
  "TEXT NOT NULL DEFAULT 'complete'"
8223
8173
  );
8174
+ this.ensureColumn(database, "saved_network_records", "capture", "TEXT");
8224
8175
  this.ensureColumn(
8225
8176
  database,
8226
8177
  "saved_network_records",
@@ -8249,6 +8200,10 @@ var SqliteSavedNetworkStore = class {
8249
8200
  function buildSavedNetworkWhere(input) {
8250
8201
  const clauses = [];
8251
8202
  const parameters = [];
8203
+ if (input.pageRef !== void 0) {
8204
+ clauses.push("r.page_ref_key = ?");
8205
+ parameters.push(input.pageRef);
8206
+ }
8252
8207
  if (input.recordId !== void 0) {
8253
8208
  clauses.push("r.record_id = ?");
8254
8209
  parameters.push(input.recordId);
@@ -8257,9 +8212,9 @@ function buildSavedNetworkWhere(input) {
8257
8212
  clauses.push("r.request_id = ?");
8258
8213
  parameters.push(input.requestId);
8259
8214
  }
8260
- if (input.actionId !== void 0) {
8261
- clauses.push("r.action_id = ?");
8262
- parameters.push(input.actionId);
8215
+ if (input.capture !== void 0) {
8216
+ clauses.push("r.capture = ?");
8217
+ parameters.push(input.capture);
8263
8218
  }
8264
8219
  if (input.tag !== void 0) {
8265
8220
  clauses.push(`
@@ -8301,6 +8256,127 @@ function buildSavedNetworkWhere(input) {
8301
8256
  parameters
8302
8257
  };
8303
8258
  }
8259
+ function buildSavedNetworkUpsertSql(bodyWriteMode) {
8260
+ const bodyUpdateSql = bodyWriteMode === "authoritative" ? `
8261
+ request_body_json = excluded.request_body_json,
8262
+ response_body_json = excluded.response_body_json,
8263
+ request_body_state = excluded.request_body_state,
8264
+ response_body_state = excluded.response_body_state,
8265
+ request_body_skip_reason = excluded.request_body_skip_reason,
8266
+ response_body_skip_reason = excluded.response_body_skip_reason,
8267
+ request_body_error = excluded.request_body_error,
8268
+ response_body_error = excluded.response_body_error,
8269
+ ` : "";
8270
+ return `
8271
+ INSERT INTO saved_network_records (
8272
+ record_id,
8273
+ request_id,
8274
+ session_ref,
8275
+ page_ref,
8276
+ page_ref_key,
8277
+ frame_ref,
8278
+ document_ref,
8279
+ capture,
8280
+ method,
8281
+ method_lc,
8282
+ url,
8283
+ url_lc,
8284
+ hostname,
8285
+ hostname_lc,
8286
+ path,
8287
+ path_lc,
8288
+ status,
8289
+ status_text,
8290
+ resource_type,
8291
+ navigation_request,
8292
+ request_headers_json,
8293
+ response_headers_json,
8294
+ request_body_json,
8295
+ response_body_json,
8296
+ initiator_json,
8297
+ timing_json,
8298
+ transfer_json,
8299
+ source_json,
8300
+ capture_state,
8301
+ request_body_state,
8302
+ response_body_state,
8303
+ request_body_skip_reason,
8304
+ response_body_skip_reason,
8305
+ request_body_error,
8306
+ response_body_error,
8307
+ redirect_from_request_id,
8308
+ redirect_to_request_id,
8309
+ saved_at
8310
+ ) VALUES (
8311
+ @record_id,
8312
+ @request_id,
8313
+ @session_ref,
8314
+ @page_ref,
8315
+ @page_ref_key,
8316
+ @frame_ref,
8317
+ @document_ref,
8318
+ @capture,
8319
+ @method,
8320
+ @method_lc,
8321
+ @url,
8322
+ @url_lc,
8323
+ @hostname,
8324
+ @hostname_lc,
8325
+ @path,
8326
+ @path_lc,
8327
+ @status,
8328
+ @status_text,
8329
+ @resource_type,
8330
+ @navigation_request,
8331
+ @request_headers_json,
8332
+ @response_headers_json,
8333
+ @request_body_json,
8334
+ @response_body_json,
8335
+ @initiator_json,
8336
+ @timing_json,
8337
+ @transfer_json,
8338
+ @source_json,
8339
+ @capture_state,
8340
+ @request_body_state,
8341
+ @response_body_state,
8342
+ @request_body_skip_reason,
8343
+ @response_body_skip_reason,
8344
+ @request_body_error,
8345
+ @response_body_error,
8346
+ @redirect_from_request_id,
8347
+ @redirect_to_request_id,
8348
+ @saved_at
8349
+ )
8350
+ ON CONFLICT(record_id) DO UPDATE SET
8351
+ page_ref = excluded.page_ref,
8352
+ page_ref_key = excluded.page_ref_key,
8353
+ frame_ref = excluded.frame_ref,
8354
+ document_ref = excluded.document_ref,
8355
+ capture = excluded.capture,
8356
+ method = excluded.method,
8357
+ method_lc = excluded.method_lc,
8358
+ url = excluded.url,
8359
+ url_lc = excluded.url_lc,
8360
+ hostname = excluded.hostname,
8361
+ hostname_lc = excluded.hostname_lc,
8362
+ path = excluded.path,
8363
+ path_lc = excluded.path_lc,
8364
+ status = excluded.status,
8365
+ status_text = excluded.status_text,
8366
+ resource_type = excluded.resource_type,
8367
+ navigation_request = excluded.navigation_request,
8368
+ request_headers_json = excluded.request_headers_json,
8369
+ response_headers_json = excluded.response_headers_json,
8370
+ ${bodyUpdateSql} initiator_json = excluded.initiator_json,
8371
+ timing_json = excluded.timing_json,
8372
+ transfer_json = excluded.transfer_json,
8373
+ source_json = excluded.source_json,
8374
+ capture_state = excluded.capture_state,
8375
+ redirect_from_request_id = excluded.redirect_from_request_id,
8376
+ redirect_to_request_id = excluded.redirect_to_request_id,
8377
+ saved_at = MIN(saved_network_records.saved_at, excluded.saved_at)
8378
+ `;
8379
+ }
8304
8380
  function inflateSavedNetworkRow(row, includeBodies) {
8305
8381
  const requestBody = includeBodies && row.request_body_json !== null ? JSON.parse(row.request_body_json) : void 0;
8306
8382
  const responseBody = includeBodies && row.response_body_json !== null ? JSON.parse(row.response_body_json) : void 0;
@@ -8371,8 +8447,7 @@ function inflateSavedNetworkRow(row, includeBodies) {
8371
8447
  }
8372
8448
  return {
8373
8449
  recordId: row.record_id,
8374
- source: "saved",
8375
- ...row.action_id === null ? {} : { actionId: row.action_id },
8450
+ ...row.capture === null ? {} : { capture: row.capture },
8376
8451
  ...row.tags === null || row.tags.length === 0 ? {} : { tags: row.tags.split(TAG_DELIMITER).filter((tag) => tag.length > 0) },
8377
8452
  savedAt: row.saved_at,
8378
8453
  record
@@ -10328,8 +10403,9 @@ var DomActionExecutor = class {
10328
10403
  })
10329
10404
  );
10330
10405
  let finalResolved = resolved;
10406
+ let finalSnapshot;
10331
10407
  if (input.pressEnter) {
10332
- await this.settle(resolved.pageRef, "dom.input", timeout);
10408
+ await this.waitForPressEnterReaction(timeout);
10333
10409
  const enterSession = this.options.createResolutionSession();
10334
10410
  const enterResolved = await timeout.runStep(
10335
10411
  () => this.options.resolveTarget(enterSession, {
@@ -10343,6 +10419,9 @@ var DomActionExecutor = class {
10343
10419
  () => bridge.inspectActionTarget(enterResolved.locator)
10344
10420
  );
10345
10421
  this.assertKeyboardActionable("dom.input", enterResolved, inspectionBeforeEnter);
10422
+ finalSnapshot = await timeout.runStep(
10423
+ () => captureActionBoundarySnapshot(this.options.engine, enterResolved.pageRef)
10424
+ );
10346
10425
  await timeout.runStep(
10347
10426
  () => bridge.pressKey(enterResolved.locator, {
10348
10427
  key: "Enter"
@@ -10350,7 +10429,15 @@ var DomActionExecutor = class {
10350
10429
  );
10351
10430
  finalResolved = enterResolved;
10352
10431
  }
10353
- await this.settle(finalResolved.pageRef, "dom.input", timeout);
10432
+ const settleDiagnostics = await this.settle(
10433
+ finalResolved.pageRef,
10434
+ "dom.input",
10435
+ timeout,
10436
+ finalSnapshot
10437
+ );
10438
+ if (finalSnapshot !== void 0) {
10439
+ recordActionBoundaryDiagnostics(timeout.signal, settleDiagnostics);
10440
+ }
10354
10441
  return finalResolved;
10355
10442
  } catch (error) {
10356
10443
  lastError = error;
@@ -10423,8 +10510,17 @@ var DomActionExecutor = class {
10423
10510
  );
10424
10511
  }
10425
10512
  }
10513
+ const actionBoundarySnapshot = await timeout.runStep(
10514
+ () => captureActionBoundarySnapshot(this.options.engine, pointerTarget.resolved.pageRef)
10515
+ );
10426
10516
  const outcome = await dispatch(pointerTarget, point, timeout);
10427
- await this.settle(pointerTarget.resolved.pageRef, input.operation, timeout);
10517
+ const settleDiagnostics = await this.settle(
10518
+ pointerTarget.resolved.pageRef,
10519
+ input.operation,
10520
+ timeout,
10521
+ actionBoundarySnapshot
10522
+ );
10523
+ recordActionBoundaryDiagnostics(timeout.signal, settleDiagnostics);
10428
10524
  return outcome;
10429
10525
  } catch (error) {
10430
10526
  lastError = error;
@@ -10442,23 +10538,49 @@ var DomActionExecutor = class {
10442
10538
  }
10443
10539
  return runWithPolicyTimeout(this.options.policy.timeout, { operation }, execute);
10444
10540
  }
10445
- async settle(pageRef, operation, timeout) {
10541
+ async settle(pageRef, operation, timeout, snapshot) {
10446
10542
  const bridge = this.requireBridge();
10447
- await timeout.runStep(
10543
+ let visualSettled = true;
10544
+ const boundary = await timeout.runStep(
10448
10545
  () => bridge.finalizeDomAction(pageRef, {
10449
10546
  operation,
10547
+ ...snapshot === void 0 ? {} : { snapshot },
10450
10548
  signal: timeout.signal,
10451
10549
  remainingMs: () => timeout.remainingMs(),
10452
- policySettle: (targetPageRef) => settleWithPolicy(this.options.policy.settle, {
10453
- operation,
10454
- trigger: "dom-action",
10455
- engine: this.options.engine,
10456
- pageRef: targetPageRef,
10457
- signal: timeout.signal,
10458
- remainingMs: timeout.remainingMs()
10459
- })
10550
+ policySettle: async (targetPageRef, trigger) => {
10551
+ try {
10552
+ await settleWithPolicy(this.options.policy.settle, {
10553
+ operation,
10554
+ trigger,
10555
+ engine: this.options.engine,
10556
+ pageRef: targetPageRef,
10557
+ signal: timeout.signal,
10558
+ remainingMs: timeout.remainingMs()
10559
+ });
10560
+ } catch (error) {
10561
+ if (snapshot !== void 0 && isSoftSettleTimeoutError(error, timeout.signal)) {
10562
+ visualSettled = false;
10563
+ return;
10564
+ }
10565
+ throw error;
10566
+ }
10567
+ }
10460
10568
  })
10461
10569
  );
10570
+ return createActionBoundaryDiagnostics({
10571
+ boundary,
10572
+ visualSettled
10573
+ });
10574
+ }
10575
+ async waitForPressEnterReaction(timeout) {
10576
+ const delayMs = this.options.policy.settle.resolveDelayMs({
10577
+ operation: "dom.input",
10578
+ trigger: "dom-action"
10579
+ });
10580
+ if (delayMs <= 0) {
10581
+ return;
10582
+ }
10583
+ await delayWithSignal(delayMs, timeout.signal);
10462
10584
  }
10463
10585
  requireBridge() {
10464
10586
  if (this.bridge !== void 0) {
@@ -11725,23 +11847,47 @@ var DefaultComputerUseRuntime = class {
11725
11847
  const preActionDisplay = createComputerDisplayTransform(preActionNativeViewport);
11726
11848
  const nativeAction = toNativeComputerAction(input.input.action, preActionDisplay);
11727
11849
  const screenshot = normalizeScreenshotOptions(input.input.screenshot);
11850
+ const snapshot = await input.timeout.runStep(
11851
+ () => captureActionBoundarySnapshot(this.options.engine, input.pageRef)
11852
+ );
11853
+ let visualSettled = true;
11728
11854
  const executed = await input.timeout.runStep(
11729
11855
  () => bridge.execute({
11730
11856
  pageRef: input.pageRef,
11857
+ snapshot,
11731
11858
  action: nativeAction,
11732
11859
  screenshot,
11733
11860
  signal: input.timeout.signal,
11734
11861
  remainingMs: () => input.timeout.remainingMs(),
11735
- policySettle: async (pageRef) => settleWithPolicy(this.options.policy.settle, {
11736
- operation: "computer.execute",
11737
- trigger: "dom-action",
11738
- engine: this.options.engine,
11739
- pageRef,
11740
- signal: input.timeout.signal,
11741
- remainingMs: input.timeout.remainingMs()
11742
- })
11862
+ policySettle: async (pageRef, trigger) => {
11863
+ try {
11864
+ await settleWithPolicy(this.options.policy.settle, {
11865
+ operation: "computer.execute",
11866
+ trigger,
11867
+ engine: this.options.engine,
11868
+ pageRef,
11869
+ signal: input.timeout.signal,
11870
+ remainingMs: input.timeout.remainingMs()
11871
+ });
11872
+ } catch (error) {
11873
+ if (pageRef === input.pageRef && isSoftSettleTimeoutError(error, input.timeout.signal)) {
11874
+ visualSettled = false;
11875
+ return;
11876
+ }
11877
+ throw error;
11878
+ }
11879
+ }
11743
11880
  })
11744
11881
  );
11882
+ if (executed.boundary !== void 0 && executed.pageRef === input.pageRef) {
11883
+ recordActionBoundaryDiagnostics(
11884
+ input.timeout.signal,
11885
+ createActionBoundaryDiagnostics({
11886
+ boundary: executed.boundary,
11887
+ visualSettled
11888
+ })
11889
+ );
11890
+ }
11745
11891
  let trace = void 0;
11746
11892
  if (!input.timeout.signal.aborted) {
11747
11893
  try {
@@ -11854,8 +12000,8 @@ async function dispatchSemanticOperation(runtime, operation, input, options = {}
11854
12000
  input,
11855
12001
  options
11856
12002
  );
11857
- case "network.save":
11858
- return runtime.saveNetwork(
12003
+ case "network.tag":
12004
+ return runtime.tagNetwork(
11859
12005
  input,
11860
12006
  options
11861
12007
  );
@@ -12729,9 +12875,9 @@ function inferRequestPlanFromNetworkRecord(record, input, options = {}) {
12729
12875
  key: input.key,
12730
12876
  version: input.version,
12731
12877
  provenance: {
12732
- source: record.source === "saved" ? "saved-network-record" : "live-network-record",
12878
+ source: record.savedAt === void 0 ? "network-record" : "saved-network-record",
12733
12879
  sourceId: record.recordId,
12734
- ...record.source === "saved" ? record.savedAt === void 0 ? {} : { capturedAt: record.savedAt } : options.observedAt === void 0 ? {} : { capturedAt: options.observedAt }
12880
+ ...record.savedAt === void 0 ? options.observedAt === void 0 ? {} : { capturedAt: options.observedAt } : { capturedAt: record.savedAt }
12735
12881
  },
12736
12882
  payload,
12737
12883
  ...record.tags === void 0 || record.tags.length === 0 ? {} : { tags: record.tags }
@@ -13111,63 +13257,67 @@ function resolveBodyEncoding(charset) {
13111
13257
  return "utf8";
13112
13258
  }
13113
13259
  }
13114
- var NetworkJournal = class {
13260
+ var NetworkHistory = class {
13115
13261
  metadataByRequestId = /* @__PURE__ */ new Map();
13116
13262
  requestIdByRecordId = /* @__PURE__ */ new Map();
13117
- requestIdsByActionId = /* @__PURE__ */ new Map();
13263
+ requestIdsByCapture = /* @__PURE__ */ new Map();
13118
13264
  requestIdsByTag = /* @__PURE__ */ new Map();
13119
- sync(records, options = {}) {
13265
+ tombstonedRequestIds = /* @__PURE__ */ new Set();
13266
+ materialize(records, options = {}) {
13120
13267
  const observedAt = Date.now();
13121
- return records.map((record) => this.materializeLiveRecord(record, observedAt, options));
13122
- }
13123
- materializeLiveRecord(record, observedAt, options = {}) {
13124
- let metadata = this.metadataByRequestId.get(record.requestId);
13125
- if (!metadata) {
13126
- metadata = {
13127
- recordId: `record:${randomUUID()}`,
13128
- observedAt,
13129
- ...record.pageRef === void 0 ? {} : { pageRef: record.pageRef },
13130
- tags: /* @__PURE__ */ new Set()
13131
- };
13132
- this.metadataByRequestId.set(record.requestId, metadata);
13133
- this.requestIdByRecordId.set(metadata.recordId, record.requestId);
13134
- } else if (metadata.pageRef === void 0 && record.pageRef !== void 0) {
13135
- metadata.pageRef = record.pageRef;
13268
+ const materialized = [];
13269
+ for (const record of records) {
13270
+ const entry = this.materializeRecord(record, observedAt, options);
13271
+ if (entry !== void 0) {
13272
+ materialized.push(entry);
13273
+ }
13136
13274
  }
13137
- return {
13138
- recordId: metadata.recordId,
13139
- source: "live",
13140
- ...metadata.actionId === void 0 ? {} : { actionId: metadata.actionId },
13141
- ...metadata.tags.size === 0 ? {} : { tags: [...metadata.tags].sort() },
13142
- record: toProtocolNetworkRecord(record, {
13143
- redactSecretHeaders: options.redactSecretHeaders ?? true
13144
- })
13145
- };
13275
+ return materialized;
13146
13276
  }
13147
- diffNewRequestIds(records, baselineRequestIds) {
13148
- const observedAt = Date.now();
13149
- const all = records.map(
13150
- (record) => this.materializeLiveRecord(record, observedAt, {
13151
- redactSecretHeaders: true
13152
- })
13153
- );
13154
- const delta = all.filter((entry) => !baselineRequestIds.has(entry.record.requestId));
13155
- return {
13156
- all,
13157
- delta
13158
- };
13277
+ async persist(records, store, options) {
13278
+ const observedAt = options.observedAt ?? Date.now();
13279
+ const metadataToSave = /* @__PURE__ */ new Set();
13280
+ const persisted = [];
13281
+ for (const record of records) {
13282
+ const entry = this.materializeRecord(record, observedAt, {
13283
+ ...options.redactSecretHeaders === void 0 ? {} : { redactSecretHeaders: options.redactSecretHeaders }
13284
+ });
13285
+ if (entry === void 0) {
13286
+ continue;
13287
+ }
13288
+ const requestId = entry.record.requestId;
13289
+ const metadata = this.metadataByRequestId.get(requestId);
13290
+ if (metadata === void 0) {
13291
+ continue;
13292
+ }
13293
+ const savedAt = metadata.savedAt ?? observedAt;
13294
+ metadataToSave.add(metadata);
13295
+ persisted.push({
13296
+ ...entry,
13297
+ savedAt
13298
+ });
13299
+ }
13300
+ if (persisted.length > 0) {
13301
+ await store.save(persisted, {
13302
+ bodyWriteMode: options.bodyWriteMode
13303
+ });
13304
+ for (const metadata of metadataToSave) {
13305
+ metadata.savedAt ??= observedAt;
13306
+ }
13307
+ }
13308
+ return persisted;
13159
13309
  }
13160
- assignActionId(records, actionId) {
13310
+ assignCapture(records, capture) {
13161
13311
  for (const record of records) {
13162
13312
  const metadata = this.metadataByRequestId.get(record.record.requestId);
13163
- if (!metadata || metadata.actionId === actionId) {
13313
+ if (!metadata || metadata.capture === capture) {
13164
13314
  continue;
13165
13315
  }
13166
- if (metadata.actionId !== void 0) {
13167
- this.requestIdsByActionId.get(metadata.actionId)?.delete(record.record.requestId);
13316
+ if (metadata.capture !== void 0) {
13317
+ this.requestIdsByCapture.get(metadata.capture)?.delete(record.record.requestId);
13168
13318
  }
13169
- metadata.actionId = actionId;
13170
- this.addIndexedRequestId(this.requestIdsByActionId, actionId, record.record.requestId);
13319
+ metadata.capture = capture;
13320
+ this.addIndexedRequestId(this.requestIdsByCapture, capture, record.record.requestId);
13171
13321
  }
13172
13322
  }
13173
13323
  addTag(records, tag) {
@@ -13187,8 +13337,8 @@ var NetworkJournal = class {
13187
13337
  getRequestId(recordId) {
13188
13338
  return this.requestIdByRecordId.get(recordId);
13189
13339
  }
13190
- getRequestIdsForActionId(actionId) {
13191
- return new Set(this.requestIdsByActionId.get(actionId) ?? []);
13340
+ getRequestIdsForCapture(capture) {
13341
+ return new Set(this.requestIdsByCapture.get(capture) ?? []);
13192
13342
  }
13193
13343
  getRequestIdsForTag(tag) {
13194
13344
  return new Set(this.requestIdsByTag.get(tag) ?? []);
@@ -13196,11 +13346,59 @@ var NetworkJournal = class {
13196
13346
  getPageRefForRequestId(requestId) {
13197
13347
  return this.metadataByRequestId.get(requestId)?.pageRef;
13198
13348
  }
13349
+ getKnownRequestIds() {
13350
+ return new Set(this.metadataByRequestId.keys());
13351
+ }
13352
+ tombstoneRequestIds(requestIds) {
13353
+ for (const requestId of requestIds) {
13354
+ this.tombstonedRequestIds.add(requestId);
13355
+ const metadata = this.metadataByRequestId.get(requestId);
13356
+ if (!metadata) {
13357
+ continue;
13358
+ }
13359
+ this.metadataByRequestId.delete(requestId);
13360
+ this.requestIdByRecordId.delete(metadata.recordId);
13361
+ if (metadata.capture !== void 0) {
13362
+ this.requestIdsByCapture.get(metadata.capture)?.delete(requestId);
13363
+ }
13364
+ for (const tag of metadata.tags) {
13365
+ this.requestIdsByTag.get(tag)?.delete(requestId);
13366
+ }
13367
+ }
13368
+ }
13199
13369
  clear() {
13200
13370
  this.metadataByRequestId.clear();
13201
13371
  this.requestIdByRecordId.clear();
13202
- this.requestIdsByActionId.clear();
13372
+ this.requestIdsByCapture.clear();
13203
13373
  this.requestIdsByTag.clear();
13374
+ this.tombstonedRequestIds.clear();
13375
+ }
13376
+ materializeRecord(record, observedAt, options) {
13377
+ if (this.tombstonedRequestIds.has(record.requestId)) {
13378
+ return void 0;
13379
+ }
13380
+ let metadata = this.metadataByRequestId.get(record.requestId);
13381
+ if (!metadata) {
13382
+ metadata = {
13383
+ recordId: `record:${randomUUID()}`,
13384
+ observedAt,
13385
+ ...record.pageRef === void 0 ? {} : { pageRef: record.pageRef },
13386
+ tags: /* @__PURE__ */ new Set()
13387
+ };
13388
+ this.metadataByRequestId.set(record.requestId, metadata);
13389
+ this.requestIdByRecordId.set(metadata.recordId, record.requestId);
13390
+ } else if (metadata.pageRef === void 0 && record.pageRef !== void 0) {
13391
+ metadata.pageRef = record.pageRef;
13392
+ }
13393
+ return {
13394
+ recordId: metadata.recordId,
13395
+ ...metadata.capture === void 0 ? {} : { capture: metadata.capture },
13396
+ ...metadata.tags.size === 0 ? {} : { tags: [...metadata.tags].sort() },
13397
+ ...metadata.savedAt === void 0 ? {} : { savedAt: metadata.savedAt },
13398
+ record: toProtocolNetworkRecord(record, {
13399
+ redactSecretHeaders: options.redactSecretHeaders ?? true
13400
+ })
13401
+ };
13204
13402
  }
13205
13403
  addIndexedRequestId(index, key, requestId) {
13206
13404
  const requestIds = index.get(key) ?? /* @__PURE__ */ new Set();
@@ -19935,6 +20133,7 @@ function diffInteractionTraces(left, right) {
19935
20133
 
19936
20134
  // src/sdk/runtime.ts
19937
20135
  var requireForAuthRecipeHook = createRequire(import.meta.url);
20136
+ var MUTATION_CAPTURE_FINALIZE_TIMEOUT_MS = 5e3;
19938
20137
  var OpensteerSessionRuntime = class {
19939
20138
  workspace;
19940
20139
  rootPath;
@@ -19951,12 +20150,11 @@ var OpensteerSessionRuntime = class {
19951
20150
  engine;
19952
20151
  dom;
19953
20152
  computer;
19954
- networkJournal = new NetworkJournal();
20153
+ networkHistory = new NetworkHistory();
19955
20154
  extractionDescriptors;
19956
20155
  sessionRef;
19957
20156
  pageRef;
19958
20157
  runId;
19959
- backgroundNetworkPersistence = /* @__PURE__ */ new Set();
19960
20158
  cookieJars = /* @__PURE__ */ new Map();
19961
20159
  recipeCache = /* @__PURE__ */ new Map();
19962
20160
  ownsEngine = false;
@@ -20318,34 +20516,32 @@ var OpensteerSessionRuntime = class {
20318
20516
  assertValidSemanticOperationInput("page.goto", input);
20319
20517
  const pageRef = await this.ensurePageRef();
20320
20518
  const startedAt = Date.now();
20519
+ let mutationCaptureDiagnostics;
20321
20520
  try {
20322
- const { navigation, state } = await this.runWithOperationTimeout(
20521
+ const { navigation, state } = await this.runMutationCapturedOperation(
20323
20522
  "page.goto",
20523
+ {
20524
+ ...input.captureNetwork === void 0 ? {} : { captureNetwork: input.captureNetwork },
20525
+ options
20526
+ },
20324
20527
  async (timeout) => {
20325
- const baselineRequestIds = await this.beginMutationCapture(timeout);
20326
- try {
20327
- const navigation2 = await this.navigatePage(
20328
- {
20329
- operation: "page.goto",
20330
- pageRef,
20331
- url: input.url
20332
- },
20333
- timeout
20334
- );
20335
- timeout.throwIfAborted();
20336
- await this.completeMutationCapture(timeout, baselineRequestIds, input.networkTag);
20337
- return {
20338
- navigation: navigation2,
20339
- state: await timeout.runStep(() => this.readSessionState())
20340
- };
20341
- } catch (error) {
20342
- await this.completeMutationCapture(timeout, baselineRequestIds, input.networkTag).catch(
20343
- () => void 0
20344
- );
20345
- throw error;
20346
- }
20528
+ const navigation2 = await this.navigatePage(
20529
+ {
20530
+ operation: "page.goto",
20531
+ pageRef,
20532
+ url: input.url
20533
+ },
20534
+ timeout
20535
+ );
20536
+ timeout.throwIfAborted();
20537
+ return {
20538
+ navigation: navigation2,
20539
+ state: await timeout.runStep(() => this.readSessionState())
20540
+ };
20347
20541
  },
20348
- options
20542
+ (diagnostics) => {
20543
+ mutationCaptureDiagnostics = diagnostics;
20544
+ }
20349
20545
  );
20350
20546
  await this.appendTrace({
20351
20547
  operation: "page.goto",
@@ -20354,7 +20550,8 @@ var OpensteerSessionRuntime = class {
20354
20550
  outcome: "ok",
20355
20551
  data: {
20356
20552
  url: input.url,
20357
- state
20553
+ state,
20554
+ ...buildMutationCaptureTraceData(mutationCaptureDiagnostics)
20358
20555
  },
20359
20556
  context: buildRuntimeTraceContext({
20360
20557
  sessionRef: this.sessionRef,
@@ -20370,6 +20567,7 @@ var OpensteerSessionRuntime = class {
20370
20567
  completedAt: Date.now(),
20371
20568
  outcome: "error",
20372
20569
  error,
20570
+ data: buildMutationCaptureTraceData(mutationCaptureDiagnostics),
20373
20571
  context: buildRuntimeTraceContext({
20374
20572
  sessionRef: this.sessionRef,
20375
20573
  pageRef
@@ -20382,9 +20580,11 @@ var OpensteerSessionRuntime = class {
20382
20580
  assertValidSemanticOperationInput("page.evaluate", input);
20383
20581
  const pageRef = input.pageRef ?? await this.ensurePageRef();
20384
20582
  const startedAt = Date.now();
20583
+ let mutationCaptureDiagnostics;
20385
20584
  try {
20386
- const output = await this.runWithOperationTimeout(
20585
+ const output = await this.runMutationCapturedOperation(
20387
20586
  "page.evaluate",
20587
+ { options },
20388
20588
  async (timeout) => {
20389
20589
  const remainingMs = timeout.remainingMs();
20390
20590
  const evaluated = await timeout.runStep(
@@ -20400,7 +20600,9 @@ var OpensteerSessionRuntime = class {
20400
20600
  value: toJsonValueOrNull(evaluated.data)
20401
20601
  };
20402
20602
  },
20403
- options
20603
+ (diagnostics) => {
20604
+ mutationCaptureDiagnostics = diagnostics;
20605
+ }
20404
20606
  );
20405
20607
  await this.appendTrace({
20406
20608
  operation: "page.evaluate",
@@ -20409,7 +20611,8 @@ var OpensteerSessionRuntime = class {
20409
20611
  outcome: "ok",
20410
20612
  data: {
20411
20613
  pageRef: output.pageRef,
20412
- value: output.value
20614
+ value: output.value,
20615
+ ...buildMutationCaptureTraceData(mutationCaptureDiagnostics)
20413
20616
  },
20414
20617
  context: buildRuntimeTraceContext({
20415
20618
  sessionRef: this.sessionRef,
@@ -20424,6 +20627,7 @@ var OpensteerSessionRuntime = class {
20424
20627
  completedAt: Date.now(),
20425
20628
  outcome: "error",
20426
20629
  error,
20630
+ data: buildMutationCaptureTraceData(mutationCaptureDiagnostics),
20427
20631
  context: buildRuntimeTraceContext({
20428
20632
  sessionRef: this.sessionRef,
20429
20633
  pageRef
@@ -20762,38 +20966,19 @@ var OpensteerSessionRuntime = class {
20762
20966
  }
20763
20967
  async queryNetwork(input = {}, options = {}) {
20764
20968
  assertValidSemanticOperationInput("network.query", input);
20765
- if (input.source !== "saved") {
20766
- await this.ensurePageRef();
20767
- }
20768
20969
  const root = await this.ensureRoot();
20769
20970
  const startedAt = Date.now();
20770
20971
  try {
20771
20972
  const output = await this.runWithOperationTimeout(
20772
20973
  "network.query",
20773
20974
  async (timeout) => {
20774
- if (input.source === "saved") {
20775
- await timeout.runStep(() => this.flushBackgroundNetworkPersistence());
20776
- return {
20777
- records: await timeout.runStep(
20778
- () => root.registry.savedNetwork.query({
20779
- ...input.recordId === void 0 ? {} : { recordId: input.recordId },
20780
- ...input.requestId === void 0 ? {} : { requestId: input.requestId },
20781
- ...input.actionId === void 0 ? {} : { actionId: input.actionId },
20782
- ...input.tag === void 0 ? {} : { tag: input.tag },
20783
- ...input.url === void 0 ? {} : { url: input.url },
20784
- ...input.hostname === void 0 ? {} : { hostname: input.hostname },
20785
- ...input.path === void 0 ? {} : { path: input.path },
20786
- ...input.method === void 0 ? {} : { method: input.method },
20787
- ...input.status === void 0 ? {} : { status: input.status },
20788
- ...input.resourceType === void 0 ? {} : { resourceType: input.resourceType },
20789
- ...input.includeBodies === void 0 ? {} : { includeBodies: input.includeBodies },
20790
- ...input.limit === void 0 ? {} : { limit: input.limit }
20791
- })
20792
- )
20793
- };
20794
- }
20975
+ await this.syncPersistedNetworkSelection(timeout, input, {
20976
+ includeBodies: input.includeBodies ?? false
20977
+ });
20795
20978
  return {
20796
- records: await this.queryLiveNetwork(input, timeout)
20979
+ records: await timeout.runStep(
20980
+ () => root.registry.savedNetwork.query(this.toSavedNetworkQueryInput(input))
20981
+ )
20797
20982
  };
20798
20983
  },
20799
20984
  options
@@ -20804,7 +20989,6 @@ var OpensteerSessionRuntime = class {
20804
20989
  completedAt: Date.now(),
20805
20990
  outcome: "ok",
20806
20991
  data: {
20807
- source: input.source ?? "live",
20808
20992
  includeBodies: input.includeBodies ?? false,
20809
20993
  limit: input.limit ?? 50,
20810
20994
  count: output.records.length
@@ -20830,56 +21014,42 @@ var OpensteerSessionRuntime = class {
20830
21014
  throw error;
20831
21015
  }
20832
21016
  }
20833
- async saveNetwork(input, options = {}) {
20834
- assertValidSemanticOperationInput("network.save", input);
20835
- await this.ensurePageRef();
21017
+ async tagNetwork(input, options = {}) {
21018
+ assertValidSemanticOperationInput("network.tag", input);
20836
21019
  const root = await this.ensureRoot();
21020
+ const filter = this.toQueryInputFromTagInput(input);
21021
+ const savedFilter = this.toSavedNetworkQueryInput(filter);
20837
21022
  const startedAt = Date.now();
20838
21023
  try {
20839
21024
  const output = await this.runWithOperationTimeout(
20840
- "network.save",
21025
+ "network.tag",
20841
21026
  async (timeout) => {
20842
- const records = await this.queryLiveNetwork(
20843
- {
20844
- includeBodies: true,
20845
- source: "live",
20846
- ...input.pageRef === void 0 ? {} : { pageRef: input.pageRef },
20847
- ...input.recordId === void 0 ? {} : { recordId: input.recordId },
20848
- ...input.requestId === void 0 ? {} : { requestId: input.requestId },
20849
- ...input.actionId === void 0 ? {} : { actionId: input.actionId },
20850
- ...input.url === void 0 ? {} : { url: input.url },
20851
- ...input.hostname === void 0 ? {} : { hostname: input.hostname },
20852
- ...input.path === void 0 ? {} : { path: input.path },
20853
- ...input.method === void 0 ? {} : { method: input.method },
20854
- ...input.status === void 0 ? {} : { status: input.status },
20855
- ...input.resourceType === void 0 ? {} : { resourceType: input.resourceType }
20856
- },
20857
- timeout,
20858
- { ignoreLimit: true, redactSecretHeaders: false }
20859
- );
20860
- this.networkJournal.addTag(records, input.tag);
21027
+ const records = await this.syncPersistedNetworkSelection(timeout, filter, {
21028
+ includeBodies: false
21029
+ });
21030
+ this.networkHistory.addTag(records, input.tag);
20861
21031
  return {
20862
- savedCount: await timeout.runStep(
20863
- () => root.registry.savedNetwork.save(records, input.tag)
21032
+ taggedCount: await timeout.runStep(
21033
+ () => root.registry.savedNetwork.tagByFilter(savedFilter, input.tag)
20864
21034
  )
20865
21035
  };
20866
21036
  },
20867
21037
  options
20868
21038
  );
20869
21039
  await this.appendTrace({
20870
- operation: "network.save",
21040
+ operation: "network.tag",
20871
21041
  startedAt,
20872
21042
  completedAt: Date.now(),
20873
21043
  outcome: "ok",
20874
21044
  data: {
20875
21045
  tag: input.tag,
20876
- savedCount: output.savedCount
21046
+ taggedCount: output.taggedCount
20877
21047
  }
20878
21048
  });
20879
21049
  return output;
20880
21050
  } catch (error) {
20881
21051
  await this.appendTrace({
20882
- operation: "network.save",
21052
+ operation: "network.tag",
20883
21053
  startedAt,
20884
21054
  completedAt: Date.now(),
20885
21055
  outcome: "error",
@@ -20896,7 +21066,31 @@ var OpensteerSessionRuntime = class {
20896
21066
  const output = await this.runWithOperationTimeout(
20897
21067
  "network.clear",
20898
21068
  async (timeout) => {
20899
- await timeout.runStep(() => this.flushBackgroundNetworkPersistence());
21069
+ if (this.sessionRef !== void 0) {
21070
+ if (input.capture !== void 0 || input.tag !== void 0) {
21071
+ const records = await this.queryLiveNetwork(
21072
+ {
21073
+ ...input.capture === void 0 ? {} : { capture: input.capture },
21074
+ ...input.tag === void 0 ? {} : { tag: input.tag }
21075
+ },
21076
+ timeout,
21077
+ {
21078
+ ignoreLimit: true
21079
+ }
21080
+ );
21081
+ this.networkHistory.tombstoneRequestIds(
21082
+ records.map((record) => record.record.requestId)
21083
+ );
21084
+ } else {
21085
+ const liveRequestIds = await this.readLiveRequestIds(timeout, {
21086
+ includeCurrentPageOnly: false
21087
+ });
21088
+ this.networkHistory.tombstoneRequestIds(liveRequestIds);
21089
+ }
21090
+ }
21091
+ if (input.capture === void 0 && input.tag === void 0) {
21092
+ this.networkHistory.tombstoneRequestIds(this.networkHistory.getKnownRequestIds());
21093
+ }
20900
21094
  return {
20901
21095
  clearedCount: await timeout.runStep(() => root.registry.savedNetwork.clear(input))
20902
21096
  };
@@ -20909,6 +21103,7 @@ var OpensteerSessionRuntime = class {
20909
21103
  completedAt: Date.now(),
20910
21104
  outcome: "ok",
20911
21105
  data: {
21106
+ ...input.capture === void 0 ? {} : { capture: input.capture },
20912
21107
  ...input.tag === void 0 ? {} : { tag: input.tag },
20913
21108
  clearedCount: output.clearedCount
20914
21109
  }
@@ -21613,7 +21808,6 @@ var OpensteerSessionRuntime = class {
21613
21808
  });
21614
21809
  const networkRecords = await this.queryLiveNetwork(
21615
21810
  {
21616
- source: "live",
21617
21811
  pageRef,
21618
21812
  ...input.network?.url === void 0 ? {} : { url: input.network.url },
21619
21813
  ...input.network?.hostname === void 0 ? {} : { hostname: input.network.hostname },
@@ -21629,7 +21823,7 @@ var OpensteerSessionRuntime = class {
21629
21823
  );
21630
21824
  const persistedNetwork = filterReverseObservationWindow(
21631
21825
  networkRecords.filter(isReverseRelevantNetworkRecord),
21632
- this.networkJournal,
21826
+ this.networkHistory,
21633
21827
  input.captureWindowMs
21634
21828
  );
21635
21829
  const fallbackSavedNetwork = persistedNetwork.length === 0 ? (await root.registry.savedNetwork.query({
@@ -21644,7 +21838,10 @@ var OpensteerSessionRuntime = class {
21644
21838
  const observationId = `observation:${randomUUID()}`;
21645
21839
  const networkTag = `reverse-case:${caseRecord.id}:${observationId}`;
21646
21840
  if (observationNetwork.length > 0) {
21647
- await root.registry.savedNetwork.save(observationNetwork, networkTag);
21841
+ await root.registry.savedNetwork.save(observationNetwork, {
21842
+ tag: networkTag,
21843
+ bodyWriteMode: input.network?.includeBodies === false ? "metadata-only" : "authoritative"
21844
+ });
21648
21845
  }
21649
21846
  const scriptArtifactIds = input.includeScripts === false ? [] : (await this.captureScriptsInternal(
21650
21847
  pageRef,
@@ -21740,7 +21937,7 @@ var OpensteerSessionRuntime = class {
21740
21937
  includeBodies: true,
21741
21938
  redactSecretHeaders: false
21742
21939
  }),
21743
- observedAt: this.networkJournal.getObservedAt(recordId)
21940
+ observedAt: this.networkHistory.getObservedAt(recordId)
21744
21941
  }))
21745
21942
  );
21746
21943
  const clusteredRecords = observationRecords.map((entry) => {
@@ -22088,7 +22285,9 @@ var OpensteerSessionRuntime = class {
22088
22285
  };
22089
22286
  }
22090
22287
  const bindings = /* @__PURE__ */ new Map();
22091
- const baselineRequestIds = await this.beginMutationCapture(timeout);
22288
+ const baselineRequestIds = await this.readLiveRequestIds(timeout, {
22289
+ includeCurrentPageOnly: true
22290
+ });
22092
22291
  const pageRef = explicitPageRef ?? await this.ensurePageRef();
22093
22292
  const validatorMap = new Map(
22094
22293
  packageRecord.payload.validators.map((validator) => [validator.id, validator])
@@ -22680,7 +22879,10 @@ var OpensteerSessionRuntime = class {
22680
22879
  timeout.signal
22681
22880
  )).filter((record) => !baselineRequestIds.has(record.record.requestId));
22682
22881
  if (deltaRecords.length > 0) {
22683
- await root.registry.savedNetwork.save(deltaRecords, `interaction:${pageRef}`);
22882
+ await root.registry.savedNetwork.save(deltaRecords, {
22883
+ tag: `interaction:${pageRef}`,
22884
+ bodyWriteMode: "authoritative"
22885
+ });
22684
22886
  }
22685
22887
  const trace = await root.registry.interactionTraces.write({
22686
22888
  key: input.key ?? buildInteractionTraceKey(pageInfo.url),
@@ -23023,7 +23225,7 @@ var OpensteerSessionRuntime = class {
23023
23225
  includeBodies: true
23024
23226
  });
23025
23227
  const inferred = inferRequestPlanFromNetworkRecord(source, input, {
23026
- ...this.networkJournal.getObservedAt(source.recordId) === void 0 ? {} : { observedAt: this.networkJournal.getObservedAt(source.recordId) }
23228
+ ...this.networkHistory.getObservedAt(source.recordId) === void 0 ? {} : { observedAt: this.networkHistory.getObservedAt(source.recordId) }
23027
23229
  });
23028
23230
  return timeout.runStep(
23029
23231
  () => root.registry.requestPlans.write({
@@ -23757,33 +23959,38 @@ var OpensteerSessionRuntime = class {
23757
23959
  assertValidSemanticOperationInput("computer.execute", input);
23758
23960
  const pageRef = await this.ensurePageRef();
23759
23961
  const startedAt = Date.now();
23962
+ let mutationCaptureDiagnostics;
23963
+ let boundaryDiagnostics;
23760
23964
  try {
23761
- const { artifacts, output } = await this.runWithOperationTimeout(
23965
+ const { artifacts, output } = await this.runMutationCapturedOperation(
23762
23966
  "computer.execute",
23967
+ {
23968
+ ...input.captureNetwork === void 0 ? {} : { captureNetwork: input.captureNetwork },
23969
+ options
23970
+ },
23763
23971
  async (timeout) => {
23764
- const baselineRequestIds = await this.beginMutationCapture(timeout);
23765
23972
  try {
23766
23973
  const output2 = await this.requireComputer().execute({
23767
23974
  pageRef,
23768
23975
  input,
23769
23976
  timeout
23770
23977
  });
23978
+ boundaryDiagnostics = takeActionBoundaryDiagnostics(timeout.signal);
23771
23979
  timeout.throwIfAborted();
23772
23980
  this.pageRef = output2.pageRef;
23773
- await this.completeMutationCapture(timeout, baselineRequestIds, input.networkTag);
23774
23981
  const artifacts2 = await this.persistComputerArtifacts(output2, timeout);
23775
23982
  return {
23776
23983
  artifacts: { manifests: artifacts2.manifests },
23777
23984
  output: artifacts2.output
23778
23985
  };
23779
23986
  } catch (error) {
23780
- await this.completeMutationCapture(timeout, baselineRequestIds, input.networkTag).catch(
23781
- () => void 0
23782
- );
23987
+ boundaryDiagnostics ??= takeActionBoundaryDiagnostics(timeout.signal);
23783
23988
  throw error;
23784
23989
  }
23785
23990
  },
23786
- options
23991
+ (diagnostics) => {
23992
+ mutationCaptureDiagnostics = diagnostics;
23993
+ }
23787
23994
  );
23788
23995
  await this.appendTrace({
23789
23996
  operation: "computer.execute",
@@ -23799,6 +24006,8 @@ var OpensteerSessionRuntime = class {
23799
24006
  nativeViewport: output.nativeViewport,
23800
24007
  displayScale: output.displayScale,
23801
24008
  timing: output.timing,
24009
+ ...boundaryDiagnostics === void 0 ? {} : { settle: boundaryDiagnostics },
24010
+ ...buildMutationCaptureTraceData(mutationCaptureDiagnostics),
23802
24011
  ...output.trace === void 0 ? {} : { trace: output.trace }
23803
24012
  },
23804
24013
  context: buildRuntimeTraceContext({
@@ -23817,6 +24026,10 @@ var OpensteerSessionRuntime = class {
23817
24026
  completedAt: Date.now(),
23818
24027
  outcome: "error",
23819
24028
  error,
24029
+ data: {
24030
+ ...boundaryDiagnostics === void 0 ? {} : { settle: boundaryDiagnostics },
24031
+ ...buildMutationCaptureTraceData(mutationCaptureDiagnostics)
24032
+ },
23820
24033
  context: buildRuntimeTraceContext({
23821
24034
  sessionRef: this.sessionRef,
23822
24035
  pageRef: this.pageRef
@@ -23835,7 +24048,7 @@ var OpensteerSessionRuntime = class {
23835
24048
  await this.runWithOperationTimeout(
23836
24049
  "session.close",
23837
24050
  async (timeout) => {
23838
- await timeout.runStep(() => this.flushBackgroundNetworkPersistence());
24051
+ await timeout.runStep(() => this.flushPersistedNetworkHistory());
23839
24052
  if (engine === void 0) {
23840
24053
  return;
23841
24054
  }
@@ -23904,10 +24117,7 @@ var OpensteerSessionRuntime = class {
23904
24117
  }
23905
24118
  async disconnect() {
23906
24119
  try {
23907
- await this.flushBackgroundNetworkPersistence();
23908
- if (this.sessionRef !== void 0 && this.pageRef !== void 0) {
23909
- await this.saveNetwork({ tag: "auto" }).catch(() => void 0);
23910
- }
24120
+ await this.flushPersistedNetworkHistory();
23911
24121
  } finally {
23912
24122
  await this.resetRuntimeState({
23913
24123
  disposeEngine: true
@@ -23923,33 +24133,38 @@ var OpensteerSessionRuntime = class {
23923
24133
  async runDomAction(operation, input, executor, options = {}) {
23924
24134
  const pageRef = await this.ensurePageRef();
23925
24135
  const startedAt = Date.now();
24136
+ let mutationCaptureDiagnostics;
24137
+ let boundaryDiagnostics;
23926
24138
  try {
23927
- const { executed, preparedTarget } = await this.runWithOperationTimeout(
24139
+ const { executed, preparedTarget } = await this.runMutationCapturedOperation(
23928
24140
  operation,
24141
+ {
24142
+ ...input.captureNetwork === void 0 ? {} : { captureNetwork: input.captureNetwork },
24143
+ options
24144
+ },
23929
24145
  async (timeout) => {
23930
- const baselineRequestIds = await this.beginMutationCapture(timeout);
24146
+ const preparedTarget2 = await this.prepareDomTarget(
24147
+ pageRef,
24148
+ operation,
24149
+ input.target,
24150
+ input.persistAsDescription,
24151
+ timeout
24152
+ );
23931
24153
  try {
23932
- const preparedTarget2 = await this.prepareDomTarget(
23933
- pageRef,
23934
- operation,
23935
- input.target,
23936
- input.persistAsDescription,
23937
- timeout
23938
- );
23939
24154
  const executed2 = await executor(pageRef, preparedTarget2.target, timeout);
23940
- await this.completeMutationCapture(timeout, baselineRequestIds, input.networkTag);
24155
+ boundaryDiagnostics = takeActionBoundaryDiagnostics(timeout.signal);
23941
24156
  return {
23942
24157
  executed: executed2,
23943
24158
  preparedTarget: preparedTarget2
23944
24159
  };
23945
24160
  } catch (error) {
23946
- await this.completeMutationCapture(timeout, baselineRequestIds, input.networkTag).catch(
23947
- () => void 0
23948
- );
24161
+ boundaryDiagnostics ??= takeActionBoundaryDiagnostics(timeout.signal);
23949
24162
  throw error;
23950
24163
  }
23951
24164
  },
23952
- options
24165
+ (diagnostics) => {
24166
+ mutationCaptureDiagnostics = diagnostics;
24167
+ }
23953
24168
  );
23954
24169
  const output = toOpensteerActionResult(executed.result, preparedTarget.persistedDescription);
23955
24170
  await this.appendTrace({
@@ -23960,7 +24175,9 @@ var OpensteerSessionRuntime = class {
23960
24175
  data: {
23961
24176
  target: output.target,
23962
24177
  ...output.point === void 0 ? {} : { point: output.point },
23963
- ...output.persistedDescription === void 0 ? {} : { persistedDescription: output.persistedDescription }
24178
+ ...output.persistedDescription === void 0 ? {} : { persistedDescription: output.persistedDescription },
24179
+ ...boundaryDiagnostics === void 0 ? {} : { settle: boundaryDiagnostics },
24180
+ ...buildMutationCaptureTraceData(mutationCaptureDiagnostics)
23964
24181
  },
23965
24182
  context: buildRuntimeTraceContext({
23966
24183
  sessionRef: this.sessionRef,
@@ -23978,6 +24195,10 @@ var OpensteerSessionRuntime = class {
23978
24195
  completedAt: Date.now(),
23979
24196
  outcome: "error",
23980
24197
  error,
24198
+ data: {
24199
+ ...boundaryDiagnostics === void 0 ? {} : { settle: boundaryDiagnostics },
24200
+ ...buildMutationCaptureTraceData(mutationCaptureDiagnostics)
24201
+ },
23981
24202
  context: buildRuntimeTraceContext({
23982
24203
  sessionRef: this.sessionRef,
23983
24204
  pageRef
@@ -23999,7 +24220,10 @@ var OpensteerSessionRuntime = class {
23999
24220
  };
24000
24221
  }
24001
24222
  if (target.kind === "element") {
24002
- const elementTarget = { kind: "selector", selector: `[c="${String(target.element)}"]` };
24223
+ const elementTarget = {
24224
+ kind: "selector",
24225
+ selector: `[c="${String(target.element)}"]`
24226
+ };
24003
24227
  const resolved2 = await timeout.runStep(
24004
24228
  () => this.requireDom().resolveTarget({
24005
24229
  pageRef,
@@ -24062,11 +24286,11 @@ var OpensteerSessionRuntime = class {
24062
24286
  };
24063
24287
  }
24064
24288
  async queryLiveNetwork(input, timeout, options = {}) {
24065
- const requestIds = resolveLiveQueryRequestIds(input, this.networkJournal);
24289
+ const requestIds = resolveLiveQueryRequestIds(input, this.networkHistory);
24066
24290
  if (requestIds !== void 0 && requestIds.length === 0) {
24067
24291
  return [];
24068
24292
  }
24069
- const pageRef = resolveLiveQueryPageRef(input, this.pageRef, requestIds, this.networkJournal);
24293
+ const pageRef = resolveLiveQueryPageRef(input, this.pageRef, requestIds, this.networkHistory);
24070
24294
  const includeCurrentPageOnly = pageRef === void 0 && input.recordId === void 0;
24071
24295
  const metadataRecords = await timeout.runStep(
24072
24296
  () => this.readLiveNetworkRecords(
@@ -24084,7 +24308,7 @@ var OpensteerSessionRuntime = class {
24084
24308
  const filtered = filterNetworkQueryRecords(metadataRecords, {
24085
24309
  ...input.recordId === void 0 ? {} : { recordId: input.recordId },
24086
24310
  ...input.requestId === void 0 ? {} : { requestId: input.requestId },
24087
- ...input.actionId === void 0 ? {} : { actionId: input.actionId },
24311
+ ...input.capture === void 0 ? {} : { capture: input.capture },
24088
24312
  ...input.tag === void 0 ? {} : { tag: input.tag },
24089
24313
  ...input.url === void 0 ? {} : { url: input.url },
24090
24314
  ...input.hostname === void 0 ? {} : { hostname: input.hostname },
@@ -24093,7 +24317,7 @@ var OpensteerSessionRuntime = class {
24093
24317
  ...input.status === void 0 ? {} : { status: input.status },
24094
24318
  ...input.resourceType === void 0 ? {} : { resourceType: input.resourceType }
24095
24319
  });
24096
- const sorted = sortLiveNetworkRecords(filtered, this.networkJournal);
24320
+ const sorted = sortLiveNetworkRecords(filtered, this.networkHistory);
24097
24321
  const limit = options.ignoreLimit ? sorted.length : Math.max(1, Math.min(input.limit ?? 50, 200));
24098
24322
  const limited = sorted.slice(0, limit);
24099
24323
  if (!(input.includeBodies ?? false) || limited.length === 0) {
@@ -24290,40 +24514,95 @@ var OpensteerSessionRuntime = class {
24290
24514
  artifactId: manifest.artifactId
24291
24515
  };
24292
24516
  }
24293
- beginMutationCapture(timeout) {
24294
- return this.readLiveRequestIds(timeout, {
24295
- includeCurrentPageOnly: true
24296
- });
24297
- }
24298
- async completeMutationCapture(timeout, baselineRequestIds, networkTag) {
24299
- const records = await timeout.runStep(
24300
- () => this.readLiveNetworkRecords(
24301
- {
24302
- includeBodies: false,
24303
- includeCurrentPageOnly: true
24517
+ async runMutationCapturedOperation(operation, input, execute, onFinalized) {
24518
+ let plan;
24519
+ try {
24520
+ const result = await this.runWithOperationTimeout(
24521
+ operation,
24522
+ async (timeout) => {
24523
+ plan = await this.beginMutationCapture(timeout, input.captureNetwork);
24524
+ return execute(timeout);
24304
24525
  },
24305
- timeout.signal
24306
- )
24526
+ input.options
24527
+ );
24528
+ const diagnostics = await this.finalizeMutationCaptureBestEffort(plan);
24529
+ onFinalized?.(diagnostics);
24530
+ return result;
24531
+ } catch (error) {
24532
+ const diagnostics = await this.finalizeMutationCaptureBestEffort(plan);
24533
+ onFinalized?.(diagnostics);
24534
+ throw error;
24535
+ }
24536
+ }
24537
+ async beginMutationCapture(timeout, capture) {
24538
+ if (capture === void 0) {
24539
+ return void 0;
24540
+ }
24541
+ return {
24542
+ baselineRequestIds: await this.readLiveRequestIds(timeout, {
24543
+ includeCurrentPageOnly: true
24544
+ }),
24545
+ capture
24546
+ };
24547
+ }
24548
+ async finalizeMutationCaptureBestEffort(plan) {
24549
+ if (plan === void 0) {
24550
+ return {};
24551
+ }
24552
+ try {
24553
+ await withDetachedTimeoutSignal(MUTATION_CAPTURE_FINALIZE_TIMEOUT_MS, async (signal) => {
24554
+ await this.completeMutationCaptureWithSignal(signal, plan);
24555
+ });
24556
+ return {};
24557
+ } catch (error) {
24558
+ return {
24559
+ finalizeError: normalizeOpensteerError(error)
24560
+ };
24561
+ }
24562
+ }
24563
+ async completeMutationCaptureWithSignal(signal, plan) {
24564
+ const records = await this.readLiveNetworkRecords(
24565
+ {
24566
+ includeBodies: false,
24567
+ includeCurrentPageOnly: true
24568
+ },
24569
+ signal
24307
24570
  );
24308
- const delta = records.filter((record) => !baselineRequestIds.has(record.record.requestId));
24571
+ const delta = records.filter((record) => !plan.baselineRequestIds.has(record.record.requestId));
24309
24572
  if (delta.length === 0) {
24310
24573
  return;
24311
24574
  }
24312
- this.networkJournal.assignActionId(delta, `action:${randomUUID()}`);
24313
- if (networkTag === void 0) {
24314
- return;
24315
- }
24316
- this.networkJournal.addTag(delta, networkTag);
24317
- this.scheduleBackgroundNetworkSaveByRequestIds(
24575
+ this.networkHistory.assignCapture(delta, plan.capture);
24576
+ await this.persistLiveRequestIdsWithSignal(
24318
24577
  delta.map((record) => record.record.requestId),
24319
- networkTag
24578
+ signal,
24579
+ {
24580
+ includeCurrentPageOnly: true
24581
+ }
24320
24582
  );
24321
24583
  }
24322
24584
  async resolveNetworkRecordByRecordId(recordId, timeout, options) {
24323
24585
  const root = await this.ensureRoot();
24586
+ await this.syncPersistedNetworkSelection(
24587
+ timeout,
24588
+ {
24589
+ recordId,
24590
+ includeBodies: options.includeBodies
24591
+ },
24592
+ {
24593
+ includeBodies: options.includeBodies
24594
+ }
24595
+ );
24596
+ const saved = await timeout.runStep(
24597
+ () => root.registry.savedNetwork.getByRecordId(recordId, {
24598
+ includeBodies: options.includeBodies
24599
+ })
24600
+ );
24601
+ if (saved) {
24602
+ return saved;
24603
+ }
24324
24604
  const live = await this.queryLiveNetwork(
24325
24605
  {
24326
- source: "live",
24327
24606
  recordId,
24328
24607
  includeBodies: options.includeBodies,
24329
24608
  limit: 1
@@ -24334,24 +24613,15 @@ var OpensteerSessionRuntime = class {
24334
24613
  ...options.redactSecretHeaders === void 0 ? {} : { redactSecretHeaders: options.redactSecretHeaders }
24335
24614
  }
24336
24615
  );
24337
- if (live.length > 0) {
24616
+ if (live[0] !== void 0) {
24338
24617
  return live[0];
24339
24618
  }
24340
- await timeout.runStep(() => this.flushBackgroundNetworkPersistence());
24341
- const saved = await timeout.runStep(
24342
- () => root.registry.savedNetwork.getByRecordId(recordId, {
24343
- includeBodies: options.includeBodies
24344
- })
24345
- );
24346
- if (!saved) {
24347
- throw new OpensteerProtocolError("not-found", `network record ${recordId} was not found`, {
24348
- details: {
24349
- recordId,
24350
- kind: "network-record"
24351
- }
24352
- });
24353
- }
24354
- return saved;
24619
+ throw new OpensteerProtocolError("not-found", `network record ${recordId} was not found`, {
24620
+ details: {
24621
+ recordId,
24622
+ kind: "network-record"
24623
+ }
24624
+ });
24355
24625
  }
24356
24626
  resolveCurrentStateSource() {
24357
24627
  const ownership = this.sessionInfoBase.provider?.ownership;
@@ -24591,7 +24861,6 @@ var OpensteerSessionRuntime = class {
24591
24861
  timeout.throwIfAborted();
24592
24862
  const records = await this.queryLiveNetwork(
24593
24863
  {
24594
- source: "live",
24595
24864
  pageRef,
24596
24865
  url,
24597
24866
  method,
@@ -24622,7 +24891,6 @@ var OpensteerSessionRuntime = class {
24622
24891
  timeout.throwIfAborted();
24623
24892
  const records = await this.queryLiveNetwork(
24624
24893
  {
24625
- source: "live",
24626
24894
  pageRef,
24627
24895
  ...filter.url === void 0 ? {} : { url: filter.url },
24628
24896
  ...filter.host === void 0 ? {} : { hostname: filter.host },
@@ -24688,12 +24956,12 @@ var OpensteerSessionRuntime = class {
24688
24956
  };
24689
24957
  }
24690
24958
  }
24691
- async readLiveNetworkRecords(input, signal) {
24959
+ async readBrowserNetworkRecords(input, signal) {
24692
24960
  const sessionRef = this.sessionRef;
24693
24961
  if (!sessionRef) {
24694
24962
  throw new Error("Opensteer session is not initialized");
24695
24963
  }
24696
- const records = await this.requireEngine().getNetworkRecords({
24964
+ return this.requireEngine().getNetworkRecords({
24697
24965
  sessionRef,
24698
24966
  ...input.includeCurrentPageOnly === false || input.pageRef !== void 0 ? input.pageRef === void 0 ? {} : { pageRef: input.pageRef } : this.pageRef === void 0 ? {} : { pageRef: this.pageRef },
24699
24967
  ...input.requestIds === void 0 ? {} : { requestIds: input.requestIds },
@@ -24706,10 +24974,103 @@ var OpensteerSessionRuntime = class {
24706
24974
  includeBodies: input.includeBodies,
24707
24975
  signal
24708
24976
  });
24709
- return this.networkJournal.sync(records, {
24977
+ }
24978
+ async readLiveNetworkRecords(input, signal) {
24979
+ const records = await this.readBrowserNetworkRecords(input, signal);
24980
+ return this.networkHistory.materialize(records, {
24710
24981
  redactSecretHeaders: input.redactSecretHeaders ?? true
24711
24982
  });
24712
24983
  }
24984
+ async persistLiveRequestIds(requestIds, timeout, options) {
24985
+ return timeout.runStep(
24986
+ () => this.persistLiveRequestIdsWithSignal(requestIds, timeout.signal, options)
24987
+ );
24988
+ }
24989
+ async persistLiveRequestIdsWithSignal(requestIds, signal, options) {
24990
+ if (requestIds.length === 0) {
24991
+ return [];
24992
+ }
24993
+ const root = await this.ensureRoot();
24994
+ const browserRecords = await this.readBrowserNetworkRecords(
24995
+ {
24996
+ includeBodies: true,
24997
+ includeCurrentPageOnly: options.includeCurrentPageOnly,
24998
+ ...options.pageRef === void 0 ? {} : { pageRef: options.pageRef },
24999
+ requestIds
25000
+ },
25001
+ signal
25002
+ );
25003
+ return this.networkHistory.persist(browserRecords, root.registry.savedNetwork, {
25004
+ bodyWriteMode: "authoritative",
25005
+ redactSecretHeaders: false
25006
+ });
25007
+ }
25008
+ async syncPersistedNetworkSelection(timeout, input, options) {
25009
+ if (this.sessionRef === void 0) {
25010
+ return [];
25011
+ }
25012
+ const requestIds = resolveLiveQueryRequestIds(input, this.networkHistory);
25013
+ if (requestIds !== void 0 && requestIds.length === 0) {
25014
+ return [];
25015
+ }
25016
+ const pageRef = resolveLiveQueryPageRef(input, this.pageRef, requestIds, this.networkHistory);
25017
+ const includeCurrentPageOnly = pageRef === void 0 && input.recordId === void 0;
25018
+ const browserRecords = await timeout.runStep(
25019
+ () => this.readBrowserNetworkRecords(
25020
+ {
25021
+ ...pageRef === void 0 ? {} : { pageRef },
25022
+ ...requestIds === void 0 ? {} : { requestIds },
25023
+ ...input.url === void 0 ? {} : { url: input.url },
25024
+ ...input.hostname === void 0 ? {} : { hostname: input.hostname },
25025
+ ...input.path === void 0 ? {} : { path: input.path },
25026
+ ...input.method === void 0 ? {} : { method: input.method },
25027
+ ...input.status === void 0 ? {} : { status: input.status },
25028
+ ...input.resourceType === void 0 ? {} : { resourceType: input.resourceType },
25029
+ includeBodies: options.includeBodies,
25030
+ includeCurrentPageOnly
25031
+ },
25032
+ timeout.signal
25033
+ )
25034
+ );
25035
+ const root = await this.ensureRoot();
25036
+ return timeout.runStep(
25037
+ () => this.networkHistory.persist(browserRecords, root.registry.savedNetwork, {
25038
+ bodyWriteMode: options.includeBodies ? "authoritative" : "metadata-only",
25039
+ redactSecretHeaders: false
25040
+ })
25041
+ );
25042
+ }
25043
+ toSavedNetworkQueryInput(input) {
25044
+ return {
25045
+ ...input.pageRef === void 0 ? {} : { pageRef: input.pageRef },
25046
+ ...input.recordId === void 0 ? {} : { recordId: input.recordId },
25047
+ ...input.requestId === void 0 ? {} : { requestId: input.requestId },
25048
+ ...input.capture === void 0 ? {} : { capture: input.capture },
25049
+ ...input.tag === void 0 ? {} : { tag: input.tag },
25050
+ ...input.url === void 0 ? {} : { url: input.url },
25051
+ ...input.hostname === void 0 ? {} : { hostname: input.hostname },
25052
+ ...input.path === void 0 ? {} : { path: input.path },
25053
+ ...input.method === void 0 ? {} : { method: input.method },
25054
+ ...input.status === void 0 ? {} : { status: input.status },
25055
+ ...input.resourceType === void 0 ? {} : { resourceType: input.resourceType },
25056
+ ...input.includeBodies === void 0 ? {} : { includeBodies: input.includeBodies },
25057
+ ...input.limit === void 0 ? {} : { limit: input.limit }
25058
+ };
25059
+ }
25060
+ toQueryInputFromTagInput(input) {
25061
+ return {
25062
+ ...input.pageRef === void 0 ? {} : { pageRef: input.pageRef },
25063
+ ...input.recordId === void 0 ? {} : { recordId: input.recordId },
25064
+ ...input.requestId === void 0 ? {} : { requestId: input.requestId },
25065
+ ...input.capture === void 0 ? {} : { capture: input.capture },
25066
+ ...input.url === void 0 ? {} : { url: input.url },
25067
+ ...input.hostname === void 0 ? {} : { hostname: input.hostname },
25068
+ ...input.path === void 0 ? {} : { path: input.path },
25069
+ ...input.method === void 0 ? {} : { method: input.method },
25070
+ ...input.status === void 0 ? {} : { status: input.status },
25071
+ ...input.resourceType === void 0 ? {} : { resourceType: input.resourceType }
25072
+ };
25073
+ }
24713
25074
  async readLiveRequestIds(timeout, options) {
24714
25075
  const records = await timeout.runStep(
24715
25076
  () => this.readLiveNetworkRecords(
@@ -24733,7 +25094,17 @@ var OpensteerSessionRuntime = class {
24733
25094
  )
24734
25095
  );
24735
25096
  const delta = records.filter((record) => !baselineRequestIds.has(record.record.requestId));
24736
- return sortLiveNetworkRecords(delta, this.networkJournal)[0]?.recordId;
25097
+ if (delta.length === 0) {
25098
+ return void 0;
25099
+ }
25100
+ await this.persistLiveRequestIds(
25101
+ delta.map((record) => record.record.requestId),
25102
+ timeout,
25103
+ {
25104
+ includeCurrentPageOnly: options.includeCurrentPageOnly
25105
+ }
25106
+ );
25107
+ return sortLiveNetworkRecords(delta, this.networkHistory)[0]?.recordId;
24737
25108
  }
24738
25109
  async executeTransportRequestWithJournal(request, timeout, sessionRef) {
24739
25110
  const baselineRequestIds = await this.readLiveRequestIds(timeout, {
@@ -25017,7 +25388,6 @@ var OpensteerSessionRuntime = class {
25017
25388
  const syntheticSessionRef = binding?.sessionRef ?? createSessionRef(`${transportLabel}-${this.workspace}`);
25018
25389
  const record = {
25019
25390
  recordId,
25020
- source: "saved",
25021
25391
  savedAt: now,
25022
25392
  record: {
25023
25393
  kind: "http",
@@ -25039,7 +25409,10 @@ var OpensteerSessionRuntime = class {
25039
25409
  ...response.body === void 0 ? {} : { responseBody: toProtocolBodyPayload(response.body) }
25040
25410
  }
25041
25411
  };
25042
- await root.registry.savedNetwork.save([record], tag);
25412
+ await root.registry.savedNetwork.save([record], {
25413
+ bodyWriteMode: "authoritative",
25414
+ ...tag === void 0 ? {} : { tag }
25415
+ });
25043
25416
  return recordId;
25044
25417
  }
25045
25418
  async executeResolvedRequestPlan(plan, input, timeout, binding) {
@@ -25412,7 +25785,6 @@ var OpensteerSessionRuntime = class {
25412
25785
  const record = await pollUntilResult(timeout, async () => {
25413
25786
  const matches = await this.queryLiveNetwork(
25414
25787
  {
25415
- source: "live",
25416
25788
  ...step.url === void 0 ? {} : { url: interpolateTemplate(step.url, variables) },
25417
25789
  ...step.hostname === void 0 ? {} : { hostname: interpolateTemplate(step.hostname, variables) },
25418
25790
  ...step.path === void 0 ? {} : { path: interpolateTemplate(step.path, variables) },
@@ -25870,37 +26242,7 @@ var OpensteerSessionRuntime = class {
25870
26242
  const pageUrl = step.pageUrl === void 0 ? void 0 : interpolateTemplate(step.pageUrl, variables);
25871
26243
  return snapshot.sessionStorage?.filter((entry) => entry.origin === origin).find((entry) => pageUrl === void 0 || entry.origin === new URL(pageUrl).origin)?.entries.find((entry) => entry.key === key)?.value;
25872
26244
  }
25873
- scheduleBackgroundNetworkSaveByRequestIds(requestIds, tag) {
25874
- const task = (async () => {
25875
- const root = await this.ensureRoot();
25876
- const requestIdSet = new Set(requestIds);
25877
- const records = await this.readLiveNetworkRecords(
25878
- {
25879
- includeBodies: true,
25880
- includeCurrentPageOnly: false,
25881
- ...this.pageRef === void 0 ? {} : { pageRef: this.pageRef },
25882
- requestIds,
25883
- redactSecretHeaders: false
25884
- },
25885
- new AbortController().signal
25886
- );
25887
- const filtered = records.filter((record) => requestIdSet.has(record.record.requestId));
25888
- if (filtered.length === 0) {
25889
- return;
25890
- }
25891
- await root.registry.savedNetwork.save(filtered, tag);
25892
- })();
25893
- this.backgroundNetworkPersistence.add(task);
25894
- task.finally(() => {
25895
- this.backgroundNetworkPersistence.delete(task);
25896
- });
25897
- void task.catch(() => void 0);
25898
- }
25899
- async flushBackgroundNetworkPersistence() {
25900
- if (this.backgroundNetworkPersistence.size === 0) {
25901
- return;
25902
- }
25903
- await Promise.all([...this.backgroundNetworkPersistence]);
26245
+ async flushPersistedNetworkHistory() {
25904
26246
  }
25905
26247
  toDomTargetRef(target) {
25906
26248
  if (target.kind === "description") {
@@ -25935,7 +26277,9 @@ var OpensteerSessionRuntime = class {
25935
26277
  ...workspace.registry,
25936
26278
  ...overrides.requestPlans === void 0 ? {} : { requestPlans: overrides.requestPlans },
25937
26279
  ...overrides.authRecipes === void 0 ? {} : { authRecipes: overrides.authRecipes },
25938
- ...overrides.recipes === void 0 ? {} : { recipes: overrides.recipes }
26280
+ ...overrides.recipes === void 0 ? {} : { recipes: overrides.recipes },
26281
+ ...overrides.reverseCases === void 0 ? {} : { reverseCases: overrides.reverseCases },
26282
+ ...overrides.reversePackages === void 0 ? {} : { reversePackages: overrides.reversePackages }
25939
26283
  }
25940
26284
  };
25941
26285
  } else {
@@ -26192,8 +26536,7 @@ var OpensteerSessionRuntime = class {
26192
26536
  }
26193
26537
  async resetRuntimeState(options) {
26194
26538
  const engine = this.engine;
26195
- this.networkJournal.clear();
26196
- this.backgroundNetworkPersistence.clear();
26539
+ this.networkHistory.clear();
26197
26540
  this.sessionRef = void 0;
26198
26541
  this.pageRef = void 0;
26199
26542
  this.runId = void 0;
@@ -26269,10 +26612,10 @@ function buildEngineNetworkRecordFilters(input) {
26269
26612
  ...input.resourceType === void 0 ? {} : { resourceType: input.resourceType }
26270
26613
  };
26271
26614
  }
26272
- function resolveLiveQueryRequestIds(input, journal) {
26615
+ function resolveLiveQueryRequestIds(input, history) {
26273
26616
  const requestIdCandidates = [];
26274
26617
  if (input.recordId !== void 0) {
26275
- const requestId = journal.getRequestId(input.recordId);
26618
+ const requestId = history.getRequestId(input.recordId);
26276
26619
  if (requestId === void 0) {
26277
26620
  return [];
26278
26621
  }
@@ -26281,25 +26624,25 @@ function resolveLiveQueryRequestIds(input, journal) {
26281
26624
  if (input.requestId !== void 0) {
26282
26625
  requestIdCandidates.push(/* @__PURE__ */ new Set([input.requestId]));
26283
26626
  }
26284
- if (input.actionId !== void 0) {
26285
- requestIdCandidates.push(journal.getRequestIdsForActionId(input.actionId));
26627
+ if (input.capture !== void 0) {
26628
+ requestIdCandidates.push(history.getRequestIdsForCapture(input.capture));
26286
26629
  }
26287
26630
  if (input.tag !== void 0) {
26288
- requestIdCandidates.push(journal.getRequestIdsForTag(input.tag));
26631
+ requestIdCandidates.push(history.getRequestIdsForTag(input.tag));
26289
26632
  }
26290
26633
  if (requestIdCandidates.length === 0) {
26291
26634
  return void 0;
26292
26635
  }
26293
26636
  return intersectRequestIdSets(requestIdCandidates);
26294
26637
  }
26295
- function resolveLiveQueryPageRef(input, currentPageRef, requestIds, journal) {
26638
+ function resolveLiveQueryPageRef(input, currentPageRef, requestIds, history) {
26296
26639
  const requestedPageRef = selectLiveQueryPageRef(input, currentPageRef);
26297
26640
  if (requestedPageRef !== void 0 || requestIds === void 0) {
26298
26641
  return requestedPageRef;
26299
26642
  }
26300
26643
  const pageRefs = /* @__PURE__ */ new Set();
26301
26644
  for (const requestId of requestIds) {
26302
- const pageRef = journal.getPageRefForRequestId(requestId);
26645
+ const pageRef = history.getPageRefForRequestId(requestId);
26303
26646
  if (pageRef === void 0) {
26304
26647
  continue;
26305
26648
  }
@@ -26329,7 +26672,7 @@ function filterNetworkQueryRecords(records, input) {
26329
26672
  if (input.requestId !== void 0 && record.record.requestId !== input.requestId) {
26330
26673
  return false;
26331
26674
  }
26332
- if (input.actionId !== void 0 && record.actionId !== input.actionId) {
26675
+ if (input.capture !== void 0 && record.capture !== input.capture) {
26333
26676
  return false;
26334
26677
  }
26335
26678
  if (input.tag !== void 0 && !(record.tags ?? []).includes(input.tag)) {
@@ -26341,10 +26684,10 @@ function filterNetworkQueryRecords(records, input) {
26341
26684
  return true;
26342
26685
  });
26343
26686
  }
26344
- function sortLiveNetworkRecords(records, journal) {
26687
+ function sortLiveNetworkRecords(records, history) {
26345
26688
  return [...records].sort((left, right) => {
26346
- const leftObservedAt = journal.getObservedAt(left.recordId) ?? 0;
26347
- const rightObservedAt = journal.getObservedAt(right.recordId) ?? 0;
26689
+ const leftObservedAt = history.getObservedAt(left.recordId) ?? 0;
26690
+ const rightObservedAt = history.getObservedAt(right.recordId) ?? 0;
26348
26691
  if (leftObservedAt !== rightObservedAt) {
26349
26692
  return rightObservedAt - leftObservedAt;
26350
26693
  }
@@ -26551,7 +26894,7 @@ function buildMinimizedRequestPlan(input) {
26551
26894
  provenance: {
26552
26895
  source: "network-minimize",
26553
26896
  sourceId: input.record.recordId,
26554
- ...input.record.source === "saved" && input.record.savedAt !== void 0 ? { capturedAt: input.record.savedAt } : {}
26897
+ ...input.record.savedAt === void 0 ? {} : { capturedAt: input.record.savedAt }
26555
26898
  },
26556
26899
  payload: normalizeRequestPlanPayload({
26557
26900
  transport: {
@@ -26770,12 +27113,12 @@ function originFromUrl(url) {
26770
27113
  return void 0;
26771
27114
  }
26772
27115
  }
26773
- function filterReverseObservationWindow(records, journal, captureWindowMs) {
27116
+ function filterReverseObservationWindow(records, history, captureWindowMs) {
26774
27117
  if (captureWindowMs === void 0) {
26775
27118
  return records;
26776
27119
  }
26777
27120
  const observedAfter = Date.now() - captureWindowMs;
26778
- return records.filter((record) => (journal.getObservedAt(record.recordId) ?? 0) >= observedAfter);
27121
+ return records.filter((record) => (history.getObservedAt(record.recordId) ?? 0) >= observedAfter);
26779
27122
  }
26780
27123
  function isReverseRelevantNetworkRecord(record) {
26781
27124
  return record.record.resourceType === "document" || record.record.resourceType === "fetch" || record.record.resourceType === "xhr" || record.record.resourceType === "websocket" || record.record.resourceType === "event-stream";
@@ -28739,9 +29082,40 @@ function toOpensteerResolvedTarget2(target) {
28739
29082
  function normalizeOpensteerError(error) {
28740
29083
  return normalizeThrownOpensteerError(error, "Unknown Opensteer runtime failure");
28741
29084
  }
29085
+ function buildMutationCaptureTraceData(diagnostics) {
29086
+ if (diagnostics?.finalizeError === void 0) {
29087
+ return {};
29088
+ }
29089
+ return {
29090
+ networkCapture: {
29091
+ finalizeError: diagnostics.finalizeError
29092
+ }
29093
+ };
29094
+ }
28742
29095
  function isIgnorableRuntimeBindingError(error) {
28743
29096
  return isBrowserCoreError(error) && (error.code === "not-found" || error.code === "page-closed" || error.code === "session-closed");
28744
29097
  }
29098
+ async function withDetachedTimeoutSignal(timeoutMs, operation) {
29099
+ const controller = new AbortController();
29100
+ const timeoutError = new OpensteerProtocolError(
29101
+ "timeout",
29102
+ `mutation capture finalization exceeded ${String(timeoutMs)}ms timeout`,
29103
+ {
29104
+ details: {
29105
+ policy: "mutation-capture-finalize",
29106
+ budgetMs: timeoutMs
29107
+ }
29108
+ }
29109
+ );
29110
+ const timer = setTimeout(() => {
29111
+ controller.abort(timeoutError);
29112
+ }, timeoutMs);
29113
+ try {
29114
+ return await operation(controller.signal);
29115
+ } finally {
29116
+ clearTimeout(timer);
29117
+ }
29118
+ }
28745
29119
  function screenshotMediaType(format2) {
28746
29120
  switch (format2) {
28747
29121
  case "png":