@opengeni/sdk 0.1.0 → 0.3.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.
package/dist/index.d.ts CHANGED
@@ -175,6 +175,64 @@ type KnownPermission = (typeof KNOWN_PERMISSIONS)[number];
175
175
  */
176
176
  type Permission = KnownPermission | (string & {});
177
177
  type ProductAccessMode = "local" | "configured" | "managed";
178
+ /**
179
+ * One model a client may select at send time, plus the provider that serves it.
180
+ * The wire API (`responses` | `chat`) lets a client reason about provider
181
+ * capabilities; the provider id/label drive a picker's grouping. Mirrors the
182
+ * `ClientModel` shape projected into `ClientConfig` by the server.
183
+ */
184
+ type ClientModel = {
185
+ id: string;
186
+ label: string;
187
+ /** Provider id (e.g. `openai`, `azure`, or a registry provider id). */
188
+ provider: string;
189
+ providerLabel: string;
190
+ api: "responses" | "chat";
191
+ contextWindowTokens?: number | undefined;
192
+ };
193
+ /**
194
+ * How a deployment expects clients to authenticate to it, surfaced so a UI can
195
+ * wire up the right header/cookie without prior knowledge of the host setup.
196
+ * Discriminated on `mode`; `none` is the back-compat default.
197
+ */
198
+ type ClientAuthConfig = {
199
+ mode: "none";
200
+ } | {
201
+ mode: "deploymentKey";
202
+ headerName: "x-opengeni-access-key";
203
+ } | {
204
+ mode: "configuredToken";
205
+ headerName: "authorization";
206
+ scheme: "bearer";
207
+ } | {
208
+ mode: "managedSession";
209
+ session: "cookie";
210
+ };
211
+ /**
212
+ * Public, unauthenticated-by-default client bootstrap config returned by
213
+ * `GET /v1/config/client`: which models + reasoning efforts are exposed, the
214
+ * MCP servers and file-upload limits a composer should offer, and how the
215
+ * deployment expects the client to authenticate. `allowedModels` is kept for
216
+ * back-compat; `models` carries the richer provider-grouped list for a picker.
217
+ */
218
+ type ClientConfig = {
219
+ deploymentRevision: string;
220
+ defaultModel: string;
221
+ allowedModels: string[];
222
+ models: ClientModel[];
223
+ defaultReasoningEffort: ReasoningEffort;
224
+ allowedReasoningEfforts: ReasoningEffort[];
225
+ mcpServers: {
226
+ id: string;
227
+ name: string;
228
+ }[];
229
+ fileUploads: {
230
+ enabled: boolean;
231
+ maxSizeBytes: number;
232
+ };
233
+ productAccessMode: ProductAccessMode;
234
+ auth: ClientAuthConfig;
235
+ };
178
236
  type AccountRole = "owner" | "admin" | "member";
