@opengeni/sdk 4.4.1 → 4.5.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.
Files changed (57) hide show
  1. package/README.md +27 -0
  2. package/dist/adapters/next.js +2 -2
  3. package/dist/artifacts.js +4 -4
  4. package/dist/browser.d.ts +2 -0
  5. package/dist/browser.js +5 -3
  6. package/dist/chat/index.js +5 -5
  7. package/dist/{chunk-3BWTS2MJ.js → chunk-ARDVIZIJ.js} +2 -2
  8. package/dist/{chunk-PVWOBYAZ.js → chunk-BXMNDWAN.js} +256 -78
  9. package/dist/chunk-BXMNDWAN.js.map +1 -0
  10. package/dist/{chunk-IVHLADZ7.js → chunk-GRUMGFK6.js} +2 -2
  11. package/dist/{chunk-2RVPHF37.js → chunk-JBAGMSN2.js} +66 -10
  12. package/dist/chunk-JBAGMSN2.js.map +1 -0
  13. package/dist/{chunk-35S72HAE.js → chunk-U4JUWOZV.js} +5 -5
  14. package/dist/{chunk-BLID3DCD.js → chunk-V4AIZAFF.js} +1 -1
  15. package/dist/chunk-V4AIZAFF.js.map +1 -0
  16. package/dist/{chunk-ALXDFL6C.js → chunk-VSOXNL3T.js} +2 -2
  17. package/dist/{chunk-CT6P2W2C.js → chunk-WI3WUT5H.js} +2 -2
  18. package/dist/client.d.ts +24 -1
  19. package/dist/codex-realtime-controller.js +1 -1
  20. package/dist/core.d.ts +2 -0
  21. package/dist/core.js +5 -3
  22. package/dist/document-authority.js +3 -3
  23. package/dist/editable-artifacts.js +1 -1
  24. package/dist/index.d.ts +4 -2
  25. package/dist/index.js +35 -33
  26. package/dist/index.js.map +1 -1
  27. package/dist/read-retry.d.ts +61 -0
  28. package/dist/realtime.js +5 -5
  29. package/dist/session-proxy.d.ts +6 -1
  30. package/dist/session-proxy.js +2 -2
  31. package/dist/site-bridge-host.js +3 -3
  32. package/dist/site-browser-runtime.gen.d.ts +1 -1
  33. package/dist/site-document.js +1 -1
  34. package/dist/site-document.js.map +1 -1
  35. package/dist/site.js +3 -3
  36. package/dist/types.d.ts +40 -0
  37. package/dist/workspace-artifacts.d.ts +5 -0
  38. package/dist/workspace-live-stream.d.ts +7 -0
  39. package/package.json +3 -3
  40. package/src/browser.ts +2 -0
  41. package/src/client.ts +191 -73
  42. package/src/core.ts +2 -0
  43. package/src/index.ts +4 -0
  44. package/src/read-retry.ts +207 -0
  45. package/src/session-proxy.ts +95 -4
  46. package/src/site-browser-runtime.gen.ts +1 -1
  47. package/src/types.ts +38 -0
  48. package/src/workspace-artifacts.ts +6 -0
  49. package/src/workspace-live-stream.ts +12 -1
  50. package/dist/chunk-2RVPHF37.js.map +0 -1
  51. package/dist/chunk-BLID3DCD.js.map +0 -1
  52. package/dist/chunk-PVWOBYAZ.js.map +0 -1
  53. /package/dist/{chunk-3BWTS2MJ.js.map → chunk-ARDVIZIJ.js.map} +0 -0
  54. /package/dist/{chunk-IVHLADZ7.js.map → chunk-GRUMGFK6.js.map} +0 -0
  55. /package/dist/{chunk-35S72HAE.js.map → chunk-U4JUWOZV.js.map} +0 -0
  56. /package/dist/{chunk-ALXDFL6C.js.map → chunk-VSOXNL3T.js.map} +0 -0
  57. /package/dist/{chunk-CT6P2W2C.js.map → chunk-WI3WUT5H.js.map} +0 -0
@@ -18,7 +18,7 @@ import {
18
18
  OPENGENI_API_CONTRACT_REVISION,
19
19
  OPENGENI_CORRELATION_HEADER,
20
20
  RETAINED_OUTPUT_MAX_PAGE_BYTES
21
- } from "./chunk-BLID3DCD.js";
21
+ } from "./chunk-V4AIZAFF.js";
22
22
  import {
23
23
  OpenGeniInteractionClient,
24
24
  decodeComputerFrameMetadataHeader,
@@ -390,6 +390,118 @@ function withDeprecationNotices(fetchImpl, handler) {
390
390
  };
391
391
  }
