opensteer 0.8.18 → 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.
package/dist/index.cjs CHANGED
@@ -2507,7 +2507,7 @@ objectSchema(
2507
2507
  }
2508
2508
  );
2509
2509
  var opensteerSessionFetchTransportSchema = enumSchema(
2510
- ["auto", "direct", "matched-tls", "page"],
2510
+ ["auto", "direct", "matched-tls", "context", "page"],
2511
2511
  {
2512
2512
  title: "OpensteerSessionFetchTransport"
2513
2513
  }
@@ -6860,6 +6860,7 @@ var opensteerSemanticOperationSpecificationsBase = [
6860
6860
  case "direct":
6861
6861
  return [];
6862
6862
  case "matched-tls":
6863
+ case "context":
6863
6864
  return ["inspect.cookies"];
6864
6865
  case "page":
6865
6866
  return ["pages.manage"];
@@ -7481,13 +7482,6 @@ var SqliteSavedNetworkStore = class {
7481
7482
  }
7482
7483
  async save(records, options) {
7483
7484
  const database = await this.requireDatabase();
7484
- const readExisting = database.prepare(`
7485
- SELECT record_id
7486
- FROM saved_network_records
7487
- WHERE session_ref = @session_ref
7488
- AND page_ref_key = @page_ref_key
7489
- AND request_id = @request_id
7490
- `);
7491
7485
  const upsertRecord = database.prepare(buildSavedNetworkUpsertSql(options.bodyWriteMode));
7492
7486
  const insertTag = database.prepare(`
7493
7487
  INSERT OR IGNORE INTO saved_network_tags (record_id, tag)
@@ -7498,14 +7492,8 @@ var SqliteSavedNetworkStore = class {
7498
7492
  for (const entry of records) {
7499
7493
  const url = new URL(entry.record.url);
7500
7494
  const pageRefKey = entry.record.pageRef ?? "";
7501
- const existing = readExisting.get({
7502
- session_ref: entry.record.sessionRef,
7503
- page_ref_key: pageRefKey,
7504
- request_id: entry.record.requestId
7505
- }) ?? void 0;
7506
- const recordId = existing?.record_id ?? entry.recordId;
7507
7495
  upsertRecord.run({
7508
- record_id: recordId,
7496
+ record_id: entry.recordId,
7509
7497
  request_id: entry.record.requestId,
7510
7498
  session_ref: entry.record.sessionRef,
7511
7499
  page_ref: entry.record.pageRef ?? null,
@@ -7550,7 +7538,7 @@ var SqliteSavedNetworkStore = class {
7550
7538
  }
7551
7539
  for (const currentTag of tags) {
7552
7540
  const result = insertTag.run({
7553
- record_id: recordId,
7541
+ record_id: entry.recordId,
7554
7542
  tag: currentTag
7555
7543
  });
7556
7544
  savedCount += result.changes ?? 0;
@@ -7653,6 +7641,49 @@ var SqliteSavedNetworkStore = class {
7653
7641
  return cleared;
7654
7642
  });
7655
7643
  }
7644
+ async *iterateBatches(options = {}) {
7645
+ const database = await this.requireDatabase();
7646
+ const batchSize = Math.max(1, Math.min(options.batchSize ?? 500, 1e3));
7647
+ let cursor;
7648
+ while (true) {
7649
+ const rows = database.prepare(
7650
+ `
7651
+ SELECT
7652
+ r.*,
7653
+ GROUP_CONCAT(t.tag, '${TAG_DELIMITER}') AS tags
7654
+ FROM saved_network_records r
7655
+ LEFT JOIN saved_network_tags t
7656
+ ON t.record_id = r.record_id
7657
+ ${cursor === void 0 ? "" : "WHERE r.saved_at > ? OR (r.saved_at = ? AND r.record_id > ?)"}
7658
+ GROUP BY r.record_id
7659
+ ORDER BY r.saved_at ASC, r.record_id ASC
7660
+ LIMIT ?
7661
+ `
7662
+ ).all(
7663
+ ...cursor === void 0 ? [] : [cursor.savedAt, cursor.savedAt, cursor.recordId],
7664
+ batchSize
7665
+ );
7666
+ if (rows.length === 0) {
7667
+ return;
7668
+ }
7669
+ yield rows.map((row) => inflateSavedNetworkRow(row, options.includeBodies ?? true));
7670
+ const lastRow = rows.at(-1);
7671
+ if (lastRow === void 0) {
7672
+ return;
7673
+ }
7674
+ cursor = {
7675
+ savedAt: lastRow.saved_at,
7676
+ recordId: lastRow.record_id
7677
+ };
7678
+ }
7679
+ }
7680
+ close() {
7681
+ if (this.database !== void 0) {
7682
+ closeSqliteDatabase(this.database);
7683
+ this.database = void 0;
7684
+ this.databaseInitialization = void 0;
7685
+ }
7686
+ }
7656
7687
  async requireDatabase() {
7657
7688
  if (this.database) {
7658
7689
  return this.database;
@@ -7737,15 +7768,6 @@ var SqliteSavedNetworkStore = class {
7737
7768
  saved_at INTEGER NOT NULL
7738
7769
  );
7739
7770
 
7740
- CREATE UNIQUE INDEX IF NOT EXISTS saved_network_records_scope_request
7741
- ON saved_network_records (session_ref, page_ref_key, request_id);
7742
-
7743
- CREATE INDEX IF NOT EXISTS saved_network_records_saved_at
7744
- ON saved_network_records (saved_at DESC);
7745
-
7746
- CREATE INDEX IF NOT EXISTS saved_network_records_capture
7747
- ON saved_network_records (capture);
7748
-
7749
7771
  CREATE TABLE IF NOT EXISTS saved_network_tags (
7750
7772
  record_id TEXT NOT NULL REFERENCES saved_network_records(record_id) ON DELETE CASCADE,
7751
7773
  tag TEXT NOT NULL,
@@ -7755,6 +7777,19 @@ var SqliteSavedNetworkStore = class {
7755
7777
  CREATE INDEX IF NOT EXISTS saved_network_tags_tag
7756
7778
  ON saved_network_tags (tag);
7757
7779
  `);
7780
+ database.exec(`DROP INDEX IF EXISTS saved_network_records_scope_request`);
7781
+ database.exec(`
7782
+ CREATE INDEX IF NOT EXISTS saved_network_records_scope_request
7783
+ ON saved_network_records (session_ref, page_ref_key, request_id)
7784
+ `);
7785
+ database.exec(`
7786
+ CREATE INDEX IF NOT EXISTS saved_network_records_saved_at
7787
+ ON saved_network_records (saved_at DESC)
7788
+ `);
7789
+ database.exec(`
7790
+ CREATE INDEX IF NOT EXISTS saved_network_records_capture
7791
+ ON saved_network_records (capture)
7792
+ `);
7758
7793
  this.ensureColumn(
7759
7794
  database,
7760
7795
  "saved_network_records",
@@ -8942,7 +8977,6 @@ var DEFAULT_TIMEOUTS = {
8942
8977
  "dom.extract": 15e3,
8943
8978
  "network.query": 15e3,
8944
8979
  "network.detail": 15e3,
8945
- "network.replay": 3e4,
8946
8980
  "scripts.capture": 15e3,
8947
8981
  "session.cookies": 1e4,
8948
8982
  "session.storage": 1e4,
@@ -15977,8 +16011,14 @@ async function launchOwnedBrowser(input) {
15977
16011
  await ensureDirectory(input.userDataDir);
15978
16012
  await clearChromeSingletonEntries(input.userDataDir);
15979
16013
  await sanitizeChromeProfile(input.userDataDir);
16014
+ const requestedRemoteDebuggingPort = readRequestedRemoteDebuggingPort(input.launch?.args);
15980
16015
  const executablePath = resolveChromeExecutablePath(input.launch?.executablePath);
15981
- const args = buildChromeArgs(input.userDataDir, input.launch, input.viewport);
16016
+ const args = buildChromeArgs(
16017
+ input.userDataDir,
16018
+ input.launch,
16019
+ input.viewport,
16020
+ requestedRemoteDebuggingPort
16021
+ );
15982
16022
  const child = child_process.spawn(executablePath, args, {
15983
16023
  stdio: ["ignore", "ignore", "pipe"],
15984
16024
  detached: process.platform !== "win32"
@@ -15994,7 +16034,8 @@ async function launchOwnedBrowser(input) {
15994
16034
  userDataDir: input.userDataDir,
15995
16035
  timeoutMs: input.launch?.timeoutMs ?? DEFAULT_TIMEOUT_MS,
15996
16036
  childExited: async () => child.exitCode,
15997
- stderrLines
16037
+ stderrLines,
16038
+ ...requestedRemoteDebuggingPort !== void 0 && requestedRemoteDebuggingPort > 0 ? { requestedRemoteDebuggingPort } : {}
15998
16039
  }).catch(async (error) => {
15999
16040
  child.kill("SIGKILL");
16000
16041
  throw error;
@@ -16005,10 +16046,10 @@ async function launchOwnedBrowser(input) {
16005
16046
  executablePath
16006
16047
  };
16007
16048
  }
16008
- function buildChromeArgs(userDataDir, launch, viewport) {
16049
+ function buildChromeArgs(userDataDir, launch, viewport, requestedRemoteDebuggingPort) {
16009
16050
  const isHeadless = launch?.headless ?? true;
16010
16051
  const args = [
16011
- "--remote-debugging-port=0",
16052
+ ...requestedRemoteDebuggingPort === void 0 ? ["--remote-debugging-port=0"] : [],
16012
16053
  "--no-first-run",
16013
16054
  "--no-default-browser-check",
16014
16055
  "--disable-blink-features=AutomationControlled",
@@ -16059,6 +16100,15 @@ async function waitForDevToolsEndpoint(input) {
16059
16100
  return `ws://127.0.0.1:${String(activePort.port)}${activePort.webSocketPath}`;
16060
16101
  }
16061
16102
  }
16103
+ if (input.requestedRemoteDebuggingPort !== void 0) {
16104
+ const endpoint = await tryInspectRemoteDebuggingPort(
16105
+ input.requestedRemoteDebuggingPort,
16106
+ input.timeoutMs
16107
+ );
16108
+ if (endpoint !== void 0) {
16109
+ return endpoint;
16110
+ }
16111
+ }
16062
16112
  const exitCode = await input.childExited();
16063
16113
  if (exitCode !== null) {
16064
16114
  throw new Error(formatChromeLaunchError(input.stderrLines));
@@ -16067,6 +16117,52 @@ async function waitForDevToolsEndpoint(input) {
16067
16117
  }
16068
16118
  throw new Error(formatChromeLaunchError(input.stderrLines));
16069
16119
  }
16120
+ function readRequestedRemoteDebuggingPort(args) {
16121
+ if (args === void 0 || args.length === 0) {
16122
+ return void 0;
16123
+ }
16124
+ let explicitFlagFound = false;
16125
+ let port;
16126
+ for (let index = 0; index < args.length; index += 1) {
16127
+ const entry = args[index];
16128
+ if (entry === "--remote-debugging-port") {
16129
+ explicitFlagFound = true;
16130
+ const next = args[index + 1];
16131
+ if (next !== void 0) {
16132
+ port = parseRemoteDebuggingPort(next);
16133
+ index += 1;
16134
+ }
16135
+ continue;
16136
+ }
16137
+ if (entry.startsWith("--remote-debugging-port=")) {
16138
+ explicitFlagFound = true;
16139
+ port = parseRemoteDebuggingPort(entry.slice("--remote-debugging-port=".length));
16140
+ }
16141
+ }
16142
+ return explicitFlagFound ? port : void 0;
16143
+ }
16144
+ function parseRemoteDebuggingPort(value) {
16145
+ const trimmed = value.trim();
16146
+ if (!/^\d+$/.test(trimmed)) {
16147
+ return void 0;
16148
+ }
16149
+ const parsed = Number.parseInt(trimmed, 10);
16150
+ if (!Number.isInteger(parsed) || parsed < 0) {
16151
+ return void 0;
16152
+ }
16153
+ return parsed;
16154
+ }
16155
+ async function tryInspectRemoteDebuggingPort(port, timeoutMs) {
16156
+ try {
16157
+ const inspected = await inspectCdpEndpoint({
16158
+ endpoint: `http://127.0.0.1:${String(port)}`,
16159
+ timeoutMs: Math.min(2e3, timeoutMs)
16160
+ });
16161
+ return inspected.endpoint;
16162
+ } catch {
16163
+ return void 0;
16164
+ }
16165
+ }
16070
16166
  function formatChromeLaunchError(stderrLines) {
16071
16167
  const collapsed = stderrLines.join("").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
16072
16168
  if (collapsed.length === 0) {
@@ -16791,21 +16887,24 @@ var OpensteerCloudClient = class {
16791
16887
  getConfig() {
16792
16888
  return this.config;
16793
16889
  }
16794
- async createSession(input = {}) {
16795
- const response = await this.request("/v1/sessions", {
16796
- method: "POST",
16797
- body: {
16798
- ...input.name === void 0 ? {} : { name: input.name },
16799
- ...input.browser === void 0 ? {} : { browser: input.browser },
16800
- ...input.context === void 0 ? {} : { context: input.context },
16801
- ...input.browserProfile === void 0 ? {} : { browserProfile: input.browserProfile },
16802
- ...input.observability === void 0 ? {} : { observability: input.observability },
16803
- ...input.sourceType === void 0 ? {} : { sourceType: input.sourceType },
16804
- ...input.sourceRef === void 0 ? {} : { sourceRef: input.sourceRef },
16805
- ...input.localWorkspaceRootPath === void 0 ? {} : { localWorkspaceRootPath: input.localWorkspaceRootPath },
16806
- ...input.locality === void 0 ? {} : { locality: input.locality }
16807
- }
16808
- });
16890
+ async createSession(input = {}, options = {}) {
16891
+ const response = await this.request(
16892
+ "/v1/sessions",
16893
+ {
16894
+ method: "POST",
16895
+ body: {
16896
+ ...input.name === void 0 ? {} : { name: input.name },
16897
+ ...input.browser === void 0 ? {} : { browser: input.browser },
16898
+ ...input.context === void 0 ? {} : { context: input.context },
16899
+ ...input.browserProfile === void 0 ? {} : { browserProfile: input.browserProfile },
16900
+ ...input.observability === void 0 ? {} : { observability: input.observability },
16901
+ ...input.sourceType === void 0 ? {} : { sourceType: input.sourceType },
16902
+ ...input.sourceRef === void 0 ? {} : { sourceRef: input.sourceRef },
16903
+ ...input.localWorkspaceRootPath === void 0 ? {} : { localWorkspaceRootPath: input.localWorkspaceRootPath }
16904
+ }
16905
+ },
16906
+ options
16907
+ );
16809
16908
  return await response.json();
16810
16909
  }
16811
16910
  async listSessions() {
@@ -16814,19 +16913,27 @@ var OpensteerCloudClient = class {
16814
16913
  });
16815
16914
  return response.json();
16816
16915
  }
16817
- async getSession(sessionId) {
16818
- const response = await this.request(`/v1/sessions/${encodeURIComponent(sessionId)}`, {
16819
- method: "GET"
16820
- });
16916
+ async getSession(sessionId, options = {}) {
16917
+ const response = await this.request(
16918
+ `/v1/sessions/${encodeURIComponent(sessionId)}`,
16919
+ {
16920
+ method: "GET"
16921
+ },
16922
+ options
16923
+ );
16821
16924
  return await response.json();
16822
16925
  }
16823
- async issueAccess(sessionId, capabilities) {
16824
- const response = await this.request(`/v1/sessions/${encodeURIComponent(sessionId)}/access`, {
16825
- method: "POST",
16826
- body: {
16827
- capabilities
16828
- }
16829
- });
16926
+ async issueAccess(sessionId, capabilities, options = {}) {
16927
+ const response = await this.request(
16928
+ `/v1/sessions/${encodeURIComponent(sessionId)}/access`,
16929
+ {
16930
+ method: "POST",
16931
+ body: {
16932
+ capabilities
16933
+ }
16934
+ },
16935
+ options
16936
+ );
16830
16937
  return await response.json();
16831
16938
  }
16832
16939
  async getSessionRecording(sessionId) {
@@ -16908,28 +17015,17 @@ var OpensteerCloudClient = class {
16908
17015
  async syncBrowserProfileCookies(input) {
16909
17016
  return syncBrowserProfileCookies(this, input);
16910
17017
  }
16911
- async importSelectorCache(entries) {
16912
- const response = await this.request("/selector-cache/import", {
17018
+ async importDescriptors(entries) {
17019
+ const response = await this.request("/registry/descriptors/import", {
16913
17020
  method: "POST",
16914
- body: {
16915
- entries: entries.map((entry) => ({
16916
- workspace: entry.workspace,
16917
- method: entry.method,
16918
- persistHash: entry.persistHash,
16919
- ...entry.persist === void 0 ? {} : { persist: entry.persist },
16920
- path: entry.path,
16921
- ...entry.schemaHash === void 0 ? {} : { schemaHash: entry.schemaHash },
16922
- createdAt: entry.createdAt,
16923
- updatedAt: entry.updatedAt
16924
- }))
16925
- }
17021
+ body: { entries }
16926
17022
  });
16927
17023
  return await response.json();
16928
17024
  }
16929
- async importDescriptors(entries) {
16930
- const response = await this.request("/registry/descriptors/import", {
17025
+ async importRequestPlans(input) {
17026
+ const response = await this.request("/registry/request-plans/import", {
16931
17027
  method: "POST",
16932
- body: { entries }
17028
+ body: input
16933
17029
  });
16934
17030
  return await response.json();
16935
17031
  }
@@ -16942,7 +17038,7 @@ var OpensteerCloudClient = class {
16942
17038
  "content-type": "application/json; charset=utf-8"
16943
17039
  };
16944
17040
  }
16945
- async request(pathname, init) {
17041
+ async request(pathname, init, options = {}) {
16946
17042
  const url = `${this.config.baseUrl}${pathname}`;
16947
17043
  let response;
16948
17044
  try {
@@ -16950,7 +17046,7 @@ var OpensteerCloudClient = class {
16950
17046
  method: init.method,
16951
17047
  headers: this.buildHeaders(),
16952
17048
  ...init.body === void 0 ? {} : { body: JSON.stringify(init.body) },
16953
- signal: AbortSignal.timeout(3e4)
17049
+ signal: createRequestSignal(options)
16954
17050
  });
16955
17051
  } catch (error) {
16956
17052
  throw wrapCloudFetchError(error, {
@@ -16980,6 +17076,13 @@ var OpensteerCloudClient = class {
16980
17076
  throw new Error(`Timed out waiting for cloud session ${sessionId} to close.`);
16981
17077
  }
16982
17078
  };
17079
+ function createRequestSignal(options) {
17080
+ const timeoutSignal = AbortSignal.timeout(options.timeoutMs ?? 3e4);
17081
+ if (options.signal === void 0) {
17082
+ return timeoutSignal;
17083
+ }
17084
+ return AbortSignal.any([options.signal, timeoutSignal]);
17085
+ }
16983
17086
  function delay(ms) {
16984
17087
  return new Promise((resolve4) => {
16985
17088
  setTimeout(resolve4, ms);
@@ -17030,7 +17133,7 @@ function resolveCloudConfig(input = {}) {
17030
17133
 
17031
17134
  // ../runtime-core/package.json
17032
17135
  var package_default = {
17033
- version: "0.1.7"};
17136
+ version: "0.2.0"};
17034
17137
 
17035
17138
  // ../runtime-core/src/version.ts
17036
17139
  var OPENSTEER_RUNTIME_CORE_VERSION = package_default.version;
@@ -20848,6 +20951,9 @@ var MUTATION_CAPTURE_FINALIZE_TIMEOUT_MS = 5e3;
20848
20951
  var PERSISTED_NETWORK_FLUSH_TIMEOUT_MS = 5e3;
20849
20952
  var PENDING_OPERATION_EVENT_CAPTURE_LIMIT = 64;
20850
20953
  var PENDING_OPERATION_EVENT_CAPTURE_SKEW_MS = 1e3;
20954
+ var REPLAY_PROBE_MIN_ATTEMPT_TIMEOUT_MS = 3e3;
20955
+ var REPLAY_PROBE_MAX_ATTEMPT_TIMEOUT_MS = 15e3;
20956
+ var REPLAY_PROBE_POST_SUCCESS_ATTEMPT_TIMEOUT_MS = 5e3;
20851
20957
  var OpensteerSessionRuntime = class {
20852
20958
  workspace;
20853
20959
  rootPath;
@@ -21709,26 +21815,27 @@ var OpensteerSessionRuntime = class {
21709
21815
  }
21710
21816
  }
21711
21817
  async queryNetwork(input = {}, options = {}) {
21712
- assertValidSemanticOperationInput("network.query", input);
21818
+ const normalizedInput = normalizeNetworkQueryInput(input);
21819
+ assertValidSemanticOperationInput("network.query", normalizedInput);
21713
21820
  const root = await this.ensureRoot();
21714
21821
  const startedAt = Date.now();
21715
21822
  try {
21716
21823
  const output = await this.runWithOperationTimeout(
21717
21824
  "network.query",
21718
21825
  async (timeout) => {
21719
- await this.syncPersistedNetworkSelection(timeout, input, {
21826
+ await this.syncPersistedNetworkSelection(timeout, normalizedInput, {
21720
21827
  includeBodies: false
21721
21828
  });
21722
21829
  const rawRecords = await timeout.runStep(
21723
21830
  () => root.registry.savedNetwork.query({
21724
- ...this.toSavedNetworkQueryInput(input),
21725
- limit: Math.max(input.limit ?? 50, 1e3)
21831
+ ...this.toSavedNetworkQueryInput(normalizedInput),
21832
+ limit: Math.max(normalizedInput.limit ?? 50, 1e3)
21726
21833
  })
21727
21834
  );
21728
- const filtered = filterNetworkSummaryRecords(rawRecords, input);
21835
+ const filtered = filterNetworkSummaryRecords(rawRecords, normalizedInput);
21729
21836
  const sorted = sortPersistedNetworkRecordsChronologically(filtered);
21730
- const sliced = sliceNetworkSummaryWindow(sorted, input);
21731
- const limited = sliced.slice(0, Math.max(1, Math.min(input.limit ?? 50, 200)));
21837
+ const sliced = sliceNetworkSummaryWindow(sorted, normalizedInput);
21838
+ const limited = sliced.slice(0, Math.max(1, Math.min(normalizedInput.limit ?? 50, 200)));
21732
21839
  const summaries = await this.buildNetworkSummaryRecords(limited, timeout);
21733
21840
  return {
21734
21841
  records: summaries
@@ -21742,9 +21849,9 @@ var OpensteerSessionRuntime = class {
21742
21849
  completedAt: Date.now(),
21743
21850
  outcome: "ok",
21744
21851
  data: {
21745
- limit: input.limit ?? 50,
21746
- ...input.capture === void 0 ? {} : { capture: input.capture },
21747
- ...input.json === true ? { json: true } : {},
21852
+ limit: normalizedInput.limit ?? 50,
21853
+ ...normalizedInput.capture === void 0 ? {} : { capture: normalizedInput.capture },
21854
+ ...normalizedInput.json === true ? { json: true } : {},
21748
21855
  count: output.records.length
21749
21856
  },
21750
21857
  context: buildRuntimeTraceContext({
@@ -21769,12 +21876,13 @@ var OpensteerSessionRuntime = class {
21769
21876
  }
21770
21877
  }
21771
21878
  async getNetworkDetail(input, options = {}) {
21879
+ const normalizedRecordId = normalizeNetworkRecordId(input.recordId);
21772
21880
  const startedAt = Date.now();
21773
21881
  try {
21774
21882
  const output = await this.runWithOperationTimeout(
21775
21883
  "network.detail",
21776
21884
  async (timeout) => {
21777
- const record = await this.resolveNetworkRecordByRecordId(input.recordId, timeout, {
21885
+ const record = await this.resolveNetworkRecordByRecordId(normalizedRecordId, timeout, {
21778
21886
  includeBodies: true,
21779
21887
  redactSecretHeaders: false
21780
21888
  });
@@ -21793,8 +21901,8 @@ var OpensteerSessionRuntime = class {
21793
21901
  completedAt: Date.now(),
21794
21902
  outcome: "ok",
21795
21903
  data: {
21796
- recordId: input.recordId,
21797
- status: output.summary.status,
21904
+ recordId: normalizedRecordId,
21905
+ ...output.summary.status === void 0 ? {} : { status: output.summary.status },
21798
21906
  url: output.summary.url
21799
21907
  },
21800
21908
  context: buildRuntimeTraceContext({
@@ -23198,7 +23306,9 @@ var OpensteerSessionRuntime = class {
23198
23306
  ...graphql.persisted === void 0 ? {} : { persisted: graphql.persisted },
23199
23307
  ...graphqlVariables === void 0 ? {} : { variables: graphqlVariables }
23200
23308
  };
23201
- const requestBody = shouldShowRequestBody(record.record.method) && record.record.requestBody !== void 0 ? buildStructuredBodyPreview(record.record.requestBody, record.record.requestHeaders) : void 0;
23309
+ const requestBody = shouldShowRequestBody(record.record.method) && record.record.requestBody !== void 0 ? buildStructuredBodyPreview(record.record.requestBody, record.record.requestHeaders, {
23310
+ truncateData: false
23311
+ }) : void 0;
23202
23312
  const responseBody = record.record.responseBody === void 0 ? void 0 : buildStructuredBodyPreview(record.record.responseBody, record.record.responseHeaders);
23203
23313
  const notes = detectNetworkRecordNotes(record);
23204
23314
  return {
@@ -23228,8 +23338,18 @@ var OpensteerSessionRuntime = class {
23228
23338
  let recommended;
23229
23339
  for (const transport of REPLAY_TRANSPORT_LADDER) {
23230
23340
  const attemptStartedAt = Date.now();
23341
+ const attemptTimeoutMs = resolveReplayProbeAttemptTimeoutMs({
23342
+ remainingMs: timeout.remainingMs(),
23343
+ transportsRemaining: REPLAY_TRANSPORT_LADDER.length - attempts.length,
23344
+ recommendedFound: recommended !== void 0
23345
+ });
23231
23346
  try {
23232
- const output = await this.executeReplayTransportAttempt(transport, request, timeout);
23347
+ const output = await this.executeReplayTransportAttemptWithinBudget(
23348
+ transport,
23349
+ request,
23350
+ timeout,
23351
+ attemptTimeoutMs
23352
+ );
23233
23353
  const ok = matchesSuccessFingerprintFromProtocolResponse(output.response, fingerprint);
23234
23354
  attempts.push({
23235
23355
  transport,
@@ -23245,7 +23365,7 @@ var OpensteerSessionRuntime = class {
23245
23365
  transport,
23246
23366
  ok: false,
23247
23367
  durationMs: Date.now() - attemptStartedAt,
23248
- error: normalizeRuntimeErrorMessage(error)
23368
+ error: normalizeProbeTransportAttemptError(transport, error, attemptTimeoutMs)
23249
23369
  });
23250
23370
  }
23251
23371
  }
@@ -23449,6 +23569,23 @@ var OpensteerSessionRuntime = class {
23449
23569
  }
23450
23570
  }
23451
23571
  }
23572
+ async executeReplayTransportAttemptWithinBudget(transport, request, timeout, attemptTimeoutMs) {
23573
+ if (attemptTimeoutMs === void 0) {
23574
+ return this.executeReplayTransportAttempt(transport, request, timeout);
23575
+ }
23576
+ return runWithPolicyTimeout(
23577
+ {
23578
+ resolveTimeoutMs() {
23579
+ return attemptTimeoutMs;
23580
+ }
23581
+ },
23582
+ {
23583
+ operation: timeout.operation,
23584
+ signal: timeout.signal
23585
+ },
23586
+ (attemptTimeout) => this.executeReplayTransportAttempt(transport, request, attemptTimeout)
23587
+ );
23588
+ }
23452
23589
  async executeFetchTransportAttempt(transport, request, timeout, input) {
23453
23590
  let prepared = finalizeMaterializedTransportRequest(request, transport);
23454
23591
  if (input.cookies !== false && transport === "direct-http" && this.currentBinding() !== void 0) {
@@ -24363,10 +24500,15 @@ var OpensteerSessionRuntime = class {
24363
24500
  return this.observationSessionId ?? this.sessionRef;
24364
24501
  }
24365
24502
  runWithOperationTimeout(operation, callback, options = {}) {
24503
+ const timeoutPolicy = options.timeoutMs === void 0 ? this.policy.timeout : {
24504
+ resolveTimeoutMs() {
24505
+ return options.timeoutMs;
24506
+ }
24507
+ };
24366
24508
  const existingCollector = this.operationEventStorage.getStore();
24367
24509
  if (existingCollector !== void 0) {
24368
24510
  return runWithPolicyTimeout(
24369
- this.policy.timeout,
24511
+ timeoutPolicy,
24370
24512
  {
24371
24513
  operation,
24372
24514
  ...options.signal === void 0 ? {} : { signal: options.signal }
@@ -24379,7 +24521,7 @@ var OpensteerSessionRuntime = class {
24379
24521
  return this.operationEventStorage.run(collector, async () => {
24380
24522
  try {
24381
24523
  return await runWithPolicyTimeout(
24382
- this.policy.timeout,
24524
+ timeoutPolicy,
24383
24525
  {
24384
24526
  operation,
24385
24527
  ...options.signal === void 0 ? {} : { signal: options.signal }
@@ -24549,6 +24691,21 @@ function buildEngineNetworkRecordFilters(input) {
24549
24691
  function normalizeNetworkStatusFilter(status) {
24550
24692
  return String(status);
24551
24693
  }
24694
+ function normalizeNetworkQueryInput(input) {
24695
+ return {
24696
+ ...input,
24697
+ ...input.recordId === void 0 ? {} : { recordId: normalizeNetworkRecordId(input.recordId) },
24698
+ ...input.before === void 0 ? {} : { before: normalizeNetworkRecordId(input.before) },
24699
+ ...input.after === void 0 ? {} : { after: normalizeNetworkRecordId(input.after) }
24700
+ };
24701
+ }
24702
+ function normalizeNetworkRecordId(recordId) {
24703
+ const trimmed = recordId.trim();
24704
+ if (trimmed.length === 0 || trimmed.startsWith("record:")) {
24705
+ return trimmed;
24706
+ }
24707
+ return `record:${trimmed}`;
24708
+ }
24552
24709
  function resolveLiveQueryRequestIds(input, history) {
24553
24710
  const requestIdCandidates = [];
24554
24711
  if (input.recordId !== void 0) {
@@ -24761,6 +24918,20 @@ var REPLAY_TRANSPORT_LADDER = [
24761
24918
  "context-http",
24762
24919
  "page-http"
24763
24920
  ];
24921
+ function resolveReplayProbeAttemptTimeoutMs(input) {
24922
+ const attemptCapMs = input.recommendedFound ? REPLAY_PROBE_POST_SUCCESS_ATTEMPT_TIMEOUT_MS : REPLAY_PROBE_MAX_ATTEMPT_TIMEOUT_MS;
24923
+ const clampedRemaining = input.remainingMs === void 0 ? void 0 : Math.max(0, input.remainingMs);
24924
+ if (clampedRemaining === 0) {
24925
+ return 0;
24926
+ }
24927
+ if (clampedRemaining === void 0) {
24928
+ return attemptCapMs;
24929
+ }
24930
+ const sliceMs = Math.floor(clampedRemaining / Math.max(1, input.transportsRemaining));
24931
+ const minimumBudgetAffordable = clampedRemaining >= REPLAY_PROBE_MIN_ATTEMPT_TIMEOUT_MS * input.transportsRemaining;
24932
+ const attemptBudgetMs = minimumBudgetAffordable ? Math.max(REPLAY_PROBE_MIN_ATTEMPT_TIMEOUT_MS, sliceMs) : sliceMs;
24933
+ return Math.min(clampedRemaining, attemptCapMs, Math.max(1, attemptBudgetMs));
24934
+ }
24764
24935
  function filterNetworkSummaryRecords(records, input) {
24765
24936
  return records.filter((record) => {
24766
24937
  if (record.record.resourceType === "preflight" || record.record.method === "OPTIONS") {
@@ -24933,10 +25104,10 @@ function extractGraphqlOperationName(queryText) {
24933
25104
  function shouldShowRequestBody(method) {
24934
25105
  return !["GET", "HEAD", "DELETE", "OPTIONS"].includes(method.trim().toUpperCase());
24935
25106
  }
24936
- function buildStructuredBodyPreview(body, headers) {
25107
+ function buildStructuredBodyPreview(body, headers, options = {}) {
24937
25108
  const contentType = headerValue(headers, "content-type") ?? body?.mimeType;
24938
25109
  const parsed = parseStructuredPayload(body, contentType);
24939
- const data = parsed === void 0 ? void 0 : typeof parsed === "string" ? truncateInlineText(parsed) : truncateStructuredValue(parsed);
25110
+ const data = parsed === void 0 ? void 0 : options.truncateData === false ? parsed : typeof parsed === "string" ? truncateInlineText(parsed) : truncateStructuredValue(parsed);
24940
25111
  return {
24941
25112
  bytes: body?.originalByteLength ?? body?.capturedByteLength ?? 0,
24942
25113
  ...contentType === void 0 ? {} : { contentType },
@@ -25149,10 +25320,12 @@ function resolveSessionFetchTransportLadder(transport) {
25149
25320
  return ["direct-http"];
25150
25321
  case "matched-tls":
25151
25322
  return ["matched-tls"];
25323
+ case "context":
25324
+ return ["context-http"];
25152
25325
  case "page":
25153
25326
  return ["page-http"];
25154
25327
  case "auto":
25155
- return ["direct-http", "matched-tls", "page-http"];
25328
+ return ["direct-http", "matched-tls", "context-http", "page-http"];
25156
25329
  }
25157
25330
  }
25158
25331
  function detectChallengeNoteFromRecord(record) {
@@ -25297,6 +25470,12 @@ function diffStorageSnapshot(left, right) {
25297
25470
  function normalizeRuntimeErrorMessage(error) {
25298
25471
  return error instanceof Error ? error.message : String(error);
25299
25472
  }
25473
+ function normalizeProbeTransportAttemptError(transport, error, attemptTimeoutMs) {
25474
+ if (attemptTimeoutMs !== void 0 && error instanceof OpensteerProtocolError && error.code === "timeout") {
25475
+ return `${transport} probe exceeded ${String(attemptTimeoutMs)}ms`;
25476
+ }
25477
+ return normalizeRuntimeErrorMessage(error);
25478
+ }
25300
25479
  function applyBrowserCookiesToTransportRequest(request, cookies) {
25301
25480
  if (cookies.length === 0) {
25302
25481
  return request;
@@ -26064,10 +26243,10 @@ var OpensteerSemanticRestClient = class {
26064
26243
  constructor(connection) {
26065
26244
  this.connection = connection;
26066
26245
  }
26067
- async invoke(operation, input) {
26068
- return this.invokeInternal(operation, input, false);
26246
+ async invoke(operation, input, options = {}) {
26247
+ return this.invokeInternal(operation, input, false, options);
26069
26248
  }
26070
- async invokeInternal(operation, input, hasRetried) {
26249
+ async invokeInternal(operation, input, hasRetried, options) {
26071
26250
  const endpoint = opensteerSemanticRestEndpoints.find((entry) => entry.name === operation);
26072
26251
  if (!endpoint) {
26073
26252
  throw new Error(`unsupported semantic operation ${operation}`);
@@ -26081,10 +26260,11 @@ var OpensteerSemanticRestClient = class {
26081
26260
  method: "POST",
26082
26261
  headers: {
26083
26262
  authorization: await this.connection.getAuthorizationHeader(),
26084
- "content-type": "application/json; charset=utf-8"
26263
+ "content-type": "application/json; charset=utf-8",
26264
+ ...options.timeoutMs === void 0 ? {} : { "x-opensteer-timeout-ms": String(options.timeoutMs) }
26085
26265
  },
26086
26266
  body: JSON.stringify(request),
26087
- signal: AbortSignal.timeout(3e4)
26267
+ signal: createRequestSignal2(options)
26088
26268
  });
26089
26269
  } catch (error) {
26090
26270
  if (operation === "session.close" && isFetchFailure(error)) {
@@ -26100,7 +26280,7 @@ var OpensteerSemanticRestClient = class {
26100
26280
  return envelope.data;
26101
26281
  } catch (error) {
26102
26282
  if (!hasRetried && this.connection.handleError && await this.connection.handleError(error, { operation })) {
26103
- return this.invokeInternal(operation, input, true);
26283
+ return this.invokeInternal(operation, input, true, options);
26104
26284
  }
26105
26285
  if (operation === "session.close" && isFetchFailure(error)) {
26106
26286
  return { closed: true };
@@ -26112,6 +26292,13 @@ var OpensteerSemanticRestClient = class {
26112
26292
  return this.invoke("session.close", {});
26113
26293
  }
26114
26294
  };
26295
+ function createRequestSignal2(options) {
26296
+ const timeoutSignal = AbortSignal.timeout(options.timeoutMs ?? 3e4);
26297
+ if (options.signal === void 0) {
26298
+ return timeoutSignal;
26299
+ }
26300
+ return AbortSignal.any([options.signal, timeoutSignal]);
26301
+ }
26115
26302
  function isFetchFailure(error) {
26116
26303
  if (!(error instanceof Error)) {
26117
26304
  return false;
@@ -26478,13 +26665,44 @@ function asRecord(value) {
26478
26665
  return value;
26479
26666
  }
26480
26667
 
26481
- // src/cloud/registry-sync.ts
26482
- var REGISTRY_SYNC_MAX_PAYLOAD_BYTES = 15e5;
26483
- var REGISTRY_SYNC_MAX_ENTRIES_PER_BATCH = 100;
26484
- async function syncLocalRegistryToCloud(client, workspace, store) {
26668
+ // src/cloud/workspace-sync.ts
26669
+ var WORKSPACE_SYNC_MAX_PAYLOAD_BYTES = 15e5;
26670
+ var WORKSPACE_SYNC_MAX_ENTRIES_PER_BATCH = 100;
26671
+ async function syncLocalWorkspaceToCloud(client, workspace, store) {
26672
+ await syncDescriptorRegistryToCloud(client, workspace, store);
26673
+ await syncRequestPlansToCloud(client, workspace, store);
26674
+ }
26675
+ async function syncDescriptorRegistryToCloud(client, workspace, store) {
26485
26676
  const descriptors = await store.registry.descriptors.list();
26486
- const descriptorEntries = descriptors.map((record) => toDescriptorImportEntry(workspace, record));
26487
- await importInBatches(descriptorEntries, (entries) => client.importDescriptors(entries));
26677
+ const entries = descriptors.map((record) => toDescriptorImportEntry(workspace, record));
26678
+ await importInBatches(entries, {
26679
+ getPayloadByteLength: (batch) => payloadByteLength({ entries: batch }),
26680
+ importBatch: (batch) => client.importDescriptors(batch)
26681
+ });
26682
+ }
26683
+ async function syncRequestPlansToCloud(client, workspace, store) {
26684
+ const requestPlans = await store.registry.requestPlans.list();
26685
+ const entries = requestPlans.map((record) => toRequestPlanImportEntry(workspace, record)).filter((entry) => entry !== void 0);
26686
+ await importInBatches(entries, {
26687
+ getPayloadByteLength: (batch) => payloadByteLength({ entries: batch }),
26688
+ importBatch: (batch) => client.importRequestPlans({ entries: batch })
26689
+ });
26690
+ }
26691
+ function toRequestPlanImportEntry(workspace, record) {
26692
+ const entry = {
26693
+ workspace,
26694
+ recordId: record.id,
26695
+ key: record.key,
26696
+ version: record.version,
26697
+ contentHash: record.contentHash,
26698
+ tags: [...record.tags],
26699
+ ...record.provenance === void 0 ? {} : { provenance: record.provenance },
26700
+ ...record.freshness === void 0 ? {} : { freshness: record.freshness },
26701
+ payload: record.payload,
26702
+ createdAt: record.createdAt,
26703
+ updatedAt: record.updatedAt
26704
+ };
26705
+ return payloadByteLength({ entries: [entry] }) <= WORKSPACE_SYNC_MAX_PAYLOAD_BYTES ? entry : void 0;
26488
26706
  }
26489
26707
  function toDescriptorImportEntry(workspace, record) {
26490
26708
  return {
@@ -26500,19 +26718,19 @@ function toDescriptorImportEntry(workspace, record) {
26500
26718
  updatedAt: record.updatedAt
26501
26719
  };
26502
26720
  }
26503
- async function importInBatches(entries, importBatch) {
26721
+ async function importInBatches(entries, options) {
26504
26722
  if (entries.length === 0) {
26505
26723
  return;
26506
26724
  }
26507
- for (const batch of chunkEntries(entries)) {
26508
- await importBatch(batch);
26725
+ for (const batch of chunkEntries(entries, options.getPayloadByteLength)) {
26726
+ await options.importBatch(batch);
26509
26727
  }
26510
26728
  }
26511
- function chunkEntries(entries) {
26729
+ function chunkEntries(entries, getPayloadByteLength) {
26512
26730
  const batches = [];
26513
26731
  let currentBatch = [];
26514
26732
  for (const entry of entries) {
26515
- if (payloadByteLength([entry]) > REGISTRY_SYNC_MAX_PAYLOAD_BYTES) {
26733
+ if (getPayloadByteLength([entry]) > WORKSPACE_SYNC_MAX_PAYLOAD_BYTES) {
26516
26734
  continue;
26517
26735
  }
26518
26736
  if (currentBatch.length === 0) {
@@ -26520,7 +26738,7 @@ function chunkEntries(entries) {
26520
26738
  continue;
26521
26739
  }
26522
26740
  const nextBatch = [...currentBatch, entry];
26523
- if (nextBatch.length > REGISTRY_SYNC_MAX_ENTRIES_PER_BATCH || payloadByteLength(nextBatch) > REGISTRY_SYNC_MAX_PAYLOAD_BYTES) {
26741
+ if (nextBatch.length > WORKSPACE_SYNC_MAX_ENTRIES_PER_BATCH || getPayloadByteLength(nextBatch) > WORKSPACE_SYNC_MAX_PAYLOAD_BYTES) {
26524
26742
  batches.push(currentBatch);
26525
26743
  currentBatch = [entry];
26526
26744
  continue;
@@ -26532,8 +26750,8 @@ function chunkEntries(entries) {
26532
26750
  }
26533
26751
  return batches;
26534
26752
  }
26535
- function payloadByteLength(entries) {
26536
- return Buffer.byteLength(JSON.stringify({ entries }), "utf8");
26753
+ function payloadByteLength(value) {
26754
+ return Buffer.byteLength(JSON.stringify(value), "utf8");
26537
26755
  }
26538
26756
 
26539
26757
  // src/cloud/session-proxy.ts
@@ -26544,14 +26762,17 @@ var CloudSessionProxy = class {
26544
26762
  cleanupRootOnClose;
26545
26763
  cloud;
26546
26764
  observability;
26765
+ policy;
26547
26766
  sessionId;
26548
26767
  semanticGrant;
26549
26768
  client;
26550
26769
  automation;
26551
26770
  workspaceStore;
26771
+ syncWorkspaceOnClose = false;
26552
26772
  constructor(cloud, options = {}) {
26553
26773
  this.cloud = cloud;
26554
26774
  this.workspace = options.workspace;
26775
+ this.policy = options.policy ?? defaultPolicy();
26555
26776
  this.observability = options.observability;
26556
26777
  this.rootPath = options.rootPath ?? (this.workspace === void 0 ? path7__default.default.join(os.tmpdir(), `${TEMPORARY_CLOUD_WORKSPACE_PREFIX}${crypto.randomUUID()}`) : resolveFilesystemWorkspacePath({
26557
26778
  rootDir: path7__default.default.resolve(options.rootDir ?? process.cwd()),
@@ -26560,14 +26781,17 @@ var CloudSessionProxy = class {
26560
26781
  this.cleanupRootOnClose = options.cleanupRootOnClose ?? this.workspace === void 0;
26561
26782
  }
26562
26783
  async open(input = {}) {
26563
- await this.ensureSession({
26564
- ...input.browser === void 0 ? {} : { browser: input.browser },
26565
- ...input.launch === void 0 ? {} : { launch: input.launch },
26566
- ...input.context === void 0 ? {} : { context: input.context }
26567
- });
26568
- return this.requireClient().invoke("session.open", {
26569
- ...input.url === void 0 ? {} : { url: input.url }
26570
- });
26784
+ return this.invokeSemanticOperation(
26785
+ "session.open",
26786
+ {
26787
+ ...input.url === void 0 ? {} : { url: input.url }
26788
+ },
26789
+ {
26790
+ ...input.browser === void 0 ? {} : { browser: input.browser },
26791
+ ...input.launch === void 0 ? {} : { launch: input.launch },
26792
+ ...input.context === void 0 ? {} : { context: input.context }
26793
+ }
26794
+ );
26571
26795
  }
26572
26796
  async info() {
26573
26797
  const persisted = this.client !== void 0 || this.sessionId !== void 0 ? void 0 : await this.loadPersistedSession();
@@ -26610,108 +26834,88 @@ var CloudSessionProxy = class {
26610
26834
  };
26611
26835
  }
26612
26836
  async listPages(input = {}) {
26613
- await this.ensureSession();
26614
- return this.requireClient().invoke("page.list", input);
26837
+ return this.invokeSemanticOperation("page.list", input);
26615
26838
  }
26616
26839
  async newPage(input = {}) {
26617
- await this.ensureSession();
26618
- return this.requireAutomation().invoke("page.new", input);
26840
+ return this.invokeAutomationOperation(
26841
+ "page.new",
26842
+ (automation) => automation.invoke("page.new", input)
26843
+ );
26619
26844
  }
26620
26845
  async activatePage(input) {
26621
- await this.ensureSession();
26622
- return this.requireClient().invoke("page.activate", input);
26846
+ return this.invokeSemanticOperation("page.activate", input);
26623
26847
  }
26624
26848
  async closePage(input = {}) {
26625
- await this.ensureSession();
26626
- return this.requireClient().invoke("page.close", input);
26849
+ return this.invokeSemanticOperation("page.close", input);
26627
26850
  }
26628
26851
  async goto(input) {
26629
- await this.ensureSession();
26630
- return this.requireClient().invoke("page.goto", input);
26852
+ return this.invokeSemanticOperation("page.goto", input);
26631
26853
  }
26632
26854
  async evaluate(input) {
26633
- await this.ensureSession();
26634
- return this.requireAutomation().invoke("page.evaluate", input);
26855
+ return this.invokeAutomationOperation(
26856
+ "page.evaluate",
26857
+ (automation) => automation.invoke("page.evaluate", input)
26858
+ );
26635
26859
  }
26636
26860
  async addInitScript(input) {
26637
- await this.ensureSession();
26638
- return this.requireClient().invoke("page.add-init-script", input);
26861
+ return this.invokeSemanticOperation("page.add-init-script", input);
26639
26862
  }
26640
26863
  async snapshot(input = {}) {
26641
- await this.ensureSession();
26642
- return this.requireClient().invoke("page.snapshot", input);
26864
+ return this.invokeSemanticOperation("page.snapshot", input);
26643
26865
  }
26644
26866
  async click(input) {
26645
- await this.ensureSession();
26646
- return this.requireClient().invoke("dom.click", input);
26867
+ return this.invokeSemanticOperation("dom.click", input);
26647
26868
  }
26648
26869
  async hover(input) {
26649
- await this.ensureSession();
26650
- return this.requireClient().invoke("dom.hover", input);
26870
+ return this.invokeSemanticOperation("dom.hover", input);
26651
26871
  }
26652
26872
  async input(input) {
26653
- await this.ensureSession();
26654
- return this.requireClient().invoke("dom.input", input);
26873
+ return this.invokeSemanticOperation("dom.input", input);
26655
26874
  }
26656
26875
  async scroll(input) {
26657
- await this.ensureSession();
26658
- return this.requireClient().invoke("dom.scroll", input);
26876
+ return this.invokeSemanticOperation("dom.scroll", input);
26659
26877
  }
26660
26878
  async extract(input) {
26661
- await this.ensureSession();
26662
- return this.requireClient().invoke("dom.extract", input);
26879
+ return this.invokeSemanticOperation("dom.extract", input);
26663
26880
  }
26664
26881
  async queryNetwork(input = {}) {
26665
- await this.ensureSession();
26666
- return this.requireClient().invoke("network.query", input);
26882
+ return this.invokeSemanticOperation("network.query", input);
26667
26883
  }
26668
26884
  async getNetworkDetail(input) {
26669
- await this.ensureSession();
26670
- return this.requireClient().invoke("network.detail", input);
26885
+ return this.invokeSemanticOperation("network.detail", input);
26671
26886
  }
26672
26887
  async captureInteraction(input) {
26673
- await this.ensureSession();
26674
- return this.requireClient().invoke("interaction.capture", input);
26888
+ return this.invokeSemanticOperation("interaction.capture", input);
26675
26889
  }
26676
26890
  async getInteraction(input) {
26677
- await this.ensureSession();
26678
- return this.requireClient().invoke("interaction.get", input);
26891
+ return this.invokeSemanticOperation("interaction.get", input);
26679
26892
  }
26680
26893
  async diffInteraction(input) {
26681
- await this.ensureSession();
26682
- return this.requireClient().invoke("interaction.diff", input);
26894
+ return this.invokeSemanticOperation("interaction.diff", input);
26683
26895
  }
26684
26896
  async replayInteraction(input) {
26685
- await this.ensureSession();
26686
- return this.requireClient().invoke("interaction.replay", input);
26897
+ return this.invokeSemanticOperation("interaction.replay", input);
26687
26898
  }
26688
26899
  async captureScripts(input = {}) {
26689
- await this.ensureSession();
26690
- return this.requireClient().invoke("scripts.capture", input);
26900
+ return this.invokeSemanticOperation("scripts.capture", input);
26691
26901
  }
26692
26902
  async readArtifact(input) {
26693
- await this.ensureSession();
26694
- return this.requireClient().invoke("artifact.read", input);
26903
+ return this.invokeSemanticOperation("artifact.read", input);
26695
26904
  }
26696
26905
  async beautifyScript(input) {
26697
- await this.ensureSession();
26698
- return this.requireClient().invoke("scripts.beautify", input);
26906
+ return this.invokeSemanticOperation("scripts.beautify", input);
26699
26907
  }
26700
26908
  async deobfuscateScript(input) {
26701
- await this.ensureSession();
26702
- return this.requireClient().invoke("scripts.deobfuscate", input);
26909
+ return this.invokeSemanticOperation("scripts.deobfuscate", input);
26703
26910
  }
26704
26911
  async sandboxScript(input) {
26705
- await this.ensureSession();
26706
- return this.requireClient().invoke("scripts.sandbox", input);
26912
+ return this.invokeSemanticOperation("scripts.sandbox", input);
26707
26913
  }
26708
26914
  async solveCaptcha(input) {
26709
- await this.ensureSession();
26710
- return this.requireClient().invoke("captcha.solve", input);
26915
+ return this.invokeSemanticOperation("captcha.solve", input);
26711
26916
  }
26712
26917
  async getCookies(input = {}) {
26713
- await this.ensureSession();
26714
- return this.requireClient().invoke("session.cookies", input);
26918
+ return this.invokeSemanticOperation("session.cookies", input);
26715
26919
  }
26716
26920
  async route(input) {
26717
26921
  await this.ensureSession();
@@ -26722,20 +26926,16 @@ var CloudSessionProxy = class {
26722
26926
  return this.requireAutomation().interceptScript(input);
26723
26927
  }
26724
26928
  async getStorageSnapshot(input = {}) {
26725
- await this.ensureSession();
26726
- return this.requireClient().invoke("session.storage", input);
26929
+ return this.invokeSemanticOperation("session.storage", input);
26727
26930
  }
26728
26931
  async getBrowserState(input = {}) {
26729
- await this.ensureSession();
26730
- return this.requireClient().invoke("session.state", input);
26932
+ return this.invokeSemanticOperation("session.state", input);
26731
26933
  }
26732
26934
  async fetch(input) {
26733
- await this.ensureSession();
26734
- return this.requireClient().invoke("session.fetch", input);
26935
+ return this.invokeSemanticOperation("session.fetch", input);
26735
26936
  }
26736
26937
  async computerExecute(input) {
26737
- await this.ensureSession();
26738
- return this.requireClient().invoke("computer.execute", input);
26938
+ return this.invokeSemanticOperation("computer.execute", input);
26739
26939
  }
26740
26940
  async close() {
26741
26941
  const session = await this.loadPersistedSession() ?? (this.sessionId === void 0 ? void 0 : {
@@ -26747,6 +26947,14 @@ var CloudSessionProxy = class {
26747
26947
  startedAt: Date.now(),
26748
26948
  updatedAt: Date.now()
26749
26949
  });
26950
+ let syncError;
26951
+ if (this.syncWorkspaceOnClose) {
26952
+ try {
26953
+ await this.syncWorkspaceToCloud();
26954
+ } catch (error) {
26955
+ syncError = error;
26956
+ }
26957
+ }
26750
26958
  try {
26751
26959
  if (session !== void 0) {
26752
26960
  await this.cloud.closeSession(session.sessionId).catch((error) => {
@@ -26767,6 +26975,9 @@ var CloudSessionProxy = class {
26767
26975
  await promises.rm(this.rootPath, { recursive: true, force: true }).catch(() => void 0);
26768
26976
  }
26769
26977
  }
26978
+ if (syncError !== void 0) {
26979
+ throw syncError;
26980
+ }
26770
26981
  return { closed: true };
26771
26982
  }
26772
26983
  async disconnect() {
@@ -26774,34 +26985,38 @@ var CloudSessionProxy = class {
26774
26985
  await this.close();
26775
26986
  return;
26776
26987
  }
26988
+ let syncError;
26989
+ if (this.syncWorkspaceOnClose) {
26990
+ try {
26991
+ await this.syncWorkspaceToCloud();
26992
+ } catch (error) {
26993
+ syncError = error;
26994
+ }
26995
+ }
26777
26996
  this.client = void 0;
26778
26997
  await this.automation?.close().catch(() => void 0);
26779
26998
  this.automation = void 0;
26780
26999
  this.sessionId = void 0;
26781
27000
  this.semanticGrant = void 0;
27001
+ if (syncError !== void 0) {
27002
+ throw syncError;
27003
+ }
26782
27004
  }
26783
- async ensureSession(input = {}) {
27005
+ async ensureSession(input = {}, timeout) {
26784
27006
  if (this.client) {
26785
27007
  return;
26786
27008
  }
26787
27009
  assertSupportedCloudBrowserMode(input.browser);
26788
27010
  const localCloud = this.shouldUseLocalCloudTransport();
27011
+ this.syncWorkspaceOnClose = localCloud && this.workspace !== void 0;
26789
27012
  const browserProfile = resolveCloudBrowserProfile(this.cloud, input);
26790
27013
  const persisted = await this.loadPersistedSession();
26791
- if (persisted !== void 0 && await this.isReusableCloudSession(persisted.sessionId)) {
26792
- if (localCloud) {
26793
- void this.syncRegistryToCloud();
26794
- } else {
26795
- await this.syncRegistryToCloud();
26796
- }
27014
+ if (persisted !== void 0 && await this.isReusableCloudSession(persisted.sessionId, timeout)) {
27015
+ await this.syncWorkspaceToCloud();
26797
27016
  this.bindClient(persisted);
26798
27017
  return;
26799
27018
  }
26800
- if (localCloud) {
26801
- void this.syncRegistryToCloud();
26802
- } else {
26803
- await this.syncRegistryToCloud();
26804
- }
27019
+ await this.syncWorkspaceToCloud();
26805
27020
  const baseCreateInput = {
26806
27021
  ...this.workspace === void 0 ? {} : { name: this.workspace },
26807
27022
  ...input.launch === void 0 ? {} : { browser: input.launch },
@@ -26813,10 +27028,12 @@ var CloudSessionProxy = class {
26813
27028
  ...baseCreateInput,
26814
27029
  sourceType: "local-cloud",
26815
27030
  sourceRef: this.workspace,
26816
- localWorkspaceRootPath: this.rootPath,
26817
- locality: "auto"
27031
+ localWorkspaceRootPath: this.rootPath
26818
27032
  } : baseCreateInput;
26819
- const session = await this.cloud.createSession(createInput);
27033
+ const session = await this.cloud.createSession(createInput, {
27034
+ signal: timeout?.signal,
27035
+ timeoutMs: timeout?.remainingMs()
27036
+ });
26820
27037
  const record = {
26821
27038
  layout: "opensteer-session",
26822
27039
  version: 1,
@@ -26829,15 +27046,12 @@ var CloudSessionProxy = class {
26829
27046
  await this.writePersistedSession(record);
26830
27047
  this.bindClient(record, session.initialGrants?.semantic);
26831
27048
  }
26832
- async syncRegistryToCloud() {
27049
+ async syncWorkspaceToCloud() {
26833
27050
  if (this.workspace === void 0) {
26834
27051
  return;
26835
27052
  }
26836
- try {
26837
- const workspaceStore = await this.ensureWorkspaceStore();
26838
- await syncLocalRegistryToCloud(this.cloud, this.workspace, workspaceStore);
26839
- } catch {
26840
- }
27053
+ const workspaceStore = await this.ensureWorkspaceStore();
27054
+ await syncLocalWorkspaceToCloud(this.cloud, this.workspace, workspaceStore);
26841
27055
  }
26842
27056
  bindClient(record, initialSemanticGrant) {
26843
27057
  this.sessionId = record.sessionId;
@@ -26871,9 +27085,12 @@ var CloudSessionProxy = class {
26871
27085
  async clearPersistedSession() {
26872
27086
  await clearPersistedSessionRecord(this.rootPath, "cloud").catch(() => void 0);
26873
27087
  }
26874
- async isReusableCloudSession(sessionId) {
27088
+ async isReusableCloudSession(sessionId, timeout) {
26875
27089
  try {
26876
- const session = await this.cloud.getSession(sessionId);
27090
+ const session = await this.cloud.getSession(sessionId, {
27091
+ signal: timeout?.signal,
27092
+ timeoutMs: timeout?.remainingMs()
27093
+ });
26877
27094
  return session.status !== "closed" && session.status !== "failed";
26878
27095
  } catch (error) {
26879
27096
  if (isMissingCloudSessionError(error)) {
@@ -26894,14 +27111,17 @@ var CloudSessionProxy = class {
26894
27111
  }
26895
27112
  return this.automation;
26896
27113
  }
26897
- async ensureSemanticGrant(forceRefresh = false) {
27114
+ async ensureSemanticGrant(forceRefresh = false, timeout) {
26898
27115
  if (!forceRefresh && this.semanticGrant?.kind === "semantic" && this.semanticGrant.expiresAt > Date.now() + 1e4) {
26899
27116
  return this.semanticGrant;
26900
27117
  }
26901
27118
  if (!this.sessionId) {
26902
27119
  throw new Error("Cloud session has not been initialized.");
26903
27120
  }
26904
- const issued = await this.cloud.issueAccess(this.sessionId, ["semantic"]);
27121
+ const issued = await this.cloud.issueAccess(this.sessionId, ["semantic"], {
27122
+ signal: timeout?.signal,
27123
+ timeoutMs: timeout?.remainingMs()
27124
+ });
26905
27125
  const grant = issued.grants.semantic;
26906
27126
  if (!grant || grant.transport !== "http") {
26907
27127
  throw new Error("cloud did not issue a valid semantic grant");
@@ -26924,6 +27144,25 @@ var CloudSessionProxy = class {
26924
27144
  return false;
26925
27145
  }
26926
27146
  }
27147
+ async invokeSemanticOperation(operation, input, sessionInit = {}) {
27148
+ return this.runOperationWithPolicy(operation, async (timeout) => {
27149
+ await this.ensureSession(sessionInit, timeout);
27150
+ await this.ensureSemanticGrant(false, timeout);
27151
+ return this.requireClient().invoke(operation, input, {
27152
+ signal: timeout.signal,
27153
+ timeoutMs: timeout.remainingMs()
27154
+ });
27155
+ });
27156
+ }
27157
+ async invokeAutomationOperation(operation, invoke, sessionInit = {}) {
27158
+ return this.runOperationWithPolicy(operation, async (timeout) => {
27159
+ await this.ensureSession(sessionInit, timeout);
27160
+ return invoke(this.requireAutomation());
27161
+ });
27162
+ }
27163
+ async runOperationWithPolicy(operation, invoke) {
27164
+ return runWithPolicyTimeout(this.policy.timeout, { operation }, invoke);
27165
+ }
26927
27166
  shouldUseLocalCloudTransport() {
26928
27167
  if (this.workspace === void 0) {
26929
27168
  return false;
@@ -27098,6 +27337,7 @@ function createOpensteerSemanticRuntime(input = {}) {
27098
27337
  ...runtimeOptions.rootDir === void 0 ? {} : { rootDir: runtimeOptions.rootDir },
27099
27338
  ...runtimeOptions.rootPath === void 0 ? {} : { rootPath: runtimeOptions.rootPath },
27100
27339
  ...runtimeOptions.workspace === void 0 ? {} : { workspace: runtimeOptions.workspace },
27340
+ ...runtimeOptions.policy === void 0 ? {} : { policy: runtimeOptions.policy },
27101
27341
  ...runtimeOptions.cleanupRootOnClose === void 0 ? {} : { cleanupRootOnClose: runtimeOptions.cleanupRootOnClose },
27102
27342
  ...runtimeOptions.observability === void 0 ? {} : { observability: runtimeOptions.observability }
27103
27343
  });