179
237
  type AccountGrant = {
180
238
  accountId: string;
@@ -251,6 +309,25 @@ type CreateApiKeyResponse = {
251
309
  type ListApiKeysResponse = {
252
310
  apiKeys: ApiKey[];
253
311
  };
312
+ type WorkspaceMember = {
313
+ subjectId: string;
314
+ subjectLabel: string | null;
315
+ role: string;
316
+ permissions: Permission[];
317
+ createdAt: string;
318
+ };
319
+ type ListWorkspaceMembersResponse = {
320
+ members: WorkspaceMember[];
321
+ };
322
+ type AddWorkspaceMemberRequest = {
323
+ email: string;
324
+ role?: string | undefined;
325
+ permissions: Permission[];
326
+ };
327
+ type UpdateWorkspaceMemberRequest = {
328
+ role?: string | undefined;
329
+ permissions: Permission[];
330
+ };
254
331
  type SessionGoalStatus = "active" | "paused" | "completed";
255
332
  type SessionGoalCreatedBy = "api" | "agent" | "scheduled_task";
256
333
  type SessionGoal = {
@@ -985,12 +1062,38 @@ declare class OpenGeniClient {
985
1062
  * it is a no-op (`status:"noop"`) with an explanatory message.
986
1063
  */
987
1064
  compactSessionContext(workspaceId: string, sessionId: string): Promise<CompactSessionContextResult>;
1065
+ /**
1066
+ * The deployment's public client bootstrap config: the host-exposed models
1067
+ * (provider-grouped in `models`, flat in `allowedModels` for back-compat),
1068
+ * reasoning efforts, MCP servers, file-upload limits, and how the client is
1069
+ * expected to authenticate. Drives a composer's model picker without prior
1070
+ * knowledge of the host setup; safe to call before any auth is established.
1071
+ */
1072
+ getClientConfig(): Promise<ClientConfig>;
988
1073
  /** The caller's access context: subject, account + workspace grants, defaults. */
989
1074
  getAccessContext(): Promise<AccessContext>;
990
1075
  listWorkspaces(): Promise<Workspace[]>;
991
1076
  createWorkspace(request: CreateWorkspaceRequest): Promise<Workspace>;
992
1077
  getWorkspace(workspaceId: string): Promise<Workspace>;
993
1078
  updateWorkspace(workspaceId: string, request: UpdateWorkspaceRequest): Promise<Workspace>;
1079
+ /**
1080
+ * Delete a workspace and everything in it. Refused (409) for the account's
1081
+ * only workspace and while it still has a running session. Irreversible.
1082
+ */
1083
+ deleteWorkspace(workspaceId: string): Promise<void>;
1084
+ /** The workspace's members (user + api_key subjects). */
1085
+ listWorkspaceMembers(workspaceId: string): Promise<WorkspaceMember[]>;
1086
+ /**
1087
+ * Add an already-registered user by email. 404s when no user with that email
1088
+ * exists (email invites for unknown users are deferred).
1089
+ */
1090
+ addWorkspaceMember(workspaceId: string, request: AddWorkspaceMemberRequest): Promise<WorkspaceMember>;
1091
+ updateWorkspaceMember(workspaceId: string, subjectId: string, request: UpdateWorkspaceMemberRequest): Promise<WorkspaceMember>;
1092
+ /**
1093
+ * Remove a member. Refused (409) for your own membership and for the last
1094
+ * member who can still manage the workspace.
1095
+ */
1096
+ removeWorkspaceMember(workspaceId: string, subjectId: string): Promise<void>;
994
1097
  createScheduledTask(workspaceId: string, request: CreateScheduledTaskRequest): Promise<ScheduledTask>;
995
1098
  updateScheduledTask(workspaceId: string, taskId: string, request: UpdateScheduledTaskRequest): Promise<ScheduledTask>;
996
1099
  pauseScheduledTask(workspaceId: string, taskId: string): Promise<ScheduledTask>;
@@ -1193,4 +1296,4 @@ type SseMessage = {
1193
1296
  };
1194
1297
  declare function parseSseStream(stream: ReadableStream<Uint8Array>): AsyncGenerator<SseMessage, void, void>;
1195
1298
 
1196
- export { type AccessContext, type AccessGrant, type AccountGrant, type AccountRole, type AgentMessageCompletedPayload, type AgentTextDeltaPayload, type AgentToolCallCreatedPayload, type AgentToolCallOutputPayload, type ApiKey, type BillingBalance, type BillingEntitlementsResponse, type BillingMode, type BillingSummary, type BillingUsageResponse, type CapabilityCatalogItem, type CapabilityCatalogResponse, type CapabilityInstallation, type CapabilityInstallationStatus, type CapabilityKind, type CapabilityPack, type CapabilityPackConnector, type CapabilityPackConnectorAuthModel, type CapabilityPackEnvironmentSpec, type CapabilityPackKnowledge, type CapabilityPackScheduledTaskTemplate, type CapabilityPackSkill, type CapabilityPackSkillFile, type CapabilityRuntime, type CapabilitySource, type ClientSessionEventInput, type CompactSessionContextResult, type CompleteFileUploadResponse, type CreateApiKeyRequest, type CreateApiKeyResponse, type CreateCapabilityCatalogItemRequest, type CreateCheckoutRequest, type CreateCheckoutResponse, type CreateDocumentBaseRequest, type CreateFileUploadRequest, type CreateFileUploadResponse, type CreateGitHubAppManifestRequest, type CreateGitHubAppManifestResponse, type CreateScheduledTaskRequest, type CreateSessionRequest, type CreateWorkspaceEnvironmentRequest, type CreateWorkspaceRequest, type DiscoverMcpCapabilitiesResponse, type Document, type DocumentBase, type DocumentSearchRequest, type DocumentSearchResponse, type DocumentSearchResult, type DocumentStatus, type EnableCapabilityRequest, type EnablePackRequest, type EntitlementValue, type Entitlements, type EntitlementsMode, type FetchLike, type FileAsset, type FileDownloadUrlResponse, type FileResourceRef, type FileStatus, type FileUploadData, type GetPackResponse, type GitHubAppInfo, type GitHubRepositoriesResponse, type GitHubRepository, type GoalSpec, KNOWN_PERMISSIONS, KNOWN_USAGE_EVENT_TYPES, type KnownPermission, type KnownSessionEventType, type KnownUsageEventType, type ListApiKeysResponse, type ListPacksResponse, OpenGeniApiError, OpenGeniClient, type OpenGeniClientOptions, OpenGeniStreamError, type PackInstallation, type PackInstallationStatus, type Permission, type ProductAccessMode, type ProxySessionEventStreamOptions, type ReasoningEffort, type RegisterCapabilityPackRequest, type RepositoryResourceRef, type ResourceRef, SESSION_EVENT_TYPES, type SandboxBackend, type ScheduledTask, type ScheduledTaskAgentConfig, type ScheduledTaskAgentConfigInput, type ScheduledTaskDayOfWeek, type ScheduledTaskOverlapPolicy, type ScheduledTaskRun, type ScheduledTaskRunMode, type ScheduledTaskRunStatus, type ScheduledTaskScheduleSpec, type ScheduledTaskStatus, type ScheduledTaskTriggerType, type SendMessageInput, type Session, type SessionEvent, type SessionEventStreamTransport, type SessionEventType, type SessionGoal, type SessionGoalCreatedBy, type SessionGoalStatus, type SessionStatus, type SessionStatusChangedPayload, type SessionTurn, type SessionTurnSource, type SessionTurnStatus, type SseMessage, type SseReStreamOptions, type SteerMessageResult, type StreamConnectionState, type StreamSessionEventsOptions, type ToolRef, type UpdateScheduledTaskRequest, type UpdateSessionGoalRequest, type UpdateSessionTurnRequest, type UpdateWorkspaceEnvironmentRequest, type UpdateWorkspaceRequest, type UploadFileInput, type UsageEvent, type UsageEventType, type UserApprovalDecisionEventInput, type UserInterruptEventInput, type UserMessageEventInput, type Workspace, type WorkspaceEnvironment, type WorkspaceEnvironmentVariableMetadata, type WorkspaceRegisteredPack, formatSseEvent, isRetryableStreamError, parseSseStream, proxySessionEventStream, resumeSequenceFromRequest, sessionEventsToSseResponse, sessionEventsToSseStream, streamSessionEvents };
1299
+ export { type AccessContext, type AccessGrant, type AccountGrant, type AccountRole, type AddWorkspaceMemberRequest, type AgentMessageCompletedPayload, type AgentTextDeltaPayload, type AgentToolCallCreatedPayload, type AgentToolCallOutputPayload, type ApiKey, type BillingBalance, type BillingEntitlementsResponse, type BillingMode, type BillingSummary, type BillingUsageResponse, type CapabilityCatalogItem, type CapabilityCatalogResponse, type CapabilityInstallation, type CapabilityInstallationStatus, type CapabilityKind, type CapabilityPack, type CapabilityPackConnector, type CapabilityPackConnectorAuthModel, type CapabilityPackEnvironmentSpec, type CapabilityPackKnowledge, type CapabilityPackScheduledTaskTemplate, type CapabilityPackSkill, type CapabilityPackSkillFile, type CapabilityRuntime, type CapabilitySource, type ClientConfig, type ClientModel, type ClientSessionEventInput, type CompactSessionContextResult, type CompleteFileUploadResponse, type CreateApiKeyRequest, type CreateApiKeyResponse, type CreateCapabilityCatalogItemRequest, type CreateCheckoutRequest, type CreateCheckoutResponse, type CreateDocumentBaseRequest, type CreateFileUploadRequest, type CreateFileUploadResponse, type CreateGitHubAppManifestRequest, type CreateGitHubAppManifestResponse, type CreateScheduledTaskRequest, type CreateSessionRequest, type CreateWorkspaceEnvironmentRequest, type CreateWorkspaceRequest, type DiscoverMcpCapabilitiesResponse, type Document, type DocumentBase, type DocumentSearchRequest, type DocumentSearchResponse, type DocumentSearchResult, type DocumentStatus, type EnableCapabilityRequest, type EnablePackRequest, type EntitlementValue, type Entitlements, type EntitlementsMode, type FetchLike, type FileAsset, type FileDownloadUrlResponse, type FileResourceRef, type FileStatus, type FileUploadData, type GetPackResponse, type GitHubAppInfo, type GitHubRepositoriesResponse, type GitHubRepository, type GoalSpec, KNOWN_PERMISSIONS, KNOWN_USAGE_EVENT_TYPES, type KnownPermission, type KnownSessionEventType, type KnownUsageEventType, type ListApiKeysResponse, type ListPacksResponse, type ListWorkspaceMembersResponse, OpenGeniApiError, OpenGeniClient, type OpenGeniClientOptions, OpenGeniStreamError, type PackInstallation, type PackInstallationStatus, type Permission, type ProductAccessMode, type ProxySessionEventStreamOptions, type ReasoningEffort, type RegisterCapabilityPackRequest, type RepositoryResourceRef, type ResourceRef, SESSION_EVENT_TYPES, type SandboxBackend, type ScheduledTask, type ScheduledTaskAgentConfig, type ScheduledTaskAgentConfigInput, type ScheduledTaskDayOfWeek, type ScheduledTaskOverlapPolicy, type ScheduledTaskRun, type ScheduledTaskRunMode, type ScheduledTaskRunStatus, type ScheduledTaskScheduleSpec, type ScheduledTaskStatus, type ScheduledTaskTriggerType, type SendMessageInput, type Session, type SessionEvent, type SessionEventStreamTransport, type SessionEventType, type SessionGoal, type SessionGoalCreatedBy, type SessionGoalStatus, type SessionStatus, type SessionStatusChangedPayload, type SessionTurn, type SessionTurnSource, type SessionTurnStatus, type SseMessage, type SseReStreamOptions, type SteerMessageResult, type StreamConnectionState, type StreamSessionEventsOptions, type ToolRef, type UpdateScheduledTaskRequest, type UpdateSessionGoalRequest, type UpdateSessionTurnRequest, type UpdateWorkspaceEnvironmentRequest, type UpdateWorkspaceMemberRequest, type UpdateWorkspaceRequest, type UploadFileInput, type UsageEvent, type UsageEventType, type UserApprovalDecisionEventInput, type UserInterruptEventInput, type UserMessageEventInput, type Workspace, type WorkspaceEnvironment, type WorkspaceEnvironmentVariableMetadata, type WorkspaceMember, type WorkspaceRegisteredPack, formatSseEvent, isRetryableStreamError, parseSseStream, proxySessionEventStream, resumeSequenceFromRequest, sessionEventsToSseResponse, sessionEventsToSseStream, streamSessionEvents };
package/dist/index.js CHANGED
@@ -427,6 +427,16 @@ var OpenGeniClient = class {
427
427
  return await this.requestJson("POST", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/context/compact`, {});
428
428
  }
429
429
  // --- Access + workspaces -----------------------------------------------------
430
+ /**
431
+ * The deployment's public client bootstrap config: the host-exposed models
432
+ * (provider-grouped in `models`, flat in `allowedModels` for back-compat),
433
+ * reasoning efforts, MCP servers, file-upload limits, and how the client is
434
+ * expected to authenticate. Drives a composer's model picker without prior
435
+ * knowledge of the host setup; safe to call before any auth is established.
436
+ */
437
+ async getClientConfig() {
438
+ return await this.requestJson("GET", "/v1/config/client");
439
+ }
430
440
  /** The caller's access context: subject, account + workspace grants, defaults. */
431
441
  async getAccessContext() {
432
442
  return await this.requestJson("GET", "/v1/access/me");
@@ -443,6 +453,40 @@ var OpenGeniClient = class {
443
453
  async updateWorkspace(workspaceId, request) {
444
454
  return await this.requestJson("PATCH", `/v1/workspaces/${workspaceId}`, request);
445
455
  }
456
+ /**
457
+ * Delete a workspace and everything in it. Refused (409) for the account's
458
+ * only workspace and while it still has a running session. Irreversible.
459
+ */
460
+ async deleteWorkspace(workspaceId) {
461
+ await this.requestVoid("DELETE", `/v1/workspaces/${workspaceId}`);
462
+ }
463
+ // --- Members ("People with access") -------------------------------------------
464
+ /** The workspace's members (user + api_key subjects). */
465
+ async listWorkspaceMembers(workspaceId) {
466
+ const response = await this.requestJson("GET", `/v1/workspaces/${workspaceId}/members`);
467
+ return response.members;
468
+ }
469
+ /**
470
+ * Add an already-registered user by email. 404s when no user with that email
471
+ * exists (email invites for unknown users are deferred).
472
+ */
473
+ async addWorkspaceMember(workspaceId, request) {
474
+ return await this.requestJson("POST", `/v1/workspaces/${workspaceId}/members`, request);
475
+ }
476
+ async updateWorkspaceMember(workspaceId, subjectId, request) {
477
+ return await this.requestJson(
478
+ "PATCH",
479
+ `/v1/workspaces/${workspaceId}/members/${encodeURIComponent(subjectId)}`,
480
+ request
481
+ );
482
+ }
483
+ /**
484
+ * Remove a member. Refused (409) for your own membership and for the last
485
+ * member who can still manage the workspace.
486
+ */
487
+ async removeWorkspaceMember(workspaceId, subjectId) {
488
+ await this.requestVoid("DELETE", `/v1/workspaces/${workspaceId}/members/${encodeURIComponent(subjectId)}`);
489
+ }
446
490
  // --- Scheduled tasks (write + runs) -------------------------------------------
447
491
  async createScheduledTask(workspaceId, request) {
448
492
  return await this.requestJson("POST", `/v1/workspaces/${workspaceId}/scheduled-tasks`, request);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/sse.ts","../src/stream.ts","../src/client.ts","../src/proxy.ts","../src/types.ts"],"sourcesContent":["/** Error for a non-2xx OpenGeni API response. */\nexport class OpenGeniApiError extends Error {\n readonly status: number;\n readonly body: string;\n\n constructor(status: number, body: string) {\n super(`OpenGeni API ${status}: ${body || \"(empty body)\"}`);\n this.name = \"OpenGeniApiError\";\n this.status = status;\n this.body = body;\n }\n}\n\n/** Error for an unrecoverable event-stream condition (not a transient drop). */\nexport class OpenGeniStreamError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"OpenGeniStreamError\";\n }\n}\n\nexport function isAbortError(error: unknown): boolean {\n return (\n (error instanceof DOMException && error.name === \"AbortError\") ||\n (error instanceof Error && error.name === \"AbortError\")\n );\n}\n\n/**\n * Transient conditions worth a reconnect: network-level failures (`fetch`\n * rejects with `TypeError`) and HTTP statuses that signal a temporary server\n * or contention condition. Auth/validation failures (401/403/404/...) are\n * permanent and surface to the caller instead.\n */\nexport function isRetryableStreamError(error: unknown): boolean {\n if (error instanceof OpenGeniApiError) {\n return error.status === 408 || error.status === 409 || error.status === 425 || error.status === 429 || error.status >= 500;\n }\n return error instanceof TypeError;\n}\n","/**\n * Minimal incremental Server-Sent Events parser over a byte stream.\n *\n * Implements the parts of the SSE wire format OpenGeni uses: `id`, `event`,\n * and `data` fields, multi-line data, comment lines, and both LF and CRLF\n * line endings. Messages without any `data` (comments, id-only blocks) are\n * not emitted.\n */\nexport type SseMessage = {\n id?: string;\n event?: string;\n data: string;\n};\n\nexport async function* parseSseStream(stream: ReadableStream<Uint8Array>): AsyncGenerator<SseMessage, void, void> {\n const reader = stream.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let id: string | undefined;\n let event: string | undefined;\n let dataLines: string[] | null = null;\n\n const dispatch = (): SseMessage | null => {\n const message = dataLines === null\n ? null\n : {\n ...(id !== undefined ? { id } : {}),\n ...(event !== undefined ? { event } : {}),\n data: dataLines.join(\"\\n\"),\n };\n id = undefined;\n event = undefined;\n dataLines = null;\n return message;\n };\n\n const handleLine = (line: string): SseMessage | null => {\n if (line === \"\") {\n return dispatch();\n }\n if (line.startsWith(\":\")) {\n return null;\n }\n const colon = line.indexOf(\":\");\n const field = colon === -1 ? line : line.slice(0, colon);\n let value = colon === -1 ? \"\" : line.slice(colon + 1);\n if (value.startsWith(\" \")) {\n value = value.slice(1);\n }\n if (field === \"data\") {\n (dataLines ??= []).push(value);\n } else if (field === \"event\") {\n event = value;\n } else if (field === \"id\") {\n id = value;\n }\n // Other fields (e.g. `retry`) are ignored; reconnect pacing is the\n // streaming layer's concern.\n return null;\n };\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n buffer += decoder.decode(value, { stream: true });\n let newline = buffer.indexOf(\"\\n\");\n while (newline !== -1) {\n let line = buffer.slice(0, newline);\n buffer = buffer.slice(newline + 1);\n if (line.endsWith(\"\\r\")) {\n line = line.slice(0, -1);\n }\n const message = handleLine(line);\n if (message) {\n yield message;\n }\n newline = buffer.indexOf(\"\\n\");\n }\n }\n // Per the SSE spec, a block is only dispatched on a blank line. Anything\n // still pending at end-of-stream came from a truncated connection and is\n // discarded — the streaming layer replays from its cursor on reconnect.\n } finally {\n await reader.cancel().catch(() => {});\n reader.releaseLock();\n }\n}\n","import { isAbortError, isRetryableStreamError, OpenGeniStreamError } from \"./errors\";\nimport { parseSseStream } from \"./sse\";\nimport type { SessionEvent } from \"./types\";\n\n/**\n * Transport boundary for the streaming core. The client implements it with\n * `fetch`; unit tests script it directly.\n */\nexport type SessionEventStreamTransport = {\n /** Open the SSE stream, replaying durable events after `after` first. */\n openStream: (after: number, signal: AbortSignal | undefined) => Promise<ReadableStream<Uint8Array>>;\n /** Replay durable events by sequence (`GET .../events?after=&limit=`). */\n listEvents: (after: number, limit: number) => Promise<SessionEvent[]>;\n};\n\nexport type StreamConnectionState = \"connecting\" | \"live\" | \"reconnecting\";\n\nexport type StreamSessionEventsOptions = {\n /** Resume after this sequence number (exclusive). Defaults to 0 (full replay). */\n after?: number;\n /** Aborting ends the stream gracefully (the generator returns). */\n signal?: AbortSignal;\n /** Reconnect on transient drops. Defaults to true. */\n reconnect?: boolean;\n /** Initial reconnect backoff. Defaults to 500ms. */\n reconnectDelayMs?: number;\n /** Backoff ceiling. Defaults to 10s. */\n maxReconnectDelayMs?: number;\n /**\n * Give up after this many consecutive failed reconnect attempts (i.e. N\n * reconnects = N+1 total open-stream calls). Defaults to unlimited.\n */\n maxReconnectAttempts?: number;\n onStateChange?: (state: StreamConnectionState) => void;\n};\n\n/**\n * Stream a session's events with exactly-once, in-order delivery.\n *\n * Guarantees, anchored on the per-session contiguous `sequence`:\n * - **No duplicates**: events at or below the cursor are dropped, so server\n * replay overlap and reconnect overlap never re-yield.\n * - **No gaps**: each reconnect resumes from the last seen sequence, and a\n * gap observed inside one connection is backfilled from the durable replay\n * endpoint before the newer event is yielded (events are durable before\n * they are published live, so the backfill always finds them).\n * - **Ordered**: sequences are yielded strictly ascending.\n *\n * The generator ends when `signal` aborts, when the server closes and\n * `reconnect` is false, or with an error for non-retryable failures.\n */\nexport async function* streamSessionEvents(\n transport: SessionEventStreamTransport,\n options: StreamSessionEventsOptions = {},\n): AsyncGenerator<SessionEvent, void, void> {\n const signal = options.signal;\n const reconnect = options.reconnect ?? true;\n const baseDelayMs = options.reconnectDelayMs ?? 500;\n const maxDelayMs = options.maxReconnectDelayMs ?? 10_000;\n const maxAttempts = options.maxReconnectAttempts ?? Number.POSITIVE_INFINITY;\n let cursor = options.after ?? 0;\n let failedAttempts = 0;\n let delayMs = baseDelayMs;\n let everConnected = false;\n\n while (!signal?.aborted) {\n options.onStateChange?.(everConnected || failedAttempts > 0 ? \"reconnecting\" : \"connecting\");\n const cursorAtOpen = cursor;\n try {\n const body = await transport.openStream(cursor, signal);\n everConnected = true;\n failedAttempts = 0;\n delayMs = baseDelayMs;\n options.onStateChange?.(\"live\");\n for await (const message of parseSseStream(body)) {\n // Re-check after every yield resumption: an abort from the consumer\n // must not let already-buffered events keep flowing.\n if (signal?.aborted) {\n return;\n }\n const event = parseSessionEvent(message.data);\n if (!event || event.sequence <= cursor) {\n continue;\n }\n if (event.sequence > cursor + 1) {\n for await (const missed of backfillEvents(transport, cursor, event.sequence - 1)) {\n cursor = missed.sequence;\n yield missed;\n if (signal?.aborted) {\n return;\n }\n }\n }\n cursor = event.sequence;\n yield event;\n }\n if (!reconnect) {\n return;\n }\n // Clean server close: reconnect immediately when the connection made\n // progress (servers legitimately cycle long SSE connections); pace\n // empty closes so a misbehaving server is not hammered in a hot loop.\n if (cursor === cursorAtOpen) {\n await sleep(baseDelayMs, signal);\n }\n continue;\n } catch (error) {\n if (signal?.aborted || isAbortError(error)) {\n return;\n }\n if (!reconnect || !isRetryableStreamError(error)) {\n throw error;\n }\n failedAttempts += 1;\n if (failedAttempts > maxAttempts) {\n throw new OpenGeniStreamError(\n `event stream gave up after ${maxAttempts} consecutive failed reconnect attempts: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n await sleep(delayMs, signal);\n delayMs = Math.min(Math.max(delayMs * 2, baseDelayMs), maxDelayMs);\n }\n}\n\n/**\n * Yield the durable events with `fromExclusive < sequence <= toInclusive`,\n * in order. Sequences are contiguous, so every one of them must exist in the\n * replay endpoint; if any is missing the function throws instead of skipping\n * it — continuing would silently break the gap-free delivery guarantee.\n */\nasync function* backfillEvents(\n transport: SessionEventStreamTransport,\n fromExclusive: number,\n toInclusive: number,\n): AsyncGenerator<SessionEvent, void, void> {\n let cursor = fromExclusive;\n while (cursor < toInclusive) {\n const page = await transport.listEvents(cursor, Math.min(500, toInclusive - cursor));\n const advancing = page\n .filter((event) => event.sequence > cursor && event.sequence <= toInclusive)\n .sort((a, b) => a.sequence - b.sequence);\n if (advancing.length === 0) {\n throw new OpenGeniStreamError(\n `event replay backfill stalled: expected sequences ${cursor + 1}..${toInclusive} but the replay endpoint returned none of them`,\n );\n }\n for (const event of advancing) {\n if (event.sequence !== cursor + 1) {\n throw new OpenGeniStreamError(\n `event replay backfill is missing sequence ${cursor + 1} (replay endpoint skipped to ${event.sequence}); refusing to deliver with a gap`,\n );\n }\n cursor = event.sequence;\n yield event;\n }\n }\n}\n\nfunction parseSessionEvent(data: string): SessionEvent | null {\n let parsed: unknown;\n try {\n parsed = JSON.parse(data);\n } catch {\n return null;\n }\n if (\n typeof parsed !== \"object\" ||\n parsed === null ||\n typeof (parsed as { sequence?: unknown }).sequence !== \"number\" ||\n typeof (parsed as { type?: unknown }).type !== \"string\" ||\n typeof (parsed as { id?: unknown }).id !== \"string\"\n ) {\n return null;\n }\n return parsed as SessionEvent;\n}\n\nasync function sleep(delayMs: number, signal: AbortSignal | undefined): Promise<void> {\n if (signal?.aborted || delayMs <= 0) {\n return;\n }\n await new Promise<void>((resolve) => {\n const timer = setTimeout(done, delayMs);\n function done(): void {\n clearTimeout(timer);\n signal?.removeEventListener(\"abort\", done);\n resolve();\n }\n signal?.addEventListener(\"abort\", done, { once: true });\n });\n}\n","import { OpenGeniApiError } from \"./errors\";\nimport { streamSessionEvents, type SessionEventStreamTransport, type StreamSessionEventsOptions } from \"./stream\";\nimport type {\n AccessContext,\n ApiKey,\n BillingEntitlementsResponse,\n BillingSummary,\n BillingUsageResponse,\n CapabilityCatalogItem,\n CapabilityCatalogResponse,\n CapabilityInstallation,\n ClientSessionEventInput,\n CompactSessionContextResult,\n CompleteFileUploadResponse,\n CreateApiKeyRequest,\n CreateApiKeyResponse,\n CreateCapabilityCatalogItemRequest,\n CreateCheckoutRequest,\n CreateCheckoutResponse,\n CreateDocumentBaseRequest,\n CreateFileUploadRequest,\n CreateFileUploadResponse,\n CreateGitHubAppManifestRequest,\n CreateGitHubAppManifestResponse,\n CreateScheduledTaskRequest,\n CreateSessionRequest,\n CreateWorkspaceEnvironmentRequest,\n CreateWorkspaceRequest,\n DiscoverMcpCapabilitiesResponse,\n Document,\n DocumentBase,\n DocumentSearchResponse,\n EnableCapabilityRequest,\n EnablePackRequest,\n FileAsset,\n FileDownloadUrlResponse,\n GetPackResponse,\n GitHubAppInfo,\n GitHubRepositoriesResponse,\n ListApiKeysResponse,\n ListPacksResponse,\n PackInstallation,\n ReasoningEffort,\n RegisterCapabilityPackRequest,\n ResourceRef,\n ScheduledTask,\n ScheduledTaskRun,\n Session,\n SessionEvent,\n SessionGoal,\n SessionTurn,\n ToolRef,\n UpdateScheduledTaskRequest,\n UpdateSessionGoalRequest,\n UpdateSessionTurnRequest,\n UpdateWorkspaceEnvironmentRequest,\n UpdateWorkspaceRequest,\n UploadFileInput,\n WorkspaceEnvironment,\n WorkspaceEnvironmentVariableMetadata,\n WorkspaceRegisteredPack,\n Workspace,\n} from \"./types\";\n\nexport type FetchLike = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;\n\nexport type OpenGeniClientOptions = {\n /** Base URL of the OpenGeni API, e.g. `https://api.example.com`. */\n baseUrl: string;\n /** OpenGeni API key, sent as `Authorization: Bearer <apiKey>`. */\n apiKey?: string;\n /** Extra headers (static or computed per request) merged into every call. */\n headers?: Record<string, string> | (() => Record<string, string>);\n /** Custom fetch implementation. Defaults to the global `fetch`. */\n fetch?: FetchLike;\n};\n\nexport type SendMessageInput = {\n text: string;\n resources?: ResourceRef[];\n tools?: ToolRef[];\n model?: string;\n reasoningEffort?: ReasoningEffort;\n clientEventId?: string;\n};\n\nexport type SteerMessageResult = {\n /** The accepted `user.message` event. */\n accepted: SessionEvent;\n /**\n * The turn created for the message, when it could be located — usually\n * still queued, but already claimed (running/requires_action or even\n * finished) when the worker picked it up mid-call.\n */\n turn: SessionTurn | null;\n /** True when the running turn was interrupted to make way for the message. */\n interrupted: boolean;\n};\n\n/**\n * Typed client for the OpenGeni public API. Framework-agnostic: only needs\n * WHATWG `fetch` + streams, so it runs in Node 18+, Bun, Deno, browsers, and\n * edge runtimes.\n */\nexport class OpenGeniClient {\n private readonly baseUrl: string;\n private readonly options: OpenGeniClientOptions;\n private readonly fetchImpl: FetchLike;\n\n constructor(options: OpenGeniClientOptions) {\n this.baseUrl = options.baseUrl.replace(/\\/+$/, \"\");\n this.options = options;\n // Bind lazily so environments that polyfill fetch after module load work.\n this.fetchImpl = options.fetch ?? ((input, init) => fetch(input, init));\n }\n\n // --- Session lifecycle ---------------------------------------------------\n\n async createSession(workspaceId: string, request: CreateSessionRequest): Promise<Session> {\n return await this.requestJson<Session>(\"POST\", `/v1/workspaces/${workspaceId}/sessions`, request);\n }\n\n async getSession(workspaceId: string, sessionId: string): Promise<Session> {\n return await this.requestJson<Session>(\"GET\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}`);\n }\n\n async listSessions(workspaceId: string, options: { limit?: number } = {}): Promise<Session[]> {\n return await this.requestJson<Session[]>(\"GET\", `/v1/workspaces/${workspaceId}/sessions`, undefined, {\n ...(options.limit !== undefined ? { limit: String(options.limit) } : {}),\n });\n }\n\n async listTurns(workspaceId: string, sessionId: string, options: { limit?: number } = {}): Promise<SessionTurn[]> {\n return await this.requestJson<SessionTurn[]>(\"GET\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/turns`, undefined, {\n ...(options.limit !== undefined ? { limit: String(options.limit) } : {}),\n });\n }\n\n // --- Scheduled tasks -------------------------------------------------------\n\n async listScheduledTasks(workspaceId: string, options: { limit?: number } = {}): Promise<ScheduledTask[]> {\n return await this.requestJson<ScheduledTask[]>(\"GET\", `/v1/workspaces/${workspaceId}/scheduled-tasks`, undefined, {\n ...(options.limit !== undefined ? { limit: String(options.limit) } : {}),\n });\n }\n\n async getScheduledTask(workspaceId: string, taskId: string): Promise<ScheduledTask> {\n return await this.requestJson<ScheduledTask>(\"GET\", `/v1/workspaces/${workspaceId}/scheduled-tasks/${taskId}`);\n }\n\n // --- Events: replay, send, stream ----------------------------------------\n\n /** Replay durable events by sequence: events with `sequence > after`, ascending. */\n async listEvents(\n workspaceId: string,\n sessionId: string,\n options: { after?: number; limit?: number } = {},\n ): Promise<SessionEvent[]> {\n return await this.requestJson<SessionEvent[]>(\"GET\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/events`, undefined, {\n ...(options.after !== undefined ? { after: String(options.after) } : {}),\n ...(options.limit !== undefined ? { limit: String(options.limit) } : {}),\n });\n }\n\n /** POST a user/control event to the session. Returns the accepted event. */\n async sendEvent(workspaceId: string, sessionId: string, event: ClientSessionEventInput): Promise<SessionEvent> {\n return await this.requestJson<SessionEvent>(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/events`, event);\n }\n\n async sendMessage(workspaceId: string, sessionId: string, message: string | SendMessageInput): Promise<SessionEvent> {\n const input = typeof message === \"string\" ? { text: message } : message;\n const { clientEventId, ...payload } = input;\n return await this.sendEvent(workspaceId, sessionId, {\n type: \"user.message\",\n ...(clientEventId !== undefined ? { clientEventId } : {}),\n payload,\n });\n }\n\n async interrupt(\n workspaceId: string,\n sessionId: string,\n options: { reason?: string; clientEventId?: string } = {},\n ): Promise<SessionEvent> {\n return await this.sendEvent(workspaceId, sessionId, {\n type: \"user.interrupt\",\n ...(options.clientEventId !== undefined ? { clientEventId: options.clientEventId } : {}),\n payload: options.reason !== undefined ? { reason: options.reason } : {},\n });\n }\n\n async sendApprovalDecision(\n workspaceId: string,\n sessionId: string,\n decision: { approvalId: string; decision: \"approve\" | \"reject\"; message?: string; clientEventId?: string },\n ): Promise<SessionEvent> {\n const { clientEventId, ...payload } = decision;\n return await this.sendEvent(workspaceId, sessionId, {\n type: \"user.approvalDecision\",\n ...(clientEventId !== undefined ? { clientEventId } : {}),\n payload,\n });\n }\n\n /**\n * Live-stream a session's events with automatic reconnect, resume from the\n * last seen sequence, gap backfill, and duplicate suppression. See\n * {@link streamSessionEvents} for the delivery guarantees.\n */\n streamEvents(\n workspaceId: string,\n sessionId: string,\n options: StreamSessionEventsOptions = {},\n ): AsyncGenerator<SessionEvent, void, void> {\n return streamSessionEvents(this.eventStreamTransport(workspaceId, sessionId), options);\n }\n\n /** The transport `streamEvents` runs on; useful for custom streaming layers. */\n eventStreamTransport(workspaceId: string, sessionId: string): SessionEventStreamTransport {\n return {\n openStream: async (after, signal) => await this.openEventStream(workspaceId, sessionId, { after, ...(signal ? { signal } : {}) }),\n listEvents: async (after, limit) => await this.listEvents(workspaceId, sessionId, { after, limit }),\n };\n }\n\n /** Open one raw SSE connection (no reconnect). Most callers want `streamEvents`. */\n async openEventStream(\n workspaceId: string,\n sessionId: string,\n options: { after?: number; signal?: AbortSignal } = {},\n ): Promise<ReadableStream<Uint8Array>> {\n const url = this.url(`/v1/workspaces/${workspaceId}/sessions/${sessionId}/events/stream`, {\n after: String(options.after ?? 0),\n });\n const response = await this.fetchImpl(url, {\n method: \"GET\",\n headers: { ...this.headers(), Accept: \"text/event-stream\" },\n ...(options.signal ? { signal: options.signal } : {}),\n });\n if (!response.ok) {\n throw new OpenGeniApiError(response.status, await safeText(response));\n }\n if (!response.body) {\n throw new OpenGeniApiError(response.status, \"SSE response did not include a readable body\");\n }\n return response.body;\n }\n\n // --- Turn queue ------------------------------------------------------------\n\n /** Edit a still-queued turn (prompt, model, resources, tools, ...). */\n async updateQueuedTurn(\n workspaceId: string,\n sessionId: string,\n turnId: string,\n update: UpdateSessionTurnRequest,\n ): Promise<SessionTurn> {\n return await this.requestJson<SessionTurn>(\n \"PATCH\",\n `/v1/workspaces/${workspaceId}/sessions/${sessionId}/turns/${turnId}`,\n update,\n );\n }\n\n /**\n * Reorder the queued turns. `turnIds` must all reference queued turns; the\n * server assigns positions in the given order and returns the queue.\n */\n async reorderQueuedTurns(workspaceId: string, sessionId: string, turnIds: string[]): Promise<SessionTurn[]> {\n return await this.requestJson<SessionTurn[]>(\n \"POST\",\n `/v1/workspaces/${workspaceId}/sessions/${sessionId}/turns/reorder`,\n { turnIds },\n );\n }\n\n /** Cancel a queued turn before it is claimed. Returns the cancelled turn. */\n async deleteQueuedTurn(workspaceId: string, sessionId: string, turnId: string): Promise<SessionTurn> {\n return await this.requestJson<SessionTurn>(\n \"DELETE\",\n `/v1/workspaces/${workspaceId}/sessions/${sessionId}/turns/${turnId}`,\n );\n }\n\n /**\n * Steer: deliver a message *now* instead of behind the queue. Sends the\n * message, promotes its queued turn to the front, and interrupts the\n * running turn so the session picks the steer turn up next. On a session\n * that is not running this degrades gracefully to a plain queued message.\n *\n * The steer turn is located by `triggerEventId` across ALL turns (retried\n * briefly in case the server is still materializing it) — not just the\n * queued ones, because the worker can claim the steer turn before it is\n * ever observed queued, and a claimed steer turn means the message is\n * already being delivered: interrupting then would cancel the very message\n * being steered. If the turn cannot be found while other turns are queued,\n * the interrupt is also skipped — stopping the running turn would otherwise\n * promote someone else's queued work over this message — and the call\n * degrades to a plain queued send (`interrupted: false`).\n */\n async steerMessage(\n workspaceId: string,\n sessionId: string,\n message: string | SendMessageInput,\n ): Promise<SteerMessageResult> {\n const accepted = await this.sendMessage(workspaceId, sessionId, message);\n let steerTurn: SessionTurn | null = null;\n let queued: SessionTurn[] = [];\n for (let attempt = 0; attempt < 4; attempt += 1) {\n if (attempt > 0) {\n await delay(150 * attempt);\n }\n const turns = await this.listTurns(workspaceId, sessionId);\n queued = turns\n .filter((turn) => turn.status === \"queued\")\n .sort((a, b) => a.position - b.position || a.createdAt.localeCompare(b.createdAt));\n // Match against every turn, whatever its status: a steer turn that is\n // already running/requires_action (or even finished) was claimed before\n // this listing — that is delivery, not grounds for an interrupt.\n steerTurn = turns.find((turn) => turn.triggerEventId === accepted.id) ?? null;\n if (steerTurn) {\n break;\n }\n }\n const steerTurnQueued = steerTurn?.status === \"queued\";\n if (steerTurn && steerTurnQueued && queued.length > 1) {\n const front = steerTurn;\n await this.reorderQueuedTurns(workspaceId, sessionId, [\n front.id,\n ...queued.filter((turn) => turn.id !== front.id).map((turn) => turn.id),\n ]);\n }\n // Interrupting is only safe when the next claim is provably this message:\n // either the steer turn sits queued (now at the front), or no turn\n // materialized yet AND nothing else is queued. A steer turn observed in\n // any non-queued state was already claimed — skip the interrupt.\n const canDeliverNext = steerTurnQueued || (steerTurn === null && queued.length === 0);\n const session = await this.getSession(workspaceId, sessionId);\n // If the previously running turn already finished and the session claimed\n // the steer turn itself, interrupting now would cancel the very message\n // being steered. `activeTurnId` is the claim check; the residual window\n // between this read and the interrupt landing is accepted (an interrupt\n // can never be atomic with a status read over HTTP).\n const steerTurnAlreadyActive = steerTurn !== null && session.activeTurnId === steerTurn.id;\n const interrupted = canDeliverNext\n && !steerTurnAlreadyActive\n && (session.status === \"running\" || session.status === \"requires_action\");\n if (interrupted) {\n await this.interrupt(workspaceId, sessionId, { reason: \"steer\" });\n }\n return { accepted, turn: steerTurn, interrupted };\n }\n\n // --- Goals -------------------------------------------------------------------\n\n /** The session's goal. 404s when the session never had one. */\n async getGoal(workspaceId: string, sessionId: string): Promise<SessionGoal> {\n return await this.requestJson<SessionGoal>(\"GET\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/goal`);\n }\n\n async updateGoal(workspaceId: string, sessionId: string, request: UpdateSessionGoalRequest): Promise<SessionGoal> {\n return await this.requestJson<SessionGoal>(\"PATCH\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/goal`, request);\n }\n\n /** Pause the goal loop: the session stops self-continuing until resumed. */\n async pauseGoal(workspaceId: string, sessionId: string, options: { rationale?: string } = {}): Promise<SessionGoal> {\n return await this.updateGoal(workspaceId, sessionId, {\n status: \"paused\",\n ...(options.rationale !== undefined ? { rationale: options.rationale } : {}),\n });\n }\n\n /** Resume a paused goal: resets counters and re-arms the continuation loop. */\n async resumeGoal(workspaceId: string, sessionId: string): Promise<SessionGoal> {\n return await this.updateGoal(workspaceId, sessionId, { status: \"active\" });\n }\n\n // --- Operator context controls (/clear, /compact) ---------------------------\n\n /**\n * Clear the session's conversation context. Destructive and audit-preserving:\n * the server supersedes (never deletes) the live history and emits a\n * `session.context.cleared` event. Refused (409) while a turn is in flight or\n * awaiting action. `confirm:true` is sent so an accidental call cannot wipe\n * context — the destructive intent is explicit on the wire.\n */\n async clearSessionContext(workspaceId: string, sessionId: string): Promise<void> {\n await this.requestVoid(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/context/clear`, { confirm: true });\n }\n\n /**\n * Trigger conversation compaction now. On the client-managed (Azure) path this\n * queues a forced compaction the worker honors before the next turn\n * (`status:\"queued\"`); on a server-managed provider or when compaction is off\n * it is a no-op (`status:\"noop\"`) with an explanatory message.\n */\n async compactSessionContext(workspaceId: string, sessionId: string): Promise<CompactSessionContextResult> {\n return await this.requestJson<CompactSessionContextResult>(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/context/compact`, {});\n }\n\n // --- Access + workspaces -----------------------------------------------------\n\n /** The caller's access context: subject, account + workspace grants, defaults. */\n async getAccessContext(): Promise<AccessContext> {\n return await this.requestJson<AccessContext>(\"GET\", \"/v1/access/me\");\n }\n\n async listWorkspaces(): Promise<Workspace[]> {\n return await this.requestJson<Workspace[]>(\"GET\", \"/v1/workspaces\");\n }\n\n async createWorkspace(request: CreateWorkspaceRequest): Promise<Workspace> {\n return await this.requestJson<Workspace>(\"POST\", \"/v1/workspaces\", request);\n }\n\n async getWorkspace(workspaceId: string): Promise<Workspace> {\n return await this.requestJson<Workspace>(\"GET\", `/v1/workspaces/${workspaceId}`);\n }\n\n async updateWorkspace(workspaceId: string, request: UpdateWorkspaceRequest): Promise<Workspace> {\n return await this.requestJson<Workspace>(\"PATCH\", `/v1/workspaces/${workspaceId}`, request);\n }\n\n // --- Scheduled tasks (write + runs) -------------------------------------------\n\n async createScheduledTask(workspaceId: string, request: CreateScheduledTaskRequest): Promise<ScheduledTask> {\n return await this.requestJson<ScheduledTask>(\"POST\", `/v1/workspaces/${workspaceId}/scheduled-tasks`, request);\n }\n\n async updateScheduledTask(workspaceId: string, taskId: string, request: UpdateScheduledTaskRequest): Promise<ScheduledTask> {\n return await this.requestJson<ScheduledTask>(\"PATCH\", `/v1/workspaces/${workspaceId}/scheduled-tasks/${taskId}`, request);\n }\n\n async pauseScheduledTask(workspaceId: string, taskId: string): Promise<ScheduledTask> {\n return await this.requestJson<ScheduledTask>(\"POST\", `/v1/workspaces/${workspaceId}/scheduled-tasks/${taskId}/pause`);\n }\n\n async resumeScheduledTask(workspaceId: string, taskId: string): Promise<ScheduledTask> {\n return await this.requestJson<ScheduledTask>(\"POST\", `/v1/workspaces/${workspaceId}/scheduled-tasks/${taskId}/resume`);\n }\n\n /**\n * Fire the task immediately (manual trigger), independent of its schedule.\n * Pass a stable `triggerId` to make a retried trigger idempotent — the same\n * token charges once and starts one run. Omit it and each call is distinct.\n */\n async triggerScheduledTask(workspaceId: string, taskId: string, options: { triggerId?: string } = {}): Promise<ScheduledTask> {\n return await this.requestJson<ScheduledTask>(\n \"POST\",\n `/v1/workspaces/${workspaceId}/scheduled-tasks/${taskId}/trigger`,\n options.triggerId ? { triggerId: options.triggerId } : undefined,\n );\n }\n\n async deleteScheduledTask(workspaceId: string, taskId: string): Promise<void> {\n await this.requestJson<unknown>(\"DELETE\", `/v1/workspaces/${workspaceId}/scheduled-tasks/${taskId}`);\n }\n\n async listScheduledTaskRuns(\n workspaceId: string,\n taskId: string,\n options: { limit?: number } = {},\n ): Promise<ScheduledTaskRun[]> {\n return await this.requestJson<ScheduledTaskRun[]>(\n \"GET\",\n `/v1/workspaces/${workspaceId}/scheduled-tasks/${taskId}/runs`,\n undefined,\n { ...(options.limit !== undefined ? { limit: String(options.limit) } : {}) },\n );\n }\n\n // --- Environments --------------------------------------------------------------\n // Variable values are write-only: reads return name/version metadata only.\n\n async listEnvironments(workspaceId: string): Promise<WorkspaceEnvironment[]> {\n return await this.requestJson<WorkspaceEnvironment[]>(\"GET\", `/v1/workspaces/${workspaceId}/environments`);\n }\n\n async createEnvironment(workspaceId: string, request: CreateWorkspaceEnvironmentRequest): Promise<WorkspaceEnvironment> {\n return await this.requestJson<WorkspaceEnvironment>(\"POST\", `/v1/workspaces/${workspaceId}/environments`, request);\n }\n\n async getEnvironment(workspaceId: string, environmentId: string): Promise<WorkspaceEnvironment> {\n return await this.requestJson<WorkspaceEnvironment>(\"GET\", `/v1/workspaces/${workspaceId}/environments/${environmentId}`);\n }\n\n async updateEnvironment(\n workspaceId: string,\n environmentId: string,\n request: UpdateWorkspaceEnvironmentRequest,\n ): Promise<WorkspaceEnvironment> {\n return await this.requestJson<WorkspaceEnvironment>(\n \"PATCH\",\n `/v1/workspaces/${workspaceId}/environments/${environmentId}`,\n request,\n );\n }\n\n async deleteEnvironment(workspaceId: string, environmentId: string): Promise<void> {\n await this.requestJson<unknown>(\"DELETE\", `/v1/workspaces/${workspaceId}/environments/${environmentId}`);\n }\n\n /** Create or rotate a variable. The value never comes back on any read. */\n async setEnvironmentVariable(\n workspaceId: string,\n environmentId: string,\n name: string,\n value: string,\n ): Promise<WorkspaceEnvironmentVariableMetadata> {\n return await this.requestJson<WorkspaceEnvironmentVariableMetadata>(\n \"PUT\",\n `/v1/workspaces/${workspaceId}/environments/${environmentId}/variables/${encodeURIComponent(name)}`,\n { value },\n );\n }\n\n async deleteEnvironmentVariable(workspaceId: string, environmentId: string, name: string): Promise<void> {\n await this.requestJson<unknown>(\n \"DELETE\",\n `/v1/workspaces/${workspaceId}/environments/${environmentId}/variables/${encodeURIComponent(name)}`,\n );\n }\n\n // --- Files -----------------------------------------------------------------------\n\n /** Step 1 of the upload flow: returns the pre-signed PUT target. */\n async beginFileUpload(workspaceId: string, request: CreateFileUploadRequest): Promise<CreateFileUploadResponse> {\n return await this.requestJson<CreateFileUploadResponse>(\"POST\", `/v1/workspaces/${workspaceId}/files/uploads`, request);\n }\n\n /** Step 3 of the upload flow: server verifies the object and marks it ready. */\n async completeFileUpload(workspaceId: string, uploadId: string): Promise<FileAsset> {\n const response = await this.requestJson<CompleteFileUploadResponse>(\n \"POST\",\n `/v1/workspaces/${workspaceId}/files/uploads/${uploadId}/complete`,\n );\n return response.file;\n }\n\n /**\n * The whole upload flow as one call: begin -> PUT the bytes to the signed\n * URL (with its required headers; no API auth is sent to object storage)\n * -> complete. Returns the ready `FileAsset`.\n */\n async uploadFile(workspaceId: string, input: UploadFileInput): Promise<FileAsset> {\n // Copy Uint8Array views into a Blob so byte offsets/shared buffers can't\n // leak surrounding bytes into the PUT body.\n const body: Blob | ArrayBuffer | string = input.data instanceof Uint8Array\n ? new Blob([input.data.slice()])\n : input.data;\n const sizeBytes = typeof body === \"string\"\n ? new TextEncoder().encode(body).byteLength\n : body instanceof Blob ? body.size : body.byteLength;\n const upload = await this.beginFileUpload(workspaceId, {\n filename: input.filename,\n contentType: input.contentType,\n sizeBytes,\n ...(input.sha256 !== undefined ? { sha256: input.sha256 } : {}),\n });\n const putResponse = await this.fetchImpl(upload.putUrl, {\n method: \"PUT\",\n // The backend's requiredHeaders already carry the canonical lowercase\n // `content-type` for every storage backend (Azure/S3/GCS). Do NOT also set\n // a `Content-Type` key here: WHATWG Headers treats the two casings as the\n // same header and comma-joins their values (e.g. \"text/plain, text/plain\"),\n // which the object store persists verbatim and COMPLETE then rejects (422),\n // and which breaks S3's presigned-URL signature.\n headers: { ...upload.requiredHeaders },\n body,\n });\n if (!putResponse.ok) {\n throw new OpenGeniApiError(putResponse.status, await safeText(putResponse));\n }\n return await this.completeFileUpload(workspaceId, upload.uploadId);\n }\n\n async getFile(workspaceId: string, fileId: string): Promise<FileAsset> {\n return await this.requestJson<FileAsset>(\"GET\", `/v1/workspaces/${workspaceId}/files/${fileId}`);\n }\n\n /** Mint a short-lived signed download URL for a ready file. */\n async createFileDownloadUrl(workspaceId: string, fileId: string): Promise<FileDownloadUrlResponse> {\n return await this.requestJson<FileDownloadUrlResponse>(\"POST\", `/v1/workspaces/${workspaceId}/files/${fileId}/download-url`);\n }\n\n // --- Documents ----------------------------------------------------------------------\n\n async createDocumentBase(workspaceId: string, request: CreateDocumentBaseRequest): Promise<DocumentBase> {\n return await this.requestJson<DocumentBase>(\"POST\", `/v1/workspaces/${workspaceId}/document-bases`, request);\n }\n\n async listDocumentBases(workspaceId: string): Promise<DocumentBase[]> {\n return await this.requestJson<DocumentBase[]>(\"GET\", `/v1/workspaces/${workspaceId}/document-bases`);\n }\n\n async getDocumentBase(workspaceId: string, baseId: string): Promise<DocumentBase> {\n return await this.requestJson<DocumentBase>(\"GET\", `/v1/workspaces/${workspaceId}/document-bases/${baseId}`);\n }\n\n /** Index an uploaded file into the base. The file must be `ready`. */\n async addDocument(workspaceId: string, baseId: string, request: { fileId: string }): Promise<Document> {\n return await this.requestJson<Document>(\"POST\", `/v1/workspaces/${workspaceId}/document-bases/${baseId}/documents`, request);\n }\n\n async listDocuments(workspaceId: string, baseId: string): Promise<Document[]> {\n return await this.requestJson<Document[]>(\"GET\", `/v1/workspaces/${workspaceId}/document-bases/${baseId}/documents`);\n }\n\n /** Retry indexing for a failed document. */\n async reindexDocument(workspaceId: string, baseId: string, documentId: string): Promise<Document> {\n return await this.requestJson<Document>(\n \"POST\",\n `/v1/workspaces/${workspaceId}/document-bases/${baseId}/documents/${documentId}/reindex`,\n );\n }\n\n async searchDocuments(\n workspaceId: string,\n baseId: string,\n request: { query: string; limit?: number },\n ): Promise<DocumentSearchResponse> {\n return await this.requestJson<DocumentSearchResponse>(\n \"POST\",\n `/v1/workspaces/${workspaceId}/document-bases/${baseId}/search`,\n request,\n );\n }\n\n // --- Capability packs ------------------------------------------------------------------\n\n /** Built-in + registered packs, with the workspace's installations. */\n async listPacks(workspaceId: string): Promise<ListPacksResponse> {\n return await this.requestJson<ListPacksResponse>(\"GET\", `/v1/workspaces/${workspaceId}/packs`);\n }\n\n /** Register (or replace) a workspace-scoped pack from a manifest. */\n async registerPack(workspaceId: string, manifest: RegisterCapabilityPackRequest): Promise<WorkspaceRegisteredPack> {\n return await this.requestJson<WorkspaceRegisteredPack>(\"POST\", `/v1/workspaces/${workspaceId}/packs`, manifest);\n }\n\n async getPack(workspaceId: string, packId: string): Promise<GetPackResponse> {\n return await this.requestJson<GetPackResponse>(\"GET\", `/v1/workspaces/${workspaceId}/packs/${encodeURIComponent(packId)}`);\n }\n\n async enablePack(workspaceId: string, packId: string, request: EnablePackRequest = {}): Promise<PackInstallation> {\n return await this.requestJson<PackInstallation>(\n \"POST\",\n `/v1/workspaces/${workspaceId}/packs/${encodeURIComponent(packId)}/enable`,\n request,\n );\n }\n\n /** Unregister a workspace-scoped pack (built-in packs cannot be deleted). */\n async deletePack(workspaceId: string, packId: string): Promise<void> {\n await this.requestVoid(\"DELETE\", `/v1/workspaces/${workspaceId}/packs/${encodeURIComponent(packId)}`);\n }\n\n async listPackInstallations(workspaceId: string): Promise<PackInstallation[]> {\n return await this.requestJson<PackInstallation[]>(\"GET\", `/v1/workspaces/${workspaceId}/packs/installations`);\n }\n\n // --- Capabilities -------------------------------------------------------------------------\n\n async listCapabilities(workspaceId: string): Promise<CapabilityCatalogResponse> {\n return await this.requestJson<CapabilityCatalogResponse>(\"GET\", `/v1/workspaces/${workspaceId}/capabilities`);\n }\n\n /** Add a manual capability catalog item (e.g. a remote MCP server). */\n async createCapability(workspaceId: string, request: CreateCapabilityCatalogItemRequest): Promise<CapabilityCatalogItem> {\n return await this.requestJson<CapabilityCatalogItem>(\"POST\", `/v1/workspaces/${workspaceId}/capabilities`, request);\n }\n\n async enableCapability(\n workspaceId: string,\n capabilityId: string,\n request: EnableCapabilityRequest = {},\n ): Promise<CapabilityInstallation> {\n return await this.requestJson<CapabilityInstallation>(\n \"POST\",\n `/v1/workspaces/${workspaceId}/capabilities/${encodeURIComponent(capabilityId)}/enable`,\n request,\n );\n }\n\n async disableCapability(workspaceId: string, capabilityId: string): Promise<CapabilityInstallation> {\n return await this.requestJson<CapabilityInstallation>(\n \"POST\",\n `/v1/workspaces/${workspaceId}/capabilities/${encodeURIComponent(capabilityId)}/disable`,\n );\n }\n\n /** Search the official MCP registry for installable capabilities. */\n async discoverMcpCapabilities(\n workspaceId: string,\n options: { query?: string; limit?: number } = {},\n ): Promise<DiscoverMcpCapabilitiesResponse> {\n return await this.requestJson<DiscoverMcpCapabilitiesResponse>(\n \"GET\",\n `/v1/workspaces/${workspaceId}/capabilities/discovery/mcp-registry`,\n undefined,\n {\n ...(options.query !== undefined ? { query: options.query } : {}),\n ...(options.limit !== undefined ? { limit: String(options.limit) } : {}),\n },\n );\n }\n\n // --- GitHub ----------------------------------------------------------------------------------\n\n /** GitHub App configuration status + a signed install URL when configured. */\n async getGitHubApp(workspaceId: string): Promise<GitHubAppInfo> {\n return await this.requestJson<GitHubAppInfo>(\"GET\", `/v1/workspaces/${workspaceId}/github/app`);\n }\n\n /**\n * Browser entry point that plants the CSRF cookie and forwards to GitHub's\n * install page. Open this in a browser (it redirects); `state` comes from\n * `getGitHubApp().installUrl` or a github_connect_link tool.\n */\n githubConnectUrl(workspaceId: string, state: string): string {\n return this.url(`/v1/workspaces/${workspaceId}/github/connect`, { state });\n }\n\n async listGitHubRepositories(workspaceId: string): Promise<GitHubRepositoriesResponse> {\n return await this.requestJson<GitHubRepositoriesResponse>(\"GET\", `/v1/workspaces/${workspaceId}/github/repositories`);\n }\n\n /** Re-sync the installation's repository list from GitHub. */\n async syncGitHubRepositories(workspaceId: string): Promise<GitHubRepositoriesResponse> {\n return await this.requestJson<GitHubRepositoriesResponse>(\"POST\", `/v1/workspaces/${workspaceId}/github/repositories/sync`);\n }\n\n /** Build a GitHub App manifest + the GitHub URL to submit it to. */\n async createGitHubAppManifest(\n workspaceId: string,\n request: CreateGitHubAppManifestRequest = {},\n ): Promise<CreateGitHubAppManifestResponse> {\n return await this.requestJson<CreateGitHubAppManifestResponse>(\n \"POST\",\n `/v1/workspaces/${workspaceId}/github/app-manifest`,\n request,\n );\n }\n\n // --- API keys ----------------------------------------------------------------------------------\n\n async listApiKeys(workspaceId: string): Promise<ApiKey[]> {\n const response = await this.requestJson<ListApiKeysResponse>(\"GET\", `/v1/workspaces/${workspaceId}/api-keys`);\n return response.apiKeys;\n }\n\n /** The returned `token` is shown once; only its prefix is stored. */\n async createApiKey(workspaceId: string, request: CreateApiKeyRequest): Promise<CreateApiKeyResponse> {\n return await this.requestJson<CreateApiKeyResponse>(\"POST\", `/v1/workspaces/${workspaceId}/api-keys`, request);\n }\n\n /** Revoke an API key. Returns the revoked key. */\n async deleteApiKey(workspaceId: string, apiKeyId: string): Promise<ApiKey> {\n return await this.requestJson<ApiKey>(\"DELETE\", `/v1/workspaces/${workspaceId}/api-keys/${apiKeyId}`);\n }\n\n // --- Billing (account-scoped) --------------------------------------------------------------------\n\n async getBilling(options: { accountId?: string } = {}): Promise<BillingSummary> {\n return await this.requestJson<BillingSummary>(\"GET\", \"/v1/billing\", undefined, {\n ...(options.accountId !== undefined ? { accountId: options.accountId } : {}),\n });\n }\n\n async getBillingUsage(options: { accountId?: string; workspaceId?: string } = {}): Promise<BillingUsageResponse> {\n return await this.requestJson<BillingUsageResponse>(\"GET\", \"/v1/billing/usage\", undefined, {\n ...(options.accountId !== undefined ? { accountId: options.accountId } : {}),\n ...(options.workspaceId !== undefined ? { workspaceId: options.workspaceId } : {}),\n });\n }\n\n async getBillingEntitlements(options: { accountId?: string } = {}): Promise<BillingEntitlementsResponse> {\n return await this.requestJson<BillingEntitlementsResponse>(\"GET\", \"/v1/billing/entitlements\", undefined, {\n ...(options.accountId !== undefined ? { accountId: options.accountId } : {}),\n });\n }\n\n /** Start a Stripe checkout for prepaid credits. */\n async createBillingCheckout(request: CreateCheckoutRequest): Promise<CreateCheckoutResponse> {\n return await this.requestJson<CreateCheckoutResponse>(\"POST\", \"/v1/billing/checkout\", request);\n }\n\n // --- Internals -------------------------------------------------------------\n\n private headers(): Record<string, string> {\n const extra = typeof this.options.headers === \"function\" ? this.options.headers() : this.options.headers;\n return {\n ...(this.options.apiKey ? { Authorization: `Bearer ${this.options.apiKey}` } : {}),\n ...extra,\n };\n }\n\n private url(path: string, query: Record<string, string> = {}): string {\n const params = new URLSearchParams(query).toString();\n return `${this.baseUrl}${path}${params ? `?${params}` : \"\"}`;\n }\n\n private async requestJson<T>(method: string, path: string, body?: unknown, query: Record<string, string> = {}): Promise<T> {\n const response = await this.fetchImpl(this.url(path, query), {\n method,\n headers: {\n ...this.headers(),\n Accept: \"application/json\",\n ...(body !== undefined ? { \"Content-Type\": \"application/json\" } : {}),\n },\n ...(body !== undefined ? { body: JSON.stringify(body) } : {}),\n });\n if (!response.ok) {\n throw new OpenGeniApiError(response.status, await safeText(response));\n }\n return (await response.json()) as T;\n }\n\n /** Like `requestJson` for endpoints that respond with no body (204). */\n private async requestVoid(method: string, path: string, body?: unknown): Promise<void> {\n const response = await this.fetchImpl(this.url(path), {\n method,\n headers: {\n ...this.headers(),\n Accept: \"application/json\",\n ...(body !== undefined ? { \"Content-Type\": \"application/json\" } : {}),\n },\n ...(body !== undefined ? { body: JSON.stringify(body) } : {}),\n });\n if (!response.ok) {\n throw new OpenGeniApiError(response.status, await safeText(response));\n }\n }\n}\n\nasync function safeText(response: Response): Promise<string> {\n try {\n return await response.text();\n } catch {\n return \"\";\n }\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","import type { OpenGeniClient } from \"./client\";\nimport type { StreamSessionEventsOptions } from \"./stream\";\nimport type { SessionEvent } from \"./types\";\n\n/**\n * Proxy-through-your-own-API helpers.\n *\n * The intended pattern: a customer's server consumes the OpenGeni event\n * stream with its own API key (`client.streamEvents(...)`) and re-emits it to\n * its browser clients over its own authenticated endpoint — the OpenGeni key\n * never reaches the browser. The re-emitted wire format is identical to\n * OpenGeni's own SSE stream (`id: <sequence>`, `event: <type>`,\n * `data: <event JSON>`), so the browser side can consume it with this same\n * SDK's streaming core (or a plain `EventSource`), including resume via\n * `?after=` / `Last-Event-ID`.\n */\n\n/** Format one event exactly as OpenGeni's API emits it over SSE. */\nexport function formatSseEvent(event: SessionEvent): string {\n return `id: ${event.sequence}\\nevent: ${event.type}\\ndata: ${JSON.stringify(event)}\\n\\n`;\n}\n\nexport type SseReStreamOptions = {\n /**\n * Emit `: ping` comment lines at this interval, keeping intermediaries from\n * idling the connection out. Disabled when omitted.\n */\n heartbeatMs?: number;\n /**\n * Called when the downstream consumer cancels (e.g. the browser\n * disconnected). Use it to abort the upstream OpenGeni stream — an async\n * iterator that is mid-`await` cannot be interrupted by `return()` alone.\n */\n onCancel?: () => void;\n};\n\n/**\n * Re-emit a stream of session events as an SSE byte stream. Pull-based, so\n * upstream consumption follows downstream demand; cancelling the returned\n * stream fires `onCancel` and ends the upstream iterator.\n */\nexport function sessionEventsToSseStream(\n events: AsyncIterable<SessionEvent>,\n options: SseReStreamOptions = {},\n): ReadableStream<Uint8Array> {\n const encoder = new TextEncoder();\n const iterator = events[Symbol.asyncIterator]();\n let heartbeat: ReturnType<typeof setInterval> | undefined;\n let cancelled = false;\n\n const stopHeartbeat = (): void => {\n if (heartbeat !== undefined) {\n clearInterval(heartbeat);\n heartbeat = undefined;\n }\n };\n\n return new ReadableStream<Uint8Array>({\n start: (controller) => {\n if (options.heartbeatMs !== undefined) {\n heartbeat = setInterval(() => {\n try {\n controller.enqueue(encoder.encode(\": ping\\n\\n\"));\n } catch {\n stopHeartbeat();\n }\n }, options.heartbeatMs);\n }\n },\n pull: async (controller) => {\n let result: IteratorResult<SessionEvent, unknown>;\n try {\n result = await iterator.next();\n } catch (error) {\n stopHeartbeat();\n throw error;\n }\n if (cancelled) {\n return;\n }\n if (result.done) {\n stopHeartbeat();\n controller.close();\n return;\n }\n controller.enqueue(encoder.encode(formatSseEvent(result.value)));\n },\n cancel: () => {\n cancelled = true;\n stopHeartbeat();\n options.onCancel?.();\n // Fire-and-forget: if the iterator is suspended mid-await, return()\n // settles only after onCancel unblocks it; cancel must not hang on it.\n void Promise.resolve(iterator.return?.(undefined)).then(\n () => undefined,\n () => undefined,\n );\n },\n });\n}\n\n/** Wrap an event stream in a ready-to-return SSE `Response`. */\nexport function sessionEventsToSseResponse(\n events: AsyncIterable<SessionEvent>,\n options: SseReStreamOptions = {},\n): Response {\n return new Response(sessionEventsToSseStream(events, options), {\n headers: {\n \"Content-Type\": \"text/event-stream; charset=utf-8\",\n \"Cache-Control\": \"no-cache, no-transform\",\n Connection: \"keep-alive\",\n },\n });\n}\n\n/**\n * Read the resume cursor a reconnecting SSE client sent: the `after` query\n * parameter, or the standard `Last-Event-ID` header (the re-emitted stream\n * sets `id:` to the sequence). Returns 0 (full replay) when absent.\n */\nexport function resumeSequenceFromRequest(request: Request): number {\n const url = new URL(request.url);\n const raw = url.searchParams.get(\"after\") ?? request.headers.get(\"Last-Event-ID\");\n const parsed = raw === null ? 0 : Number(raw);\n return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : 0;\n}\n\nexport type ProxySessionEventStreamOptions = Omit<StreamSessionEventsOptions, \"after\"> & {\n /**\n * Resume cursor. Pass a number, or the incoming browser `Request` to\n * honor its `?after=` / `Last-Event-ID` automatically.\n */\n after?: number | Request;\n /** See {@link SseReStreamOptions.heartbeatMs}. */\n heartbeatMs?: number;\n};\n\n/**\n * One-call proxy: consume the OpenGeni stream server-side and return an SSE\n * `Response` for your own browser clients. Works anywhere WHATWG `Response`\n * is the handler return type (Hono, Next.js route handlers, Bun.serve,\n * Cloudflare Workers, ...).\n *\n * The upstream OpenGeni connection is torn down when the downstream client\n * disconnects, and also when `options.signal` (e.g. the incoming request's\n * signal) aborts.\n */\nexport function proxySessionEventStream(\n client: OpenGeniClient,\n workspaceId: string,\n sessionId: string,\n options: ProxySessionEventStreamOptions = {},\n): Response {\n const { after, heartbeatMs, signal, ...streamOptions } = options;\n const upstream = new AbortController();\n if (signal?.aborted) {\n upstream.abort();\n } else {\n signal?.addEventListener(\"abort\", () => upstream.abort(), { once: true });\n }\n const resolvedAfter = after instanceof Request ? resumeSequenceFromRequest(after) : after ?? 0;\n const events = client.streamEvents(workspaceId, sessionId, {\n ...streamOptions,\n after: resolvedAfter,\n signal: upstream.signal,\n });\n return sessionEventsToSseResponse(events, {\n ...(heartbeatMs !== undefined ? { heartbeatMs } : {}),\n onCancel: () => upstream.abort(),\n });\n}\n","// Hand-written mirrors of the public wire shapes in `@opengeni/contracts`.\n// The SDK keeps zero runtime dependencies so it stays framework-agnostic and\n// publishable on its own; `test/contract-parity.test.ts` pins these types to\n// the contracts package so drift fails the gate instead of shipping.\n\nexport type SessionStatus =\n | \"queued\"\n | \"running\"\n | \"idle\"\n | \"requires_action\"\n | \"failed\"\n | \"cancelled\";\n\nexport type SandboxBackend = \"docker\" | \"modal\" | \"local\" | \"none\";\n\nexport type ReasoningEffort = \"none\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\";\n\nexport type RepositoryResourceRef = {\n kind: \"repository\";\n uri: string;\n ref: string;\n mountPath?: string | undefined;\n subpath?: string | undefined;\n githubInstallationId?: number | undefined;\n githubRepositoryId?: number | undefined;\n};\n\nexport type FileResourceRef = {\n kind: \"file\";\n fileId: string;\n mountPath?: string | undefined;\n};\n\nexport type ResourceRef = RepositoryResourceRef | FileResourceRef;\n\nexport type ToolRef = {\n kind: \"mcp\";\n id: string;\n};\n\nexport type GoalSpec = {\n text: string;\n successCriteria?: string | undefined;\n maxAutoContinuations?: number | undefined;\n};\n\nexport type Session = {\n id: string;\n workspaceId: string;\n accountId: string;\n status: SessionStatus;\n initialMessage: string;\n resources: ResourceRef[];\n tools: ToolRef[];\n metadata: Record<string, unknown>;\n model: string;\n sandboxBackend: SandboxBackend;\n environmentId: string | null;\n firstPartyMcpPermissions: string[] | null;\n createIdempotencyKey: string | null;\n temporalWorkflowId: string | null;\n activeTurnId: string | null;\n lastSequence: number;\n createdAt: string;\n updatedAt: string;\n};\n\nexport type SessionTurnStatus =\n | \"queued\"\n | \"running\"\n | \"requires_action\"\n | \"completed\"\n | \"failed\"\n | \"cancelled\";\n\nexport type SessionTurnSource = \"user\" | \"scheduled_task\" | \"api\" | \"goal\";\n\nexport type SessionTurn = {\n id: string;\n workspaceId: string;\n sessionId: string;\n triggerEventId: string;\n temporalWorkflowId: string;\n status: SessionTurnStatus;\n source: SessionTurnSource;\n position: number;\n prompt: string;\n resources: ResourceRef[];\n tools: ToolRef[];\n model: string;\n reasoningEffort: ReasoningEffort;\n sandboxBackend: SandboxBackend;\n metadata: Record<string, unknown>;\n startedAt: string | null;\n finishedAt: string | null;\n createdAt: string;\n updatedAt: string;\n};\n\nexport const SESSION_EVENT_TYPES = [\n \"session.created\",\n \"session.status.changed\",\n \"session.requiresAction\",\n \"session.context.compacted\",\n \"session.context.cleared\",\n \"user.message\",\n \"user.interrupt\",\n \"user.approvalDecision\",\n \"turn.queued\",\n \"turn.updated\",\n \"turn.started\",\n \"turn.completed\",\n \"turn.failed\",\n \"turn.cancelled\",\n \"turn.preempted\",\n \"agent.message.delta\",\n \"agent.message.completed\",\n \"agent.reasoning.delta\",\n \"agent.toolCall.created\",\n \"agent.toolCall.output\",\n \"agent.updated\",\n \"sandbox.operation.started\",\n \"sandbox.operation.completed\",\n \"sandbox.operation.failed\",\n \"sandbox.command.output.delta\",\n \"artifact.created\",\n \"goal.set\",\n \"goal.updated\",\n \"goal.completed\",\n \"goal.paused\",\n \"goal.resumed\",\n \"goal.continuation\",\n] as const;\n\nexport type KnownSessionEventType = (typeof SESSION_EVENT_TYPES)[number];\n\n/**\n * Event types the SDK knows about today, kept open so a newer OpenGeni server\n * can introduce event types without breaking older SDK consumers.\n */\nexport type SessionEventType = KnownSessionEventType | (string & {});\n\nexport type SessionEvent = {\n id: string;\n workspaceId: string;\n sessionId: string;\n /** Per-session sequence number: positive, contiguous, strictly increasing. */\n sequence: number;\n type: SessionEventType;\n payload: unknown;\n occurredAt: string;\n clientEventId?: string | null | undefined;\n turnId?: string | null | undefined;\n};\n\n// Payload shapes for the high-traffic event types. `SessionEvent.payload` is\n// `unknown` on the wire; these are the documented shapes producers emit today.\nexport type AgentTextDeltaPayload = { text: string };\nexport type AgentMessageCompletedPayload = { text: string };\nexport type AgentToolCallCreatedPayload = {\n id: string | null;\n name: string;\n arguments: unknown;\n raw?: unknown | undefined;\n};\nexport type AgentToolCallOutputPayload = { id: string | null; output: unknown };\nexport type SessionStatusChangedPayload = { status: SessionStatus };\n\nexport type ScheduledTaskStatus = \"active\" | \"paused\";\n\nexport type ScheduledTaskRunMode = \"new_session_per_run\" | \"reusable_session\";\n\nexport type ScheduledTaskOverlapPolicy = \"allow_concurrent\" | \"skip\" | \"buffer_one\";\n\nexport type ScheduledTaskDayOfWeek =\n | \"SUNDAY\"\n | \"MONDAY\"\n | \"TUESDAY\"\n | \"WEDNESDAY\"\n | \"THURSDAY\"\n | \"FRIDAY\"\n | \"SATURDAY\";\n\nexport type ScheduledTaskScheduleSpec =\n | { type: \"once\"; runAt: string; timeZone: string }\n | {\n type: \"interval\";\n everySeconds: number;\n startAt?: string | undefined;\n endAt?: string | undefined;\n }\n | {\n type: \"calendar\";\n timeZone: string;\n hour: number;\n minute: number;\n daysOfWeek?: ScheduledTaskDayOfWeek[] | undefined;\n };\n\nexport type ScheduledTaskAgentConfig = {\n prompt: string;\n resources: ResourceRef[];\n tools: ToolRef[];\n metadata: Record<string, unknown>;\n model?: string | undefined;\n reasoningEffort?: ReasoningEffort | undefined;\n sandboxBackend?: SandboxBackend | undefined;\n goal?: GoalSpec | undefined;\n};\n\nexport type ScheduledTask = {\n id: string;\n accountId: string;\n workspaceId: string;\n name: string;\n status: ScheduledTaskStatus;\n schedule: ScheduledTaskScheduleSpec;\n temporalScheduleId: string;\n runMode: ScheduledTaskRunMode;\n overlapPolicy: ScheduledTaskOverlapPolicy;\n agentConfig: ScheduledTaskAgentConfig;\n reusableSessionId: string | null;\n environmentId: string | null;\n metadata: Record<string, unknown>;\n createdAt: string;\n updatedAt: string;\n};\n\nexport type CreateSessionRequest = {\n initialMessage: string;\n resources?: ResourceRef[] | undefined;\n tools?: ToolRef[] | undefined;\n metadata?: Record<string, unknown> | undefined;\n model?: string | undefined;\n reasoningEffort?: ReasoningEffort | undefined;\n sandboxBackend?: SandboxBackend | undefined;\n environmentId?: string | undefined;\n goal?: GoalSpec | undefined;\n clientEventId?: string | undefined;\n // Workspace-scoped CREATE idempotency key: forward a STABLE value to make a\n // double-submit/retry of the same logical create collapse to one session.\n // Distinct from the per-call clientEventId.\n idempotencyKey?: string | undefined;\n firstPartyMcpPermissions?: string[] | undefined;\n};\n\n// --- Access, workspaces, API keys -------------------------------------------\n\nexport const KNOWN_PERMISSIONS = [\n \"account:read\",\n \"account:admin\",\n \"members:manage\",\n \"workspace:create\",\n \"billing:read\",\n \"billing:manage\",\n \"workspace:read\",\n \"workspace:admin\",\n \"sessions:create\",\n \"sessions:read\",\n \"sessions:control\",\n \"files:upload\",\n \"files:read\",\n \"documents:manage\",\n \"documents:search\",\n \"scheduled_tasks:manage\",\n \"scheduled_tasks:run\",\n \"github:manage\",\n \"github:use\",\n \"api_keys:manage\",\n \"environments:manage\",\n \"environments:use\",\n \"goals:manage\",\n] as const;\n\nexport type KnownPermission = (typeof KNOWN_PERMISSIONS)[number];\n\n/**\n * Permissions the SDK knows about today, kept open so a newer OpenGeni server\n * can introduce permissions without breaking older SDK consumers.\n */\nexport type Permission = KnownPermission | (string & {});\n\nexport type ProductAccessMode = \"local\" | \"configured\" | \"managed\";\n\nexport type AccountRole = \"owner\" | \"admin\" | \"member\";\n\nexport type AccountGrant = {\n accountId: string;\n subjectId: string;\n subjectLabel?: string | undefined;\n role?: AccountRole | undefined;\n permissions: Permission[];\n metadata?: Record<string, unknown> | undefined;\n};\n\nexport type AccessGrant = {\n workspaceId: string;\n accountId: string;\n subjectId: string;\n subjectLabel?: string | undefined;\n permissions: Permission[];\n metadata?: Record<string, unknown> | undefined;\n};\n\nexport type AccessContext = {\n mode: ProductAccessMode;\n subjectId: string;\n subjectLabel?: string | undefined;\n accountGrants: AccountGrant[];\n workspaceGrants: AccessGrant[];\n defaultAccountId: string | null;\n defaultWorkspaceId: string | null;\n};\n\nexport type Workspace = {\n id: string;\n accountId: string;\n name: string;\n slug: string | null;\n externalSource: string | null;\n externalId: string | null;\n agentInstructions: string | null;\n createdAt: string;\n updatedAt: string;\n};\n\nexport type CreateWorkspaceRequest = {\n accountId?: string | undefined;\n name: string;\n slug?: string | undefined;\n externalSource?: string | undefined;\n externalId?: string | undefined;\n agentInstructions?: string | null | undefined;\n};\n\nexport type UpdateWorkspaceRequest = {\n name?: string | undefined;\n slug?: string | null | undefined;\n agentInstructions?: string | null | undefined;\n};\n\nexport type ApiKey = {\n id: string;\n accountId: string;\n workspaceId: string | null;\n name: string;\n prefix: string;\n permissions: Permission[];\n expiresAt: string | null;\n revokedAt: string | null;\n lastUsedAt: string | null;\n createdAt: string;\n updatedAt: string;\n};\n\nexport type CreateApiKeyRequest = {\n name: string;\n permissions: Permission[];\n expiresAt?: string | undefined;\n};\n\nexport type CreateApiKeyResponse = {\n apiKey: ApiKey;\n /** The full secret token — shown once at creation, never returned again. */\n token: string;\n};\n\nexport type ListApiKeysResponse = {\n apiKeys: ApiKey[];\n};\n\n// --- Goals -------------------------------------------------------------------\n\nexport type SessionGoalStatus = \"active\" | \"paused\" | \"completed\";\n\nexport type SessionGoalCreatedBy = \"api\" | \"agent\" | \"scheduled_task\";\n\nexport type SessionGoal = {\n id: string;\n accountId: string;\n workspaceId: string;\n sessionId: string;\n status: SessionGoalStatus;\n text: string;\n successCriteria: string | null;\n evidence: string | null;\n rationale: string | null;\n pausedReason: string | null;\n createdBy: SessionGoalCreatedBy;\n version: number;\n autoContinuations: number;\n noProgressStreak: number;\n maxAutoContinuations: number | null;\n metadata: Record<string, unknown>;\n createdAt: string;\n updatedAt: string;\n};\n\nexport type UpdateSessionGoalRequest = {\n status: \"paused\" | \"active\";\n rationale?: string | undefined;\n};\n\n// --- Operator context controls (/clear, /compact) ----------------------------\n\n/** Outcome of a manual /compact trigger. */\nexport type CompactSessionContextResult = {\n /**\n * queued: a client-side (Azure) compaction will run before the next turn.\n * noop: nothing to do (server-managed provider, mode off, or no history).\n */\n status: \"queued\" | \"noop\";\n message: string;\n};\n\n// --- Turn queue --------------------------------------------------------------\n\nexport type UpdateSessionTurnRequest = {\n prompt?: string | undefined;\n resources?: ResourceRef[] | undefined;\n tools?: ToolRef[] | undefined;\n model?: string | undefined;\n reasoningEffort?: ReasoningEffort | undefined;\n sandboxBackend?: SandboxBackend | undefined;\n metadata?: Record<string, unknown> | undefined;\n};\n\n// --- Scheduled tasks: requests + runs ----------------------------------------\n\n/** Input shape for agent config on create/update (server applies defaults). */\nexport type ScheduledTaskAgentConfigInput = {\n prompt: string;\n resources?: ResourceRef[] | undefined;\n tools?: ToolRef[] | undefined;\n metadata?: Record<string, unknown> | undefined;\n model?: string | undefined;\n reasoningEffort?: ReasoningEffort | undefined;\n sandboxBackend?: SandboxBackend | undefined;\n goal?: GoalSpec | undefined;\n};\n\nexport type CreateScheduledTaskRequest = {\n name: string;\n schedule: ScheduledTaskScheduleSpec;\n runMode?: ScheduledTaskRunMode | undefined;\n overlapPolicy?: ScheduledTaskOverlapPolicy | undefined;\n agentConfig: ScheduledTaskAgentConfigInput;\n status?: ScheduledTaskStatus | undefined;\n environmentId?: string | null | undefined;\n metadata?: Record<string, unknown> | undefined;\n};\n\nexport type UpdateScheduledTaskRequest = {\n name?: string | undefined;\n schedule?: ScheduledTaskScheduleSpec | undefined;\n runMode?: ScheduledTaskRunMode | undefined;\n overlapPolicy?: ScheduledTaskOverlapPolicy | undefined;\n agentConfig?: ScheduledTaskAgentConfigInput | undefined;\n status?: ScheduledTaskStatus | undefined;\n environmentId?: string | null | undefined;\n metadata?: Record<string, unknown> | undefined;\n};\n\nexport type ScheduledTaskRunStatus = \"queued\" | \"dispatched\" | \"failed\";\n\nexport type ScheduledTaskTriggerType = \"scheduled\" | \"manual\";\n\nexport type ScheduledTaskRun = {\n id: string;\n accountId: string;\n workspaceId: string;\n taskId: string;\n status: ScheduledTaskRunStatus;\n triggerType: ScheduledTaskTriggerType;\n scheduledAt: string | null;\n firedAt: string;\n sessionId: string | null;\n triggerEventId: string | null;\n error: string | null;\n createdAt: string;\n updatedAt: string;\n};\n\n// --- Environments -------------------------------------------------------------\n\n/**\n * Variable values are write-only by design: the API never returns a value, so\n * reads expose name + version metadata only. Values are decrypted exclusively\n * inside the worker at sandbox materialization time.\n */\nexport type WorkspaceEnvironmentVariableMetadata = {\n name: string;\n version: number;\n createdAt: string;\n updatedAt: string;\n};\n\nexport type WorkspaceEnvironment = {\n id: string;\n accountId: string;\n workspaceId: string;\n name: string;\n description: string | null;\n variables: WorkspaceEnvironmentVariableMetadata[];\n createdAt: string;\n updatedAt: string;\n};\n\nexport type CreateWorkspaceEnvironmentRequest = {\n name: string;\n description?: string | undefined;\n /** Initial variables. Values are write-only: they never come back on reads. */\n variables?: { name: string; value: string }[] | undefined;\n};\n\nexport type UpdateWorkspaceEnvironmentRequest = {\n name?: string | undefined;\n description?: string | null | undefined;\n};\n\n// --- Files ---------------------------------------------------------------------\n\nexport type FileStatus = \"pending_upload\" | \"ready\" | \"failed\" | \"expired\" | \"deleted\";\n\nexport type FileAsset = {\n id: string;\n workspaceId: string;\n status: FileStatus;\n filename: string;\n safeFilename: string;\n contentType: string;\n sizeBytes: number;\n sha256: string | null;\n bucket: string;\n objectKey: string;\n createdAt: string;\n updatedAt: string;\n};\n\nexport type CreateFileUploadRequest = {\n filename: string;\n contentType: string;\n sizeBytes: number;\n sha256?: string | undefined;\n};\n\nexport type CreateFileUploadResponse = {\n fileId: string;\n uploadId: string;\n /** Pre-signed PUT URL for the file bytes (direct to object storage). */\n putUrl: string;\n /** Headers that MUST be sent with the PUT for the signature to validate. */\n requiredHeaders: Record<string, string>;\n expiresAt: string;\n maxSizeBytes: number;\n};\n\nexport type CompleteFileUploadResponse = {\n file: FileAsset;\n};\n\nexport type FileDownloadUrlResponse = {\n url: string;\n expiresAt: string;\n};\n\n/** Bytes accepted by the `uploadFile` helper. */\nexport type FileUploadData = Blob | ArrayBuffer | Uint8Array | string;\n\nexport type UploadFileInput = {\n filename: string;\n contentType: string;\n data: FileUploadData;\n sha256?: string | undefined;\n};\n\n// --- Documents -------------------------------------------------------------------\n\nexport type DocumentStatus = \"queued\" | \"indexing\" | \"ready\" | \"failed\";\n\nexport type DocumentBase = {\n id: string;\n workspaceId: string;\n name: string;\n description: string | null;\n createdAt: string;\n updatedAt: string;\n};\n\nexport type Document = {\n id: string;\n workspaceId: string;\n baseId: string;\n fileId: string;\n status: DocumentStatus;\n title: string;\n parser: string;\n chunkCount: number;\n error: string | null;\n createdAt: string;\n updatedAt: string;\n};\n\nexport type DocumentSearchResult = {\n chunkId: string;\n workspaceId: string;\n documentId: string;\n baseId: string;\n fileId: string;\n title: string;\n text: string;\n score: number;\n chunkIndex: number;\n metadata: Record<string, unknown>;\n};\n\nexport type CreateDocumentBaseRequest = {\n name: string;\n description?: string | undefined;\n};\n\nexport type DocumentSearchRequest = {\n query: string;\n limit?: number | undefined;\n};\n\nexport type DocumentSearchResponse = {\n results: DocumentSearchResult[];\n};\n\n// --- Capability packs ---------------------------------------------------------\n\nexport type CapabilityPackConnectorAuthModel =\n | \"oauth2_authorization_code_pkce\"\n | \"oauth2_authorization_code\"\n | \"api_key\"\n | \"credential_ref\";\n\nexport type CapabilityPackConnector = {\n id: string;\n name: string;\n category: string;\n authModel: CapabilityPackConnectorAuthModel;\n providers: string[];\n scopes: string[];\n required: boolean;\n metadata: Record<string, unknown>;\n};\n\nexport type CapabilityPackKnowledge = {\n type: \"document_base\";\n id: string;\n name: string;\n description: string | null;\n required: boolean;\n};\n\nexport type CapabilityPackScheduledTaskTemplate = {\n id: string;\n name: string;\n description: string;\n defaultSchedule: ScheduledTaskScheduleSpec;\n defaultRunMode: ScheduledTaskRunMode;\n defaultOverlapPolicy: ScheduledTaskOverlapPolicy;\n prompt?: string | undefined;\n};\n\nexport type CapabilityPackSkillFile = {\n path: string;\n content: string;\n};\n\nexport type CapabilityPackSkill = {\n name: string;\n description?: string | undefined;\n files: CapabilityPackSkillFile[];\n};\n\nexport type CapabilityPackEnvironmentSpec = {\n description: string;\n requiredVariables: string[];\n required: boolean;\n};\n\nexport type CapabilityPack = {\n id: string;\n name: string;\n description: string;\n role: string;\n category: string;\n version: string;\n sandboxImage?: string | undefined;\n skills: CapabilityPackSkill[];\n tools: ToolRef[];\n connectors: CapabilityPackConnector[];\n knowledge: CapabilityPackKnowledge[];\n scheduledTaskTemplates: CapabilityPackScheduledTaskTemplate[];\n environment?: CapabilityPackEnvironmentSpec | undefined;\n metadata: Record<string, unknown>;\n};\n\n/** Input shape for registering a pack manifest (server applies defaults). */\nexport type RegisterCapabilityPackRequest = {\n id: string;\n name: string;\n description: string;\n role: string;\n category: string;\n version: string;\n sandboxImage?: string | undefined;\n skills?: {\n name: string;\n description?: string | undefined;\n files: CapabilityPackSkillFile[];\n }[] | undefined;\n tools?: ToolRef[] | undefined;\n connectors?: {\n id: string;\n name: string;\n category: string;\n authModel: CapabilityPackConnectorAuthModel;\n providers?: string[] | undefined;\n scopes?: string[] | undefined;\n required?: boolean | undefined;\n metadata?: Record<string, unknown> | undefined;\n }[] | undefined;\n knowledge?: {\n type: \"document_base\";\n id: string;\n name: string;\n description?: string | null | undefined;\n required?: boolean | undefined;\n }[] | undefined;\n scheduledTaskTemplates?: {\n id: string;\n name: string;\n description: string;\n defaultSchedule: ScheduledTaskScheduleSpec;\n defaultRunMode?: ScheduledTaskRunMode | undefined;\n defaultOverlapPolicy?: ScheduledTaskOverlapPolicy | undefined;\n prompt?: string | undefined;\n }[] | undefined;\n environment?: {\n description: string;\n requiredVariables?: string[] | undefined;\n required?: boolean | undefined;\n } | undefined;\n metadata?: Record<string, unknown> | undefined;\n};\n\nexport type WorkspaceRegisteredPack = {\n accountId: string;\n workspaceId: string;\n pack: CapabilityPack;\n createdAt: string;\n updatedAt: string;\n};\n\nexport type PackInstallationStatus = \"active\" | \"disabled\";\n\nexport type PackInstallation = {\n id: string;\n accountId: string;\n workspaceId: string;\n packId: string;\n status: PackInstallationStatus;\n metadata: Record<string, unknown>;\n enabledAt: string;\n updatedAt: string;\n};\n\nexport type EnablePackRequest = {\n environmentId?: string | undefined;\n metadata?: Record<string, unknown> | undefined;\n};\n\nexport type ListPacksResponse = {\n packs: CapabilityPack[];\n installations: PackInstallation[];\n};\n\nexport type GetPackResponse = {\n pack: CapabilityPack;\n installation: PackInstallation | null;\n};\n\n// --- Capabilities ---------------------------------------------------------------\n\nexport type CapabilityKind = \"pack\" | \"mcp\" | \"api\" | \"skill\" | \"plugin\";\n\nexport type CapabilitySource = \"built_in\" | \"configured\" | \"public_registry\" | \"manual\";\n\nexport type CapabilityInstallationStatus = \"active\" | \"disabled\";\n\nexport type CapabilityRuntime = {\n available: boolean;\n mcpServerId?: string | undefined;\n transport?: string | undefined;\n notes: string | null;\n};\n\nexport type CapabilityCatalogItem = {\n id: string;\n accountId?: string | undefined;\n workspaceId?: string | undefined;\n kind: CapabilityKind;\n source: CapabilitySource;\n name: string;\n description: string | null;\n category: string;\n tags: string[];\n homepageUrl: string | null;\n endpointUrl: string | null;\n installUrl: string | null;\n authModel: string | null;\n tools: ToolRef[];\n runtime: CapabilityRuntime;\n enabled: boolean;\n enabledReason: string | null;\n metadata: Record<string, unknown>;\n createdAt?: string | undefined;\n updatedAt?: string | undefined;\n};\n\nexport type CapabilityInstallation = {\n id: string;\n accountId: string;\n workspaceId: string;\n capabilityId: string;\n kind: CapabilityKind;\n status: CapabilityInstallationStatus;\n config: Record<string, unknown>;\n metadata: Record<string, unknown>;\n enabledAt: string;\n updatedAt: string;\n};\n\nexport type CapabilityCatalogResponse = {\n items: CapabilityCatalogItem[];\n installations: CapabilityInstallation[];\n};\n\nexport type CreateCapabilityCatalogItemRequest = {\n id?: string | undefined;\n kind: Exclude<CapabilityKind, \"pack\">;\n source?: CapabilitySource | undefined;\n name: string;\n description?: string | undefined;\n category?: string | undefined;\n tags?: string[] | undefined;\n homepageUrl?: string | undefined;\n endpointUrl?: string | undefined;\n installUrl?: string | undefined;\n authModel?: string | undefined;\n metadata?: Record<string, unknown> | undefined;\n};\n\nexport type EnableCapabilityRequest = {\n config?: Record<string, unknown> | undefined;\n metadata?: Record<string, unknown> | undefined;\n /**\n * Credential headers for remote MCP capabilities. Write-only: encrypted at\n * rest, injected only into the runtime MCP client, never returned by the\n * API (responses expose header names only).\n */\n headers?: Record<string, string> | undefined;\n /**\n * Initial environment attachment for kind=pack capabilities — mirrors the\n * dedicated POST /packs/:id/enable body. Required to enable an\n * environment.required pack through this unified path; ignored otherwise.\n */\n environmentId?: string | undefined;\n};\n\nexport type DiscoverMcpCapabilitiesResponse = {\n items: CapabilityCatalogItem[];\n source: \"official_mcp_registry\";\n sourceUrl: string;\n};\n\n// --- GitHub ---------------------------------------------------------------------\n\nexport type GitHubRepository = {\n id: number;\n installationId: number;\n fullName: string;\n name: string;\n private: boolean;\n htmlUrl: string;\n cloneUrl: string;\n defaultBranch: string;\n accountLogin: string;\n accountType: string | null;\n};\n\nexport type GitHubAppInfo = {\n configured: boolean;\n appId: string | null;\n clientId: string | null;\n appSlug: string | null;\n /** Ready-to-open GitHub install URL (carries the signed state), if configured. */\n installUrl: string | null;\n /** Setting names still missing when `configured` is false. */\n missing: string[];\n};\n\nexport type GitHubRepositoriesResponse = {\n repositories: GitHubRepository[];\n};\n\nexport type CreateGitHubAppManifestRequest = {\n appName?: string | undefined;\n organization?: string | undefined;\n public?: boolean | undefined;\n includeCiPermissions?: boolean | undefined;\n};\n\nexport type CreateGitHubAppManifestResponse = {\n /** GitHub URL to POST the manifest to (personal or organization flow). */\n actionUrl: string;\n state: string;\n manifest: Record<string, unknown>;\n};\n\n// --- Billing --------------------------------------------------------------------\n\nexport type BillingMode = \"disabled\" | \"stripe\";\n\nexport type EntitlementsMode = \"none\" | \"static\" | \"managed\";\n\nexport type BillingBalance = {\n accountId: string;\n balanceMicros: number;\n currency: \"usd\";\n updatedAt: string;\n};\n\nexport const KNOWN_USAGE_EVENT_TYPES = [\n \"agent_run.created\",\n \"agent_run.completed\",\n \"model.tokens\",\n \"model.cost\",\n \"file.uploaded\",\n \"file.deleted\",\n \"document.indexed\",\n \"scheduled_task.fired\",\n \"api_key.request\",\n] as const;\n\nexport type KnownUsageEventType = (typeof KNOWN_USAGE_EVENT_TYPES)[number];\n\nexport type UsageEventType = KnownUsageEventType | (string & {});\n\nexport type UsageEvent = {\n id: string;\n workspaceId: string;\n accountId: string;\n subjectId: string | null;\n eventType: UsageEventType;\n quantity: number;\n unit: string;\n sourceResourceType: string | null;\n sourceResourceId: string | null;\n idempotencyKey: string;\n occurredAt: string;\n recordedAt: string;\n exportedToBillingAt: string | null;\n billingProviderEventId: string | null;\n};\n\nexport type EntitlementValue = boolean | string | number | string[];\n\nexport type Entitlements = Record<string, EntitlementValue>;\n\nexport type BillingSummary = {\n mode: BillingMode;\n balance: BillingBalance;\n};\n\nexport type BillingUsageResponse = {\n balance: BillingBalance;\n usage: UsageEvent[];\n};\n\nexport type BillingEntitlementsResponse = {\n accountId: string;\n mode: EntitlementsMode;\n entitlements: Entitlements;\n};\n\nexport type CreateCheckoutRequest = {\n accountId?: string | undefined;\n /** USD amount with cent precision (server enforces min/max). */\n amountUsd: number;\n successUrl?: string | undefined;\n cancelUrl?: string | undefined;\n};\n\nexport type CreateCheckoutResponse = {\n checkoutSessionId: string;\n url: string;\n};\n\nexport type UserMessageEventInput = {\n type: \"user.message\";\n clientEventId?: string | undefined;\n payload: {\n text: string;\n resources?: ResourceRef[] | undefined;\n tools?: ToolRef[] | undefined;\n model?: string | undefined;\n reasoningEffort?: ReasoningEffort | undefined;\n };\n};\n\nexport type UserInterruptEventInput = {\n type: \"user.interrupt\";\n clientEventId?: string | undefined;\n payload?: { reason?: string | undefined } | undefined;\n};\n\nexport type UserApprovalDecisionEventInput = {\n type: \"user.approvalDecision\";\n clientEventId?: string | undefined;\n payload: {\n approvalId: string;\n decision: \"approve\" | \"reject\";\n message?: string | undefined;\n };\n};\n\n/** Control/user events a client may POST to a session's event log. */\nexport type ClientSessionEventInput =\n | UserMessageEventInput\n | UserInterruptEventInput\n | UserApprovalDecisionEventInput;\n"],"mappings":"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;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAoHO,IAAM,oBAAoB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAypBO,IAAM,0BAA0B;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/errors.ts","../src/sse.ts","../src/stream.ts","../src/client.ts","../src/proxy.ts","../src/types.ts"],"sourcesContent":["/** Error for a non-2xx OpenGeni API response. */\nexport class OpenGeniApiError extends Error {\n readonly status: number;\n readonly body: string;\n\n constructor(status: number, body: string) {\n super(`OpenGeni API ${status}: ${body || \"(empty body)\"}`);\n this.name = \"OpenGeniApiError\";\n this.status = status;\n this.body = body;\n }\n}\n\n/** Error for an unrecoverable event-stream condition (not a transient drop). */\nexport class OpenGeniStreamError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"OpenGeniStreamError\";\n }\n}\n\nexport function isAbortError(error: unknown): boolean {\n return (\n (error instanceof DOMException && error.name === \"AbortError\") ||\n (error instanceof Error && error.name === \"AbortError\")\n );\n}\n\n/**\n * Transient conditions worth a reconnect: network-level failures (`fetch`\n * rejects with `TypeError`) and HTTP statuses that signal a temporary server\n * or contention condition. Auth/validation failures (401/403/404/...) are\n * permanent and surface to the caller instead.\n */\nexport function isRetryableStreamError(error: unknown): boolean {\n if (error instanceof OpenGeniApiError) {\n return error.status === 408 || error.status === 409 || error.status === 425 || error.status === 429 || error.status >= 500;\n }\n return error instanceof TypeError;\n}\n","/**\n * Minimal incremental Server-Sent Events parser over a byte stream.\n *\n * Implements the parts of the SSE wire format OpenGeni uses: `id`, `event`,\n * and `data` fields, multi-line data, comment lines, and both LF and CRLF\n * line endings. Messages without any `data` (comments, id-only blocks) are\n * not emitted.\n */\nexport type SseMessage = {\n id?: string;\n event?: string;\n data: string;\n};\n\nexport async function* parseSseStream(stream: ReadableStream<Uint8Array>): AsyncGenerator<SseMessage, void, void> {\n const reader = stream.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let id: string | undefined;\n let event: string | undefined;\n let dataLines: string[] | null = null;\n\n const dispatch = (): SseMessage | null => {\n const message = dataLines === null\n ? null\n : {\n ...(id !== undefined ? { id } : {}),\n ...(event !== undefined ? { event } : {}),\n data: dataLines.join(\"\\n\"),\n };\n id = undefined;\n event = undefined;\n dataLines = null;\n return message;\n };\n\n const handleLine = (line: string): SseMessage | null => {\n if (line === \"\") {\n return dispatch();\n }\n if (line.startsWith(\":\")) {\n return null;\n }\n const colon = line.indexOf(\":\");\n const field = colon === -1 ? line : line.slice(0, colon);\n let value = colon === -1 ? \"\" : line.slice(colon + 1);\n if (value.startsWith(\" \")) {\n value = value.slice(1);\n }\n if (field === \"data\") {\n (dataLines ??= []).push(value);\n } else if (field === \"event\") {\n event = value;\n } else if (field === \"id\") {\n id = value;\n }\n // Other fields (e.g. `retry`) are ignored; reconnect pacing is the\n // streaming layer's concern.\n return null;\n };\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n buffer += decoder.decode(value, { stream: true });\n let newline = buffer.indexOf(\"\\n\");\n while (newline !== -1) {\n let line = buffer.slice(0, newline);\n buffer = buffer.slice(newline + 1);\n if (line.endsWith(\"\\r\")) {\n line = line.slice(0, -1);\n }\n const message = handleLine(line);\n if (message) {\n yield message;\n }\n newline = buffer.indexOf(\"\\n\");\n }\n }\n // Per the SSE spec, a block is only dispatched on a blank line. Anything\n // still pending at end-of-stream came from a truncated connection and is\n // discarded — the streaming layer replays from its cursor on reconnect.\n } finally {\n await reader.cancel().catch(() => {});\n reader.releaseLock();\n }\n}\n","import { isAbortError, isRetryableStreamError, OpenGeniStreamError } from \"./errors\";\nimport { parseSseStream } from \"./sse\";\nimport type { SessionEvent } from \"./types\";\n\n/**\n * Transport boundary for the streaming core. The client implements it with\n * `fetch`; unit tests script it directly.\n */\nexport type SessionEventStreamTransport = {\n /** Open the SSE stream, replaying durable events after `after` first. */\n openStream: (after: number, signal: AbortSignal | undefined) => Promise<ReadableStream<Uint8Array>>;\n /** Replay durable events by sequence (`GET .../events?after=&limit=`). */\n listEvents: (after: number, limit: number) => Promise<SessionEvent[]>;\n};\n\nexport type StreamConnectionState = \"connecting\" | \"live\" | \"reconnecting\";\n\nexport type StreamSessionEventsOptions = {\n /** Resume after this sequence number (exclusive). Defaults to 0 (full replay). */\n after?: number;\n /** Aborting ends the stream gracefully (the generator returns). */\n signal?: AbortSignal;\n /** Reconnect on transient drops. Defaults to true. */\n reconnect?: boolean;\n /** Initial reconnect backoff. Defaults to 500ms. */\n reconnectDelayMs?: number;\n /** Backoff ceiling. Defaults to 10s. */\n maxReconnectDelayMs?: number;\n /**\n * Give up after this many consecutive failed reconnect attempts (i.e. N\n * reconnects = N+1 total open-stream calls). Defaults to unlimited.\n */\n maxReconnectAttempts?: number;\n onStateChange?: (state: StreamConnectionState) => void;\n};\n\n/**\n * Stream a session's events with exactly-once, in-order delivery.\n *\n * Guarantees, anchored on the per-session contiguous `sequence`:\n * - **No duplicates**: events at or below the cursor are dropped, so server\n * replay overlap and reconnect overlap never re-yield.\n * - **No gaps**: each reconnect resumes from the last seen sequence, and a\n * gap observed inside one connection is backfilled from the durable replay\n * endpoint before the newer event is yielded (events are durable before\n * they are published live, so the backfill always finds them).\n * - **Ordered**: sequences are yielded strictly ascending.\n *\n * The generator ends when `signal` aborts, when the server closes and\n * `reconnect` is false, or with an error for non-retryable failures.\n */\nexport async function* streamSessionEvents(\n transport: SessionEventStreamTransport,\n options: StreamSessionEventsOptions = {},\n): AsyncGenerator<SessionEvent, void, void> {\n const signal = options.signal;\n const reconnect = options.reconnect ?? true;\n const baseDelayMs = options.reconnectDelayMs ?? 500;\n const maxDelayMs = options.maxReconnectDelayMs ?? 10_000;\n const maxAttempts = options.maxReconnectAttempts ?? Number.POSITIVE_INFINITY;\n let cursor = options.after ?? 0;\n let failedAttempts = 0;\n let delayMs = baseDelayMs;\n let everConnected = false;\n\n while (!signal?.aborted) {\n options.onStateChange?.(everConnected || failedAttempts > 0 ? \"reconnecting\" : \"connecting\");\n const cursorAtOpen = cursor;\n try {\n const body = await transport.openStream(cursor, signal);\n everConnected = true;\n failedAttempts = 0;\n delayMs = baseDelayMs;\n options.onStateChange?.(\"live\");\n for await (const message of parseSseStream(body)) {\n // Re-check after every yield resumption: an abort from the consumer\n // must not let already-buffered events keep flowing.\n if (signal?.aborted) {\n return;\n }\n const event = parseSessionEvent(message.data);\n if (!event || event.sequence <= cursor) {\n continue;\n }\n if (event.sequence > cursor + 1) {\n for await (const missed of backfillEvents(transport, cursor, event.sequence - 1)) {\n cursor = missed.sequence;\n yield missed;\n if (signal?.aborted) {\n return;\n }\n }\n }\n cursor = event.sequence;\n yield event;\n }\n if (!reconnect) {\n return;\n }\n // Clean server close: reconnect immediately when the connection made\n // progress (servers legitimately cycle long SSE connections); pace\n // empty closes so a misbehaving server is not hammered in a hot loop.\n if (cursor === cursorAtOpen) {\n await sleep(baseDelayMs, signal);\n }\n continue;\n } catch (error) {\n if (signal?.aborted || isAbortError(error)) {\n return;\n }\n if (!reconnect || !isRetryableStreamError(error)) {\n throw error;\n }\n failedAttempts += 1;\n if (failedAttempts > maxAttempts) {\n throw new OpenGeniStreamError(\n `event stream gave up after ${maxAttempts} consecutive failed reconnect attempts: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n await sleep(delayMs, signal);\n delayMs = Math.min(Math.max(delayMs * 2, baseDelayMs), maxDelayMs);\n }\n}\n\n/**\n * Yield the durable events with `fromExclusive < sequence <= toInclusive`,\n * in order. Sequences are contiguous, so every one of them must exist in the\n * replay endpoint; if any is missing the function throws instead of skipping\n * it — continuing would silently break the gap-free delivery guarantee.\n */\nasync function* backfillEvents(\n transport: SessionEventStreamTransport,\n fromExclusive: number,\n toInclusive: number,\n): AsyncGenerator<SessionEvent, void, void> {\n let cursor = fromExclusive;\n while (cursor < toInclusive) {\n const page = await transport.listEvents(cursor, Math.min(500, toInclusive - cursor));\n const advancing = page\n .filter((event) => event.sequence > cursor && event.sequence <= toInclusive)\n .sort((a, b) => a.sequence - b.sequence);\n if (advancing.length === 0) {\n throw new OpenGeniStreamError(\n `event replay backfill stalled: expected sequences ${cursor + 1}..${toInclusive} but the replay endpoint returned none of them`,\n );\n }\n for (const event of advancing) {\n if (event.sequence !== cursor + 1) {\n throw new OpenGeniStreamError(\n `event replay backfill is missing sequence ${cursor + 1} (replay endpoint skipped to ${event.sequence}); refusing to deliver with a gap`,\n );\n }\n cursor = event.sequence;\n yield event;\n }\n }\n}\n\nfunction parseSessionEvent(data: string): SessionEvent | null {\n let parsed: unknown;\n try {\n parsed = JSON.parse(data);\n } catch {\n return null;\n }\n if (\n typeof parsed !== \"object\" ||\n parsed === null ||\n typeof (parsed as { sequence?: unknown }).sequence !== \"number\" ||\n typeof (parsed as { type?: unknown }).type !== \"string\" ||\n typeof (parsed as { id?: unknown }).id !== \"string\"\n ) {\n return null;\n }\n return parsed as SessionEvent;\n}\n\nasync function sleep(delayMs: number, signal: AbortSignal | undefined): Promise<void> {\n if (signal?.aborted || delayMs <= 0) {\n return;\n }\n await new Promise<void>((resolve) => {\n const timer = setTimeout(done, delayMs);\n function done(): void {\n clearTimeout(timer);\n signal?.removeEventListener(\"abort\", done);\n resolve();\n }\n signal?.addEventListener(\"abort\", done, { once: true });\n });\n}\n","import { OpenGeniApiError } from \"./errors\";\nimport { streamSessionEvents, type SessionEventStreamTransport, type StreamSessionEventsOptions } from \"./stream\";\nimport type {\n AccessContext,\n AddWorkspaceMemberRequest,\n ApiKey,\n BillingEntitlementsResponse,\n BillingSummary,\n BillingUsageResponse,\n CapabilityCatalogItem,\n CapabilityCatalogResponse,\n CapabilityInstallation,\n ClientConfig,\n ClientSessionEventInput,\n CompactSessionContextResult,\n CompleteFileUploadResponse,\n CreateApiKeyRequest,\n CreateApiKeyResponse,\n CreateCapabilityCatalogItemRequest,\n CreateCheckoutRequest,\n CreateCheckoutResponse,\n CreateDocumentBaseRequest,\n CreateFileUploadRequest,\n CreateFileUploadResponse,\n CreateGitHubAppManifestRequest,\n CreateGitHubAppManifestResponse,\n CreateScheduledTaskRequest,\n CreateSessionRequest,\n CreateWorkspaceEnvironmentRequest,\n CreateWorkspaceRequest,\n DiscoverMcpCapabilitiesResponse,\n Document,\n DocumentBase,\n DocumentSearchResponse,\n EnableCapabilityRequest,\n EnablePackRequest,\n FileAsset,\n FileDownloadUrlResponse,\n GetPackResponse,\n GitHubAppInfo,\n GitHubRepositoriesResponse,\n ListApiKeysResponse,\n ListPacksResponse,\n ListWorkspaceMembersResponse,\n PackInstallation,\n ReasoningEffort,\n RegisterCapabilityPackRequest,\n ResourceRef,\n ScheduledTask,\n ScheduledTaskRun,\n Session,\n SessionEvent,\n SessionGoal,\n SessionTurn,\n ToolRef,\n UpdateScheduledTaskRequest,\n UpdateSessionGoalRequest,\n UpdateSessionTurnRequest,\n UpdateWorkspaceEnvironmentRequest,\n UpdateWorkspaceMemberRequest,\n UpdateWorkspaceRequest,\n UploadFileInput,\n WorkspaceEnvironment,\n WorkspaceEnvironmentVariableMetadata,\n WorkspaceMember,\n WorkspaceRegisteredPack,\n Workspace,\n} from \"./types\";\n\nexport type FetchLike = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;\n\nexport type OpenGeniClientOptions = {\n /** Base URL of the OpenGeni API, e.g. `https://api.example.com`. */\n baseUrl: string;\n /** OpenGeni API key, sent as `Authorization: Bearer <apiKey>`. */\n apiKey?: string;\n /** Extra headers (static or computed per request) merged into every call. */\n headers?: Record<string, string> | (() => Record<string, string>);\n /** Custom fetch implementation. Defaults to the global `fetch`. */\n fetch?: FetchLike;\n};\n\nexport type SendMessageInput = {\n text: string;\n resources?: ResourceRef[];\n tools?: ToolRef[];\n model?: string;\n reasoningEffort?: ReasoningEffort;\n clientEventId?: string;\n};\n\nexport type SteerMessageResult = {\n /** The accepted `user.message` event. */\n accepted: SessionEvent;\n /**\n * The turn created for the message, when it could be located — usually\n * still queued, but already claimed (running/requires_action or even\n * finished) when the worker picked it up mid-call.\n */\n turn: SessionTurn | null;\n /** True when the running turn was interrupted to make way for the message. */\n interrupted: boolean;\n};\n\n/**\n * Typed client for the OpenGeni public API. Framework-agnostic: only needs\n * WHATWG `fetch` + streams, so it runs in Node 18+, Bun, Deno, browsers, and\n * edge runtimes.\n */\nexport class OpenGeniClient {\n private readonly baseUrl: string;\n private readonly options: OpenGeniClientOptions;\n private readonly fetchImpl: FetchLike;\n\n constructor(options: OpenGeniClientOptions) {\n this.baseUrl = options.baseUrl.replace(/\\/+$/, \"\");\n this.options = options;\n // Bind lazily so environments that polyfill fetch after module load work.\n this.fetchImpl = options.fetch ?? ((input, init) => fetch(input, init));\n }\n\n // --- Session lifecycle ---------------------------------------------------\n\n async createSession(workspaceId: string, request: CreateSessionRequest): Promise<Session> {\n return await this.requestJson<Session>(\"POST\", `/v1/workspaces/${workspaceId}/sessions`, request);\n }\n\n async getSession(workspaceId: string, sessionId: string): Promise<Session> {\n return await this.requestJson<Session>(\"GET\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}`);\n }\n\n async listSessions(workspaceId: string, options: { limit?: number } = {}): Promise<Session[]> {\n return await this.requestJson<Session[]>(\"GET\", `/v1/workspaces/${workspaceId}/sessions`, undefined, {\n ...(options.limit !== undefined ? { limit: String(options.limit) } : {}),\n });\n }\n\n async listTurns(workspaceId: string, sessionId: string, options: { limit?: number } = {}): Promise<SessionTurn[]> {\n return await this.requestJson<SessionTurn[]>(\"GET\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/turns`, undefined, {\n ...(options.limit !== undefined ? { limit: String(options.limit) } : {}),\n });\n }\n\n // --- Scheduled tasks -------------------------------------------------------\n\n async listScheduledTasks(workspaceId: string, options: { limit?: number } = {}): Promise<ScheduledTask[]> {\n return await this.requestJson<ScheduledTask[]>(\"GET\", `/v1/workspaces/${workspaceId}/scheduled-tasks`, undefined, {\n ...(options.limit !== undefined ? { limit: String(options.limit) } : {}),\n });\n }\n\n async getScheduledTask(workspaceId: string, taskId: string): Promise<ScheduledTask> {\n return await this.requestJson<ScheduledTask>(\"GET\", `/v1/workspaces/${workspaceId}/scheduled-tasks/${taskId}`);\n }\n\n // --- Events: replay, send, stream ----------------------------------------\n\n /** Replay durable events by sequence: events with `sequence > after`, ascending. */\n async listEvents(\n workspaceId: string,\n sessionId: string,\n options: { after?: number; limit?: number } = {},\n ): Promise<SessionEvent[]> {\n return await this.requestJson<SessionEvent[]>(\"GET\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/events`, undefined, {\n ...(options.after !== undefined ? { after: String(options.after) } : {}),\n ...(options.limit !== undefined ? { limit: String(options.limit) } : {}),\n });\n }\n\n /** POST a user/control event to the session. Returns the accepted event. */\n async sendEvent(workspaceId: string, sessionId: string, event: ClientSessionEventInput): Promise<SessionEvent> {\n return await this.requestJson<SessionEvent>(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/events`, event);\n }\n\n async sendMessage(workspaceId: string, sessionId: string, message: string | SendMessageInput): Promise<SessionEvent> {\n const input = typeof message === \"string\" ? { text: message } : message;\n const { clientEventId, ...payload } = input;\n return await this.sendEvent(workspaceId, sessionId, {\n type: \"user.message\",\n ...(clientEventId !== undefined ? { clientEventId } : {}),\n payload,\n });\n }\n\n async interrupt(\n workspaceId: string,\n sessionId: string,\n options: { reason?: string; clientEventId?: string } = {},\n ): Promise<SessionEvent> {\n return await this.sendEvent(workspaceId, sessionId, {\n type: \"user.interrupt\",\n ...(options.clientEventId !== undefined ? { clientEventId: options.clientEventId } : {}),\n payload: options.reason !== undefined ? { reason: options.reason } : {},\n });\n }\n\n async sendApprovalDecision(\n workspaceId: string,\n sessionId: string,\n decision: { approvalId: string; decision: \"approve\" | \"reject\"; message?: string; clientEventId?: string },\n ): Promise<SessionEvent> {\n const { clientEventId, ...payload } = decision;\n return await this.sendEvent(workspaceId, sessionId, {\n type: \"user.approvalDecision\",\n ...(clientEventId !== undefined ? { clientEventId } : {}),\n payload,\n });\n }\n\n /**\n * Live-stream a session's events with automatic reconnect, resume from the\n * last seen sequence, gap backfill, and duplicate suppression. See\n * {@link streamSessionEvents} for the delivery guarantees.\n */\n streamEvents(\n workspaceId: string,\n sessionId: string,\n options: StreamSessionEventsOptions = {},\n ): AsyncGenerator<SessionEvent, void, void> {\n return streamSessionEvents(this.eventStreamTransport(workspaceId, sessionId), options);\n }\n\n /** The transport `streamEvents` runs on; useful for custom streaming layers. */\n eventStreamTransport(workspaceId: string, sessionId: string): SessionEventStreamTransport {\n return {\n openStream: async (after, signal) => await this.openEventStream(workspaceId, sessionId, { after, ...(signal ? { signal } : {}) }),\n listEvents: async (after, limit) => await this.listEvents(workspaceId, sessionId, { after, limit }),\n };\n }\n\n /** Open one raw SSE connection (no reconnect). Most callers want `streamEvents`. */\n async openEventStream(\n workspaceId: string,\n sessionId: string,\n options: { after?: number; signal?: AbortSignal } = {},\n ): Promise<ReadableStream<Uint8Array>> {\n const url = this.url(`/v1/workspaces/${workspaceId}/sessions/${sessionId}/events/stream`, {\n after: String(options.after ?? 0),\n });\n const response = await this.fetchImpl(url, {\n method: \"GET\",\n headers: { ...this.headers(), Accept: \"text/event-stream\" },\n ...(options.signal ? { signal: options.signal } : {}),\n });\n if (!response.ok) {\n throw new OpenGeniApiError(response.status, await safeText(response));\n }\n if (!response.body) {\n throw new OpenGeniApiError(response.status, \"SSE response did not include a readable body\");\n }\n return response.body;\n }\n\n // --- Turn queue ------------------------------------------------------------\n\n /** Edit a still-queued turn (prompt, model, resources, tools, ...). */\n async updateQueuedTurn(\n workspaceId: string,\n sessionId: string,\n turnId: string,\n update: UpdateSessionTurnRequest,\n ): Promise<SessionTurn> {\n return await this.requestJson<SessionTurn>(\n \"PATCH\",\n `/v1/workspaces/${workspaceId}/sessions/${sessionId}/turns/${turnId}`,\n update,\n );\n }\n\n /**\n * Reorder the queued turns. `turnIds` must all reference queued turns; the\n * server assigns positions in the given order and returns the queue.\n */\n async reorderQueuedTurns(workspaceId: string, sessionId: string, turnIds: string[]): Promise<SessionTurn[]> {\n return await this.requestJson<SessionTurn[]>(\n \"POST\",\n `/v1/workspaces/${workspaceId}/sessions/${sessionId}/turns/reorder`,\n { turnIds },\n );\n }\n\n /** Cancel a queued turn before it is claimed. Returns the cancelled turn. */\n async deleteQueuedTurn(workspaceId: string, sessionId: string, turnId: string): Promise<SessionTurn> {\n return await this.requestJson<SessionTurn>(\n \"DELETE\",\n `/v1/workspaces/${workspaceId}/sessions/${sessionId}/turns/${turnId}`,\n );\n }\n\n /**\n * Steer: deliver a message *now* instead of behind the queue. Sends the\n * message, promotes its queued turn to the front, and interrupts the\n * running turn so the session picks the steer turn up next. On a session\n * that is not running this degrades gracefully to a plain queued message.\n *\n * The steer turn is located by `triggerEventId` across ALL turns (retried\n * briefly in case the server is still materializing it) — not just the\n * queued ones, because the worker can claim the steer turn before it is\n * ever observed queued, and a claimed steer turn means the message is\n * already being delivered: interrupting then would cancel the very message\n * being steered. If the turn cannot be found while other turns are queued,\n * the interrupt is also skipped — stopping the running turn would otherwise\n * promote someone else's queued work over this message — and the call\n * degrades to a plain queued send (`interrupted: false`).\n */\n async steerMessage(\n workspaceId: string,\n sessionId: string,\n message: string | SendMessageInput,\n ): Promise<SteerMessageResult> {\n const accepted = await this.sendMessage(workspaceId, sessionId, message);\n let steerTurn: SessionTurn | null = null;\n let queued: SessionTurn[] = [];\n for (let attempt = 0; attempt < 4; attempt += 1) {\n if (attempt > 0) {\n await delay(150 * attempt);\n }\n const turns = await this.listTurns(workspaceId, sessionId);\n queued = turns\n .filter((turn) => turn.status === \"queued\")\n .sort((a, b) => a.position - b.position || a.createdAt.localeCompare(b.createdAt));\n // Match against every turn, whatever its status: a steer turn that is\n // already running/requires_action (or even finished) was claimed before\n // this listing — that is delivery, not grounds for an interrupt.\n steerTurn = turns.find((turn) => turn.triggerEventId === accepted.id) ?? null;\n if (steerTurn) {\n break;\n }\n }\n const steerTurnQueued = steerTurn?.status === \"queued\";\n if (steerTurn && steerTurnQueued && queued.length > 1) {\n const front = steerTurn;\n await this.reorderQueuedTurns(workspaceId, sessionId, [\n front.id,\n ...queued.filter((turn) => turn.id !== front.id).map((turn) => turn.id),\n ]);\n }\n // Interrupting is only safe when the next claim is provably this message:\n // either the steer turn sits queued (now at the front), or no turn\n // materialized yet AND nothing else is queued. A steer turn observed in\n // any non-queued state was already claimed — skip the interrupt.\n const canDeliverNext = steerTurnQueued || (steerTurn === null && queued.length === 0);\n const session = await this.getSession(workspaceId, sessionId);\n // If the previously running turn already finished and the session claimed\n // the steer turn itself, interrupting now would cancel the very message\n // being steered. `activeTurnId` is the claim check; the residual window\n // between this read and the interrupt landing is accepted (an interrupt\n // can never be atomic with a status read over HTTP).\n const steerTurnAlreadyActive = steerTurn !== null && session.activeTurnId === steerTurn.id;\n const interrupted = canDeliverNext\n && !steerTurnAlreadyActive\n && (session.status === \"running\" || session.status === \"requires_action\");\n if (interrupted) {\n await this.interrupt(workspaceId, sessionId, { reason: \"steer\" });\n }\n return { accepted, turn: steerTurn, interrupted };\n }\n\n // --- Goals -------------------------------------------------------------------\n\n /** The session's goal. 404s when the session never had one. */\n async getGoal(workspaceId: string, sessionId: string): Promise<SessionGoal> {\n return await this.requestJson<SessionGoal>(\"GET\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/goal`);\n }\n\n async updateGoal(workspaceId: string, sessionId: string, request: UpdateSessionGoalRequest): Promise<SessionGoal> {\n return await this.requestJson<SessionGoal>(\"PATCH\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/goal`, request);\n }\n\n /** Pause the goal loop: the session stops self-continuing until resumed. */\n async pauseGoal(workspaceId: string, sessionId: string, options: { rationale?: string } = {}): Promise<SessionGoal> {\n return await this.updateGoal(workspaceId, sessionId, {\n status: \"paused\",\n ...(options.rationale !== undefined ? { rationale: options.rationale } : {}),\n });\n }\n\n /** Resume a paused goal: resets counters and re-arms the continuation loop. */\n async resumeGoal(workspaceId: string, sessionId: string): Promise<SessionGoal> {\n return await this.updateGoal(workspaceId, sessionId, { status: \"active\" });\n }\n\n // --- Operator context controls (/clear, /compact) ---------------------------\n\n /**\n * Clear the session's conversation context. Destructive and audit-preserving:\n * the server supersedes (never deletes) the live history and emits a\n * `session.context.cleared` event. Refused (409) while a turn is in flight or\n * awaiting action. `confirm:true` is sent so an accidental call cannot wipe\n * context — the destructive intent is explicit on the wire.\n */\n async clearSessionContext(workspaceId: string, sessionId: string): Promise<void> {\n await this.requestVoid(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/context/clear`, { confirm: true });\n }\n\n /**\n * Trigger conversation compaction now. On the client-managed (Azure) path this\n * queues a forced compaction the worker honors before the next turn\n * (`status:\"queued\"`); on a server-managed provider or when compaction is off\n * it is a no-op (`status:\"noop\"`) with an explanatory message.\n */\n async compactSessionContext(workspaceId: string, sessionId: string): Promise<CompactSessionContextResult> {\n return await this.requestJson<CompactSessionContextResult>(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/context/compact`, {});\n }\n\n // --- Access + workspaces -----------------------------------------------------\n\n /**\n * The deployment's public client bootstrap config: the host-exposed models\n * (provider-grouped in `models`, flat in `allowedModels` for back-compat),\n * reasoning efforts, MCP servers, file-upload limits, and how the client is\n * expected to authenticate. Drives a composer's model picker without prior\n * knowledge of the host setup; safe to call before any auth is established.\n */\n async getClientConfig(): Promise<ClientConfig> {\n return await this.requestJson<ClientConfig>(\"GET\", \"/v1/config/client\");\n }\n\n /** The caller's access context: subject, account + workspace grants, defaults. */\n async getAccessContext(): Promise<AccessContext> {\n return await this.requestJson<AccessContext>(\"GET\", \"/v1/access/me\");\n }\n\n async listWorkspaces(): Promise<Workspace[]> {\n return await this.requestJson<Workspace[]>(\"GET\", \"/v1/workspaces\");\n }\n\n async createWorkspace(request: CreateWorkspaceRequest): Promise<Workspace> {\n return await this.requestJson<Workspace>(\"POST\", \"/v1/workspaces\", request);\n }\n\n async getWorkspace(workspaceId: string): Promise<Workspace> {\n return await this.requestJson<Workspace>(\"GET\", `/v1/workspaces/${workspaceId}`);\n }\n\n async updateWorkspace(workspaceId: string, request: UpdateWorkspaceRequest): Promise<Workspace> {\n return await this.requestJson<Workspace>(\"PATCH\", `/v1/workspaces/${workspaceId}`, request);\n }\n\n /**\n * Delete a workspace and everything in it. Refused (409) for the account's\n * only workspace and while it still has a running session. Irreversible.\n */\n async deleteWorkspace(workspaceId: string): Promise<void> {\n await this.requestVoid(\"DELETE\", `/v1/workspaces/${workspaceId}`);\n }\n\n // --- Members (\"People with access\") -------------------------------------------\n\n /** The workspace's members (user + api_key subjects). */\n async listWorkspaceMembers(workspaceId: string): Promise<WorkspaceMember[]> {\n const response = await this.requestJson<ListWorkspaceMembersResponse>(\"GET\", `/v1/workspaces/${workspaceId}/members`);\n return response.members;\n }\n\n /**\n * Add an already-registered user by email. 404s when no user with that email\n * exists (email invites for unknown users are deferred).\n */\n async addWorkspaceMember(workspaceId: string, request: AddWorkspaceMemberRequest): Promise<WorkspaceMember> {\n return await this.requestJson<WorkspaceMember>(\"POST\", `/v1/workspaces/${workspaceId}/members`, request);\n }\n\n async updateWorkspaceMember(workspaceId: string, subjectId: string, request: UpdateWorkspaceMemberRequest): Promise<WorkspaceMember> {\n return await this.requestJson<WorkspaceMember>(\n \"PATCH\",\n `/v1/workspaces/${workspaceId}/members/${encodeURIComponent(subjectId)}`,\n request,\n );\n }\n\n /**\n * Remove a member. Refused (409) for your own membership and for the last\n * member who can still manage the workspace.\n */\n async removeWorkspaceMember(workspaceId: string, subjectId: string): Promise<void> {\n await this.requestVoid(\"DELETE\", `/v1/workspaces/${workspaceId}/members/${encodeURIComponent(subjectId)}`);\n }\n\n // --- Scheduled tasks (write + runs) -------------------------------------------\n\n async createScheduledTask(workspaceId: string, request: CreateScheduledTaskRequest): Promise<ScheduledTask> {\n return await this.requestJson<ScheduledTask>(\"POST\", `/v1/workspaces/${workspaceId}/scheduled-tasks`, request);\n }\n\n async updateScheduledTask(workspaceId: string, taskId: string, request: UpdateScheduledTaskRequest): Promise<ScheduledTask> {\n return await this.requestJson<ScheduledTask>(\"PATCH\", `/v1/workspaces/${workspaceId}/scheduled-tasks/${taskId}`, request);\n }\n\n async pauseScheduledTask(workspaceId: string, taskId: string): Promise<ScheduledTask> {\n return await this.requestJson<ScheduledTask>(\"POST\", `/v1/workspaces/${workspaceId}/scheduled-tasks/${taskId}/pause`);\n }\n\n async resumeScheduledTask(workspaceId: string, taskId: string): Promise<ScheduledTask> {\n return await this.requestJson<ScheduledTask>(\"POST\", `/v1/workspaces/${workspaceId}/scheduled-tasks/${taskId}/resume`);\n }\n\n /**\n * Fire the task immediately (manual trigger), independent of its schedule.\n * Pass a stable `triggerId` to make a retried trigger idempotent — the same\n * token charges once and starts one run. Omit it and each call is distinct.\n */\n async triggerScheduledTask(workspaceId: string, taskId: string, options: { triggerId?: string } = {}): Promise<ScheduledTask> {\n return await this.requestJson<ScheduledTask>(\n \"POST\",\n `/v1/workspaces/${workspaceId}/scheduled-tasks/${taskId}/trigger`,\n options.triggerId ? { triggerId: options.triggerId } : undefined,\n );\n }\n\n async deleteScheduledTask(workspaceId: string, taskId: string): Promise<void> {\n await this.requestJson<unknown>(\"DELETE\", `/v1/workspaces/${workspaceId}/scheduled-tasks/${taskId}`);\n }\n\n async listScheduledTaskRuns(\n workspaceId: string,\n taskId: string,\n options: { limit?: number } = {},\n ): Promise<ScheduledTaskRun[]> {\n return await this.requestJson<ScheduledTaskRun[]>(\n \"GET\",\n `/v1/workspaces/${workspaceId}/scheduled-tasks/${taskId}/runs`,\n undefined,\n { ...(options.limit !== undefined ? { limit: String(options.limit) } : {}) },\n );\n }\n\n // --- Environments --------------------------------------------------------------\n // Variable values are write-only: reads return name/version metadata only.\n\n async listEnvironments(workspaceId: string): Promise<WorkspaceEnvironment[]> {\n return await this.requestJson<WorkspaceEnvironment[]>(\"GET\", `/v1/workspaces/${workspaceId}/environments`);\n }\n\n async createEnvironment(workspaceId: string, request: CreateWorkspaceEnvironmentRequest): Promise<WorkspaceEnvironment> {\n return await this.requestJson<WorkspaceEnvironment>(\"POST\", `/v1/workspaces/${workspaceId}/environments`, request);\n }\n\n async getEnvironment(workspaceId: string, environmentId: string): Promise<WorkspaceEnvironment> {\n return await this.requestJson<WorkspaceEnvironment>(\"GET\", `/v1/workspaces/${workspaceId}/environments/${environmentId}`);\n }\n\n async updateEnvironment(\n workspaceId: string,\n environmentId: string,\n request: UpdateWorkspaceEnvironmentRequest,\n ): Promise<WorkspaceEnvironment> {\n return await this.requestJson<WorkspaceEnvironment>(\n \"PATCH\",\n `/v1/workspaces/${workspaceId}/environments/${environmentId}`,\n request,\n );\n }\n\n async deleteEnvironment(workspaceId: string, environmentId: string): Promise<void> {\n await this.requestJson<unknown>(\"DELETE\", `/v1/workspaces/${workspaceId}/environments/${environmentId}`);\n }\n\n /** Create or rotate a variable. The value never comes back on any read. */\n async setEnvironmentVariable(\n workspaceId: string,\n environmentId: string,\n name: string,\n value: string,\n ): Promise<WorkspaceEnvironmentVariableMetadata> {\n return await this.requestJson<WorkspaceEnvironmentVariableMetadata>(\n \"PUT\",\n `/v1/workspaces/${workspaceId}/environments/${environmentId}/variables/${encodeURIComponent(name)}`,\n { value },\n );\n }\n\n async deleteEnvironmentVariable(workspaceId: string, environmentId: string, name: string): Promise<void> {\n await this.requestJson<unknown>(\n \"DELETE\",\n `/v1/workspaces/${workspaceId}/environments/${environmentId}/variables/${encodeURIComponent(name)}`,\n );\n }\n\n // --- Files -----------------------------------------------------------------------\n\n /** Step 1 of the upload flow: returns the pre-signed PUT target. */\n async beginFileUpload(workspaceId: string, request: CreateFileUploadRequest): Promise<CreateFileUploadResponse> {\n return await this.requestJson<CreateFileUploadResponse>(\"POST\", `/v1/workspaces/${workspaceId}/files/uploads`, request);\n }\n\n /** Step 3 of the upload flow: server verifies the object and marks it ready. */\n async completeFileUpload(workspaceId: string, uploadId: string): Promise<FileAsset> {\n const response = await this.requestJson<CompleteFileUploadResponse>(\n \"POST\",\n `/v1/workspaces/${workspaceId}/files/uploads/${uploadId}/complete`,\n );\n return response.file;\n }\n\n /**\n * The whole upload flow as one call: begin -> PUT the bytes to the signed\n * URL (with its required headers; no API auth is sent to object storage)\n * -> complete. Returns the ready `FileAsset`.\n */\n async uploadFile(workspaceId: string, input: UploadFileInput): Promise<FileAsset> {\n // Copy Uint8Array views into a Blob so byte offsets/shared buffers can't\n // leak surrounding bytes into the PUT body.\n const body: Blob | ArrayBuffer | string = input.data instanceof Uint8Array\n ? new Blob([input.data.slice()])\n : input.data;\n const sizeBytes = typeof body === \"string\"\n ? new TextEncoder().encode(body).byteLength\n : body instanceof Blob ? body.size : body.byteLength;\n const upload = await this.beginFileUpload(workspaceId, {\n filename: input.filename,\n contentType: input.contentType,\n sizeBytes,\n ...(input.sha256 !== undefined ? { sha256: input.sha256 } : {}),\n });\n const putResponse = await this.fetchImpl(upload.putUrl, {\n method: \"PUT\",\n // The backend's requiredHeaders already carry the canonical lowercase\n // `content-type` for every storage backend (Azure/S3/GCS). Do NOT also set\n // a `Content-Type` key here: WHATWG Headers treats the two casings as the\n // same header and comma-joins their values (e.g. \"text/plain, text/plain\"),\n // which the object store persists verbatim and COMPLETE then rejects (422),\n // and which breaks S3's presigned-URL signature.\n headers: { ...upload.requiredHeaders },\n body,\n });\n if (!putResponse.ok) {\n throw new OpenGeniApiError(putResponse.status, await safeText(putResponse));\n }\n return await this.completeFileUpload(workspaceId, upload.uploadId);\n }\n\n async getFile(workspaceId: string, fileId: string): Promise<FileAsset> {\n return await this.requestJson<FileAsset>(\"GET\", `/v1/workspaces/${workspaceId}/files/${fileId}`);\n }\n\n /** Mint a short-lived signed download URL for a ready file. */\n async createFileDownloadUrl(workspaceId: string, fileId: string): Promise<FileDownloadUrlResponse> {\n return await this.requestJson<FileDownloadUrlResponse>(\"POST\", `/v1/workspaces/${workspaceId}/files/${fileId}/download-url`);\n }\n\n // --- Documents ----------------------------------------------------------------------\n\n async createDocumentBase(workspaceId: string, request: CreateDocumentBaseRequest): Promise<DocumentBase> {\n return await this.requestJson<DocumentBase>(\"POST\", `/v1/workspaces/${workspaceId}/document-bases`, request);\n }\n\n async listDocumentBases(workspaceId: string): Promise<DocumentBase[]> {\n return await this.requestJson<DocumentBase[]>(\"GET\", `/v1/workspaces/${workspaceId}/document-bases`);\n }\n\n async getDocumentBase(workspaceId: string, baseId: string): Promise<DocumentBase> {\n return await this.requestJson<DocumentBase>(\"GET\", `/v1/workspaces/${workspaceId}/document-bases/${baseId}`);\n }\n\n /** Index an uploaded file into the base. The file must be `ready`. */\n async addDocument(workspaceId: string, baseId: string, request: { fileId: string }): Promise<Document> {\n return await this.requestJson<Document>(\"POST\", `/v1/workspaces/${workspaceId}/document-bases/${baseId}/documents`, request);\n }\n\n async listDocuments(workspaceId: string, baseId: string): Promise<Document[]> {\n return await this.requestJson<Document[]>(\"GET\", `/v1/workspaces/${workspaceId}/document-bases/${baseId}/documents`);\n }\n\n /** Retry indexing for a failed document. */\n async reindexDocument(workspaceId: string, baseId: string, documentId: string): Promise<Document> {\n return await this.requestJson<Document>(\n \"POST\",\n `/v1/workspaces/${workspaceId}/document-bases/${baseId}/documents/${documentId}/reindex`,\n );\n }\n\n async searchDocuments(\n workspaceId: string,\n baseId: string,\n request: { query: string; limit?: number },\n ): Promise<DocumentSearchResponse> {\n return await this.requestJson<DocumentSearchResponse>(\n \"POST\",\n `/v1/workspaces/${workspaceId}/document-bases/${baseId}/search`,\n request,\n );\n }\n\n // --- Capability packs ------------------------------------------------------------------\n\n /** Built-in + registered packs, with the workspace's installations. */\n async listPacks(workspaceId: string): Promise<ListPacksResponse> {\n return await this.requestJson<ListPacksResponse>(\"GET\", `/v1/workspaces/${workspaceId}/packs`);\n }\n\n /** Register (or replace) a workspace-scoped pack from a manifest. */\n async registerPack(workspaceId: string, manifest: RegisterCapabilityPackRequest): Promise<WorkspaceRegisteredPack> {\n return await this.requestJson<WorkspaceRegisteredPack>(\"POST\", `/v1/workspaces/${workspaceId}/packs`, manifest);\n }\n\n async getPack(workspaceId: string, packId: string): Promise<GetPackResponse> {\n return await this.requestJson<GetPackResponse>(\"GET\", `/v1/workspaces/${workspaceId}/packs/${encodeURIComponent(packId)}`);\n }\n\n async enablePack(workspaceId: string, packId: string, request: EnablePackRequest = {}): Promise<PackInstallation> {\n return await this.requestJson<PackInstallation>(\n \"POST\",\n `/v1/workspaces/${workspaceId}/packs/${encodeURIComponent(packId)}/enable`,\n request,\n );\n }\n\n /** Unregister a workspace-scoped pack (built-in packs cannot be deleted). */\n async deletePack(workspaceId: string, packId: string): Promise<void> {\n await this.requestVoid(\"DELETE\", `/v1/workspaces/${workspaceId}/packs/${encodeURIComponent(packId)}`);\n }\n\n async listPackInstallations(workspaceId: string): Promise<PackInstallation[]> {\n return await this.requestJson<PackInstallation[]>(\"GET\", `/v1/workspaces/${workspaceId}/packs/installations`);\n }\n\n // --- Capabilities -------------------------------------------------------------------------\n\n async listCapabilities(workspaceId: string): Promise<CapabilityCatalogResponse> {\n return await this.requestJson<CapabilityCatalogResponse>(\"GET\", `/v1/workspaces/${workspaceId}/capabilities`);\n }\n\n /** Add a manual capability catalog item (e.g. a remote MCP server). */\n async createCapability(workspaceId: string, request: CreateCapabilityCatalogItemRequest): Promise<CapabilityCatalogItem> {\n return await this.requestJson<CapabilityCatalogItem>(\"POST\", `/v1/workspaces/${workspaceId}/capabilities`, request);\n }\n\n async enableCapability(\n workspaceId: string,\n capabilityId: string,\n request: EnableCapabilityRequest = {},\n ): Promise<CapabilityInstallation> {\n return await this.requestJson<CapabilityInstallation>(\n \"POST\",\n `/v1/workspaces/${workspaceId}/capabilities/${encodeURIComponent(capabilityId)}/enable`,\n request,\n );\n }\n\n async disableCapability(workspaceId: string, capabilityId: string): Promise<CapabilityInstallation> {\n return await this.requestJson<CapabilityInstallation>(\n \"POST\",\n `/v1/workspaces/${workspaceId}/capabilities/${encodeURIComponent(capabilityId)}/disable`,\n );\n }\n\n /** Search the official MCP registry for installable capabilities. */\n async discoverMcpCapabilities(\n workspaceId: string,\n options: { query?: string; limit?: number } = {},\n ): Promise<DiscoverMcpCapabilitiesResponse> {\n return await this.requestJson<DiscoverMcpCapabilitiesResponse>(\n \"GET\",\n `/v1/workspaces/${workspaceId}/capabilities/discovery/mcp-registry`,\n undefined,\n {\n ...(options.query !== undefined ? { query: options.query } : {}),\n ...(options.limit !== undefined ? { limit: String(options.limit) } : {}),\n },\n );\n }\n\n // --- GitHub ----------------------------------------------------------------------------------\n\n /** GitHub App configuration status + a signed install URL when configured. */\n async getGitHubApp(workspaceId: string): Promise<GitHubAppInfo> {\n return await this.requestJson<GitHubAppInfo>(\"GET\", `/v1/workspaces/${workspaceId}/github/app`);\n }\n\n /**\n * Browser entry point that plants the CSRF cookie and forwards to GitHub's\n * install page. Open this in a browser (it redirects); `state` comes from\n * `getGitHubApp().installUrl` or a github_connect_link tool.\n */\n githubConnectUrl(workspaceId: string, state: string): string {\n return this.url(`/v1/workspaces/${workspaceId}/github/connect`, { state });\n }\n\n async listGitHubRepositories(workspaceId: string): Promise<GitHubRepositoriesResponse> {\n return await this.requestJson<GitHubRepositoriesResponse>(\"GET\", `/v1/workspaces/${workspaceId}/github/repositories`);\n }\n\n /** Re-sync the installation's repository list from GitHub. */\n async syncGitHubRepositories(workspaceId: string): Promise<GitHubRepositoriesResponse> {\n return await this.requestJson<GitHubRepositoriesResponse>(\"POST\", `/v1/workspaces/${workspaceId}/github/repositories/sync`);\n }\n\n /** Build a GitHub App manifest + the GitHub URL to submit it to. */\n async createGitHubAppManifest(\n workspaceId: string,\n request: CreateGitHubAppManifestRequest = {},\n ): Promise<CreateGitHubAppManifestResponse> {\n return await this.requestJson<CreateGitHubAppManifestResponse>(\n \"POST\",\n `/v1/workspaces/${workspaceId}/github/app-manifest`,\n request,\n );\n }\n\n // --- API keys ----------------------------------------------------------------------------------\n\n async listApiKeys(workspaceId: string): Promise<ApiKey[]> {\n const response = await this.requestJson<ListApiKeysResponse>(\"GET\", `/v1/workspaces/${workspaceId}/api-keys`);\n return response.apiKeys;\n }\n\n /** The returned `token` is shown once; only its prefix is stored. */\n async createApiKey(workspaceId: string, request: CreateApiKeyRequest): Promise<CreateApiKeyResponse> {\n return await this.requestJson<CreateApiKeyResponse>(\"POST\", `/v1/workspaces/${workspaceId}/api-keys`, request);\n }\n\n /** Revoke an API key. Returns the revoked key. */\n async deleteApiKey(workspaceId: string, apiKeyId: string): Promise<ApiKey> {\n return await this.requestJson<ApiKey>(\"DELETE\", `/v1/workspaces/${workspaceId}/api-keys/${apiKeyId}`);\n }\n\n // --- Billing (account-scoped) --------------------------------------------------------------------\n\n async getBilling(options: { accountId?: string } = {}): Promise<BillingSummary> {\n return await this.requestJson<BillingSummary>(\"GET\", \"/v1/billing\", undefined, {\n ...(options.accountId !== undefined ? { accountId: options.accountId } : {}),\n });\n }\n\n async getBillingUsage(options: { accountId?: string; workspaceId?: string } = {}): Promise<BillingUsageResponse> {\n return await this.requestJson<BillingUsageResponse>(\"GET\", \"/v1/billing/usage\", undefined, {\n ...(options.accountId !== undefined ? { accountId: options.accountId } : {}),\n ...(options.workspaceId !== undefined ? { workspaceId: options.workspaceId } : {}),\n });\n }\n\n async getBillingEntitlements(options: { accountId?: string } = {}): Promise<BillingEntitlementsResponse> {\n return await this.requestJson<BillingEntitlementsResponse>(\"GET\", \"/v1/billing/entitlements\", undefined, {\n ...(options.accountId !== undefined ? { accountId: options.accountId } : {}),\n });\n }\n\n /** Start a Stripe checkout for prepaid credits. */\n async createBillingCheckout(request: CreateCheckoutRequest): Promise<CreateCheckoutResponse> {\n return await this.requestJson<CreateCheckoutResponse>(\"POST\", \"/v1/billing/checkout\", request);\n }\n\n // --- Internals -------------------------------------------------------------\n\n private headers(): Record<string, string> {\n const extra = typeof this.options.headers === \"function\" ? this.options.headers() : this.options.headers;\n return {\n ...(this.options.apiKey ? { Authorization: `Bearer ${this.options.apiKey}` } : {}),\n ...extra,\n };\n }\n\n private url(path: string, query: Record<string, string> = {}): string {\n const params = new URLSearchParams(query).toString();\n return `${this.baseUrl}${path}${params ? `?${params}` : \"\"}`;\n }\n\n private async requestJson<T>(method: string, path: string, body?: unknown, query: Record<string, string> = {}): Promise<T> {\n const response = await this.fetchImpl(this.url(path, query), {\n method,\n headers: {\n ...this.headers(),\n Accept: \"application/json\",\n ...(body !== undefined ? { \"Content-Type\": \"application/json\" } : {}),\n },\n ...(body !== undefined ? { body: JSON.stringify(body) } : {}),\n });\n if (!response.ok) {\n throw new OpenGeniApiError(response.status, await safeText(response));\n }\n return (await response.json()) as T;\n }\n\n /** Like `requestJson` for endpoints that respond with no body (204). */\n private async requestVoid(method: string, path: string, body?: unknown): Promise<void> {\n const response = await this.fetchImpl(this.url(path), {\n method,\n headers: {\n ...this.headers(),\n Accept: \"application/json\",\n ...(body !== undefined ? { \"Content-Type\": \"application/json\" } : {}),\n },\n ...(body !== undefined ? { body: JSON.stringify(body) } : {}),\n });\n if (!response.ok) {\n throw new OpenGeniApiError(response.status, await safeText(response));\n }\n }\n}\n\nasync function safeText(response: Response): Promise<string> {\n try {\n return await response.text();\n } catch {\n return \"\";\n }\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","import type { OpenGeniClient } from \"./client\";\nimport type { StreamSessionEventsOptions } from \"./stream\";\nimport type { SessionEvent } from \"./types\";\n\n/**\n * Proxy-through-your-own-API helpers.\n *\n * The intended pattern: a customer's server consumes the OpenGeni event\n * stream with its own API key (`client.streamEvents(...)`) and re-emits it to\n * its browser clients over its own authenticated endpoint — the OpenGeni key\n * never reaches the browser. The re-emitted wire format is identical to\n * OpenGeni's own SSE stream (`id: <sequence>`, `event: <type>`,\n * `data: <event JSON>`), so the browser side can consume it with this same\n * SDK's streaming core (or a plain `EventSource`), including resume via\n * `?after=` / `Last-Event-ID`.\n */\n\n/** Format one event exactly as OpenGeni's API emits it over SSE. */\nexport function formatSseEvent(event: SessionEvent): string {\n return `id: ${event.sequence}\\nevent: ${event.type}\\ndata: ${JSON.stringify(event)}\\n\\n`;\n}\n\nexport type SseReStreamOptions = {\n /**\n * Emit `: ping` comment lines at this interval, keeping intermediaries from\n * idling the connection out. Disabled when omitted.\n */\n heartbeatMs?: number;\n /**\n * Called when the downstream consumer cancels (e.g. the browser\n * disconnected). Use it to abort the upstream OpenGeni stream — an async\n * iterator that is mid-`await` cannot be interrupted by `return()` alone.\n */\n onCancel?: () => void;\n};\n\n/**\n * Re-emit a stream of session events as an SSE byte stream. Pull-based, so\n * upstream consumption follows downstream demand; cancelling the returned\n * stream fires `onCancel` and ends the upstream iterator.\n */\nexport function sessionEventsToSseStream(\n events: AsyncIterable<SessionEvent>,\n options: SseReStreamOptions = {},\n): ReadableStream<Uint8Array> {\n const encoder = new TextEncoder();\n const iterator = events[Symbol.asyncIterator]();\n let heartbeat: ReturnType<typeof setInterval> | undefined;\n let cancelled = false;\n\n const stopHeartbeat = (): void => {\n if (heartbeat !== undefined) {\n clearInterval(heartbeat);\n heartbeat = undefined;\n }\n };\n\n return new ReadableStream<Uint8Array>({\n start: (controller) => {\n if (options.heartbeatMs !== undefined) {\n heartbeat = setInterval(() => {\n try {\n controller.enqueue(encoder.encode(\": ping\\n\\n\"));\n } catch {\n stopHeartbeat();\n }\n }, options.heartbeatMs);\n }\n },\n pull: async (controller) => {\n let result: IteratorResult<SessionEvent, unknown>;\n try {\n result = await iterator.next();\n } catch (error) {\n stopHeartbeat();\n throw error;\n }\n if (cancelled) {\n return;\n }\n if (result.done) {\n stopHeartbeat();\n controller.close();\n return;\n }\n controller.enqueue(encoder.encode(formatSseEvent(result.value)));\n },\n cancel: () => {\n cancelled = true;\n stopHeartbeat();\n options.onCancel?.();\n // Fire-and-forget: if the iterator is suspended mid-await, return()\n // settles only after onCancel unblocks it; cancel must not hang on it.\n void Promise.resolve(iterator.return?.(undefined)).then(\n () => undefined,\n () => undefined,\n );\n },\n });\n}\n\n/** Wrap an event stream in a ready-to-return SSE `Response`. */\nexport function sessionEventsToSseResponse(\n events: AsyncIterable<SessionEvent>,\n options: SseReStreamOptions = {},\n): Response {\n return new Response(sessionEventsToSseStream(events, options), {\n headers: {\n \"Content-Type\": \"text/event-stream; charset=utf-8\",\n \"Cache-Control\": \"no-cache, no-transform\",\n Connection: \"keep-alive\",\n },\n });\n}\n\n/**\n * Read the resume cursor a reconnecting SSE client sent: the `after` query\n * parameter, or the standard `Last-Event-ID` header (the re-emitted stream\n * sets `id:` to the sequence). Returns 0 (full replay) when absent.\n */\nexport function resumeSequenceFromRequest(request: Request): number {\n const url = new URL(request.url);\n const raw = url.searchParams.get(\"after\") ?? request.headers.get(\"Last-Event-ID\");\n const parsed = raw === null ? 0 : Number(raw);\n return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : 0;\n}\n\nexport type ProxySessionEventStreamOptions = Omit<StreamSessionEventsOptions, \"after\"> & {\n /**\n * Resume cursor. Pass a number, or the incoming browser `Request` to\n * honor its `?after=` / `Last-Event-ID` automatically.\n */\n after?: number | Request;\n /** See {@link SseReStreamOptions.heartbeatMs}. */\n heartbeatMs?: number;\n};\n\n/**\n * One-call proxy: consume the OpenGeni stream server-side and return an SSE\n * `Response` for your own browser clients. Works anywhere WHATWG `Response`\n * is the handler return type (Hono, Next.js route handlers, Bun.serve,\n * Cloudflare Workers, ...).\n *\n * The upstream OpenGeni connection is torn down when the downstream client\n * disconnects, and also when `options.signal` (e.g. the incoming request's\n * signal) aborts.\n */\nexport function proxySessionEventStream(\n client: OpenGeniClient,\n workspaceId: string,\n sessionId: string,\n options: ProxySessionEventStreamOptions = {},\n): Response {\n const { after, heartbeatMs, signal, ...streamOptions } = options;\n const upstream = new AbortController();\n if (signal?.aborted) {\n upstream.abort();\n } else {\n signal?.addEventListener(\"abort\", () => upstream.abort(), { once: true });\n }\n const resolvedAfter = after instanceof Request ? resumeSequenceFromRequest(after) : after ?? 0;\n const events = client.streamEvents(workspaceId, sessionId, {\n ...streamOptions,\n after: resolvedAfter,\n signal: upstream.signal,\n });\n return sessionEventsToSseResponse(events, {\n ...(heartbeatMs !== undefined ? { heartbeatMs } : {}),\n onCancel: () => upstream.abort(),\n });\n}\n","// Hand-written mirrors of the public wire shapes in `@opengeni/contracts`.\n// The SDK keeps zero runtime dependencies so it stays framework-agnostic and\n// publishable on its own; `test/contract-parity.test.ts` pins these types to\n// the contracts package so drift fails the gate instead of shipping.\n\nexport type SessionStatus =\n | \"queued\"\n | \"running\"\n | \"idle\"\n | \"requires_action\"\n | \"failed\"\n | \"cancelled\";\n\nexport type SandboxBackend = \"docker\" | \"modal\" | \"local\" | \"none\";\n\nexport type ReasoningEffort = \"none\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\";\n\nexport type RepositoryResourceRef = {\n kind: \"repository\";\n uri: string;\n ref: string;\n mountPath?: string | undefined;\n subpath?: string | undefined;\n githubInstallationId?: number | undefined;\n githubRepositoryId?: number | undefined;\n};\n\nexport type FileResourceRef = {\n kind: \"file\";\n fileId: string;\n mountPath?: string | undefined;\n};\n\nexport type ResourceRef = RepositoryResourceRef | FileResourceRef;\n\nexport type ToolRef = {\n kind: \"mcp\";\n id: string;\n};\n\nexport type GoalSpec = {\n text: string;\n successCriteria?: string | undefined;\n maxAutoContinuations?: number | undefined;\n};\n\nexport type Session = {\n id: string;\n workspaceId: string;\n accountId: string;\n status: SessionStatus;\n initialMessage: string;\n resources: ResourceRef[];\n tools: ToolRef[];\n metadata: Record<string, unknown>;\n model: string;\n sandboxBackend: SandboxBackend;\n environmentId: string | null;\n firstPartyMcpPermissions: string[] | null;\n createIdempotencyKey: string | null;\n temporalWorkflowId: string | null;\n activeTurnId: string | null;\n lastSequence: number;\n createdAt: string;\n updatedAt: string;\n};\n\nexport type SessionTurnStatus =\n | \"queued\"\n | \"running\"\n | \"requires_action\"\n | \"completed\"\n | \"failed\"\n | \"cancelled\";\n\nexport type SessionTurnSource = \"user\" | \"scheduled_task\" | \"api\" | \"goal\";\n\nexport type SessionTurn = {\n id: string;\n workspaceId: string;\n sessionId: string;\n triggerEventId: string;\n temporalWorkflowId: string;\n status: SessionTurnStatus;\n source: SessionTurnSource;\n position: number;\n prompt: string;\n resources: ResourceRef[];\n tools: ToolRef[];\n model: string;\n reasoningEffort: ReasoningEffort;\n sandboxBackend: SandboxBackend;\n metadata: Record<string, unknown>;\n startedAt: string | null;\n finishedAt: string | null;\n createdAt: string;\n updatedAt: string;\n};\n\nexport const SESSION_EVENT_TYPES = [\n \"session.created\",\n \"session.status.changed\",\n \"session.requiresAction\",\n \"session.context.compacted\",\n \"session.context.cleared\",\n \"user.message\",\n \"user.interrupt\",\n \"user.approvalDecision\",\n \"turn.queued\",\n \"turn.updated\",\n \"turn.started\",\n \"turn.completed\",\n \"turn.failed\",\n \"turn.cancelled\",\n \"turn.preempted\",\n \"agent.message.delta\",\n \"agent.message.completed\",\n \"agent.reasoning.delta\",\n \"agent.toolCall.created\",\n \"agent.toolCall.output\",\n \"agent.updated\",\n \"sandbox.operation.started\",\n \"sandbox.operation.completed\",\n \"sandbox.operation.failed\",\n \"sandbox.command.output.delta\",\n \"artifact.created\",\n \"goal.set\",\n \"goal.updated\",\n \"goal.completed\",\n \"goal.paused\",\n \"goal.resumed\",\n \"goal.continuation\",\n] as const;\n\nexport type KnownSessionEventType = (typeof SESSION_EVENT_TYPES)[number];\n\n/**\n * Event types the SDK knows about today, kept open so a newer OpenGeni server\n * can introduce event types without breaking older SDK consumers.\n */\nexport type SessionEventType = KnownSessionEventType | (string & {});\n\nexport type SessionEvent = {\n id: string;\n workspaceId: string;\n sessionId: string;\n /** Per-session sequence number: positive, contiguous, strictly increasing. */\n sequence: number;\n type: SessionEventType;\n payload: unknown;\n occurredAt: string;\n clientEventId?: string | null | undefined;\n turnId?: string | null | undefined;\n};\n\n// Payload shapes for the high-traffic event types. `SessionEvent.payload` is\n// `unknown` on the wire; these are the documented shapes producers emit today.\nexport type AgentTextDeltaPayload = { text: string };\nexport type AgentMessageCompletedPayload = { text: string };\nexport type AgentToolCallCreatedPayload = {\n id: string | null;\n name: string;\n arguments: unknown;\n raw?: unknown | undefined;\n};\nexport type AgentToolCallOutputPayload = { id: string | null; output: unknown };\nexport type SessionStatusChangedPayload = { status: SessionStatus };\n\nexport type ScheduledTaskStatus = \"active\" | \"paused\";\n\nexport type ScheduledTaskRunMode = \"new_session_per_run\" | \"reusable_session\";\n\nexport type ScheduledTaskOverlapPolicy = \"allow_concurrent\" | \"skip\" | \"buffer_one\";\n\nexport type ScheduledTaskDayOfWeek =\n | \"SUNDAY\"\n | \"MONDAY\"\n | \"TUESDAY\"\n | \"WEDNESDAY\"\n | \"THURSDAY\"\n | \"FRIDAY\"\n | \"SATURDAY\";\n\nexport type ScheduledTaskScheduleSpec =\n | { type: \"once\"; runAt: string; timeZone: string }\n | {\n type: \"interval\";\n everySeconds: number;\n startAt?: string | undefined;\n endAt?: string | undefined;\n }\n | {\n type: \"calendar\";\n timeZone: string;\n hour: number;\n minute: number;\n daysOfWeek?: ScheduledTaskDayOfWeek[] | undefined;\n };\n\nexport type ScheduledTaskAgentConfig = {\n prompt: string;\n resources: ResourceRef[];\n tools: ToolRef[];\n metadata: Record<string, unknown>;\n model?: string | undefined;\n reasoningEffort?: ReasoningEffort | undefined;\n sandboxBackend?: SandboxBackend | undefined;\n goal?: GoalSpec | undefined;\n};\n\nexport type ScheduledTask = {\n id: string;\n accountId: string;\n workspaceId: string;\n name: string;\n status: ScheduledTaskStatus;\n schedule: ScheduledTaskScheduleSpec;\n temporalScheduleId: string;\n runMode: ScheduledTaskRunMode;\n overlapPolicy: ScheduledTaskOverlapPolicy;\n agentConfig: ScheduledTaskAgentConfig;\n reusableSessionId: string | null;\n environmentId: string | null;\n metadata: Record<string, unknown>;\n createdAt: string;\n updatedAt: string;\n};\n\nexport type CreateSessionRequest = {\n initialMessage: string;\n resources?: ResourceRef[] | undefined;\n tools?: ToolRef[] | undefined;\n metadata?: Record<string, unknown> | undefined;\n model?: string | undefined;\n reasoningEffort?: ReasoningEffort | undefined;\n sandboxBackend?: SandboxBackend | undefined;\n environmentId?: string | undefined;\n goal?: GoalSpec | undefined;\n clientEventId?: string | undefined;\n // Workspace-scoped CREATE idempotency key: forward a STABLE value to make a\n // double-submit/retry of the same logical create collapse to one session.\n // Distinct from the per-call clientEventId.\n idempotencyKey?: string | undefined;\n firstPartyMcpPermissions?: string[] | undefined;\n};\n\n// --- Access, workspaces, API keys -------------------------------------------\n\nexport const KNOWN_PERMISSIONS = [\n \"account:read\",\n \"account:admin\",\n \"members:manage\",\n \"workspace:create\",\n \"billing:read\",\n \"billing:manage\",\n \"workspace:read\",\n \"workspace:admin\",\n \"sessions:create\",\n \"sessions:read\",\n \"sessions:control\",\n \"files:upload\",\n \"files:read\",\n \"documents:manage\",\n \"documents:search\",\n \"scheduled_tasks:manage\",\n \"scheduled_tasks:run\",\n \"github:manage\",\n \"github:use\",\n \"api_keys:manage\",\n \"environments:manage\",\n \"environments:use\",\n \"goals:manage\",\n] as const;\n\nexport type KnownPermission = (typeof KNOWN_PERMISSIONS)[number];\n\n/**\n * Permissions the SDK knows about today, kept open so a newer OpenGeni server\n * can introduce permissions without breaking older SDK consumers.\n */\nexport type Permission = KnownPermission | (string & {});\n\nexport type ProductAccessMode = \"local\" | \"configured\" | \"managed\";\n\n/**\n * One model a client may select at send time, plus the provider that serves it.\n * The wire API (`responses` | `chat`) lets a client reason about provider\n * capabilities; the provider id/label drive a picker's grouping. Mirrors the\n * `ClientModel` shape projected into `ClientConfig` by the server.\n */\nexport type ClientModel = {\n id: string;\n label: string;\n /** Provider id (e.g. `openai`, `azure`, or a registry provider id). */\n provider: string;\n providerLabel: string;\n api: \"responses\" | \"chat\";\n contextWindowTokens?: number | undefined;\n};\n\n/**\n * How a deployment expects clients to authenticate to it, surfaced so a UI can\n * wire up the right header/cookie without prior knowledge of the host setup.\n * Discriminated on `mode`; `none` is the back-compat default.\n */\nexport type ClientAuthConfig =\n | { mode: \"none\" }\n | { mode: \"deploymentKey\"; headerName: \"x-opengeni-access-key\" }\n | { mode: \"configuredToken\"; headerName: \"authorization\"; scheme: \"bearer\" }\n | { mode: \"managedSession\"; session: \"cookie\" };\n\n/**\n * Public, unauthenticated-by-default client bootstrap config returned by\n * `GET /v1/config/client`: which models + reasoning efforts are exposed, the\n * MCP servers and file-upload limits a composer should offer, and how the\n * deployment expects the client to authenticate. `allowedModels` is kept for\n * back-compat; `models` carries the richer provider-grouped list for a picker.\n */\nexport type ClientConfig = {\n deploymentRevision: string;\n defaultModel: string;\n allowedModels: string[];\n models: ClientModel[];\n defaultReasoningEffort: ReasoningEffort;\n allowedReasoningEfforts: ReasoningEffort[];\n mcpServers: { id: string; name: string }[];\n fileUploads: { enabled: boolean; maxSizeBytes: number };\n productAccessMode: ProductAccessMode;\n auth: ClientAuthConfig;\n};\n\nexport type AccountRole = \"owner\" | \"admin\" | \"member\";\n\nexport type AccountGrant = {\n accountId: string;\n subjectId: string;\n subjectLabel?: string | undefined;\n role?: AccountRole | undefined;\n permissions: Permission[];\n metadata?: Record<string, unknown> | undefined;\n};\n\nexport type AccessGrant = {\n workspaceId: string;\n accountId: string;\n subjectId: string;\n subjectLabel?: string | undefined;\n permissions: Permission[];\n metadata?: Record<string, unknown> | undefined;\n};\n\nexport type AccessContext = {\n mode: ProductAccessMode;\n subjectId: string;\n subjectLabel?: string | undefined;\n accountGrants: AccountGrant[];\n workspaceGrants: AccessGrant[];\n defaultAccountId: string | null;\n defaultWorkspaceId: string | null;\n};\n\nexport type Workspace = {\n id: string;\n accountId: string;\n name: string;\n slug: string | null;\n externalSource: string | null;\n externalId: string | null;\n agentInstructions: string | null;\n createdAt: string;\n updatedAt: string;\n};\n\nexport type CreateWorkspaceRequest = {\n accountId?: string | undefined;\n name: string;\n slug?: string | undefined;\n externalSource?: string | undefined;\n externalId?: string | undefined;\n agentInstructions?: string | null | undefined;\n};\n\nexport type UpdateWorkspaceRequest = {\n name?: string | undefined;\n slug?: string | null | undefined;\n agentInstructions?: string | null | undefined;\n};\n\nexport type ApiKey = {\n id: string;\n accountId: string;\n workspaceId: string | null;\n name: string;\n prefix: string;\n permissions: Permission[];\n expiresAt: string | null;\n revokedAt: string | null;\n lastUsedAt: string | null;\n createdAt: string;\n updatedAt: string;\n};\n\nexport type CreateApiKeyRequest = {\n name: string;\n permissions: Permission[];\n expiresAt?: string | undefined;\n};\n\nexport type CreateApiKeyResponse = {\n apiKey: ApiKey;\n /** The full secret token — shown once at creation, never returned again. */\n token: string;\n};\n\nexport type ListApiKeysResponse = {\n apiKeys: ApiKey[];\n};\n\n// A person (or API key) with access to a workspace. `subjectId` is\n// `user:<betterAuthUserId>` or `api_key:<id>`; the People surface lists the\n// `user:` subjects (api_key subjects belong to the API keys section).\nexport type WorkspaceMember = {\n subjectId: string;\n subjectLabel: string | null;\n role: string;\n permissions: Permission[];\n createdAt: string;\n};\n\nexport type ListWorkspaceMembersResponse = {\n members: WorkspaceMember[];\n};\n\nexport type AddWorkspaceMemberRequest = {\n email: string;\n role?: string | undefined;\n permissions: Permission[];\n};\n\nexport type UpdateWorkspaceMemberRequest = {\n role?: string | undefined;\n permissions: Permission[];\n};\n\n// --- Goals -------------------------------------------------------------------\n\nexport type SessionGoalStatus = \"active\" | \"paused\" | \"completed\";\n\nexport type SessionGoalCreatedBy = \"api\" | \"agent\" | \"scheduled_task\";\n\nexport type SessionGoal = {\n id: string;\n accountId: string;\n workspaceId: string;\n sessionId: string;\n status: SessionGoalStatus;\n text: string;\n successCriteria: string | null;\n evidence: string | null;\n rationale: string | null;\n pausedReason: string | null;\n createdBy: SessionGoalCreatedBy;\n version: number;\n autoContinuations: number;\n noProgressStreak: number;\n maxAutoContinuations: number | null;\n metadata: Record<string, unknown>;\n createdAt: string;\n updatedAt: string;\n};\n\nexport type UpdateSessionGoalRequest = {\n status: \"paused\" | \"active\";\n rationale?: string | undefined;\n};\n\n// --- Operator context controls (/clear, /compact) ----------------------------\n\n/** Outcome of a manual /compact trigger. */\nexport type CompactSessionContextResult = {\n /**\n * queued: a client-side (Azure) compaction will run before the next turn.\n * noop: nothing to do (server-managed provider, mode off, or no history).\n */\n status: \"queued\" | \"noop\";\n message: string;\n};\n\n// --- Turn queue --------------------------------------------------------------\n\nexport type UpdateSessionTurnRequest = {\n prompt?: string | undefined;\n resources?: ResourceRef[] | undefined;\n tools?: ToolRef[] | undefined;\n model?: string | undefined;\n reasoningEffort?: ReasoningEffort | undefined;\n sandboxBackend?: SandboxBackend | undefined;\n metadata?: Record<string, unknown> | undefined;\n};\n\n// --- Scheduled tasks: requests + runs ----------------------------------------\n\n/** Input shape for agent config on create/update (server applies defaults). */\nexport type ScheduledTaskAgentConfigInput = {\n prompt: string;\n resources?: ResourceRef[] | undefined;\n tools?: ToolRef[] | undefined;\n metadata?: Record<string, unknown> | undefined;\n model?: string | undefined;\n reasoningEffort?: ReasoningEffort | undefined;\n sandboxBackend?: SandboxBackend | undefined;\n goal?: GoalSpec | undefined;\n};\n\nexport type CreateScheduledTaskRequest = {\n name: string;\n schedule: ScheduledTaskScheduleSpec;\n runMode?: ScheduledTaskRunMode | undefined;\n overlapPolicy?: ScheduledTaskOverlapPolicy | undefined;\n agentConfig: ScheduledTaskAgentConfigInput;\n status?: ScheduledTaskStatus | undefined;\n environmentId?: string | null | undefined;\n metadata?: Record<string, unknown> | undefined;\n};\n\nexport type UpdateScheduledTaskRequest = {\n name?: string | undefined;\n schedule?: ScheduledTaskScheduleSpec | undefined;\n runMode?: ScheduledTaskRunMode | undefined;\n overlapPolicy?: ScheduledTaskOverlapPolicy | undefined;\n agentConfig?: ScheduledTaskAgentConfigInput | undefined;\n status?: ScheduledTaskStatus | undefined;\n environmentId?: string | null | undefined;\n metadata?: Record<string, unknown> | undefined;\n};\n\nexport type ScheduledTaskRunStatus = \"queued\" | \"dispatched\" | \"failed\";\n\nexport type ScheduledTaskTriggerType = \"scheduled\" | \"manual\";\n\nexport type ScheduledTaskRun = {\n id: string;\n accountId: string;\n workspaceId: string;\n taskId: string;\n status: ScheduledTaskRunStatus;\n triggerType: ScheduledTaskTriggerType;\n scheduledAt: string | null;\n firedAt: string;\n sessionId: string | null;\n triggerEventId: string | null;\n error: string | null;\n createdAt: string;\n updatedAt: string;\n};\n\n// --- Environments -------------------------------------------------------------\n\n/**\n * Variable values are write-only by design: the API never returns a value, so\n * reads expose name + version metadata only. Values are decrypted exclusively\n * inside the worker at sandbox materialization time.\n */\nexport type WorkspaceEnvironmentVariableMetadata = {\n name: string;\n version: number;\n createdAt: string;\n updatedAt: string;\n};\n\nexport type WorkspaceEnvironment = {\n id: string;\n accountId: string;\n workspaceId: string;\n name: string;\n description: string | null;\n variables: WorkspaceEnvironmentVariableMetadata[];\n createdAt: string;\n updatedAt: string;\n};\n\nexport type CreateWorkspaceEnvironmentRequest = {\n name: string;\n description?: string | undefined;\n /** Initial variables. Values are write-only: they never come back on reads. */\n variables?: { name: string; value: string }[] | undefined;\n};\n\nexport type UpdateWorkspaceEnvironmentRequest = {\n name?: string | undefined;\n description?: string | null | undefined;\n};\n\n// --- Files ---------------------------------------------------------------------\n\nexport type FileStatus = \"pending_upload\" | \"ready\" | \"failed\" | \"expired\" | \"deleted\";\n\nexport type FileAsset = {\n id: string;\n workspaceId: string;\n status: FileStatus;\n filename: string;\n safeFilename: string;\n contentType: string;\n sizeBytes: number;\n sha256: string | null;\n bucket: string;\n objectKey: string;\n createdAt: string;\n updatedAt: string;\n};\n\nexport type CreateFileUploadRequest = {\n filename: string;\n contentType: string;\n sizeBytes: number;\n sha256?: string | undefined;\n};\n\nexport type CreateFileUploadResponse = {\n fileId: string;\n uploadId: string;\n /** Pre-signed PUT URL for the file bytes (direct to object storage). */\n putUrl: string;\n /** Headers that MUST be sent with the PUT for the signature to validate. */\n requiredHeaders: Record<string, string>;\n expiresAt: string;\n maxSizeBytes: number;\n};\n\nexport type CompleteFileUploadResponse = {\n file: FileAsset;\n};\n\nexport type FileDownloadUrlResponse = {\n url: string;\n expiresAt: string;\n};\n\n/** Bytes accepted by the `uploadFile` helper. */\nexport type FileUploadData = Blob | ArrayBuffer | Uint8Array | string;\n\nexport type UploadFileInput = {\n filename: string;\n contentType: string;\n data: FileUploadData;\n sha256?: string | undefined;\n};\n\n// --- Documents -------------------------------------------------------------------\n\nexport type DocumentStatus = \"queued\" | \"indexing\" | \"ready\" | \"failed\";\n\nexport type DocumentBase = {\n id: string;\n workspaceId: string;\n name: string;\n description: string | null;\n createdAt: string;\n updatedAt: string;\n};\n\nexport type Document = {\n id: string;\n workspaceId: string;\n baseId: string;\n fileId: string;\n status: DocumentStatus;\n title: string;\n parser: string;\n chunkCount: number;\n error: string | null;\n createdAt: string;\n updatedAt: string;\n};\n\nexport type DocumentSearchResult = {\n chunkId: string;\n workspaceId: string;\n documentId: string;\n baseId: string;\n fileId: string;\n title: string;\n text: string;\n score: number;\n chunkIndex: number;\n metadata: Record<string, unknown>;\n};\n\nexport type CreateDocumentBaseRequest = {\n name: string;\n description?: string | undefined;\n};\n\nexport type DocumentSearchRequest = {\n query: string;\n limit?: number | undefined;\n};\n\nexport type DocumentSearchResponse = {\n results: DocumentSearchResult[];\n};\n\n// --- Capability packs ---------------------------------------------------------\n\nexport type CapabilityPackConnectorAuthModel =\n | \"oauth2_authorization_code_pkce\"\n | \"oauth2_authorization_code\"\n | \"api_key\"\n | \"credential_ref\";\n\nexport type CapabilityPackConnector = {\n id: string;\n name: string;\n category: string;\n authModel: CapabilityPackConnectorAuthModel;\n providers: string[];\n scopes: string[];\n required: boolean;\n metadata: Record<string, unknown>;\n};\n\nexport type CapabilityPackKnowledge = {\n type: \"document_base\";\n id: string;\n name: string;\n description: string | null;\n required: boolean;\n};\n\nexport type CapabilityPackScheduledTaskTemplate = {\n id: string;\n name: string;\n description: string;\n defaultSchedule: ScheduledTaskScheduleSpec;\n defaultRunMode: ScheduledTaskRunMode;\n defaultOverlapPolicy: ScheduledTaskOverlapPolicy;\n prompt?: string | undefined;\n};\n\nexport type CapabilityPackSkillFile = {\n path: string;\n content: string;\n};\n\nexport type CapabilityPackSkill = {\n name: string;\n description?: string | undefined;\n files: CapabilityPackSkillFile[];\n};\n\nexport type CapabilityPackEnvironmentSpec = {\n description: string;\n requiredVariables: string[];\n required: boolean;\n};\n\nexport type CapabilityPack = {\n id: string;\n name: string;\n description: string;\n role: string;\n category: string;\n version: string;\n sandboxImage?: string | undefined;\n skills: CapabilityPackSkill[];\n tools: ToolRef[];\n connectors: CapabilityPackConnector[];\n knowledge: CapabilityPackKnowledge[];\n scheduledTaskTemplates: CapabilityPackScheduledTaskTemplate[];\n environment?: CapabilityPackEnvironmentSpec | undefined;\n metadata: Record<string, unknown>;\n};\n\n/** Input shape for registering a pack manifest (server applies defaults). */\nexport type RegisterCapabilityPackRequest = {\n id: string;\n name: string;\n description: string;\n role: string;\n category: string;\n version: string;\n sandboxImage?: string | undefined;\n skills?: {\n name: string;\n description?: string | undefined;\n files: CapabilityPackSkillFile[];\n }[] | undefined;\n tools?: ToolRef[] | undefined;\n connectors?: {\n id: string;\n name: string;\n category: string;\n authModel: CapabilityPackConnectorAuthModel;\n providers?: string[] | undefined;\n scopes?: string[] | undefined;\n required?: boolean | undefined;\n metadata?: Record<string, unknown> | undefined;\n }[] | undefined;\n knowledge?: {\n type: \"document_base\";\n id: string;\n name: string;\n description?: string | null | undefined;\n required?: boolean | undefined;\n }[] | undefined;\n scheduledTaskTemplates?: {\n id: string;\n name: string;\n description: string;\n defaultSchedule: ScheduledTaskScheduleSpec;\n defaultRunMode?: ScheduledTaskRunMode | undefined;\n defaultOverlapPolicy?: ScheduledTaskOverlapPolicy | undefined;\n prompt?: string | undefined;\n }[] | undefined;\n environment?: {\n description: string;\n requiredVariables?: string[] | undefined;\n required?: boolean | undefined;\n } | undefined;\n metadata?: Record<string, unknown> | undefined;\n};\n\nexport type WorkspaceRegisteredPack = {\n accountId: string;\n workspaceId: string;\n pack: CapabilityPack;\n createdAt: string;\n updatedAt: string;\n};\n\nexport type PackInstallationStatus = \"active\" | \"disabled\";\n\nexport type PackInstallation = {\n id: string;\n accountId: string;\n workspaceId: string;\n packId: string;\n status: PackInstallationStatus;\n metadata: Record<string, unknown>;\n enabledAt: string;\n updatedAt: string;\n};\n\nexport type EnablePackRequest = {\n environmentId?: string | undefined;\n metadata?: Record<string, unknown> | undefined;\n};\n\nexport type ListPacksResponse = {\n packs: CapabilityPack[];\n installations: PackInstallation[];\n};\n\nexport type GetPackResponse = {\n pack: CapabilityPack;\n installation: PackInstallation | null;\n};\n\n// --- Capabilities ---------------------------------------------------------------\n\nexport type CapabilityKind = \"pack\" | \"mcp\" | \"api\" | \"skill\" | \"plugin\";\n\nexport type CapabilitySource = \"built_in\" | \"configured\" | \"public_registry\" | \"manual\";\n\nexport type CapabilityInstallationStatus = \"active\" | \"disabled\";\n\nexport type CapabilityRuntime = {\n available: boolean;\n mcpServerId?: string | undefined;\n transport?: string | undefined;\n notes: string | null;\n};\n\nexport type CapabilityCatalogItem = {\n id: string;\n accountId?: string | undefined;\n workspaceId?: string | undefined;\n kind: CapabilityKind;\n source: CapabilitySource;\n name: string;\n description: string | null;\n category: string;\n tags: string[];\n homepageUrl: string | null;\n endpointUrl: string | null;\n installUrl: string | null;\n authModel: string | null;\n tools: ToolRef[];\n runtime: CapabilityRuntime;\n enabled: boolean;\n enabledReason: string | null;\n metadata: Record<string, unknown>;\n createdAt?: string | undefined;\n updatedAt?: string | undefined;\n};\n\nexport type CapabilityInstallation = {\n id: string;\n accountId: string;\n workspaceId: string;\n capabilityId: string;\n kind: CapabilityKind;\n status: CapabilityInstallationStatus;\n config: Record<string, unknown>;\n metadata: Record<string, unknown>;\n enabledAt: string;\n updatedAt: string;\n};\n\nexport type CapabilityCatalogResponse = {\n items: CapabilityCatalogItem[];\n installations: CapabilityInstallation[];\n};\n\nexport type CreateCapabilityCatalogItemRequest = {\n id?: string | undefined;\n kind: Exclude<CapabilityKind, \"pack\">;\n source?: CapabilitySource | undefined;\n name: string;\n description?: string | undefined;\n category?: string | undefined;\n tags?: string[] | undefined;\n homepageUrl?: string | undefined;\n endpointUrl?: string | undefined;\n installUrl?: string | undefined;\n authModel?: string | undefined;\n metadata?: Record<string, unknown> | undefined;\n};\n\nexport type EnableCapabilityRequest = {\n config?: Record<string, unknown> | undefined;\n metadata?: Record<string, unknown> | undefined;\n /**\n * Credential headers for remote MCP capabilities. Write-only: encrypted at\n * rest, injected only into the runtime MCP client, never returned by the\n * API (responses expose header names only).\n */\n headers?: Record<string, string> | undefined;\n /**\n * Initial environment attachment for kind=pack capabilities — mirrors the\n * dedicated POST /packs/:id/enable body. Required to enable an\n * environment.required pack through this unified path; ignored otherwise.\n */\n environmentId?: string | undefined;\n};\n\nexport type DiscoverMcpCapabilitiesResponse = {\n items: CapabilityCatalogItem[];\n source: \"official_mcp_registry\";\n sourceUrl: string;\n};\n\n// --- GitHub ---------------------------------------------------------------------\n\nexport type GitHubRepository = {\n id: number;\n installationId: number;\n fullName: string;\n name: string;\n private: boolean;\n htmlUrl: string;\n cloneUrl: string;\n defaultBranch: string;\n accountLogin: string;\n accountType: string | null;\n};\n\nexport type GitHubAppInfo = {\n configured: boolean;\n appId: string | null;\n clientId: string | null;\n appSlug: string | null;\n /** Ready-to-open GitHub install URL (carries the signed state), if configured. */\n installUrl: string | null;\n /** Setting names still missing when `configured` is false. */\n missing: string[];\n};\n\nexport type GitHubRepositoriesResponse = {\n repositories: GitHubRepository[];\n};\n\nexport type CreateGitHubAppManifestRequest = {\n appName?: string | undefined;\n organization?: string | undefined;\n public?: boolean | undefined;\n includeCiPermissions?: boolean | undefined;\n};\n\nexport type CreateGitHubAppManifestResponse = {\n /** GitHub URL to POST the manifest to (personal or organization flow). */\n actionUrl: string;\n state: string;\n manifest: Record<string, unknown>;\n};\n\n// --- Billing --------------------------------------------------------------------\n\nexport type BillingMode = \"disabled\" | \"stripe\";\n\nexport type EntitlementsMode = \"none\" | \"static\" | \"managed\";\n\nexport type BillingBalance = {\n accountId: string;\n balanceMicros: number;\n currency: \"usd\";\n updatedAt: string;\n};\n\nexport const KNOWN_USAGE_EVENT_TYPES = [\n \"agent_run.created\",\n \"agent_run.completed\",\n \"model.tokens\",\n \"model.cost\",\n \"file.uploaded\",\n \"file.deleted\",\n \"document.indexed\",\n \"scheduled_task.fired\",\n \"api_key.request\",\n] as const;\n\nexport type KnownUsageEventType = (typeof KNOWN_USAGE_EVENT_TYPES)[number];\n\nexport type UsageEventType = KnownUsageEventType | (string & {});\n\nexport type UsageEvent = {\n id: string;\n workspaceId: string;\n accountId: string;\n subjectId: string | null;\n eventType: UsageEventType;\n quantity: number;\n unit: string;\n sourceResourceType: string | null;\n sourceResourceId: string | null;\n idempotencyKey: string;\n occurredAt: string;\n recordedAt: string;\n exportedToBillingAt: string | null;\n billingProviderEventId: string | null;\n};\n\nexport type EntitlementValue = boolean | string | number | string[];\n\nexport type Entitlements = Record<string, EntitlementValue>;\n\nexport type BillingSummary = {\n mode: BillingMode;\n balance: BillingBalance;\n};\n\nexport type BillingUsageResponse = {\n balance: BillingBalance;\n usage: UsageEvent[];\n};\n\nexport type BillingEntitlementsResponse = {\n accountId: string;\n mode: EntitlementsMode;\n entitlements: Entitlements;\n};\n\nexport type CreateCheckoutRequest = {\n accountId?: string | undefined;\n /** USD amount with cent precision (server enforces min/max). */\n amountUsd: number;\n successUrl?: string | undefined;\n cancelUrl?: string | undefined;\n};\n\nexport type CreateCheckoutResponse = {\n checkoutSessionId: string;\n url: string;\n};\n\nexport type UserMessageEventInput = {\n type: \"user.message\";\n clientEventId?: string | undefined;\n payload: {\n text: string;\n resources?: ResourceRef[] | undefined;\n tools?: ToolRef[] | undefined;\n model?: string | undefined;\n reasoningEffort?: ReasoningEffort | undefined;\n };\n};\n\nexport type UserInterruptEventInput = {\n type: \"user.interrupt\";\n clientEventId?: string | undefined;\n payload?: { reason?: string | undefined } | undefined;\n};\n\nexport type UserApprovalDecisionEventInput = {\n type: \"user.approvalDecision\";\n clientEventId?: string | undefined;\n payload: {\n approvalId: string;\n decision: \"approve\" | \"reject\";\n message?: string | undefined;\n };\n};\n\n/** Control/user events a client may POST to a session's event log. */\nexport type ClientSessionEventInput =\n | UserMessageEventInput\n | UserInterruptEventInput\n | UserApprovalDecisionEventInput;\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,8 +1,13 @@
1
1
  {
2
2
  "name": "@opengeni/sdk",
3
- "version": "0.1.0",
3
+ "version": "0.3.1",
4
4
  "description": "Framework-agnostic TypeScript SDK for the OpenGeni API: typed client, session lifecycle, SSE event streaming with reconnect + replay-by-sequence, and proxy re-streaming helpers.",
5
5
  "license": "Apache-2.0",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/Cloudgeni-ai/opengeni.git",
9
+ "directory": "packages/sdk"
10
+ },
6
11
  "type": "module",
7
12
  "sideEffects": false,
8
13
  "main": "./dist/index.js",
package/src/client.ts CHANGED
@@ -2,6 +2,7 @@ import { OpenGeniApiError } from "./errors";
2
2
  import { streamSessionEvents, type SessionEventStreamTransport, type StreamSessionEventsOptions } from "./stream";
3
3
  import type {
4
4
  AccessContext,
5
+ AddWorkspaceMemberRequest,
5
6
  ApiKey,
6
7
  BillingEntitlementsResponse,
7
8
  BillingSummary,
@@ -9,6 +10,7 @@ import type {
9
10
  CapabilityCatalogItem,
10
11
  CapabilityCatalogResponse,
11
12
  CapabilityInstallation,
13
+ ClientConfig,
12
14
  ClientSessionEventInput,
13
15
  CompactSessionContextResult,
14
16
  CompleteFileUploadResponse,
@@ -39,6 +41,7 @@ import type {
39
41
  GitHubRepositoriesResponse,
40
42
  ListApiKeysResponse,
41
43
  ListPacksResponse,
44
+ ListWorkspaceMembersResponse,
42
45
  PackInstallation,
43
46
  ReasoningEffort,
44
47
  RegisterCapabilityPackRequest,
@@ -54,10 +57,12 @@ import type {
54
57
  UpdateSessionGoalRequest,
55
58
  UpdateSessionTurnRequest,
56
59
  UpdateWorkspaceEnvironmentRequest,
60
+ UpdateWorkspaceMemberRequest,
57
61
  UpdateWorkspaceRequest,
58
62
  UploadFileInput,
59
63
  WorkspaceEnvironment,
60
64
  WorkspaceEnvironmentVariableMetadata,
65
+ WorkspaceMember,
61
66
  WorkspaceRegisteredPack,
62
67
  Workspace,
63
68
  } from "./types";
@@ -400,6 +405,17 @@ export class OpenGeniClient {
400
405
 
401
406
  // --- Access + workspaces -----------------------------------------------------
402
407
 
408
+ /**
409
+ * The deployment's public client bootstrap config: the host-exposed models
410
+ * (provider-grouped in `models`, flat in `allowedModels` for back-compat),
411
+ * reasoning efforts, MCP servers, file-upload limits, and how the client is
412
+ * expected to authenticate. Drives a composer's model picker without prior
413
+ * knowledge of the host setup; safe to call before any auth is established.
414
+ */
415
+ async getClientConfig(): Promise<ClientConfig> {
416
+ return await this.requestJson<ClientConfig>("GET", "/v1/config/client");
417
+ }
418
+
403
419
  /** The caller's access context: subject, account + workspace grants, defaults. */
404
420
  async getAccessContext(): Promise<AccessContext> {
405
421
  return await this.requestJson<AccessContext>("GET", "/v1/access/me");
@@ -421,6 +437,46 @@ export class OpenGeniClient {
421
437
  return await this.requestJson<Workspace>("PATCH", `/v1/workspaces/${workspaceId}`, request);
422
438
  }
423
439
 
440
+ /**
441
+ * Delete a workspace and everything in it. Refused (409) for the account's
442
+ * only workspace and while it still has a running session. Irreversible.
443
+ */
444
+ async deleteWorkspace(workspaceId: string): Promise<void> {
445
+ await this.requestVoid("DELETE", `/v1/workspaces/${workspaceId}`);
446
+ }
447
+
448
+ // --- Members ("People with access") -------------------------------------------
449
+
450
+ /** The workspace's members (user + api_key subjects). */
451
+ async listWorkspaceMembers(workspaceId: string): Promise<WorkspaceMember[]> {
452
+ const response = await this.requestJson<ListWorkspaceMembersResponse>("GET", `/v1/workspaces/${workspaceId}/members`);
453
+ return response.members;
454
+ }
455
+
456
+ /**
457
+ * Add an already-registered user by email. 404s when no user with that email
458
+ * exists (email invites for unknown users are deferred).
459
+ */
460
+ async addWorkspaceMember(workspaceId: string, request: AddWorkspaceMemberRequest): Promise<WorkspaceMember> {
461
+ return await this.requestJson<WorkspaceMember>("POST", `/v1/workspaces/${workspaceId}/members`, request);
462
+ }
463
+
464
+ async updateWorkspaceMember(workspaceId: string, subjectId: string, request: UpdateWorkspaceMemberRequest): Promise<WorkspaceMember> {
465
+ return await this.requestJson<WorkspaceMember>(
466
+ "PATCH",
467
+ `/v1/workspaces/${workspaceId}/members/${encodeURIComponent(subjectId)}`,
468
+ request,
469
+ );
470
+ }
471
+
472
+ /**
473
+ * Remove a member. Refused (409) for your own membership and for the last
474
+ * member who can still manage the workspace.
475
+ */
476
+ async removeWorkspaceMember(workspaceId: string, subjectId: string): Promise<void> {
477
+ await this.requestVoid("DELETE", `/v1/workspaces/${workspaceId}/members/${encodeURIComponent(subjectId)}`);
478
+ }
479
+
424
480
  // --- Scheduled tasks (write + runs) -------------------------------------------
425
481
 
426
482
  async createScheduledTask(workspaceId: string, request: CreateScheduledTaskRequest): Promise<ScheduledTask> {
package/src/index.ts CHANGED
@@ -23,6 +23,7 @@ export type {
23
23
  AccessGrant,
24
24
  AccountGrant,
25
25
  AccountRole,
26
+ AddWorkspaceMemberRequest,
26
27
  AgentMessageCompletedPayload,
27
28
  AgentTextDeltaPayload,
28
29
  AgentToolCallCreatedPayload,
@@ -48,6 +49,8 @@ export type {
48
49
  CapabilityPackSkillFile,
49
50
  CapabilityRuntime,
50
51
  CapabilitySource,
52
+ ClientConfig,
53
+ ClientModel,
51
54
  CompactSessionContextResult,
52
55
  ClientSessionEventInput,
53
56
  CompleteFileUploadResponse,
@@ -92,6 +95,7 @@ export type {
92
95
  KnownUsageEventType,
93
96
  ListApiKeysResponse,
94
97
  ListPacksResponse,
98
+ ListWorkspaceMembersResponse,
95
99
  PackInstallation,
96
100
  PackInstallationStatus,
97
101
  Permission,
@@ -128,6 +132,7 @@ export type {
128
132
  UpdateSessionGoalRequest,
129
133
  UpdateSessionTurnRequest,
130
134
  UpdateWorkspaceEnvironmentRequest,
135
+ UpdateWorkspaceMemberRequest,
131
136
  UpdateWorkspaceRequest,
132
137
  UploadFileInput,
133
138
  UsageEvent,
@@ -138,5 +143,6 @@ export type {
138
143
  Workspace,
139
144
  WorkspaceEnvironment,
140
145
  WorkspaceEnvironmentVariableMetadata,
146
+ WorkspaceMember,
141
147
  WorkspaceRegisteredPack,
142
148
  } from "./types";
package/src/types.ts CHANGED
@@ -282,6 +282,53 @@ export type Permission = KnownPermission | (string & {});
282
282
 
283
283
  export type ProductAccessMode = "local" | "configured" | "managed";
284
284
 
285
+ /**
286
+ * One model a client may select at send time, plus the provider that serves it.
287
+ * The wire API (`responses` | `chat`) lets a client reason about provider
288
+ * capabilities; the provider id/label drive a picker's grouping. Mirrors the
289
+ * `ClientModel` shape projected into `ClientConfig` by the server.
290
+ */
291
+ export type ClientModel = {
292
+ id: string;
293
+ label: string;
294
+ /** Provider id (e.g. `openai`, `azure`, or a registry provider id). */
295
+ provider: string;
296
+ providerLabel: string;
297
+ api: "responses" | "chat";
298
+ contextWindowTokens?: number | undefined;
299
+ };
300
+
301
+ /**
302
+ * How a deployment expects clients to authenticate to it, surfaced so a UI can
303
+ * wire up the right header/cookie without prior knowledge of the host setup.
304
+ * Discriminated on `mode`; `none` is the back-compat default.
305
+ */
306
+ export type ClientAuthConfig =
307
+ | { mode: "none" }
308
+ | { mode: "deploymentKey"; headerName: "x-opengeni-access-key" }
309
+ | { mode: "configuredToken"; headerName: "authorization"; scheme: "bearer" }
310
+ | { mode: "managedSession"; session: "cookie" };
311
+
312
+ /**
313
+ * Public, unauthenticated-by-default client bootstrap config returned by
314
+ * `GET /v1/config/client`: which models + reasoning efforts are exposed, the
315
+ * MCP servers and file-upload limits a composer should offer, and how the
316
+ * deployment expects the client to authenticate. `allowedModels` is kept for
317
+ * back-compat; `models` carries the richer provider-grouped list for a picker.
318
+ */
319
+ export type ClientConfig = {
320
+ deploymentRevision: string;
321
+ defaultModel: string;
322
+ allowedModels: string[];
323
+ models: ClientModel[];
324
+ defaultReasoningEffort: ReasoningEffort;
325
+ allowedReasoningEfforts: ReasoningEffort[];
326
+ mcpServers: { id: string; name: string }[];
327
+ fileUploads: { enabled: boolean; maxSizeBytes: number };
328
+ productAccessMode: ProductAccessMode;
329
+ auth: ClientAuthConfig;
330
+ };
331
+
285
332
  export type AccountRole = "owner" | "admin" | "member";
286
333
 
287
334
  export type AccountGrant = {
@@ -369,6 +416,32 @@ export type ListApiKeysResponse = {
369
416
  apiKeys: ApiKey[];
370
417
  };
371
418
 
419
+ // A person (or API key) with access to a workspace. `subjectId` is
420
+ // `user:<betterAuthUserId>` or `api_key:<id>`; the People surface lists the
421
+ // `user:` subjects (api_key subjects belong to the API keys section).
422
+ export type WorkspaceMember = {
423
+ subjectId: string;
424
+ subjectLabel: string | null;
425
+ role: string;
426
+ permissions: Permission[];
427
+ createdAt: string;
428
+ };
429
+
430
+ export type ListWorkspaceMembersResponse = {
431
+ members: WorkspaceMember[];
432
+ };
433
+
434
+ export type AddWorkspaceMemberRequest = {
435
+ email: string;
436
+ role?: string | undefined;
437
+ permissions: Permission[];
438
+ };
439
+
440
+ export type UpdateWorkspaceMemberRequest = {
441
+ role?: string | undefined;
442
+ permissions: Permission[];
443
+ };
444
+
372
445
  // --- Goals -------------------------------------------------------------------
373
446
 
374
447
  export type SessionGoalStatus = "active" | "paused" | "completed";