392
392
 
393
+ // src/read-retry.ts
394
+ var DEFAULT_READ_RETRY_POLICY = Object.freeze({
395
+ maxAttempts: 4,
396
+ initialDelayMs: 250,
397
+ maxDelayMs: 2e3,
398
+ maxElapsedMs: 5e3
399
+ });
400
+ var MAX_READ_RETRY_ATTEMPTS = 10;
401
+ var MAX_READ_RETRY_ELAPSED_MS = 6e4;
402
+ function boundedNumber(value, fallback, min, max) {
403
+ if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
404
+ return Math.min(Math.max(value, min), max);
405
+ }
406
+ function resolveReadRetryPolicy(options) {
407
+ if (options === false) return null;
408
+ const input = options && typeof options === "object" ? options : {};
409
+ const maxAttempts = Math.floor(
410
+ boundedNumber(
411
+ input.maxAttempts,
412
+ DEFAULT_READ_RETRY_POLICY.maxAttempts,
413
+ 1,
414
+ MAX_READ_RETRY_ATTEMPTS
415
+ )
416
+ );
417
+ if (maxAttempts <= 1) return null;
418
+ const maxElapsedMs = boundedNumber(
419
+ input.maxElapsedMs,
420
+ DEFAULT_READ_RETRY_POLICY.maxElapsedMs,
421
+ 0,
422
+ MAX_READ_RETRY_ELAPSED_MS
423
+ );
424
+ const maxDelayMs = boundedNumber(
425
+ input.maxDelayMs,
426
+ DEFAULT_READ_RETRY_POLICY.maxDelayMs,
427
+ 0,
428
+ MAX_READ_RETRY_ELAPSED_MS
429
+ );
430
+ const initialDelayMs = boundedNumber(
431
+ input.initialDelayMs,
432
+ DEFAULT_READ_RETRY_POLICY.initialDelayMs,
433
+ 0,
434
+ maxDelayMs
435
+ );
436
+ return { maxAttempts, initialDelayMs, maxDelayMs, maxElapsedMs };
437
+ }
438
+ function isSafeReadMethod(method) {
439
+ const normalized = method.toUpperCase();
440
+ return normalized === "GET" || normalized === "HEAD";
441
+ }
442
+ function isTransientGatewayStatus(status) {
443
+ return status === 502 || status === 503 || status === 504;
444
+ }
445
+ var transportFailures = /* @__PURE__ */ new WeakSet();
446
+ function markTransportFailure(error) {
447
+ if (error instanceof TypeError) transportFailures.add(error);
448
+ }
449
+ var invalidResponses = /* @__PURE__ */ new WeakSet();
450
+ function invalidApiResponseError(body, options) {
451
+ const error = new OpenGeniApiError(502, body, options);
452
+ invalidResponses.add(error);
453
+ return error;
454
+ }
455
+ var NETWORK_FAILURE_MESSAGE = /failed to fetch|fetch failed|networkerror|network error|load failed|network request failed|network connection was lost|terminated|err_http2|econnreset|socket hang up/iu;
456
+ function isTransientRequestError(error) {
457
+ if (error instanceof OpenGeniApiError) {
458
+ return !invalidResponses.has(error) && isTransientGatewayStatus(error.status) && error.retryable;
459
+ }
460
+ if (!(error instanceof TypeError)) return false;
461
+ return transportFailures.has(error) || NETWORK_FAILURE_MESSAGE.test(error.message);
462
+ }
463
+ function parseRetryAfterMs(value, now = Date.now()) {
464
+ if (value === null || value === void 0) return null;
465
+ const trimmed = value.trim();
466
+ if (!trimmed) return null;
467
+ if (/^\d+$/.test(trimmed)) {
468
+ const seconds = Number(trimmed);
469
+ return Number.isSafeInteger(seconds) ? seconds * 1e3 : null;
470
+ }
471
+ if (!/[A-Za-z]/.test(trimmed)) return null;
472
+ const at = Date.parse(trimmed);
473
+ if (!Number.isFinite(at)) return null;
474
+ return Math.max(0, at - now);
475
+ }
476
+ function readRetryDelayMs(policy, retry, elapsedMs, retryAfterMs, random = Math.random) {
477
+ if (retry >= policy.maxAttempts) return null;
478
+ let delay;
479
+ if (retryAfterMs !== null) {
480
+ delay = retryAfterMs * (1 + 0.2 * Math.min(Math.max(random(), 0), 1));
481
+ } else {
482
+ const base = Math.min(policy.maxDelayMs, policy.initialDelayMs * 2 ** (retry - 1));
483
+ delay = base / 2 + base / 2 * Math.min(Math.max(random(), 0), 1);
484
+ }
485
+ if (elapsedMs + delay > policy.maxElapsedMs) return null;
486
+ return Math.round(delay);
487
+ }
488
+ function abortableReadRetryDelay(ms, signal) {
489
+ if (signal?.aborted) {
490
+ return Promise.reject(signal.reason ?? new DOMException("Request aborted", "AbortError"));
491
+ }
492
+ return new Promise((resolve, reject) => {
493
+ const onAbort = () => {
494
+ clearTimeout(timer);
495
+ reject(signal?.reason ?? new DOMException("Request aborted", "AbortError"));
496
+ };
497
+ const timer = setTimeout(() => {
498
+ signal?.removeEventListener("abort", onAbort);
499
+ resolve();
500
+ }, ms);
501
+ signal?.addEventListener("abort", onAbort, { once: true });
502
+ });
503
+ }
504
+
393
505
  // src/workspace-control-stream.ts
