/** SDK configuration options. */ type FetchFn = (input: RequestInfo | URL, init?: RequestInit) => Promise; interface ClientOptions { baseUrl: string; apiKey?: string; credentials?: { email: string; password: string; }; timeout?: number; fetch?: FetchFn; } /** Workspace resource. */ interface Workspace { id: string; name: string; userId: string; runtime: string; storageSize: string; phase: string; pvcName?: string; labels?: Record; createdAt: string; updatedAt: string; } interface CreateWorkspaceRequest { name?: string; runtime?: string; storageSize?: string; storageClass?: string; labels?: Record; } interface WorkspaceListResult { items: WorkspaceListItem[]; pagination?: PaginationMetadata; } interface WorkspaceListItem { id: string; name: string; userId: string; runtime: string; storageSize: string; phase?: string; maxActiveSessions?: number; createdAt: string; updatedAt: string; } interface PaginationMetadata { total: number; start: number; end: number; limit: number; offset: number; } interface WorkspaceStatusResult { phase: string; pvcName?: string; activeSessions: number; lastActivityAt?: string; message?: string; conditions?: WorkspaceCondition[]; credentialState: { available: boolean; reason?: string; message?: string; }; agentHealth: { status: string; providersConfigured: number; agentVersion?: string; }; sessions?: { id: string; title?: string; status: string; }[]; diskUsedBytes?: number; diskTotalBytes?: number; } interface WorkspaceCondition { type: string; status: string; reason?: string; message?: string; } interface ActivateWorkspaceResponse { resumed: string; suspended?: string; } interface RefreshWorkspaceResult { restartGeneration: number; } interface EnsureSessionResponse { workspaceId: string; workspacePhase: string; sessionId: string; resumed: boolean; } interface SessionListItem { id: string; title?: string; lastMessageAt?: string; messageCount: number; status: string; } /** * The platform-owned view of one agent session (pkg/session contract, * design 0049) — the getSession response shape. */ interface Session { id: string; workspaceId: string; parentId?: string; title?: string; agentId?: string; model?: ModelRef; status: "unknown" | "idle" | "busy" | "error" | "compacting" | "archived"; cost?: Cost; contextUsage?: ContextUsage; time?: TimeRange; summary?: string; archived?: boolean; } /** The session's live context occupancy (non-monotonic: compaction resets it). */ interface ContextUsage { used: number; window?: number; } /** Bounds a session or message. completedAt is absent while busy. */ interface TimeRange { startedAt: string; completedAt?: string; } /** * The delivery-outbox receipt for an async prompt: the 202 body of a * fresh accept, and the 200 body of a retried clientMessageID (which * echoes the ORIGINAL accepted entry with status "duplicate"). */ interface PromptAccepted { messageID: string; clientMessageID?: string; status: "queued" | "duplicate"; } interface ActiveSessionsResponse { active: string[]; maxActive: number; } /** * One entry in a session transcript (pkg/session contract, design 0049). * Flat discriminated struct: type selects which fields are meaningful. */ interface Message { id: string; sessionId?: string; type: "user" | "assistant" | "shell" | "agent_switch" | "model_switch" | "compaction" | "system"; createdAt?: string; parts?: Part[]; model?: ModelRef; cost?: Cost; /** Plain-text form (user/system/compaction messages). */ text?: string; /** Shell-message fields. */ command?: string; exitCode?: number; /** Agent/model-switch fields. */ fromAgent?: string; toAgent?: string; fromModel?: ModelRef; toModel?: ModelRef; error?: { code?: string; message: string; }; } /** One renderable part of a message — the closed 5-type union. */ interface Part { type: "text" | "reasoning" | "tool" | "file_change" | "custom"; id?: string; text?: string; reasoning?: string; tool?: ToolPart; fileChange?: FileDiff; custom?: { kind: string; data?: unknown; }; } /** Every tool call is a ToolPart discriminated by name. */ interface ToolPart { callId?: string; name: string; input?: unknown; output?: unknown; state: { status: "pending" | "running" | "completed" | "error"; error?: string; startedAt?: string; completedAt?: string; }; } /** Unified-diff payload of a file-change part (patch text is authoritative). */ interface FileDiff { path: string; oldPath?: string; status: "added" | "modified" | "deleted" | "renamed"; patch: string; additions?: number; deletions?: number; } /** The unified pending-input shape ("the agent needs a human"). */ interface InputRequest { id: string; sessionId?: string; rootSessionId?: string; kind: "question" | "permission"; question?: string; header?: string; options?: InputOption[]; multiple?: boolean; custom?: boolean; permission?: string; patterns?: string[]; always?: string[]; metadata?: Record; tool?: ToolRef; } interface InputOption { label: string; description?: string; } interface ToolRef { messageId?: string; callId?: string; } /** * The 202 body for a reply that landed as a late answer through the * delivery outbox (#1313): the ask was no longer live, so the answer * rides a Q&A user message instead of the live ask. */ interface InboxLateAnswerAccepted { status: "queued"; /** Ask-scoped dedupe key (`inbox-{requestID}-answer`); duplicate clicks return the original. */ clientMessageID: string; /** The outbox entry ID (202) or the original accepted entry (duplicate). */ messageID: string; /** Present and true when the ask-scoped key was already accepted. */ duplicate?: boolean; } interface ModelRef { id: string; provider?: string; } /** Display-only token/cost data (never billing). */ interface Cost { inputTokens?: number; outputTokens?: number; reasoningTokens?: number; cacheReadTokens?: number; cacheWriteTokens?: number; totalTokens?: number; costUsd?: number; } interface AuthResponse { token: string; user: User; } interface User { id: string; username: string; email: string; createdAt: string; updatedAt: string; active: boolean; role: string; } interface APIKey { id: string; name: string; key?: string; prefix: string; active: boolean; createdAt: string; expiresAt?: string; } interface TerminalTicket { ticket: string; expiresAt: string; } interface SecretResponse { id: string; name: string; type: string; metadata?: unknown; globalDefault: boolean; createdAt: string; updatedAt: string; } /** Regex pattern for valid secret names. Keep in sync with pkg/validation/name.go. */ declare const SECRET_NAME_PATTERN: RegExp; interface CreateSecretRequest { /** Lowercase alphanumeric, dots, underscores, hyphens only. Must not start with dot or hyphen. */ name: string; type: "api-key" | "ssh-key" | "git-credential" | "secret-file" | "env-secret"; value: string; metadata?: unknown; } interface ProviderCredential { id: string; name: string; kind: string; slug: string; baseURL?: string; modelAllowlist?: string[]; modelContextLimits?: Record; modelOutputLimits?: Record; createdAt: string; updatedAt: string; } interface CreateProviderCredentialRequest { name: string; kind: string; slug: string; apiKey: string; baseURL?: string; } interface UpdateProviderCredentialRequest { name?: string; apiKey?: string; baseURL?: string; modelAllowlist?: string[]; modelContextLimits?: Record; modelOutputLimits?: Record; } /** * A message waiting in the session queue. * * Under the V2 session-queue model (Epic 63), this is a best-effort shadow * derived from SSE events. `retry_count` is vestigial (V2 has no retry — * opencode handles durability internally). */ interface QueuedMessage { id: string; text: string; session_id: string; workspace_id: string; enqueued_at: string; retry_count: number; } /** * Result of a workspace file upload (Epic 68): the absolute path of the * stored file on the workspace PVC (/workspace/uploads/<uuid>-<name>), * its sanitized name, and the stored byte count. */ interface FileUpload { path: string; name: string; size: number; } interface McpServer { id: string; name: string; transport: "http" | "sse" | "stdio"; url?: string; command?: string; args?: string[]; timeoutMs?: number; hasSecret: boolean; enabled: boolean; createdAt?: string; updatedAt?: string; } interface CreateMcpServerRequest { name: string; transport: "http" | "sse" | "stdio"; url?: string; command?: string; args?: string[]; timeoutMs?: number; enabled?: boolean; env?: Record; headers?: Record; autoApply?: { targetType: string; targetId?: string; }; } interface UpdateMcpServerRequest { name?: string; url?: string; command?: string; args?: string[]; timeoutMs?: number; enabled?: boolean; env?: Record; headers?: Record; } interface McpAutoApplyRule { targetType: string; targetId?: string; } declare class LLMSafeSpaces { readonly baseUrl: string; private readonly timeout; private readonly fetchFn; private token; private apiKey; private credentials; private loggingIn; readonly workspaces: WorkspacesAPI; readonly sessions: SessionsAPI; readonly auth: AuthAPI; readonly secrets: SecretsAPI; readonly terminal: TerminalAPI; readonly userSettings: UserSettingsAPI; readonly account: AccountAPI; readonly providerCredentials: ProviderCredentialsAPI; readonly adminProviderCredentials: AdminProviderCredentialsAPI; readonly usage: UsageAPI; readonly inputRequests: InputRequestsAPI; readonly probe: ProbeAPI; readonly prompts: PromptsAPI; readonly agentRoles: AgentRolesAPI; readonly workflows: WorkflowsAPI; readonly triggers: TriggersAPI; readonly mcpServers: McpServersAPI; readonly adminMcpServers: AdminMcpServersAPI; readonly orgMcpServers: OrgMcpServersAPI; constructor(options: ClientOptions); /** Internal: make an authenticated request. */ request(method: string, path: string, body?: unknown, timeout?: number): Promise; /** * Internal: like {@link request}, but also returns the response headers * (e.g. pagination cursors). Body decoding follows the same contract. */ requestWithHeaders(method: string, path: string, body?: unknown, timeout?: number): Promise<{ data: T; headers: Headers; }>; private login; } declare class WorkspacesAPI { private client; constructor(client: LLMSafeSpaces); list(limit?: number, offset?: number): Promise; create(req: CreateWorkspaceRequest): Promise; get(id: string): Promise; rename(id: string, name: string): Promise; delete(id: string): Promise; getStatus(id: string): Promise; /** * Uploads a file into the workspace (Epic 68): multipart POST with a * single part named `file`; the file lands on the workspace PVC under * /workspace/uploads/. The returned path feeds the `files` parameter of * sessions.sendPromptAsync / sessions.enqueue. The workspace must be * Active; a 409 rejects with ConflictError carrying `phase`. */ upload(id: string, filename: string, content: Blob | string): Promise; activate(id: string): Promise; suspend(id: string): Promise; restart(id: string): Promise; refreshCompute(id: string): Promise; setBindings(id: string, secretIds: string[]): Promise; getBindings(id: string): Promise<{ bindings: Array<{ secretId: string; name: string; type: string; }>; }>; reloadSecrets(id: string): Promise<{ status: "applied" | "not_modified"; appliedRev: string; restarted: boolean; }>; setModel(id: string, model: string): Promise; getModels(id: string): Promise<{ models: unknown[]; currentModel: string; }>; setEnv(id: string, env: Record): Promise; getEnv(id: string): Promise<{ vars: string[]; }>; deleteEnv(id: string, varName: string): Promise; setDevPreview(id: string, enabled: boolean): Promise; /** * Returns the URL to open the dev-preview proxy in a browser. * The URL is authenticated via session cookie; no token in the URL. * The dev server must be running on `port` inside the workspace. */ devPreviewUrl(id: string, port: number, path?: string): string; } declare class SessionsAPI { private client; constructor(client: LLMSafeSpaces); ensure(workspaceId: string): Promise; list(workspaceId: string): Promise; getActive(workspaceId: string): Promise; rename(workspaceId: string, sessionId: string, title: string): Promise; /** Sends synchronously; returns the completed assistant Message (contract shape). */ sendMessage(workspaceId: string, sessionId: string, content: string): Promise; /** Returns the session transcript in contract shape. */ getHistory(workspaceId: string, sessionId: string): Promise; /** * Returns one page of session history with cursor pagination. * nextCursor is "" when the beginning of the session was reached * (no X-Next-Cursor response header). */ getHistoryPage(workspaceId: string, sessionId: string, opts?: { limit?: number; before?: string; }): Promise<{ messages: Message[]; nextCursor: string; }>; abort(workspaceId: string, sessionId: string): Promise; /** Gets one session in contract shape (pkg/session Session). */ get(workspaceId: string, sessionId: string): Promise; /** * Sends a prompt asynchronously into the delivery outbox (202) and * returns the accepted-entry receipt; the agent's reply arrives on the * workspace SSE stream. A retried clientMessageID answers 200 with the * ORIGINAL accepted entry (status "duplicate"). Optional `files` * (Epic 68) are upload-namespace paths — the API composes the v1 * attachment manifest into the dispatched text. */ sendPromptAsync(workspaceId: string, sessionId: string, message: string, files?: string[]): Promise; delete(workspaceId: string, sessionId: string): Promise; /** Enqueues a message for a busy session; optional `files` as in sendPromptAsync. */ enqueue(workspaceId: string, sessionId: string, text: string, files?: string[]): Promise<{ messageID: string; }>; /** * @deprecated Under the V2 session-queue model (Epic 63), the queue is * inboard in opencode and this endpoint returns a best-effort shadow * derived from SSE events. Subscribe to the workspace SSE stream and * track `queue.update` events instead. Removed in next major. */ listQueue(workspaceId: string, sessionId: string): Promise<{ messages: QueuedMessage[]; }>; /** * @deprecated Under the V2 session-queue model (Epic 63), abort is * non-destructive and queued messages survive. This removes from the * best-effort shadow only; it does not revoke the durable input. * Removed in next major. */ dismissQueued(workspaceId: string, sessionId: string, messageId: string): Promise; markSeen(workspaceId: string, sessionId: string): Promise; } declare class AuthAPI { private client; constructor(client: LLMSafeSpaces); me(): Promise; listApiKeys(): Promise; createApiKey(name: string): Promise; deleteApiKey(id: string): Promise; } declare class SecretsAPI { private client; constructor(client: LLMSafeSpaces); create(req: CreateSecretRequest): Promise; list(): Promise; get(id: string): Promise; update(id: string, value: string): Promise; delete(id: string): Promise; reveal(id: string, password: string): Promise<{ value: string; }>; getAuditLog(): Promise<{ entries: unknown[]; }>; getBindingsForSecret(id: string): Promise<{ workspaces: string[]; }>; } declare class TerminalAPI { private client; constructor(client: LLMSafeSpaces); getTicket(workspaceId: string): Promise; } declare class UserSettingsAPI { private client; constructor(client: LLMSafeSpaces); get(): Promise<{ settings: Record; schemaVersion: number; }>; getSchema(): Promise<{ settings: unknown[]; schemaVersion: number; }>; set(key: string, value: unknown): Promise<{ key: string; value: unknown; }>; } declare class AccountAPI { private client; constructor(client: LLMSafeSpaces); } declare class ProviderCredentialsAPI { private client; constructor(client: LLMSafeSpaces); create(req: CreateProviderCredentialRequest): Promise; list(): Promise; get(id: string): Promise; delete(id: string): Promise; probeModels(id: string): Promise<{ models: unknown[]; }>; listBindings(id: string): Promise; bind(credId: string, workspaceId: string): Promise; unbind(credId: string, workspaceId: string): Promise; } declare class AdminProviderCredentialsAPI { private client; constructor(client: LLMSafeSpaces); list(): Promise; create(req: CreateProviderCredentialRequest): Promise; get(id: string): Promise; update(id: string, req: UpdateProviderCredentialRequest): Promise; delete(id: string): Promise; probeModels(id: string): Promise<{ models: unknown[]; }>; createAutoApply(id: string, req: { targetType: string; targetId?: string; withinPriority?: number; }): Promise; listAutoApply(id: string): Promise; deleteAutoApply(id: string, targetType: string, targetId: string): Promise; } declare class UsageAPI { private client; constructor(client: LLMSafeSpaces); get(): Promise>; getWorkspace(workspaceId: string): Promise>; getQuota(): Promise>; } declare class InputRequestsAPI { private client; constructor(client: LLMSafeSpaces); /** Pending questions as contract InputRequest values (kind=question). */ listQuestions(workspaceId: string): Promise; /** * Answers a live question. When the ask is no longer live but its * unanswered-question inbox record is pending (#1313), the server * accepts the answer as a late answer through the delivery outbox * (202) and the resolved value carries the outbox entry; a live * answer (200) resolves to undefined. */ replyQuestion(workspaceId: string, requestId: string, answers: string[][]): Promise; /** Rejects (dismisses) a pending question. */ rejectQuestion(workspaceId: string, requestId: string): Promise; /** Pending permissions as contract InputRequest values (kind=permission). */ listPermissions(workspaceId: string): Promise; /** * Answers a live permission with the reply vocabulary * ("once" | "always" | "reject") and an optional message. A late * decision (ask no longer live, inbox record pending) resolves to the * 202 outbox entry; a live answer (200) resolves to undefined. */ replyPermission(workspaceId: string, requestId: string, reply: "once" | "always" | "reject", message?: string): Promise; /** * Dismisses an unanswered-question inbox record (#1313): the record * becomes dismissed; a still-live ask is rejected first server-side. */ dismissInboxRecord(workspaceId: string, sessionId: string, requestId: string): Promise; /** Triggers an input-snapshot flight (202; events arrive on the event streams). */ requestInputSnapshot(workspaceId: string): Promise; } declare class ProbeAPI { private client; constructor(client: LLMSafeSpaces); probeModels(apiKey: string, baseURL: string): Promise<{ models: unknown[]; }>; } declare class PromptsAPI { private client; constructor(client: LLMSafeSpaces); getPlatform(): Promise<{ prompt: string; }>; setPlatform(prompt: string): Promise; getOrg(orgId: string): Promise<{ prompt: string; allowUserPrompt: boolean; }>; setOrg(orgId: string, body: { prompt?: string; allowUserPrompt?: boolean; }): Promise; getWorkspace(workspaceId: string): Promise<{ prompt: string; }>; setWorkspace(workspaceId: string, prompt: string): Promise; } declare class AgentRolesAPI { private client; constructor(client: LLMSafeSpaces); listPlatform(): Promise; createPlatform(body: Record): Promise; getPlatform(roleId: string): Promise; updatePlatform(roleId: string, body: Record): Promise; deletePlatform(roleId: string): Promise; listOrg(orgId: string): Promise; createOrg(orgId: string, body: Record): Promise; getOrg(orgId: string, roleId: string): Promise; updateOrg(orgId: string, roleId: string, body: Record): Promise; deleteOrg(orgId: string, roleId: string): Promise; getWorkspaceRole(workspaceId: string): Promise; setWorkspaceRole(workspaceId: string, roleId: string): Promise; clearWorkspaceRole(workspaceId: string): Promise; getEffectiveWorkspaceRole(workspaceId: string): Promise; } interface WorkflowResponse { id: string; ownerType: string; name: string; slug: string; description: string; specYaml: string; status: string; createdAt: string; updatedAt: string; } interface WorkflowRunResponse { id: string; workflowId: string; status: string; errorCode?: string; input?: unknown; output?: unknown; startedAt?: string; finishedAt?: string; createdAt: string; } interface TriggerResponse { id: string; name: string; enabled: boolean; sourceType: string; sourceConfig: unknown; workspaceId?: string; workflowId?: string; prompt?: string; agent?: string; scriptPath?: string; scriptArgs?: string[]; scriptEnv?: unknown; memoryMode?: string; captureMode?: string; preserveSession?: string; consecutiveFailures: number; autoDisableAfter: number; lastFiredAt?: string; nextFireAt?: string; } declare class WorkflowsAPI { private readonly client; constructor(client: LLMSafeSpaces); list(): Promise<{ workflows: WorkflowResponse[]; }>; get(id: string): Promise; create(req: { name: string; specYaml: string; status?: string; }): Promise; update(id: string, req: { name?: string; status?: string; specYaml?: string; }): Promise; delete(id: string): Promise; run(id: string, input?: unknown, workspaceId?: string): Promise; getRun(runId: string): Promise; cancelRun(runId: string): Promise; } declare class TriggersAPI { private readonly client; constructor(client: LLMSafeSpaces); list(): Promise<{ triggers: TriggerResponse[]; }>; create(req: { name: string; sourceType: string; sourceConfig: unknown; workspaceId?: string; workflowId?: string; prompt?: string; agent?: string; scriptPath?: string; scriptArgs?: string[]; scriptEnv?: unknown; memoryMode?: string; captureMode?: string; preserveSession?: string; }): Promise; update(id: string, req: { enabled?: boolean; autoDisableAfter?: number; }): Promise; delete(id: string): Promise; } /** MCP servers owned by the caller (/me/mcp-servers, Epic 53). */ declare class McpServersAPI { private readonly client; constructor(client: LLMSafeSpaces); list(): Promise; get(id: string): Promise; create(req: CreateMcpServerRequest): Promise; update(id: string, req: UpdateMcpServerRequest): Promise; delete(id: string): Promise; bind(id: string, workspaceId: string): Promise; unbind(id: string, workspaceId: string): Promise; createAutoApply(id: string, targetType: string, targetId?: string): Promise; listAutoApply(id: string): Promise; } /** Platform MCP servers (/admin/mcp-servers; admin scope). */ declare class AdminMcpServersAPI { private readonly client; constructor(client: LLMSafeSpaces); list(): Promise; get(id: string): Promise; create(req: CreateMcpServerRequest): Promise; update(id: string, req: UpdateMcpServerRequest): Promise; delete(id: string): Promise; bind(id: string, workspaceId: string): Promise; unbind(id: string, workspaceId: string): Promise; createAutoApply(id: string, targetType: string, targetId?: string): Promise; listAutoApply(id: string): Promise; /** targetId omitted → removes every rule of the targetType. */ deleteAutoApply(id: string, targetType: string, targetId?: string): Promise; } /** Organization MCP servers (/orgs/{orgId}/mcp-servers; org-admin scope). */ declare class OrgMcpServersAPI { private readonly client; constructor(client: LLMSafeSpaces); list(orgId: string): Promise; get(orgId: string, id: string): Promise; create(orgId: string, req: CreateMcpServerRequest): Promise; update(orgId: string, id: string, req: UpdateMcpServerRequest): Promise; delete(orgId: string, id: string): Promise; bind(orgId: string, id: string, workspaceId: string): Promise; unbind(orgId: string, id: string, workspaceId: string): Promise; createAutoApply(orgId: string, id: string, targetType: string, targetId?: string): Promise; listAutoApply(orgId: string, id: string): Promise; } /** Base error for all LLMSafeSpaces API errors. */ declare class LLMSafeSpacesError extends Error { readonly status: number; readonly code?: string | undefined; constructor(message: string, status: number, code?: string | undefined); } declare class AuthError extends LLMSafeSpacesError { constructor(message: string, status?: number); } declare class NotFoundError extends LLMSafeSpacesError { constructor(message: string); } declare class ConflictError extends LLMSafeSpacesError { /** Current workspace phase, when the 409 body carries one (upload phase gate, Epic 68 D5). */ phase?: string; constructor(message: string); } declare class TimeoutError extends LLMSafeSpacesError { constructor(message?: string); } declare class RateLimitError extends LLMSafeSpacesError { constructor(message?: string); } declare class ServiceUnavailableError extends LLMSafeSpacesError { /** * The workspace exists but cannot service requests (503). The `reason` * field distinguishes the cause: * - "not_ready" — workspace is booting or resuming * - "agent_unreachable" — the agent process hung or crashed * - "agent_restarting" — the agent is being restarted by the health * watchdog or a credential reload * * Retry after `retryAfter` seconds (defaults to 10). */ readonly reason?: string; readonly retryAfter?: number; constructor(message?: string, reason?: string, retryAfter?: number); } export { type APIKey, type ActivateWorkspaceResponse, type ActiveSessionsResponse, AuthError, type AuthResponse, type ClientOptions, ConflictError, type ContextUsage, type Cost, type CreateMcpServerRequest, type CreateProviderCredentialRequest, type CreateSecretRequest, type CreateWorkspaceRequest, type EnsureSessionResponse, type FetchFn, type FileDiff, type FileUpload, type InboxLateAnswerAccepted, type InputOption, type InputRequest, LLMSafeSpaces, LLMSafeSpacesError, type McpAutoApplyRule, type McpServer, type Message, type ModelRef, NotFoundError, type PaginationMetadata, type Part, type PromptAccepted, type ProviderCredential, type QueuedMessage, RateLimitError, type RefreshWorkspaceResult, SECRET_NAME_PATTERN, type SecretResponse, ServiceUnavailableError, type Session, type SessionListItem, type TerminalTicket, type TimeRange, TimeoutError, type ToolPart, type ToolRef, type UpdateMcpServerRequest, type UpdateProviderCredentialRequest, type User, type Workspace, type WorkspaceCondition, type WorkspaceListItem, type WorkspaceListResult, type WorkspaceStatusResult };