@opengeni/sdk 0.48.0 → 0.52.1

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 (99) hide show
  1. package/README.md +93 -1
  2. package/dist/artifact-client.d.ts +20 -2
  3. package/dist/artifacts.d.ts +4 -0
  4. package/dist/artifacts.js +11 -0
  5. package/dist/artifacts.js.map +1 -0
  6. package/dist/chunk-FHI4DFIG.js +128 -0
  7. package/dist/chunk-FHI4DFIG.js.map +1 -0
  8. package/dist/chunk-FRJFNDIR.js +218 -0
  9. package/dist/chunk-FRJFNDIR.js.map +1 -0
  10. package/dist/{chunk-MBGBLVUV.js → chunk-ILQZR5N6.js} +1088 -577
  11. package/dist/chunk-ILQZR5N6.js.map +1 -0
  12. package/dist/chunk-MZWTWOX6.js +661 -0
  13. package/dist/chunk-MZWTWOX6.js.map +1 -0
  14. package/dist/chunk-VWE2QIVO.js +1162 -0
  15. package/dist/chunk-VWE2QIVO.js.map +1 -0
  16. package/dist/chunk-VYCTL62C.js +230 -0
  17. package/dist/chunk-VYCTL62C.js.map +1 -0
  18. package/dist/client.d.ts +150 -16
  19. package/dist/codex-realtime-controller.d.ts +6 -6
  20. package/dist/codex-realtime-lifecycle.d.ts +1 -1
  21. package/dist/codex-realtime-v3.d.ts +3 -3
  22. package/dist/codex-realtime.d.ts +1 -1
  23. package/dist/company-profile.d.ts +100 -0
  24. package/dist/core.d.ts +5 -5
  25. package/dist/core.js +9 -4
  26. package/dist/desktop.d.ts +1 -1
  27. package/dist/editable-artifact-resources.d.ts +96 -0
  28. package/dist/editable-artifacts/browser-session.d.ts +35 -0
  29. package/dist/editable-artifacts/controller.d.ts +60 -0
  30. package/dist/editable-artifacts/errors.d.ts +23 -0
  31. package/dist/editable-artifacts/http-live-transport.d.ts +46 -0
  32. package/dist/editable-artifacts/index.d.ts +19 -0
  33. package/dist/editable-artifacts/pool.d.ts +15 -0
  34. package/dist/editable-artifacts/session.d.ts +96 -0
  35. package/dist/editable-artifacts/storage.d.ts +72 -0
  36. package/dist/editable-artifacts/types.d.ts +416 -0
  37. package/dist/editable-artifacts/worker/browser-client.d.ts +80 -0
  38. package/dist/editable-artifacts/worker/browser-entry.d.ts +6 -0
  39. package/dist/editable-artifacts/worker/kernel-adapter.d.ts +45 -0
  40. package/dist/editable-artifacts/worker/rpc-protocol.d.ts +92 -0
  41. package/dist/editable-artifacts/worker/runtime.d.ts +66 -0
  42. package/dist/editable-artifacts/worker/wire-codec.d.ts +52 -0
  43. package/dist/editable-artifacts-worker.d.ts +8 -0
  44. package/dist/editable-artifacts-worker.js +1708 -0
  45. package/dist/editable-artifacts-worker.js.map +1 -0
  46. package/dist/editable-artifacts.d.ts +6 -0
  47. package/dist/editable-artifacts.js +5954 -0
  48. package/dist/editable-artifacts.js.map +1 -0
  49. package/dist/gateway-realtime-transport.d.ts +1 -1
  50. package/dist/index.d.ts +41 -35
  51. package/dist/index.js +104 -68
  52. package/dist/index.js.map +1 -1
  53. package/dist/interaction.d.ts +885 -0
  54. package/dist/interaction.js +55 -0
  55. package/dist/interaction.js.map +1 -0
  56. package/dist/memory-slack-client.d.ts +13 -0
  57. package/dist/memory-slack-delivery.d.ts +99 -0
  58. package/dist/memory-slack.d.ts +4 -0
  59. package/dist/memory-slack.js +44 -0
  60. package/dist/memory-slack.js.map +1 -0
  61. package/dist/proxy.d.ts +3 -3
  62. package/dist/realtime.d.ts +12 -12
  63. package/dist/retained-artifacts.d.ts +12 -0
  64. package/dist/stream.d.ts +1 -1
  65. package/dist/terminal.d.ts +1 -1
  66. package/dist/types.d.ts +1128 -18
  67. package/dist/workspace-control-stream.d.ts +2 -2
  68. package/dist/workspace-state.d.ts +1 -1
  69. package/package.json +29 -3
  70. package/src/artifact-client.ts +129 -0
  71. package/src/artifacts.ts +17 -0
  72. package/src/client.ts +1511 -200
  73. package/src/company-profile.ts +92 -0
  74. package/src/editable-artifact-resources.ts +119 -0
  75. package/src/editable-artifacts/browser-session.ts +176 -0
  76. package/src/editable-artifacts/controller.ts +2632 -0
  77. package/src/editable-artifacts/errors.ts +52 -0
  78. package/src/editable-artifacts/http-live-transport.ts +1449 -0
  79. package/src/editable-artifacts/index.ts +175 -0
  80. package/src/editable-artifacts/pool.ts +81 -0
  81. package/src/editable-artifacts/session.ts +778 -0
  82. package/src/editable-artifacts/storage.ts +1718 -0
  83. package/src/editable-artifacts/types.ts +499 -0
  84. package/src/editable-artifacts/worker/browser-client.ts +1167 -0
  85. package/src/editable-artifacts/worker/browser-entry.ts +57 -0
  86. package/src/editable-artifacts/worker/kernel-adapter.ts +1039 -0
  87. package/src/editable-artifacts/worker/rpc-protocol.ts +622 -0
  88. package/src/editable-artifacts/worker/runtime.ts +1204 -0
  89. package/src/editable-artifacts/worker/wire-codec.ts +852 -0
  90. package/src/editable-artifacts-worker.ts +8 -0
  91. package/src/editable-artifacts.ts +6 -0
  92. package/src/index.ts +185 -0
  93. package/src/interaction.ts +1830 -0
  94. package/src/memory-slack-client.ts +71 -0
  95. package/src/memory-slack-delivery.ts +128 -0
  96. package/src/memory-slack.ts +19 -0
  97. package/src/retained-artifacts.ts +326 -0
  98. package/src/types.ts +1343 -28
  99. package/dist/chunk-MBGBLVUV.js.map +0 -1
package/src/client.ts CHANGED
@@ -12,6 +12,48 @@ import {
12
12
  streamWorkspaceControlEvents,
13
13
  type WorkspaceControlStreamTransport,
14
14
  } from "./workspace-control-stream";
