opensteer 0.8.6 → 0.8.8

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/cli/bin.cjs CHANGED
@@ -51,7 +51,7 @@ var WebSocket2__default = /*#__PURE__*/_interopDefault(WebSocket2);
51
51
 
52
52
  // package.json
53
53
  var package_default = {
54
- version: "0.8.6"};
54
+ version: "0.8.8"};
55
55
  util.promisify(child_process.execFile);
56
56
  Math.floor(Date.now() - process.uptime() * 1e3);
57
57
  ({ ...process.env});
@@ -2848,14 +2848,10 @@ var networkRecordSchema = objectSchema(
2848
2848
  ]
2849
2849
  }
2850
2850
  );
2851
- var networkQuerySourceSchema = enumSchema(["live", "saved"], {
2852
- title: "NetworkQuerySource"
2853
- });
2854
2851
  var networkQueryRecordSchema = objectSchema(
2855
2852
  {
2856
2853
  recordId: stringSchema({ minLength: 1 }),
2857
- source: networkQuerySourceSchema,
2858
- actionId: stringSchema({ minLength: 1 }),
2854
+ capture: stringSchema({ minLength: 1 }),
2859
2855
  tags: arraySchema(stringSchema({ minLength: 1 }), {
2860
2856
  uniqueItems: true
2861
2857
  }),
@@ -2864,7 +2860,7 @@ var networkQueryRecordSchema = objectSchema(
2864
2860
  },
2865
2861
  {
2866
2862
  title: "NetworkQueryRecord",
2867
- required: ["recordId", "source", "record"]
2863
+ required: ["recordId", "record"]
2868
2864
  }
2869
2865
  );
2870
2866
  arraySchema(headerEntrySchema, {
@@ -3335,8 +3331,7 @@ var opensteerRecipeStepSchema = oneOfSchema(
3335
3331
  objectSchema(
3336
3332
  {
3337
3333
  kind: enumSchema(["goto"]),
3338
- url: stringSchema({ minLength: 1 }),
3339
- networkTag: stringSchema({ minLength: 1 })
3334
+ url: stringSchema({ minLength: 1 })
3340
3335
  },
3341
3336
  {
3342
3337
  title: "OpensteerAuthRecipeGotoStep",
@@ -3345,8 +3340,7 @@ var opensteerRecipeStepSchema = oneOfSchema(
3345
3340
  ),
3346
3341
  objectSchema(
3347
3342
  {
3348
- kind: enumSchema(["reload"]),
3349
- networkTag: stringSchema({ minLength: 1 })
3343
+ kind: enumSchema(["reload"])
3350
3344
  },
3351
3345
  {
3352
3346
  title: "OpensteerAuthRecipeReloadStep",
@@ -3560,13 +3554,10 @@ var opensteerRecipeRecordSchema = objectSchema(
3560
3554
  var opensteerAuthRecipeRecordSchema = opensteerRecipeRecordSchema;
3561
3555
  var opensteerNetworkQueryInputSchema = objectSchema(
3562
3556
  {
3563
- source: enumSchema(["live", "saved"], {
3564
- title: "OpensteerNetworkQuerySource"
3565
- }),
3566
3557
  pageRef: pageRefSchema,
3567
3558
  recordId: stringSchema({ minLength: 1 }),
3568
3559
  requestId: stringSchema({ minLength: 1 }),
3569
- actionId: stringSchema({ minLength: 1 }),
3560
+ capture: stringSchema({ minLength: 1 }),
3570
3561
  tag: stringSchema({ minLength: 1 }),
3571
3562
  url: stringSchema({ minLength: 1 }),
3572
3563
  hostname: stringSchema({ minLength: 1 }),
@@ -3590,12 +3581,12 @@ var opensteerNetworkQueryOutputSchema = objectSchema(
3590
3581
  required: ["records"]
3591
3582
  }
3592
3583
  );
3593
- var opensteerNetworkSaveInputSchema = objectSchema(
3584
+ var opensteerNetworkTagInputSchema = objectSchema(
3594
3585
  {
3595
3586
  pageRef: pageRefSchema,
3596
3587
  recordId: stringSchema({ minLength: 1 }),
3597
3588
  requestId: stringSchema({ minLength: 1 }),
3598
- actionId: stringSchema({ minLength: 1 }),
3589
+ capture: stringSchema({ minLength: 1 }),
3599
3590
  tag: stringSchema({ minLength: 1 }),
3600
3591
  url: stringSchema({ minLength: 1 }),
3601
3592
  hostname: stringSchema({ minLength: 1 }),
@@ -3605,21 +3596,22 @@ var opensteerNetworkSaveInputSchema = objectSchema(
3605
3596
  resourceType: networkResourceTypeSchema
3606
3597
  },
3607
3598
  {
3608
- title: "OpensteerNetworkSaveInput",
3599
+ title: "OpensteerNetworkTagInput",
3609
3600
  required: ["tag"]
3610
3601
  }
3611
3602
  );
3612
- var opensteerNetworkSaveOutputSchema = objectSchema(
3603
+ var opensteerNetworkTagOutputSchema = objectSchema(
3613
3604
  {
3614
- savedCount: integerSchema({ minimum: 0 })
3605
+ taggedCount: integerSchema({ minimum: 0 })
3615
3606
  },
3616
3607
  {
3617
- title: "OpensteerNetworkSaveOutput",
3618
- required: ["savedCount"]
3608
+ title: "OpensteerNetworkTagOutput",
3609
+ required: ["taggedCount"]
3619
3610
  }
3620
3611
  );
3621
3612
  var opensteerNetworkClearInputSchema = objectSchema(
3622
3613
  {
3614
+ capture: stringSchema({ minLength: 1 }),
3623
3615
  tag: stringSchema({ minLength: 1 })
3624
3616
  },
3625
3617
  {
@@ -7090,7 +7082,7 @@ var opensteerSemanticOperationNames = [
7090
7082
  "dom.scroll",
7091
7083
  "dom.extract",
7092
7084
  "network.query",
7093
- "network.save",
7085
+ "network.tag",
7094
7086
  "network.clear",
7095
7087
  "network.minimize",
7096
7088
  "network.diff",
@@ -7151,7 +7143,7 @@ var opensteerPackageRunnableSemanticOperationNames = /* @__PURE__ */ new Set([
7151
7143
  "dom.scroll",
7152
7144
  "dom.extract",
7153
7145
  "network.query",
7154
- "network.save",
7146
+ "network.tag",
7155
7147
  "network.clear",
7156
7148
  "network.minimize",
7157
7149
  "network.diff",
@@ -7471,7 +7463,7 @@ var opensteerPageCloseOutputSchema = objectSchema(
7471
7463
  var opensteerPageGotoInputSchema = objectSchema(
7472
7464
  {
7473
7465
  url: stringSchema(),
7474
- networkTag: stringSchema({ minLength: 1 })
7466
+ captureNetwork: stringSchema({ minLength: 1 })
7475
7467
  },
7476
7468
  {
7477
7469
  title: "OpensteerPageGotoInput",
@@ -7622,7 +7614,7 @@ var opensteerDomClickInputSchema = objectSchema(
7622
7614
  {
7623
7615
  target: opensteerTargetInputSchema,
7624
7616
  persistAsDescription: stringSchema(),
7625
- networkTag: stringSchema({ minLength: 1 })
7617
+ captureNetwork: stringSchema({ minLength: 1 })
7626
7618
  },
7627
7619
  {
7628
7620
  title: "OpensteerDomClickInput",
@@ -7633,7 +7625,7 @@ var opensteerDomHoverInputSchema = objectSchema(
7633
7625
  {
7634
7626
  target: opensteerTargetInputSchema,
7635
7627
  persistAsDescription: stringSchema(),
7636
- networkTag: stringSchema({ minLength: 1 })
7628
+ captureNetwork: stringSchema({ minLength: 1 })
7637
7629
  },
7638
7630
  {
7639
7631
  title: "OpensteerDomHoverInput",
@@ -7646,7 +7638,7 @@ var opensteerDomInputInputSchema = objectSchema(
7646
7638
  text: stringSchema(),
7647
7639
  pressEnter: { type: "boolean" },
7648
7640
  persistAsDescription: stringSchema(),
7649
- networkTag: stringSchema({ minLength: 1 })
7641
+ captureNetwork: stringSchema({ minLength: 1 })
7650
7642
  },
7651
7643
  {
7652
7644
  title: "OpensteerDomInputInput",
@@ -7659,7 +7651,7 @@ var opensteerDomScrollInputSchema = objectSchema(
7659
7651
  direction: enumSchema(["up", "down", "left", "right"]),
7660
7652
  amount: integerSchema({ minimum: 1 }),
7661
7653
  persistAsDescription: stringSchema(),
7662
- networkTag: stringSchema({ minLength: 1 })
7654
+ captureNetwork: stringSchema({ minLength: 1 })
7663
7655
  },
7664
7656
  {
7665
7657
  title: "OpensteerDomScrollInput",
@@ -7860,7 +7852,7 @@ var opensteerComputerExecuteInputSchema = objectSchema(
7860
7852
  {
7861
7853
  action: opensteerComputerActionSchema,
7862
7854
  screenshot: opensteerComputerScreenshotOptionsSchema,
7863
- networkTag: stringSchema({ minLength: 1 })
7855
+ captureNetwork: stringSchema({ minLength: 1 })
7864
7856
  },
7865
7857
  {
7866
7858
  title: "OpensteerComputerExecuteInput",
@@ -8061,18 +8053,17 @@ var opensteerSemanticOperationSpecificationsBase = [
8061
8053
  }),
8062
8054
  defineSemanticOperationSpec({
8063
8055
  name: "network.query",
8064
- description: "Query live or saved network records for reverse engineering workflows.",
8056
+ description: "Query persisted network records for reverse engineering workflows.",
8065
8057
  inputSchema: opensteerNetworkQueryInputSchema,
8066
8058
  outputSchema: opensteerNetworkQueryOutputSchema,
8067
- requiredCapabilities: [],
8068
- resolveRequiredCapabilities: (input) => input.source === "saved" ? [] : input.includeBodies === true ? ["inspect.network", "inspect.networkBodies"] : ["inspect.network"]
8059
+ requiredCapabilities: []
8069
8060
  }),
8070
8061
  defineSemanticOperationSpec({
8071
- name: "network.save",
8072
- description: "Persist filtered live network records into the saved network registry under a tag.",
8073
- inputSchema: opensteerNetworkSaveInputSchema,
8074
- outputSchema: opensteerNetworkSaveOutputSchema,
8075
- requiredCapabilities: ["inspect.network"]
8062
+ name: "network.tag",
8063
+ description: "Apply a tag to persisted network records matching the provided filters.",
8064
+ inputSchema: opensteerNetworkTagInputSchema,
8065
+ outputSchema: opensteerNetworkTagOutputSchema,
8066
+ requiredCapabilities: []
8076
8067
  }),
8077
8068
  defineSemanticOperationSpec({
8078
8069
  name: "network.clear",
@@ -8964,7 +8955,7 @@ var SqliteSavedNetworkStore = class {
8964
8955
  async initialize() {
8965
8956
  await this.ensureDatabaseDirectory();
8966
8957
  }
8967
- async save(records, tag) {
8958
+ async save(records, options) {
8968
8959
  const database = await this.requireDatabase();
8969
8960
  const readExisting = database.prepare(`
8970
8961
  SELECT record_id
@@ -8973,123 +8964,7 @@ var SqliteSavedNetworkStore = class {
8973
8964
  AND page_ref_key = @page_ref_key
8974
8965
  AND request_id = @request_id
8975
8966
  `);
8976
- const upsertRecord = database.prepare(`
8977
- INSERT INTO saved_network_records (
8978
- record_id,
8979
- request_id,
8980
- session_ref,
8981
- page_ref,
8982
- page_ref_key,
8983
- frame_ref,
8984
- document_ref,
8985
- action_id,
8986
- method,
8987
- method_lc,
8988
- url,
8989
- url_lc,
8990
- hostname,
8991
- hostname_lc,
8992
- path,
8993
- path_lc,
8994
- status,
8995
- status_text,
8996
- resource_type,
8997
- navigation_request,
8998
- request_headers_json,
8999
- response_headers_json,
9000
- request_body_json,
9001
- response_body_json,
9002
- initiator_json,
9003
- timing_json,
9004
- transfer_json,
9005
- source_json,
9006
- capture_state,
9007
- request_body_state,
9008
- response_body_state,
9009
- request_body_skip_reason,
9010
- response_body_skip_reason,
9011
- request_body_error,
9012
- response_body_error,
9013
- redirect_from_request_id,
9014
- redirect_to_request_id,
9015
- saved_at
9016
- ) VALUES (
9017
- @record_id,
9018
- @request_id,
9019
- @session_ref,
9020
- @page_ref,
9021
- @page_ref_key,
9022
- @frame_ref,
9023
- @document_ref,
9024
- @action_id,
9025
- @method,
9026
- @method_lc,
9027
- @url,
9028
- @url_lc,
9029
- @hostname,
9030
- @hostname_lc,
9031
- @path,
9032
- @path_lc,
9033
- @status,
9034
- @status_text,
9035
- @resource_type,
9036
- @navigation_request,
9037
- @request_headers_json,
9038
- @response_headers_json,
9039
- @request_body_json,
9040
- @response_body_json,
9041
- @initiator_json,
9042
- @timing_json,
9043
- @transfer_json,
9044
- @source_json,
9045
- @capture_state,
9046
- @request_body_state,
9047
- @response_body_state,
9048
- @request_body_skip_reason,
9049
- @response_body_skip_reason,
9050
- @request_body_error,
9051
- @response_body_error,
9052
- @redirect_from_request_id,
9053
- @redirect_to_request_id,
9054
- @saved_at
9055
- )
9056
- ON CONFLICT(record_id) DO UPDATE SET
9057
- page_ref = excluded.page_ref,
9058
- page_ref_key = excluded.page_ref_key,
9059
- frame_ref = excluded.frame_ref,
9060
- document_ref = excluded.document_ref,
9061
- action_id = excluded.action_id,
9062
- method = excluded.method,
9063
- method_lc = excluded.method_lc,
9064
- url = excluded.url,
9065
- url_lc = excluded.url_lc,
9066
- hostname = excluded.hostname,
9067
- hostname_lc = excluded.hostname_lc,
9068
- path = excluded.path,
9069
- path_lc = excluded.path_lc,
9070
- status = excluded.status,
9071
- status_text = excluded.status_text,
9072
- resource_type = excluded.resource_type,
9073
- navigation_request = excluded.navigation_request,
9074
- request_headers_json = excluded.request_headers_json,
9075
- response_headers_json = excluded.response_headers_json,
9076
- request_body_json = excluded.request_body_json,
9077
- response_body_json = excluded.response_body_json,
9078
- initiator_json = excluded.initiator_json,
9079
- timing_json = excluded.timing_json,
9080
- transfer_json = excluded.transfer_json,
9081
- source_json = excluded.source_json,
9082
- capture_state = excluded.capture_state,
9083
- request_body_state = excluded.request_body_state,
9084
- response_body_state = excluded.response_body_state,
9085
- request_body_skip_reason = excluded.request_body_skip_reason,
9086
- response_body_skip_reason = excluded.response_body_skip_reason,
9087
- request_body_error = excluded.request_body_error,
9088
- response_body_error = excluded.response_body_error,
9089
- redirect_from_request_id = excluded.redirect_from_request_id,
9090
- redirect_to_request_id = excluded.redirect_to_request_id,
9091
- saved_at = excluded.saved_at
9092
- `);
8967
+ const upsertRecord = database.prepare(buildSavedNetworkUpsertSql(options.bodyWriteMode));
9093
8968
  const insertTag = database.prepare(`
9094
8969
  INSERT OR IGNORE INTO saved_network_tags (record_id, tag)
9095
8970
  VALUES (@record_id, @tag)
@@ -9113,7 +8988,7 @@ var SqliteSavedNetworkStore = class {
9113
8988
  page_ref_key: pageRefKey,
9114
8989
  frame_ref: entry.record.frameRef ?? null,
9115
8990
  document_ref: entry.record.documentRef ?? null,
9116
- action_id: entry.actionId ?? null,
8991
+ capture: entry.capture ?? null,
9117
8992
  method: entry.record.method,
9118
8993
  method_lc: entry.record.method.toLowerCase(),
9119
8994
  url: entry.record.url,
@@ -9145,10 +9020,14 @@ var SqliteSavedNetworkStore = class {
9145
9020
  redirect_to_request_id: entry.record.redirectToRequestId ?? null,
9146
9021
  saved_at: entry.savedAt ?? Date.now()
9147
9022
  });
9148
- if (tag !== void 0) {
9023
+ const tags = new Set(entry.tags ?? []);
9024
+ if (options.tag !== void 0) {
9025
+ tags.add(options.tag);
9026
+ }
9027
+ for (const currentTag of tags) {
9149
9028
  const result = insertTag.run({
9150
9029
  record_id: recordId,
9151
- tag
9030
+ tag: currentTag
9152
9031
  });
9153
9032
  savedCount += result.changes ?? 0;
9154
9033
  }
@@ -9156,6 +9035,39 @@ var SqliteSavedNetworkStore = class {
9156
9035
  return savedCount;
9157
9036
  });
9158
9037
  }
9038
+ async tagByFilter(filter, tag) {
9039
+ const database = await this.requireDatabase();
9040
+ const { whereSql, parameters } = buildSavedNetworkWhere(filter);
9041
+ const selectRecords = database.prepare(
9042
+ `
9043
+ SELECT r.record_id
9044
+ FROM saved_network_records r
9045
+ ${whereSql}
9046
+ `
9047
+ );
9048
+ const insertTag = database.prepare(`
9049
+ INSERT OR IGNORE INTO saved_network_tags (record_id, tag)
9050
+ VALUES (@record_id, @tag)
9051
+ `);
9052
+ return withSqliteTransaction(database, () => {
9053
+ let taggedCount = 0;
9054
+ const rows = selectRecords.all(
9055
+ ...parameters
9056
+ );
9057
+ for (const row of rows) {
9058
+ const recordId = row.record_id;
9059
+ if (typeof recordId !== "string") {
9060
+ continue;
9061
+ }
9062
+ const result = insertTag.run({
9063
+ record_id: recordId,
9064
+ tag
9065
+ });
9066
+ taggedCount += result.changes ?? 0;
9067
+ }
9068
+ return taggedCount;
9069
+ });
9070
+ }
9159
9071
  async query(input = {}) {
9160
9072
  const database = await this.requireDatabase();
9161
9073
  const limit = Math.max(1, Math.min(input.limit ?? 50, 200));
@@ -9189,39 +9101,31 @@ var SqliteSavedNetworkStore = class {
9189
9101
  }
9190
9102
  async clear(input = {}) {
9191
9103
  const database = await this.requireDatabase();
9192
- const countAll = database.prepare(`
9193
- SELECT COUNT(*) AS cleared
9194
- FROM saved_network_records
9195
- `);
9196
- const countByTag = database.prepare(`
9197
- SELECT COUNT(DISTINCT record_id) AS cleared
9198
- FROM saved_network_tags
9199
- WHERE tag = @tag
9200
- `);
9201
- const deleteAllTags = database.prepare(`DELETE FROM saved_network_tags`);
9104
+ const countAll = database.prepare(`SELECT COUNT(*) AS cleared FROM saved_network_records`);
9202
9105
  const deleteAllRecords = database.prepare(`DELETE FROM saved_network_records`);
9203
- const deleteTag = database.prepare(`
9204
- DELETE FROM saved_network_tags
9205
- WHERE tag = @tag
9106
+ const { whereSql, parameters } = buildSavedNetworkWhere(input);
9107
+ const countFiltered = database.prepare(`
9108
+ SELECT COUNT(*) AS cleared
9109
+ FROM saved_network_records r
9110
+ ${whereSql}
9206
9111
  `);
9207
- const deleteOrphans = database.prepare(`
9112
+ const deleteFiltered = database.prepare(`
9208
9113
  DELETE FROM saved_network_records
9209
- WHERE NOT EXISTS (
9210
- SELECT 1
9211
- FROM saved_network_tags t
9212
- WHERE t.record_id = saved_network_records.record_id
9114
+ WHERE record_id IN (
9115
+ SELECT r.record_id
9116
+ FROM saved_network_records r
9117
+ ${whereSql}
9213
9118
  )
9214
9119
  `);
9215
9120
  return withSqliteTransaction(database, () => {
9216
- const tag = input.tag;
9217
- const cleared = tag === void 0 ? countAll.get().cleared : countByTag.get({ tag }).cleared;
9218
- if (tag === void 0) {
9219
- deleteAllTags.run();
9121
+ if (input.capture === void 0 && input.tag === void 0) {
9122
+ const cleared2 = countAll.get().cleared;
9220
9123
  deleteAllRecords.run();
9221
- return cleared;
9124
+ return cleared2;
9222
9125
  }
9223
- deleteTag.run({ tag });
9224
- deleteOrphans.run();
9126
+ const args = parameters;
9127
+ const cleared = countFiltered.get(...args).cleared;
9128
+ deleteFiltered.run(...args);
9225
9129
  return cleared;
9226
9130
  });
9227
9131
  }
@@ -9276,7 +9180,7 @@ var SqliteSavedNetworkStore = class {
9276
9180
  page_ref_key TEXT NOT NULL,
9277
9181
  frame_ref TEXT,
9278
9182
  document_ref TEXT,
9279
- action_id TEXT,
9183
+ capture TEXT,
9280
9184
  method TEXT NOT NULL,
9281
9185
  method_lc TEXT NOT NULL,
9282
9186
  url TEXT NOT NULL,
@@ -9315,6 +9219,9 @@ var SqliteSavedNetworkStore = class {
9315
9219
  CREATE INDEX IF NOT EXISTS saved_network_records_saved_at
9316
9220
  ON saved_network_records (saved_at DESC);
9317
9221
 
9222
+ CREATE INDEX IF NOT EXISTS saved_network_records_capture
9223
+ ON saved_network_records (capture);
9224
+
9318
9225
  CREATE TABLE IF NOT EXISTS saved_network_tags (
9319
9226
  record_id TEXT NOT NULL REFERENCES saved_network_records(record_id) ON DELETE CASCADE,
9320
9227
  tag TEXT NOT NULL,
@@ -9330,6 +9237,7 @@ var SqliteSavedNetworkStore = class {
9330
9237
  "capture_state",
9331
9238
  "TEXT NOT NULL DEFAULT 'complete'"
9332
9239
  );
9240
+ this.ensureColumn(database, "saved_network_records", "capture", "TEXT");
9333
9241
  this.ensureColumn(
9334
9242
  database,
9335
9243
  "saved_network_records",
@@ -9358,6 +9266,10 @@ var SqliteSavedNetworkStore = class {
9358
9266
  function buildSavedNetworkWhere(input) {
9359
9267
  const clauses = [];
9360
9268
  const parameters = [];
9269
+ if (input.pageRef !== void 0) {
9270
+ clauses.push("r.page_ref_key = ?");
9271
+ parameters.push(input.pageRef);
9272
+ }
9361
9273
  if (input.recordId !== void 0) {
9362
9274
  clauses.push("r.record_id = ?");
9363
9275
  parameters.push(input.recordId);
@@ -9366,9 +9278,9 @@ function buildSavedNetworkWhere(input) {
9366
9278
  clauses.push("r.request_id = ?");
9367
9279
  parameters.push(input.requestId);
9368
9280
  }
9369
- if (input.actionId !== void 0) {
9370
- clauses.push("r.action_id = ?");
9371
- parameters.push(input.actionId);
9281
+ if (input.capture !== void 0) {
9282
+ clauses.push("r.capture = ?");
9283
+ parameters.push(input.capture);
9372
9284
  }
9373
9285
  if (input.tag !== void 0) {
9374
9286
  clauses.push(`
@@ -9410,6 +9322,127 @@ function buildSavedNetworkWhere(input) {
9410
9322
  parameters
9411
9323
  };
9412
9324
  }
9325
+ function buildSavedNetworkUpsertSql(bodyWriteMode) {
9326
+ const bodyUpdateSql = bodyWriteMode === "authoritative" ? `
9327
+ request_body_json = excluded.request_body_json,
9328
+ response_body_json = excluded.response_body_json,
9329
+ request_body_state = excluded.request_body_state,
9330
+ response_body_state = excluded.response_body_state,
9331
+ request_body_skip_reason = excluded.request_body_skip_reason,
9332
+ response_body_skip_reason = excluded.response_body_skip_reason,
9333
+ request_body_error = excluded.request_body_error,
9334
+ response_body_error = excluded.response_body_error,
9335
+ ` : "";
9336
+ return `
9337
+ INSERT INTO saved_network_records (
9338
+ record_id,
9339
+ request_id,
9340
+ session_ref,
9341
+ page_ref,
9342
+ page_ref_key,
9343
+ frame_ref,
9344
+ document_ref,
9345
+ capture,
9346
+ method,
9347
+ method_lc,
9348
+ url,
9349
+ url_lc,
9350
+ hostname,
9351
+ hostname_lc,
9352
+ path,
9353
+ path_lc,
9354
+ status,
9355
+ status_text,
9356
+ resource_type,
9357
+ navigation_request,
9358
+ request_headers_json,
9359
+ response_headers_json,
9360
+ request_body_json,
9361
+ response_body_json,
9362
+ initiator_json,
9363
+ timing_json,
9364
+ transfer_json,
9365
+ source_json,
9366
+ capture_state,
9367
+ request_body_state,
9368
+ response_body_state,
9369
+ request_body_skip_reason,
9370
+ response_body_skip_reason,
9371
+ request_body_error,
9372
+ response_body_error,
9373
+ redirect_from_request_id,
9374
+ redirect_to_request_id,
9375
+ saved_at
9376
+ ) VALUES (
9377
+ @record_id,
9378
+ @request_id,
9379
+ @session_ref,
9380
+ @page_ref,
9381
+ @page_ref_key,
9382
+ @frame_ref,
9383
+ @document_ref,
9384
+ @capture,
9385
+ @method,
9386
+ @method_lc,
9387
+ @url,
9388
+ @url_lc,
9389
+ @hostname,
9390
+ @hostname_lc,
9391
+ @path,
9392
+ @path_lc,
9393
+ @status,
9394
+ @status_text,
9395
+ @resource_type,
9396
+ @navigation_request,
9397
+ @request_headers_json,
9398
+ @response_headers_json,
9399
+ @request_body_json,
9400
+ @response_body_json,
9401
+ @initiator_json,
9402
+ @timing_json,
9403
+ @transfer_json,
9404
+ @source_json,
9405
+ @capture_state,
9406
+ @request_body_state,
9407
+ @response_body_state,
9408
+ @request_body_skip_reason,
9409
+ @response_body_skip_reason,
9410
+ @request_body_error,
9411
+ @response_body_error,
9412
+ @redirect_from_request_id,
9413
+ @redirect_to_request_id,
9414
+ @saved_at
9415
+ )
9416
+ ON CONFLICT(record_id) DO UPDATE SET
9417
+ page_ref = excluded.page_ref,
9418
+ page_ref_key = excluded.page_ref_key,
9419
+ frame_ref = excluded.frame_ref,
9420
+ document_ref = excluded.document_ref,
9421
+ capture = excluded.capture,
9422
+ method = excluded.method,
9423
+ method_lc = excluded.method_lc,
9424
+ url = excluded.url,
9425
+ url_lc = excluded.url_lc,
9426
+ hostname = excluded.hostname,
9427
+ hostname_lc = excluded.hostname_lc,
9428
+ path = excluded.path,
9429
+ path_lc = excluded.path_lc,
9430
+ status = excluded.status,
9431
+ status_text = excluded.status_text,
9432
+ resource_type = excluded.resource_type,
9433
+ navigation_request = excluded.navigation_request,
9434
+ request_headers_json = excluded.request_headers_json,
9435
+ response_headers_json = excluded.response_headers_json,
9436
+ ${bodyUpdateSql} initiator_json = excluded.initiator_json,
9437
+ timing_json = excluded.timing_json,
9438
+ transfer_json = excluded.transfer_json,
9439
+ source_json = excluded.source_json,
9440
+ capture_state = excluded.capture_state,
9441
+ redirect_from_request_id = excluded.redirect_from_request_id,
9442
+ redirect_to_request_id = excluded.redirect_to_request_id,
9443
+ saved_at = MIN(saved_network_records.saved_at, excluded.saved_at)
9444
+ `;
9445
+ }
9413
9446
  function inflateSavedNetworkRow(row, includeBodies) {
9414
9447
  const requestBody = includeBodies && row.request_body_json !== null ? JSON.parse(row.request_body_json) : void 0;
9415
9448
  const responseBody = includeBodies && row.response_body_json !== null ? JSON.parse(row.response_body_json) : void 0;
@@ -9480,8 +9513,7 @@ function inflateSavedNetworkRow(row, includeBodies) {
9480
9513
  }
9481
9514
  return {
9482
9515
  recordId: row.record_id,
9483
- source: "saved",
9484
- ...row.action_id === null ? {} : { actionId: row.action_id },
9516
+ ...row.capture === null ? {} : { capture: row.capture },
9485
9517
  ...row.tags === null || row.tags.length === 0 ? {} : { tags: row.tags.split(TAG_DELIMITER).filter((tag) => tag.length > 0) },
9486
9518
  savedAt: row.saved_at,
9487
9519
  record
@@ -10785,8 +10817,8 @@ async function dispatchSemanticOperation(runtime, operation, input, options = {}
10785
10817
  input,
10786
10818
  options
10787
10819
  );
10788
- case "network.save":
10789
- return runtime.saveNetwork(
10820
+ case "network.tag":
10821
+ return runtime.tagNetwork(
10790
10822
  input,
10791
10823
  options
10792
10824
  );
@@ -11861,6 +11893,20 @@ var OpensteerCloudClient = class {
11861
11893
  });
11862
11894
  return await response.json();
11863
11895
  }
11896
+ async importReverseCases(entries) {
11897
+ const response = await this.request("/registry/reverse-cases/import", {
11898
+ method: "POST",
11899
+ body: { entries }
11900
+ });
11901
+ return await response.json();
11902
+ }
11903
+ async importReversePackages(entries) {
11904
+ const response = await this.request("/registry/reverse-packages/import", {
11905
+ method: "POST",
11906
+ body: { entries }
11907
+ });
11908
+ return await response.json();
11909
+ }
11864
11910
  buildAuthorizationHeader() {
11865
11911
  return `Bearer ${this.config.apiKey}`;
11866
11912
  }
@@ -11956,11 +12002,54 @@ function resolveCloudConfig(input = {}) {
11956
12002
 
11957
12003
  // ../runtime-core/package.json
11958
12004
  var package_default2 = {
11959
- version: "0.1.1"};
12005
+ version: "0.1.2"};
11960
12006
 
11961
12007
  // ../runtime-core/src/version.ts
11962
12008
  var OPENSTEER_RUNTIME_CORE_VERSION = package_default2.version;
11963
12009
 
12010
+ // ../runtime-core/src/action-boundary.ts
12011
+ var actionBoundaryDiagnosticsBySignal = /* @__PURE__ */ new WeakMap();
12012
+ async function captureActionBoundarySnapshot(engine, pageRef) {
12013
+ const frames = await engine.listFrames({ pageRef });
12014
+ const mainFrame = frames.find((frame) => frame.isMainFrame);
12015
+ if (!mainFrame) {
12016
+ throw new Error(`page ${pageRef} does not expose a main frame`);
12017
+ }
12018
+ return {
12019
+ pageRef,
12020
+ documentRef: mainFrame.documentRef
12021
+ };
12022
+ }
12023
+ function createActionBoundaryDiagnostics(input) {
12024
+ return {
12025
+ trigger: input.boundary.trigger,
12026
+ crossDocument: input.boundary.crossDocument,
12027
+ bootstrapSettled: input.boundary.bootstrapSettled,
12028
+ visualSettled: input.visualSettled,
12029
+ ...input.boundary.timedOutPhase !== void 0 ? { timedOutPhase: input.boundary.timedOutPhase } : !input.visualSettled ? { timedOutPhase: "visual" } : {}
12030
+ };
12031
+ }
12032
+ function recordActionBoundaryDiagnostics(signal, diagnostics) {
12033
+ actionBoundaryDiagnosticsBySignal.set(signal, diagnostics);
12034
+ }
12035
+ function takeActionBoundaryDiagnostics(signal) {
12036
+ const diagnostics = actionBoundaryDiagnosticsBySignal.get(signal);
12037
+ actionBoundaryDiagnosticsBySignal.delete(signal);
12038
+ return diagnostics;
12039
+ }
12040
+ function isSoftSettleTimeoutError(error, signal) {
12041
+ if (isTimeoutError(error)) {
12042
+ return true;
12043
+ }
12044
+ return signal?.aborted === true && isTimeoutError(signal.reason) && (error === signal.reason || isAbortError(error));
12045
+ }
12046
+ function isAbortError(error) {
12047
+ return error instanceof Error && error.name === "AbortError";
12048
+ }
12049
+ function isTimeoutError(error) {
12050
+ return isOpensteerProtocolError(error) && error.code === "timeout";
12051
+ }
12052
+
11964
12053
  // ../runtime-core/src/internal/errors.ts
11965
12054
  function normalizeThrownOpensteerError(error, fallbackMessage) {
11966
12055
  if (isOpensteerProtocolError(error)) {
@@ -11993,13 +12082,13 @@ var DEFAULT_TIMEOUTS = {
11993
12082
  "page.add-init-script": 1e4,
11994
12083
  "page.snapshot": 15e3,
11995
12084
  "computer.execute": 3e4,
11996
- "dom.click": 1e4,
12085
+ "dom.click": 3e4,
11997
12086
  "dom.hover": 1e4,
11998
- "dom.input": 1e4,
12087
+ "dom.input": 3e4,
11999
12088
  "dom.scroll": 1e4,
12000
12089
  "dom.extract": 15e3,
12001
12090
  "network.query": 15e3,
12002
- "network.save": 15e3,
12091
+ "network.tag": 15e3,
12003
12092
  "network.clear": 1e4,
12004
12093
  "scripts.capture": 15e3,
12005
12094
  "request.raw": 3e4,
@@ -13907,8 +13996,9 @@ var DomActionExecutor = class {
13907
13996
  })
13908
13997
  );
13909
13998
  let finalResolved = resolved;
13999
+ let finalSnapshot;
13910
14000
  if (input.pressEnter) {
13911
- await this.settle(resolved.pageRef, "dom.input", timeout);
14001
+ await this.waitForPressEnterReaction(timeout);
13912
14002
  const enterSession = this.options.createResolutionSession();
13913
14003
  const enterResolved = await timeout.runStep(
13914
14004
  () => this.options.resolveTarget(enterSession, {
@@ -13922,6 +14012,9 @@ var DomActionExecutor = class {
13922
14012
  () => bridge.inspectActionTarget(enterResolved.locator)
13923
14013
  );
13924
14014
  this.assertKeyboardActionable("dom.input", enterResolved, inspectionBeforeEnter);
14015
+ finalSnapshot = await timeout.runStep(
14016
+ () => captureActionBoundarySnapshot(this.options.engine, enterResolved.pageRef)
14017
+ );
13925
14018
  await timeout.runStep(
13926
14019
  () => bridge.pressKey(enterResolved.locator, {
13927
14020
  key: "Enter"
@@ -13929,7 +14022,15 @@ var DomActionExecutor = class {
13929
14022
  );
13930
14023
  finalResolved = enterResolved;
13931
14024
  }
13932
- await this.settle(finalResolved.pageRef, "dom.input", timeout);
14025
+ const settleDiagnostics = await this.settle(
14026
+ finalResolved.pageRef,
14027
+ "dom.input",
14028
+ timeout,
14029
+ finalSnapshot
14030
+ );
14031
+ if (finalSnapshot !== void 0) {
14032
+ recordActionBoundaryDiagnostics(timeout.signal, settleDiagnostics);
14033
+ }
13933
14034
  return finalResolved;
13934
14035
  } catch (error) {
13935
14036
  lastError = error;
@@ -14002,8 +14103,17 @@ var DomActionExecutor = class {
14002
14103
  );
14003
14104
  }
14004
14105
  }
14106
+ const actionBoundarySnapshot = await timeout.runStep(
14107
+ () => captureActionBoundarySnapshot(this.options.engine, pointerTarget.resolved.pageRef)
14108
+ );
14005
14109
  const outcome = await dispatch(pointerTarget, point, timeout);
14006
- await this.settle(pointerTarget.resolved.pageRef, input.operation, timeout);
14110
+ const settleDiagnostics = await this.settle(
14111
+ pointerTarget.resolved.pageRef,
14112
+ input.operation,
14113
+ timeout,
14114
+ actionBoundarySnapshot
14115
+ );
14116
+ recordActionBoundaryDiagnostics(timeout.signal, settleDiagnostics);
14007
14117
  return outcome;
14008
14118
  } catch (error) {
14009
14119
  lastError = error;
@@ -14021,23 +14131,49 @@ var DomActionExecutor = class {
14021
14131
  }
14022
14132
  return runWithPolicyTimeout(this.options.policy.timeout, { operation }, execute);
14023
14133
  }
14024
- async settle(pageRef, operation, timeout) {
14134
+ async settle(pageRef, operation, timeout, snapshot) {
14025
14135
  const bridge = this.requireBridge();
14026
- await timeout.runStep(
14136
+ let visualSettled = true;
14137
+ const boundary = await timeout.runStep(
14027
14138
  () => bridge.finalizeDomAction(pageRef, {
14028
14139
  operation,
14140
+ ...snapshot === void 0 ? {} : { snapshot },
14029
14141
  signal: timeout.signal,
14030
14142
  remainingMs: () => timeout.remainingMs(),
14031
- policySettle: (targetPageRef) => settleWithPolicy(this.options.policy.settle, {
14032
- operation,
14033
- trigger: "dom-action",
14034
- engine: this.options.engine,
14035
- pageRef: targetPageRef,
14036
- signal: timeout.signal,
14037
- remainingMs: timeout.remainingMs()
14038
- })
14143
+ policySettle: async (targetPageRef, trigger) => {
14144
+ try {
14145
+ await settleWithPolicy(this.options.policy.settle, {
14146
+ operation,
14147
+ trigger,
14148
+ engine: this.options.engine,
14149
+ pageRef: targetPageRef,
14150
+ signal: timeout.signal,
14151
+ remainingMs: timeout.remainingMs()
14152
+ });
14153
+ } catch (error) {
14154
+ if (snapshot !== void 0 && isSoftSettleTimeoutError(error, timeout.signal)) {
14155
+ visualSettled = false;
14156
+ return;
14157
+ }
14158
+ throw error;
14159
+ }
14160
+ }
14039
14161
  })
14040
14162
  );
14163
+ return createActionBoundaryDiagnostics({
14164
+ boundary,
14165
+ visualSettled
14166
+ });
14167
+ }
14168
+ async waitForPressEnterReaction(timeout) {
14169
+ const delayMs = this.options.policy.settle.resolveDelayMs({
14170
+ operation: "dom.input",
14171
+ trigger: "dom-action"
14172
+ });
14173
+ if (delayMs <= 0) {
14174
+ return;
14175
+ }
14176
+ await delayWithSignal(delayMs, timeout.signal);
14041
14177
  }
14042
14178
  requireBridge() {
14043
14179
  if (this.bridge !== void 0) {
@@ -15304,23 +15440,47 @@ var DefaultComputerUseRuntime = class {
15304
15440
  const preActionDisplay = createComputerDisplayTransform(preActionNativeViewport);
15305
15441
  const nativeAction = toNativeComputerAction(input.input.action, preActionDisplay);
15306
15442
  const screenshot = normalizeScreenshotOptions(input.input.screenshot);
15443
+ const snapshot = await input.timeout.runStep(
15444
+ () => captureActionBoundarySnapshot(this.options.engine, input.pageRef)
15445
+ );
15446
+ let visualSettled = true;
15307
15447
  const executed = await input.timeout.runStep(
15308
15448
  () => bridge.execute({
15309
15449
  pageRef: input.pageRef,
15450
+ snapshot,
15310
15451
  action: nativeAction,
15311
15452
  screenshot,
15312
15453
  signal: input.timeout.signal,
15313
15454
  remainingMs: () => input.timeout.remainingMs(),
15314
- policySettle: async (pageRef) => settleWithPolicy(this.options.policy.settle, {
15315
- operation: "computer.execute",
15316
- trigger: "dom-action",
15317
- engine: this.options.engine,
15318
- pageRef,
15319
- signal: input.timeout.signal,
15320
- remainingMs: input.timeout.remainingMs()
15321
- })
15455
+ policySettle: async (pageRef, trigger) => {
15456
+ try {
15457
+ await settleWithPolicy(this.options.policy.settle, {
15458
+ operation: "computer.execute",
15459
+ trigger,
15460
+ engine: this.options.engine,
15461
+ pageRef,
15462
+ signal: input.timeout.signal,
15463
+ remainingMs: input.timeout.remainingMs()
15464
+ });
15465
+ } catch (error) {
15466
+ if (pageRef === input.pageRef && isSoftSettleTimeoutError(error, input.timeout.signal)) {
15467
+ visualSettled = false;
15468
+ return;
15469
+ }
15470
+ throw error;
15471
+ }
15472
+ }
15322
15473
  })
15323
15474
  );
15475
+ if (executed.boundary !== void 0 && executed.pageRef === input.pageRef) {
15476
+ recordActionBoundaryDiagnostics(
15477
+ input.timeout.signal,
15478
+ createActionBoundaryDiagnostics({
15479
+ boundary: executed.boundary,
15480
+ visualSettled
15481
+ })
15482
+ );
15483
+ }
15324
15484
  let trace = void 0;
15325
15485
  if (!input.timeout.signal.aborted) {
15326
15486
  try {
@@ -16047,9 +16207,9 @@ function inferRequestPlanFromNetworkRecord(record, input, options = {}) {
16047
16207
  key: input.key,
16048
16208
  version: input.version,
16049
16209
  provenance: {
16050
- source: record.source === "saved" ? "saved-network-record" : "live-network-record",
16210
+ source: record.savedAt === void 0 ? "network-record" : "saved-network-record",
16051
16211
  sourceId: record.recordId,
16052
- ...record.source === "saved" ? record.savedAt === void 0 ? {} : { capturedAt: record.savedAt } : options.observedAt === void 0 ? {} : { capturedAt: options.observedAt }
16212
+ ...record.savedAt === void 0 ? options.observedAt === void 0 ? {} : { capturedAt: options.observedAt } : { capturedAt: record.savedAt }
16053
16213
  },
16054
16214
  payload,
16055
16215
  ...record.tags === void 0 || record.tags.length === 0 ? {} : { tags: record.tags }
@@ -16429,63 +16589,67 @@ function resolveBodyEncoding(charset) {
16429
16589
  return "utf8";
16430
16590
  }
16431
16591
  }
16432
- var NetworkJournal = class {
16592
+ var NetworkHistory = class {
16433
16593
  metadataByRequestId = /* @__PURE__ */ new Map();
16434
16594
  requestIdByRecordId = /* @__PURE__ */ new Map();
16435
- requestIdsByActionId = /* @__PURE__ */ new Map();
16595
+ requestIdsByCapture = /* @__PURE__ */ new Map();
16436
16596
  requestIdsByTag = /* @__PURE__ */ new Map();
16437
- sync(records, options = {}) {
16597
+ tombstonedRequestIds = /* @__PURE__ */ new Set();
16598
+ materialize(records, options = {}) {
16438
16599
  const observedAt = Date.now();
16439
- return records.map((record) => this.materializeLiveRecord(record, observedAt, options));
16440
- }
16441
- materializeLiveRecord(record, observedAt, options = {}) {
16442
- let metadata = this.metadataByRequestId.get(record.requestId);
16443
- if (!metadata) {
16444
- metadata = {
16445
- recordId: `record:${crypto.randomUUID()}`,
16446
- observedAt,
16447
- ...record.pageRef === void 0 ? {} : { pageRef: record.pageRef },
16448
- tags: /* @__PURE__ */ new Set()
16449
- };
16450
- this.metadataByRequestId.set(record.requestId, metadata);
16451
- this.requestIdByRecordId.set(metadata.recordId, record.requestId);
16452
- } else if (metadata.pageRef === void 0 && record.pageRef !== void 0) {
16453
- metadata.pageRef = record.pageRef;
16600
+ const materialized = [];
16601
+ for (const record of records) {
16602
+ const entry = this.materializeRecord(record, observedAt, options);
16603
+ if (entry !== void 0) {
16604
+ materialized.push(entry);
16605
+ }
16454
16606
  }
16455
- return {
16456
- recordId: metadata.recordId,
16457
- source: "live",
16458
- ...metadata.actionId === void 0 ? {} : { actionId: metadata.actionId },
16459
- ...metadata.tags.size === 0 ? {} : { tags: [...metadata.tags].sort() },
16460
- record: toProtocolNetworkRecord(record, {
16461
- redactSecretHeaders: options.redactSecretHeaders ?? true
16462
- })
16463
- };
16607
+ return materialized;
16464
16608
  }
16465
- diffNewRequestIds(records, baselineRequestIds) {
16466
- const observedAt = Date.now();
16467
- const all = records.map(
16468
- (record) => this.materializeLiveRecord(record, observedAt, {
16469
- redactSecretHeaders: true
16470
- })
16471
- );
16472
- const delta = all.filter((entry) => !baselineRequestIds.has(entry.record.requestId));
16473
- return {
16474
- all,
16475
- delta
16476
- };
16609
+ async persist(records, store, options) {
16610
+ const observedAt = options.observedAt ?? Date.now();
16611
+ const metadataToSave = /* @__PURE__ */ new Set();
16612
+ const persisted = [];
16613
+ for (const record of records) {
16614
+ const entry = this.materializeRecord(record, observedAt, {
16615
+ ...options.redactSecretHeaders === void 0 ? {} : { redactSecretHeaders: options.redactSecretHeaders }
16616
+ });
16617
+ if (entry === void 0) {
16618
+ continue;
16619
+ }
16620
+ const requestId = entry.record.requestId;
16621
+ const metadata = this.metadataByRequestId.get(requestId);
16622
+ if (metadata === void 0) {
16623
+ continue;
16624
+ }
16625
+ const savedAt = metadata.savedAt ?? observedAt;
16626
+ metadataToSave.add(metadata);
16627
+ persisted.push({
16628
+ ...entry,
16629
+ savedAt
16630
+ });
16631
+ }
16632
+ if (persisted.length > 0) {
16633
+ await store.save(persisted, {
16634
+ bodyWriteMode: options.bodyWriteMode
16635
+ });
16636
+ for (const metadata of metadataToSave) {
16637
+ metadata.savedAt ??= observedAt;
16638
+ }
16639
+ }
16640
+ return persisted;
16477
16641
  }
16478
- assignActionId(records, actionId) {
16642
+ assignCapture(records, capture) {
16479
16643
  for (const record of records) {
16480
16644
  const metadata = this.metadataByRequestId.get(record.record.requestId);
16481
- if (!metadata || metadata.actionId === actionId) {
16645
+ if (!metadata || metadata.capture === capture) {
16482
16646
  continue;
16483
16647
  }
16484
- if (metadata.actionId !== void 0) {
16485
- this.requestIdsByActionId.get(metadata.actionId)?.delete(record.record.requestId);
16648
+ if (metadata.capture !== void 0) {
16649
+ this.requestIdsByCapture.get(metadata.capture)?.delete(record.record.requestId);
16486
16650
  }
16487
- metadata.actionId = actionId;
16488
- this.addIndexedRequestId(this.requestIdsByActionId, actionId, record.record.requestId);
16651
+ metadata.capture = capture;
16652
+ this.addIndexedRequestId(this.requestIdsByCapture, capture, record.record.requestId);
16489
16653
  }
16490
16654
  }
16491
16655
  addTag(records, tag) {
@@ -16505,8 +16669,8 @@ var NetworkJournal = class {
16505
16669
  getRequestId(recordId) {
16506
16670
  return this.requestIdByRecordId.get(recordId);
16507
16671
  }
16508
- getRequestIdsForActionId(actionId) {
16509
- return new Set(this.requestIdsByActionId.get(actionId) ?? []);
16672
+ getRequestIdsForCapture(capture) {
16673
+ return new Set(this.requestIdsByCapture.get(capture) ?? []);
16510
16674
  }
16511
16675
  getRequestIdsForTag(tag) {
16512
16676
  return new Set(this.requestIdsByTag.get(tag) ?? []);
@@ -16514,11 +16678,59 @@ var NetworkJournal = class {
16514
16678
  getPageRefForRequestId(requestId) {
16515
16679
  return this.metadataByRequestId.get(requestId)?.pageRef;
16516
16680
  }
16681
+ getKnownRequestIds() {
16682
+ return new Set(this.metadataByRequestId.keys());
16683
+ }
16684
+ tombstoneRequestIds(requestIds) {
16685
+ for (const requestId of requestIds) {
16686
+ this.tombstonedRequestIds.add(requestId);
16687
+ const metadata = this.metadataByRequestId.get(requestId);
16688
+ if (!metadata) {
16689
+ continue;
16690
+ }
16691
+ this.metadataByRequestId.delete(requestId);
16692
+ this.requestIdByRecordId.delete(metadata.recordId);
16693
+ if (metadata.capture !== void 0) {
16694
+ this.requestIdsByCapture.get(metadata.capture)?.delete(requestId);
16695
+ }
16696
+ for (const tag of metadata.tags) {
16697
+ this.requestIdsByTag.get(tag)?.delete(requestId);
16698
+ }
16699
+ }
16700
+ }
16517
16701
  clear() {
16518
16702
  this.metadataByRequestId.clear();
16519
16703
  this.requestIdByRecordId.clear();
16520
- this.requestIdsByActionId.clear();
16704
+ this.requestIdsByCapture.clear();
16521
16705
  this.requestIdsByTag.clear();
16706
+ this.tombstonedRequestIds.clear();
16707
+ }
16708
+ materializeRecord(record, observedAt, options) {
16709
+ if (this.tombstonedRequestIds.has(record.requestId)) {
16710
+ return void 0;
16711
+ }
16712
+ let metadata = this.metadataByRequestId.get(record.requestId);
16713
+ if (!metadata) {
16714
+ metadata = {
16715
+ recordId: `record:${crypto.randomUUID()}`,
16716
+ observedAt,
16717
+ ...record.pageRef === void 0 ? {} : { pageRef: record.pageRef },
16718
+ tags: /* @__PURE__ */ new Set()
16719
+ };
16720
+ this.metadataByRequestId.set(record.requestId, metadata);
16721
+ this.requestIdByRecordId.set(metadata.recordId, record.requestId);
16722
+ } else if (metadata.pageRef === void 0 && record.pageRef !== void 0) {
16723
+ metadata.pageRef = record.pageRef;
16724
+ }
16725
+ return {
16726
+ recordId: metadata.recordId,
16727
+ ...metadata.capture === void 0 ? {} : { capture: metadata.capture },
16728
+ ...metadata.tags.size === 0 ? {} : { tags: [...metadata.tags].sort() },
16729
+ ...metadata.savedAt === void 0 ? {} : { savedAt: metadata.savedAt },
16730
+ record: toProtocolNetworkRecord(record, {
16731
+ redactSecretHeaders: options.redactSecretHeaders ?? true
16732
+ })
16733
+ };
16522
16734
  }
16523
16735
  addIndexedRequestId(index, key, requestId) {
16524
16736
  const requestIds = index.get(key) ?? /* @__PURE__ */ new Set();
@@ -23253,6 +23465,7 @@ function diffInteractionTraces(left, right) {
23253
23465
 
23254
23466
  // ../runtime-core/src/sdk/runtime.ts
23255
23467
  var requireForAuthRecipeHook = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('bin.cjs', document.baseURI).href)));
23468
+ var MUTATION_CAPTURE_FINALIZE_TIMEOUT_MS = 5e3;
23256
23469
  var OpensteerSessionRuntime = class {
23257
23470
  workspace;
23258
23471
  rootPath;
@@ -23269,12 +23482,11 @@ var OpensteerSessionRuntime = class {
23269
23482
  engine;
23270
23483
  dom;
23271
23484
  computer;
23272
- networkJournal = new NetworkJournal();
23485
+ networkHistory = new NetworkHistory();
23273
23486
  extractionDescriptors;
23274
23487
  sessionRef;
23275
23488
  pageRef;
23276
23489
  runId;
23277
- backgroundNetworkPersistence = /* @__PURE__ */ new Set();
23278
23490
  cookieJars = /* @__PURE__ */ new Map();
23279
23491
  recipeCache = /* @__PURE__ */ new Map();
23280
23492
  ownsEngine = false;
@@ -23636,34 +23848,32 @@ var OpensteerSessionRuntime = class {
23636
23848
  assertValidSemanticOperationInput("page.goto", input);
23637
23849
  const pageRef = await this.ensurePageRef();
23638
23850
  const startedAt = Date.now();
23851
+ let mutationCaptureDiagnostics;
23639
23852
  try {
23640
- const { navigation, state } = await this.runWithOperationTimeout(
23853
+ const { navigation, state } = await this.runMutationCapturedOperation(
23641
23854
  "page.goto",
23855
+ {
23856
+ ...input.captureNetwork === void 0 ? {} : { captureNetwork: input.captureNetwork },
23857
+ options
23858
+ },
23642
23859
  async (timeout) => {
23643
- const baselineRequestIds = await this.beginMutationCapture(timeout);
23644
- try {
23645
- const navigation2 = await this.navigatePage(
23646
- {
23647
- operation: "page.goto",
23648
- pageRef,
23649
- url: input.url
23650
- },
23651
- timeout
23652
- );
23653
- timeout.throwIfAborted();
23654
- await this.completeMutationCapture(timeout, baselineRequestIds, input.networkTag);
23655
- return {
23656
- navigation: navigation2,
23657
- state: await timeout.runStep(() => this.readSessionState())
23658
- };
23659
- } catch (error) {
23660
- await this.completeMutationCapture(timeout, baselineRequestIds, input.networkTag).catch(
23661
- () => void 0
23662
- );
23663
- throw error;
23664
- }
23860
+ const navigation2 = await this.navigatePage(
23861
+ {
23862
+ operation: "page.goto",
23863
+ pageRef,
23864
+ url: input.url
23865
+ },
23866
+ timeout
23867
+ );
23868
+ timeout.throwIfAborted();
23869
+ return {
23870
+ navigation: navigation2,
23871
+ state: await timeout.runStep(() => this.readSessionState())
23872
+ };
23665
23873
  },
23666
- options
23874
+ (diagnostics) => {
23875
+ mutationCaptureDiagnostics = diagnostics;
23876
+ }
23667
23877
  );
23668
23878
  await this.appendTrace({
23669
23879
  operation: "page.goto",
@@ -23672,7 +23882,8 @@ var OpensteerSessionRuntime = class {
23672
23882
  outcome: "ok",
23673
23883
  data: {
23674
23884
  url: input.url,
23675
- state
23885
+ state,
23886
+ ...buildMutationCaptureTraceData(mutationCaptureDiagnostics)
23676
23887
  },
23677
23888
  context: buildRuntimeTraceContext({
23678
23889
  sessionRef: this.sessionRef,
@@ -23688,6 +23899,7 @@ var OpensteerSessionRuntime = class {
23688
23899
  completedAt: Date.now(),
23689
23900
  outcome: "error",
23690
23901
  error,
23902
+ data: buildMutationCaptureTraceData(mutationCaptureDiagnostics),
23691
23903
  context: buildRuntimeTraceContext({
23692
23904
  sessionRef: this.sessionRef,
23693
23905
  pageRef
@@ -23700,9 +23912,11 @@ var OpensteerSessionRuntime = class {
23700
23912
  assertValidSemanticOperationInput("page.evaluate", input);
23701
23913
  const pageRef = input.pageRef ?? await this.ensurePageRef();
23702
23914
  const startedAt = Date.now();
23915
+ let mutationCaptureDiagnostics;
23703
23916
  try {
23704
- const output = await this.runWithOperationTimeout(
23917
+ const output = await this.runMutationCapturedOperation(
23705
23918
  "page.evaluate",
23919
+ { options },
23706
23920
  async (timeout) => {
23707
23921
  const remainingMs = timeout.remainingMs();
23708
23922
  const evaluated = await timeout.runStep(
@@ -23718,7 +23932,9 @@ var OpensteerSessionRuntime = class {
23718
23932
  value: toJsonValueOrNull(evaluated.data)
23719
23933
  };
23720
23934
  },
23721
- options
23935
+ (diagnostics) => {
23936
+ mutationCaptureDiagnostics = diagnostics;
23937
+ }
23722
23938
  );
23723
23939
  await this.appendTrace({
23724
23940
  operation: "page.evaluate",
@@ -23727,7 +23943,8 @@ var OpensteerSessionRuntime = class {
23727
23943
  outcome: "ok",
23728
23944
  data: {
23729
23945
  pageRef: output.pageRef,
23730
- value: output.value
23946
+ value: output.value,
23947
+ ...buildMutationCaptureTraceData(mutationCaptureDiagnostics)
23731
23948
  },
23732
23949
  context: buildRuntimeTraceContext({
23733
23950
  sessionRef: this.sessionRef,
@@ -23742,6 +23959,7 @@ var OpensteerSessionRuntime = class {
23742
23959
  completedAt: Date.now(),
23743
23960
  outcome: "error",
23744
23961
  error,
23962
+ data: buildMutationCaptureTraceData(mutationCaptureDiagnostics),
23745
23963
  context: buildRuntimeTraceContext({
23746
23964
  sessionRef: this.sessionRef,
23747
23965
  pageRef
@@ -24080,38 +24298,19 @@ var OpensteerSessionRuntime = class {
24080
24298
  }
24081
24299
  async queryNetwork(input = {}, options = {}) {
24082
24300
  assertValidSemanticOperationInput("network.query", input);
24083
- if (input.source !== "saved") {
24084
- await this.ensurePageRef();
24085
- }
24086
24301
  const root = await this.ensureRoot();
24087
24302
  const startedAt = Date.now();
24088
24303
  try {
24089
24304
  const output = await this.runWithOperationTimeout(
24090
24305
  "network.query",
24091
24306
  async (timeout) => {
24092
- if (input.source === "saved") {
24093
- await timeout.runStep(() => this.flushBackgroundNetworkPersistence());
24094
- return {
24095
- records: await timeout.runStep(
24096
- () => root.registry.savedNetwork.query({
24097
- ...input.recordId === void 0 ? {} : { recordId: input.recordId },
24098
- ...input.requestId === void 0 ? {} : { requestId: input.requestId },
24099
- ...input.actionId === void 0 ? {} : { actionId: input.actionId },
24100
- ...input.tag === void 0 ? {} : { tag: input.tag },
24101
- ...input.url === void 0 ? {} : { url: input.url },
24102
- ...input.hostname === void 0 ? {} : { hostname: input.hostname },
24103
- ...input.path === void 0 ? {} : { path: input.path },
24104
- ...input.method === void 0 ? {} : { method: input.method },
24105
- ...input.status === void 0 ? {} : { status: input.status },
24106
- ...input.resourceType === void 0 ? {} : { resourceType: input.resourceType },
24107
- ...input.includeBodies === void 0 ? {} : { includeBodies: input.includeBodies },
24108
- ...input.limit === void 0 ? {} : { limit: input.limit }
24109
- })
24110
- )
24111
- };
24112
- }
24307
+ await this.syncPersistedNetworkSelection(timeout, input, {
24308
+ includeBodies: input.includeBodies ?? false
24309
+ });
24113
24310
  return {
24114
- records: await this.queryLiveNetwork(input, timeout)
24311
+ records: await timeout.runStep(
24312
+ () => root.registry.savedNetwork.query(this.toSavedNetworkQueryInput(input))
24313
+ )
24115
24314
  };
24116
24315
  },
24117
24316
  options
@@ -24122,7 +24321,6 @@ var OpensteerSessionRuntime = class {
24122
24321
  completedAt: Date.now(),
24123
24322
  outcome: "ok",
24124
24323
  data: {
24125
- source: input.source ?? "live",
24126
24324
  includeBodies: input.includeBodies ?? false,
24127
24325
  limit: input.limit ?? 50,
24128
24326
  count: output.records.length
@@ -24148,56 +24346,42 @@ var OpensteerSessionRuntime = class {
24148
24346
  throw error;
24149
24347
  }
24150
24348
  }
24151
- async saveNetwork(input, options = {}) {
24152
- assertValidSemanticOperationInput("network.save", input);
24153
- await this.ensurePageRef();
24349
+ async tagNetwork(input, options = {}) {
24350
+ assertValidSemanticOperationInput("network.tag", input);
24154
24351
  const root = await this.ensureRoot();
24352
+ const filter = this.toQueryInputFromTagInput(input);
24353
+ const savedFilter = this.toSavedNetworkQueryInput(filter);
24155
24354
  const startedAt = Date.now();
24156
24355
  try {
24157
24356
  const output = await this.runWithOperationTimeout(
24158
- "network.save",
24357
+ "network.tag",
24159
24358
  async (timeout) => {
24160
- const records = await this.queryLiveNetwork(
24161
- {
24162
- includeBodies: true,
24163
- source: "live",
24164
- ...input.pageRef === void 0 ? {} : { pageRef: input.pageRef },
24165
- ...input.recordId === void 0 ? {} : { recordId: input.recordId },
24166
- ...input.requestId === void 0 ? {} : { requestId: input.requestId },
24167
- ...input.actionId === void 0 ? {} : { actionId: input.actionId },
24168
- ...input.url === void 0 ? {} : { url: input.url },
24169
- ...input.hostname === void 0 ? {} : { hostname: input.hostname },
24170
- ...input.path === void 0 ? {} : { path: input.path },
24171
- ...input.method === void 0 ? {} : { method: input.method },
24172
- ...input.status === void 0 ? {} : { status: input.status },
24173
- ...input.resourceType === void 0 ? {} : { resourceType: input.resourceType }
24174
- },
24175
- timeout,
24176
- { ignoreLimit: true, redactSecretHeaders: false }
24177
- );
24178
- this.networkJournal.addTag(records, input.tag);
24359
+ const records = await this.syncPersistedNetworkSelection(timeout, filter, {
24360
+ includeBodies: false
24361
+ });
24362
+ this.networkHistory.addTag(records, input.tag);
24179
24363
  return {
24180
- savedCount: await timeout.runStep(
24181
- () => root.registry.savedNetwork.save(records, input.tag)
24364
+ taggedCount: await timeout.runStep(
24365
+ () => root.registry.savedNetwork.tagByFilter(savedFilter, input.tag)
24182
24366
  )
24183
24367
  };
24184
24368
  },
24185
24369
  options
24186
24370
  );
24187
24371
  await this.appendTrace({
24188
- operation: "network.save",
24372
+ operation: "network.tag",
24189
24373
  startedAt,
24190
24374
  completedAt: Date.now(),
24191
24375
  outcome: "ok",
24192
24376
  data: {
24193
24377
  tag: input.tag,
24194
- savedCount: output.savedCount
24378
+ taggedCount: output.taggedCount
24195
24379
  }
24196
24380
  });
24197
24381
  return output;
24198
24382
  } catch (error) {
24199
24383
  await this.appendTrace({
24200
- operation: "network.save",
24384
+ operation: "network.tag",
24201
24385
  startedAt,
24202
24386
  completedAt: Date.now(),
24203
24387
  outcome: "error",
@@ -24214,7 +24398,31 @@ var OpensteerSessionRuntime = class {
24214
24398
  const output = await this.runWithOperationTimeout(
24215
24399
  "network.clear",
24216
24400
  async (timeout) => {
24217
- await timeout.runStep(() => this.flushBackgroundNetworkPersistence());
24401
+ if (this.sessionRef !== void 0) {
24402
+ if (input.capture !== void 0 || input.tag !== void 0) {
24403
+ const records = await this.queryLiveNetwork(
24404
+ {
24405
+ ...input.capture === void 0 ? {} : { capture: input.capture },
24406
+ ...input.tag === void 0 ? {} : { tag: input.tag }
24407
+ },
24408
+ timeout,
24409
+ {
24410
+ ignoreLimit: true
24411
+ }
24412
+ );
24413
+ this.networkHistory.tombstoneRequestIds(
24414
+ records.map((record) => record.record.requestId)
24415
+ );
24416
+ } else {
24417
+ const liveRequestIds = await this.readLiveRequestIds(timeout, {
24418
+ includeCurrentPageOnly: false
24419
+ });
24420
+ this.networkHistory.tombstoneRequestIds(liveRequestIds);
24421
+ }
24422
+ }
24423
+ if (input.capture === void 0 && input.tag === void 0) {
24424
+ this.networkHistory.tombstoneRequestIds(this.networkHistory.getKnownRequestIds());
24425
+ }
24218
24426
  return {
24219
24427
  clearedCount: await timeout.runStep(() => root.registry.savedNetwork.clear(input))
24220
24428
  };
@@ -24227,6 +24435,7 @@ var OpensteerSessionRuntime = class {
24227
24435
  completedAt: Date.now(),
24228
24436
  outcome: "ok",
24229
24437
  data: {
24438
+ ...input.capture === void 0 ? {} : { capture: input.capture },
24230
24439
  ...input.tag === void 0 ? {} : { tag: input.tag },
24231
24440
  clearedCount: output.clearedCount
24232
24441
  }
@@ -24931,7 +25140,6 @@ var OpensteerSessionRuntime = class {
24931
25140
  });
24932
25141
  const networkRecords = await this.queryLiveNetwork(
24933
25142
  {
24934
- source: "live",
24935
25143
  pageRef,
24936
25144
  ...input.network?.url === void 0 ? {} : { url: input.network.url },
24937
25145
  ...input.network?.hostname === void 0 ? {} : { hostname: input.network.hostname },
@@ -24947,7 +25155,7 @@ var OpensteerSessionRuntime = class {
24947
25155
  );
24948
25156
  const persistedNetwork = filterReverseObservationWindow(
24949
25157
  networkRecords.filter(isReverseRelevantNetworkRecord),
24950
- this.networkJournal,
25158
+ this.networkHistory,
24951
25159
  input.captureWindowMs
24952
25160
  );
24953
25161
  const fallbackSavedNetwork = persistedNetwork.length === 0 ? (await root.registry.savedNetwork.query({
@@ -24962,7 +25170,10 @@ var OpensteerSessionRuntime = class {
24962
25170
  const observationId = `observation:${crypto.randomUUID()}`;
24963
25171
  const networkTag = `reverse-case:${caseRecord.id}:${observationId}`;
24964
25172
  if (observationNetwork.length > 0) {
24965
- await root.registry.savedNetwork.save(observationNetwork, networkTag);
25173
+ await root.registry.savedNetwork.save(observationNetwork, {
25174
+ tag: networkTag,
25175
+ bodyWriteMode: input.network?.includeBodies === false ? "metadata-only" : "authoritative"
25176
+ });
24966
25177
  }
24967
25178
  const scriptArtifactIds = input.includeScripts === false ? [] : (await this.captureScriptsInternal(
24968
25179
  pageRef,
@@ -25058,7 +25269,7 @@ var OpensteerSessionRuntime = class {
25058
25269
  includeBodies: true,
25059
25270
  redactSecretHeaders: false
25060
25271
  }),
25061
- observedAt: this.networkJournal.getObservedAt(recordId)
25272
+ observedAt: this.networkHistory.getObservedAt(recordId)
25062
25273
  }))
25063
25274
  );
25064
25275
  const clusteredRecords = observationRecords.map((entry) => {
@@ -25406,7 +25617,9 @@ var OpensteerSessionRuntime = class {
25406
25617
  };
25407
25618
  }
25408
25619
  const bindings = /* @__PURE__ */ new Map();
25409
- const baselineRequestIds = await this.beginMutationCapture(timeout);
25620
+ const baselineRequestIds = await this.readLiveRequestIds(timeout, {
25621
+ includeCurrentPageOnly: true
25622
+ });
25410
25623
  const pageRef = explicitPageRef ?? await this.ensurePageRef();
25411
25624
  const validatorMap = new Map(
25412
25625
  packageRecord.payload.validators.map((validator) => [validator.id, validator])
@@ -25998,7 +26211,10 @@ var OpensteerSessionRuntime = class {
25998
26211
  timeout.signal
25999
26212
  )).filter((record) => !baselineRequestIds.has(record.record.requestId));
26000
26213
  if (deltaRecords.length > 0) {
26001
- await root.registry.savedNetwork.save(deltaRecords, `interaction:${pageRef}`);
26214
+ await root.registry.savedNetwork.save(deltaRecords, {
26215
+ tag: `interaction:${pageRef}`,
26216
+ bodyWriteMode: "authoritative"
26217
+ });
26002
26218
  }
26003
26219
  const trace = await root.registry.interactionTraces.write({
26004
26220
  key: input.key ?? buildInteractionTraceKey(pageInfo.url),
@@ -26341,7 +26557,7 @@ var OpensteerSessionRuntime = class {
26341
26557
  includeBodies: true
26342
26558
  });
26343
26559
  const inferred = inferRequestPlanFromNetworkRecord(source, input, {
26344
- ...this.networkJournal.getObservedAt(source.recordId) === void 0 ? {} : { observedAt: this.networkJournal.getObservedAt(source.recordId) }
26560
+ ...this.networkHistory.getObservedAt(source.recordId) === void 0 ? {} : { observedAt: this.networkHistory.getObservedAt(source.recordId) }
26345
26561
  });
26346
26562
  return timeout.runStep(
26347
26563
  () => root.registry.requestPlans.write({
@@ -27075,33 +27291,38 @@ var OpensteerSessionRuntime = class {
27075
27291
  assertValidSemanticOperationInput("computer.execute", input);
27076
27292
  const pageRef = await this.ensurePageRef();
27077
27293
  const startedAt = Date.now();
27294
+ let mutationCaptureDiagnostics;
27295
+ let boundaryDiagnostics;
27078
27296
  try {
27079
- const { artifacts, output } = await this.runWithOperationTimeout(
27297
+ const { artifacts, output } = await this.runMutationCapturedOperation(
27080
27298
  "computer.execute",
27299
+ {
27300
+ ...input.captureNetwork === void 0 ? {} : { captureNetwork: input.captureNetwork },
27301
+ options
27302
+ },
27081
27303
  async (timeout) => {
27082
- const baselineRequestIds = await this.beginMutationCapture(timeout);
27083
27304
  try {
27084
27305
  const output2 = await this.requireComputer().execute({
27085
27306
  pageRef,
27086
27307
  input,
27087
27308
  timeout
27088
27309
  });
27310
+ boundaryDiagnostics = takeActionBoundaryDiagnostics(timeout.signal);
27089
27311
  timeout.throwIfAborted();
27090
27312
  this.pageRef = output2.pageRef;
27091
- await this.completeMutationCapture(timeout, baselineRequestIds, input.networkTag);
27092
27313
  const artifacts2 = await this.persistComputerArtifacts(output2, timeout);
27093
27314
  return {
27094
27315
  artifacts: { manifests: artifacts2.manifests },
27095
27316
  output: artifacts2.output
27096
27317
  };
27097
27318
  } catch (error) {
27098
- await this.completeMutationCapture(timeout, baselineRequestIds, input.networkTag).catch(
27099
- () => void 0
27100
- );
27319
+ boundaryDiagnostics ??= takeActionBoundaryDiagnostics(timeout.signal);
27101
27320
  throw error;
27102
27321
  }
27103
27322
  },
27104
- options
27323
+ (diagnostics) => {
27324
+ mutationCaptureDiagnostics = diagnostics;
27325
+ }
27105
27326
  );
27106
27327
  await this.appendTrace({
27107
27328
  operation: "computer.execute",
@@ -27117,6 +27338,8 @@ var OpensteerSessionRuntime = class {
27117
27338
  nativeViewport: output.nativeViewport,
27118
27339
  displayScale: output.displayScale,
27119
27340
  timing: output.timing,
27341
+ ...boundaryDiagnostics === void 0 ? {} : { settle: boundaryDiagnostics },
27342
+ ...buildMutationCaptureTraceData(mutationCaptureDiagnostics),
27120
27343
  ...output.trace === void 0 ? {} : { trace: output.trace }
27121
27344
  },
27122
27345
  context: buildRuntimeTraceContext({
@@ -27135,6 +27358,10 @@ var OpensteerSessionRuntime = class {
27135
27358
  completedAt: Date.now(),
27136
27359
  outcome: "error",
27137
27360
  error,
27361
+ data: {
27362
+ ...boundaryDiagnostics === void 0 ? {} : { settle: boundaryDiagnostics },
27363
+ ...buildMutationCaptureTraceData(mutationCaptureDiagnostics)
27364
+ },
27138
27365
  context: buildRuntimeTraceContext({
27139
27366
  sessionRef: this.sessionRef,
27140
27367
  pageRef: this.pageRef
@@ -27153,7 +27380,7 @@ var OpensteerSessionRuntime = class {
27153
27380
  await this.runWithOperationTimeout(
27154
27381
  "session.close",
27155
27382
  async (timeout) => {
27156
- await timeout.runStep(() => this.flushBackgroundNetworkPersistence());
27383
+ await timeout.runStep(() => this.flushPersistedNetworkHistory());
27157
27384
  if (engine === void 0) {
27158
27385
  return;
27159
27386
  }
@@ -27222,10 +27449,7 @@ var OpensteerSessionRuntime = class {
27222
27449
  }
27223
27450
  async disconnect() {
27224
27451
  try {
27225
- await this.flushBackgroundNetworkPersistence();
27226
- if (this.sessionRef !== void 0 && this.pageRef !== void 0) {
27227
- await this.saveNetwork({ tag: "auto" }).catch(() => void 0);
27228
- }
27452
+ await this.flushPersistedNetworkHistory();
27229
27453
  } finally {
27230
27454
  await this.resetRuntimeState({
27231
27455
  disposeEngine: true
@@ -27241,33 +27465,38 @@ var OpensteerSessionRuntime = class {
27241
27465
  async runDomAction(operation, input, executor, options = {}) {
27242
27466
  const pageRef = await this.ensurePageRef();
27243
27467
  const startedAt = Date.now();
27468
+ let mutationCaptureDiagnostics;
27469
+ let boundaryDiagnostics;
27244
27470
  try {
27245
- const { executed, preparedTarget } = await this.runWithOperationTimeout(
27471
+ const { executed, preparedTarget } = await this.runMutationCapturedOperation(
27246
27472
  operation,
27473
+ {
27474
+ ...input.captureNetwork === void 0 ? {} : { captureNetwork: input.captureNetwork },
27475
+ options
27476
+ },
27247
27477
  async (timeout) => {
27248
- const baselineRequestIds = await this.beginMutationCapture(timeout);
27478
+ const preparedTarget2 = await this.prepareDomTarget(
27479
+ pageRef,
27480
+ operation,
27481
+ input.target,
27482
+ input.persistAsDescription,
27483
+ timeout
27484
+ );
27249
27485
  try {
27250
- const preparedTarget2 = await this.prepareDomTarget(
27251
- pageRef,
27252
- operation,
27253
- input.target,
27254
- input.persistAsDescription,
27255
- timeout
27256
- );
27257
27486
  const executed2 = await executor(pageRef, preparedTarget2.target, timeout);
27258
- await this.completeMutationCapture(timeout, baselineRequestIds, input.networkTag);
27487
+ boundaryDiagnostics = takeActionBoundaryDiagnostics(timeout.signal);
27259
27488
  return {
27260
27489
  executed: executed2,
27261
27490
  preparedTarget: preparedTarget2
27262
27491
  };
27263
27492
  } catch (error) {
27264
- await this.completeMutationCapture(timeout, baselineRequestIds, input.networkTag).catch(
27265
- () => void 0
27266
- );
27493
+ boundaryDiagnostics ??= takeActionBoundaryDiagnostics(timeout.signal);
27267
27494
  throw error;
27268
27495
  }
27269
27496
  },
27270
- options
27497
+ (diagnostics) => {
27498
+ mutationCaptureDiagnostics = diagnostics;
27499
+ }
27271
27500
  );
27272
27501
  const output = toOpensteerActionResult(executed.result, preparedTarget.persistedDescription);
27273
27502
  await this.appendTrace({
@@ -27278,7 +27507,9 @@ var OpensteerSessionRuntime = class {
27278
27507
  data: {
27279
27508
  target: output.target,
27280
27509
  ...output.point === void 0 ? {} : { point: output.point },
27281
- ...output.persistedDescription === void 0 ? {} : { persistedDescription: output.persistedDescription }
27510
+ ...output.persistedDescription === void 0 ? {} : { persistedDescription: output.persistedDescription },
27511
+ ...boundaryDiagnostics === void 0 ? {} : { settle: boundaryDiagnostics },
27512
+ ...buildMutationCaptureTraceData(mutationCaptureDiagnostics)
27282
27513
  },
27283
27514
  context: buildRuntimeTraceContext({
27284
27515
  sessionRef: this.sessionRef,
@@ -27296,6 +27527,10 @@ var OpensteerSessionRuntime = class {
27296
27527
  completedAt: Date.now(),
27297
27528
  outcome: "error",
27298
27529
  error,
27530
+ data: {
27531
+ ...boundaryDiagnostics === void 0 ? {} : { settle: boundaryDiagnostics },
27532
+ ...buildMutationCaptureTraceData(mutationCaptureDiagnostics)
27533
+ },
27299
27534
  context: buildRuntimeTraceContext({
27300
27535
  sessionRef: this.sessionRef,
27301
27536
  pageRef
@@ -27317,7 +27552,10 @@ var OpensteerSessionRuntime = class {
27317
27552
  };
27318
27553
  }
27319
27554
  if (target.kind === "element") {
27320
- const elementTarget = { kind: "selector", selector: `[c="${String(target.element)}"]` };
27555
+ const elementTarget = {
27556
+ kind: "selector",
27557
+ selector: `[c="${String(target.element)}"]`
27558
+ };
27321
27559
  const resolved2 = await timeout.runStep(
27322
27560
  () => this.requireDom().resolveTarget({
27323
27561
  pageRef,
@@ -27380,11 +27618,11 @@ var OpensteerSessionRuntime = class {
27380
27618
  };
27381
27619
  }
27382
27620
  async queryLiveNetwork(input, timeout, options = {}) {
27383
- const requestIds = resolveLiveQueryRequestIds(input, this.networkJournal);
27621
+ const requestIds = resolveLiveQueryRequestIds(input, this.networkHistory);
27384
27622
  if (requestIds !== void 0 && requestIds.length === 0) {
27385
27623
  return [];
27386
27624
  }
27387
- const pageRef = resolveLiveQueryPageRef(input, this.pageRef, requestIds, this.networkJournal);
27625
+ const pageRef = resolveLiveQueryPageRef(input, this.pageRef, requestIds, this.networkHistory);
27388
27626
  const includeCurrentPageOnly = pageRef === void 0 && input.recordId === void 0;
27389
27627
  const metadataRecords = await timeout.runStep(
27390
27628
  () => this.readLiveNetworkRecords(
@@ -27402,7 +27640,7 @@ var OpensteerSessionRuntime = class {
27402
27640
  const filtered = filterNetworkQueryRecords(metadataRecords, {
27403
27641
  ...input.recordId === void 0 ? {} : { recordId: input.recordId },
27404
27642
  ...input.requestId === void 0 ? {} : { requestId: input.requestId },
27405
- ...input.actionId === void 0 ? {} : { actionId: input.actionId },
27643
+ ...input.capture === void 0 ? {} : { capture: input.capture },
27406
27644
  ...input.tag === void 0 ? {} : { tag: input.tag },
27407
27645
  ...input.url === void 0 ? {} : { url: input.url },
27408
27646
  ...input.hostname === void 0 ? {} : { hostname: input.hostname },
@@ -27411,7 +27649,7 @@ var OpensteerSessionRuntime = class {
27411
27649
  ...input.status === void 0 ? {} : { status: input.status },
27412
27650
  ...input.resourceType === void 0 ? {} : { resourceType: input.resourceType }
27413
27651
  });
27414
- const sorted = sortLiveNetworkRecords(filtered, this.networkJournal);
27652
+ const sorted = sortLiveNetworkRecords(filtered, this.networkHistory);
27415
27653
  const limit = options.ignoreLimit ? sorted.length : Math.max(1, Math.min(input.limit ?? 50, 200));
27416
27654
  const limited = sorted.slice(0, limit);
27417
27655
  if (!(input.includeBodies ?? false) || limited.length === 0) {
@@ -27608,40 +27846,95 @@ var OpensteerSessionRuntime = class {
27608
27846
  artifactId: manifest.artifactId
27609
27847
  };
27610
27848
  }
27611
- beginMutationCapture(timeout) {
27612
- return this.readLiveRequestIds(timeout, {
27613
- includeCurrentPageOnly: true
27614
- });
27615
- }
27616
- async completeMutationCapture(timeout, baselineRequestIds, networkTag) {
27617
- const records = await timeout.runStep(
27618
- () => this.readLiveNetworkRecords(
27619
- {
27620
- includeBodies: false,
27621
- includeCurrentPageOnly: true
27849
+ async runMutationCapturedOperation(operation, input, execute, onFinalized) {
27850
+ let plan;
27851
+ try {
27852
+ const result = await this.runWithOperationTimeout(
27853
+ operation,
27854
+ async (timeout) => {
27855
+ plan = await this.beginMutationCapture(timeout, input.captureNetwork);
27856
+ return execute(timeout);
27622
27857
  },
27623
- timeout.signal
27624
- )
27858
+ input.options
27859
+ );
27860
+ const diagnostics = await this.finalizeMutationCaptureBestEffort(plan);
27861
+ onFinalized?.(diagnostics);
27862
+ return result;
27863
+ } catch (error) {
27864
+ const diagnostics = await this.finalizeMutationCaptureBestEffort(plan);
27865
+ onFinalized?.(diagnostics);
27866
+ throw error;
27867
+ }
27868
+ }
27869
+ async beginMutationCapture(timeout, capture) {
27870
+ if (capture === void 0) {
27871
+ return void 0;
27872
+ }
27873
+ return {
27874
+ baselineRequestIds: await this.readLiveRequestIds(timeout, {
27875
+ includeCurrentPageOnly: true
27876
+ }),
27877
+ capture
27878
+ };
27879
+ }
27880
+ async finalizeMutationCaptureBestEffort(plan) {
27881
+ if (plan === void 0) {
27882
+ return {};
27883
+ }
27884
+ try {
27885
+ await withDetachedTimeoutSignal(MUTATION_CAPTURE_FINALIZE_TIMEOUT_MS, async (signal) => {
27886
+ await this.completeMutationCaptureWithSignal(signal, plan);
27887
+ });
27888
+ return {};
27889
+ } catch (error) {
27890
+ return {
27891
+ finalizeError: normalizeOpensteerError(error)
27892
+ };
27893
+ }
27894
+ }
27895
+ async completeMutationCaptureWithSignal(signal, plan) {
27896
+ const records = await this.readLiveNetworkRecords(
27897
+ {
27898
+ includeBodies: false,
27899
+ includeCurrentPageOnly: true
27900
+ },
27901
+ signal
27625
27902
  );
27626
- const delta = records.filter((record) => !baselineRequestIds.has(record.record.requestId));
27903
+ const delta = records.filter((record) => !plan.baselineRequestIds.has(record.record.requestId));
27627
27904
  if (delta.length === 0) {
27628
27905
  return;
27629
27906
  }
27630
- this.networkJournal.assignActionId(delta, `action:${crypto.randomUUID()}`);
27631
- if (networkTag === void 0) {
27632
- return;
27633
- }
27634
- this.networkJournal.addTag(delta, networkTag);
27635
- this.scheduleBackgroundNetworkSaveByRequestIds(
27907
+ this.networkHistory.assignCapture(delta, plan.capture);
27908
+ await this.persistLiveRequestIdsWithSignal(
27636
27909
  delta.map((record) => record.record.requestId),
27637
- networkTag
27910
+ signal,
27911
+ {
27912
+ includeCurrentPageOnly: true
27913
+ }
27638
27914
  );
27639
27915
  }
27640
27916
  async resolveNetworkRecordByRecordId(recordId, timeout, options) {
27641
27917
  const root = await this.ensureRoot();
27918
+ await this.syncPersistedNetworkSelection(
27919
+ timeout,
27920
+ {
27921
+ recordId,
27922
+ includeBodies: options.includeBodies
27923
+ },
27924
+ {
27925
+ includeBodies: options.includeBodies
27926
+ }
27927
+ );
27928
+ const saved = await timeout.runStep(
27929
+ () => root.registry.savedNetwork.getByRecordId(recordId, {
27930
+ includeBodies: options.includeBodies
27931
+ })
27932
+ );
27933
+ if (saved) {
27934
+ return saved;
27935
+ }
27642
27936
  const live = await this.queryLiveNetwork(
27643
27937
  {
27644
- source: "live",
27645
27938
  recordId,
27646
27939
  includeBodies: options.includeBodies,
27647
27940
  limit: 1
@@ -27652,24 +27945,15 @@ var OpensteerSessionRuntime = class {
27652
27945
  ...options.redactSecretHeaders === void 0 ? {} : { redactSecretHeaders: options.redactSecretHeaders }
27653
27946
  }
27654
27947
  );
27655
- if (live.length > 0) {
27948
+ if (live[0] !== void 0) {
27656
27949
  return live[0];
27657
27950
  }
27658
- await timeout.runStep(() => this.flushBackgroundNetworkPersistence());
27659
- const saved = await timeout.runStep(
27660
- () => root.registry.savedNetwork.getByRecordId(recordId, {
27661
- includeBodies: options.includeBodies
27662
- })
27663
- );
27664
- if (!saved) {
27665
- throw new OpensteerProtocolError("not-found", `network record ${recordId} was not found`, {
27666
- details: {
27667
- recordId,
27668
- kind: "network-record"
27669
- }
27670
- });
27671
- }
27672
- return saved;
27951
+ throw new OpensteerProtocolError("not-found", `network record ${recordId} was not found`, {
27952
+ details: {
27953
+ recordId,
27954
+ kind: "network-record"
27955
+ }
27956
+ });
27673
27957
  }
27674
27958
  resolveCurrentStateSource() {
27675
27959
  const ownership = this.sessionInfoBase.provider?.ownership;
@@ -27909,7 +28193,6 @@ var OpensteerSessionRuntime = class {
27909
28193
  timeout.throwIfAborted();
27910
28194
  const records = await this.queryLiveNetwork(
27911
28195
  {
27912
- source: "live",
27913
28196
  pageRef,
27914
28197
  url,
27915
28198
  method,
@@ -27940,7 +28223,6 @@ var OpensteerSessionRuntime = class {
27940
28223
  timeout.throwIfAborted();
27941
28224
  const records = await this.queryLiveNetwork(
27942
28225
  {
27943
- source: "live",
27944
28226
  pageRef,
27945
28227
  ...filter.url === void 0 ? {} : { url: filter.url },
27946
28228
  ...filter.host === void 0 ? {} : { hostname: filter.host },
@@ -28006,12 +28288,12 @@ var OpensteerSessionRuntime = class {
28006
28288
  };
28007
28289
  }
28008
28290
  }
28009
- async readLiveNetworkRecords(input, signal) {
28291
+ async readBrowserNetworkRecords(input, signal) {
28010
28292
  const sessionRef = this.sessionRef;
28011
28293
  if (!sessionRef) {
28012
28294
  throw new Error("Opensteer session is not initialized");
28013
28295
  }
28014
- const records = await this.requireEngine().getNetworkRecords({
28296
+ return this.requireEngine().getNetworkRecords({
28015
28297
  sessionRef,
28016
28298
  ...input.includeCurrentPageOnly === false || input.pageRef !== void 0 ? input.pageRef === void 0 ? {} : { pageRef: input.pageRef } : this.pageRef === void 0 ? {} : { pageRef: this.pageRef },
28017
28299
  ...input.requestIds === void 0 ? {} : { requestIds: input.requestIds },
@@ -28024,10 +28306,103 @@ var OpensteerSessionRuntime = class {
28024
28306
  includeBodies: input.includeBodies,
28025
28307
  signal
28026
28308
  });
28027
- return this.networkJournal.sync(records, {
28309
+ }
28310
+ async readLiveNetworkRecords(input, signal) {
28311
+ const records = await this.readBrowserNetworkRecords(input, signal);
28312
+ return this.networkHistory.materialize(records, {
28028
28313
  redactSecretHeaders: input.redactSecretHeaders ?? true
28029
28314
  });
28030
28315
  }
28316
+ async persistLiveRequestIds(requestIds, timeout, options) {
28317
+ return timeout.runStep(
28318
+ () => this.persistLiveRequestIdsWithSignal(requestIds, timeout.signal, options)
28319
+ );
28320
+ }
28321
+ async persistLiveRequestIdsWithSignal(requestIds, signal, options) {
28322
+ if (requestIds.length === 0) {
28323
+ return [];
28324
+ }
28325
+ const root = await this.ensureRoot();
28326
+ const browserRecords = await this.readBrowserNetworkRecords(
28327
+ {
28328
+ includeBodies: true,
28329
+ includeCurrentPageOnly: options.includeCurrentPageOnly,
28330
+ ...options.pageRef === void 0 ? {} : { pageRef: options.pageRef },
28331
+ requestIds
28332
+ },
28333
+ signal
28334
+ );
28335
+ return this.networkHistory.persist(browserRecords, root.registry.savedNetwork, {
28336
+ bodyWriteMode: "authoritative",
28337
+ redactSecretHeaders: false
28338
+ });
28339
+ }
28340
+ async syncPersistedNetworkSelection(timeout, input, options) {
28341
+ if (this.sessionRef === void 0) {
28342
+ return [];
28343
+ }
28344
+ const requestIds = resolveLiveQueryRequestIds(input, this.networkHistory);
28345
+ if (requestIds !== void 0 && requestIds.length === 0) {
28346
+ return [];
28347
+ }
28348
+ const pageRef = resolveLiveQueryPageRef(input, this.pageRef, requestIds, this.networkHistory);
28349
+ const includeCurrentPageOnly = pageRef === void 0 && input.recordId === void 0;
28350
+ const browserRecords = await timeout.runStep(
28351
+ () => this.readBrowserNetworkRecords(
28352
+ {
28353
+ ...pageRef === void 0 ? {} : { pageRef },
28354
+ ...requestIds === void 0 ? {} : { requestIds },
28355
+ ...input.url === void 0 ? {} : { url: input.url },
28356
+ ...input.hostname === void 0 ? {} : { hostname: input.hostname },
28357
+ ...input.path === void 0 ? {} : { path: input.path },
28358
+ ...input.method === void 0 ? {} : { method: input.method },
28359
+ ...input.status === void 0 ? {} : { status: input.status },
28360
+ ...input.resourceType === void 0 ? {} : { resourceType: input.resourceType },
28361
+ includeBodies: options.includeBodies,
28362
+ includeCurrentPageOnly
28363
+ },
28364
+ timeout.signal
28365
+ )
28366
+ );
28367
+ const root = await this.ensureRoot();
28368
+ return timeout.runStep(
28369
+ () => this.networkHistory.persist(browserRecords, root.registry.savedNetwork, {
28370
+ bodyWriteMode: options.includeBodies ? "authoritative" : "metadata-only",
28371
+ redactSecretHeaders: false
28372
+ })
28373
+ );
28374
+ }
28375
+ toSavedNetworkQueryInput(input) {
28376
+ return {
28377
+ ...input.pageRef === void 0 ? {} : { pageRef: input.pageRef },
28378
+ ...input.recordId === void 0 ? {} : { recordId: input.recordId },
28379
+ ...input.requestId === void 0 ? {} : { requestId: input.requestId },
28380
+ ...input.capture === void 0 ? {} : { capture: input.capture },
28381
+ ...input.tag === void 0 ? {} : { tag: input.tag },
28382
+ ...input.url === void 0 ? {} : { url: input.url },
28383
+ ...input.hostname === void 0 ? {} : { hostname: input.hostname },
28384
+ ...input.path === void 0 ? {} : { path: input.path },
28385
+ ...input.method === void 0 ? {} : { method: input.method },
28386
+ ...input.status === void 0 ? {} : { status: input.status },
28387
+ ...input.resourceType === void 0 ? {} : { resourceType: input.resourceType },
28388
+ ...input.includeBodies === void 0 ? {} : { includeBodies: input.includeBodies },
28389
+ ...input.limit === void 0 ? {} : { limit: input.limit }
28390
+ };
28391
+ }
28392
+ toQueryInputFromTagInput(input) {
28393
+ return {
28394
+ ...input.pageRef === void 0 ? {} : { pageRef: input.pageRef },
28395
+ ...input.recordId === void 0 ? {} : { recordId: input.recordId },
28396
+ ...input.requestId === void 0 ? {} : { requestId: input.requestId },
28397
+ ...input.capture === void 0 ? {} : { capture: input.capture },
28398
+ ...input.url === void 0 ? {} : { url: input.url },
28399
+ ...input.hostname === void 0 ? {} : { hostname: input.hostname },
28400
+ ...input.path === void 0 ? {} : { path: input.path },
28401
+ ...input.method === void 0 ? {} : { method: input.method },
28402
+ ...input.status === void 0 ? {} : { status: input.status },
28403
+ ...input.resourceType === void 0 ? {} : { resourceType: input.resourceType }
28404
+ };
28405
+ }
28031
28406
  async readLiveRequestIds(timeout, options) {
28032
28407
  const records = await timeout.runStep(
28033
28408
  () => this.readLiveNetworkRecords(
@@ -28051,7 +28426,17 @@ var OpensteerSessionRuntime = class {
28051
28426
  )
28052
28427
  );
28053
28428
  const delta = records.filter((record) => !baselineRequestIds.has(record.record.requestId));
28054
- return sortLiveNetworkRecords(delta, this.networkJournal)[0]?.recordId;
28429
+ if (delta.length === 0) {
28430
+ return void 0;
28431
+ }
28432
+ await this.persistLiveRequestIds(
28433
+ delta.map((record) => record.record.requestId),
28434
+ timeout,
28435
+ {
28436
+ includeCurrentPageOnly: options.includeCurrentPageOnly
28437
+ }
28438
+ );
28439
+ return sortLiveNetworkRecords(delta, this.networkHistory)[0]?.recordId;
28055
28440
  }
28056
28441
  async executeTransportRequestWithJournal(request, timeout, sessionRef) {
28057
28442
  const baselineRequestIds = await this.readLiveRequestIds(timeout, {
@@ -28335,7 +28720,6 @@ var OpensteerSessionRuntime = class {
28335
28720
  const syntheticSessionRef = binding?.sessionRef ?? createSessionRef(`${transportLabel}-${this.workspace}`);
28336
28721
  const record = {
28337
28722
  recordId,
28338
- source: "saved",
28339
28723
  savedAt: now,
28340
28724
  record: {
28341
28725
  kind: "http",
@@ -28357,7 +28741,10 @@ var OpensteerSessionRuntime = class {
28357
28741
  ...response.body === void 0 ? {} : { responseBody: toProtocolBodyPayload(response.body) }
28358
28742
  }
28359
28743
  };
28360
- await root.registry.savedNetwork.save([record], tag);
28744
+ await root.registry.savedNetwork.save([record], {
28745
+ bodyWriteMode: "authoritative",
28746
+ ...tag === void 0 ? {} : { tag }
28747
+ });
28361
28748
  return recordId;
28362
28749
  }
28363
28750
  async executeResolvedRequestPlan(plan, input, timeout, binding) {
@@ -28730,7 +29117,6 @@ var OpensteerSessionRuntime = class {
28730
29117
  const record = await pollUntilResult(timeout, async () => {
28731
29118
  const matches = await this.queryLiveNetwork(
28732
29119
  {
28733
- source: "live",
28734
29120
  ...step.url === void 0 ? {} : { url: interpolateTemplate(step.url, variables) },
28735
29121
  ...step.hostname === void 0 ? {} : { hostname: interpolateTemplate(step.hostname, variables) },
28736
29122
  ...step.path === void 0 ? {} : { path: interpolateTemplate(step.path, variables) },
@@ -29188,37 +29574,7 @@ var OpensteerSessionRuntime = class {
29188
29574
  const pageUrl = step.pageUrl === void 0 ? void 0 : interpolateTemplate(step.pageUrl, variables);
29189
29575
  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;
29190
29576
  }
29191
- scheduleBackgroundNetworkSaveByRequestIds(requestIds, tag) {
29192
- const task = (async () => {
29193
- const root = await this.ensureRoot();
29194
- const requestIdSet = new Set(requestIds);
29195
- const records = await this.readLiveNetworkRecords(
29196
- {
29197
- includeBodies: true,
29198
- includeCurrentPageOnly: false,
29199
- ...this.pageRef === void 0 ? {} : { pageRef: this.pageRef },
29200
- requestIds,
29201
- redactSecretHeaders: false
29202
- },
29203
- new AbortController().signal
29204
- );
29205
- const filtered = records.filter((record) => requestIdSet.has(record.record.requestId));
29206
- if (filtered.length === 0) {
29207
- return;
29208
- }
29209
- await root.registry.savedNetwork.save(filtered, tag);
29210
- })();
29211
- this.backgroundNetworkPersistence.add(task);
29212
- task.finally(() => {
29213
- this.backgroundNetworkPersistence.delete(task);
29214
- });
29215
- void task.catch(() => void 0);
29216
- }
29217
- async flushBackgroundNetworkPersistence() {
29218
- if (this.backgroundNetworkPersistence.size === 0) {
29219
- return;
29220
- }
29221
- await Promise.all([...this.backgroundNetworkPersistence]);
29577
+ async flushPersistedNetworkHistory() {
29222
29578
  }
29223
29579
  toDomTargetRef(target) {
29224
29580
  if (target.kind === "description") {
@@ -29253,7 +29609,9 @@ var OpensteerSessionRuntime = class {
29253
29609
  ...workspace.registry,
29254
29610
  ...overrides.requestPlans === void 0 ? {} : { requestPlans: overrides.requestPlans },
29255
29611
  ...overrides.authRecipes === void 0 ? {} : { authRecipes: overrides.authRecipes },
29256
- ...overrides.recipes === void 0 ? {} : { recipes: overrides.recipes }
29612
+ ...overrides.recipes === void 0 ? {} : { recipes: overrides.recipes },
29613
+ ...overrides.reverseCases === void 0 ? {} : { reverseCases: overrides.reverseCases },
29614
+ ...overrides.reversePackages === void 0 ? {} : { reversePackages: overrides.reversePackages }
29257
29615
  }
29258
29616
  };
29259
29617
  } else {
@@ -29510,8 +29868,7 @@ var OpensteerSessionRuntime = class {
29510
29868
  }
29511
29869
  async resetRuntimeState(options) {
29512
29870
  const engine = this.engine;
29513
- this.networkJournal.clear();
29514
- this.backgroundNetworkPersistence.clear();
29871
+ this.networkHistory.clear();
29515
29872
  this.sessionRef = void 0;
29516
29873
  this.pageRef = void 0;
29517
29874
  this.runId = void 0;
@@ -29587,10 +29944,10 @@ function buildEngineNetworkRecordFilters(input) {
29587
29944
  ...input.resourceType === void 0 ? {} : { resourceType: input.resourceType }
29588
29945
  };
29589
29946
  }
29590
- function resolveLiveQueryRequestIds(input, journal) {
29947
+ function resolveLiveQueryRequestIds(input, history) {
29591
29948
  const requestIdCandidates = [];
29592
29949
  if (input.recordId !== void 0) {
29593
- const requestId = journal.getRequestId(input.recordId);
29950
+ const requestId = history.getRequestId(input.recordId);
29594
29951
  if (requestId === void 0) {
29595
29952
  return [];
29596
29953
  }
@@ -29599,25 +29956,25 @@ function resolveLiveQueryRequestIds(input, journal) {
29599
29956
  if (input.requestId !== void 0) {
29600
29957
  requestIdCandidates.push(/* @__PURE__ */ new Set([input.requestId]));
29601
29958
  }
29602
- if (input.actionId !== void 0) {
29603
- requestIdCandidates.push(journal.getRequestIdsForActionId(input.actionId));
29959
+ if (input.capture !== void 0) {
29960
+ requestIdCandidates.push(history.getRequestIdsForCapture(input.capture));
29604
29961
  }
29605
29962
  if (input.tag !== void 0) {
29606
- requestIdCandidates.push(journal.getRequestIdsForTag(input.tag));
29963
+ requestIdCandidates.push(history.getRequestIdsForTag(input.tag));
29607
29964
  }
29608
29965
  if (requestIdCandidates.length === 0) {
29609
29966
  return void 0;
29610
29967
  }
29611
29968
  return intersectRequestIdSets(requestIdCandidates);
29612
29969
  }
29613
- function resolveLiveQueryPageRef(input, currentPageRef, requestIds, journal) {
29970
+ function resolveLiveQueryPageRef(input, currentPageRef, requestIds, history) {
29614
29971
  const requestedPageRef = selectLiveQueryPageRef(input, currentPageRef);
29615
29972
  if (requestedPageRef !== void 0 || requestIds === void 0) {
29616
29973
  return requestedPageRef;
29617
29974
  }
29618
29975
  const pageRefs = /* @__PURE__ */ new Set();
29619
29976
  for (const requestId of requestIds) {
29620
- const pageRef = journal.getPageRefForRequestId(requestId);
29977
+ const pageRef = history.getPageRefForRequestId(requestId);
29621
29978
  if (pageRef === void 0) {
29622
29979
  continue;
29623
29980
  }
@@ -29647,7 +30004,7 @@ function filterNetworkQueryRecords(records, input) {
29647
30004
  if (input.requestId !== void 0 && record.record.requestId !== input.requestId) {
29648
30005
  return false;
29649
30006
  }
29650
- if (input.actionId !== void 0 && record.actionId !== input.actionId) {
30007
+ if (input.capture !== void 0 && record.capture !== input.capture) {
29651
30008
  return false;
29652
30009
  }
29653
30010
  if (input.tag !== void 0 && !(record.tags ?? []).includes(input.tag)) {
@@ -29659,10 +30016,10 @@ function filterNetworkQueryRecords(records, input) {
29659
30016
  return true;
29660
30017
  });
29661
30018
  }
29662
- function sortLiveNetworkRecords(records, journal) {
30019
+ function sortLiveNetworkRecords(records, history) {
29663
30020
  return [...records].sort((left, right) => {
29664
- const leftObservedAt = journal.getObservedAt(left.recordId) ?? 0;
29665
- const rightObservedAt = journal.getObservedAt(right.recordId) ?? 0;
30021
+ const leftObservedAt = history.getObservedAt(left.recordId) ?? 0;
30022
+ const rightObservedAt = history.getObservedAt(right.recordId) ?? 0;
29666
30023
  if (leftObservedAt !== rightObservedAt) {
29667
30024
  return rightObservedAt - leftObservedAt;
29668
30025
  }
@@ -29869,7 +30226,7 @@ function buildMinimizedRequestPlan(input) {
29869
30226
  provenance: {
29870
30227
  source: "network-minimize",
29871
30228
  sourceId: input.record.recordId,
29872
- ...input.record.source === "saved" && input.record.savedAt !== void 0 ? { capturedAt: input.record.savedAt } : {}
30229
+ ...input.record.savedAt === void 0 ? {} : { capturedAt: input.record.savedAt }
29873
30230
  },
29874
30231
  payload: normalizeRequestPlanPayload({
29875
30232
  transport: {
@@ -30088,12 +30445,12 @@ function originFromUrl(url) {
30088
30445
  return void 0;
30089
30446
  }
30090
30447
  }
30091
- function filterReverseObservationWindow(records, journal, captureWindowMs) {
30448
+ function filterReverseObservationWindow(records, history, captureWindowMs) {
30092
30449
  if (captureWindowMs === void 0) {
30093
30450
  return records;
30094
30451
  }
30095
30452
  const observedAfter = Date.now() - captureWindowMs;
30096
- return records.filter((record) => (journal.getObservedAt(record.recordId) ?? 0) >= observedAfter);
30453
+ return records.filter((record) => (history.getObservedAt(record.recordId) ?? 0) >= observedAfter);
30097
30454
  }
30098
30455
  function isReverseRelevantNetworkRecord(record) {
30099
30456
  return record.record.resourceType === "document" || record.record.resourceType === "fetch" || record.record.resourceType === "xhr" || record.record.resourceType === "websocket" || record.record.resourceType === "event-stream";
@@ -32057,9 +32414,40 @@ function toOpensteerResolvedTarget2(target) {
32057
32414
  function normalizeOpensteerError(error) {
32058
32415
  return normalizeThrownOpensteerError(error, "Unknown Opensteer runtime failure");
32059
32416
  }
32417
+ function buildMutationCaptureTraceData(diagnostics) {
32418
+ if (diagnostics?.finalizeError === void 0) {
32419
+ return {};
32420
+ }
32421
+ return {
32422
+ networkCapture: {
32423
+ finalizeError: diagnostics.finalizeError
32424
+ }
32425
+ };
32426
+ }
32060
32427
  function isIgnorableRuntimeBindingError(error) {
32061
32428
  return isBrowserCoreError(error) && (error.code === "not-found" || error.code === "page-closed" || error.code === "session-closed");
32062
32429
  }
32430
+ async function withDetachedTimeoutSignal(timeoutMs, operation) {
32431
+ const controller = new AbortController();
32432
+ const timeoutError = new OpensteerProtocolError(
32433
+ "timeout",
32434
+ `mutation capture finalization exceeded ${String(timeoutMs)}ms timeout`,
32435
+ {
32436
+ details: {
32437
+ policy: "mutation-capture-finalize",
32438
+ budgetMs: timeoutMs
32439
+ }
32440
+ }
32441
+ );
32442
+ const timer = setTimeout(() => {
32443
+ controller.abort(timeoutError);
32444
+ }, timeoutMs);
32445
+ try {
32446
+ return await operation(controller.signal);
32447
+ } finally {
32448
+ clearTimeout(timer);
32449
+ }
32450
+ }
32063
32451
  function screenshotMediaType(format2) {
32064
32452
  switch (format2) {
32065
32453
  case "png":
@@ -32493,11 +32881,13 @@ function asRecord(value) {
32493
32881
  var REGISTRY_SYNC_MAX_PAYLOAD_BYTES = 15e5;
32494
32882
  var REGISTRY_SYNC_MAX_ENTRIES_PER_BATCH = 100;
32495
32883
  async function syncLocalRegistryToCloud(client, workspace, store) {
32496
- const [descriptors, requestPlans, recipes, authRecipes] = await Promise.all([
32884
+ const [descriptors, requestPlans, recipes, authRecipes, reverseCases, reversePackages] = await Promise.all([
32497
32885
  store.registry.descriptors.list(),
32498
32886
  store.registry.requestPlans.list(),
32499
32887
  store.registry.recipes.list(),
32500
- store.registry.authRecipes.list()
32888
+ store.registry.authRecipes.list(),
32889
+ store.registry.reverseCases.list(),
32890
+ store.registry.reversePackages.list()
32501
32891
  ]);
32502
32892
  const descriptorEntries = descriptors.map((record) => toDescriptorImportEntry(workspace, record));
32503
32893
  await Promise.all([
@@ -32513,6 +32903,14 @@ async function syncLocalRegistryToCloud(client, workspace, store) {
32513
32903
  importInBatches(
32514
32904
  authRecipes.map((record) => toRegistryImportEntry(workspace, record)),
32515
32905
  (entries) => client.importAuthRecipes(entries)
32906
+ ),
32907
+ importInBatches(
32908
+ reverseCases.map((record) => toRegistryImportEntry(workspace, record)),
32909
+ (entries) => client.importReverseCases(entries)
32910
+ ),
32911
+ importInBatches(
32912
+ reversePackages.map((record) => toRegistryImportEntry(workspace, record)),
32913
+ (entries) => client.importReversePackages(entries)
32516
32914
  )
32517
32915
  ]);
32518
32916
  }
@@ -32722,9 +33120,9 @@ var CloudSessionProxy = class {
32722
33120
  await this.ensureSession();
32723
33121
  return this.requireClient().invoke("network.query", input);
32724
33122
  }
32725
- async saveNetwork(input) {
33123
+ async tagNetwork(input) {
32726
33124
  await this.ensureSession();
32727
- return this.requireClient().invoke("network.save", input);
33125
+ return this.requireClient().invoke("network.tag", input);
32728
33126
  }
32729
33127
  async minimizeNetwork(input) {
32730
33128
  await this.ensureSession();
@@ -33076,6 +33474,7 @@ var OpensteerRuntime = class extends OpensteerSessionRuntime {
33076
33474
  ...options.policy === void 0 ? {} : { policy: options.policy },
33077
33475
  ...options.descriptorStore === void 0 ? {} : { descriptorStore: options.descriptorStore },
33078
33476
  ...options.extractionDescriptorStore === void 0 ? {} : { extractionDescriptorStore: options.extractionDescriptorStore },
33477
+ ...options.registryOverrides === void 0 ? {} : { registryOverrides: options.registryOverrides },
33079
33478
  cleanupRootOnClose
33080
33479
  })
33081
33480
  );
@@ -33100,6 +33499,7 @@ function buildSharedRuntimeOptions(input) {
33100
33499
  ...input.policy === void 0 ? {} : { policy: input.policy },
33101
33500
  ...input.descriptorStore === void 0 ? {} : { descriptorStore: input.descriptorStore },
33102
33501
  ...input.extractionDescriptorStore === void 0 ? {} : { extractionDescriptorStore: input.extractionDescriptorStore },
33502
+ ...input.registryOverrides === void 0 ? {} : { registryOverrides: input.registryOverrides },
33103
33503
  cleanupRootOnClose: input.cleanupRootOnClose,
33104
33504
  sessionInfo: {
33105
33505
  provider: {
@@ -33328,7 +33728,7 @@ var OPERATION_ALIASES = /* @__PURE__ */ new Map([
33328
33728
  ["scroll", "dom.scroll"],
33329
33729
  ["extract", "dom.extract"],
33330
33730
  ["network query", "network.query"],
33331
- ["network save", "network.save"],
33731
+ ["network tag", "network.tag"],
33332
33732
  ["network clear", "network.clear"],
33333
33733
  ["network minimize", "network.minimize"],
33334
33734
  ["network diff", "network.diff"],