394
506
  async function* streamWorkspaceControlEvents(transport, options = {}) {
395
507
  const signal = options.signal;
@@ -614,7 +726,8 @@ function parseWorkspaceLiveEvent(data) {
614
726
  }
615
727
  if (!isRecord2(value)) return null;
616
728
  if (value.type === "workspace.sessions.changed" && typeof value.workspaceId === "string" && typeof value.available === "boolean" && (value.sessionIds === null || Array.isArray(value.sessionIds) && value.sessionIds.every((id) => typeof id === "string"))) {
617
- return value;
729
+ const { siteSessionIds, ...hint } = value;
730
+ return Array.isArray(siteSessionIds) && siteSessionIds.every((id) => typeof id === "string") ? { ...hint, siteSessionIds } : hint;
618
731
  }
619
732
  if (typeof value.sequence !== "number") return null;
620
733
  if (value.type === "workspace.control.changed" && typeof value.id === "string" && Number.isSafeInteger(value.sequence)) {
@@ -1019,6 +1132,7 @@ var OpenGeniClient = class {
1019
1132
  fetchImpl;
1020
1133
  sessionCommandTimeoutMs;
1021
1134
  apiContractStrict;
1135
+ readRetryPolicy;
1022
1136
  active = /* @__PURE__ */ new Map();
1023
1137
  generations = /* @__PURE__ */ new Map();
1024
1138
  queued = /* @__PURE__ */ new Map();
@@ -1039,6 +1153,7 @@ var OpenGeniClient = class {
1039
1153
  }
1040
1154
  this.sessionCommandTimeoutMs = sessionCommandTimeoutMs;
1041
1155
  this.apiContractStrict = defaultApiContractMode(options) === "strict";
1156
+ this.readRetryPolicy = resolveReadRetryPolicy(options.readRetry);
1042
1157
  this.interaction = new OpenGeniInteractionClient(this);
1043
1158
  this.tools = createLazyToolsFacade(this);
1044
1159
  }
@@ -1130,7 +1245,7 @@ var OpenGeniClient = class {
1130
1245
  options.signal ? { signal: options.signal } : {}
1131
1246
  );
1132
1247
  if (!isTranscriptionRecordingListResponse(body)) {
1133
- throw new OpenGeniApiError(502, "Invalid transcription recording list response.", {
1248
+ throw invalidApiResponseError("Invalid transcription recording list response.", {
1134
1249
  code: "invalid_response"
1135
1250
  });
1136
1251
  }
@@ -1586,6 +1701,7 @@ var OpenGeniClient = class {
1586
1701
  if (options.mineOnly) query.mineOnly = "true";
1587
1702
  if (options.includePinned === false) query.includePinned = "false";
1588
1703
  if (options.archivedOnly) query.archivedOnly = "true";
1704
+ if (options.siteConversations) query.siteConversations = options.siteConversations;
1589
1705
  result = await this.requestJson(
1590
1706
  "GET",
1591
1707
  path,
@@ -1634,6 +1750,16 @@ var OpenGeniClient = class {
1634
1750
  throw unsupportedSessionPage("Site access-filtered session lists");
1635
1751
  return response;
1636
1752
  }
1753
+ /**
1754
+ * Record that this person opened a Site, which orders its sidebar row like
1755
+ * session activity. Personal; needs `artifacts:read`.
1756
+ */
1757
+ async recordSiteOpened(workspaceId, siteId) {
1758
+ return this.requestJson(
1759
+ "POST",
1760
+ `/v1/workspaces/${encodeURIComponent(workspaceId)}/published-artifacts/${encodeURIComponent(siteId)}/opened`
1761
+ );
1762
+ }
1637
1763
  /** Console/owner-authorized agent only. Never available through the Site frame bridge. */
1638
1764
  async listSiteSessionAccess(workspaceId, siteId) {
1639
1765
  return this.requestJson(
@@ -3409,7 +3535,7 @@ var OpenGeniClient = class {
3409
3535
  const header = response.headers.get("x-opengeni-browser-frame");
3410
3536
  if (mediaType !== "image/jpeg" && mediaType !== "image/png" || !header || header.length > 64 * 1024) {
3411
3537
  await cancelResponseBody(response, "browser frame metadata is invalid");
3412
- throw new OpenGeniApiError(502, "browser frame metadata is invalid");
3538
+ throw invalidApiResponseError("browser frame metadata is invalid");
3413
3539
  }
3414
3540
  let metadata;
3415
3541
  try {
@@ -3418,11 +3544,11 @@ var OpenGeniClient = class {
3418
3544
  );
3419
3545
  } catch {
3420
3546
  await cancelResponseBody(response, "browser frame metadata is invalid");
3421
- throw new OpenGeniApiError(502, "browser frame metadata is invalid");
3547
+ throw invalidApiResponseError("browser frame metadata is invalid");
3422
3548
  }
3423
3549
  const data = await readBoundedResponseBytes(response, 24 * 1024 * 1024, null);
3424
3550
  if (metadata.browserSessionId !== browserSessionId || metadata.targetId !== targetId || metadata.mediaType !== mediaType) {
3425
- throw new OpenGeniApiError(502, "browser frame evidence does not match its request");
3551
+ throw invalidApiResponseError("browser frame evidence does not match its request");
3426
3552
  }
3427
3553
  return { ...metadata, data };
3428
3554
  }
@@ -3453,7 +3579,7 @@ var OpenGeniClient = class {
3453
3579
  options
3454
3580
  );
3455
3581
  if (state.browserSessionId !== browserSessionId || state.targetId !== targetId) {
3456
- throw new OpenGeniApiError(502, "browser target state belongs to another binding");
3582
+ throw invalidApiResponseError("browser target state belongs to another binding");
3457
3583
  }
3458
3584
  return state;
3459
3585
  }
@@ -3466,7 +3592,7 @@ var OpenGeniClient = class {
3466
3592
  options
3467
3593
  );
3468
3594
  if (result.browserSessionId !== browserSessionId || result.targetId !== targetId || result.kind !== request.kind || result.targetGeneration !== request.expectedTargetGeneration || result.documentGeneration !== request.expectedDocumentGeneration || result.frameId !== request.expectedFrameId) {
3469
- throw new OpenGeniApiError(502, "browser DOM read belongs to another binding");
3595
+ throw invalidApiResponseError("browser DOM read belongs to another binding");
3470
3596
  }
3471
3597
  return result;
3472
3598
  }
@@ -3677,19 +3803,19 @@ var OpenGeniClient = class {
3677
3803
  const mediaType = response.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase();
3678
3804
  if (mediaType !== "image/jpeg" && mediaType !== "image/png") {
3679
3805
  await cancelResponseBody(response, "computer frame media type is invalid");
3680
- throw new OpenGeniApiError(502, "computer frame media type is invalid");
3806
+ throw invalidApiResponseError("computer frame media type is invalid");
3681
3807
  }
3682
3808
  const metadataHeader = response.headers.get("x-opengeni-computer-frame");
3683
3809
  if (!metadataHeader || metadataHeader.length > 32 * 1024) {
3684
3810
  await cancelResponseBody(response, "computer frame metadata is invalid");
3685
- throw new OpenGeniApiError(502, "computer frame metadata is invalid");
3811
+ throw invalidApiResponseError("computer frame metadata is invalid");
3686
3812
  }
3687
3813
  let metadata;
3688
3814
  try {
3689
3815
  metadata = decodeComputerFrameMetadataHeader(metadataHeader);
3690
3816
  } catch {
3691
3817
  await cancelResponseBody(response, "computer frame metadata is invalid");
3692
- throw new OpenGeniApiError(502, "computer frame metadata is invalid");
3818
+ throw invalidApiResponseError("computer frame metadata is invalid");
3693
3819
  }
3694
3820
  const bytes = await readBoundedResponseBytes(response, 256 * 1024, null);
3695
3821
  const mismatchReason = computerFrameEvidenceMismatchReason(metadata, {
@@ -3699,7 +3825,7 @@ var OpenGeniClient = class {
3699
3825
  sha256: await sha256Hex(bytes)
3700
3826
  });
3701
3827
  if (mismatchReason) {
3702
- throw new OpenGeniApiError(502, "computer frame evidence does not match its request");
3828
+ throw invalidApiResponseError("computer frame evidence does not match its request");
3703
3829
  }
3704
3830
  return { ...metadata, data: bytes };
3705
3831
  }
@@ -5440,7 +5566,7 @@ var OpenGeniClient = class {
5440
5566
  if (!metadata.available) return { metadata, bytes: null };
5441
5567
  const supportedScreenshot = (metadata.kind === "computer_screenshot" || metadata.kind === "browser_screenshot") && ["image/png", "image/jpeg", "image/webp"].includes(metadata.contentType);
5442
5568
  if (!supportedScreenshot || !metadata.dimensions || metadata.originalBytes <= 0 || metadata.originalBytes > COMPUTER_SCREENSHOT_MAX_BYTES) {
5443
- throw new OpenGeniApiError(502, "retained screenshot metadata is invalid");
5569
+ throw invalidApiResponseError("retained screenshot metadata is invalid");
5444
5570
  }
5445
5571
  const bytes = await this.downloadRetainedArtifactBytes(metadata, options);
5446
5572
  return { metadata, bytes };
@@ -5453,7 +5579,7 @@ var OpenGeniClient = class {
5453
5579
  const bytes = new Uint8Array(artifact.originalBytes);
5454
5580
  const pageBytes = Math.min(artifact.retrieval.maxRangeBytes, RETAINED_OUTPUT_MAX_PAGE_BYTES);
5455
5581
  if (!Number.isSafeInteger(pageBytes) || pageBytes <= 0) {
5456
- throw new OpenGeniApiError(502, "retained artifact range metadata is invalid");
5582
+ throw invalidApiResponseError("retained artifact range metadata is invalid");
5457
5583
  }
5458
5584
  for (let start = 0; start < bytes.byteLength; start += pageBytes) {
5459
5585
  options.signal?.throwIfAborted();
@@ -5473,15 +5599,15 @@ var OpenGeniClient = class {
5473
5599
  }
5474
5600
  }
5475
5601
  }
5476
- if (!page) throw new OpenGeniApiError(502, "retained artifact range retry exhausted");
5602
+ if (!page) throw invalidApiResponseError("retained artifact range retry exhausted");
5477
5603
  const expectedLength = end - start + 1;
5478
5604
  if (page.status !== 206 || page.contentType !== artifact.contentType || page.contentLength !== expectedLength || page.contentRange !== `bytes ${start}-${end}/${artifact.originalBytes}`) {
5479
- throw new OpenGeniApiError(502, "retained artifact range response is invalid");
5605
+ throw invalidApiResponseError("retained artifact range response is invalid");
5480
5606
  }
5481
5607
  bytes.set(page.bytes, start);
5482
5608
  }
5483
5609
  if (await sha256Hex(bytes) !== artifact.sha256) {
5484
- throw new OpenGeniApiError(502, "retained artifact checksum mismatch");
5610
+ throw invalidApiResponseError("retained artifact checksum mismatch");
5485
5611
  }
5486
5612
  return bytes;
5487
5613
  }
@@ -5517,7 +5643,7 @@ var OpenGeniClient = class {
5517
5643
  }
5518
5644
  if (response.headers.get("accept-ranges") !== "bytes") {
5519
5645
  await cancelResponseBody(response, "retained artifact response omitted byte-range support");
5520
- throw new OpenGeniApiError(502, "retained artifact response omitted byte-range support");
5646
+ throw invalidApiResponseError("retained artifact response omitted byte-range support");
5521
5647
  }
5522
5648
  let declaredLength;
5523
5649
  try {
@@ -7819,63 +7945,114 @@ var OpenGeniClient = class {
7819
7945
  const correlationId = crypto.randomUUID();
7820
7946
  const abort = requestAbortSignal(options);
7821
7947
  try {
7822
- if (abort.signal?.aborted) {
7823
- throw abort.signal.reason ?? new DOMException("Request aborted", "AbortError");
7824
- }
7825
- const headers = this.headers(correlationId);
7826
- let response;
7827
- try {
7828
- response = await awaitWithAbort(
7829
- this.fetchImpl(this.url(path, query), {
7830
- method,
7831
- headers: {
7832
- ...headers,
7833
- Accept: "application/json",
7834
- ...body !== void 0 ? { "Content-Type": "application/json" } : {}
7835
- },
7836
- ...body !== void 0 ? { body: JSON.stringify(body) } : {},
7837
- ...abort.signal ? { signal: abort.signal } : {}
7838
- }),
7839
- abort.signal
7948
+ const retryPolicy = isSafeReadMethod(method) ? this.readRetryPolicy : null;
7949
+ if (!retryPolicy) {
7950
+ return await this.requestJsonAttempt(
7951
+ method,
7952
+ path,
7953
+ body,
7954
+ query,
7955
+ options,
7956
+ abort.signal,
7957
+ correlationId,
7958
+ null
7840
7959
  );
7841
- } catch (error) {
7842
- if (options.signal?.aborted) throw error;
7843
- if (isMutationMethod(method)) {
7844
- throw mutationTransportError(correlationId);
7845
- }
7846
- throw error;
7847
7960
  }
7848
- assertApiContractResponse(response, this.apiContractStrict);
7849
- try {
7850
- if (!response.ok) {
7851
- throw await awaitWithAbort(
7852
- apiErrorFromResponse(response, { method, correlationId }),
7853
- abort.signal
7961
+ const startedAt = Date.now();
7962
+ for (let retry = 1; ; retry += 1) {
7963
+ const attempt = {
7964
+ transportFailure: false,
7965
+ gatewayFailure: false,
7966
+ retryAfterMs: null
7967
+ };
7968
+ try {
7969
+ return await this.requestJsonAttempt(
7970
+ method,
7971
+ path,
7972
+ body,
7973
+ query,
7974
+ options,
7975
+ abort.signal,
7976
+ correlationId,
7977
+ attempt
7854
7978
  );
7855
- }
7856
- if (options.responseType === "void") {
7857
- await awaitWithAbort(
7858
- cancelResponseBody(response, "discarding void API response"),
7859
- abort.signal
7979
+ } catch (error) {
7980
+ const transient = attempt.transportFailure || attempt.gatewayFailure && isTransientRequestError(error);
7981
+ if (abort.signal?.aborted || !transient) throw error;
7982
+ const delay = readRetryDelayMs(
7983
+ retryPolicy,
7984
+ retry,
7985
+ Date.now() - startedAt,
7986
+ attempt.retryAfterMs
7860
7987
  );
7861
- return void 0;
7988
+ if (delay === null) throw error;
7989
+ await abortableReadRetryDelay(delay, abort.signal);
7862
7990
  }
7863
- await awaitWithAbort(assertJsonResponse(response, { method, correlationId }), abort.signal);
7864
- const value = await awaitWithAbort(response.json(), abort.signal);
7865
- recordObservedSessionSequence(value, response.headers.get(SESSION_SEQUENCE_HEADER));
7866
- return value;
7867
- } catch (error) {
7868
- if (options.signal?.aborted) throw error;
7869
- if (error instanceof OpenGeniApiError) throw error;
7870
- if (isMutationMethod(method)) {
7871
- throw mutationTransportError(correlationId);
7872
- }
7873
- throw error;
7874
7991
  }
7875
7992
  } finally {
7876
7993
  abort.dispose();
7877
7994
  }
7878
7995
  }
7996
+ async requestJsonAttempt(method, path, body, query, options, signal, correlationId, attempt) {
7997
+ if (signal?.aborted) {
7998
+ throw signal.reason ?? new DOMException("Request aborted", "AbortError");
7999
+ }
8000
+ const headers = this.headers(correlationId);
8001
+ let response;
8002
+ try {
8003
+ response = await awaitWithAbort(
8004
+ this.fetchImpl(this.url(path, query), {
8005
+ method,
8006
+ headers: {
8007
+ ...headers,
8008
+ Accept: "application/json",
8009
+ ...body !== void 0 ? { "Content-Type": "application/json" } : {}
8010
+ },
8011
+ ...body !== void 0 ? { body: JSON.stringify(body) } : {},
8012
+ ...signal ? { signal } : {}
8013
+ }),
8014
+ signal
8015
+ );
8016
+ } catch (error) {
8017
+ if (options.signal?.aborted) throw error;
8018
+ if (isMutationMethod(method)) {
8019
+ throw mutationTransportError(correlationId);
8020
+ }
8021
+ if (error instanceof TypeError && attempt) attempt.transportFailure = true;
8022
+ markTransportFailure(error);
8023
+ throw error;
8024
+ }
8025
+ assertApiContractResponse(response, this.apiContractStrict);
8026
+ try {
8027
+ if (!response.ok) {
8028
+ if (attempt && isTransientGatewayStatus(response.status)) {
8029
+ attempt.gatewayFailure = true;
8030
+ attempt.retryAfterMs = parseRetryAfterMs(response.headers.get("retry-after"));
8031
+ }
8032
+ throw await awaitWithAbort(
8033
+ apiErrorFromResponse(response, { method, correlationId }),
8034
+ signal
8035
+ );
8036
+ }
8037
+ if (options.responseType === "void") {
8038
+ await awaitWithAbort(cancelResponseBody(response, "discarding void API response"), signal);
8039
+ return void 0;
8040
+ }
8041
+ await awaitWithAbort(assertJsonResponse(response, { method, correlationId }), signal);
8042
+ const value = await awaitWithAbort(response.json(), signal);
8043
+ recordObservedSessionSequence(value, response.headers.get(SESSION_SEQUENCE_HEADER));
8044
+ return value;
8045
+ } catch (error) {
8046
+ if (options.signal?.aborted) throw error;
8047
+ if (error instanceof OpenGeniApiError) throw error;
8048
+ if (isMutationMethod(method)) {
8049
+ throw mutationTransportError(correlationId);
8050
+ }
8051
+ if (error instanceof TypeError && attempt) attempt.transportFailure = true;
8052
+ markTransportFailure(error);
8053
+ throw error;
8054
+ }
8055
+ }
7879
8056
  /** Authenticated, contract-checked response for bounded streaming downloads. */
7880
8057
  async requestResponse(method, path, query = {}, options = {}) {
7881
8058
  const correlationId = crypto.randomUUID();
@@ -7965,7 +8142,7 @@ function isTranscriptionRecordingResponse(value) {
7965
8142
  }
7966
8143
  function transcriptionRecordingResponse(value) {
7967
8144
  if (isTranscriptionRecordingResponse(value)) return value;
7968
- throw new OpenGeniApiError(502, "Invalid transcription recording response.", {
8145
+ throw invalidApiResponseError("Invalid transcription recording response.", {
7969
8146
  code: "invalid_response"
7970
8147
  });
7971
8148
  }
@@ -8062,7 +8239,7 @@ async function apiErrorFromResponse(response, context) {
8062
8239
  async function assertJsonResponse(response, context) {
8063
8240
  if (isJsonContentType(response.headers.get("content-type"))) return;
8064
8241
  await cancelResponseBody(response, "unexpected non-JSON API response");
8065
- throw new OpenGeniApiError(502, "", {
8242
+ throw invalidApiResponseError("", {
8066
8243
  code: "upstream_unavailable",
8067
8244
  retryable: true,
8068
8245
  correlationId: response.headers.get(OPENGENI_CORRELATION_HEADER) ?? context.correlationId,
@@ -8137,44 +8314,44 @@ async function cancelResponseBody(response, reason) {
8137
8314
  }
8138
8315
  function assertRetainedWorkspaceArtifactReceipt(workspaceId, artifact) {
8139
8316
  if (!parseRetainedGeneratedImageReference(artifact, workspaceId) && !parseRetainedWorkspaceFileReference(artifact, workspaceId)) {
8140
- throw new OpenGeniApiError(502, "retained workspace artifact receipt is invalid");
8317
+ throw invalidApiResponseError("retained workspace artifact receipt is invalid");
8141
8318
  }
8142
8319
  }
8143
8320
  function assertSafeArtifactDownloadUrl(value) {
8144
8321
  if (!value || typeof value.url !== "string" || typeof value.expiresAt !== "string" || !Number.isFinite(Date.parse(value.expiresAt))) {
8145
- throw new OpenGeniApiError(502, "retained artifact download URL is invalid");
8322
+ throw invalidApiResponseError("retained artifact download URL is invalid");
8146
8323
  }
8147
8324
  let parsed;
8148
8325
  try {
8149
8326
  parsed = new URL(value.url);
8150
8327
  } catch {
8151
- throw new OpenGeniApiError(502, "retained artifact download URL is invalid");
8328
+ throw invalidApiResponseError("retained artifact download URL is invalid");
8152
8329
  }
8153
8330
  if (!["http:", "https:"].includes(parsed.protocol) || parsed.username || parsed.password) {
8154
- throw new OpenGeniApiError(502, "retained artifact download URL is unsafe");
8331
+ throw invalidApiResponseError("retained artifact download URL is unsafe");
8155
8332
  }
8156
8333
  }
8157
8334
  function assertSafeVideoArtifactPlaybackSource(value, expectedArtifactId) {
8158
8335
  if (!value || value.schemaVersion !== 1 || value.artifactId !== expectedArtifactId || value.contentType !== "video/mp4" || value.acceptRanges !== "bytes" || !Number.isSafeInteger(value.sizeBytes) || value.sizeBytes <= 0 || value.sizeBytes > GENERATED_VIDEO_MAX_BYTES || !/^[0-9a-f]{64}$/.test(value.sha256)) {
8159
- throw new OpenGeniApiError(502, "generated-video playback source is invalid");
8336
+ throw invalidApiResponseError("generated-video playback source is invalid");
8160
8337
  }
8161
8338
  assertSafeArtifactDownloadUrl({ url: value.url, expiresAt: value.expiresAt });
8162
8339
  }
8163
8340
  function parseBoundedContentLength(value) {
8164
8341
  if (value === null) return null;
8165
8342
  if (!/^\d+$/.test(value)) {
8166
- throw new OpenGeniApiError(502, "invalid retained artifact content-length");
8343
+ throw invalidApiResponseError("invalid retained artifact content-length");
8167
8344
  }
8168
8345
  const length = Number(value);
8169
8346
  if (!Number.isSafeInteger(length) || length > RETAINED_OUTPUT_MAX_PAGE_BYTES) {
8170
- throw new OpenGeniApiError(502, "retained artifact response exceeds the SDK byte limit");
8347
+ throw invalidApiResponseError("retained artifact response exceeds the SDK byte limit");
8171
8348
  }
8172
8349
  return length;
8173
8350
  }
8174
8351
  async function readBoundedResponseBytes(response, maxBytes, expectedBytes) {
8175
8352
  if (!response.body) {
8176
8353
  if (expectedBytes !== null && expectedBytes !== 0) {
8177
- throw new OpenGeniApiError(502, "retained artifact response length mismatch");
8354
+ throw invalidApiResponseError("retained artifact response length mismatch");
8178
8355
  }
8179
8356
  return new Uint8Array();
8180
8357
  }
@@ -8188,7 +8365,7 @@ async function readBoundedResponseBytes(response, maxBytes, expectedBytes) {
8188
8365
  totalBytes += value.byteLength;
8189
8366
  if (totalBytes > maxBytes) {
8190
8367
  await reader.cancel("retained artifact response exceeded the SDK byte limit").catch(() => void 0);
8191
- throw new OpenGeniApiError(502, "retained artifact response exceeds the SDK byte limit");
8368
+ throw invalidApiResponseError("retained artifact response exceeds the SDK byte limit");
8192
8369
  }
8193
8370
  chunks.push(value);
8194
8371
  }
@@ -8196,7 +8373,7 @@ async function readBoundedResponseBytes(response, maxBytes, expectedBytes) {
8196
8373
  reader.releaseLock();
8197
8374
  }
8198
8375
  if (expectedBytes !== null && totalBytes !== expectedBytes) {
8199
- throw new OpenGeniApiError(502, "retained artifact response length mismatch");
8376
+ throw invalidApiResponseError("retained artifact response length mismatch");
8200
8377
  }
8201
8378
  const bytes = new Uint8Array(totalBytes);
8202
8379
  let offset = 0;
@@ -8228,6 +8405,7 @@ export {
8228
8405
  vaultErrorCode,
8229
8406
  vaultErrorDetail,
8230
8407
  parseDeprecationNotice,
8408
+ isTransientRequestError,
8231
8409
  streamWorkspaceControlEvents,
8232
8410
  streamWorkspaceInteractionRevisions,
8233
8411
  streamWorkspaceLiveEvents,
@@ -8242,4 +8420,4 @@ export {
8242
8420
  parseMediaGenerationResult,
8243
8421
  OpenGeniClient
8244
8422
  };
8245
- //# sourceMappingURL=chunk-PVWOBYAZ.js.map
8423
+ //# sourceMappingURL=chunk-BXMNDWAN.js.map