@opengeni/sdk 0.25.0 → 0.26.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.js CHANGED
@@ -2,29 +2,63 @@
2
2
  var OpenGeniApiError = class extends Error {
3
3
  status;
4
4
  code;
5
+ retryable;
6
+ correlationId;
7
+ /** True only when an uncontrolled transport failed after a mutation may have been accepted. */
8
+ outcomeUnknown;
5
9
  body;
6
- constructor(status, body) {
10
+ constructor(status, body, options = {}) {
7
11
  const decoded = decodeApiErrorBody(body);
8
- super(`OpenGeni API ${status}: ${decoded.message ?? (body || "(empty body)")}`);
12
+ const correlationId = decoded?.requestId ?? boundedCorrelationId(options.correlationId);
13
+ const gatewayFailure = status >= 502 && status <= 504;
14
+ const fromResponse = options.mutation !== void 0;
15
+ const message = decoded?.message ?? (fromResponse ? "Request failed." : body || "(empty body)");
16
+ const displayMessage = options.displayMessage ?? (gatewayFailure && fromResponse ? "OpenGeni is temporarily unavailable \u2014 retry." : `OpenGeni API ${status}: ${message}`);
17
+ super(correlationId ? `${displayMessage} Reference: ${correlationId}.` : displayMessage);
9
18
  this.name = "OpenGeniApiError";
10
19
  this.status = status;
11
- this.code = decoded.code;
12
- this.body = body;
20
+ this.code = options.code ?? decoded?.code ?? (gatewayFailure && fromResponse ? "upstream_unavailable" : void 0);
21
+ this.retryable = options.retryable ?? decoded?.retryable ?? retryableApiStatus(status);
22
+ this.correlationId = correlationId;
23
+ this.outcomeUnknown = options.outcomeUnknown ?? (gatewayFailure && !!options.mutation && !decoded);
24
+ this.body = !fromResponse || decoded ? body : "";
13
25
  }
14
26
  };
15
27
  function decodeApiErrorBody(body) {
16
- if (!body) return {};
28
+ if (!body) return null;
17
29
  try {
18
30
  const decoded = JSON.parse(body);
19
- if (!decoded || typeof decoded !== "object" || Array.isArray(decoded)) return {};
31
+ if (!decoded || typeof decoded !== "object" || Array.isArray(decoded)) return null;
20
32
  const record = decoded;
33
+ const nested = record.error && typeof record.error === "object" && !Array.isArray(record.error) ? record.error : record;
34
+ const code = boundedApiField(nested.code);
35
+ const message = boundedApiField(nested.message);
36
+ const requestId = boundedCorrelationId(nested.requestId);
37
+ const retryable = typeof nested.retryable === "boolean" ? nested.retryable : void 0;
38
+ if (!code && !message && !requestId && retryable === void 0) return null;
21
39
  return {
22
- ...typeof record.code === "string" && record.code ? { code: record.code } : {},
23
- ...typeof record.message === "string" && record.message ? { message: record.message } : {}
40
+ code,
41
+ message,
42
+ requestId,
43
+ retryable
24
44
  };
25
45
  } catch {
26
- return {};
46
+ return null;
47
+ }
48
+ }
49
+ function boundedApiField(value) {
50
+ if (typeof value !== "string") return;
51
+ const bytes = new TextEncoder().encode(value);
52
+ return bytes.byteLength <= 512 ? value : new TextDecoder().decode(bytes.slice(0, 512));
53
+ }
54
+ function retryableApiStatus(status) {
55
+ return status === 408 || status === 409 || status === 425 || status === 429 || status >= 500;
56
+ }
57
+ function boundedCorrelationId(value) {
58
+ if (typeof value !== "string" || value.length > 128 || !/^[\w.:-]+$/.test(value)) {
59
+ return;
27
60
  }
61
+ return value;
28
62
  }
29
63
  var OpenGeniSessionListCursorError = class extends OpenGeniApiError {
30
64
  };
@@ -48,9 +82,7 @@ function isAbortError(error) {
48
82
  return error instanceof DOMException && error.name === "AbortError" || error instanceof Error && error.name === "AbortError";
49
83
  }
50
84
  function isRetryableStreamError(error) {
51
- if (error instanceof OpenGeniApiError) {
52
- return error.status === 408 || error.status === 409 || error.status === 425 || error.status === 429 || error.status >= 500;
53
- }
85
+ if (error instanceof OpenGeniApiError) return error.retryable;
54
86
  return error instanceof TypeError;
55
87
  }
56
88
 
@@ -391,6 +423,7 @@ var SESSION_EVENT_TYPES = [
391
423
  "terminal.pty.exited",
392
424
  "session.title_set",
393
425
  "session.mcp.approval_policy.updated",
426
+ "session.tool_policy.updated",
394
427
  // Multi-account Codex (P1): the session's inference account changed.
395
428
  "codex.account.switched",
396
429
  // credential allocator metadata-only per-turn credential selection audit.
@@ -472,6 +505,7 @@ var KNOWN_PERMISSIONS = [
472
505
  ];
473
506
  var OPENGENI_API_CONTRACT_REVISION = "2026-07-turn-instructions-v1";
474
507
  var OPENGENI_API_CONTRACT_HEADER = "x-opengeni-api-contract";
508
+ var OPENGENI_CORRELATION_HEADER = "x-opengeni-correlation-id";
475
509
  var RETAINED_OUTPUT_DEFAULT_PAGE_BYTES = 256 * 1024;
476
510
  var RETAINED_OUTPUT_MAX_PAGE_BYTES = 1024 * 1024;
477
511
  var KNOWN_USAGE_EVENT_TYPES = [
@@ -540,6 +574,14 @@ var OpenGeniClient = class {
540
574
  request
541
575
  );
542
576
  }
577
+ /** Replace the durable tool policy or explicitly adopt workspace defaults. */
578
+ async updateSessionToolPolicy(workspaceId, sessionId, request) {
579
+ return await this.requestJson(
580
+ "PUT",
581
+ `/v1/workspaces/${workspaceId}/sessions/${sessionId}/tool-policy`,
582
+ request
583
+ );
584
+ }
543
585
  /**
544
586
  * Replace one attached MCP server's approval policy. The change is captured
545
587
  * by the next claimed attempt; already-claimed work keeps its immutable
@@ -583,7 +625,13 @@ var OpenGeniClient = class {
583
625
  );
584
626
  } catch (error) {
585
627
  if (error instanceof OpenGeniApiError && error.status === 410) {
586
- throw new OpenGeniSessionListCursorError(error.status, error.body);
628
+ throw new OpenGeniSessionListCursorError(error.status, error.body, {
629
+ ...error.code ? { code: error.code } : {},
630
+ retryable: error.retryable,
631
+ ...error.correlationId ? { correlationId: error.correlationId } : {},
632
+ outcomeUnknown: error.outcomeUnknown,
633
+ displayMessage: "The session list changed \u2014 refresh and try again."
634
+ });
587
635
  }
588
636
  throw error;
589
637
  }
@@ -758,6 +806,7 @@ var OpenGeniClient = class {
758
806
  throw new TypeError("resultMode=compact requires latest");
759
807
  }
760
808
  const listOptions = options.resultMode === "compact" ? null : options;
809
+ const correlationId = crypto.randomUUID();
761
810
  const response = await this.fetchImpl(
762
811
  this.url(`/v1/workspaces/${workspaceId}/sessions/${sessionId}/events`, {
763
812
  ...listOptions?.after !== void 0 ? { after: String(listOptions.after) } : {},
@@ -776,11 +825,14 @@ var OpenGeniClient = class {
776
825
  }),
777
826
  {
778
827
  method: "GET",
779
- headers: { ...this.headers(), Accept: "application/json" }
828
+ headers: { ...this.headers(correlationId), Accept: "application/json" }
780
829
  }
781
830
  );
782
831
  assertApiContractResponse(response);
783
- if (!response.ok) throw new OpenGeniApiError(response.status, await safeText(response));
832
+ if (!response.ok) {
833
+ throw await apiErrorFromResponse(response, { method: "GET", correlationId });
834
+ }
835
+ await assertJsonResponse(response, { method: "GET", correlationId });
784
836
  const body = await response.json();
785
837
  if (options.resultMode === "compact") {
786
838
  return body;
@@ -908,14 +960,15 @@ var OpenGeniClient = class {
908
960
  const url = this.url(`/v1/workspaces/${workspaceId}/sessions/${sessionId}/events/stream`, {
909
961
  after: String(options.after ?? 0)
910
962
  });
963
+ const correlationId = crypto.randomUUID();
911
964
  const response = await this.fetchImpl(url, {
912
965
  method: "GET",
913
- headers: { ...this.headers(), Accept: "text/event-stream" },
966
+ headers: { ...this.headers(correlationId), Accept: "text/event-stream" },
914
967
  ...options.signal ? { signal: options.signal } : {}
915
968
  });
916
969
  assertApiContractResponse(response);
917
970
  if (!response.ok) {
918
- throw new OpenGeniApiError(response.status, await safeText(response));
971
+ throw await apiErrorFromResponse(response, { method: "GET", correlationId });
919
972
  }
920
973
  if (!response.body) {
921
974
  throw new OpenGeniApiError(response.status, "SSE response did not include a readable body");
@@ -997,6 +1050,7 @@ var OpenGeniClient = class {
997
1050
  }
998
1051
  /** Count/byte-bounded page plus an explicit continuation cursor. */
999
1052
  async listWorkspaceControlEventPage(workspaceId, options = {}) {
1053
+ const correlationId = crypto.randomUUID();
1000
1054
  const response = await this.fetchImpl(
1001
1055
  this.url(`/v1/workspaces/${workspaceId}/control-events`, {
1002
1056
  ...options.after !== void 0 ? { after: String(options.after) } : {},
@@ -1004,13 +1058,14 @@ var OpenGeniClient = class {
1004
1058
  }),
1005
1059
  {
1006
1060
  method: "GET",
1007
- headers: { ...this.headers(), Accept: "application/json" }
1061
+ headers: { ...this.headers(correlationId), Accept: "application/json" }
1008
1062
  }
1009
1063
  );
1010
1064
  assertApiContractResponse(response);
1011
1065
  if (!response.ok) {
1012
- throw new OpenGeniApiError(response.status, await safeText(response));
1066
+ throw await apiErrorFromResponse(response, { method: "GET", correlationId });
1013
1067
  }
1068
+ await assertJsonResponse(response, { method: "GET", correlationId });
1014
1069
  const events = await response.json();
1015
1070
  const bytesHeader = response.headers.get("X-OpenGeni-Page-Bytes");
1016
1071
  const nextHeader = response.headers.get("X-OpenGeni-Next-After");
@@ -1035,18 +1090,21 @@ var OpenGeniClient = class {
1035
1090
  };
1036
1091
  }
1037
1092
  async openWorkspaceControlEventStream(workspaceId, options = {}) {
1093
+ const correlationId = crypto.randomUUID();
1038
1094
  const response = await this.fetchImpl(
1039
1095
  this.url(`/v1/workspaces/${workspaceId}/control-events/stream`, {
1040
1096
  after: String(options.after ?? 0)
1041
1097
  }),
1042
1098
  {
1043
1099
  method: "GET",
1044
- headers: { ...this.headers(), Accept: "text/event-stream" },
1100
+ headers: { ...this.headers(correlationId), Accept: "text/event-stream" },
1045
1101
  ...options.signal ? { signal: options.signal } : {}
1046
1102
  }
1047
1103
  );
1048
1104
  assertApiContractResponse(response);
1049
- if (!response.ok) throw new OpenGeniApiError(response.status, await safeText(response));
1105
+ if (!response.ok) {
1106
+ throw await apiErrorFromResponse(response, { method: "GET", correlationId });
1107
+ }
1050
1108
  if (!response.body) {
1051
1109
  throw new OpenGeniApiError(response.status, "SSE response did not include a readable body");
1052
1110
  }
@@ -1379,6 +1437,67 @@ var OpenGeniClient = class {
1379
1437
  async updateWorkspace(workspaceId, request) {
1380
1438
  return await this.requestJson("PATCH", `/v1/workspaces/${workspaceId}`, request);
1381
1439
  }
1440
+ /** Inspect immutable instruction-policy history, active heads, and activation audit evidence. */
1441
+ async listWorkspaceInstructionPolicies(workspaceId, options = {}) {
1442
+ const params = new URLSearchParams();
1443
+ if (options.kind !== void 0) params.set("kind", options.kind);
1444
+ if (options.scope !== void 0) params.set("scope", options.scope);
1445
+ if (options.roleKey !== void 0) params.set("roleKey", options.roleKey);
1446
+ if (options.afterRevision !== void 0) {
1447
+ params.set("afterRevision", String(options.afterRevision));
1448
+ }
1449
+ if (options.limit !== void 0) params.set("limit", String(options.limit));
1450
+ const query = params.toString();
1451
+ return await this.requestJson(
1452
+ "GET",
1453
+ `/v1/workspaces/${workspaceId}/instruction-policies${query ? `?${query}` : ""}`
1454
+ );
1455
+ }
1456
+ async getWorkspaceInstructionPolicyRevision(workspaceId, revisionId) {
1457
+ return await this.requestJson(
1458
+ "GET",
1459
+ `/v1/workspaces/${workspaceId}/instruction-policies/${encodeURIComponent(revisionId)}`
1460
+ );
1461
+ }
1462
+ async createWorkspaceInstructionPolicyDraft(workspaceId, request) {
1463
+ return await this.requestJson(
1464
+ "POST",
1465
+ `/v1/workspaces/${workspaceId}/instruction-policies/drafts`,
1466
+ request
1467
+ );
1468
+ }
1469
+ /** Import the stored legacy workspace override as an inactive charter draft. */
1470
+ async importLegacyWorkspaceInstructionPolicyDraft(workspaceId, request = {}) {
1471
+ return await this.requestJson(
1472
+ "POST",
1473
+ `/v1/workspaces/${workspaceId}/instruction-policies/import-legacy`,
1474
+ request
1475
+ );
1476
+ }
1477
+ async diffWorkspaceInstructionPolicyRevisions(workspaceId, request) {
1478
+ const params = new URLSearchParams({
1479
+ fromRevisionId: request.fromRevisionId,
1480
+ toRevisionId: request.toRevisionId
1481
+ });
1482
+ return await this.requestJson(
1483
+ "GET",
1484
+ `/v1/workspaces/${workspaceId}/instruction-policies/diff?${params}`
1485
+ );
1486
+ }
1487
+ async activateWorkspaceInstructionPolicyRevision(workspaceId, revisionId, request) {
1488
+ return await this.requestJson(
1489
+ "POST",
1490
+ `/v1/workspaces/${workspaceId}/instruction-policies/${encodeURIComponent(revisionId)}/activate`,
1491
+ request
1492
+ );
1493
+ }
1494
+ async rollbackWorkspaceInstructionPolicyRevision(workspaceId, request) {
1495
+ return await this.requestJson(
1496
+ "POST",
1497
+ `/v1/workspaces/${workspaceId}/instruction-policies/rollback`,
1498
+ request
1499
+ );
1500
+ }
1382
1501
  /**
1383
1502
  * Delete a workspace and everything in it. Refused (409) for the account's
1384
1503
  * only workspace and while it still has a running session. Irreversible.
@@ -1684,7 +1803,7 @@ var OpenGeniClient = class {
1684
1803
  body
1685
1804
  });
1686
1805
  if (!putResponse.ok) {
1687
- throw new OpenGeniApiError(putResponse.status, await safeText(putResponse));
1806
+ throw await apiErrorFromResponse(putResponse, { method: "PUT" });
1688
1807
  }
1689
1808
  return await this.completeFileUpload(workspaceId, upload.uploadId);
1690
1809
  }
@@ -1709,12 +1828,13 @@ var OpenGeniClient = class {
1709
1828
  if (options.range && (options.range.length > 128 || /[^\x20-\x7e]/.test(options.range))) {
1710
1829
  throw new RangeError("retained artifact range must be at most 128 printable ASCII bytes");
1711
1830
  }
1831
+ const correlationId = crypto.randomUUID();
1712
1832
  const response = await this.fetchImpl(
1713
1833
  this.url(`/v1/workspaces/${workspaceId}/artifacts/${artifactId}/content`),
1714
1834
  {
1715
1835
  method: "GET",
1716
1836
  headers: {
1717
- ...this.headers(),
1837
+ ...this.headers(correlationId),
1718
1838
  Accept: "application/octet-stream",
1719
1839
  ...options.range ? { Range: options.range } : {}
1720
1840
  },
@@ -1728,7 +1848,7 @@ var OpenGeniClient = class {
1728
1848
  throw error;
1729
1849
  }
1730
1850
  if (!response.ok) {
1731
- throw new OpenGeniApiError(response.status, await safeBoundedText(response));
1851
+ throw await apiErrorFromResponse(response, { method: "GET", correlationId });
1732
1852
  }
1733
1853
  if (response.status !== 200 && response.status !== 206) {
1734
1854
  await cancelResponseBody(response, "unexpected retained artifact response status");
@@ -1800,6 +1920,30 @@ var OpenGeniClient = class {
1800
1920
  `/v1/workspaces/${workspaceId}/document-bases/${baseId}/documents`
1801
1921
  );
1802
1922
  }
1923
+ /**
1924
+ * Drop raw text or an already-uploaded file into the workspace's Default
1925
+ * base. When curation is enabled, it may name, summarize, categorize, and
1926
+ * (confidence permitting) file the document into the best-matching base;
1927
+ * provider=none leaves caller metadata and Default placement unchanged.
1928
+ */
1929
+ async createKnowledgeDrop(workspaceId, request) {
1930
+ return await this.requestJson(
1931
+ "POST",
1932
+ `/v1/workspaces/${workspaceId}/knowledge/drops`,
1933
+ request
1934
+ );
1935
+ }
1936
+ /**
1937
+ * Move a document (and its indexed chunks) to another base. With no
1938
+ * targetBaseId, applies the document's stored curation suggestion.
1939
+ */
1940
+ async moveDocument(workspaceId, documentId, request = {}) {
1941
+ return await this.requestJson(
1942
+ "POST",
1943
+ `/v1/workspaces/${workspaceId}/documents/${documentId}/move`,
1944
+ request
1945
+ );
1946
+ }
1803
1947
  /** Retry indexing for a failed document. */
1804
1948
  async reindexDocument(workspaceId, baseId, documentId) {
1805
1949
  return await this.requestJson(
@@ -1982,6 +2126,15 @@ var OpenGeniClient = class {
1982
2126
  );
1983
2127
  return response.connection;
1984
2128
  }
2129
+ /** Validate and store/reinstall the workspace-shared OpenGeni Slack bot credential. */
2130
+ async connectOpenGeniSlackBot(workspaceId, request) {
2131
+ const response = await this.requestJson(
2132
+ "POST",
2133
+ `/v1/workspaces/${workspaceId}/connections/slack-bot`,
2134
+ request
2135
+ );
2136
+ return response.connection;
2137
+ }
1985
2138
  async updateConnection(workspaceId, connectionId, request) {
1986
2139
  const response = await this.requestJson(
1987
2140
  "PATCH",
@@ -2010,14 +2163,11 @@ var OpenGeniClient = class {
2010
2163
  return logoAssetPath ? `${this.baseUrl}/v1/${logoAssetPath}` : null;
2011
2164
  }
2012
2165
  // --- GitHub ----------------------------------------------------------------------------------
2013
- /** GitHub App configuration status; install/link URLs are null while new binding is disabled. */
2166
+ /** GitHub App server configuration plus truthful workspace binding status. */
2014
2167
  async getGitHubApp(workspaceId) {
2015
2168
  return await this.requestJson("GET", `/v1/workspaces/${workspaceId}/github/app`);
2016
2169
  }
2017
- /**
2018
- * Compatibility URL for previously issued state. New installation binding is
2019
- * disabled, so the endpoint validates state and terminates with HTTP 410.
2020
- */
2170
+ /** Build the GitHub owner-consent entry URL for fresh workspace-bound state. */
2021
2171
  githubConnectUrl(workspaceId, state) {
2022
2172
  return this.url(`/v1/workspaces/${workspaceId}/github/connect`, { state });
2023
2173
  }
@@ -2099,12 +2249,13 @@ var OpenGeniClient = class {
2099
2249
  return await this.requestJson("POST", "/v1/billing/checkout", request);
2100
2250
  }
2101
2251
  // --- Internals -------------------------------------------------------------
2102
- headers() {
2252
+ headers(correlationId) {
2103
2253
  const extra = typeof this.options.headers === "function" ? this.options.headers() : this.options.headers;
2104
2254
  return {
2105
2255
  ...this.options.apiKey ? { Authorization: `Bearer ${this.options.apiKey}` } : {},
2106
2256
  ...extra,
2107
- [OPENGENI_API_CONTRACT_HEADER]: OPENGENI_API_CONTRACT_REVISION
2257
+ [OPENGENI_API_CONTRACT_HEADER]: OPENGENI_API_CONTRACT_REVISION,
2258
+ ...correlationId ? { [OPENGENI_CORRELATION_HEADER]: correlationId } : {}
2108
2259
  };
2109
2260
  }
2110
2261
  url(path, query = {}) {
@@ -2220,36 +2371,62 @@ var OpenGeniClient = class {
2220
2371
  );
2221
2372
  }
2222
2373
  async requestJson(method, path, body, query = {}, options = {}) {
2223
- const response = await this.fetchImpl(this.url(path, query), {
2224
- method,
2225
- headers: {
2226
- ...this.headers(),
2227
- Accept: "application/json",
2228
- ...body !== void 0 ? { "Content-Type": "application/json" } : {}
2229
- },
2230
- ...body !== void 0 ? { body: JSON.stringify(body) } : {},
2231
- ...options.signal ? { signal: options.signal } : {}
2232
- });
2374
+ const correlationId = crypto.randomUUID();
2375
+ let response;
2376
+ try {
2377
+ response = await this.fetchImpl(this.url(path, query), {
2378
+ method,
2379
+ headers: {
2380
+ ...this.headers(correlationId),
2381
+ Accept: "application/json",
2382
+ ...body !== void 0 ? { "Content-Type": "application/json" } : {}
2383
+ },
2384
+ ...body !== void 0 ? { body: JSON.stringify(body) } : {},
2385
+ ...options.signal ? { signal: options.signal } : {}
2386
+ });
2387
+ } catch (error) {
2388
+ if (isMutationMethod(method)) {
2389
+ throw mutationTransportError(correlationId);
2390
+ }
2391
+ throw error;
2392
+ }
2233
2393
  assertApiContractResponse(response);
2234
2394
  if (!response.ok) {
2235
- throw new OpenGeniApiError(response.status, await safeText(response));
2395
+ throw await apiErrorFromResponse(response, { method, correlationId });
2396
+ }
2397
+ await assertJsonResponse(response, { method, correlationId });
2398
+ try {
2399
+ return await response.json();
2400
+ } catch (error) {
2401
+ if (isMutationMethod(method)) {
2402
+ throw mutationTransportError(correlationId);
2403
+ }
2404
+ throw error;
2236
2405
  }
2237
- return await response.json();
2238
2406
  }
2239
2407
  /** Like `requestJson` for endpoints that respond with no body (204). */
2240
2408
  async requestVoid(method, path, body) {
2241
- const response = await this.fetchImpl(this.url(path), {
2242
- method,
2243
- headers: {
2244
- ...this.headers(),
2245
- Accept: "application/json",
2246
- ...body !== void 0 ? { "Content-Type": "application/json" } : {}
2247
- },
2248
- ...body !== void 0 ? { body: JSON.stringify(body) } : {}
2249
- });
2409
+ const correlationId = crypto.randomUUID();
2410
+ let response;
2411
+ try {
2412
+ response = await this.fetchImpl(this.url(path), {
2413
+ method,
2414
+ headers: {
2415
+ ...this.headers(correlationId),
2416
+ Accept: "application/json",
2417
+ ...body !== void 0 ? { "Content-Type": "application/json" } : {}
2418
+ },
2419
+ ...body !== void 0 ? { body: JSON.stringify(body) } : {}
2420
+ });
2421
+ } catch (error) {
2422
+ if (isMutationMethod(method)) {
2423
+ throw mutationTransportError(correlationId);
2424
+ }
2425
+ throw error;
2426
+ }
2250
2427
  assertApiContractResponse(response);
2251
2428
  if (!response.ok) {
2252
- throw new OpenGeniApiError(response.status, await safeText(response));
2429
+ throw await apiErrorFromResponse(response, { method, correlationId });
2253
2430
  }
2254
2431
  }
2255
2432
  };
@@ -2259,25 +2436,62 @@ function assertApiContractResponse(response) {
2259
2436
  throw new OpenGeniApiContractMismatchError(OPENGENI_API_CONTRACT_REVISION, actual);
2260
2437
  }
2261
2438
  }
2262
- async function sha256ForUpload(body) {
2263
- const bytes = typeof body === "string" ? new TextEncoder().encode(body) : body instanceof Blob ? new Uint8Array(await body.arrayBuffer()) : new Uint8Array(body);
2264
- const digest = await globalThis.crypto.subtle.digest("SHA-256", bytes);
2265
- return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
2439
+ var API_ERROR_MAX_BYTES = 16 * 1024;
2440
+ async function apiErrorFromResponse(response, context) {
2441
+ return new OpenGeniApiError(response.status, await readBoundedJsonErrorBody(response), {
2442
+ correlationId: response.headers.get(OPENGENI_CORRELATION_HEADER) ?? context.correlationId,
2443
+ mutation: isMutationMethod(context.method)
2444
+ });
2266
2445
  }
2267
- async function safeText(response) {
2268
- try {
2269
- return await response.text();
2270
- } catch {
2446
+ async function assertJsonResponse(response, context) {
2447
+ if (isJsonContentType(response.headers.get("content-type"))) return;
2448
+ await cancelResponseBody(response, "unexpected non-JSON API response");
2449
+ throw new OpenGeniApiError(502, "", {
2450
+ code: "upstream_unavailable",
2451
+ retryable: true,
2452
+ correlationId: response.headers.get(OPENGENI_CORRELATION_HEADER) ?? context.correlationId,
2453
+ outcomeUnknown: isMutationMethod(context.method),
2454
+ displayMessage: "OpenGeni is temporarily unavailable \u2014 retry."
2455
+ });
2456
+ }
2457
+ async function readBoundedJsonErrorBody(response) {
2458
+ if (!isJsonContentType(response.headers.get("content-type"))) {
2459
+ await cancelResponseBody(response, "discarding API error body");
2460
+ return "";
2461
+ }
2462
+ if (Number(response.headers.get("content-length")) > API_ERROR_MAX_BYTES) {
2463
+ await cancelResponseBody(response, "discarding API error body");
2271
2464
  return "";
2272
2465
  }
2273
- }
2274
- async function safeBoundedText(response) {
2275
2466
  try {
2276
- return new TextDecoder().decode(await readBoundedResponseBytes(response, 64 * 1024, null));
2467
+ return new TextDecoder().decode(
2468
+ await readBoundedResponseBytes(response, API_ERROR_MAX_BYTES, null)
2469
+ );
2277
2470
  } catch {
2278
2471
  return "";
2279
2472
  }
2280
2473
  }
2474
+ function isJsonContentType(value) {
2475
+ return /^(application\/json|[^;]+\+json)\s*(;|$)/i.test(value ?? "");
2476
+ }
2477
+ function isMutationMethod(method) {
2478
+ return method !== "GET" && method !== "HEAD" && method !== "OPTIONS";
2479
+ }
2480
+ function mutationTransportError(correlationId) {
2481
+ return new OpenGeniApiError(0, "", {
2482
+ code: "network_error",
2483
+ retryable: true,
2484
+ correlationId,
2485
+ outcomeUnknown: true,
2486
+ mutation: true,
2487
+ displayMessage: "OpenGeni could not confirm the request \u2014 reconcile before retrying."
2488
+ });
2489
+ }
2490
+ async function sha256ForUpload(body) {
2491
+ const bytes = typeof body === "string" ? new TextEncoder().encode(body) : body instanceof Blob ? new Uint8Array(await body.arrayBuffer()) : new Uint8Array(body);
2492
+ const digest = await globalThis.crypto.subtle.digest("SHA-256", bytes);
2493
+ return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
2494
+ }
2281
2495
  async function cancelResponseBody(response, reason) {
2282
2496
  await response.body?.cancel(reason).catch(() => void 0);
2283
2497
  }
@@ -2511,6 +2725,11 @@ function ttydResizeFrame(columns, rows) {
2511
2725
  return TtydClientCommand.RESIZE + JSON.stringify({ columns, rows });
2512
2726
  }
2513
2727
 
2728
+ // src/workspace-instruction-policies.ts
2729
+ function normalizeWorkspaceInstructionPolicyRoleKey(value) {
2730
+ return value.normalize("NFKC").trim().toLowerCase().replace(/\s+/gu, "-").replace(/-+/g, "-");
2731
+ }
2732
+
2514
2733
  // src/transcription.ts
2515
2734
  var DEFAULT_WORKSPACE_TRANSCRIPTION_POLICY = {
2516
2735
  enabled: false,
@@ -2729,6 +2948,7 @@ export {
2729
2948
  KNOWN_USAGE_EVENT_TYPES,
2730
2949
  OPENGENI_API_CONTRACT_HEADER,
2731
2950
  OPENGENI_API_CONTRACT_REVISION,
2951
+ OPENGENI_CORRELATION_HEADER,
2732
2952
  OpenGeniApiContractMismatchError,
2733
2953
  OpenGeniApiError,
2734
2954
  OpenGeniClient,
@@ -2747,6 +2967,7 @@ export {
2747
2967
  formatSseEvent,
2748
2968
  isRetryableStreamError,
2749
2969
  nextDesktopState,
2970
+ normalizeWorkspaceInstructionPolicyRoleKey,
2750
2971
  parseSseStream,
2751
2972
  proxySessionEventStream,
2752
2973
  resolveWorkspaceTranscriptionPolicy,