15
+ import {
16
+ OpenGeniInteractionClient,
17
+ type AttachedBrowserDevice,
18
+ type AttachedBrowserDeviceListOptions,
19
+ type AttachedBrowserDeviceListResponse,
20
+ type BrowserActionReceipt,
21
+ type BrowserActionRequest,
22
+ type BrowserDiagnosticBatch,
23
+ type BrowserDiagnosticsOptions,
24
+ type BrowserIdentity,
25
+ type BrowserIdentityListOptions,
26
+ type BrowserIdentityListResponse,
27
+ type BrowserIdentityMutationResponse,
28
+ type BrowserObservation,
29
+ type BrowserOpenTargetRequest,
30
+ type BrowserSession,
31
+ type BrowserSessionAttachment,
32
+ type BrowserSessionAttachmentRequest,
33
+ type BrowserSessionHeartbeatResponse,
34
+ type BrowserSessionLifecycleRequest,
35
+ type BrowserSessionListResponse,
36
+ type BrowserSessionMutationResponse,
37
+ type BrowserTarget,
38
+ type BrowserTargetListResponse,
39
+ type BrowserRevisionListResponse,
40
+ type ComputerActionReceipt,
41
+ type ComputerActionRequest,
42
+ type ComputerObservation,
43
+ type ComputerSession,
44
+ type ComputerSessionAttachment,
45
+ type ComputerSessionAttachmentRequest,
46
+ type ComputerSessionHeartbeatResponse,
47
+ type ComputerSessionLifecycleRequest,
48
+ type ComputerSessionListResponse,
49
+ type ComputerSessionMutationResponse,
50
+ type ComputerTargetListResponse,
51
+ type CreateBrowserIdentityRequest,
52
+ type CreateBrowserSessionRequest,
53
+ type CreateComputerSessionRequest,
54
+ type PublishBrowserRevisionRequest,
55
+ type PublishBrowserRevisionResponse,
56
+ } from "./interaction";
15
57
  import type {
16
58
  AccessContext,
17
59
  ActivateCodexRealtimeConnectionRequest,
@@ -41,6 +83,29 @@ import type {
41
83
  CapabilityCatalogItem,
42
84
  CapabilityCatalogResponse,
43
85
  CapabilityInstallation,
86
+ ApiIntegrationPreview,
87
+ ApiIntegrationOAuthStartRequest,
88
+ ApiIntegrationUninstallPreview,
89
+ InstallApiIntegrationRequest,
90
+ InstalledApiIntegration,
91
+ IntegrationFeatureMutationResult,
92
+ IntegrationFeatureRemovalResult,
93
+ IntegrationInstanceFeaturesResponse,
94
+ ListApiIntegrationPresetsResponse,
95
+ ListApiIntegrationsResponse,
96
+ MutateIntegrationFeatureRequest,
97
+ PreviewApiIntegrationRequest,
98
+ UninstallApiIntegrationRequest,
99
+ UninstallApiIntegrationResult,
100
+ UpsertIntegrationFeatureRequest,
101
+ PreviewPluginRequest,
102
+ PluginPreview,
103
+ InstallPluginRequest,
104
+ InstalledPlugin,
105
+ ListInstalledPluginsResponse,
106
+ PluginUninstallPreview,
107
+ UninstallPluginRequest,
108
+ UninstallPluginResult,
44
109
  AddDocumentRequest,
45
110
  CreateKnowledgeDropRequest,
46
111
  MoveDocumentRequest,
@@ -54,6 +119,8 @@ import type {
54
119
  CreateApiKeyRequest,
55
120
  CreateApiKeyResponse,
56
121
  CreateCapabilityCatalogItemRequest,
122
+ InstallSkillRequest,
123
+ InstalledSkill,
57
124
  CreateCheckoutRequest,
58
125
  CreateCheckoutResponse,
59
126
  OpenGeniSlackBotInstallRequest,
@@ -91,7 +158,9 @@ import type {
91
158
  GetPackResponse,
92
159
  GitHubAppInfo,
93
160
  GitHubRepositoriesResponse,
161
+ GoogleDriveBrowseResponse,
94
162
  GoogleDriveDisconnectRequest,
163
+ SaveGoogleDriveIntegrationSourceRequest,
95
164
  GoogleDriveLifecycleActionRequest,
96
165
  KnowledgeMemory,
97
166
  KnowledgeMemorySearchRequest,
@@ -107,21 +176,42 @@ import type {
107
176
  SwapActiveSandboxRequest,
108
177
  SwapActiveSandboxResponse,
109
178
  ListWorkspaceMembersResponse,
179
+ InstallPackRequest,
180
+ PackInstallationPreview,
181
+ ListSlackUserLinkAccessRequestsResponse,
182
+ PrepareSlackUserLinkAccessRequest,
183
+ SlackUserLinkAccessMutationRequest,
184
+ SlackUserLinkAccessRequest,
185
+ ApproveSlackUserLinkAccessRequest,
110
186
  PackInstallation,
187
+ PackUninstallPreview,
111
188
  LatencyMode,
112
189
  ReasoningEffort,
113
190
  RetainedScreenshotDownload,
114
191
  RetainedScreenshotDownloadOptions,
192
+ RetainedArtifactDownload,
193
+ RetainedArtifactDownloadOptions,
194
+ RetainedArtifactReference,
115
195
  RetainedArtifactContent,
116
196
  RetainedArtifactContentOptions,
117
197
  RetainedArtifactMetadata,
198
+ UpdateVideoGenerationPolicyRequest,
199
+ VideoArtifactPlaybackSource,
200
+ VideoGenerationOperationSummary,
201
+ VideoGenerationPolicy,
202
+ WorkspaceVideoGenerationSettings,
118
203
  RegisterCapabilityPackRequest,
119
204
  ResourceRef,
205
+ PreviewPackInstallationRequest,
206
+ PreviewSkillImportRequest,
120
207
  ScheduledTask,
121
208
  ScheduledTaskRun,
122
209
  Session,
123
210
  SessionListResponse,
211
+ AgentTopologyPageResponse,
124
212
  UpdateSessionPinRequest,
213
+ UninstallPackRequest,
214
+ UninstallPackResult,
125
215
  SessionEvent,
126
216
  SessionEventCompactResult,
127
217
  SessionEventCompactResultOptions,
@@ -135,6 +225,7 @@ import type {
135
225
  SyncSessionRealtimeLedgerResponse,
136
226
  RenewSessionRealtimeRequest,
137
227
  SessionMcpCredentialUpdateInput,
228
+ SubmittedTimelineAnnotation,
138
229
  UpdateSessionMcpApprovalPolicyRequest,
139
230
  UpdateSessionMcpApprovalPolicyResponse,
140
231
  SessionQueueSnapshot,
@@ -226,12 +317,20 @@ import type {
226
317
  WorkspaceRegisteredPack,
227
318
  Workspace,
228
319
  ListConnectionsResponse,
320
+ ListSlackInstallationBindingsResponse,
321
+ SlackInstallationBinding,
229
322
  ConnectionResponse,
230
323
  OAuthStartRequest,
231
324
  OAuthStartResponse,
232
325
  SocialConnection,
233
326
  SocialOAuthStartRequest,
327
+ SkillImportPreview,
328
+ SkillUninstallPreview,
329
+ UninstallSkillRequest,
330
+ UninstallSkillResult,
234
331
  } from "./types";
332
+ import { GENERATED_VIDEO_MAX_BYTES } from "./types";
333
+ import { parseRetainedGeneratedImageReference } from "./retained-artifacts";
235
334
  import type {
236
335
  ActivateWorkspaceInstructionPolicyRequest,
237
336
  CreateWorkspaceInstructionPolicyDraftRequest,
@@ -248,6 +347,17 @@ import type {
248
347
  WorkspaceInstructionPolicyOnboardingProposalListResponse,
249
348
  WorkspaceInstructionPolicyRevision,
250
349
  } from "./workspace-instruction-policies";
350
+ import type {
351
+ ActivateCompanyProfileRevisionRequest,
352
+ CompanyProfileDiffRequest,
353
+ CompanyProfileDiffResponse,
354
+ CompanyProfileListOptions,
355
+ CompanyProfileListResponse,
356
+ CompanyProfileMutationResponse,
357
+ CompanyProfileRevision,
358
+ RollbackCompanyProfileRequest,
359
+ UpdateCompanyProfileRequest,
360
+ } from "./company-profile";
251
361
  import type {
252
362
  WorkspaceStateExportResponse,
253
363
  WorkspaceStateGetOptions,
@@ -315,6 +425,7 @@ export type OpenGeniRequestOptions = {
315
425
 
316
426
  export type SendMessageInput = {
317
427
  text: string;
428
+ annotations?: SubmittedTimelineAnnotation[];
318
429
  /** System instructions scoped to this exact turn; never visible timeline text. */
319
430
  turnInstructions?: string;
320
431
  resources?: ResourceRef[];
@@ -333,6 +444,10 @@ export type SteerMessageResult = {
333
444
  accepted: SessionEvent;
334
445
  /** The exact turn created for this message in the same server transaction. */
335
446
  turn: SessionTurn;
447
+ /** Number of live attempts durably asked to stop by this atomic Steer. */
448
+ interruptionCount?: number;
449
+ /** True when this response came from the command's immutable idempotency receipt. */
450
+ replay?: boolean;
336
451
  };
337
452
 
338
453
  export type TranscribeAudioInput = {
@@ -375,12 +490,15 @@ export class OpenGeniClient {
375
490
  private readonly fetchImpl: FetchLike;
376
491
  private readonly readInFlight = new Map<string, Promise<unknown>>();
377
492
  private readonly readTrailing = new Map<string, Promise<unknown>>();
493
+ /** Resource-oriented Browser/Computer facade over this exact authenticated client. */
494
+ readonly interaction: OpenGeniInteractionClient;
378
495
 
379
496
  constructor(options: OpenGeniClientOptions) {
380
497
  this.baseUrl = options.baseUrl.replace(/\/+$/, "");
381
498
  this.options = options;
382
499
  // Bind lazily so variable sets that polyfill fetch after module load work.
383
500
  this.fetchImpl = options.fetch ?? ((input, init) => fetch(input, init));
501
+ this.interaction = new OpenGeniInteractionClient(this);
384
502
  }
385
503
 
386
504
  // --- Session lifecycle ---------------------------------------------------
@@ -769,6 +887,32 @@ export class OpenGeniClient {
769
887
  return response;
770
888
  }
771
889
 
890
+ /** Compact, bounded workspace agent hierarchy page. */
891
+ async listAgentTopology(
892
+ workspaceId: string,
893
+ options: {
894
+ limit?: number;
895
+ parentSessionId?: string | null;
896
+ cursor?: string;
897
+ search?: string;
898
+ } = {},
899
+ ): Promise<AgentTopologyPageResponse> {
900
+ const search = options.search?.trim();
901
+ return await this.requestJson<AgentTopologyPageResponse>(
902
+ "GET",
903
+ `/v1/workspaces/${workspaceId}/agent-topology`,
904
+ undefined,
905
+ {
906
+ ...(options.limit !== undefined ? { limit: String(options.limit) } : {}),
907
+ ...(options.parentSessionId === undefined
908
+ ? {}
909
+ : { parentSessionId: options.parentSessionId ?? "null" }),
910
+ ...(options.cursor ? { cursor: options.cursor } : {}),
911
+ ...(search ? { search } : {}),
912
+ },
913
+ );
914
+ }
915
+
772
916
  /** Set this authenticated member's personal workspace pin for a session. */
773
917
  async updateSessionPin(
774
918
  workspaceId: string,
@@ -2087,13 +2231,11 @@ export class OpenGeniClient {
2087
2231
  }
2088
2232
 
2089
2233
  /** Attach a viewer holder (refcounted liveness — keeps the box warm while
2090
- * watched/used), spinning the box up in-process when cold, and mint the scoped
2091
- * direct-to-provider URLs for the requested plane(s). `request.desktop:true`
2092
- * opts into the un-redacted pixel plane and mints the noVNC URL — that plane
2093
- * alone throws `OpenGeniApiError(409)` when the un-redacted/shared
2094
- * acknowledgment is missing (the consent gate). A terminal-only attach
2095
- * (`desktop` omitted/false) warms the box + mints the pty-ws terminal cell with
2096
- * NO consent gate. An omitted `viewerId` mints a fresh one. */
2234
+ * watched/used), spinning the box up in-process when cold. Exact plane flags
2235
+ * mint only the requested short-lived credentials and enforce that plane's
2236
+ * permission. `desktop:true` alone carries the un-redacted/shared consent
2237
+ * gate. An entirely omitted plane set retains the legacy terminal-only
2238
+ * meaning; current clients should send all three flags. */
2097
2239
  async attachViewer(
2098
2240
  workspaceId: string,
2099
2241
  sessionId: string,
@@ -2130,254 +2272,810 @@ export class OpenGeniClient {
2130
2272
  );
2131
2273
  }
2132
2274
 
2133
- // --- Access + workspaces -----------------------------------------------------
2134
-
2135
- /**
2136
- * The deployment's public client bootstrap config: the host-exposed models
2137
- * (provider-grouped in `models`, flat in `allowedModels` for back-compat),
2138
- * reasoning efforts, MCP servers, file-upload limits, and how the client is
2139
- * expected to authenticate. Drives a composer's model picker without prior
2140
- * knowledge of the host setup; safe to call before any auth is established.
2141
- */
2142
- async getClientConfig(): Promise<ClientConfig> {
2143
- const config = await this.requestJson<ClientConfig>("GET", "/v1/config/client");
2144
- if (config.apiContractRevision !== OPENGENI_API_CONTRACT_REVISION) {
2145
- throw new OpenGeniApiContractMismatchError(
2146
- OPENGENI_API_CONTRACT_REVISION,
2147
- String(config.apiContractRevision || "(missing)"),
2148
- );
2149
- }
2150
- return config;
2151
- }
2275
+ // --- Browser / Computer interaction resources --------------------------------
2152
2276
 
2153
- /** Authenticated model definitions plus workspace-specific selectability. */
2154
- async getWorkspaceModelCatalog(workspaceId: string): Promise<WorkspaceModelCatalogResponse> {
2155
- return await this.requestJson<WorkspaceModelCatalogResponse>(
2277
+ async listAttachedBrowsers(
2278
+ workspaceId: string,
2279
+ options: AttachedBrowserDeviceListOptions = {},
2280
+ ): Promise<AttachedBrowserDeviceListResponse> {
2281
+ return await this.requestJson<AttachedBrowserDeviceListResponse>(
2156
2282
  "GET",
2157
- `/v1/workspaces/${workspaceId}/model-catalog`,
2283
+ `/v1/workspaces/${workspaceId}/attached-browsers`,
2284
+ undefined,
2285
+ options.includeDisconnected ? { includeDisconnected: "true" } : {},
2286
+ options,
2158
2287
  );
2159
2288
  }
2160
2289
 
2161
- /** Authenticated realtime voice models and credential readiness. */
2162
- async getWorkspaceRealtimeModelCatalog(
2290
+ async getAttachedBrowser(
2163
2291
  workspaceId: string,
2164
- ): Promise<WorkspaceRealtimeModelCatalogResponse> {
2165
- return await this.requestJson<WorkspaceRealtimeModelCatalogResponse>(
2292
+ deviceId: string,
2293
+ options: OpenGeniRequestOptions = {},
2294
+ ): Promise<AttachedBrowserDevice> {
2295
+ return await this.requestJson<AttachedBrowserDevice>(
2166
2296
  "GET",
2167
- `/v1/workspaces/${workspaceId}/realtime-model-catalog`,
2297
+ `/v1/workspaces/${workspaceId}/attached-browsers/${encodeURIComponent(deviceId)}`,
2298
+ undefined,
2299
+ {},
2300
+ options,
2168
2301
  );
2169
2302
  }
2170
2303
 
2171
- /** The caller's access context: subject, account + workspace grants, defaults. */
2172
- async getAccessContext(): Promise<AccessContext> {
2173
- return await this.requestJson<AccessContext>("GET", "/v1/access/me");
2174
- }
2175
-
2176
- async listWorkspaces(): Promise<Workspace[]> {
2177
- return await this.requestJson<Workspace[]>("GET", "/v1/workspaces");
2178
- }
2179
-
2180
- async createWorkspace(request: CreateWorkspaceRequest): Promise<Workspace> {
2181
- return await this.requestJson<Workspace>("POST", "/v1/workspaces", request);
2182
- }
2183
-
2184
- async getWorkspace(workspaceId: string): Promise<Workspace> {
2185
- return await this.requestJson<Workspace>("GET", `/v1/workspaces/${workspaceId}`);
2304
+ async listBrowserIdentities(
2305
+ workspaceId: string,
2306
+ options: BrowserIdentityListOptions = {},
2307
+ ): Promise<BrowserIdentityListResponse> {
2308
+ return await this.requestJson<BrowserIdentityListResponse>(
2309
+ "GET",
2310
+ `/v1/workspaces/${workspaceId}/browser-identities`,
2311
+ undefined,
2312
+ options.includeArchived ? { includeArchived: "true" } : {},
2313
+ options,
2314
+ );
2186
2315
  }
2187
2316
 
2188
- /** Read-time, secret-safe inventory of policy heads and visible workspace knowledge. */
2189
- async getWorkspaceState(
2317
+ async getBrowserIdentity(
2190
2318
  workspaceId: string,
2191
- options: WorkspaceStateGetOptions = {},
2192
- ): Promise<WorkspaceStateResponse> {
2193
- const params = new URLSearchParams();
2194
- if (options.attemptId) params.set("attemptId", options.attemptId);
2195
- const query = params.size > 0 ? `?${params.toString()}` : "";
2196
- return await this.requestJson<WorkspaceStateResponse>(
2319
+ identityId: string,
2320
+ options: OpenGeniRequestOptions = {},
2321
+ ): Promise<BrowserIdentity> {
2322
+ return await this.requestJson<BrowserIdentity>(
2197
2323
  "GET",
2198
- `/v1/workspaces/${workspaceId}/workspace-state${query}`,
2324
+ `/v1/workspaces/${workspaceId}/browser-identities/${encodeURIComponent(identityId)}`,
2325
+ undefined,
2326
+ {},
2327
+ options,
2199
2328
  );
2200
2329
  }
2201
2330
 
2202
- /** Download the canonical, explicitly sanitized Workspace State export. */
2203
- async exportWorkspaceState(
2331
+ async createBrowserIdentity(
2204
2332
  workspaceId: string,
2205
- options: WorkspaceStateGetOptions = {},
2206
- ): Promise<WorkspaceStateExportResponse> {
2207
- const params = new URLSearchParams();
2208
- if (options.attemptId) params.set("attemptId", options.attemptId);
2209
- const query = params.size > 0 ? `?${params.toString()}` : "";
2210
- return await this.requestJson<WorkspaceStateExportResponse>(
2211
- "GET",
2212
- `/v1/workspaces/${workspaceId}/workspace-state/export${query}`,
2333
+ request: CreateBrowserIdentityRequest,
2334
+ options: OpenGeniRequestOptions = {},
2335
+ ): Promise<BrowserIdentityMutationResponse> {
2336
+ return await this.requestJson<BrowserIdentityMutationResponse>(
2337
+ "POST",
2338
+ `/v1/workspaces/${workspaceId}/browser-identities`,
2339
+ request,
2340
+ {},
2341
+ options,
2213
2342
  );
2214
2343
  }
2215
2344
 
2216
- async updateWorkspace(workspaceId: string, request: UpdateWorkspaceRequest): Promise<Workspace> {
2217
- return await this.requestJson<Workspace>("PATCH", `/v1/workspaces/${workspaceId}`, request);
2345
+ async listBrowserRevisions(
2346
+ workspaceId: string,
2347
+ identityId: string,
2348
+ options: OpenGeniRequestOptions = {},
2349
+ ): Promise<BrowserRevisionListResponse> {
2350
+ return await this.requestJson<BrowserRevisionListResponse>(
2351
+ "GET",
2352
+ `/v1/workspaces/${workspaceId}/browser-identities/${encodeURIComponent(identityId)}/revisions`,
2353
+ undefined,
2354
+ {},
2355
+ options,
2356
+ );
2218
2357
  }
2219
2358
 
2220
- /** Inspect immutable instruction-policy history, active heads, and activation audit evidence. */
2221
- async listWorkspaceInstructionPolicies(
2359
+ /** Workspace-wide BrowserSession inventory. Associations express relevance,
2360
+ * not access: peers and child agents intentionally remain discoverable. */
2361
+ async listBrowserSessions(
2222
2362
  workspaceId: string,
2223
- options: WorkspaceInstructionPolicyListOptions = {},
2224
- ): Promise<WorkspaceInstructionPolicyListResponse> {
2225
- const params = new URLSearchParams();
2226
- if (options.kind !== undefined) params.set("kind", options.kind);
2227
- if (options.scope !== undefined) params.set("scope", options.scope);
2228
- if (options.roleKey !== undefined) params.set("roleKey", options.roleKey);
2229
- if (options.afterRevision !== undefined) {
2230
- params.set("afterRevision", String(options.afterRevision));
2231
- }
2232
- if (options.limit !== undefined) params.set("limit", String(options.limit));
2233
- const query = params.toString();
2234
- return await this.requestJson<WorkspaceInstructionPolicyListResponse>(
2363
+ options: OpenGeniRequestOptions = {},
2364
+ ): Promise<BrowserSessionListResponse> {
2365
+ return await this.requestJson<BrowserSessionListResponse>(
2235
2366
  "GET",
2236
- `/v1/workspaces/${workspaceId}/instruction-policies${query ? `?${query}` : ""}`,
2367
+ `/v1/workspaces/${workspaceId}/browser-sessions`,
2368
+ undefined,
2369
+ {},
2370
+ options,
2237
2371
  );
2238
2372
  }
2239
2373
 
2240
- async getWorkspaceInstructionPolicyRevision(
2374
+ async getBrowserSession(
2241
2375
  workspaceId: string,
2242
- revisionId: string,
2243
- ): Promise<WorkspaceInstructionPolicyRevision> {
2244
- return await this.requestJson<WorkspaceInstructionPolicyRevision>(
2376
+ browserSessionId: string,
2377
+ options: OpenGeniRequestOptions = {},
2378
+ ): Promise<BrowserSession> {
2379
+ return await this.requestJson<BrowserSession>(
2245
2380
  "GET",
2246
- `/v1/workspaces/${workspaceId}/instruction-policies/${encodeURIComponent(revisionId)}`,
2381
+ `/v1/workspaces/${workspaceId}/browser-sessions/${encodeURIComponent(browserSessionId)}`,
2382
+ undefined,
2383
+ {},
2384
+ options,
2247
2385
  );
2248
2386
  }
2249
2387
 
2250
- async createWorkspaceInstructionPolicyDraft(
2388
+ async createBrowserSession(
2251
2389
  workspaceId: string,
2252
- request: CreateWorkspaceInstructionPolicyDraftRequest,
2253
- ): Promise<WorkspaceInstructionPolicyRevision> {
2254
- return await this.requestJson<WorkspaceInstructionPolicyRevision>(
2390
+ request: CreateBrowserSessionRequest,
2391
+ options: OpenGeniRequestOptions = {},
2392
+ ): Promise<BrowserSessionMutationResponse> {
2393
+ return await this.requestJson<BrowserSessionMutationResponse>(
2255
2394
  "POST",
2256
- `/v1/workspaces/${workspaceId}/instruction-policies/drafts`,
2395
+ `/v1/workspaces/${workspaceId}/browser-sessions`,
2257
2396
  request,
2397
+ {},
2398
+ options,
2258
2399
  );
2259
2400
  }
2260
2401
 
2261
- /** List the newest immutable onboarding proposals and their inactive policy drafts. */
2262
- async listWorkspaceInstructionPolicyOnboardingProposals(
2402
+ async listBrowserTargets(
2263
2403
  workspaceId: string,
2264
- options: WorkspaceInstructionPolicyOnboardingProposalListOptions = {},
2265
- ): Promise<WorkspaceInstructionPolicyOnboardingProposalListResponse> {
2266
- const params = new URLSearchParams();
2267
- if (options.limit !== undefined) params.set("limit", String(options.limit));
2268
- const query = params.toString();
2269
- return await this.requestJson<WorkspaceInstructionPolicyOnboardingProposalListResponse>(
2404
+ browserSessionId: string,
2405
+ options: OpenGeniRequestOptions = {},
2406
+ ): Promise<BrowserTargetListResponse> {
2407
+ return await this.requestJson<BrowserTargetListResponse>(
2270
2408
  "GET",
2271
- `/v1/workspaces/${workspaceId}/instruction-policies/onboarding-proposals${query ? `?${query}` : ""}`,
2409
+ `/v1/workspaces/${workspaceId}/browser-sessions/${encodeURIComponent(browserSessionId)}/targets`,
2410
+ undefined,
2411
+ {},
2412
+ options,
2272
2413
  );
2273
2414
  }
2274
2415
 
2275
- /** Create one draft-only proposal against an exact active-policy baseline. */
2276
- async createWorkspaceInstructionPolicyOnboardingProposal(
2416
+ async openBrowserTarget(
2277
2417
  workspaceId: string,
2278
- request: CreateWorkspaceInstructionPolicyOnboardingProposalRequest,
2279
- ): Promise<WorkspaceInstructionPolicyOnboardingProposal> {
2280
- return await this.requestJson<WorkspaceInstructionPolicyOnboardingProposal>(
2418
+ browserSessionId: string,
2419
+ request: BrowserOpenTargetRequest = {},
2420
+ options: OpenGeniRequestOptions = {},
2421
+ ): Promise<BrowserTarget> {
2422
+ return await this.requestJson<BrowserTarget>(
2281
2423
  "POST",
2282
- `/v1/workspaces/${workspaceId}/instruction-policies/onboarding-proposals`,
2424
+ `/v1/workspaces/${workspaceId}/browser-sessions/${encodeURIComponent(browserSessionId)}/targets`,
2283
2425
  request,
2426
+ {},
2427
+ options,
2284
2428
  );
2285
2429
  }
2286
2430
 
2287
- /** Import the stored legacy workspace override as an inactive charter draft. */
2288
- async importLegacyWorkspaceInstructionPolicyDraft(
2431
+ async selectBrowserTarget(
2289
2432
  workspaceId: string,
2290
- request: ImportLegacyWorkspaceInstructionPolicyDraftRequest = {},
2291
- ): Promise<WorkspaceInstructionPolicyRevision> {
2292
- return await this.requestJson<WorkspaceInstructionPolicyRevision>(
2433
+ browserSessionId: string,
2434
+ targetId: string,
2435
+ options: OpenGeniRequestOptions = {},
2436
+ ): Promise<BrowserTarget> {
2437
+ return await this.requestJson<BrowserTarget>(
2293
2438
  "POST",
2294
- `/v1/workspaces/${workspaceId}/instruction-policies/import-legacy`,
2295
- request,
2439
+ `/v1/workspaces/${workspaceId}/browser-sessions/${encodeURIComponent(browserSessionId)}/targets/${encodeURIComponent(targetId)}/select`,
2440
+ {},
2441
+ {},
2442
+ options,
2296
2443
  );
2297
2444
  }
2298
2445
 
2299
- async diffWorkspaceInstructionPolicyRevisions(
2446
+ async closeBrowserTarget(
2300
2447
  workspaceId: string,
2301
- request: WorkspaceInstructionPolicyDiffRequest,
2302
- ): Promise<WorkspaceInstructionPolicyDiffResponse> {
2303
- const params = new URLSearchParams({
2304
- fromRevisionId: request.fromRevisionId,
2305
- toRevisionId: request.toRevisionId,
2306
- });
2307
- return await this.requestJson<WorkspaceInstructionPolicyDiffResponse>(
2308
- "GET",
2309
- `/v1/workspaces/${workspaceId}/instruction-policies/diff?${params}`,
2448
+ browserSessionId: string,
2449
+ targetId: string,
2450
+ options: OpenGeniRequestOptions = {},
2451
+ ): Promise<BrowserTargetListResponse> {
2452
+ return await this.requestJson<BrowserTargetListResponse>(
2453
+ "DELETE",
2454
+ `/v1/workspaces/${workspaceId}/browser-sessions/${encodeURIComponent(browserSessionId)}/targets/${encodeURIComponent(targetId)}`,
2455
+ undefined,
2456
+ {},
2457
+ options,
2310
2458
  );
2311
2459
  }
2312
2460
 
2313
- async activateWorkspaceInstructionPolicyRevision(
2461
+ async observeBrowserTarget(
2314
2462
  workspaceId: string,
2315
- revisionId: string,
2316
- request: ActivateWorkspaceInstructionPolicyRequest,
2317
- ): Promise<WorkspaceInstructionPolicyActivationResponse> {
2318
- return await this.requestJson<WorkspaceInstructionPolicyActivationResponse>(
2319
- "POST",
2320
- `/v1/workspaces/${workspaceId}/instruction-policies/${encodeURIComponent(revisionId)}/activate`,
2321
- request,
2463
+ browserSessionId: string,
2464
+ targetId: string,
2465
+ options: OpenGeniRequestOptions = {},
2466
+ ): Promise<BrowserObservation> {
2467
+ return await this.requestJson<BrowserObservation>(
2468
+ "GET",
2469
+ `/v1/workspaces/${workspaceId}/browser-sessions/${encodeURIComponent(browserSessionId)}/targets/${encodeURIComponent(targetId)}/observation`,
2470
+ undefined,
2471
+ {},
2472
+ options,
2322
2473
  );
2323
2474
  }
2324
2475
 
2325
- async rollbackWorkspaceInstructionPolicyRevision(
2476
+ async actInBrowser(
2326
2477
  workspaceId: string,
2327
- request: RollbackWorkspaceInstructionPolicyRequest,
2328
- ): Promise<WorkspaceInstructionPolicyActivationResponse> {
2329
- return await this.requestJson<WorkspaceInstructionPolicyActivationResponse>(
2478
+ browserSessionId: string,
2479
+ request: BrowserActionRequest,
2480
+ options: OpenGeniRequestOptions = {},
2481
+ ): Promise<BrowserActionReceipt> {
2482
+ return await this.requestJson<BrowserActionReceipt>(
2330
2483
  "POST",
2331
- `/v1/workspaces/${workspaceId}/instruction-policies/rollback`,
2484
+ `/v1/workspaces/${workspaceId}/browser-sessions/${encodeURIComponent(browserSessionId)}/actions`,
2332
2485
  request,
2486
+ {},
2487
+ options,
2333
2488
  );
2334
2489
  }
2335
2490
 
2336
- async listPreferenceRegistry(
2491
+ async getBrowserActionReceipt(
2337
2492
  workspaceId: string,
2338
- options: PreferenceRegistryListOptions = {},
2339
- ): Promise<PreferenceRegistryListResponse> {
2340
- const params = new URLSearchParams();
2341
- if (options.scope) params.set("scope", options.scope);
2342
- if (options.status) params.set("status", options.status);
2343
- if (options.limit !== undefined) params.set("limit", String(options.limit));
2344
- const query = params.toString();
2345
- return await this.requestJson<PreferenceRegistryListResponse>(
2493
+ browserSessionId: string,
2494
+ operationId: string,
2495
+ options: OpenGeniRequestOptions = {},
2496
+ ): Promise<BrowserActionReceipt> {
2497
+ return await this.requestJson<BrowserActionReceipt>(
2346
2498
  "GET",
2347
- `/v1/workspaces/${workspaceId}/preferences${query ? `?${query}` : ""}`,
2499
+ `/v1/workspaces/${workspaceId}/browser-sessions/${encodeURIComponent(browserSessionId)}/operations/${encodeURIComponent(operationId)}`,
2500
+ undefined,
2501
+ {},
2502
+ options,
2348
2503
  );
2349
2504
  }
2350
2505
 
2351
- async getPreferenceRegistry(
2506
+ async listBrowserDiagnostics(
2352
2507
  workspaceId: string,
2353
- preferenceId: string,
2354
- ): Promise<PreferenceRegistryDetailResponse> {
2355
- return await this.requestJson<PreferenceRegistryDetailResponse>(
2508
+ browserSessionId: string,
2509
+ targetId: string,
2510
+ options: BrowserDiagnosticsOptions = {},
2511
+ ): Promise<BrowserDiagnosticBatch> {
2512
+ return await this.requestJson<BrowserDiagnosticBatch>(
2356
2513
  "GET",
2357
- `/v1/workspaces/${workspaceId}/preferences/${encodeURIComponent(preferenceId)}`,
2514
+ `/v1/workspaces/${workspaceId}/browser-sessions/${encodeURIComponent(browserSessionId)}/targets/${encodeURIComponent(targetId)}/diagnostics`,
2515
+ undefined,
2516
+ {
2517
+ ...(options.kinds?.length ? { kinds: options.kinds.join(",") } : {}),
2518
+ ...(options.after !== undefined ? { after: String(options.after) } : {}),
2519
+ ...(options.limit !== undefined ? { limit: String(options.limit) } : {}),
2520
+ },
2521
+ options.signal ? { signal: options.signal } : {},
2358
2522
  );
2359
2523
  }
2360
2524
 
2361
- async createPreferenceRegistryProposal(
2525
+ async attachBrowserSession(
2362
2526
  workspaceId: string,
2363
- request: CreatePreferenceRegistryProposalRequest,
2364
- ): Promise<PreferenceRegistryRecord> {
2365
- return await this.requestJson<PreferenceRegistryRecord>(
2527
+ browserSessionId: string,
2528
+ request: BrowserSessionAttachmentRequest,
2529
+ options: OpenGeniRequestOptions = {},
2530
+ ): Promise<BrowserSessionAttachment> {
2531
+ return await this.requestJson<BrowserSessionAttachment>(
2366
2532
  "POST",
2367
- `/v1/workspaces/${workspaceId}/preferences/proposals`,
2533
+ `/v1/workspaces/${workspaceId}/browser-sessions/${encodeURIComponent(browserSessionId)}/attachments`,
2368
2534
  request,
2535
+ {},
2536
+ options,
2369
2537
  );
2370
2538
  }
2371
2539
 
2372
- async activatePreferenceRegistryRevision(
2540
+ async heartbeatBrowserSession(
2373
2541
  workspaceId: string,
2374
- preferenceId: string,
2375
- request: ActivatePreferenceRegistryRevisionRequest,
2376
- ): Promise<PreferenceRegistryMutationResponse> {
2377
- return await this.requestJson<PreferenceRegistryMutationResponse>(
2542
+ browserSessionId: string,
2543
+ options: OpenGeniRequestOptions = {},
2544
+ ): Promise<BrowserSessionHeartbeatResponse> {
2545
+ return await this.requestJson<BrowserSessionHeartbeatResponse>(
2378
2546
  "POST",
2379
- `/v1/workspaces/${workspaceId}/preferences/${encodeURIComponent(preferenceId)}/activate`,
2380
- request,
2547
+ `/v1/workspaces/${workspaceId}/browser-sessions/${encodeURIComponent(browserSessionId)}/heartbeat`,
2548
+ {},
2549
+ {},
2550
+ options,
2551
+ );
2552
+ }
2553
+
2554
+ async publishBrowserRevision(
2555
+ workspaceId: string,
2556
+ browserSessionId: string,
2557
+ request: PublishBrowserRevisionRequest,
2558
+ options: OpenGeniRequestOptions = {},
2559
+ ): Promise<PublishBrowserRevisionResponse> {
2560
+ return await this.requestJson<PublishBrowserRevisionResponse>(
2561
+ "POST",
2562
+ `/v1/workspaces/${workspaceId}/browser-sessions/${encodeURIComponent(browserSessionId)}/revisions`,
2563
+ request,
2564
+ {},
2565
+ options,
2566
+ );
2567
+ }
2568
+
2569
+ async suspendBrowserSession(
2570
+ workspaceId: string,
2571
+ browserSessionId: string,
2572
+ request: BrowserSessionLifecycleRequest,
2573
+ options: OpenGeniRequestOptions = {},
2574
+ ): Promise<BrowserSessionMutationResponse> {
2575
+ return await this.requestJson<BrowserSessionMutationResponse>(
2576
+ "POST",
2577
+ `/v1/workspaces/${workspaceId}/browser-sessions/${encodeURIComponent(browserSessionId)}/suspend`,
2578
+ request,
2579
+ {},
2580
+ options,
2581
+ );
2582
+ }
2583
+
2584
+ async resumeBrowserSession(
2585
+ workspaceId: string,
2586
+ browserSessionId: string,
2587
+ request: BrowserSessionLifecycleRequest,
2588
+ options: OpenGeniRequestOptions = {},
2589
+ ): Promise<BrowserSessionMutationResponse> {
2590
+ return await this.requestJson<BrowserSessionMutationResponse>(
2591
+ "POST",
2592
+ `/v1/workspaces/${workspaceId}/browser-sessions/${encodeURIComponent(browserSessionId)}/resume`,
2593
+ request,
2594
+ {},
2595
+ options,
2596
+ );
2597
+ }
2598
+
2599
+ async endBrowserSession(
2600
+ workspaceId: string,
2601
+ browserSessionId: string,
2602
+ request: BrowserSessionLifecycleRequest,
2603
+ options: OpenGeniRequestOptions = {},
2604
+ ): Promise<BrowserSessionMutationResponse> {
2605
+ return await this.requestJson<BrowserSessionMutationResponse>(
2606
+ "POST",
2607
+ `/v1/workspaces/${workspaceId}/browser-sessions/${encodeURIComponent(browserSessionId)}/end`,
2608
+ request,
2609
+ {},
2610
+ options,
2611
+ );
2612
+ }
2613
+
2614
+ /** Workspace-wide ComputerSession inventory. Associations express
2615
+ * relevance, not access; peer sessions intentionally remain discoverable. */
2616
+ async listComputerSessions(
2617
+ workspaceId: string,
2618
+ options: OpenGeniRequestOptions = {},
2619
+ ): Promise<ComputerSessionListResponse> {
2620
+ return await this.requestJson<ComputerSessionListResponse>(
2621
+ "GET",
2622
+ `/v1/workspaces/${workspaceId}/computer-sessions`,
2623
+ undefined,
2624
+ {},
2625
+ options,
2626
+ );
2627
+ }
2628
+
2629
+ async getComputerSession(
2630
+ workspaceId: string,
2631
+ computerSessionId: string,
2632
+ options: OpenGeniRequestOptions = {},
2633
+ ): Promise<ComputerSession> {
2634
+ return await this.requestJson<ComputerSession>(
2635
+ "GET",
2636
+ `/v1/workspaces/${workspaceId}/computer-sessions/${encodeURIComponent(computerSessionId)}`,
2637
+ undefined,
2638
+ {},
2639
+ options,
2640
+ );
2641
+ }
2642
+
2643
+ async createComputerSession(
2644
+ workspaceId: string,
2645
+ request: CreateComputerSessionRequest,
2646
+ options: OpenGeniRequestOptions = {},
2647
+ ): Promise<ComputerSessionMutationResponse> {
2648
+ return await this.requestJson<ComputerSessionMutationResponse>(
2649
+ "POST",
2650
+ `/v1/workspaces/${workspaceId}/computer-sessions`,
2651
+ request,
2652
+ {},
2653
+ options,
2654
+ );
2655
+ }
2656
+
2657
+ async listComputerTargets(
2658
+ workspaceId: string,
2659
+ computerSessionId: string,
2660
+ options: OpenGeniRequestOptions = {},
2661
+ ): Promise<ComputerTargetListResponse> {
2662
+ return await this.requestJson<ComputerTargetListResponse>(
2663
+ "GET",
2664
+ `/v1/workspaces/${workspaceId}/computer-sessions/${encodeURIComponent(computerSessionId)}/targets`,
2665
+ undefined,
2666
+ {},
2667
+ options,
2668
+ );
2669
+ }
2670
+
2671
+ async observeComputerTarget(
2672
+ workspaceId: string,
2673
+ computerSessionId: string,
2674
+ targetId: string,
2675
+ options: OpenGeniRequestOptions = {},
2676
+ ): Promise<ComputerObservation> {
2677
+ return await this.requestJson<ComputerObservation>(
2678
+ "GET",
2679
+ `/v1/workspaces/${workspaceId}/computer-sessions/${encodeURIComponent(computerSessionId)}/targets/${encodeURIComponent(targetId)}/observation`,
2680
+ undefined,
2681
+ {},
2682
+ options,
2683
+ );
2684
+ }
2685
+
2686
+ async actInComputer(
2687
+ workspaceId: string,
2688
+ computerSessionId: string,
2689
+ request: ComputerActionRequest,
2690
+ options: OpenGeniRequestOptions = {},
2691
+ ): Promise<ComputerActionReceipt> {
2692
+ return await this.requestJson<ComputerActionReceipt>(
2693
+ "POST",
2694
+ `/v1/workspaces/${workspaceId}/computer-sessions/${encodeURIComponent(computerSessionId)}/actions`,
2695
+ request,
2696
+ {},
2697
+ options,
2698
+ );
2699
+ }
2700
+
2701
+ async getComputerActionReceipt(
2702
+ workspaceId: string,
2703
+ computerSessionId: string,
2704
+ operationId: string,
2705
+ options: OpenGeniRequestOptions = {},
2706
+ ): Promise<ComputerActionReceipt> {
2707
+ return await this.requestJson<ComputerActionReceipt>(
2708
+ "GET",
2709
+ `/v1/workspaces/${workspaceId}/computer-sessions/${encodeURIComponent(computerSessionId)}/operations/${encodeURIComponent(operationId)}`,
2710
+ undefined,
2711
+ {},
2712
+ options,
2713
+ );
2714
+ }
2715
+
2716
+ async attachComputerSession(
2717
+ workspaceId: string,
2718
+ computerSessionId: string,
2719
+ request: ComputerSessionAttachmentRequest,
2720
+ options: OpenGeniRequestOptions = {},
2721
+ ): Promise<ComputerSessionAttachment> {
2722
+ return await this.requestJson<ComputerSessionAttachment>(
2723
+ "POST",
2724
+ `/v1/workspaces/${workspaceId}/computer-sessions/${encodeURIComponent(computerSessionId)}/attachments`,
2725
+ request,
2726
+ {},
2727
+ options,
2728
+ );
2729
+ }
2730
+
2731
+ async heartbeatComputerSession(
2732
+ workspaceId: string,
2733
+ computerSessionId: string,
2734
+ options: OpenGeniRequestOptions = {},
2735
+ ): Promise<ComputerSessionHeartbeatResponse> {
2736
+ return await this.requestJson<ComputerSessionHeartbeatResponse>(
2737
+ "POST",
2738
+ `/v1/workspaces/${workspaceId}/computer-sessions/${encodeURIComponent(computerSessionId)}/heartbeat`,
2739
+ {},
2740
+ {},
2741
+ options,
2742
+ );
2743
+ }
2744
+
2745
+ async endComputerSession(
2746
+ workspaceId: string,
2747
+ computerSessionId: string,
2748
+ request: ComputerSessionLifecycleRequest,
2749
+ options: OpenGeniRequestOptions = {},
2750
+ ): Promise<ComputerSessionMutationResponse> {
2751
+ return await this.requestJson<ComputerSessionMutationResponse>(
2752
+ "POST",
2753
+ `/v1/workspaces/${workspaceId}/computer-sessions/${encodeURIComponent(computerSessionId)}/end`,
2754
+ request,
2755
+ {},
2756
+ options,
2757
+ );
2758
+ }
2759
+
2760
+ // --- Access + workspaces -----------------------------------------------------
2761
+
2762
+ /**
2763
+ * The deployment's public client bootstrap config: the host-exposed models
2764
+ * (provider-grouped in `models`, flat in `allowedModels` for back-compat),
2765
+ * reasoning efforts, MCP servers, file-upload limits, and how the client is
2766
+ * expected to authenticate. Drives a composer's model picker without prior
2767
+ * knowledge of the host setup; safe to call before any auth is established.
2768
+ */
2769
+ async getClientConfig(): Promise<ClientConfig> {
2770
+ const config = await this.requestJson<ClientConfig>("GET", "/v1/config/client");
2771
+ if (config.apiContractRevision !== OPENGENI_API_CONTRACT_REVISION) {
2772
+ throw new OpenGeniApiContractMismatchError(
2773
+ OPENGENI_API_CONTRACT_REVISION,
2774
+ String(config.apiContractRevision || "(missing)"),
2775
+ );
2776
+ }
2777
+ return config;
2778
+ }
2779
+
2780
+ /** Authenticated model definitions plus workspace-specific selectability. */
2781
+ async getWorkspaceModelCatalog(workspaceId: string): Promise<WorkspaceModelCatalogResponse> {
2782
+ return await this.requestJson<WorkspaceModelCatalogResponse>(
2783
+ "GET",
2784
+ `/v1/workspaces/${workspaceId}/model-catalog`,
2785
+ );
2786
+ }
2787
+
2788
+ /** Authenticated realtime voice models and credential readiness. */
2789
+ async getWorkspaceRealtimeModelCatalog(
2790
+ workspaceId: string,
2791
+ ): Promise<WorkspaceRealtimeModelCatalogResponse> {
2792
+ return await this.requestJson<WorkspaceRealtimeModelCatalogResponse>(
2793
+ "GET",
2794
+ `/v1/workspaces/${workspaceId}/realtime-model-catalog`,
2795
+ );
2796
+ }
2797
+
2798
+ /** The caller's access context: subject, account + workspace grants, defaults. */
2799
+ async getAccessContext(): Promise<AccessContext> {
2800
+ return await this.requestJson<AccessContext>("GET", "/v1/access/me");
2801
+ }
2802
+
2803
+ async listWorkspaces(): Promise<Workspace[]> {
2804
+ return await this.requestJson<Workspace[]>("GET", "/v1/workspaces");
2805
+ }
2806
+
2807
+ async createWorkspace(request: CreateWorkspaceRequest): Promise<Workspace> {
2808
+ return await this.requestJson<Workspace>("POST", "/v1/workspaces", request);
2809
+ }
2810
+
2811
+ async getWorkspace(workspaceId: string): Promise<Workspace> {
2812
+ return await this.requestJson<Workspace>("GET", `/v1/workspaces/${workspaceId}`);
2813
+ }
2814
+
2815
+ /** Read-time, secret-safe inventory of policy heads and visible workspace knowledge. */
2816
+ async getWorkspaceState(
2817
+ workspaceId: string,
2818
+ options: WorkspaceStateGetOptions = {},
2819
+ ): Promise<WorkspaceStateResponse> {
2820
+ const params = new URLSearchParams();
2821
+ if (options.attemptId) params.set("attemptId", options.attemptId);
2822
+ const query = params.size > 0 ? `?${params.toString()}` : "";
2823
+ return await this.requestJson<WorkspaceStateResponse>(
2824
+ "GET",
2825
+ `/v1/workspaces/${workspaceId}/workspace-state${query}`,
2826
+ );
2827
+ }
2828
+
2829
+ /** Download the canonical, explicitly sanitized Workspace State export. */
2830
+ async exportWorkspaceState(
2831
+ workspaceId: string,
2832
+ options: WorkspaceStateGetOptions = {},
2833
+ ): Promise<WorkspaceStateExportResponse> {
2834
+ const params = new URLSearchParams();
2835
+ if (options.attemptId) params.set("attemptId", options.attemptId);
2836
+ const query = params.size > 0 ? `?${params.toString()}` : "";
2837
+ return await this.requestJson<WorkspaceStateExportResponse>(
2838
+ "GET",
2839
+ `/v1/workspaces/${workspaceId}/workspace-state/export${query}`,
2840
+ );
2841
+ }
2842
+
2843
+ async updateWorkspace(workspaceId: string, request: UpdateWorkspaceRequest): Promise<Workspace> {
2844
+ return await this.requestJson<Workspace>("PATCH", `/v1/workspaces/${workspaceId}`, request);
2845
+ }
2846
+
2847
+ /** Inspect immutable instruction-policy history, active heads, and activation audit evidence. */
2848
+ async listWorkspaceInstructionPolicies(
2849
+ workspaceId: string,
2850
+ options: WorkspaceInstructionPolicyListOptions = {},
2851
+ ): Promise<WorkspaceInstructionPolicyListResponse> {
2852
+ const params = new URLSearchParams();
2853
+ if (options.kind !== undefined) params.set("kind", options.kind);
2854
+ if (options.scope !== undefined) params.set("scope", options.scope);
2855
+ if (options.roleKey !== undefined) params.set("roleKey", options.roleKey);
2856
+ if (options.afterRevision !== undefined) {
2857
+ params.set("afterRevision", String(options.afterRevision));
2858
+ }
2859
+ if (options.limit !== undefined) params.set("limit", String(options.limit));
2860
+ const query = params.toString();
2861
+ return await this.requestJson<WorkspaceInstructionPolicyListResponse>(
2862
+ "GET",
2863
+ `/v1/workspaces/${workspaceId}/instruction-policies${query ? `?${query}` : ""}`,
2864
+ );
2865
+ }
2866
+
2867
+ async getWorkspaceInstructionPolicyRevision(
2868
+ workspaceId: string,
2869
+ revisionId: string,
2870
+ ): Promise<WorkspaceInstructionPolicyRevision> {
2871
+ return await this.requestJson<WorkspaceInstructionPolicyRevision>(
2872
+ "GET",
2873
+ `/v1/workspaces/${workspaceId}/instruction-policies/${encodeURIComponent(revisionId)}`,
2874
+ );
2875
+ }
2876
+
2877
+ async createWorkspaceInstructionPolicyDraft(
2878
+ workspaceId: string,
2879
+ request: CreateWorkspaceInstructionPolicyDraftRequest,
2880
+ ): Promise<WorkspaceInstructionPolicyRevision> {
2881
+ return await this.requestJson<WorkspaceInstructionPolicyRevision>(
2882
+ "POST",
2883
+ `/v1/workspaces/${workspaceId}/instruction-policies/drafts`,
2884
+ request,
2885
+ );
2886
+ }
2887
+
2888
+ /** List the newest immutable onboarding proposals and their inactive policy drafts. */
2889
+ async listWorkspaceInstructionPolicyOnboardingProposals(
2890
+ workspaceId: string,
2891
+ options: WorkspaceInstructionPolicyOnboardingProposalListOptions = {},
2892
+ ): Promise<WorkspaceInstructionPolicyOnboardingProposalListResponse> {
2893
+ const params = new URLSearchParams();
2894
+ if (options.limit !== undefined) params.set("limit", String(options.limit));
2895
+ const query = params.toString();
2896
+ return await this.requestJson<WorkspaceInstructionPolicyOnboardingProposalListResponse>(
2897
+ "GET",
2898
+ `/v1/workspaces/${workspaceId}/instruction-policies/onboarding-proposals${query ? `?${query}` : ""}`,
2899
+ );
2900
+ }
2901
+
2902
+ /** Create one draft-only proposal against an exact active-policy baseline. */
2903
+ async createWorkspaceInstructionPolicyOnboardingProposal(
2904
+ workspaceId: string,
2905
+ request: CreateWorkspaceInstructionPolicyOnboardingProposalRequest,
2906
+ ): Promise<WorkspaceInstructionPolicyOnboardingProposal> {
2907
+ return await this.requestJson<WorkspaceInstructionPolicyOnboardingProposal>(
2908
+ "POST",
2909
+ `/v1/workspaces/${workspaceId}/instruction-policies/onboarding-proposals`,
2910
+ request,
2911
+ );
2912
+ }
2913
+
2914
+ /** Import the stored legacy workspace override as an inactive charter draft. */
2915
+ async importLegacyWorkspaceInstructionPolicyDraft(
2916
+ workspaceId: string,
2917
+ request: ImportLegacyWorkspaceInstructionPolicyDraftRequest = {},
2918
+ ): Promise<WorkspaceInstructionPolicyRevision> {
2919
+ return await this.requestJson<WorkspaceInstructionPolicyRevision>(
2920
+ "POST",
2921
+ `/v1/workspaces/${workspaceId}/instruction-policies/import-legacy`,
2922
+ request,
2923
+ );
2924
+ }
2925
+
2926
+ async diffWorkspaceInstructionPolicyRevisions(
2927
+ workspaceId: string,
2928
+ request: WorkspaceInstructionPolicyDiffRequest,
2929
+ ): Promise<WorkspaceInstructionPolicyDiffResponse> {
2930
+ const params = new URLSearchParams({
2931
+ fromRevisionId: request.fromRevisionId,
2932
+ toRevisionId: request.toRevisionId,
2933
+ });
2934
+ return await this.requestJson<WorkspaceInstructionPolicyDiffResponse>(
2935
+ "GET",
2936
+ `/v1/workspaces/${workspaceId}/instruction-policies/diff?${params}`,
2937
+ );
2938
+ }
2939
+
2940
+ async activateWorkspaceInstructionPolicyRevision(
2941
+ workspaceId: string,
2942
+ revisionId: string,
2943
+ request: ActivateWorkspaceInstructionPolicyRequest,
2944
+ ): Promise<WorkspaceInstructionPolicyActivationResponse> {
2945
+ return await this.requestJson<WorkspaceInstructionPolicyActivationResponse>(
2946
+ "POST",
2947
+ `/v1/workspaces/${workspaceId}/instruction-policies/${encodeURIComponent(revisionId)}/activate`,
2948
+ request,
2949
+ );
2950
+ }
2951
+
2952
+ async rollbackWorkspaceInstructionPolicyRevision(
2953
+ workspaceId: string,
2954
+ request: RollbackWorkspaceInstructionPolicyRequest,
2955
+ ): Promise<WorkspaceInstructionPolicyActivationResponse> {
2956
+ return await this.requestJson<WorkspaceInstructionPolicyActivationResponse>(
2957
+ "POST",
2958
+ `/v1/workspaces/${workspaceId}/instruction-policies/rollback`,
2959
+ request,
2960
+ );
2961
+ }
2962
+
2963
+ /** Read the current organization profile plus immutable revision and activation history. */
2964
+ async listCompanyProfile(
2965
+ workspaceId: string,
2966
+ options: CompanyProfileListOptions = {},
2967
+ ): Promise<CompanyProfileListResponse> {
2968
+ const params = new URLSearchParams();
2969
+ if (options.afterRevision !== undefined)
2970
+ params.set("afterRevision", String(options.afterRevision));
2971
+ if (options.limit !== undefined) params.set("limit", String(options.limit));
2972
+ const query = params.toString();
2973
+ return await this.requestJson<CompanyProfileListResponse>(
2974
+ "GET",
2975
+ `/v1/workspaces/${workspaceId}/company-profile${query ? `?${query}` : ""}`,
2976
+ );
2977
+ }
2978
+
2979
+ async getCompanyProfileRevision(
2980
+ workspaceId: string,
2981
+ revisionId: string,
2982
+ ): Promise<CompanyProfileRevision> {
2983
+ return await this.requestJson<CompanyProfileRevision>(
2984
+ "GET",
2985
+ `/v1/workspaces/${workspaceId}/company-profile/revisions/${encodeURIComponent(revisionId)}`,
2986
+ );
2987
+ }
2988
+
2989
+ async updateCompanyProfile(
2990
+ workspaceId: string,
2991
+ request: UpdateCompanyProfileRequest,
2992
+ ): Promise<CompanyProfileMutationResponse> {
2993
+ return await this.requestJson<CompanyProfileMutationResponse>(
2994
+ "PUT",
2995
+ `/v1/workspaces/${workspaceId}/company-profile`,
2996
+ request,
2997
+ );
2998
+ }
2999
+
3000
+ async diffCompanyProfileRevisions(
3001
+ workspaceId: string,
3002
+ request: CompanyProfileDiffRequest,
3003
+ ): Promise<CompanyProfileDiffResponse> {
3004
+ const params = new URLSearchParams(request);
3005
+ return await this.requestJson<CompanyProfileDiffResponse>(
3006
+ "GET",
3007
+ `/v1/workspaces/${workspaceId}/company-profile/diff?${params}`,
3008
+ );
3009
+ }
3010
+
3011
+ async activateCompanyProfileRevision(
3012
+ workspaceId: string,
3013
+ revisionId: string,
3014
+ request: ActivateCompanyProfileRevisionRequest,
3015
+ ): Promise<CompanyProfileMutationResponse> {
3016
+ return await this.requestJson<CompanyProfileMutationResponse>(
3017
+ "POST",
3018
+ `/v1/workspaces/${workspaceId}/company-profile/revisions/${encodeURIComponent(revisionId)}/activate`,
3019
+ request,
3020
+ );
3021
+ }
3022
+
3023
+ async rollbackCompanyProfile(
3024
+ workspaceId: string,
3025
+ request: RollbackCompanyProfileRequest,
3026
+ ): Promise<CompanyProfileMutationResponse> {
3027
+ return await this.requestJson<CompanyProfileMutationResponse>(
3028
+ "POST",
3029
+ `/v1/workspaces/${workspaceId}/company-profile/rollback`,
3030
+ request,
3031
+ );
3032
+ }
3033
+
3034
+ async listPreferenceRegistry(
3035
+ workspaceId: string,
3036
+ options: PreferenceRegistryListOptions = {},
3037
+ ): Promise<PreferenceRegistryListResponse> {
3038
+ const params = new URLSearchParams();
3039
+ if (options.scope) params.set("scope", options.scope);
3040
+ if (options.status) params.set("status", options.status);
3041
+ if (options.limit !== undefined) params.set("limit", String(options.limit));
3042
+ const query = params.toString();
3043
+ return await this.requestJson<PreferenceRegistryListResponse>(
3044
+ "GET",
3045
+ `/v1/workspaces/${workspaceId}/preferences${query ? `?${query}` : ""}`,
3046
+ );
3047
+ }
3048
+
3049
+ async getPreferenceRegistry(
3050
+ workspaceId: string,
3051
+ preferenceId: string,
3052
+ ): Promise<PreferenceRegistryDetailResponse> {
3053
+ return await this.requestJson<PreferenceRegistryDetailResponse>(
3054
+ "GET",
3055
+ `/v1/workspaces/${workspaceId}/preferences/${encodeURIComponent(preferenceId)}`,
3056
+ );
3057
+ }
3058
+
3059
+ async createPreferenceRegistryProposal(
3060
+ workspaceId: string,
3061
+ request: CreatePreferenceRegistryProposalRequest,
3062
+ ): Promise<PreferenceRegistryRecord> {
3063
+ return await this.requestJson<PreferenceRegistryRecord>(
3064
+ "POST",
3065
+ `/v1/workspaces/${workspaceId}/preferences/proposals`,
3066
+ request,
3067
+ );
3068
+ }
3069
+
3070
+ async activatePreferenceRegistryRevision(
3071
+ workspaceId: string,
3072
+ preferenceId: string,
3073
+ request: ActivatePreferenceRegistryRevisionRequest,
3074
+ ): Promise<PreferenceRegistryMutationResponse> {
3075
+ return await this.requestJson<PreferenceRegistryMutationResponse>(
3076
+ "POST",
3077
+ `/v1/workspaces/${workspaceId}/preferences/${encodeURIComponent(preferenceId)}/activate`,
3078
+ request,
2381
3079
  );
2382
3080
  }
2383
3081
 
@@ -2516,6 +3214,86 @@ export class OpenGeniClient {
2516
3214
  );
2517
3215
  }
2518
3216
 
3217
+ /** Exchange one signed Slack bearer for durable, token-free continuation state. */
3218
+ async prepareSlackUserLinkAccess(
3219
+ workspaceId: string,
3220
+ request: PrepareSlackUserLinkAccessRequest,
3221
+ ): Promise<SlackUserLinkAccessRequest> {
3222
+ return await this.requestJson<SlackUserLinkAccessRequest>(
3223
+ "POST",
3224
+ `/v1/workspaces/${workspaceId}/integrations/slack/user-link-intents`,
3225
+ request,
3226
+ );
3227
+ }
3228
+
3229
+ async getSlackUserLinkAccess(
3230
+ workspaceId: string,
3231
+ requestId: string,
3232
+ ): Promise<SlackUserLinkAccessRequest> {
3233
+ return await this.requestJson<SlackUserLinkAccessRequest>(
3234
+ "GET",
3235
+ `/v1/workspaces/${workspaceId}/integrations/slack/user-link-intents/${requestId}`,
3236
+ );
3237
+ }
3238
+
3239
+ async requestSlackUserLinkWorkspaceAccess(
3240
+ workspaceId: string,
3241
+ requestId: string,
3242
+ request: SlackUserLinkAccessMutationRequest,
3243
+ ): Promise<SlackUserLinkAccessRequest> {
3244
+ return await this.requestJson<SlackUserLinkAccessRequest>(
3245
+ "POST",
3246
+ `/v1/workspaces/${workspaceId}/integrations/slack/user-link-intents/${requestId}/request-access`,
3247
+ request,
3248
+ );
3249
+ }
3250
+
3251
+ async cancelSlackUserLinkAccess(
3252
+ workspaceId: string,
3253
+ requestId: string,
3254
+ request: SlackUserLinkAccessMutationRequest,
3255
+ ): Promise<SlackUserLinkAccessRequest> {
3256
+ return await this.requestJson<SlackUserLinkAccessRequest>(
3257
+ "POST",
3258
+ `/v1/workspaces/${workspaceId}/integrations/slack/user-link-intents/${requestId}/cancel`,
3259
+ request,
3260
+ );
3261
+ }
3262
+
3263
+ async listSlackUserLinkAccessRequests(
3264
+ workspaceId: string,
3265
+ ): Promise<SlackUserLinkAccessRequest[]> {
3266
+ const response = await this.requestJson<ListSlackUserLinkAccessRequestsResponse>(
3267
+ "GET",
3268
+ `/v1/workspaces/${workspaceId}/members/access-requests/slack`,
3269
+ );
3270
+ return response.requests;
3271
+ }
3272
+
3273
+ async approveSlackUserLinkAccessRequest(
3274
+ workspaceId: string,
3275
+ requestId: string,
3276
+ request: ApproveSlackUserLinkAccessRequest,
3277
+ ): Promise<SlackUserLinkAccessRequest> {
3278
+ return await this.requestJson<SlackUserLinkAccessRequest>(
3279
+ "POST",
3280
+ `/v1/workspaces/${workspaceId}/members/access-requests/slack/${requestId}/approve`,
3281
+ request,
3282
+ );
3283
+ }
3284
+
3285
+ async denySlackUserLinkAccessRequest(
3286
+ workspaceId: string,
3287
+ requestId: string,
3288
+ request: SlackUserLinkAccessMutationRequest,
3289
+ ): Promise<SlackUserLinkAccessRequest> {
3290
+ return await this.requestJson<SlackUserLinkAccessRequest>(
3291
+ "POST",
3292
+ `/v1/workspaces/${workspaceId}/members/access-requests/slack/${requestId}/deny`,
3293
+ request,
3294
+ );
3295
+ }
3296
+
2519
3297
  // --- Scheduled tasks (write + runs) -------------------------------------------
2520
3298
 
2521
3299
  async createScheduledTask(
@@ -3026,6 +3804,29 @@ export class OpenGeniClient {
3026
3804
  );
3027
3805
  }
3028
3806
 
3807
+ /** Assemble one permanent generated-image receipt from bounded authenticated ranges. */
3808
+ async downloadRetainedArtifact(
3809
+ workspaceId: string,
3810
+ artifact: RetainedArtifactReference,
3811
+ options: RetainedArtifactDownloadOptions = {},
3812
+ ): Promise<RetainedArtifactDownload> {
3813
+ assertRetainedGeneratedImageReceipt(workspaceId, artifact);
3814
+ const bytes = await this.downloadRetainedArtifactBytes(artifact, options);
3815
+ return { artifact, bytes };
3816
+ }
3817
+
3818
+ /** Mint a short-lived, zero-copy browser source for a validated generated image. */
3819
+ async createRetainedArtifactDownloadUrl(
3820
+ workspaceId: string,
3821
+ artifact: RetainedArtifactReference,
3822
+ options: OpenGeniRequestOptions = {},
3823
+ ): Promise<FileDownloadUrlResponse> {
3824
+ assertRetainedGeneratedImageReceipt(workspaceId, artifact);
3825
+ const download = await this.createFileDownloadUrl(workspaceId, artifact.artifactId, options);
3826
+ assertSafeArtifactDownloadUrl(download);
3827
+ return download;
3828
+ }
3829
+
3029
3830
  /** Assemble one retained screenshot from bounded authenticated API ranges. */
3030
3831
  async downloadRetainedScreenshot(
3031
3832
  workspaceId: string,
@@ -3033,10 +3834,6 @@ export class OpenGeniClient {
3033
3834
  artifactId: string,
3034
3835
  options: RetainedScreenshotDownloadOptions = {},
3035
3836
  ): Promise<RetainedScreenshotDownload> {
3036
- const maxRetries = options.maxRetries ?? 2;
3037
- if (!Number.isInteger(maxRetries) || maxRetries < 0 || maxRetries > 3) {
3038
- throw new RangeError("retained screenshot maxRetries must be an integer from 0 to 3");
3039
- }
3040
3837
  const metadata = await this.getSessionRetainedArtifact(workspaceId, sessionId, artifactId);
3041
3838
  if (!metadata.available) return { metadata, bytes: null };
3042
3839
  if (
@@ -3048,10 +3845,22 @@ export class OpenGeniClient {
3048
3845
  ) {
3049
3846
  throw new OpenGeniApiError(502, "retained screenshot metadata is invalid");
3050
3847
  }
3051
- const bytes = new Uint8Array(metadata.originalBytes);
3052
- const pageBytes = Math.min(metadata.retrieval.maxRangeBytes, RETAINED_OUTPUT_MAX_PAGE_BYTES);
3848
+ const bytes = await this.downloadRetainedArtifactBytes(metadata, options);
3849
+ return { metadata, bytes };
3850
+ }
3851
+
3852
+ private async downloadRetainedArtifactBytes(
3853
+ artifact: RetainedArtifactReference,
3854
+ options: RetainedArtifactDownloadOptions,
3855
+ ): Promise<Uint8Array> {
3856
+ const maxRetries = options.maxRetries ?? 2;
3857
+ if (!Number.isInteger(maxRetries) || maxRetries < 0 || maxRetries > 3) {
3858
+ throw new RangeError("retained artifact maxRetries must be an integer from 0 to 3");
3859
+ }
3860
+ const bytes = new Uint8Array(artifact.originalBytes);
3861
+ const pageBytes = Math.min(artifact.retrieval.maxRangeBytes, RETAINED_OUTPUT_MAX_PAGE_BYTES);
3053
3862
  if (!Number.isSafeInteger(pageBytes) || pageBytes <= 0) {
3054
- throw new OpenGeniApiError(502, "retained screenshot range metadata is invalid");
3863
+ throw new OpenGeniApiError(502, "retained artifact range metadata is invalid");
3055
3864
  }
3056
3865
  for (let start = 0; start < bytes.byteLength; start += pageBytes) {
3057
3866
  options.signal?.throwIfAborted();
@@ -3059,7 +3868,7 @@ export class OpenGeniClient {
3059
3868
  let page: RetainedArtifactContent | null = null;
3060
3869
  for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
3061
3870
  try {
3062
- page = await this.getSessionRetainedArtifactContent(workspaceId, sessionId, artifactId, {
3871
+ page = await this.getRetainedArtifactContentAtPath(artifact.retrieval.path, {
3063
3872
  range: `bytes=${start}-${end}`,
3064
3873
  ...(options.signal ? { signal: options.signal } : {}),
3065
3874
  });
@@ -3074,22 +3883,22 @@ export class OpenGeniClient {
3074
3883
  }
3075
3884
  }
3076
3885
  }
3077
- if (!page) throw new OpenGeniApiError(502, "retained screenshot range retry exhausted");
3886
+ if (!page) throw new OpenGeniApiError(502, "retained artifact range retry exhausted");
3078
3887
  const expectedLength = end - start + 1;
3079
3888
  if (
3080
3889
  page.status !== 206 ||
3081
- page.contentType !== metadata.contentType ||
3890
+ page.contentType !== artifact.contentType ||
3082
3891
  page.contentLength !== expectedLength ||
3083
- page.contentRange !== `bytes ${start}-${end}/${metadata.originalBytes}`
3892
+ page.contentRange !== `bytes ${start}-${end}/${artifact.originalBytes}`
3084
3893
  ) {
3085
- throw new OpenGeniApiError(502, "retained screenshot range response is invalid");
3894
+ throw new OpenGeniApiError(502, "retained artifact range response is invalid");
3086
3895
  }
3087
3896
  bytes.set(page.bytes, start);
3088
3897
  }
3089
- if ((await sha256Hex(bytes)) !== metadata.sha256) {
3090
- throw new OpenGeniApiError(502, "retained screenshot checksum mismatch");
3898
+ if ((await sha256Hex(bytes)) !== artifact.sha256) {
3899
+ throw new OpenGeniApiError(502, "retained artifact checksum mismatch");
3091
3900
  }
3092
- return { metadata, bytes };
3901
+ return bytes;
3093
3902
  }
3094
3903
 
3095
3904
  private async getRetainedArtifactContentAtPath(
@@ -3141,25 +3950,92 @@ export class OpenGeniClient {
3141
3950
  RETAINED_OUTPUT_MAX_PAGE_BYTES,
3142
3951
  declaredLength,
3143
3952
  );
3144
- return {
3145
- bytes,
3146
- status: response.status,
3147
- contentType: response.headers.get("content-type") ?? "application/octet-stream",
3148
- contentLength: bytes.byteLength,
3149
- contentRange: response.headers.get("content-range"),
3150
- acceptRanges: "bytes",
3151
- };
3953
+ return {
3954
+ bytes,
3955
+ status: response.status,
3956
+ contentType: response.headers.get("content-type") ?? "application/octet-stream",
3957
+ contentLength: bytes.byteLength,
3958
+ contentRange: response.headers.get("content-range"),
3959
+ acceptRanges: "bytes",
3960
+ };
3961
+ }
3962
+
3963
+ /** Mint a short-lived signed download URL for a ready file. */
3964
+ async createFileDownloadUrl(
3965
+ workspaceId: string,
3966
+ fileId: string,
3967
+ options: OpenGeniRequestOptions = {},
3968
+ ): Promise<FileDownloadUrlResponse> {
3969
+ return await this.requestJson<FileDownloadUrlResponse>(
3970
+ "POST",
3971
+ `/v1/workspaces/${workspaceId}/files/${fileId}/download-url`,
3972
+ undefined,
3973
+ {},
3974
+ options,
3975
+ );
3976
+ }
3977
+
3978
+ // --- Video generation ---------------------------------------------------------------
3979
+
3980
+ /** Read the effective workspace policy and executable Seedance capabilities. */
3981
+ async getVideoGenerationSettings(
3982
+ workspaceId: string,
3983
+ options: OpenGeniRequestOptions = {},
3984
+ ): Promise<WorkspaceVideoGenerationSettings> {
3985
+ return await this.requestJson<WorkspaceVideoGenerationSettings>(
3986
+ "GET",
3987
+ `/v1/workspaces/${workspaceId}/video-generation`,
3988
+ undefined,
3989
+ {},
3990
+ options,
3991
+ );
3152
3992
  }
3153
3993
 
3154
- /** Mint a short-lived signed download URL for a ready file. */
3155
- async createFileDownloadUrl(
3994
+ /** Replace the enabled video models/default under optimistic policy revision control. */
3995
+ async updateVideoGenerationPolicy(
3156
3996
  workspaceId: string,
3157
- fileId: string,
3158
- ): Promise<FileDownloadUrlResponse> {
3159
- return await this.requestJson<FileDownloadUrlResponse>(
3997
+ request: UpdateVideoGenerationPolicyRequest,
3998
+ options: OpenGeniRequestOptions = {},
3999
+ ): Promise<VideoGenerationPolicy> {
4000
+ return await this.requestJson<VideoGenerationPolicy>(
4001
+ "PUT",
4002
+ `/v1/workspaces/${workspaceId}/video-generation/policy`,
4003
+ request,
4004
+ {},
4005
+ options,
4006
+ );
4007
+ }
4008
+
4009
+ /** Read durable progress for one accepted video generation operation. */
4010
+ async getVideoGenerationOperation(
4011
+ workspaceId: string,
4012
+ operationId: string,
4013
+ options: OpenGeniRequestOptions = {},
4014
+ ): Promise<VideoGenerationOperationSummary> {
4015
+ return await this.requestJson<VideoGenerationOperationSummary>(
4016
+ "GET",
4017
+ `/v1/workspaces/${workspaceId}/video-generation/operations/${operationId}`,
4018
+ undefined,
4019
+ {},
4020
+ options,
4021
+ );
4022
+ }
4023
+
4024
+ /** Mint a short-lived zero-copy URL for native Range-based video playback. */
4025
+ async createVideoArtifactPlaybackSource(
4026
+ workspaceId: string,
4027
+ artifactId: string,
4028
+ options: OpenGeniRequestOptions = {},
4029
+ ): Promise<VideoArtifactPlaybackSource> {
4030
+ const source = await this.requestJson<VideoArtifactPlaybackSource>(
3160
4031
  "POST",
3161
- `/v1/workspaces/${workspaceId}/files/${fileId}/download-url`,
4032
+ `/v1/workspaces/${workspaceId}/artifacts/${artifactId}/playback-source`,
4033
+ undefined,
4034
+ {},
4035
+ options,
3162
4036
  );
4037
+ assertSafeVideoArtifactPlaybackSource(source, artifactId);
4038
+ return source;
3163
4039
  }
3164
4040
 
3165
4041
  // --- Documents ----------------------------------------------------------------------
@@ -3408,6 +4284,53 @@ export class OpenGeniClient {
3408
4284
  );
3409
4285
  }
3410
4286
 
4287
+ /** Resolve pinned components, Variable Set, and Rig requirements before installation. */
4288
+ async previewPackInstallation(
4289
+ workspaceId: string,
4290
+ packId: string,
4291
+ request: PreviewPackInstallationRequest = {},
4292
+ ): Promise<PackInstallationPreview> {
4293
+ return await this.requestJson<PackInstallationPreview>(
4294
+ "POST",
4295
+ `/v1/workspaces/${workspaceId}/packs/${encodeURIComponent(packId)}/installation-preview`,
4296
+ request,
4297
+ );
4298
+ }
4299
+
4300
+ /** Install, update, or repair a Pack from an exact previewed manifest. */
4301
+ async installPack(
4302
+ workspaceId: string,
4303
+ packId: string,
4304
+ request: InstallPackRequest,
4305
+ ): Promise<PackInstallation> {
4306
+ return await this.requestJson<PackInstallation>(
4307
+ "POST",
4308
+ `/v1/workspaces/${workspaceId}/packs/${encodeURIComponent(packId)}/install`,
4309
+ request,
4310
+ );
4311
+ }
4312
+
4313
+ /** Preview which Pack-owned components will be retained by other owners. */
4314
+ async previewPackUninstall(workspaceId: string, packId: string): Promise<PackUninstallPreview> {
4315
+ return await this.requestJson<PackUninstallPreview>(
4316
+ "GET",
4317
+ `/v1/workspaces/${workspaceId}/packs/${encodeURIComponent(packId)}/uninstall-preview`,
4318
+ );
4319
+ }
4320
+
4321
+ /** Safely release Pack ownership and disable only now-ownerless components. */
4322
+ async uninstallPack(
4323
+ workspaceId: string,
4324
+ packId: string,
4325
+ request: UninstallPackRequest,
4326
+ ): Promise<UninstallPackResult> {
4327
+ return await this.requestJson<UninstallPackResult>(
4328
+ "DELETE",
4329
+ `/v1/workspaces/${workspaceId}/packs/${encodeURIComponent(packId)}/installation`,
4330
+ request,
4331
+ );
4332
+ }
4333
+
3411
4334
  /** Unregister a workspace-scoped pack (built-in packs cannot be deleted). */
3412
4335
  async deletePack(workspaceId: string, packId: string): Promise<void> {
3413
4336
  await this.requestVoid(
@@ -3482,6 +4405,298 @@ export class OpenGeniClient {
3482
4405
  );
3483
4406
  }
3484
4407
 
4408
+ /** List installed protocol-neutral OpenAPI and GraphQL Integrations. */
4409
+ async listApiIntegrations(workspaceId: string): Promise<ListApiIntegrationsResponse> {
4410
+ return await this.requestJson<ListApiIntegrationsResponse>(
4411
+ "GET",
4412
+ `/v1/workspaces/${workspaceId}/integrations`,
4413
+ );
4414
+ }
4415
+
4416
+ /** List curated provider presets without exposing deployment OAuth credentials. */
4417
+ async listApiIntegrationPresets(workspaceId: string): Promise<ListApiIntegrationPresetsResponse> {
4418
+ return await this.requestJson<ListApiIntegrationPresetsResponse>(
4419
+ "GET",
4420
+ `/v1/workspaces/${workspaceId}/integrations/presets`,
4421
+ );
4422
+ }
4423
+
4424
+ /** Detect and compile an Integration without mutating workspace state. */
4425
+ async previewApiIntegration(
4426
+ workspaceId: string,
4427
+ request: PreviewApiIntegrationRequest,
4428
+ ): Promise<ApiIntegrationPreview> {
4429
+ return await this.requestJson<ApiIntegrationPreview>(
4430
+ "POST",
4431
+ `/v1/workspaces/${workspaceId}/integrations/preview`,
4432
+ request,
4433
+ );
4434
+ }
4435
+
4436
+ /** Start a signed PKCE OAuth flow for a built-in Google or Microsoft preset. */
4437
+ async startApiIntegrationOAuth(
4438
+ workspaceId: string,
4439
+ request: ApiIntegrationOAuthStartRequest,
4440
+ ): Promise<OAuthStartResponse> {
4441
+ return await this.requestJson<OAuthStartResponse>(
4442
+ "POST",
4443
+ `/v1/workspaces/${workspaceId}/integrations/oauth/start`,
4444
+ request,
4445
+ );
4446
+ }
4447
+
4448
+ /** Install only the exact immutable revision and digest accepted in preview. */
4449
+ async installApiIntegration(
4450
+ workspaceId: string,
4451
+ request: InstallApiIntegrationRequest,
4452
+ ): Promise<InstalledApiIntegration> {
4453
+ return await this.requestJson<InstalledApiIntegration>(
4454
+ "POST",
4455
+ `/v1/workspaces/${workspaceId}/integrations/install`,
4456
+ request,
4457
+ );
4458
+ }
4459
+
4460
+ async previewApiIntegrationUninstall(
4461
+ workspaceId: string,
4462
+ capabilityId: string,
4463
+ instanceKey: string,
4464
+ ): Promise<ApiIntegrationUninstallPreview> {
4465
+ return await this.requestJson<ApiIntegrationUninstallPreview>(
4466
+ "GET",
4467
+ `/v1/workspaces/${workspaceId}/integrations/${encodeURIComponent(capabilityId)}/instances/${encodeURIComponent(instanceKey)}/uninstall-preview`,
4468
+ );
4469
+ }
4470
+
4471
+ /** Remove one Integration instance without deleting its Connection or sibling instances. */
4472
+ async uninstallApiIntegration(
4473
+ workspaceId: string,
4474
+ capabilityId: string,
4475
+ instanceKey: string,
4476
+ request: UninstallApiIntegrationRequest,
4477
+ ): Promise<UninstallApiIntegrationResult> {
4478
+ return await this.requestJson<UninstallApiIntegrationResult>(
4479
+ "DELETE",
4480
+ `/v1/workspaces/${workspaceId}/integrations/${encodeURIComponent(capabilityId)}/instances/${encodeURIComponent(instanceKey)}`,
4481
+ request,
4482
+ );
4483
+ }
4484
+
4485
+ /** List immutable provider facets and their exact per-account bindings. */
4486
+ async listIntegrationFeatures(
4487
+ workspaceId: string,
4488
+ capabilityId: string,
4489
+ instanceKey: string,
4490
+ ): Promise<IntegrationInstanceFeaturesResponse> {
4491
+ return await this.requestJson<IntegrationInstanceFeaturesResponse>(
4492
+ "GET",
4493
+ `/v1/workspaces/${workspaceId}/integrations/${encodeURIComponent(capabilityId)}/instances/${encodeURIComponent(instanceKey)}/features`,
4494
+ );
4495
+ }
4496
+
4497
+ /** Configure or OCC-update one adapter-owned feature on an Integration instance. */
4498
+ async configureIntegrationFeature(
4499
+ workspaceId: string,
4500
+ capabilityId: string,
4501
+ instanceKey: string,
4502
+ featureKey: string,
4503
+ request: UpsertIntegrationFeatureRequest,
4504
+ ): Promise<IntegrationFeatureMutationResult> {
4505
+ return await this.requestJson<IntegrationFeatureMutationResult>(
4506
+ "PUT",
4507
+ `/v1/workspaces/${workspaceId}/integrations/${encodeURIComponent(capabilityId)}/instances/${encodeURIComponent(instanceKey)}/features/${encodeURIComponent(featureKey)}`,
4508
+ request,
4509
+ );
4510
+ }
4511
+
4512
+ /** Browse source metadata through one exact Google Drive Integration instance. */
4513
+ async browseGoogleDriveIntegrationSource(
4514
+ workspaceId: string,
4515
+ capabilityId: string,
4516
+ instanceKey: string,
4517
+ featureKey: string,
4518
+ options: { parentId?: string | undefined; pageToken?: string | undefined } = {},
4519
+ ): Promise<GoogleDriveBrowseResponse> {
4520
+ const query = new URLSearchParams();
4521
+ if (options.parentId) query.set("parentId", options.parentId);
4522
+ if (options.pageToken) query.set("pageToken", options.pageToken);
4523
+ const suffix = query.size > 0 ? `?${query}` : "";
4524
+ return await this.requestJson<GoogleDriveBrowseResponse>(
4525
+ "GET",
4526
+ `/v1/workspaces/${workspaceId}/integrations/${encodeURIComponent(capabilityId)}/instances/${encodeURIComponent(instanceKey)}/features/${encodeURIComponent(featureKey)}/browse${suffix}`,
4527
+ );
4528
+ }
4529
+
4530
+ /** Verify and bind document sources to one exact Google Drive Integration instance. */
4531
+ async saveGoogleDriveIntegrationSource(
4532
+ workspaceId: string,
4533
+ capabilityId: string,
4534
+ instanceKey: string,
4535
+ featureKey: string,
4536
+ request: SaveGoogleDriveIntegrationSourceRequest,
4537
+ ): Promise<IntegrationFeatureMutationResult> {
4538
+ return await this.requestJson<IntegrationFeatureMutationResult>(
4539
+ "PUT",
4540
+ `/v1/workspaces/${workspaceId}/integrations/${encodeURIComponent(capabilityId)}/instances/${encodeURIComponent(instanceKey)}/features/${encodeURIComponent(featureKey)}/source`,
4541
+ request,
4542
+ );
4543
+ }
4544
+
4545
+ async pauseIntegrationFeature(
4546
+ workspaceId: string,
4547
+ capabilityId: string,
4548
+ instanceKey: string,
4549
+ featureKey: string,
4550
+ request: MutateIntegrationFeatureRequest,
4551
+ ): Promise<IntegrationFeatureMutationResult> {
4552
+ return await this.mutateIntegrationFeatureLifecycle(
4553
+ workspaceId,
4554
+ capabilityId,
4555
+ instanceKey,
4556
+ featureKey,
4557
+ "pause",
4558
+ request,
4559
+ );
4560
+ }
4561
+
4562
+ async resumeIntegrationFeature(
4563
+ workspaceId: string,
4564
+ capabilityId: string,
4565
+ instanceKey: string,
4566
+ featureKey: string,
4567
+ request: MutateIntegrationFeatureRequest,
4568
+ ): Promise<IntegrationFeatureMutationResult> {
4569
+ return await this.mutateIntegrationFeatureLifecycle(
4570
+ workspaceId,
4571
+ capabilityId,
4572
+ instanceKey,
4573
+ featureKey,
4574
+ "resume",
4575
+ request,
4576
+ );
4577
+ }
4578
+
4579
+ async removeIntegrationFeature(
4580
+ workspaceId: string,
4581
+ capabilityId: string,
4582
+ instanceKey: string,
4583
+ featureKey: string,
4584
+ request: MutateIntegrationFeatureRequest,
4585
+ ): Promise<IntegrationFeatureRemovalResult> {
4586
+ return await this.requestJson<IntegrationFeatureRemovalResult>(
4587
+ "DELETE",
4588
+ `/v1/workspaces/${workspaceId}/integrations/${encodeURIComponent(capabilityId)}/instances/${encodeURIComponent(instanceKey)}/features/${encodeURIComponent(featureKey)}`,
4589
+ request,
4590
+ );
4591
+ }
4592
+
4593
+ private async mutateIntegrationFeatureLifecycle(
4594
+ workspaceId: string,
4595
+ capabilityId: string,
4596
+ instanceKey: string,
4597
+ featureKey: string,
4598
+ action: "pause" | "resume",
4599
+ request: MutateIntegrationFeatureRequest,
4600
+ ): Promise<IntegrationFeatureMutationResult> {
4601
+ return await this.requestJson<IntegrationFeatureMutationResult>(
4602
+ "POST",
4603
+ `/v1/workspaces/${workspaceId}/integrations/${encodeURIComponent(capabilityId)}/instances/${encodeURIComponent(instanceKey)}/features/${encodeURIComponent(featureKey)}/${action}`,
4604
+ request,
4605
+ );
4606
+ }
4607
+
4608
+ async previewPlugin(workspaceId: string, request: PreviewPluginRequest): Promise<PluginPreview> {
4609
+ return await this.requestJson<PluginPreview>(
4610
+ "POST",
4611
+ `/v1/workspaces/${workspaceId}/plugins/preview`,
4612
+ request,
4613
+ );
4614
+ }
4615
+
4616
+ async listInstalledPlugins(workspaceId: string): Promise<ListInstalledPluginsResponse> {
4617
+ return await this.requestJson<ListInstalledPluginsResponse>(
4618
+ "GET",
4619
+ `/v1/workspaces/${workspaceId}/plugins`,
4620
+ );
4621
+ }
4622
+
4623
+ async installPlugin(
4624
+ workspaceId: string,
4625
+ request: InstallPluginRequest,
4626
+ ): Promise<InstalledPlugin> {
4627
+ return await this.requestJson<InstalledPlugin>(
4628
+ "POST",
4629
+ `/v1/workspaces/${workspaceId}/plugins/install`,
4630
+ request,
4631
+ );
4632
+ }
4633
+
4634
+ async previewPluginUninstall(
4635
+ workspaceId: string,
4636
+ pluginKey: string,
4637
+ ): Promise<PluginUninstallPreview> {
4638
+ return await this.requestJson<PluginUninstallPreview>(
4639
+ "GET",
4640
+ `/v1/workspaces/${workspaceId}/plugins/${encodeURIComponent(pluginKey)}/uninstall-preview`,
4641
+ );
4642
+ }
4643
+
4644
+ async uninstallPlugin(
4645
+ workspaceId: string,
4646
+ pluginKey: string,
4647
+ request: UninstallPluginRequest,
4648
+ ): Promise<UninstallPluginResult> {
4649
+ return await this.requestJson<UninstallPluginResult>(
4650
+ "DELETE",
4651
+ `/v1/workspaces/${workspaceId}/plugins/${encodeURIComponent(pluginKey)}`,
4652
+ request,
4653
+ );
4654
+ }
4655
+
4656
+ /** Resolve one public skills.sh or GitHub Skill folder to an immutable review preview. */
4657
+ async previewSkillImport(
4658
+ workspaceId: string,
4659
+ request: PreviewSkillImportRequest,
4660
+ ): Promise<SkillImportPreview> {
4661
+ return await this.requestJson<SkillImportPreview>(
4662
+ "POST",
4663
+ `/v1/workspaces/${workspaceId}/skills/preview`,
4664
+ request,
4665
+ );
4666
+ }
4667
+
4668
+ /** Install only the exact commit and full-content digest accepted during preview. */
4669
+ async installSkill(workspaceId: string, request: InstallSkillRequest): Promise<InstalledSkill> {
4670
+ return await this.requestJson<InstalledSkill>(
4671
+ "POST",
4672
+ `/v1/workspaces/${workspaceId}/skills/install`,
4673
+ request,
4674
+ );
4675
+ }
4676
+
4677
+ async previewSkillUninstall(
4678
+ workspaceId: string,
4679
+ capabilityId: string,
4680
+ ): Promise<SkillUninstallPreview> {
4681
+ return await this.requestJson<SkillUninstallPreview>(
4682
+ "GET",
4683
+ `/v1/workspaces/${workspaceId}/skills/${encodeURIComponent(capabilityId)}/uninstall-preview`,
4684
+ );
4685
+ }
4686
+
4687
+ /** Remove the direct owner under an optimistic installation-version fence. */
4688
+ async uninstallSkill(
4689
+ workspaceId: string,
4690
+ capabilityId: string,
4691
+ request: UninstallSkillRequest,
4692
+ ): Promise<UninstallSkillResult> {
4693
+ return await this.requestJson<UninstallSkillResult>(
4694
+ "DELETE",
4695
+ `/v1/workspaces/${workspaceId}/skills/${encodeURIComponent(capabilityId)}`,
4696
+ request,
4697
+ );
4698
+ }
4699
+
3485
4700
  // --- Connections -------------------------------------------------------------------------------
3486
4701
 
3487
4702
  async listConnections(workspaceId: string): Promise<ConnectionMetadata[]> {
@@ -3492,6 +4707,15 @@ export class OpenGeniClient {
3492
4707
  return response.connections;
3493
4708
  }
3494
4709
 
4710
+ /** List the secret-free Slack team -> OpenGeni tenant routing authority. */
4711
+ async listSlackInstallationBindings(workspaceId: string): Promise<SlackInstallationBinding[]> {
4712
+ const response = await this.requestJson<ListSlackInstallationBindingsResponse>(
4713
+ "GET",
4714
+ `/v1/workspaces/${workspaceId}/connections/slack-bot/bindings`,
4715
+ );
4716
+ return response.bindings;
4717
+ }
4718
+
3495
4719
  async createConnection(
3496
4720
  workspaceId: string,
3497
4721
  request: CreateConnectionRequest,
@@ -3634,8 +4858,16 @@ export class OpenGeniClient {
3634
4858
  // --- GitHub ----------------------------------------------------------------------------------
3635
4859
 
3636
4860
  /** GitHub App server configuration plus truthful workspace binding status. */
3637
- async getGitHubApp(workspaceId: string): Promise<GitHubAppInfo> {
3638
- return await this.requestJson<GitHubAppInfo>("GET", `/v1/workspaces/${workspaceId}/github/app`);
4861
+ async getGitHubApp(
4862
+ workspaceId: string,
4863
+ options: { returnPath?: string } = {},
4864
+ ): Promise<GitHubAppInfo> {
4865
+ return await this.requestJson<GitHubAppInfo>(
4866
+ "GET",
4867
+ `/v1/workspaces/${workspaceId}/github/app`,
4868
+ undefined,
4869
+ options.returnPath ? { returnPath: options.returnPath } : undefined,
4870
+ );
3639
4871
  }
3640
4872
 
3641
4873
  /** Build the GitHub owner-consent entry URL for fresh workspace-bound state. */
@@ -3955,7 +5187,8 @@ export class OpenGeniClient {
3955
5187
  );
3956
5188
  }
3957
5189
 
3958
- protected async requestJson<T>(
5190
+ /** Contract-checked JSON transport shared by opt-in typed SDK clients. */
5191
+ async requestJson<T>(
3959
5192
  method: string,
3960
5193
  path: string,
3961
5194
  body?: unknown,
@@ -3996,6 +5229,35 @@ export class OpenGeniClient {
3996
5229
  }
3997
5230
  }
3998
5231
 
5232
+ /** Authenticated, contract-checked response for bounded streaming downloads. */
5233
+ protected async requestResponse(
5234
+ method: string,
5235
+ path: string,
5236
+ query: Record<string, string> = {},
5237
+ options: OpenGeniRequestOptions = {},
5238
+ ): Promise<Response> {
5239
+ const correlationId = crypto.randomUUID();
5240
+ let response: Response;
5241
+ try {
5242
+ response = await this.fetchImpl(this.url(path, query), {
5243
+ method,
5244
+ headers: {
5245
+ ...this.headers(correlationId),
5246
+ Accept: "application/octet-stream",
5247
+ },
5248
+ ...(options.signal ? { signal: options.signal } : {}),
5249
+ });
5250
+ } catch (error) {
5251
+ if (isMutationMethod(method)) throw mutationTransportError(correlationId);
5252
+ throw error;
5253
+ }
5254
+ assertApiContractResponse(response);
5255
+ if (!response.ok) {
5256
+ throw await apiErrorFromResponse(response, { method, correlationId });
5257
+ }
5258
+ return response;
5259
+ }
5260
+
3999
5261
  /** Like `requestJson` for endpoints that respond with no body (204). */
4000
5262
  private async requestVoid(method: string, path: string, body?: unknown): Promise<void> {
4001
5263
  const correlationId = crypto.randomUUID();
@@ -4235,6 +5497,55 @@ async function cancelResponseBody(response: Response, reason: string): Promise<v
4235
5497
  await response.body?.cancel(reason).catch(() => undefined);
4236
5498
  }
4237
5499
 
5500
+ function assertRetainedGeneratedImageReceipt(
5501
+ workspaceId: string,
5502
+ artifact: RetainedArtifactReference,
5503
+ ): void {
5504
+ if (!parseRetainedGeneratedImageReference(artifact, workspaceId)) {
5505
+ throw new OpenGeniApiError(502, "retained generated image receipt is invalid");
5506
+ }
5507
+ }
5508
+
5509
+ function assertSafeArtifactDownloadUrl(value: FileDownloadUrlResponse): void {
5510
+ if (
5511
+ !value ||
5512
+ typeof value.url !== "string" ||
5513
+ typeof value.expiresAt !== "string" ||
5514
+ !Number.isFinite(Date.parse(value.expiresAt))
5515
+ ) {
5516
+ throw new OpenGeniApiError(502, "retained artifact download URL is invalid");
5517
+ }
5518
+ let parsed: URL;
5519
+ try {
5520
+ parsed = new URL(value.url);
5521
+ } catch {
5522
+ throw new OpenGeniApiError(502, "retained artifact download URL is invalid");
5523
+ }
5524
+ if (!["http:", "https:"].includes(parsed.protocol) || parsed.username || parsed.password) {
5525
+ throw new OpenGeniApiError(502, "retained artifact download URL is unsafe");
5526
+ }
5527
+ }
5528
+
5529
+ function assertSafeVideoArtifactPlaybackSource(
5530
+ value: VideoArtifactPlaybackSource,
5531
+ expectedArtifactId: string,
5532
+ ): void {
5533
+ if (
5534
+ !value ||
5535
+ value.schemaVersion !== 1 ||
5536
+ value.artifactId !== expectedArtifactId ||
5537
+ value.contentType !== "video/mp4" ||
5538
+ value.acceptRanges !== "bytes" ||
5539
+ !Number.isSafeInteger(value.sizeBytes) ||
5540
+ value.sizeBytes <= 0 ||
5541
+ value.sizeBytes > GENERATED_VIDEO_MAX_BYTES ||
5542
+ !/^[0-9a-f]{64}$/.test(value.sha256)
5543
+ ) {
5544
+ throw new OpenGeniApiError(502, "generated-video playback source is invalid");
5545
+ }
5546
+ assertSafeArtifactDownloadUrl({ url: value.url, expiresAt: value.expiresAt });
5547
+ }
5548
+
4238
5549
  function parseBoundedContentLength(value: string | null): number | null {
4239
5550
  if (value === null) return null;
4240
5551
  if (!/^\d+$/.test(value)) {