opensteer 0.8.17 → 0.9.0

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.
@@ -2913,6 +2913,16 @@ var opensteerNetworkQueryOutputSchema = objectSchema(
2913
2913
  required: ["records"]
2914
2914
  }
2915
2915
  );
2916
+ var opensteerNetworkDetailInputSchema = objectSchema(
2917
+ {
2918
+ recordId: stringSchema({ minLength: 1 }),
2919
+ probe: { type: "boolean" }
2920
+ },
2921
+ {
2922
+ title: "OpensteerNetworkDetailInput",
2923
+ required: ["recordId"]
2924
+ }
2925
+ );
2916
2926
  var opensteerParsedCookieSchema = objectSchema(
2917
2927
  {
2918
2928
  name: stringSchema({ minLength: 1 }),
@@ -3018,7 +3028,7 @@ objectSchema(
3018
3028
  }
3019
3029
  );
3020
3030
  var opensteerSessionFetchTransportSchema = enumSchema(
3021
- ["auto", "direct", "matched-tls", "page"],
3031
+ ["auto", "direct", "matched-tls", "context", "page"],
3022
3032
  {
3023
3033
  title: "OpensteerSessionFetchTransport"
3024
3034
  }
@@ -6802,15 +6812,6 @@ var opensteerPageSnapshotOutputSchema = objectSchema(
6802
6812
  required: ["url", "title", "mode", "html", "counters"]
6803
6813
  }
6804
6814
  );
6805
- var opensteerNetworkDetailInputSchema = objectSchema(
6806
- {
6807
- recordId: stringSchema({ minLength: 1 })
6808
- },
6809
- {
6810
- title: "OpensteerNetworkDetailInput",
6811
- required: ["recordId"]
6812
- }
6813
- );
6814
6815
  var opensteerComputerMouseButtonSchema = enumSchema(
6815
6816
  ["left", "middle", "right"],
6816
6817
  {
@@ -7380,6 +7381,7 @@ var opensteerSemanticOperationSpecificationsBase = [
7380
7381
  case "direct":
7381
7382
  return [];
7382
7383
  case "matched-tls":
7384
+ case "context":
7383
7385
  return ["inspect.cookies"];
7384
7386
  case "page":
7385
7387
  return ["pages.manage"];
@@ -7991,13 +7993,6 @@ var SqliteSavedNetworkStore = class {
7991
7993
  }
7992
7994
  async save(records, options) {
7993
7995
  const database = await this.requireDatabase();
7994
- const readExisting = database.prepare(`
7995
- SELECT record_id
7996
- FROM saved_network_records
7997
- WHERE session_ref = @session_ref
7998
- AND page_ref_key = @page_ref_key
7999
- AND request_id = @request_id
8000
- `);
8001
7996
  const upsertRecord = database.prepare(buildSavedNetworkUpsertSql(options.bodyWriteMode));
8002
7997
  const insertTag = database.prepare(`
8003
7998
  INSERT OR IGNORE INTO saved_network_tags (record_id, tag)
@@ -8008,14 +8003,8 @@ var SqliteSavedNetworkStore = class {
8008
8003
  for (const entry of records) {
8009
8004
  const url = new URL(entry.record.url);
8010
8005
  const pageRefKey = entry.record.pageRef ?? "";
8011
- const existing = readExisting.get({
8012
- session_ref: entry.record.sessionRef,
8013
- page_ref_key: pageRefKey,
8014
- request_id: entry.record.requestId
8015
- }) ?? void 0;
8016
- const recordId = existing?.record_id ?? entry.recordId;
8017
8006
  upsertRecord.run({
8018
- record_id: recordId,
8007
+ record_id: entry.recordId,
8019
8008
  request_id: entry.record.requestId,
8020
8009
  session_ref: entry.record.sessionRef,
8021
8010
  page_ref: entry.record.pageRef ?? null,
@@ -8060,7 +8049,7 @@ var SqliteSavedNetworkStore = class {
8060
8049
  }
8061
8050
  for (const currentTag of tags) {
8062
8051
  const result = insertTag.run({
8063
- record_id: recordId,
8052
+ record_id: entry.recordId,
8064
8053
  tag: currentTag
8065
8054
  });
8066
8055
  savedCount += result.changes ?? 0;
@@ -8163,6 +8152,49 @@ var SqliteSavedNetworkStore = class {
8163
8152
  return cleared;
8164
8153
  });
8165
8154
  }
8155
+ async *iterateBatches(options = {}) {
8156
+ const database = await this.requireDatabase();
8157
+ const batchSize = Math.max(1, Math.min(options.batchSize ?? 500, 1e3));
8158
+ let cursor;
8159
+ while (true) {
8160
+ const rows = database.prepare(
8161
+ `
8162
+ SELECT
8163
+ r.*,
8164
+ GROUP_CONCAT(t.tag, '${TAG_DELIMITER}') AS tags
8165
+ FROM saved_network_records r
8166
+ LEFT JOIN saved_network_tags t
8167
+ ON t.record_id = r.record_id
8168
+ ${cursor === void 0 ? "" : "WHERE r.saved_at > ? OR (r.saved_at = ? AND r.record_id > ?)"}
8169
+ GROUP BY r.record_id
8170
+ ORDER BY r.saved_at ASC, r.record_id ASC
8171
+ LIMIT ?
8172
+ `
8173
+ ).all(
8174
+ ...cursor === void 0 ? [] : [cursor.savedAt, cursor.savedAt, cursor.recordId],
8175
+ batchSize
8176
+ );
8177
+ if (rows.length === 0) {
8178
+ return;
8179
+ }
8180
+ yield rows.map((row) => inflateSavedNetworkRow(row, options.includeBodies ?? true));
8181
+ const lastRow = rows.at(-1);
8182
+ if (lastRow === void 0) {
8183
+ return;
8184
+ }
8185
+ cursor = {
8186
+ savedAt: lastRow.saved_at,
8187
+ recordId: lastRow.record_id
8188
+ };
8189
+ }
8190
+ }
8191
+ close() {
8192
+ if (this.database !== void 0) {
8193
+ closeSqliteDatabase(this.database);
8194
+ this.database = void 0;
8195
+ this.databaseInitialization = void 0;
8196
+ }
8197
+ }
8166
8198
  async requireDatabase() {
8167
8199
  if (this.database) {
8168
8200
  return this.database;
@@ -8247,15 +8279,6 @@ var SqliteSavedNetworkStore = class {
8247
8279
  saved_at INTEGER NOT NULL
8248
8280
  );
8249
8281
 
8250
- CREATE UNIQUE INDEX IF NOT EXISTS saved_network_records_scope_request
8251
- ON saved_network_records (session_ref, page_ref_key, request_id);
8252
-
8253
- CREATE INDEX IF NOT EXISTS saved_network_records_saved_at
8254
- ON saved_network_records (saved_at DESC);
8255
-
8256
- CREATE INDEX IF NOT EXISTS saved_network_records_capture
8257
- ON saved_network_records (capture);
8258
-
8259
8282
  CREATE TABLE IF NOT EXISTS saved_network_tags (
8260
8283
  record_id TEXT NOT NULL REFERENCES saved_network_records(record_id) ON DELETE CASCADE,
8261
8284
  tag TEXT NOT NULL,
@@ -8265,6 +8288,19 @@ var SqliteSavedNetworkStore = class {
8265
8288
  CREATE INDEX IF NOT EXISTS saved_network_tags_tag
8266
8289
  ON saved_network_tags (tag);
8267
8290
  `);
8291
+ database.exec(`DROP INDEX IF EXISTS saved_network_records_scope_request`);
8292
+ database.exec(`
8293
+ CREATE INDEX IF NOT EXISTS saved_network_records_scope_request
8294
+ ON saved_network_records (session_ref, page_ref_key, request_id)
8295
+ `);
8296
+ database.exec(`
8297
+ CREATE INDEX IF NOT EXISTS saved_network_records_saved_at
8298
+ ON saved_network_records (saved_at DESC)
8299
+ `);
8300
+ database.exec(`
8301
+ CREATE INDEX IF NOT EXISTS saved_network_records_capture
8302
+ ON saved_network_records (capture)
8303
+ `);
8268
8304
  this.ensureColumn(
8269
8305
  database,
8270
8306
  "saved_network_records",
@@ -8912,7 +8948,6 @@ var DEFAULT_TIMEOUTS = {
8912
8948
  "dom.extract": 15e3,
8913
8949
  "network.query": 15e3,
8914
8950
  "network.detail": 15e3,
8915
- "network.replay": 3e4,
8916
8951
  "scripts.capture": 15e3,
8917
8952
  "session.cookies": 1e4,
8918
8953
  "session.storage": 1e4,
@@ -15945,8 +15980,14 @@ async function launchOwnedBrowser(input) {
15945
15980
  await ensureDirectory(input.userDataDir);
15946
15981
  await clearChromeSingletonEntries(input.userDataDir);
15947
15982
  await sanitizeChromeProfile(input.userDataDir);
15983
+ const requestedRemoteDebuggingPort = readRequestedRemoteDebuggingPort(input.launch?.args);
15948
15984
  const executablePath = resolveChromeExecutablePath(input.launch?.executablePath);
15949
- const args = buildChromeArgs(input.userDataDir, input.launch, input.viewport);
15985
+ const args = buildChromeArgs(
15986
+ input.userDataDir,
15987
+ input.launch,
15988
+ input.viewport,
15989
+ requestedRemoteDebuggingPort
15990
+ );
15950
15991
  const child = spawn(executablePath, args, {
15951
15992
  stdio: ["ignore", "ignore", "pipe"],
15952
15993
  detached: process.platform !== "win32"
@@ -15962,7 +16003,8 @@ async function launchOwnedBrowser(input) {
15962
16003
  userDataDir: input.userDataDir,
15963
16004
  timeoutMs: input.launch?.timeoutMs ?? DEFAULT_TIMEOUT_MS,
15964
16005
  childExited: async () => child.exitCode,
15965
- stderrLines
16006
+ stderrLines,
16007
+ ...requestedRemoteDebuggingPort !== void 0 && requestedRemoteDebuggingPort > 0 ? { requestedRemoteDebuggingPort } : {}
15966
16008
  }).catch(async (error) => {
15967
16009
  child.kill("SIGKILL");
15968
16010
  throw error;
@@ -15973,10 +16015,10 @@ async function launchOwnedBrowser(input) {
15973
16015
  executablePath
15974
16016
  };
15975
16017
  }
15976
- function buildChromeArgs(userDataDir, launch, viewport) {
16018
+ function buildChromeArgs(userDataDir, launch, viewport, requestedRemoteDebuggingPort) {
15977
16019
  const isHeadless = launch?.headless ?? true;
15978
16020
  const args = [
15979
- "--remote-debugging-port=0",
16021
+ ...requestedRemoteDebuggingPort === void 0 ? ["--remote-debugging-port=0"] : [],
15980
16022
  "--no-first-run",
15981
16023
  "--no-default-browser-check",
15982
16024
  "--disable-blink-features=AutomationControlled",
@@ -16027,6 +16069,15 @@ async function waitForDevToolsEndpoint(input) {
16027
16069
  return `ws://127.0.0.1:${String(activePort.port)}${activePort.webSocketPath}`;
16028
16070
  }
16029
16071
  }
16072
+ if (input.requestedRemoteDebuggingPort !== void 0) {
16073
+ const endpoint = await tryInspectRemoteDebuggingPort(
16074
+ input.requestedRemoteDebuggingPort,
16075
+ input.timeoutMs
16076
+ );
16077
+ if (endpoint !== void 0) {
16078
+ return endpoint;
16079
+ }
16080
+ }
16030
16081
  const exitCode = await input.childExited();
16031
16082
  if (exitCode !== null) {
16032
16083
  throw new Error(formatChromeLaunchError(input.stderrLines));
@@ -16035,6 +16086,52 @@ async function waitForDevToolsEndpoint(input) {
16035
16086
  }
16036
16087
  throw new Error(formatChromeLaunchError(input.stderrLines));
16037
16088
  }
16089
+ function readRequestedRemoteDebuggingPort(args) {
16090
+ if (args === void 0 || args.length === 0) {
16091
+ return void 0;
16092
+ }
16093
+ let explicitFlagFound = false;
16094
+ let port;
16095
+ for (let index = 0; index < args.length; index += 1) {
16096
+ const entry = args[index];
16097
+ if (entry === "--remote-debugging-port") {
16098
+ explicitFlagFound = true;
16099
+ const next = args[index + 1];
16100
+ if (next !== void 0) {
16101
+ port = parseRemoteDebuggingPort(next);
16102
+ index += 1;
16103
+ }
16104
+ continue;
16105
+ }
16106
+ if (entry.startsWith("--remote-debugging-port=")) {
16107
+ explicitFlagFound = true;
16108
+ port = parseRemoteDebuggingPort(entry.slice("--remote-debugging-port=".length));
16109
+ }
16110
+ }
16111
+ return explicitFlagFound ? port : void 0;
16112
+ }
16113
+ function parseRemoteDebuggingPort(value) {
16114
+ const trimmed = value.trim();
16115
+ if (!/^\d+$/.test(trimmed)) {
16116
+ return void 0;
16117
+ }
16118
+ const parsed = Number.parseInt(trimmed, 10);
16119
+ if (!Number.isInteger(parsed) || parsed < 0) {
16120
+ return void 0;
16121
+ }
16122
+ return parsed;
16123
+ }
16124
+ async function tryInspectRemoteDebuggingPort(port, timeoutMs) {
16125
+ try {
16126
+ const inspected = await inspectCdpEndpoint({
16127
+ endpoint: `http://127.0.0.1:${String(port)}`,
16128
+ timeoutMs: Math.min(2e3, timeoutMs)
16129
+ });
16130
+ return inspected.endpoint;
16131
+ } catch {
16132
+ return void 0;
16133
+ }
16134
+ }
16038
16135
  function formatChromeLaunchError(stderrLines) {
16039
16136
  const collapsed = stderrLines.join("").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
16040
16137
  if (collapsed.length === 0) {
@@ -16765,21 +16862,24 @@ var OpensteerCloudClient = class {
16765
16862
  getConfig() {
16766
16863
  return this.config;
16767
16864
  }
16768
- async createSession(input = {}) {
16769
- const response = await this.request("/v1/sessions", {
16770
- method: "POST",
16771
- body: {
16772
- ...input.name === void 0 ? {} : { name: input.name },
16773
- ...input.browser === void 0 ? {} : { browser: input.browser },
16774
- ...input.context === void 0 ? {} : { context: input.context },
16775
- ...input.browserProfile === void 0 ? {} : { browserProfile: input.browserProfile },
16776
- ...input.observability === void 0 ? {} : { observability: input.observability },
16777
- ...input.sourceType === void 0 ? {} : { sourceType: input.sourceType },
16778
- ...input.sourceRef === void 0 ? {} : { sourceRef: input.sourceRef },
16779
- ...input.localWorkspaceRootPath === void 0 ? {} : { localWorkspaceRootPath: input.localWorkspaceRootPath },
16780
- ...input.locality === void 0 ? {} : { locality: input.locality }
16781
- }
16782
- });
16865
+ async createSession(input = {}, options = {}) {
16866
+ const response = await this.request(
16867
+ "/v1/sessions",
16868
+ {
16869
+ method: "POST",
16870
+ body: {
16871
+ ...input.name === void 0 ? {} : { name: input.name },
16872
+ ...input.browser === void 0 ? {} : { browser: input.browser },
16873
+ ...input.context === void 0 ? {} : { context: input.context },
16874
+ ...input.browserProfile === void 0 ? {} : { browserProfile: input.browserProfile },
16875
+ ...input.observability === void 0 ? {} : { observability: input.observability },
16876
+ ...input.sourceType === void 0 ? {} : { sourceType: input.sourceType },
16877
+ ...input.sourceRef === void 0 ? {} : { sourceRef: input.sourceRef },
16878
+ ...input.localWorkspaceRootPath === void 0 ? {} : { localWorkspaceRootPath: input.localWorkspaceRootPath }
16879
+ }
16880
+ },
16881
+ options
16882
+ );
16783
16883
  return await response.json();
16784
16884
  }
16785
16885
  async listSessions() {
@@ -16788,19 +16888,27 @@ var OpensteerCloudClient = class {
16788
16888
  });
16789
16889
  return response.json();
16790
16890
  }
16791
- async getSession(sessionId) {
16792
- const response = await this.request(`/v1/sessions/${encodeURIComponent(sessionId)}`, {
16793
- method: "GET"
16794
- });
16891
+ async getSession(sessionId, options = {}) {
16892
+ const response = await this.request(
16893
+ `/v1/sessions/${encodeURIComponent(sessionId)}`,
16894
+ {
16895
+ method: "GET"
16896
+ },
16897
+ options
16898
+ );
16795
16899
  return await response.json();
16796
16900
  }
16797
- async issueAccess(sessionId, capabilities) {
16798
- const response = await this.request(`/v1/sessions/${encodeURIComponent(sessionId)}/access`, {
16799
- method: "POST",
16800
- body: {
16801
- capabilities
16802
- }
16803
- });
16901
+ async issueAccess(sessionId, capabilities, options = {}) {
16902
+ const response = await this.request(
16903
+ `/v1/sessions/${encodeURIComponent(sessionId)}/access`,
16904
+ {
16905
+ method: "POST",
16906
+ body: {
16907
+ capabilities
16908
+ }
16909
+ },
16910
+ options
16911
+ );
16804
16912
  return await response.json();
16805
16913
  }
16806
16914
  async getSessionRecording(sessionId) {
@@ -16882,28 +16990,17 @@ var OpensteerCloudClient = class {
16882
16990
  async syncBrowserProfileCookies(input) {
16883
16991
  return syncBrowserProfileCookies(this, input);
16884
16992
  }
16885
- async importSelectorCache(entries) {
16886
- const response = await this.request("/selector-cache/import", {
16993
+ async importDescriptors(entries) {
16994
+ const response = await this.request("/registry/descriptors/import", {
16887
16995
  method: "POST",
16888
- body: {
16889
- entries: entries.map((entry) => ({
16890
- workspace: entry.workspace,
16891
- method: entry.method,
16892
- persistHash: entry.persistHash,
16893
- ...entry.persist === void 0 ? {} : { persist: entry.persist },
16894
- path: entry.path,
16895
- ...entry.schemaHash === void 0 ? {} : { schemaHash: entry.schemaHash },
16896
- createdAt: entry.createdAt,
16897
- updatedAt: entry.updatedAt
16898
- }))
16899
- }
16996
+ body: { entries }
16900
16997
  });
16901
16998
  return await response.json();
16902
16999
  }
16903
- async importDescriptors(entries) {
16904
- const response = await this.request("/registry/descriptors/import", {
17000
+ async importRequestPlans(input) {
17001
+ const response = await this.request("/registry/request-plans/import", {
16905
17002
  method: "POST",
16906
- body: { entries }
17003
+ body: input
16907
17004
  });
16908
17005
  return await response.json();
16909
17006
  }
@@ -16916,7 +17013,7 @@ var OpensteerCloudClient = class {
16916
17013
  "content-type": "application/json; charset=utf-8"
16917
17014
  };
16918
17015
  }
16919
- async request(pathname, init) {
17016
+ async request(pathname, init, options = {}) {
16920
17017
  const url = `${this.config.baseUrl}${pathname}`;
16921
17018
  let response;
16922
17019
  try {
@@ -16924,7 +17021,7 @@ var OpensteerCloudClient = class {
16924
17021
  method: init.method,
16925
17022
  headers: this.buildHeaders(),
16926
17023
  ...init.body === void 0 ? {} : { body: JSON.stringify(init.body) },
16927
- signal: AbortSignal.timeout(3e4)
17024
+ signal: createRequestSignal(options)
16928
17025
  });
16929
17026
  } catch (error) {
16930
17027
  throw wrapCloudFetchError(error, {
@@ -16954,6 +17051,13 @@ var OpensteerCloudClient = class {
16954
17051
  throw new Error(`Timed out waiting for cloud session ${sessionId} to close.`);
16955
17052
  }
16956
17053
  };
17054
+ function createRequestSignal(options) {
17055
+ const timeoutSignal = AbortSignal.timeout(options.timeoutMs ?? 3e4);
17056
+ if (options.signal === void 0) {
17057
+ return timeoutSignal;
17058
+ }
17059
+ return AbortSignal.any([options.signal, timeoutSignal]);
17060
+ }
16957
17061
  function delay(ms) {
16958
17062
  return new Promise((resolve4) => {
16959
17063
  setTimeout(resolve4, ms);
@@ -17149,7 +17253,7 @@ async function dispatchSemanticOperation(runtime, operation, input, options = {}
17149
17253
 
17150
17254
  // ../runtime-core/package.json
17151
17255
  var package_default = {
17152
- version: "0.1.7"};
17256
+ version: "0.2.0"};
17153
17257
 
17154
17258
  // ../runtime-core/src/version.ts
17155
17259
  var OPENSTEER_RUNTIME_CORE_VERSION = package_default.version;
@@ -20967,6 +21071,9 @@ var MUTATION_CAPTURE_FINALIZE_TIMEOUT_MS = 5e3;
20967
21071
  var PERSISTED_NETWORK_FLUSH_TIMEOUT_MS = 5e3;
20968
21072
  var PENDING_OPERATION_EVENT_CAPTURE_LIMIT = 64;
20969
21073
  var PENDING_OPERATION_EVENT_CAPTURE_SKEW_MS = 1e3;
21074
+ var REPLAY_PROBE_MIN_ATTEMPT_TIMEOUT_MS = 3e3;
21075
+ var REPLAY_PROBE_MAX_ATTEMPT_TIMEOUT_MS = 15e3;
21076
+ var REPLAY_PROBE_POST_SUCCESS_ATTEMPT_TIMEOUT_MS = 5e3;
20970
21077
  var OpensteerSessionRuntime = class {
20971
21078
  workspace;
20972
21079
  rootPath;
@@ -21828,26 +21935,27 @@ var OpensteerSessionRuntime = class {
21828
21935
  }
21829
21936
  }
21830
21937
  async queryNetwork(input = {}, options = {}) {
21831
- assertValidSemanticOperationInput("network.query", input);
21938
+ const normalizedInput = normalizeNetworkQueryInput(input);
21939
+ assertValidSemanticOperationInput("network.query", normalizedInput);
21832
21940
  const root = await this.ensureRoot();
21833
21941
  const startedAt = Date.now();
21834
21942
  try {
21835
21943
  const output = await this.runWithOperationTimeout(
21836
21944
  "network.query",
21837
21945
  async (timeout) => {
21838
- await this.syncPersistedNetworkSelection(timeout, input, {
21946
+ await this.syncPersistedNetworkSelection(timeout, normalizedInput, {
21839
21947
  includeBodies: false
21840
21948
  });
21841
21949
  const rawRecords = await timeout.runStep(
21842
21950
  () => root.registry.savedNetwork.query({
21843
- ...this.toSavedNetworkQueryInput(input),
21844
- limit: Math.max(input.limit ?? 50, 1e3)
21951
+ ...this.toSavedNetworkQueryInput(normalizedInput),
21952
+ limit: Math.max(normalizedInput.limit ?? 50, 1e3)
21845
21953
  })
21846
21954
  );
21847
- const filtered = filterNetworkSummaryRecords(rawRecords, input);
21955
+ const filtered = filterNetworkSummaryRecords(rawRecords, normalizedInput);
21848
21956
  const sorted = sortPersistedNetworkRecordsChronologically(filtered);
21849
- const sliced = sliceNetworkSummaryWindow(sorted, input);
21850
- const limited = sliced.slice(0, Math.max(1, Math.min(input.limit ?? 50, 200)));
21957
+ const sliced = sliceNetworkSummaryWindow(sorted, normalizedInput);
21958
+ const limited = sliced.slice(0, Math.max(1, Math.min(normalizedInput.limit ?? 50, 200)));
21851
21959
  const summaries = await this.buildNetworkSummaryRecords(limited, timeout);
21852
21960
  return {
21853
21961
  records: summaries
@@ -21861,9 +21969,9 @@ var OpensteerSessionRuntime = class {
21861
21969
  completedAt: Date.now(),
21862
21970
  outcome: "ok",
21863
21971
  data: {
21864
- limit: input.limit ?? 50,
21865
- ...input.capture === void 0 ? {} : { capture: input.capture },
21866
- ...input.json === true ? { json: true } : {},
21972
+ limit: normalizedInput.limit ?? 50,
21973
+ ...normalizedInput.capture === void 0 ? {} : { capture: normalizedInput.capture },
21974
+ ...normalizedInput.json === true ? { json: true } : {},
21867
21975
  count: output.records.length
21868
21976
  },
21869
21977
  context: buildRuntimeTraceContext({
@@ -21888,12 +21996,13 @@ var OpensteerSessionRuntime = class {
21888
21996
  }
21889
21997
  }
21890
21998
  async getNetworkDetail(input, options = {}) {
21999
+ const normalizedRecordId = normalizeNetworkRecordId(input.recordId);
21891
22000
  const startedAt = Date.now();
21892
22001
  try {
21893
22002
  const output = await this.runWithOperationTimeout(
21894
22003
  "network.detail",
21895
22004
  async (timeout) => {
21896
- const record = await this.resolveNetworkRecordByRecordId(input.recordId, timeout, {
22005
+ const record = await this.resolveNetworkRecordByRecordId(normalizedRecordId, timeout, {
21897
22006
  includeBodies: true,
21898
22007
  redactSecretHeaders: false
21899
22008
  });
@@ -21912,8 +22021,8 @@ var OpensteerSessionRuntime = class {
21912
22021
  completedAt: Date.now(),
21913
22022
  outcome: "ok",
21914
22023
  data: {
21915
- recordId: input.recordId,
21916
- status: output.summary.status,
22024
+ recordId: normalizedRecordId,
22025
+ ...output.summary.status === void 0 ? {} : { status: output.summary.status },
21917
22026
  url: output.summary.url
21918
22027
  },
21919
22028
  context: buildRuntimeTraceContext({
@@ -23317,7 +23426,9 @@ var OpensteerSessionRuntime = class {
23317
23426
  ...graphql.persisted === void 0 ? {} : { persisted: graphql.persisted },
23318
23427
  ...graphqlVariables === void 0 ? {} : { variables: graphqlVariables }
23319
23428
  };
23320
- const requestBody = shouldShowRequestBody(record.record.method) && record.record.requestBody !== void 0 ? buildStructuredBodyPreview(record.record.requestBody, record.record.requestHeaders) : void 0;
23429
+ const requestBody = shouldShowRequestBody(record.record.method) && record.record.requestBody !== void 0 ? buildStructuredBodyPreview(record.record.requestBody, record.record.requestHeaders, {
23430
+ truncateData: false
23431
+ }) : void 0;
23321
23432
  const responseBody = record.record.responseBody === void 0 ? void 0 : buildStructuredBodyPreview(record.record.responseBody, record.record.responseHeaders);
23322
23433
  const notes = detectNetworkRecordNotes(record);
23323
23434
  return {
@@ -23347,8 +23458,18 @@ var OpensteerSessionRuntime = class {
23347
23458
  let recommended;
23348
23459
  for (const transport of REPLAY_TRANSPORT_LADDER) {
23349
23460
  const attemptStartedAt = Date.now();
23461
+ const attemptTimeoutMs = resolveReplayProbeAttemptTimeoutMs({
23462
+ remainingMs: timeout.remainingMs(),
23463
+ transportsRemaining: REPLAY_TRANSPORT_LADDER.length - attempts.length,
23464
+ recommendedFound: recommended !== void 0
23465
+ });
23350
23466
  try {
23351
- const output = await this.executeReplayTransportAttempt(transport, request, timeout);
23467
+ const output = await this.executeReplayTransportAttemptWithinBudget(
23468
+ transport,
23469
+ request,
23470
+ timeout,
23471
+ attemptTimeoutMs
23472
+ );
23352
23473
  const ok = matchesSuccessFingerprintFromProtocolResponse(output.response, fingerprint);
23353
23474
  attempts.push({
23354
23475
  transport,
@@ -23364,7 +23485,7 @@ var OpensteerSessionRuntime = class {
23364
23485
  transport,
23365
23486
  ok: false,
23366
23487
  durationMs: Date.now() - attemptStartedAt,
23367
- error: normalizeRuntimeErrorMessage(error)
23488
+ error: normalizeProbeTransportAttemptError(transport, error, attemptTimeoutMs)
23368
23489
  });
23369
23490
  }
23370
23491
  }
@@ -23568,6 +23689,23 @@ var OpensteerSessionRuntime = class {
23568
23689
  }
23569
23690
  }
23570
23691
  }
23692
+ async executeReplayTransportAttemptWithinBudget(transport, request, timeout, attemptTimeoutMs) {
23693
+ if (attemptTimeoutMs === void 0) {
23694
+ return this.executeReplayTransportAttempt(transport, request, timeout);
23695
+ }
23696
+ return runWithPolicyTimeout(
23697
+ {
23698
+ resolveTimeoutMs() {
23699
+ return attemptTimeoutMs;
23700
+ }
23701
+ },
23702
+ {
23703
+ operation: timeout.operation,
23704
+ signal: timeout.signal
23705
+ },
23706
+ (attemptTimeout) => this.executeReplayTransportAttempt(transport, request, attemptTimeout)
23707
+ );
23708
+ }
23571
23709
  async executeFetchTransportAttempt(transport, request, timeout, input) {
23572
23710
  let prepared = finalizeMaterializedTransportRequest(request, transport);
23573
23711
  if (input.cookies !== false && transport === "direct-http" && this.currentBinding() !== void 0) {
@@ -24482,10 +24620,15 @@ var OpensteerSessionRuntime = class {
24482
24620
  return this.observationSessionId ?? this.sessionRef;
24483
24621
  }
24484
24622
  runWithOperationTimeout(operation, callback, options = {}) {
24623
+ const timeoutPolicy = options.timeoutMs === void 0 ? this.policy.timeout : {
24624
+ resolveTimeoutMs() {
24625
+ return options.timeoutMs;
24626
+ }
24627
+ };
24485
24628
  const existingCollector = this.operationEventStorage.getStore();
24486
24629
  if (existingCollector !== void 0) {
24487
24630
  return runWithPolicyTimeout(
24488
- this.policy.timeout,
24631
+ timeoutPolicy,
24489
24632
  {
24490
24633
  operation,
24491
24634
  ...options.signal === void 0 ? {} : { signal: options.signal }
@@ -24498,7 +24641,7 @@ var OpensteerSessionRuntime = class {
24498
24641
  return this.operationEventStorage.run(collector, async () => {
24499
24642
  try {
24500
24643
  return await runWithPolicyTimeout(
24501
- this.policy.timeout,
24644
+ timeoutPolicy,
24502
24645
  {
24503
24646
  operation,
24504
24647
  ...options.signal === void 0 ? {} : { signal: options.signal }
@@ -24668,6 +24811,21 @@ function buildEngineNetworkRecordFilters(input) {
24668
24811
  function normalizeNetworkStatusFilter(status) {
24669
24812
  return String(status);
24670
24813
  }
24814
+ function normalizeNetworkQueryInput(input) {
24815
+ return {
24816
+ ...input,
24817
+ ...input.recordId === void 0 ? {} : { recordId: normalizeNetworkRecordId(input.recordId) },
24818
+ ...input.before === void 0 ? {} : { before: normalizeNetworkRecordId(input.before) },
24819
+ ...input.after === void 0 ? {} : { after: normalizeNetworkRecordId(input.after) }
24820
+ };
24821
+ }
24822
+ function normalizeNetworkRecordId(recordId) {
24823
+ const trimmed = recordId.trim();
24824
+ if (trimmed.length === 0 || trimmed.startsWith("record:")) {
24825
+ return trimmed;
24826
+ }
24827
+ return `record:${trimmed}`;
24828
+ }
24671
24829
  function resolveLiveQueryRequestIds(input, history) {
24672
24830
  const requestIdCandidates = [];
24673
24831
  if (input.recordId !== void 0) {
@@ -24880,6 +25038,20 @@ var REPLAY_TRANSPORT_LADDER = [
24880
25038
  "context-http",
24881
25039
  "page-http"
24882
25040
  ];
25041
+ function resolveReplayProbeAttemptTimeoutMs(input) {
25042
+ const attemptCapMs = input.recommendedFound ? REPLAY_PROBE_POST_SUCCESS_ATTEMPT_TIMEOUT_MS : REPLAY_PROBE_MAX_ATTEMPT_TIMEOUT_MS;
25043
+ const clampedRemaining = input.remainingMs === void 0 ? void 0 : Math.max(0, input.remainingMs);
25044
+ if (clampedRemaining === 0) {
25045
+ return 0;
25046
+ }
25047
+ if (clampedRemaining === void 0) {
25048
+ return attemptCapMs;
25049
+ }
25050
+ const sliceMs = Math.floor(clampedRemaining / Math.max(1, input.transportsRemaining));
25051
+ const minimumBudgetAffordable = clampedRemaining >= REPLAY_PROBE_MIN_ATTEMPT_TIMEOUT_MS * input.transportsRemaining;
25052
+ const attemptBudgetMs = minimumBudgetAffordable ? Math.max(REPLAY_PROBE_MIN_ATTEMPT_TIMEOUT_MS, sliceMs) : sliceMs;
25053
+ return Math.min(clampedRemaining, attemptCapMs, Math.max(1, attemptBudgetMs));
25054
+ }
24883
25055
  function filterNetworkSummaryRecords(records, input) {
24884
25056
  return records.filter((record) => {
24885
25057
  if (record.record.resourceType === "preflight" || record.record.method === "OPTIONS") {
@@ -25052,10 +25224,10 @@ function extractGraphqlOperationName(queryText) {
25052
25224
  function shouldShowRequestBody(method) {
25053
25225
  return !["GET", "HEAD", "DELETE", "OPTIONS"].includes(method.trim().toUpperCase());
25054
25226
  }
25055
- function buildStructuredBodyPreview(body, headers) {
25227
+ function buildStructuredBodyPreview(body, headers, options = {}) {
25056
25228
  const contentType = headerValue(headers, "content-type") ?? body?.mimeType;
25057
25229
  const parsed = parseStructuredPayload(body, contentType);
25058
- const data = parsed === void 0 ? void 0 : typeof parsed === "string" ? truncateInlineText(parsed) : truncateStructuredValue(parsed);
25230
+ const data = parsed === void 0 ? void 0 : options.truncateData === false ? parsed : typeof parsed === "string" ? truncateInlineText(parsed) : truncateStructuredValue(parsed);
25059
25231
  return {
25060
25232
  bytes: body?.originalByteLength ?? body?.capturedByteLength ?? 0,
25061
25233
  ...contentType === void 0 ? {} : { contentType },
@@ -25268,10 +25440,12 @@ function resolveSessionFetchTransportLadder(transport) {
25268
25440
  return ["direct-http"];
25269
25441
  case "matched-tls":
25270
25442
  return ["matched-tls"];
25443
+ case "context":
25444
+ return ["context-http"];
25271
25445
  case "page":
25272
25446
  return ["page-http"];
25273
25447
  case "auto":
25274
- return ["direct-http", "matched-tls", "page-http"];
25448
+ return ["direct-http", "matched-tls", "context-http", "page-http"];
25275
25449
  }
25276
25450
  }
25277
25451
  function detectChallengeNoteFromRecord(record) {
@@ -25416,6 +25590,12 @@ function diffStorageSnapshot(left, right) {
25416
25590
  function normalizeRuntimeErrorMessage(error) {
25417
25591
  return error instanceof Error ? error.message : String(error);
25418
25592
  }
25593
+ function normalizeProbeTransportAttemptError(transport, error, attemptTimeoutMs) {
25594
+ if (attemptTimeoutMs !== void 0 && error instanceof OpensteerProtocolError && error.code === "timeout") {
25595
+ return `${transport} probe exceeded ${String(attemptTimeoutMs)}ms`;
25596
+ }
25597
+ return normalizeRuntimeErrorMessage(error);
25598
+ }
25419
25599
  function applyBrowserCookiesToTransportRequest(request, cookies) {
25420
25600
  if (cookies.length === 0) {
25421
25601
  return request;
@@ -27811,10 +27991,10 @@ var OpensteerSemanticRestClient = class {
27811
27991
  constructor(connection) {
27812
27992
  this.connection = connection;
27813
27993
  }
27814
- async invoke(operation, input) {
27815
- return this.invokeInternal(operation, input, false);
27994
+ async invoke(operation, input, options = {}) {
27995
+ return this.invokeInternal(operation, input, false, options);
27816
27996
  }
27817
- async invokeInternal(operation, input, hasRetried) {
27997
+ async invokeInternal(operation, input, hasRetried, options) {
27818
27998
  const endpoint = opensteerSemanticRestEndpoints.find((entry) => entry.name === operation);
27819
27999
  if (!endpoint) {
27820
28000
  throw new Error(`unsupported semantic operation ${operation}`);
@@ -27828,10 +28008,11 @@ var OpensteerSemanticRestClient = class {
27828
28008
  method: "POST",
27829
28009
  headers: {
27830
28010
  authorization: await this.connection.getAuthorizationHeader(),
27831
- "content-type": "application/json; charset=utf-8"
28011
+ "content-type": "application/json; charset=utf-8",
28012
+ ...options.timeoutMs === void 0 ? {} : { "x-opensteer-timeout-ms": String(options.timeoutMs) }
27832
28013
  },
27833
28014
  body: JSON.stringify(request),
27834
- signal: AbortSignal.timeout(3e4)
28015
+ signal: createRequestSignal2(options)
27835
28016
  });
27836
28017
  } catch (error) {
27837
28018
  if (operation === "session.close" && isFetchFailure(error)) {
@@ -27847,7 +28028,7 @@ var OpensteerSemanticRestClient = class {
27847
28028
  return envelope.data;
27848
28029
  } catch (error) {
27849
28030
  if (!hasRetried && this.connection.handleError && await this.connection.handleError(error, { operation })) {
27850
- return this.invokeInternal(operation, input, true);
28031
+ return this.invokeInternal(operation, input, true, options);
27851
28032
  }
27852
28033
  if (operation === "session.close" && isFetchFailure(error)) {
27853
28034
  return { closed: true };
@@ -27859,6 +28040,13 @@ var OpensteerSemanticRestClient = class {
27859
28040
  return this.invoke("session.close", {});
27860
28041
  }
27861
28042
  };
28043
+ function createRequestSignal2(options) {
28044
+ const timeoutSignal = AbortSignal.timeout(options.timeoutMs ?? 3e4);
28045
+ if (options.signal === void 0) {
28046
+ return timeoutSignal;
28047
+ }
28048
+ return AbortSignal.any([options.signal, timeoutSignal]);
28049
+ }
27862
28050
  function isFetchFailure(error) {
27863
28051
  if (!(error instanceof Error)) {
27864
28052
  return false;
@@ -28225,13 +28413,44 @@ function asRecord(value) {
28225
28413
  return value;
28226
28414
  }
28227
28415
 
28228
- // src/cloud/registry-sync.ts
28229
- var REGISTRY_SYNC_MAX_PAYLOAD_BYTES = 15e5;
28230
- var REGISTRY_SYNC_MAX_ENTRIES_PER_BATCH = 100;
28231
- async function syncLocalRegistryToCloud(client, workspace, store) {
28416
+ // src/cloud/workspace-sync.ts
28417
+ var WORKSPACE_SYNC_MAX_PAYLOAD_BYTES = 15e5;
28418
+ var WORKSPACE_SYNC_MAX_ENTRIES_PER_BATCH = 100;
28419
+ async function syncLocalWorkspaceToCloud(client, workspace, store) {
28420
+ await syncDescriptorRegistryToCloud(client, workspace, store);
28421
+ await syncRequestPlansToCloud(client, workspace, store);
28422
+ }
28423
+ async function syncDescriptorRegistryToCloud(client, workspace, store) {
28232
28424
  const descriptors = await store.registry.descriptors.list();
28233
- const descriptorEntries = descriptors.map((record) => toDescriptorImportEntry(workspace, record));
28234
- await importInBatches(descriptorEntries, (entries) => client.importDescriptors(entries));
28425
+ const entries = descriptors.map((record) => toDescriptorImportEntry(workspace, record));
28426
+ await importInBatches(entries, {
28427
+ getPayloadByteLength: (batch) => payloadByteLength({ entries: batch }),
28428
+ importBatch: (batch) => client.importDescriptors(batch)
28429
+ });
28430
+ }
28431
+ async function syncRequestPlansToCloud(client, workspace, store) {
28432
+ const requestPlans = await store.registry.requestPlans.list();
28433
+ const entries = requestPlans.map((record) => toRequestPlanImportEntry(workspace, record)).filter((entry) => entry !== void 0);
28434
+ await importInBatches(entries, {
28435
+ getPayloadByteLength: (batch) => payloadByteLength({ entries: batch }),
28436
+ importBatch: (batch) => client.importRequestPlans({ entries: batch })
28437
+ });
28438
+ }
28439
+ function toRequestPlanImportEntry(workspace, record) {
28440
+ const entry = {
28441
+ workspace,
28442
+ recordId: record.id,
28443
+ key: record.key,
28444
+ version: record.version,
28445
+ contentHash: record.contentHash,
28446
+ tags: [...record.tags],
28447
+ ...record.provenance === void 0 ? {} : { provenance: record.provenance },
28448
+ ...record.freshness === void 0 ? {} : { freshness: record.freshness },
28449
+ payload: record.payload,
28450
+ createdAt: record.createdAt,
28451
+ updatedAt: record.updatedAt
28452
+ };
28453
+ return payloadByteLength({ entries: [entry] }) <= WORKSPACE_SYNC_MAX_PAYLOAD_BYTES ? entry : void 0;
28235
28454
  }
28236
28455
  function toDescriptorImportEntry(workspace, record) {
28237
28456
  return {
@@ -28247,19 +28466,19 @@ function toDescriptorImportEntry(workspace, record) {
28247
28466
  updatedAt: record.updatedAt
28248
28467
  };
28249
28468
  }
28250
- async function importInBatches(entries, importBatch) {
28469
+ async function importInBatches(entries, options) {
28251
28470
  if (entries.length === 0) {
28252
28471
  return;
28253
28472
  }
28254
- for (const batch of chunkEntries(entries)) {
28255
- await importBatch(batch);
28473
+ for (const batch of chunkEntries(entries, options.getPayloadByteLength)) {
28474
+ await options.importBatch(batch);
28256
28475
  }
28257
28476
  }
28258
- function chunkEntries(entries) {
28477
+ function chunkEntries(entries, getPayloadByteLength) {
28259
28478
  const batches = [];
28260
28479
  let currentBatch = [];
28261
28480
  for (const entry of entries) {
28262
- if (payloadByteLength([entry]) > REGISTRY_SYNC_MAX_PAYLOAD_BYTES) {
28481
+ if (getPayloadByteLength([entry]) > WORKSPACE_SYNC_MAX_PAYLOAD_BYTES) {
28263
28482
  continue;
28264
28483
  }
28265
28484
  if (currentBatch.length === 0) {
@@ -28267,7 +28486,7 @@ function chunkEntries(entries) {
28267
28486
  continue;
28268
28487
  }
28269
28488
  const nextBatch = [...currentBatch, entry];
28270
- if (nextBatch.length > REGISTRY_SYNC_MAX_ENTRIES_PER_BATCH || payloadByteLength(nextBatch) > REGISTRY_SYNC_MAX_PAYLOAD_BYTES) {
28489
+ if (nextBatch.length > WORKSPACE_SYNC_MAX_ENTRIES_PER_BATCH || getPayloadByteLength(nextBatch) > WORKSPACE_SYNC_MAX_PAYLOAD_BYTES) {
28271
28490
  batches.push(currentBatch);
28272
28491
  currentBatch = [entry];
28273
28492
  continue;
@@ -28279,8 +28498,8 @@ function chunkEntries(entries) {
28279
28498
  }
28280
28499
  return batches;
28281
28500
  }
28282
- function payloadByteLength(entries) {
28283
- return Buffer.byteLength(JSON.stringify({ entries }), "utf8");
28501
+ function payloadByteLength(value) {
28502
+ return Buffer.byteLength(JSON.stringify(value), "utf8");
28284
28503
  }
28285
28504
 
28286
28505
  // src/cloud/session-proxy.ts
@@ -28291,14 +28510,17 @@ var CloudSessionProxy = class {
28291
28510
  cleanupRootOnClose;
28292
28511
  cloud;
28293
28512
  observability;
28513
+ policy;
28294
28514
  sessionId;
28295
28515
  semanticGrant;
28296
28516
  client;
28297
28517
  automation;
28298
28518
  workspaceStore;
28519
+ syncWorkspaceOnClose = false;
28299
28520
  constructor(cloud, options = {}) {
28300
28521
  this.cloud = cloud;
28301
28522
  this.workspace = options.workspace;
28523
+ this.policy = options.policy ?? defaultPolicy();
28302
28524
  this.observability = options.observability;
28303
28525
  this.rootPath = options.rootPath ?? (this.workspace === void 0 ? path7.join(tmpdir(), `${TEMPORARY_CLOUD_WORKSPACE_PREFIX}${randomUUID()}`) : resolveFilesystemWorkspacePath({
28304
28526
  rootDir: path7.resolve(options.rootDir ?? process.cwd()),
@@ -28307,14 +28529,17 @@ var CloudSessionProxy = class {
28307
28529
  this.cleanupRootOnClose = options.cleanupRootOnClose ?? this.workspace === void 0;
28308
28530
  }
28309
28531
  async open(input = {}) {
28310
- await this.ensureSession({
28311
- ...input.browser === void 0 ? {} : { browser: input.browser },
28312
- ...input.launch === void 0 ? {} : { launch: input.launch },
28313
- ...input.context === void 0 ? {} : { context: input.context }
28314
- });
28315
- return this.requireClient().invoke("session.open", {
28316
- ...input.url === void 0 ? {} : { url: input.url }
28317
- });
28532
+ return this.invokeSemanticOperation(
28533
+ "session.open",
28534
+ {
28535
+ ...input.url === void 0 ? {} : { url: input.url }
28536
+ },
28537
+ {
28538
+ ...input.browser === void 0 ? {} : { browser: input.browser },
28539
+ ...input.launch === void 0 ? {} : { launch: input.launch },
28540
+ ...input.context === void 0 ? {} : { context: input.context }
28541
+ }
28542
+ );
28318
28543
  }
28319
28544
  async info() {
28320
28545
  const persisted = this.client !== void 0 || this.sessionId !== void 0 ? void 0 : await this.loadPersistedSession();
@@ -28357,108 +28582,88 @@ var CloudSessionProxy = class {
28357
28582
  };
28358
28583
  }
28359
28584
  async listPages(input = {}) {
28360
- await this.ensureSession();
28361
- return this.requireClient().invoke("page.list", input);
28585
+ return this.invokeSemanticOperation("page.list", input);
28362
28586
  }
28363
28587
  async newPage(input = {}) {
28364
- await this.ensureSession();
28365
- return this.requireAutomation().invoke("page.new", input);
28588
+ return this.invokeAutomationOperation(
28589
+ "page.new",
28590
+ (automation) => automation.invoke("page.new", input)
28591
+ );
28366
28592
  }
28367
28593
  async activatePage(input) {
28368
- await this.ensureSession();
28369
- return this.requireClient().invoke("page.activate", input);
28594
+ return this.invokeSemanticOperation("page.activate", input);
28370
28595
  }
28371
28596
  async closePage(input = {}) {
28372
- await this.ensureSession();
28373
- return this.requireClient().invoke("page.close", input);
28597
+ return this.invokeSemanticOperation("page.close", input);
28374
28598
  }
28375
28599
  async goto(input) {
28376
- await this.ensureSession();
28377
- return this.requireClient().invoke("page.goto", input);
28600
+ return this.invokeSemanticOperation("page.goto", input);
28378
28601
  }
28379
28602
  async evaluate(input) {
28380
- await this.ensureSession();
28381
- return this.requireAutomation().invoke("page.evaluate", input);
28603
+ return this.invokeAutomationOperation(
28604
+ "page.evaluate",
28605
+ (automation) => automation.invoke("page.evaluate", input)
28606
+ );
28382
28607
  }
28383
28608
  async addInitScript(input) {
28384
- await this.ensureSession();
28385
- return this.requireClient().invoke("page.add-init-script", input);
28609
+ return this.invokeSemanticOperation("page.add-init-script", input);
28386
28610
  }
28387
28611
  async snapshot(input = {}) {
28388
- await this.ensureSession();
28389
- return this.requireClient().invoke("page.snapshot", input);
28612
+ return this.invokeSemanticOperation("page.snapshot", input);
28390
28613
  }
28391
28614
  async click(input) {
28392
- await this.ensureSession();
28393
- return this.requireClient().invoke("dom.click", input);
28615
+ return this.invokeSemanticOperation("dom.click", input);
28394
28616
  }
28395
28617
  async hover(input) {
28396
- await this.ensureSession();
28397
- return this.requireClient().invoke("dom.hover", input);
28618
+ return this.invokeSemanticOperation("dom.hover", input);
28398
28619
  }
28399
28620
  async input(input) {
28400
- await this.ensureSession();
28401
- return this.requireClient().invoke("dom.input", input);
28621
+ return this.invokeSemanticOperation("dom.input", input);
28402
28622
  }
28403
28623
  async scroll(input) {
28404
- await this.ensureSession();
28405
- return this.requireClient().invoke("dom.scroll", input);
28624
+ return this.invokeSemanticOperation("dom.scroll", input);
28406
28625
  }
28407
28626
  async extract(input) {
28408
- await this.ensureSession();
28409
- return this.requireClient().invoke("dom.extract", input);
28627
+ return this.invokeSemanticOperation("dom.extract", input);
28410
28628
  }
28411
28629
  async queryNetwork(input = {}) {
28412
- await this.ensureSession();
28413
- return this.requireClient().invoke("network.query", input);
28630
+ return this.invokeSemanticOperation("network.query", input);
28414
28631
  }
28415
28632
  async getNetworkDetail(input) {
28416
- await this.ensureSession();
28417
- return this.requireClient().invoke("network.detail", input);
28633
+ return this.invokeSemanticOperation("network.detail", input);
28418
28634
  }
28419
28635
  async captureInteraction(input) {
28420
- await this.ensureSession();
28421
- return this.requireClient().invoke("interaction.capture", input);
28636
+ return this.invokeSemanticOperation("interaction.capture", input);
28422
28637
  }
28423
28638
  async getInteraction(input) {
28424
- await this.ensureSession();
28425
- return this.requireClient().invoke("interaction.get", input);
28639
+ return this.invokeSemanticOperation("interaction.get", input);
28426
28640
  }
28427
28641
  async diffInteraction(input) {
28428
- await this.ensureSession();
28429
- return this.requireClient().invoke("interaction.diff", input);
28642
+ return this.invokeSemanticOperation("interaction.diff", input);
28430
28643
  }
28431
28644
  async replayInteraction(input) {
28432
- await this.ensureSession();
28433
- return this.requireClient().invoke("interaction.replay", input);
28645
+ return this.invokeSemanticOperation("interaction.replay", input);
28434
28646
  }
28435
28647
  async captureScripts(input = {}) {
28436
- await this.ensureSession();
28437
- return this.requireClient().invoke("scripts.capture", input);
28648
+ return this.invokeSemanticOperation("scripts.capture", input);
28438
28649
  }
28439
28650
  async readArtifact(input) {
28440
- await this.ensureSession();
28441
- return this.requireClient().invoke("artifact.read", input);
28651
+ return this.invokeSemanticOperation("artifact.read", input);
28442
28652
  }
28443
28653
  async beautifyScript(input) {
28444
- await this.ensureSession();
28445
- return this.requireClient().invoke("scripts.beautify", input);
28654
+ return this.invokeSemanticOperation("scripts.beautify", input);
28446
28655
  }
28447
28656
  async deobfuscateScript(input) {
28448
- await this.ensureSession();
28449
- return this.requireClient().invoke("scripts.deobfuscate", input);
28657
+ return this.invokeSemanticOperation("scripts.deobfuscate", input);
28450
28658
  }
28451
28659
  async sandboxScript(input) {
28452
- await this.ensureSession();
28453
- return this.requireClient().invoke("scripts.sandbox", input);
28660
+ return this.invokeSemanticOperation("scripts.sandbox", input);
28454
28661
  }
28455
28662
  async solveCaptcha(input) {
28456
- await this.ensureSession();
28457
- return this.requireClient().invoke("captcha.solve", input);
28663
+ return this.invokeSemanticOperation("captcha.solve", input);
28458
28664
  }
28459
28665
  async getCookies(input = {}) {
28460
- await this.ensureSession();
28461
- return this.requireClient().invoke("session.cookies", input);
28666
+ return this.invokeSemanticOperation("session.cookies", input);
28462
28667
  }
28463
28668
  async route(input) {
28464
28669
  await this.ensureSession();
@@ -28469,20 +28674,16 @@ var CloudSessionProxy = class {
28469
28674
  return this.requireAutomation().interceptScript(input);
28470
28675
  }
28471
28676
  async getStorageSnapshot(input = {}) {
28472
- await this.ensureSession();
28473
- return this.requireClient().invoke("session.storage", input);
28677
+ return this.invokeSemanticOperation("session.storage", input);
28474
28678
  }
28475
28679
  async getBrowserState(input = {}) {
28476
- await this.ensureSession();
28477
- return this.requireClient().invoke("session.state", input);
28680
+ return this.invokeSemanticOperation("session.state", input);
28478
28681
  }
28479
28682
  async fetch(input) {
28480
- await this.ensureSession();
28481
- return this.requireClient().invoke("session.fetch", input);
28683
+ return this.invokeSemanticOperation("session.fetch", input);
28482
28684
  }
28483
28685
  async computerExecute(input) {
28484
- await this.ensureSession();
28485
- return this.requireClient().invoke("computer.execute", input);
28686
+ return this.invokeSemanticOperation("computer.execute", input);
28486
28687
  }
28487
28688
  async close() {
28488
28689
  const session = await this.loadPersistedSession() ?? (this.sessionId === void 0 ? void 0 : {
@@ -28494,6 +28695,14 @@ var CloudSessionProxy = class {
28494
28695
  startedAt: Date.now(),
28495
28696
  updatedAt: Date.now()
28496
28697
  });
28698
+ let syncError;
28699
+ if (this.syncWorkspaceOnClose) {
28700
+ try {
28701
+ await this.syncWorkspaceToCloud();
28702
+ } catch (error) {
28703
+ syncError = error;
28704
+ }
28705
+ }
28497
28706
  try {
28498
28707
  if (session !== void 0) {
28499
28708
  await this.cloud.closeSession(session.sessionId).catch((error) => {
@@ -28514,6 +28723,9 @@ var CloudSessionProxy = class {
28514
28723
  await rm(this.rootPath, { recursive: true, force: true }).catch(() => void 0);
28515
28724
  }
28516
28725
  }
28726
+ if (syncError !== void 0) {
28727
+ throw syncError;
28728
+ }
28517
28729
  return { closed: true };
28518
28730
  }
28519
28731
  async disconnect() {
@@ -28521,34 +28733,38 @@ var CloudSessionProxy = class {
28521
28733
  await this.close();
28522
28734
  return;
28523
28735
  }
28736
+ let syncError;
28737
+ if (this.syncWorkspaceOnClose) {
28738
+ try {
28739
+ await this.syncWorkspaceToCloud();
28740
+ } catch (error) {
28741
+ syncError = error;
28742
+ }
28743
+ }
28524
28744
  this.client = void 0;
28525
28745
  await this.automation?.close().catch(() => void 0);
28526
28746
  this.automation = void 0;
28527
28747
  this.sessionId = void 0;
28528
28748
  this.semanticGrant = void 0;
28749
+ if (syncError !== void 0) {
28750
+ throw syncError;
28751
+ }
28529
28752
  }
28530
- async ensureSession(input = {}) {
28753
+ async ensureSession(input = {}, timeout) {
28531
28754
  if (this.client) {
28532
28755
  return;
28533
28756
  }
28534
28757
  assertSupportedCloudBrowserMode(input.browser);
28535
28758
  const localCloud = this.shouldUseLocalCloudTransport();
28759
+ this.syncWorkspaceOnClose = localCloud && this.workspace !== void 0;
28536
28760
  const browserProfile = resolveCloudBrowserProfile(this.cloud, input);
28537
28761
  const persisted = await this.loadPersistedSession();
28538
- if (persisted !== void 0 && await this.isReusableCloudSession(persisted.sessionId)) {
28539
- if (localCloud) {
28540
- void this.syncRegistryToCloud();
28541
- } else {
28542
- await this.syncRegistryToCloud();
28543
- }
28762
+ if (persisted !== void 0 && await this.isReusableCloudSession(persisted.sessionId, timeout)) {
28763
+ await this.syncWorkspaceToCloud();
28544
28764
  this.bindClient(persisted);
28545
28765
  return;
28546
28766
  }
28547
- if (localCloud) {
28548
- void this.syncRegistryToCloud();
28549
- } else {
28550
- await this.syncRegistryToCloud();
28551
- }
28767
+ await this.syncWorkspaceToCloud();
28552
28768
  const baseCreateInput = {
28553
28769
  ...this.workspace === void 0 ? {} : { name: this.workspace },
28554
28770
  ...input.launch === void 0 ? {} : { browser: input.launch },
@@ -28560,10 +28776,12 @@ var CloudSessionProxy = class {
28560
28776
  ...baseCreateInput,
28561
28777
  sourceType: "local-cloud",
28562
28778
  sourceRef: this.workspace,
28563
- localWorkspaceRootPath: this.rootPath,
28564
- locality: "auto"
28779
+ localWorkspaceRootPath: this.rootPath
28565
28780
  } : baseCreateInput;
28566
- const session = await this.cloud.createSession(createInput);
28781
+ const session = await this.cloud.createSession(createInput, {
28782
+ signal: timeout?.signal,
28783
+ timeoutMs: timeout?.remainingMs()
28784
+ });
28567
28785
  const record = {
28568
28786
  layout: "opensteer-session",
28569
28787
  version: 1,
@@ -28576,15 +28794,12 @@ var CloudSessionProxy = class {
28576
28794
  await this.writePersistedSession(record);
28577
28795
  this.bindClient(record, session.initialGrants?.semantic);
28578
28796
  }
28579
- async syncRegistryToCloud() {
28797
+ async syncWorkspaceToCloud() {
28580
28798
  if (this.workspace === void 0) {
28581
28799
  return;
28582
28800
  }
28583
- try {
28584
- const workspaceStore = await this.ensureWorkspaceStore();
28585
- await syncLocalRegistryToCloud(this.cloud, this.workspace, workspaceStore);
28586
- } catch {
28587
- }
28801
+ const workspaceStore = await this.ensureWorkspaceStore();
28802
+ await syncLocalWorkspaceToCloud(this.cloud, this.workspace, workspaceStore);
28588
28803
  }
28589
28804
  bindClient(record, initialSemanticGrant) {
28590
28805
  this.sessionId = record.sessionId;
@@ -28618,9 +28833,12 @@ var CloudSessionProxy = class {
28618
28833
  async clearPersistedSession() {
28619
28834
  await clearPersistedSessionRecord(this.rootPath, "cloud").catch(() => void 0);
28620
28835
  }
28621
- async isReusableCloudSession(sessionId) {
28836
+ async isReusableCloudSession(sessionId, timeout) {
28622
28837
  try {
28623
- const session = await this.cloud.getSession(sessionId);
28838
+ const session = await this.cloud.getSession(sessionId, {
28839
+ signal: timeout?.signal,
28840
+ timeoutMs: timeout?.remainingMs()
28841
+ });
28624
28842
  return session.status !== "closed" && session.status !== "failed";
28625
28843
  } catch (error) {
28626
28844
  if (isMissingCloudSessionError(error)) {
@@ -28641,14 +28859,17 @@ var CloudSessionProxy = class {
28641
28859
  }
28642
28860
  return this.automation;
28643
28861
  }
28644
- async ensureSemanticGrant(forceRefresh = false) {
28862
+ async ensureSemanticGrant(forceRefresh = false, timeout) {
28645
28863
  if (!forceRefresh && this.semanticGrant?.kind === "semantic" && this.semanticGrant.expiresAt > Date.now() + 1e4) {
28646
28864
  return this.semanticGrant;
28647
28865
  }
28648
28866
  if (!this.sessionId) {
28649
28867
  throw new Error("Cloud session has not been initialized.");
28650
28868
  }
28651
- const issued = await this.cloud.issueAccess(this.sessionId, ["semantic"]);
28869
+ const issued = await this.cloud.issueAccess(this.sessionId, ["semantic"], {
28870
+ signal: timeout?.signal,
28871
+ timeoutMs: timeout?.remainingMs()
28872
+ });
28652
28873
  const grant = issued.grants.semantic;
28653
28874
  if (!grant || grant.transport !== "http") {
28654
28875
  throw new Error("cloud did not issue a valid semantic grant");
@@ -28671,6 +28892,25 @@ var CloudSessionProxy = class {
28671
28892
  return false;
28672
28893
  }
28673
28894
  }
28895
+ async invokeSemanticOperation(operation, input, sessionInit = {}) {
28896
+ return this.runOperationWithPolicy(operation, async (timeout) => {
28897
+ await this.ensureSession(sessionInit, timeout);
28898
+ await this.ensureSemanticGrant(false, timeout);
28899
+ return this.requireClient().invoke(operation, input, {
28900
+ signal: timeout.signal,
28901
+ timeoutMs: timeout.remainingMs()
28902
+ });
28903
+ });
28904
+ }
28905
+ async invokeAutomationOperation(operation, invoke, sessionInit = {}) {
28906
+ return this.runOperationWithPolicy(operation, async (timeout) => {
28907
+ await this.ensureSession(sessionInit, timeout);
28908
+ return invoke(this.requireAutomation());
28909
+ });
28910
+ }
28911
+ async runOperationWithPolicy(operation, invoke) {
28912
+ return runWithPolicyTimeout(this.policy.timeout, { operation }, invoke);
28913
+ }
28674
28914
  shouldUseLocalCloudTransport() {
28675
28915
  if (this.workspace === void 0) {
28676
28916
  return false;
@@ -28845,6 +29085,7 @@ function createOpensteerSemanticRuntime(input = {}) {
28845
29085
  ...runtimeOptions.rootDir === void 0 ? {} : { rootDir: runtimeOptions.rootDir },
28846
29086
  ...runtimeOptions.rootPath === void 0 ? {} : { rootPath: runtimeOptions.rootPath },
28847
29087
  ...runtimeOptions.workspace === void 0 ? {} : { workspace: runtimeOptions.workspace },
29088
+ ...runtimeOptions.policy === void 0 ? {} : { policy: runtimeOptions.policy },
28848
29089
  ...runtimeOptions.cleanupRootOnClose === void 0 ? {} : { cleanupRootOnClose: runtimeOptions.cleanupRootOnClose },
28849
29090
  ...runtimeOptions.observability === void 0 ? {} : { observability: runtimeOptions.observability }
28850
29091
  });
@@ -28856,5 +29097,5 @@ function createOpensteerSemanticRuntime(input = {}) {
28856
29097
  }
28857
29098
 
28858
29099
  export { CloudSessionProxy, DEFAULT_OPENSTEER_ENGINE, DEFERRED_MATCH_ATTR_KEYS, ElementPathError, FlowRecorderCollector, MATCH_ATTRIBUTE_PRIORITY, OPENSTEER_DOM_ACTION_BRIDGE_SYMBOL, OPENSTEER_ENGINE_NAMES, OPENSTEER_FILESYSTEM_WORKSPACE_LAYOUT, OPENSTEER_FILESYSTEM_WORKSPACE_VERSION, OpensteerAttachAmbiguousError, OpensteerBrowserManager, OpensteerCloudClient, OpensteerRuntime, OpensteerSessionRuntime2 as OpensteerSessionRuntime, STABLE_PRIMARY_ATTR_KEYS, assertProviderSupportsEngine, buildArrayFieldPathCandidates, buildDomDescriptorKey, buildDomDescriptorPayload, buildDomDescriptorVersion, buildPathCandidates, buildPathSelectorHint, buildSegmentSelector, clearPersistedSessionRecord, cloneElementPath, cloneReplayElementPath, cloneStructuralElementAnchor, createArtifactStore, createDomDescriptorStore, createDomRuntime, createFilesystemOpensteerWorkspace, createObservationStore, createOpensteerExtractionDescriptorStore, createOpensteerSemanticRuntime, defaultFallbackPolicy, defaultPolicy, defaultRetryPolicy, defaultSettlePolicy, defaultTimeoutPolicy, delayWithSignal, discoverLocalCdpBrowsers, dispatchSemanticOperation, generateReplayScript, hashDomDescriptorPersist, inspectCdpEndpoint, isCurrentUrlField, isProcessRunning, isValidCssAttributeKey, listLocalChromeProfiles, loadEnvironment, manifestToExternalBinaryLocation, normalizeExtractedValue, normalizeObservabilityConfig, normalizeOpensteerEngineName, normalizeOpensteerProviderMode, normalizeWorkspaceId, parseDomDescriptorRecord, parseExtractionDescriptorRecord, pathExists, readPersistedCloudSessionRecord, readPersistedLocalBrowserSessionRecord, readPersistedSessionRecord, requireCloudAppBaseUrl, resolveCloudConfig, resolveCloudSessionRecordPath, resolveDomActionBridge, resolveExtractedValueInContext, resolveFilesystemWorkspacePath, resolveLiveSessionRecordPath, resolveLocalSessionRecordPath, resolveOpensteerEngineName, resolveOpensteerEnvironment, resolveOpensteerProvider, resolveOpensteerRuntimeConfig, runWithPolicyTimeout, sanitizeElementPath, sanitizeReplayElementPath, sanitizeStructuralElementAnchor, settleWithPolicy, shouldKeepAttributeForPath, writePersistedSessionRecord };
28859
- //# sourceMappingURL=chunk-KBO7DDPF.js.map
28860
- //# sourceMappingURL=chunk-KBO7DDPF.js.map
29100
+ //# sourceMappingURL=chunk-656MQUSM.js.map
29101
+ //# sourceMappingURL=chunk-656MQUSM.js.map