@opengeni/sdk 0.8.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +190 -0
- package/README.md +17 -0
- package/dist/index.d.ts +211 -11
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -1
- package/package.json +3 -9
- package/src/client.ts +38 -2
- package/src/index.ts +22 -0
- package/src/types.ts +231 -1
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/desktop.ts","../src/terminal.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 CodexAccount,\n CodexAccountsResponse,\n CodexRotationSettings,\n CodexConnectionStatus,\n CodexConnectPoll,\n CodexConnectStart,\n CodexUsage,\n CodexUsageMap,\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 // Enrollment UX (design 11): the click-Grant approve-page lookup/deny + headless\n // enroll-token mint.\n DeviceEnrollmentApproveResponse,\n DeviceEnrollmentDenyResponse,\n DeviceEnrollmentLookupResponse,\n MintEnrollTokenResponse,\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 // Bring-your-own-compute: the Machines dashboard + per-machine metrics (M10).\n MachinesResponse,\n MachineView,\n MetricSample,\n MachineMetricsSeriesResponse,\n // Bring-your-own-compute: the user-authenticated active-sandbox swap (M7).\n SwapActiveSandboxRequest,\n SwapActiveSandboxResponse,\n ListWorkspaceMembersResponse,\n PackInstallation,\n ReasoningEffort,\n RegisterCapabilityPackRequest,\n ResourceRef,\n ScheduledTask,\n ScheduledTaskRun,\n Session,\n SessionEvent,\n SessionGoal,\n SessionTurn,\n // Stream surfacing (Phase 5): capability negotiation + viewer lifecycle + config.\n SessionCapabilities,\n AttachViewerRequest,\n AttachViewerResponse,\n AcknowledgeStreamRequest,\n AcknowledgeStreamResponse,\n ViewerHeartbeatRequest,\n ViewerHeartbeatResponse,\n // Channel-A structured services (P4.4).\n FsListRequest,\n FsListResponse,\n FsReadRequest,\n FsReadResponse,\n FsWriteRequest,\n FsWriteResponse,\n FsDeleteRequest,\n FsDeleteResponse,\n FsMoveRequest,\n FsMoveResponse,\n FsMkdirRequest,\n FsMkdirResponse,\n GitStatusRequest,\n GitStatusResponse,\n GitDiffRequest,\n GitDiffResponse,\n GitLogRequest,\n GitLogResponse,\n GitShowRequest,\n GitShowResponse,\n TerminalExecRequest,\n TerminalExecResponse,\n PtyOpenRequest,\n PtyOpenResponse,\n PtyWriteRequest,\n PtyResizeRequest,\n PtyCloseRequest,\n ToolRef,\n UpdateScheduledTaskRequest,\n UpdateSessionGoalRequest,\n UpdateSessionRequest,\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 updateSession(workspaceId: string, sessionId: string, request: UpdateSessionRequest): Promise<Session> {\n return await this.requestJson<Session>(\"PATCH\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}`, request);\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 // --- Bring-your-own-compute: Machines dashboard + metrics (M10) ------------\n\n /**\n * List the workspace's machines (the Machines dashboard). Each enrolled\n * selfhosted machine carries its derived state + latest metrics +\n * sharedSessionCount. Pass `sessionId` for an in-session view, which adds the\n * session's synthetic Modal group box + the active-sandbox pointer.\n */\n async listMachines(workspaceId: string, options: { sessionId?: string } = {}): Promise<MachinesResponse> {\n return await this.requestJson<MachinesResponse>(\"GET\", `/v1/workspaces/${workspaceId}/machines`, undefined, {\n ...(options.sessionId !== undefined ? { sessionId: options.sessionId } : {}),\n });\n }\n\n /**\n * Read the downsampled (~1/min) metrics series for ONE machine over a time\n * window (default 1h). The samples are oldest-first (a left-to-right chart).\n */\n async machineMetricsSeries(\n workspaceId: string,\n enrollmentId: string,\n options: { window?: \"15m\" | \"1h\" | \"6h\" | \"24h\" } = {},\n ): Promise<MetricSample[]> {\n const response = await this.requestJson<MachineMetricsSeriesResponse>(\n \"GET\",\n `/v1/workspaces/${workspaceId}/machines/${enrollmentId}/metrics/series`,\n undefined,\n { ...(options.window !== undefined ? { window: options.window } : {}) },\n );\n return response.samples;\n }\n\n // --- Self-hosted enrollment UX (design 11) --------------------------------\n\n /**\n * Resolve a pending device-enrollment flow by its user_code for the click-Grant\n * approve page (EnrollmentConsent). NO workspace in the path — the server\n * resolves the workspace from the (globally-unique-among-pending) code, then\n * authorizes the caller against it (enrollments:read). Rejects (404) when the\n * code is unknown/expired OR the caller lacks the grant — the two are\n * indistinguishable by design (no cross-workspace disclosure). Does not consume\n * the request.\n */\n async lookupDeviceEnrollment(userCode: string): Promise<DeviceEnrollmentLookupResponse> {\n return await this.requestJson<DeviceEnrollmentLookupResponse>(\"POST\", \"/v1/enrollments/device/lookup\", { userCode });\n }\n\n /**\n * Approve a pending device-enrollment flow (the LOUD consent step). `allowScreenControl`\n * is the authoritative screen-control consent (whole-machine is mandatory/implicit).\n * Lands an enrollment + a selfhosted sandbox and unblocks the agent's poll.\n */\n async approveDeviceEnrollment(\n workspaceId: string,\n request: { userCode: string; allowScreenControl?: boolean },\n ): Promise<DeviceEnrollmentApproveResponse> {\n return await this.requestJson<DeviceEnrollmentApproveResponse>(\n \"POST\",\n `/v1/workspaces/${workspaceId}/enrollments/device/approve`,\n { userCode: request.userCode, allowScreenControl: request.allowScreenControl ?? false },\n );\n }\n\n /** Deny a pending device-enrollment flow (the explicit \"no\" at the approve page). */\n async denyDeviceEnrollment(\n workspaceId: string,\n request: { userCode: string },\n ): Promise<DeviceEnrollmentDenyResponse> {\n return await this.requestJson<DeviceEnrollmentDenyResponse>(\n \"POST\",\n `/v1/workspaces/${workspaceId}/enrollments/device/deny`,\n { userCode: request.userCode },\n );\n }\n\n /**\n * Mint a short-TTL headless enroll token (the `oget_` token) for the fleet /\n * non-interactive enroll path. The returned `token` is SECRET — surface it once\n * with a copy-now warning; it cannot be re-read. `allowScreenControl` bakes the\n * screen-control consent into the token.\n */\n async mintEnrollToken(\n workspaceId: string,\n request: { allowScreenControl?: boolean } = {},\n ): Promise<MintEnrollTokenResponse> {\n return await this.requestJson<MintEnrollTokenResponse>(\n \"POST\",\n `/v1/workspaces/${workspaceId}/enrollments/token`,\n { allowScreenControl: request.allowScreenControl ?? false },\n );\n }\n\n /**\n * Swap a session's active sandbox (the user-authenticated equivalent of the\n * M7 `sandbox_swap` MCP tool). `target` is a `MachineView.sandboxId` from\n * `listMachines`, or \"session\"/\"default\" to swap back to the session's own\n * group box. Validation (ownership/liveness/epoch fence) is server-side; the\n * result echoes the resulting pointer (`swapped: false` + `reason` on a\n * rejected target or a lost epoch fence).\n */\n async swapActiveSandbox(\n workspaceId: string,\n sessionId: string,\n request: SwapActiveSandboxRequest,\n ): Promise<SwapActiveSandboxResponse> {\n return await this.requestJson<SwapActiveSandboxResponse>(\n \"POST\",\n `/v1/workspaces/${workspaceId}/sessions/${sessionId}/active-sandbox`,\n request,\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 /**\n * Replay durable events by sequence, ascending. `before` is exclusive and\n * returns the newest matching window. With `compact`, consecutive delta runs\n * may be coalesced; `payload.coalescedUntil` carries the run's last sequence\n * for resume cursors.\n */\n async listEvents(\n workspaceId: string,\n sessionId: string,\n options: { after?: number; before?: number; limit?: number; compact?: boolean } = {},\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.before !== undefined ? { before: String(options.before) } : {}),\n ...(options.limit !== undefined ? { limit: String(options.limit) } : {}),\n ...(options.compact ? { compact: \"1\" } : {}),\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 // --- Channel-A structured services (P4.4) ------------------------------------\n // FileSystem (Pierre tree), Git (Pierre diff), Terminal (exec + PTY). Each is a\n // synchronous API-direct point query; the fs.changed/git.changed/terminal.pty.*\n // notifications + the PTY output stream arrive on the existing event SSE.\n\n /** FileSystem: list a directory tree (feeds the Pierre file tree). */\n async fsList(workspaceId: string, sessionId: string, request: FsListRequest = {}): Promise<FsListResponse> {\n return await this.requestJson<FsListResponse>(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/fs/list`, request);\n }\n\n /** FileSystem: read a file (text or base64; binary-safe, size-capped). */\n async fsRead(workspaceId: string, sessionId: string, request: FsReadRequest): Promise<FsReadResponse> {\n return await this.requestJson<FsReadResponse>(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/fs/read`, request);\n }\n\n /** FileSystem: write a file (last-writer-wins; emits fs.changed). */\n async fsWrite(workspaceId: string, sessionId: string, request: FsWriteRequest): Promise<FsWriteResponse> {\n return await this.requestJson<FsWriteResponse>(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/fs/write`, request);\n }\n\n /** FileSystem: delete a path (emits fs.changed). */\n async fsDelete(workspaceId: string, sessionId: string, request: FsDeleteRequest): Promise<FsDeleteResponse> {\n return await this.requestJson<FsDeleteResponse>(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/fs/delete`, request);\n }\n\n /** FileSystem: move/rename a path (emits fs.changed; 409 if destination exists and overwrite is false). */\n async fsMove(workspaceId: string, sessionId: string, request: FsMoveRequest): Promise<FsMoveResponse> {\n return await this.requestJson<FsMoveResponse>(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/fs/move`, request);\n }\n\n /** FileSystem: create a directory (emits fs.changed; recursive defaults to true). */\n async fsMkdir(workspaceId: string, sessionId: string, request: FsMkdirRequest): Promise<FsMkdirResponse> {\n return await this.requestJson<FsMkdirResponse>(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/fs/mkdir`, request);\n }\n\n /** Git: working-tree/index status (the Pierre file-status feed). */\n async gitStatus(workspaceId: string, sessionId: string, request: GitStatusRequest = {}): Promise<GitStatusResponse> {\n return await this.requestJson<GitStatusResponse>(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/git/status`, request);\n }\n\n /** Git: structured diff hunks (the Pierre diff feed). */\n async gitDiff(workspaceId: string, sessionId: string, request: GitDiffRequest = {}): Promise<GitDiffResponse> {\n return await this.requestJson<GitDiffResponse>(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/git/diff`, request);\n }\n\n /** Git: commit log. */\n async gitLog(workspaceId: string, sessionId: string, request: GitLogRequest = {}): Promise<GitLogResponse> {\n return await this.requestJson<GitLogResponse>(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/git/log`, request);\n }\n\n /** Git: show a commit (diff vs first parent) or fetch a raw blob at a ref. */\n async gitShow(workspaceId: string, sessionId: string, request: GitShowRequest): Promise<GitShowResponse> {\n return await this.requestJson<GitShowResponse>(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/git/show`, request);\n }\n\n /** Terminal: run a bounded command, returning buffered stdout/stderr inline. */\n async terminalExec(workspaceId: string, sessionId: string, request: TerminalExecRequest): Promise<TerminalExecResponse> {\n return await this.requestJson<TerminalExecResponse>(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/terminal/exec`, request);\n }\n\n /** Terminal: open an interactive PTY. Output streams on the event SSE as\n * terminal.pty.output.delta; drive it with terminalPtyWrite. */\n async terminalPtyOpen(workspaceId: string, sessionId: string, request: PtyOpenRequest = {}): Promise<PtyOpenResponse> {\n return await this.requestJson<PtyOpenResponse>(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/terminal/pty`, request);\n }\n\n /** Terminal: send stdin to an open PTY (output rides A1). */\n async terminalPtyWrite(workspaceId: string, sessionId: string, request: PtyWriteRequest): Promise<void> {\n await this.requestVoid(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/terminal/pty/write`, request);\n }\n\n /** Terminal: resize an open PTY. */\n async terminalPtyResize(workspaceId: string, sessionId: string, request: PtyResizeRequest): Promise<void> {\n await this.requestVoid(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/terminal/pty/resize`, request);\n }\n\n /** Terminal: close an open PTY (idempotent). */\n async terminalPtyClose(workspaceId: string, sessionId: string, request: PtyCloseRequest): Promise<void> {\n await this.requestVoid(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/terminal/pty/close`, request);\n }\n\n // --- Stream surfacing: capability negotiation + viewer lifecycle (Phase 5) ---\n // The capability doc is the single source of UI truth (degradation is always a\n // value, never a crash). The desktop pixel plane (Channel B) is gated behind an\n // un-redacted-acknowledgment + a viewer holder; the structured terminal/files/\n // git surfaces (Channel A) ride the methods above and the event SSE.\n\n /** Read the negotiated capability doc for a session WITHOUT acquiring a viewer\n * holder (no warm, no spawn). Drives capability-gated rendering: which\n * surfaces mount, the per-surface unavailability reasons, and the lease\n * liveness the client polls on while `cold`/`warming`. The desktop URL/token\n * are minted in-process only when the box is warm AND the principal has\n * acknowledged the un-redacted plane. */\n async getStreamCapabilities(workspaceId: string, sessionId: string): Promise<SessionCapabilities> {\n return await this.requestJson<SessionCapabilities>(\n \"GET\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/stream-capabilities`);\n }\n\n /** Record the calling principal's acknowledgment of the un-redacted desktop\n * pixel plane (and, when the box is shared, the shared-exposure disclosure).\n * The desktop viewer-attach path returns 409 until this is recorded. */\n async acknowledgeStream(\n workspaceId: string, sessionId: string, request: AcknowledgeStreamRequest = {},\n ): Promise<AcknowledgeStreamResponse> {\n return await this.requestJson<AcknowledgeStreamResponse>(\n \"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/stream-capabilities/acknowledge`, request);\n }\n\n /** Attach a viewer holder (refcounted liveness — keeps the box warm while\n * watched/used), spinning the box up in-process when cold, and mint the scoped\n * direct-to-provider URLs for the requested plane(s). `request.desktop:true`\n * opts into the un-redacted pixel plane and mints the noVNC URL — that plane\n * alone throws `OpenGeniApiError(409)` when the un-redacted/shared\n * acknowledgment is missing (the consent gate). A terminal-only attach\n * (`desktop` omitted/false) warms the box + mints the pty-ws terminal cell with\n * NO consent gate. An omitted `viewerId` mints a fresh one. */\n async attachViewer(\n workspaceId: string, sessionId: string, request: AttachViewerRequest = {},\n ): Promise<AttachViewerResponse> {\n return await this.requestJson<AttachViewerResponse>(\n \"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/viewers`, request);\n }\n\n /** Heartbeat a viewer holder (Channel-A app-level liveness). A closed laptop\n * stops sending these → the reaper drops the holder within ~90s. Echoes\n * `leaseEpoch` so a superseded epoch is rejected (`alive:false` → re-attach). */\n async heartbeatViewer(\n workspaceId: string, sessionId: string, viewerId: string, request: ViewerHeartbeatRequest,\n ): Promise<ViewerHeartbeatResponse> {\n return await this.requestJson<ViewerHeartbeatResponse>(\n \"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/viewers/${viewerId}/heartbeat`, request);\n }\n\n /** Detach a viewer (delete this holder; idempotent delete-my-row). */\n async detachViewer(workspaceId: string, sessionId: string, viewerId: string): Promise<void> {\n await this.requestVoid(\"DELETE\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/viewers/${viewerId}`);\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 /**\n * Delete a document from a base. Removes the document row and its indexed\n * chunks while leaving the uploaded file asset available for other uses.\n */\n async deleteDocument(workspaceId: string, baseId: string, documentId: string): Promise<void> {\n await this.requestVoid(\n \"DELETE\",\n `/v1/workspaces/${workspaceId}/document-bases/${baseId}/documents/${documentId}`,\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 // --- Codex (ChatGPT) subscription (workspace-scoped) --------------------------------------------\n\n /** Connection state + the codex models the workspace may select (empty until connected). */\n async codexStatus(workspaceId: string): Promise<CodexConnectionStatus> {\n return await this.requestJson<CodexConnectionStatus>(\"GET\", `/v1/workspaces/${workspaceId}/codex/status`);\n }\n\n /** Begin device-code login: show `userCode` at `verificationUri`, then poll with `state`. */\n async codexConnectStart(workspaceId: string): Promise<CodexConnectStart> {\n return await this.requestJson<CodexConnectStart>(\"POST\", `/v1/workspaces/${workspaceId}/codex/connect/start`);\n }\n\n /** Poll device-code authorization with the `state` from {@link codexConnectStart}. */\n async codexConnectPoll(workspaceId: string, state: string): Promise<CodexConnectPoll> {\n return await this.requestJson<CodexConnectPoll>(\"POST\", `/v1/workspaces/${workspaceId}/codex/connect/poll`, { state });\n }\n\n /** Remaining usage / limits for the connected (ACTIVE) subscription. Back-compat. */\n async codexUsage(workspaceId: string): Promise<CodexUsage> {\n return await this.requestJson<CodexUsage>(\"GET\", `/v1/workspaces/${workspaceId}/codex/usage`);\n }\n\n /** Live per-account usage read (refreshes THIS account's bearer; writes the cache). */\n async codexAccountUsage(workspaceId: string, accountId: string): Promise<CodexUsage> {\n return await this.requestJson<CodexUsage>(\"GET\", `/v1/workspaces/${workspaceId}/codex/accounts/${accountId}/usage`);\n }\n\n /** Batched live refresh across every connected account, keyed by credential id. */\n async refreshCodexUsage(workspaceId: string): Promise<{ usage: CodexUsageMap }> {\n return await this.requestJson<{ usage: CodexUsageMap }>(\"POST\", `/v1/workspaces/${workspaceId}/codex/usage/refresh`);\n }\n\n /** Disconnect ALL accounts (legacy workspace-wide). Prefer `disconnectCodexAccount`. */\n async codexDisconnect(workspaceId: string): Promise<{ disconnected: boolean }> {\n return await this.requestJson<{ disconnected: boolean }>(\"DELETE\", `/v1/workspaces/${workspaceId}/codex`);\n }\n\n /** List every connected Codex account + the workspace active pointer + settings. */\n async listCodexAccounts(workspaceId: string): Promise<CodexAccountsResponse> {\n return await this.requestJson<CodexAccountsResponse>(\"GET\", `/v1/workspaces/${workspaceId}/codex/accounts`);\n }\n\n /** Switch the workspace ACTIVE Codex account (the one unpinned sessions use). */\n async activateCodexAccount(workspaceId: string, accountId: string): Promise<{ activated: boolean; accountId: string }> {\n return await this.requestJson<{ activated: boolean; accountId: string }>(\"POST\", `/v1/workspaces/${workspaceId}/codex/accounts/${accountId}/activate`);\n }\n\n /** P3: enable/disable Codex auto-rotation and/or pick the strategy. Returns the effective settings. */\n async setCodexRotationSettings(\n workspaceId: string,\n patch: { rotationEnabled?: boolean; rotationStrategy?: CodexRotationSettings[\"rotationStrategy\"] },\n ): Promise<CodexRotationSettings> {\n return await this.requestJson<CodexRotationSettings>(\"PATCH\", `/v1/workspaces/${workspaceId}/codex/settings`, patch);\n }\n\n /** Disconnect ONE Codex account by id (re-picks active when the removed one was active). */\n async disconnectCodexAccount(workspaceId: string, accountId: string): Promise<{ disconnected: boolean; newActiveId: string | null }> {\n return await this.requestJson<{ disconnected: boolean; newActiveId: string | null }>(\"DELETE\", `/v1/workspaces/${workspaceId}/codex/accounts/${accountId}`);\n }\n\n /** Rename a Codex account (label only in P1). */\n async renameCodexAccount(workspaceId: string, accountId: string, label: string | null): Promise<CodexAccount> {\n return await this.requestJson<CodexAccount>(\"PATCH\", `/v1/workspaces/${workspaceId}/codex/accounts/${accountId}`, { label });\n }\n\n /** Pin (or unpin via \"auto\") a session's Codex account. Applies on the next turn. */\n async pinSessionCodexAccount(workspaceId: string, sessionId: string, target: string): Promise<{ pinned: string }> {\n return await this.requestJson<{ pinned: string }>(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/codex-account`, { target });\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","// Zero-dependency desktop (noVNC) transport contract.\n//\n// The actual `@novnc/novnc` RFB import is a browser-only DOM dependency and\n// lives in `@opengeni/react` (lazily imported, SSR-safe). The framework-agnostic\n// SDK ships only the transport CONTRACT: a URL assembler, the minimal RFB\n// surface the component drives, the connection state machine, and the\n// rotation/fence reducers — all pure and unit-testable, with no DOM and no deps.\n//\n// Channel A (terminal-as-events, files, git) needs NO new transport here: those\n// are event projections + the synchronous fs/git/terminal point queries on\n// `OpenGeniClient`. Only the desktop pixel plane (Channel B, direct-to-provider)\n// needs this.\n\nimport type { DesktopStreamCapability, StreamUrlRotatedPayload } from \"./types\";\n\n/**\n * Translate the negotiated desktop capability into the WebSocket URL the noVNC\n * RFB client connects to. The scoped provider token is ALREADY embedded in the\n * minted `url` (Modal tunnel host, Blaxel `bl_preview_token`, Daytona signed\n * preview) by `session.resolveExposedPort(6080)` — we do NOT append `cap.token`\n * as a query param (that double-auth was an adversarial-review bug: the box runs\n * `-nopw` in v1, so the RFB password is meaningless and the real auth is the\n * tunnel token in the host). We only normalize the scheme to `ws`/`wss` and, when\n * the minted URL points at a `vnc.html` viewer page, rewrite it to the\n * websockify socket path noVNC actually dials.\n */\nexport function desktopSocketUrl(cap: Pick<DesktopStreamCapability, \"url\">): string {\n if (!cap.url) {\n throw new Error(\"desktop capability has no url (transport is null)\");\n }\n const u = new URL(cap.url);\n // https → wss, http → ws (noVNC dials a WebSocket, not the HTTP viewer page).\n if (u.protocol === \"https:\") {\n u.protocol = \"wss:\";\n } else if (u.protocol === \"http:\") {\n u.protocol = \"ws:\";\n }\n // A minted `…/vnc.html` (or `…/vnc_lite.html`) viewer page → the websockify\n // socket the page itself would open (`…/websockify`). A bare host/path with no\n // viewer page is already the socket path; leave it untouched.\n if (/\\/vnc(_lite)?\\.html$/.test(u.pathname)) {\n u.pathname = u.pathname.replace(/\\/vnc(_lite)?\\.html$/, \"/websockify\");\n u.search = \"\";\n u.hash = \"\";\n }\n return u.toString();\n}\n\n/**\n * The minimal RFB surface the React component drives. Lets tests (and 3rd\n * parties swapping noVNC for a WebRTC client in v3) provide a fake without the\n * DOM. Matches `@novnc/novnc`'s RFB constructor + lifecycle.\n */\nexport interface DesktopRfbLike {\n viewOnly: boolean;\n scaleViewport: boolean;\n /**\n * 1:1 viewport clipping. We always drive this FALSE: with clipping on, noVNC\n * paints the framebuffer pixel-for-pixel and scrolls/crops to the container\n * (the \"zoomed in\" look). FALSE lets `scaleViewport` shrink the 1280x800 frame\n * to fit the panel (aspect-preserved). Declared so the hook can pin it instead\n * of relying on noVNC's default — `scaleViewport=true` forces clip off\n * internally, but a stale/partial state on reconnect could leave it on.\n */\n clipViewport: boolean;\n addEventListener(\n type: \"connect\" | \"disconnect\" | \"securityfailure\",\n cb: (e?: unknown) => void,\n ): void;\n removeEventListener?: (\n type: \"connect\" | \"disconnect\" | \"securityfailure\",\n cb: (e?: unknown) => void,\n ) => void;\n disconnect(): void;\n}\n\nexport type DesktopRfbFactory = (\n target: HTMLElement,\n url: string,\n opts: { credentials?: { password?: string | undefined } | undefined },\n) => DesktopRfbLike;\n\nexport type DesktopConnectionState =\n | \"idle\"\n | \"negotiating\"\n | \"connecting\"\n | \"connected\"\n | \"rotating\"\n | \"reconnecting\"\n | \"error\"\n | \"ended\";\n\nexport type DesktopStreamEvent =\n | { type: \"negotiated\" }\n | { type: \"connected\" }\n | { type: \"disconnected\" }\n | { type: \"rotate\" }\n | { type: \"fail\" }\n | { type: \"abort\" };\n\n/**\n * Pure reducer for the desktop connection lifecycle. The component owns the RFB\n * object + DOM; this owns the transitions so they are unit-testable. Mirrors the\n * Channel-A stream reducer discipline.\n */\nexport function nextDesktopState(\n current: DesktopConnectionState,\n ev: DesktopStreamEvent,\n): DesktopConnectionState {\n switch (ev.type) {\n case \"negotiated\":\n return \"connecting\";\n case \"connected\":\n return \"connected\";\n case \"rotate\":\n return current === \"connected\" ? \"rotating\" : current;\n case \"disconnected\":\n // A deliberate teardown (ended) stays ended; everything else reconnects.\n return current === \"ended\" ? \"ended\" : \"reconnecting\";\n case \"fail\":\n return \"error\";\n case \"abort\":\n return \"ended\";\n default:\n return current;\n }\n}\n\n// `DesktopStreamCapability` is the desktop cell of `SessionCapabilities`; alias\n// it for the rotation reducer below without depending on the whole doc.\nexport type DesktopStreamCapabilityLike = {\n url: string | null;\n token: string | null;\n expiresAt: string | null;\n};\n\n/**\n * Apply a `stream.url.rotated` event onto a desktop capability, fencing on\n * leaseEpoch (split-brain). A rotation minted under an epoch the client has\n * already advanced PAST is from a superseded owner and is dropped (returns\n * null); otherwise the fresh url/token/expiresAt are folded in.\n */\nexport function applyUrlRotation<T extends DesktopStreamCapabilityLike>(\n cap: T,\n payload: StreamUrlRotatedPayload,\n knownEpoch: number,\n): T | null {\n if (payload.leaseEpoch < knownEpoch) {\n return null;\n }\n return { ...cap, url: payload.url, token: payload.token, expiresAt: payload.expiresAt };\n}\n","// Zero-dependency interactive-terminal (ttyd PTY-over-websocket) transport\n// contract. Symmetric with `desktop.ts`: the SDK ships only the pure transport\n// CONTRACT (a URL assembler + the ttyd wire-protocol frame codec), with no DOM\n// and no deps. The actual WebSocket open + xterm attach lives in `@opengeni/react`\n// (`use-terminal-stream.ts`).\n//\n// The interactive terminal is a REAL PTY streamed over the SAME Modal raw-TLS\n// tunnel as the desktop noVNC, with the SAME scoped stream-token mechanism. The\n// box bakes `ttyd` on `TERMINAL_STREAM_PORT` (7681); `session.resolveExposedPort`\n// mints the tunnel URL. The Terminal capability cell carries transport \"pty-ws\" +\n// that url + the scoped token when the box is warm and a viewer is attached.\n\nimport type { TerminalCapability } from \"./types\";\n\n/**\n * Translate the negotiated `pty-ws` Terminal capability into the WebSocket URL\n * the ttyd client dials. The scoped provider token is ALREADY embedded in the\n * minted `url` (the Modal tunnel host) by `session.resolveExposedPort(7681)` —\n * we do NOT append `cap.token` (identical posture to the desktop: the gate is the\n * unguessable short-TTL tunnel URL + the server-recorded scoped stream token; ttyd\n * runs `--writable` with no `-c` credential in v1). We only normalize the scheme\n * to `ws`/`wss`; a bare host is already the ttyd websocket endpoint.\n */\nexport function terminalSocketUrl(cap: Pick<TerminalCapability, \"url\">): string {\n if (!cap.url) {\n throw new Error(\"terminal capability has no url (transport is not pty-ws)\");\n }\n const u = new URL(cap.url);\n // https → wss, http → ws (ttyd is dialed as a WebSocket, not an HTTP page).\n if (u.protocol === \"https:\") {\n u.protocol = \"wss:\";\n } else if (u.protocol === \"http:\") {\n u.protocol = \"ws:\";\n }\n return u.toString();\n}\n\n// ── ttyd wire protocol ───────────────────────────────────────────────────────\n// ttyd frames are a single ASCII command char + payload. These mirror ttyd's\n// `protocol.h` Command enum. Client→server and server→client share the byte 0/1/2\n// values but mean different things per direction (see below).\n\n/** ttyd subprotocol — REQUIRED on the WebSocket handshake or ttyd refuses it. */\nexport const TTYD_SUBPROTOCOL = \"tty\";\n\n/** Client→server command bytes (the first char of each outbound text frame). */\nexport const TtydClientCommand = {\n /** stdin: \"0\" + raw input bytes. */\n INPUT: \"0\",\n /** window resize: \"1\" + JSON.stringify({ columns, rows }). */\n RESIZE: \"1\",\n /** flow-control pause (back-pressure): \"2\". */\n PAUSE: \"2\",\n /** flow-control resume: \"3\". */\n RESUME: \"3\",\n} as const;\n\n/** Server→client command bytes (the first char of each inbound frame). */\nexport const TtydServerCommand = {\n /** stdout/stderr: \"0\" + raw output bytes (write the rest into xterm). */\n OUTPUT: \"0\",\n /** set the window title: \"1\" + title string. */\n SET_WINDOW_TITLE: \"1\",\n /** ttyd client preferences JSON: \"2\" + json (ignored by us). */\n SET_PREFERENCES: \"2\",\n} as const;\n\n/**\n * The ttyd handshake's first frame: an auth message. ttyd expects\n * `JSON.stringify({ AuthToken })` as the FIRST text frame on the socket. We send\n * an empty token — our gate is the tunnel URL + scoped stream token, NOT a ttyd\n * `-c` basic-auth credential (which the box does not set in v1). Optional ttyd\n * `columns`/`rows` can ride this frame to seed the PTY size before the first\n * resize. Pure (string-building only) so it stays unit-testable in the SDK.\n */\nexport function ttydAuthFrame(opts?: { columns?: number; rows?: number }): string {\n const frame: { AuthToken: string; columns?: number; rows?: number } = { AuthToken: \"\" };\n if (opts?.columns && opts.columns > 0) frame.columns = opts.columns;\n if (opts?.rows && opts.rows > 0) frame.rows = opts.rows;\n return JSON.stringify(frame);\n}\n\n/** Build a client→server INPUT (stdin) frame: \"0\" + data. */\nexport function ttydInputFrame(data: string): string {\n return TtydClientCommand.INPUT + data;\n}\n\n/** Build a client→server RESIZE frame: \"1\" + JSON.stringify({ columns, rows }). */\nexport function ttydResizeFrame(columns: number, rows: number): string {\n return TtydClientCommand.RESIZE + JSON.stringify({ columns, rows });\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\n// Mirror of `@opengeni/contracts` SandboxBackend (11 values; every member is\n// additive at the end). 3-way enum parity is pinned by\n// `test/contract-parity.test.ts`.\nexport type SandboxBackend =\n | \"docker\"\n | \"modal\"\n | \"local\"\n | \"none\"\n | \"daytona\"\n | \"runloop\"\n | \"e2b\"\n | \"blaxel\"\n | \"cloudflare\"\n | \"vercel\"\n | \"selfhosted\";\n\n// Mirror of `@opengeni/contracts` SandboxOs. Only \"linux\" is reachable in v1.\nexport type SandboxOs = \"linux\" | \"macos\" | \"windows\";\n\n// Mirror of `@opengeni/contracts` SandboxCapabilityName.\nexport type SandboxCapabilityName =\n | \"FileSystem\"\n | \"Terminal\"\n | \"Git\"\n | \"DesktopStream\"\n | \"Recording\";\n\n// Mirror of `@opengeni/contracts` CapabilityUnavailableReason.\nexport type CapabilityUnavailableReason =\n | \"backend_unsupported\"\n | \"os_unsupported\"\n | \"not_provisioned\"\n | \"disabled_by_policy\"\n | \"lease_cold\"\n | \"tier_headless\"\n // selfhosted (bring-your-own-compute) negotiation states:\n | \"agent_offline\"\n | \"agent_reconnecting\"\n | \"consent_required\"\n | \"display_unavailable\";\n\n// Mirror of `@opengeni/contracts` SessionCapabilities (the negotiated handshake\n// document). The descriptor table itself is NOT mirrored — it lives in\n// contracts (P0.1) and is consumed by the SDK config in a later PR.\nexport type SessionCapabilities = {\n sessionId: string;\n backend: SandboxBackend;\n os: SandboxOs;\n liveness: \"cold\" | \"warming\" | \"warm\" | \"draining\";\n leaseEpoch: number;\n viewerHeartbeatIntervalMs: number;\n FileSystem: {\n available: boolean;\n readOnly: boolean;\n root: string;\n pathSep: \"/\" | \"\\\\\";\n treeMode: \"lazy\" | \"snapshot\";\n reason: CapabilityUnavailableReason | null;\n };\n Terminal: {\n transport: \"sse-events\" | \"pty-ws\" | null;\n ptyCapable: boolean;\n shell: string;\n url: string | null;\n token: string | null;\n reason: CapabilityUnavailableReason | null;\n };\n Git: {\n available: boolean;\n repos: string[];\n reason: CapabilityUnavailableReason | null;\n };\n DesktopStream: {\n // \"relay-frames\" + \"frames\": the selfhosted framebuffer stream — PNG-per-frame\n // protobuf datagrams over the relay, painted by a canvas client (NOT RFB).\n transport: \"vnc-ws\" | \"rdp-ws\" | \"webrtc\" | \"relay-frames\" | null;\n client: \"novnc\" | \"web-rdp\" | \"frames\" | null;\n mode: \"read-only\" | \"interactive\";\n url: string | null;\n token: string | null;\n expiresAt: string | null;\n resolution: [number, number];\n unredacted: boolean;\n requiresAcknowledgment: boolean;\n acknowledged: boolean;\n // Shared-exposure disclosure (addendum E.1): `shared` when the group has >1\n // session; `sharedSessionIds` lists the OTHER sessions' ids ONLY (never their\n // conversation/metadata).\n shared: boolean;\n sharedSessionIds: string[];\n reason: CapabilityUnavailableReason | null;\n };\n Recording: {\n available: boolean;\n modes: (\"manual\" | \"on-turn\" | \"on-verify\")[];\n codecs: (\"h264-mp4\" | \"vp9-webm\")[];\n reason: CapabilityUnavailableReason | null;\n };\n ComputerUse: {\n available: boolean;\n readOnly: boolean;\n reason: CapabilityUnavailableReason | null;\n };\n negotiatedAt: string;\n};\n\n// Convenience aliases for the per-surface cells of `SessionCapabilities`, so the\n// client hooks/components can take a single cell without restating the inline\n// shape. These are exact structural views of the cells above.\nexport type FileSystemCapability = SessionCapabilities[\"FileSystem\"];\nexport type TerminalCapability = SessionCapabilities[\"Terminal\"];\nexport type GitCapability = SessionCapabilities[\"Git\"];\nexport type DesktopStreamCapability = SessionCapabilities[\"DesktopStream\"];\nexport type RecordingCapability = SessionCapabilities[\"Recording\"];\nexport type ComputerUseCapability = SessionCapabilities[\"ComputerUse\"];\n\n// ── Stream-surfacing client surface (Phase 5) ───────────────────────────────\n// Mirrors of the contracts viewer-attach / acknowledge / heartbeat shapes that\n// the capability-gated client (`@opengeni/react`) drives. The desktop pixel\n// plane rides Channel B (direct-to-provider noVNC); the structured terminal/\n// files/git surfaces ride Channel A (the existing event spine + the synchronous\n// fs/git/terminal point queries above). These are TYPES only (the SDK keeps zero\n// runtime deps); the contract-parity test pins them.\n\n// Mirror of `@opengeni/contracts` StreamUrlRotatedPayload — the Channel-A event\n// the client folds in to hot-swap its noVNC socket on a box rollover, fenced on\n// leaseEpoch.\nexport type StreamUrlRotatedPayload = {\n url: string;\n token: string | null;\n expiresAt: string | null;\n leaseEpoch: number;\n transport: \"vnc-ws\";\n viewerId: string | null;\n};\nexport type StreamOpenedPayload = { viewerId: string; shared: boolean; viewerCount: number };\nexport type StreamClosedPayload = {\n viewerId: string;\n reason: \"client-disconnect\" | \"reaped\" | \"revoked\" | \"box-rollover\";\n viewerCount: number;\n};\nexport type StreamRevokedPayload = {\n viewerId: string | null;\n reason: \"grant-revoked\" | \"session-failed\" | \"admin\";\n};\n\n// Mirror of `@opengeni/contracts` AttachViewerRequest. Omitting `viewerId` mints\n// a fresh holder id (returned on the response, carried through heartbeat/detach).\n// `desktop:true` opts into the un-redacted pixel plane (the consent-gated noVNC\n// stream); a terminal/files-only warm attach omits it (defaults false) so it\n// warms the box + mints the pty-ws terminal cell WITHOUT tripping the consent 409.\nexport type AttachViewerRequest = { viewerId?: string | undefined; desktop?: boolean | undefined };\n\n// Mirror of `@opengeni/contracts` ViewerHolder + the P4.2 desktop-stream fields\n// the POST /viewers handler folds in when the pixel plane is minted in-process.\nexport type ViewerHolder = {\n viewerId: string;\n sandboxGroupId: string;\n liveness: \"cold\" | \"warming\" | \"warm\" | \"draining\";\n leaseEpoch: number;\n viewerHeartbeatIntervalMs: number;\n dataPlaneUrl: string | null;\n};\nexport type AttachViewerResponse = ViewerHolder & {\n // The scoped desktop-stream address minted for THIS holder (P4.2). Null when\n // the deployment is headless / desktop is disabled / the mint degraded —\n // the client then falls back to the Channel-A surfaces only.\n streamToken: string | null;\n streamExpiresAt: string | null;\n resolution: [number, number] | null;\n transport: \"vnc-ws\" | null;\n client: \"novnc\" | null;\n // The scoped ttyd PTY-over-websocket address minted for THIS holder — the REAL\n // interactive terminal, symmetric with the desktop pixel plane (same Modal\n // tunnel, same scoped stream token). Populated on a warm box; null when the\n // terminal mint degraded (headless / no secret / tunnel failure), in which case\n // the client falls back to the Channel-A read-only command-output firehose.\n // `terminalTransport` is \"pty-ws\" iff a live `terminalUrl` was minted.\n terminalUrl: string | null;\n terminalToken: string | null;\n terminalTransport: \"pty-ws\" | null;\n};\n\n// Mirror of `@opengeni/contracts` AcknowledgeStreamRequest/Response — the\n// un-redacted-pixel + shared-exposure consent gate (P3.2).\nexport type AcknowledgeStreamRequest = {\n acknowledgeUnredacted?: boolean | undefined;\n acknowledgeShared?: boolean | undefined;\n};\nexport type AcknowledgeStreamResponse = { acknowledged: boolean; acknowledgedShared: boolean };\n\n// Mirror of `@opengeni/contracts` ViewerHeartbeatRequest/Response — the\n// Channel-A viewer-liveness ping, epoch-fenced (a stale-epoch beat → alive:false\n// → the client re-attaches).\nexport type ViewerHeartbeatRequest = { leaseEpoch: number };\nexport type ViewerHeartbeatResponse = { alive: boolean };\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 SessionMcpServerInput = {\n id: string;\n name?: string | undefined;\n url: string;\n allowedTools?: string[] | undefined;\n timeoutMs?: number | undefined;\n cacheToolsList?: boolean | undefined;\n headers?: Record<string, string> | undefined;\n};\n\nexport type SessionMcpCredentialUpdateInput = {\n id: string;\n headers: Record<string, string>;\n};\n\nexport type SessionMcpServerMetadata = {\n id: string;\n name: string | null;\n url: string;\n headerNames: string[];\n credentialVersion: number;\n};\n\nexport type Session = {\n id: string;\n workspaceId: string;\n accountId: string;\n status: SessionStatus;\n initialMessage: string;\n title: string | null;\n titleSource: \"user\" | \"agent\" | null;\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 mcpServers: SessionMcpServerMetadata[];\n createIdempotencyKey: string | null;\n temporalWorkflowId: string | null;\n activeTurnId: string | null;\n lastSequence: number;\n /** Multi-account Codex (P1): the account this session is pinned to (null ⇒ follow workspace active). */\n codexPinnedCredentialId?: string | null;\n /** Multi-account Codex (P1): the account the most recent turn ran on (the \"Running on:\" indicator). */\n codexLastCredentialId?: string | null;\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 // Channel-B desktop pixel-plane signals (mirror of contracts SessionEventType;\n // the contract-parity test asserts sorted equality).\n \"stream.url.rotated\",\n \"stream.opened\",\n \"stream.closed\",\n \"stream.revoked\",\n // Channel-B recording signals (P4.3 — \"agent films itself proving the fix\").\n \"recording.started\",\n \"recording.available\",\n \"recording.failed\",\n // Channel-A structured-service notifications (P4.4; mirror of contracts\n // SessionEventType — the contract-parity test asserts sorted equality).\n \"fs.changed\",\n \"git.changed\",\n \"terminal.pty.started\",\n \"terminal.pty.output.delta\",\n \"terminal.pty.exited\",\n \"session.title_set\",\n // Multi-account Codex (P1): the session's inference account changed.\n \"codex.account.switched\",\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\n// Recording payloads (P4.3 — plain TS mirror of the contracts Zod schemas; the\n// SDK is zero-runtime-dep so these are TYPES, not Zod, F15). The contract-parity\n// test asserts the event-type literals; these shapes document the wire payloads.\nexport type RecordingMode = \"manual\" | \"on-turn\" | \"on-verify\";\nexport type RecordingCodec = \"h264-mp4\" | \"vp9-webm\";\nexport type RecordingContentType = \"video/mp4\" | \"video/webm\";\nexport type RecordingFailedReason =\n | \"ffmpeg-error\"\n | \"box-death\"\n | \"box-rollover\"\n | \"upload-failed\"\n | \"max-bytes-exceeded\"\n | \"display-unavailable\";\n\nexport type RecordingStartedPayload = {\n recordingId: string;\n turnId: string | null;\n mode: RecordingMode;\n codec: RecordingCodec;\n dimensions: [number, number];\n framerate: number;\n startedAt: string;\n reason?: string | null | undefined;\n};\nexport type RecordingAvailablePayload = {\n recordingId: string;\n turnId: string | null;\n codec: RecordingCodec;\n contentType: RecordingContentType;\n storageKey: string;\n durationSeconds: number | null;\n sizeBytes: number;\n dimensions: [number, number];\n};\nexport type RecordingFailedPayload = {\n recordingId: string;\n turnId: string | null;\n reason: RecordingFailedReason;\n detail?: string | null | undefined;\n};\n\n// ── Channel-A structured services (P4.4) — hand-written wire mirrors ─────────\n\n// A1 notification payloads.\nexport type SandboxCommandOutputDeltaPayload = {\n stream: \"stdout\" | \"stderr\";\n chunk: string;\n commandId?: string | undefined;\n seq?: number | undefined;\n};\nexport type FsChangeKind = \"created\" | \"modified\" | \"deleted\" | \"renamed\";\nexport type FsChangedPayload = {\n changes: { path: string; kind: FsChangeKind; isDir: boolean; sizeBytes: number | null; oldPath?: string | undefined }[];\n source: \"write\" | \"watch\" | \"agent\";\n revision: number;\n leaseEpoch: number;\n};\nexport type GitChangedPayload = {\n head: string | null;\n dirty: boolean;\n ahead: number;\n behind: number;\n changedFileCount: number;\n reason: \"commit\" | \"checkout\" | \"stage\" | \"worktree\" | \"fetch\" | \"unknown\";\n revision: number;\n leaseEpoch: number;\n};\nexport type TerminalPtyStartedPayload = { ptyId: string; cols: number; rows: number; shell: string; cwd: string };\nexport type TerminalPtyOutputDeltaPayload = { ptyId: string; stream: \"stdout\" | \"stderr\"; chunk: string; seq: number };\nexport type TerminalPtyExitedPayload = { ptyId: string; exitCode: number | null; reason: \"exit\" | \"killed\" | \"owner_gone\" | \"timeout\" };\n\n// A2 FileSystem request/response.\nexport type FsNodeType = \"file\" | \"dir\" | \"symlink\" | \"other\";\nexport type FsTreeNode = {\n name: string;\n path: string;\n type: FsNodeType;\n sizeBytes: number | null;\n mtimeMs: number | null;\n mode: number | null;\n children?: FsTreeNode[] | undefined;\n truncated: boolean;\n};\nexport type FsEncoding = \"utf8\" | \"base64\";\nexport type FsListRequest = { path?: string; depth?: number; maxEntries?: number; includeHidden?: boolean };\nexport type FsListResponse = { root: FsTreeNode; revision: number; truncated: boolean };\nexport type FsReadRequest = { path: string; encoding?: FsEncoding; maxBytes?: number };\nexport type FsReadResponse = { path: string; encoding: FsEncoding; content: string; sizeBytes: number; truncated: boolean; isBinary: boolean; revision: number };\nexport type FsWriteRequest = { path: string; encoding?: FsEncoding; content: string; overwrite?: boolean; createParents?: boolean };\nexport type FsWriteResponse = { path: string; sizeBytes: number; revision: number };\nexport type FsDeleteRequest = { path: string; recursive?: boolean };\nexport type FsDeleteResponse = { revision: number };\nexport type FsMoveRequest = { path: string; newPath: string; overwrite?: boolean; createParents?: boolean };\nexport type FsMoveResponse = { path: string; newPath: string; revision: number };\nexport type FsMkdirRequest = { path: string; recursive?: boolean };\nexport type FsMkdirResponse = { path: string; revision: number };\n\n// A2 Git request/response (the Pierre-diff feed).\nexport type GitFileStatusCode = \"added\" | \"modified\" | \"deleted\" | \"renamed\" | \"copied\" | \"untracked\" | \"ignored\" | \"conflicted\" | \"typechange\";\nexport type GitFileStatus = { path: string; oldPath: string | null; index: GitFileStatusCode | null; worktree: GitFileStatusCode | null; isConflicted: boolean };\nexport type GitStatusRequest = { path?: string };\nexport type GitStatusResponse = { isRepo: boolean; head: string | null; detached: boolean; upstream: string | null; ahead: number; behind: number; files: GitFileStatus[]; revision: number };\nexport type GitDiffLineType = \"context\" | \"add\" | \"del\" | \"meta\";\nexport type GitDiffLine = { type: GitDiffLineType; oldNo: number | null; newNo: number | null; text: string };\nexport type GitDiffHunk = { oldStart: number; oldLines: number; newStart: number; newLines: number; header: string; lines: GitDiffLine[] };\nexport type GitFileDiff = { path: string; oldPath: string | null; status: GitFileStatusCode; isBinary: boolean; isImage: boolean; additions: number; deletions: number; hunks: GitDiffHunk[]; truncated: boolean };\nexport type GitDiffRequest = { path?: string; staged?: boolean; fromRef?: string; toRef?: string; pathspec?: string[]; contextLines?: number; maxBytesPerFile?: number };\nexport type GitDiffResponse = { files: GitFileDiff[]; revision: number };\nexport type GitLogRequest = { path?: string; ref?: string; maxCount?: number; skip?: number; pathspec?: string[] };\nexport type GitCommit = {\n sha: string;\n shortSha: string;\n parents: string[];\n author: { name: string; email: string; timestamp: number };\n committer: { name: string; email: string; timestamp: number };\n subject: string;\n body: string;\n refs: string[];\n};\nexport type GitLogResponse = { commits: GitCommit[]; hasMore: boolean };\nexport type GitShowRequest = { path?: string; ref: string; filePath?: string; encoding?: FsEncoding; maxBytesPerFile?: number };\nexport type GitShowResponse = { commit: GitCommit | null; files: GitFileDiff[]; blob: { content: string; encoding: FsEncoding; sizeBytes: number; truncated: boolean } | null; revision: number };\n\n// A2 Terminal exec + PTY.\nexport type TerminalExecRequest = { command: string; cwd?: string; timeoutMs?: number; emitStream?: boolean };\nexport type TerminalExecResponse = { stdout: string; stderr: string; exitCode: number | null; running: boolean; wallTimeSeconds: number };\nexport type PtyOpenRequest = { cols?: number; rows?: number; cwd?: string; shell?: string };\nexport type PtyOpenResponse = { ptyId: string; streamVia: \"sse-events\"; supportsInput: boolean };\nexport type PtyWriteRequest = { ptyId: string; data: string };\nexport type PtyResizeRequest = { ptyId: string; cols: number; rows: number };\nexport type PtyCloseRequest = { ptyId: string };\n\nexport type SessionStructuredCapabilities = {\n FileSystem: { available: boolean; readOnly: boolean; root: string };\n Terminal: { events: boolean; exec: boolean; pty: { available: boolean } };\n Git: { available: boolean; repos: string[] };\n};\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 // The enrolled machine (a sandbox id) to run this session on; seeds the\n // active-sandbox pointer at creation so the first turn lands on it.\n targetSandboxId?: string | undefined;\n // Host working directory for a connected-machine target (the agent runs here;\n // default = the machine's launch dir). Ignored for managed sandboxes.\n workingDir?: string | 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 mcpServers?: SessionMcpServerInput[] | undefined;\n // Shared-sandbox placement (mirror of `@opengeni/contracts` CreateSessionRequest.sandbox,\n // addendum 05 §D.1). Three-way union; OMITTED ⇒ the context-dependent server default\n // (from inside a session → \"shared\" with the creator's box, top-level → \"new\").\n // - \"shared\": join the CREATOR's box (requires a parent session; top-level → 422).\n // - \"new\": mint a fresh singleton box (group ≡ the new session's id).\n // - {groupId}: join a SPECIFIC sibling group in THIS workspace (manager fan-out).\n sandbox?: \"shared\" | \"new\" | { groupId: 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 // Sandbox-surfacing (mirror of @opengeni/contracts Permission). stream:view is\n // strictly broader than sessions:read (un-redacted pixels); stream:control is\n // the never-granted-v1 raw-input plane; stream:acknowledge is the secret-leak\n // consent gate.\n \"stream:view\",\n \"stream:control\",\n \"stream:acknowledge\",\n \"files:upload\",\n \"files:read\",\n \"files:write\",\n \"terminal:attach\",\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 \"mcp_servers:attach\",\n \"goals:manage\",\n \"enrollments:read\",\n \"enrollments: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 * Connection state of a workspace's Codex (ChatGPT) subscription, returned by\n * `GET /v1/workspaces/:id/codex/status`. `models` are the codex models the\n * workspace can select (projected as ClientModel under their own \"no credits\"\n * provider group), present only while connected.\n */\nexport type CodexConnectionStatus = {\n connected: boolean;\n plan?: string | null;\n valid?: boolean;\n expiresAt?: string | null;\n lastError?: string | null;\n models?: ClientModel[];\n /** The account a session runs on when unpinned (label for the in-session indicator). */\n activeAccount?: { id: string; label?: string | null; chatgptAccountId?: string | null } | null;\n /** How many Codex accounts the workspace has connected. */\n accountCount?: number;\n};\n\n/**\n * One normalized Codex usage window (5h or weekly), camelCase end-to-end (the\n * route normalizes server-side; the web layer never re-hand-types snake_case).\n * `percent` is authoritative; used/limit/remaining are a synthesized 0–100 scale\n * (limit = 100) because the provider gives only a percentage. `remaining =\n * 100 - percent` is the P3 rotation key. Identify the window by `limitWindowSeconds`\n * (18000 ⇒ 5h, 604800 ⇒ weekly), never by position.\n */\nexport type CodexUsageWindow = {\n used: number;\n limit: number;\n remaining: number;\n percent: number;\n resetAt: string | null;\n resetAfterSeconds: number | null;\n limitWindowSeconds: number;\n};\n\n/** The normalized usage payload for one account — the P2/P3 contract. */\nexport type CodexUsagePayload = {\n status: \"ok\" | \"limit_reached\" | \"error\" | \"no-data\";\n planType: string | null;\n fiveHour: CodexUsageWindow | null;\n weekly: CodexUsageWindow | null;\n limitReached: boolean;\n fetchedAt: string;\n /** Present only on an auth/refresh failure path. */\n reason?: \"needs_relogin\";\n additionalLimits?: Array<{ limitName: string; meteredFeature: string; fiveHour: CodexUsageWindow | null; weekly: CodexUsageWindow | null }>;\n credits?: { hasCredits: boolean; unlimited: boolean; overageLimitReached: boolean; balance: string };\n};\n\n/** One connected Codex (ChatGPT) account in a workspace (multi-account P1). Metadata only. */\nexport type CodexAccount = {\n id: string;\n chatgptAccountId?: string | null;\n label?: string | null;\n email?: string | null;\n plan?: string | null;\n status: \"active\" | \"needs_relogin\" | \"error\";\n active: boolean;\n expiresAt?: string | null;\n lastRefreshAt?: string | null;\n lastError?: string | null;\n // P2 CACHED usage (built from the persisted columns; renders bars off\n // listCodexAccounts with no second call). null until the first live refresh.\n fiveHour?: CodexUsageWindow | null;\n weekly?: CodexUsageWindow | null;\n usageCheckedAt?: string | null;\n // P3 rotation cooldown: ISO timestamp until which this account is cooling-down\n // (rotated-off after a usage cap). null/absent ⇒ not cooling.\n exhaustedUntil?: string | null;\n};\n\n/** Per-workspace Codex rotation/active settings. P1: rotation inert, only activeCredentialId loads. */\nexport type CodexRotationSettings = {\n rotationEnabled: boolean;\n rotationStrategy: \"most_remaining\" | \"round_robin\" | \"drain_then_next\";\n activeCredentialId: string | null;\n};\n\n/** GET /codex/accounts — the accounts list + the workspace active pointer + settings. */\nexport type CodexAccountsResponse = {\n accounts: CodexAccount[];\n activeAccountId: string | null;\n settings: CodexRotationSettings;\n};\n\n/** Payload of a `codex.account.switched` session event. */\nexport type CodexAccountSwitchedPayload = {\n fromAccountId: string | null;\n toAccountId: string;\n reason: \"manual\" | \"exhausted\" | \"rotation\";\n // P4 connector-aware rotation: the session's used connectors that the new account\n // does NOT cover (a prefer-not-require failover that dropped a connector). Present\n // only on such a switch; the UI renders a \"dropped <connector>\" badge on the pill.\n droppedConnectors?: string[];\n};\n\n/** Device-code start: show `userCode` at `verificationUri`, then poll with `state`. */\nexport type CodexConnectStart = {\n userCode: string;\n verificationUri: string;\n intervalSeconds: number;\n state: string;\n};\n\n/** Poll result: keep polling on `pending`, restart on `expired`, done on `connected`. */\nexport type CodexConnectPoll =\n | { status: \"pending\" }\n | { status: \"expired\" }\n | { status: \"connected\"; plan?: string | null; accountId?: string; isActive?: boolean };\n\n/** Remaining usage/limits for one account. `usage` is the normalized P2 payload. */\nexport type CodexUsage = { status: \"ok\" | \"limit_reached\" | \"error\" | \"no-data\"; usage: CodexUsagePayload | null };\n\n/** Batched live-refresh response, keyed by credential id; each entry independently statused. */\nexport type CodexUsageMap = Record<string, CodexUsage>;\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 // Server-wide hint: does this deployment support Channel-A structured services\n // at all (P4.4). Per-session availability is negotiated on /stream-capabilities;\n // this is the coarse on/off the client uses to decide whether to even attempt\n // the fs/git/terminal panels.\n structuredServices: { fileSystem: boolean; git: boolean; terminalEvents: boolean };\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\nexport type UpdateSessionRequest = {\n title: string;\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 // sandbox warm-time metering (P2.1) — mirrors contracts UsageEventType.\n \"sandbox.warm_seconds\",\n \"sandbox.warm_cost\",\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 mcpCredentialUpdates?: SessionMcpCredentialUpdateInput[] | 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\n// ── Bring-your-own-compute: Machines dashboard + per-machine metrics (M10) ────\n// Hand-written mirrors of the `@opengeni/contracts` MetricSample / MachineView /\n// MachinesResponse / MachineMetricsSeriesResponse (pinned by contract-parity).\n// M9 imports THESE so the dashboard UI never drifts from the API.\n\n/** A point-in-time machine metrics sample. `gpuUtilPct`/`gpuMemBytes` are null\n * when no GPU was present (not-reported, never a real zero); the bytes/load are\n * numbers; `sampledAt` is an ISO-8601 instant. */\nexport type MetricSample = {\n cpuPct: number;\n load1: number;\n load5: number;\n load15: number;\n memUsedBytes: number;\n memTotalBytes: number;\n diskUsedBytes: number;\n diskTotalBytes: number;\n gpuUtilPct: number | null;\n gpuMemBytes: number | null;\n runQueue: number;\n sampledAt: string;\n};\n\n/** The derived dashboard state of a machine (M3 liveness + consent/display\n * reasons + the in-flight device-flow). */\nexport type MachineState =\n | \"online\"\n | \"reconnecting\"\n | \"offline\"\n | \"consent_required\"\n | \"display_unavailable\"\n | \"enrolling\";\n\nexport type MachineKind = \"modal\" | \"selfhosted\";\n\n/** A machine as the Machines dashboard renders it (an enrolled selfhosted machine\n * or the session's synthetic Modal group box, `isSessionGroup: true`). */\nexport type MachineView = {\n sandboxId: string;\n enrollmentId: string | null;\n name: string;\n kind: MachineKind;\n state: MachineState;\n active: boolean;\n isSessionGroup: boolean;\n os: string;\n arch: string;\n hasDisplay: boolean;\n allowScreenControl: boolean;\n sharedSessionCount: number;\n lastSeenAt: string | null;\n metrics: MetricSample | null;\n};\n\n/** GET /v1/workspaces/:ws/machines — the dashboard list + the active-sandbox\n * pointer (null activeSandboxId == the session's own group box is active). */\nexport type MachinesResponse = {\n activeSandboxId: string | null;\n activeEpoch: number;\n machines: MachineView[];\n};\n\n/** GET /v1/workspaces/:ws/machines/:enrollmentId/metrics/series — the downsampled\n * (~1/min) history the dashboard time-range reads. */\nexport type MachineMetricsSeriesResponse = {\n samples: MetricSample[];\n};\n\n/** POST /v1/workspaces/:ws/sessions/:sessionId/active-sandbox — swap a session's\n * active sandbox. `target` is a `MachineView.sandboxId`, or \"session\"/\"default\"\n * to swap back to the session's own group box. */\nexport type SwapActiveSandboxRequest = {\n target: string;\n};\n\n/** The swap outcome (mirrors the server `FleetSwapResult`). `swapped` is true on a\n * successful repoint OR a no-op (already there); `reason` carries the failure\n * detail (unowned/offline target, or a lost epoch fence) when false. */\nexport type SwapActiveSandboxResponse = {\n swapped: boolean;\n activeSandboxId: string | null;\n activeEpoch: number;\n reason?: string;\n};\n\n// ── Self-hosted enrollment UX (design 11) ────────────────────────────────────\n// Hand-written mirrors of the `@opengeni/contracts` enrollment-UX request/response\n// shapes (the SDK keeps zero runtime deps). The click-Grant approve-page\n// lookup/deny + the headless enroll-token mint/exchange.\n\n/** Mirror of `@opengeni/contracts` EnrollmentOs. */\nexport type EnrollmentOs = \"linux\" | \"macos\" | \"windows\";\n\n/** POST /v1/enrollments/device/lookup body. */\nexport type DeviceEnrollmentLookupRequest = {\n userCode: string;\n};\n\n/** The presentational machine details the consent screen renders. */\nexport type DeviceEnrollmentLookupMachine = {\n machineName: string | null;\n os: EnrollmentOs;\n arch: string;\n canOfferDisplay: boolean;\n requestsScreenControl: boolean;\n};\n\n/** POST /v1/enrollments/device/lookup response (no secrets, no device_code). */\nexport type DeviceEnrollmentLookupResponse = {\n workspaceId: string;\n userCode: string;\n machine: DeviceEnrollmentLookupMachine;\n expiresAt: string;\n};\n\n/** POST /v1/workspaces/:ws/enrollments/device/approve body. */\nexport type DeviceEnrollmentApproveRequest = {\n userCode: string;\n allowScreenControl?: boolean;\n};\n\n/** POST /v1/workspaces/:ws/enrollments/device/approve response. */\nexport type DeviceEnrollmentApproveResponse = {\n approved: boolean;\n enrollmentId: string;\n sandboxId: string;\n allowScreenControl: boolean;\n};\n\n/** POST /v1/workspaces/:ws/enrollments/device/deny body. */\nexport type DeviceEnrollmentDenyRequest = {\n userCode: string;\n};\n\n/** POST /v1/workspaces/:ws/enrollments/device/deny response. */\nexport type DeviceEnrollmentDenyResponse = {\n denied: boolean;\n};\n\n/** POST /v1/workspaces/:ws/enrollments/token body. */\nexport type MintEnrollTokenRequest = {\n allowScreenControl?: boolean;\n};\n\n/** POST /v1/workspaces/:ws/enrollments/token response. The `token` is SECRET. */\nexport type MintEnrollTokenResponse = {\n token: string;\n expiresAt: string;\n expiresInSeconds: number;\n};\n\n/** The credential payload the headless exchange returns (a subset of the agent's\n * EnrollmentCredentials — IDENTICAL to the device-flow poll authorized branch). */\nexport type EnrollmentCredentials = {\n agentId: string;\n workspaceId: string;\n bearer: string;\n subjectPrefix: string;\n natsUrls: string[];\n relayUrl: string;\n relayToken: string;\n natsAccountCreds: string;\n updatePublicKey: string;\n consentedWholeMachine: boolean;\n consentedScreenControl: boolean;\n};\n\n/** POST /v1/enrollments/token/exchange body (the headless / fleet enroll path). */\nexport type EnrollTokenExchangeRequest = {\n token: string;\n publicKey: string;\n os?: EnrollmentOs;\n arch?: string;\n machineName?: string;\n exposure?: \"whole-machine\";\n canOfferDisplay?: boolean;\n requestsScreenControl?: boolean;\n};\n\n/** POST /v1/enrollments/token/exchange response (wraps the credential shape). */\nexport type EnrollTokenExchangeResponse = {\n credentials: EnrollmentCredentials;\n};\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/sse.ts","../src/stream.ts","../src/client.ts","../src/proxy.ts","../src/desktop.ts","../src/terminal.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 CodexAccount,\n CodexAccountsResponse,\n CodexRotationSettings,\n CodexConnectionStatus,\n CodexConnectPoll,\n CodexConnectStart,\n CodexUsage,\n CodexUsageMap,\n BillingSummary,\n BillingUsageResponse,\n CapabilityCatalogItem,\n CapabilityCatalogResponse,\n CapabilityInstallation,\n AddDocumentRequest,\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 CreateKnowledgeMemoryRequest,\n CreateScheduledTaskRequest,\n CreateSessionRequest,\n CreateWorkspaceEnvironmentRequest,\n CreateWorkspaceRequest,\n // Enrollment UX (design 11): the click-Grant approve-page lookup/deny + headless\n // enroll-token mint.\n DeviceEnrollmentApproveResponse,\n DeviceEnrollmentDenyResponse,\n DeviceEnrollmentLookupResponse,\n MintEnrollTokenResponse,\n DiscoverMcpCapabilitiesResponse,\n Document,\n DocumentBase,\n DocumentSearchRequest,\n DocumentSearchResponse,\n EnableCapabilityRequest,\n EnablePackRequest,\n FileAsset,\n FileDownloadUrlResponse,\n GetPackResponse,\n GitHubAppInfo,\n GitHubRepositoriesResponse,\n KnowledgeMemory,\n KnowledgeMemorySearchRequest,\n ListApiKeysResponse,\n ListPacksResponse,\n // Bring-your-own-compute: the Machines dashboard + per-machine metrics (M10).\n MachinesResponse,\n MachineView,\n MetricSample,\n MachineMetricsSeriesResponse,\n // Bring-your-own-compute: the user-authenticated active-sandbox swap (M7).\n SwapActiveSandboxRequest,\n SwapActiveSandboxResponse,\n ListWorkspaceMembersResponse,\n PackInstallation,\n ReasoningEffort,\n RegisterCapabilityPackRequest,\n ResourceRef,\n ScheduledTask,\n ScheduledTaskRun,\n Session,\n SessionEvent,\n SessionGoal,\n SessionTurn,\n // Stream surfacing (Phase 5): capability negotiation + viewer lifecycle + config.\n SessionCapabilities,\n AttachViewerRequest,\n AttachViewerResponse,\n AcknowledgeStreamRequest,\n AcknowledgeStreamResponse,\n ViewerHeartbeatRequest,\n ViewerHeartbeatResponse,\n // Channel-A structured services (P4.4).\n FsListRequest,\n FsListResponse,\n FsReadRequest,\n FsReadResponse,\n FsWriteRequest,\n FsWriteResponse,\n FsDeleteRequest,\n FsDeleteResponse,\n FsMoveRequest,\n FsMoveResponse,\n FsMkdirRequest,\n FsMkdirResponse,\n GitStatusRequest,\n GitStatusResponse,\n GitDiffRequest,\n GitDiffResponse,\n GitLogRequest,\n GitLogResponse,\n GitShowRequest,\n GitShowResponse,\n TerminalExecRequest,\n TerminalExecResponse,\n PtyOpenRequest,\n PtyOpenResponse,\n PtyWriteRequest,\n PtyResizeRequest,\n PtyCloseRequest,\n ToolRef,\n UpdateKnowledgeMemoryRequest,\n UpdateScheduledTaskRequest,\n UpdateSessionGoalRequest,\n UpdateSessionRequest,\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 updateSession(workspaceId: string, sessionId: string, request: UpdateSessionRequest): Promise<Session> {\n return await this.requestJson<Session>(\"PATCH\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}`, request);\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 // --- Bring-your-own-compute: Machines dashboard + metrics (M10) ------------\n\n /**\n * List the workspace's machines (the Machines dashboard). Each enrolled\n * selfhosted machine carries its derived state + latest metrics +\n * sharedSessionCount. Pass `sessionId` for an in-session view, which adds the\n * session's synthetic Modal group box + the active-sandbox pointer.\n */\n async listMachines(workspaceId: string, options: { sessionId?: string } = {}): Promise<MachinesResponse> {\n return await this.requestJson<MachinesResponse>(\"GET\", `/v1/workspaces/${workspaceId}/machines`, undefined, {\n ...(options.sessionId !== undefined ? { sessionId: options.sessionId } : {}),\n });\n }\n\n /**\n * Read the downsampled (~1/min) metrics series for ONE machine over a time\n * window (default 1h). The samples are oldest-first (a left-to-right chart).\n */\n async machineMetricsSeries(\n workspaceId: string,\n enrollmentId: string,\n options: { window?: \"15m\" | \"1h\" | \"6h\" | \"24h\" } = {},\n ): Promise<MetricSample[]> {\n const response = await this.requestJson<MachineMetricsSeriesResponse>(\n \"GET\",\n `/v1/workspaces/${workspaceId}/machines/${enrollmentId}/metrics/series`,\n undefined,\n { ...(options.window !== undefined ? { window: options.window } : {}) },\n );\n return response.samples;\n }\n\n // --- Self-hosted enrollment UX (design 11) --------------------------------\n\n /**\n * Resolve a pending device-enrollment flow by its user_code for the click-Grant\n * approve page (EnrollmentConsent). NO workspace in the path — the server\n * resolves the workspace from the (globally-unique-among-pending) code, then\n * authorizes the caller against it (enrollments:read). Rejects (404) when the\n * code is unknown/expired OR the caller lacks the grant — the two are\n * indistinguishable by design (no cross-workspace disclosure). Does not consume\n * the request.\n */\n async lookupDeviceEnrollment(userCode: string): Promise<DeviceEnrollmentLookupResponse> {\n return await this.requestJson<DeviceEnrollmentLookupResponse>(\"POST\", \"/v1/enrollments/device/lookup\", { userCode });\n }\n\n /**\n * Approve a pending device-enrollment flow (the LOUD consent step). `allowScreenControl`\n * is the authoritative screen-control consent (whole-machine is mandatory/implicit).\n * Lands an enrollment + a selfhosted sandbox and unblocks the agent's poll.\n */\n async approveDeviceEnrollment(\n workspaceId: string,\n request: { userCode: string; allowScreenControl?: boolean },\n ): Promise<DeviceEnrollmentApproveResponse> {\n return await this.requestJson<DeviceEnrollmentApproveResponse>(\n \"POST\",\n `/v1/workspaces/${workspaceId}/enrollments/device/approve`,\n { userCode: request.userCode, allowScreenControl: request.allowScreenControl ?? false },\n );\n }\n\n /** Deny a pending device-enrollment flow (the explicit \"no\" at the approve page). */\n async denyDeviceEnrollment(\n workspaceId: string,\n request: { userCode: string },\n ): Promise<DeviceEnrollmentDenyResponse> {\n return await this.requestJson<DeviceEnrollmentDenyResponse>(\n \"POST\",\n `/v1/workspaces/${workspaceId}/enrollments/device/deny`,\n { userCode: request.userCode },\n );\n }\n\n /**\n * Mint a short-TTL headless enroll token (the `oget_` token) for the fleet /\n * non-interactive enroll path. The returned `token` is SECRET — surface it once\n * with a copy-now warning; it cannot be re-read. `allowScreenControl` bakes the\n * screen-control consent into the token.\n */\n async mintEnrollToken(\n workspaceId: string,\n request: { allowScreenControl?: boolean } = {},\n ): Promise<MintEnrollTokenResponse> {\n return await this.requestJson<MintEnrollTokenResponse>(\n \"POST\",\n `/v1/workspaces/${workspaceId}/enrollments/token`,\n { allowScreenControl: request.allowScreenControl ?? false },\n );\n }\n\n /**\n * Swap a session's active sandbox (the user-authenticated equivalent of the\n * M7 `sandbox_swap` MCP tool). `target` is a `MachineView.sandboxId` from\n * `listMachines`, or \"session\"/\"default\" to swap back to the session's own\n * group box. Validation (ownership/liveness/epoch fence) is server-side; the\n * result echoes the resulting pointer (`swapped: false` + `reason` on a\n * rejected target or a lost epoch fence).\n */\n async swapActiveSandbox(\n workspaceId: string,\n sessionId: string,\n request: SwapActiveSandboxRequest,\n ): Promise<SwapActiveSandboxResponse> {\n return await this.requestJson<SwapActiveSandboxResponse>(\n \"POST\",\n `/v1/workspaces/${workspaceId}/sessions/${sessionId}/active-sandbox`,\n request,\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 /**\n * Replay durable events by sequence, ascending. `before` is exclusive and\n * returns the newest matching window. With `compact`, consecutive delta runs\n * may be coalesced; `payload.coalescedUntil` carries the run's last sequence\n * for resume cursors.\n */\n async listEvents(\n workspaceId: string,\n sessionId: string,\n options: { after?: number; before?: number; limit?: number; compact?: boolean } = {},\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.before !== undefined ? { before: String(options.before) } : {}),\n ...(options.limit !== undefined ? { limit: String(options.limit) } : {}),\n ...(options.compact ? { compact: \"1\" } : {}),\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 // --- Channel-A structured services (P4.4) ------------------------------------\n // FileSystem (Pierre tree), Git (Pierre diff), Terminal (exec + PTY). Each is a\n // synchronous API-direct point query; the fs.changed/git.changed/terminal.pty.*\n // notifications + the PTY output stream arrive on the existing event SSE.\n\n /** FileSystem: list a directory tree (feeds the Pierre file tree). */\n async fsList(workspaceId: string, sessionId: string, request: FsListRequest = {}): Promise<FsListResponse> {\n return await this.requestJson<FsListResponse>(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/fs/list`, request);\n }\n\n /** FileSystem: read a file (text or base64; binary-safe, size-capped). */\n async fsRead(workspaceId: string, sessionId: string, request: FsReadRequest): Promise<FsReadResponse> {\n return await this.requestJson<FsReadResponse>(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/fs/read`, request);\n }\n\n /** FileSystem: write a file (last-writer-wins; emits fs.changed). */\n async fsWrite(workspaceId: string, sessionId: string, request: FsWriteRequest): Promise<FsWriteResponse> {\n return await this.requestJson<FsWriteResponse>(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/fs/write`, request);\n }\n\n /** FileSystem: delete a path (emits fs.changed). */\n async fsDelete(workspaceId: string, sessionId: string, request: FsDeleteRequest): Promise<FsDeleteResponse> {\n return await this.requestJson<FsDeleteResponse>(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/fs/delete`, request);\n }\n\n /** FileSystem: move/rename a path (emits fs.changed; 409 if destination exists and overwrite is false). */\n async fsMove(workspaceId: string, sessionId: string, request: FsMoveRequest): Promise<FsMoveResponse> {\n return await this.requestJson<FsMoveResponse>(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/fs/move`, request);\n }\n\n /** FileSystem: create a directory (emits fs.changed; recursive defaults to true). */\n async fsMkdir(workspaceId: string, sessionId: string, request: FsMkdirRequest): Promise<FsMkdirResponse> {\n return await this.requestJson<FsMkdirResponse>(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/fs/mkdir`, request);\n }\n\n /** Git: working-tree/index status (the Pierre file-status feed). */\n async gitStatus(workspaceId: string, sessionId: string, request: GitStatusRequest = {}): Promise<GitStatusResponse> {\n return await this.requestJson<GitStatusResponse>(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/git/status`, request);\n }\n\n /** Git: structured diff hunks (the Pierre diff feed). */\n async gitDiff(workspaceId: string, sessionId: string, request: GitDiffRequest = {}): Promise<GitDiffResponse> {\n return await this.requestJson<GitDiffResponse>(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/git/diff`, request);\n }\n\n /** Git: commit log. */\n async gitLog(workspaceId: string, sessionId: string, request: GitLogRequest = {}): Promise<GitLogResponse> {\n return await this.requestJson<GitLogResponse>(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/git/log`, request);\n }\n\n /** Git: show a commit (diff vs first parent) or fetch a raw blob at a ref. */\n async gitShow(workspaceId: string, sessionId: string, request: GitShowRequest): Promise<GitShowResponse> {\n return await this.requestJson<GitShowResponse>(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/git/show`, request);\n }\n\n /** Terminal: run a bounded command, returning buffered stdout/stderr inline. */\n async terminalExec(workspaceId: string, sessionId: string, request: TerminalExecRequest): Promise<TerminalExecResponse> {\n return await this.requestJson<TerminalExecResponse>(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/terminal/exec`, request);\n }\n\n /** Terminal: open an interactive PTY. Output streams on the event SSE as\n * terminal.pty.output.delta; drive it with terminalPtyWrite. */\n async terminalPtyOpen(workspaceId: string, sessionId: string, request: PtyOpenRequest = {}): Promise<PtyOpenResponse> {\n return await this.requestJson<PtyOpenResponse>(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/terminal/pty`, request);\n }\n\n /** Terminal: send stdin to an open PTY (output rides A1). */\n async terminalPtyWrite(workspaceId: string, sessionId: string, request: PtyWriteRequest): Promise<void> {\n await this.requestVoid(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/terminal/pty/write`, request);\n }\n\n /** Terminal: resize an open PTY. */\n async terminalPtyResize(workspaceId: string, sessionId: string, request: PtyResizeRequest): Promise<void> {\n await this.requestVoid(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/terminal/pty/resize`, request);\n }\n\n /** Terminal: close an open PTY (idempotent). */\n async terminalPtyClose(workspaceId: string, sessionId: string, request: PtyCloseRequest): Promise<void> {\n await this.requestVoid(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/terminal/pty/close`, request);\n }\n\n // --- Stream surfacing: capability negotiation + viewer lifecycle (Phase 5) ---\n // The capability doc is the single source of UI truth (degradation is always a\n // value, never a crash). The desktop pixel plane (Channel B) is gated behind an\n // un-redacted-acknowledgment + a viewer holder; the structured terminal/files/\n // git surfaces (Channel A) ride the methods above and the event SSE.\n\n /** Read the negotiated capability doc for a session WITHOUT acquiring a viewer\n * holder (no warm, no spawn). Drives capability-gated rendering: which\n * surfaces mount, the per-surface unavailability reasons, and the lease\n * liveness the client polls on while `cold`/`warming`. The desktop URL/token\n * are minted in-process only when the box is warm AND the principal has\n * acknowledged the un-redacted plane. */\n async getStreamCapabilities(workspaceId: string, sessionId: string): Promise<SessionCapabilities> {\n return await this.requestJson<SessionCapabilities>(\n \"GET\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/stream-capabilities`);\n }\n\n /** Record the calling principal's acknowledgment of the un-redacted desktop\n * pixel plane (and, when the box is shared, the shared-exposure disclosure).\n * The desktop viewer-attach path returns 409 until this is recorded. */\n async acknowledgeStream(\n workspaceId: string, sessionId: string, request: AcknowledgeStreamRequest = {},\n ): Promise<AcknowledgeStreamResponse> {\n return await this.requestJson<AcknowledgeStreamResponse>(\n \"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/stream-capabilities/acknowledge`, request);\n }\n\n /** Attach a viewer holder (refcounted liveness — keeps the box warm while\n * watched/used), spinning the box up in-process when cold, and mint the scoped\n * direct-to-provider URLs for the requested plane(s). `request.desktop:true`\n * opts into the un-redacted pixel plane and mints the noVNC URL — that plane\n * alone throws `OpenGeniApiError(409)` when the un-redacted/shared\n * acknowledgment is missing (the consent gate). A terminal-only attach\n * (`desktop` omitted/false) warms the box + mints the pty-ws terminal cell with\n * NO consent gate. An omitted `viewerId` mints a fresh one. */\n async attachViewer(\n workspaceId: string, sessionId: string, request: AttachViewerRequest = {},\n ): Promise<AttachViewerResponse> {\n return await this.requestJson<AttachViewerResponse>(\n \"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/viewers`, request);\n }\n\n /** Heartbeat a viewer holder (Channel-A app-level liveness). A closed laptop\n * stops sending these → the reaper drops the holder within ~90s. Echoes\n * `leaseEpoch` so a superseded epoch is rejected (`alive:false` → re-attach). */\n async heartbeatViewer(\n workspaceId: string, sessionId: string, viewerId: string, request: ViewerHeartbeatRequest,\n ): Promise<ViewerHeartbeatResponse> {\n return await this.requestJson<ViewerHeartbeatResponse>(\n \"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/viewers/${viewerId}/heartbeat`, request);\n }\n\n /** Detach a viewer (delete this holder; idempotent delete-my-row). */\n async detachViewer(workspaceId: string, sessionId: string, viewerId: string): Promise<void> {\n await this.requestVoid(\"DELETE\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/viewers/${viewerId}`);\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: AddDocumentRequest): 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 /**\n * Delete a document from a base. Removes the document row and its indexed\n * chunks while leaving the uploaded file asset available for other uses.\n */\n async deleteDocument(workspaceId: string, baseId: string, documentId: string): Promise<void> {\n await this.requestVoid(\n \"DELETE\",\n `/v1/workspaces/${workspaceId}/document-bases/${baseId}/documents/${documentId}`,\n );\n }\n\n async searchDocuments(\n workspaceId: string,\n baseId: string,\n request: Omit<DocumentSearchRequest, \"baseIds\">,\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 async searchKnowledge(\n workspaceId: string,\n request: DocumentSearchRequest,\n ): Promise<DocumentSearchResponse> {\n return await this.requestJson<DocumentSearchResponse>(\"POST\", `/v1/workspaces/${workspaceId}/knowledge/search`, request);\n }\n\n async listKnowledgeMemories(workspaceId: string, request: KnowledgeMemorySearchRequest = {}): Promise<KnowledgeMemory[]> {\n const params = new URLSearchParams();\n if (request.query) params.set(\"query\", request.query);\n if (request.status) params.set(\"status\", request.status);\n if (request.kind) params.set(\"kind\", request.kind);\n if (request.scope) params.set(\"scope\", request.scope);\n if (request.limit) params.set(\"limit\", String(request.limit));\n const query = params.toString();\n return await this.requestJson<KnowledgeMemory[]>(\"GET\", `/v1/workspaces/${workspaceId}/knowledge/memories${query ? `?${query}` : \"\"}`);\n }\n\n async getKnowledgeMemory(workspaceId: string, memoryId: string): Promise<KnowledgeMemory> {\n return await this.requestJson<KnowledgeMemory>(\"GET\", `/v1/workspaces/${workspaceId}/knowledge/memories/${memoryId}`);\n }\n\n async createKnowledgeMemory(workspaceId: string, request: CreateKnowledgeMemoryRequest): Promise<KnowledgeMemory> {\n return await this.requestJson<KnowledgeMemory>(\"POST\", `/v1/workspaces/${workspaceId}/knowledge/memories`, request);\n }\n\n async updateKnowledgeMemory(workspaceId: string, memoryId: string, request: UpdateKnowledgeMemoryRequest): Promise<KnowledgeMemory> {\n return await this.requestJson<KnowledgeMemory>(\"PATCH\", `/v1/workspaces/${workspaceId}/knowledge/memories/${memoryId}`, request);\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 // --- Codex (ChatGPT) subscription (workspace-scoped) --------------------------------------------\n\n /** Connection state + the codex models the workspace may select (empty until connected). */\n async codexStatus(workspaceId: string): Promise<CodexConnectionStatus> {\n return await this.requestJson<CodexConnectionStatus>(\"GET\", `/v1/workspaces/${workspaceId}/codex/status`);\n }\n\n /** Begin device-code login: show `userCode` at `verificationUri`, then poll with `state`. */\n async codexConnectStart(workspaceId: string): Promise<CodexConnectStart> {\n return await this.requestJson<CodexConnectStart>(\"POST\", `/v1/workspaces/${workspaceId}/codex/connect/start`);\n }\n\n /** Poll device-code authorization with the `state` from {@link codexConnectStart}. */\n async codexConnectPoll(workspaceId: string, state: string): Promise<CodexConnectPoll> {\n return await this.requestJson<CodexConnectPoll>(\"POST\", `/v1/workspaces/${workspaceId}/codex/connect/poll`, { state });\n }\n\n /** Remaining usage / limits for the connected (ACTIVE) subscription. Back-compat. */\n async codexUsage(workspaceId: string): Promise<CodexUsage> {\n return await this.requestJson<CodexUsage>(\"GET\", `/v1/workspaces/${workspaceId}/codex/usage`);\n }\n\n /** Live per-account usage read (refreshes THIS account's bearer; writes the cache). */\n async codexAccountUsage(workspaceId: string, accountId: string): Promise<CodexUsage> {\n return await this.requestJson<CodexUsage>(\"GET\", `/v1/workspaces/${workspaceId}/codex/accounts/${accountId}/usage`);\n }\n\n /** Batched live refresh across every connected account, keyed by credential id. */\n async refreshCodexUsage(workspaceId: string): Promise<{ usage: CodexUsageMap }> {\n return await this.requestJson<{ usage: CodexUsageMap }>(\"POST\", `/v1/workspaces/${workspaceId}/codex/usage/refresh`);\n }\n\n /** Disconnect ALL accounts (legacy workspace-wide). Prefer `disconnectCodexAccount`. */\n async codexDisconnect(workspaceId: string): Promise<{ disconnected: boolean }> {\n return await this.requestJson<{ disconnected: boolean }>(\"DELETE\", `/v1/workspaces/${workspaceId}/codex`);\n }\n\n /** List every connected Codex account + the workspace active pointer + settings. */\n async listCodexAccounts(workspaceId: string): Promise<CodexAccountsResponse> {\n return await this.requestJson<CodexAccountsResponse>(\"GET\", `/v1/workspaces/${workspaceId}/codex/accounts`);\n }\n\n /** Switch the workspace ACTIVE Codex account (the one unpinned sessions use). */\n async activateCodexAccount(workspaceId: string, accountId: string): Promise<{ activated: boolean; accountId: string }> {\n return await this.requestJson<{ activated: boolean; accountId: string }>(\"POST\", `/v1/workspaces/${workspaceId}/codex/accounts/${accountId}/activate`);\n }\n\n /** P3: enable/disable Codex auto-rotation and/or pick the strategy. Returns the effective settings. */\n async setCodexRotationSettings(\n workspaceId: string,\n patch: { rotationEnabled?: boolean; rotationStrategy?: CodexRotationSettings[\"rotationStrategy\"] },\n ): Promise<CodexRotationSettings> {\n return await this.requestJson<CodexRotationSettings>(\"PATCH\", `/v1/workspaces/${workspaceId}/codex/settings`, patch);\n }\n\n /** Disconnect ONE Codex account by id (re-picks active when the removed one was active). */\n async disconnectCodexAccount(workspaceId: string, accountId: string): Promise<{ disconnected: boolean; newActiveId: string | null }> {\n return await this.requestJson<{ disconnected: boolean; newActiveId: string | null }>(\"DELETE\", `/v1/workspaces/${workspaceId}/codex/accounts/${accountId}`);\n }\n\n /** Rename a Codex account (label only in P1). */\n async renameCodexAccount(workspaceId: string, accountId: string, label: string | null): Promise<CodexAccount> {\n return await this.requestJson<CodexAccount>(\"PATCH\", `/v1/workspaces/${workspaceId}/codex/accounts/${accountId}`, { label });\n }\n\n /** Pin (or unpin via \"auto\") a session's Codex account. Applies on the next turn. */\n async pinSessionCodexAccount(workspaceId: string, sessionId: string, target: string): Promise<{ pinned: string }> {\n return await this.requestJson<{ pinned: string }>(\"POST\", `/v1/workspaces/${workspaceId}/sessions/${sessionId}/codex-account`, { target });\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","// Zero-dependency desktop (noVNC) transport contract.\n//\n// The actual `@novnc/novnc` RFB import is a browser-only DOM dependency and\n// lives in `@opengeni/react` (lazily imported, SSR-safe). The framework-agnostic\n// SDK ships only the transport CONTRACT: a URL assembler, the minimal RFB\n// surface the component drives, the connection state machine, and the\n// rotation/fence reducers — all pure and unit-testable, with no DOM and no deps.\n//\n// Channel A (terminal-as-events, files, git) needs NO new transport here: those\n// are event projections + the synchronous fs/git/terminal point queries on\n// `OpenGeniClient`. Only the desktop pixel plane (Channel B, direct-to-provider)\n// needs this.\n\nimport type { DesktopStreamCapability, StreamUrlRotatedPayload } from \"./types\";\n\n/**\n * Translate the negotiated desktop capability into the WebSocket URL the noVNC\n * RFB client connects to. The scoped provider token is ALREADY embedded in the\n * minted `url` (Modal tunnel host, Blaxel `bl_preview_token`, Daytona signed\n * preview) by `session.resolveExposedPort(6080)` — we do NOT append `cap.token`\n * as a query param (that double-auth was an adversarial-review bug: the box runs\n * `-nopw` in v1, so the RFB password is meaningless and the real auth is the\n * tunnel token in the host). We only normalize the scheme to `ws`/`wss` and, when\n * the minted URL points at a `vnc.html` viewer page, rewrite it to the\n * websockify socket path noVNC actually dials.\n */\nexport function desktopSocketUrl(cap: Pick<DesktopStreamCapability, \"url\">): string {\n if (!cap.url) {\n throw new Error(\"desktop capability has no url (transport is null)\");\n }\n const u = new URL(cap.url);\n // https → wss, http → ws (noVNC dials a WebSocket, not the HTTP viewer page).\n if (u.protocol === \"https:\") {\n u.protocol = \"wss:\";\n } else if (u.protocol === \"http:\") {\n u.protocol = \"ws:\";\n }\n // A minted `…/vnc.html` (or `…/vnc_lite.html`) viewer page → the websockify\n // socket the page itself would open (`…/websockify`). A bare host/path with no\n // viewer page is already the socket path; leave it untouched.\n if (/\\/vnc(_lite)?\\.html$/.test(u.pathname)) {\n u.pathname = u.pathname.replace(/\\/vnc(_lite)?\\.html$/, \"/websockify\");\n u.search = \"\";\n u.hash = \"\";\n }\n return u.toString();\n}\n\n/**\n * The minimal RFB surface the React component drives. Lets tests (and 3rd\n * parties swapping noVNC for a WebRTC client in v3) provide a fake without the\n * DOM. Matches `@novnc/novnc`'s RFB constructor + lifecycle.\n */\nexport interface DesktopRfbLike {\n viewOnly: boolean;\n scaleViewport: boolean;\n /**\n * 1:1 viewport clipping. We always drive this FALSE: with clipping on, noVNC\n * paints the framebuffer pixel-for-pixel and scrolls/crops to the container\n * (the \"zoomed in\" look). FALSE lets `scaleViewport` shrink the 1280x800 frame\n * to fit the panel (aspect-preserved). Declared so the hook can pin it instead\n * of relying on noVNC's default — `scaleViewport=true` forces clip off\n * internally, but a stale/partial state on reconnect could leave it on.\n */\n clipViewport: boolean;\n addEventListener(\n type: \"connect\" | \"disconnect\" | \"securityfailure\",\n cb: (e?: unknown) => void,\n ): void;\n removeEventListener?: (\n type: \"connect\" | \"disconnect\" | \"securityfailure\",\n cb: (e?: unknown) => void,\n ) => void;\n disconnect(): void;\n}\n\nexport type DesktopRfbFactory = (\n target: HTMLElement,\n url: string,\n opts: { credentials?: { password?: string | undefined } | undefined },\n) => DesktopRfbLike;\n\nexport type DesktopConnectionState =\n | \"idle\"\n | \"negotiating\"\n | \"connecting\"\n | \"connected\"\n | \"rotating\"\n | \"reconnecting\"\n | \"error\"\n | \"ended\";\n\nexport type DesktopStreamEvent =\n | { type: \"negotiated\" }\n | { type: \"connected\" }\n | { type: \"disconnected\" }\n | { type: \"rotate\" }\n | { type: \"fail\" }\n | { type: \"abort\" };\n\n/**\n * Pure reducer for the desktop connection lifecycle. The component owns the RFB\n * object + DOM; this owns the transitions so they are unit-testable. Mirrors the\n * Channel-A stream reducer discipline.\n */\nexport function nextDesktopState(\n current: DesktopConnectionState,\n ev: DesktopStreamEvent,\n): DesktopConnectionState {\n switch (ev.type) {\n case \"negotiated\":\n return \"connecting\";\n case \"connected\":\n return \"connected\";\n case \"rotate\":\n return current === \"connected\" ? \"rotating\" : current;\n case \"disconnected\":\n // A deliberate teardown (ended) stays ended; everything else reconnects.\n return current === \"ended\" ? \"ended\" : \"reconnecting\";\n case \"fail\":\n return \"error\";\n case \"abort\":\n return \"ended\";\n default:\n return current;\n }\n}\n\n// `DesktopStreamCapability` is the desktop cell of `SessionCapabilities`; alias\n// it for the rotation reducer below without depending on the whole doc.\nexport type DesktopStreamCapabilityLike = {\n url: string | null;\n token: string | null;\n expiresAt: string | null;\n};\n\n/**\n * Apply a `stream.url.rotated` event onto a desktop capability, fencing on\n * leaseEpoch (split-brain). A rotation minted under an epoch the client has\n * already advanced PAST is from a superseded owner and is dropped (returns\n * null); otherwise the fresh url/token/expiresAt are folded in.\n */\nexport function applyUrlRotation<T extends DesktopStreamCapabilityLike>(\n cap: T,\n payload: StreamUrlRotatedPayload,\n knownEpoch: number,\n): T | null {\n if (payload.leaseEpoch < knownEpoch) {\n return null;\n }\n return { ...cap, url: payload.url, token: payload.token, expiresAt: payload.expiresAt };\n}\n","// Zero-dependency interactive-terminal (ttyd PTY-over-websocket) transport\n// contract. Symmetric with `desktop.ts`: the SDK ships only the pure transport\n// CONTRACT (a URL assembler + the ttyd wire-protocol frame codec), with no DOM\n// and no deps. The actual WebSocket open + xterm attach lives in `@opengeni/react`\n// (`use-terminal-stream.ts`).\n//\n// The interactive terminal is a REAL PTY streamed over the SAME Modal raw-TLS\n// tunnel as the desktop noVNC, with the SAME scoped stream-token mechanism. The\n// box bakes `ttyd` on `TERMINAL_STREAM_PORT` (7681); `session.resolveExposedPort`\n// mints the tunnel URL. The Terminal capability cell carries transport \"pty-ws\" +\n// that url + the scoped token when the box is warm and a viewer is attached.\n\nimport type { TerminalCapability } from \"./types\";\n\n/**\n * Translate the negotiated `pty-ws` Terminal capability into the WebSocket URL\n * the ttyd client dials. The scoped provider token is ALREADY embedded in the\n * minted `url` (the Modal tunnel host) by `session.resolveExposedPort(7681)` —\n * we do NOT append `cap.token` (identical posture to the desktop: the gate is the\n * unguessable short-TTL tunnel URL + the server-recorded scoped stream token; ttyd\n * runs `--writable` with no `-c` credential in v1). We only normalize the scheme\n * to `ws`/`wss`; a bare host is already the ttyd websocket endpoint.\n */\nexport function terminalSocketUrl(cap: Pick<TerminalCapability, \"url\">): string {\n if (!cap.url) {\n throw new Error(\"terminal capability has no url (transport is not pty-ws)\");\n }\n const u = new URL(cap.url);\n // https → wss, http → ws (ttyd is dialed as a WebSocket, not an HTTP page).\n if (u.protocol === \"https:\") {\n u.protocol = \"wss:\";\n } else if (u.protocol === \"http:\") {\n u.protocol = \"ws:\";\n }\n return u.toString();\n}\n\n// ── ttyd wire protocol ───────────────────────────────────────────────────────\n// ttyd frames are a single ASCII command char + payload. These mirror ttyd's\n// `protocol.h` Command enum. Client→server and server→client share the byte 0/1/2\n// values but mean different things per direction (see below).\n\n/** ttyd subprotocol — REQUIRED on the WebSocket handshake or ttyd refuses it. */\nexport const TTYD_SUBPROTOCOL = \"tty\";\n\n/** Client→server command bytes (the first char of each outbound text frame). */\nexport const TtydClientCommand = {\n /** stdin: \"0\" + raw input bytes. */\n INPUT: \"0\",\n /** window resize: \"1\" + JSON.stringify({ columns, rows }). */\n RESIZE: \"1\",\n /** flow-control pause (back-pressure): \"2\". */\n PAUSE: \"2\",\n /** flow-control resume: \"3\". */\n RESUME: \"3\",\n} as const;\n\n/** Server→client command bytes (the first char of each inbound frame). */\nexport const TtydServerCommand = {\n /** stdout/stderr: \"0\" + raw output bytes (write the rest into xterm). */\n OUTPUT: \"0\",\n /** set the window title: \"1\" + title string. */\n SET_WINDOW_TITLE: \"1\",\n /** ttyd client preferences JSON: \"2\" + json (ignored by us). */\n SET_PREFERENCES: \"2\",\n} as const;\n\n/**\n * The ttyd handshake's first frame: an auth message. ttyd expects\n * `JSON.stringify({ AuthToken })` as the FIRST text frame on the socket. We send\n * an empty token — our gate is the tunnel URL + scoped stream token, NOT a ttyd\n * `-c` basic-auth credential (which the box does not set in v1). Optional ttyd\n * `columns`/`rows` can ride this frame to seed the PTY size before the first\n * resize. Pure (string-building only) so it stays unit-testable in the SDK.\n */\nexport function ttydAuthFrame(opts?: { columns?: number; rows?: number }): string {\n const frame: { AuthToken: string; columns?: number; rows?: number } = { AuthToken: \"\" };\n if (opts?.columns && opts.columns > 0) frame.columns = opts.columns;\n if (opts?.rows && opts.rows > 0) frame.rows = opts.rows;\n return JSON.stringify(frame);\n}\n\n/** Build a client→server INPUT (stdin) frame: \"0\" + data. */\nexport function ttydInputFrame(data: string): string {\n return TtydClientCommand.INPUT + data;\n}\n\n/** Build a client→server RESIZE frame: \"1\" + JSON.stringify({ columns, rows }). */\nexport function ttydResizeFrame(columns: number, rows: number): string {\n return TtydClientCommand.RESIZE + JSON.stringify({ columns, rows });\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\n// Mirror of `@opengeni/contracts` SandboxBackend (11 values; every member is\n// additive at the end). 3-way enum parity is pinned by\n// `test/contract-parity.test.ts`.\nexport type SandboxBackend =\n | \"docker\"\n | \"modal\"\n | \"local\"\n | \"none\"\n | \"daytona\"\n | \"runloop\"\n | \"e2b\"\n | \"blaxel\"\n | \"cloudflare\"\n | \"vercel\"\n | \"selfhosted\";\n\n// Mirror of `@opengeni/contracts` SandboxOs. Only \"linux\" is reachable in v1.\nexport type SandboxOs = \"linux\" | \"macos\" | \"windows\";\n\n// Mirror of `@opengeni/contracts` SandboxCapabilityName.\nexport type SandboxCapabilityName =\n | \"FileSystem\"\n | \"Terminal\"\n | \"Git\"\n | \"DesktopStream\"\n | \"Recording\";\n\n// Mirror of `@opengeni/contracts` CapabilityUnavailableReason.\nexport type CapabilityUnavailableReason =\n | \"backend_unsupported\"\n | \"os_unsupported\"\n | \"not_provisioned\"\n | \"disabled_by_policy\"\n | \"lease_cold\"\n | \"tier_headless\"\n // selfhosted (bring-your-own-compute) negotiation states:\n | \"agent_offline\"\n | \"agent_reconnecting\"\n | \"consent_required\"\n | \"display_unavailable\";\n\n// Mirror of `@opengeni/contracts` SessionCapabilities (the negotiated handshake\n// document). The descriptor table itself is NOT mirrored — it lives in\n// contracts (P0.1) and is consumed by the SDK config in a later PR.\nexport type SessionCapabilities = {\n sessionId: string;\n backend: SandboxBackend;\n os: SandboxOs;\n liveness: \"cold\" | \"warming\" | \"warm\" | \"draining\";\n leaseEpoch: number;\n viewerHeartbeatIntervalMs: number;\n FileSystem: {\n available: boolean;\n readOnly: boolean;\n root: string;\n pathSep: \"/\" | \"\\\\\";\n treeMode: \"lazy\" | \"snapshot\";\n reason: CapabilityUnavailableReason | null;\n };\n Terminal: {\n transport: \"sse-events\" | \"pty-ws\" | null;\n ptyCapable: boolean;\n shell: string;\n url: string | null;\n token: string | null;\n reason: CapabilityUnavailableReason | null;\n };\n Git: {\n available: boolean;\n repos: string[];\n reason: CapabilityUnavailableReason | null;\n };\n DesktopStream: {\n // \"relay-frames\" + \"frames\": the selfhosted framebuffer stream — PNG-per-frame\n // protobuf datagrams over the relay, painted by a canvas client (NOT RFB).\n transport: \"vnc-ws\" | \"rdp-ws\" | \"webrtc\" | \"relay-frames\" | null;\n client: \"novnc\" | \"web-rdp\" | \"frames\" | null;\n mode: \"read-only\" | \"interactive\";\n url: string | null;\n token: string | null;\n expiresAt: string | null;\n resolution: [number, number];\n unredacted: boolean;\n requiresAcknowledgment: boolean;\n acknowledged: boolean;\n // Shared-exposure disclosure (addendum E.1): `shared` when the group has >1\n // session; `sharedSessionIds` lists the OTHER sessions' ids ONLY (never their\n // conversation/metadata).\n shared: boolean;\n sharedSessionIds: string[];\n reason: CapabilityUnavailableReason | null;\n };\n Recording: {\n available: boolean;\n modes: (\"manual\" | \"on-turn\" | \"on-verify\")[];\n codecs: (\"h264-mp4\" | \"vp9-webm\")[];\n reason: CapabilityUnavailableReason | null;\n };\n ComputerUse: {\n available: boolean;\n readOnly: boolean;\n reason: CapabilityUnavailableReason | null;\n };\n negotiatedAt: string;\n};\n\n// Convenience aliases for the per-surface cells of `SessionCapabilities`, so the\n// client hooks/components can take a single cell without restating the inline\n// shape. These are exact structural views of the cells above.\nexport type FileSystemCapability = SessionCapabilities[\"FileSystem\"];\nexport type TerminalCapability = SessionCapabilities[\"Terminal\"];\nexport type GitCapability = SessionCapabilities[\"Git\"];\nexport type DesktopStreamCapability = SessionCapabilities[\"DesktopStream\"];\nexport type RecordingCapability = SessionCapabilities[\"Recording\"];\nexport type ComputerUseCapability = SessionCapabilities[\"ComputerUse\"];\n\n// ── Stream-surfacing client surface (Phase 5) ───────────────────────────────\n// Mirrors of the contracts viewer-attach / acknowledge / heartbeat shapes that\n// the capability-gated client (`@opengeni/react`) drives. The desktop pixel\n// plane rides Channel B (direct-to-provider noVNC); the structured terminal/\n// files/git surfaces ride Channel A (the existing event spine + the synchronous\n// fs/git/terminal point queries above). These are TYPES only (the SDK keeps zero\n// runtime deps); the contract-parity test pins them.\n\n// Mirror of `@opengeni/contracts` StreamUrlRotatedPayload — the Channel-A event\n// the client folds in to hot-swap its noVNC socket on a box rollover, fenced on\n// leaseEpoch.\nexport type StreamUrlRotatedPayload = {\n url: string;\n token: string | null;\n expiresAt: string | null;\n leaseEpoch: number;\n transport: \"vnc-ws\";\n viewerId: string | null;\n};\nexport type StreamOpenedPayload = { viewerId: string; shared: boolean; viewerCount: number };\nexport type StreamClosedPayload = {\n viewerId: string;\n reason: \"client-disconnect\" | \"reaped\" | \"revoked\" | \"box-rollover\";\n viewerCount: number;\n};\nexport type StreamRevokedPayload = {\n viewerId: string | null;\n reason: \"grant-revoked\" | \"session-failed\" | \"admin\";\n};\n\n// Mirror of `@opengeni/contracts` AttachViewerRequest. Omitting `viewerId` mints\n// a fresh holder id (returned on the response, carried through heartbeat/detach).\n// `desktop:true` opts into the un-redacted pixel plane (the consent-gated noVNC\n// stream); a terminal/files-only warm attach omits it (defaults false) so it\n// warms the box + mints the pty-ws terminal cell WITHOUT tripping the consent 409.\nexport type AttachViewerRequest = { viewerId?: string | undefined; desktop?: boolean | undefined };\n\n// Mirror of `@opengeni/contracts` ViewerHolder + the P4.2 desktop-stream fields\n// the POST /viewers handler folds in when the pixel plane is minted in-process.\nexport type ViewerHolder = {\n viewerId: string;\n sandboxGroupId: string;\n liveness: \"cold\" | \"warming\" | \"warm\" | \"draining\";\n leaseEpoch: number;\n viewerHeartbeatIntervalMs: number;\n dataPlaneUrl: string | null;\n};\nexport type AttachViewerResponse = ViewerHolder & {\n // The scoped desktop-stream address minted for THIS holder (P4.2). Null when\n // the deployment is headless / desktop is disabled / the mint degraded —\n // the client then falls back to the Channel-A surfaces only.\n streamToken: string | null;\n streamExpiresAt: string | null;\n resolution: [number, number] | null;\n transport: \"vnc-ws\" | null;\n client: \"novnc\" | null;\n // The scoped ttyd PTY-over-websocket address minted for THIS holder — the REAL\n // interactive terminal, symmetric with the desktop pixel plane (same Modal\n // tunnel, same scoped stream token). Populated on a warm box; null when the\n // terminal mint degraded (headless / no secret / tunnel failure), in which case\n // the client falls back to the Channel-A read-only command-output firehose.\n // `terminalTransport` is \"pty-ws\" iff a live `terminalUrl` was minted.\n terminalUrl: string | null;\n terminalToken: string | null;\n terminalTransport: \"pty-ws\" | null;\n};\n\n// Mirror of `@opengeni/contracts` AcknowledgeStreamRequest/Response — the\n// un-redacted-pixel + shared-exposure consent gate (P3.2).\nexport type AcknowledgeStreamRequest = {\n acknowledgeUnredacted?: boolean | undefined;\n acknowledgeShared?: boolean | undefined;\n};\nexport type AcknowledgeStreamResponse = { acknowledged: boolean; acknowledgedShared: boolean };\n\n// Mirror of `@opengeni/contracts` ViewerHeartbeatRequest/Response — the\n// Channel-A viewer-liveness ping, epoch-fenced (a stale-epoch beat → alive:false\n// → the client re-attaches).\nexport type ViewerHeartbeatRequest = { leaseEpoch: number };\nexport type ViewerHeartbeatResponse = { alive: boolean };\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 SessionMcpServerInput = {\n id: string;\n name?: string | undefined;\n url: string;\n allowedTools?: string[] | undefined;\n timeoutMs?: number | undefined;\n cacheToolsList?: boolean | undefined;\n headers?: Record<string, string> | undefined;\n};\n\nexport type SessionMcpCredentialUpdateInput = {\n id: string;\n headers: Record<string, string>;\n};\n\nexport type SessionMcpServerMetadata = {\n id: string;\n name: string | null;\n url: string;\n headerNames: string[];\n credentialVersion: number;\n};\n\nexport type ConnectionKind = \"oauth2\" | \"api_key\" | \"app_install\" | \"delegated\";\nexport type ConnectionStatus = \"active\" | \"needs_reauth\" | \"revoked\" | \"error\";\n\nexport type McpServerConnectionRef = {\n connectionId?: string | undefined;\n providerDomain: string;\n kind?: ConnectionKind | undefined;\n scopes?: string[] | undefined;\n resource?: string | undefined;\n subjectScope?: \"workspace\" | \"subject\" | undefined;\n};\n\nexport type ConnectionMetadata = {\n id: string;\n accountId: string;\n workspaceId: string;\n subjectId: string | null;\n providerDomain: string;\n kind: ConnectionKind;\n status: ConnectionStatus;\n grantedScopes: string[];\n expiresAt: string | null;\n lastRefreshAt: string | null;\n lastUsedAt: string | null;\n lastError: string | null;\n version: number;\n metadata: Record<string, unknown>;\n createdBySubjectId: string | null;\n updatedBySubjectId: string | null;\n createdAt: string;\n updatedAt: string;\n};\n\nexport type CreateConnectionRequest = {\n providerDomain: string;\n kind: ConnectionKind;\n subjectId?: string | null | undefined;\n credential: Record<string, unknown>;\n grantedScopes?: string[] | undefined;\n expiresAt?: string | null | undefined;\n metadata?: Record<string, unknown> | undefined;\n};\n\nexport type UpdateConnectionRequest = {\n providerDomain?: string | undefined;\n subjectId?: string | null | undefined;\n kind?: ConnectionKind | undefined;\n status?: ConnectionStatus | undefined;\n credential?: Record<string, unknown> | undefined;\n grantedScopes?: string[] | undefined;\n expiresAt?: string | null | undefined;\n metadata?: Record<string, unknown> | undefined;\n};\n\nexport type ConnectionResponse = {\n connection: ConnectionMetadata;\n};\n\nexport type ListConnectionsResponse = {\n connections: ConnectionMetadata[];\n};\n\nexport type OAuthStartRequest = {\n providerDomain?: string | undefined;\n mcpUrl?: string | undefined;\n resource?: string | undefined;\n requestedScopes?: string[] | undefined;\n returnPath?: string | undefined;\n connectionId?: string | undefined;\n};\n\nexport type OAuthStartResponse = {\n state: string;\n authorizationUrl: string | null;\n expiresAt: string;\n};\n\nexport type IntegrationClientMetadata = {\n client_id: string;\n client_name: \"OpenGeni\";\n redirect_uris: string[];\n token_endpoint_auth_method: \"none\";\n grant_types: Array<\"authorization_code\" | \"refresh_token\">;\n response_types: [\"code\"];\n};\n\nexport type Session = {\n id: string;\n workspaceId: string;\n accountId: string;\n status: SessionStatus;\n initialMessage: string;\n title: string | null;\n titleSource: \"user\" | \"agent\" | null;\n // Per-session agent persona/system instructions supplied at create; null when\n // the session carried none. Org-visible metadata, never a timeline event.\n instructions: string | null;\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 mcpServers: SessionMcpServerMetadata[];\n createIdempotencyKey: string | null;\n temporalWorkflowId: string | null;\n activeTurnId: string | null;\n lastSequence: number;\n /** Multi-account Codex (P1): the account this session is pinned to (null ⇒ follow workspace active). */\n codexPinnedCredentialId?: string | null;\n /** Multi-account Codex (P1): the account the most recent turn ran on (the \"Running on:\" indicator). */\n codexLastCredentialId?: string | null;\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 \"tool.auth_needed\",\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 // Channel-B desktop pixel-plane signals (mirror of contracts SessionEventType;\n // the contract-parity test asserts sorted equality).\n \"stream.url.rotated\",\n \"stream.opened\",\n \"stream.closed\",\n \"stream.revoked\",\n // Channel-B recording signals (P4.3 — \"agent films itself proving the fix\").\n \"recording.started\",\n \"recording.available\",\n \"recording.failed\",\n // Channel-A structured-service notifications (P4.4; mirror of contracts\n // SessionEventType — the contract-parity test asserts sorted equality).\n \"fs.changed\",\n \"git.changed\",\n \"terminal.pty.started\",\n \"terminal.pty.output.delta\",\n \"terminal.pty.exited\",\n \"session.title_set\",\n // Multi-account Codex (P1): the session's inference account changed.\n \"codex.account.switched\",\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\nexport type ToolAuthNeededPayload = {\n serverId: string;\n toolName?: string | null | undefined;\n providerDomain: string;\n connectionId?: string | null | undefined;\n reason: \"missing_connection\" | \"expired\" | \"insufficient_scope\" | \"refresh_failed\";\n scopes?: string[] | undefined;\n resource?: string | undefined;\n authorizationUrl?: string | undefined;\n subjectId?: 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\n// Recording payloads (P4.3 — plain TS mirror of the contracts Zod schemas; the\n// SDK is zero-runtime-dep so these are TYPES, not Zod, F15). The contract-parity\n// test asserts the event-type literals; these shapes document the wire payloads.\nexport type RecordingMode = \"manual\" | \"on-turn\" | \"on-verify\";\nexport type RecordingCodec = \"h264-mp4\" | \"vp9-webm\";\nexport type RecordingContentType = \"video/mp4\" | \"video/webm\";\nexport type RecordingFailedReason =\n | \"ffmpeg-error\"\n | \"box-death\"\n | \"box-rollover\"\n | \"upload-failed\"\n | \"max-bytes-exceeded\"\n | \"display-unavailable\";\n\nexport type RecordingStartedPayload = {\n recordingId: string;\n turnId: string | null;\n mode: RecordingMode;\n codec: RecordingCodec;\n dimensions: [number, number];\n framerate: number;\n startedAt: string;\n reason?: string | null | undefined;\n};\nexport type RecordingAvailablePayload = {\n recordingId: string;\n turnId: string | null;\n codec: RecordingCodec;\n contentType: RecordingContentType;\n storageKey: string;\n durationSeconds: number | null;\n sizeBytes: number;\n dimensions: [number, number];\n};\nexport type RecordingFailedPayload = {\n recordingId: string;\n turnId: string | null;\n reason: RecordingFailedReason;\n detail?: string | null | undefined;\n};\n\n// ── Channel-A structured services (P4.4) — hand-written wire mirrors ─────────\n\n// A1 notification payloads.\nexport type SandboxCommandOutputDeltaPayload = {\n stream: \"stdout\" | \"stderr\";\n chunk: string;\n commandId?: string | undefined;\n seq?: number | undefined;\n};\nexport type FsChangeKind = \"created\" | \"modified\" | \"deleted\" | \"renamed\";\nexport type FsChangedPayload = {\n changes: { path: string; kind: FsChangeKind; isDir: boolean; sizeBytes: number | null; oldPath?: string | undefined }[];\n source: \"write\" | \"watch\" | \"agent\";\n revision: number;\n leaseEpoch: number;\n};\nexport type GitChangedPayload = {\n head: string | null;\n dirty: boolean;\n ahead: number;\n behind: number;\n changedFileCount: number;\n reason: \"commit\" | \"checkout\" | \"stage\" | \"worktree\" | \"fetch\" | \"unknown\";\n revision: number;\n leaseEpoch: number;\n};\nexport type TerminalPtyStartedPayload = { ptyId: string; cols: number; rows: number; shell: string; cwd: string };\nexport type TerminalPtyOutputDeltaPayload = { ptyId: string; stream: \"stdout\" | \"stderr\"; chunk: string; seq: number };\nexport type TerminalPtyExitedPayload = { ptyId: string; exitCode: number | null; reason: \"exit\" | \"killed\" | \"owner_gone\" | \"timeout\" };\n\n// A2 FileSystem request/response.\nexport type FsNodeType = \"file\" | \"dir\" | \"symlink\" | \"other\";\nexport type FsTreeNode = {\n name: string;\n path: string;\n type: FsNodeType;\n sizeBytes: number | null;\n mtimeMs: number | null;\n mode: number | null;\n children?: FsTreeNode[] | undefined;\n truncated: boolean;\n};\nexport type FsEncoding = \"utf8\" | \"base64\";\nexport type FsListRequest = { path?: string; depth?: number; maxEntries?: number; includeHidden?: boolean };\nexport type FsListResponse = { root: FsTreeNode; revision: number; truncated: boolean };\nexport type FsReadRequest = { path: string; encoding?: FsEncoding; maxBytes?: number };\nexport type FsReadResponse = { path: string; encoding: FsEncoding; content: string; sizeBytes: number; truncated: boolean; isBinary: boolean; revision: number };\nexport type FsWriteRequest = { path: string; encoding?: FsEncoding; content: string; overwrite?: boolean; createParents?: boolean };\nexport type FsWriteResponse = { path: string; sizeBytes: number; revision: number };\nexport type FsDeleteRequest = { path: string; recursive?: boolean };\nexport type FsDeleteResponse = { revision: number };\nexport type FsMoveRequest = { path: string; newPath: string; overwrite?: boolean; createParents?: boolean };\nexport type FsMoveResponse = { path: string; newPath: string; revision: number };\nexport type FsMkdirRequest = { path: string; recursive?: boolean };\nexport type FsMkdirResponse = { path: string; revision: number };\n\n// A2 Git request/response (the Pierre-diff feed).\nexport type GitFileStatusCode = \"added\" | \"modified\" | \"deleted\" | \"renamed\" | \"copied\" | \"untracked\" | \"ignored\" | \"conflicted\" | \"typechange\";\nexport type GitFileStatus = { path: string; oldPath: string | null; index: GitFileStatusCode | null; worktree: GitFileStatusCode | null; isConflicted: boolean };\nexport type GitStatusRequest = { path?: string };\nexport type GitStatusResponse = { isRepo: boolean; head: string | null; detached: boolean; upstream: string | null; ahead: number; behind: number; files: GitFileStatus[]; revision: number };\nexport type GitDiffLineType = \"context\" | \"add\" | \"del\" | \"meta\";\nexport type GitDiffLine = { type: GitDiffLineType; oldNo: number | null; newNo: number | null; text: string };\nexport type GitDiffHunk = { oldStart: number; oldLines: number; newStart: number; newLines: number; header: string; lines: GitDiffLine[] };\nexport type GitFileDiff = { path: string; oldPath: string | null; status: GitFileStatusCode; isBinary: boolean; isImage: boolean; additions: number; deletions: number; hunks: GitDiffHunk[]; truncated: boolean };\nexport type GitDiffRequest = { path?: string; staged?: boolean; fromRef?: string; toRef?: string; pathspec?: string[]; contextLines?: number; maxBytesPerFile?: number };\nexport type GitDiffResponse = { files: GitFileDiff[]; revision: number };\nexport type GitLogRequest = { path?: string; ref?: string; maxCount?: number; skip?: number; pathspec?: string[] };\nexport type GitCommit = {\n sha: string;\n shortSha: string;\n parents: string[];\n author: { name: string; email: string; timestamp: number };\n committer: { name: string; email: string; timestamp: number };\n subject: string;\n body: string;\n refs: string[];\n};\nexport type GitLogResponse = { commits: GitCommit[]; hasMore: boolean };\nexport type GitShowRequest = { path?: string; ref: string; filePath?: string; encoding?: FsEncoding; maxBytesPerFile?: number };\nexport type GitShowResponse = { commit: GitCommit | null; files: GitFileDiff[]; blob: { content: string; encoding: FsEncoding; sizeBytes: number; truncated: boolean } | null; revision: number };\n\n// A2 Terminal exec + PTY.\nexport type TerminalExecRequest = { command: string; cwd?: string; timeoutMs?: number; emitStream?: boolean };\nexport type TerminalExecResponse = { stdout: string; stderr: string; exitCode: number | null; running: boolean; wallTimeSeconds: number };\nexport type PtyOpenRequest = { cols?: number; rows?: number; cwd?: string; shell?: string };\nexport type PtyOpenResponse = { ptyId: string; streamVia: \"sse-events\"; supportsInput: boolean };\nexport type PtyWriteRequest = { ptyId: string; data: string };\nexport type PtyResizeRequest = { ptyId: string; cols: number; rows: number };\nexport type PtyCloseRequest = { ptyId: string };\n\nexport type SessionStructuredCapabilities = {\n FileSystem: { available: boolean; readOnly: boolean; root: string };\n Terminal: { events: boolean; exec: boolean; pty: { available: boolean } };\n Git: { available: boolean; repos: string[] };\n};\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 // Per-session agent persona/system instructions (org-visible metadata, not a\n // secret). Delivered system-level, composed AFTER the per-workspace persona —\n // how a host supplies per-agent-type prompts without leaking them into the\n // user-visible timeline. Trimmed, non-empty, max 32768 chars.\n instructions?: string | undefined;\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 // The enrolled machine (a sandbox id) to run this session on; seeds the\n // active-sandbox pointer at creation so the first turn lands on it.\n targetSandboxId?: string | undefined;\n // Host working directory for a connected-machine target (the agent runs here;\n // default = the machine's launch dir). Ignored for managed sandboxes.\n workingDir?: string | 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 mcpServers?: SessionMcpServerInput[] | undefined;\n // Shared-sandbox placement (mirror of `@opengeni/contracts` CreateSessionRequest.sandbox,\n // addendum 05 §D.1). Three-way union; OMITTED ⇒ the context-dependent server default\n // (from inside a session → \"shared\" with the creator's box, top-level → \"new\").\n // - \"shared\": join the CREATOR's box (requires a parent session; top-level → 422).\n // - \"new\": mint a fresh singleton box (group ≡ the new session's id).\n // - {groupId}: join a SPECIFIC sibling group in THIS workspace (manager fan-out).\n sandbox?: \"shared\" | \"new\" | { groupId: 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 // Sandbox-surfacing (mirror of @opengeni/contracts Permission). stream:view is\n // strictly broader than sessions:read (un-redacted pixels); stream:control is\n // the never-granted-v1 raw-input plane; stream:acknowledge is the secret-leak\n // consent gate.\n \"stream:view\",\n \"stream:control\",\n \"stream:acknowledge\",\n \"files:upload\",\n \"files:read\",\n \"files:write\",\n \"terminal:attach\",\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 \"connections:read\",\n \"connections:write\",\n \"environments:manage\",\n \"environments:use\",\n \"mcp_servers:attach\",\n \"toolspace:call\",\n \"goals:manage\",\n \"enrollments:read\",\n \"enrollments: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 * Connection state of a workspace's Codex (ChatGPT) subscription, returned by\n * `GET /v1/workspaces/:id/codex/status`. `models` are the codex models the\n * workspace can select (projected as ClientModel under their own \"no credits\"\n * provider group), present only while connected.\n */\nexport type CodexConnectionStatus = {\n connected: boolean;\n plan?: string | null;\n valid?: boolean;\n expiresAt?: string | null;\n lastError?: string | null;\n models?: ClientModel[];\n /** The account a session runs on when unpinned (label for the in-session indicator). */\n activeAccount?: { id: string; label?: string | null; chatgptAccountId?: string | null } | null;\n /** How many Codex accounts the workspace has connected. */\n accountCount?: number;\n};\n\n/**\n * One normalized Codex usage window (5h or weekly), camelCase end-to-end (the\n * route normalizes server-side; the web layer never re-hand-types snake_case).\n * `percent` is authoritative; used/limit/remaining are a synthesized 0–100 scale\n * (limit = 100) because the provider gives only a percentage. `remaining =\n * 100 - percent` is the P3 rotation key. Identify the window by `limitWindowSeconds`\n * (18000 ⇒ 5h, 604800 ⇒ weekly), never by position.\n */\nexport type CodexUsageWindow = {\n used: number;\n limit: number;\n remaining: number;\n percent: number;\n resetAt: string | null;\n resetAfterSeconds: number | null;\n limitWindowSeconds: number;\n};\n\n/** The normalized usage payload for one account — the P2/P3 contract. */\nexport type CodexUsagePayload = {\n status: \"ok\" | \"limit_reached\" | \"error\" | \"no-data\";\n planType: string | null;\n fiveHour: CodexUsageWindow | null;\n weekly: CodexUsageWindow | null;\n limitReached: boolean;\n fetchedAt: string;\n /** Present only on an auth/refresh failure path. */\n reason?: \"needs_relogin\";\n additionalLimits?: Array<{ limitName: string; meteredFeature: string; fiveHour: CodexUsageWindow | null; weekly: CodexUsageWindow | null }>;\n credits?: { hasCredits: boolean; unlimited: boolean; overageLimitReached: boolean; balance: string };\n};\n\n/** One connected Codex (ChatGPT) account in a workspace (multi-account P1). Metadata only. */\nexport type CodexAccount = {\n id: string;\n chatgptAccountId?: string | null;\n label?: string | null;\n email?: string | null;\n plan?: string | null;\n status: \"active\" | \"needs_relogin\" | \"error\";\n active: boolean;\n expiresAt?: string | null;\n lastRefreshAt?: string | null;\n lastError?: string | null;\n // P2 CACHED usage (built from the persisted columns; renders bars off\n // listCodexAccounts with no second call). null until the first live refresh.\n fiveHour?: CodexUsageWindow | null;\n weekly?: CodexUsageWindow | null;\n usageCheckedAt?: string | null;\n // P3 rotation cooldown: ISO timestamp until which this account is cooling-down\n // (rotated-off after a usage cap). null/absent ⇒ not cooling.\n exhaustedUntil?: string | null;\n};\n\n/** Per-workspace Codex rotation/active settings. P1: rotation inert, only activeCredentialId loads. */\nexport type CodexRotationSettings = {\n rotationEnabled: boolean;\n rotationStrategy: \"most_remaining\" | \"round_robin\" | \"drain_then_next\";\n activeCredentialId: string | null;\n};\n\n/** GET /codex/accounts — the accounts list + the workspace active pointer + settings. */\nexport type CodexAccountsResponse = {\n accounts: CodexAccount[];\n activeAccountId: string | null;\n settings: CodexRotationSettings;\n};\n\n/** Payload of a `codex.account.switched` session event. */\nexport type CodexAccountSwitchedPayload = {\n fromAccountId: string | null;\n toAccountId: string;\n reason: \"manual\" | \"exhausted\" | \"rotation\";\n // P4 connector-aware rotation: the session's used connectors that the new account\n // does NOT cover (a prefer-not-require failover that dropped a connector). Present\n // only on such a switch; the UI renders a \"dropped <connector>\" badge on the pill.\n droppedConnectors?: string[];\n};\n\n/** Device-code start: show `userCode` at `verificationUri`, then poll with `state`. */\nexport type CodexConnectStart = {\n userCode: string;\n verificationUri: string;\n intervalSeconds: number;\n state: string;\n};\n\n/** Poll result: keep polling on `pending`, restart on `expired`, done on `connected`. */\nexport type CodexConnectPoll =\n | { status: \"pending\" }\n | { status: \"expired\" }\n | { status: \"connected\"; plan?: string | null; accountId?: string; isActive?: boolean };\n\n/** Remaining usage/limits for one account. `usage` is the normalized P2 payload. */\nexport type CodexUsage = { status: \"ok\" | \"limit_reached\" | \"error\" | \"no-data\"; usage: CodexUsagePayload | null };\n\n/** Batched live-refresh response, keyed by credential id; each entry independently statused. */\nexport type CodexUsageMap = Record<string, CodexUsage>;\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 // Server-wide hint: does this deployment support Channel-A structured services\n // at all (P4.4). Per-session availability is negotiated on /stream-capabilities;\n // this is the coarse on/off the client uses to decide whether to even attempt\n // the fs/git/terminal panels.\n structuredServices: { fileSystem: boolean; git: boolean; terminalEvents: boolean };\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\nexport type UpdateSessionRequest = {\n title: string;\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\";\nexport type KnowledgeSourceKind = \"manual_upload\" | \"meeting_transcript\" | \"repository\" | \"email\" | \"chat\" | \"document\" | \"web\" | \"other\";\nexport type DocumentSearchMode = \"hybrid\" | \"vector\" | \"keyword\";\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 sourceKind: KnowledgeSourceKind;\n sourceUri: string | null;\n sourceExternalId: string | null;\n sourceTitle: string | null;\n sourceAuthor: string | null;\n sourceCreatedAt: string | null;\n sourceUpdatedAt: string | null;\n sourceVersion: string | null;\n aclTags: string[];\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 matchType: DocumentSearchMode;\n vectorScore: number | null;\n keywordScore: number | null;\n chunkIndex: number;\n metadata: Record<string, unknown>;\n sourceKind: KnowledgeSourceKind;\n sourceUri: string | null;\n sourceExternalId: string | null;\n sourceTitle: string | null;\n sourceAuthor: string | null;\n sourceCreatedAt: string | null;\n sourceUpdatedAt: string | null;\n sourceVersion: string | null;\n aclTags: string[];\n};\n\nexport type CreateDocumentBaseRequest = {\n name: string;\n description?: string | undefined;\n};\n\nexport type AddDocumentRequest = {\n fileId: string;\n title?: string | undefined;\n sourceKind?: KnowledgeSourceKind | undefined;\n sourceUri?: string | undefined;\n sourceExternalId?: string | undefined;\n sourceTitle?: string | undefined;\n sourceAuthor?: string | undefined;\n sourceCreatedAt?: string | undefined;\n sourceUpdatedAt?: string | undefined;\n sourceVersion?: string | undefined;\n aclTags?: string[] | undefined;\n};\n\nexport type DocumentSearchRequest = {\n query: string;\n baseIds?: string[] | undefined;\n mode?: DocumentSearchMode | undefined;\n sourceKinds?: KnowledgeSourceKind[] | undefined;\n aclTags?: string[] | undefined;\n limit?: number | undefined;\n};\n\nexport type DocumentSearchResponse = {\n results: DocumentSearchResult[];\n};\n\nexport type KnowledgeMemoryStatus = \"proposed\" | \"approved\" | \"rejected\";\nexport type KnowledgeMemoryKind = \"semantic\" | \"episodic\" | \"procedural\" | \"decision\" | \"preference\";\n\nexport type KnowledgeSourceRef = {\n kind: \"document_chunk\" | \"document\" | \"session_event\" | \"memory\" | \"external\";\n id: string;\n uri?: string | undefined;\n title?: string | undefined;\n metadata?: Record<string, unknown> | undefined;\n};\n\nexport type KnowledgeMemory = {\n id: string;\n workspaceId: string;\n status: KnowledgeMemoryStatus;\n kind: KnowledgeMemoryKind;\n scope: string;\n text: string;\n sourceRefs: KnowledgeSourceRef[];\n confidence: number;\n metadata: Record<string, unknown>;\n createdBySessionId: string | null;\n reviewedBy: string | null;\n reviewedAt: string | null;\n createdAt: string;\n updatedAt: string;\n};\n\nexport type CreateKnowledgeMemoryRequest = {\n status?: KnowledgeMemoryStatus | undefined;\n kind?: KnowledgeMemoryKind | undefined;\n scope?: string | undefined;\n text: string;\n sourceRefs?: KnowledgeSourceRef[] | undefined;\n confidence?: number | undefined;\n metadata?: Record<string, unknown> | undefined;\n createdBySessionId?: string | undefined;\n};\n\nexport type UpdateKnowledgeMemoryRequest = {\n status?: KnowledgeMemoryStatus | undefined;\n kind?: KnowledgeMemoryKind | undefined;\n scope?: string | undefined;\n text?: string | undefined;\n sourceRefs?: KnowledgeSourceRef[] | undefined;\n confidence?: number | undefined;\n metadata?: Record<string, unknown> | undefined;\n reviewedBy?: string | undefined;\n};\n\nexport type KnowledgeMemorySearchRequest = {\n query?: string | undefined;\n status?: KnowledgeMemoryStatus | undefined;\n kind?: KnowledgeMemoryKind | undefined;\n scope?: string | undefined;\n limit?: number | undefined;\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\" | \"registry\" | \"manual\";\n\nexport type CapabilityInstallationStatus = \"active\" | \"disabled\";\n\nexport type CapabilityCatalogAuthKind = \"oauth2\" | \"api_key\" | \"none\" | \"unknown\";\n\nexport type CapabilityCatalogTier = \"verified\" | \"community\";\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 providerDomain: string | null;\n surfaceType: string | null;\n transport: string | null;\n mcpUrl: string | null;\n authKind: CapabilityCatalogAuthKind | null;\n credentialFacts: Record<string, unknown>[];\n tier: CapabilityCatalogTier | null;\n provenance: string | null;\n logoAssetPath: string | null;\n importBatchId: string | null;\n stale: boolean;\n staleAt: 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 connectionRef?: McpServerConnectionRef | 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 // sandbox warm-time metering (P2.1) — mirrors contracts UsageEventType.\n \"sandbox.warm_seconds\",\n \"sandbox.warm_cost\",\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 mcpCredentialUpdates?: SessionMcpCredentialUpdateInput[] | 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\n// ── Bring-your-own-compute: Machines dashboard + per-machine metrics (M10) ────\n// Hand-written mirrors of the `@opengeni/contracts` MetricSample / MachineView /\n// MachinesResponse / MachineMetricsSeriesResponse (pinned by contract-parity).\n// M9 imports THESE so the dashboard UI never drifts from the API.\n\n/** A point-in-time machine metrics sample. `gpuUtilPct`/`gpuMemBytes` are null\n * when no GPU was present (not-reported, never a real zero); the bytes/load are\n * numbers; `sampledAt` is an ISO-8601 instant. */\nexport type MetricSample = {\n cpuPct: number;\n load1: number;\n load5: number;\n load15: number;\n memUsedBytes: number;\n memTotalBytes: number;\n diskUsedBytes: number;\n diskTotalBytes: number;\n gpuUtilPct: number | null;\n gpuMemBytes: number | null;\n runQueue: number;\n sampledAt: string;\n};\n\n/** The derived dashboard state of a machine (M3 liveness + consent/display\n * reasons + the in-flight device-flow). */\nexport type MachineState =\n | \"online\"\n | \"reconnecting\"\n | \"offline\"\n | \"consent_required\"\n | \"display_unavailable\"\n | \"enrolling\";\n\nexport type MachineKind = \"modal\" | \"selfhosted\";\n\n/** A machine as the Machines dashboard renders it (an enrolled selfhosted machine\n * or the session's synthetic Modal group box, `isSessionGroup: true`). */\nexport type MachineView = {\n sandboxId: string;\n enrollmentId: string | null;\n name: string;\n kind: MachineKind;\n state: MachineState;\n active: boolean;\n isSessionGroup: boolean;\n os: string;\n arch: string;\n hasDisplay: boolean;\n /** Non-null only when a display exists but capture is blocked (macOS Screen\n * Recording / TCC not granted) — the UI can surface \"display: capture not\n * granted\". null == capture permitted OR headless. */\n desktopUnavailableReason?: string | null | undefined;\n allowScreenControl: boolean;\n sharedSessionCount: number;\n lastSeenAt: string | null;\n metrics: MetricSample | null;\n};\n\n/** GET /v1/workspaces/:ws/machines — the dashboard list + the active-sandbox\n * pointer (null activeSandboxId == the session's own group box is active). */\nexport type MachinesResponse = {\n activeSandboxId: string | null;\n activeEpoch: number;\n machines: MachineView[];\n};\n\n/** GET /v1/workspaces/:ws/machines/:enrollmentId/metrics/series — the downsampled\n * (~1/min) history the dashboard time-range reads. */\nexport type MachineMetricsSeriesResponse = {\n samples: MetricSample[];\n};\n\n/** POST /v1/workspaces/:ws/sessions/:sessionId/active-sandbox — swap a session's\n * active sandbox. `target` is a `MachineView.sandboxId`, or \"session\"/\"default\"\n * to swap back to the session's own group box. */\nexport type SwapActiveSandboxRequest = {\n target: string;\n};\n\n/** The swap outcome (mirrors the server `FleetSwapResult`). `swapped` is true on a\n * successful repoint OR a no-op (already there); `reason` carries the failure\n * detail (unowned/offline target, or a lost epoch fence) when false. */\nexport type SwapActiveSandboxResponse = {\n swapped: boolean;\n activeSandboxId: string | null;\n activeEpoch: number;\n reason?: string;\n};\n\n// ── Self-hosted enrollment UX (design 11) ────────────────────────────────────\n// Hand-written mirrors of the `@opengeni/contracts` enrollment-UX request/response\n// shapes (the SDK keeps zero runtime deps). The click-Grant approve-page\n// lookup/deny + the headless enroll-token mint/exchange.\n\n/** Mirror of `@opengeni/contracts` EnrollmentOs. */\nexport type EnrollmentOs = \"linux\" | \"macos\" | \"windows\";\n\n/** POST /v1/enrollments/device/lookup body. */\nexport type DeviceEnrollmentLookupRequest = {\n userCode: string;\n};\n\n/** The presentational machine details the consent screen renders. */\nexport type DeviceEnrollmentLookupMachine = {\n machineName: string | null;\n os: EnrollmentOs;\n arch: string;\n canOfferDisplay: boolean;\n requestsScreenControl: boolean;\n};\n\n/** POST /v1/enrollments/device/lookup response (no secrets, no device_code). */\nexport type DeviceEnrollmentLookupResponse = {\n workspaceId: string;\n userCode: string;\n machine: DeviceEnrollmentLookupMachine;\n expiresAt: string;\n};\n\n/** POST /v1/workspaces/:ws/enrollments/device/approve body. */\nexport type DeviceEnrollmentApproveRequest = {\n userCode: string;\n allowScreenControl?: boolean;\n};\n\n/** POST /v1/workspaces/:ws/enrollments/device/approve response. */\nexport type DeviceEnrollmentApproveResponse = {\n approved: boolean;\n enrollmentId: string;\n sandboxId: string;\n allowScreenControl: boolean;\n};\n\n/** POST /v1/workspaces/:ws/enrollments/device/deny body. */\nexport type DeviceEnrollmentDenyRequest = {\n userCode: string;\n};\n\n/** POST /v1/workspaces/:ws/enrollments/device/deny response. */\nexport type DeviceEnrollmentDenyResponse = {\n denied: boolean;\n};\n\n/** POST /v1/workspaces/:ws/enrollments/token body. */\nexport type MintEnrollTokenRequest = {\n allowScreenControl?: boolean;\n};\n\n/** POST /v1/workspaces/:ws/enrollments/token response. The `token` is SECRET. */\nexport type MintEnrollTokenResponse = {\n token: string;\n expiresAt: string;\n expiresInSeconds: number;\n};\n\n/** The credential payload the headless exchange returns (a subset of the agent's\n * EnrollmentCredentials — IDENTICAL to the device-flow poll authorized branch). */\nexport type EnrollmentCredentials = {\n agentId: string;\n workspaceId: string;\n bearer: string;\n subjectPrefix: string;\n natsUrls: string[];\n relayUrl: string;\n relayToken: string;\n natsAccountCreds: string;\n updatePublicKey: string;\n consentedWholeMachine: boolean;\n consentedScreenControl: boolean;\n};\n\n/** POST /v1/enrollments/token/exchange body (the headless / fleet enroll path). */\nexport type EnrollTokenExchangeRequest = {\n token: string;\n publicKey: string;\n os?: EnrollmentOs;\n arch?: string;\n machineName?: string;\n exposure?: \"whole-machine\";\n canOfferDisplay?: boolean;\n requestsScreenControl?: boolean;\n};\n\n/** POST /v1/enrollments/token/exchange response (wraps the credential shape). */\nexport type EnrollTokenExchangeResponse = {\n credentials: EnrollmentCredentials;\n};\n"],"mappings":"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","names":[]}
|