@opengeni/sdk 0.20.0 → 0.25.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
@@ -1,14 +1,33 @@
1
1
  // src/errors.ts
2
2
  var OpenGeniApiError = class extends Error {
3
3
  status;
4
+ code;
4
5
  body;
5
6
  constructor(status, body) {
6
- super(`OpenGeni API ${status}: ${body || "(empty body)"}`);
7
+ const decoded = decodeApiErrorBody(body);
8
+ super(`OpenGeni API ${status}: ${decoded.message ?? (body || "(empty body)")}`);
7
9
  this.name = "OpenGeniApiError";
8
10
  this.status = status;
11
+ this.code = decoded.code;
9
12
  this.body = body;
10
13
  }
11
14
  };
15
+ function decodeApiErrorBody(body) {
16
+ if (!body) return {};
17
+ try {
18
+ const decoded = JSON.parse(body);
19
+ if (!decoded || typeof decoded !== "object" || Array.isArray(decoded)) return {};
20
+ const record = decoded;
21
+ return {
22
+ ...typeof record.code === "string" && record.code ? { code: record.code } : {},
23
+ ...typeof record.message === "string" && record.message ? { message: record.message } : {}
24
+ };
25
+ } catch {
26
+ return {};
27
+ }
28
+ }
29
+ var OpenGeniSessionListCursorError = class extends OpenGeniApiError {
30
+ };
12
31
  var OpenGeniApiContractMismatchError = class extends Error {
13
32
  expected;
14
33
  actual;
@@ -319,6 +338,7 @@ var SESSION_EVENT_TYPES = [
319
338
  "agent.reasoning.delta",
320
339
  "agent.toolCall.created",
321
340
  "agent.toolCall.output",
341
+ "agent.model.request",
322
342
  "agent.model.usage",
323
343
  "tool.auth_needed",
324
344
  "credential.auth_needed",
@@ -370,10 +390,13 @@ var SESSION_EVENT_TYPES = [
370
390
  "terminal.pty.output.delta",
371
391
  "terminal.pty.exited",
372
392
  "session.title_set",
393
+ "session.mcp.approval_policy.updated",
373
394
  // Multi-account Codex (P1): the session's inference account changed.
374
395
  "codex.account.switched",
375
396
  // credential allocator metadata-only per-turn credential selection audit.
376
397
  "codex.credential.selected",
398
+ // Bounded, identity-free deterministic shadow/replay decision.
399
+ "codex.fleet.decision",
377
400
  // credential allocator durable zero-capacity wait lifecycle. These are system/runtime
378
401
  // events, never synthetic user messages.
379
402
  "codex.capacity.waiting",
@@ -449,6 +472,8 @@ var KNOWN_PERMISSIONS = [
449
472
  ];
450
473
  var OPENGENI_API_CONTRACT_REVISION = "2026-07-turn-instructions-v1";
451
474
  var OPENGENI_API_CONTRACT_HEADER = "x-opengeni-api-contract";
475
+ var RETAINED_OUTPUT_DEFAULT_PAGE_BYTES = 256 * 1024;
476
+ var RETAINED_OUTPUT_MAX_PAGE_BYTES = 1024 * 1024;
452
477
  var KNOWN_USAGE_EVENT_TYPES = [
453
478
  "agent_run.created",
454
479
  "agent_run.completed",
@@ -465,6 +490,13 @@ var KNOWN_USAGE_EVENT_TYPES = [
465
490
  ];
466
491
 
467
492
  // src/client.ts
493
+ function sessionListQuery(options) {
494
+ const { limit, parentSessionId } = options;
495
+ return {
496
+ ...limit === void 0 ? {} : { limit: String(limit) },
497
+ ...parentSessionId === void 0 ? {} : { parentSessionId: parentSessionId ?? "null" }
498
+ };
499
+ }
468
500
  var OpenGeniClient = class {
469
501
  baseUrl;
470
502
  options;
@@ -482,6 +514,19 @@ var OpenGeniClient = class {
482
514
  request
483
515
  );
484
516
  }
517
+ async getNewSessionDraft(workspaceId) {
518
+ return await this.requestJson(
519
+ "GET",
520
+ `/v1/workspaces/${workspaceId}/new-session-draft`
521
+ );
522
+ }
523
+ async saveNewSessionDraft(workspaceId, request) {
524
+ return await this.requestJson(
525
+ "PUT",
526
+ `/v1/workspaces/${workspaceId}/new-session-draft`,
527
+ request
528
+ );
529
+ }
485
530
  async getSession(workspaceId, sessionId) {
486
531
  return await this.requestJson(
487
532
  "GET",
@@ -495,36 +540,66 @@ var OpenGeniClient = class {
495
540
  request
496
541
  );
497
542
  }
543
+ /**
544
+ * Replace one attached MCP server's approval policy. The change is captured
545
+ * by the next claimed attempt; already-claimed work keeps its immutable
546
+ * policy snapshot.
547
+ */
548
+ async updateSessionMcpApprovalPolicy(workspaceId, sessionId, serverId, request) {
549
+ return await this.requestJson(
550
+ "PATCH",
551
+ `/v1/workspaces/${workspaceId}/sessions/${sessionId}/mcp-servers/${encodeURIComponent(serverId)}/approval-policy`,
552
+ request
553
+ );
554
+ }
498
555
  async listSessions(workspaceId, options = {}) {
556
+ if (options.search?.trim()) {
557
+ const page = await this.listSessionPage(workspaceId, options);
558
+ return [...page.pinned, ...page.sessions];
559
+ }
499
560
  return await this.requestJson(
500
561
  "GET",
501
562
  `/v1/workspaces/${workspaceId}/sessions`,
502
563
  void 0,
503
- {
504
- ...options.limit !== void 0 ? { limit: String(options.limit) } : {},
505
- ...options.search?.trim() ? { search: options.search.trim() } : {},
506
- ...Object.prototype.hasOwnProperty.call(options, "parentSessionId") && options.parentSessionId !== void 0 ? {
507
- parentSessionId: options.parentSessionId === null ? "null" : String(options.parentSessionId)
508
- } : {}
509
- }
564
+ sessionListQuery(options)
510
565
  );
511
566
  }
512
567
  /** Pin-aware ordinary-session page with a stable keyset cursor. */
513
568
  async listSessionPage(workspaceId, options = {}) {
514
- return await this.requestJson(
515
- "GET",
516
- `/v1/workspaces/${workspaceId}/sessions`,
517
- void 0,
518
- {
519
- view: "page",
520
- ...options.limit !== void 0 ? { limit: String(options.limit) } : {},
521
- ...options.cursor !== void 0 ? { cursor: options.cursor } : {},
522
- ...options.search?.trim() ? { search: options.search.trim() } : {},
523
- ...Object.prototype.hasOwnProperty.call(options, "parentSessionId") && options.parentSessionId !== void 0 ? {
524
- parentSessionId: options.parentSessionId === null ? "null" : String(options.parentSessionId)
525
- } : {}
569
+ const search = options.search?.trim();
570
+ let response;
571
+ try {
572
+ response = await this.requestJson(
573
+ "GET",
574
+ `/v1/workspaces/${workspaceId}/sessions`,
575
+ void 0,
576
+ {
577
+ view: "page",
578
+ ...sessionListQuery(options),
579
+ ...options.cursor !== void 0 ? { cursor: options.cursor } : {},
580
+ ...search ? { search } : {},
581
+ ...options.pinsOnly ? { pinsOnly: "true" } : {}
582
+ }
583
+ );
584
+ } catch (error) {
585
+ if (error instanceof OpenGeniApiError && error.status === 410) {
586
+ throw new OpenGeniSessionListCursorError(error.status, error.body);
526
587
  }
527
- );
588
+ throw error;
589
+ }
590
+ if (Array.isArray(response)) {
591
+ if (options.cursor) {
592
+ throw new Error("The connected OpenGeni API does not support stable session-page cursors");
593
+ }
594
+ if (search) {
595
+ throw new Error("The connected OpenGeni API does not support session search");
596
+ }
597
+ if (options.pinsOnly) {
598
+ throw new Error("The connected OpenGeni API does not support pins-only session lists");
599
+ }
600
+ return { pinned: [], sessions: response, nextCursor: null };
601
+ }
602
+ return response;
528
603
  }
529
604
  /** Set this authenticated member's personal workspace pin for a session. */
530
605
  async updateSessionPin(workspaceId, sessionId, request) {
@@ -673,26 +748,30 @@ var OpenGeniClient = class {
673
748
  async listEvents(workspaceId, sessionId, options = {}) {
674
749
  return (await this.listEventPage(workspaceId, sessionId, options)).events;
675
750
  }
676
- /** Bounded durable/monitoring page plus exact projection and cursor facts. */
677
751
  async listEventPage(workspaceId, sessionId, options = {}) {
678
752
  if (options.latest && ["includeTypes", "excludeTypes", "includeClasses", "excludeClasses"].some(
679
753
  (name) => Object.prototype.hasOwnProperty.call(options, name)
680
754
  )) {
681
755
  throw new TypeError("latest cannot be combined with event filters");
682
756
  }
757
+ if (options.resultMode === "compact" && !options.latest) {
758
+ throw new TypeError("resultMode=compact requires latest");
759
+ }
760
+ const listOptions = options.resultMode === "compact" ? null : options;
683
761
  const response = await this.fetchImpl(
684
762
  this.url(`/v1/workspaces/${workspaceId}/sessions/${sessionId}/events`, {
685
- ...options.after !== void 0 ? { after: String(options.after) } : {},
686
- ...options.before !== void 0 ? { before: String(options.before) } : {},
687
- ...options.limit !== void 0 ? { limit: String(options.limit) } : {},
688
- ...options.compact ? { compact: "1" } : {},
763
+ ...listOptions?.after !== void 0 ? { after: String(listOptions.after) } : {},
764
+ ...listOptions?.before !== void 0 ? { before: String(listOptions.before) } : {},
765
+ ...listOptions?.limit !== void 0 ? { limit: String(listOptions.limit) } : {},
766
+ ...listOptions?.compact ? { compact: "1" } : {},
689
767
  ...options.mode ? { mode: options.mode } : {},
690
- ...options.direction ? { direction: options.direction } : {},
768
+ ...listOptions?.direction ? { direction: listOptions.direction } : {},
691
769
  ...options.payloadMode ? { payloadMode: options.payloadMode } : {},
692
- ...options.includeTypes?.length ? { includeTypes: options.includeTypes.join(",") } : {},
693
- ...options.excludeTypes?.length ? { excludeTypes: options.excludeTypes.join(",") } : {},
694
- ...options.includeClasses?.length ? { includeClasses: options.includeClasses.join(",") } : {},
695
- ...options.excludeClasses?.length ? { excludeClasses: options.excludeClasses.join(",") } : {},
770
+ ...options.resultMode ? { resultMode: options.resultMode } : {},
771
+ ...listOptions?.includeTypes?.length ? { includeTypes: listOptions.includeTypes.join(",") } : {},
772
+ ...listOptions?.excludeTypes?.length ? { excludeTypes: listOptions.excludeTypes.join(",") } : {},
773
+ ...listOptions?.includeClasses?.length ? { includeClasses: listOptions.includeClasses.join(",") } : {},
774
+ ...listOptions?.excludeClasses?.length ? { excludeClasses: listOptions.excludeClasses.join(",") } : {},
696
775
  ...options.latest ? { latest: options.latest } : {}
697
776
  }),
698
777
  {
@@ -702,7 +781,11 @@ var OpenGeniClient = class {
702
781
  );
703
782
  assertApiContractResponse(response);
704
783
  if (!response.ok) throw new OpenGeniApiError(response.status, await safeText(response));
705
- const events = await response.json();
784
+ const body = await response.json();
785
+ if (options.resultMode === "compact") {
786
+ return body;
787
+ }
788
+ const events = body;
706
789
  const integerHeader = (name) => {
707
790
  const raw = response.headers.get(name);
708
791
  if (raw === null) return null;
@@ -735,6 +818,18 @@ var OpenGeniClient = class {
735
818
  forensicExact: response.headers.get("X-OpenGeni-Forensic-Exact") === "true"
736
819
  };
737
820
  }
821
+ /**
822
+ * Fetch the authoritative newest-sequence semantic result directly. This is
823
+ * the callback-loss recovery path: it reads one compact durable result and
824
+ * never creates a model turn. `latest: "receipt"` aliases `tool_receipt`;
825
+ * turn generation remains scoped retry metadata.
826
+ */
827
+ async getLatestEventResult(workspaceId, sessionId, options = { latest: "terminal" }) {
828
+ return await this.listEventPage(workspaceId, sessionId, {
829
+ ...options,
830
+ resultMode: "compact"
831
+ });
832
+ }
738
833
  /** POST a user/control event to the session. Returns the accepted event. */
739
834
  async sendEvent(workspaceId, sessionId, event) {
740
835
  return await this.requestJson(
@@ -1261,6 +1356,13 @@ var OpenGeniClient = class {
1261
1356
  }
1262
1357
  return config;
1263
1358
  }
1359
+ /** Authenticated model definitions plus workspace-specific selectability. */
1360
+ async getWorkspaceModelCatalog(workspaceId) {
1361
+ return await this.requestJson(
1362
+ "GET",
1363
+ `/v1/workspaces/${workspaceId}/model-catalog`
1364
+ );
1365
+ }
1264
1366
  /** The caller's access context: subject, account + workspace grants, defaults. */
1265
1367
  async getAccessContext() {
1266
1368
  return await this.requestJson("GET", "/v1/access/me");
@@ -1561,13 +1663,14 @@ var OpenGeniClient = class {
1561
1663
  * -> complete. Returns the ready `FileAsset`.
1562
1664
  */
1563
1665
  async uploadFile(workspaceId, input) {
1564
- const body = input.data instanceof Uint8Array ? new Blob([input.data.slice()]) : input.data;
1666
+ const body = input.data instanceof Uint8Array ? new Blob([input.data.slice()]) : input.data instanceof ArrayBuffer ? input.data.slice(0) : input.data;
1565
1667
  const sizeBytes = typeof body === "string" ? new TextEncoder().encode(body).byteLength : body instanceof Blob ? body.size : body.byteLength;
1668
+ const sha256 = input.sha256 ?? await sha256ForUpload(body);
1566
1669
  const upload = await this.beginFileUpload(workspaceId, {
1567
1670
  filename: input.filename,
1568
1671
  contentType: input.contentType,
1569
1672
  sizeBytes,
1570
- ...input.sha256 !== void 0 ? { sha256: input.sha256 } : {}
1673
+ sha256
1571
1674
  });
1572
1675
  const putResponse = await this.fetchImpl(upload.putUrl, {
1573
1676
  method: "PUT",
@@ -1591,6 +1694,71 @@ var OpenGeniClient = class {
1591
1694
  `/v1/workspaces/${workspaceId}/files/${fileId}`
1592
1695
  );
1593
1696
  }
1697
+ /** Read provider-neutral retained evidence metadata; never returns a storage location. */
1698
+ async getRetainedArtifact(workspaceId, artifactId) {
1699
+ return await this.requestJson(
1700
+ "GET",
1701
+ `/v1/workspaces/${workspaceId}/artifacts/${artifactId}`
1702
+ );
1703
+ }
1704
+ /**
1705
+ * Read at most one authenticated retained-evidence range from the API. This
1706
+ * deliberately does not use the ordinary signed file-download URL.
1707
+ */
1708
+ async getRetainedArtifactContent(workspaceId, artifactId, options = {}) {
1709
+ if (options.range && (options.range.length > 128 || /[^\x20-\x7e]/.test(options.range))) {
1710
+ throw new RangeError("retained artifact range must be at most 128 printable ASCII bytes");
1711
+ }
1712
+ const response = await this.fetchImpl(
1713
+ this.url(`/v1/workspaces/${workspaceId}/artifacts/${artifactId}/content`),
1714
+ {
1715
+ method: "GET",
1716
+ headers: {
1717
+ ...this.headers(),
1718
+ Accept: "application/octet-stream",
1719
+ ...options.range ? { Range: options.range } : {}
1720
+ },
1721
+ ...options.signal ? { signal: options.signal } : {}
1722
+ }
1723
+ );
1724
+ try {
1725
+ assertApiContractResponse(response);
1726
+ } catch (error) {
1727
+ await cancelResponseBody(response, "retained artifact API contract mismatch");
1728
+ throw error;
1729
+ }
1730
+ if (!response.ok) {
1731
+ throw new OpenGeniApiError(response.status, await safeBoundedText(response));
1732
+ }
1733
+ if (response.status !== 200 && response.status !== 206) {
1734
+ await cancelResponseBody(response, "unexpected retained artifact response status");
1735
+ throw new OpenGeniApiError(response.status, "unexpected retained artifact response status");
1736
+ }
1737
+ if (response.headers.get("accept-ranges") !== "bytes") {
1738
+ await cancelResponseBody(response, "retained artifact response omitted byte-range support");
1739
+ throw new OpenGeniApiError(502, "retained artifact response omitted byte-range support");
1740
+ }
1741
+ let declaredLength;
1742
+ try {
1743
+ declaredLength = parseBoundedContentLength(response.headers.get("content-length"));
1744
+ } catch (error) {
1745
+ await cancelResponseBody(response, "invalid retained artifact content-length");
1746
+ throw error;
1747
+ }
1748
+ const bytes = await readBoundedResponseBytes(
1749
+ response,
1750
+ RETAINED_OUTPUT_MAX_PAGE_BYTES,
1751
+ declaredLength
1752
+ );
1753
+ return {
1754
+ bytes,
1755
+ status: response.status,
1756
+ contentType: response.headers.get("content-type") ?? "application/octet-stream",
1757
+ contentLength: bytes.byteLength,
1758
+ contentRange: response.headers.get("content-range"),
1759
+ acceptRanges: "bytes"
1760
+ };
1761
+ }
1594
1762
  /** Mint a short-lived signed download URL for a ready file. */
1595
1763
  async createFileDownloadUrl(workspaceId, fileId) {
1596
1764
  return await this.requestJson(
@@ -1984,6 +2152,13 @@ var OpenGeniClient = class {
1984
2152
  `/v1/workspaces/${workspaceId}/codex/usage/refresh`
1985
2153
  );
1986
2154
  }
2155
+ /** Live independently-settled quota + reset-credit overview for every account. */
2156
+ async codexOverview(workspaceId) {
2157
+ return await this.requestJson(
2158
+ "GET",
2159
+ `/v1/workspaces/${workspaceId}/codex/overview`
2160
+ );
2161
+ }
1987
2162
  /** Disconnect ALL accounts (legacy workspace-wide). Prefer `disconnectCodexAccount`. */
1988
2163
  async codexDisconnect(workspaceId) {
1989
2164
  return await this.requestJson(
@@ -2013,6 +2188,14 @@ var OpenGeniClient = class {
2013
2188
  patch
2014
2189
  );
2015
2190
  }
2191
+ /** Toggle only NEW automatic allocations under independent allocator OCC. */
2192
+ async setCodexAccountAllocator(workspaceId, accountId, input) {
2193
+ return await this.requestJson(
2194
+ "PATCH",
2195
+ `/v1/workspaces/${workspaceId}/codex/accounts/${accountId}/allocator`,
2196
+ input
2197
+ );
2198
+ }
2016
2199
  /** Disconnect ONE Codex account by id (re-picks active when the removed one was active). */
2017
2200
  async disconnectCodexAccount(workspaceId, accountId) {
2018
2201
  return await this.requestJson(
@@ -2076,6 +2259,11 @@ function assertApiContractResponse(response) {
2076
2259
  throw new OpenGeniApiContractMismatchError(OPENGENI_API_CONTRACT_REVISION, actual);
2077
2260
  }
2078
2261
  }
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("");
2266
+ }
2079
2267
  async function safeText(response) {
2080
2268
  try {
2081
2269
  return await response.text();
@@ -2083,6 +2271,62 @@ async function safeText(response) {
2083
2271
  return "";
2084
2272
  }
2085
2273
  }
2274
+ async function safeBoundedText(response) {
2275
+ try {
2276
+ return new TextDecoder().decode(await readBoundedResponseBytes(response, 64 * 1024, null));
2277
+ } catch {
2278
+ return "";
2279
+ }
2280
+ }
2281
+ async function cancelResponseBody(response, reason) {
2282
+ await response.body?.cancel(reason).catch(() => void 0);
2283
+ }
2284
+ function parseBoundedContentLength(value) {
2285
+ if (value === null) return null;
2286
+ if (!/^\d+$/.test(value)) {
2287
+ throw new OpenGeniApiError(502, "invalid retained artifact content-length");
2288
+ }
2289
+ const length = Number(value);
2290
+ if (!Number.isSafeInteger(length) || length > RETAINED_OUTPUT_MAX_PAGE_BYTES) {
2291
+ throw new OpenGeniApiError(502, "retained artifact response exceeds the SDK byte limit");
2292
+ }
2293
+ return length;
2294
+ }
2295
+ async function readBoundedResponseBytes(response, maxBytes, expectedBytes) {
2296
+ if (!response.body) {
2297
+ if (expectedBytes !== null && expectedBytes !== 0) {
2298
+ throw new OpenGeniApiError(502, "retained artifact response length mismatch");
2299
+ }
2300
+ return new Uint8Array();
2301
+ }
2302
+ const reader = response.body.getReader();
2303
+ const chunks = [];
2304
+ let totalBytes = 0;
2305
+ try {
2306
+ while (true) {
2307
+ const { done, value } = await reader.read();
2308
+ if (done) break;
2309
+ totalBytes += value.byteLength;
2310
+ if (totalBytes > maxBytes) {
2311
+ await reader.cancel("retained artifact response exceeded the SDK byte limit").catch(() => void 0);
2312
+ throw new OpenGeniApiError(502, "retained artifact response exceeds the SDK byte limit");
2313
+ }
2314
+ chunks.push(value);
2315
+ }
2316
+ } finally {
2317
+ reader.releaseLock();
2318
+ }
2319
+ if (expectedBytes !== null && totalBytes !== expectedBytes) {
2320
+ throw new OpenGeniApiError(502, "retained artifact response length mismatch");
2321
+ }
2322
+ const bytes = new Uint8Array(totalBytes);
2323
+ let offset = 0;
2324
+ for (const chunk of chunks) {
2325
+ bytes.set(chunk, offset);
2326
+ offset += chunk.byteLength;
2327
+ }
2328
+ return bytes;
2329
+ }
2086
2330
 
2087
2331
  // src/proxy.ts
2088
2332
  function formatSseEvent(event) {
@@ -2488,7 +2732,10 @@ export {
2488
2732
  OpenGeniApiContractMismatchError,
2489
2733
  OpenGeniApiError,
2490
2734
  OpenGeniClient,
2735
+ OpenGeniSessionListCursorError,
2491
2736
  OpenGeniStreamError,
2737
+ RETAINED_OUTPUT_DEFAULT_PAGE_BYTES,
2738
+ RETAINED_OUTPUT_MAX_PAGE_BYTES,
2492
2739
  SESSION_EVENT_TYPES,
2493
2740
  TTYD_SUBPROTOCOL,
2494
2741
  TtydClientCommand,